diff options
Diffstat (limited to 'dice-lang/src/main/java/bjc')
24 files changed, 555 insertions, 1271 deletions
diff --git a/dice-lang/src/main/java/bjc/dicelang/IDiceExpression.java b/dice-lang/src/main/java/bjc/dicelang/IDiceExpression.java index c6aa0a7..16e1761 100644 --- a/dice-lang/src/main/java/bjc/dicelang/IDiceExpression.java +++ b/dice-lang/src/main/java/bjc/dicelang/IDiceExpression.java @@ -1,5 +1,7 @@ package bjc.dicelang; +import bjc.utils.funcutils.StringUtils; + /** * An expression for something that can be rolled like a polyhedral die * @@ -37,4 +39,37 @@ public interface IDiceExpression { public default boolean canOptimize() { return false; } + + /** + * Parse this node into an expression + * @param exp The string to convert to an expression + * + * @return The node in expression form + */ + static IDiceExpression toExpression(String exp) { + String literalData = exp; + + if (StringUtils.containsInfixOperator(literalData, "c")) { + String[] strangs = literalData.split("c"); + + return new CompoundDice(strangs); + } else if (StringUtils.containsInfixOperator(literalData, + "d")) { + /* + * Handle dice groups + */ + return ComplexDice.fromString(literalData); + } else { + try { + return new ScalarDie(Integer.parseInt(literalData)); + } catch (NumberFormatException nfex) { + UnsupportedOperationException usex = new UnsupportedOperationException( + "Found malformed leaf token " + exp); + + usex.initCause(nfex); + + throw usex; + } + } + } }
\ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTEvaluator.java b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTEvaluator.java new file mode 100644 index 0000000..93b56de --- /dev/null +++ b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTEvaluator.java @@ -0,0 +1,261 @@ +package bjc.dicelang.ast; + +import java.util.function.BinaryOperator; + +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; + +import bjc.utils.data.GenHolder; +import bjc.utils.data.IPair; +import bjc.utils.data.Pair; +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.IFunctionalMap; +import bjc.utils.funcdata.bst.ITreePart.TreeLinearizationMethod; +import bjc.utils.parserutils.AST; + +/** + * Evaluate a dice AST to an integer value + * + * @author ben + * + */ +public class DiceASTEvaluator { + private static final class ArithmeticCollapser + implements IOperatorCollapser { + private OperatorDiceNode type; + + private BinaryOperator<Integer> valueOp; + + public ArithmeticCollapser(OperatorDiceNode type, + BinaryOperator<Integer> valueOp) { + this.type = type; + this.valueOp = valueOp; + } + + @Override + public IPair<Integer, AST<IDiceASTNode>> apply( + IPair<Integer, AST<IDiceASTNode>> leftNode, + IPair<Integer, AST<IDiceASTNode>> rightNode) { + return leftNode.merge((leftValue, leftAST) -> { + return rightNode.merge((rightValue, rightAST) -> { + if (type == OperatorDiceNode.DIVIDE + && rightValue == 0) { + throw new ArithmeticException( + "Attempted to divide by zero. The AST of the problem expression is " + + rightAST); + } + + return new Pair<>(valueOp.apply(leftValue, rightValue), + new AST<>(type, leftAST, rightAST)); + }); + }); + } + } + + /** + * 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 int evaluateAST(AST<IDiceASTNode> expression, + IFunctionalMap<String, AST<IDiceASTNode>> enviroment) { + IFunctionalMap<IDiceASTNode, IOperatorCollapser> collapsers = + buildOperations(enviroment); + + return expression.collapse((node) -> { + return evaluateLeaf(node, enviroment); + }, collapsers::get, (pair) -> { + return pair.merge((left, right) -> left); + }); + } + + /** + * 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 IFunctionalMap<IDiceASTNode, IOperatorCollapser> + buildOperations( + IFunctionalMap<String, AST<IDiceASTNode>> enviroment) { + IFunctionalMap<IDiceASTNode, IOperatorCollapser> operatorCollapsers = + new FunctionalMap<>(); + + operatorCollapsers.put(OperatorDiceNode.ADD, + new ArithmeticCollapser(OperatorDiceNode.ADD, + (left, right) -> left + right)); + + operatorCollapsers.put(OperatorDiceNode.SUBTRACT, + new ArithmeticCollapser(OperatorDiceNode.SUBTRACT, + (left, right) -> left - right)); + + operatorCollapsers.put(OperatorDiceNode.MULTIPLY, + new ArithmeticCollapser(OperatorDiceNode.MULTIPLY, + (left, right) -> left * right)); + + operatorCollapsers.put(OperatorDiceNode.DIVIDE, + new ArithmeticCollapser(OperatorDiceNode.DIVIDE, + (left, right) -> left / right)); + + operatorCollapsers.put(OperatorDiceNode.ASSIGN, (left, right) -> { + return parseBinding(enviroment, left, right); + }); + + operatorCollapsers.put(OperatorDiceNode.COMPOUND, + DiceASTEvaluator::parseCompound); + + operatorCollapsers.put(OperatorDiceNode.GROUP, + DiceASTEvaluator::parseGroup); + + return operatorCollapsers; + } + + private static IPair<Integer, AST<IDiceASTNode>> parseBinding( + IFunctionalMap<String, AST<IDiceASTNode>> enviroment, + IPair<Integer, AST<IDiceASTNode>> left, + IPair<Integer, AST<IDiceASTNode>> right) { + return left.merge((leftValue, leftAST) -> { + return right.merge((rightValue, rightAST) -> { + String variableName = leftAST.applyToHead((node) -> { + if (node.getType() != DiceASTType.VARIABLE) { + throw new UnsupportedOperationException( + "Attempted to assign to '" + node + + "' which is not a variable"); + } + + return ((VariableDiceNode) node).getVariable(); + }); + + GenHolder<Boolean> selfReference = new GenHolder<>(false); + + DiceASTReferenceChecker refChecker = + new DiceASTReferenceChecker(selfReference, + variableName); + + rightAST.traverse(TreeLinearizationMethod.PREORDER, + refChecker); + + // Ignore meta-variable that'll be auto-frozen to restore + // definition sanity + if (selfReference.unwrap((bool) -> bool) + && !variableName.equals("last")) { + throw new UnsupportedOperationException( + "Variable '" + variableName + + "' references itself. Problematic definition: \n\t" + + rightAST); + } + + if (!variableName.equals("last")) { + enviroment.put(variableName, rightAST); + } else { + // Do nothing, last is a auto-handled meta-variable + } + + return new Pair<>(rightValue, new AST<>( + OperatorDiceNode.ASSIGN, leftAST, rightAST)); + }); + }); + } + + private static IPair<Integer, AST<IDiceASTNode>> parseCompound( + IPair<Integer, AST<IDiceASTNode>> leftNode, + IPair<Integer, AST<IDiceASTNode>> rightNode) { + return leftNode.merge((leftValue, leftAST) -> { + return rightNode.merge((rightValue, rightAST) -> { + int compoundValue = + Integer.parseInt(Integer.toString(leftValue) + + Integer.toString(rightValue)); + + return new Pair<>(compoundValue, new AST<>( + OperatorDiceNode.COMPOUND, leftAST, rightAST)); + }); + }); + } + + private static IPair<Integer, AST<IDiceASTNode>> parseGroup( + IPair<Integer, AST<IDiceASTNode>> leftNode, + IPair<Integer, AST<IDiceASTNode>> rightNode) { + return leftNode.merge((leftValue, leftAST) -> { + return rightNode.merge((rightValue, rightAST) -> { + if (leftValue < 0) { + throw new UnsupportedOperationException( + "Can't attempt to roll a negative number of dice." + + " The problematic AST is " + + leftAST); + } else if (rightValue < 1) { + throw new UnsupportedOperationException( + "Can't roll dice with less than one side." + + " The problematic AST is " + + rightAST); + } + + int rolledValue = + new ComplexDice(leftValue, rightValue).roll(); + + return new Pair<>(rolledValue, new AST<>( + OperatorDiceNode.GROUP, leftAST, rightAST)); + }); + }); + } + + private static IPair<Integer, AST<IDiceASTNode>> evaluateLeaf( + IDiceASTNode leafNode, + IFunctionalMap<String, AST<IDiceASTNode>> enviroment) { + int returnedValue = 0; + + switch (leafNode.getType()) { + case LITERAL: + returnedValue = evaluateLiteral(leafNode, returnedValue); + + break; + case VARIABLE: + String variableName = + ((VariableDiceNode) leafNode).getVariable(); + + returnedValue = evaluateAST(enviroment.get(variableName), + enviroment); + break; + case OPERATOR: + throw new UnsupportedOperationException( + "Operator '" + leafNode + "' cannot be a leaf."); + default: + break; + + } + + return new Pair<>(returnedValue, new AST<>(leafNode)); + } + + private static int evaluateLiteral(IDiceASTNode leafNode, + int returnedValue) { + DiceLiteralType literalType = + ((ILiteralDiceNode) leafNode).getLiteralType(); + + switch (literalType) { + case DICE: + returnedValue = ((DiceLiteralNode) leafNode).getValue(); + break; + case INTEGER: + returnedValue = ((IntegerLiteralNode) leafNode).getValue(); + break; + default: + throw new UnsupportedOperationException("Literal value '" + + leafNode + "' is of a type (" + literalType + + ") not currently supported."); + } + return returnedValue; + } +} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTParser.java b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTParser.java index db2ba98..f6500e3 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTParser.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTParser.java @@ -2,10 +2,14 @@ package bjc.dicelang.ast; import java.util.InputMismatchException; -import bjc.dicelang.old.ast.nodes.IDiceASTNode; -import bjc.dicelang.old.ast.nodes.LiteralDiceNode; -import bjc.dicelang.old.ast.nodes.OperatorDiceNode; -import bjc.dicelang.old.ast.nodes.VariableDiceNode; +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; import bjc.utils.funcdata.IFunctionalList; import bjc.utils.parserutils.AST; import bjc.utils.parserutils.TreeConstructor; @@ -26,8 +30,8 @@ public class DiceASTParser { */ public static AST<IDiceASTNode> createFromString( IFunctionalList<String> tokens) { - AST<String> rawTokens = - TreeConstructor.constructTree(tokens, (token) -> { + AST<String> rawTokens = TreeConstructor.constructTree(tokens, + (token) -> { return isOperatorNode(token); }, (operator) -> false, null); // The last argument is valid because there are no special @@ -48,8 +52,22 @@ public class DiceASTParser { } private static IDiceASTNode convertLeafNode(String leafNode) { - if (LiteralDiceNode.isLiteral(leafNode)) { - return new LiteralDiceNode(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."); + } } return new VariableDiceNode(leafNode); diff --git a/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTReferenceChecker.java b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTReferenceChecker.java index 3e0ceec..809243a 100644 --- a/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTReferenceChecker.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTReferenceChecker.java @@ -1,10 +1,10 @@ -package bjc.dicelang.old.ast; +package bjc.dicelang.ast; import java.util.function.Consumer; -import bjc.dicelang.old.ast.nodes.DiceASTType; -import bjc.dicelang.old.ast.nodes.IDiceASTNode; -import bjc.dicelang.old.ast.nodes.VariableDiceNode; +import bjc.dicelang.ast.nodes.DiceASTType; +import bjc.dicelang.ast.nodes.IDiceASTNode; +import bjc.dicelang.ast.nodes.VariableDiceNode; import bjc.utils.data.IHolder; /** diff --git a/dice-lang/src/main/java/bjc/dicelang/old/ast/IOperatorCollapser.java b/dice-lang/src/main/java/bjc/dicelang/ast/IOperatorCollapser.java index 36243a6..ed818d4 100644 --- a/dice-lang/src/main/java/bjc/dicelang/old/ast/IOperatorCollapser.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/IOperatorCollapser.java @@ -1,9 +1,9 @@ -package bjc.dicelang.old.ast; +package bjc.dicelang.ast; import java.util.function.BinaryOperator; -import bjc.dicelang.old.ast.nodes.IDiceASTNode; -import bjc.utils.data.Pair; +import bjc.dicelang.ast.nodes.IDiceASTNode; +import bjc.utils.data.IPair; import bjc.utils.parserutils.AST; /** @@ -13,6 +13,6 @@ import bjc.utils.parserutils.AST; * */ public interface IOperatorCollapser - extends BinaryOperator<Pair<Integer, AST<IDiceASTNode>>> { + extends BinaryOperator<IPair<Integer, AST<IDiceASTNode>>> { // Just an alias } diff --git a/dice-lang/src/main/java/bjc/dicelang/old/ast/nodes/DiceASTType.java b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/DiceASTType.java index 633d1d9..9feb461 100644 --- a/dice-lang/src/main/java/bjc/dicelang/old/ast/nodes/DiceASTType.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/DiceASTType.java @@ -1,4 +1,4 @@ -package bjc.dicelang.old.ast.nodes; +package bjc.dicelang.ast.nodes; /** * An enum to represent the type of node an AST node is 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 new file mode 100644 index 0000000..82c764d --- /dev/null +++ b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/DiceLiteralNode.java @@ -0,0 +1,57 @@ +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; + } + + /** + * 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() + */ + public boolean canOptimize() { + return expression.canOptimize(); + } + + @Override + public DiceLiteralType getLiteralType() { + return DiceLiteralType.DICE; + } + + /** + * Return a value from the expression being represented + * + * @return A value from the expression being represented + */ + public int getValue() { + return expression.roll(); + } + + /** + * Optimize this node to a constant if possible + * + * @return This node in constant form if possible + * @see bjc.dicelang.IDiceExpression#optimize() + */ + public int optimize() { + return expression.optimize(); + } +} 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 new file mode 100644 index 0000000..41c6b05 --- /dev/null +++ b/dice-lang/src/main/java/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/main/java/bjc/dicelang/ast/nodes/DiceOperatorType.java b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/DiceOperatorType.java new file mode 100644 index 0000000..76aa2e3 --- /dev/null +++ b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/DiceOperatorType.java @@ -0,0 +1,25 @@ +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 dice + */ + DICE, + /** + * Represents operators that do things with expressions + */ + EXPRESSION; +} diff --git a/dice-lang/src/main/java/bjc/dicelang/old/ast/nodes/IDiceASTNode.java b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/IDiceASTNode.java index 579c595..b7bf9a6 100644 --- a/dice-lang/src/main/java/bjc/dicelang/old/ast/nodes/IDiceASTNode.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/IDiceASTNode.java @@ -1,4 +1,4 @@ -package bjc.dicelang.old.ast.nodes; +package bjc.dicelang.ast.nodes; /** * The interface for a node in a dice AST @@ -8,16 +8,16 @@ package bjc.dicelang.old.ast.nodes; */ public interface IDiceASTNode { /** - * Check if this node represents an operator or not + * Get the type of AST node this node is * - * @return Whether or not this node represents an operator + * @return The type of AST node this AST node is */ - public boolean isOperator(); + public DiceASTType getType(); /** - * Get the type of AST node this node is + * Check if this node represents an operator or not * - * @return The type of AST node this AST node is + * @return Whether or not this node represents an operator */ - public DiceASTType getType(); + 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 new file mode 100644 index 0000000..b12b516 --- /dev/null +++ b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/ILiteralDiceNode.java @@ -0,0 +1,54 @@ +package bjc.dicelang.ast.nodes; + +import org.apache.commons.lang3.StringUtils; + +/** + * Represents a literal of some type in the AST + * + * @author ben + * + */ +public interface ILiteralDiceNode extends IDiceASTNode { + @Override + default DiceASTType getType() { + return DiceASTType.LITERAL; + } + + @Override + default boolean isOperator() { + return false; + } + + /** + * Get the type of literal this node represents + * + * @return The type of literal this node represents + */ + DiceLiteralType getLiteralType(); + + /** + * 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) { + if (StringUtils.countMatches(tok, 'c') == 1 + && !tok.equalsIgnoreCase("c")) { + return DiceLiteralType.DICE; + } else if (StringUtils.countMatches(tok, 'd') == 1 + && !tok.equalsIgnoreCase("d")) { + return DiceLiteralType.DICE; + } else { + try { + Integer.parseInt(tok); + return DiceLiteralType.INTEGER; + } catch (@SuppressWarnings("unused") NumberFormatException nfex) { + // We don't care about details + return null; + } + } + } +} 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 new file mode 100644 index 0000000..415f30f --- /dev/null +++ b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/IntegerLiteralNode.java @@ -0,0 +1,35 @@ +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 DiceLiteralType getLiteralType() { + return DiceLiteralType.INTEGER; + } + + /** + * Get the value this node represents + * + * @return The integer value of this node + */ + public int getValue() { + return value; + } +} diff --git a/dice-lang/src/main/java/bjc/dicelang/old/ast/nodes/OperatorDiceNode.java b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/OperatorDiceNode.java index 3c06553..d034943 100644 --- a/dice-lang/src/main/java/bjc/dicelang/old/ast/nodes/OperatorDiceNode.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/OperatorDiceNode.java @@ -1,4 +1,6 @@ -package bjc.dicelang.old.ast.nodes; +package bjc.dicelang.ast.nodes; + +import static bjc.dicelang.ast.nodes.DiceOperatorType.*; // The following classes need to be changed upon addition of a new operator // 1. DiceASTExpression @@ -14,35 +16,41 @@ public enum OperatorDiceNode implements IDiceASTNode { /** * Represents adding two nodes */ - ADD, + ADD(MATH), /** * Represents assigning one node to another */ - ASSIGN, + ASSIGN(EXPRESSION), /** * Representings combining two node values together */ - COMPOUND, + COMPOUND(DICE), /** * Represents dividing two nodes */ - DIVIDE, + DIVIDE(MATH), /** * Represents using one node a variable number of times */ - GROUP, + GROUP(DICE), /** * Represents multiplying two nodes */ - MULTIPLY, + MULTIPLY(MATH), /** * Represents subtracting two nodes */ - SUBTRACT, + SUBTRACT(MATH); + /** - * Represents executing one statement in the context of the other + * Represents the group of operator this operator is sorted into. + * */ - LET; + public final DiceOperatorType type; + + private OperatorDiceNode(DiceOperatorType ty) { + type = ty; + } /** * Create a operator node from a string @@ -64,11 +72,11 @@ public enum OperatorDiceNode implements IDiceASTNode { case "/": return DIVIDE; case "d": + case "group": return GROUP; case "c": + case "compound": return COMPOUND; - case "->": - return LET; default: throw new IllegalArgumentException( s + " is not a valid operator node"); diff --git a/dice-lang/src/main/java/bjc/dicelang/old/ast/nodes/VariableDiceNode.java b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/VariableDiceNode.java index 262f99b..da66608 100644 --- a/dice-lang/src/main/java/bjc/dicelang/old/ast/nodes/VariableDiceNode.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/VariableDiceNode.java @@ -1,7 +1,7 @@ -package bjc.dicelang.old.ast.nodes; +package bjc.dicelang.ast.nodes; /** - * A node that represents a variable reference + * A node that represents a reference to a variable * * @author ben * 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 new file mode 100644 index 0000000..cfa2511 --- /dev/null +++ b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/package-info.java @@ -0,0 +1,6 @@ +/** + * 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/old/ast/DiceASTDefinedChecker.java b/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTDefinedChecker.java deleted file mode 100644 index e279d8e..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTDefinedChecker.java +++ /dev/null @@ -1,61 +0,0 @@ -package bjc.dicelang.old.ast; - -import java.util.Map; -import java.util.function.Consumer; - -import bjc.dicelang.old.ast.nodes.DiceASTType; -import bjc.dicelang.old.ast.nodes.IDiceASTNode; -import bjc.dicelang.old.ast.nodes.VariableDiceNode; -import bjc.utils.data.IHolder; - -/** - * Check if the specified node references a particular variable - * - * @author ben - * - */ -public final class DiceASTDefinedChecker - implements Consumer<IDiceASTNode> { - /** - * This is true if the specified node references the set variable - */ - private IHolder<Boolean> referencesVariable; - - private Map<String, DiceASTExpression> enviroment; - - /** - * Create a new reference checker - * - * @param referencesVar - * The holder of whether the variable is referenced or not - * @param env - * The enviroment to check undefinedness against - */ - public DiceASTDefinedChecker(IHolder<Boolean> referencesVar, - Map<String, DiceASTExpression> env) { - this.referencesVariable = referencesVar; - this.enviroment = env; - } - - @Override - public void accept(IDiceASTNode astNode) { - referencesVariable.transform((bool) -> checkUndefined(astNode)); - } - - /** - * Check if a given AST node references an undefined variable - * - * @param astNode - * The node to check - * @return Whether or not the node directly the variable - */ - private boolean checkUndefined(IDiceASTNode astNode) { - if (astNode.getType() == DiceASTType.VARIABLE) { - VariableDiceNode node = (VariableDiceNode) astNode; - - return !enviroment.containsKey(node.getVariable()); - } - - return false; - } -}
\ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTExpression.java b/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTExpression.java deleted file mode 100644 index e6dca9e..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTExpression.java +++ /dev/null @@ -1,332 +0,0 @@ -package bjc.dicelang.old.ast; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.BinaryOperator; -import java.util.function.Function; - -import bjc.dicelang.ComplexDice; -import bjc.dicelang.IDiceExpression; -import bjc.dicelang.old.ast.nodes.DiceASTType; -import bjc.dicelang.old.ast.nodes.IDiceASTNode; -import bjc.dicelang.old.ast.nodes.LiteralDiceNode; -import bjc.dicelang.old.ast.nodes.OperatorDiceNode; -import bjc.dicelang.old.ast.nodes.VariableDiceNode; -import bjc.utils.data.GenHolder; -import bjc.utils.data.Pair; -import bjc.utils.funcdata.bst.ITreePart.TreeLinearizationMethod; -import bjc.utils.parserutils.AST; - -/** - * An implementation of {@link IDiceExpression} backed by an AST of - * {@link IDiceASTNode}s - * - * @author ben - * - */ -public class DiceASTExpression implements IDiceExpression { - private static final class ArithmeticCollapser - implements IOperatorCollapser { - private OperatorDiceNode type; - - private BinaryOperator<Integer> valueOp; - - public ArithmeticCollapser(OperatorDiceNode type, - BinaryOperator<Integer> valueOp) { - this.type = type; - this.valueOp = valueOp; - } - - @Override - public Pair<Integer, AST<IDiceASTNode>> apply( - Pair<Integer, AST<IDiceASTNode>> leftNode, - Pair<Integer, AST<IDiceASTNode>> rightNode) { - return leftNode.merge((leftValue, leftAST) -> { - return rightNode.merge((rightValue, rightAST) -> { - if (type == OperatorDiceNode.DIVIDE - && rightValue == 0) { - throw new ArithmeticException( - "Attempted to divide by zero. The AST of the problem expression is " - + rightAST); - } - - return new Pair<>(valueOp.apply(leftValue, rightValue), - new AST<>(type, leftAST, rightAST)); - }); - }); - } - } - - private static final class VariableRetriever - implements Function<IDiceASTNode, String> { - @Override - public String apply(IDiceASTNode node) { - if (node.getType() != DiceASTType.VARIABLE) { - throw new UnsupportedOperationException( - "Attempted to assign to something that isn't a variable." - + " This isn't supported yet. The problem node is " - + node); - } - - return ((VariableDiceNode) node).getVariable(); - } - } - - /** - * Build the map of operations to use when collapsing the AST - * - * @param enviroment - * The enviroment to evaluate bindings and such against - * @return The operations to use when collapsing the AST - */ - private static Map<IDiceASTNode, IOperatorCollapser> buildOperations( - Map<String, DiceASTExpression> enviroment) { - Map<IDiceASTNode, IOperatorCollapser> operatorCollapsers = - new HashMap<>(); - - operatorCollapsers.put(OperatorDiceNode.ADD, - new ArithmeticCollapser(OperatorDiceNode.ADD, - (left, right) -> left + right)); - - operatorCollapsers.put(OperatorDiceNode.SUBTRACT, - new ArithmeticCollapser(OperatorDiceNode.SUBTRACT, - (left, right) -> left - right)); - - operatorCollapsers.put(OperatorDiceNode.MULTIPLY, - new ArithmeticCollapser(OperatorDiceNode.MULTIPLY, - (left, right) -> left * right)); - - operatorCollapsers.put(OperatorDiceNode.DIVIDE, - new ArithmeticCollapser(OperatorDiceNode.DIVIDE, - (left, right) -> left / right)); - - operatorCollapsers.put(OperatorDiceNode.ASSIGN, (left, right) -> { - return parseBinding(enviroment, left, right); - }); - - operatorCollapsers.put(OperatorDiceNode.COMPOUND, - DiceASTExpression::parseCompound); - - operatorCollapsers.put(OperatorDiceNode.GROUP, - DiceASTExpression::parseGroup); - - operatorCollapsers.put(OperatorDiceNode.LET, (left, right) -> { - return doLet(left, right); - }); - - return operatorCollapsers; - } - - private static Pair<Integer, AST<IDiceASTNode>> doLet( - Pair<Integer, AST<IDiceASTNode>> left, - Pair<Integer, AST<IDiceASTNode>> right) { - return left.merge((leftValue, leftAST) -> { - return right.merge((rightValue, rightAST) -> { - if (!leftAST - .applyToHead(DiceASTExpression::isAssignNode)) { - // Just ignore the left block then - return new Pair<>(rightValue, rightAST); - } - - return null; - }); - }); - } - - private static Boolean isAssignNode(IDiceASTNode node) { - return node.getType() == DiceASTType.OPERATOR - && node == OperatorDiceNode.ASSIGN; - } - - private static Pair<Integer, AST<IDiceASTNode>> parseBinding( - Map<String, DiceASTExpression> enviroment, - Pair<Integer, AST<IDiceASTNode>> left, - Pair<Integer, AST<IDiceASTNode>> right) { - return left.merge((leftValue, leftAST) -> { - return right.merge((rightValue, rightAST) -> { - String variableName = leftAST - .collapse(new VariableRetriever(), (operator) -> { - throw new UnsupportedOperationException( - "Can only assign to plain variable names. The problem operator is " - + operator); - }, (returnedAST) -> returnedAST); - - GenHolder<Boolean> selfReference = new GenHolder<>(false); - - DiceASTReferenceChecker refChecker = - new DiceASTReferenceChecker(selfReference, - variableName); - - rightAST.traverse(TreeLinearizationMethod.PREORDER, - refChecker); - - // Ignore meta-variable that'll be auto-frozen to restore - // definition sanity - if (selfReference.unwrap((bool) -> bool) - && !variableName.equals("last")) { - throw new UnsupportedOperationException( - "Variable '" + variableName - + "' references itself. Problematic definition: \n\t" - + rightAST); - } - - if (!variableName.equals("last")) { - enviroment.put(variableName, - new DiceASTExpression(rightAST, enviroment)); - } else { - // Do nothing, last is a auto-handled meta-variable - } - - return new Pair<>(rightValue, new AST<>( - OperatorDiceNode.ASSIGN, leftAST, rightAST)); - }); - }); - } - - private static Pair<Integer, AST<IDiceASTNode>> parseCompound( - Pair<Integer, AST<IDiceASTNode>> leftNode, - Pair<Integer, AST<IDiceASTNode>> rightNode) { - return leftNode.merge((leftValue, leftAST) -> { - return rightNode.merge((rightValue, rightAST) -> { - int compoundValue = - Integer.parseInt(Integer.toString(leftValue) - + Integer.toString(rightValue)); - - return new Pair<>(compoundValue, new AST<>( - OperatorDiceNode.COMPOUND, leftAST, rightAST)); - }); - }); - } - - private static Pair<Integer, AST<IDiceASTNode>> parseGroup( - Pair<Integer, AST<IDiceASTNode>> leftNode, - Pair<Integer, AST<IDiceASTNode>> rightNode) { - return leftNode.merge((leftValue, leftAST) -> { - return rightNode.merge((rightValue, rightAST) -> { - if (leftValue < 0) { - throw new UnsupportedOperationException( - "Can't attempt to roll a negative number of dice." - + " The problematic AST is " - + leftAST); - } else if (rightValue < 1) { - throw new UnsupportedOperationException( - "Can't roll dice with less than one side." - + " The problematic AST is " - + rightAST); - } - - int rolledValue = - new ComplexDice(leftValue, rightValue).roll(); - - return new Pair<>(rolledValue, new AST<>( - OperatorDiceNode.GROUP, leftAST, rightAST)); - }); - }); - } - - /** - * The AST this expression will evaluate - */ - private AST<IDiceASTNode> ast; - - /** - * The enviroment to evaluate bindings and such against - */ - private Map<String, DiceASTExpression> env; - - /** - * Create a new dice expression backed by an AST - * - * @param ast - * The AST backing this expression - * @param env - * The enviroment to evaluate bindings against - */ - public DiceASTExpression(AST<IDiceASTNode> ast, - Map<String, DiceASTExpression> env) { - this.ast = ast; - this.env = env; - } - - /** - * Expand a leaf AST token into a pair for evaluation - * - * @param leafNode - * The token to evaluate - * @return A pair consisting of the token's value and the AST it - * represents - */ - private Pair<Integer, AST<IDiceASTNode>> evaluateLeaf( - IDiceASTNode leafNode) { - if (leafNode.getType() == DiceASTType.VARIABLE) { - VariableDiceNode node = (VariableDiceNode) leafNode; - - return parseVariable(node); - } else if (leafNode.getType() == DiceASTType.LITERAL) { - LiteralDiceNode node = (LiteralDiceNode) leafNode; - - return node.toParseValue(); - } else { - throw new UnsupportedOperationException("Found leaf operator " - + leafNode + ". These aren't supported."); - } - } - - private Pair<Integer, AST<IDiceASTNode>> parseVariable( - VariableDiceNode leafNode) { - String varName = leafNode.getVariable(); - - if (env.containsKey(varName)) { - return new Pair<>(env.get(varName).roll(), - new AST<>(leafNode)); - } - - // Handle special case for defining variables - return new Pair<>(0, new AST<>(leafNode)); - } - - /** - * Get the AST bound to this expression - * - * @return the ast - */ - public AST<IDiceASTNode> getAst() { - return ast; - } - - /* - * (non-Javadoc) - * - * @see bjc.utils.dice.IDiceExpression#roll() - */ - @Override - public int roll() { - Map<IDiceASTNode, IOperatorCollapser> operations = - buildOperations(env); - - return ast.collapse(this::evaluateLeaf, operations::get, - (returnedValue) -> returnedValue - .merge((left, right) -> left)); - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return ast.toString(); - } - - @Override - public int optimize() { - throw new UnsupportedOperationException( - "Use DiceASTOptimizer for optimizing these"); - } - - @Override - public boolean canOptimize() { - return false; - } -}
\ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTFlattener.java b/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTFlattener.java deleted file mode 100644 index 39c0797..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTFlattener.java +++ /dev/null @@ -1,145 +0,0 @@ -package bjc.dicelang.old.ast; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.BinaryOperator; -import java.util.function.Function; - -import org.apache.commons.lang3.StringUtils; - -import bjc.dicelang.BindingDiceExpression; -import bjc.dicelang.ComplexDice; -import bjc.dicelang.CompoundDice; -import bjc.dicelang.OperatorDiceExpression; -import bjc.dicelang.DiceExpressionType; -import bjc.dicelang.IDiceExpression; -import bjc.dicelang.ReferenceDiceExpression; -import bjc.dicelang.ScalarDie; -import bjc.dicelang.old.ast.nodes.DiceASTType; -import bjc.dicelang.old.ast.nodes.IDiceASTNode; -import bjc.dicelang.old.ast.nodes.LiteralDiceNode; -import bjc.dicelang.old.ast.nodes.OperatorDiceNode; -import bjc.dicelang.old.ast.nodes.VariableDiceNode; -import bjc.utils.parserutils.AST; - -/** - * Flatten an {@link AST} of {@link IDiceASTNode} into a - * {@link IDiceExpression} - * - * @author ben - * - */ -public class DiceASTFlattener { - private static final class NodeCollapser - implements Function<IDiceASTNode, IDiceExpression> { - private Map<String, IDiceExpression> enviroment; - - public NodeCollapser(Map<String, IDiceExpression> env) { - this.enviroment = env; - } - - @Override - public IDiceExpression apply(IDiceASTNode nod) { - if (nod.getType() == DiceASTType.LITERAL) { - return expFromLiteral((LiteralDiceNode) nod); - } else if (nod.getType() == DiceASTType.VARIABLE) { - String varName = ((VariableDiceNode) nod).getVariable(); - - return new ReferenceDiceExpression(varName, enviroment); - } else { - throw new UnsupportedOperationException( - "Attempted to flatten something that can't be" - + " flattened. The culprit is " + nod); - } - } - - /** - * Create a dice expression from a literal token - * - * @param tok - * The token to convert to an expression - * @return The dice expression represented by the token - */ - private static IDiceExpression - expFromLiteral(LiteralDiceNode tok) { - String data = tok.getData(); - - if (data.equals("")) { - throw new UnsupportedOperationException( - "Can't convert a blank token into a literal"); - } - - if (StringUtils.countMatches(data, 'c') == 1 - && !data.equalsIgnoreCase("c")) { - String[] strangs = data.split("c"); - - return new CompoundDice(ComplexDice.fromString(strangs[0]), - ComplexDice.fromString(strangs[1])); - } else if (StringUtils.countMatches(data, 'd') == 1 - && !data.equalsIgnoreCase("d")) { - return ComplexDice.fromString(data); - } else { - return new ScalarDie(Integer.parseInt(data)); - } - } - } - - /** - * Build the operations to use for tree flattening - * - * @param env - * The enviroment the tree will be flattened against - * @return The operations needed for tree flattening - */ - private static Map<IDiceASTNode, BinaryOperator<IDiceExpression>> - buildOperations(Map<String, IDiceExpression> env) { - Map<IDiceASTNode, BinaryOperator<IDiceExpression>> opCollapsers = - new HashMap<>(); - - opCollapsers.put(OperatorDiceNode.ADD, (left, right) -> { - return new OperatorDiceExpression(right, left, - DiceExpressionType.ADD); - }); - opCollapsers.put(OperatorDiceNode.SUBTRACT, (left, right) -> { - return new OperatorDiceExpression(right, left, - DiceExpressionType.SUBTRACT); - }); - opCollapsers.put(OperatorDiceNode.MULTIPLY, (left, right) -> { - return new OperatorDiceExpression(right, left, - DiceExpressionType.MULTIPLY); - }); - opCollapsers.put(OperatorDiceNode.DIVIDE, (left, right) -> { - return new OperatorDiceExpression(right, left, - DiceExpressionType.DIVIDE); - }); - opCollapsers.put(OperatorDiceNode.ASSIGN, (left, right) -> { - return new BindingDiceExpression(left, right, env); - }); - opCollapsers.put(OperatorDiceNode.COMPOUND, (left, right) -> { - return new CompoundDice(left, right); - }); - opCollapsers.put(OperatorDiceNode.GROUP, (left, right) -> { - return new ComplexDice(left, right); - }); - - return opCollapsers; - } - - /** - * Flatten a AST into a dice expression - * - * @param ast - * The AST to flatten - * @param env - * The enviroment to flatten against - * @return The AST, flattened into a dice expression - */ - public static IDiceExpression flatten(AST<IDiceASTNode> ast, - Map<String, IDiceExpression> env) { - Map<IDiceASTNode, BinaryOperator<IDiceExpression>> opCollapsers = - buildOperations(env); - - return ast.collapse(new NodeCollapser(env), opCollapsers::get, - (r) -> r); - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTInliner.java b/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTInliner.java deleted file mode 100644 index 802741f..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTInliner.java +++ /dev/null @@ -1,138 +0,0 @@ -package bjc.dicelang.old.ast; - -import java.util.function.Function; - -import bjc.dicelang.old.ast.nodes.DiceASTType; -import bjc.dicelang.old.ast.nodes.IDiceASTNode; -import bjc.dicelang.old.ast.nodes.VariableDiceNode; -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.FunctionalMap; -import bjc.utils.funcdata.IFunctionalList; -import bjc.utils.funcdata.IFunctionalMap; -import bjc.utils.parserutils.AST; - -/** - * Inline references in a dice AST, replacing variable references with what - * the variables refer to - * - * @author ben - * - */ -public class DiceASTInliner { - private static class NodeInliner - implements Function<IDiceASTNode, AST<IDiceASTNode>> { - private IFunctionalMap<String, AST<IDiceASTNode>> enviroment; - - public NodeInliner(IFunctionalMap<String, AST<IDiceASTNode>> env) { - enviroment = env; - } - - @Override - public AST<IDiceASTNode> apply(IDiceASTNode nod) { - if (nod.getType() == DiceASTType.VARIABLE) { - return expandNode((VariableDiceNode) nod); - } - - return new AST<>(nod); - } - - protected AST<IDiceASTNode> expandNode( - VariableDiceNode variableNode) { - String varName = variableNode.getVariable(); - - if (!enviroment.containsKey(varName)) { - throw new IllegalArgumentException( - "Attempted to freeze reference" - + " to an undefined variable " + varName); - } - - return enviroment.get(varName); - } - } - - private static final class SelectiveInliner extends NodeInliner { - - private IFunctionalList<String> variableNames; - - public SelectiveInliner( - IFunctionalMap<String, AST<IDiceASTNode>> env, - IFunctionalList<String> varNames) { - super(env); - - variableNames = varNames; - } - - @Override - protected AST<IDiceASTNode> expandNode( - VariableDiceNode variableNode) { - if (variableNames.contains(variableNode.getVariable())) { - return super.expandNode(variableNode); - } - - return new AST<>(variableNode); - } - } - - /** - * Inline the references in an AST - * - * @param tree - * The tree to inline references in - * @param env - * The enviroment to get reference values from - * @return The tree with references inlined - */ - public static AST<IDiceASTNode> inlineAST(AST<IDiceASTNode> tree, - IFunctionalMap<String, AST<IDiceASTNode>> env) { - return selectiveInline(tree, env); - } - - /** - * Inline the references in an expression backed by an AST - * - * @param tree - * The tree-backed expression to inline references in - * @param env - * The enviroment to get reference values from - * @return The tree with references inlined - */ - public static AST<IDiceASTNode> inlineAST(DiceASTExpression tree, - FunctionalMap<String, DiceASTExpression> env) { - return inlineAST(tree.getAst(), - env.mapValues(expression -> expression.getAst())); - } - - /** - * Inline references to specified variables - * - * @param tree - * The tree-backed expression to inline references in - * @param env - * The enviroment to resolve variables against - * @param varNames - * The names of the variables to inline - * @return An AST with the specified variables inlined - */ - public static AST<IDiceASTNode> selectiveInline(AST<IDiceASTNode> tree, - IFunctionalMap<String, AST<IDiceASTNode>> env, - String... varNames) { - return selectiveInline(tree, env, new FunctionalList<>(varNames)); - } - - /** - * Inline references to specified variables - * - * @param tree - * The tree-backed expression to inline references in - * @param env - * The enviroment to resolve variables against - * @param varNames - * The names of the variables to inline - * @return An AST with the specified variables inline - */ - public static AST<IDiceASTNode> selectiveInline(AST<IDiceASTNode> tree, - IFunctionalMap<String, AST<IDiceASTNode>> env, - IFunctionalList<String> varNames) { - return tree.flatMapTree(new SelectiveInliner(env, varNames)); - } -}
\ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTParser.java b/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTParser.java deleted file mode 100644 index c9b48c8..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/old/ast/DiceASTParser.java +++ /dev/null @@ -1,119 +0,0 @@ -package bjc.dicelang.old.ast; - -import java.util.Deque; -import java.util.LinkedList; -import java.util.function.Function; - -import bjc.dicelang.old.ast.nodes.IDiceASTNode; -import bjc.dicelang.old.ast.nodes.LiteralDiceNode; -import bjc.dicelang.old.ast.nodes.OperatorDiceNode; -import bjc.dicelang.old.ast.nodes.VariableDiceNode; -import bjc.utils.data.IPair; -import bjc.utils.data.Pair; -import bjc.utils.funcdata.FunctionalStringTokenizer; -import bjc.utils.funcdata.IFunctionalList; -import bjc.utils.funcutils.ListUtils; -import bjc.utils.parserutils.AST; -import bjc.utils.parserutils.ShuntingYard; -import bjc.utils.parserutils.TreeConstructor; - -/** - * Create an AST from a string expression - * - * @author ben - * - */ -public class DiceASTParser { - private static final class NodeBaker - implements Function<String, IDiceASTNode> { - @Override - public IDiceASTNode apply(String tok) { - if (isOperator(tok)) { - return OperatorDiceNode.fromString(tok); - } else if (LiteralDiceNode.isLiteral(tok)) { - return new LiteralDiceNode(tok); - } else { - return new VariableDiceNode(tok); - } - } - } - - /** - * The yard to use for shunting expressions - */ - private static ShuntingYard<String> yard; - - static { - yard = new ShuntingYard<>(); - - yard.addOp("d", 5); // dice operator: use for creating variable - // size dice groups - yard.addOp("c", 6); // compound operator: use for creating compound - // dice from expressions - yard.addOp(":=", 0); // binding operator: Bind a name to a variable - // expression - } - - /** - * Build an AST from a string expression - * - * @param exp - * The string to build from - * @return An AST built from the passed in string - */ - public static AST<IDiceASTNode> buildAST(String exp) { - IFunctionalList<String> tokens = FunctionalStringTokenizer - .fromString(exp).toList(); - - Deque<IPair<String, String>> ops = new LinkedList<>(); - - ops.add(new Pair<>("+", "\\+")); - ops.add(new Pair<>("-", "-")); - ops.add(new Pair<>("*", "\\*")); - ops.add(new Pair<>("/", "/")); - ops.add(new Pair<>(":=", ":=")); - - IFunctionalList<String> semiExpandedTokens = ListUtils - .splitTokens(tokens, ops); - - ops = new LinkedList<>(); - - ops.add(new Pair<>("(", "\\(")); - ops.add(new Pair<>(")", "\\)")); - - IFunctionalList<String> fullyExpandedTokens = ListUtils - .deAffixTokens(semiExpandedTokens, ops); - - IFunctionalList<String> shunted = yard.postfix(fullyExpandedTokens, - (s) -> s); - - AST<String> rawAST = TreeConstructor.constructTree(shunted, - DiceASTParser::isOperator); - - AST<IDiceASTNode> bakedAST = rawAST.transmuteAST(new NodeBaker()); - - return bakedAST; - } - - /** - * Check if a token represents an operator - * - * @param tok - * The token to check if it represents an operator - * @return Whether or not the token represents an operator - */ - private static boolean isOperator(String tok) { - switch (tok) { - case ":=": - case "+": - case "-": - case "*": - case "/": - case "c": - case "d": - return true; - default: - return false; - } - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/old/ast/nodes/LiteralDiceNode.java b/dice-lang/src/main/java/bjc/dicelang/old/ast/nodes/LiteralDiceNode.java deleted file mode 100644 index 46c84d0..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/old/ast/nodes/LiteralDiceNode.java +++ /dev/null @@ -1,217 +0,0 @@ -package bjc.dicelang.old.ast.nodes; - -import org.apache.commons.lang3.StringUtils; - -import bjc.dicelang.ComplexDice; -import bjc.dicelang.CompoundDice; -import bjc.dicelang.IDiceExpression; -import bjc.dicelang.ScalarDie; -import bjc.utils.data.Pair; -import bjc.utils.parserutils.AST; - -/** - * A AST node that represents a literal value - * - * @author ben - * - */ -public class LiteralDiceNode implements IDiceASTNode { - private static boolean isValidInfixOperator(String dat, String op) { - return StringUtils.countMatches(dat, op) == 1 - && !dat.equalsIgnoreCase(op) && !dat.startsWith(op); - } - - /** - * The value contained by this node - */ - private String value; - - /** - * Create a new node with the given value - * - * @param data - * The value to be in this node - */ - public LiteralDiceNode(String data) { - this.value = data; - } - - /** - * Create a new node with the given value - * - * @param val - * The value for this node - */ - public LiteralDiceNode(int val) { - this(Integer.toString(val)); - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } else if (obj == null) { - return false; - } else if (getClass() != obj.getClass()) { - return false; - } else { - LiteralDiceNode other = (LiteralDiceNode) obj; - - if (value == null) { - if (other.value != null) { - return false; - } - } else if (!value.equals(other.value)) { - return false; - } - - return true; - } - } - - /** - * Get the data stored in this AST node - * - * @return the data stored in this AST node - */ - public String getData() { - return value; - } - - @Override - public DiceASTType getType() { - return DiceASTType.LITERAL; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((value == null) ? 0 : value.hashCode()); - return result; - } - - @Override - public boolean isOperator() { - return false; - } - - /** - * Parse this node into an expression - * - * @return The node in expression form - */ - public IDiceExpression toExpression() { - String literalData = this.getData(); - - if (LiteralDiceNode.isValidInfixOperator(literalData, "c")) { - String[] strangs = literalData.split("c"); - - return new CompoundDice(strangs); - } else if (LiteralDiceNode.isValidInfixOperator(literalData, - "d")) { - /* - * Handle dice groups - */ - return ComplexDice.fromString(literalData); - } else { - try { - return new ScalarDie(Integer.parseInt(literalData)); - } catch (NumberFormatException nfex) { - UnsupportedOperationException usex = new UnsupportedOperationException( - "Found malformed leaf token " + this); - - usex.initCause(nfex); - - throw usex; - } - } - } - - /** - * Parse this node into an expression - * - * @return The node as a pair of a sample value and the AST it - * represents - */ - public Pair<Integer, AST<IDiceASTNode>> toParseValue() { - AST<IDiceASTNode> returnedAST = new AST<>(this); - - IDiceExpression expression = toExpression(); - - return new Pair<>(expression.roll(), returnedAST); - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return value; - } - - /** - * Check if this node represents a constant value - * - * @return Whether or not this node represents a constant value - */ - public boolean isConstant() { - try { - Integer.parseInt(value); - return true; - } catch (@SuppressWarnings("unused") NumberFormatException nfex) { - // We don't care about details - return false; - } - } - - /** - * Return the constant value this node represents - * - * @return The constant value of this node - * - * @throws NumberFormatException - * if you call this on a node that doesn't represent a - * constant value - */ - public int toConstant() { - return Integer.parseInt(value); - } - - /** - * Check if a token represents a literal - * - * @param tok - * The token to check - * @return Whether or not the token represents a literal - */ - public static boolean isLiteral(String tok) { - if (StringUtils.countMatches(tok, 'c') == 1 - && !tok.equalsIgnoreCase("c")) { - return true; - } else if (StringUtils.countMatches(tok, 'd') == 1 - && !tok.equalsIgnoreCase("d")) { - return true; - } else { - try { - Integer.parseInt(tok); - return true; - } catch (@SuppressWarnings("unused") NumberFormatException nfex) { - // We don't care about details - return false; - } - } - } -}
\ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/old/ast/nodes/package-info.java b/dice-lang/src/main/java/bjc/dicelang/old/ast/nodes/package-info.java deleted file mode 100644 index f99776f..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/old/ast/nodes/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * This package contains the various Node types in the Dice AST - * @author ben - * - */ -package bjc.dicelang.old.ast.nodes;
\ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/old/ast/optimization/DiceASTOptimizer.java b/dice-lang/src/main/java/bjc/dicelang/old/ast/optimization/DiceASTOptimizer.java deleted file mode 100644 index dead812..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/old/ast/optimization/DiceASTOptimizer.java +++ /dev/null @@ -1,209 +0,0 @@ -package bjc.dicelang.old.ast.optimization; - -import static bjc.dicelang.old.ast.nodes.DiceASTType.*; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.BiFunction; -import java.util.function.BinaryOperator; - -import bjc.dicelang.IDiceExpression; -import bjc.dicelang.old.ast.nodes.DiceASTType; -import bjc.dicelang.old.ast.nodes.IDiceASTNode; -import bjc.dicelang.old.ast.nodes.LiteralDiceNode; -import bjc.dicelang.old.ast.nodes.OperatorDiceNode; -import bjc.utils.parserutils.AST; - -/** - * Optimize an AST - * - * @author ben - * - */ -public class DiceASTOptimizer { - private static final class NestedArithmeticOperationCollapser - implements BinaryOperator<AST<IDiceASTNode>> { - private IDiceASTNode type; - private BiFunction<Integer, Integer, Integer> valueCollapser; - - public NestedArithmeticOperationCollapser(IDiceASTNode type, - BiFunction<Integer, Integer, Integer> valueCollapser) { - this.type = type; - this.valueCollapser = valueCollapser; - } - - @Override - public AST<IDiceASTNode> apply(AST<IDiceASTNode> leftAST, - AST<IDiceASTNode> rightAST) { - AST<IDiceASTNode> rightBranchOfLeftAST = leftAST - .applyToRight((rightSideAST) -> rightSideAST); - AST<IDiceASTNode> leftBranchOfLeftAST = leftAST - .applyToRight((rightSideAST) -> rightSideAST); - - boolean leftContainsNestedConstant = DiceASTOptimizer - .checkNodeType(rightBranchOfLeftAST, LITERAL) - && DiceASTOptimizer.isNodeConstant(leftAST); - - boolean isRightConstant = DiceASTOptimizer - .checkNodeType(rightAST, LITERAL) - && DiceASTOptimizer.isNodeConstant(leftAST); - - if (leftContainsNestedConstant && isRightConstant) { - int combinedValue = valueCollapser.apply( - getNodeValue(rightBranchOfLeftAST), - getNodeValue(rightAST)); - - AST<IDiceASTNode> newRightBranch = new AST<>( - new LiteralDiceNode(combinedValue)); - - return new AST<>(type, leftBranchOfLeftAST, - newRightBranch); - } - - return new AST<>(type, leftAST, rightAST); - } - } - - private static final class ArithmeticOperationCollapser - implements BinaryOperator<AST<IDiceASTNode>> { - private IDiceASTNode type; - private BiFunction<Integer, Integer, Integer> valueCollapser; - private boolean doSwap; - - public ArithmeticOperationCollapser(IDiceASTNode type, - BiFunction<Integer, Integer, Integer> valueCollapser, - boolean doSwap) { - this.type = type; - this.valueCollapser = valueCollapser; - this.doSwap = doSwap; - } - - @Override - public AST<IDiceASTNode> apply(AST<IDiceASTNode> leftAST, - AST<IDiceASTNode> rightAST) { - boolean isLeftConstant = DiceASTOptimizer - .checkNodeType(leftAST, LITERAL) - && DiceASTOptimizer.isNodeConstant(leftAST); - - boolean isRightConstant = DiceASTOptimizer - .checkNodeType(rightAST, LITERAL) - && DiceASTOptimizer.isNodeConstant(leftAST); - - if (isLeftConstant) { - if (isRightConstant) { - int combinedValue = valueCollapser.apply( - getNodeValue(leftAST), getNodeValue(rightAST)); - - return new AST<>(new LiteralDiceNode(combinedValue)); - } - - if (doSwap) { - return new AST<>(type, rightAST, leftAST); - } - } - - return new AST<>(type, leftAST, rightAST); - } - } - - private static Map<IDiceASTNode, BinaryOperator<AST<IDiceASTNode>>> buildConstantCollapsers() { - Map<IDiceASTNode, BinaryOperator<AST<IDiceASTNode>>> operatorCollapsers = new HashMap<>(); - - operatorCollapsers.put(OperatorDiceNode.ADD, - new ArithmeticOperationCollapser(OperatorDiceNode.ADD, - (leftVal, rightVal) -> leftVal + rightVal, true)); - - operatorCollapsers.put(OperatorDiceNode.MULTIPLY, - new ArithmeticOperationCollapser(OperatorDiceNode.MULTIPLY, - (leftVal, rightVal) -> leftVal * rightVal, true)); - - operatorCollapsers.put(OperatorDiceNode.SUBTRACT, - new ArithmeticOperationCollapser(OperatorDiceNode.SUBTRACT, - (leftVal, rightVal) -> leftVal - rightVal, false)); - - operatorCollapsers.put(OperatorDiceNode.DIVIDE, - new ArithmeticOperationCollapser(OperatorDiceNode.DIVIDE, - (leftVal, rightVal) -> leftVal / rightVal, false)); - - return operatorCollapsers; - } - - private static Map<IDiceASTNode, BinaryOperator<AST<IDiceASTNode>>> buildNestedConstantCollapsers() { - Map<IDiceASTNode, BinaryOperator<AST<IDiceASTNode>>> operatorCollapsers = new HashMap<>(); - - operatorCollapsers.put(OperatorDiceNode.ADD, - new NestedArithmeticOperationCollapser( - OperatorDiceNode.ADD, - (leftVal, rightVal) -> leftVal + rightVal)); - - operatorCollapsers.put(OperatorDiceNode.MULTIPLY, - new NestedArithmeticOperationCollapser( - OperatorDiceNode.MULTIPLY, - (leftVal, rightVal) -> leftVal * rightVal)); - - return operatorCollapsers; - } - - private static AST<IDiceASTNode> collapseLeaf(IDiceASTNode leaf) { - // Can't optimize a simple reference - if (leaf.getType() == VARIABLE) { - return new AST<>(leaf); - } else if (leaf.getType() == LITERAL) { - LiteralDiceNode node = (LiteralDiceNode) leaf; - - return new AST<>(optimizeLiteral(node, node.toExpression())); - } else { - throw new UnsupportedOperationException( - "Found leaf operator. This isn't supported"); - } - } - - private static IDiceASTNode optimizeLiteral(LiteralDiceNode node, - IDiceExpression leaf) { - if (leaf.canOptimize()) { - return new LiteralDiceNode(Integer.toString(leaf.optimize())); - } - - return node; - } - - private static AST<IDiceASTNode> finishTree(AST<IDiceASTNode> tree) { - return tree; - } - - /** - * Optimize a tree of expressions into a simpler form - * - * @param tree - * The tree to optimize - * @return The optimized tree - */ - public static AST<IDiceASTNode> optimizeTree(AST<IDiceASTNode> tree) { - AST<IDiceASTNode> astWithConstantsFolded = tree.collapse( - DiceASTOptimizer::collapseLeaf, - buildConstantCollapsers()::get, - DiceASTOptimizer::finishTree); - - AST<IDiceASTNode> astWithNestedConstantsFolded = astWithConstantsFolded - .collapse(DiceASTOptimizer::collapseLeaf, - buildNestedConstantCollapsers()::get, - DiceASTOptimizer::finishTree); - - return astWithNestedConstantsFolded; - } - - private static boolean checkNodeType(AST<IDiceASTNode> ast, - DiceASTType type) { - return ast.applyToHead((node) -> node.getType()) == type; - } - - private static boolean isNodeConstant(AST<IDiceASTNode> ast) { - return ast.applyToHead( - (node) -> ((LiteralDiceNode) node).isConstant()); - } - - private static int getNodeValue(AST<IDiceASTNode> ast) { - return ast.applyToHead( - (node) -> ((LiteralDiceNode) node).toConstant()); - } -} diff --git a/dice-lang/src/main/java/bjc/dicelang/old/ast/optimization/package-info.java b/dice-lang/src/main/java/bjc/dicelang/old/ast/optimization/package-info.java deleted file mode 100644 index ef39522..0000000 --- a/dice-lang/src/main/java/bjc/dicelang/old/ast/optimization/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Contains classes for optimizing ASTs - * @author ben - * - */ -package bjc.dicelang.old.ast.optimization;
\ No newline at end of file |
