mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
Initial interpreter implemenation
--HG-- branch : dev
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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" />
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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; } }
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -25,5 +25,9 @@ namespace Orchard.Widgets.SimpleScripting.Ast {
|
||||
yield return _expr;
|
||||
}
|
||||
}
|
||||
|
||||
public override object Accept(AstVisitor visitor) {
|
||||
return visitor.VisitUnary(this);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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> {
|
||||
}
|
||||
}
|
@@ -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 };
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user