
1 // 2 // Copyright (c) 2006, Brian Frank and Andy Frank 3 // Licensed under the Academic Free License version 3.0 4 // 5 // History: 6 // 19 Jul 06 Brian Frank Creation 7 // 8 9 ** 10 ** Expr 11 ** 12 abstract class Expr : Node 13 { 14 15 ////////////////////////////////////////////////////////////////////////// 16 // Construction 17 ////////////////////////////////////////////////////////////////////////// 18 19 new make(Location location, ExprId id) 20 : super(location) 21 { 22 this.id = id 23 } 24 25 ////////////////////////////////////////////////////////////////////////// 26 // Expr 27 ////////////////////////////////////////////////////////////////////////// 28 29 ** 30 ** Return this expression as an Int literal usable in a tableswitch, 31 ** or null if this Expr doesn't represent a constant Int. Expressions 32 ** which work as table switch cases: int literals and enum constants 33 ** 34 virtual Int asTableSwitchCase() { return null } 35 36 ** 37 ** Return if this expression matches the expected type. 38 ** 39 Bool fits(CType expected) 40 { 41 // sanity check that this expression has been typed 42 if (ctype == null) throw NullErr.make("null ctype: ${this}") 43 actual := ctype 44 45 // void is never any type 46 if (actual.isVoid) return false 47 48 // null can be used for any type (everything is boxed) 49 if (id === ExprId.nullLiteral) return true 50 51 // route to CType.fits 52 return actual.fits(expected) 53 } 54 55 ** 56 ** Return if this expression can be used as the 57 ** left hand side of an assignment expression. 58 ** 59 virtual Bool isAssignable() 60 { 61 return false 62 } 63 64 ** 65 ** Is this a boolean conditional (boolOr/boolAnd) 66 ** 67 virtual Bool isCond() 68 { 69 return false 70 } 71 72 ** 73 ** Does this expression make up a complete statement 74 ** 75 virtual Bool isStmt() 76 { 77 return false 78 } 79 80 ** 81 ** Assignments to instance fields require a temporary local variable. 82 ** 83 virtual Bool assignRequiresTempVar() 84 { 85 return false 86 } 87 88 ** 89 ** Map the list of expressions into their list of types 90 ** 91 static CType[] ctypes(Expr[] exprs) 92 { 93 return (CType[])exprs.map(CType[,]) |Expr e->Obj| { return e.ctype } 94 } 95 96 ** 97 ** Given a list of Expr instances, find the common base type 98 ** they all share. This method does not take into account 99 ** the null literal. It is used for type inference for lists 100 ** and maps. 101 ** 102 static CType commonType(Namespace ns, Expr[] exprs) 103 { 104 exprs = exprs.exclude |Expr e->Bool| { return e.id == ExprId.nullLiteral } 105 return CType.common(ns, ctypes(exprs)) 106 } 107 108 ** 109 ** Return this expression as an ExprStmt 110 ** 111 ExprStmt toStmt() 112 { 113 return ExprStmt.make(this) 114 } 115 116 ** 117 ** Return this expression as serialization text or 118 ** throw exception if not serializable. 119 ** 120 virtual Str serialize() 121 { 122 throw CompilerErr.make("'$id' not serializable", location) 123 } 124 125 ////////////////////////////////////////////////////////////////////////// 126 // Tree 127 ////////////////////////////////////////////////////////////////////////// 128 129 Expr walk(Visitor v) 130 { 131 walkChildren(v) 132 return v.visitExpr(this) 133 } 134 135 virtual Void walkChildren(Visitor v) 136 { 137 } 138 139 static Expr walkExpr(Visitor v, Expr expr) 140 { 141 if (expr == null) return null 142 return expr.walk(v) 143 } 144 145 static Expr[] walkExprs(Visitor v, Expr[] exprs) 146 { 147 for (i:=0; i<exprs.size; ++i) 148 { 149 expr := exprs[i] 150 if (expr != null) 151 { 152 replace := expr.walk(v) 153 if (expr !== replace) 154 exprs[i] = replace 155 } 156 } 157 return exprs 158 } 159 160 ////////////////////////////////////////////////////////////////////////// 161 // Debug 162 ////////////////////////////////////////////////////////////////////////// 163 164 override abstract Str toStr() 165 166 override Void print(AstWriter out) 167 { 168 out.w(toStr) 169 } 170 171 ////////////////////////////////////////////////////////////////////////// 172 // Fields 173 ////////////////////////////////////////////////////////////////////////// 174 175 readonly ExprId id // expression type identifier 176 CType ctype // type expression resolves to 177 Bool leave := true // leave this expression on the stack 178 } 179 180 ************************************************************************** 181 ** LiteralExpr 182 ************************************************************************** 183 184 ** 185 ** LiteralExpr puts an Bool, Int, Float, Str, Duration, Uri, 186 ** or null constant onto the stack. 187 ** 188 class LiteralExpr : Expr 189 { 190 static LiteralExpr makeFor(Location loc, Namespace ns, Obj val) 191 { 192 switch (val.type) 193 { 194 case Bool.type: 195 return val == true ? 196 make(loc, ExprId.trueLiteral, ns.boolType, true) : 197 make(loc, ExprId.falseLiteral, ns.boolType, false) 198 default: 199 throw Err.make("Unsupported literal type $val.type") 200 } 201 } 202 203 new make(Location location, ExprId id, CType ctype, Obj val) 204 : super(location, id) 205 { 206 this.ctype = ctype 207 this.val = val 208 } 209 210 override Int asTableSwitchCase() 211 { 212 return val as Int 213 } 214 215 override Str serialize() 216 { 217 switch (id) 218 { 219 case ExprId.falseLiteral: return "false" 220 case ExprId.trueLiteral: return "true" 221 case ExprId.intLiteral: return val.toStr 222 case ExprId.floatLiteral: return val.toStr 223 case ExprId.strLiteral: return val.toStr.toCode 224 case ExprId.uriLiteral: return val.toStr.toCode('`') 225 case ExprId.typeLiteral: return val->signature + ".type" 226 case ExprId.durationLiteral: return val.toStr 227 default: return super.serialize 228 } 229 } 230 231 override Str toStr() 232 { 233 switch (id) 234 { 235 case ExprId.nullLiteral: return "null" 236 case ExprId.strLiteral: return "\"" + val.toStr.replace("\n", "\\n") + "\"" 237 case ExprId.typeLiteral: return "${val}.type" 238 case ExprId.uriLiteral: return "`$`"val 239 default: return val.toStr 240 } 241 } 242 243 Obj val // Bool, Int, Float, Str (for Str/Uri), Duration, CType, or null 244 } 245 246 ************************************************************************** 247 ** RangeLiteralExpr 248 ************************************************************************** 249 250 ** 251 ** RangeLiteralExpr creates a Range instance 252 ** 253 class RangeLiteralExpr : Expr 254 { 255 new make(Location location, CType ctype) 256 : super(location, ExprId.rangeLiteral) 257 { 258 this.ctype = ctype 259 } 260 261 override Void walkChildren(Visitor v) 262 { 263 start = start.walk(v) 264 end = end.walk(v) 265 } 266 267 override Str toStr() 268 { 269 if (exclusive) 270 return "${start}...${end}" 271 else 272 return "${start}..${end}" 273 } 274 275 Expr start 276 Expr end 277 Bool exclusive 278 } 279 280 ************************************************************************** 281 ** ListLiteralExpr 282 ************************************************************************** 283 284 ** 285 ** ListLiteralExpr creates a List instance 286 ** 287 class ListLiteralExpr : Expr 288 { 289 new make(Location location, ListType explicitType := null) 290 : super(location, ExprId.listLiteral) 291 { 292 this.explicitType = explicitType 293 } 294 295 override Void walkChildren(Visitor v) 296 { 297 vals = walkExprs(v, vals) 298 } 299 300 override Str serialize() 301 { 302 return format |Expr e->Str| { return e.serialize } 303 } 304 305 override Str toStr() 306 { 307 return format |Expr e->Str| { return e.toStr } 308 } 309 310 Str format(|Expr e->Str| f) 311 { 312 s := StrBuf.make 313 if (explicitType != null) s.add(explicitType.v) 314 s.add("[") 315 if (vals.isEmpty) s.add(",") 316 else vals.each |Expr v, Int i| 317 { 318 if (i > 0) s.add(",") 319 s.add(f(v)) 320 } 321 s.add("]") 322 return s.toStr 323 } 324 325 ListType explicitType 326 Expr[] vals := Expr[,] 327 } 328 329 ************************************************************************** 330 ** MapLiteralExpr 331 ************************************************************************** 332 333 ** 334 ** MapLiteralExpr creates a List instance 335 ** 336 class MapLiteralExpr : Expr 337 { 338 new make(Location location, MapType explicitType := null) 339 : super(location, ExprId.mapLiteral) 340 { 341 this.explicitType = explicitType 342 } 343 344 override Void walkChildren(Visitor v) 345 { 346 keys = walkExprs(v, keys) 347 vals = walkExprs(v, vals) 348 } 349 350 override Str serialize() 351 { 352 return format |Expr e->Str| { return e.serialize } 353 } 354 355 override Str toStr() 356 { 357 return format |Expr e->Str| { return e.toStr } 358 } 359 360 Str format(|Expr e->Str| f) 361 { 362 s := StrBuf.make 363 if (explicitType != null) s.add(explicitType) 364 s.add("[") 365 if (vals.isEmpty) s.add(":") 366 else 367 { 368 keys.size.times |Int i| 369 { 370 if (i > 0) s.add(",") 371 s.add(f(keys[i])).add(":").add(f(vals[i])) 372 } 373 } 374 s.add("]") 375 return s.toStr 376 } 377 378 MapType explicitType 379 Expr[] keys := Expr[,] 380 Expr[] vals := Expr[,] 381 } 382 383 ************************************************************************** 384 ** SimpleLiteralExpr 385 ************************************************************************** 386 387 ** 388 ** SimpleLiteralExpr is convenience for 'ctype.parse(arg)' 389 ** 390 class SimpleLiteralExpr : Expr 391 { 392 new make(Location location, CType ctype, Expr arg) 393 : super(location, ExprId.simpleLiteral) 394 { 395 this.ctype = ctype 396 this.arg = arg 397 } 398 399 override Void walkChildren(Visitor v) 400 { 401 arg = arg.walk(v) 402 } 403 404 override Str serialize() 405 { 406 return "${ctype}(${arg.serialize})" 407 } 408 409 override Str toStr() 410 { 411 return "${ctype}($arg)" 412 } 413 414 Expr arg // str representation 415 CMethod method // parse method (resolved in CheckErrors) 416 } 417 418 ************************************************************************** 419 ** UnaryExpr 420 ************************************************************************** 421 422 ** 423 ** UnaryExpr is used for unary expressions including !, +. 424 ** Note that - is mapped to negate() as a shortcut method. 425 ** 426 class UnaryExpr : Expr 427 { 428 new make(Location location, ExprId id, Token opToken, Expr operand) 429 : super(location, id) 430 { 431 this.opToken = opToken 432 this.operand = operand 433 } 434 435 override Void walkChildren(Visitor v) 436 { 437 operand = operand.walk(v) 438 } 439 440 override Str toStr() 441 { 442 if (id == ExprId.cmpNull) 443 return operand.toStr + " == null" 444 else if (id == ExprId.cmpNotNull) 445 return operand.toStr + " != null" 446 else 447 return opToken.toStr + operand.toStr 448 } 449 450 Token opToken // operator token type (Token.bang, etc) 451 Expr operand // operand expression 452 453 } 454 455 ************************************************************************** 456 ** BinaryExpr 457 ************************************************************************** 458 459 ** 460 ** BinaryExpr is used for binary expressions with a left hand side and a 461 ** right hand side including assignment. Note that many common binary 462 ** operations are actually modeled as ShortcutExpr to enable method based 463 ** operator overloading. 464 ** 465 class BinaryExpr : Expr 466 { 467 new make(Expr lhs, Token opToken, Expr rhs) 468 : super(lhs.location, opToken.toExprId) 469 { 470 this.lhs = lhs 471 this.opToken = opToken 472 this.rhs = rhs 473 } 474 475 new makeAssign(Expr lhs, Expr rhs, Bool leave := false) 476 : this.make(lhs, Token.assign, rhs) 477 { 478 this.ctype = lhs.ctype 479 this.leave = leave 480 } 481 482 override Bool isStmt() { return id === ExprId.assign } 483 484 override Void walkChildren(Visitor v) 485 { 486 lhs = lhs.walk(v) 487 rhs = rhs.walk(v) 488 } 489 490 override Str serialize() 491 { 492 if (id === ExprId.assign) 493 return "${lhs.serialize}=${rhs.serialize}" 494 else 495 return super.serialize 496 } 497 498 override Str toStr() 499 { 500 return "($lhs $opToken $rhs)" 501 } 502 503 Token opToken // operator token type (Token.and, etc) 504 Expr lhs // left hand side 505 Expr rhs // right hand side 506 MethodVar tempVar // temp local var to store field assignment leaves 507 508 } 509 510 ************************************************************************** 511 ** CondExpr 512 ************************************************************************** 513 514 ** 515 ** CondExpr is used for || and && short-circuit boolean conditionals. 516 ** 517 class CondExpr : Expr 518 { 519 new make(Expr first, Token opToken) 520 : super(first.location, opToken.toExprId) 521 { 522 this.opToken = opToken 523 this.operands = [first] 524 } 525 526 override Bool isCond() { return true } 527 528 override Void walkChildren(Visitor v) 529 { 530 operands = walkExprs(v, operands) 531 } 532 533 override Str toStr() 534 { 535 return operands.join(" $opToken ") 536 } 537 538 Token opToken // operator token type (Token.and, etc) 539 Expr[] operands // list of operands 540 541 } 542 543 ************************************************************************** 544 ** NameExpr 545 ************************************************************************** 546 547 ** 548 ** NameExpr is the base class for an identifier expression which has 549 ** an optional base expression. NameExpr is the base class for 550 ** UnknownVarExpr and CallExpr which are resolved via CallResolver 551 ** 552 abstract class NameExpr : Expr 553 { 554 new make(Location location, ExprId id, Expr target, Str name) 555 : super(location, id) 556 { 557 this.target = target 558 this.name = name 559 } 560 561 override Void walkChildren(Visitor v) 562 { 563 target = walkExpr(v, target) 564 } 565 566 override Str toStr() 567 { 568 if (target != null) 569 return "${target}.${name}" 570 else 571 return name 572 } 573 574 Expr target // base target expression or null 575 Str name // name of variable (local/field/method) 576 } 577 578 ************************************************************************** 579 ** UnknownVarExpr 580 ************************************************************************** 581 582 ** 583 ** UnknownVarExpr is a place holder in the AST for a variable until 584 ** we can figure out what it references: local or slot. We also use 585 ** this class for storage operators before they are resolved to a field. 586 ** 587 class UnknownVarExpr : NameExpr 588 { 589 new make(Location location, Expr target, Str name) 590 : super(location, ExprId.unknownVar, target, name) 591 { 592 } 593 594 new makeStorage(Location location, Expr target, Str name) 595 : super.make(location, ExprId.storage, target, name) 596 { 597 } 598 } 599 600 ************************************************************************** 601 ** CallExpr 602 ************************************************************************** 603 604 ** 605 ** CallExpr is a method call. 606 ** 607 class CallExpr : NameExpr 608 { 609 new make(Location location, Expr target := null, Str name := null, ExprId id := ExprId.call) 610 : super(location, id, target, name) 611 { 612 args = Expr[,] 613 isDynamic = false 614 isCtorChain = false 615 } 616 617 new makeWithMethod(Location location, Expr target, CMethod method, Expr[] args := null) 618 : this.make(location, target, method.name, ExprId.call) 619 { 620 this.method = method 621 622 if (args != null) 623 this.args = args 624 625 if (method.isCtor) 626 ctype = method.parent 627 else 628 ctype = method.returnType 629 } 630 631 override Str toStr() 632 { 633 return toCallStr(true) 634 } 635 636 override Bool isStmt() { return true } 637 638 override Void walkChildren(Visitor v) 639 { 640 target = walkExpr(v, target) 641 args = walkExprs(v, args) 642 } 643 644 override Void print(AstWriter out) 645 { 646 out.w(toCallStr(false)) 647 if (args.size > 0 && args.last is ClosureExpr) 648 args.last.print(out) 649 } 650 651 private Str toCallStr(Bool isToStr) 652 { 653 s := StrBuf.make 654 655 if (target != null) 656 s.add(target).add(isDynamic ? "->" : ".") 657 else if (method != null && (method.isStatic || method.isCtor)) 658 s.add(method.parent.qname).add(".") 659 660 s.add(name).add("(") 661 if (args.last is ClosureExpr) 662 { 663 s.add(args[0..-2].join(", ")).add(") "); 664 if (isToStr) s.add(args.last) 665 } 666 else 667 { 668 s.add(args.join(", ")).add(")") 669 } 670 return s.toStr 671 } 672 673 Expr[] args // Expr[] arguments to pass 674 Bool isDynamic // true if this is a -> dynamic call 675 Bool isCtorChain // true if this is MethodDef.ctorChain call 676 CMethod method // resolved method 677 } 678 679 ************************************************************************** 680 ** ShortcutExpr 681 ************************************************************************** 682 683 ** 684 ** ShortcutExpr is used for operator expressions which are a shortcut 685 ** to a method call: 686 ** a + b -> a.plus(b) 687 ** a - b -> a.minus(b) 688 ** a * b -> a.star(b) 689 ** a / b -> a.slash(b) 690 ** a % b -> a.percent(b) 691 ** a[b] -> a.get(b) 692 ** a[b] = c -> a.set(b, c) 693 ** a[b] -> a.slice(b) if b is Range 694 ** a[b] = c -> a.splice(b, c) if b is Range 695 ** a << b -> a.lshift(b) 696 ** a >> b -> a.rshift(b) 697 ** a & b -> a.amp(b) 698 ** a | b -> a.pipe(b) 699 ** a ^ b -> a.caret(b) 700 ** ~a -> a.tilde() 701 ** -a -> a.negate() 702 ** ++a, a++ -> a.increment() 703 ** --a, a-- -> a.decrement() 704 ** a == b -> a.equals(b) 705 ** a != b -> ! a.equals(b) 706 ** a <=> -> a.compare(b) 707 ** a > b -> a.compare(b) > 0 708 ** a >= b -> a.compare(b) >= 0 709 ** a < b -> a.compare(b) < 0 710 ** a <= b -> a.compare(b) <= 0 711 ** 712 class ShortcutExpr : CallExpr 713 { 714 new makeUnary(Location loc, Token opToken, Expr operand) 715 : super.make(loc, null, null, ExprId.shortcut) 716 { 717 this.op = opToken.toShortcutOp(1) 718 this.opToken = opToken 719 this.name = op.methodName 720 this.target = operand 721 } 722 723 new makeBinary(Expr lhs, Token opToken, Expr rhs) 724 : super.make(lhs.location, null, null, ExprId.shortcut) 725 { 726 this.op = opToken.toShortcutOp(2) 727 this.opToken = opToken 728 this.name = op.methodName 729 this.target = lhs 730 this.args.add(rhs) 731 } 732 733 new makeGet(Location loc, Expr target, Expr index) 734 : super.make(loc, null, null, ExprId.shortcut) 735 { 736 this.op = ShortcutOp.get 737 this.opToken = Token.lbracket 738 this.name = op.methodName 739 this.target = target 740 this.args.add(index) 741 } 742 743 new makeFrom(ShortcutExpr from) 744 : super.make(from.location, null, null, ExprId.shortcut) 745 { 746 this.op = from.op 747 this.opToken = from.opToken 748 this.name = from.name 749 this.target = from.target 750 this.args = from.args 751 this.isPostfixLeave = from.isPostfixLeave 752 } 753 754 override Bool assignRequiresTempVar() 755 { 756 return isAssignable 757 } 758 759 override Bool isAssignable() 760 { 761 return op == ShortcutOp.get 762 } 763 764 override Bool isStmt() { return isAssign || op === ShortcutOp.set } 765 766 Bool isAssign() { return opToken.isAssign } 767 768 Bool isStrConcat() 769 { 770 return opToken == Token.plus && (target.ctype.isStr || args.first.ctype.isStr) 771 } 772 773 override Str toStr() 774 { 775 if (op == ShortcutOp.get) return "${target}[$args.first]" 776 if (op == ShortcutOp.increment) return isPostfixLeave ? "${target}++" : "++${target}" 777 if (op == ShortcutOp.decrement) return isPostfixLeave ? "${target}--" : "--${target}" 778 if (isAssign) return "${target} ${opToken} ${args.first}" 779 if (op.degree == 1) return "${opToken}${target}" 780 if (op.degree == 2) return "(${target} ${opToken} ${args.first})" 781 return super.toStr 782 } 783 784 override Void print(AstWriter out) 785 { 786 out.w(toStr()) 787 } 788 789 ShortcutOp op 790 Token opToken 791 Bool isPostfixLeave := false // x++ or x-- (must have Expr.leave set too) 792 MethodVar tempVar // temp local var to store += to field/indexed 793 } 794 795 ** 796 ** IndexedAssignExpr is a subclass of ShortcutExpr used 797 ** in situations like x[y] += z where we need keep of two 798 ** extra scratch variables and the get's matching set method. 799 ** Note this class models the top x[y] += z, NOT the get target 800 ** which is x[y]. 801 ** 802 ** In this example, IndexedAssignExpr shortcuts Int.plus and 803 ** its target shortcuts List.get: 804 ** x := [2] 805 ** x[0] += 3 806 ** 807 class IndexedAssignExpr : ShortcutExpr 808 { 809 new makeFrom(ShortcutExpr from) 810 : super.makeFrom(from) 811 { 812 } 813 814 MethodVar scratchA 815 MethodVar scratchB 816 CMethod setMethod 817 } 818 819 ************************************************************************** 820 ** FieldExpr 821 ************************************************************************** 822 823 ** 824 ** FieldExpr is used for a field variable access. 825 ** 826 class FieldExpr : Expr 827 { 828 new make(Location location, Expr target := null, CField field := null, Bool useAccessor := true) 829 : super(location, ExprId.field) 830 { 831 this.target = target 832 this.useAccessor = useAccessor 833 if (field != null) 834 { 835 this.name = field.name 836 this.field = field 837 this.ctype = field.fieldType 838 } 839 } 840 841 override Bool isAssignable() { return true } 842 843 override Bool assignRequiresTempVar() { return !field.isStatic } 844 845 override Int asTableSwitchCase() 846 { 847 // TODO - this should probably be tightened up if we switch to const 848 if (field.isStatic && field.parent.isEnum && ctype.isEnum) 849 { 850 switch (field.type) 851 { 852 case ReflectField.type: 853 ifield := field as ReflectField 854 return ((Enum)ifield.f.get).ordinal 855 case FieldDef.type: 856 fieldDef := field as FieldDef 857 enumDef := fieldDef.parentDef.enumDef(field.name) 858 if (enumDef != null) return enumDef.ordinal 859 default: 860 throw CompilerErr.make("Invalid field for tableswitch: " + field.type, location) 861 } 862 } 863 return null 864 } 865 866 override Void walkChildren(Visitor v) 867 { 868 target = walkExpr(v, target) 869 } 870 871 override Str serialize() 872 { 873 if (target != null && target.id === ExprId.withBase) 874 return "$name" 875 876 if (field.isStatic) 877 { 878 if (field.parent.isFloat) 879 { 880 switch (name) 881 { 882 case "nan": return "sys::Float(\"NaN\")" 883 case "posInf": return "sys::Float(\"INF\")" 884 case "negInf": return "sys::Float(\"-INF\")" 885 } 886 } 887 888 if (field.isEnum) 889 return "${field.parent.qname}(\"$name\")" 890 } 891 892 return super.serialize 893 } 894 895 override Str toStr() 896 { 897 s := StrBuf.make 898 if (target != null) s.add(target).add("."); 899 if (!useAccessor) s.add("@") 900 s.add(name) 901 return s.toStr 902 } 903 904 Expr target // target of field access 905 Str name // unresolved name of field to get/set 906 CField field // resolved field 907 Bool useAccessor // false if access using '@' storage operator 908 } 909 910 ************************************************************************** 911 ** LocalVarExpr 912 ************************************************************************** 913 914 ** 915 ** LocalVarExpr is used to access a local variable stored in a register. 916 ** 917 class LocalVarExpr : Expr 918 { 919 new make(Location location, MethodVar var, ExprId id := ExprId.localVar) 920 : super(location, id) 921 { 922 if (var != null) 923 { 924 this.var = var 925 this.ctype = var.ctype 926 } 927 } 928 929 override Bool isAssignable() { return true } 930 931 override Bool assignRequiresTempVar() { return var.usedInClosure } 932 933 virtual Int register() { return var.register } 934 935 override Str toStr() 936 { 937 if (var == null) return "???" 938 return var.name 939 } 940 941 MethodVar var // bound variable 942 943 // used to mark a local var access that should not be 944 // pulled out into cvars, even if var.usedInClosure is true 945 Bool noRemapToCvars := false 946 } 947 948 ************************************************************************** 949 ** ThisExpr 950 ************************************************************************** 951 952 ** 953 ** ThisExpr models the "this" keyword to access the implicit this 954 ** local variable always stored in register zero. 955 ** 956 class ThisExpr : LocalVarExpr 957 { 958 new make(Location location, CType ctype := null) 959 : super(location, null, ExprId.thisExpr) 960 { 961 this.ctype = ctype 962 } 963 964 override Bool isAssignable() { return false } 965 966 override Int register() { return 0 } 967 968 override Str toStr() 969 { 970 return "this" 971 } 972 } 973 974 ************************************************************************** 975 ** SuperExpr 976 ************************************************************************** 977 978 ** 979 ** SuperExpr is used to access super class slots. It always references 980 ** the implicit this local variable stored in register zero, but the 981 ** super class's slot definitions. 982 ** 983 class SuperExpr : LocalVarExpr 984 { 985 new make(Location location, CType explicitType := null) 986 : super(location, null, ExprId.superExpr) 987 { 988 this.explicitType = explicitType 989 } 990 991 override Bool isAssignable() { return false } 992 993 override Int register() { return 0 } 994 995 override Str toStr() 996 { 997 if (explicitType != null) 998 return "${explicitType}.super" 999 else 1000 return "super" 1001 } 1002 1003 CType explicitType // if "named super" 1004 } 1005 1006 ************************************************************************** 1007 ** StaticTargetExpr 1008 ************************************************************************** 1009 1010 ** 1011 ** StaticTargetExpr wraps a type reference as an Expr for use as 1012 ** a target in a static field access or method call 1013 ** 1014 class StaticTargetExpr : Expr 1015 { 1016 new make(Location location, CType ctype) 1017 : super(location, ExprId.staticTarget) 1018 { 1019 this.ctype = ctype 1020 } 1021 1022 override Str toStr() 1023 { 1024 return ctype.signature 1025 } 1026 } 1027 1028 ************************************************************************** 1029 ** TypeCheckExpr 1030 ************************************************************************** 1031 1032 ** 1033 ** TypeCheckExpr is an expression which is composed of an arbitrary 1034 ** expression and a type - is, as, & casts 1035 ** 1036 class TypeCheckExpr : Expr 1037 { 1038 new make(Location location, ExprId id, Expr target, CType check) 1039 : super(location, id) 1040 { 1041 this.target = target 1042 this.check = check 1043 } 1044 1045 new cast(Expr target, CType to) 1046 : super.make(target.location, ExprId.cast) 1047 { 1048 this.target = target 1049 this.check = to 1050 } 1051 1052 override Void walkChildren(Visitor v) 1053 { 1054 target = walkExpr(v, target) 1055 } 1056 1057 override Str toStr() 1058 { 1059 switch (id) 1060 { 1061 case ExprId.isExpr: return "($target is $check)" 1062 case ExprId.asExpr: return "($target as $check)" 1063 case ExprId.cast: return "($check)$target" 1064 default: throw Err.make(id.toStr) 1065 } 1066 } 1067 1068 Expr target 1069 CType check 1070 } 1071 1072 ************************************************************************** 1073 ** TernaryExpr 1074 ************************************************************************** 1075 1076 ** 1077 ** TernaryExpr is used for the ternary expression <cond> ? <true> : <false> 1078 ** 1079 class TernaryExpr : Expr 1080 { 1081 new make(Expr condition, Expr trueExpr, Expr falseExpr) 1082 : super(condition.location, ExprId.ternary) 1083 { 1084 this.condition = condition 1085 this.trueExpr = trueExpr 1086 this.falseExpr = falseExpr 1087 } 1088 1089 override Void walkChildren(Visitor v) 1090 { 1091 condition = condition.walk(v) 1092 trueExpr = trueExpr.walk(v) 1093 falseExpr = falseExpr.walk(v) 1094 } 1095 1096 override Str toStr() 1097 { 1098 return "$condition ? $trueExpr : $falseExpr" 1099 } 1100 1101 Expr condition // boolean test 1102 Expr trueExpr // result of expression if condition is true 1103 Expr falseExpr // result of expression if condition is false 1104 } 1105 1106 ************************************************************************** 1107 ** WithBlockExpr 1108 ************************************************************************** 1109 1110 ** 1111 ** WithBlockExpr is used enclose a series of sub-expressions 1112 ** against a base expression: 1113 ** base { a = b; c() } 1114 ** Translates to: 1115 ** temp := base 1116 ** temp.a = b 1117 ** temp.c() 1118 ** 1119 class WithBlockExpr : Expr 1120 { 1121 new make(Expr base) 1122 : super(base.location, ExprId.withBlock) 1123 { 1124 this.base = base 1125 this.subs = Expr[,] 1126 } 1127 1128 override Void walkChildren(Visitor v) 1129 { 1130 base = base.walk(v) 1131 ctype = base.ctype 1132 subs = walkExprs(v, subs) 1133 } 1134 1135 override Str serialize() 1136 { 1137 if (base.id != ExprId.call || base->method->isCtor != true || 1138 base->name != "make" || base->args->size != 0) 1139 return super.serialize 1140 1141 s := StrBuf.make 1142 s.add("${base->target}{") 1143 subs.each |Expr sub| { s.add("$sub.serialize;") } 1144 s.add("}") 1145 return s.toStr 1146 } 1147 1148 override Str toStr() 1149 { 1150 s := StrBuf.make 1151 s.add("$base { ") 1152 subs.each |Expr sub| { s.add("$sub; ") } 1153 s.add("}") 1154 return s.toStr 1155 } 1156 1157 Expr base // base expression 1158 Expr[] subs // sub-expressions applied to base 1159 } 1160 1161 ** 1162 ** WithBaseExpr is a place holder used as the target of 1163 ** sub-expressions within a with block typed to the with base. 1164 ** 1165 class WithBaseExpr : Expr 1166 { 1167 new make(WithBlockExpr withBlock) 1168 : super(withBlock.location, ExprId.withBase) 1169 { 1170 this.withBlock = withBlock 1171 } 1172 1173 override Str toStr() 1174 { 1175 return "with" 1176 } 1177 1178 override Void walkChildren(Visitor v) 1179 { 1180 // this node never has children, but whenever the 1181 // tree is walked update its ctype from the withBlock 1182 ctype = withBlock.ctype 1183 } 1184 1185 WithBlockExpr withBlock 1186 } 1187 1188 ************************************************************************** 1189 ** CurryExpr 1190 ************************************************************************** 1191 1192 ** 1193 ** CurryExpr is used to "curry" a function into another 1194 ** function thru partially evaluation 1195 ** 1196 class CurryExpr : Expr 1197 { 1198 new make(Location location, Expr operand) 1199 : super(location, ExprId.curry) 1200 { 1201 this.operand = operand 1202 } 1203 1204 override Void walkChildren(Visitor v) 1205 { 1206 operand = operand.walk(v) 1207 } 1208 1209 override Str toStr() 1210 { 1211 return "&$operand" 1212 } 1213 1214 Expr operand 1215 } 1216 1217 ************************************************************************** 1218 ** ClosureExpr 1219 ************************************************************************** 1220 1221 ** 1222 ** ClosureExpr is an "inlined anonymous method" which closes over it's 1223 ** lexical scope. ClosureExpr is placed into the AST by the parser 1224 ** with the code field containing the method implementation. In 1225 ** InitClosures we remap a ClosureExpr to an anonymous class TypeDef 1226 ** which extends Func. The function implementation is moved to the 1227 ** anonymous class's doCall() method. However we leave ClosureExpr 1228 ** in the AST in it's original location with a substitute expression. 1229 ** The substitute expr just creates an instance of the anonymous class. 1230 ** But by leaving the ClosureExpr in the tree, we can keep track of 1231 ** the original lexical scope of the closure. 1232 ** 1233 class ClosureExpr : Expr 1234 { 1235 new make(Location location, TypeDef enclosingType, 1236 MethodDef enclosingMethod, ClosureExpr enclosingClosure, 1237 FuncType signature, Str name) 1238 : super(location, ExprId.closure) 1239 { 1240 this.ctype = signature 1241 this.enclosingType = enclosingType 1242 this.enclosingMethod = enclosingMethod 1243 this.enclosingClosure = enclosingClosure 1244 this.signature = signature 1245 this.name = name 1246 this.code = code 1247 this.usesCvars = false 1248 } 1249 1250 readonly CField outerThisField 1251 { 1252 get 1253 { 1254 if (@outerThisField == null) 1255 { 1256 if (enclosingMethod.isStatic) throw Err.make("Internal error: $location.toLocationStr") 1257 @outerThisField = ClosureVars.makeOuterThisField(this) 1258 } 1259 return @outerThisField 1260 } 1261 } 1262 1263 override Str toStr() 1264 { 1265 return "$signature { ... }" 1266 } 1267 1268 override Void print(AstWriter out) 1269 { 1270 out.w(signature.toStr) 1271 if (substitute != null) 1272 { 1273 out.w(" { substitute: ") 1274 substitute.print(out) 1275 out.w(" }").nl 1276 } 1277 else 1278 { 1279 out.nl 1280 code.print(out) 1281 } 1282 } 1283 1284 // Parse 1285 TypeDef enclosingType // enclosing class 1286 MethodDef enclosingMethod // enclosing method 1287 ClosureExpr enclosingClosure // if nested closure 1288 FuncType signature // parameter and return signature 1289 Block code // moved into a MethodDef in InitClosures 1290 Str name // anonymous class name 1291 1292 // InitClosures 1293 CallExpr substitute // expression to substitute during assembly 1294 TypeDef cls // anonymous class which implements the closure 1295 MethodDef doCall // anonymous class's doCall() with code 1296 1297 // ResolveExpr 1298 Str:MethodVar enclosingLocals // locals in scope 1299 Bool usesCvars // does this guy use vars from outer scope 1300 } 1301 1302 ************************************************************************** 1303 ** ExprId 1304 ************************************************************************** 1305 1306 ** 1307 ** ExprId uniquely identifies the type of expr 1308 ** 1309 enum ExprId 1310 { 1311 nullLiteral, // LiteralExpr 1312 trueLiteral, 1313 falseLiteral, 1314 intLiteral, 1315 floatLiteral, 1316 strLiteral, 1317 durationLiteral, 1318 uriLiteral, 1319 typeLiteral, 1320 rangeLiteral, // RangeLiteralExpr 1321 listLiteral, // ListLiteralExpr 1322 mapLiteral, // MapLiteralExpr 1323 simpleLiteral, // SimpleLiteralExpr 1324 boolNot, // UnaryExpr 1325 cmpNull, 1326 cmpNotNull, 1327 assign, // BinaryExpr 1328 same, 1329 notSame, 1330 boolOr, // CondExpr 1331 boolAnd, 1332 isExpr, // TypeCheckExpr 1333 asExpr, 1334 cast, 1335 call, // CallExpr 1336 shortcut, // ShortcutExpr (has ShortcutOp) 1337 field, // FieldExpr 1338 localVar, // LocalVarExpr 1339 thisExpr, // ThisExpr 1340 superExpr, // SuperExpr 1341 staticTarget, // StaticTargetExpr 1342 unknownVar, // UnknownVarExpr 1343 storage, 1344 ternary, // TernaryExpr 1345 withBlock, // WithBlockExpr 1346 withBase, // WithBaseExpr 1347 curry, // CurryExpr 1348 closure // ClosureExpr 1349 } 1350 1351 ************************************************************************** 1352 ** ShortcutId 1353 ************************************************************************** 1354 1355 ** 1356 ** ShortcutOp is a sub-id for ExprId.shortcut which identifies the 1357 ** an shortuct operation and it's method call 1358 ** 1359 enum ShortcutOp 1360 { 1361 plus(2), 1362 minus(2), 1363 star(2), 1364 slash(2), 1365 percent(2), 1366 lshift(2), 1367 rshift(2), 1368 amp(2), 1369 pipe(2), 1370 caret(2), 1371 tilde(1), 1372 negate(1), 1373 increment(1), 1374 decrement(1), 1375 eq(2, "equals"), 1376 cmp(2, "compare"), 1377 get(2), 1378 set(2), 1379 slice(2) 1380 1381 private new make(Int degree, Str methodName := null) 1382 { 1383 this.degree = degree 1384 this.methodName = methodName == null ? name : methodName 1385 } 1386 1387 const Int degree 1388 const Str methodName 1389 }