1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
|
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)
}
// 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)
}
// 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)
}
// 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)
}
// 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)
}
// 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)
}
// 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) {}
// 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) {}
// 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)
}
// 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)
}
// 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)
}
|