/* --------------------------------------------------------- * * __________ D E L T A S C R I P T * * (_________() * * / === / - A fast, dynamic scripting language * * | == | - Version 4.13.11.0 * * / === / - Developed by Adam R. Nelson * * | = = | - 2011-2013 * * / === / - Distributed under GNU LGPL v3 * * (________() - http://github.com/ar-nelson/deltascript * * * * --------------------------------------------------------- */ package com.sector91.delta.script; import java.io.*; import java.nio.charset.Charset; import java.util.Scanner; import com.sector91.delta.script.objects.DS_Blank; import com.sector91.delta.script.objects.DS_Object; import com.sector91.delta.script.objects.DS_Scope; import com.sector91.delta.script.objects.DS_String; import com.sector91.delta.script.objects.DS_Tag; import com.sector91.delta.script.parser.DScriptLexer; import com.sector91.delta.script.parser.DScriptParserException; import com.sector91.util.NestedPrintable; import com.sector91.util.StringUtil; /** * <p>An interactive command-line DeltaScript interpreter. Presents a prompt * to an arbitrary {@link OutputStream}, and can receive user input from an * arbitrary {@link InputStream}, which will be parsed and executed as * DeltaScript.</p> * * @author Adam R. Nelson * @version 4.13.11.0 */ public class DScriptInterpreter implements Runnable { private static final boolean UNICODE = Charset.defaultCharset().contains( Charset.forName("UTF-8")); private static final String UNICODE_PROMPT = "\u03b4 > ", UNICODE_BLOCK_PROMPT = "..> ", UNICODE_RESULT_PROMPT = "[o] ", ASCII_PROMPT = "DS> ", ASCII_BLOCK_PROMPT = "..> ", ASCII_RESULT_PROMPT = "[o] ", PROMPT = UNICODE ? UNICODE_PROMPT : ASCII_PROMPT, BLOCK_PROMPT = UNICODE ? UNICODE_BLOCK_PROMPT : ASCII_BLOCK_PROMPT, RESULT_PROMPT = UNICODE ? UNICODE_RESULT_PROMPT : ASCII_RESULT_PROMPT; private PrintStream out; private InputStream in; private DScriptContext context; private DS_Scope scope; private boolean running, debug; /** * <p>Creates an interpreter that uses the given input and output streams, * and a new scope.</p> * * @param output The stream that the interpreter prints output to. * @param input The stream from which the interpreter receives user input. */ public DScriptInterpreter(OutputStream output, InputStream input) {this(output, input, new DScriptContext());} /** * <p>Creates an interpreter that uses the given input and output streams, * and the given scope.</p> * * @param output The stream that the interpreter prints output to. * @param input The stream from which the interpreter receives user input. * @param scope The scope that the interpreter will execute scripts in. */ public DScriptInterpreter(OutputStream output, InputStream input, DScriptContext context) { out = new PrintStream(output); in = input; this.context = context; this.scope = context.createScope(); } /** * <p>Creates an interpreter that uses the given input and output streams, * and the given scope.</p> * * @param output The stream that the interpreter prints output to. * @param input The stream from which the interpreter receives user input. * @param scope The scope that the interpreter will execute scripts in. */ public DScriptInterpreter(OutputStream output, InputStream input, DS_Scope scope) { out = new PrintStream(output); in = input; this.context = scope.getContext(); this.scope = scope; } public void run() { out.println( DeltaScript.NAME + ", version " + DeltaScript.VERSION + "\n" + DeltaScript.AUTHOR + "\n" + "For more information, visit " + DeltaScript.WEBSITE + "\n" + "------------------------------------------------------------"); String text = ""; running = true; boolean continued = false; while (running) { out.print(continued?BLOCK_PROMPT:PROMPT); Scanner sc = new Scanner(in); sc.useDelimiter("\n"); String input = sc.next(); text += input + "\n"; if ("quit".equalsIgnoreCase(input.trim()) || "exit".equalsIgnoreCase(input.trim())) { stop(); break; } try { final DScriptLexer lexer = new DScriptLexer(text); // Scan for unclosed tags. DS_Object result = DeltaScript.eval(text, "Interpreter Script", scope, context); if (result != DS_Blank.BLANK && result != null) { out.print(RESULT_PROMPT); if (result instanceof DS_String) out.println("\"" + result.toString() + "\""); else if (result instanceof NestedPrintable) out.println(((NestedPrintable)result).toStringNested(2)); else out.println(result.toString()); } continued = false; } catch (DScriptParserException ex) { //if (ex.isUnclosed()) // continued = true; //else //{ out.println(getConsoleMessage(ex, text)); if (debug) ex.printStackTrace(out); continued = false; //} } catch (DScriptErr ex) { out.println(getConsoleMessage(ex, text)); if (debug) ex.printStackTrace(out); continued = false; } if (!continued) text = ""; } } private String getConsoleMessage(DScriptErr err, String scriptText) { StringBuilder consoleOutput = new StringBuilder(); for (int i=0; i<60; i++) consoleOutput.append('-'); consoleOutput.append("\n!! DeltaScript Error !!"); consoleOutput.append(err.getLocatorText("Interpreter Script", scriptText)); StringBuilder tagsLine = new StringBuilder("Tags: "); if (err.tags.size() > 0) { for (DS_Tag tag : err.tags) { tagsLine.append(tag); tagsLine.append(", "); } } else tagsLine.append("(None) "); tagsLine.delete(tagsLine.length()-2, tagsLine.length()); consoleOutput.append("\n"); consoleOutput.append(StringUtil.wrap(tagsLine.toString(), 60)); consoleOutput.append("\n\n"); consoleOutput.append(StringUtil.wrap(err.getMessage()==null? "Null Pointer" : err.getMessage(), 60)); consoleOutput.append("\n"); for (int i=0; i<60; i++) consoleOutput.append('-'); return consoleOutput.toString(); } private String getConsoleMessage(DScriptParserException err, String scriptText) { StringBuilder consoleOutput = new StringBuilder(); for (int i=0; i<60; i++) consoleOutput.append('-'); consoleOutput.append("\n!! Parsing Error !!"); consoleOutput.append(err.getLocatorText(scriptText)); consoleOutput.append("\n\n"); consoleOutput.append(StringUtil.wrap(err.getMessage()==null? "Null Pointer" : err.getMessage(), 60)); consoleOutput.append("\n"); for (int i=0; i<60; i++) consoleOutput.append('-'); return consoleOutput.toString(); } /** * <p>Gets the scope that the interpreter is currently using.</p> * * @return The scope that the interpreter is currently using. */ public DS_Scope getScope() {return scope;} /** * <p>Adds a Java object to the interpreter's scope. The object will be * boxed using {@link DeltaScript#box(Object)}.</p> * * @param key The name that the Java object will be referred to by in * scripts. By convention, this should start with a capital * letter. * @param obj The Java object to box and add to this interpreter's scope. */ public void putJavaObject(String key, Object obj) throws DScriptErr {scope.setJavaObject(DS_Tag.tag(key), obj);} /** * <p>Stops the interpreter's thread.</p> */ public void stop() {running = false;} /** * <p>Sets whether or not error messages should show full stack traces.</p> */ public void setDebugMode(boolean debug) {this.debug = debug;} /** * <p>Runs the interpreter as a standalone application.</p> * * @param args Command-line arguments; ignored. */ public static void main(String[] args) { DScriptInterpreter interpreter = new DScriptInterpreter(System.out, System.in); interpreter.run(); } }