From 7ac470c22e9e179daf0a10579a9f9e347cf6f94f Mon Sep 17 00:00:00 2001 From: "Benjamin J. Culkin" Date: Mon, 28 May 2018 13:42:11 -0300 Subject: Move SCL into new project SCL is now independant of dicelang, and thus deserving of its own repo. --- .../java/bjc/dicelang/scl/StreamControlEngine.java | 437 +++++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 src/main/java/bjc/dicelang/scl/StreamControlEngine.java (limited to 'src/main/java/bjc/dicelang/scl/StreamControlEngine.java') diff --git a/src/main/java/bjc/dicelang/scl/StreamControlEngine.java b/src/main/java/bjc/dicelang/scl/StreamControlEngine.java new file mode 100644 index 0000000..5c2b4de --- /dev/null +++ b/src/main/java/bjc/dicelang/scl/StreamControlEngine.java @@ -0,0 +1,437 @@ +package bjc.dicelang.scl; + +import java.util.HashMap; +import java.util.Map; + +import bjc.dicelang.scl.tokens.ArraySCLToken; +import bjc.dicelang.scl.tokens.BooleanSCLToken; +import bjc.dicelang.scl.tokens.IntSCLToken; +import bjc.dicelang.scl.tokens.SCLToken; +import bjc.dicelang.scl.tokens.StringLitSCLToken; +import bjc.dicelang.scl.tokens.SymbolSCLToken; +import bjc.dicelang.scl.tokens.WordListSCLToken; +import bjc.dicelang.scl.tokens.WordSCLToken; +import bjc.dicelang.scl.tokens.WordsSCLToken; +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.tokens.TokenType.*; +import static bjc.dicelang.scl.tokens.WordType.*; + +/* + * @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; + case DEFINE: + succ = handleDefine(); + if (!succ) { + return false; + } + break; + default: + Errors.inst.printError(EK_SCL_UNWORD, tk.toString()); + return false; + } + + return true; + } + + private boolean handleDefine() { + if (curStack.size() < 2) { + Errors.inst.printError(EK_SCL_SUNDERFLOW, "def"); + return false; + } + + SCLToken name = curStack.pop(); + if (name.type != SYMBOL) { + Errors.inst.printError(EK_SCL_INVARG, name.type.toString()); + return false; + } + String nam = ((SymbolSCLToken) name).stringVal; + + SCLToken def = curStack.pop(); + if (name.type != WORDS) { + Errors.inst.printError(EK_SCL_INVARG, def.type.toString()); + return false; + } + + words.put(nam, def); + return false; + } + + /* 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 StringLitSCLToken(strang)); + + return n; + } +} -- cgit v1.2.3