Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions HowSimpleflowWorks.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@startuml HowSimpleflowWorks

skinparam partition {
BorderColor #dddddd
BorderThickness 1
FontColor grey
RoundCorner 10
}

skinparam component {
FontSize 15
FontName Courier
BorderColor black
BackgroundColor white
ArrowFontName Impact
ArrowColor #1d3ddb
}
: Input (Script, Argument);
: Determine cache key of the given script: \n Get cache key based on hash of the script \n or with the user supplied cache id;

if (if the function is\n avilable in cache?) then (yes)
:Get function from cache;
else (no)
partition Compile {
: Parse;
: Generate IL code;
: Compile as a function\n with an input parameter;
: Store in cache;
}
endif
:Execute function (with given argument);
:Output;

@enduml
19 changes: 19 additions & 0 deletions src/Simpleflow/ArgumentInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) navtech.io. All rights reserved.
// See License in the project root for license information.

using System;

namespace Simpleflow
{
/// <summary>
/// Represents argument information of a function
/// </summary>
public class ArgumentInfo
{
/// <summary>
/// Gets or sets name of argument
/// </summary>
public string ArgumentName { get; set; }

}
}
2 changes: 2 additions & 0 deletions src/Simpleflow/CodeGenerator/FastExpressionCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@ namespace FastExpressionCompiler
{
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;

/// <summary>Compiles expression to delegate ~20 times faster than Expression.Compile.
/// Partial to extend with your things when used as source file.</summary>
[ExcludeFromCodeCoverage]
internal static partial class ExpressionCompiler
{
#region Obsolete APIs
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// Copyright (c) navtech.io. All rights reserved.
// See License in the project root for license information.

using System;
using System.Linq;
using System.Collections.Generic;
using System.Linq.Expressions;
using Antlr4.Runtime.Misc;
using Antlr4.Runtime.Tree;
Expand All @@ -26,6 +25,9 @@ public override Expression VisitRelationalExpression([NotNull] SimpleflowParser.
case SimpleflowLexer.Equal:
return Expression.Equal(left, right);

case SimpleflowLexer.In:
return InOperatorExpression(left, right);

case SimpleflowLexer.NotEqual:
return Expression.NotEqual(left, right);

Expand Down Expand Up @@ -70,5 +72,20 @@ public override Expression VisitNotExpression([NotNull] SimpleflowParser.NotExpr
{
return Expression.Not( HandleNonBooleanExpression( Visit(context.expression()) ));
}

private Expression InOperatorExpression(Expression left, Expression right)
{
if (right.Type.GenericTypeArguments.Length == 0
|| right.Type != typeof(List<>).MakeGenericType(right.Type.GenericTypeArguments[0]))
{
throw new Exceptions.SimpleflowException(Resources.Message.InOperatorOnList);
}

return Expression.Call(
right,
right.Type.GetMethod("Contains", right.Type.GenericTypeArguments),
left
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,13 @@ public override Expression VisitFunction([NotNull] SimpleflowParser.FunctionCont
{
var functionName = context.FunctionName().GetText().Substring(1); // Remove $ symbol

var argumentNames = context.functionArguments()
.functionArgument()
.Select(arg => new ArgumentInfo { ArgumentName = arg.Identifier().GetText() })
.ToArray();

// Get registered function
var function = FunctionRegister.GetFunction(functionName);
var function = FunctionRegister.GetFunction(functionName, argumentNames);

if (function == null)
{
Expand All @@ -35,7 +40,7 @@ public override Expression VisitFunction([NotNull] SimpleflowParser.FunctionCont
EventPublisher.Publish(EventType.VisitFunctionOnAvail, functionName);

// Get actual method meta-data info and parameters
var methodInfo = function.GetMethodInfo();
var methodInfo = function.Reference.GetMethodInfo();

// Map script arguments to method parameters
var argumentsExpressions = GetArgumentExpressions(context, methodInfo);
Expand Down
12 changes: 12 additions & 0 deletions src/Simpleflow/FunctionPointer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) navtech.io. All rights reserved.
// See License in the project root for license information.

using System;

namespace Simpleflow
{
public class FunctionPointer
{
public Delegate Reference { get; set; }
}
}
3 changes: 2 additions & 1 deletion src/Simpleflow/FunctionRegister.BuiltIn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public static FunctionRegister Default
.Add("IndexOf", (Func<string, string, int, int>)StringFunctions.IndexOf)
.Add("Length", (Func<string, int>)StringFunctions.Length)
.Add("Match", (Func<string, string, bool>)StringFunctions.Match)
.Add("Concat", (Func<string, string, string, string, string, string>)StringFunctions.Concat);
.Add("Concat", (Func<string, string, string, string, string, string>)StringFunctions.Concat)
;
}
}

Expand Down
125 changes: 59 additions & 66 deletions src/Simpleflow/FunctionRegister.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,35 @@ namespace Simpleflow
/// </summary>
public partial class FunctionRegister : IFunctionRegister // ,IActivityInvoker
{
// Key is name, and value is index, an index represents block (type store/method store)
// and index of store. 30th bit represents block, and rest of them as index (2**29) in block
// block - 1 represents _typeStore and 0 represents _methodStore
// Key is name, and value is index, an index represents block (type store/method store/function provider Store)
// and index of store. 29th and 30th bit represent block/store, and rest of them as index (2**28) in block
// block - 0 represents _methodStore
// block - 1 represents _providerStore
// block - 2 represents ..unused..
// block - 3 represents ..unused..

private const int MethodStoreIndex = 0;
private const int ProviderStoreIndex = 1;

private readonly Dictionary<string, int> _bitmapIndex =
new Dictionary<string, int>(StringComparer.InvariantCultureIgnoreCase);

private readonly List<Type> _typeStore = new List<Type>();
private readonly List<Delegate> _methodStore = new List<Delegate>();
private readonly List<Delegate> _methodStore = new List<Delegate>();
private readonly List<IFunctionProvider> _providerStore = new List<IFunctionProvider>();

private readonly object _sync = new object();

/// <summary>
///
/// </summary>
/// <param name="name"></param>
/// <param name="activity"></param>
/// <returns></returns>
///
// Need to enable this later once this feature is implemented
private IFunctionRegister Add(string name, Type activity)

public IFunctionRegister Add(string name, Delegate @delegate)
{
// TODO : able to invoke methods of the Type
// find all methods and add it
// let customer = $customer.new()
// $MethodName (p1: value, ...) on customer

ValidateFunctionName(name);

//Allow only static methods
if (@delegate.Target != null)
{
throw new SimpleflowException(Resources.Message.RegisterNonStaticMethodError);
}

if (_bitmapIndex.ContainsKey(name))
{
throw new DuplicateFunctionException(name);
Expand All @@ -51,33 +51,26 @@ private IFunctionRegister Add(string name, Type activity)
int index;
lock (_sync)
{
Debug.Assert(_bitmapIndex.Count == _typeStore.Count + _methodStore.Count);
Debug.Assert(_bitmapIndex.Count == _methodStore.Count + _providerStore.Count);

_typeStore.Add(activity);
index = _typeStore.Count - 1;
_methodStore.Add(@delegate);
index = _methodStore.Count - 1;
}

_bitmapIndex.Add(name, 1 << 29 | index);
_bitmapIndex.Add(name, CreateBitmapIndex(storeIndex: MethodStoreIndex, valueIndex: index));

return this;
}

/// <summary>
///
/// </summary>
/// <param name="name"></param>
/// <param name="delegate"></param>
/// <returns></returns>
public IFunctionRegister Add(string name, Delegate @delegate)

public IFunctionRegister Add(string name, IFunctionProvider functionProvider)
{
//Allow only static methods
if (@delegate.Target != null)
ValidateFunctionName(name);

if (functionProvider == null)
{
throw new SimpleflowException(Resources.Message.RegisterNonStaticMethodError);
throw new ArgumentNullException(nameof(functionProvider));
}

ValidateFunctionName(name);

if (_bitmapIndex.ContainsKey(name))
{
throw new DuplicateFunctionException(name);
Expand All @@ -86,61 +79,61 @@ public IFunctionRegister Add(string name, Delegate @delegate)
int index;
lock (_sync)
{
Debug.Assert(_bitmapIndex.Count == _typeStore.Count + _methodStore.Count);
Debug.Assert(_bitmapIndex.Count == _methodStore.Count + _providerStore.Count);

_methodStore.Add(@delegate);
index = _methodStore.Count - 1;
_providerStore.Add(functionProvider);
index = _providerStore.Count - 1;
}
// 0 << 29 | index , since 0 << 29 is 0,
// so here we don't need to use bitwise operation to add up together
_bitmapIndex.Add(name, index);
_bitmapIndex.Add(name, CreateBitmapIndex(storeIndex: ProviderStoreIndex, valueIndex: index));

return this;
}


/// <inheritdoc />
public Delegate GetFunction(string name)
public FunctionPointer GetFunction(string name, ArgumentInfo[] argumentInfo) // ArgumentInfo
{
if (_bitmapIndex.ContainsKey(name))
{
var bitmapIndex = _bitmapIndex[name];

var firstBit = bitmapIndex >> 29;
var index = (firstBit << 29) ^ bitmapIndex;

// ReSharper disable once InconsistentlySynchronizedField
return firstBit == 1 ? null : _methodStore[index];
(int storeIndex, int valueIndex) = GetStoreAndValueIndex(bitmapIndex);

return storeIndex switch
{
MethodStoreIndex => new FunctionPointer { Reference = _methodStore[valueIndex] },
ProviderStoreIndex => _providerStore[valueIndex].GetFunction(name, argumentInfo),
_ => null
};
}
return null;
}

/// <summary>
///
/// </summary>
/// <param name="name"></param>
/// <returns></returns>

private bool? IsFunctionAvailableInClass(string name)
private (int storeIndex, int valueIndex) GetStoreAndValueIndex(int bitmapIndex)
{
if (_bitmapIndex.ContainsKey(name))
{
var bitmapIndex = _bitmapIndex[name];
var firstBit = bitmapIndex >> 29;
var storeIndex = bitmapIndex >> 28;
var valueIndex = (storeIndex << 28) ^ bitmapIndex;

return firstBit == 1;
}
return false;
return (storeIndex, valueIndex);
}

private int CreateBitmapIndex(int storeIndex, int valueIndex)
{
return storeIndex << 28 | valueIndex;
}

private void ValidateFunctionName(string name)
{
var pattern = "^[_]*[a-zA-Z][_a-zA-Z0-9]*([.][_]*[a-zA-Z][_a-zA-Z0-9]*)*$";
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentNullException(nameof(name));
}

if (!System.Text.RegularExpressions.Regex.Match(name,pattern).Success)
var pattern = "^[_]*[a-zA-Z][_a-zA-Z0-9]*([.][_]*[a-zA-Z][_a-zA-Z0-9]*)*$";

if (!System.Text.RegularExpressions.Regex.Match(name, pattern).Success)
{
throw new InvalidFunctionNameException(name);
}
}

}
}
20 changes: 20 additions & 0 deletions src/Simpleflow/IFunctionProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) navtech.io. All rights reserved.
// See License in the project root for license information.

namespace Simpleflow
{
/// <summary>
/// Use to provide appropriate delegate based on name
/// and argument information
/// </summary>
public interface IFunctionProvider
{
/// <summary>
/// Get function reference to invoke
/// </summary>
/// <param name="name"></param>
/// <param name="argumentInfo"></param>
/// <returns></returns>
FunctionPointer GetFunction(string name, ArgumentInfo[] argumentInfo);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,15 @@ public interface IFunctionRegister
/// <returns></returns>
IFunctionRegister Add(string name, Delegate @delegate);


IFunctionRegister Add(string name, IFunctionProvider provider);

/// <summary>
///
/// </summary>
/// <param name="name"></param>
/// <param name="argumentInfo"></param>
/// <returns></returns>
Delegate GetFunction(string name);
FunctionPointer GetFunction(string name, ArgumentInfo[] argumentInfo);
}
}
Loading