github.com/tiagovtristao/plz@v13.4.0+incompatible/src/parse/asp/interpreter.go (about) 1 package asp 2 3 import ( 4 "fmt" 5 "reflect" 6 "strings" 7 "sync" 8 9 "github.com/thought-machine/please/src/core" 10 ) 11 12 // An interpreter holds the package-independent state about our parsing process. 13 type interpreter struct { 14 builtinScope *scope 15 scope *scope 16 parser *Parser 17 subincludes map[string]pyDict 18 config map[*core.Configuration]*pyConfig 19 mutex sync.RWMutex 20 configMutex sync.RWMutex 21 } 22 23 // newInterpreter creates and returns a new interpreter instance. 24 // It loads all the builtin rules at this point. 25 func newInterpreter(state *core.BuildState, p *Parser) *interpreter { 26 s := &scope{ 27 state: state, 28 locals: map[string]pyObject{}, 29 } 30 bs := &scope{ 31 state: state, 32 locals: map[string]pyObject{}, 33 } 34 i := &interpreter{ 35 builtinScope: bs, 36 scope: s, 37 parser: p, 38 subincludes: map[string]pyDict{}, 39 config: map[*core.Configuration]*pyConfig{}, 40 } 41 s.interpreter = i 42 bs.interpreter = i 43 s.LoadSingletons(state) 44 bs.LoadSingletons(state) 45 return i 46 } 47 48 // LoadBuiltins loads a set of builtins from a file, optionally with its contents. 49 func (i *interpreter) LoadBuiltins(filename string, contents []byte, statements []*Statement) error { 50 // Gentle hack - attach the native code once we have loaded the correct file. 51 // Needs to be after this file is loaded but before any of the others that will 52 // use functions from it. 53 if filename == "builtins.build_defs" || filename == "src/parse/rules/builtins.build_defs" { 54 defer registerBuiltins(i.builtinScope) 55 } else if filename == "misc_rules.build_defs" || filename == "src/parse/rules/misc_rules.build_defs" { 56 defer registerSubincludePackage(i.builtinScope) 57 } else if filename == "config_rules.build_defs" || filename == "src/parse/rules/config_rules.build_defs" { 58 defer setNativeCode(i.builtinScope, "select", selectFunc) 59 } 60 defer i.scope.SetAll(i.builtinScope.Freeze(), true) 61 if statements != nil { 62 return i.interpretStatements(i.builtinScope, statements) 63 } else if len(contents) != 0 { 64 return i.loadBuiltinStatements(i.parser.ParseData(contents, filename)) 65 } 66 return i.loadBuiltinStatements(i.parser.parse(filename)) 67 } 68 69 // loadBuiltinStatements loads statements as builtins. 70 func (i *interpreter) loadBuiltinStatements(statements []*Statement, err error) error { 71 if err != nil { 72 return err 73 } 74 i.optimiseExpressions(reflect.ValueOf(statements)) 75 return i.interpretStatements(i.builtinScope, i.parser.optimise(statements)) 76 } 77 78 // interpretAll runs a series of statements in the context of the given package. 79 // The first return value is for testing only. 80 func (i *interpreter) interpretAll(pkg *core.Package, statements []*Statement) (s *scope, err error) { 81 s = i.scope.NewPackagedScope(pkg) 82 // Config needs a little separate tweaking. 83 // Annoyingly we'd like to not have to do this at all, but it's very hard to handle 84 // mutating operations like .setdefault() otherwise. 85 s.config = i.pkgConfig(pkg).Copy() 86 s.Set("CONFIG", s.config) 87 err = i.interpretStatements(s, statements) 88 if err == nil { 89 s.Callback = true // From here on, if anything else uses this scope, it's in a post-build callback. 90 } 91 return s, err 92 } 93 94 // interpretStatements runs a series of statements in the context of the given scope. 95 func (i *interpreter) interpretStatements(s *scope, statements []*Statement) (err error) { 96 defer func() { 97 if r := recover(); r != nil { 98 if e, ok := r.(error); ok { 99 err = e 100 } else { 101 err = fmt.Errorf("%s", r) 102 } 103 } 104 }() 105 s.interpretStatements(statements) 106 return nil // Would have panicked if there was an error 107 } 108 109 // Subinclude returns the global values corresponding to subincluding the given file. 110 func (i *interpreter) Subinclude(path string) pyDict { 111 i.mutex.RLock() 112 globals, present := i.subincludes[path] 113 i.mutex.RUnlock() 114 if present { 115 return globals 116 } 117 // If we get here, it's not been subincluded already. Parse it now. 118 // Note that there is a race here whereby it's possible for two packages to parse the same 119 // subinclude simultaneously - this doesn't matter since they'll get different but equivalent 120 // scopes, and sooner or later things will sort themselves out. 121 stmts, err := i.parser.parse(path) 122 if err != nil { 123 panic(err) // We're already inside another interpreter, which will handle this for us. 124 } 125 stmts = i.parser.optimise(stmts) 126 s := i.scope.NewScope() 127 // Scope needs a local version of CONFIG 128 s.config = i.scope.config.Copy() 129 s.Set("CONFIG", s.config) 130 i.optimiseExpressions(reflect.ValueOf(stmts)) 131 s.interpretStatements(stmts) 132 locals := s.Freeze() 133 if s.config.overlay == nil { 134 delete(locals, "CONFIG") // Config doesn't have any local modifications 135 } 136 i.mutex.Lock() 137 defer i.mutex.Unlock() 138 i.subincludes[path] = locals 139 return s.locals 140 } 141 142 // getConfig returns a new configuration object for the given configuration object. 143 func (i *interpreter) getConfig(config *core.Configuration) *pyConfig { 144 i.configMutex.RLock() 145 if c, present := i.config[config]; present { 146 i.configMutex.RUnlock() 147 return c 148 } 149 i.configMutex.RUnlock() 150 i.configMutex.Lock() 151 defer i.configMutex.Unlock() 152 c := newConfig(config) 153 i.config[config] = c 154 return c 155 } 156 157 // pkgConfig returns a new configuration object for the given package. 158 func (i *interpreter) pkgConfig(pkg *core.Package) *pyConfig { 159 if pkg.Subrepo != nil && pkg.Subrepo.State != nil { 160 return i.getConfig(pkg.Subrepo.State.Config) 161 } 162 return i.getConfig(i.scope.state.Config) 163 } 164 165 // optimiseExpressions implements a peephole optimiser for expressions by precalculating constants 166 // and identifying simple local variable lookups. 167 func (i *interpreter) optimiseExpressions(v reflect.Value) { 168 callback := func(astStruct interface{}) interface{} { 169 if expr, ok := astStruct.(*Expression); ok && expr != nil { 170 if constant := i.scope.Constant(expr); constant != nil { 171 expr.Optimised = &OptimisedExpression{Constant: constant} // Extract constant expression 172 expr.Val = nil 173 } else if expr.Val != nil && expr.Val.Ident != nil && expr.Val.Call == nil && expr.Op == nil && expr.If == nil && expr.Val.Slice == nil { 174 if expr.Val.Property == nil && len(expr.Val.Ident.Action) == 0 { 175 expr.Optimised = &OptimisedExpression{Local: expr.Val.Ident.Name} 176 } else if expr.Val.Ident.Name == "CONFIG" && len(expr.Val.Ident.Action) == 1 && expr.Val.Ident.Action[0].Property != nil && len(expr.Val.Ident.Action[0].Property.Action) == 0 { 177 expr.Optimised = &OptimisedExpression{Config: expr.Val.Ident.Action[0].Property.Name} 178 expr.Val = nil 179 } 180 } 181 } 182 return nil 183 } 184 185 WalkAST(v, callback) 186 } 187 188 // A scope contains all the information about a lexical scope. 189 type scope struct { 190 interpreter *interpreter 191 state *core.BuildState 192 pkg *core.Package 193 parent *scope 194 locals pyDict 195 config *pyConfig 196 // True if this scope is for a pre- or post-build callback. 197 Callback bool 198 } 199 200 // NewScope creates a new child scope of this one. 201 func (s *scope) NewScope() *scope { 202 return s.NewPackagedScope(s.pkg) 203 } 204 205 // NewPackagedScope creates a new child scope of this one pointing to the given package. 206 func (s *scope) NewPackagedScope(pkg *core.Package) *scope { 207 s2 := &scope{ 208 interpreter: s.interpreter, 209 state: s.state, 210 pkg: pkg, 211 parent: s, 212 locals: pyDict{}, 213 config: s.config, 214 Callback: s.Callback, 215 } 216 if pkg != nil && pkg.Subrepo != nil && pkg.Subrepo.State != nil { 217 s2.state = pkg.Subrepo.State 218 } 219 return s2 220 } 221 222 // Error emits an error that stops further interpretation. 223 // For convenience it is declared to return a pyObject but it never actually returns. 224 func (s *scope) Error(msg string, args ...interface{}) pyObject { 225 panic(fmt.Errorf(msg, args...)) 226 } 227 228 // Assert emits an error that stops further interpretation if the given condition is false. 229 func (s *scope) Assert(condition bool, msg string, args ...interface{}) { 230 if !condition { 231 s.Error(msg, args...) 232 } 233 } 234 235 // NAssert is the inverse of Assert, it emits an error if the given condition is true. 236 func (s *scope) NAssert(condition bool, msg string, args ...interface{}) { 237 if condition { 238 s.Error(msg, args...) 239 } 240 } 241 242 // Lookup looks up a variable name in this scope, walking back up its ancestor scopes as needed. 243 // It panics if the variable is not defined. 244 func (s *scope) Lookup(name string) pyObject { 245 if obj, present := s.locals[name]; present { 246 return obj 247 } else if s.parent != nil { 248 return s.parent.Lookup(name) 249 } 250 return s.Error("name '%s' is not defined", name) 251 } 252 253 // LocalLookup looks up a variable name in the current scope. 254 // It does *not* walk back up parent scopes and instead returns nil if the variable could not be found. 255 // This is typically used for things like function arguments where we're only interested in variables 256 // in immediate scope. 257 func (s *scope) LocalLookup(name string) pyObject { 258 return s.locals[name] 259 } 260 261 // Set sets the given variable in this scope. 262 func (s *scope) Set(name string, value pyObject) { 263 s.locals[name] = value 264 } 265 266 // SetAll sets all contents of the given dict in this scope. 267 // Optionally it can filter to just public objects (i.e. those not prefixed with an underscore) 268 func (s *scope) SetAll(d pyDict, publicOnly bool) { 269 for k, v := range d { 270 if k == "CONFIG" { 271 // Special case; need to merge config entries rather than overwriting the entire object. 272 c, ok := v.(*pyFrozenConfig) 273 s.Assert(ok, "incoming CONFIG isn't a config object") 274 s.config.Merge(c) 275 } else if !publicOnly || k[0] != '_' { 276 s.locals[k] = v 277 } 278 } 279 } 280 281 // Freeze freezes the contents of this scope, preventing mutable objects from being changed. 282 // It returns the newly frozen set of locals. 283 func (s *scope) Freeze() pyDict { 284 for k, v := range s.locals { 285 if f, ok := v.(freezable); ok { 286 s.locals[k] = f.Freeze() 287 } 288 } 289 return s.locals 290 } 291 292 // LoadSingletons loads the global builtin singletons into this scope. 293 func (s *scope) LoadSingletons(state *core.BuildState) { 294 s.Set("True", True) 295 s.Set("False", False) 296 s.Set("None", None) 297 if state != nil { 298 s.config = s.interpreter.getConfig(state.Config) 299 s.Set("CONFIG", s.config) 300 } 301 } 302 303 // interpretStatements interprets a series of statements in a particular scope. 304 // Note that the return value is only non-nil if a return statement is encountered; 305 // it is not implicitly the result of the last statement or anything like that. 306 func (s *scope) interpretStatements(statements []*Statement) pyObject { 307 var stmt *Statement 308 defer func() { 309 if r := recover(); r != nil { 310 panic(AddStackFrame(stmt.Pos, r)) 311 } 312 }() 313 for _, stmt = range statements { 314 if stmt.FuncDef != nil { 315 s.Set(stmt.FuncDef.Name, newPyFunc(s, stmt.FuncDef)) 316 } else if stmt.If != nil { 317 if ret := s.interpretIf(stmt.If); ret != nil { 318 return ret 319 } 320 } else if stmt.For != nil { 321 if ret := s.interpretFor(stmt.For); ret != nil { 322 return ret 323 } 324 } else if stmt.Return != nil { 325 if len(stmt.Return.Values) == 0 { 326 return None 327 } else if len(stmt.Return.Values) == 1 { 328 return s.interpretExpression(stmt.Return.Values[0]) 329 } 330 return pyList(s.evaluateExpressions(stmt.Return.Values)) 331 } else if stmt.Ident != nil { 332 s.interpretIdentStatement(stmt.Ident) 333 } else if stmt.Assert != nil { 334 s.Assert(s.interpretExpression(stmt.Assert.Expr).IsTruthy(), stmt.Assert.Message) 335 } else if stmt.Raise != nil { 336 s.Error(s.interpretExpression(stmt.Raise).String()) 337 } else if stmt.Literal != nil { 338 // Do nothing, literal statements are likely docstrings and don't require any action. 339 } else if stmt.Continue { 340 // This is definitely awkward since we need to control a for loop that's happening in a function outside this scope. 341 return continueIteration 342 } else if stmt.Pass { 343 continue // Nothing to do... 344 } else { 345 s.Error("Unknown statement") // Shouldn't happen, amirite? 346 } 347 } 348 return nil 349 } 350 351 func (s *scope) interpretIf(stmt *IfStatement) pyObject { 352 if s.interpretExpression(&stmt.Condition).IsTruthy() { 353 return s.interpretStatements(stmt.Statements) 354 } 355 for _, elif := range stmt.Elif { 356 if s.interpretExpression(&elif.Condition).IsTruthy() { 357 return s.interpretStatements(elif.Statements) 358 } 359 } 360 return s.interpretStatements(stmt.ElseStatements) 361 } 362 363 func (s *scope) interpretFor(stmt *ForStatement) pyObject { 364 for _, li := range s.iterate(&stmt.Expr) { 365 s.unpackNames(stmt.Names, li) 366 if ret := s.interpretStatements(stmt.Statements); ret != nil { 367 if b, ok := ret.(pyBool); ok && b == continueIteration { 368 continue 369 } 370 return ret 371 } 372 } 373 return nil 374 } 375 376 func (s *scope) interpretExpression(expr *Expression) pyObject { 377 // Check the optimised sites first 378 if expr.Optimised != nil { 379 if expr.Optimised.Constant != nil { 380 return expr.Optimised.Constant 381 } else if expr.Optimised.Local != "" { 382 return s.Lookup(expr.Optimised.Local) 383 } 384 return s.config.Property(expr.Optimised.Config) 385 } 386 defer func() { 387 if r := recover(); r != nil { 388 panic(AddStackFrame(expr.Pos, r)) 389 } 390 }() 391 if expr.If != nil && !s.interpretExpression(expr.If.Condition).IsTruthy() { 392 return s.interpretExpression(expr.If.Else) 393 } 394 var obj pyObject 395 if expr.Val != nil { 396 obj = s.interpretValueExpression(expr.Val) 397 } else if expr.UnaryOp != nil { 398 obj = s.interpretValueExpression(&expr.UnaryOp.Expr) 399 if expr.UnaryOp.Op == "not" { 400 if obj.IsTruthy() { 401 obj = False 402 } else { 403 obj = True 404 } 405 } else { 406 i, ok := obj.(pyInt) 407 s.Assert(ok, "Unary - can only be applied to an integer") 408 obj = pyInt(-int(i)) 409 } 410 } 411 for _, op := range expr.Op { 412 switch op.Op { 413 case And, Or: 414 // Careful here to mimic lazy-evaluation semantics (import for `x = x or []` etc) 415 if obj.IsTruthy() == (op.Op == And) { 416 obj = s.interpretExpression(op.Expr) 417 } 418 case Equal: 419 obj = newPyBool(reflect.DeepEqual(obj, s.interpretExpression(op.Expr))) 420 case NotEqual: 421 obj = newPyBool(!reflect.DeepEqual(obj, s.interpretExpression(op.Expr))) 422 case Is: 423 // Is only works on boolean types. 424 b1, isBool1 := obj.(pyBool) 425 b2, isBool2 := s.interpretExpression(op.Expr).(pyBool) 426 obj = newPyBool(isBool1 && isBool2 && b1 == b2) 427 case In, NotIn: 428 // the implementation of in is defined by the right-hand side, not the left. 429 obj = s.interpretExpression(op.Expr).Operator(op.Op, obj) 430 default: 431 obj = obj.Operator(op.Op, s.interpretExpression(op.Expr)) 432 } 433 } 434 return obj 435 } 436 437 func (s *scope) interpretValueExpression(expr *ValueExpression) pyObject { 438 obj := s.interpretValueExpressionPart(expr) 439 if expr.Slice != nil { 440 if expr.Slice.Colon == "" { 441 // Indexing, much simpler... 442 s.Assert(expr.Slice.End == nil, "Invalid syntax") 443 obj = obj.Operator(Index, s.interpretExpression(expr.Slice.Start)) 444 } else { 445 obj = s.interpretSlice(obj, expr.Slice) 446 } 447 } 448 if expr.Property != nil { 449 obj = s.interpretIdent(obj.Property(expr.Property.Name), expr.Property) 450 } else if expr.Call != nil { 451 obj = s.callObject("", obj, expr.Call) 452 } 453 return obj 454 } 455 456 func (s *scope) interpretValueExpressionPart(expr *ValueExpression) pyObject { 457 if expr.Ident != nil { 458 obj := s.Lookup(expr.Ident.Name) 459 if len(expr.Ident.Action) == 0 { 460 return obj // fast path 461 } 462 return s.interpretIdent(obj, expr.Ident) 463 } else if expr.String != "" { 464 // Strings are surrounded by quotes to make it easier for the parser; here they come off again. 465 return pyString(stringLiteral(expr.String)) 466 } else if expr.FString != nil { 467 return s.interpretFString(expr.FString) 468 } else if expr.Int != nil { 469 return pyInt(expr.Int.Int) 470 } else if expr.Bool != "" { 471 return s.Lookup(expr.Bool) 472 } else if expr.List != nil { 473 return s.interpretList(expr.List) 474 } else if expr.Dict != nil { 475 return s.interpretDict(expr.Dict) 476 } else if expr.Tuple != nil { 477 // Parentheses can also indicate precedence; a single parenthesised expression does not create a list object. 478 l := s.interpretList(expr.Tuple) 479 if len(l) == 1 && expr.Tuple.Comprehension == nil { 480 return l[0] 481 } 482 return l 483 } else if expr.Lambda != nil { 484 // A lambda is just an inline function definition with a single return statement. 485 stmt := &Statement{} 486 stmt.Return = &ReturnStatement{ 487 Values: []*Expression{&expr.Lambda.Expr}, 488 } 489 return newPyFunc(s, &FuncDef{ 490 Name: "<lambda>", 491 Arguments: expr.Lambda.Arguments, 492 Statements: []*Statement{stmt}, 493 }) 494 } 495 return None 496 } 497 498 func (s *scope) interpretFString(f *FString) pyObject { 499 var b strings.Builder 500 for _, v := range f.Vars { 501 b.WriteString(v.Prefix) 502 if v.Config != "" { 503 b.WriteString(s.config.MustGet(v.Config).String()) 504 } else { 505 b.WriteString(s.Lookup(v.Var).String()) 506 } 507 } 508 b.WriteString(f.Suffix) 509 return pyString(b.String()) 510 } 511 512 func (s *scope) interpretSlice(obj pyObject, sl *Slice) pyObject { 513 lst, ok1 := obj.(pyList) 514 str, ok2 := obj.(pyString) 515 s.Assert(ok1 || ok2, "Unsliceable type "+obj.Type()) 516 start := s.interpretSliceExpression(obj, sl.Start, 0) 517 end := s.interpretSliceExpression(obj, sl.End, pyInt(obj.Len())) 518 if ok1 { 519 return lst[start:end] 520 } 521 return str[start:end] 522 } 523 524 // interpretSliceExpression interprets one of the begin or end parts of a slice. 525 // expr may be null, if it is the value of def is used instead. 526 func (s *scope) interpretSliceExpression(obj pyObject, expr *Expression, def pyInt) pyInt { 527 if expr == nil { 528 return def 529 } 530 return pyIndex(obj, s.interpretExpression(expr), true) 531 } 532 533 func (s *scope) interpretIdent(obj pyObject, expr *IdentExpr) pyObject { 534 name := expr.Name 535 for _, action := range expr.Action { 536 if action.Property != nil { 537 name = action.Property.Name 538 obj = s.interpretIdent(obj.Property(name), action.Property) 539 } else if action.Call != nil { 540 obj = s.callObject(name, obj, action.Call) 541 } 542 } 543 return obj 544 } 545 546 func (s *scope) interpretIdentStatement(stmt *IdentStatement) { 547 if stmt.Index != nil { 548 // Need to special-case these, because types are immutable so we can't return a modifiable reference to them. 549 obj := s.Lookup(stmt.Name) 550 idx := s.interpretExpression(stmt.Index.Expr) 551 if stmt.Index.Assign != nil { 552 obj.IndexAssign(idx, s.interpretExpression(stmt.Index.Assign)) 553 } else { 554 obj.IndexAssign(idx, obj.Operator(Index, idx).Operator(Add, s.interpretExpression(stmt.Index.AugAssign))) 555 } 556 } else if stmt.Unpack != nil { 557 obj := s.interpretExpression(stmt.Unpack.Expr) 558 l, ok := obj.(pyList) 559 s.Assert(ok, "Cannot unpack type %s", l.Type()) 560 // This is a little awkward because the first item here is the name of the ident node. 561 s.Assert(len(l) == len(stmt.Unpack.Names)+1, "Wrong number of items to unpack; expected %d, got %d", len(stmt.Unpack.Names)+1, len(l)) 562 s.Set(stmt.Name, l[0]) 563 for i, name := range stmt.Unpack.Names { 564 s.Set(name, l[i+1]) 565 } 566 } else if stmt.Action.Property != nil { 567 s.interpretIdent(s.Lookup(stmt.Name).Property(stmt.Action.Property.Name), stmt.Action.Property) 568 } else if stmt.Action.Call != nil { 569 s.callObject(stmt.Name, s.Lookup(stmt.Name), stmt.Action.Call) 570 } else if stmt.Action.Assign != nil { 571 s.Set(stmt.Name, s.interpretExpression(stmt.Action.Assign)) 572 } else if stmt.Action.AugAssign != nil { 573 // The only augmented assignment operation we support is +=, and it's implemented 574 // exactly as x += y -> x = x + y since that matches the semantics of Go types. 575 s.Set(stmt.Name, s.Lookup(stmt.Name).Operator(Add, s.interpretExpression(stmt.Action.AugAssign))) 576 } 577 } 578 579 func (s *scope) interpretList(expr *List) pyList { 580 if expr.Comprehension == nil { 581 return pyList(s.evaluateExpressions(expr.Values)) 582 } 583 cs := s.NewScope() 584 l := s.iterate(expr.Comprehension.Expr) 585 ret := make(pyList, 0, len(l)) 586 cs.evaluateComprehension(l, expr.Comprehension, func(li pyObject) { 587 if len(expr.Values) == 1 { 588 ret = append(ret, cs.interpretExpression(expr.Values[0])) 589 } else { 590 ret = append(ret, pyList(cs.evaluateExpressions(expr.Values))) 591 } 592 }) 593 return ret 594 } 595 596 func (s *scope) interpretDict(expr *Dict) pyObject { 597 if expr.Comprehension == nil { 598 d := make(pyDict, len(expr.Items)) 599 for _, v := range expr.Items { 600 d.IndexAssign(s.interpretExpression(&v.Key), s.interpretExpression(&v.Value)) 601 } 602 return d 603 } 604 cs := s.NewScope() 605 l := cs.iterate(expr.Comprehension.Expr) 606 ret := make(pyDict, len(l)) 607 cs.evaluateComprehension(l, expr.Comprehension, func(li pyObject) { 608 ret.IndexAssign(cs.interpretExpression(&expr.Items[0].Key), cs.interpretExpression(&expr.Items[0].Value)) 609 }) 610 return ret 611 } 612 613 // evaluateComprehension handles iterating a comprehension's loops. 614 // The provided callback function is called with each item to be added to the result. 615 func (s *scope) evaluateComprehension(l pyList, comp *Comprehension, callback func(pyObject)) { 616 if comp.Second != nil { 617 for _, li := range l { 618 s.unpackNames(comp.Names, li) 619 for _, li := range s.iterate(comp.Second.Expr) { 620 if s.evaluateComprehensionExpression(comp, comp.Second.Names, li) { 621 callback(li) 622 } 623 } 624 } 625 } else { 626 for _, li := range l { 627 if s.evaluateComprehensionExpression(comp, comp.Names, li) { 628 callback(li) 629 } 630 } 631 } 632 } 633 634 // evaluateComprehensionExpression runs an expression from a list or dict comprehension, and returns true if the caller 635 // should continue to use it, or false if it's been filtered out of the comprehension. 636 func (s *scope) evaluateComprehensionExpression(comp *Comprehension, names []string, li pyObject) bool { 637 s.unpackNames(names, li) 638 return comp.If == nil || s.interpretExpression(comp.If).IsTruthy() 639 } 640 641 // unpackNames unpacks the given object into this scope. 642 func (s *scope) unpackNames(names []string, obj pyObject) { 643 if len(names) == 1 { 644 s.Set(names[0], obj) 645 } else { 646 l, ok := obj.(pyList) 647 s.Assert(ok, "Cannot unpack %s into %s", obj.Type(), names) 648 s.Assert(len(l) == len(names), "Incorrect number of values to unpack; expected %d, got %d", len(names), len(l)) 649 for i, name := range names { 650 s.Set(name, l[i]) 651 } 652 } 653 } 654 655 // iterate returns the result of the given expression as a pyList, which is our only iterable type. 656 func (s *scope) iterate(expr *Expression) pyList { 657 o := s.interpretExpression(expr) 658 l, ok := o.(pyList) 659 if !ok { 660 if l, ok := o.(pyFrozenList); ok { 661 return l.pyList 662 } 663 } 664 s.Assert(ok, "Non-iterable type %s; must be a list", o.Type()) 665 return l 666 } 667 668 // evaluateExpressions runs a series of Python expressions in this scope and creates a series of concrete objects from them. 669 func (s *scope) evaluateExpressions(exprs []*Expression) []pyObject { 670 l := make(pyList, len(exprs)) 671 for i, v := range exprs { 672 l[i] = s.interpretExpression(v) 673 } 674 return l 675 } 676 677 // stringLiteral converts a parsed string literal (which is still surrounded by quotes) to an unquoted version. 678 func stringLiteral(s string) string { 679 return s[1 : len(s)-1] 680 } 681 682 // callObject attempts to call the given object 683 func (s *scope) callObject(name string, obj pyObject, c *Call) pyObject { 684 // We only allow function objects to be called, so don't bother making it part of the pyObject interface. 685 f, ok := obj.(*pyFunc) 686 if !ok { 687 s.Error("Non-callable object '%s' (is a %s)", name, obj.Type()) 688 } 689 return f.Call(s, c) 690 } 691 692 // Constant returns an object from an expression that describes a constant, 693 // e.g. None, "string", 42, [], etc. It returns nil if the expression cannot be determined to be constant. 694 func (s *scope) Constant(expr *Expression) pyObject { 695 // Technically some of these might be constant (e.g. 'a,b,c'.split(',') or `1 if True else 2`. 696 // That's probably unlikely to be common though - we could do a generalised constant-folding pass 697 // but it's rare that people would write something of that nature in this language. 698 if expr.Optimised != nil && expr.Optimised.Constant != nil { 699 return expr.Optimised.Constant 700 } else if expr.Val == nil || expr.Val.Slice != nil || expr.Val.Property != nil || expr.Val.Call != nil || expr.Op != nil || expr.If != nil { 701 return nil 702 } else if expr.Val.Bool != "" || expr.Val.String != "" || expr.Val.Int != nil { 703 return s.interpretValueExpression(expr.Val) 704 } else if expr.Val.List != nil && expr.Val.List.Comprehension == nil { 705 // Lists can be constant if all their elements are also. 706 for _, v := range expr.Val.List.Values { 707 if s.Constant(v) == nil { 708 return nil 709 } 710 } 711 return s.interpretValueExpression(expr.Val) 712 } 713 // N.B. dicts are not optimised to constants currently because they are mutable (because Go maps have 714 // pointer semantics). It might be nice to be able to do that later but it is probably not critical - 715 // we might also be able to do a more aggressive pass in cases where we know we're passing a constant 716 // to a builtin that won't modify it (e.g. calling build_rule with a constant dict). 717 return nil 718 } 719 720 // pkgFilename returns the filename of the current package, or the empty string if there is none. 721 func (s *scope) pkgFilename() string { 722 if s.pkg != nil { 723 return s.pkg.Filename 724 } 725 return "" 726 }