diff options
| author | bculkin2442 <bjculkin@mix.wvu.edu> | 2017-02-19 08:30:45 -0500 |
|---|---|---|
| committer | bculkin2442 <bjculkin@mix.wvu.edu> | 2017-02-19 08:30:45 -0500 |
| commit | e03b3f477bcc160b72af4ab09cd8d12081017d2c (patch) | |
| tree | f4e1779c5cf3aa893bd7bdc6c826c64f15648ed9 /dice-lang/src/bjc | |
| parent | 03e40ec669ee51c697de64178af6bb662161a8ae (diff) | |
Lots of things, but mostly evaluation
Diffstat (limited to 'dice-lang/src/bjc')
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/Define.java | 20 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/DiceLangConsole.java | 48 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java | 224 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/Evaluator.java | 262 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/Node.java | 82 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/Parser.java | 147 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/Shunter.java | 86 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/StreamEngine.java | 83 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/v2/Token.java | 72 |
9 files changed, 916 insertions, 108 deletions
diff --git a/dice-lang/src/bjc/dicelang/v2/Define.java b/dice-lang/src/bjc/dicelang/v2/Define.java index 6ca0c74..2ecbc69 100644 --- a/dice-lang/src/bjc/dicelang/v2/Define.java +++ b/dice-lang/src/bjc/dicelang/v2/Define.java @@ -23,7 +23,8 @@ public class Define implements UnaryOperator<String> { private Iterator<String> replacers; private String replacer; - public Define(int priorty, boolean isSub, boolean recur, + public Define(int priorty, + boolean isSub, boolean recur, boolean isCircular, String predicte, String searchr, Iterable<String> replacrs) { priority = priorty; doRecur = recur; @@ -36,7 +37,7 @@ public class Define implements UnaryOperator<String> { if(subType) { if(replacrs.iterator().hasNext()) { - replacers = new CircularIterator<>(replacrs); + replacers = new CircularIterator<>(replacrs, isCircular); } else { replacers = null; } @@ -79,8 +80,19 @@ public class Define implements UnaryOperator<String> { StringBuffer sb = new StringBuffer(); while(searcherMatcher.find()) { - if(replacers == null) searcherMatcher.appendReplacement(sb,""); - else searcherMatcher.appendReplacement(sb, replacers.next()); + if(replacers == null) { + System.out.println("\t\tTRACE: running null replacer on substring " + + tok.substring(searcherMatcher.start(), searcherMatcher.end())); + + searcherMatcher.appendReplacement(sb,""); + } else { + String replac = replacers.next(); + + System.out.println("\t\tTRACE: running replacer " + replac + " on substring " + + tok.substring(searcherMatcher.start(), searcherMatcher.end())); + + searcherMatcher.appendReplacement(sb, replacers.next()); + } } searcherMatcher.appendTail(sb); diff --git a/dice-lang/src/bjc/dicelang/v2/DiceLangConsole.java b/dice-lang/src/bjc/dicelang/v2/DiceLangConsole.java index 5460286..5731877 100644 --- a/dice-lang/src/bjc/dicelang/v2/DiceLangConsole.java +++ b/dice-lang/src/bjc/dicelang/v2/DiceLangConsole.java @@ -58,15 +58,28 @@ public class DiceLangConsole { private boolean handlePragma(String pragma) { System.out.println("\tRaw pragma: " + pragma); - switch(pragma.substring(0, pragma.indexOf(' '))) { + String pragmaName = null; + int firstIndex = pragma.indexOf(' '); + if(firstIndex == -1) { + pragmaName = pragma; + } else { + pragmaName = pragma.substring(0, firstIndex); + } + + 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 "define": return defineMode(pragma.substring(7)); + case "help": + return helpMode(pragma.substring(5)); default: System.out.println("\tERROR: Unknown pragma: " + pragma); return false; @@ -75,6 +88,19 @@ public class DiceLangConsole { return true; } + private boolean helpMode(String pragma) { + switch(pragma.trim()) { + case "define": + 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; + } + /* * Matches slash-delimited strings * (like /text/ or /text\/text/) @@ -87,7 +113,7 @@ public class DiceLangConsole { * Then, we just follow the pattern, escape it for java strings, and * add the enclosing slashes */ - private Pattern slashPattern = Pattern.compile("/([^/\\\\]*(?:\\\\/(?:[^/\\\\])*)*)/"); + private Pattern slashPattern = Pattern.compile("/((?:\\\\.|[^/\\\\])*)/"); private boolean defineMode(String defineText) { int firstIndex = defineText.indexOf(' '); @@ -95,6 +121,7 @@ public class DiceLangConsole { int thirdIndex = defineText.indexOf(' ', secondIndex + 1); int fourthIndex = defineText.indexOf(' ', thirdIndex + 1); int fifthIndex = defineText.indexOf(' ', fourthIndex + 1); + int sixthIndex = defineText.indexOf(' ', fifthIndex + 1); if(firstIndex == -1) { System.out.println("\tERROR: Improperly formatted define (no priority)"); @@ -109,6 +136,9 @@ public class DiceLangConsole { System.out.println("\tERROR: Improperly formatted define (no guard type)"); return false; } else if(fifthIndex == -1) { + System.out.println("\tERROR: Improperly formatted define (no circularity)"); + return false; + } else if(sixthIndex == -1) { System.out.println("\tERROR: Improperly formatted define (no patterns)"); return false; } @@ -141,13 +171,14 @@ public class DiceLangConsole { return false; } - boolean doRecur = defineText.substring(secondIndex + 1, thirdIndex) + boolean doRecur = defineText.substring(secondIndex + 1, thirdIndex) + .equalsIgnoreCase("true"); + boolean hasGuard = defineText.substring(thirdIndex + 1, fourthIndex) + .equalsIgnoreCase("true"); + boolean isCircular = defineText.substring(thirdIndex + 1, fourthIndex) .equalsIgnoreCase("true"); - boolean hasGuard = defineText.substring(thirdIndex + 1, fourthIndex). - equalsIgnoreCase("true"); - - String pats = defineText.substring(fourthIndex + 1); + String pats = defineText.substring(fifthIndex + 1).trim(); Matcher patMatcher = slashPattern.matcher(pats); String guardPattern = null; @@ -171,7 +202,8 @@ public class DiceLangConsole { replacePatterns.add(patMatcher.group(1)); } - Define dfn = new Define(priority, subMode, doRecur, guardPattern, searchPattern, replacePatterns); + Define dfn = new Define(priority, subMode, doRecur, isCircular, + guardPattern, searchPattern, replacePatterns); if(type == Define.Type.LINE) { eng.addLineDefine(dfn); diff --git a/dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java b/dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java index db471c2..5fbfb3a 100644 --- a/dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java +++ b/dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java @@ -1,7 +1,9 @@ package bjc.dicelang.v2; import bjc.utils.data.IPair; +import bjc.utils.data.ITree; import bjc.utils.data.Pair; +import bjc.utils.data.Tree; import bjc.utils.funcdata.FunctionalList; import bjc.utils.funcdata.FunctionalMap; import bjc.utils.funcdata.FunctionalStringTokenizer; @@ -33,9 +35,15 @@ public class DiceLangEngine { private boolean debugMode; // Should we do shunting? private boolean postfixMode; + // Should we reverse the token stream + private boolean prefixMode; // Shunter for token postfixing private Shunter shunt; + // Parser for tree construction + private Parser parsr; + // Evaluator for evaluating + private Evaluator eval; // Tables for symbols private IMap<Integer, String> symTable; @@ -51,6 +59,7 @@ public class DiceLangEngine { // Are defns sorted by priority private boolean defnsSorted; + // Stream engine for processing streams private StreamEngine streamEng; private final int MATH_PREC = 20; @@ -67,13 +76,14 @@ public class DiceLangEngine { opExpansionList = new LinkedList<>(); - opExpansionList.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<>("//", "//")); - opExpansionList.add(new Pair<>("/", "/")); + opExpansionList.add(new Pair<>("/", "/")); opExpansionList.add(new Pair<>(":=", ":=")); opExpansionList.add(new Pair<>("=>", "=>")); + opExpansionList.add(new Pair<>(",", ",")); deaffixationList = new LinkedList<>(); @@ -81,6 +91,8 @@ public class DiceLangEngine { deaffixationList.add(new Pair<>(")", "\\)")); deaffixationList.add(new Pair<>("[", "\\[")); deaffixationList.add(new Pair<>("]", "\\]")); + deaffixationList.add(new Pair<>("{", "\\{")); + deaffixationList.add(new Pair<>("}", "}")); litTokens = new FunctionalMap<>(); @@ -94,15 +106,19 @@ public class DiceLangEngine { litTokens.put("dl", DICELIST); litTokens.put("=>", LET); litTokens.put(":=", BIND); - - shunt = new Shunter(); + litTokens.put(",", GROUPSEP); nextLiteral = 1; debugMode = true; postfixMode = false; + prefixMode = false; streamEng = new StreamEngine(this); + + shunt = new Shunter(); + parsr = new Parser(); + eval = new Evaluator(this); } public void sortDefns() { @@ -138,6 +154,12 @@ public class DiceLangEngine { return postfixMode; } + public boolean togglePrefix() { + prefixMode = !prefixMode; + + return prefixMode; + } + /* * Matches quote-delimited strings * (like "text" or "text\"text") @@ -150,7 +172,10 @@ public class DiceLangEngine { * Then, we just follow the pattern, escape it for java strings, and * add the enclosing quotes */ - private Pattern quotePattern = Pattern.compile("\"([^\\\"]*(?:\\\"/(?:[^\\\"])*)*)\""); + private Pattern quotePattern = Pattern.compile("\"([^\\\"]*(?:\\\"(?:[^\\\"])*)*)\""); + + // Similiar to the above, but using angle brackets instead of quotes and not allowing spaces + private Pattern nonExpandPattern = Pattern.compile("<([^\\>&&[^\\s]]*(?:\\>(?:[^\\>&&[^\\s]])*)*)>"); public boolean runCommand(String command) { // Sort the defines if they aren't sorted @@ -190,31 +215,59 @@ public class DiceLangEngine { // Split the command into tokens IList<String> tokens = FunctionalStringTokenizer - .fromString(destringedCommand.toString()) - .toList(); + .fromString(destringedCommand.toString()).toList(); if(debugMode) { - System.out.println("\tCommand after destringing: " + tokens.toString()); + System.out.println("\tCommand after destringing: " + destringedCommand); - System.out.println("\tString literals in table"); + if(stringLiterals.getSize() > 0) { + System.out.println("\tString literals in table"); - stringLiterals.forEach((key, val) -> { - System.out.printf("\t\tName: (%s)\tValue: (%s)\n", - key, val); - }); + stringLiterals.forEach((key, val) -> { + System.out.printf("\t\tName: (%s)\tValue: (%s)\n", + key, val); + }); + } } + IMap<String, String> nonExpandedTokens = new FunctionalMap<>(); + + tokens = tokens.map(tk -> { + Matcher nonExpandMatcher = nonExpandPattern.matcher(tk); + + if(nonExpandMatcher.matches()) { + String tkName = "nonExpandToken" + nextLiteral++; + nonExpandedTokens.put(tkName, nonExpandMatcher.group(1)); + + return tkName; + } else { + return tk; + } + }); + + System.out.println("\tCommand after removal of non-expanders: " + tokens.toString()); + IList<String> semiExpandedTokens = deaffixTokens(tokens, deaffixationList); IList<String> fullyExpandedTokens = deaffixTokens(semiExpandedTokens, opExpansionList); + System.out.println("\tCommand after token expansion: " + fullyExpandedTokens.toString()); + + fullyExpandedTokens = fullyExpandedTokens.map(tk -> { + if(tk.startsWith("nonExpandToken")) { + return nonExpandedTokens.get(tk); + } else { + return tk; + } + }); + if(debugMode) - System.out.printf("\tCommand after token expansion: " + System.out.printf("\tCommand after non-expander reinsertion: " + fullyExpandedTokens.toString() + "\n"); IList<Token> lexedTokens = new FunctionalList<>(); - for(String token : fullyExpandedTokens.toIterable()) { + for(String token : fullyExpandedTokens) { String newTok = token; for(Define dfn : tokenDefns.toIterable()) { @@ -233,15 +286,113 @@ public class DiceLangEngine { IList<Token> shuntedTokens = lexedTokens; - if(!postfixMode) { + IList<Token> preparedTokens = new FunctionalList<>(); + int curBraceCount = 0; + Deque<IList<Token>> bracedTokens = new LinkedList<>(); + IList<Token> curBracedTokens = null; + + for(Token tk : lexedTokens) { + if(tk.type == Token.Type.OBRACE && tk.intValue == 2) { + curBraceCount += 1; + + if(curBraceCount != 1) { + bracedTokens.push(curBracedTokens); + } + + curBracedTokens = new FunctionalList<>(); + } else if(tk.type == Token.Type.CBRACE && tk.intValue == 2) { + if(curBraceCount == 0) { + System.out.println("\tERROR: Encountered closing brace without matching open brace"); + return false; + } + + curBraceCount -= 1; + + IList<Token> preshuntTokens = new FunctionalList<>(); + + success = shunt.shuntTokens(curBracedTokens, preshuntTokens); + + if(debugMode) + System.out.println("\t\tPreshunted " + curBracedTokens + " into " + preshuntTokens); + + if(!success) return false; + + if(curBraceCount >= 1) { + curBracedTokens = bracedTokens.pop(); + + curBracedTokens.add(new Token(Token.Type.TOKGROUP, preshuntTokens)); + } else { + preparedTokens.add(new Token(Token.Type.TOKGROUP, preshuntTokens)); + } + } else { + if(curBraceCount >= 1) { + curBracedTokens.add(tk); + } else { + preparedTokens.add(tk); + } + } + } + + if(debugMode && !postfixMode) + System.out.printf("\tCommand after pre-shunter removal: %s\n", preparedTokens.toString()); + + if(!postfixMode && !prefixMode) { shuntedTokens = new FunctionalList<>(); - success = shunt.shuntTokens(lexedTokens, shuntedTokens); + success = shunt.shuntTokens(preparedTokens, shuntedTokens); if(!success) return false; + } else if(prefixMode) { + preparedTokens.reverse(); + shuntedTokens = preparedTokens.map(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; + } + }); } if(debugMode && !postfixMode) System.out.printf("\tCommand after shunting: %s\n", shuntedTokens.toString()); + IList<Token> readyTokens = shuntedTokens.flatMap(tk -> { + if(tk.type == Token.Type.TOKGROUP) { + return tk.tokenValues; + } else { + return new FunctionalList<>(tk); + } + }); + + if(debugMode && !postfixMode) + System.out.printf("\tCommand after re-preshunting: %s\n", readyTokens.toString()); + + IList<ITree<Node>> astForest = new FunctionalList<>(); + success = parsr.parseTokens(readyTokens, astForest); + + if(!success) return false; + + if(debugMode) { + System.out.println("\tParsed forest of asts"); + int treeNo = 1; + + for(ITree<Node> ast : astForest) { + System.out.println("\t\tTree " + treeNo + " in forest:\n\t\t " + ast); + + System.out.println("\t\tEvaluates to " + eval.evaluate(ast)); + treeNo += 1; + } + } + return true; } @@ -253,11 +404,13 @@ public class DiceLangEngine { if(litTokens.containsKey(token)) { tk = new Token(litTokens.get(token)); } else { - switch(token) { - case "(": - case ")": - case "[": - case "]": + switch(token.charAt(0)) { + case '(': + case ')': + case '[': + case ']': + case '{': + case '}': tk = tokenizeGrouping(token); break; default: @@ -272,19 +425,28 @@ public class DiceLangEngine { Token tk = Token.NIL_TOKEN; if(StringUtils.containsOnly(token, "\\" + token.charAt(0))) { - switch(token) { - case "(": + switch(token.charAt(0)) { + case '(': tk = new Token(OPAREN, token.length()); break; - case ")": + case ')': tk = new Token(CPAREN, token.length()); break; - case "[": + case '[': tk = new Token(OBRACKET, token.length()); break; - case "]": + case ']': tk = new Token(CBRACKET, token.length()); break; + case '{': + tk = new Token(OBRACE, token.length()); + break; + case '}': + tk = new Token(CBRACE, token.length()); + break; + default: + System.out.println("\tERROR: Unrecognized grouping token " + token); + break; } } @@ -343,7 +505,7 @@ public class DiceLangEngine { private IList<String> deaffixTokens(IList<String> tokens, List<IPair<String, String>> deaffixTokens) { Deque<String> working = new LinkedList<>(); - for(String tk : tokens.toIterable()) { + for(String tk : tokens) { working.add(tk); } @@ -359,7 +521,6 @@ public class DiceLangEngine { Pattern opRegexEnding = Pattern.compile(opRegex + "\\Z"); for(String tk : working) { - // @Incomplete if(opRegexOnly.matcher(tk).matches()) { // The string contains only the operator newWorking.add(tk); @@ -379,7 +540,6 @@ public class DiceLangEngine { boolean startsWith = startMatcher.find(); boolean endsWith = endMatcher.find(); - boolean doSplit = medianMatcher.find(); medianMatcher.reset(); diff --git a/dice-lang/src/bjc/dicelang/v2/Evaluator.java b/dice-lang/src/bjc/dicelang/v2/Evaluator.java new file mode 100644 index 0000000..d9efbea --- /dev/null +++ b/dice-lang/src/bjc/dicelang/v2/Evaluator.java @@ -0,0 +1,262 @@ +package bjc.dicelang.v2; + +import bjc.utils.data.ITree; +import bjc.utils.data.Tree; +import bjc.utils.data.TopDownTransformResult; + +public class Evaluator { + public static class Result { + public static enum Type { + FAILURE, + INT, FLOAT, DICE + } + + public final Type type; + + // These may or may not have values based + // off of the result type + public long intVal; + public double floatVal; + public DiceBox.DieExpression diceVal; + + public Result(Type typ) { + type = typ; + } + + public Result(Type typ, long iVal) { + this(typ); + + intVal = iVal; + } + + public Result(Type typ, double dVal) { + this(typ); + + floatVal = dVal; + } + + public Result(Type typ, DiceBox.DieExpression dVal) { + this(typ); + + diceVal = dVal; + } + + public String toString() { + switch(type) { + case INT: + return type.toString() + "(" + intVal + ")"; + case FLOAT: + return type.toString() + "(" + floatVal + ")"; + case DICE: + return type.toString() + "(" + diceVal + ")"; + case FAILURE: + return type.toString(); + default: + return "Unknown result type " + type.toString(); + } + } + } + + private DiceLangEngine eng; + + public Evaluator(DiceLangEngine en) { + eng = en; + } + + public Result evaluate(ITree<Node> comm) { + return comm.topDownTransform(this::pickEvaluationType, this::evaluateNode).getHead().resultVal; + } + + private TopDownTransformResult pickEvaluationType(Node nd) { + switch(nd.type) { + default: + return TopDownTransformResult.PUSHDOWN; + } + } + + private ITree<Node> evaluateNode(ITree<Node> ast) { + switch(ast.getHead().type) { + case UNARYOP: + System.out.println("\tEVALUATOR ERROR: Unary operator evaluation isn't supported yet"); + return new Tree<>(new Node(Node.Type.RESULT, new Result(Result.Type.FAILURE))); + case BINOP: + return evaluateBinaryOp(ast); + case TOKREF: + return evaluateTokenRef(ast.getHead().tokenVal); + default: + System.out.println("\tERROR: Unknown node in evaluator: " + ast.getHead().type); + return new Tree<>(new Node(Node.Type.RESULT, new Result(Result.Type.FAILURE))); + } + } + + private ITree<Node> evaluateBinaryOp(ITree<Node> ast) { + Token.Type binOp = ast.getHead().operatorType; + + if(ast.getChildrenCount() != 2) { + System.out.println("\tERROR: Binary operators only take two operands"); + return new Tree<>(new Node(Node.Type.RESULT, new Result(Result.Type.FAILURE))); + } + + ITree<Node> left = ast.getChild(0); + ITree<Node> right = ast.getChild(1); + + switch(binOp) { + case ADD: + case SUBTRACT: + case MULTIPLY: + case DIVIDE: + case IDIVIDE: + return evaluateMathBinary(binOp, left.getHead().resultVal, right.getHead().resultVal); + default: + System.out.println("\tERROR: Unknown binary operator: " + binOp); + return new Tree<>(new Node(Node.Type.RESULT, new Result(Result.Type.FAILURE))); + } + } + + private ITree<Node> evaluateMathBinary(Token.Type op, Result left, Result right) { + Result.Type resultType; + + if(left.type == Result.Type.DICE || right.type == Result.Type.DICE) { + System.out.println("\tEVALUATOR ERROR: Math on dice isn't supported yet"); + } + + Result res = null; + + switch(op) { + case ADD: + if(left.type == Result.Type.INT) { + if(right.type == Result.Type.INT) { + res = new Result(Result.Type.INT, left.intVal + right.intVal); + } else { + res = new Result(Result.Type.FLOAT, left.intVal + right.floatVal); + } + } else { + if(right.type == Result.Type.INT) { + res = new Result(Result.Type.FLOAT, left.floatVal + right.intVal); + } else { + res = new Result(Result.Type.FLOAT, left.floatVal + right.floatVal); + } + } + break; + case SUBTRACT: + if(left.type == Result.Type.INT) { + if(right.type == Result.Type.INT) { + res = new Result(Result.Type.INT, left.intVal - right.intVal); + } else { + res = new Result(Result.Type.FLOAT, left.intVal - right.floatVal); + } + } else { + if(right.type == Result.Type.INT) { + res = new Result(Result.Type.FLOAT, left.floatVal - right.intVal); + } else { + res = new Result(Result.Type.FLOAT, left.floatVal - right.floatVal); + } + } + break; + case MULTIPLY: + if(left.type == Result.Type.INT) { + if(right.type == Result.Type.INT) { + res = new Result(Result.Type.INT, left.intVal * right.intVal); + } else { + res = new Result(Result.Type.FLOAT, left.intVal * right.floatVal); + } + } else { + if(right.type == Result.Type.INT) { + res = new Result(Result.Type.FLOAT, left.floatVal * right.intVal); + } else { + res = new Result(Result.Type.FLOAT, left.floatVal * right.floatVal); + } + } + break; + case DIVIDE: + if(left.type == Result.Type.INT) { + if(right.type == Result.Type.INT) { + if(right.intVal == 0) { + System.out.println("\tERROR: Attempted divide by zero"); + res = new Result(Result.Type.FAILURE); + } else { + res = new Result(Result.Type.FLOAT, left.intVal / right.intVal); + } + } else { + if(right.floatVal == 0) { + System.out.println("\tERROR: Attempted divide by zero"); + res = new Result(Result.Type.FAILURE); + } else { + res = new Result(Result.Type.FLOAT, left.intVal / right.floatVal); + } + } + } else { + if(right.type == Result.Type.INT) { + if(right.intVal == 0) { + System.out.println("\tERROR: Attempted divide by zero"); + res = new Result(Result.Type.FAILURE); + } else { + res = new Result(Result.Type.FLOAT, left.floatVal / right.intVal); + } + } else { + if(right.floatVal == 0) { + System.out.println("\tERROR: Attempted divide by zero"); + res = new Result(Result.Type.FAILURE); + } else { + res = new Result(Result.Type.FLOAT, left.floatVal / right.floatVal); + } + } + } + break; + case IDIVIDE: + if(left.type == Result.Type.INT) { + if(right.type == Result.Type.INT) { + if(right.intVal == 0) { + System.out.println("\tERROR: Attempted divide by zero"); + res = new Result(Result.Type.FAILURE); + } else { + res = new Result(Result.Type.INT, (int) (left.intVal / right.intVal)); + } + } else { + if(right.floatVal == 0) { + System.out.println("\tERROR: Attempted divide by zero"); + res = new Result(Result.Type.FAILURE); + } else { + res = new Result(Result.Type.INT, (int) (left.intVal / right.floatVal)); + } + } + } else { + if(right.type == Result.Type.INT) { + if(right.intVal == 0) { + System.out.println("\tERROR: Attempted divide by zero"); + res = new Result(Result.Type.FAILURE); + } else { + res = new Result(Result.Type.INT, (int) (left.floatVal / right.intVal)); + } + } else { + if(right.floatVal == 0) { + System.out.println("\tERROR: Attempted divide by zero"); + res = new Result(Result.Type.FAILURE); + } else { + res = new Result(Result.Type.INT, (int) (left.floatVal / right.floatVal)); + } + } + } + break; + default: + System.out.println("\tERROR: Unknown math binary operator: " + op); + return new Tree<>(new Node(Node.Type.RESULT, new Result(Result.Type.FAILURE))); + } + + return new Tree<>(new Node(Node.Type.RESULT, res)); + } + + private ITree<Node> evaluateTokenRef(Token tk) { + switch(tk.type) { + case INT_LIT: + return new Tree<>(new Node(Node.Type.RESULT, new Result(Result.Type.INT, tk.intValue))); + case FLOAT_LIT: + return new Tree<>(new Node(Node.Type.RESULT, new Result(Result.Type.FLOAT, tk.floatValue))); + case DICE_LIT: + return new Tree<>(new Node(Node.Type.RESULT, new Result(Result.Type.DICE, tk.diceValue))); + default: + System.out.println("\tERROR: Unknown token ref: " + tk.type); + return new Tree<>(new Node(Node.Type.RESULT, new Result(Result.Type.FAILURE))); + } + } +} diff --git a/dice-lang/src/bjc/dicelang/v2/Node.java b/dice-lang/src/bjc/dicelang/v2/Node.java new file mode 100644 index 0000000..bd191d2 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/v2/Node.java @@ -0,0 +1,82 @@ +package bjc.dicelang.v2; + +public class Node { + public static enum Type { + ROOT, TOKREF, + UNARYOP, BINOP, + GROUP, OGROUP, + RESULT + } + + public static enum GroupType { + ARRAY, CODE + } + + public final Type type; + + // These can have or not have values based of the node type + public Token tokenVal; + public Token.Type operatorType; + public GroupType groupType; + public Evaluator.Result resultVal; + + public Node(Type typ) { + type = typ; + } + + public Node(Type typ, Token tokenVl) { + this(typ); + + tokenVal = tokenVl; + } + + public Node(Type typ, Token.Type opType) { + this(typ); + + operatorType = opType; + } + + public Node(Type typ, GroupType grupType) { + this(typ); + + groupType = grupType; + } + + public Node(Type typ, Evaluator.Result res) { + this(typ); + + resultVal = res; + } + + public String toString() { + switch(type) { + case UNARYOP: + case BINOP: + return "(" + type.name() + " : " + operatorType + ")"; + case OGROUP: + case TOKREF: + return "(" + type.name() + " : " + tokenVal + ")"; + case GROUP: + return "(" + type.name() + " : " + groupType + ")"; + case RESULT: + return "(" + type.name() + " : " + resultVal + ")"; + default: + return "Unknown node type " + type; + } + } + + public boolean equals(Object other) { + if(!(other instanceof Node)) return false; + + Node otk = (Node)other; + + if(otk.type != type) return false; + + switch(type) { + case OGROUP: + return tokenVal.equals(otk.tokenVal); + default: + return true; + } + } +} diff --git a/dice-lang/src/bjc/dicelang/v2/Parser.java b/dice-lang/src/bjc/dicelang/v2/Parser.java new file mode 100644 index 0000000..0e778b4 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/v2/Parser.java @@ -0,0 +1,147 @@ +package bjc.dicelang.v2; + +import java.util.Deque; +import java.util.LinkedList; + +import bjc.utils.data.ITree; +import bjc.utils.data.Tree; +import bjc.utils.funcdata.IList; + +import static bjc.dicelang.v2.Node.Type.*; +import static bjc.dicelang.v2.Token.Type.*; + +public class Parser { + public Parser() { + + } + + public boolean parseTokens(IList<Token> tokens, + IList<ITree<Node>> results) { + Deque<ITree<Node>> working = new LinkedList<>(); + + for(Token tk : tokens) { + switch(tk.type) { + case OBRACKET: + case OBRACE: + working.push(new Tree<>(new Node(OGROUP, tk))); + break; + case CBRACKET: + case CBRACE: + if(working.size() == 0) { + System.out.println("\tERROR: Group closing with no possible group opener"); + return false; + } + + ITree<Node> groupNode = null; + switch(tk.type) { + 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: + break; + } + Token matching = null; + + if(tk.type == CBRACKET) { + matching = new Token(Token.Type.OBRACKET, tk.intValue); + } else if(tk.type == CBRACE) { + matching = new Token(Token.Type.OBRACE, tk.intValue); + } + + ITree<Node> matchNode = new Tree<>(new Node(OGROUP, matching)); + + if(!working.contains(matchNode)) { + System.out.println("\tERROR: Found group closing without group opener: (closing was " + tk + ", matcher was " + + matchNode + ")"); + + System.out.println("\tCurrent forest is: "); + + int treeNo = 1; + for(ITree<Node> ast : working) { + System.out.println("Tree " + treeNo++ + ": " + ast.toString()); + } + return false; + } else { + Deque<ITree<Node>> childs = new LinkedList<>(); + + while(!working.peek().equals(matchNode)) { + childs.push(working.pop()); + } + + // Discard opener + working.pop(); + + for(ITree<Node> child : childs) { + groupNode.addChild(child); + } + + working.push(groupNode); + } + break; + case LET: + case BIND: + if(working.size() < 2) { + System.out.println("\tERROR: Let and bind require at least two operands"); + } else { + ITree<Node> right = working.pop(); + ITree<Node> left = working.pop(); + + ITree<Node> opNode = new Tree<>(new Node(BINOP, tk.type)); + + working.push(opNode); + } + break; + case ADD: + case SUBTRACT: + case MULTIPLY: + case DIVIDE: + case IDIVIDE: + case DICEGROUP: + case DICECONCAT: + case DICELIST: + if(working.size() == 0) { + System.out.println("\tERROR: Binary operator " + tk.type + " requires more operands than provided."); + return false; + } else if(working.size() == 1) { + ITree<Node> operand = working.pop(); + + ITree<Node> opNode = new Tree<>(new Node(UNARYOP, tk.type)); + + opNode.addChild(operand); + + working.push(opNode); + } else { + ITree<Node> right = working.pop(); + ITree<Node> left = working.pop(); + + ITree<Node> opNode = new Tree<>(new Node(BINOP, tk.type)); + + opNode.addChild(left); + opNode.addChild(right); + + working.push(opNode); + } + break; + case INT_LIT: + case FLOAT_LIT: + case STRING_LIT: + case VREF: + case DICE_LIT: + working.push(new Tree<>(new Node(TOKREF, tk))); + break; + default: + System.out.println("\tERROR: Unrecognized token type in parsing: " + tk.type); + return false; + } + } + + for(ITree<Node> ast : working) { + results.add(ast); + } + + return true; + } +} diff --git a/dice-lang/src/bjc/dicelang/v2/Shunter.java b/dice-lang/src/bjc/dicelang/v2/Shunter.java index b0a822a..7e17e6f 100644 --- a/dice-lang/src/bjc/dicelang/v2/Shunter.java +++ b/dice-lang/src/bjc/dicelang/v2/Shunter.java @@ -25,9 +25,9 @@ public class Shunter { // applied to operator tokens private Set<Token.Type> unaryAdverbs; - private final int MATH_PREC = 20; - private final int DICE_PREC = 10; - private final int EXPR_PREC = 0; + private final int MATH_PREC = 20; + private final int DICE_PREC = 10; + private final int EXPR_PREC = 0; public Shunter() { ops = new FunctionalMap<>(); @@ -53,12 +53,13 @@ public class Shunter { public boolean shuntTokens(IList<Token> tks, IList<Token> returned) { Deque<Token> opStack = new LinkedList<>(); - boolean unaryMode = false; + boolean unaryMode = false; Deque<Token> unaryOps = new LinkedList<>(); + Deque<Token> currReturned = new LinkedList<>(); + for(Token tk : tks.toIterable()) { if(unaryMode) { - // @TODO finish unary mode if(unaryAdjectives.contains(tk.type) || unaryAdverbs.contains(tk.type)) { unaryOps.push(tk); continue; @@ -79,8 +80,8 @@ public class Shunter { return false; } - returned.add(tk); - returned.add(unaryOps.pop()); + currReturned.addLast(tk); + currReturned.addLast(unaryOps.pop()); } else if (unaryAdverbs.contains(currOperator.type)) { if(opStack.size() < 1) { System.out.printf("\tError: Unary operators %s is an adverb," @@ -101,8 +102,8 @@ public class Shunter { return false; } - returned.add(tk); - returned.add(unaryOps.pop()); + currReturned.addLast(tk); + currReturned.addLast(unaryOps.pop()); } if(unaryOps.isEmpty()) unaryMode = false; @@ -115,34 +116,75 @@ public class Shunter { } else if(ops.containsKey(tk.type)) { while(!opStack.isEmpty() && isHigherPrec(tk.type, opStack.peek().type)) { - returned.add(opStack.pop()); + currReturned.addLast(opStack.pop()); } opStack.push(tk); - } else if(tk.type == OPAREN) { + } else if(tk.type == OPAREN || tk.type == OBRACE) { opStack.push(tk); - } else if(tk.type == CPAREN) { - Token currTk = opStack.peek(); - while(currTk.type != OPAREN && currTk.intValue != tk.intValue) { - if(opStack.isEmpty()) { - System.out.printf("\tError: Could not find matching parenthesis" - + " with matching level %d\n", tk.intValue); + if(tk.type == OBRACE) + currReturned.addLast(tk); + } else if(tk.type == CPAREN || tk.type == CBRACE) { + Token matching = null; + + switch(tk.type) { + case CPAREN: + matching = new Token(OPAREN, tk.intValue); + break; + case CBRACE: + matching = new Token(OBRACE, tk.intValue); + break; + default: + break; + } + + if(!opStack.contains(matching)) { + System.out.printf("\tError: Could not find matching grouping " + + tk.type); + + return false; + } + + while(!opStack.peek().equals(matching)) { + currReturned.addLast(opStack.pop()); + } + + if(tk.type == CBRACE) { + currReturned.addLast(tk); + } + + opStack.pop(); + } else if(tk.type == GROUPSEP) { + IList<Token> group = new FunctionalList<>(); - return false; - } + while(currReturned.size() != 0 && !currReturned.peek().isGrouper()) { + group.add(currReturned.pop()); + } - returned.add(opStack.pop()); + while(opStack.size() != 0 && !opStack.peek().isGrouper()) { + group.add(opStack.pop()); } + + if(currReturned.size() == 0) { + System.out.println("\tERROR: Didn't find grouper for group seperator to attach to"); + return false; + } + + currReturned.addLast(new Token(TOKGROUP, group)); } else { - returned.add(tk); + currReturned.addLast(tk); } } } // Flush leftover operators while(!opStack.isEmpty()) { - returned.add(opStack.pop()); + currReturned.addLast(opStack.pop()); + } + + for(Token tk : currReturned) { + returned.add(tk); } return true; diff --git a/dice-lang/src/bjc/dicelang/v2/StreamEngine.java b/dice-lang/src/bjc/dicelang/v2/StreamEngine.java index afd4bc1..39bbc0d 100644 --- a/dice-lang/src/bjc/dicelang/v2/StreamEngine.java +++ b/dice-lang/src/bjc/dicelang/v2/StreamEngine.java @@ -11,23 +11,29 @@ public class StreamEngine { private Tape<IList<String>> streams; private IList<String> currStream; - public StreamEngine(DiceLangEngine eng) { + public StreamEngine(DiceLangEngine engine) { + eng = engine; + } + + private void init() { streams = new SingleTape<>(); currStream = new FunctionalList<>(); - streams.append(currStream); + streams.insertBefore(currStream); } public boolean doStreams(String[] toks, IList<String> dest) { + init(); + for(String tk : toks) { - if(tk.startsWith("{@")) { + if(tk.startsWith("{@S")) { if(!processCommand(tk)) return false; } else { currStream.add(tk); } } - for(String tk : currStream.toIterable()) { + for(String tk : currStream) { dest.add(tk); } @@ -35,38 +41,49 @@ public class StreamEngine { } private boolean processCommand(String tk) { - switch(tk.charAt(2)) { - case '+': - streams.add(new FunctionalList<>()); - break; - case '>': - if(!streams.right()) { - System.out.println("\tERROR: Attempted to switch to non-existent stream"); - return false; - } + char[] comms = null; - currStream = streams.item(); - break; - case '<': - if(!streams.left()) { - System.out.println("\tERROR: Attempted to switch to non-existent stream"); - return false; - } + if(tk.length() > 5) { + comms = tk.substring(3, tk.length() - 1).toCharArray(); + } else { + comms = new char[1]; + comms[0] = tk.charAt(3); + } + + for(char comm : comms) { + switch(comm) { + case '+': + streams.insertAfter(new FunctionalList<>()); + break; + case '>': + if(!streams.right()) { + System.out.println("\tERROR: Attempted to switch to non-existent stream"); + return false; + } + + currStream = streams.item(); + break; + case '<': + if(!streams.left()) { + System.out.println("\tERROR: Attempted to switch to non-existent stream"); + return false; + } - currStream = streams.item(); - break; - case '-': - if(streams.size() == 1) { - System.out.println("\tERROR: Cannot delete last stream"); - return false; - } else { - streams.remove(); currStream = streams.item(); - } - break; - default: - System.out.println("\tERROR: Unknown stream control command: " + tk); - return false; + break; + case '-': + if(streams.size() == 1) { + System.out.println("\tERROR: Cannot delete last stream"); + return false; + } else { + streams.remove(); + currStream = streams.item(); + } + break; + default: + System.out.println("\tERROR: Unknown stream control command: " + tk); + return false; + } } return true; diff --git a/dice-lang/src/bjc/dicelang/v2/Token.java b/dice-lang/src/bjc/dicelang/v2/Token.java index 9825dae..e6f22a5 100644 --- a/dice-lang/src/bjc/dicelang/v2/Token.java +++ b/dice-lang/src/bjc/dicelang/v2/Token.java @@ -1,5 +1,7 @@ package bjc.dicelang.v2; +import bjc.utils.funcdata.IList; + /** * Lexer token */ @@ -10,25 +12,33 @@ public class Token { * Possible token types */ public static enum Type { - ADD, SUBTRACT, - MULTIPLY, - DIVIDE, IDIVIDE, - INT_LIT, FLOAT_LIT, STRING_LIT, + // Natural tokens + // These are produced from lexemes + ADD, SUBTRACT, + MULTIPLY, + DIVIDE, IDIVIDE, + INT_LIT, FLOAT_LIT, STRING_LIT, VREF, DICE_LIT, DICEGROUP, DICECONCAT, DICELIST, - LET, BIND, - OPAREN, CPAREN, + LET, BIND, + OPAREN, CPAREN, OBRACKET, CBRACKET, - NIL, + OBRACE, CBRACE, + // Synthetic tokens + // These are produced when needed + NIL, PRESHUNT, GROUPSEP, + TOKGROUP } public final Type type; // At most one of these is valid // based on the token type - public long intValue; - public double floatValue; + public long intValue; + public double floatValue; + public String stringValue; public DiceBox.DieExpression diceValue; + public IList<Token> tokenValues; public Token(Type typ) { type = typ; @@ -46,12 +56,24 @@ public class Token { floatValue = val; } + public Token(Type typ, String val) { + this(typ); + + stringValue = val; + } + public Token(Type typ, DiceBox.DieExpression val) { this(typ); diceValue = val; } + public Token(Type typ, IList<Token> tkVals) { + this(typ); + + tokenValues = tkVals; + } + public String toString() { switch(type) { case INT_LIT: @@ -61,6 +83,8 @@ public class Token { case CPAREN: case OBRACKET: case CBRACKET: + case OBRACE: + case CBRACE: return type.toString() + "(" + intValue + ")"; case FLOAT_LIT: @@ -69,8 +93,38 @@ public class Token { case DICE_LIT: return type.toString() + "(" + diceValue + ")"; + case TOKGROUP: + return type.toString() + "(" + + tokenValues + ")"; default: return type.toString(); } } + + public boolean equals(Object other) { + if(!(other instanceof Token)) return false; + + Token otk = (Token)other; + + if(otk.type != type) return false; + + switch(type) { + case OBRACE: + case OBRACKET: + return intValue == otk.intValue; + default: + return true; + } + } + + public boolean isGrouper() { + switch(type) { + case OPAREN: + case OBRACE: + case OBRACKET: + return true; + default: + return false; + } + } } |
