-
Notifications
You must be signed in to change notification settings - Fork 66
Addition of loops in stdlambda #126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
2 Things are added: Do loops and for loops. Do loops work by running an infinite loop through a jump operation until a statement is true. For loops work by running the loop n times. Do-Loop syntax is the same as VBAs For-Loops are the same too, only that putting a word behind "next" will do nothing useful. Just write "next" at the end for that loop. Both loops need all their variables defined before the loop. If not, an error occures where the variable-name will be pushed onto stack instead of its value. I added some new subs to work with these two loops. I did not change anything major in the operation-creation. Only exception to that is how for loops are interpreted, as they requiere more inline creation of tokens. (for example it needs i = i + 1 to increment its index, otherwise it would run infinetly. Mind the line 1082. This is a proof of concept, feedback would be nice.
To test:
|
Was this generated with AI? lol |
Needs more testing, expressions can be embedded within the for loop, currently token stream gets messed up. Example: Dim lambda As stdLambda
Set lambda = stdLambda.CreateMultiline(Array( _
"let x = $1", _
"For i = 0 to If x > 10 Then 10 Else 20 End Step 2", _
" let x = x / 2", _
"next" _
))
Debug.Print lambda.Run(9)
Debug.Print lambda.Run(10)
Debug.Print lambda.Run(11) or even Dim lambda As stdLambda
Set lambda = stdLambda.CreateMultiline(Array( _
"let x = $1", _
"let i = 0", _
"For i = 0 to (10+2) Step +2", _
" let x = x / 2", _
"next" _
))
Debug.Print lambda.Run(9)
Debug.Print lambda.Run(10)
Debug.Print lambda.Run(11) and the following infinite loops: Dim lambda As stdLambda
Set lambda = stdLambda.CreateMultiline(Array( _
"let x = $1", _
"let i = 0", _
"For i = 0 to 10+2 Step +2", _
" let x = x / 2", _
"next" _
))
Debug.Print lambda.Run(9)
Debug.Print lambda.Run(10)
Debug.Print lambda.Run(11) Also not a fan that you have to define the variable first else it will fail. I.E. Dim forloop As stdLambda
Set forloop = stdLambda.CreateMultiline(Array( _
"let i = 0", _
"let x = 0", _
"for i = 1 To 10", _
" let x = 2 + i", _
"next"))
Debug.Print forloop.Run() Works but Dim forloop As stdLambda
Set forloop = stdLambda.CreateMultiline(Array( _
"let x = 0", _
"for i = 1 To 10", _
" let x = 2 + i", _
"next"))
Debug.Print forloop.Run() fails... Ultimately syntax should be: for {var} = {expr} to {expr} Step {expr}
{block}
next |
ouch. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did another small review:
for-loop
I have issues with the lack of re-use of existing code for this one. In my mind I would have liked to see parseExpression
called a lot more than it actually was. Additionally, there is a lot of peek
going on, where I think they would be better utilised as optConsume
s. Not a fan of this being seperated over multiple parser functions either. Consolidation into 1 function would be ideal in my head because unless these functions are helpers for other common tasks and patterns. E.G. Happy to have a function which defines a variable which is re-used multiple locations in the codebase. Not happy for a function which is only called by 1 other function.
do-loop
No major issues with the way this was implemented, well done! 😊
src/stdLambda.cls
Outdated
'@param {ByRef Token()} Array of Tokens | ||
'@param {ByVal NewToken} New Value | ||
'@param {ByVal Index} Position where to put in NewToken | ||
Private Function InsertToken(ByRef Tokens() As Token, ByVal NewToken As Token, ByVal Index As Long) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: NewToken
cannot be passed ByVal
- compile error
src/stdLambda.cls
Outdated
Dim Condition As TokenDefinition | ||
Dim NewToken As token | ||
defs = getTokenDefinitions() | ||
If peek("step", 4) Then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my mind, this should be an optConsume
src/stdLambda.cls
Outdated
defs = getTokenDefinitions() | ||
If peek("step", 4) Then | ||
Select Case True | ||
Case peek("add" , 5) : Operator = "add" : OperatorValue = "+": Increment = this.Tokens(this.iTokenIndex + 5).value: Condition = getTokenDefinitionByName(defs, "greaterThan") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in my mind this should be an optConsume too. And we should parse a regular value/expression directly afterwards.
e.g. for i = 1 to 10 step 11+32*someFunc()
src/stdLambda.cls
Outdated
Private Sub parseFlowPriority3() | ||
Dim varName As String, Operator As String, OperatorValue As String, Increment As String | ||
If optConsume("for") Then | ||
Call ForLoopLet() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not too big a fan of for loop being spread over many function calls. The functions that were created aren't really re-usable for anything other than the for-loop in the end, so it feels unnecessary in my head?
1. For loops now dont add tokens anymore but create operations directly. 2. Add "exit" as a token "exit" works by pushing calling function (do, for, if, fun) and creating a jump operation, which the next calling function will set the operation value. 3. Combined for loop-procedures into 1 big procedure 4. Added do-loops with while|until at the end 5. Disabled if-statement returnvalue for now.
Im sorry if my pull requests are a mess, im new to this. I updated the code for the points mentioned for you. ' Very creative names i know
Public Sub Testing()
Dim i As Long
For i = 0 To 20
Debug.Print i & "-------------------------------"
Debug.Print "TestForLoop " & i & ": " & TestForLoop(i)
Debug.Print "TestIfInUbound " & i & ": " & TestIfInUbound(i)
Debug.Print "TestParantInUbound " & i & ": " & TestParantInUbound(i)
Debug.Print "TestExprInUbound " & i & ": " & TestExprInUbound(i)
Debug.Print "TestForLoopPredeclared " & i & ": " & TestForLoopPredeclared(i)
Debug.Print "TestFunctionInForLoop " & i & ": " & TestFunctionInForLoop(i)
' Debug.Print "TestFunctionInForLoop2 " & i & ": " & TestFunctionInForLoop2(i)
Debug.Print "TestFunctionInDoLoop " & i & ": " & TestFunctionInDoLoop(i)
Debug.Print "TestFunctionInDoLoopWithWhileAtEnd " & i & ": " & TestFunctionInDoLoopWithWhileAtEnd(i)
Debug.Print "TestFunctionInDoLoopExit " & i & ": " & TestFunctionInDoLoopExit(i)
Next i
End Sub
Function TestForLoop(index As Long)
Static Lambda As stdLambda
If Lambda Is Nothing Then Set Lambda = stdLambda.CreateMultiline(Array( _
"for i = 1 To $1", _
" let x = 2 + i", _
"next"))
TestForLoop = Lambda.Run(index)
End Function
Function TestIfInUbound(index As Long)
Static Lambda As stdLambda
If Lambda Is Nothing Then Set Lambda = stdLambda.CreateMultiline(Array( _
"let x = $1", _
"For i = 0 to If x > 10 Then 10 Else 20 End Step +2", _
" let x = x / 2", _
"next" _
))
TestIfInUbound = Lambda.Run(index)
End Function
Function TestParantInUbound(index As Long)
Static Lambda As stdLambda
If Lambda Is Nothing Then Set Lambda = stdLambda.CreateMultiline(Array( _
"let x = $1", _
"let i = 0", _
"For i = 0 to (10+2) Step +2", _
" let x = x / 2", _
"next" _
))
TestParantInUbound = Lambda.Run(index)
End Function
Function TestExprInUbound(index As Long)
Static Lambda As stdLambda
If Lambda Is Nothing Then Set Lambda = stdLambda.CreateMultiline(Array( _
"let x = $1", _
"let i = 0", _
"For i = 0 to 10+2 Step +2", _
" let x = x / 2", _
"next" _
))
TestExprInUbound = Lambda.Run(index)
End Function
Function TestForLoopPredeclared(index As Long)
Static Lambda As stdLambda
If Lambda Is Nothing Then Set Lambda = stdLambda.CreateMultiline(Array( _
"let i = 0", _
"let x = 0", _
"for i = 1 To $1", _
" let x = 2 + i", _
"next"))
TestForLoopPredeclared = Lambda.Run(index)
End Function
Function TestFunctionInForLoop(index As Long)
Static Lambda As stdLambda
If Lambda Is Nothing Then Set Lambda = stdLambda.CreateMultiline(Array( _
"let x = 0", _
"fun fib(v)", _
" if v<=1 then", _
" v", _
" else ", _
" fib(v-2) + fib(v-1)", _
" end", _
"end", _
" ", _
"for i = 1 To 10", _
" let x = x + fib($1)", _
"next"))
TestFunctionInForLoop = Lambda.Run(index)
End Function
' This will not work, as x is not defined previously
' meaning the moment lambda tries to do let x = x + duuble($1) it will view the second x as "x", since it wasnt initialised before
Function TestFunctionInForLoop2(index As Long)
Static Lambda As stdLambda
If Lambda Is Nothing Then Set Lambda = stdLambda.CreateMultiline(Array( _
"fun duuble(v)", _
" let v * 2", _
"end", _
" ", _
"For i = 1 To $1", _
" let x = x + duuble($1)", _
"next"))
TestFunctionInForLoop2 = Lambda.Run(index)
End Function
Function TestFunctionInDoLoop(index As Long)
Static Lambda As stdLambda
If Lambda Is Nothing Then Set Lambda = stdLambda.CreateMultiline(Array( _
"let x = 1", _
"let i = 0", _
"fun duuble(v)", _
" let v * 2", _
"end", _
" ", _
"Do While i < $1", _
" let i = i + 1", _
" let x = x + duuble($1)", _
"loop"))
TestFunctionInDoLoop = Lambda.Run(index)
End Function
Function TestFunctionInDoLoopWithWhileAtEnd(index As Long)
Static Lambda As stdLambda
If Lambda Is Nothing Then Set Lambda = stdLambda.CreateMultiline(Array( _
"let x = 1", _
"let i = 0", _
"fun duuble(v)", _
" let v * 2", _
"end", _
" ", _
"Do", _
" let i = i + 1", _
" let x = x + duuble($1)", _
"loop While i < $1"))
TestFunctionInDoLoopWithWhileAtEnd = Lambda.Run(index)
End Function
' This will not work at the moment, as i dont know thow to find the index to jump to depending on where it is at the moment
Function TestFunctionInDoLoopExit(index As Long)
Static Lambda As stdLambda
If Lambda Is Nothing Then Set Lambda = stdLambda.CreateMultiline(Array( _
"let x = 1", _
"let i = 0", _
"Do", _
" let i = i + 1", _
"if $1 > 10 then Exit do end", _
" let x = x * 2", _
"loop While i < $1"))
TestFunctionInDoLoopExit = Lambda.Run(index)
End Function
|
Thanks for the extensive tests, I'm not ignoring this PR, will look at it hopefully this next week. Just have a lot going on at the moment! 👍 No need to apologise either! There is only one way to learn and that's by doing 👍 |
Hi @Almesi I will try to get to this PR soon, recently there were pretty large scale changes to the stdLambda VM which have boosted speed significantly, and it's re-reminded me of how the VM works. So while it's fresh I think looking at this PR makes a lot of sense! But I will have to make some changes to your PR as the VM upgrade was pretty major👍 |
@@ -738,7 +759,7 @@ Private Sub parseFlowPriority1() | |||
this.stackSize = size | |||
|
|||
If optConsume("end") Then | |||
Call addOperation(oPush, , 0, 1) 'Expressions should always return a value | |||
' Why should it always return? I disabled it for now because in a loop it keeps pushing onto the stack Call addOperation(oPush, , 0, 1) 'Expressions should always return a value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removing this from this function has me very nervous. I'm not sure what the side effects would be of removing this.
In order to remove from the stack you can add your own cleanup in the loop. Either using iPop
or iMerge
.
2 Things are added: Do loops and for loops.
Do loops work by running an infinite loop through a jump operation until a statement is true.
For loops work by running the loop n times.
Do-Loop syntax is the same as VBAs
For-Loops are the same too, only that putting a word behind "next" will do nothing useful. Just write "next" at the end for that loop.
Both loops need all their variables defined before the loop.
If not, an error occures where the variable-name will be pushed onto stack instead of its value.
I added some new subs to work with these two loops. I did not change anything major in the operation-creation.
Only exception to that is how for loops are interpreted, as they requiere more inline creation of tokens. (for example it needs i = i + 1 to increment its index, otherwise it would run infinetly.
Mind the line 1082.
This is a proof of concept, feedback would be nice.