2011-04-07 11 views
5

ठीक है। ScriptEngine.eval(String string) अपनी स्ट्रिंग में एक स्ट्रिंग का मूल्यांकन करता है, और ScriptEngine.eval(Reader reader)Reader से पूरी तरह से इनपुट का मूल्यांकन करता है।jsr223 + एक स्क्रिप्ट दुभाषिया लिखना

तो अगर मेरे पास फ़ाइल है, तो मैं एक फ़ाइल इनपुटप्रीम खोल सकता हूं, इसके चारों ओर एक पाठक लपेट सकता हूं, और scriptEngine.eval(reader) पर कॉल कर सकता हूं।

यदि मेरे पास स्ट्रिंग के रूप में पूर्ण विवरण है, तो मैं scriptEngine.eval(string) पर कॉल कर सकता हूं।

यदि मुझे एक इंटरैक्टिव दुभाषिया को लागू करने की आवश्यकता है तो मैं क्या करूँ? मेरे पास एक ऐसा उपयोगकर्ता है जो एक बहुभाषी कथन में इंटरैक्टिव टाइपिंग कर रहा है, उदा।

function f() { 
    return 3; 
} 

अगर मैं लाइन द्वारा इनपुट लाइन पढ़ा है, और eval() की स्ट्रिंग फार्म का उपयोग करें, मैं इसे अधूरा बयान करता है, उदा गुजर पहुंच जाएंगे function f() { और एक त्रुटि प्राप्त करें।

यदि मैं एक पाठक में पास करता हूं, तो ScriptEngine इनपुट पूर्ण होने तक हमेशा के लिए प्रतीक्षा करेगा, और यह इंटरैक्टिव नहीं है।

सहायता!


बस स्पष्ट करने के लिए: समस्या यहाँ है कि मैं केवल ScriptEngine.eval() पूरा बयान से पारित सकता है, और ScriptEngine के ग्राहक के रूप में, मैं नहीं जानता कि एक इनपुट लाइन ScriptEngine से ही कुछ मदद के बिना पूरा हो गया है जब।


राइनो के इंटरैक्टिव खोल का उपयोग करता राइनो के Context.stringIsCompilableUnit() (usage और implementation के लिए LXR देखें)।

उत्तर

4

मैंने कुछ ऐसा लागू किया जो जावा एसई 6 राइनो (जावास्क्रिप्ट) और ज्योथन 1.5.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; 
    } 

} 
2

कुंजीपटल (स्कैनर क्लास) से पढ़ता है और इनपुट की कई पंक्तियों से एक पूर्ण स्ट्रिंग बनाता है। रिक्त रेखा पर प्रवेश करें उपयोगकर्ता इनपुट के अंत संकेत। स्ट्रिंग को eval विधि में पास करें।

+0

... और कैसे है कि मदद करता है? मुझे स्क्रिप्टिंगइंजिन से सहायता के बिना अपूर्ण कथन और पूर्ण बयान के बीच अंतर कैसे करना चाहिए? –

+0

"रिक्त रेखा पर प्रवेश करें उपयोगकर्ता इनपुट के अंत संकेत" - आईसीके - यह काम करेगा, लेकिन यह कष्टप्रद है। –

+0

किसी भी स्ट्रीम से आने वाले अधूरे कथन को रोकने के लिए कुछ भी नहीं है, चाहे वह फ़ाइल, नेटवर्क कनेक्शन या कीबोर्ड हो। आपको कुछ पूर्व-सत्यापन करना होगा या eval impl को कथन को संभालने/सत्यापित करने के लिए आवश्यक कार्रवाई करना होगा, पूरा नहीं हुआ है। दूसरा, प्रत्येक स्ट्रीम में एक अंत मार्कर होना चाहिए। गैर-कीबोर्ड स्ट्रीम से पढ़ना कुछ ध्वज मूल्य जैसे ईओएफ, -1, नल इत्यादि देता है। आप इनपुट के अंत के लिए जो भी साधन चाहते हैं उसे चुन सकते हैं लेकिन उपयोगकर्ता को निर्धारित करने के लिए एक प्रोग्रामेटिक तरीका होना चाहिए। – karakuricoder

संबंधित मुद्दे