jsr223 java_java – jsr223编写脚本解释器

我使用类似于Rhino的交互式shell的相当简单的方法实现了一些适用于Java SE 6 Rhino(Javascript)和Jython 1.5.2(Python)的东西(请参阅我在问题末尾的评论):

>保留尚未评估的待处理输入行列表.

>尝试编译(但不评估)待处理的输入行.

>如果编译正常,我们可以执行挂起的输入行.

>如果编译抛出异常,并且指示错误的位置(行列号),并且这与挂起输入的结束匹配,那么这就是我们期待更多输入的线索,所以吞下异常并等待下一行.

>否则,我们要么不知道错误在哪里,要么在挂起的输入结束之前发生,所以重新抛出异常.

>如果我们不期望任何更多的输入行,并且我们只有一行待定输入,那么评估它并重新启动.

>如果我们不期望任何更多的输入行,并且最后一个是空白的(按@ karakuricoder的回答)并且我们有多行未决输入,那么评估它并重新启动. Python的交互式shell似乎就是这样做的.

>否则,继续阅读输入行.

我不想发生的事情是:

>单线输入后,用户不得不输入额外的空行

>用户输入一个长多行语句,只有在第二行出现语法错误后才能找到.

这是我编写的一个帮助类,它实现了我的方法:

import java.lang.reflect.Method;

import javax.script.Bindings;

import javax.script.Compilable;

import javax.script.CompiledScript;

import javax.script.ScriptEngine;

import javax.script.ScriptException;

public class ScriptEngineInterpreter

{

private static final boolean DEBUG = false;

final private ScriptEngine engine;

final private Bindings bindings;

final private StringBuilder sb;

private int lineNumber;

private int pendingLineCount;

private boolean expectingMoreInput;

/**

* @param engine ScriptingEngine to use in this interpreter

* @param bindings Bindings to use in this interpreter

*/

public ScriptEngineInterpreter(ScriptEngine engine, Bindings bindings)

{

this.engine = engine;

this.bindings = bindings;

this.sb = new StringBuilder();

this.lineNumber = 0;

reset();

}

/** @return ScriptEngine used by this interpreter */

public ScriptEngine getEngine() { return this.engine; }

protected void reset() {

this.sb.setLength(0);

this.pendingLineCount = 0;

setExpectingMoreInput(false);

}

/** @return whether the interpreter is ready for a brand new statement. */

public boolean isReady() { return this.sb.length() == 0; }

/**

* @return whether the interpreter expects more input

*

* A true value means there is definitely more input needed.

* A false value means no more input is needed, but it may not yet

* be appropriate to evaluate all the pending lines.

* (there's some ambiguity depending on the language)

*/

public boolean isExpectingMoreInput() { return this.expectingMoreInput; }

protected void setExpectingMoreInput(boolean b) { this.expectingMoreInput = b; }

/**

* @return number of lines pending execution

*/

protected int getPendingLineCount() { return this.pendingLineCount; }

/**

* @param lineIsEmpty whether the last line is empty

* @return whether we should evaluate the pending input

* The default behavior is to evaluate if we only have one line of input,

* or if the user enters a blank line.

* This behavior should be overridden where appropriate.

*/

protected boolean shouldEvaluatePendingInput(boolean lineIsEmpty)

{

if (isExpectingMoreInput())

return false;

else

return (getPendingLineCount() == 1) || lineIsEmpty;

}

/**

* @param line line to interpret

* @return value of the line (or null if there is still pending input)

* @throws ScriptException in case of an exception

*/

public Object interpret(String line) throws ScriptException

{

++this.lineNumber;

if (line.isEmpty())

{

if (!shouldEvaluatePendingInput(true))

return null;

}

++this.pendingLineCount;

this.sb.append(line);

this.sb.append("\n");

CompiledScript cs = tryCompiling(this.sb.toString(), getPendingLineCount(), line.length());

if (cs == null)

{

return null;

}

else if (shouldEvaluatePendingInput(line.isEmpty()))

{

try

{

Object result = cs.eval(this.bindings);

return result;

}

finally

{

reset();

}

}

else

{

return null;

}

}

private CompiledScript tryCompiling(String string, int lineCount, int lastLineLength)

throws ScriptException

{

CompiledScript result = null;

try

{

Compilable c = (Compilable)this.engine;

result = c.compile(string);

}

catch (ScriptException se) {

boolean rethrow = true;

if (se.getCause() != null)

{

Integer col = columnNumber(se);

Integer line = lineNumber(se);

/* swallow the exception if it occurs at the last character

* of the input (we may need to wait for more lines)

*/

if (col != null

&& line != null

&& line.intValue() == lineCount

&& col.intValue() == lastLineLength)

{

rethrow = false;

}

else if (DEBUG)

{

String msg = se.getCause().getMessage();

System.err.println("L"+line+" C"+col+"("+lineCount+","+lastLineLength+"): "+msg);

System.err.println("in '"+string+"'");

}

}

if (rethrow)

{

reset();

throw se;

}

}

setExpectingMoreInput(result == null);

return result;

}

private Integer columnNumber(ScriptException se)

{

if (se.getColumnNumber() >= 0)

return se.getColumnNumber();

return callMethod(se.getCause(), "columnNumber", Integer.class);

}

private Integer lineNumber(ScriptException se)

{

if (se.getLineNumber() >= 0)

return se.getLineNumber();

return callMethod(se.getCause(), "lineNumber", Integer.class);

}

static private Method getMethod(Object object, String methodName)

{

try {

return object.getClass().getMethod(methodName);

}

catch (NoSuchMethodException e) {

return null;

/* gulp */

}

}

static private T callMethod(Object object, String methodName, Class cl) {

try {

Method m = getMethod(object, methodName);

if (m != null)

{

Object result = m.invoke(object);

return cl.cast(result);

}

}

catch (Exception e) {

e.printStackTrace();

}

return null;

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值