diff options
Diffstat (limited to 'dice/src/example/java/bjc/dicelang/neodice/DieBoxCLI.java')
| -rw-r--r-- | dice/src/example/java/bjc/dicelang/neodice/DieBoxCLI.java | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/dice/src/example/java/bjc/dicelang/neodice/DieBoxCLI.java b/dice/src/example/java/bjc/dicelang/neodice/DieBoxCLI.java new file mode 100644 index 0000000..557fd51 --- /dev/null +++ b/dice/src/example/java/bjc/dicelang/neodice/DieBoxCLI.java @@ -0,0 +1,219 @@ +package bjc.dicelang.neodice; + +import static bjc.dicelang.neodice.statements.BooleanStatementValue.*; +import static bjc.dicelang.neodice.statements.VoidStatementValue.*; + +import java.io.*; +import java.util.*; +import java.util.regex.*; + +import bjc.data.*; +import bjc.dicelang.neodice.commands.*; +import bjc.dicelang.neodice.statements.*; +import bjc.funcdata.*; + +/** + * Command-line interface for testing the neodice implementation. + * + * @author Ben Culkin + * + */ +public class DieBoxCLI { + private static final Pattern INT_PATTERN = Pattern.compile("(?:\\+|-)?\\d+"); + Scanner input; + public PrintStream output; + + public IMap<String, StatementValue> bindings = new FunctionalMap<>(); + + public Random rng = new Random(); + + static final IMap<String, Command> builtInCommands; + static final IMap<String, Command> builtInliterals; + + final IMap<String, Command> commands; + final IMap<String, Command> literals; + + private int numStatements = 0; + + /** + * Whether or not to print out a prompt before asking for input + */ + public boolean doPrompt = true; + + /** + * Whether or not to output the results of each command. + */ + public boolean doOutput = true; + + /** + * Should warning messages be printed? + */ + public boolean doWarn = true; + + static { + // Initialize all of our literal-formers + builtInliterals = new FunctionalMap<>(); + + builtInliterals.put("void", + new LiteralCommand( + VOID_INST, + "the unique instance of type VOID", + "Returns a reference to the unique instance of type VOID.")); + builtInliterals.put("true", + new LiteralCommand( + TRUE_INST, + "the unique true value of type BOOLEAN", + "Returns a reference to the unique true instance of type BOOLEAN")); + builtInliterals.put("false", + new LiteralCommand( + FALSE_INST, + "the unique false value of type BOOLEAN", + "Returns a reference to the unique false instance of type BOOLEAN")); + + builtInliterals.deepFreeze(); + + // Initialize all of our built-in commands + builtInCommands = new FunctionalMap<>(); + + builtInCommands.put("show-bindings", new ShowBindingsCommand()); + builtInCommands.put("bind", new BindCommand()); + builtInCommands.put("polyhedral-die", new PolyhedralDieCommand()); + builtInCommands.put("roll", new RollCommand()); + builtInCommands.put("help", new HelpCommand()); + builtInCommands.deepFreeze(); + } + + /** + * Create a new CLI for interacting with dice. + * + * @param input The place to read input from. + * @param output The place to read output from. + */ + public DieBoxCLI(Scanner input, PrintStream output) { + this.input = input; + this.output = output; + + this.commands = builtInCommands.extend(); + this.literals = builtInliterals.extend(); + } + + /** + * Create a new CLI for interacting with dice. + * + * @param input The place to read input from. + * @param output The place to read output from. + */ + public DieBoxCLI(InputStream input, OutputStream output) { + this(new Scanner(input), new PrintStream(output)); + } + + /** + * Main method. + * + * @param args Currently unused CLI arguments. + */ + public static void main(String[] args) { + Scanner input = new Scanner(System.in); + PrintStream output = System.out; + + DieBoxCLI box = new DieBoxCLI(input, output); + box.run(); + } + + private void run() { + if (doPrompt) { + output.println("diebox CLI - enter 'help' for help, 'exit' to exit"); + } + + if (doPrompt) output.printf("diebox(%d)> ", numStatements); + while(input.hasNextLine()) { + String nextLine = input.nextLine().trim(); + + numStatements += 1; + + if (nextLine.equals("")) continue; + // @FIXME Nov 15th, 2020 Ben Culkin :HardcodeExit + // Exit should not be hard-coded like this + if (nextLine.equals("exit")) break; + + String[] lineWords = nextLine.split("\\s+"); + Iterator<String> wordIter = new ArrayIterator<>(lineWords); + try { + StatementValue val = runStatement(wordIter); + + if (doOutput) output.printf("%s%s\n", doPrompt ? "==> " : "", val); + } catch (DieBoxException dbex) { + output.printf("ERROR (in statement %d): %s\n", + numStatements, dbex.getMessage()); + Throwable curEx = dbex.getCause(); + while (curEx != null) { + output.printf("...caused by: %s\n", curEx); + + curEx = dbex.getCause(); + } + } catch (Exception ex) { + output.printf("INTERNAL ERROR (in statement %d): %s\n", + numStatements, ex.getMessage()); + ex.printStackTrace(output); + } + + if (doPrompt) output.printf("diebox(%d)> ", numStatements); + } + + input.close(); + output.close(); + } + + public StatementValue runStatement(Iterator<String> words) { + if (!words.hasNext()) { + return VOID_INST; + } + + String command = words.next().trim(); + + if (command.startsWith("$")) { + // All variable refs start with $ + String varName = command.substring(1); + + if (bindings.containsKey(varName)) { + return bindings.get(varName); + } else { + // @TODO Nov 15th, 2020 Ben Culkin :Autovars + // Perhaps something along the lines of 'auto-variables' (here + // called 'spring-loaded variables') should be created? These + // would be essentially values which invoke a given expression + // whenever they are referenced. + throw new DieBoxException("Attempted to reference non-existing variable %s", varName); + } + } else if (command.startsWith("#")) { + // All literals/literal-formers start with # + String actualCommand = command.substring(1); + + // Attempt to use a mapped literal/literal-former + Command literalCommand = literals.get(actualCommand); + if (literalCommand != null) { + return literalCommand.execute(words, this); + } else { + if (INT_PATTERN.matcher(actualCommand).matches()) { + try { + int val = Integer.parseInt(actualCommand); + + return new IntegerStatementValue(val); + } catch (NumberFormatException nfex) { + throw new DieBoxException(nfex, "Improper integer literal (%s)", actualCommand); + } + } else { + throw new DieBoxException("Unknown literal format (%s)", actualCommand); + } + } + } else { + // Attempt to use a mapped command first + Command mapCommand = commands.get(command); + if (mapCommand != null) { + return mapCommand.execute(words, this); + } else { + throw new DieBoxException("Unknown command %s", command); + } + } + } +}
\ No newline at end of file |
