├── t ├── Statements │ ├── Expressions │ │ ├── OpListKeywordExpr │ │ │ ├── OpKeywordSpliceExpr.t │ │ │ ├── OpKeywordSortExpr.t │ │ │ ├── OpKeywordOpenExpr.t │ │ │ ├── OpKeywordSplitExpr.t │ │ │ ├── OpKeywordSayExpr.t │ │ │ ├── OpKeywordPrintExpr.t │ │ │ ├── OpKeywordBinmodeExpr.t │ │ │ ├── OpKeywordPrintfExpr.t │ │ │ ├── OpKeywordChmodExpr.t │ │ │ └── OpKeywordSprintfExpr.t │ │ ├── ExprNameNot.t │ │ ├── OpUnaryKeywordExpr │ │ │ ├── OpKeywordStatExpr.t │ │ │ └── OpKeywordReadlineExpr.t │ │ ├── Value │ │ │ ├── NonLiteral │ │ │ │ ├── SubCall.t │ │ │ │ ├── DiamondExpr.t │ │ │ │ ├── DerefVariable.t │ │ │ │ └── Variable.t │ │ │ ├── Literal │ │ │ │ └── LitString.t │ │ │ ├── ArrowDerefVariable.t │ │ │ ├── Literal.t │ │ │ └── QLikeValue.t │ │ ├── ExprComma.t │ │ ├── basic.t │ │ ├── unary.t │ │ ├── arrow.t │ │ ├── OpNullaryKeywordExpr │ │ │ └── OpKeywordSubExpr.t │ │ └── variable.t │ ├── WhileStatement.t │ ├── Ellipsis.t │ ├── PhaseStatements.t │ ├── Block.t │ ├── Condition.t │ ├── RequireStatement.t │ ├── PackageStatement.t │ ├── UseNoStatement.t │ ├── LoopStatement.t │ └── SubStatement.t └── bugs │ └── rt_132920_regex_subset.t ├── cpanfile ├── .gitignore ├── tools ├── expl └── standardize ├── .github └── workflows │ └── ci.yml ├── dist.ini ├── lib ├── Guacamole │ ├── Dumper.pm │ ├── Test.pm │ └── Deparse.pm ├── standard.pm └── Guacamole.pm ├── Changes └── README.mkdn /t/Statements/Expressions/OpListKeywordExpr/OpKeywordSpliceExpr.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses('splice @foo, 0, 2'); 6 | 7 | done_testing(); 8 | -------------------------------------------------------------------------------- /t/bugs/rt_132920_regex_subset.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses(' 6 | map { s/\\\// } @_; 7 | 1; 8 | '); 9 | 10 | done_testing(); 11 | -------------------------------------------------------------------------------- /t/Statements/Expressions/ExprNameNot.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses('not defined $foo'); 6 | parses('! defined $foo && $x = 2'); 7 | 8 | done_testing(); 9 | -------------------------------------------------------------------------------- /t/Statements/Expressions/OpUnaryKeywordExpr/OpKeywordStatExpr.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses('my $perm = (stat $fh)[2] & 07777;'); 6 | 7 | done_testing(); 8 | -------------------------------------------------------------------------------- /t/Statements/Expressions/Value/NonLiteral/SubCall.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | use Test::More; 5 | 6 | parses('a()'); 7 | parses('a::b()'); 8 | parsent('a::()'); 9 | 10 | done_testing(); 11 | -------------------------------------------------------------------------------- /t/Statements/WhileStatement.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses('while ( my ( $foo, $bar ) = splice @foo, 0, 2 ) {1}'); 6 | parses('while ( my $foo = splice @foo, 0, 1 ) {1}'); 7 | 8 | done_testing(); 9 | -------------------------------------------------------------------------------- /t/Statements/Ellipsis.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses('...'); 6 | parses('...;'); 7 | parses('while (1) {...}'); 8 | parses('while (1) {...;}'); 9 | parses('while (1) { 1; ...;}'); 10 | 11 | done_testing(); 12 | -------------------------------------------------------------------------------- /t/Statements/Expressions/ExprComma.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses('$foo = { "a" => "b" }'); 6 | parses('$foo = { "a" => "b", }'); 7 | parses('$foo = [ 1, 2 ]'); 8 | parses('$foo = [ 1, 2, ]'); 9 | parses('$foo = [ 1, ]'); 10 | 11 | done_testing(); 12 | -------------------------------------------------------------------------------- /t/Statements/Expressions/OpListKeywordExpr/OpKeywordSortExpr.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses('sort $a + $b'); 6 | parses('sort { $a <=> $b } @foo'); 7 | parses('sort $foo'); 8 | parses('sort $foo @foo'); 9 | parses('sort @foo'); 10 | 11 | done_testing(); 12 | -------------------------------------------------------------------------------- /t/Statements/Expressions/OpUnaryKeywordExpr/OpKeywordReadlineExpr.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses('readline;'); 6 | parses('readline();'); 7 | parses('readline STDIN;'); 8 | parses('readline (STDIN);'); 9 | parses('readline(STDIN);'); 10 | 11 | done_testing(); 12 | -------------------------------------------------------------------------------- /t/Statements/Expressions/basic.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses("a(1, 2, 3)"); 6 | parses("a( b( 1 + 2 * 5 ), c::d( 1, e() ), 4 * 5 + 1 )"); 7 | parses("1 * 2 * 3 * 4"); 8 | parses("[ 1, 2 ]"); 9 | parses("+{ 1 => 2, a() }"); 10 | parsent("{ foo => bar() }"); 11 | 12 | done_testing; 13 | -------------------------------------------------------------------------------- /t/Statements/Expressions/Value/Literal/LitString.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | use Guacamole::Test; 5 | 6 | parses(q{"Foo"}); 7 | parses(q{"Fo#o"}); 8 | 9 | TODO: { 10 | local $TODO = 'Strings beginning with #'; 11 | parses(q{"#Foo"}); 12 | parses(q{'#Foo'}); 13 | } 14 | 15 | done_testing(); 16 | -------------------------------------------------------------------------------- /cpanfile: -------------------------------------------------------------------------------- 1 | requires 'Marpa::R2'; 2 | requires 'parent'; 3 | requires 'Exporter', '5.57'; 4 | requires 'Data::Visitor::Tiny'; 5 | requires 'Ref::Util'; 6 | requires 'List::Util'; 7 | requires 'Data::Dumper'; 8 | requires 'Path::Tiny'; 9 | 10 | test_requires 'Test::Builder'; 11 | test_requires 'Test::More'; 12 | test_requires 'Test::Fatal'; 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /blib/ 2 | /.build/ 3 | _build/ 4 | cover_db/ 5 | inc/ 6 | Build 7 | !Build/ 8 | Build.bat 9 | .last_cover_stats 10 | /Makefile 11 | /Makefile.old 12 | /MANIFEST.bak 13 | /META.yml 14 | /META.json 15 | /MYMETA.* 16 | nytprof.out 17 | /pm_to_blib 18 | *.o 19 | *.bs 20 | /_eumm/ 21 | *.sw? 22 | /*.zip 23 | /*.tar.gz 24 | Guacamole-* 25 | -------------------------------------------------------------------------------- /t/Statements/Expressions/OpListKeywordExpr/OpKeywordOpenExpr.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses('open($fh, "<", "foo");'); 6 | parses('open $fh, "<", "foo";'); 7 | parses('open(my $fh, "<", "foo");'); 8 | parses('open my $fh, "<", "foo";'); 9 | parses('my $res = open my $fh, "<", "foo";'); 10 | 11 | done_testing(); 12 | -------------------------------------------------------------------------------- /t/Statements/PhaseStatements.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses('BEGIN {1}'); 6 | parses('CHECK {1}'); 7 | parses('INIT {1}'); 8 | parses('UNITCHECK {1}'); 9 | parses('END {1}'); 10 | 11 | parses('sub BEGIN {1}'); 12 | parses('sub CHECK {1}'); 13 | parses('sub INIT {1}'); 14 | parses('sub UNITCHECK {1}'); 15 | parses('sub END {1}'); 16 | 17 | done_testing(); 18 | -------------------------------------------------------------------------------- /t/Statements/Expressions/unary.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses('defined $x'); 6 | parses('defined $x + $y'); 7 | parses('!defined $x + $y'); 8 | parses('$x + !defined $y + $z'); 9 | parses('$x ** !defined $y << $z'); 10 | parses('!-t STDIN'); 11 | parses('-f $x << $y'); 12 | parses('-x -x -x STDERR'); 13 | 14 | parses('-4'); 15 | parses('-44.4'); 16 | 17 | 18 | done_testing; 19 | -------------------------------------------------------------------------------- /t/Statements/Expressions/arrow.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | use Test::More; 5 | use Test::Fatal; 6 | 7 | parses('foo()->(foo())'); 8 | parses('foo()->BAR::baz()'); 9 | parses('foo()->$BAR::baz()'); 10 | parses('foo()->[1]'); 11 | parses('foo()->{1}'); 12 | parses('foo()->[1]{2}'); 13 | parses('$foo->thing()'); 14 | parses('"Foo"->thing()'); 15 | parses('Foo->thing()'); 16 | 17 | parses('Foo->thing;'); 18 | 19 | done_testing; 20 | -------------------------------------------------------------------------------- /t/Statements/Block.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use Guacamole::Test; 5 | 6 | parses('do { 1 }'); 7 | parses('do { { 1 } }'); 8 | parses('do { ( 1 => 2 ) }'); 9 | parses('do { +{ 1 => 2 } }'); 10 | 11 | parsent('do { 1, 2 }'); 12 | parsent('do { 1 => 2 }'); 13 | parsent('do { { 1 => 2 } }'); 14 | 15 | # parses('{;}'); should it? 16 | parses('{pop}'); 17 | parses('{pop;}'); 18 | parses('{a(); b();}'); 19 | 20 | parsent('sort {} 1'); 21 | 22 | done_testing; 23 | -------------------------------------------------------------------------------- /t/Statements/Expressions/OpListKeywordExpr/OpKeywordSplitExpr.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses('split //'); 6 | parses('split //, $foo'); 7 | parses('split /foo/, $foo'); 8 | parses('split /foo/x, $foo'); 9 | parses('split /foo/xms, $foo'); 10 | 11 | parses('split m{}'); 12 | parses('split m{}, $foo'); 13 | parses('split m{foo}, $foo'); 14 | parses('split m{foo}x, $foo'); 15 | parses('split m{foo}xms, $foo'); 16 | 17 | done_testing(); 18 | -------------------------------------------------------------------------------- /t/Statements/Condition.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses('if ($x > 10) {1}'); 6 | parses('if ($x > 10) {1} else {2}'); 7 | parses('if ($x > 10) {1} elsif ( $x < 20 ) {2}'); 8 | parses('if ($x > 10) {1} elsif ( $x < 20 ) {3} else {3}'); 9 | parses('if ($x > 10) {1} elsif ( $x < 20 ) {2} elsif ( $x < 15 ) {3}'); 10 | parses('if ($x > 10) {1} elsif ( $x < 20 ) {3} elsif ( $x < 15 ) {3} else {4}'); 11 | 12 | parses('unless ($x > 10) {1}'); 13 | 14 | done_testing(); 15 | -------------------------------------------------------------------------------- /t/Statements/Expressions/OpListKeywordExpr/OpKeywordSayExpr.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses('say $foo;'); 6 | parses('say "hello";'); 7 | parses('say $foo, $bar, $baz;'); 8 | parses('say {$fh} $foo;'); 9 | parses('say {$fh} $foo, bar();'); 10 | parses('say STDOUT $foo, bar();'); 11 | parses('say STDERR $foo, bar();'); 12 | parses('say STDERR'); 13 | 14 | parsent('say OTHER_THING $foo;'); 15 | parsent('say foo;'); 16 | parses('say foo();'); 17 | 18 | done_testing(); 19 | -------------------------------------------------------------------------------- /t/Statements/Expressions/OpListKeywordExpr/OpKeywordPrintExpr.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses('print $foo;'); 6 | parses('print "hello";'); 7 | parses('print $foo, $bar, $baz;'); 8 | parses('print {$fh} $foo;'); 9 | parses('print {$fh} $foo, bar();'); 10 | parses('print STDOUT $foo, bar();'); 11 | parses('print STDERR $foo, bar();'); 12 | parses('print STDERR'); 13 | 14 | parsent('print OTHER_THING $foo;'); 15 | parsent('print foo;'); 16 | parses('print foo();'); 17 | 18 | done_testing(); 19 | -------------------------------------------------------------------------------- /t/Statements/Expressions/OpListKeywordExpr/OpKeywordBinmodeExpr.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | use utf8; 6 | 7 | use Guacamole::Test; 8 | 9 | parses( 'binmode $fh;' ); 10 | parses( 'binmode( $fh );' ); 11 | parses( 'binmode STDIN;' ); 12 | parses( 'binmode( STDIN );' ); 13 | parses( 'binmode $fh, ":utf8";' ); 14 | parses( 'binmode( $fh, ":utf8" );' ); 15 | parses( 'binmode STDIN, ":utf8";' ); 16 | parses( 'binmode( STDIN, ":utf8" );' ); 17 | parses( 'binmode( STDIN, ":utf8" );' ); 18 | 19 | done_testing(); 20 | -------------------------------------------------------------------------------- /t/Statements/Expressions/OpListKeywordExpr/OpKeywordPrintfExpr.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses('printf $foo;'); 6 | parses('printf "hello";'); 7 | parses('printf $foo, $bar, $baz;'); 8 | parses('printf {$fh} $foo;'); 9 | parses('printf {$fh} $foo, bar();'); 10 | parses('printf STDOUT $foo, bar();'); 11 | parses('printf STDERR $foo, bar();'); 12 | parses('printf STDERR'); 13 | 14 | parsent('printf OTHER_THING $foo;'); 15 | parsent('printf foo;'); 16 | parses('printf foo();'); 17 | 18 | done_testing(); 19 | -------------------------------------------------------------------------------- /t/Statements/RequireStatement.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use Guacamole::Test; 5 | 6 | parses(q{require v5.10.0}); 7 | parses(q{require 5.010.000}); 8 | 9 | parses(q{require Foo}); 10 | parses(q{require Foo::Bar}); 11 | 12 | parses(q{require "Foo.pm"}); 13 | parses(q{require 'Foo.pm'}); 14 | 15 | parses(q{require "Foo/Bar.pm"}); 16 | parses(q{require 'Foo/Bar.pm'}); 17 | 18 | parses(q{require qq}); 19 | parses(q{require q}); 20 | 21 | parses(q{require qq}); 22 | parses(q{require q}); 23 | 24 | done_testing(); 25 | -------------------------------------------------------------------------------- /t/Statements/Expressions/OpListKeywordExpr/OpKeywordChmodExpr.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses('$cnt = chmod 0755, "foo", "bar";'); 6 | parses('chmod 0755, "foo", "bar";'); 7 | parses('chmod 0755, @executables;'); 8 | parses('$mode = "0644";'); 9 | parses('chmod $mode, "foo";'); 10 | parses('$mode = "0644";'); 11 | parses('chmod oct($mode), "foo";'); 12 | parses('chmod $mode, "foo";'); 13 | parses('chmod($perm | 0600, $fh);'); 14 | parses('chmod S_IRWXU()|S_IRGRP()|S_IXGRP()|S_IROTH()|S_IXOTH(), @executables;'); 15 | 16 | done_testing(); 17 | -------------------------------------------------------------------------------- /t/Statements/PackageStatement.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use Guacamole::Test; 5 | 6 | parses('package Foo'); 7 | parses('package Foo::Bar'); 8 | parses('package Foo;'); 9 | parses('package Foo::Bar;'); 10 | 11 | # you can have package q, qq, qw, qx, qr 12 | parses('package q;'); 13 | parses('package qq;'); 14 | parses('package qw;'); 15 | parses('package qx;'); 16 | parses('package qr;'); 17 | 18 | parses('package Foo {1}'); 19 | parses('package Foo::Bar {1}'); 20 | 21 | parses('package Foo 1.4.0;'); 22 | parses('package Foo v1.4.0;'); 23 | 24 | parses('package Foo 1.4.0 {1}'); 25 | parses('package Foo v1.4.0 {1}'); 26 | 27 | done_testing(); 28 | -------------------------------------------------------------------------------- /t/Statements/Expressions/OpListKeywordExpr/OpKeywordSprintfExpr.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses('sprintf $foo;'); 6 | parses('sprintf "hello";'); 7 | parses('sprintf $format, $bar, $baz;'); 8 | parsent('sprintf {$fh} $foo;'); 9 | parsent('sprintf {$fh} $foo, bar();'); 10 | parsent('sprintf STDOUT $foo, bar();'); 11 | parsent('sprintf STDERR $foo, bar();'); 12 | parsent('sprintf STDERR;'); 13 | 14 | parses('sprintf "%s", $foo;'); 15 | parses('sprintf "%s", "hello";'); 16 | parses('sprintf $foo;'); 17 | parses('sprintf $foo, bar();'); 18 | 19 | parsent('sprintf OTHER_THING $foo;'); 20 | parsent('sprintf foo;'); 21 | parses('sprintf foo();'); 22 | 23 | done_testing(); 24 | -------------------------------------------------------------------------------- /t/Statements/Expressions/OpNullaryKeywordExpr/OpKeywordSubExpr.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses('$cb = sub () {1}'); 6 | parses('$cb = sub :attr {1}'); 7 | parses('$cb = sub : attr {1}'); 8 | parses('$cb = sub : attra :attrb {1}'); 9 | parses('$cb = sub :attr(foo) {1}'); 10 | parses('$cb = sub : attr(foo) {1}'); 11 | parses('$cb = sub ( $left, $right ) {1}'); 12 | parses('$cb = sub ($first, $bar, $third){1}'); 13 | parses('$cb = sub ($left, $right = 0) {1}'); 14 | parses('$cb = sub ($thing, $id = $auto_id++) {1}'); 15 | parses('$cb = sub :prototype($) {1}'); 16 | parses('$cb = sub :prototype($@\@) ($thing, $id = $auto_id++) {1}'); 17 | parses('$cb = sub : const {$x}'); 18 | 19 | done_testing(); 20 | -------------------------------------------------------------------------------- /t/Statements/Expressions/Value/ArrowDerefVariable.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | use Test::Fatal qw< exception >; 5 | use Guacamole::Test; 6 | 7 | foreach my $lead_sigil (qw< $ @ % & * >) { 8 | parses( sprintf '$foo->%s*', $lead_sigil ); 9 | 10 | my $fail_str = sprintf '$foo->%s *', $lead_sigil; 11 | parsent($fail_str); 12 | } 13 | 14 | parses('$foo->$#*'); 15 | parsent('$foo->$#'); 16 | parsent('$foo->$# *'); 17 | 18 | foreach my $slice_sigil (qw< @ % >) { 19 | parses("\$foo->$slice_sigil\[ 0, 3 \]"); 20 | parses("\$foo->$slice_sigil\{ 0, 3 \}"); 21 | } 22 | 23 | parsent('$foo->@ [ "foo", "bar" ]'); 24 | parsent('$foo->% [ "foo", "bar" ]'); 25 | parsent('$foo->@ { "foo", "bar" }'); 26 | parsent('$foo->% { "foo", "bar" }'); 27 | 28 | done_testing(); 29 | -------------------------------------------------------------------------------- /tools/expl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use FindBin '$Bin'; 4 | use lib "$Bin/../lib"; 5 | 6 | use Guacamole; 7 | use Guacamole::Dumper qw/dump_tree/; 8 | 9 | # add_keyword will do the lexeme adding anyway 10 | #Guacamole->add_lexemes( 11 | # [ 'OpKeywordTry' => 'try' ], 12 | # [ 'OpKeywordCatch' => 'catch' ], 13 | #); 14 | 15 | # Example of adding support for Try::Tiny's try/catch 16 | #Guacamole->add_keyword( 17 | # 'Catch', 18 | # 'unary', 19 | # 'catch', 20 | # [ 'OpKeywordCatch BlockNonEmpty' ] 21 | #); 22 | # 23 | #Guacamole->add_keyword( 24 | # 'Try', 25 | # 'list', 26 | # 'try', 27 | # [ 28 | # 'OpKeywordTry BlockNonEmpty OpKeywordCatchExpr', 29 | # 'OpKeywordTry BlockNonEmpty', 30 | # ], 31 | #); 32 | 33 | my $src = shift @ARGV; 34 | foreach my $ast (Guacamole->parse($src)) { 35 | print dump_tree($ast); 36 | } 37 | 38 | -------------------------------------------------------------------------------- /t/Statements/Expressions/Value/NonLiteral/DiamondExpr.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | use Test::More; 5 | 6 | parses('<>'); 7 | parses('<$fh>'); 8 | parses('<<>>'); 9 | parses('<<$fh>>'); 10 | 11 | TODO: { 12 | local $TODO = 'Reserve whitespace policy to avoid unacceptable spacing'; 13 | parsent('< $fh >'); 14 | parsent('<< $fh >>'); 15 | } 16 | 17 | parsent('<@fh>'); 18 | parsent('<<@fh>>'); 19 | parsent(''); 20 | parsent('<>'); 21 | parsent(''); 22 | parsent('<>'); 23 | parses('while (<>) {...}'); 24 | parses('while ( my $line = <>) {...}'); 25 | parses('while (<<>>) {...}'); 26 | parses('while ( my $line = <<>>) {...}'); 27 | 28 | parses('while () {...}'); 29 | parses('while ( my $line = ) {...}'); 30 | parses('while (<>) {...}'); 31 | parses('while ( my $line = <>) {...}'); 32 | 33 | done_testing(); 34 | -------------------------------------------------------------------------------- /t/Statements/Expressions/variable.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | use Guacamole::Test; 5 | 6 | parses('$foo'); 7 | parses('$#foo::bar'); 8 | parsent('%$foo::bar::baz'); 9 | parses('%{ $foo::bar::baz }'); 10 | parsent('$#$foo'); 11 | parses('$#{ $foo }'); 12 | parsent('@$$bar'); 13 | parses('@{ ${$bar} }'); 14 | 15 | parsent('@@foo'); 16 | parses('@$#foo'); # @$ and # foo 17 | 18 | parsent('@%foo'); 19 | parsent('@@$foo'); 20 | parsent('$#@foo'); 21 | 22 | parses('&foo()'); 23 | parsent('$foo()'); 24 | parsent('&$foo()'); 25 | parses('$foo->()'); 26 | parsent('$$foo()'); 27 | 28 | parses('$foo[5]'); 29 | parses('@foo[1, 2]'); 30 | parses('%foo[1, 5]'); 31 | parses('$foo[bar()]'); 32 | parses('$foo["baz"]'); 33 | parses('(stat $file)[2]'); 34 | parses('$foo{1}'); 35 | parses('$foo{1, 2}'); 36 | parses('$foo{qw/foo bar/}'); 37 | parses("(stat)[2]"); 38 | 39 | parsent("5[1]"); 40 | parsent('$x->y[1]'); 41 | 42 | done_testing; 43 | -------------------------------------------------------------------------------- /t/Statements/Expressions/Value/NonLiteral/DerefVariable.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | use Test::More; 5 | 6 | parsent('$$foo'); 7 | parsent('@$foo'); 8 | parsent('%$foo'); 9 | parsent('*$foo'); 10 | parsent('$#$foo'); 11 | 12 | parsent('$ $foo'); 13 | parsent('@ $foo'); 14 | parsent('% $foo'); 15 | parsent('* $foo'); 16 | 17 | parses('${$foo}'); 18 | parses('@{$foo}'); 19 | parses('%{$foo}'); 20 | parses('*{$foo}'); 21 | parses('$#{$foo}'); 22 | 23 | parses(q! ${ $foo->{'bar'} } !); 24 | parses(q! @{ $foo->{'bar'} } !); 25 | parses(q! %{ $foo->{'bar'} } !); 26 | parses(q! *{ $foo->{'bar'} } !); 27 | parses(q! $#{ $foo->{'bar'} } !); 28 | 29 | parses(q! $ { $foo->{'bar'} } !); 30 | parses(q! @ { $foo->{'bar'} } !); 31 | parses(q! % { $foo->{'bar'} } !); 32 | parses(q! * { $foo->{'bar'} } !); 33 | parses(q! $# { $foo->{'bar'} } !); 34 | 35 | parsent('$$$foo'); 36 | parsent('@$$foo'); 37 | parsent('%$$foo'); 38 | parsent('*$$foo'); 39 | parsent('$#$$foo'); 40 | 41 | done_testing(); 42 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the action will run. Triggers the workflow on push or pull request 6 | # events but only for the master branch 7 | on: 8 | push: 9 | branches: [ master ] 10 | pull_request: 11 | branches: [ master ] 12 | 13 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 14 | jobs: 15 | # This workflow contains a single job called "build" 16 | build: 17 | # The type of runner that the job will run on 18 | runs-on: ubuntu-latest 19 | 20 | # Steps represent a sequence of tasks that will be executed as part of the job 21 | steps: 22 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 23 | - uses: actions/checkout@v2 24 | 25 | # Runs a single command using the runners shell 26 | - name: Install dependencies 27 | run: sudo apt install libmarpa-r2-perl libtest-fatal-perl 28 | 29 | # Runs a set of commands using the runners shell 30 | - name: Test 31 | run: | 32 | prove -lr t 33 | -------------------------------------------------------------------------------- /t/Statements/UseNoStatement.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use Guacamole::Test; 5 | 6 | foreach my $op ( qw< use no > ) { 7 | # use Module 8 | parses("$op Module ()"); 9 | parses("$op Module"); 10 | parses("$op My::Module"); 11 | parses("$op My::Module ()"); 12 | parses("$op My::Module::Foo"); 13 | parses("$op My::Module::Foo ()"); 14 | 15 | # use VERSION 16 | parses("$op 5.010"); 17 | parses("$op 5.010.000"); 18 | parses("$op v5.10"); 19 | parses("$op v5.010"); 20 | parses("$op v5.10.0"); 21 | 22 | # use Module LIST 23 | parses("$op My::Module ( 'foo', 'bar' )"); 24 | parses("$op My::Module ( 'foo' )"); 25 | parses("$op My::Module qw< foo >"); 26 | parses("$op My::Module ( qw< foo > )"); 27 | 28 | # use Module VERSION 29 | parses("$op Module 4.10"); 30 | parses("$op Module 4.10.999"); 31 | parses("$op My::Module v5.200.123"); 32 | parses("$op My::Module::Foo 5.3.5"); 33 | parses("$op My::Module::Foo v5.3.5"); 34 | 35 | # use Module VERSION LIST 36 | parses("$op Module 4.10 ( 'foo', 'bar' )"); 37 | parses("$op Module 4.10.999 ('foo')"); 38 | parses("$op My::Module v5.200.123 qw< foo >"); 39 | parses("$op My::Module::Foo 5.3.5 ( qw< foo > )"); 40 | 41 | # use with qw<> 42 | parses("$op Fcntl qw( :mode );"); 43 | parses("$op Fcntl qw( :mode );"); 44 | } 45 | 46 | done_testing(); 47 | -------------------------------------------------------------------------------- /dist.ini: -------------------------------------------------------------------------------- 1 | name = Guacamole 2 | author = Sawyer X 3 | author = Vickenty Fesunov 4 | license = MIT 5 | copyright_holder = Sawyer X 6 | copyright_year = 2022 7 | main_module = lib/Guacamole.pm 8 | 9 | version = 0.008 10 | 11 | [NextRelease] 12 | filename = Changes 13 | 14 | [TestRelease] 15 | [ConfirmRelease] 16 | 17 | [@Git] 18 | allow_dirty = Changes 19 | allow_dirty = dist.ini 20 | allow_dirty = README.mkdn 21 | add_files_in = Changes 22 | add_files_in = dist.ini 23 | add_files_in = README.mkdn 24 | 25 | [@Basic] 26 | [PkgVersion] 27 | [Authority] 28 | [Test::NoTabs] 29 | [PodSyntaxTests] 30 | [Test::ReportPrereqs] 31 | [PodWeaver] 32 | 33 | [MetaProvides::Package] 34 | [MetaJSON] 35 | 36 | ; -- static meta-information 37 | [MetaResources] 38 | repository.url = git://github.com/xsawyerx/guacamole.git 39 | repository.web = https://github.com/xsawyerx/guacamole 40 | repository.type = git 41 | 42 | [PruneFiles] 43 | match = ~$ ; emacs backup files 44 | match = tools/ 45 | match = dist.ini 46 | 47 | ;[PodCoverageTests] 48 | ;[Test::EOL] 49 | 50 | [Prereqs::FromCPANfile] 51 | 52 | ; PerlTidy interferes with share/skel 53 | ;[PerlTidy] 54 | ;perltidyrc = xt/perltidy.rc 55 | 56 | ; maybe too late for perlcritic ;) 57 | ;[Test::Perl::Critic] 58 | #critic_config = xt/perlcritic.rc 59 | 60 | ; also needs a PR for this one to be possible 61 | ; [Test::UnusedVars] 62 | 63 | [ ReadmeAnyFromPod / MarkdownInRoot ] 64 | filename = README.mkdn 65 | -------------------------------------------------------------------------------- /t/Statements/LoopStatement.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use Guacamole::Test; 5 | 6 | # ForStatement 7 | parses('for (@foo) {}'); 8 | parses('for (@foo) {} continue {}'); 9 | parses('for $foo (@bar) {}'); 10 | parses('for my $foo (@bar) {}'); 11 | parses('for ($i = 0; $i < 10; $ii) {}'); 12 | parses('for (my $i = 0; $i < 10; $ii) {}'); 13 | parses('for (; $i < 10; $ii) {}'); # cases with missing part or two 14 | parses('for (my $i = 0;; $ii) {}'); 15 | parses('for (my $i = 0; $i < 10;) {}'); 16 | parses('for (;; $ii) {}'); 17 | parses('for (my $i = 0;;) {}'); 18 | parses('for (;;) {}'); # special case for infinite loop 19 | 20 | parses('foreach (@foo) {}'); 21 | parses('foreach (@foo) {} continue {}'); 22 | parses('foreach $foo (@bar) {}'); 23 | parses('foreach my $foo (@bar) {}'); 24 | parses('foreach ($i = 0; $i < 10; $i) {}'); 25 | parses('foreach (my $i = 0; $i < 10; $i) {}'); 26 | parses('foreach (; $i < 10; $i) {}'); # cases with missing part or two 27 | parses('foreach (my $i = 0;; $i) {}'); 28 | parses('foreach (my $i = 0; $i < 10;) {}'); 29 | parses('foreach (;; $i) {}'); 30 | parses('foreach (my $i = 0;;) {}'); 31 | parses('for (;;) {}'); # special case for infinite loop 32 | 33 | # WhileStatement 34 | parses('while ( $foo = shift ) {}'); 35 | parses('while ( my $foo = shift ) {}'); 36 | parses('while (1) {}'); 37 | parses('while (1) {} continue {}'); 38 | parses('while () {}'); # special case for infinite loop 39 | 40 | # UntilStatement 41 | parses('until ( $foo = shift ) {}'); 42 | parses('until ( my $foo = shift ) {}'); 43 | parses('until (1) {}'); 44 | parses('until (1) {} continue {}'); 45 | 46 | done_testing(); 47 | -------------------------------------------------------------------------------- /lib/Guacamole/Dumper.pm: -------------------------------------------------------------------------------- 1 | package Guacamole::Dumper; 2 | # ABSTRACT: Dump Guacamole ASTs 3 | 4 | use strict; 5 | use warnings; 6 | 7 | use List::Util qw/any sum/; 8 | use Exporter "import"; 9 | 10 | our @EXPORT_OK = qw/dump_tree/; 11 | 12 | sub dump_tree { 13 | my ($tree, $offset) = @_; 14 | return join "", map "$_\n", _dump_tree_inner($tree, "", $offset); 15 | } 16 | 17 | sub _dump_tree_inner { 18 | my ($tree, $indent, $offset) = @_; 19 | $indent //= ""; 20 | $offset //= " "; 21 | 22 | ref $tree eq 'HASH' 23 | or die "Bad token object: $tree"; 24 | 25 | my $head = $tree->{ 26 | $tree->{'type'} eq 'lexeme' 27 | ? 'value' 28 | : 'name' 29 | }; 30 | 31 | my @tail = $tree->{'type'} eq 'lexeme' 32 | ? () 33 | : @{ $tree->{'children'} }; 34 | 35 | if ( any { ref $_ } @tail ) { 36 | my @rest = map { ref $_ ? _dump_tree_inner($_, "$indent$offset", $offset) : "$indent$offset'$_'" } @tail; 37 | 38 | my @clean = map { s/^\s+//r } @rest; 39 | if (sum(map length, @clean) < 40) { 40 | my @items = ($head, @clean); 41 | return ("$indent(@items)"); 42 | } 43 | 44 | $rest[-1] .= ")"; 45 | return ("$indent($head", @rest); 46 | } else { 47 | my @tailq = map "'$_'", @tail; 48 | my @items = ($head, @tailq); 49 | return ("$indent(@items)"); 50 | } 51 | } 52 | 53 | 1; 54 | 55 | __END__ 56 | 57 | =pod 58 | 59 | =head1 SYNOPSIS 60 | 61 | use Gaucamole; 62 | use Guacamole::Dump qw< dump_tree >; 63 | dump_tree( Gaucamole->parse($string) ); 64 | 65 | =head1 WHERE'S THE REST? 66 | 67 | Soon. 68 | 69 | =head1 SEE ALSO 70 | 71 | L 72 | -------------------------------------------------------------------------------- /lib/Guacamole/Test.pm: -------------------------------------------------------------------------------- 1 | package Guacamole::Test; 2 | # ABSTRACT: What Guacamole uses to test itself 3 | 4 | use strict; 5 | use warnings; 6 | 7 | use Test::More; 8 | use Data::Dumper; 9 | 10 | use Guacamole; 11 | use Guacamole::Dumper qw/dump_tree/; 12 | 13 | use Exporter "import"; 14 | 15 | our @EXPORT = qw( 16 | parses 17 | parses_as 18 | parsent 19 | done_testing 20 | ); 21 | 22 | sub parses { 23 | my ($text) = @_; 24 | 25 | local $Test::Builder::Level = $Test::Builder::Level + 1; 26 | 27 | my @trees; 28 | eval { @trees = Guacamole->parse($text); } 29 | or do { diag($@); }; 30 | 31 | # debugging 32 | if (0) { 33 | require DDP; 34 | my @dumped_trees = map dump_tree($_), @trees; 35 | &DDP::p([@dumped_trees]); 36 | } 37 | 38 | is( scalar(@trees), 1, "'$text': parsed unambiguously" ); 39 | return \@trees; 40 | } 41 | 42 | sub parses_as { 43 | my ( $text, $user_trees ) = @_; 44 | 45 | local $Test::Builder::Level = $Test::Builder::Level + 1; 46 | 47 | my $trees = parses($text); 48 | my @dumped_trees = map dump_tree($_), @{$trees}; 49 | my @dumped_user_trees = map dump_tree($_), @{$user_trees}; 50 | 51 | is_deeply( 52 | \@dumped_user_trees, 53 | \@dumped_trees, 54 | "'$text': parsed exactly as expected", 55 | ); 56 | } 57 | 58 | sub parsent { 59 | my ($text) = @_; 60 | 61 | local $Test::Builder::Level = $Test::Builder::Level + 1; 62 | 63 | my @trees; 64 | my $res = eval { 65 | @trees = Guacamole->parse($text); 66 | 1; 67 | }; 68 | my $err = $@; 69 | ok( !defined $res && defined $err, "'$text': did not parse" ); 70 | 71 | foreach my $tree (@trees) { 72 | diag( dump_tree($tree) ); 73 | } 74 | } 75 | 76 | 1; 77 | 78 | __END__ 79 | 80 | =pod 81 | 82 | =head1 SYNOPSIS 83 | 84 | You don't really need to use this. 85 | 86 | =head1 WHERE'S THE REST? 87 | 88 | Working on it. 89 | 90 | =head1 SEE ALSO 91 | 92 | L 93 | -------------------------------------------------------------------------------- /Changes: -------------------------------------------------------------------------------- 1 | {{$NEXT}} 2 | 3 | 0.008 2022-06-20 19:15:30+02:00 Europe/Amsterdam 4 | 5 | * GH #23: (VERY) Preliminary support for extending the grammar. 6 | * GH #111: Support "die". 7 | * GH #112: Support "readline STDIN" or "readline(STDIN)". 8 | * GH #113: Support "$#_" (but not "$# _"). 9 | * GH #114: Support all forms of binmode() (Val @valcomm). 10 | * GH #115: Support all forms of for() loop (Val @valcomm). 11 | * RT #132920: Fix confusion of quote-like operators by comments. 12 | * Support "_" in file operations ("-x _", etc.). 13 | * Support "$foo->$bar" and "$foo->$_". 14 | * Support 'eval "..."' or 'eval $foo'. 15 | * Improve wording on top-level anonymous hashes. 16 | * When failing without an exception, we throw an exception with 17 | more information (e.g., "print _"). 18 | 19 | 0.007 2020-08-15 20 | 21 | * GH #106: Document eval EXPR is not supported. 22 | (Thanks, Hauke @haukex D) 23 | * Add preliminary tool to help turn non-standard code into 24 | standard code. For now, it can quote autoquoted strings pretty 25 | well. Alpha-level, be warned! 26 | 27 | 28 | 0.006 2020-08-01 29 | 30 | * GH #101: Implement number parsing: Decimal, octal, hexadecimal, 31 | binary, exponents, and visual separators. 32 | 33 | 0.005 2020-07-04 34 | 35 | * Add proper documentation to Guacamole.pm. 36 | * GH #88: Fix bareword STDIN with -t and file op precedence. 37 | * GH #87: Document Standard Perl in standard.pm. 38 | * GH #89: Added '|' as a delimiter for QLike values. 39 | * GH #90: Support methods without arglists (Thanks, chromatic!) 40 | * GH #93: Explain Standard Perl better. (Thanks, brian d foy) 41 | * GH #97: Fix test dependency. (Thanks, Graham @plicease Ollis) 42 | 43 | 0.004 2020-06-26 44 | 45 | * Fixed a typo (thanks, Olivier Mengué) 46 | 47 | 0.003 2020-06-25 48 | 49 | * Another fix. (And changes for some reason.) 50 | 51 | 0.002 2020-06-25 52 | 53 | * Fix naming of rules vs. lexemes. 54 | 55 | 0.001 2020-06-25 56 | 57 | * Woohoo! 58 | -------------------------------------------------------------------------------- /lib/Guacamole/Deparse.pm: -------------------------------------------------------------------------------- 1 | package Guacamole::Deparse; 2 | # ABSTRACT: A Gaucamole-based Deparser 3 | 4 | use strict; 5 | use warnings; 6 | use parent 'Exporter'; 7 | use experimental qw< postderef signatures >; 8 | use Guacamole; 9 | use Data::Visitor::Tiny qw< visit >; 10 | use Ref::Util qw< is_hashref >; 11 | 12 | our @EXPORT_OK = qw< deparse >; 13 | 14 | my $var_names_re = qr/^Var(Scalar|Array|Hash|Code|Glob|ArrayTop)$/; 15 | 16 | sub deparse ($string) { 17 | my @results = Guacamole->parse($string); 18 | foreach my $result (@results) { 19 | my @elements; 20 | 21 | visit( $result, sub ( $key, $valueref, $ctx ) { 22 | # Fold some stuff 23 | if ( is_hashref( $valueref->$* ) ) { 24 | my $name = $valueref->$*->{'name'} || ''; 25 | 26 | if ( $name =~ $var_names_re ) { 27 | fold_var_name($valueref); 28 | } 29 | 30 | if ( $name =~ m/^(Interpol|Literal)String$/ ) { 31 | fold_string($valueref); 32 | } 33 | } 34 | 35 | # Take lexeme as a string element 36 | if ( 37 | is_hashref( $valueref->$* ) 38 | && $valueref->$*->{'type'} eq 'lexeme' 39 | ) { 40 | push @elements, $valueref->$*->{'value'}; 41 | } 42 | }); 43 | 44 | print join( ' ', @elements ), "\n"; 45 | } 46 | } 47 | 48 | sub fold_var_name ($valueref) { 49 | # Merge children 50 | my $sigil_elem = shift $valueref->$*->{'children'}->@*; 51 | my $sigil = $sigil_elem->{'value'}; 52 | 53 | $valueref->$*->{'children'}[0]{'children'}[0]{'value'} 54 | = $sigil . $valueref->$*->{'children'}[0]{'children'}[0]{'value'}; 55 | } 56 | 57 | sub fold_string ($valueref) { 58 | my $value = join '', map $_->{'value'}, $valueref->$*->{'children'}->@*; 59 | 60 | $valueref->$*->{'children'} = [ 61 | { 62 | 'type' => 'lexeme', 63 | 'value' => $value, 64 | } 65 | ]; 66 | } 67 | 68 | 1; 69 | 70 | __END__ 71 | 72 | =pod 73 | 74 | =head1 SYNOPSIS 75 | 76 | use Guacamole::Deparse; 77 | deparse($string); # prints all lexemes with a some folding rules 78 | 79 | =head1 WHERE'S THE REST? 80 | 81 | Soon. 82 | 83 | =head1 SEE ALSO 84 | 85 | L 86 | 87 | -------------------------------------------------------------------------------- /t/Statements/Expressions/Value/NonLiteral/Variable.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses('$foo'); 6 | parses('@foo'); 7 | parses('%foo'); 8 | parses('*foo'); 9 | parses('$0'); 10 | parses('$1'); 11 | parses('$2'); 12 | parses('$3'); 13 | parses('$4'); 14 | parses('$5'); 15 | parses('$6'); 16 | parses('$7'); 17 | parses('$8'); 18 | parses('$9'); 19 | 20 | #$ perldoc perlvar | grep '^ [$@%]' | awk '{print $1}' | sort | uniq | grep -v '[$@%][A-Za-z]' 21 | 22 | parses('$!'); 23 | parses('$"'); 24 | parses('$#'); 25 | parses('$#_'); 26 | parsent('$# _'); 27 | parses('$%'); 28 | parses('$&'); 29 | parses('$\''); 30 | parses('$('); 31 | parses('$)'); 32 | parses('$*'); 33 | parses('$+'); 34 | parses('$,'); 35 | parses('$-'); 36 | parses('$.'); 37 | parses('$/'); 38 | parses('$:'); 39 | parses('$;'); 40 | parses('$<'); 41 | parses('$='); 42 | parses('$>'); 43 | parses('$?'); 44 | parses('$@'); 45 | parses('$['); 46 | parses('$\\'); 47 | parses('$]'); 48 | parses('$^'); 49 | parses('$_'); 50 | parses('$`'); 51 | parses('$|'); 52 | parses('$~'); 53 | parses('$$'); 54 | parses('$^A'); 55 | parses('$^C'); 56 | parses('${^CHILD_ERROR_NATIVE}'); 57 | parses('$^D'); 58 | parses('$0'); 59 | parses('$100'); 60 | parses('$^E'); 61 | parses('${^ENCODING}'); 62 | parses('$^F'); 63 | parses('${^GLOBAL_PHASE}'); 64 | parses('$^H'); 65 | parses('$^I'); 66 | parses('$^L'); 67 | parses('${^LAST_FH}'); 68 | parses('$^M'); 69 | parses('${^MATCH}'); 70 | parses('$^N'); 71 | parses('$^O'); 72 | parses('${^OPEN}'); 73 | parses('$^P'); 74 | parses('${^POSTMATCH}'); 75 | parses('${^PREMATCH}'); 76 | parses('$^R'); 77 | parses('${^RE_COMPILE_RECURSION_LIMIT}'); 78 | parses('${^RE_DEBUG_FLAGS}'); 79 | parses('${^RE_TRIE_MAXBUF}'); 80 | parses('$^S'); 81 | parses('${^SAFE_LOCALES}'); 82 | parses('$^T'); 83 | parses('${^TAINT}'); 84 | parses('${^UNICODE}'); 85 | parses('${^UTF8CACHE}'); 86 | parses('${^UTF8LOCALE}'); 87 | parses('$^V'); 88 | parses('$^W'); 89 | parses('${^WARNING_BITS}'); 90 | parses('${^WIN32_SLOPPY_STAT}'); 91 | parses('$^X'); 92 | 93 | parses('@+'); 94 | parses('@-'); 95 | parses('@_'); 96 | parses('@{^CAPTURE}'); 97 | 98 | parses('%!'); 99 | parses('%+'); 100 | parses('%-'); 101 | parses('%{^CAPTURE}'); 102 | parses('%{^CAPTURE_ALL}'); 103 | parses('%^H'); 104 | 105 | parses('$ \''); 106 | 107 | done_testing(); 108 | -------------------------------------------------------------------------------- /t/Statements/Expressions/Value/Literal.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | use Test::More; 5 | 6 | # LitNumberDec 7 | parses('4'); 8 | parses('44'); 9 | parses('44.4'); 10 | parses("4."); 11 | parses(".4"); 12 | parses("0"); 13 | parses("0."); 14 | parses(".0"); 15 | 16 | parses(".0e1"); 17 | parses("0.e1"); 18 | parsent(".e1"); 19 | 20 | parses("1_000"); 21 | parses("1__00"); 22 | parses("1_000_000"); 23 | parses("1_0_0_"); 24 | parses("0._4"); 25 | parses("1_.4"); 26 | parses(".4_"); 27 | parsent("._4"); 28 | 29 | parses("1._e1"); 30 | parses("1_.e1"); 31 | parses("1e_+_1"); 32 | parses("1e_+__1"); 33 | parsent("1e+_"); 34 | parsent("1e__+_1"); 35 | 36 | # LitNumberOct 37 | parses("0755"); 38 | parses("07p1"); 39 | parses("07p-2"); 40 | parses("0_1"); 41 | parses("01_"); 42 | parses("0_7_5_5"); 43 | parsent("08"); 44 | parsent("0_"); 45 | 46 | parses("01.4p1"); 47 | parses("01.4_p1"); 48 | parses("01_.4p1"); 49 | parses("0_.4p1"); 50 | parsent("0_.4"); 51 | 52 | # LitNumberHex 53 | parses("0xf"); 54 | parses("0XF"); 55 | parses("0xf_"); 56 | parses("0x_f"); 57 | parses("0xfP1"); 58 | parses("0xeP2_"); 59 | parses("0xfp-1"); 60 | parses("0x_ap+2"); 61 | parses("0xap+2_"); 62 | # parsent("0x_"); # FIXME: doesn't parse and doesn't error 63 | parsent("0xap_+2"); 64 | parsent("0xap+_2"); 65 | parsent("0xap_"); 66 | parsent("0xap-_"); 67 | 68 | parses("0x1.8p1"); 69 | parses("0x1.8_p1"); 70 | parses("0x1_.8p1"); 71 | parses("0x_.8p1"); 72 | parsent("0x_.8"); 73 | 74 | # LitNumberBin 75 | parses("0b0"); 76 | parses("0b1"); 77 | parses("0b001"); 78 | parses("0B1"); 79 | parses("0B_1"); 80 | parses("0B1_"); 81 | parsent("0b2"); 82 | parsent("0b1_2"); 83 | parsent("0b_"); 84 | parsent("0b_"); 85 | 86 | parses("0b1.1p1"); 87 | parses("0b1.1_p1"); 88 | parses("0b1_.1p1"); 89 | parses("0b_.1p1"); 90 | parsent("0b_.1"); 91 | 92 | # LitArray 93 | parses('[]'); 94 | parses('[ 1, 2 ]'); 95 | parses('[ 1, 2, ( 10, () ) ]'); 96 | 97 | # LitHash 98 | parses('{}'); 99 | parses('sub { {} }'); 100 | parses('sort {}, 1'); 101 | parses('$x = {}'); 102 | parses('$x = { "foo" => "bar" }'); 103 | parses('return { 1, 2, 3, 4 }'); 104 | parsent('{ 1, 2, 3, 4 }'); 105 | 106 | # LitString (SingleQuote) 107 | parses(q{''}); 108 | parses(q{ 'hello' }); 109 | parses(q{ 'hello world \' foo " ' }); 110 | 111 | # InterpolString (DoubleQuote) 112 | parses(q{""}); 113 | parses(q{ "hello" }); 114 | parses(q{ "hello world \" foo ' " }); 115 | 116 | done_testing(); 117 | -------------------------------------------------------------------------------- /t/Statements/SubStatement.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Guacamole::Test; 4 | 5 | parses('sub foo'); 6 | parses('sub foo;'); 7 | parses('sub foo () {1}'); 8 | parses('sub foo :attr {1}'); 9 | parses('sub foo : attr {1}'); 10 | parses('sub foo : attra :attrb {1}'); 11 | parses('sub foo :attr(foo) {1}'); 12 | parses('sub foo : attr(foo) {1}'); 13 | parses('sub foo ( $left, $right ) {1}'); 14 | parses('sub foo ($first, $bar, $third){1}'); 15 | parses('sub foo ($left, $right = 0) {1}'); 16 | parses('sub foo ($thing, $id = $auto_id++) {1}'); 17 | parses('sub foo :prototype($) {1}'); 18 | parses('sub foo :prototype($@\@) ($thing, $id = $auto_id++) {1}'); 19 | 20 | parses('Foo->method();'); 21 | parsent('Foo::->method();'); 22 | parses('Foo->method;'); 23 | parses('Foo->method->chain;'); 24 | parses('$object->method->chain->two;'); 25 | 26 | # make sure we didn't screw up non q-like subroutines 27 | 28 | foreach my $func ( qw< q qq qw qx qr s m y tr > ) { 29 | if ( length $func == 1 ) { 30 | # single q-like letter 31 | parses('sub z {1}'); # NonQLike single char 32 | parses("sub ${func}z {1}"); # NonQLike double char with leading q 33 | parses("sub $func\_z {1}"); # NonQLike with underscore, leading q 34 | parses("sub $func\_ {1}"); # NonQLike with underscore, leading q 35 | parses("sub _$func {1}"); # NonQLike with underscore, leading q 36 | parses( 'sub ' . uc($func) . '{1}'); 37 | } 38 | 39 | 40 | if ( length $func == 2 ) { 41 | my ( $letter1, $letter2 ) = split //xms, $func; 42 | # double q-like letters 43 | parses( sprintf 'sub %s_ {1}', $func ); 44 | parses( sprintf 'sub %s_%s {1}', $letter1, $letter2 ); 45 | parses( sprintf 'sub %s%s {1}', $func, $letter1 ); 46 | parses( sprintf 'sub %s%s {1}', $func, $letter2 ); 47 | parses( sprintf 'sub %sl {1}', $func ); 48 | parses( sprintf 'sub %sl%s {1}', $letter1, $letter2 ); 49 | parses( sprintf 'sub %s%s {1}', $letter1, uc $letter2 ); 50 | parses( sprintf 'sub %s%s {1}', uc $letter1, $letter2 ); 51 | parses( sprintf 'sub %s {1}', uc $func ); 52 | } 53 | } 54 | 55 | parses('sub t {1}'); 56 | parses('sub r {1}'); 57 | parses('sub x {1}'); 58 | parses('sub w {1}'); 59 | 60 | parsent('sub 14 {1}'); # Numbers cannot be subnames 61 | parsent('sub 14_f {1}'); # Numbers cannot be subnames, even with underscores 62 | parsent('sub 14f {1}'); # Numbers cannot be subnames, even with letters 63 | parses('sub trz {1}'); 64 | parses('sub rtz {1}'); 65 | 66 | done_testing(); 67 | -------------------------------------------------------------------------------- /README.mkdn: -------------------------------------------------------------------------------- 1 | # NAME 2 | 3 | Guacamole - A parser toolkit for Standard Perl 4 | 5 | # VERSION 6 | 7 | version 0.008 8 | 9 | # SYNOPSIS 10 | 11 | use Guacamole; 12 | my ($ast) = Guacamole->parse($string); 13 | 14 | # DESCRIPITON 15 | 16 | **Guacamole** is a Perl parser toolkit. 17 | 18 | It can: 19 | 20 | - Parse Standard Perl 21 | 22 | This is explained in this document. 23 | 24 | For **Standard Perl**, see the next clause. 25 | 26 | - Check a file is written in Standard Perl 27 | 28 | This is done by [standard](https://metacpan.org/pod/standard), which is where Standard Perl is described. 29 | 30 | - Lint your code 31 | 32 | See [Guacamole::Linter](https://metacpan.org/pod/Guacamole%3A%3ALinter). 33 | 34 | - Deparse your code 35 | 36 | See [Guacamole::Deparse](https://metacpan.org/pod/Guacamole%3A%3ADeparse). 37 | 38 | - Rewrite your code 39 | 40 | There is a proof-of-concept for this and we hope to provide this as a framework. 41 | 42 | # Standard Perl 43 | 44 | Guacamole only works on Standard Perl. You can read about it here: [standard](https://metacpan.org/pod/standard). 45 | 46 | # Parser 47 | 48 | my ($ast) = Guacamole->parse($string); 49 | 50 | To parse a string, call [Gaucamole](https://metacpan.org/pod/Gaucamole)'s `parse` method. (This might turn to an 51 | object-oriented interface in the future.) 52 | 53 | It returns a list of results. If it ever returns more than one, this is a bug that 54 | means it couldn't ambiguously parse something. This will later be enforced in the 55 | interface. The current interface is not official. 56 | 57 | ## AST Nodes 58 | 59 | Guacamole returns an AST with two types of nodes. 60 | 61 | my ($ast) = Guacamole->parse('$foo = 1'); 62 | 63 | The above will generate a larger AST than you imagine (which might be pruned 64 | in the future). We'll focus on two types of nodes that will appear above. 65 | 66 | ### Rules 67 | 68 | Rules are the top level expressions. They include the definitions for rules. 69 | They include information on location in the file, length, line, and column. 70 | 71 | $rule = { 72 | 'children' => [...], 73 | 'column' => 2, 74 | 'length' => 3, 75 | 'line' => 1, 76 | 'name' => 'VarIdentExpr', 77 | 'start_pos' => 1, 78 | 'type' => 'rule', 79 | }, 80 | 81 | This rule is a `VarIdentExpr` which is an expression for a variable identity. 82 | 83 | In the code above, it refers to the `foo` in `$foo` - which is the identity 84 | itself. 85 | 86 | It has one child, described below under `Lexemes`. 87 | 88 | ### Lexemes 89 | 90 | The child for the `VarIdentExpr` rule should be the value of the identity. 91 | 92 | $lexeme = { 93 | 'name' => '', 94 | 'type' => 'lexeme', 95 | 'value' => 'foo', 96 | }; 97 | 98 | The `name` attribute for all lexemes is empty. This is to make it easy to 99 | write code that checks for the value of a rule without having to check whether 100 | it's a rule first. 101 | 102 | # THANKS 103 | 104 | - Damian Conway 105 | 106 | For helping understand what is feasible, what isn't, and why, and for having 107 | infinite patience in explaining these. 108 | 109 | - Jeffrey Kegler 110 | 111 | For [Marpa](https://metacpan.org/pod/Marpa) and helping understand how to use Marpa better. 112 | 113 | - Gonzalo Diethelm 114 | 115 | For continuous feedback and support. 116 | 117 | - H. Merijn Brand (@Tux) 118 | 119 | For providing the initial production-level test of Guacamole to 120 | help shake many of the bugs in the BNF. 121 | 122 | # SEE ALSO 123 | 124 | - [standard](https://metacpan.org/pod/standard) 125 | - [Gaucamole::Linter](https://metacpan.org/pod/Gaucamole%3A%3ALinter) 126 | - [Guacamole::Deparse](https://metacpan.org/pod/Guacamole%3A%3ADeparse) 127 | 128 | # AUTHORS 129 | 130 | - Sawyer X 131 | - Vickenty Fesunov 132 | 133 | # COPYRIGHT AND LICENSE 134 | 135 | This software is Copyright (c) 2022 by Sawyer X. 136 | 137 | This is free software, licensed under: 138 | 139 | The MIT (X11) License 140 | -------------------------------------------------------------------------------- /t/Statements/Expressions/Value/QLikeValue.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use experimental qw< postderef >; 4 | 5 | use Test::More; 6 | use Test::Fatal qw< exception >; 7 | use Guacamole::Test; 8 | 9 | # qw is using spaces in between - that's different 10 | # qr can have regex modifiers - that's different 11 | # here we're just testing the delimiters 12 | my @q_functions = ( 'q', 'qq', 'qw', 'qx', 'qr', 'm' ); 13 | 14 | my @delimiters = ( 15 | [ '(', ')' ], # q(...) | q() 16 | [ '{', '}' ], # q{...} | q{} 17 | [ '<', '>' ], # q<...> | q<> 18 | [ '/', '/' ], # q/.../ | q// 19 | [ '!', '!' ], # q!...! | q!! 20 | [ '|', '|' ], # q|...| | q|| 21 | ); 22 | 23 | foreach my $function (@q_functions) { 24 | # This condition helps test with modifiers too 25 | my $delimiters = ( $function eq 'm' || $function eq 'qr' ) ? 'xms' : ''; 26 | 27 | foreach my $delimiter_set (@delimiters) { 28 | my $simple_string = sprintf 'say %s%s$foo%s%s', 29 | $function, 30 | $delimiter_set->@[ 0, 1 ], 31 | $delimiters; 32 | 33 | parses($simple_string); 34 | 35 | my $escaped_string = sprintf 'say %s%s \\%s $foo \\%s %s%s', 36 | $function, 37 | $delimiter_set->@[ 0, 0, 1, 1 ], 38 | $delimiters; 39 | 40 | parses($escaped_string); 41 | 42 | # and one without delimiters 43 | my $emptystring = sprintf 'say %s%s%s%s', $function, 44 | $delimiter_set->@[ 0, 1 ], 45 | $delimiters; 46 | 47 | parses($emptystring); 48 | 49 | my $bad_string = sprintf 'say %s %s%s', $function, 50 | $delimiter_set->@[ 0, 1 ]; 51 | 52 | # q-like value with space will fail 53 | # this includes s / m / y / tr 54 | parsent($bad_string); 55 | } 56 | } 57 | 58 | parses('$foo =~ /foo/'); 59 | parses('$foo =~ /foo/xms'); 60 | parses('$foo =~ //'); 61 | parses('$foo =~ //xms'); 62 | 63 | # Should this work? 64 | #parses(' 65 | #s {foo} # Replace foo 66 | # {bar} # with bar. 67 | #'); 68 | 69 | parses('$foo =~ /foo/'); 70 | parses('$foo =~ /foo/xms'); 71 | parses('$foo =~ //'); 72 | parses('$foo =~ //xms'); 73 | 74 | parses('tr/h-k/H-K/'); 75 | parses('y/h-k/H-K/'); 76 | parses('$foo =~ s/foo/bar/'); 77 | parses('$foo =~ s/foo//'); 78 | parses('$foo =~ s///'); 79 | 80 | parses('tr{h-k}{H-K}'); 81 | parses('y{h-k}{H-K}'); 82 | parses('$foo =~ s{foo}{bar}'); 83 | parses('$foo =~ s{foo}{}'); 84 | parses('$foo =~ s{}{}'); 85 | parses('$foo =~ s{}{}g'); 86 | 87 | parses('`foo`'); 88 | parses('``'); 89 | 90 | # make sure we didn't screw up non q-like subroutine parsing 91 | 92 | foreach my $func ( qw< q qq qw qx qr s m y tr > ) { 93 | if ( length $func == 1 ) { 94 | # single q-like letter 95 | parses('z()'); # NonQLike single char 96 | parses("${func}z()"); # NonQLike double char with leading q 97 | parses("$func\_z()"); # NonQLike with underscore, leading q 98 | parses("$func\_()"); # NonQLike with underscore, leading q 99 | parses("_$func()"); # NonQLike with underscore, leading q 100 | parses( uc($func) . '()'); 101 | } 102 | 103 | 104 | if ( length $func == 2 ) { 105 | my ( $letter1, $letter2 ) = split //xms, $func; 106 | # double q-like letters 107 | parses( sprintf '%s_()', $func ); 108 | parses( sprintf '%s_%s()', $letter1, $letter2 ); 109 | parses( sprintf '%s%s()', $func, $letter1 ); 110 | parses( sprintf '%s%s()', $func, $letter2 ); 111 | parses( sprintf '%sl()', $func ); 112 | parses( sprintf '%sl%s()', $letter1, $letter2 ); 113 | parses( sprintf '%s%s()', $letter1, uc $letter2 ); 114 | parses( sprintf '%s%s()', uc $letter1, $letter2 ); 115 | parses( sprintf '%s()', uc $func ); 116 | } 117 | } 118 | 119 | parses('t()'); 120 | parses('r()'); 121 | parses('x()'); 122 | parses('w()'); 123 | 124 | parsent('14()'); # Numbers cannot be subnames 125 | parsent('14_f()'); # Numbers cannot be subnames, even with underscores 126 | parsent('14f()'); # Numbers cannot be subnames, even with letters 127 | parses('trz()'); 128 | parses('rtz()'); 129 | 130 | done_testing; 131 | -------------------------------------------------------------------------------- /lib/standard.pm: -------------------------------------------------------------------------------- 1 | package standard; 2 | # ABSTRACT: Enforce Standard Perl syntax with Guacamole 3 | 4 | use strict; 5 | use warnings; 6 | use experimental qw< signatures >; 7 | use Guacamole; 8 | use Path::Tiny (); 9 | 10 | sub import ( $class ) { 11 | my @caller = caller(); 12 | my $caller_file = $caller[1]; 13 | my $content = Path::Tiny::path($caller_file)->slurp_utf8(); 14 | 15 | # Filter out everything past __DATA__ or __END__ 16 | strip_terminators(\$content); 17 | 18 | # Strip POD, it isn't Perl 19 | strip_pods(\$content); 20 | 21 | eval { Guacamole->parse($content); } 22 | or do { 23 | print STDERR "File '$caller_file' does not pass Standard Perl.\n" 24 | . "Parser says:\n" 25 | . join '', map "> $_\n", split /\n/xms, $@; 26 | }; 27 | 28 | return; 29 | } 30 | 31 | sub strip_terminators ($contentref) { 32 | ${$contentref} =~ s/^__DATA__\n.*//xms; 33 | ${$contentref} =~ s/^__END__\n.*//xms; 34 | } 35 | 36 | sub strip_pods ($contentref) { 37 | my @content_lines = split /\n/, ${$contentref}; 38 | my $in_pod; 39 | foreach my $line (@content_lines) { 40 | if ( $line =~ /^=(?!cut)/ ) { 41 | $in_pod = 1; 42 | } 43 | 44 | $in_pod 45 | or next; 46 | 47 | $line =~ s{^}{#}xms; 48 | 49 | # This is after replace, so it's already commented out 50 | if ( $line =~ /^#=cut$/ ) { 51 | $in_pod = 0; 52 | } 53 | } 54 | 55 | ${$contentref} = join "\n", @content_lines; 56 | } 57 | 58 | 1; 59 | 60 | __END__ 61 | 62 | =pod 63 | 64 | =head1 SYNOPSIS 65 | 66 | use standard; 67 | # Now you will get a warning if you don't conform to Standard Perl 68 | 69 | =head1 DESCRIPTION 70 | 71 | B aims to use only the syntax that makes Perl easy to parse. 72 | A Perl static parser such as L isn't that hard if we avoid a small 73 | set of constructs that cause parser ambiguities. 74 | 75 | These changes are described below. Over time, this documentation will explain 76 | the standard itself versus the differences between this subset and what the 77 | perl interpreter supports. 78 | 79 | =head1 DIFFERENCES 80 | 81 | =head2 Things not supported 82 | 83 | This list covers constructs that the perl interpreter understands (for whatever 84 | value of "understand" is) but that that Standard Perl does not support. 85 | 86 | =over 4 87 | 88 | =item * Auto-quoting 89 | 90 | Perl's auto-quoting rules are... rather elaborate and awkward. Much of it is 91 | unknown and can even depend on lowercase vs. uppercase and specific letters with 92 | special meaning to the interpreter and not the user. 93 | 94 | Thus, a string in Standard Perl is always quoted. 95 | 96 | $foo{key} # not ok 97 | $foo{'key'} # ok 98 | 99 | %hash = ( foo => 'bar' ); # not ok 100 | %hash = ( 'foo' => 'bar' ); # ok 101 | 102 | =item * HEREDOCs 103 | 104 | HEREDOCs are a monstrosity for parsers and cannot be expressed with a BNF. It is 105 | thus not supported. 106 | 107 | # This will fail 108 | my $value = << '_END_OF_VALUE'; 109 | ... 110 | _END_OF_VALUE 111 | 112 | # This is an alternative 113 | my $value = 114 | q{... 115 | }; 116 | 117 | # This is another alternative: 118 | my $value = q{ 119 | ... 120 | } =~ s/^\n//r; 121 | 122 | =item * Indirect object notation 123 | 124 | my $instance = new Class; # not ok 125 | my $instance = Class->new(); # ok 126 | 127 | =item * Bareword filehandles 128 | 129 | open FOO, ... # not ok 130 | open my $fh, ... # ok 131 | open $fh, ... # ok 132 | 133 | print STDOUT $foo; # ok 134 | print STDERR $foo; # ok 135 | 136 | while ( ) {...} # not ok 137 | while ( <$foo> ) {...} # ok 138 | while ( ) {...} # ok 139 | 140 | The following bareword filehandles are supported: 141 | 142 | =over 4 143 | 144 | =item * C 145 | 146 | =item * C 147 | 148 | =item * C 149 | 150 | =item * C 151 | 152 | =item * C 153 | 154 | =item * C 155 | 156 | =back 157 | 158 | =item * Printing to filehandles with no brace 159 | 160 | print $fh $foo; # not ok 161 | print {$fh} $foo; # ok 162 | 163 | 164 | =item * C<_> outside file operations 165 | 166 | if ( -f $foo && -r _ ) {...} # ok 167 | print {$fh} _ # not ok 168 | 169 | C<_> should only be used as a bareword identifier within C<-X> file 170 | operations. In the C example, Perl understands it as printing 171 | an underscore character (C<"_">) to the filehandle, which is just 172 | odd, so we do not support that. 173 | 174 | =item * C / C / C 175 | 176 | Not supported. 177 | 178 | =item * Anonymous hashes vs. bare block as top-level expressions 179 | 180 | # not ok 181 | package Foo { 182 | { 'bar' => 'baz' }; 183 | } 184 | 185 | The above shows a useless use of anonymous hash (per the Perl warning 186 | on it) as a top-level expression (in this case, inside a C 187 | block). 188 | 189 | Standard Perl does not support this since it can be confused with a 190 | bare code block. Instead, you should add some top-level token to 191 | disambiguate: 192 | 193 | # ok 194 | package Foo { 195 | +{ 'bar' => 'baz' } 196 | } 197 | 198 | It's still useless (and Perl would warn about it), but it's allowed. 199 | 200 | # not ok: 201 | sub foo { 202 | { 'a' => 'b' }; 203 | } 204 | 205 | # ok 206 | sub foo { 207 | +{ 'a' => 'b' }; # "+" is a top-level token 208 | } 209 | 210 | # ok 211 | sub foo { 212 | return { 'a' => 'b' }; # used as argument to "return" 213 | } 214 | 215 | =back 216 | 217 | =head2 Things we changed 218 | 219 | The following are limitations that Standard Perl has which the perl 220 | interpreter doesn't. 221 | 222 | =head3 Q-Like values 223 | 224 | Q-Like values are one of the following: C, C, C, C, C 225 | 226 | However, the following limitations also apply to: C, C C, 227 | and C. 228 | 229 | =over 4 230 | 231 | =item * No nested delimiters 232 | 233 | $val = q< <> >; # not ok 234 | $val = q< \<\> >; # ok 235 | 236 | If you want to use the delimiter within delimited space, escape it. 237 | 238 | =item * Limited delimiters 239 | 240 | Only the following delimiters are supported: 241 | 242 | C<()>, C<[]>, C<{}>, C<< E E >>, C, C, and C<||>. 243 | 244 | $val = q(...) # ok 245 | $val = q[...] # ok 246 | $val = q{...} # ok 247 | $val = q<...> # ok 248 | $val = q/.../ # ok 249 | $val = q!...! # ok 250 | $val = q|...| # ok 251 | 252 | $val = q@...@ # not ok 253 | $val = q#...# # not ok 254 | $val = q Z ... Z # not ok 255 | 256 | If you want to advocate for another set of delimiters, open a ticket. 257 | 258 | =item * No spaces between before delimiters in Q-like values: 259 | 260 | q # not ok 261 | q < foo > # not ok 262 | q () # not ok 263 | 264 | q # ok 265 | q< foo >; # ok 266 | q() # ok 267 | 268 | =back 269 | 270 | =head3 Subroutines 271 | 272 | =over 4 273 | 274 | =item * B subroutines must use parentheses 275 | 276 | foo $bar # not ok 277 | foo($bar) # ok 278 | 279 | There is an exception for methods: 280 | 281 | $foo->bar() # ok 282 | $foo->bar # ok 283 | 284 | $foo->bar()->baz() # ok 285 | $foo->bar->baz # ok 286 | 287 | =item * Subroutines can have attributes and signatures 288 | 289 | Standard Perl accepts both attributes and signatures. 290 | 291 | =item * All subroutine prototypes must be declared using an attribute 292 | 293 | sub foo ($) {...} # signature, not prototype 294 | sub foo :prototype($) {...} # prototype, not signature 295 | 296 | =item * Prototypes do not change the parsing rules 297 | 298 | first {...} @foo # not ok 299 | first( sub {...}, @foo ) # ok 300 | 301 | There is preliminary support for adding your own keywords by hooking 302 | into the grammar of the L parser, but it is B stage, 303 | so it's not widely docuemnted. 304 | 305 | This will be useful for stuff like L, L, 306 | L, L, L, etc. 307 | 308 | Having said that, Standard Perl will likely never prototypes directly, 309 | nor should it. 310 | 311 | =back 312 | 313 | =head3 Class names 314 | 315 | =over 4 316 | 317 | =item * Left of arrow is always an invocant, never a function 318 | 319 | Foo->new(); # always a class, never a function "Foo" 320 | 321 | This is tricky because the perl interpreter might see a function called 322 | C in the same scope and call that instead. This would mean that 323 | Standard Perl and the perl interpreter would report different results. 324 | 325 | We have a shim layer in L that checks for this and alerts if 326 | this will happen, so you never hit this issue when using C. 327 | 328 | We advise other parsers who use Standard Perl BNF to include this part. 329 | 330 | =item * Namespaces cannot end with a double colon 331 | 332 | Foo->bar(); # ok 333 | Foo::->bar(); # not ok 334 | 335 | This might be changed in the future. 336 | 337 | =back 338 | 339 | =head3 Dereferencing 340 | 341 | =over 4 342 | 343 | =item * Prefixed dereferencing is only supported with braces 344 | 345 | @$foo # not ok 346 | @{$foo} # ok 347 | $foo->@* # ok 348 | 349 | =back 350 | 351 | =head3 Expressions 352 | 353 | =over 4 354 | 355 | =item * C that attempts to return a pair must use parenthesis 356 | 357 | map { $_ => 1 }, @foo # not ok 358 | map { ( $_ => 1 ) }, @foo # ok 359 | 360 | =back 361 | 362 | =head1 SEE ALSO 363 | 364 | L 365 | -------------------------------------------------------------------------------- /tools/standardize: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | use strict; 3 | use warnings; 4 | use experimental qw< postderef signatures >; 5 | use DDP; 6 | use PPI::Document; 7 | use Git::Wrapper; 8 | use Path::Tiny qw< path >; 9 | use Getopt::Long qw< :config no_ignore_case >; 10 | 11 | # This changes your code to conform to standard.pm, as much as it can 12 | 13 | sub help ( $error = '' ) { 14 | $error 15 | and print "Error: $error\n\n"; 16 | 17 | print qq{$0 [OPTIONS] 18 | 19 | Options: 20 | 21 | -h | --help Print this help menu and exit 22 | -v | --verbose Be more verbose 23 | }; 24 | 25 | exit $error ? 1 : 0; 26 | } 27 | 28 | sub check_if_under_git ($file_path) { 29 | if ( !$file_path->is_file() ) { 30 | warn "Path '$file_path' is not a file\n"; 31 | return; 32 | }; 33 | 34 | my $dir = $file_path->dirname(); 35 | my $git = Git::Wrapper->new($dir); 36 | 37 | my $file_under_git = $git->RUN( 'ls-files', "$file_path" ); 38 | 39 | # TODO: Check whether it's clean or not 40 | # just because it's tracked, doesn't mean it's clean 41 | 42 | if ( !$file_under_git ) { 43 | print "$0 must be run from a Git repository, skipping...\n"; 44 | return; 45 | } 46 | 47 | return 1; 48 | } 49 | 50 | sub fix_code ($doc) { 51 | fix_autoquoting($doc); 52 | fix_simple_deref($doc); 53 | } 54 | 55 | sub fix_autoquoting ($doc) { 56 | # Handle left of fat comma (=>) 57 | my $operators = $doc->find('PPI::Token::Operator') 58 | or return; 59 | 60 | my @fatcomma_ops = grep $_->content() eq '=>', $operators->@* 61 | or return; 62 | 63 | foreach my $op (@fatcomma_ops) { 64 | my $word = $op->sprevious_sibling 65 | or next; 66 | 67 | should_fix_elem_autoquote($word) 68 | and quote_elem($word); 69 | } 70 | 71 | # Handle autoquoting in hash access 72 | my @subscript_ops = $doc->find('PPI::Structure::Subscript')->@*; 73 | foreach my $op (@subscript_ops) { 74 | $op->start() eq '{' 75 | or next; 76 | 77 | # ignore empty subscripts 78 | my $first_child = ( $op->children )[0] 79 | or next; 80 | 81 | # Try to find an expression 82 | my @exprs = grep !$_->isa('PPI::Token::Whitespace'), 83 | $op->children(); 84 | 85 | @exprs == 1 86 | or next; 87 | 88 | # Then try to find the next major element 89 | my @elems = grep !$_->isa('PPI::Token::Whitespace'), 90 | $exprs[0]->children(); 91 | 92 | # ignore multi-element subscripts 93 | @elems == 1 94 | or next; 95 | 96 | # Find if there are spaces before and after 97 | my $last_child = ( $op->children )[-1]; 98 | my $pre_or_post_newline 99 | = grep $_->content() =~ /\n/xms, 100 | get_all_ws( 'next', $first_child ), 101 | get_all_ws( 'prev', $last_child ); 102 | 103 | $pre_or_post_newline 104 | and next; 105 | 106 | my $elem = $elems[0]; 107 | 108 | should_fix_elem_autoquote($elem) 109 | and quote_elem($elem); 110 | } 111 | } 112 | 113 | sub fix_simple_deref ($doc) { 114 | # Handle ${$foo}, @{$foo}, %{$foo} 115 | my $operators = $doc->find('PPI::Token::Cast') 116 | or return; 117 | 118 | foreach my $cast_op ( $operators->@* ) { 119 | my $cast_string = $cast_op->content(); 120 | my $var_op = $cast_op->snext_sibling(); 121 | 122 | $var_op->isa('PPI::Token::Symbol') 123 | or next; 124 | 125 | my $var_string = $var_op->content(); 126 | 127 | $cast_op->set_content( 128 | sprintf '%s{%s}', $cast_string, $var_string 129 | ); 130 | 131 | $var_op->remove(); 132 | } 133 | } 134 | 135 | sub should_fix_elem_autoquote ($elem) { 136 | $elem =~ /^['"].*['"]$/ 137 | and return 0; 138 | 139 | # =>\n breaks auto-quoting 140 | my @next_ws = get_all_ws( 'next', $elem ); 141 | grep $_->content() =~ /\n/xms, @next_ws 142 | and return 0; 143 | 144 | $elem->isa('PPI::Token::Word') 145 | and return 1; 146 | } 147 | 148 | sub get_all_ws ( $loc, $elem ) { 149 | my $iter = $loc eq 'prev' 150 | ? sub ($i) { $i->previous_sibling() } 151 | : sub ($i) { $i->next_sibling() }; 152 | 153 | my @all; 154 | $elem && $elem->isa('PPI::Token::Whitespace') 155 | and push @all, $elem; 156 | 157 | my $item = $iter->($elem); 158 | while ( $item && $item->isa('PPI::Token::Whitespace') ) { 159 | push @all, $item; 160 | $item = $iter->($item); 161 | } 162 | 163 | return @all; 164 | } 165 | 166 | sub quote_elem ($elem) { 167 | $elem->set_content( sprintf "'%s'", $elem->content ); 168 | } 169 | 170 | my %opts; 171 | GetOptions( 172 | 'help|h' => \$opts{'help'}, 173 | 'verbose|v' => \$opts{'verbose'}, 174 | ); 175 | 176 | $opts{'help'} 177 | and help(); 178 | 179 | my $arg = $ARGV[0] 180 | or help('Missing which file or directory to operate on'); 181 | 182 | my $path = path($arg); 183 | $path->is_dir 184 | and help('We don\'t support directories yet...'); 185 | 186 | my $path_content = $path->slurp_utf8(); 187 | my $ppi_doc = PPI::Document->new( \$path_content ); 188 | 189 | fix_code($ppi_doc); 190 | 191 | print "$ppi_doc\n"; 192 | 193 | # TODO: turn this on 194 | #$path->spew_utf8($fixed_content); 195 | 196 | __END__ 197 | 198 | $foo{"hi"} 199 | 200 | PPI::Document 201 | PPI::Statement 202 | PPI::Token::Symbol '$foo' 203 | PPI::Structure::Subscript { ... } 204 | PPI::Statement::Expression 205 | PPI::Token::Quote::Double '"hi"' 206 | 207 | 208 | 209 | $foo{"hello"}[0]{"hi"} 210 | 211 | PPI::Document 212 | PPI::Statement 213 | PPI::Token::Symbol '$foo' 214 | PPI::Structure::Subscript { ... } 215 | PPI::Statement::Expression 216 | PPI::Token::Quote::Double '"hello"' 217 | PPI::Structure::Subscript [ ... ] 218 | PPI::Statement::Expression 219 | PPI::Token::Number '0' 220 | PPI::Structure::Subscript { ... } 221 | PPI::Statement::Expression 222 | PPI::Token::Quote::Double '"hi"' 223 | 224 | 225 | 226 | foo( { "hello" => 1 } ) 227 | 228 | PPI::Document 229 | PPI::Statement 230 | PPI::Token::Word 'foo' 231 | PPI::Structure::List ( ... ) 232 | PPI::Statement 233 | PPI::Structure::Constructor { ... } 234 | PPI::Statement 235 | PPI::Token::Quote::Double '"hello"' 236 | PPI::Token::Operator '=>' 237 | PPI::Token::Number '1' 238 | 239 | 240 | 241 | __END__ 242 | PPI::Document 243 | PPI::Statement 244 | PPI::Token::Symbol '$foo' 245 | PPI::Structure::Subscript { ... } 246 | PPI::Statement::Expression 247 | PPI::Token::Word 'hello' 248 | PPI::Token::Whitespace ' ' 249 | PPI::Token::Operator '=' 250 | PPI::Token::Whitespace ' ' 251 | PPI::Token::Quote::Double '"hi"' 252 | PPI::Token::Structure ';' 253 | PPI::Token::Whitespace '\n' 254 | PPI::Statement 255 | PPI::Token::Symbol '$foo' 256 | PPI::Token::Whitespace ' ' 257 | PPI::Token::Operator '=' 258 | PPI::Token::Whitespace ' ' 259 | PPI::Structure::Constructor { ... } 260 | PPI::Token::Whitespace ' ' 261 | PPI::Statement::Expression 262 | PPI::Token::Word 'hello' 263 | PPI::Token::Whitespace ' ' 264 | PPI::Token::Operator '=>' 265 | PPI::Token::Whitespace ' ' 266 | PPI::Token::Quote::Double '"hi"' 267 | PPI::Token::Whitespace ' ' 268 | PPI::Token::Structure ';' 269 | PPI::Token::Whitespace '\n' 270 | PPI::Statement 271 | PPI::Token::Symbol '%foo' 272 | PPI::Token::Whitespace ' ' 273 | PPI::Token::Operator '=' 274 | PPI::Token::Whitespace ' ' 275 | PPI::Structure::List ( ... ) 276 | PPI::Token::Whitespace ' ' 277 | PPI::Statement::Expression 278 | PPI::Token::Word 'hello' 279 | PPI::Token::Whitespace ' ' 280 | PPI::Token::Operator '=>' 281 | PPI::Token::Whitespace ' ' 282 | PPI::Token::Quote::Double '"hi"' 283 | PPI::Token::Whitespace ' ' 284 | PPI::Token::Structure ';' 285 | PPI::Token::Whitespace '\n' 286 | PPI::Statement 287 | PPI::Token::Word 'foo' 288 | PPI::Structure::List ( ... ) 289 | PPI::Token::Whitespace ' ' 290 | PPI::Statement::Expression 291 | PPI::Token::Word 'hello' 292 | PPI::Token::Whitespace ' ' 293 | PPI::Token::Operator '=>' 294 | PPI::Token::Whitespace ' ' 295 | PPI::Token::Quote::Double '"hi"' 296 | PPI::Token::Operator ',' 297 | PPI::Token::Whitespace ' ' 298 | PPI::Token::Word 'foo' 299 | PPI::Token::Whitespace ' ' 300 | PPI::Token::Operator '=>' 301 | PPI::Token::Whitespace ' ' 302 | PPI::Token::Word 'bar' 303 | PPI::Token::Whitespace ' ' 304 | PPI::Token::Operator '=>' 305 | PPI::Token::Whitespace ' ' 306 | PPI::Token::Word 'baz' 307 | PPI::Token::Whitespace ' ' 308 | PPI::Token::Operator '=>' 309 | PPI::Token::Whitespace ' ' 310 | PPI::Token::Number '1' 311 | PPI::Token::Whitespace ' ' 312 | PPI::Token::Structure ';' 313 | PPI::Token::Whitespace '\n' 314 | PPI::Token::Whitespace '\n' 315 | PPI::Token::Comment '# with newlines\n' 316 | PPI::Statement 317 | PPI::Token::Symbol '$foo' 318 | PPI::Structure::Subscript { ... } 319 | PPI::Token::Whitespace '\n' 320 | PPI::Statement::Expression 321 | PPI::Token::Word 'hello' 322 | PPI::Token::Whitespace '\n' 323 | PPI::Token::Whitespace ' ' 324 | PPI::Token::Operator '=' 325 | PPI::Token::Whitespace ' ' 326 | PPI::Token::Quote::Double '"hi"' 327 | PPI::Token::Structure ';' 328 | PPI::Token::Whitespace '\n' 329 | PPI::Statement 330 | PPI::Token::Symbol '$foo' 331 | PPI::Token::Whitespace ' ' 332 | PPI::Token::Operator '=' 333 | PPI::Token::Whitespace ' ' 334 | PPI::Structure::Constructor { ... } 335 | PPI::Token::Whitespace '\n' 336 | PPI::Token::Whitespace ' ' 337 | PPI::Statement::Expression 338 | PPI::Token::Word 'hello' 339 | PPI::Token::Whitespace '\n' 340 | PPI::Token::Whitespace ' ' 341 | PPI::Token::Operator '=>' 342 | PPI::Token::Whitespace ' ' 343 | PPI::Token::Quote::Double '"hi"' 344 | PPI::Token::Whitespace ' ' 345 | PPI::Token::Structure ';' 346 | PPI::Token::Whitespace '\n' 347 | PPI::Statement 348 | PPI::Token::Symbol '%foo' 349 | PPI::Token::Whitespace ' ' 350 | PPI::Token::Operator '=' 351 | PPI::Token::Whitespace ' ' 352 | PPI::Structure::List ( ... ) 353 | PPI::Token::Whitespace '\n' 354 | PPI::Statement::Expression 355 | PPI::Token::Word 'hello' 356 | PPI::Token::Whitespace ' ' 357 | PPI::Token::Operator '=>' 358 | PPI::Token::Whitespace ' ' 359 | PPI::Token::Quote::Double '"hi"' 360 | PPI::Token::Whitespace ' ' 361 | PPI::Token::Structure ';' 362 | PPI::Token::Whitespace '\n' 363 | PPI::Statement 364 | PPI::Token::Word 'foo' 365 | PPI::Structure::List ( ... ) 366 | PPI::Token::Whitespace '\n' 367 | PPI::Token::Whitespace ' ' 368 | PPI::Statement::Expression 369 | PPI::Token::Word 'hello' 370 | PPI::Token::Whitespace ' ' 371 | PPI::Token::Operator '=>' 372 | PPI::Token::Whitespace ' ' 373 | PPI::Token::Quote::Double '"hi"' 374 | PPI::Token::Operator ',' 375 | PPI::Token::Whitespace '\n' 376 | PPI::Token::Whitespace ' ' 377 | PPI::Token::Word 'foo' 378 | PPI::Token::Whitespace ' ' 379 | PPI::Token::Operator '=>' 380 | PPI::Token::Whitespace ' ' 381 | PPI::Token::Quote::Double '"bar"' 382 | PPI::Token::Whitespace ' ' 383 | PPI::Token::Operator '=>' 384 | PPI::Token::Whitespace '\n' 385 | PPI::Token::Whitespace ' ' 386 | PPI::Token::Word 'baz' 387 | PPI::Token::Whitespace ' ' 388 | PPI::Token::Operator '=>' 389 | PPI::Token::Whitespace ' ' 390 | PPI::Token::Number '1' 391 | PPI::Token::Whitespace '\n' 392 | PPI::Token::Structure ';' 393 | PPI::Token::Whitespace '\n' 394 | PPI::Token::Whitespace '\n' 395 | 396 | -------------------------------------------------------------------------------- /lib/Guacamole.pm: -------------------------------------------------------------------------------- 1 | package Guacamole; 2 | # ABSTRACT: A parser toolkit for Standard Perl 3 | 4 | use strict; 5 | use warnings; 6 | use feature qw< state >; 7 | use constant { 8 | 'DEBUG' => 0, 9 | }; 10 | 11 | use Marpa::R2; 12 | 13 | our $grammar_source = q{ 14 | lexeme default = latm => 1 15 | :default ::= action => [ name, start, length, values ] 16 | 17 | Program ::= StatementSeq 18 | 19 | StatementSeq ::= Statement 20 | | Statement Semicolon 21 | | Statement Semicolon StatementSeq 22 | | BlockStatement 23 | | BlockStatement StatementSeq 24 | 25 | # Statements that end with a block and do not need a semicolon terminator. 26 | BlockStatement ::= LoopStatement 27 | | PackageStatement 28 | | SubStatement 29 | | Condition 30 | | BlockNonEmpty 31 | 32 | Statement ::= BlockLevelExpression StatementModifier 33 | | BlockLevelExpression 34 | | EllipsisStatement 35 | | UseStatement 36 | | NoStatement 37 | | RequireStatement 38 | | PackageDeclaration 39 | | SubDeclaration 40 | 41 | LoopStatement ::= ForStatement 42 | | WhileStatement 43 | | UntilStatement 44 | 45 | ForStatement ::= ForStatementOp LParen Statement Semicolon Statement Semicolon Statement RParen Block ContinueExpr 46 | | ForStatementOp LParen Statement Semicolon Statement Semicolon Statement RParen Block 47 | | ForStatementOp LParen Semicolon Statement Semicolon Statement RParen Block ContinueExpr 48 | | ForStatementOp LParen Semicolon Statement Semicolon Statement RParen Block 49 | | ForStatementOp LParen Statement Semicolon Semicolon Statement RParen Block ContinueExpr 50 | | ForStatementOp LParen Statement Semicolon Semicolon Statement RParen Block 51 | | ForStatementOp LParen Statement Semicolon Statement Semicolon RParen Block ContinueExpr 52 | | ForStatementOp LParen Statement Semicolon Statement Semicolon RParen Block 53 | | ForStatementOp LParen Semicolon Semicolon Statement RParen Block ContinueExpr 54 | | ForStatementOp LParen Semicolon Semicolon Statement RParen Block 55 | | ForStatementOp LParen Statement Semicolon Semicolon RParen Block ContinueExpr 56 | | ForStatementOp LParen Statement Semicolon Semicolon RParen Block 57 | | ForStatementOp OpKeywordMy VarScalar LParen Expression RParen Block ContinueExpr 58 | | ForStatementOp OpKeywordMy VarScalar LParen Expression RParen Block 59 | | ForStatementOp VarScalar LParen Expression RParen Block ContinueExpr 60 | | ForStatementOp VarScalar LParen Expression RParen Block 61 | | ForStatementOp LParen Semicolon Semicolon RParen Block ContinueExpr 62 | | ForStatementOp LParen Semicolon Semicolon RParen Block 63 | | ForStatementOp LParen Expression RParen Block ContinueExpr 64 | | ForStatementOp LParen Expression RParen Block 65 | 66 | ContinueExpr ::= OpKeywordContinue Block 67 | 68 | ForStatementOp ::= OpKeywordFor 69 | | OpKeywordForeach 70 | 71 | WhileStatement ::= ConditionWhile LParen Expression RParen Block OpKeywordContinue Block 72 | | ConditionWhile LParen Expression RParen Block 73 | | ConditionWhile LParen RParen Block OpKeywordContinue Block 74 | | ConditionWhile LParen RParen Block 75 | 76 | UntilStatement ::= ConditionUntil LParen Expression RParen Block OpKeywordContinue Block 77 | | ConditionUntil LParen Expression RParen Block 78 | 79 | StatementModifier ::= ConditionIfPostfixExpr 80 | | ConditionUnlessPostfixExpr 81 | | ConditionWhilePostfixExpr 82 | | ConditionUntilPostfixExpr 83 | | ConditionForPostfixExpr 84 | | ConditionForeachPostfixExpr 85 | 86 | EllipsisStatement ::= Ellipsis 87 | 88 | UseStatement ::= OpKeywordUse ClassIdent VersionExpr Expression 89 | | OpKeywordUse ClassIdent VersionExpr 90 | | OpKeywordUse ClassIdent Expression 91 | | OpKeywordUse VersionExpr 92 | | OpKeywordUse ClassIdent 93 | 94 | NoStatement ::= OpKeywordNo ClassIdent VersionExpr Expression 95 | | OpKeywordNo ClassIdent VersionExpr 96 | | OpKeywordNo ClassIdent Expression 97 | | OpKeywordNo VersionExpr 98 | | OpKeywordNo ClassIdent 99 | 100 | RequireStatement ::= OpKeywordRequire VersionExpr 101 | | OpKeywordRequire ClassIdent 102 | | OpKeywordRequire Expression 103 | 104 | PackageStatement ::= OpKeywordPackage ClassIdent VersionExpr Block 105 | | OpKeywordPackage ClassIdent Block 106 | 107 | PackageDeclaration ::= OpKeywordPackage ClassIdent VersionExpr 108 | | OpKeywordPackage ClassIdent 109 | 110 | SubStatement ::= PhaseStatement Block 111 | | OpKeywordSub PhaseStatement Block 112 | | OpKeywordSub SubNameExpr SubDefinition 113 | 114 | SubDeclaration ::= OpKeywordSub SubNameExpr 115 | 116 | SubDefinition ::= SubAttrsDefinitionSeq SubSigsDefinition Block 117 | | SubAttrsDefinitionSeq Block 118 | | SubSigsDefinition Block 119 | | Block 120 | 121 | SubAttrsDefinitionSeq ::= SubAttrsDefinition SubAttrsDefinitionSeq 122 | | SubAttrsDefinition 123 | 124 | SubAttrsDefinition ::= Colon IdentComp SubAttrArgs 125 | | Colon IdentComp 126 | 127 | SubSigsDefinition ::= ParenExpr 128 | 129 | PhaseStatement ::= PhaseName 130 | 131 | Condition ::= ConditionIfExpr ConditionElsifExpr ConditionElseExpr 132 | | ConditionIfExpr ConditionElseExpr 133 | | ConditionIfExpr ConditionElsifExpr 134 | | ConditionIfExpr 135 | | ConditionUnlessExpr 136 | 137 | ConditionUnlessExpr ::= ConditionUnless LParen Expression RParen Block 138 | ConditionIfExpr ::= ConditionIf LParen Expression RParen Block 139 | ConditionElsifExpr ::= ConditionElsif LParen Expression RParen Block ConditionElsifExpr 140 | | ConditionElsif LParen Expression RParen Block 141 | ConditionElseExpr ::= ConditionElse Block 142 | ConditionIfPostfixExpr ::= ConditionIf Expression 143 | ConditionUnlessPostfixExpr ::= ConditionUnless Expression 144 | ConditionWhilePostfixExpr ::= ConditionWhile Expression 145 | ConditionUntilPostfixExpr ::= ConditionUntil Expression 146 | ConditionForPostfixExpr ::= ConditionFor Expression 147 | ConditionForeachPostfixExpr ::= ConditionForeach Expression 148 | 149 | Label ::= IdentComp Colon 150 | 151 | # this is based on the order of ops in `perldoc perlop` 152 | # U can be LHS of shift and up (value) << >> 153 | # 0 can be LHS of assignment and up (value or unary) = += -= *= 154 | # L can be LHS of comma and up (value, assign, unary) , => 155 | # R can be LHS of anything (value, list, assign, unary) (anything) 156 | 157 | # There are four types of keywords: 158 | # * Nullary (This is "OpNullaryKeywordExpr" -> Value") 159 | # * Unary (This is "OpUnaryKeywordExpr") 160 | # * Assign (This is "OpAssignKeywordExpr") 161 | # * List (This is "OpListKeywordExpr") 162 | 163 | # Hence, there are four types of expressions: 164 | # ExprValueU: Those that are just values 165 | # ExprValue0: Those that are values, and unary keywords 166 | # ExprValueL: Those that are values, assignment keywords (+ goto,last,next,redo,dump), or unary keywords 167 | # ExprValueR: Those that are values, list, assigment, or unary 168 | 169 | ExprValueU ::= Value 170 | ExprValue0 ::= Value | OpUnaryKeywordExpr 171 | ExprValueL ::= Value | OpAssignKeywordExpr | OpUnaryKeywordExpr 172 | ExprValueR ::= Value | OpListKeywordExpr | OpAssignKeywordExpr | OpUnaryKeywordExpr 173 | ExprArrowU ::= ExprArrowU OpArrow ArrowRHS | ExprValueU action => ::first 174 | ExprArrow0 ::= ExprArrowU OpArrow ArrowRHS | ExprValue0 action => ::first 175 | ExprArrowL ::= ExprArrowU OpArrow ArrowRHS | ExprValueL action => ::first 176 | ExprArrowR ::= ExprArrowU OpArrow ArrowRHS | ExprValueR action => ::first 177 | ExprIncU ::= OpInc ExprArrowU | ExprArrowR OpInc | ExprArrowU action => ::first 178 | ExprInc0 ::= OpInc ExprArrow0 | ExprArrowR OpInc | ExprArrow0 action => ::first 179 | ExprIncL ::= OpInc ExprArrowL | ExprArrowR OpInc | ExprArrowL action => ::first 180 | ExprIncR ::= OpInc ExprArrowR | ExprArrowL OpInc | ExprArrowR action => ::first 181 | ExprPowerU ::= ExprIncU OpPower ExprUnaryU | ExprIncU action => ::first 182 | ExprPower0 ::= ExprIncU OpPower ExprUnary0 | ExprInc0 action => ::first 183 | ExprPowerL ::= ExprIncU OpPower ExprUnaryL | ExprIncL action => ::first 184 | ExprPowerR ::= ExprIncU OpPower ExprUnaryR | ExprIncR action => ::first 185 | ExprUnaryU ::= OpUnary ExprUnaryU | ExprPowerU action => ::first 186 | ExprUnary0 ::= OpUnary ExprUnary0 | ExprPower0 action => ::first 187 | ExprUnaryL ::= OpUnary ExprUnaryL | ExprPowerL action => ::first 188 | ExprUnaryR ::= OpUnary ExprUnaryR | ExprPowerR action => ::first 189 | ExprRegexU ::= ExprRegexU OpRegex ExprUnaryU | ExprUnaryU action => ::first 190 | ExprRegex0 ::= ExprRegexU OpRegex ExprUnary0 | ExprUnary0 action => ::first 191 | ExprRegexL ::= ExprRegexU OpRegex ExprUnaryL | ExprUnaryL action => ::first 192 | ExprRegexR ::= ExprRegexU OpRegex ExprUnaryR | ExprUnaryR action => ::first 193 | ExprMulU ::= ExprMulU OpMulti ExprRegexU | ExprRegexU action => ::first 194 | ExprMul0 ::= ExprMulU OpMulti ExprRegex0 | ExprRegex0 action => ::first 195 | ExprMulL ::= ExprMulU OpMulti ExprRegexL | ExprRegexL action => ::first 196 | ExprMulR ::= ExprMulU OpMulti ExprRegexR | ExprRegexR action => ::first 197 | ExprAddU ::= ExprAddU OpAdd ExprMulU | ExprMulU action => ::first 198 | ExprAdd0 ::= ExprAddU OpAdd ExprMul0 | ExprMul0 action => ::first 199 | ExprAddL ::= ExprAddU OpAdd ExprMulL | ExprMulL action => ::first 200 | ExprAddR ::= ExprAddU OpAdd ExprMulR | ExprMulR action => ::first 201 | ExprShiftU ::= ExprShiftU OpShift ExprAddU | ExprAddU action => ::first 202 | ExprShift0 ::= ExprShiftU OpShift ExprAdd0 | ExprAdd0 action => ::first 203 | ExprShiftL ::= ExprShiftU OpShift ExprAddL | ExprAddL action => ::first 204 | ExprShiftR ::= ExprShiftU OpShift ExprAddR | ExprAddR action => ::first 205 | ExprNeq0 ::= ExprShift0 OpInequal ExprShift0 | ExprShift0 action => ::first 206 | ExprNeqL ::= ExprShift0 OpInequal ExprShiftL | ExprShiftL action => ::first 207 | ExprNeqR ::= ExprShift0 OpInequal ExprShiftR | ExprShiftR action => ::first 208 | ExprEq0 ::= ExprNeq0 OpEqual ExprNeq0 | ExprNeq0 action => ::first 209 | ExprEqL ::= ExprNeq0 OpEqual ExprNeqL | ExprNeqL action => ::first 210 | ExprEqR ::= ExprNeq0 OpEqual ExprNeqR | ExprNeqR action => ::first 211 | ExprBinAnd0 ::= ExprBinAnd0 OpBinAnd ExprEq0 | ExprEq0 action => ::first 212 | ExprBinAndL ::= ExprBinAnd0 OpBinAnd ExprEqL | ExprEqL action => ::first 213 | ExprBinAndR ::= ExprBinAnd0 OpBinAnd ExprEqR | ExprEqR action => ::first 214 | ExprBinOr0 ::= ExprBinOr0 OpBinOr ExprBinAnd0 | ExprBinAnd0 action => ::first 215 | ExprBinOrL ::= ExprBinOr0 OpBinOr ExprBinAndL | ExprBinAndL action => ::first 216 | ExprBinOrR ::= ExprBinOr0 OpBinOr ExprBinAndR | ExprBinAndR action => ::first 217 | ExprLogAnd0 ::= ExprLogAnd0 OpLogAnd ExprBinOr0 | ExprBinOr0 action => ::first 218 | ExprLogAndL ::= ExprLogAnd0 OpLogAnd ExprBinOrL | ExprBinOrL action => ::first 219 | ExprLogAndR ::= ExprLogAnd0 OpLogAnd ExprBinOrR | ExprBinOrR action => ::first 220 | ExprLogOr0 ::= ExprLogOr0 OpLogOr ExprLogAnd0 | ExprLogAnd0 action => ::first 221 | ExprLogOrL ::= ExprLogOr0 OpLogOr ExprLogAndL | ExprLogAndL action => ::first 222 | ExprLogOrR ::= ExprLogOr0 OpLogOr ExprLogAndR | ExprLogAndR action => ::first 223 | ExprRange0 ::= ExprLogOr0 OpRange ExprLogOr0 | ExprLogOr0 action => ::first 224 | ExprRangeL ::= ExprLogOr0 OpRange ExprLogOrL | ExprLogOrL action => ::first 225 | ExprRangeR ::= ExprLogOr0 OpRange ExprLogOrR | ExprLogOrR action => ::first 226 | ExprCond0 ::= ExprRange0 OpTriThen ExprRange0 OpTriElse ExprCond0 | ExprRange0 action => ::first 227 | ExprCondL ::= ExprRange0 OpTriThen ExprRangeL OpTriElse ExprCondL | ExprRangeL action => ::first 228 | ExprCondR ::= ExprRange0 OpTriThen ExprRangeR OpTriElse ExprCondR | ExprRangeR action => ::first 229 | ExprAssignL ::= ExprCond0 OpAssign ExprAssignL | OpAssignKeywordExpr 230 | | ExprCondL action => ::first 231 | ExprAssignR ::= ExprCond0 OpAssign ExprAssignR | ExprCondR action => ::first 232 | ExprComma ::= ExprAssignL OpComma ExprComma | ExprAssignL OpComma | ExprAssignR action => ::first 233 | ExprNameNot ::= OpNameNot ExprNameNot | ExprComma action => ::first 234 | ExprNameAnd ::= ExprNameAnd OpNameAnd ExprNameNot | ExprNameNot action => ::first 235 | ExprNameOr ::= ExprNameOr OpNameOr ExprNameAnd | ExprNameAnd action => ::first 236 | Expression ::= ExprNameOr action => ::first 237 | 238 | # Hashrefs (LiteralHash) are not allowed to be top-level so they aren't confused with block 239 | # However, "+{...}" or "return {...}" means they aren't top level because of the preceding op 240 | # which are themselves top-level tokens ("+", "return") 241 | NonBraceExprValueU ::= NonBraceValue 242 | NonBraceExprValue0 ::= NonBraceValue | OpUnaryKeywordExpr 243 | NonBraceExprValueL ::= NonBraceValue | OpAssignKeywordExpr | OpUnaryKeywordExpr 244 | NonBraceExprValueR ::= NonBraceValue | OpListKeywordExpr | OpAssignKeywordExpr | OpUnaryKeywordExpr 245 | NonBraceExprArrowU ::= NonBraceExprArrowU OpArrow ArrowRHS | NonBraceExprValueU action => ::first 246 | NonBraceExprArrow0 ::= NonBraceExprArrowU OpArrow ArrowRHS | NonBraceExprValue0 action => ::first 247 | NonBraceExprArrowL ::= NonBraceExprArrowU OpArrow ArrowRHS | NonBraceExprValueL action => ::first 248 | NonBraceExprArrowR ::= NonBraceExprArrowU OpArrow ArrowRHS | NonBraceExprValueR action => ::first 249 | NonBraceExprIncU ::= OpInc ExprArrowU | NonBraceExprArrowR OpInc | NonBraceExprArrowU action => ::first 250 | NonBraceExprInc0 ::= OpInc ExprArrow0 | NonBraceExprArrowR OpInc | NonBraceExprArrow0 action => ::first 251 | NonBraceExprIncL ::= OpInc ExprArrowL | NonBraceExprArrowR OpInc | NonBraceExprArrowL action => ::first 252 | NonBraceExprIncR ::= OpInc ExprArrowR | NonBraceExprArrowL OpInc | NonBraceExprArrowR action => ::first 253 | NonBraceExprPowerU ::= NonBraceExprIncU OpPower ExprUnaryU | NonBraceExprIncU action => ::first 254 | NonBraceExprPower0 ::= NonBraceExprIncU OpPower ExprUnary0 | NonBraceExprInc0 action => ::first 255 | NonBraceExprPowerL ::= NonBraceExprIncU OpPower ExprUnaryL | NonBraceExprIncL action => ::first 256 | NonBraceExprPowerR ::= NonBraceExprIncU OpPower ExprUnaryR | NonBraceExprIncR action => ::first 257 | NonBraceExprUnaryU ::= OpUnary ExprUnaryU | NonBraceExprPowerU action => ::first 258 | NonBraceExprUnary0 ::= OpUnary ExprUnary0 | NonBraceExprPower0 action => ::first 259 | NonBraceExprUnaryL ::= OpUnary ExprUnaryL | NonBraceExprPowerL action => ::first 260 | NonBraceExprUnaryR ::= OpUnary ExprUnaryR | NonBraceExprPowerR action => ::first 261 | NonBraceExprRegexU ::= NonBraceExprRegexU OpRegex ExprUnaryU | NonBraceExprUnaryU action => ::first 262 | NonBraceExprRegex0 ::= NonBraceExprRegexU OpRegex ExprUnary0 | NonBraceExprUnary0 action => ::first 263 | NonBraceExprRegexL ::= NonBraceExprRegexU OpRegex ExprUnaryL | NonBraceExprUnaryL action => ::first 264 | NonBraceExprRegexR ::= NonBraceExprRegexU OpRegex ExprUnaryR | NonBraceExprUnaryR action => ::first 265 | NonBraceExprMulU ::= NonBraceExprMulU OpMulti ExprRegexU | NonBraceExprRegexU action => ::first 266 | NonBraceExprMul0 ::= NonBraceExprMulU OpMulti ExprRegex0 | NonBraceExprRegex0 action => ::first 267 | NonBraceExprMulL ::= NonBraceExprMulU OpMulti ExprRegexL | NonBraceExprRegexL action => ::first 268 | NonBraceExprMulR ::= NonBraceExprMulU OpMulti ExprRegexR | NonBraceExprRegexR action => ::first 269 | NonBraceExprAddU ::= NonBraceExprAddU OpAdd ExprMulU | NonBraceExprMulU action => ::first 270 | NonBraceExprAdd0 ::= NonBraceExprAddU OpAdd ExprMul0 | NonBraceExprMul0 action => ::first 271 | NonBraceExprAddL ::= NonBraceExprAddU OpAdd ExprMulL | NonBraceExprMulL action => ::first 272 | NonBraceExprAddR ::= NonBraceExprAddU OpAdd ExprMulR | NonBraceExprMulR action => ::first 273 | NonBraceExprShiftU ::= NonBraceExprShiftU OpShift ExprAddU | NonBraceExprAddU action => ::first 274 | NonBraceExprShift0 ::= NonBraceExprShiftU OpShift ExprAdd0 | NonBraceExprAdd0 action => ::first 275 | NonBraceExprShiftL ::= NonBraceExprShiftU OpShift ExprAddL | NonBraceExprAddL action => ::first 276 | NonBraceExprShiftR ::= NonBraceExprShiftU OpShift ExprAddR | NonBraceExprAddR action => ::first 277 | NonBraceExprNeq0 ::= NonBraceExprShift0 OpInequal ExprShift0 | NonBraceExprShift0 action => ::first 278 | NonBraceExprNeqL ::= NonBraceExprShift0 OpInequal ExprShiftL | NonBraceExprShiftL action => ::first 279 | NonBraceExprNeqR ::= NonBraceExprShift0 OpInequal ExprShiftR | NonBraceExprShiftR action => ::first 280 | NonBraceExprEq0 ::= NonBraceExprNeq0 OpEqual ExprNeq0 | NonBraceExprNeq0 action => ::first 281 | NonBraceExprEqL ::= NonBraceExprNeq0 OpEqual ExprNeqL | NonBraceExprNeqL action => ::first 282 | NonBraceExprEqR ::= NonBraceExprNeq0 OpEqual ExprNeqR | NonBraceExprNeqR action => ::first 283 | NonBraceExprBinAnd0 ::= NonBraceExprBinAnd0 OpBinAnd ExprEq0 | NonBraceExprEq0 action => ::first 284 | NonBraceExprBinAndL ::= NonBraceExprBinAnd0 OpBinAnd ExprEqL | NonBraceExprEqL action => ::first 285 | NonBraceExprBinAndR ::= NonBraceExprBinAnd0 OpBinAnd ExprEqR | NonBraceExprEqR action => ::first 286 | NonBraceExprBinOr0 ::= NonBraceExprBinOr0 OpBinOr ExprBinAnd0 | NonBraceExprBinAnd0 action => ::first 287 | NonBraceExprBinOrL ::= NonBraceExprBinOr0 OpBinOr ExprBinAndL | NonBraceExprBinAndL action => ::first 288 | NonBraceExprBinOrR ::= NonBraceExprBinOr0 OpBinOr ExprBinAndR | NonBraceExprBinAndR action => ::first 289 | NonBraceExprLogAnd0 ::= NonBraceExprLogAnd0 OpLogAnd ExprBinOr0 | NonBraceExprBinOr0 action => ::first 290 | NonBraceExprLogAndL ::= NonBraceExprLogAnd0 OpLogAnd ExprBinOrL | NonBraceExprBinOrL action => ::first 291 | NonBraceExprLogAndR ::= NonBraceExprLogAnd0 OpLogAnd ExprBinOrR | NonBraceExprBinOrR action => ::first 292 | NonBraceExprLogOr0 ::= NonBraceExprLogOr0 OpLogOr ExprLogAnd0 | NonBraceExprLogAnd0 action => ::first 293 | NonBraceExprLogOrL ::= NonBraceExprLogOr0 OpLogOr ExprLogAndL | NonBraceExprLogAndL action => ::first 294 | NonBraceExprLogOrR ::= NonBraceExprLogOr0 OpLogOr ExprLogAndR | NonBraceExprLogAndR action => ::first 295 | NonBraceExprRange0 ::= NonBraceExprLogOr0 OpRange ExprLogOr0 | NonBraceExprLogOr0 action => ::first 296 | NonBraceExprRangeL ::= NonBraceExprLogOr0 OpRange ExprLogOrL | NonBraceExprLogOrL action => ::first 297 | NonBraceExprRangeR ::= NonBraceExprLogOr0 OpRange ExprLogOrR | NonBraceExprLogOrR action => ::first 298 | NonBraceExprCond0 ::= NonBraceExprRange0 OpTriThen ExprRange0 OpTriElse ExprCond0 | NonBraceExprRange0 action => ::first 299 | NonBraceExprCondL ::= NonBraceExprRange0 OpTriThen ExprRangeL OpTriElse ExprCondL | NonBraceExprRangeL action => ::first 300 | NonBraceExprCondR ::= NonBraceExprRange0 OpTriThen ExprRangeR OpTriElse ExprCondR | NonBraceExprRangeR action => ::first 301 | NonBraceExprAssignL ::= NonBraceExprCond0 OpAssign ExprAssignL | OpAssignKeywordExpr 302 | | NonBraceExprCondL action => ::first 303 | NonBraceExprAssignR ::= NonBraceExprCond0 OpAssign ExprAssignR | NonBraceExprCondR action => ::first 304 | 305 | 306 | NonBraceExprComma ::= NonBraceExprAssignL OpComma ExprComma | NonBraceExprAssignR action => ::first 307 | 308 | # Comma is only allowed if it follows a keyword operator, to avoid block/hash disambiguation in perl. 309 | BlockLevelExprNameNot ::= OpNameNot ExprNameNot | NonBraceExprAssignR action => ::first 310 | BlockLevelExprNameAnd ::= BlockLevelExprNameAnd OpNameAnd ExprNameNot | BlockLevelExprNameNot action => ::first 311 | BlockLevelExprNameOr ::= BlockLevelExprNameOr OpNameOr ExprNameAnd | BlockLevelExprNameAnd action => ::first 312 | BlockLevelExpression ::= BlockLevelExprNameOr action => ::first 313 | 314 | Value ::= Literal | NonLiteral | QLikeValue 315 | 316 | # Arguments of operators according to the operator precedence 317 | OpUnaryKeywordArg ::= ExprShiftR 318 | OpUnaryKeywordArgNonBrace ::= NonBraceExprShiftR 319 | OpAssignKeywordArg ::= ExprAssignR 320 | OpListKeywordArg ::= ExprComma 321 | OpListKeywordArgNonBrace ::= NonBraceExprComma 322 | 323 | # Same as Value above, but with a NonBraceLiteral 324 | NonBraceValue ::= NonBraceLiteral | NonLiteral | QLikeValue 325 | 326 | NonLiteral ::= Variable 327 | | DerefVariable 328 | | Modifier Variable 329 | | Modifier ParenExpr 330 | | UnderscoreValues 331 | | SubCall 332 | | PackageArrow 333 | | ParenExpr ElemSeq0 334 | | OpNullaryKeywordExpr 335 | | DiamondExpr 336 | 337 | DiamondExpr ::= Diamond 338 | | DoubleDiamond 339 | 340 | # This is written this way because of whitespace rules 341 | Diamond ::= '<' VarScalar '>' 342 | | '<' BuiltinFilehandle '>' 343 | | '<>' 344 | 345 | # This is written this way because of whitespace rules 346 | DoubleDiamond ::= '<<' VarScalar '>>' 347 | | '<<' BuiltinFilehandle '>>' 348 | | '<<>>' 349 | 350 | ParenExpr ::= LParen Expression RParen 351 | | LParen RParen # support () 352 | 353 | Modifier ::= OpKeywordMy | OpKeywordOur | OpKeywordLocal | OpKeywordState 354 | 355 | ElemSeq0 ::= Element* 356 | ElemSeq1 ::= Element+ 357 | Element ::= ArrayElem | HashElem 358 | 359 | # UnderscoreData and UnderscoreEnd are not values 360 | UnderscoreValues ::= UnderscorePackage 361 | | UnderscoreFile 362 | | UnderscoreLine 363 | | UnderscoreSub 364 | 365 | # Silence these until they are supported 366 | #UnderscoreTokens ::= UnderscoreValues 367 | # | UnderscoreData 368 | # | UnderscoreEnd 369 | 370 | Variable ::= GlobalVarExpr 371 | | VarScalar 372 | | VarArray 373 | | VarHash 374 | | VarCode 375 | | VarGlob 376 | | VarArrayTop 377 | 378 | GlobalVarExpr ::= '$#' 379 | | SigilScalar GlobalVariables ElemSeq0 380 | | SigilArray GlobalVariables ElemSeq0 381 | | SigilHash GlobalVariables ElemSeq0 382 | | SigilGlob GlobalVariables ElemSeq0 383 | 384 | VarScalar ::= SigilScalar VarIdentExpr ElemSeq0 385 | VarArray ::= SigilArray VarIdentExpr ElemSeq0 386 | VarHash ::= SigilHash VarIdentExpr ElemSeq0 387 | VarCode ::= SigilCode VarIdentExpr 388 | VarGlob ::= SigilGlob VarIdentExpr 389 | VarArrayTop ::= SigilArrayTop VarIdentExpr 390 | | '$#_' 391 | 392 | SubCall ::= SubNameCallExpr CallArgs 393 | | VarCode CallArgs 394 | 395 | PackageArrow ::= SubNameExpr OpArrow PackageArrowRHS 396 | 397 | PackageArrowRHS ::= ArrowMethodCall 398 | | ArrowIndirectCall 399 | 400 | # Used for function calls (Non-QLikeValue string) 401 | SubNameCallExpr ::= SubNameNonQLike 402 | | SubNameCallExpr PackageSep SubNameNonQLike 403 | 404 | # Used for defining subs (no limits) 405 | SubNameExpr ::= SubName 406 | | SubNameExpr PackageSep SubName 407 | 408 | # SubName is used for methods and subroutine definitions 409 | # They are not limited in any regard (other than no-digits in first char) 410 | SubName ~ LeadingSubLetter CoreSubLetters 411 | LeadingSubLetter ~ [a-zA-Z_] 412 | CoreSubLetters ~ [a-zA-Z0-9_]* 413 | 414 | # SubNameNonQLike is for function calls 415 | # They are not allowed to be: 416 | # q / qq / qw / qr / qx 417 | # s / m / y / tr 418 | SubNameNonQLike ~ 419 | NonQLikeLetters # [non-qlike] 420 | | NonQLikeLetters AllSubLetters # [non-qlike][*] 421 | | 'q' NonQRWXLetters # q[non-qrwx] 422 | | 'q' NonQRWXLetters AllSubLetters # q[non-qrwx][*] 423 | | 'qq' AllSubLetters # qq[*] 424 | | 'qr' AllSubLetters # qr[*] 425 | | 'qw' AllSubLetters # qw[*] 426 | | 'qx' AllSubLetters # qx[*] 427 | | 't' # t 428 | | 't' NonRLetter # t[non-r] 429 | | 't' NonRLetter AllSubLetters # t[non-r][*] 430 | | 'tr' AllSubLetters # tr[*] 431 | | 's' AllSubLetters # s[*] 432 | | 'm' AllSubLetters # m[*] 433 | | 'y' AllSubLetters # y[*] 434 | 435 | # Variables are defined using a different ident 436 | # Namespaced variables ($x::y) might have a different ident 437 | VarIdentExpr ::= VarIdent 438 | | VarIdentExpr PackageSep VarIdent 439 | 440 | VarIdent ~ NonGlobalVarLetters 441 | | NonGlobalVarLetters AllVarLetters 442 | | '_' AllVarLetters 443 | 444 | NonGlobalVarLetters ~ [a-zA-Z]+ 445 | AllVarLetters ~ [a-zA-Z0-9_]+ 446 | 447 | GlobalVariables ~ '!' 448 | | '"' 449 | | '%' 450 | | '&' 451 | | ['] 452 | | '(' 453 | | ')' 454 | | '*' 455 | | '+' 456 | | ',' 457 | | '-' 458 | | '.' 459 | | '/' 460 | | ':' 461 | | ';' 462 | | '<' 463 | | '=' 464 | | '>' 465 | | '?' 466 | | '@' 467 | | '[' 468 | | '\\' 469 | | ']' 470 | | '^' 471 | | '_' 472 | | '`' 473 | | '|' 474 | | '~' 475 | | '$' 476 | | '^A' 477 | | '^C' 478 | | '{^CHILD_ERROR_NATIVE}' 479 | | '^D' 480 | | '^E' 481 | | '{^ENCODING}' 482 | | '^F' 483 | | '{^GLOBAL_PHASE}' 484 | | '^H' 485 | | '^I' 486 | | '^L' 487 | | '{^LAST_FH}' 488 | | '^M' 489 | | '{^MATCH}' 490 | | '^N' 491 | | '^O' 492 | | '{^OPEN}' 493 | | '^P' 494 | | '{^POSTMATCH}' 495 | | '{^PREMATCH}' 496 | | '^R' 497 | | '{^RE_COMPILE_RECURSION_LIMIT}' 498 | | '{^RE_DEBUG_FLAGS}' 499 | | '{^RE_TRIE_MAXBUF}' 500 | | '^S' 501 | | '{^SAFE_LOCALES}' 502 | | '^T' 503 | | '{^TAINT}' 504 | | '{^UNICODE}' 505 | | '{^UTF8CACHE}' 506 | | '{^UTF8LOCALE}' 507 | | '^V' 508 | | '^W' 509 | | '{^WARNING_BITS}' 510 | | '{^WIN32_SLOPPY_STAT}' 511 | | '^X' 512 | | '{^CAPTURE}' 513 | | '{^CAPTURE_ALL}' 514 | | Digits 515 | 516 | VarDefaultArg ::= '$_' 517 | 518 | # This uses the same definition as subroutine names 519 | # In the future, we might want to split those 520 | # but they are basically the same 521 | ClassIdent ::= SubNameExpr 522 | 523 | CallArgs ::= ParenExpr 524 | 525 | # Depending on context, "{}" may be interpreted as an empty block or a hash literal. 526 | Block ::= BlockEmpty action => ::first 527 | | BlockNonEmpty action => ::first 528 | BlockEmpty ::= LBrace RBrace name => Block 529 | BlockNonEmpty ::= LBrace StatementSeq RBrace name => Block 530 | 531 | ArrayElem ::= LBracket Expression RBracket 532 | 533 | HashElem ::= LBrace Expression RBrace 534 | 535 | NonBraceLiteral ::= LitNumber 536 | | LitArray 537 | | LitString 538 | | InterpolString 539 | | LitHashEmpty 540 | 541 | Literal ::= NonBraceLiteral 542 | | LitHashNonEmpty 543 | 544 | LitArray ::= LBracket Expression RBracket 545 | | LBracket RBracket 546 | 547 | # Depending on context, "{}" may be interpreted as an empty block or a hash literal. 548 | LitHashEmpty ::= LBrace RBrace name => LitHash 549 | LitHashNonEmpty ::= LBrace Expression RBrace name => LitHash 550 | 551 | LitString ::= SingleQuote NonSingleOrEscapedQuote_Many SingleQuote 552 | | SingleQuote SingleQuote 553 | 554 | InterpolString ::= DoubleQuote NonDoubleOrEscapedQuote_Many DoubleQuote 555 | | DoubleQuote DoubleQuote 556 | 557 | ArrowRHS ::= ArrowDerefCall 558 | | ArrowDerefVariable 559 | | ArrowMethodCall 560 | | ArrowIndirectCall 561 | | ElemSeq1 562 | | VarScalar 563 | | VarDefaultArg 564 | 565 | ArrowDerefCall ::= CallArgs 566 | ArrowDerefVariable ::= DerefVariableArgsAll 567 | | DerefVariableSlice 568 | ArrowMethodCall ::= SubNameExpr CallArgs 569 | | SubNameExpr 570 | ArrowIndirectCall ::= SigilScalar VarIdentExpr CallArgs 571 | 572 | DerefVariableArgsAll ::= '$*' | '@*' | '%*' | '&*' | '**' | '$#*' 573 | 574 | DerefVariableSlice ::= '@[' Expression ']' 575 | | '@{' Expression '}' 576 | | '%[' Expression ']' 577 | | '%{' Expression '}' 578 | 579 | DerefVariable ::= SigilScalar BlockNonEmpty 580 | | SigilArray BlockNonEmpty ElemSeq0 581 | | SigilHash BlockNonEmpty ElemSeq0 582 | | SigilGlob BlockNonEmpty 583 | | SigilArrayTop BlockNonEmpty 584 | 585 | OpNullaryKeywordExpr ::= 586 | OpKeywordBreakExpr 587 | | OpKeywordForkExpr 588 | | OpKeywordGetloginExpr 589 | | OpKeywordGetppidExpr 590 | | OpKeywordGetpwentExpr 591 | | OpKeywordGetgrentExpr 592 | | OpKeywordGethostentExpr 593 | | OpKeywordGetnetentExpr 594 | | OpKeywordGetprotoentExpr 595 | | OpKeywordGetserventExpr 596 | | OpKeywordSetpwentExpr 597 | | OpKeywordSetgrentExpr 598 | | OpKeywordEndpwentExpr 599 | | OpKeywordEndgrentExpr 600 | | OpKeywordEndhostentExpr 601 | | OpKeywordEndnetentExpr 602 | | OpKeywordEndprotoentExpr 603 | | OpKeywordEndserventExpr 604 | | OpKeywordEvalExpr 605 | | OpKeywordSubExpr 606 | | OpKeywordTimeExpr 607 | | OpKeywordTimesExpr 608 | | OpKeywordWaitExpr 609 | | OpKeywordWantarrayExpr 610 | 611 | # Unary keyword operators: 612 | # abs $a, $b => ((abs $a), $b) 613 | # abs $a = $b => ((abs $a) = $b) 614 | # List keyword operators: 615 | # push $a, $b => (push ($a, $b)) 616 | # push $a = $b => (push ($a = $b)) 617 | # Assign keyword operators: 618 | # goto $a, $b => ((goto $a), $b) 619 | # goto $a = $b => (goto ($a = $b)) 620 | 621 | OpUnaryKeywordExpr ::= 622 | OpKeywordAbsExpr 623 | | OpKeywordAlarmExpr 624 | | OpKeywordCallerExpr 625 | | OpKeywordChdirExpr 626 | | OpKeywordChompExpr 627 | | OpKeywordChopExpr 628 | | OpKeywordChrExpr 629 | | OpKeywordChrootExpr 630 | | OpKeywordCloseExpr 631 | | OpKeywordClosedirExpr 632 | | OpKeywordCosExpr 633 | | OpKeywordDbmcloseExpr 634 | | OpKeywordDefinedExpr 635 | | OpKeywordDeleteExpr 636 | | OpKeywordDoExpr 637 | | OpKeywordEachExpr 638 | | OpKeywordEofExpr 639 | | OpKeywordEvalbytesExpr 640 | | OpKeywordExistsExpr 641 | | OpKeywordExitExpr 642 | | OpKeywordExpExpr 643 | | OpKeywordFcExpr 644 | | OpKeywordFilenoExpr 645 | | OpKeywordGetcExpr 646 | | OpKeywordGetpeernameExpr 647 | | OpKeywordGetpgrpExpr 648 | | OpKeywordGetpwnamExpr 649 | | OpKeywordGetgrnamExpr 650 | | OpKeywordGethostbynameExpr 651 | | OpKeywordGetnetbynameExpr 652 | | OpKeywordGetprotobynameExpr 653 | | OpKeywordGetpwuidExpr 654 | | OpKeywordGetgrgidExpr 655 | | OpKeywordGetprotobynumberExpr 656 | | OpKeywordSethostentExpr 657 | | OpKeywordSetnetentExpr 658 | | OpKeywordSetprotoentExpr 659 | | OpKeywordSetserventExpr 660 | | OpKeywordGetsocknameExpr 661 | | OpKeywordGmtimeExpr 662 | | OpKeywordHexExpr 663 | | OpKeywordIntExpr 664 | | OpKeywordKeysExpr 665 | | OpKeywordLcExpr 666 | | OpKeywordLcfirstExpr 667 | | OpKeywordLengthExpr 668 | | OpKeywordLocaltimeExpr 669 | | OpKeywordLockExpr 670 | | OpKeywordLogExpr 671 | | OpKeywordLstatExpr 672 | | OpKeywordOctExpr 673 | | OpKeywordOrdExpr 674 | | OpKeywordPopExpr 675 | | OpKeywordPosExpr 676 | | OpKeywordPrototypeExpr 677 | | OpKeywordQuotemetaExpr 678 | | OpKeywordRandExpr 679 | | OpKeywordReaddirExpr 680 | | OpKeywordReadlineExpr 681 | | OpKeywordReadlinkExpr 682 | | OpKeywordReadpipeExpr 683 | | OpKeywordRefExpr 684 | | OpKeywordResetExpr 685 | | OpKeywordRewinddirExpr 686 | | OpKeywordRmdirExpr 687 | | OpKeywordScalarExpr 688 | | OpKeywordShiftExpr 689 | | OpKeywordSinExpr 690 | | OpKeywordSleepExpr 691 | | OpKeywordSqrtExpr 692 | | OpKeywordSrandExpr 693 | | OpKeywordStatExpr 694 | | OpKeywordStudyExpr 695 | | OpKeywordTellExpr 696 | | OpKeywordTelldirExpr 697 | | OpKeywordTiedExpr 698 | | OpKeywordUcExpr 699 | | OpKeywordUcfirstExpr 700 | | OpKeywordUmaskExpr 701 | | OpKeywordUndefExpr 702 | | OpKeywordUnlinkExpr 703 | | OpKeywordUntieExpr 704 | | OpKeywordUtimeExpr 705 | | OpKeywordValuesExpr 706 | | OpFileExpr 707 | 708 | OpListKeywordExpr ::= 709 | OpKeywordAcceptExpr 710 | | OpKeywordAtan2Expr 711 | | OpKeywordBindExpr 712 | | OpKeywordBinmodeExpr 713 | | OpKeywordBlessExpr 714 | | OpKeywordChmodExpr 715 | | OpKeywordChownExpr 716 | | OpKeywordConnectExpr 717 | | OpKeywordCryptExpr 718 | | OpKeywordDbmopenExpr 719 | | OpKeywordDieExpr 720 | | OpKeywordFcntlExpr 721 | | OpKeywordFlockExpr 722 | | OpKeywordGetpriorityExpr 723 | | OpKeywordGetservbynameExpr 724 | | OpKeywordGethostbyaddrExpr 725 | | OpKeywordGetnetbyaddrExpr 726 | | OpKeywordGetservbyportExpr 727 | | OpKeywordExecExpr 728 | | OpKeywordGetsockoptExpr 729 | | OpKeywordGlobExpr 730 | | OpKeywordGrepExpr 731 | | OpKeywordIndexExpr 732 | | OpKeywordIoctlExpr 733 | | OpKeywordJoinExpr 734 | | OpKeywordKillExpr 735 | | OpKeywordLinkExpr 736 | | OpKeywordListenExpr 737 | | OpKeywordMapExpr 738 | | OpKeywordMkdirExpr 739 | | OpKeywordMsgctlExpr 740 | | OpKeywordMsggetExpr 741 | | OpKeywordMsgrcvExpr 742 | | OpKeywordMsgsndExpr 743 | | OpKeywordOpenExpr 744 | | OpKeywordOpendirExpr 745 | | OpKeywordPackExpr 746 | | OpKeywordPipeExpr 747 | | OpKeywordPrintExpr 748 | | OpKeywordPrintfExpr 749 | | OpKeywordPushExpr 750 | | OpKeywordReadExpr 751 | | OpKeywordRecvExpr 752 | | OpKeywordRenameExpr 753 | | OpKeywordReturnExpr 754 | | OpKeywordReverseExpr 755 | | OpKeywordRindexExpr 756 | | OpKeywordSayExpr 757 | | OpKeywordSeekExpr 758 | | OpKeywordSeekdirExpr 759 | | OpKeywordSelectExpr 760 | | OpKeywordSemctlExpr 761 | | OpKeywordSemgetExpr 762 | | OpKeywordSemopExpr 763 | | OpKeywordSendExpr 764 | | OpKeywordSetpgrpExpr 765 | | OpKeywordSetpriorityExpr 766 | | OpKeywordSetsockoptExpr 767 | | OpKeywordShmctlExpr 768 | | OpKeywordShmgetExpr 769 | | OpKeywordShmreadExpr 770 | | OpKeywordShmwriteExpr 771 | | OpKeywordShutdownExpr 772 | | OpKeywordSocketExpr 773 | | OpKeywordSocketpairExpr 774 | | OpKeywordSortExpr 775 | | OpKeywordSpliceExpr 776 | | OpKeywordSplitExpr 777 | | OpKeywordSprintfExpr 778 | | OpKeywordSubstrExpr 779 | | OpKeywordSymlinkExpr 780 | | OpKeywordSyscallExpr 781 | | OpKeywordSysopenExpr 782 | | OpKeywordSysreadExpr 783 | | OpKeywordSysseekExpr 784 | | OpKeywordSyswriteExpr 785 | | OpKeywordSystemExpr 786 | | OpKeywordTieExpr 787 | | OpKeywordTruncateExpr 788 | | OpKeywordUnpackExpr 789 | | OpKeywordUnshiftExpr 790 | | OpKeywordVecExpr 791 | | OpKeywordWaitpidExpr 792 | | OpKeywordWarnExpr 793 | | OpKeywordWriteExpr 794 | 795 | OpAssignKeywordExpr ::= 796 | OpKeywordDumpExpr 797 | | OpKeywordGotoExpr 798 | | OpKeywordLastExpr 799 | | OpKeywordNextExpr 800 | | OpKeywordRedoExpr 801 | 802 | OpKeywordAbsExpr ::= OpKeywordAbs OpUnaryKeywordArg 803 | | OpKeywordAbs 804 | 805 | OpKeywordAcceptExpr ::= OpKeywordAccept OpListKeywordArg 806 | 807 | OpKeywordAlarmExpr ::= OpKeywordAlarm OpUnaryKeywordArg 808 | | OpKeywordAlarm 809 | 810 | OpKeywordAtan2Expr ::= OpKeywordAtan2 OpListKeywordArg 811 | 812 | OpKeywordBindExpr ::= OpKeywordBind OpListKeywordArg 813 | 814 | OpKeywordBinmodeExpr ::= OpKeywordBinmode OpListKeywordArg 815 | | OpKeywordBinmode LParen BuiltinFilehandle RParen 816 | | OpKeywordBinmode BuiltinFilehandle 817 | | OpKeywordBinmode LParen BuiltinFilehandle OpComma ExprAssignR RParen 818 | | OpKeywordBinmode BuiltinFilehandle OpComma ExprAssignR 819 | 820 | OpKeywordBlessExpr ::= OpKeywordBless OpListKeywordArg 821 | 822 | OpKeywordBreakExpr ::= OpKeywordBreak Label 823 | | OpKeywordBreak 824 | 825 | OpKeywordCallerExpr ::= OpKeywordCaller OpUnaryKeywordArg 826 | | OpKeywordCaller 827 | 828 | OpKeywordChdirExpr ::= OpKeywordChdir OpUnaryKeywordArg 829 | | OpKeywordChdir 830 | 831 | OpKeywordChmodExpr ::= OpKeywordChmod OpListKeywordArg 832 | 833 | OpKeywordChompExpr ::= OpKeywordChomp OpUnaryKeywordArg 834 | | OpKeywordChomp 835 | 836 | OpKeywordChopExpr ::= OpKeywordChop OpUnaryKeywordArg 837 | | OpKeywordChop 838 | 839 | OpKeywordChownExpr ::= OpKeywordChown OpListKeywordArg 840 | 841 | OpKeywordChrExpr ::= OpKeywordChr OpUnaryKeywordArg 842 | | OpKeywordChr 843 | 844 | OpKeywordChrootExpr ::= OpKeywordChroot OpUnaryKeywordArg 845 | | OpKeywordChroot 846 | 847 | OpKeywordCloseExpr ::= OpKeywordClose OpUnaryKeywordArg 848 | | OpKeywordClose 849 | 850 | OpKeywordClosedirExpr ::= OpKeywordClosedir OpUnaryKeywordArg 851 | 852 | OpKeywordConnectExpr ::= OpKeywordConnect OpListKeywordArg 853 | 854 | OpKeywordCosExpr ::= OpKeywordCos OpUnaryKeywordArg 855 | 856 | OpKeywordCryptExpr ::= OpKeywordCrypt OpListKeywordArg 857 | 858 | OpKeywordDbmcloseExpr ::= OpKeywordDbmclose OpUnaryKeywordArg 859 | 860 | OpKeywordDbmopenExpr ::= OpKeywordDbmopen OpListKeywordArg 861 | 862 | OpKeywordDefinedExpr ::= OpKeywordDefined OpUnaryKeywordArg 863 | | OpKeywordDefined 864 | 865 | OpKeywordDeleteExpr ::= OpKeywordDelete OpUnaryKeywordArg 866 | 867 | OpKeywordDieExpr ::= OpKeywordDie OpListKeywordArg 868 | | OpKeywordDie 869 | 870 | OpKeywordDoExpr ::= OpKeywordDo BlockNonEmpty 871 | | OpKeywordDo OpUnaryKeywordArgNonBrace 872 | 873 | OpKeywordDumpExpr ::= OpKeywordDump OpAssignKeywordArg 874 | | OpKeywordDump Label 875 | | OpKeywordDump 876 | 877 | OpKeywordEachExpr ::= OpKeywordEach OpUnaryKeywordArg 878 | 879 | OpKeywordEofExpr ::= OpKeywordEof OpUnaryKeywordArg 880 | | OpKeywordEof 881 | 882 | OpKeywordEvalExpr ::= OpKeywordEval BlockNonEmpty 883 | | OpKeywordEval Value 884 | 885 | OpKeywordEvalbytesExpr ::= OpKeywordEvalbytes OpUnaryKeywordArg 886 | | OpKeywordEvalbytes 887 | 888 | OpKeywordExistsExpr ::= OpKeywordExists OpUnaryKeywordArg 889 | 890 | OpKeywordExitExpr ::= OpKeywordExit OpUnaryKeywordArg 891 | | OpKeywordExit 892 | 893 | OpKeywordExpExpr ::= OpKeywordExp OpUnaryKeywordArg 894 | | OpKeywordExp 895 | 896 | OpKeywordFcExpr ::= OpKeywordFc OpUnaryKeywordArg 897 | | OpKeywordFc 898 | 899 | OpKeywordFcntlExpr ::= OpKeywordFcntl OpListKeywordArg 900 | 901 | OpKeywordFilenoExpr ::= OpKeywordFileno OpUnaryKeywordArg 902 | 903 | OpKeywordFlockExpr ::= OpKeywordFlock OpListKeywordArg 904 | 905 | OpKeywordForkExpr ::= OpKeywordFork LParen RParen 906 | | OpKeywordFork 907 | 908 | OpKeywordGetcExpr ::= OpKeywordGetc OpUnaryKeywordArg 909 | | OpKeywordGetc 910 | 911 | OpKeywordGetloginExpr ::= OpKeywordGetlogin LParen RParen 912 | | OpKeywordGetlogin 913 | 914 | OpKeywordGetpeernameExpr ::= OpKeywordGetpeername OpUnaryKeywordArg 915 | 916 | OpKeywordGetpgrpExpr ::= OpKeywordGetpgrp OpUnaryKeywordArg 917 | 918 | OpKeywordGetppidExpr ::= OpKeywordGetppid LParen RParen 919 | | OpKeywordGetppid 920 | 921 | OpKeywordGetpriorityExpr ::= OpKeywordGetpriority OpListKeywordArg 922 | 923 | OpKeywordGetpwnamExpr ::= OpKeywordGetpwnam OpUnaryKeywordArg 924 | 925 | OpKeywordGetgrnamExpr ::= OpKeywordGetgrnam OpUnaryKeywordArg 926 | 927 | OpKeywordGethostbynameExpr ::= OpKeywordGethostbyname OpUnaryKeywordArg 928 | 929 | OpKeywordGetnetbynameExpr ::= OpKeywordGetnetbyname OpUnaryKeywordArg 930 | 931 | OpKeywordGetprotobynameExpr ::= OpKeywordGetprotobyname OpUnaryKeywordArg 932 | 933 | OpKeywordGetpwuidExpr ::= OpKeywordGetpwuid OpUnaryKeywordArg 934 | 935 | OpKeywordGetgrgidExpr ::= OpKeywordGetgrgid OpUnaryKeywordArg 936 | 937 | OpKeywordGetservbynameExpr ::= OpKeywordGetservbyname OpListKeywordArg 938 | 939 | OpKeywordGethostbyaddrExpr ::= OpKeywordGethostbyaddr OpListKeywordArg 940 | 941 | OpKeywordGetnetbyaddrExpr ::= OpKeywordGetnetbyaddr OpListKeywordArg 942 | 943 | OpKeywordGetprotobynumberExpr ::= OpKeywordGetprotobynumber OpUnaryKeywordArg 944 | 945 | OpKeywordGetservbyportExpr ::= OpKeywordGetservbyport OpListKeywordArg 946 | 947 | OpKeywordGetpwentExpr ::= OpKeywordGetpwent LParen RParen 948 | | OpKeywordGetpwent 949 | 950 | OpKeywordGetgrentExpr ::= OpKeywordGetgrent LParen RParen 951 | | OpKeywordGetgrent 952 | 953 | OpKeywordGethostentExpr ::= OpKeywordGethostent LParen RParen 954 | | OpKeywordGethostent 955 | 956 | OpKeywordGetnetentExpr ::= OpKeywordGetnetent LParen RParen 957 | | OpKeywordGetnetent 958 | 959 | OpKeywordGetprotoentExpr ::= OpKeywordGetprotoent LParen RParen 960 | | OpKeywordGetprotoent 961 | 962 | OpKeywordGetserventExpr ::= OpKeywordGetservent LParen RParen 963 | | OpKeywordGetservent 964 | 965 | OpKeywordSetpwentExpr ::= OpKeywordSetpwent LParen RParen 966 | | OpKeywordSetpwent 967 | 968 | OpKeywordSetgrentExpr ::= OpKeywordSetgrent LParen RParen 969 | | OpKeywordSetgrent 970 | 971 | OpKeywordSethostentExpr ::= OpKeywordSethostent OpUnaryKeywordArg 972 | 973 | OpKeywordSetnetentExpr ::= OpKeywordSetnetent OpUnaryKeywordArg 974 | 975 | OpKeywordSetprotoentExpr ::= OpKeywordSetprotoent OpUnaryKeywordArg 976 | 977 | OpKeywordSetserventExpr ::= OpKeywordSetservent OpUnaryKeywordArg 978 | 979 | OpKeywordEndpwentExpr ::= OpKeywordEndpwent LParen RParen 980 | | OpKeywordEndpwent 981 | 982 | OpKeywordEndgrentExpr ::= OpKeywordEndgrent LParen RParen 983 | | OpKeywordEndgrent 984 | 985 | OpKeywordEndhostentExpr ::= OpKeywordEndhostent LParen RParen 986 | | OpKeywordEndhostent 987 | 988 | OpKeywordEndnetentExpr ::= OpKeywordEndnetent LParen RParen 989 | | OpKeywordEndnetent 990 | 991 | OpKeywordEndprotoentExpr ::= OpKeywordEndprotoent LParen RParen 992 | | OpKeywordEndprotoent 993 | 994 | OpKeywordEndserventExpr ::= OpKeywordEndservent LParen RParen 995 | | OpKeywordEndservent 996 | 997 | OpKeywordExecExpr ::= OpKeywordExec BlockNonEmpty OpListKeywordArg 998 | | OpKeywordExec OpListKeywordArgNonBrace 999 | 1000 | OpKeywordGetsocknameExpr ::= OpKeywordGetsockname OpUnaryKeywordArg 1001 | 1002 | OpKeywordGetsockoptExpr ::= OpKeywordGetsockopt OpListKeywordArg 1003 | 1004 | OpKeywordGlobExpr ::= OpKeywordGlob OpListKeywordArg 1005 | | OpKeywordGlob 1006 | 1007 | OpKeywordGmtimeExpr ::= OpKeywordGmtime OpUnaryKeywordArg 1008 | | OpKeywordGmtime 1009 | 1010 | # &NAME is an expression too 1011 | OpKeywordGotoExpr ::= OpKeywordGoto OpAssignKeywordArg 1012 | | OpKeywordGoto Label 1013 | 1014 | OpKeywordGrepExpr ::= OpKeywordGrep BlockNonEmpty OpListKeywordArg 1015 | | OpKeywordGrep OpListKeywordArgNonBrace 1016 | 1017 | OpKeywordHexExpr ::= OpKeywordHex OpUnaryKeywordArg 1018 | | OpKeywordHex 1019 | 1020 | OpKeywordIndexExpr ::= OpKeywordIndex OpListKeywordArg 1021 | 1022 | OpKeywordIntExpr ::= OpKeywordInt OpUnaryKeywordArg 1023 | | OpKeywordInt 1024 | 1025 | OpKeywordIoctlExpr ::= OpKeywordIoctl OpListKeywordArg 1026 | 1027 | OpKeywordJoinExpr ::= OpKeywordJoin OpListKeywordArg 1028 | 1029 | OpKeywordKeysExpr ::= OpKeywordKeys OpUnaryKeywordArg 1030 | 1031 | OpKeywordKillExpr ::= OpKeywordKill OpListKeywordArg 1032 | | OpKeywordKill Expression 1033 | 1034 | OpKeywordLastExpr ::= OpKeywordLast OpAssignKeywordArg 1035 | | OpKeywordLast Label 1036 | | OpKeywordLast 1037 | 1038 | OpKeywordLcExpr ::= OpKeywordLc OpUnaryKeywordArg 1039 | | OpKeywordLc 1040 | 1041 | OpKeywordLcfirstExpr ::= OpKeywordLcfirst OpUnaryKeywordArg 1042 | | OpKeywordLcfirst 1043 | 1044 | OpKeywordLengthExpr ::= OpKeywordLength OpUnaryKeywordArg 1045 | | OpKeywordLength 1046 | 1047 | OpKeywordLinkExpr ::= OpKeywordLink OpListKeywordArg 1048 | 1049 | OpKeywordListenExpr ::= OpKeywordListen OpListKeywordArg 1050 | 1051 | OpKeywordLocaltimeExpr ::= OpKeywordLocaltime OpUnaryKeywordArg 1052 | | OpKeywordLocaltime 1053 | 1054 | OpKeywordLockExpr ::= OpKeywordLock OpUnaryKeywordArg 1055 | 1056 | OpKeywordLogExpr ::= OpKeywordLog OpUnaryKeywordArg 1057 | | OpKeywordLog 1058 | 1059 | OpKeywordLstatExpr ::= OpKeywordLstat OpUnaryKeywordArg 1060 | | OpKeywordLstat 1061 | 1062 | OpKeywordMapExpr ::= OpKeywordMap BlockNonEmpty OpListKeywordArg 1063 | | OpKeywordMap OpListKeywordArgNonBrace 1064 | 1065 | OpKeywordMkdirExpr ::= OpKeywordMkdir OpListKeywordArg 1066 | | OpKeywordMkdir 1067 | 1068 | OpKeywordMsgctlExpr ::= OpKeywordMsgctl OpListKeywordArg 1069 | 1070 | OpKeywordMsggetExpr ::= OpKeywordMsgget OpListKeywordArg 1071 | 1072 | OpKeywordMsgrcvExpr ::= OpKeywordMsgrcv OpListKeywordArg 1073 | 1074 | OpKeywordMsgsndExpr ::= OpKeywordMsgsnd OpListKeywordArg 1075 | 1076 | OpKeywordNextExpr ::= OpKeywordNext OpAssignKeywordArg 1077 | | OpKeywordNext Label 1078 | | OpKeywordNext 1079 | 1080 | OpKeywordOctExpr ::= OpKeywordOct OpUnaryKeywordArg 1081 | | OpKeywordOct 1082 | 1083 | OpKeywordOpenExpr ::= OpKeywordOpen OpListKeywordArg 1084 | 1085 | OpKeywordOpendirExpr ::= OpKeywordOpendir OpListKeywordArg 1086 | 1087 | OpKeywordOrdExpr ::= OpKeywordOrd OpUnaryKeywordArg 1088 | | OpKeywordOrd 1089 | 1090 | OpKeywordPackExpr ::= OpKeywordPack OpListKeywordArg 1091 | 1092 | OpKeywordPipeExpr ::= OpKeywordPipe OpListKeywordArg 1093 | 1094 | OpKeywordPopExpr ::= OpKeywordPop OpUnaryKeywordArg 1095 | | OpKeywordPop 1096 | 1097 | OpKeywordPosExpr ::= OpKeywordPos OpUnaryKeywordArg 1098 | | OpKeywordPos 1099 | 1100 | OpKeywordPrintExpr ::= OpKeywordPrint BlockNonEmpty OpListKeywordArg 1101 | | OpKeywordPrint BuiltinFilehandle OpListKeywordArgNonBrace 1102 | | OpKeywordPrint BuiltinFilehandle 1103 | | OpKeywordPrint OpListKeywordArgNonBrace 1104 | | OpKeywordPrint BlockNonEmpty 1105 | | OpKeywordPrint 1106 | 1107 | OpKeywordPrintfExpr ::= OpKeywordPrintf BlockNonEmpty OpListKeywordArg 1108 | | OpKeywordPrintf BuiltinFilehandle OpListKeywordArgNonBrace 1109 | | OpKeywordPrintf BuiltinFilehandle 1110 | | OpKeywordPrintf OpListKeywordArgNonBrace 1111 | | OpKeywordPrintf BlockNonEmpty 1112 | 1113 | OpKeywordPrototypeExpr ::= OpKeywordPrototype OpUnaryKeywordArg 1114 | | OpKeywordPrototype 1115 | 1116 | OpKeywordPushExpr ::= OpKeywordPush OpListKeywordArg 1117 | 1118 | OpKeywordQuotemetaExpr ::= OpKeywordQuotemeta OpUnaryKeywordArg 1119 | | OpKeywordQuotemeta 1120 | 1121 | OpKeywordRandExpr ::= OpKeywordRand OpUnaryKeywordArg 1122 | | OpKeywordRand 1123 | 1124 | OpKeywordReadExpr ::= OpKeywordRead OpListKeywordArg 1125 | 1126 | OpKeywordReaddirExpr ::= OpKeywordReaddir OpUnaryKeywordArg 1127 | 1128 | OpKeywordReadlineExpr ::= OpKeywordReadline LParen BuiltinFilehandle RParen 1129 | | OpKeywordReadline BuiltinFilehandle 1130 | | OpKeywordReadline OpUnaryKeywordArg 1131 | | OpKeywordReadline 1132 | 1133 | OpKeywordReadlinkExpr ::= OpKeywordReadlink OpUnaryKeywordArg 1134 | | OpKeywordReadlink 1135 | 1136 | OpKeywordReadpipeExpr ::= OpKeywordReadpipe OpUnaryKeywordArg 1137 | | OpKeywordReadpipe 1138 | 1139 | OpKeywordRecvExpr ::= OpKeywordRecv OpListKeywordArg 1140 | 1141 | OpKeywordRedoExpr ::= OpKeywordRedo OpAssignKeywordArg 1142 | | OpKeywordRedo Label 1143 | | OpKeywordRedo 1144 | 1145 | OpKeywordRefExpr ::= OpKeywordRef OpUnaryKeywordArg 1146 | | OpKeywordRef 1147 | 1148 | OpKeywordRenameExpr ::= OpKeywordRename OpListKeywordArg 1149 | 1150 | OpKeywordResetExpr ::= OpKeywordReset OpUnaryKeywordArg 1151 | | OpKeywordReset 1152 | 1153 | OpKeywordReturnExpr ::= OpKeywordReturn OpListKeywordArg 1154 | | OpKeywordReturn 1155 | 1156 | OpKeywordReverseExpr ::= OpKeywordReverse OpListKeywordArg 1157 | 1158 | OpKeywordRewinddirExpr ::= OpKeywordRewinddir OpUnaryKeywordArg 1159 | | OpKeywordRewinddir 1160 | 1161 | OpKeywordRindexExpr ::= OpKeywordRindex OpListKeywordArg 1162 | | OpKeywordRindex 1163 | 1164 | OpKeywordRmdirExpr ::= OpKeywordRmdir OpUnaryKeywordArg 1165 | | OpKeywordRmdir 1166 | 1167 | OpKeywordSayExpr ::= OpKeywordSay BlockNonEmpty OpListKeywordArg 1168 | | OpKeywordSay BuiltinFilehandle OpListKeywordArgNonBrace 1169 | | OpKeywordSay BuiltinFilehandle 1170 | | OpKeywordSay OpListKeywordArgNonBrace 1171 | | OpKeywordSay BlockNonEmpty 1172 | | OpKeywordSay 1173 | 1174 | OpKeywordScalarExpr ::= OpKeywordScalar OpUnaryKeywordArg 1175 | 1176 | OpKeywordSeekExpr ::= OpKeywordSeek OpListKeywordArg 1177 | 1178 | OpKeywordSeekdirExpr ::= OpKeywordSeekdir OpListKeywordArg 1179 | 1180 | OpKeywordSelectExpr ::= OpKeywordSelect OpListKeywordArg 1181 | 1182 | OpKeywordSemctlExpr ::= OpKeywordSemctl OpListKeywordArg 1183 | 1184 | OpKeywordSemgetExpr ::= OpKeywordSemget OpListKeywordArg 1185 | 1186 | OpKeywordSemopExpr ::= OpKeywordSemop OpListKeywordArg 1187 | 1188 | OpKeywordSendExpr ::= OpKeywordSend OpListKeywordArg 1189 | 1190 | OpKeywordSetpgrpExpr ::= OpKeywordSetpgrp OpListKeywordArg 1191 | 1192 | OpKeywordSetpriorityExpr ::= OpKeywordSetpriority OpListKeywordArg 1193 | 1194 | OpKeywordSetsockoptExpr ::= OpKeywordSetsockopt OpListKeywordArg 1195 | 1196 | OpKeywordShiftExpr ::= OpKeywordShift OpUnaryKeywordArg 1197 | | OpKeywordShift 1198 | 1199 | OpKeywordShmctlExpr ::= OpKeywordShmctl OpListKeywordArg 1200 | 1201 | OpKeywordShmgetExpr ::= OpKeywordShmget OpListKeywordArg 1202 | 1203 | OpKeywordShmreadExpr ::= OpKeywordShmread OpListKeywordArg 1204 | 1205 | OpKeywordShmwriteExpr ::= OpKeywordShmwrite OpListKeywordArg 1206 | 1207 | OpKeywordShutdownExpr ::= OpKeywordShutdown OpListKeywordArg 1208 | 1209 | OpKeywordSinExpr ::= OpKeywordSin OpUnaryKeywordArg 1210 | | OpKeywordSin 1211 | 1212 | OpKeywordSleepExpr ::= OpKeywordSleep OpUnaryKeywordArg 1213 | | OpKeywordSleep 1214 | 1215 | OpKeywordSocketExpr ::= OpKeywordSocket OpListKeywordArg 1216 | 1217 | OpKeywordSocketpairExpr ::= OpKeywordSocketpair OpListKeywordArg 1218 | 1219 | OpKeywordSortExpr ::= OpKeywordSort BlockNonEmpty OpListKeywordArg 1220 | | OpKeywordSort VarScalar OpListKeywordArg 1221 | | OpKeywordSort OpListKeywordArgNonBrace 1222 | 1223 | OpKeywordSpliceExpr ::= OpKeywordSplice OpListKeywordArg 1224 | 1225 | OpKeywordSplitExpr ::= OpKeywordSplit OpListKeywordArg 1226 | 1227 | OpKeywordSprintfExpr ::= OpKeywordSprintf OpListKeywordArg 1228 | 1229 | OpKeywordSqrtExpr ::= OpKeywordSqrt OpUnaryKeywordArg 1230 | | OpKeywordSqrt 1231 | 1232 | OpKeywordSrandExpr ::= OpKeywordSrand OpUnaryKeywordArg 1233 | | OpKeywordSrand 1234 | 1235 | OpKeywordStatExpr ::= OpKeywordStat OpUnaryKeywordArg 1236 | | OpKeywordStat 1237 | 1238 | OpKeywordStudyExpr ::= OpKeywordStudy OpUnaryKeywordArg 1239 | | OpKeywordStudy 1240 | 1241 | OpKeywordSubExpr ::= OpKeywordSub SubDefinition 1242 | 1243 | OpKeywordSubstrExpr ::= OpKeywordSubstr OpListKeywordArg 1244 | 1245 | OpKeywordSymlinkExpr ::= OpKeywordSymlink OpListKeywordArg 1246 | 1247 | OpKeywordSyscallExpr ::= OpKeywordSyscall OpListKeywordArg 1248 | 1249 | OpKeywordSysopenExpr ::= OpKeywordSysopen OpListKeywordArg 1250 | 1251 | OpKeywordSysreadExpr ::= OpKeywordSysread OpListKeywordArg 1252 | 1253 | OpKeywordSysseekExpr ::= OpKeywordSysseek OpListKeywordArg 1254 | 1255 | OpKeywordSyswriteExpr ::= OpKeywordSyswrite OpListKeywordArg 1256 | 1257 | OpKeywordSystemExpr ::= OpKeywordSystem BlockNonEmpty OpListKeywordArg 1258 | | OpKeywordSystem OpListKeywordArgNonBrace 1259 | 1260 | OpKeywordTellExpr ::= OpKeywordTell OpUnaryKeywordArg 1261 | | OpKeywordTell 1262 | 1263 | OpKeywordTelldirExpr ::= OpKeywordTelldir OpUnaryKeywordArg 1264 | 1265 | OpKeywordTieExpr ::= OpKeywordTie OpListKeywordArg 1266 | 1267 | OpKeywordTiedExpr ::= OpKeywordTied OpUnaryKeywordArg 1268 | 1269 | OpKeywordTimeExpr ::= OpKeywordTime LParen RParen 1270 | | OpKeywordTime 1271 | 1272 | OpKeywordTimesExpr ::= OpKeywordTimes LParen RParen 1273 | | OpKeywordTimes 1274 | 1275 | OpKeywordTruncateExpr ::= OpKeywordTruncate OpListKeywordArg 1276 | 1277 | OpKeywordUcExpr ::= OpKeywordUc OpUnaryKeywordArg 1278 | | OpKeywordUc 1279 | 1280 | OpKeywordUcfirstExpr ::= OpKeywordUcfirst OpUnaryKeywordArg 1281 | | OpKeywordUcfirst 1282 | 1283 | OpKeywordUmaskExpr ::= OpKeywordUmask OpUnaryKeywordArg 1284 | | OpKeywordUmask 1285 | 1286 | OpKeywordUndefExpr ::= OpKeywordUndef OpUnaryKeywordArg 1287 | | OpKeywordUndef 1288 | 1289 | OpKeywordUnlinkExpr ::= OpKeywordUnlink OpUnaryKeywordArg 1290 | | OpKeywordUnlink 1291 | 1292 | OpKeywordUnpackExpr ::= OpKeywordUnpack OpListKeywordArg 1293 | 1294 | OpKeywordUnshiftExpr ::= OpKeywordUnshift OpListKeywordArg 1295 | 1296 | OpKeywordUntieExpr ::= OpKeywordUntie OpUnaryKeywordArg 1297 | 1298 | OpKeywordUtimeExpr ::= OpKeywordUtime OpUnaryKeywordArg 1299 | 1300 | OpKeywordValuesExpr ::= OpKeywordValues OpUnaryKeywordArg 1301 | 1302 | OpKeywordVecExpr ::= OpKeywordVec OpListKeywordArg 1303 | 1304 | OpKeywordWaitExpr ::= OpKeywordWait LParen RParen 1305 | | OpKeywordWait 1306 | 1307 | OpKeywordWaitpidExpr ::= OpKeywordWaitpid OpListKeywordArg 1308 | 1309 | OpKeywordWantarrayExpr ::= OpKeywordWantarray LParen RParen 1310 | | OpKeywordWantarray 1311 | 1312 | OpKeywordWarnExpr ::= OpKeywordWarn OpListKeywordArg 1313 | | OpKeywordWarn 1314 | 1315 | OpKeywordWriteExpr ::= OpKeywordWrite OpListKeywordArg 1316 | | OpKeywordWrite 1317 | 1318 | OpFile ::= 1319 | OpFileReadableEffective 1320 | | OpFileWritableEffective 1321 | | OpFileRExecutableEffective 1322 | | OpFileOwnedEffective 1323 | | OpFileReadableReal 1324 | | OpFileWritableReal 1325 | | OpFileRExecutableReal 1326 | | OpFileOwnedReal 1327 | | OpFileExists 1328 | | OpFileEmpty 1329 | | OpFileNonEmpty 1330 | | OpFilePlain 1331 | | OpFileDirectory 1332 | | OpFileSymbolic 1333 | | OpFileNamedPipe 1334 | | OpFileSocket 1335 | | OpFileBlock 1336 | | OpFileCharacter 1337 | | OpFileOpenedTty 1338 | | OpFileSetuid 1339 | | OpFileSetgid 1340 | | OpFileSticky 1341 | | OpFileAsciiUtf8 1342 | | OpFileBinary 1343 | | OpFileStartTime 1344 | | OpFileAccessTime 1345 | | OpFileChangeTime 1346 | 1347 | OpFileExpr ::= OpFile OpFileArg 1348 | OpFileArg ::= OpUnaryKeywordArg 1349 | | BuiltinFilehandle 1350 | | OpFileDefaultArg 1351 | 1352 | OpFileDefaultArg ~ '_' 1353 | 1354 | QLikeValue ::= QLikeValueExpr | QLikeValueExprWithMods 1355 | 1356 | QLikeValueExpr 1357 | ~ QLikeFunction '(' NonRParenOrEscapedParens_Any ')' 1358 | | QLikeFunction '{' NonRBraceOrEscapedBraces_Any '}' 1359 | | QLikeFunction '<' NonRAngleOrEscapedAngles_Any '>' 1360 | | QLikeFunction '[' NonRBracketOrEscapedBrackets_Any ']' 1361 | | QLikeFunction '/' NonForwardSlashOrEscapedForwardSlashes_Any '/' 1362 | | QLikeFunction '!' NonExclamPointOrEscapedExclamPoints_Any '!' 1363 | | QLikeFunction '|' NonPipeOrEscapedPipes_Any '|' 1364 | 1365 | QLikeFunction ~ OpKeywordQ 1366 | | OpKeywordQq 1367 | | OpKeywordQx 1368 | | OpKeywordQw 1369 | 1370 | # Here we begin with "qr//" and "m//" which can have parameters 1371 | # Then we continue with "s///", "tr///", and "y///" which have two args, not one 1372 | # "//" follow at the end 1373 | QLikeValueExprWithMods 1374 | ~ QLikeFunctionWithMods '(' NonRParenOrEscapedParens_Any ')' RegexModifiers 1375 | | QLikeFunctionWithMods '{' NonRBraceOrEscapedBraces_Any '}' RegexModifiers 1376 | | QLikeFunctionWithMods '<' NonRAngleOrEscapedAngles_Any '>' RegexModifiers 1377 | | QLikeFunctionWithMods '[' NonRBracketOrEscapedBrackets_Any ']' RegexModifiers 1378 | | QLikeFunctionWithMods '/' NonForwardSlashOrEscapedForwardSlashes_Any '/' RegexModifiers 1379 | | QLikeFunctionWithMods '!' NonExclamPointOrEscapedExclamPoints_Any '!' RegexModifiers 1380 | | QLikeFunctionWithMods '|' NonPipeOrEscapedPipes_Any '|' RegexModifiers 1381 | | QLikeSubstWithMods '(' NonRParenOrEscapedParens_Any ')(' NonRParenOrEscapedParens_Any ')' RegexModifiers 1382 | | QLikeSubstWithMods '{' NonRBraceOrEscapedBraces_Any '}{' NonRBraceOrEscapedBraces_Any '}' RegexModifiers 1383 | | QLikeSubstWithMods '<' NonRAngleOrEscapedAngles_Any '><' NonRAngleOrEscapedAngles_Any '>' RegexModifiers 1384 | | QLikeSubstWithMods '[' NonRBracketOrEscapedBrackets_Any '][' NonRBracketOrEscapedBrackets_Any ']' RegexModifiers 1385 | | QLikeSubstWithMods '/' NonForwardSlashOrEscapedForwardSlashes_Any '/' NonForwardSlashOrEscapedForwardSlashes_Any '/' RegexModifiers 1386 | | QLikeSubstWithMods '!' NonExclamPointOrEscapedExclamPoints_Any '!' NonExclamPointOrEscapedExclamPoints_Any '!' RegexModifiers 1387 | | QLikeSubstWithMods '|' NonPipeOrEscapedPipes_Any '|' NonPipeOrEscapedPipes_Any '|' RegexModifiers 1388 | | '/' NonForwardSlashOrEscapedForwardSlashes_Any '/' RegexModifiers 1389 | | '`' NonBacktickOrEscapedBackticks_Any '`' 1390 | 1391 | QLikeFunctionWithMods ~ OpKeywordQr 1392 | | OpKeywordM 1393 | 1394 | QLikeSubstWithMods ~ OpKeywordS 1395 | | OpKeywordTr 1396 | | OpKeywordY 1397 | 1398 | RegexModifiers ~ [a-z]* 1399 | 1400 | ### 1401 | 1402 | # Everything except: # q* / s / m / y / t* 1403 | # | | | | | 1404 | # a b c d e f g h i j k l m n o p q r s t u v w x y z 1405 | # a - l n - p r u - x z 1406 | # (Cannot begin with digit, so digits are out) 1407 | NonQLikeLetters ~ [a-ln-pru-xzA-Z_]+ 1408 | 1409 | # Everything except: q / w / r / x (qq, qw, qr, qx) 1410 | # | | | | 1411 | # a b c d e f g h i j k l m n o p q r s t u v w x y z 1412 | # a - p s - v y-z 1413 | # (digits also allowed at this point) 1414 | NonQRWXLetters ~ [a-ps-vy-zA-Z0-9_]+ 1415 | 1416 | # Everything except: r (tr) 1417 | # | 1418 | # a b c d e f g h i j k l m n o p q r s t u v w x y z 1419 | # a - q s - z 1420 | # (digits also allowed at this point) 1421 | NonRLetter ~ [a-qs-zA-Z0-9_] 1422 | 1423 | # Everything else allowed (including digits) 1424 | AllSubLetters ~ [a-zA-Z0-9_]+ 1425 | 1426 | IdentComp ~ [a-zA-Z_0-9]+ 1427 | PackageSep ~ '::' 1428 | 1429 | VersionExpr ::= VersionNumber 1430 | VersionNumber ~ VersionNumberSegments 1431 | | 'v' VersionNumberSegments 1432 | 1433 | VersionNumberSegments ~ VersionNumberSegment '.' VersionNumberSegment '.' VersionNumberSegment 1434 | | VersionNumberSegment '.' VersionNumberSegment 1435 | | VersionNumberSegment 1436 | 1437 | VersionNumberSegment ~ [0-9] [0-9] [0-9] 1438 | | [0-9] [0-9] 1439 | | [0-9] 1440 | 1441 | LitNumber ::= LitNumberDec 1442 | | LitNumberOct 1443 | | LitNumberHex 1444 | | LitNumberBin 1445 | 1446 | LitNumberDec ::= NumberDec 1447 | LitNumberOct ::= NumberOct 1448 | LitNumberHex ::= NumberHex 1449 | LitNumberBin ::= NumberBin 1450 | 1451 | NumberDec ~ NumberDecInt 1452 | | NumberDecInt ExpDec 1453 | | NumberDecInt '.' DigitsDec 1454 | | NumberDecInt '.' DigitsDec ExpDec 1455 | | '.' DigitDec DigitsDec 1456 | | '.' DigitDec DigitsDec ExpDec 1457 | 1458 | NumberDecInt ~ [1-9] DigitsDec 1459 | | '0' 1460 | 1461 | NumberOct ~ '0' Underbars DigitOct DigitsOct 1462 | | '0' Underbars DigitOct DigitsOct ExpHex 1463 | | '0' DigitsOct '.' DigitOct DigitsOct ExpHex 1464 | 1465 | NumberHex ~ '0' [xX] Underbars DigitHex DigitsHex 1466 | | '0' [xX] Underbars DigitHex DigitsHex ExpHex 1467 | | '0' [xX] DigitsHex '.' DigitHex DigitsHex ExpHex 1468 | 1469 | NumberBin ~ '0' [bB] Underbars DigitBin DigitsBin 1470 | | '0' [bB] Underbars DigitBin DigitsBin ExpHex 1471 | | '0' [bB] DigitsBin '.' DigitBin DigitsBin ExpHex 1472 | 1473 | ExpDec ~ [eE] [+-] ExpDecExp 1474 | | [eE] [_] [+-] ExpDecExp 1475 | | [eE] ExpDecExp 1476 | 1477 | ExpDecExp ~ Underbars DigitDec DigitsDec 1478 | Underbars ~ [_]* 1479 | 1480 | ExpHex ~ [pP] [+-] DigitDec DigitsDec 1481 | | [pP] DigitDec DigitsDec 1482 | 1483 | DigitDec ~ [0-9] 1484 | DigitsDec ~ [0-9_]* 1485 | 1486 | DigitOct ~ [0-7] 1487 | DigitsOct ~ [0-7_]* 1488 | DigitHex ~ [0-9a-fA-F] 1489 | DigitsHex ~ [0-9a-fA-F_]* 1490 | 1491 | DigitBin ~ [01] 1492 | DigitsBin ~ [01_]* 1493 | 1494 | Digits ~ [0-9]+ 1495 | SingleQuote ~ ['] 1496 | DoubleQuote ~ ["] 1497 | 1498 | NonDoubleOrEscapedQuote_Many ~ NonDoubleOrEscapedQuote+ 1499 | NonDoubleOrEscapedQuote ~ EscapedDoubleQuote | NonDoubleQuote 1500 | EscapedDoubleQuote ~ Escape ["] | Escape [^"] 1501 | NonDoubleQuote ~ [^"\x{005C}] 1502 | 1503 | NonSingleOrEscapedQuote_Many ~ NonSingleOrEscapedQuote+ 1504 | NonSingleOrEscapedQuote ~ EscapedSingleQuote | NonSingleQuote 1505 | EscapedSingleQuote ~ Escape ['] | Escape [^'] 1506 | NonSingleQuote ~ [^'\x{005C}] 1507 | 1508 | Colon ~ ':' 1509 | Semicolon ~ ';' 1510 | Escape ~ '\' 1511 | 1512 | SigilScalar ~ '$' 1513 | SigilArray ~ '@' 1514 | SigilHash ~ '%' 1515 | SigilCode ~ '&' 1516 | SigilGlob ~ '*' 1517 | SigilArrayTop ~ '$#' 1518 | 1519 | LParen ~ '(' 1520 | RParen ~ ')' 1521 | LBracket ~ '[' 1522 | RBracket ~ ']' 1523 | LBrace ~ '{' 1524 | RBrace ~ '}' 1525 | 1526 | NonRParenOrEscapedParens_Any ~ NonRParenOrEscapedParens* 1527 | NonRParenOrEscapedParens ~ EscapedParens | NonRParen 1528 | EscapedParens ~ EscapedLParen | EscapedRParen 1529 | EscapedLParen ~ Escape [(] 1530 | EscapedRParen ~ Escape [)] | Escape [^)] 1531 | NonRParen ~ [^)\x{005C}] 1532 | 1533 | NonRBracketOrEscapedBrackets_Any ~ NonRBracketOrEscapedBrackets* 1534 | NonRBracketOrEscapedBrackets ~ EscapedBrackets | NonRBracket 1535 | EscapedBrackets ~ EscapedLBracket | EscapedRBracket 1536 | EscapedLBracket ~ Escape [\[] 1537 | EscapedRBracket ~ Escape [\]] | Escape [^\]] 1538 | NonRBracket ~ [^\]\x{005C}] 1539 | 1540 | NonRBraceOrEscapedBraces_Any ~ NonRBraceOrEscapedBraces* 1541 | NonRBraceOrEscapedBraces ~ EscapedBraces | NonRBrace 1542 | EscapedBraces ~ EscapedLBrace | EscapedRBrace 1543 | EscapedLBrace ~ Escape [\{] 1544 | EscapedRBrace ~ Escape [\}] | Escape [^\}] 1545 | NonRBrace ~ [^\}\x{005C}] 1546 | 1547 | NonRAngleOrEscapedAngles_Any ~ NonRAngleOrEscapedAngles* 1548 | NonRAngleOrEscapedAngles ~ EscapedAngles | NonRAngle 1549 | EscapedAngles ~ EscapedLAngle | EscapedRAngle 1550 | EscapedLAngle ~ Escape [<] 1551 | EscapedRAngle ~ Escape [>] | Escape [^>] 1552 | NonRAngle ~ [^>\x{005C}] 1553 | 1554 | # Escape == \x{005C} 1555 | # ForwardSlash (solidus) == \x{002F} 1556 | NonForwardSlashOrEscapedForwardSlashes_Any ~ NonForwardSlashOrEscapedForwardSlashes* 1557 | NonForwardSlashOrEscapedForwardSlashes ~ EscapedForwardSlash | NonForwardSlash 1558 | EscapedForwardSlash ~ Escape Escape | Escape [^\x{005C}] 1559 | NonForwardSlash ~ [^\x{002F}\x{005C}] 1560 | 1561 | NonExclamPointOrEscapedExclamPoints_Any ~ NonExclamPointOrEscapedExclamPoints* 1562 | NonExclamPointOrEscapedExclamPoints ~ EscapedExclamPoint | NonExclamPoint 1563 | EscapedExclamPoint ~ Escape [!] | Escape [^!] 1564 | NonExclamPoint ~ [^!\x{005C}] 1565 | 1566 | NonPipeOrEscapedPipes_Any ~ NonPipeOrEscapedPipes* 1567 | NonPipeOrEscapedPipes ~ EscapedPipe | NonPipe 1568 | EscapedPipe ~ Escape [\|] | Escape [^\|] 1569 | NonPipe ~ [^\|\x{005C}] 1570 | 1571 | NonBacktickOrEscapedBackticks_Any ~ NonBacktickOrEscapedBackticks* 1572 | NonBacktickOrEscapedBackticks ~ EscapedBacktick | NonBacktick 1573 | EscapedBacktick ~ Escape [\`] | Escape [^\`] 1574 | NonBacktick ~ [^`\x{005C}] 1575 | 1576 | Ellipsis ~ '...' 1577 | 1578 | UnderscorePackage ~ '__PACKAGE__' 1579 | UnderscoreFile ~ '__FILE__' 1580 | UnderscoreLine ~ '__LINE__' 1581 | UnderscoreSub ~ '__SUB__' 1582 | #UnderscoreData ~ '__DATA__' 1583 | #UnderscoreEnd ~ '__END__' 1584 | 1585 | PhaseName ~ 'BEGIN' | 'CHECK' | 'INIT' | 'UNITCHECK' | 'END' 1586 | 1587 | SubAttrArgs ~ '(' NonRParenOrEscapedParens_Any ')' 1588 | 1589 | OpArrow ~ '->' 1590 | OpInc ~ '++' | '--' 1591 | OpPower ~ '**' 1592 | OpUnary ~ '!' | '~' | '\' | '+' | '-' 1593 | OpRegex ~ '=~' | '!~' 1594 | OpMulti ~ '*' | '/' | '%' | 'x' 1595 | OpAdd ~ '+' | '-' | '.' 1596 | OpShift ~ '<<' | '>>' 1597 | OpInequal ~ '<' | '>' | '<=' | '>=' | 'lt' | 'gt' | 'le' | 'ge' 1598 | OpEqual ~ '==' | '!=' | '<=>' | 'eq' | 'ne' | 'cmp' 1599 | OpBinAnd ~ '&' 1600 | OpBinOr ~ '|' | '^' 1601 | OpLogAnd ~ '&&' 1602 | OpLogOr ~ '||' | '//' 1603 | OpRange ~ '..' | '...' 1604 | OpTriThen ~ '?' 1605 | OpTriElse ~ ':' 1606 | OpAssign ~ '=' | '*=' | '/=' | '%=' | 'x=' | '+=' | '-=' | '.=' | '<<=' | '>>=' | '&=' | '|=' | '^=' | '&&=' | '||=' | '//=' 1607 | OpComma ~ ',' | '=>' 1608 | OpNameNot ~ 'not' 1609 | OpNameAnd ~ 'and' 1610 | OpNameOr ~ 'or' | 'xor' 1611 | 1612 | OpKeywordAbs ~ 'abs' 1613 | OpKeywordAccept ~ 'accept' 1614 | OpKeywordAlarm ~ 'alarm' 1615 | OpKeywordAtan2 ~ 'atan2' 1616 | OpKeywordBind ~ 'bind' 1617 | OpKeywordBinmode ~ 'binmode' 1618 | OpKeywordBless ~ 'bless' 1619 | OpKeywordBreak ~ 'break' 1620 | OpKeywordCaller ~ 'caller' 1621 | OpKeywordChdir ~ 'chdir' 1622 | OpKeywordChmod ~ 'chmod' 1623 | OpKeywordChomp ~ 'chomp' 1624 | OpKeywordChop ~ 'chop' 1625 | OpKeywordChown ~ 'chown' 1626 | OpKeywordChr ~ 'chr' 1627 | OpKeywordChroot ~ 'chroot' 1628 | OpKeywordClose ~ 'close' 1629 | OpKeywordClosedir ~ 'closedir' 1630 | OpKeywordConnect ~ 'connect' 1631 | OpKeywordContinue ~ 'continue' 1632 | OpKeywordCos ~ 'cos' 1633 | OpKeywordCrypt ~ 'crypt' 1634 | OpKeywordDbmclose ~ 'dbmclose' 1635 | OpKeywordDbmopen ~ 'dbmopen' 1636 | OpKeywordDefined ~ 'defined' 1637 | OpKeywordDelete ~ 'delete' 1638 | OpKeywordDie ~ 'die' 1639 | OpKeywordDo ~ 'do' 1640 | OpKeywordDump ~ 'dump' 1641 | OpKeywordEach ~ 'each' 1642 | OpKeywordEof ~ 'eof' 1643 | OpKeywordEval ~ 'eval' 1644 | OpKeywordEvalbytes ~ 'evalbytes' 1645 | OpKeywordExec ~ 'exec' 1646 | OpKeywordExists ~ 'exists' 1647 | OpKeywordExit ~ 'exit' 1648 | OpKeywordExp ~ 'exp' 1649 | OpKeywordFc ~ 'fc' 1650 | OpKeywordFor ~ 'for' 1651 | OpKeywordForeach ~ 'foreach' 1652 | OpKeywordFcntl ~ 'fcntl' 1653 | OpKeywordFileno ~ 'fileno' 1654 | OpKeywordFlock ~ 'flock' 1655 | OpKeywordFork ~ 'fork' 1656 | OpKeywordGetc ~ 'getc' 1657 | OpKeywordGetlogin ~ 'getlogin' 1658 | OpKeywordGetpeername ~ 'getpeername' 1659 | OpKeywordGetpgrp ~ 'getpgrp' 1660 | OpKeywordGetppid ~ 'getppid' 1661 | OpKeywordGetpriority ~ 'getpriority' 1662 | OpKeywordGetpwnam ~ 'getpwnam' 1663 | OpKeywordGetgrnam ~ 'getgrnam' 1664 | OpKeywordGethostbyname ~ 'gethostbyname' 1665 | OpKeywordGetnetbyname ~ 'getnetbyname' 1666 | OpKeywordGetprotobyname ~ 'getprotobyname' 1667 | OpKeywordGetpwuid ~ 'getpwuid' 1668 | OpKeywordGetgrgid ~ 'getgrgid' 1669 | OpKeywordGetservbyname ~ 'getservbyname' 1670 | OpKeywordGethostbyaddr ~ 'gethostbyaddr' 1671 | OpKeywordGetnetbyaddr ~ 'getnetbyaddr' 1672 | OpKeywordGetprotobynumber ~ 'getprotobynumber' 1673 | OpKeywordGetservbyport ~ 'getservbyport' 1674 | OpKeywordGetpwent ~ 'getpwent' 1675 | OpKeywordGetgrent ~ 'getgrent' 1676 | OpKeywordGethostent ~ 'gethostent' 1677 | OpKeywordGetnetent ~ 'getnetent' 1678 | OpKeywordGetprotoent ~ 'getprotoent' 1679 | OpKeywordGetservent ~ 'getservent' 1680 | OpKeywordSetpwent ~ 'setpwent' 1681 | OpKeywordSetgrent ~ 'setgrent' 1682 | OpKeywordSethostent ~ 'sethostent' 1683 | OpKeywordSetnetent ~ 'setnetent' 1684 | OpKeywordSetprotoent ~ 'setprotoent' 1685 | OpKeywordSetservent ~ 'setservent' 1686 | OpKeywordEndpwent ~ 'endpwent' 1687 | OpKeywordEndgrent ~ 'endgrent' 1688 | OpKeywordEndhostent ~ 'endhostent' 1689 | OpKeywordEndnetent ~ 'endnetent' 1690 | OpKeywordEndprotoent ~ 'endprotoent' 1691 | OpKeywordEndservent ~ 'endservent' 1692 | OpKeywordGetsockname ~ 'getsockname' 1693 | OpKeywordGetsockopt ~ 'getsockopt' 1694 | OpKeywordGlob ~ 'glob' 1695 | OpKeywordGmtime ~ 'gmtime' 1696 | OpKeywordGoto ~ 'goto' 1697 | OpKeywordGrep ~ 'grep' 1698 | OpKeywordHex ~ 'hex' 1699 | OpKeywordIndex ~ 'index' 1700 | OpKeywordInt ~ 'int' 1701 | OpKeywordIoctl ~ 'ioctl' 1702 | OpKeywordJoin ~ 'join' 1703 | OpKeywordKeys ~ 'keys' 1704 | OpKeywordKill ~ 'kill' 1705 | OpKeywordLast ~ 'last' 1706 | OpKeywordLc ~ 'lc' 1707 | OpKeywordLcfirst ~ 'lcfirst' 1708 | OpKeywordLength ~ 'length' 1709 | OpKeywordLink ~ 'link' 1710 | OpKeywordListen ~ 'listen' 1711 | OpKeywordLocal ~ 'local' 1712 | OpKeywordLocaltime ~ 'localtime' 1713 | OpKeywordLock ~ 'lock' 1714 | OpKeywordLog ~ 'log' 1715 | OpKeywordLstat ~ 'lstat' 1716 | OpKeywordM ~ 'm' 1717 | OpKeywordMap ~ 'map' 1718 | OpKeywordMkdir ~ 'mkdir' 1719 | OpKeywordMsgctl ~ 'msgctl' 1720 | OpKeywordMsgget ~ 'msgget' 1721 | OpKeywordMsgrcv ~ 'msgrcv' 1722 | OpKeywordMsgsnd ~ 'msgsnd' 1723 | OpKeywordMy ~ 'my' 1724 | OpKeywordNext ~ 'next' 1725 | OpKeywordNo ~ 'no' 1726 | OpKeywordOct ~ 'oct' 1727 | OpKeywordOpen ~ 'open' 1728 | OpKeywordOpendir ~ 'opendir' 1729 | OpKeywordOrd ~ 'ord' 1730 | OpKeywordOur ~ 'our' 1731 | OpKeywordPack ~ 'pack' 1732 | OpKeywordPackage ~ 'package' 1733 | OpKeywordPipe ~ 'pipe' 1734 | OpKeywordPop ~ 'pop' 1735 | OpKeywordPos ~ 'pos' 1736 | OpKeywordPrint ~ 'print' 1737 | OpKeywordPrintf ~ 'printf' 1738 | OpKeywordPrototype ~ 'prototype' 1739 | OpKeywordPush ~ 'push' 1740 | OpKeywordQ ~ 'q' 1741 | OpKeywordQq ~ 'qq' 1742 | OpKeywordQx ~ 'qx' 1743 | OpKeywordQw ~ 'qw' 1744 | OpKeywordQr ~ 'qr' 1745 | OpKeywordQuotemeta ~ 'quotemeta' 1746 | OpKeywordRand ~ 'rand' 1747 | OpKeywordRead ~ 'read' 1748 | OpKeywordReaddir ~ 'readdir' 1749 | OpKeywordReadline ~ 'readline' 1750 | OpKeywordReadlink ~ 'readlink' 1751 | OpKeywordReadpipe ~ 'readpipe' 1752 | OpKeywordRecv ~ 'recv' 1753 | OpKeywordRedo ~ 'redo' 1754 | OpKeywordRef ~ 'ref' 1755 | OpKeywordRename ~ 'rename' 1756 | OpKeywordRequire ~ 'require' 1757 | OpKeywordReset ~ 'reset' 1758 | OpKeywordReturn ~ 'return' 1759 | OpKeywordReverse ~ 'reverse' 1760 | OpKeywordRewinddir ~ 'rewinddir' 1761 | OpKeywordRindex ~ 'rindex' 1762 | OpKeywordRmdir ~ 'rmdir' 1763 | OpKeywordS ~ 's' 1764 | OpKeywordSay ~ 'say' 1765 | OpKeywordScalar ~ 'scalar' 1766 | OpKeywordSeek ~ 'seek' 1767 | OpKeywordSeekdir ~ 'seekdir' 1768 | OpKeywordSelect ~ 'select' 1769 | OpKeywordSemctl ~ 'semctl' 1770 | OpKeywordSemget ~ 'semget' 1771 | OpKeywordSemop ~ 'semop' 1772 | OpKeywordSend ~ 'send' 1773 | OpKeywordSetpgrp ~ 'setpgrp' 1774 | OpKeywordSetpriority ~ 'setpriority' 1775 | OpKeywordSetsockopt ~ 'setsockopt' 1776 | OpKeywordShift ~ 'shift' 1777 | OpKeywordShmctl ~ 'shmctl' 1778 | OpKeywordShmget ~ 'shmget' 1779 | OpKeywordShmread ~ 'shmread' 1780 | OpKeywordShmwrite ~ 'shmwrite' 1781 | OpKeywordShutdown ~ 'shutdown' 1782 | OpKeywordSin ~ 'sin' 1783 | OpKeywordSleep ~ 'sleep' 1784 | OpKeywordSocket ~ 'socket' 1785 | OpKeywordSocketpair ~ 'socketpair' 1786 | OpKeywordSort ~ 'sort' 1787 | OpKeywordSplice ~ 'splice' 1788 | OpKeywordSplit ~ 'split' 1789 | OpKeywordSprintf ~ 'sprintf' 1790 | OpKeywordSqrt ~ 'sqrt' 1791 | OpKeywordSrand ~ 'srand' 1792 | OpKeywordStat ~ 'stat' 1793 | OpKeywordState ~ 'state' 1794 | OpKeywordStudy ~ 'study' 1795 | OpKeywordSub ~ 'sub' 1796 | OpKeywordSubstr ~ 'substr' 1797 | OpKeywordSymlink ~ 'symlink' 1798 | OpKeywordSyscall ~ 'syscall' 1799 | OpKeywordSysopen ~ 'sysopen' 1800 | OpKeywordSysread ~ 'sysread' 1801 | OpKeywordSysseek ~ 'sysseek' 1802 | OpKeywordSystem ~ 'system' 1803 | OpKeywordSyswrite ~ 'syswrite' 1804 | OpKeywordTr ~ 'tr' 1805 | OpKeywordTell ~ 'tell' 1806 | OpKeywordTelldir ~ 'telldir' 1807 | OpKeywordTie ~ 'tie' 1808 | OpKeywordTied ~ 'tied' 1809 | OpKeywordTime ~ 'time' 1810 | OpKeywordTimes ~ 'times' 1811 | OpKeywordTruncate ~ 'truncate' 1812 | OpKeywordUc ~ 'uc' 1813 | OpKeywordUcfirst ~ 'ucfirst' 1814 | OpKeywordUmask ~ 'umask' 1815 | OpKeywordUndef ~ 'undef' 1816 | OpKeywordUnlink ~ 'unlink' 1817 | OpKeywordUnpack ~ 'unpack' 1818 | OpKeywordUnshift ~ 'unshift' 1819 | OpKeywordUntie ~ 'untie' 1820 | OpKeywordUse ~ 'use' 1821 | OpKeywordUtime ~ 'utime' 1822 | OpKeywordValues ~ 'values' 1823 | OpKeywordVec ~ 'vec' 1824 | OpKeywordWait ~ 'wait' 1825 | OpKeywordWaitpid ~ 'waitpid' 1826 | OpKeywordWantarray ~ 'wantarray' 1827 | OpKeywordWarn ~ 'warn' 1828 | OpKeywordWrite ~ 'write' 1829 | OpKeywordY ~ 'y' 1830 | 1831 | OpFileReadableEffective ~ '-r' 1832 | OpFileWritableEffective ~ '-w' 1833 | OpFileRExecutableEffective ~ '-x' 1834 | OpFileOwnedEffective ~ '-o' 1835 | OpFileReadableReal ~ '-R' 1836 | OpFileWritableReal ~ '-W' 1837 | OpFileRExecutableReal ~ '-X' 1838 | OpFileOwnedReal ~ '-O' 1839 | OpFileExists ~ '-e' 1840 | OpFileEmpty ~ '-z' 1841 | OpFileNonEmpty ~ '-s' 1842 | OpFilePlain ~ '-f' 1843 | OpFileDirectory ~ '-d' 1844 | OpFileSymbolic ~ '-l' 1845 | OpFileNamedPipe ~ '-p' 1846 | OpFileSocket ~ '-S' 1847 | OpFileBlock ~ '-b' 1848 | OpFileCharacter ~ '-c' 1849 | OpFileOpenedTty ~ '-t' 1850 | OpFileSetuid ~ '-u' 1851 | OpFileSetgid ~ '-g' 1852 | OpFileSticky ~ '-k' 1853 | OpFileAsciiUtf8 ~ '-T' 1854 | OpFileBinary ~ '-B' 1855 | OpFileStartTime ~ '-M' 1856 | OpFileAccessTime ~ '-A' 1857 | OpFileChangeTime ~ '-C' 1858 | 1859 | ConditionIf ~ 'if' 1860 | ConditionElsif ~ 'elsif' 1861 | ConditionElse ~ 'else' 1862 | ConditionUnless ~ 'unless' 1863 | ConditionWhile ~ 'while' 1864 | ConditionUntil ~ 'until' 1865 | ConditionFor ~ 'for' 1866 | ConditionForeach ~ 'foreach' 1867 | 1868 | BuiltinFilehandle ~ 'STDIN' | 'STDOUT' | 'STDERR' | 'ARGV' | 'ARGVOUT' | 'DATA' 1869 | 1870 | # Ignore whitespace 1871 | :discard ~ whitespace 1872 | whitespace ~ [\s]+ 1873 | 1874 | # Comments 1875 | :discard ~ 1876 | ~ | 1877 | ~ '#' 1878 | ~ '#' 1879 | ~ * 1880 | ~ [\x{A}\x{B}\x{C}\x{D}\x{0085}\x{2028}\x{2029}] 1881 | ~ [^\x{A}\x{B}\x{C}\x{D}\x{0085}\x{2028}\x{2029}] 1882 | 1883 | # Recognize phases before subroutines 1884 | :lexeme ~ PhaseName priority => 1 1885 | 1886 | # Recognize q* / s / m / y / tr before subroutines 1887 | :lexeme ~ QLikeValueExpr priority => 1 1888 | :lexeme ~ QLikeValueExprWithMods priority => 1 1889 | 1890 | :lexeme ~ VersionNumber priority => 1 1891 | 1892 | # Prioritize keywords over functions 1893 | :lexeme ~ OpKeywordAbs priority => 1 1894 | :lexeme ~ OpKeywordAccept priority => 1 1895 | :lexeme ~ OpKeywordAlarm priority => 1 1896 | :lexeme ~ OpKeywordAtan2 priority => 1 1897 | :lexeme ~ OpKeywordBind priority => 1 1898 | :lexeme ~ OpKeywordBinmode priority => 1 1899 | :lexeme ~ OpKeywordBless priority => 1 1900 | :lexeme ~ OpKeywordBreak priority => 1 1901 | :lexeme ~ OpKeywordCaller priority => 1 1902 | :lexeme ~ OpKeywordChdir priority => 1 1903 | :lexeme ~ OpKeywordChmod priority => 1 1904 | :lexeme ~ OpKeywordChomp priority => 1 1905 | :lexeme ~ OpKeywordChop priority => 1 1906 | :lexeme ~ OpKeywordChown priority => 1 1907 | :lexeme ~ OpKeywordChr priority => 1 1908 | :lexeme ~ OpKeywordChroot priority => 1 1909 | :lexeme ~ OpKeywordClose priority => 1 1910 | :lexeme ~ OpKeywordClosedir priority => 1 1911 | :lexeme ~ OpKeywordConnect priority => 1 1912 | :lexeme ~ OpKeywordContinue priority => 1 1913 | :lexeme ~ OpKeywordCos priority => 1 1914 | :lexeme ~ OpKeywordCrypt priority => 1 1915 | :lexeme ~ OpKeywordDbmclose priority => 1 1916 | :lexeme ~ OpKeywordDbmopen priority => 1 1917 | :lexeme ~ OpKeywordDefined priority => 1 1918 | :lexeme ~ OpKeywordDelete priority => 1 1919 | :lexeme ~ OpKeywordDie priority => 1 1920 | :lexeme ~ OpKeywordDo priority => 1 1921 | :lexeme ~ OpKeywordDump priority => 1 1922 | :lexeme ~ OpKeywordEach priority => 1 1923 | :lexeme ~ OpKeywordEof priority => 1 1924 | :lexeme ~ OpKeywordEval priority => 1 1925 | :lexeme ~ OpKeywordEvalbytes priority => 1 1926 | :lexeme ~ OpKeywordExec priority => 1 1927 | :lexeme ~ OpKeywordExists priority => 1 1928 | :lexeme ~ OpKeywordExit priority => 1 1929 | :lexeme ~ OpKeywordExp priority => 1 1930 | :lexeme ~ OpKeywordFc priority => 1 1931 | :lexeme ~ OpKeywordFor priority => 1 1932 | :lexeme ~ OpKeywordForeach priority => 1 1933 | :lexeme ~ OpKeywordFcntl priority => 1 1934 | :lexeme ~ OpKeywordFileno priority => 1 1935 | :lexeme ~ OpKeywordFlock priority => 1 1936 | :lexeme ~ OpKeywordFork priority => 1 1937 | :lexeme ~ OpKeywordGetc priority => 1 1938 | :lexeme ~ OpKeywordGetlogin priority => 1 1939 | :lexeme ~ OpKeywordGetpeername priority => 1 1940 | :lexeme ~ OpKeywordGetpgrp priority => 1 1941 | :lexeme ~ OpKeywordGetppid priority => 1 1942 | :lexeme ~ OpKeywordGetpriority priority => 1 1943 | :lexeme ~ OpKeywordGetpwnam priority => 1 1944 | :lexeme ~ OpKeywordGetgrnam priority => 1 1945 | :lexeme ~ OpKeywordGethostbyname priority => 1 1946 | :lexeme ~ OpKeywordGetnetbyname priority => 1 1947 | :lexeme ~ OpKeywordGetprotobyname priority => 1 1948 | :lexeme ~ OpKeywordGetpwuid priority => 1 1949 | :lexeme ~ OpKeywordGetgrgid priority => 1 1950 | :lexeme ~ OpKeywordGetservbyname priority => 1 1951 | :lexeme ~ OpKeywordGethostbyaddr priority => 1 1952 | :lexeme ~ OpKeywordGetnetbyaddr priority => 1 1953 | :lexeme ~ OpKeywordGetprotobynumber priority => 1 1954 | :lexeme ~ OpKeywordGetservbyport priority => 1 1955 | :lexeme ~ OpKeywordGetpwent priority => 1 1956 | :lexeme ~ OpKeywordGetgrent priority => 1 1957 | :lexeme ~ OpKeywordGethostent priority => 1 1958 | :lexeme ~ OpKeywordGetnetent priority => 1 1959 | :lexeme ~ OpKeywordGetprotoent priority => 1 1960 | :lexeme ~ OpKeywordGetservent priority => 1 1961 | :lexeme ~ OpKeywordSetpwent priority => 1 1962 | :lexeme ~ OpKeywordSetgrent priority => 1 1963 | :lexeme ~ OpKeywordSethostent priority => 1 1964 | :lexeme ~ OpKeywordSetnetent priority => 1 1965 | :lexeme ~ OpKeywordSetprotoent priority => 1 1966 | :lexeme ~ OpKeywordSetservent priority => 1 1967 | :lexeme ~ OpKeywordEndpwent priority => 1 1968 | :lexeme ~ OpKeywordEndgrent priority => 1 1969 | :lexeme ~ OpKeywordEndhostent priority => 1 1970 | :lexeme ~ OpKeywordEndnetent priority => 1 1971 | :lexeme ~ OpKeywordEndprotoent priority => 1 1972 | :lexeme ~ OpKeywordEndservent priority => 1 1973 | :lexeme ~ OpKeywordGetsockname priority => 1 1974 | :lexeme ~ OpKeywordGetsockopt priority => 1 1975 | :lexeme ~ OpKeywordGlob priority => 1 1976 | :lexeme ~ OpKeywordGmtime priority => 1 1977 | :lexeme ~ OpKeywordGoto priority => 1 1978 | :lexeme ~ OpKeywordGrep priority => 1 1979 | :lexeme ~ OpKeywordHex priority => 1 1980 | :lexeme ~ OpKeywordIndex priority => 1 1981 | :lexeme ~ OpKeywordInt priority => 1 1982 | :lexeme ~ OpKeywordIoctl priority => 1 1983 | :lexeme ~ OpKeywordJoin priority => 1 1984 | :lexeme ~ OpKeywordKeys priority => 1 1985 | :lexeme ~ OpKeywordKill priority => 1 1986 | :lexeme ~ OpKeywordLast priority => 1 1987 | :lexeme ~ OpKeywordLc priority => 1 1988 | :lexeme ~ OpKeywordLcfirst priority => 1 1989 | :lexeme ~ OpKeywordLength priority => 1 1990 | :lexeme ~ OpKeywordLink priority => 1 1991 | :lexeme ~ OpKeywordListen priority => 1 1992 | :lexeme ~ OpKeywordLocal priority => 1 1993 | :lexeme ~ OpKeywordLocaltime priority => 1 1994 | :lexeme ~ OpKeywordLock priority => 1 1995 | :lexeme ~ OpKeywordLog priority => 1 1996 | :lexeme ~ OpKeywordLstat priority => 1 1997 | :lexeme ~ OpKeywordMap priority => 1 1998 | :lexeme ~ OpKeywordMkdir priority => 1 1999 | :lexeme ~ OpKeywordMsgctl priority => 1 2000 | :lexeme ~ OpKeywordMsgget priority => 1 2001 | :lexeme ~ OpKeywordMsgrcv priority => 1 2002 | :lexeme ~ OpKeywordMsgsnd priority => 1 2003 | :lexeme ~ OpKeywordMy priority => 1 2004 | :lexeme ~ OpKeywordNext priority => 1 2005 | :lexeme ~ OpKeywordNo priority => 1 2006 | :lexeme ~ OpKeywordOct priority => 1 2007 | :lexeme ~ OpKeywordOpen priority => 1 2008 | :lexeme ~ OpKeywordOpendir priority => 1 2009 | :lexeme ~ OpKeywordOrd priority => 1 2010 | :lexeme ~ OpKeywordOur priority => 1 2011 | :lexeme ~ OpKeywordPack priority => 1 2012 | :lexeme ~ OpKeywordPackage priority => 1 2013 | :lexeme ~ OpKeywordPipe priority => 1 2014 | :lexeme ~ OpKeywordPop priority => 1 2015 | :lexeme ~ OpKeywordPos priority => 1 2016 | :lexeme ~ OpKeywordPrint priority => 1 2017 | :lexeme ~ OpKeywordPrintf priority => 1 2018 | :lexeme ~ OpKeywordPrototype priority => 1 2019 | :lexeme ~ OpKeywordPush priority => 1 2020 | :lexeme ~ OpKeywordQuotemeta priority => 1 2021 | :lexeme ~ OpKeywordRand priority => 1 2022 | :lexeme ~ OpKeywordRead priority => 1 2023 | :lexeme ~ OpKeywordReaddir priority => 1 2024 | :lexeme ~ OpKeywordReadline priority => 1 2025 | :lexeme ~ OpKeywordReadlink priority => 1 2026 | :lexeme ~ OpKeywordReadpipe priority => 1 2027 | :lexeme ~ OpKeywordRecv priority => 1 2028 | :lexeme ~ OpKeywordRedo priority => 1 2029 | :lexeme ~ OpKeywordRef priority => 1 2030 | :lexeme ~ OpKeywordRename priority => 1 2031 | :lexeme ~ OpKeywordRequire priority => 1 2032 | :lexeme ~ OpKeywordReset priority => 1 2033 | :lexeme ~ OpKeywordReturn priority => 1 2034 | :lexeme ~ OpKeywordReverse priority => 1 2035 | :lexeme ~ OpKeywordRewinddir priority => 1 2036 | :lexeme ~ OpKeywordRindex priority => 1 2037 | :lexeme ~ OpKeywordRmdir priority => 1 2038 | :lexeme ~ OpKeywordSay priority => 1 2039 | :lexeme ~ OpKeywordScalar priority => 1 2040 | :lexeme ~ OpKeywordSeek priority => 1 2041 | :lexeme ~ OpKeywordSeekdir priority => 1 2042 | :lexeme ~ OpKeywordSelect priority => 1 2043 | :lexeme ~ OpKeywordSemctl priority => 1 2044 | :lexeme ~ OpKeywordSemget priority => 1 2045 | :lexeme ~ OpKeywordSemop priority => 1 2046 | :lexeme ~ OpKeywordSend priority => 1 2047 | :lexeme ~ OpKeywordSetpgrp priority => 1 2048 | :lexeme ~ OpKeywordSetpriority priority => 1 2049 | :lexeme ~ OpKeywordSetsockopt priority => 1 2050 | :lexeme ~ OpKeywordShift priority => 1 2051 | :lexeme ~ OpKeywordShmctl priority => 1 2052 | :lexeme ~ OpKeywordShmget priority => 1 2053 | :lexeme ~ OpKeywordShmread priority => 1 2054 | :lexeme ~ OpKeywordShmwrite priority => 1 2055 | :lexeme ~ OpKeywordShutdown priority => 1 2056 | :lexeme ~ OpKeywordSin priority => 1 2057 | :lexeme ~ OpKeywordSleep priority => 1 2058 | :lexeme ~ OpKeywordSocket priority => 1 2059 | :lexeme ~ OpKeywordSocketpair priority => 1 2060 | :lexeme ~ OpKeywordSort priority => 1 2061 | :lexeme ~ OpKeywordSplice priority => 1 2062 | :lexeme ~ OpKeywordSplit priority => 1 2063 | :lexeme ~ OpKeywordSprintf priority => 1 2064 | :lexeme ~ OpKeywordSqrt priority => 1 2065 | :lexeme ~ OpKeywordSrand priority => 1 2066 | :lexeme ~ OpKeywordStat priority => 1 2067 | :lexeme ~ OpKeywordState priority => 1 2068 | :lexeme ~ OpKeywordStudy priority => 1 2069 | :lexeme ~ OpKeywordSub priority => 1 2070 | :lexeme ~ OpKeywordSubstr priority => 1 2071 | :lexeme ~ OpKeywordSymlink priority => 1 2072 | :lexeme ~ OpKeywordSyscall priority => 1 2073 | :lexeme ~ OpKeywordSysopen priority => 1 2074 | :lexeme ~ OpKeywordSysread priority => 1 2075 | :lexeme ~ OpKeywordSysseek priority => 1 2076 | :lexeme ~ OpKeywordSystem priority => 1 2077 | :lexeme ~ OpKeywordSyswrite priority => 1 2078 | :lexeme ~ OpKeywordTell priority => 1 2079 | :lexeme ~ OpKeywordTelldir priority => 1 2080 | :lexeme ~ OpKeywordTie priority => 1 2081 | :lexeme ~ OpKeywordTied priority => 1 2082 | :lexeme ~ OpKeywordTime priority => 1 2083 | :lexeme ~ OpKeywordTimes priority => 1 2084 | :lexeme ~ OpKeywordTruncate priority => 1 2085 | :lexeme ~ OpKeywordUc priority => 1 2086 | :lexeme ~ OpKeywordUcfirst priority => 1 2087 | :lexeme ~ OpKeywordUmask priority => 1 2088 | :lexeme ~ OpKeywordUndef priority => 1 2089 | :lexeme ~ OpKeywordUnlink priority => 1 2090 | :lexeme ~ OpKeywordUnpack priority => 1 2091 | :lexeme ~ OpKeywordUnshift priority => 1 2092 | :lexeme ~ OpKeywordUntie priority => 1 2093 | :lexeme ~ OpKeywordUse priority => 1 2094 | :lexeme ~ OpKeywordUtime priority => 1 2095 | :lexeme ~ OpKeywordValues priority => 1 2096 | :lexeme ~ OpKeywordVec priority => 1 2097 | :lexeme ~ OpKeywordWait priority => 1 2098 | :lexeme ~ OpKeywordWaitpid priority => 1 2099 | :lexeme ~ OpKeywordWantarray priority => 1 2100 | :lexeme ~ OpKeywordWarn priority => 1 2101 | :lexeme ~ OpKeywordWrite priority => 1 2102 | 2103 | # OpAdd ("+") conflicts with OpUnary ("+") 2104 | # So when it's both, OpAdd should win 2105 | # (e.g., "sort $x + $y" should be OpAdd, not OpUnary) 2106 | :lexeme ~ OpAdd priority => 1 2107 | 2108 | }; 2109 | 2110 | sub add_lexemes { 2111 | my ( $class, @items ) = @_; 2112 | 2113 | foreach my $item (@items) { 2114 | ref $item eq 'ARRAY' 2115 | or die 'add_lexemes( [NAME, VALUE], [NAME, VALUE] )'; 2116 | 2117 | $grammar_source .= "$item->[0] ~ '$item->[1]'\n"; 2118 | } 2119 | } 2120 | 2121 | sub add_keyword { 2122 | my ( $class, $name, $type, $value, $rules_arrayref ) = @_; 2123 | $name && $type && $value && $rules_arrayref 2124 | or die 'add_keyword( NAME_STR, TYPE_STR, VALUE_STR, RULES_ARRAYREF )'; 2125 | 2126 | ref $rules_arrayref eq 'ARRAY' 2127 | or die 'rules must be an arrayref'; 2128 | 2129 | $type =~ /^( nullary | unary | assign | list )$/xms 2130 | or die 'Type must be "nullary", "unary", "assign", or "list"'; 2131 | 2132 | grep !length, $name, $type, $value 2133 | and die 'name, type, value must have length'; 2134 | 2135 | $name = ucfirst $name; # Just in case 2136 | $type = ucfirst $type; 2137 | 2138 | my $rules_string = join "\n | ", $rules_arrayref->@*; 2139 | 2140 | $grammar_source .= qq{ 2141 | OpKeyword$name ~ '$value' 2142 | OpKeyword${name}Expr ::= $rules_string 2143 | :lexeme ~ OpKeyword${name} priority => 1 2144 | }; 2145 | 2146 | my $type_rule = "Op${type}KeywordExpr"; 2147 | $grammar_source =~ s{($type_rule\s+::=)}{$1 OpKeyword${name}Expr | }xms; 2148 | } 2149 | 2150 | sub build_struct { 2151 | my ( $rec, $initial_valueref ) = @_; 2152 | my @values = ($initial_valueref); 2153 | 2154 | while ( my $valueref = shift @values ) { 2155 | if ( ! ref ${$valueref} ) { 2156 | ${$valueref} = { 2157 | 'name' => '', 2158 | 'type' => 'lexeme', 2159 | 'value' => ${$valueref}, 2160 | }; 2161 | 2162 | next; 2163 | } 2164 | 2165 | my $name = shift @{ ${$valueref} }; 2166 | my $start_pos = shift @{ ${$valueref} }; 2167 | my $length = shift @{ ${$valueref} }; 2168 | my @children = @{ ${$valueref} }; 2169 | 2170 | my ( $line, $column ) = $rec->line_column($start_pos); 2171 | ${$valueref} = { 2172 | 'name' => $name, 2173 | 'type' => 'rule', 2174 | 'start_pos' => $start_pos, 2175 | 'length' => $length, 2176 | 'children' => \@children, 2177 | 'line' => $line, 2178 | 'column' => $column, 2179 | }; 2180 | 2181 | push @values, map \$_, @{ ${$valueref}->{'children'} } 2182 | } 2183 | } 2184 | 2185 | sub parse { 2186 | my ($class, $text) = @_; 2187 | 2188 | state $grammar = Marpa::R2::Scanless::G->new({ source => \$grammar_source }); 2189 | 2190 | my %args = ( 2191 | 'grammar' => $grammar, 2192 | 2193 | DEBUG() 2194 | ? ( 2195 | 'trace_terminals' => 1, 2196 | 'trace_values' => 1, 2197 | ) 2198 | : (), 2199 | ); 2200 | 2201 | my $rec = Marpa::R2::Scanless::R->new( \%args ); 2202 | 2203 | my @values; 2204 | eval { 2205 | my $res = $rec->read( \$text ); 2206 | while ( my $value = $rec->value() ) { 2207 | build_struct( $rec, $value ); 2208 | push @values, $$value; 2209 | } 2210 | 1; 2211 | } 2212 | or do { 2213 | my $err = $@; 2214 | if (!@values) { 2215 | for my $nterm (reverse qw/Program BlockStatement Statement NonBraceExprComma BlockLevelExpression Expression SubCall VarIdentExpr SubNameExpr/) { 2216 | my ($start, $length) = $rec->last_completed($nterm); 2217 | next unless defined $start; 2218 | my $range = $rec->substring($start, $length); 2219 | my $expect = $rec->terminals_expected(); 2220 | my $progress = $rec->show_progress(); 2221 | die "$err\nFailed to parse past: $range (char $start, length $length), expected " . ( join ',', @{$expect} );# . "\n$progress"; 2222 | } 2223 | die "Failed to parse, dunno why."; 2224 | } 2225 | }; 2226 | 2227 | if (!@values) { 2228 | my ( $g1_start, $g1_length ) = $rec->last_completed('Program'); 2229 | die "Program could not be successfully parsed\n" if not defined $g1_start; 2230 | my $last_expression = $rec->substring( $g1_start, $g1_length ); 2231 | die "Last text successfully parsed was: $last_expression\n"; 2232 | } 2233 | 2234 | return @values; 2235 | } 2236 | 2237 | 1; 2238 | 2239 | __END__ 2240 | 2241 | =pod 2242 | 2243 | =head1 SYNOPSIS 2244 | 2245 | use Guacamole; 2246 | my ($ast) = Guacamole->parse($string); 2247 | 2248 | =head1 DESCRIPITON 2249 | 2250 | B is a Perl parser toolkit. 2251 | 2252 | It can: 2253 | 2254 | =over 4 2255 | 2256 | =item * Parse Standard Perl 2257 | 2258 | This is explained in this document. 2259 | 2260 | For B, see the next clause. 2261 | 2262 | =item * Check a file is written in Standard Perl 2263 | 2264 | This is done by L, which is where Standard Perl is described. 2265 | 2266 | =item * Lint your code 2267 | 2268 | See L. 2269 | 2270 | =item * Deparse your code 2271 | 2272 | See L. 2273 | 2274 | =item * Rewrite your code 2275 | 2276 | There is a proof-of-concept for this and we hope to provide this as a framework. 2277 | 2278 | =back 2279 | 2280 | =head1 Standard Perl 2281 | 2282 | Guacamole only works on Standard Perl. You can read about it here: L. 2283 | 2284 | =head1 Parser 2285 | 2286 | my ($ast) = Guacamole->parse($string); 2287 | 2288 | To parse a string, call L's C method. (This might turn to an 2289 | object-oriented interface in the future.) 2290 | 2291 | It returns a list of results. If it ever returns more than one, this is a bug that 2292 | means it couldn't ambiguously parse something. This will later be enforced in the 2293 | interface. The current interface is not official. 2294 | 2295 | =head2 AST Nodes 2296 | 2297 | Guacamole returns an AST with two types of nodes. 2298 | 2299 | my ($ast) = Guacamole->parse('$foo = 1'); 2300 | 2301 | The above will generate a larger AST than you imagine (which might be pruned 2302 | in the future). We'll focus on two types of nodes that will appear above. 2303 | 2304 | =head3 Rules 2305 | 2306 | Rules are the top level expressions. They include the definitions for rules. 2307 | They include information on location in the file, length, line, and column. 2308 | 2309 | $rule = { 2310 | 'children' => [...], 2311 | 'column' => 2, 2312 | 'length' => 3, 2313 | 'line' => 1, 2314 | 'name' => 'VarIdentExpr', 2315 | 'start_pos' => 1, 2316 | 'type' => 'rule', 2317 | }, 2318 | 2319 | This rule is a C which is an expression for a variable identity. 2320 | 2321 | In the code above, it refers to the C in C<$foo> - which is the identity 2322 | itself. 2323 | 2324 | It has one child, described below under C. 2325 | 2326 | =head3 Lexemes 2327 | 2328 | The child for the C rule should be the value of the identity. 2329 | 2330 | $lexeme = { 2331 | 'name' => '', 2332 | 'type' => 'lexeme', 2333 | 'value' => 'foo', 2334 | }; 2335 | 2336 | The C attribute for all lexemes is empty. This is to make it easy to 2337 | write code that checks for the value of a rule without having to check whether 2338 | it's a rule first. 2339 | 2340 | =head1 THANKS 2341 | 2342 | =over 4 2343 | 2344 | =item * Damian Conway 2345 | 2346 | For helping understand what is feasible, what isn't, and why, and for having 2347 | infinite patience in explaining these. 2348 | 2349 | =item * Jeffrey Kegler 2350 | 2351 | For L and helping understand how to use Marpa better. 2352 | 2353 | =item * Gonzalo Diethelm 2354 | 2355 | For continuous feedback and support. 2356 | 2357 | =item * H. Merijn Brand (@Tux) 2358 | 2359 | For providing the initial production-level test of Guacamole to 2360 | help shake many of the bugs in the BNF. 2361 | 2362 | =back 2363 | 2364 | =head1 SEE ALSO 2365 | 2366 | =over 4 2367 | 2368 | =item * L 2369 | 2370 | =item * L 2371 | 2372 | =item * L 2373 | 2374 | =back 2375 | 2376 | --------------------------------------------------------------------------------