# New Ticket Created by Bruce Keeler # Please include the string: [perl #73244] # in the subject line of all future correspondence about this issue. # <URL: http://rt.perl.org/rt3/Ticket/Display.html?id=73244 >
The attached patch is also available in the 'grammar' branch of my github fork (bkeeler/rakudo). I'll keep that branch up to date to make sure it applies cleanly. Notes: * regex_declarator now calls regex_def, which is prototyped over 'rule', 'token' and 'regex'. This matches STD.pm * The <subrule($a, $b)> form of passing arguments to subrules now uses the Rakudo arglist rule, and so can take arbitrary expressions. The <subrule: 1, 2> form cannot at preset use the rakudo arglist, as EXPR tries to eat the closing angle. The colon form is limited to a list of simple literals. * builtins/Grammar.pir has been rewritten as core/Grammar.pm * Signatures may be applied to regexes. Parameters may be referenced in closures within the regex. * Named regexes may not be declared outside of a grammar or class. * The <Foo::Bar> form of calling subrules in another grammar does not work. This will require changes to the regex engine. Unfortunately, this holds up a number of tests that would otherwise pass. * S05-grammar/action-stubs.t and S05-grammar/methods.t now pass, as well as two new test files S05-grammar/protos.t and S05-grammar/signatures. * I attempted to factor out common code between regex_def and method_def, but ran into problems and backed out. I'll take another crack at this sometime soon.
From 1d3b996dee09880c20dbe40dab96158ba1dfe220 Mon Sep 17 00:00:00 2001 From: Bruce Keeler <br...@drangle.com> Date: Sun, 28 Feb 2010 20:36:20 -0800 Subject: [PATCH] Implementation of grammars --- build/Makefile.in | 2 +- src/Perl6/Actions.pm | 186 +++++++++++++++++++++++++++++++++++--------- src/Perl6/Grammar.pm | 44 ++++++++--- src/builtins/Grammar.pir | 94 ---------------------- src/core/Grammar.pm | 16 ++++ src/metamodel/ClassHOW.pir | 3 +- t/spectest.data | 6 +- 7 files changed, 206 insertions(+), 145 deletions(-) delete mode 100644 src/builtins/Grammar.pir create mode 100644 src/core/Grammar.pm diff --git a/build/Makefile.in b/build/Makefile.in index edfb97c..5a7a98b 100644 --- a/build/Makefile.in +++ b/build/Makefile.in @@ -98,7 +98,6 @@ BUILTINS_PIR = \ src/builtins/EMPTY.pir \ src/builtins/ParrotIter.pir \ src/builtins/List.pir \ - src/builtins/Grammar.pir \ src/builtins/Parcel.pir \ src/builtins/Bool.pir \ src/builtins/Int.pir \ @@ -190,6 +189,7 @@ CORE_SOURCES = \ src/core/Block.pm \ src/core/Regex.pm \ src/core/Junction.pm \ + src/core/Grammar.pm \ src/core/system.pm \ src/cheats/match-bool.pm \ src/cheats/setup-io.pm \ diff --git a/src/Perl6/Actions.pm b/src/Perl6/Actions.pm index 5c65c0a..3ee710a 100644 --- a/src/Perl6/Actions.pm +++ b/src/Perl6/Actions.pm @@ -1035,33 +1035,135 @@ method method_def($/) { make $past; } -method regex_declarator($/, $key?) { - if $key ne 'open' { - # Create regex code object. - # XXX TODO: token/regex/rule differences, signatures, traits. - my $past := Regex::P6Regex::Actions::buildsub($<p6regex>.ast); - $past := create_code_object($past, 'Regex', 0, ''); - - # Install in lexpad or namespace. XXX Need & on start of name? - my $name := ~$<deflongname>; - if $*SCOPE ne 'our' { - @BLOCK[0][0].push(PAST::Var.new( :name($name), :isdecl(1), - :viviself($past), :scope('lexical') ) ); - @BLOCK[0].symbol($name, :scope('lexical') ); - } +our %REGEX_MODIFIERS; +method regex_declarator:sym<regex>($/, $key?) { + if ($key) { + my %h; + %REGEX_MODIFIERS := %h; + } else { + make $<regex_def>.ast; + } +} - # Otherwise, package scoped; add something to loadinit to install them. - else { - @PACKAGE[0].block.loadinit.push(PAST::Op.new( - :pasttype('bind'), - PAST::Var.new( :name($name), :scope('package') ), - $past - )); - @BLOCK[0].symbol($name, :scope('package') ); +method regex_declarator:sym<token>($/, $key?) { + if ($key) { + my %h; + %h<r> := 1; + %REGEX_MODIFIERS := %h; + } else { + make $<regex_def>.ast; + } +} + +method regex_declarator:sym<rule>($/, $key?) { + if ($key) { + my %h; + %h<r> := 1; %h<s> :=1; + %REGEX_MODIFIERS := %h; + } else { + make $<regex_def>.ast; + } +} + +method regex_def($/, $key?) { + my $name := ~$<deflongname>[0]; + my @MODIFIERS := Q:PIR { + %r = get_hll_global ['Regex';'P6Regex';'Actions'], '@MODIFIERS' + }; + my $past; + if $key eq 'open' { + @MODIFIERS.unshift(%REGEX_MODIFIERS); + # The following is so that <sym> can work + Q:PIR { + $P0 = find_lex '$name' + set_hll_global ['Regex';'P6Regex';'Actions'], '$REGEXNAME', $P0 + }; + return 0; + } elsif $*MULTINESS eq 'proto' { + @MODIFIERS.shift; + @BLOCK.shift; + unless ($name) { + $/.CURSOR.panic('proto ' ~ ~$<sym> ~ 's cannot be anonymous'); + } +# $/.CURSOR.panic('proto ' ~ ~$<sym> ~ 's not implemented yet'); + our @PACKAGE; + unless +...@package { + $/.CURSOR.panic("Can not declare named " ~ ~$<sym> ~ " outside of a package"); + } + my %table; + %table := @PACKAGE[0].methods(); + unless %table{$name} { my %tmp; %table{$name} := %tmp; } + if %table{$name} { + $/.CURSOR.panic('Cannot declare proto ' ~ ~$<sym> ~ ' ' ~ $name ~ + ' when another with this name was already declared'); } + %table{$name}<code_ref> := + create_code_object( + PAST::Block.new( :name($name), + PAST::Op.new( + PAST::Var.new( :name('self'), :scope('register') ), + $name, + :name('!protoregex'), + :pasttype('callmethod') + ), + :lexical(0), + :blocktype('method'), + :pirflags(':anon'), + :node($/) + ), + 'Regex', 0, ''); + %table{'!PREFIX__' ~ $name}<code_ref> := + create_code_object( + PAST::Block.new( :name('!PREFIX__' ~ $name), + PAST::Op.new( + PAST::Var.new( :name('self'), :scope('register') ), + $name, + :name('!PREFIX__!protoregex'), + :pasttype('callmethod') + ), + :blocktype('method'), + :pirflags(':anon'), + :lexical(0), + :node($/) + ), + 'Regex', 0, ''); + } else { + @MODIFIERS.shift; + $past := Regex::P6Regex::Actions::buildsub($<p6regex>.ast, @BLOCK.shift); + $past.unshift(PAST::Op.new( + :pasttype('inline'), + :inline(" .local pmc self\n self = find_lex 'self'") + )); + my $sig := $<signature> ?? $<signature>[0].ast !! Perl6::Compiler::Signature.new(); + $sig.add_invocant(); + $sig.set_default_parameter_type('Any'); + $past[0].unshift(PAST::Var.new( :name('self'), :scope('lexical'), :isdecl(1), :viviself(sigiltype('$')) )); + $past.symbol('self', :scope('lexical')); + my $sig_setup_block := add_signature($past, $sig, 1); + $past.name($name); + $past.blocktype("declaration"); + # If the methods are not :anon they'll conflict at class composition time. + $past.pirflags(':anon'); + $past := create_code_object($past, 'Regex', 0, $sig_setup_block); + if ($name) { + our @PACKAGE; + unless +...@package { + $/.CURSOR.panic("Can not declare named " ~ ~$<sym> ~ " outside of a package"); + } + my %table; + %table := @PACKAGE[0].methods(); + unless %table{$name} { my %tmp; %table{$name} := %tmp; } - make PAST::Var.new( :name($name) ); + if %table{$name} { + $/.CURSOR.panic('Cannot declare ' ~ ~$<sym> ~ ' ' ~ $name ~ + ' when another with this name was already declared'); + } + %table{$name}<code_ref> := $past; + make PAST::Stmts.new(); + return 0; + } } + make $past; } method type_declarator:sym<enum>($/) { @@ -1949,23 +2051,31 @@ class Perl6::RegexActions is Regex::P6Regex::Actions { method codeblock($/) { my $block := $<block>.ast; $block.blocktype('immediate'); - my $past := - PAST::Regex.new( - PAST::Stmts.new( + make bindmatch($block); + } + + method p6arglist($/) { + my $arglist := $<arglist>.ast; +# make bindmatch($arglist); + make $arglist; + } + + sub bindmatch($past) { + PAST::Regex.new( + PAST::Stmts.new( + PAST::Op.new( + PAST::Var.new( :name('$/') ), PAST::Op.new( - PAST::Var.new( :name('$/') ), - PAST::Op.new( - PAST::Var.new( :name('$¢') ), - :name('MATCH'), - :pasttype('callmethod') - ), - :pasttype('bind') + PAST::Var.new( :name('$¢') ), + :name('MATCH'), + :pasttype('callmethod') ), - $block + :pasttype('bind') ), - :pasttype('pastnode') - ); - make $past; + $past + ), + :pasttype('pastnode') + ); } } @@ -2321,3 +2431,5 @@ sub prevent_null_return($block) { $block[1].push(PAST::Op.new( :name('&Nil') )); } } + +# vim: ft=perl6 diff --git a/src/Perl6/Grammar.pm b/src/Perl6/Grammar.pm index 41e1672..d325012 100644 --- a/src/Perl6/Grammar.pm +++ b/src/Perl6/Grammar.pm @@ -759,18 +759,28 @@ rule post_constraint { ] } -rule regex_declarator { +proto token regex_declarator { <...> } +token regex_declarator:sym<rule> { + <sym> {*} #= open + <regex_def> +} +token regex_declarator:sym<token> { + <sym> {*} #= open + <regex_def> +} +token regex_declarator:sym<regex> { + <sym> {*} #= open + <regex_def> +} + +rule regex_def { [ - | $<proto>=[proto] [regex|token|rule] - <deflongname> - '{' '<...>' '}'<?ENDSTMT> - | $<sym>=[regex|token|rule] - <deflongname> + <deflongname>? <.newpad> - [ '(' <signature> ')' ]? + [ [ ':'?'(' <signature> ')'] | <trait> ]* {*} #= open - '{'<p6regex=.LANG('Regex','nibbler')>'}'<?ENDSTMT> - ] + '{'[ '<...>' |<p6regex=.LANG('Regex','nibbler')>]'}'<?ENDSTMT> + ] || <.panic: "Malformed regex"> } proto token type_declarator { <...> } @@ -1235,6 +1245,21 @@ grammar Perl6::Regex is Regex::P6Regex::Grammar { token codeblock { <block=.LANG('MAIN','block')> } + + token assertion:sym<name> { + $<longname>=[\w+] + [ + | <?before '>'> + | '=' <assertion> + | ':' <arglist> + | '(' <arglist=p6arglist> ')' + | <.normspace> <nibbler> + ]? + } + + token p6arglist { + <arglist=.LANG('MAIN','arglist')> + } } @@ -1285,4 +1310,3 @@ sub parse_name($name) { .return (list) } } - diff --git a/src/builtins/Grammar.pir b/src/builtins/Grammar.pir deleted file mode 100644 index ab4fa01..0000000 --- a/src/builtins/Grammar.pir +++ /dev/null @@ -1,94 +0,0 @@ -## $Id$ - -=head1 TITLE - -Grammar - Perl 6 Grammar class - -=head1 DESCRIPTION - -This file implements the Grammar class. - -=cut - -.sub '' :anon :init :load - .local pmc p6meta - p6meta = get_hll_global ['Mu'], '$!P6META' - p6meta.'new_class'('Grammar', 'parent'=>'Any') - - # XXX pmichaud++ needs to fix this bunch. kplzthnxbai jnthn :-) - #p6meta.'new_class'('Grammar', 'parent'=>'Match') - - #$P0 = get_root_namespace ['parrot';'PGE';'Grammar'] - #$P0 = get_class $P0 - #.const 'Sub' $P1 = 'Grammar.parse' - #$P0.'add_method'('parse', $P1) -.end - -=head2 Methods - -=over - -=item parse(string) - -Parse a string according to the TOP rule in the grammar. - -=cut - -=item parse(topic) - -Invokes the TOP rule in the grammar on the given topic. - -=cut - -.namespace ['Grammar'] -.sub 'parse' :method :subid('Grammar.parse') - .param pmc topic - .param pmc options :slurpy :named - .local pmc TOP - - # If there's a TOP rule, invoke it. - push_eh no_TOP - TOP = find_method self, "TOP" - pop_eh - .local pmc match, p6meta - p6meta = get_hll_global ['Mu'], '$!P6META' - $P0 = p6meta.'get_parrotclass'(self) - $P0 = inspect $P0, 'namespace' - $P0 = $P0.'get_name'() - $S0 = shift $P0 - $S0 = join '::', $P0 - match = TOP(topic, options :named :flat, 'grammar' => $S0) - $P0 = getinterp - $P1 = $P0['lexpad';1] - $P1['$/'] = match - .return(match) - - no_TOP: - pop_eh - 'die'("The grammar has no TOP rule to invoke.") -.end - - -=item parsefile(filename) - -Reads in the file in filename and then invokes the TOP rule in the -grammar on it. - -=cut - -.sub 'parsefile' :method - .param string filename - .param pmc options :slurpy :named - $S0 = 'slurp'(filename) - .tailcall self.'parse'($S0, options :named :flat) -.end - -=back - -=cut - -# Local Variables: -# mode: pir -# fill-column: 100 -# End: -# vim: expandtab shiftwidth=4 ft=pir: diff --git a/src/core/Grammar.pm b/src/core/Grammar.pm new file mode 100644 index 0000000..f045243 --- /dev/null +++ b/src/core/Grammar.pm @@ -0,0 +1,16 @@ +class Grammar is Regex::Cursor { + method parsefile($file, *%options) { + my $fh = open($file, :r) + || die "$file: $!"; + my $str = $fh.slurp; + self.parse($str, %options); + } +} + +our sub make($ast) { + Q:PIR { + $P0 = find_dynamic_lex '$/' + $P1 = find_lex '$ast' + $P0.'!make'($P1) + } +} diff --git a/src/metamodel/ClassHOW.pir b/src/metamodel/ClassHOW.pir index a9d338a..2f51316 100644 --- a/src/metamodel/ClassHOW.pir +++ b/src/metamodel/ClassHOW.pir @@ -108,7 +108,8 @@ Creates a new instance of the meta-class and returns it in an associated # Stash in metaclass instance. have_parrotclass: - how = new ['ClassHOW'] + $P0 = typeof self + how = new [$P0] setattribute how, 'parrotclass', parrotclass $P0 = root_new ['parrot';'ResizablePMCArray'] setattribute how, '$!composees', $P0 diff --git a/t/spectest.data b/t/spectest.data index 6d16ff3..8ecca34 100644 --- a/t/spectest.data +++ b/t/spectest.data @@ -220,11 +220,13 @@ S04-statements/while.t # S05-capture/dot.t # S05-capture/named.t # S05-capture/subrule.t -# S05-grammar/action-stubs.t +S05-grammar/action-stubs.t # S05-grammar/inheritance.t -# S05-grammar/methods.t +S05-grammar/methods.t # S05-grammar/namespace.t # S05-grammar/parse_and_parsefile.t +S05-grammar/protos.t +S05-grammar/signatures.t # S05-grammar/ws.t # S05-mass/named-chars.t # icu # S05-mass/properties-block.t # icu -- 1.7.0