Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
5665 lines (5094 sloc) 205 KB
use QRegex;
use NQPP6QRegex;
use NQPP5QRegex;
use Perl6::Actions;
use Perl6::World;
use Perl6::Pod;
role startstops[$start, $stop1, $stop2] {
token starter { $start }
token stopper { $stop1 | $stop2 }
}
role startstop[$start, $stop] {
token starter { $start }
token stopper { $stop }
}
role stop[$stop] {
token starter { <!> }
token stopper { $stop }
}
# This role captures things that STD factors out from any individual grammar,
# but that don't make sense to go in HLL::Grammar.
role STD {
token opener {
<[
\x0028 \x003C \x005B \x007B \x00AB \x0F3A \x0F3C \x169B \x2018 \x201A \x201B
\x201C \x201E \x201F \x2039 \x2045 \x207D \x208D \x2208 \x2209 \x220A \x2215
\x223C \x2243 \x2252 \x2254 \x2264 \x2266 \x2268 \x226A \x226E \x2270 \x2272
\x2274 \x2276 \x2278 \x227A \x227C \x227E \x2280 \x2282 \x2284 \x2286 \x2288
\x228A \x228F \x2291 \x2298 \x22A2 \x22A6 \x22A8 \x22A9 \x22AB \x22B0 \x22B2
\x22B4 \x22B6 \x22C9 \x22CB \x22D0 \x22D6 \x22D8 \x22DA \x22DC \x22DE \x22E0
\x22E2 \x22E4 \x22E6 \x22E8 \x22EA \x22EC \x22F0 \x22F2 \x22F3 \x22F4 \x22F6
\x22F7 \x2308 \x230A \x2329 \x23B4 \x2768 \x276A \x276C \x276E \x2770 \x2772
\x2774 \x27C3 \x27C5 \x27D5 \x27DD \x27E2 \x27E4 \x27E6 \x27E8 \x27EA \x2983
\x2985 \x2987 \x2989 \x298B \x298D \x298F \x2991 \x2993 \x2995 \x2997 \x29C0
\x29C4 \x29CF \x29D1 \x29D4 \x29D8 \x29DA \x29F8 \x29FC \x2A2B \x2A2D \x2A34
\x2A3C \x2A64 \x2A79 \x2A7D \x2A7F \x2A81 \x2A83 \x2A8B \x2A91 \x2A93 \x2A95
\x2A97 \x2A99 \x2A9B \x2AA1 \x2AA6 \x2AA8 \x2AAA \x2AAC \x2AAF \x2AB3 \x2ABB
\x2ABD \x2ABF \x2AC1 \x2AC3 \x2AC5 \x2ACD \x2ACF \x2AD1 \x2AD3 \x2AD5 \x2AEC
\x2AF7 \x2AF9 \x2E02 \x2E04 \x2E09 \x2E0C \x2E1C \x2E20 \x2E28 \x3008 \x300A
\x300C \x300E \x3010 \x3014 \x3016 \x3018 \x301A \x301D \xFD3E \xFE17 \xFE35
\xFE37 \xFE39 \xFE3B \xFE3D \xFE3F \xFE41 \xFE43 \xFE47 \xFE59 \xFE5B \xFE5D
\xFF08 \xFF1C \xFF3B \xFF5B \xFF5F \xFF62
]>
}
method balanced($start, $stop) {
if nqp::istype($stop, VMArray) {
self.HOW.mixin(self, startstops.HOW.curry(startstops, $start, $stop[0], $stop[1]));
}
else {
self.HOW.mixin(self, startstop.HOW.curry(startstop, $start, $stop));
}
}
method unbalanced($stop) {
self.HOW.mixin(self, stop.HOW.curry(stop, $stop));
}
token starter { <!> }
token stopper { <!> }
method quote_lang($l, $start, $stop, @base_tweaks?, @extra_tweaks?) {
sub lang_key() {
my $stopstr := nqp::istype($stop,VMArray) ?? nqp::join(' ',$stop) !! $stop;
my @keybits := [
self.HOW.name(self), $l.HOW.name($l), $start, $stopstr
];
for @base_tweaks {
@keybits.push($_);
}
for @extra_tweaks {
if $_[0] eq 'to' {
return 'NOCACHE';
}
@keybits.push($_[0] ~ '=' ~ $_[1]);
}
nqp::join("\0", @keybits)
}
sub con_lang() {
my $lang := $l.'!cursor_init'(self.orig(), :p(self.pos()), :shared(self.'!shared'()));
$lang.clone_braid_from(self);
for @base_tweaks {
$lang := $lang."tweak_$_"(1);
}
for @extra_tweaks {
my $t := $_[0];
if nqp::can($lang, "tweak_$t") {
$lang := $lang."tweak_$t"($_[1]);
}
else {
self.sorry("Unrecognized adverb: :$t");
}
}
for self.slangs {
if nqp::istype($lang, $_.value) {
$lang.set_actions(self.slang_actions($_.key));
last;
}
}
$lang.set_pragma("STOPPER",$stop);
nqp::istype($stop,VMArray) ||
$start ne $stop ?? $lang.balanced($start, $stop)
!! $lang.unbalanced($stop);
}
# Get language from cache or derive it.
my $key := lang_key();
my %quote_lang_cache := $*W.quote_lang_cache;
my $quote_lang := nqp::existskey(%quote_lang_cache, $key) && $key ne 'NOCACHE'
?? %quote_lang_cache{$key}
!! (%quote_lang_cache{$key} := con_lang());
$quote_lang.set_package(self.package);
$quote_lang;
}
token babble($l, @base_tweaks?) {
:my @extra_tweaks;
[ <quotepair> <.ws>
{
my $kv := $<quotepair>[-1].ast;
my $k := $kv.named;
if nqp::istype($kv, QAST::Stmts) || nqp::istype($kv, QAST::Stmt) && +@($kv) == 1 {
$kv := $kv[0];
}
my $v := nqp::istype($kv, QAST::IVal)
?? $kv.value
!! $kv.has_compile_time_value
?? $kv.compile_time_value
!! self.panic("Invalid adverb value for " ~ $<quotepair>[-1].Str);
nqp::push(@extra_tweaks, [$k, $v]);
}
]*
$<B>=[<?before .>]
{
# Work out the delimiters.
my $c := $/;
my @delims := $c.peek_delimiters($c.target, $c.pos);
my $start := @delims[0];
my $stop := @delims[1];
# Get the language.
my $lang := self.quote_lang($l, $start, $stop, @base_tweaks, @extra_tweaks);
$<B>.make([$lang, $start, $stop]);
}
}
my @herestub_queue;
my class Herestub {
has $!delim;
has $!orignode;
has $!grammar;
method delim() { $!delim }
method orignode() { $!orignode }
method grammar() { $!grammar }
}
role herestop {
token starter { <!> }
token stopper { ^^ {} $<ws>=(\h*) $*DELIM \h* $$ [\r\n | \v]? }
method parsing_heredoc() { 1 }
}
method heredoc () {
my $actions := self.actions;
if @herestub_queue {
my $here := self.'!cursor_start_cur'();
$here.'!cursor_pos'(self.pos);
while @herestub_queue {
my $herestub := nqp::shift(@herestub_queue);
my $*DELIM := $herestub.delim;
my $lang := $herestub.grammar.HOW.mixin($herestub.grammar, herestop);
for self.slangs {
if nqp::istype($lang, $_.value) {
$lang.set_actions(self.slang_actions($_.key));
last;
}
}
my $doc := $here.nibble($lang);
if $doc {
# Match stopper.
my $stop := $lang.'!cursor_init'(self.orig(), :p($doc.pos), :shared(self.'!shared'())).stopper();
$stop.clone_braid_from(self);
unless $stop {
self.panic("Ending delimiter $*DELIM not found");
}
$here.'!cursor_pos'($stop.pos);
# Get it trimmed and AST updated.
$actions.trim_heredoc(self, $doc, $stop, $herestub.orignode.MATCH.ast);
}
else {
self.panic("Ending delimiter $*DELIM not found");
}
}
$here.'!cursor_pass'($here.pos);
$here.set_actions($actions);
$here
}
else {
self
}
}
token cheat_heredoc {
<?{ +@herestub_queue }> \h* <[ ; } ]> \h* <?before \n | '#'> <.ws> <?MARKER('endstmt')>
}
method queue_heredoc($delim, $grammar) {
nqp::ifnull(@herestub_queue, @herestub_queue := []);
nqp::push(@herestub_queue, Herestub.new(:$delim, :$grammar, :orignode(self)));
return self;
}
method fail-terminator ($/, $start, $stop, $line?) {
my $message;
if $start ne nqp::chr(nqp::ord($start)) {
$message := "Starter $start is immediately followed by a combining codepoint. Please use {nqp::chr(nqp::ord($start))} without a combining glyph";
if $line {
$message := "$message ($start was at line $line)";
}
}
else {
$message := "Couldn't find terminator $stop";
if $line {
$message := "$message (corresponding $start was at line $line)";
}
}
$/.typed_panic('X::Comp::AdHoc',
payload => $message,
expected => [$stop]
);
}
# nibbler for q quoting
token quibble($l, *@base_tweaks) {
:my $lang;
:my $start;
:my $stop;
<babble($l, @base_tweaks)>
{ my $B := $<babble><B>.ast; $lang := $B[0]; $start := $B[1]; $stop := $B[2]; }
$start <nibble($lang)> [ $stop || { self.fail-terminator($/, $start, $stop, HLL::Compiler.lineof($<babble><B>.orig(), $<babble><B>.from(), :cache(1) )) } ]
{
nqp::can($lang, 'herelang') && self.queue_heredoc(
$*W.nibble_to_str($/, $<nibble>.ast[1], -> { "Stopper '" ~ $<nibble> ~ "' too complex for heredoc" }),
$lang.herelang)
}
}
# Note, $lang must carry its own actions by the time we call this.
method nibble($lang) {
$lang.'!cursor_init'(self.orig(), :p(self.pos()), :shared(self.'!shared'())).nibbler().set_braid_from(self)
}
token obsbrace { <.obs('curlies around escape argument','square brackets')> }
method FAILGOAL($goal, $dba?) {
my $stopper;
unless $dba {
$dba := nqp::getcodename(nqp::callercode());
# Handle special case to conceal variable name leaked by core grammar
if ~$goal eq '$stopper ' {
my $ch := $dba ~~ /[post]?circumfix\:sym[\<|\«]\S+\s+(\S+)[\>|\»]/;
$ch := ~$ch[0];
if nqp::chars($ch) {
$stopper := "'" ~ $ch ~ "'";
}
}
}
# core grammar also has a penchant for sending us trailing .ws contents
$stopper := $stopper // $goal;
$stopper := $stopper ~~ /(.*\S)\s*/;
$stopper := ~$stopper[0];
self.typed_panic('X::Comp::FailGoal', :$dba, :goal($stopper),
:line-real(HLL::Compiler.lineof(self.orig(), self.from(),
:cache(1))));
}
method panic(*@args) {
self.typed_panic('X::Comp::AdHoc', payload => nqp::join('', @args))
}
method sorry(*@args) {
self.typed_sorry('X::Comp::AdHoc', payload => nqp::join('', @args))
}
method worry(*@args) {
self.typed_worry('X::Comp::AdHoc', payload => nqp::join('', @args))
}
method typed_panic($type_str, *%opts) {
$*W.throw(self.MATCH(), nqp::split('::', $type_str), |%opts);
}
method typed_sorry($type_str, *%opts) {
if +@*SORROWS + 1 == $*SORRY_LIMIT {
$*W.throw(self.MATCH(), nqp::split('::', $type_str), |%opts);
}
else {
@*SORROWS.push($*W.typed_exception(self.MATCH(), nqp::split('::', $type_str), |%opts));
}
self
}
method typed_worry($type_str, *%opts) {
if self.pragma('worries') {
self.pragma('fatal')
?? self.typed_sorry($type_str, |%opts)
!! @*WORRIES.push($*W.typed_exception(
self.MATCH(), nqp::split('::', $type_str), |%opts));
}
self
}
method security($payload) {
self.typed_panic('X::SecurityPolicy::Eval', :$payload);
}
method malformed($what) {
self.typed_panic('X::Syntax::Malformed', :$what);
}
method missing_block($borg, $has_mystery) {
my $marked := self.MARKED('ws');
my $pos := $marked ?? $marked.from !! self.pos;
if $borg<block> {
self.'!clear_highwater'();
self.'!cursor_pos'($borg<block>.pos);
self.typed_sorry('X::Syntax::BlockGobbled', what => ($borg<name> // ''));
self.'!cursor_pos'($pos);
self.missing("block (apparently claimed by " ~ ($borg<name> ?? "'" ~ $borg<name> ~ "'" !! "expression") ~ ")");
} elsif $pos > 0 && nqp::eqat(self.orig(), '}', $pos - 1) {
self.missing("block (whitespace needed before curlies taken as a hash subscript?)");
} elsif $has_mystery {
self.missing("block (taken by some undeclared routine?)");
} else {
self.missing("block");
}
}
method missing($what) {
self.typed_panic('X::Syntax::Missing', :$what);
}
method NYI($feature) {
self.typed_panic('X::Comp::NYI', :$feature)
}
token experimental($feature) {
<?{ try $*W.find_symbol(['EXPERIMENTAL-' ~ nqp::uc($feature)]) }>
|| <.typed_panic('X::Experimental', :$feature)>
}
method EXPR_nonassoc($cur, $left, $right) {
self.typed_panic('X::Syntax::NonAssociative', :left(~$left), :right(~$right));
}
method EXPR_nonlistassoc($cur, $left, $right) {
self.typed_panic('X::Syntax::NonListAssociative', :left(~$left), :right(~$right));
}
# "when" arg assumes more things will become obsolete after Perl 6 comes out...
method obs($old, $new, $when = 'in Perl 6', :$ism = 'p5isms') {
unless $*LANG.pragma($ism) {
$*W.throw(self.MATCH(), ['X', 'Obsolete'],
old => $old,
replacement => $new,
when => $when,
);
}
self;
}
method obsvar($name, $identifier-name?) {
unless $*LANG.pragma('p5isms') {
$*W.throw(self.MATCH(), ['X', 'Syntax', 'Perl5Var'],
:$name, :$identifier-name);
}
self;
}
method sorryobs($old, $new, $when = 'in Perl 6') {
unless $*LANG.pragma('p5isms') {
$*W.throw(self.MATCH(), ['X', 'Obsolete'],
old => $old,
replacement => $new,
when => $when,
);
}
self;
}
method worryobs($old, $new, $when = 'in Perl 6') {
unless $*LANG.pragma('p5isms') {
self.typed_worry('X::Obsolete',
old => $old,
replacement => $new,
when => $when,
);
}
self;
}
method dupprefix($prefixes) {
self.typed_panic('X::Syntax::DuplicatedPrefix', :$prefixes);
}
method mark_variable_used($name) {
my $lex := $*W.cur_lexpad();
my %sym := $lex.symbol($name);
if %sym {
%sym<used> := 1;
}
else {
# Add mention-only record (used to poison outer
# usages and disambiguate hashes/blocks by use of
# $_ when $*IMPLICIT is in force).
my $au := $lex.ann('also_uses');
$lex.annotate('also_uses', $au := {}) unless $au;
$au{$name} := 1;
}
}
method check_variable($var) {
my $varast := $var.ast;
if nqp::istype($varast, QAST::Op) && $varast.op eq 'ifnull' {
$varast := $varast[0];
}
if !$*IN_DECL && nqp::istype($varast, QAST::Var) && $varast.scope eq 'lexical' {
my $name := $varast.name;
if $name ne '%_' && $name ne '@_' && !$*W.is_lexical($name) {
my $sigil := $var<sigil> || nqp::substr($name,0,1);
if $sigil ne '&' {
if !$*STRICT {
$*W.auto_declare_var($var);
}
else {
my @suggestions := $*W.suggest_lexicals($name);
my $package := self.package;
if nqp::can($package.HOW, 'get_attribute_for_usage') {
my $sigil := nqp::substr($name, 0, 1);
my $twigil := nqp::concat($sigil, '!');
my $basename := nqp::substr($name, 1, nqp::chars($name) - 1);
my $attrname := nqp::concat($twigil, $basename);
my $attribute := $package.HOW.get_attribute_for_usage($package, $attrname);
nqp::push(@suggestions, $attrname);
CATCH {}
}
$*W.throw($var, ['X', 'Undeclared'], symbol => $name, suggestions => @suggestions, precursor => '1');
}
}
else {
$var.add_mystery($name, $var.to, 'var');
}
}
else {
self.mark_variable_used($name);
}
}
if !$*IN_DECL && nqp::istype($varast, QAST::Op) && $varast.name eq '&DYNAMIC' {
my $lex := $*W.cur_lexpad();
if nqp::istype($varast[0], QAST::Want) && nqp::istype($varast[0][2], QAST::SVal) {
my $au := $lex.ann('also_uses');
$lex.annotate('also_uses', $au := {}) unless $au;
$au{$varast[0][2].value} := 1;
}
}
self
}
token RESTRICTED {
:my $r := $*RESTRICTED || "(not)";
[ <?{ $*RESTRICTED }> [ $ || <.security($*RESTRICTED)> ] ]?
<!>
}
}
grammar Perl6::Grammar is HLL::Grammar does STD {
#================================================================
# AMBIENT AND POD-COMMON CODE HANDLERS
#================================================================
my class SerializationContextId {
my $count := 0;
my $lock := NQPLock.new;
method next-id() {
$lock.protect({ $count++ })
}
}
method TOP() {
# Language braid.
my $*LANG := self;
my $*LEAF := self; # the leaf cursor, workaround for when we can't pass via $/ into world
self.define_slang('MAIN', self.WHAT, self.actions);
self.define_slang('Quote', Perl6::QGrammar, Perl6::QActions);
self.define_slang('Regex', Perl6::RegexGrammar, Perl6::RegexActions);
self.define_slang('P5Regex', Perl6::P5RegexGrammar, Perl6::P5RegexActions);
self.define_slang('Pod', Perl6::PodGrammar, Perl6::PodActions);
# Old language braid, going away eventually
# XXX TODO: if these are going out, be sure to make similar change
# to src/perl6-debug.nqp and ensure it still works.
my %*LANG;
%*LANG<Regex> := Perl6::RegexGrammar;
%*LANG<Regex-actions> := Perl6::RegexActions;
%*LANG<P5Regex> := Perl6::P5RegexGrammar;
%*LANG<P5Regex-actions> := Perl6::P5RegexActions;
%*LANG<Quote> := Perl6::QGrammar;
%*LANG<Quote-actions> := Perl6::QActions;
%*LANG<MAIN> := self.WHAT;
%*LANG<MAIN-actions> := self.actions;
# We could start out TOP with a fatalizing language in self, conceivably...
my $*FATAL := self.pragma('fatal'); # also set if somebody calls 'use fatal' in mainline
self.set_pragma('worries', 1);
# A cacheable false dynvar value.
my $*WANTEDOUTERBLOCK := 0;
# Package declarator to meta-package mapping. Starts pretty much empty;
# we get the mappings either imported or supplied by the setting. One
# issue is that we may have no setting to provide them, e.g. when we
# compile the setting, but it still wants some kinda package. We just
# fudge in knowhow for that.
self.set_how('knowhow', nqp::knowhow());
self.set_how('package', nqp::knowhow());
# Will we use the result of this? (Yes for EVAL and REPL).
my $*NEED_RESULT := nqp::existskey(%*COMPILING<%?OPTIONS>, 'outer_ctx');
# Symbol table and serialization context builder - keeps track of
# objects that cross the compile-time/run-time boundary that are
# associated with this compilation unit.
my $file := nqp::getlexdyn('$?FILES');
my $source_id := nqp::sha1($file ~ (
nqp::defined(%*COMPILING<%?OPTIONS><outer_ctx>)
?? self.target() ~ SerializationContextId.next-id()
!! self.target()));
my $outer_world := nqp::getlexdyn('$*W');
my $is_nested := (
$outer_world
&& nqp::defined(%*COMPILING<%?OPTIONS><outer_ctx>)
&& $outer_world.is_precompilation_mode()
);
my $*W := $is_nested
?? $outer_world.create_nested()
!! nqp::isnull($file)
?? Perl6::World.new(:handle($source_id))
!! Perl6::World.new(:handle($source_id), :description($file));
unless $is_nested {
$*W.add_initializations();
}
my $cursor := self.comp_unit;
$*W.pop_lexpad(); # UNIT
$*W.pop_lexpad(); # UNIT_OUTER
$cursor;
}
## Lexer stuff
token apostrophe {
<[ ' \- ]>
}
token identifier {
<.ident> [ <.apostrophe> <.ident> ]*
}
token name {
[
| <identifier> <morename>*
| <morename>+
]
}
token morename {
:my $*QSIGIL := '';
'::'
[
|| <?before '(' | <.alpha> >
[
| <identifier>
| :dba('indirect name') '(' ~ ')' [ <.ws> <EXPR> ]
]
|| <?before '::'> <.typed_panic: "X::Syntax::Name::Null">
|| $<bad>=[<.sigil><.identifier>] { my str $b := $<bad>; self.malformed("lookup of ::$b; please use ::('$b'), ::\{'$b'\}, or ::<$b>") }
]?
}
token longname {
<name> {} [ <?before ':' <.+alpha+[\< \[ \« ]>> <!RESTRICTED> <colonpair> ]*
}
token deflongname {
:dba('new name to be defined')
<name> <colonpair>*
}
token subshortname {
<desigilname>
}
token sublongname {
<subshortname> <sigterm>?
}
token deftermnow { <defterm> }
token defterm { # XXX this is probably too general
:dba('new term to be defined')
<identifier>
[
| <colonpair>+
{
if $<colonpair>[0]<coloncircumfix> -> $cf {
my $category := $<identifier>.Str;
my $opname := $cf<circumfix>
?? $*W.colonpair_nibble_to_str($/, $cf<circumfix><nibble>)
!! '';
my $canname := $category ~ $*W.canonicalize_pair('sym', $opname);
my $termname := $category ~ $*W.canonicalize_pair('', $opname);
$/.add_categorical($category, $opname, $canname, $termname, :defterm);
}
}
| <?>
]
}
token module_name {
<longname>
[ <?[[]> :dba('generic role') '[' ~ ']' <arglist> ]?
}
token end_keyword {
» <!before <.[ \( \\ ' \- ]> || \h* '=>'>
}
token end_prefix {
<.end_keyword> \s*
}
token spacey { <?[\s#]> }
token kok {
<.end_keyword>
[
|| <?before <.[ \s \# ]> > <.ws>
|| <?{
my $n := nqp::substr(self.orig, self.from, self.pos - self.from);
$*W.is_name([$n]) || $*W.is_name(['&' ~ $n])
?? False
!! self.panic("Whitespace required after keyword '$n'")
}>
]
}
token tok {
<.end_keyword>
<!{
my $n := nqp::substr(self.orig, self.from, self.pos - self.from);
$*W.is_name([$n]) || $*W.is_name(['&' ~ $n])
}>
}
token ENDSTMT {
[
| \h* $$ <.ws> <?MARKER('endstmt')>
| <.unv>? $$ <.ws> <?MARKER('endstmt')>
]?
}
# ws is highly performance sensitive. So, we check if we already marked it
# at this point with a simple method, and only if that is not the case do
# we bother doing any pattern matching.
method ws() {
self.MARKED('ws') ?? self !! self._ws()
}
token _ws {
:my $old_highexpect := self.'!fresh_highexpect'();
:dba('whitespace')
<!ww>
[
| [\r\n || \v] <.heredoc>
| <.unv>
| <.unsp>
]*
<?MARKER('ws')>
:my $stub := self.'!fresh_highexpect'();
}
token unsp {
\\ <?before \s | '#'>
:dba('unspace')
[
| <.vws>
| <.unv>
| <.unsp>
]*
}
token vws {
:dba('vertical whitespace')
[
[
| \v
| '<<<<<<<' {} <?before [.*? \v '=======']: .*? \v '>>>>>>>' > <.sorry: 'Found a version control conflict marker'> \V* \v
| '=======' {} .*? \v '>>>>>>>' \V* \v # ignore second half
]
]+
}
token unv {
:dba('horizontal whitespace')
[
| \h+
| \h* <.comment>
| <?before \h* '=' [ \w | '\\'] > ^^ <.pod_content_toplevel>
]
}
token install_doc_phaser { <?> }
token vnum {
\w+ | '*'
}
token version {
<?before v\d+\w*> 'v' $<vstr>=[<vnum>+ % '.' '+'?]
<!before '-'|\'> # cheat because of LTM fail
}
## Top-level rules
token comp_unit {
# From STD.pm.
:my $*LEFTSIGIL; # sigil of LHS for item vs list assignment
:my $*SCOPE := ''; # which scope declarator we're under
:my $*MULTINESS := ''; # which multi declarator we're under
:my $*QSIGIL := ''; # sigil of current interpolation
:my $*IN_META := ''; # parsing a metaoperator like [..]
:my $*IN_REDUCE := 0; # attempting to parse an [op] construct
:my $*IN_DECL; # what declaration we're in
:my $*IN_RETURN := 0; # are we in a return?
:my $*HAS_SELF := ''; # is 'self' available? (for $.foo style calls)
:my $*begin_compunit := 1; # whether we're at start of a compilation unit
:my $*DECLARAND; # the current thingy we're declaring, and subject of traits
:my $*CODE_OBJECT; # the code object we're currently inside
:my $*METHODTYPE; # the current type of method we're in, if any
:my $*PKGDECL; # what type of package we're in, if any
:my %*MYSTERY; # names we assume may be post-declared functions
:my $*BORG := {}; # who gets blamed for a missing block
:my $*CCSTATE := '';
:my $*STRICT;
:my $*INVOCANT_OK := 0;
:my $*INVOCANT;
:my $*ARG_FLAT_OK := 0;
:my $*WHENEVER_COUNT := -1; # -1 indicates whenever not valid here
# Error related. There are three levels: worry (just a warning), sorry
# (fatal but not immediately so) and panic (immediately deadly). There
# is a limit on the number of sorrows also. Unlike STD, which emits the
# textual messages as it goes, we keep track of the exception objects
# and, if needed, make a composite exception group.
:my @*WORRIES; # exception objects resulting from worry
:my @*SORROWS; # exception objects resulting from sorry
:my $*SORRY_LIMIT := 10; # when sorrow turns to panic
# Extras.
:my @*NQP_VIOLATIONS; # nqp::ops per line number
:my %*HANDLERS; # block exception handlers
:my $*IMPLICIT; # whether we allow an implicit param
:my $*HAS_YOU_ARE_HERE := 0; # whether {YOU_ARE_HERE} has shown up
:my $*OFTYPE;
:my $*VMARGIN := 0; # pod stuff
:my $*ALLOW_INLINE_CODE := 0; # pod stuff
:my $*POD_IN_CODE_BLOCK := 0; # pod stuff
:my $*POD_IN_FORMATTINGCODE := 0; # pod stuff
:my $*POD_ALLOW_FCODES := 0b11111111111111111111111111; # allow which fcodes?
:my $*POD_ANGLE_COUNT := 0; # pod stuff
:my $*IN_REGEX_ASSERTION := 0;
:my $*IN_PROTO := 0; # are we inside a proto?
:my $*NEXT_STATEMENT_ID := 1; # to give each statement an ID
:my $*IN_STMT_MOD := 0; # are we inside a statement modifier?
:my $*COMPILING_CORE_SETTING := 0; # are we compiling CORE.setting?
# TODO XXX: see https://github.com/rakudo/rakudo/issues/2432
:my $*SET_DEFAULT_LANG_VER := 1;
:my %*SIG_INFO; # information about recent signature
:my $*CAN_LOWER_TOPIC := 1; # true if we optimize the $_ lexical away
# Various interesting scopes we'd like to keep to hand.
:my $*GLOBALish;
:my $*PACKAGE;
:my $*UNIT;
:my $*UNIT_OUTER;
:my $*EXPORT;
# stack of packages, which the 'is export' needs
:my @*PACKAGES := [];
# A place for Pod
:my $*POD_BLOCKS := [];
:my $*POD_BLOCKS_SEEN := {};
:my $*POD_PAST;
:my $*DECLARATOR_DOCS;
:my $*PRECEDING_DECL; # for #= comments
:my $*PRECEDING_DECL_LINE := -1; # XXX update this when I see another comment like it?
# TODO use these vars to implement S26 pod data block handling
:my $*DATA-BLOCKS := [];
:my %*DATA-BLOCKS := {};
# Quasis and unquotes
:my $*IN_QUASI := 0; # whether we're currently in a quasi block
:my $*MAIN := 'MAIN';
# performance improvement stuff
:my $*FAKE_INFIX_FOUND := 0;
# for runaway detection
:my $*LASTQUOTE := [0,0];
{
nqp::getcomp('perl6').reset_language_version();
$*W.loading_and_symbol_setup($/)
}
<.bom>?
<lang-version>
<.finishpad>
<statementlist=.FOREIGN_LANG($*MAIN, 'statementlist', 1)>
<.install_doc_phaser>
[ $ || <.typed_panic: 'X::Syntax::Confused'> ]
<.explain_mystery>
<.cry_sorrows>
{ $*W.mop_up_and_check($/) }
}
method clonecursor() {
my $new := self.'!cursor_init'(
self.orig(),
:p(self.pos()),
:shared(self.'!shared'()),
:braid(self."!braid"()."!clone"()));
$new;
}
rule lang-version {
:my $comp := nqp::getcomp('perl6');
[
<.ws>? 'use' <version> {} # <-- update $/ so we can grab $<version>
# we parse out the numeral, since we could have "6d"
:my $version := nqp::radix(10,$<version><vnum>[0],0,0)[0];
[
|| <?{ $version == 6 }> { $*W.load-lang-ver: $<version>, $comp }
|| { $/.typed_panic: 'X::Language::Unsupported',
version => ~$<version> }
]
|| {
# This is the path we take when the user did not
# provide any `use v6.blah` lang version statement
$*W.load-lang-ver: 'v6', $comp if $*SET_DEFAULT_LANG_VER;
}
]
}
rule statementlist($*statement_level = 0) {
:my $*LANG;
:my $*LEAF;
:my %*LANG := self.shallow_copy(self.slangs); # XXX deprecated
:my $*STRICT := nqp::getlexdyn('$*STRICT');
:dba('statement list')
# <.check_LANG_oopsies('statementlist')>
<.ws>
# Define this scope to be a new language.
<!!{ $*LANG := $*LEAF := $/.clone_braid_from(self); 1 }>
[
| $
| <?before <.[\)\]\}]>>
| [ <statement> <.eat_terminator> ]*
]
<.set_braid_from(self)> # any language tweaks must not escape
<!!{ nqp::rebless($/, self.WHAT); 1 }>
}
method shallow_copy(%hash) {
my %result;
for %hash {
%result{$_.key} := $_.value;
}
%result
}
rule semilist {
:dba('list composer')
''
[
| <?before <.[)\]}]> >
| [<statement><.eat_terminator> ]*
]
}
rule sequence {
:dba('sequence of statements')
''
[
| <?before <.[)\]}]> >
| [<statement><.eat_terminator> ]*
]
}
token label {
<identifier> ':' <?[\s]> <.ws>
{
$*LABEL := ~$<identifier>;
if $*W.already_declared('my', self.package, $*W.cur_lexpad(), [$*LABEL]) {
$*W.throw($/, ['X', 'Redeclaration'], symbol => $*LABEL);
}
my str $orig := self.orig();
my int $total := nqp::chars($orig);
my int $from := self.MATCH.from();
my int $to := self.MATCH.to() + nqp::chars($*LABEL);
my int $line := HLL::Compiler.lineof($orig, self.from(), :cache(1));
my str $prematch := nqp::substr($orig, $from > 20 ?? $from - 20 !! 0, $from > 20 ?? 20 !! $from);
my str $postmatch := nqp::substr($orig, $to, 20);
my $label := $*W.find_symbol(['Label']).new( :name($*LABEL), :$line, :$prematch, :$postmatch );
$*W.add_object_if_no_sc($label);
$*W.install_lexical_symbol($*W.cur_lexpad(), $*LABEL, $label);
}
}
token statement($*LABEL = '') {
:my $*QSIGIL := '';
:my $*SCOPE := '';
# NOTE: annotations that use STATEMENT_ID often also need IN_STMT_MOD annotation, in order
# to correctly migrate QAST::Blocks in constructs inside topics of statement modifiers
:my $*STATEMENT_ID := $*NEXT_STATEMENT_ID++;
:my $*IN_STMT_MOD := nqp::getlexdyn('$*IN_STMT_MOD');
:my $*ESCAPEBLOCK := 0;
:my $actions := self.slang_actions('MAIN');
<!!{ $/.set_actions($actions); 1 }>
<!before <.[\])}]> | $ >
<!stopper>
<!!{ nqp::rebless($/, self.slang_grammar('MAIN')); 1 }>
[
| <label> <statement($*LABEL)> { $*LABEL := '' if $*LABEL }
| <statement_control>
| <EXPR> :dba('statement end') { $*IN_STMT_MOD := 1 }
[
|| <?MARKED('endstmt')>
|| :dba('statement modifier') <.ws> <statement_mod_cond> <statement_mod_loop>?
|| :dba('statement modifier loop') <.ws> <statement_mod_loop>
{
my $sp := $<EXPR><statement_prefix>;
if $sp && $sp<sym> eq 'do' {
my $s := $<statement_mod_loop><sym>;
$/.obs("do..." ~ $s, "repeat...while or repeat...until")
unless $*LANG.pragma('p5isms');
}
}
]?
| <?[;]>
| <?stopper>
| {} <.panic: "Bogus statement">
]
}
token eat_terminator {
|| ';'
|| <?MARKED('endstmt')> <.ws>
|| <?before ')' | ']' | '}' >
|| $
|| <?stopper>
|| <?before [if|while|for|loop|repeat|given|when] » > { $/.'!clear_highwater'(); self.typed_panic( 'X::Syntax::Confused', reason => "Missing semicolon" ) }
|| { $/.typed_panic( 'X::Syntax::Confused', reason => "Confused" ) }
}
# Options for xblock/block implicit topic.
my $PBLOCK_NO_TOPIC := 0;
my $PBLOCK_OPTIONAL_TOPIC := 1;
my $PBLOCK_REQUIRED_TOPIC := 2;
token xblock($*IMPLICIT = $PBLOCK_NO_TOPIC) {
:my $*GOAL := '{';
:my $*BORG := {};
<EXPR> <.ws> <pblock($*IMPLICIT)>
}
token pblock($*IMPLICIT = $PBLOCK_NO_TOPIC) {
:my $*DECLARAND := $*W.stub_code_object('Block');
:my $*CODE_OBJECT := $*DECLARAND;
:my $*SIG_OBJ;
:my %*SIG_INFO;
:my $*POD_BLOCK;
:my $*DOC := $*DECLARATOR_DOCS;
:my $*LINE_NO := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
:my $*FATAL := self.pragma('fatal'); # can also be set inside statementlist
{
$*DECLARATOR_DOCS := '';
if $*PRECEDING_DECL_LINE < $*LINE_NO {
$*PRECEDING_DECL_LINE := $*LINE_NO;
$*PRECEDING_DECL := $*DECLARAND;
}
}
<.attach_leading_docs>
:dba('block or pointy block')
:my $borg := $*BORG;
:my $has_mystery := $*MYSTERY ?? 1 !! 0;
{ $*BORG := {} }
[
| <lambda>
<.newpad>
:my $*SCOPE := 'my';
:my $*GOAL := '{';
<signature> {
%*SIG_INFO := $<signature>.ast;
$*SIG_OBJ := $*W.create_signature_and_params($<signature>,
%*SIG_INFO, $*W.cur_lexpad(), 'Mu', :rw($<lambda> eq '<->'));
}
<blockoid>
| <?[{]>
<.newpad>
<blockoid>
|| <.missing_block($borg, $has_mystery)>
]
}
token lambda { '->' | '<->' }
token block($*IMPLICIT = 0) {
:my $*DECLARAND := $*W.stub_code_object('Block');
:my $*CODE_OBJECT := $*DECLARAND;
:dba('scoped block')
:my $borg := $*BORG;
:my $has_mystery := $*MYSTERY ?? 1 !! 0;
:my $*FATAL := self.pragma('fatal'); # can also be set inside statementlist
{ $*BORG := {} }
[ <?[{]> || <.missing_block($borg, $has_mystery)>]
<.newpad>
<blockoid>
}
token blockoid {
:my $*CURPAD;
:my %*HANDLERS;
<.finishpad>
:my $borg := $*BORG;
:my $has_mystery := $*MYSTERY ?? 1 !! 0;
{ $*BORG := {} }
[
| '{YOU_ARE_HERE}' <you_are_here>
| :dba('block')
'{'
<!!{ $*VARIABLE := '' if $*VARIABLE; 1 }>
<statementlist(1)>
[<.cheat_heredoc> || '}']
<?ENDSTMT>
|| <.missing_block($borg, $has_mystery)>
]
{ $*CURPAD := $*W.pop_lexpad() }
}
token unitstart { <?> }
token you_are_here {
<?{ nqp::getlexdyn('$?FILES') ~~ /\.setting$/ }> ||
{ self.typed_panic('X::Syntax::Reserved',
reserved => 'use of {YOU_ARE_HERE} outside of a setting',
instead => ' (use whitespace if not a setting, or rename file with .setting extension?)');
}
}
token newpad { <?> { $*W.push_lexpad($/) } }
token newthunk { <?> { $*W.push_thunk($/) } }
token finishpad { <?> }
token bom { \xFEFF }
proto token terminator { <...> }
token terminator:sym<;> { <?[;]> }
token terminator:sym<)> { <?[)]> }
token terminator:sym<]> { <?[\]]> }
token terminator:sym<}> { <?[}]> }
token terminator:sym<ang> { <?[>]> <?{ $*IN_REGEX_ASSERTION }> }
token terminator:sym<if> { 'if' <.kok> }
token terminator:sym<unless> { 'unless' <.kok> }
token terminator:sym<while> { 'while' <.kok> }
token terminator:sym<until> { 'until' <.kok> }
token terminator:sym<for> { 'for' <.kok> }
token terminator:sym<given> { 'given' <.kok> }
token terminator:sym<when> { 'when' <.kok> }
token terminator:sym<with> { 'with' <.kok> }
token terminator:sym<without> { 'without' <.kok> }
token terminator:sym<arrow> { '-->' }
token stdstopper {
[
|| <?MARKED('endstmt')> <?>
|| [
| <?terminator>
| $
]
]
}
## Statement control
proto rule statement_control { <...> }
rule statement_control:sym<if> {
$<sym>=[if|with]<.kok> {}
<xblock(~$<sym>[0] ~~ /with/ ?? $PBLOCK_REQUIRED_TOPIC !! $PBLOCK_NO_TOPIC)>
[
[
| 'else'\h*'if' <.typed_panic: 'X::Syntax::Malformed::Elsif'>
| 'elif' { $/.typed_panic('X::Syntax::Malformed::Elsif', what => "elif") }
| $<sym>='elsif' <xblock>
| $<sym>='orwith' <xblock($PBLOCK_REQUIRED_TOPIC)>
]
]*
{}
[
'else'
<else=.pblock(~$<sym>[-1] ~~ /with/ ?? $PBLOCK_REQUIRED_TOPIC !! $PBLOCK_NO_TOPIC)>
]?
}
rule statement_control:sym<unless> {
$<sym>='unless'<.kok>
<xblock($PBLOCK_NO_TOPIC)> # 0 means we're not parsing `without`
[ <!before [els[e|if]|orwith]» >
|| $<wrong-keyword>=[els[e|if]|orwith]» {}
<.typed_panic: 'X::Syntax::UnlessElse',
keyword => ~$<wrong-keyword>,
>
]
}
rule statement_control:sym<without> {
$<sym>='without'<.kok>
<xblock($PBLOCK_REQUIRED_TOPIC)> # 1 means we're not parsing `unless`
[ <!before [els[e|if]|orwith]» >
|| $<wrong-keyword>=[els[e|if]|orwith]» {}
<.typed_panic: 'X::Syntax::WithoutElse',
keyword => ~$<wrong-keyword>,
>
]
}
rule statement_control:sym<while> {
$<sym>=[while|until]<.kok> {}
<xblock>
}
rule statement_control:sym<repeat> {
<sym><.kok> {}
[
| $<wu>=[while|until]<.kok> <xblock>
| <pblock>
[$<wu>=['while'|'until']<.kok> || <.missing('"while" or "until"')>]
<EXPR>
]
}
rule statement_control:sym<for> {
<sym><.kok> {}
[ <?before 'my'? '$'\w+\s+'(' >
<.typed_panic: 'X::Syntax::P5'> ]?
[ <?before '(' <.EXPR>? ';' <.EXPR>? ';' <.EXPR>? ')' >
<.obs('C-style "for (;;)" loop', '"loop (;;)"')> ]?
<xblock($PBLOCK_REQUIRED_TOPIC)>
}
rule statement_control:sym<whenever> {
<sym><.kok>
[
|| <?{
nqp::getcomp('perl6').language_version eq '6.c'
|| $*WHENEVER_COUNT >= 0
}>
|| <.typed_panic('X::Comp::WheneverOutOfScope')>
]
{ $*WHENEVER_COUNT++ }
<xblock($PBLOCK_REQUIRED_TOPIC)>
}
rule statement_control:sym<foreach> {
<sym><.end_keyword> <.obs("'foreach'", "'for'")>
}
token statement_control:sym<loop> {
<sym><.kok>
:s''
[ '('
[
<e1=.EXPR>? ';' <e2=.EXPR>? ';' <e3=.EXPR>?
|| <.malformed('loop spec')>
]
')' ]?
<block>
}
rule statement_control:sym<need> {
<sym>
[
| <version> <.sorry('In case of using pragma, use "use" instead (e.g., "use v6;", "use v6.c;").')>
| <module_name>
]+ % ','
{
for $<module_name> {
my $lnd := $*W.dissect_longname($_<longname>);
my $name := $lnd.name;
my %cp := $lnd.colonpairs_hash('need');
$*W.load_module($/, $name, %cp, $*W.cur_lexpad);
}
}
}
token statement_control:sym<import> {
:my $*IN_DECL := 'import';
<sym> <.ws>
<module_name> [ <.spacey> <arglist> ]? <.ws>
:my $*HAS_SELF := '';
{
my $longname := $*W.dissect_longname($<module_name><longname>);
my $module;
my $found := 0;
try {
$module := $*W.find_symbol($longname.components());
$found := 1;
}
if $found {
# todo: fix arglist
$*W.do_import($/, $*W.find_symbol(<CompUnit Handle>).from-unit($module.WHO), $longname.name, $*W.arglist($/));
}
else {
$/.panic("Could not find module " ~ ~$<module_name> ~
" to import symbols from");
}
}
}
token statement_control:sym<no> {
:my $*IN_DECL := 'no';
:my $longname;
<sym> <.ws>
[
| <module_name> [ <.spacey> <arglist> ]? <.explain_mystery> <.cry_sorrows>
{ $*W.do_pragma_or_load_module($/,0) }
]
<.ws>
}
token statement_control:sym<use> {
:my $longname;
:my $*IN_DECL := 'use';
:my $*HAS_SELF := '';
:my $*SCOPE := 'use';
:my $OLD_MAIN := ~$*MAIN;
:my %*MYSTERY;
$<doc>=[ 'DOC' \h+ ]**0..1
<sym> <.ws>
[
| <version>
{ $/.typed_panic: 'X::Language::TooLate', version => ~$<version> }
| <module_name>
[
|| <.spacey> <arglist> <.cheat_heredoc>? <?{ $<arglist><EXPR> }> <.explain_mystery> <.cry_sorrows>
{
my $oldmain := %*LANG<MAIN>;
$*W.do_pragma_or_load_module($/,1);
:= $*LANG;
if nqp::istype($oldmain, %*LANG<MAIN>.WHAT) {
%*LANG := self.shallow_copy($*LANG.slangs);
}
else {
$/.check_LANG_oopsies('use');
}
}
|| {
unless ~$<doc> && !%*COMPILING<%?OPTIONS><doc> {
my $oldmain := %*LANG<MAIN>;
# CATCH {
# nqp::say("Died doing '" ~ $/ ~ "' with:");
# nqp::rethrow($_);
# }
$*W.do_pragma_or_load_module($/,1);
:= $*LANG;
if nqp::istype($oldmain, %*LANG<MAIN>.WHAT) {
%*LANG := self.shallow_copy($*LANG.slangs);
}
else {
$/.check_LANG_oopsies('use');
}
}
}
]
]
[ <?{ $*MAIN ne $OLD_MAIN }>
<.eat_terminator>
<statementlist=.FOREIGN_LANG($*MAIN, 'statementlist', 1)>
|| <?> ]
<.ws>
}
# This is like HLL::Grammar.LANG but it allows to call a token of a Perl 6 level grammar.
method FOREIGN_LANG($langname, $regex, *@args) {
my $grammar := self.slang_grammar($langname);
if nqp::istype($grammar, NQPMatch) {
self.LANG($langname, $regex, @args);
}
else {
my $Str := $*W.find_symbol(['Str']);
my $actions := self.slang_actions($langname);
my $lang_cursor := $grammar.'!cursor_init'($Str.new( :value(self.orig())), :p(self.pos()));
$lang_cursor.clone_braid_from(self);
$lang_cursor.set_actions($actions);
if self.HOW.traced(self) {
$lang_cursor.HOW.trace-on($lang_cursor, self.HOW.trace_depth(self));
}
my $ret := $lang_cursor."$regex"(|@args);
# Build up something NQP-levelish we can return.
my $new := NQPMatch.'!cursor_init'(self.orig(), :p(self.pos()), :shared(self.'!shared'()));
my $p6cursor := $*W.find_symbol(['Match']);
nqp::bindattr_i($new, NQPMatch, '$!from', nqp::getattr_i($ret, $p6cursor, '$!from'));
nqp::bindattr_i($new, NQPMatch, '$!pos', nqp::getattr_i($ret, $p6cursor, '$!pos'));
my str $p6c_name := nqp::getattr_s($ret, $p6cursor, '$!name');
if !nqp::isnull_s($p6c_name) {
nqp::bindattr($new, NQPMatch, '$!name', $p6c_name);
}
nqp::bindattr($new, NQPMatch, '$!made', nqp::getattr($ret, $p6cursor, '$!made'));
$new.MATCH;
$new.set_braid_from(self)
}
}
rule statement_control:sym<require> {
<sym>
[
| <module_name>
| <file=.variable>
| <!sigil> <file=.term>
]
<EXPR>?
}
rule statement_control:sym<given> {
<sym><.kok> <xblock($PBLOCK_REQUIRED_TOPIC)>
}
rule statement_control:sym<when> {
<sym><.kok> <xblock>
}
rule statement_control:sym<default> {
<sym><.kok> <block>
}
rule statement_control:sym<CATCH> {<sym> <block(1)> }
rule statement_control:sym<CONTROL> {<sym> <block(1)> }
rule statement_control:sym<QUIT> {<sym> <block(1)> }
proto token statement_prefix { <...> }
token statement_prefix:sym<BEGIN> { :my %*MYSTERY; <sym><.kok> <blorst> <.explain_mystery> <.cry_sorrows> }
token statement_prefix:sym<COMPOSE> { <sym><.kok> <blorst> }
token statement_prefix:sym<TEMP> { <sym><.kok> <blorst> }
token statement_prefix:sym<CHECK> { <sym><.kok> <blorst> }
token statement_prefix:sym<INIT> { <sym><.kok> <blorst> }
token statement_prefix:sym<ENTER> { <sym><.kok> <blorst> }
token statement_prefix:sym<FIRST> { <sym><.kok> <blorst> }
token statement_prefix:sym<END> { <sym><.kok> <blorst> }
token statement_prefix:sym<LEAVE> { <sym><.kok> <blorst> }
token statement_prefix:sym<KEEP> { <sym><.kok> <blorst> }
token statement_prefix:sym<UNDO> { <sym><.kok> <blorst> }
token statement_prefix:sym<NEXT> { <sym><.kok> <blorst> }
token statement_prefix:sym<LAST> { <sym><.kok> <blorst> }
token statement_prefix:sym<PRE> { <sym><.kok> <blorst> }
token statement_prefix:sym<POST> { <sym><.kok> <blorst> }
token statement_prefix:sym<CLOSE> { <sym><.kok> <blorst> }
token statement_prefix:sym<race> {
<sym><.kok>
[
| <?before 'for' <.kok>> <for=.statement_control>
| <blorst>
]
}
token statement_prefix:sym<hyper> {
<sym><.kok>
[
| <?before 'for' <.kok>> <for=.statement_control>
| <blorst>
]
}
token statement_prefix:sym<lazy> {
<sym><.kok>
[
| <?before 'for' <.kok>> <for=.statement_control>
| <blorst>
]
}
token statement_prefix:sym<eager> { <sym><.kok> <blorst> }
token statement_prefix:sym<sink> { <sym><.kok> <blorst> }
token statement_prefix:sym<try> {
:my $*FATAL := 1;
<!!{ $/.clone_braid_from(self).set_pragma('fatal',1); }>
<sym><.kok> <blorst>
<.set_braid_from(self)>
}
token statement_prefix:sym<quietly> { <sym><.kok> <blorst> }
token statement_prefix:sym<gather> { <sym><.kok> <blorst> }
token statement_prefix:sym<once> { <sym><.kok> <blorst> }
token statement_prefix:sym<start> { <sym><.kok> <blorst> }
token statement_prefix:sym<supply> {
:my $*WHENEVER_COUNT := 0;
<sym><.kok> <blorst>
}
token statement_prefix:sym<react> {
:my $*WHENEVER_COUNT := 0;
<sym><.kok> <blorst>
}
token statement_prefix:sym<do> { <sym><.kok> <blorst> }
token statement_prefix:sym<DOC> {
<sym><.kok> $<phase>=['BEGIN' || 'CHECK' || 'INIT']<.end_keyword><.ws>
<blorst>
}
token blorst {
[ <?[{]> <block> | <![;]> <statement> <.cheat_heredoc>? || <.missing: 'block or statement'> ]
}
## Statement modifiers
proto rule statement_mod_cond { <...> }
method nomodexpr($k) {
self.'!clear_highwater'();
self.typed_panic( 'X::Syntax::Confused', reason => "Missing expression for '$k' statement modifier" );
self;
}
token modifier_expr($k) { <EXPR> || <.nomodexpr($k)> }
token smexpr($k) { <EXPR> || <.nomodexpr($k)> }
rule statement_mod_cond:sym<if> { <sym><.kok> <modifier_expr('if')> }
rule statement_mod_cond:sym<unless> { <sym><.kok> <modifier_expr('unless')> }
rule statement_mod_cond:sym<when> { <sym><.kok> <modifier_expr('when')> }
rule statement_mod_cond:sym<with> { <sym><.kok> <modifier_expr('with')> }
rule statement_mod_cond:sym<without>{ <sym><.kok> <modifier_expr('without')> }
proto rule statement_mod_loop { <...> }
rule statement_mod_loop:sym<while> { <sym><.kok> <smexpr('while')> }
rule statement_mod_loop:sym<until> { <sym><.kok> <smexpr('until')> }
rule statement_mod_loop:sym<for> { <sym><.kok> <smexpr('for')> }
rule statement_mod_loop:sym<given> { <sym><.kok> <smexpr('given')> }
## Terms
token term:sym<fatarrow> { <fatarrow> }
token term:sym<colonpair> { <colonpair> }
token term:sym<variable> { <variable> { $*VAR := $<variable> unless $*VAR; } } # maybe desigilname already set it
token term:sym<package_declarator> { <package_declarator> }
token term:sym<scope_declarator> { <scope_declarator> }
token term:sym<routine_declarator> { <routine_declarator> }
token term:sym<multi_declarator> { <?before 'multi'|'proto'|'only'> <multi_declarator> }
token term:sym<regex_declarator> { <regex_declarator> }
token term:sym<circumfix> { <circumfix> }
token term:sym<statement_prefix> { <statement_prefix> }
token term:sym<**> { <sym> }
token term:sym<*> { <sym> }
token term:sym<lambda> { <?lambda> <pblock> {$*BORG<block> := $<pblock> } }
token term:sym<type_declarator> { <type_declarator> }
token term:sym<value> { <value> }
token term:sym<unquote> { '{{{' <?{ $*IN_QUASI }> <statementlist> '}}}' }
token term:sym<!!> { '!!' <?before \s> } # actual error produced inside infix:<?? !!>
token term:sym<::?IDENT> {
$<sym> = [ '::?' <identifier> ] »
}
token term:sym<p5end> {
<< __END__ >>
<.obs('__END__ as end of code',
'the =finish pod marker and $=finish to read')>
}
token term:sym<p5data> {
<< __DATA__ >>
<.obs('__DATA__ as start of data',
'the =finish pod marker and $=finish to read')>
}
token infix:sym<lambda> {
<?before '{' | <.lambda> > <!{ $*IN_META }> {
my $needparens := 0;
my $pos := $/.from;
my $line := HLL::Compiler.lineof($/.orig, $/.from, :cache(1));
my $lex := $*W.cur_lexpad();
for 'if', 'unless', 'while', 'until', 'for', 'given', 'when', 'loop', 'sub', 'method', 'with', 'without', 'supply', 'whenever', 'react' {
$needparens++ if $_ eq 'loop';
my $m := %*MYSTERY{$_ ~ '-' ~ $lex.cuid};
next unless $m;
my $m_pos := $m<pos>[nqp::elems($m<pos>) - 1];
my $m_line := HLL::Compiler.lineof($/.orig, $m_pos, :cache(1));
if $line - $m_line < 5 {
if $m<ctx> eq '(' {
$/.'!clear_highwater'();
$/.'!cursor_pos'($m_pos);
$/.typed_sorry('X::Syntax::KeywordAsFunction',
word => $_,
:$needparens,
);
$/.'!cursor_pos'($pos);
$/.panic("Unexpected block in infix position (two terms in a row)");
}
else {
$/.'!clear_highwater'();
$/.'!cursor_pos'($m_pos);
$/.sorry("Word '$_' interpreted as a listop; please use 'do $_' to introduce the statement control word");
$/.'!cursor_pos'($pos);
$/.panic("Unexpected block in infix position (two terms in a row)");
}
}
}
}
[
|| <!{ $*IN_REDUCE }> {
$/.panic("Unexpected block in infix position (missing statement control word before the expression?)");
}
|| <!>
]
}
token term:sym<undef> {
<!{ $*LANG.pragma('p5isms') }>
<sym> >> {}
[ <?before \h*'$/' >
<.obs('$/ variable as input record separator',
"the filehandle's .slurp method")>
]?
[ <?before [ '(' || \h*<.sigil><.twigil>?\w ] >
<.obs('undef as a verb', 'undefine() or assignment of Nil')>
]?
<.obs('undef as a value', "something more specific:\n\tan undefined type object such as Any or Int,\n\t:!defined as a matcher,\n\tAny:U as a type constraint,\n\tNil as the absence of an expected value\n\tor fail() as a failure return\n\t ")>
}
token term:sym<new> {
<!{ $*LANG.pragma('c++isms') }>
'new' \h+ <longname> \h* <![:]> <.obs("C++ constructor syntax", "method call syntax", :ism<c++isms>)>
}
token fatarrow {
<key=.identifier> \h* '=>' <.ws> <val=.EXPR('i<=')>
}
token coloncircumfix($front) {
# reset $*IN_DECL in case this colonpair is part of var we're
# declaring, since colonpair might have other vars. Don't make those
# think we're declaring them
:my $*IN_DECL := '';
[
| '<>' <.worry("Pair with <> really means an empty list, not null string; use :$front" ~ "('') to represent the null string,\n or :$front" ~ "() to represent the empty list more accurately")>
| {} <circumfix>
]
}
token colonpair {
:my $*key;
:my $*value;
':'
:dba('colon pair')
[
| '!' [ <identifier> || <.panic: "Malformed False pair; expected identifier"> ]
[ <[ \[ \( \< \{ ]> {
$/.typed_panic('X::Syntax::NegatedPair', key => ~$<identifier>) } ]?
{ $*key := $<identifier>.Str; $*value := 0; }
| $<num> = [\d+] <identifier> [ <?before <.[ \[ \( \< \{ ]>> {} <.sorry("Extra argument not allowed; pair already has argument of " ~ $<num>.Str)> <.circumfix> ]?
<?{
# Here we go over each character in the numeral and check $ch.chr eq $ch.ord.chr
# to fail any matches that have synthetics, such as 7\x[308]
my $num := ~$<num>;
my $chars-num := nqp::chars($num);
my $pos := -1;
nqp::while(
nqp::islt_i( ($pos := nqp::add_i($pos, 1)), $chars-num )
&& nqp::eqat(
$num,
nqp::chr( nqp::ord($num, $pos) ),
$pos,
),
nqp::null,
);
nqp::iseq_i($chars-num, $pos);
}>
{ $*key := $<identifier>.Str; $*value := nqp::radix_I(10, $<num>, 0, 0, $*W.find_symbol(['Int']))[0]; }
| <identifier>
{ $*key := $<identifier>.Str; }
[
|| <.unsp>? :dba('pair value') <coloncircumfix($*key)> { $*value := $<coloncircumfix>; }
|| { $*value := 1; }
]
| :dba('signature') '(' ~ ')' <fakesignature>
| <coloncircumfix('')>
{ $*key := ""; $*value := $<coloncircumfix>; }
| <var=.colonpair_variable>
{ $*key := $<var><desigilname>.Str; $*value := $<var>; self.check_variable($*value); }
]
}
token colonpair_variable {
<sigil> {}
[
| <twigil>? <desigilname>
| $<capvar>='<' <desigilname> '>'
]
}
proto token special_variable { <...> }
token special_variable:sym<$!{ }> {
[ '$!{' .*? '}' | '%!' ]
<.obsvar('%!')>
}
token special_variable:sym<$`> {
<sym> <?before \s | ',' | <.terminator> >
<.obsvar('$`')>
}
token special_variable:sym<$@> {
<sym> <[ \s ; , ) ]> .
<.obsvar('$@')>
}
token special_variable:sym<$#> {
<sym> <identifier>
{}
<.obsvar('$#', ~$<identifier>)>
}
token special_variable:sym<$$> {
<sym> \W
<.obsvar('$$')>
}
token special_variable:sym<$&> {
<sym> <?before \s | ',' | <.terminator> >
<.obsvar('$&')>
}
token special_variable:sym<@+> {
<sym> <?before \s | ',' | <.terminator> >
<.obsvar('@+')>
}
token special_variable:sym<%+> {
<sym> <?before \s | ',' | <.terminator> >
<.obsvar('%+')>
}
token special_variable:sym<$+[ ]> {
'$+['
<.obsvar('@+')>
}
token special_variable:sym<@+[ ]> {
'@+['
<.obsvar('@+')>
}
token special_variable:sym<@+{ }> {
'@+{'
<.obsvar('%+')>
}
token special_variable:sym<@-> {
<sym> <?before \s | ',' | <.terminator> >
<.obsvar('@-')>
}
token special_variable:sym<%-> {
<sym> <?before \s | ',' | <.terminator> >
<.obsvar('%-')>
}
token special_variable:sym<$-[ ]> {
'$-['
<.obsvar('@-')>
}
token special_variable:sym<@-[ ]> {
'@-['
<.obsvar('@-')>
}
token special_variable:sym<%-{ }> {
'@-{'
<.obsvar('%-')>
}
token special_variable:sym<$/> {
<sym> <?before \h* '=' \h* <.[ ' " ]> >
<.obsvar('$/')>
}
token special_variable:sym<$\\> {
'$\\' <?before \s | ',' | '=' | <.terminator> >
<.obsvar('$\\')>
}
token special_variable:sym<$|> {
<sym> <?before \h* '='>
<.obsvar('$|')>
}
token special_variable:sym<$;> {
<sym> <?before \h* '='>
<.obsvar('$;')>
}
token special_variable:sym<$'> { #'
<sym> <?before \s | ',' | <.terminator> >
<.obsvar('$' ~ "'")>
}
token special_variable:sym<$"> {
<sym> <?before \h* '='>
<.obsvar('$"')>
}
token special_variable:sym<$,> {
<sym> <?before \h* '='>
<.obsvar('$,')>
}
token special_variable:sym<$.> {
<sym> {} <!before \w | '(' | '^' >
<.obsvar('$.')>
}
token special_variable:sym<$?> {
<sym> {} <!before \w | '('>
<.obsvar('$?')>
}
token special_variable:sym<$]> {
<sym> {} <!before \w | '('>
<.obsvar('$]')>
}
regex special_variable:sym<${ }> {
<sigil> '{' {} $<text>=[.*?] '}'
<!{ $*IN_DECL }>
<!{ $<text> ~~ / '=>' || ':'<:alpha> || '|%' / }>
<!{ $<text> ~~ / ^ \s* $ / }>
<?{
my $sigil := $<sigil>.Str;
my $text := $<text>.Str;
my $bad := $sigil ~ '{' ~ $text ~ '}';
if $text ~~ /^\d+$/ {
$text := nqp::radix(10, $text, 0, 0)[0];
$text := $text - 1 if $text > 0;
}
if $sigil ne '$' && $sigil ne '@' {
False; # not likely a P5ism
}
elsif !($text ~~ /^(\w|\:)+$/) {
$/.obs($bad, "$sigil\($text) for hard ref or $sigil\::($text) for symbolic ref");
}
elsif $*QSIGIL {
$/.obs($bad, '{' ~ $sigil ~ $text ~ '}');
}
else {
$/.obs($bad, $sigil ~ $text);
}
}>
}
token desigilname {
[
| <?before <.sigil> <.sigil> > <variable>
| <?sigil>
[ <?{ $*IN_DECL }> <.typed_panic: 'X::Syntax::Variable::IndirectDeclaration'> ]?
<variable> {
$*VAR := $<variable>;
}
| <longname>
]
}
token desigilmetaname {
$<longname>=( $<name>=( <identifier> ) )
}
token variable {
:my $*IN_META := '';
[
| :dba('infix noun') '&[' ~ ']' <infixish('[]')>
| <sigil> [ $<twigil>=['.^'] <desigilname=desigilmetaname> | <twigil>? <desigilname> ]
[ <?{ !$*IN_DECL && $*VARIABLE && $*VARIABLE eq $<sigil> ~ $<twigil> ~ $<desigilname> }>
{ self.typed_panic: 'X::Syntax::Variable::Initializer', name => $*VARIABLE } ]?
| <special_variable>
| <sigil> $<index>=[\d+] [<?{ $*IN_DECL }> <.typed_panic: "X::Syntax::Variable::Numeric">]?
| <sigil> <?[<]> <postcircumfix> [<?{ $*IN_DECL }> <.typed_panic('X::Syntax::Variable::Match')>]?
| <?before <.sigil> <.?[ ( [ { ]>> <!RESTRICTED> <?{ !$*IN_DECL }> <contextualizer>
| $<sigil>=['$'] $<desigilname>=[<[/_!¢]>]
| {} <sigil> <!{ $*QSIGIL }> <?MARKER('baresigil')> # try last, to allow sublanguages to redefine sigils (like & in regex)
]
[ <?{ $<twigil> && ( $<twigil> eq '.' || $<twigil> eq '.^' ) }>
[ <.unsp> | '\\' | <?> ] <?[(]> <!RESTRICTED> <arglist=.postcircumfix>
]?
{ $*LEFTSIGIL := nqp::substr(self.orig(), self.from, 1) unless $*LEFTSIGIL }
}
token contextualizer {
:dba('contextualizer')
[ <?{ $*IN_DECL }> <.panic: "Cannot declare a contextualizer"> ]?
[
| <sigil> '(' ~ ')' <coercee=sequence>
| <sigil> <?[ \[ \{ ]> <coercee=circumfix>
]
}
token sigil { <[$@%&]> }
proto token twigil { <...> }
token twigil:sym<.> { <sym> <?before \w> }
token twigil:sym<!> { <sym> <?before \w> }
token twigil:sym<^> { <sym> <?before \w> }
token twigil:sym<:> { <sym> <?before \w> }
token twigil:sym<*> { <sym> <?before \w> }
token twigil:sym<?> { <sym> <?before \w> }
token twigil:sym<=> { <sym> <?before \w> }
token twigil:sym<~> { <sym> <?before \w> }
proto token package_declarator { <...> }
token package_declarator:sym<package> {
:my $*OUTERPACKAGE := self.package;
:my $*PKGDECL := 'package';
:my $*LINE_NO := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
<sym><.kok> <package_def>
<.set_braid_from(self)>
}
token package_declarator:sym<module> {
:my $*OUTERPACKAGE := self.package;
:my $*PKGDECL := 'module';
:my $*LINE_NO := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
<sym><.kok> <package_def>
<.set_braid_from(self)>
}
token package_declarator:sym<class> {
:my $*OUTERPACKAGE := self.package;
:my $*PKGDECL := 'class';
:my $*LINE_NO := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
<sym><.kok> <package_def>
<.set_braid_from(self)>
}
token package_declarator:sym<grammar> {
:my $*OUTERPACKAGE := self.package;
:my $*PKGDECL := 'grammar';
:my $*LINE_NO := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
<sym><.kok> <package_def>
<.set_braid_from(self)>
}
token package_declarator:sym<role> {
:my $*OUTERPACKAGE := self.package;
:my $*PKGDECL := 'role';
:my $*LINE_NO := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
<sym><.kok> <package_def>
<.set_braid_from(self)>
}
token package_declarator:sym<knowhow> {
:my $*OUTERPACKAGE := self.package;
:my $*PKGDECL := 'knowhow';
:my $*LINE_NO := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
<sym><.kok> <package_def>
<.set_braid_from(self)>
}
token package_declarator:sym<native> {
:my $*OUTERPACKAGE := self.package;
:my $*PKGDECL := 'native';
:my $*LINE_NO := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
<sym><.kok> <package_def>
<.set_braid_from(self)>
}
token package_declarator:sym<slang> {
:my $*OUTERPACKAGE := self.package;
:my $*PKGDECL := 'slang';
:my $*LINE_NO := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
<sym><.kok> <package_def>
<.set_braid_from(self)>
}
token package_declarator:sym<trusts> {
<sym><.kok> [ <typename> || <.typo_typename(1)> ]
}
rule package_declarator:sym<also> {
<sym><.kok>
[ <trait>+ || <.panic: "No valid trait found after also"> ]
}
rule package_def {
:my $longname;
:my $outer := $*W.cur_lexpad();
:my $*IMPLICIT := 0;
:my $*DECLARAND;
:my $*CODE_OBJECT := $*W.stub_code_object($*PKGDECL eq 'role' ?? 'Sub' !! 'Block');
:my $*IN_DECL := 'package';
:my $*HAS_SELF := '';
:my $*CURPAD;
:my $*DOC := $*DECLARATOR_DOCS;
:my $*POD_BLOCK;
:my $*BORG := {};
{ $*DECLARATOR_DOCS := '' }
<.attach_leading_docs>
# Type-object will live in here; also set default REPR (a trait
# may override this, e.g. is repr('...')).
:my $*PACKAGE := $*OUTERPACKAGE;
:my $package;
:my %*ATTR_USAGES;
:my $*REPR;
:my $*VER;
:my $*API;
:my $*AUTH;
# Default to our scoped.
{ unless $*SCOPE { $*SCOPE := 'our'; } }
<!!{ $/.clone_braid_from(self) }>
[
[ <longname> { $longname := $*W.dissect_longname($<longname>); } ]?
<.newpad>
[ :dba('generic role')
<?{ ($*PKGDECL//'') eq 'role' }>
'[' ~ ']' <signature>
{ $*IN_DECL := ''; }
]?
<trait>*
{
my $target_package := $longname && $longname.is_declared_in_global()
?? $*GLOBALish
!! $*OUTERPACKAGE;
# Unless we're augmenting...
if $*SCOPE ne 'augment' {
if $longname {
for $longname.colonpairs_hash('class') -> $adverb {
my str $key := $adverb.key;
if $key eq 'ver' {
$*VER := $*W.handle-begin-time-exceptions($/,
'parsing package version',
-> { $*W.find_symbol(['Version']).new($adverb.value) });
}
elsif $key eq 'api' {
$*API := $adverb.value;
}
elsif $key eq 'auth' {
$*AUTH := $adverb.value;
}
else {
$/.typed_panic('X::Syntax::Type::Adverb', adverb => $adverb.key);
}
}
}
# Locate any existing symbol. Note that it's only a match
# with "my" if we already have a declaration in this scope.
my $exists := 0;
my @name := $longname ??
$longname.type_name_parts('package name', :decl(1)) !!
[];
if @name && $*SCOPE ne 'anon' {
if @name && $*W.already_declared($*SCOPE, $target_package, $outer, @name) {
$*PACKAGE := $package := $*W.find_symbol(@name, cur-package => $target_package);
$/.set_package($package);
$exists := 1;
}
}
my $fullname;
if @name {
$fullname := $longname.fully_qualified_with($target_package);
}
# If it exists already, then it's either uncomposed (in which
# case we just stubbed it), a role (in which case multiple
# variants are OK) or else an illegal redecl.
if $exists && ($*PKGDECL ne 'role' || !nqp::can($package.HOW, 'configure_punning')) {
if $*PKGDECL eq 'role' || !nqp::can($package.HOW, 'is_composed') || $package.HOW.is_composed($package) {
$*W.throw($/, ['X', 'Redeclaration'],
symbol => $longname.name(),
);
}
if nqp::defined($*REPR) {
$*W.throw($/, ['X', 'TooLateForREPR'], type => $package);
}
}
# If it's not a role, or it is a role but one with no name,
# then just needs meta-object construction and installation.
elsif $*PKGDECL ne 'role' || !@name {
# Construct meta-object for this package.
my %args;
if @name {
%args<name> := $fullname;
%args<ver> := $*VER;
%args<api> := $*API;
%args<auth> := $*AUTH;
}
if $*REPR ne '' {
%args<repr> := $*REPR;
}
$*PACKAGE := $package := $*W.pkg_create_mo($/, $*W.resolve_mo($/, $*PKGDECL), |%args);
$/.set_package($package);
# Install it in the symbol table if needed.
if @name {
$*W.install_package($/, @name, $*SCOPE, $*PKGDECL, $target_package, $outer, $package);
}
}
# If it's a named role, a little trickier. We need to make
# a parametric role group for it (unless we got one), and
# then install it in that.
else {
# If the group doesn't exist, create it.
my $group;
if $exists {
$group := $package;
}
else {
$group := $*W.pkg_create_mo($/, $*W.resolve_mo($/, 'role-group'),
:name($fullname), :repr($*REPR));
$*W.install_package($/, @name, $*SCOPE, $*PKGDECL, $target_package, $outer, $group);
}
# Construct role meta-object with group.
sub needs_args($s) {
return 0 if !$s;
my @params := $s.ast<parameters>;
return 0 if nqp::elems(@params) == 0;
return nqp::elems(@params) > 1 || !@params[0]<optional>;
}
$*PACKAGE := $package := $*W.pkg_create_mo($/, $*W.resolve_mo($/, $*PKGDECL),
:name($fullname), :ver($*VER), :api($*API), :auth($*AUTH), :repr($*REPR),
:group($group), :signatured(needs_args($<signature>)));
$/.set_package($package);
}
}
else {
# Augment. Ensure we can.
if !$/.pragma('MONKEY-TYPING') && $longname.text ne 'Cool' {
$/.typed_panic('X::Syntax::Augment::WithoutMonkeyTyping');
}
elsif !$longname {
$*W.throw($/, 'X::Anon::Augment', package-kind => $*PKGDECL);
}
if $longname.colonpairs_hash('class') {
$/.typed_panic('X::Syntax::Augment::Adverb');
}
# Locate type.
my @name :=
$longname.type_name_parts('package name', :decl(1));
my int $found;
try {
$*PACKAGE := $package := $*W.find_symbol(@name, cur-package => $target_package);
$/.set_package($package);
$found := 1
}
unless $found {
$*W.throw($/, 'X::Augment::NoSuchType',
package-kind => $*PKGDECL,
package => $longname.text(),
);
}
unless $package.HOW.archetypes.augmentable {
$/.typed_panic('X::Syntax::Augment::Illegal',
package => $longname.text);
}
}
# Install $?PACKAGE, $?MODULE, $?ROLE, $?CLASS, and :: variants as needed.
my $curpad := $*W.cur_lexpad();
unless $curpad.symbol('$?PACKAGE') {
$*W.install_lexical_symbol($curpad, '$?PACKAGE', $package);
$*W.install_lexical_symbol($curpad, '::?PACKAGE', $package);
if $*PKGDECL eq 'role' {
$*W.install_lexical_symbol($curpad, '$?ROLE', $package);
$*W.install_lexical_symbol($curpad, '::?ROLE', $package);
$*W.install_lexical_symbol($curpad, '$?CLASS',
$*W.pkg_create_mo($/, $*W.resolve_mo($/, 'generic'), :name('$?CLASS')));
$*W.install_lexical_symbol($curpad, '::?CLASS',
$*W.pkg_create_mo($/, $*W.resolve_mo($/, 'generic'), :name('::?CLASS')));
}
elsif $*PKGDECL eq 'module' {
$*W.install_lexical_symbol($curpad, '$?MODULE', $package);
$*W.install_lexical_symbol($curpad, '::?MODULE', $package);
}
elsif $*PKGDECL ne 'package'{
$*W.install_lexical_symbol($curpad, '$?CLASS', $package);
$*W.install_lexical_symbol($curpad, '::?CLASS', $package);
}
}
# Set declarand as the package.
$*DECLARAND := $package;
if $*PRECEDING_DECL_LINE < $*LINE_NO {
$*PRECEDING_DECL_LINE := $*LINE_NO;
$*PRECEDING_DECL := $*DECLARAND;
}
# Apply any traits.
$*W.apply_traits($<trait>, $*DECLARAND);
}
:!s
{ nqp::push(@*PACKAGES, $package); }
[
|| <?[{]>
[
:my $*FATAL := self.pragma('fatal'); # can also be set from inside statementlist
{
$*IN_DECL := '';
$*begin_compunit := 0;
if $*SCOPE eq 'unit' {
$/.typed_panic("X::Declaration::Scope", scope => 'unit', declaration => "block form of $*PKGDECL");
}
}
<blockoid>
]
|| ';'
[
|| <?{ $*begin_compunit }>
{
unless $longname {
$/.panic("Compilation unit cannot be anonymous");
}
unless $*SCOPE eq 'unit' {
if $*PKGDECL eq 'package' {
$/.panic('This appears to be Perl 5 code. If you intended it to be Perl 6 code, please use a Perl 6 style declaration like "unit package Foo;" or "unit module Foo;", or use the block form instead of the semicolon form.');
}
$/.panic("Semicolon form of '$*PKGDECL' without 'unit' is illegal. You probably want to use 'unit $*PKGDECL'");
}
unless $outer =:= $*UNIT {
$/.typed_panic("X::UnitScope::Invalid", what => $*PKGDECL, where => "in a subscope");
}
$*begin_compunit := 0;
}
{ $*IN_DECL := ''; }
<.finishpad>
<statementlist(1)> # whole rest of file, presumably
{ $*CURPAD := $*W.pop_lexpad() }
|| { $/.typed_panic("X::UnitScope::TooLate", what => $*PKGDECL); }
]
|| <.panic("Unable to parse $*PKGDECL definition")>
]
{ nqp::pop(@*PACKAGES); }
]:!s || { $/.malformed($*PKGDECL) }
}
token declarator {
:my $*LEFTSIGIL := '';
:my $*VARIABLE := '';
[
# STD.pm6 uses <defterm> here, but we need different
# action methods
| '\\' <deftermnow>
[ <.ws> <term_init=initializer> || <.typed_panic: "X::Syntax::Term::MissingInitializer"> ]
| <variable_declarator>
[
|| <?{ $*SCOPE eq 'has' }> <.newpad> [<.ws> <initializer>]? { $*ATTR_INIT_BLOCK := $*W.pop_lexpad() }
|| [<.ws> <initializer>]?
]
| '(' ~ ')' <signature('variable')> [ <.ws> <trait>+ ]? [ <.ws> <initializer> ]?
| <routine_declarator>
| <regex_declarator>
| <type_declarator>
]
}
proto token multi_declarator { <...> }
token multi_declarator:sym<multi> {
:my $*LINE_NO := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
<sym><.kok> :my $*MULTINESS := 'multi';
[ <?before '('> { $*W.throw($/, 'X::Anon::Multi', multiness => $*MULTINESS) } ]?
[ <declarator> || <routine_def('sub')> || <.malformed('multi')> ]
}
token multi_declarator:sym<proto> {
:my $*LINE_NO := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
<sym><.kok> :my $*MULTINESS := 'proto'; :my $*IN_PROTO := 1;
[ <?before '('> { $*W.throw($/, 'X::Anon::Multi', multiness => $*MULTINESS) } ]?
[ <declarator> || <routine_def('sub')> || <.malformed('proto')> ]
}
token multi_declarator:sym<only> {
:my $*LINE_NO := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
<sym><.kok> :my $*MULTINESS := 'only';
[ <declarator> || <routine_def('sub')> || <.malformed('only')>]
}
token multi_declarator:sym<null> {
:my $*MULTINESS := '';
<declarator>
}
proto token scope_declarator { <...> }
token scope_declarator:sym<my> { <sym> <scoped('my')> }
token scope_declarator:sym<our> { <sym> <scoped('our')> }
token scope_declarator:sym<has> {
:my $*LINE_NO := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
<sym>
:my $*HAS_SELF := 'partial';
:my $*ATTR_INIT_BLOCK;
<scoped('has')>
}
token scope_declarator:sym<HAS> {
:my $*LINE_NO := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
<sym>
:my $*HAS_SELF := 'partial';
:my $*ATTR_INIT_BLOCK;
<scoped('has')>
}
token scope_declarator:sym<augment> { <sym> <scoped('augment')> }
token scope_declarator:sym<anon> { <sym> <scoped('anon')> }
token scope_declarator:sym<state> { <sym> <scoped('state')> }
token scope_declarator:sym<supersede> {
<sym> <scoped('supersede')> <.NYI('"supersede"')>
}
token scope_declarator:sym<unit> { <sym> <scoped('unit')> }
token scoped($*SCOPE) {
<.end_keyword>
:dba('scoped declarator')
[
:my $*DOC := $*DECLARATOR_DOCS;
:my $*POD_BLOCK;
{
if $*SCOPE eq 'has' {
$*DECLARATOR_DOCS := '';
if $*PRECEDING_DECL_LINE < $*LINE_NO {
$*PRECEDING_DECL_LINE := $*LINE_NO;
$*PRECEDING_DECL := Mu; # actual declarand comes later, in Actions::declare_variable
}
self.attach_leading_docs;
}
}
<.ws>
[
| <DECL=declarator>
| <DECL=regex_declarator>
| <DECL=package_declarator>
| [<typename><.ws>]+
{
if +$<typename> > 1 {
$/.NYI('Multiple prefix constraints');
}
$*OFTYPE := $<typename>[0];
}
<DECL=multi_declarator>
| <DECL=multi_declarator>
]
|| <.ws>[<typename><.ws>]* <ident>
<?before <.ws>
[
| ':'?':'?'='
| <.terminator>
| <trait>
| "where" <.ws> <EXPR>
| $
]
> {} <.malformed("$*SCOPE (did you mean to declare a sigilless \\{~$<ident>} or \${~$<ident>}?)")>
|| <.ws><typename><.ws> <?before "where" <.ws> <EXPR>> {}
<.malformed("$*SCOPE (found type followed by constraint; did you forget a variable in between?)")>
|| <.ws><typename><.ws> <?before <trait>> {}
<.malformed("$*SCOPE (found type followed by trait; did you forget a variable in between?)")>
|| <.ws><typename><.ws> <?before [ <.terminator> | $ ]> {}
<.malformed("$*SCOPE (did you forget a variable after type?)")>
|| <.ws><!typename> <typo_typename> <!>
|| <.malformed($*SCOPE)>
]
}
token variable_declarator {
:my $*IN_DECL := 'variable';
:my $sigil;
<variable>
{
$*VARIABLE := $<variable>.ast.name;
$/.add_variable($*VARIABLE);
$sigil := $<variable><sigil>.Str;
$*IN_DECL := '';
}
[
<.unsp>?
$<shape>=[
| '(' ~ ')' <signature>
{
if $sigil eq '&' {
self.typed_sorry('X::Syntax::Reserved',
reserved => '() shape syntax in routine declarations',
instead => ' (maybe use :() to declare a longname?)'
);
}
elsif $sigil eq '@' {
self.typed_sorry('X::Syntax::Reserved',
reserved => '() shape syntax in array declarations');
}
elsif $sigil eq '%' {
self.typed_sorry('X::Syntax::Reserved',
reserved => '() shape syntax in hash declarations');
}
else {
self.typed_sorry('X::Syntax::Reserved',
reserved => '() shape syntax in variable declarations');
}
}
| :dba('shape definition') '[' ~ ']' <semilist>
{ $sigil ne '@' && self.typed_sorry('X::Syntax::Reserved',
reserved => '[] shape syntax with the ' ~ $sigil ~ ' sigil') }
| :dba('shape definition') '{' ~ '}' <semilist>
{ $sigil ne '%' && self.typed_sorry('X::Syntax::Reserved',
reserved => '{} shape syntax with the ' ~ $sigil ~ ' sigil') }
| <?[<]> <postcircumfix> <.NYI: "Shaped variable declarations">
]+
]?
[ <.ws> <trait>+ ]?
[ <.ws> <post_constraint('var')>+ ]?
}
proto token routine_declarator { <...> }
token routine_declarator:sym<sub> {
:my $*LINE_NO := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
<sym> <.end_keyword> <routine_def('sub')>
}
token routine_declarator:sym<method> {
:my $*LINE_NO := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
<sym> <.end_keyword> <method_def('method')>
}
token routine_declarator:sym<submethod> {
:my $*LINE_NO := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
<sym> <.end_keyword> <method_def('submethod')>
}
token routine_declarator:sym<macro> {
:my $*LINE_NO := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
<sym> <.end_keyword> <macro_def()>
}
rule routine_def($d) {
:my $*IN_DECL := $d;
:my $*METHODTYPE;
:my $*IMPLICIT := 0;
:my $*DOC := $*DECLARATOR_DOCS;
{ $*DECLARATOR_DOCS := '' }
:my $*POD_BLOCK;
:my $*DECLARAND := $*W.stub_code_object('Sub');
:my $*CODE_OBJECT := $*DECLARAND;
:my $*CURPAD;
:my $*SIG_OBJ;
:my %*SIG_INFO;
:my $outer := $*W.cur_lexpad();
:my $*BORG := {};
:my $*FATAL := self.pragma('fatal'); # can also be set from inside statementlist
{
if $*PRECEDING_DECL_LINE < $*LINE_NO {
$*PRECEDING_DECL_LINE := $*LINE_NO;
$*PRECEDING_DECL := $*DECLARAND;
}
}
<.attach_leading_docs>
<deflongname>?
{
if $<deflongname> && $<deflongname><colonpair>[0]<coloncircumfix> -> $cf {
# It's an (potentially new) operator, circumfix, etc. that we
# need to tweak into the grammar.
my $category := $<deflongname><name>.Str;
my $opname := $cf<circumfix>
?? $*W.colonpair_nibble_to_str($/, $cf<circumfix><nibble> // $cf<circumfix><semilist>)
!! '';
my $canname := $category ~ $*W.canonicalize_pair('sym', $opname);
$/.add_categorical($category, $opname, $canname, $<deflongname>.ast, $*DECLARAND);
}
}
<.newpad>
[
'(' <multisig> ')' {
%*SIG_INFO := $<multisig>.ast;
$*SIG_OBJ := $*W.create_signature_and_params($<multisig>,
%*SIG_INFO, $*W.cur_lexpad(), 'Any');
}
]?
<trait>* :!s
{ $*IN_DECL := ''; }
[
|| ';'
{
if $<deflongname> ne 'MAIN' {
$/.typed_panic("X::UnitScope::Invalid", what => "sub",
where => "except on a MAIN sub", suggestion =>
'Please use the block form. If you did not mean to '
~ "declare a unit-scoped sub,\nperhaps you accidentally "
~ "placed a semicolon after routine's definition?"
);
}
unless $*begin_compunit {
$/.typed_panic("X::UnitScope::TooLate", what => "sub");
}
unless $*MULTINESS eq '' || $*MULTINESS eq 'only' {
$/.typed_panic("X::UnitScope::Invalid", what => "sub", where => "on a $*MULTINESS sub");
}
unless $outer =:= $*UNIT {
$/.typed_panic("X::UnitScope::Invalid", what => "sub", where => "in a subscope");
}
$*begin_compunit := 0;
}
<.finishpad>
<statementlist(1)>
{ $*CURPAD := $*W.pop_lexpad() }
|| <onlystar>
|| <!before '{'> <possibly_subname=.deflongname> { if self.parse($<deflongname>.Str, :rule('typename')) { $/.panic("Did you mean to write \"my $<deflongname> sub $<possibly_subname>\" or put \"returns $<deflongname>\" before the block?"); } } <!>
|| <blockoid>
]
}
rule method_def($d) {
:my $*IN_DECL := $d;
:my $*METHODTYPE := $d;
:my $*HAS_SELF := $d eq 'submethod' ?? 'partial' !! 'complete';
:my $*DOC := $*DECLARATOR_DOCS;
{ $*DECLARATOR_DOCS := '' }
:my $*POD_BLOCK;
:my $*DECLARAND := $*W.stub_code_object($d eq 'submethod' ?? 'Submethod' !! 'Method');
:my $*CODE_OBJECT := $*DECLARAND;
:my $*SIG_OBJ;
:my %*SIG_INFO;
:my $*BORG := {};
:my $*FATAL := self.pragma('fatal'); # can also be set from inside statementlist
{
if $*PRECEDING_DECL_LINE < $*LINE_NO {
$*PRECEDING_DECL_LINE := $*LINE_NO;
$*PRECEDING_DECL := $*DECLARAND;
}
}
<.attach_leading_docs>
[
<.newpad>
[
| $<specials>=[<[ ! ^ ]>?]<longname> [ '(' <multisig(1)> ')' ]? <trait>*
| '(' <multisig(1)> ')' <trait>*
| <sigil>'.':!s
:dba('subscript signature')
[
| '(' ~ ')' <multisig(1)>
| '[' ~ ']' <multisig(1)>
| '{' ~ '}' <multisig(1)>
]:s
<trait>*
| <?>
]
{
$*IN_DECL := '';
my $meta := $<specials> && ~$<specials> eq '^';
my $invocant_type := $*W.find_symbol([
$<longname> && $*W.is_lexical('$?CLASS') && !$meta
?? '$?CLASS'
!! 'Mu']);
if $<multisig> {
%*SIG_INFO := $<multisig>.ast;
$*SIG_OBJ := $*W.create_signature_and_params($<multisig>,
%*SIG_INFO, $*W.cur_lexpad(), 'Any', :method,
:$invocant_type);
}
else {
%*SIG_INFO := hash(parameters => []);
$*SIG_OBJ := $*W.create_signature_and_params($/,
%*SIG_INFO, $*W.cur_lexpad(), 'Any', :method,
:$invocant_type);
}
}
[
|| <onlystar>
|| <blockoid>
]
] || <.malformed('method')>
}
rule macro_def() {
:my $*IN_DECL := 'macro';
:my $*IMPLICIT := 0;
:my $*DOC := $*DECLARATOR_DOCS;
<.experimental('macros')>
{ $*DECLARATOR_DOCS := '' }
:my $*POD_BLOCK;
:my $*DECLARAND := $*W.stub_code_object('Macro');
:my $*CODE_OBJECT := $*DECLARAND;
:my $*BORG := {};
:my $*FATAL := self.pragma('fatal'); # can also be set from inside statementlist
{
if $*PRECEDING_DECL_LINE < $*LINE_NO {
$*PRECEDING_DECL_LINE := $*LINE_NO;
$*PRECEDING_DECL := $*DECLARAND;
}
}
<.attach_leading_docs>
<deflongname>?
{
if $<deflongname> && $<deflongname><colonpair>[0]<coloncircumfix> -> $cf {
# It's an (potentially new) operator, circumfix, etc. that we
# need to tweak into the grammar.
my $category := $<deflongname><name>.Str;
my $opname := $cf<circumfix>
?? $*W.colonpair_nibble_to_str($/, $cf<circumfix><nibble>)
!! '';
my $canname := $category ~ $*W.canonicalize_pair('sym', $opname);
$/.add_categorical($category, $opname, $canname, $<deflongname>.ast, $*DECLARAND);
}
}
<.newpad>
[ '(' <multisig> ')' ]?
<trait>*
{ $*IN_DECL := ''; }
[
|| <onlystar>
|| <blockoid>
]
}
token onlystar {
:my $*CURPAD;
<?{ $*MULTINESS eq 'proto' }>
'{' <.ws> '*' <.ws> '}'
<?ENDSTMT>
<.finishpad>
{ $*CURPAD := $*W.pop_lexpad() }
}
###########################
# Captures and Signatures #
###########################
token capterm {
'\\'
[
| '(' <semiarglist> ')'
| <?before '$' | '@' | '%' | '&'> <.typed_worry('X::Worry::P5::Reference')> <termish>
| <?before \d> <.typed_worry('X::Worry::P5::BackReference')> <termish>
| <?before \S> <termish>
| {} <.panic: "You can't backslash that">
]
}
rule param_sep {
'' $<sep>=[','|':'|';;'|';'] {
if $<sep> eq ';;' {
$/.panic("Can only specify ';;' once in a signature")
if $*multi_invocant == 0;
$*multi_invocant := 0;
}
@*seps.push($<sep>);
}
}
# XXX Not really implemented yet.
token multisig($allow_invocant = 0) {
:my $*SCOPE := 'my';
<signature('sig', $allow_invocant)>
}
token sigterm {
:dba('signature')
':(' ~ ')' <fakesignature>
}
token fakesignature {
<.newpad>
<signature('sig', 1)>
}
token signature($*IN_DECL = 'sig', $*ALLOW_INVOCANT = 0) {
:my $*zone := 'posreq';
:my $*multi_invocant := 1;
:my @*seps := nqp::list();
<.ws>
[
| <?before '-->' | ')' | ']' | '{' | ':'\s | ';;' >
| <parameter>
]+ % <param_sep>
<.ws>
[ <?before '-->' | ')' | ']' | '{' | ':'\s | ';;' > || <.malformed('parameter')> ]
{ $*IN_DECL := ''; }
[ '-->' <.ws> [ || [<typename>|<value>||<typo_typename(1)>] <.ws>
[ || <?[ { ) ]>
|| <?before <.param_sep>? <.parameter>>
<.malformed('return value (return constraints only allowed at the end of the signature)')>
]
|| <.malformed('return value')>
] ]?
{ $*LEFTSIGIL := '@'; }
}
token parameter {
# We'll collect parameter information into a hash, then use it to
# build up the parameter object in the action method
:my %*PARAM_INFO;
:my $*CURTHUNK;
[
| <type_constraint>+
[
| $<quant>=['**'|'*'|'+'] <param_var>
| $<quant>=['\\'|'|'] <param_var> {
$/.panic('Obsolete use of | or \\ with sigil on param ' ~ $<param_var>);
}
| $<quant>=['\\'|'|'|'+'] <param_term>
| [ <param_var> | <named_param> ] $<quant>=['?'|'!'|<?>]
| <?>
]
| $<quant>=['**'|'*'|'+'] <param_var>
| $<quant>=['\\'|'|'] <param_var> {
$/.panic('Obsolete use of | or \\ with sigil on param ' ~ $<param_var>);
}
| $<quant>=['\\'|'|'|'+'] <param_term>
| [ <param_var> | <named_param> ] $<quant>=['?'|'!'|<?>]
| <longname>
{
my $name := $*W.dissect_longname($<longname>);
$*W.throw($/, ['X', 'Parameter', 'InvalidType'],
:typename($name.name),
:suggestions($*W.suggest_typename($name.name)));
}
]
<.ws>
<trait>*
<post_constraint('param')>*
<.newthunk>
[
<default_value>
[ <modifier=.trait> {
self.typed_panic: "X::Parameter::AfterDefault", type => "trait", modifier => $<modifier>, default => $<default_value>
}]?
[ <modifier=.post_constraint('param')> {
self.typed_panic: "X::Parameter::AfterDefault", type => "post constraint", modifier => $<modifier>, default => $<default_value>
}]?
]**0..1
{ $*CURTHUNK := $*W.pop_thunk() }
# enforce zone constraints
{
my $kind :=
$<named_param> ?? '*' !!
$<quant> eq '?' || $<default_value> ?? '?' !!
$<quant> eq '!' ?? '!' !!
$<quant> ne '' && $<quant> ne '\\' ?? '*' !!
'!';
my $name := %*PARAM_INFO<variable_name> // '';
if $kind eq '!' {
if $*zone eq 'posopt' {
$/.typed_panic('X::Parameter::WrongOrder', misplaced => 'required', after => 'optional', parameter => $name);
}
elsif $*zone eq 'var' {
$/.typed_panic('X::Parameter::WrongOrder', misplaced => 'required', after => 'variadic', parameter => $name);
}
}
elsif $kind eq '?' {
if $*zone eq 'posreq' {
$*zone := 'posopt';
}
elsif $*zone eq 'var' {
$/.typed_panic('X::Parameter::WrongOrder', misplaced => 'optional positional', after => 'variadic', parameter => $name);
}
}
elsif $kind eq '*' {
$*zone := 'var';
}
%*PARAM_INFO<is_multi_invocant> := $*multi_invocant;
%*PARAM_INFO<node> := $/;
}
}
token param_var {
:dba('formal parameter')
:my $*DOC := $*DECLARATOR_DOCS; # these get cleared later
:my $*POD_BLOCK;
:my $*SURROUNDING_DECL := nqp::getlexdyn('$*IN_DECL');
<.attach_leading_docs>
{
my $line_no := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
if $*PRECEDING_DECL_LINE < $line_no {
$*PRECEDING_DECL_LINE := $line_no;
my $par_type := $*W.find_symbol(['Parameter'], :setting-only);
$*PRECEDING_DECL := nqp::create($par_type); # actual declarand comes later, in World::create_parameter
}
}
[
| '[' ~ ']' <signature>
| '(' ~ ')' <signature>
| <sigil> <twigil>?
[
|| <?{ $<sigil>.Str eq '&' }>
[<?identifier> {} <name=.sublongname> | <sigterm>]
|| <name=.identifier>
|| <name=.decint> { $*W.throw($/, 'X::Syntax::Variable::Numeric', what => 'parameter') }
|| $<name>=[<[/!]>]
]?
:dba('shape declaration')
:my $*IN_DECL := '';
[
| <?before ':('> ':' # XXX allow fakesig parsed as subsig for the moment
| <?before '('> <.sorry: "Shape declaration with () is reserved;\n please use whitespace if you meant a subsignature for unpacking,\n or use the :() form if you meant to add signature info to the function's type">
| <?before '['> <arrayshape=.postcircumfix>
| <?before <.[ { < « ]>> <.sorry: 'Shape declaration is not yet implemented; please use whitespace if you meant something else'>
<postcircumfix>
]?
]
}
token param_term {
<defterm>?
}
token named_param {
:my $*GOAL := ')';
:dba('named parameter')
':'
[
| <name=.identifier> '('
<.ws> [ <named_param> | <param_var> ] <.ws>
[ ')' || <.panic: 'Unable to parse named parameter; couldn\'t find right parenthesis'> ]
| <param_var>
]
}
rule default_value {
:my $*IN_DECL := '';
'=' <EXPR('i=')>
}
token type_constraint {
:my $*IN_DECL := '';
[
| <value>
| [ <[-−]> :my $*NEGATE_VALUE := 1; | '+' ] $<value>=<numish>
| <typename>
| where <.ws> <EXPR('i=')>
]
<.ws>
}
rule post_constraint($*CONSTRAINT_USAGE) {
:my $*IN_DECL := '';
:my $*HAS_SELF := $*CONSTRAINT_USAGE eq 'var' && $*SCOPE eq 'has'
?? nqp::null !! nqp::getlexdyn('$*HAS_SELF');
:dba('constraint')
[
| '[' ~ ']' <signature>
| '(' ~ ')' <signature>
| where <EXPR('i=')>
]
}
proto token regex_declarator { <...> }
token regex_declarator:sym<rule> {
<sym><.kok>
:my %*RX;
:my $*INTERPOLATE := 1;
:my $*METHODTYPE := 'rule';
:my $*IN_DECL := 'rule';
:my $*LINE_NO := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
{
%*RX<s> := 1;
%*RX<r> := 1;
}
<regex_def>
}
token regex_declarator:sym<token> {
<sym><.kok>
:my %*RX;
:my $*INTERPOLATE := 1;
:my $*METHODTYPE := 'token';
:my $*IN_DECL := 'token';
:my $*LINE_NO := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
{
%*RX<r> := 1;
}
<regex_def>
}
token regex_declarator:sym<regex> {
<sym><.kok>
:my %*RX;
:my $*INTERPOLATE := 1;
:my $*METHODTYPE := 'regex';
:my $*IN_DECL := 'regex';
:my $*LINE_NO := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
<regex_def>
}
rule regex_def {
:my $*CURPAD;
:my $*HAS_SELF := 'complete';
:my $*DOC := $*DECLARATOR_DOCS;
{ $*DECLARATOR_DOCS := '' }
:my $*POD_BLOCK;
:my $*DECLARAND := $*W.stub_code_object('Regex');
:my $*CODE_OBJECT := $*DECLARAND;
{
if $*PRECEDING_DECL_LINE < $*LINE_NO {
$*PRECEDING_DECL_LINE := $*LINE_NO;
$*PRECEDING_DECL := $*DECLARAND;
}
}
<.attach_leading_docs>
[
<deflongname>?
{ if $<deflongname> { %*RX<name> := ~$<deflongname>.ast } }
{ $*IN_DECL := '' }
<.newpad>
[ [ ':'?'(' <signature('sig', 1)> ')' ] | <trait> ]*
'{'
[
| ['*'|'<...>'|'<*>'] <?{ $*MULTINESS eq 'proto' }> $<onlystar>={1}
| <nibble(self.quote_lang(%*RX<P5> ?? self.slang_grammar('P5Regex') !! self.slang_grammar('Regex'), '{', '}'))>
]
'}'<!RESTRICTED><?ENDSTMT>
{ $*CURPAD := $*W.pop_lexpad() }
] || <.malformed('regex')>
}
proto token type_declarator { <...> }
token type_declarator:sym<enum> {
<sym><.kok>
:my $*IN_DECL := 'enum';
:my $*DOC := $*DECLARATOR_DOCS;
{ $*DECLARATOR_DOCS := '' }
:my $*POD_BLOCK;
:my $*DECLARAND;
{
my $line_no := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
if $*PRECEDING_DECL_LINE < $line_no {
$*PRECEDING_DECL_LINE := $line_no;
$*PRECEDING_DECL := Mu; # actual declarand comes later, in Actions::type_declarator:sym<enum>
}
}
<.attach_leading_docs>
[
| <longname>
{
my $longname := $*W.dissect_longname($<longname>);
my @name := $longname.type_name_parts('enum name', :decl(1));
if $*W.already_declared($*SCOPE, self.package, $*W.cur_lexpad(), @name) {
$*W.throw($/, ['X', 'Redeclaration'],
symbol => $longname.name(),
);
}
}
| <variable>
| <?>
]
{ $*IN_DECL := ''; }
<.ws>
<trait>*
:my %*MYSTERY;
[ <?[<(«]> <term> <.ws> || <.panic: 'An enum must supply an expression using <>, «», or ()'> ]
<.explain_mystery> <.cry_sorrows>
}
rule type_declarator:sym<subset> {
<sym><.kok> :my $*IN_DECL := 'subset';
:my $*DOC := $*DECLARATOR_DOCS;
{ $*DECLARATOR_DOCS := '' }
:my $*POD_BLOCK;
:my $*DECLARAND;
{
my $line_no := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
if $*PRECEDING_DECL_LINE < $line_no {
$*PRECEDING_DECL_LINE := $line_no;
$*PRECEDING_DECL := Mu; # actual declarand comes later, in Actions::type_declarator:sym<subset>
}
}
<.attach_leading_docs>
[
[
[
<longname>
{
my $longname := $*W.dissect_longname($<longname>);
my @name := $longname.type_name_parts('subset name', :decl(1));
if $*W.already_declared($*SCOPE, self.package, $*W.cur_lexpad(), @name) {
$*W.throw($/, ['X', 'Redeclaration'],
symbol => $longname.name(),
);
}
}
]?
{ $*IN_DECL := '' }
<trait>*
[ where <EXPR('e=')> ]?
]
|| <.malformed('subset')>
]
}
token type_declarator:sym<constant> {
:my $*IN_DECL := 'constant';
<sym><.kok>
[
| '\\'? <defterm>
| <variable> {.add_variable(~$<variable>) } # for new &infix:<foo> synonyms
| <?>
]
{ $*IN_DECL := ''; }
<.ws>
<trait>*
{ $*W.push_lexpad($/) }
[
|| :my %*MYSTERY; <initializer> <.explain_mystery> <.cry_sorrows>
|| <.missing: "initializer on constant declaration">
]
<.cheat_heredoc>?
}