/* | |
* $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()); | |
} | |
} | |
} |