Files
fabric-carpet/src/main/java/carpet/script/language/ControlFlow.java
T

150 lines
6.3 KiB
Java

package carpet.script.language;
import carpet.script.Context;
import carpet.script.Expression;
import carpet.script.LazyValue;
import carpet.script.exception.ExitStatement;
import carpet.script.exception.InternalExpressionException;
import carpet.script.exception.ProcessedThrowStatement;
import carpet.script.exception.ThrowStatement;
import carpet.script.exception.Throwables;
import carpet.script.value.ListValue;
import carpet.script.value.MapValue;
import carpet.script.value.NumericValue;
import carpet.script.value.StringValue;
import carpet.script.value.Value;
import com.google.common.collect.ImmutableMap;
import java.util.stream.Collectors;
public class ControlFlow {
public static void apply(Expression expression) // public just to get the javadoc right
{
expression.addLazyBinaryOperator(";", Operators.precedence.get("nextop;"), true, (c, t, lv1, lv2) ->
{
lv1.evalValue(c, Context.VOID);
Value v2 = lv2.evalValue(c, t);
return (cc, tt) -> v2;
});
// if(cond1, expr1, cond2, expr2, ..., ?default) => value
expression.addLazyFunction("if", -1, (c, t, lv) ->
{
if ( lv.size() < 2 )
throw new InternalExpressionException("'if' statement needs to have at least one condition and one case");
for (int i=0; i<lv.size()-1; i+=2)
{
if (lv.get(i).evalValue(c, Context.BOOLEAN).getBoolean())
{
//int iFinal = i;
Value ret = lv.get(i+1).evalValue(c, t);
return (cc, tt) -> ret;
}
}
if (lv.size()%2 == 1)
{
Value ret = lv.get(lv.size() - 1).evalValue(c, t);
return (cc, tt) -> ret;
}
return (cc, tt) -> Value.NULL;
});
expression.addFunction("exit", (lv) -> { throw new ExitStatement(lv.size()==0?Value.NULL:lv.get(0)); });
expression.addLazyFunction("throw", -1, (c, t, lv)->
{
switch (lv.size())
{
case 0:
throw new ThrowStatement(Value.NULL, Throwables.USER_DEFINED);
case 1:
throw new ThrowStatement(lv.get(0).evalValue(c), Throwables.USER_DEFINED );
case 2:
throw new ThrowStatement(lv.get(1).evalValue(c), Throwables.getTypeForException(lv.get(0).evalValue(c).getString()));
case 3:
throw new ThrowStatement(lv.get(2).evalValue(c), Throwables.getTypeForException(lv.get(1).evalValue(c).getString()), lv.get(0).evalValue(c).getString());
default:
throw new InternalExpressionException("throw() can't accept more than 3 parameters");
}
});
expression.addLazyFunction("try", -1, (c, t, lv) ->
{
if (lv.size()==0)
throw new InternalExpressionException("'try' needs at least an expression block, and either a catch_epr, or a number of pairs of filters and catch_expr");
try
{
Value retval = lv.get(0).evalValue(c, t);
return (c_, t_) -> retval;
}
catch (ProcessedThrowStatement ret)
{
if (lv.size() == 1)
{
if (!ret.thrownExceptionType.isUserException())
throw ret;
return (c_, t_) -> Value.NULL;
}
if (lv.size() > 3 && lv.size() % 2 == 0)
{
throw new InternalExpressionException("Try-catch block needs the code to run, and either a catch expression for user thrown exceptions, or a number of pairs of filters and catch expressions");
}
Value val = null; // This is always assigned at some point, just the compiler doesn't know
LazyValue __ = c.getVariable("_");
c.setVariable("_", (__c, __t) -> ret.data.reboundedTo("_"));
LazyValue _trace = c.getVariable("_trace");
c.setVariable("_trace", (__c, __t) -> MapValue.wrap(ImmutableMap.of(
StringValue.of("stack"), ListValue.wrap(ret.stack.stream().map(f -> ListValue.of(
StringValue.of(f.getModule().getName()),
StringValue.of(f.getString()),
NumericValue.of(f.getToken().lineno+1),
NumericValue.of(f.getToken().linepos+1)
)).collect(Collectors.toList())),
StringValue.of("locals"), MapValue.wrap(ret.context.variables.entrySet().stream().filter(e -> !e.getKey().equals("_trace")).collect(Collectors.toMap(
e -> StringValue.of(e.getKey()),
e -> e.getValue().evalValue(ret.context)
))),
StringValue.of("token"), ListValue.of(
StringValue.of(ret.token.surface),
NumericValue.of(ret.token.lineno+1),
NumericValue.of(ret.token.linepos+1)
)
)));
if (lv.size() == 2)
{
if (ret.thrownExceptionType.isUserException())
val = lv.get(1).evalValue(c, t);
}
else
{
int pointer = 1;
while (pointer < lv.size() -1)
{
if (ret.thrownExceptionType.isRelevantFor(lv.get(pointer).evalValue(c).getString()))
{
val = lv.get(pointer + 1).evalValue(c, t);
break;
}
pointer += 2;
}
}
c.setVariable("_", __);
if (_trace != null)
c.setVariable("_trace", _trace);
else
c.delVariable("_trace");
if (val == null) // not handled
{
throw ret;
}
Value retval = val;
return (c_, t_) -> retval;
}
});
}
}