Skip to content

Conversation

Almesi
Copy link

@Almesi Almesi commented May 21, 2025

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.

Almesi added 2 commits May 21, 2025 15:46
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.
@Almesi
Copy link
Author

Almesi commented May 21, 2025

To test:

Public Sub Test()
    Dim doloop As stdLambda
    Set doloop = stdLambda.CreateMultiline(Array( _
        "let i = 0", _
        "let x = 0", _
        "do until i > 10", _
            "let x = 2 + i", _
            "let i = i + 1", _
        "loop")) 
    Debug.Print doloop.Run()

    Dim doloop2 As stdLambda
    Set doloop2 = stdLambda.CreateMultiline(Array( _
        "let i = 0", _
        "let x = 0", _
        "do while i =< 10", _
            "let x = 2 + i", _
            "let i = i + 1", _
        "loop"))
    Debug.Print doloop2.Run()


    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()

    Dim forloop2 As stdLambda
    Set forloop2 = stdLambda.CreateMultiline(Array( _
    "let i = 0", _
    "let x = 0", _
    "for i = 1 To 10 Step +2", _
    "let x = 2 + i", _
    "next"))
    Debug.Print forloop2.Run()

    Dim forloop3 As stdLambda
    Set forloop3 = stdLambda.CreateMultiline(Array( _
    "let i = 0", _
    "let x = 0", _
    "for let i = 1 To 10", _
    "let x = 2 + i", _
    "next"))
    Debug.Print forloop3.Run()

    Dim forloop4 As stdLambda
    Set forloop4 = stdLambda.CreateMultiline(Array( _
    "let i = 0", _
    "let x = 0", _
    "for let i = 1 To 10 Step +2", _
    "let x = 2 + i", _
    "next"))
    Debug.Print forloop4.Run()
End Sub

@sancarn
Copy link
Owner

sancarn commented May 21, 2025

Was this generated with AI? lol

@sancarn
Copy link
Owner

sancarn commented May 21, 2025

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

@Almesi
Copy link
Author

Almesi commented May 22, 2025

Was this generated with AI? lol

ouch.
Did you also test the do loop or am i to assume that that one works?

Copy link
Owner

@sancarn sancarn left a 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 optConsumes. 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! 😊

'@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)
Copy link
Owner

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

Dim Condition As TokenDefinition
Dim NewToken As token
defs = getTokenDefinitions()
If peek("step", 4) Then
Copy link
Owner

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

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")
Copy link
Owner

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()

Private Sub parseFlowPriority3()
Dim varName As String, Operator As String, OperatorValue As String, Increment As String
If optConsume("for") Then
Call ForLoopLet()
Copy link
Owner

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?

Almesi added 2 commits May 23, 2025 14:59
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.
@Almesi
Copy link
Author

Almesi commented May 23, 2025

Im sorry if my pull requests are a mess, im new to this. I updated the code for the points mentioned for you.
Here are the used tests for this request.

' 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



@sancarn
Copy link
Owner

sancarn commented May 25, 2025

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 👍

@sancarn
Copy link
Owner

sancarn commented Jun 7, 2025

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
Copy link
Owner

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants