diff options
Diffstat (limited to 'nexgb/xgbgen/xml_expression.go')
-rw-r--r-- | nexgb/xgbgen/xml_expression.go | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/nexgb/xgbgen/xml_expression.go b/nexgb/xgbgen/xml_expression.go new file mode 100644 index 0000000..dd32512 --- /dev/null +++ b/nexgb/xgbgen/xml_expression.go @@ -0,0 +1,160 @@ +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") +} |