Nested function/Phix

From Rosetta Code

Archived from Nested_function#Phix
The following was written in a single afternoon, and nearly four years later I have to accept that I will likely never finish it.
I have however penned two reasonably acceptable work-arounds, that have now taken it's place.

Prior to this task, Phix had no support whatsoever for nested functions.
Instead I have taken the first baby steps and documented them.
The indicated source of the demo contains some far more detailed information.

Yes, this is pig-ugly, and incomplete. But it is a good start for anyone that needs nested functions, and shows what can be done in just a few (less than six) hours.

NB as it stands, the compiler front-end "thinks" that l_counter and sep live in the same place. They are properly separate in the runtime/VM, but the front-end will happily emit nonsense code if you let it. Update: additional checks and error messages added to 1.0.0 mitigate that somewhat, but it basically remains a fundamental truth. <lang Phix>-- demo\rosetta\Nested_function.exw

  1. ilASM{ jmp :fin
   --
   -- This is, of course, something the compiler should end up doing automatically,
   -- and this assembly, or something similar, should be hidden away in builtins/VM.
   --
 :%opGetnlv        -- [edi] := [esi] from frame edx
                   -- nb no reference counting (would be rqd)
   [32]
       sub esi,ebp         -- --> frame offset
     @@:
       mov ecx,[ebp+20]    -- ebp_prev
       cmp [ecx+8],edx     -- rtn
       jne @b
       mov eax,[esi+ecx]
       mov [edi],eax
   [64]
       sub rsi,rbp         -- --> frame offset
     @@:
       mov rcx,[rbp+40]    -- rbp_prev
       cmp [rcx+16],rdx    -- rtn
       jne @b
       mov rax,[rsi+rcx]
       mov [rdi],rax
   []
       ret
 :%opSetnlv        -- [edi] in frame edx := [esi] (zeroed)
   [32]
       sub edi,ebp         -- --> frame offset
     @@:
       mov ecx,[ebp+20]    -- ebp_prev
       cmp [ecx+8],edx     -- rtn
       jne @b
       mov eax,[esi]
       mov [edi+ecx],eax

-- mov [esi],ebx -- zero src

   [64]
       sub rdi,rbp         -- --> frame offset
     @@:
       mov rcx,[rbp+40]    -- rbp_prev
       cmp [rcx+16],rdx    -- rtn
       jne @b
       mov eax,[rsi]
       mov [rdi+rcx],rax

-- mov [rsi],rbx -- zero src

   []
       ret
   ::fin
     }

function MakeList(string sep=". ") integer counter = 0

   function MakeItem()

-- -- what we'd really like to see: -- counter += 1 -- return sprintf("%d%s%s",{counter,sep,{"first","second","third"}[counter]})

       -- bar these locals, some idea of what the compiler should be doing:
       integer l_counter
       string l_sep
       #ilASM{
           [32]
               mov edx,routine_id(MakeList)
               lea esi,[counter]
               lea edi,[l_counter]
               call :%opGetnlv
               lea esi,[sep]
               lea edi,[l_sep]
               call :%opGetnlv
           [64]
               mov rdx,routine_id(MakeList)
               lea rsi,[counter]
               lea rdi,[l_counter]
               call :%opGetnlv
               lea rsi,[sep]
               lea rdi,[l_sep]
               call :%opGetnlv
           []
             }
       l_counter += 1
       #ilASM{
           [32]
               mov edx,routine_id(MakeList)
               lea esi,[l_counter]
               lea edi,[counter]
               call :%opSetnlv
           [64]
               mov rdx,routine_id(MakeList)
               lea rsi,[l_counter]
               lea rdi,[counter]
               call :%opSetnlv
           []
             }
       string res = sprintf("%d%s%s",{l_counter,l_sep,{"first","second","third"}[l_counter]})
       #ilASM{
           [32]
               mov [l_sep],ebx     -- (in lieu of proper refcounting)
           [64]
               mov [l_sep],rbx     -- (in lieu of proper refcounting)
           []
             }
       return res              
   end function
   sequence res = {}
   for i=1 to 3 do
       res = append(res,MakeItem())
   end for
   return res

end function

?MakeList()</lang>

Output:
{"1.  first","2.  second","3.  third"}