Skip to content

Commit 31cedcb

Browse files
authored
Merge pull request #27 from navtech-io/develop
Support 'in' relational operator and, improve internal code
2 parents f79c553 + 866ab1c commit 31cedcb

29 files changed

+690
-399
lines changed

HowSimpleflowWorks.puml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
@startuml HowSimpleflowWorks
2+
3+
skinparam partition {
4+
BorderColor #dddddd
5+
BorderThickness 1
6+
FontColor grey
7+
RoundCorner 10
8+
}
9+
10+
skinparam component {
11+
FontSize 15
12+
FontName Courier
13+
BorderColor black
14+
BackgroundColor white
15+
ArrowFontName Impact
16+
ArrowColor #1d3ddb
17+
}
18+
: Input (Script, Argument);
19+
: 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;
20+
21+
if (if the function is\n avilable in cache?) then (yes)
22+
:Get function from cache;
23+
else (no)
24+
partition Compile {
25+
: Parse;
26+
: Generate IL code;
27+
: Compile as a function\n with an input parameter;
28+
: Store in cache;
29+
}
30+
endif
31+
:Execute function (with given argument);
32+
:Output;
33+
34+
@enduml

src/Simpleflow/ArgumentInfo.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) navtech.io. All rights reserved.
2+
// See License in the project root for license information.
3+
4+
using System;
5+
6+
namespace Simpleflow
7+
{
8+
/// <summary>
9+
/// Represents argument information of a function
10+
/// </summary>
11+
public class ArgumentInfo
12+
{
13+
/// <summary>
14+
/// Gets or sets name of argument
15+
/// </summary>
16+
public string ArgumentName { get; set; }
17+
18+
}
19+
}

src/Simpleflow/CodeGenerator/FastExpressionCompiler.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@ namespace FastExpressionCompiler
2626
{
2727
using System;
2828
using System.Collections.Generic;
29+
using System.Diagnostics.CodeAnalysis;
2930
using System.Linq;
3031
using System.Linq.Expressions;
3132
using System.Reflection;
3233
using System.Reflection.Emit;
3334

3435
/// <summary>Compiles expression to delegate ~20 times faster than Expression.Compile.
3536
/// Partial to extend with your things when used as source file.</summary>
37+
[ExcludeFromCodeCoverage]
3638
internal static partial class ExpressionCompiler
3739
{
3840
#region Obsolete APIs

src/Simpleflow/CodeGenerator/SimpleflowCodeVisitor.VisitExpression.Predicate.cs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
// Copyright (c) navtech.io. All rights reserved.
22
// See License in the project root for license information.
33

4-
using System;
5-
using System.Linq;
4+
using System.Collections.Generic;
65
using System.Linq.Expressions;
76
using Antlr4.Runtime.Misc;
87
using Antlr4.Runtime.Tree;
@@ -26,6 +25,9 @@ public override Expression VisitRelationalExpression([NotNull] SimpleflowParser.
2625
case SimpleflowLexer.Equal:
2726
return Expression.Equal(left, right);
2827

28+
case SimpleflowLexer.In:
29+
return InOperatorExpression(left, right);
30+
2931
case SimpleflowLexer.NotEqual:
3032
return Expression.NotEqual(left, right);
3133

@@ -70,5 +72,20 @@ public override Expression VisitNotExpression([NotNull] SimpleflowParser.NotExpr
7072
{
7173
return Expression.Not( HandleNonBooleanExpression( Visit(context.expression()) ));
7274
}
75+
76+
private Expression InOperatorExpression(Expression left, Expression right)
77+
{
78+
if (right.Type.GenericTypeArguments.Length == 0
79+
|| right.Type != typeof(List<>).MakeGenericType(right.Type.GenericTypeArguments[0]))
80+
{
81+
throw new Exceptions.SimpleflowException(Resources.Message.InOperatorOnList);
82+
}
83+
84+
return Expression.Call(
85+
right,
86+
right.Type.GetMethod("Contains", right.Type.GenericTypeArguments),
87+
left
88+
);
89+
}
7390
}
7491
}

src/Simpleflow/CodeGenerator/SimpleflowCodeVisitor.VisitFunction.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,13 @@ public override Expression VisitFunction([NotNull] SimpleflowParser.FunctionCont
2323
{
2424
var functionName = context.FunctionName().GetText().Substring(1); // Remove $ symbol
2525

26+
var argumentNames = context.functionArguments()
27+
.functionArgument()
28+
.Select(arg => new ArgumentInfo { ArgumentName = arg.Identifier().GetText() })
29+
.ToArray();
30+
2631
// Get registered function
27-
var function = FunctionRegister.GetFunction(functionName);
32+
var function = FunctionRegister.GetFunction(functionName, argumentNames);
2833

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

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

4045
// Map script arguments to method parameters
4146
var argumentsExpressions = GetArgumentExpressions(context, methodInfo);

src/Simpleflow/FunctionPointer.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright (c) navtech.io. All rights reserved.
2+
// See License in the project root for license information.
3+
4+
using System;
5+
6+
namespace Simpleflow
7+
{
8+
public class FunctionPointer
9+
{
10+
public Delegate Reference { get; set; }
11+
}
12+
}

src/Simpleflow/FunctionRegister.BuiltIn.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ public static FunctionRegister Default
3838
.Add("IndexOf", (Func<string, string, int, int>)StringFunctions.IndexOf)
3939
.Add("Length", (Func<string, int>)StringFunctions.Length)
4040
.Add("Match", (Func<string, string, bool>)StringFunctions.Match)
41-
.Add("Concat", (Func<string, string, string, string, string, string>)StringFunctions.Concat);
41+
.Add("Concat", (Func<string, string, string, string, string, string>)StringFunctions.Concat)
42+
;
4243
}
4344
}
4445

src/Simpleflow/FunctionRegister.cs

Lines changed: 59 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,35 @@ namespace Simpleflow
1414
/// </summary>
1515
public partial class FunctionRegister : IFunctionRegister // ,IActivityInvoker
1616
{
17-
// Key is name, and value is index, an index represents block (type store/method store)
18-
// and index of store. 30th bit represents block, and rest of them as index (2**29) in block
19-
// block - 1 represents _typeStore and 0 represents _methodStore
17+
// Key is name, and value is index, an index represents block (type store/method store/function provider Store)
18+
// and index of store. 29th and 30th bit represent block/store, and rest of them as index (2**28) in block
19+
// block - 0 represents _methodStore
20+
// block - 1 represents _providerStore
21+
// block - 2 represents ..unused..
22+
// block - 3 represents ..unused..
23+
24+
private const int MethodStoreIndex = 0;
25+
private const int ProviderStoreIndex = 1;
2026

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

24-
private readonly List<Type> _typeStore = new List<Type>();
25-
private readonly List<Delegate> _methodStore = new List<Delegate>();
30+
private readonly List<Delegate> _methodStore = new List<Delegate>();
31+
private readonly List<IFunctionProvider> _providerStore = new List<IFunctionProvider>();
2632

2733
private readonly object _sync = new object();
2834

29-
/// <summary>
30-
///
31-
/// </summary>
32-
/// <param name="name"></param>
33-
/// <param name="activity"></param>
34-
/// <returns></returns>
35-
///
36-
// Need to enable this later once this feature is implemented
37-
private IFunctionRegister Add(string name, Type activity)
35+
36+
public IFunctionRegister Add(string name, Delegate @delegate)
3837
{
39-
// TODO : able to invoke methods of the Type
40-
// find all methods and add it
41-
// let customer = $customer.new()
42-
// $MethodName (p1: value, ...) on customer
43-
4438
ValidateFunctionName(name);
4539

40+
//Allow only static methods
41+
if (@delegate.Target != null)
42+
{
43+
throw new SimpleflowException(Resources.Message.RegisterNonStaticMethodError);
44+
}
45+
4646
if (_bitmapIndex.ContainsKey(name))
4747
{
4848
throw new DuplicateFunctionException(name);
@@ -51,33 +51,26 @@ private IFunctionRegister Add(string name, Type activity)
5151
int index;
5252
lock (_sync)
5353
{
54-
Debug.Assert(_bitmapIndex.Count == _typeStore.Count + _methodStore.Count);
54+
Debug.Assert(_bitmapIndex.Count == _methodStore.Count + _providerStore.Count);
5555

56-
_typeStore.Add(activity);
57-
index = _typeStore.Count - 1;
56+
_methodStore.Add(@delegate);
57+
index = _methodStore.Count - 1;
5858
}
59-
60-
_bitmapIndex.Add(name, 1 << 29 | index);
59+
_bitmapIndex.Add(name, CreateBitmapIndex(storeIndex: MethodStoreIndex, valueIndex: index));
6160

6261
return this;
6362
}
6463

65-
/// <summary>
66-
///
67-
/// </summary>
68-
/// <param name="name"></param>
69-
/// <param name="delegate"></param>
70-
/// <returns></returns>
71-
public IFunctionRegister Add(string name, Delegate @delegate)
64+
65+
public IFunctionRegister Add(string name, IFunctionProvider functionProvider)
7266
{
73-
//Allow only static methods
74-
if (@delegate.Target != null)
67+
ValidateFunctionName(name);
68+
69+
if (functionProvider == null)
7570
{
76-
throw new SimpleflowException(Resources.Message.RegisterNonStaticMethodError);
71+
throw new ArgumentNullException(nameof(functionProvider));
7772
}
7873

79-
ValidateFunctionName(name);
80-
8174
if (_bitmapIndex.ContainsKey(name))
8275
{
8376
throw new DuplicateFunctionException(name);
@@ -86,61 +79,61 @@ public IFunctionRegister Add(string name, Delegate @delegate)
8679
int index;
8780
lock (_sync)
8881
{
89-
Debug.Assert(_bitmapIndex.Count == _typeStore.Count + _methodStore.Count);
82+
Debug.Assert(_bitmapIndex.Count == _methodStore.Count + _providerStore.Count);
9083

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

9889
return this;
9990
}
10091

92+
10193
/// <inheritdoc />
102-
public Delegate GetFunction(string name)
94+
public FunctionPointer GetFunction(string name, ArgumentInfo[] argumentInfo) // ArgumentInfo
10395
{
10496
if (_bitmapIndex.ContainsKey(name))
10597
{
10698
var bitmapIndex = _bitmapIndex[name];
107-
108-
var firstBit = bitmapIndex >> 29;
109-
var index = (firstBit << 29) ^ bitmapIndex;
110-
111-
// ReSharper disable once InconsistentlySynchronizedField
112-
return firstBit == 1 ? null : _methodStore[index];
99+
(int storeIndex, int valueIndex) = GetStoreAndValueIndex(bitmapIndex);
100+
101+
return storeIndex switch
102+
{
103+
MethodStoreIndex => new FunctionPointer { Reference = _methodStore[valueIndex] },
104+
ProviderStoreIndex => _providerStore[valueIndex].GetFunction(name, argumentInfo),
105+
_ => null
106+
};
113107
}
114108
return null;
115109
}
116110

117-
/// <summary>
118-
///
119-
/// </summary>
120-
/// <param name="name"></param>
121-
/// <returns></returns>
122-
123-
private bool? IsFunctionAvailableInClass(string name)
111+
private (int storeIndex, int valueIndex) GetStoreAndValueIndex(int bitmapIndex)
124112
{
125-
if (_bitmapIndex.ContainsKey(name))
126-
{
127-
var bitmapIndex = _bitmapIndex[name];
128-
var firstBit = bitmapIndex >> 29;
113+
var storeIndex = bitmapIndex >> 28;
114+
var valueIndex = (storeIndex << 28) ^ bitmapIndex;
129115

130-
return firstBit == 1;
131-
}
132-
return false;
116+
return (storeIndex, valueIndex);
117+
}
118+
119+
private int CreateBitmapIndex(int storeIndex, int valueIndex)
120+
{
121+
return storeIndex << 28 | valueIndex;
133122
}
134123

135124
private void ValidateFunctionName(string name)
136125
{
137-
var pattern = "^[_]*[a-zA-Z][_a-zA-Z0-9]*([.][_]*[a-zA-Z][_a-zA-Z0-9]*)*$";
126+
if (string.IsNullOrWhiteSpace(name))
127+
{
128+
throw new ArgumentNullException(nameof(name));
129+
}
138130

139-
if (!System.Text.RegularExpressions.Regex.Match(name,pattern).Success)
131+
var pattern = "^[_]*[a-zA-Z][_a-zA-Z0-9]*([.][_]*[a-zA-Z][_a-zA-Z0-9]*)*$";
132+
133+
if (!System.Text.RegularExpressions.Regex.Match(name, pattern).Success)
140134
{
141135
throw new InvalidFunctionNameException(name);
142136
}
143137
}
144-
145138
}
146139
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) navtech.io. All rights reserved.
2+
// See License in the project root for license information.
3+
4+
namespace Simpleflow
5+
{
6+
/// <summary>
7+
/// Use to provide appropriate delegate based on name
8+
/// and argument information
9+
/// </summary>
10+
public interface IFunctionProvider
11+
{
12+
/// <summary>
13+
/// Get function reference to invoke
14+
/// </summary>
15+
/// <param name="name"></param>
16+
/// <param name="argumentInfo"></param>
17+
/// <returns></returns>
18+
FunctionPointer GetFunction(string name, ArgumentInfo[] argumentInfo);
19+
}
20+
}
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,15 @@ public interface IFunctionRegister
2626
/// <returns></returns>
2727
IFunctionRegister Add(string name, Delegate @delegate);
2828

29+
30+
IFunctionRegister Add(string name, IFunctionProvider provider);
31+
2932
/// <summary>
3033
///
3134
/// </summary>
3235
/// <param name="name"></param>
36+
/// <param name="argumentInfo"></param>
3337
/// <returns></returns>
34-
Delegate GetFunction(string name);
38+
FunctionPointer GetFunction(string name, ArgumentInfo[] argumentInfo);
3539
}
3640
}

0 commit comments

Comments
 (0)