Skip to content

Commit 299e85f

Browse files
committed
Added widening conversions for number types
1 parent 4f0dae1 commit 299e85f

11 files changed

Lines changed: 229 additions & 32 deletions

File tree

CodeModel/src/main/java/org/openzen/zenscript/codemodel/OperatorType.java

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
* Enum used to indicate operator type.
55
*/
66
public enum OperatorType {
7-
ADD("+", "add"),
8-
SUB("-", "subtract"),
9-
MUL("*", "multiply"),
10-
DIV("/", "divide"),
11-
MOD("%", "modulo"),
7+
ADD("+", "add", true),
8+
SUB("-", "subtract", true),
9+
MUL("*", "multiply", true),
10+
DIV("/", "divide", true),
11+
MOD("%", "modulo", true),
1212
CAT("~", "concat"),
13-
OR("|", "or"),
14-
AND("&", "and"),
15-
XOR("^", "xor"),
13+
OR("|", "or", true),
14+
AND("&", "and", true),
15+
XOR("^", "xor", true),
1616
NEG("-", "negate"),
1717
INVERT("~", "invert"),
1818
NOT("!", "not"),
@@ -56,16 +56,26 @@ public enum OperatorType {
5656
public final OperatorType assignOperatorFor;
5757
public final String operator;
5858
public final String compiledName;
59+
public final boolean widening;
5960

6061
OperatorType(String operator, String compiledName) {
6162
this.operator = operator;
6263
this.compiledName = compiledName;
6364
assignOperatorFor = null;
65+
this.widening = false;
66+
}
67+
68+
OperatorType(String operator, String compiledName, boolean widening) {
69+
this.operator = operator;
70+
this.compiledName = compiledName;
71+
assignOperatorFor = null;
72+
this.widening = widening;
6473
}
6574

6675
OperatorType(String operator, String compiledName, OperatorType assignOperatorFor) {
6776
this.operator = operator;
6877
this.compiledName = compiledName;
6978
this.assignOperatorFor = assignOperatorFor;
79+
this.widening = false;
7080
}
7181
}

CodeModel/src/main/java/org/openzen/zenscript/codemodel/compilation/AnyMethod.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,8 @@ public interface AnyMethod {
1010
FunctionHeader getHeader();
1111

1212
Optional<MethodInstance> asMethod();
13+
14+
default boolean hasWideningConversions() {
15+
return false;
16+
}
1317
}

CodeModel/src/main/java/org/openzen/zenscript/codemodel/compilation/CastedExpression.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
public class CastedExpression {
66
public enum Level {
77
EXACT,
8+
WIDENING,
89
IMPLICIT,
910
EXPLICIT,
1011
INVALID;

CodeModel/src/main/java/org/openzen/zenscript/codemodel/compilation/InstanceCallable.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import org.openzen.zencode.shared.CodePosition;
44
import org.openzen.zenscript.codemodel.FunctionHeader;
55
import org.openzen.zenscript.codemodel.compilation.impl.BoundInstanceCallable;
6+
import org.openzen.zenscript.codemodel.expression.CallArguments;
67
import org.openzen.zenscript.codemodel.expression.Expression;
78
import org.openzen.zenscript.codemodel.identifiers.MethodSymbol;
89
import org.openzen.zenscript.codemodel.identifiers.instances.MethodInstance;
@@ -32,7 +33,14 @@ public InstanceCallable union(InstanceCallable other) {
3233

3334
public Expression call(ExpressionCompiler compiler, CodePosition position, Expression instance, TypeID[] typeArguments, CompilingExpression... arguments) {
3435
MatchedCallArguments<InstanceCallableMethod> matched = MatchedCallArguments.match(compiler, position, overloads, null, typeArguments, arguments);
35-
return matched.eval(compiler.at(position), (buildr, method, args) -> method.call(buildr, instance, args));
36+
if (matched.requiresWidenedInstance(instance.type)) {
37+
Expression originalInstance = instance;
38+
instance = compiler.resolve(instance.type).findCaster(matched.getWidenedInstanceType())
39+
.map(caster -> caster.call(compiler.at(position), originalInstance, CallArguments.EMPTY))
40+
.orElseThrow(() -> new IllegalStateException("No widening conversion found for " + originalInstance.type + " to " + matched.getWidenedInstanceType()));
41+
}
42+
Expression finalInstance = instance;
43+
return matched.eval(compiler.at(position), (buildr, method, args) -> method.call(buildr, finalInstance, args));
3644
}
3745

3846
public CastedExpression cast(ExpressionCompiler compiler, CodePosition position, CastedEval cast, Expression instance, TypeID[] typeArguments, CompilingExpression... arguments) {

CodeModel/src/main/java/org/openzen/zenscript/codemodel/compilation/MatchedCallArguments.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
public class MatchedCallArguments<T extends AnyMethod> {
1919

20-
private static final CastedExpression.Level[] candidateLevelsInOrderOfPriority = {CastedExpression.Level.EXACT, CastedExpression.Level.IMPLICIT};
20+
private static final CastedExpression.Level[] candidateLevelsInOrderOfPriority = {CastedExpression.Level.EXACT, CastedExpression.Level.WIDENING, CastedExpression.Level.IMPLICIT};
2121

2222
public static <T extends AnyMethod> MatchedCallArguments<T> match(
2323
ExpressionCompiler compiler,
@@ -73,6 +73,14 @@ private MatchedCallArguments(CompileError error) {
7373
this.error = error;
7474
}
7575

76+
public boolean requiresWidenedInstance(TypeID instanceType) {
77+
return method != null && method.hasWideningConversions() && method.asMethod().map(method -> !method.getTarget().equals(instanceType)).orElse(false);
78+
}
79+
80+
public TypeID getWidenedInstanceType() {
81+
return method.asMethod().map(MethodInstance::getTarget).orElse(null);
82+
}
83+
7684
public Expression eval(ExpressionBuilder builder, CallEvaluator<T> evaluator) {
7785
if (this.error != null) {
7886
return builder.invalid(error);
@@ -195,6 +203,22 @@ private static CallArguments match(
195203
}
196204
}
197205

206+
if (method.hasWideningConversions()) {
207+
if (levelNormalCall == CastedExpression.Level.EXACT)
208+
levelNormalCall = CastedExpression.Level.WIDENING;
209+
210+
FunctionHeader originalHeader = method.asMethod().map(instance -> instance.method.getHeader()).orElse(header);
211+
212+
for (int i = 0; i < originalHeader.parameters.length; i++) {
213+
if (!cArguments[i].type.equals(originalHeader.parameters[i].type)) {
214+
int finali = i;
215+
cArguments[i] = compiler.resolve(cArguments[i].type).findCaster(originalHeader.parameters[i].type)
216+
.map(caster -> caster.call(compiler.at(position), cArguments[finali], CallArguments.EMPTY))
217+
.orElse(cArguments[i]);
218+
}
219+
}
220+
}
221+
198222
if (levelVarargCall != null && levelVarargCall.min(levelNormalCall) == levelVarargCall) {
199223
cArguments[cArguments.length - 1] = compiler.at(position).newArray(new ArrayTypeID(variadicType), varargArguments.toArray(Expression.NONE));
200224
levelNormalCall = levelVarargCall;

CodeModel/src/main/java/org/openzen/zenscript/codemodel/identifiers/instances/MethodInstance.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,38 @@ public class MethodInstance implements InstanceCallableMethod, StaticCallableMet
1818
private final FunctionHeader header;
1919
private final TypeID target;
2020
private final TypeID[] expansionTypeArguments;
21+
private final boolean hasWideningConversions;
2122

2223
public MethodInstance(MethodSymbol method) {
2324
this.method = method;
2425
this.header = method.getHeader();
2526
this.target = method.getTargetType();
2627
this.expansionTypeArguments = TypeID.NONE;
28+
this.hasWideningConversions = false;
2729
}
2830

2931
public MethodInstance(MethodSymbol method, FunctionHeader header, TypeID target) {
3032
this.method = method;
3133
this.header = header;
3234
this.target = target;
3335
this.expansionTypeArguments = TypeID.NONE;
36+
this.hasWideningConversions = false;
37+
}
38+
39+
public MethodInstance(MethodSymbol method, FunctionHeader header, TypeID target, boolean hasWideningConversions) {
40+
this.method = method;
41+
this.header = header;
42+
this.target = target;
43+
this.expansionTypeArguments = TypeID.NONE;
44+
this.hasWideningConversions = hasWideningConversions;
3445
}
3546

3647
public MethodInstance(MethodSymbol method, FunctionHeader header, TypeID target, TypeID[] expansionTypeArguments) {
3748
this.method = method;
3849
this.header = header;
3950
this.target = target;
4051
this.expansionTypeArguments = expansionTypeArguments;
52+
this.hasWideningConversions = false;
4153
}
4254

4355
public TypeID getTarget() {
@@ -77,6 +89,11 @@ public boolean isImplicit() {
7789
return method.getModifiers().isImplicit();
7890
}
7991

92+
@Override
93+
public boolean hasWideningConversions() {
94+
return hasWideningConversions;
95+
}
96+
8097
public TypeID[] getExpansionTypeArguments() {
8198
return expansionTypeArguments;
8299
}

CodeModel/src/main/java/org/openzen/zenscript/codemodel/type/builtin/BasicTypeMembers.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package org.openzen.zenscript.codemodel.type.builtin;
22

3+
import org.openzen.zenscript.codemodel.FunctionHeader;
4+
import org.openzen.zenscript.codemodel.FunctionParameter;
35
import org.openzen.zenscript.codemodel.OperatorType;
46
import org.openzen.zenscript.codemodel.compilation.CastedEval;
57
import org.openzen.zenscript.codemodel.expression.CompareExpression;
68
import org.openzen.zenscript.codemodel.identifiers.MethodID;
79
import org.openzen.zenscript.codemodel.identifiers.instances.FieldInstance;
810
import org.openzen.zenscript.codemodel.identifiers.instances.MethodInstance;
911
import org.openzen.zenscript.codemodel.type.BasicTypeID;
12+
import org.openzen.zenscript.codemodel.type.TypeID;
1013
import org.openzen.zenscript.codemodel.type.member.MemberSet;
1114

1215
public class BasicTypeMembers {
@@ -552,6 +555,22 @@ private static void setup(MemberSet.Builder builder, BasicTypeID type) {
552555
}*/
553556
if (method.getDefiningType() == type) {
554557
builder.method(new MethodInstance(method));
558+
if (method.useWideningConversions) {
559+
for (MethodInstance widened : getWideningMethodInstances(method)) {
560+
builder.method(widened);
561+
}
562+
}
563+
}
564+
if (method.useWideningConversions) {
565+
method.getDefiningType().asType().ifPresent(definingType -> {
566+
BasicTypeID basicDefiningType = (BasicTypeID) definingType;
567+
TypeID[] wideningSources = getWideningSources(basicDefiningType);
568+
for (TypeID source : wideningSources) {
569+
if (source == type) {
570+
builder.method(new MethodInstance(method, method.getHeader(), basicDefiningType, true));
571+
}
572+
}
573+
});
555574
}
556575
}
557576

@@ -572,6 +591,50 @@ private static void comparator(MemberSet.Builder builder, BuiltinMethodSymbol me
572591
type)));
573592
}
574593

594+
private static MethodInstance[] getWideningMethodInstances(BuiltinMethodSymbol method) {
595+
FunctionHeader original = method.getHeader();
596+
if (original.parameters.length != 1)
597+
throw new IllegalArgumentException("Not doing widening on multiple method arguments");
598+
599+
FunctionParameter originalParameter = original.parameters[0];
600+
TypeID[] wideningSources = getWideningSources((BasicTypeID) originalParameter.type);
601+
MethodInstance[] wideningMethodInstances = new MethodInstance[wideningSources.length];
602+
for (int i = 0; i < wideningSources.length; i++) {
603+
FunctionParameter parameter = new FunctionParameter(wideningSources[i], originalParameter.name, false);
604+
FunctionHeader header = new FunctionHeader(original.typeParameters, original.getReturnType(), original.thrownType, parameter);
605+
wideningMethodInstances[i] = new MethodInstance(method, header, method.getTargetType(), true);
606+
}
607+
return wideningMethodInstances;
608+
}
609+
610+
private static TypeID[] getWideningSources(BasicTypeID type) {
611+
switch (type) {
612+
case BYTE:
613+
case SBYTE:
614+
return TypeID.NONE;
615+
case SHORT:
616+
return new TypeID[]{BasicTypeID.BYTE, BasicTypeID.SBYTE};
617+
case USHORT:
618+
return new TypeID[]{BasicTypeID.BYTE};
619+
case INT:
620+
return new TypeID[]{BasicTypeID.BYTE, BasicTypeID.SBYTE, BasicTypeID.SHORT, BasicTypeID.USHORT};
621+
case UINT:
622+
return new TypeID[]{BasicTypeID.BYTE, BasicTypeID.USHORT};
623+
case USIZE:
624+
return new TypeID[]{BasicTypeID.BYTE, BasicTypeID.USHORT, BasicTypeID.UINT};
625+
case LONG:
626+
return new TypeID[]{BasicTypeID.BYTE, BasicTypeID.SBYTE, BasicTypeID.SHORT, BasicTypeID.USHORT, BasicTypeID.INT, BasicTypeID.USIZE};
627+
case ULONG:
628+
return new TypeID[]{BasicTypeID.BYTE, BasicTypeID.USHORT, BasicTypeID.UINT, BasicTypeID.USIZE};
629+
case FLOAT:
630+
return new TypeID[]{BasicTypeID.BYTE, BasicTypeID.SBYTE, BasicTypeID.SHORT, BasicTypeID.USHORT, BasicTypeID.INT, BasicTypeID.UINT, BasicTypeID.LONG, BasicTypeID.ULONG, BasicTypeID.USIZE};
631+
case DOUBLE:
632+
return new TypeID[]{BasicTypeID.BYTE, BasicTypeID.SBYTE, BasicTypeID.SHORT, BasicTypeID.USHORT, BasicTypeID.INT, BasicTypeID.UINT, BasicTypeID.LONG, BasicTypeID.ULONG, BasicTypeID.USIZE, BasicTypeID.FLOAT};
633+
default:
634+
return TypeID.NONE;
635+
}
636+
}
637+
575638
private BasicTypeMembers() {
576639
}
577640
}

0 commit comments

Comments
 (0)