/* * $Id: LuaConsole.java 79 2012-01-08 11:08:32Z andre@naef.com $ * See LICENSE.txt for license terms. */ package com.naef.jnlua.console; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import com.naef.jnlua.LuaException; import com.naef.jnlua.LuaRuntimeException; import com.naef.jnlua.LuaState; /** * A simple Lua console. * * <p> * The console collects input until a line with the sole content of the word * <i>go</i> is encountered. At that point, the collected input is run as a Lua * chunk. If the Lua chunk loads and runs successfully, the console displays the * returned values of the chunk as well as the execution time based on a * <code>System.nanoTime()</code> measurement. Otherwise, the console shows the * error that has occurred. * </p> * * <p> * Expressions can be printed by prepending <i>=</i> to the expression at the * beginning of a chunk. The console translates <i>=</i> into * <code>return</code> followed by a space and executes the chunk immediately. * No separate <i>go</i> is required. Therefore, expressions printed this way * must be entered on a single line. * </p> */ public class LuaConsole { // -- Static private static final String[] EMPTY_ARGS = new String[0]; /** * Main routine. * * @param args * the command line arguments */ public static void main(String[] args) { LuaConsole luaConsole = new LuaConsole(args); luaConsole.run(); System.exit(0); } // -- State private LuaState luaState; // -- Construction /** * Creates a new instance. */ public LuaConsole() { this(EMPTY_ARGS); } /** * Creates a new instance with the specified command line arguments. The * arguments are passed to Lua as the <code>argv</code> global variable. * * @param args */ public LuaConsole(String[] args) { luaState = new LuaState(); // Process arguments luaState.newTable(args.length, 0); for (int i = 0; i < args.length; i++) { luaState.pushString(args[i]); luaState.rawSet(-2, i + 1); } luaState.setGlobal("argv"); // Open standard libraries luaState.openLibs(); // Set buffer mode luaState.load("io.stdout:setvbuf(\"no\")", "=consoleInitStdout"); luaState.call(0, 0); luaState.load("io.stderr:setvbuf(\"no\")", "=consoleInitStderr"); luaState.call(0, 0); } // -- Properties /** * Returns the Lua state of this console. * * @return the Lua state */ public LuaState getLuaState() { return luaState; } // -- Operations /** * Runs the console. */ public void run() { // Banner System.out.println(String.format("JNLua %s Console using Lua %s.", LuaState.VERSION, LuaState.LUA_VERSION)); System.out.print("Type 'go' on an empty line to evaluate a chunk. "); System.out.println("Type =<expression> to print an expression."); // Prepare reader BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(System.in)); try { // Process chunks chunk: while (true) { ByteArrayOutputStream out = new ByteArrayOutputStream(); OutputStreamWriter outWriter = new OutputStreamWriter(out, "UTF-8"); boolean firstLine = true; // Process lines while (true) { String line = bufferedReader.readLine(); if (line == null) { break chunk; } if (line.equals("go")) { outWriter.flush(); InputStream in = new ByteArrayInputStream(out .toByteArray()); runChunk(in); continue chunk; } if (firstLine && line.startsWith("=")) { outWriter.write("return " + line.substring(1)); outWriter.flush(); InputStream in = new ByteArrayInputStream(out .toByteArray()); runChunk(in); continue chunk; } outWriter.write(line); outWriter.write('\n'); firstLine = false; } } } catch (IOException e) { System.out.print("IO error: "); System.out.print(e.getMessage()); System.out.println(); } } /** * Runs a chunk of Lua code from an input stream. */ protected void runChunk(InputStream in) throws IOException { try { long start = System.nanoTime(); luaState.setTop(0); luaState.load(in, "=console", "t"); luaState.call(0, LuaState.MULTRET); long stop = System.nanoTime(); for (int i = 1; i <= luaState.getTop(); i++) { if (i > 1) { System.out.print(", "); } switch (luaState.type(i)) { case BOOLEAN: System.out.print(Boolean.valueOf(luaState.toBoolean(i))); break; case NUMBER: case STRING: System.out.print(luaState.toString(i)); break; default: System.out.print(luaState.typeName(i)); } } System.out.print("\t#msec="); System.out.print(String.format("%.3f", (stop - start) / 1000000.0)); System.out.println(); } catch (LuaRuntimeException e) { e.printLuaStackTrace(); } catch (LuaException e) { System.err.println(e.getMessage()); } } }