From 63390a44ab57983472b87b8869923af02c36b45a Mon Sep 17 00:00:00 2001 From: student Date: Tue, 13 Feb 2018 11:14:30 -0500 Subject: Refactor SCL into its own module --- base/pom.xml | 10 +- base/src/bjc/dicelang/DiceLangEngine.java | 2 +- base/src/bjc/dicelang/scl/ArraySCLToken.java | 15 - base/src/bjc/dicelang/scl/BooleanSCLToken.java | 39 -- base/src/bjc/dicelang/scl/FloatSCLToken.java | 41 --- base/src/bjc/dicelang/scl/IntSCLToken.java | 18 - base/src/bjc/dicelang/scl/SCLToken.java | 92 ----- .../src/bjc/dicelang/scl/StreamControlConsole.java | 73 ---- base/src/bjc/dicelang/scl/StreamControlEngine.java | 397 --------------------- base/src/bjc/dicelang/scl/StreamEngine.java | 261 -------------- base/src/bjc/dicelang/scl/StringLitSCLToken.java | 13 - base/src/bjc/dicelang/scl/StringSCLToken.java | 46 --- base/src/bjc/dicelang/scl/SymbolSCLToken.java | 13 - base/src/bjc/dicelang/scl/WordListSCLToken.java | 48 --- base/src/bjc/dicelang/scl/WordSCLToken.java | 82 ----- base/src/bjc/dicelang/scl/WordsSCLToken.java | 15 - scl/.classpath | 26 ++ scl/.project | 23 ++ scl/.settings/org.eclipse.core.resources.prefs | 4 + scl/.settings/org.eclipse.jdt.core.prefs | 5 + scl/.settings/org.eclipse.m2e.core.prefs | 4 + scl/pom.xml | 60 ++-- scl/src/main/java/bjc/App.java | 13 - .../main/java/bjc/dicelang/scl/ArraySCLToken.java | 15 + .../java/bjc/dicelang/scl/BooleanSCLToken.java | 39 ++ scl/src/main/java/bjc/dicelang/scl/Errors.java | 146 ++++++++ .../main/java/bjc/dicelang/scl/FloatSCLToken.java | 41 +++ .../main/java/bjc/dicelang/scl/IntSCLToken.java | 18 + scl/src/main/java/bjc/dicelang/scl/SCLToken.java | 90 +++++ .../bjc/dicelang/scl/StreamControlConsole.java | 73 ++++ .../java/bjc/dicelang/scl/StreamControlEngine.java | 396 ++++++++++++++++++++ .../main/java/bjc/dicelang/scl/StreamEngine.java | 252 +++++++++++++ .../java/bjc/dicelang/scl/StringLitSCLToken.java | 13 + .../main/java/bjc/dicelang/scl/StringSCLToken.java | 46 +++ .../main/java/bjc/dicelang/scl/SymbolSCLToken.java | 13 + .../java/bjc/dicelang/scl/WordListSCLToken.java | 48 +++ .../main/java/bjc/dicelang/scl/WordSCLToken.java | 82 +++++ .../main/java/bjc/dicelang/scl/WordsSCLToken.java | 15 + 38 files changed, 1394 insertions(+), 1193 deletions(-) delete mode 100644 base/src/bjc/dicelang/scl/ArraySCLToken.java delete mode 100644 base/src/bjc/dicelang/scl/BooleanSCLToken.java delete mode 100644 base/src/bjc/dicelang/scl/FloatSCLToken.java delete mode 100644 base/src/bjc/dicelang/scl/IntSCLToken.java delete mode 100644 base/src/bjc/dicelang/scl/SCLToken.java delete mode 100644 base/src/bjc/dicelang/scl/StreamControlConsole.java delete mode 100644 base/src/bjc/dicelang/scl/StreamControlEngine.java delete mode 100644 base/src/bjc/dicelang/scl/StreamEngine.java delete mode 100644 base/src/bjc/dicelang/scl/StringLitSCLToken.java delete mode 100644 base/src/bjc/dicelang/scl/StringSCLToken.java delete mode 100644 base/src/bjc/dicelang/scl/SymbolSCLToken.java delete mode 100644 base/src/bjc/dicelang/scl/WordListSCLToken.java delete mode 100644 base/src/bjc/dicelang/scl/WordSCLToken.java delete mode 100644 base/src/bjc/dicelang/scl/WordsSCLToken.java create mode 100644 scl/.classpath create mode 100644 scl/.project create mode 100644 scl/.settings/org.eclipse.core.resources.prefs create mode 100644 scl/.settings/org.eclipse.jdt.core.prefs create mode 100644 scl/.settings/org.eclipse.m2e.core.prefs delete mode 100644 scl/src/main/java/bjc/App.java create mode 100644 scl/src/main/java/bjc/dicelang/scl/ArraySCLToken.java create mode 100644 scl/src/main/java/bjc/dicelang/scl/BooleanSCLToken.java create mode 100644 scl/src/main/java/bjc/dicelang/scl/Errors.java create mode 100644 scl/src/main/java/bjc/dicelang/scl/FloatSCLToken.java create mode 100644 scl/src/main/java/bjc/dicelang/scl/IntSCLToken.java create mode 100644 scl/src/main/java/bjc/dicelang/scl/SCLToken.java create mode 100644 scl/src/main/java/bjc/dicelang/scl/StreamControlConsole.java create mode 100644 scl/src/main/java/bjc/dicelang/scl/StreamControlEngine.java create mode 100644 scl/src/main/java/bjc/dicelang/scl/StreamEngine.java create mode 100644 scl/src/main/java/bjc/dicelang/scl/StringLitSCLToken.java create mode 100644 scl/src/main/java/bjc/dicelang/scl/StringSCLToken.java create mode 100644 scl/src/main/java/bjc/dicelang/scl/SymbolSCLToken.java create mode 100644 scl/src/main/java/bjc/dicelang/scl/WordListSCLToken.java create mode 100644 scl/src/main/java/bjc/dicelang/scl/WordSCLToken.java create mode 100644 scl/src/main/java/bjc/dicelang/scl/WordsSCLToken.java diff --git a/base/pom.xml b/base/pom.xml index bfea276..f231c13 100644 --- a/base/pom.xml +++ b/base/pom.xml @@ -1,5 +1,4 @@ - 4.0.0 @@ -115,6 +114,12 @@ 1.0.0 + + bjc + dicelang-scl + 1.0-SNAPSHOT + + jline jline @@ -126,5 +131,6 @@ guava 19.0 + diff --git a/base/src/bjc/dicelang/DiceLangEngine.java b/base/src/bjc/dicelang/DiceLangEngine.java index e7ba4e8..f8fc5c6 100644 --- a/base/src/bjc/dicelang/DiceLangEngine.java +++ b/base/src/bjc/dicelang/DiceLangEngine.java @@ -127,7 +127,7 @@ public class DiceLangEngine { shunt = new Shunter(); parsr = new Parser(); - streamEng = new StreamEngine(this); + streamEng = new StreamEngine(); tokenzer = new Tokenizer(this); eval = new Evaluator(this); } diff --git a/base/src/bjc/dicelang/scl/ArraySCLToken.java b/base/src/bjc/dicelang/scl/ArraySCLToken.java deleted file mode 100644 index ff86f48..0000000 --- a/base/src/bjc/dicelang/scl/ArraySCLToken.java +++ /dev/null @@ -1,15 +0,0 @@ -package bjc.dicelang.scl; - -import bjc.utils.funcdata.IList; - -public class ArraySCLToken extends WordListSCLToken { - - public ArraySCLToken(IList tokens) { - super(true, tokens); - } - - @Override - public String toString() { - return "ArraySCLToken [tokenVals=" + tokenVals + "]"; - } -} diff --git a/base/src/bjc/dicelang/scl/BooleanSCLToken.java b/base/src/bjc/dicelang/scl/BooleanSCLToken.java deleted file mode 100644 index e2e8e12..0000000 --- a/base/src/bjc/dicelang/scl/BooleanSCLToken.java +++ /dev/null @@ -1,39 +0,0 @@ -package bjc.dicelang.scl; - -public class BooleanSCLToken extends SCLToken { - /* Used for BLIT */ - public boolean boolVal; - - public BooleanSCLToken(boolean val) { - super(Type.BLIT); - - boolVal = val; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + (boolVal ? 1231 : 1237); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (!super.equals(obj)) - return false; - if (getClass() != obj.getClass()) - return false; - BooleanSCLToken other = (BooleanSCLToken) obj; - if (boolVal != other.boolVal) - return false; - return true; - } - - @Override - public String toString() { - return "BooleanSCLToken [boolVal=" + boolVal + "]"; - } -} diff --git a/base/src/bjc/dicelang/scl/FloatSCLToken.java b/base/src/bjc/dicelang/scl/FloatSCLToken.java deleted file mode 100644 index f02c796..0000000 --- a/base/src/bjc/dicelang/scl/FloatSCLToken.java +++ /dev/null @@ -1,41 +0,0 @@ -package bjc.dicelang.scl; - -public class FloatSCLToken extends SCLToken { - /* Used for FLIT */ - public double floatVal; - - public FloatSCLToken(double val) { - super(Type.FLIT); - - floatVal = val; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - long temp; - temp = Double.doubleToLongBits(floatVal); - result = prime * result + (int) (temp ^ (temp >>> 32)); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (!super.equals(obj)) - return false; - if (getClass() != obj.getClass()) - return false; - FloatSCLToken other = (FloatSCLToken) obj; - if (Double.doubleToLongBits(floatVal) != Double.doubleToLongBits(other.floatVal)) - return false; - return true; - } - - @Override - public String toString() { - return "FloatSCLToken [floatVal=" + floatVal + "]"; - } -} diff --git a/base/src/bjc/dicelang/scl/IntSCLToken.java b/base/src/bjc/dicelang/scl/IntSCLToken.java deleted file mode 100644 index 9e88dbe..0000000 --- a/base/src/bjc/dicelang/scl/IntSCLToken.java +++ /dev/null @@ -1,18 +0,0 @@ -package bjc.dicelang.scl; - -public class IntSCLToken extends SCLToken { - /* Used for ILIT */ - public long intVal; - - /* Create a new token. */ - public IntSCLToken(final Type typ) { - super(typ); - } - - /* Create a new token. */ - public IntSCLToken(final long iVal) { - super(Type.ILIT); - - intVal = iVal; - } -} \ No newline at end of file diff --git a/base/src/bjc/dicelang/scl/SCLToken.java b/base/src/bjc/dicelang/scl/SCLToken.java deleted file mode 100644 index b5dadb0..0000000 --- a/base/src/bjc/dicelang/scl/SCLToken.java +++ /dev/null @@ -1,92 +0,0 @@ -package bjc.dicelang.scl; - -import java.util.HashMap; -import java.util.Map; - -import bjc.dicelang.Errors; -import bjc.utils.parserutils.TokenUtils; - -import static bjc.dicelang.Errors.ErrorKey.EK_SCL_INVTOKEN; - -import static bjc.dicelang.scl.SCLToken.Type.*; - -public class SCLToken { - - 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, - } - - public SCLToken.Type type; - - public static SCLToken tokenizeString(final String token) { - if (litTokens.containsKey(token)) { - return new IntSCLToken(litTokens.get(token)); - } else if (token.startsWith("\\")) { - return new SymbolSCLToken(token.substring(1)); - } else if (WordSCLToken.isBuiltinWord(token)) { - return new WordSCLToken(token); - } else if (token.equals("true")) { - return new BooleanSCLToken(true); - } else if (token.equals("false")) { - return new BooleanSCLToken(false); - } else if (TokenUtils.isInt(token)) { - return new IntSCLToken(Long.parseLong(token)); - } else if (TokenUtils.isDouble(token)) { - return new FloatSCLToken(Double.parseDouble(token)); - } else { - Errors.inst.printError(EK_SCL_INVTOKEN, token); - return null; - } - } - - protected static final Map litTokens; - - protected SCLToken() { - - } - - protected SCLToken(Type typ) { - type = typ; - } - - static { - /* Init literal tokens. */ - litTokens = new HashMap<>(); - - litTokens.put("'", SQUOTE); - litTokens.put("\"", DQUOTE); - litTokens.put("[", OBRACKET); - litTokens.put("{", OBRACE); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((type == null) ? 0 : type.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - SCLToken other = (SCLToken) obj; - if (type != other.type) - return false; - return true; - } - - @Override - public String toString() { - return "SCLToken [type=" + type + "]"; - } -} \ No newline at end of file diff --git a/base/src/bjc/dicelang/scl/StreamControlConsole.java b/base/src/bjc/dicelang/scl/StreamControlConsole.java deleted file mode 100644 index ca61244..0000000 --- a/base/src/bjc/dicelang/scl/StreamControlConsole.java +++ /dev/null @@ -1,73 +0,0 @@ -package bjc.dicelang.scl; - -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; - -import java.util.Scanner; - -/** - * Implement a SCL REPL - * - * @author Ben Culkin - */ -public class StreamControlConsole { - /* - * @TODO 10/08/17 :SCLArgs Do something useful with the CLI args. - * - */ - /** - * Main method - * - * @param args - * Unused CLI args. - */ - public static void main(String[] args) { - /* - * Initialize vars. - * - * We can get away with passing the null, because StreamEngine doesn't reference - * any parts of DiceLangEngine. - */ - StreamEngine sengine = new StreamEngine(null); - StreamControlEngine sclengine = new StreamControlEngine(sengine); - Scanner scn = new Scanner(System.in); - - /* Get input from the user. */ - System.out.print("Enter a SCL command string (blank to exit): "); - - /* Process it. */ - while (scn.hasNextLine()) { - String ln = scn.nextLine().trim(); - - if (ln.equals("")) { - /* Ignore empty lines. */ - break; - } - - /* Break the token into strings. */ - IList res = new FunctionalList<>(); - String[] tokens = ln.split(" "); - - /* Run the stream engine on the tokens. */ - boolean succ = sengine.doStreams(tokens, res); - if (!succ) { - System.out.printf("ERROR: Stream engine failed for line '%s'\n", ln); - continue; - } - - /* Run the command through SCL. */ - tokens = res.toArray(new String[res.getSize()]); - succ = sclengine.runProgram(tokens); - if (!succ) { - System.out.printf("ERROR: SCL engine failed for line '%s'\n", ln); - continue; - } - - /* Prompt again. */ - System.out.print("Command string executed succesfully.\n\n"); - System.out.print("Enter a SCL command string (blank to exit): "); - } - - scn.close(); - } -} diff --git a/base/src/bjc/dicelang/scl/StreamControlEngine.java b/base/src/bjc/dicelang/scl/StreamControlEngine.java deleted file mode 100644 index 32ae8ba..0000000 --- a/base/src/bjc/dicelang/scl/StreamControlEngine.java +++ /dev/null @@ -1,397 +0,0 @@ -package bjc.dicelang.scl; - -import java.util.HashMap; -import java.util.Map; - -import bjc.dicelang.Errors; -import bjc.utils.esodata.SimpleStack; -import bjc.utils.esodata.Stack; -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; -import bjc.utils.parserutils.TokenUtils; - -import static bjc.dicelang.Errors.ErrorKey.*; -import static bjc.dicelang.scl.SCLToken.Type.*; -import static bjc.dicelang.scl.WordSCLToken.Word.*; - -/* - * @TODO 10/08/17 Ben Culkin :SCLReorg - * - * This is a large enough class that it should maybe be split into subclasses. - */ -/** - * Runs a Stream Control Language (SCL) program. - * - * SCL is a stack-based concatenative language based mostly off of Postscript - * and Factor, with inspiration from various other languages. - * - * @author Ben Culkin - */ -public class StreamControlEngine { - /* The stream engine we're hooked to. */ - private final StreamEngine eng; - - /* The current stack state. */ - private final Stack curStack; - - /* Map of user defined words. */ - private final Map words; - - /** - * Create a new stream control engine. - * - * @param engine - * The engine to control. - */ - public StreamControlEngine(final StreamEngine engine) { - eng = engine; - - words = new HashMap<>(); - curStack = new SimpleStack<>(); - } - - /** - * Run a SCL program. - * - * @param tokens - * The program to run. - * - * @return Whether the program executed successfully. - */ - public boolean runProgram(final String[] tokens) { - for (int i = 0; i < tokens.length; i++) { - /* Tokenize each token. */ - final String token = tokens[i]; - final SCLToken tok = SCLToken.tokenizeString(token); - - if (tok == null) { - System.out.printf("ERROR: Tokenization failed for '%s'\n", token); - return false; - } - - /* Handle token types. */ - switch (tok.type) { - case SQUOTE: - /* Handle single-quotes. */ - i = handleSingleQuote(i, tokens); - if (i == -1) { - return false; - } - break; - case OBRACKET: - /* Handle delimited brackets. */ - i = handleDelim(i, tokens, "]"); - if (i == -1) { - return false; - } - break; - case OBRACE: - /* Handle delimited braces. */ - i = handleDelim(i, tokens, "}"); - if (i == -1) { - return false; - } - final SCLToken brak = curStack.pop(); - curStack.push(new ArraySCLToken(((WordListSCLToken) brak).tokenVals)); - break; - - case WORD: - /* Handle words. */ - if (!handleWord((WordSCLToken) tok)) { - System.out.printf("WARNING: Execution of word '%s' failed\n", tok); - } - break; - default: - /* Put it onto the stack. */ - curStack.push(tok); - break; - } - } - - return true; - } - - private boolean handleWord(final WordSCLToken tk) { - boolean succ = true; - - /* Handle each type of word. */ - /* - * @NOTE This should probably use something other than a switch statement. - */ - switch (tk.wordVal) { - 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 IntSCLToken(curStack.size())); - break; - case STACKEMPTY: - curStack.push(new BooleanSCLToken(curStack.empty())); - break; - case DROP: - if (curStack.size() == 0) { - Errors.inst.printError(EK_SCL_SUNDERFLOW, tk.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.toString()); - return false; - } - curStack.nip(); - break; - case NNIP: - succ = handleNNip(); - if (!succ) { - return false; - } - break; - default: - Errors.inst.printError(EK_SCL_UNWORD, tk.toString()); - return false; - } - - return true; - } - - /* Handle nipping a specified number of items. */ - private boolean handleNNip() { - final SCLToken num = curStack.pop(); - - if (num.type != ILIT) { - Errors.inst.printError(EK_SCL_INVARG, num.type.toString()); - return false; - } - - final int n = (int) ((IntSCLToken) num).intVal; - - if (curStack.size() < n) { - Errors.inst.printError(EK_SCL_SUNDERFLOW, NNIP.toString()); - return false; - } - - curStack.nip(n); - return true; - } - - /* Handle dropping a specified number of items. */ - private boolean handleNDrop() { - final SCLToken num = curStack.pop(); - - if (num.type != ILIT) { - Errors.inst.printError(EK_SCL_INVARG, num.type.toString()); - return false; - } - - final int n = (int) ((IntSCLToken) num).intVal; - - if (curStack.size() < n) { - Errors.inst.printError(EK_SCL_SUNDERFLOW, NDROP.toString()); - return false; - } - - curStack.drop(n); - return true; - } - - /* Handle toggling the executable flag on an array. */ - private boolean toggleExec(final boolean exec) { - final SCLToken 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; - } - - /* Handle creating an array. */ - private boolean makeArray() { - final SCLToken num = curStack.pop(); - - if (num.type != ILIT) { - Errors.inst.printError(EK_SCL_INVARG, num.type.toString()); - } - - final IList arr = new FunctionalList<>(); - - for (int i = 0; i < ((IntSCLToken) num).intVal; i++) { - arr.add(curStack.pop()); - } - - curStack.push(new ArraySCLToken(arr)); - - return true; - } - - /* Handle a delimited series of tokens. */ - private int handleDelim(final int i, final String[] tokens, final String delim) { - final IList 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)) { - final SCLToken ntok = SCLToken.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: - n = handleDelim(i, tokens, "}"); - if (n == -1) { - return -1; - } - final SCLToken brak = curStack.pop(); - toks.add(new ArraySCLToken(((WordListSCLToken) 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; - - /* - * @NOTE Instead of being hardcoded, this should be a parameter. - */ - curStack.push(new WordsSCLToken(toks)); - - return n; - } - - /* Handle a single-quoted string. */ - private int handleSingleQuote(final int i, final String[] tokens) { - final 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; - - String strang = TokenUtils.descapeString(sb.toString()); - - curStack.push(new StringSCLToken(false, strang)); - - return n; - } -} diff --git a/base/src/bjc/dicelang/scl/StreamEngine.java b/base/src/bjc/dicelang/scl/StreamEngine.java deleted file mode 100644 index 2d931b9..0000000 --- a/base/src/bjc/dicelang/scl/StreamEngine.java +++ /dev/null @@ -1,261 +0,0 @@ -package bjc.dicelang.scl; - -import bjc.dicelang.DiceLangEngine; -import bjc.dicelang.Errors; -import bjc.utils.esodata.SingleTape; -import bjc.utils.esodata.Tape; -import bjc.utils.esodata.TapeLibrary; -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; -import bjc.utils.funcutils.ListUtils; - -import java.util.Arrays; - -import static bjc.dicelang.Errors.ErrorKey.*; - -/** - * Implements multiple interleaved parse streams, as well as a command language - * for the streams. - * - * The idea for the interleaved streams came from the language Oozylbub & - * Murphy, but the command language was my own idea. - * - * @author Ben Culkin - */ -public class StreamEngine { - /* Whether or not we're doing debugging. */ - public final boolean debug = true; - - /* The engine we're attached to. */ - DiceLangEngine eng; - - /* Our streams. */ - Tape> streams; - IList currStream; - - /* Saved streams */ - TapeLibrary> savedStreams; - - /* Handler for SCL programs */ - private final StreamControlEngine scleng; - - /** - * Create a new stream engine. - * - * @param engine - * The dice engine we're attached to. - */ - public StreamEngine(final DiceLangEngine engine) { - eng = engine; - - savedStreams = new TapeLibrary<>(); - scleng = new StreamControlEngine(this); - } - - /* Do pre-run (re)initialization. */ - private void init() { - /* Reinitialize our list of streams. */ - streams = new SingleTape<>(); - - /* Create an initial stream. */ - currStream = new FunctionalList<>(); - streams.insertBefore(currStream); - } - - /** - * Process a possibly interleaved set of streams. - * - * @param toks - * The raw token to read streams from. - * - * @param dest - * The list to write the final stream to. - * - * @return Whether or not the streams were successfully processed. - */ - public boolean doStreams(final String[] toks, final IList dest) { - return doStreams(Arrays.asList(toks), dest); - } - - /** - * Process a possibly interleaved set of streams. - * - * @param toks - * The raw token to read streams from. - * - * @param dest - * The list to write the final stream to. - * - * @return Whether or not the streams were successfully processed. - */ - public boolean doStreams(final Iterable toks, final IList dest) { - /* Initialize per-run state. */ - init(); - - /* Are we currently quoting things? */ - boolean quoteMode = false; - - /* Process each token. */ - for (final String tk : toks) { - /* Process stream commands. */ - if (tk.startsWith("{@S") && !quoteMode) { - if (tk.equals("{@SQ}")) { - /* Start quoting. */ - quoteMode = true; - } else if (!processCommand(tk)) { - return false; - } - } else { - if (tk.equals("{@SU}")) { - /* Stop quoting. */ - quoteMode = false; - } else if (tk.startsWith("\\") && tk.endsWith("{@SU}")) { - /* Unquote quoted end. */ - currStream.add(tk.substring(1)); - } else { - currStream.add(tk); - } - } - } - - for (final String tk : currStream) { - /* Collect tokens from the current stream. */ - dest.add(tk); - } - - return true; - } - - /** Create a new stream. */ - public void newStream() { - streams.insertAfter(new FunctionalList<>()); - } - - /** - * Move to a stream to the right. - * - * @return Whether or not the move was successful. - */ - public boolean rightStream() { - if (!streams.right()) { - Errors.inst.printError(EK_STRM_NONEX); - return false; - } - - currStream = streams.item(); - return true; - } - - /** - * Move to a stream to the left. - * - * @return Whether or not the move was successful. - */ - public boolean leftStream() { - if (!streams.left()) { - Errors.inst.printError(EK_STRM_NONEX); - return false; - } - - currStream = streams.item(); - return true; - } - - /** - * Delete the current stream. - * - * @return Whether or not the delete succeeded. - */ - public boolean deleteStream() { - if (streams.size() == 1) { - Errors.inst.printError(EK_STRM_LAST); - return false; - } - - streams.remove(); - currStream = streams.item(); - - return true; - } - - /** - * Merge the current stream into the previous stream. - * - * @return Whether or not the merge succeded. - */ - public boolean mergeStream() { - if (streams.size() == 1) { - Errors.inst.printError(EK_STRM_LAST); - return false; - } - - final IList stringLit = streams.remove(); - currStream = streams.item(); - currStream.add(ListUtils.collapseTokens(stringLit, " ")); - - return true; - } - - private boolean processCommand(final String tk) { - char[] comms = null; - - if (tk.length() > 5) { - /* Pull off {@S and closing } */ - comms = tk.substring(3, tk.length() - 1).toCharArray(); - } else { - /* Its a single char. command. */ - comms = new char[1]; - comms[0] = tk.charAt(3); - } - - boolean succ; - - /* Process each command. */ - /* - * @TODO 10/09/17 Ben Culkin :StreamCommands This should probably be refactored - * in some way, so as to make it easier to add new commands. - */ - for (final char comm : comms) { - switch (comm) { - case '+': - newStream(); - break; - case '>': - succ = rightStream(); - if (!succ) { - return false; - } - break; - case '<': - succ = leftStream(); - if (!succ) { - return false; - } - break; - case '-': - succ = deleteStream(); - if (!succ) { - return false; - } - break; - case 'M': - succ = mergeStream(); - if (!succ) { - return false; - } - break; - case 'L': - succ = scleng.runProgram(currStream.toArray(new String[0])); - if (!succ) { - return false; - } - break; - default: - Errors.inst.printError(EK_STRM_INVCOM, tk); - return false; - } - } - - return true; - } -} diff --git a/base/src/bjc/dicelang/scl/StringLitSCLToken.java b/base/src/bjc/dicelang/scl/StringLitSCLToken.java deleted file mode 100644 index caf425a..0000000 --- a/base/src/bjc/dicelang/scl/StringLitSCLToken.java +++ /dev/null @@ -1,13 +0,0 @@ -package bjc.dicelang.scl; - -public class StringLitSCLToken extends StringSCLToken { - - public StringLitSCLToken(String val) { - super(false, val); - } - - @Override - public String toString() { - return "StringLitSCLToken [stringVal=" + stringVal + "]"; - } -} diff --git a/base/src/bjc/dicelang/scl/StringSCLToken.java b/base/src/bjc/dicelang/scl/StringSCLToken.java deleted file mode 100644 index 1436bc3..0000000 --- a/base/src/bjc/dicelang/scl/StringSCLToken.java +++ /dev/null @@ -1,46 +0,0 @@ -package bjc.dicelang.scl; - -public class StringSCLToken extends SCLToken { - /* Used for SYMBOL & SLIT */ - public String stringVal; - - protected StringSCLToken(boolean isSymbol, String val) { - if (isSymbol) { - type = Type.SYMBOL; - } else { - type = Type.SLIT; - } - - stringVal = val; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((stringVal == null) ? 0 : stringVal.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (!super.equals(obj)) - return false; - if (getClass() != obj.getClass()) - return false; - StringSCLToken other = (StringSCLToken) obj; - if (stringVal == null) { - if (other.stringVal != null) - return false; - } else if (!stringVal.equals(other.stringVal)) - return false; - return true; - } - - @Override - public String toString() { - return "StringSCLToken [stringVal=" + stringVal + "]"; - } -} diff --git a/base/src/bjc/dicelang/scl/SymbolSCLToken.java b/base/src/bjc/dicelang/scl/SymbolSCLToken.java deleted file mode 100644 index f841654..0000000 --- a/base/src/bjc/dicelang/scl/SymbolSCLToken.java +++ /dev/null @@ -1,13 +0,0 @@ -package bjc.dicelang.scl; - -public class SymbolSCLToken extends StringSCLToken { - - public SymbolSCLToken(String val) { - super(true, val); - } - - @Override - public String toString() { - return "SymbolSCLToken [stringVal=" + stringVal + "]"; - } -} diff --git a/base/src/bjc/dicelang/scl/WordListSCLToken.java b/base/src/bjc/dicelang/scl/WordListSCLToken.java deleted file mode 100644 index ffa0e85..0000000 --- a/base/src/bjc/dicelang/scl/WordListSCLToken.java +++ /dev/null @@ -1,48 +0,0 @@ -package bjc.dicelang.scl; - -import bjc.utils.funcdata.IList; - -public class WordListSCLToken extends SCLToken { - /* Used for WORDS & ARRAY */ - public IList tokenVals; - - protected WordListSCLToken(boolean isArray, IList tokens) { - if (isArray) { - type = Type.ARRAY; - } else { - type = Type.WORDS; - } - - tokenVals = tokens; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((tokenVals == null) ? 0 : tokenVals.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (!super.equals(obj)) - return false; - if (getClass() != obj.getClass()) - return false; - WordListSCLToken other = (WordListSCLToken) obj; - if (tokenVals == null) { - if (other.tokenVals != null) - return false; - } else if (!tokenVals.equals(other.tokenVals)) - return false; - return true; - } - - @Override - public String toString() { - return "WordsSCLToken [tokenVals=" + tokenVals + "]"; - } -} diff --git a/base/src/bjc/dicelang/scl/WordSCLToken.java b/base/src/bjc/dicelang/scl/WordSCLToken.java deleted file mode 100644 index ea3118e..0000000 --- a/base/src/bjc/dicelang/scl/WordSCLToken.java +++ /dev/null @@ -1,82 +0,0 @@ -package bjc.dicelang.scl; - -import java.util.HashMap; -import java.util.Map; - -import static bjc.dicelang.scl.WordSCLToken.Word.*; - -public class WordSCLToken extends SCLToken { - public static enum Word { - /* Array manipulation */ - MAKEARRAY, MAKEEXEC, MAKEUNEXEC, - /* Stream manipulation */ - NEWSTREAM, LEFTSTREAM, RIGHTSTREAM, DELETESTREAM, MERGESTREAM, - /* Stack manipulation */ - STACKCOUNT, STACKEMPTY, DROP, NDROP, NIP, NNIP, - } - - public Word wordVal; - - public WordSCLToken(String wrd) { - this(builtinWords.get(wrd)); - } - - public WordSCLToken(Word wrd) { - super(Type.WORD); - - wordVal = wrd; - } - - @Override - public String toString() { - return "WordSCLToken [wordVal=" + wordVal + "]"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((wordVal == null) ? 0 : wordVal.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (!super.equals(obj)) - return false; - if (getClass() != obj.getClass()) - return false; - WordSCLToken other = (WordSCLToken) obj; - if (wordVal != other.wordVal) - return false; - return true; - } - - public static boolean isBuiltinWord(String wrd) { - return builtinWords.containsKey(wrd); - } - - private static final Map builtinWords; - - static { - /* Init builtin words. */ - builtinWords = new HashMap<>(); - - builtinWords.put("makearray", MAKEARRAY); - builtinWords.put("+stream", NEWSTREAM); - builtinWords.put(">stream", LEFTSTREAM); - builtinWords.put(" tokens) { - super(false, tokens); - } - - @Override - public String toString() { - return "WordsSCLToken [tokenVals=" + tokenVals + "]"; - } -} diff --git a/scl/.classpath b/scl/.classpath new file mode 100644 index 0000000..af1430b --- /dev/null +++ b/scl/.classpath @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scl/.project b/scl/.project new file mode 100644 index 0000000..2d9b4f5 --- /dev/null +++ b/scl/.project @@ -0,0 +1,23 @@ + + + dicelang-scl + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/scl/.settings/org.eclipse.core.resources.prefs b/scl/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..f9fe345 --- /dev/null +++ b/scl/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding//src/test/java=UTF-8 +encoding/=UTF-8 diff --git a/scl/.settings/org.eclipse.jdt.core.prefs b/scl/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..714351a --- /dev/null +++ b/scl/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/scl/.settings/org.eclipse.m2e.core.prefs b/scl/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..f897a7f --- /dev/null +++ b/scl/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/scl/pom.xml b/scl/pom.xml index a4e9e4c..2b26545 100644 --- a/scl/pom.xml +++ b/scl/pom.xml @@ -1,26 +1,38 @@ - - 4.0.0 - - bjc - dicelang-parent - 1.0.0 - - bjc - dicelang-scl - 1.0-SNAPSHOT - scl - http://maven.apache.org - - UTF-8 - - - - junit - junit - 3.8.1 - test - - + + 4.0.0 + + bjc + dicelang-parent + 1.0.0 + + dicelang-scl + 1.0-SNAPSHOT + scl + http://maven.apache.org + + UTF-8 + + + + junit + junit + 3.8.1 + test + + + + + bjc + BJC-Utils2 + 1.0.0 + + + bjc + dicelang-dice + 1.0.0 + + diff --git a/scl/src/main/java/bjc/App.java b/scl/src/main/java/bjc/App.java deleted file mode 100644 index ff1c4f9..0000000 --- a/scl/src/main/java/bjc/App.java +++ /dev/null @@ -1,13 +0,0 @@ -package bjc; - -/** - * Hello world! - * - */ -public class App -{ - public static void main( String[] args ) - { - System.out.println( "Hello World!" ); - } -} diff --git a/scl/src/main/java/bjc/dicelang/scl/ArraySCLToken.java b/scl/src/main/java/bjc/dicelang/scl/ArraySCLToken.java new file mode 100644 index 0000000..ff86f48 --- /dev/null +++ b/scl/src/main/java/bjc/dicelang/scl/ArraySCLToken.java @@ -0,0 +1,15 @@ +package bjc.dicelang.scl; + +import bjc.utils.funcdata.IList; + +public class ArraySCLToken extends WordListSCLToken { + + public ArraySCLToken(IList tokens) { + super(true, tokens); + } + + @Override + public String toString() { + return "ArraySCLToken [tokenVals=" + tokenVals + "]"; + } +} diff --git a/scl/src/main/java/bjc/dicelang/scl/BooleanSCLToken.java b/scl/src/main/java/bjc/dicelang/scl/BooleanSCLToken.java new file mode 100644 index 0000000..e2e8e12 --- /dev/null +++ b/scl/src/main/java/bjc/dicelang/scl/BooleanSCLToken.java @@ -0,0 +1,39 @@ +package bjc.dicelang.scl; + +public class BooleanSCLToken extends SCLToken { + /* Used for BLIT */ + public boolean boolVal; + + public BooleanSCLToken(boolean val) { + super(Type.BLIT); + + boolVal = val; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + (boolVal ? 1231 : 1237); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + BooleanSCLToken other = (BooleanSCLToken) obj; + if (boolVal != other.boolVal) + return false; + return true; + } + + @Override + public String toString() { + return "BooleanSCLToken [boolVal=" + boolVal + "]"; + } +} diff --git a/scl/src/main/java/bjc/dicelang/scl/Errors.java b/scl/src/main/java/bjc/dicelang/scl/Errors.java new file mode 100644 index 0000000..d372850 --- /dev/null +++ b/scl/src/main/java/bjc/dicelang/scl/Errors.java @@ -0,0 +1,146 @@ +package bjc.dicelang.scl; + +/** + * Repository for error messages. + * + * @TODO 10/08/17 Ben Culkin :ErrorRefactor + * + * This way of handling error messages is not easy to deal with. Something + * else needs to be done, but I'm not sure what at the moment. + * + * @author EVE + * + */ +public class Errors { + /** + * The types of error message. + * + * @author EVE + * + */ + public static enum ErrorKey { + /* Stream Errors */ + /** + * Attempted to switch to a non-existant stream + */ + EK_STRM_NONEX, + /** + * Can't delete the last stream + */ + EK_STRM_LAST, + /** + * 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, + } + + /** + * The mode for the type of error messages to print out. + * + * @author EVE + * + */ + public static enum ErrorMode { + /** + * Output error messages for wizards. + */ + WIZARD, + /** + * Output error messages for developers. + */ + DEV + } + + private ErrorMode mode; + + /** + * Print an error. + * + * @param key + * The key of the error. + * + * @param args + * The arguments for the error. + */ + public void printError(final ErrorKey key, final String... args) { + switch (mode) { + case WIZARD: + System.out.println("\t? " + key.ordinal()); + break; + + case DEV: + devError(key, args); + break; + + default: + System.out.println("\tERROR ERROR: Unknown error mode " + mode); + } + } + + private static void devError(final ErrorKey key, final String[] args) { + switch (key) { + case EK_STRM_NONEX: + System.out.printf("\tERROR: Attempted to switch to non-existent stream\n"); + break; + + case EK_STRM_LAST: + System.out.printf("\tERROR: Cannot delete last stream\n"); + break; + + 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); + } + } + + /** + * The instance of the errors. + */ + public final static Errors inst; + + static { + inst = new Errors(); + + inst.mode = ErrorMode.DEV; + } +} diff --git a/scl/src/main/java/bjc/dicelang/scl/FloatSCLToken.java b/scl/src/main/java/bjc/dicelang/scl/FloatSCLToken.java new file mode 100644 index 0000000..f02c796 --- /dev/null +++ b/scl/src/main/java/bjc/dicelang/scl/FloatSCLToken.java @@ -0,0 +1,41 @@ +package bjc.dicelang.scl; + +public class FloatSCLToken extends SCLToken { + /* Used for FLIT */ + public double floatVal; + + public FloatSCLToken(double val) { + super(Type.FLIT); + + floatVal = val; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + long temp; + temp = Double.doubleToLongBits(floatVal); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + FloatSCLToken other = (FloatSCLToken) obj; + if (Double.doubleToLongBits(floatVal) != Double.doubleToLongBits(other.floatVal)) + return false; + return true; + } + + @Override + public String toString() { + return "FloatSCLToken [floatVal=" + floatVal + "]"; + } +} diff --git a/scl/src/main/java/bjc/dicelang/scl/IntSCLToken.java b/scl/src/main/java/bjc/dicelang/scl/IntSCLToken.java new file mode 100644 index 0000000..9e88dbe --- /dev/null +++ b/scl/src/main/java/bjc/dicelang/scl/IntSCLToken.java @@ -0,0 +1,18 @@ +package bjc.dicelang.scl; + +public class IntSCLToken extends SCLToken { + /* Used for ILIT */ + public long intVal; + + /* Create a new token. */ + public IntSCLToken(final Type typ) { + super(typ); + } + + /* Create a new token. */ + public IntSCLToken(final long iVal) { + super(Type.ILIT); + + intVal = iVal; + } +} \ No newline at end of file diff --git a/scl/src/main/java/bjc/dicelang/scl/SCLToken.java b/scl/src/main/java/bjc/dicelang/scl/SCLToken.java new file mode 100644 index 0000000..c4056e0 --- /dev/null +++ b/scl/src/main/java/bjc/dicelang/scl/SCLToken.java @@ -0,0 +1,90 @@ +package bjc.dicelang.scl; + +import java.util.HashMap; +import java.util.Map; + +import bjc.utils.parserutils.TokenUtils; + +import static bjc.dicelang.scl.Errors.ErrorKey.*; +import static bjc.dicelang.scl.SCLToken.Type.*; + +public class SCLToken { + + 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, + } + + public SCLToken.Type type; + + public static SCLToken tokenizeString(final String token) { + if (litTokens.containsKey(token)) { + return new IntSCLToken(litTokens.get(token)); + } else if (token.startsWith("\\")) { + return new SymbolSCLToken(token.substring(1)); + } else if (WordSCLToken.isBuiltinWord(token)) { + return new WordSCLToken(token); + } else if (token.equals("true")) { + return new BooleanSCLToken(true); + } else if (token.equals("false")) { + return new BooleanSCLToken(false); + } else if (TokenUtils.isInt(token)) { + return new IntSCLToken(Long.parseLong(token)); + } else if (TokenUtils.isDouble(token)) { + return new FloatSCLToken(Double.parseDouble(token)); + } else { + Errors.inst.printError(EK_SCL_INVTOKEN, token); + return null; + } + } + + protected static final Map litTokens; + + protected SCLToken() { + + } + + protected SCLToken(Type typ) { + type = typ; + } + + static { + /* Init literal tokens. */ + litTokens = new HashMap<>(); + + litTokens.put("'", SQUOTE); + litTokens.put("\"", DQUOTE); + litTokens.put("[", OBRACKET); + litTokens.put("{", OBRACE); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + SCLToken other = (SCLToken) obj; + if (type != other.type) + return false; + return true; + } + + @Override + public String toString() { + return "SCLToken [type=" + type + "]"; + } +} \ No newline at end of file diff --git a/scl/src/main/java/bjc/dicelang/scl/StreamControlConsole.java b/scl/src/main/java/bjc/dicelang/scl/StreamControlConsole.java new file mode 100644 index 0000000..26266ff --- /dev/null +++ b/scl/src/main/java/bjc/dicelang/scl/StreamControlConsole.java @@ -0,0 +1,73 @@ +package bjc.dicelang.scl; + +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; + +import java.util.Scanner; + +/** + * Implement a SCL REPL + * + * @author Ben Culkin + */ +public class StreamControlConsole { + /* + * @TODO 10/08/17 :SCLArgs + * + * Do something useful with the CLI args. + * + */ + /** + * Main method + * + * @param args + * Unused CLI args. + */ + public static void main(String[] args) { + /* + * Initialize vars. + * + */ + StreamEngine sengine = new StreamEngine(); + StreamControlEngine sclengine = new StreamControlEngine(sengine); + Scanner scn = new Scanner(System.in); + + /* Get input from the user. */ + System.out.print("Enter a SCL command string (blank to exit): "); + + /* Process it. */ + while (scn.hasNextLine()) { + String ln = scn.nextLine().trim(); + + if (ln.equals("")) { + /* Ignore empty lines. */ + break; + } + + /* Break the token into strings. */ + IList res = new FunctionalList<>(); + String[] tokens = ln.split(" "); + + /* Run the stream engine on the tokens. */ + boolean succ = sengine.doStreams(tokens, res); + if (!succ) { + System.out.printf("ERROR: Stream engine failed for line '%s'\n", ln); + continue; + } + + /* Run the command through SCL. */ + tokens = res.toArray(new String[res.getSize()]); + succ = sclengine.runProgram(tokens); + if (!succ) { + System.out.printf("ERROR: SCL engine failed for line '%s'\n", ln); + continue; + } + + /* Prompt again. */ + System.out.print("Command string executed succesfully.\n\n"); + System.out.print("Enter a SCL command string (blank to exit): "); + } + + scn.close(); + } +} diff --git a/scl/src/main/java/bjc/dicelang/scl/StreamControlEngine.java b/scl/src/main/java/bjc/dicelang/scl/StreamControlEngine.java new file mode 100644 index 0000000..5224b6f --- /dev/null +++ b/scl/src/main/java/bjc/dicelang/scl/StreamControlEngine.java @@ -0,0 +1,396 @@ +package bjc.dicelang.scl; + +import java.util.HashMap; +import java.util.Map; + +import bjc.utils.esodata.SimpleStack; +import bjc.utils.esodata.Stack; +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; +import bjc.utils.parserutils.TokenUtils; + +import static bjc.dicelang.scl.Errors.ErrorKey.*; +import static bjc.dicelang.scl.SCLToken.Type.*; +import static bjc.dicelang.scl.WordSCLToken.Word.*; + +/* + * @TODO 10/08/17 Ben Culkin :SCLReorg + * + * This is a large enough class that it should maybe be split into subclasses. + */ +/** + * Runs a Stream Control Language (SCL) program. + * + * SCL is a stack-based concatenative language based mostly off of Postscript + * and Factor, with inspiration from various other languages. + * + * @author Ben Culkin + */ +public class StreamControlEngine { + /* The stream engine we're hooked to. */ + private final StreamEngine eng; + + /* The current stack state. */ + private final Stack curStack; + + /* Map of user defined words. */ + private final Map words; + + /** + * Create a new stream control engine. + * + * @param engine + * The engine to control. + */ + public StreamControlEngine(final StreamEngine engine) { + eng = engine; + + words = new HashMap<>(); + curStack = new SimpleStack<>(); + } + + /** + * Run a SCL program. + * + * @param tokens + * The program to run. + * + * @return Whether the program executed successfully. + */ + public boolean runProgram(final String[] tokens) { + for (int i = 0; i < tokens.length; i++) { + /* Tokenize each token. */ + final String token = tokens[i]; + final SCLToken tok = SCLToken.tokenizeString(token); + + if (tok == null) { + System.out.printf("ERROR: Tokenization failed for '%s'\n", token); + return false; + } + + /* Handle token types. */ + switch (tok.type) { + case SQUOTE: + /* Handle single-quotes. */ + i = handleSingleQuote(i, tokens); + if (i == -1) { + return false; + } + break; + case OBRACKET: + /* Handle delimited brackets. */ + i = handleDelim(i, tokens, "]"); + if (i == -1) { + return false; + } + break; + case OBRACE: + /* Handle delimited braces. */ + i = handleDelim(i, tokens, "}"); + if (i == -1) { + return false; + } + final SCLToken brak = curStack.pop(); + curStack.push(new ArraySCLToken(((WordListSCLToken) brak).tokenVals)); + break; + + case WORD: + /* Handle words. */ + if (!handleWord((WordSCLToken) tok)) { + System.out.printf("WARNING: Execution of word '%s' failed\n", tok); + } + break; + default: + /* Put it onto the stack. */ + curStack.push(tok); + break; + } + } + + return true; + } + + private boolean handleWord(final WordSCLToken tk) { + boolean succ = true; + + /* Handle each type of word. */ + /* + * @NOTE This should probably use something other than a switch statement. + */ + switch (tk.wordVal) { + 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 IntSCLToken(curStack.size())); + break; + case STACKEMPTY: + curStack.push(new BooleanSCLToken(curStack.empty())); + break; + case DROP: + if (curStack.size() == 0) { + Errors.inst.printError(EK_SCL_SUNDERFLOW, tk.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.toString()); + return false; + } + curStack.nip(); + break; + case NNIP: + succ = handleNNip(); + if (!succ) { + return false; + } + break; + default: + Errors.inst.printError(EK_SCL_UNWORD, tk.toString()); + return false; + } + + return true; + } + + /* Handle nipping a specified number of items. */ + private boolean handleNNip() { + final SCLToken num = curStack.pop(); + + if (num.type != ILIT) { + Errors.inst.printError(EK_SCL_INVARG, num.type.toString()); + return false; + } + + final int n = (int) ((IntSCLToken) num).intVal; + + if (curStack.size() < n) { + Errors.inst.printError(EK_SCL_SUNDERFLOW, NNIP.toString()); + return false; + } + + curStack.nip(n); + return true; + } + + /* Handle dropping a specified number of items. */ + private boolean handleNDrop() { + final SCLToken num = curStack.pop(); + + if (num.type != ILIT) { + Errors.inst.printError(EK_SCL_INVARG, num.type.toString()); + return false; + } + + final int n = (int) ((IntSCLToken) num).intVal; + + if (curStack.size() < n) { + Errors.inst.printError(EK_SCL_SUNDERFLOW, NDROP.toString()); + return false; + } + + curStack.drop(n); + return true; + } + + /* Handle toggling the executable flag on an array. */ + private boolean toggleExec(final boolean exec) { + final SCLToken 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; + } + + /* Handle creating an array. */ + private boolean makeArray() { + final SCLToken num = curStack.pop(); + + if (num.type != ILIT) { + Errors.inst.printError(EK_SCL_INVARG, num.type.toString()); + } + + final IList arr = new FunctionalList<>(); + + for (int i = 0; i < ((IntSCLToken) num).intVal; i++) { + arr.add(curStack.pop()); + } + + curStack.push(new ArraySCLToken(arr)); + + return true; + } + + /* Handle a delimited series of tokens. */ + private int handleDelim(final int i, final String[] tokens, final String delim) { + final IList 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)) { + final SCLToken ntok = SCLToken.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: + n = handleDelim(i, tokens, "}"); + if (n == -1) { + return -1; + } + final SCLToken brak = curStack.pop(); + toks.add(new ArraySCLToken(((WordListSCLToken) 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; + + /* + * @NOTE Instead of being hardcoded, this should be a parameter. + */ + curStack.push(new WordsSCLToken(toks)); + + return n; + } + + /* Handle a single-quoted string. */ + private int handleSingleQuote(final int i, final String[] tokens) { + final 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; + + String strang = TokenUtils.descapeString(sb.toString()); + + curStack.push(new StringSCLToken(false, strang)); + + return n; + } +} diff --git a/scl/src/main/java/bjc/dicelang/scl/StreamEngine.java b/scl/src/main/java/bjc/dicelang/scl/StreamEngine.java new file mode 100644 index 0000000..e22cae1 --- /dev/null +++ b/scl/src/main/java/bjc/dicelang/scl/StreamEngine.java @@ -0,0 +1,252 @@ +package bjc.dicelang.scl; + +import bjc.utils.esodata.SingleTape; +import bjc.utils.esodata.Tape; +import bjc.utils.esodata.TapeLibrary; +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; +import bjc.utils.funcutils.ListUtils; + +import java.util.Arrays; + +import static bjc.dicelang.scl.Errors.ErrorKey.*; + +/** + * Implements multiple interleaved parse streams, as well as a command language + * for the streams. + * + * The idea for the interleaved streams came from the language Oozylbub & + * Murphy, but the command language was my own idea. + * + * @author Ben Culkin + */ +public class StreamEngine { + /* Whether or not we're doing debugging. */ + public final boolean debug = true; + + /* Our streams. */ + Tape> streams; + IList currStream; + + /* Saved streams */ + TapeLibrary> savedStreams; + + /* Handler for SCL programs */ + private final StreamControlEngine scleng; + + /** + * Create a new stream engine. + * + */ + public StreamEngine() { + savedStreams = new TapeLibrary<>(); + scleng = new StreamControlEngine(this); + } + + /* Do pre-run (re)initialization. */ + private void init() { + /* Reinitialize our list of streams. */ + streams = new SingleTape<>(); + + /* Create an initial stream. */ + currStream = new FunctionalList<>(); + streams.insertBefore(currStream); + } + + /** + * Process a possibly interleaved set of streams. + * + * @param toks + * The raw token to read streams from. + * + * @param dest + * The list to write the final stream to. + * + * @return Whether or not the streams were successfully processed. + */ + public boolean doStreams(final String[] toks, final IList dest) { + return doStreams(Arrays.asList(toks), dest); + } + + /** + * Process a possibly interleaved set of streams. + * + * @param toks + * The raw token to read streams from. + * + * @param dest + * The list to write the final stream to. + * + * @return Whether or not the streams were successfully processed. + */ + public boolean doStreams(final Iterable toks, final IList dest) { + /* Initialize per-run state. */ + init(); + + /* Are we currently quoting things? */ + boolean quoteMode = false; + + /* Process each token. */ + for (final String tk : toks) { + /* Process stream commands. */ + if (tk.startsWith("{@S") && !quoteMode) { + if (tk.equals("{@SQ}")) { + /* Start quoting. */ + quoteMode = true; + } else if (!processCommand(tk)) { + return false; + } + } else { + if (tk.equals("{@SU}")) { + /* Stop quoting. */ + quoteMode = false; + } else if (tk.startsWith("\\") && tk.endsWith("{@SU}")) { + /* Unquote quoted end. */ + currStream.add(tk.substring(1)); + } else { + currStream.add(tk); + } + } + } + + for (final String tk : currStream) { + /* Collect tokens from the current stream. */ + dest.add(tk); + } + + return true; + } + + /** Create a new stream. */ + public void newStream() { + streams.insertAfter(new FunctionalList<>()); + } + + /** + * Move to a stream to the right. + * + * @return Whether or not the move was successful. + */ + public boolean rightStream() { + if (!streams.right()) { + Errors.inst.printError(EK_STRM_NONEX); + return false; + } + + currStream = streams.item(); + return true; + } + + /** + * Move to a stream to the left. + * + * @return Whether or not the move was successful. + */ + public boolean leftStream() { + if (!streams.left()) { + Errors.inst.printError(EK_STRM_NONEX); + return false; + } + + currStream = streams.item(); + return true; + } + + /** + * Delete the current stream. + * + * @return Whether or not the delete succeeded. + */ + public boolean deleteStream() { + if (streams.size() == 1) { + Errors.inst.printError(EK_STRM_LAST); + return false; + } + + streams.remove(); + currStream = streams.item(); + + return true; + } + + /** + * Merge the current stream into the previous stream. + * + * @return Whether or not the merge succeded. + */ + public boolean mergeStream() { + if (streams.size() == 1) { + Errors.inst.printError(EK_STRM_LAST); + return false; + } + + final IList stringLit = streams.remove(); + currStream = streams.item(); + currStream.add(ListUtils.collapseTokens(stringLit, " ")); + + return true; + } + + private boolean processCommand(final String tk) { + char[] comms = null; + + if (tk.length() > 5) { + /* Pull off {@S and closing } */ + comms = tk.substring(3, tk.length() - 1).toCharArray(); + } else { + /* Its a single char. command. */ + comms = new char[1]; + comms[0] = tk.charAt(3); + } + + boolean succ; + + /* Process each command. */ + /* + * @TODO 10/09/17 Ben Culkin :StreamCommands This should probably be refactored + * in some way, so as to make it easier to add new commands. + */ + for (final char comm : comms) { + switch (comm) { + case '+': + newStream(); + break; + case '>': + succ = rightStream(); + if (!succ) { + return false; + } + break; + case '<': + succ = leftStream(); + if (!succ) { + return false; + } + break; + case '-': + succ = deleteStream(); + if (!succ) { + return false; + } + break; + case 'M': + succ = mergeStream(); + if (!succ) { + return false; + } + break; + case 'L': + succ = scleng.runProgram(currStream.toArray(new String[0])); + if (!succ) { + return false; + } + break; + default: + Errors.inst.printError(EK_STRM_INVCOM, tk); + return false; + } + } + + return true; + } +} diff --git a/scl/src/main/java/bjc/dicelang/scl/StringLitSCLToken.java b/scl/src/main/java/bjc/dicelang/scl/StringLitSCLToken.java new file mode 100644 index 0000000..caf425a --- /dev/null +++ b/scl/src/main/java/bjc/dicelang/scl/StringLitSCLToken.java @@ -0,0 +1,13 @@ +package bjc.dicelang.scl; + +public class StringLitSCLToken extends StringSCLToken { + + public StringLitSCLToken(String val) { + super(false, val); + } + + @Override + public String toString() { + return "StringLitSCLToken [stringVal=" + stringVal + "]"; + } +} diff --git a/scl/src/main/java/bjc/dicelang/scl/StringSCLToken.java b/scl/src/main/java/bjc/dicelang/scl/StringSCLToken.java new file mode 100644 index 0000000..1436bc3 --- /dev/null +++ b/scl/src/main/java/bjc/dicelang/scl/StringSCLToken.java @@ -0,0 +1,46 @@ +package bjc.dicelang.scl; + +public class StringSCLToken extends SCLToken { + /* Used for SYMBOL & SLIT */ + public String stringVal; + + protected StringSCLToken(boolean isSymbol, String val) { + if (isSymbol) { + type = Type.SYMBOL; + } else { + type = Type.SLIT; + } + + stringVal = val; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((stringVal == null) ? 0 : stringVal.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + StringSCLToken other = (StringSCLToken) obj; + if (stringVal == null) { + if (other.stringVal != null) + return false; + } else if (!stringVal.equals(other.stringVal)) + return false; + return true; + } + + @Override + public String toString() { + return "StringSCLToken [stringVal=" + stringVal + "]"; + } +} diff --git a/scl/src/main/java/bjc/dicelang/scl/SymbolSCLToken.java b/scl/src/main/java/bjc/dicelang/scl/SymbolSCLToken.java new file mode 100644 index 0000000..f841654 --- /dev/null +++ b/scl/src/main/java/bjc/dicelang/scl/SymbolSCLToken.java @@ -0,0 +1,13 @@ +package bjc.dicelang.scl; + +public class SymbolSCLToken extends StringSCLToken { + + public SymbolSCLToken(String val) { + super(true, val); + } + + @Override + public String toString() { + return "SymbolSCLToken [stringVal=" + stringVal + "]"; + } +} diff --git a/scl/src/main/java/bjc/dicelang/scl/WordListSCLToken.java b/scl/src/main/java/bjc/dicelang/scl/WordListSCLToken.java new file mode 100644 index 0000000..ffa0e85 --- /dev/null +++ b/scl/src/main/java/bjc/dicelang/scl/WordListSCLToken.java @@ -0,0 +1,48 @@ +package bjc.dicelang.scl; + +import bjc.utils.funcdata.IList; + +public class WordListSCLToken extends SCLToken { + /* Used for WORDS & ARRAY */ + public IList tokenVals; + + protected WordListSCLToken(boolean isArray, IList tokens) { + if (isArray) { + type = Type.ARRAY; + } else { + type = Type.WORDS; + } + + tokenVals = tokens; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((tokenVals == null) ? 0 : tokenVals.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + WordListSCLToken other = (WordListSCLToken) obj; + if (tokenVals == null) { + if (other.tokenVals != null) + return false; + } else if (!tokenVals.equals(other.tokenVals)) + return false; + return true; + } + + @Override + public String toString() { + return "WordsSCLToken [tokenVals=" + tokenVals + "]"; + } +} diff --git a/scl/src/main/java/bjc/dicelang/scl/WordSCLToken.java b/scl/src/main/java/bjc/dicelang/scl/WordSCLToken.java new file mode 100644 index 0000000..ea3118e --- /dev/null +++ b/scl/src/main/java/bjc/dicelang/scl/WordSCLToken.java @@ -0,0 +1,82 @@ +package bjc.dicelang.scl; + +import java.util.HashMap; +import java.util.Map; + +import static bjc.dicelang.scl.WordSCLToken.Word.*; + +public class WordSCLToken extends SCLToken { + public static enum Word { + /* Array manipulation */ + MAKEARRAY, MAKEEXEC, MAKEUNEXEC, + /* Stream manipulation */ + NEWSTREAM, LEFTSTREAM, RIGHTSTREAM, DELETESTREAM, MERGESTREAM, + /* Stack manipulation */ + STACKCOUNT, STACKEMPTY, DROP, NDROP, NIP, NNIP, + } + + public Word wordVal; + + public WordSCLToken(String wrd) { + this(builtinWords.get(wrd)); + } + + public WordSCLToken(Word wrd) { + super(Type.WORD); + + wordVal = wrd; + } + + @Override + public String toString() { + return "WordSCLToken [wordVal=" + wordVal + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((wordVal == null) ? 0 : wordVal.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + WordSCLToken other = (WordSCLToken) obj; + if (wordVal != other.wordVal) + return false; + return true; + } + + public static boolean isBuiltinWord(String wrd) { + return builtinWords.containsKey(wrd); + } + + private static final Map builtinWords; + + static { + /* Init builtin words. */ + builtinWords = new HashMap<>(); + + builtinWords.put("makearray", MAKEARRAY); + builtinWords.put("+stream", NEWSTREAM); + builtinWords.put(">stream", LEFTSTREAM); + builtinWords.put(" tokens) { + super(false, tokens); + } + + @Override + public String toString() { + return "WordsSCLToken [tokenVals=" + tokenVals + "]"; + } +} -- cgit v1.2.3