From 9658afb5b07d2b5a965dea322b0ad8fa3c16ce2d Mon Sep 17 00:00:00 2001 From: bculkin2442 Date: Sun, 3 Apr 2016 18:43:50 -0400 Subject: More refactoring and maintenance --- .../java/bjc/dicelang/ast/DiceASTExpression.java | 183 ++++++++++++++------- .../java/bjc/dicelang/ast/DiceASTFlattener.java | 101 +++++++----- .../main/java/bjc/dicelang/ast/DiceASTParser.java | 79 +++++---- .../bjc/dicelang/ast/DiceASTReferenceChecker.java | 12 +- 4 files changed, 236 insertions(+), 139 deletions(-) (limited to 'dice-lang/src/main/java/bjc/dicelang/ast') diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTExpression.java b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTExpression.java index 078c952..eaadc53 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTExpression.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTExpression.java @@ -2,6 +2,8 @@ package bjc.dicelang.ast; import java.util.HashMap; import java.util.Map; +import java.util.function.Function; + import org.apache.commons.lang3.StringUtils; import bjc.dicelang.ComplexDice; @@ -20,6 +22,21 @@ import bjc.utils.parserutils.AST; */ public class DiceASTExpression implements IDiceExpression { + private static final class VariableRetriever + implements Function { + @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 * @@ -44,69 +61,104 @@ public class DiceASTExpression implements IDiceExpression { }); operatorCollapsers.put(OperatorDiceNode.SUBTRACT, - (left, right) -> { - return left.merge( - (lval, last) -> right.merge((rval, rast) -> { - return new Pair<>(lval - rval, - new AST<>( - OperatorDiceNode.SUBTRACT, - last, rast)); - })); - + (leftNode, rightNode) -> { + return leftNode.merge((leftValue, leftAST) -> { + return rightNode.merge((rightValue, rightAST) -> { + return new Pair<>(leftValue - rightValue, + new AST<>(OperatorDiceNode.SUBTRACT, + leftAST, rightAST)); + }); + }); }); + operatorCollapsers.put(OperatorDiceNode.MULTIPLY, - (left, right) -> { - return left.merge( - (lval, last) -> right.merge((rval, rast) -> { - return new Pair<>(lval * rval, - new AST<>( - OperatorDiceNode.MULTIPLY, - last, rast)); - })); + (leftNode, rightNode) -> { + return leftNode.merge((leftValue, leftAST) -> { + return rightNode.merge((rightValue, rightAST) -> { + return new Pair<>(leftValue * rightValue, + new AST<>(OperatorDiceNode.MULTIPLY, + leftAST, rightAST)); + }); + }); }); - operatorCollapsers.put(OperatorDiceNode.DIVIDE, (left, right) -> { - return left.merge((lval, last) -> right.merge((rval, rast) -> { - return new Pair<>(lval / rval, - new AST<>(OperatorDiceNode.DIVIDE, last, rast)); - })); - }); + operatorCollapsers.put(OperatorDiceNode.DIVIDE, + (leftNode, rightNode) -> { + return leftNode.merge((leftValue, leftAST) -> { + return rightNode.merge((rightValue, rightAST) -> { + if (rightValue == 0) { + throw new ArithmeticException( + "Attempted to divide by zero. The AST of the problem expression is " + + rightAST); + } + + return new Pair<>(leftValue / rightValue, + new AST<>(OperatorDiceNode.DIVIDE, + leftAST, rightAST)); + }); + }); + }); operatorCollapsers.put(OperatorDiceNode.ASSIGN, (left, right) -> { - return left.merge((lval, last) -> right.merge((rval, rast) -> { - String nam = last.collapse((nod) -> { - return ((VariableDiceNode) nod).getVariable(); - }, (v) -> (lv, rv) -> null, (r) -> r); + 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); - enviroment.put(nam, - new DiceASTExpression(rast, enviroment)); + enviroment.put(variableName, + new DiceASTExpression(rightAST, enviroment)); - return new Pair<>(rval, - new AST<>(OperatorDiceNode.ASSIGN, last, rast)); - })); + return new Pair<>(rightValue, new AST<>( + OperatorDiceNode.ASSIGN, leftAST, rightAST)); + }); + }); }); operatorCollapsers.put(OperatorDiceNode.COMPOUND, - (left, right) -> { - return left.merge( - (lval, last) -> right.merge((rval, rast) -> { - int ival = Integer - .parseInt(Integer.toString(lval) - + Integer.toString(rval)); - - return new Pair<>(ival, - new AST<>( - OperatorDiceNode.COMPOUND, - last, rast)); - })); + (leftNode, 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)); + }); + }); }); - operatorCollapsers.put(OperatorDiceNode.GROUP, (left, right) -> { - return left.merge((lval, last) -> right.merge((rval, rast) -> { - return new Pair<>(new ComplexDice(lval, rval).roll(), - new AST<>(OperatorDiceNode.GROUP, last, rast)); - })); - }); + operatorCollapsers.put(OperatorDiceNode.GROUP, + (leftNode, 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)); + }); + }); + }); return operatorCollapsers; } @@ -144,7 +196,7 @@ public class DiceASTExpression implements IDiceExpression { * represents */ private Pair> evalLeaf(IDiceASTNode tokn) { - if (tokn instanceof VariableDiceNode) { + if (tokn.getType() == DiceASTType.VARIABLE) { String varName = ((VariableDiceNode) tokn).getVariable(); if (env.containsKey(varName)) { @@ -154,25 +206,37 @@ public class DiceASTExpression implements IDiceExpression { // Handle special case for defining variables return new Pair<>(0, new AST<>(tokn)); } - } else { - LiteralDiceNode lnod = (LiteralDiceNode) tokn; - String dat = lnod.getData(); + } else if (tokn.getType() == DiceASTType.LITERAL) { + LiteralDiceNode literalNode = (LiteralDiceNode) tokn; + String dat = literalNode.getData(); if (StringUtils.countMatches(dat, 'c') == 1 - && !dat.equalsIgnoreCase("c")) { + && !dat.equalsIgnoreCase("c") + && !dat.startsWith("c")) { String[] strangs = dat.split("c"); + return new Pair<>(new CompoundDice(strangs).roll(), new AST<>(tokn)); } else if (StringUtils.countMatches(dat, 'd') == 1 - && !dat.equalsIgnoreCase("d")) { + && !dat.equalsIgnoreCase("d") + && !dat.startsWith("d")) { /* * Handle dice groups */ return new Pair<>(ComplexDice.fromString(dat).roll(), new AST<>(tokn)); } else { - return new Pair<>(Integer.parseInt(dat), new AST<>(tokn)); + try { + return new Pair<>(Integer.parseInt(dat), + new AST<>(tokn)); + } catch (NumberFormatException nfex) { + throw new UnsupportedOperationException( + "Found malformed leaf token " + tokn); + } } + } else { + throw new UnsupportedOperationException("Found leaf operator " + + tokn + ". These aren't supported."); } } @@ -196,7 +260,8 @@ public class DiceASTExpression implements IDiceExpression { buildOperations(env); return ast.collapse(this::evalLeaf, operations::get, - (r) -> r.merge((left, right) -> left)); + (returnedValue) -> returnedValue + .merge((left, right) -> left)); } /* @@ -208,4 +273,4 @@ public class DiceASTExpression implements IDiceExpression { public String toString() { return ast.toString(); } -} +} \ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTFlattener.java b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTFlattener.java index aa15c4d..fad4870 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTFlattener.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTFlattener.java @@ -3,17 +3,19 @@ package bjc.dicelang.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.CompoundDiceExpression; +import bjc.dicelang.OperatorDiceExpression; import bjc.dicelang.DiceExpressionType; import bjc.dicelang.IDiceExpression; import bjc.dicelang.ReferenceDiceExpression; import bjc.dicelang.ScalarDie; + import bjc.utils.parserutils.AST; /** @@ -24,6 +26,60 @@ import bjc.utils.parserutils.AST; * */ public class DiceASTFlattener { + private static final class NodeCollapser + implements Function { + private Map enviroment; + + public NodeCollapser(Map 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 * @@ -35,20 +91,21 @@ public class DiceASTFlattener { buildOperations(Map env) { Map> opCollapsers = new HashMap<>(); + opCollapsers.put(OperatorDiceNode.ADD, (left, right) -> { - return new CompoundDiceExpression(right, left, + return new OperatorDiceExpression(right, left, DiceExpressionType.ADD); }); opCollapsers.put(OperatorDiceNode.SUBTRACT, (left, right) -> { - return new CompoundDiceExpression(right, left, + return new OperatorDiceExpression(right, left, DiceExpressionType.SUBTRACT); }); opCollapsers.put(OperatorDiceNode.MULTIPLY, (left, right) -> { - return new CompoundDiceExpression(right, left, + return new OperatorDiceExpression(right, left, DiceExpressionType.MULTIPLY); }); opCollapsers.put(OperatorDiceNode.DIVIDE, (left, right) -> { - return new CompoundDiceExpression(right, left, + return new OperatorDiceExpression(right, left, DiceExpressionType.DIVIDE); }); opCollapsers.put(OperatorDiceNode.ASSIGN, (left, right) -> { @@ -64,30 +121,6 @@ public class DiceASTFlattener { return opCollapsers; } - /** - * Create a dice expression from a literal token - * - * @param tok - * The token to convert to an expression - * @return The dice expression represented by the token - */ - private static IDiceExpression expFromLiteral(LiteralDiceNode tok) { - String data = tok.getData(); - - if (StringUtils.countMatches(data, 'c') == 1 - && !data.equalsIgnoreCase("c")) { - String[] strangs = data.split("c"); - - return new CompoundDice(ComplexDice.fromString(strangs[0]), - ComplexDice.fromString(strangs[1])); - } else if (StringUtils.countMatches(data, 'd') == 1 - && !data.equalsIgnoreCase("d")) { - return ComplexDice.fromString(data); - } else { - return new ScalarDie(Integer.parseInt(data)); - } - } - /** * Flatten a AST into a dice expression * @@ -102,13 +135,7 @@ public class DiceASTFlattener { Map> opCollapsers = buildOperations(env); - return ast.collapse((nod) -> { - if (nod instanceof LiteralDiceNode) { - return expFromLiteral((LiteralDiceNode) nod); - } else { - return new ReferenceDiceExpression( - ((VariableDiceNode) nod).getVariable(), env); - } - } , opCollapsers::get, (r) -> r); + return ast.collapse(new NodeCollapser(env), opCollapsers::get, + (r) -> r); } } 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 d0b970a..67d2d50 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTParser.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTParser.java @@ -2,6 +2,7 @@ package bjc.dicelang.ast; import java.util.Deque; import java.util.LinkedList; +import java.util.function.Function; import org.apache.commons.lang3.StringUtils; @@ -20,6 +21,44 @@ import bjc.utils.parserutils.TreeConstructor; * */ public class DiceASTParser { + private static final class NodeBaker + implements Function { + @Override + public IDiceASTNode apply(String tok) { + if (isOperator(tok)) { + return OperatorDiceNode.fromString(tok); + } else if (NodeBaker.isLiteral(tok)) { + return new LiteralDiceNode(tok); + } else { + return new VariableDiceNode(tok); + } + } + + /** + * Check if a token represents a literal + * + * @param tok + * The token to check + * @return Whether or not the token represents a literal + */ + private static boolean isLiteral(String tok) { + if (StringUtils.countMatches(tok, 'c') == 1 + && !tok.equalsIgnoreCase("c")) { + return true; + } else if (StringUtils.countMatches(tok, 'd') == 1 + && !tok.equalsIgnoreCase("d")) { + return true; + } else { + try { + Integer.parseInt(tok); + return true; + } catch (NumberFormatException nfx) { + return false; + } + } + } + } + /** * The yard to use for shunting expressions */ @@ -45,7 +84,7 @@ public class DiceASTParser { */ public AST buildAST(String exp) { FunctionalList tokens = - FunctionalStringTokenizer.fromString(exp).toList((s) -> s); + FunctionalStringTokenizer.fromString(exp).toList(); Deque> ops = new LinkedList<>(); @@ -70,45 +109,13 @@ public class DiceASTParser { yard.postfix(fullyExpandedTokens, (s) -> s); AST rawAST = TreeConstructor.constructTree(shunted, - this::isOperator, (op) -> false, null); + DiceASTParser::isOperator); - AST bakedAST = rawAST.transmuteAST((tok) -> { - if (isOperator(tok)) { - return OperatorDiceNode.fromString(tok); - } else if (isLiteral(tok)) { - return new LiteralDiceNode(tok); - } else { - return new VariableDiceNode(tok); - } - }); + AST bakedAST = rawAST.transmuteAST(new NodeBaker()); return bakedAST; } - /** - * Check if a token represents a literal - * - * @param tok - * The token to check - * @return Whether or not the token represents a literal - */ - private static boolean isLiteral(String tok) { - if (StringUtils.countMatches(tok, 'c') == 1 - && !tok.equalsIgnoreCase("c")) { - return true; - } else if (StringUtils.countMatches(tok, 'd') == 1 - && !tok.equalsIgnoreCase("d")) { - return true; - } else { - try { - Integer.parseInt(tok); - return true; - } catch (NumberFormatException nfx) { - return false; - } - } - } - /** * Check if a token represents an operator * @@ -116,7 +123,7 @@ public class DiceASTParser { * The token to check if it represents an operator * @return Whether or not the token represents an operator */ - private boolean isOperator(String tok) { + private static boolean isOperator(String tok) { switch (tok) { case ":=": case "+": diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTReferenceChecker.java b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTReferenceChecker.java index aaabe8b..a74d61e 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTReferenceChecker.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTReferenceChecker.java @@ -23,8 +23,7 @@ public final class DiceASTReferenceChecker * Create a new reference checker * * @param referencesVar - * The holder of whether the variable is referenced or - * not + * The holder of whether the variable is referenced or not * @param varName * The variable to check for references in */ @@ -37,21 +36,20 @@ public final class DiceASTReferenceChecker @Override public void accept(IDiceASTNode astNode) { if (!referencesVariable.unwrap(bool -> bool)) { - if (isDirectReferenceToLast(astNode)) { + if (isDirectReference(astNode)) { referencesVariable.transform((bool) -> false); } } } /** - * Check if a given AST node directly references the meta-variable - * last + * Check if a given AST node directly references the specified variable * * @param astNode * The node to check - * @return Whether or not the node directly references last + * @return Whether or not the node directly the variable */ - private boolean isDirectReferenceToLast(IDiceASTNode astNode) { + private boolean isDirectReference(IDiceASTNode astNode) { if (astNode.getType() == DiceASTType.VARIABLE) { VariableDiceNode node = (VariableDiceNode) astNode; -- cgit v1.2.3