script compile time optimizations.

This commit is contained in:
gnembon committed 2021-05-05 00:25:31 -03:00
1 parent 06c9aea326
commit 8d97ab26a9
27 files changed
+1071 -193

No files matched your search

@@ -3,6 +3,7 @@ package carpet.script.language;
import carpet.script.Context;
import carpet.script.Expression;
import carpet.script.exception.InternalExpressionException;
import carpet.script.value.BooleanValue;
import carpet.script.value.ListValue;
import carpet.script.value.NumericValue;
import carpet.script.value.Value;
@@ -15,7 +16,7 @@ public class Arithmetic {
public static void apply(Expression expression)
{
expression.addLazyFunction("not", 1, (c, t, lv) -> lv.get(0).evalValue(c, Context.BOOLEAN).getBoolean() ? ((cc, tt) -> Value.FALSE) : ((cc, tt) -> Value.TRUE));
expression.addTypedContextFunction("not", 1, Context.Type.BOOLEAN, (c, t, lv) -> BooleanValue.of(lv.get(0).getBoolean()) );
expression.addUnaryFunction("fact", (v) ->
{
long number = NumericValue.asNumber(v).getLong();
@@ -21,13 +21,22 @@ 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, (c, t, lv1, lv2) ->
expression.addLazyBinaryOperator(";", 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) -> {
int imax = lv.size()-1;
for (int i = 0; i < imax; i++) lv.get(i).evalValue(c, Context.VOID);
Value v = lv.get(imax).evalValue(c, t);
return (cc, tt) -> v;
});
expression.addFunctionalEquivalence(";", "then");
// obvious lazy due to conditional evaluation of arguments
expression.addLazyFunction("if", -1, (c, t, lv) ->
{
@@ -50,9 +59,9 @@ public class ControlFlow {
return (cc, tt) -> Value.NULL;
});
expression.addFunction("exit", (lv) -> { throw new ExitStatement(lv.size()==0?Value.NULL:lv.get(0)); });
expression.addImpureFunction("exit", (lv) -> { throw new ExitStatement(lv.size()==0?Value.NULL:lv.get(0)); });
expression.addFunction("throw", lv->
expression.addImpureFunction("throw", lv->
{
switch (lv.size())
{
@@ -189,7 +189,7 @@ public class DataStructures {
return Value.NULL;
});
expression.addBinaryContextOperator(":", Operators.precedence.get("attribute~:"),true, (c, t, container, address) ->
expression.addBinaryContextOperator(":", Operators.precedence.get("attribute~:"),true, true, false, (ctx, t, container, address) ->
{
if (container instanceof LContainerValue)
{
@@ -3,6 +3,8 @@ package carpet.script.language;
import carpet.script.Context;
import carpet.script.Expression;
import carpet.script.Fluff;
import carpet.script.LazyValue;
import carpet.script.Tokenizer;
import carpet.script.argument.FunctionArgument;
import carpet.script.exception.InternalExpressionException;
import carpet.script.exception.ReturnStatement;
@@ -35,53 +37,75 @@ public class Functions {
return ListValue.wrap(c.host.availableImports(moduleName).map(StringValue::new).collect(Collectors.toList()));
});
// needs to be lazy because of custom context of execution of arguments as a signature
expression.addLazyFunctionWithDelegation("call",-1, (c, t, expr, tok, lv) ->
{ // adjust based on c
if (lv.size() == 0)
throw new InternalExpressionException("'call' expects at least function name to call");
//lv.remove(lv.size()-1); // aint gonna cut it // maybe it will because of the eager eval changes
if (t != Context.SIGNATURE) // just call the function
expression.addCustomFunction("call", new Fluff.AbstractLazyFunction(-1, "call") {
@Override
public LazyValue lazyEval(Context c, Context.Type t, Expression expr, Tokenizer.Token tok, List<LazyValue> lv)
{
List<Value> args = Fluff.AbstractFunction.unpackLazy(lv, c, Context.NONE);
FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, args, 0, false, true);
FunctionValue fun = functionArgument.function;
return fun.callInContext(c, t, functionArgument.args);
}
// gimme signature
String name = lv.get(0).evalValue(c, Context.NONE).getString();
List<String> args = new ArrayList<>();
List<String> globals = new ArrayList<>();
String varArgs = null;
for (int i = 1; i < lv.size(); i++)
{
Value v = lv.get(i).evalValue(c, Context.LOCALIZATION);
if (!v.isBound())
if (lv.size() == 0)
throw new InternalExpressionException("'call' expects at least function name to call");
//lv.remove(lv.size()-1); // aint gonna cut it // maybe it will because of the eager eval changes
if (t != Context.SIGNATURE) // just call the function
{
throw new InternalExpressionException("Only variables can be used in function signature, not " + v.getString());
List<Value> args = Fluff.AbstractFunction.unpackLazy(lv, c, Context.NONE);
FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, args, 0, false, true);
FunctionValue fun = functionArgument.function;
return fun.callInContext(c, t, functionArgument.args);
}
if (v instanceof FunctionAnnotationValue)
// gimme signature
String name = lv.get(0).evalValue(c, Context.NONE).getString();
List<String> args = new ArrayList<>();
List<String> globals = new ArrayList<>();
String varArgs = null;
for (int i = 1; i < lv.size(); i++)
{
if (((FunctionAnnotationValue) v).type == FunctionAnnotationValue.Type.GLOBAL)
Value v = lv.get(i).evalValue(c, Context.LOCALIZATION);
if (!v.isBound())
{
globals.add(v.boundVariable);
throw new InternalExpressionException("Only variables can be used in function signature, not " + v.getString());
}
if (v instanceof FunctionAnnotationValue)
{
if (((FunctionAnnotationValue) v).type == FunctionAnnotationValue.Type.GLOBAL)
{
globals.add(v.boundVariable);
}
else
{
if (varArgs != null)
throw new InternalExpressionException("Variable argument identifier is already defined as "+varArgs+", trying to overwrite with "+v.boundVariable);
varArgs = v.boundVariable;
}
}
else
{
if (varArgs != null)
throw new InternalExpressionException("Variable argument identifier is already defined as "+varArgs+", trying to overwrite with "+v.boundVariable);
varArgs = v.boundVariable;
args.add(v.boundVariable);
}
}
else
{
args.add(v.boundVariable);
}
Value retval = new FunctionSignatureValue(name, args, varArgs, globals);
return (cc, tt) -> retval;
}
@Override
public boolean pure()
{
return false; //true for sinature, but lets leave it for later
}
@Override
public boolean transitive() {
return false;
}
@Override
public Context.Type staticType(Context.Type outerType)
{
return outerType==Context.SIGNATURE?Context.LOCALIZATION:Context.NONE;
}
Value retval = new FunctionSignatureValue(name, args, varArgs, globals);
return (cc, tt) -> retval;
});
expression.addContextFunction("outer", 1, (c, t, lv) ->
{
if (t != Context.LOCALIZATION)
@@ -91,7 +115,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, (c, type, e, t, lv1, lv2) ->
expression.addLazyBinaryOperatorWithDelegation("->", Operators.precedence.get("def->"), false, false, (c, type, e, t, lv1, lv2) ->
{
if (type == Context.MAPDEF)
{
@@ -106,6 +130,6 @@ public class Functions {
return (cc, tt) -> result;
});
expression.addFunction("return", (lv) -> { throw new ReturnStatement(lv.size()==0?Value.NULL:lv.get(0));} );
expression.addImpureFunction("return", (lv) -> { throw new ReturnStatement(lv.size()==0?Value.NULL:lv.get(0));} );
}
}
@@ -21,14 +21,14 @@ public class Loops {
// condition and expression will get a bound '_i'
// returns last successful expression or false
// while(cond, limit, expr) => ??
expression.addFunction("break", lv ->
expression.addImpureFunction("break", lv ->
{
if (lv.size()==0) throw new BreakStatement(null);
if (lv.size()==1) throw new BreakStatement(lv.get(0));
throw new InternalExpressionException("'break' can only be called with zero or one argument");
});
expression.addFunction("continue", lv ->
expression.addImpureFunction("continue", lv ->
{
if (lv.size()==0) throw new ContinueStatement(null);
if (lv.size()==1) throw new ContinueStatement(lv.get(0));
@@ -5,6 +5,7 @@ import carpet.script.Expression;
import carpet.script.LazyValue;
import carpet.script.exception.InternalExpressionException;
import carpet.script.value.AbstractListValue;
import carpet.script.value.BooleanValue;
import carpet.script.value.ContainerValueInterface;
import carpet.script.value.FunctionAnnotationValue;
import carpet.script.value.FunctionUnpackedArgumentsValue;
@@ -14,6 +15,7 @@ import carpet.script.value.MapValue;
import carpet.script.value.NumericValue;
import carpet.script.value.Value;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -38,47 +40,203 @@ public class Operators {
public static void apply(Expression expression)
{
expression.addBinaryOperator("+", precedence.get("addition+-"), true, Value::add);
expression.addFunction("sum", lv -> {
int size = lv.size();
if (size == 0) return Value.NULL;
Value accumulator = lv.get(0);
for (Value v: lv.subList(1, size)) accumulator = accumulator.add(v);
return accumulator;
});
expression.addFunctionalEquivalence("+", "sum");
expression.addBinaryOperator("-", precedence.get("addition+-"), true, Value::subtract);
expression.addFunction("difference", lv -> {
int size = lv.size();
if (size == 0) return Value.NULL;
Value accumulator = lv.get(0);
for (Value v: lv.subList(1, size)) accumulator = accumulator.subtract(v);
return accumulator;
});
expression.addFunctionalEquivalence("-", "difference");
expression.addBinaryOperator("*", precedence.get("multiplication*/%"), true, Value::multiply);
expression.addFunction("product", lv -> {
int size = lv.size();
if (size == 0) return Value.NULL;
Value accumulator = lv.get(0);
for (Value v: lv.subList(1, size)) accumulator = accumulator.multiply(v);
return accumulator;
});
expression.addFunctionalEquivalence("*", "product");
expression.addBinaryOperator("/", precedence.get("multiplication*/%"), true, Value::divide);
expression.addFunction("quotient", lv -> {
int size = lv.size();
if (size == 0) return Value.NULL;
Value accumulator = lv.get(0);
for (Value v: lv.subList(1, size)) accumulator = accumulator.multiply(v);
return accumulator;
});
expression.addFunctionalEquivalence("/", "quotient");
expression.addBinaryOperator("%", precedence.get("multiplication*/%"), true, (v1, v2) ->
new NumericValue(NumericValue.asNumber(v1).getDouble() % NumericValue.asNumber(v2).getDouble()));
expression.addBinaryOperator("^", precedence.get("exponent^"), false, (v1, v2) ->
new NumericValue(java.lang.Math.pow(NumericValue.asNumber(v1).getDouble(), NumericValue.asNumber(v2).getDouble())));
// lazy cause RHS is only conditional
expression.addLazyBinaryOperator("&&", precedence.get("and&&"), false, (c, t, lv1, lv2) ->
{
expression.addLazyBinaryOperator("&&", 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);
if (!v1.getBoolean()) return (cc, tt) -> v1;
return lv2;
});
expression.addPureLazyFunction("and", -1, t -> Context.Type.BOOLEAN, (c, t, lv) -> {
int last = lv.size()-1;
if (last == -1) return LazyValue.TRUE;
for (LazyValue l: lv.subList(0, last))
{
Value val = l.evalValue(c, Context.Type.BOOLEAN);
if (val instanceof FunctionUnpackedArgumentsValue)
{
for (Value it : (FunctionUnpackedArgumentsValue) val)
if (!it.getBoolean()) return (cc, tt) -> it;
}
else
{
if (!val.getBoolean()) return (cc, tt) -> val;
}
}
return lv.get(last);
});
expression.addFunctionalEquivalence("&&", "and");
// lazy cause RHS is only conditional
expression.addLazyBinaryOperator("||", precedence.get("or||"), false, (c, t, lv1, lv2) ->
expression.addLazyBinaryOperator("||", precedence.get("or||"), false, true, t -> Context.Type.BOOLEAN, (c, t, lv1, lv2) ->
{
Value v1 = lv1.evalValue(c, Context.BOOLEAN);
if (v1.getBoolean()) return (cc, tt) -> v1;
return lv2;
});
expression.addPureLazyFunction("or", -1, t -> Context.Type.BOOLEAN, (c, t, lv) -> {
int last = lv.size()-1;
if (last == -1) return LazyValue.FALSE;
for (LazyValue l: lv.subList(0, last))
{
Value val = l.evalValue(c, Context.Type.BOOLEAN);
if (val instanceof FunctionUnpackedArgumentsValue)
{
for (Value it : (FunctionUnpackedArgumentsValue) val)
if (it.getBoolean()) return (cc, tt) -> it;
}
else
{
if (val.getBoolean()) return (cc, tt) -> val;
}
}
return lv.get(last);
});
expression.addFunctionalEquivalence("||", "or");
expression.addBinaryOperator("~", precedence.get("attribute~:"), true, Value::in);
expression.addBinaryOperator(">", precedence.get("compare>=><=<"), false, (v1, v2) ->
v1.compareTo(v2) > 0 ? Value.TRUE : Value.FALSE);
BooleanValue.of(v1.compareTo(v2) > 0));
expression.addFunction("decreasing", lv -> {
int size = lv.size();
if (size < 2) return Value.TRUE;
Value prev = lv.get(0);
for (Value next: lv.subList(1, size))
{
if (prev.compareTo(next) <= 0) return Value.FALSE;
prev = next;
}
return Value.TRUE;
});
expression.addFunctionalEquivalence(">", "decreasing");
expression.addBinaryOperator(">=", precedence.get("compare>=><=<"), false, (v1, v2) ->
v1.compareTo(v2) >= 0 ? Value.TRUE : Value.FALSE);
BooleanValue.of(v1.compareTo(v2) >= 0));
expression.addFunction("nonincreasing", lv -> {
int size = lv.size();
if (size < 2) return Value.TRUE;
Value prev = lv.get(0);
for (Value next: lv.subList(1, size))
{
if (prev.compareTo(next) < 0) return Value.FALSE;
prev = next;
}
return Value.TRUE;
});
expression.addFunctionalEquivalence(">=", "nonincreasing");
expression.addBinaryOperator("<", precedence.get("compare>=><=<"), false, (v1, v2) ->
v1.compareTo(v2) < 0 ? Value.TRUE : Value.FALSE);
BooleanValue.of(v1.compareTo(v2) < 0));
expression.addFunction("increasing", lv -> {
int size = lv.size();
if (size < 2) return Value.TRUE;
Value prev = lv.get(0);
for (Value next: lv.subList(1, size))
{
if (prev.compareTo(next) >= 0) return Value.FALSE;
prev = next;
}
return Value.TRUE;
});
expression.addFunctionalEquivalence("<", "increasing");
expression.addBinaryOperator("<=", precedence.get("compare>=><=<"), false, (v1, v2) ->
v1.compareTo(v2) <= 0 ? Value.TRUE : Value.FALSE);
BooleanValue.of(v1.compareTo(v2) <= 0));
expression.addFunction("nondecreasing", lv -> {
int size = lv.size();
if (size < 2) return Value.TRUE;
Value prev = lv.get(0);
for (Value next: lv.subList(1, size))
{
if (prev.compareTo(next) > 0) return Value.FALSE;
prev = next;
}
return Value.TRUE;
});
expression.addFunctionalEquivalence("<=", "nondecreasing");
expression.addBinaryOperator("==", precedence.get("equal==!="), false, (v1, v2) ->
v1.equals(v2) ? Value.TRUE : Value.FALSE);
expression.addFunction("equal", lv -> {
int size = lv.size();
if (size < 2) return Value.TRUE;
Value prev = lv.get(0);
for (Value next: lv.subList(1, size))
{
if (!prev.equals(next)) return Value.FALSE;
prev = next;
}
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 -> {
int size = lv.size();
if (size < 2) return Value.TRUE;
// need to order them so same obejects will be next to each other.
lv.sort(Comparator.comparingInt(Value::hashCode));
Value prev = lv.get(0);
for (Value next: lv.subList(1, size))
{
if (prev.equals(next)) return Value.FALSE;
prev = next;
}
return Value.TRUE;
});
expression.addFunctionalEquivalence("!=", "unique");
// lazy cause of assignment which is non-trivial
expression.addLazyBinaryOperator("=", precedence.get("assign=<>"), false, (c, t, lv1, lv2) ->
expression.addLazyBinaryOperator("=", 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);
@@ -117,7 +275,7 @@ public class Operators {
});
// lazy due to assignment
expression.addLazyBinaryOperator("+=", precedence.get("assign=<>"), false, (c, t, lv1, lv2) ->
expression.addLazyBinaryOperator("+=", 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);
@@ -177,7 +335,7 @@ public class Operators {
return boundedLHS;
});
expression.addBinaryContextOperator("<>", precedence.get("assign=<>"), false, (c, t, v1, v2) ->
expression.addBinaryContextOperator("<>", precedence.get("assign=<>"), false, false, false, (c, t, v1, v2) ->
{
if (v1 instanceof ListValue.ListConstructorValue && v2 instanceof ListValue.ListConstructorValue)
{
@@ -218,12 +376,12 @@ public class Operators {
expression.addUnaryOperator("+", false, NumericValue::asNumber);
// could be non-lazy, but who cares - its a small one.
expression.addLazyUnaryOperator("!", precedence.get("unary+-!..."), false, (c, t, lv) ->
expression.addLazyUnaryOperator("!", 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, (c, t, lv) ->
expression.addLazyUnaryOperator("...", Operators.precedence.get("unary+-!..."), false, true, t -> t== Context.Type.LOCALIZATION?Context.NONE:t, (c, t, lv) ->
{
if (t == Context.LOCALIZATION)
return (cc, tt) -> new FunctionAnnotationValue(lv.evalValue(c), FunctionAnnotationValue.Type.VARARG);
@@ -36,7 +36,7 @@ public class Sys {
{
expression.addUnaryFunction("hash_code", v -> new NumericValue(v.hashCode()));
expression.addUnaryFunction("copy", Value::deepcopy);
expression.addImpureUnaryFunction("copy", Value::deepcopy);
expression.addTypedContextFunction("bool", 1, Context.BOOLEAN, (c, t, lv) ->
{
@@ -12,7 +12,7 @@ public class Threading
{
public static void apply(Expression expression)
{
expression.addFunctionWithDelegation("task", -1, (c, t, expr, tok, lv) ->
expression.addFunctionWithDelegation("task", -1, false, false, (c, t, expr, tok, lv) ->
{
if (lv.size() == 0)
throw new InternalExpressionException("'task' requires at least function to call as a parameter");
@@ -22,7 +22,7 @@ public class Threading
return thread;
});
expression.addFunctionWithDelegation("task_thread", -1, (c, t, expr, tok, lv) ->
expression.addFunctionWithDelegation("task_thread", -1, false, false, (c, t, expr, tok, lv) ->
{
if (lv.size() < 2)
throw new InternalExpressionException("'task' requires at least function to call as a parameter");