use of buildcraft.lib.expression.api.IExpressionNode in project BuildCraft by BuildCraft.
the class FunctionContext method getVariable.
// Variable getter/setters
public IExpressionNode getVariable(String name) {
name = name.toLowerCase(Locale.ROOT);
IExpressionNode current = variables.get(name);
if (current != null) {
return current;
}
for (FunctionContext parent : parents) {
IExpressionNode node = parent.getVariable(name);
if (node != null)
return node;
}
INodeFunc func = getFunction(name, Collections.emptyList());
if (func != null) {
try {
return func.getNode(new NodeStack());
} catch (InvalidExpressionException e) {
throw new IllegalStateException("Found a 0-args function that didn't allow us to get a node for it!", e);
}
}
return null;
}
use of buildcraft.lib.expression.api.IExpressionNode in project BuildCraft by BuildCraft.
the class InternalCompiler method makeExpression.
private static IExpressionNode makeExpression(String[] postfix, FunctionContext context) throws InvalidExpressionException {
NodeStack stack = new NodeStack();
for (int i = 0; i < postfix.length; i++) {
String op = postfix[i];
if (OPERATORS.contains(op) && !"?".equals(op) && !":".equals(op)) {
boolean isNegation = UNARY_NEGATION.equals(op);
int count = 2;
if (isNegation || OPERATORS_SINGLE.contains(op)) {
op = isNegation ? "-" : op;
count = 1;
}
String function = op + FUNCTION_ARGS + count;
pushFunctionNode(stack, function, context);
} else if (// NO-OP, all handled by "?"
":".equals(op))
// NO-OP, all handled by "?"
continue;
else if ("?".equals(op))
pushConditional(stack);
else if (isValidLong(op)) {
long val = parseValidLong(op);
stack.push(new NodeConstantLong(val));
} else if (isValidDouble(op)) {
stack.push(new NodeConstantDouble(Double.parseDouble(op)));
} else if (BOOLEAN_MATCHER.matcher(op).matches()) {
stack.push(NodeConstantBoolean.of(Boolean.parseBoolean(op)));
} else if (STRING_MATCHER.matcher(op).matches()) {
stack.push(new NodeConstantObject<>(String.class, op.substring(1, op.length() - 1)));
} else if (op.startsWith(FUNCTION_START)) {
// Its a function
String function = op.substring(1);
pushFunctionNode(stack, function, context);
} else {
IExpressionNode node = context == null ? null : context.getVariable(op);
if (node == null && op.contains(".")) {
int index = op.indexOf('.');
String type = op.substring(0, index);
FunctionContext ctx = getContext(type);
if (ctx != null) {
node = ctx.getVariable(op);
if (node == null) {
node = ctx.getVariable(op.substring(index + 1));
}
}
}
if (node != null) {
stack.push(node);
} else {
String vars = getValidVariablesErrorString(context);
throw new InvalidExpressionException("Unknown variable '" + op + "'" + vars);
}
}
}
IExpressionNode node = stack.pop().inline();
if (!stack.isEmpty()) {
throw new InvalidExpressionException("Tried to make an expression with too many nodes! (" + stack + ")");
}
return node;
}
use of buildcraft.lib.expression.api.IExpressionNode in project BuildCraft by BuildCraft.
the class NodeStack method popLong.
@Override
public INodeLong popLong() throws InvalidExpressionException {
checkTypeMatch(long.class);
IExpressionNode node = pop();
if (node instanceof INodeLong) {
return (INodeLong) node;
} else {
throw new InvalidExpressionException("Cannot cast " + node + " to a long!");
}
}
use of buildcraft.lib.expression.api.IExpressionNode in project BuildCraft by BuildCraft.
the class NodeCasting method castToType.
public static IExpressionNode castToType(IExpressionNode node, Class<?> to) throws InvalidExpressionException {
Class<?> from = NodeTypes.getType(node);
if (from == to) {
return node;
}
FunctionContext castingContext = new FunctionContext(NodeTypes.getContext(from), NodeTypes.getContext(to));
INodeFunc caster = castingContext.getFunction("(" + NodeTypes.getName(to) + ")", Collections.singletonList(from));
if (caster == null) {
if (to == String.class) {
return new NodeCastToString(node);
}
throw new InvalidExpressionException("Cannot cast from " + NodeTypes.getName(from) + " to " + NodeTypes.getName(to));
}
NodeStack stack = new NodeStack(node);
stack.setRecorder(Collections.singletonList(from), caster);
IExpressionNode casted = caster.getNode(stack);
stack.checkAndRemoveRecorder();
Class<?> actual = NodeTypes.getType(casted);
if (actual != to) {
throw new IllegalStateException("The caster " + caster + " didn't produce the correct result! (Expected " + to + ", but got " + actual + ")");
}
return casted;
}
use of buildcraft.lib.expression.api.IExpressionNode in project BuildCraft by BuildCraft.
the class InternalCompiler method pushFunctionNode.
private static void pushFunctionNode(NodeStack stack, String function, FunctionContext context) throws InvalidExpressionException {
String name = function.substring(0, function.indexOf(FUNCTION_ARGS));
String argCount = function.substring(function.indexOf(FUNCTION_ARGS) + 1);
int count = Integer.parseInt(argCount);
if (name.startsWith(".")) {
/*
* Allow object style function calling by making the called node be the first argument to the function,
* pushing all other nodes back
*/
name = name.substring(1);
count++;
}
List<IExpressionNode> popOrder = stack.peek(count);
List<Class<?>> functionOrder = new ArrayList<>(popOrder.size());
Map<List<Class<?>>, INodeFunc> functions = new HashMap<>();
if (name.contains(".")) {
int index = name.indexOf('.');
String type = name.substring(0, index);
FunctionContext ctx = getContext(type);
if (ctx != null) {
functions.putAll(ctx.getFunctions(name.substring(index + 1)));
}
}
FunctionContext[] ctxs = new FunctionContext[count + 1];
ctxs[0] = context;
int n = 1;
for (IExpressionNode node : popOrder) {
Class<?> nodeType = NodeTypes.getType(node);
functionOrder.add(0, nodeType);
ctxs[n++] = NodeTypes.getContext(nodeType);
}
functions.putAll(new FunctionContext(ctxs).getFunctions(name));
INodeFunc bestFunction = null;
int bestCastCount = Integer.MAX_VALUE;
List<INodeFunc> bestCasters = null;
List<Class<?>> bestClassesTo = null;
List<String> fnOrderNames = functionOrder.stream().map(NodeTypes::getName).collect(Collectors.toList());
ExpressionDebugManager.debugStart("Finding best function called '" + name + "' for " + fnOrderNames);
for (Map.Entry<List<Class<?>>, INodeFunc> func : functions.entrySet()) {
List<Class<?>> functionClasses = func.getKey();
List<String> fnClassNames = functionClasses.stream().map(NodeTypes::getName).collect(Collectors.toList());
ExpressionDebugManager.debugPrintln("Found " + fnClassNames);
if (functionClasses.size() != functionOrder.size()) {
continue;
}
int casts = 0;
boolean canCast = true;
List<INodeFunc> casters = new ArrayList<>();
ExpressionDebugManager.debugStart("Finding casters...");
for (int i = 0; i < functionClasses.size(); i++) {
Class<?> from = functionOrder.get(i);
Class<?> to = functionClasses.get(i);
ExpressionDebugManager.debugPrintln(" - " + NodeTypes.getName(from) + " -> " + NodeTypes.getName(to));
if (from == to) {
casters.add(a -> a.pop(from));
ExpressionDebugManager.debugPrintln(" - Equal types, no cast needed.");
continue;
}
INodeFunc caster;
if (from == long.class && to == INodeLong.class) {
caster = new NodeFuncWrapper() {
@Override
public IExpressionNode getNode(INodeStack s) throws InvalidExpressionException {
return new NodeConstantObject<>(INodeLong.class, s.popLong());
}
};
} else if (from == double.class && to == INodeDouble.class) {
caster = new NodeFuncWrapper() {
@Override
public IExpressionNode getNode(INodeStack s) throws InvalidExpressionException {
return new NodeConstantObject<>(INodeDouble.class, s.popDouble());
}
};
} else if (from == long.class && to == INodeDouble.class) {
caster = new NodeFuncWrapper() {
@Override
public IExpressionNode getNode(INodeStack s) throws InvalidExpressionException {
INodeLong node = s.popLong();
INodeDouble nodeD = new NodeCastLongToDouble(node);
return new NodeConstantObject<>(INodeDouble.class, nodeD.inline());
}
};
} else if (from == boolean.class && to == INodeBoolean.class) {
caster = new NodeFuncWrapper() {
@Override
public IExpressionNode getNode(INodeStack s) throws InvalidExpressionException {
return new NodeConstantObject<>(INodeBoolean.class, s.popBoolean());
}
};
} else {
FunctionContext castingCtx = new FunctionContext(NodeTypes.getContext(from), NodeTypes.getContext(to));
caster = castingCtx.getFunction("(" + NodeTypes.getName(to) + ")", Collections.singletonList(from));
if (caster == null) {
ExpressionDebugManager.debugPrintln(" - No cast found!");
canCast = false;
break;
}
}
casts++;
casters.add(caster);
ExpressionDebugManager.debugPrintln(" - Caster = " + caster);
}
ExpressionDebugManager.debugEnd("");
if (!canCast) {
continue;
}
if (casts < bestCastCount) {
bestCastCount = casts;
bestFunction = func.getValue();
bestCasters = casters;
bestClassesTo = functionClasses;
}
}
ExpressionDebugManager.debugEnd("Best = " + bestFunction);
if (bestFunction == null || bestCasters == null) {
// Allow any object to be compared to itself with == and !=
boolean isEq = "==".equals(name);
boolean isNE = "!=".equals(name);
if (count == 2 && (isEq | isNE) && functionOrder.get(0) == functionOrder.get(1)) {
Class<?> cls = functionOrder.get(0);
NodeFuncObjectObjectToBoolean.IFuncObjectObjectToBoolean<?, ?> func = isEq ? Objects::equals : (a, b) -> !Objects.equals(a, b);
bestFunction = new NodeFuncObjectObjectToBoolean(name, cls, cls, func);
bestCastCount = 0;
bestCasters = Collections.emptyList();
bestClassesTo = new ArrayList<>(functionOrder);
ExpressionDebugManager.debugPrintln("Using implicit object equality comparison.");
} else {
throw new InvalidExpressionException("No viable function called '" + name + "' found for " + fnOrderNames + getValidFunctionsErrorString(context));
}
}
NodeStack realStack;
if (bestCastCount == 0) {
realStack = stack;
} else {
IExpressionNode[] nodes = new IExpressionNode[count];
for (int i = count - 1; i >= 0; i--) {
INodeFunc caster = bestCasters.get(i);
Class<?> from = functionOrder.get(i);
Class<?> to = bestClassesTo.get(i);
stack.setRecorder(Collections.singletonList(from), caster);
IExpressionNode node = caster.getNode(stack);
stack.checkAndRemoveRecorder();
Class<?> actual = NodeTypes.getType(node);
if (actual != to) {
throw new IllegalStateException("The caster " + caster + " didn't produce the correct result! (Expected " + to + ", but got " + actual + ")");
}
nodes[i] = node;
}
ExpressionDebugManager.debugPrintln("Nodes = " + nodes);
realStack = new NodeStack(nodes);
}
INodeFunc func = bestFunction;
bestClassesTo = new ArrayList<>(bestClassesTo);
Collections.reverse(bestClassesTo);
realStack.setRecorder(bestClassesTo, func);
IExpressionNode node = func.getNode(realStack);
realStack.checkAndRemoveRecorder();
stack.push(node);
}
Aggregations