From 7c222f25d4b2d9f3b149d880f0e1acf8d673e4f5 Mon Sep 17 00:00:00 2001 From: bculkin2442 Date: Sun, 17 Apr 2016 20:41:47 -0400 Subject: Fixed a bug with arithmetic operators --- .../bjc/dicelang/examples/DiceASTLanguageTest.java | 67 +++++++++++- .../java/bjc/dicelang/examples/DiceASTPragma.java | 19 ++++ .../examples/DiceASTReferenceSanitizer.java | 28 +++++ .../dicelang/examples/DiceExpressionPreparer.java | 7 ++ .../java/bjc/dicelang/ast/ArithmeticCollapser.java | 22 ++-- .../java/bjc/dicelang/ast/DiceASTEvaluator.java | 8 +- .../main/java/bjc/dicelang/ast/DiceASTInliner.java | 115 +++++++++++++++++++++ .../main/java/bjc/dicelang/ast/DiceASTParser.java | 20 ++-- .../bjc/dicelang/ast/nodes/OperatorDiceNode.java | 4 - 9 files changed, 266 insertions(+), 24 deletions(-) create mode 100644 dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTPragma.java create mode 100644 dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTReferenceSanitizer.java create mode 100644 dice-lang/src/main/java/bjc/dicelang/ast/DiceASTInliner.java 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 18f0562..5dbaff3 100644 --- a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTLanguageTest.java +++ b/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTLanguageTest.java @@ -3,10 +3,11 @@ package bjc.dicelang.examples; import java.util.Scanner; import bjc.dicelang.ast.DiceASTEvaluator; +import bjc.dicelang.ast.DiceASTInliner; import bjc.dicelang.ast.DiceASTParser; import bjc.dicelang.ast.nodes.IDiceASTNode; - import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.FunctionalStringTokenizer; import bjc.utils.funcdata.IFunctionalList; import bjc.utils.funcdata.IFunctionalMap; import bjc.utils.funcdata.ITree; @@ -18,6 +19,49 @@ import bjc.utils.funcdata.ITree; * */ public class DiceASTLanguageTest { + private static IFunctionalMap actions; + + static { + actions = new FunctionalMap<>(); + + actions.put("inline", DiceASTLanguageTest::handleInlineAction); + + actions.put("env", (tokenizer, enviroment) -> { + enviroment.forEach((varName, varValue) -> { + System.out.println(varName + " is bound to " + varValue); + }); + }); + } + + private static void handleInlineAction( + FunctionalStringTokenizer tokenizer, + IFunctionalMap> enviroment) { + // Skip the pragma name + tokenizer.nextToken(); + + IFunctionalList pragmaArgs = tokenizer.toList(); + + if (pragmaArgs.getSize() < 3) { + System.err.println( + "ERROR: Inline requires at least 3 parameters. They are:" + + "\n\t1. The name of the expression to inline." + + "\n\t2. The name of the variable to bind the result to." + + "\n\t3 and onwards. Names of variables to inline in the expression."); + } else { + String inlineExpression = pragmaArgs.getByIndex(0); + String variableName = pragmaArgs.getByIndex(1); + + IFunctionalList inlinedVariables = + pragmaArgs.tail().tail(); + + ITree inlinedExpression = DiceASTInliner + .selectiveInline(enviroment.get(inlineExpression), + enviroment, inlinedVariables); + + enviroment.put(variableName, inlinedExpression); + } + } + /** * Main method of class * @@ -36,6 +80,25 @@ public class DiceASTLanguageTest { new FunctionalMap<>(); while (!currentLine.equalsIgnoreCase("quit")) { + String possibleActionName = currentLine.split(" ")[0]; + + if (actions.containsKey(possibleActionName)) { + System.err.println( + "\nTRACE: Executing action " + possibleActionName + + " with line " + currentLine + "\n"); + + // Execute action + FunctionalStringTokenizer tokenizer = + new FunctionalStringTokenizer(currentLine); + + actions.get(possibleActionName).accept(tokenizer, + enviroment); + + currentLine = getNextCommand(inputSource, commandNumber); + + continue; + } + // Build an AST from the string expression ITree builtAST; @@ -83,7 +146,7 @@ public class DiceASTLanguageTest { private static String getNextCommand(Scanner inputSource, int commandNumber) { - System.out.print("dice-lang-" + commandNumber + "> "); + System.out.print("\ndice-lang-" + commandNumber + "> "); return inputSource.nextLine(); } diff --git a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTPragma.java b/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTPragma.java new file mode 100644 index 0000000..67b0adc --- /dev/null +++ b/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTPragma.java @@ -0,0 +1,19 @@ +package bjc.dicelang.examples; + +import java.util.function.BiConsumer; + +import bjc.dicelang.ast.nodes.IDiceASTNode; +import bjc.utils.funcdata.FunctionalStringTokenizer; +import bjc.utils.funcdata.IFunctionalMap; +import bjc.utils.funcdata.ITree; + +/** + * Alias for the type of a 'pragma' or special language command + * + * @author ben + * + */ +public interface DiceASTPragma extends + BiConsumer>> { + // Just an alias +} diff --git a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTReferenceSanitizer.java b/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTReferenceSanitizer.java new file mode 100644 index 0000000..b2e441d --- /dev/null +++ b/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTReferenceSanitizer.java @@ -0,0 +1,28 @@ +package bjc.dicelang.examples; + +import bjc.dicelang.ast.nodes.IDiceASTNode; +import bjc.utils.funcdata.IFunctionalMap; +import bjc.utils.funcdata.ITree; + +/** + * Sanitize the references in an AST so that a variable that refers to + * itself in its definition has the occurance of it replaced with its + * previous definition + * + * @author ben + * + */ +public class DiceASTReferenceSanitizer { + /** + * Sanitize the references in an AST + * + * @param ast + * @param enviroment + * @return The sanitized AST + */ + public static ITree sanitize(ITree ast, + IFunctionalMap> enviroment) { + // TODO implement me + return null; + } +} diff --git a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceExpressionPreparer.java b/dice-lang/src/examples/java/bjc/dicelang/examples/DiceExpressionPreparer.java index 1d46cc9..91abf7d 100644 --- a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceExpressionPreparer.java +++ b/dice-lang/src/examples/java/bjc/dicelang/examples/DiceExpressionPreparer.java @@ -52,10 +52,17 @@ public class DiceExpressionPreparer { ops.add(new Pair<>("(", "\\(")); ops.add(new Pair<>(")", "\\)")); + ops.add(new Pair<>("+", "\\+")); + ops.add(new Pair<>("-", "-")); + ops.add(new Pair<>("*", "\\*")); + ops.add(new Pair<>("/", "/")); + ops.add(new Pair<>(":=", ":=")); IFunctionalList fullyExpandedTokens = ListUtils.deAffixTokens(semiExpandedTokens, ops); + fullyExpandedTokens.removeIf((strang) -> strang.equals("")); + return yard.postfix(fullyExpandedTokens, (token) -> token); } } diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/ArithmeticCollapser.java b/dice-lang/src/main/java/bjc/dicelang/ast/ArithmeticCollapser.java index 5ad0a0f..5c00fe2 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/ArithmeticCollapser.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/ArithmeticCollapser.java @@ -16,8 +16,7 @@ import bjc.utils.funcdata.Tree; * @author ben * */ -final class ArithmeticCollapser - implements IOperatorCollapser { +final class ArithmeticCollapser implements IOperatorCollapser { private OperatorDiceNode type; private BinaryOperator valueOp; @@ -35,12 +34,18 @@ final class ArithmeticCollapser new Pair<>(0, new Tree<>(type)); BinaryOperator>> reducer = - (accumulatedState, currentState) -> { - return reduceStates(accumulatedState, - currentState); + (currentState, accumulatedState) -> { + // Force evaluation of accumulated state to prevent + // certain bugs from occuring + accumulatedState.merge((l, r) -> null); + + return reduceStates(accumulatedState, currentState); }; - return nodes.reduceAux(initState, reducer, (state) -> state); + IPair> reducedState = + nodes.reduceAux(initState, reducer, (state) -> state); + + return reducedState; } private IPair> reduceStates( @@ -52,9 +57,8 @@ final class ArithmeticCollapser .bind((currentValue, currentTree) -> { accumulatedTree.addChild(currentTree); - Integer combinedValue = - valueOp.apply(accumulatedValue, - currentValue); + Integer combinedValue = valueOp.apply( + accumulatedValue, currentValue); return new Pair<>(combinedValue, accumulatedTree); diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTEvaluator.java b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTEvaluator.java index e934b9f..016fa8a 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTEvaluator.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTEvaluator.java @@ -112,7 +112,10 @@ public class DiceASTEvaluator { String variableName = ((VariableDiceNode) leafNode).getVariable(); if (enviroment.containsKey(variableName)) { - return evaluateAST(enviroment.get(variableName), enviroment); + int result = + evaluateAST(enviroment.get(variableName), enviroment); + + return result; } // Value to allow for assignments @@ -148,6 +151,9 @@ public class DiceASTEvaluator { IPair> valueNode = nodes.getByIndex(1); + // Force valueNode to materialize for debugging purposes + valueNode.merge((l, r) -> null); + return nameNode.bindRight((nameTree) -> { return valueNode.bind((valueValue, valueTree) -> { if (containsSimpleVariable(nameTree)) { diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTInliner.java b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTInliner.java new file mode 100644 index 0000000..6091cc3 --- /dev/null +++ b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTInliner.java @@ -0,0 +1,115 @@ +package bjc.dicelang.ast; + +import bjc.dicelang.ast.nodes.DiceASTType; +import bjc.dicelang.ast.nodes.IDiceASTNode; +import bjc.dicelang.ast.nodes.VariableDiceNode; +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IFunctionalList; +import bjc.utils.funcdata.IFunctionalMap; +import bjc.utils.funcdata.ITree; +import bjc.utils.funcdata.Tree; + +/** + * Inline variables in a dice AST + * + * @author ben + * + */ +public class DiceASTInliner { + /** + * Inline all the variables in the AST + * + * @param ast + * The AST to inline variables into + * @param enviroment + * The enviroment to inline from + * @return The inlined AST + */ + public static ITree inlineAll(ITree ast, + IFunctionalMap> enviroment) { + // Tell the compiler that the null is for the entire varargs + // parameter, not a single one with a null value + return selectiveInline(ast, enviroment, (String[]) null); + } + + private static ITree inlineNode(IDiceASTNode node, + IFunctionalMap> enviroment, + boolean specificInline, + IFunctionalList variableNames) { + if (node.getType() != DiceASTType.VARIABLE) { + return new Tree<>(node); + } + + String variableName = ((VariableDiceNode) node).getVariable(); + + if (specificInline) { + if (variableNames.contains(variableName)) { + if (!enviroment.containsKey(variableName)) { + throw new UnsupportedOperationException( + "Attempted to inline non-existant variable " + + variableName); + } + + return enviroment.get(variableName); + } + } else { + if (!enviroment.containsKey(variableName)) { + throw new UnsupportedOperationException( + "Attempted to inline non-existant variable " + + variableName); + } + + return enviroment.get(variableName); + } + + return new Tree<>(node); + } + + /** + * Inline the specified variables in the AST + * + * @param ast + * The AST to inline variables into + * @param enviroment + * The enviroment to inline from + * @param variables + * The variables to inline + * @return The inlined AST + */ + public static ITree selectiveInline( + ITree ast, + IFunctionalMap> enviroment, + String... variables) { + if (variables != null && variables.length > 0) { + IFunctionalList variableNames = + new FunctionalList<>(variables); + + return ast.flatMapTree((node) -> { + return inlineNode(node, enviroment, true, variableNames); + }); + } + + return ast.flatMapTree((node) -> { + return inlineNode(node, enviroment, false, null); + }); + } + + /** + * Inline the specified variables in the AST + * + * @param ast + * The AST to inline variables into + * @param enviroment + * The enviroment to inline from + * @param variables + * The variables to inline + * @return The inlined AST + */ + public static ITree selectiveInline( + ITree ast, + IFunctionalMap> enviroment, + IFunctionalList variables) { + return selectiveInline(ast, enviroment, + variables.toArray(new String[0])); + } +} 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 95495bd..d8c94dc 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTParser.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTParser.java @@ -28,17 +28,21 @@ public class DiceASTParser { * The list of tokens to convert * @return An AST built from the tokens */ - public static ITree createFromString( - IFunctionalList tokens) { - ITree rawTokens = TreeConstructor.constructTree(tokens, - (token) -> { + public static ITree + createFromString(IFunctionalList tokens) { + ITree rawTokens = + TreeConstructor.constructTree(tokens, (token) -> { return isOperatorNode(token); }, (operator) -> false, null); + // The last argument is valid because there are no special // operators yet, so it'll never get called - return rawTokens.rebuildTree(DiceASTParser::convertLeafNode, - DiceASTParser::convertOperatorNode); + ITree tokenizedTree = + rawTokens.rebuildTree(DiceASTParser::convertLeafNode, + DiceASTParser::convertOperatorNode); + + return tokenizedTree; } private static boolean isOperatorNode(String token) { @@ -52,8 +56,8 @@ public class DiceASTParser { } private static IDiceASTNode convertLeafNode(String leafNode) { - DiceLiteralType literalType = ILiteralDiceNode - .getLiteralType(leafNode); + DiceLiteralType literalType = + ILiteralDiceNode.getLiteralType(leafNode); if (literalType != null) { switch (literalType) { diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/OperatorDiceNode.java b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/OperatorDiceNode.java index d034943..ab47b56 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/nodes/OperatorDiceNode.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/nodes/OperatorDiceNode.java @@ -2,10 +2,6 @@ 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 -// 2. DiceASTFlattener -// 3. DiceASTParser /** * A node that represents an operator * -- cgit v1.2.3