Skip to content

Commit ec04f95

Browse files
committed
Added for loops and do loops
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 ec04f95

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)