diff --git a/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj b/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj index 8d33b4f96..61f52fa86 100644 --- a/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj +++ b/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj @@ -135,6 +135,7 @@ + diff --git a/src/Orchard.Tests.Modules/SimpleScriptingTests/ExpressionEvaluatorTests.cs b/src/Orchard.Tests.Modules/SimpleScriptingTests/ExpressionEvaluatorTests.cs new file mode 100644 index 000000000..e69778fe9 --- /dev/null +++ b/src/Orchard.Tests.Modules/SimpleScriptingTests/ExpressionEvaluatorTests.cs @@ -0,0 +1,13 @@ +using NUnit.Framework; +using Orchard.Widgets.SimpleScripting; + +namespace Orchard.Tests.Modules.SimpleScriptingTests { + [TestFixture] + public class ExpressionEvaluatorTests { + [Test] + public void EvaluateSimpleConstant() { + var tree = new ExpressionParser("1*2+3").Parse(); + + } + } +} diff --git a/src/Orchard.Tests.Modules/SimpleScriptingTests/ExpressionLexerTests.cs b/src/Orchard.Tests.Modules/SimpleScriptingTests/ExpressionLexerTests.cs index 6a2f41052..350305bc4 100644 --- a/src/Orchard.Tests.Modules/SimpleScriptingTests/ExpressionLexerTests.cs +++ b/src/Orchard.Tests.Modules/SimpleScriptingTests/ExpressionLexerTests.cs @@ -35,9 +35,9 @@ namespace Orchard.Tests.Modules.SimpleScriptingTests { public void LexerShouldProcessReservedWords() { TestReservedWord("true", true, TokenKind.True); TestReservedWord("false", false, TokenKind.False); - TestReservedWord("not", "not", TokenKind.Not); - TestReservedWord("and", "and", TokenKind.And); - TestReservedWord("or", "or", TokenKind.Or); + TestReservedWord("not", null, TokenKind.Not); + TestReservedWord("and", null, TokenKind.And); + TestReservedWord("or", null, TokenKind.Or); } private void TestReservedWord(string expression, object value, TokenKind expectedTokenKind) { @@ -58,7 +58,7 @@ namespace Orchard.Tests.Modules.SimpleScriptingTests { [Test] public void LexerShouldProcesSequenceOfTokens2() { - CheckTokenSequence("1+2*3", TokenKind.NumberLiteral, TokenKind.Plus, TokenKind.NumberLiteral, TokenKind.Mul, TokenKind.NumberLiteral); + CheckTokenSequence("1+2*3", TokenKind.Integer, TokenKind.Plus, TokenKind.Integer, TokenKind.Mul, TokenKind.Integer); } diff --git a/src/Orchard.Tests.Modules/SimpleScriptingTests/ExpressionParserTests.cs b/src/Orchard.Tests.Modules/SimpleScriptingTests/ExpressionParserTests.cs index 1de65577c..069f31e81 100644 --- a/src/Orchard.Tests.Modules/SimpleScriptingTests/ExpressionParserTests.cs +++ b/src/Orchard.Tests.Modules/SimpleScriptingTests/ExpressionParserTests.cs @@ -10,7 +10,7 @@ namespace Orchard.Tests.Modules.SimpleScriptingTests { public void ParserShouldUnderstandConstantExpressions() { var tree = new ExpressionParser("true").Parse(); CheckTree(tree, new object[] { - typeof(ExpressionTree.ContantExpression), true, + "const", true, }); } @@ -18,9 +18,9 @@ namespace Orchard.Tests.Modules.SimpleScriptingTests { public void ParserShouldUnderstandBinaryExpressions() { var tree = new ExpressionParser("true+true").Parse(); CheckTree(tree, new object[] { - typeof(ExpressionTree.BinaryExpression), TokenKind.Plus, - typeof(ExpressionTree.ContantExpression), true, - typeof(ExpressionTree.ContantExpression), true, + "binop", TokenKind.Plus, + "const", true, + "const", true, }); } @@ -28,11 +28,11 @@ namespace Orchard.Tests.Modules.SimpleScriptingTests { public void ParserShouldUnderstandOperatorPrecedence() { var tree = new ExpressionParser("1+2*3").Parse(); CheckTree(tree, new object[] { - typeof(ExpressionTree.BinaryExpression), TokenKind.Plus, - typeof(ExpressionTree.ContantExpression), 1, - typeof(ExpressionTree.BinaryExpression), TokenKind.Mul, - typeof(ExpressionTree.ContantExpression), 2, - typeof(ExpressionTree.ContantExpression), 3, + "binop", TokenKind.Plus, + "const", 1, + "binop", TokenKind.Mul, + "const", 2, + "const", 3, }); } @@ -40,11 +40,11 @@ namespace Orchard.Tests.Modules.SimpleScriptingTests { public void ParserShouldUnderstandOperatorPrecedence2() { var tree = new ExpressionParser("1*2+3").Parse(); CheckTree(tree, new object[] { - typeof(ExpressionTree.BinaryExpression), TokenKind.Plus, - typeof(ExpressionTree.BinaryExpression), TokenKind.Mul, - typeof(ExpressionTree.ContantExpression), 1, - typeof(ExpressionTree.ContantExpression), 2, - typeof(ExpressionTree.ContantExpression), 3, + "binop", TokenKind.Plus, + "binop", TokenKind.Mul, + "const", 1, + "const", 2, + "const", 3, }); } @@ -52,10 +52,10 @@ namespace Orchard.Tests.Modules.SimpleScriptingTests { public void ParserShouldUnderstandOperatorPrecedence3() { var tree = new ExpressionParser("not true or true").Parse(); CheckTree(tree, new object[] { - typeof(ExpressionTree.BinaryExpression), TokenKind.Or, - typeof(ExpressionTree.UnaryExpression), TokenKind.Not, - typeof(ExpressionTree.ContantExpression), true, - typeof(ExpressionTree.ContantExpression), true, + "binop", TokenKind.Or, + "unop", TokenKind.Not, + "const", true, + "const", true, }); } @@ -63,10 +63,10 @@ namespace Orchard.Tests.Modules.SimpleScriptingTests { public void ParserShouldUnderstandOperatorPrecedence4() { var tree = new ExpressionParser("not (true or true)").Parse(); CheckTree(tree, new object[] { - typeof(ExpressionTree.UnaryExpression), TokenKind.Not, - typeof(ExpressionTree.BinaryExpression), TokenKind.Or, - typeof(ExpressionTree.ContantExpression), true, - typeof(ExpressionTree.ContantExpression), true, + "unop", TokenKind.Not, + "binop", TokenKind.Or, + "const", true, + "const", true, }); } @@ -74,11 +74,11 @@ namespace Orchard.Tests.Modules.SimpleScriptingTests { public void ParserShouldUnderstandParenthesis() { var tree = new ExpressionParser("1*(2+3)").Parse(); CheckTree(tree, new object[] { - typeof(ExpressionTree.BinaryExpression), TokenKind.Mul, - typeof(ExpressionTree.ContantExpression), 1, - typeof(ExpressionTree.BinaryExpression), TokenKind.Plus, - typeof(ExpressionTree.ContantExpression), 2, - typeof(ExpressionTree.ContantExpression), 3, + "binop", TokenKind.Mul, + "const", 1, + "binop", TokenKind.Plus, + "const", 2, + "const", 3, }); } @@ -86,16 +86,16 @@ namespace Orchard.Tests.Modules.SimpleScriptingTests { public void ParserShouldUnderstandComplexExpressions() { var tree = new ExpressionParser("not 1 * (2 / 4 * 6 + (3))").Parse(); CheckTree(tree, new object[] { - typeof(ExpressionTree.UnaryExpression), TokenKind.Not, - typeof(ExpressionTree.BinaryExpression), TokenKind.Mul, - typeof(ExpressionTree.ContantExpression), 1, - typeof(ExpressionTree.BinaryExpression), TokenKind.Plus, - typeof(ExpressionTree.BinaryExpression), TokenKind.Div, - typeof(ExpressionTree.ContantExpression), 2, - typeof(ExpressionTree.BinaryExpression), TokenKind.Mul, - typeof(ExpressionTree.ContantExpression), 4, - typeof(ExpressionTree.ContantExpression), 6, - typeof(ExpressionTree.ContantExpression), 3, + "unop", TokenKind.Not, + "binop", TokenKind.Mul, + "const", 1, + "binop", TokenKind.Plus, + "binop", TokenKind.Div, + "const", 2, + "binop", TokenKind.Mul, + "const", 4, + "const", 6, + "const", 3, }); } @@ -103,9 +103,9 @@ namespace Orchard.Tests.Modules.SimpleScriptingTests { public void ParserShouldContainErrorExpressions() { var tree = new ExpressionParser("1 + not 3").Parse(); CheckTree(tree, new object[] { - typeof(ExpressionTree.BinaryExpression), TokenKind.Plus, - typeof(ExpressionTree.ContantExpression), 1, - typeof(ExpressionTree.ErrorExpression), + "binop", TokenKind.Plus, + "const", 1, + "error", }); } @@ -119,19 +119,34 @@ namespace Orchard.Tests.Modules.SimpleScriptingTests { } private void CheckExpression(ExpressionTree.Expression expression, int indent, object[] objects, ref int index) { - var type = (Type)objects[index++]; + var exprName = (string)objects[index++]; + Type type = null; + switch(exprName) { + case "const": + type = typeof(ExpressionTree.ConstantExpression); + break; + case "binop": + type = typeof(ExpressionTree.BinaryExpression); + break; + case "unop": + type = typeof(ExpressionTree.UnaryExpression); + break; + case "error": + type = typeof(ExpressionTree.ErrorExpression); + break; + } Trace.WriteLine(string.Format("{0}: {1}{2} (Current: {3})", indent, new string(' ', indent * 2), type.Name, expression)); Assert.That(expression.GetType(), Is.EqualTo(type)); - if (type == typeof(ExpressionTree.ContantExpression)) { - Assert.That((expression as ExpressionTree.ContantExpression).Value, Is.EqualTo(objects[index++])); + if (exprName == "const") { + Assert.That((expression as ExpressionTree.ConstantExpression).Value, Is.EqualTo(objects[index++])); } - else if (type == typeof(ExpressionTree.BinaryExpression)) { + else if (exprName == "binop") { Assert.That((expression as ExpressionTree.BinaryExpression).Operator.Kind, Is.EqualTo(objects[index++])); } - else if (type == typeof(ExpressionTree.UnaryExpression)) { + else if (exprName == "unop") { Assert.That((expression as ExpressionTree.UnaryExpression).Operator.Kind, Is.EqualTo(objects[index++])); } diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/ExpressionParser.cs b/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/ExpressionParser.cs index 97af5edf8..50c35d49b 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/ExpressionParser.cs +++ b/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/ExpressionParser.cs @@ -109,18 +109,26 @@ namespace Orchard.Widgets.SimpleScripting { case TokenKind.False: case TokenKind.SingleQuotedStringLiteral: case TokenKind.StringLiteral: - case TokenKind.NumberLiteral: - _lexer.NextToken(); - return new ExpressionTree.ContantExpression(token); + case TokenKind.Integer: + return ProduceConstant(token); case TokenKind.OpenParen: return ParseParenthesizedExpression(); default: - _lexer.NextToken(); - return new ExpressionTree.ErrorExpression(token, - string.Format("Unexptected token in primary expression ({0})", token)); + return ProduceError(token); } } + private ExpressionTree.Expression ProduceConstant(ExpressionTokenizer.Token token) { + _lexer.NextToken(); + return new ExpressionTree.ConstantExpression(token); + } + + private ExpressionTree.Expression ProduceError(ExpressionTokenizer.Token token) { + _lexer.NextToken(); + return new ExpressionTree.ErrorExpression(token, + string.Format("Unexptected token in primary expression ({0})", token)); + } + private ExpressionTree.Expression ParseParenthesizedExpression() { Match(TokenKind.OpenParen); var expr = ParseExpression(); diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/ExpressionTokenizer.cs b/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/ExpressionTokenizer.cs index 8287e9b3e..ef495de44 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/ExpressionTokenizer.cs +++ b/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/ExpressionTokenizer.cs @@ -6,6 +6,7 @@ namespace Orchard.Widgets.SimpleScripting { private readonly string _expression; private readonly StringBuilder _stringBuilder; private int _index; + private int _startTokenIndex; public ExpressionTokenizer(string expression) { _expression = expression; @@ -17,6 +18,7 @@ namespace Orchard.Widgets.SimpleScripting { return CreateToken(TokenKind.Eof); LexAgain: + _startTokenIndex = _index; char ch = Character(); switch (ch) { case '(': @@ -84,7 +86,7 @@ namespace Orchard.Widgets.SimpleScripting { _stringBuilder.Append(Character()); } else { - return CreateToken(TokenKind.NumberLiteral, Int32.Parse(_stringBuilder.ToString())); + return CreateToken(TokenKind.Integer, Int32.Parse(_stringBuilder.ToString())); } } } @@ -96,11 +98,11 @@ namespace Orchard.Widgets.SimpleScripting { case "false": return CreateToken(TokenKind.False, false); case "or": - return CreateToken(TokenKind.Or, identifier); + return CreateToken(TokenKind.Or, null); case "and": - return CreateToken(TokenKind.And, identifier); + return CreateToken(TokenKind.And, null); case "not": - return CreateToken(TokenKind.Not, identifier); + return CreateToken(TokenKind.Not, null); default: return CreateToken(TokenKind.Identifier, identifier); } @@ -201,7 +203,7 @@ namespace Orchard.Widgets.SimpleScripting { private Token CreateToken(TokenKind kind, object value = null) { return new Token { Kind = kind, - Position = _index, + Position = _startTokenIndex, Value = value }; } @@ -216,7 +218,7 @@ namespace Orchard.Widgets.SimpleScripting { public object Value { get; set; } public override string ToString() { - return string.Format("{0} at position {1}", Value ?? Kind, Position); + return string.Format("{0} ({1}) at position {2}", Kind, Value ?? "", Position); } } } diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/ExpressionTree.cs b/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/ExpressionTree.cs index d10af8bfb..dc9c4a497 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/ExpressionTree.cs +++ b/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/ExpressionTree.cs @@ -24,11 +24,7 @@ namespace Orchard.Widgets.SimpleScripting { var ewt = (this as IExpressionWithToken); if (ewt != null) { sb.Append(" - "); - sb.Append(ewt.Token.Kind); - if (ewt.Token.Value != null) { - sb.Append(" - "); - sb.Append(ewt.Token.Value); - } + sb.Append(ewt.Token); } return sb.ToString(); } @@ -57,10 +53,10 @@ namespace Orchard.Widgets.SimpleScripting { } - public class ContantExpression : Expression, IExpressionWithToken { + public class ConstantExpression : Expression, IExpressionWithToken { private readonly ExpressionTokenizer.Token _token; - public ContantExpression(ExpressionTokenizer.Token token) { + public ConstantExpression(ExpressionTokenizer.Token token) { _token = token; } diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/TokenKind.cs b/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/TokenKind.cs index 83ddff262..1a157c777 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/TokenKind.cs +++ b/src/Orchard.Web/Modules/Orchard.Widgets/SimpleScripting/TokenKind.cs @@ -5,7 +5,7 @@ CloseParen, StringLiteral, SingleQuotedStringLiteral, - NumberLiteral, + Integer, Plus, Minus, Mul,