aboutsummaryrefslogtreecommitdiff
path: root/nexgb/xgbgen/expression.go
diff options
context:
space:
mode:
Diffstat (limited to 'nexgb/xgbgen/expression.go')
-rw-r--r--nexgb/xgbgen/expression.go436
1 files changed, 436 insertions, 0 deletions
diff --git a/nexgb/xgbgen/expression.go b/nexgb/xgbgen/expression.go
new file mode 100644
index 0000000..3e2235d
--- /dev/null
+++ b/nexgb/xgbgen/expression.go
@@ -0,0 +1,436 @@
+package main
+
+import (
+ "fmt"
+ "log"
+)
+
+// Expression represents all the different forms of expressions possible in
+// side an XML protocol description file. It's also received a few custom
+// addendums to make applying special functions (like padding) easier.
+type Expression interface {
+ // Concrete determines whether this particular expression can be computed
+ // to some constant value inside xgbgen. (The alternative is that the
+ // expression can only be computed with values at run time of the
+ // generated code.)
+ Concrete() bool
+
+ // Eval evaluates a concrete expression. It is an error to call Eval
+ // on any expression that is not concrete (or contains any sub-expression
+ // that is not concrete).
+ Eval() int
+
+ // Reduce attempts to evaluate any concrete sub-expressions.
+ // i.e., (1 + 2 * (5 + 1 + someSizeOfStruct) reduces to
+ // (3 * (6 + someSizeOfStruct)).
+ // 'prefix' is used preprended to any field reference name.
+ Reduce(prefix string) string
+
+ // String is an alias for Reduce("")
+ String() string
+
+ // Initialize makes sure all names in this expression and any subexpressions
+ // have been translated to Go source names.
+ Initialize(p *Protocol)
+
+ // Makes all field references relative to path
+ Specialize(path string) Expression
+}
+
+// Function is a custom expression not found in the XML. It's simply used
+// to apply a function named in 'Name' to the Expr expression.
+type Function struct {
+ Name string
+ Expr Expression
+}
+
+func (e *Function) Concrete() bool {
+ return false
+}
+
+func (e *Function) Eval() int {
+ log.Fatalf("Cannot evaluate a 'Function'. It is not concrete.")
+ panic("unreachable")
+}
+
+func (e *Function) Reduce(prefix string) string {
+ return fmt.Sprintf("%s(%s)", e.Name, e.Expr.Reduce(prefix))
+}
+
+func (e *Function) String() string {
+ return e.Reduce("")
+}
+
+func (e *Function) Initialize(p *Protocol) {
+ e.Expr.Initialize(p)
+}
+
+func (e *Function) Specialize(path string) Expression {
+ r := *e
+ r.Expr = r.Expr.Specialize(path)
+ return &r
+}
+
+// BinaryOp is an expression that performs some operation (defined in the XML
+// file) with Expr1 and Expr2 as operands.
+type BinaryOp struct {
+ Op string
+ Expr1 Expression
+ Expr2 Expression
+}
+
+// newBinaryOp constructs a new binary expression when both expr1 and expr2
+// are not nil. If one or both are nil, then the non-nil expression is
+// returned unchanged or nil is returned.
+func newBinaryOp(op string, expr1, expr2 Expression) Expression {
+ switch {
+ case expr1 != nil && expr2 != nil:
+ return &BinaryOp{
+ Op: op,
+ Expr1: expr1,
+ Expr2: expr2,
+ }
+ case expr1 != nil && expr2 == nil:
+ return expr1
+ case expr1 == nil && expr2 != nil:
+ return expr2
+ case expr1 == nil && expr2 == nil:
+ return nil
+ }
+ panic("unreachable")
+}
+
+func (e *BinaryOp) Concrete() bool {
+ return e.Expr1.Concrete() && e.Expr2.Concrete()
+}
+
+func (e *BinaryOp) Eval() int {
+ switch e.Op {
+ case "+":
+ return e.Expr1.Eval() + e.Expr2.Eval()
+ case "-":
+ return e.Expr1.Eval() - e.Expr2.Eval()
+ case "*":
+ return e.Expr1.Eval() * e.Expr2.Eval()
+ case "/":
+ return e.Expr1.Eval() / e.Expr2.Eval()
+ case "&":
+ return e.Expr1.Eval() & e.Expr2.Eval()
+ case "<<":
+ return int(uint(e.Expr1.Eval()) << uint(e.Expr2.Eval()))
+ }
+
+ log.Fatalf("Invalid binary operator '%s' for expression.", e.Op)
+ panic("unreachable")
+}
+
+func (e *BinaryOp) Reduce(prefix string) string {
+ if e.Concrete() {
+ return fmt.Sprintf("%d", e.Eval())
+ }
+
+ // An incredibly dirty hack to make sure any time we perform an operation
+ // on a field, we're dealing with ints...
+ expr1, expr2 := e.Expr1, e.Expr2
+ switch expr1.(type) {
+ case *FieldRef:
+ expr1 = &Function{
+ Name: "int",
+ Expr: expr1,
+ }
+ }
+ switch expr2.(type) {
+ case *FieldRef:
+ expr2 = &Function{
+ Name: "int",
+ Expr: expr2,
+ }
+ }
+ return fmt.Sprintf("(%s %s %s)",
+ expr1.Reduce(prefix), e.Op, expr2.Reduce(prefix))
+}
+
+func (e *BinaryOp) String() string {
+ return e.Reduce("")
+}
+
+func (e *BinaryOp) Initialize(p *Protocol) {
+ e.Expr1.Initialize(p)
+ e.Expr2.Initialize(p)
+}
+
+func (e *BinaryOp) Specialize(path string) Expression {
+ r := *e
+ r.Expr1 = r.Expr1.Specialize(path)
+ r.Expr2 = r.Expr2.Specialize(path)
+ return &r
+}
+
+// UnaryOp is the same as BinaryOp, except it's a unary operator with only
+// one sub-expression.
+type UnaryOp struct {
+ Op string
+ Expr Expression
+}
+
+func (e *UnaryOp) Concrete() bool {
+ return e.Expr.Concrete()
+}
+
+func (e *UnaryOp) Eval() int {
+ switch e.Op {
+ case "~":
+ return ^e.Expr.Eval()
+ }
+
+ log.Fatalf("Invalid unary operator '%s' for expression.", e.Op)
+ panic("unreachable")
+}
+
+func (e *UnaryOp) Reduce(prefix string) string {
+ if e.Concrete() {
+ return fmt.Sprintf("%d", e.Eval())
+ }
+ return fmt.Sprintf("(%s (%s))", e.Op, e.Expr.Reduce(prefix))
+}
+
+func (e *UnaryOp) String() string {
+ return e.Reduce("")
+}
+
+func (e *UnaryOp) Initialize(p *Protocol) {
+ e.Expr.Initialize(p)
+}
+
+func (e *UnaryOp) Specialize(path string) Expression {
+ r := *e
+ r.Expr = r.Expr.Specialize(path)
+ return &r
+}
+
+// Padding represents the application of the 'pad' function to some
+// sub-expression.
+type Padding struct {
+ Expr Expression
+}
+
+func (e *Padding) Concrete() bool {
+ return e.Expr.Concrete()
+}
+
+func (e *Padding) Eval() int {
+ return pad(e.Expr.Eval())
+}
+
+func (e *Padding) Reduce(prefix string) string {
+ if e.Concrete() {
+ return fmt.Sprintf("%d", e.Eval())
+ }
+ return fmt.Sprintf("xgb.Pad(%s)", e.Expr.Reduce(prefix))
+}
+
+func (e *Padding) String() string {
+ return e.Reduce("")
+}
+
+func (e *Padding) Initialize(p *Protocol) {
+ e.Expr.Initialize(p)
+}
+
+func (e *Padding) Specialize(path string) Expression {
+ r := *e
+ r.Expr = r.Expr.Specialize(path)
+ return &r
+}
+
+// PopCount represents the application of the 'PopCount' function to
+// some sub-expression.
+type PopCount struct {
+ Expr Expression
+}
+
+func (e *PopCount) Concrete() bool {
+ return e.Expr.Concrete()
+}
+
+func (e *PopCount) Eval() int {
+ return int(popCount(uint(e.Expr.Eval())))
+}
+
+func (e *PopCount) Reduce(prefix string) string {
+ if e.Concrete() {
+ return fmt.Sprintf("%d", e.Eval())
+ }
+ return fmt.Sprintf("xgb.PopCount(%s)", e.Expr.Reduce(prefix))
+}
+
+func (e *PopCount) String() string {
+ return e.Reduce("")
+}
+
+func (e *PopCount) Initialize(p *Protocol) {
+ e.Expr.Initialize(p)
+}
+
+func (e *PopCount) Specialize(path string) Expression {
+ r := *e
+ r.Expr = r.Expr.Specialize(path)
+ return &r
+}
+
+// Value represents some constant integer.
+type Value struct {
+ v int
+}
+
+func (e *Value) Concrete() bool {
+ return true
+}
+
+func (e *Value) Eval() int {
+ return e.v
+}
+
+func (e *Value) Reduce(prefix string) string {
+ return fmt.Sprintf("%d", e.v)
+}
+
+func (e *Value) String() string {
+ return e.Reduce("")
+}
+
+func (e *Value) Initialize(p *Protocol) {}
+
+func (e *Value) Specialize(path string) Expression {
+ return e
+}
+
+// Bit represents some bit whose value is computed by '1 << bit'.
+type Bit struct {
+ b int
+}
+
+func (e *Bit) Concrete() bool {
+ return true
+}
+
+func (e *Bit) Eval() int {
+ return int(1 << uint(e.b))
+}
+
+func (e *Bit) Reduce(prefix string) string {
+ return fmt.Sprintf("%d", e.Eval())
+}
+
+func (e *Bit) String() string {
+ return e.Reduce("")
+}
+
+func (e *Bit) Initialize(p *Protocol) {}
+
+func (e *Bit) Specialize(path string) Expression {
+ return e
+}
+
+// FieldRef represents a reference to some variable in the generated code
+// with name Name.
+type FieldRef struct {
+ Name string
+}
+
+func (e *FieldRef) Concrete() bool {
+ return false
+}
+
+func (e *FieldRef) Eval() int {
+ log.Fatalf("Cannot evaluate a 'FieldRef'. It is not concrete.")
+ panic("unreachable")
+}
+
+func (e *FieldRef) Reduce(prefix string) string {
+ val := e.Name
+ if len(prefix) > 0 {
+ val = fmt.Sprintf("%s%s", prefix, val)
+ }
+ return val
+}
+
+func (e *FieldRef) String() string {
+ return e.Reduce("")
+}
+
+func (e *FieldRef) Initialize(p *Protocol) {
+ e.Name = SrcName(p, e.Name)
+}
+
+func (e *FieldRef) Specialize(path string) Expression {
+ return &FieldRef{Name: path + "." + e.Name}
+}
+
+// EnumRef represents a reference to some enumeration field.
+// EnumKind is the "group" an EnumItem is the name of the specific enumeration
+// value inside that group.
+type EnumRef struct {
+ EnumKind Type
+ EnumItem string
+}
+
+func (e *EnumRef) Concrete() bool {
+ return false
+}
+
+func (e *EnumRef) Eval() int {
+ log.Fatalf("Cannot evaluate an 'EnumRef'. It is not concrete.")
+ panic("unreachable")
+}
+
+func (e *EnumRef) Reduce(prefix string) string {
+ return fmt.Sprintf("%s%s", e.EnumKind, e.EnumItem)
+}
+
+func (e *EnumRef) String() string {
+ return e.Reduce("")
+}
+
+func (e *EnumRef) Initialize(p *Protocol) {
+ e.EnumKind = e.EnumKind.(*Translation).RealType(p)
+ e.EnumItem = SrcName(p, e.EnumItem)
+}
+
+func (e *EnumRef) Specialize(path string) Expression {
+ return e
+}
+
+// SumOf represents a summation of the variable in the generated code named by
+// Name. It is not currently used. (It's XKB voodoo.)
+type SumOf struct {
+ Name string
+}
+
+func (e *SumOf) Concrete() bool {
+ return false
+}
+
+func (e *SumOf) Eval() int {
+ log.Fatalf("Cannot evaluate a 'SumOf'. It is not concrete.")
+ panic("unreachable")
+}
+
+func (e *SumOf) Reduce(prefix string) string {
+ if len(prefix) > 0 {
+ return fmt.Sprintf("sum(%s%s)", prefix, e.Name)
+ }
+ return fmt.Sprintf("sum(%s)", e.Name)
+}
+
+func (e *SumOf) String() string {
+ return e.Reduce("")
+}
+
+func (e *SumOf) Initialize(p *Protocol) {
+ e.Name = SrcName(p, e.Name)
+}
+
+func (e *SumOf) Specialize(path string) Expression {
+ return e
+}