track code location

This commit is contained in:
Johannes Frohnmeyer 2023-04-18 13:40:34 +02:00
parent 51cc7e41ab
commit e652bfe6ba
Signed by: Johannes
GPG Key ID: E76429612C2929F4
56 changed files with 500 additions and 382 deletions

View File

@ -1,12 +1,11 @@
package io.gitlab.jfronny.muscript.ast; package io.gitlab.jfronny.muscript.ast;
import io.gitlab.jfronny.muscript.ast.dynamic.DynamicCoerce; import io.gitlab.jfronny.muscript.ast.dynamic.DynamicCoerce;
import io.gitlab.jfronny.muscript.compiler.Order; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Type;
public abstract non-sealed class BoolExpr extends Expr<Boolean> { public abstract non-sealed class BoolExpr extends Expr<Boolean> {
protected BoolExpr(Order order, int chStart, int chEnd) { protected BoolExpr(Order order, CodeLocation location) {
super(order, chStart, chEnd); super(order, location);
} }
@Override @Override
@ -19,6 +18,6 @@ public abstract non-sealed class BoolExpr extends Expr<Boolean> {
@Override @Override
public DynamicExpr asDynamicExpr() { public DynamicExpr asDynamicExpr() {
return new DynamicCoerce(chStart, chEnd, this); return new DynamicCoerce(location, this);
} }
} }

View File

@ -1,13 +1,12 @@
package io.gitlab.jfronny.muscript.ast; package io.gitlab.jfronny.muscript.ast;
import io.gitlab.jfronny.muscript.compiler.Order; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic; import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.ast.dynamic.unpack.*; import io.gitlab.jfronny.muscript.ast.dynamic.unpack.*;
import io.gitlab.jfronny.muscript.compiler.Type;
public abstract non-sealed class DynamicExpr extends Expr<Dynamic<?>> { public abstract non-sealed class DynamicExpr extends Expr<Dynamic<?>> {
protected DynamicExpr(Order order, int chStart, int chEnd) { protected DynamicExpr(Order order, CodeLocation location) {
super(order, chStart, chEnd); super(order, location);
} }
@Override @Override
@ -20,17 +19,17 @@ public abstract non-sealed class DynamicExpr extends Expr<Dynamic<?>> {
@Override @Override
public BoolExpr asBoolExpr() { public BoolExpr asBoolExpr() {
return new BoolUnpack(chStart, chEnd, this); return new BoolUnpack(location, this);
} }
@Override @Override
public StringExpr asStringExpr() { public StringExpr asStringExpr() {
return new StringUnpack(chStart, chEnd, this); return new StringUnpack(location, this);
} }
@Override @Override
public NumberExpr asNumberExpr() { public NumberExpr asNumberExpr() {
return new NumberUnpack(chStart, chEnd, this); return new NumberUnpack(location, this);
} }
@Override @Override

View File

@ -12,12 +12,10 @@ import io.gitlab.jfronny.muscript.error.TypeMismatchException;
@CanThrow @CanThrow
public abstract sealed class Expr<T> extends Decompilable public abstract sealed class Expr<T> extends Decompilable
permits BoolExpr, DynamicExpr, NullLiteral, NumberExpr, StringExpr { permits BoolExpr, DynamicExpr, NullLiteral, NumberExpr, StringExpr {
public final int chStart; public final CodeLocation location;
public final int chEnd; protected Expr(Order order, CodeLocation location) {
protected Expr(Order order, int chStart, int chEnd) {
super(order); super(order);
this.chStart = chStart; this.location = location;
this.chEnd = chEnd;
} }
public abstract Type getResultType(); public abstract Type getResultType();
@ -33,17 +31,17 @@ public abstract sealed class Expr<T> extends Decompilable
public BoolExpr asBoolExpr() { public BoolExpr asBoolExpr() {
if (this instanceof BoolExpr e) return e; if (this instanceof BoolExpr e) return e;
throw new TypeMismatchException(chStart, chEnd, Type.Boolean, getResultType()); throw new TypeMismatchException(location, Type.Boolean, getResultType());
} }
public StringExpr asStringExpr() { public StringExpr asStringExpr() {
if (this instanceof StringExpr e) return e; if (this instanceof StringExpr e) return e;
return new StringCoerce(chStart, chEnd, this); return new StringCoerce(location, this);
} }
public NumberExpr asNumberExpr() { public NumberExpr asNumberExpr() {
if (this instanceof NumberExpr e) return e; if (this instanceof NumberExpr e) return e;
throw new TypeMismatchException(chStart, chEnd, Type.Number, getResultType()); throw new TypeMismatchException(location, Type.Number, getResultType());
} }
public abstract DynamicExpr asDynamicExpr(); public abstract DynamicExpr asDynamicExpr();
@ -53,54 +51,74 @@ public abstract sealed class Expr<T> extends Decompilable
} }
public static BoolExpr literal(boolean bool) { public static BoolExpr literal(boolean bool) {
return literal(-1, bool); return literal(CodeLocation.NONE, bool);
} }
public static StringExpr literal(String string) { public static StringExpr literal(String string) {
return literal(-1, string); return literal(CodeLocation.NONE, string);
} }
public static NumberExpr literal(double number) { public static NumberExpr literal(double number) {
return literal(-1, number); return literal(CodeLocation.NONE, number);
} }
public static NullLiteral literalNull() { public static NullLiteral literalNull() {
return literalNull(-1); return literalNull(CodeLocation.NONE);
} }
@Deprecated @Deprecated
public static BoolExpr literal(int character, boolean bool) { public static BoolExpr literal(int character, boolean bool) {
return literal(character, character, bool); return literal(new CodeLocation(character), bool);
} }
@Deprecated @Deprecated
public static StringExpr literal(int character, String string) { public static StringExpr literal(int character, String string) {
return literal(character, character, string); return literal(new CodeLocation(character), string);
} }
@Deprecated @Deprecated
public static NumberExpr literal(int character, double number) { public static NumberExpr literal(int character, double number) {
return literal(character, character, number); return literal(new CodeLocation(character), number);
} }
@Deprecated @Deprecated
public static NullLiteral literalNull(int character) { public static NullLiteral literalNull(int character) {
return literalNull(character, character); return literalNull(new CodeLocation(character));
} }
@Deprecated
public static BoolExpr literal(int chStart, int chEnd, boolean bool) { public static BoolExpr literal(int chStart, int chEnd, boolean bool) {
return new BoolLiteral(chStart, chEnd, bool); return literal(new CodeLocation(chStart, chEnd), bool);
} }
@Deprecated
public static StringExpr literal(int chStart, int chEnd, String string) { public static StringExpr literal(int chStart, int chEnd, String string) {
return new StringLiteral(chStart, chEnd, string); return literal(new CodeLocation(chStart, chEnd), string);
} }
@Deprecated
public static NumberExpr literal(int chStart, int chEnd, double number) { public static NumberExpr literal(int chStart, int chEnd, double number) {
return new NumberLiteral(chStart, chEnd, number); return literal(new CodeLocation(chStart, chEnd), number);
} }
@Deprecated
public static NullLiteral literalNull(int chStart, int chEnd) { public static NullLiteral literalNull(int chStart, int chEnd) {
return new NullLiteral(chStart, chEnd); return literalNull(new CodeLocation(chStart, chEnd));
}
public static BoolExpr literal(CodeLocation location, boolean bool) {
return new BoolLiteral(location, bool);
}
public static StringExpr literal(CodeLocation location, String string) {
return new StringLiteral(location, string);
}
public static NumberExpr literal(CodeLocation location, double number) {
return new NumberLiteral(location, number);
}
public static NullLiteral literalNull(CodeLocation location) {
return new NullLiteral(location);
} }
} }

View File

@ -11,8 +11,8 @@ import java.io.IOException;
@CanThrow @CanThrow
public final class NullLiteral extends Expr<Object> { public final class NullLiteral extends Expr<Object> {
public NullLiteral(int chStart, int chEnd) { public NullLiteral(CodeLocation location) {
super(Order.Primary, chStart, chEnd); super(Order.Primary, location);
} }
@Override @Override
@ -37,22 +37,22 @@ public final class NullLiteral extends Expr<Object> {
@Override @Override
public DynamicExpr asDynamicExpr() { public DynamicExpr asDynamicExpr() {
return new DynamicLiteral<>(chStart, chEnd, new DNull()); return new DynamicLiteral<>(location, new DNull());
} }
@Override @Override
public NumberExpr asNumberExpr() { public NumberExpr asNumberExpr() {
throw new LocationalException(chStart, chEnd, "Attempted to convert null to a number"); throw new LocationalException(location, "Attempted to convert null to a number");
} }
@Override @Override
public StringExpr asStringExpr() { public StringExpr asStringExpr() {
return literal(chStart, chEnd, "null"); return literal(location, "null");
} }
@Override @Override
public BoolExpr asBoolExpr() { public BoolExpr asBoolExpr() {
throw new LocationalException(chStart, chEnd, "Attempted to convert null to a boolean"); throw new LocationalException(location, "Attempted to convert null to a boolean");
} }
@Override @Override

View File

@ -1,12 +1,11 @@
package io.gitlab.jfronny.muscript.ast; package io.gitlab.jfronny.muscript.ast;
import io.gitlab.jfronny.muscript.ast.dynamic.DynamicCoerce; import io.gitlab.jfronny.muscript.ast.dynamic.DynamicCoerce;
import io.gitlab.jfronny.muscript.compiler.Order; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Type;
public abstract non-sealed class NumberExpr extends Expr<Double> { public abstract non-sealed class NumberExpr extends Expr<Double> {
protected NumberExpr(Order order, int chStart, int chEnd) { protected NumberExpr(Order order, CodeLocation location) {
super(order, chStart, chEnd); super(order, location);
} }
@Override @Override
@ -19,6 +18,6 @@ public abstract non-sealed class NumberExpr extends Expr<Double> {
@Override @Override
public DynamicExpr asDynamicExpr() { public DynamicExpr asDynamicExpr() {
return new DynamicCoerce(chStart, chEnd, this); return new DynamicCoerce(location, this);
} }
} }

View File

@ -1,12 +1,11 @@
package io.gitlab.jfronny.muscript.ast; package io.gitlab.jfronny.muscript.ast;
import io.gitlab.jfronny.muscript.ast.dynamic.DynamicCoerce; import io.gitlab.jfronny.muscript.ast.dynamic.DynamicCoerce;
import io.gitlab.jfronny.muscript.compiler.Order; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Type;
public abstract non-sealed class StringExpr extends Expr<String> { public abstract non-sealed class StringExpr extends Expr<String> {
protected StringExpr(Order order, int chStart, int chEnd) { protected StringExpr(Order order, CodeLocation location) {
super(order, chStart, chEnd); super(order, location);
} }
@Override @Override
@ -19,6 +18,6 @@ public abstract non-sealed class StringExpr extends Expr<String> {
@Override @Override
public DynamicExpr asDynamicExpr() { public DynamicExpr asDynamicExpr() {
return new DynamicCoerce(chStart, chEnd, this); return new DynamicCoerce(location, this);
} }
} }

View File

@ -1,7 +1,6 @@
package io.gitlab.jfronny.muscript.ast.bool; package io.gitlab.jfronny.muscript.ast.bool;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.BoolExpr; import io.gitlab.jfronny.muscript.ast.BoolExpr;
import io.gitlab.jfronny.muscript.ast.Expr; import io.gitlab.jfronny.muscript.ast.Expr;
@ -13,8 +12,8 @@ public class And extends BoolExpr {
private final BoolExpr left; private final BoolExpr left;
private final BoolExpr right; private final BoolExpr right;
public And(int chStart, int chEnd, BoolExpr left, BoolExpr right) { public And(CodeLocation location, BoolExpr left, BoolExpr right) {
super(Order.And, chStart, chEnd); super(Order.And, location);
this.left = left; this.left = left;
this.right = right; this.right = right;
} }
@ -28,9 +27,9 @@ public class And extends BoolExpr {
public BoolExpr optimize() { public BoolExpr optimize() {
BoolExpr left = this.left.optimize(); BoolExpr left = this.left.optimize();
BoolExpr right = this.right.optimize(); BoolExpr right = this.right.optimize();
if (left instanceof BoolLiteral literal) return literal.value ? right : Expr.literal(chStart, chEnd, false); if (left instanceof BoolLiteral literal) return literal.value ? right : literal(location, false);
if (right instanceof BoolLiteral literal) return literal.value ? left : Expr.literal(chStart, chEnd, false); if (right instanceof BoolLiteral literal) return literal.value ? left : literal(location, false);
return new And(chStart, chEnd, left, right); return new And(location, left, right);
} }
@Override @Override

View File

@ -3,8 +3,7 @@ package io.gitlab.jfronny.muscript.ast.bool;
import io.gitlab.jfronny.muscript.ast.compare.Equal; import io.gitlab.jfronny.muscript.ast.compare.Equal;
import io.gitlab.jfronny.muscript.ast.compare.Greater; import io.gitlab.jfronny.muscript.ast.compare.Greater;
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral; import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.BoolExpr; import io.gitlab.jfronny.muscript.ast.BoolExpr;
import io.gitlab.jfronny.muscript.ast.Expr; import io.gitlab.jfronny.muscript.ast.Expr;
@ -15,8 +14,8 @@ import java.io.IOException;
public class Not extends BoolExpr { public class Not extends BoolExpr {
public final BoolExpr inner; public final BoolExpr inner;
public Not(int chStart, int chEnd, BoolExpr inner) { public Not(CodeLocation location, BoolExpr inner) {
super(Order.Unary, chStart, chEnd); super(Order.Unary, location);
this.inner = inner; this.inner = inner;
} }
@ -29,8 +28,8 @@ public class Not extends BoolExpr {
public BoolExpr optimize() { public BoolExpr optimize() {
BoolExpr inner = this.inner.optimize(); BoolExpr inner = this.inner.optimize();
if (inner instanceof Not not) return not.inner; if (inner instanceof Not not) return not.inner;
if (inner instanceof BoolLiteral literal) return Expr.literal(chStart, chEnd, !literal.value); if (inner instanceof BoolLiteral literal) return literal(location, !literal.value);
return new Not(chStart, chEnd, inner); return new Not(location, inner);
} }
@Override @Override

View File

@ -1,7 +1,6 @@
package io.gitlab.jfronny.muscript.ast.bool; package io.gitlab.jfronny.muscript.ast.bool;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.BoolExpr; import io.gitlab.jfronny.muscript.ast.BoolExpr;
import io.gitlab.jfronny.muscript.ast.Expr; import io.gitlab.jfronny.muscript.ast.Expr;
@ -13,8 +12,8 @@ public class Or extends BoolExpr {
private final BoolExpr left; private final BoolExpr left;
private final BoolExpr right; private final BoolExpr right;
public Or(int chStart, int chEnd, BoolExpr left, BoolExpr right) { public Or(CodeLocation location, BoolExpr left, BoolExpr right) {
super(Order.Or, chStart, chEnd); super(Order.Or, location);
this.left = left; this.left = left;
this.right = right; this.right = right;
} }
@ -28,9 +27,9 @@ public class Or extends BoolExpr {
public BoolExpr optimize() { public BoolExpr optimize() {
BoolExpr left = this.left.optimize(); BoolExpr left = this.left.optimize();
BoolExpr right = this.right.optimize(); BoolExpr right = this.right.optimize();
if (left instanceof BoolLiteral literal) return literal.value ? Expr.literal(chStart, chEnd, true) : right; if (left instanceof BoolLiteral literal) return literal.value ? literal(location, true) : right;
if (right instanceof BoolLiteral literal) return literal.value ? Expr.literal(chStart, chEnd, true) : left; if (right instanceof BoolLiteral literal) return literal.value ? literal(location, true) : left;
return new Or(chStart, chEnd, left, right); return new Or(location, left, right);
} }
@Override @Override

View File

@ -2,8 +2,7 @@ package io.gitlab.jfronny.muscript.ast.compare;
import io.gitlab.jfronny.muscript.ast.BoolExpr; import io.gitlab.jfronny.muscript.ast.BoolExpr;
import io.gitlab.jfronny.muscript.ast.Expr; import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic; import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
@ -14,8 +13,8 @@ public class Equal extends BoolExpr {
public final Expr<?> left; public final Expr<?> left;
public final Expr<?> right; public final Expr<?> right;
public Equal(int chStart, int chEnd, Expr<?> left, Expr<?> right) { public Equal(CodeLocation location, Expr<?> left, Expr<?> right) {
super(Order.Equality, chStart, chEnd); super(Order.Equality, location);
this.left = left; this.left = left;
this.right = right; this.right = right;
} }
@ -34,8 +33,8 @@ public class Equal extends BoolExpr {
public BoolExpr optimize() { public BoolExpr optimize() {
Expr<?> left = this.left.optimize(); Expr<?> left = this.left.optimize();
Expr<?> right = this.right.optimize(); Expr<?> right = this.right.optimize();
if (left.equals(right)) return Expr.literal(chStart, chEnd, true); if (left.equals(right)) return literal(location, true);
return new Equal(chStart, chEnd, left, right); return new Equal(location, left, right);
} }
@Override @Override

View File

@ -1,7 +1,6 @@
package io.gitlab.jfronny.muscript.ast.compare; package io.gitlab.jfronny.muscript.ast.compare;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.*; import io.gitlab.jfronny.muscript.ast.*;
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral; import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
@ -13,8 +12,8 @@ public class Greater extends BoolExpr {
public final NumberExpr left; public final NumberExpr left;
public final NumberExpr right; public final NumberExpr right;
public Greater(int chStart, int chEnd, NumberExpr left, NumberExpr right) { public Greater(CodeLocation location, NumberExpr left, NumberExpr right) {
super(Order.Comparison, chStart, chEnd); super(Order.Comparison, location);
this.left = left; this.left = left;
this.right = right; this.right = right;
} }
@ -29,19 +28,19 @@ public class Greater extends BoolExpr {
NumberExpr left = this.left.optimize(); NumberExpr left = this.left.optimize();
NumberExpr right = this.right.optimize(); NumberExpr right = this.right.optimize();
if (left instanceof NumberLiteral litL && right instanceof NumberLiteral litR) if (left instanceof NumberLiteral litL && right instanceof NumberLiteral litR)
return Expr.literal(chStart, chEnd, litL.value > litR.value); return literal(location, litL.value > litR.value);
if (left instanceof Divide divide) if (left instanceof Divide divide)
return new Greater(chStart, chEnd, divide.dividend, new Multiply(divide.chStart, divide.chEnd, right, divide.divisor)).optimize(); return new Greater(location, divide.dividend, new Multiply(divide.location, right, divide.divisor)).optimize();
if (left instanceof Invert invert) if (left instanceof Invert invert)
return new Greater(chStart, chEnd, new Invert(right.chStart, right.chEnd, right), invert.inner).optimize(); return new Greater(location, new Invert(right.location, right), invert.inner).optimize();
if (left instanceof Minus minus) if (left instanceof Minus minus)
return new Greater(chStart, chEnd, minus.minuend, new Plus(minus.chStart, minus.chEnd, minus.subtrahend, right)).optimize(); return new Greater(location, minus.minuend, new Plus(minus.location, minus.subtrahend, right)).optimize();
// Modulo is left out because it is too complicated for this naive impl // Modulo is left out because it is too complicated for this naive impl
// Multiply is left out since it would transform into a division and may be 0 // Multiply is left out since it would transform into a division and may be 0
if (left instanceof Plus plus) if (left instanceof Plus plus)
return new Greater(chStart, chEnd, plus.augend, new Minus(plus.chStart, plus.chEnd, plus.addend, right)).optimize(); return new Greater(location, plus.augend, new Minus(plus.location, plus.addend, right)).optimize();
// Power is left out because it can't be transformed cleanly either // Power is left out because it can't be transformed cleanly either
return new Greater(chStart, chEnd, left, right); return new Greater(location, left, right);
} }
@Override @Override

View File

@ -1,8 +1,7 @@
package io.gitlab.jfronny.muscript.ast.conditional; package io.gitlab.jfronny.muscript.ast.conditional;
import io.gitlab.jfronny.muscript.ast.bool.Not; import io.gitlab.jfronny.muscript.ast.bool.Not;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.BoolExpr; import io.gitlab.jfronny.muscript.ast.BoolExpr;
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral; import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
@ -14,8 +13,8 @@ public class BoolConditional extends BoolExpr {
public final BoolExpr trueExpr; public final BoolExpr trueExpr;
public final BoolExpr falseExpr; public final BoolExpr falseExpr;
public BoolConditional(int chStart, int chEnd, BoolExpr condition, BoolExpr trueExpr, BoolExpr falseExpr) { public BoolConditional(CodeLocation location, BoolExpr condition, BoolExpr trueExpr, BoolExpr falseExpr) {
super(Order.Conditional, chStart, chEnd); super(Order.Conditional, location);
this.condition = condition; this.condition = condition;
this.trueExpr = trueExpr; this.trueExpr = trueExpr;
this.falseExpr = falseExpr; this.falseExpr = falseExpr;
@ -33,8 +32,8 @@ public class BoolConditional extends BoolExpr {
BoolExpr falseExpr = this.falseExpr.optimize(); BoolExpr falseExpr = this.falseExpr.optimize();
if (condition instanceof BoolLiteral literal) return literal.value ? trueExpr : falseExpr; if (condition instanceof BoolLiteral literal) return literal.value ? trueExpr : falseExpr;
if (trueExpr.equals(falseExpr)) return trueExpr; if (trueExpr.equals(falseExpr)) return trueExpr;
if (condition instanceof Not not) return new BoolConditional(chStart, chEnd, not.inner, falseExpr, trueExpr); if (condition instanceof Not not) return new BoolConditional(location, not.inner, falseExpr, trueExpr);
return new BoolConditional(chStart, chEnd, condition, trueExpr, falseExpr); return new BoolConditional(location, condition, trueExpr, falseExpr);
} }
@Override @Override

View File

@ -1,8 +1,7 @@
package io.gitlab.jfronny.muscript.ast.conditional; package io.gitlab.jfronny.muscript.ast.conditional;
import io.gitlab.jfronny.muscript.ast.bool.Not; import io.gitlab.jfronny.muscript.ast.bool.Not;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic; import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.ast.BoolExpr; import io.gitlab.jfronny.muscript.ast.BoolExpr;
@ -16,8 +15,8 @@ public class DynamicConditional extends DynamicExpr {
public final DynamicExpr trueExpr; public final DynamicExpr trueExpr;
public final DynamicExpr falseExpr; public final DynamicExpr falseExpr;
public DynamicConditional(int chStart, int chEnd, BoolExpr condition, DynamicExpr trueExpr, DynamicExpr falseExpr) { public DynamicConditional(CodeLocation location, BoolExpr condition, DynamicExpr trueExpr, DynamicExpr falseExpr) {
super(Order.Conditional, chStart, chEnd); super(Order.Conditional, location);
this.condition = condition; this.condition = condition;
this.trueExpr = trueExpr; this.trueExpr = trueExpr;
this.falseExpr = falseExpr; this.falseExpr = falseExpr;
@ -35,8 +34,8 @@ public class DynamicConditional extends DynamicExpr {
DynamicExpr falseExpr = this.falseExpr.optimize(); DynamicExpr falseExpr = this.falseExpr.optimize();
if (condition instanceof BoolLiteral literal) return literal.value ? trueExpr : falseExpr; if (condition instanceof BoolLiteral literal) return literal.value ? trueExpr : falseExpr;
if (trueExpr.equals(falseExpr)) return trueExpr; if (trueExpr.equals(falseExpr)) return trueExpr;
if (condition instanceof Not not) return new DynamicConditional(chStart, chEnd, not.inner, falseExpr, trueExpr); if (condition instanceof Not not) return new DynamicConditional(location, not.inner, falseExpr, trueExpr);
return new DynamicConditional(chStart, chEnd, condition, trueExpr, falseExpr); return new DynamicConditional(location, condition, trueExpr, falseExpr);
} }
@Override @Override

View File

@ -1,8 +1,7 @@
package io.gitlab.jfronny.muscript.ast.conditional; package io.gitlab.jfronny.muscript.ast.conditional;
import io.gitlab.jfronny.muscript.ast.bool.Not; import io.gitlab.jfronny.muscript.ast.bool.Not;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.BoolExpr; import io.gitlab.jfronny.muscript.ast.BoolExpr;
import io.gitlab.jfronny.muscript.ast.NumberExpr; import io.gitlab.jfronny.muscript.ast.NumberExpr;
@ -15,8 +14,8 @@ public class NumberConditional extends NumberExpr {
public final NumberExpr trueExpr; public final NumberExpr trueExpr;
public final NumberExpr falseExpr; public final NumberExpr falseExpr;
public NumberConditional(int chStart, int chEnd, BoolExpr condition, NumberExpr trueExpr, NumberExpr falseExpr) { public NumberConditional(CodeLocation location, BoolExpr condition, NumberExpr trueExpr, NumberExpr falseExpr) {
super(Order.Conditional, chStart, chEnd); super(Order.Conditional, location);
this.condition = condition; this.condition = condition;
this.trueExpr = trueExpr; this.trueExpr = trueExpr;
this.falseExpr = falseExpr; this.falseExpr = falseExpr;
@ -34,8 +33,8 @@ public class NumberConditional extends NumberExpr {
NumberExpr falseExpr = this.falseExpr.optimize(); NumberExpr falseExpr = this.falseExpr.optimize();
if (condition instanceof BoolLiteral literal) return literal.value ? trueExpr : falseExpr; if (condition instanceof BoolLiteral literal) return literal.value ? trueExpr : falseExpr;
if (trueExpr.equals(falseExpr)) return trueExpr; if (trueExpr.equals(falseExpr)) return trueExpr;
if (condition instanceof Not not) return new NumberConditional(chStart, chEnd, not.inner, falseExpr, trueExpr); if (condition instanceof Not not) return new NumberConditional(location, not.inner, falseExpr, trueExpr);
return new NumberConditional(chStart, chEnd, condition, trueExpr, falseExpr); return new NumberConditional(location, condition, trueExpr, falseExpr);
} }
@Override @Override

View File

@ -1,8 +1,7 @@
package io.gitlab.jfronny.muscript.ast.conditional; package io.gitlab.jfronny.muscript.ast.conditional;
import io.gitlab.jfronny.muscript.ast.bool.Not; import io.gitlab.jfronny.muscript.ast.bool.Not;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.BoolExpr; import io.gitlab.jfronny.muscript.ast.BoolExpr;
import io.gitlab.jfronny.muscript.ast.StringExpr; import io.gitlab.jfronny.muscript.ast.StringExpr;
@ -15,8 +14,8 @@ public class StringConditional extends StringExpr {
public final StringExpr trueExpr; public final StringExpr trueExpr;
public final StringExpr falseExpr; public final StringExpr falseExpr;
public StringConditional(int chStart, int chEnd, BoolExpr condition, StringExpr trueExpr, StringExpr falseExpr) { public StringConditional(CodeLocation location, BoolExpr condition, StringExpr trueExpr, StringExpr falseExpr) {
super(Order.Conditional, chStart, chEnd); super(Order.Conditional, location);
this.condition = condition; this.condition = condition;
this.trueExpr = trueExpr; this.trueExpr = trueExpr;
this.falseExpr = falseExpr; this.falseExpr = falseExpr;
@ -34,8 +33,8 @@ public class StringConditional extends StringExpr {
StringExpr falseExpr = this.falseExpr.optimize(); StringExpr falseExpr = this.falseExpr.optimize();
if (condition instanceof BoolLiteral literal) return literal.value ? trueExpr : falseExpr; if (condition instanceof BoolLiteral literal) return literal.value ? trueExpr : falseExpr;
if (trueExpr.equals(falseExpr)) return trueExpr; if (trueExpr.equals(falseExpr)) return trueExpr;
if (condition instanceof Not not) return new StringConditional(chStart, chEnd, not.inner, falseExpr, trueExpr); if (condition instanceof Not not) return new StringConditional(location, not.inner, falseExpr, trueExpr);
return new StringConditional(chStart, chEnd, condition, trueExpr, falseExpr); return new StringConditional(location, condition, trueExpr, falseExpr);
} }
@Override @Override

View File

@ -17,8 +17,8 @@ public class UnresolvedConditional extends DynamicExpr {
private final Expr<?> trueExpr; private final Expr<?> trueExpr;
private final Expr<?> falseExpr; private final Expr<?> falseExpr;
public UnresolvedConditional(int chStart, int chEnd, BoolExpr condition, Expr<?> trueExpr, Expr<?> falseExpr) { public UnresolvedConditional(CodeLocation location, BoolExpr condition, Expr<?> trueExpr, Expr<?> falseExpr) {
super(Order.Conditional, chStart, chEnd); super(Order.Conditional, location);
this.condition = condition; this.condition = condition;
this.trueExpr = trueExpr; this.trueExpr = trueExpr;
this.falseExpr = falseExpr; this.falseExpr = falseExpr;
@ -36,8 +36,8 @@ public class UnresolvedConditional extends DynamicExpr {
Expr<?> falseExpr = this.falseExpr.optimize(); Expr<?> falseExpr = this.falseExpr.optimize();
if (condition instanceof BoolLiteral literal) return literal.value ? trueExpr.asDynamicExpr() : falseExpr.asDynamicExpr(); if (condition instanceof BoolLiteral literal) return literal.value ? trueExpr.asDynamicExpr() : falseExpr.asDynamicExpr();
if (trueExpr.equals(falseExpr)) return trueExpr.asDynamicExpr(); if (trueExpr.equals(falseExpr)) return trueExpr.asDynamicExpr();
if (condition instanceof Not not) return new UnresolvedConditional(chStart, chEnd, not.inner, falseExpr, trueExpr); if (condition instanceof Not not) return new UnresolvedConditional(location, not.inner, falseExpr, trueExpr);
return new UnresolvedConditional(chStart, chEnd, condition, trueExpr, falseExpr); return new UnresolvedConditional(location, condition, trueExpr, falseExpr);
} }
@Override @Override
@ -60,22 +60,22 @@ public class UnresolvedConditional extends DynamicExpr {
@Override @Override
public DynamicExpr asDynamicExpr() { public DynamicExpr asDynamicExpr() {
return new DynamicConditional(chStart, chEnd, condition, trueExpr.asDynamicExpr(), falseExpr.asDynamicExpr()); return new DynamicConditional(location, condition, trueExpr.asDynamicExpr(), falseExpr.asDynamicExpr());
} }
@Override @Override
public BoolExpr asBoolExpr() { public BoolExpr asBoolExpr() {
return new BoolConditional(chStart, chEnd, condition, trueExpr.asBoolExpr(), falseExpr.asBoolExpr()); return new BoolConditional(location, condition, trueExpr.asBoolExpr(), falseExpr.asBoolExpr());
} }
@Override @Override
public StringExpr asStringExpr() { public StringExpr asStringExpr() {
return new StringConditional(chStart, chEnd, condition, trueExpr.asStringExpr(), falseExpr.asStringExpr()); return new StringConditional(location, condition, trueExpr.asStringExpr(), falseExpr.asStringExpr());
} }
@Override @Override
public NumberExpr asNumberExpr() { public NumberExpr asNumberExpr() {
return new NumberConditional(chStart, chEnd, condition, trueExpr.asNumberExpr(), falseExpr.asNumberExpr()); return new NumberConditional(location, condition, trueExpr.asNumberExpr(), falseExpr.asNumberExpr());
} }
@Override @Override

View File

@ -14,8 +14,8 @@ public class Bind extends DynamicExpr {
public final DynamicExpr callable; public final DynamicExpr callable;
public final DynamicExpr parameter; public final DynamicExpr parameter;
public Bind(int chStart, int chEnd, DynamicExpr callable, DynamicExpr parameter) { public Bind(CodeLocation location, DynamicExpr callable, DynamicExpr parameter) {
super(Order.Call, chStart, chEnd); super(Order.Call, location);
this.callable = callable; this.callable = callable;
this.parameter = parameter; this.parameter = parameter;
} }
@ -47,6 +47,6 @@ public class Bind extends DynamicExpr {
@Override @Override
public DynamicExpr optimize() { public DynamicExpr optimize() {
return new Bind(chStart, chEnd, callable.optimize(), parameter.optimize()); return new Bind(location, callable.optimize(), parameter.optimize());
} }
} }

View File

@ -3,8 +3,7 @@ package io.gitlab.jfronny.muscript.ast.dynamic;
import io.gitlab.jfronny.muscript.ast.DynamicExpr; import io.gitlab.jfronny.muscript.ast.DynamicExpr;
import io.gitlab.jfronny.muscript.annotations.CanThrow; import io.gitlab.jfronny.muscript.annotations.CanThrow;
import io.gitlab.jfronny.muscript.annotations.UncheckedDynamic; import io.gitlab.jfronny.muscript.annotations.UncheckedDynamic;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.*; import io.gitlab.jfronny.muscript.data.dynamic.*;
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal; import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
@ -21,8 +20,8 @@ public class Call extends DynamicExpr {
public final DynamicExpr left; public final DynamicExpr left;
public final List<Arg> args; public final List<Arg> args;
public Call(int chStart, int chEnd, DynamicExpr left, List<Arg> args) { public Call(CodeLocation location, DynamicExpr left, List<Arg> args) {
super(Order.Call, chStart, chEnd); super(Order.Call, location);
this.left = left; this.left = left;
this.args = args; this.args = args;
} }
@ -36,18 +35,18 @@ public class Call extends DynamicExpr {
.asCallable(); .asCallable();
arg = DFinal.of(args.stream().flatMap(e -> e.get(dataRoot)).toArray(Dynamic[]::new)); arg = DFinal.of(args.stream().flatMap(e -> e.get(dataRoot)).toArray(Dynamic[]::new));
} catch (DynamicTypeConversionException e) { } catch (DynamicTypeConversionException e) {
throw e.locational(chStart, chEnd); throw e.locational(location);
} catch (RuntimeException e) { } catch (RuntimeException e) {
throw new LocationalException(chStart, chEnd, "Could not perform call successfully", e); throw new LocationalException(location, "Could not perform call successfully", e);
} }
try { try {
return dc.getValue().apply(arg); return dc.getValue().apply(arg);
} catch (LocationalException le) { } catch (LocationalException le) {
throw le.appendStack(new StackFrame.Raw(dc.getName(), left.chStart)); throw le.appendStack(new StackFrame.Raw(location.file(), dc.getName(), left.location.chStart()));
} catch (DynamicTypeConversionException e) { } catch (DynamicTypeConversionException e) {
throw e.locational(chStart, chEnd); throw e.locational(location);
} catch (RuntimeException e) { } catch (RuntimeException e) {
throw new LocationalException(chStart, chEnd, "Could not perform call successfully", e); throw new LocationalException(location, "Could not perform call successfully", e);
} }
} }
@ -60,7 +59,7 @@ public class Call extends DynamicExpr {
args.add(new Arg(bind.parameter, false)); args.add(new Arg(bind.parameter, false));
} }
for (Arg arg : this.args) args.add(arg.optimize()); for (Arg arg : this.args) args.add(arg.optimize());
return new Call(chStart, chEnd, left, args); return new Call(location, left, args);
} }
@Override @Override

View File

@ -19,12 +19,12 @@ public class Closure extends DynamicExpr {
private final DynamicExpr fin; private final DynamicExpr fin;
private final boolean variadic; private final boolean variadic;
public Closure(int chStart, int chEnd, List<String> boundArgs, List<Expr<?>> expressions, boolean variadic) { public Closure(CodeLocation location, List<String> boundArgs, List<Expr<?>> expressions, boolean variadic) {
this(chStart, chEnd, boundArgs, expressions.subList(0, expressions.size() - 1), expressions.get(expressions.size() - 1).asDynamicExpr(), variadic); this(location, boundArgs, expressions.subList(0, expressions.size() - 1), expressions.get(expressions.size() - 1).asDynamicExpr(), variadic);
} }
private Closure(int chStart, int chEnd, List<String> boundArgs, List<Expr<?>> steps, DynamicExpr fin, boolean variadic) { private Closure(CodeLocation location, List<String> boundArgs, List<Expr<?>> steps, DynamicExpr fin, boolean variadic) {
super(Order.Primary, chStart, chEnd); super(Order.Primary, location);
this.boundArgs = List.copyOf(boundArgs); this.boundArgs = List.copyOf(boundArgs);
this.steps = List.copyOf(steps); this.steps = List.copyOf(steps);
this.fin = fin; this.fin = fin;
@ -37,8 +37,8 @@ public class Closure extends DynamicExpr {
int ac = args.size(); int ac = args.size();
int ae = boundArgs.size(); int ae = boundArgs.size();
if (variadic) ae--; if (variadic) ae--;
if (ac < ae) throw new LocationalException(chStart, chEnd, "Invoked with too few arguments (expected " + ae + " but got " + ac + ")"); if (ac < ae) throw new LocationalException(location, "Invoked with too few arguments (expected " + ae + " but got " + ac + ")");
if (!variadic && ac > ae) throw new LocationalException(chStart, chEnd, "Invoked with too many arguments (expected " + ae + " but got " + ac + ")"); if (!variadic && ac > ae) throw new LocationalException(location, "Invoked with too many arguments (expected " + ae + " but got " + ac + ")");
Scope fork = dataRoot.fork(); Scope fork = dataRoot.fork();
for (int i = 0; i < ae; i++) fork.set(boundArgs.get(i), args.get(i)); for (int i = 0; i < ae; i++) fork.set(boundArgs.get(i), args.get(i));
if (variadic) { if (variadic) {
@ -56,7 +56,7 @@ public class Closure extends DynamicExpr {
@Override @Override
public DynamicExpr optimize() { public DynamicExpr optimize() {
// Eliminating side effect free steps might be possible, but would be too much work imo // Eliminating side effect free steps might be possible, but would be too much work imo
return new Closure(chStart, chEnd, boundArgs, steps.stream().<Expr<?>>map(Expr::optimize).toList(), fin.optimize(), variadic); return new Closure(location, boundArgs, steps.stream().<Expr<?>>map(Expr::optimize).toList(), fin.optimize(), variadic);
} }
@Override @Override

View File

@ -2,6 +2,7 @@ package io.gitlab.jfronny.muscript.ast.dynamic;
import io.gitlab.jfronny.muscript.ast.*; import io.gitlab.jfronny.muscript.ast.*;
import io.gitlab.jfronny.muscript.ast.dynamic.unpack.*; import io.gitlab.jfronny.muscript.ast.dynamic.unpack.*;
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.ExprWriter;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.*; import io.gitlab.jfronny.muscript.data.dynamic.*;
@ -12,8 +13,8 @@ import java.io.IOException;
public class DynamicCoerce extends DynamicExpr { public class DynamicCoerce extends DynamicExpr {
public final Expr<?> inner; public final Expr<?> inner;
public DynamicCoerce(int chStart, int chEnd, Expr<?> inner) { public DynamicCoerce(CodeLocation location, Expr<?> inner) {
super(inner.order, chStart, chEnd); super(inner.order, location);
this.inner = inner; this.inner = inner;
if (!(inner instanceof DynamicExpr) if (!(inner instanceof DynamicExpr)
&& !(inner instanceof BoolExpr) && !(inner instanceof BoolExpr)
@ -40,7 +41,7 @@ public class DynamicCoerce extends DynamicExpr {
if (inner instanceof BoolUnpack unpack) return unpack.inner; if (inner instanceof BoolUnpack unpack) return unpack.inner;
if (inner instanceof NumberUnpack unpack) return unpack.inner; if (inner instanceof NumberUnpack unpack) return unpack.inner;
if (inner instanceof StringUnpack unpack) return unpack.inner; if (inner instanceof StringUnpack unpack) return unpack.inner;
return new DynamicCoerce(chStart, chEnd, inner); return new DynamicCoerce(location, inner);
} }
@Override @Override

View File

@ -18,12 +18,12 @@ public class Get extends DynamicExpr {
private final DynamicExpr left; private final DynamicExpr left;
private final Expr<?> name; private final Expr<?> name;
public Get(int chStart, int chEnd, DynamicExpr left, Expr<?> name) { public Get(CodeLocation location, DynamicExpr left, Expr<?> name) {
super(Order.Call, chStart, chEnd); super(Order.Call, location);
this.left = left; this.left = left;
this.name = name; this.name = name;
if (name.getResultType() != Type.String && name.getResultType() != Type.Number && name.getResultType() != Type.Dynamic) { if (name.getResultType() != Type.String && name.getResultType() != Type.Number && name.getResultType() != Type.Dynamic) {
throw new TypeMismatchException(chStart, chEnd, Type.String, name.getResultType(), "Name must be either a string or a number"); throw new TypeMismatchException(location, Type.String, name.getResultType(), "Name must be either a string or a number");
} }
} }
@ -35,14 +35,14 @@ public class Get extends DynamicExpr {
} else if (left instanceof DList l) { } else if (left instanceof DList l) {
return l.get(name.asNumberExpr().get(dataRoot).intValue()); return l.get(name.asNumberExpr().get(dataRoot).intValue());
} }
throw new DynamicTypeConversionException("object or list", left).locational(chStart, chEnd); throw new DynamicTypeConversionException("object or list", left).locational(location);
} }
@Override @Override
public DynamicExpr optimize() { public DynamicExpr optimize() {
DynamicExpr left = this.left.optimize(); DynamicExpr left = this.left.optimize();
Expr<?> name = this.name.optimize(); Expr<?> name = this.name.optimize();
return new Get(chStart, chEnd, left, name); return new Get(location, left, name);
} }
@Override @Override

View File

@ -14,8 +14,8 @@ import java.util.Map;
public class ObjectLiteral extends DynamicExpr { public class ObjectLiteral extends DynamicExpr {
public final Map<String, DynamicExpr> content; public final Map<String, DynamicExpr> content;
public ObjectLiteral(int chStart, int chEnd, Map<String, DynamicExpr> content) { public ObjectLiteral(CodeLocation location, Map<String, DynamicExpr> content) {
super(Order.Primary, chStart, chEnd); super(Order.Primary, location);
this.content = content; this.content = content;
} }
@ -37,8 +37,8 @@ public class ObjectLiteral extends DynamicExpr {
else literal = false; else literal = false;
content.put(entry.getKey(), de); content.put(entry.getKey(), de);
} }
if (literal) return new DynamicLiteral<>(chStart, chEnd, DFinal.of(literalContent)); if (literal) return new DynamicLiteral<>(location, DFinal.of(literalContent));
return new ObjectLiteral(chStart, chEnd, content); return new ObjectLiteral(location, content);
} }
@Override @Override

View File

@ -15,8 +15,8 @@ import java.io.IOException;
public class Variable extends DynamicExpr { public class Variable extends DynamicExpr {
public final String name; public final String name;
public Variable(int chStart, int chEnd, String name) { public Variable(CodeLocation location, String name) {
super(Order.Primary, chStart, chEnd); super(Order.Primary, location);
this.name = name; this.name = name;
} }
@ -24,7 +24,7 @@ public class Variable extends DynamicExpr {
public Dynamic<?> get(Scope dataRoot) { public Dynamic<?> get(Scope dataRoot) {
if (name.equals("this")) return dataRoot; if (name.equals("this")) return dataRoot;
if (dataRoot.has(name)) return dataRoot.get(name); if (dataRoot.has(name)) return dataRoot.get(name);
else throw new LocationalException(chStart, chEnd, "This object doesn't contain '" + name + "'"); else throw new LocationalException(location, "This object doesn't contain '" + name + "'");
} }
@Override @Override

View File

@ -1,8 +1,7 @@
package io.gitlab.jfronny.muscript.ast.dynamic.assign; package io.gitlab.jfronny.muscript.ast.dynamic.assign;
import io.gitlab.jfronny.muscript.ast.BoolExpr; import io.gitlab.jfronny.muscript.ast.BoolExpr;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.error.LocationalException; import io.gitlab.jfronny.muscript.error.LocationalException;
@ -12,9 +11,9 @@ public class BoolAssign extends BoolExpr {
private final String name; private final String name;
private final BoolExpr value; private final BoolExpr value;
protected BoolAssign(int chStart, int chEnd, String name, BoolExpr value) { protected BoolAssign(CodeLocation location, String name, BoolExpr value) {
super(Order.Primary, chStart, chEnd); super(Order.Primary, location);
if (name.equals("this")) throw new LocationalException(chStart, chEnd, "Cannot reassign 'this'"); if (name.equals("this")) throw new LocationalException(location, "Cannot reassign 'this'");
this.name = name; this.name = name;
this.value = value; this.value = value;
} }
@ -28,7 +27,7 @@ public class BoolAssign extends BoolExpr {
@Override @Override
public BoolExpr optimize() { public BoolExpr optimize() {
return new BoolAssign(chStart, chEnd, name, value.optimize()); return new BoolAssign(location, name, value.optimize());
} }
@Override @Override

View File

@ -13,9 +13,9 @@ public class DynamicAssign extends DynamicExpr {
private final String name; private final String name;
private final DynamicExpr value; private final DynamicExpr value;
public DynamicAssign(int chStart, int chEnd, String name, DynamicExpr value) { public DynamicAssign(CodeLocation location, String name, DynamicExpr value) {
super(Order.Primary, chStart, chEnd); super(Order.Primary, location);
if (name.equals("this")) throw new LocationalException(chStart, chEnd, "Cannot reassign 'this'"); if (name.equals("this")) throw new LocationalException(location, "Cannot reassign 'this'");
this.name = name; this.name = name;
this.value = value; this.value = value;
} }
@ -29,7 +29,7 @@ public class DynamicAssign extends DynamicExpr {
@Override @Override
public DynamicExpr optimize() { public DynamicExpr optimize() {
return new DynamicAssign(chStart, chEnd, name, value.optimize()); return new DynamicAssign(location, name, value.optimize());
} }
@Override @Override
@ -40,17 +40,17 @@ public class DynamicAssign extends DynamicExpr {
@Override @Override
public BoolExpr asBoolExpr() { public BoolExpr asBoolExpr() {
return new BoolAssign(chStart, chEnd, name, value.asBoolExpr()); return new BoolAssign(location, name, value.asBoolExpr());
} }
@Override @Override
public StringExpr asStringExpr() { public StringExpr asStringExpr() {
return new StringAssign(chStart, chEnd, name, value.asStringExpr()); return new StringAssign(location, name, value.asStringExpr());
} }
@Override @Override
public NumberExpr asNumberExpr() { public NumberExpr asNumberExpr() {
return new NumberAssign(chStart, chEnd, name, value.asNumberExpr()); return new NumberAssign(location, name, value.asNumberExpr());
} }
@Override @Override

View File

@ -1,8 +1,7 @@
package io.gitlab.jfronny.muscript.ast.dynamic.assign; package io.gitlab.jfronny.muscript.ast.dynamic.assign;
import io.gitlab.jfronny.muscript.ast.NumberExpr; import io.gitlab.jfronny.muscript.ast.NumberExpr;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.error.LocationalException; import io.gitlab.jfronny.muscript.error.LocationalException;
@ -12,9 +11,9 @@ public class NumberAssign extends NumberExpr {
private final String name; private final String name;
private final NumberExpr value; private final NumberExpr value;
protected NumberAssign(int chStart, int chEnd, String name, NumberExpr value) { protected NumberAssign(CodeLocation location, String name, NumberExpr value) {
super(Order.Primary, chStart, chEnd); super(Order.Primary, location);
if (name.equals("this")) throw new LocationalException(chStart, chEnd, "Cannot reassign 'this'"); if (name.equals("this")) throw new LocationalException(location, "Cannot reassign 'this'");
this.name = name; this.name = name;
this.value = value; this.value = value;
} }
@ -28,7 +27,7 @@ public class NumberAssign extends NumberExpr {
@Override @Override
public NumberExpr optimize() { public NumberExpr optimize() {
return new NumberAssign(chStart, chEnd, name, value.optimize()); return new NumberAssign(location, name, value.optimize());
} }
@Override @Override

View File

@ -1,8 +1,7 @@
package io.gitlab.jfronny.muscript.ast.dynamic.assign; package io.gitlab.jfronny.muscript.ast.dynamic.assign;
import io.gitlab.jfronny.muscript.ast.StringExpr; import io.gitlab.jfronny.muscript.ast.StringExpr;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.error.LocationalException; import io.gitlab.jfronny.muscript.error.LocationalException;
@ -12,9 +11,9 @@ public class StringAssign extends StringExpr {
private final String name; private final String name;
private final StringExpr value; private final StringExpr value;
protected StringAssign(int chStart, int chEnd, String name, StringExpr value) { protected StringAssign(CodeLocation location, String name, StringExpr value) {
super(Order.Primary, chStart, chEnd); super(Order.Primary, location);
if (name.equals("this")) throw new LocationalException(chStart, chEnd, "Cannot reassign 'this'"); if (name.equals("this")) throw new LocationalException(location, "Cannot reassign 'this'");
this.name = name; this.name = name;
this.value = value; this.value = value;
} }
@ -28,7 +27,7 @@ public class StringAssign extends StringExpr {
@Override @Override
public StringExpr optimize() { public StringExpr optimize() {
return new StringAssign(chStart, chEnd, name, value.optimize()); return new StringAssign(location, name, value.optimize());
} }
@Override @Override

View File

@ -1,5 +1,6 @@
package io.gitlab.jfronny.muscript.ast.dynamic.unpack; package io.gitlab.jfronny.muscript.ast.dynamic.unpack;
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.ExprWriter;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.DynamicTypeConversionException; import io.gitlab.jfronny.muscript.data.dynamic.DynamicTypeConversionException;
@ -16,8 +17,8 @@ import java.io.IOException;
public class BoolUnpack extends BoolExpr { public class BoolUnpack extends BoolExpr {
public final DynamicExpr inner; public final DynamicExpr inner;
public BoolUnpack(int chStart, int chEnd, DynamicExpr inner) { public BoolUnpack(CodeLocation location, DynamicExpr inner) {
super(inner.order, chStart, chEnd); super(inner.order, location);
this.inner = inner; this.inner = inner;
} }
@ -26,7 +27,7 @@ public class BoolUnpack extends BoolExpr {
try { try {
return inner.get(dataRoot).asBool().getValue(); return inner.get(dataRoot).asBool().getValue();
} catch (DynamicTypeConversionException e) { } catch (DynamicTypeConversionException e) {
throw e.locational(chStart, chEnd); throw e.locational(location);
} }
} }
@ -34,7 +35,7 @@ public class BoolUnpack extends BoolExpr {
public BoolExpr optimize() { public BoolExpr optimize() {
DynamicExpr inner = this.inner.optimize(); DynamicExpr inner = this.inner.optimize();
if (inner instanceof DynamicCoerce coerce) return coerce.inner.asBoolExpr(); if (inner instanceof DynamicCoerce coerce) return coerce.inner.asBoolExpr();
return new BoolUnpack(chStart, chEnd, inner); return new BoolUnpack(location, inner);
} }
@Override @Override

View File

@ -1,5 +1,6 @@
package io.gitlab.jfronny.muscript.ast.dynamic.unpack; package io.gitlab.jfronny.muscript.ast.dynamic.unpack;
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.ExprWriter;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.DynamicTypeConversionException; import io.gitlab.jfronny.muscript.data.dynamic.DynamicTypeConversionException;
@ -16,8 +17,8 @@ import java.io.IOException;
public class NumberUnpack extends NumberExpr { public class NumberUnpack extends NumberExpr {
public final DynamicExpr inner; public final DynamicExpr inner;
public NumberUnpack(int chStart, int chEnd, DynamicExpr inner) { public NumberUnpack(CodeLocation location, DynamicExpr inner) {
super(inner.order, chStart, chEnd); super(inner.order, location);
this.inner = inner; this.inner = inner;
} }
@ -26,7 +27,7 @@ public class NumberUnpack extends NumberExpr {
try { try {
return inner.get(dataRoot).asNumber().getValue(); return inner.get(dataRoot).asNumber().getValue();
} catch (DynamicTypeConversionException e) { } catch (DynamicTypeConversionException e) {
throw e.locational(chStart, chEnd); throw e.locational(location);
} }
} }
@ -34,7 +35,7 @@ public class NumberUnpack extends NumberExpr {
public NumberExpr optimize() { public NumberExpr optimize() {
DynamicExpr inner = this.inner.optimize(); DynamicExpr inner = this.inner.optimize();
if (inner instanceof DynamicCoerce coerce) return coerce.inner.asNumberExpr(); if (inner instanceof DynamicCoerce coerce) return coerce.inner.asNumberExpr();
return new NumberUnpack(chStart, chEnd, inner.optimize()); return new NumberUnpack(location, inner.optimize());
} }
@Override @Override

View File

@ -1,5 +1,6 @@
package io.gitlab.jfronny.muscript.ast.dynamic.unpack; package io.gitlab.jfronny.muscript.ast.dynamic.unpack;
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.ExprWriter;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.DynamicTypeConversionException; import io.gitlab.jfronny.muscript.data.dynamic.DynamicTypeConversionException;
@ -16,8 +17,8 @@ import java.io.IOException;
public class StringUnpack extends StringExpr { public class StringUnpack extends StringExpr {
public final DynamicExpr inner; public final DynamicExpr inner;
public StringUnpack(int chStart, int chEnd, DynamicExpr inner) { public StringUnpack(CodeLocation location, DynamicExpr inner) {
super(inner.order, chStart, chEnd); super(inner.order, location);
this.inner = inner; this.inner = inner;
} }
@ -26,7 +27,7 @@ public class StringUnpack extends StringExpr {
try { try {
return inner.get(dataRoot).asString().getValue(); return inner.get(dataRoot).asString().getValue();
} catch (DynamicTypeConversionException e) { } catch (DynamicTypeConversionException e) {
throw e.locational(chStart, chEnd); throw e.locational(location);
} }
} }
@ -34,7 +35,7 @@ public class StringUnpack extends StringExpr {
public StringExpr optimize() { public StringExpr optimize() {
DynamicExpr inner = this.inner.optimize(); DynamicExpr inner = this.inner.optimize();
if (inner instanceof DynamicCoerce coerce) return coerce.inner.asStringExpr(); if (inner instanceof DynamicCoerce coerce) return coerce.inner.asStringExpr();
return new StringUnpack(chStart, chEnd, inner); return new StringUnpack(location, inner);
} }
@Override @Override

View File

@ -1,7 +1,6 @@
package io.gitlab.jfronny.muscript.ast.literal; package io.gitlab.jfronny.muscript.ast.literal;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.BoolExpr; import io.gitlab.jfronny.muscript.ast.BoolExpr;
@ -10,8 +9,8 @@ import java.io.IOException;
public final class BoolLiteral extends BoolExpr { public final class BoolLiteral extends BoolExpr {
public final boolean value; public final boolean value;
public BoolLiteral(int chStart, int chEnd, boolean value) { public BoolLiteral(CodeLocation location, boolean value) {
super(Order.Primary, chStart, chEnd); super(Order.Primary, location);
this.value = value; this.value = value;
} }

View File

@ -1,7 +1,6 @@
package io.gitlab.jfronny.muscript.ast.literal; package io.gitlab.jfronny.muscript.ast.literal;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic; import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.ast.DynamicExpr; import io.gitlab.jfronny.muscript.ast.DynamicExpr;
@ -11,8 +10,8 @@ import java.io.IOException;
public final class DynamicLiteral<T> extends DynamicExpr { public final class DynamicLiteral<T> extends DynamicExpr {
public final Dynamic<T> value; public final Dynamic<T> value;
public DynamicLiteral(int chStart, int chEnd, Dynamic<T> value) { public DynamicLiteral(CodeLocation location, Dynamic<T> value) {
super(Order.Primary, chStart, chEnd); super(Order.Primary, location);
this.value = value; this.value = value;
} }

View File

@ -1,8 +1,7 @@
package io.gitlab.jfronny.muscript.ast.literal; package io.gitlab.jfronny.muscript.ast.literal;
import io.gitlab.jfronny.commons.StringFormatter; import io.gitlab.jfronny.commons.StringFormatter;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.NumberExpr; import io.gitlab.jfronny.muscript.ast.NumberExpr;
@ -11,8 +10,8 @@ import java.io.IOException;
public final class NumberLiteral extends NumberExpr { public final class NumberLiteral extends NumberExpr {
public final double value; public final double value;
public NumberLiteral(int chStart, int chEnd, double value) { public NumberLiteral(CodeLocation location, double value) {
super(Order.Primary, chStart, chEnd); super(Order.Primary, location);
this.value = value; this.value = value;
} }

View File

@ -1,8 +1,7 @@
package io.gitlab.jfronny.muscript.ast.literal; package io.gitlab.jfronny.muscript.ast.literal;
import io.gitlab.jfronny.muscript.ast.StringExpr; import io.gitlab.jfronny.muscript.ast.StringExpr;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import java.io.IOException; import java.io.IOException;
@ -10,8 +9,8 @@ import java.io.IOException;
public final class StringLiteral extends StringExpr { public final class StringLiteral extends StringExpr {
public final String value; public final String value;
public StringLiteral(int chStart, int chEnd, String value) { public StringLiteral(CodeLocation location, String value) {
super(Order.Primary, chStart, chEnd); super(Order.Primary, location);
this.value = value; this.value = value;
} }

View File

@ -1,7 +1,6 @@
package io.gitlab.jfronny.muscript.ast.math; package io.gitlab.jfronny.muscript.ast.math;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.Expr; import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.NumberExpr; import io.gitlab.jfronny.muscript.ast.NumberExpr;
@ -13,8 +12,8 @@ public class Divide extends NumberExpr {
public final NumberExpr dividend; public final NumberExpr dividend;
public final NumberExpr divisor; public final NumberExpr divisor;
public Divide(int chStart, int chEnd, NumberExpr dividend, NumberExpr divisor) { public Divide(CodeLocation location, NumberExpr dividend, NumberExpr divisor) {
super(Order.Factor, chStart, chEnd); super(Order.Factor, location);
this.dividend = dividend; this.dividend = dividend;
this.divisor = divisor; this.divisor = divisor;
} }
@ -29,10 +28,10 @@ public class Divide extends NumberExpr {
NumberExpr dividend = this.dividend.optimize(); NumberExpr dividend = this.dividend.optimize();
NumberExpr divisor = this.divisor.optimize(); NumberExpr divisor = this.divisor.optimize();
if (dividend instanceof NumberLiteral litL && divisor instanceof NumberLiteral litR) if (dividend instanceof NumberLiteral litL && divisor instanceof NumberLiteral litR)
return Expr.literal(chStart, chEnd, litL.value / litR.value); return literal(location, litL.value / litR.value);
if (dividend instanceof Divide divide && divide.dividend instanceof NumberLiteral literal) if (dividend instanceof Divide divide && divide.dividend instanceof NumberLiteral literal)
return new Divide(chStart, chEnd, divide.dividend, new Multiply(divide.chStart, dividend.chEnd, divisor, literal)).optimize(); return new Divide(location, divide.dividend, new Multiply(divide.location, divisor, literal)).optimize();
return new Divide(chStart, chEnd, dividend, divisor); return new Divide(location, dividend, divisor);
} }
@Override @Override

View File

@ -1,7 +1,6 @@
package io.gitlab.jfronny.muscript.ast.math; package io.gitlab.jfronny.muscript.ast.math;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.Expr; import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.NumberExpr; import io.gitlab.jfronny.muscript.ast.NumberExpr;
@ -12,8 +11,8 @@ import java.io.IOException;
public class Invert extends NumberExpr { public class Invert extends NumberExpr {
public final NumberExpr inner; public final NumberExpr inner;
public Invert(int chStart, int chEnd, NumberExpr inner) { public Invert(CodeLocation location, NumberExpr inner) {
super(Order.Unary, chStart, chEnd); super(Order.Unary, location);
this.inner = inner; this.inner = inner;
} }
@ -26,9 +25,9 @@ public class Invert extends NumberExpr {
public NumberExpr optimize() { public NumberExpr optimize() {
NumberExpr inner = this.inner.optimize(); NumberExpr inner = this.inner.optimize();
if (inner instanceof Invert invert) return invert.inner; if (inner instanceof Invert invert) return invert.inner;
if (inner instanceof NumberLiteral literal) return Expr.literal(chStart, chEnd, -literal.value); if (inner instanceof NumberLiteral literal) return Expr.literal(location, -literal.value);
if (inner instanceof Minus minus) return new Minus(chStart, chEnd, minus.subtrahend, minus.minuend).optimize(); if (inner instanceof Minus minus) return new Minus(location, minus.subtrahend, minus.minuend).optimize();
return new Invert(chStart, chEnd, inner); return new Invert(location, inner);
} }
@Override @Override

View File

@ -1,7 +1,6 @@
package io.gitlab.jfronny.muscript.ast.math; package io.gitlab.jfronny.muscript.ast.math;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.Expr; import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.NumberExpr; import io.gitlab.jfronny.muscript.ast.NumberExpr;
@ -13,8 +12,8 @@ public class Minus extends NumberExpr {
public final NumberExpr minuend; public final NumberExpr minuend;
public final NumberExpr subtrahend; public final NumberExpr subtrahend;
public Minus(int chStart, int chEnd, NumberExpr minuend, NumberExpr subtrahend) { public Minus(CodeLocation location, NumberExpr minuend, NumberExpr subtrahend) {
super(Order.Term, chStart, chEnd); super(Order.Term, location);
this.minuend = minuend; this.minuend = minuend;
this.subtrahend = subtrahend; this.subtrahend = subtrahend;
} }
@ -29,10 +28,15 @@ public class Minus extends NumberExpr {
NumberExpr minuend = this.minuend.optimize(); NumberExpr minuend = this.minuend.optimize();
NumberExpr subtrahend = this.subtrahend.optimize(); NumberExpr subtrahend = this.subtrahend.optimize();
if (minuend instanceof NumberLiteral litM && subtrahend instanceof NumberLiteral litS) if (minuend instanceof NumberLiteral litM && subtrahend instanceof NumberLiteral litS)
return Expr.literal(chStart, chEnd, litM.value - litS.value); return literal(location, litM.value - litS.value);
if (minuend instanceof Minus minus) if (minuend instanceof Minus minus)
return new Minus(chStart, chEnd, minus.minuend, new Plus(minus.chStart, minus.chEnd, minus.subtrahend, subtrahend)).optimize(); return new Minus(location, minus.minuend, new Plus(minus.location, minus.subtrahend, subtrahend)).optimize();
return new Minus(chStart, chEnd, minuend, subtrahend); if (minuend instanceof Invert invM && subtrahend instanceof Invert invS)
return new Minus(location, invS.inner, invM.inner);
if (minuend instanceof Invert inv)
return new Invert(location, new Plus(location, inv.inner, subtrahend)).optimize();
if (subtrahend instanceof Invert inv) return new Plus(location, minuend, inv.inner).optimize();
return new Minus(location, minuend, subtrahend);
} }
@Override @Override

View File

@ -1,7 +1,6 @@
package io.gitlab.jfronny.muscript.ast.math; package io.gitlab.jfronny.muscript.ast.math;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.Expr; import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.NumberExpr; import io.gitlab.jfronny.muscript.ast.NumberExpr;
@ -13,8 +12,8 @@ public class Modulo extends NumberExpr {
private final NumberExpr dividend; private final NumberExpr dividend;
private final NumberExpr divisor; private final NumberExpr divisor;
public Modulo(int chStart, int chEnd, NumberExpr dividend, NumberExpr divisor) { public Modulo(CodeLocation location, NumberExpr dividend, NumberExpr divisor) {
super(Order.Factor, chStart, chEnd); super(Order.Factor, location);
this.dividend = dividend; this.dividend = dividend;
this.divisor = divisor; this.divisor = divisor;
} }
@ -29,8 +28,8 @@ public class Modulo extends NumberExpr {
NumberExpr dividend = this.dividend.optimize(); NumberExpr dividend = this.dividend.optimize();
NumberExpr divisor = this.divisor.optimize(); NumberExpr divisor = this.divisor.optimize();
if (dividend instanceof NumberLiteral litD && divisor instanceof NumberLiteral litS) if (dividend instanceof NumberLiteral litD && divisor instanceof NumberLiteral litS)
return Expr.literal(chStart, chEnd, litD.value % litS.value); return Expr.literal(location, litD.value % litS.value);
return new Modulo(chStart, chEnd, dividend, divisor); return new Modulo(location, dividend, divisor);
} }
@Override @Override

View File

@ -1,7 +1,6 @@
package io.gitlab.jfronny.muscript.ast.math; package io.gitlab.jfronny.muscript.ast.math;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.Expr; import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.NumberExpr; import io.gitlab.jfronny.muscript.ast.NumberExpr;
@ -13,8 +12,8 @@ public class Multiply extends NumberExpr {
public final NumberExpr multiplier; public final NumberExpr multiplier;
public final NumberExpr multiplicand; public final NumberExpr multiplicand;
public Multiply(int chStart, int chEnd, NumberExpr multiplier, NumberExpr multiplicand) { public Multiply(CodeLocation location, NumberExpr multiplier, NumberExpr multiplicand) {
super(Order.Factor, chStart, chEnd); super(Order.Factor, location);
this.multiplier = multiplier; this.multiplier = multiplier;
this.multiplicand = multiplicand; this.multiplicand = multiplicand;
} }
@ -29,8 +28,8 @@ public class Multiply extends NumberExpr {
NumberExpr multiplier = this.multiplier.optimize(); NumberExpr multiplier = this.multiplier.optimize();
NumberExpr multiplicand = this.multiplicand.optimize(); NumberExpr multiplicand = this.multiplicand.optimize();
if (multiplier instanceof NumberLiteral litEr && multiplicand instanceof NumberLiteral litAnd) if (multiplier instanceof NumberLiteral litEr && multiplicand instanceof NumberLiteral litAnd)
return Expr.literal(chStart, chEnd, litEr.value * litAnd.value); return literal(location, litEr.value * litAnd.value);
return new Multiply(chStart, chEnd, multiplier, multiplicand); return new Multiply(location, multiplier, multiplicand);
} }
@Override @Override

View File

@ -1,7 +1,6 @@
package io.gitlab.jfronny.muscript.ast.math; package io.gitlab.jfronny.muscript.ast.math;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.Expr; import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.NumberExpr; import io.gitlab.jfronny.muscript.ast.NumberExpr;
@ -13,8 +12,8 @@ public class Plus extends NumberExpr {
public final NumberExpr augend; public final NumberExpr augend;
public final NumberExpr addend; public final NumberExpr addend;
public Plus(int chStart, int chEnd, NumberExpr augend, NumberExpr addend) { public Plus(CodeLocation location, NumberExpr augend, NumberExpr addend) {
super(Order.Term, chStart, chEnd); super(Order.Term, location);
this.augend = augend; this.augend = augend;
this.addend = addend; this.addend = addend;
} }
@ -29,8 +28,12 @@ public class Plus extends NumberExpr {
NumberExpr augend = this.augend.optimize(); NumberExpr augend = this.augend.optimize();
NumberExpr addend = this.addend.optimize(); NumberExpr addend = this.addend.optimize();
if (augend instanceof NumberLiteral litU && addend instanceof NumberLiteral litD) if (augend instanceof NumberLiteral litU && addend instanceof NumberLiteral litD)
return Expr.literal(chStart, chEnd, litU.value + litD.value); return literal(location, litU.value + litD.value);
return new Plus(chStart, chEnd, augend, addend); if (augend instanceof Invert invU && addend instanceof Invert invD)
return new Invert(location, new Plus(location, invU.inner, invD.inner)).optimize();
if (augend instanceof Invert inv) return new Minus(location, addend, inv.inner).optimize();
if (addend instanceof Invert inv) return new Minus(location, augend, inv.inner).optimize();
return new Plus(location, augend, addend);
} }
@Override @Override

View File

@ -1,7 +1,6 @@
package io.gitlab.jfronny.muscript.ast.math; package io.gitlab.jfronny.muscript.ast.math;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.Expr; import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.NumberExpr; import io.gitlab.jfronny.muscript.ast.NumberExpr;
@ -13,8 +12,8 @@ public class Power extends NumberExpr {
private final NumberExpr base; private final NumberExpr base;
private final NumberExpr exponent; private final NumberExpr exponent;
public Power(int chStart, int chEnd, NumberExpr base, NumberExpr exponent) { public Power(CodeLocation location, NumberExpr base, NumberExpr exponent) {
super(Order.Exp, chStart, chEnd); super(Order.Exp, location);
this.base = base; this.base = base;
this.exponent = exponent; this.exponent = exponent;
} }
@ -29,18 +28,18 @@ public class Power extends NumberExpr {
NumberExpr base = this.base.optimize(); NumberExpr base = this.base.optimize();
NumberExpr exponent = this.exponent.optimize(); NumberExpr exponent = this.exponent.optimize();
if (base instanceof NumberLiteral litB && exponent instanceof NumberLiteral litE) if (base instanceof NumberLiteral litB && exponent instanceof NumberLiteral litE)
return Expr.literal(chStart, chEnd, Math.pow(litB.value, litE.value)); return literal(location, Math.pow(litB.value, litE.value));
if (exponent instanceof NumberLiteral exp && base instanceof Multiply multiply) { if (exponent instanceof NumberLiteral exp && base instanceof Multiply multiply) {
if (multiply.multiplier instanceof NumberLiteral literal) if (multiply.multiplier instanceof NumberLiteral literal)
return new Multiply(multiply.chStart, multiply.chEnd, return new Multiply(multiply.location,
Expr.literal(chStart, chEnd, Math.pow(literal.value, exp.value)), literal(location, Math.pow(literal.value, exp.value)),
new Power(chStart, chEnd, multiply.multiplicand, exp)); new Power(location, multiply.multiplicand, exp));
if (multiply.multiplicand instanceof NumberLiteral literal) if (multiply.multiplicand instanceof NumberLiteral literal)
return new Multiply(multiply.chStart, multiply.chEnd, return new Multiply(multiply.location,
Expr.literal(chStart, chEnd, Math.pow(literal.value, exp.value)), literal(location, Math.pow(literal.value, exp.value)),
new Power(chStart, chEnd, multiply.multiplier, exp)); new Power(location, multiply.multiplier, exp));
} }
return new Power(chStart, chEnd, base, exponent); return new Power(location, base, exponent);
} }
@Override @Override

View File

@ -1,7 +1,6 @@
package io.gitlab.jfronny.muscript.ast.string; package io.gitlab.jfronny.muscript.ast.string;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.Expr; import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.StringExpr; import io.gitlab.jfronny.muscript.ast.StringExpr;
@ -13,8 +12,8 @@ public class Concatenate extends StringExpr {
public final StringExpr left; public final StringExpr left;
public final StringExpr right; public final StringExpr right;
public Concatenate(int chStart, int chEnd, StringExpr left, StringExpr right) { public Concatenate(CodeLocation location, StringExpr left, StringExpr right) {
super(Order.Concat, chStart, chEnd); super(Order.Concat, location);
this.left = left; this.left = left;
this.right = right; this.right = right;
} }
@ -29,14 +28,14 @@ public class Concatenate extends StringExpr {
StringExpr left = this.left.optimize(); StringExpr left = this.left.optimize();
StringExpr right = this.right.optimize(); StringExpr right = this.right.optimize();
if (left instanceof StringLiteral litL && right instanceof StringLiteral litR) if (left instanceof StringLiteral litL && right instanceof StringLiteral litR)
return Expr.literal(chStart, chEnd, litL.value + litR.value); return literal(location, litL.value + litR.value);
if (right instanceof StringLiteral litR && left instanceof Concatenate concatenate && concatenate.right instanceof StringLiteral litL) { if (right instanceof StringLiteral litR && left instanceof Concatenate concatenate && concatenate.right instanceof StringLiteral litL) {
return new Concatenate(chStart, chEnd, concatenate.left, Expr.literal(concatenate.chStart, concatenate.chEnd, litL.value + litR.value)); return new Concatenate(location, concatenate.left, literal(concatenate.location, litL.value + litR.value));
} }
if (left instanceof StringLiteral litL && right instanceof Concatenate concatenate && concatenate.left instanceof StringLiteral litR) { if (left instanceof StringLiteral litL && right instanceof Concatenate concatenate && concatenate.left instanceof StringLiteral litR) {
return new Concatenate(chStart, chEnd, Expr.literal(concatenate.chStart, concatenate.chEnd, litL.value + litR.value), concatenate.right); return new Concatenate(location, literal(concatenate.location, litL.value + litR.value), concatenate.right);
} }
return new Concatenate(chStart, chEnd, left, right); return new Concatenate(location, left, right);
} }
@Override @Override

View File

@ -1,6 +1,7 @@
package io.gitlab.jfronny.muscript.ast.string; package io.gitlab.jfronny.muscript.ast.string;
import io.gitlab.jfronny.commons.StringFormatter; import io.gitlab.jfronny.commons.StringFormatter;
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
import io.gitlab.jfronny.muscript.compiler.ExprWriter; import io.gitlab.jfronny.muscript.compiler.ExprWriter;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.*; import io.gitlab.jfronny.muscript.ast.*;
@ -11,8 +12,8 @@ import java.io.IOException;
public class StringCoerce extends StringExpr { public class StringCoerce extends StringExpr {
private final Expr<?> inner; private final Expr<?> inner;
public StringCoerce(int chStart, int chEnd, Expr<?> inner) { public StringCoerce(CodeLocation location, Expr<?> inner) {
super(inner.order, chStart, chEnd); super(inner.order, location);
this.inner = inner; this.inner = inner;
} }
@ -24,12 +25,12 @@ public class StringCoerce extends StringExpr {
@Override @Override
public StringExpr optimize() { public StringExpr optimize() {
Expr<?> inner = this.inner.optimize(); Expr<?> inner = this.inner.optimize();
if (inner instanceof NullLiteral) return Expr.literal(chStart, chEnd, "null"); if (inner instanceof NullLiteral) return literal(location, "null");
if (inner instanceof BoolLiteral expr) return Expr.literal(chStart, chEnd, StringFormatter.toString(expr.value)); if (inner instanceof BoolLiteral expr) return literal(location, StringFormatter.toString(expr.value));
if (inner instanceof DynamicLiteral<?> expr) return Expr.literal(chStart, chEnd, StringFormatter.toString(expr.value)); if (inner instanceof DynamicLiteral<?> expr) return literal(location, StringFormatter.toString(expr.value));
if (inner instanceof NumberLiteral expr) return Expr.literal(chStart, chEnd, StringFormatter.toString(expr.value)); if (inner instanceof NumberLiteral expr) return literal(location, StringFormatter.toString(expr.value));
if (inner instanceof StringExpr expr) return expr; if (inner instanceof StringExpr expr) return expr;
return new StringCoerce(chStart, chEnd, inner); return new StringCoerce(location, inner);
} }
@Override @Override

View File

@ -0,0 +1,23 @@
package io.gitlab.jfronny.muscript.compiler;
import org.jetbrains.annotations.Nullable;
public record CodeLocation(int chStart, int chEnd, @Nullable String source, @Nullable String file) {
public static final CodeLocation NONE = new CodeLocation(-1, -1);
public CodeLocation(int chStart, int chEnd) {
this(chStart, chEnd, null, null);
}
public CodeLocation(int ch) {
this(ch, ch);
}
public CodeLocation withSource(String source) {
return new CodeLocation(chStart, chEnd, source, file);
}
public CodeLocation withFile(String file) {
return new CodeLocation(chStart, chEnd, source, file);
}
}

View File

@ -1,10 +1,13 @@
package io.gitlab.jfronny.muscript.compiler; package io.gitlab.jfronny.muscript.compiler;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.regex.Pattern; import java.util.regex.Pattern;
// Heavily inspired by starscript // Heavily inspired by starscript
public class Lexer { public class Lexer {
public final String file;
/** /**
* The type of the token * The type of the token
*/ */
@ -20,7 +23,12 @@ public class Lexer {
public int start, current; public int start, current;
public Lexer(String source) { public Lexer(String source) {
this.source = source; this(source, null);
}
public Lexer(String source, String file) {
this.source = Objects.requireNonNull(source);
this.file = file;
} }
/** /**

View File

@ -16,6 +16,7 @@ import io.gitlab.jfronny.muscript.error.*;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
public class Parser { public class Parser {
private final Lexer lexer; private final Lexer lexer;
@ -24,11 +25,56 @@ public class Parser {
private final TokenData current = new TokenData(); private final TokenData current = new TokenData();
public static Expr<?> parse(String source) { public static Expr<?> parse(String source) {
return new Parser(new Lexer(source)).parse().optimize(); return parse(source, null);
}
public static Expr<?> parse(String source, String file) {
return new Parser(new Lexer(source, file)).parse().optimize();
} }
public static Script parseScript(String source) { public static Script parseScript(String source) {
return new Parser(new Lexer(source)).parseScript().optimize(); return parseScript(source, null);
}
public static Script parseScript(String source, String file) {
return new Parser(new Lexer(source, file)).parseScript().optimize();
}
public static Script parseMultiScript(String startFile, SourceFS filesystem) {
return new Script(parseMultiScript(startFile, filesystem, new HashSet<>()).stream().flatMap(Script::stream).toList());
}
private static List<Script> parseMultiScript(String startFile, SourceFS filesystem, Set<String> alreadyIncluded) {
alreadyIncluded.add(startFile);
boolean isIncludes = true;
StringBuilder src = new StringBuilder();
List<Script> includes = new LinkedList<>();
int row = 0;
final String includePrefix = "#include ";
for (String s : filesystem.read(startFile).split("\n")) {
row++;
if (s.isBlank()) {
src.append("\n");
} else if (s.startsWith(includePrefix)) {
if (isIncludes) {
String file = s.substring(includePrefix.length());
src.append("// include ").append(file).append("\n");
if (!alreadyIncluded.contains(file)) {
includes.addAll(parseMultiScript(file, filesystem, alreadyIncluded));
}
} else {
throw new ParseException(LocationalError.builder()
.setLocation(new LocationalError.Location(s, 0, row), new LocationalError.Location(s, s.length() - 1, row))
.setMessage("Includes MUST be located at the top of the file")
.build());
}
} else {
isIncludes = false;
src.append(s).append("\n");
}
}
includes.add(parseScript(src.toString(), startFile));
return includes;
} }
public Parser(Lexer lexer) { public Parser(Lexer lexer) {
@ -73,7 +119,7 @@ public class Parser {
} catch (RuntimeException e) { } catch (RuntimeException e) {
if (e instanceof ParseException) throw e; if (e instanceof ParseException) throw e;
else if (e instanceof LocationalException le) { else if (e instanceof LocationalException le) {
throw new ParseException(le.asPrintable(lexer.source), le.getCause()); throw new ParseException(le.asPrintable(), le.getCause());
} else throw error(e.getMessage()); } else throw error(e.getMessage());
} }
} }
@ -82,12 +128,11 @@ public class Parser {
Expr<?> expr = and(); Expr<?> expr = and();
if (match(Token.QuestionMark)) { if (match(Token.QuestionMark)) {
int start = previous.start; CodeLocation location = previous.location();
int end = previous.current - 1;
Expr<?> trueExpr = expression(); Expr<?> trueExpr = expression();
consume(Token.Colon, "Expected ':' after first part of condition"); consume(Token.Colon, "Expected ':' after first part of condition");
Expr<?> falseExpr = expression(); Expr<?> falseExpr = expression();
expr = new UnresolvedConditional(start, end, asBool(expr), trueExpr, falseExpr); expr = new UnresolvedConditional(location, asBool(expr), trueExpr, falseExpr);
} }
return expr; return expr;
@ -97,10 +142,9 @@ public class Parser {
Expr<?> expr = or(); Expr<?> expr = or();
while (match(Token.And)) { while (match(Token.And)) {
int start = previous.start; CodeLocation location = previous.location();
int end = previous.current - 1;
Expr<?> right = or(); Expr<?> right = or();
expr = new And(start, end, asBool(expr), asBool(right)); expr = new And(location, asBool(expr), asBool(right));
} }
return expr; return expr;
@ -110,10 +154,9 @@ public class Parser {
Expr<?> expr = equality(); Expr<?> expr = equality();
while (match(Token.Or)) { while (match(Token.Or)) {
int start = previous.start; CodeLocation location = previous.location();
int end = previous.current - 1;
Expr<?> right = equality(); Expr<?> right = equality();
expr = new Or(start, end, asBool(expr), asBool(right)); expr = new Or(location, asBool(expr), asBool(right));
} }
return expr; return expr;
@ -124,11 +167,10 @@ public class Parser {
while (match(Token.EqualEqual, Token.BangEqual)) { while (match(Token.EqualEqual, Token.BangEqual)) {
Token op = previous.token; Token op = previous.token;
int start = previous.start; CodeLocation location = previous.location();
int end = previous.current - 1;
Expr<?> right = concat(); Expr<?> right = concat();
BoolExpr e = new Equal(start, end, expr, right); BoolExpr e = new Equal(location, expr, right);
if (op == Token.BangEqual) e = new Not(start, end, e); if (op == Token.BangEqual) e = new Not(location, e);
expr = e; expr = e;
} }
@ -139,10 +181,9 @@ public class Parser {
Expr<?> expr = comparison(); Expr<?> expr = comparison();
while (match(Token.Concat)) { while (match(Token.Concat)) {
int start = previous.start; CodeLocation location = previous.location();
int end = previous.current - 1;
Expr<?> right = comparison(); Expr<?> right = comparison();
expr = new Concatenate(start, end, asString(expr), asString(right)); expr = new Concatenate(location, asString(expr), asString(right));
} }
return expr; return expr;
@ -153,14 +194,13 @@ public class Parser {
while (match(Token.Greater, Token.GreaterEqual, Token.Less, Token.LessEqual)) { while (match(Token.Greater, Token.GreaterEqual, Token.Less, Token.LessEqual)) {
Token op = previous.token; Token op = previous.token;
int start = previous.start; CodeLocation location = previous.location();
int end = previous.current - 1;
NumberExpr right = asNumber(term()); NumberExpr right = asNumber(term());
expr = switch (op) { expr = switch (op) {
case Greater -> new Greater(start, end, asNumber(expr), right); case Greater -> new Greater(location, asNumber(expr), right);
case GreaterEqual -> new Not(start, end, new Greater(start, end, right, asNumber(expr))); case GreaterEqual -> new Not(location, new Greater(location, right, asNumber(expr)));
case Less -> new Greater(start, end, right, asNumber(expr)); case Less -> new Greater(location, right, asNumber(expr));
case LessEqual -> new Not(start, end, new Greater(start, end, asNumber(expr), right)); case LessEqual -> new Not(location, new Greater(location, asNumber(expr), right));
default -> throw new IllegalStateException(); default -> throw new IllegalStateException();
}; };
} }
@ -173,12 +213,11 @@ public class Parser {
while (match(Token.Plus, Token.Minus)) { while (match(Token.Plus, Token.Minus)) {
Token op = previous.token; Token op = previous.token;
int start = previous.start; CodeLocation location = previous.location();
int end = previous.current - 1;
NumberExpr right = asNumber(factor()); NumberExpr right = asNumber(factor());
expr = switch (op) { expr = switch (op) {
case Plus -> new Plus(start, end, asNumber(expr), right); case Plus -> new Plus(location, asNumber(expr), right);
case Minus -> new Minus(start, end, asNumber(expr), right); case Minus -> new Minus(location, asNumber(expr), right);
default -> throw new IllegalStateException(); default -> throw new IllegalStateException();
}; };
} }
@ -191,13 +230,12 @@ public class Parser {
while (match(Token.Star, Token.Slash, Token.Percentage)) { while (match(Token.Star, Token.Slash, Token.Percentage)) {
Token op = previous.token; Token op = previous.token;
int start = previous.start; CodeLocation location = previous.location();
int end = previous.current - 1;
NumberExpr right = asNumber(exp()); NumberExpr right = asNumber(exp());
expr = switch (op) { expr = switch (op) {
case Star -> new Multiply(start, end, asNumber(expr), right); case Star -> new Multiply(location, asNumber(expr), right);
case Slash -> new Divide(start, end, asNumber(expr), right); case Slash -> new Divide(location, asNumber(expr), right);
case Percentage -> new Modulo(start, end, asNumber(expr), right); case Percentage -> new Modulo(location, asNumber(expr), right);
default -> throw new IllegalStateException(); default -> throw new IllegalStateException();
}; };
} }
@ -209,10 +247,9 @@ public class Parser {
Expr<?> expr = unary(); Expr<?> expr = unary();
while (match(Token.UpArrow)) { while (match(Token.UpArrow)) {
int start = previous.start; CodeLocation location = previous.location();
int end = previous.current - 1;
NumberExpr right = asNumber(unary()); NumberExpr right = asNumber(unary());
expr = new Power(start, end, asNumber(expr), right); expr = new Power(location, asNumber(expr), right);
} }
return expr; return expr;
@ -221,12 +258,11 @@ public class Parser {
private Expr<?> unary() { private Expr<?> unary() {
if (match(Token.Bang, Token.Minus)) { if (match(Token.Bang, Token.Minus)) {
Token op = previous.token; Token op = previous.token;
int start = previous.start; CodeLocation location = previous.location();
int end = previous.current - 1;
Expr<?> right = unary(); Expr<?> right = unary();
return switch (op) { return switch (op) {
case Bang -> new Not(start, end, asBool(right)); case Bang -> new Not(location, asBool(right));
case Minus -> new Invert(start, end, asNumber(right)); case Minus -> new Invert(location, asNumber(right));
default -> throw new IllegalStateException(); default -> throw new IllegalStateException();
}; };
} }
@ -238,26 +274,25 @@ public class Parser {
Expr<?> expr = primary(); Expr<?> expr = primary();
while (match(Token.LeftParen, Token.Dot, Token.LeftBracket, Token.DoubleColon)) { while (match(Token.LeftParen, Token.Dot, Token.LeftBracket, Token.DoubleColon)) {
int start = previous.start; CodeLocation location = previous.location();
int end = previous.current - 1;
expr = switch (previous.token) { expr = switch (previous.token) {
case LeftParen -> finishCall(start, end, expr); case LeftParen -> finishCall(location, expr);
case Dot -> { case Dot -> {
TokenData name = consume(Token.Identifier, "Expected field name after '.'"); TokenData name = consume(Token.Identifier, "Expected field name after '.'");
yield new Get(start, end, asDynamic(expr), Expr.literal(name.start, name.current - 1, name.lexeme)); yield new Get(location, asDynamic(expr), Expr.literal(name.location(), name.lexeme));
} }
case DoubleColon -> { case DoubleColon -> {
DynamicExpr callable; DynamicExpr callable;
if (match(Token.Identifier)) { if (match(Token.Identifier)) {
callable = new Variable(previous.start, previous.current - 1, previous.lexeme); callable = new Variable(previous.location(), previous.lexeme);
} else if (match(Token.LeftParen)) { } else if (match(Token.LeftParen)) {
callable = expression().asDynamicExpr(); callable = expression().asDynamicExpr();
consume(Token.RightParen, "Expected ')' after expression"); consume(Token.RightParen, "Expected ')' after expression");
} else throw error("Bind operator requires right side to be a literal identifier or to be wrapped in parentheses."); } else throw error("Bind operator requires right side to be a literal identifier or to be wrapped in parentheses.");
yield new Bind(start, end, callable, expr.asDynamicExpr()); yield new Bind(location, callable, expr.asDynamicExpr());
} }
case LeftBracket -> { case LeftBracket -> {
expr = new Get(start, end, asDynamic(expr), expression()); expr = new Get(location, asDynamic(expr), expression());
consume(Token.RightBracket, "Expected closing bracket"); consume(Token.RightBracket, "Expected closing bracket");
yield expr; yield expr;
} }
@ -268,7 +303,7 @@ public class Parser {
return expr; return expr;
} }
private Expr<?> finishCall(int start, int end, Expr<?> callee) { private Expr<?> finishCall(CodeLocation location, Expr<?> callee) {
List<Call.Arg> args = new ArrayList<>(2); List<Call.Arg> args = new ArrayList<>(2);
if (!check(Token.RightParen)) { if (!check(Token.RightParen)) {
@ -278,20 +313,19 @@ public class Parser {
} }
consume(Token.RightParen, "Expected ')' after function arguments"); consume(Token.RightParen, "Expected ')' after function arguments");
return new Call(start, end, asDynamic(callee), args); return new Call(location, asDynamic(callee), args);
} }
private Expr<?> primary() { private Expr<?> primary() {
if (match(Token.Null)) return Expr.literalNull(previous.start, previous.current - 1); if (match(Token.Null)) return Expr.literalNull(previous.location());
if (match(Token.String)) return Expr.literal(previous.start, previous.current - 1, previous.lexeme); if (match(Token.String)) return Expr.literal(previous.location(), previous.lexeme);
if (match(Token.True, Token.False)) return Expr.literal(previous.start, previous.current - 1, previous.lexeme.equals("true")); if (match(Token.True, Token.False)) return Expr.literal(previous.location(), previous.lexeme.equals("true"));
if (match(Token.Number)) return Expr.literal(previous.start, previous.current - 1, Double.parseDouble(previous.lexeme)); if (match(Token.Number)) return Expr.literal(previous.location(), Double.parseDouble(previous.lexeme));
if (match(Token.Identifier)) { if (match(Token.Identifier)) {
int start = previous.start; CodeLocation location = previous.location();
int end = previous.current - 1;
String name = previous.lexeme; String name = previous.lexeme;
if (match(Token.Assign)) return new DynamicAssign(start, end, name, expression().asDynamicExpr()); if (match(Token.Assign)) return new DynamicAssign(location, name, expression().asDynamicExpr());
else return new Variable(start, end, name); else return new Variable(location, name);
} }
if (match(Token.LeftParen)) { if (match(Token.LeftParen)) {
@ -303,7 +337,7 @@ public class Parser {
if (match(Token.LeftBrace)) { if (match(Token.LeftBrace)) {
int start = previous.start; int start = previous.start;
if (match(Token.Arrow)) return finishClosure(start, null, false); if (match(Token.Arrow)) return finishClosure(start, null, false);
if (match(Token.RightBrace)) return new DynamicLiteral<>(start, previous.start, DFinal.of(Map.of())); if (match(Token.RightBrace)) return new DynamicLiteral<>(location(start, previous.start), DFinal.of(Map.of()));
consume(Token.Identifier, "Expected arrow or identifier as first element in closure or object"); consume(Token.Identifier, "Expected arrow or identifier as first element in closure or object");
String first = previous.lexeme; String first = previous.lexeme;
if (check(Token.Arrow)) return finishClosure(start, first, false); if (check(Token.Arrow)) return finishClosure(start, first, false);
@ -345,7 +379,7 @@ public class Parser {
match(Token.Semicolon); // Consume semicolon if present match(Token.Semicolon); // Consume semicolon if present
} }
int end = previous.start; int end = previous.start;
return new Closure(start, end, boundArgs, expressions, variadic); return new Closure(location(start, end), boundArgs, expressions, variadic);
} }
private Expr<?> finishObject(int start, @Nullable String firstArg, @Nullable DynamicExpr firstValue) { private Expr<?> finishObject(int start, @Nullable String firstArg, @Nullable DynamicExpr firstValue) {
@ -358,7 +392,7 @@ public class Parser {
content.put(name, expression().asDynamicExpr()); content.put(name, expression().asDynamicExpr());
} }
consume(Token.RightBrace, "Expected end of object"); consume(Token.RightBrace, "Expected end of object");
return new ObjectLiteral(start, previous.start, content); return new ObjectLiteral(location(start, previous.start), content);
} }
// Type conversion // Type conversion
@ -395,12 +429,16 @@ public class Parser {
} }
// Helpers // Helpers
private CodeLocation location(int chStart, int chEnd) {
return new CodeLocation(chStart, chEnd, lexer.source, lexer.file);
}
private ParseException error(String message) { private ParseException error(String message) {
return new ParseException(LocationalError.create(lexer.source, current.current - 1, message)); return new ParseException(LocationalError.create(lexer.source, current.current - 1, message));
} }
private ParseException error(String message, Expr<?> expr) { private ParseException error(String message, Expr<?> expr) {
return new ParseException(LocationalError.create(lexer.source, expr.chStart, expr.chEnd, message)); return new ParseException(LocationalError.create(expr.location, message));
} }
private TokenData consume(Token token, String message) { private TokenData consume(Token token, String message) {
@ -442,7 +480,7 @@ public class Parser {
} }
// Token data // Token data
private static class TokenData { private class TokenData {
public Token token; public Token token;
public String lexeme; public String lexeme;
public int start, current; public int start, current;
@ -464,6 +502,10 @@ public class Parser {
public String toString() { public String toString() {
return String.format("%s '%s'", token, lexeme); return String.format("%s '%s'", token, lexeme);
} }
public CodeLocation location() {
return new CodeLocation(start, current - 1, lexer.source, lexer.file);
}
} }
// Parse Exception // Parse Exception

View File

@ -0,0 +1,5 @@
package io.gitlab.jfronny.muscript.compiler;
public interface SourceFS {
String read(String file);
}

View File

@ -15,15 +15,21 @@ import java.util.stream.Stream;
public class Script extends Decompilable { public class Script extends Decompilable {
private final List<Expr<?>> steps; private final List<Expr<?>> steps;
private final DynamicExpr fin; private final DynamicExpr fin;
private final String file;
public Script(List<Expr<?>> expressions) { public Script(List<Expr<?>> expressions) {
this(expressions.subList(0, expressions.size() - 1), expressions.get(expressions.size() - 1).asDynamicExpr()); this(expressions, null);
} }
private Script(List<Expr<?>> steps, DynamicExpr fin) { public Script(List<Expr<?>> expressions, String file) {
this(expressions.subList(0, expressions.size() - 1), expressions.get(expressions.size() - 1).asDynamicExpr(), file);
}
private Script(List<Expr<?>> steps, DynamicExpr fin, String file) {
super(Order.Script); super(Order.Script);
this.steps = steps; this.steps = steps;
this.fin = fin; this.fin = fin;
this.file = file;
} }
public Dynamic<?> run(DObject scope) { public Dynamic<?> run(DObject scope) {
@ -45,11 +51,12 @@ public class Script extends Decompilable {
} }
public DynamicExpr asExpr() { public DynamicExpr asExpr() {
return new Call(-1, -1, new Closure(-1, -1, List.of(), stream().toList(), false), List.of()); CodeLocation location = CodeLocation.NONE.withFile(file);
return new Call(location, new Closure(location, List.of(), stream().toList(), false), List.of());
} }
public Script optimize() { public Script optimize() {
return new Script(steps.stream().<Expr<?>>map(Expr::optimize).toList(), fin.optimize()); return new Script(steps.stream().<Expr<?>>map(Expr::optimize).toList(), fin.optimize(), file);
} }
public Script concat(Script other) { public Script concat(Script other) {

View File

@ -1,5 +1,6 @@
package io.gitlab.jfronny.muscript.data.dynamic; package io.gitlab.jfronny.muscript.data.dynamic;
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
import io.gitlab.jfronny.muscript.error.LocationalException; import io.gitlab.jfronny.muscript.error.LocationalException;
public class DynamicTypeConversionException extends RuntimeException { public class DynamicTypeConversionException extends RuntimeException {
@ -14,7 +15,7 @@ public class DynamicTypeConversionException extends RuntimeException {
this.actual = dynamic.getClass().getSimpleName(); this.actual = dynamic.getClass().getSimpleName();
} }
public LocationalException locational(int start, int end) { public LocationalException locational(CodeLocation location) {
return new LocationalException(start, end, MESSAGE1 + actual + MESSAGE2 + target, this); return new LocationalException(location, MESSAGE1 + actual + MESSAGE2 + target, this);
} }
} }

View File

@ -1,9 +1,12 @@
package io.gitlab.jfronny.muscript.error; package io.gitlab.jfronny.muscript.error;
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.*; import java.util.*;
//TODO maybe this can be eliminated since the source is now included?
/** /**
* Class for storing errors with code context * Class for storing errors with code context
* Can be generated from a LocationalException with asPrintable * Can be generated from a LocationalException with asPrintable
@ -76,6 +79,17 @@ public record LocationalError(String message, Location start, @Nullable Location
return builder().setSource(source).setLocation(start, end).setMessage(message).build(); return builder().setSource(source).setLocation(start, end).setMessage(message).build();
} }
/**
* Generates an error by getting the relevant data from what is available while lexing/parsing
*
* @param location The location of the piece of code causing this
* @param message The error message
* @return An error using the provided information
*/
public static LocationalError create(CodeLocation location, String message) {
return builder().setSource(location.source()).setLocation(location.chStart(), location.chEnd()).setMessage(message).build();
}
/** /**
* Converts this error to a human-readable error message * Converts this error to a human-readable error message
* *
@ -120,9 +134,7 @@ public record LocationalError(String message, Location start, @Nullable Location
} }
public Builder setLocation(int chStart) { public Builder setLocation(int chStart) {
start = Location.create(Objects.requireNonNull(source, "source is required to set location"), chStart); return setLocation(Location.create(Objects.requireNonNull(source, "source is required to set location"), chStart), null);
end = null;
return this;
} }
public Builder setLocation(int chStart, int chEnd) { public Builder setLocation(int chStart, int chEnd) {
@ -133,8 +145,12 @@ public record LocationalError(String message, Location start, @Nullable Location
chEnd = chStart; chEnd = chStart;
chStart = a; chStart = a;
} }
start = Location.create(source, chStart); return setLocation(Location.create(source, chStart), Location.create(source, chEnd));
end = Location.create(source, chEnd); }
public Builder setLocation(Location start, Location end) {
this.start = start;
this.end = end;
return this; return this;
} }

View File

@ -1,5 +1,7 @@
package io.gitlab.jfronny.muscript.error; package io.gitlab.jfronny.muscript.error;
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -8,37 +10,33 @@ import java.util.List;
* For use in MuScript, can be converted to a pretty LocationalError with asPrintable * For use in MuScript, can be converted to a pretty LocationalError with asPrintable
*/ */
public class LocationalException extends RuntimeException { public class LocationalException extends RuntimeException {
private final int start, end; private final CodeLocation location;
private final List<StackFrame> callStack = new LinkedList<>(); private final List<StackFrame> callStack = new LinkedList<>();
public LocationalException(int start, int end) { public LocationalException(CodeLocation location) {
super(); super();
this.start = start; this.location = location;
this.end = end;
} }
public LocationalException(int start, int end, String message) { public LocationalException(CodeLocation location, String message) {
super(message); super(message);
this.start = start; this.location = location;
this.end = end;
} }
public LocationalException(int start, int end, String message, Throwable cause) { public LocationalException(CodeLocation location, String message, Throwable cause) {
super(message, cause); super(message, cause);
this.start = start; this.location = location;
this.end = end;
} }
public LocationalException(int start, int end, Throwable cause) { public LocationalException(CodeLocation location, Throwable cause) {
super(cause); super(cause);
this.start = start; this.location = location;
this.end = end;
} }
public LocationalError asPrintable(String source) { public LocationalError asPrintable() {
return LocationalError.builder() return LocationalError.builder()
.setSource(source) .setSource(location.source())
.setLocation(start, end) .setLocation(location.chStart(), location.chEnd())
.setMessage(getLocalizedMessage()) .setMessage(getLocalizedMessage())
.setCallStack(callStack) .setCallStack(callStack)
.build(); .build();

View File

@ -1,23 +1,23 @@
package io.gitlab.jfronny.muscript.error; package io.gitlab.jfronny.muscript.error;
public sealed interface StackFrame { public sealed interface StackFrame {
record Raw(String name, int chStart) implements StackFrame { record Raw(String file, String name, int chStart) implements StackFrame {
@Override @Override
public String toString() { public String toString() {
return name + " (call: character " + chStart + ")"; return name + " (call: character " + chStart + (file == null ? ")" : " in " + file + ")");
} }
public Lined lined(String source) { public Lined lined(String source) {
int lineStart = source.lastIndexOf('\n', chStart); int lineStart = source.lastIndexOf('\n', chStart);
int lineIndex = lineStart > 0 ? (int) source.substring(0, lineStart).chars().filter(c -> c == '\n').count() : 0; int lineIndex = lineStart > 0 ? (int) source.substring(0, lineStart).chars().filter(c -> c == '\n').count() : 0;
return new Lined(name, lineIndex + 1); return new Lined(file, name, lineIndex + 1);
} }
} }
record Lined(String name, int row) implements StackFrame { record Lined(String file, String name, int row) implements StackFrame {
@Override @Override
public String toString() { public String toString() {
return name + " (call: line " + row + ")"; return name + " (call: line " + row + (file == null ? ")" : " in " + file + ")");
} }
} }
} }

View File

@ -1,5 +1,6 @@
package io.gitlab.jfronny.muscript.error; package io.gitlab.jfronny.muscript.error;
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
import io.gitlab.jfronny.muscript.compiler.Type; import io.gitlab.jfronny.muscript.compiler.Type;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -11,12 +12,12 @@ public class TypeMismatchException extends LocationalException {
private final Type expected; private final Type expected;
private final @Nullable Type actual; private final @Nullable Type actual;
public TypeMismatchException(int start, int end, Type expected, @Nullable Type actual) { public TypeMismatchException(CodeLocation location, Type expected, @Nullable Type actual) {
this(start, end, expected, actual, "Expected " + expected + (actual == null ? "" : " but got " + actual)); this(location, expected, actual, "Expected " + expected + (actual == null ? "" : " but got " + actual));
} }
public TypeMismatchException(int start, int end, Type expected, @Nullable Type actual, String message) { public TypeMismatchException(CodeLocation location, Type expected, @Nullable Type actual, String message) {
super(start, end, message); super(location, message);
this.expected = expected; this.expected = expected;
this.actual = actual; this.actual = actual;
} }

View File

@ -3,6 +3,7 @@ package io.gitlab.jfronny.muscript.test;
import io.gitlab.jfronny.muscript.ast.StringExpr; import io.gitlab.jfronny.muscript.ast.StringExpr;
import io.gitlab.jfronny.muscript.ast.dynamic.assign.DynamicAssign; import io.gitlab.jfronny.muscript.ast.dynamic.assign.DynamicAssign;
import io.gitlab.jfronny.muscript.ast.literal.StringLiteral; import io.gitlab.jfronny.muscript.ast.literal.StringLiteral;
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
import io.gitlab.jfronny.muscript.compiler.Parser; import io.gitlab.jfronny.muscript.compiler.Parser;
import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.test.util.UnforkableScope; import io.gitlab.jfronny.muscript.test.util.UnforkableScope;
@ -15,7 +16,7 @@ class AssignTest {
@Test @Test
void testAssignSimple() { void testAssignSimple() {
StringExpr expr = Parser.parse("someval = 'test'").asStringExpr(); StringExpr expr = Parser.parse("someval = 'test'").asStringExpr();
assertEquals(new DynamicAssign(0, 6, "someval", new StringLiteral(10, 15, "test").asDynamicExpr()).asStringExpr(), expr); assertEquals(new DynamicAssign(new CodeLocation(0, 6), "someval", new StringLiteral(new CodeLocation(10, 15), "test").asDynamicExpr()).asStringExpr(), expr);
assertEquals("someval = 'test'", expr.toString()); assertEquals("someval = 'test'", expr.toString());
Scope scope = new UnforkableScope(); Scope scope = new UnforkableScope();
assertEquals("test", expr.get(scope)); assertEquals("test", expr.get(scope));

View File

@ -2,6 +2,7 @@ package io.gitlab.jfronny.muscript.test;
import io.gitlab.jfronny.muscript.StandardLib; import io.gitlab.jfronny.muscript.StandardLib;
import io.gitlab.jfronny.muscript.compiler.Parser; import io.gitlab.jfronny.muscript.compiler.Parser;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.error.LocationalException; import io.gitlab.jfronny.muscript.error.LocationalException;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -9,9 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
class StackTraceTest { class StackTraceTest {
@Test final String source = """
void stackTrace() {
String source = """
someInner = { -> someInner = { ->
throw() throw()
} }
@ -23,18 +22,32 @@ class StackTraceTest {
someOuter() someOuter()
"""; """;
final Scope scope = StandardLib.createScope()
.set("throw", args -> {
throw new IllegalArgumentException("No");
});
@Test
void stackTrace() {
assertEquals(""" assertEquals("""
Error at '(' (character 8): Could not perform call successfully Error at '(' (character 8): Could not perform call successfully
1 | throw() 1 | throw()
^-- Here ^-- Here
at someInner (call: line 5) at someInner (call: line 5)
at someOuter (call: line 8)""", at someOuter (call: line 8)""",
assertThrows(LocationalException.class, () -> Parser.parseScript(source) assertThrows(LocationalException.class, () -> Parser.parseScript(source).run(scope))
.run(StandardLib.createScope() .asPrintable().toString());
.set("throw", args -> { }
throw new IllegalArgumentException("No");
}) @Test
)) void stackTrace2() {
.asPrintable(source).toString()); assertEquals("""
Error at '(' (character 8): Could not perform call successfully
1 | throw()
^-- Here
at someInner (call: line 5 in some/file.mu)
at someOuter (call: line 8 in some/file.mu)""",
assertThrows(LocationalException.class, () -> Parser.parseScript(source, "some/file.mu").run(scope))
.asPrintable().toString());
} }
} }

View File

@ -56,7 +56,7 @@ class ValidExampleTest {
} catch (Throwable t) { } catch (Throwable t) {
assertEquals(expectedResult, StringFormatter.toString(t, e -> assertEquals(expectedResult, StringFormatter.toString(t, e ->
e instanceof LocationalException le e instanceof LocationalException le
? le.asPrintable(source).toString() ? le.asPrintable().toString()
: e.toString() : e.toString()
)); ));
} }