package Pugs::Grammar::Term;
use strict;
use warnings;
use base qw(Pugs::Grammar::BaseCategory);
use Pugs::Runtime::Match;
use Pugs::Compiler::Token;
our %hash;
# <audreyt>
# $infix:<plus> plus => $infix<plus>
# :$<foo> is a special case of that
# :$/<foo> is not valid
# $/:<foo> would be the currently specced equiv
# another thought is to make :%h<foo> parse as foo=>%h<foo>
# not (h=>%h)<foo> which is likely nonsensical
*cpan_bareword = Pugs::Compiler::Token->compile( '
[ _ | <?alnum> | \: ]+
\-
[ _ | <?alnum> | \- | \. | \* ]+
<before \( | \; | \s | $ >
',
{ grammar => __PACKAGE__ }
)->code;
*perl5source = Pugs::Compiler::Token->compile( q(
( [ <!before [ ; | <?ws> ] use <?ws> v6 > . ]+ )
<-[ ;\}\)\] ]>*
{ return {
perl5source => $_[0][0]->()
} }
),
{ grammar => __PACKAGE__ }
)->code;
sub substitution {
my $grammar = shift;
return $grammar->no_match(@_) unless $_[0];
my $pos = $_[1]{p} || 0;
my $s = substr( $_[0], $pos );
my $options;
while ($s =~ s/^:(\w+)//) {
$options->{lc($1)} = 1;
}
return $grammar->no_match(@_) unless substr($s, 0 , 1) eq '/';
substr($s, 0, 1, '');
my ($extracted,$remainder) = Text::Balanced::extract_delimited( "/" . $s, "/" );
return $grammar->no_match(@_) unless length($extracted) > 0;
$extracted = substr( $extracted, 1, -1 );
my $extracted2;
($extracted2,$remainder) = Text::Balanced::extract_delimited( "/" . $remainder, "/" );
return $grammar->no_match(@_) unless length($extracted2) > 0;
$extracted2 = substr( $extracted2, 1, -1 );
return Pugs::Runtime::Match->new( {
bool => \1,
str => \$_[0],
match => [],
from => \$pos,
to => \( length($_[0]) - length($remainder) ),
capture => \{ options => $options, substitution => [$extracted, $extracted2] },
} );
};
my %openmatch = ( '/' => '/',
'{' => '}',
'!' => '!',
'\'' => '\'');
sub rx {
my $grammar = shift;
return $grammar->no_match(@_) unless $_[0];
my $options;
my $pos = $_[1]{p} || 0;
while ( substr( $_[0], $pos ) =~ m/^:(\w+)/ ) {
$options->{lc($1)} = 1;
$pos += 1 + length($1);
}
my $open = substr($_[0], $pos , 1);
#print "rx options ", keys( %$options ), ", open $open \n";
my $ret = rx_body($grammar, $_[0], { p => $pos+1, args => { open => $open } });
#print "rx match: ", Dumper($ret->data->{capture} );
${ $ret->data->{capture} }->{options} = $options if $ret;
return $ret;
}
sub rx_body {
my $grammar = shift;
use Data::Dumper;
my $open = $_[1]->{args}{open};
return $grammar->no_match(@_) unless exists $openmatch{$open};
my $pos = $_[1]{p} || 0;
my $s = substr( $_[0], $pos );
my ($extracted,$remainder) = $open eq $openmatch{$open}
? Text::Balanced::extract_delimited( $open . $s, $openmatch{$open} )
: Text::Balanced::extract_bracketed( $open . $s, $open.$openmatch{$open} );
#print "rx_body at $s got $extracted\n";
return $grammar->no_match(@_) unless length($extracted) > 0;
$extracted = substr( $extracted, 1, -1 );
return Pugs::Runtime::Match->new( {
bool => \1,
str => \$_[0],
match => [],
from => \$pos,
to => \( length($_[0]) - length($remainder) ),
capture => \{ rx => $extracted },
} );
};
*ident = Pugs::Compiler::Token->compile( q(
<[ \? \* \: ]>? # $?CALLER $*x $:x
[
[ <'::'> | <null> ]
[ _ | <?alpha> ]
[ _ | <?alnum> ]*
]+
) )->code;
*bare_ident = Pugs::Compiler::Token->compile( q(
[
[ <'::'> | <null> ]
[ _ | <?alpha> ]
[ _ | <?alnum> ]*
]+
) )->code;
*parenthesis = Pugs::Compiler::Token->compile( q^
<?ws>? <Pugs::Grammar::Expression.parse('allow_semicolon', 1)> <?ws>?
<')'>
{ return {
op1 => "(",
op2 => ")",
fixity => "circumfix",
exp1 => $_[0]{'Pugs::Grammar::Expression.parse'}->()
} }
|
<?ws>? <Pugs::Grammar::Perl6.block> <?ws>?
<')'>
{ return {
op1 => "(",
op2 => ")",
fixity => "circumfix",
exp1 => $_[0]{'Pugs::Grammar::Perl6.block'}->()
} }
|
<?ws>?
<')'>
{ return {
op1 => "(",
op2 => ")",
fixity => "circumfix",
} }
^ )->code;
*brackets = Pugs::Compiler::Token->compile( q(
<Pugs::Grammar::Infix.parse>
<']'>
{ return {
op => $_[0]{'Pugs::Grammar::Infix.parse'}->(),
reduce => 1,
} }
|
<?ws>? <Pugs::Grammar::Expression.parse> <?ws>?
<']'>
{ return {
op1 => "[",
op2 => "]",
fixity => "circumfix",
exp1 => $_[0]{'Pugs::Grammar::Expression.parse'}->()
} }
|
<?ws>? <Pugs::Grammar::Perl6.block> <?ws>?
<']'>
{ return {
op1 => "[",
op2 => "]",
fixity => "circumfix",
exp1 => $_[0]{'Pugs::Grammar::Perl6.block'}->()
} }
|
<?ws>?
<']'>
{ return {
op1 => "[",
op2 => "]",
fixity => "circumfix",
} }
) )->code;
sub recompile {
my $class = shift;
%hash = (
'$' => q(
| <before <[ \{ \[ \< \« ]> >
{ return { scalar => '$/' ,} }
| <?Pugs::Grammar::Term.ident>
{ return { scalar => '$' . $_[0] ,} }
| (\d+)
{ return {
'exp1' => {
'scalar' => '$/'
},
'exp2' => {
'int' => $/[0]()
},
'fixity' => 'postcircumfix',
'op1' => '[',
'op2' => ']',
},
}
),
'$.' => q(
<?Pugs::Grammar::Term.ident>
{ return { scalar => '$.' . $_[0]->() ,} }
),
'$/' => q(
{ return { scalar => '$/' ,} }
),
'$!' => q(
{ return { scalar => '$!' ,} }
),
'$()' => q(
{ return
{
'exp1' => {
'pos' => 2,
'scalar' => '$/'
},
'fixity' => 'prefix',
'op1' => '$',
}
}
),
'@' => q(
# XXX t/subroutines/multidimensional_arglists.t
\; <?Pugs::Grammar::Term.ident>
{ return { die => "not implemented" } }
|
<?Pugs::Grammar::Term.ident>
{ return { array => "\@" . $_[0]->() ,} }
),
'@()' => q(
{ return
{
'exp1' => {
'pos' => 2,
'scalar' => '$/'
},
'fixity' => 'prefix',
'op1' => '@',
}
}
),
'%' => q(
<?Pugs::Grammar::Term.ident>
{ return { hash => "\%" . $_[0]->() ,} }
),
'%()' => q(
{ return
{
'exp1' => {
'pos' => 2,
'scalar' => '$/'
},
'fixity' => 'prefix',
'op1' => '%',
}
}
),
'&' => q(
<?Pugs::Grammar::Term.ident>
{ return { code => "\&" . $_[0]->() ,} }
),
'(' => q(
<Pugs::Grammar::Term.parenthesis>
{ return $_[0]{'Pugs::Grammar::Term.parenthesis'}->() }
),
'[' => q(
<Pugs::Grammar::Term.brackets>
{ return $_[0]{'Pugs::Grammar::Term.brackets'}->() }
),
'{' => q(
<?ws>? <'}'>
{
return {
bare_block => { statements => [] },
} }
|
<?ws>? <Pugs::Grammar::Perl6.statements> <?ws>? <'}'>
{
#print "Term block\n";
return {
bare_block => $_[0]{'Pugs::Grammar::Perl6.statements'}->(),
} }
),
'->' => q(
[
<?ws>? <Pugs::Grammar::Perl6.signature_no_invocant> <?ws>?
\{ <?ws>? <Pugs::Grammar::Perl6.statements> <?ws>? \}
{ return {
pointy_block => $_[0]{'Pugs::Grammar::Perl6.statements'}->(),
signature => $_[0]{'Pugs::Grammar::Perl6.signature_no_invocant'}->(),
} }
|
<?ws>?
\{ <?ws>? <Pugs::Grammar::Perl6.statements> <?ws>? \}
{ return {
pointy_block => $_[0]{'Pugs::Grammar::Perl6.statements'}->(),
signature => undef,
} }
]
),
'.' => q(
# .method op
<?Pugs::Grammar::Term.ident>
{ return { dot_bareword => $_[0]->() ,} }
),
'...' => q(
{ return { term => "yada" } }
),
'???' => q(
{ return { term => "???" } }
),
'!!!' => q(
{ return { term => "!!!" } }
),
'Inf' => q(
{ return { num => "Inf" } }
),
'NaN' => q(
{ return { num => "NaN" } }
),
'self' => q(
{ return { term => "self" } }
),
'undef' => q(
{ return { term => "undef" } }
),
'my' => q(
<?ws> <Pugs::Grammar::Perl6.signature_term_type>
<?ws>? <Pugs::Grammar::Term.parse>
<?ws>? <Pugs::Grammar::Perl6.attribute>
{
return {
exp1 => $/{'Pugs::Grammar::Term.parse'}->(),
attribute => $/{'Pugs::Grammar::Perl6.attribute'}->(),
variable_declarator => "my",
type => $/{'Pugs::Grammar::Perl6.signature_term_type'}->(),
}
}
),
'our' => q(
<?ws> <Pugs::Grammar::Perl6.signature_term_type>
<?ws>? <Pugs::Grammar::Term.parse>
<?ws>? <Pugs::Grammar::Perl6.attribute>
{
return {
exp1 => $/{'Pugs::Grammar::Term.parse'}->(),
attribute => $/{'Pugs::Grammar::Perl6.attribute'}->(),
variable_declarator => "our",
type => $/{'Pugs::Grammar::Perl6.signature_term_type'}->(),
}
}
),
'has' => q(
<?ws> <Pugs::Grammar::Perl6.signature_term_type>
<?ws>? <Pugs::Grammar::Term.parse>
<?ws>? <Pugs::Grammar::Perl6.attribute>
{
return {
exp1 => $/{'Pugs::Grammar::Term.parse'}->(),
attribute => $/{'Pugs::Grammar::Perl6.attribute'}->(),
variable_declarator => "has",
type => $/{'Pugs::Grammar::Perl6.signature_term_type'}->(),
}
}
),
'state' => q(
<?ws> <Pugs::Grammar::Perl6.signature_term_type>
<?ws>? <Pugs::Grammar::Term.parse>
<?ws>? <Pugs::Grammar::Perl6.attribute>
{
return {
exp1 => $/{'Pugs::Grammar::Term.parse'}->(),
attribute => $/{'Pugs::Grammar::Perl6.attribute'}->(),
variable_declarator => "state",
type => $/{'Pugs::Grammar::Perl6.signature_term_type'}->(),
}
}
),
'constant' => q(
<?ws> <Pugs::Grammar::Perl6.signature_term_type>
<?ws>? <Pugs::Grammar::Term.parse>
<?ws>? <Pugs::Grammar::Perl6.attribute>
{
return {
exp1 => $/{'Pugs::Grammar::Term.parse'}->(),
attribute => $/{'Pugs::Grammar::Perl6.attribute'}->(),
variable_declarator => "constant",
type => $/{'Pugs::Grammar::Perl6.signature_term_type'}->(),
}
}
),
q(s) => q(
<Pugs::Grammar::Term.substitution>
{ return {
substitution => $/{'Pugs::Grammar::Term.substitution'}->(),
}
}
),
q(rx) => q(
<Pugs::Grammar::Term.rx>
{ return {
rx => $/{'Pugs::Grammar::Term.rx'}->(),
}
}
),
q(m) => q(
<Pugs::Grammar::Term.rx>
{ return {
rx => $/{'Pugs::Grammar::Term.rx'}->(),
}
}
),
q(/) => q(
<Pugs::Grammar::Term.rx_body('open','/')>
{ return {
rx => $/{'Pugs::Grammar::Term.rx_body'}->(),
}
}
),
q(perl5:) => q(
### perl5:Test::More
<Pugs::Grammar::Term.bare_ident>
{ return {
bareword => $/{'Pugs::Grammar::Term.bare_ident'}->(),
lang => 'perl5',
} }
),
q(use) => q(
# "use v5"
<?ws> v5 <?ws>?; <perl5source>
{ return $_[0]{perl5source}->()
}
|
# default
{ return { bareword => 'use' } }
),
q(do) => q(
# { print "statement do \n"; }
<?ws>
$<exp1> := <Pugs::Grammar::Perl6.statement>
{ return {
statement => 'do',
exp1 => $_[0]{exp1}->(),
} }
),
q(:) => Pugs::Compiler::Token->compile( q^
### pair - long:<name>
# :foo<bar>
([_|\w]+) \< <Pugs::Grammar::Quote.angle_quoted>
{ return {
pair => {
key => { single_quoted => $/[0]() },
value => { single_quoted => $/{'Pugs::Grammar::Quote.angle_quoted'}() },
} } }
|
# :foo(exp)
([_|\w]+) \(
<?ws>? <Pugs::Grammar::Expression.parse> <?ws>?
\)
{
return {
pair => {
key => { single_quoted => $/[0]() },
value => $/{'Pugs::Grammar::Expression.parse'}->(),
} } }
|
# :$foo
\$ ((_|\w)+)
{ return {
pair => {
key => { single_quoted => $/[0]() },
value => { scalar => '$' . $/[0]() },
} } }
|
# :$<foo>
<'$<'> ((_|\w)+) \>
{ return {
pair => {
key => { single_quoted => $/[0]() },
value => {
'exp1' => {
'scalar' => '$/'
},
'exp2' => {
'angle_quoted' => $/[0]()
},
'fixity' => 'postcircumfix',
'op1' => '<',
'op2' => '>',
},
} } }
|
# :$$<foo>
<'$$<'> ((_|\w)+) \>
{ return {
pair => {
key => { single_quoted => $/[0]() },
value =>
{
'exp1' => {
'exp1' => {
'scalar' => '$/'
},
'exp2' => {
'angle_quoted' => $/[0]()
},
'fixity' => 'postcircumfix',
'op1' => '<',
'op2' => '>',
},
'fixity' => 'prefix',
'op1' => '$',
},
} } }
|
# :foo
((_|\w)+)
{ return {
pair => {
key => { single_quoted => $/[0]() },
value => { num => 1 },
} } }
|
# :!foo
<'!'> ((_|\w)+)
{ return {
pair => {
key => { single_quoted => $/[0]() },
value => { num => 0 },
} } }
^ ),
q() => Pugs::Compiler::Token->compile( q^
### num/int
\d+
[
\.\d+
[ <[Ee]> <[+-]>? \d+ ]?
{ return { num => $() ,} }
|
<[Ee]> <[+-]>? \d+
{ return { num => $() ,} }
|
{ return { int => $() ,} }
]
|
<Pugs::Grammar::Perl6.sub_decl>
{ return $_[0]{'Pugs::Grammar::Perl6.sub_decl'}->();
}
|
<Pugs::Grammar::Perl6.rule_decl>
{ return $_[0]{'Pugs::Grammar::Perl6.rule_decl'}->();
}
|
<Pugs::Grammar::Perl6.class_decl>
{ return $_[0]{'Pugs::Grammar::Perl6.class_decl'}->();
}
|
### Test-0.0.6
<Pugs::Grammar::Term.cpan_bareword>
{ return { cpan_bareword => $/{'Pugs::Grammar::Term.cpan_bareword'}->() } }
|
### Test => ... - autoquote before '=>'
<Pugs::Grammar::Term.bare_ident>
[
<before <?ws>? \=\> >
{ return { single_quoted => $/{'Pugs::Grammar::Term.bare_ident'}->() } }
|
### Test::More
{ return { bareword => $/{'Pugs::Grammar::Term.bare_ident'}->() } }
]
^ ),
);
for my $trait ( qw(
BEGIN
| CHECK
| INIT
| START
| FIRST
| ENTER
) ) {
__PACKAGE__->add_rule(
$trait => qq(
<?ws>? <Pugs::Grammar::Perl6.block>
{ return {
trait => '$trait',
\%{ \$_[0]{'Pugs::Grammar::Perl6.block'}->() },
} }
) );
}
$class->SUPER::recompile;
}
BEGIN {
__PACKAGE__->recompile;
}
1;