mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2026-02-09 09:16:41 +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;
|
||||||
using Orchard.Widgets.SimpleScripting.Compiler;
|
using Orchard.Widgets.SimpleScripting.Compiler;
|
||||||
|
|
||||||
@@ -7,8 +8,25 @@ namespace Orchard.Tests.Modules.SimpleScriptingTests {
|
|||||||
public class EvaluatorTests {
|
public class EvaluatorTests {
|
||||||
[Test]
|
[Test]
|
||||||
public void EvaluateSimpleConstant() {
|
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\AstNode.cs" />
|
||||||
<Compile Include="SimpleScripting\Ast\BinaryAstNode.cs" />
|
<Compile Include="SimpleScripting\Ast\BinaryAstNode.cs" />
|
||||||
<Compile Include="SimpleScripting\Ast\ConstantAstNode.cs" />
|
<Compile Include="SimpleScripting\Ast\ConstantAstNode.cs" />
|
||||||
|
<Compile Include="SimpleScripting\Ast\AstVisitor.cs" />
|
||||||
<Compile Include="SimpleScripting\Ast\ErrorAstNode.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\Lexer.cs" />
|
||||||
<Compile Include="SimpleScripting\Compiler\Tokenizer.cs" />
|
<Compile Include="SimpleScripting\Compiler\Tokenizer.cs" />
|
||||||
<Compile Include="SimpleScripting\Compiler\Parser.cs" />
|
<Compile Include="SimpleScripting\Compiler\Parser.cs" />
|
||||||
|
|||||||
@@ -3,13 +3,15 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Orchard.Widgets.SimpleScripting.Ast {
|
namespace Orchard.Widgets.SimpleScripting.Ast {
|
||||||
public class AstNode {
|
public abstract class AstNode {
|
||||||
public virtual IEnumerable<AstNode> Children {
|
public virtual IEnumerable<AstNode> Children {
|
||||||
get {
|
get {
|
||||||
return Enumerable.Empty<AstNode>();
|
return Enumerable.Empty<AstNode>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract object Accept(AstVisitor visitor);
|
||||||
|
|
||||||
public override string ToString() {
|
public override string ToString() {
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
sb.Append(this.GetType().Name);
|
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;
|
using Orchard.Widgets.SimpleScripting.Compiler;
|
||||||
|
|
||||||
namespace Orchard.Widgets.SimpleScripting.Ast {
|
namespace Orchard.Widgets.SimpleScripting.Ast {
|
||||||
@@ -21,11 +22,19 @@ namespace Orchard.Widgets.SimpleScripting.Ast {
|
|||||||
get { return _token; }
|
get { return _token; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override object Accept(AstVisitor visitor) {
|
||||||
|
return visitor.VisitBinary(this);
|
||||||
|
}
|
||||||
|
|
||||||
public override IEnumerable<AstNode> Children {
|
public override IEnumerable<AstNode> Children {
|
||||||
get {
|
get {
|
||||||
yield return _left;
|
yield return _left;
|
||||||
yield return _right;
|
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 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() {
|
public override string ToString() {
|
||||||
return String.Format("{0} - {1}", GetType().Name, Message);
|
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;
|
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> {
|
||||||
|
}
|
||||||
|
}
|
||||||
+87
@@ -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