tern-discuss
[Top][All Lists]
Advanced

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

[tern] [Fwd: Proper Lexical Pragma Modules]


From: david nicol
Subject: [tern] [Fwd: Proper Lexical Pragma Modules]
Date: 04 Sep 2003 05:11:52 -0500

-- 
David Nicol / If at first you don't succeed, use a bigger hammer. 
                                        http://gallaghersmash.com
--- Begin Message --- Subject: Proper Lexical Pragma Modules Date: Tue, 02 Sep 2003 06:29:48 -0400
This core patch implements my idea for cheap lexical pragmas, 
as I described in

http://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/2002-04/msg02113.html
http://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/2002-04/msg02119.html

This is to be considered a trial balloon, not a finished submission.
In no particular order:

0. The patch is against 5.9.0.  

1. Writing pragmas with the new feature is very, very easy.  I think
   this is the Right Solution.  See pragma/Demo.pm and pragma/t/1.t
   for an example.

2. Most of the new code is in pp_ctl.c.  This seems to me to be the
   wrong place for it.  

   It's important for a Perl function to be able to test whether or
   not a certain pragma was in scope at the place it was called.  The
   new C function I wrote for doing that is called 'firstpragma_n'.
   Since pragmas are attached to COPs, firstpragma_n needs to look up
   the context stack to find the COP in the calling fucntion.  Perl
   has a function  for doing just that, called 'dopoptosub_at'.
   'dopoptosub_at' is defined in pp_ctl.c and is static, so
   'firstpragma_n' also had to be in pp_ctl.c.

   a. Should I leave 'firstpragma_n' and its siblings in pp_ctl.c?  Or
   b. Should I make 'dopoptosub_at' not static?  Or
   c. Is there an alternative way to do it that I overlooked?

3. Access to this functionality is through a new standard module
   called 'pragma' which provides functions for setting and examining
   pragmas.  A pragma can have arbitrary user-defined data associated
   with it; the 'pragma' module demonstrates how to make pragmas where
   this user data is a single SV.

4. It's easy to write an XS module that provides different access to
   the core functionality, and I expect people will do this.

5. As noted above, given 'pragma', it's totally trivial to write an
   all-Perl module that defines a new block-scoped pragma.
   pragma::Demo is an example of how to do this.

6. I don't really know what I'm doing in the XS world, so I would be
   grateful if people would look at what I've done for obvious
   errors.  Also this is my first time touching 'embed.fnc', so ditto.

7. I have NOT rerun Perl's test suite with this patch in place.  It
   might be a disaster.  

8. I expect it might be a good idea to convert some of the existing
   lexically scoped pragmata to use this interface, instead of
   whatever they're doing now.  I also expect it might be a good idea
   to convert Hook::LexWrap, which claims to be lexically scoped, but
   isn't.  If it were to use this new 'pragma' feature, it could be
   made lexically scoped.

9. The documentation is incomplete.

10. Eventually I would like to see runtime access to pragmas through a
    special variable, perhaps %{^Pragmas}.

11. Not all of the functionality of the pragma feature is available
   through the 'pragma' module.  For example, given:

        {
           use mypragma 'on';
           ...
           use mypragma 'off';
           ...
           # is my pragma on or off here?    # Line 377
        }

   The 'pragma' module will tell you that your pragma is off at line
   377.    But in

        {
           use somepragma 'enable-feature-A';
           ...
           use somepragma 'enable-feature-B';
           ...
           use somepragma 'disable-feature-A';
           ...
           # which features of 'somepragma' are enabled here?  # Line 377
        }

   The internal functions are sufficient to figure out that feature B
   is enabled but feature A isn't, but you will need to write some XS
   code to get at them; access to the necessary features isn't (yet)
   provided by pragma.pm.



Best regards,

-D.



--- cop.h       2003/09/01 19:08:30     1.1
+++ cop.h       2003/09/01 21:43:40
@@ -13,6 +13,20 @@
  * and thus can be used to determine our current state.
  */
 
+/* 20030901 address@hidden
+ * The pragma structure is a node in a linked list.
+ * The first two fields are always the same: a pointer to the next
+ * node in the list, and the name of the pragma.
+ * The structure may contain additional data whose type and format
+ * depends on the pragma name.  
+ * Pragmas are a GLOBAL namespace, like modules.
+ */
+typedef struct st_pragma {
+  struct st_pragma *next;
+  char *name;
+} *PRAGMA;
+
+
 struct cop {
     BASEOP
     char *     cop_label;      /* label for this construct */
@@ -28,6 +42,7 @@
     line_t      cop_line;       /* line # of this command */
     SV *       cop_warnings;   /* lexical warnings bitmask */
     SV *       cop_io;         /* lexical IO defaults */
+    PRAGMA      cop_pragmas;    /* lexical pragmas defined for this line */
 };
 
 #define Nullcop Null(COP*)
--- op.c        2003/09/01 18:34:33     1.1
+++ op.c        2003/09/01 19:32:01
@@ -1757,6 +1757,7 @@
         PL_compiling.cop_io = newSVsv(PL_compiling.cop_io) ;
         SAVEFREESV(PL_compiling.cop_io) ;
     }
+    SAVEPPTR(PL_compiling.cop_pragmas);
     return retval;
 }
 
@@ -3233,7 +3234,7 @@
         cop->cop_io = PL_curcop->cop_io;
     else
         cop->cop_io = newSVsv(PL_curcop->cop_io) ;
-
+    cop->cop_pragmas = PL_curcop->cop_pragmas ;
 
     if (PL_copline == NOLINE)
         CopLINE_set(cop, CopLINE(PL_curcop));
--- pp_ctl.c    2003/09/02 09:54:30     1.1
+++ pp_ctl.c    2003/09/02 09:59:48
@@ -3841,3 +3841,62 @@
     else
        return FALSE;
 }
+
+
+
+/*
+ * Lexical pragma utility routines for use by
+ * lexical pragma modules
+ *
+ * 20030802 address@hidden
+ */
+
+void install_pragma(PRAGMA pdata)
+{
+  /* invoked only at compile time */
+  pdata->next = PL_compiling.cop_pragmas;
+  PL_compiling.cop_pragmas = pdata;
+}
+
+PRAGMA nextpragma(char *name, PRAGMA start)
+{
+  /* invoked only at run time */
+  PRAGMA p;
+  if (*name == '\0') return start;
+  
+  for (p = start; p; p = p->next)
+    if (strEQ(p->name, name))
+      return p;
+  
+  return 0;
+}
+
+PRAGMA firstpragma_cop(char *name, COP *cop)
+{
+  /* invoked only at run time */
+  return nextpragma(name, cop->cop_pragmas);
+}
+
+PRAGMA firstpragma_n(char *name, int n)
+{
+  /* invoked only at run time */
+  register I32 cxix = dopoptosub(cxstack_ix);
+  register PERL_CONTEXT *cx;
+  COP * caller_cop;
+
+  n--;
+
+  while (n--)
+    cxix = dopoptosub_at(cxstack, cxix - 1);
+  cx = &cxstack[cxix];
+  caller_cop = cx->blk_oldcop;
+
+  return firstpragma_cop(name, caller_cop);
+}
+
+PRAGMA firstpragma(char *name)
+{
+  /* invoked only at run time */
+  return firstpragma_cop(name, PL_curcop);
+}
+
--- embed.fnc   2003/09/02 07:18:56     1.1
+++ embed.fnc   2003/09/02 08:51:56
@@ -1381,7 +1381,10 @@
 pd     |CV*    |find_runcv     |U32 *db_seqp
 p      |void   |free_tied_hv_pool
 
-
-
 END_EXTERN_C
 
+
+Ap      |void   |install_pragma |PRAGMA start
+Ap      |PRAGMA |nextpragma     |char *name   |PRAGMA start
+Ap      |PRAGMA |firstpragma    |char *name
+Ap      |PRAGMA |firstpragma_cop|char *name   |COP *cop
--- /dev/null   Fri Mar 23 23:37:44 2001
+++ ext/pragma/Makefile.PL      Tue Sep  2 06:01:54 2003
@@ -0,0 +1,18 @@
+use 5.009;
+use ExtUtils::MakeMaker;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+WriteMakefile(
+    'NAME'             => 'pragma',
+    'VERSION_FROM'     => 'pragma.pm', # finds $VERSION
+    'PREREQ_PM'                => {}, # e.g., Module::Name => 1.1
+    ($] >= 5.005 ?    ## Add these new keywords supported since 5.005
+      (
+#       ABSTRACT_FROM => 'pragma.pm', # retrieve abstract from module
+       AUTHOR     => 'Mark Jason Dominus <address@hidden>') : ()),
+#    'LIBS'            => [''], # e.g., '-lm'
+    'DEFINE'           => '', # e.g., '-DHAVE_SOMETHING'
+    'INC'              => '-I.', # e.g., '-I. -I/usr/include/other'
+       # Un-comment this if you add C files to link with later:
+    'OBJECT'           => '$(O_FILES)', # link all the C files too
+);
--- /dev/null   Fri Mar 23 23:37:44 2001
+++ ext/pragma/README   Tue Sep  2 03:13:13 2003
@@ -0,0 +1,38 @@
+Pragma version 0.01
+===================
+
+The README is used to introduce the module and provide instructions on
+how to install the module, any machine dependencies it may have (for
+example C compilers and installed libraries) and any other information
+that should be provided before the module is installed.
+
+A README file is required for CPAN modules since CPAN extracts the
+README file from a module distribution so that people browsing the
+archive can use it get an idea of the modules uses. It is usually a
+good idea to provide version information here so that people can
+decide whether fixes for the module are worth downloading.
+
+INSTALLATION
+
+To install this module type the following:
+
+   perl Makefile.PL
+   make
+   make test
+   make install
+
+DEPENDENCIES
+
+This module requires these other modules and libraries:
+
+  blah blah blah
+
+COPYRIGHT AND LICENCE
+
+Put the correct copyright and licence information here.
+
+Copyright (C) 2003 Mark Jason Dominus
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself. 
+
--- /dev/null   Fri Mar 23 23:37:44 2001
+++ ext/pragma/t/1.t    Tue Sep  2 06:19:20 2003
@@ -0,0 +1,73 @@
+# Before `make install' is performed this script should be runnable with
+# `make test'. After `make install' it should work as `perl 1.t'
+
+#########################
+
+# change 'tests => 1' to 'tests => last_test_to_print';
+
+use Test::More tests => 26;
+BEGIN { 
+  use_ok('pragma::Demo'); 
+};
+
+sub bar {
+  is (current_pragma_value('pragma::Demo'), undef, "Scope is lexical, not 
dynamic");
+}
+
+use pragma;
+
+use pragma::Demo 'x1';
+is (current_pragma_value('pragma::Demo'), '1', "Outer value is set");
+{
+  is (current_pragma_value('pragma::Demo'), '1', "Outer value is inherited");
+  use pragma::Demo 'x2';
+  is (current_pragma_value('pragma::Demo'), '2', "Inner value is set");
+  bar();
+}
+is (current_pragma_value('pragma::Demo'), '1', "Outer value is restored");
+use pragma::Demo 'x3';
+is (current_pragma_value('pragma::Demo'), '3', "Outer value is overidden");
+bar();
+
+{
+  is (current_pragma_value('pragma::Demo'), '3', "Outer value is inherited");
+  {
+    is (current_pragma_value('pragma::Demo'), '3', "Outer value is inherited");
+    use pragma::Demo 'x4';
+    is (current_pragma_value('pragma::Demo'), '4', "Inner value is 
overridden");
+    bar();
+  }   
+  is (current_pragma_value('pragma::Demo'), '3', "Inner value is restored");
+  use pragma::Demo 'x5';
+  is (current_pragma_value('pragma::Demo'), '5', "Inner value is overridden");
+  bar();
+}
+is (current_pragma_value('pragma::Demo'), '3', "Outer value is restored");
+is (current_pragma_value('SNONK'), undef, "Absent pragma is undef");
+bar();
+
+sub foo {
+  check_cpv(3);
+  use pragma::Demo 'x6';
+  check_cpv(6);
+  {
+    check_cpv(6);
+    use pragma::Demo 'x7';
+    check_cpv(7);
+    bar();
+  }
+  check_cpv(6);
+  use pragma::Demo 'x8';
+  check_cpv(8);
+  bar();
+}
+
+sub check_cpv {
+  my $expected = shift;
+  is (caller_pragma_value('pragma::Demo'), $expected, 
+        "Caller pragma value $expected");
+}
+
+foo();
+
+
--- /dev/null   Fri Mar 23 23:37:44 2001
+++ ext/pragma/pragma.pm        Tue Sep  2 06:00:47 2003
@@ -0,0 +1,162 @@
+package pragma;
+
+use 5.008;
+use strict;
+use warnings;
+
+require Exporter;
+# use AutoLoader qw(AUTOLOAD);
+
+our @ISA = qw(Exporter);
+
+# Items to export into callers namespace by default. Note: do not export
+# names by default without a very good reason. Use EXPORT_OK instead.
+# Do not simply export all your public functions/methods/constants.
+
+# This allows declaration      use Pragma3 ':all';
+# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
+# will save memory.
+our %EXPORT_TAGS = ( 'all' => [ qw(
+) ] );
+
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} },   );
+
+our @EXPORT = qw(
+install_pragma_value current_pragma_value
+                     caller_pragma_value
+);
+
+our $VERSION = '0.01';
+
+require XSLoader;
+XSLoader::load('pragma', $VERSION);
+
+1;
+__END__
+
+=head1 NAME
+
+pragma - utility functions for lexically-scoped pragmas
+
+=head1 SYNOPSIS
+
+  package MyPragma;
+  use pragma;
+
+  sub import {
+    my ($class, $value) = @_;
+
+    # check $value here...
+
+    install_pragma_value($class, $value);
+  }
+
+  1;
+
+=head1 ABSTRACT
+
+  This should be the abstract for pragma.
+  The abstract is used when making PPD (Perl Package Description) files.
+  If you don't want an ABSTRACT you should also edit Makefile.PL to
+  remove the ABSTRACT_FROM option.
+
+=head1 DESCRIPTION
+
+Every statement in a Perl program has a list of 'pragmas' associated
+with it.  The pragmas are hints to modules or to the Perl interpreter
+that certain types of behavior should be changed.  For example, a
+pragma might indicate to Perl that the C<strict 'vars'> checks should
+be performed for that statement.
+
+Each pragma has a name, which is a string, and perhaps some associated
+data.  The data can be anything, but this module assumes that the data
+will be a regular Perl scalar (which, of course, could be a reference
+to a more complex data structure).
+
+Pragmas must be set at compile time.  They can be looked up at run
+time.  See L<perlpragma> for complete details about the internals of
+pragmas and the C interface to pragmas.
+
+Pragmatic modules may inherit the C<import> subroutine from the
+C<pragma> module itself, which performs the appropriate installation.
+If you want to define a pragma called C<mypragma>, create a
+C<mypragma.pm> file as indicated in the L<"SYNOPSIS"> section above.
+Then any other part of the program may use
+
+       { 
+         ...
+          use mypragma "Carrots";
+         ...
+       }
+
+This declares that the pragma C<mypragma> is in scope to the end of
+the enclosing block.  The name of the pragma is 'mypragma'; the value
+is C<"Carrots">.  The value C<"Carrots"> will not be used by any
+module other than the C<mypragma> module.
+
+A more typical use of the pragma value would be:
+
+       { 
+         ...
+          use mypragma "on";
+         ...
+          use mypragma "off";
+          ...
+       }
+
+Or perhaps:
+
+       { 
+         ...
+          use strict 'vars';
+         ...
+          use strict 'refs', 'subs';
+          ...
+       }
+
+=head2 EXPORT
+
+=over 4
+
+=item C<install_pragma_value($name, $value)>
+
+This function associates a pragma with rest of the block that is
+presently being compiled.  The scope of the pragma is from the
+statement currently being compiled to the end of the enclosing block.
+
+The C<$name> may be used to retrieve the specified C<$value>, which
+has no special meaning
+
+C<install_pragma_value> should be called at compile time, from within
+an C<import> subroutine. .
+
+=item C<current_pragma_value($name, $value)>
+
+Looks in the pragma list for the currently executing statement for the
+first pragma named C<$name>.  If it finds one, it returns the
+corresponding C<$value>; otherwise, it returns C<undef>.  
+
+=item C<caller_pragma_value($name, $value)>
+
+Like L<"current_pragma_value">, but for the currently executing
+statement in the calling function instead of the current function.
+
+=back
+
+
+=head1 SEE ALSO
+
+L<perlvar/"%^H">
+
+=head1 AUTHOR
+
+Mark Jason Dominus, E<lt>address@hidden<gt>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2003 Mark Jason Dominus.
+
+This library is free software; you may redistribute it and/or modify
+it under the same terms as Perl itself. 
+
+=cut
--- /dev/null   Fri Mar 23 23:37:44 2001
+++ ext/pragma/pragma.xs        Tue Sep  2 05:59:51 2003
@@ -0,0 +1,62 @@
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+#include "ppport.h"
+
+typedef struct Pragma_SV_pragma {
+  PRAGMA next;
+  char *name;
+  SV *sv;
+} *Pragma_SV_PRAGMA;
+
+
+MODULE = pragma                PACKAGE = pragma
+
+
+IV install_pragma_value(name, sv)
+        char *name;
+        SV *sv;
+        CODE:
+        {
+        
+          Pragma_SV_PRAGMA p;
+
+          New(119, p, 1, struct Pragma_SV_pragma);
+          p->sv = newSVsv(sv);
+          p->name = name;
+          install_pragma((PRAGMA) p);
+          RETVAL = 1;
+        }
+        OUTPUT:
+        RETVAL
+
+SV*
+current_pragma_value(name)
+        char *name;
+        CODE:
+        {
+          Pragma_SV_PRAGMA p =
+            (Pragma_SV_PRAGMA) firstpragma(name);
+          if (p == 0 || p->sv == 0) 
+            RETVAL = &PL_sv_undef;
+          else 
+            RETVAL = newSVsv(p->sv);
+        }
+        OUTPUT:
+        RETVAL
+
+SV*
+caller_pragma_value(name)
+        char *name;
+        CODE:
+        {
+          Pragma_SV_PRAGMA p =
+            (Pragma_SV_PRAGMA) firstpragma_n(name, 1);
+          if (p == 0 || p->sv == 0) 
+            RETVAL = &PL_sv_undef;
+          else 
+            RETVAL = newSVsv(p->sv);
+        }
+        OUTPUT:
+        RETVAL
--- /dev/null   Fri Mar 23 23:37:44 2001
+++ ext/pragma/Demo/Makefile.PL Tue Sep  2 04:31:29 2003
@@ -0,0 +1,11 @@
+use 5.009;
+use strict;
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+              INC              => "-I../pragma",
+             NAME              => 'pragma::Demo',
+             VERSION_FROM      => "Demo.pm",
+             MAN3PODS  => {},
+             );
+
--- /dev/null   Fri Mar 23 23:37:44 2001
+++ ext/pragma/Demo/Demo.pm     Tue Sep  2 05:25:37 2003
@@ -0,0 +1,15 @@
+
+package pragma::Demo;
+use pragma;
+
+our $VERSION = 0.01;
+
+sub import {
+  my ($class, $arg) = @_;
+  $arg ||= 0;
+  $arg =~ tr/0-9//cd;
+#  warn "pragma::Demo::import($arg)\n";
+  install_pragma_value($class, $arg);
+}
+
+1;


--- End Message ---

reply via email to

[Prev in Thread] Current Thread [Next in Thread]