in reply to Re^4: Which internal DSL are there in Perl? (Domain Specific Languages - Part 1)
in thread Which internal DSL are there in Perl? (Domain Specific Languages - Part 1)

All your html/xml elements take a code-block as *{$_} = sub(&) { _elem($name, @_) }; and should per default memorize the result and return the pre-calculated result.

How would you do that? In the anonsub, I can inspect @_, but how would you determine what _elem() returns, without that subroutine being called? I can't think of a way to tell whether the codepath is entirely static.

But memoizing should be done per DTD inside HTML::Writer, so import() doesn't do the DTD parsing again and again.

The most obvious solution is to use a package , something like ...

TIMTOWTDI. The prescripted usage could be

package Test; my $foo = "my \$foo"; our $bar = "our \$bar"; my $pack = __PACKAGE__; sub baz { "$_[0] called Test::baz" } package Page { use HTML::Writer qw(xhtml1-transitional.dtd); push local @ISA, $pack; render { HTML { HEAD { TITLE { "foo bar"}; }; BODY { class_ "ugly"; onload_ "javascript: mumble()"; DIV { class_ "foo"; id_ "bar"; t "If in doubt, mumble."; IMG { src_ "foo.jpg" }; }; TABLE { my $c; for ($foo, $bar, Page->baz) { TR { TD { $_ }; TD { $c++}; } } }; DIV { class_ "bar"; t "End of that." }; } }; }; };

which constrains the DSL symbols to the Page package, whilst having the outer context visible.

Other ways are possible, of course. More thinking required.

The re-evaling would introduce yet more overhead, see 1) performance...

update: in the above example, in package Test there is my $pack = __PACKAGE__;. Is there a way to get at the name of the lexically surrounding package inside package Page ?

perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'

Replies are listed 'Best First'.
Re^6: Which internal DSL are there in Perl? (Domain Specific Languages - Part 1)
by soonix (Chancellor) on Aug 04, 2017 at 13:16 UTC
    Is there a way to get at the name of the lexically surrounding package inside package Page ?
    To further beech's hint in the CB: perlmod says
    The scope of the package declaration is … until the next package declaration.
    which basically means packages can't surround each other, your packages Test and Page are on the same level.
      packages can't surround each other, your packages Test and Page are on the same level.

      I know that packages don't nest, and that namespaces are not organized in a hierarchy. What I am after is information about when package switching occurs, and after switching, whence. This information must be known to the compiler to restore the previous package's scope after the current package's scope ends:

      package Foo { ... package Bar { ... # I want to know 'whence' here }; ... # package Foo resumed. ... # the compiler knows about the package to be resumed. ... # how can I use that information inside 'Bar'? };
      perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
      The ellipses are enough to make that quote inaccurate. This is a package with a block, which means it continues until the end of the block. Which means that at the end of the block, things go back to being in the surrounding package.
        I don't think so. Yes, it's a partial quote, but what I left off (end of surrounding block etc.) doesn't change much. When a package begins, the previous package ends. As I understand it, at end of enclosing block, the package ends, and you are in package main.
        Update: I was completely wrong, see below...
Re^6: Which internal DSL are there in Perl? (Domain Specific Languages - Part 1)
by LanX (Saint) on Aug 04, 2017 at 18:50 UTC
    I'm not sure why you are trying to load subs from the outer package as methods but you could basically import to the target DSL package without exporting to the calling package.

    You basically can export the whole DSL logic and anything else to "Page" and reuse the package multiple times.

    package Pack; use strict; use warnings; sub bla { warn ((caller(0))[3]); } use Import2Package 'DTD' => 'Page'; package Page { Page->bla(); }

    exec "perl import2package.pl" unless caller(); use strict; use warnings; package Import2Package; sub import { my ($self, $dtd, $pack) =@_; my $super = (caller())[0]; no strict 'refs'; push @{"${pack}::ISA"}, $super; } 1;

    Pack::bla at import2package.pl line 7.

    update

    of course TIMTOWTDI.

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

      > of course TIMTOWTDI.

      Autoload rules! =)

      package OuterPkg; use strict; use warnings; use Carp qw/cluck/; sub foo { my $sub =(caller(0))[3]; $"=","; return "Calling $sub(@_)"; } use Import2Package 'DTD' => 'myDSL'; package myDSL { print TABLE { TR { TD {1}, TD{foo()}, } } } # TABLE(); # not visible in outer Package

      exec "perl OuterPkg.pl" unless caller(); use strict; use warnings; use Carp qw/cluck/; package Import2Package; sub delegate_subs { my ( $pkg, $super )=@_; use vars '$AUTOLOAD'; no strict 'refs'; *{${pkg}."::AUTOLOAD"} = sub { my $delegate = $AUTOLOAD; $delegate =~ s/.*:://; # strip packagename goto &{"${super}::${delegate}"}; } } sub install_vocab { my ( $pkg, $super ) = @_; my @vocab = qw/TABLE TR TD/; my $level = 0; no strict 'refs'; for my $CMD (@vocab) { *{"${pkg}::$CMD"} = sub (&) { $level++; my @inside = $_[0]->(); $level--; my $indent = "\n" . "\t" x $level; my $tag =lc($CMD); return join "",map {"<$tag>$_</$tag>"} @inside; } } *{"${pkg}::render"} = sub { print @_ }; } sub import { my ($self, $dtd, $pkg) =@_; my $super = (caller())[0]; delegate_subs $pkg => $super; install_vocab $pkg, $super; } 1;

      <table><tr><td>1</td></tr><tr><td>Calling OuterPkg::foo()</td></tr></t +able>

      Cheers Rolf
      (addicted to the Perl Programming Language and ☆☆☆☆ :)
      Je suis Charlie!

Re^6: Which internal DSL are there in Perl? (Domain Specific Languages - Part 1)
by LanX (Saint) on Aug 04, 2017 at 19:30 UTC
    Another way is using a (very safe) Source Filter, which is just introducing a package XYZ; line and memorizing the original package and switching back in an unimport.

    use My::DSL; ... DSL logic ... no My::DSL;

    keep in mind that there is still this %^H hint-hash mechanism for lexically scoped exports.

    See perlpragma , though the routines are not really unimported they are just aware about the correct scope.

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

      See perlpragma, though the routines are not really unimported they are just aware about the correct scope.

      Yes, that's a good way to go. There are so many paths, so much OWTDI, and to get a good one, so many things want to be thinked about... Thanks.

      perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
Re^6: Which internal DSL are there in Perl? (Domain Specific Languages - Part 1)
by LanX (Saint) on Aug 04, 2017 at 19:42 UTC
    why not

    my $baz = sub { "$_[0] called Test::baz" }; ... package Page { ... for ( $foo, $bar, $baz->() ) { # less characters, safe mecha +nism TR { TD { $_ }; TD { $c++}; } } ... }

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

      why not
      for ( $foo, $bar, $baz->() ) { # less characters, safe mecha +nism

      Yes of course, but for the sake of TIMTOWTDI, transparent calls of functions/methods known in the scope which uses the DSL should be possible. And yes, the notation Page->baz() is a kludge. Need something else.

      perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
        You asked me how to know if a result of a code block should be cached.

        PadWalker can list closed over lexicals hence indicating possible side effects.

        Parsing for imported functions is more complicated.

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!