muScript: support for custom objects, script decompiling and Dynamic serialization
This commit is contained in:
parent
cfbc10387f
commit
efb1512a60
@ -2,7 +2,7 @@ plugins {
|
||||
id("commons.library")
|
||||
}
|
||||
|
||||
version = "1.1-SNAPSHOT"
|
||||
version = "1.2-SNAPSHOT"
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
|
@ -2,14 +2,14 @@ package io.gitlab.jfronny.muscript;
|
||||
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static io.gitlab.jfronny.muscript.data.dynamic.DFinal.of;
|
||||
import static io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal.of;
|
||||
|
||||
public class StandardLib {
|
||||
private static final Random rnd = new Random();
|
||||
@ -32,7 +32,7 @@ public class StandardLib {
|
||||
int a1 = args.get(1).asNumber().getValue().intValue();
|
||||
int a2 = args.get(2).asNumber().getValue().intValue();
|
||||
return new DDate(() -> LocalDate.of(a0, a1, a2));
|
||||
})))
|
||||
}, () -> "date")))
|
||||
.set("time", new DCallableObject(Map.of(
|
||||
"now", new DTime(LocalTime::now)
|
||||
), DFinal.of(args -> {
|
||||
@ -43,7 +43,7 @@ public class StandardLib {
|
||||
int a1 = args.get(1).asNumber().getValue().intValue();
|
||||
int a2 = args.get(2).asNumber().getValue().intValue();
|
||||
return new DTime(() -> LocalTime.of(a0, a1, a2));
|
||||
})))
|
||||
}, () -> "time")))
|
||||
|
||||
.set("round", StandardLib::round)
|
||||
.set("floor", StandardLib::floor)
|
||||
@ -64,59 +64,10 @@ public class StandardLib {
|
||||
.set("map", StandardLib::map)
|
||||
.set("flatMap", StandardLib::flatMap)
|
||||
.set("fold", StandardLib::fold)
|
||||
.set("forEach", StandardLib::forEach);
|
||||
}
|
||||
.set("forEach", StandardLib::forEach)
|
||||
|
||||
private record DDate(Supplier<LocalDate> date) implements DObject {
|
||||
@Override
|
||||
public Map<String, Dynamic<?>> getValue() {
|
||||
return Map.of(
|
||||
"year", DFinal.of(date.get().getYear()),
|
||||
"month", DFinal.of(date.get().getMonthValue()),
|
||||
"day", DFinal.of(date.get().getDayOfMonth())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DString asString() {
|
||||
return DFinal.of(toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DNumber asNumber() {
|
||||
return DFinal.of(date.get().toEpochDay());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return date.get().toString();
|
||||
}
|
||||
}
|
||||
|
||||
private record DTime(Supplier<LocalTime> time) implements DObject {
|
||||
@Override
|
||||
public Map<String, Dynamic<?>> getValue() {
|
||||
return Map.of(
|
||||
"hour", DFinal.of(time.get().getHour()),
|
||||
"minute", DFinal.of(time.get().getMinute()),
|
||||
"second", DFinal.of(time.get().getSecond())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DString asString() {
|
||||
return DFinal.of(toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DNumber asNumber() {
|
||||
return DFinal.of(time.get().toSecondOfDay());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return time.get().toString();
|
||||
}
|
||||
.set("callableObject", StandardLib::callableObject)
|
||||
.set("enum", StandardLib::enum_);
|
||||
}
|
||||
|
||||
// Numbers
|
||||
@ -199,29 +150,29 @@ public class StandardLib {
|
||||
}
|
||||
|
||||
public static DList filter(DList args) {
|
||||
if (args.size() != 2) throw new IllegalArgumentException("Invalid number of arguments for filter: expected at least 2 but got " + args.size());
|
||||
if (args.size() != 2) throw new IllegalArgumentException("Invalid number of arguments for filter: expected 2 but got " + args.size());
|
||||
DCallable dc = args.get(1).asCallable();
|
||||
return of(args.get(0).asList().getValue().stream().filter(a -> dc.call(a).asBool().getValue()).toList());
|
||||
}
|
||||
|
||||
public static DList map(DList args) {
|
||||
if (args.size() != 2) throw new IllegalArgumentException("Invalid number of arguments for map: expected at least 2 but got " + args.size());
|
||||
if (args.size() != 2) throw new IllegalArgumentException("Invalid number of arguments for map: expected 2 but got " + args.size());
|
||||
return of(args.get(0).asList().getValue().stream().<Dynamic<?>>map(args.get(1).asCallable()::call).toList());
|
||||
}
|
||||
|
||||
public static DList flatMap(DList args) {
|
||||
if (args.size() != 2) throw new IllegalArgumentException("Invalid number of arguments for flatMap: expected at least 2 but got " + args.size());
|
||||
if (args.size() != 2) throw new IllegalArgumentException("Invalid number of arguments for flatMap: expected 2 but got " + args.size());
|
||||
DCallable dc = args.get(1).asCallable();
|
||||
return of(args.get(0).asList().getValue().stream().flatMap(a -> dc.call(a).asList().getValue().stream()).toList());
|
||||
}
|
||||
|
||||
public static Dynamic<?> fold(DList args) {
|
||||
if (args.size() != 3) throw new IllegalArgumentException("Invalid number of arguments for fold: expected at least 3 but got " + args.size());
|
||||
if (args.size() != 3) throw new IllegalArgumentException("Invalid number of arguments for fold: expected 3 but got " + args.size());
|
||||
return args.get(0).asList().getValue().stream().reduce(args.get(1), args.get(2).asCallable()::call);
|
||||
}
|
||||
|
||||
public static Dynamic<?> forEach(DList args) {
|
||||
if (args.size() != 2) throw new IllegalArgumentException("Invalid number of arguments for forEach: expected at least 2 but got " + args.size());
|
||||
if (args.size() != 2) throw new IllegalArgumentException("Invalid number of arguments for forEach: expected 2 but got " + args.size());
|
||||
Dynamic<?> result = new DNull();
|
||||
DCallable dc = args.get(1).asCallable();
|
||||
for (Dynamic<?> dynamic : args.get(0).asList().getValue()) {
|
||||
@ -229,4 +180,16 @@ public class StandardLib {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Objects
|
||||
public static DCallableObject callableObject(DList args) {
|
||||
if (args.size() != 2) throw new IllegalArgumentException("Invalid number of arguments for callableObject: expected 2 but got " + args.size());
|
||||
return new DCallableObject(args.get(0).asObject().getValue(), args.get(1).asCallable());
|
||||
}
|
||||
|
||||
public static DEnum enum_(DList args) {
|
||||
if (args.size() == 1) return new DEnum(args.get(0).asObject().getValue());
|
||||
else if (args.size() == 2) return new DEnum(args.get(0).asObject().getValue(), args.get(1).asString().getValue());
|
||||
else throw new IllegalArgumentException("Invalid number of arguments for enum: expected 1 or 2 but got " + args.size());
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
package io.gitlab.jfronny.muscript;
|
||||
|
||||
import io.gitlab.jfronny.commons.log.Logger;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
import io.gitlab.jfronny.muscript.compiler.Decompilable;
|
||||
|
||||
public class StarScriptIngester {
|
||||
private static final Logger LOGGER = Logger.forName("MuScript");
|
||||
@ -21,7 +19,7 @@ public class StarScriptIngester {
|
||||
if (c == '{') {
|
||||
if (!currbuf.isEmpty()) {
|
||||
if (!result.isEmpty()) result.append(" || ");
|
||||
result.append(enquote(currbuf.toString()));
|
||||
result.append(Decompilable.enquote(currbuf.toString()));
|
||||
}
|
||||
currbuf = new StringBuilder();
|
||||
state = State.UnquotedInner;
|
||||
@ -61,7 +59,7 @@ public class StarScriptIngester {
|
||||
if (!currbuf.isEmpty() && !result.isEmpty()) result.append(" || ");
|
||||
switch (state) {
|
||||
case Surrounding -> {
|
||||
if (!currbuf.isEmpty()) result.append(enquote(currbuf.toString()));
|
||||
if (!currbuf.isEmpty()) result.append(Decompilable.enquote(currbuf.toString()));
|
||||
}
|
||||
case UnquotedInner -> {
|
||||
LOGGER.warn("Starscript code segment improperly closed, closing automatically");
|
||||
@ -79,16 +77,6 @@ public class StarScriptIngester {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates quotes around a string, supports strings containing quotes
|
||||
*/
|
||||
private static String enquote(String literalText) {
|
||||
if (!literalText.contains("'")) return "'" + literalText + "'";
|
||||
if (!literalText.contains("\"")) return "\"" + literalText + "\"";
|
||||
return Arrays.stream(literalText.split("'")).map(s -> "'" + s + "'")
|
||||
.collect(Collectors.joining(" || \"'\" || "));
|
||||
}
|
||||
|
||||
enum State {
|
||||
Surrounding, UnquotedInner, SingleQuoteInner, DoubleQuoteInner
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
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;
|
||||
|
||||
public abstract non-sealed class BoolExpr extends Expr<Boolean> {
|
||||
protected BoolExpr(int chStart, int chEnd) {
|
||||
super(chStart, chEnd);
|
||||
protected BoolExpr(Order order, int chStart, int chEnd) {
|
||||
super(order, chStart, chEnd);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,12 +1,13 @@
|
||||
package io.gitlab.jfronny.muscript.ast;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.Order;
|
||||
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(int chStart, int chEnd) {
|
||||
super(chStart, chEnd);
|
||||
protected DynamicExpr(Order order, int chStart, int chEnd) {
|
||||
super(order, chStart, chEnd);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,17 +1,21 @@
|
||||
package io.gitlab.jfronny.muscript.ast;
|
||||
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.annotations.CanThrow;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.*;
|
||||
import io.gitlab.jfronny.muscript.ast.string.StringCoerce;
|
||||
import io.gitlab.jfronny.muscript.compiler.Type;
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DObject;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
import io.gitlab.jfronny.muscript.error.TypeMismatchException;
|
||||
|
||||
@CanThrow
|
||||
public abstract sealed class Expr<T> permits BoolExpr, DynamicExpr, NullLiteral, NumberExpr, StringExpr {
|
||||
protected Expr(int chStart, int chEnd) {
|
||||
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) {
|
||||
super(order);
|
||||
this.chStart = chStart;
|
||||
this.chEnd = chEnd;
|
||||
}
|
||||
@ -26,8 +30,6 @@ public abstract sealed class Expr<T> permits BoolExpr, DynamicExpr, NullLiteral,
|
||||
return get(dataRoot.asObject());
|
||||
}
|
||||
public abstract Expr<T> optimize();
|
||||
public final int chStart;
|
||||
public final int chEnd;
|
||||
|
||||
public BoolExpr asBoolExpr() {
|
||||
if (this instanceof BoolExpr e) return e;
|
||||
|
@ -1,16 +1,18 @@
|
||||
package io.gitlab.jfronny.muscript.ast;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DNull;
|
||||
import io.gitlab.jfronny.muscript.annotations.CanThrow;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.DynamicLiteral;
|
||||
import io.gitlab.jfronny.muscript.compiler.Type;
|
||||
import io.gitlab.jfronny.muscript.error.LocationalException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@CanThrow
|
||||
public final class NullLiteral extends Expr<Object> {
|
||||
public NullLiteral(int chStart, int chEnd) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Primary, chStart, chEnd);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -28,6 +30,11 @@ public final class NullLiteral extends Expr<Object> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
writer.append("null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr asDynamicExpr() {
|
||||
return new DynamicLiteral<>(chStart, chEnd, new DNull());
|
||||
|
@ -1,11 +1,12 @@
|
||||
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;
|
||||
|
||||
public abstract non-sealed class NumberExpr extends Expr<Double> {
|
||||
protected NumberExpr(int chStart, int chEnd) {
|
||||
super(chStart, chEnd);
|
||||
protected NumberExpr(Order order, int chStart, int chEnd) {
|
||||
super(order, chStart, chEnd);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,11 +1,12 @@
|
||||
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;
|
||||
|
||||
public abstract non-sealed class StringExpr extends Expr<String> {
|
||||
protected StringExpr(int chStart, int chEnd) {
|
||||
super(chStart, chEnd);
|
||||
protected StringExpr(Order order, int chStart, int chEnd) {
|
||||
super(order, chStart, chEnd);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,16 +1,20 @@
|
||||
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.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.BoolExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class And extends BoolExpr {
|
||||
private final BoolExpr left;
|
||||
private final BoolExpr right;
|
||||
|
||||
public And(int chStart, int chEnd, BoolExpr left, BoolExpr right) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.And, chStart, chEnd);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
@ -29,6 +33,13 @@ public class And extends BoolExpr {
|
||||
return new And(chStart, chEnd, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
parenthesize(left, writer, false);
|
||||
writer.append(" & ");
|
||||
parenthesize(right, writer, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof And and && left.equals(and.left) && right.equals(and.right);
|
||||
|
@ -1,15 +1,22 @@
|
||||
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.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.BoolExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Not extends BoolExpr {
|
||||
private final BoolExpr inner;
|
||||
|
||||
public Not(int chStart, int chEnd, BoolExpr inner) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Unary, chStart, chEnd);
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@ -26,6 +33,28 @@ public class Not extends BoolExpr {
|
||||
return new Not(chStart, chEnd, inner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
if (inner instanceof Equal eq) {
|
||||
parenthesize(eq.left, writer, false);
|
||||
writer.append(" != ");
|
||||
parenthesize(eq.right, writer, true);
|
||||
} else if (inner instanceof Greater gt) {
|
||||
if (gt.left instanceof NumberLiteral && !(gt.right instanceof NumberLiteral)) {
|
||||
parenthesize(gt.right, writer, false);
|
||||
writer.append(" >= ");
|
||||
parenthesize(gt.left, writer, true);
|
||||
} else {
|
||||
parenthesize(gt.left, writer, false);
|
||||
writer.append(" <= ");
|
||||
parenthesize(gt.right, writer, true);
|
||||
}
|
||||
} else {
|
||||
writer.append("!");
|
||||
parenthesize(inner, writer, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Not not && inner.equals(not.inner);
|
||||
|
@ -1,16 +1,20 @@
|
||||
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.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.BoolExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Or extends BoolExpr {
|
||||
private final BoolExpr left;
|
||||
private final BoolExpr right;
|
||||
|
||||
public Or(int chStart, int chEnd, BoolExpr left, BoolExpr right) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Or, chStart, chEnd);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
@ -29,6 +33,13 @@ public class Or extends BoolExpr {
|
||||
return new Or(chStart, chEnd, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
parenthesize(left, writer, false);
|
||||
writer.append(" | ");
|
||||
parenthesize(right, writer, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Or or && left.equals(or.left) && right.equals(or.right);
|
||||
|
@ -1,18 +1,21 @@
|
||||
package io.gitlab.jfronny.muscript.ast.compare;
|
||||
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
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.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Equal extends BoolExpr {
|
||||
private final Expr<?> left;
|
||||
private final Expr<?> right;
|
||||
public final Expr<?> left;
|
||||
public final Expr<?> right;
|
||||
|
||||
public Equal(int chStart, int chEnd, Expr<?> left, Expr<?> right) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Equality, chStart, chEnd);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
@ -35,6 +38,13 @@ public class Equal extends BoolExpr {
|
||||
return new Equal(chStart, chEnd, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
parenthesize(left, writer, false);
|
||||
writer.append(" == ");
|
||||
parenthesize(right, writer, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Equal equal && left.equals(equal.left) && right.equals(equal.right);
|
||||
|
@ -1,16 +1,20 @@
|
||||
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.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.*;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
import io.gitlab.jfronny.muscript.ast.math.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Greater extends BoolExpr {
|
||||
private final NumberExpr left;
|
||||
private final NumberExpr right;
|
||||
public final NumberExpr left;
|
||||
public final NumberExpr right;
|
||||
|
||||
public Greater(int chStart, int chEnd, NumberExpr left, NumberExpr right) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Comparison, chStart, chEnd);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
@ -29,7 +33,7 @@ public class Greater extends BoolExpr {
|
||||
if (left instanceof Divide divide)
|
||||
return new Greater(chStart, chEnd, divide.dividend, new Multiply(divide.chStart, divide.chEnd, right, divide.divisor)).optimize();
|
||||
if (left instanceof Invert invert)
|
||||
return new Less(chStart, chEnd, invert.inner, new Invert(right.chStart, right.chEnd, right)).optimize();
|
||||
return new Greater(chStart, chEnd, new Invert(right.chStart, right.chEnd, 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();
|
||||
// Modulo is left out because it is too complicated for this naive impl
|
||||
@ -40,6 +44,19 @@ public class Greater extends BoolExpr {
|
||||
return new Greater(chStart, chEnd, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
if (left instanceof NumberLiteral && !(right instanceof NumberLiteral)) {
|
||||
parenthesize(right, writer, false);
|
||||
writer.append(" < ");
|
||||
parenthesize(left, writer, true);
|
||||
} else {
|
||||
parenthesize(left, writer, false);
|
||||
writer.append(" > ");
|
||||
parenthesize(right, writer, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Greater greater && left.equals(greater.left) && right.equals(greater.right);
|
||||
|
@ -1,47 +0,0 @@
|
||||
package io.gitlab.jfronny.muscript.ast.compare;
|
||||
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.*;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
import io.gitlab.jfronny.muscript.ast.math.*;
|
||||
|
||||
public class Less extends BoolExpr {
|
||||
private final NumberExpr left;
|
||||
private final NumberExpr right;
|
||||
|
||||
public Less(int chStart, int chEnd, NumberExpr left, NumberExpr right) {
|
||||
super(chStart, chEnd);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get(Scope dataRoot) {
|
||||
return left.get(dataRoot) < right.get(dataRoot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoolExpr optimize() {
|
||||
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);
|
||||
if (left instanceof Divide divide)
|
||||
return new Less(chStart, chEnd, divide.dividend, new Multiply(divide.chStart, divide.chEnd, divide.divisor, right)).optimize();
|
||||
if (left instanceof Invert invert)
|
||||
return new Greater(chStart, chEnd, invert.inner, new Invert(right.chStart, right.chEnd, right)).optimize();
|
||||
if (left instanceof Minus minus)
|
||||
return new Less(chStart, chEnd, minus.minuend, new Plus(minus.chStart, minus.chEnd, 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 Less(chStart, chEnd, plus.augend, new Minus(plus.chStart, plus.chEnd, plus.addend, right)).optimize();
|
||||
// Power is left out because it can't be transformed cleanly either
|
||||
return new Less(chStart, chEnd, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Less less && left.equals(less.left) && right.equals(less.right);
|
||||
}
|
||||
}
|
@ -1,16 +1,20 @@
|
||||
package io.gitlab.jfronny.muscript.ast.conditional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.compiler.Order;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.BoolExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class BoolConditional extends BoolExpr {
|
||||
public final BoolExpr condition;
|
||||
public final BoolExpr trueExpr;
|
||||
public final BoolExpr falseExpr;
|
||||
|
||||
public BoolConditional(int chStart, int chEnd, BoolExpr condition, BoolExpr trueExpr, BoolExpr falseExpr) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Conditional, chStart, chEnd);
|
||||
this.condition = condition;
|
||||
this.trueExpr = trueExpr;
|
||||
this.falseExpr = falseExpr;
|
||||
@ -31,6 +35,15 @@ public class BoolConditional extends BoolExpr {
|
||||
return new BoolConditional(chStart, chEnd, condition, trueExpr, falseExpr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
parenthesize(condition, writer, true);
|
||||
writer.append(" ? ");
|
||||
trueExpr.decompile(writer);
|
||||
writer.append(" : ");
|
||||
falseExpr.decompile(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof BoolConditional conditional
|
||||
|
@ -1,18 +1,22 @@
|
||||
package io.gitlab.jfronny.muscript.ast.conditional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.compiler.Order;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
import io.gitlab.jfronny.muscript.ast.BoolExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DynamicConditional extends DynamicExpr {
|
||||
public final BoolExpr condition;
|
||||
public final DynamicExpr trueExpr;
|
||||
public final DynamicExpr falseExpr;
|
||||
|
||||
public DynamicConditional(int chStart, int chEnd, BoolExpr condition, DynamicExpr trueExpr, DynamicExpr falseExpr) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Conditional, chStart, chEnd);
|
||||
this.condition = condition;
|
||||
this.trueExpr = trueExpr;
|
||||
this.falseExpr = falseExpr;
|
||||
@ -33,6 +37,15 @@ public class DynamicConditional extends DynamicExpr {
|
||||
return new DynamicConditional(chStart, chEnd, condition, trueExpr, falseExpr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
parenthesize(condition, writer, true);
|
||||
writer.append(" ? ");
|
||||
trueExpr.decompile(writer);
|
||||
writer.append(" : ");
|
||||
falseExpr.decompile(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof DynamicConditional conditional
|
||||
|
@ -1,17 +1,21 @@
|
||||
package io.gitlab.jfronny.muscript.ast.conditional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.compiler.Order;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.BoolExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.NumberExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class NumberConditional extends NumberExpr {
|
||||
public final BoolExpr condition;
|
||||
public final NumberExpr trueExpr;
|
||||
public final NumberExpr falseExpr;
|
||||
|
||||
public NumberConditional(int chStart, int chEnd, BoolExpr condition, NumberExpr trueExpr, NumberExpr falseExpr) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Conditional, chStart, chEnd);
|
||||
this.condition = condition;
|
||||
this.trueExpr = trueExpr;
|
||||
this.falseExpr = falseExpr;
|
||||
@ -32,6 +36,15 @@ public class NumberConditional extends NumberExpr {
|
||||
return new NumberConditional(chStart, chEnd, condition, trueExpr, falseExpr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
parenthesize(condition, writer, true);
|
||||
writer.append(" ? ");
|
||||
trueExpr.decompile(writer);
|
||||
writer.append(" : ");
|
||||
falseExpr.decompile(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof NumberConditional conditional
|
||||
|
@ -1,17 +1,21 @@
|
||||
package io.gitlab.jfronny.muscript.ast.conditional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.compiler.Order;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.BoolExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.StringExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class StringConditional extends StringExpr {
|
||||
public final BoolExpr condition;
|
||||
public final StringExpr trueExpr;
|
||||
public final StringExpr falseExpr;
|
||||
|
||||
public StringConditional(int chStart, int chEnd, BoolExpr condition, StringExpr trueExpr, StringExpr falseExpr) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Conditional, chStart, chEnd);
|
||||
this.condition = condition;
|
||||
this.trueExpr = trueExpr;
|
||||
this.falseExpr = falseExpr;
|
||||
@ -32,6 +36,15 @@ public class StringConditional extends StringExpr {
|
||||
return new StringConditional(chStart, chEnd, condition, trueExpr, falseExpr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
parenthesize(condition, writer, true);
|
||||
writer.append(" ? ");
|
||||
trueExpr.decompile(writer);
|
||||
writer.append(" : ");
|
||||
falseExpr.decompile(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof StringConditional conditional
|
||||
|
@ -1,12 +1,14 @@
|
||||
package io.gitlab.jfronny.muscript.ast.conditional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DNull;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
import io.gitlab.jfronny.muscript.ast.*;
|
||||
import io.gitlab.jfronny.muscript.annotations.CanThrow;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
|
||||
import io.gitlab.jfronny.muscript.compiler.Type;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@CanThrow
|
||||
public class UnresolvedConditional extends DynamicExpr {
|
||||
@ -15,7 +17,7 @@ public class UnresolvedConditional extends DynamicExpr {
|
||||
private final Expr<?> falseExpr;
|
||||
|
||||
public UnresolvedConditional(int chStart, int chEnd, BoolExpr condition, Expr<?> trueExpr, Expr<?> falseExpr) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Conditional, chStart, chEnd);
|
||||
this.condition = condition;
|
||||
this.trueExpr = trueExpr;
|
||||
this.falseExpr = falseExpr;
|
||||
@ -36,6 +38,15 @@ public class UnresolvedConditional extends DynamicExpr {
|
||||
return new UnresolvedConditional(chStart, chEnd, condition, trueExpr, falseExpr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
parenthesize(condition, writer, true);
|
||||
writer.append(" ? ");
|
||||
trueExpr.decompile(writer);
|
||||
writer.append(" : ");
|
||||
falseExpr.decompile(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic<?> get(Scope dataRoot) {
|
||||
// unresolved conditionals may exist as root elements in scripts/closures
|
||||
|
@ -1,16 +1,19 @@
|
||||
package io.gitlab.jfronny.muscript.ast.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
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.error.LocationalException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Assign extends DynamicExpr {
|
||||
private final String name;
|
||||
private final DynamicExpr value;
|
||||
|
||||
public Assign(int chStart, int chEnd, String name, DynamicExpr value) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Primary, chStart, chEnd);
|
||||
if (name.equals("this")) throw new LocationalException(chStart, chEnd, "Cannot reassign 'this'");
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
@ -28,6 +31,13 @@ public class Assign extends DynamicExpr {
|
||||
return new Assign(chStart, chEnd, name, value.optimize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
if (!Lexer.isValidId(name)) throw new IllegalArgumentException("Not a valid variable name: " + name);
|
||||
writer.append(name).append(" = ");
|
||||
value.decompile(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Assign assign
|
||||
|
@ -1,10 +1,12 @@
|
||||
package io.gitlab.jfronny.muscript.ast.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DFinal;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@ -13,7 +15,7 @@ public class Bind extends DynamicExpr {
|
||||
public final DynamicExpr parameter;
|
||||
|
||||
public Bind(int chStart, int chEnd, DynamicExpr callable, DynamicExpr parameter) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Call, chStart, chEnd);
|
||||
this.callable = callable;
|
||||
this.parameter = parameter;
|
||||
}
|
||||
@ -27,7 +29,20 @@ public class Bind extends DynamicExpr {
|
||||
.asCallable()
|
||||
.getValue()
|
||||
.apply(DFinal.of(argsWithParameter));
|
||||
});
|
||||
}, this::toString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
parenthesize(parameter, writer, false);
|
||||
writer.append("::");
|
||||
if (callable instanceof Variable) {
|
||||
callable.decompile(writer);
|
||||
} else {
|
||||
writer.append('(');
|
||||
callable.decompile(writer);
|
||||
writer.append(')');
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -3,10 +3,14 @@ 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.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
import io.gitlab.jfronny.muscript.error.LocationalException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@ -17,7 +21,7 @@ public class Call extends DynamicExpr {
|
||||
private final List<Arg> args;
|
||||
|
||||
public Call(int chStart, int chEnd, DynamicExpr left, List<Arg> args) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Call, chStart, chEnd);
|
||||
this.left = left;
|
||||
this.args = args;
|
||||
}
|
||||
@ -48,6 +52,33 @@ public class Call extends DynamicExpr {
|
||||
return new Call(chStart, chEnd, left, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
parenthesize(left, writer, false);
|
||||
if (args.size() > 3) {
|
||||
writer.increaseIndent();
|
||||
writer.append("(\n");
|
||||
boolean first = true;
|
||||
for (Arg arg : args) {
|
||||
if (!first) writer.append(",\n");
|
||||
first = false;
|
||||
arg.expr.decompile(writer);
|
||||
if (arg.variadic) writer.append("...");
|
||||
}
|
||||
writer.decreaseIndent();
|
||||
writer.append("\n)");
|
||||
} else {
|
||||
writer.append("(");
|
||||
boolean first = true;
|
||||
for (Arg arg : args) {
|
||||
if (!first) writer.append(", ");
|
||||
first = false;
|
||||
arg.expr.decompile(writer);
|
||||
if (arg.variadic) writer.append("...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Call call && left.equals(call.left) && args.equals(call.args);
|
||||
|
@ -2,12 +2,16 @@ package io.gitlab.jfronny.muscript.ast.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
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;
|
||||
import io.gitlab.jfronny.muscript.error.LocationalException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Closure extends DynamicExpr {
|
||||
private final List<String> boundArgs;
|
||||
@ -20,7 +24,7 @@ public class Closure extends DynamicExpr {
|
||||
}
|
||||
|
||||
private Closure(int chStart, int chEnd, List<String> boundArgs, List<Expr<?>> steps, DynamicExpr fin, boolean variadic) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Primary, chStart, chEnd);
|
||||
this.boundArgs = List.copyOf(boundArgs);
|
||||
this.steps = List.copyOf(steps);
|
||||
this.fin = fin;
|
||||
@ -46,7 +50,7 @@ public class Closure extends DynamicExpr {
|
||||
step.get(fork);
|
||||
}
|
||||
return fin.get(fork);
|
||||
});
|
||||
}, this::toString);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -55,6 +59,25 @@ public class Closure extends DynamicExpr {
|
||||
return new Closure(chStart, chEnd, boundArgs, steps.stream().<Expr<?>>map(Expr::optimize).toList(), fin.optimize(), variadic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
writer.append("{ ");
|
||||
for (int i = 0; i < boundArgs.size(); i++) {
|
||||
String arg = boundArgs.get(i);
|
||||
if (!Lexer.isValidId(arg)) throw new IllegalArgumentException("Not a valid argument name: " + arg);
|
||||
writer.append(arg);
|
||||
if (i == boundArgs.size() - 1 && variadic) writer.append("...");
|
||||
writer.append(' ');
|
||||
}
|
||||
writer.append("->").increaseIndent();
|
||||
for (Expr<?> expr : stream().toList()) {
|
||||
writer.append("\n");
|
||||
expr.decompile(writer);
|
||||
writer.append(";");
|
||||
}
|
||||
writer.decreaseIndent().append("\n}");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Closure closure
|
||||
@ -62,4 +85,8 @@ public class Closure extends DynamicExpr {
|
||||
&& steps.equals(closure.steps)
|
||||
&& fin.equals(closure.fin);
|
||||
}
|
||||
|
||||
public Stream<Expr<?>> stream() {
|
||||
return Stream.concat(steps.stream(), Stream.of(fin));
|
||||
}
|
||||
}
|
||||
|
@ -2,14 +2,18 @@ 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.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DynamicCoerce extends DynamicExpr {
|
||||
public final Expr<?> inner;
|
||||
|
||||
public DynamicCoerce(int chStart, int chEnd, Expr<?> inner) {
|
||||
super(chStart, chEnd);
|
||||
super(inner.order, chStart, chEnd);
|
||||
this.inner = inner;
|
||||
if (!(inner instanceof DynamicExpr)
|
||||
&& !(inner instanceof BoolExpr)
|
||||
@ -39,6 +43,11 @@ public class DynamicCoerce extends DynamicExpr {
|
||||
return new DynamicCoerce(chStart, chEnd, inner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
inner.decompile(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof DynamicCoerce coerce && inner.equals(coerce.inner);
|
||||
|
@ -4,11 +4,14 @@ import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.annotations.CanThrow;
|
||||
import io.gitlab.jfronny.muscript.annotations.UncheckedDynamic;
|
||||
import io.gitlab.jfronny.muscript.compiler.Type;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.StringLiteral;
|
||||
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.error.TypeMismatchException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@CanThrow
|
||||
@UncheckedDynamic
|
||||
public class Get extends DynamicExpr {
|
||||
@ -16,7 +19,7 @@ public class Get extends DynamicExpr {
|
||||
private final Expr<?> name;
|
||||
|
||||
public Get(int chStart, int chEnd, DynamicExpr left, Expr<?> name) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Call, chStart, chEnd);
|
||||
this.left = left;
|
||||
this.name = name;
|
||||
if (name.getResultType() != Type.String && name.getResultType() != Type.Number && name.getResultType() != Type.Dynamic) {
|
||||
@ -42,6 +45,18 @@ public class Get extends DynamicExpr {
|
||||
return new Get(chStart, chEnd, left, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
parenthesize(left, writer, false);
|
||||
if (name instanceof StringLiteral lit && Lexer.isValidId(lit.value)) {
|
||||
writer.append('.').append(lit.value);
|
||||
} else {
|
||||
writer.append('[');
|
||||
name.decompile(writer);
|
||||
writer.append(']');
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Get get && left.equals(get.left) && name.equals(get.name);
|
||||
|
@ -0,0 +1,57 @@
|
||||
package io.gitlab.jfronny.muscript.ast.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.DynamicLiteral;
|
||||
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.data.dynamic.additional.DFinal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ObjectLiteral extends DynamicExpr {
|
||||
private final Map<String, DynamicExpr> content;
|
||||
|
||||
public ObjectLiteral(int chStart, int chEnd, Map<String, DynamicExpr> content) {
|
||||
super(Order.Primary, chStart, chEnd);
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic<?> get(Scope dataRoot) {
|
||||
Map<String, Dynamic<?>> result = new LinkedHashMap<>();
|
||||
this.content.forEach((k, v) -> result.put(k, v.get(dataRoot)));
|
||||
return DFinal.of(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr optimize() {
|
||||
Map<String, DynamicExpr> content = new LinkedHashMap<>();
|
||||
Map<String, Dynamic<?>> literalContent = new LinkedHashMap<>();
|
||||
boolean literal = true;
|
||||
for (Map.Entry<String, DynamicExpr> entry : this.content.entrySet()) {
|
||||
DynamicExpr de = entry.getValue().optimize();
|
||||
if (de instanceof DynamicLiteral<?> dl && literal) literalContent.put(entry.getKey(), dl.value);
|
||||
else literal = false;
|
||||
content.put(entry.getKey(), de);
|
||||
}
|
||||
if (literal) return new DynamicLiteral<>(chStart, chEnd, DFinal.of(literalContent));
|
||||
return new ObjectLiteral(chStart, chEnd, content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
writer.increaseIndent().append("{\n");
|
||||
boolean first = true;
|
||||
for (Map.Entry<String, DynamicExpr> entry : content.entrySet()) {
|
||||
if (!Lexer.isValidId(entry.getKey())) throw new IllegalStateException("Illegal key: " + entry.getKey());
|
||||
if (!first) writer.append(",\n");
|
||||
first = false;
|
||||
writer.append(entry.getKey()).append(" = ");
|
||||
entry.getValue().decompile(writer);
|
||||
}
|
||||
writer.decreaseIndent().append("\n}");
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package io.gitlab.jfronny.muscript.ast.dynamic;
|
||||
|
||||
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;
|
||||
@ -7,13 +8,15 @@ import io.gitlab.jfronny.muscript.annotations.CanThrow;
|
||||
import io.gitlab.jfronny.muscript.annotations.UncheckedDynamic;
|
||||
import io.gitlab.jfronny.muscript.error.LocationalException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@CanThrow
|
||||
@UncheckedDynamic
|
||||
public class Variable extends DynamicExpr {
|
||||
private final String name;
|
||||
public final String name;
|
||||
|
||||
public Variable(int chStart, int chEnd, String name) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Primary, chStart, chEnd);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@ -29,6 +32,12 @@ public class Variable extends DynamicExpr {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
if (!Lexer.isValidId(name)) throw new IllegalArgumentException("Not a valid variable name: " + name);
|
||||
writer.append(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Variable variable && name.equals(variable.name);
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.gitlab.jfronny.muscript.ast.dynamic.unpack;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DynamicTypeConversionException;
|
||||
import io.gitlab.jfronny.muscript.ast.BoolExpr;
|
||||
@ -8,13 +9,15 @@ import io.gitlab.jfronny.muscript.annotations.CanThrow;
|
||||
import io.gitlab.jfronny.muscript.annotations.UncheckedDynamic;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.DynamicCoerce;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@CanThrow
|
||||
@UncheckedDynamic
|
||||
public class BoolUnpack extends BoolExpr {
|
||||
public final DynamicExpr inner;
|
||||
|
||||
public BoolUnpack(int chStart, int chEnd, DynamicExpr inner) {
|
||||
super(chStart, chEnd);
|
||||
super(inner.order, chStart, chEnd);
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@ -34,6 +37,11 @@ public class BoolUnpack extends BoolExpr {
|
||||
return new BoolUnpack(chStart, chEnd, inner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
inner.decompile(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof BoolUnpack unpack && inner.equals(unpack.inner);
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.gitlab.jfronny.muscript.ast.dynamic.unpack;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DynamicTypeConversionException;
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
@ -8,13 +9,15 @@ import io.gitlab.jfronny.muscript.annotations.CanThrow;
|
||||
import io.gitlab.jfronny.muscript.annotations.UncheckedDynamic;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.DynamicCoerce;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@CanThrow
|
||||
@UncheckedDynamic
|
||||
public class NumberUnpack extends NumberExpr {
|
||||
public final DynamicExpr inner;
|
||||
|
||||
public NumberUnpack(int chStart, int chEnd, DynamicExpr inner) {
|
||||
super(chStart, chEnd);
|
||||
super(inner.order, chStart, chEnd);
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@ -34,6 +37,11 @@ public class NumberUnpack extends NumberExpr {
|
||||
return new NumberUnpack(chStart, chEnd, inner.optimize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
inner.decompile(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof NumberUnpack unpack && inner.equals(unpack.inner);
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.gitlab.jfronny.muscript.ast.dynamic.unpack;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DynamicTypeConversionException;
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
@ -8,13 +9,15 @@ import io.gitlab.jfronny.muscript.annotations.CanThrow;
|
||||
import io.gitlab.jfronny.muscript.annotations.UncheckedDynamic;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.DynamicCoerce;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@CanThrow
|
||||
@UncheckedDynamic
|
||||
public class StringUnpack extends StringExpr {
|
||||
public final DynamicExpr inner;
|
||||
|
||||
public StringUnpack(int chStart, int chEnd, DynamicExpr inner) {
|
||||
super(chStart, chEnd);
|
||||
super(inner.order, chStart, chEnd);
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@ -34,6 +37,11 @@ public class StringUnpack extends StringExpr {
|
||||
return new StringUnpack(chStart, chEnd, inner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
inner.decompile(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof StringUnpack unpack && inner.equals(unpack.inner);
|
||||
|
@ -1,13 +1,17 @@
|
||||
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.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.BoolExpr;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public final class BoolLiteral extends BoolExpr {
|
||||
public final boolean value;
|
||||
|
||||
public BoolLiteral(int chStart, int chEnd, boolean value) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Primary, chStart, chEnd);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@ -21,6 +25,11 @@ public final class BoolLiteral extends BoolExpr {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
writer.append(value ? "true" : "false");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof BoolLiteral literal && value == literal.value;
|
||||
|
@ -1,14 +1,18 @@
|
||||
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.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
|
||||
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(chStart, chEnd);
|
||||
super(Order.Primary, chStart, chEnd);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@ -22,6 +26,11 @@ public final class DynamicLiteral<T> extends DynamicExpr {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
value.serialize(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof DynamicLiteral<?> literal && value.equals(literal.value);
|
||||
|
@ -1,13 +1,18 @@
|
||||
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.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.NumberExpr;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public final class NumberLiteral extends NumberExpr {
|
||||
public final double value;
|
||||
|
||||
public NumberLiteral(int chStart, int chEnd, double value) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Primary, chStart, chEnd);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@ -21,6 +26,11 @@ public final class NumberLiteral extends NumberExpr {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
writer.append(StringFormatter.toString(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof NumberLiteral literal && value == literal.value;
|
||||
|
@ -1,13 +1,17 @@
|
||||
package io.gitlab.jfronny.muscript.ast.literal;
|
||||
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
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.data.Scope;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public final class StringLiteral extends StringExpr {
|
||||
public final String value;
|
||||
|
||||
public StringLiteral(int chStart, int chEnd, String value) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Primary, chStart, chEnd);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@ -21,6 +25,11 @@ public final class StringLiteral extends StringExpr {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
writer.append(enquote(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof StringLiteral literal && value.equals(literal.value);
|
||||
|
@ -1,16 +1,20 @@
|
||||
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.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.NumberExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Divide extends NumberExpr {
|
||||
public final NumberExpr dividend;
|
||||
public final NumberExpr divisor;
|
||||
|
||||
public Divide(int chStart, int chEnd, NumberExpr dividend, NumberExpr divisor) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Factor, chStart, chEnd);
|
||||
this.dividend = dividend;
|
||||
this.divisor = divisor;
|
||||
}
|
||||
@ -31,6 +35,13 @@ public class Divide extends NumberExpr {
|
||||
return new Divide(chStart, chEnd, dividend, divisor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
parenthesize(dividend, writer, false);
|
||||
writer.append(" / ");
|
||||
parenthesize(divisor, writer, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Divide divide && dividend.equals(divide.dividend) && divisor.equals(divide.divisor);
|
||||
|
@ -1,15 +1,19 @@
|
||||
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.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.NumberExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Invert extends NumberExpr {
|
||||
public final NumberExpr inner;
|
||||
|
||||
public Invert(int chStart, int chEnd, NumberExpr inner) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Unary, chStart, chEnd);
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@ -23,9 +27,16 @@ public class Invert extends NumberExpr {
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
writer.append('-');
|
||||
parenthesize(inner, writer, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Invert invert && inner.equals(invert.inner);
|
||||
|
@ -1,16 +1,20 @@
|
||||
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.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.NumberExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Minus extends NumberExpr {
|
||||
public final NumberExpr minuend;
|
||||
public final NumberExpr subtrahend;
|
||||
|
||||
public Minus(int chStart, int chEnd, NumberExpr minuend, NumberExpr subtrahend) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Term, chStart, chEnd);
|
||||
this.minuend = minuend;
|
||||
this.subtrahend = subtrahend;
|
||||
}
|
||||
@ -31,6 +35,13 @@ public class Minus extends NumberExpr {
|
||||
return new Minus(chStart, chEnd, minuend, subtrahend);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
parenthesize(minuend, writer, false);
|
||||
writer.append(" - ");
|
||||
parenthesize(subtrahend, writer, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Minus minus && minuend.equals(minus.minuend) && subtrahend.equals(minus.subtrahend);
|
||||
|
@ -1,16 +1,20 @@
|
||||
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.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.NumberExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Modulo extends NumberExpr {
|
||||
private final NumberExpr dividend;
|
||||
private final NumberExpr divisor;
|
||||
|
||||
public Modulo(int chStart, int chEnd, NumberExpr dividend, NumberExpr divisor) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Factor, chStart, chEnd);
|
||||
this.dividend = dividend;
|
||||
this.divisor = divisor;
|
||||
}
|
||||
@ -29,6 +33,13 @@ public class Modulo extends NumberExpr {
|
||||
return new Modulo(chStart, chEnd, dividend, divisor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
parenthesize(dividend, writer, false);
|
||||
writer.append(" % ");
|
||||
parenthesize(divisor, writer, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Modulo modulo && dividend.equals(modulo.dividend) && divisor.equals(modulo.divisor);
|
||||
|
@ -1,16 +1,20 @@
|
||||
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.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.NumberExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Multiply extends NumberExpr {
|
||||
public final NumberExpr multiplier;
|
||||
public final NumberExpr multiplicand;
|
||||
|
||||
public Multiply(int chStart, int chEnd, NumberExpr multiplier, NumberExpr multiplicand) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Factor, chStart, chEnd);
|
||||
this.multiplier = multiplier;
|
||||
this.multiplicand = multiplicand;
|
||||
}
|
||||
@ -29,6 +33,13 @@ public class Multiply extends NumberExpr {
|
||||
return new Multiply(chStart, chEnd, multiplier, multiplicand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
parenthesize(multiplier, writer, false);
|
||||
writer.append(" * ");
|
||||
parenthesize(multiplicand, writer, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Multiply multiply && multiplier.equals(multiply.multiplier) && multiplicand.equals(multiply.multiplicand);
|
||||
|
@ -1,16 +1,20 @@
|
||||
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.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.NumberExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Plus extends NumberExpr {
|
||||
public final NumberExpr augend;
|
||||
public final NumberExpr addend;
|
||||
|
||||
public Plus(int chStart, int chEnd, NumberExpr augend, NumberExpr addend) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Term, chStart, chEnd);
|
||||
this.augend = augend;
|
||||
this.addend = addend;
|
||||
}
|
||||
@ -29,6 +33,13 @@ public class Plus extends NumberExpr {
|
||||
return new Plus(chStart, chEnd, augend, addend);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
parenthesize(augend, writer, false);
|
||||
writer.append(" + ");
|
||||
parenthesize(addend, writer, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Plus plus && augend.equals(plus.augend) && addend.equals(plus.addend);
|
||||
|
@ -1,16 +1,20 @@
|
||||
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.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.NumberExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Power extends NumberExpr {
|
||||
private final NumberExpr base;
|
||||
private final NumberExpr exponent;
|
||||
|
||||
public Power(int chStart, int chEnd, NumberExpr base, NumberExpr exponent) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Exp, chStart, chEnd);
|
||||
this.base = base;
|
||||
this.exponent = exponent;
|
||||
}
|
||||
@ -39,6 +43,13 @@ public class Power extends NumberExpr {
|
||||
return new Power(chStart, chEnd, base, exponent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
parenthesize(base, writer, false);
|
||||
writer.append(" ^ ");
|
||||
parenthesize(exponent, writer, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Power power && base.equals(power.base) && exponent.equals(power.exponent);
|
||||
|
@ -1,16 +1,20 @@
|
||||
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.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.StringExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.StringLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Concatenate extends StringExpr {
|
||||
private final StringExpr left;
|
||||
private final StringExpr right;
|
||||
|
||||
public Concatenate(int chStart, int chEnd, StringExpr left, StringExpr right) {
|
||||
super(chStart, chEnd);
|
||||
super(Order.Concat, chStart, chEnd);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
@ -35,6 +39,13 @@ public class Concatenate extends StringExpr {
|
||||
return new Concatenate(chStart, chEnd, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
parenthesize(left, writer, false);
|
||||
writer.append(" || ");
|
||||
parenthesize(right, writer, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Concatenate concatenate && left.equals(concatenate.left) && right.equals(concatenate.right);
|
||||
|
@ -1,15 +1,18 @@
|
||||
package io.gitlab.jfronny.muscript.ast.string;
|
||||
|
||||
import io.gitlab.jfronny.commons.StringFormatter;
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.*;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class StringCoerce extends StringExpr {
|
||||
private final Expr<?> inner;
|
||||
|
||||
public StringCoerce(int chStart, int chEnd, Expr<?> inner) {
|
||||
super(chStart, chEnd);
|
||||
super(inner.order, chStart, chEnd);
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@ -29,6 +32,11 @@ public class StringCoerce extends StringExpr {
|
||||
return new StringCoerce(chStart, chEnd, inner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
inner.decompile(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof StringCoerce coerce && inner.equals(coerce.inner);
|
||||
|
@ -0,0 +1,43 @@
|
||||
package io.gitlab.jfronny.muscript.compiler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class Decompilable {
|
||||
public final Order order;
|
||||
|
||||
protected Decompilable(Order order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public abstract void decompile(ExprWriter writer) throws IOException;
|
||||
|
||||
protected void parenthesize(Decompilable val, ExprWriter writer, boolean parenEqualOrder) throws IOException {
|
||||
boolean wrap = !parenEqualOrder ? val.order.ordinal() > this.order.ordinal() : val.order.ordinal() >= this.order.ordinal();
|
||||
if (wrap) writer.append('(');
|
||||
val.decompile(writer);
|
||||
if (wrap) writer.append(')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates quotes around a string, supports strings containing quotes
|
||||
*/
|
||||
public static String enquote(String literalText) {
|
||||
if (!literalText.contains("'")) return "'" + literalText + "'";
|
||||
if (!literalText.contains("\"")) return "\"" + literalText + "\"";
|
||||
return Arrays.stream(literalText.split("'")).map(s -> "'" + s + "'")
|
||||
.collect(Collectors.joining(" || \"'\" || "));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try (ExprWriter ew = new ExprWriter(sb)) {
|
||||
decompile(ew);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not decompile", e);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package io.gitlab.jfronny.muscript.compiler;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ExprWriter implements Appendable, Closeable {
|
||||
private final Appendable target;
|
||||
private int indent = 0;
|
||||
|
||||
public ExprWriter(Appendable target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExprWriter append(CharSequence csq) throws IOException {
|
||||
target.append(csq.toString().lines().collect(Collectors.joining("\n" + indent())));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExprWriter append(CharSequence csq, int start, int end) throws IOException {
|
||||
return append(csq.subSequence(start, end));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExprWriter append(char c) throws IOException {
|
||||
if (c == '\n' || c == '\r') target.append("\n").append(indent());
|
||||
else target.append(c);
|
||||
return this;
|
||||
}
|
||||
|
||||
private String indent() {
|
||||
return " ".repeat(indent);
|
||||
}
|
||||
|
||||
public ExprWriter increaseIndent() {
|
||||
indent += 2;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExprWriter decreaseIndent() {
|
||||
if (indent <= 1) throw new IllegalStateException("Attempted to decrease indent lower than 0");
|
||||
indent -= 2;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (indent != 0) throw new IllegalStateException("Attempted to close ExprWriter before end");
|
||||
}
|
||||
}
|
@ -1,5 +1,8 @@
|
||||
package io.gitlab.jfronny.muscript.compiler;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
// Heavily inspired by starscript
|
||||
public class Lexer {
|
||||
/**
|
||||
@ -215,6 +218,12 @@ public class Lexer {
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '$';
|
||||
}
|
||||
|
||||
public static boolean isValidId(String id) {
|
||||
return IDENTIFIER.matcher(id).matches() && !RESERVED_IDS.contains(id);
|
||||
}
|
||||
public static final Set<String> RESERVED_IDS = Set.of("null", "true", "false");
|
||||
public static final Pattern IDENTIFIER = Pattern.compile("[a-zA-Z_$][a-zA-Z_$0-9]*");
|
||||
|
||||
// Visualization
|
||||
@Override
|
||||
public String toString() {
|
||||
|
@ -0,0 +1,17 @@
|
||||
package io.gitlab.jfronny.muscript.compiler;
|
||||
|
||||
public enum Order {
|
||||
Script,
|
||||
Conditional,
|
||||
And,
|
||||
Or,
|
||||
Equality,
|
||||
Concat,
|
||||
Comparison,
|
||||
Term,
|
||||
Factor,
|
||||
Exp,
|
||||
Unary,
|
||||
Call,
|
||||
Primary;
|
||||
}
|
@ -2,13 +2,17 @@ package io.gitlab.jfronny.muscript.compiler;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.*;
|
||||
import io.gitlab.jfronny.muscript.ast.bool.*;
|
||||
import io.gitlab.jfronny.muscript.ast.compare.*;
|
||||
import io.gitlab.jfronny.muscript.ast.compare.Equal;
|
||||
import io.gitlab.jfronny.muscript.ast.compare.Greater;
|
||||
import io.gitlab.jfronny.muscript.ast.conditional.UnresolvedConditional;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.DynamicLiteral;
|
||||
import io.gitlab.jfronny.muscript.ast.math.*;
|
||||
import io.gitlab.jfronny.muscript.ast.string.Concatenate;
|
||||
import io.gitlab.jfronny.muscript.data.Script;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
import io.gitlab.jfronny.muscript.error.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@ -153,8 +157,8 @@ public class Parser {
|
||||
NumberExpr right = asNumber(term());
|
||||
expr = switch (op) {
|
||||
case Greater -> new Greater(start, end, asNumber(expr), right);
|
||||
case GreaterEqual -> new Not(start, end, new Less(start, end, asNumber(expr), right));
|
||||
case Less -> new Less(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));
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
@ -297,32 +301,65 @@ public class Parser {
|
||||
|
||||
if (match(Token.LeftBrace)) {
|
||||
int start = previous.start;
|
||||
List<String> boundArgs = new LinkedList<>();
|
||||
boolean variadic = false;
|
||||
boolean first = true;
|
||||
while (!match(Token.Arrow)) {
|
||||
if (!first) consume(Token.Comma, "Closure parameters MUST be comma-seperated");
|
||||
first = false;
|
||||
consume(Token.Identifier, "Closure arguments MUST be identifiers");
|
||||
boundArgs.add(previous.lexeme);
|
||||
if (match(Token.Ellipsis)) {
|
||||
variadic = true;
|
||||
consume(Token.Arrow, "Variadic argument MUST be the last argument");
|
||||
break;
|
||||
}
|
||||
if (match(Token.Arrow)) return finishClosure(start, null, false);
|
||||
if (match(Token.RightBrace)) return new DynamicLiteral<>(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);
|
||||
if (match(Token.Ellipsis)) return finishClosure(start, first, true);
|
||||
if (check(Token.Comma)) return finishClosure(start, first, false);
|
||||
if (match(Token.Assign)) {
|
||||
return finishObject(start, first, expression().asDynamicExpr());
|
||||
}
|
||||
List<Expr<?>> expressions = new LinkedList<>();
|
||||
while (!match(Token.RightBrace)) {
|
||||
expressions.add(expression());
|
||||
match(Token.Semicolon); // Consume semicolon if present
|
||||
}
|
||||
int end = previous.start;
|
||||
return new Closure(start, end, boundArgs, expressions, variadic);
|
||||
throw error("Unexpected");
|
||||
}
|
||||
|
||||
throw error("Expected expression.");
|
||||
}
|
||||
|
||||
private Expr<?> finishClosure(int start, @Nullable String firstArg, boolean firstVariadic) {
|
||||
List<String> boundArgs = new LinkedList<>();
|
||||
boolean variadic = false;
|
||||
if (firstArg != null) {
|
||||
boundArgs.add(firstArg);
|
||||
if (firstVariadic) {
|
||||
consume(Token.Arrow, "Variadic argument MUST be the last argument");
|
||||
variadic = true;
|
||||
} else {
|
||||
while (!match(Token.Arrow)) {
|
||||
consume(Token.Comma, "Closure parameters MUST be comma-seperated");
|
||||
consume(Token.Identifier, "Closure arguments MUST be identifiers");
|
||||
boundArgs.add(previous.lexeme);
|
||||
if (match(Token.Ellipsis)) {
|
||||
variadic = true;
|
||||
consume(Token.Arrow, "Variadic argument MUST be the last argument");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
List<Expr<?>> expressions = new LinkedList<>();
|
||||
while (!match(Token.RightBrace)) {
|
||||
expressions.add(expression());
|
||||
match(Token.Semicolon); // Consume semicolon if present
|
||||
}
|
||||
int end = previous.start;
|
||||
return new Closure(start, end, boundArgs, expressions, variadic);
|
||||
}
|
||||
|
||||
private Expr<?> finishObject(int start, @Nullable String firstArg, @Nullable DynamicExpr firstValue) {
|
||||
Map<String, DynamicExpr> content = new LinkedHashMap<>();
|
||||
content.put(firstArg, firstValue);
|
||||
while (match(Token.Comma)) {
|
||||
consume(Token.Identifier, "Object element MUST start with an identifier");
|
||||
String name = previous.lexeme;
|
||||
consume(Token.Assign, "Object element name and value MUST be seperated with '='");
|
||||
content.put(name, expression().asDynamicExpr());
|
||||
}
|
||||
consume(Token.RightBrace, "Expected end of object");
|
||||
return new ObjectLiteral(start, previous.start, content);
|
||||
}
|
||||
|
||||
// Type conversion
|
||||
private BoolExpr asBool(Expr<?> expression) {
|
||||
try {
|
||||
|
@ -2,6 +2,7 @@ package io.gitlab.jfronny.muscript.data;
|
||||
|
||||
import io.gitlab.jfronny.commons.data.ImmCollection;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
@ -83,10 +84,15 @@ public class Scope implements DObject {
|
||||
}
|
||||
|
||||
public Scope set(String key, Function<DList, Dynamic<?>> value) {
|
||||
return set(key, DFinal.of(value));
|
||||
return set(key, DFinal.of(value, () -> key));
|
||||
}
|
||||
|
||||
public Scope fork() {
|
||||
return new Scope(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Dynamic.serialize(this);
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,15 @@ import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.Call;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.Closure;
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Script {
|
||||
public class Script extends Decompilable {
|
||||
private final List<Expr<?>> steps;
|
||||
private final DynamicExpr fin;
|
||||
|
||||
@ -18,6 +21,7 @@ public class Script {
|
||||
}
|
||||
|
||||
private Script(List<Expr<?>> steps, DynamicExpr fin) {
|
||||
super(Order.Script);
|
||||
this.steps = steps;
|
||||
this.fin = fin;
|
||||
}
|
||||
@ -37,7 +41,7 @@ public class Script {
|
||||
return DFinal.of(args -> {
|
||||
scope.set("args", args);
|
||||
return run(scope);
|
||||
});
|
||||
}, () -> "{->\n" + this + "\n}()");
|
||||
}
|
||||
|
||||
public DynamicExpr asExpr() {
|
||||
@ -55,4 +59,12 @@ public class Script {
|
||||
public Stream<Expr<?>> stream() {
|
||||
return Stream.concat(steps.stream(), Stream.of(fin));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
for (Expr<?> expr : stream().toList()) {
|
||||
expr.decompile(writer);
|
||||
writer.append(";\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,12 @@
|
||||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
|
||||
public interface DBool extends Dynamic<Boolean> {
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public non-sealed interface DBool extends Dynamic<Boolean> {
|
||||
@Override
|
||||
default void serialize(ExprWriter writer) throws IOException {
|
||||
writer.append(getValue().toString());
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface DCallable extends Dynamic<Function<DList, Dynamic<?>>> {
|
||||
public non-sealed interface DCallable extends Dynamic<Function<DList, Dynamic<?>>> {
|
||||
default Dynamic<?> call(DList args) {
|
||||
return getValue().apply(args);
|
||||
}
|
||||
|
@ -1,15 +0,0 @@
|
||||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public record DCallableObject(Map<String, Dynamic<?>> value, DCallable callable) implements DObject {
|
||||
@Override
|
||||
public Map<String, Dynamic<?>> getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DCallable asCallable() {
|
||||
return callable;
|
||||
}
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public interface DList extends Dynamic<List<Dynamic<?>>> {
|
||||
public non-sealed interface DList extends Dynamic<List<Dynamic<?>>> {
|
||||
default Dynamic<?> get(int i) {
|
||||
return getValue().get(i);
|
||||
}
|
||||
@ -14,4 +17,16 @@ public interface DList extends Dynamic<List<Dynamic<?>>> {
|
||||
default boolean isEmpty() {
|
||||
return getValue().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
default void serialize(ExprWriter writer) throws IOException {
|
||||
writer.append("listOf(");
|
||||
boolean first = true;
|
||||
for (Dynamic<?> dynamic : getValue()) {
|
||||
if (!first) writer.append(", ");
|
||||
first = false;
|
||||
dynamic.serialize(writer);
|
||||
}
|
||||
writer.append(')');
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,22 @@
|
||||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public final class DNull implements Dynamic<Object> {
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(ExprWriter writer) throws IOException {
|
||||
writer.append(toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "null";
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,13 @@
|
||||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
|
||||
public interface DNumber extends Dynamic<Double> {
|
||||
import io.gitlab.jfronny.commons.StringFormatter;
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public non-sealed interface DNumber extends Dynamic<Double> {
|
||||
@Override
|
||||
default void serialize(ExprWriter writer) throws IOException {
|
||||
writer.append(StringFormatter.toString(getValue()));
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,12 @@
|
||||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.compiler.Lexer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
public interface DObject extends Dynamic<Map<String, Dynamic<?>>> {
|
||||
public non-sealed interface DObject extends Dynamic<Map<String, Dynamic<?>>> {
|
||||
default Dynamic<?> get(String key) {
|
||||
return getValue().get(key);
|
||||
}
|
||||
@ -10,4 +14,18 @@ public interface DObject extends Dynamic<Map<String, Dynamic<?>>> {
|
||||
default boolean has(String key) {
|
||||
return getValue().containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void serialize(ExprWriter writer) throws IOException {
|
||||
writer.append('{');
|
||||
boolean first = true;
|
||||
for (Map.Entry<String, Dynamic<?>> entry : getValue().entrySet()) {
|
||||
if (!Lexer.isValidId(entry.getKey())) throw new IllegalStateException("Illegal key: " + entry.getKey());
|
||||
if (!first) writer.append(", ");
|
||||
first = false;
|
||||
writer.append(entry.getKey()).append(" = ");
|
||||
entry.getValue().serialize(writer);
|
||||
}
|
||||
writer.append('}');
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,13 @@
|
||||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
|
||||
public interface DString extends Dynamic<String> {
|
||||
import io.gitlab.jfronny.muscript.compiler.Decompilable;
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public non-sealed interface DString extends Dynamic<String> {
|
||||
@Override
|
||||
default void serialize(ExprWriter writer) throws IOException {
|
||||
writer.append(Decompilable.enquote(getValue()));
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,35 @@
|
||||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.commons.StringFormatter;
|
||||
import io.gitlab.jfronny.muscript.StandardLib;
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.compiler.Parser;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DContainer;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
|
||||
public interface Dynamic<T> {
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Represents a value of an unknown type
|
||||
* Override toString(StringBuilder) to support custom serialization (note: the serialized form is ran with muScript to generate the tree)
|
||||
* @param <T> the type represented
|
||||
*/
|
||||
public sealed interface Dynamic<T> permits DBool, DNumber, DString, DObject, DList, DCallable, DNull, DContainer {
|
||||
static Dynamic<?> deserialize(String source) {
|
||||
return Parser.parse(source).asDynamicExpr().get(StandardLib.createScope());
|
||||
}
|
||||
|
||||
static String serialize(Dynamic<?> dynamic) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try (ExprWriter ew = new ExprWriter(sb)) {
|
||||
dynamic.serialize(ew);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not stringify", e);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
void serialize(ExprWriter writer) throws IOException;
|
||||
T getValue();
|
||||
|
||||
default DBool asBool() {
|
||||
|
@ -0,0 +1,32 @@
|
||||
package io.gitlab.jfronny.muscript.data.dynamic.additional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
public record DCallableObject(Map<String, Dynamic<?>> value, DCallable callable) implements DObject {
|
||||
@Override
|
||||
public Map<String, Dynamic<?>> getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DCallable asCallable() {
|
||||
return callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(ExprWriter writer) throws IOException {
|
||||
DObject.super.serialize(writer);
|
||||
writer.append("::callableObject(");
|
||||
callable.serialize(writer);
|
||||
writer.append(')');
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Dynamic.serialize(this);
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
package io.gitlab.jfronny.muscript.data.dynamic.additional;
|
||||
|
||||
import io.gitlab.jfronny.commons.StringFormatter;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
|
||||
public abstract class DContainer<T> implements Dynamic<T> {
|
||||
public abstract non-sealed class DContainer<T> implements Dynamic<T> {
|
||||
private T value;
|
||||
|
||||
@Override
|
||||
@ -18,6 +18,6 @@ public abstract class DContainer<T> implements Dynamic<T> {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return StringFormatter.toString(value);
|
||||
return Dynamic.serialize(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package io.gitlab.jfronny.muscript.data.dynamic.additional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public record DDate(Supplier<LocalDate> date) implements DObject {
|
||||
@Override
|
||||
public Map<String, Dynamic<?>> getValue() {
|
||||
return Map.of(
|
||||
"year", DFinal.of(date.get().getYear()),
|
||||
"month", DFinal.of(date.get().getMonthValue()),
|
||||
"day", DFinal.of(date.get().getDayOfMonth())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DString asString() {
|
||||
return DFinal.of(toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DNumber asNumber() {
|
||||
return DFinal.of(date.get().toEpochDay());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(ExprWriter writer) throws IOException {
|
||||
writer.append("date(")
|
||||
.append(String.valueOf(date.get().getYear())).append(", ")
|
||||
.append(String.valueOf(date.get().getMonthValue())).append(", ")
|
||||
.append(String.valueOf(date.get().getDayOfMonth()))
|
||||
.append(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return date.get().toString();
|
||||
}
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
package io.gitlab.jfronny.muscript.data.dynamic.additional;
|
||||
|
||||
import io.gitlab.jfronny.commons.StringFormatter;
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@ -11,7 +14,11 @@ import java.util.*;
|
||||
*/
|
||||
public record DEnum(Map<String, Dynamic<?>> values, @Nullable DEnumEntry value) implements DObject {
|
||||
public DEnum(Map<String, Dynamic<?>> values) {
|
||||
this(values, null);
|
||||
this(values, (DEnumEntry) null);
|
||||
}
|
||||
|
||||
public DEnum(Map<String, Dynamic<?>> values, @Nullable String value) {
|
||||
this(values, value == null ? null : new DEnumEntry(value, values.keySet().stream().toList().indexOf(value), true));
|
||||
}
|
||||
|
||||
public DEnum(List<String> values, String value) {
|
||||
@ -42,6 +49,16 @@ public record DEnum(Map<String, Dynamic<?>> values, @Nullable DEnumEntry value)
|
||||
return value != null ? value.asNumber() : DObject.super.asNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(ExprWriter writer) throws IOException {
|
||||
writer.append("enum(");
|
||||
DObject.super.serialize(writer);
|
||||
if (value != null) {
|
||||
writer.append(", ").append(value.value);
|
||||
}
|
||||
writer.append(')');
|
||||
}
|
||||
|
||||
private static Map<String, Dynamic<?>> createMap(List<String> values, String value) {
|
||||
Map<String, Dynamic<?>> result = new LinkedHashMap<>();
|
||||
DEnumEntry v = new DEnumEntry(value, values.indexOf(value), true);
|
@ -1,13 +1,18 @@
|
||||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
package io.gitlab.jfronny.muscript.data.dynamic.additional;
|
||||
|
||||
import io.gitlab.jfronny.commons.LazySupplier;
|
||||
import io.gitlab.jfronny.commons.StringFormatter;
|
||||
import io.gitlab.jfronny.commons.data.ImmCollection;
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public record DFinal<T>(T value) implements Dynamic<T> {
|
||||
public class DFinal<T> {
|
||||
public static DBool of(boolean b) {
|
||||
return new FBool(b);
|
||||
}
|
||||
@ -32,18 +37,8 @@ public record DFinal<T>(T value) implements Dynamic<T> {
|
||||
return new FList(ImmCollection.of(b));
|
||||
}
|
||||
|
||||
public static DCallable of(Function<DList, Dynamic<?>> b) {
|
||||
return new FCallable(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return StringFormatter.toString(value);
|
||||
public static DCallable of(Function<DList, Dynamic<?>> b, Supplier<String> serialized) {
|
||||
return new FCallable(b, new LazySupplier<>(serialized));
|
||||
}
|
||||
|
||||
private record FBool(boolean value) implements DBool {
|
||||
@ -78,7 +73,7 @@ public record DFinal<T>(T value) implements Dynamic<T> {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return StringFormatter.toString(value);
|
||||
return Dynamic.serialize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,7 +85,7 @@ public record DFinal<T>(T value) implements Dynamic<T> {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return StringFormatter.toString(value);
|
||||
return Dynamic.serialize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,11 +97,16 @@ public record DFinal<T>(T value) implements Dynamic<T> {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return StringFormatter.toString(value);
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private record FCallable(Function<DList, Dynamic<?>> value) implements DCallable {
|
||||
private record FCallable(Function<DList, Dynamic<?>> value, Supplier<String> string) implements DCallable {
|
||||
@Override
|
||||
public void serialize(ExprWriter writer) throws IOException {
|
||||
writer.append(toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<DList, Dynamic<?>> getValue() {
|
||||
return value;
|
||||
@ -114,7 +114,7 @@ public record DFinal<T>(T value) implements Dynamic<T> {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "<Callable>";
|
||||
return string.get();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package io.gitlab.jfronny.muscript.data.dynamic.additional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalTime;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public record DTime(Supplier<LocalTime> time) implements DObject {
|
||||
@Override
|
||||
public Map<String, Dynamic<?>> getValue() {
|
||||
return Map.of(
|
||||
"hour", DFinal.of(time.get().getHour()),
|
||||
"minute", DFinal.of(time.get().getMinute()),
|
||||
"second", DFinal.of(time.get().getSecond())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DString asString() {
|
||||
return DFinal.of(toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DNumber asNumber() {
|
||||
return DFinal.of(time.get().toSecondOfDay());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(ExprWriter writer) throws IOException {
|
||||
writer.append("time(")
|
||||
.append(String.valueOf(time.get().getHour())).append(", ")
|
||||
.append(String.valueOf(time.get().getMinute())).append(", ")
|
||||
.append(String.valueOf(time.get().getSecond()))
|
||||
.append(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return time.get().toString();
|
||||
}
|
||||
}
|
@ -8,4 +8,5 @@ module io.gitlab.jfronny.commons.muscript {
|
||||
exports io.gitlab.jfronny.muscript.error;
|
||||
exports io.gitlab.jfronny.muscript.data;
|
||||
exports io.gitlab.jfronny.muscript.data.dynamic;
|
||||
exports io.gitlab.jfronny.muscript.data.dynamic.additional;
|
||||
}
|
@ -16,6 +16,7 @@ class AssignTest {
|
||||
void testAssignSimple() {
|
||||
StringExpr expr = Parser.parse("someval = 'test'").asStringExpr();
|
||||
assertEquals(new Assign(0, 6, "someval", new StringLiteral(10, 15, "test").asDynamicExpr()).asStringExpr(), expr);
|
||||
assertEquals("someval = 'test'", expr.toString());
|
||||
Scope scope = new UnforkableScope();
|
||||
assertEquals("test", expr.get(scope));
|
||||
assertEquals("test", scope.getValue().get("someval").asString().getValue());
|
||||
|
@ -2,7 +2,7 @@ package io.gitlab.jfronny.muscript.test;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.Parser;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DFinal;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static io.gitlab.jfronny.muscript.test.util.MuTestUtil.makeArgs;
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.gitlab.jfronny.muscript.test;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.Parser;
|
||||
import org.junit.jupiter.api.*;
|
||||
|
||||
import static io.gitlab.jfronny.muscript.test.util.MuTestUtil.*;
|
||||
@ -21,4 +22,17 @@ class ObjectTest {
|
||||
assertTrue(bool("object2['sub'].val == 10"));
|
||||
assertTrue(bool("object2.sub['val'] == 10"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void objectLiteral() {
|
||||
assertEquals(12, Parser.parseScript("""
|
||||
ob = {}
|
||||
ob = {test = 2, test2 = 3}
|
||||
t = ob.test
|
||||
ob = {test2 = 3, test = 2}
|
||||
t = t * ob.test
|
||||
ob = {test = 3}
|
||||
t * ob.test
|
||||
""").run(makeArgs()).asNumber().getValue());
|
||||
}
|
||||
}
|
||||
|
@ -14,4 +14,10 @@ class StringTest {
|
||||
assertTrue(bool("string == 'Value'"));
|
||||
assertFalse(bool("string == 'Something else'"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void concatComparison() {
|
||||
assertEquals("Hellotrue", string("'Hello' || -12 < 5"));
|
||||
assertEquals("trueHello", string("-12 < 5 || 'Hello'"));
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import io.gitlab.jfronny.muscript.debug.ObjectGraphPrinter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static io.gitlab.jfronny.muscript.data.dynamic.DFinal.of;
|
||||
import static io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal.of;
|
||||
|
||||
public class MuTestUtil {
|
||||
public static double number(String source) {
|
||||
@ -40,7 +40,7 @@ public class MuTestUtil {
|
||||
.set("string", "Value")
|
||||
.set("object", Map.of(
|
||||
"subvalue", of(1024),
|
||||
"subfunc", of(v -> of(v.get(1).asNumber().getValue() * v.size())),
|
||||
"subfunc", of(v -> of(v.get(1).asNumber().getValue() * v.size()), () -> "object.subfunc"),
|
||||
"1", of("One")
|
||||
))
|
||||
.set("object2", Map.of(
|
||||
@ -48,7 +48,7 @@ public class MuTestUtil {
|
||||
"sub", of(Map.of(
|
||||
"val", of(10)
|
||||
)),
|
||||
"stringfunc", of(v -> of(v.get(0).asString().getValue()))
|
||||
"stringfunc", of(v -> of(v.get(0).asString().getValue()), () -> "object2.stringfunc")
|
||||
))
|
||||
.set("list", of(of(true), of(2), of("3")))
|
||||
.set("numbers", of(of(1), of(2), of(3), of(4), of(5), of(6), of(7), of(8), of(9), of(10)))
|
||||
|
@ -75,7 +75,7 @@ listOf(
|
||||
```
|
||||
Result:
|
||||
```
|
||||
[this is correct, false, false, true, true, false]
|
||||
['this is correct', false, false, true, true, false]
|
||||
```
|
||||
</details>
|
||||
|
||||
@ -133,7 +133,7 @@ listOf(
|
||||
```
|
||||
Result:
|
||||
```
|
||||
[subvalue, subvalue, subvalue, subvalue, One, 20, 64, some parameter, 2023-05-13, 23:55:10, false]
|
||||
['subvalue', 'subvalue', 'subvalue', 'subvalue', 'One', 20, 64, 'some parameter', 2023-05-13, 23:55:10, false]
|
||||
```
|
||||
</details>
|
||||
|
||||
@ -217,6 +217,6 @@ listOf(
|
||||
```
|
||||
Result:
|
||||
```
|
||||
[2, some expression(s), 10, 7, 7, 2, 1, 1, 12, 24]
|
||||
[2, 'some expression(s)', 10, 7, 7, 2, 1, 1, 12, 24]
|
||||
```
|
||||
</details>
|
||||
|
Loading…
Reference in New Issue
Block a user