diff --git a/latte-build/.gitignore b/latte-build/.gitignore index b9c3aae..6f5b292 100644 --- a/latte-build/.gitignore +++ b/latte-build/.gitignore @@ -3,3 +3,4 @@ build/* *.ipr *.iws +out/* diff --git a/latte-build/build.gradle b/latte-build/build.gradle index cb4de95..0c6ef22 100644 --- a/latte-build/build.gradle +++ b/latte-build/build.gradle @@ -17,7 +17,7 @@ if ('deploy' == ACTION) { } apply plugin: LatteBuild -mainClassName = 'lt.repl.REPL' +mainClassName = 'lt.repl.Entry' applicationName = 'latte' sourceCompatibility = 1.6 @@ -105,7 +105,7 @@ if (null == ACTION) { jar { manifest { attributes("Manifest-Version": 1.0, - "Main-Class": "lt.repl.REPL") + "Main-Class": "lt.repl.Entry") } from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } into('assets') { @@ -123,14 +123,14 @@ if ('deploy' == ACTION) { dependencies { compile group: 'org.latte-lang', name: 'latte-compiler', version: VERSION compile group: 'org.latte-lang', name: 'latte-library', version: VERSION - provided group: 'jline', name: 'jline', version: '2.14.2' + provided group: 'jline', name: 'jline', version: '2.14.5' } class LatteBuild implements Plugin { @Override void apply(Project project) { def latteBuild = project.task('latteBuild') - latteBuild.dependsOn project.tasks['assemble'] + latteBuild.dependsOn project.tasks['install'] project.task('latteTest') // do nothing diff --git a/latte-build/src/main/java/lt/repl/CtrlCHandler.java b/latte-build/src/main/java/lt/repl/CtrlCHandler.java index f2a5ebd..84d355a 100644 --- a/latte-build/src/main/java/lt/repl/CtrlCHandler.java +++ b/latte-build/src/main/java/lt/repl/CtrlCHandler.java @@ -4,7 +4,13 @@ * handle ctrl-c (INT) event */ public interface CtrlCHandler { - void handle(); + interface ExitCallback { + void exit(); + } + + void setExitCallback(ExitCallback exitCallback); - void onAlert(Runnable alert); + void setAlert(Runnable alert); + + void handle(); } diff --git a/latte-build/src/main/java/lt/repl/CtrlCHandlerImpl.java b/latte-build/src/main/java/lt/repl/CtrlCHandlerImpl.java new file mode 100644 index 0000000..13e409d --- /dev/null +++ b/latte-build/src/main/java/lt/repl/CtrlCHandlerImpl.java @@ -0,0 +1,53 @@ +package lt.repl; + +import sun.misc.Signal; +import sun.misc.SignalHandler; + +/** + * handle ctrl-c (INT) event + */ +public class CtrlCHandlerImpl implements CtrlCHandler { + private Runnable alert = null; + private ExitCallback exitCallback = null; + private final IO io; + private int count; + + public CtrlCHandlerImpl(IO io) { + this.io = io; + } + + @Override + public void setExitCallback(final ExitCallback exitCallback) { + this.exitCallback = exitCallback; + } + + @Override + public void setAlert(Runnable alert) { + this.alert = alert; + } + + @Override + public void handle() { + ++count; + if (count == 2) { + io.out.println(); + exitCallback.exit(); + } else { + io.out.println("\n(To exit, press ^C again or type :q)"); + if (alert != null) { + alert.run(); + } + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(io.err); + } + count = 0; + } + }).run(); + } + } +} diff --git a/latte-build/src/main/java/lt/repl/CtrlCSignalHandler.java b/latte-build/src/main/java/lt/repl/CtrlCSignalHandler.java deleted file mode 100644 index 523c357..0000000 --- a/latte-build/src/main/java/lt/repl/CtrlCSignalHandler.java +++ /dev/null @@ -1,52 +0,0 @@ -package lt.repl; - -import sun.misc.Signal; -import sun.misc.SignalHandler; - -/** - * handle ctrl-c (INT) event - */ -public class CtrlCSignalHandler implements CtrlCHandler { - private Runnable alert = null; - - @Override - public void handle() { - SignalHandler handler = new SignalHandler() { - private int count = 0; - - @Override - public void handle(Signal signal) { - if (!signal.getName().equals("INT")) { - return; - } - ++count; - if (count == 2) { - System.out.println(); - System.exit(2); // SIGINT - } else { - System.out.println("\n(To exit, press ^C again or type :q)"); - if (alert != null) { - alert.run(); - } - new Thread(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - count = 0; - } - }).run(); - } - } - }; - Signal.handle(new Signal("INT"), handler); - } - - @Override - public void onAlert(Runnable alert) { - this.alert = alert; - } -} diff --git a/latte-build/src/main/java/lt/repl/Entry.java b/latte-build/src/main/java/lt/repl/Entry.java new file mode 100644 index 0000000..11ac6fe --- /dev/null +++ b/latte-build/src/main/java/lt/repl/Entry.java @@ -0,0 +1,271 @@ +package lt.repl; + +import lt.compiler.SyntaxException; +import lt.repl.scripting.EvalEntry; +import lt.util.Utils; + +import java.io.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class Entry { + public static void main(String[] args) throws Exception { + if (args == null || args.length > 0) { + runCommands(args); + } else { + StringReader reader; + try { + Class.forName("jline.console.ConsoleReader"); // check exists + reader = (StringReader) Class.forName("lt.repl.JLineStringReader") + .getConstructor().newInstance(); + } catch (ClassNotFoundException ignore) { + reader = new SimpleStringReader(); + } + + REPL repl = new REPL(reader, new IO(System.in, System.out, System.err), + new CtrlCHandler.ExitCallback() { + @Override + public void exit() { + System.exit(0); + } + }); + repl.handleCtrlC(); + repl.start(); + } + } + + /** + * run the program with arguments + * + * @param args arguments + */ + private static void runCommands(String[] args) throws Exception { + String command = args[0]; + if (command.equals("help") || command.equals("-help") || command.equals("--help") || command.equals("-h") || command.equals("/h") || command.equals("/help")) {// help + System.out.println("" + + "usage: -s [arguments [,...]]\n" + + " -c [-r] [-o ] [-cp ]\n" + + " ClassName [-cp ]\n" + + " -gb \n" + + " -repl\n" + + " -v | -version\n" + + " -e \n" + + "\n" + + "-s Specify the script location and run the script\n" + + "-c Specify the source file directory and compile *.lt files\n" + + "-r [option] Add sub directory files to compiling list.\n" + + "-o [option] Specify the output directory. (the source-directory/target/classes/ as default)\n" + + "-cp [option] The classpath. use ':' to separate the class-paths\n" + + "-repl Start the repl (or run the program with 0 arguments)\n" + + "-gb Generate build.lts and run.lts in the given directory\n" + + "-e Evaluate the given statement and print the result\n" + + "-version Show current version\n"); + + } else if (command.equals("-v") || command.equals("-version")) { + System.out.println("Latte-lang " + VersionRetriever.version()); + + } else if (command.equals("-s")) {// run scripts + // -s ? + if (args.length < 2) { + System.err.println("invalid command -s. the script file location should be specified\n" + + "see --help"); + return; + } + String path = args[1]; + File f = new File(path); + ScriptCompiler s = new ScriptCompiler(ClassLoader.getSystemClassLoader()); + try { + ScriptCompiler.Script script = s.compile(f); + String[] scriptArgs = new String[args.length - 2]; + System.arraycopy(args, 2, scriptArgs, 0, args.length - 2); + script.run(scriptArgs); + } catch (Throwable e) { + if (e instanceof SyntaxException) { + System.err.println("[ERROR] " + e.getMessage()); + } else { + e.printStackTrace(); + } + } + + } else if (command.equals("-c")) {// compile + // -c ? + if (args.length < 2) { + System.err.println("invalid command -c. the source directory should be specified\n" + + "see --help"); + return; + } + String sourceDir = args[1].trim(); + if (sourceDir.endsWith(File.separator)) { + sourceDir = sourceDir.substring(0, sourceDir.length() - File.separator.length()); + } + + boolean recursive = false; + String outputDir = sourceDir + File.separator + "target" + File.separator + "classes"; + List classPaths = new ArrayList(); + + for (int i = 2; i < args.length; ++i) { + String cmd = args[i]; + if (cmd.equals("-r")) { + recursive = true; + + } else if (cmd.equals("-o")) { + if (args.length - 1 == i) { + System.err.println("invalid option -o. the output directory should be specified"); + System.err.println("see --help"); + return; + } + outputDir = args[++i]; + + } else if (cmd.equals("-cp")) { + if (args.length - 1 == i) { + System.err.println("invalid option -cp. the class-path should be specified"); + System.err.println("see --help"); + return; + } + String[] class_paths = args[++i].split(":"); + for (String class_path : class_paths) { + try { + classPaths.add(new URL(new File(class_path).toURI().toString())); + } catch (MalformedURLException e) { + System.err.println("[ERROR] " + e.getMessage()); + return; + } + } + + + } else { + System.err.println("unknown option " + cmd); + System.err.println("see --help"); + return; + } + } + + Compiler compiler = new Compiler(); + File outputDirFile = new File(outputDir); + if (!outputDirFile.exists()) //noinspection ResultOfMethodCallIgnored + outputDirFile.mkdirs(); + compiler.config.result.outputDir = outputDirFile; + compiler.config.classpath = classPaths; + + try { + compiler.compile(Utils.filesInDirectory(sourceDir, ".*\\.(lt|latte)", recursive)); + } catch (Exception e) { + if (e instanceof SyntaxException) { + System.err.println("[ERROR] " + e.getMessage()); + } else { + e.printStackTrace(); + } + } + + } else if (command.equals("-gb")) { + final List theFilesToBeGenerated = Arrays.asList("build.lts", "run.lts"); + + if (args.length != 2) { + System.err.println("invalid command -gb."); + System.err.println("see --help"); + return; + } + String projectDir = args[1].trim(); + if (projectDir.endsWith(File.separator)) { + projectDir = projectDir.substring(0, projectDir.length() - File.separator.length()); + } + + String core = String.valueOf(Runtime.getRuntime().availableProcessors()); + String separator = File.separator; + + for (String theFile : theFilesToBeGenerated) { + String filePath = projectDir + File.separator + theFile; + File file = new File(filePath); + if (file.exists()) { + System.out.println("[INFO] " + filePath + " exists"); + } else { + try { + //noinspection ResultOfMethodCallIgnored + file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + try { + FileWriter fw = new FileWriter(file); + BufferedReader br = new BufferedReader(new InputStreamReader(REPL.class.getClassLoader().getResourceAsStream(theFile + ".template"))); + + String ss; + while ((ss = br.readLine()) != null) { + ss = ss.replace("${core}", core) + .replace("${dir}", projectDir.replace("\\", "\\\\")) + .replace("${separator}", separator) + "\n"; + fw.write(ss.toCharArray()); + } + fw.flush(); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + } else if (command.equals("-repl")) {// repl + main(new String[0]); + } else if (command.equals("-e")) {// eval + if (args.length == 1 || args[1] == null || args[1].trim().isEmpty()) { + System.err.println("missing statements to eval"); + return; + } + String statements = args[1].trim(); + Evaluator e = new Evaluator(new ClassPathLoader(Thread.currentThread().getContextClassLoader())); + EvalEntry entry = e.eval(statements); + System.out.println(entry.result); + + } else {// run + List urls = new ArrayList(); + try { + String url = new File("").toURI().toString(); + urls.add(new URL(url)); + } catch (MalformedURLException e) { + System.err.println("[ERROR] " + e.getMessage()); + return; + } + String[] runArgs = new String[0]; + for (int i = 1; i < args.length; ++i) { + String cmd = args[i]; + if (cmd.equals("-cp")) { + if (i == args.length - 1) { + System.err.println("invalid option -cp. the class-path should be specified"); + System.err.println("see --help"); + return; + } + String cps = args[++i]; + for (String cp : cps.split(":")) { + try { + urls.add(new URL(new File(cp).toURI().toString())); + } catch (MalformedURLException e) { + System.err.println("[ERROR] " + e.getMessage()); + return; + } + } + + } else if (cmd.equals("-args")) { + runArgs = new String[args.length - 1 - i]; + System.arraycopy(args, i + 1, runArgs, 0, runArgs.length); + break; + } else { + System.err.println("unknown option " + cmd); + System.err.println("see --help"); + return; + } + } + + try { + // run the class + Run run = new Run(urls, command); + run.exec(runArgs); + } catch (Throwable t) { + t.printStackTrace(); + } + } + } +} diff --git a/latte-build/src/main/java/lt/repl/IO.java b/latte-build/src/main/java/lt/repl/IO.java new file mode 100644 index 0000000..5d28216 --- /dev/null +++ b/latte-build/src/main/java/lt/repl/IO.java @@ -0,0 +1,22 @@ +package lt.repl; + +import java.io.*; + +public class IO { + public final InputStream inStream; + public final OutputStream outStream; + public final OutputStream errStream; + public final Reader in; + public final PrintStream out; + public final PrintStream err; + + public IO(InputStream in, OutputStream out, OutputStream err) { + this.inStream = in; + this.outStream = out; + this.errStream = err; + + this.in = new InputStreamReader(in); + this.out = new PrintStream(out, true); + this.err = new PrintStream(err, true); + } +} diff --git a/latte-build/src/main/java/lt/repl/JLineLineReader.java b/latte-build/src/main/java/lt/repl/JLineLineReader.java deleted file mode 100644 index c66a9e2..0000000 --- a/latte-build/src/main/java/lt/repl/JLineLineReader.java +++ /dev/null @@ -1,31 +0,0 @@ -package lt.repl; - -import java.lang.reflect.Method; - -/** - * read line from jLine - */ -public class JLineLineReader implements LineReader { - private final Class ConsoleReader; - private final Method readLine; - private Object reader; - - public JLineLineReader() { - try { - ConsoleReader = Class.forName("jline.console.ConsoleReader"); - readLine = ConsoleReader.getMethod("readLine"); - } catch (Throwable e) { - throw new Error(e); - } - System.out.println("using jline.console.ConsoleReader to read input"); - } - - @Override - public String readLine() throws Exception { - if (reader == null) { - reader = ConsoleReader.newInstance(); - } - - return (String) readLine.invoke(reader); - } -} diff --git a/latte-build/src/main/java/lt/repl/JLineStringReader.java b/latte-build/src/main/java/lt/repl/JLineStringReader.java new file mode 100644 index 0000000..b99924d --- /dev/null +++ b/latte-build/src/main/java/lt/repl/JLineStringReader.java @@ -0,0 +1,58 @@ +package lt.repl; + +import jline.console.ConsoleReader; +import jline.console.UserInterruptException; + +import java.io.IOException; + +/** + * read line from jLine + */ +public class JLineStringReader implements StringReader { + private ConsoleReader reader; + private IO io; + private CtrlCHandler handler = null; + + public JLineStringReader() { + } + + @Override + public void setIO(IO io) { + this.io = io; + io.out.println("using jline.console.ConsoleReader to read input"); + } + + @Override + public void setCtrlCHandler(CtrlCHandler handler) { + this.handler = handler; + } + + private void initReader() throws IOException { + if (reader != null) { + reader.close(); + } + reader = new ConsoleReader(io.inStream, io.outStream); + reader.setHandleUserInterrupt(true); + } + + @Override + public String read() throws Exception { + if (reader == null) { + initReader(); + } + + String line; + while (true) { + try { + line = reader.readLine(); + break; + } catch (UserInterruptException ignore) { + if (handler != null) { + handler.handle(); + } + } + } + if (line == null) return null; + return line + '\n'; + } +} diff --git a/latte-build/src/main/java/lt/repl/LineReader.java b/latte-build/src/main/java/lt/repl/LineReader.java deleted file mode 100644 index f94edad..0000000 --- a/latte-build/src/main/java/lt/repl/LineReader.java +++ /dev/null @@ -1,8 +0,0 @@ -package lt.repl; - -/** - * reads a line - */ -public interface LineReader { - String readLine() throws Exception; -} diff --git a/latte-build/src/main/java/lt/repl/REPL.java b/latte-build/src/main/java/lt/repl/REPL.java index 5cbe00f..e69fdb2 100644 --- a/latte-build/src/main/java/lt/repl/REPL.java +++ b/latte-build/src/main/java/lt/repl/REPL.java @@ -27,463 +27,225 @@ import lt.compiler.SyntaxException; import lt.repl.scripting.Config; import lt.repl.scripting.EvalEntry; -import lt.util.Utils; import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.net.MalformedURLException; import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; /** * repl */ public class REPL { - private static final String lineStarter = "lt> "; - private static final String multipleLine = " | "; - private static StringBuilder replSourceRec = new StringBuilder(); - - private REPL() { + private final String lineStarter = "lt> "; + private final String multipleLine = " | "; + private StringBuilder replSourceRec = new StringBuilder(); + private final IO io; + private final CtrlCHandler.ExitCallback exitCallback; + private StringReader reader; + + public REPL(StringReader reader, IO io, CtrlCHandler.ExitCallback exitCallback) { + this.reader = reader; + this.io = io; + this.exitCallback = exitCallback; } - public static void main(String[] args) throws Exception { - if (args != null && args.length != 0) { - runCommands(args); - } else { - System.out.println("Welcome to Latte lang"); - System.out.println("Type in expressions and double Enter to have them evaluated."); - System.out.println("Type :help for more information."); - System.out.println("for syntax help, please visit https://github.com/wkgcass/Latte-lang/"); - System.out.println(); + public void start() throws Exception { + io.out.println("Welcome to Latte lang"); + io.out.println("Type in expressions and double Enter to have them evaluated."); + io.out.println("Type :help for more information."); + io.out.println("for syntax help, please visit https://github.com/wkgcass/Latte-lang/"); + io.out.println(); - handleCtrlC(); + ClassPathLoader classPathLoader = new ClassPathLoader(Thread.currentThread().getContextClassLoader()); - ClassPathLoader classPathLoader = new ClassPathLoader(Thread.currentThread().getContextClassLoader()); + Evaluator evaluator = new Evaluator(classPathLoader); + reader.setIO(io); + io.out.println(); + io.out.print(lineStarter); - Evaluator evaluator = new Evaluator(classPathLoader); - LineReader reader; - if (System.console() == null) { - reader = new ScannerLineReader(); - } else { - try { - Class.forName("jline.console.ConsoleReader"); // check exists - reader = (LineReader) Class.forName("lt.repl.JLineLineReader").newInstance(); - } catch (ClassNotFoundException ignore) { - reader = new ScannerLineReader(); - } - } - System.out.println(); - System.out.print(lineStarter); + StringBuilder strBuilder = new StringBuilder(); + out: + while (true) { while (true) { - String str = reader.readLine(); - if (str.trim().startsWith(":")) { - String cmd = str.trim(); - // some functions that controls the REPL - if (cmd.equals(":help")) { - System.out.println(":q exit the REPL"); - System.out.println(":reset reset the repl to its initial state, forgetting all session entries"); - System.out.println(":restart restart the repl environment, drop all loaded jars"); - System.out.println(": set current input to empty string"); - System.out.println(":cp load classes"); - System.out.println(":script compile a script"); - System.out.println("----------------------------------------------------------------"); - System.out.println(":scanner-indent use IndentScanner to scan input"); - System.out.println(":scanner-brace use BraceScanner to scan input"); - System.out.println("----------------------------------------------------------------"); - System.out.println("Compiler() construct a new Compiler"); - System.out.println("compiler >> '' set compiler output directory"); - System.out.println("compiler compile filesInDirectory('')"); - System.out.println(" start compiling and generate class files"); - System.out.println("----------------------------------------------------------------"); - System.out.println("ScriptCompiler() construct a new ScriptCompiler"); - System.out.println("scriptCompiler << File('') add source code file"); - System.out.println("scriptCompiler compile File('script') compile the script"); - } else if (cmd.equals(":q")) { - break; - } else if (cmd.equals(":reset")) { - replSourceRec.delete(0, replSourceRec.length()); - evaluator = new Evaluator(classPathLoader); - } else if (cmd.equals(":restart")) { - replSourceRec.delete(0, replSourceRec.length()); - classPathLoader = new ClassPathLoader(Thread.currentThread().getContextClassLoader()); - evaluator = new Evaluator(classPathLoader); - } else if (cmd.startsWith(":cp ")) { - String cp = cmd.substring(":cp ".length()).trim(); - try { - URL url = new URL(new File(cp).toURI().toString()); - classPathLoader.load(url); - } catch (Throwable t) { - t.printStackTrace(); - sleep(10); - } - } else if (cmd.startsWith(":script ")) { - String run = cmd.substring(":script ".length()).trim(); - try { - ScriptCompiler scriptCompiler = new ScriptCompiler(classPathLoader); - ScriptCompiler.Script script = scriptCompiler.compile(new File(run)); - evaluator.put("script", script); - System.out.println("script : " + script.getClass().getName() + " = " + script); - } catch (Throwable t) { - t.printStackTrace(); - sleep(10); - } - } else if (cmd.equals(":scanner-indent")) { - evaluator.setScannerType(Config.SCANNER_TYPE_INDENT); - } else if (cmd.equals(":scanner-brace")) { - evaluator.setScannerType(Config.SCANNER_TYPE_BRACE); - } else if (cmd.equals(":")) { - replSourceRec.delete(0, replSourceRec.length()); + String s = reader.read(); + if (s == null) { + // the reader reached EOF + break out; + } + if (s.endsWith("\n") || s.endsWith("\r")) { + if (s.endsWith("\r\n")) { + strBuilder.append(s.subSequence(0, s.length() - 2)); } else { - System.err.println("unknown command " + cmd + ", Type :help for more more information"); + strBuilder.append(s.subSequence(0, s.length() - 1)); + } + break; + } + strBuilder.append(s); + } + String str = strBuilder.toString(); + strBuilder.delete(0, strBuilder.length()); + if (str.trim().startsWith(":")) { + String cmd = str.trim(); + // some functions that controls the REPL + if (cmd.equals(":help")) { + io.out.println(":q exit the REPL"); + io.out.println(":reset reset the repl to its initial state, forgetting all session entries"); + io.out.println(":restart restart the repl environment, drop all loaded jars"); + io.out.println(": set current input to empty string"); + io.out.println(":cp load classes"); + io.out.println(":script compile a script"); + io.out.println("----------------------------------------------------------------"); + io.out.println(":scanner-indent use IndentScanner to scan input"); + io.out.println(":scanner-brace use BraceScanner to scan input"); + io.out.println("----------------------------------------------------------------"); + io.out.println("Compiler() construct a new Compiler"); + io.out.println("compiler >> '' set compiler output directory"); + io.out.println("compiler compile filesInDirectory('')"); + io.out.println(" start compiling and generate class files"); + io.out.println("----------------------------------------------------------------"); + io.out.println("ScriptCompiler() construct a new ScriptCompiler"); + io.out.println("scriptCompiler << File('') add source code file"); + io.out.println("scriptCompiler compile File('script') compile the script"); + } else if (cmd.equals(":q")) { + break; + } else if (cmd.equals(":reset")) { + replSourceRec.delete(0, replSourceRec.length()); + evaluator = new Evaluator(classPathLoader); + } else if (cmd.equals(":restart")) { + replSourceRec.delete(0, replSourceRec.length()); + classPathLoader = new ClassPathLoader(Thread.currentThread().getContextClassLoader()); + evaluator = new Evaluator(classPathLoader); + } else if (cmd.startsWith(":cp ")) { + String cp = cmd.substring(":cp ".length()).trim(); + try { + URL url = new URL(new File(cp).toURI().toString()); + classPathLoader.load(url); + } catch (Throwable t) { + t.printStackTrace(io.err); + sleep(10); + } + } else if (cmd.startsWith(":script ")) { + String run = cmd.substring(":script ".length()).trim(); + try { + ScriptCompiler scriptCompiler = new ScriptCompiler(classPathLoader); + ScriptCompiler.Script script = scriptCompiler.compile(new File(run)); + evaluator.put("script", script); + io.out.println("script : " + script.getClass().getName() + " = " + script); + } catch (Throwable t) { + t.printStackTrace(io.err); sleep(10); } - System.out.print("\n" + lineStarter); + } else if (cmd.equals(":scanner-indent")) { + evaluator.setScannerType(Config.SCANNER_TYPE_INDENT); + } else if (cmd.equals(":scanner-brace")) { + evaluator.setScannerType(Config.SCANNER_TYPE_BRACE); + } else if (cmd.equals(":")) { + replSourceRec.delete(0, replSourceRec.length()); } else { - if (str.trim().isEmpty()) { - if (replSourceRec.length() != 0) { - // do repl - String stmt = replSourceRec.toString(); - try { - EvalEntry entry = evaluator.eval(stmt); - String name = entry.name; - Object o = entry.result; - if (name == null) { - showObjectStructure(o); + io.err.println("unknown command " + cmd + ", Type :help for more more information"); + sleep(10); + } + io.out.print("\n" + lineStarter); + } else { + if (str.trim().isEmpty()) { + if (replSourceRec.length() != 0) { + // do repl + String stmt = replSourceRec.toString(); + try { + EvalEntry entry = evaluator.eval(stmt); + String name = entry.name; + Object o = entry.result; + if (name == null) { + showObjectStructure(o); + } else { + io.out.println(name + " : " + entry.type.getName().replaceAll("\\.", "::") + " = " + o); + } + io.out.print("\n" + lineStarter); + } catch (Throwable t) { + if (t instanceof InvocationTargetException) { + t.getCause().printStackTrace(io.err); + } else if (t instanceof SyntaxException) { + int line = ((SyntaxException) t).lineCol.line - 1; + int col = ((SyntaxException) t).lineCol.column - 1; + + if (line < 0) { + io.err.println(t.getMessage()); } else { - System.out.println(name + " : " + entry.type.getName().replaceAll("\\.", "::") + " = " + o); - } - System.out.print("\n" + lineStarter); - } catch (Throwable t) { - if (t instanceof InvocationTargetException) { - t.getCause().printStackTrace(); - } else if (t instanceof SyntaxException) { - int line = ((SyntaxException) t).lineCol.line - 1; - int col = ((SyntaxException) t).lineCol.column - 1; - - if (line < 0) { - System.err.println(t.getMessage()); - } else { - String[] strs = stmt.split("\\n|\\r"); - String s = strs[line]; - System.err.println(s); - for (int i = 0; i < col; ++i) { - System.err.print(" "); - } - System.err.print("^ "); - System.err.println(t.getMessage()); + String[] strs = stmt.split("\\n|\\r"); + String s = strs[line]; + io.err.println(s); + for (int i = 0; i < col; ++i) { + io.err.print(" "); } - } else { - t.printStackTrace(); + io.err.print("^ "); + io.err.println(t.getMessage()); } - sleep(10); - System.out.print(lineStarter); + } else { + t.printStackTrace(io.err); } - replSourceRec.delete(0, replSourceRec.length()); - } else { - System.out.print(lineStarter); + sleep(10); + io.out.print(lineStarter); } + replSourceRec.delete(0, replSourceRec.length()); } else { - replSourceRec.append(str).append("\n"); - System.out.print(multipleLine); - } - } - - } - } - } - - /** - * run the program with arguments - * - * @param args arguments - */ - private static void runCommands(String[] args) throws Exception { - String command = args[0]; - if (command.equals("help") || command.equals("-help") || command.equals("--help") || command.equals("-h") || command.equals("/h") || command.equals("/help")) {// help - System.out.println("" + - "usage: -s [arguments [,...]]\n" + - " -c [-r] [-o ] [-cp ]\n" + - " ClassName [-cp ]\n" + - " -gb \n" + - " -repl\n" + - " -v | -version\n" + - " -e \n" + - "\n" + - "-s Specify the script location and run the script\n" + - "-c Specify the source file directory and compile *.lt files\n" + - "-r [option] Add sub directory files to compiling list.\n" + - "-o [option] Specify the output directory. (the source-directory/target/classes/ as default)\n" + - "-cp [option] The classpath. use ':' to separate the class-paths\n" + - "-repl Start the repl (or run the program with 0 arguments)\n" + - "-gb Generate build.lts and run.lts in the given directory\n" + - "-e Evaluate the given statement and print the result\n" + - "-version Show current version\n"); - - } else if (command.equals("-v") || command.equals("-version")) { - System.out.println("Latte-lang " + VersionRetriever.version()); - - } else if (command.equals("-s")) {// run scripts - // -s ? - if (args.length < 2) { - System.err.println("invalid command -s. the script file location should be specified\n" + - "see --help"); - return; - } - String path = args[1]; - File f = new File(path); - ScriptCompiler s = new ScriptCompiler(ClassLoader.getSystemClassLoader()); - try { - ScriptCompiler.Script script = s.compile(f); - String[] scriptArgs = new String[args.length - 2]; - System.arraycopy(args, 2, scriptArgs, 0, args.length - 2); - script.run(scriptArgs); - } catch (Throwable e) { - if (e instanceof SyntaxException) { - System.err.println("[ERROR] " + e.getMessage()); - } else { - e.printStackTrace(); - } - } - - } else if (command.equals("-c")) {// compile - // -c ? - if (args.length < 2) { - System.err.println("invalid command -c. the source directory should be specified\n" + - "see --help"); - return; - } - String sourceDir = args[1].trim(); - if (sourceDir.endsWith(File.separator)) { - sourceDir = sourceDir.substring(0, sourceDir.length() - File.separator.length()); - } - - boolean recursive = false; - String outputDir = sourceDir + File.separator + "target" + File.separator + "classes"; - List classPaths = new ArrayList(); - - for (int i = 2; i < args.length; ++i) { - String cmd = args[i]; - if (cmd.equals("-r")) { - recursive = true; - - } else if (cmd.equals("-o")) { - if (args.length - 1 == i) { - System.err.println("invalid option -o. the output directory should be specified"); - System.err.println("see --help"); - return; + io.out.print(lineStarter); } - outputDir = args[++i]; - - } else if (cmd.equals("-cp")) { - if (args.length - 1 == i) { - System.err.println("invalid option -cp. the class-path should be specified"); - System.err.println("see --help"); - return; - } - String[] class_paths = args[++i].split(":"); - for (String class_path : class_paths) { - try { - classPaths.add(new URL(new File(class_path).toURI().toString())); - } catch (MalformedURLException e) { - System.err.println("[ERROR] " + e.getMessage()); - return; - } - } - - } else { - System.err.println("unknown option " + cmd); - System.err.println("see --help"); - return; + replSourceRec.append(str).append("\n"); + io.out.print(multipleLine); } } - Compiler compiler = new Compiler(); - File outputDirFile = new File(outputDir); - if (!outputDirFile.exists()) //noinspection ResultOfMethodCallIgnored - outputDirFile.mkdirs(); - compiler.config.result.outputDir = outputDirFile; - compiler.config.classpath = classPaths; - - try { - compiler.compile(Utils.filesInDirectory(sourceDir, ".*\\.(lt|latte)", recursive)); - } catch (Exception e) { - if (e instanceof SyntaxException) { - System.err.println("[ERROR] " + e.getMessage()); - } else { - e.printStackTrace(); - } - } - - } else if (command.equals("-gb")) { - final List theFilesToBeGenerated = Arrays.asList("build.lts", "run.lts"); - - if (args.length != 2) { - System.err.println("invalid command -gb."); - System.err.println("see --help"); - return; - } - String projectDir = args[1].trim(); - if (projectDir.endsWith(File.separator)) { - projectDir = projectDir.substring(0, projectDir.length() - File.separator.length()); - } - - String core = String.valueOf(Runtime.getRuntime().availableProcessors()); - String separator = File.separator; - - for (String theFile : theFilesToBeGenerated) { - String filePath = projectDir + File.separator + theFile; - File file = new File(filePath); - if (file.exists()) { - System.out.println("[INFO] " + filePath + " exists"); - } else { - try { - //noinspection ResultOfMethodCallIgnored - file.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - try { - FileWriter fw = new FileWriter(file); - BufferedReader br = new BufferedReader(new InputStreamReader(REPL.class.getClassLoader().getResourceAsStream(theFile + ".template"))); - - String ss; - while ((ss = br.readLine()) != null) { - ss = ss.replace("${core}", core) - .replace("${dir}", projectDir.replace("\\", "\\\\")) - .replace("${separator}", separator) + "\n"; - fw.write(ss.toCharArray()); - } - fw.flush(); - fw.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - } else if (command.equals("-repl")) {// repl - main(new String[0]); - } else if (command.equals("-e")) {// eval - if (args.length == 1 || args[1] == null || args[1].trim().isEmpty()) { - System.err.println("missing statements to eval"); - return; - } - String statements = args[1].trim(); - Evaluator e = new Evaluator(new ClassPathLoader(Thread.currentThread().getContextClassLoader())); - EvalEntry entry = e.eval(statements); - System.out.println(entry.result); - - } else {// run - List urls = new ArrayList(); - try { - String url = new File("").toURI().toString(); - urls.add(new URL(url)); - } catch (MalformedURLException e) { - System.err.println("[ERROR] " + e.getMessage()); - return; - } - String[] runArgs = new String[0]; - for (int i = 1; i < args.length; ++i) { - String cmd = args[i]; - if (cmd.equals("-cp")) { - if (i == args.length - 1) { - System.err.println("invalid option -cp. the class-path should be specified"); - System.err.println("see --help"); - return; - } - String cps = args[++i]; - for (String cp : cps.split(":")) { - try { - urls.add(new URL(new File(cp).toURI().toString())); - } catch (MalformedURLException e) { - System.err.println("[ERROR] " + e.getMessage()); - return; - } - } - - } else if (cmd.equals("-args")) { - runArgs = new String[args.length - 1 - i]; - System.arraycopy(args, i + 1, runArgs, 0, runArgs.length); - break; - } else { - System.err.println("unknown option " + cmd); - System.err.println("see --help"); - return; - } - } - - try { - // run the class - Run run = new Run(urls, command); - run.exec(runArgs); - } catch (Throwable t) { - t.printStackTrace(); - } } + exitCallback.exit(); } - private static void showObjectStructure(Object o) throws IllegalAccessException { + private void showObjectStructure(Object o) throws IllegalAccessException { Class cls = o.getClass(); String className = cls.getName(); - System.out.println("class " + className); + io.out.println("class " + className); for (Field f : cls.getDeclaredFields()) { f.setAccessible(true); Object value = f.get(o); - System.out.println(" " + f.getName() + " : " + f.getType().getName().replaceAll("\\.", "::") + " = " + value); + io.out.println(" " + f.getName() + " : " + f.getType().getName().replaceAll("\\.", "::") + " = " + value); } for (Method m : cls.getDeclaredMethods()) { - System.out.print(" " + m.getName() + "("); + io.out.print(" " + m.getName() + "("); boolean isFirst = true; for (Class paramT : m.getParameterTypes()) { if (isFirst) { isFirst = false; } else { - System.out.print(", "); + io.out.print(", "); } - System.out.print(paramT); + io.out.print(paramT); } - System.out.println(") : " + m.getReturnType().getName().replaceAll("\\.", "::")); + io.out.println(") : " + m.getReturnType().getName().replaceAll("\\.", "::")); } } - private static void sleep(long millis) { + private void sleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { - e.printStackTrace(); + e.printStackTrace(io.err); } } - private static void handleCtrlC() { + public void handleCtrlC() { // listen ctrl-c - Class signalHandlerClass; - try { - signalHandlerClass = Class.forName("lt.repl.CtrlCSignalHandler"); - } catch (Throwable ignore) { - // Signal api not correct or found - return; - } - CtrlCHandler ctrlCHandler; - try { - ctrlCHandler = (CtrlCHandler) signalHandlerClass.newInstance(); - } catch (Throwable ignore) { - // unknown error - return; - } - ctrlCHandler.handle(); - ctrlCHandler.onAlert(new Runnable() { + CtrlCHandler ctrlCHandler = new CtrlCHandlerImpl(io); + ctrlCHandler.setExitCallback(exitCallback); + ctrlCHandler.setAlert(new Runnable() { @Override public void run() { - System.out.print(lineStarter); - replSourceRec = new StringBuilder(); + io.out.print(lineStarter); + replSourceRec.delete(0, replSourceRec.length()); } }); + reader.setCtrlCHandler(ctrlCHandler); } } diff --git a/latte-build/src/main/java/lt/repl/ScannerLineReader.java b/latte-build/src/main/java/lt/repl/ScannerLineReader.java deleted file mode 100644 index e059da7..0000000 --- a/latte-build/src/main/java/lt/repl/ScannerLineReader.java +++ /dev/null @@ -1,22 +0,0 @@ -package lt.repl; - -import java.util.Scanner; - -/** - * scanner line reader - */ -public class ScannerLineReader implements LineReader { - private Scanner scanner; - - public ScannerLineReader() { - System.out.println("using java.util.Scanner to read input"); - } - - @Override - public String readLine() throws Exception { - if (scanner == null) { - scanner = new Scanner(System.in); - } - return scanner.nextLine(); - } -} diff --git a/latte-build/src/main/java/lt/repl/SimpleStringReader.java b/latte-build/src/main/java/lt/repl/SimpleStringReader.java new file mode 100644 index 0000000..6655646 --- /dev/null +++ b/latte-build/src/main/java/lt/repl/SimpleStringReader.java @@ -0,0 +1,43 @@ +package lt.repl; + +import sun.misc.Signal; +import sun.misc.SignalHandler; + +/** + * scanner line reader + */ +public class SimpleStringReader implements StringReader { + private IO io; + + public SimpleStringReader() { + } + + @Override + public void setIO(IO io) { + this.io = io; + io.out.println("using java.util.Scanner to read input"); + } + + @Override + public void setCtrlCHandler(final CtrlCHandler ctrlCSignalHandler) { + SignalHandler handler = new SignalHandler() { + @Override + public void handle(Signal signal) { + if (!signal.getName().equals("INT")) { + return; + } + ctrlCSignalHandler.handle(); + } + }; + Signal.handle(new Signal("INT"), handler); + } + + @Override + public String read() throws Exception { + int b = io.in.read(); + if (b <= 0) { + return null; + } + return String.valueOf((char) b); + } +} diff --git a/latte-build/src/main/java/lt/repl/StringReader.java b/latte-build/src/main/java/lt/repl/StringReader.java new file mode 100644 index 0000000..d828696 --- /dev/null +++ b/latte-build/src/main/java/lt/repl/StringReader.java @@ -0,0 +1,12 @@ +package lt.repl; + +/** + * reads a line + */ +public interface StringReader { + void setIO(IO io); + + void setCtrlCHandler(CtrlCHandler handler); + + String read() throws Exception; +} diff --git a/latte-build/src/main/resources/version b/latte-build/src/main/resources/version index 5f6a810..dad139f 100644 --- a/latte-build/src/main/resources/version +++ b/latte-build/src/main/resources/version @@ -1,3 +1,4 @@ +0.0.11-ALPHA 0.0.10-ALPHA 0.0.9.1-ALPHA 0.0.9-ALPHA diff --git a/latte-compiler/src/main/java/lt/runtime/Dynamic.java b/latte-compiler/src/main/java/lt/runtime/Dynamic.java index 90491f8..146651f 100644 --- a/latte-compiler/src/main/java/lt/runtime/Dynamic.java +++ b/latte-compiler/src/main/java/lt/runtime/Dynamic.java @@ -684,7 +684,12 @@ private static T findBestMatch(List methodList, Object[] args, boolean[] type = ((Constructor) m).getParameterTypes()[i]; } if (primitives[i] && type.isPrimitive()) { - step[i] = getNumberPrimitiveCastDepth(args[i].getClass(), type); + if (args[i] instanceof Number || args[i] instanceof Character) { + step[i] = getNumberPrimitiveCastDepth(args[i].getClass(), type); + } else { + // for non number type (bool) + step[i] = 0; + } } else if (primitives[i]) { // param is not primitive step[i] = PRIMITIVE_BOX_CAST_BASE; // first cast to box type diff --git a/latte-compiler/src/test/java/lt/compiler/cases/TestCodeGen.java b/latte-compiler/src/test/java/lt/compiler/cases/TestCodeGen.java index ed38847..5969b02 100644 --- a/latte-compiler/src/test/java/lt/compiler/cases/TestCodeGen.java +++ b/latte-compiler/src/test/java/lt/compiler/cases/TestCodeGen.java @@ -4262,4 +4262,26 @@ public void testHigherKindType() throws Exception { assertEquals("X" + Consts.GENERIC_NAME_SPLIT + "int", p1.getName()); assertEquals("X" + Consts.GENERIC_NAME_SPLIT + "double", fmap.getReturnType().getName()); } + + @Test + public void testDynamicInvokeWithBool() throws Exception { + Class cls = retrieveClass("" + + "class TestDynamicInvokeWithBool\n" + + " def a(b:bool)=1\n" + + " def m(o) = o.a(true)" + , "TestDynamicInvokeWithBool"); + Object o = cls.newInstance(); + Method m = cls.getMethod("m", Object.class); + // it used to cause an exception + /* + * lt.compiler.LtBug: should not reach here, from: class java.lang.Boolean + * at lt.runtime.Dynamic.getNumberPrimitiveCastDepth(Dynamic.java:662) + * at lt.runtime.Dynamic.findBestMatch(Dynamic.java:687) + * at lt.runtime.Dynamic.findMethod(Dynamic.java:214) + * at lt.runtime.Dynamic.invoke(Dynamic.java:840) + * at lt.runtime.Dynamic.invoke(Dynamic.java:1093) + * at TestDynamicInvokeWithBool.m(test.lt:3) + */ + assertEquals(1, m.invoke(o, o)); + } }