mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-14 10:54:50 +08:00
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
This commit is contained in:
@@ -138,6 +138,8 @@
|
||||
<Compile Include="Comments\Services\CommentServiceTests.cs" />
|
||||
<Compile Include="Indexing\LuceneIndexProviderTests.cs" />
|
||||
<Compile Include="Indexing\LuceneSearchBuilderTests.cs" />
|
||||
<Compile Include="Scripting.Dlr\EvaluatorTests.cs" />
|
||||
<Compile Include="Scripting\EvaluatorTestsBase.cs" />
|
||||
<Compile Include="Scripting\EvaluatorTests.cs" />
|
||||
<Compile Include="Scripting\ParserTests.cs" />
|
||||
<Compile Include="Scripting\TokenizerTests.cs" />
|
||||
|
50
src/Orchard.Tests.Modules/Scripting.Dlr/EvaluatorTests.cs
Normal file
50
src/Orchard.Tests.Modules/Scripting.Dlr/EvaluatorTests.cs
Normal file
@@ -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<RubyScriptingRuntime>().As<IScriptingRuntime>();
|
||||
builder.RegisterType<ScriptingManager>().As<IScriptingManager>();
|
||||
_container = builder.Build();
|
||||
_scriptingManager = _container.Resolve<IScriptingManager>();
|
||||
}
|
||||
|
||||
protected override EvaluationResult EvaluateSimpleExpression(string expression, Func<string, IList<object>, 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<string, IList<object>, object> _methodInvocationCallback;
|
||||
|
||||
public GlobalMethodProvider(Func<string, IList<object>, object> methodInvocationCallback) {
|
||||
_methodInvocationCallback = methodInvocationCallback;
|
||||
}
|
||||
|
||||
public void Process(GlobalMethodContext context) {
|
||||
context.Result = _methodInvocationCallback(context.FunctionName, context.Arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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<string, IList<object>, object> methodInvocationCallback) {
|
||||
|
||||
public class EvaluatorTests : EvaluatorTestsBase {
|
||||
protected override EvaluationResult EvaluateSimpleExpression(string expression, Func<string, IList<object>, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
527
src/Orchard.Tests.Modules/Scripting/EvaluatorTestsBase.cs
Normal file
527
src/Orchard.Tests.Modules/Scripting/EvaluatorTestsBase.cs
Normal file
@@ -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<string, IList<object>, object> methodInvocationCallback);
|
||||
}
|
||||
}
|
@@ -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,
|
||||
|
@@ -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<IGlobalMethodProvider> providers, string name, IList<object> args) {
|
||||
GlobalMethodContext ruleContext = new GlobalMethodContext { FunctionName = name, Arguments = args.ToArray() };
|
||||
private object Evaluate(IEnumerable<IGlobalMethodProvider> providers, string name, IEnumerable<object> args) {
|
||||
var ruleContext = new GlobalMethodContext {
|
||||
FunctionName = name,
|
||||
Arguments = args.Select(v => ConvertRubyValue(v)).ToArray()
|
||||
};
|
||||
|
||||
foreach (var ruleProvider in providers) {
|
||||
ruleProvider.Process(ruleContext);
|
||||
|
@@ -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);
|
||||
|
@@ -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<EvaluationResult, EvaluationResult, EvaluationResult> 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) {
|
||||
|
@@ -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<string> _errors = new List<string>();
|
||||
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<AstNode>());
|
||||
}
|
||||
|
||||
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<AstNode>();
|
||||
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<AstNode>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -16,11 +16,13 @@
|
||||
True,
|
||||
False,
|
||||
And,
|
||||
AndSign,
|
||||
Or,
|
||||
OrSign,
|
||||
Not,
|
||||
NotSign,
|
||||
Equal,
|
||||
EqualEqual,
|
||||
NotSign,
|
||||
NotEqual,
|
||||
LessThan,
|
||||
LessThanEqual,
|
||||
|
@@ -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();
|
||||
|
Reference in New Issue
Block a user