mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-14 19:04:51 +08:00
Implement parsing of method calls
Parsing methods calls of the form: <identifier> '(' arg [, arg, ...] ')' <identifier> arg [, arg, ...] --HG-- branch : dev
This commit is contained in:
@@ -1,10 +1,8 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Widgets.SimpleScripting;
|
||||
using Orchard.Widgets.SimpleScripting.Compiler;
|
||||
|
||||
namespace Orchard.Tests.Modules.SimpleScriptingTests {
|
||||
namespace Orchard.Tests.Modules.SimpleScripting {
|
||||
[TestFixture]
|
||||
public class EvaluatorTests {
|
||||
[Test]
|
||||
|
@@ -1,11 +1,10 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Widgets.SimpleScripting;
|
||||
using Orchard.Widgets.SimpleScripting.Ast;
|
||||
using Orchard.Widgets.SimpleScripting.Compiler;
|
||||
|
||||
namespace Orchard.Tests.Modules.SimpleScriptingTests {
|
||||
namespace Orchard.Tests.Modules.SimpleScripting {
|
||||
[TestFixture]
|
||||
public class ParserTests {
|
||||
[Test]
|
||||
@@ -26,6 +25,37 @@ namespace Orchard.Tests.Modules.SimpleScriptingTests {
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParserShouldUnderstandCommandExpressions() {
|
||||
var tree = new Parser("print 'foo', 'bar'").Parse();
|
||||
CheckTree(tree, new object[] {
|
||||
"call", TokenKind.Identifier, "print",
|
||||
"const", "foo",
|
||||
"const", "bar",
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParserShouldUnderstandCallExpressions() {
|
||||
var tree = new Parser("print('foo', 'bar')").Parse();
|
||||
CheckTree(tree, new object[] {
|
||||
"call", TokenKind.Identifier, "print",
|
||||
"const", "foo",
|
||||
"const", "bar",
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParserShouldUnderstandCallExpressions2() {
|
||||
var tree = new Parser("print 1+2").Parse();
|
||||
CheckTree(tree, new object[] {
|
||||
"call", TokenKind.Identifier, "print",
|
||||
"binop", TokenKind.Plus,
|
||||
"const", 1,
|
||||
"const", 2,
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParserShouldUnderstandOperatorPrecedence() {
|
||||
var tree = new Parser("1+2*3").Parse();
|
||||
@@ -123,7 +153,7 @@ namespace Orchard.Tests.Modules.SimpleScriptingTests {
|
||||
private void CheckExpression(AstNode astNode, int indent, object[] objects, ref int index) {
|
||||
var exprName = (string)objects[index++];
|
||||
Type type = null;
|
||||
switch(exprName) {
|
||||
switch (exprName) {
|
||||
case "const":
|
||||
type = typeof(ConstantAstNode);
|
||||
break;
|
||||
@@ -133,6 +163,9 @@ namespace Orchard.Tests.Modules.SimpleScriptingTests {
|
||||
case "unop":
|
||||
type = typeof(UnaryAstNode);
|
||||
break;
|
||||
case "call":
|
||||
type = typeof(MethodCallAstNode);
|
||||
break;
|
||||
case "error":
|
||||
type = typeof(ErrorAstNode);
|
||||
break;
|
||||
@@ -151,8 +184,12 @@ namespace Orchard.Tests.Modules.SimpleScriptingTests {
|
||||
else if (exprName == "unop") {
|
||||
Assert.That((astNode as UnaryAstNode).Operator.Kind, Is.EqualTo(objects[index++]));
|
||||
}
|
||||
else if (exprName == "call") {
|
||||
Assert.That((astNode as MethodCallAstNode).Token.Kind, Is.EqualTo(objects[index++]));
|
||||
Assert.That((astNode as MethodCallAstNode).Token.Value, Is.EqualTo(objects[index++]));
|
||||
}
|
||||
|
||||
foreach(var child in astNode.Children) {
|
||||
foreach (var child in astNode.Children) {
|
||||
CheckExpression(child, indent + 1, objects, ref index);
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,7 @@
|
||||
using NUnit.Framework;
|
||||
using Orchard.Widgets.SimpleScripting;
|
||||
using Orchard.Widgets.SimpleScripting.Compiler;
|
||||
|
||||
namespace Orchard.Tests.Modules.SimpleScriptingTests {
|
||||
namespace Orchard.Tests.Modules.SimpleScripting {
|
||||
[TestFixture]
|
||||
public class TokenizerTests {
|
||||
|
||||
|
@@ -80,6 +80,7 @@
|
||||
<Compile Include="SimpleScripting\Ast\ConstantAstNode.cs" />
|
||||
<Compile Include="SimpleScripting\Ast\AstVisitor.cs" />
|
||||
<Compile Include="SimpleScripting\Ast\ErrorAstNode.cs" />
|
||||
<Compile Include="SimpleScripting\Ast\MethodCallAstNode.cs" />
|
||||
<Compile Include="SimpleScripting\Compiler\Interpreter.cs" />
|
||||
<Compile Include="SimpleScripting\Compiler\InterpreterVisitor.cs" />
|
||||
<Compile Include="SimpleScripting\Compiler\Lexer.cs" />
|
||||
|
@@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
|
||||
namespace Orchard.Widgets.SimpleScripting.Ast {
|
||||
public class AstVisitor {
|
||||
@@ -28,5 +25,9 @@ namespace Orchard.Widgets.SimpleScripting.Ast {
|
||||
public virtual object VisitUnary(UnaryAstNode node) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual object VisitMethodCall(MethodCallAstNode node) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Widgets.SimpleScripting.Compiler;
|
||||
|
||||
namespace Orchard.Widgets.SimpleScripting.Ast {
|
||||
public class MethodCallAstNode : AstNode, IAstNodeWithToken {
|
||||
private readonly Token _token;
|
||||
private readonly IList<AstNode> _arguments;
|
||||
|
||||
public MethodCallAstNode(Token token, IList<AstNode> arguments) {
|
||||
_token = token;
|
||||
_arguments = arguments;
|
||||
}
|
||||
|
||||
public Token Token { get { return _token; } }
|
||||
|
||||
public override IEnumerable<AstNode> Children {
|
||||
get { return _arguments; }
|
||||
}
|
||||
|
||||
public override object Accept(AstVisitor visitor) {
|
||||
return visitor.VisitMethodCall(this);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Widgets.SimpleScripting.Ast;
|
||||
|
||||
namespace Orchard.Widgets.SimpleScripting.Compiler {
|
||||
@@ -29,8 +30,7 @@ namespace Orchard.Widgets.SimpleScripting.Compiler {
|
||||
var expr = ParseKeywordAndExpression();
|
||||
|
||||
var token = IsMatch(TokenKind.Or);
|
||||
if (token != null)
|
||||
{
|
||||
if (token != null) {
|
||||
var right = ParseKeywordOrExpression();
|
||||
|
||||
expr = new BinaryAstNode(expr, token, right);
|
||||
@@ -56,7 +56,7 @@ namespace Orchard.Widgets.SimpleScripting.Compiler {
|
||||
var token = IsMatch(TokenKind.Not);
|
||||
if (token != null) {
|
||||
var expr = ParseKeywordNotExpression();
|
||||
|
||||
|
||||
return new UnaryAstNode(expr, token);
|
||||
}
|
||||
|
||||
@@ -66,8 +66,6 @@ namespace Orchard.Widgets.SimpleScripting.Compiler {
|
||||
private AstNode ParseRelationalExpression() {
|
||||
var expr = ParseAdditiveExpression();
|
||||
//TODO
|
||||
//var Token = IsMatch(TokenKind.Not);
|
||||
//if (Token != null) {
|
||||
return expr;
|
||||
}
|
||||
|
||||
@@ -104,7 +102,7 @@ namespace Orchard.Widgets.SimpleScripting.Compiler {
|
||||
|
||||
private AstNode ParsePrimaryExpression() {
|
||||
var token = _lexer.Token();
|
||||
switch(_lexer.Token().Kind) {
|
||||
switch (_lexer.Token().Kind) {
|
||||
case TokenKind.True:
|
||||
case TokenKind.False:
|
||||
case TokenKind.SingleQuotedStringLiteral:
|
||||
@@ -113,11 +111,41 @@ namespace Orchard.Widgets.SimpleScripting.Compiler {
|
||||
return ProduceConstant(token);
|
||||
case TokenKind.OpenParen:
|
||||
return ParseParenthesizedExpression();
|
||||
case TokenKind.Identifier:
|
||||
return ParseMethodCallExpression();
|
||||
default:
|
||||
return ProduceError(token);
|
||||
}
|
||||
}
|
||||
|
||||
private AstNode ParseParenthesizedExpression() {
|
||||
Match(TokenKind.OpenParen);
|
||||
var expr = ParseExpression();
|
||||
Match(TokenKind.CloseParen);
|
||||
return expr;
|
||||
}
|
||||
|
||||
private AstNode ParseMethodCallExpression() {
|
||||
var target = _lexer.Token();
|
||||
_lexer.NextToken();
|
||||
|
||||
bool hasParenthesis = (IsMatch(TokenKind.OpenParen) != null);
|
||||
|
||||
var arguments = new List<AstNode>();
|
||||
while (true) {
|
||||
var argument = ParseExpression();
|
||||
arguments.Add(argument);
|
||||
|
||||
if (IsMatch(TokenKind.Comma) == null)
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasParenthesis)
|
||||
Match(TokenKind.CloseParen);
|
||||
|
||||
return new MethodCallAstNode(target, arguments);
|
||||
}
|
||||
|
||||
private AstNode ProduceConstant(Token token) {
|
||||
_lexer.NextToken();
|
||||
return new ConstantAstNode(token);
|
||||
@@ -125,15 +153,7 @@ namespace Orchard.Widgets.SimpleScripting.Compiler {
|
||||
|
||||
private AstNode ProduceError(Token token) {
|
||||
_lexer.NextToken();
|
||||
return new ErrorAstNode(token,
|
||||
string.Format("Unexptected Token in primary expression ({0})", token));
|
||||
}
|
||||
|
||||
private AstNode ParseParenthesizedExpression() {
|
||||
Match(TokenKind.OpenParen);
|
||||
var expr = ParseExpression();
|
||||
Match(TokenKind.CloseParen);
|
||||
return expr;
|
||||
return new ErrorAstNode(token, string.Format("Unexptected Token in primary expression ({0})", token));
|
||||
}
|
||||
|
||||
private void Match(TokenKind kind) {
|
||||
|
@@ -6,6 +6,7 @@
|
||||
StringLiteral,
|
||||
SingleQuotedStringLiteral,
|
||||
Integer,
|
||||
Comma,
|
||||
Plus,
|
||||
Minus,
|
||||
Mul,
|
||||
|
@@ -27,6 +27,9 @@ namespace Orchard.Widgets.SimpleScripting.Compiler {
|
||||
case ')':
|
||||
NextCharacter();
|
||||
return CreateToken(TokenKind.CloseParen);
|
||||
case ',':
|
||||
NextCharacter();
|
||||
return CreateToken(TokenKind.Comma);
|
||||
case '+':
|
||||
NextCharacter();
|
||||
return CreateToken(TokenKind.Plus);
|
||||
|
Reference in New Issue
Block a user