diff options
| author | Benjamin J. Culkin <bjculkin@mix.wvu.edu> | 2017-10-09 16:02:10 -0300 |
|---|---|---|
| committer | Benjamin J. Culkin <bjculkin@mix.wvu.edu> | 2017-10-09 16:02:10 -0300 |
| commit | f028ea6dc555fc5192a96b00b8e96e90dbf6de55 (patch) | |
| tree | 4b2a28ecbeb30095b50e6e9e8ac8b98fa8ddc79e /dice-lang/src/bjc/dicelang | |
| parent | be4675f9512060aa85b1e0a4f223208b51b55812 (diff) | |
TODO tagging
Diffstat (limited to 'dice-lang/src/bjc/dicelang')
31 files changed, 645 insertions, 663 deletions
diff --git a/dice-lang/src/bjc/dicelang/CLIArgsParser.java b/dice-lang/src/bjc/dicelang/CLIArgsParser.java index a1f822c..54061bd 100644 --- a/dice-lang/src/bjc/dicelang/CLIArgsParser.java +++ b/dice-lang/src/bjc/dicelang/CLIArgsParser.java @@ -47,97 +47,79 @@ public class CLIArgsParser { for (int i = 0; i < args.length; i++) { final String arg = args[i]; + /* + * @TODO 10/08/17 Ben Culkin :CLIArgRefactor + * Use whatever library gets added to BJC-Utils for + * this, and extend these to do more things. + */ switch (arg) { case "-d": case "--debug": if (!eng.toggleDebug()) { eng.toggleDebug(); } - break; - case "-nd": case "--no-debug": if (eng.toggleDebug()) { eng.toggleDebug(); } - break; - case "-po": case "--postfix": if (!eng.togglePostfix()) { eng.togglePostfix(); } - break; - case "-npo": case "--no-postfix": if (eng.togglePostfix()) { eng.togglePostfix(); } - break; - case "-pr": case "--prefix": if (!eng.togglePrefix()) { eng.togglePrefix(); } - break; - case "-npr": case "--no-prefix": if (eng.togglePrefix()) { eng.togglePrefix(); } - break; - case "-se": case "--stepeval": if (!eng.toggleStepEval()) { eng.toggleStepEval(); } - break; - case "-nse": case "--no-stepeval": if (eng.toggleStepEval()) { eng.toggleStepEval(); } - break; - case "-D": case "--define": i = simpleDefine(i, args, eng); - if (i == -1) { return false; } - break; - case "-df": case "--define-file": i = defineFile(i, args, eng); - if (i == -1) { return false; } - break; - case "-ctf": case "--compiler-tweak-file": - - /* - * @TODO not yet implemented - */ + /* @NOTE + * Not yet implemented. + */ default: Errors.inst.printError(EK_CLI_UNARG, arg); return false; @@ -147,8 +129,11 @@ public class CLIArgsParser { return true; } + /* Handle parsing a simple define. */ private static int simpleDefine(final int i, final String[] args, final DiceLangEngine eng) { + /* :DefineRefactor */ + if (i >= args.length - 1) { Errors.inst.printError(EK_CLI_MISARG, "define"); return -1; @@ -177,6 +162,7 @@ public class CLIArgsParser { return i + 2; } + /* Load a series of defines from a file. */ private static int defineFile(final int i, final String[] args, final DiceLangEngine eng) { if (i >= args.length - 1) { @@ -223,8 +209,7 @@ public class CLIArgsParser { private static Define parseDefine(final String ln) { final Define res = null; - // @TODO move this functionality from DiceLangConsole to some - // common ground where it can be used by both functions + /* :DefineRefactor */ return res; } } diff --git a/dice-lang/src/bjc/dicelang/CompilerTweaker.java b/dice-lang/src/bjc/dicelang/CompilerTweaker.java index bb5cbde..1154be4 100644 --- a/dice-lang/src/bjc/dicelang/CompilerTweaker.java +++ b/dice-lang/src/bjc/dicelang/CompilerTweaker.java @@ -2,15 +2,17 @@ package bjc.dicelang; import bjc.utils.parserutils.splitter.ConfigurableTokenSplitter; +/* + * @TODO 10/09/17 Ben Culkin :CompilerTweaking + * Expand this to allow tweaking more things about the compiler. + */ /** * Contains methods for customizing the DiceLang and SCL compilers. * * @author Ben Culkin */ public class CompilerTweaker { - /* - * Bits of the compiler necessary - */ + /* Bits of the compiler necessary */ private final DiceLangEngine eng; private final ConfigurableTokenSplitter opExpander; diff --git a/dice-lang/src/bjc/dicelang/Define.java b/dice-lang/src/bjc/dicelang/Define.java index f4a8871..5524477 100644 --- a/dice-lang/src/bjc/dicelang/Define.java +++ b/dice-lang/src/bjc/dicelang/Define.java @@ -12,6 +12,10 @@ import java.util.regex.PatternSyntaxException; import bjc.utils.data.CircularIterator; +/* + * @TODO 10/09/17 Ben Culkin :DefineRefactor + * Consider replacing this with the defines package from BJC-Utils. + */ /** * A regular expression based pre-processor define. * @@ -26,38 +30,34 @@ public class Define implements UnaryOperator<String>, Comparable<Define> { * */ public static enum Type { - /** - * Match on lines. - */ + /** Match on lines. */ LINE, - /** - * Match on tokens. - */ + /** Match on tokens. */ TOKEN } - /** - * The max amount of times to recur on expansions. - */ + /** The max amount of times to recur on expansions. */ public static int MAX_RECURS = 10; - /** - * The priority of this definition. - */ + /** The priority of this definition. */ public final int priority; - /** - * Whether or not this definition is in error. - */ + /** Whether or not this definition is in error. */ public final boolean inError; + /* Whether this define is recurring. */ private boolean doRecur; + /* Whether this define is applied multiple times per unit. */ private boolean subType; + /* The pattern that needs to match to apply this. */ private Pattern predicate; + /* The pattern to use to find everything to replace. */ private Pattern searcher; + /* The array of replacement strings to use. */ private Iterator<String> replacers; + /* The current replacement string to use. */ private String replacer; /** @@ -91,9 +91,7 @@ public class Define implements UnaryOperator<String>, Comparable<Define> { doRecur = recur; subType = isSub; - /* - * Only try to compile non-null predicates - */ + /* Only try to compile non-null predicates */ if (predicte != null) { try { predicate = Pattern.compile(predicte); @@ -104,9 +102,7 @@ public class Define implements UnaryOperator<String>, Comparable<Define> { } } - /* - * Compile the search pattern - */ + /* Compile the search pattern */ try { searcher = Pattern.compile(searchr); } catch (final PatternSyntaxException psex) { @@ -117,9 +113,7 @@ public class Define implements UnaryOperator<String>, Comparable<Define> { inError = false; - /* - * Check whether or not we do sub-replacements - */ + /* Check whether or not we do sub-replacements */ if (subType) { if (replacrs.iterator().hasNext()) { replacers = new CircularIterator<>(replacrs, isCircular); @@ -174,6 +168,7 @@ public class Define implements UnaryOperator<String>, Comparable<Define> { return strang; } + /* Apply a definition pass. */ private String doPass(final String tok) { final Matcher searcherMatcher = searcher.matcher(tok); diff --git a/dice-lang/src/bjc/dicelang/DiceLangConsole.java b/dice-lang/src/bjc/dicelang/DiceLangConsole.java index ebf9b59..296874f 100644 --- a/dice-lang/src/bjc/dicelang/DiceLangConsole.java +++ b/dice-lang/src/bjc/dicelang/DiceLangConsole.java @@ -43,9 +43,7 @@ public class DiceLangConsole { Terminal.setupTerminal(); } - /** - * Run the console. - */ + /** Run the console. */ public void run() { /* Set up console. */ try { @@ -55,6 +53,7 @@ public class DiceLangConsole { return; } + /* Print greeting. */ System.out.println("dice-lang v0.2"); String comm = null; @@ -67,6 +66,10 @@ public class DiceLangConsole { } /* Run commands. */ + /* @NOTE + * Should switch this to a do-while loop to reduce code + * duplication. + */ while (!comm.equals("quit") && !comm.equals("exit")) { if (comm.startsWith("pragma")) { /* Run pragmas. */ @@ -94,6 +97,7 @@ public class DiceLangConsole { commandNumber += 1; } + /* Read the next command. */ try { comm = read.readLine(String.format("(%d) dice-lang> ", commandNumber)); } catch (final IOException ioex) { @@ -103,6 +107,7 @@ public class DiceLangConsole { } } + /* Handle running pragmas. */ private boolean handlePragma(final String pragma) { if (eng.debugMode) { System.out.println("\tRaw pragma: " + pragma); @@ -119,32 +124,29 @@ public class DiceLangConsole { pragmaName = pragma.substring(0, firstIndex); } + /* Run pragmas. */ /* - * Run pragmas. + * @TODO 10/09/17 Ben Culkin :PragmaRefactor + * Swap to using something that makes it easier to add + * pragmas. */ switch (pragmaName) { case "debug": System.out.println("\tDebug mode is now " + eng.toggleDebug()); break; - case "postfix": System.out.println("\tPostfix mode is now " + eng.togglePostfix()); break; - case "prefix": System.out.println("\tPrefix mode is now " + eng.togglePrefix()); break; - case "stepeval": System.out.println("\tStepeval mode is now" + eng.toggleStepEval()); break; - case "define": return defineMode(pragma.substring(7)); - case "help": return helpMode(pragma.substring(5)); - default: Errors.inst.printError(EK_CONS_INVPRAG, pragma); return false; @@ -155,36 +157,30 @@ public class DiceLangConsole { /* Run a help mode. */ private static boolean helpMode(final String pragma) { + /* Get the help topic. */ 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 "stepeval": System.out.println("\tToggle stepeval mode. (Print out evaluation progress)"); break; - case "define": System.out.println("\tAdd a macro rewrite directive."); System.out.println("\tdefine <priority> <type> <recursion> <guard> <circular> <patterns>..."); break; - default: System.out.println("\tNo help available for pragma " + pragma); } - /* Help always works */ return true; } @@ -226,48 +222,51 @@ public class DiceLangConsole { return false; } + /* Get the priority and define type. */ final int priority = Integer.parseInt(defineText.substring(0, firstIndex)); final String defineType = defineText.substring(firstIndex + 1, secondIndex); Define.Type type; boolean subMode = false; + /* Parse the define type. */ switch (defineType) { case "line": type = Define.Type.LINE; break; - case "token": type = Define.Type.TOKEN; break; - case "subline": type = Define.Type.LINE; subMode = true; break; - case "subtoken": type = Define.Type.TOKEN; subMode = true; break; - default: Errors.inst.printError(EK_CONS_INVDEFINE, "(unknown type)"); return false; } + /* Do we want this to be a recursive pattern? */ final boolean doRecur = defineText.substring(secondIndex + 1, thirdIndex) .equalsIgnoreCase("true"); + /* Do we want this pattern to have a guard? */ final boolean hasGuard = defineText.substring(thirdIndex + 1, fourthIndex) .equalsIgnoreCase("true"); + /* Do we want this pattern to use circular replacements. */ final boolean isCircular = defineText.substring(thirdIndex + 1, fourthIndex) .equalsIgnoreCase("true"); + /* The part of the string that contains patterns. */ final String pats = defineText.substring(fifthIndex + 1).trim(); final Matcher patMatcher = slashPattern.matcher(pats); String guardPattern = null; if (hasGuard) { + /* Grab the guard pattern. */ if (!patMatcher.find()) { Errors.inst.printError(EK_CONS_INVDEFINE, "(no guard pattern)"); return false; @@ -277,6 +276,7 @@ public class DiceLangConsole { } if (!patMatcher.find()) { + /* Grab the search pattern. */ Errors.inst.printError(EK_CONS_INVDEFINE, "(no search pattern)"); return false; } @@ -285,6 +285,7 @@ public class DiceLangConsole { final List<String> replacePatterns = new LinkedList<>(); while (patMatcher.find()) { + /* Grab the replacer patterns. */ replacePatterns.add(patMatcher.group(1)); } @@ -296,6 +297,7 @@ public class DiceLangConsole { return false; } + /* Add the define to the proper place. */ if (type == Define.Type.LINE) { eng.addLineDefine(dfn); } else { diff --git a/dice-lang/src/bjc/dicelang/DiceLangEngine.java b/dice-lang/src/bjc/dicelang/DiceLangEngine.java index 7b65f77..71e5ee8 100644 --- a/dice-lang/src/bjc/dicelang/DiceLangEngine.java +++ b/dice-lang/src/bjc/dicelang/DiceLangEngine.java @@ -34,6 +34,7 @@ import bjc.utils.parserutils.splitter.ConfigurableTokenSplitter; * @author Ben Culkin */ public class DiceLangEngine { + /* Logger. */ private static final Logger LOG = Logger.getLogger(DiceLangEngine.class.getName()); /* @@ -120,22 +121,21 @@ public class DiceLangEngine { nextLiteral = 1; /* Initial mode settings. */ - debugMode = true; + debugMode = true; postfixMode = false; - prefixMode = false; - stepEval = false; + prefixMode = false; + stepEval = false; /* Create components. */ + shunt = new Shunter(); + parsr = new Parser(); + streamEng = new StreamEngine(this); - shunt = new Shunter(); - tokenzer = new Tokenizer(this); - parsr = new Parser(); - eval = new Evaluator(this); + tokenzer = new Tokenizer(this); + eval = new Evaluator(this); } - /** - * Sort defns by priority. - */ + /** Sort defns by priority. */ public void sortDefns() { lineDefns.sort(null); tokenDefns.sort(null); @@ -225,6 +225,10 @@ public class DiceLangEngine { */ public boolean runCommand(final String command) { /* Preprocess the command into tokens */ + /* @NOTE + * Instead of strings, this should maybe use a RawToken + * class or something. + */ final IList<String> preprocessedTokens = preprocessCommand(command); if (preprocessedTokens == null) { @@ -260,6 +264,10 @@ public class DiceLangEngine { /* Apply token defns */ for (final Define dfn : tokenDefns.toIterable()) { + /* @NOTE + * What happens with a define that produces + * multiple tokens from one token? + */ newTok = dfn.apply(newTok); } @@ -273,9 +281,8 @@ public class DiceLangEngine { if (tk == null) { /* Ignore blank tokens */ continue; - } else if (tk == Token.NIL_TOKEN) + } else if (tk == Token.NIL_TOKEN) { /* Fail on bad tokens */ - { return null; } else { lexedTokens.add(tk); @@ -305,6 +312,7 @@ public class DiceLangEngine { System.out.print(msg); } + /* Only shunt if we're not in a special mode. */ if (!postfixMode && !prefixMode) { /* Shunt the tokens */ shuntedTokens = new FunctionalList<>(); @@ -315,6 +323,11 @@ public class DiceLangEngine { } } else if (prefixMode) { /* Reverse directional tokens */ + /* + * @NOTE + * Merge these two operations into one iteration + * over the list? + */ preparedTokens.reverse(); shuntedTokens = preparedTokens.map(this::reverseToken); } @@ -328,8 +341,8 @@ public class DiceLangEngine { /* Expand token groups */ final IList<Token> readyTokens = shuntedTokens.flatMap(tk -> { if (tk.type == Token.Type.TOKGROUP || - tk.type == Token.Type.TAGOP || - tk.type == Token.Type.TAGOPR ) { + tk.type == Token.Type.TAGOP || + tk.type == Token.Type.TAGOPR ) { LOG.finer(String.format("Expanding token group to: %s\n", tk.tokenValues.toString())); return tk.tokenValues; } else { @@ -350,28 +363,22 @@ public class DiceLangEngine { /* * Reverse orientation-sensitive tokens. * - * These are mostly just things like (, {, and [ + * These are things like (, {, and [ */ private Token reverseToken(final Token tk) { switch (tk.type) { case OBRACE: return new Token(CBRACE, tk.intValue); - case OPAREN: return new Token(CPAREN, tk.intValue); - case OBRACKET: return new Token(CBRACKET, tk.intValue); - case CBRACE: return new Token(OBRACE, tk.intValue); - case CPAREN: return new Token(OPAREN, tk.intValue); - case CBRACKET: return new Token(OBRACKET, tk.intValue); - default: return tk; } @@ -386,13 +393,13 @@ public class DiceLangEngine { /* Run the tokens through the stream engine */ final IList<String> streamToks = new FunctionalList<>(); - final boolean succ = streamEng.doStreams(command.split(" "), streamToks); + final boolean succ = streamEng.doStreams(command.split(" "), + streamToks); if (!succ) { return null; } - /* Apply line defns */ String newComm = ListUtils.collapseTokens(streamToks, " "); if (debugMode) { @@ -401,6 +408,7 @@ public class DiceLangEngine { System.out.print(msg); } + /* Apply line defns */ for (final Define dfn : lineDefns.toIterable()) { newComm = dfn.apply(newComm); } @@ -412,7 +420,7 @@ public class DiceLangEngine { } /* Remove string literals. */ - final List<String> destringedParts = TokenUtils.removeDQuotedStrings(newComm); + final List<String> destringedParts = TokenUtils.removeDQuotedStrings(newComm); final StringBuffer destringedCommand = new StringBuffer(); for (final String part : destringedParts) { @@ -420,7 +428,7 @@ public class DiceLangEngine { if (part.startsWith("\"") && part.endsWith("\"")) { /* Get the actual string. */ final String litName = "stringLiteral" + nextLiteral; - final String litVal = part.substring(1, part.length() - 1); + final String litVal = part.substring(1, part.length() - 1); /* * Insert the string with its escape sequences @@ -458,7 +466,7 @@ public class DiceLangEngine { } /* Split the command into tokens */ - final String strang = destringedCommand.toString(); + final String strang = destringedCommand.toString(); IList<String> tokens = FunctionalStringTokenizer.fromString(strang).toList(); /* Temporarily remove non-expanding tokens */ @@ -522,7 +530,11 @@ public class DiceLangEngine { } if (debugMode && stepEval) { - /* @TODO fix stepEval */ + /* + * @NOTE + * This is broken until stepwise top-down + * tree transforms are fixed. + */ int step = 1; /* Evaluate it step by step */ @@ -556,9 +568,7 @@ public class DiceLangEngine { System.out.printf(")"); } - /* - * Advance a step - */ + /* Advance a step */ System.out.println(); step += 1; } @@ -606,9 +616,7 @@ public class DiceLangEngine { curBracedTokens = new FunctionalList<>(); } else if (tk.type == Token.Type.CBRACE && tk.intValue == 2) { - /* - * Close a preshunt group. - */ + /* Close a preshunt group. */ if (curBraceCount == 0) { /* * Error if there couldn't have been an @@ -622,9 +630,7 @@ public class DiceLangEngine { final IList<Token> preshuntTokens = new FunctionalList<>(); - /* - * Shunt preshunt group. - */ + /* Shunt preshunt group. */ final boolean success = shunt.shuntTokens(curBracedTokens, preshuntTokens); if (debugMode) { @@ -647,7 +653,7 @@ public class DiceLangEngine { } else { /* * Add the preshunt group to the token - * stream.. + * stream. */ preparedTokens.add(new Token(Token.Type.TOKGROUP, preshuntTokens)); } @@ -665,9 +671,7 @@ public class DiceLangEngine { } if (curBraceCount > 0) { - /* - * There was an unclosed group. - */ + /* There was an unclosed group. */ Errors.inst.printError(EK_ENG_NOCLOSING); return false; } @@ -675,10 +679,16 @@ public class DiceLangEngine { return true; } + /* Get a string literal from the string literal table. */ String getStringLiteral(final int key) { return stringLits.get(key); } + /* Add a string literal to the string literal table. */ + /* @NOTE + * The string literal table should be abstracted into some kind of + * auto-numbered map thing. + */ void addStringLiteral(final int key, final String val) { stringLits.put(key, val); } diff --git a/dice-lang/src/bjc/dicelang/Evaluator.java b/dice-lang/src/bjc/dicelang/Evaluator.java index 0a583c8..10f7651 100644 --- a/dice-lang/src/bjc/dicelang/Evaluator.java +++ b/dice-lang/src/bjc/dicelang/Evaluator.java @@ -1,28 +1,5 @@ package bjc.dicelang; -import static bjc.dicelang.Errors.ErrorKey.EK_EVAL_DIVDICE; -import static bjc.dicelang.Errors.ErrorKey.EK_EVAL_DIVZERO; -import static bjc.dicelang.Errors.ErrorKey.EK_EVAL_INVBIN; -import static bjc.dicelang.Errors.ErrorKey.EK_EVAL_INVDCREATE; -import static bjc.dicelang.Errors.ErrorKey.EK_EVAL_INVDGROUP; -import static bjc.dicelang.Errors.ErrorKey.EK_EVAL_INVDICE; -import static bjc.dicelang.Errors.ErrorKey.EK_EVAL_INVNODE; -import static bjc.dicelang.Errors.ErrorKey.EK_EVAL_INVSTRING; -import static bjc.dicelang.Errors.ErrorKey.EK_EVAL_INVUNARY; -import static bjc.dicelang.Errors.ErrorKey.EK_EVAL_MISMATH; -import static bjc.dicelang.Errors.ErrorKey.EK_EVAL_STRINGMATH; -import static bjc.dicelang.Errors.ErrorKey.EK_EVAL_UNBIN; -import static bjc.dicelang.Errors.ErrorKey.EK_EVAL_UNDICE; -import static bjc.dicelang.Errors.ErrorKey.EK_EVAL_UNMATH; -import static bjc.dicelang.Errors.ErrorKey.EK_EVAL_UNSTRING; -import static bjc.dicelang.Errors.ErrorKey.EK_EVAL_UNTOK; -import static bjc.dicelang.Errors.ErrorKey.EK_EVAL_UNUNARY; -import static bjc.dicelang.EvaluatorResult.Type.DICE; -import static bjc.dicelang.EvaluatorResult.Type.FAILURE; -import static bjc.dicelang.EvaluatorResult.Type.FLOAT; -import static bjc.dicelang.EvaluatorResult.Type.INT; -import static bjc.dicelang.EvaluatorResult.Type.STRING; - import java.util.Deque; import java.util.Iterator; import java.util.LinkedList; @@ -40,6 +17,18 @@ import bjc.utils.data.TopDownTransformIterator; import bjc.utils.data.TopDownTransformResult; import bjc.utils.data.Tree; +import static bjc.dicelang.Errors.ErrorKey.*; +import static bjc.dicelang.EvaluatorResult.Type.DICE; +import static bjc.dicelang.EvaluatorResult.Type.FAILURE; +import static bjc.dicelang.EvaluatorResult.Type.FLOAT; +import static bjc.dicelang.EvaluatorResult.Type.INT; +import static bjc.dicelang.EvaluatorResult.Type.STRING; + + +/* @TODO 10/09/17 Ben Culkin :EvaluatorSplit + * Type/sanity checking should be moved into a seperate stage, not part of + * evaluation. + */ /** * Evaluate DiceLang ASTs * @@ -47,19 +36,26 @@ import bjc.utils.data.Tree; * */ public class Evaluator { + /* The steps of type coercion. */ private static enum CoerceSteps { INTEGER, DOUBLE; } + /* The context during iteration. */ private static class Context { public Consumer<Iterator<ITree<Node>>> thunk; public boolean isDebug; public Context() { + /* Empty block. */ } } + /* @TODO 10/09/17 Ben Culkin :NodeFAIL + * These methods should be moved to Node. + */ + /* Create a failing node. */ private static Node FAIL() { return new Node(Node.Type.RESULT, new EvaluatorResult(FAILURE)); } @@ -68,7 +64,6 @@ public class Evaluator { return new Node(Node.Type.RESULT, new EvaluatorResult(FAILURE, orig)); } - @SuppressWarnings("unused") private static Node FAIL(final Node orig) { return new Node(Node.Type.RESULT, new EvaluatorResult(FAILURE, orig)); } @@ -78,6 +73,7 @@ public class Evaluator { return new Node(Node.Type.RESULT, eres); } + /* The engine we are connected to. */ private final DiceLangEngine eng; /** @@ -103,25 +99,26 @@ public class Evaluator { ctx.isDebug = false; ctx.thunk = itr -> { - /** + /* * Deliberately finish the iterator, but ignore results. * It's only for stepwise evaluation, but we don't know - * if stepping the iterator causes something to happen. - * + * if stepping the iterator has side effects. */ while (itr.hasNext()) { itr.next(); } }; - final ITree<Node> res = comm.topDownTransform(this::pickEvaluationType, - node -> this.evaluateNode(node, ctx)); + /* The result. */ + final ITree<Node> res = comm.topDownTransform( + this::pickEvaluationType, + node -> this.evaluateNode(node, ctx)); return res.getHead().resultVal; } - // FIXME Something's broken with step evaluation - @SuppressWarnings("javadoc") + /* @NOTE + * This is broken until stepwise top-down transforms are fixed. */ public Iterator<ITree<Node>> stepDebug(final ITree<Node> comm) { final Context ctx = new Context(); @@ -140,8 +137,8 @@ public class Evaluator { case UNARYOP: switch (nd.operatorType) { case COERCE: + /* Coerce does special things to the tree. */ return TopDownTransformResult.RTRANSFORM; - default: return TopDownTransformResult.PUSHDOWN; } @@ -156,19 +153,14 @@ public class Evaluator { switch (ast.getHead().type) { case UNARYOP: return evaluateUnaryOp(ast, ctx); - case BINOP: return evaluateBinaryOp(ast, ctx); - case TOKREF: return evaluateTokenRef(ast.getHead().tokenVal, ctx); - case ROOT: return ast.getChild(ast.getChildrenCount() - 1); - case RESULT: return ast; - default: Errors.inst.printError(EK_EVAL_INVNODE, ast.getHead().type.toString()); return new Tree<>(FAIL(ast)); @@ -177,31 +169,45 @@ public class Evaluator { /* Evaluate a unary operator. */ private ITree<Node> evaluateUnaryOp(final ITree<Node> ast, final Context ctx) { + /* Unary operators only take one operand. */ if (ast.getChildrenCount() != 1) { Errors.inst.printError(EK_EVAL_UNUNARY, Integer.toString(ast.getChildrenCount())); return new Tree<>(FAIL(ast)); } switch (ast.getHead().operatorType) { - /* @TODO move coercing to its own class */ + /* + * @TODO 10/09/17 Ben Culkin :CoerceRefactor :EvaluatorSplit + * Coercing should be moved to its own class, or at the + * very least its own method. When the evaluator splits, + * this node type'll be handled exclusively by the + * type-checker. + * + * Coerce also needs to be able to coerce things to + * dice and ratios (whenever they get added). + */ case COERCE: final ITree<Node> toCoerce = ast.getChild(0); final ITree<Node> retVal = new Tree<>(toCoerce.getHead()); final Deque<ITree<Node>> children = new LinkedList<>(); + /* The current type we are coercing to. */ CoerceSteps curLevel = CoerceSteps.INTEGER; for (int i = 0; i < toCoerce.getChildrenCount(); i++) { final ITree<Node> child = toCoerce.getChild(i); ITree<Node> nChild = null; + /* Tell our thunk we processed a node. */ if (ctx.isDebug) { + /* Evaluate each step of the child. */ final Iterator<ITree<Node>> nd = stepDebug(child); for (; nd.hasNext(); nChild = nd.next()) { ctx.thunk.accept(new SingleIterator<>(child)); } } else { + /* Evaluate the child. */ nChild = new Tree<>(new Node(Node.Type.RESULT, evaluate(child))); ctx.thunk.accept(new SingleIterator<>(nChild)); @@ -215,6 +221,7 @@ public class Evaluator { final Node childNode = nChild.getHead(); final EvaluatorResult res = childNode.resultVal; + /* Move up to coercing to a float. */ if (res.type == FLOAT) { curLevel = CoerceSteps.DOUBLE; } @@ -228,14 +235,12 @@ public class Evaluator { switch (res.type) { case INT: + /* Coerce ints to doubles if we need to. */ if (curLevel == CoerceSteps.DOUBLE) { nd.resultVal = new EvaluatorResult(FLOAT, (double) res.intVal); } - default: - /* - * Do nothing - */ + /* Do nothing */ break; } @@ -243,7 +248,6 @@ public class Evaluator { } return retVal; - case DICESCALAR: final EvaluatorResult opr = ast.getChild(0).getHead().resultVal; @@ -251,9 +255,8 @@ public class Evaluator { Errors.inst.printError(EK_EVAL_INVDCREATE, opr.type.toString()); } - return new Tree<>(new Node(Node.Type.RESULT, - new EvaluatorResult(DICE, new ScalarDie(opr.intVal)))); - + final EvaluatorResult sres = new EvaluatorResult(DICE, new ScalarDie(opr.intVal)); + return new Tree<>(new Node(Node.Type.RESULT, sres)); case DICEFUDGE: final EvaluatorResult oprn = ast.getChild(0).getHead().resultVal; @@ -261,57 +264,56 @@ public class Evaluator { Errors.inst.printError(EK_EVAL_INVDCREATE, oprn.type.toString()); } - return new Tree<>(new Node(Node.Type.RESULT, - new EvaluatorResult(DICE, new FudgeDie(oprn.intVal)))); - + final EvaluatorResult fres = new EvaluatorResult(DICE, new ScalarDie(oprn.intVal)); + return new Tree<>(new Node(Node.Type.RESULT, fres)); default: Errors.inst.printError(EK_EVAL_INVUNARY, ast.getHead().operatorType.toString()); return new Tree<>(FAIL(ast)); } } + /* Evaluate a binary operator. */ private static ITree<Node> evaluateBinaryOp(final ITree<Node> ast, final Context ctx) { final Token.Type binOp = ast.getHead().operatorType; + /* Binary operators always have two children. */ if (ast.getChildrenCount() != 2) { Errors.inst.printError(EK_EVAL_INVBIN, Integer.toString(ast.getChildrenCount()), - ast.toString()); + ast.toString()); return new Tree<>(FAIL(ast)); } - final ITree<Node> left = ast.getChild(0); + final ITree<Node> left = ast.getChild(0); final ITree<Node> right = ast.getChild(1); + final EvaluatorResult leftRes = left.getHead().resultVal; + final EvaluatorResult rightRes = right.getHead().resultVal; + switch (binOp) { case ADD: case SUBTRACT: case MULTIPLY: case DIVIDE: case IDIVIDE: - return evaluateMathBinary(binOp, left.getHead().resultVal, right.getHead().resultVal, - ctx); - + return evaluateMathBinary(binOp, leftRes, rightRes, ctx); case DICEGROUP: case DICECONCAT: case DICELIST: - return evaluateDiceBinary(binOp, left.getHead().resultVal, right.getHead().resultVal, - ctx); - + return evaluateDiceBinary(binOp, leftRes, rightRes, ctx); case STRCAT: case STRREP: - return evaluateStringBinary(binOp, left.getHead().resultVal, right.getHead().resultVal, - ctx); - + return evaluateStringBinary(binOp, leftRes, rightRes, ctx); default: Errors.inst.printError(EK_EVAL_UNBIN, binOp.toString()); return new Tree<>(FAIL(ast)); } } + /* Evaluate a binary operator on strings. */ private static ITree<Node> evaluateStringBinary(final Token.Type op, - final EvaluatorResult left, - final EvaluatorResult right, final Context ctx) { + final EvaluatorResult left, + final EvaluatorResult right, final Context ctx) { if (left.type != STRING) { Errors.inst.printError(EK_EVAL_INVSTRING, left.type.toString()); return new Tree<>(FAIL(left)); @@ -327,9 +329,9 @@ public class Evaluator { } final String strung = right.stringVal; - return new Tree<>(new Node(Node.Type.RESULT, new EvaluatorResult(STRING, - strang + strung))); + final EvaluatorResult cres = new EvaluatorResult(STRING, strang + strung); + return new Tree<>(new Node(Node.Type.RESULT, cres)); case STRREP: if (right.type != INT) { Errors.inst.printError(EK_EVAL_INVSTRING, right.type.toString()); @@ -344,27 +346,35 @@ public class Evaluator { } return new Tree<>(new Node(Node.Type.RESULT, new EvaluatorResult(STRING, res))); - default: Errors.inst.printError(EK_EVAL_UNSTRING, op.toString()); return new Tree<>(FAIL()); } } + /* Evaluate dice binary operators. */ private static ITree<Node> evaluateDiceBinary(final Token.Type op, - final EvaluatorResult left, - final EvaluatorResult right, final Context ctx) { + final EvaluatorResult left, + final EvaluatorResult right, final Context ctx) { EvaluatorResult res = null; switch (op) { + /* + * @TODO 10/09/17 Ben Culkin :DiceSimplify + * Figure out some way to simplify this sort of + * thing. + */ case DICEGROUP: if (left.type == DICE && !left.diceVal.isList) { if (right.type == DICE && !right.diceVal.isList) { - res = new EvaluatorResult(DICE, - new SimpleDie(left.diceVal.scalar, right.diceVal.scalar)); + Die simple = new SimpleDie( + left.diceVal.scalar, + right.diceVal.scalar); + + res = new EvaluatorResult(DICE, simple); } else if (right.type == INT) { res = new EvaluatorResult(DICE, - new SimpleDie(left.diceVal.scalar, right.intVal)); + new SimpleDie(left.diceVal.scalar, right.intVal)); } else { Errors.inst.printError(EK_EVAL_INVDGROUP, right.type.toString()); return new Tree<>(FAIL(right)); @@ -372,7 +382,7 @@ public class Evaluator { } else if (left.type == INT) { if (right.type == DICE && !right.diceVal.isList) { res = new EvaluatorResult(DICE, - new SimpleDie(left.intVal, right.diceVal.scalar)); + new SimpleDie(left.intVal, right.diceVal.scalar)); } else if (right.type == INT) { res = new EvaluatorResult(DICE, new SimpleDie(left.intVal, right.intVal)); } else { @@ -393,7 +403,7 @@ public class Evaluator { return new Tree<>(FAIL(right)); } else { res = new EvaluatorResult(DICE, - new CompoundDie(left.diceVal.scalar, right.diceVal.scalar)); + new CompoundDie(left.diceVal.scalar, right.diceVal.scalar)); } break; @@ -407,7 +417,7 @@ public class Evaluator { return new Tree<>(FAIL(right)); } else { res = new EvaluatorResult(DICE, - new SimpleDieList(left.diceVal.scalar, right.diceVal.scalar)); + new SimpleDieList(left.diceVal.scalar, right.diceVal.scalar)); } break; @@ -420,9 +430,10 @@ public class Evaluator { return new Tree<>(new Node(Node.Type.RESULT, res)); } + /* Evaluate a binary math operator. */ private static ITree<Node> evaluateMathBinary(final Token.Type op, - final EvaluatorResult left, - final EvaluatorResult right, final Context ctx) { + final EvaluatorResult left, + final EvaluatorResult right, final Context ctx) { if (left.type == STRING || right.type == STRING) { Errors.inst.printError(EK_EVAL_STRINGMATH); return new Tree<>(FAIL()); @@ -464,7 +475,7 @@ public class Evaluator { } res = new EvaluatorResult(DICE, new MathDie(MathDie.MathOp.ADD, left.diceVal.scalar, - right.diceVal.scalar)); + right.diceVal.scalar)); } else { res = new EvaluatorResult(FLOAT, left.floatVal + right.floatVal); } @@ -484,7 +495,7 @@ public class Evaluator { } res = new EvaluatorResult(DICE, new MathDie(MathDie.MathOp.SUBTRACT, - left.diceVal.scalar, right.diceVal.scalar)); + left.diceVal.scalar, right.diceVal.scalar)); } else { res = new EvaluatorResult(FLOAT, left.floatVal - right.floatVal); } @@ -504,7 +515,7 @@ public class Evaluator { } res = new EvaluatorResult(DICE, new MathDie(MathDie.MathOp.MULTIPLY, - left.diceVal.scalar, right.diceVal.scalar)); + left.diceVal.scalar, right.diceVal.scalar)); } else { res = new EvaluatorResult(FLOAT, left.floatVal * right.floatVal); } @@ -563,6 +574,7 @@ public class Evaluator { return new Tree<>(new Node(Node.Type.RESULT, res)); } + /* Evaluate a token reference. */ private ITree<Node> evaluateTokenRef(final Token tk, final Context ctx) { EvaluatorResult res = null; @@ -570,19 +582,15 @@ public class Evaluator { case INT_LIT: res = new EvaluatorResult(INT, tk.intValue); break; - case FLOAT_LIT: res = new EvaluatorResult(FLOAT, tk.floatValue); break; - case DICE_LIT: res = new EvaluatorResult(DICE, tk.diceValue); break; - case STRING_LIT: res = new EvaluatorResult(STRING, eng.getStringLiteral((int) tk.intValue)); break; - default: Errors.inst.printError(EK_EVAL_UNTOK, tk.type.toString()); res = new EvaluatorResult(FAILURE); diff --git a/dice-lang/src/bjc/dicelang/EvaluatorResult.java b/dice-lang/src/bjc/dicelang/EvaluatorResult.java index 75bbb3f..3ceee9b 100644 --- a/dice-lang/src/bjc/dicelang/EvaluatorResult.java +++ b/dice-lang/src/bjc/dicelang/EvaluatorResult.java @@ -6,6 +6,10 @@ import bjc.dicelang.dice.DieList; import bjc.utils.data.ITree; import bjc.utils.data.Tree; +/* + * @TODO 10/09/17 Ben Culkin :EvalResultReorg + * Again, split it into seperate classes based off of the type. + */ /** * The result from the evaluator. * @@ -213,4 +217,4 @@ public class EvaluatorResult { return "Unknown result type " + type.toString(); } } -}
\ No newline at end of file +} diff --git a/dice-lang/src/bjc/dicelang/Node.java b/dice-lang/src/bjc/dicelang/Node.java index 0a57c54..4238cfc 100644 --- a/dice-lang/src/bjc/dicelang/Node.java +++ b/dice-lang/src/bjc/dicelang/Node.java @@ -1,6 +1,15 @@ package bjc.dicelang; -@SuppressWarnings("javadoc") +/* + * @TODO 10/09/17 Ben Culkin :NodeReorg + * Same thing, different class. Split into subclasses based off of the type + * values. + */ +/** + * Represents a node in the AST. + * + * @author Ben Culkin + */ public class Node { public static enum Type { ROOT, TOKREF, UNARYOP, BINOP, GROUP, OGROUP, RESULT diff --git a/dice-lang/src/bjc/dicelang/Parser.java b/dice-lang/src/bjc/dicelang/Parser.java index ddeccd2..0861e96 100644 --- a/dice-lang/src/bjc/dicelang/Parser.java +++ b/dice-lang/src/bjc/dicelang/Parser.java @@ -27,9 +27,7 @@ import bjc.utils.funcdata.IList; * */ public class Parser { - /** - * Create a new parser. - */ + /** Create a new parser. */ public Parser() { } @@ -53,11 +51,13 @@ public class Parser { switch (tk.type) { case OBRACKET: case OBRACE: + /* Parse opening delims. */ working.push(new Tree<>(new Node(OGROUP, tk))); - break; + break; case CBRACKET: case CBRACE: + /* Parse closing delims. */ final boolean sc = parseClosingGrouper(working, tk); if (!sc) { @@ -65,7 +65,6 @@ public class Parser { } break; - case MULTIPLY: case DIVIDE: case IDIVIDE: @@ -76,6 +75,7 @@ public class Parser { case STRREP: case LET: case BIND: + /* Parse binary operator. */ if (working.size() < 2) { Errors.inst.printError(EK_PARSE_BINARY); return false; @@ -83,16 +83,15 @@ public class Parser { handleBinaryNode(working, tk); break; - case ADD: case SUBTRACT: + /* Handle binary/unary operators. */ if (working.size() == 0) { Errors.inst.printError(EK_PARSE_UNOPERAND, tk.toString()); return false; } else if (working.size() == 1) { final ITree<Node> operand = working.pop(); - - final ITree<Node> opNode = new Tree<>(new Node(UNARYOP, tk.type)); + final ITree<Node> opNode = new Tree<>(new Node(UNARYOP, tk.type)); opNode.addChild(operand); @@ -102,15 +101,15 @@ public class Parser { } break; - case COERCE: case DICESCALAR: case DICEFUDGE: + /* Handle unary operators. */ if (working.size() == 0) { Errors.inst.printError(EK_PARSE_UNOPERAND, tk.toString()); } else { final ITree<Node> operand = working.pop(); - final ITree<Node> opNode = new Tree<>(new Node(UNARYOP, tk.type)); + final ITree<Node> opNode = new Tree<>(new Node(UNARYOP, tk.type)); opNode.addChild(operand); @@ -118,21 +117,24 @@ public class Parser { } break; - case INT_LIT: case FLOAT_LIT: case STRING_LIT: case VREF: case DICE_LIT: + /* Handle literals. */ working.push(new Tree<>(new Node(TOKREF, tk))); break; - default: Errors.inst.printError(EK_PARSE_INVTOKEN, tk.type.toString()); return false; } } + /* + * Collect the remaining nodes as the roots of the trees in the + * AST forest. + */ for (final ITree<Node> ast : working) { results.add(ast); } @@ -140,9 +142,10 @@ public class Parser { return true; } + /* Handle a binary operator. */ private static void handleBinaryNode(final Deque<ITree<Node>> working, final Token tk) { final ITree<Node> right = working.pop(); - final ITree<Node> left = working.pop(); + final ITree<Node> left = working.pop(); final ITree<Node> opNode = new Tree<>(new Node(BINOP, tk.type)); @@ -152,6 +155,7 @@ public class Parser { working.push(opNode); } + /* Parse a closing delimiter. */ private static boolean parseClosingGrouper(final Deque<ITree<Node>> working, final Token tk) { if (working.size() == 0) { @@ -165,11 +169,9 @@ public class Parser { case CBRACE: groupNode = new Tree<>(new Node(GROUP, Node.GroupType.CODE)); break; - case CBRACKET: groupNode = new Tree<>(new Node(GROUP, Node.GroupType.ARRAY)); break; - default: Errors.inst.printError(EK_PARSE_UNCLOSE, tk.type.toString()); return false; @@ -205,7 +207,7 @@ public class Parser { childs.push(working.pop()); } - // Discard opener + /* Discard opener */ working.pop(); for (final ITree<Node> child : childs) { diff --git a/dice-lang/src/bjc/dicelang/Shunter.java b/dice-lang/src/bjc/dicelang/Shunter.java index 407ca5a..9d63b41 100644 --- a/dice-lang/src/bjc/dicelang/Shunter.java +++ b/dice-lang/src/bjc/dicelang/Shunter.java @@ -1,32 +1,5 @@ package bjc.dicelang; -import static bjc.dicelang.Errors.ErrorKey.EK_SHUNT_INVSEP; -import static bjc.dicelang.Errors.ErrorKey.EK_SHUNT_NOGROUP; -import static bjc.dicelang.Errors.ErrorKey.EK_SHUNT_NOTADJ; -import static bjc.dicelang.Errors.ErrorKey.EK_SHUNT_NOTADV; -import static bjc.dicelang.Errors.ErrorKey.EK_SHUNT_NOTASSOC; -import static bjc.dicelang.Token.Type.ADD; -import static bjc.dicelang.Token.Type.BIND; -import static bjc.dicelang.Token.Type.CBRACE; -import static bjc.dicelang.Token.Type.COERCE; -import static bjc.dicelang.Token.Type.CPAREN; -import static bjc.dicelang.Token.Type.DICECONCAT; -import static bjc.dicelang.Token.Type.DICEGROUP; -import static bjc.dicelang.Token.Type.DICELIST; -import static bjc.dicelang.Token.Type.DIVIDE; -import static bjc.dicelang.Token.Type.GROUPSEP; -import static bjc.dicelang.Token.Type.IDIVIDE; -import static bjc.dicelang.Token.Type.LET; -import static bjc.dicelang.Token.Type.MULTIPLY; -import static bjc.dicelang.Token.Type.OBRACE; -import static bjc.dicelang.Token.Type.OPAREN; -import static bjc.dicelang.Token.Type.STRCAT; -import static bjc.dicelang.Token.Type.STRREP; -import static bjc.dicelang.Token.Type.SUBTRACT; -import static bjc.dicelang.Token.Type.TAGOP; -import static bjc.dicelang.Token.Type.TAGOPR; -import static bjc.dicelang.Token.Type.TOKGROUP; - import java.util.Deque; import java.util.HashSet; import java.util.LinkedList; @@ -37,6 +10,13 @@ import bjc.utils.funcdata.FunctionalMap; import bjc.utils.funcdata.IList; import bjc.utils.funcdata.IMap; +import static bjc.dicelang.Errors.ErrorKey.EK_SHUNT_INVSEP; +import static bjc.dicelang.Errors.ErrorKey.EK_SHUNT_NOGROUP; +import static bjc.dicelang.Errors.ErrorKey.EK_SHUNT_NOTADJ; +import static bjc.dicelang.Errors.ErrorKey.EK_SHUNT_NOTADV; +import static bjc.dicelang.Errors.ErrorKey.EK_SHUNT_NOTASSOC; +import static bjc.dicelang.Token.Type.*; + /** * Shunt a set of infix tokens to postfix tokens. * @@ -71,56 +51,56 @@ public class Shunter { */ Set<Token.Type> unaryGerunds; - /** - * Precedence for math operators. - */ - public final int MATH_PREC = 30; - /** - * Precedence for dice operators. - */ - public final int DICE_PREC = 20; - /** - * Precedence for string operators. - */ - public final int STR_PREC = 10; - /** - * Precedence for expression operators. - */ - public final int EXPR_PREC = 0; + /** Precedence for math operators. */ + public final int MATH_PREC = 30; + /** Precedence for dice operators. */ + public final int DICE_PREC = 20; + /** Precedence for string operators. */ + public final int STR_PREC = 10; + /** Precedence for expression operators. */ + public final int EXPR_PREC = 0; - /** - * Create a new shunter. - */ + /** Create a new shunter. */ public Shunter() { + /* Create op map. */ ops = new FunctionalMap<>(); + /* Create association maps. */ rightAssoc = new HashSet<>(); - notAssoc = new HashSet<>(); + notAssoc = new HashSet<>(); + /* Create unary maps. */ unaryAdjectives = new HashSet<>(); - unaryAdverbs = new HashSet<>(); - unaryGerunds = new HashSet<>(); + unaryAdverbs = new HashSet<>(); + unaryGerunds = new HashSet<>(); + /* Set up unary adverbs. */ unaryAdverbs.add(COERCE); - ops.put(ADD, 0 + MATH_PREC); + /* Setup operators. */ + /* Math operators. */ + ops.put(ADD, 0 + MATH_PREC); ops.put(SUBTRACT, 0 + MATH_PREC); ops.put(MULTIPLY, 1 + MATH_PREC); - ops.put(IDIVIDE, 1 + MATH_PREC); - ops.put(DIVIDE, 1 + MATH_PREC); + ops.put(IDIVIDE, 1 + MATH_PREC); + ops.put(DIVIDE, 1 + MATH_PREC); + /* Dice operators. */ ops.put(DICEGROUP, 0 + DICE_PREC); ops.put(DICECONCAT, 1 + DICE_PREC); ops.put(DICELIST, 2 + DICE_PREC); + /* String operators. */ ops.put(STRCAT, 0 + STR_PREC); ops.put(STRREP, 1 + STR_PREC); + /* Expression operators. */ ops.put(LET, 0 + EXPR_PREC); + ops.put(BIND, 1 + EXPR_PREC); } @@ -136,16 +116,20 @@ public class Shunter { * @return Whether or not the shunt succeeded. */ public boolean shuntTokens(final IList<Token> tks, final IList<Token> returned) { - final Deque<Token> opStack = new LinkedList<>(); + /* Operator stack for normal and unary operators. */ + final Deque<Token> opStack = new LinkedList<>(); final Deque<Token> unaryOps = new LinkedList<>(); + /* Currently returned lists. */ final Deque<Token> currReturned = new LinkedList<>(); + /* Tokens to feed ahead of the current one. */ final Deque<Token> feed = new LinkedList<>(); for (final Token tk : tks.toIterable()) { boolean succ; + /* Drain the feed queue. */ while (feed.size() != 0) { succ = shuntToken(feed.poll(), opStack, unaryOps, currReturned, feed); @@ -161,11 +145,12 @@ public class Shunter { } } - // Flush leftover operators + /* Flush leftover operators. */ while (!opStack.isEmpty()) { currReturned.addLast(opStack.pop()); } + /* Add the tokens to the returned list. */ for (final Token tk : currReturned) { returned.add(tk); } @@ -173,9 +158,11 @@ public class Shunter { return true; } + /* Shunt a token. */ private boolean shuntToken(final Token tk, final Deque<Token> opStack, final Deque<Token> unaryStack, final Deque<Token> currReturned, final Deque<Token> feed) { + /* Handle unary operators. */ if (unaryStack.size() != 0) { if (isUnary(tk)) { unaryStack.add(tk); @@ -187,6 +174,10 @@ public class Shunter { final Token.Type unaryType = unaryOp.type; if (unaryAdjectives.contains(unaryType)) { + /* + * Handle unary adjectives that take a + * non-operator. + */ if (isOp(tk)) { Errors.inst.printError(EK_SHUNT_NOTADV, unaryOp.toString(), tk.toString()); return false; @@ -205,6 +196,7 @@ public class Shunter { return true; } else if (unaryAdverbs.contains(unaryType)) { + /* Handle unary adverbs that take an operator. */ if (!isOp(tk)) { Errors.inst.printError(EK_SHUNT_NOTADJ, unaryOp.toString(), tk.toString()); return false; @@ -229,6 +221,7 @@ public class Shunter { unaryStack.add(tk); return true; } else if (isOp(tk)) { + /* Drain higher precedence operators. */ while (!opStack.isEmpty() && isHigherPrec(tk, opStack.peek())) { final Token newOp = opStack.pop(); @@ -247,17 +240,16 @@ public class Shunter { currReturned.addLast(tk); } } else if (tk.type == CPAREN || tk.type == CBRACE) { + /* Handle closing delimiter. */ Token matching = null; switch (tk.type) { case CPAREN: matching = new Token(OPAREN, tk.intValue); break; - case CBRACE: matching = new Token(OBRACE, tk.intValue); break; - default: Errors.inst.printError(EK_SHUNT_NOGROUP); return false; @@ -278,6 +270,7 @@ public class Shunter { opStack.pop(); } else if (tk.type == GROUPSEP) { + /* Add a grouped token. */ final IList<Token> group = new FunctionalList<>(); while (currReturned.size() != 0 && !currReturned.peek().isGrouper()) { @@ -301,8 +294,9 @@ public class Shunter { return true; } + /* Check if an operator has higher precedence. */ private boolean isHigherPrec(final Token lft, final Token rght) { - final Token.Type left = lft.type; + final Token.Type left = lft.type; final Token.Type right = rght.type; boolean exists = ops.containsKey(right); @@ -311,7 +305,7 @@ public class Shunter { exists = true; } - // If it doesn't, the left is higher precedence. + /* If it doesn't, the left is higher precedence. */ if (!exists) { return false; } @@ -338,6 +332,7 @@ public class Shunter { return rightPrecedence >= leftPrecedence; } + /* Check if something is an operator. */ private boolean isOp(final Token tk) { final Token.Type ty = tk.type; @@ -364,6 +359,7 @@ public class Shunter { return false; } + /* Check if something is a unary operator. */ private boolean isUnary(final Token tk) { final Token.Type ty = tk.type; diff --git a/dice-lang/src/bjc/dicelang/Token.java b/dice-lang/src/bjc/dicelang/Token.java index 692430e..2841973 100644 --- a/dice-lang/src/bjc/dicelang/Token.java +++ b/dice-lang/src/bjc/dicelang/Token.java @@ -3,6 +3,10 @@ package bjc.dicelang; import bjc.dicelang.dice.DieExpression; import bjc.utils.funcdata.IList; +/* + * @TODO 10/09/17 Ben Culkin :TokenReorg + * Split the class into subclasses based off of type. + */ /** * Lexer token. */ diff --git a/dice-lang/src/bjc/dicelang/Tokenizer.java b/dice-lang/src/bjc/dicelang/Tokenizer.java index 4bf0d5d..3e4a490 100644 --- a/dice-lang/src/bjc/dicelang/Tokenizer.java +++ b/dice-lang/src/bjc/dicelang/Tokenizer.java @@ -14,9 +14,11 @@ import bjc.utils.funcdata.IMap; import bjc.utils.funcutils.StringUtils; import bjc.utils.parserutils.TokenUtils; -@SuppressWarnings("javadoc") +/** + * Converts strings into tokens. + */ public class Tokenizer { - // Literal tokens for tokenization + /* Literal tokens for tokenization */ private final IMap<String, Token.Type> litTokens; private final DiceLangEngine eng; @@ -65,7 +67,6 @@ public class Tokenizer { case '}': tk = tokenizeGrouping(token); break; - default: tk = tokenizeLiteral(token, stringLts); } @@ -78,6 +79,7 @@ public class Tokenizer { Token tk = Token.NIL_TOKEN; if (StringUtils.containsOnly(token, "\\" + token.charAt(0))) { + /* Handle multiple-grouped delimiters. */ switch (token.charAt(0)) { case '(': tk = new Token(OPAREN, token.length()); @@ -112,6 +114,7 @@ public class Tokenizer { return tk; } + /* Patterns for matching. */ private final Pattern hexadecimalMatcher = Pattern.compile("\\A[\\-\\+]?0x[0-9A-Fa-f]+\\Z"); private final Pattern flexadecimalMatcher = @@ -119,6 +122,7 @@ public class Tokenizer { private final Pattern stringLitMatcher = Pattern.compile("\\AstringLiteral(\\d+)\\Z"); + /* Tokenize a literal value. */ private Token tokenizeLiteral(final String rtoken, final IMap<String, String> stringLts) { Token tk = Token.NIL_TOKEN; diff --git a/dice-lang/src/bjc/dicelang/dice/DiceBox.java b/dice-lang/src/bjc/dicelang/dice/DiceBox.java index f7bf719..8d00d96 100644 --- a/dice-lang/src/bjc/dicelang/dice/DiceBox.java +++ b/dice-lang/src/bjc/dicelang/dice/DiceBox.java @@ -27,8 +27,11 @@ public class DiceBox { /* * @TODO 10/08/17 Ben Culkin :DieErrors :ErrorRefactor * Use different types of exceptions to provide - * better error messages. */ - System.out.println("ERROR: Could not parse die expression (Cause: %s)\n", ex.getMessage); + * better error messages. + */ + String exMessage = ex.getMessage(); + + System.out.printf("ERROR: Could not parse die expression (Cause: %s)\n", exMessage); ex.printStackTrace(); return null; @@ -260,6 +263,10 @@ public class DiceBox { * @return Whether or not the string is a valid command. */ public static boolean isValidExpression(final String exp) { + /* @NOTE + * Should this matcher/matches expression be abstracted in + * some way? + */ if (scalarDiePattern.matcher(exp).matches()) { return true; } else if (simpleDiePattern.matcher(exp).matches()) { @@ -281,24 +288,23 @@ public class DiceBox { } } - /* - * Derive a predicate from a compare point - */ + /* Derive a predicate from a compare point */ private static Predicate<Long> deriveCond(final String patt) { final long num = Long.parseLong(patt.substring(1)); + /* @NOTE + * Should this be extended in some way, to provide other + * operators? + */ switch (patt.charAt(0)) { case '<': - return (roll) -> roll < num; - + return (roll) -> (roll < num); case '=': - return (roll) -> roll == num; - + return (roll) -> (roll == num); case '>': - return (roll) -> roll > num; - + return (roll) -> (roll > num); default: - return (roll) -> false; + return (roll) -> (false); } } } diff --git a/dice-lang/src/bjc/dicelang/dice/DieExpression.java b/dice-lang/src/bjc/dicelang/dice/DieExpression.java index 71b7a68..b8faeda 100644 --- a/dice-lang/src/bjc/dicelang/dice/DieExpression.java +++ b/dice-lang/src/bjc/dicelang/dice/DieExpression.java @@ -2,24 +2,25 @@ package bjc.dicelang.dice; import java.util.Arrays; +/* + * @NOTE + * I'm not a particularly large fan of sticking everything on this class + * and just documenting which fields are tied together in a non-obvious + * way. I think a class hierarchy might be better, but I am unsure of the + * details. + */ /** * Represents either a die or a die list. * * @author Ben Culkin */ public class DieExpression { - /** - * Is this expression a list? - */ + /** Is this expression a list? */ public final boolean isList; - /** - * The scalar value in this expression, if there is one. - */ + /** The scalar value in this expression, if there is one. */ public Die scalar; - /** - * The list value in this expression, if there is one. - */ + /** The list value in this expression, if there is one. */ public DieList list; /** diff --git a/dice-lang/src/bjc/dicelang/dice/ExplodingDice.java b/dice-lang/src/bjc/dicelang/dice/ExplodingDice.java index 6458f04..e891a1c 100644 --- a/dice-lang/src/bjc/dicelang/dice/ExplodingDice.java +++ b/dice-lang/src/bjc/dicelang/dice/ExplodingDice.java @@ -13,16 +13,13 @@ import java.util.function.Predicate; * @author Ben Culkin
*/
public class ExplodingDice implements DieList {
- /*
- * The source die to use.
- */
+ /* The source die to use. */
private final Die source;
- /*
- * The conditions for exploding.
- */
+ /* The conditions for exploding. */
private final Predicate<Long> explodeOn;
private final String explodePattern;
+ /* Whether or not to apply a -1 penalty to explosions. */
private final boolean explodePenetrates;
/**
@@ -90,6 +87,7 @@ public class ExplodingDice implements DieList { long oldRes = res;
final List<Long> resList = new LinkedList<>();
+ resList.add(res);
while (explodeOn.test(oldRes)) {
oldRes = source.rollSingle();
@@ -101,25 +99,26 @@ public class ExplodingDice implements DieList { resList.add(oldRes);
}
- final long[] newRes = new long[resList.size() + 1];
- newRes[0] = res;
+ final long resArr[] = new long[resList.size()];
- int i = 1;
-
- for (final long rll : resList) {
- newRes[i] = rll;
- i += 1;
+ int i = 0;
+ for(long rll : resList) {
+ resArr[i] = rll;
+ i += 1;
}
- return newRes;
+ return resArr;
}
@Override
public String toString() {
+ String penString = explodePenetrates ? "p" : "";
+ String sourceString = source.toString();
+
if (explodePattern == null) {
- return source + (explodePenetrates ? "p" : "") + "!";
+ return String.format("%s%s!<complex-pred>", sourceString, penString);
}
- return source + (explodePenetrates ? "p" : "") + "!" + explodePattern;
+ return String.format("%s%s!%s", sourceString, penString, explodePattern);
}
}
diff --git a/dice-lang/src/bjc/dicelang/dice/FudgeDie.java b/dice-lang/src/bjc/dicelang/dice/FudgeDie.java index 9457b58..23951b2 100644 --- a/dice-lang/src/bjc/dicelang/dice/FudgeDie.java +++ b/dice-lang/src/bjc/dicelang/dice/FudgeDie.java @@ -7,6 +7,7 @@ package bjc.dicelang.dice; * */ public class FudgeDie implements Die { + /* The number of dice to roll. */ private final Die numDice; /** @@ -59,6 +60,8 @@ public class FudgeDie implements Die { @Override public String toString() { - return numDice + "dF"; + String dieString = numDice.toString(); + + return String.format("%sdF", dieString); } } diff --git a/dice-lang/src/bjc/dicelang/dice/MathDie.java b/dice-lang/src/bjc/dicelang/dice/MathDie.java index 1984581..e4f2953 100644 --- a/dice-lang/src/bjc/dicelang/dice/MathDie.java +++ b/dice-lang/src/bjc/dicelang/dice/MathDie.java @@ -7,6 +7,12 @@ package bjc.dicelang.dice; * */ public class MathDie implements Die { + /* + * @TODO 10/08/17 Ben Culkin :MathGeneralize + * Why do we have the operator types hardcoded, instead of just + * having a general thing for applying a binary operator to dice? + * Fix this by changing it to the more general form. + */ /** * The types of a math operator. * @@ -14,17 +20,11 @@ public class MathDie implements Die { * */ public static enum MathOp { - /** - * Add two dice. - */ + /** Add two dice. */ ADD, - /** - * Subtract two dice. - */ + /** Subtract two dice. */ SUBTRACT, - /** - * Multiply two dice. - */ + /** Multiply two dice. */ MULTIPLY; @Override @@ -118,4 +118,4 @@ public class MathDie implements Die { public String toString() { return left.toString() + " " + type.toString() + " " + right.toString(); } -}
\ No newline at end of file +} diff --git a/dice-lang/src/bjc/dicelang/dice/ScalarDie.java b/dice-lang/src/bjc/dicelang/dice/ScalarDie.java index e7b5afa..cbe9d3f 100644 --- a/dice-lang/src/bjc/dicelang/dice/ScalarDie.java +++ b/dice-lang/src/bjc/dicelang/dice/ScalarDie.java @@ -7,6 +7,7 @@ package bjc.dicelang.dice; * */ public class ScalarDie implements Die { + /* The die value. */ private final long val; /** @@ -41,6 +42,6 @@ public class ScalarDie implements Die { @Override public String toString() { - return Long.toString(val); + return String.format("%d", val); } -}
\ No newline at end of file +} diff --git a/dice-lang/src/bjc/dicelang/dice/SimpleDie.java b/dice-lang/src/bjc/dicelang/dice/SimpleDie.java index ee10070..6cae423 100644 --- a/dice-lang/src/bjc/dicelang/dice/SimpleDie.java +++ b/dice-lang/src/bjc/dicelang/dice/SimpleDie.java @@ -7,7 +7,17 @@ package bjc.dicelang.dice; * */ public class SimpleDie implements Die { + /* The number of dice to roll. */ private final Die numDice; + /* + * The size of each dice to roll. + * + * Rolled once per role, not once for each dice rolled. + * + * @NOTE + * Would having some way to roll it once for each dice rolled be + * useful in any sort of case? + */ private final Die diceSize; /** @@ -106,4 +116,4 @@ public class SimpleDie implements Die { public String toString() { return numDice + "d" + diceSize; } -}
\ No newline at end of file +} diff --git a/dice-lang/src/bjc/dicelang/dice/SimpleDieList.java b/dice-lang/src/bjc/dicelang/dice/SimpleDieList.java index 4546238..8391054 100644 --- a/dice-lang/src/bjc/dicelang/dice/SimpleDieList.java +++ b/dice-lang/src/bjc/dicelang/dice/SimpleDieList.java @@ -5,9 +5,19 @@ package bjc.dicelang.dice; * * @author EVE * + * @TODO 10/08/17 Ben Culkin :DieListGeneralize + * DieList in general should be changed to be able to be + * constructed from an arbitrary die using rollSingle and things + * like that. */ public class SimpleDieList implements DieList { + /* The number of dice to roll. */ private final Die numDice; + /* + * The size of each die to roll. + * + * Checked once per roll, not once per dice rolled. + */ private final Die size; /** @@ -64,4 +74,4 @@ public class SimpleDieList implements DieList { public String toString() { return numDice.toString() + "dl" + size.toString(); } -}
\ No newline at end of file +} diff --git a/dice-lang/src/bjc/dicelang/expr/Ezpr.java b/dice-lang/src/bjc/dicelang/expr/Ezpr.java index 7186d6f..862c097 100644 --- a/dice-lang/src/bjc/dicelang/expr/Ezpr.java +++ b/dice-lang/src/bjc/dicelang/expr/Ezpr.java @@ -1,3 +1,7 @@ +/* + * @TODO 10/08/17 Ben Culkin :EzprFixing + * Implement these, and make sure they work correctly. + */ // package bjc.dicelang.expr; // import bjc.utils.data.ITree; diff --git a/dice-lang/src/bjc/dicelang/expr/Lexer.java b/dice-lang/src/bjc/dicelang/expr/Lexer.java index bac866b..dfa0f76 100644 --- a/dice-lang/src/bjc/dicelang/expr/Lexer.java +++ b/dice-lang/src/bjc/dicelang/expr/Lexer.java @@ -6,20 +6,22 @@ import java.util.List; import bjc.utils.funcdata.IList; import bjc.utils.parserutils.splitter.ConfigurableTokenSplitter; +/* + * @TODO 10/08/18 :IntExpressions + * Add support for integer constants, and maybe floating-point ones as well + * if you feel like. Heck, you could even go for ratio constants and things + * as well. + */ /** * Implements the lexer for simple expression operations. * * @author Ben Culkin */ public class Lexer { - /* - * Splitter we use. - */ + /* Splitter we use. */ private final ConfigurableTokenSplitter split; - /** - * Create a new expression lexer. - */ + /** Create a new expression lexer. */ public Lexer() { split = new ConfigurableTokenSplitter(true); @@ -39,14 +41,19 @@ public class Lexer { * @return A series of infix tokens representing the command. */ public Token[] lexString(final String inp, final Tokens tks) { + /* Split tokens on whitespace. */ final String[] spacedTokens = inp.split("[ \t]"); + /* Tokens to return. */ + final List<Token> tokens = new LinkedList<>(); - final List<Token> tokens = new LinkedList<>(); - + /* Process each token. */ for (final String spacedToken : spacedTokens) { + /* Split on operators. */ final IList<String> splitTokens = split.split(spacedToken); - final IList<Token> rawTokens = splitTokens.map(tok -> tks.lexToken(tok, spacedToken)); + /* Convert strings to tokens. */ + final IList<Token> rawTokens = splitTokens.map(tok -> tks.lexToken(tok, spacedToken)); + /* Add tokens to results. */ rawTokens.forEach(tokens::add); } diff --git a/dice-lang/src/bjc/dicelang/expr/Parser.java b/dice-lang/src/bjc/dicelang/expr/Parser.java index 6d34b96..5fa2d3d 100644 --- a/dice-lang/src/bjc/dicelang/expr/Parser.java +++ b/dice-lang/src/bjc/dicelang/expr/Parser.java @@ -13,6 +13,10 @@ import bjc.utils.parserutils.TreeConstructor; * @author Ben Culkin */ public class Parser { + /* + * @TODO 10/08/17 Ben Culkin :MainSeperation + * This main method should be moved to its own class. + */ /** * Main method. * @@ -20,100 +24,81 @@ public class Parser { * Unused CLI args. */ public static void main(final String[] args) { - /* - * Create our objects. - */ + /* Create our objects. */ final Tokens toks = new Tokens(); final Lexer lex = new Lexer(); - /* - * Prepare our input. - */ + /* Prepare our input source. */ final Scanner scan = new Scanner(System.in); - /* - * Read initial command. - */ + /* Read initial command. */ System.out.print("Enter a math expression (blank line to quit): "); String ln = scan.nextLine().trim(); - /* - * Enter REPL loop. - */ + /* Enter REPL loop. */ while (!ln.equals("")) { - /* - * Print raw command. - */ + /* Print raw command. */ System.out.println("Raw command: " + ln); System.out.println(); - /* - * Lex command to infix tokens. - */ + /* Lex command to infix tokens. */ final Token[] infixTokens = lex.lexString(ln, toks); System.out.println("Lexed tokens: "); - for (final Token tok : infixTokens) { System.out.println("\t" + tok); } - /* - * Print out infix expression. - */ + /* Print out infix expression. */ System.out.print("Lexed expression: "); - for (final Token tok : infixTokens) { System.out.print(tok.toExpr() + " "); } + /* Space stages. */ System.out.println(); System.out.println(); - /* - * Shunt infix tokens to postfix tokens. - */ + /* Shunt infix tokens to postfix tokens. */ final Token[] postfixTokens = Shunter.shuntTokens(infixTokens); System.out.println("Lexed tokens: "); - for (final Token tok : postfixTokens) { System.out.println("\t" + tok); } - /* - * Print out postfix tokens. - */ + /* Print out postfix tokens. */ System.out.print("Shunted expression: "); - for (final Token tok : postfixTokens) { System.out.print(tok.toExpr() + " "); } + /* Space stages. */ System.out.println(); System.out.println(); - final FunctionalList<Token> tokList = new FunctionalList<>(Arrays.asList(postfixTokens)); + /* Construct a list from the array of tokens. */ + final FunctionalList<Token> tokList = new FunctionalList<>( + Arrays.asList(postfixTokens)); + + /* Construct a tree from the list of postfixed tokens. */ final ITree<Token> ast = TreeConstructor.constructTree(tokList, - tok -> tok.typ.isOperator); + tok -> tok.typ.isOperator); - /* - * Print the tree, then the canonical expression for it. - */ + /* Print the tree, then the canonical expression for it. */ System.out.println("Parsed tree"); System.out.println(ast.toString()); System.out.println("\nCanonical expr: " + toCanonicalExpr(ast)); + /* Space stages. */ + System.out.println(); System.out.println(); - /* - * Prompt for a new expression. - */ + /* Prompt for a new expression. */ System.out.print("Enter a math expression (blank line to quit): "); + /* Read it. */ ln = scan.nextLine().trim(); } - /* - * Cleanup after ourselves. - */ + /* Cleanup after ourselves. */ scan.close(); } @@ -124,38 +109,39 @@ public class Parser { private static String toCanonicalExpr(final ITree<Token> ast) { final Token data = ast.getHead(); - if (ast.getChildrenCount() == 0) - /* - * Handle leaf nodes. - */ - { + if (ast.getChildrenCount() == 0) { + /* Handle leaf nodes. */ return data.toExpr(); } - final ITree<Token> left = ast.getChild(0); + /* The left/right children. */ + final ITree<Token> left = ast.getChild(0); final ITree<Token> right = ast.getChild(1); - String leftExpr = toCanonicalExpr(left); + /* Recursively canonicalize them. */ + String leftExpr = toCanonicalExpr(left); String rightExpr = toCanonicalExpr(right); - /* - * Add parens if the left was higher priority. - */ + /* Add parens if the left was higher priority. */ if (left.getChildrenCount() == 0) { - if (left.getHead().typ.operatorPriority >= data.typ.operatorPriority) { - leftExpr = "(" + leftExpr + ")"; + int leftPriority = left.getHead().typ.operatorPriority; + int dataPriority = data.typ.operatorPriority; + + if (leftPriority >= dataPriority) { + leftExpr = String.format("(%s)", leftExpr); } } - /* - * Add parens if the right was higher priority. - */ + /* Add parens if the right was higher priority. */ if (right.getChildrenCount() == 0) { - if (right.getHead().typ.operatorPriority >= data.typ.operatorPriority) { - rightExpr = "(" + rightExpr + ")"; + int rightPriority = right.getHead().typ.operatorPriority; + int dataPriority = data.typ.operatorPriority; + + if (rightPriority >= dataPriority) { + rightExpr = String.format("(%s)", rightExpr); } } - return leftExpr + " " + data.toExpr() + " " + rightExpr; + return String.format("%s %s %s", leftExpr, data.toExpr(), rightExpr); } -}
\ No newline at end of file +} diff --git a/dice-lang/src/bjc/dicelang/expr/Shunter.java b/dice-lang/src/bjc/dicelang/expr/Shunter.java index 3e49356..213e473 100644 --- a/dice-lang/src/bjc/dicelang/expr/Shunter.java +++ b/dice-lang/src/bjc/dicelang/expr/Shunter.java @@ -11,6 +11,11 @@ import java.util.List; * @author Ben Culkin */ public class Shunter { + /* + * @NOTE + * Why does this method return an array, and not the list of + * tokens? + */ /** * Convert a infix series of tokens to a postfix series of tokens. * @@ -20,26 +25,27 @@ public class Shunter { * @return The tokens in postfix order. */ public static Token[] shuntTokens(final Token[] infixTokens) { + /* The returned tokens. */ final List<Token> postfixTokens = new ArrayList<>(infixTokens.length); + /* The current stack of operators. */ final Deque<Token> opStack = new LinkedList<>(); - /* - * Shunt each token. - */ + /* Shunt each token. */ for (final Token tok : infixTokens) { - /* - * Handle operators. - */ + /* Handle operators. */ if (tok.typ.isOperator) { Token curOp = opStack.peek(); /* * Check if an operator is higher priority, * respecting their left associativity. + * + * @NOTE + * Should this be factored out into a + * method? */ int leftPriority = tok.typ.operatorPriority; - int rightPriority; if (curOp == null) { @@ -56,11 +62,9 @@ public class Shunter { */ while (!opStack.isEmpty() && isHigherPrec) { postfixTokens.add(opStack.pop()); - curOp = opStack.peek(); leftPriority = tok.typ.operatorPriority; - if (curOp == null) { rightPriority = 0; } else { @@ -82,9 +86,7 @@ public class Shunter { */ while (curOp.typ != TokenType.OPAREN) { final Token tk = opStack.pop(); - postfixTokens.add(tk); - curOp = opStack.peek(); } @@ -105,4 +107,4 @@ public class Shunter { return postfixTokens.toArray(new Token[0]); } -}
\ No newline at end of file +} diff --git a/dice-lang/src/bjc/dicelang/expr/Token.java b/dice-lang/src/bjc/dicelang/expr/Token.java index 1a506bf..bf92f97 100644 --- a/dice-lang/src/bjc/dicelang/expr/Token.java +++ b/dice-lang/src/bjc/dicelang/expr/Token.java @@ -1,14 +1,18 @@ package bjc.dicelang.expr; +/* + * @TODO 10/08/17 :TokenReorg + * I am not a fan of this 'having a bunch of subclasses' in one thing I + * seem to have been doing around this project. This should be multiple + * subclasses, one for each value for TokenType. + */ /** * Represents a lexical token. * * @author Ben Culkin */ public class Token { - /* - * The state for this token. - */ + /* The state for this token. */ private final Tokens tks; /** @@ -18,14 +22,10 @@ public class Token { */ public final TokenType typ; - /** - * The integer value attached to this token. - */ + /** The integer value attached to this token. */ public int intValue; - /** - * The original string this token was part of. - */ + /** The original string this token was part of. */ public String rawValue; /** @@ -44,21 +44,20 @@ public class Token { this.typ = type; rawValue = raw; - tks = toks; } @Override public String toString() { String typeStr = typ.toString(); - typeStr += " (" + typ.name() + ")"; + typeStr = String.format("%s (%s)", typeStr, typ.name()); if (typ == TokenType.VREF) { typeStr += " (ind. " + intValue; typeStr += ", sym. \"" + tks.symbolTable.get(intValue) + "\")"; } - return typeStr + " (originally from: " + rawValue + ")"; + return String.format("%s (originally from: %s)", typeStr, rawValue); } /** @@ -70,27 +69,20 @@ public class Token { switch (typ) { case ADD: return "+"; - case SUBTRACT: return "-"; - case MULTIPLY: return "*"; - case DIVIDE: return "/"; - case VREF: return tks.symbolTable.get(intValue); - case OPAREN: return "("; - case CPAREN: return ")"; - default: return "???"; } } -}
\ No newline at end of file +} diff --git a/dice-lang/src/bjc/dicelang/expr/TokenType.java b/dice-lang/src/bjc/dicelang/expr/TokenType.java index d88283e..ad01917 100644 --- a/dice-lang/src/bjc/dicelang/expr/TokenType.java +++ b/dice-lang/src/bjc/dicelang/expr/TokenType.java @@ -2,59 +2,46 @@ package bjc.dicelang.expr; /** * Represents the type of this token. + * + * @author Ben Culkin */ public enum TokenType { - /** - * Represents + + /* + * @NOTE + * Do we want to switch to auto-numbering the tokens? They were + * manually numbered because this was an assignment for PoPL and + * that was what Dr. Naz wanted. */ - ADD(14, true, 0), - /** - * Represents - - */ - SUBTRACT(15, true, 0), - /** - * Represents * - */ + /** Represents + */ + ADD( 14, true, 0), + /** Represents - */ + SUBTRACT(15, true, 0), + /** Represents * */ MULTIPLY(16, true, 1), - /** - * Represents / - */ - DIVIDE(17, true, 1), + /** Represents / */ + DIVIDE( 17, true, 1), - /** - * Represents variable names. - */ + /** Represents variable names. */ VREF(11), - /** - * Represents ( - */ + /** Represents ( */ OPAREN(0, false, 100), - /** - * Represents ) - */ + /** Represents ) */ CPAREN(0, false, 100); - /** - * The ID number for this token type. - */ + /** The ID number for this token type. */ public final int nVal; - /** - * Whether or not this type of token is an operator. - */ + /** Whether or not this type of token is an operator. */ public final boolean isOperator; - /** - * The priority of this operator, if it is one. - */ + /** The priority of this operator, if it is one. */ public final int operatorPriority; + /* Create a new token. */ private TokenType(final int num, final boolean isOp, final int priority) { - nVal = num; - - isOperator = isOp; - + nVal = num; + isOperator = isOp; operatorPriority = priority; } @@ -66,4 +53,4 @@ public enum TokenType { public String toString() { return Integer.toString(nVal); } -}
\ No newline at end of file +} diff --git a/dice-lang/src/bjc/dicelang/expr/Tokens.java b/dice-lang/src/bjc/dicelang/expr/Tokens.java index f763d37..287d2b4 100644 --- a/dice-lang/src/bjc/dicelang/expr/Tokens.java +++ b/dice-lang/src/bjc/dicelang/expr/Tokens.java @@ -11,43 +11,39 @@ import java.util.Map; * */ public class Tokens { - /* - * Contains mappings from variable references to string names. - */ + /* Contains mappings from variable references to string names. */ private final Map<Integer, String> symTab; - /* - * Reverse index into the symbol table. - */ + /* Reverse index into the symbol table. */ private final Map<String, Integer> revSymTab; - /** - * Read-only view on the symbol table. - */ + /** Read-only view on the symbol table. */ public final Map<Integer, String> symbolTable; - /* - * Next index into the symbol table. - */ + /* Next index into the symbol table. */ private int nextSym; - /* - * Mapping from literal tokens to token types. - */ + /* Mapping from literal tokens to token types. */ private final Map<String, TokenType> litTokens; - /** - * Create a new set of tokens. - */ + /** Create a new set of tokens. */ public Tokens() { + /* Create tables. */ symTab = new HashMap<>(); revSymTab = new HashMap<>(); + /* Init public view. */ symbolTable = Collections.unmodifiableMap(symTab); + /* Set sym ID. */ nextSym = 0; + /* + * Setup literal mappings. + * + * @NOTE + * Should this be a static member? + */ litTokens = new HashMap<>(); - litTokens.put("+", TokenType.ADD); litTokens.put("-", TokenType.SUBTRACT); litTokens.put("*", TokenType.MULTIPLY); @@ -68,32 +64,30 @@ public class Tokens { */ public Token lexToken(final String tok, final String raw) { if (litTokens.containsKey(tok)) { + /* Return matching literal token. */ return new Token(litTokens.get(tok), raw, this); } + /* Its a variable reference. */ return parseVRef(tok, raw); } - /* - * Parse a variable reference. - */ + /* Parse a variable reference. */ private Token parseVRef(final String tok, final String raw) { final Token tk = new Token(TokenType.VREF, raw, this); if (revSymTab.containsKey(tok)) { - /* - * Reuse the entry if it exists. - */ + /* Reuse the entry if it exists. */ tk.intValue = revSymTab.get(tok); } else { - /* - * Create a new entry. - */ + /* Create a new entry. */ tk.intValue = nextSym; + /* Record it. */ symTab.put(nextSym, tok); revSymTab.put(tok, nextSym); + /* Next ID. */ nextSym += 1; } diff --git a/dice-lang/src/bjc/dicelang/scl/StreamControlConsole.java b/dice-lang/src/bjc/dicelang/scl/StreamControlConsole.java index 8ace8ad..649c6fa 100644 --- a/dice-lang/src/bjc/dicelang/scl/StreamControlConsole.java +++ b/dice-lang/src/bjc/dicelang/scl/StreamControlConsole.java @@ -8,21 +8,43 @@ import java.util.Scanner; import java.util.function.Supplier; +/** + * Implement a SCL REPL + * + * @author Ben Culkin + */ public class StreamControlConsole { + /* + * @TODO 10/08/17 :SCLArgs + * Do something useful with the CLI args. + * + */ + /** + * Main method + * + * @param args + * Unused CLI args. + */ public static void main(String[] args) { - /* Initialize vars. */ - /* We're not using the DiceLangEngine in the streams yet. */ - StreamEngine sengine = new StreamEngine(null); + /* + * Initialize vars. + * + * We can get away with passing the null, because StreamEngine + * doesn't reference any parts of DiceLangEngine. + */ + StreamEngine sengine = new StreamEngine(null); StreamControlEngine sclengine = new StreamControlEngine(sengine); - Scanner scn = new Scanner(System.in); + Scanner scn = new Scanner(System.in); /* Get input from the user. */ System.out.print("Enter a SCL command string (blank to exit): "); + /* Process it. */ while (scn.hasNextLine()) { - String ln = scn.nextLine(); + String ln = scn.nextLine().trim(); - if (ln.trim().equals("")) { + if (ln.equals("")) { + /* Ignore empty lines. */ break; } @@ -32,16 +54,16 @@ public class StreamControlConsole { /* Run the stream engine on the tokens. */ boolean succ = sengine.doStreams(tokens, res); - if (!succ) { + System.out.printf("ERROR: Stream engine failed for line '%s'\n", ln); continue; } /* Run the command through SCL. */ tokens = res.toArray(new String[res.getSize()]); succ = sclengine.runProgram(tokens); - if (!succ) { + System.out.printf("ERROR: SCL engine failed for line '%s'\n", ln); continue; } diff --git a/dice-lang/src/bjc/dicelang/scl/StreamControlEngine.java b/dice-lang/src/bjc/dicelang/scl/StreamControlEngine.java index 2952a89..d5e8b72 100644 --- a/dice-lang/src/bjc/dicelang/scl/StreamControlEngine.java +++ b/dice-lang/src/bjc/dicelang/scl/StreamControlEngine.java @@ -1,37 +1,5 @@ package bjc.dicelang.scl; -import static bjc.dicelang.Errors.ErrorKey.EK_SCL_INVARG; -import static bjc.dicelang.Errors.ErrorKey.EK_SCL_INVTOKEN; -import static bjc.dicelang.Errors.ErrorKey.EK_SCL_MMQUOTE; -import static bjc.dicelang.Errors.ErrorKey.EK_SCL_SUNDERFLOW; -import static bjc.dicelang.Errors.ErrorKey.EK_SCL_UNWORD; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.ARRAY; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.BLIT; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.DELETESTREAM; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.DQUOTE; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.DROP; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.FLIT; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.ILIT; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.LEFTSTREAM; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.MAKEARRAY; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.MAKEEXEC; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.MAKEUNEXEC; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.MERGESTREAM; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.NDROP; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.NEWSTREAM; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.NIP; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.NNIP; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.OBRACE; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.OBRACKET; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.RIGHTSTREAM; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.SLIT; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.SQUOTE; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.STACKCOUNT; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.STACKEMPTY; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.SYMBOL; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.WORD; -import static bjc.dicelang.scl.StreamControlEngine.Token.Type.WORDS; - import java.util.HashMap; import java.util.Map; @@ -42,117 +10,118 @@ import bjc.utils.funcdata.FunctionalList; import bjc.utils.funcdata.IList; import bjc.utils.parserutils.TokenUtils; +import static bjc.dicelang.Errors.ErrorKey.*; +import static bjc.dicelang.scl.StreamControlEngine.Token.Type.*; + +/* + * @TODO 10/08/17 Ben Culkin :SCLReorg + * This is a large enough class that it should maybe be split into + * subclasses. + */ /** * Runs a Stream Control Language (SCL) program. * * SCL is a stack-based concatenative language based mostly off of Postscript * and Factor, with inspiration from various other languages. + * + * @author Ben Culkin */ public class StreamControlEngine { - static class Token { + /* + * @TODO 10/08/17 Ben Culkin :TokenSplit + * Again with the multiple subclasses in one class. Split it so + * that each subclass only has the fields it needs. + */ + public static class Token { public static enum Type { - /* - * Natural tokens. These come directly from strings - */ + /* Natural tokens. These come directly from strings */ ILIT, FLIT, BLIT, SQUOTE, DQUOTE, OBRACKET, OBRACE, SYMBOL, WORD, - /* - * Synthetic tokens. These are produced from special - * tokens. - */ + /* Synthetic tokens. These are produced from special tokens. */ SLIT, WORDS, ARRAY, + /* Word tokens These are subordinate to WORD tokens */ /* - * Word tokens These are subordinate to WORD tokens - */ - /* - * Array manipulation + * @NOTE + * These should really be in their own enum. */ + /* Array manipulation */ MAKEARRAY, MAKEEXEC, MAKEUNEXEC, - /* - * Stream manipulation - */ + /* Stream manipulation */ NEWSTREAM, LEFTSTREAM, RIGHTSTREAM, DELETESTREAM, MERGESTREAM, - /* - * Stack manipulation - */ + /* Stack manipulation */ STACKCOUNT, STACKEMPTY, DROP, NDROP, NIP, NNIP, } - /* - * The type of this token - */ + /* The type of this token */ public Type type; - /* - * Used for ILIT - */ + /* Used for ILIT */ public long intVal; - /* - * Used for FLIT - */ + /* Used for FLIT */ public double floatVal; - /* - * Used for BLIT - */ + /* Used for BLIT */ public boolean boolVal; - /* - * Used for SYMBOL SLIT - */ + /* Used for SYMBOL & SLIT */ public String stringVal; - /* - * Used for WORD - */ + /* Used for WORD */ public Token tokenVal; - /* - * Used for WORDS ARRAY - */ + /* Used for WORDS & ARRAY */ public IList<Token> tokenVals; + /* Create a new token. */ public Token(final Type typ) { type = typ; } + /* Create a new token. */ public Token(final Type typ, final long iVal) { this(typ); intVal = iVal; } + /* Create a new token. */ public Token(final Type typ, final double dVal) { this(typ); floatVal = dVal; } + /* Create a new token. */ public Token(final Type typ, final boolean bVal) { this(typ); boolVal = bVal; } + /* Create a new token. */ public Token(final Type typ, final String sVal) { this(typ); stringVal = sVal; } + /* Create a new token. */ public Token(final Type typ, final Token tVal) { this(typ); tokenVal = tVal; } + /* Create a new token. */ public Token(final Type typ, final Token.Type tVal) { this(typ, new Token(tVal)); } + /* Create a new token. */ public Token(final Type typ, final IList<Token> tVals) { this(typ); tokenVals = tVals; } + /* Convert a string into a token. */ public static Token tokenizeString(final String token) { if (litTokens.containsKey(token)) { return new Token(litTokens.get(token)); @@ -174,10 +143,13 @@ public class StreamControlEngine { } } + /* The literal tokens. */ private static final Map<String, Token.Type> litTokens; + /* The builtin words. */ private static final Map<String, Token.Type> builtinWords; static { + /* Init literal tokens. */ litTokens = new HashMap<>(); litTokens.put("'", SQUOTE); @@ -185,6 +157,7 @@ public class StreamControlEngine { litTokens.put("[", OBRACKET); litTokens.put("{", OBRACE); + /* Init builtin words. */ builtinWords = new HashMap<>(); builtinWords.put("makearray", MAKEARRAY); @@ -204,11 +177,13 @@ public class StreamControlEngine { } } + /* The stream engine we're hooked to. */ private final StreamEngine eng; + /* The current stack state. */ private final Stack<Token> curStack; - @SuppressWarnings("unused") + /* Map of user defined words. */ private final Map<String, Token> words; /** @@ -234,48 +209,49 @@ public class StreamControlEngine { */ public boolean runProgram(final String[] tokens) { for (int i = 0; i < tokens.length; i++) { + /* Tokenize each token. */ final String token = tokens[i]; - final Token tok = Token.tokenizeString(token); + final Token tok = Token.tokenizeString(token); if (tok == null) { + System.out.printf("ERROR: Tokenization failed for '%s'\n", token); return false; } + /* Handle token types. */ switch (tok.type) { case SQUOTE: + /* Handle single-quotes. */ i = handleSingleQuote(i, tokens); - if (i == -1) { return false; } - break; - case OBRACKET: + /* Handle delimited brackets. */ i = handleDelim(i, tokens, "]"); - if (i == -1) { return false; } - break; - case OBRACE: + /* Handle delimited braces. */ i = handleDelim(i, tokens, "}"); - if (i == -1) { return false; } - final Token brak = curStack.pop(); curStack.push(new Token(ARRAY, brak.tokenVals)); break; case WORD: - handleWord(tok); + /* Handle words. */ + if(!handleWord(tok)) { + System.out.printf("WARNING: Execution of word '%s' failed\n", tok); + } break; - default: + /* Put it onto the stack. */ curStack.push(tok); break; } @@ -287,118 +263,90 @@ public class StreamControlEngine { private boolean handleWord(final Token tk) { boolean succ = true; + /* Handle each type of word. */ + /* + * @NOTE + * This should probably use something other than a switch + * statement. + */ switch (tk.tokenVal.type) { case NEWSTREAM: eng.newStream(); break; - case LEFTSTREAM: succ = eng.leftStream(); - if (!succ) { return false; } - break; - case RIGHTSTREAM: succ = eng.rightStream(); - if (!succ) { return false; } - break; - case DELETESTREAM: succ = eng.deleteStream(); - if (!succ) { return false; } - break; - case MERGESTREAM: succ = eng.mergeStream(); - if (!succ) { return false; } - break; - case MAKEARRAY: succ = makeArray(); - if (!succ) { return false; } - break; - case MAKEEXEC: succ = toggleExec(true); - if (!succ) { return false; } - break; - case MAKEUNEXEC: succ = toggleExec(false); - if (!succ) { return false; } - break; - case STACKCOUNT: curStack.push(new Token(ILIT, curStack.size())); break; - case STACKEMPTY: curStack.push(new Token(BLIT, curStack.empty())); break; - case DROP: if (curStack.size() == 0) { Errors.inst.printError(EK_SCL_SUNDERFLOW, tk.tokenVal.type.toString()); return false; } - curStack.drop(); break; - case NDROP: succ = handleNDrop(); - if (!succ) { return false; } - break; - case NIP: if (curStack.size() < 2) { Errors.inst.printError(EK_SCL_SUNDERFLOW, tk.tokenVal.type.toString()); return false; } - curStack.nip(); break; - case NNIP: succ = handleNNip(); - if (!succ) { return false; } - break; - default: Errors.inst.printError(EK_SCL_UNWORD, tk.tokenVal.type.toString()); return false; @@ -407,6 +355,7 @@ public class StreamControlEngine { return true; } + /* Handle nipping a specified number of items. */ private boolean handleNNip() { final Token num = curStack.pop(); @@ -426,6 +375,7 @@ public class StreamControlEngine { return true; } + /* Handle dropping a specified number of items. */ private boolean handleNDrop() { final Token num = curStack.pop(); @@ -445,6 +395,7 @@ public class StreamControlEngine { return true; } + /* Handle toggling the executable flag on an array. */ private boolean toggleExec(final boolean exec) { final Token top = curStack.top(); @@ -467,6 +418,7 @@ public class StreamControlEngine { return true; } + /* Handle creating an array. */ private boolean makeArray() { final Token num = curStack.pop(); @@ -485,6 +437,7 @@ public class StreamControlEngine { return true; } + /* Handle a delimited series of tokens. */ private int handleDelim(final int i, final String[] tokens, final String delim) { final IList<Token> toks = new FunctionalList<>(); @@ -503,42 +456,31 @@ public class StreamControlEngine { switch (ntok.type) { case SQUOTE: n = handleSingleQuote(n, tokens); - if (n == -1) { return -1; } - toks.add(curStack.pop()); break; - case OBRACKET: n = handleDelim(n, tokens, "]"); - if (n == -1) { return -1; } - toks.add(curStack.pop()); break; - case OBRACE: n = handleDelim(i, tokens, "}"); - if (n == -1) { return -1; } - final Token brak = curStack.pop(); toks.add(new Token(ARRAY, brak.tokenVals)); break; - default: toks.add(ntok); } - /* - * Move to the next token - */ + /* Move to the next token */ n += 1; if (n >= tokens.length) { @@ -549,16 +491,18 @@ public class StreamControlEngine { tok = tokens[n]; } - /* - * Skip the closing bracket - */ + /* Skip the closing bracket */ n += 1; + /* @NOTE + * Instead of being hardcoded, this should be a parameter. + */ curStack.push(new Token(WORDS, toks)); return n; } + /* Handle a single-quoted string. */ private int handleSingleQuote(final int i, final String[] tokens) { final StringBuilder sb = new StringBuilder(); @@ -573,17 +517,13 @@ public class StreamControlEngine { while (!tok.equals("'")) { if (tok.matches("\\\\+'")) { - /* - * Handle escaped quotes. - */ + /* Handle escaped quotes. */ sb.append(tok.substring(1)); } else { sb.append(tok); } - /* - * Move to the next token - */ + /* Move to the next token */ n += 1; if (n >= tokens.length) { diff --git a/dice-lang/src/bjc/dicelang/scl/StreamEngine.java b/dice-lang/src/bjc/dicelang/scl/StreamEngine.java index 2082c9b..6e970b7 100644 --- a/dice-lang/src/bjc/dicelang/scl/StreamEngine.java +++ b/dice-lang/src/bjc/dicelang/scl/StreamEngine.java @@ -103,15 +103,18 @@ public class StreamEngine { for (final String tk : toks) { /* Process stream commands. */ if (tk.startsWith("{@S") && !quoteMode) { - if (tk.equals("{@SQ}")) { + if (tk.equals("{@SQ}")) { + /* Start quoting. */ quoteMode = true; } else if (!processCommand(tk)) { return false; } } else { if (tk.equals("{@SU}")) { + /* Stop quoting. */ quoteMode = false; } else if (tk.startsWith("\\") && tk.endsWith("{@SU}")) { + /* Unquote quoted end. */ currStream.add(tk.substring(1)); } else { currStream.add(tk); @@ -120,15 +123,14 @@ public class StreamEngine { } for (final String tk : currStream) { + /* Collect tokens from the current stream. */ dest.add(tk); } return true; } - /** - * Create a new stream. - */ + /** Create a new stream. */ public void newStream() { streams.insertAfter(new FunctionalList<>()); } @@ -202,65 +204,57 @@ public class StreamEngine { char[] comms = null; if (tk.length() > 5) { + /* Pull off {@S and closing } */ comms = tk.substring(3, tk.length() - 1).toCharArray(); } else { + /* Its a single char. command. */ comms = new char[1]; comms[0] = tk.charAt(3); } boolean succ; + /* Process each command. */ + /* + * @TODO 10/09/17 Ben Culkin :StreamCommands + * This should probably be refactored in some way, so as to + * make it easier to add new commands. + */ for (final char comm : comms) { switch (comm) { case '+': newStream(); break; - case '>': succ = rightStream(); - if (!succ) { return false; } - break; - case '<': succ = leftStream(); - if (!succ) { return false; } - break; - case '-': succ = deleteStream(); - if (!succ) { return false; } - break; - case 'M': succ = mergeStream(); - if (!succ) { return false; } - break; - case 'L': succ = scleng.runProgram(currStream.toArray(new String[0])); - if (!succ) { return false; } - break; - default: Errors.inst.printError(EK_STRM_INVCOM, tk); return false; diff --git a/dice-lang/src/bjc/dicelang/util/ResourceLoader.java b/dice-lang/src/bjc/dicelang/util/ResourceLoader.java index 37cf5b3..db5c6fc 100644 --- a/dice-lang/src/bjc/dicelang/util/ResourceLoader.java +++ b/dice-lang/src/bjc/dicelang/util/ResourceLoader.java @@ -6,6 +6,7 @@ import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import bjc.dicelang.Errors; @@ -30,7 +31,9 @@ public class ResourceLoader { final URL fle = ResourceLoader.class.getResource("/data/help/" + name + ".help"); try { - return Files.lines(Paths.get(fle.toURI())).toArray(sze -> new String[sze]); + Path pth = Paths.get(fle.toURI()); + + return Files.lines(pth).toArray(sze -> new String[sze]); } catch (IOException | URISyntaxException ioex) { Errors.inst.printError(EK_MISC_IOEX, fle.toString()); } |
