chore(muscript): delete old implementation keeping redirects in place for docs
ci/woodpecker/push/woodpecker Pipeline was successful
Details
ci/woodpecker/push/woodpecker Pipeline was successful
Details
This commit is contained in:
parent
4b44f9516d
commit
108a370c51
|
@ -1,27 +0,0 @@
|
|||
import io.gitlab.jfronny.scripts.*
|
||||
|
||||
plugins {
|
||||
commons.library
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.commons)
|
||||
|
||||
testImplementation(libs.junit.jupiter.api)
|
||||
testRuntimeOnly(libs.junit.jupiter.engine)
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("maven") {
|
||||
groupId = "io.gitlab.jfronny"
|
||||
artifactId = "muscript"
|
||||
|
||||
from(components["java"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.javadoc {
|
||||
linksOffline("https://maven.frohnmeyer-wds.de/javadoc/artifacts/io/gitlab/jfronny/commons/$version/raw", projects.commons)
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.Decompilable;
|
||||
|
||||
public class StarScriptIngester {
|
||||
private static final System.Logger LOGGER = System.getLogger("MuScript");
|
||||
|
||||
/**
|
||||
* Naive conversion of starscript syntax to muscript
|
||||
*/
|
||||
public static String starScriptToMu(String source) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
StringBuilder currbuf = new StringBuilder();
|
||||
State state = State.Surrounding;
|
||||
for (char c : source.toCharArray()) {
|
||||
switch (state) {
|
||||
case Surrounding -> {
|
||||
if (c == '{') {
|
||||
if (!currbuf.isEmpty()) {
|
||||
if (!result.isEmpty()) result.append(" || ");
|
||||
result.append(Decompilable.enquote(currbuf.toString()));
|
||||
}
|
||||
currbuf = new StringBuilder();
|
||||
state = State.UnquotedInner;
|
||||
} else currbuf.append(c);
|
||||
}
|
||||
case UnquotedInner -> {
|
||||
switch (c) {
|
||||
case '}' -> {
|
||||
if (!currbuf.isEmpty()) {
|
||||
if (!result.isEmpty()) result.append(" || ");
|
||||
result.append("(").append(currbuf).append(")");
|
||||
}
|
||||
currbuf = new StringBuilder();
|
||||
state = State.Surrounding;
|
||||
}
|
||||
case '\'' -> {
|
||||
currbuf.append(c);
|
||||
state = State.SingleQuoteInner;
|
||||
}
|
||||
case '"' -> {
|
||||
currbuf.append(c);
|
||||
state = State.DoubleQuoteInner;
|
||||
}
|
||||
default -> currbuf.append(c);
|
||||
}
|
||||
}
|
||||
case SingleQuoteInner -> {
|
||||
currbuf.append(c);
|
||||
if (c == '\'') state = State.UnquotedInner;
|
||||
}
|
||||
case DoubleQuoteInner -> {
|
||||
currbuf.append(c);
|
||||
if (c == '"') state = State.UnquotedInner;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!currbuf.isEmpty() && !result.isEmpty()) result.append(" || ");
|
||||
switch (state) {
|
||||
case Surrounding -> {
|
||||
if (!currbuf.isEmpty()) result.append(Decompilable.enquote(currbuf.toString()));
|
||||
}
|
||||
case UnquotedInner -> {
|
||||
LOGGER.log(System.Logger.Level.WARNING, "Starscript code segment improperly closed, closing automatically");
|
||||
if (!currbuf.isEmpty()) result.append("(").append(currbuf).append(")");
|
||||
}
|
||||
case SingleQuoteInner -> {
|
||||
LOGGER.log(System.Logger.Level.WARNING, "Quote in starscript swallows ending, completing with closing quote");
|
||||
if (!currbuf.isEmpty()) result.append("(").append(currbuf).append("')");
|
||||
}
|
||||
case DoubleQuoteInner -> {
|
||||
LOGGER.log(System.Logger.Level.WARNING, "Quote in starscript swallows ending, completing with closing quote");
|
||||
if (!currbuf.isEmpty()) result.append("(").append(currbuf).append("\")");
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
enum State {
|
||||
Surrounding, UnquotedInner, SingleQuoteInner, DoubleQuoteInner
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation for expressions that may throw exceptions
|
||||
* Any expression that doesn't have this annotation will only throw if an expression referenced from it throws
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface CanThrow {
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation for expressions that utilize Dynamic.as*
|
||||
* These expressions must also be annotated with @CanThrow
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface UncheckedDynamic {
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.DynamicCoerce;
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
|
||||
public abstract non-sealed class BoolExpr extends Expr<Boolean> {
|
||||
protected BoolExpr(Order order, CodeLocation location) {
|
||||
super(order, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getResultType() {
|
||||
return Type.Boolean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract BoolExpr optimize();
|
||||
|
||||
@Override
|
||||
public DynamicExpr asDynamicExpr() {
|
||||
return new DynamicCoerce(location, this);
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.unpack.*;
|
||||
|
||||
public abstract non-sealed class DynamicExpr extends Expr<Dynamic> {
|
||||
protected DynamicExpr(Order order, CodeLocation location) {
|
||||
super(order, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getResultType() {
|
||||
return Type.Dynamic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract DynamicExpr optimize();
|
||||
|
||||
@Override
|
||||
public BoolExpr asBoolExpr() {
|
||||
return new BoolUnpack(location, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringExpr asStringExpr() {
|
||||
return new StringUnpack(location, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberExpr asNumberExpr() {
|
||||
return new NumberUnpack(location, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr asDynamicExpr() {
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast;
|
||||
|
||||
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.*;
|
||||
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;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@CanThrow
|
||||
public abstract sealed class Expr<T> extends Decompilable
|
||||
permits BoolExpr, DynamicExpr, NullLiteral, NumberExpr, StringExpr {
|
||||
public final CodeLocation location;
|
||||
protected Expr(Order order, CodeLocation location) {
|
||||
super(order);
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public abstract Type getResultType();
|
||||
public abstract T get(Scope dataRoot);
|
||||
public T get(DObject dataRoot) {
|
||||
return get(dataRoot instanceof Scope scope ? scope : new Scope(dataRoot));
|
||||
}
|
||||
public abstract Expr<T> optimize();
|
||||
|
||||
public BoolExpr asBoolExpr() {
|
||||
if (this instanceof BoolExpr e) return e;
|
||||
throw new TypeMismatchException(location, Type.Boolean, getResultType());
|
||||
}
|
||||
|
||||
public StringExpr asStringExpr() {
|
||||
if (this instanceof StringExpr e) return e;
|
||||
return new StringCoerce(location, this);
|
||||
}
|
||||
|
||||
public NumberExpr asNumberExpr() {
|
||||
if (this instanceof NumberExpr e) return e;
|
||||
throw new TypeMismatchException(location, Type.Number, getResultType());
|
||||
}
|
||||
|
||||
public abstract DynamicExpr asDynamicExpr();
|
||||
|
||||
public abstract Stream<Expr<?>> extractSideEffects();
|
||||
|
||||
public boolean isNull() {
|
||||
return this instanceof NullLiteral;
|
||||
}
|
||||
|
||||
public static BoolExpr literal(boolean bool) {
|
||||
return literal(CodeLocation.NONE, bool);
|
||||
}
|
||||
|
||||
public static StringExpr literal(String string) {
|
||||
return literal(CodeLocation.NONE, string);
|
||||
}
|
||||
|
||||
public static NumberExpr literal(double number) {
|
||||
return literal(CodeLocation.NONE, number);
|
||||
}
|
||||
|
||||
public static NullLiteral literalNull() {
|
||||
return literalNull(CodeLocation.NONE);
|
||||
}
|
||||
|
||||
public static BoolExpr literal(CodeLocation location, boolean bool) {
|
||||
return new BoolLiteral(location, bool);
|
||||
}
|
||||
|
||||
public static StringExpr literal(CodeLocation location, String string) {
|
||||
return new StringLiteral(location, string);
|
||||
}
|
||||
|
||||
public static NumberExpr literal(CodeLocation location, double number) {
|
||||
return new NumberLiteral(location, number);
|
||||
}
|
||||
|
||||
public static NullLiteral literalNull(CodeLocation location) {
|
||||
return new NullLiteral(location);
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
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.error.LocationalException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@CanThrow
|
||||
public final class NullLiteral extends Expr<Object> {
|
||||
public NullLiteral(CodeLocation location) {
|
||||
super(Order.Primary, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getResultType() {
|
||||
return Type.Dynamic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(Scope dataRoot) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NullLiteral optimize() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
writer.append("null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr asDynamicExpr() {
|
||||
return new DynamicLiteral<>(location, new DNull());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberExpr asNumberExpr() {
|
||||
throw new LocationalException(location, "Attempted to convert null to a number");
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringExpr asStringExpr() {
|
||||
return literal(location, "null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoolExpr asBoolExpr() {
|
||||
throw new LocationalException(location, "Attempted to convert null to a boolean");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof NullLiteral;
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.DynamicCoerce;
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
|
||||
public abstract non-sealed class NumberExpr extends Expr<Double> {
|
||||
protected NumberExpr(Order order, CodeLocation location) {
|
||||
super(order, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getResultType() {
|
||||
return Type.Number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract NumberExpr optimize();
|
||||
|
||||
@Override
|
||||
public DynamicExpr asDynamicExpr() {
|
||||
return new DynamicCoerce(location, this);
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.DynamicCoerce;
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
|
||||
public abstract non-sealed class StringExpr extends Expr<String> {
|
||||
protected StringExpr(Order order, CodeLocation location) {
|
||||
super(order, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getResultType() {
|
||||
return Type.String;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract StringExpr optimize();
|
||||
|
||||
@Override
|
||||
public DynamicExpr asDynamicExpr() {
|
||||
return new DynamicCoerce(location, this);
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.bool;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.BoolExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class And extends BoolExpr {
|
||||
private final BoolExpr left;
|
||||
private final BoolExpr right;
|
||||
|
||||
public And(CodeLocation location, BoolExpr left, BoolExpr right) {
|
||||
super(Order.And, location);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get(Scope dataRoot) {
|
||||
return left.get(dataRoot) && right.get(dataRoot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoolExpr optimize() {
|
||||
BoolExpr left = this.left.optimize();
|
||||
BoolExpr right = this.right.optimize();
|
||||
if (left instanceof BoolLiteral literal) return literal.value ? right : literal(location, false);
|
||||
if (right instanceof BoolLiteral literal) return literal.value ? left : literal(location, false);
|
||||
return new And(location, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.concat(left.extractSideEffects(), right.extractSideEffects());
|
||||
}
|
||||
|
||||
@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,67 +0,0 @@
|
|||
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.*;
|
||||
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;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Not extends BoolExpr {
|
||||
public final BoolExpr inner;
|
||||
|
||||
public Not(CodeLocation location, BoolExpr inner) {
|
||||
super(Order.Unary, location);
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get(Scope dataRoot) {
|
||||
return !inner.get(dataRoot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoolExpr optimize() {
|
||||
BoolExpr inner = this.inner.optimize();
|
||||
if (inner instanceof Not not) return not.inner;
|
||||
if (inner instanceof BoolLiteral literal) return literal(location, !literal.value);
|
||||
return new Not(location, inner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return inner.extractSideEffects();
|
||||
}
|
||||
|
||||
@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,52 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.bool;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.BoolExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Or extends BoolExpr {
|
||||
private final BoolExpr left;
|
||||
private final BoolExpr right;
|
||||
|
||||
public Or(CodeLocation location, BoolExpr left, BoolExpr right) {
|
||||
super(Order.Or, location);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get(Scope dataRoot) {
|
||||
return left.get(dataRoot) || right.get(dataRoot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoolExpr optimize() {
|
||||
BoolExpr left = this.left.optimize();
|
||||
BoolExpr right = this.right.optimize();
|
||||
if (left instanceof BoolLiteral literal) return literal.value ? literal(location, true) : right;
|
||||
if (right instanceof BoolLiteral literal) return literal.value ? literal(location, true) : left;
|
||||
return new Or(location, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.concat(left.extractSideEffects(), right.extractSideEffects());
|
||||
}
|
||||
|
||||
@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,57 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.compare;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.BoolExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Equal extends BoolExpr {
|
||||
public final Expr<?> left;
|
||||
public final Expr<?> right;
|
||||
|
||||
public Equal(CodeLocation location, Expr<?> left, Expr<?> right) {
|
||||
super(Order.Equality, location);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get(Scope dataRoot) {
|
||||
return Objects.equals(unwrap(left.get(dataRoot)), unwrap(right.get(dataRoot)));
|
||||
}
|
||||
|
||||
private Object unwrap(Object o) {
|
||||
if (o instanceof Dynamic a) return unwrap(a.getValue());
|
||||
return o;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoolExpr optimize() {
|
||||
Expr<?> left = this.left.optimize();
|
||||
Expr<?> right = this.right.optimize();
|
||||
if (left.equals(right)) return literal(location, true);
|
||||
return new Equal(location, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.concat(left.extractSideEffects(), right.extractSideEffects());
|
||||
}
|
||||
|
||||
@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,69 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.compare;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.*;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
import io.gitlab.jfronny.muscript.ast.math.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Greater extends BoolExpr {
|
||||
public final NumberExpr left;
|
||||
public final NumberExpr right;
|
||||
|
||||
public Greater(CodeLocation location, NumberExpr left, NumberExpr right) {
|
||||
super(Order.Comparison, location);
|
||||
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 literal(location, litL.value > litR.value);
|
||||
if (left instanceof Divide divide)
|
||||
return new Greater(location, divide.dividend, new Multiply(divide.location, right, divide.divisor)).optimize();
|
||||
if (left instanceof Invert invert)
|
||||
return new Greater(location, new Invert(right.location, right), invert.inner).optimize();
|
||||
if (left instanceof Minus minus)
|
||||
return new Greater(location, minus.minuend, new Plus(minus.location, minus.subtrahend, right)).optimize();
|
||||
// Modulo is left out because it is too complicated for this naive impl
|
||||
// Multiply is left out since it would transform into a division and may be 0
|
||||
if (left instanceof Plus plus)
|
||||
return new Greater(location, plus.augend, new Minus(plus.location, plus.addend, right)).optimize();
|
||||
// Power is left out because it can't be transformed cleanly either
|
||||
return new Greater(location, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.concat(left.extractSideEffects(), right.extractSideEffects());
|
||||
}
|
||||
|
||||
@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,64 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.conditional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.bool.Not;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.ExprGroup;
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.BoolExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class BoolConditional extends BoolExpr {
|
||||
public final BoolExpr condition;
|
||||
public final BoolExpr trueExpr;
|
||||
public final BoolExpr falseExpr;
|
||||
|
||||
public BoolConditional(CodeLocation location, BoolExpr condition, BoolExpr trueExpr, BoolExpr falseExpr) {
|
||||
super(Order.Conditional, location);
|
||||
this.condition = condition;
|
||||
this.trueExpr = trueExpr;
|
||||
this.falseExpr = falseExpr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get(Scope dataRoot) {
|
||||
return condition.get(dataRoot) ? trueExpr.get(dataRoot) : falseExpr.get(dataRoot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoolExpr optimize() {
|
||||
BoolExpr condition = this.condition.optimize();
|
||||
BoolExpr trueExpr = this.trueExpr.optimize();
|
||||
BoolExpr falseExpr = this.falseExpr.optimize();
|
||||
if (condition instanceof BoolLiteral literal) return literal.value ? trueExpr : falseExpr;
|
||||
if (trueExpr.equals(falseExpr)) return trueExpr;
|
||||
if (condition instanceof Not not) return new BoolConditional(location, not.inner, falseExpr, trueExpr);
|
||||
return new BoolConditional(location, condition, trueExpr, falseExpr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return UnresolvedConditional.extractSideEffects(location, 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
|
||||
&& condition.equals(conditional.condition)
|
||||
&& trueExpr.equals(conditional.trueExpr)
|
||||
&& falseExpr.equals(conditional.falseExpr);
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.conditional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.bool.Not;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.ExprGroup;
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
import io.gitlab.jfronny.muscript.ast.BoolExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class DynamicConditional extends DynamicExpr {
|
||||
public final BoolExpr condition;
|
||||
public final DynamicExpr trueExpr;
|
||||
public final DynamicExpr falseExpr;
|
||||
|
||||
public DynamicConditional(CodeLocation location, BoolExpr condition, DynamicExpr trueExpr, DynamicExpr falseExpr) {
|
||||
super(Order.Conditional, location);
|
||||
this.condition = condition;
|
||||
this.trueExpr = trueExpr;
|
||||
this.falseExpr = falseExpr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic get(Scope dataRoot) {
|
||||
return condition.get(dataRoot) ? trueExpr.get(dataRoot) : falseExpr.get(dataRoot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr optimize() {
|
||||
BoolExpr condition = this.condition.optimize();
|
||||
DynamicExpr trueExpr = this.trueExpr.optimize();
|
||||
DynamicExpr falseExpr = this.falseExpr.optimize();
|
||||
if (condition instanceof BoolLiteral literal) return literal.value ? trueExpr : falseExpr;
|
||||
if (trueExpr.equals(falseExpr)) return trueExpr;
|
||||
if (condition instanceof Not not) return new DynamicConditional(location, not.inner, falseExpr, trueExpr);
|
||||
return new DynamicConditional(location, condition, trueExpr, falseExpr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return UnresolvedConditional.extractSideEffects(location, 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
|
||||
&& condition.equals(conditional.condition)
|
||||
&& trueExpr.equals(conditional.trueExpr)
|
||||
&& falseExpr.equals(conditional.falseExpr);
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.conditional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.bool.Not;
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.BoolExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.NumberExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class NumberConditional extends NumberExpr {
|
||||
public final BoolExpr condition;
|
||||
public final NumberExpr trueExpr;
|
||||
public final NumberExpr falseExpr;
|
||||
|
||||
public NumberConditional(CodeLocation location, BoolExpr condition, NumberExpr trueExpr, NumberExpr falseExpr) {
|
||||
super(Order.Conditional, location);
|
||||
this.condition = condition;
|
||||
this.trueExpr = trueExpr;
|
||||
this.falseExpr = falseExpr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(Scope dataRoot) {
|
||||
return condition.get(dataRoot) ? trueExpr.get(dataRoot) : falseExpr.get(dataRoot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberExpr optimize() {
|
||||
BoolExpr condition = this.condition.optimize();
|
||||
NumberExpr trueExpr = this.trueExpr.optimize();
|
||||
NumberExpr falseExpr = this.falseExpr.optimize();
|
||||
if (condition instanceof BoolLiteral literal) return literal.value ? trueExpr : falseExpr;
|
||||
if (trueExpr.equals(falseExpr)) return trueExpr;
|
||||
if (condition instanceof Not not) return new NumberConditional(location, not.inner, falseExpr, trueExpr);
|
||||
return new NumberConditional(location, condition, trueExpr, falseExpr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return UnresolvedConditional.extractSideEffects(location, 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
|
||||
&& condition.equals(conditional.condition)
|
||||
&& trueExpr.equals(conditional.trueExpr)
|
||||
&& falseExpr.equals(conditional.falseExpr);
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.conditional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.bool.Not;
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.BoolExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.StringExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class StringConditional extends StringExpr {
|
||||
public final BoolExpr condition;
|
||||
public final StringExpr trueExpr;
|
||||
public final StringExpr falseExpr;
|
||||
|
||||
public StringConditional(CodeLocation location, BoolExpr condition, StringExpr trueExpr, StringExpr falseExpr) {
|
||||
super(Order.Conditional, location);
|
||||
this.condition = condition;
|
||||
this.trueExpr = trueExpr;
|
||||
this.falseExpr = falseExpr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(Scope dataRoot) {
|
||||
return condition.get(dataRoot) ? trueExpr.get(dataRoot) : falseExpr.get(dataRoot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringExpr optimize() {
|
||||
BoolExpr condition = this.condition.optimize();
|
||||
StringExpr trueExpr = this.trueExpr.optimize();
|
||||
StringExpr falseExpr = this.falseExpr.optimize();
|
||||
if (condition instanceof BoolLiteral literal) return literal.value ? trueExpr : falseExpr;
|
||||
if (trueExpr.equals(falseExpr)) return trueExpr;
|
||||
if (condition instanceof Not not) return new StringConditional(location, not.inner, falseExpr, trueExpr);
|
||||
return new StringConditional(location, condition, trueExpr, falseExpr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return UnresolvedConditional.extractSideEffects(location, 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
|
||||
&& condition.equals(conditional.condition)
|
||||
&& trueExpr.equals(conditional.trueExpr)
|
||||
&& falseExpr.equals(conditional.falseExpr);
|
||||
}
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.conditional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.bool.Not;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.ExprGroup;
|
||||
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.ast.literal.BoolLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class UnresolvedConditional extends DynamicExpr {
|
||||
private final BoolExpr condition;
|
||||
private final Expr<?> trueExpr;
|
||||
private final Expr<?> falseExpr;
|
||||
|
||||
public UnresolvedConditional(CodeLocation location, BoolExpr condition, Expr<?> trueExpr, Expr<?> falseExpr) {
|
||||
super(Order.Conditional, location);
|
||||
this.condition = condition;
|
||||
this.trueExpr = trueExpr;
|
||||
this.falseExpr = falseExpr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getResultType() {
|
||||
return trueExpr.getResultType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr optimize() {
|
||||
BoolExpr condition = this.condition.optimize();
|
||||
Expr<?> trueExpr = this.trueExpr.optimize();
|
||||
Expr<?> falseExpr = this.falseExpr.optimize();
|
||||
if (condition instanceof BoolLiteral literal) return literal.value ? trueExpr.asDynamicExpr() : falseExpr.asDynamicExpr();
|
||||
if (trueExpr.equals(falseExpr)) return trueExpr.asDynamicExpr();
|
||||
if (condition instanceof Not not) return new UnresolvedConditional(location, not.inner, falseExpr, trueExpr);
|
||||
return new UnresolvedConditional(location, 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
|
||||
// thus, this needs to be handled, but the result will always be null
|
||||
if (condition.get(dataRoot)) trueExpr.get(dataRoot);
|
||||
else falseExpr.get(dataRoot);
|
||||
return new DNull();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr asDynamicExpr() {
|
||||
return new DynamicConditional(location, condition, trueExpr.asDynamicExpr(), falseExpr.asDynamicExpr());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return extractSideEffects(location, condition, trueExpr, falseExpr);
|
||||
}
|
||||
|
||||
public static Stream<Expr<?>> extractSideEffects(CodeLocation location, BoolExpr condition, Expr<?> trueExpr, Expr<?> falseExpr) {
|
||||
List<Expr<?>> trueSE = trueExpr.extractSideEffects().toList();
|
||||
List<Expr<?>> falseSE = falseExpr.extractSideEffects().toList();
|
||||
if (trueSE.isEmpty() && falseSE.isEmpty()) return condition.extractSideEffects();
|
||||
return Stream.of(new UnresolvedConditional(
|
||||
location,
|
||||
condition,
|
||||
ExprGroup.of(trueExpr.location, trueSE, false),
|
||||
ExprGroup.of(falseExpr.location, falseSE, false)
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoolExpr asBoolExpr() {
|
||||
return new BoolConditional(location, condition, trueExpr.asBoolExpr(), falseExpr.asBoolExpr());
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringExpr asStringExpr() {
|
||||
return new StringConditional(location, condition, trueExpr.asStringExpr(), falseExpr.asStringExpr());
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberExpr asNumberExpr() {
|
||||
return new NumberConditional(location, condition, trueExpr.asNumberExpr(), falseExpr.asNumberExpr());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof UnresolvedConditional conditional
|
||||
&& condition.equals(conditional.condition)
|
||||
&& trueExpr.equals(conditional.trueExpr)
|
||||
&& falseExpr.equals(conditional.falseExpr);
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
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.DCallable;
|
||||
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;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Bind extends DynamicExpr {
|
||||
public final DynamicExpr callable;
|
||||
public final DynamicExpr parameter;
|
||||
|
||||
public Bind(CodeLocation location, DynamicExpr callable, DynamicExpr parameter) {
|
||||
super(Order.Call, location);
|
||||
this.callable = callable;
|
||||
this.parameter = parameter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic get(Scope dataRoot) {
|
||||
Dynamic param = parameter.get(dataRoot);
|
||||
DCallable clb = callable.get(dataRoot).asCallable();
|
||||
return DFinal.of("<bind>", args -> {
|
||||
List<Dynamic> argsWithParameter = new LinkedList<>(args.getValue());
|
||||
argsWithParameter.addFirst(param);
|
||||
return clb.call(DFinal.of(argsWithParameter));
|
||||
}, () -> this);
|
||||
}
|
||||
|
||||
@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
|
||||
public DynamicExpr optimize() {
|
||||
return new Bind(location, callable.optimize(), parameter.optimize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.concat(callable.extractSideEffects(), parameter.extractSideEffects());
|
||||
}
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
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.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 io.gitlab.jfronny.muscript.error.StackFrame;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@CanThrow
|
||||
@UncheckedDynamic
|
||||
public class Call extends DynamicExpr {
|
||||
public final DynamicExpr left;
|
||||
public final List<Arg> args;
|
||||
|
||||
public Call(CodeLocation location, DynamicExpr left, List<Arg> args) {
|
||||
super(Order.Call, location);
|
||||
this.left = left;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic get(Scope dataRoot) {
|
||||
DCallable dc;
|
||||
DList arg;
|
||||
try {
|
||||
Dynamic lv = left.get(dataRoot);
|
||||
if (Dynamic.isNull(lv)) throw new LocationalException(location, "Cannot invoke null");
|
||||
dc = lv.asCallable();
|
||||
arg = DFinal.of(args.stream().flatMap(e -> e.get(dataRoot)).toArray(Dynamic[]::new));
|
||||
} catch (DynamicTypeConversionException e) {
|
||||
throw e.locational(location);
|
||||
} catch (RuntimeException e) {
|
||||
throw new LocationalException(location, "Could not perform call successfully", e);
|
||||
}
|
||||
try {
|
||||
return dc.call(arg);
|
||||
} catch (LocationalException le) {
|
||||
throw le.appendStack(new StackFrame.Raw(location.file(), dc.getName(), left.location.chStart()));
|
||||
} catch (DynamicTypeConversionException e) {
|
||||
throw e.locational(location);
|
||||
} catch (RuntimeException e) {
|
||||
throw new LocationalException(location, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr optimize() {
|
||||
List<Arg> args = new ArrayList<>();
|
||||
DynamicExpr left = this.left.optimize();
|
||||
if (left instanceof Bind bind) {
|
||||
left = bind.callable;
|
||||
args.add(new Arg(bind.parameter, false));
|
||||
}
|
||||
for (Arg arg : this.args) args.add(arg.optimize());
|
||||
if (left instanceof Closure closure) {
|
||||
return new ExprGroup(
|
||||
closure.location,
|
||||
closure.stream().toList(),
|
||||
new ExprGroup.PackedArgs(
|
||||
args,
|
||||
closure.boundArgs,
|
||||
closure.variadic
|
||||
)
|
||||
);
|
||||
}
|
||||
return new Call(location, left, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.of(this);
|
||||
}
|
||||
|
||||
@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("...");
|
||||
}
|
||||
writer.append(')');
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Call call && left.equals(call.left) && args.equals(call.args);
|
||||
}
|
||||
|
||||
public record Arg(DynamicExpr expr, boolean variadic) {
|
||||
public Stream<? extends Dynamic> get(Scope dataRoot) {
|
||||
return variadic ? expr.get(dataRoot).asList().getValue().stream() : Stream.of(expr.get(dataRoot));
|
||||
}
|
||||
|
||||
public Arg optimize() {
|
||||
return new Arg(expr.optimize(), variadic);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
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.data.dynamic.type.DTypeCallable;
|
||||
import io.gitlab.jfronny.muscript.error.LocationalException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Closure extends DynamicExpr {
|
||||
public final List<String> boundArgs;
|
||||
private final List<Expr<?>> steps;
|
||||
private final DynamicExpr fin;
|
||||
public final boolean variadic;
|
||||
|
||||
public Closure(CodeLocation location, List<String> boundArgs, List<Expr<?>> expressions, boolean variadic) {
|
||||
this(location, boundArgs, expressions.subList(0, expressions.size() - 1), expressions.getLast().asDynamicExpr(), variadic);
|
||||
}
|
||||
|
||||
private Closure(CodeLocation location, List<String> boundArgs, List<Expr<?>> steps, DynamicExpr fin, boolean variadic) {
|
||||
super(Order.Primary, location);
|
||||
this.boundArgs = List.copyOf(boundArgs);
|
||||
this.steps = List.copyOf(steps);
|
||||
this.fin = fin;
|
||||
this.variadic = variadic;
|
||||
}
|
||||
|
||||
private DTypeCallable getSignature() {
|
||||
//TODO take into account inner instructions to properly determine type
|
||||
List<DTypeCallable.Arg> args = new LinkedList<>();
|
||||
for (int i = 0; i < boundArgs.size(); i++) {
|
||||
args.add(new DTypeCallable.Arg(boundArgs.get(i), null, variadic && (i == boundArgs.size() - 1)));
|
||||
}
|
||||
return new DTypeCallable(args, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DCallable get(Scope dataRoot) {
|
||||
return DFinal.of(getSignature(), null, args -> {
|
||||
// Compare with ExprGroup.get()
|
||||
int ac = args.size();
|
||||
int ae = boundArgs.size();
|
||||
if (variadic) ae--;
|
||||
if (ac < ae) throw new LocationalException(location, "Invoked with too few arguments (expected " + ae + " but got " + ac + ")");
|
||||
if (!variadic && ac > ae) throw new LocationalException(location, "Invoked with too many arguments (expected " + ae + " but got " + ac + ")");
|
||||
Scope fork = dataRoot.fork();
|
||||
for (int i = 0; i < ae; i++) fork.set(boundArgs.get(i), args.get(i));
|
||||
if (variadic) {
|
||||
fork.set(boundArgs.getLast(), IntStream.range(ae, ac)
|
||||
.mapToObj(args::get)
|
||||
.toList());
|
||||
}
|
||||
for (Expr<?> step : steps) {
|
||||
step.get(fork);
|
||||
}
|
||||
return fin.get(fork);
|
||||
}, () -> this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr optimize() {
|
||||
return new Closure(
|
||||
location,
|
||||
boundArgs,
|
||||
steps.stream()
|
||||
.map(Expr::optimize)
|
||||
.flatMap(Expr::extractSideEffects)
|
||||
.<Expr<?>>map(Expr::optimize)
|
||||
.toList(),
|
||||
fin.optimize(),
|
||||
variadic
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
writer.append("{");
|
||||
boolean first = true;
|
||||
for (int i = 0; i < boundArgs.size(); i++) {
|
||||
if (!first) writer.append(",");
|
||||
first = false;
|
||||
writer.append(' ').appendLiteral(boundArgs.get(i));
|
||||
if (i == boundArgs.size() - 1 && variadic) 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
|
||||
&& boundArgs.equals(closure.boundArgs)
|
||||
&& steps.equals(closure.steps)
|
||||
&& fin.equals(closure.fin);
|
||||
}
|
||||
|
||||
public Stream<Expr<?>> stream() {
|
||||
return Stream.concat(steps.stream(), Stream.of(fin));
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.*;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.unpack.*;
|
||||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class DynamicCoerce extends DynamicExpr {
|
||||
public final Expr<?> inner;
|
||||
|
||||
public DynamicCoerce(CodeLocation location, Expr<?> inner) {
|
||||
super(inner.order, location);
|
||||
this.inner = inner;
|
||||
if (!(inner instanceof DynamicExpr)
|
||||
&& !(inner instanceof BoolExpr)
|
||||
&& !(inner instanceof StringExpr)
|
||||
&& !(inner instanceof NumberExpr)
|
||||
&& !(inner instanceof NullLiteral)) {
|
||||
throw new IllegalArgumentException("A DynamicCoerce can only be created with a well-defined expression type");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic get(Scope dataRoot) {
|
||||
if (inner instanceof DynamicExpr e) return e.get(dataRoot);
|
||||
if (inner instanceof BoolExpr e) return DFinal.of(e.get(dataRoot));
|
||||
if (inner instanceof StringExpr e) return DFinal.of(e.get(dataRoot));
|
||||
if (inner instanceof NumberExpr e) return DFinal.of(e.get(dataRoot));
|
||||
if (inner instanceof NullLiteral) return new DNull();
|
||||
throw new IllegalStateException("The inner expression of DynamicCoerce should never be of undefined type");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr optimize() {
|
||||
Expr<?> inner = this.inner.optimize();
|
||||
if (inner instanceof BoolUnpack unpack) return unpack.inner;
|
||||
if (inner instanceof NumberUnpack unpack) return unpack.inner;
|
||||
if (inner instanceof StringUnpack unpack) return unpack.inner;
|
||||
return new DynamicCoerce(location, inner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return inner.extractSideEffects();
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
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.ast.NullLiteral;
|
||||
import io.gitlab.jfronny.muscript.ast.string.Concatenate;
|
||||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
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.error.LocationalException;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ExprGroup extends DynamicExpr {
|
||||
private final List<Expr<?>> steps;
|
||||
private final DynamicExpr fin;
|
||||
private final @Nullable PackedArgs packedArgs;
|
||||
private final boolean fork;
|
||||
|
||||
public ExprGroup(CodeLocation location, List<Expr<?>> expressions) {
|
||||
this(location, expressions, null);
|
||||
}
|
||||
|
||||
public ExprGroup(CodeLocation location, List<Expr<?>> expressions, @Nullable PackedArgs packedArgs) {
|
||||
this(location, expressions, packedArgs, true);
|
||||
}
|
||||
|
||||
public ExprGroup(CodeLocation location, List<Expr<?>> expressions, @Nullable PackedArgs packedArgs, boolean fork) {
|
||||
super(Order.Primary, location);
|
||||
this.steps = expressions.subList(0, expressions.size() - 1);
|
||||
this.fin = expressions.getLast().asDynamicExpr();
|
||||
this.packedArgs = packedArgs;
|
||||
this.fork = fork;
|
||||
if (!fork && packedArgs != null) throw new UnsupportedOperationException("packedArgs may only be used for forking ExprGroups");
|
||||
}
|
||||
|
||||
public static Expr<?> of(CodeLocation location, List<Expr<?>> expressions) {
|
||||
return of(location, expressions, true);
|
||||
}
|
||||
|
||||
public static Expr<?> of(CodeLocation location, List<Expr<?>> expressions, boolean fork) {
|
||||
if (expressions.isEmpty()) return new NullLiteral(location);
|
||||
if (!fork && expressions.size() == 1) return expressions.getFirst();
|
||||
return new ExprGroup(location, expressions, null, fork);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic get(Scope dataRoot) {
|
||||
Scope fork = this.fork ? dataRoot.fork() : dataRoot;
|
||||
if (this.fork && packedArgs != null) {
|
||||
List<Dynamic> from = new LinkedList<>();
|
||||
for (Call.Arg arg : packedArgs.from) {
|
||||
Dynamic data = arg.expr().get(dataRoot);
|
||||
if (arg.variadic()) from.addAll(data.asList().getValue());
|
||||
else from.add(data);
|
||||
}
|
||||
List<String> to = packedArgs.to;
|
||||
// Compare with Closure.get()
|
||||
int ac = from.size();
|
||||
int ae = to.size();
|
||||
if (packedArgs.variadic) ae--;
|
||||
if (ac < ae) throw new LocationalException(location, "Invoked with too few arguments (expected " + ae + " but got " + ac + ")");
|
||||
if (!packedArgs.variadic && ac > ae) throw new LocationalException(location, "Invoked with too many arguments (expected " + ae + " but got " + ac + ")");
|
||||
for (int i = 0; i < ae; i++) fork.set(to.get(i), from.get(i));
|
||||
if (packedArgs.variadic) {
|
||||
fork.set(to.get(to.size() - 1), IntStream.range(ae, ac)
|
||||
.mapToObj(from::get)
|
||||
.toList());
|
||||
}
|
||||
}
|
||||
for (Expr<?> step : steps) {
|
||||
step.get(fork);
|
||||
}
|
||||
return fin.get(fork);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr optimize() {
|
||||
List<Expr<?>> exprs = Stream.concat(
|
||||
steps.stream()
|
||||
.map(Expr::optimize)
|
||||
.flatMap(Expr::extractSideEffects)
|
||||
.map(Expr::optimize),
|
||||
Stream.of(fin.optimize())
|
||||
).toList();
|
||||
return packedArgs == null
|
||||
? of(location, exprs, fork).asDynamicExpr()
|
||||
: new ExprGroup(location, exprs, packedArgs, fork);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return fork ? Stream.of(this) : Stream.of(of(location, stream().flatMap(Expr::extractSideEffects).toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
if (!fork) {
|
||||
// Use string concatenation since the result is ignored either way
|
||||
stream()
|
||||
.reduce((left, right) -> new Concatenate(location, left.asStringExpr(), right.asStringExpr()))
|
||||
.orElseGet(() -> new NullLiteral(location))
|
||||
.optimize()
|
||||
.decompile(writer);
|
||||
} else {
|
||||
new Call(
|
||||
location,
|
||||
new Closure(
|
||||
location,
|
||||
packedArgs == null
|
||||
? List.of()
|
||||
: packedArgs.to,
|
||||
stream().toList(),
|
||||
false
|
||||
),
|
||||
packedArgs == null ? List.of() : packedArgs.from
|
||||
).decompile(writer);
|
||||
}
|
||||
}
|
||||
|
||||
public Stream<Expr<?>> stream() {
|
||||
return Stream.concat(steps.stream(), Stream.of(fin));
|
||||
}
|
||||
|
||||
public record PackedArgs(List<Call.Arg> from, List<String> to, boolean variadic) {}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
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.annotations.CanThrow;
|
||||
import io.gitlab.jfronny.muscript.annotations.UncheckedDynamic;
|
||||
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.LocationalException;
|
||||
import io.gitlab.jfronny.muscript.error.TypeMismatchException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@CanThrow
|
||||
@UncheckedDynamic
|
||||
public class Get extends DynamicExpr {
|
||||
private final DynamicExpr left;
|
||||
private final Expr<?> name;
|
||||
|
||||
public Get(CodeLocation location, DynamicExpr left, Expr<?> name) {
|
||||
super(Order.Call, location);
|
||||
this.left = left;
|
||||
this.name = name;
|
||||
if (name.getResultType() != Type.String && name.getResultType() != Type.Number && name.getResultType() != Type.Dynamic) {
|
||||
throw new TypeMismatchException(location, Type.String, name.getResultType(), "Name must be either a string or a number");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic get(Scope dataRoot) {
|
||||
Dynamic left = this.left.get(dataRoot);
|
||||
if (Dynamic.isNull(left)) throw new LocationalException(location, "Could not get \"" + name.asStringExpr().get(dataRoot) + "\" because left is null");
|
||||
if (left.isObject()) {
|
||||
DObject o = left.asObject();
|
||||
var n = name.asStringExpr().get(dataRoot);
|
||||
if (!o.has(n)) throw new LocationalException(location, "Object does not contain \"" + n + "\"");
|
||||
return o.get(n);
|
||||
} else if (left.isList()) {
|
||||
DList l = left.asList();
|
||||
int idx = name.asNumberExpr().get(dataRoot).intValue();
|
||||
if (idx < 0 || idx >= l.size()) throw new LocationalException(location, "Index " + idx + " is out of range for list with size " + l.size());
|
||||
return l.get(idx);
|
||||
}
|
||||
throw new DynamicTypeConversionException("object or list", left).locational(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr optimize() {
|
||||
DynamicExpr left = this.left.optimize();
|
||||
Expr<?> name = this.name.optimize();
|
||||
return new Get(location, left, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.concat(left.extractSideEffects(), name.extractSideEffects());
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
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.CodeLocation;
|
||||
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.data.dynamic.additional.DFinal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ListOf extends DynamicExpr {
|
||||
private final List<DynamicExpr> elements;
|
||||
|
||||
protected ListOf(CodeLocation location, List<DynamicExpr> elements) {
|
||||
super(Order.Primary, location);
|
||||
this.elements = elements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic get(Scope dataRoot) {
|
||||
return DFinal.of(elements.stream().map(s -> s.get(dataRoot)).toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr optimize() {
|
||||
return new ListOf(location, elements.stream().map(DynamicExpr::optimize).toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return elements.stream().flatMap(Expr::extractSideEffects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
new Call(
|
||||
location,
|
||||
new Variable(location, "listOf"),
|
||||
elements.stream().map(s -> new Call.Arg(s, false)).toList()
|
||||
).decompile(writer);
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
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.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;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ObjectLiteral extends DynamicExpr {
|
||||
public final Map<String, DynamicExpr> content;
|
||||
|
||||
public ObjectLiteral(CodeLocation location, Map<String, DynamicExpr> content) {
|
||||
super(Order.Primary, location);
|
||||
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<>(location, DFinal.of(literalContent));
|
||||
return new ObjectLiteral(location, content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return content.values().stream().flatMap(Expr::extractSideEffects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
writer.increaseIndent().append("{\n");
|
||||
boolean first = true;
|
||||
for (Map.Entry<String, DynamicExpr> entry : content.entrySet()) {
|
||||
if (!first) writer.append(",\n");
|
||||
first = false;
|
||||
writer.appendLiteral(entry.getKey()).append(" = ");
|
||||
entry.getValue().decompile(writer);
|
||||
}
|
||||
writer.decreaseIndent().append("\n}");
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.dynamic;
|
||||
|
||||
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.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.error.LocationalException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@CanThrow
|
||||
@UncheckedDynamic
|
||||
public class Variable extends DynamicExpr {
|
||||
public final String name;
|
||||
|
||||
public Variable(CodeLocation location, String name) {
|
||||
super(Order.Primary, location);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic get(Scope dataRoot) {
|
||||
if (name.equals("this")) return dataRoot;
|
||||
if (dataRoot.has(name)) return dataRoot.get(name);
|
||||
else throw new LocationalException(location, "This object doesn't contain '" + name + "'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr optimize() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
writer.appendLiteral(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Variable variable && name.equals(variable.name);
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.dynamic.assign;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.BoolExpr;
|
||||
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.error.LocationalException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class BoolAssign extends BoolExpr {
|
||||
private final String name;
|
||||
private final BoolExpr value;
|
||||
|
||||
protected BoolAssign(CodeLocation location, String name, BoolExpr value) {
|
||||
super(Order.Primary, location);
|
||||
if (name.equals("this")) throw new LocationalException(location, "Cannot reassign 'this'");
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get(Scope dataRoot) {
|
||||
boolean data = value.get(dataRoot);
|
||||
dataRoot.set(name, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoolExpr optimize() {
|
||||
return new BoolAssign(location, name, value.optimize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.of(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
writer.appendLiteral(name).append(" = ");
|
||||
value.decompile(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof BoolAssign assign
|
||||
&& name.equals(assign.name)
|
||||
&& value.equals(assign.value);
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.dynamic.assign;
|
||||
|
||||
import 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.DCallable;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
import io.gitlab.jfronny.muscript.error.LocationalException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class DynamicAssign extends DynamicExpr {
|
||||
private final String name;
|
||||
private final DynamicExpr value;
|
||||
|
||||
public DynamicAssign(CodeLocation location, String name, DynamicExpr value) {
|
||||
super(Order.Primary, location);
|
||||
if (name.equals("this")) throw new LocationalException(location, "Cannot reassign 'this'");
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic get(Scope dataRoot) {
|
||||
Dynamic data = value.get(dataRoot);
|
||||
dataRoot.set(name, data.isCallable() ? data.asCallable().named(name) : data);
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr optimize() {
|
||||
return new DynamicAssign(location, name, value.optimize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
writer.appendLiteral(name).append(" = ");
|
||||
value.decompile(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoolExpr asBoolExpr() {
|
||||
return new BoolAssign(location, name, value.asBoolExpr());
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberExpr asNumberExpr() {
|
||||
return new NumberAssign(location, name, value.asNumberExpr());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.of(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof DynamicAssign assign
|
||||
&& name.equals(assign.name)
|
||||
&& value.equals(assign.value);
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.dynamic.assign;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.NumberExpr;
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.error.LocationalException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class NumberAssign extends NumberExpr {
|
||||
private final String name;
|
||||
private final NumberExpr value;
|
||||
|
||||
protected NumberAssign(CodeLocation location, String name, NumberExpr value) {
|
||||
super(Order.Primary, location);
|
||||
if (name.equals("this")) throw new LocationalException(location, "Cannot reassign 'this'");
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(Scope dataRoot) {
|
||||
double data = value.get(dataRoot);
|
||||
dataRoot.set(name, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberExpr optimize() {
|
||||
return new NumberAssign(location, name, value.optimize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.of(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
writer.appendLiteral(name).append(" = ");
|
||||
value.decompile(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof NumberAssign assign
|
||||
&& name.equals(assign.name)
|
||||
&& value.equals(assign.value);
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.dynamic.assign;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.StringExpr;
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.error.LocationalException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class StringAssign extends StringExpr {
|
||||
private final String name;
|
||||
private final StringExpr value;
|
||||
|
||||
protected StringAssign(CodeLocation location, String name, StringExpr value) {
|
||||
super(Order.Primary, location);
|
||||
if (name.equals("this")) throw new LocationalException(location, "Cannot reassign 'this'");
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(Scope dataRoot) {
|
||||
String data = value.get(dataRoot);
|
||||
dataRoot.set(name, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringExpr optimize() {
|
||||
return new StringAssign(location, name, value.optimize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.of(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
writer.appendLiteral(name).append(" = ");
|
||||
value.decompile(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof StringAssign assign
|
||||
&& name.equals(assign.name)
|
||||
&& value.equals(assign.value);
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.dynamic.unpack;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DynamicTypeConversionException;
|
||||
import io.gitlab.jfronny.muscript.ast.BoolExpr;
|
||||
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.ast.dynamic.DynamicCoerce;
|
||||
import io.gitlab.jfronny.muscript.error.LocationalException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@CanThrow
|
||||
@UncheckedDynamic
|
||||
public class BoolUnpack extends BoolExpr {
|
||||
public final DynamicExpr inner;
|
||||
|
||||
public BoolUnpack(CodeLocation location, DynamicExpr inner) {
|
||||
super(inner.order, location);
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get(Scope dataRoot) {
|
||||
try {
|
||||
Dynamic iv = inner.get(dataRoot);
|
||||
if (Dynamic.isNull(iv)) throw new LocationalException(location, "Cannot unpack null");
|
||||
return iv.asBool().getValue();
|
||||
} catch (DynamicTypeConversionException e) {
|
||||
throw e.locational(location);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoolExpr optimize() {
|
||||
DynamicExpr inner = this.inner.optimize();
|
||||
if (inner instanceof DynamicCoerce coerce) return coerce.inner.asBoolExpr();
|
||||
return new BoolUnpack(location, inner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return inner.extractSideEffects();
|
||||
}
|
||||
|
||||
@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,61 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.dynamic.unpack;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DynamicTypeConversionException;
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.NumberExpr;
|
||||
import io.gitlab.jfronny.muscript.annotations.CanThrow;
|
||||
import io.gitlab.jfronny.muscript.annotations.UncheckedDynamic;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.DynamicCoerce;
|
||||
import io.gitlab.jfronny.muscript.error.LocationalException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@CanThrow
|
||||
@UncheckedDynamic
|
||||
public class NumberUnpack extends NumberExpr {
|
||||
public final DynamicExpr inner;
|
||||
|
||||
public NumberUnpack(CodeLocation location, DynamicExpr inner) {
|
||||
super(inner.order, location);
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(Scope dataRoot) {
|
||||
try {
|
||||
Dynamic iv = inner.get(dataRoot);
|
||||
if (Dynamic.isNull(iv)) throw new LocationalException(location, "Cannot unpack null");
|
||||
return iv.asNumber().getValue();
|
||||
} catch (DynamicTypeConversionException e) {
|
||||
throw e.locational(location);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberExpr optimize() {
|
||||
DynamicExpr inner = this.inner.optimize();
|
||||
if (inner instanceof DynamicCoerce coerce) return coerce.inner.asNumberExpr();
|
||||
return new NumberUnpack(location, inner.optimize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return inner.extractSideEffects();
|
||||
}
|
||||
|
||||
@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,61 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.dynamic.unpack;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DynamicTypeConversionException;
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.StringExpr;
|
||||
import io.gitlab.jfronny.muscript.annotations.CanThrow;
|
||||
import io.gitlab.jfronny.muscript.annotations.UncheckedDynamic;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.DynamicCoerce;
|
||||
import io.gitlab.jfronny.muscript.error.LocationalException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@CanThrow
|
||||
@UncheckedDynamic
|
||||
public class StringUnpack extends StringExpr {
|
||||
public final DynamicExpr inner;
|
||||
|
||||
public StringUnpack(CodeLocation location, DynamicExpr inner) {
|
||||
super(inner.order, location);
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(Scope dataRoot) {
|
||||
try {
|
||||
Dynamic iv = inner.get(dataRoot);
|
||||
if (Dynamic.isNull(iv)) throw new LocationalException(location, "Cannot unpack null");
|
||||
return iv.asString().getValue();
|
||||
} catch (DynamicTypeConversionException e) {
|
||||
throw e.locational(location);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringExpr optimize() {
|
||||
DynamicExpr inner = this.inner.optimize();
|
||||
if (inner instanceof DynamicCoerce coerce) return coerce.inner.asStringExpr();
|
||||
return new StringUnpack(location, inner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return inner.extractSideEffects();
|
||||
}
|
||||
|
||||
@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,43 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.literal;
|
||||
|
||||
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.ast.BoolExpr;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class BoolLiteral extends BoolExpr {
|
||||
public final boolean value;
|
||||
|
||||
public BoolLiteral(CodeLocation location, boolean value) {
|
||||
super(Order.Primary, location);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get(Scope dataRoot) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoolExpr optimize() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
@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,44 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.literal;
|
||||
|
||||
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.Dynamic;
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class DynamicLiteral<T> extends DynamicExpr {
|
||||
public final Dynamic value;
|
||||
|
||||
public DynamicLiteral(CodeLocation location, Dynamic value) {
|
||||
super(Order.Primary, location);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic get(Scope dataRoot) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr optimize() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
@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,44 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.literal;
|
||||
|
||||
import io.gitlab.jfronny.commons.StringFormatter;
|
||||
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.ast.NumberExpr;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class NumberLiteral extends NumberExpr {
|
||||
public final double value;
|
||||
|
||||
public NumberLiteral(CodeLocation location, double value) {
|
||||
super(Order.Primary, location);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(Scope dataRoot) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberExpr optimize() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
@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,43 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.literal;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.StringExpr;
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class StringLiteral extends StringExpr {
|
||||
public final String value;
|
||||
|
||||
public StringLiteral(CodeLocation location, String value) {
|
||||
super(Order.Primary, location);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(Scope dataRoot) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringExpr optimize() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
@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,54 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.math;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.NumberExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Divide extends NumberExpr {
|
||||
public final NumberExpr dividend;
|
||||
public final NumberExpr divisor;
|
||||
|
||||
public Divide(CodeLocation location, NumberExpr dividend, NumberExpr divisor) {
|
||||
super(Order.Factor, location);
|
||||
this.dividend = dividend;
|
||||
this.divisor = divisor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(Scope dataRoot) {
|
||||
return dividend.get(dataRoot) / divisor.get(dataRoot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberExpr optimize() {
|
||||
NumberExpr dividend = this.dividend.optimize();
|
||||
NumberExpr divisor = this.divisor.optimize();
|
||||
if (dividend instanceof NumberLiteral litL && divisor instanceof NumberLiteral litR)
|
||||
return literal(location, litL.value / litR.value);
|
||||
if (dividend instanceof Divide divide && divide.dividend instanceof NumberLiteral literal)
|
||||
return new Divide(location, divide.dividend, new Multiply(divide.location, divisor, literal)).optimize();
|
||||
return new Divide(location, dividend, divisor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.concat(dividend.extractSideEffects(), divisor.extractSideEffects());
|
||||
}
|
||||
|
||||
@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,49 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.math;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.NumberExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Invert extends NumberExpr {
|
||||
public final NumberExpr inner;
|
||||
|
||||
public Invert(CodeLocation location, NumberExpr inner) {
|
||||
super(Order.Unary, location);
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(Scope dataRoot) {
|
||||
return -inner.get(dataRoot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberExpr optimize() {
|
||||
NumberExpr inner = this.inner.optimize();
|
||||
if (inner instanceof Invert invert) return invert.inner;
|
||||
if (inner instanceof NumberLiteral literal) return Expr.literal(location, -literal.value);
|
||||
if (inner instanceof Minus minus) return new Minus(location, minus.subtrahend, minus.minuend).optimize();
|
||||
return new Invert(location, inner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return inner.extractSideEffects();
|
||||
}
|
||||
|
||||
@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,60 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.math;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.NumberExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Minus extends NumberExpr {
|
||||
public final NumberExpr minuend;
|
||||
public final NumberExpr subtrahend;
|
||||
|
||||
public Minus(CodeLocation location, NumberExpr minuend, NumberExpr subtrahend) {
|
||||
super(Order.Term, location);
|
||||
this.minuend = minuend;
|
||||
this.subtrahend = subtrahend;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(Scope dataRoot) {
|
||||
return minuend.get(dataRoot) - subtrahend.get(dataRoot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberExpr optimize() {
|
||||
NumberExpr minuend = this.minuend.optimize();
|
||||
NumberExpr subtrahend = this.subtrahend.optimize();
|
||||
if (minuend instanceof NumberLiteral litM && subtrahend instanceof NumberLiteral litS)
|
||||
return literal(location, litM.value - litS.value);
|
||||
if (minuend instanceof Minus minus)
|
||||
return new Minus(location, minus.minuend, new Plus(minus.location, minus.subtrahend, subtrahend)).optimize();
|
||||
if (minuend instanceof Invert invM && subtrahend instanceof Invert invS)
|
||||
return new Minus(location, invS.inner, invM.inner);
|
||||
if (minuend instanceof Invert inv)
|
||||
return new Invert(location, new Plus(location, inv.inner, subtrahend)).optimize();
|
||||
if (subtrahend instanceof Invert inv)
|
||||
return new Plus(location, minuend, inv.inner).optimize();
|
||||
return new Minus(location, minuend, subtrahend);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.concat(minuend.extractSideEffects(), subtrahend.extractSideEffects());
|
||||
}
|
||||
|
||||
@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,52 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.math;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.NumberExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Modulo extends NumberExpr {
|
||||
private final NumberExpr dividend;
|
||||
private final NumberExpr divisor;
|
||||
|
||||
public Modulo(CodeLocation location, NumberExpr dividend, NumberExpr divisor) {
|
||||
super(Order.Factor, location);
|
||||
this.dividend = dividend;
|
||||
this.divisor = divisor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(Scope dataRoot) {
|
||||
return dividend.get(dataRoot) % divisor.get(dataRoot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberExpr optimize() {
|
||||
NumberExpr dividend = this.dividend.optimize();
|
||||
NumberExpr divisor = this.divisor.optimize();
|
||||
if (dividend instanceof NumberLiteral litD && divisor instanceof NumberLiteral litS)
|
||||
return Expr.literal(location, litD.value % litS.value);
|
||||
return new Modulo(location, dividend, divisor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.concat(dividend.extractSideEffects(), divisor.extractSideEffects());
|
||||
}
|
||||
|
||||
@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,52 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.math;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.NumberExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Multiply extends NumberExpr {
|
||||
public final NumberExpr multiplier;
|
||||
public final NumberExpr multiplicand;
|
||||
|
||||
public Multiply(CodeLocation location, NumberExpr multiplier, NumberExpr multiplicand) {
|
||||
super(Order.Factor, location);
|
||||
this.multiplier = multiplier;
|
||||
this.multiplicand = multiplicand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(Scope dataRoot) {
|
||||
return multiplier.get(dataRoot) * multiplicand.get(dataRoot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberExpr optimize() {
|
||||
NumberExpr multiplier = this.multiplier.optimize();
|
||||
NumberExpr multiplicand = this.multiplicand.optimize();
|
||||
if (multiplier instanceof NumberLiteral litEr && multiplicand instanceof NumberLiteral litAnd)
|
||||
return literal(location, litEr.value * litAnd.value);
|
||||
return new Multiply(location, multiplier, multiplicand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.concat(multiplier.extractSideEffects(), multiplicand.extractSideEffects());
|
||||
}
|
||||
|
||||
@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,56 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.math;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.NumberExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Plus extends NumberExpr {
|
||||
public final NumberExpr augend;
|
||||
public final NumberExpr addend;
|
||||
|
||||
public Plus(CodeLocation location, NumberExpr augend, NumberExpr addend) {
|
||||
super(Order.Term, location);
|
||||
this.augend = augend;
|
||||
this.addend = addend;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(Scope dataRoot) {
|
||||
return augend.get(dataRoot) + addend.get(dataRoot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberExpr optimize() {
|
||||
NumberExpr augend = this.augend.optimize();
|
||||
NumberExpr addend = this.addend.optimize();
|
||||
if (augend instanceof NumberLiteral litU && addend instanceof NumberLiteral litD)
|
||||
return literal(location, litU.value + litD.value);
|
||||
if (augend instanceof Invert invU && addend instanceof Invert invD)
|
||||
return new Invert(location, new Plus(location, invU.inner, invD.inner)).optimize();
|
||||
if (augend instanceof Invert inv) return new Minus(location, addend, inv.inner).optimize();
|
||||
if (addend instanceof Invert inv) return new Minus(location, augend, inv.inner).optimize();
|
||||
return new Plus(location, augend, addend);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.concat(augend.extractSideEffects(), addend.extractSideEffects());
|
||||
}
|
||||
|
||||
@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,62 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.math;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.NumberExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Power extends NumberExpr {
|
||||
private final NumberExpr base;
|
||||
private final NumberExpr exponent;
|
||||
|
||||
public Power(CodeLocation location, NumberExpr base, NumberExpr exponent) {
|
||||
super(Order.Exp, location);
|
||||
this.base = base;
|
||||
this.exponent = exponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(Scope dataRoot) {
|
||||
return Math.pow(base.get(dataRoot), exponent.get(dataRoot));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberExpr optimize() {
|
||||
NumberExpr base = this.base.optimize();
|
||||
NumberExpr exponent = this.exponent.optimize();
|
||||
if (base instanceof NumberLiteral litB && exponent instanceof NumberLiteral litE)
|
||||
return literal(location, Math.pow(litB.value, litE.value));
|
||||
if (exponent instanceof NumberLiteral exp && base instanceof Multiply multiply) {
|
||||
if (multiply.multiplier instanceof NumberLiteral literal)
|
||||
return new Multiply(multiply.location,
|
||||
literal(location, Math.pow(literal.value, exp.value)),
|
||||
new Power(location, multiply.multiplicand, exp));
|
||||
if (multiply.multiplicand instanceof NumberLiteral literal)
|
||||
return new Multiply(multiply.location,
|
||||
literal(location, Math.pow(literal.value, exp.value)),
|
||||
new Power(location, multiply.multiplier, exp));
|
||||
}
|
||||
return new Power(location, base, exponent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.concat(base.extractSideEffects(), exponent.extractSideEffects());
|
||||
}
|
||||
|
||||
@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,58 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.string;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.StringExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.StringLiteral;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Concatenate extends StringExpr {
|
||||
public final StringExpr left;
|
||||
public final StringExpr right;
|
||||
|
||||
public Concatenate(CodeLocation location, StringExpr left, StringExpr right) {
|
||||
super(Order.Concat, location);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(Scope dataRoot) {
|
||||
return left.get(dataRoot) + right.get(dataRoot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringExpr optimize() {
|
||||
StringExpr left = this.left.optimize();
|
||||
StringExpr right = this.right.optimize();
|
||||
if (left instanceof StringLiteral litL && right instanceof StringLiteral litR)
|
||||
return literal(location, litL.value + litR.value);
|
||||
if (right instanceof StringLiteral litR && left instanceof Concatenate concatenate && concatenate.right instanceof StringLiteral litL) {
|
||||
return new Concatenate(location, concatenate.left, literal(concatenate.location, litL.value + litR.value));
|
||||
}
|
||||
if (left instanceof StringLiteral litL && right instanceof Concatenate concatenate && concatenate.left instanceof StringLiteral litR) {
|
||||
return new Concatenate(location, literal(concatenate.location, litL.value + litR.value), concatenate.right);
|
||||
}
|
||||
return new Concatenate(location, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return Stream.concat(left.extractSideEffects(), right.extractSideEffects());
|
||||
}
|
||||
|
||||
@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,51 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.ast.string;
|
||||
|
||||
import io.gitlab.jfronny.commons.StringFormatter;
|
||||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.ast.*;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class StringCoerce extends StringExpr {
|
||||
private final Expr<?> inner;
|
||||
|
||||
public StringCoerce(CodeLocation location, Expr<?> inner) {
|
||||
super(inner.order, location);
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(Scope dataRoot) {
|
||||
return StringFormatter.toString(inner.get(dataRoot));
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringExpr optimize() {
|
||||
Expr<?> inner = this.inner.optimize();
|
||||
if (inner instanceof NullLiteral) return literal(location, "null");
|
||||
if (inner instanceof BoolLiteral expr) return literal(location, StringFormatter.toString(expr.value));
|
||||
if (inner instanceof DynamicLiteral<?> expr) return literal(location, StringFormatter.toString(expr.value));
|
||||
if (inner instanceof NumberLiteral expr) return literal(location, StringFormatter.toStringPrecise(expr.value));
|
||||
if (inner instanceof StringExpr expr) return expr;
|
||||
return new StringCoerce(location, inner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Expr<?>> extractSideEffects() {
|
||||
return inner.extractSideEffects();
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.compiler;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public record CodeLocation(int chStart, int chEnd, @Nullable String source, @Nullable String file) {
|
||||
public static final CodeLocation NONE = new CodeLocation(-1, -1);
|
||||
|
||||
public CodeLocation(int chStart, int chEnd) {
|
||||
this(chStart, chEnd, null, null);
|
||||
}
|
||||
|
||||
public CodeLocation(int ch) {
|
||||
this(ch, ch);
|
||||
}
|
||||
|
||||
public CodeLocation withSource(String source) {
|
||||
return new CodeLocation(chStart, chEnd, source, file);
|
||||
}
|
||||
|
||||
public CodeLocation withFile(String file) {
|
||||
return new CodeLocation(chStart, chEnd, source, file);
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
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, false)) {
|
||||
decompile(ew);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not decompile", e);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.compiler;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ExprWriter implements Appendable, Closeable {
|
||||
private final Appendable target;
|
||||
private final boolean compact;
|
||||
private int indent = 0;
|
||||
|
||||
public ExprWriter(Appendable target, boolean compact) {
|
||||
this.target = target;
|
||||
this.compact = compact;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExprWriter append(CharSequence csq) throws IOException {
|
||||
target.append(csq.toString().replace("\r", "").replace("\n", compact ? " " : "\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 {
|
||||
switch (c) {
|
||||
case '\r' -> {}
|
||||
case '\n' -> {
|
||||
if (compact) target.append(" ");
|
||||
else target.append("\n").append(indent());
|
||||
}
|
||||
default -> target.append(c);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExprWriter appendLiteral(String s) throws IOException {
|
||||
if (!Lexer.isValidId(s)) {
|
||||
if (s.contains("`")) throw new IllegalArgumentException("Not a valid literal: " + s);
|
||||
else return append('`').append(s).append('`');
|
||||
} else return append(s);
|
||||
}
|
||||
|
||||
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,273 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.compiler;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* The lexer for muScript, heavily inspired by starscript
|
||||
* @deprecated To be moved into the lexer module and refactored
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public class Lexer extends VersionedComponent {
|
||||
public final String file;
|
||||
|
||||
/**
|
||||
* The type of the token
|
||||
*/
|
||||
public Token token;
|
||||
/**
|
||||
* The string representation of the token
|
||||
*/
|
||||
public String lexeme;
|
||||
|
||||
public char ch;
|
||||
|
||||
public final String source;
|
||||
public int start, current;
|
||||
|
||||
public boolean passedNewline = false;
|
||||
|
||||
public Lexer(MuScriptVersion version, String source) {
|
||||
this(version, source, null);
|
||||
}
|
||||
|
||||
public Lexer(MuScriptVersion version, String source, String file) {
|
||||
super(version);
|
||||
this.source = Objects.requireNonNull(source);
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public CodeLocation location() {
|
||||
return new CodeLocation(start, current - 1, source, file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans for the next token storing it in {@link Lexer#token} and {@link Lexer#lexeme}. Produces {@link Token#EOF} if the end of source code has been reached and {@link Token#Error} if there has been an error
|
||||
*/
|
||||
public void next() {
|
||||
start = current;
|
||||
passedNewline = false;
|
||||
|
||||
if (isAtEnd()) {
|
||||
createToken(Token.EOF);
|
||||
return;
|
||||
}
|
||||
|
||||
// Scan expression
|
||||
skipWhitespaceAndComments();
|
||||
if (isAtEnd()) {
|
||||
createToken(Token.EOF);
|
||||
return;
|
||||
}
|
||||
|
||||
char c = advance();
|
||||
|
||||
if (isDigit(c)) number();
|
||||
else if (isIdentifier(c)) identifier(false);
|
||||
else {
|
||||
switch (c) {
|
||||
case '\'', '"' -> string(c);
|
||||
|
||||
case '`' -> identifier(true);
|
||||
|
||||
case '=' -> {
|
||||
if (match('=')) createToken(Token.EqualEqual);
|
||||
else createToken(Token.Assign);
|
||||
}
|
||||
case '!' -> createToken(match('=') ? Token.BangEqual : Token.Bang);
|
||||
|
||||
case '+' -> createToken(Token.Plus);
|
||||
case '-' -> {
|
||||
if (match('>')) createToken(Token.Arrow);
|
||||
else createToken(Token.Minus);
|
||||
}
|
||||
case '*' -> createToken(Token.Star);
|
||||
case '/' -> createToken(Token.Slash);
|
||||
case '%' -> createToken(Token.Percentage);
|
||||
case '>' -> createToken(match('=') ? Token.GreaterEqual : Token.Greater);
|
||||
case '<' -> createToken(match('=') ? Token.LessEqual : Token.Less);
|
||||
|
||||
case '&' -> createToken(Token.And);
|
||||
case '|' -> createToken(match('|') ? Token.Concat : Token.Or);
|
||||
case '^' -> createToken(Token.UpArrow);
|
||||
|
||||
case '.' -> {
|
||||
if (match('.')) {
|
||||
if (match('.')) createToken(Token.Ellipsis);
|
||||
else createToken(Token.Error, "Unexpected '..', did you mean '...'?");
|
||||
} else createToken(Token.Dot);
|
||||
}
|
||||
case ',' -> createToken(Token.Comma);
|
||||
case '?' -> createToken(Token.QuestionMark);
|
||||
case ':' -> createToken(match(':') ? Token.DoubleColon : Token.Colon);
|
||||
case '(' -> createToken(Token.LeftParen);
|
||||
case ')' -> createToken(Token.RightParen);
|
||||
case '[' -> createToken(Token.LeftBracket);
|
||||
case ']' -> createToken(Token.RightBracket);
|
||||
case '{' -> createToken(Token.LeftBrace);
|
||||
case '}' -> createToken(Token.RightBrace);
|
||||
|
||||
case ';' -> {
|
||||
createToken(Token.Semicolon);
|
||||
passedNewline = true;
|
||||
}
|
||||
|
||||
default -> unexpected();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void string(char stringChar) {
|
||||
while (!isAtEnd() && peek() != stringChar) {
|
||||
advance();
|
||||
}
|
||||
|
||||
if (isAtEnd()) {
|
||||
createToken(Token.Error, "Unterminated expression");
|
||||
} else {
|
||||
advance();
|
||||
createToken(Token.String, source.substring(start + 1, current - 1));
|
||||
}
|
||||
}
|
||||
|
||||
private void number() {
|
||||
while (isDigit(peek())) advance();
|
||||
|
||||
if (peek() == '.' && isDigit(peekNext())) {
|
||||
advance();
|
||||
|
||||
while (isDigit(peek())) advance();
|
||||
}
|
||||
|
||||
createToken(Token.Number);
|
||||
}
|
||||
|
||||
private void identifier(boolean explicit) {
|
||||
if (explicit) {
|
||||
while (!isAtEnd() && peek() != '`') {
|
||||
advance();
|
||||
}
|
||||
if (isAtEnd()) {
|
||||
createToken(Token.Error, "Unterminated expression");
|
||||
} else {
|
||||
advance();
|
||||
createToken(Token.Identifier, source.substring(start + 1, current - 1));
|
||||
}
|
||||
} else {
|
||||
while (!isAtEnd()) {
|
||||
char c = peek();
|
||||
if (isIdentifier(c) || isDigit(c)) {
|
||||
advance();
|
||||
} else break;
|
||||
}
|
||||
|
||||
createToken(Token.Identifier);
|
||||
|
||||
switch (lexeme) {
|
||||
case "null" -> token = Token.Null;
|
||||
case "true" -> token = Token.True;
|
||||
case "false" -> token = Token.False;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void skipWhitespaceAndComments() {
|
||||
while (true) {
|
||||
if (isAtEnd()) return;
|
||||
|
||||
switch (peek()) {
|
||||
case ' ', '\t' -> advance();
|
||||
case '\r', '\n' -> {
|
||||
advance();
|
||||
passedNewline = true;
|
||||
}
|
||||
case '/' -> {
|
||||
switch (peekNext()) {
|
||||
case '/' -> {
|
||||
while (!isAtEnd() && peek() != '\r' && peek() != '\n') advance();
|
||||
}
|
||||
case '*' -> {
|
||||
advance();
|
||||
advance();
|
||||
while (!isAtEnd() && (peek() != '*' || peekNext() != '/')) advance();
|
||||
if (!isAtEnd()) {
|
||||
advance();
|
||||
advance();
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
start = current;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
start = current;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
private void unexpected() {
|
||||
createToken(Token.Error, "Unexpected character");
|
||||
}
|
||||
|
||||
private void createToken(Token token, String lexeme) {
|
||||
this.token = token;
|
||||
this.lexeme = lexeme;
|
||||
}
|
||||
|
||||
private void createToken(Token token) {
|
||||
createToken(token, source.substring(start, current));
|
||||
}
|
||||
|
||||
private boolean match(char expected) {
|
||||
if (isAtEnd()) return false;
|
||||
if (source.charAt(current) != expected) return false;
|
||||
|
||||
advance();
|
||||
return true;
|
||||
}
|
||||
|
||||
private char advance() {
|
||||
return ch = source.charAt(current++);
|
||||
}
|
||||
|
||||
private char peek() {
|
||||
if (isAtEnd()) return '\0';
|
||||
return source.charAt(current);
|
||||
}
|
||||
|
||||
private char peekNext() {
|
||||
if (current + 1 >= source.length()) return '\0';
|
||||
return source.charAt(current + 1);
|
||||
}
|
||||
|
||||
private boolean isAtEnd() {
|
||||
return current >= source.length();
|
||||
}
|
||||
|
||||
private boolean isDigit(char c) {
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
private boolean isIdentifier(char c) {
|
||||
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() {
|
||||
return source.substring(0, start) + '[' + source.substring(start, current) + ']' + source.substring(current);
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.compiler;
|
||||
|
||||
public enum MuScriptVersion {
|
||||
V1, V2, V3;
|
||||
|
||||
public static final MuScriptVersion DEFAULT = V3;
|
||||
|
||||
public boolean contains(MuScriptVersion version) {
|
||||
return compareTo(version) >= 0;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.compiler;
|
||||
|
||||
public enum Order {
|
||||
Script,
|
||||
Conditional,
|
||||
And,
|
||||
Or,
|
||||
Equality,
|
||||
Concat,
|
||||
Comparison,
|
||||
Term,
|
||||
Factor,
|
||||
Exp,
|
||||
Unary,
|
||||
Call,
|
||||
Primary;
|
||||
}
|
|
@ -1,508 +0,0 @@
|
|||
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.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.dynamic.assign.DynamicAssign;
|
||||
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.compiler.lexer.LegacyLexer;
|
||||
import io.gitlab.jfronny.muscript.compiler.lexer.Lexer;
|
||||
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.*;
|
||||
|
||||
public class Parser extends VersionedComponent {
|
||||
private final Lexer lexer;
|
||||
|
||||
private Lexer.Token previous = null;
|
||||
|
||||
public static Expr<?> parse(MuScriptVersion version, String source) {
|
||||
return parse(version, source, null);
|
||||
}
|
||||
|
||||
public static Expr<?> parse(MuScriptVersion version, String source, String file) {
|
||||
return new Parser(new LegacyLexer(version, source, file)).parse().optimize();
|
||||
}
|
||||
|
||||
public static Script parseScript(MuScriptVersion version, String source) {
|
||||
return parseScript(version, source, null);
|
||||
}
|
||||
|
||||
public static Script parseScript(MuScriptVersion version, String source, String file) {
|
||||
return new Parser(new LegacyLexer(version, source, file)).parseScript().optimize();
|
||||
}
|
||||
|
||||
public static Script parseMultiScript(MuScriptVersion version, String startFile, SourceFS filesystem) {
|
||||
return new Script(parseMultiScript(version, startFile, filesystem, new HashSet<>()).stream().flatMap(Script::stream).toList());
|
||||
}
|
||||
|
||||
private static List<Script> parseMultiScript(MuScriptVersion version, String startFile, SourceFS filesystem, Set<String> alreadyIncluded) {
|
||||
alreadyIncluded.add(startFile);
|
||||
boolean isIncludes = true;
|
||||
StringBuilder src = new StringBuilder();
|
||||
List<Script> includes = new LinkedList<>();
|
||||
int row = 0;
|
||||
final String includePrefix = "#include ";
|
||||
for (String s : filesystem.read(startFile).split("\n")) {
|
||||
row++;
|
||||
if (s.isBlank()) {
|
||||
src.append("\n");
|
||||
} else if (s.startsWith(includePrefix)) {
|
||||
if (isIncludes) {
|
||||
String file = s.substring(includePrefix.length());
|
||||
src.append("// include ").append(file).append("\n");
|
||||
if (!alreadyIncluded.contains(file)) {
|
||||
includes.addAll(parseMultiScript(version, file, filesystem, alreadyIncluded));
|
||||
}
|
||||
} else {
|
||||
throw new ParseException(PrettyPrintError.builder()
|
||||
.setLocation(new PrettyPrintError.Location(s, 0, row), new PrettyPrintError.Location(s, s.length() - 1, row))
|
||||
.setMessage("Includes MUST be located at the top of the file")
|
||||
.build());
|
||||
}
|
||||
} else {
|
||||
isIncludes = false;
|
||||
src.append(s).append("\n");
|
||||
}
|
||||
}
|
||||
includes.add(parseScript(version, src.toString(), startFile));
|
||||
return includes;
|
||||
}
|
||||
|
||||
public Parser(io.gitlab.jfronny.muscript.compiler.Lexer lexer) {
|
||||
this(new LegacyLexer(lexer));
|
||||
}
|
||||
|
||||
public Parser(Lexer lexer) {
|
||||
super(lexer.version());
|
||||
this.lexer = lexer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a single expression.
|
||||
* Must exhaust the source!
|
||||
*
|
||||
* @return the resulting expression
|
||||
*/
|
||||
public Expr<?> parse() {
|
||||
advance();
|
||||
Expr<?> expr = expression();
|
||||
if (!isAtEnd() && version.contains(MuScriptVersion.V2))
|
||||
throw new ParseException(PrettyPrintError.builder(lexer.location()).setMessage("Unexpected element after end of expression").build());
|
||||
return expr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a script instance.
|
||||
* Multiple instructions will be executed in sequence and the result of the last one will be returned.
|
||||
*
|
||||
* @return the resulting expression
|
||||
*/
|
||||
public Script parseScript() {
|
||||
advance();
|
||||
List<Expr<?>> expressions = new LinkedList<>();
|
||||
while (!isAtEnd()) {
|
||||
expressions.add(expression());
|
||||
// Consume semicolon if present
|
||||
if (!lexer.wasNewlinePassed() & !match(Token.Semicolon) & !isAtEnd() & version.contains(MuScriptVersion.V3)) {
|
||||
throw error("Either a semicolon or a new line must separate expressions in scripts");
|
||||
}
|
||||
}
|
||||
if (expressions.isEmpty()) throw new ParseException(PrettyPrintError.builder(lexer.location()).setMessage("Missing any elements in closure").build());
|
||||
return new Script(expressions);
|
||||
}
|
||||
|
||||
// Expressions
|
||||
private Expr<?> expression() {
|
||||
try {
|
||||
return conditional();
|
||||
} catch (RuntimeException e) {
|
||||
if (e instanceof ParseException) throw e;
|
||||
else if (e instanceof LocationalException le) {
|
||||
throw new ParseException(le.asPrintable(), le.getCause());
|
||||
} else throw error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private Expr<?> conditional() {
|
||||
Expr<?> expr = and();
|
||||
|
||||
if (match(Token.QuestionMark)) {
|
||||
CodeLocation location = previous.location();
|
||||
Expr<?> trueExpr = expression();
|
||||
consume(Token.Colon, "Expected ':' after first part of condition");
|
||||
Expr<?> falseExpr = expression();
|
||||
expr = new UnresolvedConditional(location, asBool(expr), trueExpr, falseExpr);
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
private Expr<?> and() {
|
||||
Expr<?> expr = or();
|
||||
|
||||
while (match(Token.And)) {
|
||||
CodeLocation location = previous.location();
|
||||
Expr<?> right = or();
|
||||
expr = new And(location, asBool(expr), asBool(right));
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
private Expr<?> or() {
|
||||
Expr<?> expr = equality();
|
||||
|
||||
while (match(Token.Or)) {
|
||||
CodeLocation location = previous.location();
|
||||
Expr<?> right = equality();
|
||||
expr = new Or(location, asBool(expr), asBool(right));
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
private Expr<?> equality() {
|
||||
Expr<?> expr = concat();
|
||||
|
||||
while (match(Token.EqualEqual, Token.BangEqual)) {
|
||||
Token op = previous.token();
|
||||
CodeLocation location = previous.location();
|
||||
Expr<?> right = concat();
|
||||
BoolExpr e = new Equal(location, expr, right);
|
||||
if (op == Token.BangEqual) e = new Not(location, e);
|
||||
expr = e;
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
private Expr<?> concat() {
|
||||
Expr<?> expr = comparison();
|
||||
|
||||
while (match(Token.Concat)) {
|
||||
CodeLocation location = previous.location();
|
||||
Expr<?> right = comparison();
|
||||
expr = new Concatenate(location, asString(expr), asString(right));
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
private Expr<?> comparison() {
|
||||
Expr<?> expr = term();
|
||||
|
||||
while (match(Token.Greater, Token.GreaterEqual, Token.Less, Token.LessEqual)) {
|
||||
Token op = previous.token();
|
||||
CodeLocation location = previous.location();
|
||||
NumberExpr right = asNumber(term());
|
||||
expr = switch (op) {
|
||||
case Greater -> new Greater(location, asNumber(expr), right);
|
||||
case GreaterEqual -> new Not(location, new Greater(location, right, asNumber(expr)));
|
||||
case Less -> new Greater(location, right, asNumber(expr));
|
||||
case LessEqual -> new Not(location, new Greater(location, asNumber(expr), right));
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
private Expr<?> term() {
|
||||
Expr<?> expr = factor();
|
||||
|
||||
while (match(Token.Plus, Token.Minus)) {
|
||||
Token op = previous.token();
|
||||
CodeLocation location = previous.location();
|
||||
NumberExpr right = asNumber(factor());
|
||||
expr = switch (op) {
|
||||
case Plus -> new Plus(location, asNumber(expr), right);
|
||||
case Minus -> new Minus(location, asNumber(expr), right);
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
private Expr<?> factor() {
|
||||
Expr<?> expr = exp();
|
||||
|
||||
while (match(Token.Star, Token.Slash, Token.Percentage)) {
|
||||
Token op = previous.token();
|
||||
CodeLocation location = previous.location();
|
||||
NumberExpr right = asNumber(exp());
|
||||
expr = switch (op) {
|
||||
case Star -> new Multiply(location, asNumber(expr), right);
|
||||
case Slash -> new Divide(location, asNumber(expr), right);
|
||||
case Percentage -> new Modulo(location, asNumber(expr), right);
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
private Expr<?> exp() {
|
||||
Expr<?> expr = unary();
|
||||
|
||||
while (match(Token.UpArrow)) {
|
||||
CodeLocation location = previous.location();
|
||||
NumberExpr right = asNumber(unary());
|
||||
expr = new Power(location, asNumber(expr), right);
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
private Expr<?> unary() {
|
||||
if (match(Token.Bang, Token.Minus)) {
|
||||
Token op = previous.token();
|
||||
CodeLocation location = previous.location();
|
||||
Expr<?> right = unary();
|
||||
return switch (op) {
|
||||
case Bang -> new Not(location, asBool(right));
|
||||
case Minus -> new Invert(location, asNumber(right));
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
}
|
||||
|
||||
return call();
|
||||
}
|
||||
|
||||
private Expr<?> call() {
|
||||
Expr<?> expr = primary();
|
||||
|
||||
while (match(Token.LeftParen, Token.Dot, Token.LeftBracket, Token.DoubleColon)) {
|
||||
CodeLocation location = previous.location();
|
||||
expr = switch (previous.token()) {
|
||||
case LeftParen -> finishCall(location, expr);
|
||||
case Dot -> {
|
||||
Lexer.Token name = consume(Token.Identifier, "Expected field name after '.'");
|
||||
yield new Get(location, asDynamic(expr), Expr.literal(name.location(), name.lexeme()));
|
||||
}
|
||||
case DoubleColon -> {
|
||||
DynamicExpr callable;
|
||||
if (match(Token.Identifier)) {
|
||||
callable = new Variable(previous.location(), previous.lexeme());
|
||||
} else if (match(Token.LeftParen)) {
|
||||
callable = expression().asDynamicExpr();
|
||||
consume(Token.RightParen, "Expected ')' after expression");
|
||||
} else throw error("Bind operator requires right side to be a literal identifier or to be wrapped in parentheses.");
|
||||
yield new Bind(location, callable, expr.asDynamicExpr());
|
||||
}
|
||||
case LeftBracket -> {
|
||||
expr = new Get(location, asDynamic(expr), expression());
|
||||
consume(Token.RightBracket, "Expected closing bracket");
|
||||
yield expr;
|
||||
}
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
private Expr<?> finishCall(CodeLocation location, Expr<?> callee) {
|
||||
List<Call.Arg> args = new ArrayList<>(2);
|
||||
|
||||
if (!check(Token.RightParen)) {
|
||||
do {
|
||||
args.add(new Call.Arg(asDynamic(expression()), match(Token.Ellipsis)));
|
||||
} while (match(Token.Comma));
|
||||
}
|
||||
|
||||
consume(Token.RightParen, "Expected ')' after function arguments");
|
||||
return new Call(location, asDynamic(callee), args);
|
||||
}
|
||||
|
||||
private Expr<?> primary() {
|
||||
if (match(Token.Null)) return Expr.literalNull(previous.location());
|
||||
if (match(Token.String)) return Expr.literal(previous.location(), previous.lexeme());
|
||||
if (match(Token.True, Token.False)) return Expr.literal(previous.location(), previous.lexeme().equals("true"));
|
||||
if (match(Token.Number)) return Expr.literal(previous.location(), Double.parseDouble(previous.lexeme()));
|
||||
if (match(Token.Identifier)) {
|
||||
CodeLocation location = previous.location();
|
||||
String name = previous.lexeme();
|
||||
if (match(Token.Assign)) return new DynamicAssign(location, name, expression().asDynamicExpr());
|
||||
else return new Variable(location, name);
|
||||
}
|
||||
|
||||
if (match(Token.LeftParen)) {
|
||||
Expr<?> expr = expression();
|
||||
consume(Token.RightParen, "Expected ')' after expression");
|
||||
return expr;
|
||||
}
|
||||
|
||||
if (match(Token.LeftBrace)) {
|
||||
int start = previous.start();
|
||||
if (match(Token.Arrow)) return finishClosure(start, null, false);
|
||||
if (match(Token.RightBrace)) return new DynamicLiteral<>(location(start, previous.start()), DFinal.of(Map.of()));
|
||||
consume(Token.Identifier, "Expected arrow or identifier as first element in closure or object");
|
||||
String first = previous.lexeme();
|
||||
if (check(Token.Arrow)) return finishClosure(start, first, false);
|
||||
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());
|
||||
}
|
||||
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());
|
||||
// Consume semicolon if present
|
||||
if (!lexer.wasNewlinePassed() & !match(Token.Semicolon) & version.contains(MuScriptVersion.V3)) {
|
||||
if (match(Token.RightBrace)) break;
|
||||
throw error("Either a semicolon or a new line must separate expressions in closures");
|
||||
}
|
||||
}
|
||||
int end = previous.start();
|
||||
return new Closure(location(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(location(start, previous.start()), content);
|
||||
}
|
||||
|
||||
// Type conversion
|
||||
private BoolExpr asBool(Expr<?> expression) {
|
||||
try {
|
||||
return expression.asBoolExpr();
|
||||
} catch (TypeMismatchException e) {
|
||||
throw error(e.getMessage(), expression);
|
||||
}
|
||||
}
|
||||
|
||||
private NumberExpr asNumber(Expr<?> expression) {
|
||||
try {
|
||||
return expression.asNumberExpr();
|
||||
} catch (TypeMismatchException e) {
|
||||
throw error(e.getMessage(), expression);
|
||||
}
|
||||
}
|
||||
|
||||
private StringExpr asString(Expr<?> expression) {
|
||||
try {
|
||||
return expression.asStringExpr();
|
||||
} catch (TypeMismatchException e) {
|
||||
throw error(e.getMessage(), expression);
|
||||
}
|
||||
}
|
||||
|
||||
private DynamicExpr asDynamic(Expr<?> expression) {
|
||||
try {
|
||||
return expression.asDynamicExpr();
|
||||
} catch (TypeMismatchException e) {
|
||||
throw error(e.getMessage(), expression);
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers
|
||||
private CodeLocation location(int chStart, int chEnd) {
|
||||
return new CodeLocation(chStart, chEnd, lexer.getSource(), lexer.getFile());
|
||||
}
|
||||
|
||||
private ParseException error(String message) {
|
||||
int loc = lexer.getPrevious().current() - 1;
|
||||
return new ParseException(PrettyPrintError.builder(location(loc, loc)).setMessage(message).build());
|
||||
}
|
||||
|
||||
private ParseException error(String message, Expr<?> expr) {
|
||||
return new ParseException(PrettyPrintError.builder(expr.location).setMessage(message).build());
|
||||
}
|
||||
|
||||
private Lexer.Token consume(Token token, String message) {
|
||||
if (check(token)) return advance();
|
||||
throw error(message + " but got " + lexer.getPrevious().token());
|
||||
}
|
||||
|
||||
private boolean match(Token... tokens) {
|
||||
for (Token token : tokens) {
|
||||
if (check(token)) {
|
||||
advance();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean check(Token token) {
|
||||
if (isAtEnd()) return false;
|
||||
return lexer.getPrevious().token() == token;
|
||||
}
|
||||
|
||||
private Lexer.Token advance() {
|
||||
previous = lexer.getPrevious();
|
||||
|
||||
lexer.advance();
|
||||
|
||||
if (lexer.getPrevious().token() == Token.Error) {
|
||||
throw error(lexer.getPrevious().lexeme());
|
||||
}
|
||||
|
||||
return previous;
|
||||
}
|
||||
|
||||
private boolean isAtEnd() {
|
||||
return lexer.getPrevious().token() == Token.EOF;
|
||||
}
|
||||
|
||||
// Parse Exception
|
||||
public static class ParseException extends RuntimeException {
|
||||
public final PrettyPrintError error;
|
||||
|
||||
public ParseException(PrettyPrintError error) {
|
||||
super(error.toString());
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public ParseException(PrettyPrintError error, Throwable cause) {
|
||||
super(error.toString(), cause);
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.compiler;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface SourceFS {
|
||||
String read(String file);
|
||||
|
||||
record Immutable(Map<String, String> files) implements SourceFS {
|
||||
@Override
|
||||
public String read(String file) {
|
||||
return files.get(file);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.compiler;
|
||||
|
||||
public enum Token {
|
||||
String, Identifier, Number,
|
||||
|
||||
Null,
|
||||
True, False,
|
||||
And, Or,
|
||||
|
||||
Assign,
|
||||
EqualEqual, BangEqual,
|
||||
|
||||
Concat,
|
||||
|
||||
Greater, GreaterEqual,
|
||||
Less, LessEqual,
|
||||
|
||||
Plus, Minus,
|
||||
Star, Slash, Percentage, UpArrow,
|
||||
Bang,
|
||||
|
||||
Dot, Ellipsis, Comma,
|
||||
QuestionMark, Colon,
|
||||
LeftParen, RightParen,
|
||||
LeftBracket, RightBracket,
|
||||
|
||||
LeftBrace, RightBrace,
|
||||
Arrow,
|
||||
|
||||
Semicolon,
|
||||
|
||||
DoubleColon,
|
||||
|
||||
Error, EOF
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.compiler;
|
||||
|
||||
public enum Type {
|
||||
String, Number, Boolean, Dynamic
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.compiler;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class VersionedComponent {
|
||||
protected final MuScriptVersion version;
|
||||
|
||||
public VersionedComponent(MuScriptVersion version) {
|
||||
this.version = Objects.requireNonNull(version);
|
||||
}
|
||||
|
||||
public MuScriptVersion getComponentVersion() {
|
||||
return version;
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.compiler.lexer;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Wraps the old Lexer implementation in the new Lexer interface for compatibility
|
||||
*/
|
||||
public class LegacyLexer implements Lexer {
|
||||
private final io.gitlab.jfronny.muscript.compiler.Lexer backend;
|
||||
private Lexer.Token previous = null;
|
||||
|
||||
public LegacyLexer(MuScriptVersion version, String source) {
|
||||
this(new io.gitlab.jfronny.muscript.compiler.Lexer(version, source));
|
||||
}
|
||||
|
||||
public LegacyLexer(MuScriptVersion version, String source, String file) {
|
||||
this(new io.gitlab.jfronny.muscript.compiler.Lexer(version, source, file));
|
||||
}
|
||||
|
||||
public LegacyLexer(io.gitlab.jfronny.muscript.compiler.Lexer backend) {
|
||||
this.backend = backend;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeLocation location() {
|
||||
return backend.location();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MuScriptVersion version() {
|
||||
return backend.getComponentVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wasNewlinePassed() {
|
||||
return backend.passedNewline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token getPrevious() {
|
||||
return previous;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token advance() {
|
||||
backend.next();
|
||||
return previous = new Token(
|
||||
backend.lexeme,
|
||||
backend.token,
|
||||
backend.start,
|
||||
backend.current,
|
||||
backend.ch,
|
||||
new CodeLocation(backend.start, backend.current - 1, backend.source, backend.file)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getSource() {
|
||||
return backend.source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getFile() {
|
||||
return backend.file;
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.compiler.lexer;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface Lexer {
|
||||
CodeLocation location();
|
||||
MuScriptVersion version();
|
||||
boolean wasNewlinePassed();
|
||||
Token getPrevious();
|
||||
Token advance();
|
||||
|
||||
@Nullable String getSource();
|
||||
@Nullable String getFile();
|
||||
|
||||
record Token(String lexeme, io.gitlab.jfronny.muscript.compiler.Token token, int start, int current, char ch, CodeLocation location) {
|
||||
@Override
|
||||
public String toString() {
|
||||
return token + " '" + lexeme + "'";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
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 io.gitlab.jfronny.muscript.data.dynamic.type.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class Scope implements DObject {
|
||||
private final @Nullable Scope source;
|
||||
private final Map<String, Dynamic> override = new HashMap<>();
|
||||
|
||||
public Scope() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public Scope(@Nullable DObject source) {
|
||||
if (source == null) {
|
||||
this.source = null;
|
||||
} else if (source instanceof Scope src) {
|
||||
this.source = src;
|
||||
} else {
|
||||
this.source = new Scope();
|
||||
source.getValue().forEach(this::set);
|
||||
}
|
||||
}
|
||||
|
||||
public Scope(@Nullable Scope source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Dynamic> getValue() {
|
||||
if (source == null) return ImmCollection.of(override);
|
||||
var map = new HashMap<>(source.getValue());
|
||||
map.putAll(override);
|
||||
return ImmCollection.of(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Using the convenience methods is recommended wherever possible.
|
||||
*/
|
||||
@Deprecated(forRemoval = false)
|
||||
public Scope set(String key, Dynamic value) {
|
||||
if (key.startsWith("$")) {
|
||||
if (!setGlobal(key, value)) override.put(key, value);
|
||||
} else {
|
||||
override.put(key, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private boolean setGlobal(String key, Dynamic value) {
|
||||
if (override.containsKey(key)) {
|
||||
override.put(key, value);
|
||||
return true;
|
||||
} else if (source != null) {
|
||||
return source.setGlobal(key, value);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Scope set(String key, boolean value) {
|
||||
return set(key, DFinal.of(value));
|
||||
}
|
||||
|
||||
public Scope set(String key, double value) {
|
||||
return set(key, DFinal.of(value));
|
||||
}
|
||||
|
||||
public Scope set(String key, String value) {
|
||||
return set(key, DFinal.of(value));
|
||||
}
|
||||
|
||||
public Scope set(String key, Map<String, ? extends Dynamic> value) {
|
||||
return set(key, DFinal.of(value));
|
||||
}
|
||||
|
||||
public Scope set(String key, DTypeObject signature, Map<String, ? extends Dynamic> value) {
|
||||
return set(key, DFinal.of(signature, value));
|
||||
}
|
||||
|
||||
public Scope set(String key, List<? extends Dynamic> value) {
|
||||
return set(key, DFinal.of(value));
|
||||
}
|
||||
|
||||
public Scope set(String key, DTypeList signature, List<? extends Dynamic> value) {
|
||||
return set(key, DFinal.of(signature, value));
|
||||
}
|
||||
|
||||
public Scope set(String key, Function<DList, ? extends Dynamic> value) {
|
||||
return set(key, DFinal.of(value, () -> key, key));
|
||||
}
|
||||
|
||||
public Scope set(String key, DType signature, Function<DList, ? extends Dynamic> value) {
|
||||
return set(key, DFinal.of(signature, value, () -> key, key));
|
||||
}
|
||||
|
||||
public Scope fork() {
|
||||
return new Scope(this);
|
||||
}
|
||||
|
||||
public DObject getOverrides() {
|
||||
return DFinal.of(override);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Dynamic.serialize(this);
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data;
|
||||
|
||||
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.ast.dynamic.ExprGroup;
|
||||
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.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Script extends Decompilable {
|
||||
private final DynamicExpr content;
|
||||
|
||||
public Script(List<Expr<?>> expressions) {
|
||||
this(expressions, null);
|
||||
}
|
||||
|
||||
public Script(List<Expr<?>> expressions, String file) {
|
||||
this(new ExprGroup(CodeLocation.NONE.withFile(file), expressions));
|
||||
}
|
||||
|
||||
private Script(DynamicExpr content) {
|
||||
super(Order.Script);
|
||||
this.content = Objects.requireNonNull(content);
|
||||
}
|
||||
|
||||
public Dynamic run(DObject scope) {
|
||||
return content.get(scope);
|
||||
}
|
||||
|
||||
public Dynamic run(Scope scope) {
|
||||
return content.get(scope);
|
||||
}
|
||||
|
||||
public DCallable bindTo(Scope scope) {
|
||||
return DFinal.of("<root>", args -> {
|
||||
scope.set("args", args);
|
||||
return run(scope);
|
||||
}, this::asExpr);
|
||||
}
|
||||
|
||||
public DynamicExpr asExpr() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public Script optimize() {
|
||||
return new Script(content.optimize());
|
||||
}
|
||||
|
||||
public Script concat(Script other) {
|
||||
return new Script(Stream.concat(this.stream(), other.stream()).toList());
|
||||
}
|
||||
|
||||
public Stream<Expr<?>> stream() {
|
||||
return content instanceof ExprGroup g
|
||||
? g.stream()
|
||||
: Stream.of(content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompile(ExprWriter writer) throws IOException {
|
||||
for (Expr<?> expr : stream().toList()) {
|
||||
expr.decompile(writer);
|
||||
writer.append(";\n");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.BoolExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
|
||||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
|
||||
public non-sealed interface DBool extends Dynamic {
|
||||
@Override
|
||||
default BoolExpr toExpr() {
|
||||
return new BoolLiteral(CodeLocation.NONE, getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
Boolean getValue();
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.NamedDCallable;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public non-sealed interface DCallable extends Dynamic {
|
||||
String DEFAULT_NAME = "<unnamed>";
|
||||
|
||||
default Dynamic call(DList args) {
|
||||
DType signature = getSignature();
|
||||
if (signature != null) {
|
||||
List<DType> argTypes = args.getValue().stream().map(Dynamic::getSignature).toList();
|
||||
if (!TypeMatcher.match(signature, argTypes)) {
|
||||
List<DTypeCallable.Arg> extraArgs = argTypes.stream().map(s -> new DTypeCallable.Arg(null, s, false)).toList();
|
||||
DTypeCallable callSignature = new DTypeCallable(extraArgs, null);
|
||||
throw new IllegalArgumentException("Signature mismatch for " + getName() + ": expected <" + signature + "> but got <" + callSignature + ">");
|
||||
}
|
||||
}
|
||||
return getValue().apply(args);
|
||||
}
|
||||
|
||||
default Dynamic call(Dynamic... args) {
|
||||
return call(DFinal.of(args));
|
||||
}
|
||||
|
||||
default String getName() {
|
||||
return DEFAULT_NAME;
|
||||
}
|
||||
|
||||
default DCallable named(String name) {
|
||||
return new NamedDCallable(this, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
Function<DList, Dynamic> getValue();
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.Call;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.Variable;
|
||||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public non-sealed interface DList extends Dynamic {
|
||||
default Dynamic get(int i) {
|
||||
return getValue().get(i);
|
||||
}
|
||||
|
||||
default int size() {
|
||||
return getValue().size();
|
||||
}
|
||||
|
||||
default boolean isEmpty() {
|
||||
return getValue().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
default DynamicExpr toExpr() {
|
||||
return new Call(CodeLocation.NONE, new Variable(CodeLocation.NONE, "listOf"), getValue().stream().map(s -> new Call.Arg(s.toExpr().asDynamicExpr(), false)).toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
List<? extends Dynamic> getValue();
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.NullLiteral;
|
||||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.DType;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.DTypePrimitive;
|
||||
|
||||
public final class DNull implements Dynamic {
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NullLiteral toExpr() {
|
||||
return new NullLiteral(CodeLocation.NONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return DTypePrimitive.NULL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "null";
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.NumberExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
|
||||
public non-sealed interface DNumber extends Dynamic {
|
||||
@Override
|
||||
default NumberExpr toExpr() {
|
||||
return new NumberLiteral(CodeLocation.NONE, getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
Double getValue();
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.ObjectLiteral;
|
||||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public non-sealed interface DObject extends Dynamic {
|
||||
default @Nullable Dynamic get(String key) {
|
||||
return getValue().get(key);
|
||||
}
|
||||
|
||||
default boolean has(String key) {
|
||||
return getValue().containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
default DynamicExpr toExpr() {
|
||||
return new ObjectLiteral(CodeLocation.NONE, getValue().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, s -> s.getValue().toExpr().asDynamicExpr())));
|
||||
}
|
||||
|
||||
@Override
|
||||
Map<String, ? extends Dynamic> getValue();
|
||||
|
||||
@Override
|
||||
default DString asString() {
|
||||
return DFinal.of(getValue()
|
||||
.entrySet()
|
||||
.stream()
|
||||
.map(e -> e.getKey() + " = " + e.getValue().asString().getValue())
|
||||
.collect(Collectors.joining(", ", "{", "}")));
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.StringExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.StringLiteral;
|
||||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
|
||||
public non-sealed interface DString extends Dynamic {
|
||||
@Override
|
||||
default StringExpr toExpr() {
|
||||
return new StringLiteral(CodeLocation.NONE, getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
String getValue();
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.commons.StringFormatter;
|
||||
import io.gitlab.jfronny.muscript.compiler.MuScriptVersion;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.*;
|
||||
import io.gitlab.jfronny.muscript.libs.StandardLib;
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.compiler.Parser;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.lens.DStringLens;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
public sealed interface Dynamic permits DBool, DCallable, DList, DNull, DNumber, DObject, DString, DynamicBase {
|
||||
/**
|
||||
* Deserialize simply dynamic values. DOES NOT SUPPORT CALLABLES!
|
||||
* Use Parser.parse() if you truly need to deserialize them, but be aware that that might enable DOS attacks
|
||||
*/
|
||||
static Dynamic deserialize(String source) {
|
||||
DynamicExpr expr = Parser.parse(MuScriptVersion.DEFAULT, source).asDynamicExpr();
|
||||
if (!DirectPreconditionVisitor.visit(expr)) throw new IllegalArgumentException("This expression does not directly express a dynamic and may not be loaded this way");
|
||||
return expr.get(StandardLib.createScope(MuScriptVersion.DEFAULT));
|
||||
}
|
||||
|
||||
static String serialize(Dynamic dynamic) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try (ExprWriter ew = new ExprWriter(sb, true)) {
|
||||
dynamic.serialize(ew);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not stringify", e);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
static boolean isNull(Dynamic dynamic) {
|
||||
return dynamic == null || dynamic instanceof DNull;
|
||||
}
|
||||
|
||||
static @NotNull Dynamic fromNullable(@Nullable Dynamic dynamic) {
|
||||
return dynamic == null ? new DNull() : dynamic;
|
||||
}
|
||||
|
||||
default void serialize(ExprWriter writer) throws IOException {
|
||||
toExpr().decompile(writer);
|
||||
}
|
||||
|
||||
Expr<?> toExpr();
|
||||
Object getValue();
|
||||
|
||||
default boolean isBool() {
|
||||
return this instanceof DBool;
|
||||
}
|
||||
|
||||
default DBool asBool() {
|
||||
if (this instanceof DBool bool) return bool;
|
||||
else throw new DynamicTypeConversionException("bool", this);
|
||||
}
|
||||
|
||||
default boolean isNumber() {
|
||||
return this instanceof DNumber;
|
||||
}
|
||||
|
||||
default DNumber asNumber() {
|
||||
if (this instanceof DNumber number) return number;
|
||||
else throw new DynamicTypeConversionException("number", this);
|
||||
}
|
||||
|
||||
default boolean isString() {
|
||||
return this instanceof DString;
|
||||
}
|
||||
|
||||
default DString asString() {
|
||||
if (this instanceof DString string) return string;
|
||||
else return new DStringLens(this, () -> StringFormatter.toString(getValue()));
|
||||
}
|
||||
|
||||
default boolean isObject() {
|
||||
return this instanceof DObject;
|
||||
}
|
||||
|
||||
default DObject asObject() {
|
||||
if (this instanceof DObject object) return object;
|
||||
else throw new DynamicTypeConversionException("object", this);
|
||||
}
|
||||
|
||||
default boolean isList() {
|
||||
return this instanceof DList;
|
||||
}
|
||||
|
||||
default DList asList() {
|
||||
if (this instanceof DList list) return list;
|
||||
else throw new DynamicTypeConversionException("list", this);
|
||||
}
|
||||
|
||||
default boolean isCallable() {
|
||||
return this instanceof DCallable;
|
||||
}
|
||||
|
||||
default DCallable asCallable() {
|
||||
if (this instanceof DCallable callable) return callable;
|
||||
else throw new DynamicTypeConversionException("callable", this);
|
||||
}
|
||||
|
||||
default DType getSignature() {
|
||||
Set<DType> variants = new HashSet<>();
|
||||
if (isBool()) variants.add(DTypePrimitive.BOOL);
|
||||
if (isNumber()) variants.add(DTypePrimitive.NUMBER);
|
||||
if (isString()) variants.add(DTypePrimitive.STRING);
|
||||
if (isObject()) variants.add(new DTypeObject(null));
|
||||
if (isList()) variants.add(new DTypeList(null));
|
||||
if (isCallable()) variants.add(new DTypeCallable(null, null));
|
||||
return variants.size() == 1 ? variants.stream().findFirst().orElseThrow() : new DTypeAnd(variants);
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
|
||||
/**
|
||||
* Use this for ABSTRACT (!) implementations of custom dynamic containers.
|
||||
* Any concrete implementation MUST still implement a valid Dynamic subtype (DBool, DString, ...)
|
||||
*/
|
||||
public non-sealed interface DynamicBase extends Dynamic {
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
import io.gitlab.jfronny.muscript.error.LocationalException;
|
||||
|
||||
public class DynamicTypeConversionException extends RuntimeException {
|
||||
private static final String MESSAGE1 = "Could not convert dynamic of type ";
|
||||
private static final String MESSAGE2 = " to ";
|
||||
private final String target;
|
||||
private final String actual;
|
||||
|
||||
public DynamicTypeConversionException(String target, Dynamic dynamic) {
|
||||
super(MESSAGE1 + (dynamic == null ? "null" : dynamic.getClass().getSimpleName()) + MESSAGE2 + target);
|
||||
this.target = target;
|
||||
this.actual = dynamic == null ? "null" : dynamic.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
public LocationalException locational(CodeLocation location) {
|
||||
return new LocationalException(location, MESSAGE1 + actual + MESSAGE2 + target, this);
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.additional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.lens.DCallableLens;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public record DCallableObject(Map<String, ? extends Dynamic> value, DTypeObject objectSignature, DCallable callable) implements DObject {
|
||||
public DCallableObject(Map<String, ? extends Dynamic> value, DCallable callable) {
|
||||
this(value, new DTypeObject(null), callable);
|
||||
}
|
||||
|
||||
public DCallableObject {
|
||||
Objects.requireNonNull(value);
|
||||
Objects.requireNonNull(objectSignature);
|
||||
Objects.requireNonNull(callable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ? extends Dynamic> getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCallable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DCallable asCallable() {
|
||||
return new DCallableLens(this, callable::getValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr toExpr() {
|
||||
return new Call(CodeLocation.NONE, new Bind(CodeLocation.NONE, new Variable(CodeLocation.NONE, "callableObject"), DObject.super.toExpr()), List.of(new Call.Arg(callable.toExpr().asDynamicExpr(), false)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return new DTypeAnd(Set.of(objectSignature, callable.getSignature()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Dynamic.serialize(this);
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.additional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DynamicBase;
|
||||
|
||||
public abstract class DContainer<T> implements DynamicBase {
|
||||
private T value;
|
||||
|
||||
@Override
|
||||
public T getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public T setValue(T value) {
|
||||
if (value != null)
|
||||
this.value = value;
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Dynamic.serialize(this);
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.additional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.Call;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.Variable;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.lens.DNumberLens;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.lens.DStringLens;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static io.gitlab.jfronny.muscript.data.dynamic.type.DSL.*;
|
||||
import static io.gitlab.jfronny.muscript.data.dynamic.type.DSL.NUMBER;
|
||||
|
||||
public record DDate(Supplier<LocalDate> date) implements DObject {
|
||||
public static final DType SIGNATURE = object(NUMBER).or(STRING).or(NUMBER);
|
||||
|
||||
@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 boolean isString() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DString asString() {
|
||||
return new DStringLens(this, this::toString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNumber() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DNumber asNumber() {
|
||||
return new DNumberLens(this, () -> date.get().toEpochDay());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr toExpr() {
|
||||
return new Call(CodeLocation.NONE, new Variable(CodeLocation.NONE, "date"), List.of(
|
||||
new Call.Arg(new NumberLiteral(CodeLocation.NONE, date.get().getYear()).asDynamicExpr(), false),
|
||||
new Call.Arg(new NumberLiteral(CodeLocation.NONE, date.get().getMonthValue()).asDynamicExpr(), false),
|
||||
new Call.Arg(new NumberLiteral(CodeLocation.NONE, date.get().getDayOfMonth()).asDynamicExpr(), false)
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return SIGNATURE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return date.get().toString();
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.additional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.lens.DListLens;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public enum DEmpty implements DObject {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ? extends Dynamic> getValue() {
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isList() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DList asList() {
|
||||
return new DListLens(this, List::of);
|
||||
}
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.additional;
|
||||
|
||||
import io.gitlab.jfronny.commons.StringFormatter;
|
||||
import io.gitlab.jfronny.commons.data.ImmCollection;
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.Call;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.Variable;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.StringLiteral;
|
||||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.lens.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* An enum represented as an OObject.
|
||||
* May also have a selected value with automatic conversions to a number (index) and string
|
||||
*/
|
||||
public record DEnum(Map<String, ? extends Dynamic> values, @Nullable DEnumEntry value, @Nullable DType valueSignature) implements DObject {
|
||||
public DEnum(Map<String, ? extends Dynamic> values) {
|
||||
this(null, values);
|
||||
}
|
||||
|
||||
public DEnum(DType valueSignature, Map<String, ? extends Dynamic> values) {
|
||||
this(values, null, valueSignature);
|
||||
}
|
||||
|
||||
public DEnum(Map<String, ? extends Dynamic> values, @Nullable String value) {
|
||||
this(null, values, value);
|
||||
}
|
||||
|
||||
public DEnum(DType valueSignature, Map<String, ? extends Dynamic> values, @Nullable String value) {
|
||||
this(values, value == null ? null : new DEnumEntry(value, values.keySet().stream().toList().indexOf(value), true), valueSignature);
|
||||
}
|
||||
|
||||
public DEnum(List<String> values, String value) {
|
||||
this(createMap(values, value), value == null ? null : new DEnumEntry(value, values.indexOf(value), true), DTypePrimitive.STRING);
|
||||
}
|
||||
|
||||
public DEnum(List<String> values) {
|
||||
this(values, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ? extends Dynamic> getValue() {
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isList() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DList asList() {
|
||||
return new DListLens(this, () -> List.copyOf(values.values()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isString() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DString asString() {
|
||||
return value != null ? value : new DStringLens(this, () -> StringFormatter.toString(values));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNumber() {
|
||||
return value != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DNumber asNumber() {
|
||||
return value != null ? new DNumberLens(this, value::index) : DObject.super.asNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr toExpr() {
|
||||
List<Call.Arg> args = new LinkedList<>();
|
||||
args.add(new Call.Arg(DObject.super.toExpr(), false));
|
||||
if (value != null) args.add(new Call.Arg(new StringLiteral(CodeLocation.NONE, value.value).asDynamicExpr(), false));
|
||||
return new Call(CodeLocation.NONE, new Variable(CodeLocation.NONE, "enum"), args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
Set<DType> types = new HashSet<>();
|
||||
types.add(new DTypeObject(valueSignature));
|
||||
types.add(new DTypeList(valueSignature));
|
||||
types.add(DTypePrimitive.STRING);
|
||||
if (value != null) types.add(DTypePrimitive.NUMBER);
|
||||
return new DTypeAnd(types);
|
||||
}
|
||||
|
||||
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);
|
||||
result.put("value", v);
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
result.put(values.get(i), new DEnumEntry(values.get(i), i, values.get(i).equals(value)));
|
||||
}
|
||||
return ImmCollection.of(result);
|
||||
}
|
||||
|
||||
public record DEnumEntry(String value, int index, boolean selected) implements DString {
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNumber() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DNumber asNumber() {
|
||||
return new DNumberLens(this, this::index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBool() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBool asBool() {
|
||||
return new DBoolLens(this, this::selected);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,207 +0,0 @@
|
|||
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.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.Variable;
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class DFinal {
|
||||
public static DBool of(boolean b) {
|
||||
return new FBool(b);
|
||||
}
|
||||
|
||||
public static DNumber of(double b) {
|
||||
return new FNumber(b);
|
||||
}
|
||||
|
||||
public static DString of(String b) {
|
||||
return new FString(b);
|
||||
}
|
||||
|
||||
public static DObject of(Map<String, ? extends Dynamic> b) {
|
||||
return of(new DTypeObject(null), b);
|
||||
}
|
||||
|
||||
public static DObject of(DTypeObject signature, Map<String, ? extends Dynamic> b) {
|
||||
return new FObject(ImmCollection.copyOf((Map<String, Dynamic>) b), Objects.requireNonNull(signature));
|
||||
}
|
||||
|
||||
public static DList of(Dynamic... b) {
|
||||
return of(new DTypeList(null), b);
|
||||
}
|
||||
|
||||
public static DList of(DTypeList signature, Dynamic... b) {
|
||||
return new FList(List.of(b), Objects.requireNonNull(signature));
|
||||
}
|
||||
|
||||
public static DList of(List<? extends Dynamic> b) {
|
||||
return of(new DTypeList(null), b);
|
||||
}
|
||||
|
||||
public static DList of(DTypeList signature, List<? extends Dynamic> b) {
|
||||
return new FList(ImmCollection.copyOf((List<Dynamic>) b), Objects.requireNonNull(signature));
|
||||
}
|
||||
|
||||
public static DCallable of(Function<DList, ? extends Dynamic> b, Supplier<String> serialized) {
|
||||
return of(new DTypeCallable(null, null), b, Objects.requireNonNull(serialized));
|
||||
}
|
||||
|
||||
public static DCallable of(DType signature, Function<DList, ? extends Dynamic> b, Supplier<String> serialized) {
|
||||
return of(Objects.requireNonNull(signature), b, serialized, null);
|
||||
}
|
||||
|
||||
public static DCallable of(Function<DList, ? extends Dynamic> b, String name) {
|
||||
return of(new DTypeCallable(null, null), b, name);
|
||||
}
|
||||
|
||||
public static DCallable of(DType signature, Function<DList, ? extends Dynamic> b, String name) {
|
||||
return of(Objects.requireNonNull(signature), name, b, () -> new Variable(CodeLocation.NONE, name));
|
||||
}
|
||||
|
||||
public static DCallable of(Function<DList, ? extends Dynamic> b, Supplier<String> serialized, String name) {
|
||||
return of(new DTypeCallable(null, null), b, serialized, name);
|
||||
}
|
||||
|
||||
public static DCallable of(DType signature, Function<DList, ? extends Dynamic> b, Supplier<String> serialized, String name) {
|
||||
return of(Objects.requireNonNull(signature), name, b, () -> Parser.parse(MuScriptVersion.DEFAULT, serialized.get()));
|
||||
}
|
||||
|
||||
public static DCallable of(String name, Function<DList, ? extends Dynamic> b, Supplier<Expr<?>> serialized) {
|
||||
return of(new DTypeCallable(null, null), name, b, serialized);
|
||||
}
|
||||
|
||||
public static DCallable of(DType signature, String name, Function<DList, ? extends Dynamic> b, Supplier<Expr<?>> serialized) {
|
||||
return new FCallable((Function<DList, Dynamic>) b, new LazySupplier<>(serialized), name, Objects.requireNonNull(signature));
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks all DFinal implementation classes. Use this in instanceof checks
|
||||
*/
|
||||
public sealed interface FImpl {
|
||||
}
|
||||
|
||||
private record FBool(boolean value) implements DBool, FImpl {
|
||||
@Override
|
||||
public Boolean getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return StringFormatter.toString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return DTypePrimitive.BOOL;
|
||||
}
|
||||
}
|
||||
|
||||
private record FNumber(double value) implements DNumber, FImpl {
|
||||
@Override
|
||||
public Double getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return StringFormatter.toStringPrecise(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return DTypePrimitive.NUMBER;
|
||||
}
|
||||
}
|
||||
|
||||
private record FString(String value) implements DString, FImpl {
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Dynamic.serialize(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return DTypePrimitive.STRING;
|
||||
}
|
||||
}
|
||||
|
||||
private record FObject(Map<String, Dynamic> value, DTypeObject signature) implements DObject, FImpl {
|
||||
@Override
|
||||
public Map<String, Dynamic> getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Dynamic.serialize(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return signature;
|
||||
}
|
||||
}
|
||||
|
||||
private record FList(List<Dynamic> value, DTypeList signature) implements DList, FImpl {
|
||||
@Override
|
||||
public List<Dynamic> getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return signature;
|
||||
}
|
||||
}
|
||||
|
||||
private record FCallable(Function<DList, Dynamic> value, Supplier<Expr<?>> gen, String name, DType signature) implements DCallable, FImpl {
|
||||
@Override
|
||||
public Expr<?> toExpr() {
|
||||
return gen.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<DList, Dynamic> getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toExpr().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name == null ? DCallable.super.getName() : name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DCallable named(String name) {
|
||||
return new FCallable(value, gen, name, signature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return signature;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.additional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.Call;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.Variable;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.lens.DNumberLens;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.lens.DStringLens;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.*;
|
||||
|
||||
import java.time.LocalTime;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static io.gitlab.jfronny.muscript.data.dynamic.type.DSL.*;
|
||||
|
||||
public record DTime(Supplier<LocalTime> time) implements DObject {
|
||||
public static final DType SIGNATURE = object(NUMBER).or(STRING).or(NUMBER);
|
||||
|
||||
@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 boolean isString() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DString asString() {
|
||||
return new DStringLens(this, this::toString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNumber() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DNumber asNumber() {
|
||||
return new DNumberLens(this, () -> time.get().toSecondOfDay());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr toExpr() {
|
||||
return new Call(CodeLocation.NONE, new Variable(CodeLocation.NONE, "time"), List.of(
|
||||
new Call.Arg(new NumberLiteral(CodeLocation.NONE, time.get().getHour()).asDynamicExpr(), false),
|
||||
new Call.Arg(new NumberLiteral(CodeLocation.NONE, time.get().getMinute()).asDynamicExpr(), false),
|
||||
new Call.Arg(new NumberLiteral(CodeLocation.NONE, time.get().getSecond()).asDynamicExpr(), false)
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return SIGNATURE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return time.get().toString();
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.additional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.DType;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface DelegateDynamic extends DynamicBase {
|
||||
Dynamic getDelegate();
|
||||
|
||||
@Override
|
||||
default void serialize(ExprWriter writer) throws IOException {
|
||||
getDelegate().serialize(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
default Expr<?> toExpr() {
|
||||
return getDelegate().toExpr();
|
||||
}
|
||||
|
||||
@Override
|
||||
default Object getValue() {
|
||||
return getDelegate().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isBool() {
|
||||
return getDelegate().isBool();
|
||||
}
|
||||
|
||||
@Override
|
||||
default DBool asBool() {
|
||||
return getDelegate().asBool();
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isNumber() {
|
||||
return getDelegate().isNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
default DNumber asNumber() {
|
||||
return getDelegate().asNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isString() {
|
||||
return getDelegate().isString();
|
||||
}
|
||||
|
||||
@Override
|
||||
default DString asString() {
|
||||
return getDelegate().asString();
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isObject() {
|
||||
return getDelegate().isObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
default DObject asObject() {
|
||||
return getDelegate().asObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isList() {
|
||||
return getDelegate().isList();
|
||||
}
|
||||
|
||||
@Override
|
||||
default DList asList() {
|
||||
return getDelegate().asList();
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isCallable() {
|
||||
return getDelegate().isCallable();
|
||||
}
|
||||
|
||||
@Override
|
||||
default DCallable asCallable() {
|
||||
return getDelegate().asCallable();
|
||||
}
|
||||
|
||||
@Override
|
||||
default DType getSignature() {
|
||||
return getDelegate().getSignature();
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.additional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.*;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.*;
|
||||
import io.gitlab.jfronny.muscript.ast.string.Concatenate;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class DirectPreconditionVisitor {
|
||||
public static boolean visit(Expr<?> expr) {
|
||||
//TODO replace this with switch pattern matching
|
||||
if (expr instanceof BoolLiteral) return true;
|
||||
if (expr instanceof NullLiteral) return true;
|
||||
if (expr instanceof NumberLiteral) return true;
|
||||
if (expr instanceof StringLiteral) return true;
|
||||
if (expr instanceof DynamicLiteral<?> e) return visit(e.value);
|
||||
if (expr instanceof DynamicCoerce e) return visit(e.inner);
|
||||
if (expr instanceof ObjectLiteral e) return e.content.values().stream().allMatch(DirectPreconditionVisitor::visit);
|
||||
if (expr instanceof Concatenate e) return visit(e.left) && visit(e.right);
|
||||
// if (expr instanceof BoolUnpack e) return visit(e.inner);
|
||||
// if (expr instanceof NumberUnpack e) return visit(e.inner);
|
||||
// if (expr instanceof StringUnpack e) return visit(e.inner);
|
||||
if (expr instanceof Bind e) return visitCallable(e.callable) && visit(e.parameter);
|
||||
if (expr instanceof Call e) return visitCallable(e.left) && e.args.stream().allMatch(a -> !a.variadic() && visit(a.expr()));
|
||||
return false; // unknown expression
|
||||
}
|
||||
|
||||
public static boolean visit(Dynamic dynamic) {
|
||||
if (dynamic instanceof DNull) return true;
|
||||
if (dynamic instanceof DFinal.FImpl) {
|
||||
if (dynamic instanceof DBool) return true;
|
||||
if (dynamic instanceof DNumber) return true;
|
||||
if (dynamic instanceof DString) return true;
|
||||
if (dynamic instanceof DObject ob) return ob.getValue().values().stream().allMatch(DirectPreconditionVisitor::visit);
|
||||
if (dynamic instanceof DList ob) return ob.getValue().stream().allMatch(DirectPreconditionVisitor::visit);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final Set<String> allowedVariables = Set.of("date", "time", "enum", "listOf");
|
||||
private static boolean visitCallable(DynamicExpr expr) {
|
||||
if (expr instanceof Variable v) return allowedVariables.contains(v.name);
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.additional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.DType;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public record NamedDCallable(DCallable inner, String name) implements DCallable {
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DCallable named(String name) {
|
||||
return new NamedDCallable(inner, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expr<?> toExpr() {
|
||||
return inner.toExpr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<DList, Dynamic> getValue() {
|
||||
return inner.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return inner.getSignature();
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.lens;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.BoolExpr;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DBool;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
public final class DBoolLens extends DLens implements DBool {
|
||||
private final BooleanSupplier value;
|
||||
|
||||
public DBoolLens(Dynamic source, BooleanSupplier value) {
|
||||
super(source);
|
||||
this.value = Objects.requireNonNull(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getValue() {
|
||||
return value.getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoolExpr toExpr() {
|
||||
return source.toExpr().asBoolExpr();
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.lens;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class DCallableLens extends DLens implements DCallable {
|
||||
private final Supplier<Function<DList, Dynamic>> value;
|
||||
|
||||
public DCallableLens(Dynamic source, Supplier<Function<DList, Dynamic>> value) {
|
||||
super(source);
|
||||
this.value = Objects.requireNonNull(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<DList, Dynamic> getValue() {
|
||||
return value.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expr<?> toExpr() {
|
||||
return source.toExpr();
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.lens;
|
||||
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.DType;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract sealed class DLens implements DynamicBase permits DBoolLens, DCallableLens, DListLens, DNumberLens, DObjectLens, DStringLens {
|
||||
protected final Dynamic source;
|
||||
|
||||
protected DLens(Dynamic source) {
|
||||
this.source = Objects.requireNonNull(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(ExprWriter writer) throws IOException {
|
||||
source.serialize(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBool() {
|
||||
return source.isBool();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBool asBool() {
|
||||
return source.asBool();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNumber() {
|
||||
return source.isNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DNumber asNumber() {
|
||||
return source.asNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isString() {
|
||||
return source.isString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DString asString() {
|
||||
return source.asString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isObject() {
|
||||
return source.isObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DObject asObject() {
|
||||
return source.asObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isList() {
|
||||
return source.isList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DList asList() {
|
||||
return source.asList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCallable() {
|
||||
return source.isCallable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DCallable asCallable() {
|
||||
return source.asCallable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable DType getSignature() {
|
||||
return source.getSignature();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return source.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof DLens lens ? source.equals(lens.source) : source.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return source.toString();
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.lens;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DList;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class DListLens extends DLens implements DList {
|
||||
private final Supplier<List<? extends Dynamic>> value;
|
||||
|
||||
public DListLens(Dynamic source, Supplier<List<? extends Dynamic>> value) {
|
||||
super(source);
|
||||
this.value = Objects.requireNonNull(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends Dynamic> getValue() {
|
||||
return value.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr toExpr() {
|
||||
return source.toExpr().asDynamicExpr();
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.lens;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.NumberExpr;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DNumber;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.DoubleSupplier;
|
||||
|
||||
public final class DNumberLens extends DLens implements DNumber {
|
||||
private final DoubleSupplier value;
|
||||
|
||||
public DNumberLens(Dynamic source, DoubleSupplier value) {
|
||||
super(source);
|
||||
this.value = Objects.requireNonNull(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getValue() {
|
||||
return value.getAsDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberExpr toExpr() {
|
||||
return source.toExpr().asNumberExpr();
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.lens;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DObject;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class DObjectLens extends DLens implements DObject {
|
||||
private final Supplier<Map<String, ? extends Dynamic>> value;
|
||||
|
||||
public DObjectLens(Dynamic source, Supplier<Map<String, ? extends Dynamic>> value) {
|
||||
super(source);
|
||||
this.value = Objects.requireNonNull(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ? extends Dynamic> getValue() {
|
||||
return value.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicExpr toExpr() {
|
||||
return source.toExpr().asDynamicExpr();
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.lens;
|
||||
|
||||
import io.gitlab.jfronny.muscript.ast.StringExpr;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DString;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class DStringLens extends DLens implements DString {
|
||||
private final Supplier<String> value;
|
||||
|
||||
public DStringLens(Dynamic source, Supplier<String> value) {
|
||||
super(source);
|
||||
this.value = Objects.requireNonNull(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringExpr toExpr() {
|
||||
return source.toExpr().asStringExpr();
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.type;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DSL {
|
||||
public static final DTypePrimitive BOOL = DTypePrimitive.BOOL;
|
||||
public static final DTypePrimitive STRING = DTypePrimitive.STRING;
|
||||
public static final DTypePrimitive NUMBER = DTypePrimitive.NUMBER;
|
||||
public static final DTypePrimitive NULL = DTypePrimitive.NULL;
|
||||
|
||||
public static DTypeGeneric generic(int index) {
|
||||
return new DTypeGeneric(index);
|
||||
}
|
||||
|
||||
public static DTypeList list(@Nullable DType entryType) {
|
||||
return new DTypeList(entryType);
|
||||
}
|
||||
|
||||
public static DTypeObject object(@Nullable DType entryType) {
|
||||
return new DTypeObject(entryType);
|
||||
}
|
||||
|
||||
public static DTypeCallable callable(@Nullable List<DTypeCallable.Arg> from, @Nullable DType to) {
|
||||
return new DTypeCallable(from, to);
|
||||
}
|
||||
|
||||
public static DTypeCallable callable(@Nullable DType to, DTypeCallable.Arg... from) {
|
||||
return new DTypeCallable(List.of(from), to);
|
||||
}
|
||||
|
||||
public static DTypeCallable.Arg arg(@Nullable String name, @Nullable DType type) {
|
||||
return new DTypeCallable.Arg(name, type, false);
|
||||
}
|
||||
|
||||
public static DTypeCallable.Arg arg(@Nullable String name, @Nullable DType type, boolean variadic) {
|
||||
return new DTypeCallable.Arg(name, type, variadic);
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.type;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public sealed interface DType permits DTypeCallable, DTypeGeneric, DTypeList, DTypeObject, DTypePrimitive, DTypeSum, DTypeAnd {
|
||||
static String toString(@Nullable DType type) {
|
||||
return toString(type, false);
|
||||
}
|
||||
|
||||
default DType or(DType alternative) {
|
||||
return new DTypeSum(Set.of(this, alternative));
|
||||
}
|
||||
|
||||
default DType and(DType additional) {
|
||||
return new DTypeAnd(Set.of(this, additional));
|
||||
}
|
||||
|
||||
static String toString(@Nullable DType type, boolean wrapComplex) {
|
||||
//TODO replace with switch pattern
|
||||
if (type == null) return "any";
|
||||
else if (type instanceof DTypePrimitive t) return switch (t) {
|
||||
case BOOL -> "bool";
|
||||
case NUMBER -> "number";
|
||||
case STRING -> "string";
|
||||
case NULL -> "null";
|
||||
};
|
||||
else if (type instanceof DTypeGeneric t) return "T" + t.index();
|
||||
else if (type instanceof DTypeList t) return "[" + toString(t.entryType(), false) + "]";
|
||||
else if (type instanceof DTypeObject t) return "{" + toString(t.entryType(), false) + "}";
|
||||
else if (type instanceof DTypeCallable t) {
|
||||
String args = t.from() == null
|
||||
? "any"
|
||||
: "(" + t.from().stream().map(Objects::toString).collect(Collectors.joining(", ")) + ")";
|
||||
return args + " -> " + toString(t.to(), true);
|
||||
} else if (type instanceof DTypeSum t) return (wrapComplex ? "<" : "") + t.elements().stream().map(s -> toString(s, true)).collect(Collectors.joining(" | ")) + (wrapComplex ? ">" : "");
|
||||
else if (type instanceof DTypeAnd t) return (wrapComplex ? "<" : "") + t.elements().stream().map(s -> toString(s, true)).collect(Collectors.joining(" & ")) + (wrapComplex ? ">" : "");
|
||||
else throw new IllegalArgumentException("Unexpected DType implementation: " + type.getClass());
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.type;
|
||||
|
||||
import io.gitlab.jfronny.commons.data.ImmCollection;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public record DTypeAnd(Set<DType> elements) implements DType {
|
||||
public DTypeAnd(Set<DType> elements) {
|
||||
if (elements.isEmpty()) throw new IllegalArgumentException("Cannot create union type without elements");
|
||||
|
||||
Set<DType> simple = new LinkedHashSet<>();
|
||||
Set<DType> list = new LinkedHashSet<>();
|
||||
Set<DType> object = new LinkedHashSet<>();
|
||||
|
||||
boolean foundNullList = false, foundNullObject = false;
|
||||
|
||||
Queue<DType> toProcess = new LinkedList<>(elements);
|
||||
while (!toProcess.isEmpty()) {
|
||||
DType type = toProcess.remove();
|
||||
//TODO replace with pattern match
|
||||
if (type instanceof DTypePrimitive || type instanceof DTypeSum || type instanceof DTypeCallable || type instanceof DTypeGeneric) simple.add(type);
|
||||
else if (type instanceof DTypeList u) {
|
||||
if (u.entryType() == null) {
|
||||
if (!foundNullList) simple.add(new DTypeList(null));
|
||||
foundNullList = true;
|
||||
} else list.add(u.entryType());
|
||||
} else if (type instanceof DTypeObject u) {
|
||||
if (u.entryType() == null) {
|
||||
if (!foundNullObject) simple.add(new DTypeObject(null));
|
||||
foundNullObject = true;
|
||||
} else object.add(u.entryType());
|
||||
} else if (type instanceof DTypeAnd u) toProcess.addAll(u.elements);
|
||||
else throw new IllegalArgumentException("Unexpected DType implementation: " + type.getClass());
|
||||
}
|
||||
|
||||
if (!list.isEmpty()) simple.add(new DTypeList(new DTypeAnd(list)));
|
||||
if (!object.isEmpty()) simple.add(new DTypeObject(new DTypeAnd(object)));
|
||||
this.elements = ImmCollection.copyOf(simple);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return DType.toString(this);
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.type;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record DTypeCallable(@Nullable List<Arg> from, @Nullable DType to) implements DType {
|
||||
public DTypeCallable {
|
||||
if (from != null) {
|
||||
for (Arg arg : from) {
|
||||
if (arg == null) throw new IllegalArgumentException("Contents of 'from' cannot be null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return DType.toString(this);
|
||||
}
|
||||
|
||||
public record Arg(@Nullable String name, @Nullable DType type, boolean variadic) {
|
||||
@Override
|
||||
public String toString() {
|
||||
return (name == null ? DType.toString(type) : name + ": " + DType.toString(type)) + (variadic ? "..." : "");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.type;
|
||||
|
||||
public record DTypeGeneric(int index) implements DType {
|
||||
@Override
|
||||
public String toString() {
|
||||
return DType.toString(this);
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.type;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public record DTypeList(@Nullable DType entryType) implements DType {
|
||||
@Override
|
||||
public String toString() {
|
||||
return DType.toString(this);
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.type;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
//TODO redesign for complex objects
|
||||
public record DTypeObject(@Nullable DType entryType) implements DType {
|
||||
@Override
|
||||
public String toString() {
|
||||
return DType.toString(this);
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.type;
|
||||
|
||||
public enum DTypePrimitive implements DType {
|
||||
BOOL, NUMBER, STRING, NULL;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return DType.toString(this);
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.type;
|
||||
|
||||
import io.gitlab.jfronny.commons.data.ImmCollection;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public record DTypeSum(Set<DType> elements) implements DType {
|
||||
public DTypeSum(Set<DType> elements) {
|
||||
if (elements.isEmpty()) throw new IllegalArgumentException("Cannot create sum type without elements");
|
||||
|
||||
Set<DType> simple = new LinkedHashSet<>();
|
||||
Set<DType> list = new LinkedHashSet<>();
|
||||
Set<DType> object = new LinkedHashSet<>();
|
||||
|
||||
Queue<DType> toProcess = new LinkedList<>(elements);
|
||||
while (!toProcess.isEmpty()) {
|
||||
DType type = toProcess.remove();
|
||||
//TODO replace with pattern match
|
||||
if (type instanceof DTypePrimitive || type instanceof DTypeAnd || type instanceof DTypeCallable) simple.add(type);
|
||||
else if (type instanceof DTypeList u) list.add(u.entryType());
|
||||
else if (type instanceof DTypeObject u) object.add(u.entryType());
|
||||
else if (type instanceof DTypeSum u) toProcess.addAll(u.elements);
|
||||
else throw new IllegalArgumentException("Unexpected DType implementation: " + type.getClass());
|
||||
}
|
||||
|
||||
if (!list.isEmpty()) simple.add(new DTypeList(list.contains(null) ? null : new DTypeSum(list)));
|
||||
if (!object.isEmpty()) simple.add(new DTypeObject(object.contains(null) ? null : new DTypeSum(object)));
|
||||
this.elements = ImmCollection.copyOf(simple);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return DType.toString(this);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue