Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java jsr223&x2B;编写脚本解释器_Java_Interpreter_Jsr223 - Fatal编程技术网

Java jsr223&x2B;编写脚本解释器

Java jsr223&x2B;编写脚本解释器,java,interpreter,jsr223,Java,Interpreter,Jsr223,嗯。对字符串进行整体求值,并对读取器的输入进行整体求值 因此,如果我有一个文件,我可以打开一个FileInputStream,在它周围包装一个读卡器,然后调用scriptEngine.eval(Reader) 如果我有一个完整的语句作为字符串,我可以调用scriptEngine.eval(string) 如果需要实现交互式解释器,该怎么办?我有一个用户正在以交互方式键入多行语句,例如 function f() { return 3; } 如果我逐行读取输入,并使用字符串形式eva

嗯。对字符串进行整体求值,并对
读取器的输入进行整体求值

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

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

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

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

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

救命啊


只是想澄清一下:这里的问题是,我只能传递
ScriptEngine.eval()
complete语句,作为ScriptEngine的客户,如果没有ScriptEngine本身的帮助,我不知道输入行何时完成



Rhino的交互式shell使用Rhino(请参见LXR了解和)。

创建一个从键盘读取的方法(Scanner类)并从多行输入创建一个完整的字符串。在空行上输入表示用户输入结束。将字符串传递到eval方法。

我实现了一个可以在Java SE 6 Rhino上正常工作的方法(Javascript)和Jython 1.5.2(Python),使用一种相当简单的方法,类似于Rhino的交互式shell(请参见我在问题末尾的评论):

  • 保留尚未计算的输入行的挂起列表
  • 尝试编译(但不计算)挂起的输入行。
    • 如果编译正常,我们可能可以执行挂起的输入行
    • 如果编译抛出异常,并且有错误位置(行+列号)的指示,并且这与挂起的输入的结尾相匹配,那么这就是我们期望更多输入的线索,所以吞下异常并等待下一行
    • 否则,我们要么不知道错误在哪里,要么它发生在挂起的输入结束之前,所以重新抛出异常
  • 如果我们不希望有更多的输入行,并且只有一行挂起的输入,那么请评估它并重新启动
  • 如果我们不希望有更多的输入行,而最后一行是空白的(根据@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> 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;
    }

}
import java.lang.reflect.Method;
导入javax.script.Bindings;
导入javax.script.compileable;
导入javax.script.CompiledScript;
导入javax.script.ScriptEngine;
导入javax.script.ScriptException;
公共类ScriptEngineInterpreter
{
私有静态最终布尔调试=false;
最终私有脚本引擎;
最终私有绑定;
最后一次私人谈判;
专用整数行号;
私有int pendingLineCount;
私有布尔期望更多输入;
/**
*@param engine ScriptingEngine用于此解释器
*@param bindings要在此解释器中使用的绑定
*/
公共ScriptEngineInterpreter(ScriptEngine引擎、绑定)
{ 
这个。发动机=发动机;
this.bindings=绑定;
this.sb=新的StringBuilder();
这个.lineNumber=0;
重置();
}       
/**@return此解释器使用的脚本引擎*/
public ScriptEngine getEngine(){返回this.engine;}
受保护的无效重置({
此.sb.设定长度(0);
此参数为.pendingLineCount=0;
设置ExpectingMoreInput(false);
}
/**@return口译员是否准备好接受全新的陈述*/
公共布尔值isReady(){返回此.sb.length()==0;}
/**
*@return解释器是否需要更多输入
* 
*真值意味着需要更多的输入。
*假值表示不需要更多输入,但可能还不需要
*适当地评估所有挂起的行。
*(根据语言的不同,有些歧义)
*/
公共布尔值isExpectingMoreInput(){返回this.expectingMoreInput;}
受保护的void setExpectingMoreInput(布尔b){this.expectingMoreInput=b;}
/**
*@返回挂起执行的行数
*/
受保护的int getPendingLineCount(){返回this.pendingLineCount;}
/**
*@param lineIsEmpty最后一行是否为空
*@return我们是否应该评估挂起的输入
*默认行为是在只有一行输入时进行计算,
*或者如果用户输入一个空行。
*在适当的情况下,应覆盖此行为。
*/
受保护的布尔值应评估输入(布尔线为空)
{
如果(isExpectingMoreInput())
返回false;
其他的
return(getPendingLineCount()==1)| | lineIsEmpty;
} 
/**
*@要解释的参数行
*@行的返回值(如果仍有挂起的输入,则为null)
*@在出现异常时抛出ScriptException
*/
公共对象解释(字符串行)引发脚本异常
{
++这是一个行号;
if(line.isEmpty())
{
如果(!shouldEvaluatePendingInput(true))
返回null;
}
++这是pendingLineCount;
这是某人附加的(行);
T