feat(muscript): enhance Get to prefer List/Object-specific variants if type can be easily inferred
ci/woodpecker/push/woodpecker Pipeline was successful
Details
ci/woodpecker/push/woodpecker Pipeline was successful
Details
This commit is contained in:
parent
1f8fd9c7cd
commit
268fe2aeac
|
@ -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 {
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"),
|
||||
|
|
Loading…
Reference in New Issue