diff options
| author | bculkin2442 <bjculkin@mix.wvu.edu> | 2017-02-26 08:22:59 -0500 |
|---|---|---|
| committer | bculkin2442 <bjculkin@mix.wvu.edu> | 2017-02-27 03:55:15 -0500 |
| commit | b776b8a558a2e27e4551768050f7e34e233326b5 (patch) | |
| tree | 6f574992a406d65ec3fac24a3b0bc498e9d9243a | |
| parent | 07d8b9547a654021d8c56021779f4cdaa5f03f1b (diff) | |
Dice math
| -rw-r--r-- | dice-lang/pom.xml | 20 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/Define.java | 16 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/DiceBox.java | 176 | ||||
| -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 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/StreamEngine.java | 2 |
9 files changed, 475 insertions, 228 deletions
diff --git a/dice-lang/pom.xml b/dice-lang/pom.xml index a3a3f6f..86855bf 100644 --- a/dice-lang/pom.xml +++ b/dice-lang/pom.xml @@ -19,16 +19,36 @@ <artifactId>exec-maven-plugin</artifactId> <version>1.5.0</version> <configuration> + <executable>java</executable> + <arguments> + <argument>-agentlib:jdwp=transport=dt_socket,server=y,suspend=n</argument> + + <argument>-classpath</argument> + <classpath/> + <argument>bjc.dicelang.v2.DiceLangConsole</argument> + </arguments> <mainClass>bjc.dicelang.v2.DiceLangConsole</mainClass> </configuration> </plugin> </plugins> </build> + <repositories> + <repository> + <id>jline</id> + <name>JLine Project Repository</name> + <url>http://jline.sourceforge.net/m2repo</url> + </repository> + </repositories> <dependencies> <dependency> <groupId>bjc</groupId> <artifactId>BJC-Utils2</artifactId> <version>0.1.0-SNAPSHOT</version> </dependency> + <dependency> + <groupId>jline</groupId> + <artifactId>jline</artifactId> + <version>0.9.9</version> + </dependency> </dependencies> </project> diff --git a/dice-lang/src/bjc/dicelang/v2/Define.java b/dice-lang/src/bjc/dicelang/v2/Define.java index f87bab6..9a68a98 100644 --- a/dice-lang/src/bjc/dicelang/v2/Define.java +++ b/dice-lang/src/bjc/dicelang/v2/Define.java @@ -11,13 +11,11 @@ import java.util.regex.PatternSyntaxException; import static bjc.dicelang.v2.Errors.ErrorKey.*; public class Define implements UnaryOperator<String> { - public static enum Type { - LINE, TOKEN - } + public static enum Type { LINE, TOKEN } public static final int MAX_RECURS = 10; - public final int priority; + public final int priority; public final boolean inError; private boolean doRecur; @@ -27,7 +25,7 @@ public class Define implements UnaryOperator<String> { private Pattern searcher; private Iterator<String> replacers; - private String replacer; + private String replacer; public Define(int priorty, boolean isSub, boolean recur, boolean isCircular, @@ -42,7 +40,6 @@ public class Define implements UnaryOperator<String> { } catch (PatternSyntaxException psex) { Errors.inst.printError(EK_DFN_PREDSYN, psex.getMessage()); inError = true; - return; } } @@ -52,7 +49,6 @@ public class Define implements UnaryOperator<String> { } catch (PatternSyntaxException psex) { Errors.inst.printError(EK_DFN_SRCSYN, psex.getMessage()); inError = true; - return; } @@ -93,7 +89,6 @@ public class Define implements UnaryOperator<String> { do { strang = doPass(tok); - recurCount += 1; } while(!strang.equals(oldStrang) && recurCount < MAX_RECURS); @@ -112,19 +107,16 @@ public class Define implements UnaryOperator<String> { if(subType) { StringBuffer sb = new StringBuffer(); - while(searcherMatcher.find()) { if(replacers == null) { searcherMatcher.appendReplacement(sb,""); } else { String replac = replacers.next(); - searcherMatcher.appendReplacement(sb, replac); } } - - searcherMatcher.appendTail(sb); + searcherMatcher.appendTail(sb); return sb.toString(); } else { return searcherMatcher.replaceAll(replacer); diff --git a/dice-lang/src/bjc/dicelang/v2/DiceBox.java b/dice-lang/src/bjc/dicelang/v2/DiceBox.java index 2a32f47..ae0481e 100644 --- a/dice-lang/src/bjc/dicelang/v2/DiceBox.java +++ b/dice-lang/src/bjc/dicelang/v2/DiceBox.java @@ -12,7 +12,7 @@ public class DiceBox { public interface Die { boolean canOptimize(); - long optimize(); + long optimize(); long roll(); long rollSingle(); @@ -20,7 +20,7 @@ public class DiceBox { public interface DieList { boolean canOptimize(); - long[] optimize(); + long[] optimize(); long[] roll(); } @@ -33,13 +33,11 @@ public class DiceBox { public DieExpression(Die scal) { isList = false; - scalar = scal; } public DieExpression(DieList lst) { isList = true; - list = lst; } @@ -107,12 +105,16 @@ public class DiceBox { } public boolean canOptimize() { - if(diceSize.canOptimize() && diceSize.optimize() == 1) return numDice.canOptimize(); - else return false; + if(diceSize.canOptimize() && (diceSize.optimize() <= 1) { + return numDice.canOptimize(); + } else return false; } public long optimize() { - return numDice.optimize(); + long optSize = diceSize.optimize(); + + if(optSize == 0) return 0; + else return numDice.optimize(); } public long roll() { @@ -145,7 +147,7 @@ public class DiceBox { } public boolean canOptimize() { - return false; + return numDice.canOptimize() && numDice.optimize() == 0; } public long optimize() { @@ -153,9 +155,9 @@ public class DiceBox { } public long roll() { - long nDice = numDice.roll(); - long res = 0; + + long nDice = numDice.roll(); for(int i = 0; i < nDice; i++) { res += rollSingle(); @@ -165,16 +167,7 @@ public class DiceBox { } public long rollSingle() { - switch(rng.nextInt(3)) { - case 0: - return -1; - case 1: - return 0; - case 2: - return 1; - default: - return 0; - } + return rng.nextInt(3) - 1; } public String toString() { @@ -214,14 +207,14 @@ public class DiceBox { public static class CompoundingDie implements Die { private Die source; - + private Predicate<Long> compoundOn; private String compoundPattern; public CompoundingDie(Die src, Predicate<Long> compound) { this(src, compound, null); } - + public CompoundingDie(Die src, Predicate<Long> compound, String patt) { source = src; @@ -230,7 +223,7 @@ public class DiceBox { } public boolean canOptimize() { - return false; + return source.canOptimize() && source.optimize() == 0; } public long optimize() { @@ -239,7 +232,6 @@ public class DiceBox { public long roll() { long res = source.roll(); - long oldRes = res; while(compoundOn.test(oldRes)) { @@ -253,7 +245,6 @@ public class DiceBox { public long rollSingle() { long res = source.rollSingle(); - long oldRes = res; while(compoundOn.test(oldRes)) { @@ -274,6 +265,79 @@ 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(); + } + + private long performOp(long lft, long rght) { + switch(type) { + case ADD: + return lft + rght; + case SUBTRACT: + return lft - rght; + case MULTIPLY: + return lft * rght; + default: + return 0; + } + } + + public long optimize() { + long lft = left.optimize(); + long rght = right.optimize(); + + return performOp(lft, rght); + } + + public long roll() { + long lft = left.roll(); + long rght = right.roll(); + + return performOp(lft, rght); + } + + public long rollSingle() { + long lft = left.rollSingle(); + long rght = right.rollSingle(); + + return performOp(lft, rght); + } + + public String toString() { + return left.toString() + " " + type.toString() + " " + right.toString(); + } + } + public static class SimpleDieList implements DieList { private Die numDice; private Die size; @@ -284,7 +348,7 @@ public class DiceBox { } public boolean canOptimize() { - if(size.canOptimize() && size.optimize() == 1) { + if(size.canOptimize() && size.optimize() <= 1) { return numDice.canOptimize(); } else { return false; @@ -292,12 +356,13 @@ public class DiceBox { } public long[] optimize() { - long[] ret = new long[(int)numDice.optimize()]; + int sze = (int)numDice.optimize(); + long res = size.optimize(); - long optSize = size.optimize(); + long[] ret = new long[sze]; - for(int i = 0; i < optSize; i++) { - ret[i] = 1; + for(int i = 0; i < sze; i++) { + ret[i] = res; } return ret; @@ -335,7 +400,8 @@ public class DiceBox { this(src, explode, null, penetrate); } - public ExplodingDice(Die src, Predicate<Long> explode, String patt, boolean penetrate) { + public ExplodingDice(Die src, Predicate<Long> explode, String patt, + boolean penetrate) { source = src; explodeOn = explode; explodePattern = patt; @@ -352,21 +418,18 @@ public class DiceBox { public long[] roll() { long res = source.roll(); + long oldRes = res; List<Long> resList = new LinkedList<>(); - long oldRes = res; - while(explodeOn.test(oldRes)) { oldRes = source.rollSingle(); if(explodePenetrates) oldRes -= 1; - resList.add(oldRes); } - long[] newRes = new long[resList.size()+1]; - + long[] newRes = new long[resList.size() + 1]; newRes[0] = res; int i = 1; @@ -391,14 +454,19 @@ public class DiceBox { if(!isValidExpression(exp)) return null; if(scalarDiePattern.matcher(exp).matches()) { - return new DieExpression(new ScalarDie(Long.parseLong(exp.substring(0, exp.indexOf('s'))))); + Die scal = new ScalarDie(Long.parseLong(exp.substring(0, exp.indexOf('s')))) + + return new DieExpression(scal); } else if(simpleDiePattern.matcher(exp).matches()) { String[] dieParts = exp.split("d"); + long right = Long.parseLong(dieParts[1]); if(dieParts[0].equals("")) { - return new DieExpression(new SimpleDie(1, Long.parseLong(dieParts[1]))); + Die scal = new SimpleDie(1, right) + return new DieExpression(scal); } else { - return new DieExpression(new SimpleDie(Long.parseLong(dieParts[0]), Long.parseLong(dieParts[1]))); + Die scal = new SimpleDie(Long.parseLong(dieParts[0]), right); + return new DieExpression(scal); } } else if(fudgeDiePattern.matcher(exp).matches()) { String nDice = exp.substring(0, exp.indexOf('d')); @@ -417,28 +485,32 @@ public class DiceBox { DieExpression left = parseExpression(dieParts[0]); Predicate<Long> right = deriveCond(dieParts[1]); - return new DieExpression(new CompoundingDie(left.scalar, right, dieParts[1])); + Die scal = new CompoundingDie(left.scalar, right, dieParts[1]) + return new DieExpression(scal); } else if(explodingDiePattern.matcher(exp).matches()) { String[] dieParts = exp.split("!"); DieExpression left = parseExpression(dieParts[0]); Predicate<Long> right = deriveCond(dieParts[1]); - return new DieExpression(new ExplodingDice(left.scalar, right, dieParts[1], false)); + DieList lst = new ExplodingDice(left.scalar, right, dieParts[1], false); + return new DieExpression(lst); } else if(penetratingDiePattern.matcher(exp).matches()) { String[] dieParts = exp.split("p!"); DieExpression left = parseExpression(dieParts[0]); Predicate<Long> right = deriveCond(dieParts[1]); - return new DieExpression(new ExplodingDice(left.scalar, right, dieParts[1], true)); + DieList lst = new ExplodingDice(left.scalar, right, dieParts[1], true); + return new DieExpression(lst); } else if(diceListPattern.matcher(exp).matches()) { String[] dieParts = exp.split("dl"); DieExpression left = parseExpression(dieParts[0]); DieExpression right = parseExpression(dieParts[1]); - - return new DieExpression(new SimpleDieList(left.scalar, right.scalar)); + + DieList lst = new SimpleDieList(left.scalar, right.scalar); + return new DieExpression(); } // @TODO give a specific error message @@ -500,14 +572,14 @@ public class DiceBox { long num = Long.parseLong(patt.substring(1)); switch(patt.charAt(0)) { - case '<': - return (roll) -> roll < num; - case '=': - return (roll) -> roll == num; - case '>': - return (roll) -> roll > num; - default: - return (roll) -> false; + case '<': + return (roll) -> roll < num; + case '=': + return (roll) -> roll == num; + case '>': + return (roll) -> roll > num; + default: + return (roll) -> false; } } } 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; } diff --git a/dice-lang/src/bjc/dicelang/v2/StreamEngine.java b/dice-lang/src/bjc/dicelang/v2/StreamEngine.java index 383c7f3..41f01a7 100644 --- a/dice-lang/src/bjc/dicelang/v2/StreamEngine.java +++ b/dice-lang/src/bjc/dicelang/v2/StreamEngine.java @@ -41,7 +41,7 @@ public class StreamEngine { } else { if(tk.equals("{@SU}")) { quoteMode = false; - } else if(tk.matches("\\\\+{@SU}")) { + } else if(tk.startsWith("\\") && tk.endsWith("{@SU}")) { currStream.add(tk.substring(1)); } else { currStream.add(tk); |
