From 9139064c95f6c9c4f7ba1d0aea21e2f5233ad188 Mon Sep 17 00:00:00 2001 From: bculkin2442 Date: Mon, 6 Mar 2017 14:15:03 -0500 Subject: Formatting/Documentation --- dice-lang/src/bjc/dicelang/Define.java | 13 +- dice-lang/src/bjc/dicelang/DiceLangEngine.java | 293 +++++++++++++++------ dice-lang/src/bjc/dicelang/Errors.java | 254 +++++++++--------- dice-lang/src/bjc/dicelang/StreamEngine.java | 134 +++++++--- dice-lang/src/bjc/dicelang/dice/CompoundDie.java | 24 +- .../src/bjc/dicelang/dice/CompoundingDie.java | 31 ++- dice-lang/src/bjc/dicelang/dice/DiceBox.java | 107 +++++++- dice-lang/src/bjc/dicelang/dice/Die.java | 30 ++- dice-lang/src/bjc/dicelang/dice/DieExpression.java | 30 ++- dice-lang/src/bjc/dicelang/dice/DieList.java | 24 +- dice-lang/src/bjc/dicelang/dice/ExplodingDice.java | 43 ++- 11 files changed, 733 insertions(+), 250 deletions(-) (limited to 'dice-lang/src/bjc') 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 { 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 { } } + /* + * Compile the search pattern + */ try { searcher = Pattern.compile(searchr); } catch (PatternSyntaxException psex) { @@ -52,8 +60,9 @@ public class Define implements UnaryOperator { 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> opExpansionList; - private List> 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 symTable; public final IMap stringLits; - // Lists for preprocessing + /* + * Lists for preprocessing. + */ private IList lineDefns; private IList 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 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 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 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 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 tokens = FunctionalStringTokenizer + .fromString(destringedCommand.toString()).toList(); + + + /* + * Temporarily remove non-expanding tokens + */ IMap 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 semiExpandedTokens = deaffixTokens(tokens, deaffixationList); - IList fullyExpandedTokens = deaffixTokens(semiExpandedTokens, opExpansionList); - + /* + * Expand tokens + */ + IList 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 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 shuntedTokens = lexedTokens; - IList 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 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> 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> astForest) { - System.out.println("\tParsed forest of asts"); + if(debugMode) + System.out.println("\tParsed forest of asts"); int treeNo = 1; for(ITree 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> itr = eval.stepDebug(ast); itr.hasNext();){ ITree 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> streams; private IList currStream; + /* + * Saved streams + */ + private TapeLibrary> 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 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 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 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 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 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 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 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 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 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 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 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 +} -- cgit v1.2.3