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  }