summaryrefslogtreecommitdiff
path: root/dice-lang/src
diff options
context:
space:
mode:
authorbculkin2442 <bjculkin@mix.wvu.edu>2017-03-08 14:15:40 -0500
committerbculkin2442 <bjculkin@mix.wvu.edu>2017-03-08 14:15:40 -0500
commitd15bcca6cd2a18850a0d109c4a8292ef7f20ac22 (patch)
tree14321c17cc633365f2855352cf9880107d791297 /dice-lang/src
parent6b2e58c60a540874e17f7b50e9f892811275f0ff (diff)
SCL is now partially implemented
Diffstat (limited to 'dice-lang/src')
-rw-r--r--dice-lang/src/bjc/dicelang/CLIArgsParser.java5
-rw-r--r--dice-lang/src/bjc/dicelang/DiceLangEngine.java7
-rw-r--r--dice-lang/src/bjc/dicelang/Errors.java23
-rw-r--r--dice-lang/src/bjc/dicelang/Evaluator.java4
-rw-r--r--dice-lang/src/bjc/dicelang/scl/StreamControlEngine.java501
-rw-r--r--dice-lang/src/bjc/dicelang/scl/StreamEngine.java (renamed from dice-lang/src/bjc/dicelang/StreamEngine.java)113
6 files changed, 614 insertions, 39 deletions
diff --git a/dice-lang/src/bjc/dicelang/CLIArgsParser.java b/dice-lang/src/bjc/dicelang/CLIArgsParser.java
new file mode 100644
index 0000000..b8e0baf
--- /dev/null
+++ b/dice-lang/src/bjc/dicelang/CLIArgsParser.java
@@ -0,0 +1,5 @@
+package bjc.dicelang;
+
+public class CLIArgsParser {
+
+}
diff --git a/dice-lang/src/bjc/dicelang/DiceLangEngine.java b/dice-lang/src/bjc/dicelang/DiceLangEngine.java
index 98e42fd..86a9aa6 100644
--- a/dice-lang/src/bjc/dicelang/DiceLangEngine.java
+++ b/dice-lang/src/bjc/dicelang/DiceLangEngine.java
@@ -1,5 +1,7 @@
package bjc.dicelang;
+import bjc.dicelang.scl.StreamEngine;
+
import bjc.utils.data.IPair;
import bjc.utils.data.ITree;
import bjc.utils.data.Pair;
@@ -10,9 +12,6 @@ import bjc.utils.funcdata.IList;
import bjc.utils.funcdata.IMap;
import bjc.utils.funcutils.ListUtils;
-import static bjc.dicelang.Errors.ErrorKey.*;
-import static bjc.dicelang.Token.Type.*;
-
import java.util.Comparator;
import java.util.Deque;
import java.util.Iterator;
@@ -21,6 +20,8 @@ import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import static bjc.dicelang.Errors.ErrorKey.*;
+import static bjc.dicelang.Token.Type.*;
/**
* Implements the orchestration necessary for processing DiceLang commands
*
diff --git a/dice-lang/src/bjc/dicelang/Errors.java b/dice-lang/src/bjc/dicelang/Errors.java
index 8c2b1d5..201a6c4 100644
--- a/dice-lang/src/bjc/dicelang/Errors.java
+++ b/dice-lang/src/bjc/dicelang/Errors.java
@@ -94,6 +94,17 @@ public class Errors {
// Unknown stream command
EK_STRM_INVCOM,
+ // SCL Errors
+ // Unknown SCL token
+ EK_SCL_INVTOKEN,
+ // Mismatched quote in SCL command
+ EK_SCL_MMQUOTE,
+ // Stack underflow in SCL command
+ EK_SCL_SUNDERFLOW,
+ // Unknown word in SCL command
+ EK_SCL_UNWORD,
+ // Invalid argument to SCL command
+ EK_SCL_INVARG,
}
public static enum ErrorMode {
@@ -236,6 +247,18 @@ public class Errors {
case EK_STRM_INVCOM:
System.out.printf("\tERROR: Unknown stream control command %s\n", args[0]);
break;
+ case EK_SCL_INVTOKEN:
+ System.out.printf("\tERROR: Unknown SCL token %s\n", args[0]);
+ break;
+ case EK_SCL_MMQUOTE:
+ System.out.printf("\tERROR: Mismatched delimiter in SCL command\n");
+ break;
+ case EK_SCL_SUNDERFLOW:
+ System.out.printf("\tERROR: Not enough items in stack for word %s\n", args[0]);
+ break;
+ case EK_SCL_UNWORD:
+ System.out.printf("\tERROR: Unknown word %s\n", args[0]);
+ break;
default:
System.out.printf("\tERROR ERROR: Unknown error key %s\n", key);
}
diff --git a/dice-lang/src/bjc/dicelang/Evaluator.java b/dice-lang/src/bjc/dicelang/Evaluator.java
index 0fadd60..87980d4 100644
--- a/dice-lang/src/bjc/dicelang/Evaluator.java
+++ b/dice-lang/src/bjc/dicelang/Evaluator.java
@@ -172,7 +172,7 @@ public class Evaluator {
EvaluatorResult opr = ast.getChild(0).getHead().resultVal;
if(opr.type != INT) {
- Errors.inst.printError(EK_EVAL_INVDCREATE, opr.type);
+ Errors.inst.printError(EK_EVAL_INVDCREATE, opr.type.toString());
}
return new Tree<>(new Node(Node.Type.RESULT,
@@ -182,7 +182,7 @@ public class Evaluator {
EvaluatorResult oprn = ast.getChild(0).getHead().resultVal;
if(oprn.type != INT) {
- Errors.inst.printError(EK_EVAL_INVDCREATE, oprn.type);
+ Errors.inst.printError(EK_EVAL_INVDCREATE, oprn.type.toString());
}
return new Tree<>(new Node(Node.Type.RESULT,
diff --git a/dice-lang/src/bjc/dicelang/scl/StreamControlEngine.java b/dice-lang/src/bjc/dicelang/scl/StreamControlEngine.java
new file mode 100644
index 0000000..fff8b3e
--- /dev/null
+++ b/dice-lang/src/bjc/dicelang/scl/StreamControlEngine.java
@@ -0,0 +1,501 @@
+package bjc.dicelang.scl;
+
+import bjc.dicelang.DoubleMatcher;
+import bjc.dicelang.Errors;
+
+import bjc.utils.esodata.Stack;
+import bjc.utils.esodata.Tape;
+import bjc.utils.funcdata.IList;
+import bjc.utils.funcdata.FunctionalList;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import static bjc.dicelang.Errors.ErrorKey.*;
+import static bjc.dicelang.scl.StreamControlEngine.Token.Type.*;
+
+/**
+ * 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.
+ */
+public class StreamControlEngine {
+ static class Token {
+ public static enum Type {
+ /*
+ * Natural tokens.
+ * These come directly from strings
+ */
+ ILIT, FLIT, BLIT,
+ SQUOTE, DQUOTE,
+ OBRACKET, OBRACE,
+ SYMBOL, WORD,
+
+ /*
+ * Synthetic tokens.
+ * These are produced from special tokens.
+ */
+ SLIT,
+ WORDS, ARRAY,
+
+ /*
+ * Word tokens
+ * These are subordinate to WORD tokens
+ */
+ /*
+ * Array manipulation
+ */
+ MAKEARRAY,
+ MAKEEXEC, MAKEUNEXEC,
+ /*
+ * Stream manipulation
+ */
+ NEWSTREAM,
+ LEFTSTREAM, RIGHTSTREAM,
+ DELETESTREAM, MERGESTREAM,
+ /*
+ * Stack manipulation
+ */
+ STACKCOUNT, STACKEMPTY,
+ DROP, NDROP,
+ NIP, NNIP,
+ }
+
+ /*
+ * The type of this token
+ */
+ public Type type;
+
+ /*
+ * Used for
+ * ILIT
+ */
+ public long intVal;
+ /*
+ * Used for
+ * FLIT
+ */
+ public double floatVal;
+ /*
+ * Used for
+ * BLIT
+ */
+ public boolean boolVal;
+ /*
+ * Used for
+ * SYMBOL
+ * SLIT
+ */
+ public String stringVal;
+ /*
+ * Used for
+ * WORD
+ */
+ public Token tokenVal;
+ /*
+ * Used for
+ * WORDS
+ * ARRAY
+ */
+ public IList<Token> tokenVals;
+
+ public Token(Type typ) {
+ type = typ;
+ }
+
+ public Token(Type typ, long iVal) {
+ this(typ);
+
+ intVal = iVal;
+ }
+
+ public Token(Type typ, double dVal) {
+ this(typ);
+
+ floatVal = dVal;
+ }
+
+ public Token(Type typ, boolean bVal) {
+ this(typ);
+
+ boolVal = bVal;
+ }
+
+ public Token(Type typ, String sVal) {
+ this(typ);
+
+ stringVal = sVal;
+ }
+
+ public Token(Type typ, Token tVal) {
+ this(typ);
+
+ tokenVal = tVal;
+ }
+
+ public Token(Type typ, Token.Type tVal) {
+ this(typ, new Token(tVal));
+ }
+
+
+ public Token(Type typ, IList<Token> tVals) {
+ this(typ);
+
+ tokenVals = tVals;
+ }
+
+ public static Token tokenizeString(String token) {
+ if(litTokens.containsKey(token)) {
+ return new Token(litTokens.get(token));
+ } else if(token.startsWith("\\")) {
+ return new Token(SYMBOL, token.substring(1));
+ } else if(builtinWords.containsKey(token)) {
+ return new Token(WORD, builtinWords.get(token));
+ } else if(token.equals("true")) {
+ return new Token(BLIT, true);
+ } else if(token.equals("false")) {
+ return new Token(BLIT, false);
+ } else if(intLitPattern.matcher(token).matches()) {
+ return new Token(ILIT, Long.parseLong(token));
+ } else if(DoubleMatcher.floatingLiteral.matcher(token).matches()) {
+ return new Token(FLIT, Double.parseDouble(token));
+ } else {
+ Errors.inst.printError(EK_SCL_INVTOKEN, token);
+ return null;
+ }
+ }
+
+ private static final Pattern intLitPattern = Pattern.compile("\\A[+\\-]?\\d+\\Z");
+
+ private static final Map<String, Token.Type> litTokens;
+ private static final Map<String, Token.Type> builtinWords;
+
+ static {
+ litTokens = new HashMap<>();
+
+ litTokens.put("'", SQUOTE);
+ litTokens.put("\"", DQUOTE);
+ litTokens.put("[", OBRACKET);
+ litTokens.put("{", OBRACE);
+
+ builtinWords = new HashMap<>();
+
+ builtinWords.put("makearray", MAKEARRAY);
+ builtinWords.put("+stream", NEWSTREAM);
+ builtinWords.put(">stream", LEFTSTREAM);
+ builtinWords.put("<stream", RIGHTSTREAM);
+ builtinWords.put("-stream", DELETESTREAM);
+ builtinWords.put("<-stream", MERGESTREAM);
+ builtinWords.put("cvx", MAKEEXEC);
+ builtinWords.put("cvux", MAKEUNEXEC);
+ builtinWords.put("#", STACKCOUNT);
+ builtinWords.put("empty?", STACKEMPTY);
+ builtinWords.put("drop", DROP);
+ builtinWords.put("ndrop", NDROP);
+ builtinWords.put("nip", NIP);
+ builtinWords.put("nnip", NNIP);
+ }
+ }
+
+ private StreamEngine eng;
+
+ private Stack<Token> curStack;
+
+ private Map<String, Token> words;
+
+ public StreamControlEngine(StreamEngine eng) {
+ this.eng = eng;
+
+ words = new HashMap<>();
+ }
+
+ /**
+ * Run a SCL program.
+ *
+ * @param tokens The program to run
+ *
+ * @return Whether the program executed succesfully
+ */
+ public boolean runProgram(String[] tokens) {
+ for(int i = 0; i < tokens.length; i++) {
+ String token = tokens[i];
+ Token tok = Token.tokenizeString(token);
+
+ if(tok == null) {
+ return false;
+ }
+
+ switch(tok.type) {
+ case SQUOTE:
+ i = handleSingleQuote(i, tokens);
+ if(i == -1) return false;
+ break;
+ case OBRACKET:
+ i = handleDelim(i, tokens, "]");
+ if(i == -1) return false;
+ break;
+ case OBRACE:
+ i = handleDelim(i, tokens, "}");
+ if(i == -1) return false;
+ Token brak = curStack.pop();
+ curStack.push(new Token(ARRAY, brak.tokenVals));
+ break;
+ case WORD:
+ handleWord(tok);
+ break;
+ default:
+ curStack.push(tok);
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean handleWord(Token tk) {
+ boolean succ = true;
+
+ 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;
+ }
+
+ return true;
+ }
+
+ private boolean handleNNip() {
+ Token num = curStack.pop();
+
+ if(num.type != ILIT) {
+ Errors.inst.printError(EK_SCL_INVARG, num.type.toString());
+ return false;
+ }
+
+ int n = (int)num.intVal;
+
+ if(curStack.size() < n) {
+ Errors.inst.printError(EK_SCL_SUNDERFLOW, NNIP.toString());
+ return false;
+ }
+
+ curStack.nip(n);
+ return true;
+ }
+
+ private boolean handleNDrop() {
+ Token num = curStack.pop();
+
+ if(num.type != ILIT) {
+ Errors.inst.printError(EK_SCL_INVARG, num.type.toString());
+ return false;
+ }
+
+ int n = (int)num.intVal;
+
+ if(curStack.size() < n) {
+ Errors.inst.printError(EK_SCL_SUNDERFLOW, NDROP.toString());
+ return false;
+ }
+
+ curStack.drop(n);
+ return true;
+ }
+
+ private boolean toggleExec(boolean exec) {
+ Token top = curStack.top();
+
+ if(exec) {
+ if(top.type != ARRAY) {
+ Errors.inst.printError(EK_SCL_INVARG, top.toString());
+ return false;
+ }
+
+ top.type = WORDS;
+ } else {
+ if(top.type != WORDS) {
+ Errors.inst.printError(EK_SCL_INVARG, top.toString());
+ return false;
+ }
+
+ top.type = ARRAY;
+ }
+
+ return true;
+ }
+
+ private boolean makeArray() {
+ Token num = curStack.pop();
+
+ if(num.type != ILIT) {
+ Errors.inst.printError(EK_SCL_INVARG, num.type.toString());
+ }
+
+ IList<Token> arr = new FunctionalList<>();
+
+ for(int i = 0; i < num.intVal; i++) {
+ arr.add(curStack.pop());
+ }
+
+ curStack.push(new Token(ARRAY, arr));
+
+ return true;
+ }
+
+ private int handleDelim(int i, String[] tokens, String delim) {
+ IList<Token> toks = new FunctionalList<>();
+
+ int n = i + 1;
+ if(n >= tokens.length) {
+ Errors.inst.printError(EK_SCL_MMQUOTE);
+ return -1;
+ }
+ String tok = tokens[n];
+
+ while(!tok.equals(delim)) {
+ Token ntok = Token.tokenizeString(tok);
+
+ 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:
+ i = handleDelim(i, tokens, "}");
+ if(i == -1) return -1;
+ Token brak = curStack.pop();
+ toks.add(new Token(ARRAY, brak.tokenVals));
+ break;
+ default:
+ toks.add(ntok);
+ }
+ /*
+ * Move to the next token
+ */
+ n += 1;
+ if(n >= tokens.length) {
+ Errors.inst.printError(EK_SCL_MMQUOTE);
+ return -1;
+ }
+ tok = tokens[n];
+ }
+
+ /*
+ * Skip the closing bracket
+ */
+ n += 1;
+
+ curStack.push(new Token(WORDS, toks));
+
+ return n;
+ }
+
+ private int handleSingleQuote(int i, String[] tokens) {
+ StringBuilder sb = new StringBuilder();
+
+ int n = i + 1;
+ if(n >= tokens.length) {
+ Errors.inst.printError(EK_SCL_MMQUOTE);
+ return -1;
+ }
+ String tok = tokens[n];
+
+ while(!tok.equals("'")) {
+ if(tok.matches("\\\\+'")) {
+ /*
+ * Handle escaped quotes.
+ */
+ sb.append(tok.substring(1));
+ } else {
+ sb.append(tok);
+ }
+
+ /*
+ * Move to the next token
+ */
+ n += 1;
+ if(n >= tokens.length) {
+ Errors.inst.printError(EK_SCL_MMQUOTE);
+ return -1;
+ }
+ tok = tokens[n];
+ }
+
+ /*
+ * Skip the single quote
+ */
+ n += 1;
+
+ curStack.push(new Token(SLIT, sb.toString()));
+
+ return n;
+ }
+}
diff --git a/dice-lang/src/bjc/dicelang/StreamEngine.java b/dice-lang/src/bjc/dicelang/scl/StreamEngine.java
index 5debe00..65e566d 100644
--- a/dice-lang/src/bjc/dicelang/StreamEngine.java
+++ b/dice-lang/src/bjc/dicelang/scl/StreamEngine.java
@@ -1,4 +1,7 @@
-package bjc.dicelang;
+package bjc.dicelang.scl;
+
+import bjc.dicelang.DiceLangEngine;
+import bjc.dicelang.Errors;
import bjc.utils.funcdata.FunctionalList;
import bjc.utils.funcdata.IList;
@@ -23,18 +26,23 @@ public class StreamEngine {
/*
* The engine we're attached to.
*/
- private DiceLangEngine eng;
+ DiceLangEngine eng;
/*
* Our streams.
*/
- private Tape<IList<String>> streams;
- private IList<String> currStream;
+ Tape<IList<String>> streams;
+ IList<String> currStream;
/*
* Saved streams
*/
- private TapeLibrary<IList<String>> savedStreams;
+ TapeLibrary<IList<String>> savedStreams;
+
+ /*
+ * Handler for SCL programs
+ */
+ private StreamControlEngine scleng;
/**
* Create a new stream engine.
@@ -45,6 +53,7 @@ public class StreamEngine {
eng = engine;
savedStreams = new TapeLibrary<>();
+ scleng = new StreamControlEngine(this);
}
private void init() {
@@ -113,6 +122,55 @@ public class StreamEngine {
return true;
}
+ public void newStream() {
+ streams.insertAfter(new FunctionalList<>());
+ }
+
+ public boolean rightStream() {
+ if(!streams.right()) {
+ Errors.inst.printError(EK_STRM_NONEX);
+ return false;
+ }
+
+ currStream = streams.item();
+ return true;
+ }
+
+ public boolean leftStream() {
+ if(!streams.left()) {
+ Errors.inst.printError(EK_STRM_NONEX);
+ return false;
+ }
+
+ currStream = streams.item();
+ return true;
+ }
+
+ public boolean deleteStream() {
+ if(streams.size() == 1) {
+ Errors.inst.printError(EK_STRM_LAST);
+ return false;
+ } else {
+ streams.remove();
+ currStream = streams.item();
+ }
+
+ return true;
+ }
+
+ public boolean mergeStream() {
+ if(streams.size() == 1) {
+ Errors.inst.printError(EK_STRM_LAST);
+ return false;
+ } else {
+ IList<String> stringLit = streams.remove();
+ currStream = streams.item();
+ currStream.add(ListUtils.collapseTokens(stringLit, " "));
+ }
+
+ return true;
+ }
+
private boolean processCommand(String tk) {
char[] comms = null;
@@ -123,45 +181,32 @@ public class StreamEngine {
comms[0] = tk.charAt(3);
}
+ boolean succ;
+
for(char comm : comms) {
switch(comm) {
case '+':
- streams.insertAfter(new FunctionalList<>());
+ newStream();
break;
case '>':
- if(!streams.right()) {
- Errors.inst.printError(EK_STRM_NONEX);
- return false;
- }
-
- currStream = streams.item();
+ succ = rightStream();
+ if(!succ) return false;
break;
case '<':
- if(!streams.left()) {
- Errors.inst.printError(EK_STRM_NONEX);
- return false;
- }
-
- currStream = streams.item();
+ succ = leftStream();
+ if(!succ) return false;
break;
case '-':
- if(streams.size() == 1) {
- Errors.inst.printError(EK_STRM_LAST);
- return false;
- } else {
- streams.remove();
- currStream = streams.item();
- }
+ succ = deleteStream();
+ if(!succ) return false;
break;
- case 'S':
- if(streams.size() == 1) {
- Errors.inst.printError(EK_STRM_LAST);
- return false;
- } else {
- IList<String> stringLit = streams.remove();
- currStream = streams.item();
- currStream.add(ListUtils.collapseTokens(stringLit, " "));
- }
+ 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);