161 lines
4.1 KiB
Go
161 lines
4.1 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"fmt"
|
|
"log"
|
|
"strconv"
|
|
)
|
|
|
|
type Expression struct {
|
|
XMLName xml.Name
|
|
|
|
Exprs []*Expression `xml:",any"`
|
|
|
|
Data string `xml:",chardata"`
|
|
Op string `xml:"op,attr"`
|
|
Ref string `xml:"ref,attr"`
|
|
}
|
|
|
|
func newValueExpression(v uint) *Expression {
|
|
return &Expression{
|
|
XMLName: xml.Name{Local: "value"},
|
|
Data: fmt.Sprintf("%d", v),
|
|
}
|
|
}
|
|
|
|
// String is for debugging. For actual use, please use 'Morph'.
|
|
func (e *Expression) String() string {
|
|
switch e.XMLName.Local {
|
|
case "op":
|
|
return fmt.Sprintf("(%s %s %s)", e.Exprs[0], e.Op, e.Exprs[1])
|
|
case "unop":
|
|
return fmt.Sprintf("(%s (%s))", e.Op, e.Exprs[0])
|
|
case "popcount":
|
|
return fmt.Sprintf("popcount(%s)", e.Exprs[0])
|
|
case "fieldref":
|
|
fallthrough
|
|
case "value":
|
|
return fmt.Sprintf("%s", e.Data)
|
|
case "bit":
|
|
return fmt.Sprintf("(1 << %s)", e.Data)
|
|
case "enumref":
|
|
return fmt.Sprintf("%s%s", e.Ref, e.Data)
|
|
case "sumof":
|
|
return fmt.Sprintf("sum(%s)", e.Ref)
|
|
default:
|
|
log.Panicf("Unrecognized expression element: %s", e.XMLName.Local)
|
|
}
|
|
|
|
panic("unreachable")
|
|
}
|
|
|
|
// Eval is used to *attempt* to compute a concrete value for a particular
|
|
// expression. This is used in the initial setup to instantiate values for
|
|
// empty items in enums.
|
|
// We can't compute a concrete value for expressions that rely on a context,
|
|
// i.e., some field value.
|
|
func (e *Expression) Eval() uint {
|
|
switch e.XMLName.Local {
|
|
case "op":
|
|
if len(e.Exprs) != 2 {
|
|
log.Panicf("'op' found %d expressions; expected 2.", len(e.Exprs))
|
|
}
|
|
return e.BinaryOp(e.Exprs[0], e.Exprs[1]).Eval()
|
|
case "unop":
|
|
if len(e.Exprs) != 1 {
|
|
log.Panicf("'unop' found %d expressions; expected 1.", len(e.Exprs))
|
|
}
|
|
return e.UnaryOp(e.Exprs[0]).Eval()
|
|
case "popcount":
|
|
if len(e.Exprs) != 1 {
|
|
log.Panicf("'popcount' found %d expressions; expected 1.",
|
|
len(e.Exprs))
|
|
}
|
|
return popCount(e.Exprs[0].Eval())
|
|
case "value":
|
|
val, err := strconv.Atoi(e.Data)
|
|
if err != nil {
|
|
log.Panicf("Could not convert '%s' in 'value' expression to int.",
|
|
e.Data)
|
|
}
|
|
return uint(val)
|
|
case "bit":
|
|
bit, err := strconv.Atoi(e.Data)
|
|
if err != nil {
|
|
log.Panicf("Could not convert '%s' in 'bit' expression to int.",
|
|
e.Data)
|
|
}
|
|
if bit < 0 || bit > 31 {
|
|
log.Panicf("A 'bit' literal must be in the range [0, 31], but " +
|
|
" is %d", bit)
|
|
}
|
|
return 1 << uint(bit)
|
|
case "fieldref":
|
|
log.Panicf("Cannot compute concrete value of 'fieldref' in " +
|
|
"expression '%s'.", e)
|
|
case "enumref":
|
|
log.Panicf("Cannot compute concrete value of 'enumref' in " +
|
|
"expression '%s'.", e)
|
|
case "sumof":
|
|
log.Panicf("Cannot compute concrete value of 'sumof' in " +
|
|
"expression '%s'.", e)
|
|
}
|
|
|
|
log.Panicf("Unrecognized tag '%s' in expression context. Expected one of " +
|
|
"op, fieldref, value, bit, enumref, unop, sumof or popcount.",
|
|
e.XMLName.Local)
|
|
panic("unreachable")
|
|
}
|
|
|
|
func (e *Expression) BinaryOp(operand1, operand2 *Expression) *Expression {
|
|
if e.XMLName.Local != "op" {
|
|
log.Panicf("Cannot perform binary operation on non-op expression: %s",
|
|
e.XMLName.Local)
|
|
}
|
|
if len(e.Op) == 0 {
|
|
log.Panicf("Cannot perform binary operation without operator for: %s",
|
|
e.XMLName.Local)
|
|
}
|
|
|
|
wrap := newValueExpression
|
|
switch e.Op {
|
|
case "+":
|
|
return wrap(operand1.Eval() + operand2.Eval())
|
|
case "-":
|
|
return wrap(operand1.Eval() + operand2.Eval())
|
|
case "*":
|
|
return wrap(operand1.Eval() * operand2.Eval())
|
|
case "/":
|
|
return wrap(operand1.Eval() / operand2.Eval())
|
|
case "&":
|
|
return wrap(operand1.Eval() & operand2.Eval())
|
|
case "<<":
|
|
return wrap(operand1.Eval() << operand2.Eval())
|
|
}
|
|
|
|
log.Panicf("Invalid binary operator '%s' for '%s' expression.",
|
|
e.Op, e.XMLName.Local)
|
|
panic("unreachable")
|
|
}
|
|
|
|
func (e *Expression) UnaryOp(operand *Expression) *Expression {
|
|
if e.XMLName.Local != "unop" {
|
|
log.Panicf("Cannot perform unary operation on non-unop expression: %s",
|
|
e.XMLName.Local)
|
|
}
|
|
if len(e.Op) == 0 {
|
|
log.Panicf("Cannot perform unary operation without operator for: %s",
|
|
e.XMLName.Local)
|
|
}
|
|
|
|
switch e.Op {
|
|
case "~":
|
|
return newValueExpression(^operand.Eval())
|
|
}
|
|
|
|
log.Panicf("Invalid unary operator '%s' for '%s' expression.",
|
|
e.Op, e.XMLName.Local)
|
|
panic("unreachable")
|
|
}
|