This is a longish message describing some obstacles I'm encountering with implementing eval() in perl6, especially as it relates to handling of lexical (my) variables. IIRC there are quite a few tests in the Perl 6/Pugs test suite that expect a working eval(), so we may need this capability sooner rather than later. (Where possible we will also see about rewriting tests to avoid eval().)
Let's begin by looking at a simple eval call in Perl 6: sub foo() { my $a = 'hello'; my $b = 'say $a'; eval($b); } The argument to eval() is compiled and executed at runtime as if it were nested within the lexical scope of the block, thus the '$a' in the string ends up referring to $a of the block containing the eval(). Ignoring lexicals for a bit, a naive PIR implementation of an eval() function for perl6 could look something like: .sub 'eval' .param string source $P0 = compreg 'Perl6' $P1 = $P0.'compile'(source) .return ($P1) .end The 'compile' method above internally translates the source into a set of PIR instructions, and then calls the imcc compiler to return an invokable subroutine object. No problem, as long as we don't need lexicals. Once lexicals are involved, we need a way to say that the source code being compiled is scoped within the caller to eval. Parrot seems to be missing a few pieces needed for that. For the rest of this message, rather than worry about the details of how the perl6 compiler and the compiler tools work, let's just look at the case of creating an "eval"-like function for PIR. In other words, I want to be able to call a sub named 'eval' and pass it a string containing a pir subroutine that is to be lexically nested within the current executing sub. Thus a PIR equivalent of the Perl 6 'foo' sub above might look like: .sub 'foo' ## my $a = 'hello'; $P0 = new 'String' $P0 = 'hello' .lex '$a', $P0 ## my $b = 'say $a'; $P1 = new 'String' $P1 = <<' END' .sub 'anon' :outer('foo') .local pmc a a = find_lex '$a' say a .end END .lex '$b', $P1 ## eval($b); $P2 = find_lex '$b' 'eval'($P2) .end .sub 'eval' .param pmc code say "Code to eval:" say code say "Compiling code..." $P0 = compreg 'PIR' $P1 = $P0(code) say "Invoking code..." .return $P1() .end Running the above gives me: $ ./parrot outer.pir Code to eval: .sub 'anon' :outer('foo') .local pmc a a = find_lex '$a' say a .end Compiling code... Invoking code... Segmentation fault (core dumped) $ It appears that imcc is unable to handle :outer() flags when they cross source code boundaries or to be able to use :outer() to refer to subroutines currently stored in the symbol table. (There may also be an issue in how one would use :outer() for nameless subs, but I can come up with workarounds for that.) So, we need some way to be able to dynamically compile new subroutines that are treated as lexically nested within an existing sub. ----- One temporary workaround that I considered for this problem would be to have eval() use introspection on its caller to create a "wrapper sub" that duplicates the lexical environment of the caller, and then use that as the target of an :outer() flag when it's passed to imcc. For example, the code passed to imcc would end up looking something like: .sub 'wrapper' :lex ## get lexpads .local pmc my, caller $P0 = getinterp my = $P0['lexpad'; 0] caller = $P0['lexpad'; 1] ## copy caller's lexpad entries into my lexpad $P0 = caller['$a'] my['$a'] = $P0 $P0 = caller['$b'] my['$b'] = $P0 ## now call the wrapped sub 'anon'() .end .sub 'anon' :outer('wrapper') .local pmc a a = find_lex '$a' say a .end Unfortunately, LexPads don't seem to be open for much in the way of introspection. Although it seems to be possible to grab individual known symbols from a LexPad, there doesn't seem to be a way to get a list of all symbols in the LexPad: $ cat x.pir .sub 'foo' $P0 = new 'String' .lex '$x', $P0 $P0 = '$x value' $P1 = new 'String' .lex '$y', $P1 $P1 = '$y value' ## display my lexical symbol table bar() .end .sub 'bar' ## get the caller's lexpad .local pmc caller $P0 = getinterp caller = $P0['lexpad';1] ## direct lookup -- display caller's lexical $x (works) .local pmc x x = caller['$x'] say x ## symbol table display -- display symbols in caller's lexpad (fails) .local pmc iter iter = new 'Iterator', caller iter_loop: unless iter goto iter_end $S0 = shift iter say $S0 goto iter_loop iter_end: .end $ ./parrot x.pir $x value get_string_keyed() not implemented in class 'LexPad' current instr.: 'bar' pc 53 (x.pir:30) called from Sub 'foo' pc 19 (x.pir:10) $ Suggestions and comments on this topic welcomed. Pm