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 }