aboutsummaryrefslogtreecommitdiff
path: root/nexgb/xgbgen/xml_expression.go
diff options
context:
space:
mode:
Diffstat (limited to 'nexgb/xgbgen/xml_expression.go')
-rw-r--r--nexgb/xgbgen/xml_expression.go160
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 "&amp;":
+ return wrap(operand1.Eval() & operand2.Eval())
+ case "&lt;&lt;":
+ 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")
+}