# 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

Reply via email to