Nested function/Phix
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
- 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"}