diff options
| author | bculkin2442 <bjculkin@mix.wvu.edu> | 2017-03-06 14:15:03 -0500 |
|---|---|---|
| committer | bculkin2442 <bjculkin@mix.wvu.edu> | 2017-03-06 14:15:03 -0500 |
| commit | 9139064c95f6c9c4f7ba1d0aea21e2f5233ad188 (patch) | |
| tree | dd93017bff73e61fec20c58b7baa43d1662c0c5b /dice-lang/src | |
| parent | b11f8d2c92aaaf1160e69190559ffadc4774f138 (diff) | |
Formatting/Documentation
Diffstat (limited to 'dice-lang/src')
| -rw-r--r-- | dice-lang/src/bjc/dicelang/Define.java | 13 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/DiceLangEngine.java | 293 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/Errors.java | 254 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/StreamEngine.java | 134 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/dice/CompoundDie.java | 24 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/dice/CompoundingDie.java | 31 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/dice/DiceBox.java | 107 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/dice/Die.java | 30 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/dice/DieExpression.java | 30 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/dice/DieList.java | 24 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/dice/ExplodingDice.java | 43 |
11 files changed, 733 insertions, 250 deletions
diff --git a/dice-lang/src/bjc/dicelang/Define.java b/dice-lang/src/bjc/dicelang/Define.java index 2967af6..a70ca61 100644 --- a/dice-lang/src/bjc/dicelang/Define.java +++ b/dice-lang/src/bjc/dicelang/Define.java @@ -34,6 +34,11 @@ public class Define implements UnaryOperator<String> { doRecur = recur; subType = isSub; + inError = false; + + /* + * Only try to compile non-null predicates + */ if(predicte != null) { try { predicate = Pattern.compile(predicte); @@ -44,6 +49,9 @@ public class Define implements UnaryOperator<String> { } } + /* + * Compile the search pattern + */ try { searcher = Pattern.compile(searchr); } catch (PatternSyntaxException psex) { @@ -52,8 +60,9 @@ public class Define implements UnaryOperator<String> { return; } - inError = false; - + /* + * Check whether or not we do sub-replacements + */ if(subType) { if(replacrs.iterator().hasNext()) { replacers = new CircularIterator<>(replacrs, isCircular); diff --git a/dice-lang/src/bjc/dicelang/DiceLangEngine.java b/dice-lang/src/bjc/dicelang/DiceLangEngine.java index 195c98a..dfe956e 100644 --- a/dice-lang/src/bjc/dicelang/DiceLangEngine.java +++ b/dice-lang/src/bjc/dicelang/DiceLangEngine.java @@ -21,57 +21,97 @@ import java.util.LinkedList; import java.util.regex.Matcher; import java.util.regex.Pattern; +/** + * Implements the orchestration necessary for processing DiceLang commands + * + * @author Ben Culkin + */ public class DiceLangEngine { - // Input rules for processing tokens + /* + * Input rules for processing tokens. + */ private List<IPair<String, String>> opExpansionList; - private List<IPair<String, String>> deaffixationList; - // ID for generation + /* + * ID for generation. + */ private int nextLiteral; - // Debug indicator + /* + * Debug indicator. + */ private boolean debugMode; - // Should we do shunting? + /* + * Should we do shunting? + */ private boolean postfixMode; - // Should we reverse the token stream + /* + * Should we reverse the token stream? + */ private boolean prefixMode; - // Should we do step-by-step evaluation + /* + * Should we do step-by-step evaluation? + */ private boolean stepEval; - // Shunter for token postfixing + /* + * Shunter for token postfixing. + */ private Shunter shunt; - // Tokenizer for tokenizing + /* + * Tokenizer for tokenizing. + */ private Tokenizer tokenzer; - // Parser for tree construction + /* + * Parser for tree construction. + */ private Parser parsr; - // Evaluator for evaluating + /* + * Evaluator for evaluating. + */ private Evaluator eval; - // Tables for symbols + /* + * Tables for symbols. + */ public final IMap<Integer, String> symTable; public final IMap<Integer, String> stringLits; - // Lists for preprocessing + /* + * Lists for preprocessing. + */ private IList<Define> lineDefns; private IList<Define> tokenDefns; - // Are defns sorted by priority + /* + * Are defns sorted by priority? + */ private boolean defnsSorted; - // Stream engine for processing streams + /* + * Stream engine for processing streams. + */ private StreamEngine streamEng; public DiceLangEngine() { + /* + * Initialize defns. + */ lineDefns = new FunctionalList<>(); tokenDefns = new FunctionalList<>(); defnsSorted = true; + /* + * Init tables. + */ symTable = new FunctionalMap<>(); stringLits = new FunctionalMap<>(); + /* + * Initialize operator expansion list. + */ opExpansionList = new LinkedList<>(); - opExpansionList.add(new Pair<>("+", "\\+")); opExpansionList.add(new Pair<>("-", "-")); opExpansionList.add(new Pair<>("*", "\\*")); @@ -80,30 +120,36 @@ public class DiceLangEngine { opExpansionList.add(new Pair<>(":=", ":=")); opExpansionList.add(new Pair<>("=>", "=>")); opExpansionList.add(new Pair<>(",", ",")); - - deaffixationList = new LinkedList<>(); - - deaffixationList.add(new Pair<>("(", "\\(")); - deaffixationList.add(new Pair<>(")", "\\)")); - deaffixationList.add(new Pair<>("[", "\\[")); - deaffixationList.add(new Pair<>("]", "\\]")); - deaffixationList.add(new Pair<>("{", "\\{")); - deaffixationList.add(new Pair<>("}", "}")); + opExpansionList.add(new Pair<>("(", "\\(")); + opExpansionList.add(new Pair<>(")", "\\)")); + opExpansionList.add(new Pair<>("[", "\\[")); + opExpansionList.add(new Pair<>("]", "\\]")); + opExpansionList.add(new Pair<>("{", "\\{")); + opExpansionList.add(new Pair<>("}", "}")); nextLiteral = 1; + /* + * Initial mode settings. + */ debugMode = true; postfixMode = false; prefixMode = false; stepEval = false; + /* + * Create components. + */ streamEng = new StreamEngine(this); - shunt = new Shunter(); - tokenzer = new Tokenizer(this); - parsr = new Parser(); - eval = new Evaluator(this); + shunt = new Shunter(); + tokenzer = new Tokenizer(this); + parsr = new Parser(); + eval = new Evaluator(this); } + /** + * Sort defns by priority. + */ public void sortDefns() { Comparator<Define> defnCmp = (dfn1, dfn2) -> dfn1.priority - dfn2.priority; @@ -113,36 +159,66 @@ public class DiceLangEngine { defnsSorted = true; } + /** + * Add a defn that's applied to lines. + * + * @param dfn The defn to add. + */ public void addLineDefine(Define dfn) { lineDefns.add(dfn); defnsSorted = false; } + /** + * Add a defn that's applied to tokens. + * + * @param dfn The defn to add. + */ public void addTokenDefine(Define dfn) { tokenDefns.add(dfn); defnsSorted = false; } + /** + * Toggle debug mode. + * + * @return The current state of debug mode. + */ public boolean toggleDebug() { debugMode = !debugMode; return debugMode; } + /** + * Toggle postfix mode. + * + * @return The current state of postfix mode. + */ public boolean togglePostfix() { postfixMode = !postfixMode; return postfixMode; } + /** + * Toggle prefix mode. + * + * @return The current state of prefix mode + */ public boolean togglePrefix() { prefixMode = !prefixMode; return prefixMode; } + /** + * Toggle step-eval mode + * + * @return The current state of step-eval mode + */ public boolean toggleStepEval() { stepEval = !stepEval; @@ -163,34 +239,49 @@ public class DiceLangEngine { */ private Pattern quotePattern = Pattern.compile("\"([^\\\"]*(?:\\\"(?:[^\\\"])*)*)\""); - // Similiar to the above, but using angle brackets instead of quotes + /* + * Similiar to the above, but using angle brackets instead of quotes + */ private Pattern nonExpandPattern = Pattern.compile("<<([^\\>]*(?:\\>(?:[^\\>])*)*)>>"); + /** + * Run a command to completion. + * + * @param command The command to run + * + * @return Whether or not the command ran succesfully + */ public boolean runCommand(String command) { - // Sort the defines if they aren't sorted + /* + * Sort the defines if they aren't sorted + */ if(!defnsSorted) sortDefns(); + /* + * Run the tokens through the stream engine + */ IList<String> streamToks = new FunctionalList<>(); boolean success = streamEng.doStreams(command.split(" "), streamToks); if(!success) return false; + /* + * Apply line defns + */ String newComm = ListUtils.collapseTokens(streamToks, " "); - if(debugMode) System.out.println("\tCommand after stream commands: " + newComm); - for(Define dfn : lineDefns.toIterable()) { newComm = dfn.apply(newComm); } - if(debugMode) System.out.println("\tCommand after line defines: " + newComm); + /* + * Destring command + */ IMap<String, String> stringLiterals = new FunctionalMap<>(); - Matcher quoteMatcher = quotePattern.matcher(newComm); StringBuffer destringedCommand = new StringBuffer(); - while(quoteMatcher.find()) { String stringLit = quoteMatcher.group(1); @@ -199,13 +290,7 @@ public class DiceLangEngine { quoteMatcher.appendReplacement(destringedCommand, " " + litName + " "); } - quoteMatcher.appendTail(destringedCommand); - - // Split the command into tokens - IList<String> tokens = FunctionalStringTokenizer - .fromString(destringedCommand.toString()).toList(); - if(debugMode) { System.out.println("\tCommand after destringing: " + destringedCommand); @@ -218,11 +303,19 @@ public class DiceLangEngine { } } + /* + * Split the command into tokens + */ + IList<String> tokens = FunctionalStringTokenizer + .fromString(destringedCommand.toString()).toList(); + + + /* + * Temporarily remove non-expanding tokens + */ IMap<String, String> nonExpandedTokens = new FunctionalMap<>(); - tokens = tokens.map(tk -> { Matcher nonExpandMatcher = nonExpandPattern.matcher(tk); - if(nonExpandMatcher.matches()) { String tkName = "nonExpandToken" + nextLiteral++; nonExpandedTokens.put(tkName, nonExpandMatcher.group(1)); @@ -232,14 +325,18 @@ public class DiceLangEngine { return tk; } }); + if(debugMode) + System.out.printf("\tCommand after removal of non-expanders: %s\n", tokens.toString()); - System.out.println("\tCommand after removal of non-expanders: " + tokens.toString()); - - IList<String> semiExpandedTokens = deaffixTokens(tokens, deaffixationList); - IList<String> fullyExpandedTokens = deaffixTokens(semiExpandedTokens, opExpansionList); - + /* + * Expand tokens + */ + IList<String> fullyExpandedTokens = deaffixTokens(tokens, opExpansionList); System.out.println("\tCommand after token expansion: " + fullyExpandedTokens.toString()); + /* + * Reinsert non-expanded tokens + */ fullyExpandedTokens = fullyExpandedTokens.map(tk -> { if(tk.startsWith("nonExpandToken")) { return nonExpandedTokens.get(tk); @@ -247,46 +344,61 @@ public class DiceLangEngine { return tk; } }); - if(debugMode) - System.out.printf("\tCommand after non-expander reinsertion: " - + fullyExpandedTokens.toString() + "\n"); + System.out.printf("\tCommand after non-expander reinsertion: %s\n", + fullyExpandedTokens.toString()); IList<Token> lexedTokens = new FunctionalList<>(); - for(String token : fullyExpandedTokens) { String newTok = token; + /* + * Apply token defns + */ for(Define dfn : tokenDefns.toIterable()) { newTok = dfn.apply(newTok); } + /* + * Lex the token + */ Token tk = tokenzer.lexToken(token, stringLiterals); + /* + * Ignore blank tokens + */ if(tk == null) continue; + /* + * Fail on bad tokens + */ else if(tk == Token.NIL_TOKEN) return false; else lexedTokens.add(tk); } - if(debugMode) System.out.printf("\tCommand after tokenization: %s\n", lexedTokens.toString()); + /* + * Handle preshunted tokens + */ IList<Token> shuntedTokens = lexedTokens; - IList<Token> preparedTokens = new FunctionalList<>(); - boolean sc = removePreshuntTokens(lexedTokens, preparedTokens); - + succ = removePreshuntTokens(lexedTokens, preparedTokens); if(!sc) return false; - if(debugMode && !postfixMode) System.out.printf("\tCommand after pre-shunter removal: %s\n", preparedTokens.toString()); if(!postfixMode && !prefixMode) { + /* + * Shunt the tokens + */ shuntedTokens = new FunctionalList<>(); success = shunt.shuntTokens(preparedTokens, shuntedTokens); if(!success) return false; } else if(prefixMode) { + /* + * Reverse directional tokens + */ preparedTokens.reverse(); shuntedTokens = preparedTokens.map(tk -> { switch(tk.type) { @@ -307,10 +419,12 @@ public class DiceLangEngine { } }); } - if(debugMode && !postfixMode) System.out.printf("\tCommand after shunting: %s\n", shuntedTokens.toString()); + /* + * Expand token groups + */ IList<Token> readyTokens = shuntedTokens.flatMap(tk -> { if(tk.type == Token.Type.TOKGROUP) { return tk.tokenValues; @@ -320,37 +434,47 @@ public class DiceLangEngine { return new FunctionalList<>(tk); } }); - if(debugMode && !postfixMode) System.out.printf("\tCommand after re-preshunting: %s\n", readyTokens.toString()); + /* + * Parse the tokens + */ IList<ITree<Node>> astForest = new FunctionalList<>(); success = parsr.parseTokens(readyTokens, astForest); - if(!success) return false; - if(debugMode) { - evaluateForest(astForest); - } + /* + * Evaluate the tokens + */ + evaluateForest(astForest); return true; } private void evaluateForest(IList<ITree<Node>> astForest) { - System.out.println("\tParsed forest of asts"); + if(debugMode) + System.out.println("\tParsed forest of asts"); int treeNo = 1; for(ITree<Node> ast : astForest) { - System.out.println("\t\tTree " + treeNo + " in forest:\n" + ast); + if(debugMode) + System.out.println("\t\tTree " + treeNo + " in forest:\n" + ast); - if(stepEval) { + if(debugMode && stepEval) { int step = 1; + /* + * Evaluate it step by step + */ for(Iterator<ITree<Node>> itr = eval.stepDebug(ast); itr.hasNext();){ ITree<Node> nodeStep = itr.next(); System.out.printf("\t\tStep %d: Node is %s", step, nodeStep); + /* + * Don't evaluate null steps + */ if(nodeStep == null) { System.out.println(); @@ -358,6 +482,9 @@ public class DiceLangEngine { continue; } + /* + * Print out details for results + */ if(nodeStep.getHead().type == Node.Type.RESULT) { EvaluatorResult res = nodeStep.getHead().resultVal; @@ -374,16 +501,24 @@ public class DiceLangEngine { System.out.printf(")"); } - + /* + * Advance a step + */ System.out.println(); step += 1; } } else { + /* + * Evaluate it normally + */ EvaluatorResult res = eval.evaluate(ast); - System.out.printf("\t\tEvaluates to %s", res); - if(res.type == EvaluatorResult.Type.DICE) { - System.out.println("\t\t (sample roll " + res.diceVal.value() + ")"); + if(debugMode) { + System.out.printf("\t\tEvaluates to %s", res); + + if(res.type == EvaluatorResult.Type.DICE) { + System.out.println("\t\t (sample roll " + res.diceVal.value() + ")"); + } } } @@ -470,12 +605,16 @@ public class DiceLangEngine { for(String tk : working) { if(opRegexOnly.matcher(tk).matches()) { - // The string contains only the operator + /* + * The string contains only the operator + */ newWorking.add(tk); } else { Matcher medianMatcher = opRegexPattern.matcher(tk); - // Read the first match + /* + * Read the first match + */ boolean found = medianMatcher.find(); if(!found) { @@ -496,7 +635,9 @@ public class DiceLangEngine { String[] pieces = opRegexPattern.split(tk); if(startsWith) { - // Skip the starting operator + /* + * Skip the starting operator + */ medianMatcher.find(); newWorking.add(tk.substring(0, startMatcher.end())); } @@ -504,7 +645,9 @@ public class DiceLangEngine { for(int i = 0; i < pieces.length; i++) { String piece = pieces[i]; - // Find the next operator + /* + * Find the next operator + */ boolean didFind = medianMatcher.find(); if(piece.equals("")) { diff --git a/dice-lang/src/bjc/dicelang/Errors.java b/dice-lang/src/bjc/dicelang/Errors.java index e6a825c..729ecc2 100644 --- a/dice-lang/src/bjc/dicelang/Errors.java +++ b/dice-lang/src/bjc/dicelang/Errors.java @@ -21,7 +21,7 @@ public class Errors { EK_ENG_NOOPENING, // Reached end of command w/out balanced doublebraces EK_ENG_NOCLOSING, - + // Tokenizer Errors // Found an unexpected grouping token EK_TOK_UNGROUP, @@ -29,7 +29,7 @@ public class Errors { EK_TOK_INVBASE, // Invalid flexadecimal number in a given base EK_TOK_INVFLEX, - + // Evaluator Errors // Unknown node type EK_EVAL_INVNODE, @@ -102,136 +102,136 @@ public class Errors { 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); + 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" - + "\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]); - 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_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; - 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_EVAL_MISMATH: - System.out.printf("\tERROR: Math operators expect two operands of the same type\n"); - 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); + 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" + + "\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]); + 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_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; + 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_EVAL_MISMATH: + System.out.printf("\tERROR: Math operators expect two operands of the same type\n"); + 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); } } diff --git a/dice-lang/src/bjc/dicelang/StreamEngine.java b/dice-lang/src/bjc/dicelang/StreamEngine.java index 2971392..5debe00 100644 --- a/dice-lang/src/bjc/dicelang/StreamEngine.java +++ b/dice-lang/src/bjc/dicelang/StreamEngine.java @@ -8,37 +8,93 @@ import static bjc.dicelang.Errors.ErrorKey.*; import bjc.utils.esodata.SingleTape; import bjc.utils.esodata.Tape; - +import bjc.utils.esodata.TapeLibrary; + +/** + * Implements multiple interleaved parse streams, as well as a command language + * for the streams. + * + * The idea for the interleaved streams came from the language Oozylbub & + * Murphy, but the command language was my own idea. + * + * @author Ben Culkin + */ public class StreamEngine { + /* + * The engine we're attached to. + */ private DiceLangEngine eng; + /* + * Our streams. + */ private Tape<IList<String>> streams; private IList<String> currStream; + /* + * Saved streams + */ + private TapeLibrary<IList<String>> savedStreams; + + /** + * Create a new stream engine. + * + * @param engine The dice engine we're attached to. + */ public StreamEngine(DiceLangEngine engine) { eng = engine; + + savedStreams = new TapeLibrary<>(); } private void init() { + /* + * Reinitialize our list of streams. + */ streams = new SingleTape<>(); + /* + * Create an initial stream. + */ currStream = new FunctionalList<>(); streams.insertBefore(currStream); } + /** + * Process a possibly interleaved set of streams from toks into dest. + * + * @param toks The raw token to read streams from. + * @param dest The list to write the final stream to. + * + * @return Whether or not the streams were successfully processed. + */ public boolean doStreams(String[] toks, IList<String> dest) { + /* + * Initialize per-run state. + */ init(); + /* + * Are we currently quoting things? + */ boolean quoteMode = false; + /* + * Process each token. + */ for(String tk : toks) { + /* + * Process stream commands. + */ if(tk.startsWith("{@S") && !quoteMode) { if(tk.equals("{@SQ}")) { quoteMode = true; } else if(!processCommand(tk)) { return false; } - // Command ran correctly, continue + /* + * Command ran correctly, continue + */ } else { if(tk.equals("{@SU}")) { quoteMode = false; @@ -69,47 +125,47 @@ public class StreamEngine { for(char comm : comms) { switch(comm) { - case '+': - streams.insertAfter(new FunctionalList<>()); - break; - case '>': - if(!streams.right()) { - Errors.inst.printError(EK_STRM_NONEX); - return false; - } + case '+': + streams.insertAfter(new FunctionalList<>()); + break; + case '>': + if(!streams.right()) { + Errors.inst.printError(EK_STRM_NONEX); + return false; + } - currStream = streams.item(); - break; - case '<': - if(!streams.left()) { - Errors.inst.printError(EK_STRM_NONEX); - return false; - } + currStream = streams.item(); + break; + case '<': + if(!streams.left()) { + Errors.inst.printError(EK_STRM_NONEX); + return false; + } + currStream = streams.item(); + break; + case '-': + if(streams.size() == 1) { + Errors.inst.printError(EK_STRM_LAST); + return false; + } else { + streams.remove(); currStream = streams.item(); - break; - case '-': - if(streams.size() == 1) { - 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: - Errors.inst.printError(EK_STRM_INVCOM, tk); + } + 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: + Errors.inst.printError(EK_STRM_INVCOM, tk); + return false; } } diff --git a/dice-lang/src/bjc/dicelang/dice/CompoundDie.java b/dice-lang/src/bjc/dicelang/dice/CompoundDie.java index 16aec76..a20095a 100644 --- a/dice-lang/src/bjc/dicelang/dice/CompoundDie.java +++ b/dice-lang/src/bjc/dicelang/dice/CompoundDie.java @@ -1,31 +1,53 @@ package bjc.dicelang.dice;
+/**
+ * A die whose rolls result from concatenating two other rolls together.
+ *
+ * @author Ben Culkin
+ */
public class CompoundDie implements Die {
+ /*
+ * The dice that form this die
+ */
private Die left;
private Die right;
+ /**
+ * Create a new compound die.
+ *
+ * @param lft The left die
+ * @param rght The right die
+ */
public CompoundDie(Die lft, Die rght) {
left = lft;
right = rght;
}
+ @Override
public boolean canOptimize() {
return left.canOptimize() && right.canOptimize();
}
+ @Override
public long optimize() {
return Long.parseLong(left.optimize() + "" + right.optimize());
}
+ @Override
public long roll() {
return Long.parseLong(left.roll() + "" + right.roll());
}
+ @Override
public long rollSingle() {
+ /*
+ * We're only one die, we can't be split
+ */
return roll();
}
+ @Override
public String toString() {
return left.toString() + "c" + right.toString();
}
-}
\ No newline at end of file +}
diff --git a/dice-lang/src/bjc/dicelang/dice/CompoundingDie.java b/dice-lang/src/bjc/dicelang/dice/CompoundingDie.java index 9744650..3291b52 100644 --- a/dice-lang/src/bjc/dicelang/dice/CompoundingDie.java +++ b/dice-lang/src/bjc/dicelang/dice/CompoundingDie.java @@ -2,16 +2,37 @@ package bjc.dicelang.dice; import java.util.function.Predicate;
+/**
+ * Implements a compounding die.
+ *
+ * This means that the source will be rolled, and then more single rolls will be
+ * added while it meets a qualification.
+ *
+ * @author Ben Culkin
+ */
public class CompoundingDie implements Die {
private Die source;
private Predicate<Long> compoundOn;
private String compoundPattern;
+ /**
+ * Create a new compounding die with no pattern.
+ *
+ * @param src The die to compound from
+ * @param compound The conditions to compound on
+ */
public CompoundingDie(Die src, Predicate<Long> compound) {
this(src, compound, null);
}
+ /**
+ * Create a new compounding die with a specified pattern.
+ *
+ * @param src The die to compound from
+ * @param compound The conditions to compound on
+ * @param patt The string pattern the condition came from, for printing
+ */
public CompoundingDie(Die src, Predicate<Long> compound, String patt) {
source = src;
@@ -19,14 +40,17 @@ public class CompoundingDie implements Die { compoundPattern = patt;
}
+ @Override
public boolean canOptimize() {
return source.canOptimize() && source.optimize() == 0;
}
+ @Override
public long optimize() {
return 0;
}
+ @Override
public long roll() {
long res = source.roll();
long oldRes = res;
@@ -40,7 +64,11 @@ public class CompoundingDie implements Die { return res;
}
+ @Override
public long rollSingle() {
+ /*
+ * Just compound on a single roll
+ */
long res = source.rollSingle();
long oldRes = res;
@@ -53,6 +81,7 @@ public class CompoundingDie implements Die { return res;
}
+ @Override
public String toString() {
if(compoundPattern == null) {
return source + "!!";
@@ -60,4 +89,4 @@ public class CompoundingDie implements Die { return source + "!!" + compoundPattern;
}
}
-}
\ No newline at end of file +}
diff --git a/dice-lang/src/bjc/dicelang/dice/DiceBox.java b/dice-lang/src/bjc/dicelang/dice/DiceBox.java index 4c0641a..92e58af 100644 --- a/dice-lang/src/bjc/dicelang/dice/DiceBox.java +++ b/dice-lang/src/bjc/dicelang/dice/DiceBox.java @@ -4,21 +4,43 @@ import java.util.Random; import java.util.function.Predicate; import java.util.regex.Pattern; +/** + * Contains static methods for producing dice from strings. + * + * @author Ben Culkin + */ public class DiceBox { static final Random rng = new Random(); + /** + * Parse a die expression from a string. + * + * @return The die expression from the string, or null if it wasn't one + */ public static DieExpression parseExpression(String exp) { + /* + * Only bother will valid expressions + */ if(!isValidExpression(exp)) return null; if(scalarDiePattern.matcher(exp).matches()) { + /* + * Parse scalar die + */ Die scal = new ScalarDie(Long.parseLong(exp.substring(0, exp.indexOf('s')))); return new DieExpression(scal); } else if(simpleDiePattern.matcher(exp).matches()) { + /* + * Parse simple die groups + */ String[] dieParts = exp.split("d"); long right = Long.parseLong(dieParts[1]); if(dieParts[0].equals("")) { + /* + * Handle short-form expressions. + */ Die scal = new SimpleDie(1, right); return new DieExpression(scal); } else { @@ -26,10 +48,16 @@ public class DiceBox { return new DieExpression(scal); } } else if(fudgeDiePattern.matcher(exp).matches()) { + /* + * Parse fudge dice + */ String nDice = exp.substring(0, exp.indexOf('d')); return new DieExpression(new FudgeDie(Long.parseLong(nDice))); } else if(compoundDiePattern.matcher(exp).matches()) { + /* + * Parse compound die expressions + */ String[] dieParts = exp.split("c"); DieExpression left = parseExpression(dieParts[0]); @@ -37,6 +65,9 @@ public class DiceBox { return new DieExpression(new CompoundDie(left.scalar, right.scalar)); } else if(compoundingDiePattern.matcher(exp).matches()) { + /* + * Parse compounding die expressions + */ String[] dieParts = exp.split("!!"); DieExpression left = parseExpression(dieParts[0]); @@ -45,6 +76,9 @@ public class DiceBox { Die scal = new CompoundingDie(left.scalar, right, dieParts[1]); return new DieExpression(scal); } else if(explodingDiePattern.matcher(exp).matches()) { + /* + * Parse exploding die expressions + */ String[] dieParts = exp.split("!"); DieExpression left = parseExpression(dieParts[0]); @@ -53,6 +87,9 @@ public class DiceBox { DieList lst = new ExplodingDice(left.scalar, right, dieParts[1], false); return new DieExpression(lst); } else if(penetratingDiePattern.matcher(exp).matches()) { + /* + * Parse penetrating die expressions + */ String[] dieParts = exp.split("p!"); DieExpression left = parseExpression(dieParts[0]); @@ -61,6 +98,9 @@ public class DiceBox { DieList lst = new ExplodingDice(left.scalar, right, dieParts[1], true); return new DieExpression(lst); } else if(diceListPattern.matcher(exp).matches()) { + /* + * Parse simple die lists + */ String[] dieParts = exp.split("dl"); DieExpression left = parseExpression(dieParts[0]); @@ -70,39 +110,101 @@ public class DiceBox { return new DieExpression(lst); } - // @TODO give a specific error message return null; } + /* + * The strings and patterns used for matching + */ + + /* + * Defines a comparison predicate + */ private static final String comparePoint = "[<>=]\\d+"; + /* + * Defines a scalar die. + * + * This is just a number. + */ private static final String scalarDie = "[\\+\\-]?\\d+sd"; private static final Pattern scalarDiePattern = Pattern.compile("\\A" + scalarDie + "\\Z"); + /* + * Defines a simple die. + * + * This is a group of one or more dice of the same size. + */ private static final String simpleDie = "(?:\\d+)?d\\d+"; private static final Pattern simpleDiePattern = Pattern.compile("\\A" + simpleDie + "\\Z"); + /* + * Defines a fudge die. + * + * This is like a simple die, but all the die give -1, 0, or 1 as + * results. + */ private static final String fudgeDie = "(?:\\d+)?dF"; private static final Pattern fudgeDiePattern = Pattern.compile("\\A" + fudgeDie + "\\Z"); + /* + * Defines a compound die. + * + * This is like using two d10's to simulate a d100 + */ private static final String compoundDie = simpleDie + "c(?:(?:" + simpleDie + ")|(?:\\d+))"; private static final Pattern compoundDiePattern = Pattern.compile("\\A" + compoundDie + "\\Z"); + /* + * Defines a compound group. + * + * This is used for forming die list type expressions. + */ private static final String compoundGroup = "(?:(?:" + scalarDie + ")|(?:" + simpleDie + ")|(?:" + compoundDie + ")|(?:" + fudgeDie +"))"; + /* + * Defines a compounding die. + * + * This is like an exploding die, but is a single die, not a group of + * them. + */ private static final String compoundingDie = compoundGroup + "!!" + comparePoint; private static final Pattern compoundingDiePattern = Pattern.compile("\\A" + compoundingDie + "\\Z"); + /* + * Defines an exploding die. + * + * This is a die that you reroll the component of if it meets a certain + * condition. + */ private static final String explodingDie = compoundGroup + "!" + comparePoint; private static final Pattern explodingDiePattern = Pattern.compile("\\A" + explodingDie + "\\Z"); + /* + * Defines a penetrating die. + * + * This is like an exploding die, but the exploded result gets a -1 + * penalty. + */ private static final String penetratingDie = compoundGroup + "!" + comparePoint; private static final Pattern penetratingDiePattern = Pattern.compile("\\A" + penetratingDie + "\\Z"); + /* + * Defines a die list. + * + * This is an array of dice of the specified size + */ private static final String diceList = compoundGroup + "dl" + compoundGroup; private static final Pattern diceListPattern = Pattern.compile("\\A" + diceList + "\\Z"); + /** + * Check if a given string is a valid die expression. + * + * @param exp The string to check validity of. + * + * @return Whether or not the string is a valid command + */ public static boolean isValidExpression(String exp) { if(scalarDiePattern.matcher(exp).matches()) { return true; @@ -125,6 +227,9 @@ public class DiceBox { } } + /* + * Derive a predicate from a compare point + */ private static Predicate<Long> deriveCond(String patt) { long num = Long.parseLong(patt.substring(1)); diff --git a/dice-lang/src/bjc/dicelang/dice/Die.java b/dice-lang/src/bjc/dicelang/dice/Die.java index 9f839ed..22ba522 100644 --- a/dice-lang/src/bjc/dicelang/dice/Die.java +++ b/dice-lang/src/bjc/dicelang/dice/Die.java @@ -1,9 +1,37 @@ package bjc.dicelang.dice;
+/**
+ * Represents one or more dice that produce a scalar result.
+ *
+ * @author Ben Culkin
+ */
public interface Die {
+ /**
+ * Can this die be optimized to a single number?
+ *
+ * @return Whether this die can be optimized or not.
+ */
boolean canOptimize();
+ /**
+ * Optimize this die to a single number.
+ *
+ * Calling optimize on dice that return false for canOptimize produces
+ * undefined behavior
+ *
+ * @return The optimized form of this die
+ */
long optimize();
+ /**
+ * Roll this die.
+ *
+ * @return A possible roll of this die
+ */
long roll();
+ /**
+ * Roll only a single portion of this die.
+ *
+ * @return A possible roll of a single portion of this die.
+ */
long rollSingle();
-}
\ No newline at end of file +}
diff --git a/dice-lang/src/bjc/dicelang/dice/DieExpression.java b/dice-lang/src/bjc/dicelang/dice/DieExpression.java index 3bf121b..81d0b7d 100644 --- a/dice-lang/src/bjc/dicelang/dice/DieExpression.java +++ b/dice-lang/src/bjc/dicelang/dice/DieExpression.java @@ -2,29 +2,57 @@ package bjc.dicelang.dice; import java.util.Arrays;
+/**
+ * Represents either a die or a die list
+ *
+ * @author Ben Culkin
+ */
public class DieExpression {
+ /**
+ * Is this expression a list?
+ */
public final boolean isList;
+ /**
+ * The scalar value in this expression, if there is one.
+ */
public Die scalar;
+ /**
+ * The list value in this expression, if there is one.
+ */
public DieList list;
+ /**
+ * Create a scalar die expression.
+ *
+ * @param scal The scalar value of this expression.
+ */
public DieExpression(Die scal) {
isList = false;
scalar = scal;
}
+ /**
+ * Create a list die expression.
+ *
+ * @param lst The list value of this expression.
+ */
public DieExpression(DieList lst) {
isList = true;
list = lst;
}
+ @Override
public String toString() {
if(isList) return list.toString();
else return scalar.toString();
}
+ /**
+ * Get the value of this expression as a string.
+ */
public String value() {
if(isList) return Arrays.toString(list.roll());
else return Long.toString(scalar.roll());
}
-}
\ No newline at end of file +}
diff --git a/dice-lang/src/bjc/dicelang/dice/DieList.java b/dice-lang/src/bjc/dicelang/dice/DieList.java index a55f2b9..5454759 100644 --- a/dice-lang/src/bjc/dicelang/dice/DieList.java +++ b/dice-lang/src/bjc/dicelang/dice/DieList.java @@ -1,8 +1,30 @@ package bjc.dicelang.dice;
+/**
+ * Represents a group of dice.
+ *
+ * @author Ben Culkin.
+ */
public interface DieList {
+ /**
+ * Can this list be optimized?
+ *
+ * @return Whether or not this list cna be optimized.
+ */
boolean canOptimize();
+ /**
+ * Optimize this list, if it can be done.
+ *
+ * Invoking this on unoptimizable expression is undefined.
+ *
+ * @return The optimized form of this list.
+ */
long[] optimize();
+ /**
+ * Roll this group of dice.
+ *
+ * @param A possible roll of this group.
+ */
long[] roll();
-}
\ No newline at end of file +}
diff --git a/dice-lang/src/bjc/dicelang/dice/ExplodingDice.java b/dice-lang/src/bjc/dicelang/dice/ExplodingDice.java index 6ab9902..59e739e 100644 --- a/dice-lang/src/bjc/dicelang/dice/ExplodingDice.java +++ b/dice-lang/src/bjc/dicelang/dice/ExplodingDice.java @@ -4,21 +4,58 @@ import java.util.LinkedList; import java.util.List;
import java.util.function.Predicate;
+/**
+ * An exploding die.
+ *
+ * Represents a die list that keeps getting another added die as long as a
+ * condition is met.
+ *
+ * @author Ben Culkin
+ */
public class ExplodingDice implements DieList {
+ /*
+ * The source die to use.
+ */
private Die source;
+ /*
+ * The conditions for exploding.
+ */
private Predicate<Long> explodeOn;
private String explodePattern;
private boolean explodePenetrates;
+ /**
+ * Create a new exploding die.
+ *
+ * @param src The source die for exploding.
+ * @param explode The condition to explode on.
+ */
public ExplodingDice(Die src, Predicate<Long> explode) {
this(src, explode, null, false);
}
+ /**
+ * Create a new exploding die that may penetrate.
+ *
+ * @param src The source die for exploding.
+ * @param explode The condition to explode on.
+ * @param penetrate Whether or not for explosions to penetrate (-1 to
+ * exploded die).
+ */
public ExplodingDice(Die src, Predicate<Long> explode, boolean penetrate) {
this(src, explode, null, penetrate);
}
+ /**
+ * Create a new exploding die that may penetrate.
+ *
+ * @param src The source die for exploding.
+ * @param explode The condition to explode on.
+ * @param penetrate Whether or not for explosions to penetrate (-1 to
+ * exploded die).
+ * @param patt The string the condition came from, for printing.
+ */
public ExplodingDice(Die src, Predicate<Long> explode, String patt,
boolean penetrate) {
source = src;
@@ -27,14 +64,17 @@ public class ExplodingDice implements DieList { explodePenetrates = penetrate;
}
+ @Override
public boolean canOptimize() {
return false;
}
+ @Override
public long[] optimize() {
return new long[0];
}
+ @Override
public long[] roll() {
long res = source.roll();
long oldRes = res;
@@ -60,6 +100,7 @@ public class ExplodingDice implements DieList { return newRes;
}
+ @Override
public String toString() {
if(explodePattern == null) {
return source + (explodePenetrates ? "p" : "") + "!";
@@ -67,4 +108,4 @@ public class ExplodingDice implements DieList { return source + (explodePenetrates ? "p" : "") + "!" + explodePattern;
}
}
-}
\ No newline at end of file +}
|
