diff options
| author | bculkin2442 <bjculkin@mix.wvu.edu> | 2017-03-08 14:15:40 -0500 |
|---|---|---|
| committer | bculkin2442 <bjculkin@mix.wvu.edu> | 2017-03-08 14:15:40 -0500 |
| commit | d15bcca6cd2a18850a0d109c4a8292ef7f20ac22 (patch) | |
| tree | 14321c17cc633365f2855352cf9880107d791297 /dice-lang/src | |
| parent | 6b2e58c60a540874e17f7b50e9f892811275f0ff (diff) | |
SCL is now partially implemented
Diffstat (limited to 'dice-lang/src')
| -rw-r--r-- | dice-lang/src/bjc/dicelang/CLIArgsParser.java | 5 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/DiceLangEngine.java | 7 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/Errors.java | 23 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/Evaluator.java | 4 | ||||
| -rw-r--r-- | dice-lang/src/bjc/dicelang/scl/StreamControlEngine.java | 501 | ||||
| -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); |
