summaryrefslogtreecommitdiff
path: root/dice-lang/src
diff options
context:
space:
mode:
authorbculkin2442 <bjculkin@mix.wvu.edu>2016-03-28 08:44:54 -0400
committerbculkin2442 <bjculkin@mix.wvu.edu>2016-03-28 08:44:54 -0400
commit78d9c539e25f16fd15f06c2b2c48c0ad37a21540 (patch)
tree52d5db559d390c2fb83d4a4cd3bbfa4344c482e8 /dice-lang/src
parent1a020352bdcbb6b5279b703f93e42a1eb7071e36 (diff)
Imported dice stuff from general utils into dedicated project
Diffstat (limited to 'dice-lang/src')
-rw-r--r--dice-lang/src/bjc/utils/dice/BindingDiceExpression.java77
-rw-r--r--dice-lang/src/bjc/utils/dice/ComplexDice.java113
-rw-r--r--dice-lang/src/bjc/utils/dice/CompoundDice.java80
-rw-r--r--dice-lang/src/bjc/utils/dice/CompoundDiceExpression.java83
-rw-r--r--dice-lang/src/bjc/utils/dice/DiceExpressionBuilder.java153
-rw-r--r--dice-lang/src/bjc/utils/dice/DiceExpressionParser.java152
-rw-r--r--dice-lang/src/bjc/utils/dice/DiceExpressionType.java49
-rw-r--r--dice-lang/src/bjc/utils/dice/Die.java51
-rw-r--r--dice-lang/src/bjc/utils/dice/IDiceExpression.java17
-rw-r--r--dice-lang/src/bjc/utils/dice/PolyhedralDice.java86
-rw-r--r--dice-lang/src/bjc/utils/dice/ReferenceDiceExpression.java70
-rw-r--r--dice-lang/src/bjc/utils/dice/ScalarDiceExpression.java75
-rw-r--r--dice-lang/src/bjc/utils/dice/ScalarDie.java45
-rw-r--r--dice-lang/src/bjc/utils/dice/ast/DiceASTExpression.java195
-rw-r--r--dice-lang/src/bjc/utils/dice/ast/DiceASTFlattener.java114
-rw-r--r--dice-lang/src/bjc/utils/dice/ast/DiceASTFreezer.java92
-rw-r--r--dice-lang/src/bjc/utils/dice/ast/DiceASTParser.java108
-rw-r--r--dice-lang/src/bjc/utils/dice/ast/IDiceASTNode.java16
-rw-r--r--dice-lang/src/bjc/utils/dice/ast/LiteralDiceNode.java48
-rw-r--r--dice-lang/src/bjc/utils/dice/ast/OperatorDiceNode.java81
-rw-r--r--dice-lang/src/bjc/utils/dice/ast/VariableDiceNode.java53
21 files changed, 1758 insertions, 0 deletions
diff --git a/dice-lang/src/bjc/utils/dice/BindingDiceExpression.java b/dice-lang/src/bjc/utils/dice/BindingDiceExpression.java
new file mode 100644
index 0000000..9ecce97
--- /dev/null
+++ b/dice-lang/src/bjc/utils/dice/BindingDiceExpression.java
@@ -0,0 +1,77 @@
+package bjc.utils.dice;
+
+import java.util.Map;
+
+/**
+ * A variable expression that represents binding a variable to a name in an
+ * enviroment
+ *
+ * @author ben
+ *
+ */
+public class BindingDiceExpression implements IDiceExpression {
+ /**
+ * The expression being bound to a name
+ */
+ private IDiceExpression exp;
+
+ /**
+ * The name to bind the expression to
+ */
+ private String name;
+
+ /**
+ * Create a new dice expression binder from two expressions and an
+ * enviroment
+ *
+ * @param left
+ * The left side expression to get a name from. Must be a
+ * ReferenceDiceExpression
+ * @param right
+ * The right side to bind to the name
+ * @param env
+ * The enviroment to bind into
+ */
+ public BindingDiceExpression(IDiceExpression left,
+ IDiceExpression right, Map<String, IDiceExpression> env) {
+ this(((ReferenceDiceExpression) left).getName(), right, env);
+ }
+
+ /**
+ * Create a new dice expression binder
+ *
+ * @param name
+ * The name of the variable to bind
+ * @param exp
+ * The expression to bind to the variable
+ * @param env
+ * The enviroment to bind it in
+ */
+ public BindingDiceExpression(String name, IDiceExpression exp,
+ Map<String, IDiceExpression> env) {
+ this.name = name;
+ this.exp = exp;
+
+ env.put(name, exp);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see bjc.utils.dice.IDiceExpression#roll()
+ */
+ @Override
+ public int roll() {
+ return exp.roll();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "assign[n=" + name + ", exp=" + exp.toString() + "]";
+ }
+}
diff --git a/dice-lang/src/bjc/utils/dice/ComplexDice.java b/dice-lang/src/bjc/utils/dice/ComplexDice.java
new file mode 100644
index 0000000..226f9fd
--- /dev/null
+++ b/dice-lang/src/bjc/utils/dice/ComplexDice.java
@@ -0,0 +1,113 @@
+package bjc.utils.dice;
+
+/**
+ * Implements a collection of one or more of a particular die, where the
+ * number of dice in the group is variable.
+ *
+ * @author ben
+ *
+ */
+public class ComplexDice implements IDiceExpression {
+ /**
+ * Create a dice from a string expression
+ *
+ * @param dice
+ * The string to parse the dice from
+ * @return A dice group parsed from the string
+ */
+ public static ComplexDice fromString(String dice) {
+ /*
+ * Split it on the dice type marker
+ */
+ String[] strangs = dice.split("d");
+
+ try {
+ /*
+ * Create the actual dice
+ */
+ return new ComplexDice(
+ new ScalarDie(Integer.parseInt(strangs[0])),
+ new Die(Integer.parseInt(strangs[1])));
+ } catch (NumberFormatException nfex) {
+ /*
+ * Tell the user the expression is invalid
+ */
+ throw new IllegalArgumentException(
+ "Attempted to create a dice using something that's not"
+ + " an integer: " + strangs[0] + " and "
+ + strangs[1] + " are likely culprits.");
+ }
+ }
+
+ /**
+ * The die being rolled
+ */
+ private IDiceExpression die;
+
+ /**
+ * The number of the specified die to roll
+ */
+ private IDiceExpression nDice;
+
+ /**
+ * Create a new collection of dice
+ *
+ * @param nDce
+ * The number of dice in the collection
+ * @param de
+ * The type of dice the collection is composed of
+ */
+ public ComplexDice(IDiceExpression nDce, IDiceExpression de) {
+ nDice = nDce;
+ die = de;
+ }
+
+ /**
+ * Create a new collection of dice
+ *
+ * @param nSides
+ * The number of dice in the collection
+ * @param de
+ * The type of dice the collection is composed of
+ */
+ public ComplexDice(int nSides, int de) {
+ nDice = new ScalarDie(nSides);
+ die = new Die(de);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see bjc.utils.dice.IDiceExpression#roll()
+ */
+ @Override
+ public int roll() {
+ int res = 0;
+
+ /*
+ * Add the results of rolling each die
+ */
+ int nRoll = nDice.roll();
+
+ for (int i = 0; i < nRoll; i++) {
+ res += die.roll();
+ }
+
+ return res;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ if (nDice instanceof ScalarDie && die instanceof Die) {
+ return nDice.toString() + die.toString();
+ } else {
+ return "complex[n=" + nDice.toString() + ", d="
+ + die.toString() + "]";
+ }
+ }
+}
diff --git a/dice-lang/src/bjc/utils/dice/CompoundDice.java b/dice-lang/src/bjc/utils/dice/CompoundDice.java
new file mode 100644
index 0000000..3393711
--- /dev/null
+++ b/dice-lang/src/bjc/utils/dice/CompoundDice.java
@@ -0,0 +1,80 @@
+package bjc.utils.dice;
+
+/**
+ * Implements a "compound dice"
+ *
+ * To explain, a compound dice is something like a d100 composed from two
+ * d10s instead of a hundred sided die.
+ *
+ * @author ben
+ *
+ */
+public class CompoundDice implements IDiceExpression {
+ /**
+ * The left die of the expression
+ */
+ private IDiceExpression l;
+
+ /**
+ * The right die of the expression
+ */
+ private IDiceExpression r;
+
+ /**
+ * Create a new compound dice using the specified dice
+ *
+ * @param l
+ * The die to use on the left
+ * @param r
+ * The die to use on the right
+ */
+ public CompoundDice(IDiceExpression l, IDiceExpression r) {
+ this.l = l;
+ this.r = r;
+ }
+
+ /**
+ * Create a new compound dice from two dice strings
+ *
+ * @param l
+ * The left side dice
+ * @param r
+ * The right side dice
+ */
+ public CompoundDice(String l, String r) {
+ this(ComplexDice.fromString(l), ComplexDice.fromString(r));
+ }
+
+ /**
+ * Create a new compound dice from an array of dice strings
+ *
+ * @param exps
+ * An array of dice strings
+ */
+ public CompoundDice(String[] exps) {
+ this(exps[0], exps[1]);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see bjc.utils.dice.IDiceExpression#roll()
+ */
+ @Override
+ public int roll() {
+ /*
+ * Make the combination of the two dice
+ */
+ return Integer.parseInt(l.roll() + "" + r.roll());
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "compound[l=" + l.toString() + ", r=" + r.toString() + "]";
+ }
+}
diff --git a/dice-lang/src/bjc/utils/dice/CompoundDiceExpression.java b/dice-lang/src/bjc/utils/dice/CompoundDiceExpression.java
new file mode 100644
index 0000000..41b1df2
--- /dev/null
+++ b/dice-lang/src/bjc/utils/dice/CompoundDiceExpression.java
@@ -0,0 +1,83 @@
+package bjc.utils.dice;
+
+/**
+ * Implements a class for combining two dice with an operator
+ *
+ * @author ben
+ *
+ */
+public class CompoundDiceExpression implements IDiceExpression {
+ /**
+ * The operator to use for combining the dice
+ */
+ private DiceExpressionType det;
+
+ /**
+ * The dice on the left side of the expression
+ */
+ private IDiceExpression left;
+
+ /**
+ * The dice on the right side of the expression
+ */
+ private IDiceExpression right;
+
+ /**
+ * Create a new compound expression using the specified parameters
+ *
+ * @param right
+ * The die on the right side of the expression
+ * @param left
+ * The die on the left side of the expression
+ * @param det
+ * The operator to use for combining the dices
+ */
+ public CompoundDiceExpression(IDiceExpression right,
+ IDiceExpression left, DiceExpressionType det) {
+ this.right = right;
+ this.left = left;
+ this.det = det;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see bjc.utils.dice.IDiceExpression#roll()
+ */
+ @Override
+ public int roll() {
+ /*
+ * Handle each operator
+ */
+ switch (det) {
+ case ADD:
+ return right.roll() + left.roll();
+ case SUBTRACT:
+ return right.roll() - left.roll();
+ case MULTIPLY:
+ return right.roll() * left.roll();
+ case DIVIDE:
+ /*
+ * Round to keep results as integers. We don't really have
+ * any need for floating-point dice
+ */
+ return Math.round(right.roll() / left.roll());
+ default:
+ throw new IllegalArgumentException(
+ "Got passed a invalid ScalarExpressionType " + det
+ + ". WAT");
+
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "dice-exp[type=" + det + ", l=" + left.toString() + ", r="
+ + right.toString() + "]";
+ }
+}
diff --git a/dice-lang/src/bjc/utils/dice/DiceExpressionBuilder.java b/dice-lang/src/bjc/utils/dice/DiceExpressionBuilder.java
new file mode 100644
index 0000000..b1de9ad
--- /dev/null
+++ b/dice-lang/src/bjc/utils/dice/DiceExpressionBuilder.java
@@ -0,0 +1,153 @@
+package bjc.utils.dice;
+
+import static bjc.utils.dice.DiceExpressionType.*;
+
+/**
+ * Build a dice expression piece by piece
+ *
+ * @author ben
+ *
+ */
+public class DiceExpressionBuilder {
+ /**
+ * The dice expression we are building
+ */
+ private IDiceExpression baking;
+
+ /**
+ * Build a dice expresssion from a seed dice
+ *
+ * @param firstDice
+ * The dice to use as a seed
+ */
+ public DiceExpressionBuilder(ComplexDice firstDice) {
+ baking = firstDice;
+ }
+
+ /**
+ * Build a dice expression from a seed dice expression
+ *
+ * @param seed
+ * The dice expression to use as a seed
+ */
+ public DiceExpressionBuilder(IDiceExpression seed) {
+ baking = seed;
+ }
+
+ /**
+ * Build a dice expression from a seed dice
+ *
+ * @param nSides
+ * The number of sides in the dice
+ * @param nDice
+ * The number of dice in the group
+ */
+ public DiceExpressionBuilder(int nSides, int nDice) {
+ baking = new ComplexDice(nSides, nDice);
+ }
+
+ /**
+ * Add a term to this dice expression
+ *
+ * @param exp
+ * The expression to use on the left
+ * @return A new expression adding the two dice
+ */
+ public DiceExpressionBuilder add(IDiceExpression exp) {
+ baking = new CompoundDiceExpression(baking, exp, ADD);
+ return this;
+ }
+
+ /**
+ * Add a scalar to this dice
+ *
+ * @param num
+ * The scalar to add to the dice
+ * @return A dice expression adding a scalar to this
+ */
+ public DiceExpressionBuilder add(int num) {
+ baking = new ScalarDiceExpression(baking, num, ADD);
+ return this;
+ }
+
+ /**
+ * Bake the expression being built to completion
+ *
+ * @return A usable dice expression
+ */
+ public IDiceExpression bake() {
+ return baking;
+ }
+
+ /**
+ * Divide a term from dice expression
+ *
+ * @param exp
+ * The expression to use on the left
+ * @return A new expression dividing the two dice
+ */
+ public DiceExpressionBuilder divide(IDiceExpression exp) {
+ baking = new CompoundDiceExpression(baking, exp, DIVIDE);
+ return this;
+ }
+
+ /**
+ * Divide a scalar from this dice
+ *
+ * @param num
+ * The scalar to add to the dice
+ * @return A dice expression dividing a scalar from this
+ */
+ public DiceExpressionBuilder divide(int num) {
+ baking = new ScalarDiceExpression(baking, num, DIVIDE);
+ return this;
+ }
+
+ /**
+ * Multiply a term by this dice expression
+ *
+ * @param exp
+ * The expression to use on the left
+ * @return A new expression multiplying the two dice
+ */
+ public DiceExpressionBuilder multiply(IDiceExpression exp) {
+ baking = new CompoundDiceExpression(baking, exp, MULTIPLY);
+ return this;
+ }
+
+ /**
+ * Multiply a scalar by this dice
+ *
+ * @param num
+ * The scalar to multiply to the dice
+ * @return A dice expression multiplying a scalar to this
+ */
+ public DiceExpressionBuilder multiply(int num) {
+ baking = new ScalarDiceExpression(baking, num, MULTIPLY);
+ return this;
+ }
+
+ /**
+ * Add a term to this dice expression
+ *
+ * @param exp
+ * The expression to use on the left
+ * @return A new expression adding the two dice
+ */
+ public DiceExpressionBuilder subtract(IDiceExpression exp) {
+ baking = new CompoundDiceExpression(baking, exp, SUBTRACT);
+ return this;
+ }
+
+ /**
+ * Add a scalar to this dice
+ *
+ * @param num
+ * The scalar to add to the dice
+ * @return A dice expression adding a scalar to this
+ */
+ public DiceExpressionBuilder subtract(int num) {
+ baking = new ScalarDiceExpression(baking, num, SUBTRACT);
+ return this;
+ }
+}
diff --git a/dice-lang/src/bjc/utils/dice/DiceExpressionParser.java b/dice-lang/src/bjc/utils/dice/DiceExpressionParser.java
new file mode 100644
index 0000000..4113be4
--- /dev/null
+++ b/dice-lang/src/bjc/utils/dice/DiceExpressionParser.java
@@ -0,0 +1,152 @@
+package bjc.utils.dice;
+
+import java.util.Map;
+import java.util.Stack;
+
+import org.apache.commons.lang3.StringUtils;
+
+import bjc.utils.funcdata.FunctionalList;
+import bjc.utils.funcdata.FunctionalStringTokenizer;
+import bjc.utils.parserutils.ShuntingYard;
+
+/**
+ * Parse a dice expression from a string
+ *
+ * @author ben
+ *
+ */
+public class DiceExpressionParser {
+ /**
+ * Parse a dice expression from a string
+ *
+ * @param exp
+ * The string to parse an expression from
+ * @param env
+ * The enviroment to use when parsing expressions
+ * @return The parsed dice expression
+ */
+ public IDiceExpression parse(String exp,
+ Map<String, IDiceExpression> env) {
+ /*
+ * Create a tokenizer over the strings
+ */
+ FunctionalStringTokenizer fst = new FunctionalStringTokenizer(exp);
+
+ /*
+ * Create a shunter to rewrite the expression
+ */
+ ShuntingYard<String> yard = new ShuntingYard<>();
+
+ /*
+ * Add our custom operators to the yard
+ */
+ yard.addOp("d", 5); // dice operator: use for creating variable
+ // size dice groups
+ yard.addOp("c", 6); // compound operator: use for creating compound
+ // dice from expressions
+ yard.addOp(":=", 0); // binding operator: Bind a name to a variable
+ // expression
+
+ /*
+ * Shunt the expression to postfix form
+ */
+ FunctionalList<String> ls =
+ yard.postfix(fst.toList(s -> s), s -> s);
+
+ /*
+ * Create a stack for building an expression from parts
+ */
+ Stack<IDiceExpression> dexps = new Stack<>();
+
+ /*
+ * Create the expression from parts
+ */
+ ls.forEach((tok) -> {
+ /*
+ * Handle compound dice
+ */
+ if (StringUtils.countMatches(tok, 'c') == 1
+ && !tok.equalsIgnoreCase("c")) {
+ String[] strangs = tok.split("c");
+
+ dexps.push(new CompoundDice(strangs));
+ } else if (StringUtils.countMatches(tok, 'd') == 1
+ && !tok.equalsIgnoreCase("d")) {
+ /*
+ * Handle dice groups
+ */
+ dexps.push(ComplexDice.fromString(tok));
+ } else {
+ try {
+ /*
+ * Handle scalar numbers
+ */
+ dexps.push(new ScalarDie(Integer.parseInt(tok)));
+ } catch (NumberFormatException nfex) {
+
+ if (dexps.size() >= 2) {
+ /*
+ * Apply an operation to two dice
+ */
+ IDiceExpression r = dexps.pop();
+ IDiceExpression l = dexps.pop();
+ switch (tok) {
+ case ":=":
+ dexps.push(new BindingDiceExpression(l, r,
+ env));
+ break;
+ case "+":
+ dexps.push(new CompoundDiceExpression(r, l,
+ DiceExpressionType.ADD));
+ break;
+ case "-":
+ dexps.push(new CompoundDiceExpression(r, l,
+ DiceExpressionType.SUBTRACT));
+ break;
+ case "*":
+ dexps.push(new CompoundDiceExpression(r, l,
+ DiceExpressionType.MULTIPLY));
+ break;
+ case "/":
+ dexps.push(new CompoundDiceExpression(r, l,
+ DiceExpressionType.DIVIDE));
+ break;
+ case "c":
+ dexps.push(new CompoundDice(l, r));
+ break;
+ case "d":
+ dexps.push(new ComplexDice(l, r));
+ break;
+ default:
+ /*
+ * Parse it as a variable reference
+ *
+ * Make sure to restore popped variables
+ */
+ dexps.push(l);
+ dexps.push(r);
+
+ dexps.push(new ReferenceDiceExpression(tok,
+ env));
+ }
+ } else {
+ /*
+ * Parse it as a variable reference
+ */
+ dexps.push(new ReferenceDiceExpression(tok, env));
+ }
+ }
+ }
+ });
+
+ if (dexps.size() != 1) {
+ System.err.println(
+ "WARNING: Leftovers found on dice expression stack. Remember, := is assignment.");
+ }
+
+ /*
+ * Return the built expression
+ */
+ return dexps.pop();
+ }
+}
diff --git a/dice-lang/src/bjc/utils/dice/DiceExpressionType.java b/dice-lang/src/bjc/utils/dice/DiceExpressionType.java
new file mode 100644
index 0000000..d719ae8
--- /dev/null
+++ b/dice-lang/src/bjc/utils/dice/DiceExpressionType.java
@@ -0,0 +1,49 @@
+package bjc.utils.dice;
+
+/**
+ * Enumeration for basic dice expression operators
+ */
+public enum DiceExpressionType {
+ /**
+ * Add two expressions
+ */
+ ADD,
+
+ /**
+ * Divide two expressions
+ */
+ DIVIDE,
+
+ /**
+ * Multiply two expressions
+ */
+ MULTIPLY,
+
+ /**
+ * Subtract two expressions
+ */
+ SUBTRACT;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Enum#toString()
+ */
+ @Override
+ public String toString() {
+ switch (this) {
+ case ADD:
+ return "+";
+ case DIVIDE:
+ return "/";
+ case MULTIPLY:
+ return "*";
+ case SUBTRACT:
+ return "-";
+ default:
+ throw new IllegalArgumentException(
+ "Got passed a invalid ScalarExpressionType "
+ + this + ". WAT");
+ }
+ };
+} \ No newline at end of file
diff --git a/dice-lang/src/bjc/utils/dice/Die.java b/dice-lang/src/bjc/utils/dice/Die.java
new file mode 100644
index 0000000..c768aa5
--- /dev/null
+++ b/dice-lang/src/bjc/utils/dice/Die.java
@@ -0,0 +1,51 @@
+package bjc.utils.dice;
+
+import java.util.Random;
+
+/**
+ * A single polyhedral dice
+ *
+ * @author ben
+ *
+ */
+public class Die implements IDiceExpression {
+ /**
+ * Random # gen to use for dice
+ */
+ private static Random rng = new Random();
+
+ /**
+ * Number of sides this die has
+ */
+ private int nSides;
+
+ /**
+ * Create a die with the specified number of sides
+ *
+ * @param nSides
+ * The number of sides this dice has
+ */
+ public Die(int nSides) {
+ this.nSides = nSides;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see bjc.utils.dice.IDiceExpression#roll()
+ */
+ @Override
+ public int roll() {
+ return rng.nextInt(nSides) + 1;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "d" + nSides;
+ }
+}
diff --git a/dice-lang/src/bjc/utils/dice/IDiceExpression.java b/dice-lang/src/bjc/utils/dice/IDiceExpression.java
new file mode 100644
index 0000000..5ead9ad
--- /dev/null
+++ b/dice-lang/src/bjc/utils/dice/IDiceExpression.java
@@ -0,0 +1,17 @@
+package bjc.utils.dice;
+
+/**
+ * An expression for something that can be rolled like a polyhedral die
+ *
+ * @author ben
+ *
+ */
+@FunctionalInterface
+public interface IDiceExpression {
+ /**
+ * Roll the dice once
+ *
+ * @return The result of rowing the dice
+ */
+ public int roll();
+}
diff --git a/dice-lang/src/bjc/utils/dice/PolyhedralDice.java b/dice-lang/src/bjc/utils/dice/PolyhedralDice.java
new file mode 100644
index 0000000..314d47b
--- /dev/null
+++ b/dice-lang/src/bjc/utils/dice/PolyhedralDice.java
@@ -0,0 +1,86 @@
+package bjc.utils.dice;
+
+/**
+ * Utility class that produces common polyhedral dice
+ *
+ * @author ben
+ *
+ */
+public class PolyhedralDice {
+ /**
+ * Produce the specified number of 10-sided dice
+ *
+ * @param nDice
+ * The number of ten-sided dice to produce
+ * @return A group of ten-sided dice of the specified size
+ */
+ public static IDiceExpression d10(int nDice) {
+ return new ComplexDice(nDice, 10);
+ }
+
+ /**
+ * Produce the specified number of 100-sided dice
+ *
+ * @param nDice
+ * The number of hundred-sided dice to produce
+ * @return A group of hundred-sided dice of the specified size
+ */
+ public static IDiceExpression d100(int nDice) {
+ return new ComplexDice(nDice, 100);
+ }
+
+ /**
+ * Produce the specified number of 12-sided dice
+ *
+ * @param nDice
+ * The number of twelve-sided dice to produce
+ * @return A group of twelve-sided dice of the specified size
+ */
+ public static IDiceExpression d12(int nDice) {
+ return new ComplexDice(nDice, 12);
+ }
+
+ /**
+ * Produce the specified number of 20-sided dice
+ *
+ * @param nDice
+ * The number of twenty-sided dice to produce
+ * @return A group of twenty-sided dice of the specified size
+ */
+ public static IDiceExpression d20(int nDice) {
+ return new ComplexDice(nDice, 20);
+ }
+
+ /**
+ * Produce the specified number of 4-sided dice
+ *
+ * @param nDice
+ * The number of four-sided dice to produce
+ * @return A group of four-sided dice of the specified size
+ */
+ public static IDiceExpression d4(int nDice) {
+ return new ComplexDice(nDice, 4);
+ }
+
+ /**
+ * Produce the specified number of 6-sided dice
+ *
+ * @param nDice
+ * The number of six-sided dice to produce
+ * @return A group of six-sided dice of the specified size
+ */
+ public static IDiceExpression d6(int nDice) {
+ return new ComplexDice(nDice, 6);
+ }
+
+ /**
+ * Produce the specified number of 8-sided dice
+ *
+ * @param nDice
+ * The number of eight-sided dice to produce
+ * @return A group of eight-sided dice of the specified size
+ */
+ public static IDiceExpression d8(int nDice) {
+ return new ComplexDice(nDice, 8);
+ }
+}
diff --git a/dice-lang/src/bjc/utils/dice/ReferenceDiceExpression.java b/dice-lang/src/bjc/utils/dice/ReferenceDiceExpression.java
new file mode 100644
index 0000000..d38e0f9
--- /dev/null
+++ b/dice-lang/src/bjc/utils/dice/ReferenceDiceExpression.java
@@ -0,0 +1,70 @@
+package bjc.utils.dice;
+
+import java.util.Map;
+
+/**
+ * A dice expression that refers to a variable bound in a mutable
+ * enviroment
+ *
+ * @author ben
+ *
+ */
+public class ReferenceDiceExpression implements IDiceExpression {
+ /**
+ * The enviroment to do variable dereferencing against
+ */
+ private Map<String, IDiceExpression> env;
+
+ /**
+ * The name of the bound variable
+ */
+ private String name;
+
+ /**
+ * Create a new reference dice expression referring to the given name
+ * in an enviroment
+ *
+ * @param name
+ * The name of the bound variable
+ * @param env
+ * The enviroment to resolve the variable against
+ */
+ public ReferenceDiceExpression(String name,
+ Map<String, IDiceExpression> env) {
+ this.name = name;
+ this.env = env;
+ }
+
+ /**
+ * Get the name of the referenced variable
+ *
+ * @return the name of the referenced variable
+ */
+ public String getName() {
+ return name;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see bjc.utils.dice.IDiceExpression#roll()
+ */
+ @Override
+ public int roll() {
+ return env.get(name).roll();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ if (env.containsKey(name)) {
+ return env.get(name).toString();
+ } else {
+ return name;
+ }
+ }
+}
diff --git a/dice-lang/src/bjc/utils/dice/ScalarDiceExpression.java b/dice-lang/src/bjc/utils/dice/ScalarDiceExpression.java
new file mode 100644
index 0000000..4c3f244
--- /dev/null
+++ b/dice-lang/src/bjc/utils/dice/ScalarDiceExpression.java
@@ -0,0 +1,75 @@
+package bjc.utils.dice;
+
+/**
+ * A dice expression that combines a scalar and a dice
+ *
+ * @author ben
+ *
+ */
+public class ScalarDiceExpression implements IDiceExpression {
+ /**
+ * The operation to combine with
+ */
+ private DiceExpressionType det;
+
+ /**
+ * The expression to be combined
+ */
+ private IDiceExpression exp;
+
+ /**
+ * The scalar to be combined
+ */
+ private int scalar;
+
+ /**
+ * Create a dice expression with a scalar
+ *
+ * @param dex
+ * The dice to use
+ * @param scalr
+ * The scalar to use
+ * @param dt
+ * The operation to combine with
+ */
+ public ScalarDiceExpression(IDiceExpression dex, int scalr,
+ DiceExpressionType dt) {
+ exp = dex;
+ scalar = scalr;
+ det = dt;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see bjc.utils.dice.IDiceExpression#roll()
+ */
+ @Override
+ public int roll() {
+ switch (det) {
+ case ADD:
+ return exp.roll() + scalar;
+ case SUBTRACT:
+ return exp.roll() - scalar;
+ case MULTIPLY:
+ return exp.roll() * scalar;
+ case DIVIDE:
+ return Math.round(exp.roll() / scalar);
+ default:
+ throw new IllegalStateException(
+ "Got passed a invalid ScalarExpressionType "
+ + det);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "scalar-exp[type=" + det + ", l=" + scalar + ", r="
+ + exp.toString() + "]";
+ }
+}
diff --git a/dice-lang/src/bjc/utils/dice/ScalarDie.java b/dice-lang/src/bjc/utils/dice/ScalarDie.java
new file mode 100644
index 0000000..4ed99b9
--- /dev/null
+++ b/dice-lang/src/bjc/utils/dice/ScalarDie.java
@@ -0,0 +1,45 @@
+package bjc.utils.dice;
+
+/**
+ * A die that represents a static number
+ *
+ * @author ben
+ *
+ */
+public class ScalarDie implements IDiceExpression {
+ /**
+ * The represented number
+ */
+ private int num;
+
+ /**
+ * Create a dice with the specified number
+ *
+ * @param num
+ * The number used for the dice
+ */
+ public ScalarDie(int num) {
+ this.num = num;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see bjc.utils.dice.IDiceExpression#roll()
+ */
+ @Override
+ public int roll() {
+ return num;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return Integer.toString(num);
+ }
+
+}
diff --git a/dice-lang/src/bjc/utils/dice/ast/DiceASTExpression.java b/dice-lang/src/bjc/utils/dice/ast/DiceASTExpression.java
new file mode 100644
index 0000000..89adb65
--- /dev/null
+++ b/dice-lang/src/bjc/utils/dice/ast/DiceASTExpression.java
@@ -0,0 +1,195 @@
+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;
+
+/**
+ * An implementation of {@link IDiceExpression} backed by an AST of
+ * {@link IDiceASTNode}s
+ *
+ * @author ben
+ *
+ */
+public class DiceASTExpression implements IDiceExpression {
+
+ /**
+ * Build the map of operations to use when collapsing the AST
+ *
+ * @param env
+ * The enviroment to evaluate bindings and such against
+ * @return The operations to use when collapsing the AST
+ */
+ private static
+ Map<IDiceASTNode, BinaryOperator<Pair<Integer, AST<IDiceASTNode>>>>
+ buildOperations(Map<String, DiceASTExpression> env) {
+ Map<IDiceASTNode, BinaryOperator<Pair<Integer, AST<IDiceASTNode>>>> 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;
+ }
+
+ /**
+ * The AST this expression will evaluate
+ */
+ private AST<IDiceASTNode> ast;
+
+ /**
+ * The enviroment to evaluate bindings and such against
+ */
+ private Map<String, DiceASTExpression> env;
+
+ /**
+ * Create a new dice expression backed by an AST
+ *
+ * @param ast
+ * The AST backing this expression
+ * @param env
+ * The enviroment to evaluate bindings against
+ */
+ public DiceASTExpression(AST<IDiceASTNode> ast,
+ Map<String, DiceASTExpression> env) {
+ this.ast = ast;
+ this.env = env;
+ }
+
+ /**
+ * Expand a leaf AST token into a pair for evaluation
+ *
+ * @param tokn
+ * The token to evaluate
+ * @return A pair consisting of the token's value and the AST it
+ * represents
+ */
+ private Pair<Integer, AST<IDiceASTNode>> 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));
+ }
+ }
+ }
+
+ /**
+ * Get the AST bound to this expression
+ *
+ * @return the ast
+ */
+ public AST<IDiceASTNode> getAst() {
+ return ast;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see bjc.utils.dice.IDiceExpression#roll()
+ */
+ @Override
+ public int roll() {
+ Map<IDiceASTNode, BinaryOperator<Pair<Integer, AST<IDiceASTNode>>>> operations =
+ buildOperations(env);
+
+ return ast.collapse(this::evalLeaf, operations::get,
+ (r) -> r.merge((left, right) -> left));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return ast.toString();
+ }
+}
diff --git a/dice-lang/src/bjc/utils/dice/ast/DiceASTFlattener.java b/dice-lang/src/bjc/utils/dice/ast/DiceASTFlattener.java
new file mode 100644
index 0000000..70465a5
--- /dev/null
+++ b/dice-lang/src/bjc/utils/dice/ast/DiceASTFlattener.java
@@ -0,0 +1,114 @@
+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.dice.BindingDiceExpression;
+import bjc.utils.dice.ComplexDice;
+import bjc.utils.dice.CompoundDice;
+import bjc.utils.dice.CompoundDiceExpression;
+import bjc.utils.dice.DiceExpressionType;
+import bjc.utils.dice.IDiceExpression;
+import bjc.utils.dice.ReferenceDiceExpression;
+import bjc.utils.dice.ScalarDie;
+import bjc.utils.parserutils.AST;
+
+/**
+ * Flatten an {@link AST} of {@link IDiceASTNode} into a
+ * {@link IDiceExpression}
+ *
+ * @author ben
+ *
+ */
+public class DiceASTFlattener {
+ /**
+ * Build the operations to use for tree flattening
+ *
+ * @param env
+ * The enviroment the tree will be flattened against
+ * @return The operations needed for tree flattening
+ */
+ private static Map<IDiceASTNode, BinaryOperator<IDiceExpression>>
+ buildOperations(Map<String, IDiceExpression> env) {
+ Map<IDiceASTNode, BinaryOperator<IDiceExpression>> opCollapsers =
+ new HashMap<>();
+ opCollapsers.put(OperatorDiceNode.ADD, (left, right) -> {
+ return new CompoundDiceExpression(right, left,
+ DiceExpressionType.ADD);
+ });
+ opCollapsers.put(OperatorDiceNode.SUBTRACT, (left, right) -> {
+ return new CompoundDiceExpression(right, left,
+ DiceExpressionType.SUBTRACT);
+ });
+ opCollapsers.put(OperatorDiceNode.MULTIPLY, (left, right) -> {
+ return new CompoundDiceExpression(right, left,
+ DiceExpressionType.MULTIPLY);
+ });
+ opCollapsers.put(OperatorDiceNode.DIVIDE, (left, right) -> {
+ return new CompoundDiceExpression(right, left,
+ DiceExpressionType.DIVIDE);
+ });
+ opCollapsers.put(OperatorDiceNode.ASSIGN, (left, right) -> {
+ return new BindingDiceExpression(left, right, env);
+ });
+ opCollapsers.put(OperatorDiceNode.COMPOUND, (left, right) -> {
+ return new CompoundDice(left, right);
+ });
+ opCollapsers.put(OperatorDiceNode.GROUP, (left, right) -> {
+ return new ComplexDice(left, right);
+ });
+
+ return opCollapsers;
+ }
+
+ /**
+ * Create a dice expression from a literal token
+ *
+ * @param tok
+ * The token to convert to an expression
+ * @return The dice expression represented by the token
+ */
+ private static IDiceExpression expFromLiteral(LiteralDiceNode tok) {
+ String data = tok.getData();
+
+ if (StringUtils.countMatches(data, 'c') == 1
+ && !data.equalsIgnoreCase("c")) {
+ String[] strangs = data.split("c");
+
+ return new CompoundDice(ComplexDice.fromString(strangs[0]),
+ ComplexDice.fromString(strangs[1]));
+ } else if (StringUtils.countMatches(data, 'd') == 1
+ && !data.equalsIgnoreCase("d")) {
+ return ComplexDice.fromString(data);
+ } else {
+ return new ScalarDie(Integer.parseInt(data));
+ }
+ }
+
+ /**
+ * Flatten a AST into a dice expression
+ *
+ * @param ast
+ * The AST to flatten
+ * @param env
+ * The enviroment to flatten against
+ * @return The AST, flattened into a dice expression
+ */
+ public static IDiceExpression flatten(AST<IDiceASTNode> ast,
+ Map<String, IDiceExpression> env) {
+ Map<IDiceASTNode, BinaryOperator<IDiceExpression>> opCollapsers =
+ buildOperations(env);
+
+ return ast.collapse((nod) -> {
+ if (nod instanceof LiteralDiceNode) {
+ return expFromLiteral((LiteralDiceNode) nod);
+ } else {
+ return new ReferenceDiceExpression(
+ ((VariableDiceNode) nod).getVariable(), env);
+ }
+ } , opCollapsers::get, (r) -> r);
+ }
+}
diff --git a/dice-lang/src/bjc/utils/dice/ast/DiceASTFreezer.java b/dice-lang/src/bjc/utils/dice/ast/DiceASTFreezer.java
new file mode 100644
index 0000000..efe37c0
--- /dev/null
+++ b/dice-lang/src/bjc/utils/dice/ast/DiceASTFreezer.java
@@ -0,0 +1,92 @@
+package bjc.utils.dice.ast;
+
+import java.util.Map;
+
+import bjc.utils.parserutils.AST;
+
+/**
+ * Freeze references in a dice AST, replacing variable references with what
+ * the variables refer to
+ *
+ * @author ben
+ *
+ */
+public class DiceASTFreezer {
+ /**
+ * Expand a reference
+ *
+ * @param vnode
+ * The node containing the reference to expand
+ * @param env
+ * The enviroment to expand against
+ * @return The expanded reference
+ */
+ private static AST<IDiceASTNode> expandNode(VariableDiceNode vnode,
+ Map<String, AST<IDiceASTNode>> env) {
+ return env.get(vnode.getVariable());
+ }
+
+ /**
+ * Expand a reference
+ *
+ * @param vnode
+ * The node containing the reference to expand
+ * @param env
+ * The enviroment to expand against
+ * @return The expanded reference
+ */
+ private static AST<IDiceASTNode> expandNode2(VariableDiceNode vnode,
+ Map<String, DiceASTExpression> env) {
+ return env.get(vnode.getVariable()).getAst();
+ }
+
+ /**
+ * Freeze the references in an AST
+ *
+ * @param tree
+ * The tree to freeze references in
+ * @param env
+ * The enviroment to get reference values from
+ * @return The tree with references frozen
+ */
+ @SuppressWarnings("unused")
+ public static AST<IDiceASTNode> freezeAST(AST<IDiceASTNode> tree,
+ Map<String, AST<IDiceASTNode>> 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<IDiceASTNode>(nod);
+ }
+ } , (op) -> (left, right) -> {
+ return new AST<IDiceASTNode>(op, left, right);
+ } , (r) -> r);
+ }
+
+ /**
+ * Freeze the references in an expression backed by an AST
+ *
+ * @param tree
+ * The tree-backed expression to freeze references in
+ * @param env
+ * The enviroment to get reference values from
+ * @return The tree with references frozen
+ */
+ @SuppressWarnings("unused")
+ public static AST<IDiceASTNode> freezeAST(DiceASTExpression tree,
+ Map<String, DiceASTExpression> 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<IDiceASTNode>(nod);
+ }
+ } , (op) -> (left, right) -> {
+ return new AST<IDiceASTNode>(op, left, right);
+ } , (r) -> r);
+ }
+}
diff --git a/dice-lang/src/bjc/utils/dice/ast/DiceASTParser.java b/dice-lang/src/bjc/utils/dice/ast/DiceASTParser.java
new file mode 100644
index 0000000..38c514a
--- /dev/null
+++ b/dice-lang/src/bjc/utils/dice/ast/DiceASTParser.java
@@ -0,0 +1,108 @@
+package bjc.utils.dice.ast;
+
+import org.apache.commons.lang3.StringUtils;
+
+import bjc.utils.funcdata.FunctionalList;
+import bjc.utils.funcdata.FunctionalStringTokenizer;
+import bjc.utils.parserutils.AST;
+import bjc.utils.parserutils.ShuntingYard;
+import bjc.utils.parserutils.TreeConstructor;
+
+/**
+ * Create an AST from a string expression
+ *
+ * @author ben
+ *
+ */
+public class DiceASTParser {
+ /**
+ * The yard to use for shunting expressions
+ */
+ private static ShuntingYard<String> yard;
+
+ static {
+ yard = new ShuntingYard<>();
+
+ yard.addOp("d", 5); // dice operator: use for creating variable
+ // size dice groups
+ yard.addOp("c", 6); // compound operator: use for creating compound
+ // dice from expressions
+ yard.addOp(":=", 0); // binding operator: Bind a name to a variable
+ // expression
+ }
+
+ /**
+ * Build an AST from a string expression
+ *
+ * @param exp
+ * The string to build from
+ * @return An AST built from the passed in string
+ */
+ public AST<IDiceASTNode> buildAST(String exp) {
+ FunctionalList<String> tokens = FunctionalStringTokenizer
+ .fromString(exp).toList((s) -> s);
+
+ FunctionalList<String> shunted = yard.postfix(tokens, (s) -> s);
+
+ AST<String> rawAST = TreeConstructor.constructTree(shunted,
+ this::isOperator, (op) -> false, null);
+
+ AST<IDiceASTNode> bakedAST = rawAST.transmuteAST((tok) -> {
+ if (isOperator(tok)) {
+ return OperatorDiceNode.fromString(tok);
+ } else if (isLiteral(tok)) {
+ return new LiteralDiceNode(tok);
+ } else {
+ return new VariableDiceNode(tok);
+ }
+ });
+
+ return bakedAST;
+ }
+
+ /**
+ * Check if a token represents a literal
+ *
+ * @param tok
+ * The token to check
+ * @return Whether or not the token represents a literal
+ */
+ private static boolean isLiteral(String tok) {
+ if (StringUtils.countMatches(tok, 'c') == 1
+ && !tok.equalsIgnoreCase("c")) {
+ return true;
+ } else if (StringUtils.countMatches(tok, 'd') == 1
+ && !tok.equalsIgnoreCase("d")) {
+ return true;
+ } else {
+ try {
+ Integer.parseInt(tok);
+ return true;
+ } catch (NumberFormatException nfx) {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Check if a token represents an operator
+ *
+ * @param tok
+ * The token to check if it represents an operator
+ * @return Whether or not the token represents an operator
+ */
+ private boolean isOperator(String tok) {
+ switch (tok) {
+ case ":=":
+ case "+":
+ case "-":
+ case "*":
+ case "/":
+ case "c":
+ case "d":
+ return true;
+ default:
+ return false;
+ }
+ }
+}
diff --git a/dice-lang/src/bjc/utils/dice/ast/IDiceASTNode.java b/dice-lang/src/bjc/utils/dice/ast/IDiceASTNode.java
new file mode 100644
index 0000000..073da89
--- /dev/null
+++ b/dice-lang/src/bjc/utils/dice/ast/IDiceASTNode.java
@@ -0,0 +1,16 @@
+package bjc.utils.dice.ast;
+
+/**
+ * The interface for a node in a dice AST
+ *
+ * @author ben
+ *
+ */
+public interface IDiceASTNode {
+ /**
+ * Check if this node represents an operator or not
+ *
+ * @return Whether or not this node represents an operator
+ */
+ public boolean isOperator();
+}
diff --git a/dice-lang/src/bjc/utils/dice/ast/LiteralDiceNode.java b/dice-lang/src/bjc/utils/dice/ast/LiteralDiceNode.java
new file mode 100644
index 0000000..b0c1400
--- /dev/null
+++ b/dice-lang/src/bjc/utils/dice/ast/LiteralDiceNode.java
@@ -0,0 +1,48 @@
+package bjc.utils.dice.ast;
+
+/**
+ * A AST node that represents a literal value
+ *
+ * @author ben
+ *
+ */
+public class LiteralDiceNode implements IDiceASTNode {
+ /**
+ * The value contained by this node
+ */
+ private String data;
+
+ /**
+ * Create a new node with the given value
+ *
+ * @param data
+ * The value to be in this node
+ */
+ public LiteralDiceNode(String data) {
+ this.data = data;
+ }
+
+ @Override
+ public boolean isOperator() {
+ return false;
+ }
+
+ /**
+ * Get the data stored in this AST node
+ *
+ * @return the data stored in this AST node
+ */
+ public String getData() {
+ return data;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return data;
+ }
+}
diff --git a/dice-lang/src/bjc/utils/dice/ast/OperatorDiceNode.java b/dice-lang/src/bjc/utils/dice/ast/OperatorDiceNode.java
new file mode 100644
index 0000000..c4f7763
--- /dev/null
+++ b/dice-lang/src/bjc/utils/dice/ast/OperatorDiceNode.java
@@ -0,0 +1,81 @@
+package bjc.utils.dice.ast;
+
+// The following classes need to be changed upon addition of a new operator
+// 1. DiceASTExpression
+// 2. DiceASTFlattener
+// 3. DiceASTParser
+/**
+ * A node that represents an operator
+ *
+ * @author ben
+ *
+ */
+public enum OperatorDiceNode implements IDiceASTNode {
+ /**
+ * Represents adding two nodes
+ */
+ ADD,
+ /**
+ * Represents assigning one node to another
+ */
+ ASSIGN,
+ /**
+ * Representings combining two node values together
+ */
+ COMPOUND,
+ /**
+ * Represents dividing two nodes
+ */
+ DIVIDE,
+ /**
+ * Represents using one node a variable number of times
+ */
+ GROUP,
+ /**
+ * Represents multiplying two nodes
+ */
+ MULTIPLY,
+ /**
+ * Represents subtracting two nodes
+ */
+ SUBTRACT;
+
+ /**
+ * Create a operator node from a string
+ *
+ * @param s
+ * The string to convert to a node
+ * @return The operator corresponding to the node
+ */
+ public static OperatorDiceNode fromString(String s) {
+ switch (s) {
+ case ":=":
+ return ASSIGN;
+ case "+":
+ return ADD;
+ case "-":
+ return SUBTRACT;
+ case "*":
+ return MULTIPLY;
+ case "/":
+ return DIVIDE;
+ case "d":
+ return GROUP;
+ case "c":
+ return COMPOUND;
+ default:
+ throw new IllegalArgumentException(
+ s + " is not a valid operator node");
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see bjc.utils.dice.ast.IDiceASTNode#isOperator()
+ */
+ @Override
+ public boolean isOperator() {
+ return true;
+ }
+}
diff --git a/dice-lang/src/bjc/utils/dice/ast/VariableDiceNode.java b/dice-lang/src/bjc/utils/dice/ast/VariableDiceNode.java
new file mode 100644
index 0000000..43a09b2
--- /dev/null
+++ b/dice-lang/src/bjc/utils/dice/ast/VariableDiceNode.java
@@ -0,0 +1,53 @@
+package bjc.utils.dice.ast;
+
+/**
+ * A node that represents a variable reference
+ *
+ * @author ben
+ *
+ */
+public class VariableDiceNode implements IDiceASTNode {
+ /**
+ * The variable referenced by this node
+ */
+ private String var;
+
+ /**
+ * Create a new node representing the specified variable
+ *
+ * @param data
+ * The name of the variable being referenced
+ */
+ public VariableDiceNode(String data) {
+ this.var = data;
+ }
+
+ /**
+ * Get the variable referenced by this AST node
+ *
+ * @return the variable referenced by this AST node
+ */
+ public String getVariable() {
+ return var;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see bjc.utils.dice.ast.IDiceASTNode#isOperator()
+ */
+ @Override
+ public boolean isOperator() {
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return var;
+ }
+}