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:
Renaud Paquay
2010-11-27 23:04:30 -08:00
parent 98abc18658
commit b299c73824
9 changed files with 114 additions and 30 deletions

View File

@@ -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]

View File

@@ -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);
}
}

View File

@@ -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 {

View File

@@ -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" />

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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) {

View File

@@ -6,6 +6,7 @@
StringLiteral,
SingleQuotedStringLiteral,
Integer,
Comma,
Plus,
Minus,
Mul,

View File

@@ -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);