/* --------------------------------------------------------- *
* __________ 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();
}
}