github.com/robotn/xgb@v0.0.0-20190912153532-2cb92d044934/xgbgen/expression.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  )
     7  
     8  // Expression represents all the different forms of expressions possible in
     9  // side an XML protocol description file. It's also received a few custom
    10  // addendums to make applying special functions (like padding) easier.
    11  type Expression interface {
    12  	// Concrete determines whether this particular expression can be computed
    13  	// to some constant value inside xgbgen. (The alternative is that the
    14  	// expression can only be computed with values at run time of the
    15  	// generated code.)
    16  	Concrete() bool
    17  
    18  	// Eval evaluates a concrete expression. It is an error to call Eval
    19  	// on any expression that is not concrete (or contains any sub-expression
    20  	// that is not concrete).
    21  	Eval() int
    22  
    23  	// Reduce attempts to evaluate any concrete sub-expressions.
    24  	// i.e., (1 + 2 * (5 + 1 + someSizeOfStruct) reduces to
    25  	// (3 * (6 + someSizeOfStruct)).
    26  	// 'prefix' is used preprended to any field reference name.
    27  	Reduce(prefix string) string
    28  
    29  	// String is an alias for Reduce("")
    30  	String() string
    31  
    32  	// Initialize makes sure all names in this expression and any subexpressions
    33  	// have been translated to Go source names.
    34  	Initialize(p *Protocol)
    35  }
    36  
    37  // Function is a custom expression not found in the XML. It's simply used
    38  // to apply a function named in 'Name' to the Expr expression.
    39  type Function struct {
    40  	Name string
    41  	Expr Expression
    42  }
    43  
    44  func (e *Function) Concrete() bool {
    45  	return false
    46  }
    47  
    48  func (e *Function) Eval() int {
    49  	log.Fatalf("Cannot evaluate a 'Function'. It is not concrete.")
    50  	panic("unreachable")
    51  }
    52  
    53  func (e *Function) Reduce(prefix string) string {
    54  	return fmt.Sprintf("%s(%s)", e.Name, e.Expr.Reduce(prefix))
    55  }
    56  
    57  func (e *Function) String() string {
    58  	return e.Reduce("")
    59  }
    60  
    61  func (e *Function) Initialize(p *Protocol) {
    62  	e.Expr.Initialize(p)
    63  }
    64  
    65  // BinaryOp is an expression that performs some operation (defined in the XML
    66  // file) with Expr1 and Expr2 as operands.
    67  type BinaryOp struct {
    68  	Op    string
    69  	Expr1 Expression
    70  	Expr2 Expression
    71  }
    72  
    73  // newBinaryOp constructs a new binary expression when both expr1 and expr2
    74  // are not nil. If one or both are nil, then the non-nil expression is
    75  // returned unchanged or nil is returned.
    76  func newBinaryOp(op string, expr1, expr2 Expression) Expression {
    77  	switch {
    78  	case expr1 != nil && expr2 != nil:
    79  		return &BinaryOp{
    80  			Op:    op,
    81  			Expr1: expr1,
    82  			Expr2: expr2,
    83  		}
    84  	case expr1 != nil && expr2 == nil:
    85  		return expr1
    86  	case expr1 == nil && expr2 != nil:
    87  		return expr2
    88  	case expr1 == nil && expr2 == nil:
    89  		return nil
    90  	}
    91  	panic("unreachable")
    92  }
    93  
    94  func (e *BinaryOp) Concrete() bool {
    95  	return e.Expr1.Concrete() && e.Expr2.Concrete()
    96  }
    97  
    98  func (e *BinaryOp) Eval() int {
    99  	switch e.Op {
   100  	case "+":
   101  		return e.Expr1.Eval() + e.Expr2.Eval()
   102  	case "-":
   103  		return e.Expr1.Eval() - e.Expr2.Eval()
   104  	case "*":
   105  		return e.Expr1.Eval() * e.Expr2.Eval()
   106  	case "/":
   107  		return e.Expr1.Eval() / e.Expr2.Eval()
   108  	case "&":
   109  		return e.Expr1.Eval() & e.Expr2.Eval()
   110  	case "<<":
   111  		return int(uint(e.Expr1.Eval()) << uint(e.Expr2.Eval()))
   112  	}
   113  
   114  	log.Fatalf("Invalid binary operator '%s' for expression.", e.Op)
   115  	panic("unreachable")
   116  }
   117  
   118  func (e *BinaryOp) Reduce(prefix string) string {
   119  	if e.Concrete() {
   120  		return fmt.Sprintf("%d", e.Eval())
   121  	}
   122  
   123  	// An incredibly dirty hack to make sure any time we perform an operation
   124  	// on a field, we're dealing with ints...
   125  	expr1, expr2 := e.Expr1, e.Expr2
   126  	switch expr1.(type) {
   127  	case *FieldRef:
   128  		expr1 = &Function{
   129  			Name: "int",
   130  			Expr: expr1,
   131  		}
   132  	}
   133  	switch expr2.(type) {
   134  	case *FieldRef:
   135  		expr2 = &Function{
   136  			Name: "int",
   137  			Expr: expr2,
   138  		}
   139  	}
   140  	return fmt.Sprintf("(%s %s %s)",
   141  		expr1.Reduce(prefix), e.Op, expr2.Reduce(prefix))
   142  }
   143  
   144  func (e *BinaryOp) String() string {
   145  	return e.Reduce("")
   146  }
   147  
   148  func (e *BinaryOp) Initialize(p *Protocol) {
   149  	e.Expr1.Initialize(p)
   150  	e.Expr2.Initialize(p)
   151  }
   152  
   153  // UnaryOp is the same as BinaryOp, except it's a unary operator with only
   154  // one sub-expression.
   155  type UnaryOp struct {
   156  	Op   string
   157  	Expr Expression
   158  }
   159  
   160  func (e *UnaryOp) Concrete() bool {
   161  	return e.Expr.Concrete()
   162  }
   163  
   164  func (e *UnaryOp) Eval() int {
   165  	switch e.Op {
   166  	case "~":
   167  		return ^e.Expr.Eval()
   168  	}
   169  
   170  	log.Fatalf("Invalid unary operator '%s' for expression.", e.Op)
   171  	panic("unreachable")
   172  }
   173  
   174  func (e *UnaryOp) Reduce(prefix string) string {
   175  	if e.Concrete() {
   176  		return fmt.Sprintf("%d", e.Eval())
   177  	}
   178  	return fmt.Sprintf("(%s (%s))", e.Op, e.Expr.Reduce(prefix))
   179  }
   180  
   181  func (e *UnaryOp) String() string {
   182  	return e.Reduce("")
   183  }
   184  
   185  func (e *UnaryOp) Initialize(p *Protocol) {
   186  	e.Expr.Initialize(p)
   187  }
   188  
   189  // Padding represents the application of the 'pad' function to some
   190  // sub-expression.
   191  type Padding struct {
   192  	Expr Expression
   193  }
   194  
   195  func (e *Padding) Concrete() bool {
   196  	return e.Expr.Concrete()
   197  }
   198  
   199  func (e *Padding) Eval() int {
   200  	return pad(e.Expr.Eval())
   201  }
   202  
   203  func (e *Padding) Reduce(prefix string) string {
   204  	if e.Concrete() {
   205  		return fmt.Sprintf("%d", e.Eval())
   206  	}
   207  	return fmt.Sprintf("xgb.Pad(%s)", e.Expr.Reduce(prefix))
   208  }
   209  
   210  func (e *Padding) String() string {
   211  	return e.Reduce("")
   212  }
   213  
   214  func (e *Padding) Initialize(p *Protocol) {
   215  	e.Expr.Initialize(p)
   216  }
   217  
   218  // PopCount represents the application of the 'PopCount' function to
   219  // some sub-expression.
   220  type PopCount struct {
   221  	Expr Expression
   222  }
   223  
   224  func (e *PopCount) Concrete() bool {
   225  	return e.Expr.Concrete()
   226  }
   227  
   228  func (e *PopCount) Eval() int {
   229  	return int(popCount(uint(e.Expr.Eval())))
   230  }
   231  
   232  func (e *PopCount) Reduce(prefix string) string {
   233  	if e.Concrete() {
   234  		return fmt.Sprintf("%d", e.Eval())
   235  	}
   236  	return fmt.Sprintf("xgb.PopCount(%s)", e.Expr.Reduce(prefix))
   237  }
   238  
   239  func (e *PopCount) String() string {
   240  	return e.Reduce("")
   241  }
   242  
   243  func (e *PopCount) Initialize(p *Protocol) {
   244  	e.Expr.Initialize(p)
   245  }
   246  
   247  // Value represents some constant integer.
   248  type Value struct {
   249  	v int
   250  }
   251  
   252  func (e *Value) Concrete() bool {
   253  	return true
   254  }
   255  
   256  func (e *Value) Eval() int {
   257  	return e.v
   258  }
   259  
   260  func (e *Value) Reduce(prefix string) string {
   261  	return fmt.Sprintf("%d", e.v)
   262  }
   263  
   264  func (e *Value) String() string {
   265  	return e.Reduce("")
   266  }
   267  
   268  func (e *Value) Initialize(p *Protocol) {}
   269  
   270  // Bit represents some bit whose value is computed by '1 << bit'.
   271  type Bit struct {
   272  	b int
   273  }
   274  
   275  func (e *Bit) Concrete() bool {
   276  	return true
   277  }
   278  
   279  func (e *Bit) Eval() int {
   280  	return int(1 << uint(e.b))
   281  }
   282  
   283  func (e *Bit) Reduce(prefix string) string {
   284  	return fmt.Sprintf("%d", e.Eval())
   285  }
   286  
   287  func (e *Bit) String() string {
   288  	return e.Reduce("")
   289  }
   290  
   291  func (e *Bit) Initialize(p *Protocol) {}
   292  
   293  // FieldRef represents a reference to some variable in the generated code
   294  // with name Name.
   295  type FieldRef struct {
   296  	Name string
   297  }
   298  
   299  func (e *FieldRef) Concrete() bool {
   300  	return false
   301  }
   302  
   303  func (e *FieldRef) Eval() int {
   304  	log.Fatalf("Cannot evaluate a 'FieldRef'. It is not concrete.")
   305  	panic("unreachable")
   306  }
   307  
   308  func (e *FieldRef) Reduce(prefix string) string {
   309  	val := e.Name
   310  	if len(prefix) > 0 {
   311  		val = fmt.Sprintf("%s%s", prefix, val)
   312  	}
   313  	return val
   314  }
   315  
   316  func (e *FieldRef) String() string {
   317  	return e.Reduce("")
   318  }
   319  
   320  func (e *FieldRef) Initialize(p *Protocol) {
   321  	e.Name = SrcName(p, e.Name)
   322  }
   323  
   324  // EnumRef represents a reference to some enumeration field.
   325  // EnumKind is the "group" an EnumItem is the name of the specific enumeration
   326  // value inside that group.
   327  type EnumRef struct {
   328  	EnumKind Type
   329  	EnumItem string
   330  }
   331  
   332  func (e *EnumRef) Concrete() bool {
   333  	return false
   334  }
   335  
   336  func (e *EnumRef) Eval() int {
   337  	log.Fatalf("Cannot evaluate an 'EnumRef'. It is not concrete.")
   338  	panic("unreachable")
   339  }
   340  
   341  func (e *EnumRef) Reduce(prefix string) string {
   342  	return fmt.Sprintf("%s%s", e.EnumKind, e.EnumItem)
   343  }
   344  
   345  func (e *EnumRef) String() string {
   346  	return e.Reduce("")
   347  }
   348  
   349  func (e *EnumRef) Initialize(p *Protocol) {
   350  	e.EnumKind = e.EnumKind.(*Translation).RealType(p)
   351  	e.EnumItem = SrcName(p, e.EnumItem)
   352  }
   353  
   354  // SumOf represents a summation of the variable in the generated code named by
   355  // Name. It is not currently used. (It's XKB voodoo.)
   356  type SumOf struct {
   357  	Name string
   358  }
   359  
   360  func (e *SumOf) Concrete() bool {
   361  	return false
   362  }
   363  
   364  func (e *SumOf) Eval() int {
   365  	log.Fatalf("Cannot evaluate a 'SumOf'. It is not concrete.")
   366  	panic("unreachable")
   367  }
   368  
   369  func (e *SumOf) Reduce(prefix string) string {
   370  	if len(prefix) > 0 {
   371  		return fmt.Sprintf("sum(%s%s)", prefix, e.Name)
   372  	}
   373  	return fmt.Sprintf("sum(%s)", e.Name)
   374  }
   375  
   376  func (e *SumOf) String() string {
   377  	return e.Reduce("")
   378  }
   379  
   380  func (e *SumOf) Initialize(p *Protocol) {
   381  	e.Name = SrcName(p, e.Name)
   382  }