github.com/tiagovtristao/plz@v13.4.0+incompatible/src/parse/asp/objects.go (about)

     1  package asp
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"sort"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/thought-machine/please/src/core"
    11  )
    12  
    13  // A pyObject is the base type for all interpreter objects.
    14  // Strictly the "py" prefix is a misnomer but it's short and easy to follow...
    15  type pyObject interface {
    16  	// All pyObjects are stringable.
    17  	fmt.Stringer
    18  	// Returns the name of this object's type.
    19  	Type() string
    20  	// Returns true if this object evaluates to something truthy.
    21  	IsTruthy() bool
    22  	// Returns a property of this object with the given name.
    23  	Property(name string) pyObject
    24  	// Invokes the given operator on this object and returns the result.
    25  	Operator(operator Operator, operand pyObject) pyObject
    26  	// Used for index-assignment statements
    27  	IndexAssign(index, value pyObject)
    28  	// Returns the length of this object
    29  	Len() int
    30  }
    31  
    32  // A freezable represents an object that can be frozen into a readonly state.
    33  // Not all pyObjects implement this.
    34  type freezable interface {
    35  	Freeze() pyObject
    36  }
    37  
    38  type pyBool int // Don't ask.
    39  
    40  // True, False and None are the singletons representing those values in Python.
    41  // None isn't really a bool of course, but it's easier to use an instance of bool than another
    42  // custom type.
    43  var (
    44  	True         pyBool = 1
    45  	False        pyBool
    46  	None         pyBool = -1
    47  	FileNotFound pyBool = -2
    48  	// continueIteration is used as a sentinel value to implement the "continue" statement.
    49  	continueIteration pyBool = -3
    50  )
    51  
    52  // newPyBool creates a new bool. It's a minor optimisation to treat them as singletons
    53  // although also means one can write "is True" and have it work (not that you should, really).
    54  func newPyBool(b bool) pyObject {
    55  	if b {
    56  		return True
    57  	}
    58  	return False
    59  }
    60  
    61  func (b pyBool) Type() string {
    62  	return "bool"
    63  }
    64  
    65  func (b pyBool) IsTruthy() bool {
    66  	return b == True
    67  }
    68  
    69  func (b pyBool) Property(name string) pyObject {
    70  	panic("bool object has no property " + name)
    71  }
    72  
    73  func (b pyBool) Operator(operator Operator, operand pyObject) pyObject {
    74  	panic(fmt.Sprintf("operator %s not implemented on type bool", operator))
    75  }
    76  
    77  func (b pyBool) IndexAssign(index, value pyObject) {
    78  	panic("bool type is not indexable")
    79  }
    80  
    81  func (b pyBool) Len() int {
    82  	panic("bool has no len()")
    83  }
    84  
    85  func (b pyBool) String() string {
    86  	if b == None {
    87  		return "None"
    88  	} else if b == True {
    89  		return "True"
    90  	}
    91  	return "False"
    92  }
    93  
    94  type pyInt int
    95  
    96  // pyIndex converts an object that's being used as an index to an int.
    97  func pyIndex(obj, index pyObject, slice bool) pyInt {
    98  	i, ok := index.(pyInt)
    99  	if !ok {
   100  		panic(obj.Type() + " indices must be integers, not " + index.Type())
   101  	} else if i < 0 {
   102  		i = pyInt(obj.Len()) + i // Go doesn't support negative indices
   103  	} else if int(i) > obj.Len() {
   104  		if slice {
   105  			return pyInt(obj.Len())
   106  		}
   107  		panic(obj.Type() + " index out of range")
   108  	}
   109  	return i
   110  }
   111  
   112  func (i pyInt) Type() string {
   113  	return "int"
   114  }
   115  
   116  func (i pyInt) IsTruthy() bool {
   117  	return i != 0
   118  }
   119  
   120  func (i pyInt) Property(name string) pyObject {
   121  	panic("int object has no property " + name)
   122  }
   123  
   124  func (i pyInt) Operator(operator Operator, operand pyObject) pyObject {
   125  	i2, ok := operand.(pyInt)
   126  	if !ok {
   127  		panic("Cannot operate on int and " + operand.Type())
   128  	}
   129  	switch operator {
   130  	case Add:
   131  		return i + i2
   132  	case Subtract:
   133  		return i - i2
   134  	case LessThan:
   135  		return newPyBool(i < i2)
   136  	case GreaterThan:
   137  		return newPyBool(i > i2)
   138  	case LessThanOrEqual:
   139  		return newPyBool(i <= i2)
   140  	case GreaterThanOrEqual:
   141  		return newPyBool(i >= i2)
   142  	case Modulo:
   143  		return i % i2
   144  	case In:
   145  		panic("bad operator: 'in' int")
   146  	}
   147  	panic("unknown operator")
   148  }
   149  func (i pyInt) IndexAssign(index, value pyObject) {
   150  	panic("int type is not indexable")
   151  }
   152  
   153  func (i pyInt) Len() int {
   154  	panic("int has no len()")
   155  }
   156  
   157  func (i pyInt) String() string {
   158  	return strconv.Itoa(int(i))
   159  }
   160  
   161  type pyString string
   162  
   163  func (s pyString) Type() string {
   164  	return "str"
   165  }
   166  
   167  func (s pyString) IsTruthy() bool {
   168  	return s != ""
   169  }
   170  
   171  func (s pyString) Property(name string) pyObject {
   172  	if prop, present := stringMethods[name]; present {
   173  		return prop.Member(s)
   174  	}
   175  	panic("str object has no property " + name)
   176  }
   177  
   178  func (s pyString) Operator(operator Operator, operand pyObject) pyObject {
   179  	s2, ok := operand.(pyString)
   180  	if !ok && operator != Modulo && operator != Index {
   181  		panic("Cannot operate on str and " + operand.Type())
   182  	}
   183  	switch operator {
   184  	case Add:
   185  		return s + s2
   186  	case LessThan:
   187  		return newPyBool(s < s2)
   188  	case GreaterThan:
   189  		return newPyBool(s > s2)
   190  	case LessThanOrEqual:
   191  		return newPyBool(s <= s2)
   192  	case GreaterThanOrEqual:
   193  		return newPyBool(s >= s2)
   194  	case Modulo:
   195  		if ok {
   196  			// Special case: "%s" % "x"
   197  			return pyString(fmt.Sprintf(string(s), s2))
   198  		} else if i, ok := operand.(pyInt); ok {
   199  			// Another one: "%d" % 4
   200  			return pyString(fmt.Sprintf(string(s), i))
   201  		}
   202  		l, ok := operand.(pyList)
   203  		if !ok {
   204  			panic("Argument to string interpolation must be a string or list; was " + operand.Type())
   205  		}
   206  		// Classic issue: can't use a []pyObject as a []interface{} :(
   207  		l2 := make([]interface{}, len(l))
   208  		for i, v := range l {
   209  			l2[i] = v
   210  		}
   211  		return pyString(fmt.Sprintf(string(s), l2...))
   212  	case In:
   213  		return newPyBool(strings.Contains(string(s), string(s2)))
   214  	case NotIn:
   215  		return newPyBool(!strings.Contains(string(s), string(s2)))
   216  	case Index:
   217  		return pyString(s[pyIndex(s, operand, false)])
   218  	}
   219  	panic("unknown operator")
   220  }
   221  
   222  func (s pyString) IndexAssign(index, value pyObject) {
   223  	panic("str type cannot be partially assigned to")
   224  }
   225  
   226  func (s pyString) Len() int {
   227  	return len(s)
   228  }
   229  
   230  func (s pyString) String() string {
   231  	return string(s)
   232  }
   233  
   234  type pyList []pyObject
   235  
   236  func (l pyList) Type() string {
   237  	return "list"
   238  }
   239  
   240  func (l pyList) IsTruthy() bool {
   241  	return len(l) > 0
   242  }
   243  
   244  func (l pyList) Property(name string) pyObject {
   245  	panic("list object has no property " + name)
   246  }
   247  
   248  func (l pyList) Operator(operator Operator, operand pyObject) pyObject {
   249  	switch operator {
   250  	case Add:
   251  		l2, ok := operand.(pyList)
   252  		if !ok {
   253  			if l2, ok := operand.(pyFrozenList); ok {
   254  				return append(l, l2.pyList...)
   255  			}
   256  			panic("Cannot add list and " + operand.Type())
   257  		}
   258  		return append(l, l2...)
   259  	case In, NotIn:
   260  		for _, item := range l {
   261  			if item == operand {
   262  				return newPyBool(operator == In)
   263  			}
   264  		}
   265  		return newPyBool(operator == NotIn)
   266  	case Index:
   267  		return l[pyIndex(l, operand, false)]
   268  	case LessThan:
   269  		// Needed for sorting.
   270  		l2, ok := operand.(pyList)
   271  		if !ok {
   272  			panic("Cannot compare list and " + operand.Type())
   273  		}
   274  		for i, li := range l {
   275  			if i >= len(l2) || l2[i].Operator(LessThan, li).IsTruthy() {
   276  				return False
   277  			} else if li.Operator(LessThan, l2[i]).IsTruthy() {
   278  				return True
   279  			}
   280  		}
   281  		if len(l) < len(l2) {
   282  			return True
   283  		}
   284  		return False
   285  	}
   286  	panic("Unsupported operator on list: " + operator.String())
   287  }
   288  
   289  func (l pyList) IndexAssign(index, value pyObject) {
   290  	i, ok := index.(pyInt)
   291  	if !ok {
   292  		panic("List indices must be integers, not " + index.Type())
   293  	}
   294  	l[i] = value
   295  }
   296  
   297  func (l pyList) Len() int {
   298  	return len(l)
   299  }
   300  
   301  func (l pyList) String() string {
   302  	return fmt.Sprintf("%s", []pyObject(l))
   303  }
   304  
   305  // Freeze freezes this list for further updates.
   306  // Note that this is a "soft" freeze; callers holding the original unfrozen
   307  // reference can still modify it.
   308  func (l pyList) Freeze() pyObject {
   309  	return pyFrozenList{pyList: l}
   310  }
   311  
   312  // A pyFrozenList implements an immutable list.
   313  type pyFrozenList struct{ pyList }
   314  
   315  func (l pyFrozenList) IndexAssign(index, value pyObject) {
   316  	panic("list is immutable")
   317  }
   318  
   319  type pyDict map[string]pyObject // Dicts can only be keyed by strings
   320  
   321  func (d pyDict) Type() string {
   322  	return "dict"
   323  }
   324  
   325  func (d pyDict) IsTruthy() bool {
   326  	return len(d) > 0
   327  }
   328  
   329  func (d pyDict) Property(name string) pyObject {
   330  	// We allow looking up dict members by . as well as by indexing in order to facilitate the config map.
   331  	if obj, present := d[name]; present {
   332  		return obj
   333  	} else if prop, present := dictMethods[name]; present {
   334  		return prop.Member(d)
   335  	}
   336  	panic("dict object has no property " + name)
   337  }
   338  
   339  func (d pyDict) Operator(operator Operator, operand pyObject) pyObject {
   340  	if operator == In || operator == NotIn {
   341  		if s, ok := operand.(pyString); ok {
   342  			_, present := d[string(s)]
   343  			return newPyBool(present == (operator == In))
   344  		}
   345  		return newPyBool(operator == NotIn)
   346  	} else if operator == Index {
   347  		s, ok := operand.(pyString)
   348  		if !ok {
   349  			panic("Dict keys must be strings, not " + operand.Type())
   350  		} else if v, present := d[string(s)]; present {
   351  			return v
   352  		}
   353  		panic("unknown dict key: " + s.String())
   354  	}
   355  	panic("Unsupported operator on dict")
   356  }
   357  
   358  func (d pyDict) IndexAssign(index, value pyObject) {
   359  	key, ok := index.(pyString)
   360  	if !ok {
   361  		panic("Dict keys must be strings, not " + index.Type())
   362  	}
   363  	d[string(key)] = value
   364  }
   365  
   366  func (d pyDict) Len() int {
   367  	return len(d)
   368  }
   369  
   370  func (d pyDict) String() string {
   371  	var b strings.Builder
   372  	b.WriteByte('{')
   373  	started := false
   374  	for _, k := range d.Keys() {
   375  		if started {
   376  			b.WriteString(", ")
   377  		}
   378  		started = true
   379  		b.WriteByte('"')
   380  		b.WriteString(k)
   381  		b.WriteString(`": `)
   382  		b.WriteString(d[k].String())
   383  	}
   384  	b.WriteByte('}')
   385  	return b.String()
   386  }
   387  
   388  // Copy creates a shallow duplicate of this dictionary.
   389  func (d pyDict) Copy() pyDict {
   390  	m := make(pyDict, len(d))
   391  	for k, v := range d {
   392  		m[k] = v
   393  	}
   394  	return m
   395  }
   396  
   397  // Freeze freezes this dict for further updates.
   398  // Note that this is a "soft" freeze; callers holding the original unfrozen
   399  // reference can still modify it.
   400  func (d pyDict) Freeze() pyObject {
   401  	return pyFrozenDict{pyDict: d}
   402  }
   403  
   404  // Keys returns the keys of this dict, in order.
   405  func (d pyDict) Keys() []string {
   406  	ret := make([]string, 0, len(d))
   407  	for k := range d {
   408  		ret = append(ret, k)
   409  	}
   410  	sort.Strings(ret)
   411  	return ret
   412  }
   413  
   414  // A pyFrozenDict implements an immutable python dict.
   415  type pyFrozenDict struct{ pyDict }
   416  
   417  func (d pyFrozenDict) Property(name string) pyObject {
   418  	if name == "setdefault" {
   419  		panic("dict is immutable")
   420  	}
   421  	return d.pyDict.Property(name)
   422  }
   423  
   424  func (d pyFrozenDict) IndexAssign(index, value pyObject) {
   425  	panic("dict is immutable")
   426  }
   427  
   428  type pyFunc struct {
   429  	name       string
   430  	docstring  string
   431  	scope      *scope
   432  	args       []string
   433  	argIndices map[string]int
   434  	defaults   []*Expression
   435  	constants  []pyObject
   436  	types      [][]string
   437  	code       []*Statement
   438  	// If the function is implemented natively, this is the pointer to its real code.
   439  	nativeCode func(*scope, []pyObject) pyObject
   440  	// If the function has been bound as a member function, this is the implicit self argument.
   441  	self pyObject
   442  	// True if this function accepts non-keyword varargs (like the log functions, or zip()).
   443  	varargs bool
   444  	// True if this function accepts arbitrary keyword arguments (e.g. package(), str.format()).
   445  	kwargs bool
   446  	// True if this function may only be called using keyword arguments.
   447  	// This is the case for all builtin build rules, although for now it cannot be specified
   448  	// on any user-defined ones.
   449  	kwargsonly bool
   450  	// return type of the function
   451  	returnType string
   452  }
   453  
   454  func newPyFunc(parentScope *scope, def *FuncDef) pyObject {
   455  	f := &pyFunc{
   456  		name:       def.Name,
   457  		scope:      parentScope,
   458  		args:       make([]string, len(def.Arguments)),
   459  		argIndices: make(map[string]int, len(def.Arguments)),
   460  		constants:  make([]pyObject, len(def.Arguments)),
   461  		types:      make([][]string, len(def.Arguments)),
   462  		code:       def.Statements,
   463  		kwargsonly: def.KeywordsOnly,
   464  		returnType: def.Return,
   465  	}
   466  	if def.Docstring != "" {
   467  		f.docstring = stringLiteral(def.Docstring)
   468  	}
   469  	for i, arg := range def.Arguments {
   470  		f.args[i] = arg.Name
   471  		f.argIndices[arg.Name] = i
   472  		f.types[i] = arg.Type
   473  		if arg.Value != nil {
   474  			if constant := parentScope.Constant(arg.Value); constant != nil {
   475  				f.constants[i] = constant
   476  			} else {
   477  				if f.defaults == nil {
   478  					// Minor optimisation: defaults is lazily allocated
   479  					f.defaults = make([]*Expression, len(def.Arguments))
   480  				}
   481  				f.defaults[i] = arg.Value
   482  			}
   483  		}
   484  		for _, alias := range arg.Aliases {
   485  			f.argIndices[alias] = i
   486  		}
   487  	}
   488  	return f
   489  }
   490  
   491  func (f *pyFunc) Type() string {
   492  	return "function"
   493  }
   494  
   495  func (f *pyFunc) IsTruthy() bool {
   496  	return true
   497  }
   498  
   499  func (f *pyFunc) Property(name string) pyObject {
   500  	panic("function object has no property " + name)
   501  }
   502  
   503  func (f *pyFunc) Operator(operator Operator, operand pyObject) pyObject {
   504  	panic("cannot use operators on a function")
   505  }
   506  
   507  func (f *pyFunc) IndexAssign(index, value pyObject) {
   508  	panic("function type is not indexable")
   509  }
   510  
   511  func (f *pyFunc) Len() int {
   512  	panic("function has no len()")
   513  }
   514  
   515  func (f *pyFunc) String() string {
   516  	return fmt.Sprintf("<function %s>", f.name)
   517  }
   518  
   519  func (f *pyFunc) Call(s *scope, c *Call) pyObject {
   520  	if f.nativeCode != nil {
   521  		if f.kwargs {
   522  			return f.callNative(s.NewScope(), c)
   523  		}
   524  		return f.callNative(s, c)
   525  	}
   526  	s2 := f.scope.NewPackagedScope(s.pkg)
   527  	s2.config = s.config
   528  	s2.Set("CONFIG", s.config) // This needs to be copied across too :(
   529  	s2.Callback = s.Callback
   530  	// Handle implicit 'self' parameter for bound functions.
   531  	args := c.Arguments
   532  	if f.self != nil {
   533  		args = append([]CallArgument{{
   534  			Value: Expression{Optimised: &OptimisedExpression{Constant: f.self}},
   535  		}}, args...)
   536  	}
   537  	for i, a := range args {
   538  		if a.Name != "" { // Named argument
   539  			name := a.Name
   540  			idx, present := f.argIndices[name]
   541  			s.Assert(present || f.kwargs, "Unknown argument to %s: %s", f.name, name)
   542  			if present {
   543  				name = f.args[idx]
   544  			}
   545  			s2.Set(name, f.validateType(s, idx, &a.Value))
   546  		} else {
   547  			s.NAssert(i >= len(f.args), "Too many arguments to %s", f.name)
   548  			s.NAssert(f.kwargsonly, "Function %s can only be called with keyword arguments", f.name)
   549  			s2.Set(f.args[i], f.validateType(s, i, &a.Value))
   550  		}
   551  	}
   552  	// Now make sure any arguments with defaults are set, and check any others have been passed.
   553  	for i, a := range f.args {
   554  		if s2.LocalLookup(a) == nil {
   555  			s2.Set(a, f.defaultArg(s, i, a))
   556  		}
   557  	}
   558  	ret := s2.interpretStatements(f.code)
   559  	if ret == nil {
   560  		return None // Implicit 'return None' in any function that didn't do that itself.
   561  	}
   562  	if f.returnType != "" && ret.Type() != f.returnType {
   563  		return s.Error("Invalid return type %s from function %s, expecting %s", ret.Type(), f.name, f.returnType)
   564  	}
   565  
   566  	return ret
   567  }
   568  
   569  // callNative implements the "calling convention" for functions implemented with native code.
   570  // For performance reasons these are done differently - rather then receiving a pointer to a scope
   571  // they receive their arguments as a slice, in which unpassed arguments are nil.
   572  func (f *pyFunc) callNative(s *scope, c *Call) pyObject {
   573  	args := make([]pyObject, len(f.args))
   574  	offset := 0
   575  	if f.self != nil {
   576  		args[0] = f.self
   577  		offset = 1
   578  	}
   579  	for i, a := range c.Arguments {
   580  		if a.Name != "" { // Named argument
   581  			if idx, present := f.argIndices[a.Name]; present {
   582  				args[idx] = f.validateType(s, idx, &a.Value)
   583  			} else if f.kwargs {
   584  				s.Set(a.Name, s.interpretExpression(&a.Value))
   585  			} else {
   586  				s.Error("Unknown argument to %s: %s", f.name, a.Name)
   587  			}
   588  		} else if i >= len(args) {
   589  			s.Assert(f.varargs, "Too many arguments to %s", f.name)
   590  			args = append(args, s.interpretExpression(&a.Value))
   591  		} else {
   592  			s.NAssert(f.kwargsonly, "Function %s can only be called with keyword arguments", f.name)
   593  			args[i+offset] = f.validateType(s, i+offset, &a.Value)
   594  		}
   595  	}
   596  
   597  	// Now make sure any arguments with defaults are set, and check any others have been passed.
   598  	for i, a := range f.args {
   599  		if args[i] == nil {
   600  			args[i] = f.defaultArg(s, i, a)
   601  		}
   602  	}
   603  	return f.nativeCode(s, args)
   604  }
   605  
   606  // defaultArg returns the default value for an argument, whether it's constant or not.
   607  func (f *pyFunc) defaultArg(s *scope, i int, arg string) pyObject {
   608  	if f.constants[i] != nil {
   609  		return f.constants[i]
   610  	}
   611  	s.Assert(f.defaults != nil && f.defaults[i] != nil, "Missing required argument to %s: %s", f.name, arg)
   612  	return s.interpretExpression(f.defaults[i])
   613  }
   614  
   615  // Member duplicates this function as a member function of the given object.
   616  func (f *pyFunc) Member(obj pyObject) pyObject {
   617  	return &pyFunc{
   618  		name:       f.name,
   619  		scope:      f.scope,
   620  		args:       f.args,
   621  		argIndices: f.argIndices,
   622  		defaults:   f.defaults,
   623  		constants:  f.constants,
   624  		types:      f.types,
   625  		code:       f.code,
   626  		nativeCode: f.nativeCode,
   627  		varargs:    f.varargs,
   628  		kwargs:     f.kwargs,
   629  		self:       obj,
   630  	}
   631  }
   632  
   633  // validateType validates that this argument matches the given type
   634  func (f *pyFunc) validateType(s *scope, i int, expr *Expression) pyObject {
   635  	val := s.interpretExpression(expr)
   636  	if f.types[i] == nil {
   637  		return val
   638  	} else if val == None {
   639  		if f.constants[i] == nil && f.defaults[i] == nil {
   640  			return val
   641  		}
   642  		return f.defaultArg(s, i, f.args[i])
   643  	}
   644  	actual := val.Type()
   645  	for _, t := range f.types[i] {
   646  		if t == actual {
   647  			return val
   648  		}
   649  	}
   650  	// Using integers in place of booleans seems common in Bazel BUILD files :(
   651  	if s.state.Config.Bazel.Compatibility && f.types[i][0] == "bool" && actual == "int" {
   652  		return val
   653  	}
   654  	defer func() {
   655  		panic(AddStackFrame(expr.Pos, recover()))
   656  	}()
   657  	return s.Error("Invalid type for argument %s to %s; expected %s, was %s", f.args[i], f.name, strings.Join(f.types[i], " or "), actual)
   658  }
   659  
   660  // A pyConfig is a wrapper object around Please's global config.
   661  // Initially it was implemented as just a dict but that requires us to spend a lot of time
   662  // copying & duplicating it - this structure instead requires very little to be copied
   663  // on each update.
   664  type pyConfig struct {
   665  	base    pyDict
   666  	overlay pyDict
   667  }
   668  
   669  func (c *pyConfig) String() string {
   670  	return "<global config object>"
   671  }
   672  
   673  func (c *pyConfig) Type() string {
   674  	return "config"
   675  }
   676  
   677  func (c *pyConfig) IsTruthy() bool {
   678  	return true // sure, why not
   679  }
   680  
   681  func (c *pyConfig) Property(name string) pyObject {
   682  	if obj := c.Get(name, nil); obj != nil {
   683  		return obj
   684  	} else if f, present := configMethods[name]; present {
   685  		return f.Member(c)
   686  	}
   687  	panic("Config has no such property " + name)
   688  }
   689  
   690  func (c *pyConfig) Operator(operator Operator, operand pyObject) pyObject {
   691  	s, ok := operand.(pyString)
   692  	if !ok {
   693  		panic("config keys must be strings")
   694  	}
   695  	if operator == In || operator == NotIn {
   696  		v := c.Get(string(s), nil)
   697  		if (v != nil) == (operator == In) {
   698  			return True
   699  		}
   700  		return False
   701  	} else if operator == Index {
   702  		return c.MustGet(string(s))
   703  	}
   704  	panic("Cannot operate on config object")
   705  }
   706  
   707  func (c *pyConfig) IndexAssign(index, value pyObject) {
   708  	key := string(index.(pyString))
   709  	if c.overlay == nil {
   710  		c.overlay = pyDict{key: value}
   711  	} else {
   712  		c.overlay[key] = value
   713  	}
   714  }
   715  
   716  func (c *pyConfig) Len() int {
   717  	panic("Config object has no len()")
   718  }
   719  
   720  // Copy creates a copy of this config object. It does not copy the overlay config, so be careful
   721  // where it is used.
   722  func (c *pyConfig) Copy() *pyConfig {
   723  	return &pyConfig{base: c.base}
   724  }
   725  
   726  // Get implements the get() method, similarly to a dict but looks up in both internal maps.
   727  func (c *pyConfig) Get(key string, fallback pyObject) pyObject {
   728  	if c.overlay != nil {
   729  		if obj, present := c.overlay[key]; present {
   730  			return obj
   731  		}
   732  	}
   733  	if obj, present := c.base[key]; present {
   734  		return obj
   735  	}
   736  	return fallback
   737  }
   738  
   739  // MustGet implements getting items from the config. If the requested item is not present, it panics.
   740  func (c *pyConfig) MustGet(key string) pyObject {
   741  	v := c.Get(key, nil)
   742  	if v == nil {
   743  		panic("unknown config key " + key)
   744  	}
   745  	return v
   746  }
   747  
   748  // Freeze returns a copy of this config that is frozen for further updates.
   749  func (c *pyConfig) Freeze() pyObject {
   750  	return &pyFrozenConfig{pyConfig: *c}
   751  }
   752  
   753  // Merge merges the contents of the given config object into this one.
   754  func (c *pyConfig) Merge(other *pyFrozenConfig) {
   755  	if c.overlay == nil {
   756  		// N.B. We cannot directly copy since this might get mutated again later on.
   757  		c.overlay = make(pyDict, len(other.overlay))
   758  	}
   759  	for k, v := range other.overlay {
   760  		c.overlay[k] = v
   761  	}
   762  }
   763  
   764  // newConfig creates a new pyConfig object from the configuration.
   765  // This is typically only created once at global scope, other scopes copy it with .Copy()
   766  func newConfig(config *core.Configuration) *pyConfig {
   767  	c := make(pyDict, 100)
   768  	v := reflect.ValueOf(config).Elem()
   769  	for i := 0; i < v.NumField(); i++ {
   770  		if field := v.Field(i); field.Kind() == reflect.Struct {
   771  			for j := 0; j < field.NumField(); j++ {
   772  				if tag := field.Type().Field(j).Tag.Get("var"); tag != "" {
   773  					subfield := field.Field(j)
   774  					switch subfield.Kind() {
   775  					case reflect.String:
   776  						c[tag] = pyString(subfield.String())
   777  					case reflect.Bool:
   778  						c[tag] = newPyBool(subfield.Bool())
   779  					case reflect.Slice:
   780  						l := make(pyList, subfield.Len())
   781  						for i := 0; i < subfield.Len(); i++ {
   782  							l[i] = pyString(subfield.Index(i).String())
   783  						}
   784  						c[tag] = l
   785  					case reflect.Struct:
   786  						c[tag] = pyString(subfield.Interface().(fmt.Stringer).String())
   787  					default:
   788  						log.Fatalf("Unknown config field type for %s", tag)
   789  					}
   790  				}
   791  			}
   792  		}
   793  	}
   794  	// Arbitrary build config stuff
   795  	for k, v := range config.BuildConfig {
   796  		c[strings.Replace(strings.ToUpper(k), "-", "_", -1)] = pyString(v)
   797  	}
   798  	// Settings specific to package() which aren't in the config, but it's easier to
   799  	// just put them in now.
   800  	c["DEFAULT_VISIBILITY"] = None
   801  	c["DEFAULT_TESTONLY"] = False
   802  	c["DEFAULT_LICENCES"] = None
   803  	// Bazel supports a 'features' flag to toggle things on and off.
   804  	// We don't but at least let them call package() without blowing up.
   805  	if config.Bazel.Compatibility {
   806  		c["FEATURES"] = pyList{}
   807  	}
   808  	c["OS"] = pyString(config.Build.Arch.OS)
   809  	c["ARCH"] = pyString(config.Build.Arch.Arch)
   810  	return &pyConfig{base: c}
   811  }
   812  
   813  // A pyFrozenConfig is a config object that disallows further updates.
   814  type pyFrozenConfig struct{ pyConfig }
   815  
   816  // IndexAssign always fails, assignments to a pyFrozenConfig aren't allowed.
   817  func (c *pyFrozenConfig) IndexAssign(index, value pyObject) {
   818  	panic("Config object is not assignable in this scope")
   819  }
   820  
   821  // Property disallows setdefault() since it's immutable.
   822  func (c *pyFrozenConfig) Property(name string) pyObject {
   823  	if name == "setdefault" {
   824  		panic("Config object is not assignable in this scope")
   825  	}
   826  	return c.pyConfig.Property(name)
   827  }