From e7413128ff4e376997de6e94e4bea5eca14811ef Mon Sep 17 00:00:00 2001 From: bculkin2442 Date: Thu, 27 Oct 2016 21:56:18 -0400 Subject: Moved examples --- dice-lang/src/.DS_Store | Bin 0 -> 6148 bytes .../src/bjc/dicelang/BindingDiceExpression.java | 93 ++++++ dice-lang/src/bjc/dicelang/ComplexDice.java | 147 ++++++++++ dice-lang/src/bjc/dicelang/CompoundDice.java | 100 +++++++ .../src/bjc/dicelang/DiceExpressionBuilder.java | 150 ++++++++++ .../src/bjc/dicelang/DiceExpressionParser.java | 173 +++++++++++ dice-lang/src/bjc/dicelang/DiceExpressionType.java | 49 ++++ dice-lang/src/bjc/dicelang/Die.java | 71 +++++ dice-lang/src/bjc/dicelang/IDiceExpression.java | 91 ++++++ .../src/bjc/dicelang/OperatorDiceExpression.java | 96 ++++++ dice-lang/src/bjc/dicelang/PolyhedralDice.java | 149 ++++++++++ .../src/bjc/dicelang/ReferenceDiceExpression.java | 76 +++++ dice-lang/src/bjc/dicelang/ScalarDie.java | 54 ++++ .../src/bjc/dicelang/ast/ArithmeticCollapser.java | 192 ++++++++++++ dice-lang/src/bjc/dicelang/ast/ArrayResult.java | 43 +++ .../src/bjc/dicelang/ast/DiceASTEvaluator.java | 321 +++++++++++++++++++++ dice-lang/src/bjc/dicelang/ast/DiceASTInliner.java | 125 ++++++++ .../src/bjc/dicelang/ast/DiceASTOptimizer.java | 60 ++++ dice-lang/src/bjc/dicelang/ast/DiceASTParser.java | 160 ++++++++++ .../bjc/dicelang/ast/DiceASTReferenceChecker.java | 61 ++++ .../dicelang/ast/DiceASTReferenceSanitizer.java | 201 +++++++++++++ dice-lang/src/bjc/dicelang/ast/DiceASTUtils.java | 86 ++++++ dice-lang/src/bjc/dicelang/ast/DummyResult.java | 31 ++ .../src/bjc/dicelang/ast/IOperatorCollapser.java | 20 ++ dice-lang/src/bjc/dicelang/ast/IResult.java | 16 + dice-lang/src/bjc/dicelang/ast/IntegerResult.java | 40 +++ dice-lang/src/bjc/dicelang/ast/ResultType.java | 22 ++ .../src/bjc/dicelang/ast/nodes/DiceASTType.java | 27 ++ .../bjc/dicelang/ast/nodes/DiceLiteralNode.java | 52 ++++ .../bjc/dicelang/ast/nodes/DiceLiteralType.java | 18 ++ .../bjc/dicelang/ast/nodes/DiceOperatorType.java | 29 ++ .../src/bjc/dicelang/ast/nodes/IDiceASTNode.java | 23 ++ .../bjc/dicelang/ast/nodes/ILiteralDiceNode.java | 73 +++++ .../bjc/dicelang/ast/nodes/IntegerLiteralNode.java | 50 ++++ .../bjc/dicelang/ast/nodes/OperatorDiceNode.java | 110 +++++++ .../bjc/dicelang/ast/nodes/VariableDiceNode.java | 101 +++++++ .../src/bjc/dicelang/ast/nodes/package-info.java | 7 + .../ast/optimization/ArithmeticCollapser.java | 50 ++++ .../ast/optimization/ConstantCollapser.java | 91 ++++++ .../ast/optimization/IOptimizationPass.java | 35 +++ .../ast/optimization/OperationCondenser.java | 107 +++++++ .../dicelang/ast/optimization/package-info.java | 7 + dice-lang/src/bjc/dicelang/ast/package-info.java | 7 + .../bjc/dicelang/examples/DiceASTLanguageTest.java | 261 +++++++++++++++++ .../src/bjc/dicelang/examples/DiceASTPragma.java | 22 ++ .../examples/DiceExpressionParserTest.java | 59 ++++ .../dicelang/examples/DiceExpressionPreparer.java | 93 ++++++ .../bjc/dicelang/examples/DiceLanguageState.java | 37 +++ .../bjc/dicelang/examples/DiceLanguageTest.java | 96 ++++++ .../bjc/dicelang/examples/DiceASTLanguageTest.java | 261 ----------------- .../java/bjc/dicelang/examples/DiceASTPragma.java | 22 -- .../examples/DiceExpressionParserTest.java | 59 ---- .../dicelang/examples/DiceExpressionPreparer.java | 93 ------ .../bjc/dicelang/examples/DiceLanguageState.java | 37 --- .../bjc/dicelang/examples/DiceLanguageTest.java | 96 ------ .../java/bjc/dicelang/BindingDiceExpression.java | 93 ------ .../src/main/java/bjc/dicelang/ComplexDice.java | 147 ---------- .../src/main/java/bjc/dicelang/CompoundDice.java | 100 ------- .../java/bjc/dicelang/DiceExpressionBuilder.java | 150 ---------- .../java/bjc/dicelang/DiceExpressionParser.java | 173 ----------- .../main/java/bjc/dicelang/DiceExpressionType.java | 49 ---- dice-lang/src/main/java/bjc/dicelang/Die.java | 71 ----- .../main/java/bjc/dicelang/IDiceExpression.java | 91 ------ .../java/bjc/dicelang/OperatorDiceExpression.java | 96 ------ .../src/main/java/bjc/dicelang/PolyhedralDice.java | 149 ---------- .../java/bjc/dicelang/ReferenceDiceExpression.java | 76 ----- .../src/main/java/bjc/dicelang/ScalarDie.java | 54 ---- .../java/bjc/dicelang/ast/ArithmeticCollapser.java | 192 ------------ .../main/java/bjc/dicelang/ast/ArrayResult.java | 43 --- .../java/bjc/dicelang/ast/DiceASTEvaluator.java | 321 --------------------- .../main/java/bjc/dicelang/ast/DiceASTInliner.java | 125 -------- .../java/bjc/dicelang/ast/DiceASTOptimizer.java | 60 ---- .../main/java/bjc/dicelang/ast/DiceASTParser.java | 160 ---------- .../bjc/dicelang/ast/DiceASTReferenceChecker.java | 61 ---- .../dicelang/ast/DiceASTReferenceSanitizer.java | 201 ------------- .../main/java/bjc/dicelang/ast/DiceASTUtils.java | 86 ------ .../main/java/bjc/dicelang/ast/DummyResult.java | 31 -- .../java/bjc/dicelang/ast/IOperatorCollapser.java | 20 -- .../src/main/java/bjc/dicelang/ast/IResult.java | 16 - .../main/java/bjc/dicelang/ast/IntegerResult.java | 40 --- .../src/main/java/bjc/dicelang/ast/ResultType.java | 22 -- .../java/bjc/dicelang/ast/nodes/DiceASTType.java | 27 -- .../bjc/dicelang/ast/nodes/DiceLiteralNode.java | 52 ---- .../bjc/dicelang/ast/nodes/DiceLiteralType.java | 18 -- .../bjc/dicelang/ast/nodes/DiceOperatorType.java | 29 -- .../java/bjc/dicelang/ast/nodes/IDiceASTNode.java | 23 -- .../bjc/dicelang/ast/nodes/ILiteralDiceNode.java | 73 ----- .../bjc/dicelang/ast/nodes/IntegerLiteralNode.java | 50 ---- .../bjc/dicelang/ast/nodes/OperatorDiceNode.java | 110 ------- .../bjc/dicelang/ast/nodes/VariableDiceNode.java | 101 ------- .../java/bjc/dicelang/ast/nodes/package-info.java | 7 - .../ast/optimization/ArithmeticCollapser.java | 50 ---- .../ast/optimization/ConstantCollapser.java | 91 ------ .../ast/optimization/IOptimizationPass.java | 35 --- .../ast/optimization/OperationCondenser.java | 107 ------- .../dicelang/ast/optimization/package-info.java | 7 - .../main/java/bjc/dicelang/ast/package-info.java | 7 - 97 files changed, 3982 insertions(+), 3982 deletions(-) create mode 100644 dice-lang/src/.DS_Store create mode 100644 dice-lang/src/bjc/dicelang/BindingDiceExpression.java create mode 100644 dice-lang/src/bjc/dicelang/ComplexDice.java create mode 100644 dice-lang/src/bjc/dicelang/CompoundDice.java create mode 100644 dice-lang/src/bjc/dicelang/DiceExpressionBuilder.java create mode 100644 dice-lang/src/bjc/dicelang/DiceExpressionParser.java create mode 100644 dice-lang/src/bjc/dicelang/DiceExpressionType.java create mode 100644 dice-lang/src/bjc/dicelang/Die.java create mode 100644 dice-lang/src/bjc/dicelang/IDiceExpression.java create mode 100644 dice-lang/src/bjc/dicelang/OperatorDiceExpression.java create mode 100644 dice-lang/src/bjc/dicelang/PolyhedralDice.java create mode 100644 dice-lang/src/bjc/dicelang/ReferenceDiceExpression.java create mode 100644 dice-lang/src/bjc/dicelang/ScalarDie.java create mode 100644 dice-lang/src/bjc/dicelang/ast/ArithmeticCollapser.java create mode 100644 dice-lang/src/bjc/dicelang/ast/ArrayResult.java create mode 100644 dice-lang/src/bjc/dicelang/ast/DiceASTEvaluator.java create mode 100644 dice-lang/src/bjc/dicelang/ast/DiceASTInliner.java create mode 100644 dice-lang/src/bjc/dicelang/ast/DiceASTOptimizer.java create mode 100644 dice-lang/src/bjc/dicelang/ast/DiceASTParser.java create mode 100644 dice-lang/src/bjc/dicelang/ast/DiceASTReferenceChecker.java create mode 100644 dice-lang/src/bjc/dicelang/ast/DiceASTReferenceSanitizer.java create mode 100644 dice-lang/src/bjc/dicelang/ast/DiceASTUtils.java create mode 100644 dice-lang/src/bjc/dicelang/ast/DummyResult.java create mode 100644 dice-lang/src/bjc/dicelang/ast/IOperatorCollapser.java create mode 100644 dice-lang/src/bjc/dicelang/ast/IResult.java create mode 100644 dice-lang/src/bjc/dicelang/ast/IntegerResult.java create mode 100644 dice-lang/src/bjc/dicelang/ast/ResultType.java create mode 100644 dice-lang/src/bjc/dicelang/ast/nodes/DiceASTType.java create mode 100644 dice-lang/src/bjc/dicelang/ast/nodes/DiceLiteralNode.java create mode 100644 dice-lang/src/bjc/dicelang/ast/nodes/DiceLiteralType.java create mode 100644 dice-lang/src/bjc/dicelang/ast/nodes/DiceOperatorType.java create mode 100644 dice-lang/src/bjc/dicelang/ast/nodes/IDiceASTNode.java create mode 100644 dice-lang/src/bjc/dicelang/ast/nodes/ILiteralDiceNode.java create mode 100644 dice-lang/src/bjc/dicelang/ast/nodes/IntegerLiteralNode.java create mode 100644 dice-lang/src/bjc/dicelang/ast/nodes/OperatorDiceNode.java create mode 100644 dice-lang/src/bjc/dicelang/ast/nodes/VariableDiceNode.java create mode 100644 dice-lang/src/bjc/dicelang/ast/nodes/package-info.java create mode 100644 dice-lang/src/bjc/dicelang/ast/optimization/ArithmeticCollapser.java create mode 100644 dice-lang/src/bjc/dicelang/ast/optimization/ConstantCollapser.java create mode 100644 dice-lang/src/bjc/dicelang/ast/optimization/IOptimizationPass.java create mode 100644 dice-lang/src/bjc/dicelang/ast/optimization/OperationCondenser.java create mode 100644 dice-lang/src/bjc/dicelang/ast/optimization/package-info.java create mode 100644 dice-lang/src/bjc/dicelang/ast/package-info.java create mode 100644 dice-lang/src/bjc/dicelang/examples/DiceASTLanguageTest.java create mode 100644 dice-lang/src/bjc/dicelang/examples/DiceASTPragma.java create mode 100644 dice-lang/src/bjc/dicelang/examples/DiceExpressionParserTest.java create mode 100644 dice-lang/src/bjc/dicelang/examples/DiceExpressionPreparer.java create mode 100644 dice-lang/src/bjc/dicelang/examples/DiceLanguageState.java create mode 100644 dice-lang/src/bjc/dicelang/examples/DiceLanguageTest.java delete mode 100644 dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTLanguageTest.java delete mode 100644 dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTPragma.java delete mode 100644 dice-lang/src/examples/java/bjc/dicelang/examples/DiceExpressionParserTest.java delete mode 100644 dice-lang/src/examples/java/bjc/dicelang/examples/DiceExpressionPreparer.java delete mode 100644 dice-lang/src/examples/java/bjc/dicelang/examples/DiceLanguageState.java delete mode 100644 dice-lang/src/examples/java/bjc/dicelang/examples/DiceLanguageTest.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/BindingDiceExpression.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ComplexDice.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/CompoundDice.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/DiceExpressionBuilder.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/DiceExpressionParser.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/DiceExpressionType.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/Die.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/IDiceExpression.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/OperatorDiceExpression.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/PolyhedralDice.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ReferenceDiceExpression.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ScalarDie.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/ArithmeticCollapser.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/ArrayResult.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/DiceASTEvaluator.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/DiceASTInliner.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/DiceASTOptimizer.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/DiceASTParser.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/DiceASTReferenceChecker.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/DiceASTReferenceSanitizer.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/DiceASTUtils.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/DummyResult.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/IOperatorCollapser.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/IResult.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/IntegerResult.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/ResultType.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/nodes/DiceASTType.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/nodes/DiceLiteralNode.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/nodes/DiceLiteralType.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/nodes/DiceOperatorType.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/nodes/IDiceASTNode.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/nodes/ILiteralDiceNode.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/nodes/IntegerLiteralNode.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/nodes/OperatorDiceNode.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/nodes/VariableDiceNode.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/nodes/package-info.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/optimization/ArithmeticCollapser.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/optimization/ConstantCollapser.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/optimization/IOptimizationPass.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/optimization/OperationCondenser.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/optimization/package-info.java delete mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/package-info.java (limited to 'dice-lang/src') diff --git a/dice-lang/src/.DS_Store b/dice-lang/src/.DS_Store new file mode 100644 index 0000000..c5a38b7 Binary files /dev/null and b/dice-lang/src/.DS_Store differ diff --git a/dice-lang/src/bjc/dicelang/BindingDiceExpression.java b/dice-lang/src/bjc/dicelang/BindingDiceExpression.java new file mode 100644 index 0000000..6a030e3 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/BindingDiceExpression.java @@ -0,0 +1,93 @@ +package bjc.dicelang; + +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 expression; + + /** + * The name to bind the expression to + */ + private String variableName; + + /** + * 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 enviroment + * The enviroment to bind into + */ + public BindingDiceExpression(IDiceExpression left, + IDiceExpression right, + Map enviroment) { + if (!(left instanceof ReferenceDiceExpression)) { + throw new UnsupportedOperationException( + "Error: Binding an expression to something that is not a variable reference," + + " or array thereof. is unsupported." + + " Problematic expression is " + left); + } + + String varName = ((ReferenceDiceExpression) left).getName(); + + initialize(varName, right, enviroment); + } + + /** + * Create a new dice expression binder + * + * @param name + * The name of the variable to bind + * @param expression + * The expression to bind to the variable + * @param enviroment + * The enviroment to bind it in + */ + public BindingDiceExpression(String name, IDiceExpression expression, + Map enviroment) { + initialize(name, expression, enviroment); + } + + private void initialize(String name, IDiceExpression expr, + Map enviroment) { + this.variableName = name; + this.expression = expr; + + enviroment.put(name, expr); + } + + /* + * (non-Javadoc) + * + * @see bjc.utils.dice.IDiceExpression#roll() + */ + @Override + public int roll() { + return expression.roll(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "assign[n=" + variableName + ", exp=" + + expression.toString() + "]"; + } +} diff --git a/dice-lang/src/bjc/dicelang/ComplexDice.java b/dice-lang/src/bjc/dicelang/ComplexDice.java new file mode 100644 index 0000000..9bf191f --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ComplexDice.java @@ -0,0 +1,147 @@ +package bjc.dicelang; + +/** + * 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 expression + * The string to parse the dice from + * @return A dice group parsed from the string + */ + public static IDiceExpression fromString(String expression) { + // Handle the case where someone passes us a simple expression + // containing a single die + if (!expression.contains("d")) { + return new Die(Integer.parseInt(expression)); + } + // Split it on the dice type marker + + String[] strangs = expression.split("d"); + + try { + // Create the actual group of dice + return new ComplexDice( + new ScalarDie(Integer.parseInt(strangs[0])), + new Die(Integer.parseInt(strangs[1]))); + } catch (@SuppressWarnings("unused") NumberFormatException nfex) { + // We don't care about details + + // Tell the user the expression is invalid + throw new IllegalArgumentException( + "Attempted to create a set of dice using invalid arguments." + + " They must be integers. " + strangs[0] + + " and " + strangs[1] + + " are likely culprits."); + } + } + + /** + * The die being rolled + */ + private IDiceExpression die; + + /** + * The number of the particular 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); + } + + @Override + public boolean canOptimize() { + // Can only optimize this dice group if both components can be + // optimized and the die itself has only one value + if (nDice.canOptimize() && die.canOptimize()) { + return die.optimize() == 1; + } + + return false; + } + + @Override + public int optimize() { + if (!canOptimize()) { + throw new UnsupportedOperationException( + "This complex dice cannot be optimized. " + + "Both the dice to be rolled and the number of" + + " dice must be optimizable."); + } + + return nDice.optimize(); + } + + /* + * (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(); + + if (nRoll < 0) { + throw new UnsupportedOperationException( + "Attempted to roll a negative number of dice. " + + "The problematic expression is " + nDice); + } + + // Roll all the dice and combine them + for (int i = 0; i < nRoll; i++) { + res += die.roll(); + } + + return res; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + // Print simple dice groups in a much clearer manner + if (nDice instanceof ScalarDie && die instanceof Die) { + return nDice.toString() + die.toString(); + } + + return "complex[n=" + nDice.toString() + ", d=" + die.toString() + + "]"; + } +} \ No newline at end of file diff --git a/dice-lang/src/bjc/dicelang/CompoundDice.java b/dice-lang/src/bjc/dicelang/CompoundDice.java new file mode 100644 index 0000000..704e4cd --- /dev/null +++ b/dice-lang/src/bjc/dicelang/CompoundDice.java @@ -0,0 +1,100 @@ +package bjc.dicelang; + +/** + * 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 leftDice; + + /** + * The right die of the expression + */ + private IDiceExpression rightDice; + + /** + * Create a new compound dice using the specified dice + * + * @param left + * The die to use on the left + * @param right + * The die to use on the right + */ + public CompoundDice(IDiceExpression left, IDiceExpression right) { + this.leftDice = left; + this.rightDice = right; + } + + /** + * Create a new compound dice from two dice strings + * + * @param leftExp + * The left side dice as a string + * @param rightExp + * The right side dice as a string + */ + public CompoundDice(String leftExp, String rightExp) { + this(ComplexDice.fromString(leftExp), + ComplexDice.fromString(rightExp)); + } + + /** + * Create a new compound dice from an array of dice strings + * + * @param exps + * An array of two dice strings + */ + public CompoundDice(String[] exps) { + this(exps[0], exps[1]); + } + + @Override + public boolean canOptimize() { + return leftDice.canOptimize() && rightDice.canOptimize(); + } + + @Override + public int optimize() { + if (!canOptimize()) { + throw new UnsupportedOperationException( + "Cannot optimize this compound dice. " + + "Both component dice must be optimizable" + + " to optimize a compound dice"); + } + + return Integer + .parseInt(leftDice.optimize() + "" + rightDice.optimize()); + } + + /* + * (non-Javadoc) + * + * @see bjc.utils.dice.IDiceExpression#roll() + */ + @Override + public int roll() { + /* + * Make the combination of the two dice + */ + return Integer.parseInt(leftDice.roll() + "" + rightDice.roll()); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "compound[l=" + leftDice.toString() + ", r=" + + rightDice.toString() + "]"; + } +} \ No newline at end of file diff --git a/dice-lang/src/bjc/dicelang/DiceExpressionBuilder.java b/dice-lang/src/bjc/dicelang/DiceExpressionBuilder.java new file mode 100644 index 0000000..af856a6 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/DiceExpressionBuilder.java @@ -0,0 +1,150 @@ +package bjc.dicelang; + +import static bjc.dicelang.DiceExpressionType.ADD; +import static bjc.dicelang.DiceExpressionType.DIVIDE; +import static bjc.dicelang.DiceExpressionType.MULTIPLY; +import static bjc.dicelang.DiceExpressionType.SUBTRACT; + +/** + * Build a dice expression piece by piece + * + * @author ben + * + */ +public class DiceExpressionBuilder { + /** + * The dice expression we are building + */ + private IDiceExpression baking; + + /** + * 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 OperatorDiceExpression(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 OperatorDiceExpression(baking, new ScalarDie(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 OperatorDiceExpression(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 OperatorDiceExpression(baking, new ScalarDie(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 OperatorDiceExpression(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 OperatorDiceExpression(baking, new ScalarDie(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 OperatorDiceExpression(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 OperatorDiceExpression(baking, new ScalarDie(num), + SUBTRACT); + return this; + } +} diff --git a/dice-lang/src/bjc/dicelang/DiceExpressionParser.java b/dice-lang/src/bjc/dicelang/DiceExpressionParser.java new file mode 100644 index 0000000..8a1552a --- /dev/null +++ b/dice-lang/src/bjc/dicelang/DiceExpressionParser.java @@ -0,0 +1,173 @@ +package bjc.dicelang; + +import java.util.Map; +import java.util.Stack; + +import bjc.utils.funcdata.FunctionalStringTokenizer; +import bjc.utils.funcdata.IList; +import bjc.utils.parserutils.ShuntingYard; + +import org.apache.commons.lang3.StringUtils; + +/** + * Parse a dice expression from a string + * + * @author ben + * + */ +public class DiceExpressionParser { + /** + * Parse a dice expression from a string + * + * @param expression + * The string to parse an expression from + * @param enviroment + * The enviroment to use when parsing expressions + * @return The parsed dice expression + */ + public static IDiceExpression parse(String expression, + Map enviroment) { + /* + * Create a tokenizer over the strings + */ + FunctionalStringTokenizer tokenizer = new FunctionalStringTokenizer( + expression); + + /* + * Create a shunter to rewrite the expression + */ + ShuntingYard yard = new ShuntingYard<>(true); + + /* + * 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 + */ + IList list = yard.postfix(tokenizer.toList(), s -> s); + + /* + * Create a stack for building an expression from parts + */ + Stack expressions = new Stack<>(); + + /* + * Create the expression from parts + */ + list.forEach((expressionPart) -> { + /* + * Handle compound dice + */ + if (StringUtils.countMatches(expressionPart, 'c') == 1 + && !expressionPart.equalsIgnoreCase("c")) { + String[] strangs = expressionPart.split("c"); + + expressions.push(new CompoundDice(strangs)); + } else if (StringUtils.countMatches(expressionPart, 'd') == 1 + && !expressionPart.equalsIgnoreCase("d")) { + /* + * Handle dice groups + */ + expressions.push(ComplexDice.fromString(expressionPart)); + } else { + try { + /* + * Handle scalar numbers + */ + expressions.push(new ScalarDie( + Integer.parseInt(expressionPart))); + } catch (@SuppressWarnings("unused") NumberFormatException nfex) { + // We don't care about details, just that it failed + if (expressions.size() >= 2) { + /* + * Apply an operation to two dice + */ + IDiceExpression rightExpression = expressions + .pop(); + IDiceExpression leftExpression = expressions.pop(); + + switch (expressionPart) { + case ":=": + expressions.push(new BindingDiceExpression( + leftExpression, rightExpression, + enviroment)); + break; + case "+": + expressions + .push(new OperatorDiceExpression( + rightExpression, + leftExpression, + DiceExpressionType.ADD)); + break; + case "-": + expressions + .push(new OperatorDiceExpression( + rightExpression, + leftExpression, + DiceExpressionType.SUBTRACT)); + break; + case "*": + expressions + .push(new OperatorDiceExpression( + rightExpression, + leftExpression, + DiceExpressionType.MULTIPLY)); + break; + case "/": + expressions + .push(new OperatorDiceExpression( + rightExpression, + leftExpression, + DiceExpressionType.DIVIDE)); + break; + case "c": + expressions.push(new CompoundDice( + leftExpression, rightExpression)); + break; + case "d": + expressions.push(new ComplexDice( + leftExpression, rightExpression)); + break; + default: + /* + * Parse it as a variable reference + * + * Make sure to restore popped variables + */ + expressions.push(leftExpression); + expressions.push(rightExpression); + + expressions + .push(new ReferenceDiceExpression( + expressionPart, + enviroment)); + } + } else { + /* + * Parse it as a variable reference + */ + expressions.push(new ReferenceDiceExpression( + expressionPart, enviroment)); + } + } + } + }); + + if (expressions.size() != 1) { + System.err.println( + "WARNING: Leftovers found on dice expression stack. Remember, := is assignment."); + } + + /* + * Return the built expression + */ + return expressions.pop(); + } +} diff --git a/dice-lang/src/bjc/dicelang/DiceExpressionType.java b/dice-lang/src/bjc/dicelang/DiceExpressionType.java new file mode 100644 index 0000000..296d751 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/DiceExpressionType.java @@ -0,0 +1,49 @@ +package bjc.dicelang; + +/** + * 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/dicelang/Die.java b/dice-lang/src/bjc/dicelang/Die.java new file mode 100644 index 0000000..192daaa --- /dev/null +++ b/dice-lang/src/bjc/dicelang/Die.java @@ -0,0 +1,71 @@ +package bjc.dicelang; + +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) { + if (nSides < 1) { + throw new UnsupportedOperationException( + "Dice with less than 1 side are not supported"); + } + + this.nSides = nSides; + } + + @Override + public boolean canOptimize() { + return nSides == 1; + } + + @Override + public int optimize() { + if (nSides != 1) { + throw new UnsupportedOperationException( + "Can't optimize " + nSides + "-sided dice"); + } + + return 1; + } + + /* + * (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/dicelang/IDiceExpression.java b/dice-lang/src/bjc/dicelang/IDiceExpression.java new file mode 100644 index 0000000..acb1d4d --- /dev/null +++ b/dice-lang/src/bjc/dicelang/IDiceExpression.java @@ -0,0 +1,91 @@ +package bjc.dicelang; + +import bjc.utils.funcutils.StringUtils; + +/** + * An expression for something that can be rolled like a polyhedral die + * + * @author ben + * + */ +@FunctionalInterface +public interface IDiceExpression { + /** + * Parse a string into an expression. + * + * It can accept the following types of expressions + *
    + *
  • Simple integers - '2'
  • + *
  • Simple dice - 'd6'
  • + *
  • Groups of simple dice - '2d6'
  • + *
  • Number concatenation - '2c6'
  • + *
  • Dice concatenation - '1d10c1d10
  • + *
+ * + * Dice concatenation is like using 2 d10s to emulate a d100, so + * instead of adding them, it reads them side by side. + * + * @param expression + * The string to convert to an expression + * + * @return The string, converted into expression form + */ + static IDiceExpression toExpression(String expression) { + String literalData = expression; + + if (StringUtils.containsInfixOperator(literalData, "c")) { + // Parse a compound die + String[] strangs = literalData.split("c"); + + return new CompoundDice(strangs); + } else if (StringUtils.containsInfixOperator(literalData, "d")) { + // Handle groups of similiar dice + return ComplexDice.fromString(literalData); + } else if (literalData.matches("\\Ad\\d+\\Z")) { + // Handle people who put 'd6' instead of '1d6' + return new Die(Integer.parseInt(literalData.substring(1))); + } else { + // Parse a scalar number + try { + return new ScalarDie(Integer.parseInt(literalData)); + } catch (NumberFormatException nfex) { + UnsupportedOperationException usex = new UnsupportedOperationException( + "Found malformed leaf token " + expression); + + usex.initCause(nfex); + + throw usex; + } + } + } + + /** + * Check if this expression can be optimized to a scalar value + * + * @return Whether or not this expression can be optimized to a scalar + * value + */ + public default boolean canOptimize() { + return false; + } + + /** + * Optimize this expression to a scalar value + * + * @return This expression, optimized to a scalar value + * + * @throws UnsupportedOperationException + * if this type of expression can't be optimized + */ + public default int optimize() { + throw new UnsupportedOperationException( + "Can't optimize this type of expression"); + } + + /** + * Roll the dice once + * + * @return The result of rowing the dice + */ + public int roll(); +} \ No newline at end of file diff --git a/dice-lang/src/bjc/dicelang/OperatorDiceExpression.java b/dice-lang/src/bjc/dicelang/OperatorDiceExpression.java new file mode 100644 index 0000000..f86773d --- /dev/null +++ b/dice-lang/src/bjc/dicelang/OperatorDiceExpression.java @@ -0,0 +1,96 @@ +package bjc.dicelang; + +/** + * Implements a class for combining two dice with an operator + * + * @author ben + * + */ +public class OperatorDiceExpression implements IDiceExpression { + /** + * The operator to use for combining the dice + */ + private DiceExpressionType expressionType; + + /** + * The dice on the left side of the expression + */ + private IDiceExpression leftExpression; + + /** + * The dice on the right side of the expression + */ + private IDiceExpression rightExpression; + + /** + * 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 type + * The operator to use for combining the dices + */ + public OperatorDiceExpression(IDiceExpression right, + IDiceExpression left, DiceExpressionType type) { + this.rightExpression = right; + this.leftExpression = left; + this.expressionType = type; + } + + /* + * (non-Javadoc) + * + * @see bjc.utils.dice.IDiceExpression#roll() + */ + @Override + public int roll() { + /* + * Handle each operator + */ + switch (expressionType) { + case ADD: + return rightExpression.roll() + leftExpression.roll(); + case SUBTRACT: + return rightExpression.roll() - leftExpression.roll(); + case MULTIPLY: + return rightExpression.roll() * leftExpression.roll(); + case DIVIDE: + /* + * Round to keep results as integers. We don't really have + * any need for floating-point dice, and continuous + * probability is a pain + */ + try { + return rightExpression.roll() / leftExpression.roll(); + } catch (ArithmeticException aex) { + UnsupportedOperationException usex = new UnsupportedOperationException( + "Attempted to divide by zero." + + " Problematic expression is " + + leftExpression); + + usex.initCause(aex); + + throw usex; + } + default: + throw new IllegalArgumentException( + "Got passed a invalid ScalarExpressionType (" + + expressionType + "). WAT"); + + } + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "dice-exp[type=" + expressionType + ", l=" + + leftExpression.toString() + ", r=" + + rightExpression.toString() + "]"; + } +} \ No newline at end of file diff --git a/dice-lang/src/bjc/dicelang/PolyhedralDice.java b/dice-lang/src/bjc/dicelang/PolyhedralDice.java new file mode 100644 index 0000000..3ac9420 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/PolyhedralDice.java @@ -0,0 +1,149 @@ +package bjc.dicelang; + +/** + * Utility class that produces common polyhedral dice + * + * @author ben + * + */ +public class PolyhedralDice { + /** + * Produce a single d10 + * + * @return A single d10 + */ + public static IDiceExpression d10() { + return d10(1); + } + + /** + * 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 a single d100 + * + * @return A single d100 + */ + public static IDiceExpression d100() { + return d100(1); + } + + /** + * 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 a single d12 + * + * @return A single d12 + */ + public static IDiceExpression d12() { + return d12(1); + } + + /** + * 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 a single d20 + * + * @return A single d20 + */ + public static IDiceExpression d20() { + return d20(1); + } + + /** + * 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 a single d4 + * + * @return A single d4 + */ + public static IDiceExpression d4() { + return d4(1); + } + + /** + * 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 a single d6 + * + * @return A single d6 + */ + public static IDiceExpression d6() { + return d6(1); + } + + /** + * 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 a single d8 + * + * @return A single d8 + */ + public static IDiceExpression d8() { + return d8(1); + } + + /** + * 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/dicelang/ReferenceDiceExpression.java b/dice-lang/src/bjc/dicelang/ReferenceDiceExpression.java new file mode 100644 index 0000000..483545b --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ReferenceDiceExpression.java @@ -0,0 +1,76 @@ +package bjc.dicelang; + +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 enviroment; + + /** + * The name of the bound variable + */ + private String variableName; + + /** + * 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 env) { + this.variableName = name; + this.enviroment = env; + } + + /** + * Get the name of the referenced variable + * + * @return the name of the referenced variable + */ + public String getName() { + return variableName; + } + + /* + * (non-Javadoc) + * + * @see bjc.utils.dice.IDiceExpression#roll() + */ + @Override + public int roll() { + if (!enviroment.containsKey(variableName)) { + throw new UnsupportedOperationException( + "Attempted to reference undefined variable " + + variableName); + } + + return enviroment.get(variableName).roll(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + if (enviroment.containsKey(variableName)) { + return enviroment.get(variableName).toString(); + } + + return variableName; + } +} \ No newline at end of file diff --git a/dice-lang/src/bjc/dicelang/ScalarDie.java b/dice-lang/src/bjc/dicelang/ScalarDie.java new file mode 100644 index 0000000..8d31d15 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ScalarDie.java @@ -0,0 +1,54 @@ +package bjc.dicelang; + +/** + * A die that represents a static number + * + * @author ben + * + */ +public class ScalarDie implements IDiceExpression { + /** + * The represented number + */ + private int number; + + /** + * Create a dice with the specified number + * + * @param num + * The number used for the dice + */ + public ScalarDie(int num) { + this.number = num; + } + + @Override + public boolean canOptimize() { + return true; + } + + @Override + public int optimize() { + return number; + } + + /* + * (non-Javadoc) + * + * @see bjc.utils.dice.IDiceExpression#roll() + */ + @Override + public int roll() { + return number; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return Integer.toString(number); + } +} \ No newline at end of file diff --git a/dice-lang/src/bjc/dicelang/ast/ArithmeticCollapser.java b/dice-lang/src/bjc/dicelang/ast/ArithmeticCollapser.java new file mode 100644 index 0000000..2b9eaa4 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/ArithmeticCollapser.java @@ -0,0 +1,192 @@ +package bjc.dicelang.ast; + +import java.util.function.BinaryOperator; + +import bjc.utils.data.IPair; +import bjc.utils.data.Pair; +import bjc.utils.funcdata.IList; +import bjc.utils.funcdata.ITree; +import bjc.utils.funcdata.Tree; + +import bjc.dicelang.ast.nodes.IDiceASTNode; +import bjc.dicelang.ast.nodes.OperatorDiceNode; + +/** + * Responsible for collapsing arithmetic operators + * + * @author ben + * + */ +final class ArithmeticCollapser implements IOperatorCollapser { + // The type of operator we're collapsing + private OperatorDiceNode type; + + // The operator to use to collapse operators + private BinaryOperator valueOp; + + private int initialValue; + + public ArithmeticCollapser(OperatorDiceNode type, + BinaryOperator valueOp, int initVal) { + this.type = type; + this.valueOp = valueOp; + this.initialValue = initVal; + } + + @Override + public IPair> apply( + IList>> nodes) { + IPair> initialState = new Pair<>( + new IntegerResult(initialValue), new Tree<>(type)); + + BinaryOperator>> reducer = ( + currentState, accumulatedState) -> { + // Force evaluation of accumulated state to prevent + // certain bugs from occuring + // accumulatedState.merge((l, r) -> null); + + return reduceStates(accumulatedState, currentState); + }; + + IPair> reducedState = nodes + .reduceAux(initialState, reducer, (state) -> state); + + return reducedState; + } + + private IList combineArrayResults(IResult accumulatedValue, + IResult currentValue) { + IList currentList = ((ArrayResult) currentValue) + .getValue(); + IList accumulatedList = ((ArrayResult) accumulatedValue) + .getValue(); + + if (currentList.getSize() != accumulatedList.getSize()) { + throw new UnsupportedOperationException( + "Can only apply operations to equal-length arrays"); + } + + IList resultList = currentList.combineWith( + accumulatedList, (currentNode, accumulatedNode) -> { + boolean currentNotInt = currentNode + .getType() != ResultType.INTEGER; + boolean accumulatedNotInt = accumulatedNode + .getType() != ResultType.INTEGER; + + if (currentNotInt || accumulatedNotInt) { + throw new UnsupportedOperationException( + "Nesting of array operations isn't allowed"); + } + + int accumulatedInt = ((IntegerResult) accumulatedNode) + .getValue(); + int currentInt = ((IntegerResult) currentNode) + .getValue(); + + IResult combinedValue = new IntegerResult( + valueOp.apply(accumulatedInt, currentInt)); + return combinedValue; + }); + return resultList; + } + + private IPair> doArithmeticCollapse( + IResult accumulatedValue, ITree accumulatedTree, + IResult currentValue) { + if (accumulatedValue.getType() == ResultType.DUMMY + || currentValue.getType() == ResultType.DUMMY) { + DummyResult result = new DummyResult( + "Found dummy result with either accumulated dummy (" + + ((DummyResult) accumulatedValue).getData() + + ") or current dummy (" + + ((DummyResult) currentValue).getData() + + ")."); + + return new Pair<>(result, accumulatedTree); + } + + boolean currentIsInt = currentValue + .getType() == ResultType.INTEGER; + boolean accumulatedIsInt = accumulatedValue + .getType() == ResultType.INTEGER; + + if (!currentIsInt) { + if (!accumulatedIsInt) { + IList resultList = combineArrayResults( + accumulatedValue, currentValue); + + return new Pair<>(new ArrayResult(resultList), + accumulatedTree); + } + + IList resultList = halfCombineLists( + ((ArrayResult) currentValue).getValue(), + accumulatedValue, true); + + return new Pair<>(new ArrayResult(resultList), + accumulatedTree); + } else if (!accumulatedIsInt) { + IList resultList = halfCombineLists( + ((ArrayResult) accumulatedValue).getValue(), + currentValue, false); + + return new Pair<>(new ArrayResult(resultList), + accumulatedTree); + } + + int accumulatedInt = ((IntegerResult) accumulatedValue).getValue(); + int currentInt = ((IntegerResult) currentValue).getValue(); + + int combinedValue = valueOp.apply(accumulatedInt, currentInt); + + return new Pair<>(new IntegerResult(combinedValue), + accumulatedTree); + } + + private IList halfCombineLists(IList list, + IResult scalar, boolean scalarLeft) { + if (scalar.getType() != ResultType.INTEGER) { + throw new UnsupportedOperationException( + "Nested array operations not supported"); + } + + int scalarInt = ((IntegerResult) scalar).getValue(); + + return list.map((element) -> { + if (element.getType() != ResultType.INTEGER) { + throw new UnsupportedOperationException( + "Nested array operations not supported"); + } + + int elementInt = ((IntegerResult) element).getValue(); + + IResult combinedValue; + + if (scalarLeft) { + combinedValue = new IntegerResult( + valueOp.apply(scalarInt, elementInt)); + } else { + combinedValue = new IntegerResult( + valueOp.apply(elementInt, scalarInt)); + } + + return combinedValue; + }); + } + + private IPair> reduceStates( + IPair> accumulatedState, + IPair> currentState) { + return accumulatedState + .bind((accumulatedValue, accumulatedTree) -> { + return currentState + .bind((currentValue, currentTree) -> { + accumulatedTree.addChild(currentTree); + + return doArithmeticCollapse( + accumulatedValue, accumulatedTree, + currentValue); + }); + }); + } +} \ No newline at end of file diff --git a/dice-lang/src/bjc/dicelang/ast/ArrayResult.java b/dice-lang/src/bjc/dicelang/ast/ArrayResult.java new file mode 100644 index 0000000..ac78287 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/ArrayResult.java @@ -0,0 +1,43 @@ +package bjc.dicelang.ast; + +import bjc.utils.funcdata.IList; + +/** + * Represents a result that is an array of other results + * + * @author ben + * + * TODO finish implementing me + */ +public class ArrayResult implements IResult { + private IList arrayContents; + + /** + * Create a new array-valued result + * + * @param results + * The results in the array + */ + public ArrayResult(IList results) { + this.arrayContents = results; + } + + @Override + public ResultType getType() { + return ResultType.ARRAY; + } + + /** + * Get the value of this result + * + * @return The value of this result + */ + public IList getValue() { + return arrayContents; + } + + @Override + public String toString() { + return arrayContents.toString(); + } +} diff --git a/dice-lang/src/bjc/dicelang/ast/DiceASTEvaluator.java b/dice-lang/src/bjc/dicelang/ast/DiceASTEvaluator.java new file mode 100644 index 0000000..cef2e19 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/DiceASTEvaluator.java @@ -0,0 +1,321 @@ +package bjc.dicelang.ast; + +import java.util.function.Supplier; + +import bjc.utils.data.IHolder; +import bjc.utils.data.IPair; +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.funcdata.ITree; +import bjc.utils.funcdata.Tree; + +import bjc.dicelang.ComplexDice; +import bjc.dicelang.ast.nodes.DiceASTType; +import bjc.dicelang.ast.nodes.DiceLiteralNode; +import bjc.dicelang.ast.nodes.DiceLiteralType; +import bjc.dicelang.ast.nodes.IDiceASTNode; +import bjc.dicelang.ast.nodes.ILiteralDiceNode; +import bjc.dicelang.ast.nodes.IntegerLiteralNode; +import bjc.dicelang.ast.nodes.OperatorDiceNode; +import bjc.dicelang.ast.nodes.VariableDiceNode; + +/** + * Evaluate a dice AST to an integer value + * + * @author ben + * + */ +public class DiceASTEvaluator { + private static IResult bindLiteralValue(IDiceASTNode leafNode, + IMap> 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 buildOperations( + IMap> enviroment) { + IMap 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 resultSupplier = () -> { + IList resultList = new FunctionalList<>(); + + nodes.forEach((node) -> { + resultList.add(node.getLeft()); + }); + + return new ArrayResult(resultList); + }; + + Supplier> treeSupplier = () -> { + ITree 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> enviroment, + IPair> nameNode, + ITree nameTree, ITree valueTree, + IHolder childCount, ITree 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 expression, + IMap> enviroment) { + IMap collapsers = buildOperations( + enviroment); + + return expression.collapse( + (node) -> evaluateLeaf(node, enviroment), collapsers::get, + (pair) -> pair.getLeft()); + } + + private static IPair> evaluateLeaf( + IDiceASTNode leafNode, + IMap> enviroment) { + ITree 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> parseBinding( + IMap> enviroment, + IList>> nodes) { + if (nodes.getSize() != 2) { + throw new UnsupportedOperationException( + "Can only bind nodes with two children. Problem children are " + + nodes); + } + + IPair> nameNode = nodes.getByIndex(0); + IPair> 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 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> parseGroup( + IList>> nodes) { + if (nodes.getSize() != 2) { + throw new UnsupportedOperationException( + "Can only form a group from two dice"); + } + + IPair> numberDiceNode = nodes + .getByIndex(0); + IPair> 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> parseLet( + IMap> enviroment, + IList>> nodes) { + if (nodes.getSize() != 2) { + throw new UnsupportedOperationException( + "Can only use let with two expressions."); + } + + ITree bindTree = nodes.getByIndex(0).getRight(); + ITree expressionTree = nodes.getByIndex(1) + .getRight(); + + IMap> letEnviroment = enviroment + .extend(); + + System.out.println("Evaluating tree for bound values"); + + evaluateAST(bindTree, letEnviroment); + + IResult exprResult = evaluateAST(expressionTree, letEnviroment); + + IList> childrn = nodes + .map((pair) -> pair.getRight()); + + return new Pair<>(exprResult, + new Tree<>(OperatorDiceNode.LET, childrn)); + } +} diff --git a/dice-lang/src/bjc/dicelang/ast/DiceASTInliner.java b/dice-lang/src/bjc/dicelang/ast/DiceASTInliner.java new file mode 100644 index 0000000..305c9af --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/DiceASTInliner.java @@ -0,0 +1,125 @@ +package bjc.dicelang.ast; + +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; +import bjc.utils.funcdata.IMap; +import bjc.utils.funcdata.ITree; +import bjc.utils.funcdata.Tree; + +import bjc.dicelang.ast.nodes.DiceASTType; +import bjc.dicelang.ast.nodes.IDiceASTNode; +import bjc.dicelang.ast.nodes.VariableDiceNode; + +/** + * Inline variables in a dice AST + * + * @author ben + * + */ +public class DiceASTInliner { + /** + * Inline all the variables in the AST + * + * @param ast + * The AST to inline variables into + * @param enviroment + * The enviroment to inline from + * @return The inlined AST + */ + public static ITree inlineAll(ITree ast, + IMap> enviroment) { + // Tell the compiler that the null is for the entire varargs + // parameter, not a single one with a null value + return selectiveInline(ast, enviroment, (String[]) null); + } + + private static ITree inlineNode(IDiceASTNode node, + IMap> enviroment, + boolean specificInline, IList variableNames) { + // Only variables get inlined + if (node.getType() != DiceASTType.VARIABLE) { + return new Tree<>(node); + } + + // Get the name of what we're inlining + String variableName = ((VariableDiceNode) node).getVariable(); + + // If we're inlining only certain variables, do so + if (specificInline) { + // Only inline the variable if we're supposed to + if (variableNames.contains(variableName)) { + // You can't inline non-existent variables + if (!enviroment.containsKey(variableName)) { + throw new UnsupportedOperationException( + "Attempted to inline non-existant variable " + + variableName); + } + + // Return the tree for the variable + return enviroment.get(variableName); + } + } else { + // You can't inline non-existent variables + if (!enviroment.containsKey(variableName)) { + throw new UnsupportedOperationException( + "Attempted to inline non-existant variable " + + variableName); + } + + // Return the tree for the variable + return enviroment.get(variableName); + } + + // return new Tree<>(node); + } + + /** + * Inline the specified variables in the AST + * + * @param ast + * The AST to inline variables into + * @param enviroment + * The enviroment to inline from + * @param variables + * The variables to inline + * @return The inlined AST + */ + public static ITree selectiveInline( + ITree ast, + IMap> enviroment, + IList variables) { + return selectiveInline(ast, enviroment, + variables.toArray(new String[0])); + } + + /** + * Inline the specified variables in the AST + * + * @param ast + * The AST to inline variables into + * @param enviroment + * The enviroment to inline from + * @param variables + * The variables to inline + * @return The inlined AST + */ + public static ITree selectiveInline( + ITree ast, + IMap> enviroment, + String... variables) { + // If we're selectively inlining, do so + if (variables != null && variables.length > 0) { + IList variableNames = new FunctionalList<>(variables); + + // Selectively inline each tree node + return ast.flatMapTree((node) -> { + return inlineNode(node, enviroment, true, variableNames); + }); + } + + // Inline everything in each node + return ast.flatMapTree((node) -> { + return inlineNode(node, enviroment, false, null); + }); + } +} diff --git a/dice-lang/src/bjc/dicelang/ast/DiceASTOptimizer.java b/dice-lang/src/bjc/dicelang/ast/DiceASTOptimizer.java new file mode 100644 index 0000000..d7fc23c --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/DiceASTOptimizer.java @@ -0,0 +1,60 @@ +package bjc.dicelang.ast; + +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; +import bjc.utils.funcdata.IMap; +import bjc.utils.funcdata.ITree; + +import bjc.dicelang.ast.nodes.IDiceASTNode; +import bjc.dicelang.ast.optimization.IOptimizationPass; + +/** + * Contains optimizations appliable to a dice AST + * + * @author ben + * + */ +public class DiceASTOptimizer { + private IList passes; + + /** + * Create a new optimizer + */ + public DiceASTOptimizer() { + passes = new FunctionalList<>(); + } + + /** + * Add a pass to the list of optimization passes + * + * @param pass + * The pass to add + */ + public void addPass(IOptimizationPass pass) { + passes.add(pass); + } + + /** + * Optimize the passed in tree + * + * @param ast + * The tree to optimize + * @param enviroment + * The enviroment for variable references + * @return The optimized tree + */ + public ITree optimizeTree(ITree ast, + IMap> enviroment) { + ITree optimizedTree = passes.reduceAux(ast, + (currentPass, currentTree) -> { + return currentTree.collapse(currentPass::optimizeLeaf, + (operator) -> { + return (nodes) -> { + return currentPass.optimizeOperator( + operator, nodes); + }; + }, (tree) -> tree); + }, (tree) -> tree); + return optimizedTree; + } +} diff --git a/dice-lang/src/bjc/dicelang/ast/DiceASTParser.java b/dice-lang/src/bjc/dicelang/ast/DiceASTParser.java new file mode 100644 index 0000000..9a36951 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/DiceASTParser.java @@ -0,0 +1,160 @@ +package bjc.dicelang.ast; + +import java.util.Deque; +import java.util.InputMismatchException; +import java.util.function.Function; +import java.util.function.Predicate; + +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.IList; +import bjc.utils.funcdata.IMap; +import bjc.utils.funcdata.ITree; +import bjc.utils.funcdata.Tree; +import bjc.utils.funcutils.StringUtils; +import bjc.utils.parserutils.TreeConstructor; + +import bjc.dicelang.IDiceExpression; +import bjc.dicelang.ast.nodes.DiceLiteralNode; +import bjc.dicelang.ast.nodes.DiceLiteralType; +import bjc.dicelang.ast.nodes.IDiceASTNode; +import bjc.dicelang.ast.nodes.ILiteralDiceNode; +import bjc.dicelang.ast.nodes.IntegerLiteralNode; +import bjc.dicelang.ast.nodes.OperatorDiceNode; +import bjc.dicelang.ast.nodes.VariableDiceNode; + +/** + * Parse a string expression into AST form. Doesn't do anything else + * + * @author ben + * + */ +public class DiceASTParser { + private static IDiceASTNode convertLeafNode(String leafNode) { + DiceLiteralType literalType = ILiteralDiceNode + .getLiteralType(leafNode); + + if (literalType != null) { + switch (literalType) { + case DICE: + return new DiceLiteralNode( + IDiceExpression.toExpression(leafNode)); + case INTEGER: + return new IntegerLiteralNode( + Integer.parseInt(leafNode)); + default: + throw new InputMismatchException( + "Cannot convert string '" + leafNode + + "' into a literal."); + } + } + + if (leafNode.matches("[+-]?\\d*\\.\\d+")) { + throw new InputMismatchException( + "Floating point literals are not supported"); + } + + return new VariableDiceNode(leafNode); + } + + private static IDiceASTNode convertOperatorNode(String operatorNode) { + try { + return OperatorDiceNode.fromString(operatorNode); + } catch (IllegalArgumentException iaex) { + InputMismatchException imex = new InputMismatchException( + "Attempted to parse invalid operator " + operatorNode); + + imex.initCause(iaex); + + throw imex; + } + } + + /** + * Create an AST from a list of tokens + * + * @param tokens + * The list of tokens to convert + * @return An AST built from the tokens + */ + public static ITree createFromString( + IList tokens) { + Predicate specialPicker = (operator) -> { + if (StringUtils.containsOnly(operator, "\\[")) { + return true; + } else if (StringUtils.containsOnly(operator, "\\]")) { + return true; + } + + return false; + }; + + IMap>, ITree>> operators = new FunctionalMap<>(); + + operators.put("[", (queuedTrees) -> { + Tree openArray = new Tree<>("["); + + return openArray; + }); + + operators.put("]", (queuedTrees) -> { + return parseCloseArray(queuedTrees); + }); + + ITree rawTokens = TreeConstructor.constructTree(tokens, + (token) -> { + return isOperatorNode(token); + }, specialPicker, operators::get); + + ITree tokenizedTree = rawTokens.rebuildTree( + DiceASTParser::convertLeafNode, + DiceASTParser::convertOperatorNode); + + return tokenizedTree; + } + + private static boolean isOperatorNode(String token) { + if (StringUtils.containsOnly(token, "\\[")) { + return true; + } else if (StringUtils.containsOnly(token, "\\]")) { + return true; + } + + if (token.equals("[]")) { + // This is a synthetic operator, constructed by [ and ] + return true; + } + + try { + OperatorDiceNode.fromString(token); + return true; + } catch (@SuppressWarnings("unused") IllegalArgumentException iaex) { + // We don't care about details + return false; + } + } + + private static ITree parseCloseArray( + Deque> queuedTrees) { + IList> children = new FunctionalList<>(); + + while (shouldContinuePopping(queuedTrees)) { + children.add(queuedTrees.pop()); + } + + queuedTrees.pop(); + + children.reverse(); + + ITree arrayTree = new Tree<>("[]", children); + + return arrayTree; + } + + private static boolean shouldContinuePopping( + Deque> queuedTrees) { + String peekToken = queuedTrees.peek().getHead(); + + return !peekToken.equals("["); + } +} diff --git a/dice-lang/src/bjc/dicelang/ast/DiceASTReferenceChecker.java b/dice-lang/src/bjc/dicelang/ast/DiceASTReferenceChecker.java new file mode 100644 index 0000000..34414c5 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/DiceASTReferenceChecker.java @@ -0,0 +1,61 @@ +package bjc.dicelang.ast; + +import java.util.function.Consumer; + +import bjc.utils.data.IHolder; + +import bjc.dicelang.ast.nodes.DiceASTType; +import bjc.dicelang.ast.nodes.IDiceASTNode; +import bjc.dicelang.ast.nodes.VariableDiceNode; + +/** + * Check if the specified node references a particular variable + * + * @author ben + * + */ +public final class DiceASTReferenceChecker + implements Consumer { + /** + * This is true if the specified node references the set variable + */ + private IHolder 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 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); + } + + return false; + } +} \ No newline at end of file diff --git a/dice-lang/src/bjc/dicelang/ast/DiceASTReferenceSanitizer.java b/dice-lang/src/bjc/dicelang/ast/DiceASTReferenceSanitizer.java new file mode 100644 index 0000000..d8f658e --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/DiceASTReferenceSanitizer.java @@ -0,0 +1,201 @@ +package bjc.dicelang.ast; + +import bjc.utils.data.IHolder; +import bjc.utils.data.Identity; +import bjc.utils.funcdata.IMap; +import bjc.utils.funcdata.ITree; +import bjc.utils.funcdata.TopDownTransformResult; +import bjc.utils.funcdata.Tree; + +import bjc.dicelang.ast.nodes.IDiceASTNode; +import bjc.dicelang.ast.nodes.OperatorDiceNode; +import bjc.dicelang.ast.nodes.VariableDiceNode; + +/** + * Sanitize the references in an AST so that a variable that refers to + * itself in its definition has the occurance of it replaced with its + * previous definition + * + * @author ben + * + */ +public class DiceASTReferenceSanitizer { + private static ITree doSanitize(ITree ast, + IMap> enviroment) { + if (ast.getChildrenCount() != 2) { + throw new UnsupportedOperationException( + "Assignment must have two arguments."); + } + + ITree nameTree = ast.getChild(0); + ITree valueTree = ast.getChild(1); + + if (!DiceASTUtils.containsSimpleVariable(nameTree)) { + if (nameTree.getHead() == OperatorDiceNode.ARRAY) { + IHolder allSimpleVariables = new Identity<>(true); + + nameTree.doForChildren((child) -> { + if (allSimpleVariables.getValue()) { + boolean isSimple = DiceASTUtils + .containsSimpleVariable(child); + + allSimpleVariables.replace(isSimple); + } + }); + + if (!allSimpleVariables.getValue()) { + throw new UnsupportedOperationException( + "Array assignment must be between variables and" + + " a expression/array of expressions"); + } + + if (valueTree.getHead() == OperatorDiceNode.ARRAY) { + if (nameTree.getChildrenCount() != valueTree + .getChildrenCount()) { + throw new UnsupportedOperationException( + "Array assignment between arrays must be" + + " between two arrays of equal length"); + } + } + } else { + throw new UnsupportedOperationException( + "Assignment must be between a variable and a expression"); + } + } + + if (nameTree.getHead() == OperatorDiceNode.ARRAY) { + if (valueTree.getHead() == OperatorDiceNode.ARRAY) { + IHolder childCounter = new Identity<>(0); + + ITree returnTree = new Tree<>( + OperatorDiceNode.ARRAY); + + nameTree.doForChildren((child) -> { + String variableName = child.transformHead((node) -> { + return ((VariableDiceNode) node).getVariable(); + }); + + ITree currentValue = valueTree + .getChild(childCounter.getValue()); + + ITree sanitizedSubtree = doSingleSanitize( + ast, enviroment, child, currentValue, + variableName); + + if (sanitizedSubtree == null) { + ITree oldTree = new Tree<>( + ast.getHead(), child, currentValue); + + returnTree.addChild(oldTree); + } else { + returnTree.addChild(sanitizedSubtree); + } + + childCounter.transform((count) -> count + 1); + }); + + return returnTree; + } + + ITree returnTree = new Tree<>( + OperatorDiceNode.ARRAY); + + nameTree.doForChildren((child) -> { + String variableName = child.transformHead( + (node) -> ((VariableDiceNode) node).getVariable()); + + ITree sanitizedChild = doSingleSanitize(ast, + enviroment, child, valueTree, variableName); + if (sanitizedChild == null) { + ITree oldTree = new Tree<>(ast.getHead(), + child, valueTree); + + returnTree.addChild(oldTree); + } else { + returnTree.addChild(sanitizedChild); + } + }); + + return returnTree; + } + + String variableName = nameTree.transformHead( + (node) -> ((VariableDiceNode) node).getVariable()); + + ITree sanitizedTree = doSingleSanitize(ast, + enviroment, nameTree, valueTree, variableName); + + if (sanitizedTree == null) { + return ast; + } + + return sanitizedTree; + } + + private static ITree doSingleSanitize( + ITree ast, + IMap> enviroment, + ITree nameTree, ITree valueTree, + String variableName) { + if (enviroment.containsKey(variableName)) { + // @ is a meta-variable standing for the left side of an + // assignment + ITree oldVal = enviroment.put("@", + enviroment.get(variableName)); + + // We should always inline out references to last, because it + // will always change + ITree inlinedValue = DiceASTInliner + .selectiveInline(valueTree, enviroment, variableName, + "last", "@"); + + if (oldVal != null) { + enviroment.put("@", oldVal); + } else { + enviroment.remove("@"); + } + + return new Tree<>(ast.getHead(), nameTree, inlinedValue); + } + + return null; + } + + /** + * Sanitize the references in an AST + * + * @param ast + * @param enviroment + * @return The sanitized AST + */ + public static ITree sanitize(ITree ast, + IMap> enviroment) { + return ast.topDownTransform( + DiceASTReferenceSanitizer::shouldSanitize, (subTree) -> { + return doSanitize(subTree, enviroment); + }); + } + + private static TopDownTransformResult shouldSanitize( + IDiceASTNode node) { + if (!node.isOperator()) { + return TopDownTransformResult.SKIP; + } + + switch (((OperatorDiceNode) node)) { + case ASSIGN: + return TopDownTransformResult.TRANSFORM; + case ARRAY: + case LET: + return TopDownTransformResult.PASSTHROUGH; + case ADD: + case COMPOUND: + case DIVIDE: + case GROUP: + case MULTIPLY: + case SUBTRACT: + default: + return TopDownTransformResult.SKIP; + } + } +} diff --git a/dice-lang/src/bjc/dicelang/ast/DiceASTUtils.java b/dice-lang/src/bjc/dicelang/ast/DiceASTUtils.java new file mode 100644 index 0000000..d98c8fe --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/DiceASTUtils.java @@ -0,0 +1,86 @@ +package bjc.dicelang.ast; + +import bjc.utils.funcdata.ITree; + +import bjc.dicelang.IDiceExpression; +import bjc.dicelang.ScalarDie; +import bjc.dicelang.ast.nodes.DiceASTType; +import bjc.dicelang.ast.nodes.DiceLiteralNode; +import bjc.dicelang.ast.nodes.IDiceASTNode; +import bjc.dicelang.ast.nodes.ILiteralDiceNode; +import bjc.dicelang.ast.nodes.IntegerLiteralNode; + +/** + * Functions that are useful when dealing with dice ASTs + * + * @author ben + * + */ +public class DiceASTUtils { + /** + * Check if a dice AST contains a simple variable reference + * + * @param nameTree + * The tree to check for a reference in + * @return Whether or not a dice AST contains a simple variable + * reference + */ + public static boolean containsSimpleVariable( + ITree nameTree) { + return nameTree.transformHead((nameNode) -> { + if (nameNode.getType() != DiceASTType.VARIABLE) { + return false; + } + + return true; + }); + } + + /** + * Convert an literal AST node to a dice expression, if possible. + * + * @param tree + * The node to convert in tree form + * @return The tree as a dice expression + * + * @throws ClassCastException + * if the head of the tree is not a literal (implements + * {@link ILiteralDiceNode}) + * @throws UnsupportedOperationException + * if the head of the tree is not optimizable + */ + public static IDiceExpression literalToExpression( + ITree tree) { + ILiteralDiceNode literalNode = (ILiteralDiceNode) tree.getHead(); + + switch (literalNode.getLiteralType()) { + case DICE: + return ((DiceLiteralNode) literalNode).getValue(); + case INTEGER: + return new ScalarDie( + ((IntegerLiteralNode) literalNode).getValue()); + default: + throw new UnsupportedOperationException( + "This type of literal isn't convertable to an expression"); + } + } + + /** + * Convert an literal AST node to an integer, if possible. + * + * @param tree + * The literal node to convert, as a tree + * @return The node as an integer + * + * @throws ClassCastException + * if the head of the tree is not a literal (implements + * {@link ILiteralDiceNode}) + * @throws UnsupportedOperationException + * if the head of the tree is not optimizable + */ + public static int literalToInteger(ITree tree) { + return tree.transformHead((node) -> { + return ((ILiteralDiceNode) node).optimize(); + }); + } +} diff --git a/dice-lang/src/bjc/dicelang/ast/DummyResult.java b/dice-lang/src/bjc/dicelang/ast/DummyResult.java new file mode 100644 index 0000000..a84bb7c --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/DummyResult.java @@ -0,0 +1,31 @@ +package bjc.dicelang.ast; + +public class DummyResult implements IResult { + /* + * The reason why this result is a dummy + */ + private String dummyData; + + public DummyResult(String data) { + dummyData = data; + } + + /** + * Get the data in this dummy + * + * @return The reason why this result is a dummy + */ + public String getData() { + return dummyData; + } + + @Override + public ResultType getType() { + return ResultType.DUMMY; + } + + @Override + public String toString() { + return "Dummy with reason " + dummyData; + } +} diff --git a/dice-lang/src/bjc/dicelang/ast/IOperatorCollapser.java b/dice-lang/src/bjc/dicelang/ast/IOperatorCollapser.java new file mode 100644 index 0000000..0efaca9 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/IOperatorCollapser.java @@ -0,0 +1,20 @@ +package bjc.dicelang.ast; + +import java.util.function.Function; + +import bjc.utils.data.IPair; +import bjc.utils.funcdata.IList; +import bjc.utils.funcdata.ITree; + +import bjc.dicelang.ast.nodes.IDiceASTNode; + +/** + * Alias for operator collapsers. Because 68-char types are too long + * + * @author ben + * + */ +public interface IOperatorCollapser extends + Function>>, IPair>> { + // Just an alias +} diff --git a/dice-lang/src/bjc/dicelang/ast/IResult.java b/dice-lang/src/bjc/dicelang/ast/IResult.java new file mode 100644 index 0000000..9a3f325 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/IResult.java @@ -0,0 +1,16 @@ +package bjc.dicelang.ast; + +/** + * Represents a result from an expression evaluation + * + * @author ben + * + */ +public interface IResult { + /** + * Get the type of this result + * + * @return The type of this result + */ + public ResultType getType(); +} diff --git a/dice-lang/src/bjc/dicelang/ast/IntegerResult.java b/dice-lang/src/bjc/dicelang/ast/IntegerResult.java new file mode 100644 index 0000000..ce61d38 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/IntegerResult.java @@ -0,0 +1,40 @@ +package bjc.dicelang.ast; + +/** + * Represents a integer-valued result + * + * @author ben + * + */ +public class IntegerResult implements IResult { + private int value; + + /** + * Create a new integer valued result + * + * @param val + * The value of the result + */ + public IntegerResult(int val) { + value = val; + } + + @Override + public ResultType getType() { + return ResultType.INTEGER; + } + + /** + * Get the value of this result + * + * @return The value of this result + */ + public int getValue() { + return value; + } + + @Override + public String toString() { + return Integer.toString(value); + } +} diff --git a/dice-lang/src/bjc/dicelang/ast/ResultType.java b/dice-lang/src/bjc/dicelang/ast/ResultType.java new file mode 100644 index 0000000..9e3b129 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/ResultType.java @@ -0,0 +1,22 @@ +package bjc.dicelang.ast; + +/** + * Represents the result of a computation + * + * @author ben + * + */ +public enum ResultType { + /** + * Represents a result that is equivalent to a single integer + */ + INTEGER, + /** + * Represents a result that is an array + */ + ARRAY, + /** + * Represents something not to poke at + */ + DUMMY +} diff --git a/dice-lang/src/bjc/dicelang/ast/nodes/DiceASTType.java b/dice-lang/src/bjc/dicelang/ast/nodes/DiceASTType.java new file mode 100644 index 0000000..9feb461 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/nodes/DiceASTType.java @@ -0,0 +1,27 @@ +package bjc.dicelang.ast.nodes; + +/** + * An enum to represent the type of node an AST node is + * + * @author ben + * + */ +public enum DiceASTType { + /** + * A node that contains a literal value + */ + LITERAL, + /** + * A node that contains an operator expression + */ + OPERATOR, + /** + * A node that contains a variable reference + */ + VARIABLE; + + @Override + public String toString() { + return this.name().toLowerCase(); + } +} \ No newline at end of file diff --git a/dice-lang/src/bjc/dicelang/ast/nodes/DiceLiteralNode.java b/dice-lang/src/bjc/dicelang/ast/nodes/DiceLiteralNode.java new file mode 100644 index 0000000..b398ac6 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/nodes/DiceLiteralNode.java @@ -0,0 +1,52 @@ +package bjc.dicelang.ast.nodes; + +import bjc.dicelang.IDiceExpression; + +/** + * Represents a literal backed by a dice expression + * + * @author ben + * + */ +public class DiceLiteralNode implements ILiteralDiceNode { + private IDiceExpression expression; + + /** + * Create a new literal from an expression + * + * @param exp + * The expression to attempt to create a literal from + */ + public DiceLiteralNode(IDiceExpression exp) { + expression = exp; + } + + @Override + public boolean canOptimize() { + return expression.canOptimize(); + } + + @Override + public DiceLiteralType getLiteralType() { + return DiceLiteralType.DICE; + } + + /** + * Return the expression being represented + * + * @return The expression being represented + */ + public IDiceExpression getValue() { + return expression; + } + + @Override + public int optimize() { + return expression.optimize(); + } + + @Override + public String toString() { + return expression.toString(); + } +} diff --git a/dice-lang/src/bjc/dicelang/ast/nodes/DiceLiteralType.java b/dice-lang/src/bjc/dicelang/ast/nodes/DiceLiteralType.java new file mode 100644 index 0000000..41c6b05 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/nodes/DiceLiteralType.java @@ -0,0 +1,18 @@ +package bjc.dicelang.ast.nodes; + +/** + * Represents the type of literals that can be in an AST + * + * @author ben + * + */ +public enum DiceLiteralType { + /** + * Represents a integral constant + */ + INTEGER, + /** + * Represents a dice literal + */ + DICE; +} diff --git a/dice-lang/src/bjc/dicelang/ast/nodes/DiceOperatorType.java b/dice-lang/src/bjc/dicelang/ast/nodes/DiceOperatorType.java new file mode 100644 index 0000000..7cc1e42 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/nodes/DiceOperatorType.java @@ -0,0 +1,29 @@ +package bjc.dicelang.ast.nodes; + +/** + * Represents the different type of operators. + * + * Mostly, what distinguishes groups is that all the operators in a group + * have similiar precedence, and operate on similiar things + * + * @author ben + * + */ +public enum DiceOperatorType { + /** + * Represents operators that do math operations + */ + MATH, + /** + * Represents operators that do things with arrays + */ + ARRAY, + /** + * Represents operators that do things with dice + */ + DICE, + /** + * Represents operators that do things with expressions + */ + EXPRESSION; +} diff --git a/dice-lang/src/bjc/dicelang/ast/nodes/IDiceASTNode.java b/dice-lang/src/bjc/dicelang/ast/nodes/IDiceASTNode.java new file mode 100644 index 0000000..b7bf9a6 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/nodes/IDiceASTNode.java @@ -0,0 +1,23 @@ +package bjc.dicelang.ast.nodes; + +/** + * The interface for a node in a dice AST + * + * @author ben + * + */ +public interface IDiceASTNode { + /** + * Get the type of AST node this node is + * + * @return The type of AST node this AST node is + */ + public DiceASTType getType(); + + /** + * Check if this node represents an operator or not + * + * @return Whether or not this node represents an operator + */ + public boolean isOperator(); +} \ No newline at end of file diff --git a/dice-lang/src/bjc/dicelang/ast/nodes/ILiteralDiceNode.java b/dice-lang/src/bjc/dicelang/ast/nodes/ILiteralDiceNode.java new file mode 100644 index 0000000..b94bcc8 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/nodes/ILiteralDiceNode.java @@ -0,0 +1,73 @@ +package bjc.dicelang.ast.nodes; + +/** + * Represents a literal of some type in the AST + * + * @author ben + * + */ +public interface ILiteralDiceNode extends IDiceASTNode { + /** + * Check if a token represents a literal, and if so, what type + * + * @param tok + * The token to check + * @return The type the literal would be if it is one, or null + * otherwise + */ + static DiceLiteralType getLiteralType(String tok) { + String diceGroupOrNumber = "[(?:\\d*d\\d+)(?:\\d+)]"; + + if (tok.matches("\\A" + diceGroupOrNumber + "?" + "c" + + diceGroupOrNumber + "\\Z")) { + return DiceLiteralType.DICE; + } + + String diceGroup = "\\d*d\\d+\\"; + + if (tok.matches("\\A" + diceGroup + "Z")) { + return DiceLiteralType.DICE; + } + + try { + Integer.parseInt(tok); + return DiceLiteralType.INTEGER; + } catch (@SuppressWarnings("unused") NumberFormatException nfex) { + // We don't care about details + return null; + } + } + + /** + * Check if this node can be optimized to a constant + * + * @return Whether or not this node can be optimized to a constant + * @see bjc.dicelang.IDiceExpression#canOptimize() + */ + boolean canOptimize(); + + /** + * Get the type of literal this node represents + * + * @return The type of literal this node represents + */ + DiceLiteralType getLiteralType(); + + @Override + default DiceASTType getType() { + return DiceASTType.LITERAL; + } + + @Override + default boolean isOperator() { + return false; + } + + /** + * Optimize this node to a constant if possible + * + * @return This node in constant form if possible + * @see bjc.dicelang.IDiceExpression#optimize() + */ + int optimize(); +} diff --git a/dice-lang/src/bjc/dicelang/ast/nodes/IntegerLiteralNode.java b/dice-lang/src/bjc/dicelang/ast/nodes/IntegerLiteralNode.java new file mode 100644 index 0000000..3d43bb1 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/nodes/IntegerLiteralNode.java @@ -0,0 +1,50 @@ +package bjc.dicelang.ast.nodes; + +/** + * Represents an integer literal of some kind + * + * @author ben + * + */ +public class IntegerLiteralNode implements ILiteralDiceNode { + private int value; + + /** + * Create a new integer literal from the given number + * + * @param val + * The value this node represents + */ + public IntegerLiteralNode(int val) { + value = val; + } + + @Override + public boolean canOptimize() { + return true; + } + + @Override + public DiceLiteralType getLiteralType() { + return DiceLiteralType.INTEGER; + } + + /** + * Get the value this node represents + * + * @return The integer value of this node + */ + public int getValue() { + return value; + } + + @Override + public int optimize() { + return value; + } + + @Override + public String toString() { + return Integer.toString(value); + } +} diff --git a/dice-lang/src/bjc/dicelang/ast/nodes/OperatorDiceNode.java b/dice-lang/src/bjc/dicelang/ast/nodes/OperatorDiceNode.java new file mode 100644 index 0000000..7c0a29d --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/nodes/OperatorDiceNode.java @@ -0,0 +1,110 @@ +package bjc.dicelang.ast.nodes; + +import static bjc.dicelang.ast.nodes.DiceOperatorType.DICE; +import static bjc.dicelang.ast.nodes.DiceOperatorType.EXPRESSION; +import static bjc.dicelang.ast.nodes.DiceOperatorType.MATH; + +/** + * A node that represents an operator + * + * @author ben + * + */ +public enum OperatorDiceNode implements IDiceASTNode { + /** + * Represents adding two nodes + */ + ADD(MATH), + /** + * Represents dividing two nodes + */ + DIVIDE(MATH), + /** + * Represents multiplying two nodes + */ + MULTIPLY(MATH), + /** + * Represents subtracting two nodes + */ + SUBTRACT(MATH), + /** + * Representings combining two node values together + */ + COMPOUND(DICE), + /** + * Represents using one node a variable number of times + */ + GROUP(DICE), + /** + * Represents constructing an array from a sequence of expressions + */ + ARRAY(DiceOperatorType.ARRAY), + /** + * Represents assigning one node to another + */ + ASSIGN(EXPRESSION), + /** + * Represents evaluating one expression in the context of another + */ + LET(EXPRESSION); + + /** + * 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": + case "group": + return GROUP; + case "c": + case "compound": + return COMPOUND; + case "=>": + return LET; + case "[]": + return ARRAY; + default: + throw new IllegalArgumentException( + s + " is not a valid operator node"); + } + } + + /** + * Represents the group of operator this operator is sorted into. + * + */ + public final DiceOperatorType type; + + private OperatorDiceNode(DiceOperatorType ty) { + type = ty; + } + + @Override + public DiceASTType getType() { + return DiceASTType.OPERATOR; + } + + /* + * (non-Javadoc) + * + * @see bjc.utils.dice.ast.IDiceASTNode#isOperator() + */ + @Override + public boolean isOperator() { + return true; + } +} diff --git a/dice-lang/src/bjc/dicelang/ast/nodes/VariableDiceNode.java b/dice-lang/src/bjc/dicelang/ast/nodes/VariableDiceNode.java new file mode 100644 index 0000000..da66608 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/nodes/VariableDiceNode.java @@ -0,0 +1,101 @@ +package bjc.dicelang.ast.nodes; + +/** + * A node that represents a reference to a variable + * + * @author ben + * + */ +public class VariableDiceNode implements IDiceASTNode { + /** + * The variable referenced by this node + */ + private String variableName; + + /** + * Create a new node representing the specified variable + * + * @param varName + * The name of the variable being referenced + */ + public VariableDiceNode(String varName) { + this.variableName = varName; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + // Handle special cases + if (this == obj) { + return true; + } else if (obj == null) { + return false; + } else if (getClass() != obj.getClass()) { + return false; + } else { + VariableDiceNode other = (VariableDiceNode) obj; + + if (variableName == null) { + if (other.variableName != null) { + return false; + } + } else if (!variableName.equals(other.variableName)) { + return false; + } + + return true; + } + } + + @Override + public DiceASTType getType() { + return DiceASTType.VARIABLE; + } + + /** + * Get the variable referenced by this AST node + * + * @return the variable referenced by this AST node + */ + public String getVariable() { + return variableName; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((variableName == null) ? 0 : variableName.hashCode()); + return result; + } + + /* + * (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 variableName; + } +} \ No newline at end of file diff --git a/dice-lang/src/bjc/dicelang/ast/nodes/package-info.java b/dice-lang/src/bjc/dicelang/ast/nodes/package-info.java new file mode 100644 index 0000000..f0f7366 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/nodes/package-info.java @@ -0,0 +1,7 @@ +/** + * Classes for nodes in the dice-lang AST + * + * @author ben + * + */ +package bjc.dicelang.ast.nodes; \ No newline at end of file diff --git a/dice-lang/src/bjc/dicelang/ast/optimization/ArithmeticCollapser.java b/dice-lang/src/bjc/dicelang/ast/optimization/ArithmeticCollapser.java new file mode 100644 index 0000000..960fbf7 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/optimization/ArithmeticCollapser.java @@ -0,0 +1,50 @@ +package bjc.dicelang.ast.optimization; + +import java.util.function.BinaryOperator; + +import bjc.utils.funcdata.IList; +import bjc.utils.funcdata.ITree; +import bjc.utils.funcdata.Tree; + +import bjc.dicelang.ast.DiceASTUtils; +import bjc.dicelang.ast.nodes.DiceASTType; +import bjc.dicelang.ast.nodes.IDiceASTNode; +import bjc.dicelang.ast.nodes.ILiteralDiceNode; +import bjc.dicelang.ast.nodes.IntegerLiteralNode; +import bjc.dicelang.ast.nodes.OperatorDiceNode; + +class ArithmeticCollapser { + private BinaryOperator reducer; + private OperatorDiceNode type; + + public ArithmeticCollapser(BinaryOperator reducr, + OperatorDiceNode typ) { + reducer = reducr; + this.type = typ; + } + + public ITree collapse( + IList> children) { + boolean allConstant = children.allMatch((subtree) -> { + return subtree.transformHead((node) -> { + if (node.getType() == DiceASTType.LITERAL) { + return ((ILiteralDiceNode) node).canOptimize(); + } + + return false; + }); + }); + + if (!allConstant) { + return new Tree<>(type, children); + } + + int initState = DiceASTUtils.literalToInteger(children.first()); + + return children.tail().reduceAux(initState, + (currentNode, state) -> { + return reducer.apply(state, + DiceASTUtils.literalToInteger(currentNode)); + }, (state) -> new Tree<>(new IntegerLiteralNode(state))); + } +} diff --git a/dice-lang/src/bjc/dicelang/ast/optimization/ConstantCollapser.java b/dice-lang/src/bjc/dicelang/ast/optimization/ConstantCollapser.java new file mode 100644 index 0000000..95badd2 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/optimization/ConstantCollapser.java @@ -0,0 +1,91 @@ +package bjc.dicelang.ast.optimization; + +import bjc.utils.funcdata.IList; +import bjc.utils.funcdata.ITree; +import bjc.utils.funcdata.Tree; + +import bjc.dicelang.ComplexDice; +import bjc.dicelang.ast.DiceASTUtils; +import bjc.dicelang.ast.nodes.IDiceASTNode; +import bjc.dicelang.ast.nodes.IntegerLiteralNode; +import bjc.dicelang.ast.nodes.OperatorDiceNode; + +/** + * Collapses operations with constants to constants + * + * @author ben + * + */ +public class ConstantCollapser implements IOptimizationPass { + private static final ArithmeticCollapser additionCollapser = new ArithmeticCollapser( + (left, right) -> left + right, OperatorDiceNode.ADD); + + private static final ArithmeticCollapser divideCollapser = new ArithmeticCollapser( + (left, right) -> left / right, OperatorDiceNode.DIVIDE); + + private static final ArithmeticCollapser multiplyCollapser = new ArithmeticCollapser( + (left, right) -> left * right, OperatorDiceNode.MULTIPLY); + + private static final ArithmeticCollapser subtractCollapser = new ArithmeticCollapser( + (left, right) -> left - right, OperatorDiceNode.SUBTRACT); + + private static final ArithmeticCollapser compoundCollapser = new ArithmeticCollapser( + (left, right) -> Integer.parseInt( + Integer.toString(left) + Integer.toString(left)), + OperatorDiceNode.COMPOUND); + + @Override + public ITree optimizeLeaf(IDiceASTNode leafNode) { + // We don't do anything special here + return new Tree<>(leafNode); + } + + @Override + public ITree optimizeOperator(IDiceASTNode operator, + IList> children) { + if (!operator.isOperator()) { + return new Tree<>(operator, children); + } + + switch ((OperatorDiceNode) operator) { + case ADD: + return additionCollapser.collapse(children); + case DIVIDE: + return divideCollapser.collapse(children); + case MULTIPLY: + return multiplyCollapser.collapse(children); + case SUBTRACT: + return subtractCollapser.collapse(children); + case COMPOUND: + return compoundCollapser.collapse(children); + case GROUP: + if (children.getSize() != 2) { + return new Tree<>(operator, children); + } + + ComplexDice dice = new ComplexDice( + DiceASTUtils.literalToExpression( + children.getByIndex(0)), + DiceASTUtils.literalToExpression( + children.getByIndex(1))); + + if (dice.canOptimize()) { + return new Tree<>( + new IntegerLiteralNode(dice.optimize())); + } + + return new Tree<>(operator, children); + case ARRAY: + if (children.getSize() != 1) { + return new Tree<>(operator, children); + } + + return children.first(); + case ASSIGN: + case LET: + default: + // We don't optimize these operators + return new Tree<>(operator, children); + } + } +} diff --git a/dice-lang/src/bjc/dicelang/ast/optimization/IOptimizationPass.java b/dice-lang/src/bjc/dicelang/ast/optimization/IOptimizationPass.java new file mode 100644 index 0000000..36b03f1 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/optimization/IOptimizationPass.java @@ -0,0 +1,35 @@ +package bjc.dicelang.ast.optimization; + +import bjc.utils.funcdata.IList; +import bjc.utils.funcdata.ITree; + +import bjc.dicelang.ast.nodes.IDiceASTNode; + +/** + * Represents a pass of optimizations over a dice AST + * + * @author ben + * + */ +public interface IOptimizationPass { + /** + * Optimize a leaf in the tree + * + * @param leafNode + * The node to optimize + * @return The optimized node + */ + public ITree optimizeLeaf(IDiceASTNode leafNode); + + /** + * Optimize an operator in an AST node + * + * @param operator + * The operator being optimized + * @param children + * The children of the operator being optimized + * @return The optimized node + */ + public ITree optimizeOperator(IDiceASTNode operator, + IList> children); +} diff --git a/dice-lang/src/bjc/dicelang/ast/optimization/OperationCondenser.java b/dice-lang/src/bjc/dicelang/ast/optimization/OperationCondenser.java new file mode 100644 index 0000000..f646a17 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/optimization/OperationCondenser.java @@ -0,0 +1,107 @@ +package bjc.dicelang.ast.optimization; + +import bjc.utils.data.IHolder; +import bjc.utils.data.Identity; +import bjc.utils.funcdata.ITree; +import bjc.utils.funcdata.TopDownTransformResult; +import bjc.utils.funcdata.Tree; + +import bjc.dicelang.ast.nodes.DiceASTType; +import bjc.dicelang.ast.nodes.IDiceASTNode; +import bjc.dicelang.ast.nodes.OperatorDiceNode; + +/** + * Condenses chained operations into a single level + * + * @author ben + * + */ +public class OperationCondenser { + /** + * Condense chained similiar operations into a single level + * + * @param ast + * The AST to condense + * @return The condensed AST + */ + public static ITree condense(ITree ast) { + return ast.topDownTransform(OperationCondenser::pickNode, + OperationCondenser::doCondense); + } + + private static ITree doCondense( + ITree ast) { + OperatorDiceNode operation = ast + .transformHead((node) -> (OperatorDiceNode) node); + + IHolder canCondense = new Identity<>(true); + + ast.doForChildren((child) -> { + if (canCondense.getValue()) { + canCondense.replace(child.transformHead((node) -> { + if (node.getType() == DiceASTType.OPERATOR) { + if (operation.equals(node)) { + return true; + } + + return false; + } + + return true; + })); + } + }); + + if (!canCondense.getValue()) { + return ast; + } + + ITree condensedAST = new Tree<>(operation); + + ast.doForChildren((child) -> { + if (child.getHead().getType() == DiceASTType.OPERATOR) { + child.doForChildren((subChild) -> { + condensedAST.addChild(subChild); + }); + } else { + condensedAST.addChild(child); + } + }); + + return condensedAST; + } + + private static TopDownTransformResult pickNode(IDiceASTNode node) { + switch (node.getType()) { + case LITERAL: + return TopDownTransformResult.SKIP; + case OPERATOR: + return pickOperator((OperatorDiceNode) node); + case VARIABLE: + return TopDownTransformResult.SKIP; + default: + throw new UnsupportedOperationException( + "Attempted to traverse unknown node type " + node); + } + } + + private static TopDownTransformResult pickOperator( + OperatorDiceNode node) { + switch (node) { + case ADD: + case MULTIPLY: + case SUBTRACT: + case DIVIDE: + case COMPOUND: + return TopDownTransformResult.PUSHDOWN; + case ARRAY: + case ASSIGN: + case GROUP: + case LET: + return TopDownTransformResult.PASSTHROUGH; + default: + throw new UnsupportedOperationException( + "Attempted to traverse unknown operator " + node); + } + } +} diff --git a/dice-lang/src/bjc/dicelang/ast/optimization/package-info.java b/dice-lang/src/bjc/dicelang/ast/optimization/package-info.java new file mode 100644 index 0000000..6f75bf9 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/optimization/package-info.java @@ -0,0 +1,7 @@ +/** + * Contains optimizations on dice ASTs + * + * @author ben + * + */ +package bjc.dicelang.ast.optimization; \ No newline at end of file diff --git a/dice-lang/src/bjc/dicelang/ast/package-info.java b/dice-lang/src/bjc/dicelang/ast/package-info.java new file mode 100644 index 0000000..f6352aa --- /dev/null +++ b/dice-lang/src/bjc/dicelang/ast/package-info.java @@ -0,0 +1,7 @@ +/** + * New implementation of AST for dice-lang + * + * @author ben + * + */ +package bjc.dicelang.ast; \ No newline at end of file diff --git a/dice-lang/src/bjc/dicelang/examples/DiceASTLanguageTest.java b/dice-lang/src/bjc/dicelang/examples/DiceASTLanguageTest.java new file mode 100644 index 0000000..fa92fd2 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/examples/DiceASTLanguageTest.java @@ -0,0 +1,261 @@ +package bjc.dicelang.examples; + +import java.util.InputMismatchException; +import java.util.Scanner; + +import bjc.utils.data.ITree; +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.FunctionalStringTokenizer; +import bjc.utils.funcdata.IList; +import bjc.utils.funcdata.IMap; + +import bjc.dicelang.ast.DiceASTEvaluator; +import bjc.dicelang.ast.DiceASTInliner; +import bjc.dicelang.ast.DiceASTOptimizer; +import bjc.dicelang.ast.DiceASTParser; +import bjc.dicelang.ast.DiceASTReferenceSanitizer; +import bjc.dicelang.ast.IResult; +import bjc.dicelang.ast.nodes.IDiceASTNode; +import bjc.dicelang.ast.optimization.ConstantCollapser; +import bjc.dicelang.ast.optimization.OperationCondenser; + +/** + * Test interface for AST-based dice language + * + * @author ben + * + */ +public class DiceASTLanguageTest { + private static IMap actions; + + private static DiceASTOptimizer optimizer; + + // Set up things that need to be configured + static { + actions = new FunctionalMap<>(); + + // Inline all the variables in a given expression + actions.put("inline", DiceASTLanguageTest::handleInlineAction); + + // Print out the enviroment + actions.put("env", (tokenizer, enviroment) -> { + enviroment.forEach((varName, varValue) -> { + System.out.println(varName + " is bound to " + varValue); + }); + }); + + // Create and configure the optimizer + optimizer = new DiceASTOptimizer(); + + optimizer.addPass(new ConstantCollapser()); + } + + // Read in a command + private static String getNextCommand(Scanner inputSource, + int commandNumber) { + // Print a prompt using the current command number + System.out.print("\ndice-lang-" + commandNumber + "> "); + + // Read in the next command + return inputSource.nextLine(); + } + + private static void handleInlineAction( + FunctionalStringTokenizer tokenizer, + IMap> enviroment) { + // Skip the pragma name + tokenizer.nextToken(); + + // Get the pragma arguments + IList pragmaArgs = tokenizer.toList(); + + if (pragmaArgs.getSize() < 3) { + // Complain about pragma arguments not being valid + System.err.println( + "ERROR: Inline requires at least 3 parameters. They are:" + + "\n\t1. The name of the expression to inline." + + "\n\t2. The name of the variable to bind the result to." + + "\n\t3 and onwards. Names of variables to inline in the expression."); + } else { + // Get arguments + String inlineExpression = pragmaArgs.getByIndex(0); + String variableName = pragmaArgs.getByIndex(1); + + // Grab the variables we want to inline + IList inlinedVariables = pragmaArgs.tail().tail(); + + // Actually inline the variable + ITree inlinedExpression = DiceASTInliner + .selectiveInline(enviroment.get(inlineExpression), + enviroment, inlinedVariables); + + // Stick the inlined variable into the enviroment + enviroment.put(variableName, inlinedExpression); + } + } + + /** + * Main method of class + * + * @param args + * Unused CLI args + */ + public static void main(String[] args) { + // Prepare the things we need for input + Scanner inputSource = new Scanner(System.in); + int commandNumber = 0; + + // Grab the initial command + String currentLine = getNextCommand(inputSource, commandNumber); + + // The enviroment for variables + IMap> enviroment = new FunctionalMap<>(); + + // Handle commands + while (!currentLine.equalsIgnoreCase("quit")) { + // Get the name of a possible action + String possibleActionName = currentLine.split(" ")[0]; + + // Check and see if we're executing an action + if (actions.containsKey(possibleActionName)) { + // Execute action + FunctionalStringTokenizer tokenizer = new FunctionalStringTokenizer( + currentLine); + + // Execute the action + actions.get(possibleActionName).accept(tokenizer, + enviroment); + + // Get the next command + currentLine = getNextCommand(inputSource, commandNumber); + + continue; + } + + // The AST we are going to build + ITree builtAST; + + // Time command preparation + long time = System.nanoTime(); + + // Prepare the command + IList preparedTokens = DiceExpressionPreparer + .prepareCommand(currentLine); + + System.out.println("Command prepared in " + + (double) (System.nanoTime() - time) / 1000000000 + + " s"); + + try { + // Time the AST creation + time = System.nanoTime(); + + // Create the AST + builtAST = DiceASTParser.createFromString(preparedTokens); + + System.out + .println( + "Command parsed in " + + (double) (System.nanoTime() + - time) / 1000000000 + + " s"); + } catch (InputMismatchException | IllegalStateException + | UnsupportedOperationException ex) { + // Tell the user there was an error in parsing + System.out.println("PARSING ERROR: " + ex.getLocalizedMessage()); + + // Move onto the next command + currentLine = getNextCommand(inputSource, commandNumber); + + continue; + } + + // Print out parsed AST + System.out.println("\tParsed: " + builtAST.toString()); + + // Time AST transformation + time = System.nanoTime(); + + // Transform the AST + ITree transformedAST = transformAST(builtAST, + enviroment); + + System.out.println("Command transformed in " + + (double) (System.nanoTime() - time) / 1000000000 + + " s"); + + // Print out the transformed AST + System.out + .println("\tTransformed: " + transformedAST.toString()); + + + try { + // Time the evaluation + time = System.nanoTime(); + + // Evaluate the expression once + IResult sampleResult = DiceASTEvaluator.evaluateAST(transformedAST, + enviroment); + + System.out + .println( + "Command evaluated in " + + (double) (System.nanoTime() + - time) / 1000000000 + + " s"); + + // Print out the result of evaluating the expression + System.out.println("\t\tSample Result: " + sampleResult); + + // Update the 'last' meta-variable + enviroment.put("last", transformedAST); + } catch (UnsupportedOperationException usex) { + // Tell the user there was an error in evaluation + System.out.println("EVALUATION ERROR: " + usex.getLocalizedMessage()); + + // Get the next command + currentLine = getNextCommand(inputSource, commandNumber); + + // Process it + continue; + } + + + // Increase the number of commands + commandNumber++; + + // Get the next command + currentLine = getNextCommand(inputSource, commandNumber); + } + + System.out.println("Bye."); + + // Cleanup after ourselves + inputSource.close(); + } + + // Transform a parsed AST + private static ITree transformAST( + ITree builtAST, + IMap> enviroment) { + // Optimize the tree first + ITree optimizedTree = optimizer + .optimizeTree(builtAST, enviroment); + + // Then, condense unnecessary operations + ITree condensedTree = OperationCondenser + .condense(optimizedTree); + + // Next, sanitize references + ITree sanitizedTree = DiceASTReferenceSanitizer + .sanitize(condensedTree, enviroment); + + // Re-optimize the sanitized & condensed tree + optimizedTree = optimizer.optimizeTree(sanitizedTree, enviroment); + + // Re-condense the newly optimized tree + condensedTree = OperationCondenser.condense(optimizedTree); + + return condensedTree; + } +} diff --git a/dice-lang/src/bjc/dicelang/examples/DiceASTPragma.java b/dice-lang/src/bjc/dicelang/examples/DiceASTPragma.java new file mode 100644 index 0000000..4900e46 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/examples/DiceASTPragma.java @@ -0,0 +1,22 @@ +package bjc.dicelang.examples; + +import java.util.function.BiConsumer; + +import bjc.utils.funcdata.FunctionalStringTokenizer; +import bjc.utils.funcdata.IMap; +import bjc.utils.funcdata.ITree; + +import bjc.dicelang.ast.nodes.IDiceASTNode; + +/** + * Alias for the type of a 'pragma' or special language command + * + * To explain it, a pragma is a function that takes a tokenizer with the rest + * of the line, and an enviroment that contains variable bindings + * @author ben + * + */ +public interface DiceASTPragma extends + BiConsumer>> { + // Just an alias +} diff --git a/dice-lang/src/bjc/dicelang/examples/DiceExpressionParserTest.java b/dice-lang/src/bjc/dicelang/examples/DiceExpressionParserTest.java new file mode 100644 index 0000000..b31f3a0 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/examples/DiceExpressionParserTest.java @@ -0,0 +1,59 @@ +package bjc.dicelang.examples; + +import java.util.HashMap; +import java.util.Scanner; + +import bjc.dicelang.DiceExpressionParser; +import bjc.dicelang.IDiceExpression; + +/** + * Driver class for testing expression parser + * + * @author ben + * + */ +public class DiceExpressionParserTest { + /** + * Run the parser test + * + * @param args + * Unused CLI arguments + */ + public static void main(String[] args) { + /* + * Get a scanner for input + */ + Scanner scn = new Scanner(System.in); + + /* + * Ask to enter a expression + */ + System.out.print("Enter dice expression: "); + + String exp = scn.nextLine(); + + /* + * Enter amount of times to roll an expression + */ + System.out.print("Enter number of times to roll: "); + + int nTimes = Integer.parseInt(scn.nextLine()); + + IDiceExpression dexp = DiceExpressionParser.parse(exp, + new HashMap<>()); + + /* + * Roll the dice a specified amount of times + */ + for (int i = 1; i <= nTimes; i++) { + int roll = dexp.roll(); + + System.out.println("Rolled " + roll); + } + + /* + * Clean up after ourselves + */ + scn.close(); + } +} diff --git a/dice-lang/src/bjc/dicelang/examples/DiceExpressionPreparer.java b/dice-lang/src/bjc/dicelang/examples/DiceExpressionPreparer.java new file mode 100644 index 0000000..5488b5d --- /dev/null +++ b/dice-lang/src/bjc/dicelang/examples/DiceExpressionPreparer.java @@ -0,0 +1,93 @@ +package bjc.dicelang.examples; + +import java.util.Deque; +import java.util.LinkedList; + +import bjc.utils.data.IPair; +import bjc.utils.data.Pair; +import bjc.utils.funcdata.FunctionalStringTokenizer; +import bjc.utils.funcdata.IList; +import bjc.utils.funcutils.ListUtils; +import bjc.utils.parserutils.ShuntingYard; + +/** + * Prepare a dice expression to be parsed + * + * @author ben + * + */ +public class DiceExpressionPreparer { + /** + * The yard to use for shunting expressions + */ + private static ShuntingYard yard; + + private static final int MATH_PREC = 20; + private static final int DICE_PREC = 10; + private static final int EXPR_PREC = 0; + + // Do initialization for all parsers + static { + // The shunter we're going to use + yard = new ShuntingYard<>(false); + + // Configure the shunters operators + // Basic mathematical operators + yard.addOp("+", 0 + MATH_PREC); + yard.addOp("-", 0 + MATH_PREC); + + yard.addOp("*", 1 + MATH_PREC); + yard.addOp("/", 1 + MATH_PREC); + + yard.addOp("d", 0 + DICE_PREC); // dice operator: use for creating + // variable size dice groups + yard.addOp("c", 1 + DICE_PREC); // compound operator: use for + // creating compound dice from expressions + + yard.addOp("=>", 0 + EXPR_PREC); // let operator: evaluate an + // expression in the context of another + yard.addOp(":=", 1 + EXPR_PREC); // binding operator: Bind a name + // to a variable expression + } + + // Prepare a command, turning raw tokens into input for the tree builder + public static IList prepareCommand(String currentLine) { + // Split the command into tokens + IList tokens = FunctionalStringTokenizer + .fromString(currentLine).toList(); + + // The linked list to use for handling tokens + Deque> ops = new LinkedList<>(); + + // Prepare the list for operator expansion + ops.add(new Pair<>("+", "\\+")); + ops.add(new Pair<>("-", "-")); + ops.add(new Pair<>("*", "\\*")); + ops.add(new Pair<>("/", "/")); + ops.add(new Pair<>(":=", ":=")); + ops.add(new Pair<>("=>", "=>")); + + // Expand infix single tokens to multiple infix tokens + IList semiExpandedTokens = ListUtils.splitTokens(tokens, + ops); + + // Reinitialize the list + ops = new LinkedList<>(); + + // Prepare the list for deaffixation + ops.add(new Pair<>("(", "\\(")); + ops.add(new Pair<>(")", "\\)")); + ops.add(new Pair<>("[", "\\[")); + ops.add(new Pair<>("]", "\\]")); + + // Deaffix ('s and ['s from tokens + IList fullyExpandedTokens = ListUtils + .deAffixTokens(semiExpandedTokens, ops); + + // Remove blank tokens + fullyExpandedTokens.removeIf((strang) -> strang.equals("")); + + // Shunt the tokens, and hand them back + return yard.postfix(fullyExpandedTokens, (token) -> token); + } +} diff --git a/dice-lang/src/bjc/dicelang/examples/DiceLanguageState.java b/dice-lang/src/bjc/dicelang/examples/DiceLanguageState.java new file mode 100644 index 0000000..fbb103f --- /dev/null +++ b/dice-lang/src/bjc/dicelang/examples/DiceLanguageState.java @@ -0,0 +1,37 @@ +package bjc.dicelang.examples; + +import java.util.Map; + +import bjc.utils.data.Pair; + +import bjc.dicelang.DiceExpressionParser; +import bjc.dicelang.IDiceExpression; + +/** + * Internal state of dice language + * + * @author ben + * + */ +public class DiceLanguageState + extends Pair> { + + /** + * Create a new state + */ + public DiceLanguageState() { + } + + /** + * Create a new state with the desired parameters + * + * @param left + * The parser to use + * @param right + * The enviroment to use + */ + public DiceLanguageState(DiceExpressionParser left, + Map right) { + super(left, right); + } +} diff --git a/dice-lang/src/bjc/dicelang/examples/DiceLanguageTest.java b/dice-lang/src/bjc/dicelang/examples/DiceLanguageTest.java new file mode 100644 index 0000000..bd6270e --- /dev/null +++ b/dice-lang/src/bjc/dicelang/examples/DiceLanguageTest.java @@ -0,0 +1,96 @@ +package bjc.dicelang.examples; + +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; +import java.util.function.BiConsumer; + +import bjc.dicelang.DiceExpressionParser; +import bjc.dicelang.IDiceExpression; + +/** + * Test of dice language + * + * @author ben + * + */ +public class DiceLanguageTest { + private static Map> acts; + + static { + acts = new HashMap<>(); + + acts.put("roll", DiceLanguageTest::rollReference); + acts.put("env", DiceLanguageTest::printEnv); + } + + /** + * Main method + * + * @param args + * Unused CLI args + */ + public static void main(String[] args) { + Scanner scn = new Scanner(System.in); + int i = 0; + + System.out.print("dice-lang-" + i + "> "); + String ln = scn.nextLine(); + + DiceExpressionParser dep = new DiceExpressionParser(); + Map env = new HashMap<>(); + DiceLanguageState state = new DiceLanguageState(dep, env); + + while (!ln.equalsIgnoreCase("quit")) { + String header = ln.split(" ")[0]; + + if (acts.containsKey(header)) { + acts.get(header).accept(ln, state); + } else { + IDiceExpression exp = DiceExpressionParser.parse(ln, env); + + System.out.println("\tParsed: " + exp.toString()); + System.out.println("\tSample Roll: " + exp.roll()); + + env.put("last", exp); + } + + i++; + + System.out.print("dice-lang-" + i + "> "); + ln = scn.nextLine(); + } + + System.out.println("Bye."); + scn.close(); + } + + /** + * @param ln + * Unused parameter, kept to comply with expected type sig + */ + private static void printEnv(String ln, DiceLanguageState 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()))); + } + + private static void rollReference(String ln, DiceLanguageState stat) { + String[] strangs = ln.split(" "); + + System.out.println("\tRolling dice expression " + strangs[1] + " " + + strangs[2] + " times."); + + int nRolls = Integer.parseInt(strangs[2]); + + IDiceExpression dexp = stat + .merge((dep, env) -> env.get(strangs[1])); + + for (int i = 1; i <= nRolls; i++) { + int roll = dexp.roll(); + + System.out.println("\tRolled " + roll); + } + } +} diff --git a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTLanguageTest.java b/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTLanguageTest.java deleted file mode 100644 index fa92fd2..0000000 --- a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTLanguageTest.java +++ /dev/null @@ -1,261 +0,0 @@ -package bjc.dicelang.examples; - -import java.util.InputMismatchException; -import java.util.Scanner; - -import bjc.utils.data.ITree; -import bjc.utils.funcdata.FunctionalMap; -import bjc.utils.funcdata.FunctionalStringTokenizer; -import bjc.utils.funcdata.IList; -import bjc.utils.funcdata.IMap; - -import bjc.dicelang.ast.DiceASTEvaluator; -import bjc.dicelang.ast.DiceASTInliner; -import bjc.dicelang.ast.DiceASTOptimizer; -import bjc.dicelang.ast.DiceASTParser; -import bjc.dicelang.ast.DiceASTReferenceSanitizer; -import bjc.dicelang.ast.IResult; -import bjc.dicelang.ast.nodes.IDiceASTNode; -import bjc.dicelang.ast.optimization.ConstantCollapser; -import bjc.dicelang.ast.optimization.OperationCondenser; - -/** - * Test interface for AST-based dice language - * - * @author ben - * - */ -public class DiceASTLanguageTest { - private static IMap actions; - - private static DiceASTOptimizer optimizer; - - // Set up things that need to be configured - static { - actions = new FunctionalMap<>(); - - // Inline all the variables in a given expression - actions.put("inline", DiceASTLanguageTest::handleInlineAction); - - // Print out the enviroment - actions.put("env", (tokenizer, enviroment) -> { - enviroment.forEach((varName, varValue) -> { - System.out.println(varName + " is bound to " + varValue); - }); - }); - - // Create and configure the optimizer - optimizer = new DiceASTOptimizer(); - - optimizer.addPass(new ConstantCollapser()); - } - - // Read in a command - private static String getNextCommand(Scanner inputSource, - int commandNumber) { - // Print a prompt using the current command number - System.out.print("\ndice-lang-" + commandNumber + "> "); - - // Read in the next command - return inputSource.nextLine(); - } - - private static void handleInlineAction( - FunctionalStringTokenizer tokenizer, - IMap> enviroment) { - // Skip the pragma name - tokenizer.nextToken(); - - // Get the pragma arguments - IList pragmaArgs = tokenizer.toList(); - - if (pragmaArgs.getSize() < 3) { - // Complain about pragma arguments not being valid - System.err.println( - "ERROR: Inline requires at least 3 parameters. They are:" - + "\n\t1. The name of the expression to inline." - + "\n\t2. The name of the variable to bind the result to." - + "\n\t3 and onwards. Names of variables to inline in the expression."); - } else { - // Get arguments - String inlineExpression = pragmaArgs.getByIndex(0); - String variableName = pragmaArgs.getByIndex(1); - - // Grab the variables we want to inline - IList inlinedVariables = pragmaArgs.tail().tail(); - - // Actually inline the variable - ITree inlinedExpression = DiceASTInliner - .selectiveInline(enviroment.get(inlineExpression), - enviroment, inlinedVariables); - - // Stick the inlined variable into the enviroment - enviroment.put(variableName, inlinedExpression); - } - } - - /** - * Main method of class - * - * @param args - * Unused CLI args - */ - public static void main(String[] args) { - // Prepare the things we need for input - Scanner inputSource = new Scanner(System.in); - int commandNumber = 0; - - // Grab the initial command - String currentLine = getNextCommand(inputSource, commandNumber); - - // The enviroment for variables - IMap> enviroment = new FunctionalMap<>(); - - // Handle commands - while (!currentLine.equalsIgnoreCase("quit")) { - // Get the name of a possible action - String possibleActionName = currentLine.split(" ")[0]; - - // Check and see if we're executing an action - if (actions.containsKey(possibleActionName)) { - // Execute action - FunctionalStringTokenizer tokenizer = new FunctionalStringTokenizer( - currentLine); - - // Execute the action - actions.get(possibleActionName).accept(tokenizer, - enviroment); - - // Get the next command - currentLine = getNextCommand(inputSource, commandNumber); - - continue; - } - - // The AST we are going to build - ITree builtAST; - - // Time command preparation - long time = System.nanoTime(); - - // Prepare the command - IList preparedTokens = DiceExpressionPreparer - .prepareCommand(currentLine); - - System.out.println("Command prepared in " - + (double) (System.nanoTime() - time) / 1000000000 - + " s"); - - try { - // Time the AST creation - time = System.nanoTime(); - - // Create the AST - builtAST = DiceASTParser.createFromString(preparedTokens); - - System.out - .println( - "Command parsed in " - + (double) (System.nanoTime() - - time) / 1000000000 - + " s"); - } catch (InputMismatchException | IllegalStateException - | UnsupportedOperationException ex) { - // Tell the user there was an error in parsing - System.out.println("PARSING ERROR: " + ex.getLocalizedMessage()); - - // Move onto the next command - currentLine = getNextCommand(inputSource, commandNumber); - - continue; - } - - // Print out parsed AST - System.out.println("\tParsed: " + builtAST.toString()); - - // Time AST transformation - time = System.nanoTime(); - - // Transform the AST - ITree transformedAST = transformAST(builtAST, - enviroment); - - System.out.println("Command transformed in " - + (double) (System.nanoTime() - time) / 1000000000 - + " s"); - - // Print out the transformed AST - System.out - .println("\tTransformed: " + transformedAST.toString()); - - - try { - // Time the evaluation - time = System.nanoTime(); - - // Evaluate the expression once - IResult sampleResult = DiceASTEvaluator.evaluateAST(transformedAST, - enviroment); - - System.out - .println( - "Command evaluated in " - + (double) (System.nanoTime() - - time) / 1000000000 - + " s"); - - // Print out the result of evaluating the expression - System.out.println("\t\tSample Result: " + sampleResult); - - // Update the 'last' meta-variable - enviroment.put("last", transformedAST); - } catch (UnsupportedOperationException usex) { - // Tell the user there was an error in evaluation - System.out.println("EVALUATION ERROR: " + usex.getLocalizedMessage()); - - // Get the next command - currentLine = getNextCommand(inputSource, commandNumber); - - // Process it - continue; - } - - - // Increase the number of commands - commandNumber++; - - // Get the next command - currentLine = getNextCommand(inputSource, commandNumber); - } - - System.out.println("Bye."); - - // Cleanup after ourselves - inputSource.close(); - } - - // Transform a parsed AST - private static ITree transformAST( - ITree builtAST, - IMap> enviroment) { - // Optimize the tree first - ITree optimizedTree = optimizer - .optimizeTree(builtAST, enviroment); - - // Then, condense unnecessary operations - ITree condensedTree = OperationCondenser - .condense(optimizedTree); - - // Next, sanitize references - ITree sanitizedTree = DiceASTReferenceSanitizer - .sanitize(condensedTree, enviroment); - - // Re-optimize the sanitized & condensed tree - optimizedTree = optimizer.optimizeTree(sanitizedTree, enviroment); - - // Re-condense the newly optimized tree - condensedTree = OperationCondenser.condense(optimizedTree); - - return condensedTree; - } -} diff --git a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTPragma.java b/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTPragma.java deleted file mode 100644 index 4900e46..0000000 --- a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTPragma.java +++ /dev/null @@ -1,22 +0,0 @@ -package bjc.dicelang.examples; - -import java.util.function.BiConsumer; - -import bjc.utils.funcdata.FunctionalStringTokenizer; -import bjc.utils.funcdata.IMap; -import bjc.utils.funcdata.ITree; - -import bjc.dicelang.ast.nodes.IDiceASTNode; - -/** - * Alias for the type of a 'pragma' or special language command - * - * To explain it, a pragma is a function that takes a tokenizer with the rest - * of the line, and an enviroment that contains variable bindings - * @author ben - * - */ -public interface DiceASTPragma extends - BiConsumer>> { - // Just an alias -} diff --git a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceExpressionParserTest.java b/dice-lang/src/examples/java/bjc/dicelang/examples/DiceExpressionParserTest.java deleted file mode 100644 index b31f3a0..0000000 --- a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceExpressionParserTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package bjc.dicelang.examples; - -import java.util.HashMap; -import java.util.Scanner; - -import bjc.dicelang.DiceExpressionParser; -import bjc.dicelang.IDiceExpression; - -/** - * Driver class for testing expression parser - * - * @author ben - * - */ -public class DiceExpressionParserTest { - /** - * Run the parser test - * - * @param args - * Unused CLI arguments - */ - public static void main(String[] args) { - /* - * Get a scanner for input - */ - Scanner scn = new Scanner(System.in); - - /* - * Ask to enter a expression - */ - System.out.print("Enter dice expression: "); - - String exp = scn.nextLine(); - - /* - * Enter amount of times to roll an expression - */ - System.out.print("Enter number of times to roll: "); - - int nTimes = Integer.parseInt(scn.nextLine()); - - IDiceExpression dexp = DiceExpressionParser.parse(exp, - new HashMap<>()); - - /* - * Roll the dice a specified amount of times - */ - for (int i = 1; i <= nTimes; i++) { - int roll = dexp.roll(); - - System.out.println("Rolled " + roll); - } - - /* - * Clean up after ourselves - */ - scn.close(); - } -} diff --git a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceExpressionPreparer.java b/dice-lang/src/examples/java/bjc/dicelang/examples/DiceExpressionPreparer.java deleted file mode 100644 index 5488b5d..0000000 --- a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceExpressionPreparer.java +++ /dev/null @@ -1,93 +0,0 @@ -package bjc.dicelang.examples; - -import java.util.Deque; -import java.util.LinkedList; - -import bjc.utils.data.IPair; -import bjc.utils.data.Pair; -import bjc.utils.funcdata.FunctionalStringTokenizer; -import bjc.utils.funcdata.IList; -import bjc.utils.funcutils.ListUtils; -import bjc.utils.parserutils.ShuntingYard; - -/** - * Prepare a dice expression to be parsed - * - * @author ben - * - */ -public class DiceExpressionPreparer { - /** - * The yard to use for shunting expressions - */ - private static ShuntingYard yard; - - private static final int MATH_PREC = 20; - private static final int DICE_PREC = 10; - private static final int EXPR_PREC = 0; - - // Do initialization for all parsers - static { - // The shunter we're going to use - yard = new ShuntingYard<>(false); - - // Configure the shunters operators - // Basic mathematical operators - yard.addOp("+", 0 + MATH_PREC); - yard.addOp("-", 0 + MATH_PREC); - - yard.addOp("*", 1 + MATH_PREC); - yard.addOp("/", 1 + MATH_PREC); - - yard.addOp("d", 0 + DICE_PREC); // dice operator: use for creating - // variable size dice groups - yard.addOp("c", 1 + DICE_PREC); // compound operator: use for - // creating compound dice from expressions - - yard.addOp("=>", 0 + EXPR_PREC); // let operator: evaluate an - // expression in the context of another - yard.addOp(":=", 1 + EXPR_PREC); // binding operator: Bind a name - // to a variable expression - } - - // Prepare a command, turning raw tokens into input for the tree builder - public static IList prepareCommand(String currentLine) { - // Split the command into tokens - IList tokens = FunctionalStringTokenizer - .fromString(currentLine).toList(); - - // The linked list to use for handling tokens - Deque> ops = new LinkedList<>(); - - // Prepare the list for operator expansion - ops.add(new Pair<>("+", "\\+")); - ops.add(new Pair<>("-", "-")); - ops.add(new Pair<>("*", "\\*")); - ops.add(new Pair<>("/", "/")); - ops.add(new Pair<>(":=", ":=")); - ops.add(new Pair<>("=>", "=>")); - - // Expand infix single tokens to multiple infix tokens - IList semiExpandedTokens = ListUtils.splitTokens(tokens, - ops); - - // Reinitialize the list - ops = new LinkedList<>(); - - // Prepare the list for deaffixation - ops.add(new Pair<>("(", "\\(")); - ops.add(new Pair<>(")", "\\)")); - ops.add(new Pair<>("[", "\\[")); - ops.add(new Pair<>("]", "\\]")); - - // Deaffix ('s and ['s from tokens - IList fullyExpandedTokens = ListUtils - .deAffixTokens(semiExpandedTokens, ops); - - // Remove blank tokens - fullyExpandedTokens.removeIf((strang) -> strang.equals("")); - - // Shunt the tokens, and hand them back - return yard.postfix(fullyExpandedTokens, (token) -> token); - } -} diff --git a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceLanguageState.java b/dice-lang/src/examples/java/bjc/dicelang/examples/DiceLanguageState.java deleted file mode 100644 index fbb103f..0000000 --- a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceLanguageState.java +++ /dev/null @@ -1,37 +0,0 @@ -package bjc.dicelang.examples; - -import java.util.Map; - -import bjc.utils.data.Pair; - -import bjc.dicelang.DiceExpressionParser; -import bjc.dicelang.IDiceExpression; - -/** - * Internal state of dice language - * - * @author ben - * - */ -public class DiceLanguageState - extends Pair> { - - /** - * Create a new state - */ - public DiceLanguageState() { - } - - /** - * Create a new state with the desired parameters - * - * @param left - * The parser to use - * @param right - * The enviroment to use - */ - public DiceLanguageState(DiceExpressionParser left, - Map right) { - super(left, right); - } -} diff --git a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceLanguageTest.java b/dice-lang/src/examples/java/bjc/dicelang/examples/DiceLanguageTest.java deleted file mode 100644 index bd6270e..0000000 --- a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceLanguageTest.java +++ /dev/null @@ -1,96 +0,0 @@ -package bjc.dicelang.examples; - -import java.util.HashMap; -import java.util.Map; -import java.util.Scanner; -import java.util.function.BiConsumer; - -import bjc.dicelang.DiceExpressionParser; -import bjc.dicelang.IDiceExpression; - -/** - * Test of dice language - * - * @author ben - * - */ -public class DiceLanguageTest { - private static Map> acts; - - static { - acts = new HashMap<>(); - - acts.put("roll", DiceLanguageTest::rollReference); - acts.put("env", DiceLanguageTest::printEnv); - } - - /** - * Main method - * - * @param args - * Unused CLI args - */ - public static void main(String[] args) { - Scanner scn = new Scanner(System.in); - int i = 0; - - System.out.print("dice-lang-" + i + "> "); - String ln = scn.nextLine(); - - DiceExpressionParser dep = new DiceExpressionParser(); - Map env = new HashMap<>(); - DiceLanguageState state = new DiceLanguageState(dep, env); - - while (!ln.equalsIgnoreCase("quit")) { - String header = ln.split(" ")[0]; - - if (acts.containsKey(header)) { - acts.get(header).accept(ln, state); - } else { - IDiceExpression exp = DiceExpressionParser.parse(ln, env); - - System.out.println("\tParsed: " + exp.toString()); - System.out.println("\tSample Roll: " + exp.roll()); - - env.put("last", exp); - } - - i++; - - System.out.print("dice-lang-" + i + "> "); - ln = scn.nextLine(); - } - - System.out.println("Bye."); - scn.close(); - } - - /** - * @param ln - * Unused parameter, kept to comply with expected type sig - */ - private static void printEnv(String ln, DiceLanguageState 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()))); - } - - private static void rollReference(String ln, DiceLanguageState stat) { - String[] strangs = ln.split(" "); - - System.out.println("\tRolling dice expression " + strangs[1] + " " - + strangs[2] + " times."); - - int nRolls = Integer.parseInt(strangs[2]); - - IDiceExpression dexp = stat - .merge((dep, env) -> env.get(strangs[1])); - - for (int i = 1; i <= nRolls; i++) { - int roll = dexp.roll(); - - System.out.println("\tRolled " + roll); - } - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/BindingDiceExpression.java b/dice-lang/src/main/java/bjc/dicelang/BindingDiceExpression.java deleted file mode 100644 index 6a030e3..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/BindingDiceExpression.java +++ /dev/null @@ -1,93 +0,0 @@ -package bjc.dicelang; - -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 expression; - - /** - * The name to bind the expression to - */ - private String variableName; - - /** - * 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 enviroment - * The enviroment to bind into - */ - public BindingDiceExpression(IDiceExpression left, - IDiceExpression right, - Map enviroment) { - if (!(left instanceof ReferenceDiceExpression)) { - throw new UnsupportedOperationException( - "Error: Binding an expression to something that is not a variable reference," - + " or array thereof. is unsupported." - + " Problematic expression is " + left); - } - - String varName = ((ReferenceDiceExpression) left).getName(); - - initialize(varName, right, enviroment); - } - - /** - * Create a new dice expression binder - * - * @param name - * The name of the variable to bind - * @param expression - * The expression to bind to the variable - * @param enviroment - * The enviroment to bind it in - */ - public BindingDiceExpression(String name, IDiceExpression expression, - Map enviroment) { - initialize(name, expression, enviroment); - } - - private void initialize(String name, IDiceExpression expr, - Map enviroment) { - this.variableName = name; - this.expression = expr; - - enviroment.put(name, expr); - } - - /* - * (non-Javadoc) - * - * @see bjc.utils.dice.IDiceExpression#roll() - */ - @Override - public int roll() { - return expression.roll(); - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "assign[n=" + variableName + ", exp=" - + expression.toString() + "]"; - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ComplexDice.java b/dice-lang/src/main/java/bjc/dicelang/ComplexDice.java deleted file mode 100644 index 9bf191f..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ComplexDice.java +++ /dev/null @@ -1,147 +0,0 @@ -package bjc.dicelang; - -/** - * 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 expression - * The string to parse the dice from - * @return A dice group parsed from the string - */ - public static IDiceExpression fromString(String expression) { - // Handle the case where someone passes us a simple expression - // containing a single die - if (!expression.contains("d")) { - return new Die(Integer.parseInt(expression)); - } - // Split it on the dice type marker - - String[] strangs = expression.split("d"); - - try { - // Create the actual group of dice - return new ComplexDice( - new ScalarDie(Integer.parseInt(strangs[0])), - new Die(Integer.parseInt(strangs[1]))); - } catch (@SuppressWarnings("unused") NumberFormatException nfex) { - // We don't care about details - - // Tell the user the expression is invalid - throw new IllegalArgumentException( - "Attempted to create a set of dice using invalid arguments." - + " They must be integers. " + strangs[0] - + " and " + strangs[1] - + " are likely culprits."); - } - } - - /** - * The die being rolled - */ - private IDiceExpression die; - - /** - * The number of the particular 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); - } - - @Override - public boolean canOptimize() { - // Can only optimize this dice group if both components can be - // optimized and the die itself has only one value - if (nDice.canOptimize() && die.canOptimize()) { - return die.optimize() == 1; - } - - return false; - } - - @Override - public int optimize() { - if (!canOptimize()) { - throw new UnsupportedOperationException( - "This complex dice cannot be optimized. " - + "Both the dice to be rolled and the number of" - + " dice must be optimizable."); - } - - return nDice.optimize(); - } - - /* - * (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(); - - if (nRoll < 0) { - throw new UnsupportedOperationException( - "Attempted to roll a negative number of dice. " - + "The problematic expression is " + nDice); - } - - // Roll all the dice and combine them - for (int i = 0; i < nRoll; i++) { - res += die.roll(); - } - - return res; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - // Print simple dice groups in a much clearer manner - if (nDice instanceof ScalarDie && die instanceof Die) { - return nDice.toString() + die.toString(); - } - - return "complex[n=" + nDice.toString() + ", d=" + die.toString() - + "]"; - } -} \ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/CompoundDice.java b/dice-lang/src/main/java/bjc/dicelang/CompoundDice.java deleted file mode 100644 index 704e4cd..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/CompoundDice.java +++ /dev/null @@ -1,100 +0,0 @@ -package bjc.dicelang; - -/** - * 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 leftDice; - - /** - * The right die of the expression - */ - private IDiceExpression rightDice; - - /** - * Create a new compound dice using the specified dice - * - * @param left - * The die to use on the left - * @param right - * The die to use on the right - */ - public CompoundDice(IDiceExpression left, IDiceExpression right) { - this.leftDice = left; - this.rightDice = right; - } - - /** - * Create a new compound dice from two dice strings - * - * @param leftExp - * The left side dice as a string - * @param rightExp - * The right side dice as a string - */ - public CompoundDice(String leftExp, String rightExp) { - this(ComplexDice.fromString(leftExp), - ComplexDice.fromString(rightExp)); - } - - /** - * Create a new compound dice from an array of dice strings - * - * @param exps - * An array of two dice strings - */ - public CompoundDice(String[] exps) { - this(exps[0], exps[1]); - } - - @Override - public boolean canOptimize() { - return leftDice.canOptimize() && rightDice.canOptimize(); - } - - @Override - public int optimize() { - if (!canOptimize()) { - throw new UnsupportedOperationException( - "Cannot optimize this compound dice. " - + "Both component dice must be optimizable" - + " to optimize a compound dice"); - } - - return Integer - .parseInt(leftDice.optimize() + "" + rightDice.optimize()); - } - - /* - * (non-Javadoc) - * - * @see bjc.utils.dice.IDiceExpression#roll() - */ - @Override - public int roll() { - /* - * Make the combination of the two dice - */ - return Integer.parseInt(leftDice.roll() + "" + rightDice.roll()); - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "compound[l=" + leftDice.toString() + ", r=" - + rightDice.toString() + "]"; - } -} \ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/DiceExpressionBuilder.java b/dice-lang/src/main/java/bjc/dicelang/DiceExpressionBuilder.java deleted file mode 100644 index af856a6..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/DiceExpressionBuilder.java +++ /dev/null @@ -1,150 +0,0 @@ -package bjc.dicelang; - -import static bjc.dicelang.DiceExpressionType.ADD; -import static bjc.dicelang.DiceExpressionType.DIVIDE; -import static bjc.dicelang.DiceExpressionType.MULTIPLY; -import static bjc.dicelang.DiceExpressionType.SUBTRACT; - -/** - * Build a dice expression piece by piece - * - * @author ben - * - */ -public class DiceExpressionBuilder { - /** - * The dice expression we are building - */ - private IDiceExpression baking; - - /** - * 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 OperatorDiceExpression(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 OperatorDiceExpression(baking, new ScalarDie(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 OperatorDiceExpression(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 OperatorDiceExpression(baking, new ScalarDie(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 OperatorDiceExpression(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 OperatorDiceExpression(baking, new ScalarDie(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 OperatorDiceExpression(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 OperatorDiceExpression(baking, new ScalarDie(num), - SUBTRACT); - return this; - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/DiceExpressionParser.java b/dice-lang/src/main/java/bjc/dicelang/DiceExpressionParser.java deleted file mode 100644 index 8a1552a..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/DiceExpressionParser.java +++ /dev/null @@ -1,173 +0,0 @@ -package bjc.dicelang; - -import java.util.Map; -import java.util.Stack; - -import bjc.utils.funcdata.FunctionalStringTokenizer; -import bjc.utils.funcdata.IList; -import bjc.utils.parserutils.ShuntingYard; - -import org.apache.commons.lang3.StringUtils; - -/** - * Parse a dice expression from a string - * - * @author ben - * - */ -public class DiceExpressionParser { - /** - * Parse a dice expression from a string - * - * @param expression - * The string to parse an expression from - * @param enviroment - * The enviroment to use when parsing expressions - * @return The parsed dice expression - */ - public static IDiceExpression parse(String expression, - Map enviroment) { - /* - * Create a tokenizer over the strings - */ - FunctionalStringTokenizer tokenizer = new FunctionalStringTokenizer( - expression); - - /* - * Create a shunter to rewrite the expression - */ - ShuntingYard yard = new ShuntingYard<>(true); - - /* - * 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 - */ - IList list = yard.postfix(tokenizer.toList(), s -> s); - - /* - * Create a stack for building an expression from parts - */ - Stack expressions = new Stack<>(); - - /* - * Create the expression from parts - */ - list.forEach((expressionPart) -> { - /* - * Handle compound dice - */ - if (StringUtils.countMatches(expressionPart, 'c') == 1 - && !expressionPart.equalsIgnoreCase("c")) { - String[] strangs = expressionPart.split("c"); - - expressions.push(new CompoundDice(strangs)); - } else if (StringUtils.countMatches(expressionPart, 'd') == 1 - && !expressionPart.equalsIgnoreCase("d")) { - /* - * Handle dice groups - */ - expressions.push(ComplexDice.fromString(expressionPart)); - } else { - try { - /* - * Handle scalar numbers - */ - expressions.push(new ScalarDie( - Integer.parseInt(expressionPart))); - } catch (@SuppressWarnings("unused") NumberFormatException nfex) { - // We don't care about details, just that it failed - if (expressions.size() >= 2) { - /* - * Apply an operation to two dice - */ - IDiceExpression rightExpression = expressions - .pop(); - IDiceExpression leftExpression = expressions.pop(); - - switch (expressionPart) { - case ":=": - expressions.push(new BindingDiceExpression( - leftExpression, rightExpression, - enviroment)); - break; - case "+": - expressions - .push(new OperatorDiceExpression( - rightExpression, - leftExpression, - DiceExpressionType.ADD)); - break; - case "-": - expressions - .push(new OperatorDiceExpression( - rightExpression, - leftExpression, - DiceExpressionType.SUBTRACT)); - break; - case "*": - expressions - .push(new OperatorDiceExpression( - rightExpression, - leftExpression, - DiceExpressionType.MULTIPLY)); - break; - case "/": - expressions - .push(new OperatorDiceExpression( - rightExpression, - leftExpression, - DiceExpressionType.DIVIDE)); - break; - case "c": - expressions.push(new CompoundDice( - leftExpression, rightExpression)); - break; - case "d": - expressions.push(new ComplexDice( - leftExpression, rightExpression)); - break; - default: - /* - * Parse it as a variable reference - * - * Make sure to restore popped variables - */ - expressions.push(leftExpression); - expressions.push(rightExpression); - - expressions - .push(new ReferenceDiceExpression( - expressionPart, - enviroment)); - } - } else { - /* - * Parse it as a variable reference - */ - expressions.push(new ReferenceDiceExpression( - expressionPart, enviroment)); - } - } - } - }); - - if (expressions.size() != 1) { - System.err.println( - "WARNING: Leftovers found on dice expression stack. Remember, := is assignment."); - } - - /* - * Return the built expression - */ - return expressions.pop(); - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/DiceExpressionType.java b/dice-lang/src/main/java/bjc/dicelang/DiceExpressionType.java deleted file mode 100644 index 296d751..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/DiceExpressionType.java +++ /dev/null @@ -1,49 +0,0 @@ -package bjc.dicelang; - -/** - * 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/main/java/bjc/dicelang/Die.java b/dice-lang/src/main/java/bjc/dicelang/Die.java deleted file mode 100644 index 192daaa..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/Die.java +++ /dev/null @@ -1,71 +0,0 @@ -package bjc.dicelang; - -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) { - if (nSides < 1) { - throw new UnsupportedOperationException( - "Dice with less than 1 side are not supported"); - } - - this.nSides = nSides; - } - - @Override - public boolean canOptimize() { - return nSides == 1; - } - - @Override - public int optimize() { - if (nSides != 1) { - throw new UnsupportedOperationException( - "Can't optimize " + nSides + "-sided dice"); - } - - return 1; - } - - /* - * (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/main/java/bjc/dicelang/IDiceExpression.java b/dice-lang/src/main/java/bjc/dicelang/IDiceExpression.java deleted file mode 100644 index acb1d4d..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/IDiceExpression.java +++ /dev/null @@ -1,91 +0,0 @@ -package bjc.dicelang; - -import bjc.utils.funcutils.StringUtils; - -/** - * An expression for something that can be rolled like a polyhedral die - * - * @author ben - * - */ -@FunctionalInterface -public interface IDiceExpression { - /** - * Parse a string into an expression. - * - * It can accept the following types of expressions - *
    - *
  • Simple integers - '2'
  • - *
  • Simple dice - 'd6'
  • - *
  • Groups of simple dice - '2d6'
  • - *
  • Number concatenation - '2c6'
  • - *
  • Dice concatenation - '1d10c1d10
  • - *
- * - * Dice concatenation is like using 2 d10s to emulate a d100, so - * instead of adding them, it reads them side by side. - * - * @param expression - * The string to convert to an expression - * - * @return The string, converted into expression form - */ - static IDiceExpression toExpression(String expression) { - String literalData = expression; - - if (StringUtils.containsInfixOperator(literalData, "c")) { - // Parse a compound die - String[] strangs = literalData.split("c"); - - return new CompoundDice(strangs); - } else if (StringUtils.containsInfixOperator(literalData, "d")) { - // Handle groups of similiar dice - return ComplexDice.fromString(literalData); - } else if (literalData.matches("\\Ad\\d+\\Z")) { - // Handle people who put 'd6' instead of '1d6' - return new Die(Integer.parseInt(literalData.substring(1))); - } else { - // Parse a scalar number - try { - return new ScalarDie(Integer.parseInt(literalData)); - } catch (NumberFormatException nfex) { - UnsupportedOperationException usex = new UnsupportedOperationException( - "Found malformed leaf token " + expression); - - usex.initCause(nfex); - - throw usex; - } - } - } - - /** - * Check if this expression can be optimized to a scalar value - * - * @return Whether or not this expression can be optimized to a scalar - * value - */ - public default boolean canOptimize() { - return false; - } - - /** - * Optimize this expression to a scalar value - * - * @return This expression, optimized to a scalar value - * - * @throws UnsupportedOperationException - * if this type of expression can't be optimized - */ - public default int optimize() { - throw new UnsupportedOperationException( - "Can't optimize this type of expression"); - } - - /** - * Roll the dice once - * - * @return The result of rowing the dice - */ - public int roll(); -} \ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/OperatorDiceExpression.java b/dice-lang/src/main/java/bjc/dicelang/OperatorDiceExpression.java deleted file mode 100644 index f86773d..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/OperatorDiceExpression.java +++ /dev/null @@ -1,96 +0,0 @@ -package bjc.dicelang; - -/** - * Implements a class for combining two dice with an operator - * - * @author ben - * - */ -public class OperatorDiceExpression implements IDiceExpression { - /** - * The operator to use for combining the dice - */ - private DiceExpressionType expressionType; - - /** - * The dice on the left side of the expression - */ - private IDiceExpression leftExpression; - - /** - * The dice on the right side of the expression - */ - private IDiceExpression rightExpression; - - /** - * 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 type - * The operator to use for combining the dices - */ - public OperatorDiceExpression(IDiceExpression right, - IDiceExpression left, DiceExpressionType type) { - this.rightExpression = right; - this.leftExpression = left; - this.expressionType = type; - } - - /* - * (non-Javadoc) - * - * @see bjc.utils.dice.IDiceExpression#roll() - */ - @Override - public int roll() { - /* - * Handle each operator - */ - switch (expressionType) { - case ADD: - return rightExpression.roll() + leftExpression.roll(); - case SUBTRACT: - return rightExpression.roll() - leftExpression.roll(); - case MULTIPLY: - return rightExpression.roll() * leftExpression.roll(); - case DIVIDE: - /* - * Round to keep results as integers. We don't really have - * any need for floating-point dice, and continuous - * probability is a pain - */ - try { - return rightExpression.roll() / leftExpression.roll(); - } catch (ArithmeticException aex) { - UnsupportedOperationException usex = new UnsupportedOperationException( - "Attempted to divide by zero." - + " Problematic expression is " - + leftExpression); - - usex.initCause(aex); - - throw usex; - } - default: - throw new IllegalArgumentException( - "Got passed a invalid ScalarExpressionType (" - + expressionType + "). WAT"); - - } - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "dice-exp[type=" + expressionType + ", l=" - + leftExpression.toString() + ", r=" - + rightExpression.toString() + "]"; - } -} \ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/PolyhedralDice.java b/dice-lang/src/main/java/bjc/dicelang/PolyhedralDice.java deleted file mode 100644 index 3ac9420..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/PolyhedralDice.java +++ /dev/null @@ -1,149 +0,0 @@ -package bjc.dicelang; - -/** - * Utility class that produces common polyhedral dice - * - * @author ben - * - */ -public class PolyhedralDice { - /** - * Produce a single d10 - * - * @return A single d10 - */ - public static IDiceExpression d10() { - return d10(1); - } - - /** - * 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 a single d100 - * - * @return A single d100 - */ - public static IDiceExpression d100() { - return d100(1); - } - - /** - * 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 a single d12 - * - * @return A single d12 - */ - public static IDiceExpression d12() { - return d12(1); - } - - /** - * 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 a single d20 - * - * @return A single d20 - */ - public static IDiceExpression d20() { - return d20(1); - } - - /** - * 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 a single d4 - * - * @return A single d4 - */ - public static IDiceExpression d4() { - return d4(1); - } - - /** - * 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 a single d6 - * - * @return A single d6 - */ - public static IDiceExpression d6() { - return d6(1); - } - - /** - * 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 a single d8 - * - * @return A single d8 - */ - public static IDiceExpression d8() { - return d8(1); - } - - /** - * 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/main/java/bjc/dicelang/ReferenceDiceExpression.java b/dice-lang/src/main/java/bjc/dicelang/ReferenceDiceExpression.java deleted file mode 100644 index 483545b..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ReferenceDiceExpression.java +++ /dev/null @@ -1,76 +0,0 @@ -package bjc.dicelang; - -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 enviroment; - - /** - * The name of the bound variable - */ - private String variableName; - - /** - * 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 env) { - this.variableName = name; - this.enviroment = env; - } - - /** - * Get the name of the referenced variable - * - * @return the name of the referenced variable - */ - public String getName() { - return variableName; - } - - /* - * (non-Javadoc) - * - * @see bjc.utils.dice.IDiceExpression#roll() - */ - @Override - public int roll() { - if (!enviroment.containsKey(variableName)) { - throw new UnsupportedOperationException( - "Attempted to reference undefined variable " - + variableName); - } - - return enviroment.get(variableName).roll(); - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - if (enviroment.containsKey(variableName)) { - return enviroment.get(variableName).toString(); - } - - return variableName; - } -} \ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/ScalarDie.java b/dice-lang/src/main/java/bjc/dicelang/ScalarDie.java deleted file mode 100644 index 8d31d15..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ScalarDie.java +++ /dev/null @@ -1,54 +0,0 @@ -package bjc.dicelang; - -/** - * A die that represents a static number - * - * @author ben - * - */ -public class ScalarDie implements IDiceExpression { - /** - * The represented number - */ - private int number; - - /** - * Create a dice with the specified number - * - * @param num - * The number used for the dice - */ - public ScalarDie(int num) { - this.number = num; - } - - @Override - public boolean canOptimize() { - return true; - } - - @Override - public int optimize() { - return number; - } - - /* - * (non-Javadoc) - * - * @see bjc.utils.dice.IDiceExpression#roll() - */ - @Override - public int roll() { - return number; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return Integer.toString(number); - } -} \ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/ArithmeticCollapser.java b/dice-lang/src/main/java/bjc/dicelang/ast/ArithmeticCollapser.java deleted file mode 100644 index 2b9eaa4..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/ArithmeticCollapser.java +++ /dev/null @@ -1,192 +0,0 @@ -package bjc.dicelang.ast; - -import java.util.function.BinaryOperator; - -import bjc.utils.data.IPair; -import bjc.utils.data.Pair; -import bjc.utils.funcdata.IList; -import bjc.utils.funcdata.ITree; -import bjc.utils.funcdata.Tree; - -import bjc.dicelang.ast.nodes.IDiceASTNode; -import bjc.dicelang.ast.nodes.OperatorDiceNode; - -/** - * Responsible for collapsing arithmetic operators - * - * @author ben - * - */ -final class ArithmeticCollapser implements IOperatorCollapser { - // The type of operator we're collapsing - private OperatorDiceNode type; - - // The operator to use to collapse operators - private BinaryOperator valueOp; - - private int initialValue; - - public ArithmeticCollapser(OperatorDiceNode type, - BinaryOperator valueOp, int initVal) { - this.type = type; - this.valueOp = valueOp; - this.initialValue = initVal; - } - - @Override - public IPair> apply( - IList>> nodes) { - IPair> initialState = new Pair<>( - new IntegerResult(initialValue), new Tree<>(type)); - - BinaryOperator>> reducer = ( - currentState, accumulatedState) -> { - // Force evaluation of accumulated state to prevent - // certain bugs from occuring - // accumulatedState.merge((l, r) -> null); - - return reduceStates(accumulatedState, currentState); - }; - - IPair> reducedState = nodes - .reduceAux(initialState, reducer, (state) -> state); - - return reducedState; - } - - private IList combineArrayResults(IResult accumulatedValue, - IResult currentValue) { - IList currentList = ((ArrayResult) currentValue) - .getValue(); - IList accumulatedList = ((ArrayResult) accumulatedValue) - .getValue(); - - if (currentList.getSize() != accumulatedList.getSize()) { - throw new UnsupportedOperationException( - "Can only apply operations to equal-length arrays"); - } - - IList resultList = currentList.combineWith( - accumulatedList, (currentNode, accumulatedNode) -> { - boolean currentNotInt = currentNode - .getType() != ResultType.INTEGER; - boolean accumulatedNotInt = accumulatedNode - .getType() != ResultType.INTEGER; - - if (currentNotInt || accumulatedNotInt) { - throw new UnsupportedOperationException( - "Nesting of array operations isn't allowed"); - } - - int accumulatedInt = ((IntegerResult) accumulatedNode) - .getValue(); - int currentInt = ((IntegerResult) currentNode) - .getValue(); - - IResult combinedValue = new IntegerResult( - valueOp.apply(accumulatedInt, currentInt)); - return combinedValue; - }); - return resultList; - } - - private IPair> doArithmeticCollapse( - IResult accumulatedValue, ITree accumulatedTree, - IResult currentValue) { - if (accumulatedValue.getType() == ResultType.DUMMY - || currentValue.getType() == ResultType.DUMMY) { - DummyResult result = new DummyResult( - "Found dummy result with either accumulated dummy (" - + ((DummyResult) accumulatedValue).getData() - + ") or current dummy (" - + ((DummyResult) currentValue).getData() - + ")."); - - return new Pair<>(result, accumulatedTree); - } - - boolean currentIsInt = currentValue - .getType() == ResultType.INTEGER; - boolean accumulatedIsInt = accumulatedValue - .getType() == ResultType.INTEGER; - - if (!currentIsInt) { - if (!accumulatedIsInt) { - IList resultList = combineArrayResults( - accumulatedValue, currentValue); - - return new Pair<>(new ArrayResult(resultList), - accumulatedTree); - } - - IList resultList = halfCombineLists( - ((ArrayResult) currentValue).getValue(), - accumulatedValue, true); - - return new Pair<>(new ArrayResult(resultList), - accumulatedTree); - } else if (!accumulatedIsInt) { - IList resultList = halfCombineLists( - ((ArrayResult) accumulatedValue).getValue(), - currentValue, false); - - return new Pair<>(new ArrayResult(resultList), - accumulatedTree); - } - - int accumulatedInt = ((IntegerResult) accumulatedValue).getValue(); - int currentInt = ((IntegerResult) currentValue).getValue(); - - int combinedValue = valueOp.apply(accumulatedInt, currentInt); - - return new Pair<>(new IntegerResult(combinedValue), - accumulatedTree); - } - - private IList halfCombineLists(IList list, - IResult scalar, boolean scalarLeft) { - if (scalar.getType() != ResultType.INTEGER) { - throw new UnsupportedOperationException( - "Nested array operations not supported"); - } - - int scalarInt = ((IntegerResult) scalar).getValue(); - - return list.map((element) -> { - if (element.getType() != ResultType.INTEGER) { - throw new UnsupportedOperationException( - "Nested array operations not supported"); - } - - int elementInt = ((IntegerResult) element).getValue(); - - IResult combinedValue; - - if (scalarLeft) { - combinedValue = new IntegerResult( - valueOp.apply(scalarInt, elementInt)); - } else { - combinedValue = new IntegerResult( - valueOp.apply(elementInt, scalarInt)); - } - - return combinedValue; - }); - } - - private IPair> reduceStates( - IPair> accumulatedState, - IPair> currentState) { - return accumulatedState - .bind((accumulatedValue, accumulatedTree) -> { - return currentState - .bind((currentValue, currentTree) -> { - accumulatedTree.addChild(currentTree); - - return doArithmeticCollapse( - accumulatedValue, accumulatedTree, - currentValue); - }); - }); - } -} \ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/ArrayResult.java b/dice-lang/src/main/java/bjc/dicelang/ast/ArrayResult.java deleted file mode 100644 index ac78287..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/ArrayResult.java +++ /dev/null @@ -1,43 +0,0 @@ -package bjc.dicelang.ast; - -import bjc.utils.funcdata.IList; - -/** - * Represents a result that is an array of other results - * - * @author ben - * - * TODO finish implementing me - */ -public class ArrayResult implements IResult { - private IList arrayContents; - - /** - * Create a new array-valued result - * - * @param results - * The results in the array - */ - public ArrayResult(IList results) { - this.arrayContents = results; - } - - @Override - public ResultType getType() { - return ResultType.ARRAY; - } - - /** - * Get the value of this result - * - * @return The value of this result - */ - public IList getValue() { - return arrayContents; - } - - @Override - public String toString() { - return arrayContents.toString(); - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTEvaluator.java b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTEvaluator.java deleted file mode 100644 index cef2e19..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTEvaluator.java +++ /dev/null @@ -1,321 +0,0 @@ -package bjc.dicelang.ast; - -import java.util.function.Supplier; - -import bjc.utils.data.IHolder; -import bjc.utils.data.IPair; -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.funcdata.ITree; -import bjc.utils.funcdata.Tree; - -import bjc.dicelang.ComplexDice; -import bjc.dicelang.ast.nodes.DiceASTType; -import bjc.dicelang.ast.nodes.DiceLiteralNode; -import bjc.dicelang.ast.nodes.DiceLiteralType; -import bjc.dicelang.ast.nodes.IDiceASTNode; -import bjc.dicelang.ast.nodes.ILiteralDiceNode; -import bjc.dicelang.ast.nodes.IntegerLiteralNode; -import bjc.dicelang.ast.nodes.OperatorDiceNode; -import bjc.dicelang.ast.nodes.VariableDiceNode; - -/** - * Evaluate a dice AST to an integer value - * - * @author ben - * - */ -public class DiceASTEvaluator { - private static IResult bindLiteralValue(IDiceASTNode leafNode, - IMap> 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 buildOperations( - IMap> enviroment) { - IMap 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 resultSupplier = () -> { - IList resultList = new FunctionalList<>(); - - nodes.forEach((node) -> { - resultList.add(node.getLeft()); - }); - - return new ArrayResult(resultList); - }; - - Supplier> treeSupplier = () -> { - ITree 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> enviroment, - IPair> nameNode, - ITree nameTree, ITree valueTree, - IHolder childCount, ITree 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 expression, - IMap> enviroment) { - IMap collapsers = buildOperations( - enviroment); - - return expression.collapse( - (node) -> evaluateLeaf(node, enviroment), collapsers::get, - (pair) -> pair.getLeft()); - } - - private static IPair> evaluateLeaf( - IDiceASTNode leafNode, - IMap> enviroment) { - ITree 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> parseBinding( - IMap> enviroment, - IList>> nodes) { - if (nodes.getSize() != 2) { - throw new UnsupportedOperationException( - "Can only bind nodes with two children. Problem children are " - + nodes); - } - - IPair> nameNode = nodes.getByIndex(0); - IPair> 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 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> parseGroup( - IList>> nodes) { - if (nodes.getSize() != 2) { - throw new UnsupportedOperationException( - "Can only form a group from two dice"); - } - - IPair> numberDiceNode = nodes - .getByIndex(0); - IPair> 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> parseLet( - IMap> enviroment, - IList>> nodes) { - if (nodes.getSize() != 2) { - throw new UnsupportedOperationException( - "Can only use let with two expressions."); - } - - ITree bindTree = nodes.getByIndex(0).getRight(); - ITree expressionTree = nodes.getByIndex(1) - .getRight(); - - IMap> letEnviroment = enviroment - .extend(); - - System.out.println("Evaluating tree for bound values"); - - evaluateAST(bindTree, letEnviroment); - - IResult exprResult = evaluateAST(expressionTree, letEnviroment); - - IList> childrn = nodes - .map((pair) -> pair.getRight()); - - return new Pair<>(exprResult, - new Tree<>(OperatorDiceNode.LET, childrn)); - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTInliner.java b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTInliner.java deleted file mode 100644 index 305c9af..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTInliner.java +++ /dev/null @@ -1,125 +0,0 @@ -package bjc.dicelang.ast; - -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; -import bjc.utils.funcdata.IMap; -import bjc.utils.funcdata.ITree; -import bjc.utils.funcdata.Tree; - -import bjc.dicelang.ast.nodes.DiceASTType; -import bjc.dicelang.ast.nodes.IDiceASTNode; -import bjc.dicelang.ast.nodes.VariableDiceNode; - -/** - * Inline variables in a dice AST - * - * @author ben - * - */ -public class DiceASTInliner { - /** - * Inline all the variables in the AST - * - * @param ast - * The AST to inline variables into - * @param enviroment - * The enviroment to inline from - * @return The inlined AST - */ - public static ITree inlineAll(ITree ast, - IMap> enviroment) { - // Tell the compiler that the null is for the entire varargs - // parameter, not a single one with a null value - return selectiveInline(ast, enviroment, (String[]) null); - } - - private static ITree inlineNode(IDiceASTNode node, - IMap> enviroment, - boolean specificInline, IList variableNames) { - // Only variables get inlined - if (node.getType() != DiceASTType.VARIABLE) { - return new Tree<>(node); - } - - // Get the name of what we're inlining - String variableName = ((VariableDiceNode) node).getVariable(); - - // If we're inlining only certain variables, do so - if (specificInline) { - // Only inline the variable if we're supposed to - if (variableNames.contains(variableName)) { - // You can't inline non-existent variables - if (!enviroment.containsKey(variableName)) { - throw new UnsupportedOperationException( - "Attempted to inline non-existant variable " - + variableName); - } - - // Return the tree for the variable - return enviroment.get(variableName); - } - } else { - // You can't inline non-existent variables - if (!enviroment.containsKey(variableName)) { - throw new UnsupportedOperationException( - "Attempted to inline non-existant variable " - + variableName); - } - - // Return the tree for the variable - return enviroment.get(variableName); - } - - // return new Tree<>(node); - } - - /** - * Inline the specified variables in the AST - * - * @param ast - * The AST to inline variables into - * @param enviroment - * The enviroment to inline from - * @param variables - * The variables to inline - * @return The inlined AST - */ - public static ITree selectiveInline( - ITree ast, - IMap> enviroment, - IList variables) { - return selectiveInline(ast, enviroment, - variables.toArray(new String[0])); - } - - /** - * Inline the specified variables in the AST - * - * @param ast - * The AST to inline variables into - * @param enviroment - * The enviroment to inline from - * @param variables - * The variables to inline - * @return The inlined AST - */ - public static ITree selectiveInline( - ITree ast, - IMap> enviroment, - String... variables) { - // If we're selectively inlining, do so - if (variables != null && variables.length > 0) { - IList variableNames = new FunctionalList<>(variables); - - // Selectively inline each tree node - return ast.flatMapTree((node) -> { - return inlineNode(node, enviroment, true, variableNames); - }); - } - - // Inline everything in each node - return ast.flatMapTree((node) -> { - return inlineNode(node, enviroment, false, null); - }); - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTOptimizer.java b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTOptimizer.java deleted file mode 100644 index d7fc23c..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTOptimizer.java +++ /dev/null @@ -1,60 +0,0 @@ -package bjc.dicelang.ast; - -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; -import bjc.utils.funcdata.IMap; -import bjc.utils.funcdata.ITree; - -import bjc.dicelang.ast.nodes.IDiceASTNode; -import bjc.dicelang.ast.optimization.IOptimizationPass; - -/** - * Contains optimizations appliable to a dice AST - * - * @author ben - * - */ -public class DiceASTOptimizer { - private IList passes; - - /** - * Create a new optimizer - */ - public DiceASTOptimizer() { - passes = new FunctionalList<>(); - } - - /** - * Add a pass to the list of optimization passes - * - * @param pass - * The pass to add - */ - public void addPass(IOptimizationPass pass) { - passes.add(pass); - } - - /** - * Optimize the passed in tree - * - * @param ast - * The tree to optimize - * @param enviroment - * The enviroment for variable references - * @return The optimized tree - */ - public ITree optimizeTree(ITree ast, - IMap> enviroment) { - ITree optimizedTree = passes.reduceAux(ast, - (currentPass, currentTree) -> { - return currentTree.collapse(currentPass::optimizeLeaf, - (operator) -> { - return (nodes) -> { - return currentPass.optimizeOperator( - operator, nodes); - }; - }, (tree) -> tree); - }, (tree) -> tree); - return optimizedTree; - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTParser.java b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTParser.java deleted file mode 100644 index 9a36951..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTParser.java +++ /dev/null @@ -1,160 +0,0 @@ -package bjc.dicelang.ast; - -import java.util.Deque; -import java.util.InputMismatchException; -import java.util.function.Function; -import java.util.function.Predicate; - -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.FunctionalMap; -import bjc.utils.funcdata.IList; -import bjc.utils.funcdata.IMap; -import bjc.utils.funcdata.ITree; -import bjc.utils.funcdata.Tree; -import bjc.utils.funcutils.StringUtils; -import bjc.utils.parserutils.TreeConstructor; - -import bjc.dicelang.IDiceExpression; -import bjc.dicelang.ast.nodes.DiceLiteralNode; -import bjc.dicelang.ast.nodes.DiceLiteralType; -import bjc.dicelang.ast.nodes.IDiceASTNode; -import bjc.dicelang.ast.nodes.ILiteralDiceNode; -import bjc.dicelang.ast.nodes.IntegerLiteralNode; -import bjc.dicelang.ast.nodes.OperatorDiceNode; -import bjc.dicelang.ast.nodes.VariableDiceNode; - -/** - * Parse a string expression into AST form. Doesn't do anything else - * - * @author ben - * - */ -public class DiceASTParser { - private static IDiceASTNode convertLeafNode(String leafNode) { - DiceLiteralType literalType = ILiteralDiceNode - .getLiteralType(leafNode); - - if (literalType != null) { - switch (literalType) { - case DICE: - return new DiceLiteralNode( - IDiceExpression.toExpression(leafNode)); - case INTEGER: - return new IntegerLiteralNode( - Integer.parseInt(leafNode)); - default: - throw new InputMismatchException( - "Cannot convert string '" + leafNode - + "' into a literal."); - } - } - - if (leafNode.matches("[+-]?\\d*\\.\\d+")) { - throw new InputMismatchException( - "Floating point literals are not supported"); - } - - return new VariableDiceNode(leafNode); - } - - private static IDiceASTNode convertOperatorNode(String operatorNode) { - try { - return OperatorDiceNode.fromString(operatorNode); - } catch (IllegalArgumentException iaex) { - InputMismatchException imex = new InputMismatchException( - "Attempted to parse invalid operator " + operatorNode); - - imex.initCause(iaex); - - throw imex; - } - } - - /** - * Create an AST from a list of tokens - * - * @param tokens - * The list of tokens to convert - * @return An AST built from the tokens - */ - public static ITree createFromString( - IList tokens) { - Predicate specialPicker = (operator) -> { - if (StringUtils.containsOnly(operator, "\\[")) { - return true; - } else if (StringUtils.containsOnly(operator, "\\]")) { - return true; - } - - return false; - }; - - IMap>, ITree>> operators = new FunctionalMap<>(); - - operators.put("[", (queuedTrees) -> { - Tree openArray = new Tree<>("["); - - return openArray; - }); - - operators.put("]", (queuedTrees) -> { - return parseCloseArray(queuedTrees); - }); - - ITree rawTokens = TreeConstructor.constructTree(tokens, - (token) -> { - return isOperatorNode(token); - }, specialPicker, operators::get); - - ITree tokenizedTree = rawTokens.rebuildTree( - DiceASTParser::convertLeafNode, - DiceASTParser::convertOperatorNode); - - return tokenizedTree; - } - - private static boolean isOperatorNode(String token) { - if (StringUtils.containsOnly(token, "\\[")) { - return true; - } else if (StringUtils.containsOnly(token, "\\]")) { - return true; - } - - if (token.equals("[]")) { - // This is a synthetic operator, constructed by [ and ] - return true; - } - - try { - OperatorDiceNode.fromString(token); - return true; - } catch (@SuppressWarnings("unused") IllegalArgumentException iaex) { - // We don't care about details - return false; - } - } - - private static ITree parseCloseArray( - Deque> queuedTrees) { - IList> children = new FunctionalList<>(); - - while (shouldContinuePopping(queuedTrees)) { - children.add(queuedTrees.pop()); - } - - queuedTrees.pop(); - - children.reverse(); - - ITree arrayTree = new Tree<>("[]", children); - - return arrayTree; - } - - private static boolean shouldContinuePopping( - Deque> queuedTrees) { - String peekToken = queuedTrees.peek().getHead(); - - return !peekToken.equals("["); - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTReferenceChecker.java b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTReferenceChecker.java deleted file mode 100644 index 34414c5..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTReferenceChecker.java +++ /dev/null @@ -1,61 +0,0 @@ -package bjc.dicelang.ast; - -import java.util.function.Consumer; - -import bjc.utils.data.IHolder; - -import bjc.dicelang.ast.nodes.DiceASTType; -import bjc.dicelang.ast.nodes.IDiceASTNode; -import bjc.dicelang.ast.nodes.VariableDiceNode; - -/** - * Check if the specified node references a particular variable - * - * @author ben - * - */ -public final class DiceASTReferenceChecker - implements Consumer { - /** - * This is true if the specified node references the set variable - */ - private IHolder 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 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); - } - - return false; - } -} \ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTReferenceSanitizer.java b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTReferenceSanitizer.java deleted file mode 100644 index d8f658e..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTReferenceSanitizer.java +++ /dev/null @@ -1,201 +0,0 @@ -package bjc.dicelang.ast; - -import bjc.utils.data.IHolder; -import bjc.utils.data.Identity; -import bjc.utils.funcdata.IMap; -import bjc.utils.funcdata.ITree; -import bjc.utils.funcdata.TopDownTransformResult; -import bjc.utils.funcdata.Tree; - -import bjc.dicelang.ast.nodes.IDiceASTNode; -import bjc.dicelang.ast.nodes.OperatorDiceNode; -import bjc.dicelang.ast.nodes.VariableDiceNode; - -/** - * Sanitize the references in an AST so that a variable that refers to - * itself in its definition has the occurance of it replaced with its - * previous definition - * - * @author ben - * - */ -public class DiceASTReferenceSanitizer { - private static ITree doSanitize(ITree ast, - IMap> enviroment) { - if (ast.getChildrenCount() != 2) { - throw new UnsupportedOperationException( - "Assignment must have two arguments."); - } - - ITree nameTree = ast.getChild(0); - ITree valueTree = ast.getChild(1); - - if (!DiceASTUtils.containsSimpleVariable(nameTree)) { - if (nameTree.getHead() == OperatorDiceNode.ARRAY) { - IHolder allSimpleVariables = new Identity<>(true); - - nameTree.doForChildren((child) -> { - if (allSimpleVariables.getValue()) { - boolean isSimple = DiceASTUtils - .containsSimpleVariable(child); - - allSimpleVariables.replace(isSimple); - } - }); - - if (!allSimpleVariables.getValue()) { - throw new UnsupportedOperationException( - "Array assignment must be between variables and" - + " a expression/array of expressions"); - } - - if (valueTree.getHead() == OperatorDiceNode.ARRAY) { - if (nameTree.getChildrenCount() != valueTree - .getChildrenCount()) { - throw new UnsupportedOperationException( - "Array assignment between arrays must be" - + " between two arrays of equal length"); - } - } - } else { - throw new UnsupportedOperationException( - "Assignment must be between a variable and a expression"); - } - } - - if (nameTree.getHead() == OperatorDiceNode.ARRAY) { - if (valueTree.getHead() == OperatorDiceNode.ARRAY) { - IHolder childCounter = new Identity<>(0); - - ITree returnTree = new Tree<>( - OperatorDiceNode.ARRAY); - - nameTree.doForChildren((child) -> { - String variableName = child.transformHead((node) -> { - return ((VariableDiceNode) node).getVariable(); - }); - - ITree currentValue = valueTree - .getChild(childCounter.getValue()); - - ITree sanitizedSubtree = doSingleSanitize( - ast, enviroment, child, currentValue, - variableName); - - if (sanitizedSubtree == null) { - ITree oldTree = new Tree<>( - ast.getHead(), child, currentValue); - - returnTree.addChild(oldTree); - } else { - returnTree.addChild(sanitizedSubtree); - } - - childCounter.transform((count) -> count + 1); - }); - - return returnTree; - } - - ITree returnTree = new Tree<>( - OperatorDiceNode.ARRAY); - - nameTree.doForChildren((child) -> { - String variableName = child.transformHead( - (node) -> ((VariableDiceNode) node).getVariable()); - - ITree sanitizedChild = doSingleSanitize(ast, - enviroment, child, valueTree, variableName); - if (sanitizedChild == null) { - ITree oldTree = new Tree<>(ast.getHead(), - child, valueTree); - - returnTree.addChild(oldTree); - } else { - returnTree.addChild(sanitizedChild); - } - }); - - return returnTree; - } - - String variableName = nameTree.transformHead( - (node) -> ((VariableDiceNode) node).getVariable()); - - ITree sanitizedTree = doSingleSanitize(ast, - enviroment, nameTree, valueTree, variableName); - - if (sanitizedTree == null) { - return ast; - } - - return sanitizedTree; - } - - private static ITree doSingleSanitize( - ITree ast, - IMap> enviroment, - ITree nameTree, ITree valueTree, - String variableName) { - if (enviroment.containsKey(variableName)) { - // @ is a meta-variable standing for the left side of an - // assignment - ITree oldVal = enviroment.put("@", - enviroment.get(variableName)); - - // We should always inline out references to last, because it - // will always change - ITree inlinedValue = DiceASTInliner - .selectiveInline(valueTree, enviroment, variableName, - "last", "@"); - - if (oldVal != null) { - enviroment.put("@", oldVal); - } else { - enviroment.remove("@"); - } - - return new Tree<>(ast.getHead(), nameTree, inlinedValue); - } - - return null; - } - - /** - * Sanitize the references in an AST - * - * @param ast - * @param enviroment - * @return The sanitized AST - */ - public static ITree sanitize(ITree ast, - IMap> enviroment) { - return ast.topDownTransform( - DiceASTReferenceSanitizer::shouldSanitize, (subTree) -> { - return doSanitize(subTree, enviroment); - }); - } - - private static TopDownTransformResult shouldSanitize( - IDiceASTNode node) { - if (!node.isOperator()) { - return TopDownTransformResult.SKIP; - } - - switch (((OperatorDiceNode) node)) { - case ASSIGN: - return TopDownTransformResult.TRANSFORM; - case ARRAY: - case LET: - return TopDownTransformResult.PASSTHROUGH; - case ADD: - case COMPOUND: - case DIVIDE: - case GROUP: - case MULTIPLY: - case SUBTRACT: - default: - return TopDownTransformResult.SKIP; - } - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTUtils.java b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTUtils.java deleted file mode 100644 index d98c8fe..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTUtils.java +++ /dev/null @@ -1,86 +0,0 @@ -package bjc.dicelang.ast; - -import bjc.utils.funcdata.ITree; - -import bjc.dicelang.IDiceExpression; -import bjc.dicelang.ScalarDie; -import bjc.dicelang.ast.nodes.DiceASTType; -import bjc.dicelang.ast.nodes.DiceLiteralNode; -import bjc.dicelang.ast.nodes.IDiceASTNode; -import bjc.dicelang.ast.nodes.ILiteralDiceNode; -import bjc.dicelang.ast.nodes.IntegerLiteralNode; - -/** - * Functions that are useful when dealing with dice ASTs - * - * @author ben - * - */ -public class DiceASTUtils { - /** - * Check if a dice AST contains a simple variable reference - * - * @param nameTree - * The tree to check for a reference in - * @return Whether or not a dice AST contains a simple variable - * reference - */ - public static boolean containsSimpleVariable( - ITree nameTree) { - return nameTree.transformHead((nameNode) -> { - if (nameNode.getType() != DiceASTType.VARIABLE) { - return false; - } - - return true; - }); - } - - /** - * Convert an literal AST node to a dice expression, if possible. - * - * @param tree - * The node to convert in tree form - * @return The tree as a dice expression - * - * @throws ClassCastException - * if the head of the tree is not a literal (implements - * {@link ILiteralDiceNode}) - * @throws UnsupportedOperationException - * if the head of the tree is not optimizable - */ - public static IDiceExpression literalToExpression( - ITree tree) { - ILiteralDiceNode literalNode = (ILiteralDiceNode) tree.getHead(); - - switch (literalNode.getLiteralType()) { - case DICE: - return ((DiceLiteralNode) literalNode).getValue(); - case INTEGER: - return new ScalarDie( - ((IntegerLiteralNode) literalNode).getValue()); - default: - throw new UnsupportedOperationException( - "This type of literal isn't convertable to an expression"); - } - } - - /** - * Convert an literal AST node to an integer, if possible. - * - * @param tree - * The literal node to convert, as a tree - * @return The node as an integer - * - * @throws ClassCastException - * if the head of the tree is not a literal (implements - * {@link ILiteralDiceNode}) - * @throws UnsupportedOperationException - * if the head of the tree is not optimizable - */ - public static int literalToInteger(ITree tree) { - return tree.transformHead((node) -> { - return ((ILiteralDiceNode) node).optimize(); - }); - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/DummyResult.java b/dice-lang/src/main/java/bjc/dicelang/ast/DummyResult.java deleted file mode 100644 index a84bb7c..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/DummyResult.java +++ /dev/null @@ -1,31 +0,0 @@ -package bjc.dicelang.ast; - -public class DummyResult implements IResult { - /* - * The reason why this result is a dummy - */ - private String dummyData; - - public DummyResult(String data) { - dummyData = data; - } - - /** - * Get the data in this dummy - * - * @return The reason why this result is a dummy - */ - public String getData() { - return dummyData; - } - - @Override - public ResultType getType() { - return ResultType.DUMMY; - } - - @Override - public String toString() { - return "Dummy with reason " + dummyData; - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/IOperatorCollapser.java b/dice-lang/src/main/java/bjc/dicelang/ast/IOperatorCollapser.java deleted file mode 100644 index 0efaca9..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/IOperatorCollapser.java +++ /dev/null @@ -1,20 +0,0 @@ -package bjc.dicelang.ast; - -import java.util.function.Function; - -import bjc.utils.data.IPair; -import bjc.utils.funcdata.IList; -import bjc.utils.funcdata.ITree; - -import bjc.dicelang.ast.nodes.IDiceASTNode; - -/** - * Alias for operator collapsers. Because 68-char types are too long - * - * @author ben - * - */ -public interface IOperatorCollapser extends - Function>>, IPair>> { - // Just an alias -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/IResult.java b/dice-lang/src/main/java/bjc/dicelang/ast/IResult.java deleted file mode 100644 index 9a3f325..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/IResult.java +++ /dev/null @@ -1,16 +0,0 @@ -package bjc.dicelang.ast; - -/** - * Represents a result from an expression evaluation - * - * @author ben - * - */ -public interface IResult { - /** - * Get the type of this result - * - * @return The type of this result - */ - public ResultType getType(); -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/IntegerResult.java b/dice-lang/src/main/java/bjc/dicelang/ast/IntegerResult.java deleted file mode 100644 index ce61d38..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/IntegerResult.java +++ /dev/null @@ -1,40 +0,0 @@ -package bjc.dicelang.ast; - -/** - * Represents a integer-valued result - * - * @author ben - * - */ -public class IntegerResult implements IResult { - private int value; - - /** - * Create a new integer valued result - * - * @param val - * The value of the result - */ - public IntegerResult(int val) { - value = val; - } - - @Override - public ResultType getType() { - return ResultType.INTEGER; - } - - /** - * Get the value of this result - * - * @return The value of this result - */ - public int getValue() { - return value; - } - - @Override - public String toString() { - return Integer.toString(value); - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/ResultType.java b/dice-lang/src/main/java/bjc/dicelang/ast/ResultType.java deleted file mode 100644 index 9e3b129..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/ResultType.java +++ /dev/null @@ -1,22 +0,0 @@ -package bjc.dicelang.ast; - -/** - * Represents the result of a computation - * - * @author ben - * - */ -public enum ResultType { - /** - * Represents a result that is equivalent to a single integer - */ - INTEGER, - /** - * Represents a result that is an array - */ - ARRAY, - /** - * Represents something not to poke at - */ - DUMMY -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/DiceASTType.java b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/DiceASTType.java deleted file mode 100644 index 9feb461..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/DiceASTType.java +++ /dev/null @@ -1,27 +0,0 @@ -package bjc.dicelang.ast.nodes; - -/** - * An enum to represent the type of node an AST node is - * - * @author ben - * - */ -public enum DiceASTType { - /** - * A node that contains a literal value - */ - LITERAL, - /** - * A node that contains an operator expression - */ - OPERATOR, - /** - * A node that contains a variable reference - */ - VARIABLE; - - @Override - public String toString() { - return this.name().toLowerCase(); - } -} \ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/DiceLiteralNode.java b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/DiceLiteralNode.java deleted file mode 100644 index b398ac6..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/DiceLiteralNode.java +++ /dev/null @@ -1,52 +0,0 @@ -package bjc.dicelang.ast.nodes; - -import bjc.dicelang.IDiceExpression; - -/** - * Represents a literal backed by a dice expression - * - * @author ben - * - */ -public class DiceLiteralNode implements ILiteralDiceNode { - private IDiceExpression expression; - - /** - * Create a new literal from an expression - * - * @param exp - * The expression to attempt to create a literal from - */ - public DiceLiteralNode(IDiceExpression exp) { - expression = exp; - } - - @Override - public boolean canOptimize() { - return expression.canOptimize(); - } - - @Override - public DiceLiteralType getLiteralType() { - return DiceLiteralType.DICE; - } - - /** - * Return the expression being represented - * - * @return The expression being represented - */ - public IDiceExpression getValue() { - return expression; - } - - @Override - public int optimize() { - return expression.optimize(); - } - - @Override - public String toString() { - return expression.toString(); - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/DiceLiteralType.java b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/DiceLiteralType.java deleted file mode 100644 index 41c6b05..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/DiceLiteralType.java +++ /dev/null @@ -1,18 +0,0 @@ -package bjc.dicelang.ast.nodes; - -/** - * Represents the type of literals that can be in an AST - * - * @author ben - * - */ -public enum DiceLiteralType { - /** - * Represents a integral constant - */ - INTEGER, - /** - * Represents a dice literal - */ - DICE; -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/DiceOperatorType.java b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/DiceOperatorType.java deleted file mode 100644 index 7cc1e42..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/DiceOperatorType.java +++ /dev/null @@ -1,29 +0,0 @@ -package bjc.dicelang.ast.nodes; - -/** - * Represents the different type of operators. - * - * Mostly, what distinguishes groups is that all the operators in a group - * have similiar precedence, and operate on similiar things - * - * @author ben - * - */ -public enum DiceOperatorType { - /** - * Represents operators that do math operations - */ - MATH, - /** - * Represents operators that do things with arrays - */ - ARRAY, - /** - * Represents operators that do things with dice - */ - DICE, - /** - * Represents operators that do things with expressions - */ - EXPRESSION; -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/IDiceASTNode.java b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/IDiceASTNode.java deleted file mode 100644 index b7bf9a6..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/IDiceASTNode.java +++ /dev/null @@ -1,23 +0,0 @@ -package bjc.dicelang.ast.nodes; - -/** - * The interface for a node in a dice AST - * - * @author ben - * - */ -public interface IDiceASTNode { - /** - * Get the type of AST node this node is - * - * @return The type of AST node this AST node is - */ - public DiceASTType getType(); - - /** - * Check if this node represents an operator or not - * - * @return Whether or not this node represents an operator - */ - public boolean isOperator(); -} \ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/ILiteralDiceNode.java b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/ILiteralDiceNode.java deleted file mode 100644 index b94bcc8..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/ILiteralDiceNode.java +++ /dev/null @@ -1,73 +0,0 @@ -package bjc.dicelang.ast.nodes; - -/** - * Represents a literal of some type in the AST - * - * @author ben - * - */ -public interface ILiteralDiceNode extends IDiceASTNode { - /** - * Check if a token represents a literal, and if so, what type - * - * @param tok - * The token to check - * @return The type the literal would be if it is one, or null - * otherwise - */ - static DiceLiteralType getLiteralType(String tok) { - String diceGroupOrNumber = "[(?:\\d*d\\d+)(?:\\d+)]"; - - if (tok.matches("\\A" + diceGroupOrNumber + "?" + "c" - + diceGroupOrNumber + "\\Z")) { - return DiceLiteralType.DICE; - } - - String diceGroup = "\\d*d\\d+\\"; - - if (tok.matches("\\A" + diceGroup + "Z")) { - return DiceLiteralType.DICE; - } - - try { - Integer.parseInt(tok); - return DiceLiteralType.INTEGER; - } catch (@SuppressWarnings("unused") NumberFormatException nfex) { - // We don't care about details - return null; - } - } - - /** - * Check if this node can be optimized to a constant - * - * @return Whether or not this node can be optimized to a constant - * @see bjc.dicelang.IDiceExpression#canOptimize() - */ - boolean canOptimize(); - - /** - * Get the type of literal this node represents - * - * @return The type of literal this node represents - */ - DiceLiteralType getLiteralType(); - - @Override - default DiceASTType getType() { - return DiceASTType.LITERAL; - } - - @Override - default boolean isOperator() { - return false; - } - - /** - * Optimize this node to a constant if possible - * - * @return This node in constant form if possible - * @see bjc.dicelang.IDiceExpression#optimize() - */ - int optimize(); -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/IntegerLiteralNode.java b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/IntegerLiteralNode.java deleted file mode 100644 index 3d43bb1..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/IntegerLiteralNode.java +++ /dev/null @@ -1,50 +0,0 @@ -package bjc.dicelang.ast.nodes; - -/** - * Represents an integer literal of some kind - * - * @author ben - * - */ -public class IntegerLiteralNode implements ILiteralDiceNode { - private int value; - - /** - * Create a new integer literal from the given number - * - * @param val - * The value this node represents - */ - public IntegerLiteralNode(int val) { - value = val; - } - - @Override - public boolean canOptimize() { - return true; - } - - @Override - public DiceLiteralType getLiteralType() { - return DiceLiteralType.INTEGER; - } - - /** - * Get the value this node represents - * - * @return The integer value of this node - */ - public int getValue() { - return value; - } - - @Override - public int optimize() { - return value; - } - - @Override - public String toString() { - return Integer.toString(value); - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/OperatorDiceNode.java b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/OperatorDiceNode.java deleted file mode 100644 index 7c0a29d..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/OperatorDiceNode.java +++ /dev/null @@ -1,110 +0,0 @@ -package bjc.dicelang.ast.nodes; - -import static bjc.dicelang.ast.nodes.DiceOperatorType.DICE; -import static bjc.dicelang.ast.nodes.DiceOperatorType.EXPRESSION; -import static bjc.dicelang.ast.nodes.DiceOperatorType.MATH; - -/** - * A node that represents an operator - * - * @author ben - * - */ -public enum OperatorDiceNode implements IDiceASTNode { - /** - * Represents adding two nodes - */ - ADD(MATH), - /** - * Represents dividing two nodes - */ - DIVIDE(MATH), - /** - * Represents multiplying two nodes - */ - MULTIPLY(MATH), - /** - * Represents subtracting two nodes - */ - SUBTRACT(MATH), - /** - * Representings combining two node values together - */ - COMPOUND(DICE), - /** - * Represents using one node a variable number of times - */ - GROUP(DICE), - /** - * Represents constructing an array from a sequence of expressions - */ - ARRAY(DiceOperatorType.ARRAY), - /** - * Represents assigning one node to another - */ - ASSIGN(EXPRESSION), - /** - * Represents evaluating one expression in the context of another - */ - LET(EXPRESSION); - - /** - * 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": - case "group": - return GROUP; - case "c": - case "compound": - return COMPOUND; - case "=>": - return LET; - case "[]": - return ARRAY; - default: - throw new IllegalArgumentException( - s + " is not a valid operator node"); - } - } - - /** - * Represents the group of operator this operator is sorted into. - * - */ - public final DiceOperatorType type; - - private OperatorDiceNode(DiceOperatorType ty) { - type = ty; - } - - @Override - public DiceASTType getType() { - return DiceASTType.OPERATOR; - } - - /* - * (non-Javadoc) - * - * @see bjc.utils.dice.ast.IDiceASTNode#isOperator() - */ - @Override - public boolean isOperator() { - return true; - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/VariableDiceNode.java b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/VariableDiceNode.java deleted file mode 100644 index da66608..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/VariableDiceNode.java +++ /dev/null @@ -1,101 +0,0 @@ -package bjc.dicelang.ast.nodes; - -/** - * A node that represents a reference to a variable - * - * @author ben - * - */ -public class VariableDiceNode implements IDiceASTNode { - /** - * The variable referenced by this node - */ - private String variableName; - - /** - * Create a new node representing the specified variable - * - * @param varName - * The name of the variable being referenced - */ - public VariableDiceNode(String varName) { - this.variableName = varName; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - // Handle special cases - if (this == obj) { - return true; - } else if (obj == null) { - return false; - } else if (getClass() != obj.getClass()) { - return false; - } else { - VariableDiceNode other = (VariableDiceNode) obj; - - if (variableName == null) { - if (other.variableName != null) { - return false; - } - } else if (!variableName.equals(other.variableName)) { - return false; - } - - return true; - } - } - - @Override - public DiceASTType getType() { - return DiceASTType.VARIABLE; - } - - /** - * Get the variable referenced by this AST node - * - * @return the variable referenced by this AST node - */ - public String getVariable() { - return variableName; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((variableName == null) ? 0 : variableName.hashCode()); - return result; - } - - /* - * (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 variableName; - } -} \ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/package-info.java b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/package-info.java deleted file mode 100644 index f0f7366..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Classes for nodes in the dice-lang AST - * - * @author ben - * - */ -package bjc.dicelang.ast.nodes; \ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/optimization/ArithmeticCollapser.java b/dice-lang/src/main/java/bjc/dicelang/ast/optimization/ArithmeticCollapser.java deleted file mode 100644 index 960fbf7..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/optimization/ArithmeticCollapser.java +++ /dev/null @@ -1,50 +0,0 @@ -package bjc.dicelang.ast.optimization; - -import java.util.function.BinaryOperator; - -import bjc.utils.funcdata.IList; -import bjc.utils.funcdata.ITree; -import bjc.utils.funcdata.Tree; - -import bjc.dicelang.ast.DiceASTUtils; -import bjc.dicelang.ast.nodes.DiceASTType; -import bjc.dicelang.ast.nodes.IDiceASTNode; -import bjc.dicelang.ast.nodes.ILiteralDiceNode; -import bjc.dicelang.ast.nodes.IntegerLiteralNode; -import bjc.dicelang.ast.nodes.OperatorDiceNode; - -class ArithmeticCollapser { - private BinaryOperator reducer; - private OperatorDiceNode type; - - public ArithmeticCollapser(BinaryOperator reducr, - OperatorDiceNode typ) { - reducer = reducr; - this.type = typ; - } - - public ITree collapse( - IList> children) { - boolean allConstant = children.allMatch((subtree) -> { - return subtree.transformHead((node) -> { - if (node.getType() == DiceASTType.LITERAL) { - return ((ILiteralDiceNode) node).canOptimize(); - } - - return false; - }); - }); - - if (!allConstant) { - return new Tree<>(type, children); - } - - int initState = DiceASTUtils.literalToInteger(children.first()); - - return children.tail().reduceAux(initState, - (currentNode, state) -> { - return reducer.apply(state, - DiceASTUtils.literalToInteger(currentNode)); - }, (state) -> new Tree<>(new IntegerLiteralNode(state))); - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/optimization/ConstantCollapser.java b/dice-lang/src/main/java/bjc/dicelang/ast/optimization/ConstantCollapser.java deleted file mode 100644 index 95badd2..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/optimization/ConstantCollapser.java +++ /dev/null @@ -1,91 +0,0 @@ -package bjc.dicelang.ast.optimization; - -import bjc.utils.funcdata.IList; -import bjc.utils.funcdata.ITree; -import bjc.utils.funcdata.Tree; - -import bjc.dicelang.ComplexDice; -import bjc.dicelang.ast.DiceASTUtils; -import bjc.dicelang.ast.nodes.IDiceASTNode; -import bjc.dicelang.ast.nodes.IntegerLiteralNode; -import bjc.dicelang.ast.nodes.OperatorDiceNode; - -/** - * Collapses operations with constants to constants - * - * @author ben - * - */ -public class ConstantCollapser implements IOptimizationPass { - private static final ArithmeticCollapser additionCollapser = new ArithmeticCollapser( - (left, right) -> left + right, OperatorDiceNode.ADD); - - private static final ArithmeticCollapser divideCollapser = new ArithmeticCollapser( - (left, right) -> left / right, OperatorDiceNode.DIVIDE); - - private static final ArithmeticCollapser multiplyCollapser = new ArithmeticCollapser( - (left, right) -> left * right, OperatorDiceNode.MULTIPLY); - - private static final ArithmeticCollapser subtractCollapser = new ArithmeticCollapser( - (left, right) -> left - right, OperatorDiceNode.SUBTRACT); - - private static final ArithmeticCollapser compoundCollapser = new ArithmeticCollapser( - (left, right) -> Integer.parseInt( - Integer.toString(left) + Integer.toString(left)), - OperatorDiceNode.COMPOUND); - - @Override - public ITree optimizeLeaf(IDiceASTNode leafNode) { - // We don't do anything special here - return new Tree<>(leafNode); - } - - @Override - public ITree optimizeOperator(IDiceASTNode operator, - IList> children) { - if (!operator.isOperator()) { - return new Tree<>(operator, children); - } - - switch ((OperatorDiceNode) operator) { - case ADD: - return additionCollapser.collapse(children); - case DIVIDE: - return divideCollapser.collapse(children); - case MULTIPLY: - return multiplyCollapser.collapse(children); - case SUBTRACT: - return subtractCollapser.collapse(children); - case COMPOUND: - return compoundCollapser.collapse(children); - case GROUP: - if (children.getSize() != 2) { - return new Tree<>(operator, children); - } - - ComplexDice dice = new ComplexDice( - DiceASTUtils.literalToExpression( - children.getByIndex(0)), - DiceASTUtils.literalToExpression( - children.getByIndex(1))); - - if (dice.canOptimize()) { - return new Tree<>( - new IntegerLiteralNode(dice.optimize())); - } - - return new Tree<>(operator, children); - case ARRAY: - if (children.getSize() != 1) { - return new Tree<>(operator, children); - } - - return children.first(); - case ASSIGN: - case LET: - default: - // We don't optimize these operators - return new Tree<>(operator, children); - } - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/optimization/IOptimizationPass.java b/dice-lang/src/main/java/bjc/dicelang/ast/optimization/IOptimizationPass.java deleted file mode 100644 index 36b03f1..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/optimization/IOptimizationPass.java +++ /dev/null @@ -1,35 +0,0 @@ -package bjc.dicelang.ast.optimization; - -import bjc.utils.funcdata.IList; -import bjc.utils.funcdata.ITree; - -import bjc.dicelang.ast.nodes.IDiceASTNode; - -/** - * Represents a pass of optimizations over a dice AST - * - * @author ben - * - */ -public interface IOptimizationPass { - /** - * Optimize a leaf in the tree - * - * @param leafNode - * The node to optimize - * @return The optimized node - */ - public ITree optimizeLeaf(IDiceASTNode leafNode); - - /** - * Optimize an operator in an AST node - * - * @param operator - * The operator being optimized - * @param children - * The children of the operator being optimized - * @return The optimized node - */ - public ITree optimizeOperator(IDiceASTNode operator, - IList> children); -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/optimization/OperationCondenser.java b/dice-lang/src/main/java/bjc/dicelang/ast/optimization/OperationCondenser.java deleted file mode 100644 index f646a17..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/optimization/OperationCondenser.java +++ /dev/null @@ -1,107 +0,0 @@ -package bjc.dicelang.ast.optimization; - -import bjc.utils.data.IHolder; -import bjc.utils.data.Identity; -import bjc.utils.funcdata.ITree; -import bjc.utils.funcdata.TopDownTransformResult; -import bjc.utils.funcdata.Tree; - -import bjc.dicelang.ast.nodes.DiceASTType; -import bjc.dicelang.ast.nodes.IDiceASTNode; -import bjc.dicelang.ast.nodes.OperatorDiceNode; - -/** - * Condenses chained operations into a single level - * - * @author ben - * - */ -public class OperationCondenser { - /** - * Condense chained similiar operations into a single level - * - * @param ast - * The AST to condense - * @return The condensed AST - */ - public static ITree condense(ITree ast) { - return ast.topDownTransform(OperationCondenser::pickNode, - OperationCondenser::doCondense); - } - - private static ITree doCondense( - ITree ast) { - OperatorDiceNode operation = ast - .transformHead((node) -> (OperatorDiceNode) node); - - IHolder canCondense = new Identity<>(true); - - ast.doForChildren((child) -> { - if (canCondense.getValue()) { - canCondense.replace(child.transformHead((node) -> { - if (node.getType() == DiceASTType.OPERATOR) { - if (operation.equals(node)) { - return true; - } - - return false; - } - - return true; - })); - } - }); - - if (!canCondense.getValue()) { - return ast; - } - - ITree condensedAST = new Tree<>(operation); - - ast.doForChildren((child) -> { - if (child.getHead().getType() == DiceASTType.OPERATOR) { - child.doForChildren((subChild) -> { - condensedAST.addChild(subChild); - }); - } else { - condensedAST.addChild(child); - } - }); - - return condensedAST; - } - - private static TopDownTransformResult pickNode(IDiceASTNode node) { - switch (node.getType()) { - case LITERAL: - return TopDownTransformResult.SKIP; - case OPERATOR: - return pickOperator((OperatorDiceNode) node); - case VARIABLE: - return TopDownTransformResult.SKIP; - default: - throw new UnsupportedOperationException( - "Attempted to traverse unknown node type " + node); - } - } - - private static TopDownTransformResult pickOperator( - OperatorDiceNode node) { - switch (node) { - case ADD: - case MULTIPLY: - case SUBTRACT: - case DIVIDE: - case COMPOUND: - return TopDownTransformResult.PUSHDOWN; - case ARRAY: - case ASSIGN: - case GROUP: - case LET: - return TopDownTransformResult.PASSTHROUGH; - default: - throw new UnsupportedOperationException( - "Attempted to traverse unknown operator " + node); - } - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/optimization/package-info.java b/dice-lang/src/main/java/bjc/dicelang/ast/optimization/package-info.java deleted file mode 100644 index 6f75bf9..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/optimization/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Contains optimizations on dice ASTs - * - * @author ben - * - */ -package bjc.dicelang.ast.optimization; \ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/package-info.java b/dice-lang/src/main/java/bjc/dicelang/ast/package-info.java deleted file mode 100644 index f6352aa..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/ast/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -/** - * New implementation of AST for dice-lang - * - * @author ben - * - */ -package bjc.dicelang.ast; \ No newline at end of file -- cgit v1.2.3