From 9a4c5d6c5282d92fb937dac35cccc200bb7cbca6 Mon Sep 17 00:00:00 2001 From: Renaud Paquay Date: Fri, 7 Jan 2011 21:44:50 -0800 Subject: [PATCH] Fixing widget layer rule issue Ensuring that the layer rule scripting engine supports more complex rules such as (authenticated) and (url "~/notes"). Add a bunch of unit tests to ensure consistency between DLR based engine and custom engine. Work Item: 17186 --HG-- branch : 1.x --- .../Orchard.Tests.Modules.csproj | 2 + .../Scripting.Dlr/EvaluatorTests.cs | 50 ++ .../Scripting/EvaluatorTests.cs | 147 +---- .../Scripting/EvaluatorTestsBase.cs | 527 ++++++++++++++++++ .../Scripting/ParserTests.cs | 70 ++- .../Services/RubyScriptExpressionEvaluator.cs | 13 +- .../Compiler/EvaluationResult.cs | 1 + .../Compiler/InterpreterVisitor.cs | 23 +- .../Orchard.Scripting/Compiler/Parser.cs | 188 +++++-- .../Compiler/PrimitiveType.cs | 64 +++ .../Orchard.Scripting/Compiler/Token.cs | 2 +- .../Orchard.Scripting/Compiler/TokenKind.cs | 4 +- .../Orchard.Scripting/Compiler/Tokenizer.cs | 30 +- 13 files changed, 904 insertions(+), 217 deletions(-) create mode 100644 src/Orchard.Tests.Modules/Scripting.Dlr/EvaluatorTests.cs create mode 100644 src/Orchard.Tests.Modules/Scripting/EvaluatorTestsBase.cs diff --git a/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj b/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj index 65360fa8e..ef2c26728 100644 --- a/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj +++ b/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj @@ -138,6 +138,8 @@ + + diff --git a/src/Orchard.Tests.Modules/Scripting.Dlr/EvaluatorTests.cs b/src/Orchard.Tests.Modules/Scripting.Dlr/EvaluatorTests.cs new file mode 100644 index 000000000..5dd2b3aca --- /dev/null +++ b/src/Orchard.Tests.Modules/Scripting.Dlr/EvaluatorTests.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Autofac; +using NUnit.Framework; +using Orchard.Scripting; +using Orchard.Scripting.Compiler; +using Orchard.Scripting.Dlr.Services; +using Orchard.Tests.Stubs; + +namespace Orchard.Tests.Modules.Scripting.Dlr { + [TestFixture] + public class EvaluatorTests : EvaluatorTestsBase { + private IContainer _container; + private IScriptingManager _scriptingManager; + + [SetUp] + public void Init() { + var builder = new ContainerBuilder(); + builder.RegisterType().As(); + builder.RegisterType().As(); + _container = builder.Build(); + _scriptingManager = _container.Resolve(); + } + + protected override EvaluationResult EvaluateSimpleExpression(string expression, Func, object> methodInvocationCallback) { + var evaluator = new RubyScriptExpressionEvaluator(_scriptingManager, new StubCacheManager()); + try { + var value = evaluator.Evaluate(expression, new[] { new GlobalMethodProvider(methodInvocationCallback) }); + return new EvaluationResult(value); + } + catch (Exception e) { + Trace.WriteLine(string.Format("Error during evaluation of '{0}': {1}", expression, e.Message)); + return new EvaluationResult(new Error { Message = e.Message, Exception = e }); + } + } + + private class GlobalMethodProvider : IGlobalMethodProvider { + private readonly Func, object> _methodInvocationCallback; + + public GlobalMethodProvider(Func, object> methodInvocationCallback) { + _methodInvocationCallback = methodInvocationCallback; + } + + public void Process(GlobalMethodContext context) { + context.Result = _methodInvocationCallback(context.FunctionName, context.Arguments); + } + } + } +} diff --git a/src/Orchard.Tests.Modules/Scripting/EvaluatorTests.cs b/src/Orchard.Tests.Modules/Scripting/EvaluatorTests.cs index 4f84c496c..f93c211e8 100644 --- a/src/Orchard.Tests.Modules/Scripting/EvaluatorTests.cs +++ b/src/Orchard.Tests.Modules/Scripting/EvaluatorTests.cs @@ -7,146 +7,17 @@ using Orchard.Scripting.Compiler; namespace Orchard.Tests.Modules.Scripting { [TestFixture] - public class EvaluatorTests { - [Test] - public void EvaluateSimpleConstant() { - var result = EvaluateSimpleExpression("true and true"); - Assert.That(result.IsError, Is.False); - Assert.That(result.Value, Is.EqualTo(true)); - } - - [Test] - public void EvaluateInvalidBooleanExpression() { - var result = EvaluateSimpleExpression("true and 1"); - Assert.That(result.IsError, Is.True); - } - - [Test] - public void EvaluateBooleanExpression() { - var result = EvaluateSimpleExpression("not true"); - Assert.That(result.IsError, Is.False); - Assert.That(result.BoolValue, Is.EqualTo(false)); - } - - [Test] - public void EvaluateSimpleArithmetic() { - var result = EvaluateSimpleExpression("1 + 2 * 3 - 6 / 2"); - Assert.That(result.IsError, Is.False); - Assert.That(result.Value, Is.EqualTo(4)); - } - - [Test] - public void EvaluateRelationalOperators() { - var result = EvaluateSimpleExpression("1 < 2"); - Assert.That(result.IsError, Is.False); - Assert.That(result.Value, Is.EqualTo(true)); - } - - [Test] - public void EvaluateRelationalOperators2() { - var result = EvaluateSimpleExpression("2 <= 2"); - Assert.That(result.IsError, Is.False); - Assert.That(result.Value, Is.EqualTo(true)); - } - - [Test] - public void EvaluateRelationalOperators3() { - var result = EvaluateSimpleExpression("1 < 2 or 2 > 3 and !false"); - Assert.That(result.IsError, Is.False); - Assert.That(result.Value, Is.EqualTo(true)); - } - - [Test] - public void EvaluateRelationalOperators4() { - var result = EvaluateSimpleExpression("1 > 2 or 2 > 3 and !false"); - Assert.That(result.IsError, Is.False); - Assert.That(result.Value, Is.EqualTo(false)); - } - - [Test] - public void EvaluateRelationalOperators5() { - var result = EvaluateSimpleExpression("1 > 2 or 4 > 3 and !false"); - Assert.That(result.IsError, Is.False); - Assert.That(result.Value, Is.EqualTo(true)); - } - - [Test] - public void EvaluateRelationalOperators6() { - var result = EvaluateSimpleExpression("!false"); - Assert.That(result.IsError, Is.False); - Assert.That(result.Value, Is.EqualTo(true)); - } - - [Test] - public void EvaluateEqualityOperators() { - var result = EvaluateSimpleExpression("1 == 2"); - Assert.That(result.IsError, Is.False); - Assert.That(result.Value, Is.EqualTo(false)); - } - - [Test] - public void EvaluateEqualityOperators2() { - var result = EvaluateSimpleExpression("1 != 2"); - Assert.That(result.IsError, Is.False); - Assert.That(result.Value, Is.EqualTo(true)); - } - - [Test] - public void EvaluateSimpleMethodCall() { - var result = EvaluateSimpleExpression("print 1 + 2 * 3 - 6 / 2", - (m, args) => (m == "print") ? (int)args[0] * 2 : 0); - Assert.That(result.IsError, Is.False); - Assert.That(result.Value, Is.EqualTo(4 * 2)); - } - - [Test] - public void EvaluateSimpleMethodCall2() { - var result = EvaluateSimpleExpression("foo 1 + bar 3", - (m, args) => - (m == "foo") ? (int)args[0] * 2 : - (m == "bar") ? (int)args[0] : 0); - Assert.That(result.IsError, Is.False); - Assert.That(result.Value, Is.EqualTo(2 * (1 + 3))); - } - - [Test] - public void EvaluateSimpleMethodCall3() { - var result = EvaluateSimpleExpression("foo(1) + bar(3)", - (m, args) => - (m == "foo") ? (int)args[0] * 2 : - (m == "bar") ? (int)args[0] : 0); - Assert.That(result.IsError, Is.False); - Assert.That(result.Value, Is.EqualTo(2 + 3)); - } - - [Test] - public void EvaluateSimpleMethodCall4() { - var result = EvaluateSimpleExpression("foo", - (m, args) => (m == "foo") ? true : false); - Assert.That(result.IsError, Is.False); - Assert.That(result.Value, Is.EqualTo(true)); - } - - [Test] - public void EvaluateSimpleMethodCall5() { - var result = EvaluateSimpleExpression("foo()", - (m, args) => (m == "foo") ? true : false); - Assert.That(result.IsError, Is.False); - Assert.That(result.Value, Is.EqualTo(true)); - } - - private EvaluationResult EvaluateSimpleExpression(string expression) { - return EvaluateSimpleExpression(expression, (m, args) => null); - } - - private EvaluationResult EvaluateSimpleExpression( - string expression, Func, object> methodInvocationCallback) { - + public class EvaluatorTests : EvaluatorTestsBase { + protected override EvaluationResult EvaluateSimpleExpression(string expression, Func, object> methodInvocationCallback) { var ast = new Parser(expression).Parse(); - foreach(var error in ast.GetErrors()) { + foreach (var error in ast.GetErrors()) { Trace.WriteLine(string.Format("Error during parsing of '{0}': {1}", expression, error.Message)); } - Assert.That(ast.GetErrors().Any(), Is.False); + + if (ast.GetErrors().Any()) { + return new EvaluationResult(new Error { Message = ast.GetErrors().First().Message }); + } + var result = new Interpreter().Evalutate(new EvaluationContext { Tree = ast, MethodInvocationCallback = methodInvocationCallback @@ -155,4 +26,4 @@ namespace Orchard.Tests.Modules.Scripting { return result; } } -} +} \ No newline at end of file diff --git a/src/Orchard.Tests.Modules/Scripting/EvaluatorTestsBase.cs b/src/Orchard.Tests.Modules/Scripting/EvaluatorTestsBase.cs new file mode 100644 index 000000000..1f3be7ae3 --- /dev/null +++ b/src/Orchard.Tests.Modules/Scripting/EvaluatorTestsBase.cs @@ -0,0 +1,527 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Orchard.Scripting.Compiler; + +namespace Orchard.Tests.Modules.Scripting { + [TestFixture] + public abstract class EvaluatorTestsBase { + [Test] + public void EvaluateSimpleConstant() { + var result = EvaluateSimpleExpression("true and true"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateSimpleConstant0() { + var result = EvaluateSimpleExpression("true && true"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateConvertingBooleanExpression() { + var result = EvaluateSimpleExpression("true and 1"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(1)); + } + + [Test] + public void EvaluateConvertingBooleanExpression1() { + var result = EvaluateSimpleExpression("true && 1"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(1)); + } + + [Test] + public void EvaluateConvertingBooleanExpression2() { + var result = EvaluateSimpleExpression("true and 0"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(0)); + } + + [Test] + public void EvaluateConvertingBooleanExpression3() { + var result = EvaluateSimpleExpression("true && 0"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(0)); + } + + [Test] + public void EvaluateConvertingBooleanExpression4() { + var result = EvaluateSimpleExpression("1 and true"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateConvertingBooleanExpression5() { + var result = EvaluateSimpleExpression("0 and true"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateConvertingBooleanExpression6() { + var result = EvaluateSimpleExpression("1 && true"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateConvertingBooleanExpression7() { + var result = EvaluateSimpleExpression("true and 'boo'"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo("boo")); + } + + [Test] + public void EvaluateConvertingBooleanExpression8() { + var result = EvaluateSimpleExpression("true && 'boo'"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo("boo")); + } + + [Test] + public void EvaluateConvertingBooleanExpression9() { + var result = EvaluateSimpleExpression("'boo' and true"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateConvertingBooleanExpression10() { + var result = EvaluateSimpleExpression("'boo' && true"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateConvertingBooleanExpression11() { + var result = EvaluateSimpleExpression("true or 1"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateConvertingBooleanExpression12() { + var result = EvaluateSimpleExpression("true || 1"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateConvertingBooleanExpression13() { + var result = EvaluateSimpleExpression("1 or true"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(1)); + } + + [Test] + public void EvaluateConvertingBooleanExpression14() { + var result = EvaluateSimpleExpression("1 || true"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(1)); + } + + [Test] + public void EvaluateConvertingBooleanExpression15() { + var result = EvaluateSimpleExpression("true or 'boo'"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateConvertingBooleanExpression16() { + var result = EvaluateSimpleExpression("false or 'boo'"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo("boo")); + } + + [Test] + public void EvaluateConvertingBooleanExpression17() { + var result = EvaluateSimpleExpression("nil or 'boo'"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo("boo")); + } + + [Test] + public void EvaluateConvertingBooleanExpression18() { + var result = EvaluateSimpleExpression("'boo' or nil"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo("boo")); + } + + [Test] + public void EvaluateConvertingBooleanExpression19() { + var result = EvaluateSimpleExpression("true || 'boo'"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateConvertingBooleanExpression20() { + var result = EvaluateSimpleExpression("'boo' or true"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo("boo")); + } + + [Test] + public void EvaluateConvertingBooleanExpression21() { + var result = EvaluateSimpleExpression("'boo' || true"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo("boo")); + } + + [Test] + public void EvaluateConvertingBooleanExpression22() { + var result = EvaluateSimpleExpression("1 and 2"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(2)); + } + + [Test] + public void EvaluateConvertingBooleanExpression23() { + var result = EvaluateSimpleExpression("false and 2"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(false)); + } + + [Test] + public void EvaluateConvertingBooleanExpression24() { + var result = EvaluateSimpleExpression("nil and 2"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(null)); + } + + [Test] + public void EvaluateConvertingBooleanExpression25() { + var result = EvaluateSimpleExpression("nil and false"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(null)); + } + + [Test] + public void EvaluateConvertingBooleanExpression26() { + var result = EvaluateSimpleExpression("nil and true"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(null)); + } + + [Test] + public void EvaluateBooleanExpression() { + var result = EvaluateSimpleExpression("not true"); + Assert.That(result.IsError, Is.False); + Assert.That(result.BoolValue, Is.EqualTo(false)); + } + + [Test] + public void EvaluateBooleanExpression0() { + var result = EvaluateSimpleExpression("!true"); + Assert.That(result.IsError, Is.False); + Assert.That(result.BoolValue, Is.EqualTo(false)); + } + + [Test] + public void EvaluateSimpleArithmetic() { + var result = EvaluateSimpleExpression("1 + 2 * 3 - 6 / 2"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(4)); + } + + [Test] + public void EvaluateRelationalOperators() { + var result = EvaluateSimpleExpression("1 < 2"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateRelationalOperators2() { + var result = EvaluateSimpleExpression("2 <= 2"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateRelationalOperators3() { + var result = EvaluateSimpleExpression("1 < 2 or 2 > 3 and !false"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateRelationalOperators4() { + var result = EvaluateSimpleExpression("1 > 2 or 2 > 3 and !false"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(false)); + } + + [Test] + public void EvaluateRelationalOperators5() { + var result = EvaluateSimpleExpression("1 > 2 or 4 > 3 and !false"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateRelationalOperators6() { + var result = EvaluateSimpleExpression("!false"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateRelationalOperators7() { + var result = EvaluateSimpleExpression("5 || 10 && nil"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(5)); + } + + [Test] + public void EvaluateRelationalOperators8() { + var result = EvaluateSimpleExpression("true or false and nil"); + Assert.That(result.IsError, Is.False); + Assert.That(result.IsNull, Is.True); + } + + [Test] + public void EvaluateRelationalOperators9() { + var result = EvaluateSimpleExpression("true and nil"); + Assert.That(result.IsError, Is.False); + Assert.That(result.IsNull, Is.True); + } + + [Test] + public void EvaluateRelationalOperators10() { + var result = EvaluateSimpleExpression("5 and nil"); + Assert.That(result.IsError, Is.False); + Assert.That(result.IsNull, Is.True); + } + + [Test] + public void EvaluateEqualityOperators() { + var result = EvaluateSimpleExpression("1 == 2"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(false)); + } + + [Test] + public void EvaluateEqualityOperators2() { + var result = EvaluateSimpleExpression("1 != 2"); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateSimpleMethodCall() { + var result = EvaluateSimpleExpression("printtoto 1 + 2 * 3 - 6 / 2", + (m, args) => (m == "printtoto") ? (int)args[0] * 2 : 0); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(4 * 2)); + } + + [Test] + public void EvaluateSimpleMethodCall2() { + var result = EvaluateSimpleExpression("printtoto(1 + 2 * 3 - 6 / 2)", + (m, args) => (m == "printtoto") ? (int)args[0] * 2 : 0); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(4 * 2)); + } + + [Test] + public void EvaluateSimpleMethodCall3() { + var result = EvaluateSimpleExpression("foo 1 + bar 3", + (m, args) => + (m == "foo") ? (int)args[0] * 2 : + (m == "bar") ? (int)args[0] : 0); + Assert.That(result.IsError, Is.True); + } + + [Test] + public void EvaluateSimpleMethodCall4() { + var result = EvaluateSimpleExpression("foo(1 + bar 3)", + (m, args) => + (m == "foo") ? (int)args[0] * 2 : + (m == "bar") ? (int)args[0] : 0); + Assert.That(result.IsError, Is.True); + } + + [Test] + public void EvaluateSimpleMethodCall5() { + var result = EvaluateSimpleExpression("foo 1 + bar(3)", + (m, args) => + (m == "foo") ? (int)args[0] * 2 : + (m == "bar") ? (int)args[0] : 0); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(2 * (1 + 3))); + } + + [Test] + public void EvaluateSimpleMethodCall6() { + var result = EvaluateSimpleExpression("foo(1) + bar(3)", + (m, args) => + (m == "foo") ? (int)args[0] * 2 : + (m == "bar") ? (int)args[0] : 0); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(2 + 3)); + } + + [Test] + public void EvaluateSimpleMethodCall7() { + var result = EvaluateSimpleExpression("foo", + (m, args) => (m == "foo") ? true : false); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateSimpleMethodCall8() { + var result = EvaluateSimpleExpression("foo()", + (m, args) => (m == "foo") ? true : false); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateSimpleMethodCall9() { +#if false + var result = EvaluateSimpleExpression("1 + bar 3", + (m, args) => + (m == "bar") ? (int)args[0] : 0); + Assert.That(result.IsError, Is.True); +#endif + } + + [Test] + public void EvaluateSimpleMethodCall10() { +#if false + var result = EvaluateSimpleExpression("1 || bar 3", + (m, args) => + (m == "bar") ? (int)args[0] : 0); + Assert.That(result.IsError, Is.True); +#endif + } + + [Test] + public void EvaluateSimpleMethodCall11() { +#if false + var result = EvaluateSimpleExpression("1 * bar 3", + (m, args) => + (m == "bar") ? (int)args[0] : 0); + Assert.That(result.IsError, Is.True); +#endif + } + + [Test] + public void EvaluateSimpleMethodCall12() { +#if false + var result = EvaluateSimpleExpression("1 && bar 3", + (m, args) => + (m == "bar") ? (int)args[0] : 0); + Assert.That(result.IsError, Is.True); +#endif + } + + [Test] + public void EvaluateSimpleMethodCall13() { +#if false + var result = EvaluateSimpleExpression("(1 + bar 3)", + (m, args) => + (m == "bar") ? (int)args[0] : 0); + Assert.That(result.IsError, Is.True); +#endif + } + + [Test] + public void EvaluateSimpleMethodCall14() { + var result = EvaluateSimpleExpression("1 + bar(3)", + (m, args) => + (m == "bar") ? (int)args[0] : 0); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(1 + 3)); + } + + [Test] + public void EvaluateSimpleMethodCall15() { + var result = EvaluateSimpleExpression("1 + (bar 3)", + (m, args) => + (m == "bar") ? (int)args[0] : 0); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(1 + 3)); + } + + [Test] + public void EvaluateSimpleMethodCall16() { + var result = EvaluateSimpleExpression("1 and bar 3", + (m, args) => + (m == "bar") ? (int)args[0] : 0); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(3)); + } + + [Test] + public void EvaluateSimpleMethodCall17() { + var result = EvaluateSimpleExpression("1 or bar 3", + (m, args) => + (m == "bar") ? (int)args[0] : 0); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(1)); + } + + [Test] + public void EvaluateComplexMethodCall() { + var result = EvaluateSimpleExpression("authenticated and url \"~/boo*\"", + (m, args) => (m == "authenticated") ? true : (m == "url") ? (string)args[0] == "~/boo*" : false); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateComplexMethodCall2() { + var result = EvaluateSimpleExpression("(authenticated) and (url \"~/boo*\")", + (m, args) => (m == "authenticated") ? true : (m == "url") ? (string)args[0] == "~/boo*" : false); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateComplexMethodCall3() { + var result = EvaluateSimpleExpression("(authenticated and url \"~/boo*\")", + (m, args) => (m == "authenticated") ? true : (m == "url") ? (string)args[0] == "~/boo*" : false); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateComplexMethodCall4() { + var result = EvaluateSimpleExpression("(authenticated) and url \"~/boo*\"", + (m, args) => (m == "authenticated") ? true : (m == "url") ? (string)args[0] == "~/boo*" : false); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateComplexMethodCall5() { + var result = EvaluateSimpleExpression("(authenticated()) and (url \"~/boo*\")", + (m, args) => (m == "authenticated") ? true : (m == "url") ? (string)args[0] == "~/boo*" : false); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public void EvaluateComplexMethodCall6() { + var result = EvaluateSimpleExpression("authenticated() and url(\"~/boo*\")", + (m, args) => (m == "authenticated") ? true : (m == "url") ? (string)args[0] == "~/boo*" : false); + Assert.That(result.IsError, Is.False); + Assert.That(result.Value, Is.EqualTo(true)); + } + + private EvaluationResult EvaluateSimpleExpression(string expression) { + return EvaluateSimpleExpression(expression, (m, args) => null); + } + + protected abstract EvaluationResult EvaluateSimpleExpression(string expression, Func, object> methodInvocationCallback); + } +} \ No newline at end of file diff --git a/src/Orchard.Tests.Modules/Scripting/ParserTests.cs b/src/Orchard.Tests.Modules/Scripting/ParserTests.cs index d4ecfc481..1f5afce59 100644 --- a/src/Orchard.Tests.Modules/Scripting/ParserTests.cs +++ b/src/Orchard.Tests.Modules/Scripting/ParserTests.cs @@ -110,6 +110,30 @@ namespace Orchard.Tests.Modules.Scripting { }); } + [Test] + public void ParserShouldUnderstandOperatorPrecedence5() { + var tree = new Parser("1+2+3").Parse(); + CheckTree(tree, new object[] { + "binop", TokenKind.Plus, + "binop", TokenKind.Plus, + "const", 1, + "const", 2, + "const", 3, + }); + } + + [Test] + public void ParserShouldUnderstandOperatorPrecedence6() { + var tree = new Parser("1+2-3").Parse(); + CheckTree(tree, new object[] { + "binop", TokenKind.Minus, + "binop", TokenKind.Plus, + "const", 1, + "const", 2, + "const", 3, + }); + } + [Test] public void ParserShouldUnderstandRelationalOperators() { var tree = new Parser("true == true").Parse(); @@ -184,14 +208,14 @@ namespace Orchard.Tests.Modules.Scripting { public void ParserShouldUnderstandRelationalOperatorPrecedence() { var tree = new Parser("1 < 2 or 2 > 3 and !false").Parse(); CheckTree(tree, new object[] { - "binop", TokenKind.Or, - "binop", TokenKind.LessThan, - "const", 1, - "const", 2, "binop", TokenKind.And, - "binop", TokenKind.GreaterThan, - "const", 2, - "const", 3, + "binop", TokenKind.Or, + "binop", TokenKind.LessThan, + "const", 1, + "const", 2, + "binop", TokenKind.GreaterThan, + "const", 2, + "const", 3, "unop", TokenKind.NotSign, "const", false, }); @@ -201,14 +225,14 @@ namespace Orchard.Tests.Modules.Scripting { public void ParserShouldUnderstandRelationalOperatorPrecedence2() { var tree = new Parser("1 < 2 and 2 > 3 or !false").Parse(); CheckTree(tree, new object[] { - "binop", TokenKind.And, - "binop", TokenKind.LessThan, - "const", 1, - "const", 2, - "binop", TokenKind.Or, - "binop", TokenKind.GreaterThan, - "const", 2, - "const", 3, + "binop", TokenKind.Or, + "binop", TokenKind.And, + "binop", TokenKind.LessThan, + "const", 1, + "const", 2, + "binop", TokenKind.GreaterThan, + "const", 2, + "const", 3, "unop", TokenKind.NotSign, "const", false, }); @@ -234,11 +258,11 @@ namespace Orchard.Tests.Modules.Scripting { "binop", TokenKind.Mul, "const", 1, "binop", TokenKind.Plus, - "binop", TokenKind.Div, - "const", 2, - "binop", TokenKind.Mul, + "binop", TokenKind.Mul, + "binop", TokenKind.Div, + "const", 2, "const", 4, - "const", 6, + "const", 6, "const", 3, }); } @@ -246,6 +270,14 @@ namespace Orchard.Tests.Modules.Scripting { [Test] public void ParserShouldContainErrorExpressions() { var tree = new Parser("1 + not 3").Parse(); + CheckTree(tree, new object[] { + "error", + }); + } + + [Test] + public void ParserShouldContainErrorExpressions2() { + var tree = new Parser("1 +").Parse(); CheckTree(tree, new object[] { "binop", TokenKind.Plus, "const", 1, diff --git a/src/Orchard.Web/Modules/Orchard.Scripting.Dlr/Services/RubyScriptExpressionEvaluator.cs b/src/Orchard.Web/Modules/Orchard.Scripting.Dlr/Services/RubyScriptExpressionEvaluator.cs index 5f4fa40ac..61a283ce3 100644 --- a/src/Orchard.Web/Modules/Orchard.Scripting.Dlr/Services/RubyScriptExpressionEvaluator.cs +++ b/src/Orchard.Web/Modules/Orchard.Scripting.Dlr/Services/RubyScriptExpressionEvaluator.cs @@ -41,6 +41,12 @@ ExecContext var ops = _cacheManager.Get("----", ctx => (ObjectOperations)_scriptingManager.ExecuteOperation(x => x)); object execContext = _cacheManager.Get(expression, ctx => (object)ops.InvokeMember(execContextType, "alloc", expression)); dynamic result = ops.InvokeMember(execContext, "evaluate", new CallbackApi(this, providers)); + return ConvertRubyValue(result); + } + + private object ConvertRubyValue(object result) { + if (result is IronRuby.Builtins.MutableString) + return result.ToString(); return result; } @@ -58,8 +64,11 @@ ExecContext } } - private object Evaluate(IEnumerable providers, string name, IList args) { - GlobalMethodContext ruleContext = new GlobalMethodContext { FunctionName = name, Arguments = args.ToArray() }; + private object Evaluate(IEnumerable providers, string name, IEnumerable args) { + var ruleContext = new GlobalMethodContext { + FunctionName = name, + Arguments = args.Select(v => ConvertRubyValue(v)).ToArray() + }; foreach (var ruleProvider in providers) { ruleProvider.Process(ruleContext); diff --git a/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/EvaluationResult.cs b/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/EvaluationResult.cs index a7428bb7e..20f295a64 100644 --- a/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/EvaluationResult.cs +++ b/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/EvaluationResult.cs @@ -39,6 +39,7 @@ namespace Orchard.Scripting.Compiler { public class Error { public string Message { get; set; } + public Exception Exception { get; set; } public override string ToString() { return string.Format("Error: {0}", Message); diff --git a/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/InterpreterVisitor.cs b/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/InterpreterVisitor.cs index f1a6ef50f..25a300a14 100644 --- a/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/InterpreterVisitor.cs +++ b/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/InterpreterVisitor.cs @@ -59,9 +59,11 @@ namespace Orchard.Scripting.Compiler { case TokenKind.Div: return EvaluateArithmetic(left, right, (a, b) => b.Int32Value == 0 ? Error("Attempted to divide by zero.") : Result(a.Int32Value / b.Int32Value)); case TokenKind.And: - return EvaluateLogical(left, right, (a, b) => Result(a.BoolValue && b.BoolValue)); + case TokenKind.AndSign: + return EvaluateLogicalAnd(left, right); case TokenKind.Or: - return EvaluateLogical(left, right, (a, b) => Result(a.BoolValue || b.BoolValue)); + case TokenKind.OrSign: + return EvaluateLogicalOr(left, right); case TokenKind.EqualEqual: return EvaluateEquality(left, right, v => v); case TokenKind.NotEqual: @@ -122,17 +124,14 @@ namespace Orchard.Scripting.Compiler { return operation(leftValue, rightValue); } - private static EvaluationResult EvaluateLogical(EvaluationResult left, EvaluationResult right, - Func operation) { - var leftValue = ConvertToBool(left); - if (leftValue.IsError) - return leftValue; + private EvaluationResult EvaluateLogicalAnd(EvaluationResult left, EvaluationResult right) { + var type = PrimitiveType.InstanceFor(left.Value); + return type.LogicalAnd(left, right); + } - var rightValue = ConvertToBool(right); - if (rightValue.IsError) - return rightValue; - - return operation(leftValue, rightValue); + private EvaluationResult EvaluateLogicalOr(EvaluationResult left, EvaluationResult right) { + var type = PrimitiveType.InstanceFor(left.Value); + return type.LogicalOr(left, right); } private static EvaluationResult ConvertToInt(EvaluationResult value) { diff --git a/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/Parser.cs b/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/Parser.cs index e1157ee45..1da1dbb17 100644 --- a/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/Parser.cs +++ b/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/Parser.cs @@ -1,11 +1,12 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Orchard.Scripting.Ast; namespace Orchard.Scripting.Compiler { public class Parser { private readonly string _expression; private readonly Lexer _lexer; - private readonly List _errors = new List(); + private bool _parsingMethodCall = false; public Parser(string expression) { _expression = expression; @@ -14,6 +15,9 @@ namespace Orchard.Scripting.Compiler { public AbstractSyntaxTree Parse() { var node = ParseExpression(); + if (_lexer.Token().Kind != TokenKind.Eof) { + node = UnexpectedTokenError(); + } return new AbstractSyntaxTree { Root = node }; } @@ -24,11 +28,13 @@ namespace Orchard.Scripting.Compiler { private AstNode ParseKeywordLogicalExpression() { var expr = ParseKeywordNotExpression(); + again: var token = IsMatch(TokenKind.Or, TokenKind.And); if (token != null) { - var right = ParseKeywordLogicalExpression(); + var right = ParseKeywordNotExpression(); expr = new BinaryAstNode(expr, token, right); + goto again; } return expr; @@ -42,11 +48,42 @@ namespace Orchard.Scripting.Compiler { return new UnaryAstNode(token, expr); } - return ParseEqualityExpression(); + return ParseLogicalOrExpression(); } + private AstNode ParseLogicalOrExpression() { + var expr = ParseLogicalAndExpression(); + + again: + var token = IsMatch(TokenKind.OrSign); + if (token != null) { + var right = ParseLogicalAndExpression(); + + expr = new BinaryAstNode(expr, token, right); + goto again; + } + + return expr; + } + + private AstNode ParseLogicalAndExpression() { + var expr = ParseEqualityExpression(); + + again: + var token = IsMatch(TokenKind.AndSign); + if (token != null) { + var right = ParseEqualityExpression(); + + expr = new BinaryAstNode(expr, token, right); + goto again; + } + + return expr; + } + + private AstNode ParseEqualityExpression() { - var expr = ParseRelationalExpression(); + var expr = ParseComparisonExpression(); var token = IsMatch(TokenKind.EqualEqual, TokenKind.NotEqual); if (token != null) { @@ -58,14 +95,14 @@ namespace Orchard.Scripting.Compiler { return expr; } - private AstNode ParseRelationalExpression() { + private AstNode ParseComparisonExpression() { var expr = ParseAdditiveExpression(); - var token = + var token = IsMatch(TokenKind.LessThan, TokenKind.LessThanEqual) ?? IsMatch(TokenKind.GreaterThan, TokenKind.GreaterThanEqual); if (token != null) { - var right = ParseRelationalExpression(); + var right = ParseComparisonExpression(); expr = new BinaryAstNode(expr, token, right); } @@ -76,11 +113,13 @@ namespace Orchard.Scripting.Compiler { private AstNode ParseAdditiveExpression() { var expr = ParseMultiplicativeExpression(); + again: var token = IsMatch(TokenKind.Plus, TokenKind.Minus); if (token != null) { - var right = ParseAdditiveExpression(); + var right = ParseMultiplicativeExpression(); expr = new BinaryAstNode(expr, token, right); + goto again; } return expr; @@ -89,11 +128,13 @@ namespace Orchard.Scripting.Compiler { private AstNode ParseMultiplicativeExpression() { var expr = ParseUnaryExpression(); + again: var token = IsMatch(TokenKind.Mul, TokenKind.Div); if (token != null) { - var right = ParseMultiplicativeExpression(); + var right = ParseUnaryExpression(); expr = new BinaryAstNode(expr, token, right); + goto again; } return expr; @@ -125,14 +166,25 @@ namespace Orchard.Scripting.Compiler { case TokenKind.Identifier: return ParseMethodCallExpression(); default: - return ProduceError(token); + return UnexpectedTokenError(); } } + private AstNode ParseIndentifier(Token identifier) { + return new MethodCallAstNode(identifier, new List()); + } + private AstNode ParseParenthesizedExpression() { - Match(TokenKind.OpenParen); + // '(' + _lexer.NextToken(); + var expr = ParseExpression(); - Match(TokenKind.CloseParen); + + // ')' + if (IsMatch(TokenKind.CloseParen) == null) { + return ExpectedTokenError(TokenKind.CloseParen); + } + return expr; } @@ -142,27 +194,84 @@ namespace Orchard.Scripting.Compiler { bool isParenthesizedCall = (IsMatch(TokenKind.OpenParen) != null); - var arguments = new List(); - while (true) { - // Special case: we might reach the end of the token stream - if (_lexer.Token().Kind == TokenKind.Eof) - break; - - // Special case: we must support "foo()" - if (isParenthesizedCall && _lexer.Token().Kind == TokenKind.CloseParen) - break; - - var argument = ParseExpression(); - arguments.Add(argument); - - if (IsMatch(TokenKind.Comma) == null) - break; + // This is to avoid parsing method calls within method calls that have no + // parenthesis (language ambiguity) + if (!isParenthesizedCall && _parsingMethodCall) { + return ParseIndentifier(target); } - if (isParenthesizedCall) - Match(TokenKind.CloseParen); + // Detect tokens that can't be a function argument start token + if (!IsValidMethodArgumentToken(isParenthesizedCall)) { + return ParseIndentifier(target); + } - return new MethodCallAstNode(target, arguments); + _parsingMethodCall = true; + try { + + var arguments = new List(); + while (true) { + // Special case: we might reach the end of the token stream + if (_lexer.Token().Kind == TokenKind.Eof) + break; + + // Special case: we must support "foo()" + if (isParenthesizedCall && _lexer.Token().Kind == TokenKind.CloseParen) + break; + + // Special case: for non parenthized calls, some tokens mark the end of the call + if (!isParenthesizedCall) { + bool endOfMethodCall = false; + switch (_lexer.Token().Kind) { + case TokenKind.And: + case TokenKind.Or: + case TokenKind.Not: + endOfMethodCall = true; + break; + } + if (endOfMethodCall) + break; + } + + var argument = ParseExpression(); + arguments.Add(argument); + + if (IsMatch(TokenKind.Comma) == null) + break; + } + + if (isParenthesizedCall) { + // ')' + if (IsMatch(TokenKind.CloseParen) == null) { + return ExpectedTokenError(TokenKind.CloseParen); + } + } + + return new MethodCallAstNode(target, arguments); + } + finally { + _parsingMethodCall = false; + } + } + + private bool IsValidMethodArgumentToken(bool isParenthesizedCall) { + switch(_lexer.Token().Kind) { + case TokenKind.OpenParen: + case TokenKind.StringLiteral: + case TokenKind.SingleQuotedStringLiteral: + case TokenKind.Identifier: + case TokenKind.Integer: + case TokenKind.NotSign: + case TokenKind.NullLiteral: + case TokenKind.Minus: + case TokenKind.Plus: + case TokenKind.True: + case TokenKind.False: + return true; + case TokenKind.CloseParen: + return isParenthesizedCall; + default: + return false; + } } private AstNode ProduceConstant(Token token) { @@ -170,18 +279,15 @@ namespace Orchard.Scripting.Compiler { return new ConstantAstNode(token); } - private AstNode ProduceError(Token token) { + private AstNode UnexpectedTokenError() { + var token = _lexer.Token(); _lexer.NextToken(); - return new ErrorAstNode(token, string.Format("Unexptected Token in primary expression ({0})", token)); + return new ErrorAstNode(token, string.Format("Unexpected token in primary expression ({0})", token)); } - private void Match(TokenKind kind) { + private AstNode ExpectedTokenError(TokenKind tokenKind) { var token = _lexer.Token(); - if (token.Kind == kind) { - _lexer.NextToken(); - return; - } - AddError(token, string.Format("Expected Token {0}", kind)); + return new ErrorAstNode(token, string.Format("Expected token {0}", tokenKind)); } private Token IsMatch(TokenKind kind) { @@ -201,9 +307,5 @@ namespace Orchard.Scripting.Compiler { } return null; } - - private void AddError(Token token, string message) { - _errors.Add(message); - } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/PrimitiveType.cs b/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/PrimitiveType.cs index 8681a86a6..f6bacf4bf 100644 --- a/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/PrimitiveType.cs +++ b/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/PrimitiveType.cs @@ -11,11 +11,15 @@ namespace Orchard.Scripting.Compiler { return IntegerPrimitiveType.Instance; if (value is string) return StringPrimitiveType.Instance; + if (value is Error) + return ErrorPrimitiveType.Instance; throw new InvalidOperationException(string.Format("Scripting engine internal error: no primitive type for value '{0}'", value)); } public abstract EvaluationResult EqualityOperator(EvaluationResult value, EvaluationResult other); public abstract EvaluationResult ComparisonOperator(EvaluationResult value, EvaluationResult other); + public abstract EvaluationResult LogicalAnd(EvaluationResult value, EvaluationResult other); + public abstract EvaluationResult LogicalOr(EvaluationResult value, EvaluationResult other); protected EvaluationResult Result(object value) { return EvaluationResult.Result(value); @@ -42,6 +46,18 @@ namespace Orchard.Scripting.Compiler { public override EvaluationResult ComparisonOperator(EvaluationResult value, EvaluationResult other) { return Error("Boolean values can only be compared to other boolean values"); } + + public override EvaluationResult LogicalAnd(EvaluationResult value, EvaluationResult other) { + if (!value.BoolValue) + return value; + return other; + } + + public override EvaluationResult LogicalOr(EvaluationResult value, EvaluationResult other) { + if (value.BoolValue) + return value; + return other; + } } public class IntegerPrimitiveType : PrimitiveType { @@ -62,6 +78,14 @@ namespace Orchard.Scripting.Compiler { return Result(value.Int32Value.CompareTo(other.Int32Value)); return Error("Integer values can only be compared to other integer values"); } + + public override EvaluationResult LogicalAnd(EvaluationResult value, EvaluationResult other) { + return other; + } + + public override EvaluationResult LogicalOr(EvaluationResult value, EvaluationResult other) { + return value; + } } public class StringPrimitiveType : PrimitiveType { @@ -80,6 +104,14 @@ namespace Orchard.Scripting.Compiler { public override EvaluationResult ComparisonOperator(EvaluationResult value, EvaluationResult other) { return Error("String values can not be compared"); } + + public override EvaluationResult LogicalAnd(EvaluationResult value, EvaluationResult other) { + return other; + } + + public override EvaluationResult LogicalOr(EvaluationResult value, EvaluationResult other) { + return value; + } } public class NullPrimitiveType : PrimitiveType { @@ -96,5 +128,37 @@ namespace Orchard.Scripting.Compiler { public override EvaluationResult ComparisonOperator(EvaluationResult value, EvaluationResult other) { return Error("'null' values can not be compared"); } + + public override EvaluationResult LogicalAnd(EvaluationResult value, EvaluationResult other) { + return value; + } + + public override EvaluationResult LogicalOr(EvaluationResult value, EvaluationResult other) { + return other; + } + } + + public class ErrorPrimitiveType : PrimitiveType { + private static ErrorPrimitiveType _instance; + + public static ErrorPrimitiveType Instance { + get { return _instance ?? (_instance = new ErrorPrimitiveType()); } + } + + public override EvaluationResult EqualityOperator(EvaluationResult value, EvaluationResult other) { + return value; + } + + public override EvaluationResult ComparisonOperator(EvaluationResult value, EvaluationResult other) { + return value; + } + + public override EvaluationResult LogicalAnd(EvaluationResult value, EvaluationResult other) { + return value; + } + + public override EvaluationResult LogicalOr(EvaluationResult value, EvaluationResult other) { + return value; + } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/Token.cs b/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/Token.cs index 39184c5fa..fa2c7ecb1 100644 --- a/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/Token.cs +++ b/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/Token.cs @@ -7,7 +7,7 @@ namespace Orchard.Scripting.Compiler { public object Value { get; set; } public override string ToString() { - return Value == null ? String.Format("Token {0} at position {1}", Kind, Position) : String.Format("Token {0} ({1}) at position {2}", Kind, Value, Position); + return Value == null ? String.Format("Token '{0}' at position {1}", Kind, Position) : String.Format("Token '{0}' ({1}) at position {2}", Kind, Value, Position); } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/TokenKind.cs b/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/TokenKind.cs index be7e38a63..da2f80879 100644 --- a/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/TokenKind.cs +++ b/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/TokenKind.cs @@ -16,11 +16,13 @@ True, False, And, + AndSign, Or, + OrSign, Not, + NotSign, Equal, EqualEqual, - NotSign, NotEqual, LessThan, LessThanEqual, diff --git a/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/Tokenizer.cs b/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/Tokenizer.cs index 2acd53175..c12f14106 100644 --- a/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/Tokenizer.cs +++ b/src/Orchard.Web/Modules/Orchard.Scripting/Compiler/Tokenizer.cs @@ -48,6 +48,10 @@ namespace Orchard.Scripting.Compiler { return LexSingleQuotedStringLiteral(); case '!': return LexNotSign(); + case '|': + return LexOrSign(); + case '&': + return LexAndSign(); case '=': return LexEqual(); case '<': @@ -67,10 +71,14 @@ namespace Orchard.Scripting.Compiler { continue; } - return CreateToken(TokenKind.Invalid, "Unrecognized character"); + return InvalidToken(); } } + private Token InvalidToken() { + return CreateToken(TokenKind.Invalid, "Unrecognized character"); + } + private Token LexNotSign() { NextCharacter(); char ch = Character(); @@ -81,6 +89,26 @@ namespace Orchard.Scripting.Compiler { return CreateToken(TokenKind.NotSign); } + private Token LexOrSign() { + NextCharacter(); + char ch = Character(); + if (ch == '|') { + NextCharacter(); + return CreateToken(TokenKind.OrSign); + } + return InvalidToken(); + } + + private Token LexAndSign() { + NextCharacter(); + char ch = Character(); + if (ch == '&') { + NextCharacter(); + return CreateToken(TokenKind.AndSign); + } + return InvalidToken(); + } + private Token LexGreaterThan() { NextCharacter(); char ch = Character();