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