mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-09-24 21:43:37 +08:00
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:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
@@ -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" />
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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; }
|
||||
}
|
||||
}
|
@@ -1,5 +1,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Orchard.Scripting {
|
||||
public interface IScriptExpressionEvaluator : ISingletonDependency {
|
||||
object Evaluate(string expression, IEnumerable<IGlobalMethodProvider> providers);
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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">
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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>
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user