summaryrefslogtreecommitdiff
path: root/dice-lang/src/bjc
diff options
context:
space:
mode:
Diffstat (limited to 'dice-lang/src/bjc')
-rw-r--r--dice-lang/src/bjc/dicelang/v2/Define.java20
-rw-r--r--dice-lang/src/bjc/dicelang/v2/DiceLangConsole.java48
-rw-r--r--dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java224
-rw-r--r--dice-lang/src/bjc/dicelang/v2/Evaluator.java262
-rw-r--r--dice-lang/src/bjc/dicelang/v2/Node.java82
-rw-r--r--dice-lang/src/bjc/dicelang/v2/Parser.java147
-rw-r--r--dice-lang/src/bjc/dicelang/v2/Shunter.java86
-rw-r--r--dice-lang/src/bjc/dicelang/v2/StreamEngine.java83
-rw-r--r--dice-lang/src/bjc/dicelang/v2/Token.java72
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;
+ }
+ }
}