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

View File

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

View File

@ -1,12 +1,11 @@
package io.gitlab.jfronny.muscript.ast;
import io.gitlab.jfronny.muscript.ast.dynamic.DynamicCoerce;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.compiler.Type;
import io.gitlab.jfronny.muscript.compiler.*;
public abstract non-sealed class NumberExpr extends Expr<Double> {
protected NumberExpr(Order order, int chStart, int chEnd) {
super(order, chStart, chEnd);
protected NumberExpr(Order order, CodeLocation location) {
super(order, location);
}
@Override
@ -19,6 +18,6 @@ public abstract non-sealed class NumberExpr extends Expr<Double> {
@Override
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;
import io.gitlab.jfronny.muscript.ast.dynamic.DynamicCoerce;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.compiler.Type;
import io.gitlab.jfronny.muscript.compiler.*;
public abstract non-sealed class StringExpr extends Expr<String> {
protected StringExpr(Order order, int chStart, int chEnd) {
super(order, chStart, chEnd);
protected StringExpr(Order order, CodeLocation location) {
super(order, location);
}
@Override
@ -19,6 +18,6 @@ public abstract non-sealed class StringExpr extends Expr<String> {
@Override
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;
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.BoolExpr;
import io.gitlab.jfronny.muscript.ast.Expr;
@ -13,8 +12,8 @@ public class And extends BoolExpr {
private final BoolExpr left;
private final BoolExpr right;
public And(int chStart, int chEnd, BoolExpr left, BoolExpr right) {
super(Order.And, chStart, chEnd);
public And(CodeLocation location, BoolExpr left, BoolExpr right) {
super(Order.And, location);
this.left = left;
this.right = right;
}
@ -28,9 +27,9 @@ public class And extends BoolExpr {
public BoolExpr optimize() {
BoolExpr left = this.left.optimize();
BoolExpr right = this.right.optimize();
if (left instanceof BoolLiteral literal) return literal.value ? right : Expr.literal(chStart, chEnd, false);
if (right instanceof BoolLiteral literal) return literal.value ? left : Expr.literal(chStart, chEnd, false);
return new And(chStart, chEnd, left, right);
if (left instanceof BoolLiteral literal) return literal.value ? right : literal(location, false);
if (right instanceof BoolLiteral literal) return literal.value ? left : literal(location, false);
return new And(location, left, right);
}
@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.Greater;
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.BoolExpr;
import io.gitlab.jfronny.muscript.ast.Expr;
@ -15,8 +14,8 @@ import java.io.IOException;
public class Not extends BoolExpr {
public final BoolExpr inner;
public Not(int chStart, int chEnd, BoolExpr inner) {
super(Order.Unary, chStart, chEnd);
public Not(CodeLocation location, BoolExpr inner) {
super(Order.Unary, location);
this.inner = inner;
}
@ -29,8 +28,8 @@ public class Not extends BoolExpr {
public BoolExpr optimize() {
BoolExpr inner = this.inner.optimize();
if (inner instanceof Not not) return not.inner;
if (inner instanceof BoolLiteral literal) return Expr.literal(chStart, chEnd, !literal.value);
return new Not(chStart, chEnd, inner);
if (inner instanceof BoolLiteral literal) return literal(location, !literal.value);
return new Not(location, inner);
}
@Override

View File

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

View File

@ -1,7 +1,6 @@
package io.gitlab.jfronny.muscript.ast.compare;
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.*;
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
@ -13,8 +12,8 @@ public class Greater extends BoolExpr {
public final NumberExpr left;
public final NumberExpr right;
public Greater(int chStart, int chEnd, NumberExpr left, NumberExpr right) {
super(Order.Comparison, chStart, chEnd);
public Greater(CodeLocation location, NumberExpr left, NumberExpr right) {
super(Order.Comparison, location);
this.left = left;
this.right = right;
}
@ -29,19 +28,19 @@ public class Greater extends BoolExpr {
NumberExpr left = this.left.optimize();
NumberExpr right = this.right.optimize();
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)
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)
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)
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
// Multiply is left out since it would transform into a division and may be 0
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
return new Greater(chStart, chEnd, left, right);
return new Greater(location, left, right);
}
@Override

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -14,8 +14,8 @@ public class Bind extends DynamicExpr {
public final DynamicExpr callable;
public final DynamicExpr parameter;
public Bind(int chStart, int chEnd, DynamicExpr callable, DynamicExpr parameter) {
super(Order.Call, chStart, chEnd);
public Bind(CodeLocation location, DynamicExpr callable, DynamicExpr parameter) {
super(Order.Call, location);
this.callable = callable;
this.parameter = parameter;
}
@ -47,6 +47,6 @@ public class Bind extends DynamicExpr {
@Override
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.annotations.CanThrow;
import io.gitlab.jfronny.muscript.annotations.UncheckedDynamic;
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.*;
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
@ -21,8 +20,8 @@ public class Call extends DynamicExpr {
public final DynamicExpr left;
public final List<Arg> args;
public Call(int chStart, int chEnd, DynamicExpr left, List<Arg> args) {
super(Order.Call, chStart, chEnd);
public Call(CodeLocation location, DynamicExpr left, List<Arg> args) {
super(Order.Call, location);
this.left = left;
this.args = args;
}
@ -36,18 +35,18 @@ public class Call extends DynamicExpr {
.asCallable();
arg = DFinal.of(args.stream().flatMap(e -> e.get(dataRoot)).toArray(Dynamic[]::new));
} catch (DynamicTypeConversionException e) {
throw e.locational(chStart, chEnd);
throw e.locational(location);
} 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 {
return dc.getValue().apply(arg);
} 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) {
throw e.locational(chStart, chEnd);
throw e.locational(location);
} 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));
}
for (Arg arg : this.args) args.add(arg.optimize());
return new Call(chStart, chEnd, left, args);
return new Call(location, left, args);
}
@Override

View File

@ -19,12 +19,12 @@ public class Closure extends DynamicExpr {
private final DynamicExpr fin;
private final boolean variadic;
public Closure(int chStart, int chEnd, 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);
public Closure(CodeLocation location, List<String> boundArgs, List<Expr<?>> expressions, boolean 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) {
super(Order.Primary, chStart, chEnd);
private Closure(CodeLocation location, List<String> boundArgs, List<Expr<?>> steps, DynamicExpr fin, boolean variadic) {
super(Order.Primary, location);
this.boundArgs = List.copyOf(boundArgs);
this.steps = List.copyOf(steps);
this.fin = fin;
@ -37,8 +37,8 @@ public class Closure extends DynamicExpr {
int ac = args.size();
int ae = boundArgs.size();
if (variadic) ae--;
if (ac < ae) throw new LocationalException(chStart, chEnd, "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 (ac < ae) throw new LocationalException(location, "Invoked with too few 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();
for (int i = 0; i < ae; i++) fork.set(boundArgs.get(i), args.get(i));
if (variadic) {
@ -56,7 +56,7 @@ public class Closure extends DynamicExpr {
@Override
public DynamicExpr optimize() {
// 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

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

View File

@ -18,12 +18,12 @@ public class Get extends DynamicExpr {
private final DynamicExpr left;
private final Expr<?> name;
public Get(int chStart, int chEnd, DynamicExpr left, Expr<?> name) {
super(Order.Call, chStart, chEnd);
public Get(CodeLocation location, DynamicExpr left, Expr<?> name) {
super(Order.Call, location);
this.left = left;
this.name = name;
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) {
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
public DynamicExpr optimize() {
DynamicExpr left = this.left.optimize();
Expr<?> name = this.name.optimize();
return new Get(chStart, chEnd, left, name);
return new Get(location, left, name);
}
@Override

View File

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

View File

@ -15,8 +15,8 @@ import java.io.IOException;
public class Variable extends DynamicExpr {
public final String name;
public Variable(int chStart, int chEnd, String name) {
super(Order.Primary, chStart, chEnd);
public Variable(CodeLocation location, String name) {
super(Order.Primary, location);
this.name = name;
}
@ -24,7 +24,7 @@ public class Variable extends DynamicExpr {
public Dynamic<?> get(Scope dataRoot) {
if (name.equals("this")) return dataRoot;
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,6 @@
package io.gitlab.jfronny.muscript.ast.math;
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.NumberExpr;
@ -13,8 +12,8 @@ public class Minus extends NumberExpr {
public final NumberExpr minuend;
public final NumberExpr subtrahend;
public Minus(int chStart, int chEnd, NumberExpr minuend, NumberExpr subtrahend) {
super(Order.Term, chStart, chEnd);
public Minus(CodeLocation location, NumberExpr minuend, NumberExpr subtrahend) {
super(Order.Term, location);
this.minuend = minuend;
this.subtrahend = subtrahend;
}
@ -29,10 +28,15 @@ public class Minus extends NumberExpr {
NumberExpr minuend = this.minuend.optimize();
NumberExpr subtrahend = this.subtrahend.optimize();
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)
return new Minus(chStart, chEnd, minus.minuend, new Plus(minus.chStart, minus.chEnd, minus.subtrahend, subtrahend)).optimize();
return new Minus(chStart, chEnd, minuend, subtrahend);
return new Minus(location, minus.minuend, new Plus(minus.location, minus.subtrahend, subtrahend)).optimize();
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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,6 @@
package io.gitlab.jfronny.muscript.ast.string;
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
import io.gitlab.jfronny.muscript.compiler.Order;
import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.StringExpr;
@ -13,8 +12,8 @@ public class Concatenate extends StringExpr {
public final StringExpr left;
public final StringExpr right;
public Concatenate(int chStart, int chEnd, StringExpr left, StringExpr right) {
super(Order.Concat, chStart, chEnd);
public Concatenate(CodeLocation location, StringExpr left, StringExpr right) {
super(Order.Concat, location);
this.left = left;
this.right = right;
}
@ -29,14 +28,14 @@ public class Concatenate extends StringExpr {
StringExpr left = this.left.optimize();
StringExpr right = this.right.optimize();
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) {
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) {
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

View File

@ -1,6 +1,7 @@
package io.gitlab.jfronny.muscript.ast.string;
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.data.Scope;
import io.gitlab.jfronny.muscript.ast.*;
@ -11,8 +12,8 @@ import java.io.IOException;
public class StringCoerce extends StringExpr {
private final Expr<?> inner;
public StringCoerce(int chStart, int chEnd, Expr<?> inner) {
super(inner.order, chStart, chEnd);
public StringCoerce(CodeLocation location, Expr<?> inner) {
super(inner.order, location);
this.inner = inner;
}
@ -24,12 +25,12 @@ public class StringCoerce extends StringExpr {
@Override
public StringExpr optimize() {
Expr<?> inner = this.inner.optimize();
if (inner instanceof NullLiteral) return Expr.literal(chStart, chEnd, "null");
if (inner instanceof BoolLiteral expr) return Expr.literal(chStart, chEnd, StringFormatter.toString(expr.value));
if (inner instanceof DynamicLiteral<?> expr) return Expr.literal(chStart, chEnd, StringFormatter.toString(expr.value));
if (inner instanceof NumberLiteral expr) return Expr.literal(chStart, chEnd, StringFormatter.toString(expr.value));
if (inner instanceof NullLiteral) return literal(location, "null");
if (inner instanceof BoolLiteral expr) return literal(location, StringFormatter.toString(expr.value));
if (inner instanceof DynamicLiteral<?> expr) return literal(location, StringFormatter.toString(expr.value));
if (inner instanceof NumberLiteral expr) return literal(location, StringFormatter.toString(expr.value));
if (inner instanceof StringExpr expr) return expr;
return new StringCoerce(chStart, chEnd, inner);
return new StringCoerce(location, inner);
}
@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;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Pattern;
// Heavily inspired by starscript
public class Lexer {
public final String file;
/**
* The type of the token
*/
@ -20,7 +23,12 @@ public class Lexer {
public int start, current;
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 java.util.*;
import java.util.stream.Collectors;
public class Parser {
private final Lexer lexer;
@ -24,11 +25,56 @@ public class Parser {
private final TokenData current = new TokenData();
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) {
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) {
@ -73,7 +119,7 @@ public class Parser {
} catch (RuntimeException e) {
if (e instanceof ParseException) throw e;
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());
}
}
@ -82,12 +128,11 @@ public class Parser {
Expr<?> expr = and();
if (match(Token.QuestionMark)) {
int start = previous.start;
int end = previous.current - 1;
CodeLocation location = previous.location();
Expr<?> trueExpr = expression();
consume(Token.Colon, "Expected ':' after first part of condition");
Expr<?> falseExpr = expression();
expr = new UnresolvedConditional(start, end, asBool(expr), trueExpr, falseExpr);
expr = new UnresolvedConditional(location, asBool(expr), trueExpr, falseExpr);
}
return expr;
@ -97,10 +142,9 @@ public class Parser {
Expr<?> expr = or();
while (match(Token.And)) {
int start = previous.start;
int end = previous.current - 1;
CodeLocation location = previous.location();
Expr<?> right = or();
expr = new And(start, end, asBool(expr), asBool(right));
expr = new And(location, asBool(expr), asBool(right));
}
return expr;
@ -110,10 +154,9 @@ public class Parser {
Expr<?> expr = equality();
while (match(Token.Or)) {
int start = previous.start;
int end = previous.current - 1;
CodeLocation location = previous.location();
Expr<?> right = equality();
expr = new Or(start, end, asBool(expr), asBool(right));
expr = new Or(location, asBool(expr), asBool(right));
}
return expr;
@ -124,11 +167,10 @@ public class Parser {
while (match(Token.EqualEqual, Token.BangEqual)) {
Token op = previous.token;
int start = previous.start;
int end = previous.current - 1;
CodeLocation location = previous.location();
Expr<?> right = concat();
BoolExpr e = new Equal(start, end, expr, right);
if (op == Token.BangEqual) e = new Not(start, end, e);
BoolExpr e = new Equal(location, expr, right);
if (op == Token.BangEqual) e = new Not(location, e);
expr = e;
}
@ -139,10 +181,9 @@ public class Parser {
Expr<?> expr = comparison();
while (match(Token.Concat)) {
int start = previous.start;
int end = previous.current - 1;
CodeLocation location = previous.location();
Expr<?> right = comparison();
expr = new Concatenate(start, end, asString(expr), asString(right));
expr = new Concatenate(location, asString(expr), asString(right));
}
return expr;
@ -153,14 +194,13 @@ public class Parser {
while (match(Token.Greater, Token.GreaterEqual, Token.Less, Token.LessEqual)) {
Token op = previous.token;
int start = previous.start;
int end = previous.current - 1;
CodeLocation location = previous.location();
NumberExpr right = asNumber(term());
expr = switch (op) {
case Greater -> new Greater(start, end, asNumber(expr), right);
case GreaterEqual -> new Not(start, end, new Greater(start, end, right, asNumber(expr)));
case Less -> new Greater(start, end, right, asNumber(expr));
case LessEqual -> new Not(start, end, new Greater(start, end, asNumber(expr), right));
case Greater -> new Greater(location, asNumber(expr), right);
case GreaterEqual -> new Not(location, new Greater(location, right, asNumber(expr)));
case Less -> new Greater(location, right, asNumber(expr));
case LessEqual -> new Not(location, new Greater(location, asNumber(expr), right));
default -> throw new IllegalStateException();
};
}
@ -173,12 +213,11 @@ public class Parser {
while (match(Token.Plus, Token.Minus)) {
Token op = previous.token;
int start = previous.start;
int end = previous.current - 1;
CodeLocation location = previous.location();
NumberExpr right = asNumber(factor());
expr = switch (op) {
case Plus -> new Plus(start, end, asNumber(expr), right);
case Minus -> new Minus(start, end, asNumber(expr), right);
case Plus -> new Plus(location, asNumber(expr), right);
case Minus -> new Minus(location, asNumber(expr), right);
default -> throw new IllegalStateException();
};
}
@ -191,13 +230,12 @@ public class Parser {
while (match(Token.Star, Token.Slash, Token.Percentage)) {
Token op = previous.token;
int start = previous.start;
int end = previous.current - 1;
CodeLocation location = previous.location();
NumberExpr right = asNumber(exp());
expr = switch (op) {
case Star -> new Multiply(start, end, asNumber(expr), right);
case Slash -> new Divide(start, end, asNumber(expr), right);
case Percentage -> new Modulo(start, end, asNumber(expr), right);
case Star -> new Multiply(location, asNumber(expr), right);
case Slash -> new Divide(location, asNumber(expr), right);
case Percentage -> new Modulo(location, asNumber(expr), right);
default -> throw new IllegalStateException();
};
}
@ -209,10 +247,9 @@ public class Parser {
Expr<?> expr = unary();
while (match(Token.UpArrow)) {
int start = previous.start;
int end = previous.current - 1;
CodeLocation location = previous.location();
NumberExpr right = asNumber(unary());
expr = new Power(start, end, asNumber(expr), right);
expr = new Power(location, asNumber(expr), right);
}
return expr;
@ -221,12 +258,11 @@ public class Parser {
private Expr<?> unary() {
if (match(Token.Bang, Token.Minus)) {
Token op = previous.token;
int start = previous.start;
int end = previous.current - 1;
CodeLocation location = previous.location();
Expr<?> right = unary();
return switch (op) {
case Bang -> new Not(start, end, asBool(right));
case Minus -> new Invert(start, end, asNumber(right));
case Bang -> new Not(location, asBool(right));
case Minus -> new Invert(location, asNumber(right));
default -> throw new IllegalStateException();
};
}
@ -238,26 +274,25 @@ public class Parser {
Expr<?> expr = primary();
while (match(Token.LeftParen, Token.Dot, Token.LeftBracket, Token.DoubleColon)) {
int start = previous.start;
int end = previous.current - 1;
CodeLocation location = previous.location();
expr = switch (previous.token) {
case LeftParen -> finishCall(start, end, expr);
case LeftParen -> finishCall(location, expr);
case Dot -> {
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 -> {
DynamicExpr callable;
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)) {
callable = expression().asDynamicExpr();
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.");
yield new Bind(start, end, callable, expr.asDynamicExpr());
yield new Bind(location, callable, expr.asDynamicExpr());
}
case LeftBracket -> {
expr = new Get(start, end, asDynamic(expr), expression());
expr = new Get(location, asDynamic(expr), expression());
consume(Token.RightBracket, "Expected closing bracket");
yield expr;
}
@ -268,7 +303,7 @@ public class Parser {
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);
if (!check(Token.RightParen)) {
@ -278,20 +313,19 @@ public class Parser {
}
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() {
if (match(Token.Null)) return Expr.literalNull(previous.start, previous.current - 1);
if (match(Token.String)) return Expr.literal(previous.start, previous.current - 1, previous.lexeme);
if (match(Token.True, Token.False)) return Expr.literal(previous.start, previous.current - 1, previous.lexeme.equals("true"));
if (match(Token.Number)) return Expr.literal(previous.start, previous.current - 1, Double.parseDouble(previous.lexeme));
if (match(Token.Null)) return Expr.literalNull(previous.location());
if (match(Token.String)) return Expr.literal(previous.location(), previous.lexeme);
if (match(Token.True, Token.False)) return Expr.literal(previous.location(), previous.lexeme.equals("true"));
if (match(Token.Number)) return Expr.literal(previous.location(), Double.parseDouble(previous.lexeme));
if (match(Token.Identifier)) {
int start = previous.start;
int end = previous.current - 1;
CodeLocation location = previous.location();
String name = previous.lexeme;
if (match(Token.Assign)) return new DynamicAssign(start, end, name, expression().asDynamicExpr());
else return new Variable(start, end, name);
if (match(Token.Assign)) return new DynamicAssign(location, name, expression().asDynamicExpr());
else return new Variable(location, name);
}
if (match(Token.LeftParen)) {
@ -303,7 +337,7 @@ public class Parser {
if (match(Token.LeftBrace)) {
int start = previous.start;
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");
String first = previous.lexeme;
if (check(Token.Arrow)) return finishClosure(start, first, false);
@ -345,7 +379,7 @@ public class Parser {
match(Token.Semicolon); // Consume semicolon if present
}
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) {
@ -358,7 +392,7 @@ public class Parser {
content.put(name, expression().asDynamicExpr());
}
consume(Token.RightBrace, "Expected end of object");
return new ObjectLiteral(start, previous.start, content);
return new ObjectLiteral(location(start, previous.start), content);
}
// Type conversion
@ -395,12 +429,16 @@ public class Parser {
}
// Helpers
private CodeLocation location(int chStart, int chEnd) {
return new CodeLocation(chStart, chEnd, lexer.source, lexer.file);
}
private ParseException error(String message) {
return new ParseException(LocationalError.create(lexer.source, current.current - 1, message));
}
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) {
@ -442,7 +480,7 @@ public class Parser {
}
// Token data
private static class TokenData {
private class TokenData {
public Token token;
public String lexeme;
public int start, current;
@ -464,6 +502,10 @@ public class Parser {
public String toString() {
return String.format("%s '%s'", token, lexeme);
}
public CodeLocation location() {
return new CodeLocation(start, current - 1, lexer.source, lexer.file);
}
}
// 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 {
private final List<Expr<?>> steps;
private final DynamicExpr fin;
private final String file;
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);
this.steps = steps;
this.fin = fin;
this.file = file;
}
public Dynamic<?> run(DObject scope) {
@ -45,11 +51,12 @@ public class Script extends Decompilable {
}
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() {
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) {

View File

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

View File

@ -1,9 +1,12 @@
package io.gitlab.jfronny.muscript.error;
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
import org.jetbrains.annotations.Nullable;
import java.util.*;
//TODO maybe this can be eliminated since the source is now included?
/**
* Class for storing errors with code context
* 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();
}
/**
* 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
*
@ -120,9 +134,7 @@ public record LocationalError(String message, Location start, @Nullable Location
}
public Builder setLocation(int chStart) {
start = Location.create(Objects.requireNonNull(source, "source is required to set location"), chStart);
end = null;
return this;
return setLocation(Location.create(Objects.requireNonNull(source, "source is required to set location"), chStart), null);
}
public Builder setLocation(int chStart, int chEnd) {
@ -133,8 +145,12 @@ public record LocationalError(String message, Location start, @Nullable Location
chEnd = chStart;
chStart = a;
}
start = Location.create(source, chStart);
end = Location.create(source, chEnd);
return setLocation(Location.create(source, chStart), Location.create(source, chEnd));
}
public Builder setLocation(Location start, Location end) {
this.start = start;
this.end = end;
return this;
}

View File

@ -1,5 +1,7 @@
package io.gitlab.jfronny.muscript.error;
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
import java.util.LinkedList;
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
*/
public class LocationalException extends RuntimeException {
private final int start, end;
private final CodeLocation location;
private final List<StackFrame> callStack = new LinkedList<>();
public LocationalException(int start, int end) {
public LocationalException(CodeLocation location) {
super();
this.start = start;
this.end = end;
this.location = location;
}
public LocationalException(int start, int end, String message) {
public LocationalException(CodeLocation location, String message) {
super(message);
this.start = start;
this.end = end;
this.location = location;
}
public LocationalException(int start, int end, String message, Throwable cause) {
public LocationalException(CodeLocation location, String message, Throwable cause) {
super(message, cause);
this.start = start;
this.end = end;
this.location = location;
}
public LocationalException(int start, int end, Throwable cause) {
public LocationalException(CodeLocation location, Throwable cause) {
super(cause);
this.start = start;
this.end = end;
this.location = location;
}
public LocationalError asPrintable(String source) {
public LocationalError asPrintable() {
return LocationalError.builder()
.setSource(source)
.setLocation(start, end)
.setSource(location.source())
.setLocation(location.chStart(), location.chEnd())
.setMessage(getLocalizedMessage())
.setCallStack(callStack)
.build();

View File

@ -1,23 +1,23 @@
package io.gitlab.jfronny.muscript.error;
public sealed interface StackFrame {
record Raw(String name, int chStart) implements StackFrame {
record Raw(String file, String name, int chStart) implements StackFrame {
@Override
public String toString() {
return name + " (call: character " + chStart + ")";
return name + " (call: character " + chStart + (file == null ? ")" : " in " + file + ")");
}
public Lined lined(String source) {
int lineStart = source.lastIndexOf('\n', chStart);
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
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;
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
import io.gitlab.jfronny.muscript.compiler.Type;
import org.jetbrains.annotations.Nullable;
@ -11,12 +12,12 @@ public class TypeMismatchException extends LocationalException {
private final Type expected;
private final @Nullable Type actual;
public TypeMismatchException(int start, int end, Type expected, @Nullable Type actual) {
this(start, end, expected, actual, "Expected " + expected + (actual == null ? "" : " but got " + actual));
public TypeMismatchException(CodeLocation location, Type expected, @Nullable Type 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) {
super(start, end, message);
public TypeMismatchException(CodeLocation location, Type expected, @Nullable Type actual, String message) {
super(location, message);
this.expected = expected;
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.dynamic.assign.DynamicAssign;
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.data.Scope;
import io.gitlab.jfronny.muscript.test.util.UnforkableScope;
@ -15,7 +16,7 @@ class AssignTest {
@Test
void testAssignSimple() {
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());
Scope scope = new UnforkableScope();
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.compiler.Parser;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.error.LocationalException;
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;
class StackTraceTest {
@Test
void stackTrace() {
String source = """
final String source = """
someInner = { ->
throw()
}
@ -23,18 +22,32 @@ class StackTraceTest {
someOuter()
""";
final Scope scope = StandardLib.createScope()
.set("throw", args -> {
throw new IllegalArgumentException("No");
});
@Test
void stackTrace() {
assertEquals("""
Error at '(' (character 8): Could not perform call successfully
1 | throw()
^-- Here
at someInner (call: line 5)
at someOuter (call: line 8)""",
assertThrows(LocationalException.class, () -> Parser.parseScript(source)
.run(StandardLib.createScope()
.set("throw", args -> {
throw new IllegalArgumentException("No");
})
))
.asPrintable(source).toString());
assertThrows(LocationalException.class, () -> Parser.parseScript(source).run(scope))
.asPrintable().toString());
}
@Test
void stackTrace2() {
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) {
assertEquals(expectedResult, StringFormatter.toString(t, e ->
e instanceof LocationalException le
? le.asPrintable(source).toString()
? le.asPrintable().toString()
: e.toString()
));
}