goboscript icon indicating copy to clipboard operation
goboscript copied to clipboard

feat: recursive local variables

Open faretek1 opened this issue 7 months ago • 5 comments

The lack of recursion in goboscript (only tail recursion) reduces the flexibility and power of functions/procs in gs. This is because local vars do not belong to a specific 'frame' or 'level' of the function. This post provides a simple concept for how local variables could work (efficiently) in gs.

[!NOTE] Recursive local vars should only be allowed with warped (not nowarp) procs and funcs, because otherwise there could be memory conflicts

Syntax

example syntax (each local var is explicitly made into a recursive local var):

func foo(a) {
    rloc c = ...;
    ...
}

or (function is assigned as recursive, so all local variables are also recursive):

recur func foo(a) {
    local c = ...;
    ...
}

concept

  1. For each recursive local variable, make a list (it also has to be local to the function.) e.g. In this case, rloc c could generate a list called: foo: c (note this would conflict with local lists but that is a rejected feature)

  2. At the start of the func or proc, add an empty item to each listNote that this *could* be optimised to only add to the list when the variable is first assigned, but then checks for recursion and possibilities of not being initialised (e.g. in an if statement). But this does not matter because if you are using recursive local vars, one shouldn't expect it to be that micro-optimised anyway
  3. access the rloc variables using the 'last' item of list (using 'last' instead of item[length list] would reduce block count and improve efficiency in vanilla scratch): Image

  4. At the end of the proc, delete the last item of each of the rloc lists. This may happen just before a stop_this_script block, or just before the stop_this_script block in the return keyword's codegen, or just when a proc ends naturally Image

things that would have to be handled differently (kind of obvious)

  • c = "string"; -> Image

  • c += 12; -> Image

  • say c; -> Image

faretek1 avatar Jul 19 '25 14:07 faretek1

IIRC, "replace item X of Y with Z" is really slow. I could be wrong. What if the variable needs to be a list?

Bituvo avatar Jul 19 '25 18:07 Bituvo

  1. it's not slow - what is slow is list insertion - hence the use of item last of list rather than item 1 of list and using an insertion
  2. goboscript doesnt have local lists. if you want to make sure these will never conflict with local lists, were they to be implemented, just add a prefix 'rloc ' inside of the list name, e.g. foo: rloc c

faretek1 avatar Jul 19 '25 21:07 faretek1

recursion should be detected and all local variables should be made into recursive variables instead of adding a new keyword and scope

aspizu avatar Jul 20 '25 02:07 aspizu

that means you would have to check for indirect recursion as well, e.g. foo calls bar, and bar calls foo, except for some certain condition

faretek1 avatar Jul 20 '25 17:07 faretek1

im already doing that, to detect unused functions

aspizu avatar Jul 20 '25 18:07 aspizu