From c9249e857761951821249a4e05aafd0299db5da8 Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 1 Aug 2016 19:45:14 +0300 Subject: [PATCH] =?UTF-8?q?=D0=92=20REPL=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4=20=D1=84?= =?UTF-8?q?=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B9,=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=BC=D0=B5=D0=BD=D0=BD=D1=8B=D1=85=20=D0=B8=20=D0=B2?= =?UTF-8?q?=D1=81=D0=B5=D0=B3=D0=BE=20=D0=B8=D1=81=D1=85=D0=BE=D0=B4=D0=BD?= =?UTF-8?q?=D0=B8=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/Main.java | 47 +----- .../java/com/annimon/ownlang/utils/Repl.java | 140 ++++++++++++++++++ 2 files changed, 143 insertions(+), 44 deletions(-) create mode 100644 src/main/java/com/annimon/ownlang/utils/Repl.java diff --git a/src/main/java/com/annimon/ownlang/Main.java b/src/main/java/com/annimon/ownlang/Main.java index deccadd..8b8335b 100644 --- a/src/main/java/com/annimon/ownlang/Main.java +++ b/src/main/java/com/annimon/ownlang/Main.java @@ -1,6 +1,5 @@ package com.annimon.ownlang; -import com.annimon.ownlang.exceptions.LexerException; import com.annimon.ownlang.exceptions.StoppedException; import com.annimon.ownlang.parser.Beautifier; import com.annimon.ownlang.parser.Lexer; @@ -11,10 +10,10 @@ import com.annimon.ownlang.parser.SourceLoader; import com.annimon.ownlang.parser.Token; import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.visitors.FunctionAdder; +import com.annimon.ownlang.utils.Repl; import com.annimon.ownlang.utils.TimeMeasurement; import java.io.IOException; import java.util.List; -import java.util.Scanner; import java.util.concurrent.TimeUnit; /** @@ -22,7 +21,7 @@ import java.util.concurrent.TimeUnit; */ public final class Main { - private static final String VERSION = "1.2.0"; + public static final String VERSION = "1.2.0"; private static String[] ownlangArgs = new String[0]; @@ -97,7 +96,7 @@ public final class Main { case "-r": case "--repl": - repl(); + Repl.main(new String[0]); return; case "-l": @@ -192,46 +191,6 @@ public final class Main { } } } - - private static void repl() { - final StringBuilder buffer = new StringBuilder(); - final Scanner scanner = new Scanner(System.in); - System.out.println("Welcome to OwnLang " + VERSION + " REPL\n" - + "Type in expressions to have them evaluated.\n" - + "Type :reset to clear buffer.\n" - + "Type :exit to exit REPL."); - while (true) { - System.out.print((buffer.length() == 0) ? "\n> " : " "); - - if (!scanner.hasNextLine()) break; - - final String line = scanner.nextLine(); - if (":exit".equalsIgnoreCase(line)) break; - if (":reset".equalsIgnoreCase(line)) { - buffer.setLength(0); - continue; - } - - buffer.append(line).append(System.lineSeparator()); - try { - final List tokens = Lexer.tokenize(buffer.toString()); - final Parser parser = new Parser(tokens); - final Statement program = parser.parse(); - if (parser.getParseErrors().hasErrors()) { - continue; - } - program.execute(); - } catch (LexerException lex) { - continue; - } catch (StoppedException ex) { - // skip - } catch (Exception ex) { - Console.handleException(Thread.currentThread(), ex); - } - buffer.setLength(0); - } - scanner.close(); - } private static class Options { boolean showTokens, showAst, showMeasurements; diff --git a/src/main/java/com/annimon/ownlang/utils/Repl.java b/src/main/java/com/annimon/ownlang/utils/Repl.java new file mode 100644 index 0000000..765b471 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/utils/Repl.java @@ -0,0 +1,140 @@ +package com.annimon.ownlang.utils; + +import com.annimon.ownlang.Console; +import com.annimon.ownlang.Main; +import com.annimon.ownlang.exceptions.LexerException; +import com.annimon.ownlang.exceptions.StoppedException; +import com.annimon.ownlang.lib.Functions; +import com.annimon.ownlang.lib.UserDefinedFunction; +import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.parser.Lexer; +import com.annimon.ownlang.parser.Parser; +import com.annimon.ownlang.parser.Token; +import com.annimon.ownlang.parser.ast.BlockStatement; +import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.parser.visitors.PrintVisitor; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Scanner; +import java.util.stream.Collectors; + +public final class Repl { + + private static final String + HELP = ":help", + VARS = ":vars", + FUNCS = ":funcs", + SOURCE = ":source", + RESET = ":reset", + EXIT = ":exit"; + + public static void main(String[] args) { + System.out.println("Welcome to OwnLang " + Main.VERSION + " REPL"); + printHelp(false); + + final BlockStatement history = new BlockStatement(); + final StringBuilder buffer = new StringBuilder(); + final Scanner scanner = new Scanner(System.in); + while (true) { + System.out.print((buffer.length() == 0) ? "\n> " : " "); + + if (!scanner.hasNextLine()) break; + + final String line = scanner.nextLine(); + if (EXIT.equalsIgnoreCase(line)) break; + switch (line.toLowerCase(Locale.ENGLISH)) { + case RESET: + buffer.setLength(0); + continue; + case HELP: + printHelp(true); + continue; + case VARS: + printVariables(); + continue; + case FUNCS: + printFunctions(); + continue; + case SOURCE: + System.out.println(history.accept(new PrintVisitor(), new StringBuilder())); + continue; + } + + buffer.append(line).append(System.lineSeparator()); + Statement program = null; + try { + final List tokens = Lexer.tokenize(buffer.toString()); + final Parser parser = new Parser(tokens); + program = parser.parse(); + if (parser.getParseErrors().hasErrors()) { + continue; + } + program.execute(); + } catch (LexerException lex) { + continue; + } catch (StoppedException ex) { + // skip + } catch (Exception ex) { + Console.handleException(Thread.currentThread(), ex); + } + if (program != null) { + history.add(program); + } + buffer.setLength(0); + } + scanner.close(); + } + + private static void printHelp(boolean full) { + System.out.println("Type in expressions to have them evaluated."); + final List commands = new ArrayList<>(); + if (full) { + commands.add(VARS + " - listing variables"); + commands.add(FUNCS + " - listing functions"); + commands.add(SOURCE + " - listing source"); + } + commands.add(HELP + " - show help"); + commands.add(RESET + " - clear buffer"); + commands.add(EXIT + " - exit REPL"); + + int maxLength = commands.stream() + .mapToInt(String::length) + .max().getAsInt(); + + final int maxCols = 2; + final int size = commands.size(); + for (int i = 0; i < size; i += maxCols) { + // Pad to max length and print in tab-separatex maxCols columns + System.out.println(commands + .subList(i, Math.min(size, i + maxCols)) + .stream() + .map(str -> String.format("%-" + maxLength + "s", str)) + .collect(Collectors.joining("\t", " ", "")) + ); + } + } + + private static void printVariables() { + Variables.variables().entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .forEach(e -> System.out.printf("\t%s = %s%n", + e.getKey(), e.getValue().toString())); + } + + private static void printFunctions() { + System.out.println("User functions:"); + Functions.getFunctions().entrySet().stream() + .filter(p -> p.getValue() instanceof UserDefinedFunction) + .sorted(Map.Entry.comparingByKey()) + .forEach(e -> System.out.printf("\t%s%s%n", + e.getKey(), ((UserDefinedFunction)e.getValue()).arguments)); + + System.out.println("Library functions:"); + Functions.getFunctions().entrySet().stream() + .filter(p -> !(p.getValue() instanceof UserDefinedFunction)) + .sorted(Map.Entry.comparingByKey()) + .forEach(e -> System.out.printf("\t%s%n", e.getKey())); + } +}