-
Notifications
You must be signed in to change notification settings - Fork 29
Expand file tree
/
Copy pathExprTranslation.java
More file actions
855 lines (748 loc) · 36.9 KB
/
ExprTranslation.java
File metadata and controls
855 lines (748 loc) · 36.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
package de.peeeq.wurstscript.translation.imtranslation;
import com.google.common.collect.Lists;
import de.peeeq.wurstscript.WLogger;
import de.peeeq.wurstscript.WurstOperator;
import de.peeeq.wurstscript.ast.*;
import de.peeeq.wurstscript.ast.Element;
import de.peeeq.wurstscript.attributes.AttrFuncDef;
import de.peeeq.wurstscript.attributes.CompileError;
import de.peeeq.wurstscript.attributes.names.FuncLink;
import de.peeeq.wurstscript.attributes.names.NameLink;
import de.peeeq.wurstscript.attributes.names.OtherLink;
import de.peeeq.wurstscript.jassIm.*;
import de.peeeq.wurstscript.types.*;
import de.peeeq.wurstscript.utils.Utils;
import io.vavr.control.Either;
import io.vavr.control.Option;
import org.eclipse.jdt.annotation.Nullable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static de.peeeq.wurstscript.jassIm.JassIm.*;
import static de.peeeq.wurstscript.validation.WurstValidator.isTypeParamNewGeneric;
public class ExprTranslation {
public static ImExpr translate(ExprBinary e, ImTranslator t, ImFunction f) {
return wrapTranslation(e, t, translateIntern(e, t, f));
}
public static ImExpr translate(ExprUnary e, ImTranslator t, ImFunction f) {
return wrapTranslation(e, t, translateIntern(e, t, f));
}
public static ImExpr translate(ExprBoolVal e, ImTranslator t, ImFunction f) {
return wrapTranslation(e, t, translateIntern(e, t, f));
}
public static ImExpr translate(ExprFuncRef e, ImTranslator t, ImFunction f) {
return wrapTranslation(e, t, translateIntern(e, t, f));
}
public static ImExpr translate(ExprIntVal e, ImTranslator t, ImFunction f) {
return wrapTranslation(e, t, translateIntern(e, t, f));
}
public static ImExpr translate(ExprNull e, ImTranslator t, ImFunction f) {
return wrapTranslation(e, t, translateIntern(e, t, f));
}
public static ImExpr translate(ExprRealVal e, ImTranslator t, ImFunction f) {
return wrapTranslation(e, t, translateIntern(e, t, f));
}
public static ImExpr translate(ExprStringVal e, ImTranslator t, ImFunction f) {
return wrapTranslation(e, t, translateIntern(e, t, f));
}
public static ImExpr translate(ExprThis e, ImTranslator t, ImFunction f) {
return wrapTranslation(e, t, translateIntern(e, t, f));
}
public static ImExpr translate(ExprSuper e, ImTranslator t, ImFunction f) {
return wrapTranslation(e, t, translateIntern(e, t, f));
}
public static ImExpr translate(NameRef e, ImTranslator t, ImFunction f) {
return wrapTranslation(e, t, translateIntern(e, t, f));
}
public static ImExpr translate(ExprCast e, ImTranslator t, ImFunction f) {
return wrapTranslation(e, t, translateIntern(e, t, f));
}
public static ImExpr translate(FunctionCall e, ImTranslator t, ImFunction f) {
return wrapTranslation(e, t, translateIntern(e, t, f));
}
public static ImExpr translate(ExprIncomplete e, ImTranslator t, ImFunction f) {
return wrapTranslation(e, t, translateIntern(e, t, f));
}
public static ImExpr translate(ExprNewObject e, ImTranslator t, ImFunction f) {
return wrapTranslation(e, t, translateIntern(e, t, f));
}
public static ImExpr translate(ExprInstanceOf e, ImTranslator t, ImFunction f) {
return wrapTranslation(e, t, translateIntern(e, t, f));
}
private static ImExpr wrapTranslation(Expr e, ImTranslator t, ImExpr translated) {
WurstType actualType = e.attrTypRaw();
WurstType expectedTypRaw = e.attrExpectedTypRaw();
return wrapTranslation(e, t, translated, actualType, expectedTypRaw);
}
static ImExpr wrapLua(Element trace, ImTranslator t, ImExpr translated, WurstType actualType) {
// use ensureType functions for lua
// these functions convert nil to the default value for primitive types (int, string, bool, real)
if (t.isLuaTarget() && actualType instanceof WurstTypeBoundTypeParam) {
WurstTypeBoundTypeParam wtb = (WurstTypeBoundTypeParam) actualType;
@Nullable ImFunction ensureType = null;
switch (wtb.getName()) {
case "integer":
ensureType = t.ensureIntFunc;
break;
case "string":
ensureType = t.ensureStrFunc;
break;
case "boolean":
ensureType = t.ensureBoolFunc;
break;
case "real":
ensureType = t.ensureRealFunc;
break;
}
if(ensureType != null) {
return ImFunctionCall(trace, ensureType, ImTypeArguments(), JassIm.ImExprs(translated), false, CallType.NORMAL);
}
}
return translated;
}
static ImExpr wrapTranslation(Element trace, ImTranslator t, ImExpr translated, WurstType actualType, WurstType expectedTypRaw) {
ImFunction toIndex = null;
ImFunction fromIndex = null;
if (actualType instanceof WurstTypeBoundTypeParam) {
WurstTypeBoundTypeParam wtb = (WurstTypeBoundTypeParam) actualType;
FuncDef fromIndexFunc = wtb.getFromIndex();
if (fromIndexFunc != null) {
fromIndex = t.getFuncFor(fromIndexFunc);
}
}
if (expectedTypRaw instanceof WurstTypeBoundTypeParam) {
WurstTypeBoundTypeParam wtb = (WurstTypeBoundTypeParam) expectedTypRaw;
FuncDef toIndexFunc = wtb.getToIndex();
if (toIndexFunc != null) {
toIndex = t.getFuncFor(toIndexFunc);
}
}
// System.out.println("CAll " + Utils.prettyPrintWithLine(trace));
// System.out.println(" actualType = " + actualType.getFullName());
// System.out.println(" expectedTypRaw = " + expectedTypRaw.getFullName());
if (toIndex != null && fromIndex != null) {
// System.out.println(" --> cancel");
// the two conversions cancel each other out
return wrapLua(trace, t, translated, actualType);
} else if (fromIndex != null) {
// System.out.println(" --> fromIndex");
if(t.isLuaTarget()) {
translated = ImFunctionCall(trace, t.ensureIntFunc, ImTypeArguments(), JassIm.ImExprs(translated), false, CallType.NORMAL);
}
// no ensure type necessary here, because the fromIndex function is already type safe
return ImFunctionCall(trace, fromIndex, ImTypeArguments(), JassIm.ImExprs(translated), false, CallType.NORMAL);
} else if (toIndex != null) {
// System.out.println(" --> toIndex");
return wrapLua(trace, t, ImFunctionCall(trace, toIndex, ImTypeArguments(), JassIm.ImExprs(translated), false, CallType.NORMAL), actualType);
}
return wrapLua(trace, t, translated, actualType);
}
public static ImExpr translateIntern(ExprBinary e, ImTranslator t, ImFunction f) {
ImExpr left = e.getLeft().imTranslateExpr(t, f);
ImExpr right = e.getRight().imTranslateExpr(t, f);
WurstOperator op = e.getOp();
if (e.attrFuncLink() != null) {
// overloaded operator
ImFunction calledFunc = t.getFuncFor(e.attrFuncDef());
return ImFunctionCall(e, calledFunc, ImTypeArguments(), ImExprs(left, right), false, CallType.NORMAL);
}
if (op == WurstOperator.DIV_REAL) {
if (Utils.isJassCode(e)) {
if (e.getLeft().attrTyp().isSubtypeOf(WurstTypeInt.instance(), e)
&& e.getRight().attrTyp().isSubtypeOf(WurstTypeInt.instance(), e)) {
// in jass when we have int1 / int2 this actually means int1
// div int2
op = WurstOperator.DIV_INT;
}
} else {
if (e.getLeft().attrTyp().isSubtypeOf(WurstTypeInt.instance(), e)
&& e.getRight().attrTyp().isSubtypeOf(WurstTypeInt.instance(), e)) {
// we want a real division but have 2 ints so we need to
// multiply with 1.0
// TODO is this really needed or handled in IM->Jass
// translation?
left = ImOperatorCall(WurstOperator.MULT, ImExprs(left, ImRealVal("1.")));
}
}
}
return ImOperatorCall(op, ImExprs(left, right));
}
public static ImExpr translateIntern(ExprUnary e, ImTranslator t, ImFunction f) {
return ImOperatorCall(e.getOpU(), ImExprs(e.getRight().imTranslateExpr(t, f)));
}
public static ImExpr translateIntern(ExprBoolVal e, ImTranslator t, ImFunction f) {
return JassIm.ImBoolVal(e.getValB());
}
public static ImExpr translateIntern(ExprFuncRef e, ImTranslator t, ImFunction f) {
ImFunction func = t.getFuncFor(e.attrFuncDef());
return ImFuncRef(e, func);
}
public static ImExpr translateIntern(ExprIntVal e, ImTranslator t, ImFunction f) {
if (e.attrExpectedTyp() instanceof WurstTypeReal) {
// translate differently when real is expected
return ImRealVal(e.getValI() + ".");
}
return ImIntVal(e.getValI());
}
public static ImExpr translateIntern(ExprNull e, ImTranslator t, ImFunction f) {
WurstType expectedTypeRaw = e.attrExpectedTypRaw();
if (expectedTypeRaw instanceof WurstTypeUnknown) {
e.addError("Cannot use 'null' in this context.");
}
return ImNull(expectedTypeRaw.imTranslateType(t));
}
public static ImExpr translateIntern(ExprRealVal e, ImTranslator t, ImFunction f) {
return ImRealVal(e.getValR());
}
public static ImExpr translateIntern(ExprStringVal e, ImTranslator t, ImFunction f) {
return ImStringVal(e.getValS());
}
public static ImExpr translateIntern(ExprThis e, ImTranslator t, ImFunction f) {
ImVar var = t.getThisVar(f, e);
return ImVarAccess(var);
}
public static ImExpr translateIntern(ExprSuper e, ImTranslator t, ImFunction f) {
ImVar var = t.getThisVar(f, e);
return ImVarAccess(var);
}
public static ImExpr translateIntern(NameRef e, ImTranslator t, ImFunction f) {
return translateNameDef(e, t, f);
}
private static ImExpr translateNameDef(NameRef e, ImTranslator t, ImFunction f) throws CompileError {
NameLink link = e.attrNameLink();
if (link instanceof OtherLink) {
return ((OtherLink) link).translate(e, t, f);
}
NameDef decl = link == null ? null : link.getDef();
if (decl == null) {
// should only happen with gg_ variables
if (!t.isEclipseMode()) {
e.addError("Translation Error: Could not find definition of " + e.getVarName() + ".");
}
return ImHelper.nullExpr();
}
if (decl instanceof VarDef) {
VarDef varDef = (VarDef) decl;
ImVar v = t.getVarFor(varDef);
@Nullable FuncLink indexGetOverload = getIndexGetOverload(e, link);
if (e.attrImplicitParameter() instanceof Expr) {
// we have implicit parameter
// e.g. "someObject.someField"
Expr implicitParam = (Expr) e.attrImplicitParameter();
if (implicitParam.attrTyp() instanceof WurstTypeTuple) {
WurstTypeTuple tupleType = (WurstTypeTuple) implicitParam.attrTyp();
if (e instanceof ExprMemberVar) {
ExprMemberVar e2 = (ExprMemberVar) e;
return translateTupleSelection(t, f, e2);
} else {
throw new CompileError(e.getSource(), "Cannot create tuple access");
}
}
if (e instanceof AstElementWithIndexes) {
if (indexGetOverload != null) {
AstElementWithIndexes withIndexes = (AstElementWithIndexes) e;
ImExpr receiver = JassIm.ImMemberAccess(e, implicitParam.imTranslateExpr(t, f), JassIm.ImTypeArguments(), v, JassIm.ImExprs());
ImExpr index = withIndexes.getIndexes().get(0).imTranslateExpr(t, f);
ImFunction calledFunc = t.getFuncFor(indexGetOverload.getDef());
return ImFunctionCall(e, calledFunc, ImTypeArguments(), ImExprs(receiver, index), false, CallType.NORMAL);
}
ImExpr index1 = implicitParam.imTranslateExpr(t, f);
ImExpr index2 = ((AstElementWithIndexes) e).getIndexes().get(0).imTranslateExpr(t, f);
return JassIm.ImMemberAccess(e, index1, JassIm.ImTypeArguments(), v, JassIm.ImExprs(index2));
} else {
ImExpr index = implicitParam.imTranslateExpr(t, f);
return JassIm.ImMemberAccess(e, index, JassIm.ImTypeArguments(), v, JassIm.ImExprs());
}
} else {
// direct var access
if (e instanceof AstElementWithIndexes) {
if (indexGetOverload != null) {
AstElementWithIndexes withIndexes = (AstElementWithIndexes) e;
ImExpr receiver = ImVarAccess(v);
ImExpr index = withIndexes.getIndexes().get(0).imTranslateExpr(t, f);
ImFunction calledFunc = t.getFuncFor(indexGetOverload.getDef());
return ImFunctionCall(e, calledFunc, ImTypeArguments(), ImExprs(receiver, index), false, CallType.NORMAL);
}
// direct access array var
AstElementWithIndexes withIndexes = (AstElementWithIndexes) e;
if (withIndexes.getIndexes().size() > 1) {
throw new CompileError(e.getSource(), "More than one index is not supported.");
}
ImExpr index = withIndexes.getIndexes().get(0).imTranslateExpr(t, f);
return ImVarArrayAccess(e, v, JassIm.ImExprs(index));
} else {
// not an array var
return ImVarAccess(v);
}
}
} else if (decl instanceof EnumMember) {
EnumMember enumMember = (EnumMember) decl;
int id = t.getEnumMemberId(enumMember);
return ImIntVal(id);
} else {
throw new CompileError(e.getSource(), "Cannot translate reference to " + Utils.printElement(decl));
}
}
private static ImExpr translateTupleSelection(ImTranslator t, ImFunction f, ExprMemberVar mv) {
ImExpr left = mv.getLeft().imTranslateExpr(t, f);
WParameter tupleParam = (WParameter) mv.attrNameDef();
WParameters tupleParams = (WParameters) tupleParam.getParent();
int tupleIndex = tupleParams.indexOf(tupleParam);
if (left instanceof ImLExpr) {
return ImTupleSelection(left, tupleIndex);
} else {
// if tupleExpr is not an l-value (e.g. foo().x)
// store result in intermediate variable first:
ImVar v = ImVar(left.attrTrace(), left.attrTyp(), "temp_tuple", false);
f.getLocals().add(v);
return JassIm.ImStatementExpr(
JassIm.ImStmts(
ImSet(left.attrTrace(), ImVarAccess(v), left)
),
ImTupleSelection(ImVarAccess(v), tupleIndex)
);
}
}
/*
private static ImExpr translateTupleSelection(ImTranslator t, ImFunction f, ExprMemberVar mv) {
List<WParameter> indexes = new ArrayList<>();
Expr expr = mv;
while (true) {
if (expr instanceof ExprMemberVar) {
ExprMemberVar mv2 = (ExprMemberVar) expr;
Expr left = mv2.getLeft();
if (left.attrTyp() instanceof WurstTypeTuple) {
indexes.add(0, (WParameter) mv2.attrNameDef());
expr = left;
continue;
}
}
break;
}
WurstTypeTuple tt = (WurstTypeTuple) expr.attrTyp();
int tupleIndex = 0;
WurstType resultTupleType = null;
for (int i = 0; i < indexes.size(); i++) {
WParameter param = indexes.get(i);
TupleDef tdef = tt.getTupleDef();
int pos = 0;
while (tdef.getParameters().get(pos) != param) {
tupleIndex += tupleSize(tdef.getParameters().get(pos).getTyp().attrTyp());
pos++;
}
resultTupleType = tdef.getParameters().get(pos).getTyp().attrTyp();
if (i < indexes.size() - 1) {
tt = (WurstTypeTuple) tdef.getParameters().get(pos).getTyp().attrTyp();
}
}
ImExpr exprTr = expr.imTranslateExpr(t, f);
if (resultTupleType instanceof WurstTypeTuple) {
// if the result is a tuple, create it:
int tupleSize = tupleSize(resultTupleType);
if (exprTr instanceof ImLExpr
&& (exprTr.attrPurity() instanceof Pure || exprTr.attrPurity() instanceof ReadsGlobals)) {
ImExprs exprs = JassIm.ImExprs();
for (int i = 0; i < tupleSize; i++) {
exprs.add(ImTupleSelection((ImLExpr) exprTr.copy(), tupleIndex + i));
}
return ImTupleExpr(exprs);
} else {
ImVar temp = JassIm.ImVar(expr, exprTr.attrTyp(), "temp", false);
// for impure expressions use a temporary:
f.getLocals().add(temp);
ImExprs exprs = JassIm.ImExprs();
for (int i = 0; i < tupleSize; i++) {
// TODO use temporary var
exprs.add(ImTupleSelection(JassIm.ImVarAccess(temp), tupleIndex + i));
}
return JassIm.ImStatementExpr(JassIm.ImStmts(ImSet(expr, ImVarAccess(temp), exprTr)), ImTupleExpr(exprs));
}
} else {
if (exprTr instanceof ImLExpr) {
return ImTupleSelection((ImLExpr) exprTr, tupleIndex);
} else {
// if tupleExpr is not an l-value (e.g. foo().x)
// store result in intermediate variable first:
ImVar v = ImVar(exprTr.attrTrace(), exprTr.attrTyp(), "temp_tuple", false);
f.getLocals().add(v);
return JassIm.ImStatementExpr(
JassIm.ImStmts(
ImSet(exprTr.attrTrace(), ImVarAccess(v), exprTr)
),
ImTupleSelection(ImVarAccess(v), tupleIndex)
);
}
}
}
*/
/**
* counts the components of a tuple (including nested)
*/
private static int tupleSize(WurstType t) {
if (t instanceof WurstTypeTuple) {
WurstTypeTuple tt = (WurstTypeTuple) t;
int sum = 0;
for (WParameter p : tt.getTupleDef().getParameters()) {
sum += tupleSize(p.getTyp().attrTyp());
}
return sum;
}
// all other types have size 1
return 1;
}
public static ImExpr translateIntern(ExprCast e, ImTranslator t, ImFunction f) {
ImExpr et = e.getExpr().imTranslateExpr(t, f);
ImType toType = e.getTyp().attrTyp().imTranslateType(t);
return JassIm.ImCast(et, toType);
}
public static ImExpr translateIntern(FunctionCall e, ImTranslator t, ImFunction f) {
if (e instanceof ExprMemberMethodDotDot) {
return translateFunctionCall(e, t, f, true);
} else {
return translateFunctionCall(e, t, f, false);
}
}
private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFunction f, boolean returnReveiver) {
if (e.getFuncName().equals("getStackTraceString") && e.attrImplicitParameter() instanceof NoExpr
&& e.getArgs().size() == 0) {
// special built-in error function
return JassIm.ImGetStackTrace();
}
if (e.getFuncName().equals("ExecuteFunc")) {
ExprStringVal s = (ExprStringVal) e.getArgs().get(0);
String exFunc = s.getValS();
NameLink func = Utils.getFirst(e.lookupFuncs(exFunc));
ImFunction executedFunc = t.getFuncFor((TranslatedToImFunction) func.getDef());
return ImFunctionCall(e, executedFunc, ImTypeArguments(), JassIm.ImExprs(), true, CallType.EXECUTE);
}
if (e.getFuncName().equals("compiletime")
&& e.attrImplicitParameter() instanceof NoExpr
&& e.getArgs().size() == 1) {
// special compiletime-expression
return JassIm.ImCompiletimeExpr(e, e.getArgs().get(0).imTranslateExpr(t, f), t.getCompiletimeExpressionsOrder(e));
}
List<Expr> arguments = Lists.newArrayList(e.getArgs());
Expr leftExpr = null;
FunctionDefinition calledFunc = e.attrFuncDef();
if (e.attrImplicitParameter() instanceof Expr) {
// keep implicit parameter
leftExpr = (Expr) e.attrImplicitParameter();
}
// get real func def (override of module function)
boolean useRealFuncDef = true;
if (e instanceof ExprMemberMethod) {
ExprMemberMethod exprMemberMethod = (ExprMemberMethod) e;
WurstType left = exprMemberMethod.getLeft().attrTyp();
if (left instanceof WurstTypeModuleInstanciation) {
// if we have a call like A.foo() and A is a module,
// use this function
useRealFuncDef = false;
}
}
if (calledFunc == null) {
// this must be an ignored function
return ImHelper.nullExpr();
}
if (useRealFuncDef) {
calledFunc = calledFunc.attrRealFuncDef();
}
boolean dynamicDispatch = false;
if (leftExpr instanceof ExprThis && calledFunc == e.attrNearestFuncDef()) {
// recursive self calls are bound statically
// (necessary because jass does not allow mutually recursive calls)
dynamicDispatch = false;
} else if (leftExpr != null
&& isCalledOnDynamicRef(e)
&& calledFunc instanceof FuncDef
&& !((FuncDef) calledFunc).attrIsStatic()) {
// only instance methods participate in dispatch
dynamicDispatch = true;
}
// logging
if (calledFunc instanceof FuncDef) {
FuncDef fd = (FuncDef) calledFunc;
if (WLogger.isTraceEnabled()) WLogger.trace("[DISPATCH] call " + fd.getName()
+ " isStatic=" + fd.attrIsStatic()
+ " dynCtx=" + isCalledOnDynamicRef(e)
+ " -> dynamicDispatch=" + dynamicDispatch);
} else {
if (WLogger.isTraceEnabled()) WLogger.trace("[DISPATCH] call " + calledFunc.getName()
+ " (non-FuncDef)"
+ " dynCtx=" + isCalledOnDynamicRef(e)
+ " -> dynamicDispatch=" + dynamicDispatch);
}
ImExpr receiver = leftExpr == null ? null : leftExpr.imTranslateExpr(t, f);
ImExprs imArgs = translateExprs(arguments, t, f);
if (calledFunc instanceof TupleDef) {
// creating a new tuple...
return ImTupleExpr(imArgs);
}
ImStmts stmts = null;
ImVar tempVar = null;
if (returnReveiver) {
if (leftExpr == null) {
throw new Error("impossible");
}
tempVar = JassIm.ImVar(leftExpr, leftExpr.attrTyp().imTranslateType(t), "receiver", false);
f.getLocals().add(tempVar);
stmts = JassIm.ImStmts(ImSet(e, ImVarAccess(tempVar), receiver));
receiver = JassIm.ImVarAccess(tempVar);
}
ImExpr call;
if (dynamicDispatch) {
ImMethod method = t.getMethodFor((FuncDef) calledFunc);
ImTypeArguments typeArguments = getFunctionCallTypeArguments(
t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables());
call = ImMethodCall(e, method, typeArguments, receiver, imArgs, false);
} else {
ImFunction calledImFunc = t.getFuncFor(calledFunc);
if (receiver != null) {
imArgs.add(0, receiver);
}
ImTypeArguments typeArguments = getFunctionCallTypeArguments(
t, e.attrFunctionSignature(), e, calledImFunc.getTypeVariables());
call = ImFunctionCall(e, calledImFunc, typeArguments, imArgs, false, CallType.NORMAL);
}
if (returnReveiver) {
if (stmts == null) {
throw new Error("impossible");
}
stmts.add(call);
return JassIm.ImStatementExpr(stmts, JassIm.ImVarAccess(tempVar));
} else {
return call;
}
}
private static ImTypeArguments getFunctionCallTypeArguments(ImTranslator tr, FunctionSignature sig, Element location, ImTypeVars typeVariables) {
ImTypeArguments res = ImTypeArguments();
VariableBinding mapping = sig.getMapping();
for (ImTypeVar tv : typeVariables) {
TypeParamDef tp = tr.getTypeParamDef(tv);
if (tp == null) {
// Should not happen, but be defensive: if we cannot map back, just pass through
Map<ImTypeClassFunc, Either<ImMethod, ImFunction>> typeClassBinding = new HashMap<>();
res.add(ImTypeArgument(JassIm.ImTypeVarRef(tv), typeClassBinding));
continue;
}
Option<WurstTypeBoundTypeParam> to = mapping.get(tp);
if (to.isEmpty()) {
if (isTypeParamNewGeneric(tp)) {
ImType tvType = JassIm.ImTypeVarRef(tr.getTypeVar(tp));
res.add(ImTypeArgument(tvType, new HashMap<>()));
continue;
}
throw new CompileError(location, "Type variable " + tp.getName() + " not bound in mapping.");
}
WurstTypeBoundTypeParam t = to.get();
if (!t.isTemplateTypeParameter()) {
continue;
}
ImType type = t.imTranslateType(tr);
// TODO handle constraints
Map<ImTypeClassFunc, Either<ImMethod, ImFunction>> typeClassBinding = new HashMap<>();
res.add(ImTypeArgument(type, typeClassBinding));
}
return res;
}
private static boolean isCalledOnDynamicRef(FunctionCall e) {
if (e instanceof ExprMemberMethod) {
ExprMemberMethod mm = (ExprMemberMethod) e;
return mm.getLeft().attrTyp().allowsDynamicDispatch();
} else return e.attrIsDynamicContext();
}
private static ImExprs translateExprs(List<Expr> arguments, ImTranslator t, ImFunction f) {
ImExprs result = ImExprs();
for (Expr e : arguments) {
result.add(e.imTranslateExpr(t, f));
}
return result;
}
public static ImExpr translateIntern(ExprIncomplete e, ImTranslator t, ImFunction f) {
throw new CompileError(e.getSource(), "Incomplete expression.");
}
public static ImExpr translateIntern(ExprNewObject e, ImTranslator t, ImFunction f) {
ConstructorDef constructorFunc = e.attrConstructorDef();
ImFunction constructorImFunc = t.getConstructNewFunc(constructorFunc);
FunctionSignature sig = e.attrFunctionSignature();
WurstTypeClass wurstType = (WurstTypeClass) e.attrTyp();
ImClass imClass = t.getClassFor(wurstType.getClassDef());
ImTypeArguments typeArgs = getFunctionCallTypeArguments(t, sig, e, imClass.getTypeVariables());
return ImFunctionCall(e, constructorImFunc, typeArgs, translateExprs(e.getArgs(), t, f), false, CallType.NORMAL);
}
public static ImExprOpt translate(NoExpr e, ImTranslator translator, ImFunction f) {
return JassIm.ImNoExpr();
}
public static ImExpr translateIntern(ExprInstanceOf e, ImTranslator translator, ImFunction f) {
WurstType targetType = e.getTyp().attrTyp();
ImType imTargetType = targetType.imTranslateType(translator);
if (imTargetType instanceof ImClassType) {
return JassIm.ImInstanceof(e.getExpr().imTranslateExpr(translator, f), (ImClassType) imTargetType);
}
throw new Error("Cannot compile instanceof " + targetType);
}
public static ImExpr translate(ExprTypeId e, ImTranslator translator, ImFunction f) {
WurstType leftType = e.getLeft().attrTyp();
ImType imLeftType = leftType.imTranslateType(translator);
if (imLeftType instanceof ImClassType) {
ImClassType imLeftTypeC = (ImClassType) imLeftType;
if (leftType instanceof WurstTypeClassOrInterface) {
WurstTypeClassOrInterface wtc = (WurstTypeClassOrInterface) leftType;
if (wtc.isStaticRef()) {
return JassIm.ImTypeIdOfClass(imLeftTypeC);
} else {
return JassIm.ImTypeIdOfObj(e.getLeft().imTranslateExpr(translator, f), imLeftTypeC);
}
} else {
throw new CompileError(e, "not implemented for " + leftType);
}
} else {
throw new CompileError(e, "not implemented for " + leftType);
}
}
public static ImExpr translate(ExprClosure e, ImTranslator tr, ImFunction f) {
return new ClosureTranslator(e, tr, f).translate();
}
public static ImExpr translate(ExprStatementsBlock e, ImTranslator translator, ImFunction f) {
ImStmts statements = JassIm.ImStmts();
for (WStatement s : e.getBody()) {
if (s instanceof StmtReturn) {
continue;
}
ImStmt translated = s.imTranslateStmt(translator, f);
statements.add(translated);
}
StmtReturn r = e.getReturnStmt();
if (r != null && r.getReturnedObj() instanceof Expr) {
ImExpr expr = ((Expr) r.getReturnedObj()).imTranslateExpr(translator, f);
return JassIm.ImStatementExpr(statements, expr);
} else {
return ImHelper.statementExprVoid(statements);
}
}
public static ImExpr translate(ExprDestroy s, ImTranslator t, ImFunction f) {
WurstType typ = s.getDestroyedObj().attrTyp();
if (typ instanceof WurstTypeClass) {
WurstTypeClass classType = (WurstTypeClass) typ;
return destroyClass(s, t, f, classType.getClassDef());
} else if (typ instanceof WurstTypeInterface) {
WurstTypeInterface wti = (WurstTypeInterface) typ;
return destroyClass(s, t, f, wti.getDef());
} else if (typ instanceof WurstTypeModuleInstanciation) {
WurstTypeModuleInstanciation minsType = (WurstTypeModuleInstanciation) typ;
ClassDef classDef = minsType.getDef().attrNearestClassDef();
return destroyClass(s, t, f, classDef);
}
// TODO destroy interfaces?
throw new CompileError(s.getSource(), "cannot destroy object of type " + typ);
}
public static ImExpr destroyClass(ExprDestroy s, ImTranslator t, ImFunction f, StructureDef classDef) {
ImMethod destroyFunc = t.destroyMethod.getFor(classDef);
return ImMethodCall(s, destroyFunc, ImTypeArguments(), s.getDestroyedObj().imTranslateExpr(t, f), ImExprs(), false);
}
public static ImExpr translate(ExprEmpty s, ImTranslator translator, ImFunction f) {
throw new CompileError(s.getSource(), "cannot translate empty expression");
}
public static ImExpr translate(ExprIfElse e, ImTranslator t, ImFunction f) {
ImExpr ifTrue = e.getIfTrue().imTranslateExpr(t, f);
ImExpr ifFalse = e.getIfFalse().imTranslateExpr(t, f);
// TODO common super type of both
ImVar res = JassIm.ImVar(e, ifTrue.attrTyp(), "cond_result", false);
f.getLocals().add(res);
return JassIm.ImStatementExpr(
ImStmts(
ImIf(e, e.getCond().imTranslateExpr(t, f),
ImStmts(
ImSet(e.getIfTrue(), ImVarAccess(res), ifTrue)
),
ImStmts(
ImSet(e.getIfFalse(), ImVarAccess(res), ifFalse)
))
),
JassIm.ImVarAccess(res)
);
}
public static ImLExpr translateLvalue(LExpr e, ImTranslator t, ImFunction f) {
NameDef decl = e.attrNameDef();
if (decl == null) {
// should only happen with gg_ variables
throw new CompileError(e.getSource(), "Translation Error: Could not find definition of " + e.getVarName() + ".");
}
if (decl instanceof VarDef) {
VarDef varDef = (VarDef) decl;
ImVar v = t.getVarFor(varDef);
NameLink link = e.attrNameLink();
@Nullable FuncLink indexGetOverload = (link == null || !(e instanceof NameRef))
? null
: getIndexGetOverload((NameRef) e, link);
if (e.attrImplicitParameter() instanceof Expr) {
// we have implicit parameter
// e.g. "someObject.someField"
Expr implicitParam = (Expr) e.attrImplicitParameter();
if (implicitParam.attrTyp() instanceof WurstTypeTuple) {
WurstTypeTuple tupleType = (WurstTypeTuple) implicitParam.attrTyp();
if (e instanceof ExprMemberVar && ((ExprMemberVar) e).getLeft() instanceof LExpr) {
ExprMemberVar emv = (ExprMemberVar) e;
LExpr left = (LExpr) emv.getLeft();
ImLExpr lt = left.imTranslateExprLvalue(t, f);
return JassIm.ImTupleSelection(lt, tupleType.getTupleIndex(varDef));
} else {
throw new CompileError(e.getSource(), "Cannot create tuple access");
}
}
if (e instanceof AstElementWithIndexes) {
if (indexGetOverload != null) {
throw new CompileError(e.getSource(), "Cannot assign to overloaded [] access without " + AttrFuncDef.overloadingIndexSet + ".");
}
ImExpr index1 = implicitParam.imTranslateExpr(t, f);
ImExpr index2 = ((AstElementWithIndexes) e).getIndexes().get(0).imTranslateExpr(t, f);
return JassIm.ImMemberAccess(e, index1, JassIm.ImTypeArguments(), v, JassIm.ImExprs(index2));
} else {
ImExpr index = implicitParam.imTranslateExpr(t, f);
return JassIm.ImMemberAccess(e, index, JassIm.ImTypeArguments(), v, JassIm.ImExprs());
}
} else {
// direct var access
if (e instanceof AstElementWithIndexes) {
if (indexGetOverload != null) {
throw new CompileError(e.getSource(), "Cannot assign to overloaded [] access without " + AttrFuncDef.overloadingIndexSet + ".");
}
// direct access array var
AstElementWithIndexes withIndexes = (AstElementWithIndexes) e;
if (withIndexes.getIndexes().size() > 1) {
throw new CompileError(e.getSource(), "More than one index is not supported.");
}
ImExpr index = withIndexes.getIndexes().get(0).imTranslateExpr(t, f);
return ImVarArrayAccess(e, v, JassIm.ImExprs(index));
} else {
// not an array var
return ImVarAccess(v);
}
}
} else {
throw new CompileError(e.getSource(), "Cannot translate reference to " + Utils.printElement(decl));
}
}
public static ImExpr translate(ExprArrayLength exprArrayLength, ImTranslator translator, ImFunction f) {
var t = exprArrayLength.getArray().attrTyp();
if (t instanceof WurstTypeArray wta && wta.getDimensions() > 0) {
return JassIm.ImIntVal(wta.getSize(0));
}
// if you ever support dynamic length, translate accordingly (otherwise error)
exprArrayLength.addError("length is only available for arrays with known size.");
return JassIm.ImIntVal(0);
}
private static @Nullable FuncLink getIndexGetOverload(NameRef e, NameLink link) {
if (!(e instanceof AstElementWithIndexes)) {
return null;
}
AstElementWithIndexes withIndexes = (AstElementWithIndexes) e;
if (withIndexes.getIndexes().size() != 1) {
return null;
}
WurstType receiverType = link.getTyp();
if (receiverType instanceof WurstTypeArray) {
return null;
}
return AttrFuncDef.getIndexGetOperator(e, receiverType, withIndexes.getIndexes().get(0).attrTyp());
}
}