gpt4 book ai didi

java - jsr223 + 编写脚本解释器

转载 作者:搜寻专家 更新时间:2023-10-31 19:58:18 25 4
gpt4 key购买 nike

好的。 ScriptEngine.eval(String string)评估整个字符串,ScriptEngine.eval(Reader reader)完整地评估来自 Reader 的输入。

因此,如果我有一个文件,我可以打开一个 FileInputStream,将一个 Reader 包裹在它周围,然后调用 scriptEngine.eval(reader)

如果我有一个完整的字符串形式的语句,我可以调用 scriptEngine.eval(string)

如果我需要实现交互式解释器,我该怎么办?我有一个用户正在交互式地输入多行语句,例如

 function f() {
return 3;
}

如果我逐行读取输入,并使用 eval() 的字符串形式,我将最终传递给它不完整的语句,例如function f() { 并得到一个错误。

如果我传入一个 Reader,ScriptEngine 将永远等待输入完成,并且它不是交互式的。

帮助!


澄清一下:这里的问题是我只能通过 ScriptEngine.eval() 完整的语句,作为 ScriptEngine 的客户,我不知道没有输入行何时完成来自 ScriptEngine 本身的一些帮助。


Rhino 的交互式 shell 使用 Rhino 的 Context.stringIsCompilableUnit() (参见 LXR 的 usageimplementation)。

最佳答案

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

  • 保留尚未评估的输入行的待定列表。
  • 尝试编译(但不评估)待处理的输入行。
    • 如果编译正常,我们可能能够执行挂起的输入行。
    • 如果编译抛出异常,并且有错误位置(行 + 列号)的指示,并且这与待处理输入的末尾相匹配,那么这就是我们期待更多输入的线索,所以吞下异常并等待下一行。
    • 否则,我们要么不知道错误在哪里,要么发生在待处理输入结束之前,因此重新抛出异常。
  • 如果我们不需要更多的输入行,并且只有一行待处理的输入,则评估它并重新启动。
  • 如果我们不期望更多的输入行,并且最后一行是空白的(根据@karakuricoder 的回答)并且我们有不止一行的待处理输入,则评估它并重新启动。 Python 的交互式 shell 似乎可以做到这一点。
  • 否则,继续阅读输入行。

想要发生的是:

  • 用户厌烦在单行输入后必须输入额外的空行
  • 用户输入一个长的多行语句,只有在第 2 行出现语法错误后才发现。

这是我编写的实现我的方法的辅助类:

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> T callMethod(Object object, String methodName, Class<T> 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;
}

}

关于java - jsr223 + 编写脚本解释器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5584674/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com