Skip to content

Commit c861788

Browse files
authored
Added for loops and do loops to stdLambda (#148)
No implementation of exit keyword as of now. That is because premature leaving of either `do`, `for` or `fun` can result in misaligned stack, which will cause problems down the line.
1 parent cbface4 commit c861788

File tree

1 file changed

+152
-4
lines changed

1 file changed

+152
-4
lines changed

src/stdLambda.cls

Lines changed: 152 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ Private Type TThis
199199
End Type
200200
Private This As TThis
201201

202+
Private InLoop As Boolean
202203

203204

204205

@@ -470,6 +471,22 @@ Private Function getTokenDefinitions() As TokenDefinition()
470471
i = i + 1: arr(i) = getTokenDefinition("then", "then", isKeyword:=True)
471472
i = i + 1: arr(i) = getTokenDefinition("else", "else", isKeyword:=True)
472473
i = i + 1: arr(i) = getTokenDefinition("end", "end", isKeyword:=True)
474+
475+
'Do Loop
476+
i = i + 1: arr(i) = getTokenDefinition("do", "do", isKeyword:=True)
477+
i = i + 1: arr(i) = getTokenDefinition("until", "until", isKeyword:=True)
478+
i = i + 1: arr(i) = getTokenDefinition("while", "while", isKeyword:=True)
479+
i = i + 1: arr(i) = getTokenDefinition("loop", "loop", isKeyword:=True)
480+
481+
'For Loop
482+
i = i + 1: arr(i) = getTokenDefinition("for", "for", isKeyword:=True)
483+
i = i + 1: arr(i) = getTokenDefinition("to", "to", isKeyword:=True)
484+
i = i + 1: arr(i) = getTokenDefinition("step", "step", isKeyword:=True)
485+
i = i + 1: arr(i) = getTokenDefinition("next", "next", isKeyword:=True)
486+
487+
'Exit keyword
488+
i = i + 1: arr(i) = getTokenDefinition("exit", "exit", isKeyword:=True)
489+
473490
' Brackets
474491
i = i + 1: arr(i) = getTokenDefinition("lBracket", "\(")
475492
i = i + 1: arr(i) = getTokenDefinition("rBracket", "\)")
@@ -582,7 +599,9 @@ End Sub
582599
'11. Arithmetic (^)
583600
'12. Arithmetic (Unary +, -) (for RHS of power operator) e.g. 2^-1
584601
'13. Flow (if then else)
585-
'14. Value (numbers, $vars, strings, booleans, brackets)
602+
'14. Do (Do Loop)
603+
'15. For(For Loop)
604+
'16. Value (numbers, $vars, strings, booleans, brackets)
586605
'@remark - The order of priority is opposite to the order of evaluation. I.E. Comparrison is evaluated before Logical AND allowing
587606
'expressions such as `1<2 and 2<3` to be evaluated correctly without requiring bracketing. It's important to note however that all
588607
'comparrisons have the same priority. This means that `1<2<3` will be evaluated as `(1<2)<3` which is not the same as `1<(2<3)`.
@@ -799,7 +818,9 @@ Private Sub parseFlowPriority1()
799818
This.stackSize = size
800819

801820
If optConsume("end") Then
802-
Call addOperation(iPush, 0, 1) 'Expressions should always return a value
821+
If InLoop = False Then
822+
Call addOperation(iPush, 0, 1) 'Expressions should always return a value
823+
End If
803824
This.operations(skipElseJumpIndex).value = This.iOperationIndex
804825
Else
805826
Call consume("else")
@@ -808,6 +829,129 @@ Private Sub parseFlowPriority1()
808829

809830
Call optConsume("end")
810831
End If
832+
Else
833+
Call parseFlowPriority2
834+
End If
835+
End Sub
836+
837+
'Parse loops (do ... {until|while} ... loop{until|while})
838+
Private Sub parseFlowPriority2()
839+
If optConsume("do") Then
840+
Dim LoopEntered As Boolean
841+
If InLoop = False Then LoopEntered = True
842+
InLoop = True
843+
' Do Until|While {Expr}
844+
Dim RepeatLoop As Integer: RepeatLoop = This.iOperationIndex
845+
Dim SkipLoop As Integer: SkipLoop = -1
846+
If optConsume("until") Then
847+
Call parseExpression
848+
SkipLoop = addOperation(iJump_IfTrue, , -1)
849+
ElseIf optConsume("while") Then
850+
Call parseExpression
851+
SkipLoop = addOperation(iJump_IfFalse, , -1)
852+
Else
853+
' Infinite loop
854+
End If
855+
856+
Call parseBlock("loop")
857+
Call consume("loop")
858+
859+
' xxx
860+
' Loop Until|While {expr}
861+
If optConsume("until") Then
862+
Call parseExpression
863+
SkipLoop = addOperation(iJump_IfTrue, , -1)
864+
ElseIf optConsume("while") Then
865+
Call parseExpression
866+
SkipLoop = addOperation(iJump_IfFalse, , -1)
867+
Else
868+
' Loop Ends
869+
End If
870+
871+
872+
' Loop back to start
873+
Call addOperation(iJump_Unconditional, RepeatLoop)
874+
If SkipLoop > -1 Then This.operations(SkipLoop).value = This.iOperationIndex
875+
If LoopEntered Then InLoop = False
876+
Else
877+
Call parseFlowPriority3
878+
End If
879+
End Sub
880+
881+
'Parse for loops (for ... {let} var = lbound to ubound ... {step operator increment} ... next)
882+
Private Sub parseFlowPriority3()
883+
If optConsume("for") Then
884+
Dim LoopEntered As Boolean
885+
If InLoop = False Then LoopEntered = True
886+
InLoop = True
887+
' 1. let index = LowerBound
888+
889+
Dim varName As String: varName = consume("var")
890+
Call consume("equal")
891+
Call parseExpression
892+
Dim IncrementIndex As Long: IncrementIndex = findVariable(varName)
893+
If IncrementIndex >= 0 Then
894+
' If the variable already existed, move the data to that pos on the stack
895+
Call addOperation(iSet_General, IncrementIndex, -1)
896+
Else
897+
' If the variable didn't exist yet, treat this stack pos as its source
898+
Call This.scopes(This.scopeCount).Add(varName, This.stackSize)
899+
IncrementIndex = This.stackSize - 1
900+
If IncrementIndex = 0 Then IncrementIndex = 1 ' Avoid referencing the current operation. | Used when the last added variable was declared in for loop
901+
End If
902+
903+
' Index > UpperBound
904+
' Jump if Condition true
905+
Call addOperation(iAccess_General, IncrementIndex, 1)
906+
Dim RepeatLoop As Long: RepeatLoop = This.iOperationIndex - 1 ' beginning of loop (condition)
907+
If peek("to") Then
908+
This.iTokenIndex = This.iTokenIndex + 1
909+
Else
910+
Call parseBlock("to")
911+
Call consume("to")
912+
End If
913+
Call parseStatement
914+
Dim ConditionIndex As Long: ConditionIndex = addOperation(iComparison_GreaterThan, , -1)
915+
Dim SkipLoop As Integer: SkipLoop = addOperation(iJump_IfTrue, , -1) ' Skip loop
916+
917+
' Setup increment Values
918+
Dim Increment As Long, Operator As IInstruction, Condition As IInstruction
919+
If optConsume("step") Then
920+
If peek("literalNumber") Then
921+
Operator = iArithmetic_Add
922+
Condition = iComparison_GreaterThan
923+
Else
924+
Select Case True
925+
Case optConsume("add"): Operator = iArithmetic_Add: Condition = iComparison_GreaterThan
926+
Case optConsume("subtract"): Operator = iArithmetic_Subtract: Condition = iComparison_LessThan
927+
Case optConsume("multiply"): Operator = iArithmetic_Multiply: Condition = iComparison_GreaterThan
928+
Case optConsume("divide"): Operator = iArithmetic_Divide: Condition = iComparison_LessThan
929+
Case optConsume("power"): Operator = iArithmetic_Power: Condition = iComparison_GreaterThan
930+
End Select
931+
End If
932+
Increment = CLng(consume("literalNumber")) ' TODO add more functionality than just numbers
933+
Else
934+
Increment = 1
935+
Operator = iArithmetic_Add
936+
Condition = iComparison_GreaterThan
937+
End If
938+
939+
This.operations(ConditionIndex).instruction = Condition ' Changes compare-operator of condition according to "step"
940+
941+
' Get rest of loop
942+
' Setup index-incrementation: let index = index Operator Increment
943+
' Setup loop-repeat
944+
Call parseBlock("next")
945+
Call consume("next")
946+
947+
Call addOperation(iAccess_General, IncrementIndex, 1)
948+
Call addOperation(iPush, Increment, 1)
949+
Call addOperation(Operator, , -1)
950+
Call addOperation(iSet_General, IncrementIndex, -1)
951+
Call addOperation(iJump_Unconditional, RepeatLoop, 0)
952+
953+
This.operations(SkipLoop).value = This.iOperationIndex
954+
If LoopEntered Then InLoop = False
811955
Else
812956
Call parseValuePriority1
813957
End If
@@ -992,8 +1136,12 @@ Private Sub parseAssignment()
9921136
Dim offset As Long: offset = findVariable(varName)
9931137
If offset >= 0 Then
9941138
' If the variable already existed, move the data to that pos on the stack
995-
Call addOperation(iSet_General, offset, -1)
996-
Call addOperation(iAccess_General, offset, 1) ' To keep a return value
1139+
If InLoop Then
1140+
Call addOperation(iSet_General, offset, -1)
1141+
Else
1142+
Call addOperation(iSet_General, offset, -1)
1143+
Call addOperation(iAccess_General, offset, 1) ' To keep a return value
1144+
End If
9971145
Else
9981146
' If the variable didn't exist yet, treat this stack pos as its source
9991147
Call This.scopes(This.scopeCount).add(varName, This.stackSize)

0 commit comments

Comments
 (0)