diff options
| author | bculkin2442 <bjculkin@mix.wvu.edu> | 2016-04-18 17:22:36 -0400 |
|---|---|---|
| committer | bculkin2442 <bjculkin@mix.wvu.edu> | 2016-04-18 17:22:36 -0400 |
| commit | bf5f97983a7cd58e5d1147d6d6d93c2b307fe0fb (patch) | |
| tree | 3d1e41c8a7b9cfa5f32c5708a3a223f4e7ee5e8f | |
| parent | 24f3ce54983348e1aa0229d5c08b3fe99d739d40 (diff) | |
Implemented arrays
14 files changed, 548 insertions, 60 deletions
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 a6d5c75..fb4b086 100644 --- a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTLanguageTest.java +++ b/dice-lang/src/examples/java/bjc/dicelang/examples/DiceASTLanguageTest.java @@ -7,6 +7,7 @@ import bjc.dicelang.ast.DiceASTInliner; import bjc.dicelang.ast.DiceASTOptimizer; import bjc.dicelang.ast.DiceASTParser; import bjc.dicelang.ast.DiceASTReferenceSanitizer; +import bjc.dicelang.ast.IResult; import bjc.dicelang.ast.nodes.IDiceASTNode; import bjc.dicelang.ast.optimization.ConstantCollapser; import bjc.dicelang.ast.optimization.OperationCondenser; @@ -114,7 +115,7 @@ public class DiceASTLanguageTest { IFunctionalList<String> preparedTokens = DiceExpressionPreparer.prepareCommand(currentLine); - + try { builtAST = DiceASTParser.createFromString(preparedTokens); } catch (IllegalStateException isex) { @@ -125,11 +126,17 @@ public class DiceASTLanguageTest { continue; } - int sampleRoll; + // Print out results + System.out.println("\tParsed: " + builtAST.toString()); ITree<IDiceASTNode> transformedAST = transformAST(builtAST, enviroment); + System.out + .println("\tEvaluated: " + transformedAST.toString()); + + IResult sampleRoll; + try { sampleRoll = DiceASTEvaluator.evaluateAST(transformedAST, enviroment); @@ -143,10 +150,6 @@ public class DiceASTLanguageTest { continue; } - // Print out results - System.out.println("\tParsed: " + builtAST.toString()); - System.out - .println("\tEvaluated: " + transformedAST.toString()); System.out.println("\t\tSample Roll: " + sampleRoll); // Increase the number of commands @@ -173,7 +176,11 @@ public class DiceASTLanguageTest { ITree<IDiceASTNode> sanitizedTree = DiceASTReferenceSanitizer .sanitize(condensedTree, enviroment); - return sanitizedTree; + optimizedTree = optimizer.optimizeTree(sanitizedTree, enviroment); + + condensedTree = OperationCondenser.condense(optimizedTree); + + return condensedTree; } private static String getNextCommand(Scanner inputSource, 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 caf7f37..4524fa0 100644 --- a/dice-lang/src/examples/java/bjc/dicelang/examples/DiceExpressionPreparer.java +++ b/dice-lang/src/examples/java/bjc/dicelang/examples/DiceExpressionPreparer.java @@ -67,6 +67,8 @@ public class DiceExpressionPreparer { ops.add(new Pair<>("(", "\\(")); ops.add(new Pair<>(")", "\\)")); + ops.add(new Pair<>("[", "\\[")); + ops.add(new Pair<>("]", "\\]")); IFunctionalList<String> fullyExpandedTokens = ListUtils.deAffixTokens(semiExpandedTokens, ops); 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 5c00fe2..ad508c8 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/ArithmeticCollapser.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/ArithmeticCollapser.java @@ -28,12 +28,12 @@ final class ArithmeticCollapser implements IOperatorCollapser { } @Override - public IPair<Integer, ITree<IDiceASTNode>> apply( - IFunctionalList<IPair<Integer, ITree<IDiceASTNode>>> nodes) { - IPair<Integer, ITree<IDiceASTNode>> initState = - new Pair<>(0, new Tree<>(type)); + public IPair<IResult, ITree<IDiceASTNode>> apply( + IFunctionalList<IPair<IResult, ITree<IDiceASTNode>>> nodes) { + IPair<IResult, ITree<IDiceASTNode>> initState = + new Pair<>(new IntegerResult(0), new Tree<>(type)); - BinaryOperator<IPair<Integer, ITree<IDiceASTNode>>> reducer = + BinaryOperator<IPair<IResult, ITree<IDiceASTNode>>> reducer = (currentState, accumulatedState) -> { // Force evaluation of accumulated state to prevent // certain bugs from occuring @@ -42,27 +42,133 @@ final class ArithmeticCollapser implements IOperatorCollapser { return reduceStates(accumulatedState, currentState); }; - IPair<Integer, ITree<IDiceASTNode>> reducedState = + IPair<IResult, ITree<IDiceASTNode>> reducedState = nodes.reduceAux(initState, reducer, (state) -> state); return reducedState; } - private IPair<Integer, ITree<IDiceASTNode>> reduceStates( - IPair<Integer, ITree<IDiceASTNode>> accumulatedState, - IPair<Integer, ITree<IDiceASTNode>> currentState) { + private IPair<IResult, ITree<IDiceASTNode>> reduceStates( + IPair<IResult, ITree<IDiceASTNode>> accumulatedState, + IPair<IResult, ITree<IDiceASTNode>> currentState) { return accumulatedState .bind((accumulatedValue, accumulatedTree) -> { return currentState .bind((currentValue, currentTree) -> { accumulatedTree.addChild(currentTree); - Integer combinedValue = valueOp.apply( - accumulatedValue, currentValue); - - return new Pair<>(combinedValue, - accumulatedTree); + return doArithmeticCollapse( + accumulatedValue, accumulatedTree, + currentValue); }); }); } + + private IPair<IResult, ITree<IDiceASTNode>> doArithmeticCollapse( + IResult accumulatedValue, ITree<IDiceASTNode> accumulatedTree, + IResult currentValue) { + boolean currentIsInt = + currentValue.getType() == ResultType.INTEGER; + boolean accumulatedIsInt = + accumulatedValue.getType() == ResultType.INTEGER; + + if (!currentIsInt) { + if (!accumulatedIsInt) { + IFunctionalList<IResult> resultList = combineArrayResults( + accumulatedValue, currentValue); + + return new Pair<>(new ArrayResult(resultList), + accumulatedTree); + } + + IFunctionalList<IResult> resultList = halfCombineLists( + ((ArrayResult) currentValue).getValue(), + accumulatedValue, true); + + return new Pair<>(new ArrayResult(resultList), + accumulatedTree); + } else if (!accumulatedIsInt) { + IFunctionalList<IResult> resultList = halfCombineLists( + ((ArrayResult) accumulatedValue).getValue(), + currentValue, false); + + return new Pair<>(new ArrayResult(resultList), + accumulatedTree); + } + + int accumulatedInt = ((IntegerResult) accumulatedValue).getValue(); + int currentInt = ((IntegerResult) currentValue).getValue(); + + int combinedValue = valueOp.apply(accumulatedInt, currentInt); + + return new Pair<>(new IntegerResult(combinedValue), + accumulatedTree); + } + + private IFunctionalList<IResult> combineArrayResults( + IResult accumulatedValue, IResult currentValue) { + IFunctionalList<IResult> currentList = + ((ArrayResult) currentValue).getValue(); + IFunctionalList<IResult> accumulatedList = + ((ArrayResult) accumulatedValue).getValue(); + + if (currentList.getSize() != accumulatedList.getSize()) { + throw new UnsupportedOperationException( + "Can only apply operations to equal-length arrays"); + } + + IFunctionalList<IResult> resultList = currentList.combineWith( + accumulatedList, (currentNode, accumulatedNode) -> { + boolean currentNotInt = + currentNode.getType() != ResultType.INTEGER; + boolean accumulatedNotInt = accumulatedNode + .getType() != ResultType.INTEGER; + + if (currentNotInt || accumulatedNotInt) { + throw new UnsupportedOperationException( + "Nesting of array operations isn't allowed"); + } + + int accumulatedInt = + ((IntegerResult) accumulatedNode).getValue(); + int currentInt = + ((IntegerResult) currentNode).getValue(); + + IResult combinedValue = new IntegerResult( + valueOp.apply(accumulatedInt, currentInt)); + return combinedValue; + }); + return resultList; + } + + private IFunctionalList<IResult> halfCombineLists( + IFunctionalList<IResult> list, IResult scalar, + boolean scalarLeft) { + if (scalar.getType() != ResultType.INTEGER) { + throw new UnsupportedOperationException( + "Nested array operations not supported"); + } + + int scalarInt = ((IntegerResult) scalar).getValue(); + + return list.map((element) -> { + if (element.getType() != ResultType.INTEGER) { + throw new UnsupportedOperationException( + "Nested array operations not supported"); + } + int elementInt = ((IntegerResult) element).getValue(); + + IResult combinedValue; + + if (scalarLeft) { + combinedValue = new IntegerResult( + valueOp.apply(scalarInt, elementInt)); + } else { + combinedValue = new IntegerResult( + valueOp.apply(elementInt, scalarInt)); + } + + return combinedValue; + }); + } }
\ No newline at end of file diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/ArrayResult.java b/dice-lang/src/main/java/bjc/dicelang/ast/ArrayResult.java new file mode 100644 index 0000000..1bd0940 --- /dev/null +++ b/dice-lang/src/main/java/bjc/dicelang/ast/ArrayResult.java @@ -0,0 +1,43 @@ +package bjc.dicelang.ast; + +import bjc.utils.funcdata.IFunctionalList; + +/** + * Represents a result that is an array of other results + * + * @author ben + * + * TODO finish implementing me + */ +public class ArrayResult implements IResult { + private IFunctionalList<IResult> arrayContents; + + /** + * Create a new array-valued result + * + * @param results + * The results in the array + */ + public ArrayResult(IFunctionalList<IResult> results) { + this.arrayContents = results; + } + + @Override + public ResultType getType() { + return ResultType.ARRAY; + } + + /** + * Get the value of this result + * + * @return The value of this result + */ + public IFunctionalList<IResult> getValue() { + return arrayContents; + } + + @Override + public String toString() { + return arrayContents.toString(); + } +} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTEvaluator.java b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTEvaluator.java index aeecb38..7d073d4 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTEvaluator.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTEvaluator.java @@ -1,6 +1,7 @@ package bjc.dicelang.ast; 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; @@ -8,9 +9,12 @@ 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.IHolder; import bjc.utils.data.IPair; +import bjc.utils.data.Identity; import bjc.utils.data.LazyPair; import bjc.utils.data.Pair; +import bjc.utils.funcdata.FunctionalList; import bjc.utils.funcdata.FunctionalMap; import bjc.utils.funcdata.IFunctionalList; import bjc.utils.funcdata.IFunctionalMap; @@ -71,12 +75,27 @@ public class DiceASTEvaluator { return parseLet(enviroment, nodes); }); + operatorCollapsers.put(OperatorDiceNode.ARRAY, (nodes) -> { + ITree<IDiceASTNode> returnedTree = + new Tree<>(OperatorDiceNode.ARRAY); + IFunctionalList<IResult> resultList = new FunctionalList<>(); + + nodes.forEach((element) -> { + element.doWith((result, tree) -> { + resultList.add(result); + returnedTree.addChild(tree); + }); + }); + + return new Pair<>(new ArrayResult(resultList), returnedTree); + }); + return operatorCollapsers; } - private static IPair<Integer, ITree<IDiceASTNode>> parseLet( + private static IPair<IResult, ITree<IDiceASTNode>> parseLet( IFunctionalMap<String, ITree<IDiceASTNode>> enviroment, - IFunctionalList<IPair<Integer, ITree<IDiceASTNode>>> nodes) { + IFunctionalList<IPair<IResult, ITree<IDiceASTNode>>> nodes) { if (nodes.getSize() != 2) { throw new UnsupportedOperationException( "Can only use let with two expressions."); @@ -90,7 +109,7 @@ public class DiceASTEvaluator { enviroment.extend(); evaluateAST(bindTree, letEnviroment); - int exprResult = evaluateAST(expressionTree, letEnviroment); + IResult exprResult = evaluateAST(expressionTree, letEnviroment); IFunctionalList<ITree<IDiceASTNode>> childrn = nodes.map((pair) -> pair.getRight()); @@ -108,7 +127,7 @@ public class DiceASTEvaluator { * The enviroment to look up variables in * @return The integer value of the expression */ - public static int evaluateAST(ITree<IDiceASTNode> expression, + public static IResult evaluateAST(ITree<IDiceASTNode> expression, IFunctionalMap<String, ITree<IDiceASTNode>> enviroment) { IFunctionalMap<IDiceASTNode, IOperatorCollapser> collapsers = buildOperations(enviroment); @@ -118,7 +137,7 @@ public class DiceASTEvaluator { (pair) -> pair.getLeft()); } - private static IPair<Integer, ITree<IDiceASTNode>> evaluateLeaf( + private static IPair<IResult, ITree<IDiceASTNode>> evaluateLeaf( IDiceASTNode leafNode, IFunctionalMap<String, ITree<IDiceASTNode>> enviroment) { ITree<IDiceASTNode> returnedAST = new Tree<>(leafNode); @@ -139,31 +158,35 @@ public class DiceASTEvaluator { } } - private static Integer bindLiteralValue(IDiceASTNode leafNode, + private static IResult bindLiteralValue(IDiceASTNode leafNode, IFunctionalMap<String, ITree<IDiceASTNode>> enviroment) { String variableName = ((VariableDiceNode) leafNode).getVariable(); if (enviroment.containsKey(variableName)) { - int result = + IResult result = evaluateAST(enviroment.get(variableName), enviroment); return result; } - throw new UnsupportedOperationException( - "Attempted to dereference unbound variable " - + variableName); + // Allow for array assignment easily + return new IntegerResult(0); } - private static int evaluateLiteral(IDiceASTNode leafNode) { + private static IResult evaluateLiteral(IDiceASTNode leafNode) { DiceLiteralType literalType = ((ILiteralDiceNode) leafNode).getLiteralType(); switch (literalType) { case DICE: - return ((DiceLiteralNode) leafNode).getValue().roll(); + int diceRoll = + ((DiceLiteralNode) leafNode).getValue().roll(); + + return new IntegerResult(diceRoll); case INTEGER: - return ((IntegerLiteralNode) leafNode).getValue(); + int val = ((IntegerLiteralNode) leafNode).getValue(); + + return new IntegerResult(val); default: throw new UnsupportedOperationException("Literal value '" + leafNode + "' is of a type (" + literalType @@ -171,22 +194,19 @@ public class DiceASTEvaluator { } } - private static IPair<Integer, ITree<IDiceASTNode>> parseBinding( + private static IPair<IResult, ITree<IDiceASTNode>> parseBinding( IFunctionalMap<String, ITree<IDiceASTNode>> enviroment, - IFunctionalList<IPair<Integer, ITree<IDiceASTNode>>> nodes) { + IFunctionalList<IPair<IResult, ITree<IDiceASTNode>>> nodes) { if (nodes.getSize() != 2) { throw new UnsupportedOperationException( "Can only bind nodes with two children. Problem children are " + nodes); } - IPair<Integer, ITree<IDiceASTNode>> nameNode = nodes.getByIndex(0); - IPair<Integer, ITree<IDiceASTNode>> valueNode = + IPair<IResult, ITree<IDiceASTNode>> nameNode = nodes.getByIndex(0); + IPair<IResult, ITree<IDiceASTNode>> 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 (DiceASTUtils.containsSimpleVariable(nameTree)) { @@ -197,6 +217,36 @@ public class DiceASTEvaluator { enviroment.put(varName, valueTree); return new Pair<>(valueValue, nameTree); + } else if (nameTree.getHead() == OperatorDiceNode.ARRAY) { + if (valueTree.getHead() == OperatorDiceNode.ARRAY) { + if (nameTree.getChildrenCount() != valueTree + .getChildrenCount()) { + throw new UnsupportedOperationException( + "Array assignment must be between two equal length arrays"); + } + + IHolder<Integer> childCount = new Identity<>(0); + + nameTree.doForChildren((child) -> { + doArrayAssign(enviroment, nameNode, nameTree, + valueTree, childCount, child); + + childCount.transform(val -> val + 1); + }); + + return new Pair<>(valueValue, nameTree); + } + + nameTree.doForChildren((child) -> { + String varName = child.transformHead((nameNod) -> { + return ((VariableDiceNode) nameNod) + .getVariable(); + }); + + enviroment.put(varName, valueTree); + }); + + return new Pair<>(valueValue, nameTree); } throw new UnsupportedOperationException( @@ -206,24 +256,45 @@ public class DiceASTEvaluator { }); } - private static IPair<Integer, ITree<IDiceASTNode>> parseGroup( - IFunctionalList<IPair<Integer, ITree<IDiceASTNode>>> nodes) { + private static void doArrayAssign( + IFunctionalMap<String, ITree<IDiceASTNode>> enviroment, + IPair<IResult, ITree<IDiceASTNode>> nameNode, + ITree<IDiceASTNode> nameTree, ITree<IDiceASTNode> valueTree, + IHolder<Integer> childCount, ITree<IDiceASTNode> child) { + if (nameTree.getHead().getType() != DiceASTType.VARIABLE) { + throw new UnsupportedOperationException( + "Assigning to complex variables isn't supported. Problem node is " + + nameNode.getRight()); + } + + String varName = child.transformHead((nameNod) -> { + return ((VariableDiceNode) nameNod).getVariable(); + }); + + enviroment.put(varName, valueTree.getChild(childCount.getValue())); + + childCount.transform(val -> val + 1); + } + + private static IPair<IResult, ITree<IDiceASTNode>> parseGroup( + IFunctionalList<IPair<IResult, ITree<IDiceASTNode>>> nodes) { if (nodes.getSize() != 2) { throw new UnsupportedOperationException( "Can only form a group from two dice"); } - IPair<Integer, ITree<IDiceASTNode>> numberDiceNode = + IPair<IResult, ITree<IDiceASTNode>> numberDiceNode = nodes.getByIndex(0); - IPair<Integer, ITree<IDiceASTNode>> diceTypeNode = + IPair<IResult, ITree<IDiceASTNode>> diceTypeNode = nodes.getByIndex(1); return numberDiceNode.bind((numberDiceValue, numberDiceTree) -> { return diceTypeNode.bind((diceTypeValue, diceTypeTree) -> { - ComplexDice cDice = - new ComplexDice(numberDiceValue, diceTypeValue); + ComplexDice cDice = new ComplexDice( + ((IntegerResult) numberDiceValue).getValue(), + ((IntegerResult) diceTypeValue).getValue()); - return new Pair<>(cDice.roll(), + return new Pair<>(new IntegerResult(cDice.roll()), new Tree<>(OperatorDiceNode.GROUP, numberDiceTree, diceTypeTree)); }); 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 d8c94dc..c746c8b 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTParser.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTParser.java @@ -1,6 +1,9 @@ package bjc.dicelang.ast; +import java.util.Deque; import java.util.InputMismatchException; +import java.util.function.Function; +import java.util.function.Predicate; import bjc.dicelang.IDiceExpression; import bjc.dicelang.ast.nodes.DiceLiteralNode; @@ -10,8 +13,13 @@ 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.FunctionalList; +import bjc.utils.funcdata.FunctionalMap; import bjc.utils.funcdata.IFunctionalList; +import bjc.utils.funcdata.IFunctionalMap; import bjc.utils.funcdata.ITree; +import bjc.utils.funcdata.Tree; +import bjc.utils.funcutils.StringUtils; import bjc.utils.parserutils.TreeConstructor; /** @@ -30,22 +38,77 @@ public class DiceASTParser { */ public static ITree<IDiceASTNode> createFromString(IFunctionalList<String> tokens) { + Predicate<String> specialPicker = (operator) -> { + if (StringUtils.containsOnly(operator, "\\[")) { + return true; + } else if (StringUtils.containsOnly(operator, "\\]")) { + return true; + } + + return false; + }; + + IFunctionalMap<String, Function<Deque<ITree<String>>, ITree<String>>> operators = + new FunctionalMap<>(); + + operators.put("[", (queuedTrees) -> { + Tree<String> openTree = new Tree<>("["); + + return openTree; + }); + + operators.put("]", (queuedTrees) -> { + return parseCloseArray(queuedTrees); + }); + ITree<String> 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 + }, specialPicker, operators::get); ITree<IDiceASTNode> tokenizedTree = rawTokens.rebuildTree(DiceASTParser::convertLeafNode, DiceASTParser::convertOperatorNode); - + return tokenizedTree; } + private static ITree<String> + parseCloseArray(Deque<ITree<String>> queuedTrees) { + IFunctionalList<ITree<String>> children = new FunctionalList<>(); + + while (shouldContinuePopping(queuedTrees)) { + children.add(queuedTrees.pop()); + } + + queuedTrees.pop(); + + children.reverse(); + + ITree<String> arrayTree = new Tree<>("[]", children); + + return arrayTree; + } + + private static boolean + shouldContinuePopping(Deque<ITree<String>> queuedTrees) { + String peekToken = queuedTrees.peek().getHead(); + + return !peekToken.equals("["); + } + private static boolean isOperatorNode(String token) { + if (StringUtils.containsOnly(token, "\\[")) { + return true; + } else if (StringUtils.containsOnly(token, "\\]")) { + return true; + } + + if (token.equals("[]")) { + // This is a synthetic operator, constructed by [ and ] + return true; + } + try { OperatorDiceNode.fromString(token); return true; diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTReferenceSanitizer.java b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTReferenceSanitizer.java index 05fddf6..6263ea9 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTReferenceSanitizer.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTReferenceSanitizer.java @@ -3,6 +3,8 @@ package bjc.dicelang.ast; import bjc.dicelang.ast.nodes.IDiceASTNode; import bjc.dicelang.ast.nodes.OperatorDiceNode; import bjc.dicelang.ast.nodes.VariableDiceNode; +import bjc.utils.data.IHolder; +import bjc.utils.data.Identity; import bjc.utils.funcdata.IFunctionalMap; import bjc.utils.funcdata.ITree; import bjc.utils.funcdata.TopDownTransformResult; @@ -41,6 +43,7 @@ public class DiceASTReferenceSanitizer { switch (((OperatorDiceNode) node)) { case ASSIGN: return TopDownTransformResult.TRANSFORM; + case ARRAY: case LET: return TopDownTransformResult.PASSTHROUGH; case ADD: @@ -60,28 +63,134 @@ public class DiceASTReferenceSanitizer { throw new UnsupportedOperationException( "Assignment must have two arguments."); } - + ITree<IDiceASTNode> nameTree = ast.getChild(0); ITree<IDiceASTNode> valueTree = ast.getChild(1); if (!DiceASTUtils.containsSimpleVariable(nameTree)) { - throw new UnsupportedOperationException( - "Assignment must be between a variable and a expression"); + if (nameTree.getHead() == OperatorDiceNode.ARRAY) { + IHolder<Boolean> allSimpleVariables = new Identity<>(true); + + nameTree.doForChildren((child) -> { + if (allSimpleVariables.getValue()) { + boolean isSimple = + DiceASTUtils.containsSimpleVariable(child); + + allSimpleVariables.replace(isSimple); + } + }); + + if (!allSimpleVariables.getValue()) { + throw new UnsupportedOperationException( + "Array assignment must be between variables and" + + " a expression/array of expressions"); + } + + if (valueTree.getHead() == OperatorDiceNode.ARRAY) { + if (nameTree.getChildrenCount() != valueTree + .getChildrenCount()) { + throw new UnsupportedOperationException( + "Array assignment between arrays must be" + + " between two arrays of equal length"); + } + } + } else { + throw new UnsupportedOperationException( + "Assignment must be between a variable and a expression"); + } + } + + if (nameTree.getHead() == OperatorDiceNode.ARRAY) { + if (valueTree.getHead() == OperatorDiceNode.ARRAY) { + IHolder<Integer> childCounter = new Identity<>(0); + + ITree<IDiceASTNode> returnTree = + new Tree<>(OperatorDiceNode.ARRAY); + + nameTree.doForChildren((child) -> { + String variableName = child.transformHead((node) -> { + return ((VariableDiceNode) node).getVariable(); + }); + + ITree<IDiceASTNode> currentValue = + valueTree.getChild(childCounter.getValue()); + + ITree<IDiceASTNode> sanitizedSubtree = + doSingleSanitize(ast, enviroment, child, + currentValue, variableName); + + if (sanitizedSubtree == null) { + ITree<IDiceASTNode> oldTree = new Tree<>( + ast.getHead(), child, currentValue); + + returnTree.addChild(oldTree); + } else { + returnTree.addChild(sanitizedSubtree); + } + + childCounter.transform((count) -> count + 1); + }); + + return returnTree; + } + + ITree<IDiceASTNode> returnTree = + new Tree<>(OperatorDiceNode.ARRAY); + + nameTree.doForChildren((child) -> { + String variableName = child.transformHead( + (node) -> ((VariableDiceNode) node).getVariable()); + + ITree<IDiceASTNode> sanitizedChild = doSingleSanitize(ast, + enviroment, child, valueTree, variableName); + if (sanitizedChild == null) { + ITree<IDiceASTNode> oldTree = + new Tree<>(ast.getHead(), child, valueTree); + + returnTree.addChild(oldTree); + } else { + returnTree.addChild(sanitizedChild); + } + }); + + return returnTree; } String variableName = nameTree.transformHead( (node) -> ((VariableDiceNode) node).getVariable()); + ITree<IDiceASTNode> sanitizedTree = doSingleSanitize(ast, + enviroment, nameTree, valueTree, variableName); + + if (sanitizedTree == null) { + return ast; + } + + return sanitizedTree; + } + + private static ITree<IDiceASTNode> doSingleSanitize( + ITree<IDiceASTNode> ast, + IFunctionalMap<String, ITree<IDiceASTNode>> enviroment, + ITree<IDiceASTNode> nameTree, ITree<IDiceASTNode> valueTree, + String variableName) { if (enviroment.containsKey(variableName)) { + // @ is a meta-variable standing for the left side of an + // assignment + enviroment.put("@", enviroment.get(variableName)); + // We should always inline out references to last, because it // will always change ITree<IDiceASTNode> inlinedValue = DiceASTInliner.selectiveInline(valueTree, enviroment, - variableName, "last"); + variableName, "last", "@"); + + // Remove temporary meta-variable + enviroment.remove("@"); return new Tree<>(ast.getHead(), nameTree, inlinedValue); } - return ast; + return null; } } diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/IOperatorCollapser.java b/dice-lang/src/main/java/bjc/dicelang/ast/IOperatorCollapser.java index 10df77b..8fd066d 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/IOperatorCollapser.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/IOperatorCollapser.java @@ -14,7 +14,7 @@ import bjc.utils.funcdata.ITree; * */ public interface IOperatorCollapser extends - Function<IFunctionalList<IPair<Integer, ITree<IDiceASTNode>>>, - IPair<Integer, ITree<IDiceASTNode>>> { + Function<IFunctionalList<IPair<IResult, ITree<IDiceASTNode>>>, + IPair<IResult, ITree<IDiceASTNode>>> { // Just an alias } diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/IResult.java b/dice-lang/src/main/java/bjc/dicelang/ast/IResult.java new file mode 100644 index 0000000..9a3f325 --- /dev/null +++ b/dice-lang/src/main/java/bjc/dicelang/ast/IResult.java @@ -0,0 +1,16 @@ +package bjc.dicelang.ast; + +/** + * Represents a result from an expression evaluation + * + * @author ben + * + */ +public interface IResult { + /** + * Get the type of this result + * + * @return The type of this result + */ + public ResultType getType(); +} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/IntegerResult.java b/dice-lang/src/main/java/bjc/dicelang/ast/IntegerResult.java new file mode 100644 index 0000000..c8d9a64 --- /dev/null +++ b/dice-lang/src/main/java/bjc/dicelang/ast/IntegerResult.java @@ -0,0 +1,40 @@ +package bjc.dicelang.ast; + +/** + * Represents a integer-valued result + * + * @author ben + * + */ +public class IntegerResult implements IResult { + private int value; + + /** + * Create a new integer valued result + * + * @param val + * The value of the result + */ + public IntegerResult(int val) { + value = val; + } + + /** + * Get the value of this result + * + * @return The value of this result + */ + public int getValue() { + return value; + } + + @Override + public ResultType getType() { + return ResultType.INTEGER; + } + + @Override + public String toString() { + return Integer.toString(value); + } +} diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/ResultType.java b/dice-lang/src/main/java/bjc/dicelang/ast/ResultType.java new file mode 100644 index 0000000..d5e94b9 --- /dev/null +++ b/dice-lang/src/main/java/bjc/dicelang/ast/ResultType.java @@ -0,0 +1,18 @@ +package bjc.dicelang.ast; + +/** + * Represents the result of a computation + * + * @author ben + * + */ +public enum ResultType { + /** + * Represents a result that is equivalent to a single integer + */ + INTEGER, + /** + * Represents a result that is an array + */ + ARRAY; +} 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 c0f7bd3..18df122 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 @@ -34,6 +34,10 @@ public enum OperatorDiceNode implements IDiceASTNode { */ GROUP(DICE), /** + * Represents constructing an array from a sequence of expressions + */ + ARRAY(EXPRESSION), + /** * Represents assigning one node to another */ ASSIGN(EXPRESSION), @@ -79,6 +83,8 @@ public enum OperatorDiceNode implements IDiceASTNode { return COMPOUND; case "=>": return LET; + case "[]": + return ARRAY; default: throw new IllegalArgumentException( s + " is not a valid operator node"); diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/optimization/ConstantCollapser.java b/dice-lang/src/main/java/bjc/dicelang/ast/optimization/ConstantCollapser.java index e476d74..7b746ac 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/optimization/ConstantCollapser.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/optimization/ConstantCollapser.java @@ -77,6 +77,12 @@ public class ConstantCollapser implements IOptimizationPass { } return new Tree<>(operator, children); + case ARRAY: + if (children.getSize() != 1) { + return new Tree<>(operator, children); + } + + return children.first(); case ASSIGN: case LET: default: diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/optimization/OperationCondenser.java b/dice-lang/src/main/java/bjc/dicelang/ast/optimization/OperationCondenser.java index f41a3ef..3988d2a 100644 --- a/dice-lang/src/main/java/bjc/dicelang/ast/optimization/OperationCondenser.java +++ b/dice-lang/src/main/java/bjc/dicelang/ast/optimization/OperationCondenser.java @@ -51,6 +51,7 @@ public class OperationCondenser { case DIVIDE: case COMPOUND: return TopDownTransformResult.PUSHDOWN; + case ARRAY: case ASSIGN: case GROUP: case LET: |
