Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lexical persistence & autoprinting for the REPL #1760

Closed
p6rt opened this issue May 11, 2010 · 8 comments
Closed

Lexical persistence & autoprinting for the REPL #1760

p6rt opened this issue May 11, 2010 · 8 comments
Labels

Comments

@p6rt
Copy link

p6rt commented May 11, 2010

Migrated from rt.perl.org#75030 (status was 'resolved')

Searchable as RT75030$

@p6rt
Copy link
Author

p6rt commented May 11, 2010

From @sorear

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.

@p6rt
Copy link
Author

p6rt commented May 11, 2010

From @sorear

Lexical-persistence-printing.patch
From b9c4079ccc62711f9e29f2a8d9833da27d5b14d8 Mon Sep 17 00:00:00 2001
From: Stefan O'Rear <stefanor@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

@p6rt
Copy link
Author

p6rt commented May 11, 2010

From @pmichaud

This patch is an excellent first cut, but I'm going to reject it in its
current form for now. There are several pieces I don't like about it at
the moment​:

1. I don't like that it modifies the parameters of !UNIT_START -- feels
like we should not be passing an initial subroutine there. (I might be
argued out of this one.)

2. I don't think we should suppress autoprinting of statements and
blocks. For example, the 'while' statement above _does_ return a value,
it should be printed.

3. I'm really not a fan that being in REPL mode or having autoprinting
enabled changes the AST that is produced. I think it ought to be
independent somehow. (Again, I might be able to be argued out of this one.)

It's excellent work, but I'd like to see the patch reworked a bit in
light of the above before it gets applied.

Thanks!

Pm

@p6rt
Copy link
Author

p6rt commented May 11, 2010

The RT System itself - Status changed from 'new' to 'open'

@p6rt
Copy link
Author

p6rt commented May 12, 2010

From @sorear

On Tue, May 11, 2010 at 08​:25​:47AM -0700, Patrick R. Michaud via RT wrote​:

1. I don't like that it modifies the parameters of !UNIT_START.

I see three alternatives - clone UNIT_START (DRY), inline UNIT_START in
the PAST (DRY), and unify !RESUME_HERE with !YOU_ARE_HERE (makes modules
run in the REPL setting - buggy). Do you see a better one?

2. I don't think we should suppress autoprinting of statements and
blocks. For example, the 'while' statement above _does_ return a value,
it should be printed.

The autoprint heuristic is currently "all non-nested expression statements".
It's the simplest workable one I could think of, but it definitely needs
refinement - some non-expression statements have values worth printing, and
there's no point in printing the value of 'my $x = 2' or 'say $x'. Again,
ideas welcome.

3. I'm really not a fan that being in REPL mode or having autoprinting
enabled changes the AST that is produced. I think it ought to be
independent somehow. (Again, I might be able to be argued out of this one.)

Are you suggesting moving all of this logic into PAST​::Compiler? I would
rather see it at least start in Rakudo, where it's easier to change; also
I'm not a fan of PAST​::Compiler generating completely different output from
the same input depending on a couple contextuals, whereas our parser already
is highly context sensitive (e.g. $*SCOPE). Suggestions welcome.

@p6rt
Copy link
Author

p6rt commented Sep 20, 2011

From @coke

Closing ticket as resolved - I don't think we applied the original patch, but we have REPL
autoprinting for some time now.

Thanks for the submission!

--
Will "Coke" Coleda

1 similar comment
@p6rt
Copy link
Author

p6rt commented Sep 20, 2011

From @coke

Closing ticket as resolved - I don't think we applied the original patch, but we have REPL
autoprinting for some time now.

Thanks for the submission!

--
Will "Coke" Coleda

@p6rt
Copy link
Author

p6rt commented Sep 20, 2011

@coke - Status changed from 'open' to 'resolved'

@p6rt p6rt closed this as completed Sep 20, 2011
@p6rt p6rt added the patch label Jan 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant