diff options
| author | bculkin2442 <bjculkin@mix.wvu.edu> | 2017-02-26 08:22:59 -0500 |
|---|---|---|
| committer | bculkin2442 <bjculkin@mix.wvu.edu> | 2017-02-26 08:22:59 -0500 |
| commit | 8e40356ab2e7aa79c9e4c90c0de48308506c359c (patch) | |
| tree | dd052f4a4c18c2621932eed69cf2ec8da210a717 /dice-lang/src | |
| parent | 07d8b9547a654021d8c56021779f4cdaa5f03f1b (diff) | |
Dice math
Diffstat (limited to 'dice-lang/src')
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/DiceBox.java | 87 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/DiceLangConsole.java | 42 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java | 28 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/Errors.java | 8 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/Evaluator.java | 186 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/Shunter.java | 225 |
6 files changed, 413 insertions, 163 deletions
diff --git a/dice-lang/src/bjc/dicelang/v2/DiceBox.java b/dice-lang/src/bjc/dicelang/v2/DiceBox.java index 2a32f47..9859d93 100644 --- a/dice-lang/src/bjc/dicelang/v2/DiceBox.java +++ b/dice-lang/src/bjc/dicelang/v2/DiceBox.java @@ -274,6 +274,93 @@ public class DiceBox { } } + public static class MathDie implements Die { + public static enum MathOp { + ADD, SUBTRACT, MULTIPLY; + + public String toString() { + switch(this) { + case ADD: + return "+"; + case SUBTRACT: + return "-"; + case MULTIPLY: + return "*"; + default: + return this.name(); + } + } + } + + private MathOp type; + + private Die left; + private Die right; + + public MathDie(MathOp op, Die lft, Die rght) { + type = op; + + left = lft; + right = rght; + } + + public boolean canOptimize() { + return left.canOptimize() && right.canOptimize(); + } + + public long optimize() { + long lft = left.optimize(); + long rght = right.optimize(); + + switch(type) { + case ADD: + return lft + rght; + case SUBTRACT: + return lft - rght; + case MULTIPLY: + return lft * rght; + default: + return 0; + } + } + + public long roll() { + long lft = left.roll(); + long rght = right.roll(); + + switch(type) { + case ADD: + return lft + rght; + case SUBTRACT: + return lft - rght; + case MULTIPLY: + return lft * rght; + default: + return 0; + } + } + + public long rollSingle() { + long lft = left.rollSingle(); + long rght = right.rollSingle(); + + switch(type) { + case ADD: + return lft + rght; + case SUBTRACT: + return lft - rght; + case MULTIPLY: + return lft * rght; + default: + return 0; + } + } + + public String toString() { + return left.toString() + " " + type.toString() + " " + right.toString(); + } + } + public static class SimpleDieList implements DieList { private Die numDice; private Die size; diff --git a/dice-lang/src/bjc/dicelang/v2/DiceLangConsole.java b/dice-lang/src/bjc/dicelang/v2/DiceLangConsole.java index 863bce5..9d69704 100644 --- a/dice-lang/src/bjc/dicelang/v2/DiceLangConsole.java +++ b/dice-lang/src/bjc/dicelang/v2/DiceLangConsole.java @@ -1,11 +1,14 @@ package bjc.dicelang.v2; +import java.io.IOException; import java.util.List; import java.util.LinkedList; -import java.util.Scanner; import java.util.regex.Matcher; import java.util.regex.Pattern; +import jline.ConsoleReader; +import jline.Terminal; + import static bjc.dicelang.v2.Errors.ErrorKey.*; public class DiceLangConsole { @@ -13,20 +16,35 @@ public class DiceLangConsole { private DiceLangEngine eng; + private ConsoleReader read; + public DiceLangConsole(String[] args) { // @TODO do something with the args commandNumber = 0; eng = new DiceLangEngine(); + + Terminal.setupTerminal(); } public void run() { + try { + read = new ConsoleReader(); + } catch(IOException ioex) { + System.out.println("ERROR: Console init failed"); + return; + } + System.out.println("dice-lang v0.2"); - Scanner scn = new Scanner(System.in); + String comm = null; - System.out.printf("(%d) dice-lang> ", commandNumber); - String comm = scn.nextLine(); + try { + comm = read.readLine(String.format("(%d) dice-lang> ", commandNumber)); + } catch (IOException ioex) { + System.out.println("ERROR: I/O failed"); + return; + } while(!comm.equals("quit") && !comm.equals("exit")) { if(comm.startsWith("pragma")) { @@ -45,11 +63,13 @@ public class DiceLangConsole { commandNumber += 1; } - System.out.printf("(%d) dice-lang> ", commandNumber); - comm = scn.nextLine(); + try { + comm = read.readLine(String.format("(%d) dice-lang> ", commandNumber)); + } catch (IOException ioex) { + System.out.println("ERROR: I/O failed"); + return; + } } - - scn.close(); } private boolean handlePragma(String pragma) { @@ -73,6 +93,9 @@ public class DiceLangConsole { case "prefix": System.out.println("\tPrefix mode is now " + eng.togglePrefix()); break; + case "stepeval": + System.out.println("\tStepeval mode is now" + eng.toggleStepEval()); + break; case "define": return defineMode(pragma.substring(7)); case "help": @@ -99,6 +122,9 @@ public class DiceLangConsole { case "prefix": System.out.println("\tToggle prefix mode. (Reverse token order instead of shunting)"); break; + case "stepeval": + System.out.println("\tToggle stepeval mode. (Print out evaluation progress)"); + break; case "define": System.out.println("\tAdd a macro rewrite directive."); System.out.println("\tdefine <priority> <type> <recursion> <guard> <circular> <patterns>..."); diff --git a/dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java b/dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java index 1517abd..0798988 100644 --- a/dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java +++ b/dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java @@ -112,7 +112,7 @@ public class DiceLangEngine { debugMode = true; postfixMode = false; prefixMode = false; - stepEval = true; + stepEval = false; streamEng = new StreamEngine(this); @@ -160,6 +160,12 @@ public class DiceLangEngine { return prefixMode; } + public boolean toggleStepEval() { + stepEval = !stepEval; + + return stepEval; + } + /* * Matches quote-delimited strings * (like "text" or "text\"text") @@ -174,8 +180,8 @@ public class DiceLangEngine { */ private Pattern quotePattern = Pattern.compile("\"([^\\\"]*(?:\\\"(?:[^\\\"])*)*)\""); - // Similiar to the above, but using angle brackets instead of quotes and not allowing spaces - private Pattern nonExpandPattern = Pattern.compile("<([^\\>&&[^\\s]]*(?:\\>(?:[^\\>&&[^\\s]])*)*)>"); + // Similiar to the above, but using angle brackets instead of quotes + private Pattern nonExpandPattern = Pattern.compile("<<([^\\>]*(?:\\>(?:[^\\>])*)*)>>"); public boolean runCommand(String command) { // Sort the defines if they aren't sorted @@ -352,7 +358,7 @@ public class DiceLangEngine { int treeNo = 1; for(ITree<Node> ast : astForest) { - System.out.println("\t\tTree " + treeNo + " in forest:\n\t\t " + ast); + System.out.println("\t\tTree " + treeNo + " in forest:\n" + ast); if(stepEval) { int step = 1; @@ -362,6 +368,13 @@ public class DiceLangEngine { System.out.printf("\t\tStep %d: Node is %s", step, nodeStep); + if(nodeStep == null) { + System.out.println(); + + step += 1; + continue; + } + if(nodeStep.getHead().type == Node.Type.RESULT) { EvaluatorResult res = nodeStep.getHead().resultVal; @@ -387,7 +400,7 @@ public class DiceLangEngine { System.out.printf("\t\tEvaluates to %s", res); if(res.type == EvaluatorResult.Type.DICE) { - System.out.println(" (sample roll " + res.diceVal.value() + ")"); + System.out.println("\t\t (sample roll " + res.diceVal.value() + ")"); } } @@ -544,11 +557,6 @@ public class DiceLangEngine { tk = new Token(FLOAT_LIT, Double.parseDouble(token)); } else if(DiceBox.isValidExpression(token)) { tk = new Token(DICE_LIT, DiceBox.parseExpression(token)); - - if(debugMode) - System.out.println("\tDEBUG: Parsed dice expression" - + ", evaluated as: " - + tk.diceValue.value()); } else { Matcher stringLit = stringLitMatcher.matcher(token); diff --git a/dice-lang/src/bjc/dicelang/v2/Errors.java b/dice-lang/src/bjc/dicelang/v2/Errors.java index 0f4c52d..6ad74ab 100644 --- a/dice-lang/src/bjc/dicelang/v2/Errors.java +++ b/dice-lang/src/bjc/dicelang/v2/Errors.java @@ -45,6 +45,8 @@ public class Errors { EK_EVAL_STRINGMATH, // Attempted divide by zero EK_EVAL_DIVZERO, + // Attempted to divide dice + EK_EVAL_DIVDICE, // Unknown math operator EK_EVAL_UNMATH, // Unknown token reference @@ -150,7 +152,8 @@ public class Errors { System.out.printf("\tERROR: Unknown node in evaluator: %s\n", args[0]); break; case EK_EVAL_INVBIN: - System.out.printf("\tERROR: Binary operators take 2 operand, not %s\n", args[0]); + System.out.printf("\tERROR: Binary operators take 2 operand, not %s\n" + + "\tProblem node is %s\n", args[0], args[1]); break; case EK_EVAL_UNBIN: System.out.printf("\tERROR: Unknown binary operator %s\n", args[0]); @@ -161,6 +164,9 @@ public class Errors { case EK_EVAL_DIVZERO: System.out.printf("\tERROR: Attempted divide by zero\n"); break; + case EK_EVAL_DIVDICE: + System.out.printf("\tERROR: Dice cannot be divided\n"); + break; case EK_EVAL_UNMATH: System.out.printf("\tERROR: Unknown math binary operator: %s\n", args[0]); break; diff --git a/dice-lang/src/bjc/dicelang/v2/Evaluator.java b/dice-lang/src/bjc/dicelang/v2/Evaluator.java index 7624b47..904c8bc 100644 --- a/dice-lang/src/bjc/dicelang/v2/Evaluator.java +++ b/dice-lang/src/bjc/dicelang/v2/Evaluator.java @@ -1,16 +1,24 @@ package bjc.dicelang.v2; import bjc.utils.data.ITree; +import bjc.utils.data.SingleIterator; import bjc.utils.data.Tree; import bjc.utils.data.TopDownTransformIterator; import bjc.utils.data.TopDownTransformResult; + +import java.util.Deque; import java.util.Iterator; +import java.util.LinkedList; import java.util.function.Consumer; import static bjc.dicelang.v2.Errors.ErrorKey.*; import static bjc.dicelang.v2.EvaluatorResult.Type.*; public class Evaluator { + private static enum CoerceSteps { + INTEGER, FLOAT; + } + private static class Context { public Consumer<Iterator<ITree<Node>>> thunk; @@ -18,19 +26,19 @@ public class Evaluator { } private static Node FAIL() { - return new Node(Node.Type.RESULT, new EvaluatorResult(EvaluatorResult.Type.FAILURE)); + return new Node(Node.Type.RESULT, new EvaluatorResult(FAILURE)); } private static Node FAIL(ITree<Node> orig) { - return new Node(Node.Type.RESULT, new EvaluatorResult(EvaluatorResult.Type.FAILURE, orig)); + return new Node(Node.Type.RESULT, new EvaluatorResult(FAILURE, orig)); } private static Node FAIL(Node orig) { - return new Node(Node.Type.RESULT, new EvaluatorResult(EvaluatorResult.Type.FAILURE, orig)); + return new Node(Node.Type.RESULT, new EvaluatorResult(FAILURE, orig)); } private static Node FAIL(EvaluatorResult res) { - return new Node(Node.Type.RESULT, new EvaluatorResult(EvaluatorResult.Type.FAILURE, new Node(Node.Type.RESULT, res))); + return new Node(Node.Type.RESULT, new EvaluatorResult(FAILURE, new Node(Node.Type.RESULT, res))); } private DiceLangEngine eng; @@ -53,6 +61,7 @@ public class Evaluator { (node) -> this.evaluateNode(node, ctx)).getHead().resultVal; } + // @FIXME Something's broken with step evaluation public Iterator<ITree<Node>> stepDebug(ITree<Node> comm) { Context ctx = new Context(); @@ -89,6 +98,8 @@ public class Evaluator { return evaluateTokenRef(ast.getHead().tokenVal, ctx); case ROOT: return ast.getChild(ast.getChildrenCount() - 1); + case RESULT: + return ast; default: Errors.inst.printError(EK_EVAL_INVNODE, ast.getHead().type.toString()); return new Tree<>(FAIL(ast)); @@ -102,21 +113,66 @@ public class Evaluator { Errors.inst.printError(EK_EVAL_UNUNARY, Integer.toString(ast.getChildrenCount())); return new Tree<>(FAIL(ast)); } - break; + + ITree<Node> toCoerce = ast.getChild(0); + ITree<Node> retVal = new Tree<>(toCoerce.getHead()); + Deque<ITree<Node>> children = new LinkedList<>(); + + CoerceSteps curLevel = CoerceSteps.INTEGER; + + for(int i = 0; i < toCoerce.getChildrenCount(); i++) { + ITree<Node> child = toCoerce.getChild(i); + ITree<Node> nChild = null; + + if(ctx.isDebug) { + Iterator<ITree<Node>> nd = stepDebug(child); + + for(; nd.hasNext(); nChild = nd.next()) { + ctx.thunk.accept(new SingleIterator(child)); + } + } else { + nChild = new Tree<>(new Node(Node.Type.RESULT, evaluate(child))); + + if(nChild != null) ctx.thunk.accept(new SingleIterator(nChild)); + } + + Node childNode = nChild.getHead(); + EvaluatorResult res = childNode.resultVal; + + if(res.type == FLOAT) curLevel = CoerceSteps.FLOAT; + + children.add(nChild); + } + + for(ITree<Node> child : children) { + Node nd = child.getHead(); + EvaluatorResult res = nd.resultVal; + + switch(res.type) { + case INT: + if(curLevel == CoerceSteps.FLOAT) { + nd.resultVal = new EvaluatorResult(FLOAT, (double)res.intVal); + } + default: + // Do nothing + break; + } + + retVal.addChild(child); + } + + return retVal; default: Errors.inst.printError(EK_EVAL_INVUNARY, ast.getHead().operatorType.toString()); return new Tree<>(FAIL(ast)); } - - // @TODO remove me - return new Tree<>(FAIL(ast)); } private ITree<Node> evaluateBinaryOp(ITree<Node> ast, Context ctx) { Token.Type binOp = ast.getHead().operatorType; if(ast.getChildrenCount() != 2) { - Errors.inst.printError(EK_EVAL_INVBIN, Integer.toString(ast.getChildrenCount())); + Errors.inst.printError(EK_EVAL_INVBIN, Integer.toString(ast.getChildrenCount()), ast.toString()); return new Tree<>(FAIL(ast)); } @@ -208,24 +264,27 @@ public class Evaluator { private ITree<Node> evaluateMathBinary(Token.Type op, EvaluatorResult left, EvaluatorResult right, Context ctx) { - if(left.type == EvaluatorResult.Type.DICE || right.type == EvaluatorResult.Type.DICE) { - System.out.println("\tEVALUATOR ERROR: Math on dice isn't supported yet"); - return new Tree<>(FAIL()); - } else if(left.type == EvaluatorResult.Type.STRING || right.type == EvaluatorResult.Type.STRING) { + if(left.type == STRING || right.type == STRING) { Errors.inst.printError(EK_EVAL_STRINGMATH); return new Tree<>(FAIL()); - } else if(left.type == EvaluatorResult.Type.FAILURE || right.type == EvaluatorResult.Type.FAILURE) { + } else if(left.type == FAILURE || right.type == FAILURE) { return new Tree<>(FAIL()); - } else if(left.type == EvaluatorResult.Type.INT && right.type != EvaluatorResult.Type.INT) { + } else if(left.type == INT && right.type != INT) { Errors.inst.printError(EK_EVAL_MISMATH); return new Tree<>(FAIL(right)); - } else if(left.type == EvaluatorResult.Type.FLOAT && right.type != EvaluatorResult.Type.FLOAT) { + } else if(left.type == FLOAT && right.type != FLOAT) { Errors.inst.printError(EK_EVAL_MISMATH); return new Tree<>(FAIL(right)); - } else if(right.type == EvaluatorResult.Type.INT && left.type != EvaluatorResult.Type.INT) { + } else if(left.type == DICE && right.type != DICE) { + Errors.inst.printError(EK_EVAL_MISMATH); + return new Tree<>(FAIL(right)); + } else if(right.type == INT && left.type != INT) { + Errors.inst.printError(EK_EVAL_MISMATH); + return new Tree<>(FAIL(left)); + } else if(right.type == FLOAT && left.type != FLOAT) { Errors.inst.printError(EK_EVAL_MISMATH); return new Tree<>(FAIL(left)); - } else if(right.type == EvaluatorResult.Type.FLOAT && left.type != EvaluatorResult.Type.FLOAT) { + } else if(right.type == DICE && left.type != DICE) { Errors.inst.printError(EK_EVAL_MISMATH); return new Tree<>(FAIL(left)); } @@ -234,58 +293,97 @@ public class Evaluator { switch(op) { case ADD: - if(left.type == EvaluatorResult.Type.INT) { - res = new EvaluatorResult(EvaluatorResult.Type.INT, left.intVal + right.intVal); + if(left.type == INT) { + res = new EvaluatorResult(INT, left.intVal + right.intVal); + } else if(left.type == DICE) { + if(left.diceVal.isList) { + Errors.inst.printError(EK_EVAL_INVDICE, left.toString()); + return new Tree<>(FAIL(left)); + } else if(right.diceVal.isList) { + Errors.inst.printError(EK_EVAL_INVDICE, right.toString()); + return new Tree<>(FAIL(right)); + } + + res = new EvaluatorResult(DICE, new DiceBox.MathDie(DiceBox.MathDie.MathOp.ADD, + left.diceVal.scalar, right.diceVal.scalar)); } else { - res = new EvaluatorResult(EvaluatorResult.Type.FLOAT, left.floatVal + right.floatVal); + res = new EvaluatorResult(FLOAT, left.floatVal + right.floatVal); } break; case SUBTRACT: - if(left.type == EvaluatorResult.Type.INT) { - res = new EvaluatorResult(EvaluatorResult.Type.INT, left.intVal - right.intVal); + if(left.type == INT) { + res = new EvaluatorResult(INT, left.intVal - right.intVal); + } else if(left.type == DICE) { + if(left.diceVal.isList) { + Errors.inst.printError(EK_EVAL_INVDICE, left.toString()); + return new Tree<>(FAIL(left)); + } else if(right.diceVal.isList) { + Errors.inst.printError(EK_EVAL_INVDICE, right.toString()); + return new Tree<>(FAIL(right)); + } + + res = new EvaluatorResult(DICE, new DiceBox.MathDie(DiceBox.MathDie.MathOp.SUBTRACT, + left.diceVal.scalar, right.diceVal.scalar)); } else { - res = new EvaluatorResult(EvaluatorResult.Type.FLOAT, left.floatVal - right.floatVal); + res = new EvaluatorResult(FLOAT, left.floatVal - right.floatVal); } break; case MULTIPLY: - if(left.type == EvaluatorResult.Type.INT) { - res = new EvaluatorResult(EvaluatorResult.Type.INT, left.intVal * right.intVal); + if(left.type == INT) { + res = new EvaluatorResult(INT, left.intVal * right.intVal); + } else if(left.type == DICE) { + if(left.diceVal.isList) { + Errors.inst.printError(EK_EVAL_INVDICE, left.toString()); + return new Tree<>(FAIL(left)); + } else if(right.diceVal.isList) { + Errors.inst.printError(EK_EVAL_INVDICE, right.toString()); + return new Tree<>(FAIL(right)); + } + + res = new EvaluatorResult(DICE, new DiceBox.MathDie(DiceBox.MathDie.MathOp.MULTIPLY, + left.diceVal.scalar, right.diceVal.scalar)); } else { - res = new EvaluatorResult(EvaluatorResult.Type.FLOAT, left.floatVal * right.floatVal); + res = new EvaluatorResult(FLOAT, left.floatVal * right.floatVal); } break; case DIVIDE: - if(left.type == EvaluatorResult.Type.INT) { + if(left.type == INT) { if(right.intVal == 0) { Errors.inst.printError(EK_EVAL_DIVZERO); - res = new EvaluatorResult(EvaluatorResult.Type.FAILURE, right); + res = new EvaluatorResult(FAILURE, right); } else { - res = new EvaluatorResult(EvaluatorResult.Type.FLOAT, left.intVal / right.intVal); + res = new EvaluatorResult(FLOAT, left.intVal / right.intVal); } - } else { + } else if(left.type == FLOAT) { if(right.floatVal == 0) { Errors.inst.printError(EK_EVAL_DIVZERO); - res = new EvaluatorResult(EvaluatorResult.Type.FAILURE, right); + res = new EvaluatorResult(FAILURE, right); } else { - res = new EvaluatorResult(EvaluatorResult.Type.FLOAT, left.floatVal / right.floatVal); + res = new EvaluatorResult(FLOAT, left.floatVal / right.floatVal); } + } else { + Errors.inst.printError(EK_EVAL_DIVDICE); + return new Tree<>(FAIL()); } break; case IDIVIDE: - if(left.type == EvaluatorResult.Type.INT) { + if(left.type == INT) { if(right.intVal == 0) { Errors.inst.printError(EK_EVAL_DIVZERO); - res = new EvaluatorResult(EvaluatorResult.Type.FAILURE, right); + res = new EvaluatorResult(FAILURE, right); } else { - res = new EvaluatorResult(EvaluatorResult.Type.INT, (int) (left.intVal / right.intVal)); + res = new EvaluatorResult(INT, (int) (left.intVal / right.intVal)); } - } else { + } else if(left.type == FLOAT) { if(right.floatVal == 0) { Errors.inst.printError(EK_EVAL_DIVZERO); - res = new EvaluatorResult(EvaluatorResult.Type.FAILURE, right); + res = new EvaluatorResult(FAILURE, right); } else { - res = new EvaluatorResult(EvaluatorResult.Type.INT, (int) (left.floatVal / right.floatVal)); + res = new EvaluatorResult(INT, (int) (left.floatVal / right.floatVal)); } + } else { + Errors.inst.printError(EK_EVAL_DIVDICE); + return new Tree<>(FAIL()); } break; default: @@ -301,20 +399,20 @@ public class Evaluator { switch(tk.type) { case INT_LIT: - res = new EvaluatorResult(EvaluatorResult.Type.INT, tk.intValue); + res = new EvaluatorResult(INT, tk.intValue); break; case FLOAT_LIT: - res = new EvaluatorResult(EvaluatorResult.Type.FLOAT, tk.floatValue); + res = new EvaluatorResult(FLOAT, tk.floatValue); break; case DICE_LIT: - res = new EvaluatorResult(EvaluatorResult.Type.DICE, tk.diceValue); + res = new EvaluatorResult(DICE, tk.diceValue); break; case STRING_LIT: - res = new EvaluatorResult(EvaluatorResult.Type.STRING, eng.stringLits.get((int)(tk.intValue))); + res = new EvaluatorResult(STRING, eng.stringLits.get((int)(tk.intValue))); break; default: Errors.inst.printError(EK_EVAL_UNTOK, tk.type.toString()); - res = new EvaluatorResult(EvaluatorResult.Type.FAILURE); + res = new EvaluatorResult(FAILURE); } return new Tree<>(new Node(Node.Type.RESULT, res)); diff --git a/dice-lang/src/bjc/dicelang/v2/Shunter.java b/dice-lang/src/bjc/dicelang/v2/Shunter.java index e95b642..d5f9135 100644 --- a/dice-lang/src/bjc/dicelang/v2/Shunter.java +++ b/dice-lang/src/bjc/dicelang/v2/Shunter.java @@ -43,26 +43,29 @@ public class Shunter { unaryAdjectives.add(COERCE); - ops.put(ADD, 0 + MATH_PREC); - ops.put(SUBTRACT, 0 + MATH_PREC); + ops.put(ADD, 0 + MATH_PREC); + ops.put(SUBTRACT, 0 + MATH_PREC); - ops.put(MULTIPLY, 1 + MATH_PREC); - ops.put(IDIVIDE, 1 + MATH_PREC); - ops.put(DIVIDE, 1 + MATH_PREC); + ops.put(MULTIPLY, 1 + MATH_PREC); + ops.put(IDIVIDE, 1 + MATH_PREC); + ops.put(DIVIDE, 1 + MATH_PREC); ops.put(DICEGROUP, 0 + DICE_PREC); + ops.put(DICECONCAT, 1 + DICE_PREC); + ops.put(DICELIST, 2 + DICE_PREC); - ops.put(LET, 0 + EXPR_PREC); - ops.put(BIND, 1 + EXPR_PREC); + ops.put(LET, 0 + EXPR_PREC); + ops.put(BIND, 1 + EXPR_PREC); } private boolean isUnary(Token ty) { - switch(ty.type) { - default: - return false; - } + if(unaryAdjectives.contains(ty)) return true; + if(unaryAdverbs.contains(ty)) return true; + if(unaryGerunds.contains(ty)) return true; + + return false; } private boolean isOp(Token tk) { @@ -77,111 +80,119 @@ public class Shunter { return false; } - public boolean shuntTokens(IList<Token> tks, IList<Token> returned) { - Deque<Token> opStack = new LinkedList<>(); + private boolean shuntToken(Token tk, Deque<Token> opStack, + Deque<Token> unaryStack, Deque<Token> currReturned, + Deque<Token> feed) { + if(unaryStack.size() != 0) { + if(isUnary(tk)) { + unaryStack.add(tk); + return true; + } - boolean unaryMode = false; - Deque<Token> unaryOps = new LinkedList<>(); + Token unaryOp = unaryStack.pop(); - Deque<Token> currReturned = new LinkedList<>(); + Token.Type unaryType = unaryOp.type; - for(Token tk : tks.toIterable()) { - if(unaryMode) { - if(isUnary(tk)) { - unaryOps.add(tk); - continue; + if(unaryAdjectives.contains(unaryType)) { + if(isOp(tk)) { + Errors.inst.printError(EK_SHUNT_NOTADV, unaryOp.toString(), tk.toString()); + return false; } - Token unaryOp = unaryOps.pop(); - - Token.Type unaryType = unaryOp.type; - - if(unaryAdjectives.contains(unaryType)) { - if(isOp(tk)) { - Errors.inst.printError(EK_SHUNT_NOTADV, unaryOp.toString(), tk.toString()); - return false; - } - - Token newTok = new Token(TAGOPR); - - if(tk.type == TAGOP) { - newTok.tokenValues = tk.tokenValues; - } else { - newTok.tokenValues = new FunctionalList<>(); - } - - newTok.tokenValues.add(unaryOp); - opStack.push(newTok); - } else if(unaryAdverbs.contains(unaryType)) { - // @TODO finish implementing unary operators - // this will require adding a 'backfeed' to the shunter to catch - // tokens we missed while parsing unary operators + Token newTok = new Token(TAGOPR); + + if(tk.type == TAGOP) { + newTok.tokenValues = tk.tokenValues; + } else { + newTok.tokenValues = new FunctionalList<>(); } - } - if(isUnary(tk)) { - unaryMode = true; + newTok.tokenValues.add(unaryOp); + opStack.push(newTok); - unaryOps.add(tk); - continue; - } else if(isOp(tk)) { - while(!opStack.isEmpty() && isHigherPrec(tk, opStack.peek())) { - currReturned.addLast(opStack.pop()); - } + return true; + } else if(unaryAdverbs.contains(unaryType)) { + + } + } - opStack.push(tk); - } else if(tk.type == OPAREN || tk.type == OBRACE) { - opStack.push(tk); - - if(tk.type == OBRACE) currReturned.addLast(tk); - } else if(tk.type == CPAREN || tk.type == CBRACE) { - Token matching = null; - - switch(tk.type) { - case CPAREN: - matching = new Token(OPAREN, tk.intValue); - break; - case CBRACE: - matching = new Token(OBRACE, tk.intValue); - break; - default: - break; - } + if(isUnary(tk)) { + unaryStack.add(tk); + return true; + } else if(isOp(tk)) { + while(!opStack.isEmpty() && isHigherPrec(tk, opStack.peek())) { + currReturned.addLast(opStack.pop()); + } - if(!opStack.contains(matching)) { - Errors.inst.printError(EK_SHUNT_NOGROUP, tk.toString(), matching.toString()); - return false; - } + opStack.push(tk); + } else if(tk.type == OPAREN || tk.type == OBRACE) { + opStack.push(tk); + + if(tk.type == OBRACE) currReturned.addLast(tk); + } else if(tk.type == CPAREN || tk.type == CBRACE) { + Token matching = null; + + switch(tk.type) { + case CPAREN: + matching = new Token(OPAREN, tk.intValue); + break; + case CBRACE: + matching = new Token(OBRACE, tk.intValue); + break; + default: + break; + } - while(!opStack.peek().equals(matching)) { - currReturned.addLast(opStack.pop()); - } + if(!opStack.contains(matching)) { + Errors.inst.printError(EK_SHUNT_NOGROUP, tk.toString(), matching.toString()); + return false; + } - if(tk.type == CBRACE) { - currReturned.addLast(tk); - } + while(!opStack.peek().equals(matching)) { + currReturned.addLast(opStack.pop()); + } - opStack.pop(); - } else if(tk.type == GROUPSEP) { - IList<Token> group = new FunctionalList<>(); + if(tk.type == CBRACE) { + currReturned.addLast(tk); + } - while(currReturned.size() != 0 && !currReturned.peek().isGrouper()) { - group.add(currReturned.pop()); - } + opStack.pop(); + } else if(tk.type == GROUPSEP) { + IList<Token> group = new FunctionalList<>(); - while(opStack.size() != 0 && !opStack.peek().isGrouper()) { - group.add(opStack.pop()); - } + while(currReturned.size() != 0 && !currReturned.peek().isGrouper()) { + group.add(currReturned.pop()); + } - if(currReturned.size() == 0) { - Errors.inst.printError(EK_SHUNT_INVSEP); - return false; - } + while(opStack.size() != 0 && !opStack.peek().isGrouper()) { + group.add(opStack.pop()); + } - currReturned.addLast(new Token(TOKGROUP, group)); - } else { - currReturned.addLast(tk); + if(currReturned.size() == 0) { + Errors.inst.printError(EK_SHUNT_INVSEP); + return false; } + + currReturned.addLast(new Token(TOKGROUP, group)); + } else { + currReturned.addLast(tk); + } + + return true; + } + + public boolean shuntTokens(IList<Token> tks, IList<Token> returned) { + Deque<Token> opStack = new LinkedList<>(); + Deque<Token> unaryOps = new LinkedList<>(); + + Deque<Token> currReturned = new LinkedList<>(); + + Deque<Token> feed = new LinkedList<>(); + + for(Token tk : tks.toIterable()) { + while(feed.size() != 0) + shuntToken(feed.poll(), opStack, unaryOps, currReturned, feed); + shuntToken(tk, opStack, unaryOps, currReturned, feed); } // Flush leftover operators @@ -202,13 +213,27 @@ public class Shunter { boolean exists = ops.containsKey(right); + if(rght.type == TAGOPR) exists = true; + // If it doesn't, the left is higher precedence. if (!exists) { return false; } - int rightPrecedence = ops.get(right); - int leftPrecedence = ops.get(left); + int rightPrecedence; + int leftPrecedence; + + if(rght.type == TAGOPR) { + rightPrecedence = (int)rght.intValue; + } else { + rightPrecedence = ops.get(right); + } + + if(lft.type == TAGOPR) { + leftPrecedence = (int)lft.intValue; + } else { + leftPrecedence = ops.get(left); + } return rightPrecedence >= leftPrecedence; } |
