Skip to content

Commit b7c35fd

Browse files
committed
allow overloading the index operator
1 parent 0ca5f73 commit b7c35fd

6 files changed

Lines changed: 207 additions & 9 deletions

File tree

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrExprType.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,15 @@ public static WurstType calculate(ExprVarArrayAccess term) {
101101
WurstType varDefType = varDef.getTyp();
102102
if (varDefType instanceof WurstTypeArray) {
103103
return ((WurstTypeArray) varDefType).getBaseType();
104-
} else {
105-
term.addError("Variable " + varDef.getName() + " is of type " + varDefType + ", should be an array variable.");
106104
}
105+
if (term.getIndexes().size() == 1) {
106+
FuncLink overload = AttrFuncDef.getIndexGetOperator(term, varDefType, term.getIndexes().get(0).attrTyp());
107+
if (overload != null) {
108+
return overload.getReturnType();
109+
}
110+
}
111+
term.addError("Variable " + varDef.getName() + " is of type " + varDefType +
112+
", should be an array variable or define operator overloading function " + AttrFuncDef.overloadingIndexGet + ".");
107113
return WurstTypeUnknown.instance();
108114
}
109115

@@ -412,7 +418,13 @@ public static WurstType calculate(ExprMemberArrayVarDot term) {
412418
WurstTypeArray ar = (WurstTypeArray) typ;
413419
return ar.getBaseType();
414420
}
415-
term.addError("Variable " + term.getVarName() + " is not an array.");
421+
if (term.getIndexes().size() == 1) {
422+
FuncLink overload = AttrFuncDef.getIndexGetOperator(term, typ, term.getIndexes().get(0).attrTyp());
423+
if (overload != null) {
424+
return overload.getReturnType();
425+
}
426+
}
427+
term.addError("Variable " + term.getVarName() + " is not an array and has no " + AttrFuncDef.overloadingIndexGet + " overload.");
416428
return typ;
417429
}
418430

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFuncDef.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public class AttrFuncDef {
3434
public final static String overloadingMinus = "op_minus";
3535
public final static String overloadingMult = "op_mult";
3636
public final static String overloadingDiv = "op_divReal";
37+
public final static String overloadingIndexGet = "op_index";
38+
public final static String overloadingIndexSet = "op_indexAssign";
3739

3840
public static FuncLink calculate(final ExprFuncRef node) {
3941

@@ -206,6 +208,14 @@ private static boolean isConstructorThisCall(ExprFunctionCall node) {
206208
return searchMemberFunc(left, left.attrTyp(), funcName, Collections.singletonList(right.attrTyp()));
207209
}
208210

211+
public static @Nullable FuncLink getIndexGetOperator(Expr node, WurstType receiverType, WurstType indexType) {
212+
return searchMemberFunc(node, receiverType, overloadingIndexGet, Collections.singletonList(indexType));
213+
}
214+
215+
public static @Nullable FuncLink getIndexSetOperator(Expr node, WurstType receiverType, WurstType indexType, WurstType valueType) {
216+
return searchMemberFunc(node, receiverType, overloadingIndexSet, Lists.newArrayList(indexType, valueType));
217+
}
218+
209219

210220
/**
211221
* checks if operator is a native operator like for 1+2

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
import de.peeeq.wurstscript.WurstOperator;
66
import de.peeeq.wurstscript.ast.*;
77
import de.peeeq.wurstscript.ast.Element;
8+
import de.peeeq.wurstscript.attributes.AttrFuncDef;
89
import de.peeeq.wurstscript.attributes.CompileError;
10+
import de.peeeq.wurstscript.attributes.names.FuncLink;
911
import de.peeeq.wurstscript.attributes.names.NameLink;
1012
import de.peeeq.wurstscript.attributes.names.OtherLink;
1113
import de.peeeq.wurstscript.jassIm.*;
@@ -262,6 +264,7 @@ private static ImExpr translateNameDef(NameRef e, ImTranslator t, ImFunction f)
262264
VarDef varDef = (VarDef) decl;
263265

264266
ImVar v = t.getVarFor(varDef);
267+
@Nullable FuncLink indexGetOverload = getIndexGetOverload(e, link);
265268

266269
if (e.attrImplicitParameter() instanceof Expr) {
267270
// we have implicit parameter
@@ -279,6 +282,13 @@ private static ImExpr translateNameDef(NameRef e, ImTranslator t, ImFunction f)
279282
}
280283

281284
if (e instanceof AstElementWithIndexes) {
285+
if (indexGetOverload != null) {
286+
AstElementWithIndexes withIndexes = (AstElementWithIndexes) e;
287+
ImExpr receiver = JassIm.ImMemberAccess(e, implicitParam.imTranslateExpr(t, f), JassIm.ImTypeArguments(), v, JassIm.ImExprs());
288+
ImExpr index = withIndexes.getIndexes().get(0).imTranslateExpr(t, f);
289+
ImFunction calledFunc = t.getFuncFor(indexGetOverload.getDef());
290+
return ImFunctionCall(e, calledFunc, ImTypeArguments(), ImExprs(receiver, index), false, CallType.NORMAL);
291+
}
282292
ImExpr index1 = implicitParam.imTranslateExpr(t, f);
283293
ImExpr index2 = ((AstElementWithIndexes) e).getIndexes().get(0).imTranslateExpr(t, f);
284294
return JassIm.ImMemberAccess(e, index1, JassIm.ImTypeArguments(), v, JassIm.ImExprs(index2));
@@ -289,6 +299,13 @@ private static ImExpr translateNameDef(NameRef e, ImTranslator t, ImFunction f)
289299
} else {
290300
// direct var access
291301
if (e instanceof AstElementWithIndexes) {
302+
if (indexGetOverload != null) {
303+
AstElementWithIndexes withIndexes = (AstElementWithIndexes) e;
304+
ImExpr receiver = ImVarAccess(v);
305+
ImExpr index = withIndexes.getIndexes().get(0).imTranslateExpr(t, f);
306+
ImFunction calledFunc = t.getFuncFor(indexGetOverload.getDef());
307+
return ImFunctionCall(e, calledFunc, ImTypeArguments(), ImExprs(receiver, index), false, CallType.NORMAL);
308+
}
292309
// direct access array var
293310
AstElementWithIndexes withIndexes = (AstElementWithIndexes) e;
294311
if (withIndexes.getIndexes().size() > 1) {
@@ -752,6 +769,10 @@ public static ImLExpr translateLvalue(LExpr e, ImTranslator t, ImFunction f) {
752769
VarDef varDef = (VarDef) decl;
753770

754771
ImVar v = t.getVarFor(varDef);
772+
NameLink link = e.attrNameLink();
773+
@Nullable FuncLink indexGetOverload = (link == null || !(e instanceof NameRef))
774+
? null
775+
: getIndexGetOverload((NameRef) e, link);
755776

756777
if (e.attrImplicitParameter() instanceof Expr) {
757778
// we have implicit parameter
@@ -771,6 +792,9 @@ public static ImLExpr translateLvalue(LExpr e, ImTranslator t, ImFunction f) {
771792
}
772793

773794
if (e instanceof AstElementWithIndexes) {
795+
if (indexGetOverload != null) {
796+
throw new CompileError(e.getSource(), "Cannot assign to overloaded [] access without " + AttrFuncDef.overloadingIndexSet + ".");
797+
}
774798
ImExpr index1 = implicitParam.imTranslateExpr(t, f);
775799
ImExpr index2 = ((AstElementWithIndexes) e).getIndexes().get(0).imTranslateExpr(t, f);
776800
return JassIm.ImMemberAccess(e, index1, JassIm.ImTypeArguments(), v, JassIm.ImExprs(index2));
@@ -782,6 +806,9 @@ public static ImLExpr translateLvalue(LExpr e, ImTranslator t, ImFunction f) {
782806
} else {
783807
// direct var access
784808
if (e instanceof AstElementWithIndexes) {
809+
if (indexGetOverload != null) {
810+
throw new CompileError(e.getSource(), "Cannot assign to overloaded [] access without " + AttrFuncDef.overloadingIndexSet + ".");
811+
}
785812
// direct access array var
786813
AstElementWithIndexes withIndexes = (AstElementWithIndexes) e;
787814
if (withIndexes.getIndexes().size() > 1) {
@@ -810,4 +837,19 @@ public static ImExpr translate(ExprArrayLength exprArrayLength, ImTranslator tra
810837
return JassIm.ImIntVal(0);
811838
}
812839

840+
private static @Nullable FuncLink getIndexGetOverload(NameRef e, NameLink link) {
841+
if (!(e instanceof AstElementWithIndexes)) {
842+
return null;
843+
}
844+
AstElementWithIndexes withIndexes = (AstElementWithIndexes) e;
845+
if (withIndexes.getIndexes().size() != 1) {
846+
return null;
847+
}
848+
WurstType receiverType = link.getTyp();
849+
if (receiverType instanceof WurstTypeArray) {
850+
return null;
851+
}
852+
return AttrFuncDef.getIndexGetOperator(e, receiverType, withIndexes.getIndexes().get(0).attrTyp());
853+
}
854+
813855
}

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/StmtTranslation.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,17 @@
44
import de.peeeq.wurstscript.WurstOperator;
55
import de.peeeq.wurstscript.ast.*;
66
import de.peeeq.wurstscript.ast.Element;
7+
import de.peeeq.wurstscript.attributes.AttrFuncDef;
78
import de.peeeq.wurstscript.attributes.CompileError;
89
import de.peeeq.wurstscript.attributes.names.FuncLink;
10+
import de.peeeq.wurstscript.attributes.names.NameLink;
11+
import de.peeeq.wurstscript.attributes.names.OtherLink;
912
import de.peeeq.wurstscript.jassIm.*;
1013
import de.peeeq.wurstscript.types.TypesHelper;
1114
import de.peeeq.wurstscript.types.WurstType;
15+
import de.peeeq.wurstscript.types.WurstTypeArray;
1216
import de.peeeq.wurstscript.types.WurstTypeVararg;
17+
import org.eclipse.jdt.annotation.Nullable;
1318

1419
import java.util.List;
1520
import java.util.Optional;
@@ -330,11 +335,54 @@ public static ImStmt translate(StmtReturn s, ImTranslator t, ImFunction f) {
330335

331336

332337
public static ImStmt translate(StmtSet s, ImTranslator t, ImFunction f) {
338+
ImStmt overloadedIndexSet = translateOverloadedIndexSet(s, t, f);
339+
if (overloadedIndexSet != null) {
340+
return overloadedIndexSet;
341+
}
333342
ImLExpr updated = s.getUpdatedExpr().imTranslateExprLvalue(t, f);
334343
ImExpr right = s.getRight().imTranslateExpr(t, f);
335344
return ImSet(s, updated, right);
336345
}
337346

347+
private static @Nullable ImStmt translateOverloadedIndexSet(StmtSet s, ImTranslator t, ImFunction f) {
348+
if (!(s.getUpdatedExpr() instanceof NameRef) || !(s.getUpdatedExpr() instanceof AstElementWithIndexes)) {
349+
return null;
350+
}
351+
NameRef left = (NameRef) s.getUpdatedExpr();
352+
AstElementWithIndexes withIndexes = (AstElementWithIndexes) s.getUpdatedExpr();
353+
if (withIndexes.getIndexes().size() != 1) {
354+
return null;
355+
}
356+
NameLink link = left.attrNameLink();
357+
if (link == null || link.getTyp() instanceof WurstTypeArray) {
358+
return null;
359+
}
360+
FuncLink setOverload = AttrFuncDef.getIndexSetOperator(
361+
left,
362+
link.getTyp(),
363+
withIndexes.getIndexes().get(0).attrTyp(),
364+
s.getRight().attrTyp());
365+
if (setOverload == null) {
366+
return null;
367+
}
368+
if (link instanceof OtherLink || !(link.getDef() instanceof VarDef)) {
369+
throw new CompileError(s.getSource(), "Could not resolve assignment receiver for overloaded [] assignment.");
370+
}
371+
VarDef varDef = (VarDef) link.getDef();
372+
ImVar receiverVar = t.getVarFor(varDef);
373+
ImExpr receiver;
374+
if (left.attrImplicitParameter() instanceof Expr) {
375+
Expr implicit = (Expr) left.attrImplicitParameter();
376+
receiver = JassIm.ImMemberAccess(left, implicit.imTranslateExpr(t, f), JassIm.ImTypeArguments(), receiverVar, JassIm.ImExprs());
377+
} else {
378+
receiver = ImVarAccess(receiverVar);
379+
}
380+
ImExpr index = withIndexes.getIndexes().get(0).imTranslateExpr(t, f);
381+
ImExpr value = s.getRight().imTranslateExpr(t, f);
382+
ImFunction calledFunc = t.getFuncFor(setOverload.getDef());
383+
return ImFunctionCall(s, calledFunc, ImTypeArguments(), ImExprs(receiver, index, value), false, CallType.NORMAL);
384+
}
385+
338386

339387
public static ImStmt translate(StmtWhile s, ImTranslator t, ImFunction f) {
340388
List<ImStmt> body = Lists.newArrayList();

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import de.peeeq.wurstscript.attributes.CompileError;
88
import de.peeeq.wurstscript.attributes.ImplicitFuncs;
99
import de.peeeq.wurstscript.attributes.OverloadingResolver;
10+
import de.peeeq.wurstscript.attributes.AttrFuncDef;
1011
import de.peeeq.wurstscript.attributes.names.DefLink;
1112
import de.peeeq.wurstscript.attributes.names.FuncLink;
1213
import de.peeeq.wurstscript.attributes.names.NameLink;
@@ -1198,10 +1199,33 @@ private void checkStmtSet(StmtSet s) {
11981199
.addError("Invalid assignment. This is not a variable, this is a " + nameLink);
11991200
return;
12001201
}
1201-
1202-
WurstType leftType = s.getUpdatedExpr().attrTyp();
12031202
WurstType rightType = s.getRight().attrTyp();
1204-
1203+
if (s.getUpdatedExpr() instanceof NameRef && s.getUpdatedExpr() instanceof AstElementWithIndexes) {
1204+
NameRef left = (NameRef) s.getUpdatedExpr();
1205+
AstElementWithIndexes leftWithIndex = (AstElementWithIndexes) s.getUpdatedExpr();
1206+
WurstType targetType = nameLink.getTyp();
1207+
if (!(targetType instanceof WurstTypeArray)) {
1208+
if (leftWithIndex.getIndexes().size() != 1) {
1209+
s.addError("Only one index is supported for overloaded [] assignment.");
1210+
return;
1211+
}
1212+
FuncLink setOverload = AttrFuncDef.getIndexSetOperator(
1213+
left,
1214+
targetType,
1215+
leftWithIndex.getIndexes().get(0).attrTyp(),
1216+
rightType);
1217+
if (setOverload == null) {
1218+
s.addError("No operator overloading function for [] assignment was found for receiver type "
1219+
+ targetType + ". The overloading function has to be named: " + AttrFuncDef.overloadingIndexSet);
1220+
return;
1221+
}
1222+
checkAssignment(Utils.isJassCode(s), s, setOverload.getParameterType(1), rightType);
1223+
checkIfAssigningToConstant(s.getUpdatedExpr());
1224+
checkIfNoEffectAssignment(s);
1225+
return;
1226+
}
1227+
}
1228+
WurstType leftType = s.getUpdatedExpr().attrTyp();
12051229
checkAssignment(Utils.isJassCode(s), s, leftType, rightType);
12061230

12071231
checkIfAssigningToConstant(s.getUpdatedExpr());
@@ -2644,9 +2668,12 @@ private void checkConstructor(ConstructorDef d) {
26442668

26452669
private void checkArrayAccess(ExprVarArrayAccess ea) {
26462670
checkNameRefDeprecated(ea, ea.tryGetNameDef());
2647-
for (Expr index : ea.getIndexes()) {
2648-
if (!(index.attrTyp().isSubtypeOf(WurstTypeInt.instance(), ea))) {
2649-
index.addError("Arrayindices have to be of type int");
2671+
NameLink nameLink = ea.attrNameLink();
2672+
if (nameLink != null && nameLink.getTyp() instanceof WurstTypeArray) {
2673+
for (Expr index : ea.getIndexes()) {
2674+
if (!(index.attrTyp().isSubtypeOf(WurstTypeInt.instance(), ea))) {
2675+
index.addError("Arrayindices have to be of type int");
2676+
}
26502677
}
26512678
}
26522679
}

de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/OpOverloading.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,5 +182,64 @@ public void testOverloading_shortForm2() {
182182
"endpackage");
183183
}
184184

185+
@Test
186+
public void testOverloading_indexRead() {
187+
testAssertOkLines(true,
188+
"package test",
189+
" native testSuccess()",
190+
" class ListLike",
191+
" function op_index(int i) returns int",
192+
" return i + 5",
193+
" init",
194+
" ListLike l = new ListLike()",
195+
" int x = l[3]",
196+
" if x == 8",
197+
" testSuccess()",
198+
"endpackage");
199+
}
200+
201+
@Test
202+
public void testOverloading_indexReadMissing() {
203+
testAssertErrorsLines(true, "op_index",
204+
"package test",
205+
" class ListLike",
206+
" init",
207+
" ListLike l = new ListLike()",
208+
" int x = l[3]",
209+
"endpackage");
210+
}
211+
212+
@Test
213+
public void testOverloading_indexWrite() {
214+
testAssertOkLines(true,
215+
"package test",
216+
" native testSuccess()",
217+
" class ListLike",
218+
" int x = 0",
219+
" function op_index(int i) returns int",
220+
" return x + i",
221+
" function op_indexAssign(int i, int v)",
222+
" x = v + i",
223+
" init",
224+
" ListLike l = new ListLike()",
225+
" l[1] = 2",
226+
" if l[1] == 4",
227+
" testSuccess()",
228+
"endpackage");
229+
}
230+
231+
@Test
232+
public void testOverloading_indexWriteMissingSetter() {
233+
testAssertErrorsLines(true, "op_indexAssign",
234+
"package test",
235+
" class ListLike",
236+
" function op_index(int i) returns int",
237+
" return i",
238+
" init",
239+
" ListLike l = new ListLike()",
240+
" l[1] = 2",
241+
"endpackage");
242+
}
243+
185244

186245
}

0 commit comments

Comments
 (0)