Replace dlr scripting implementation with the new lightweight engine

DLR scripting engine still available in new Orchard.Scripting.Dlr module

--HG--
branch : dev
rename : src/Orchard.Web/Modules/Orchard.Scripting/ScriptingManager.cs => src/Orchard.Web/Modules/Orchard.Scripting/ScriptExpressionEvaluator.cs
This commit is contained in:
Renaud Paquay
2010-11-28 12:03:11 -08:00
parent 14dd1c754f
commit f96abb981a
11 changed files with 159 additions and 107 deletions

View File

@@ -2,30 +2,24 @@
using NUnit.Framework;
using Orchard.Scripting;
using Orchard.Tests.Stubs;
using Orchard.Widgets.Services;
namespace Orchard.Tests.Modules.Scripting {
[TestFixture]
public class SimpleScriptingTests {
[Test]
public void EngineThrowsSyntaxErrors() {
var engine = new ScriptingEngine(Enumerable.Empty<IGlobalMethodProvider>(), new StubCacheManager());
Assert.That(() => engine.Matches("true+"), Throws.Exception);
}
[Test]
public void EngineThrowsEvalErrors() {
var engine = new ScriptingEngine(Enumerable.Empty<IGlobalMethodProvider>(), new StubCacheManager());
Assert.That(() => engine.Matches("1 + 1"), Throws.Exception);
var engine = new ScriptExpressionEvaluator(new StubCacheManager());
Assert.That(() => engine.Evaluate("true+", Enumerable.Empty<IGlobalMethodProvider>()), Throws.Exception);
}
[Test]
public void EngineUnderstandsPrimitiveValues() {
var engine = new ScriptingEngine(Enumerable.Empty<IGlobalMethodProvider>(), new StubCacheManager());
Assert.That(engine.Matches("true"), Is.True);
var engine = new ScriptExpressionEvaluator(new StubCacheManager());
Assert.That(engine.Evaluate("true", Enumerable.Empty<IGlobalMethodProvider>()), Is.True);
}
[Test]
public void EngineUnderstandsPrimitiveValues2() {
var engine = new ScriptingEngine(Enumerable.Empty<IGlobalMethodProvider>(), new StubCacheManager());
Assert.That(engine.Matches("true and true"), Is.True);
var engine = new ScriptExpressionEvaluator(new StubCacheManager());
Assert.That(engine.Evaluate("true and true", Enumerable.Empty<IGlobalMethodProvider>()), Is.True);
}
}
}

View File

@@ -7,5 +7,6 @@ OrchardVersion: 0.8.0
Description: The DLR scripting module enables the possibility to execute scripts using the DLR.
Features:
Orchard.Scripting.Dlr:
Description: DLR Scripting support.
Description: DLR scripting support.
Dependencies: Orchard.Scripting
Category: Scripting

View File

@@ -62,6 +62,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\RubyScriptExpressionEvaluator.cs" />
<Compile Include="Services\IScriptingManager.cs" />
<Compile Include="Services\IScriptingRuntime.cs" />
<Compile Include="Services\RubyScriptingRuntime.cs" />
@@ -73,6 +74,10 @@
<Name>Orchard.Framework</Name>
<Private>True</Private>
</ProjectReference>
<ProjectReference Include="..\Orchard.Scripting\Orchard.Scripting.csproj">
<Project>{99002B65-86F7-415E-BF4A-381AA8AB9CCC}</Project>
<Name>Orchard.Scripting</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="Module.txt" />

View File

@@ -0,0 +1,71 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.Scripting.Hosting;
using Orchard.Caching;
namespace Orchard.Scripting.Dlr.Services {
public class RubyScriptExpressionEvaluator : IScriptExpressionEvaluator {
private readonly IScriptingManager _scriptingManager;
private readonly ICacheManager _cacheManager;
public RubyScriptExpressionEvaluator(IScriptingManager scriptingManager, ICacheManager cacheManager) {
_scriptingManager = scriptingManager;
_cacheManager = cacheManager;
}
public object Evaluate(string expression, IEnumerable<IGlobalMethodProvider> providers) {
object execContextType = _cacheManager.Get("---", ctx => (object)_scriptingManager.ExecuteExpression(@"
class ExecBlock
def initialize(callbacks)
@callbacks = callbacks
end
def method_missing(name, *args, &block)
@callbacks.send(name, args, &block);
end
end
class ExecContext
class << self
def alloc(thing)
instance_eval 'self.new {' + thing + '}'
end
end
def initialize(&block)
@block = block
end
def evaluate(callbacks)
ExecBlock.new(callbacks).instance_eval(&@block)
end
end
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 result;
}
public class CallbackApi {
private readonly RubyScriptExpressionEvaluator _ruleManager;
private readonly IEnumerable<IGlobalMethodProvider> _providers;
public CallbackApi(RubyScriptExpressionEvaluator ruleManager, IEnumerable<IGlobalMethodProvider> providers) {
_ruleManager = ruleManager;
_providers = providers;
}
public object send(string name, IList<object> args) {
return _ruleManager.Evaluate(_providers, name, args);
}
}
private object Evaluate(IEnumerable<IGlobalMethodProvider> providers, string name, IList<object> args) {
GlobalMethodContext ruleContext = new GlobalMethodContext { FunctionName = name, Arguments = args.ToArray() };
foreach (var ruleProvider in providers) {
ruleProvider.Process(ruleContext);
}
return ruleContext.Result;
}
}
}

View File

@@ -0,0 +1,13 @@
using System.Collections.Generic;
namespace Orchard.Scripting {
public interface IGlobalMethodProvider {
void Process(GlobalMethodContext context);
}
public class GlobalMethodContext {
public string FunctionName { get; set; }
public IList<object> Arguments { get; set; }
public object Result { get; set; }
}
}

View File

@@ -1,5 +1,7 @@

using System.Collections.Generic;
namespace Orchard.Scripting {
public interface IScriptExpressionEvaluator : ISingletonDependency {
object Evaluate(string expression, IEnumerable<IGlobalMethodProvider> providers);
}
}

View File

@@ -6,6 +6,11 @@ Version: 0.8.0
OrchardVersion: 0.8.0
Description: The scripting module enables the possibility to execute scripts using a simple custom scripting language.
Features:
Orchard.Scripting.:
Description: Simple scripting support.
Orchard.Scripting:
Description: Scripting support.
Category: Scripting
Orchard.Scripting.Lightweight:
Name: Lightweight scripting
Description: Custom lightweight and simple scripting language.
Dependencies: Orchard.Scripting
Category: Scripting

View File

@@ -54,6 +54,7 @@
<Compile Include="Ast\IAstNodeWithToken.cs" />
<Compile Include="Ast\MethodCallAstNode.cs" />
<Compile Include="Ast\UnaryAstNode.cs" />
<Compile Include="IGlobalMethodProvider.cs" />
<Compile Include="IScriptExpressionEvaluator.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Compiler\Interpreter.cs" />
@@ -63,7 +64,7 @@
<Compile Include="Compiler\Token.cs" />
<Compile Include="Compiler\Tokenizer.cs" />
<Compile Include="Compiler\TokenKind.cs" />
<Compile Include="ScriptingManager.cs" />
<Compile Include="ScriptExpressionEvaluator.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Orchard\Orchard.Framework.csproj">

View File

@@ -2,38 +2,24 @@
using System.Collections.Generic;
using System.Linq;
using Orchard.Caching;
using Orchard.Environment.Extensions;
using Orchard.Localization;
using Orchard.Scripting.Ast;
using Orchard.Scripting.Compiler;
namespace Orchard.Scripting {
public class GlobalMethodContext {
public string FunctionName { get; set; }
public IList<object> Arguments { get; set; }
public object Result { get; set; }
}
public interface IGlobalMethodProvider {
object Process(GlobalMethodContext context);
}
public interface IScriptingEngine : IDependency {
bool Matches(string expression);
}
public class ScriptingEngine : IScriptingEngine {
private readonly IEnumerable<IGlobalMethodProvider> _ruleProviders;
[OrchardFeature("Orchard.Scripting.Lightweight")]
public class ScriptExpressionEvaluator : IScriptExpressionEvaluator {
private readonly ICacheManager _cacheManager;
public ScriptingEngine(IEnumerable<IGlobalMethodProvider> ruleProviders, ICacheManager cacheManager) {
_ruleProviders = ruleProviders;
public ScriptExpressionEvaluator(ICacheManager cacheManager) {
_cacheManager = cacheManager;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public bool Matches(string expression) {
public object Evaluate(string expression, IEnumerable<IGlobalMethodProvider> providers) {
var expr = _cacheManager.Get(expression, ctx => {
var ast = ParseExpression(expression);
return new { Tree = ast, Errors = ast.GetErrors().ToList() };
@@ -44,38 +30,38 @@ namespace Orchard.Scripting {
throw new OrchardException(T("Syntax error: {0}", expr.Errors.First().Message));
}
var result = EvaluateExpression(expr.Tree);
var result = EvaluateExpression(expr.Tree, providers);
if (result.IsError) {
throw new ApplicationException(result.Error.Message);
}
if (!result.IsBool) {
throw new OrchardException(T("Expression is not a boolean value"));
}
return result.BoolValue;
return result.Value;
}
private AbstractSyntaxTree ParseExpression(string expression) {
return new Parser(expression).Parse();
}
private EvaluationResult EvaluateExpression(AbstractSyntaxTree tree) {
private EvaluationResult EvaluateExpression(AbstractSyntaxTree tree, IEnumerable<IGlobalMethodProvider> providers) {
var context = new EvaluationContext {
Tree = tree,
MethodInvocationCallback = (m, args) => Evaluate(m, args)
MethodInvocationCallback = (m, args) => Evaluate(providers, m, args)
};
return new Interpreter().Evalutate(context);
}
private object Evaluate(string name, IEnumerable<object> args) {
var ruleContext = new GlobalMethodContext() { FunctionName = name, Arguments = args.ToArray() };
private object Evaluate(IEnumerable<IGlobalMethodProvider> globalMethodProviders, string name, IEnumerable<object> args) {
var globalMethodContext = new GlobalMethodContext {
FunctionName = name,
Arguments = args.ToArray(),
Result = null
};
foreach (var ruleProvider in _ruleProviders) {
ruleProvider.Process(ruleContext);
foreach (var globalMethodProvider in globalMethodProviders) {
globalMethodProvider.Process(globalMethodContext);
}
return ruleContext.Result;
return globalMethodContext.Result;
}
}
}

View File

@@ -35,10 +35,6 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Scripting, Version=1.1.0.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\lib\dlr\Microsoft.Scripting.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
@@ -100,10 +96,6 @@
<Name>Orchard.Framework</Name>
<Private>True</Private>
</ProjectReference>
<ProjectReference Include="..\Orchard.Scripting.Dlr\Orchard.Scripting.Dlr.csproj">
<Project>{2AD6973D-C7BB-416E-89FE-EEE34664E05F}</Project>
<Name>Orchard.Scripting.Dlr</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.Scripting\Orchard.Scripting.csproj">
<Project>{99002B65-86F7-415E-BF4A-381AA8AB9CCC}</Project>
<Name>Orchard.Scripting</Name>

View File

@@ -1,73 +1,55 @@
using System.Collections.Generic;
using System.Linq;
using Orchard.Scripting.Dlr.Services;
using Orchard.Localization;
using Orchard.Scripting;
using Orchard.Widgets.Services;
using Microsoft.Scripting.Hosting;
using Orchard.Caching;
namespace Orchard.Widgets.RuleEngine {
public class RuleManager : IRuleManager {
private readonly IEnumerable<IRuleProvider> _ruleProviders;
private readonly IScriptingManager _scriptingManager;
private readonly ICacheManager _cacheManager;
private readonly IEnumerable<IScriptExpressionEvaluator> _evaluators;
public RuleManager(IEnumerable<IRuleProvider> ruleProviders, IScriptingManager scriptingManager, ICacheManager cacheManager) {
public RuleManager(IEnumerable<IRuleProvider> ruleProviders, IEnumerable<IScriptExpressionEvaluator> evaluators) {
_ruleProviders = ruleProviders;
_scriptingManager = scriptingManager;
_cacheManager = cacheManager;
_evaluators = evaluators;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public bool Matches(string expression) {
object execContextType = _cacheManager.Get("---", ctx => (object)_scriptingManager.ExecuteExpression(@"
class ExecBlock
def initialize(callbacks)
@callbacks = callbacks
end
def method_missing(name, *args, &block)
@callbacks.send(name, args, &block);
end
end
class ExecContext
class << self
def alloc(thing)
instance_eval 'self.new {' + thing + '}'
end
end
def initialize(&block)
@block = block
end
def evaluate(callbacks)
ExecBlock.new(callbacks).instance_eval(&@block)
end
end
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));
return result;
var evaluator = _evaluators.FirstOrDefault();
if (evaluator == null) {
throw new OrchardException(T("There are currently not scripting engine enabled"));
}
var result = evaluator.Evaluate(expression, new List<IGlobalMethodProvider> { new GlobalMethodProvider(this) });
if (!(result is bool)) {
throw new OrchardException(T("Expression is not a boolean value"));
}
return (bool)result;
}
public class CallbackApi {
private class GlobalMethodProvider : IGlobalMethodProvider {
private readonly RuleManager _ruleManager;
public CallbackApi(RuleManager ruleManager) {
public GlobalMethodProvider(RuleManager ruleManager) {
_ruleManager = ruleManager;
}
public object send(string name, IList<object> args) {
return _ruleManager.Evaluate(name, args);
public void Process(GlobalMethodContext context) {
var ruleContext = new RuleContext {
FunctionName = context.FunctionName,
Arguments = context.Arguments.ToArray(),
Result = context.Result
};
foreach(var ruleProvider in _ruleManager._ruleProviders) {
ruleProvider.Process(ruleContext);
}
context.Result = ruleContext.Result;
}
}
private object Evaluate(string name, IList<object> args) {
RuleContext ruleContext = new RuleContext { FunctionName = name, Arguments = args.ToArray() };
foreach (var ruleProvider in _ruleProviders) {
ruleProvider.Process(ruleContext);
}
return ruleContext.Result;
}
}
}