diff options
| author | bculkin2442 <bjculkin@mix.wvu.edu> | 2016-04-03 17:45:05 -0400 |
|---|---|---|
| committer | bculkin2442 <bjculkin@mix.wvu.edu> | 2016-04-03 17:45:05 -0400 |
| commit | c8ae1ec096f5d1ac6db4f3a0035f7da106444e4e (patch) | |
| tree | 6c32c65d84d11efcea2bad699d0b68fbbb362290 | |
| parent | 9a6ac8c88689073cd0769da15b40c4fe091f0813 (diff) | |
General code refactoring and maintenance
12 files changed, 637 insertions, 196 deletions
diff --git a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTLanguagePragmaHandlers.java b/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTLanguagePragmaHandlers.java new file mode 100644 index 0000000..3eaf6b0 --- /dev/null +++ b/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTLanguagePragmaHandlers.java @@ -0,0 +1,81 @@ +package bjc.dicelang.examples; + +import java.util.Map; +import java.util.function.BiConsumer; + +import bjc.dicelang.ast.DiceASTExpression; +import bjc.dicelang.ast.DiceASTFreezer; +import bjc.dicelang.ast.DiceASTParser; +import bjc.dicelang.ast.IDiceASTNode; +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.parserutils.AST; + +/** + * Container for pragma handlers + * + * @author ben + * + */ +public class DiceASTLanguagePragmaHandlers { + /** + * Handles freezing a specified expression to a new name + * + * @author ben + * + */ + public static final class FreezeHandler implements + BiConsumer<DiceASTParser, Map<String, DiceASTExpression>> { + private String expressionToFreeze; + private String resultingVariable; + + /** + * Create a new freeze handler + * + * @param expressionToFreeze + * The name of the expression to freeze + * @param resultingVariable + * The name of the variable to bind the frozen + * expression to + */ + public FreezeHandler(String expressionToFreeze, + String resultingVariable) { + this.expressionToFreeze = expressionToFreeze; + this.resultingVariable = resultingVariable; + } + + @Override + public void accept(DiceASTParser astParser, + Map<String, DiceASTExpression> enviroment) { + if (enviroment.containsKey(expressionToFreeze)) { + System.err.println( + "ERROR: There is no expression bound to the variable " + + expressionToFreeze + "."); + } else { + AST<IDiceASTNode> frozenAST = DiceASTFreezer.freezeAST( + enviroment.get(expressionToFreeze), + new FunctionalMap<>(enviroment)); + + enviroment.put(resultingVariable, + new DiceASTExpression(frozenAST, enviroment)); + } + } + } + + /** + * Print the enviroment for debugging/inspection purposes + * + * @author ben + * + */ + public static final class EnviromentPrinter implements + BiConsumer<DiceASTParser, Map<String, DiceASTExpression>> { + @Override + public void accept(DiceASTParser astParser, + Map<String, DiceASTExpression> enviroment) { + enviroment.forEach((variable, boundExpression) -> System.out + .println("\tKey: " + variable + "\n\t\tExp: " + + boundExpression.toString())); + } + } + +} diff --git a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTLanguageState.java b/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTLanguageState.java index f5a6a29..92e75e3 100644 --- a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTLanguageState.java +++ b/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTLanguageState.java @@ -2,8 +2,8 @@ package bjc.dicelang.examples; import java.util.Map; -import bjc.dicelang.DiceExpressionParser; import bjc.dicelang.ast.DiceASTExpression; +import bjc.dicelang.ast.DiceASTParser; import bjc.utils.data.Pair; /** @@ -12,8 +12,8 @@ import bjc.utils.data.Pair; * @author ben * */ -public class DiceASTLanguageState extends - Pair<DiceExpressionParser, Map<String, DiceASTExpression>> { +public class DiceASTLanguageState + extends Pair<DiceASTParser, Map<String, DiceASTExpression>> { /** * Create a new state @@ -29,8 +29,8 @@ public class DiceASTLanguageState extends * @param right * The enviroment to use */ - public DiceASTLanguageState(DiceExpressionParser left, + public DiceASTLanguageState(DiceASTParser left, Map<String, DiceASTExpression> right) { super(left, right); } -} +}
\ No newline at end of file diff --git a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTLanguageTest.java b/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTLanguageTest.java index e0f276d..a21ed66 100644 --- a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTLanguageTest.java +++ b/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTLanguageTest.java @@ -4,16 +4,18 @@ import java.util.HashMap; import java.util.Map; import java.util.Scanner; import java.util.function.BiConsumer; -import java.util.function.Consumer; -import bjc.dicelang.DiceExpressionParser; import bjc.dicelang.IDiceExpression; import bjc.dicelang.ast.DiceASTExpression; import bjc.dicelang.ast.DiceASTFreezer; import bjc.dicelang.ast.DiceASTParser; +import bjc.dicelang.ast.DiceASTReferenceChecker; import bjc.dicelang.ast.IDiceASTNode; -import bjc.dicelang.ast.VariableDiceNode; + +import static bjc.dicelang.examples.DiceASTLanguagePragmaHandlers.*; + import bjc.utils.data.GenHolder; +import bjc.utils.funcdata.FunctionalMap; import bjc.utils.funcdata.bst.ITreePart.TreeLinearizationMethod; import bjc.utils.parserutils.AST; @@ -24,75 +26,127 @@ import bjc.utils.parserutils.AST; * */ public class DiceASTLanguageTest { - private static final class LastChecker - implements Consumer<IDiceASTNode> { - private GenHolder<Boolean> canUpdateLast; + /** + * The 'special commands' that aren't normal expressions that can be + * invoked from the prompt + */ + private static Map<String, BiConsumer<String, DiceASTLanguageState>> specialCommands; - public LastChecker(GenHolder<Boolean> canUpdateLast) { - this.canUpdateLast = canUpdateLast; - } + static { + specialCommands = new HashMap<>(); - @Override - public void accept(IDiceASTNode tn) { - if (tn instanceof VariableDiceNode && ((VariableDiceNode) tn) - .getVariable().equals("last")) { - canUpdateLast.transform((s) -> false); - } else { - canUpdateLast.transform((s) -> true); - } - } + // Put all the defined special commands in place + specialCommands.put("roll", DiceASTLanguageTest::rollReference); + specialCommands.put("env", DiceASTLanguageTest::printEnv); + specialCommands.put("freeze", DiceASTLanguageTest::freezeVar); } - private static Map<String, BiConsumer<String, DiceASTLanguageState>> acts; - - static { - acts = new HashMap<>(); + /** + * Freeze the references in an expression. + * + * This means replace variable references with the current contents of + * the variables they refer to + * + * @param command + * The command and its arguments + * @param languageState + * The state of the language at the moment + */ + private static void freezeVar(String command, + DiceASTLanguageState languageState) { + // Split the string into components + String[] args = command.split(" "); + + // Make sure we have the correct amount of arguments + if (args.length != 3) { + System.err.println( + "ERROR: Freeze requires you provide the name of expression" + + " to freeze, as well as the name of the variable to bind" + + " the result to."); + return; + } - acts.put("roll", DiceASTLanguageTest::rollReference); - acts.put("env", DiceASTLanguageTest::printEnv); - acts.put("freeze", DiceASTLanguageTest::freezeVar); - } + String expressionToFreeze = args[1]; - private static void freezeVar(String ln, DiceASTLanguageState stat) { - String[] strangs = ln.split(" "); + String resultingVariable = args[2]; - System.out.println("Freezing references in " + strangs[1]); + System.out.println("Freezing references in " + args[1] + + " and binding to " + resultingVariable); - stat.doWith((dep, env) -> { - env.put(strangs[1], new DiceASTExpression( - DiceASTFreezer.freezeAST(env.get(strangs[1]), env), - env)); - }); + languageState.doWith( + new FreezeHandler(expressionToFreeze, resultingVariable)); } /** - * @param ln - * Unused parameter, kept in place to conform to expected - * type sig + * Print all bound variables in the current enviroment, as well as what + * they're bound to + * + * @param command + * The command and its arguments + * @param stat + * The state of the language at the moment */ - private static void printEnv(String ln, DiceASTLanguageState stat) { + private static void printEnv(String command, + DiceASTLanguageState 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()))); + stat.doWith(new EnviromentPrinter()); } - private static void rollReference(String ln, - DiceASTLanguageState stat) { - String[] strangs = ln.split(" "); + /** + * Roll an expression a given number of times + * + * @param command + * The command and its arguments + * @param languageState + * The state of the language at the moments + */ + private static void rollReference(String command, + DiceASTLanguageState languageState) { + String[] args = command.split(" "); + + if (args.length != 3) { + System.err.println("ERROR: Roll requires two arguments." + + " The name of the expression to roll, " + + "followed by the number of times to roll it"); + return; + } - System.out.println("\tRolling dice expression " + strangs[1] + " " - + strangs[2] + " times."); + System.out.println("\tRolling dice expression " + args[1] + " " + + args[2] + " times."); - int nRolls = Integer.parseInt(strangs[2]); + String expressionName = args[1]; - IDiceExpression dexp = - stat.merge((dep, env) -> env.get(strangs[1])); + int numberOfRolls; - for (int i = 1; i <= nRolls; i++) { - int roll = dexp.roll(); + try { + numberOfRolls = Integer.parseInt(args[2]); + } catch (NumberFormatException nfex) { + System.err.println( + "ERROR: The second argument must be a valid number, and " + + args[2] + " is not one."); + return; + } - System.out.println("\tRolled " + roll); + IDiceExpression expressionToRoll = + languageState.merge((astParser, enviroment) -> { + if (!enviroment.containsKey(expressionName)) { + return null; + } else { + return enviroment.get(expressionName); + } + }); + + if (expressionToRoll == null) { + System.err.println( + "ERROR: There is no expression bound to the variable " + + expressionName + "."); + } + + for (int i = 1; i <= numberOfRolls; i++) { + int currentRoll = expressionToRoll.roll(); + + System.out.println("\tRolled " + currentRoll); } } @@ -103,49 +157,86 @@ public class DiceASTLanguageTest { * Unused CLI args */ public static void main(String[] args) { - Scanner scn = new Scanner(System.in); - int i = 0; + Scanner inputSource = new Scanner(System.in); + int commandNumber = 0; - System.out.print("dice-lang-" + i + "> "); - String ln = scn.nextLine(); + System.out.print("dice-lang-" + commandNumber + "> "); + String currentLine = inputSource.nextLine(); - DiceASTParser dap = new DiceASTParser(); + // The enviroment for variables + Map<String, DiceASTExpression> enviroment = new HashMap<>(); - DiceExpressionParser dep = new DiceExpressionParser(); - Map<String, DiceASTExpression> env = new HashMap<>(); - DiceASTLanguageState state = new DiceASTLanguageState(dep, env); + // The parser to turn strings into AST's + DiceASTParser astParser = new DiceASTParser(); - while (!ln.equalsIgnoreCase("quit")) { - String header = ln.split(" ")[0]; + DiceASTLanguageState languageState = + new DiceASTLanguageState(astParser, enviroment); - if (acts.containsKey(header)) { - acts.get(header).accept(ln, state); - } else { + while (!currentLine.equalsIgnoreCase("quit")) { + String prospectiveCommandName = currentLine.split(" ")[0]; - AST<IDiceASTNode> builtAST = dap.buildAST(ln); - DiceASTExpression exp = - new DiceASTExpression(builtAST, env); - - System.out.println("\tParsed: " + exp.toString()); - System.out.println("\tSample Roll: " + exp.roll()); - - GenHolder<Boolean> canUpdateLast = new GenHolder<>(false); - - exp.getAst().traverse(TreeLinearizationMethod.PREORDER, - new LastChecker(canUpdateLast)); + // Handle special commands + if (specialCommands.containsKey(prospectiveCommandName)) { + specialCommands.get(prospectiveCommandName) + .accept(currentLine, languageState); + } else { - if (canUpdateLast.unwrap((s) -> s)) { - env.put("last", exp); + // Build an AST from the string expression + AST<IDiceASTNode> builtAST = + astParser.buildAST(currentLine); + + // Build a rollable expression from the AST + DiceASTExpression expression = + new DiceASTExpression(builtAST, enviroment); + + // Print out results + System.out.println("\tParsed: " + expression.toString()); + System.out + .println("\t\tSample Roll: " + expression.roll()); + + // Assume we can update last by default + GenHolder<Boolean> canUpdateLast = new GenHolder<>(true); + + // Check that no node references last + expression.getAst().traverse( + TreeLinearizationMethod.PREORDER, + new DiceASTReferenceChecker(canUpdateLast, + "last")); + + // Update last if we can + if (canUpdateLast.unwrap((flag) -> flag)) { + enviroment.put("last", expression); + } else { + // We need to freeze out references to last + enviroment.put("last", + freezeOutLast(enviroment, builtAST)); } } - i++; + // Increase the number of commands + commandNumber++; - System.out.print("dice-lang-" + i + "> "); - ln = scn.nextLine(); + // Read a new command + System.out.print("dice-lang-" + commandNumber + "> "); + currentLine = inputSource.nextLine(); } System.out.println("Bye."); - scn.close(); + + // Cleanup after ourselves + inputSource.close(); + } + + private static DiceASTExpression freezeOutLast( + Map<String, DiceASTExpression> enviroment, + AST<IDiceASTNode> builtAST) { + FunctionalMap<String, AST<IDiceASTNode>> transformedEnviroment = + new FunctionalMap<>(enviroment) + .mapValues((expr) -> expr.getAst()); + + AST<IDiceASTNode> expressionSansLast = DiceASTFreezer + .selectiveFreeze(builtAST, transformedEnviroment, "last"); + + return new DiceASTExpression(expressionSansLast, enviroment); } } 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 a79c466..078c952 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTExpression.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTExpression.java @@ -2,13 +2,12 @@ package bjc.dicelang.ast; import java.util.HashMap; import java.util.Map; -import java.util.function.BinaryOperator; - import org.apache.commons.lang3.StringUtils; import bjc.dicelang.ComplexDice; import bjc.dicelang.CompoundDice; import bjc.dicelang.IDiceExpression; + import bjc.utils.data.Pair; import bjc.utils.parserutils.AST; @@ -24,67 +23,84 @@ public class DiceASTExpression implements IDiceExpression { /** * Build the map of operations to use when collapsing the AST * - * @param env + * @param enviroment * The enviroment to evaluate bindings and such against * @return The operations to use when collapsing the AST */ - private static - Map<IDiceASTNode, BinaryOperator<Pair<Integer, AST<IDiceASTNode>>>> - buildOperations(Map<String, DiceASTExpression> env) { - Map<IDiceASTNode, BinaryOperator<Pair<Integer, AST<IDiceASTNode>>>> opCollapsers = + private static Map<IDiceASTNode, IOperatorCollapser> + buildOperations(Map<String, DiceASTExpression> enviroment) { + Map<IDiceASTNode, IOperatorCollapser> operatorCollapsers = new HashMap<>(); - opCollapsers.put(OperatorDiceNode.ADD, (left, right) -> { - return left.merge((lval, last) -> right.merge((rval, rast) -> { - return new Pair<>(lval + rval, - new AST<>(OperatorDiceNode.ADD, last, rast)); - })); - - }); - opCollapsers.put(OperatorDiceNode.SUBTRACT, (left, right) -> { - return left.merge((lval, last) -> right.merge((rval, rast) -> { - return new Pair<>(lval - rval, - new AST<>(OperatorDiceNode.SUBTRACT, last, rast)); - })); - - }); - opCollapsers.put(OperatorDiceNode.MULTIPLY, (left, right) -> { - return left.merge((lval, last) -> right.merge((rval, rast) -> { - return new Pair<>(lval * rval, - new AST<>(OperatorDiceNode.MULTIPLY, last, rast)); - })); - - }); - opCollapsers.put(OperatorDiceNode.DIVIDE, (left, right) -> { + operatorCollapsers.put(OperatorDiceNode.ADD, + (leftNode, rightNode) -> { + return leftNode.merge((leftValue, leftAST) -> { + return rightNode.merge((rightValue, rightAST) -> { + return new Pair<>(leftValue + rightValue, + new AST<>(OperatorDiceNode.ADD, + leftAST, rightAST)); + }); + }); + + }); + 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)); + })); + + }); + 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)); + })); + + }); + 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)); })); }); - opCollapsers.put(OperatorDiceNode.ASSIGN, (left, right) -> { + 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); - env.put(nam, new DiceASTExpression(rast, env)); + enviroment.put(nam, + new DiceASTExpression(rast, enviroment)); return new Pair<>(rval, new AST<>(OperatorDiceNode.ASSIGN, last, rast)); })); }); - opCollapsers.put(OperatorDiceNode.COMPOUND, (left, right) -> { - return left.merge((lval, last) -> right.merge((rval, rast) -> { - int ival = Integer.parseInt( - Integer.toString(lval) + Integer.toString(rval)); - - return new Pair<>(ival, - new AST<>(OperatorDiceNode.COMPOUND, last, rast)); - })); - }); - opCollapsers.put(OperatorDiceNode.GROUP, (left, right) -> { + 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)); + })); + }); + operatorCollapsers.put(OperatorDiceNode.GROUP, (left, right) -> { return left.merge((lval, last) -> right.merge((rval, rast) -> { return new Pair<>(new ComplexDice(lval, rval).roll(), @@ -92,7 +108,7 @@ public class DiceASTExpression implements IDiceExpression { })); }); - return opCollapsers; + return operatorCollapsers; } /** @@ -176,7 +192,7 @@ public class DiceASTExpression implements IDiceExpression { */ @Override public int roll() { - Map<IDiceASTNode, BinaryOperator<Pair<Integer, AST<IDiceASTNode>>>> operations = + Map<IDiceASTNode, IOperatorCollapser> operations = buildOperations(env); return ast.collapse(this::evalLeaf, operations::get, diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTFreezer.java b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTFreezer.java index 0e2134b..bad24f8 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTFreezer.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTFreezer.java @@ -1,7 +1,9 @@ package bjc.dicelang.ast; -import java.util.Map; +import java.util.function.Function; +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.FunctionalMap; import bjc.utils.parserutils.AST; /** @@ -12,32 +14,57 @@ import bjc.utils.parserutils.AST; * */ public class DiceASTFreezer { - /** - * Expand a reference - * - * @param vnode - * The node containing the reference to expand - * @param env - * The enviroment to expand against - * @return The expanded reference - */ - private static AST<IDiceASTNode> expandNode(VariableDiceNode vnode, - Map<String, AST<IDiceASTNode>> env) { - return env.get(vnode.getVariable()); + private static class NodeFreezer + implements Function<IDiceASTNode, AST<IDiceASTNode>> { + private FunctionalMap<String, AST<IDiceASTNode>> enviroment; + + public NodeFreezer(FunctionalMap<String, AST<IDiceASTNode>> env) { + enviroment = env; + } + + @Override + public AST<IDiceASTNode> apply(IDiceASTNode nod) { + if (nod.getType() == DiceASTType.VARIABLE) { + return expandNode((VariableDiceNode) nod); + } else { + 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); + } } - /** - * Expand a reference - * - * @param vnode - * The node containing the reference to expand - * @param env - * The enviroment to expand against - * @return The expanded reference - */ - private static AST<IDiceASTNode> expandNode2(VariableDiceNode vnode, - Map<String, DiceASTExpression> env) { - return env.get(vnode.getVariable()).getAst(); + private static final class SelectiveFreezer extends NodeFreezer { + + private FunctionalList<String> variableNames; + + public SelectiveFreezer( + FunctionalMap<String, AST<IDiceASTNode>> env, + FunctionalList<String> varNames) { + super(env); + variableNames = varNames; + } + + @Override + protected AST<IDiceASTNode> + expandNode(VariableDiceNode variableNode) { + if (variableNames.contains(variableNode.getVariable())) { + return super.expandNode(variableNode); + } else { + return new AST<>(variableNode); + } + } } /** @@ -49,20 +76,9 @@ public class DiceASTFreezer { * The enviroment to get reference values from * @return The tree with references frozen */ - @SuppressWarnings("unused") public static AST<IDiceASTNode> freezeAST(AST<IDiceASTNode> tree, - Map<String, AST<IDiceASTNode>> env) { - return tree.collapse((nod) -> { - if (nod instanceof VariableDiceNode) { - return expandNode((VariableDiceNode) nod, env); - } else { - // Type is specified here so compiler can know the type - // we're using - return new AST<IDiceASTNode>(nod); - } - } , (op) -> (left, right) -> { - return new AST<IDiceASTNode>(op, left, right); - } , (r) -> r); + FunctionalMap<String, AST<IDiceASTNode>> env) { + return selectiveFreeze(tree, env); } /** @@ -74,19 +90,43 @@ public class DiceASTFreezer { * The enviroment to get reference values from * @return The tree with references frozen */ - @SuppressWarnings("unused") public static AST<IDiceASTNode> freezeAST(DiceASTExpression tree, - Map<String, DiceASTExpression> env) { - return tree.getAst().collapse((nod) -> { - if (nod instanceof VariableDiceNode) { - return expandNode2((VariableDiceNode) nod, env); - } else { - // Type is specified here so compiler can know the type - // we're using - return new AST<IDiceASTNode>(nod); - } - } , (op) -> (left, right) -> { - return new AST<IDiceASTNode>(op, left, right); - } , (r) -> r); + FunctionalMap<String, DiceASTExpression> env) { + return freezeAST(tree.getAst(), + env.mapValues(expression -> expression.getAst())); + } + + /** + * Freeze references to specified variables + * + * @param tree + * The tree-backed expression to freeze references in + * @param env + * The enviroment to resolve variables against + * @param varNames + * The names of the variables to freeze + * @return An AST with the specified variables frozen + */ + public static AST<IDiceASTNode> selectiveFreeze(AST<IDiceASTNode> tree, + FunctionalMap<String, AST<IDiceASTNode>> env, + String... varNames) { + return selectiveFreeze(tree, env, new FunctionalList<>(varNames)); + } + + /** + * Freeze references to specified variables + * + * @param tree + * The tree-backed expression to freeze references in + * @param env + * The enviroment to resolve variables against + * @param varNames + * The names of the variables to freeze + * @return An AST with the specified variables frozen + */ + public static AST<IDiceASTNode> selectiveFreeze(AST<IDiceASTNode> tree, + FunctionalMap<String, AST<IDiceASTNode>> env, + FunctionalList<String> varNames) { + return tree.expand(new SelectiveFreezer(env, varNames)); } -} +}
\ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTReferenceChecker.java b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTReferenceChecker.java new file mode 100644 index 0000000..aaabe8b --- /dev/null +++ b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTReferenceChecker.java @@ -0,0 +1,63 @@ +package bjc.dicelang.ast; + +import java.util.function.Consumer; + +import bjc.utils.data.GenHolder; + +/** + * Check if the specified node references a particular variable + * + * @author ben + * + */ +public final class DiceASTReferenceChecker + implements Consumer<IDiceASTNode> { + /** + * This is true if the specified node references the set variable + */ + private GenHolder<Boolean> referencesVariable; + + private String varName; + + /** + * Create a new reference checker + * + * @param referencesVar + * The holder of whether the variable is referenced or + * not + * @param varName + * The variable to check for references in + */ + public DiceASTReferenceChecker(GenHolder<Boolean> referencesVar, + String varName) { + this.referencesVariable = referencesVar; + this.varName = varName; + } + + @Override + public void accept(IDiceASTNode astNode) { + if (!referencesVariable.unwrap(bool -> bool)) { + if (isDirectReferenceToLast(astNode)) { + referencesVariable.transform((bool) -> false); + } + } + } + + /** + * Check if a given AST node directly references the meta-variable + * last + * + * @param astNode + * The node to check + * @return Whether or not the node directly references last + */ + private boolean isDirectReferenceToLast(IDiceASTNode astNode) { + if (astNode.getType() == DiceASTType.VARIABLE) { + VariableDiceNode node = (VariableDiceNode) astNode; + + return node.getVariable().equals(varName); + } else { + return false; + } + } +}
\ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTType.java b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTType.java new file mode 100644 index 0000000..0a128f5 --- /dev/null +++ b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTType.java @@ -0,0 +1,27 @@ +package bjc.dicelang.ast; + +/** + * 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/IDiceASTNode.java b/dice-lang/src/main/java/bjc/dicelang/ast/IDiceASTNode.java index 439bdac..38a1cc1 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/IDiceASTNode.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/IDiceASTNode.java @@ -13,4 +13,11 @@ public interface IDiceASTNode { * @return Whether or not this node represents an operator */ public boolean isOperator(); -} + + /** + * Get the type of AST node this node is + * + * @return The type of AST node this AST node is + */ + public DiceASTType getType(); +}
\ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/IOperatorCollapser.java b/dice-lang/src/main/java/bjc/dicelang/ast/IOperatorCollapser.java new file mode 100644 index 0000000..ebd0436 --- /dev/null +++ b/dice-lang/src/main/java/bjc/dicelang/ast/IOperatorCollapser.java @@ -0,0 +1,17 @@ +package bjc.dicelang.ast; + +import java.util.function.BinaryOperator; + +import bjc.utils.data.Pair; +import bjc.utils.parserutils.AST; + +/** + * Alias for operator collapsers. Because 68-char types are too long + * + * @author ben + * + */ +public interface IOperatorCollapser + extends BinaryOperator<Pair<Integer, AST<IDiceASTNode>>> { + +} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/LiteralDiceNode.java b/dice-lang/src/main/java/bjc/dicelang/ast/LiteralDiceNode.java index 8157844..b80f1a4 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/LiteralDiceNode.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/LiteralDiceNode.java @@ -10,7 +10,7 @@ public class LiteralDiceNode implements IDiceASTNode { /** * The value contained by this node */ - private String data; + private String value; /** * Create a new node with the given value @@ -19,12 +19,35 @@ public class LiteralDiceNode implements IDiceASTNode { * The value to be in this node */ public LiteralDiceNode(String data) { - this.data = data; + this.value = data; } + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ @Override - public boolean isOperator() { - return false; + 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; + } } /** @@ -33,7 +56,30 @@ public class LiteralDiceNode implements IDiceASTNode { * @return the data stored in this AST node */ public String getData() { - return data; + 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; } /* @@ -43,6 +89,6 @@ public class LiteralDiceNode implements IDiceASTNode { */ @Override public String toString() { - return data; + return value; } -} +}
\ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/OperatorDiceNode.java b/dice-lang/src/main/java/bjc/dicelang/ast/OperatorDiceNode.java index 45c3d58..90e08b7 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/OperatorDiceNode.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/OperatorDiceNode.java @@ -69,6 +69,11 @@ public enum OperatorDiceNode implements IDiceASTNode { } } + @Override + public DiceASTType getType() { + return DiceASTType.OPERATOR; + } + /* * (non-Javadoc) * diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/VariableDiceNode.java b/dice-lang/src/main/java/bjc/dicelang/ast/VariableDiceNode.java index e02952d..e2dec64 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/VariableDiceNode.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/VariableDiceNode.java @@ -10,16 +10,50 @@ public class VariableDiceNode implements IDiceASTNode { /** * The variable referenced by this node */ - private String var; + private String variableName; /** * Create a new node representing the specified variable * - * @param data + * @param varName * The name of the variable being referenced */ - public VariableDiceNode(String data) { - this.var = data; + 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; } /** @@ -28,7 +62,21 @@ public class VariableDiceNode implements IDiceASTNode { * @return the variable referenced by this AST node */ public String getVariable() { - return var; + 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; } /* @@ -48,6 +96,6 @@ public class VariableDiceNode implements IDiceASTNode { */ @Override public String toString() { - return var; + return variableName; } -} +}
\ No newline at end of file |
