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 }