# New Ticket Created by Stefan O'Rear # Please include the string: [perl #75030] # in the subject line of all future correspondence about this issue. # <URL: http://rt.perl.org/rt3/Ticket/Display.html?id=75030 >
> my $x = 10 10 > my $y = 1 1 > while $x { $y *= $x-- } > $y 3628800 > [*] ^10 0 > [*] 1..10 3628800 > sub fac($n) { [*] 1..$n } fac > fac 10 3628800 > $*AUTOPRINT = 0 0 > fac(20) > fac(20).say 2.43290200817664e+18 Internally, the YOU_ARE_HERE mechanism is generalized to allow continuing any lexical scope. Yes, this does mean that extremely long REPL sessions will take linear space; I'm not too worried.
>From b9c4079ccc62711f9e29f2a8d9833da27d5b14d8 Mon Sep 17 00:00:00 2001 From: Stefan O'Rear <stefa...@cox.net> Date: Tue, 4 May 2010 03:00:55 -0700 Subject: [PATCH] Lexical persistence and printing for the REPL It works by reusing and generalizing the settings machinery; each line acts as the setting for subsequent lines. The setting is also used to initialize the symbol table for compilations, so lexicals will be found. $*AUTOPRINT, a compile time contextual, causes 'say' to be wrapped around every expression statement. It is disabled in blocks and all non-REPL sources of code. Autoprinting can be toggled in the REPL itself by assigning to $*AUTOPRINT. --- src/Perl6/Actions.pm | 73 ++++++++++++++++++++++++++++++++++++------------ src/Perl6/Compiler.pir | 43 ++++++++++++++++++++++++++++ src/Perl6/Grammar.pm | 2 + src/glue/run.pir | 11 ++++++- 4 files changed, 110 insertions(+), 19 deletions(-) diff --git a/src/Perl6/Actions.pm b/src/Perl6/Actions.pm index fca683c..a0115db 100644 --- a/src/Perl6/Actions.pm +++ b/src/Perl6/Actions.pm @@ -42,17 +42,42 @@ method deflongname($/) { } method comp_unit($/, $key?) { + my $setting := $*IN_REPL ?? '!RESUME_HERE' !! '!YOU_ARE_HERE'; + # If this is the start of the unit, add an outer module. if $key eq 'open' { @PACKAGE.unshift(Perl6::Compiler::Module.new()); @PACKAGE[0].block(@BLOCK[0]); + + # Make setting lexicals available at compile-time. XXX not all of + # these should be :does_abstraction. + + my $outer := pir::get_hll_global__PS($setting); + + until pir::isnull__IP($outer) { + my $lexinfo := $outer.get_lexinfo; + + for $lexinfo -> $kv { + @BLOCK[0].symbol($kv.key, :scope('lexical'), + :does_abstraction(1)); + } + + $outer := $outer.get_outer; + } + return 1; } # Create the block for the mainline code. my $mainline := @BLOCK.shift; $mainline.push($<statementlist>.ast); - + + # In the REPL, we want to save the lexical scope so it can be reused + # in the next line + if $*IN_REPL { + $mainline[0].push(make_lexical_continuation("!RESUME_HERE")); + } + # If it's the setting, just need to run the mainline. if $*SETTING_MODE { $mainline.hll($?RAKUDO_HLL); @@ -73,6 +98,7 @@ method comp_unit($/, $key?) { PAST::Op.new( :pirop('tailcall'), PAST::Var.new( :name('!UNIT_START'), :namespace(''), :scope('package') ), + PAST::Var.new( :name($setting), :namespace(''), :scope('package') ), $mainline, PAST::Var.new( :scope('parameter'), :name('@_'), :slurpy(1) ) ) @@ -96,7 +122,8 @@ method comp_unit($/, $key?) { :pirflags(':load'), :lexical(0), :namespace(''), PAST::Op.new( :name('!UNIT_START'), :pasttype('call'), - PAST::Val.new( :value($unit) ), + PAST::Var.new( :name($setting), :namespace(''), :scope('package') ), + PAST::Val.new( :value($unit) ) ) ) ); @@ -169,6 +196,9 @@ method statement($/, $key?) { $past := PAST::Op.new($cond, $past, :pasttype(~$ml<sym>), :node($/) ); } } + if $*AUTOPRINT && !$mc && !$ml { + $past := PAST::Op.new(:pasttype('call'), :name('&say'), $past); + } } elsif $<statement_control> { $past := $<statement_control>.ast; } else { $past := 0; } @@ -594,25 +624,32 @@ method term:sym<statement_prefix>($/) { make $<statement_prefix>.ast; } method term:sym<lambda>($/) { make create_code_object($<pblock>.ast, 'Block', 0, ''); } method term:sym<sigterm>($/) { make $<sigterm>.ast; } -method term:sym<YOU_ARE_HERE>($/) { - my $past := PAST::Block.new( - :name('!YOU_ARE_HERE'), - PAST::Var.new( :name('mainline'), :scope('parameter') ), - PAST::Op.new( :pasttype('callmethod'), :name('set_outer'), - PAST::Var.new( :name('mainline'), :scope('lexical') ), - PAST::Var.new( :scope('keyed'), PAST::Op.new( :pirop('getinterp P') ), 'sub' ) - ), - PAST::Op.new( :pasttype('call'), PAST::Var.new( :name('mainline'), :scope('lexical') ) ) - ); - @BLOCK[0][0].push(PAST::Var.new( - :name('!YOU_ARE_HERE'), :isdecl(1), :viviself($past), :scope('lexical') - )); - make PAST::Op.new( :pasttype('call'), - PAST::Var.new( :name('!YOU_ARE_HERE'), :scope('lexical') ), - PAST::Block.new( ) +sub make_lexical_continuation($name) { + PAST::Op.new( + :pasttype('bind'), + PAST::Var.new( :name($name), :namespace(''), :scope('package') ), + PAST::Block.new( + :blocktype('declaration'), + :name($name), + :nsentry(0), + PAST::Var.new( :name('!mainline'), :scope('parameter') ), + PAST::Op.new( :pasttype('callmethod'), :name('set_outer'), + PAST::Var.new( :name('!mainline'), :scope('lexical') ), + PAST::Var.new( :scope('keyed'), + PAST::Op.new( :pirop('getinterp P') ), 'sub' ) + ), + PAST::Op.new( :pasttype('call'), + PAST::Var.new( :name('!mainline'), :scope('lexical') ) ) + ) ); } +method term:sym<YOU_ARE_HERE>($/) { + # the first is for modules, the second is for the REPL + @BLOCK[0][0].push(make_lexical_continuation('!YOU_ARE_HERE')); + @BLOCK[0][0].push(make_lexical_continuation('!RESUME_HERE')); +} + method name($/) { } method module_name($/) { diff --git a/src/Perl6/Compiler.pir b/src/Perl6/Compiler.pir index 4d69039..d707488 100644 --- a/src/Perl6/Compiler.pir +++ b/src/Perl6/Compiler.pir @@ -152,6 +152,11 @@ Perl6::Compiler - Perl6 compiler $P2 = new ['Array'] $P2.'!STORE'($P1) set_hll_global '@INC', $P2 + + $P0 = box 1 + $P1 = box 1 + setprop $P0, "rw", $P1 + set_hll_global '$AUTOPRINT', $P0 .end .sub load_module :method @@ -185,6 +190,44 @@ Perl6::Compiler - Perl6 compiler exit 0 .end +# Thread the "Are we in the REPL?" state down into the compiler, where it can +# be used to slightly modify behavior +.sub 'interactive' :method + .param pmc adverbs :slurpy :named + .local pmc true, super + true = box 1 + adverbs["in_repl"] = true + + super = get_class ["HLL";"Compiler"] + super = super.'find_method'('interactive') + + .tailcall super(self, adverbs :flat :named) +.end + +.sub 'parse' :method + .param pmc source + .param pmc in_repl :named("in_repl") :optional + .param pmc adverbs :slurpy :named + .local pmc autoprint + + autoprint = '!find_contextual'("$*AUTOPRINT") + .lex "$*AUTOPRINT", autoprint + + unless null in_repl goto have_in_repl + in_repl = box 0 + autoprint = box 0 + have_in_repl: + + .lex "$*IN_REPL", in_repl + .local pmc super + + super = get_class ["HLL";"Compiler"] + super = super.'find_method'('parse') + + $P0 = super(self, source, adverbs :flat :named) + .return($P0) +.end + .include 'src/gen/core.pir' # Cheats go at the end, because some of them are in the 'parrot' HLL diff --git a/src/Perl6/Grammar.pm b/src/Perl6/Grammar.pm index 04aa4b7..fdce8ad 100644 --- a/src/Perl6/Grammar.pm +++ b/src/Perl6/Grammar.pm @@ -333,6 +333,7 @@ token block($*IMPLICIT = 0) { } token blockoid { + :my $*AUTOPRINT := 0; <.finishpad> '{' ~ '}' <statementlist> <?ENDSTMT> @@ -465,6 +466,7 @@ token statement_prefix:sym<gather>{ <sym> <blorst> } token statement_prefix:sym<do> { <sym> <blorst> } token blorst { + :my $*AUTOPRINT := 0; \s <.ws> [ <?[{]> <block> | <statement> ] } diff --git a/src/glue/run.pir b/src/glue/run.pir index e54bb07..7e5a364 100644 --- a/src/glue/run.pir +++ b/src/glue/run.pir @@ -21,7 +21,16 @@ of the compilation unit. .include 'sysinfo.pasm' .include 'iglobals.pasm' +# The initialization code here is problematic. Parrot forces us +# to put it here, because the command-line arguments do not +# appear to be available anywhere else but in the :main sub; but +# in order to inject @*ARGS into the namespace, we need to use +# setting-defined types. Which in turn means that !UNIT_START +# cannot be used when compiling the setting, which leads to +# somewhat more magic than we'd really like in the compiler. + .sub '!UNIT_START' + .param pmc lex_cont .param pmc mainline .param pmc args :slurpy @@ -83,6 +92,6 @@ of the compilation unit. $P0 = mainline() .return ($P0) in_setting: - $P0 = '!YOU_ARE_HERE'(mainline) + $P0 = lex_cont(mainline) .return ($P0) .end -- 1.6.6