Initial interpreter implemenation

--HG--
branch : dev
This commit is contained in:
Renaud Paquay
2010-11-27 19:57:33 -08:00
parent 289ba2ff5e
commit 8fa1af1939
10 changed files with 192 additions and 4 deletions

View File

@@ -1,4 +1,5 @@
using NUnit.Framework;
using System;
using NUnit.Framework;
using Orchard.Widgets.SimpleScripting;
using Orchard.Widgets.SimpleScripting.Compiler;
@@ -7,8 +8,25 @@ namespace Orchard.Tests.Modules.SimpleScriptingTests {
public class EvaluatorTests {
[Test]
public void EvaluateSimpleConstant() {
var tree = new Parser("1*2+3").Parse();
var result = EvaluateSimpleExpression("true and true");
Assert.That(result.HasErrors, Is.False);
Assert.That(result.Value, Is.EqualTo(true));
}
[Test]
public void EvaluateSimpleArithmetic() {
var result = EvaluateSimpleExpression("1 + 2 * 3 - 6 / 2");
Assert.That(result.HasErrors, Is.False);
Assert.That(result.Value, Is.EqualTo(4));
}
private EvaluationResult EvaluateSimpleExpression(string expression) {
var ast = new Parser(expression).Parse();
var result = new Interpreter().Evalutate(new EvaluationContext {
Tree = ast,
MethodInvocationCallback = (m, args) => null
});
return result;
}
}
}

View File

@@ -78,7 +78,10 @@
<Compile Include="SimpleScripting\Ast\AstNode.cs" />
<Compile Include="SimpleScripting\Ast\BinaryAstNode.cs" />
<Compile Include="SimpleScripting\Ast\ConstantAstNode.cs" />
<Compile Include="SimpleScripting\Ast\AstVisitor.cs" />
<Compile Include="SimpleScripting\Ast\ErrorAstNode.cs" />
<Compile Include="SimpleScripting\Compiler\Interpreter.cs" />
<Compile Include="SimpleScripting\Compiler\InterpreterVisitor.cs" />
<Compile Include="SimpleScripting\Compiler\Lexer.cs" />
<Compile Include="SimpleScripting\Compiler\Tokenizer.cs" />
<Compile Include="SimpleScripting\Compiler\Parser.cs" />

View File

@@ -3,13 +3,15 @@ using System.Linq;
using System.Text;
namespace Orchard.Widgets.SimpleScripting.Ast {
public class AstNode {
public abstract class AstNode {
public virtual IEnumerable<AstNode> Children {
get {
return Enumerable.Empty<AstNode>();
}
}
public abstract object Accept(AstVisitor visitor);
public override string ToString() {
var sb = new StringBuilder();
sb.Append(this.GetType().Name);

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Orchard.Widgets.SimpleScripting.Ast {
public class AstVisitor {
public virtual object Visit(AstNode node) {
return node.Accept(this);
}
public virtual object VisitChildren(AstNode node) {
return node.Children.Aggregate<AstNode, object>(null, (prev, child) => Visit(child));
}
public virtual object VisitBinary(BinaryAstNode node) {
return null;
}
public virtual object VisitConstant(ConstantAstNode node) {
return null;
}
public virtual object VisitError(ErrorAstNode node) {
return null;
}
public virtual object VisitUnary(UnaryAstNode node) {
return null;
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Orchard.Widgets.SimpleScripting.Compiler;
namespace Orchard.Widgets.SimpleScripting.Ast {
@@ -21,11 +22,19 @@ namespace Orchard.Widgets.SimpleScripting.Ast {
get { return _token; }
}
public override object Accept(AstVisitor visitor) {
return visitor.VisitBinary(this);
}
public override IEnumerable<AstNode> Children {
get {
yield return _left;
yield return _right;
}
}
public AstNode Left { get { return _left; } }
public AstNode Right { get { return _right; } }
}
}

View File

@@ -13,5 +13,9 @@ namespace Orchard.Widgets.SimpleScripting.Ast {
}
public object Value { get { return _token.Value; } }
public override object Accept(AstVisitor visitor) {
return visitor.VisitConstant(this);
}
}
}

View File

@@ -22,5 +22,9 @@ namespace Orchard.Widgets.SimpleScripting.Ast {
public override string ToString() {
return String.Format("{0} - {1}", GetType().Name, Message);
}
public override object Accept(AstVisitor visitor) {
return visitor.VisitError(this);
}
}
}

View File

@@ -25,5 +25,9 @@ namespace Orchard.Widgets.SimpleScripting.Ast {
yield return _expr;
}
}
public override object Accept(AstVisitor visitor) {
return visitor.VisitUnary(this);
}
}
}

View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using Orchard.Widgets.SimpleScripting.Ast;
namespace Orchard.Widgets.SimpleScripting.Compiler {
public class Interpreter {
public EvaluationResult Evalutate(EvaluationContext context) {
return new InterpreterVisitor(context).Evaluate();
}
}
public class EvaluationContext {
public AbstractSyntaxTree Tree { get; set; }
public Func<object, string, IList<object>> MethodInvocationCallback { get; set; }
}
public class EvaluationResult<T> {
public bool HasErrors { get; set; }
public T Value { get; set; }
}
public class EvaluationResult : EvaluationResult<object> {
}
}

View File

@@ -0,0 +1,87 @@
using System;
using Orchard.Widgets.SimpleScripting.Ast;
namespace Orchard.Widgets.SimpleScripting.Compiler {
public class InterpreterVisitor : AstVisitor {
private readonly EvaluationContext _context;
public InterpreterVisitor(EvaluationContext context) {
_context = context;
}
public EvaluationResult Evaluate() {
return Evaluate(_context.Tree.Root);
}
public EvaluationResult Evaluate(AstNode node) {
return (EvaluationResult)this.Visit(node);
}
public override object VisitConstant(ConstantAstNode node) {
return new EvaluationResult { Value = node.Value };
}
public override object VisitBinary(BinaryAstNode node) {
var left = Evaluate(node.Left);
if (left.HasErrors)
return left;
var right = Evaluate(node.Right);
if (right.HasErrors)
return right;
switch (node.Token.Kind) {
case TokenKind.Plus:
return EvaluateArithmetic(left, right, (a, b) => a + b);
case TokenKind.Minus:
return EvaluateArithmetic(left, right, (a, b) => a - b);
case TokenKind.Mul:
return EvaluateArithmetic(left, right, (a, b) => a * b);
case TokenKind.Div:
//TODO: divide by zero?
return EvaluateArithmetic(left, right, (a, b) => a / b);
case TokenKind.And:
return EvaluateLogical(left, right, (a, b) => a && b);
case TokenKind.Or:
return EvaluateLogical(left, right, (a, b) => a || b);
}
return new EvaluationResult {HasErrors = true};
}
private EvaluationResult EvaluateArithmetic(EvaluationResult left, EvaluationResult right, Func<int, int, int> operation) {
//TODO: Proper type conversion
var leftValue = ConvertToInt(left);
var rightValue = ConvertToInt(right);
return new EvaluationResult { Value = operation(leftValue.Value, rightValue.Value) };
}
private EvaluationResult EvaluateLogical(EvaluationResult left, EvaluationResult right, Func<bool, bool, bool> operation) {
//TODO: Proper type conversion
var leftValue = ConvertToBool(left);
var rightValue = ConvertToBool(right);
return new EvaluationResult { Value = operation(leftValue.Value, rightValue.Value) };
}
private EvaluationResult<int> ConvertToInt(EvaluationResult value) {
if (value.Value is int)
return new EvaluationResult<int> { Value = (int)value.Value };
return new EvaluationResult<int>() { HasErrors = true, Value = 0 };
}
private EvaluationResult<bool> ConvertToBool(EvaluationResult value) {
if (value.Value is bool)
return new EvaluationResult<bool>() { Value = (bool)value.Value };
if (value.Value is int)
return new EvaluationResult<bool>() { Value = ((int)value.Value) != 0 };
return new EvaluationResult<bool>() { HasErrors = true, Value = false };
}
}
}