From f9d9bd4bbf7dd6a297e1daf5ee7b4263d706d9cd Mon Sep 17 00:00:00 2001 From: bjculkin Date: Thu, 1 Mar 2018 19:13:48 -0500 Subject: Update --- base/src/bjc/dicelang/cli/CLIArgsParser.java | 211 ++++++++++++++ base/src/bjc/dicelang/cli/DiceLangConsole.java | 374 +++++++++++++++++++++++++ base/src/bjc/dicelang/cli/DiceLangPragma.java | 36 +++ 3 files changed, 621 insertions(+) create mode 100644 base/src/bjc/dicelang/cli/CLIArgsParser.java create mode 100644 base/src/bjc/dicelang/cli/DiceLangConsole.java create mode 100644 base/src/bjc/dicelang/cli/DiceLangPragma.java (limited to 'base/src/bjc/dicelang/cli') diff --git a/base/src/bjc/dicelang/cli/CLIArgsParser.java b/base/src/bjc/dicelang/cli/CLIArgsParser.java new file mode 100644 index 0000000..b4c0c7c --- /dev/null +++ b/base/src/bjc/dicelang/cli/CLIArgsParser.java @@ -0,0 +1,211 @@ +package bjc.dicelang.cli; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Arrays; +import java.util.Scanner; + +import bjc.dicelang.Define; +import bjc.dicelang.DiceLangEngine; +import bjc.dicelang.Errors; +import bjc.dicelang.util.ResourceLoader; + +import static bjc.dicelang.Errors.ErrorKey.*; + +/** + * Parse CLI arguments. + * + * @author Ben Culkin + * + */ +public class CLIArgsParser { + /** + * Parse the provided set of CLI arguments. + * + * @param args + * The CLI arguments to parse. + * @param eng + * The engine to affect with parsing. + * + * @return Whether or not to continue to the DiceLang repl. + */ + public static boolean parseArgs(final String[] args, final DiceLangEngine eng) { + if(args.length < 0) { + return true; + } + + if(args.length == 1 && (args[0].equals("--help") || args[0].equals("-h"))) { + for(final String lne : ResourceLoader.loadHelpFile("cli")) { + System.out.println(lne); + } + + System.exit(0); + } + + for(int i = 0; i < args.length; i++) { + final String arg = args[i]; + + /* + * @TODO 10/08/17 Ben Culkin :CLIArgRefactor + * + * Use whatever library gets added to BJC-Utils for + * this, and extend these to do more things. + */ + switch(arg) { + case "-d": + case "--debug": + if(!eng.toggleDebug()) { + eng.toggleDebug(); + } + break; + case "-nd": + case "--no-debug": + if(eng.toggleDebug()) { + eng.toggleDebug(); + } + break; + case "-po": + case "--postfix": + if(!eng.togglePostfix()) { + eng.togglePostfix(); + } + break; + case "-npo": + case "--no-postfix": + if(eng.togglePostfix()) { + eng.togglePostfix(); + } + break; + case "-pr": + case "--prefix": + if(!eng.togglePrefix()) { + eng.togglePrefix(); + } + break; + case "-npr": + case "--no-prefix": + if(eng.togglePrefix()) { + eng.togglePrefix(); + } + break; + case "-se": + case "--stepeval": + if(!eng.toggleStepEval()) { + eng.toggleStepEval(); + } + break; + case "-nse": + case "--no-stepeval": + if(eng.toggleStepEval()) { + eng.toggleStepEval(); + } + break; + case "-D": + case "--define": + i = simpleDefine(i, args, eng); + if(i == -1) { + return false; + } + break; + case "-df": + case "--define-file": + i = defineFile(i, args, eng); + if(i == -1) { + return false; + } + break; + case "-ctf": + case "--compiler-tweak-file": + /* + * @NOTE Not yet implemented. + */ + default: + Errors.inst.printError(EK_CLI_UNARG, arg); + return false; + } + } + + return true; + } + + /* Handle parsing a simple define. */ + private static int simpleDefine(final int i, final String[] args, final DiceLangEngine eng) { + /* :DefineRefactor */ + + if(i >= args.length - 1) { + Errors.inst.printError(EK_CLI_MISARG, "define"); + return -1; + } + + if(i >= args.length - 2) { + final Define dfn = new Define(5, false, false, false, null, args[i + 1], Arrays.asList("")); + + if(dfn.inError) { + return -1; + } + + eng.addLineDefine(dfn); + return i + 1; + } + + final Define dfn = new Define(5, false, false, false, null, args[i + 1], Arrays.asList(args[i + 2])); + + if(dfn.inError) { + return -1; + } + + eng.addLineDefine(dfn); + return i + 2; + } + + /* Load a series of defines from a file. */ + private static int defineFile(final int i, final String[] args, final DiceLangEngine eng) { + if(i >= args.length - 1) { + Errors.inst.printError(EK_CLI_MISARG, "define-file"); + return -1; + } + + final String fName = args[i + 1]; + + try(FileInputStream fis = new FileInputStream(fName)) { + try(Scanner scan = new Scanner(fis)) { + while(scan.hasNextLine()) { + final String ln = scan.nextLine(); + + final Define dfn = parseDefine(ln.substring(ln.indexOf(' '))); + + if(dfn == null || dfn.inError) { + return -1; + } + + if(ln.startsWith("line")) { + eng.addLineDefine(dfn); + } else if(ln.startsWith("token")) { + eng.addTokenDefine(dfn); + } else { + final String defnType = ln.substring(0, ln.indexOf(' ')); + + Errors.inst.printError(EK_CLI_INVDFNTYPE, defnType); + return -1; + } + } + } + } catch(final FileNotFoundException fnfex) { + Errors.inst.printError(EK_MISC_NOFILE, fName); + return -1; + } catch(final IOException ioex) { + Errors.inst.printError(EK_MISC_IOEX, fName); + return -1; + } + + return i + 1; + } + + private static Define parseDefine(final String ln) { + final Define res = null; + + /* :DefineRefactor */ + return res; + } +} diff --git a/base/src/bjc/dicelang/cli/DiceLangConsole.java b/base/src/bjc/dicelang/cli/DiceLangConsole.java new file mode 100644 index 0000000..c19793f --- /dev/null +++ b/base/src/bjc/dicelang/cli/DiceLangConsole.java @@ -0,0 +1,374 @@ +package bjc.dicelang.cli; + +import bjc.dicelang.Define; +import bjc.dicelang.DiceLangEngine; +import bjc.dicelang.Errors; +import bjc.dicelang.Define.Type; +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.IMap; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; +import java.util.function.BiPredicate; +import java.util.function.Predicate; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import jline.ConsoleReader; +import jline.Terminal; + +import static bjc.dicelang.Errors.ErrorKey.*; + +/** + * CLI interface to DiceLang + * + * @author EVE + * + */ +public class DiceLangConsole { + /* The number of commands executed so far. */ + private int commandNumber; + /* The engine that executes commands. */ + private final DiceLangEngine eng; + /* The place to read input from. */ + private ConsoleReader read; + + /* Are we in multi-line mode? */ + private boolean multiLine; + + private static IMap pragmas; + + static { + pragmas = new FunctionalMap<>(); + + pragmas.put("debug", new DiceLangPragma() { + + @Override + public String getDescription() { + return "Toggle debug mode, which includes a bunch more output during various stages of compilation and interpretation"; + } + + @Override + public String getBrief() { + return "Toggle Debug mode"; + } + + @Override + public boolean execute(String lne, DiceLangEngine eng) { + System.out.println("\tDebug mode is now " + eng.toggleDebug()); + return true; + } + }); + } + + /** + * Create a new console. + * + * @param args + * The CLI args for the console. + */ + public DiceLangConsole(final String[] args) { + commandNumber = 0; + eng = new DiceLangEngine(); + + if(!CLIArgsParser.parseArgs(args, eng)) { + System.exit(1); + } + + Terminal.setupTerminal(); + } + + /** Run the console. */ + public void run() { + /* Set up console. */ + try { + read = new ConsoleReader(); + } catch(final IOException ioex) { + System.out.println("ERROR: Console init failed"); + return; + } + + /* Print greeting. */ + System.out.println("dice-lang v0.2"); + + do { + String comm = null; + + /* Read initial command. */ + try { + comm = read.readLine(String.format("(%d) dice-lang> ", commandNumber)); + } catch(final IOException ioex) { + System.out.println("ERROR: I/O failed"); + return; + } + /* Run commands. */ + if(comm.equals("quit") || comm.equals("exit")) { + break; + } + + if(comm.startsWith("pragma")) { + /* Run pragmas. */ + final boolean success = handlePragma(comm.substring(7)); + + if(success) { + System.out.println("Pragma completed succesfully"); + } else { + System.out.println("Pragma execution failed"); + } + } else { + if(multiLine) { + try { + do { + String nLine = read.readLine( + String.format("(%d)\t...> ", commandNumber)); + + if(nLine.trim().equals("")) break; + + comm = String.format("%s %s", comm, nLine); + } while(true); + } catch(IOException ioex) { + System.out.println("ERROR: I/O failed"); + return; + } + } + /* Run commands. */ + if(eng.debugMode) { + System.out.printf("\tRaw command: %s\n", comm); + } + + final boolean success = eng.runCommand(comm); + + if(success) { + System.out.println("Command completed succesfully"); + } else { + System.out.println("Command execution failed"); + } + + commandNumber += 1; + } + + /* Read the next command. */ + try { + comm = read.readLine(String.format("(%d) dice-lang> ", commandNumber)); + } catch(final IOException ioex) { + System.out.println("ERROR: I/O failed"); + return; + } + } while(true); + + } + + /* Handle running pragmas. */ + private boolean handlePragma(final String pragma) { + if(eng.debugMode) { + System.out.println("\tRaw pragma: " + pragma); + } + + /* Grab the name from the arguments. */ + String pragmaName = null; + final int firstIndex = pragma.indexOf(' '); + + /* Handle argless pragmas. */ + if(firstIndex == -1) { + pragmaName = pragma; + } else { + pragmaName = pragma.substring(0, firstIndex); + } + + /* Run pragmas. */ + /* + * @TODO 10/09/17 Ben Culkin :PragmaRefactor + * + * Swap to using something that makes it easier to add pragmas. + */ + switch(pragmaName) { + case "debug": + System.out.println("\tDebug mode is now " + eng.toggleDebug()); + break; + case "postfix": + System.out.println("\tPostfix mode is now " + eng.togglePostfix()); + break; + case "prefix": + System.out.println("\tPrefix mode is now " + eng.togglePrefix()); + break; + case "stepeval": + System.out.println("\tStepeval mode is now" + eng.toggleStepEval()); + break; + case "define": + return defineMode(pragma.substring(7)); + case "help": + return helpMode(pragma.substring(5)); + case "multi-line": + multiLine = !multiLine; + break; + default: + Errors.inst.printError(EK_CONS_INVPRAG, pragma); + return false; + } + + return true; + } + + /* Run a help mode. */ + private static boolean helpMode(final String pragma) { + /* Get the help topic. */ + switch(pragma.trim()) { + case "help": + System.out.println("\tGet help on pragmas"); + break; + case "debug": + System.out.println("\tToggle debug mode. (Output stage results)"); + break; + case "postfix": + System.out.println("\tToggle postfix mode. (Don't shunt tokens)"); + break; + case "prefix": + System.out.println("\tToggle prefix mode. (Reverse token order instead of shunting)"); + break; + case "stepeval": + System.out.println("\tToggle stepeval mode. (Print out evaluation progress)"); + break; + case "define": + System.out.println("\tAdd a macro rewrite directive."); + System.out.println("\tdefine ..."); + break; + case "multi-line": + System.out.println("\tToggle multi-line input mode."); + break; + default: + System.out.println("\tNo help available for pragma " + pragma); + } + /* Help always works */ + return true; + } + + /* Matches slash-delimited strings (like /text/ or /text\/text/). */ + private final Pattern slashPattern = Pattern.compile("/((?:\\\\.|[^/\\\\])*)/"); + + /* Parse a define macro. */ + private boolean defineMode(final String defineText) { + /* Grab all of the separator spaces. */ + final int firstIndex = defineText.indexOf(' '); + final int secondIndex = defineText.indexOf(' ', firstIndex + 1); + final int thirdIndex = defineText.indexOf(' ', secondIndex + 1); + final int fourthIndex = defineText.indexOf(' ', thirdIndex + 1); + final int fifthIndex = defineText.indexOf(' ', fourthIndex + 1); + final int sixthIndex = defineText.indexOf(' ', fifthIndex + 1); + + /* + * Error if we got something we didn't need, or didn't get + * something we need. + */ + if(firstIndex == -1) { + Errors.inst.printError(EK_CONS_INVDEFINE, "(no priority)"); + return false; + } else if(secondIndex == -1) { + Errors.inst.printError(EK_CONS_INVDEFINE, "(no define type)"); + return false; + } else if(thirdIndex == -1) { + Errors.inst.printError(EK_CONS_INVDEFINE, "(no recursion type)"); + return false; + } else if(fourthIndex == -1) { + Errors.inst.printError(EK_CONS_INVDEFINE, "(no guard type)"); + return false; + } else if(fifthIndex == -1) { + Errors.inst.printError(EK_CONS_INVDEFINE, "(no circularity)"); + return false; + } else if(sixthIndex == -1) { + Errors.inst.printError(EK_CONS_INVDEFINE, "(no patterns)"); + return false; + } + + /* Get the priority and define type. */ + final int priority = Integer.parseInt(defineText.substring(0, firstIndex)); + final String defineType = defineText.substring(firstIndex + 1, secondIndex); + + Define.Type type; + boolean subMode = false; + + /* Parse the define type. */ + switch(defineType) { + case "line": + type = Define.Type.LINE; + break; + case "token": + type = Define.Type.TOKEN; + break; + case "subline": + type = Define.Type.LINE; + subMode = true; + break; + case "subtoken": + type = Define.Type.TOKEN; + subMode = true; + break; + default: + Errors.inst.printError(EK_CONS_INVDEFINE, "(unknown type)"); + return false; + } + + /* Do we want this to be a recursive pattern? */ + final boolean doRecur = defineText.substring(secondIndex + 1, thirdIndex).equalsIgnoreCase("true"); + /* Do we want this pattern to have a guard? */ + final boolean hasGuard = defineText.substring(thirdIndex + 1, fourthIndex).equalsIgnoreCase("true"); + /* Do we want this pattern to use circular replacements. */ + final boolean isCircular = defineText.substring(thirdIndex + 1, fourthIndex).equalsIgnoreCase("true"); + + /* The part of the string that contains patterns. */ + final String pats = defineText.substring(fifthIndex + 1).trim(); + final Matcher patMatcher = slashPattern.matcher(pats); + String guardPattern = null; + + if(hasGuard) { + /* Grab the guard pattern. */ + if(!patMatcher.find()) { + Errors.inst.printError(EK_CONS_INVDEFINE, "(no guard pattern)"); + return false; + } + + guardPattern = patMatcher.group(1); + } + + if(!patMatcher.find()) { + /* Grab the search pattern. */ + Errors.inst.printError(EK_CONS_INVDEFINE, "(no search pattern)"); + return false; + } + + final String searchPattern = patMatcher.group(1); + final List replacePatterns = new LinkedList<>(); + + while(patMatcher.find()) { + /* Grab the replacer patterns. */ + replacePatterns.add(patMatcher.group(1)); + } + + final Define dfn = new Define(priority, subMode, doRecur, isCircular, guardPattern, searchPattern, + replacePatterns); + + if(dfn.inError) { + return false; + } + + /* Add the define to the proper place. */ + if(type == Define.Type.LINE) { + eng.addLineDefine(dfn); + } else { + eng.addTokenDefine(dfn); + } + + return true; + } + + /** + * Main method. + * + * @param args + * CLI arguments. + */ + public static void main(final String[] args) { + final DiceLangConsole console = new DiceLangConsole(args); + console.run(); + } +} diff --git a/base/src/bjc/dicelang/cli/DiceLangPragma.java b/base/src/bjc/dicelang/cli/DiceLangPragma.java new file mode 100644 index 0000000..9bbdf49 --- /dev/null +++ b/base/src/bjc/dicelang/cli/DiceLangPragma.java @@ -0,0 +1,36 @@ +package bjc.dicelang.cli; + +import bjc.dicelang.DiceLangEngine; + +/** + * Represents a pragma for the command interface. + * + * @author EVE + * + */ +public interface DiceLangPragma { + /** + * Execute the pragma. + * + * @param lne + * The command line the pragma came from. + * @param eng + * The engine we are attached to. + * @return Whether or not the pragma succeeded. + */ + public boolean execute(String lne, DiceLangEngine eng); + + /** + * Get a description on how to use this pragma + * + * @return The description on how to use the pragma + */ + public String getDescription(); + + /** + * Get a brief idea on what this pragma does + * + * @return A brief description of what this pragma does. + */ + public String getBrief(); +} -- cgit v1.2.3