[muscript] Even better errors
This commit is contained in:
parent
a9cd45c39d
commit
8bba3a33d5
|
@ -1,9 +1,9 @@
|
|||
# μScript
|
||||
MuScript was created to allow respackopts pack authors to specify conditions for loading resources
|
||||
μScript was created to allow respackopts pack authors to specify conditions for loading resources
|
||||
in a more human-friendly manner than the previous json tree-based system.
|
||||
It is intended to be vaguely like java in its syntax, though it deviates in some aspects.
|
||||
The language is parsed into an AST representation which is executed in-place, no compilation is performed.
|
||||
MuScript supports outputting data using various types, not just strings or booleans
|
||||
μScript supports outputting data using various types, not just strings or booleans
|
||||
|
||||
## Value types
|
||||
This DSL supports numbers (double), booleans, strings, objects, lists and functions.
|
||||
|
@ -52,4 +52,48 @@ Objects support the following operators (x is an object with an entry called `en
|
|||
|
||||
Parentheses (`()`) may be used to indicate order of operation
|
||||
|
||||
Namespacing is done using double colons like in c++ (`namespace::value`), though it should not be used
|
||||
Namespacing is done using double colons like in c++ (`namespace::value`), though it should not be used
|
||||
|
||||
## Embedding μScript
|
||||
μScript is available as a [maven package](https://gitlab.com/JFronny/java-commons/-/packages) which you can add to your project.
|
||||
To use it, first parse a script via `Parser.parse(String script)` and convert the returned generic expression to a typed one
|
||||
by calling `as(Bool|String|Number|Dynamic)Expr`.
|
||||
This process may throw a ParseException.
|
||||
You can call `get(Dynamic<?> dataRoot)` on the result to execute the script on the provided data, which should be an
|
||||
`ExpressionParameter` on which you called `StandardLib.addTo()` to add standard methods.
|
||||
This is also where you can add custom data to be accessed by your script.
|
||||
The execution of a script can throw a LocationalException which may be converted to a LocationalError for printing
|
||||
using the source of the expression if available.
|
||||
You may also call `StarScriptIngester.starScriptToMu()` to generate μScript code from StarScript code.
|
||||
|
||||
A full example could look as follows:
|
||||
```java
|
||||
String source = args[0];
|
||||
Expr<?> parsed;
|
||||
try {
|
||||
parsed = Parser.parse(source); // or Parser.parse(StarScriptIngester.starScriptToMu(source))
|
||||
} catch (Parser.ParseException e) { // Could not parse
|
||||
System.err.println(e.error);
|
||||
return;
|
||||
}
|
||||
BoolExpr typed;
|
||||
try {
|
||||
typed = parsed.asBoolExpr();
|
||||
} catch (LocationalException e) {
|
||||
System.err.println(e.asPrintable(source));
|
||||
return;
|
||||
}
|
||||
ExpressionParameter parameter = StandardLib.addTo(new ExpressionParameter())
|
||||
.set("someValue", DFinal.of(15))
|
||||
.set("someOther", DFinal.of(Map.of(
|
||||
"subValue", DFinal.of(true)
|
||||
)));
|
||||
boolean result;
|
||||
try {
|
||||
result = typed.get(parameter);
|
||||
} catch (LocationalException e) {
|
||||
System.err.println(e.asPrintable(source));
|
||||
return;
|
||||
}
|
||||
System.out.println("Result: " + result);
|
||||
```
|
||||
|
|
|
@ -8,7 +8,9 @@ import io.gitlab.jfronny.muscript.compiler.expr.number.compare.*;
|
|||
import io.gitlab.jfronny.muscript.compiler.expr.number.math.*;
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.string.*;
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.unresolved.*;
|
||||
import io.gitlab.jfronny.muscript.error.*;
|
||||
|
||||
import java.lang.*;
|
||||
import java.util.*;
|
||||
|
||||
public class Parser {
|
||||
|
@ -37,6 +39,9 @@ public class Parser {
|
|||
return conditional();
|
||||
} catch (RuntimeException e) {
|
||||
if (e instanceof ParseException) throw e;
|
||||
else if (e instanceof LocationalException le) {
|
||||
throw new ParseException(le.asPrintable(lexer.source));
|
||||
}
|
||||
else throw error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
@ -45,10 +50,11 @@ public class Parser {
|
|||
Expr<?> expr = and();
|
||||
|
||||
if (match(Token.QuestionMark)) {
|
||||
int character = previous.start;
|
||||
Expr<?> trueExpr = expression();
|
||||
consume(Token.Colon, "Expected ':' after first part of condition.");
|
||||
Expr<?> falseExpr = expression();
|
||||
expr = new UnresolvedConditional(expr.asBoolExpr(), trueExpr, falseExpr);
|
||||
expr = new UnresolvedConditional(character, asBool(expr), trueExpr, falseExpr);
|
||||
}
|
||||
|
||||
return expr;
|
||||
|
@ -58,8 +64,9 @@ public class Parser {
|
|||
Expr<?> expr = or();
|
||||
|
||||
while (match(Token.And)) {
|
||||
int character = previous.start;
|
||||
Expr<?> right = or();
|
||||
expr = new And(expr.asBoolExpr(), right.asBoolExpr());
|
||||
expr = new And(character, asBool(expr), asBool(right));
|
||||
}
|
||||
|
||||
return expr;
|
||||
|
@ -69,8 +76,9 @@ public class Parser {
|
|||
Expr<?> expr = equality();
|
||||
|
||||
while (match(Token.Or)) {
|
||||
int character = previous.start;
|
||||
Expr<?> right = equality();
|
||||
expr = new Or(expr.asBoolExpr(), right.asBoolExpr());
|
||||
expr = new Or(character, asBool(expr), asBool(right));
|
||||
}
|
||||
|
||||
return expr;
|
||||
|
@ -81,9 +89,10 @@ public class Parser {
|
|||
|
||||
while (match(Token.EqualEqual, Token.BangEqual)) {
|
||||
Token op = previous.token;
|
||||
int character = previous.start;
|
||||
Expr<?> right = concat();
|
||||
BoolExpr e = new Equal(expr, right);
|
||||
if (op == Token.BangEqual) e = new Not(e);
|
||||
BoolExpr e = new Equal(character, expr, right);
|
||||
if (op == Token.BangEqual) e = new Not(character, e);
|
||||
expr = e;
|
||||
}
|
||||
|
||||
|
@ -94,8 +103,9 @@ public class Parser {
|
|||
Expr<?> expr = comparison();
|
||||
|
||||
while (match(Token.Concat)) {
|
||||
int character = previous.start;
|
||||
Expr<?> right = comparison();
|
||||
expr = new Concatenate(expr.asStringExpr(), right.asStringExpr());
|
||||
expr = new Concatenate(character, asString(expr), asString(right));
|
||||
}
|
||||
|
||||
return expr;
|
||||
|
@ -106,12 +116,13 @@ public class Parser {
|
|||
|
||||
while (match(Token.Greater, Token.GreaterEqual, Token.Less, Token.LessEqual)) {
|
||||
Token op = previous.token;
|
||||
NumberExpr right = term().asNumberExpr();
|
||||
int character = previous.start;
|
||||
NumberExpr right = asNumber(term());
|
||||
expr = switch (op) {
|
||||
case Greater -> new Greater(expr.asNumberExpr(), right);
|
||||
case GreaterEqual -> new Not(new Less(expr.asNumberExpr(), right));
|
||||
case Less -> new Less(expr.asNumberExpr(), right);
|
||||
case LessEqual -> new Not(new Greater(expr.asNumberExpr(), right));
|
||||
case Greater -> new Greater(character, asNumber(expr), right);
|
||||
case GreaterEqual -> new Not(character, new Less(character, asNumber(expr), right));
|
||||
case Less -> new Less(character, asNumber(expr), right);
|
||||
case LessEqual -> new Not(character, new Greater(character, asNumber(expr), right));
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
}
|
||||
|
@ -124,10 +135,11 @@ public class Parser {
|
|||
|
||||
while (match(Token.Plus, Token.Minus)) {
|
||||
Token op = previous.token;
|
||||
NumberExpr right = factor().asNumberExpr();
|
||||
int character = previous.start;
|
||||
NumberExpr right = asNumber(factor());
|
||||
expr = switch (op) {
|
||||
case Plus -> new Plus(expr.asNumberExpr(), right);
|
||||
case Minus -> new Minus(expr.asNumberExpr(), right);
|
||||
case Plus -> new Plus(character, asNumber(expr), right);
|
||||
case Minus -> new Minus(character, asNumber(expr), right);
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
}
|
||||
|
@ -140,11 +152,12 @@ public class Parser {
|
|||
|
||||
while (match(Token.Star, Token.Slash, Token.Percentage)) {
|
||||
Token op = previous.token;
|
||||
NumberExpr right = exp().asNumberExpr();
|
||||
int character = previous.start;
|
||||
NumberExpr right = asNumber(exp());
|
||||
expr = switch (op) {
|
||||
case Star -> new Multiply(expr.asNumberExpr(), right);
|
||||
case Slash -> new Divide(expr.asNumberExpr(), right);
|
||||
case Percentage -> new Modulo(expr.asNumberExpr(), right);
|
||||
case Star -> new Multiply(character, asNumber(expr), right);
|
||||
case Slash -> new Divide(character, asNumber(expr), right);
|
||||
case Percentage -> new Modulo(character, asNumber(expr), right);
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
}
|
||||
|
@ -156,8 +169,9 @@ public class Parser {
|
|||
Expr<?> expr = unary();
|
||||
|
||||
while (match(Token.UpArrow)) {
|
||||
NumberExpr right = unary().asNumberExpr();
|
||||
expr = new Power(expr.asNumberExpr(), right);
|
||||
int character = previous.start;
|
||||
NumberExpr right = asNumber(unary());
|
||||
expr = new Power(character, asNumber(expr), right);
|
||||
}
|
||||
|
||||
return expr;
|
||||
|
@ -166,10 +180,11 @@ public class Parser {
|
|||
private Expr<?> unary() {
|
||||
if (match(Token.Bang, Token.Minus)) {
|
||||
Token op = previous.token;
|
||||
int character = previous.start;
|
||||
Expr<?> right = unary();
|
||||
return switch (op) {
|
||||
case Bang -> new Not(right.asBoolExpr());
|
||||
case Minus -> new Invert(right.asNumberExpr());
|
||||
case Bang -> new Not(character, asBool(right));
|
||||
case Minus -> new Invert(character, asNumber(right));
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
}
|
||||
|
@ -181,14 +196,15 @@ public class Parser {
|
|||
Expr<?> expr = primary();
|
||||
|
||||
while (match(Token.LeftParen, Token.Dot, Token.LeftBracket)) {
|
||||
int character = previous.start;
|
||||
expr = switch (previous.token) {
|
||||
case LeftParen -> finishCall(expr);
|
||||
case LeftParen -> finishCall(character, expr);
|
||||
case Dot -> {
|
||||
TokenData name = consume(Token.Identifier, "Expected field name after '.'.");
|
||||
yield new Get(expr.asDynamicExpr(), Expr.literal(name.lexeme));
|
||||
yield new Get(character, asDynamic(expr), Expr.literal(name.start, name.lexeme));
|
||||
}
|
||||
case LeftBracket -> {
|
||||
expr = new Get(expr.asDynamicExpr(), expression());
|
||||
expr = new Get(character, asDynamic(expr), expression());
|
||||
consume(Token.RightBracket, "Expected closing bracket");
|
||||
yield expr;
|
||||
}
|
||||
|
@ -199,25 +215,25 @@ public class Parser {
|
|||
return expr;
|
||||
}
|
||||
|
||||
private Expr<?> finishCall(Expr<?> callee) {
|
||||
private Expr<?> finishCall(int character, Expr<?> callee) {
|
||||
List<DynamicExpr> args = new ArrayList<>(2);
|
||||
|
||||
if (!check(Token.RightParen)) {
|
||||
do {
|
||||
args.add(expression().asDynamicExpr());
|
||||
args.add(asDynamic(expression()));
|
||||
} while (match(Token.Comma));
|
||||
}
|
||||
|
||||
consume(Token.RightParen, "Expected ')' after function arguments.");
|
||||
return new Call(callee.asDynamicExpr(), args);
|
||||
return new Call(character, asDynamic(callee), args);
|
||||
}
|
||||
|
||||
private Expr<?> primary() {
|
||||
if (match(Token.Null)) return Expr.literalNull();
|
||||
if (match(Token.String)) return Expr.literal(previous.lexeme);
|
||||
if (match(Token.True, Token.False)) return Expr.literal(previous.lexeme.equals("true"));
|
||||
if (match(Token.Number)) return Expr.literal(Double.parseDouble(previous.lexeme));
|
||||
if (match(Token.Identifier)) return new Variable(previous.lexeme);
|
||||
if (match(Token.Null)) return Expr.literalNull(previous.start);
|
||||
if (match(Token.String)) return Expr.literal(previous.start, previous.lexeme);
|
||||
if (match(Token.True, Token.False)) return Expr.literal(previous.start, previous.lexeme.equals("true"));
|
||||
if (match(Token.Number)) return Expr.literal(previous.start, Double.parseDouble(previous.lexeme));
|
||||
if (match(Token.Identifier)) return new Variable(previous.start, previous.lexeme);
|
||||
|
||||
if (match(Token.LeftParen)) {
|
||||
Expr<?> expr = expression();
|
||||
|
@ -228,9 +244,42 @@ public class Parser {
|
|||
throw error("Expected expression.");
|
||||
}
|
||||
|
||||
// Type conversion
|
||||
private BoolExpr asBool(Expr<?> expression) {
|
||||
try {
|
||||
return expression.asBoolExpr();
|
||||
} catch (TypeMismatchException e) {
|
||||
throw new ParseException(LocationalError.create(lexer.source, expression.character, e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private NumberExpr asNumber(Expr<?> expression) {
|
||||
try {
|
||||
return expression.asNumberExpr();
|
||||
} catch (TypeMismatchException e) {
|
||||
throw new ParseException(LocationalError.create(lexer.source, expression.character, e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private StringExpr asString(Expr<?> expression) {
|
||||
try {
|
||||
return expression.asStringExpr();
|
||||
} catch (TypeMismatchException e) {
|
||||
throw new ParseException(LocationalError.create(lexer.source, expression.character, e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private DynamicExpr asDynamic(Expr<?> expression) {
|
||||
try {
|
||||
return expression.asDynamicExpr();
|
||||
} catch (TypeMismatchException e) {
|
||||
throw new ParseException(LocationalError.create(lexer.source, expression.character, e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers
|
||||
private ParseException error(String message) {
|
||||
return new ParseException(Error.create(lexer.source, current.current - 1, message));
|
||||
return new ParseException(LocationalError.create(lexer.source, current.current - 1, message));
|
||||
}
|
||||
|
||||
private TokenData consume(Token token, String message) {
|
||||
|
@ -298,9 +347,9 @@ public class Parser {
|
|||
|
||||
// Parse Exception
|
||||
public static class ParseException extends RuntimeException {
|
||||
public final Error error;
|
||||
public final LocationalError error;
|
||||
|
||||
public ParseException(Error error) {
|
||||
public ParseException(LocationalError error) {
|
||||
super(error.toString());
|
||||
this.error = error;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,10 @@ import io.gitlab.jfronny.muscript.compiler.*;
|
|||
import io.gitlab.jfronny.muscript.compiler.expr.dynamic.*;
|
||||
|
||||
public abstract non-sealed class BoolExpr extends Expr<Boolean> {
|
||||
public BoolExpr(int character) {
|
||||
super(character);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getResultType() {
|
||||
return Type.Boolean;
|
||||
|
@ -11,6 +15,6 @@ public abstract non-sealed class BoolExpr extends Expr<Boolean> {
|
|||
|
||||
@Override
|
||||
public DynamicExpr asDynamicExpr() {
|
||||
return new DynamicCoerce(this);
|
||||
return new DynamicCoerce(character, this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,10 @@ import io.gitlab.jfronny.muscript.compiler.expr.dynamic.unpack.*;
|
|||
import io.gitlab.jfronny.muscript.dynamic.*;
|
||||
|
||||
public abstract non-sealed class DynamicExpr extends Expr<Dynamic<?>> {
|
||||
public DynamicExpr(int character) {
|
||||
super(character);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getResultType() {
|
||||
return Type.Dynamic;
|
||||
|
@ -12,17 +16,17 @@ public abstract non-sealed class DynamicExpr extends Expr<Dynamic<?>> {
|
|||
|
||||
@Override
|
||||
public BoolExpr asBoolExpr() {
|
||||
return new BoolUnpack(this);
|
||||
return new BoolUnpack(character, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringExpr asStringExpr() {
|
||||
return new StringUnpack(this);
|
||||
return new StringUnpack(character, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberExpr asNumberExpr() {
|
||||
return new NumberUnpack(this);
|
||||
return new NumberUnpack(character, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,31 +1,35 @@
|
|||
package io.gitlab.jfronny.muscript.compiler.expr;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.annotations.*;
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.common.literal.*;
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.string.*;
|
||||
import io.gitlab.jfronny.muscript.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.error.*;
|
||||
|
||||
@CanThrow
|
||||
public sealed abstract class Expr<T> permits BoolExpr, DynamicExpr, NullLiteral, NumberExpr, StringExpr {
|
||||
public abstract Type getResultType();
|
||||
public abstract T get(Dynamic<?> branch, Dynamic<?> dataRoot);
|
||||
|
||||
public T get(Dynamic<?> branch) {
|
||||
return get(branch, branch);
|
||||
protected Expr(int character) {
|
||||
this.character = character;
|
||||
}
|
||||
|
||||
public abstract Type getResultType();
|
||||
public abstract T get(Dynamic<?> dataRoot);
|
||||
public final int character;
|
||||
|
||||
public BoolExpr asBoolExpr() {
|
||||
if (this instanceof BoolExpr e) return e;
|
||||
throw new IllegalArgumentException("Expected boolean but is " + getResultType());
|
||||
throw new TypeMismatchException(character, Type.Boolean, getResultType());
|
||||
}
|
||||
|
||||
public StringExpr asStringExpr() {
|
||||
if (this instanceof StringExpr e) return e;
|
||||
return new StringCoerce(this);
|
||||
return new StringCoerce(character, this);
|
||||
}
|
||||
|
||||
public NumberExpr asNumberExpr() {
|
||||
if (this instanceof NumberExpr e) return e;
|
||||
throw new IllegalArgumentException("Expected number but is " + getResultType());
|
||||
throw new TypeMismatchException(character, Type.Number, getResultType());
|
||||
}
|
||||
|
||||
public abstract DynamicExpr asDynamicExpr();
|
||||
|
@ -34,19 +38,19 @@ public sealed abstract class Expr<T> permits BoolExpr, DynamicExpr, NullLiteral,
|
|||
return this instanceof NullLiteral;
|
||||
}
|
||||
|
||||
public static BoolExpr literal(boolean bool) {
|
||||
return new BoolLiteral(bool);
|
||||
public static BoolExpr literal(int character, boolean bool) {
|
||||
return new BoolLiteral(character, bool);
|
||||
}
|
||||
|
||||
public static StringExpr literal(String string) {
|
||||
return new StringLiteral(string);
|
||||
public static StringExpr literal(int character, String string) {
|
||||
return new StringLiteral(character, string);
|
||||
}
|
||||
|
||||
public static NumberExpr literal(double number) {
|
||||
return new NumberLiteral(number);
|
||||
public static NumberExpr literal(int character, double number) {
|
||||
return new NumberLiteral(character, number);
|
||||
}
|
||||
|
||||
public static Expr<?> literalNull() {
|
||||
return new NullLiteral();
|
||||
public static NullLiteral literalNull(int character) {
|
||||
return new NullLiteral(character);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,44 @@
|
|||
package io.gitlab.jfronny.muscript.compiler.expr;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.annotations.*;
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.common.literal.*;
|
||||
import io.gitlab.jfronny.muscript.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.error.*;
|
||||
|
||||
@CanThrow
|
||||
public final class NullLiteral extends Expr<Object> {
|
||||
public NullLiteral(int character) {
|
||||
super(character);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getResultType() {
|
||||
return Type.Dynamic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
public Object get(Dynamic<?> dataRoot) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr asDynamicExpr() {
|
||||
return new DynamicLiteral<>(new DNull());
|
||||
return new DynamicLiteral<>(character, new DNull());
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberExpr asNumberExpr() {
|
||||
throw new LocationalException(character, "Attempted to convert null to a number");
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringExpr asStringExpr() {
|
||||
return Expr.literal(character, "null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoolExpr asBoolExpr() {
|
||||
throw new LocationalException(character, "Attempted to convert null to a boolean");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,10 @@ import io.gitlab.jfronny.muscript.compiler.*;
|
|||
import io.gitlab.jfronny.muscript.compiler.expr.dynamic.*;
|
||||
|
||||
public abstract non-sealed class NumberExpr extends Expr<Double> {
|
||||
public NumberExpr(int character) {
|
||||
super(character);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getResultType() {
|
||||
return Type.Number;
|
||||
|
@ -11,6 +15,6 @@ public abstract non-sealed class NumberExpr extends Expr<Double> {
|
|||
|
||||
@Override
|
||||
public DynamicExpr asDynamicExpr() {
|
||||
return new DynamicCoerce(this);
|
||||
return new DynamicCoerce(character, this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,10 @@ import io.gitlab.jfronny.muscript.compiler.*;
|
|||
import io.gitlab.jfronny.muscript.compiler.expr.dynamic.*;
|
||||
|
||||
public abstract non-sealed class StringExpr extends Expr<String> {
|
||||
public StringExpr(int character) {
|
||||
super(character);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getResultType() {
|
||||
return Type.String;
|
||||
|
@ -11,6 +15,6 @@ public abstract non-sealed class StringExpr extends Expr<String> {
|
|||
|
||||
@Override
|
||||
public DynamicExpr asDynamicExpr() {
|
||||
return new DynamicCoerce(this);
|
||||
return new DynamicCoerce(character, this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package io.gitlab.jfronny.muscript.compiler.expr.annotations;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Annotation for expressions that may throw exceptions
|
||||
* Any expression that doesn't have this annotation will only throw if an expression referenced from it throws
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface CanThrow {
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package io.gitlab.jfronny.muscript.compiler.expr.annotations;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Annotation for expressions that utilize Dynamic.as*
|
||||
* These expressions must also be annotated with @CanThrow
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface UncheckedDynamic {
|
||||
}
|
|
@ -7,13 +7,14 @@ public class And extends BoolExpr {
|
|||
private final BoolExpr left;
|
||||
private final BoolExpr right;
|
||||
|
||||
public And(BoolExpr left, BoolExpr right) {
|
||||
public And(int character, BoolExpr left, BoolExpr right) {
|
||||
super(character);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return left.get(branch, dataRoot) && right.get(branch, dataRoot);
|
||||
public Boolean get(Dynamic<?> dataRoot) {
|
||||
return left.get(dataRoot) && right.get(dataRoot);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,13 @@ import io.gitlab.jfronny.muscript.dynamic.*;
|
|||
public class Not extends BoolExpr {
|
||||
private final BoolExpr inner;
|
||||
|
||||
public Not(BoolExpr inner) {
|
||||
public Not(int character, BoolExpr inner) {
|
||||
super(character);
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return !inner.get(branch, dataRoot);
|
||||
public Boolean get(Dynamic<?> dataRoot) {
|
||||
return !inner.get(dataRoot);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,14 @@ public class Or extends BoolExpr {
|
|||
private final BoolExpr left;
|
||||
private final BoolExpr right;
|
||||
|
||||
public Or(BoolExpr left, BoolExpr right) {
|
||||
public Or(int character, BoolExpr left, BoolExpr right) {
|
||||
super(character);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return left.get(branch, dataRoot) || right.get(branch, dataRoot);
|
||||
public Boolean get(Dynamic<?> dataRoot) {
|
||||
return left.get(dataRoot) || right.get(dataRoot);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,14 +9,15 @@ public class Equal extends BoolExpr {
|
|||
private final Expr<?> left;
|
||||
private final Expr<?> right;
|
||||
|
||||
public Equal(Expr<?> left, Expr<?> right) {
|
||||
public Equal(int character, Expr<?> left, Expr<?> right) {
|
||||
super(character);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return Objects.equals(unwrap(left.get(branch, dataRoot)), unwrap(right.get(branch, dataRoot)));
|
||||
public Boolean get(Dynamic<?> dataRoot) {
|
||||
return Objects.equals(unwrap(left.get(dataRoot)), unwrap(right.get(dataRoot)));
|
||||
}
|
||||
|
||||
private Object unwrap(Object o) {
|
||||
|
|
|
@ -8,16 +8,15 @@ public class BoolConditional extends BoolExpr {
|
|||
public final BoolExpr trueExpr;
|
||||
public final BoolExpr falseExpr;
|
||||
|
||||
public BoolConditional(BoolExpr condition, BoolExpr trueExpr, BoolExpr falseExpr) {
|
||||
public BoolConditional(int character, BoolExpr condition, BoolExpr trueExpr, BoolExpr falseExpr) {
|
||||
super(character);
|
||||
this.condition = condition;
|
||||
this.trueExpr = trueExpr;
|
||||
this.falseExpr = falseExpr;
|
||||
if (trueExpr.getResultType() != falseExpr.getResultType())
|
||||
throw new IllegalArgumentException("Values used in conditional operator must be of the same type");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return condition.get(branch, dataRoot) ? trueExpr.get(branch, dataRoot) : falseExpr.get(branch, dataRoot);
|
||||
public Boolean get(Dynamic<?> dataRoot) {
|
||||
return condition.get(dataRoot) ? trueExpr.get(dataRoot) : falseExpr.get(dataRoot);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,22 +2,22 @@ package io.gitlab.jfronny.muscript.compiler.expr.common.conditional;
|
|||
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.*;
|
||||
import io.gitlab.jfronny.muscript.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.error.*;
|
||||
|
||||
public class DynamicConditional extends DynamicExpr {
|
||||
public final BoolExpr condition;
|
||||
public final DynamicExpr trueExpr;
|
||||
public final DynamicExpr falseExpr;
|
||||
|
||||
public DynamicConditional(BoolExpr condition, DynamicExpr trueExpr, DynamicExpr falseExpr) {
|
||||
public DynamicConditional(int character, BoolExpr condition, DynamicExpr trueExpr, DynamicExpr falseExpr) throws TypeMismatchException {
|
||||
super(character);
|
||||
this.condition = condition;
|
||||
this.trueExpr = trueExpr;
|
||||
this.falseExpr = falseExpr;
|
||||
if (trueExpr.getResultType() != falseExpr.getResultType())
|
||||
throw new IllegalArgumentException("Values used in conditional operator must be of the same type");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return condition.get(branch, dataRoot) ? trueExpr.get(branch, dataRoot) : falseExpr.get(branch, dataRoot);
|
||||
public Dynamic get(Dynamic<?> dataRoot) {
|
||||
return condition.get(dataRoot) ? trueExpr.get(dataRoot) : falseExpr.get(dataRoot);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,16 +8,15 @@ public class NumberConditional extends NumberExpr {
|
|||
public final NumberExpr trueExpr;
|
||||
public final NumberExpr falseExpr;
|
||||
|
||||
public NumberConditional(BoolExpr condition, NumberExpr trueExpr, NumberExpr falseExpr) {
|
||||
public NumberConditional(int character, BoolExpr condition, NumberExpr trueExpr, NumberExpr falseExpr) {
|
||||
super(character);
|
||||
this.condition = condition;
|
||||
this.trueExpr = trueExpr;
|
||||
this.falseExpr = falseExpr;
|
||||
if (trueExpr.getResultType() != falseExpr.getResultType())
|
||||
throw new IllegalArgumentException("Values used in conditional operator must be of the same type");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return condition.get(branch, dataRoot) ? trueExpr.get(branch, dataRoot) : falseExpr.get(branch, dataRoot);
|
||||
public Double get(Dynamic<?> dataRoot) {
|
||||
return condition.get(dataRoot) ? trueExpr.get(dataRoot) : falseExpr.get(dataRoot);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,16 +8,15 @@ public class StringConditional extends StringExpr {
|
|||
public final StringExpr trueExpr;
|
||||
public final StringExpr falseExpr;
|
||||
|
||||
public StringConditional(BoolExpr condition, StringExpr trueExpr, StringExpr falseExpr) {
|
||||
public StringConditional(int character, BoolExpr condition, StringExpr trueExpr, StringExpr falseExpr) {
|
||||
super(character);
|
||||
this.condition = condition;
|
||||
this.trueExpr = trueExpr;
|
||||
this.falseExpr = falseExpr;
|
||||
if (trueExpr.getResultType() != falseExpr.getResultType())
|
||||
throw new IllegalArgumentException("Values used in conditional operator must be of the same type");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return condition.get(branch, dataRoot) ? trueExpr.get(branch, dataRoot) : falseExpr.get(branch, dataRoot);
|
||||
public String get(Dynamic<?> dataRoot) {
|
||||
return condition.get(dataRoot) ? trueExpr.get(dataRoot) : falseExpr.get(dataRoot);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,13 @@ import io.gitlab.jfronny.muscript.dynamic.*;
|
|||
public final class BoolLiteral extends BoolExpr {
|
||||
private final boolean value;
|
||||
|
||||
public BoolLiteral(boolean value) {
|
||||
public BoolLiteral(int character, boolean value) {
|
||||
super(character);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
public Boolean get(Dynamic<?> dataRoot) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,13 @@ import io.gitlab.jfronny.muscript.dynamic.*;
|
|||
public final class DynamicLiteral<T> extends DynamicExpr {
|
||||
private final Dynamic<T> value;
|
||||
|
||||
public DynamicLiteral(Dynamic<T> value) {
|
||||
public DynamicLiteral(int character, Dynamic<T> value) {
|
||||
super(character);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic<T> get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
public Dynamic<T> get(Dynamic<?> dataRoot) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,13 @@ import io.gitlab.jfronny.muscript.dynamic.*;
|
|||
public final class NumberLiteral extends NumberExpr {
|
||||
private final double value;
|
||||
|
||||
public NumberLiteral(double value) {
|
||||
public NumberLiteral(int character, double value) {
|
||||
super(character);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
public Double get(Dynamic<?> dataRoot) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,13 @@ import io.gitlab.jfronny.muscript.dynamic.*;
|
|||
public final class StringLiteral extends StringExpr {
|
||||
private final String value;
|
||||
|
||||
public StringLiteral(String value) {
|
||||
public StringLiteral(int character, String value) {
|
||||
super(character);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
public String get(Dynamic<?> dataRoot) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,28 @@
|
|||
package io.gitlab.jfronny.muscript.compiler.expr.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.*;
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.annotations.*;
|
||||
import io.gitlab.jfronny.muscript.dynamic.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@CanThrow @UncheckedDynamic
|
||||
public class Call extends DynamicExpr {
|
||||
private final DynamicExpr left;
|
||||
private final List<DynamicExpr> args;
|
||||
|
||||
public Call(DynamicExpr left, List<DynamicExpr> args) {
|
||||
public Call(int character, DynamicExpr left, List<DynamicExpr> args) {
|
||||
super(character);
|
||||
this.left = left;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic<?> get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return left.get(branch, dataRoot).asCallable().getValue().apply(DFinal.of(args.stream().map(e -> e.get(dataRoot, dataRoot)).toArray(Dynamic[]::new)));
|
||||
public Dynamic<?> get(Dynamic<?> dataRoot) {
|
||||
try {
|
||||
return left.get(dataRoot).asCallable().getValue().apply(DFinal.of(args.stream().map(e -> e.get(dataRoot)).toArray(Dynamic[]::new)));
|
||||
} catch (DynamicTypeConversionException e) {
|
||||
throw e.locational(character);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,22 +6,25 @@ import io.gitlab.jfronny.muscript.dynamic.*;
|
|||
public class DynamicCoerce extends DynamicExpr {
|
||||
private final Expr<?> inner;
|
||||
|
||||
public DynamicCoerce(Expr<?> inner) {
|
||||
public DynamicCoerce(int character, Expr<?> inner) {
|
||||
super(character);
|
||||
this.inner = inner;
|
||||
if (!(inner instanceof DynamicExpr)
|
||||
&& !(inner instanceof BoolExpr)
|
||||
&& !(inner instanceof StringExpr)
|
||||
&& !(inner instanceof NumberExpr)) {
|
||||
&& !(inner instanceof NumberExpr)
|
||||
&& !(inner instanceof NullLiteral)) {
|
||||
throw new IllegalArgumentException("A DynamicCoerce can only be created with a well-defined expression type");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic<?> get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
if (inner instanceof DynamicExpr e) return e.get(branch, dataRoot);
|
||||
if (inner instanceof BoolExpr e) return DFinal.of(e.get(branch, dataRoot));
|
||||
if (inner instanceof StringExpr e) return DFinal.of(e.get(branch, dataRoot));
|
||||
if (inner instanceof NumberExpr e) return DFinal.of(e.get(branch, dataRoot));
|
||||
public Dynamic<?> get(Dynamic<?> dataRoot) {
|
||||
if (inner instanceof DynamicExpr e) return e.get(dataRoot);
|
||||
if (inner instanceof BoolExpr e) return DFinal.of(e.get(dataRoot));
|
||||
if (inner instanceof StringExpr e) return DFinal.of(e.get(dataRoot));
|
||||
if (inner instanceof NumberExpr e) return DFinal.of(e.get(dataRoot));
|
||||
if (inner instanceof NullLiteral) return new DNull();
|
||||
throw new IllegalStateException("The inner expression of DynamicCoerce should never be of undefined type");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,28 +2,32 @@ package io.gitlab.jfronny.muscript.compiler.expr.dynamic;
|
|||
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.*;
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.annotations.*;
|
||||
import io.gitlab.jfronny.muscript.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.error.*;
|
||||
|
||||
@CanThrow @UncheckedDynamic
|
||||
public class Get extends DynamicExpr {
|
||||
private final DynamicExpr left;
|
||||
private final Expr<?> name;
|
||||
|
||||
public Get(DynamicExpr left, Expr<?> name) {
|
||||
public Get(int character, DynamicExpr left, Expr<?> name) {
|
||||
super(character);
|
||||
this.left = left;
|
||||
this.name = name;
|
||||
if (name.getResultType() != Type.String && name.getResultType() != Type.Number && name.getResultType() != Type.Dynamic) {
|
||||
throw new IllegalArgumentException("Name must be either a string or a number");
|
||||
throw new TypeMismatchException(character, Type.String, name.getResultType(), "Name must be either a string or a number");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic<?> get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
Dynamic<?> left = this.left.get(branch, dataRoot);
|
||||
public Dynamic<?> get(Dynamic<?> dataRoot) {
|
||||
Dynamic<?> left = this.left.get(dataRoot);
|
||||
if (left instanceof DObject o) {
|
||||
return o.get(name.asStringExpr().get(dataRoot, dataRoot));
|
||||
return o.get(name.asStringExpr().get(dataRoot));
|
||||
} else if (left instanceof DList l) {
|
||||
return l.get(name.asNumberExpr().get(dataRoot, dataRoot).intValue());
|
||||
return l.get(name.asNumberExpr().get(dataRoot).intValue());
|
||||
}
|
||||
throw new IllegalArgumentException("The element to get value \"" + name.asStringExpr().get(dataRoot, dataRoot) + "\" from is not of a valid type");
|
||||
throw new DynamicTypeConversionException("object or list").locational(character);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,30 @@
|
|||
package io.gitlab.jfronny.muscript.compiler.expr.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.*;
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.annotations.*;
|
||||
import io.gitlab.jfronny.muscript.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.error.*;
|
||||
|
||||
@CanThrow @UncheckedDynamic
|
||||
public class Variable extends DynamicExpr {
|
||||
private final String name;
|
||||
|
||||
public Variable(String name) {
|
||||
public Variable(int character, String name) {
|
||||
super(character);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic<?> get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
if (branch.asObject().has(name)) return branch.asObject().get(name);
|
||||
public Dynamic<?> get(Dynamic<?> dataRoot) {
|
||||
if (dataRoot.asObject().has(name)) return dataRoot.asObject().get(name);
|
||||
else if (name.contains("::")) {
|
||||
Dynamic<?> res = branch;
|
||||
Dynamic<?> res = dataRoot;
|
||||
for (String s : name.split("::")) {
|
||||
if (!res.asObject().has(s))
|
||||
throw new IllegalArgumentException("This object doesn't contain that name");
|
||||
throw new LocationalException(character, "This object doesn't contain that name");
|
||||
res = res.asObject().get(s);
|
||||
}
|
||||
return res;
|
||||
} else throw new IllegalArgumentException("This object doesn't contain that name");
|
||||
} else throw new LocationalException(character, "This object doesn't contain that name");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,24 @@
|
|||
package io.gitlab.jfronny.muscript.compiler.expr.dynamic.unpack;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.*;
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.annotations.*;
|
||||
import io.gitlab.jfronny.muscript.dynamic.*;
|
||||
|
||||
@CanThrow @UncheckedDynamic
|
||||
public class BoolUnpack extends BoolExpr {
|
||||
private final DynamicExpr inner;
|
||||
|
||||
public BoolUnpack(DynamicExpr inner) {
|
||||
public BoolUnpack(int character, DynamicExpr inner) {
|
||||
super(character);
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return inner.get(branch, dataRoot).asBool().getValue();
|
||||
public Boolean get(Dynamic<?> dataRoot) {
|
||||
try {
|
||||
return inner.get(dataRoot).asBool().getValue();
|
||||
} catch (DynamicTypeConversionException e) {
|
||||
throw e.locational(character);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,24 @@
|
|||
package io.gitlab.jfronny.muscript.compiler.expr.dynamic.unpack;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.*;
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.annotations.*;
|
||||
import io.gitlab.jfronny.muscript.dynamic.*;
|
||||
|
||||
@CanThrow @UncheckedDynamic
|
||||
public class NumberUnpack extends NumberExpr {
|
||||
private final DynamicExpr inner;
|
||||
|
||||
public NumberUnpack(DynamicExpr inner) {
|
||||
public NumberUnpack(int character, DynamicExpr inner) {
|
||||
super(character);
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return inner.get(branch, dataRoot).asNumber().getValue();
|
||||
public Double get(Dynamic<?> dataRoot) {
|
||||
try {
|
||||
return inner.get(dataRoot).asNumber().getValue();
|
||||
} catch (DynamicTypeConversionException e) {
|
||||
throw e.locational(character);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,24 @@
|
|||
package io.gitlab.jfronny.muscript.compiler.expr.dynamic.unpack;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.*;
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.annotations.*;
|
||||
import io.gitlab.jfronny.muscript.dynamic.*;
|
||||
|
||||
@CanThrow @UncheckedDynamic
|
||||
public class StringUnpack extends StringExpr {
|
||||
private final DynamicExpr inner;
|
||||
|
||||
public StringUnpack(DynamicExpr inner) {
|
||||
public StringUnpack(int character, DynamicExpr inner) {
|
||||
super(character);
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return inner.get(branch, dataRoot).asString().getValue();
|
||||
public String get(Dynamic<?> dataRoot) {
|
||||
try {
|
||||
return inner.get(dataRoot).asString().getValue();
|
||||
} catch (DynamicTypeConversionException e) {
|
||||
throw e.locational(character);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,14 @@ public class Greater extends BoolExpr {
|
|||
private final NumberExpr left;
|
||||
private final NumberExpr right;
|
||||
|
||||
public Greater(NumberExpr left, NumberExpr right) {
|
||||
public Greater(int character, NumberExpr left, NumberExpr right) {
|
||||
super(character);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return left.get(branch, dataRoot) > right.get(branch, dataRoot);
|
||||
public Boolean get(Dynamic<?> dataRoot) {
|
||||
return left.get(dataRoot) > right.get(dataRoot);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,14 @@ public class Less extends BoolExpr {
|
|||
private final NumberExpr left;
|
||||
private final NumberExpr right;
|
||||
|
||||
public Less(NumberExpr left, NumberExpr right) {
|
||||
public Less(int character, NumberExpr left, NumberExpr right) {
|
||||
super(character);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return left.get(branch, dataRoot) < right.get(branch, dataRoot);
|
||||
public Boolean get(Dynamic<?> dataRoot) {
|
||||
return left.get(dataRoot) < right.get(dataRoot);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,14 @@ public class Divide extends NumberExpr {
|
|||
private final NumberExpr dividend;
|
||||
private final NumberExpr divisor;
|
||||
|
||||
public Divide(NumberExpr dividend, NumberExpr divisor) {
|
||||
public Divide(int character, NumberExpr dividend, NumberExpr divisor) {
|
||||
super(character);
|
||||
this.dividend = dividend;
|
||||
this.divisor = divisor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return dividend.get(branch, dataRoot) / divisor.get(branch, dataRoot);
|
||||
public Double get(Dynamic<?> dataRoot) {
|
||||
return dividend.get(dataRoot) / divisor.get(dataRoot);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,13 @@ import io.gitlab.jfronny.muscript.dynamic.*;
|
|||
public class Invert extends NumberExpr {
|
||||
private final NumberExpr inner;
|
||||
|
||||
public Invert(NumberExpr inner) {
|
||||
public Invert(int character, NumberExpr inner) {
|
||||
super(character);
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return -inner.get(branch, dataRoot);
|
||||
public Double get(Dynamic<?> dataRoot) {
|
||||
return -inner.get(dataRoot);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,14 @@ public class Minus extends NumberExpr {
|
|||
private final NumberExpr minuend;
|
||||
private final NumberExpr subtrahend;
|
||||
|
||||
public Minus(NumberExpr minuend, NumberExpr subtrahend) {
|
||||
public Minus(int character, NumberExpr minuend, NumberExpr subtrahend) {
|
||||
super(character);
|
||||
this.minuend = minuend;
|
||||
this.subtrahend = subtrahend;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return minuend.get(branch, dataRoot) - subtrahend.get(branch, dataRoot);
|
||||
public Double get(Dynamic<?> dataRoot) {
|
||||
return minuend.get(dataRoot) - subtrahend.get(dataRoot);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,14 @@ public class Modulo extends NumberExpr {
|
|||
private final NumberExpr dividend;
|
||||
private final NumberExpr divisor;
|
||||
|
||||
public Modulo(NumberExpr dividend, NumberExpr divisor) {
|
||||
public Modulo(int character, NumberExpr dividend, NumberExpr divisor) {
|
||||
super(character);
|
||||
this.dividend = dividend;
|
||||
this.divisor = divisor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return dividend.get(branch, dataRoot) % divisor.get(branch, dataRoot);
|
||||
public Double get(Dynamic<?> dataRoot) {
|
||||
return dividend.get(dataRoot) % divisor.get(dataRoot);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,14 @@ public class Multiply extends NumberExpr {
|
|||
private final NumberExpr multiplier;
|
||||
private final NumberExpr multiplicand;
|
||||
|
||||
public Multiply(NumberExpr multiplier, NumberExpr multiplicand) {
|
||||
public Multiply(int character, NumberExpr multiplier, NumberExpr multiplicand) {
|
||||
super(character);
|
||||
this.multiplier = multiplier;
|
||||
this.multiplicand = multiplicand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return multiplier.get(branch, dataRoot) * multiplicand.get(branch, dataRoot);
|
||||
public Double get(Dynamic<?> dataRoot) {
|
||||
return multiplier.get(dataRoot) * multiplicand.get(dataRoot);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,14 @@ public class Plus extends NumberExpr {
|
|||
private final NumberExpr augend;
|
||||
private final NumberExpr addend;
|
||||
|
||||
public Plus(NumberExpr augend, NumberExpr addend) {
|
||||
public Plus(int character, NumberExpr augend, NumberExpr addend) {
|
||||
super(character);
|
||||
this.augend = augend;
|
||||
this.addend = addend;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return augend.get(branch, dataRoot) + addend.get(branch, dataRoot);
|
||||
public Double get(Dynamic<?> dataRoot) {
|
||||
return augend.get(dataRoot) + addend.get(dataRoot);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,14 @@ public class Power extends NumberExpr {
|
|||
private final NumberExpr base;
|
||||
private final NumberExpr exponent;
|
||||
|
||||
public Power(NumberExpr base, NumberExpr exponent) {
|
||||
public Power(int character, NumberExpr base, NumberExpr exponent) {
|
||||
super(character);
|
||||
this.base = base;
|
||||
this.exponent = exponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return Math.pow(base.get(branch, dataRoot), exponent.get(branch, dataRoot));
|
||||
public Double get(Dynamic<?> dataRoot) {
|
||||
return Math.pow(base.get(dataRoot), exponent.get(dataRoot));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,14 @@ public class Concatenate extends StringExpr {
|
|||
private final StringExpr left;
|
||||
private final StringExpr right;
|
||||
|
||||
public Concatenate(StringExpr left, StringExpr right) {
|
||||
public Concatenate(int character, StringExpr left, StringExpr right) {
|
||||
super(character);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return left.get(branch, dataRoot) + right.get(branch, dataRoot);
|
||||
public String get(Dynamic<?> dataRoot) {
|
||||
return left.get(dataRoot) + right.get(dataRoot);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,13 @@ import io.gitlab.jfronny.muscript.dynamic.*;
|
|||
public class StringCoerce extends StringExpr {
|
||||
private final Expr<?> inner;
|
||||
|
||||
public StringCoerce(Expr<?> inner) {
|
||||
public StringCoerce(int character, Expr<?> inner) {
|
||||
super(character);
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(Dynamic<?> branch, Dynamic<?> dataRoot) {
|
||||
return StringFormatter.toString(inner.get(branch, dataRoot));
|
||||
public String get(Dynamic<?> dataRoot) {
|
||||
return StringFormatter.toString(inner.get(dataRoot));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,15 +2,18 @@ package io.gitlab.jfronny.muscript.compiler.expr.unresolved;
|
|||
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.*;
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.annotations.*;
|
||||
import io.gitlab.jfronny.muscript.compiler.expr.common.conditional.*;
|
||||
import io.gitlab.jfronny.muscript.dynamic.*;
|
||||
|
||||
@CanThrow
|
||||
public class UnresolvedConditional extends DynamicExpr {
|
||||
private final BoolExpr condition;
|
||||
private final Expr<?> trueExpr;
|
||||
private final Expr<?> falseExpr;
|
||||
|
||||
public UnresolvedConditional(BoolExpr condition, Expr<?> trueExpr, Expr<?> falseExpr) {
|
||||
public UnresolvedConditional(int character, BoolExpr condition, Expr<?> trueExpr, Expr<?> falseExpr) {
|
||||
super(character);
|
||||
this.condition = condition;
|
||||
this.trueExpr = trueExpr;
|
||||
this.falseExpr = falseExpr;
|
||||
|
@ -22,27 +25,27 @@ public class UnresolvedConditional extends DynamicExpr {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Dynamic get(Dynamic branch, Dynamic dataRoot) {
|
||||
public Dynamic get(Dynamic dataRoot) {
|
||||
throw new UnsupportedOperationException("Conditional was kept unresolved. This is not supported!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr asDynamicExpr() {
|
||||
return new DynamicConditional(condition, trueExpr.asDynamicExpr(), falseExpr.asDynamicExpr());
|
||||
return new DynamicConditional(character, condition, trueExpr.asDynamicExpr(), falseExpr.asDynamicExpr());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoolExpr asBoolExpr() {
|
||||
return new BoolConditional(condition, trueExpr.asBoolExpr(), falseExpr.asBoolExpr());
|
||||
return new BoolConditional(character, condition, trueExpr.asBoolExpr(), falseExpr.asBoolExpr());
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringExpr asStringExpr() {
|
||||
return new StringConditional(condition, trueExpr.asStringExpr(), falseExpr.asStringExpr());
|
||||
return new StringConditional(character, condition, trueExpr.asStringExpr(), falseExpr.asStringExpr());
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberExpr asNumberExpr() {
|
||||
return new NumberConditional(condition, trueExpr.asNumberExpr(), falseExpr.asNumberExpr());
|
||||
return new NumberConditional(character, condition, trueExpr.asNumberExpr(), falseExpr.asNumberExpr());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,12 @@ public interface Dynamic<T> {
|
|||
|
||||
default DBool asBool() {
|
||||
if (this instanceof DBool bool) return bool;
|
||||
else throw new IllegalArgumentException("This value is not a bool");
|
||||
else throw new DynamicTypeConversionException("bool");
|
||||
}
|
||||
|
||||
default DNumber asNumber() {
|
||||
if (this instanceof DNumber number) return number;
|
||||
else throw new IllegalArgumentException("This value is not a number");
|
||||
else throw new DynamicTypeConversionException("number");
|
||||
}
|
||||
|
||||
default DString asString() {
|
||||
|
@ -22,16 +22,16 @@ public interface Dynamic<T> {
|
|||
|
||||
default DObject asObject() {
|
||||
if (this instanceof DObject object) return object;
|
||||
else throw new IllegalArgumentException("This value is not an object");
|
||||
else throw new DynamicTypeConversionException("object");
|
||||
}
|
||||
|
||||
default DList asList() {
|
||||
if (this instanceof DList list) return list;
|
||||
else throw new IllegalArgumentException("This value is not a list");
|
||||
else throw new DynamicTypeConversionException("list");
|
||||
}
|
||||
|
||||
default DCallable asCallable() {
|
||||
if (this instanceof DCallable callable) return callable;
|
||||
else throw new IllegalArgumentException("This value is not a callable");
|
||||
else throw new DynamicTypeConversionException("callable");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package io.gitlab.jfronny.muscript.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.muscript.error.*;
|
||||
|
||||
public class DynamicTypeConversionException extends RuntimeException {
|
||||
private final String target;
|
||||
|
||||
public DynamicTypeConversionException(String target) {
|
||||
super("Could not convert dynamic to " + target);
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public LocationalException locational(int character) {
|
||||
return new LocationalException(character, target, this);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
package io.gitlab.jfronny.muscript.compiler;
|
||||
package io.gitlab.jfronny.muscript.error;
|
||||
|
||||
/**
|
||||
* Class for storing errors produced while parsing.
|
||||
* Class for storing errors with code context
|
||||
* Can be generated from a LocationalException with asPrintable
|
||||
*/
|
||||
public record Error(String message, String line, int lineNumber, int column) {
|
||||
public record LocationalError(String message, String line, int lineNumber, int column) {
|
||||
/**
|
||||
* Generates an error by getting the relevant data from what is available while lexing/parsing
|
||||
* @param source The complete source for the code that generated this error
|
||||
|
@ -11,7 +12,7 @@ public record Error(String message, String line, int lineNumber, int column) {
|
|||
* @param message The error message
|
||||
* @return An error using the provided information
|
||||
*/
|
||||
public static Error create(String source, int character, String message) {
|
||||
public static LocationalError create(String source, int character, String message) {
|
||||
int lineStart = source.lastIndexOf('\n', character);
|
||||
int lineEnd = source.indexOf('\n', character);
|
||||
if (lineEnd == -1) lineEnd = source.length();
|
||||
|
@ -19,7 +20,7 @@ public record Error(String message, String line, int lineNumber, int column) {
|
|||
int lineIndex = lineStart > 0 ? (int) source.substring(0, lineStart).chars().filter(c -> c == '\n').count() : 0;
|
||||
int column = character - lineStart;
|
||||
|
||||
return new Error(message, source.substring(lineStart + 1, lineEnd), lineIndex + 1, column);
|
||||
return new LocationalError(message, source.substring(lineStart + 1, lineEnd), lineIndex + 1, column);
|
||||
}
|
||||
|
||||
/**
|
|
@ -0,0 +1,33 @@
|
|||
package io.gitlab.jfronny.muscript.error;
|
||||
|
||||
/**
|
||||
* An exception type with a location
|
||||
* For use in MuScript, can be converted to a pretty LocationalError with asPrintable
|
||||
*/
|
||||
public class LocationalException extends RuntimeException {
|
||||
private final int location;
|
||||
|
||||
public LocationalException(int location) {
|
||||
super();
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public LocationalException(int location, String message) {
|
||||
super(message);
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public LocationalException(int location, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public LocationalException(int location, Throwable cause) {
|
||||
super(cause);
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public LocationalError asPrintable(String source) {
|
||||
return LocationalError.create(source, location, getLocalizedMessage());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package io.gitlab.jfronny.muscript.error;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
/**
|
||||
* An exception that expresses an unexpected type
|
||||
* To be used in expressions
|
||||
*/
|
||||
public class TypeMismatchException extends LocationalException {
|
||||
private final Type expected;
|
||||
private final @Nullable Type actual;
|
||||
|
||||
public TypeMismatchException(int character, Type expected, @Nullable Type actual) {
|
||||
this(character, expected, actual, "Expected " + expected + (actual == null ? "" : " but got " + actual));
|
||||
}
|
||||
|
||||
public TypeMismatchException(int character, Type expected, @Nullable Type actual, String message) {
|
||||
super(character, message);
|
||||
this.expected = expected;
|
||||
this.actual = actual;
|
||||
}
|
||||
}
|
|
@ -5,18 +5,18 @@ import org.junit.jupiter.api.*;
|
|||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class ErrorTest {
|
||||
public class LocationalErrorTest {
|
||||
@Test
|
||||
void invalidCode() {
|
||||
assertEquals("""
|
||||
Error at 'e' (character 9): Expected number but is Boolean
|
||||
Error at 't' (character 6): Expected Number but got Boolean
|
||||
1 | 15 + true
|
||||
^-- Here""",
|
||||
^-- Here""",
|
||||
assertThrows(Parser.ParseException.class, () -> Parser.parse("15 + true")).error.toString());
|
||||
assertEquals("""
|
||||
Error at ''' (character 10): Expected number but is String
|
||||
Error at ''' (character 6): Expected Number but got String
|
||||
1 | 15 + 'yes'
|
||||
^-- Here""",
|
||||
^-- Here""",
|
||||
assertThrows(Parser.ParseException.class, () -> Parser.parse("15 + 'yes'")).error.toString());
|
||||
assertEquals("""
|
||||
Error at '=' (character 8): Unexpected character
|
Loading…
Reference in New Issue