diff options
Diffstat (limited to 'dice-lang/src')
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/Define.java | 48 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/DiceBox.java | 326 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/DiceLangConsole.java | 53 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java | 39 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/Errors.java | 232 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/Evaluator.java | 157 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/Parser.java | 14 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/Shunter.java | 23 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/StreamEngine.java | 40 |
9 files changed, 813 insertions, 119 deletions
diff --git a/dice-lang/src/bjc/dicelang/v2/Define.java b/dice-lang/src/bjc/dicelang/v2/Define.java index 2ecbc69..3ee2f37 100644 --- a/dice-lang/src/bjc/dicelang/v2/Define.java +++ b/dice-lang/src/bjc/dicelang/v2/Define.java @@ -4,15 +4,21 @@ import bjc.utils.data.CircularIterator; import java.util.Iterator; import java.util.function.UnaryOperator; -import java.util.regex.Pattern; import java.util.regex.Matcher; +import java.util.regex.Pattern; +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 final int MAX_RECURS = 10; + public final int priority; + public final boolean inError; private boolean doRecur; private boolean subType; @@ -31,9 +37,26 @@ public class Define implements UnaryOperator<String> { subType = isSub; if(predicte != null) { - predicate = Pattern.compile(predicte); + try { + predicate = Pattern.compile(predicte); + } catch (PatternSyntaxException psex) { + Errors.inst.printError(EK_DFN_PREDSYN, psex.getMessage()); + inError = true; + + return; + } + } + + try { + searcher = Pattern.compile(searchr); + } catch (PatternSyntaxException psex) { + Errors.inst.printError(EK_DFN_SRCSYN, psex.getMessage()); + inError = true; + + return; } - searcher = Pattern.compile(searchr); + + inError = false; if(subType) { if(replacrs.iterator().hasNext()) { @@ -50,6 +73,8 @@ public class Define implements UnaryOperator<String> { } public String apply(String tok) { + if(inError) return tok; + if(predicate != null) { if(!predicate.matcher(tok).matches()) { return tok; @@ -59,6 +84,8 @@ public class Define implements UnaryOperator<String> { String strang = doPass(tok); if(doRecur) { + int recurCount = 0; + if(strang.equals(tok)) { return strang; } else { @@ -66,7 +93,14 @@ public class Define implements UnaryOperator<String> { do { strang = doPass(tok); - } while(!strang.equals(oldStrang)); + + recurCount += 1; + } while(!strang.equals(oldStrang) && recurCount < MAX_RECURS); + + if(recurCount >= MAX_RECURS) { + Errors.inst.printError(EK_DFN_RECUR, Integer.toString(MAX_RECURS), tok, strang); + return strang; + } } } @@ -81,16 +115,10 @@ public class Define implements UnaryOperator<String> { while(searcherMatcher.find()) { if(replacers == null) { - System.out.println("\t\tTRACE: running null replacer on substring " - + tok.substring(searcherMatcher.start(), searcherMatcher.end())); - searcherMatcher.appendReplacement(sb,""); } else { String replac = replacers.next(); - System.out.println("\t\tTRACE: running replacer " + replac + " on substring " - + tok.substring(searcherMatcher.start(), searcherMatcher.end())); - searcherMatcher.appendReplacement(sb, replacers.next()); } } diff --git a/dice-lang/src/bjc/dicelang/v2/DiceBox.java b/dice-lang/src/bjc/dicelang/v2/DiceBox.java index 375e256..342ea2e 100644 --- a/dice-lang/src/bjc/dicelang/v2/DiceBox.java +++ b/dice-lang/src/bjc/dicelang/v2/DiceBox.java @@ -1,9 +1,14 @@ package bjc.dicelang.v2; import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; import java.util.Random; +import java.util.function.Predicate; import java.util.regex.Pattern; +import static bjc.dicelang.v2.Errors.ErrorKey.*; + public class DiceBox { private static final Random rng = new Random(); @@ -12,6 +17,7 @@ public class DiceBox { long optimize(); long roll(); + long rollSingle(); } public interface DieList { @@ -50,7 +56,7 @@ public class DiceBox { } } - private static class ScalarDie implements Die { + public static class ScalarDie implements Die { private long val; public ScalarDie(long vl) { @@ -69,45 +75,116 @@ public class DiceBox { return val; } + public long rollSingle() { + return val; + } + public String toString() { return Long.toString(val); } } - private static class SimpleDie implements Die { - private long numDice; - private long diceSize; + public static class SimpleDie implements Die { + private Die numDice; + private Die diceSize; public SimpleDie(long nDice, long size) { - numDice = nDice; + numDice = new ScalarDie(nDice); + diceSize = new ScalarDie(size); + } + + public SimpleDie(Die nDice, long size) { + numDice = nDice; + diceSize = new ScalarDie(size); + } + + public SimpleDie(long nDice, Die size) { + numDice = new ScalarDie(nDice); + diceSize = size; + } + + public SimpleDie(Die nDice, Die size) { + numDice = nDice; diceSize = size; } public boolean canOptimize() { - if(diceSize == 1) return true; + if(diceSize.canOptimize() && diceSize.optimize() == 1) return numDice.canOptimize(); else return false; } public long optimize() { - return numDice; + return numDice.optimize(); } public long roll() { long total = 0; - for(int i = 0; i < numDice; i++) { - total += (rng.nextLong() % numDice) + 1; + long nDice = numDice.roll(); + long dSize = diceSize.roll(); + + for(int i = 0; i < nDice; i++) { + total += (Math.abs(rng.nextLong()) % dSize) + 1; } return total; } + public long rollSingle() { + return (Math.abs(rng.nextLong()) % diceSize.roll()) + 1; + } + public String toString() { return numDice + "d" + diceSize; } } - private static class CompoundDie implements Die { + public static class FudgeDie implements Die { + private Die numDice; + + public FudgeDie(long nDice) { + numDice = new ScalarDie(nDice); + } + + public boolean canOptimize() { + return false; + } + + public long optimize() { + return 0; + } + + public long roll() { + long nDice = numDice.roll(); + + long res = 0; + + for(int i = 0; i < nDice; i++) { + res += rollSingle(); + } + + return res; + } + + public long rollSingle() { + switch(rng.nextInt(3)) { + case 0: + return -1; + case 1: + return 0; + case 2: + return 1; + default: + return 0; + } + } + + public String toString() { + return numDice + "dF"; + } + } + + public static class CompoundDie implements Die { private Die left; private Die right; @@ -128,12 +205,78 @@ public class DiceBox { return Long.parseLong(left.roll() + "" + right.roll()); } + public long rollSingle() { + return roll(); + } + public String toString() { return left.toString() + "c" + right.toString(); } } - private static class SimpleDieList implements DieList { + 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; + + compoundOn = compound; + compoundPattern = patt; + } + + public boolean canOptimize() { + return false; + } + + public long optimize() { + return 0; + } + + public long roll() { + long res = source.roll(); + + long oldRes = res; + + while(compoundOn.test(oldRes)) { + oldRes = source.rollSingle(); + + res += oldRes; + } + + return res; + } + + public long rollSingle() { + long res = source.rollSingle(); + + long oldRes = res; + + while(compoundOn.test(oldRes)) { + oldRes = source.rollSingle(); + + res += oldRes; + } + + return res; + } + + public String toString() { + if(compoundPattern == null) { + return source + "!!"; + } else { + return source + "!!" + compoundPattern; + } + } + } + + public static class SimpleDieList implements DieList { private Die numDice; private Die size; @@ -179,11 +322,78 @@ public class DiceBox { } } + public static class ExplodingDice implements DieList { + private Die source; + + private Predicate<Long> explodeOn; + private String explodePattern; + private boolean explodePenetrates; + + public ExplodingDice(Die src, Predicate<Long> explode) { + this(src, explode, null, false); + } + + public ExplodingDice(Die src, Predicate<Long> explode, boolean penetrate) { + this(src, explode, null, penetrate); + } + + public ExplodingDice(Die src, Predicate<Long> explode, String patt, boolean penetrate) { + source = src; + explodeOn = explode; + explodePattern = patt; + explodePenetrates = penetrate; + } + + public boolean canOptimize() { + return false; + } + + public long[] optimize() { + return new long[0]; + } + + public long[] roll() { + long res = source.roll(); + + 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]; + + newRes[0] = res; + + int i = 1; + for(long rll : resList) { + newRes[i] = rll; + i += 1; + } + + return newRes; + } + + public String toString() { + if(explodePattern == null) { + return source + (explodePenetrates ? "p" : "") + "!"; + } else { + return source + (explodePenetrates ? "p" : "") + "!" + explodePattern; + } + } + } + public static DieExpression parseExpression(String exp) { if(!isValidExpression(exp)) return null; if(scalarDiePattern.matcher(exp).matches()) { - return new DieExpression(new ScalarDie(Long.parseLong(exp))); + return new DieExpression(new ScalarDie(Long.parseLong(exp.substring(0, exp.indexOf('s'))))); } else if(simpleDiePattern.matcher(exp).matches()) { String[] dieParts = exp.split("d"); @@ -192,28 +402,44 @@ public class DiceBox { } else { return new DieExpression(new SimpleDie(Long.parseLong(dieParts[0]), Long.parseLong(dieParts[1]))); } + } else if(fudgeDiePattern.matcher(exp).matches()) { + String nDice = exp.substring(0, exp.indexOf('d')); + + return new DieExpression(new FudgeDie(Long.parseLong(nDice))); } else if(compoundDiePattern.matcher(exp).matches()) { String[] dieParts = exp.split("c"); DieExpression left = parseExpression(dieParts[0]); DieExpression right = parseExpression(dieParts[1]); - if(left.isList || right.isList) { - // @TODO give a specific error message - return null; - } - return new DieExpression(new CompoundDie(left.scalar, right.scalar)); + } else if(compoundingDiePattern.matcher(exp).matches()) { + String[] dieParts = exp.split("!!"); + + DieExpression left = parseExpression(dieParts[0]); + Predicate<Long> right = deriveCond(dieParts[1]); + + return new DieExpression(new CompoundingDie(left.scalar, right, dieParts[1])); + } 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)); + } 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)); } else if(diceListPattern.matcher(exp).matches()) { String[] dieParts = exp.split("dl"); DieExpression left = parseExpression(dieParts[0]); DieExpression right = parseExpression(dieParts[1]); - if(left.isList || right.isList) { - return null; - } - return new DieExpression(new SimpleDieList(left.scalar, right.scalar)); } @@ -221,27 +447,50 @@ public class DiceBox { return null; } - private static final String scalarDie = "[\\+\\-]?\\d+"; + private static final String comparePoint = "[<>=]\\d+"; + + private static final String scalarDie = "[\\+\\-]?\\d+sd"; private static final Pattern scalarDiePattern = Pattern.compile("\\A" + scalarDie + "\\Z"); - private static final String simpleDie = "(?:\\d+)?d\\d+"; - private static final Pattern simpleDiePattern = Pattern.compile("\\A" + simpleDie + "\\Z"); + private static final String simpleDie = "(?:\\d+)?d\\d+"; + private static final Pattern simpleDiePattern = Pattern.compile("\\A" + simpleDie + "\\Z"); + + private static final String fudgeDie = "(?:\\d+)?dF"; + private static final Pattern fudgeDiePattern = Pattern.compile("\\A" + fudgeDie + "\\Z"); + + private static final String compoundDie = simpleDie + "c(?:(?:" + simpleDie + ")|(?:\\d+))"; + private static final Pattern compoundDiePattern = Pattern.compile("\\A" + compoundDie + "\\Z"); + + private static final String compoundGroup = "(?:(?:\\d+)|(?:" + simpleDie + ")|(?:" + + compoundDie + ")|(?:" + fudgeDie +"))"; + + private static final String compoundingDie = compoundGroup + "!!" + comparePoint; + private static final Pattern compoundingDiePattern = Pattern.compile("\\A" + compoundingDie + "\\Z"); + + private static final String explodingDie = compoundGroup + "!" + comparePoint; + private static final Pattern explodingDiePattern = Pattern.compile("\\A" + explodingDie + "\\Z"); - private static final String compoundDie = simpleDie + "c(?:(?:" + simpleDie + ")|(?:\\d+))"; - private static final Pattern compoundDiePattern = Pattern.compile("\\A" + compoundDie + "\\Z"); + private static final String penetratingDie = compoundGroup + "!" + comparePoint; + private static final Pattern penetratingDiePattern = Pattern.compile("\\A" + penetratingDie + "\\Z"); - private static final String compoundGroup = "(?:(?:" + scalarDie + ")|(?:" + simpleDie + ")|(?:" - + compoundDie + "))"; + private static final String diceList = compoundGroup + "dl" + compoundGroup; + private static final Pattern diceListPattern = Pattern.compile("\\A" + diceList + "\\Z"); - private static final String diceList = compoundGroup + "dl" + compoundGroup; - private static final Pattern diceListPattern = Pattern.compile("\\A" + diceList + "\\Z"); public static boolean isValidExpression(String exp) { if(scalarDiePattern.matcher(exp).matches()) { return true; } else if(simpleDiePattern.matcher(exp).matches()) { return true; - } else if (compoundDiePattern.matcher(exp).matches()) { + } else if(fudgeDiePattern.matcher(exp).matches()) { + return true; + } else if(compoundDiePattern.matcher(exp).matches()) { + return true; + } else if(compoundingDiePattern.matcher(exp).matches()) { + return true; + } else if(explodingDiePattern.matcher(exp).matches()) { + return true; + } else if(penetratingDiePattern.matcher(exp).matches()) { return true; } else if (diceListPattern.matcher(exp).matches()) { return true; @@ -249,4 +498,19 @@ public class DiceBox { return false; } } + + private static Predicate<Long> deriveCond(String patt) { + 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; + } + } } diff --git a/dice-lang/src/bjc/dicelang/v2/DiceLangConsole.java b/dice-lang/src/bjc/dicelang/v2/DiceLangConsole.java index 5731877..863bce5 100644 --- a/dice-lang/src/bjc/dicelang/v2/DiceLangConsole.java +++ b/dice-lang/src/bjc/dicelang/v2/DiceLangConsole.java @@ -6,6 +6,8 @@ import java.util.Scanner; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static bjc.dicelang.v2.Errors.ErrorKey.*; + public class DiceLangConsole { private int commandNumber; @@ -13,7 +15,6 @@ public class DiceLangConsole { public DiceLangConsole(String[] args) { // @TODO do something with the args - commandNumber = 0; eng = new DiceLangEngine(); @@ -31,19 +32,15 @@ public class DiceLangConsole { if(comm.startsWith("pragma")) { boolean success = handlePragma(comm.substring(7)); - if(success) - System.out.println("Pragma completed succesfully"); - else - System.out.println("Pragma execution failed"); + if(success) System.out.println("Pragma completed succesfully"); + else System.out.println("Pragma execution failed"); } else { System.out.printf("\tRaw command: %s\n", comm); boolean success = eng.runCommand(comm); - if(success) - System.out.println("Command completed succesfully"); - else - System.out.println("Command execution failed"); + if(success) System.out.println("Command completed succesfully"); + else System.out.println("Command execution failed"); commandNumber += 1; } @@ -81,7 +78,7 @@ public class DiceLangConsole { case "help": return helpMode(pragma.substring(5)); default: - System.out.println("\tERROR: Unknown pragma: " + pragma); + Errors.inst.printError(EK_CONS_INVPRAG, pragma); return false; } @@ -90,7 +87,20 @@ public class DiceLangConsole { private boolean helpMode(String pragma) { switch(pragma.trim()) { + case "help": + System.out.println("\tGet help on pragmas"); + break; + case "debug": + System.out.println("\tToggle debug mode. (Output stage results)"); + break; + case "postfix": + System.out.println("\tToggle postfix mode. (Don't shunt tokens)"); + break; + case "prefix": + System.out.println("\tToggle prefix mode. (Reverse token order instead of shunting)"); + break; case "define": + System.out.println("\tAdd a macro rewrite directive."); System.out.println("\tdefine <priority> <type> <recursion> <guard> <circular> <patterns>..."); break; default: @@ -124,22 +134,22 @@ public class DiceLangConsole { int sixthIndex = defineText.indexOf(' ', fifthIndex + 1); if(firstIndex == -1) { - System.out.println("\tERROR: Improperly formatted define (no priority)"); + Errors.inst.printError(EK_CONS_INVDEFINE, "(no priority)"); return false; } else if(secondIndex == -1) { - System.out.println("\tERROR: Improperly formatted define (no define type)"); + Errors.inst.printError(EK_CONS_INVDEFINE, "(no define type)"); return false; } else if(thirdIndex == -1) { - System.out.println("\tERROR: Improperly formatted define (no recursion type)"); + Errors.inst.printError(EK_CONS_INVDEFINE, "(no recursion type)"); return false; } else if(fourthIndex == -1) { - System.out.println("\tERROR: Improperly formatted define (no guard type)"); + Errors.inst.printError(EK_CONS_INVDEFINE, "(no guard type)"); return false; } else if(fifthIndex == -1) { - System.out.println("\tERROR: Improperly formatted define (no circularity)"); + Errors.inst.printError(EK_CONS_INVDEFINE, "(no circularity)"); return false; } else if(sixthIndex == -1) { - System.out.println("\tERROR: Improperly formatted define (no patterns)"); + Errors.inst.printError(EK_CONS_INVDEFINE, "(no patterns)"); return false; } @@ -166,8 +176,7 @@ public class DiceLangConsole { subMode = true; break; default: - System.out.println("\tERROR: Unknown define type " - + defineType); + Errors.inst.printError(EK_CONS_INVDEFINE, "(unknown type)"); return false; } @@ -185,14 +194,16 @@ public class DiceLangConsole { if(hasGuard) { if(!patMatcher.find()) { - System.out.println("\tERROR: Improperly formatted define (no guard pattern)"); + Errors.inst.printError(EK_CONS_INVDEFINE, "(no guard pattern)"); + return false; } guardPattern = patMatcher.group(1); } if(!patMatcher.find()) { - System.out.println("\tERROR: Improperly formatted define (no search pattern)"); + Errors.inst.printError(EK_CONS_INVDEFINE, "(no search pattern)"); + return false; } String searchPattern = patMatcher.group(1); @@ -205,6 +216,8 @@ public class DiceLangConsole { Define dfn = new Define(priority, subMode, doRecur, isCircular, guardPattern, searchPattern, replacePatterns); + if(dfn.inError) return false; + if(type == Define.Type.LINE) { eng.addLineDefine(dfn); } else { diff --git a/dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java b/dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java index 5fbfb3a..07b6b10 100644 --- a/dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java +++ b/dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java @@ -20,6 +20,7 @@ import java.util.LinkedList; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static bjc.dicelang.v2.Errors.ErrorKey.*; import static bjc.dicelang.v2.Token.Type.*; public class DiceLangEngine { @@ -46,8 +47,8 @@ public class DiceLangEngine { private Evaluator eval; // Tables for symbols - private IMap<Integer, String> symTable; - private IMap<Integer, String> stringLits; + public final IMap<Integer, String> symTable; + public final IMap<Integer, String> stringLits; // Literal tokens for tokenization private IMap<String, Token.Type> litTokens; @@ -302,7 +303,7 @@ public class DiceLangEngine { curBracedTokens = new FunctionalList<>(); } else if(tk.type == Token.Type.CBRACE && tk.intValue == 2) { if(curBraceCount == 0) { - System.out.println("\tERROR: Encountered closing brace without matching open brace"); + Errors.inst.printError(EK_ENG_NOOPENING); return false; } @@ -333,6 +334,11 @@ public class DiceLangEngine { } } + if(curBraceCount > 0) { + Errors.inst.printError(EK_ENG_NOCLOSING); + return false; + } + if(debugMode && !postfixMode) System.out.printf("\tCommand after pre-shunter removal: %s\n", preparedTokens.toString()); @@ -388,7 +394,13 @@ public class DiceLangEngine { for(ITree<Node> ast : astForest) { System.out.println("\t\tTree " + treeNo + " in forest:\n\t\t " + ast); - System.out.println("\t\tEvaluates to " + eval.evaluate(ast)); + Evaluator.Result res = eval.evaluate(ast); + System.out.printf("\t\tEvaluates to %s", res); + + if(res.type == Evaluator.Result.Type.DICE) { + System.out.println(" (sample roll " + res.diceVal.value() + ")"); + } + treeNo += 1; } } @@ -445,7 +457,7 @@ public class DiceLangEngine { tk = new Token(CBRACE, token.length()); break; default: - System.out.println("\tERROR: Unrecognized grouping token " + token); + Errors.inst.printError(EK_TOK_UNGROUP, token); break; } } @@ -468,8 +480,21 @@ public class DiceLangEngine { tk = new Token(INT_LIT, Long.parseLong(newToken.substring(2).toUpperCase(), 16)); } else if(flexadecimalMatcher.matcher(token).matches()) { - tk = new Token(INT_LIT, Long.parseLong(token.substring(0, token.lastIndexOf('B')), - Integer.parseInt(token.substring(token.lastIndexOf('B') + 1)))); + int parseBase = Integer.parseInt(token.substring(token.lastIndexOf('B') + 1)); + + if(parseBase < Character.MIN_RADIX || parseBase > Character.MAX_RADIX) { + Errors.inst.printError(EK_TOK_INVBASE, Integer.toString(parseBase)); + return Token.NIL_TOKEN; + } + + String flexNum = token.substring(0, token.lastIndexOf('B')); + + try { + tk = new Token(INT_LIT, Long.parseLong(flexNum, parseBase)); + } catch (NumberFormatException nfex) { + Errors.inst.printError(EK_TOK_INVFLEX, flexNum, Integer.toString(parseBase)); + return Token.NIL_TOKEN; + } } else if(DoubleMatcher.floatingLiteral.matcher(token).matches()) { tk = new Token(FLOAT_LIT, Double.parseDouble(token)); } else if(DiceBox.isValidExpression(token)) { diff --git a/dice-lang/src/bjc/dicelang/v2/Errors.java b/dice-lang/src/bjc/dicelang/v2/Errors.java new file mode 100644 index 0000000..c358214 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/v2/Errors.java @@ -0,0 +1,232 @@ +package bjc.dicelang.v2; + +import static bjc.dicelang.v2.Errors.ErrorKey.*; + +public class Errors { + public static enum ErrorKey { + // Define Errors + // Incorrect define guard syntax + EK_DFN_PREDSYN, + // Incorrect define search syntax + EK_DFN_SRCSYN, + // Recursive define recursed too many times + EK_DFN_RECUR, + + // Console Errors + // Unknown console pragma + EK_CONS_INVPRAG, + // Improperly formatted define + EK_CONS_INVDEFINE, + + // Language Engine Errors + // Found closing doublebrace w/out opening doublebrace + EK_ENG_NOOPENING, + // Reached end of command w/out balanced doublebraces + EK_ENG_NOCLOSING, + + // Tokenizer Errors + // Found an unexpected grouping token + EK_TOK_UNGROUP, + // Invalid base for a flexadecimal number + EK_TOK_INVBASE, + // Invalid flexadecimal number in a given base + EK_TOK_INVFLEX, + + // Evaluator Errors + // Unknown node type + EK_EVAL_INVNODE, + // Incorrect # of args to binary operator + EK_EVAL_INVBIN, + // Unknown binary operator + EK_EVAL_UNBIN, + // Math on strings doesn't work + EK_EVAL_STRINGMATH, + // Attempted divide by zero + EK_EVAL_DIVZERO, + // Unknown math operator + EK_EVAL_UNMATH, + // Unknown token reference + EK_EVAL_UNTOK, + // Unknown dice operator + EK_EVAL_UNDICE, + // Incorrect type to dice group operator + EK_EVAL_INVDGROUP, + // Incorrect type to other dice operator + EK_EVAL_INVDICE, + + // Parser Error + // Group closing where there couldn't be an opener + EK_PARSE_NOCLOSE, + // Group closing without group opener + EK_PARSE_UNCLOSE, + // Incorrect # of arguments to binary operator + EK_PARSE_BINARY, + // Not enough operands to binary operator + EK_PARSE_UNOPERAND, + // Unrecognized token type + EK_PARSE_INVTOKEN, + + // Shunter Error + // Unary operator expected a operand, but got an operator + EK_SHUNT_NOTADV, + // Unary operator expected an operator, but got an operand + EK_SHUNT_NOTADJ, + // Unary operator expected an operator, but didn't find one + EK_SHUNT_NOOP, + // Asked for opening grouping operator, but couldn't find one + EK_SHUNT_NOGROUP, + // No group for group seperator to attach to + EK_SHUNT_INVSEP, + + // Stream Errors + // Attempted to switch to a non-existant stream + EK_STRM_NONEX, + // Can't delete the last stream + EK_STRM_LAST, + // Unknown stream command + EK_STRM_INVCOM, + + } + + public static enum ErrorMode { + WIZARD, DEV + } + + private ErrorMode mode; + + public void printError(ErrorKey key, String... args) { + switch(mode) { + case WIZARD: + System.out.println("\t? " + key.ordinal()); + break; + case DEV: + devError(key, args); + break; + default: + System.out.println("\tERROR ERROR: Unknown error mode " + mode); + } + } + + private void devError(ErrorKey key, String[] args) { + switch(key) { + case EK_DFN_PREDSYN: + System.out.printf("\tERROR: Incorrect define guard syntax %s\n", args[0]); + break; + case EK_DFN_SRCSYN: + System.out.printf("\tERROR: Incorrect define match syntax %s\n", args[0]); + break; + case EK_DFN_RECUR: + System.out.printf("\tERROR: Recursive define didn't converge after %s iterations." + + " Original string was %s, last iteration was %s\n", + args[0], args[1], args[2]); + break; + case EK_CONS_INVPRAG: + System.out.printf("\tERROR: Unknown pragma %s\n", args[0]); + break; + case EK_CONS_INVDEFINE: + System.out.printf("\tERROR: Improperly formatted define %s\n", args[0]); + break; + case EK_ENG_NOOPENING: + System.out.printf("\tERROR: Encountered closing doublebrace without" + + " matching opening doublebrace\n"); + break; + case EK_ENG_NOCLOSING: + System.out.printf("\tERROR: Reached end of string before closing doublebrace was found\n"); + break; + case EK_TOK_UNGROUP: + System.out.printf("\tERROR: Unrecognized grouping token %s\n", args[0]); + break; + case EK_TOK_INVBASE: + System.out.printf("\tERROR: Invalid flexadecimal base %s\n", args[0]); + break; + case EK_TOK_INVFLEX: + System.out.printf("\tERROR: Invalid flexadecimal number %s in base %s\n", args[0], args[1]); + break; + case EK_EVAL_INVNODE: + 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]); + break; + case EK_EVAL_UNBIN: + System.out.printf("\tERROR: Unknown binary operator %s\n", args[0]); + break; + case EK_EVAL_STRINGMATH: + System.out.printf("\tERROR: Math operators don't work on strings\n"); + break; + case EK_EVAL_DIVZERO: + System.out.printf("\tERROR: Attempted divide by zero\n"); + break; + case EK_EVAL_UNMATH: + System.out.printf("\tERROR: Unknown math binary operator: %s\n", args[0]); + break; + case EK_EVAL_UNTOK: + System.out.printf("\tERROR: Unknown token ref %s\n", args[0]); + break; + case EK_EVAL_UNDICE: + System.out.printf("\tERROR: Unknown dice operator %s\n", args[0]); + break; + case EK_EVAL_INVDGROUP: + System.out.printf("\tERROR: Dice group operator expects scalar dice or integers," + + " not %s\n", args[0]); + break; + case EK_EVAL_INVDICE: + System.out.printf("\tERROR: Dice operators expect scalar dice, not %s\n", args[0]); + break; + case EK_PARSE_NOCLOSE: + System.out.printf("\tERROR: Group closing with no possible group opener\n"); + break; + case EK_PARSE_UNCLOSE: + System.out.printf("\tERROR: Found group closer without opener: (closing was %s" + + ", expected %s)\n", args[0], args[1]); + break; + case EK_PARSE_BINARY: + System.out.printf("\tERROR: Expected at least two operands\n"); + break; + case EK_PARSE_UNOPERAND: + System.out.printf("\tERROR: Operator %s expected more operands than provided\n", args[0]); + break; + case EK_PARSE_INVTOKEN: + System.out.printf("\tERROR: Unrecognized token type in parsing: %s\n", args[0]); + break; + case EK_SHUNT_NOTADV: + System.out.printf("\tERROR: Unary operator %s is an adjective, not an adverb. It can't be" + + " applied to the operator %s\n", args[0], args[1]); + break; + case EK_SHUNT_NOTADJ: + System.out.printf("\tERROR: Unary operator %s is an adjective, not an adverb. It can't be" + + " applied to the operator %s\n", args[0], args[1]); + break; + case EK_SHUNT_NOOP: + System.out.printf("\tERROR: Unary operator %s is an adverb, but there is no operator" + + " to apply it to\n", args[0]); + break; + case EK_SHUNT_NOGROUP: + System.out.printf("\tERROR: Couldn't find matching grouping %s (expected %s)\n", + args[0], args[1]); + break; + case EK_SHUNT_INVSEP: + System.out.printf("\tERROR: Couldn't find grouper for group seperator to attach to\n"); + break; + case EK_STRM_NONEX: + System.out.printf("\tERROR: Attempted to switch to non-existent stream\n"); + break; + case EK_STRM_LAST: + System.out.printf("\tERROR: Cannot delete last stream\n"); + break; + case EK_STRM_INVCOM: + System.out.printf("\tERROR: Unknown stream control command %s\n", args[0]); + break; + default: + System.out.printf("\tERROR ERROR: Unknown error key %s\n", key); + } + } + + public final static Errors inst; + + static { + inst = new Errors(); + + inst.mode = ErrorMode.DEV; + } +} diff --git a/dice-lang/src/bjc/dicelang/v2/Evaluator.java b/dice-lang/src/bjc/dicelang/v2/Evaluator.java index d9efbea..ef50364 100644 --- a/dice-lang/src/bjc/dicelang/v2/Evaluator.java +++ b/dice-lang/src/bjc/dicelang/v2/Evaluator.java @@ -4,11 +4,14 @@ import bjc.utils.data.ITree; import bjc.utils.data.Tree; import bjc.utils.data.TopDownTransformResult; +import static bjc.dicelang.v2.Errors.ErrorKey.*; +import static bjc.dicelang.v2.Evaluator.Result.Type.*; + public class Evaluator { public static class Result { public static enum Type { FAILURE, - INT, FLOAT, DICE + INT, FLOAT, DICE, STRING } public final Type type; @@ -18,6 +21,7 @@ public class Evaluator { public long intVal; public double floatVal; public DiceBox.DieExpression diceVal; + public String stringVal; public Result(Type typ) { type = typ; @@ -41,6 +45,24 @@ public class Evaluator { diceVal = dVal; } + public Result(Type typ, DiceBox.Die dVal) { + this(typ); + + diceVal = new DiceBox.DieExpression(dVal); + } + + public Result(Type typ, DiceBox.DieList dVal) { + this(typ); + + diceVal = new DiceBox.DieExpression(dVal); + } + + public Result(Type typ, String strang) { + this(typ); + + stringVal = strang; + } + public String toString() { switch(type) { case INT: @@ -49,6 +71,8 @@ public class Evaluator { return type.toString() + "(" + floatVal + ")"; case DICE: return type.toString() + "(" + diceVal + ")"; + case STRING: + return type.toString() + "(" + stringVal + ")"; case FAILURE: return type.toString(); default: @@ -57,6 +81,8 @@ public class Evaluator { } } + private final static Node FAIL = new Node(Node.Type.RESULT, new Result(Result.Type.FAILURE)); + private DiceLangEngine eng; public Evaluator(DiceLangEngine en) { @@ -78,14 +104,16 @@ public class Evaluator { switch(ast.getHead().type) { case UNARYOP: System.out.println("\tEVALUATOR ERROR: Unary operator evaluation isn't supported yet"); - return new Tree<>(new Node(Node.Type.RESULT, new Result(Result.Type.FAILURE))); + return new Tree<>(FAIL); case BINOP: return evaluateBinaryOp(ast); case TOKREF: return evaluateTokenRef(ast.getHead().tokenVal); + case ROOT: + return ast.getChild(ast.getChildrenCount() - 1); default: - System.out.println("\tERROR: Unknown node in evaluator: " + ast.getHead().type); - return new Tree<>(new Node(Node.Type.RESULT, new Result(Result.Type.FAILURE))); + Errors.inst.printError(EK_EVAL_INVNODE, ast.getHead().type.toString()); + return new Tree<>(FAIL); } } @@ -93,8 +121,9 @@ public class Evaluator { Token.Type binOp = ast.getHead().operatorType; if(ast.getChildrenCount() != 2) { - System.out.println("\tERROR: Binary operators only take two operands"); - return new Tree<>(new Node(Node.Type.RESULT, new Result(Result.Type.FAILURE))); + Errors.inst.printError(EK_EVAL_INVBIN, Integer.toString(ast.getChildrenCount())); + + return new Tree<>(FAIL); } ITree<Node> left = ast.getChild(0); @@ -107,17 +136,85 @@ public class Evaluator { case DIVIDE: case IDIVIDE: return evaluateMathBinary(binOp, left.getHead().resultVal, right.getHead().resultVal); + case DICEGROUP: + case DICECONCAT: + case DICELIST: + return evaluateDiceBinary(binOp, left.getHead().resultVal, right.getHead().resultVal); default: - System.out.println("\tERROR: Unknown binary operator: " + binOp); - return new Tree<>(new Node(Node.Type.RESULT, new Result(Result.Type.FAILURE))); + Errors.inst.printError(EK_EVAL_UNBIN, binOp.toString()); + return new Tree<>(FAIL); } } - private ITree<Node> evaluateMathBinary(Token.Type op, Result left, Result right) { - Result.Type resultType; + private ITree<Node> evaluateDiceBinary(Token.Type op, Result left, Result right) { + Result res = null; + switch(op) { + case DICEGROUP: + if(left.type == DICE && !left.diceVal.isList) { + if(right.type == DICE && !right.diceVal.isList) { + res = new Result(DICE, new DiceBox.SimpleDie(left.diceVal.scalar, + right.diceVal.scalar)); + } else if (right.type == INT) { + res = new Result(DICE, new DiceBox.SimpleDie(left.diceVal.scalar, right.intVal)); + } else { + Errors.inst.printError(EK_EVAL_INVDGROUP, right.type.toString()); + return new Tree<>(FAIL); + } + } else if(left.type == INT) { + if(right.type == DICE && !right.diceVal.isList) { + res = new Result(DICE, new DiceBox.SimpleDie(left.intVal, right.diceVal.scalar)); + } else if (right.type == INT) { + res = new Result(DICE, new DiceBox.SimpleDie(left.intVal, right.intVal)); + } else { + Errors.inst.printError(EK_EVAL_INVDGROUP, right.type.toString()); + return new Tree<>(FAIL); + } + } else { + Errors.inst.printError(EK_EVAL_INVDGROUP, left.type.toString()); + return new Tree<>(FAIL); + } + case DICECONCAT: + if(left.type != DICE || left.diceVal.isList) { + Errors.inst.printError(EK_EVAL_INVDICE, left.type.toString()); + return new Tree<>(FAIL); + } else if(right.type != DICE || right.diceVal.isList) { + Errors.inst.printError(EK_EVAL_INVDICE, right.type.toString()); + return new Tree<>(FAIL); + } else { + res = new Result(DICE, new DiceBox.CompoundDie(left.diceVal.scalar, + right.diceVal.scalar)); + } + break; + case DICELIST: + if(left.type != DICE || left.diceVal.isList) { + Errors.inst.printError(EK_EVAL_INVDICE, left.type.toString()); + return new Tree<>(FAIL); + } else if(right.type != DICE || right.diceVal.isList) { + Errors.inst.printError(EK_EVAL_INVDICE, right.type.toString()); + return new Tree<>(FAIL); + } else { + res = new Result(DICE, new DiceBox.SimpleDieList(left.diceVal.scalar, + right.diceVal.scalar)); + } + break; + default: + Errors.inst.printError(EK_EVAL_UNDICE, op.toString()); + return new Tree<>(FAIL); + } + + return new Tree<>(new Node(Node.Type.RESULT, res)); + } + + private ITree<Node> evaluateMathBinary(Token.Type op, Result left, Result right) { if(left.type == Result.Type.DICE || right.type == Result.Type.DICE) { System.out.println("\tEVALUATOR ERROR: Math on dice isn't supported yet"); + return new Tree<>(FAIL); + } else if(left.type == Result.Type.STRING || right.type == Result.Type.STRING) { + System.out.println("\tERROR: Math operators don't work on strings"); + return new Tree<>(FAIL); + } else if(left.type == Result.Type.FAILURE || right.type == Result.Type.FAILURE) { + return new Tree<>(FAIL); } Result res = null; @@ -172,14 +269,14 @@ public class Evaluator { if(left.type == Result.Type.INT) { if(right.type == Result.Type.INT) { if(right.intVal == 0) { - System.out.println("\tERROR: Attempted divide by zero"); + Errors.inst.printError(EK_EVAL_DIVZERO); res = new Result(Result.Type.FAILURE); } else { res = new Result(Result.Type.FLOAT, left.intVal / right.intVal); } } else { if(right.floatVal == 0) { - System.out.println("\tERROR: Attempted divide by zero"); + Errors.inst.printError(EK_EVAL_DIVZERO); res = new Result(Result.Type.FAILURE); } else { res = new Result(Result.Type.FLOAT, left.intVal / right.floatVal); @@ -188,14 +285,14 @@ public class Evaluator { } else { if(right.type == Result.Type.INT) { if(right.intVal == 0) { - System.out.println("\tERROR: Attempted divide by zero"); + Errors.inst.printError(EK_EVAL_DIVZERO); res = new Result(Result.Type.FAILURE); } else { res = new Result(Result.Type.FLOAT, left.floatVal / right.intVal); } } else { if(right.floatVal == 0) { - System.out.println("\tERROR: Attempted divide by zero"); + Errors.inst.printError(EK_EVAL_DIVZERO); res = new Result(Result.Type.FAILURE); } else { res = new Result(Result.Type.FLOAT, left.floatVal / right.floatVal); @@ -207,14 +304,14 @@ public class Evaluator { if(left.type == Result.Type.INT) { if(right.type == Result.Type.INT) { if(right.intVal == 0) { - System.out.println("\tERROR: Attempted divide by zero"); + Errors.inst.printError(EK_EVAL_DIVZERO); res = new Result(Result.Type.FAILURE); } else { res = new Result(Result.Type.INT, (int) (left.intVal / right.intVal)); } } else { if(right.floatVal == 0) { - System.out.println("\tERROR: Attempted divide by zero"); + Errors.inst.printError(EK_EVAL_DIVZERO); res = new Result(Result.Type.FAILURE); } else { res = new Result(Result.Type.INT, (int) (left.intVal / right.floatVal)); @@ -223,14 +320,14 @@ public class Evaluator { } else { if(right.type == Result.Type.INT) { if(right.intVal == 0) { - System.out.println("\tERROR: Attempted divide by zero"); + Errors.inst.printError(EK_EVAL_DIVZERO); res = new Result(Result.Type.FAILURE); } else { res = new Result(Result.Type.INT, (int) (left.floatVal / right.intVal)); } } else { if(right.floatVal == 0) { - System.out.println("\tERROR: Attempted divide by zero"); + Errors.inst.printError(EK_EVAL_DIVZERO); res = new Result(Result.Type.FAILURE); } else { res = new Result(Result.Type.INT, (int) (left.floatVal / right.floatVal)); @@ -239,24 +336,34 @@ public class Evaluator { } break; default: - System.out.println("\tERROR: Unknown math binary operator: " + op); - return new Tree<>(new Node(Node.Type.RESULT, new Result(Result.Type.FAILURE))); + Errors.inst.printError(EK_EVAL_UNMATH, op.toString()); + return new Tree<>(FAIL); } return new Tree<>(new Node(Node.Type.RESULT, res)); } private ITree<Node> evaluateTokenRef(Token tk) { + Result res = null; + switch(tk.type) { case INT_LIT: - return new Tree<>(new Node(Node.Type.RESULT, new Result(Result.Type.INT, tk.intValue))); + res = new Result(Result.Type.INT, tk.intValue); + break; case FLOAT_LIT: - return new Tree<>(new Node(Node.Type.RESULT, new Result(Result.Type.FLOAT, tk.floatValue))); + res = new Result(Result.Type.FLOAT, tk.floatValue); + break; case DICE_LIT: - return new Tree<>(new Node(Node.Type.RESULT, new Result(Result.Type.DICE, tk.diceValue))); + res = new Result(Result.Type.DICE, tk.diceValue); + break; + case STRING_LIT: + res = new Result(Result.Type.STRING, eng.stringLits.get((int)(tk.intValue))); + break; default: - System.out.println("\tERROR: Unknown token ref: " + tk.type); - return new Tree<>(new Node(Node.Type.RESULT, new Result(Result.Type.FAILURE))); + Errors.inst.printError(EK_EVAL_UNTOK, tk.type.toString()); + res = new Result(Result.Type.FAILURE); } + + return new Tree<>(new Node(Node.Type.RESULT, res)); } } diff --git a/dice-lang/src/bjc/dicelang/v2/Parser.java b/dice-lang/src/bjc/dicelang/v2/Parser.java index 0e778b4..9a873f1 100644 --- a/dice-lang/src/bjc/dicelang/v2/Parser.java +++ b/dice-lang/src/bjc/dicelang/v2/Parser.java @@ -7,6 +7,7 @@ import bjc.utils.data.ITree; import bjc.utils.data.Tree; import bjc.utils.funcdata.IList; +import static bjc.dicelang.v2.Errors.ErrorKey.*; import static bjc.dicelang.v2.Node.Type.*; import static bjc.dicelang.v2.Token.Type.*; @@ -28,7 +29,7 @@ public class Parser { case CBRACKET: case CBRACE: if(working.size() == 0) { - System.out.println("\tERROR: Group closing with no possible group opener"); + Errors.inst.printError(EK_PARSE_NOCLOSE); return false; } @@ -54,8 +55,7 @@ public class Parser { ITree<Node> matchNode = new Tree<>(new Node(OGROUP, matching)); if(!working.contains(matchNode)) { - System.out.println("\tERROR: Found group closing without group opener: (closing was " + tk + ", matcher was " - + matchNode + ")"); + Errors.inst.printError(EK_PARSE_UNCLOSE, tk.toString(), matchNode.toString()); System.out.println("\tCurrent forest is: "); @@ -63,6 +63,7 @@ public class Parser { for(ITree<Node> ast : working) { System.out.println("Tree " + treeNo++ + ": " + ast.toString()); } + return false; } else { Deque<ITree<Node>> childs = new LinkedList<>(); @@ -84,7 +85,8 @@ public class Parser { case LET: case BIND: if(working.size() < 2) { - System.out.println("\tERROR: Let and bind require at least two operands"); + Errors.inst.printError(EK_PARSE_BINARY); + return false; } else { ITree<Node> right = working.pop(); ITree<Node> left = working.pop(); @@ -103,7 +105,7 @@ public class Parser { case DICECONCAT: case DICELIST: if(working.size() == 0) { - System.out.println("\tERROR: Binary operator " + tk.type + " requires more operands than provided."); + Errors.inst.printError(EK_PARSE_UNOPERAND, tk.toString()); return false; } else if(working.size() == 1) { ITree<Node> operand = working.pop(); @@ -133,7 +135,7 @@ public class Parser { working.push(new Tree<>(new Node(TOKREF, tk))); break; default: - System.out.println("\tERROR: Unrecognized token type in parsing: " + tk.type); + Errors.inst.printError(EK_PARSE_INVTOKEN, tk.type.toString()); return false; } } diff --git a/dice-lang/src/bjc/dicelang/v2/Shunter.java b/dice-lang/src/bjc/dicelang/v2/Shunter.java index 7e17e6f..014be7d 100644 --- a/dice-lang/src/bjc/dicelang/v2/Shunter.java +++ b/dice-lang/src/bjc/dicelang/v2/Shunter.java @@ -10,6 +10,7 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.Set; +import static bjc.dicelang.v2.Errors.ErrorKey.*; import static bjc.dicelang.v2.Token.Type.*; public class Shunter { @@ -73,10 +74,7 @@ public class Shunter { || unaryAdjectives.contains(tk.type); if(isOp) { - System.out.printf("\tError: Unary operator %s is an" - + " adjective, not an adverb (can't be applied" - + " to operator %s)\n", currOperator, tk ); - + Errors.inst.printError(EK_SHUNT_NOTADV, currOperator.toString(), tk.toString()); return false; } @@ -84,8 +82,8 @@ public class Shunter { currReturned.addLast(unaryOps.pop()); } else if (unaryAdverbs.contains(currOperator.type)) { if(opStack.size() < 1) { - System.out.printf("\tError: Unary operators %s is an adverb," - + " but there is no operator to apply it to\n"); + Errors.inst.printError(EK_SHUNT_NOOP, currOperator.toString()); + return false; } Token currOperand = opStack.peek(); @@ -95,9 +93,9 @@ public class Shunter { || unaryAdjectives.contains(currOperand.type); if(!isOp) { - System.out.printf("\tError: Unary operator %s is an adverb," - + " not an adjective (can't be applied to operand %s)\n", - currOperator, tk); + Errors.inst.printError(EK_SHUNT_NOTADJ, + currOperator.toString(), + tk.toString()); return false; } @@ -140,9 +138,8 @@ public class Shunter { } if(!opStack.contains(matching)) { - System.out.printf("\tError: Could not find matching grouping " - + tk.type); - + Errors.inst.printError(EK_SHUNT_NOGROUP, + tk.toString(), matching.toString()); return false; } @@ -167,7 +164,7 @@ public class Shunter { } if(currReturned.size() == 0) { - System.out.println("\tERROR: Didn't find grouper for group seperator to attach to"); + Errors.inst.printError(EK_SHUNT_INVSEP); return false; } diff --git a/dice-lang/src/bjc/dicelang/v2/StreamEngine.java b/dice-lang/src/bjc/dicelang/v2/StreamEngine.java index 39bbc0d..8755e19 100644 --- a/dice-lang/src/bjc/dicelang/v2/StreamEngine.java +++ b/dice-lang/src/bjc/dicelang/v2/StreamEngine.java @@ -2,9 +2,12 @@ package bjc.dicelang.v2; import bjc.utils.funcdata.FunctionalList; import bjc.utils.funcdata.IList; +import bjc.utils.funcutils.ListUtils; import bjc.utils.esodata.SingleTape; import bjc.utils.esodata.Tape; +import static bjc.dicelang.v2.Errors.ErrorKey.*; + public class StreamEngine { private DiceLangEngine eng; @@ -25,11 +28,24 @@ public class StreamEngine { public boolean doStreams(String[] toks, IList<String> dest) { init(); + boolean quoteMode = false; + for(String tk : toks) { - if(tk.startsWith("{@S")) { - if(!processCommand(tk)) return false; + if(tk.startsWith("{@S") && !quoteMode) { + if(tk.equals("{@SQ}")) { + quoteMode = true; + } else if(!processCommand(tk)) { + return false; + } + // Command ran correctly, continue } else { - currStream.add(tk); + if(tk.equals("{@SU}")) { + quoteMode = false; + } else if(tk.equals("\\{@SU}")) { + currStream.add("{@SU}"); + } else { + currStream.add(tk); + } } } @@ -57,7 +73,7 @@ public class StreamEngine { break; case '>': if(!streams.right()) { - System.out.println("\tERROR: Attempted to switch to non-existent stream"); + Errors.inst.printError(EK_STRM_NONEX); return false; } @@ -65,7 +81,7 @@ public class StreamEngine { break; case '<': if(!streams.left()) { - System.out.println("\tERROR: Attempted to switch to non-existent stream"); + Errors.inst.printError(EK_STRM_NONEX); return false; } @@ -73,15 +89,25 @@ public class StreamEngine { break; case '-': if(streams.size() == 1) { - System.out.println("\tERROR: Cannot delete last stream"); + Errors.inst.printError(EK_STRM_LAST); return false; } else { streams.remove(); currStream = streams.item(); } break; + case 'S': + if(streams.size() == 1) { + Errors.inst.printError(EK_STRM_LAST); + return false; + } else { + IList<String> stringLit = streams.remove(); + currStream = streams.item(); + currStream.add(ListUtils.collapseTokens(stringLit, " ")); + } + break; default: - System.out.println("\tERROR: Unknown stream control command: " + tk); + Errors.inst.printError(EK_STRM_INVCOM, tk); return false; } } |
