diff --git a/src/Orchard.Tests.Modules/SimpleScripting/EvaluatorTests.cs b/src/Orchard.Tests.Modules/SimpleScripting/EvaluatorTests.cs index e7da077a0..8c99c148a 100644 --- a/src/Orchard.Tests.Modules/SimpleScripting/EvaluatorTests.cs +++ b/src/Orchard.Tests.Modules/SimpleScripting/EvaluatorTests.cs @@ -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] diff --git a/src/Orchard.Tests.Modules/SimpleScripting/ParserTests.cs b/src/Orchard.Tests.Modules/SimpleScripting/ParserTests.cs index 86480a851..9ba3c1436 100644 --- a/src/Orchard.Tests.Modules/SimpleScripting/ParserTests.cs +++ b/src/Orchard.Tests.Modules/SimpleScripting/ParserTests.cs @@ -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); } } diff --git a/src/Orchard.Tests.Modules/SimpleScripting/TokenizerTests.cs b/src/Orchard.Tests.Modules/SimpleScripting/TokenizerTests.cs index 8504b453b..7286e58cd 100644 --- a/src/Orchard.Tests.Modules/SimpleScripting/TokenizerTests.cs +++ b/src/Orchard.Tests.Modules/SimpleScripting/TokenizerTests.cs @@ -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 { diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Orchard.Widgets.csproj b/src/Orchard.Web/Modules/Orchard.Widgets/Orchard.Widgets.csproj index df88d2065..66b4c5264 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/Orchard.Widgets.csproj +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Orchard.Widgets.csproj @@ -80,6 +80,7 @@ + diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/Ast/AstVisitor.cs b/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/Ast/AstVisitor.cs index e91d3ec69..6999d25f2 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/Ast/AstVisitor.cs +++ b/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/Ast/AstVisitor.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; + } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/Ast/MethodCallAstNode.cs b/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/Ast/MethodCallAstNode.cs new file mode 100644 index 000000000..d77d1004a --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/Ast/MethodCallAstNode.cs @@ -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 _arguments; + + public MethodCallAstNode(Token token, IList arguments) { + _token = token; + _arguments = arguments; + } + + public Token Token { get { return _token; } } + + public override IEnumerable Children { + get { return _arguments; } + } + + public override object Accept(AstVisitor visitor) { + return visitor.VisitMethodCall(this); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/Compiler/Parser.cs b/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/Compiler/Parser.cs index 79e230629..35b8b1b2d 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/Compiler/Parser.cs +++ b/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/Compiler/Parser.cs @@ -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(); + 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) { diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/Compiler/TokenKind.cs b/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/Compiler/TokenKind.cs index 0e6f3023b..dd6f8245e 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/Compiler/TokenKind.cs +++ b/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/Compiler/TokenKind.cs @@ -6,6 +6,7 @@ StringLiteral, SingleQuotedStringLiteral, Integer, + Comma, Plus, Minus, Mul, diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/Compiler/Tokenizer.cs b/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/Compiler/Tokenizer.cs index 2d069154e..94b5952eb 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/Compiler/Tokenizer.cs +++ b/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/Compiler/Tokenizer.cs @@ -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);