Skip to content

Commit 9df04a2

Browse files
author
Rajesh Jinaga
committed
Add single dimensional array support
1 parent 058253b commit 9df04a2

23 files changed

+2445
-1298
lines changed

src/Simpleflow/CodeGenerator/SimpleflowCodeVisitor.Helpers.cs

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,24 +44,6 @@ private string GetUnquotedText(string @string)
4444
return @string.Substring(1, @string.Length - 2); // Trim first and last quotes
4545
}
4646

47-
private Expression GetEndProperty(Expression rootObjectExp, string[] propertiesHierarchy, int startIndex = 0)
48-
{
49-
for (int index = startIndex; index < propertiesHierarchy.Length; index++)
50-
{
51-
var propertyName = propertiesHierarchy[index];
52-
var prop = GetPropertyInfo(rootObjectExp.Type, propertyName);
53-
54-
if (prop == null)
55-
{
56-
throw new InvalidPropertyException($"Invalid property '{propertyName}'");
57-
}
58-
59-
rootObjectExp = Expression.Property(rootObjectExp, prop);
60-
}
61-
62-
return rootObjectExp;
63-
}
64-
6547
private ParameterExpression GetVariable(string name)
6648
{
6749
return Variables.SingleOrDefault(@var => string.Equals(@var.Name , name, StringComparison.OrdinalIgnoreCase));
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) navtech.io. All rights reserved.
2+
// See License in the project root for license information.
3+
4+
using System;
5+
using System.Linq;
6+
using System.Collections.Generic;
7+
using System.Linq.Expressions;
8+
using Antlr4.Runtime.Misc;
9+
10+
using Simpleflow.Parser;
11+
12+
namespace Simpleflow.CodeGenerator
13+
{
14+
partial class SimpleflowCodeVisitor<TArg>
15+
{
16+
public override Expression VisitArrayLiteral([NotNull] SimpleflowParser.ArrayLiteralContext context)
17+
{
18+
var arrayValue = context.arrayValue();
19+
var ilArrayValue = new List<Expression>(arrayValue.Length);
20+
Type ilArrayType = null;
21+
22+
// process each value in array
23+
foreach (var value in arrayValue)
24+
{
25+
var ilexp = value.Accept(this);
26+
if (ilexp != null)
27+
{
28+
ilArrayValue.Add(ilexp);
29+
30+
// Check for type, if type is not matched with previous one then consider it as array of objects
31+
if (ilArrayType != null && ilArrayType != ilexp.Type)
32+
{
33+
ilArrayType = typeof(object);
34+
}
35+
else
36+
{
37+
ilArrayType = ilexp.Type;
38+
}
39+
}
40+
}
41+
42+
ilArrayType = ilArrayType ?? typeof(object); // This assignment if no values in array
43+
44+
// Convert all of the expressions into object expressions if all the types are not same
45+
if (ilArrayType == typeof(object))
46+
{
47+
ilArrayValue = ilArrayValue.Select(item => Expression.Convert(item, typeof(object))).ToList<Expression>();
48+
}
49+
50+
// Create list object
51+
var constructorInfo = typeof(List<>).MakeGenericType(ilArrayType)
52+
.GetConstructor(new Type[] { typeof(IEnumerable<>).MakeGenericType(ilArrayType) });
53+
54+
return Expression.New(constructorInfo, Expression.NewArrayInit(ilArrayType, ilArrayValue));
55+
}
56+
57+
public override Expression VisitArrayValue([NotNull] SimpleflowParser.ArrayValueContext context)
58+
{
59+
return Visit(context.GetChild(0));
60+
}
61+
}
62+
}

src/Simpleflow/CodeGenerator/SimpleflowCodeVisitor.VisitEmitters.cs

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -58,28 +58,6 @@ public override Expression VisitOutputStmt(SimpleflowParser.OutputStmtContext co
5858
return callExpr;
5959
}
6060

61-
62-
/// <summary>
63-
/// Trim first and last quotes
64-
/// </summary>
65-
/// <param name="string"></param>
66-
/// <returns></returns>
67-
68-
69-
private Expression CallListAddMethod(Expression message, string propertyName)
70-
{
71-
72-
Expression callExpr = Expression.Call(
73-
Expression.Property(OutputParam, propertyName),
74-
// ReSharper disable once AssignNullToNotNullAttribute
75-
typeof(List<string>).GetMethod(nameof(List<string>.Add), new Type[] { typeof(string) }),
76-
message
77-
);
78-
79-
// Call function to to add message
80-
return callExpr;
81-
}
82-
8361
private Expression HandleMessageText(SimpleflowParser.MessageTextContext messageToken, string outputProperty)
8462
{
8563
var identifier = Visit(messageToken.GetChild(0));
@@ -88,9 +66,19 @@ private Expression HandleMessageText(SimpleflowParser.MessageTextContext message
8866
identifier : ToStringExpression(identifier);
8967

9068
return CallListAddMethod(identifier, outputProperty);
91-
9269
}
9370

71+
private Expression CallListAddMethod(Expression message, string outputProperty)
72+
{
73+
Expression callExpr = Expression.Call(
74+
Expression.Property(OutputParam, outputProperty),
75+
// ReSharper disable once AssignNullToNotNullAttribute
76+
typeof(List<string>).GetMethod(nameof(List<string>.Add), new Type[] { typeof(string) }),
77+
message
78+
);
9479

80+
// Call function to add message
81+
return callExpr;
82+
}
9583
}
9684
}
Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
// Copyright (c) navtech.io. All rights reserved.
22
// See License in the project root for license information.
33

4-
using System;
54
using System.Linq;
65
using System.Linq.Expressions;
76

8-
using Antlr4.Runtime.Tree;
9-
107
using Simpleflow.Exceptions;
118
using Simpleflow.Parser;
129

@@ -16,17 +13,63 @@ partial class SimpleflowCodeVisitor<TArg>
1613
{
1714
public override Expression VisitObjectIdentifier(SimpleflowParser.ObjectIdentifierContext context)
1815
{
19-
var objectPathProperties = context.GetText().Split('.');
20-
21-
var propName = objectPathProperties[0];
22-
Expression identifier = GetVariable(propName) ?? GetSmartVariable(propName)?.VariableExpression?.Left;
16+
// Get initial object
17+
var variableName = context.identifierIndex()[0].Identifier().GetText();
18+
Expression objectExp = GetVariable(variableName) ?? GetSmartVariable(variableName)?.VariableExpression?.Left;
2319

24-
if (identifier == null)
20+
if (objectExp == null)
2521
{
26-
throw new UndeclaredVariableException(objectPathProperties[0]);
22+
throw new UndeclaredVariableException(variableName);
23+
}
24+
25+
// Get index object if specified
26+
var indexObjectExp = GetIndexObjectExpIfDefined(objectExp, context.identifierIndex()[0]);
27+
28+
// Traverse through and get final object
29+
return GetFinalPropertyValue(indexObjectExp, context.identifierIndex());
30+
}
31+
32+
private Expression GetIndexObjectExpIfDefined(Expression objectExp, SimpleflowParser.IdentifierIndexContext context)
33+
{
34+
if (context.index() != null)
35+
{
36+
var indexExpression = Visit(context.index().indexExpression().GetChild(0)); // represents index
37+
38+
var indexProperty
39+
= objectExp
40+
.Type
41+
.GetProperties()
42+
.SingleOrDefault(p => p.GetIndexParameters().Length == 1 &&
43+
p.GetIndexParameters()[0].ParameterType == indexExpression.Type);
44+
45+
return Expression.MakeIndex(objectExp, indexProperty, new[] { indexExpression });
2746
}
2847

29-
return GetEndProperty(identifier, objectPathProperties, startIndex: 1);
48+
return objectExp;
49+
}
50+
51+
private Expression GetFinalPropertyValue(Expression propExp, SimpleflowParser.IdentifierIndexContext[] propertiesHierarchy)
52+
{
53+
for (int i = 1; i < propertiesHierarchy.Length; i++)
54+
{
55+
var property = propertiesHierarchy[i];
56+
57+
// Get next property name
58+
var propName = property.Identifier().GetText();
59+
var prop = GetPropertyInfo(propExp.Type, propName);
60+
61+
if (prop == null)
62+
{
63+
throw new InvalidPropertyException($"Invalid property '{propName}'");
64+
}
65+
66+
// Get indexed object
67+
propExp = GetIndexObjectExpIfDefined(propExp, property);
68+
69+
// Get property of indexed object
70+
propExp = Expression.Property(propExp, prop);
71+
}
72+
return propExp;
3073
}
3174
}
3275
}

src/Simpleflow/CodeGenerator/SimpleflowCodeVisitor.VisitRule.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,9 @@ public override Expression VisitRuleStmt(SimpleflowParser.RuleStmtContext contex
2121
var statements = new List<Expression>();
2222

2323
// Process predicate and statements
24-
for (int i = 0; i < context.ChildCount; i++)
24+
foreach (var statement in context.generalStatement())
2525
{
26-
var c = context.GetChild(i);
27-
28-
// Stop at end statement and return block outside of if else
26+
var c = statement. GetChild(0);
2927

3028
if (c.GetType() != typeof(SimpleflowParser.PredicateContext))
3129
{
@@ -38,6 +36,5 @@ public override Expression VisitRuleStmt(SimpleflowParser.RuleStmtContext contex
3836
}
3937
return Expression.IfThen(testExpression, Expression.Block(statements));
4038
}
41-
4239
}
4340
}

src/Simpleflow/Parser/Base/SimpleflowParserBase.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,22 @@ protected SimpleflowParserBase(ITokenStream input, TextWriter output, TextWriter
2323
{
2424
}
2525

26+
protected bool NotLineTerminator()
27+
{
28+
return !Here(LineTerminator);
29+
}
30+
31+
protected bool Here(int type)
32+
{
33+
// Get the token ahead of the current index.
34+
int possibleIndexEosToken = CurrentToken.TokenIndex - 1;
35+
IToken ahead = ((ITokenStream)this.InputStream).Get(possibleIndexEosToken);
36+
37+
// Check if the token resides on the Hidden channel and if it's of the
38+
// provided type.
39+
return ahead.Channel == Lexer.Hidden && ahead.Type == type;
40+
}
41+
2642
protected bool LineTerminatorAhead()
2743
{
2844
// Get the token ahead of the current index.

src/Simpleflow/Parser/Grammar/SimpleflowLexer.g4

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@ lexer grammar SimpleflowLexer;
33
channels { ERROR }
44
options { superClass=SimpleflowLexerBase; }
55

6-
WhiteSpaces: [\t\u000B\u000C\u0020\u00A0]+ -> channel(HIDDEN);
7-
LineTerminator: [\r\n\u2028\u2029] -> channel(HIDDEN);
86
MultiLineComment: '/*' .*? '*/' -> channel(HIDDEN);
97
SingleLineComment: '#' ~[\r\n\u2028\u2029]* -> channel(HIDDEN);
108

9+
OpenBracket: '[';
10+
CloseBracket: ']';
11+
OpenParen: '(';
12+
CloseParen: ')';
1113
OpenBrace: '{';
1214
TemplateCloseBrace: {this.IsInTemplateString()}? '}' -> popMode;
1315
CloseBrace: '}';
14-
OpenParen: '(';
15-
CloseParen: ')';
1616

1717
Colon: ':' ;
1818
Comma : ',';
@@ -34,7 +34,6 @@ LessThan: '<';
3434
LessThanEqual: '<=';
3535
Equal: '==';
3636
NotEqual: '!=';
37-
Contains: 'contains';
3837

3938
// keywords
4039

@@ -55,6 +54,8 @@ Output: 'output';
5554

5655
// relational operators
5756

57+
Semicolon: ';';
58+
5859
And: 'and';
5960
Or: 'or';
6061
Not: 'not';
@@ -63,15 +64,21 @@ Not: 'not';
6364
True: 'true';
6465
False: 'false';
6566
Number: ('+'|'-')?[0-9]+('.'[0-9]+)?;
67+
6668
String: '"' ( '\\"' | ~["\r\n] )*? '"';
6769
None: 'none' ;
6870
6971
Identifier: [_]*[a-zA-Z][_a-zA-Z0-9]* ;
7072
IgnoreIdentifier: '_';
7173
74+
Indexer: OpenBracket Number CloseBracket;
75+
7276
FunctionName: '$' NAME ('.' NAME)*;
7377
BackTick: '`' {this.IncreaseTemplateDepth();} -> pushMode(TEMPLATE);
7478
79+
WhiteSpaces: [\t\u000B\u000C\u0020\u00A0]+ -> channel(HIDDEN);
80+
LineTerminator: [\r\n\u2028\u2029] -> channel(HIDDEN);
81+
7582
mode TEMPLATE;
7683
7784
BackTickInside: '`' {this.DecreaseTemplateDepth();} -> type(BackTick), popMode;

0 commit comments

Comments
 (0)