summaryrefslogtreecommitdiff
path: root/dice-lang/src/main/java/bjc/dicelang/old
diff options
context:
space:
mode:
authorbculkin2442 <bjculkin@mix.wvu.edu>2016-04-08 13:29:48 -0400
committerbculkin2442 <bjculkin@mix.wvu.edu>2016-04-08 13:29:48 -0400
commit90d1cc6c9f47f1b6f74fb57e07865795a46c23b8 (patch)
treeb74cd5b9989c9f5a1bbde1e1b8c751faf9cb7086 /dice-lang/src/main/java/bjc/dicelang/old
parentb1df3ff8c890bf6d4cc16fb4f28ddb7833512d71 (diff)
Change to data interfaces, as well as prepare to rewrite parser
Diffstat (limited to 'dice-lang/src/main/java/bjc/dicelang/old')
-rw-r--r--dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTDefinedChecker.java61
-rw-r--r--dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTExpression.java344
-rw-r--r--dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTFlattener.java145
-rw-r--r--dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTInliner.java138
-rw-r--r--dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTParser.java146
-rw-r--r--dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTReferenceChecker.java60
-rw-r--r--dice-lang/src/main/java/bjc/dicelang/old/ast/IOperatorCollapser.java18
7 files changed, 912 insertions, 0 deletions
diff --git a/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTDefinedChecker.java b/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTDefinedChecker.java
new file mode 100644
index 0000000..4a20a82
--- /dev/null
+++ b/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTDefinedChecker.java
@@ -0,0 +1,61 @@
+package bjc.dicelang.old.ast;
+
+import java.util.Map;
+import java.util.function.Consumer;
+
+import bjc.dicelang.ast.nodes.DiceASTType;
+import bjc.dicelang.ast.nodes.IDiceASTNode;
+import bjc.dicelang.ast.nodes.VariableDiceNode;
+import bjc.utils.data.IHolder;
+
+/**
+ * Check if the specified node references a particular variable
+ *
+ * @author ben
+ *
+ */
+public final class DiceASTDefinedChecker
+ implements Consumer<IDiceASTNode> {
+ /**
+ * This is true if the specified node references the set variable
+ */
+ private IHolder<Boolean> referencesVariable;
+
+ private Map<String, DiceASTExpression> enviroment;
+
+ /**
+ * Create a new reference checker
+ *
+ * @param referencesVar
+ * The holder of whether the variable is referenced or not
+ * @param env
+ * The enviroment to check undefinedness against
+ */
+ public DiceASTDefinedChecker(IHolder<Boolean> referencesVar,
+ Map<String, DiceASTExpression> env) {
+ this.referencesVariable = referencesVar;
+ this.enviroment = env;
+ }
+
+ @Override
+ public void accept(IDiceASTNode astNode) {
+ referencesVariable.transform((bool) -> checkUndefined(astNode));
+ }
+
+ /**
+ * Check if a given AST node references an undefined variable
+ *
+ * @param astNode
+ * The node to check
+ * @return Whether or not the node directly the variable
+ */
+ private boolean checkUndefined(IDiceASTNode astNode) {
+ if (astNode.getType() == DiceASTType.VARIABLE) {
+ VariableDiceNode node = (VariableDiceNode) astNode;
+
+ return !enviroment.containsKey(node.getVariable());
+ } else {
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTExpression.java b/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTExpression.java
new file mode 100644
index 0000000..7651093
--- /dev/null
+++ b/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTExpression.java
@@ -0,0 +1,344 @@
+package bjc.dicelang.old.ast;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BinaryOperator;
+import java.util.function.Function;
+
+import bjc.dicelang.ComplexDice;
+import bjc.dicelang.IDiceExpression;
+import bjc.dicelang.ast.nodes.DiceASTType;
+import bjc.dicelang.ast.nodes.IDiceASTNode;
+import bjc.dicelang.ast.nodes.LiteralDiceNode;
+import bjc.dicelang.ast.nodes.OperatorDiceNode;
+import bjc.dicelang.ast.nodes.VariableDiceNode;
+import bjc.utils.data.GenHolder;
+import bjc.utils.data.Pair;
+import bjc.utils.funcdata.bst.ITreePart.TreeLinearizationMethod;
+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 {
+ private static final class ArithmeticCollapser
+ implements IOperatorCollapser {
+ private OperatorDiceNode type;
+
+ private BinaryOperator<Integer> valueOp;
+
+ public ArithmeticCollapser(OperatorDiceNode type,
+ BinaryOperator<Integer> valueOp) {
+ this.type = type;
+ this.valueOp = valueOp;
+ }
+
+ @Override
+ public Pair<Integer, AST<IDiceASTNode>> apply(
+ Pair<Integer, AST<IDiceASTNode>> leftNode,
+ Pair<Integer, AST<IDiceASTNode>> rightNode) {
+ return leftNode.merge((leftValue, leftAST) -> {
+ return rightNode.merge((rightValue, rightAST) -> {
+ if (type == OperatorDiceNode.DIVIDE
+ && rightValue == 0) {
+ throw new ArithmeticException(
+ "Attempted to divide by zero. The AST of the problem expression is "
+ + rightAST);
+ }
+
+ return new Pair<>(valueOp.apply(leftValue, rightValue),
+ new AST<>(type, leftAST, rightAST));
+ });
+ });
+ }
+ }
+
+ private static final class VariableRetriever
+ implements Function<IDiceASTNode, String> {
+ @Override
+ public String apply(IDiceASTNode node) {
+ if (node.getType() != DiceASTType.VARIABLE) {
+ throw new UnsupportedOperationException(
+ "Attempted to assign to something that isn't a variable."
+ + " This isn't supported yet. The problem node is "
+ + node);
+ }
+
+ return ((VariableDiceNode) node).getVariable();
+ }
+ }
+
+ /**
+ * 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 Map<IDiceASTNode, IOperatorCollapser>
+ buildOperations(Map<String, DiceASTExpression> enviroment) {
+ Map<IDiceASTNode, IOperatorCollapser> operatorCollapsers =
+ new HashMap<>();
+
+ operatorCollapsers.put(OperatorDiceNode.ADD,
+ new ArithmeticCollapser(OperatorDiceNode.ADD,
+ (left, right) -> left + right));
+
+ operatorCollapsers.put(OperatorDiceNode.SUBTRACT,
+ new ArithmeticCollapser(OperatorDiceNode.SUBTRACT,
+ (left, right) -> left - right));
+
+ operatorCollapsers.put(OperatorDiceNode.MULTIPLY,
+ new ArithmeticCollapser(OperatorDiceNode.MULTIPLY,
+ (left, right) -> left * right));
+
+ operatorCollapsers.put(OperatorDiceNode.DIVIDE,
+ new ArithmeticCollapser(OperatorDiceNode.DIVIDE,
+ (left, right) -> left / right));
+
+ operatorCollapsers.put(OperatorDiceNode.ASSIGN, (left, right) -> {
+ return parseBinding(enviroment, left, right);
+ });
+
+ operatorCollapsers.put(OperatorDiceNode.COMPOUND,
+ DiceASTExpression::parseCompound);
+
+ operatorCollapsers.put(OperatorDiceNode.GROUP,
+ DiceASTExpression::parseGroup);
+
+ operatorCollapsers.put(OperatorDiceNode.LET, (left, right) -> {
+ return doLet(enviroment, left, right);
+ });
+
+ return operatorCollapsers;
+ }
+
+ private static Pair<Integer, AST<IDiceASTNode>> doLet(
+ Map<String, DiceASTExpression> enviroment,
+ Pair<Integer, AST<IDiceASTNode>> left,
+ Pair<Integer, AST<IDiceASTNode>> right) {
+ return left.merge((leftValue, leftAST) -> {
+ return right.merge((rightValue, rightAST) -> {
+ if (!leftAST
+ .applyToHead(DiceASTExpression::isAssignNode)) {
+ // Just ignore the left block then
+ return new Pair<>(rightValue, rightAST);
+ } else {
+ // Left block has an assignment to handle
+ String varName = leftAST.applyToLeft((leftBranch) -> {
+ return getAssignmentVar(leftBranch);
+ });
+
+ return null;
+ }
+ });
+ });
+ }
+
+ private static String getAssignmentVar(AST<IDiceASTNode> leftBranch) {
+ return leftBranch.applyToHead((node) -> {
+ return ((VariableDiceNode) node).getVariable();
+ });
+ }
+
+ private static Boolean isAssignNode(IDiceASTNode node) {
+ return node.getType() == DiceASTType.OPERATOR
+ && node == OperatorDiceNode.ASSIGN;
+ }
+
+ private static Pair<Integer, AST<IDiceASTNode>> parseBinding(
+ Map<String, DiceASTExpression> enviroment,
+ Pair<Integer, AST<IDiceASTNode>> left,
+ Pair<Integer, AST<IDiceASTNode>> right) {
+ return left.merge((leftValue, leftAST) -> {
+ return right.merge((rightValue, rightAST) -> {
+ String variableName = leftAST
+ .collapse(new VariableRetriever(), (operator) -> {
+ throw new UnsupportedOperationException(
+ "Can only assign to plain variable names. The problem operator is "
+ + operator);
+ }, (returnedAST) -> returnedAST);
+
+ GenHolder<Boolean> selfReference = new GenHolder<>(false);
+
+ DiceASTReferenceChecker refChecker =
+ new DiceASTReferenceChecker(selfReference,
+ variableName);
+
+ rightAST.traverse(TreeLinearizationMethod.PREORDER,
+ refChecker);
+
+ // Ignore meta-variable that'll be auto-frozen to restore
+ // definition sanity
+ if (selfReference.unwrap((bool) -> bool)
+ && !variableName.equals("last")) {
+ throw new UnsupportedOperationException(
+ "Variable '" + variableName
+ + "' references itself. Problematic definition: \n\t"
+ + rightAST);
+ }
+
+ if (!variableName.equals("last")) {
+ enviroment.put(variableName,
+ new DiceASTExpression(rightAST, enviroment));
+ } else {
+ // Do nothing, last is a auto-handled meta-variable
+ }
+
+ return new Pair<>(rightValue, new AST<>(
+ OperatorDiceNode.ASSIGN, leftAST, rightAST));
+ });
+ });
+ }
+
+ private static Pair<Integer, AST<IDiceASTNode>> parseCompound(
+ Pair<Integer, AST<IDiceASTNode>> leftNode,
+ Pair<Integer, AST<IDiceASTNode>> rightNode) {
+ return leftNode.merge((leftValue, leftAST) -> {
+ return rightNode.merge((rightValue, rightAST) -> {
+ int compoundValue =
+ Integer.parseInt(Integer.toString(leftValue)
+ + Integer.toString(rightValue));
+
+ return new Pair<>(compoundValue, new AST<>(
+ OperatorDiceNode.COMPOUND, leftAST, rightAST));
+ });
+ });
+ }
+
+ private static Pair<Integer, AST<IDiceASTNode>> parseGroup(
+ Pair<Integer, AST<IDiceASTNode>> leftNode,
+ Pair<Integer, AST<IDiceASTNode>> rightNode) {
+ return leftNode.merge((leftValue, leftAST) -> {
+ return rightNode.merge((rightValue, rightAST) -> {
+ if (leftValue < 0) {
+ throw new UnsupportedOperationException(
+ "Can't attempt to roll a negative number of dice."
+ + " The problematic AST is "
+ + leftAST);
+ } else if (rightValue < 1) {
+ throw new UnsupportedOperationException(
+ "Can't roll dice with less than one side."
+ + " The problematic AST is "
+ + rightAST);
+ }
+
+ int rolledValue =
+ new ComplexDice(leftValue, rightValue).roll();
+
+ return new Pair<>(rolledValue, new AST<>(
+ OperatorDiceNode.GROUP, leftAST, rightAST));
+ });
+ });
+ }
+
+ /**
+ * 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 leafNode
+ * The token to evaluate
+ * @return A pair consisting of the token's value and the AST it
+ * represents
+ */
+ private Pair<Integer, AST<IDiceASTNode>>
+ evaluateLeaf(IDiceASTNode leafNode) {
+ if (leafNode.getType() == DiceASTType.VARIABLE) {
+ VariableDiceNode node = (VariableDiceNode) leafNode;
+
+ return parseVariable(node);
+ } else if (leafNode.getType() == DiceASTType.LITERAL) {
+ LiteralDiceNode node = (LiteralDiceNode) leafNode;
+
+ return node.toParseValue();
+ } else {
+ throw new UnsupportedOperationException("Found leaf operator "
+ + leafNode + ". These aren't supported.");
+ }
+ }
+
+ private Pair<Integer, AST<IDiceASTNode>>
+ parseVariable(VariableDiceNode leafNode) {
+ String varName = leafNode.getVariable();
+
+ if (env.containsKey(varName)) {
+ return new Pair<>(env.get(varName).roll(),
+ new AST<>(leafNode));
+ } else {
+ // Handle special case for defining variables
+ return new Pair<>(0, new AST<>(leafNode));
+ }
+ }
+
+ /**
+ * 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, IOperatorCollapser> operations =
+ buildOperations(env);
+
+ return ast.collapse(this::evaluateLeaf, operations::get,
+ (returnedValue) -> returnedValue
+ .merge((left, right) -> left));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return ast.toString();
+ }
+
+ @Override
+ public int optimize() {
+ throw new UnsupportedOperationException(
+ "Use DiceASTOptimizer for optimizing these");
+ }
+
+ @Override
+ public boolean canOptimize() {
+ return false;
+ }
+} \ No newline at end of file
diff --git a/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTFlattener.java b/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTFlattener.java
new file mode 100644
index 0000000..a7500a8
--- /dev/null
+++ b/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTFlattener.java
@@ -0,0 +1,145 @@
+package bjc.dicelang.old.ast;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BinaryOperator;
+import java.util.function.Function;
+
+import org.apache.commons.lang3.StringUtils;
+
+import bjc.dicelang.BindingDiceExpression;
+import bjc.dicelang.ComplexDice;
+import bjc.dicelang.CompoundDice;
+import bjc.dicelang.OperatorDiceExpression;
+import bjc.dicelang.DiceExpressionType;
+import bjc.dicelang.IDiceExpression;
+import bjc.dicelang.ReferenceDiceExpression;
+import bjc.dicelang.ScalarDie;
+import bjc.dicelang.ast.nodes.DiceASTType;
+import bjc.dicelang.ast.nodes.IDiceASTNode;
+import bjc.dicelang.ast.nodes.LiteralDiceNode;
+import bjc.dicelang.ast.nodes.OperatorDiceNode;
+import bjc.dicelang.ast.nodes.VariableDiceNode;
+import bjc.utils.parserutils.AST;
+
+/**
+ * Flatten an {@link AST} of {@link IDiceASTNode} into a
+ * {@link IDiceExpression}
+ *
+ * @author ben
+ *
+ */
+public class DiceASTFlattener {
+ private static final class NodeCollapser
+ implements Function<IDiceASTNode, IDiceExpression> {
+ private Map<String, IDiceExpression> enviroment;
+
+ public NodeCollapser(Map<String, IDiceExpression> env) {
+ this.enviroment = env;
+ }
+
+ @Override
+ public IDiceExpression apply(IDiceASTNode nod) {
+ if (nod.getType() == DiceASTType.LITERAL) {
+ return expFromLiteral((LiteralDiceNode) nod);
+ } else if (nod.getType() == DiceASTType.VARIABLE) {
+ String varName = ((VariableDiceNode) nod).getVariable();
+
+ return new ReferenceDiceExpression(varName, enviroment);
+ } else {
+ throw new UnsupportedOperationException(
+ "Attempted to flatten something that can't be"
+ + " flattened. The culprit is " + nod);
+ }
+ }
+
+ /**
+ * 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 (data.equals("")) {
+ throw new UnsupportedOperationException(
+ "Can't convert a blank token into a literal");
+ }
+
+ 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));
+ }
+ }
+ }
+
+ /**
+ * 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 OperatorDiceExpression(right, left,
+ DiceExpressionType.ADD);
+ });
+ opCollapsers.put(OperatorDiceNode.SUBTRACT, (left, right) -> {
+ return new OperatorDiceExpression(right, left,
+ DiceExpressionType.SUBTRACT);
+ });
+ opCollapsers.put(OperatorDiceNode.MULTIPLY, (left, right) -> {
+ return new OperatorDiceExpression(right, left,
+ DiceExpressionType.MULTIPLY);
+ });
+ opCollapsers.put(OperatorDiceNode.DIVIDE, (left, right) -> {
+ return new OperatorDiceExpression(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;
+ }
+
+ /**
+ * 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(new NodeCollapser(env), opCollapsers::get,
+ (r) -> r);
+ }
+}
diff --git a/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTInliner.java b/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTInliner.java
new file mode 100644
index 0000000..3a8e796
--- /dev/null
+++ b/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTInliner.java
@@ -0,0 +1,138 @@
+package bjc.dicelang.old.ast;
+
+import java.util.function.Function;
+
+import bjc.dicelang.ast.nodes.DiceASTType;
+import bjc.dicelang.ast.nodes.IDiceASTNode;
+import bjc.dicelang.ast.nodes.VariableDiceNode;
+import bjc.utils.funcdata.FunctionalList;
+import bjc.utils.funcdata.FunctionalMap;
+import bjc.utils.funcdata.IFunctionalList;
+import bjc.utils.funcdata.IFunctionalMap;
+import bjc.utils.parserutils.AST;
+
+/**
+ * Inline references in a dice AST, replacing variable references with what
+ * the variables refer to
+ *
+ * @author ben
+ *
+ */
+public class DiceASTInliner {
+ private static class NodeInliner
+ implements Function<IDiceASTNode, AST<IDiceASTNode>> {
+ private IFunctionalMap<String, AST<IDiceASTNode>> enviroment;
+
+ public NodeInliner(IFunctionalMap<String, AST<IDiceASTNode>> env) {
+ enviroment = env;
+ }
+
+ @Override
+ public AST<IDiceASTNode> apply(IDiceASTNode nod) {
+ if (nod.getType() == DiceASTType.VARIABLE) {
+ return expandNode((VariableDiceNode) nod);
+ }
+
+ return new AST<>(nod);
+ }
+
+ protected AST<IDiceASTNode> expandNode(
+ VariableDiceNode variableNode) {
+ String varName = variableNode.getVariable();
+
+ if (!enviroment.containsKey(varName)) {
+ throw new IllegalArgumentException(
+ "Attempted to freeze reference"
+ + " to an undefined variable " + varName);
+ }
+
+ return enviroment.get(varName);
+ }
+ }
+
+ private static final class SelectiveInliner extends NodeInliner {
+
+ private IFunctionalList<String> variableNames;
+
+ public SelectiveInliner(
+ IFunctionalMap<String, AST<IDiceASTNode>> env,
+ IFunctionalList<String> varNames) {
+ super(env);
+
+ variableNames = varNames;
+ }
+
+ @Override
+ protected AST<IDiceASTNode> expandNode(
+ VariableDiceNode variableNode) {
+ if (variableNames.contains(variableNode.getVariable())) {
+ return super.expandNode(variableNode);
+ }
+
+ return new AST<>(variableNode);
+ }
+ }
+
+ /**
+ * Inline the references in an AST
+ *
+ * @param tree
+ * The tree to inline references in
+ * @param env
+ * The enviroment to get reference values from
+ * @return The tree with references inlined
+ */
+ public static AST<IDiceASTNode> inlineAST(AST<IDiceASTNode> tree,
+ IFunctionalMap<String, AST<IDiceASTNode>> env) {
+ return selectiveInline(tree, env);
+ }
+
+ /**
+ * Inline the references in an expression backed by an AST
+ *
+ * @param tree
+ * The tree-backed expression to inline references in
+ * @param env
+ * The enviroment to get reference values from
+ * @return The tree with references inlined
+ */
+ public static AST<IDiceASTNode> inlineAST(DiceASTExpression tree,
+ FunctionalMap<String, DiceASTExpression> env) {
+ return inlineAST(tree.getAst(),
+ env.mapValues(expression -> expression.getAst()));
+ }
+
+ /**
+ * Inline references to specified variables
+ *
+ * @param tree
+ * The tree-backed expression to inline references in
+ * @param env
+ * The enviroment to resolve variables against
+ * @param varNames
+ * The names of the variables to inline
+ * @return An AST with the specified variables inlined
+ */
+ public static AST<IDiceASTNode> selectiveInline(AST<IDiceASTNode> tree,
+ IFunctionalMap<String, AST<IDiceASTNode>> env,
+ String... varNames) {
+ return selectiveInline(tree, env, new FunctionalList<>(varNames));
+ }
+
+ /**
+ * Inline references to specified variables
+ *
+ * @param tree
+ * The tree-backed expression to inline references in
+ * @param env
+ * The enviroment to resolve variables against
+ * @param varNames
+ * The names of the variables to inline
+ * @return An AST with the specified variables inline
+ */
+ public static AST<IDiceASTNode> selectiveInline(AST<IDiceASTNode> tree,
+ IFunctionalMap<String, AST<IDiceASTNode>> env,
+ IFunctionalList<String> varNames) {
+ return tree.flatMapTree(new SelectiveInliner(env, varNames));
+ }
+} \ No newline at end of file
diff --git a/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTParser.java b/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTParser.java
new file mode 100644
index 0000000..d8e13f7
--- /dev/null
+++ b/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTParser.java
@@ -0,0 +1,146 @@
+package bjc.dicelang.old.ast;
+
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.function.Function;
+
+import org.apache.commons.lang3.StringUtils;
+
+import bjc.dicelang.ast.nodes.IDiceASTNode;
+import bjc.dicelang.ast.nodes.LiteralDiceNode;
+import bjc.dicelang.ast.nodes.OperatorDiceNode;
+import bjc.dicelang.ast.nodes.VariableDiceNode;
+import bjc.utils.data.IPair;
+import bjc.utils.data.Pair;
+import bjc.utils.funcdata.FunctionalStringTokenizer;
+import bjc.utils.funcdata.IFunctionalList;
+import bjc.utils.funcutils.ListUtils;
+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 {
+ private static final class NodeBaker
+ implements Function<String, IDiceASTNode> {
+ @Override
+ public IDiceASTNode apply(String tok) {
+ if (isOperator(tok)) {
+ return OperatorDiceNode.fromString(tok);
+ } else if (NodeBaker.isLiteral(tok)) {
+ return new LiteralDiceNode(tok);
+ } else {
+ return new VariableDiceNode(tok);
+ }
+ }
+
+ /**
+ * 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 (@SuppressWarnings("unused") NumberFormatException nfex) {
+ // We don't care about details
+ return false;
+ }
+ }
+ }
+ }
+
+ /**
+ * 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 static AST<IDiceASTNode> buildAST(String exp) {
+ IFunctionalList<String> tokens = FunctionalStringTokenizer
+ .fromString(exp).toList();
+
+ Deque<IPair<String, String>> ops = new LinkedList<>();
+
+ ops.add(new Pair<>("+", "\\+"));
+ ops.add(new Pair<>("-", "-"));
+ ops.add(new Pair<>("*", "\\*"));
+ ops.add(new Pair<>("/", "/"));
+ ops.add(new Pair<>(":=", ":="));
+
+ IFunctionalList<String> semiExpandedTokens = ListUtils
+ .splitTokens(tokens, ops);
+
+ ops = new LinkedList<>();
+
+ ops.add(new Pair<>("(", "\\("));
+ ops.add(new Pair<>(")", "\\)"));
+
+ IFunctionalList<String> fullyExpandedTokens = ListUtils
+ .deAffixTokens(semiExpandedTokens, ops);
+
+ IFunctionalList<String> shunted = yard.postfix(fullyExpandedTokens,
+ (s) -> s);
+
+ AST<String> rawAST = TreeConstructor.constructTree(shunted,
+ DiceASTParser::isOperator);
+
+ AST<IDiceASTNode> bakedAST = rawAST.transmuteAST(new NodeBaker());
+
+ return bakedAST;
+ }
+
+ /**
+ * 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 static 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/main/java/bjc/dicelang/old/ast/DiceASTReferenceChecker.java b/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTReferenceChecker.java
new file mode 100644
index 0000000..e526ee9
--- /dev/null
+++ b/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTReferenceChecker.java
@@ -0,0 +1,60 @@
+package bjc.dicelang.old.ast;
+
+import java.util.function.Consumer;
+
+import bjc.dicelang.ast.nodes.DiceASTType;
+import bjc.dicelang.ast.nodes.IDiceASTNode;
+import bjc.dicelang.ast.nodes.VariableDiceNode;
+import bjc.utils.data.IHolder;
+
+/**
+ * Check if the specified node references a particular variable
+ *
+ * @author ben
+ *
+ */
+public final class DiceASTReferenceChecker
+ implements Consumer<IDiceASTNode> {
+ /**
+ * This is true if the specified node references the set variable
+ */
+ private IHolder<Boolean> referencesVariable;
+
+ private String varName;
+
+ /**
+ * Create a new reference checker
+ *
+ * @param referencesVar
+ * The holder of whether the variable is referenced or not
+ * @param varName
+ * The variable to check for references in
+ */
+ public DiceASTReferenceChecker(IHolder<Boolean> referencesVar,
+ String varName) {
+ this.referencesVariable = referencesVar;
+ this.varName = varName;
+ }
+
+ @Override
+ public void accept(IDiceASTNode astNode) {
+ referencesVariable.transform((bool) -> isDirectReference(astNode));
+ }
+
+ /**
+ * Check if a given AST node directly references the specified variable
+ *
+ * @param astNode
+ * The node to check
+ * @return Whether or not the node directly the variable
+ */
+ private boolean isDirectReference(IDiceASTNode astNode) {
+ if (astNode.getType() == DiceASTType.VARIABLE) {
+ VariableDiceNode node = (VariableDiceNode) astNode;
+
+ return node.getVariable().equals(varName);
+ } else {
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/dice-lang/src/main/java/bjc/dicelang/old/ast/IOperatorCollapser.java b/dice-lang/src/main/java/bjc/dicelang/old/ast/IOperatorCollapser.java
new file mode 100644
index 0000000..65cb264
--- /dev/null
+++ b/dice-lang/src/main/java/bjc/dicelang/old/ast/IOperatorCollapser.java
@@ -0,0 +1,18 @@
+package bjc.dicelang.old.ast;
+
+import java.util.function.BinaryOperator;
+
+import bjc.dicelang.ast.nodes.IDiceASTNode;
+import bjc.utils.data.Pair;
+import bjc.utils.parserutils.AST;
+
+/**
+ * Alias for operator collapsers. Because 68-char types are too long
+ *
+ * @author ben
+ *
+ */
+public interface IOperatorCollapser
+ extends BinaryOperator<Pair<Integer, AST<IDiceASTNode>>> {
+ // Just an alias
+}