use of buildcraft.lib.expression.api.InvalidExpressionException 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);
}
use of buildcraft.lib.expression.api.InvalidExpressionException in project BuildCraft by BuildCraft.
the class InternalCompiler method convertToPostfix.
private static String[] convertToPostfix(String[] infix) throws InvalidExpressionException {
// Implementation of https://en.wikipedia.org/wiki/Shunting-yard_algorithm
Deque<String> stack = new ArrayDeque<>();
List<String> postfix = new ArrayList<>();
int index = 0;
ExpressionDebugManager.debugPrintln("Converting " + Arrays.toString(infix));
ExpressionDebugManager.debugPrintln(" Stack=" + stack + ", postfix=" + postfix);
boolean justPushedFunc = false;
for (index = 0; index < infix.length; index++) {
String token = infix[index];
ExpressionDebugManager.debugPrintln(" - Token \"" + token + "\"");
if (justPushedFunc && !")".equals(token)) {
if (stack.peek() != null && stack.peek().startsWith(FUNCTION_START)) {
stack.push(",");
}
}
justPushedFunc = false;
if (",".equals(token)) {
int commas = 1;
boolean found = false;
while (!stack.isEmpty()) {
String fromStack = stack.pop();
if ("(".equals(fromStack) || fromStack.startsWith(FUNCTION_START)) {
found = true;
stack.push(fromStack);
break;
} else if (",".equals(fromStack)) {
commas++;
} else {
postfix.add(fromStack);
}
}
for (int i = 0; i < commas; i++) {
stack.push(",");
}
if (!found) {
throw new InvalidExpressionException("Did not find an opening parenthesis for the comma!");
}
} else if ("(".equals(token)) {
stack.push(token);
} else if (")".equals(token)) {
int commas = 0;
boolean found = false;
while (!stack.isEmpty()) {
String fromStack = stack.pop();
if ("(".equals(fromStack)) {
found = true;
break;
} else if (fromStack.startsWith(FUNCTION_START)) {
found = true;
// Add it back onto the stack to be used later
fromStack = fromStack + FUNCTION_ARGS + commas;
postfix.add(fromStack);
break;
} else if (",".equals(fromStack)) {
commas++;
} else {
postfix.add(fromStack);
}
}
if (!found) {
throw new InvalidExpressionException("Too many closing parenthesis!");
}
} else if (":".equals(token)) {
String s;
while ((s = stack.peek()) != null) {
if ("?".equals(s)) {
break;
} else {
postfix.add(stack.pop());
}
}
} else if (OPERATORS.contains(token)) {
// Its an operator
if ("-".equals(token) && (index == 0 || (OPERATORS + "(,").contains(infix[index - 1]))) {
token = UNARY_NEGATION;
}
String s;
while ((s = stack.peek()) != null) {
int tokenPrec = getPrecedence(token);
int stackPrec = getPrecedence(s);
boolean continueIfEqual = !"?".contains(token);
boolean shouldContinue = leftAssociative.contains(token) && (continueIfEqual ? tokenPrec <= stackPrec : tokenPrec < stackPrec);
if (!shouldContinue && rightAssociative.contains(token)) {
if (tokenPrec > stackPrec)
shouldContinue = true;
}
if (shouldContinue) {
postfix.add(stack.pop());
} else {
break;
}
}
stack.push(token);
} else if (index + 1 < infix.length && "(".equals(infix[index + 1])) {
justPushedFunc = true;
// Its a function (The next token is an open parenthesis)
stack.push(FUNCTION_START + token);
// Also ignore the parenthesis (the function is treated as if it was an open parenthesis)
index++;
} else {
// Either an argument, number, string or boolean
postfix.add(token);
}
ExpressionDebugManager.debugPrintln(" Stack=" + stack + ", postfix=" + postfix);
}
while (!stack.isEmpty()) {
String operator = stack.pop();
ExpressionDebugManager.debugPrintln(" - Operator \"" + operator + "\"");
if ("(".equals(operator)) {
throw new InvalidExpressionException("Too many opening parenthesis!");
} else if (")".equals(operator)) {
throw new InvalidExpressionException("Too many closing parenthesis!");
} else {
postfix.add(operator);
}
ExpressionDebugManager.debugPrintln(" Stack=" + stack + ", postfix=" + postfix);
}
return postfix.toArray(new String[postfix.size()]);
}
use of buildcraft.lib.expression.api.InvalidExpressionException in project BuildCraft by BuildCraft.
the class InternalCompiler method pushConditional.
private static void pushConditional(NodeStack stack) throws InvalidExpressionException {
IExpressionNode right = stack.pop();
IExpressionNode left = stack.pop();
IExpressionNode conditional = stack.pop();
right = convertBinary(right, left);
left = convertBinary(left, right);
if (conditional instanceof INodeBoolean) {
INodeBoolean condition = (INodeBoolean) conditional;
if (right instanceof INodeBoolean) {
stack.push(new NodeConditionalBoolean(condition, (INodeBoolean) left, (INodeBoolean) right));
} else if (right instanceof INodeDouble) {
stack.push(new NodeConditionalDouble(condition, (INodeDouble) left, (INodeDouble) right));
} else if (right instanceof INodeObject) {
stack.push(new NodeConditionalObject(condition, (INodeObject) left, (INodeObject) right));
} else if (right instanceof INodeLong) {
stack.push(new NodeConditionalLong(condition, (INodeLong) left, (INodeLong) right));
} else {
throw new InvalidExpressionException("Unknown node " + left);
}
} else {
throw new InvalidExpressionException("Required a boolean node, but got '" + conditional + "' of " + conditional.getClass());
}
}
use of buildcraft.lib.expression.api.InvalidExpressionException in project BuildCraft by BuildCraft.
the class NodeStack method popObject.
@Override
public <T> INodeObject<T> popObject(Class<T> type) throws InvalidExpressionException {
checkTypeMatch(type);
IExpressionNode node = pop();
if (node instanceof INodeObject) {
INodeObject<?> nodeObj = (INodeObject<?>) node;
if (nodeObj.getType() == type) {
return (INodeObject<T>) nodeObj;
} else {
throw new InvalidExpressionException("Cannot cast " + nodeObj.getType().getSimpleName() + " to " + type.getSimpleName() + "!");
}
} else {
throw new InvalidExpressionException("Cannot cast " + node + " to " + type.getSimpleName() + "!");
}
}
use of buildcraft.lib.expression.api.InvalidExpressionException in project BuildCraft by BuildCraft.
the class NodeStack method pop.
public IExpressionNode pop() throws InvalidExpressionException {
if (stack.isEmpty()) {
throw new InvalidExpressionException("No more nodes to pop!");
} else {
IExpressionNode node = stack.remove(stack.size() - 1);
ExpressionDebugManager.debugPrintln("Popped " + node);
return node;
}
}
Aggregations