@@ -199,6 +199,7 @@ Private Type TThis
199199End Type
200200Private 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