diff options
| author | bjculkin <bjculkin@WIT-136XG42.wvu-ad.wvu.edu> | 2017-02-27 10:08:50 -0500 |
|---|---|---|
| committer | bjculkin <bjculkin@WIT-136XG42.wvu-ad.wvu.edu> | 2017-02-27 10:08:50 -0500 |
| commit | 79ee129fc0d36ad10bceb942262f2842419c030c (patch) | |
| tree | d1298fdb8b81726f4b9012d7a29c3029a55a3aa7 /dice-lang/src/bjc/dicelang/v1/ast/DiceASTEvaluator.java | |
| parent | c50a0744269ce22604c0604cc69e6d5e5ce8a3fc (diff) | |
Pacakge reorganization
Diffstat (limited to 'dice-lang/src/bjc/dicelang/v1/ast/DiceASTEvaluator.java')
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v1/ast/DiceASTEvaluator.java | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/dice-lang/src/bjc/dicelang/v1/ast/DiceASTEvaluator.java b/dice-lang/src/bjc/dicelang/v1/ast/DiceASTEvaluator.java new file mode 100644 index 0000000..c8c0032 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/v1/ast/DiceASTEvaluator.java @@ -0,0 +1,320 @@ +package bjc.dicelang.v1.ast; + +import java.util.function.Supplier; + +import bjc.dicelang.v1.ComplexDice; +import bjc.dicelang.v1.ast.nodes.DiceASTType; +import bjc.dicelang.v1.ast.nodes.DiceLiteralNode; +import bjc.dicelang.v1.ast.nodes.DiceLiteralType; +import bjc.dicelang.v1.ast.nodes.IDiceASTNode; +import bjc.dicelang.v1.ast.nodes.ILiteralDiceNode; +import bjc.dicelang.v1.ast.nodes.IntegerLiteralNode; +import bjc.dicelang.v1.ast.nodes.OperatorDiceNode; +import bjc.dicelang.v1.ast.nodes.VariableDiceNode; +import bjc.utils.data.IHolder; +import bjc.utils.data.IPair; +import bjc.utils.data.ITree; +import bjc.utils.data.Identity; +import bjc.utils.data.LazyPair; +import bjc.utils.data.Pair; +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.IList; +import bjc.utils.funcdata.IMap; +import bjc.utils.data.Tree; + +/** + * Evaluate a dice AST to an integer value + * + * @author ben + * + */ +public class DiceASTEvaluator { + private static IResult bindLiteralValue(IDiceASTNode leafNode, + IMap<String, ITree<IDiceASTNode>> enviroment) { + String variableName = ((VariableDiceNode) leafNode).getVariable(); + + if (enviroment.containsKey(variableName)) { + IResult result = evaluateAST(enviroment.get(variableName), + enviroment); + + return result; + } + + // Return a DummyResult to handle lets properly + return new DummyResult( + "Attempted to deref unbound variable " + variableName); + } + + /** + * Build the map of operations to use when collapsing the AST + * + * @param enviroment + * The enviroment to evaluate bindings and such against + * @return The operations to use when collapsing the AST + */ + private static IMap<IDiceASTNode, IOperatorCollapser> buildOperations( + IMap<String, ITree<IDiceASTNode>> enviroment) { + IMap<IDiceASTNode, IOperatorCollapser> operatorCollapsers = new FunctionalMap<>(); + + operatorCollapsers.put(OperatorDiceNode.ADD, + new ArithmeticCollapser(OperatorDiceNode.ADD, + (left, right) -> left + right, 0)); + + operatorCollapsers.put(OperatorDiceNode.SUBTRACT, + new ArithmeticCollapser(OperatorDiceNode.SUBTRACT, + (left, right) -> left - right, 0)); + + operatorCollapsers.put(OperatorDiceNode.MULTIPLY, + new ArithmeticCollapser(OperatorDiceNode.MULTIPLY, + (left, right) -> left * right, 1)); + + operatorCollapsers.put(OperatorDiceNode.DIVIDE, + new ArithmeticCollapser(OperatorDiceNode.DIVIDE, + (left, right) -> left / right, 1)); + + operatorCollapsers.put(OperatorDiceNode.ASSIGN, (nodes) -> { + return parseBinding(enviroment, nodes); + }); + + operatorCollapsers.put(OperatorDiceNode.COMPOUND, + new ArithmeticCollapser(OperatorDiceNode.COMPOUND, + (left, right) -> { + return Integer.parseInt(Integer.toString(left) + + Integer.toString(right)); + }, 0)); + + operatorCollapsers.put(OperatorDiceNode.GROUP, + DiceASTEvaluator::parseGroup); + + operatorCollapsers.put(OperatorDiceNode.LET, (nodes) -> { + // @TODO Fix lets prematurely evaluating things + return parseLet(enviroment, nodes); + }); + + operatorCollapsers.put(OperatorDiceNode.ARRAY, (nodes) -> { + + // This is so that arrays respect lazy results properly + Supplier<IResult> resultSupplier = () -> { + IList<IResult> resultList = new FunctionalList<>(); + + nodes.forEach((node) -> { + resultList.add(node.getLeft()); + }); + + return new ArrayResult(resultList); + }; + + Supplier<ITree<IDiceASTNode>> treeSupplier = () -> { + ITree<IDiceASTNode> returnedTree = new Tree<>( + OperatorDiceNode.ARRAY); + + nodes.forEach((element) -> { + returnedTree.addChild(element.getRight()); + }); + + return returnedTree; + }; + + return new LazyPair<>(resultSupplier, treeSupplier); + }); + + return operatorCollapsers; + } + + private static void doArrayAssign( + IMap<String, ITree<IDiceASTNode>> enviroment, + IPair<IResult, ITree<IDiceASTNode>> nameNode, + ITree<IDiceASTNode> nameTree, ITree<IDiceASTNode> valueTree, + IHolder<Integer> childCount, ITree<IDiceASTNode> child) { + if (nameTree.getHead().getType() != DiceASTType.VARIABLE) { + throw new UnsupportedOperationException( + "Assigning to complex variables isn't supported. Problem node is " + + nameNode.getRight()); + } + + String varName = child.transformHead((nameNod) -> { + return ((VariableDiceNode) nameNod).getVariable(); + }); + + enviroment.put(varName, valueTree.getChild(childCount.getValue())); + + childCount.transform(val -> val + 1); + } + + /** + * Evaluate the provided AST to a numeric value + * + * @param expression + * The expression to evaluate + * @param enviroment + * The enviroment to look up variables in + * @return The integer value of the expression + */ + public static IResult evaluateAST(ITree<IDiceASTNode> expression, + IMap<String, ITree<IDiceASTNode>> enviroment) { + IMap<IDiceASTNode, IOperatorCollapser> collapsers = buildOperations( + enviroment); + + return expression.collapse( + (node) -> evaluateLeaf(node, enviroment), collapsers::get, + (pair) -> pair.getLeft()); + } + + private static IPair<IResult, ITree<IDiceASTNode>> evaluateLeaf( + IDiceASTNode leafNode, + IMap<String, ITree<IDiceASTNode>> enviroment) { + ITree<IDiceASTNode> returnedAST = new Tree<>(leafNode); + + switch (leafNode.getType()) { + case LITERAL: + return new Pair<>(evaluateLiteral(leafNode), returnedAST); + + case VARIABLE: + return new LazyPair<>(() -> { + return bindLiteralValue(leafNode, enviroment); + }, () -> returnedAST); + + case OPERATOR: + default: + throw new UnsupportedOperationException( + "Node '" + leafNode + "' cannot be a leaf."); + } + } + + private static IResult evaluateLiteral(IDiceASTNode leafNode) { + DiceLiteralType literalType = ((ILiteralDiceNode) leafNode) + .getLiteralType(); + + switch (literalType) { + case DICE: + int diceRoll = ((DiceLiteralNode) leafNode).getValue() + .roll(); + + return new IntegerResult(diceRoll); + case INTEGER: + int val = ((IntegerLiteralNode) leafNode).getValue(); + + return new IntegerResult(val); + default: + throw new UnsupportedOperationException("Literal value '" + + leafNode + "' is of a type (" + literalType + + ") not currently supported."); + } + } + + private static IPair<IResult, ITree<IDiceASTNode>> parseBinding( + IMap<String, ITree<IDiceASTNode>> enviroment, + IList<IPair<IResult, ITree<IDiceASTNode>>> nodes) { + if (nodes.getSize() != 2) { + throw new UnsupportedOperationException( + "Can only bind nodes with two children. Problem children are " + + nodes); + } + + IPair<IResult, ITree<IDiceASTNode>> nameNode = nodes.getByIndex(0); + IPair<IResult, ITree<IDiceASTNode>> valueNode = nodes + .getByIndex(1); + + return nameNode.bindRight((nameTree) -> { + return valueNode.bind((valueValue, valueTree) -> { + if (DiceASTUtils.containsSimpleVariable(nameTree)) { + String varName = nameTree.transformHead((nameNod) -> { + return ((VariableDiceNode) nameNod).getVariable(); + }); + + enviroment.put(varName, valueTree); + + return new Pair<>(valueValue, nameTree); + } else if (nameTree.getHead() == OperatorDiceNode.ARRAY) { + if (valueTree.getHead() == OperatorDiceNode.ARRAY) { + if (nameTree.getChildrenCount() != valueTree + .getChildrenCount()) { + throw new UnsupportedOperationException( + "Array assignment must be between two equal length arrays"); + } + + IHolder<Integer> childCount = new Identity<>(0); + + nameTree.doForChildren((child) -> { + doArrayAssign(enviroment, nameNode, nameTree, + valueTree, childCount, child); + + childCount.transform(val -> val + 1); + }); + + return new Pair<>(valueValue, nameTree); + } + + nameTree.doForChildren((child) -> { + String varName = child.transformHead((nameNod) -> { + return ((VariableDiceNode) nameNod) + .getVariable(); + }); + + enviroment.put(varName, valueTree); + }); + + return new Pair<>(valueValue, nameTree); + } + + throw new UnsupportedOperationException( + "Assigning to complex variables isn't supported. Problem node is " + + nameNode.getRight()); + }); + }); + } + + private static IPair<IResult, ITree<IDiceASTNode>> parseGroup( + IList<IPair<IResult, ITree<IDiceASTNode>>> nodes) { + if (nodes.getSize() != 2) { + throw new UnsupportedOperationException( + "Can only form a group from two dice"); + } + + IPair<IResult, ITree<IDiceASTNode>> numberDiceNode = nodes + .getByIndex(0); + IPair<IResult, ITree<IDiceASTNode>> diceTypeNode = nodes + .getByIndex(1); + + return numberDiceNode.bind((numberDiceValue, numberDiceTree) -> { + return diceTypeNode.bind((diceTypeValue, diceTypeTree) -> { + ComplexDice cDice = new ComplexDice( + ((IntegerResult) numberDiceValue).getValue(), + ((IntegerResult) diceTypeValue).getValue()); + + return new Pair<>(new IntegerResult(cDice.roll()), + new Tree<>(OperatorDiceNode.GROUP, numberDiceTree, + diceTypeTree)); + }); + }); + } + + private static IPair<IResult, ITree<IDiceASTNode>> parseLet( + IMap<String, ITree<IDiceASTNode>> enviroment, + IList<IPair<IResult, ITree<IDiceASTNode>>> nodes) { + if (nodes.getSize() != 2) { + throw new UnsupportedOperationException( + "Can only use let with two expressions."); + } + + ITree<IDiceASTNode> bindTree = nodes.getByIndex(0).getRight(); + ITree<IDiceASTNode> expressionTree = nodes.getByIndex(1) + .getRight(); + + IMap<String, ITree<IDiceASTNode>> letEnviroment = enviroment + .extend(); + + System.out.println("Evaluating tree for bound values"); + + evaluateAST(bindTree, letEnviroment); + + IResult exprResult = evaluateAST(expressionTree, letEnviroment); + + IList<ITree<IDiceASTNode>> childrn = nodes + .map((pair) -> pair.getRight()); + + return new Pair<>(exprResult, + new Tree<>(OperatorDiceNode.LET, childrn)); + } +} |
