diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index 3689d1f5..46114c24 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -111,12 +111,20 @@ public class Expression } private final Map functionalEquivalence = new Object2ObjectOpenHashMap<>(); + private final Map functionalAliases = new Object2ObjectOpenHashMap<>(); - public void addFunctionalEquivalence(String operator, String function) + private void addFunctionalEquivalence(String operator, String function) { assert operators.containsKey(operator); assert functions.containsKey(function); functionalEquivalence.put(operator, function); + functionalAliases.put(operator, function); + } + private void addFunctionalAlias(String operator, String function) + { + assert operators.containsKey(operator); + assert functions.containsKey(function); + functionalAliases.put(operator, function); } private final Map constants = Map.of( @@ -191,7 +199,7 @@ public class Expression } - public void addLazyUnaryOperator(String surface, int precedence, boolean leftAssoc, boolean pure, Function staticTyper, + public void addLazyUnaryOperator(String surface, String function, int precedence, boolean leftAssoc, boolean pure, Function staticTyper, TriFunction lazyfun) { operators.put(surface + "u", new AbstractLazyOperator(precedence, leftAssoc) @@ -227,10 +235,45 @@ public class Expression } } }); + + functions.put(function, new AbstractLazyFunction(1, function) + { + @Override + public boolean pure() + { + return pure; + } + + @Override + public boolean transitive() + { + return false; + } + + @Override + public Context.Type staticType(Context.Type outerType) + { + return staticTyper.apply(outerType); + } + + @Override + public LazyValue lazyEval(Context c, Context.Type t, Expression e, Token token, List v) + { + try + { + return lazyfun.apply(c, t, v.get(0)); + } + catch (RuntimeException exc) + { + throw handleCodeException(c, exc, e, token); + } + } + }); + addFunctionalAlias(surface + "u", function); } - public void addLazyBinaryOperatorWithDelegation(String surface, int precedence, boolean leftAssoc, boolean pure, + public void addLazyBinaryOperatorWithDelegation(String surface, String function, int precedence, boolean leftAssoc, boolean pure, SexFunction lazyfun) { operators.put(surface, new AbstractLazyOperator(precedence, leftAssoc) @@ -260,6 +303,36 @@ public class Expression } } }); + + functions.put(function, new AbstractLazyFunction(2, function) + { + @Override + public boolean pure() + { + return pure; + } + + @Override + public boolean transitive() + { + return false; + } + + @Override + public LazyValue lazyEval(Context c, Context.Type type, Expression e, Token t, List v) + { + try + { + return lazyfun.apply(c, type, e, t, v.get(0), v.get(1)); + } + catch (RuntimeException exc) + { + throw handleCodeException(c, exc, e, t); + } + } + }); + + addFunctionalAlias(surface, function); } public void addCustomFunction(String name, ILazyFunction fun) @@ -333,7 +406,84 @@ public class Expression }); } - public void addLazyBinaryOperator(String surface, int precedence, boolean leftAssoc, boolean pure, Function typer, + public void addLazyBinaryOperator(String surface, String function, int precedence, boolean leftAssoc, boolean pure, Function typer, + QuadFunction lazyfun, TriFunction, LazyValue> multiFun) + { + operators.put(surface, new AbstractLazyOperator(precedence, leftAssoc) + { + + @Override + public boolean pure() + { + return pure; + } + + @Override + public boolean transitive() + { + return false; + } + + @Override + public Context.Type staticType(Context.Type outerType) + { + return typer.apply(outerType); + } + + @Override + public LazyValue lazyEval(Context c, Context.Type t, Expression e, Token token, LazyValue v1, LazyValue v2) + { + ILazyFunction.checkInterrupts(); + try + { + return lazyfun.apply(c, t, v1, v2); + } + catch (RuntimeException exc) + { + throw handleCodeException(c, exc, e, token); + } + } + }); + + functions.put(function, new AbstractLazyFunction(-1, function) + { + @Override + public boolean pure() + { + return true; + } + + @Override + public boolean transitive() + { + return false; + } + + @Override + public Context.Type staticType(Context.Type outerType) + { + return typer.apply(outerType); + } + + @Override + public LazyValue lazyEval(Context c, Context.Type i, Expression e, Token t, List lazyParams) + { + ILazyFunction.checkInterrupts(); + try + { + return multiFun.apply(c, i, lazyParams); + } + catch (RuntimeException exc) + { + throw handleCodeException(c, exc, e, t); + } + } + }); + + addFunctionalEquivalence(surface, function); + } + + public void addLazyBinaryOperator(String surface, String function, int precedence, boolean leftAssoc, boolean pure, Function typer, QuadFunction lazyfun) { operators.put(surface, new AbstractLazyOperator(precedence, leftAssoc) @@ -371,9 +521,45 @@ public class Expression } } }); + + functions.put(function, new AbstractLazyFunction(2, function) + { + @Override + public boolean pure() + { + return pure; + } + + @Override + public boolean transitive() + { + return false; + } + + @Override + public Context.Type staticType(Context.Type outerType) + { + return typer.apply(outerType); + } + + @Override + public LazyValue lazyEval(Context c, Context.Type t, Expression e, Token token, List v) + { + try + { + return lazyfun.apply(c, t, v.get(0), v.get(1)); + } + catch (RuntimeException exc) + { + throw handleCodeException(c, exc, e, token); + } + } + }); + + addFunctionalAlias(surface, function); } - public void addBinaryContextOperator(String surface, int precedence, boolean leftAssoc, boolean pure, boolean transitive, + public void addBinaryContextOperator(String surface, String function, int precedence, boolean leftAssoc, boolean pure, boolean transitive, QuadFunction fun) { operators.put(surface, new AbstractLazyOperator(precedence, leftAssoc) @@ -404,6 +590,37 @@ public class Expression } } }); + + functions.put(function, new AbstractLazyFunction(2, function) + { + @Override + public boolean pure() + { + return pure; + } + + @Override + public boolean transitive() + { + return transitive; + } + + @Override + public LazyValue lazyEval(Context c, Context.Type t, Expression e, Token token, List v) + { + try + { + Value ret = fun.apply(c, t, v.get(0).evalValue(c, Context.NONE), v.get(1).evalValue(c, Context.NONE)); + return (cc, tt) -> ret; + } + catch (RuntimeException exc) + { + throw handleCodeException(c, exc, e, token); + } + } + }); + + addFunctionalAlias(surface, function); } public static RuntimeException handleCodeException(Context c, RuntimeException exc, Expression e, Token token) @@ -433,7 +650,7 @@ public class Expression return new ExpressionException(c, e, token, "Internal error (please report this issue to Carpet) while evaluating: " + exc); } - public void addUnaryOperator(String surface, boolean leftAssoc, Function fun) + public void addUnaryOperator(String surface, String function, boolean leftAssoc, Function fun) { operators.put(surface + "u", new AbstractUnaryOperator(Operators.precedence.get("unary+-!..."), leftAssoc) { @@ -443,18 +660,57 @@ public class Expression return fun.apply(v1); } }); + functions.put(function, new AbstractFunction(1, function) + { + @Override + public Value eval(List v1) + { + return fun.apply(v1.get(0)); + } + }); + addFunctionalAlias(surface + "u", function); } - public void addBinaryOperator(String surface, int precedence, boolean leftAssoc, BiFunction fun) + public void addBinaryOperator(String surface, String function, int precedence, boolean leftAssoc, BiFunction fun, Function, Value> multiFun) { operators.put(surface, new AbstractOperator(precedence, leftAssoc) { @Override - public Value eval(Value v1, Value v2) - { + public Value eval(Value v1, Value v2) { return fun.apply(v1, v2); } }); + + functions.put(function, new AbstractFunction(-1, function) + { + @Override + public Value eval(List parameters) + { + return multiFun.apply(parameters); + } + }); + addFunctionalEquivalence(surface, function); + } + + public void addBinaryOperator(String surface, String name, int precedence, boolean leftAssoc, BiFunction fun) + { + operators.put(surface, new AbstractOperator(precedence, leftAssoc) + { + @Override + public Value eval(Value v1, Value v2) { + return fun.apply(v1, v2); + } + }); + functions.put(name, new AbstractFunction(2, name) + { + @Override + public Value eval(List parameters) + { + return fun.apply(parameters.get(0), parameters.get(1)); + } + }); + + addFunctionalAlias(surface, name); } @@ -623,44 +879,6 @@ public class Expression }); } - public void addPureLazyFunction(String name, int num_params, Function typer, TriFunction, LazyValue> fun) - { - functions.put(name, new AbstractLazyFunction(num_params, name) - { - @Override - public boolean pure() - { - return true; - } - - @Override - public boolean transitive() - { - return false; - } - - @Override - public Context.Type staticType(Context.Type outerType) - { - return typer.apply(outerType); - } - - @Override - public LazyValue lazyEval(Context c, Context.Type i, Expression e, Token t, List lazyParams) - { - ILazyFunction.checkInterrupts(); - try - { - return fun.apply(c, i, lazyParams); - } - catch (RuntimeException exc) - { - throw handleCodeException(c, exc, e, t); - } - } - }); - } - public void addContextFunction(String name, int num_params, TriFunction, Value> fun) { functions.put(name, new AbstractLazyFunction(num_params, name) @@ -859,6 +1077,9 @@ public class Expression Threading.apply(this); Loops.apply(this); DataStructures.apply(this); + for(String op : operators.keySet()) { + assert functionalAliases.containsKey(op) : "Missing function for operator " + op; + } } @@ -1381,6 +1602,13 @@ public class Expression } } } + + if (toFunctional && functionalAliases.containsKey(symbol) && !node.args.isEmpty()) + { + optimized = true; + node.token.morph(Token.TokenType.FUNCTION, functionalAliases.get(symbol)); + } + return optimized; } diff --git a/src/main/java/carpet/script/language/ControlFlow.java b/src/main/java/carpet/script/language/ControlFlow.java index d96d1209..978938aa 100644 --- a/src/main/java/carpet/script/language/ControlFlow.java +++ b/src/main/java/carpet/script/language/ControlFlow.java @@ -22,14 +22,12 @@ public class ControlFlow public static void apply(Expression expression) // public just to get the javadoc right { // needs to be lazy cause of custom contextualization - expression.addLazyBinaryOperator(";", Operators.precedence.get("nextop;"), true, true, t -> Context.Type.VOID, (c, t, lv1, lv2) -> + expression.addLazyBinaryOperator(";", "then", Operators.precedence.get("nextop;"), true, true, t -> Context.Type.VOID, (c, t, lv1, lv2) -> { lv1.evalValue(c, Context.VOID); Value v2 = lv2.evalValue(c, t); return (cc, tt) -> v2; - }); - - expression.addPureLazyFunction("then", -1, t -> Context.Type.VOID, (c, t, lv) -> { + }, (c, t, lv) -> { int imax = lv.size() - 1; for (int i = 0; i < imax; i++) { @@ -38,7 +36,6 @@ public class ControlFlow Value v = lv.get(imax).evalValue(c, t); return (cc, tt) -> v; }); - expression.addFunctionalEquivalence(";", "then"); // obvious lazy due to conditional evaluation of arguments diff --git a/src/main/java/carpet/script/language/DataStructures.java b/src/main/java/carpet/script/language/DataStructures.java index 24adb642..01fa6c80 100644 --- a/src/main/java/carpet/script/language/DataStructures.java +++ b/src/main/java/carpet/script/language/DataStructures.java @@ -188,7 +188,7 @@ public class DataStructures ? ListValue.wrap(map.getMap().entrySet().stream().map(p -> ListValue.of(p.getKey(), p.getValue()))) : Value.NULL); - expression.addBinaryContextOperator(":", Operators.precedence.get("attribute~:"), true, true, false, (ctx, t, container, address) -> + expression.addBinaryContextOperator(":", "element", Operators.precedence.get("attribute~:"), true, true, false, (ctx, t, container, address) -> { if (container instanceof final LContainerValue lcv) { diff --git a/src/main/java/carpet/script/language/Functions.java b/src/main/java/carpet/script/language/Functions.java index 509b7270..2c544be3 100644 --- a/src/main/java/carpet/script/language/Functions.java +++ b/src/main/java/carpet/script/language/Functions.java @@ -126,7 +126,7 @@ public class Functions //assigns const procedure to the lhs, returning its previous value // must be lazy due to RHS being an expression to save to execute - expression.addLazyBinaryOperatorWithDelegation("->", Operators.precedence.get("def->"), false, false, (c, type, e, t, lv1, lv2) -> + expression.addLazyBinaryOperatorWithDelegation("->", "define", Operators.precedence.get("def->"), false, false, (c, type, e, t, lv1, lv2) -> { if (type == Context.MAPDEF) { diff --git a/src/main/java/carpet/script/language/Operators.java b/src/main/java/carpet/script/language/Operators.java index ddad96fe..f097ae8d 100644 --- a/src/main/java/carpet/script/language/Operators.java +++ b/src/main/java/carpet/script/language/Operators.java @@ -41,8 +41,7 @@ public class Operators public static void apply(Expression expression) { - expression.addBinaryOperator("+", precedence.get("addition+-"), true, Value::add); - expression.addFunction("sum", lv -> { + expression.addBinaryOperator("+", "sum", precedence.get("addition+-"), true, Value::add, lv -> { int size = lv.size(); if (size == 0) { @@ -55,10 +54,8 @@ public class Operators } return accumulator; }); - expression.addFunctionalEquivalence("+", "sum"); - expression.addBinaryOperator("-", precedence.get("addition+-"), true, Value::subtract); - expression.addFunction("difference", lv -> { + expression.addBinaryOperator("-", "difference", precedence.get("addition+-"), true, Value::subtract, lv -> { int size = lv.size(); if (size == 0) { @@ -71,10 +68,8 @@ public class Operators } return accumulator; }); - expression.addFunctionalEquivalence("-", "difference"); - expression.addBinaryOperator("*", precedence.get("multiplication*/%"), true, Value::multiply); - expression.addFunction("product", lv -> { + expression.addBinaryOperator("*", "product", precedence.get("multiplication*/%"), true, Value::multiply, lv -> { int size = lv.size(); if (size == 0) { @@ -87,10 +82,8 @@ public class Operators } return accumulator; }); - expression.addFunctionalEquivalence("*", "product"); - expression.addBinaryOperator("/", precedence.get("multiplication*/%"), true, Value::divide); - expression.addFunction("quotient", lv -> { + expression.addBinaryOperator("/", "quotient", precedence.get("multiplication*/%"), true, Value::divide, lv -> { int size = lv.size(); if (size == 0) { @@ -103,11 +96,11 @@ public class Operators } return accumulator; }); - expression.addFunctionalEquivalence("/", "quotient"); - expression.addBinaryOperator("%", precedence.get("multiplication*/%"), true, (v1, v2) -> + expression.addBinaryOperator("%", "modulo", precedence.get("multiplication*/%"), true, (v1, v2) -> NumericValue.asNumber(v1).mod(NumericValue.asNumber(v2))); - expression.addBinaryOperator("^", precedence.get("exponent^"), false, (v1, v2) -> + + expression.addBinaryOperator("^", "exponent", precedence.get("exponent^"), false, (v1, v2) -> new NumericValue(java.lang.Math.pow(NumericValue.asNumber(v1).getDouble(), NumericValue.asNumber(v2).getDouble()))); expression.addFunction("bitwise_and", lv -> { @@ -153,13 +146,11 @@ public class Operators }); // lazy cause RHS is only conditional - expression.addLazyBinaryOperator("&&", precedence.get("and&&"), false, true, t -> Context.Type.BOOLEAN, (c, t, lv1, lv2) -> + expression.addLazyBinaryOperator("&&", "and", precedence.get("and&&"), false, true, t -> Context.Type.BOOLEAN, (c, t, lv1, lv2) -> { // todo check how is optimizations going Value v1 = lv1.evalValue(c, Context.BOOLEAN); return v1.getBoolean() ? lv2 : ((cc, tt) -> v1); - }); - - expression.addPureLazyFunction("and", -1, t -> Context.Type.BOOLEAN, (c, t, lv) -> { + }, (c, t, lv) -> { int last = lv.size() - 1; if (last == -1) { @@ -168,7 +159,7 @@ public class Operators for (LazyValue l : lv.subList(0, last)) { Value val = l.evalValue(c, Context.Type.BOOLEAN); - if (val instanceof final FunctionUnpackedArgumentsValue fuav) + if (val instanceof FunctionUnpackedArgumentsValue fuav) { for (Value it : fuav) { @@ -188,16 +179,13 @@ public class Operators } return lv.get(last); }); - expression.addFunctionalEquivalence("&&", "and"); // lazy cause RHS is only conditional - expression.addLazyBinaryOperator("||", precedence.get("or||"), false, true, t -> Context.Type.BOOLEAN, (c, t, lv1, lv2) -> + expression.addLazyBinaryOperator("||", "or", precedence.get("or||"), false, true, t -> Context.Type.BOOLEAN, (c, t, lv1, lv2) -> { Value v1 = lv1.evalValue(c, Context.BOOLEAN); return v1.getBoolean() ? ((cc, tt) -> v1) : lv2; - }); - - expression.addPureLazyFunction("or", -1, t -> Context.Type.BOOLEAN, (c, t, lv) -> { + }, (c, t, lv) -> { int last = lv.size() - 1; if (last == -1) { @@ -226,13 +214,10 @@ public class Operators } return lv.get(last); }); - expression.addFunctionalEquivalence("||", "or"); - expression.addBinaryOperator("~", precedence.get("attribute~:"), true, Value::in); + expression.addBinaryOperator("~", "match", precedence.get("attribute~:"), true, Value::in); - expression.addBinaryOperator(">", precedence.get("compare>=><=<"), false, (v1, v2) -> - BooleanValue.of(v1.compareTo(v2) > 0)); - expression.addFunction("decreasing", lv -> { + expression.addBinaryOperator(">", "decreasing", precedence.get("compare>=><=<"), false, (v1, v2) -> BooleanValue.of(v1.compareTo(v2) > 0), lv -> { int size = lv.size(); if (size < 2) { @@ -249,11 +234,8 @@ public class Operators } return Value.TRUE; }); - expression.addFunctionalEquivalence(">", "decreasing"); - expression.addBinaryOperator(">=", precedence.get("compare>=><=<"), false, (v1, v2) -> - BooleanValue.of(v1.compareTo(v2) >= 0)); - expression.addFunction("nonincreasing", lv -> { + expression.addBinaryOperator(">=", "nonincreasing", precedence.get("compare>=><=<"), false, (v1, v2) -> BooleanValue.of(v1.compareTo(v2) >= 0), lv -> { int size = lv.size(); if (size < 2) { @@ -270,11 +252,8 @@ public class Operators } return Value.TRUE; }); - expression.addFunctionalEquivalence(">=", "nonincreasing"); - expression.addBinaryOperator("<", precedence.get("compare>=><=<"), false, (v1, v2) -> - BooleanValue.of(v1.compareTo(v2) < 0)); - expression.addFunction("increasing", lv -> { + expression.addBinaryOperator("<", "increasing", precedence.get("compare>=><=<"), false, (v1, v2) -> BooleanValue.of(v1.compareTo(v2) < 0), lv -> { int size = lv.size(); if (size < 2) { @@ -291,11 +270,8 @@ public class Operators } return Value.TRUE; }); - expression.addFunctionalEquivalence("<", "increasing"); - expression.addBinaryOperator("<=", precedence.get("compare>=><=<"), false, (v1, v2) -> - BooleanValue.of(v1.compareTo(v2) <= 0)); - expression.addFunction("nondecreasing", lv -> { + expression.addBinaryOperator("<=", "nondecreasing", precedence.get("compare>=><=<"), false, (v1, v2) -> BooleanValue.of(v1.compareTo(v2) <= 0), lv -> { int size = lv.size(); if (size < 2) { @@ -312,7 +288,7 @@ public class Operators } return Value.TRUE; }); - expression.addFunctionalEquivalence("<=", "nondecreasing"); + expression.addMathematicalBinaryIntFunction("bitwise_shift_left", (num, amount) -> num << amount); expression.addMathematicalBinaryIntFunction("bitwise_shift_right", (num, amount) -> num >>> amount); expression.addMathematicalBinaryIntFunction("bitwise_arithmetic_shift_right", (num, amount) -> num >> amount); @@ -326,9 +302,7 @@ public class Operators expression.addMathematicalUnaryIntFunction("double_to_long_bits", Double::doubleToLongBits); expression.addUnaryFunction("long_to_double_bits", v -> new NumericValue(Double.longBitsToDouble(NumericValue.asNumber(v).getLong()))); - expression.addBinaryOperator("==", precedence.get("equal==!="), false, (v1, v2) -> - v1.equals(v2) ? Value.TRUE : Value.FALSE); - expression.addFunction("equal", lv -> { + expression.addBinaryOperator("==", "equal", precedence.get("equal==!="), false, (v1, v2) -> v1.equals(v2) ? Value.TRUE : Value.FALSE, lv -> { int size = lv.size(); if (size < 2) { @@ -345,11 +319,8 @@ public class Operators } return Value.TRUE; }); - expression.addFunctionalEquivalence("==", "equal"); - expression.addBinaryOperator("!=", precedence.get("equal==!="), false, (v1, v2) -> - v1.equals(v2) ? Value.FALSE : Value.TRUE); - expression.addFunction("unique", lv -> { + expression.addBinaryOperator("!=", "unique", precedence.get("equal==!="), false, (v1, v2) -> v1.equals(v2) ? Value.FALSE : Value.TRUE, lv -> { int size = lv.size(); if (size < 2) { @@ -368,10 +339,9 @@ public class Operators } return Value.TRUE; }); - expression.addFunctionalEquivalence("!=", "unique"); // lazy cause of assignment which is non-trivial - expression.addLazyBinaryOperator("=", precedence.get("assign=<>"), false, false, t -> Context.Type.LVALUE, (c, t, lv1, lv2) -> + expression.addLazyBinaryOperator("=", "assign", precedence.get("assign=<>"), false, false, t -> Context.Type.LVALUE, (c, t, lv1, lv2) -> { Value v1 = lv1.evalValue(c, Context.LVALUE); Value v2 = lv2.evalValue(c); @@ -424,7 +394,7 @@ public class Operators }); // lazy due to assignment - expression.addLazyBinaryOperator("+=", precedence.get("assign=<>"), false, false, t -> Context.Type.LVALUE, (c, t, lv1, lv2) -> + expression.addLazyBinaryOperator("+=", "append", precedence.get("assign=<>"), false, false, t -> Context.Type.LVALUE, (c, t, lv1, lv2) -> { Value v1 = lv1.evalValue(c, Context.LVALUE); Value v2 = lv2.evalValue(c); @@ -493,7 +463,7 @@ public class Operators return boundedLHS; }); - expression.addBinaryContextOperator("<>", precedence.get("assign=<>"), false, false, false, (c, t, v1, v2) -> + expression.addBinaryContextOperator("<>", "swap", precedence.get("assign=<>"), false, false, false, (c, t, v1, v2) -> { if (v1 instanceof final ListValue.ListConstructorValue lcv1 && v2 instanceof final ListValue.ListConstructorValue lcv2) { @@ -541,17 +511,17 @@ public class Operators return lval; }); - expression.addUnaryOperator("-", false, v -> NumericValue.asNumber(v).opposite()); + expression.addUnaryOperator("-", "opposite", false, v -> NumericValue.asNumber(v).opposite()); - expression.addUnaryOperator("+", false, NumericValue::asNumber); + expression.addUnaryOperator("+", "identity", false, NumericValue::asNumber); // could be non-lazy, but who cares - its a small one. - expression.addLazyUnaryOperator("!", precedence.get("unary+-!..."), false, true, x -> Context.Type.BOOLEAN, (c, t, lv) -> + expression.addLazyUnaryOperator("!", "not", precedence.get("unary+-!..."), false, true, x -> Context.Type.BOOLEAN, (c, t, lv) -> lv.evalValue(c, Context.BOOLEAN).getBoolean() ? (cc, tt) -> Value.FALSE : (cc, tt) -> Value.TRUE ); // might need context boolean // lazy because of typed evaluation of the argument - expression.addLazyUnaryOperator("...", Operators.precedence.get("unary+-!..."), false, true, t -> t == Context.Type.LOCALIZATION ? Context.NONE : t, (c, t, lv) -> + expression.addLazyUnaryOperator("...", "unpack", Operators.precedence.get("unary+-!..."), false, true, t -> t == Context.Type.LOCALIZATION ? Context.NONE : t, (c, t, lv) -> { if (t == Context.LOCALIZATION) {