singrdk/base/Applications/Shell/ScriptEngine.cs

727 lines
27 KiB
C#

///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
///////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections;
using System.IO;
using System.Text.RegularExpressions;
using System.Text;
using Microsoft.Singularity;
using Microsoft.Singularity.Io;
using Microsoft.Contracts;
namespace Microsoft.Singularity.Applications
{
/**
* Provides a reasonable script engine implementation.
* This engine is composed of two distinct parts: the parser
* and the interpreter. Parser.cs and ScriptParser.cs provide
* the parsing functionality. Parser.cs is the general handwritten slr
* driver while ScriptParser contains the automatically generated
* parse tables and semantic action functions for productions
* and lex operations of this specific scripting language. This file
* provides the interpretation mechanism that runs over an ast produced
* by the parser.
**/
class ScriptEngine
{
private String script;
private Block parsedScript;
public const String LAST_COMMAND_EXIT_SYMBOL = "?";
public const String NUM_ARGUMENTS_SYM = "#";
public delegate int CommandLineRunner(ShellControl! shellControl,
String[] commandLine,
bool isBackground);
private CommandLineRunner runner;
public static int Run(String script, ShellControl! shellControl, CommandLineRunner runner)
{
return Run(script, shellControl, runner, null);
}
public static int Run(String script, ShellControl! shellControl,
CommandLineRunner runner, String[] arguments)
{
ScriptEngine engine = new ScriptEngine(script, runner);
try {
engine.Parse();
} catch (Parser.ParseException e) {
Console.WriteLine(e.Message);
return -1;
} catch (Exception e) {
Console.WriteLine(e.Message);
return -1;
}
try {
return engine.Run(shellControl, arguments);
} catch (Interpretation.InterpretationException e) {
Console.WriteLine(e.Message);
} catch (ScriptException e) {
Console.WriteLine(e.Message);
} catch (Exception e) {
Console.WriteLine(e.Message);
}
return -1;
}
private ScriptEngine(String script, CommandLineRunner runner)
{
this.script = script;
this.runner = runner;
}
private void Parse()
{
ScriptParser parser = new ScriptParser();
parsedScript = (Block)parser.Parse(script);
}
private int Run(ShellControl! shellControl, String[] arguments)
{
Interpretation interp = new Interpretation(runner, arguments);
parsedScript.Interpret(shellControl, interp);
return interp.ExitCode;
}
public class ScriptException : Exception
{
public ScriptException(String message) : base(message) { }
}
public class Interpretation
{
public static readonly object! undefinedObj = new Object();
private Hashtable! symbolTable;
private bool exit = false;
[NotDelayed]
public Interpretation(CommandLineRunner runner, String[] arguments)
{
this.runner = runner;
symbolTable = new Hashtable();
base();
int i = 0;
if (arguments != null) {
for (; i < arguments.Length; ++i) {
SetSymbol(i.ToString(), arguments[i]);
}
}
SetSymbol(NUM_ARGUMENTS_SYM, i);
}
public int ExitCode
{
get {
Object exitCode = LookupSymbol(LAST_COMMAND_EXIT_SYMBOL);
if (exitCode == null) { return 0; }
return (Int32) exitCode;
}
set { SetSymbol(LAST_COMMAND_EXIT_SYMBOL, value); }
}
public bool Exit
{
get { return exit; }
set { exit = value; }
}
public CommandLineRunner runner;
public object! LookupSymbol(string! symbol)
{
Object value = symbolTable[symbol];
if (value == null) {
return undefinedObj;
}
return value;
}
public void SetSymbol(string! key, Object value)
{
symbolTable[key] = value;
}
public class InterpretationException : Exception
{
public InterpretationException(String message) : base(message) { }
}
public class UndefinedVariableException : InterpretationException
{
public UndefinedVariableException(String variable)
: base("Use of undefined variable: " + variable) { }
}
public class TypeConversionException : InterpretationException
{
public TypeConversionException(String value, String type)
: base("Could not convert " + value + " to type: " + type) { }
}
public class UnknownTypeException : InterpretationException
{
public UnknownTypeException(String type)
: base("Unknown type" + type) { }
}
public class DivideByZeroException : InterpretationException
{
public DivideByZeroException() : base("Division by zero undefined.") { }
}
}
public abstract class Element { }
public abstract class Expression : Element
{
public abstract object! GetValue(Interpretation! interp);
public abstract string! StringValue(Interpretation! interp);
public abstract int IntValue(Interpretation! interp);
public abstract bool BoolValue(Interpretation! interp);
public bool IsIntLike() { return true; }
public bool IsBoolLike() { return true; }
public bool IsStringLike() { return true; }
}
public class VariableReference : Expression
{
String variable;
public VariableReference(String variable)
{
this.variable = variable;
}
public override object! GetValue(Interpretation! interp)
{
object value = interp.LookupSymbol(variable);
if (value == Interpretation.undefinedObj)
throw new Interpretation.UndefinedVariableException(variable);
return value;
}
public override string! StringValue(Interpretation! interp)
{
object value = GetValue(interp);
return value.ToString();
}
public override int IntValue(Interpretation! interp)
{
Object value = GetValue(interp);
if (value is String) {
return Int32.Parse((String)value);
} else if (value is Int32) {
return (int)value;
} else if (value is bool) {
return ((bool)value) ? 1 : 0;
}
throw new Interpretation.UnknownTypeException(value.GetType().ToString());
}
public override bool BoolValue(Interpretation! interp)
{
Object value = GetValue(interp);
if (value is String) {
try {
return Boolean.Parse((String)value);
} catch (FormatException) {
throw new Interpretation.TypeConversionException((String)value, "bool");
}
} else if (value is Int32) {
return ((int)value) == 0 ? true : false;
} else if (value is Boolean) {
return (bool)value;
}
throw new Interpretation.UnknownTypeException(value.GetType().ToString());
}
}
public abstract class IntegerExpression : Expression
{
public override object! GetValue(Interpretation! interp) { return IntValue(interp); }
public override string! StringValue(Interpretation! interp)
{
int value = IntValue(interp);
return value.ToString();
}
public override bool BoolValue(Interpretation! interp)
{
int value = IntValue(interp);
return value == 0 ? true : false;
}
}
public class IntegerLiteral : IntegerExpression
{
private int value;
public IntegerLiteral(int value)
{
this.value = value;
}
public override int IntValue(Interpretation! interp) { return value; }
}
public abstract class IntegerBinaryExpression : IntegerExpression
{
public Expression left;
public Expression right;
protected IntegerBinaryExpression(Expression left, Expression right)
{
this.left = left; this.right = right;
}
}
public class Add : IntegerBinaryExpression
{
public Add(Expression left, Expression right) : base(left, right) { }
public override int IntValue(Interpretation! interp)
{
return left.IntValue(interp) + right.IntValue(interp);
}
}
public class Subtract : IntegerBinaryExpression
{
public Subtract(Expression left, Expression right) : base(left, right) { }
public override int IntValue(Interpretation! interp)
{
return left.IntValue(interp) - right.IntValue(interp);
}
}
public class Multiply : IntegerBinaryExpression
{
public Multiply(Expression left, Expression right) : base(left, right) { }
public override int IntValue(Interpretation! interp)
{
return left.IntValue(interp) * right.IntValue(interp);
}
}
public class Divide : IntegerBinaryExpression
{
public Divide(Expression left, Expression right) : base(left, right) { }
public override int IntValue(Interpretation! interp)
{
int leftVal = left.IntValue(interp);
int rightVal = right.IntValue(interp);
if (rightVal == 0) {
throw new DivideByZeroException();
}
return leftVal / rightVal;
}
}
public class Mod : IntegerBinaryExpression
{
public Mod(Expression left, Expression right) : base(left, right) { }
public override int IntValue(Interpretation! interp)
{
return left.IntValue(interp) % right.IntValue(interp);
}
}
public abstract class BooleanExpression : Expression
{
public override object! GetValue(Interpretation! interp) { return BoolValue(interp); }
public override int IntValue(Interpretation! interp)
{
bool value = BoolValue(interp);
return value ? 1 : 0;
}
public override string! StringValue(Interpretation! interp)
{
bool value = BoolValue(interp);
return value.ToString();
}
}
public class BooleanLiteral : BooleanExpression
{
bool value;
public BooleanLiteral(bool value)
{
this.value = value;
}
public override bool BoolValue(Interpretation! interp) { return value; }
}
public abstract class BooleanBinaryExpression : BooleanExpression
{
protected Expression left;
protected Expression right;
public BooleanBinaryExpression(Expression left, Expression right)
{
this.left = left; this.right = right;
}
protected int CompareOperands(Interpretation! interp)
{
return CompareTo(left.GetValue(interp), right, interp);
}
private static int CompareTo(Object left, Expression e, Interpretation! interp)
{
if (left == null) {
if (e == null) { return 0; }
return -1;
}
if (e == null) { return 1; }
if (left is String) {
return ((String)left).CompareTo(e.StringValue(interp));
} else if (left is int) {
return ((int)left).CompareTo(e.IntValue(interp));
} else if (left is Boolean) {
return ((Boolean)left).CompareTo(e.BoolValue(interp));
}
throw new Interpretation.UnknownTypeException("unsupported type");
}
}
public class And : BooleanBinaryExpression
{
public And(Expression left, Expression right) : base(left, right) { }
public override bool BoolValue(Interpretation! interp)
{
return left.BoolValue(interp) && right.BoolValue(interp);
}
}
public class Or : BooleanBinaryExpression
{
public Or(Expression left, Expression right) : base(left, right) { }
public override bool BoolValue(Interpretation! interp)
{
return left.BoolValue(interp) || right.BoolValue(interp);
}
}
public class Less : BooleanBinaryExpression
{
public Less(Expression left, Expression right) : base(left, right) { }
public override bool BoolValue(Interpretation! interp)
{
int compare = CompareOperands(interp);
return compare < 0;
}
}
public class LessEqual : BooleanBinaryExpression
{
public LessEqual(Expression left, Expression right) : base(left, right) { }
public override bool BoolValue(Interpretation! interp)
{
int compare = CompareOperands(interp);
return compare < 0 || compare == 0;
}
}
public class Equal : BooleanBinaryExpression
{
public Equal(Expression left, Expression right) : base(left, right) { }
public override bool BoolValue(Interpretation! interp)
{
int compare = CompareOperands(interp);
return compare == 0;
}
}
public class Greater : BooleanBinaryExpression
{
public Greater(Expression left, Expression right) : base(left, right) { }
public override bool BoolValue(Interpretation! interp)
{
int compare = CompareOperands(interp);
return compare > 0;
}
}
public class GreaterEqual : BooleanBinaryExpression
{
public GreaterEqual(Expression left, Expression right) : base(left, right) { }
public override bool BoolValue(Interpretation! interp)
{
int compare = CompareOperands(interp);
return compare == 0 || compare > 0;
}
}
public class Negate : BooleanExpression
{
Expression expr;
public Negate(Expression expr)
{
this.expr = expr;
}
public override bool BoolValue(Interpretation! interp)
{
return !expr.BoolValue(interp);
}
}
public abstract class StringExpression : Expression
{
public override object! GetValue(Interpretation! interp)
{
return StringValue(interp);
}
public override int IntValue(Interpretation! interp)
{
String value = StringValue(interp);
try {
return Int32.Parse(value);
} catch (FormatException) {
throw new Interpretation.TypeConversionException(value, "String");
}
}
public override bool BoolValue(Interpretation! interp)
{
String value = StringValue(interp);
try {
return Boolean.Parse(value);
} catch (FormatException) {
throw new Interpretation.TypeConversionException(value, "String");
}
}
}
private static Regex whitespace = new Regex(@"\s+");
public class StringInterpolation : StringExpression
{
private ArrayList interpolationElems;
private static Regex escaped_dollar = new Regex(@"\\\$");
private static String[]! Split(string! interpolation)
{
ArrayList chunks = new ArrayList();
char last = (char) -1;
int lastIndex = 0;
for (int i = 0; i < interpolation.Length; last = interpolation[i], ++i) {
if (interpolation[i] == '$' && last != '\\') {
chunks.Add(interpolation.Substring(lastIndex, i - lastIndex));
lastIndex = i + 1;
}
}
chunks.Add((lastIndex < interpolation.Length - 1 ? interpolation.Substring(lastIndex) : ""));
String[] strings = new string[chunks.Count];
chunks.CopyTo(strings);
return strings;
}
[NotDelayed]
public StringInterpolation(string! interpolation)
{
interpolationElems = new ArrayList();
String[] split = Split(interpolation);
if (split[0] != "") {
//replace escaped $ and add
interpolationElems.Add(new StringLiteral(escaped_dollar.Replace(split[0], "$")));
}
for (int i = 1; i < split.Length; ++i) {
string var, rest;
string! split_i = (!)split[i];
if (split_i[0] == '{') {
int index = split_i.IndexOf('}');
if (index == -1) {
throw new ScriptException("Unmatched left brace in " + interpolation);
}
var = split_i.Substring(1, index - 1);
rest = index + 1 < split_i.Length ? split_i.Substring(index + 1) : "";
} else {
Match m = whitespace.Match(split_i);
assert m != null;
int index = m.Success ? m.Index : split_i.Length;
var = split_i.Substring(0, index);
rest = index + 1 < split_i.Length ? split_i.Substring(index + 1) : "";
}
//didn't replace escaped dollar signs in variable, bad syntax anyway.
interpolationElems.Add(new VariableReference(var));
if (rest != "") {
interpolationElems.Add(new StringLiteral(escaped_dollar.Replace(rest, "$")));
}
}
}
public override string! StringValue(Interpretation! interp)
{
StringBuilder sb = new StringBuilder();
foreach (Expression! elem in interpolationElems) {
sb.Append(elem.StringValue(interp));
}
return sb.ToString();
}
}
public class StringLiteral : StringExpression
{
private string literal;
public StringLiteral(String literal)
{
this.literal = literal;
}
public override string! StringValue(Interpretation! interp)
{
return literal;
}
}
public class Concat : StringExpression
{
private Expression left;
private Expression right;
public Concat(Expression left, Expression right)
{
this.left = left; this.right = right;
}
public override string! StringValue(Interpretation! interp)
{
return left.StringValue(interp) + right.StringValue(interp);
}
}
public abstract class Statement : Element
{
public abstract void Interpret(ShellControl! shellControl, Interpretation! interp);
}
public class Assign : Statement
{
String variable;
Expression expression;
public Assign(String variable, Expression expression)
{
this.variable = variable; this.expression = expression;
}
public override void Interpret(ShellControl! shellControl, Interpretation! interp)
{
Object value = expression.GetValue(interp);
if (interp != null)
interp.SetSymbol(variable, value);
}
}
public class If : Statement
{
Expression condition;
Block block;
public If(Expression condition, Block block)
{
this.condition = condition; this.block = block;
}
public override void Interpret(ShellControl! shellControl, Interpretation! interp)
{
if (condition.BoolValue(interp)) {
block.Interpret(shellControl, interp);
}
}
}
public class IfThenElse : Statement
{
Expression condition;
Block trueBlock;
Block falseBlock;
public IfThenElse(Expression condition, Block trueBlock, Block falseBlock)
{
this.condition = condition; this.trueBlock = trueBlock; this.falseBlock = falseBlock;
}
public override void Interpret(ShellControl! shellControl, Interpretation! interp)
{
if (condition.BoolValue(interp)) {
trueBlock.Interpret(shellControl, interp);
} else {
falseBlock.Interpret(shellControl, interp);
}
}
}
public class While : Statement
{
Expression condition;
Block block;
public While(Expression condition, Block block)
{
this.condition = condition; this.block = block;
}
public override void Interpret(ShellControl! shellControl, Interpretation! interp)
{
while (condition.BoolValue(interp)) {
block.Interpret(shellControl, interp);
if (interp.Exit) break;
}
}
}
public class Block : Statement
{
ArrayList statements;
public Block(ArrayList statements)
{
this.statements = statements;
}
public override void Interpret(ShellControl! shellControl, Interpretation! interp)
{
foreach (Statement statement in statements) {
if (statement != null)
statement.Interpret(shellControl, interp);
if (interp.Exit) break;
}
}
public override string! ToString()
{
throw new ScriptException("The method or operation is not implemented.");
}
}
public class Command : Statement
{
public ArrayList commandLine;
bool async;
public Command(ArrayList commandLine) : this(commandLine, false) { }
public Command(ArrayList commandLine, bool async)
{
this.commandLine = commandLine;
this.async = async;
}
public override void Interpret(ShellControl! shellControl, Interpretation! interp)
{
String[] arguments = new String[commandLine.Count];
int i = 0;
//Console.WriteLine("Interpret Command");
foreach (Expression! e in commandLine) {
string arg = e.StringValue(interp);
//Console.WriteLine(" CMD[{0}]: {1}", i, arg);
arguments[i++] = arg;
}
if (i != 0 && ((!)arguments[0]).Trim() == "exit") {
if (arguments.Length >= 2) {
try {
int exitCode = Int32.Parse(arguments[1]);
interp.ExitCode = exitCode;
} catch (FormatException) {
throw new Interpretation.TypeConversionException((String)arguments[1], "integer");
}
}
interp.Exit = true;
} else {
try {
int exitCode = interp.runner(shellControl, arguments, async);
interp.ExitCode = exitCode;
}
catch (FileNotFoundException) {
interp.Exit = true;
}
}
}
}
}
}