From 523eea5fdbecaea141f3aafe89b307f1e9e75b25 Mon Sep 17 00:00:00 2001 From: bculkin2442 Date: Mon, 21 Mar 2016 21:42:27 -0400 Subject: Polished up work on AST-based dice --- .../examples/parsing/DiceASTLanguageState.java | 19 +++ .../examples/parsing/DiceASTLanguageTest.java | 38 ++++-- .../java/bjc/utils/dice/ast/DiceASTExpression.java | 144 +++++++++++++++++++++ .../java/bjc/utils/dice/ast/DiceASTFlattener.java | 2 +- .../java/bjc/utils/dice/ast/DiceASTFreezer.java | 47 +++++++ .../java/bjc/utils/dice/ast/LiteralDiceNode.java | 5 + .../java/bjc/utils/dice/ast/OperatorDiceNode.java | 6 +- .../java/bjc/utils/dice/ast/VariableDiceNode.java | 5 + 8 files changed, 254 insertions(+), 12 deletions(-) create mode 100644 BJC-Utils2/src/examples/java/bjc/utils/examples/parsing/DiceASTLanguageState.java create mode 100644 BJC-Utils2/src/main/java/bjc/utils/dice/ast/DiceASTExpression.java create mode 100644 BJC-Utils2/src/main/java/bjc/utils/dice/ast/DiceASTFreezer.java diff --git a/BJC-Utils2/src/examples/java/bjc/utils/examples/parsing/DiceASTLanguageState.java b/BJC-Utils2/src/examples/java/bjc/utils/examples/parsing/DiceASTLanguageState.java new file mode 100644 index 0000000..82dccf5 --- /dev/null +++ b/BJC-Utils2/src/examples/java/bjc/utils/examples/parsing/DiceASTLanguageState.java @@ -0,0 +1,19 @@ +package bjc.utils.examples.parsing; + +import java.util.Map; + +import bjc.utils.data.Pair; +import bjc.utils.dice.DiceExpressionParser; +import bjc.utils.dice.ast.DiceASTExpression; + +public class DiceASTLanguageState + extends Pair> { + + public DiceASTLanguageState() { + } + + public DiceASTLanguageState(DiceExpressionParser left, + Map right) { + super(left, right); + } +} diff --git a/BJC-Utils2/src/examples/java/bjc/utils/examples/parsing/DiceASTLanguageTest.java b/BJC-Utils2/src/examples/java/bjc/utils/examples/parsing/DiceASTLanguageTest.java index 1a93da9..f8034c8 100644 --- a/BJC-Utils2/src/examples/java/bjc/utils/examples/parsing/DiceASTLanguageTest.java +++ b/BJC-Utils2/src/examples/java/bjc/utils/examples/parsing/DiceASTLanguageTest.java @@ -7,27 +7,44 @@ import java.util.function.BiConsumer; import bjc.utils.dice.DiceExpressionParser; import bjc.utils.dice.IDiceExpression; -import bjc.utils.dice.ast.DiceASTFlattener; +import bjc.utils.dice.ast.DiceASTExpression; +import bjc.utils.dice.ast.DiceASTFreezer; import bjc.utils.dice.ast.DiceASTParser; +import bjc.utils.dice.ast.IDiceASTNode; +import bjc.utils.parserutils.AST; public class DiceASTLanguageTest { - private static Map> acts; + private static Map> acts; static { acts = new HashMap<>(); acts.put("roll", DiceASTLanguageTest::rollReference); acts.put("env", DiceASTLanguageTest::printEnv); + acts.put("freeze", DiceASTLanguageTest::freezeVar); } - public static void printEnv(String ln, DiceLanguageState stat) { + private static void freezeVar(String ln, DiceASTLanguageState stat) { + String[] strangs = ln.split(" "); + + System.out.println("Freezing references in " + strangs[1]); + + stat.doWith((dep, env) -> { + env.put(strangs[1], new DiceASTExpression( + DiceASTFreezer.freezeAST(env.get(strangs[1]), env), + env)); + }); + } + + public static void printEnv(String ln, DiceASTLanguageState stat) { System.out.println("Printing enviroment for debugging purposes."); stat.doWith((dep, env) -> env.forEach((key, exp) -> System.out .println("\tKey: " + key + "\tExp: " + exp.toString()))); } - public static void rollReference(String ln, DiceLanguageState stat) { + public static void rollReference(String ln, + DiceASTLanguageState stat) { String[] strangs = ln.split(" "); System.out.println("\tRolling dice expression " + strangs[1] + " " @@ -35,8 +52,8 @@ public class DiceASTLanguageTest { int nRolls = Integer.parseInt(strangs[2]); - IDiceExpression dexp = stat - .merge((dep, env) -> env.get(strangs[1])); + IDiceExpression dexp = + stat.merge((dep, env) -> env.get(strangs[1])); for (int i = 1; i <= nRolls; i++) { int roll = dexp.roll(); @@ -55,8 +72,8 @@ public class DiceASTLanguageTest { DiceASTParser dap = new DiceASTParser(); DiceExpressionParser dep = new DiceExpressionParser(); - Map env = new HashMap<>(); - DiceLanguageState state = new DiceLanguageState(dep, env); + Map env = new HashMap<>(); + DiceASTLanguageState state = new DiceASTLanguageState(dep, env); while (!ln.equalsIgnoreCase("quit")) { String header = ln.split(" ")[0]; @@ -65,8 +82,9 @@ public class DiceASTLanguageTest { acts.get(header).accept(ln, state); } else { - IDiceExpression exp = DiceASTFlattener - .flatten(dap.buildAST(ln), env); + AST builtAST = dap.buildAST(ln); + DiceASTExpression exp = + new DiceASTExpression(builtAST, env); System.out.println("\tParsed: " + exp.toString()); System.out.println("\tSample Roll: " + exp.roll()); diff --git a/BJC-Utils2/src/main/java/bjc/utils/dice/ast/DiceASTExpression.java b/BJC-Utils2/src/main/java/bjc/utils/dice/ast/DiceASTExpression.java new file mode 100644 index 0000000..3b81888 --- /dev/null +++ b/BJC-Utils2/src/main/java/bjc/utils/dice/ast/DiceASTExpression.java @@ -0,0 +1,144 @@ +package bjc.utils.dice.ast; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.BinaryOperator; + +import org.apache.commons.lang3.StringUtils; + +import bjc.utils.data.Pair; +import bjc.utils.dice.ComplexDice; +import bjc.utils.dice.CompoundDice; +import bjc.utils.dice.IDiceExpression; +import bjc.utils.parserutils.AST; + +public class DiceASTExpression implements IDiceExpression { + private AST ast; + private Map env; + + public DiceASTExpression(AST ast, + Map env) { + this.ast = ast; + this.env = env; + } + + private Pair> evalLeaf(IDiceASTNode tokn) { + if (tokn instanceof VariableDiceNode) { + String varName = ((VariableDiceNode) tokn).getVariable(); + + if (env.containsKey(varName)) { + return new Pair<>(env.get(varName).roll(), new AST<>(tokn)); + } else { + // Handle special case for defining variables + return new Pair<>(0, new AST<>(tokn)); + } + } else { + LiteralDiceNode lnod = (LiteralDiceNode) tokn; + String dat = lnod.getData(); + + if (StringUtils.countMatches(dat, 'c') == 1 + && !dat.equalsIgnoreCase("c")) { + String[] strangs = dat.split("c"); + return new Pair<>(new CompoundDice(strangs).roll(), + new AST<>(tokn)); + } else if (StringUtils.countMatches(dat, 'd') == 1 + && !dat.equalsIgnoreCase("d")) { + /* + * Handle dice groups + */ + return new Pair<>(ComplexDice.fromString(dat).roll(), + new AST<>(tokn)); + } else { + return new Pair<>(Integer.parseInt(dat), new AST<>(tokn)); + } + } + } + + private static + Map>>> + buildOperations(Map env) { + Map>>> opCollapsers = + new HashMap<>(); + + opCollapsers.put(OperatorDiceNode.ADD, (left, right) -> { + return left.merge((lval, last) -> right.merge((rval, rast) -> { + return new Pair<>(lval + rval, + new AST<>(OperatorDiceNode.ADD, last, rast)); + })); + + }); + opCollapsers.put(OperatorDiceNode.SUBTRACT, (left, right) -> { + return left.merge((lval, last) -> right.merge((rval, rast) -> { + return new Pair<>(lval - rval, + new AST<>(OperatorDiceNode.SUBTRACT, last, rast)); + })); + + }); + opCollapsers.put(OperatorDiceNode.MULTIPLY, (left, right) -> { + return left.merge((lval, last) -> right.merge((rval, rast) -> { + return new Pair<>(lval * rval, + new AST<>(OperatorDiceNode.MULTIPLY, last, rast)); + })); + + }); + opCollapsers.put(OperatorDiceNode.DIVIDE, (left, right) -> { + return left.merge((lval, last) -> right.merge((rval, rast) -> { + return new Pair<>(lval / rval, + new AST<>(OperatorDiceNode.DIVIDE, last, rast)); + })); + }); + + opCollapsers.put(OperatorDiceNode.ASSIGN, (left, right) -> { + return left.merge((lval, last) -> right.merge((rval, rast) -> { + String nam = last.collapse((nod) -> { + return ((VariableDiceNode) nod).getVariable(); + } , (v) -> (lv, rv) -> null, (r) -> r); + + env.put(nam, new DiceASTExpression(rast, env)); + + return new Pair<>(rval, + new AST<>(OperatorDiceNode.ASSIGN, last, rast)); + })); + }); + + opCollapsers.put(OperatorDiceNode.COMPOUND, (left, right) -> { + return left.merge((lval, last) -> right.merge((rval, rast) -> { + int ival = Integer.parseInt( + Integer.toString(lval) + Integer.toString(rval)); + + return new Pair<>(ival, + new AST<>(OperatorDiceNode.COMPOUND, last, rast)); + })); + }); + opCollapsers.put(OperatorDiceNode.GROUP, (left, right) -> { + return left.merge((lval, last) -> right.merge((rval, rast) -> { + + return new Pair<>(new ComplexDice(lval, rval).roll(), + new AST<>(OperatorDiceNode.GROUP, last, rast)); + })); + }); + + return opCollapsers; + } + + @Override + public int roll() { + Map>>> operations = + buildOperations(env); + + return ast.collapse(this::evalLeaf, operations::get, + (r) -> r.merge((left, right) -> left)); + } + + @Override + public String toString() { + return ast.toString(); + } + + /** + * @return the ast + */ + public AST getAst() { + return ast; + } +} diff --git a/BJC-Utils2/src/main/java/bjc/utils/dice/ast/DiceASTFlattener.java b/BJC-Utils2/src/main/java/bjc/utils/dice/ast/DiceASTFlattener.java index 3cf1d4e..da402c3 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/dice/ast/DiceASTFlattener.java +++ b/BJC-Utils2/src/main/java/bjc/utils/dice/ast/DiceASTFlattener.java @@ -29,7 +29,7 @@ public class DiceASTFlattener { return new ReferenceDiceExpression( ((VariableDiceNode) nod).getVariable(), env); } - } , opCollapsers, (r) -> r); + } , opCollapsers::get, (r) -> r); } private static Map> buildOperations( diff --git a/BJC-Utils2/src/main/java/bjc/utils/dice/ast/DiceASTFreezer.java b/BJC-Utils2/src/main/java/bjc/utils/dice/ast/DiceASTFreezer.java new file mode 100644 index 0000000..04cc99b --- /dev/null +++ b/BJC-Utils2/src/main/java/bjc/utils/dice/ast/DiceASTFreezer.java @@ -0,0 +1,47 @@ +package bjc.utils.dice.ast; + +import java.util.Map; + +import bjc.utils.parserutils.AST; + +public class DiceASTFreezer { + public static AST freezeAST(AST tree, + Map> env) { + return tree.collapse((nod) -> { + if (nod instanceof VariableDiceNode) { + return expandNode((VariableDiceNode) nod, env); + } else { + // Type is specified here so compiler can know the type + // we're using + return new AST(nod); + } + } , (op) -> (left, right) -> { + return new AST(op, left, right); + } , (r) -> r); + } + + public static AST freezeAST(DiceASTExpression tree, + Map env) { + return tree.getAst().collapse((nod) -> { + if (nod instanceof VariableDiceNode) { + return expandNode2((VariableDiceNode) nod, env); + } else { + // Type is specified here so compiler can know the type + // we're using + return new AST(nod); + } + } , (op) -> (left, right) -> { + return new AST(op, left, right); + } , (r) -> r); + } + + private static AST expandNode(VariableDiceNode vnode, + Map> env) { + return env.get(vnode.getVariable()); + } + + private static AST expandNode2(VariableDiceNode vnode, + Map env) { + return env.get(vnode.getVariable()).getAst(); + } +} diff --git a/BJC-Utils2/src/main/java/bjc/utils/dice/ast/LiteralDiceNode.java b/BJC-Utils2/src/main/java/bjc/utils/dice/ast/LiteralDiceNode.java index 5bfbc85..20358fb 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/dice/ast/LiteralDiceNode.java +++ b/BJC-Utils2/src/main/java/bjc/utils/dice/ast/LiteralDiceNode.java @@ -20,4 +20,9 @@ public class LiteralDiceNode implements IDiceASTNode { public String getData() { return data; } + + @Override + public String toString() { + return data; + } } diff --git a/BJC-Utils2/src/main/java/bjc/utils/dice/ast/OperatorDiceNode.java b/BJC-Utils2/src/main/java/bjc/utils/dice/ast/OperatorDiceNode.java index 84f45aa..92b49b7 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/dice/ast/OperatorDiceNode.java +++ b/BJC-Utils2/src/main/java/bjc/utils/dice/ast/OperatorDiceNode.java @@ -1,8 +1,12 @@ package bjc.utils.dice.ast; +// The following classes need to be changed upon addition of a new operator +// 1. DiceASTExpression +// 2. DiceASTFlattener +// 3. DiceASTParser public enum OperatorDiceNode implements IDiceASTNode { ASSIGN, ADD, SUBTRACT, MULTIPLY, DIVIDE, GROUP, COMPOUND; - + @Override public boolean isOperator() { return true; diff --git a/BJC-Utils2/src/main/java/bjc/utils/dice/ast/VariableDiceNode.java b/BJC-Utils2/src/main/java/bjc/utils/dice/ast/VariableDiceNode.java index 1d3a63c..6ae3189 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/dice/ast/VariableDiceNode.java +++ b/BJC-Utils2/src/main/java/bjc/utils/dice/ast/VariableDiceNode.java @@ -20,4 +20,9 @@ public class VariableDiceNode implements IDiceASTNode { public String getVariable() { return var; } + + @Override + public String toString() { + return var; + } } -- cgit v1.2.3