feat(muscript): enhance Get to prefer List/Object-specific variants if type can be easily inferred
ci/woodpecker/push/woodpecker Pipeline was successful Details

This commit is contained in:
Johannes Frohnmeyer 2024-04-07 16:21:18 +02:00
parent 1f8fd9c7cd
commit 268fe2aeac
Signed by: Johannes
GPG Key ID: E76429612C2929F4
8 changed files with 87 additions and 8 deletions

View File

@ -3,5 +3,5 @@ package io.gitlab.jfronny.muscript.ast;
import io.gitlab.jfronny.muscript.ast.dynamic.*;
import io.gitlab.jfronny.muscript.ast.extensible.ExtensibleDynamicExpr;
public sealed interface DynamicExpr extends Expr permits Bind, Call, Closure, DynamicAssign, DynamicCoerce, DynamicConditional, DynamicLiteral, ExprGroup, Get, ListLiteral, ObjectLiteral, This, Variable, ExtensibleDynamicExpr {
public sealed interface DynamicExpr extends Expr permits At, Bind, Call, Closure, DynamicAssign, DynamicCoerce, DynamicConditional, DynamicLiteral, ExprGroup, Get, GetOrAt, ListLiteral, ObjectLiteral, This, Variable, ExtensibleDynamicExpr {
}

View File

@ -0,0 +1,13 @@
package io.gitlab.jfronny.muscript.ast.dynamic;
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
import io.gitlab.jfronny.muscript.ast.NumberExpr;
import io.gitlab.jfronny.muscript.core.CodeLocation;
import io.gitlab.jfronny.muscript.core.Order;
public record At(CodeLocation location, DynamicExpr left, NumberExpr index) implements DynamicExpr {
@Override
public Order order() {
return Order.Call;
}
}

View File

@ -2,10 +2,11 @@ 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.StringExpr;
import io.gitlab.jfronny.muscript.core.CodeLocation;
import io.gitlab.jfronny.muscript.core.Order;
public record Get(CodeLocation location, DynamicExpr left, Expr name) implements DynamicExpr {
public record Get(CodeLocation location, DynamicExpr left, StringExpr name) implements DynamicExpr {
@Override
public Order order() {
return Order.Call;

View File

@ -0,0 +1,13 @@
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.core.CodeLocation;
import io.gitlab.jfronny.muscript.core.Order;
public record GetOrAt(CodeLocation location, DynamicExpr left, Expr nameOrIndex) implements DynamicExpr {
@Override
public Order order() {
return Order.Call;
}
}

View File

@ -301,6 +301,16 @@ public class Optimizer {
case This e -> e;
case Variable e -> e;
case Get(var location, var left, var name) -> new Get(location, optimize(left), optimize(name));
case At(var location, var left, var index) -> new At(location, optimize(left), optimize(index));
case GetOrAt(var location, var leftX, var nameOrIndexX) -> {
var left = optimize(leftX);
var nameOrIndex = optimize(nameOrIndexX);
if (nameOrIndex instanceof StringExpr name) yield new Get(location, left, name);
if (nameOrIndex instanceof NumberExpr index) yield new At(location, left, index);
if (left instanceof ObjectLiteral l) yield new Get(location, optimize(l), asString(nameOrIndex));
if (left instanceof ListLiteral l) yield new At(location, optimize(l), asNumber(nameOrIndex));
yield new GetOrAt(location, optimize(left), optimize(nameOrIndex));
}
case Bind(var location, var callable, var parameter) -> new Bind(location, optimize(callable), optimize(parameter));
case Call e -> {
var location = e.location();
@ -399,6 +409,8 @@ public class Optimizer {
? Stream.of(expr)
: Stream.of(ExprGroup.of(location, steps.stream().flatMap(Optimizer::extractSideEffects).toList()));
case Get(var location, var left, var name) -> Stream.concat(extractSideEffects(left), extractSideEffects(name));
case At(var location, var left, var index) -> Stream.concat(extractSideEffects(left), extractSideEffects(index));
case GetOrAt(var location, var left, var nameOrIndex) -> Stream.concat(extractSideEffects(left), extractSideEffects(nameOrIndex));
case ListLiteral(var location, var elements) -> elements.stream().flatMap(Optimizer::extractSideEffects);
case ObjectLiteral(var location, var content) -> content.values().stream().flatMap(Optimizer::extractSideEffects);
case Divide(var location, var dividend, var divisor) -> Stream.concat(extractSideEffects(dividend), extractSideEffects(divisor));

View File

@ -300,7 +300,7 @@ public class Parser extends VersionedComponent {
yield new Bind(location, callable, ExprUtils.asDynamic(expr));
}
case LeftBracket -> {
expr = new Get(location, asDynamic(expr), expression());
expr = new GetOrAt(location, asDynamic(expr), expression());
consume(Token.RightBracket, "Expected closing bracket");
yield expr;
}

View File

@ -11,6 +11,7 @@ import io.gitlab.jfronny.muscript.ast.extensible.ExtensibleNumberExpr;
import io.gitlab.jfronny.muscript.ast.extensible.ExtensibleStringExpr;
import io.gitlab.jfronny.muscript.ast.number.*;
import io.gitlab.jfronny.muscript.ast.string.*;
import io.gitlab.jfronny.muscript.core.CodeLocation;
import io.gitlab.jfronny.muscript.core.IDynamic;
import io.gitlab.jfronny.muscript.core.LocationalException;
import io.gitlab.jfronny.muscript.core.StackFrame;
@ -167,17 +168,44 @@ public class Runtime {
if (scope.has(name)) yield scope.get(name);
else throw locationalException(expr, "Variable " + name + " not found");
}
case Get e -> {
var left = evaluate(e.left(), scope);
if (Dynamic.isNull(left)) throw locationalException(expr, "Could not get \"" + evaluate(e.name(), scope) + "\" because left is null");
case Get(var location, var leftExpr, var name) -> {
var left = evaluate(leftExpr, scope);
if (Dynamic.isNull(left)) throw locationalException(expr, "Could not get \"" + evaluate(name, scope) + "\" because left is null");
if (!left.isObject()) throw locationalException(expr, "Cannot get from non-object");
var o = left.asObject();
var n = evaluate(asString(name), scope);
if (!o.has(n)) throw locationalException(expr, "Object does not contain \"" + n + "\"");
yield o.get(n);
}
case At(var location, var leftExpr, var index) -> {
var left = evaluate(leftExpr, scope);
if (Dynamic.isNull(left)) throw locationalException(expr, "Could not get \"" + evaluate(index, scope) + "\" because left is null");
if (!left.isList()) {
if (left.isObject()) {
// optimizer was too eager, handle like Get
var o = left.asObject();
var n = evaluate(asString(index), scope);
if (!o.has(n)) throw locationalException(expr, "Object does not contain \"" + n + "\"");
yield o.get(n);
}
throw locationalException(expr, "Cannot index non-list");
}
var l = left.asList();
int idx = (int) evaluate(index, scope);
if (idx < 0 || idx >= l.size()) throw locationalException(expr, "Index " + idx + " is out of range for list with size " + l.size());
yield l.get(idx);
}
case GetOrAt(var location, var leftExpr, var nameOrIndex) -> {
var left = evaluate(leftExpr, scope);
if (Dynamic.isNull(left)) throw locationalException(expr, "Could not get \"" + evaluate(nameOrIndex, scope) + "\" because left is null");
if (left.isObject()) {
var o = left.asObject();
var n = evaluate(asString(e.name()), scope);
var n = evaluate(asString(nameOrIndex), scope);
if (!o.has(n)) throw locationalException(expr, "Object does not contain \"" + n + "\"");
yield o.get(n);
} else if (left.isList()) {
var l = left.asList();
int idx = (int) evaluate(asNumber(e.name()), scope);
int idx = (int) evaluate(asNumber(nameOrIndex), scope);
if (idx < 0 || idx >= l.size()) throw locationalException(expr, "Index " + idx + " is out of range for list with size " + l.size());
yield l.get(idx);
}

View File

@ -223,6 +223,18 @@ public abstract class Decompiler {
writer.append(']');
}
}
case At(var location, var left, var index) -> {
parenthesize(expr, left, writer, false);
writer.append('[');
decompile(writer, index);
writer.append(']');
}
case GetOrAt(var location, var left, var index) -> {
parenthesize(expr, left, writer, false);
writer.append('[');
decompile(writer, index);
writer.append(']');
}
case ListLiteral(var location, var elements) -> decompile(writer, new Call(
location,
new Variable(location, "listOf"),