summaryrefslogtreecommitdiff
path: root/dice-lang/src/bjc/dicelang/v1/ast/DiceASTEvaluator.java
diff options
context:
space:
mode:
authorbjculkin <bjculkin@WIT-136XG42.wvu-ad.wvu.edu>2017-02-27 10:08:50 -0500
committerbjculkin <bjculkin@WIT-136XG42.wvu-ad.wvu.edu>2017-02-27 10:08:50 -0500
commit79ee129fc0d36ad10bceb942262f2842419c030c (patch)
treed1298fdb8b81726f4b9012d7a29c3029a55a3aa7 /dice-lang/src/bjc/dicelang/v1/ast/DiceASTEvaluator.java
parentc50a0744269ce22604c0604cc69e6d5e5ce8a3fc (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.java320
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));
+ }
+}