lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]

On 15-Dec-06, at 5:53 AM, Fabien wrote:

By "local to a function", I really mean "in the very same function, not in a nested one". You can't jump out of a function even if it's an anonymous, local one, so you can't jump through a closure boundary, unless I missed something.

You have to be careful with branch instructions in the Lua VM. The VM needs explicit notification about when a closed variable needs to be promoted to the heap.

Consider the "satisfaction" expressions which are part of the modified Lua in Aranha: (See note at bottom for notation)

<binding> ::= { <name> // ',' } '=' { <expr> // ',' }
<test>    ::= [ <binding> 'satisfies' ] <expr>
<if>      ::= 'if' { <test> 'then' <block> // 'elseif' }
                   [ 'else' <block> ]
<while>   ::= 'while' <test> 'do' <block> 'end


  if capture = s:match(pat1) satisfies #cap1 > 4 then
  elseif capture = s:match(pat2) satisfies capture then
    print "no match"

The scope of <binding> (at least in my original implementation) is the <expr> and <block>. (Some might prefer it to extend to the 'end', but that's irrelevant to this discussion.)

On the face of it, that requires very little modification of the existing code generation, but it's not quite that simple, because a local might need to be heapified in the evaluation of the test expression. I haven't found a readable piece of code which exhibits this situation, but it is syntactically possible; consider the following <test>:

  if a = f(x) satisifies     seq1:any(function(y) return y < a end)
                         and seq2:any(function(y) return y > a end)
     then ...

Now, it's unlikely that either of the inner functions will escape, but it's conceivable; the implementation of the 'any' method might choose
to save its parameter for whatever reason. Unless we can prove that
that cannot happen, 'a' needs to be heapified. Consequently, for example, the branch opcode emitted in the 'and' operator must pass through an OP_CLOSE.

This can be implemented with BREAK pseudo-ops (which is how I did it, in fact):
  'BREAKcond level, target'
expands to:
  'JUMP~cond .+3; CLOSE level; JMP target'

Of course, that could also be implemented directly in the VM, but it's probably too rare a situation to bother.

Note: Pseudo BNF:

  <foo>       non-terminal
  'foo'       terminal
  [ A ]       optional A
  { A // B }  A ( B A )*