github.phpd.cn/thought-machine/please@v12.2.0+incompatible/src/parse/asp/interpreter.go (about) 1 package asp 2 3 import ( 4 "fmt" 5 "reflect" 6 "sync" 7 8 "core" 9 ) 10 11 // An interpreter holds the package-independent state about our parsing process. 12 type interpreter struct { 13 builtinScope *scope 14 scope *scope 15 parser *Parser 16 subincludeScope *scope 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 i.optimiseExpressions(reflect.ValueOf(stmts)) 128 s.interpretStatements(stmts) 129 locals := s.Freeze() 130 i.mutex.Lock() 131 defer i.mutex.Unlock() 132 i.subincludes[path] = locals 133 return s.locals 134 } 135 136 // getConfig returns a new configuration object for the given configuration object. 137 func (i *interpreter) getConfig(config *core.Configuration) *pyConfig { 138 i.configMutex.RLock() 139 if c, present := i.config[config]; present { 140 i.configMutex.RUnlock() 141 return c 142 } 143 i.configMutex.RUnlock() 144 i.configMutex.Lock() 145 defer i.configMutex.Unlock() 146 c := newConfig(config) 147 i.config[config] = c 148 return c 149 } 150 151 // pkgConfig returns a new configuration object for the given package. 152 func (i *interpreter) pkgConfig(pkg *core.Package) *pyConfig { 153 if pkg.Subrepo != nil && pkg.Subrepo.State != nil { 154 return i.getConfig(pkg.Subrepo.State.Config) 155 } 156 return i.getConfig(i.scope.state.Config) 157 } 158 159 // optimiseExpressions implements a peephole optimiser for expressions by precalculating constants 160 // and identifying simple local variable lookups. 161 func (i *interpreter) optimiseExpressions(v reflect.Value) { 162 if v.Type() == reflect.TypeOf(&Expression{}) && !v.IsNil() { 163 expr := v.Interface().(*Expression) 164 if constant := i.scope.Constant(expr); constant != nil { 165 expr.Optimised = &OptimisedExpression{Constant: constant} // Extract constant expression 166 expr.Val = nil 167 } else if expr.Val != nil && expr.Val.Ident != nil && expr.Val.Call == nil && expr.Op == nil && expr.If == nil && expr.Val.Slice == nil { 168 if expr.Val.Property == nil && len(expr.Val.Ident.Action) == 0 { 169 expr.Optimised = &OptimisedExpression{Local: expr.Val.Ident.Name} 170 } 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 { 171 expr.Optimised = &OptimisedExpression{Config: expr.Val.Ident.Action[0].Property.Name} 172 expr.Val = nil 173 } 174 } 175 } else if v.Kind() == reflect.Ptr && !v.IsNil() { 176 i.optimiseExpressions(v.Elem()) 177 } else if v.Kind() == reflect.Slice { 178 for j := 0; j < v.Len(); j++ { 179 i.optimiseExpressions(v.Index(j)) 180 } 181 } else if v.Kind() == reflect.Struct { 182 for j := 0; j < v.NumField(); j++ { 183 i.optimiseExpressions(v.Field(j)) 184 } 185 } 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 !publicOnly || k[0] != '_' { 271 s.locals[k] = v 272 } 273 } 274 } 275 276 // Freeze freezes the contents of this scope, preventing mutable objects from being changed. 277 // It returns the newly frozen set of locals. 278 func (s *scope) Freeze() pyDict { 279 for k, v := range s.locals { 280 if d, ok := v.(pyDict); ok { 281 s.locals[k] = d.Freeze() 282 } else if l, ok := v.(pyList); ok { 283 s.locals[k] = l.Freeze() 284 } 285 } 286 return s.locals 287 } 288 289 // LoadSingletons loads the global builtin singletons into this scope. 290 func (s *scope) LoadSingletons(state *core.BuildState) { 291 s.Set("True", True) 292 s.Set("False", False) 293 s.Set("None", None) 294 if state != nil { 295 s.config = s.interpreter.getConfig(state.Config) 296 s.Set("CONFIG", s.config) 297 } 298 } 299 300 // interpretStatements interprets a series of statements in a particular scope. 301 // Note that the return value is only non-nil if a return statement is encountered; 302 // it is not implicitly the result of the last statement or anything like that. 303 func (s *scope) interpretStatements(statements []*Statement) pyObject { 304 var stmt *Statement 305 defer func() { 306 if r := recover(); r != nil { 307 panic(AddStackFrame(stmt.Pos, r)) 308 } 309 }() 310 for _, stmt = range statements { 311 if stmt.FuncDef != nil { 312 s.Set(stmt.FuncDef.Name, newPyFunc(s, stmt.FuncDef)) 313 } else if stmt.If != nil { 314 if ret := s.interpretIf(stmt.If); ret != nil { 315 return ret 316 } 317 } else if stmt.For != nil { 318 if ret := s.interpretFor(stmt.For); ret != nil { 319 return ret 320 } 321 } else if stmt.Return != nil { 322 if len(stmt.Return.Values) == 0 { 323 return None 324 } else if len(stmt.Return.Values) == 1 { 325 return s.interpretExpression(stmt.Return.Values[0]) 326 } 327 return pyList(s.evaluateExpressions(stmt.Return.Values)) 328 } else if stmt.Ident != nil { 329 s.interpretIdentStatement(stmt.Ident) 330 } else if stmt.Assert != nil { 331 s.Assert(s.interpretExpression(stmt.Assert.Expr).IsTruthy(), stmt.Assert.Message) 332 } else if stmt.Raise != nil { 333 s.Error(s.interpretExpression(stmt.Raise).String()) 334 } else if stmt.Literal != nil { 335 // Do nothing, literal statements are likely docstrings and don't require any action. 336 } else if stmt.Continue { 337 // This is definitely awkward since we need to control a for loop that's happening in a function outside this scope. 338 return continueIteration 339 } else if stmt.Pass { 340 continue // Nothing to do... 341 } else { 342 s.Error("Unknown statement") // Shouldn't happen, amirite? 343 } 344 } 345 return nil 346 } 347 348 func (s *scope) interpretIf(stmt *IfStatement) pyObject { 349 if s.interpretExpression(&stmt.Condition).IsTruthy() { 350 return s.interpretStatements(stmt.Statements) 351 } 352 for _, elif := range stmt.Elif { 353 if s.interpretExpression(&elif.Condition).IsTruthy() { 354 return s.interpretStatements(elif.Statements) 355 } 356 } 357 return s.interpretStatements(stmt.ElseStatements) 358 } 359 360 func (s *scope) interpretFor(stmt *ForStatement) pyObject { 361 for _, li := range s.iterate(&stmt.Expr) { 362 s.unpackNames(stmt.Names, li) 363 if ret := s.interpretStatements(stmt.Statements); ret != nil { 364 if b, ok := ret.(pyBool); ok && b == continueIteration { 365 continue 366 } 367 return ret 368 } 369 } 370 return nil 371 } 372 373 func (s *scope) interpretExpression(expr *Expression) pyObject { 374 // Check the optimised sites first 375 if expr.Optimised != nil { 376 if expr.Optimised.Constant != nil { 377 return expr.Optimised.Constant 378 } else if expr.Optimised.Local != "" { 379 return s.Lookup(expr.Optimised.Local) 380 } 381 return s.config.Property(expr.Optimised.Config) 382 } 383 defer func() { 384 if r := recover(); r != nil { 385 panic(AddStackFrame(expr.Pos, r)) 386 } 387 }() 388 if expr.If != nil && !s.interpretExpression(expr.If.Condition).IsTruthy() { 389 return s.interpretExpression(expr.If.Else) 390 } 391 var obj pyObject 392 if expr.Val != nil { 393 obj = s.interpretValueExpression(expr.Val) 394 } else if expr.UnaryOp != nil { 395 obj = s.interpretValueExpression(&expr.UnaryOp.Expr) 396 if expr.UnaryOp.Op == "not" { 397 if obj.IsTruthy() { 398 obj = False 399 } else { 400 obj = True 401 } 402 } else { 403 i, ok := obj.(pyInt) 404 s.Assert(ok, "Unary - can only be applied to an integer") 405 obj = pyInt(-int(i)) 406 } 407 } 408 for _, op := range expr.Op { 409 switch op.Op { 410 case And, Or: 411 // Careful here to mimic lazy-evaluation semantics (import for `x = x or []` etc) 412 if obj.IsTruthy() == (op.Op == And) { 413 obj = s.interpretExpression(op.Expr) 414 } 415 case Equal: 416 obj = newPyBool(reflect.DeepEqual(obj, s.interpretExpression(op.Expr))) 417 case NotEqual: 418 obj = newPyBool(!reflect.DeepEqual(obj, s.interpretExpression(op.Expr))) 419 case Is: 420 // Is only works on boolean types. 421 b1, isBool1 := obj.(pyBool) 422 b2, isBool2 := s.interpretExpression(op.Expr).(pyBool) 423 obj = newPyBool(isBool1 && isBool2 && b1 == b2) 424 case In, NotIn: 425 // the implementation of in is defined by the right-hand side, not the left. 426 obj = s.interpretExpression(op.Expr).Operator(op.Op, obj) 427 default: 428 obj = obj.Operator(op.Op, s.interpretExpression(op.Expr)) 429 } 430 } 431 return obj 432 } 433 434 func (s *scope) interpretValueExpression(expr *ValueExpression) pyObject { 435 obj := s.interpretValueExpressionPart(expr) 436 if expr.Slice != nil { 437 if expr.Slice.Colon == "" { 438 // Indexing, much simpler... 439 s.Assert(expr.Slice.End == nil, "Invalid syntax") 440 obj = obj.Operator(Index, s.interpretExpression(expr.Slice.Start)) 441 } else { 442 obj = s.interpretSlice(obj, expr.Slice) 443 } 444 } 445 if expr.Property != nil { 446 obj = s.interpretIdent(obj.Property(expr.Property.Name), expr.Property) 447 } else if expr.Call != nil { 448 obj = s.callObject("", obj, expr.Call) 449 } 450 return obj 451 } 452 453 func (s *scope) interpretValueExpressionPart(expr *ValueExpression) pyObject { 454 if expr.Ident != nil { 455 obj := s.Lookup(expr.Ident.Name) 456 if len(expr.Ident.Action) == 0 { 457 return obj // fast path 458 } 459 return s.interpretIdent(obj, expr.Ident) 460 } else if expr.String != "" { 461 // Strings are surrounded by quotes to make it easier for the parser; here they come off again. 462 return pyString(stringLiteral(expr.String)) 463 } else if expr.Int != nil { 464 return pyInt(expr.Int.Int) 465 } else if expr.Bool != "" { 466 return s.Lookup(expr.Bool) 467 } else if expr.List != nil { 468 return s.interpretList(expr.List) 469 } else if expr.Dict != nil { 470 return s.interpretDict(expr.Dict) 471 } else if expr.Tuple != nil { 472 // Parentheses can also indicate precedence; a single parenthesised expression does not create a list object. 473 l := s.interpretList(expr.Tuple) 474 if len(l) == 1 && expr.Tuple.Comprehension == nil { 475 return l[0] 476 } 477 return l 478 } else if expr.Lambda != nil { 479 // A lambda is just an inline function definition with a single return statement. 480 stmt := &Statement{} 481 stmt.Return = &ReturnStatement{ 482 Values: []*Expression{&expr.Lambda.Expr}, 483 } 484 return newPyFunc(s, &FuncDef{ 485 Name: "<lambda>", 486 Arguments: expr.Lambda.Arguments, 487 Statements: []*Statement{stmt}, 488 }) 489 } 490 return None 491 } 492 493 func (s *scope) interpretSlice(obj pyObject, sl *Slice) pyObject { 494 lst, ok1 := obj.(pyList) 495 str, ok2 := obj.(pyString) 496 s.Assert(ok1 || ok2, "Unsliceable type "+obj.Type()) 497 start := s.interpretSliceExpression(obj, sl.Start, 0) 498 end := s.interpretSliceExpression(obj, sl.End, pyInt(obj.Len())) 499 if ok1 { 500 return lst[start:end] 501 } 502 return str[start:end] 503 } 504 505 // interpretSliceExpression interprets one of the begin or end parts of a slice. 506 // expr may be null, if it is the value of def is used instead. 507 func (s *scope) interpretSliceExpression(obj pyObject, expr *Expression, def pyInt) pyInt { 508 if expr == nil { 509 return def 510 } 511 return pyIndex(obj, s.interpretExpression(expr), true) 512 } 513 514 func (s *scope) interpretIdent(obj pyObject, expr *IdentExpr) pyObject { 515 name := expr.Name 516 for _, action := range expr.Action { 517 if action.Property != nil { 518 name = action.Property.Name 519 obj = s.interpretIdent(obj.Property(name), action.Property) 520 } else if action.Call != nil { 521 obj = s.callObject(name, obj, action.Call) 522 } 523 } 524 return obj 525 } 526 527 func (s *scope) interpretIdentStatement(stmt *IdentStatement) { 528 if stmt.Index != nil { 529 // Need to special-case these, because types are immutable so we can't return a modifiable reference to them. 530 obj := s.Lookup(stmt.Name) 531 idx := s.interpretExpression(stmt.Index.Expr) 532 if stmt.Index.Assign != nil { 533 obj.IndexAssign(idx, s.interpretExpression(stmt.Index.Assign)) 534 } else { 535 obj.IndexAssign(idx, obj.Operator(Index, idx).Operator(Add, s.interpretExpression(stmt.Index.AugAssign))) 536 } 537 } else if stmt.Unpack != nil { 538 obj := s.interpretExpression(stmt.Unpack.Expr) 539 l, ok := obj.(pyList) 540 s.Assert(ok, "Cannot unpack type %s", l.Type()) 541 // This is a little awkward because the first item here is the name of the ident node. 542 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)) 543 s.Set(stmt.Name, l[0]) 544 for i, name := range stmt.Unpack.Names { 545 s.Set(name, l[i+1]) 546 } 547 } else if stmt.Action.Property != nil { 548 s.interpretIdent(s.Lookup(stmt.Name).Property(stmt.Action.Property.Name), stmt.Action.Property) 549 } else if stmt.Action.Call != nil { 550 s.callObject(stmt.Name, s.Lookup(stmt.Name), stmt.Action.Call) 551 } else if stmt.Action.Assign != nil { 552 s.Set(stmt.Name, s.interpretExpression(stmt.Action.Assign)) 553 } else if stmt.Action.AugAssign != nil { 554 // The only augmented assignment operation we support is +=, and it's implemented 555 // exactly as x += y -> x = x + y since that matches the semantics of Go types. 556 s.Set(stmt.Name, s.Lookup(stmt.Name).Operator(Add, s.interpretExpression(stmt.Action.AugAssign))) 557 } 558 } 559 560 func (s *scope) interpretList(expr *List) pyList { 561 if expr.Comprehension == nil { 562 return pyList(s.evaluateExpressions(expr.Values)) 563 } 564 cs := s.NewScope() 565 l := s.iterate(expr.Comprehension.Expr) 566 ret := make(pyList, 0, len(l)) 567 cs.evaluateComprehension(l, expr.Comprehension, func(li pyObject) { 568 if len(expr.Values) == 1 { 569 ret = append(ret, cs.interpretExpression(expr.Values[0])) 570 } else { 571 ret = append(ret, pyList(cs.evaluateExpressions(expr.Values))) 572 } 573 }) 574 return ret 575 } 576 577 func (s *scope) interpretDict(expr *Dict) pyObject { 578 if expr.Comprehension == nil { 579 d := make(pyDict, len(expr.Items)) 580 for _, v := range expr.Items { 581 d.IndexAssign(s.interpretExpression(&v.Key), s.interpretExpression(&v.Value)) 582 } 583 return d 584 } 585 cs := s.NewScope() 586 l := cs.iterate(expr.Comprehension.Expr) 587 ret := make(pyDict, len(l)) 588 cs.evaluateComprehension(l, expr.Comprehension, func(li pyObject) { 589 ret.IndexAssign(cs.interpretExpression(&expr.Items[0].Key), cs.interpretExpression(&expr.Items[0].Value)) 590 }) 591 return ret 592 } 593 594 // evaluateComprehension handles iterating a comprehension's loops. 595 // The provided callback function is called with each item to be added to the result. 596 func (s *scope) evaluateComprehension(l pyList, comp *Comprehension, callback func(pyObject)) { 597 if comp.Second != nil { 598 for _, li := range l { 599 s.unpackNames(comp.Names, li) 600 for _, li := range s.iterate(comp.Second.Expr) { 601 if s.evaluateComprehensionExpression(comp, comp.Second.Names, li) { 602 callback(li) 603 } 604 } 605 } 606 } else { 607 for _, li := range l { 608 if s.evaluateComprehensionExpression(comp, comp.Names, li) { 609 callback(li) 610 } 611 } 612 } 613 } 614 615 // evaluateComprehensionExpression runs an expression from a list or dict comprehension, and returns true if the caller 616 // should continue to use it, or false if it's been filtered out of the comprehension. 617 func (s *scope) evaluateComprehensionExpression(comp *Comprehension, names []string, li pyObject) bool { 618 s.unpackNames(names, li) 619 return comp.If == nil || s.interpretExpression(comp.If).IsTruthy() 620 } 621 622 // unpackNames unpacks the given object into this scope. 623 func (s *scope) unpackNames(names []string, obj pyObject) { 624 if len(names) == 1 { 625 s.Set(names[0], obj) 626 } else { 627 l, ok := obj.(pyList) 628 s.Assert(ok, "Cannot unpack %s into %s", obj.Type(), names) 629 s.Assert(len(l) == len(names), "Incorrect number of values to unpack; expected %d, got %d", len(names), len(l)) 630 for i, name := range names { 631 s.Set(name, l[i]) 632 } 633 } 634 } 635 636 // iterate returns the result of the given expression as a pyList, which is our only iterable type. 637 func (s *scope) iterate(expr *Expression) pyList { 638 o := s.interpretExpression(expr) 639 l, ok := o.(pyList) 640 if !ok { 641 if l, ok := o.(pyFrozenList); ok { 642 return l.pyList 643 } 644 } 645 s.Assert(ok, "Non-iterable type %s; must be a list", o.Type()) 646 return l 647 } 648 649 // evaluateExpressions runs a series of Python expressions in this scope and creates a series of concrete objects from them. 650 func (s *scope) evaluateExpressions(exprs []*Expression) []pyObject { 651 l := make(pyList, len(exprs)) 652 for i, v := range exprs { 653 l[i] = s.interpretExpression(v) 654 } 655 return l 656 } 657 658 // stringLiteral converts a parsed string literal (which is still surrounded by quotes) to an unquoted version. 659 func stringLiteral(s string) string { 660 return s[1 : len(s)-1] 661 } 662 663 // callObject attempts to call the given object 664 func (s *scope) callObject(name string, obj pyObject, c *Call) pyObject { 665 // We only allow function objects to be called, so don't bother making it part of the pyObject interface. 666 f, ok := obj.(*pyFunc) 667 if !ok { 668 s.Error("Non-callable object '%s' (is a %s)", name, obj.Type()) 669 } 670 return f.Call(s, c) 671 } 672 673 // Constant returns an object from an expression that describes a constant, 674 // e.g. None, "string", 42, [], etc. It returns nil if the expression cannot be determined to be constant. 675 func (s *scope) Constant(expr *Expression) pyObject { 676 // Technically some of these might be constant (e.g. 'a,b,c'.split(',') or `1 if True else 2`. 677 // That's probably unlikely to be common though - we could do a generalised constant-folding pass 678 // but it's rare that people would write something of that nature in this language. 679 if expr.Optimised != nil && expr.Optimised.Constant != nil { 680 return expr.Optimised.Constant 681 } else if expr.Val == nil || expr.Val.Slice != nil || expr.Val.Property != nil || expr.Val.Call != nil || expr.Op != nil || expr.If != nil { 682 return nil 683 } else if expr.Val.Bool != "" || expr.Val.String != "" || expr.Val.Int != nil { 684 return s.interpretValueExpression(expr.Val) 685 } else if expr.Val.List != nil && expr.Val.List.Comprehension == nil { 686 // Lists can be constant if all their elements are also. 687 for _, v := range expr.Val.List.Values { 688 if s.Constant(v) == nil { 689 return nil 690 } 691 } 692 return s.interpretValueExpression(expr.Val) 693 } 694 // N.B. dicts are not optimised to constants currently because they are mutable (because Go maps have 695 // pointer semantics). It might be nice to be able to do that later but it is probably not critical - 696 // we might also be able to do a more aggressive pass in cases where we know we're passing a constant 697 // to a builtin that won't modify it (e.g. calling build_rule with a constant dict). 698 return nil 699 } 700 701 // pkgFilename returns the filename of the current package, or the empty string if there is none. 702 func (s *scope) pkgFilename() string { 703 if s.pkg != nil { 704 return s.pkg.Filename 705 } 706 return "" 707 }