Skip to content

Commit 2711c2c

Browse files
author
Steve Ives
authored
Merge pull request #12 from Synergex/complexExpression
Complex expression support
2 parents 6397453 + 62dd286 commit 2711c2c

File tree

11 files changed

+355
-40
lines changed

11 files changed

+355
-40
lines changed

CodeGenEngine/ErrorReporting.dbl

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,29 @@ namespace CodeGen.Engine
116116
required in node, @ExpressionNode
117117
proc
118118
reportValidity(node.Value)
119-
endmethod
119+
endmethod
120+
121+
public method Visit, void
122+
required in node, @UnaryExpressionNode
123+
proc
124+
reportValidity(node.Value)
125+
Visit(node.Expression)
126+
endmethod
127+
128+
public method Visit, void
129+
required in node, @BinaryExpressionNode
130+
proc
131+
reportValidity(node.Value)
132+
Visit(node.Left)
133+
Visit(node.Right)
134+
endmethod
135+
136+
public method Visit, void
137+
required in node, @GroupExpressionNode
138+
proc
139+
reportValidity(node.Value)
140+
Visit(node.Expression)
141+
endmethod
120142

121143
;;; <summary>
122144
;;;

CodeGenEngine/ExpressionEvaluators/ExpressionEvaluator.dbl

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,14 @@ namespace CodeGen.Engine
9393
;;; <param name="loops"></param>
9494
;;; <returns></returns>
9595
public method EvaluateExpression, boolean
96-
currentContext, TokenType
96+
currentContextFn, @Func<ExpressionNode, TokenType>
9797
expression, @ExpressionNode
9898
template, @FileNode
9999
loops, @IEnumerable<LoopNode>
100100
endparams
101-
proc
101+
proc
102+
data currentContext = currentContextFn(expression)
103+
102104
lambda doNoEvaluatorMessage()
103105
begin
104106
mreturn string.Format("CODEGEN BUG: No evaluator method was found for the {0} expression <IF {1}>. Define a new evaluator method in ExpressionEvaluator.", currentContext.ToString(), expression.Value.Value)
@@ -112,7 +114,30 @@ namespace CodeGen.Engine
112114
data badEvaluationRequestMessage, @Func<string>, doBadEvaluationRequestMessage
113115

114116
lambda doFindAndRunEvaluator(evaluators)
115-
begin
117+
begin
118+
if(expression .is. UnaryExpressionNode) then
119+
begin
120+
if(expression.Value.Value == "NOT")
121+
begin
122+
mreturn !EvaluateExpression(currentContextFn, ((@UnaryExpressionNode)expression).Expression, template, loops)
123+
end
124+
end
125+
else if(expression .is. BinaryExpressionNode) then
126+
begin
127+
data leftExpr = ((@BinaryExpressionNode)expression).Left
128+
data rightExpr = ((@BinaryExpressionNode)expression).Right
129+
if(expression.Value.Value == "AND") then
130+
mreturn EvaluateExpression(currentContextFn, leftExpr, template, loops) &&
131+
& EvaluateExpression(currentContextFn, rightExpr, template, loops)
132+
else if(expression.Value.Value == "OR")
133+
mreturn EvaluateExpression(currentContextFn, leftExpr, template, loops) ||
134+
& EvaluateExpression(currentContextFn, rightExpr, template, loops)
135+
end
136+
else if(expression .is. GroupExpressionNode)
137+
begin
138+
mreturn EvaluateExpression(currentContextFn, ((@GroupExpressionNode)expression).Expression, template, loops)
139+
end
140+
116141
;;Normal expressions
117142
if (evaluators.ContainsKey(expression.Value.Value)) then
118143
mreturn evaluators[expression.Value.Value](expression.Value, template, loops)

CodeGenEngine/ITreeNodeVisitor.dbl

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,19 @@ namespace CodeGen.Engine
6060

6161
method Visit, void
6262
node, @ExpansionNode
63-
endmethod
63+
endmethod
64+
65+
method Visit, void
66+
node, @UnaryExpressionNode
67+
endmethod
68+
69+
method Visit, void
70+
node, @BinaryExpressionNode
71+
endmethod
72+
73+
method Visit, void
74+
node, @GroupExpressionNode
75+
endmethod
6476

6577
method Visit, void
6678
node, @LoopNode

CodeGenEngine/Parser.dbl

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,80 @@ namespace CodeGen.Engine
588588

589589
mreturn closer
590590

591+
endmethod
592+
593+
private static method ProcessOperator, void
594+
operatorStack, @Stack<Token>
595+
expressionStack, @Stack<ExpressionNode>
596+
proc
597+
data tkn = operatorStack.Pop()
598+
if(tkn.Value == '(') then
599+
begin
600+
data expr = expressionStack.Pop()
601+
expressionStack.Push(new GroupExpressionNode() { Value = tkn, Expression = expr })
602+
end
603+
else if(tkn.Value == 'NOT') then
604+
begin
605+
data expr = expressionStack.Pop()
606+
expressionStack.Push(new UnaryExpressionNode() { Value = tkn, Expression = expr })
607+
end
608+
else if(tkn.Value == 'AND' || tkn.Value == 'OR') then
609+
begin
610+
data expr1 = expressionStack.Pop()
611+
data expr2 = expressionStack.Pop()
612+
expressionStack.Push(new BinaryExpressionNode() { Value = tkn, Left = expr1, Right = expr2 })
613+
end
614+
else
615+
throw new Exception("expression processor resulted in too few operands")
616+
endmethod
617+
618+
619+
private static method ProcessExpressionNode, @ExpressionNode
620+
required in tokens, @List<Token>
621+
required inout index, int
622+
proc
623+
data operatorStack = new Stack<Token>()
624+
data expressionStack = new Stack<ExpressionNode>()
625+
626+
while(index < tokens.Count - 1)
627+
begin
628+
data tkn = tokens[index]
629+
if(tkn.TypeOfToken == TokenType.Expression) then
630+
begin
631+
if(tkn.Value == '(' || tkn.Value == 'NOT' || tkn.Value == 'AND' || tkn.Value == 'OR') then
632+
begin
633+
operatorStack.Push(tkn)
634+
end
635+
else if(tkn.Value == ')') then
636+
begin
637+
data targetToken = operatorStack.LastOrDefault(lambda(opTkn) { opTkn.Value == '(' })
638+
while(operatorStack.Contains(targetToken))
639+
begin
640+
ProcessOperator(operatorStack, expressionStack)
641+
end
642+
end
643+
else
644+
begin
645+
expressionStack.Push(new ExpressionNode() { Value = tkn })
646+
end
647+
incr index
648+
end
649+
else
650+
begin
651+
decr index
652+
while(operatorStack.Count > 0)
653+
begin
654+
ProcessOperator(operatorStack, expressionStack)
655+
end
656+
657+
if(expressionStack.Count > 1) then
658+
throw new Exception("expression processor resulted in too many operands")
659+
else if(expressionStack.Count == 0) then
660+
throw new Exception("expression processor resulted in too few operands")
661+
else
662+
mreturn expressionStack.Peek()
663+
end
664+
end
591665
endmethod
592666

593667
private static method processConditional, int
@@ -612,8 +686,9 @@ namespace CodeGen.Engine
612686
using (tkn.TypeOfToken) select
613687

614688
(TokenType.Expression),
615-
ctrlNode.Expression = new ExpressionNode() { Value = tkn }
616-
689+
begin
690+
ctrlNode.Expression = ProcessExpressionNode(tokens, ix)
691+
end
617692
(),
618693
begin
619694
state.Push(ParserState.LookingForElseToken)

CodeGenEngine/Tokenizer.dbl

Lines changed: 64 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ namespace CodeGen.Engine
8080
private canonicalNameLookup, @Dictionary<string, string>, new Dictionary<string, string>()
8181

8282
private allowsExpressions, @HashSet<string>, new HashSet<string>()
83+
private expressionGlue, @HashSet<string>, new HashSet<string>()
8384

8485
private customValidity, @List<TokenValidity>
8586
private userTokenValidity, @List<TokenValidity>
@@ -1064,6 +1065,18 @@ namespace CodeGen.Engine
10641065
expressions.Add("VOID_SUBROUTINE", TokenValidity.MethodLoop)
10651066
expressions.Add("WEB", TokenValidity.FieldLoop | TokenValidity.KeySegmentLoop | TokenValidity.RelationSegmentLoop)
10661067

1068+
expressions.Add("AND", TokenValidity.Anywhere)
1069+
expressions.Add("OR", TokenValidity.Anywhere)
1070+
expressions.Add("NOT", TokenValidity.Anywhere)
1071+
expressions.Add("(", TokenValidity.Anywhere)
1072+
expressions.Add(")", TokenValidity.Anywhere)
1073+
1074+
expressionGlue.Add("AND")
1075+
expressionGlue.Add("OR")
1076+
expressionGlue.Add("NOT")
1077+
expressionGlue.Add("(")
1078+
expressionGlue.Add(")")
1079+
10671080
if (context != ^null)
10681081
begin
10691082
data define, string
@@ -1433,7 +1446,7 @@ namespace CodeGen.Engine
14331446

14341447
while (ix < templateCode.Length) do
14351448
begin
1436-
data nextToken, @PossibleToken, nextPossibleToken(ix, templateCode)
1449+
data nextToken, @PossibleToken, nextPossibleToken(ix, templateCode, tokens.LastOrDefault())
14371450

14381451
if (nextToken == ^null) then
14391452
begin
@@ -1450,8 +1463,8 @@ namespace CodeGen.Engine
14501463
;; Is the token a closer?
14511464
data closer = nextTokenValue.StartsWith("/")
14521465

1453-
;; The next token isn't here, so we have just text. Add a Text token.
1454-
if (nextToken.StartsAtPosition != ix)
1466+
;; The next token isn't here, so we have just text. Add a Text token.
1467+
if (nextToken.StartsAtPosition != ix && nextToken.StartsAtPosition > ix)
14551468
tokens.Add(new Token(fileName, ix, nextToken.StartsAtPosition - 1, false, templateCode.Substring(ix, nextToken.StartsAtPosition - ix), TokenType.Text, TokenModifier.None, ^null, lineStarts, false, false))
14561469

14571470
if (nextToken.IsComment) then
@@ -1576,16 +1589,31 @@ namespace CodeGen.Engine
15761589
ix = nextToken.EndsAtPosition + 1
15771590

15781591
if (nextToken.IsExpression)
1579-
begin
1580-
data nextExpression, @Tuple<int, int, List<TokenValidity>>, nextExpressionToken(ix, templateCode)
1581-
if (nextExpression != ^null)
1592+
begin
1593+
data expressionError = ""
1594+
data nextExpression, @Tuple<int, int, List<TokenValidity>>, nextExpressionToken(ix, templateCode, expressionError)
1595+
if(!string.IsNullOrWhiteSpace(expressionError) && !nextToken.IsCloser)
1596+
reportError(expressionError)
1597+
while (nextExpression != ^null)
15821598
begin
15831599
data expressionValue, string, templateCode.Substring(nextExpression.Item1, nextExpression.Item2 - nextExpression.Item1)
15841600
;; TODO: Does this expression require repository structure processing?
1585-
data requiresRps, boolean, false
1601+
data requiresRps, boolean, false
15861602
tokens.Add(new Token(fileName, nextExpression.Item1, nextExpression.Item2, false, expressionValue, TokenType.Expression, TokenModifier.None, nextExpression.Item3, lineStarts, requiresRps, false))
1587-
ix = nextExpression.Item2 + 1
1588-
end
1603+
if(expressionValue == '(' || expressionValue == ')' || templateCode[nextExpression.Item2] == ')') then
1604+
ix = nextExpression.Item2
1605+
else
1606+
begin
1607+
ix = nextExpression.Item2 + 1
1608+
if(templateCode[nextExpression.Item2] == '>')
1609+
exitloop
1610+
end
1611+
1612+
nextExpression = nextExpressionToken(ix, templateCode, expressionError)
1613+
end
1614+
1615+
if(ix < templateCode.Length && templateCode[ix] == '>')
1616+
incr ix
15891617
end
15901618

15911619
end
@@ -1663,21 +1691,22 @@ namespace CodeGen.Engine
16631691
;;; <returns>PossibleToken instance, or null if no possible tokens found.</returns>
16641692
private method nextPossibleToken, @PossibleToken
16651693
searchFrom, int
1666-
templateCode, string
1667-
proc
1668-
data foundPossibleToken = false
1694+
templateCode, string
1695+
lastToken, @Token
1696+
proc
1697+
data inTagTail = searchFrom > 0 && templateCode[searchFrom - 1] == '>'
1698+
data foundPossibleToken = !inTagTail && lastToken != ^null ? lastToken.TypeOfToken == TokenType.Expression : false
16691699
data isCloser = false
16701700
data isComment = false
1671-
data isExpression = false
1672-
data possibleTokenStartsAt, int, -1
1701+
data isExpression = foundPossibleToken
1702+
data possibleTokenStartsAt, int, foundPossibleToken ? searchFrom - 1 : -1
16731703

16741704
;; Character by character looking for a token
16751705
data ix, int
16761706
for ix from searchFrom thru templateCode.Length - 1
16771707
begin
16781708
;; Did we find a newline?
16791709
data foundNewLine, boolean, (((templateCode[ix] == %char(13)) && (templateCode.Length > ix + 1) && (templateCode[ix + 1] == %char(10))) || (templateCode[ix] == %char(10)))
1680-
16811710
;; Did we find the start of a template file comment (;//)?
16821711
if (templateCode.Length > ix + 2 && templateCode[ix] == ';' && templateCode[ix + 1] == '/' && templateCode[ix + 2] == '/') then
16831712
begin
@@ -1713,7 +1742,7 @@ namespace CodeGen.Engine
17131742
isCloser = false
17141743
foundPossibleToken = true
17151744
possibleTokenStartsAt = ix
1716-
end
1745+
end
17171746
end
17181747
else
17191748
begin
@@ -1736,8 +1765,9 @@ namespace CodeGen.Engine
17361765
begin
17371766
;; So we found a > or a space after an <
17381767

1739-
;; Get the start and end indexes of the VALUE of the possible token (withoit the < > or " ")
1740-
data realStartIndex, int, (possibleTokenStartsAt + 1)
1768+
;; Get the start and end indexes of the VALUE of the possible token (withoit the < > or " ")
1769+
data skipStartLetter = templateCode[possibleTokenStartsAt] == '<' || templateCode[possibleTokenStartsAt] == ' '
1770+
data realStartIndex, int, skipStartLetter ? possibleTokenStartsAt + 1 : possibleTokenStartsAt
17411771
data realEndIndex, int, ix - 1
17421772
if (isCloser)
17431773
realStartIndex += 1
@@ -1854,7 +1884,8 @@ namespace CodeGen.Engine
18541884
;;; <returns></returns>
18551885
private method nextExpressionToken, @Tuple<int, int, List<TokenValidity>>
18561886
startIndex, int
1857-
templateCode, string
1887+
templateCode, string
1888+
out errorText, string
18581889
proc
18591890
data startedToken, boolean, false
18601891
data startedTokenIndex, int, -1
@@ -1864,19 +1895,21 @@ namespace CodeGen.Engine
18641895
begin
18651896
if (!startedToken) then
18661897
begin
1867-
if (char.IsLetter(templateCode[ix]))
1898+
if (char.IsLetter(templateCode[ix])) then
18681899
begin
18691900
startedToken = true
18701901
startedTokenIndex = ix
1871-
end
1902+
end
1903+
else if(templateCode[ix] == '(' || templateCode[ix] == ')')
1904+
mreturn Tuple.Create(ix, ix + 1, userTokenValidity)
18721905
end
18731906
else
1874-
begin
1907+
begin
18751908
if (char.IsLetterOrDigit(templateCode[ix]) || (templateCode[ix] == '_')) then
18761909
begin
18771910
nextloop
18781911
end
1879-
else if ((templateCode[ix] == ' ') || (templateCode[ix] == '>')) then
1912+
else if ((templateCode[ix] == ' ') || (templateCode[ix] == '>') || (templateCode[ix] == ')')) then
18801913
begin
18811914
data expressionType, @List<TokenValidity>
18821915
data expstring, string, templateCode.Substring(startedTokenIndex, ix - startedTokenIndex)
@@ -1909,12 +1942,16 @@ namespace CodeGen.Engine
19091942
& || expstring.StartsWith("TOTAL_ITEMS_")) then
19101943
begin
19111944
mreturn Tuple.Create(startedTokenIndex, ix, loopUtilityTokenValidity)
1912-
end
1945+
end
1946+
else if(expressionGlue.Contains(expstring)) then
1947+
begin
1948+
mreturn Tuple.Create(startedTokenIndex, ix, userTokenValidity)
1949+
end
19131950
else
19141951
begin
1915-
;; Invalid expression
1916-
reportError(String.Format("Invalid expression <IF {0}> at offset {1}!", expstring, startedTokenIndex - 4, ""))
1917-
exitloop
1952+
;; Invalid expression
1953+
errorText = String.Format("Invalid expression <IF {0}> at offset {1}!", expstring, startedTokenIndex - 4, "")
1954+
mreturn ^null
19181955
end
19191956
end
19201957
else if (char.IsControl(templateCode[ix])) then

0 commit comments

Comments
 (0)