package bjc.dicelang; import static bjc.dicelang.Errors.ErrorKey.EK_CONS_INVDEFINE; import static bjc.dicelang.Errors.ErrorKey.EK_CONS_INVPRAG; import java.io.IOException; import java.util.LinkedList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import jline.ConsoleReader; import jline.Terminal; /** * CLI interface to DiceLang * * @author EVE * */ public class DiceLangConsole { private int commandNumber; private final DiceLangEngine eng; private ConsoleReader read; /** * 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; } System.out.println("dice-lang v0.2"); 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. */ while (!comm.equals("quit") && !comm.equals("exit")) { 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 { /* * 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; } try { comm = read.readLine(String.format("(%d) dice-lang> ", commandNumber)); } catch (final IOException ioex) { System.out.println("ERROR: I/O failed"); return; } } } 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. */ 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)); default: Errors.inst.printError(EK_CONS_INVPRAG, pragma); return false; } return true; } /* * Run a help mode. */ private static boolean helpMode(final String pragma) { 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; 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("/((?:\\\\.|[^/\\\\])*)/"); 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. */ 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; } final int priority = Integer.parseInt(defineText.substring(0, firstIndex)); final String defineType = defineText.substring(firstIndex + 1, secondIndex); Define.Type type; boolean subMode = false; 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; } final boolean doRecur = defineText.substring(secondIndex + 1, thirdIndex).equalsIgnoreCase("true"); final boolean hasGuard = defineText.substring(thirdIndex + 1, fourthIndex).equalsIgnoreCase("true"); final boolean isCircular = defineText.substring(thirdIndex + 1, fourthIndex).equalsIgnoreCase("true"); final String pats = defineText.substring(fifthIndex + 1).trim(); final Matcher patMatcher = slashPattern.matcher(pats); String guardPattern = null; if (hasGuard) { if (!patMatcher.find()) { Errors.inst.printError(EK_CONS_INVDEFINE, "(no guard pattern)"); return false; } guardPattern = patMatcher.group(1); } if (!patMatcher.find()) { 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()) { replacePatterns.add(patMatcher.group(1)); } final Define dfn = new Define(priority, subMode, doRecur, isCircular, guardPattern, searchPattern, replacePatterns); if (dfn.inError) return false; 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(); } }