use of buildcraft.lib.expression.api.INodeStack in project BuildCraft by BuildCraft.
the class NodeCasting method castToObject.
public static <T> INodeFuncObject<T> castToObject(INodeFunc func, Class<T> to) throws InvalidExpressionException {
Class<?> from = NodeTypes.getType(func);
if (from == to) {
return (INodeFuncObject<T>) func;
}
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 (INodeFuncObject<T>) castToString(func);
}
throw new InvalidExpressionException("Cannot cast from " + NodeTypes.getName(from) + " to " + NodeTypes.getName(to));
}
return new INodeFuncObject<T>() {
@Override
public INodeObject<T> getNode(INodeStack stack) throws InvalidExpressionException {
return (INodeObject<T>) caster.getNode(new NodeStack(func.getNode(stack)));
}
@Override
public Class<T> getType() {
return to;
}
};
}
use of buildcraft.lib.expression.api.INodeStack 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