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