summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/examplefiles/Grammar.pm63803
-rw-r--r--tests/examplefiles/Optimizer.pm6702
2 files changed, 0 insertions, 4505 deletions
diff --git a/tests/examplefiles/Grammar.pm6 b/tests/examplefiles/Grammar.pm6
deleted file mode 100644
index 62507481..00000000
--- a/tests/examplefiles/Grammar.pm6
+++ /dev/null
@@ -1,3803 +0,0 @@
-use QRegex;
-use NQPP6QRegex;
-use NQPP5QRegex;
-use Perl6::Actions;
-use Perl6::World;
-use Perl6::Pod;
-
-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 \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) {
- 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 { <!> }
-
- my %quote_lang_cache;
- method quote_lang($l, $start, $stop, @base_tweaks?, @extra_tweaks?) {
- sub lang_key() {
- my @keybits := [$l.HOW.name($l), $start, $stop];
- 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;
- 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");
- }
- }
- $start ne $stop ?? $lang.balanced($start, $stop)
- !! $lang.unbalanced($stop);
- }
-
- # Get language from cache or derive it.
- my $key := lang_key();
- nqp::ifnull(%quote_lang_cache, %quote_lang_cache := nqp::hash());
- nqp::existskey(%quote_lang_cache, $key) && $key ne 'NOCACHE'
- ?? %quote_lang_cache{$key}
- !! (%quote_lang_cache{$key} := con_lang());
- }
-
- token babble($l, @base_tweaks?) {
- :my @extra_tweaks;
-
- <.ws>
- [ <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>=[<?>]
- {
- # Work out the delimeters.
- my $c := $/.CURSOR;
- 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 $!lang;
- method delim() { $!delim }
- method orignode() { $!orignode }
- method lang() { $!lang }
- }
-
- role herestop {
- token stopper { ^^ {} $<ws>=(\h*) $*DELIM \h* $$ \v? }
- }
-
- method heredoc () {
- my $here := self.'!cursor_start'();
- $here.'!cursor_pos'(self.pos);
- while @herestub_queue {
- my $herestub := nqp::shift(@herestub_queue);
- my $*DELIM := $herestub.delim;
- my $lang := $herestub.lang.HOW.mixin($herestub.lang, herestop);
- my $doc := $here.nibble($lang);
- if $doc {
- # Match stopper.
- my $stop := $lang.'!cursor_init'(self.orig(), :p($doc.pos), :shared(self.'!shared'())).stopper();
- unless $stop {
- self.panic("Ending delimiter $*DELIM not found");
- }
- $here.'!cursor_pos'($stop.pos);
-
- # Get it trimmed and AST updated.
- $*ACTIONS.trim_heredoc($doc, $stop, $herestub.orignode.MATCH.ast);
- }
- else {
- self.panic("Ending delimiter $*DELIM not found");
- }
- }
- $here.'!cursor_pass'($here.pos);
- $here
- }
-
- method queue_heredoc($delim, $lang) {
- nqp::ifnull(@herestub_queue, @herestub_queue := []);
- nqp::push(@herestub_queue, Herestub.new(:$delim, :$lang, :orignode(self)));
- return self;
- }
-
- 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 || { $/.CURSOR.panic("Couldn't find terminator $stop") } ]
-
- {
- nqp::can($lang, 'herelang') && self.queue_heredoc(
- $*W.nibble_to_str($/, $<nibble>.ast[1], -> { "Stopper '" ~ $<nibble> ~ "' too complex for heredoc" }),
- $lang.herelang)
- }
- }
-
- method nibble($lang) {
- my $lang_cursor := $lang.'!cursor_init'(self.orig(), :p(self.pos()), :shared(self.'!shared'()));
- my $*ACTIONS;
- for %*LANG {
- if nqp::istype($lang, $_.value) {
- $*ACTIONS := %*LANG{$_.key ~ '-actions'};
- last;
- }
- }
- $lang_cursor.nibbler();
- }
-
- 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) {
- @*WORRIES.push($*W.typed_exception(self.MATCH(), nqp::split('::', $type_str), |%opts));
- self
- }
-
- method malformed($what) {
- self.typed_panic('X::Syntax::Malformed', :$what);
- }
- method missing($what) {
- self.typed_panic('X::Syntax::Missing', :$what);
- }
- method NYI($feature) {
- self.typed_panic('X::Comp::NYI', :$feature)
- }
-
- method EXPR_nonassoc($cur, $left, $right) {
- self.typed_panic('X::Syntax::NonAssociative', :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') {
- $*W.throw(self.MATCH(), ['X', 'Obsolete'],
- old => $old,
- replacement => $new,
- when => $when,
- );
- }
- method sorryobs($old, $new, $when = ' in Perl 6') {
- $*W.throw(self.MATCH(), ['X', 'Obsolete'],
- old => $old,
- replacement => $new,
- when => $when,
- );
- }
- method worryobs($old, $new, $when = ' in Perl 6') {
- $*W.throw(self.MATCH(), ['X', 'Obsolete'],
- old => $old,
- replacement => $new,
- when => $when,
- );
- }
-
- 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) {
- if $var<sigil> ne '&' {
- $*W.throw($var, ['X', 'Undeclared'], symbol => $varast.name());
- }
- else {
- $var.CURSOR.add_mystery($varast.name, $var.to, 'var');
- }
- }
- else {
- 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).
- $lex<also_uses> := {} unless $lex<also_uses>;
- $lex<also_uses>{$name} := 1;
- }
- }
- }
- self
- }
-}
-
-grammar Perl6::Grammar is HLL::Grammar does STD {
- method TOP() {
- # Language braid.
- my %*LANG;
- %*LANG<Regex> := Perl6::RegexGrammar;
- %*LANG<Regex-actions> := Perl6::RegexActions;
- %*LANG<P5Regex> := Perl6::P5RegexGrammar;
- %*LANG<P5Regex-actions> := Perl6::P5RegexActions;
- %*LANG<Q> := Perl6::QGrammar;
- %*LANG<Q-actions> := Perl6::QActions;
- %*LANG<MAIN> := Perl6::Grammar;
- %*LANG<MAIN-actions> := Perl6::Actions;
-
- # 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.
- my %*HOW;
- %*HOW<knowhow> := pir::get_knowhow__P();
- %*HOW<package> := pir::get_knowhow__P();
-
- # 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 := pir::find_caller_lex__Ps('$?FILES');
- my $source_id := nqp::sha1(self.target());
- my $*W := nqp::isnull($file) ??
- Perl6::World.new(:handle($source_id)) !!
- Perl6::World.new(:handle($source_id), :description($file));
- $*W.add_initializations();
-
- # XXX Hack: clear any marks.
- pir::set_hll_global__vsP('%!MARKHASH', nqp::null());
-
- my $cursor := self.comp_unit;
- $*W.pop_lexpad(); # UNIT
- $*W.pop_lexpad(); # UNIT_OUTER
- $cursor;
- }
-
- ## Lexer stuff
-
- token apostrophe {
- <[ ' \- ]>
- }
-
- token ident {
- <.alpha> \w*
- }
-
- token identifier {
- <.ident> [ <.apostrophe> <.ident> ]*
- }
-
- token name {
- [
- | <identifier> <morename>*
- | <morename>+
- ]
- }
-
- token morename {
- :my $*QSIGIL := '';
- '::'
- [
- || <?before '(' | <alpha> >
- [
- | <identifier>
- | :dba('indirect name') '(' ~ ')' <EXPR>
- ]
- || <?before '::'> <.typed_panic: "X::Syntax::Name::Null">
- ]?
- }
-
- token longname {
- <name> {} [ <?before ':' <+alpha+[\< \[ \« ]>> <colonpair> ]*
- }
-
- token deflongname {
- :dba('new name to be defined')
- <name> <colonpair>*
- }
-
- token module_name {
- <longname>
- [ <?before '['> :dba('generic role') '[' ~ ']' <arglist> ]?
- }
-
- token end_keyword {
- <!before <[ \( \\ ' \- ]> || \h* '=>'> »
- }
- token spacey { <?before [\s | '#']> }
-
- token ENDSTMT {
- [
- | \h* $$ <.ws> <?MARKER('endstmt')>
- | <.unv>? $$ <.ws> <?MARKER('endstmt')>
- ]?
- }
-
- token ws {
- :my $old_highexpect := self.'!fresh_highexpect'();
- :dba('whitespace')
- [
- || <?MARKED('ws')>
- || <!ww>
- [
- | <.vws> <.heredoc>
- | <.unv>
- ]*
- <?MARKER('ws')>
- ]
- :my $stub := self.'!set_highexpect'($old_highexpect);
- }
-
- token unsp {
- \\ <?before [\s|'#'] >
- :dba('unspace')
- [
- | <.vws>
- | <.unv>
- ]*
- }
-
- 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>
- ]
- }
-
- proto token comment { <...> }
-
- token comment:sym<#> {
- '#' {} \N*
- }
-
- token comment:sym<#`(...)> {
- '#`' <?opener> {}
- [ <.quibble(%*LANG<Q>)> || <.typed_panic: 'X::Syntax::Comment::Embedded'> ]
- }
-
- token comment:sym<#=(...)> {
- '#=' <?opener> $<attachment>=<.quibble(%*LANG<Q>)>
- }
-
- token comment:sym<#=> {
- '#=' \h+ $<attachment>=[\N*]
- { $*DECLARATOR_DOCS := $<attachment> }
- }
-
- token attach_docs {
- {
- if ~$*DOC ne '' {
- my $cont := Perl6::Pod::serialize_aos(
- [Perl6::Pod::formatted_text(~$*DOC)]
- ).compile_time_value;
- my $block := $*W.add_constant(
- 'Pod::Block::Declarator', 'type_new',
- :nocache, :content($cont),
- );
- $*DOCEE := $block.compile_time_value;
- $*POD_BLOCKS.push($*DOCEE);
- }
- }
- <?>
- }
-
- token pod_content_toplevel {
- <pod_block>
- }
-
- proto token pod_content { <...> }
-
- token pod_content:sym<block> {
- <pod_newline>*
- <pod_block>
- <pod_newline>*
- }
-
- # any number of paragraphs of text
- token pod_content:sym<text> {
- <pod_newline>*
- <pod_textcontent>+ % <pod_newline>+
- <pod_newline>*
- }
-
- # not a block, just a directive
- token pod_content:sym<config> {
- <pod_newline>*
- ^^ \h* '=config' \h+ $<type>=\S+ <pod_configuration>
- <pod_newline>+
- }
-
- proto token pod_textcontent { <...> }
-
- # text not being code
- token pod_textcontent:sym<regular> {
- $<spaces>=[ \h* ]
- <?{ !$*ALLOW_CODE
- || ($<spaces>.to - $<spaces>.from) <= $*VMARGIN }>
-
- $<text> = [
- \h* <!before '=' \w> <pod_string> <pod_newline>
- ] +
- }
-
- token pod_textcontent:sym<code> {
- $<spaces>=[ \h* ]
- <?{ $*ALLOW_CODE
- && ($<spaces>.to - $<spaces>.from) > $*VMARGIN }>
- $<text> = [
- [<!before '=' \w> \N+]+ % [<pod_newline> $<spaces>]
- ]
- }
-
- token pod_formatting_code {
- $<code>=<[A..Z]>
- '<' { $*POD_IN_FORMATTINGCODE := 1 }
- $<content>=[ <!before '>'> <pod_string_character> ]+
- '>' { $*POD_IN_FORMATTINGCODE := 0 }
- }
-
- token pod_string {
- <pod_string_character>+
- }
-
- token pod_string_character {
- <pod_formatting_code> || $<char>=[ \N || [
- <?{ $*POD_IN_FORMATTINGCODE == 1}> \n <!before \h* '=' \w>
- ]
- ]
- }
-
- proto token pod_block { <...> }
-
- token pod_configuration($spaces = '') {
- [ [\n $spaces '=']? \h+ <colonpair> ]*
- }
-
- token pod_block:sym<delimited_raw> {
- ^^
- $<spaces> = [ \h* ]
- '=begin' \h+ $<type>=[ 'code' | 'comment' ] {}
- <pod_configuration($<spaces>)> <pod_newline>+
- [
- $<pod_content> = [ .*? ]
- ^^ $<spaces> '=end' \h+ $<type> <pod_newline>
- || <.typed_panic: 'X::Syntax::Pod::BeginWithoutEnd'>
- ]
- }
-
- token pod_block:sym<delimited> {
- ^^
- $<spaces> = [ \h* ]
- '=begin'
- [ <?before <pod_newline>>
- <.typed_panic('X::Syntax::Pod::BeginWithoutIdentifier')>
- ]?
- \h+ <!before 'END'>
- {
- $*VMARGIN := $<spaces>.to - $<spaces>.from;
- }
- :my $*ALLOW_CODE := 0;
- $<type> = [
- <pod_code_parent> { $*ALLOW_CODE := 1 }
- || <identifier>
- ]
- <pod_configuration($<spaces>)> <pod_newline>+
- [
- <pod_content> *
- ^^ $<spaces> '=end' \h+ $<type> <pod_newline>
- || <.typed_panic: 'X::Syntax::Pod::BeginWithoutEnd'>
- ]
- }
-
-
- token pod_block:sym<delimited_table> {
- ^^
- $<spaces> = [ \h* ]
- '=begin' \h+ 'table'
- <pod_configuration($<spaces>)> <pod_newline>+
- [
- <table_row>*
- ^^ \h* '=end' \h+ 'table' <pod_newline>
- || <.typed_panic: 'X::Syntax::Pod::BeginWithoutEnd'>
- ]
- }
-
- token table_row {
- \h* <!before '=' \w> \N* \n
- }
-
- token pod_block:sym<end> {
- ^^ \h*
- [
- | '=begin' \h+ 'END' <pod_newline>
- | '=for' \h+ 'END' <pod_newline>
- | '=END' <pod_newline>
- ]
- .*
- }
-
- token pod_block:sym<paragraph> {
- ^^
- $<spaces> = [ \h* ]
- '=for' \h+ <!before 'END'>
- {
- $*VMARGIN := $<spaces>.to - $<spaces>.from;
- }
- :my $*ALLOW_CODE := 0;
- $<type> = [
- <pod_code_parent> { $*ALLOW_CODE := 1 }
- || <identifier>
- ]
- <pod_configuration($<spaces>)> <pod_newline>
- $<pod_content> = <pod_textcontent>?
- }
-
- token pod_block:sym<paragraph_raw> {
- ^^
- $<spaces> = [ \h* ]
- '=for' \h+ $<type>=[ 'code' | 'comment' ]
- <pod_configuration($<spaces>)> <pod_newline>
- $<pod_content> = [ \h* <!before '=' \w> \N+ \n ]+
- }
-
- token pod_block:sym<paragraph_table> {
- ^^
- $<spaces> = [ \h* ]
- '=for' \h+ 'table'
- <pod_configuration($<spaces>)> <pod_newline>
- [ <!before \h* \n> <table_row>]*
- }
-
- token pod_block:sym<abbreviated> {
- ^^
- $<spaces> = [ \h* ]
- '=' <!before begin || end || for || END || config>
- {
- $*VMARGIN := $<spaces>.to - $<spaces>.from;
- }
- :my $*ALLOW_CODE := 0;
- $<type> = [
- <pod_code_parent> { $*ALLOW_CODE := 1 }
- || <identifier>
- ]
- <pod_configuration($<spaces>)>
- [\r\n|\s]
- $<pod_content> = <pod_textcontent>?
- }
-
- token pod_block:sym<abbreviated_raw> {
- ^^
- $<spaces> = [ \h* ]
- '=' $<type>=[ 'code' | 'comment' ]
- <pod_configuration($<spaces>)> [\r\n|\s]
- $<pod_content> = [ \h* <!before '=' \w> \N+ \n ]*
- }
-
- token pod_block:sym<abbreviated_table> {
- ^^
- $<spaces> = [ \h* ]
- '=table' <pod_configuration($<spaces>)> <pod_newline>
- [ <!before \h* \n> <table_row>]*
- }
-
- token pod_newline {
- \h* \n
- }
-
- token pod_code_parent {
- 'pod' <!before \w> || 'item' \d* <!before \w>
- # TODO: Also Semantic blocks one day
- }
-
- token install_doc_phaser { <?> }
-
- token vnum {
- \d+ | '*'
- }
-
- token version {
- 'v' <?before \d> {} $<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_DECL; # what declaration we're in
- :my $*HAS_SELF := ''; # is 'self' available? (for $.foo style calls)
- :my $*MONKEY_TYPING := 0; # whether augment/supersede are allowed
- :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 $*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
-
- # 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 compositite 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 %*METAOPGEN; # hash of generated metaops
- :my %*HANDLERS; # block exception handlers
- :my $*IMPLICIT; # whether we allow an implicit param
- :my $*FORBID_PIR := 0; # whether pir::op and Q:PIR { } are disallowed
- :my $*HAS_YOU_ARE_HERE := 0; # whether {YOU_ARE_HERE} has shown up
- :my $*OFTYPE;
- :my $*VMARGIN := 0; # pod stuff
- :my $*ALLOW_CODE := 0; # pod stuff
- :my $*POD_IN_FORMATTINGCODE := 0; # pod stuff
- :my $*IN_REGEX_ASSERTION := 0;
- :my $*SOFT := 0; # is the soft pragma in effect
- :my $*IN_PROTO := 0; # are we inside a proto?
-
- # Various interesting scopes we'd like to keep to hand.
- :my $*GLOBALish;
- :my $*PACKAGE;
- :my $*SETTING;
- :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;
-
- # Quasis and unquotes
- :my $*IN_QUASI := 0; # whether we're currently in a quasi block
-
- # Setting loading and symbol setup.
- {
- # Create unit outer (where we assemble any lexicals accumulated
- # from e.g. REPL) and the real UNIT.
- $*UNIT_OUTER := $*W.push_lexpad($/);
- $*UNIT := $*W.push_lexpad($/);
- $*UNIT<IN_DECL> := 'mainline';
-
- # If we already have a specified outer context, then that's
- # our setting. Otherwise, load one.
- my $have_outer := nqp::defined(%*COMPILING<%?OPTIONS><outer_ctx>);
- unless $have_outer {
- $*SETTING := $*W.load_setting($/, %*COMPILING<%?OPTIONS><setting> // 'CORE');
- }
- $/.CURSOR.unitstart();
- try {
- my $EXPORTHOW := $*W.find_symbol(['EXPORTHOW']);
- for $EXPORTHOW.WHO {
- %*HOW{$_.key} := $_.value;
- }
- }
-
- # Create GLOBAL(ish), unless we were given one.
- if nqp::existskey(%*COMPILING<%?OPTIONS>, 'global') {
- $*GLOBALish := %*COMPILING<%?OPTIONS><global>;
- }
- elsif $have_outer && $*UNIT_OUTER.symbol('GLOBALish') {
- $*GLOBALish := $*UNIT_OUTER.symbol('GLOBALish')<value>;
- }
- else {
- $*GLOBALish := $*W.pkg_create_mo($/, %*HOW<package>, :name('GLOBAL'));
- $*W.pkg_compose($*GLOBALish);
- }
-
- # Create or pull in existing EXPORT.
- if $have_outer && $*UNIT_OUTER.symbol('EXPORT') {
- $*EXPORT := $*UNIT_OUTER.symbol('EXPORT')<value>;
- }
- else {
- $*EXPORT := $*W.pkg_create_mo($/, %*HOW<package>, :name('EXPORT'));
- $*W.pkg_compose($*EXPORT);
- }
-
- # If there's a self in scope, set $*HAS_SELF.
- if $have_outer && $*UNIT_OUTER.symbol('self') {
- $*HAS_SELF := 'complete';
- }
-
- # Take current package from outer context if any, otherwise for a
- # fresh compilation unit we start in GLOBAL.
- if $have_outer && $*UNIT_OUTER.symbol('$?PACKAGE') {
- $*PACKAGE := $*UNIT_OUTER.symbol('$?PACKAGE')<value>;
- }
- else {
- $*PACKAGE := $*GLOBALish;
- }
-
- # If we're eval'ing in the context of a %?LANG, set up our own
- # %*LANG based on it.
- if $have_outer && $*UNIT_OUTER.symbol('%?LANG') {
- for $*UNIT_OUTER.symbol('%?LANG')<value>.FLATTENABLE_HASH() {
- %*LANG{$_.key} := $_.value;
- }
- }
-
- # Install unless we've no setting, in which case we've likely no
- # static lexpad class yet either. Also, UNIT needs a code object.
- unless %*COMPILING<%?OPTIONS><setting> eq 'NULL' {
- $*W.install_lexical_symbol($*UNIT, 'GLOBALish', $*GLOBALish);
- $*W.install_lexical_symbol($*UNIT, 'EXPORT', $*EXPORT);
- $*W.install_lexical_symbol($*UNIT, '$?PACKAGE', $*PACKAGE);
- $*W.install_lexical_symbol($*UNIT, '::?PACKAGE', $*PACKAGE);
- $*DECLARAND := $*W.stub_code_object('Block');
- }
- my $M := %*COMPILING<%?OPTIONS><M>;
- if nqp::defined($M) {
- for nqp::islist($M) ?? $M !! [$M] -> $longname {
- my $module := $*W.load_module($/,
- $longname,
- $*GLOBALish);
- do_import($/, $module, $longname);
- $/.CURSOR.import_EXPORTHOW($module);
- }
- }
- }
-
- <.finishpad>
- <.bom>?
- <statementlist>
-
- <.install_doc_phaser>
-
- [ $ || <.typed_panic: 'X::Syntax::Confused'> ]
-
- {
- # Emit any errors/worries.
- self.explain_mystery();
- if @*SORROWS {
- if +@*SORROWS == 1 && !@*WORRIES {
- @*SORROWS[0].throw()
- }
- else {
- $*W.group_exception(@*SORROWS.pop).throw();
- }
- }
- if @*WORRIES {
- pir::getstderr__P().print($*W.group_exception().gist());
- }
-
- # Install POD-related variables.
- $*POD_PAST := $*W.add_constant(
- 'Array', 'type_new', |$*POD_BLOCKS
- );
- $*W.install_lexical_symbol(
- $*UNIT, '$=pod', $*POD_PAST.compile_time_value
- );
-
- # Tag UNIT with a magical lexical. Also if we're compiling CORE,
- # give it such a tag too.
- if %*COMPILING<%?OPTIONS><setting> eq 'NULL' {
- $*W.install_lexical_symbol($*UNIT, '!CORE_MARKER',
- $*W.pkg_create_mo($/, %*HOW<package>, :name('!CORE_MARKER')));
- }
- else {
- $*W.install_lexical_symbol($*UNIT, '!UNIT_MARKER',
- $*W.pkg_create_mo($/, %*HOW<package>, :name('!UNIT_MARKER')));
- }
- }
-
- # CHECK time.
- { $*W.CHECK(); }
- }
-
- method import_EXPORTHOW($UNIT) {
- # See if we've exported any HOWs.
- if nqp::existskey($UNIT, 'EXPORTHOW') {
- for $UNIT<EXPORTHOW>.WHO {
- %*HOW{$_.key} := pir::nqp_decontainerize__PP($_.value);
- }
- }
- }
-
- token statementlist {
- :my %*LANG := self.shallow_copy(pir::find_dynamic_lex__Ps('%*LANG'));
- :my %*HOW := self.shallow_copy(pir::find_dynamic_lex__Ps('%*HOW'));
- :dba('statement list')
- :s
- [
- | $
- | <?before <[\)\]\}]>>
- | [<statement><.eat_terminator> ]*
- ]
- }
-
- method shallow_copy(%hash) {
- my %result;
- for %hash {
- %result{$_.key} := $_.value;
- }
- %result
- }
-
- rule semilist {
- :dba('semicolon list')
- [
- | <?before <[)\]}]> >
- | [<statement><.eat_terminator> ]*
- ]
- }
-
- token statement {
- :my $*QSIGIL := '';
- :my $*SCOPE := '';
- :my $*ACTIONS := %*LANG<MAIN-actions>;
- <!before <[\])}]> | $ >
- <!stopper>
- <!!{ nqp::rebless($/.CURSOR, %*LANG<MAIN>) }>
- [
- | <statement_control>
- | <EXPR> :dba('statement end')
- [
- || <?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>[0]<sym>;
- $/.CURSOR.obs("do..." ~ $s, "repeat..." ~ $s);
- }
- }
- ]?
- | <?before ';'>
- | <?before <stopper> >
- | {} <.panic: "Bogus statement">
- ]
- }
-
- token eat_terminator {
- || ';'
- || <?MARKED('endstmt')>
- || <?before ')' | ']' | '}' >
- || $
- || <?stopper>
- || <.typed_panic: 'X::Syntax::Confused'>
- }
-
- token xblock($*IMPLICIT = 0) {
- :my $*GOAL := '{';
- <EXPR> <.ws> <pblock($*IMPLICIT)>
- }
-
- token pblock($*IMPLICIT = 0) {
- :my $*DECLARAND := $*W.stub_code_object('Block');
- :dba('parameterized block')
- [
- | <lambda>
- <.newpad>
- :my $*SCOPE := 'my';
- <signature>
- <blockoid>
- | <?[{]>
- <.newpad>
- <blockoid>
- || <.missing: 'block'>
- ]
- }
-
- token lambda { '->' | '<->' }
-
- token block($*IMPLICIT = 0) {
- :my $*DECLARAND := $*W.stub_code_object('Block');
- :dba('scoped block')
- [ <?[{]> || <.missing: 'block'>]
- <.newpad>
- <blockoid>
- }
-
- token blockoid {
- :my $*CURPAD;
- :my %*HANDLERS;
- <.finishpad>
- [
- | '{YOU_ARE_HERE}' <you_are_here>
- | :dba('block') '{' ~ '}' <statementlist> <?ENDSTMT>
- | <?terminator> { $*W.throw($/, 'X::Syntax::Missing', what =>'block') }
- | <?> { $*W.throw($/, 'X::Syntax::Missing', what => 'block') }
- ]
- { $*CURPAD := $*W.pop_lexpad() }
- }
-
- token unitstart { <?> }
- token you_are_here { <?> }
- token newpad { <?> { $*W.push_lexpad($/) } }
- 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' <.end_keyword> }
- token terminator:sym<unless> { 'unless' <.end_keyword> }
- token terminator:sym<while> { 'while' <.end_keyword> }
- token terminator:sym<until> { 'until' <.end_keyword> }
- token terminator:sym<for> { 'for' <.end_keyword> }
- token terminator:sym<given> { 'given' <.end_keyword> }
- token terminator:sym<when> { 'when' <.end_keyword> }
-
- token stdstopper { <?terminator> }
-
- ## Statement control
-
- proto token statement_control { <...> }
-
- token statement_control:sym<if> {
- <sym> <.end_keyword> :s
- <xblock>
- [ 'elsif'\s <xblock> ]*
- [ 'else'\s <else=.pblock> ]?
- }
-
- token statement_control:sym<unless> {
- <sym> <.end_keyword> :s
- <xblock>
- [ <!before 'else'> || <.typed_panic: 'X::Syntax::UnlessElse'> ]
- }
-
- token statement_control:sym<while> {
- $<sym>=[while|until] <.end_keyword> :s
- <xblock>
- }
-
- token statement_control:sym<repeat> {
- <sym> <.end_keyword> :s
- [
- | $<wu>=[while|until]\s <xblock>
- | <pblock>
- [$<wu>=['while'|'until']\s || <.missing('"while" or "until"')>]
- <EXPR>
- ]
- }
-
- token statement_control:sym<for> {
- <sym> <.end_keyword> :s
- [ <?before 'my'? '$'\w+ '(' >
- <.typed_panic: 'X::Syntax::P5'> ]?
- [ <?before '(' <.EXPR>? ';' <.EXPR>? ';' <.EXPR>? ')' >
- <.obs('C-style "for (;;)" loop', '"loop (;;)"')> ]?
- <xblock(1)>
- }
-
- token statement_control:sym<foreach> {
- <sym> <.end_keyword> <.obs("'foreach'", "'for'")>
- }
-
- token statement_control:sym<loop> {
- <sym> <.end_keyword>
- [ <?[({]> <.sorry: "Whitespace required after 'loop'"> ]?
- :s
- [ '('
- <e1=.EXPR>? ';'
- <e2=.EXPR>? ';'
- <e3=.EXPR>?
- ')' ]?
- <block>
- }
-
- token statement_control:sym<need> {
- <sym> <.ws>
- [
- | <version>
- | <module_name>
- ]+ % ','
- {
- for $<module_name> {
- $*W.load_module($/, ~$_<longname>, $*GLOBALish);
- }
- }
- }
-
- token statement_control:sym<import> {
- <sym> <.ws>
- <module_name> [ <.spacey> <arglist> ]? <.ws>
- :my $*HAS_SELF := '';
- {
- my $longname := $*W.disect_longname($<module_name><longname>);
- my $module;
- my $found := 0;
- try { $module := $*W.find_symbol($longname.components()); $found := 1; }
- if $found {
- # todo: fix arglist
- my $arglist;
- if $<arglist> {
- $arglist := $*W.compile_time_evaluate($/, $<arglist>[0]<EXPR>.ast);
- $arglist := nqp::getattr($arglist.list.eager,
- $*W.find_symbol(['List']), '$!items');
- }
- do_import($/, $module.WHO, ~$<module_name><longname>, $arglist);
- }
- else {
- $/.CURSOR.panic("Could not find module " ~ ~$<module_name> ~
- " to import symbols from");
- }
- }
- }
-
- token statement_control:sym<use> {
- :my $longname;
- :my $*IN_DECL := 'use';
- :my $*HAS_SELF := '';
- :my $*SCOPE := 'use';
- $<doc>=[ 'DOC' \h+ ]?
- <sym> <.ws>
- [
- | <version>
- | <module_name>
- {
- $longname := $<module_name><longname>;
-
- # Some modules are handled in the actions are just turn on a
- # setting of some kind.
- if $longname.Str eq 'MONKEY_TYPING' {
- $*MONKEY_TYPING := 1;
- $longname := "";
- }
- elsif $longname.Str eq 'soft' {
- # This is an approximation; need to pay attention to argument
- # list really.
- $*SOFT := 1;
- $longname := "";
- }
- elsif $longname.Str eq 'FORBID_PIR' ||
- $longname.Str eq 'Devel::Trace' ||
- $longname.Str eq 'fatal' {
- $longname := "";
- }
- }
- [
- || <.spacey> <arglist> <?{ $<arglist><EXPR> }>
- {
- my $arglist := $*W.compile_time_evaluate($/,
- $<arglist><EXPR>.ast);
- $arglist := nqp::getattr($arglist.list.eager,
- $*W.find_symbol(['List']), '$!items');
- my $module := $*W.load_module($/,
- ~$longname,
- $*GLOBALish);
- do_import($/, $module, ~$longname, $arglist);
- $/.CURSOR.import_EXPORTHOW($module);
- }
- || {
- unless ~$<doc> && !%*COMPILING<%?OPTIONS><doc> {
- if $longname {
- my $module := $*W.load_module($/,
- ~$longname,
- $*GLOBALish);
- do_import($/, $module, ~$longname);
- $/.CURSOR.import_EXPORTHOW($module);
- }
- }
- }
- ]
- ]
- <.ws>
- }
-
- sub do_import($/, $module, $package_source_name, $arglist?) {
- if nqp::existskey($module, 'EXPORT') {
- my $EXPORT := $module<EXPORT>.WHO;
- my @to_import := ['MANDATORY'];
- my @positional_imports := [];
- if nqp::defined($arglist) {
- my $Pair := $*W.find_symbol(['Pair']);
- for $arglist -> $tag {
- if nqp::istype($tag, $Pair) {
- $tag := nqp::unbox_s($tag.key);
- if nqp::existskey($EXPORT, $tag) {
- $*W.import($/, $EXPORT{$tag}, $package_source_name);
- }
- else {
- nqp::die("Error while importing from '$package_source_name': no such tag '$tag'");
-
- }
- }
- else {
- nqp::push(@positional_imports, $tag);
- }
- }
- }
- else {
- nqp::push(@to_import, 'DEFAULT');
- }
- for @to_import -> $tag {
- if nqp::existskey($EXPORT, $tag) {
- $*W.import($/, $EXPORT{$tag}, $package_source_name);
- }
- }
- if +@positional_imports {
- if nqp::existskey($module, '&EXPORT') {
- $module<&EXPORT>(|@positional_imports);
- }
- else {
- nqp::die("Error while importing from '$package_source_name': no EXPORT sub, but you provided positional argument in the 'use' statement");
- }
- }
- }
- }
-
- rule statement_control:sym<require> {
- <sym>
- [
- | <module_name> <EXPR>?
- | <EXPR>
- ]
- }
-
- token statement_control:sym<given> {
- <sym> <.end_keyword> :s <xblock(1)>
- }
- token statement_control:sym<when> {
- <sym> <.end_keyword> :s <xblock>
- }
- rule statement_control:sym<default> {
- <sym><.end_keyword> <block>
- }
-
- rule statement_control:sym<CATCH> {<sym> <block(1)> }
- rule statement_control:sym<CONTROL> {<sym> <block(1)> }
-
- proto token statement_prefix { <...> }
- token statement_prefix:sym<BEGIN> { <sym> <blorst> }
- token statement_prefix:sym<CHECK> { <sym> <blorst> }
- token statement_prefix:sym<INIT> { <sym> <blorst> }
- token statement_prefix:sym<START> { <sym> <blorst> }
- token statement_prefix:sym<ENTER> { <sym> <blorst> }
- token statement_prefix:sym<FIRST> { <sym> <blorst> }
-
- token statement_prefix:sym<END> { <sym> <blorst> }
- token statement_prefix:sym<LEAVE> { <sym> <blorst> }
- token statement_prefix:sym<KEEP> { <sym> <blorst> }
- token statement_prefix:sym<UNDO> { <sym> <blorst> }
- token statement_prefix:sym<NEXT> { <sym> <blorst> }
- token statement_prefix:sym<LAST> { <sym> <blorst> }
- token statement_prefix:sym<PRE> { <sym> <blorst> }
- token statement_prefix:sym<POST> { <sym> <blorst> }
-
- token statement_prefix:sym<sink> { <sym> <blorst> }
- token statement_prefix:sym<try> { <sym> <blorst> }
- token statement_prefix:sym<gather>{ <sym> <blorst> }
- token statement_prefix:sym<do> { <sym> <blorst> }
- token statement_prefix:sym<DOC> {
- <sym> \s <.ws> $<phase>=['BEGIN' || 'CHECK' || 'INIT']
- <blorst>
- }
-
- token blorst {
- \s <.ws> [ <?[{]> <block> | <statement> ]
- }
-
- ## Statement modifiers
-
- proto token statement_mod_cond { <...> }
-
- rule modifier_expr { <EXPR> }
-
- token statement_mod_cond:sym<if> { <sym> <modifier_expr> }
- token statement_mod_cond:sym<unless> { <sym> <modifier_expr> }
- token statement_mod_cond:sym<when> { <sym> <modifier_expr> }
-
- proto token statement_mod_loop { <...> }
-
- token statement_mod_loop:sym<while> { <sym> :s <smexpr=.EXPR> }
- token statement_mod_loop:sym<until> { <sym> :s <smexpr=.EXPR> }
- token statement_mod_loop:sym<for> { <sym> :s <smexpr=.EXPR> }
- token statement_mod_loop:sym<given> { <sym> :s <smexpr=.EXPR> }
-
- ## Terms
-
- token term:sym<fatarrow> { <fatarrow> }
- token term:sym<colonpair> { <colonpair> }
- token term:sym<variable> { <variable> { $*VAR := $<variable> } }
- 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> <.NYI('HyperWhatever (**)')> }
- token term:sym<*> { <sym> }
- token term:sym<lambda> { <?lambda> <pblock> }
- token term:sym<type_declarator> { <type_declarator> }
- token term:sym<value> { <value> }
- token term:sym<unquote> { '{{{' <?{ $*IN_QUASI }> <statementlist> '}}}' }
-
- # XXX temporary Bool::True/Bool::False until we can get a permanent definition
- token term:sym<boolean> { 'Bool::'? $<value>=[True|False] » }
-
- token term:sym<::?IDENT> {
- $<sym> = [ '::?' <identifier> ] »
- }
-
- token term:sym<undef> {
- <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 function or assignment of Nil')>
- ]?
- <.obs('undef as a value', "something more specific:\n\tMu (the \"most undefined\" type object),\n\tan undefined type object such as Int,\n\tNil as an empty list,\n\t!*.defined as a matcher or method,\n\tAny:U as a type constraint\n\tor fail() as a failure return\n\t ")>
- }
-
- token term:sym<new> {
- 'new' \h+ <longname> \h* <!before ':'> <.obs("C++ constructor syntax", "method call syntax")>
- }
-
- token fatarrow {
- <key=.identifier> \h* '=>' <.ws> <val=.EXPR('i=')>
- }
-
- token colonpair {
- :my $*key;
- :my $*value;
-
- ':'
- :dba('colon pair')
- [
- | '!' [ <identifier> || <.panic: "Malformed False pair; expected identifier"> ]
- [ <[ \[ \( \< \{ ]> {
- $/.CURSOR.typed_panic('X::Syntax::NegatedPair', key => ~$<identifier>) } ]?
- { $*key := $<identifier>.Str; $*value := 0; }
- | <identifier>
- { $*key := $<identifier>.Str; }
- [
- || <.unsp>? :dba('pair value') <circumfix> { $*value := $<circumfix>; }
- || { $*value := 1; }
- ]
- | :dba('signature') '(' ~ ')' <fakesignature>
- | <circumfix>
- { $*key := ""; $*value := $<circumfix>; }
- | <var=.colonpair_variable>
- { $*key := $<var><desigilname>.Str; $*value := $<var>; self.check_variable($*value); }
- ]
- }
-
- token colonpair_variable {
- <sigil> {} <twigil>? <desigilname>
- }
-
- proto token special_variable { <...> }
-
- token special_variable:sym<$!{ }> {
- '$!{' .*? '}'
- <.obs('${ ... } or %! variable', 'smart match against $!')>
- }
-
- token special_variable:sym<$~> {
- <sym> <?before \s | ',' | '=' <terminator> >
- <.obs('$~ variable', 'Form module')>
- }
-
- token special_variable:sym<$`> {
- <sym> <?before \s | ',' | <terminator> >
- <.obs('$` variable', 'explicit pattern before <(')>
- }
-
- token special_variable:sym<$@> {
- <sym> <?before \W>
- <.obs('$@ variable as eval error', '$!')>
- }
-
- # TODO: use actual variable in error message
- token special_variable:sym<$#> {
- <sym>
- [
- || \w+ <.obs('$#variable', '@variable.end')>
- || <.obs('$# variable', '.fmt')>
- ]
- }
-
- token special_variable:sym<$$> {
- <sym> <!alpha> <?before \s | ',' | <terminator> >
- <.obs('$$ variable', '$*PID')>
- }
- token special_variable:sym<$%> {
- <sym> <!before \w> <!sigil>
- <.obs('$% variable', 'Form module')>
- }
-
- # TODO: $^X and other "caret" variables
-
- token special_variable:sym<$^> {
- <sym> <?before \s | ',' | '=' | <terminator> >
- <.obs('$^ variable', 'Form module')>
- }
-
- token special_variable:sym<$&> {
- <sym> <?before \s | ',' | <terminator> >
- <.obs('$& variable', '$/ or $()')>
- }
-
- token special_variable:sym<$*> {
- <sym> <?before \s | ',' | '=' | <terminator> >
- <.obs('$* variable', '^^ and $$')>
- }
-
- token special_variable:sym<$)> {
- <sym> <?{ $*GOAL ne ')' }> <?before \s | ',' | <terminator> >
- <.obs('$) variable', '$*EGID')>
- }
-
- token special_variable:sym<$-> {
- <sym> <?before \s | ',' | '=' | <terminator> >
- <.obs('$- variable', 'Form module')>
- }
-
- token special_variable:sym<$=> {
- <sym> <?before \s | ',' | '=' | <terminator> >
- <.obs('$= variable', 'Form module')>
- }
-
- token special_variable:sym<@+> {
- <sym> <?before \s | ',' | <terminator> >
- <.obs('@+ variable', '.to method')>
- }
-
- token special_variable:sym<%+> {
- <sym> <?before \s | ',' | <terminator> >
- <.obs('%+ variable', '.to method')>
- }
-
- token special_variable:sym<$+[ ]> {
- '$+['
- <.obs('@+ variable', '.to method')>
- }
-
- token special_variable:sym<@+[ ]> {
- '@+['
- <.obs('@+ variable', '.to method')>
- }
-
- token special_variable:sym<@+{ }> {
- '@+{'
- <.obs('%+ variable', '.to method')>
- }
-
- token special_variable:sym<@-> {
- <sym> <?before \s | ',' | <terminator> >
- <.obs('@- variable', '.from method')>
- }
-
- token special_variable:sym<%-> {
- <sym> <?before \s | ',' | <terminator> >
- <.obs('%- variable', '.from method')>
- }
-
- token special_variable:sym<$-[ ]> {
- '$-['
- <.obs('@- variable', '.from method')>
- }
-
- token special_variable:sym<@-[ ]> {
- '@-['
- <.obs('@- variable', '.from method')>
- }
-
- token special_variable:sym<%-{ }> {
- '@-{'
- <.obs('%- variable', '.from method')>
- }
-
- token special_variable:sym<$+> {
- <sym> <?before \s | ',' | <terminator> >
- <.obs('$+ variable', 'Form module')>
- }
-
- token special_variable:sym<$[> {
- <sym> <?before \s | ',' | '=' | <terminator> >
- <.obs('$[ variable', 'user-defined array indices')>
- }
-
- token special_variable:sym<$]> {
- <sym> <?before \s | ',' | <terminator> >
- <.obs('$] variable', '$*PERL_VERSION')>
- }
-
- token special_variable:sym<$\\> {
- '$\\' <?before \s | ',' | '=' | <terminator> >
- <.obs('$\\ variable', "the filehandle's :ors attribute")>
- }
-
- token special_variable:sym<$|> {
- <sym> <?before \s | ',' | '=' | <terminator> >
- <.obs('$| variable', ':autoflush on open')>
- }
-
- token special_variable:sym<$:> {
- <sym> <?before <[\x20\t\n\],=)}]> >
- <.obs('$: variable', 'Form module')>
- }
-
- token special_variable:sym<$;> {
- <sym> <?before \s | ',' | '=' | <terminator> >
- <.obs('$; variable', 'real multidimensional hashes')>
- }
-
- token special_variable:sym<$'> { #'
- <sym> <?before \s | ',' | <terminator> >
- <.obs('$' ~ "'" ~ 'variable', "explicit pattern after )\x3E")>
- }
-
- # TODO: $"
-
- token special_variable:sym<$,> {
- <sym> <?before \s | ',' | <terminator> >
- <.obs('$, variable', ".join() method")>
- }
-
- token special_variable:sym['$<'] {
- <sym> <?before \h* <[ = , ; ? : ! ) \] } ]> <!before \S* '>'> >
- <.obs('$< variable', '$*UID')>
- }
-
- token special_variable:sym«\$>» {
- <sym> {} <?before \s | ',' | <terminator> >
- <.obs('$> variable', '$*EUID')>
- }
-
- token special_variable:sym<$.> {
- <sym> {} <?before \s | ',' | <terminator> >
- <.obs('$. variable', "the filehandle's .line method")>
- }
-
- token special_variable:sym<$?> {
- <sym> {} <?before \s | ',' | <terminator> >
- <.obs('$? variable as child error', '$!')>
- }
-
- regex special_variable:sym<${ }> {
- <sigil> '{' {} $<text>=[.*?] '}'
- <?{
- my $sigil := $<sigil>.Str;
- my $text := $<text>.Str;
- my $bad := $sigil ~ '{' ~ $text ~ '}';
- $text := $text - 1 if $text ~~ /^\d+$/ && $text > 0;
- if !($text ~~ /^(\w|\:)+$/) {
- if $*QSIGIL {
- 0
- }
- else {
- $/.CURSOR.obs($bad, $sigil ~ '(' ~ $text ~ ')');
- }
- }
- elsif $*QSIGIL {
- $/.CURSOR.obs($bad, '{' ~ $sigil ~ $text ~ '}');
- }
- else {
- $/.CURSOR.obs($bad, $sigil ~ $text);
- }
- }>
- }
-
- token desigilname {
- [
- | <?[$]>
- [ <?{ $*IN_DECL }> <.typed_panic: 'X::Syntax::Variable::IndirectDeclaration'> ]?
- <variable> {
- $*VAR := $<variable>;
- self.check_variable($*VAR);
- }
- | <?before <[\@\%\&]> <sigil>* \w > <.panic: "Invalid hard reference syntax">
- | <longname>
- ]
- }
-
- token variable {
- <?before <sigil> {
- unless $*LEFTSIGIL {
- $*LEFTSIGIL := $<sigil>.Str;
- }
- }> {}
- [
- || '&'
- [
- | :dba('infix noun') '[' ~ ']' <infixish>
- ]
- || [
- | <sigil> <twigil>? <desigilname>
- | <special_variable>
- | <sigil> $<index>=[\d+] [ <?{ $*IN_DECL}> <.typed_panic: "X::Syntax::Variable::Numeric">]?
- | <sigil> <?[<[]> [ <?{ $*IN_DECL }> <.typed_panic('X::Syntax::Variable::Match')>]? <postcircumfix>
- | $<sigil>=['$'] $<desigilname>=[<[/_!]>]
- | <sigil> <?{ $*IN_DECL }>
- | <!{ $*QSIGIL }> <.typed_panic: 'X::Syntax::SigilWithoutName'>
- ]
- ]
- [ <?{ $<twigil> && $<twigil>[0] eq '.' }>
- [ <.unsp> | '\\' | <?> ] <?before '('> <arglist=.postcircumfix>
- ]?
- }
-
- 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> }
-
- proto token package_declarator { <...> }
- token package_declarator:sym<package> {
- :my $*OUTERPACKAGE := $*PACKAGE;
- :my $*PKGDECL := 'package';
- <sym> <.end_keyword> <package_def>
- }
- token package_declarator:sym<module> {
- :my $*OUTERPACKAGE := $*PACKAGE;
- :my $*PKGDECL := 'module';
- <sym> <.end_keyword> <package_def>
- }
- token package_declarator:sym<class> {
- :my $*OUTERPACKAGE := $*PACKAGE;
- :my $*PKGDECL := 'class';
- <sym> <.end_keyword> <package_def>
- }
- token package_declarator:sym<grammar> {
- :my $*OUTERPACKAGE := $*PACKAGE;
- :my $*PKGDECL := 'grammar';
- <sym> <.end_keyword> <package_def>
- }
- token package_declarator:sym<role> {
- :my $*OUTERPACKAGE := $*PACKAGE;
- :my $*PKGDECL := 'role';
- <sym> <.end_keyword> <package_def>
- }
- token package_declarator:sym<knowhow> {
- :my $*OUTERPACKAGE := $*PACKAGE;
- :my $*PKGDECL := 'knowhow';
- <sym> <.end_keyword> <package_def>
- }
- token package_declarator:sym<native> {
- :my $*OUTERPACKAGE := $*PACKAGE;
- :my $*PKGDECL := 'native';
- <sym> <.end_keyword> <package_def>
- }
- token package_declarator:sym<slang> {
- :my $*OUTERPACKAGE := $*PACKAGE;
- :my $*PKGDECL := 'slang';
- <sym> <.end_keyword> <package_def>
- }
- token package_declarator:sym<trusts> {
- <sym> <.ws> <typename>
- }
- token package_declarator:sym<also> {
- <sym>:s
- [ <trait>+ || <.panic: "No valid trait found after also"> ]
- }
-
- rule package_def {
- :my $longname;
- :my $outer := $*W.cur_lexpad();
- :my $*DECLARAND;
- :my $*IN_DECL := 'package';
- :my $*HAS_SELF := '';
- :my $*CURPAD;
- :my $*DOC := $*DECLARATOR_DOCS;
- :my $*DOCEE;
- <.attach_docs>
-
- # Meta-object will live in here; also set default REPR (a trait
- # may override this, e.g. is repr('...')).
- :my $*PACKAGE;
- :my $*REPR;
-
- # Default to our scoped.
- { unless $*SCOPE { $*SCOPE := 'our'; } }
-
- [
- [ <longname> { $longname := $*W.disect_longname($<longname>[0]); } ]?
- <.newpad>
-
- [ :dba('generic role')
- <?{ ($*PKGDECL//'') eq 'role' }>
- { $*PACKAGE := $*OUTERPACKAGE } # in case signature tries to declare a package
- '[' ~ ']' <signature>
- { $*IN_DECL := ''; }
- ]?
-
- <trait>*
-
- {
- # Unless we're augmenting...
- if $*SCOPE ne 'augment' {
- # 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, $*OUTERPACKAGE, $outer, @name) {
- $*PACKAGE := $*W.find_symbol(@name);
- $exists := 1;
- }
- }
-
- # 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' || $*PACKAGE.HOW.is_composed($*PACKAGE) {
- $*W.throw($/, ['X', 'Redeclaration'],
- symbol => $longname.name(),
- );
- }
- }
-
- # 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> := $longname.name();
- }
- if $*REPR ne '' {
- %args<repr> := $*REPR;
- }
- $*PACKAGE := $*W.pkg_create_mo($/, %*HOW{$*PKGDECL}, |%args);
-
- # Install it in the symbol table if needed.
- if @name {
- $*W.install_package($/, @name, $*SCOPE, $*PKGDECL, $*OUTERPACKAGE, $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($/, %*HOW{'role-group'}, :name($longname.name()));
- $*W.install_package($/, @name, $*SCOPE, $*PKGDECL, $*OUTERPACKAGE, $outer, $group);
- }
-
- # Construct role meta-object with group.
- $*PACKAGE := $*W.pkg_create_mo($/, %*HOW{$*PKGDECL}, :name($longname.name()),
- :group($group), :signatured($<signature> ?? 1 !! 0));
- }
- }
- else {
- # Augment. Ensure we can.
- my @name := $longname ??
- $longname.type_name_parts('package name', :decl(1)) !!
- [];
- unless $*MONKEY_TYPING {
- $/.CURSOR.typed_panic('X::Syntax::Augment::WithoutMonkeyTyping');
- }
- if $*PKGDECL eq 'role' {
- $/.CURSOR.typed_panic('X::Syntax::Augment::Role',
- role-name => $longname.text);
- }
- unless @name {
- $*W.throw($/, 'X::Anon::Augment', package-kind => $*PKGDECL);
- }
-
- # Locate type.
- my $found;
- try { $*PACKAGE := $*W.find_symbol(@name); $found := 1 }
- unless $found {
- $*W.throw($/, 'X::Augment::NoSuchType',
- package-kind => $*PKGDECL,
- package => $longname.text(),
- );
- }
- }
-
- # Install $?PACKAGE, $?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 'class' || $*PKGDECL eq 'grammar' {
- $*W.install_lexical_symbol($curpad, '$?CLASS', $*PACKAGE);
- $*W.install_lexical_symbol($curpad, '::?CLASS', $*PACKAGE);
- }
- elsif $*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($/, %*HOW<generic>, :name('$?CLASS')));
- $*W.install_lexical_symbol($curpad, '::?CLASS',
- $*W.pkg_create_mo($/, %*HOW<generic>, :name('::?CLASS')));
- }
- }
-
- # Set declarand as the package.
- $*DECLARAND := $*PACKAGE;
-
- # Apply any traits.
- for $<trait> {
- my $applier := $_.ast;
- if $applier {
- $applier($*DECLARAND);
- }
- }
- }
-
- { nqp::push(@*PACKAGES, $*PACKAGE); }
- [
- || <?[{]>
- [
- {
- $*IN_DECL := '';
- $*begin_compunit := 0;
- }
- <blockoid>
- ]
-
- || ';'
- [
- || <?{ $*begin_compunit }>
- {
- unless $longname {
- $/.CURSOR.panic("Compilation unit cannot be anonymous");
- }
- unless $outer =:= $*UNIT {
- $/.CURSOR.panic("Semicolon form of " ~ $*PKGDECL ~ " definition not allowed in subscope;\n please use block form");
- }
- if $*PKGDECL eq 'package' {
- $/.CURSOR.panic('This appears to be Perl 5 code. If you intended it to be Perl 6 code, please use a Perl 6 style package block like "package Foo { ... }", or "module Foo; ...".');
- }
- $*begin_compunit := 0;
- }
- { $*IN_DECL := ''; }
- <.finishpad>
- <statementlist> # whole rest of file, presumably
- { $*CURPAD := $*W.pop_lexpad() }
- || <.panic("Too late for semicolon form of $*PKGDECL definition")>
- ]
- || <.panic("Unable to parse $*PKGDECL definition")>
- ]
- { nqp::pop(@*PACKAGES); }
- ] || { $/.CURSOR.malformed($*PKGDECL) }
- }
-
- token declarator {
- [
- # STD.pm6 uses <defterm> here, but we need different
- # action methods
- | '\\' <identifier> <.ws>
- [ <term_init=initializer> || <.sorry("Term definition requires an initializer")> ]
- | <variable_declarator>
- [
- || <?{ $*SCOPE eq 'has' }> <.newpad> <initializer>? { $*ATTR_INIT_BLOCK := $*W.pop_lexpad() }
- || <initializer>?
- ]
- | '(' ~ ')' <signature> <trait>* <.ws> <initializer>?
- | <routine_declarator>
- | <regex_declarator>
- | <type_declarator>
- ]
- }
-
- proto token multi_declarator { <...> }
- token multi_declarator:sym<multi> {
- <sym> :my $*MULTINESS := 'multi'; <.end_keyword>
- <.ws> [ <declarator> || <routine_def('sub')> || <.malformed('multi')> ]
- }
- token multi_declarator:sym<proto> {
- <sym> :my $*MULTINESS := 'proto'; :my $*IN_PROTO := 1; <.end_keyword>
- <.ws> [ <declarator> || <routine_def('sub')> || <.malformed('proto')> ]
- }
- token multi_declarator:sym<only> {
- <sym> :my $*MULTINESS := 'only'; <.end_keyword>
- <.ws> [ <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> {
- <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 scoped($*SCOPE) {
- <.end_keyword>
- :dba('scoped declarator')
- [
- :my $*DOC := $*DECLARATOR_DOCS;
- :my $*DOCEE;
- <.attach_docs>
- <.ws>
- [
- | <DECL=declarator>
- | <DECL=regex_declarator>
- | <DECL=package_declarator>
- | [<typename><.ws>]+
- {
- if +$<typename> > 1 {
- $/.CURSOR.NYI('Multiple prefix constraints');
- }
- $*OFTYPE := $<typename>[0];
- }
- <DECL=multi_declarator>
- | <DECL=multi_declarator>
- ] <.ws>
- || <?before <[A..Z]>><longname>{
- my $t := $<longname>.Str;
- $/.CURSOR.sorry("In \"$*SCOPE\" declaration, typename $t must be predeclared (or marked as declarative with :: prefix)");
- }
- <!> # drop through
- || <.malformed($*SCOPE)>
- ]
- }
-
- token variable_declarator {
- :my $*IN_DECL := 'variable';
- :my $var;
- <variable>
- {
- $var := $<variable>.Str;
- $/.CURSOR.add_variable($var);
- $*IN_DECL := '';
- }
- [
- <.unsp>?
- $<shape>=[
- | '(' ~ ')' <signature>
- {
- my $sigil := nqp::substr($var, 0, 1);
- 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> <.NYI: "Shaped variable declarations">
- | :dba('shape definition') '{' ~ '}' <semilist>
- | <?before '<'> <postcircumfix> <.NYI: "Shaped variable declarations">
- ]+
- ]?
- <.ws>
-
- <trait>*
- <post_constraint>*
- }
-
- proto token routine_declarator { <...> }
- token routine_declarator:sym<sub>
- { <sym> <.end_keyword> <routine_def('sub')> }
- token routine_declarator:sym<method>
- { <sym> <.end_keyword> <method_def('method')> }
- token routine_declarator:sym<submethod>
- { <sym> <.end_keyword> <method_def('submethod')> }
- token routine_declarator:sym<macro>
- { <sym> <.end_keyword> <macro_def()> }
-
- rule routine_def($d) {
- :my $*IN_DECL := $d;
- :my $*METHODTYPE;
- :my $*IMPLICIT := 0;
- :my $*DOC := $*DECLARATOR_DOCS;
- :my $*DOCEE;
- :my $*DECLARAND := $*W.stub_code_object('Sub');
- <.attach_docs>
- <deflongname>?
- {
- if $<deflongname> && $<deflongname>[0]<colonpair>[0]<circumfix><nibble> -> $cp {
- # It's an (potentially new) operator, circumfix, etc. that we
- # need to tweak into the grammar.
- my $category := $<deflongname>[0]<name>.Str;
- my $opname := $*W.colonpair_nibble_to_str($/, $cp);
- my $canname := $category ~ ":sym<" ~ $opname ~ ">";
- $/.CURSOR.add_categorical($category, $opname, $canname, $<deflongname>[0].ast, $*DECLARAND);
- }
- }
- <.newpad>
- [ '(' <multisig> ')' ]?
- <trait>*
- { $*IN_DECL := ''; }
- [
- || <onlystar>
- || <blockoid>
- ]
- }
-
- rule method_def($d) {
- :my $*IN_DECL := $d;
- :my $*METHODTYPE := $d;
- :my $*HAS_SELF := $d eq 'submethod' ?? 'partial' !! 'complete';
- :my $*DOC := $*DECLARATOR_DOCS;
- :my $*DOCEE;
- :my $*DECLARAND := $*W.stub_code_object($d eq 'submethod' ?? 'Submethod' !! 'Method');
- <.attach_docs>
- [
- <.newpad>
- [
- | $<specials>=[<[ ! ^ ]>?]<longname> [ '(' <multisig> ')' ]? <trait>*
- | '(' <multisig> ')' <trait>*
- | <sigil>'.':!s
- :dba('subscript signature')
- [
- | '(' ~ ')' <multisig>
- | '[' ~ ']' <multisig>
- | '{' ~ '}' <multisig>
- ]:s
- <trait>*
- | <?>
- ]
- { $*IN_DECL := ''; }
- [
- || <onlystar>
- || <blockoid>
- ]
- ] || <.malformed('method')>
- }
-
- rule macro_def() {
- :my $*IN_DECL := 'macro';
- :my $*IMPLICIT := 0;
- :my $*DOC := $*DECLARATOR_DOCS;
- :my $*DOCEE;
- :my $*DECLARAND := $*W.stub_code_object('Macro');
- <.attach_docs>
- <deflongname>?
- {
- if $<deflongname> && $<deflongname>[0]<colonpair>[0]<circumfix><nibble> -> $cp {
- # It's an (potentially new) operator, circumfix, etc. that we
- # need to tweak into the grammar.
- my $category := $<deflongname>[0]<name>.Str;
- my $opname := $*W.colonpair_nibble_to_str($/, $cp);
- my $canname := $category ~ ":sym<" ~ $opname ~ ">";
- $/.CURSOR.add_categorical($category, $opname, $canname, $<deflongname>[0].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 {
- '\\'
- [
- | '(' <capture>? ')'
- | <?before \S> <termish>
- | {} <.panic: "You can't backslash that">
- ]
- }
-
- rule capture {
- <EXPR>
- }
-
- rule param_sep {
- $<sep>=[','|':'|';;'|';'] { @*seps.push($<sep>) }
- }
-
- # XXX Not really implemented yet.
- rule multisig {
- :my $*SCOPE := 'my';
- <signature>
- }
-
- token fakesignature {
- <.newpad>
- <signature>
- }
-
- token signature {
- :my $*IN_DECL := 'sig';
- :my $*zone := 'posreq';
- :my @*seps := nqp::list();
- <.ws>
- [
- | <?before '-->' | ')' | ']' | '{' | ':'\s >
- | [ <parameter> || <.malformed('parameter')> ]
- ]+ % <param_sep>
- <.ws>
- { $*IN_DECL := ''; }
- [ '-->' <.ws> <typename> ]?
- { $*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;
- [
- | <type_constraint>+
- [
- | $<quant>=['**'|'*'] <param_var>
- | $<quant>=['\\'|'|'] <param_var> { pir::getstderr__P().print("Obsolete use of | or \\ with sigil on param { $<param_var> }\n") }
- | $<quant>=['\\'|'|'] <defterm>?
-
- | [ <param_var> | <named_param> ] $<quant>=['?'|'!'|<?>]
- | <?>
- ]
- | $<quant>=['**'|'*'] <param_var>
- | $<quant>=['\\'|'|'] <param_var> { pir::getstderr__P().print("Obsolete use of | or \\ with sigil on param { $<param_var> }\n") }
- | $<quant>=['\\'|'|'] <defterm>?
- | [ <param_var> | <named_param> ] $<quant>=['?'|'!'|<?>]
- | <longname> <.panic('Invalid typename in parameter declaration')>
- ]
- <trait>*
- <post_constraint>*
- <default_value>?
-
- # 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' {
- $/.CURSOR.typed_panic('X::Parameter::WrongOrder', misplaced => 'required', after => 'optional', parameter => $name);
- }
- elsif $*zone eq 'var' {
- $/.CURSOR.typed_panic('X::Parameter::WrongOrder', misplaced => 'required', after => 'variadic', parameter => $name);
- }
- }
- elsif $kind eq '?' {
- if $*zone eq 'posreq' {
- $*zone := 'posopt';
- }
- elsif $*zone eq 'var' {
- $/.CURSOR.typed_panic('X::Parameter::WrongOrder', misplaced => 'optional positional', after => 'variadic', parameter => $name);
- }
- }
- elsif $kind eq '*' {
- $*zone := 'var';
- }
- }
- }
-
- token defterm {
- :dba('new term to be defined')
- <identifier>
- }
-
- token param_var {
- :dba('formal parameter')
- [
- | '[' ~ ']' <signature>
- | '(' ~ ')' <signature>
- | <sigil> <twigil>?
- [
- || <name=.identifier>
- || <name=.decint> { $*W.throw($/, 'X::Syntax::Variable::Numeric', what => 'parameter') }
- || $<name>=[<[/!]>]
- ]?
- ]
- }
-
- token named_param {
- :my $*GOAL := ')';
- :dba('named parameter')
- ':'
- [
- | <name=.identifier> '(' <.ws>
- [ <named_param> | <param_var> <.ws> ]
- [ ')' || <.panic: 'Unable to parse named parameter; couldnt find right parenthesis'> ]
- | <param_var>
- ]
- }
-
- rule default_value {
- :my $*IN_DECL := '';
- '=' <EXPR('i=')>
- }
-
- token type_constraint {
- :my $*IN_DECL := '';
- [
- | <value>
- | <typename>
- | where <.ws> <EXPR('m=')>
- ]
- <.ws>
- }
-
- rule post_constraint {
- :my $*IN_DECL := '';
- :dba('constraint')
- [
- | '[' ~ ']' <signature>
- | '(' ~ ')' <signature>
- | where <EXPR('m=')>
- ]
- }
-
- proto token regex_declarator { <...> }
- token regex_declarator:sym<rule> {
- <sym>
- :my %*RX;
- :my $*METHODTYPE := 'rule';
- :my $*IN_DECL := 'rule';
- {
- %*RX<s> := 1;
- %*RX<r> := 1;
- }
- <regex_def>
- }
- token regex_declarator:sym<token> {
- <sym>
- :my %*RX;
- :my $*METHODTYPE := 'token';
- :my $*IN_DECL := 'token';
- {
- %*RX<r> := 1;
- }
- <regex_def>
- }
- token regex_declarator:sym<regex> {
- <sym>
- :my %*RX;
- :my $*METHODTYPE := 'regex';
- :my $*IN_DECL := 'regex';
- <regex_def>
- }
-
- rule regex_def {<.end_keyword> [
- :my $*CURPAD;
- :my $*HAS_SELF := 'complete';
- :my $*DECLARAND := $*W.stub_code_object('Regex');
- [
- <deflongname>?
- { if $<deflongname> { %*RX<name> := ~$<deflongname>[0].ast } }
- { $*IN_DECL := '' }
- <.newpad>
- [ [ ':'?'(' <signature> ')'] | <trait> ]*
- '{'[
- | ['*'|'<...>'|'<*>'] <?{ $*MULTINESS eq 'proto' }> $<onlystar>={1}
- |<nibble(self.quote_lang(%*RX<P5> ?? %*LANG<P5Regex> !! %*LANG<Regex>, '{', '}'))>]'}'<?ENDSTMT>
- { $*CURPAD := $*W.pop_lexpad() }
- ] || <.malformed('regex')>
- ] }
-
- proto token type_declarator { <...> }
-
- token type_declarator:sym<enum> {
- :my $*IN_DECL := 'enum';
- :my $*DECLARAND;
- <sym> <.end_keyword> <.ws>
- [
- | <longname>
- {
- my $longname := $*W.disect_longname($<longname>);
- my @name := $longname.type_name_parts('enum name', :decl(1));
- if $*W.already_declared($*SCOPE, $*PACKAGE, $*W.cur_lexpad(), @name) {
- $*W.throw($/, ['X', 'Redeclaration'],
- symbol => $longname.name(),
- );
- }
- }
- | <variable>
- | <?>
- ]
- { $*IN_DECL := ''; }
- <.ws>
- <trait>*
- <?before <[ < ( « ]> > <term> <.ws>
- }
-
- token type_declarator:sym<subset> {
- <sym> :my $*IN_DECL := 'subset';
- <.end_keyword>
- :s
- [
- [
- [
- <longname>
- {
- my $longname := $*W.disect_longname($<longname>[0]);
- my @name := $longname.type_name_parts('subset name', :decl(1));
- if $*W.already_declared($*SCOPE, $*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> <.end_keyword> <.ws>
-
- [
- | <identifier>
- | <variable>
- | <?>
- ]
- { $*IN_DECL := ''; }
- <.ws>
-
- <trait>*
-
- { $*W.push_lexpad($/) }
- [
- || <initializer>
- || <.missing: "initializer on constant declaration">
- ]
- }
-
- proto token initializer { <...> }
- token initializer:sym<=> {
- <sym>
- [
- <.ws>
- [
- || <?{ $*LEFTSIGIL eq '$' }> <EXPR('i=')>
- || <EXPR('e=')>
- ]
- || <.malformed: 'initializer'>
- ]
- }
- token initializer:sym<:=> {
- <sym> [ <.ws> <EXPR('e=')> || <.malformed: 'binding'> ]
- }
- token initializer:sym<::=> {
- <sym> [ <.ws> <EXPR('e=')> || <.malformed: 'binding'> ]
- }
- token initializer:sym<.=> {
- <sym> [ <.ws> <dottyopish> || <.malformed: 'mutator method call'> ]
- }
-
- rule trait {
- :my $*IN_DECL := '';
- [
- | <trait_mod>
- | <colonpair>
- ]
- }
-
- proto token trait_mod { <...> }
- token trait_mod:sym<is> { <sym>:s <longname><circumfix>? }
- token trait_mod:sym<hides> { <sym>:s <typename> }
- token trait_mod:sym<does> { <sym>:s <typename> }
- token trait_mod:sym<will> { <sym>:s <identifier> <pblock> }
- token trait_mod:sym<of> { <sym>:s <typename> }
- token trait_mod:sym<as> { <sym>:s <typename> }
- token trait_mod:sym<returns> { <sym>:s <typename> }
- token trait_mod:sym<handles> { <sym>:s <term> }
-
-
- ## Terms
-
- proto token term { <...> }
-
- token term:sym<self> {
- <sym> <.end_keyword>
- {
- $*HAS_SELF || self.typed_sorry('X::Syntax::Self::WithoutObject')
- }
- }
-
- token term:sym<now> { <sym> <.end_keyword> }
-
- token term:sym<time> { <sym> <.end_keyword> }
-
- token term:sym<rand> {
- <sym> »
- [ <?before '('? \h* [\d|'$']> <.obs('rand(N)', 'N.rand or (1..N).pick')> ]?
- [ <?before '()'> <.obs('rand()', 'rand')> ]?
- <.end_keyword>
- }
-
- token term:sym<...> { <sym> <args> }
- token term:sym<???> { <sym> <args> }
- token term:sym<!!!> { <sym> <args> }
-
- token term:sym<identifier> {
- <identifier> <!{ $*W.is_type([~$<identifier>]) }> <?[(]> <args>
- { self.add_mystery($<identifier>, $<args>.from, nqp::substr(~$<args>, 0, 1)) }
- }
-
- token term:sym<pir::op> {
- 'pir::' $<op>=[\w+] <args>?
- }
-
- token term:sym<pir::const> {
- 'pir::const::' $<const>=[\w+]
- }
-
- token term:sym<nqp::op> {
- 'nqp::' $<op>=[\w+] <args>?
- }
-
- token term:sym<name> {
- <longname>
- :my $*longname;
- { $*longname := $*W.disect_longname($<longname>) }
- [
- || <?{ nqp::substr($<longname>.Str, 0, 2) eq '::' || $*W.is_name($*longname.components()) }>
- <.unsp>?
- [
- <?{ $*W.is_type($*longname.components()) }>
- <?before '['> :dba('type parameter') '[' ~ ']' <arglist>
- ]?
- || <args> { self.add_mystery($<longname>, $<args>.from, 'termish')
- unless nqp::index($<longname>.Str, '::') >= 0 }
- ]
- }
-
- token term:sym<dotty> { <dotty> }
-
- token term:sym<capterm> { <capterm> }
-
- token term:sym<onlystar> {
- '{*}' <?ENDSTMT>
- [ <?{ $*IN_PROTO }> || <.panic: '{*} may only appear in proto'> ]
- }
-
- token args {
- :my $*GOAL := '';
- :dba('argument list')
- [
- | '(' ~ ')' <semiarglist>
- | [ \s <arglist> ]
- | <?>
- ]
- }
-
- token semiarglist {
- <arglist>+ % ';'
- <.ws>
- }
-
- token arglist {
- :my $*GOAL := 'endargs';
- :my $*QSIGIL := '';
- <.ws>
- :dba('argument list')
- [
- | <?stdstopper>
- | <EXPR('e=')>
- | <?>
- ]
- }
-
- proto token value { <...> }
- token value:sym<quote> { <quote> }
- token value:sym<number> { <number> }
- token value:sym<version> { <version> }
-
- proto token number { <...> }
- token number:sym<complex> { <im=.numish>'\\'?'i' }
- token number:sym<numish> { <numish> }
-
- token numish {
- [
- | <dec_number>
- | <integer>
- | <rad_number>
- | 'NaN' >>
- | 'Inf' >>
- | '+Inf' >>
- | '-Inf' >>
- ]
- }
-
- token dec_number {
- :dba('decimal number')
- [
- | $<coeff> = [ '.' <frac=.decint> ] <escale>?
- | $<coeff> = [ <int=.decint> '.' <frac=.decint> ] <escale>?
- | $<coeff> = [ <int=.decint> ] <escale>
- ]
- }
-
- token rad_number {
- ':' $<radix> = [\d+] <.unsp>?
- {} # don't recurse in lexer
- :dba('number in radix notation')
- [
- || '<'
- $<intpart> = [ <[ 0..9 a..z A..Z ]>+ [ _ <[ 0..9 a..z A..Z ]>+ ]* ]
- $<fracpart> = [ '.' <[ 0..9 a..z A..Z ]>+ [ _ <[ 0..9 a..z A..Z ]>+ ]* ]?
- [ '*' <base=.radint> '**' <exp=.radint> ]?
- '>'
- || <?before '['> <bracket=circumfix>
- || <?before '('> <circumfix>
- || <.malformed: 'radix number'>
- ]
- }
-
- token radint {
- [
- | <integer>
- # | <?before ':'\d> <rad_number> <?{
- # defined $<rad_number><intpart>
- # and
- # not defined $<rad_number><fracpart>
- # }>
- ]
- }
-
- token escale { <[Ee]> $<sign>=[<[+\-]>?] <decint> }
-
- token typename {
- [
- | '::?'<identifier> <colonpair>* # parse ::?CLASS as special case
- | <longname>
- <?{
- my $longname := $*W.disect_longname($<longname>);
- nqp::substr(~$<longname>, 0, 2) eq '::' ??
- 1 !! # ::T introduces a type, so always is one
- $*W.is_name($longname.type_name_parts('type name'))
- }>
- ]
- # parametric type?
- <.unsp>? [ <?before '['> '[' ~ ']' <arglist> ]?
- [<.ws> 'of' <.ws> <typename> ]?
- }
-
- token quotepair($*purpose = 'quoteadverb') {
- :my $*key;
- :my $*value;
- ':'
- :dba('colon pair (restricted)')
- [
- | '!' <identifier> [ <?before '('> <.sorry('Argument not allowed on negated pair')> ]?
- { $*key := ~$<identifier>; $*value := 0; }
- | <identifier>
- { $*key := ~$<identifier> }
- [
- || <?before '('> <circumfix> { $*value := $<circumfix>.ast; }
- || { $*value := 1; }
- ]
- | (\d+) <identifier>
- [ <?before '('> <.circumfix> <.sorry('2nd argument not allowed on pair')> ]?
- { $*key := ~$<identifier>; $*value := +~$/[0] }
- ]
- }
-
- token rx_adverbs {
- [
- <quotepair('rxadverb')> <.ws>
- :my $*ADVERB;
- { $*ADVERB := $<quotepair>[-1] }
- <.setup_quotepair>
- ]*
- }
-
- proto token quote_mod {*}
- token quote_mod:sym<w> { <sym> }
- token quote_mod:sym<ww> { <sym> }
- # XXX uncomment this when it's implemented
- #token quote_mod:sym<p> { <sym> }
- token quote_mod:sym<x> { <sym> }
- token quote_mod:sym<to> { <sym> }
- token quote_mod:sym<s> { <sym> }
- token quote_mod:sym<a> { <sym> }
- token quote_mod:sym<h> { <sym> }
- token quote_mod:sym<f> { <sym> }
- token quote_mod:sym<c> { <sym> }
- token quote_mod:sym<b> { <sym> }
-
- proto token quote { <...> }
- token quote:sym<apos> { :dba('single quotes') "'" ~ "'" <nibble(self.quote_lang(%*LANG<Q>, "'", "'", ['q']))> }
- token quote:sym<dblq> { :dba('double quotes') '"' ~ '"' <nibble(self.quote_lang(%*LANG<Q>, '"', '"', ['qq']))> }
- token quote:sym<q> {
- :my $qm;
- 'q'
- [
- | <quote_mod> » <!before '('> { $qm := $<quote_mod>.Str } <quibble(%*LANG<Q>, 'q', $qm)>
- | » <!before '('> <.ws> <quibble(%*LANG<Q>, 'q')>
- ]
- }
- token quote:sym<qq> {
- :my $qm;
- 'qq'
- [
- | <quote_mod> » <!before '('> { $qm := $<quote_mod>.Str } <.ws> <quibble(%*LANG<Q>, 'qq', $qm)>
- | » <!before '('> <.ws> <quibble(%*LANG<Q>, 'qq')>
- ]
- }
- token quote:sym<Q> {
- :my $qm;
- 'Q'
- [
- | <quote_mod> » <!before '('> { $qm := $<quote_mod>.Str } <quibble(%*LANG<Q>, $qm)>
- | » <!before '('> <.ws> <quibble(%*LANG<Q>)>
- ]
- }
- token quote:sym<Q:PIR> { 'Q:PIR' <.ws> <quibble(%*LANG<Q>)> }
-
- token quote:sym</null/> { '/' \s* '/' <.typed_panic: "X::Syntax::Regex::NullRegex"> }
- token quote:sym</ /> {
- :my %*RX;
- '/' <nibble(self.quote_lang(%*LANG<Regex>, '/', '/'))> [ '/' || <.panic: "Unable to parse regex; couldn't find final '/'"> ]
- <.old_rx_mods>?
- }
- token quote:sym<rx> {
- <sym> >>
- :my %*RX;
- <rx_adverbs>
- <quibble(%*RX<P5> ?? %*LANG<P5Regex> !! %*LANG<Regex>)>
- <!old_rx_mods>
- }
-
- token quote:sym<m> {
- <sym> (s)?>>
- :my %*RX;
- { %*RX<s> := 1 if $/[0] }
- <rx_adverbs>
- <quibble(%*RX<P5> ?? %*LANG<P5Regex> !! %*LANG<Regex>)>
- <!old_rx_mods>
- }
-
- token quote:sym<qr> {
- <sym> <.end_keyword> <.obs('qr for regex quoting', 'rx//')>
- }
-
- token setup_quotepair { '' }
-
- token sibble($l, $lang2, @lang2tweaks?) {
- :my $lang;
- :my $start;
- :my $stop;
- <babble($l)>
- { my $B := $<babble><B>.ast; $lang := $B[0]; $start := $B[1]; $stop := $B[2]; }
-
- $start <left=.nibble($lang)> [ $stop || <.panic: "Couldn't find terminator $stop"> ]
- [ <?{ $start ne $stop }>
- <.ws>
- [ <?[ \[ \{ \( \< ]> <.obs('brackets around replacement', 'assignment syntax')> ]?
- [ <infixish> || <.missing: "assignment operator"> ]
- [ <?{ $<infixish>.Str eq '=' }> || <.malformed: "assignment operator"> ]
- # XXX When we support it, above check should add: || $<infixish><infix_postfix_meta_operator>[0]
- <.ws>
- [ <right=.EXPR('i')> || <.panic: "Assignment operator missing its expression"> ]
- ||
- { $lang := self.quote_lang($lang2, $stop, $stop, @lang2tweaks); }
- <right=.nibble($lang)> $stop || <.panic: "Malformed replacement part; couldn't find final $stop">
- ]
- }
-
- token quote:sym<s> {
- <sym> (s)? >>
- :my %*RX;
- {
- %*RX<s> := 1 if $/[0]
- }
- <rx_adverbs>
- <sibble(%*RX<P5> ?? %*LANG<P5Regex> !! %*LANG<Regex>, %*LANG<Q>, ['qq'])>
- <.old_rx_mods>?
- }
-
- token old_rx_mods {
- (<[ i g s m x c e ]>)
- {
- my $m := $/[0].Str;
- if $m eq 'i' { $/.CURSOR.obs('/i',':i'); }
- elsif $m eq 'g' { $/.CURSOR.obs('/g',':g'); }
- elsif $m eq 'm' { $/.CURSOR.obs('/m','^^ and $$ anchors'); }
- elsif $m eq 's' { $/.CURSOR.obs('/s','. or \N'); }
- elsif $m eq 'x' { $/.CURSOR.obs('/x','normal default whitespace'); }
- elsif $m eq 'c' { $/.CURSOR.obs('/c',':c or :p'); }
- elsif $m eq 'e' { $/.CURSOR.obs('/e','interpolated {...} or s{} = ... form'); }
- else { $/.CURSOR.obs('suffix regex modifiers','prefix adverbs'); }
- }
- }
-
- token quote:sym<quasi> {
- <sym> <.ws> <!before '('>
- :my $*IN_QUASI := 1;
- :my @*UNQUOTE_ASTS := [];
- <block>
- }
-
- token circumfix:sym<( )> { :dba('parenthesized expression') '(' ~ ')' <semilist> }
- token circumfix:sym<[ ]> { :dba('array composer') '[' ~ ']' <semilist> }
- token circumfix:sym<ang> {
- :dba('quote words')
- '<' ~ '>'
- [
- [ <?before 'STDIN>' > <.obs('<STDIN>', '$*IN.lines (or add whitespace to suppress warning)')> ]?
- [ <?before '>' > <.obs('<>', 'lines() to read input, (\'\') to represent a null string or () to represent an empty list')> ]?
- <nibble(self.quote_lang(%*LANG<Q>, "<", ">", ['q', 'w']))>
- ]
- }
- token circumfix:sym«<< >>» { :dba('shell-quote words') '<<' ~ '>>' <nibble(self.quote_lang(%*LANG<Q>, "<<", ">>", ['qq', 'ww']))> }
- token circumfix:sym<« »> { :dba('shell-quote words') '«' ~ '»' <nibble(self.quote_lang(%*LANG<Q>, "«", "»", ['qq', 'ww']))> }
- token circumfix:sym<{ }> { <?[{]> <pblock(1)> }
- token circumfix:sym<sigil> {
- :dba('contextualizer')
- <sigil> '(' ~ ')' <semilist>
- { unless $*LEFTSIGIL { $*LEFTSIGIL := $<sigil>.Str } }
- }
-
- ## Operators
-
- INIT {
- Perl6::Grammar.O(':prec<y=>, :assoc<unary>', '%methodcall');
- Perl6::Grammar.O(':prec<x=>, :assoc<unary>', '%autoincrement');
- Perl6::Grammar.O(':prec<w=>, :assoc<right>', '%exponentiation');
- Perl6::Grammar.O(':prec<v=>, :assoc<unary>', '%symbolic_unary');
- Perl6::Grammar.O(':prec<u=>, :assoc<left>', '%multiplicative');
- Perl6::Grammar.O(':prec<t=>, :assoc<left>', '%additive');
- Perl6::Grammar.O(':prec<s=>, :assoc<left>', '%replication');
- Perl6::Grammar.O(':prec<r=>, :assoc<left>', '%concatenation');
- Perl6::Grammar.O(':prec<q=>, :assoc<list>', '%junctive_and');
- Perl6::Grammar.O(':prec<p=>, :assoc<list>', '%junctive_or');
- Perl6::Grammar.O(':prec<o=>, :assoc<unary>', '%named_unary');
- Perl6::Grammar.O(':prec<n=>, :assoc<non>', '%structural');
- Perl6::Grammar.O(':prec<m=>, :assoc<left>, :iffy<1>, :pasttype<chain>', '%chaining');
- Perl6::Grammar.O(':prec<l=>, :assoc<left>', '%tight_and');
- Perl6::Grammar.O(':prec<k=>, :assoc<list>', '%tight_or');
- Perl6::Grammar.O(':prec<j=>, :assoc<right>', '%conditional');
- Perl6::Grammar.O(':prec<i=>, :assoc<right>', '%item_assignment');
- Perl6::Grammar.O(':prec<i=>, :assoc<right>, :sub<e=>', '%list_assignment');
- Perl6::Grammar.O(':prec<h=>, :assoc<unary>', '%loose_unary');
- Perl6::Grammar.O(':prec<g=>, :assoc<list>, :nextterm<nulltermish>', '%comma');
- Perl6::Grammar.O(':prec<f=>, :assoc<list>', '%list_infix');
- Perl6::Grammar.O(':prec<e=>, :assoc<right>', '%list_prefix');
- Perl6::Grammar.O(':prec<d=>, :assoc<left>', '%loose_and');
- Perl6::Grammar.O(':prec<c=>, :assoc<list>', '%loose_or');
- Perl6::Grammar.O(':prec<b=>, :assoc<list>', '%sequencer');
- }
-
- token termish {
- :my $*SCOPE := "";
- :my $*MULTINESS := "";
- :my $*OFTYPE;
- :my $*VAR;
- :dba('prefix or term')
- [
- || <prefixish>* <term>
- :dba('postfix')
- [
- || <?{ $*QSIGIL }>
- [
- || <?{ $*QSIGIL eq '$' }> [ <postfixish>+! <?{ bracket_ending($<postfixish>) }> ]?
- || <postfixish>+! <?{ bracket_ending($<postfixish>) }>
- || { $*VAR := 0 } <!>
- ]
- || <!{ $*QSIGIL }> <postfixish>*
- ]
- || <!{ $*QSIGIL }> <?before <infixish> {
- $/.CURSOR.typed_panic('X::Syntax::InfixInTermPosition', infix => ~$<infixish>); } >
- || <!>
- ]
- { self.check_variable($*VAR) if $*VAR; }
- }
-
- sub bracket_ending($matches) {
- my $check := $matches[+$matches - 1];
- my $str := $check.Str;
- my $last := nqp::substr($str, nqp::chars($check) - 1, 1);
- $last eq ')' || $last eq '}' || $last eq ']' || $last eq '>'
- }
-
- method EXPR(str $preclim = '') {
- # Override this so we can set $*LEFTSIGIL.
- my $*LEFTSIGIL := '';
- nqp::findmethod(HLL::Grammar, 'EXPR')(self, $preclim, :noinfix($preclim eq 'y='));
- }
-
- token prefixish {
- :dba('prefix or meta-prefix')
- [
- | <OPER=prefix>
- | <OPER=prefix_circumfix_meta_operator>
- ]
- <prefix_postfix_meta_operator>?
- <.ws>
- }
-
- token infixish {
- :dba('infix or meta-infix')
- <!infixstopper>
- <!stdstopper>
- [
- | <colonpair> <OPER=fake_infix>
- | :dba('bracketed infix') '[' ~ ']' <infixish> {} <OPER=.copyOPER($<infixish>)>
- | <OPER=infix_circumfix_meta_operator>
- | <OPER=infix> <![=]>
- | <OPER=infix_prefix_meta_operator>
- | <infix> <OPER=infix_postfix_meta_operator>
- ]
- }
-
- token fake_infix {
- <O('%item_assignment, :assoc<unary>, :fake<1>, :dba<adverb>')>
- }
-
- regex infixstopper {
- :dba('infix stopper')
- [
- | <?before '!!'> <?{ $*GOAL eq '!!' }>
- | <?before '{' | <lambda> > <?MARKED('ws')> <?{ $*GOAL eq '{' || $*GOAL eq 'endargs' }>
- ]
- }
-
- token postfixish {
- # last whitespace didn't end here
- <!MARKED('ws')>
-
- [ <!{ $*QSIGIL }> [ <.unsp> | '\\' ] ]?
-
- :dba('postfix')
- <postfix_prefix_meta_operator>?
- [
- | <OPER=postfix>
- | <OPER=postcircumfix>
- | <OPER=dotty>
- | <OPER=privop>
- ]
- { $*LEFTSIGIL := '@'; }
- }
-
- token postop {
- | <postfix>
- | <postcircumfix>
- }
-
- proto token prefix_circumfix_meta_operator { <...> }
-
- proto token infix_postfix_meta_operator { <...> }
-
- proto token infix_prefix_meta_operator { <...> }
-
- proto token infix_circumfix_meta_operator { <...> }
-
- proto token postfix_prefix_meta_operator { <...> }
-
- proto token prefix_postfix_meta_operator { <...> }
-
- regex term:sym<reduce> {
- :my $*IN_REDUCE := 1;
- <?before '['\S+']'>
-
- '['
- [
- || <op=.infixish> <?before ']'>
- || $<triangle>=[\\]<op=.infixish> <?before ']'>
- || <!>
- ]
- ']'
-
- <args>
- }
-
- token postfix_prefix_meta_operator:sym<»> {
- [ <sym> | '>>' ]
- [ <!{ $*QSIGIL }> || <!before '('> ]
- }
-
- token prefix_postfix_meta_operator:sym<«> {
- <sym> | '<<'
- }
-
- token infix_circumfix_meta_operator:sym<« »> {
- $<opening>=[ '«' | '»' ]
- {} <infixish>
- $<closing>=[ '«' | '»' || <.missing("« or »")> ]
- {} <O=.copyO($<infixish>)>
- }
-
- token infix_circumfix_meta_operator:sym«<< >>» {
- $<opening>=[ '<<' | '>>' ]
- {} <infixish>
- $<closing>=[ '<<' | '>>' || <.missing("<< or >>")> ]
- {} <O=.copyO($<infixish>)>
- }
-
- method copyO($from) {
- my $O := $from<OPER><O>;
- my $cur := self.'!cursor_start'();
- $cur.'!cursor_pass'(self.pos());
- nqp::bindattr($cur, NQPCursor, '$!match', $O);
- $cur
- }
-
- method copyOPER($from) {
- my $OPER := $from<OPER>;
- my $cur := self.'!cursor_start'();
- $cur.'!cursor_pass'(self.pos());
- nqp::bindattr($cur, NQPCursor, '$!match', $OPER);
- $cur
- }
-
- proto token dotty { <...> }
- token dotty:sym<.> {
- <sym> <dottyop>
- <O('%methodcall')>
- }
-
- token dotty:sym<.*> {
- $<sym>=['.' [ <[+*?=]> | '^' '!'? ]] <dottyop>
- <O('%methodcall')>
- }
-
- token dottyop {
- :dba('dotty method or postfix')
- [
- | <methodop>
- | <!alpha> <postop>
- ]
- }
-
- token privop {
- '!' <methodop>
- <O('%methodcall')>
- }
-
- token methodop {
- [
- | <longname>
- | <?before '$' | '@' | '&' > <variable> { self.check_variable($<variable>) }
- | <?before <[ ' " ]> >
- [ <!{$*QSIGIL}> || <!before '"' <-["]>*? \s > ] # dwim on "$foo."
- <quote>
- [ <?before '(' | '.(' | '\\'> || <.panic: "Quoted method name requires parenthesized arguments. If you meant to concatenate two strings, use '~'."> ]
- ] <.unsp>?
- :dba('method arguments')
- [
- [
- | <?[(]> <args>
- | ':' <?before \s | '{'> <!{ $*QSIGIL }> <args=.arglist>
- ]
- || <!{ $*QSIGIL }> <?>
- || <?{ $*QSIGIL }> <?['.']> <?>
- ]
- }
-
- token dottyopish {
- <term=.dottyop>
- }
-
- token postcircumfix:sym<[ ]> {
- :my $*QSIGIL := '';
- :dba('subscript')
- '[' ~ ']' [ <.ws> <semilist> ]
- <O('%methodcall')>
- }
-
- token postcircumfix:sym<{ }> {
- :my $*QSIGIL := '';
- :dba('subscript')
- '{' ~ '}' [ <.ws> <semilist> ]
- <O('%methodcall')>
- }
-
- token postcircumfix:sym<ang> {
- '<'
- [
- || <nibble(self.quote_lang(%*LANG<Q>, "<", ">", ['q', 'w']))> '>'
- || <?before \h* [ \d | <sigil> | ':' ] >
- { $/.CURSOR.panic("Whitespace required before < operator") }
- || { $/.CURSOR.panic("Unable to parse quote-words subscript; couldn't find right angle quote") }
- ]
- <O('%methodcall')>
- }
-
- token postcircumfix:sym<( )> {
- :dba('argument list')
- '(' ~ ')' [ <.ws> <arglist> ]
- <O('%methodcall')>
- }
-
- token postfix:sym<i> { <sym> >> <O('%methodcall')> }
-
- token prefix:sym<++> { <sym> <O('%autoincrement')> }
- token prefix:sym<--> { <sym> <O('%autoincrement')> }
- token postfix:sym<++> { <sym> <O('%autoincrement')> }
- token postfix:sym<--> { <sym> <O('%autoincrement')> }
-
- # TODO: report the correct bracket in error message
- token postfix:sym«->» {
- <sym>
- [
- | ['[' | '{' | '(' ] <.obs('->(), ->{} or ->[] as postfix dereferencer', '.(), .[] or .{} to deref, or whitespace to delimit a pointy block')>
- | <.obs('-> as postfix', 'either . to call a method, or whitespace to delimit a pointy block')>
- ]
- }
-
- token infix:sym<**> { <sym> <O('%exponentiation')> }
-
- token prefix:sym<+> { <sym> <O('%symbolic_unary')> }
- token prefix:sym<~> { <sym> <O('%symbolic_unary')> }
- token prefix:sym<-> { <sym> <![>]> <O('%symbolic_unary')> }
- token prefix:sym<?> { <sym> <!before '??'> <O('%symbolic_unary')> }
- token prefix:sym<!> { <sym> <!before '!!'> <O('%symbolic_unary')> }
- token prefix:sym<+^> { <sym> <O('%symbolic_unary')> }
- token prefix:sym<~^> { <sym> <O('%symbolic_unary')> }
- token prefix:sym<?^> { <sym> <O('%symbolic_unary')> }
- token prefix:sym<^> { <sym> <O('%symbolic_unary')> }
- token prefix:sym<|> { <sym> <O('%symbolic_unary')> }
-
- token infix:sym<*> { <sym> <O('%multiplicative')> }
- token infix:sym</> { <sym> <O('%multiplicative')> }
- token infix:sym<div> { <sym> >> <O('%multiplicative')> }
- token infix:sym<gcd> { <sym> >> <O('%multiplicative')> }
- token infix:sym<lcm> { <sym> >> <O('%multiplicative')> }
- token infix:sym<%> { <sym> <O('%multiplicative')> }
- token infix:sym<mod> { <sym> >> <O('%multiplicative')> }
- token infix:sym<%%> { <sym> <O('%multiplicative, :iffy<1>')> }
- token infix:sym<+&> { <sym> <O('%multiplicative')> }
- token infix:sym<~&> { <sym> <O('%multiplicative')> }
- token infix:sym<?&> { <sym> <O('%multiplicative')> }
- token infix:sym«+<» { <sym> <!before '<'> <O('%multiplicative')> }
- token infix:sym«+>» { <sym> <!before '>'> <O('%multiplicative')> }
-
- token infix:sym«<<» { <sym> \s <.sorryobs('<< to do left shift', '+< or ~<')> }
-
- token infix:sym«>>» { <sym> \s <.sorryobs('>> to do right shift', '+> or ~>')> }
-
- token infix:sym<+> { <sym> <O('%additive')> }
- token infix:sym<-> {
- # We want to match in '$a >>->> $b' but not 'if $a -> { ... }'.
- <sym> [<?before '>>'> || <!before '>'>]
- <O('%additive')>
- }
- token infix:sym<+|> { <sym> <O('%additive')> }
- token infix:sym<+^> { <sym> <O('%additive')> }
- token infix:sym<~|> { <sym> <O('%additive')> }
- token infix:sym<~^> { <sym> <O('%additive')> }
- token infix:sym<?|> { <sym> <O('%additive')> }
- token infix:sym<?^> { <sym> <O('%additive')> }
-
- token infix:sym<x> { <sym> >> <O('%replication')> }
- token infix:sym<xx> { <sym> >> <O('%replication')> }
-
- token infix:sym<~> { <sym> <O('%concatenation')> }
- token infix:sym<.> { <sym> <[\]\)\},:\s\$"']> <.obs('. to concatenate strings', '~')> }
-
- token infix:sym<&> { <sym> <O('%junctive_and')> }
- token infix:sym<|> { <sym> <O('%junctive_or')> }
- token infix:sym<^> { <sym> <O('%junctive_or')> }
-
- token prefix:sym<let> { <sym> \s+ <!before '=>'> <O('%named_unary')> { $*W.give_cur_block_let($/) } }
- token prefix:sym<temp> { <sym> \s+ <!before '=>'> <O('%named_unary')> { $*W.give_cur_block_temp($/) } }
-
- token infix:sym«==» { <sym> <O('%chaining')> }
- token infix:sym«!=» { <sym> <?before \s|']'> <O('%chaining')> }
- token infix:sym«<=» { <sym> <O('%chaining')> }
- token infix:sym«>=» { <sym> <O('%chaining')> }
- token infix:sym«<» { <sym> <O('%chaining')> }
- token infix:sym«>» { <sym> <O('%chaining')> }
- token infix:sym«eq» { <sym> >> <O('%chaining')> }
- token infix:sym«ne» { <sym> >> <O('%chaining')> }
- token infix:sym«le» { <sym> >> <O('%chaining')> }
- token infix:sym«ge» { <sym> >> <O('%chaining')> }
- token infix:sym«lt» { <sym> >> <O('%chaining')> }
- token infix:sym«gt» { <sym> >> <O('%chaining')> }
- token infix:sym«=:=» { <sym> <O('%chaining')> }
- token infix:sym<===> { <sym> <O('%chaining')> }
- token infix:sym<eqv> { <sym> >> <O('%chaining')> }
- token infix:sym<before> { <sym> >> <O('%chaining')> }
- token infix:sym<after> { <sym>>> <O('%chaining')> }
- token infix:sym<~~> { <sym> <O('%chaining')> <!dumbsmart> }
- token infix:sym<!~~> { <sym> <O('%chaining')> <!dumbsmart> }
- token infix:sym<(elem)> { <sym> <O('%chaining')> }
- token infix:sym<(cont)> { <sym> <O('%chaining')> }
- token infix:sym«(<=)» { <sym> <O('%chaining')> }
- token infix:sym«(<)» { <sym> <O('%chaining')> }
- token infix:sym«(>=)» { <sym> <O('%chaining')> }
- token infix:sym«(>)» { <sym> <O('%chaining')> }
-
- token dumbsmart {
- # should be
- # 'Bool::'? True && <.longname>
- # once && in regexes is implemented
- | <?before \h* [ 'Bool::'? 'True' && <.longname> ] > <.worry("Smartmatch against True always matches; if you mean to test the topic for truthiness, use :so or *.so or ?* instead")>
- | <?before \h* [ 'Bool::'? 'False' && <.longname> ] > <.worry("Smartmatch against False always fails; if you mean to test the topic for truthiness, use :!so or *.not or !* instead")>
- }
-
- token infix:sym<&&> { <sym> <O('%tight_and, :pasttype<if>')> }
-
- token infix:sym<||> { <sym> <O('%tight_or, :assoc<left>, :pasttype<unless>')> }
- token infix:sym<^^> { <sym> <O('%tight_or, :pasttype<xor>')> }
- token infix:sym<//> { <sym> <O('%tight_or, :assoc<left>, :pasttype<defor>')> }
- token infix:sym<min> { <sym> >> <O('%tight_or')> }
- token infix:sym<max> { <sym> >> <O('%tight_or')> }
-
- token infix:sym<?? !!> {
- :my $*GOAL := '!!';
- '??'
- <.ws>
- <EXPR('i=')>
- [ '!!'
- || <?before '::'<-[=]>> <.panic: "Please use !! rather than ::">
- || <?before ':' <-[=]>> <.panic: "Please use !! rather than :">
- || <?before \N*? [\n\N*?]?> '!!' <.sorry("Bogus code found before the !!")> <.panic("Confused")>
- || <.sorry("Found ?? but no !!")> <.panic("Confused")>
- ]
- <O('%conditional, :reducecheck<ternary>, :pasttype<if>')>
- }
-
- token infix_prefix_meta_operator:sym<!> {
- <sym> <infixish>
- [
- || <?{ $<infixish>.Str eq '=' }> <O('%chaining')>
- || <?{ $<infixish><OPER><O><iffy> }> <O=.copyO($<infixish>)>
- || <.panic("Cannot negate " ~ $<infixish>.Str ~ " because it is not iffy enough")>
- ]
- }
- token infix_prefix_meta_operator:sym<R> { <sym> <infixish> {} <O=.copyO($<infixish>)> }
- token infix_prefix_meta_operator:sym<S> { <sym> <infixish> {} <O=.copyO($<infixish>)> }
- token infix_prefix_meta_operator:sym<X> { <sym> <infixish> <O('%list_infix')> }
- token infix_prefix_meta_operator:sym<Z> { <sym> <infixish> <O('%list_infix')> }
- token infix:sym<minmax> { <sym> >> <O('%list_infix')> }
-
- token infix:sym<:=> {
- <sym> <O('%list_assignment')>
- }
-
- token infix:sym<::=> {
- <sym> <O('%item_assignment')>
- }
-
- token infix:sym<.=> { <sym> <O('%item_assignment, :nextterm<dottyopish>')> }
-
- # Should probably have <!after '='> to agree w/spec, but after NYI.
- # Modified infix != below instead to prevent misparse
- token infix_postfix_meta_operator:sym<=> { '=' <O('%item_assignment')> }
-
- token infix:sym«=>» { <sym> <O('%item_assignment')> }
-
- token prefix:sym<so> { <sym> >> <O('%loose_unary')> }
- token prefix:sym<not> { <sym> >> <O('%loose_unary')> }
-
- token infix:sym<,> {
- <sym> <O('%comma')>
- # TODO: should be <.worry>, not <.panic>
- [ <?before \h*'...'> <.panic: "Comma found before apparent series operator; please remove comma (or put parens\n around the ... listop, or use 'fail' instead of ...)"> ]?
- }
-
- token infix:sym<Z> { <!before <sym> <infixish> > <sym> <O('%list_infix')> }
- token infix:sym<X> { <!before <sym> <infixish> > <sym> <O('%list_infix')> }
- token infix:sym<(|)> { <!before <sym> <infixish> > <sym> <O('%list_infix')> }
- token infix:sym<(&)> { <!before <sym> <infixish> > <sym> <O('%list_infix')> }
- token infix:sym<(-)> { <!before <sym> <infixish> > <sym> <O('%list_infix')> }
- token infix:sym<(^)> { <!before <sym> <infixish> > <sym> <O('%list_infix')> }
- token infix:sym<(.)> { <!before <sym> <infixish> > <sym> <O('%list_infix')> }
- token infix:sym<(+)> { <!before <sym> <infixish> > <sym> <O('%list_infix')> }
-
- token infix:sym<...> { <sym> <O('%list_infix')> }
- token infix:sym<...^> { <sym> <O('%list_infix')> }
- # token term:sym<...> { <sym> <args>? <O(|%list_prefix)> }
-
- token infix:sym<?> { <sym> {} <!before '?'> <?before <-[;]>*?':'> <.obs('?: for the conditional operator', '??!!')> <O('%conditional')> }
-
- token infix:sym<ff> { <sym> <O('%conditional')> }
- token infix:sym<^ff> { <sym> <O('%conditional')> }
- token infix:sym<ff^> { <sym> <O('%conditional')> }
- token infix:sym<^ff^> { <sym> <O('%conditional')> }
-
- token infix:sym<fff> { <sym> <O('%conditional')> }
- token infix:sym<^fff> { <sym> <O('%conditional')> }
- token infix:sym<fff^> { <sym> <O('%conditional')> }
- token infix:sym<^fff^> { <sym> <O('%conditional')> }
-
- token infix:sym<=> {
- <sym>
- [
- || <?{ $*LEFTSIGIL eq '$' }> <O('%item_assignment')>
- || <O('%list_assignment')>
- ]
- }
-
- token infix:sym<and> { <sym> >> <O('%loose_and, :pasttype<if>')> }
- token infix:sym<andthen> { <sym> >> <O('%loose_and, :assoc<list>')> }
-
- token infix:sym<or> { <sym> >> <O('%loose_or, :assoc<left>, :pasttype<unless>')> }
- token infix:sym<xor> { <sym> >> <O('%loose_or, :pasttype<xor>')> }
- token infix:sym<orelse> { <sym> >> <O('%loose_or, :assoc<left>, :pasttype<defor>')> }
-
- token infix:sym«<==» { <sym> <O('%sequencer')> }
- token infix:sym«==>» { <sym> <O('%sequencer')> }
- token infix:sym«<<==» { <sym> <O('%sequencer')> }
- token infix:sym«==>>» { <sym> <O('%sequencer')> }
-
- token infix:sym<..> { <sym> <O('%structural')> }
- token infix:sym<^..> { <sym> <O('%structural')> }
- token infix:sym<..^> { <sym> <O('%structural')> }
- token infix:sym<^..^> { <sym> <O('%structural')> }
-
- token infix:sym<leg> { <sym> >> <O('%structural')> }
- token infix:sym<cmp> { <sym> >> <O('%structural')> }
- token infix:sym«<=>» { <sym> <O('%structural')> }
-
- token infix:sym<but> { <sym> >> <O('%structural')> }
- token infix:sym<does> { <sym> >> <O('%structural')> }
-
- token infix:sym<!~> { <sym> \s <.obs('!~ to do negated pattern matching', '!~~')> <O('%chaining')> }
- token infix:sym<=~> { <sym> <.obs('=~ to do pattern matching', '~~')> <O('%chaining')> }
-
- method add_mystery($token, $pos, $ctx) {
- my $name := ~$token;
- unless $name eq '' || $*W.is_lexical('&' ~ $name) || $*W.is_lexical($name) {
- my $lex := $*W.cur_lexpad();
- my $key := $name ~ '-' ~ $lex.cuid;
- if nqp::existskey(%*MYSTERY, $key) {
- nqp::push(%*MYSTERY{$key}<pos>, $pos);
- }
- else {
- %*MYSTERY{$key} := nqp::hash(
- 'lex', $lex,
- 'name', $name,
- 'ctx', $ctx,
- 'pos', [$pos]);
- }
- }
- self;
- }
-
- method explain_mystery() {
- my %post_types;
- my %unk_types;
- my %unk_routines;
-
- sub push_lines(@target, @pos) {
- for @pos {
- nqp::push(@target, HLL::Compiler.lineof(self.orig, $_));
- }
- }
-
- for %*MYSTERY {
- my %sym := $_.value;
- my $name := %sym<name>;
- my $decl := $*W.is_lexically_visible($name, %sym<lex>);
- if $decl == 2 {
- # types may not be post-declared
- %post_types{$name} := [] unless %post_types{$name};
- push_lines(%post_types{$name}, %sym<pos>);
- next;
- }
-
- next if $decl == 1;
- next if $*W.is_lexically_visible('&' ~ $name, %sym<lex>);
-
- # just a guess, but good enough to improve error reporting
- if $_ lt 'a' {
- %unk_types{$name} := [] unless %unk_types{$name};
- push_lines(%unk_types{$name}, %sym<pos>);
- }
- else {
- %unk_routines{$name} := [] unless %unk_routines{$name};
- push_lines(%unk_routines{$name}, %sym<pos>);
- }
- }
-
- if %post_types || %unk_types || %unk_routines {
- self.typed_sorry('X::Undeclared::Symbols',
- :%post_types, :%unk_types, :%unk_routines);
- }
-
- self;
- }
-
- method add_variable($name) {
- my $categorical := $name ~~ /^'&'((\w+)':<'\s*(\S+?)\s*'>')$/;
- if $categorical {
- self.add_categorical(~$categorical[0][0], ~$categorical[0][1], ~$categorical[0], $name);
- }
- }
-
- # Called when we add a new choice to an existing syntactic category, for
- # example new infix operators add to the infix category. Augments the
- # grammar as needed.
- method add_categorical($category, $opname, $canname, $subname, $declarand?) {
- my $self := self;
-
- # If we already have the required operator in the grammar, just return.
- if nqp::can(self, $canname) {
- return 1;
- }
-
- # Work out what default precedence we want, or if it's more special than
- # just an operator.
- my $prec;
- my $is_oper;
- my $is_term := 0;
- if $category eq 'infix' {
- $prec := '%additive';
- $is_oper := 1;
- }
- elsif $category eq 'prefix' {
- $prec := '%symbolic_unary';
- $is_oper := 1;
- }
- elsif $category eq 'postfix' {
- $prec := '%autoincrement';
- $is_oper := 1;
- }
- elsif $category eq 'circumfix' {
- $is_oper := 0;
- }
- elsif $category eq 'trait_mod' {
- return 0;
- }
- elsif $category eq 'term' {
- $is_term := 1;
- }
- elsif $category eq 'METAOP_TEST_ASSIGN' {
- return 0;
- }
- else {
- self.typed_panic('X::Syntax::Extension::Category', :$category);
- }
-
- if $is_term {
- my role Term[$meth_name, $op] {
- token ::($meth_name) { $<sym>=[$op] }
- }
- self.HOW.mixin(self, Term.HOW.curry(Term, $canname, $opname));
- }
- # Mix an appropraite role into the grammar for parsing the new op.
- elsif $is_oper {
- my role Oper[$meth_name, $op, $precedence, $declarand] {
- token ::($meth_name) { $<sym>=[$op] <O=.genO($precedence, $declarand)> }
- }
- self.HOW.mixin(self, Oper.HOW.curry(Oper, $canname, $opname, $prec, $declarand));
- }
- else {
- # Find opener and closer and parse an EXPR between them.
- # XXX One day semilist would be nice, but right now that
- # runs us into fun with terminators.
- my @parts := nqp::split(' ', $opname);
- if +@parts != 2 {
- nqp::die("Unable to find starter and stopper from '$opname'");
- }
- my role Circumfix[$meth_name, $opener, $closer] {
- token ::($meth_name) { $opener <EXPR> $closer }
- }
- self.HOW.mixin(self, Circumfix.HOW.curry(Circumfix, $canname, @parts[0], @parts[1]));
- }
-
- # This also becomes the current MAIN. Also place it in %?LANG.
- %*LANG<MAIN> := self.WHAT;
- $*W.install_lexical_symbol($*W.cur_lexpad(), '%?LANG', $*W.p6ize_recursive(%*LANG));
-
- # Declarand should get precedence traits.
- if $is_oper && nqp::isconcrete($declarand) {
- my $base_prec := self.O($prec).MATCH<prec>;
- $*W.apply_trait(self.MATCH, '&trait_mod:<is>', $declarand,
- :prec(nqp::hash('prec', $base_prec)));
- }
-
- # May also need to add to the actions.
- if $category eq 'circumfix' {
- my role CircumfixAction[$meth, $subname] {
- method ::($meth)($/) {
- make QAST::Op.new(
- :op('call'), :name('&' ~ $subname),
- $<EXPR>.ast
- );
- }
- };
- %*LANG<MAIN-actions> := $*ACTIONS.HOW.mixin($*ACTIONS,
- CircumfixAction.HOW.curry(CircumfixAction, $canname, $subname));
- }
- elsif $is_term {
- my role TermAction[$meth, $subname] {
- method ::($meth)($/) {
- make QAST::Op.new(
- :op('call'), :name('&' ~ $subname),
- );
- }
- };
- %*LANG<MAIN-actions> := $*ACTIONS.HOW.mixin($*ACTIONS,
- TermAction.HOW.curry(TermAction, $canname, $subname));
- }
-
- return 1;
- }
-
- method genO($default, $declarand) {
- my $desc := $default;
- if nqp::can($declarand, 'prec') {
- my %extras := $declarand.prec.FLATTENABLE_HASH;
- for %extras {
- $desc := "$desc, :" ~ $_.key ~ "<" ~ $_.value ~ ">";
- }
- }
- self.O($desc)
- }
-}
-
-grammar Perl6::QGrammar is HLL::Grammar does STD {
-
- method throw_unrecog_backslash_seq ($sequence) {
- self.typed_sorry('X::Backslash::UnrecognizedSequence', :$sequence);
- }
-
- proto token escape {*}
- proto token backslash {*}
-
- role b1 {
- token escape:sym<\\> { <sym> {} <item=.backslash> }
- token backslash:sym<qq> { <?before 'q'> <quote=.LANG('MAIN','quote')> }
- token backslash:sym<\\> { <text=.sym> }
- token backslash:sym<stopper> { <text=.stopper> }
- token backslash:sym<a> { <sym> }
- token backslash:sym<b> { <sym> }
- token backslash:sym<c> { <sym> <charspec> }
- token backslash:sym<e> { <sym> }
- token backslash:sym<f> { <sym> }
- token backslash:sym<n> { <sym> }
- token backslash:sym<o> { :dba('octal character') <sym> [ <octint> | '[' ~ ']' <octints> ] }
- token backslash:sym<r> { <sym> }
- token backslash:sym<t> { <sym> }
- token backslash:sym<x> { :dba('hex character') <sym> [ <hexint> | '[' ~ ']' <hexints> ] }
- token backslash:sym<0> { <sym> }
- }
-
- role b0 {
- token escape:sym<\\> { <!> }
- }
-
- role c1 {
- token escape:sym<{ }> { <?before '{'> <block=.LANG('MAIN','block')> }
- }
-
- role c0 {
- token escape:sym<{ }> { <!> }
- }
-
- role s1 {
- token escape:sym<$> {
- :my $*QSIGIL := '$';
- <?before '$'>
- [ <EXPR=.LANG('MAIN', 'EXPR', 'y=')> || { $*W.throw($/, 'X::Backslash::NonVariableDollar') } ]
- }
- }
-
- role s0 {
- token escape:sym<$> { <!> }
- }
-
- role a1 {
- token escape:sym<@> {
- :my $*QSIGIL := '@';
- <?before '@'>
- <EXPR=.LANG('MAIN', 'EXPR', 'y=')>
- }
- }
-
- role a0 {
- token escape:sym<@> { <!> }
- }
-
- role h1 {
- token escape:sym<%> {
- :my $*QSIGIL := '%';
- <?before '%'>
- <EXPR=.LANG('MAIN', 'EXPR', 'y=')>
- }
- }
-
- role h0 {
- token escape:sym<%> { <!> }
- }
-
- role f1 {
- token escape:sym<&> {
- :my $*QSIGIL := '&';
- <?before '&'>
- <EXPR=.LANG('MAIN', 'EXPR', 'y=')>
- }
- }
-
- role f0 {
- token escape:sym<&> { <!> }
- }
-
- role p1 {
- method postprocessor () { 'path' }
- }
-
- role p0 {
- method postprocessor () { 'null' }
- }
-
- role v1 {
- method postprocessor () { 'val' }
- }
-
- role v0 {
- method postprocessor () { 'null' }
- }
-
- role w1 {
- method postprocessor () { 'words' }
- }
-
- role w0 {
- method postprocessor () { 'null' }
- }
-
- role ww1 {
- method postprocessor () { 'quotewords' }
- token escape:sym<' '> {
- <?[']> <quote=.LANG('MAIN','quote')>
- }
- token escape:sym<" "> {
- <?["]> <quote=.LANG('MAIN','quote')>
- }
- token escape:sym<colonpair> {
- <?[:]> <colonpair=.LANG('MAIN','colonpair')>
- }
- }
-
- role ww0 {
- method postprocessor () { 'null' }
- }
-
- role x1 {
- method postprocessor () { 'run' }
- }
-
- role x0 {
- method postprocessor () { 'null' }
- }
-
- role to[$herelang] {
- method herelang() { $herelang }
- method postprocessor () { 'heredoc' }
- }
-
- role q {
- token stopper { \' }
-
- token escape:sym<\\> { <sym> <item=.backslash> }
-
- token backslash:sym<qq> { <?before 'q'> <quote=.LANG('MAIN','quote')> }
- token backslash:sym<\\> { <text=.sym> }
- token backslash:sym<stopper> { <text=.stopper> }
-
- token backslash:sym<miscq> { {} . }
-
- method tweak_q($v) { self.panic("Too late for :q") }
- method tweak_qq($v) { self.panic("Too late for :qq") }
- }
-
- role qq does b1 does c1 does s1 does a1 does h1 does f1 {
- token stopper { \" }
- token backslash:sym<unrec> { {} (\w) { self.throw_unrecog_backslash_seq: $/[0].Str } }
- token backslash:sym<misc> { \W }
-
- method tweak_q($v) { self.panic("Too late for :q") }
- method tweak_qq($v) { self.panic("Too late for :qq") }
- }
-
- token nibbler {
- :my @*nibbles;
- <.do_nibbling>
- }
-
- token do_nibbling {
- :my $from := self.pos;
- :my $to := $from;
- [
- <!before <stopper> >
- [
- || <starter> <nibbler> <stopper>
- {
- my $c := $/.CURSOR;
- $to := $<starter>[-1].from;
- if $from != $to {
- nqp::push(@*nibbles, nqp::substr($c.orig, $from, $to - $from));
- }
-
- nqp::push(@*nibbles, $<starter>[-1].Str);
- nqp::push(@*nibbles, $<nibbler>[-1]);
- nqp::push(@*nibbles, $<stopper>[-1].Str);
-
- $from := $to := $c.pos;
- }
- || <escape>
- {
- my $c := $/.CURSOR;
- $to := $<escape>[-1].from;
- if $from != $to {
- nqp::push(@*nibbles, nqp::substr($c.orig, $from, $to - $from));
- }
-
- nqp::push(@*nibbles, $<escape>[-1]);
-
- $from := $to := $c.pos;
- }
- || .
- ]
- ]*
- {
- my $c := $/.CURSOR;
- $to := $c.pos;
- if $from != $to || !@*nibbles {
- nqp::push(@*nibbles, nqp::substr($c.orig, $from, $to - $from));
- }
- }
- }
-
- method truly($bool, $opt) {
- self.sorry("Cannot negate $opt adverb") unless $bool;
- self;
- }
-
- method tweak_q($v) { self.truly($v, ':q'); self.HOW.mixin(self, Perl6::QGrammar::q) }
- method tweak_single($v) { self.tweak_q($v) }
- method tweak_qq($v) { self.truly($v, ':qq'); self.HOW.mixin(self, Perl6::QGrammar::qq); }
- method tweak_double($v) { self.tweak_qq($v) }
-
- method tweak_b($v) { self.HOW.mixin(self, $v ?? b1 !! b0) }
- method tweak_backslash($v) { self.tweak_b($v) }
- method tweak_s($v) { self.HOW.mixin(self, $v ?? s1 !! s0) }
- method tweak_scalar($v) { self.tweak_s($v) }
- method tweak_a($v) { self.HOW.mixin(self, $v ?? a1 !! a0) }
- method tweak_array($v) { self.tweak_a($v) }
- method tweak_h($v) { self.HOW.mixin(self, $v ?? h1 !! h0) }
- method tweak_hash($v) { self.tweak_h($v) }
- method tweak_f($v) { self.HOW.mixin(self, $v ?? f1 !! f0) }
- method tweak_function($v) { self.tweak_f($v) }
- method tweak_c($v) { self.HOW.mixin(self, $v ?? c1 !! c0) }
- method tweak_closure($v) { self.tweak_c($v) }
-
- method tweak_x($v) { self.HOW.mixin(self, $v ?? x1 !! x0) }
- method tweak_exec($v) { self.tweak_x($v) }
- method tweak_w($v) { self.HOW.mixin(self, $v ?? w1 !! w0) }
- method tweak_words($v) { self.tweak_w($v) }
- method tweak_ww($v) { self.HOW.mixin(self, $v ?? ww1 !! ww0) }
- method tweak_quotewords($v) { self.tweak_ww($v) }
-
- method tweak_to($v) {
- self.truly($v, ':to');
- %*LANG<Q>.HOW.mixin(%*LANG<Q>, to.HOW.curry(to, self))
- }
- method tweak_heredoc($v) { self.tweak_to($v) }
-
- method tweak_regex($v) {
- self.truly($v, ':regex');
- return %*LANG<Regex>;
- }
-}
-
-grammar Perl6::RegexGrammar is QRegex::P6Regex::Grammar does STD {
- method throw_unrecognized_metachar ($metachar) {
- self.typed_sorry('X::Syntax::Regex::UnrecognizedMetachar', :$metachar);
- }
- method throw_null_pattern() {
- self.typed_sorry('X::Syntax::Regex::NullRegex');
- }
-
- token rxstopper { <stopper> }
-
- token metachar:sym<:my> {
- ':' <?before 'my'|'constant'|'state'|'our'> <statement=.LANG('MAIN', 'statement')> <.ws> ';'
- }
-
- token metachar:sym<{ }> {
- <?[{]> <codeblock>
- }
-
- token metachar:sym<rakvar> {
- <?before <[$@&]> [<alpha> | \W<alpha>]> <var=.LANG('MAIN', 'variable')>
- { self.check_variable($<var>) }
- }
-
- token metachar:sym<qw> {
- <?before '<' \s > # (note required whitespace)
- '<' <nibble(self.quote_lang(%*LANG<Q>, "<", ">", ['q', 'w']))> '>'
- }
-
- token metachar:sym<'> { <?[']> <quote=.LANG('MAIN','quote')> }
-
- token metachar:sym<"> { <?["]> <quote=.LANG('MAIN','quote')> }
-
- token assertion:sym<{ }> {
- <?[{]> <codeblock>
- }
-
- token assertion:sym<?{ }> {
- $<zw>=[ <[?!]> <?before '{'> ] <codeblock>
- }
-
- token assertion:sym<var> {
- <?[$@&]> <var=.LANG('MAIN', 'variable')>
- }
-
- token assertion:sym<~~> {
- <sym>
- [ <?before '>'> | $<num>=[\d+] | <desigilname=.LANG('MAIN','desigilname')> ]
- }
-
- token codeblock {
- <block=.LANG('MAIN','block')>
- }
-
- token arglist {
- :my $*IN_REGEX_ASSERTION := 1;
- <arglist=.LANG('MAIN','arglist')>
- }
-
- token assertion:sym<name> {
- <longname=.LANG('MAIN','longname')>
- [
- | <?before '>'>
- | '=' <assertion>
- | ':' <arglist>
- | '(' <arglist> ')'
- | <.normspace> <nibbler>
- ]?
- }
-}
-
-grammar Perl6::P5RegexGrammar is QRegex::P5Regex::Grammar does STD {
- token rxstopper { <stopper> }
-}
diff --git a/tests/examplefiles/Optimizer.pm6 b/tests/examplefiles/Optimizer.pm6
deleted file mode 100644
index 63844b0f..00000000
--- a/tests/examplefiles/Optimizer.pm6
+++ /dev/null
@@ -1,702 +0,0 @@
-use NQPP6QRegex;
-use QAST;
-
-# This powers the optimization pass. It takes place after we've done all
-# of the stuff in the grammar and actions, which means CHECK time is over.
-# Thus we're allowed to assume that lexpads are immutable, declarations are
-# over and done with, multi candidate lists won't change and so forth.
-class Perl6::Optimizer {
- # Tracks the nested blocks we're in; it's the lexical chain, essentially.
- has @!block_stack;
-
- # How deep a chain we're in, for chaining operators.
- has $!chain_depth;
-
- # Unique ID for topic ($_) preservation registers.
- has $!pres_topic_counter;
-
- # Unique ID for inline args variables.
- has $!inline_arg_counter;
-
- # Things that should cause compilation to fail; keys are errors, value is
- # array of line numbers.
- has %!deadly;
-
- # Things that should be warned about; keys are warnings, value is an array
- # of line numbers.
- has %!worrying;
-
- # The type type, Mu.
- has $!Mu;
-
- has %!foldable_junction;
- has %!foldable_outer;
-
- # Entry point for the optimization process.
- method optimize($past, *%adverbs) {
- # Initialize.
- @!block_stack := [$past[0]];
- $!chain_depth := 0;
- $!pres_topic_counter := 0;
- $!inline_arg_counter := 0;
- %!deadly := nqp::hash();
- %!worrying := nqp::hash();
- my $*DYNAMICALLY_COMPILED := 0;
- %!foldable_junction{'&infix:<|>'} := '&infix:<||>';
- %!foldable_junction{'&infix:<&>'} := '&infix:<&&>';
-
- # until there's a good way to figure out flattening at compile time,
- # don't support these junctions
- #%!foldable_junction{'&any'} := '&infix:<||>';
- #%!foldable_junction{'&all'} := '&infix:<&&>';
-
- %!foldable_outer{'&prefix:<?>'} := 1;
- %!foldable_outer{'&prefix:<!>'} := 1;
- %!foldable_outer{'&prefix:<so>'} := 1;
- %!foldable_outer{'&prefix:<not>'} := 1;
-
- %!foldable_outer{'if'} := 1;
- %!foldable_outer{'unless'} := 1;
- %!foldable_outer{'while'} := 1;
- %!foldable_outer{'until'} := 1;
-
- # Work out optimization level.
- my $*LEVEL := nqp::existskey(%adverbs, 'optimize') ??
- +%adverbs<optimize> !! 2;
-
- # Locate UNIT and some other useful symbols.
- my $unit := $past<UNIT>;
- my $*GLOBALish := $past<GLOBALish>;
- my $*W := $past<W>;
- unless nqp::istype($unit, QAST::Block) {
- nqp::die("Optimizer could not find UNIT");
- }
- nqp::push(@!block_stack, $unit);
- $!Mu := self.find_lexical('Mu');
- nqp::pop(@!block_stack);
-
- # Walk and optimize the program.
- self.visit_block($unit);
-
- # Die if we failed check in any way; otherwise, print any warnings.
- if +%!deadly {
- my @fails;
- for %!deadly {
- my @parts := nqp::split("\n", $_.key);
- my $headline := @parts.shift();
- @fails.push("$headline (line" ~ (+$_.value == 1 ?? ' ' !! 's ') ~
- nqp::join(', ', $_.value) ~ ")" ~
- (+@parts ?? "\n" ~ nqp::join("\n", @parts) !! ""));
- }
- nqp::die("CHECK FAILED:\n" ~ nqp::join("\n", @fails))
- }
- if +%!worrying {
- pir::printerr__vs("WARNINGS:\n");
- my @fails;
- for %!worrying {
- pir::printerr__vs($_.key ~ " (line" ~ (+$_.value == 1 ?? ' ' !! 's ') ~
- nqp::join(', ', $_.value) ~ ")\n");
- }
- }
-
- $past
- }
-
- # Called when we encounter a block in the tree.
- method visit_block($block) {
- # Push block onto block stack.
- @!block_stack.push($block);
-
- # Visit children.
- if $block<DYNAMICALLY_COMPILED> {
- my $*DYNAMICALLY_COMPILED := 1;
- self.visit_children($block);
- }
- else {
- self.visit_children($block);
- }
-
- # Pop block from block stack.
- @!block_stack.pop();
-
- # If the block is immediate, we may be able to inline it.
- my $outer := @!block_stack[+@!block_stack - 1];
- if $block.blocktype eq 'immediate' && !$*DYNAMICALLY_COMPILED {
- # Scan symbols for any non-interesting ones.
- my @sigsyms;
- for $block.symtable() {
- my $name := $_.key;
- if $name ne '$_' && $name ne 'call_sig' && $name ne '$*DISPATCHER' {
- @sigsyms.push($name);
- }
- }
-
- # If we have no interesting ones, then we can inline the
- # statements.
- # XXX We can also check for lack of colliding symbols and
- # do something in that case. However, it's non-trivial as
- # the static lexpad entries will need twiddling with.
- if +@sigsyms == 0 {
- if $*LEVEL >= 3 {
- return self.inline_immediate_block($block, $outer);
- }
- }
- }
-
- $block
- }
- method is_from_core($name) {
- my $i := +@!block_stack;
- while $i > 0 {
- $i := $i - 1;
- my $block := @!block_stack[$i];
- my %sym := $block.symbol($name);
- if +%sym && nqp::existskey(%sym, 'value') {
- my %sym := $block.symbol("!CORE_MARKER");
- if +%sym {
- return 1;
- }
- return 0;
- }
- }
- return 0;
- }
-
- method can_chain_junction_be_warped($node) {
- sub has_core-ish_junction($node) {
- if nqp::istype($node, QAST::Op) && $node.op eq 'call' &&
- nqp::existskey(%!foldable_junction, $node.name) {
- if self.is_from_core($node.name) {
- # TODO: special handling for any()/all(), because they create
- # a Stmts with a infix:<,> in it.
- if +$node.list == 1 {
- return 0;
- }
- return 1;
- }
- }
- return 0;
- }
-
- if has_core-ish_junction($node[0]) {
- return 0;
- } elsif has_core-ish_junction($node[1]) {
- return 1;
- }
- return -1;
- }
-
- # Called when we encounter a QAST::Op in the tree. Produces either
- # the op itself or some replacement opcode to put in the tree.
- method visit_op($op) {
- # If it's a QAST::Op of type handle, needs some special attention.
- my $optype := $op.op;
- if $optype eq 'handle' {
- return self.visit_handle($op);
- }
-
- # A chain with exactly two children can become the op itself.
- if $optype eq 'chain' {
- $!chain_depth := $!chain_depth + 1;
- $optype := 'call' if $!chain_depth == 1 &&
- !(nqp::istype($op[0], QAST::Op) && $op[0].op eq 'chain') &&
- !(nqp::istype($op[1], QAST::Op) && $op[1].op eq 'chain');
- }
-
- # there's a list of foldable outers up in the constructor.
- sub is_outer_foldable() {
- if $op.op eq "call" {
- if nqp::existskey(%!foldable_outer, $op.name) && self.is_from_core($op.name) {
- return 1;
- }
- } elsif nqp::existskey(%!foldable_outer, $op.op) {
- return 1;
- }
- return 0;
- }
-
- # we may be able to unfold a junction at compile time.
- if $*LEVEL >= 2 && is_outer_foldable() && nqp::istype($op[0], QAST::Op) && $op[0].op eq "chain" {
- my $exp-side := self.can_chain_junction_be_warped($op[0]);
- if $exp-side != -1 {
- my $juncop := $op[0][$exp-side].name eq '&infix:<&>' ?? 'if' !! 'unless';
- my $juncname := %!foldable_junction{$op[0][$exp-side].name};
- my $chainop := $op[0].op;
- my $chainname := $op[0].name;
- my $values := $op[0][$exp-side];
- my $ovalue := $op[0][1 - $exp-side];
-
- # the first time $valop is refered to, create a bind op for a
- # local var, next time create a reference var op.
- my %reference;
- sub refer_to($valop) {
- my $id := $valop;
- if nqp::existskey(%reference, $id) {
- QAST::Var.new(:name(%reference{$id}), :scope<local>);
- } else {
- %reference{$id} := $op.unique('junction_unfold');
- QAST::Op.new(:op<bind>,
- QAST::Var.new(:name(%reference{$id}),
- :scope<local>,
- :decl<var>),
- $valop);
- }
- }
-
- # create a comparison operation for the inner comparisons
- sub chain($value) {
- if $exp-side == 0 {
- QAST::Op.new(:op($chainop), :name($chainname),
- $value,
- refer_to($ovalue));
- } else {
- QAST::Op.new(:op($chainop), :name($chainname),
- refer_to($ovalue),
- $value);
- }
- }
-
- # create a chain of outer logical junction operators with inner comparisons
- sub create_junc() {
- my $junc := QAST::Op.new(:name($juncname), :op($juncop));
-
- $junc.push(chain($values.shift()));
-
- if +$values.list > 1 {
- $junc.push(create_junc());
- } else {
- $junc.push(chain($values.shift()));
- }
- return $junc;
- }
-
- $op.shift;
- $op.unshift(create_junc());
- #say($op.dump);
- return self.visit_op($op);
- }
- }
-
- # Visit the children.
- self.visit_children($op);
-
- # Calls are especially interesting as we may wish to do some
- # kind of inlining.
- if $optype eq 'call' && $op.name ne '' {
- # See if we can find the thing we're going to call.
- my $obj;
- my $found;
- try {
- $obj := self.find_lexical($op.name);
- $found := 1;
- }
- if $found {
- # If it's an onlystar proto, we have a couple of options.
- # The first is that we may be able to work out what to
- # call at compile time. Failing that, we can at least inline
- # the proto.
- my $dispatcher;
- try { if $obj.is_dispatcher { $dispatcher := 1 } }
- if $dispatcher && $obj.onlystar {
- # Try to do compile-time multi-dispatch. Need to consider
- # both the proto and the multi candidates.
- my @ct_arg_info := self.analyze_args_for_ct_call($op);
- if +@ct_arg_info {
- my @types := @ct_arg_info[0];
- my @flags := @ct_arg_info[1];
- my $ct_result_proto := pir::perl6_trial_bind_ct__IPPP($obj.signature, @types, @flags);
- my @ct_result_multi := pir::perl6_multi_dispatch_ct__PPPP($obj, @types, @flags);
- if $ct_result_proto == 1 && @ct_result_multi[0] == 1 {
- my $chosen := @ct_result_multi[1];
- if $op.op eq 'chain' { $!chain_depth := $!chain_depth - 1 }
- if $*LEVEL >= 2 {
- return nqp::can($chosen, 'inline_info') && nqp::istype($chosen.inline_info, QAST::Node)
- ?? self.inline_call($op, $chosen)
- !! self.call_ct_chosen_multi($op, $obj, $chosen);
- }
- }
- elsif $ct_result_proto == -1 || @ct_result_multi[0] == -1 {
- self.report_innevitable_dispatch_failure($op, @types, @flags, $obj,
- :protoguilt($ct_result_proto == -1));
- }
- }
-
- # Otherwise, inline the proto.
- if $op.op eq 'chain' { $!chain_depth := $!chain_depth - 1 }
- if $*LEVEL >= 2 {
- return self.inline_proto($op, $obj);
- }
- }
- elsif !$dispatcher && nqp::can($obj, 'signature') {
- # If we know enough about the arguments, do a "trial bind".
- my @ct_arg_info := self.analyze_args_for_ct_call($op);
- if +@ct_arg_info {
- my @types := @ct_arg_info[0];
- my @flags := @ct_arg_info[1];
- my $ct_result := pir::perl6_trial_bind_ct__IPPP($obj.signature, @types, @flags);
- if $ct_result == 1 {
- if $op.op eq 'chain' { $!chain_depth := $!chain_depth - 1 }
- #say("# trial bind worked!");
- if $*LEVEL >= 2 {
- return nqp::can($obj, 'inline_info') && nqp::istype($obj.inline_info, QAST::Node)
- ?? self.inline_call($op, $obj)
- !! copy_returns($op, $obj);
- }
- }
- elsif $ct_result == -1 {
- self.report_innevitable_dispatch_failure($op, @types, @flags, $obj);
- }
- }
- }
- }
- else {
- # We really should find routines; failure to do so is a CHECK
- # time error. Check that it's not just compile-time unknown,
- # however (shows up in e.g. sub foo(&x) { x() }).
- unless self.is_lexical_declared($op.name) {
- self.add_deadly($op, "Undefined routine '" ~ $op.name ~ "' called");
- }
- }
- }
-
- # If it's a private method call, we can sometimes resolve it at
- # compile time. If so, we can reduce it to a sub call in some cases.
- elsif $*LEVEL >= 3 && $op.op eq 'callmethod' && $op.name eq 'dispatch:<!>' {
- if $op[1].has_compile_time_value && nqp::istype($op[1], QAST::Want) && $op[1][1] eq 'Ss' {
- my $name := $op[1][2].value; # get raw string name
- my $pkg := $op[2].returns; # actions always sets this
- my $meth := $pkg.HOW.find_private_method($pkg, $name);
- if $meth {
- try {
- $*W.get_ref($meth); # may fail, thus the try; verifies it's in SC
- my $call := QAST::WVal.new( :value($meth) );
- my $inv := $op.shift;
- $op.shift; $op.shift; # name, package (both pre-resolved now)
- $op.unshift($inv);
- $op.unshift($call);
- $op.op('call');
- $op.name(nqp::null());
- }
- }
- else {
- self.add_deadly($op, "Undefined private method '" ~ $name ~ "' called");
- }
- }
- }
-
- # If we end up here, just leave op as is.
- if $op.op eq 'chain' {
- $!chain_depth := $!chain_depth - 1;
- }
- $op
- }
-
- # Handles visiting a QAST::Op :op('handle').
- method visit_handle($op) {
- self.visit_children($op, :skip_selectors);
- $op
- }
-
- # Handles visiting a QAST::Want node.
- method visit_want($want) {
- # Just visit the children for now. We ignore the literal strings, so
- # it all works out.
- self.visit_children($want, :skip_selectors)
- }
-
- # Handles visit a variable node.
- method visit_var($var) {
- # Nothing to do yet.
- }
-
- # Checks arguments to see if we're going to be able to do compile
- # time analysis of the call.
- my @allo_map := ['', 'Ii', 'Nn', 'Ss'];
- my %allo_rev := nqp::hash('Ii', 1, 'Nn', 2, 'Ss', 3);
- method analyze_args_for_ct_call($op) {
- my @types;
- my @flags;
- my @allomorphs;
- my $num_prim := 0;
- my $num_allo := 0;
-
- # Initial analysis.
- for @($op) {
- # Can't cope with flattening or named.
- if $_.flat || $_.named ne '' {
- return [];
- }
-
- # See if we know the node's type; if so, check it.
- my $type := $_.returns();
- my $ok_type := 0;
- try $ok_type := nqp::istype($type, $!Mu);
- if $ok_type {
- my $prim := pir::repr_get_primitive_type_spec__IP($type);
- my $allo := $_.has_compile_time_value && nqp::istype($_, QAST::Want)
- ?? $_[1] !! '';
- @types.push($type);
- @flags.push($prim);
- @allomorphs.push($allo);
- $num_prim := $num_prim + 1 if $prim;
- $num_allo := $num_allo + 1 if $allo;
- }
- else {
- return [];
- }
- }
-
- # See if we have an allomorphic constant that may allow us to do
- # a native dispatch with it; takes at least one declaratively
- # native argument to make this happen.
- if @types == 2 && $num_prim == 1 && $num_allo == 1 {
- my $prim_flag := @flags[0] || @flags[1];
- my $allo_idx := @allomorphs[0] ?? 0 !! 1;
- if @allomorphs[$allo_idx] eq @allo_map[$prim_flag] {
- @flags[$allo_idx] := $prim_flag;
- }
- }
-
- # Alternatively, a single arg that is allomorphic will prefer
- # the literal too.
- if @types == 1 && $num_allo == 1 {
- @flags[0] := %allo_rev{@allomorphs[0]} // 0;
- }
-
- [@types, @flags]
- }
-
- method report_innevitable_dispatch_failure($op, @types, @flags, $obj, :$protoguilt) {
- my @arg_names;
- my $i := 0;
- while $i < +@types {
- @arg_names.push(
- @flags[$i] == 1 ?? 'int' !!
- @flags[$i] == 2 ?? 'num' !!
- @flags[$i] == 3 ?? 'str' !!
- @types[$i].HOW.name(@types[$i]));
- $i := $i + 1;
- }
- self.add_deadly($op,
- ($protoguilt ?? "Calling proto of '" !! "Calling '") ~
- $obj.name ~ "' will never work with " ~
- (+@arg_names == 0 ??
- "no arguments" !!
- "argument types (" ~ nqp::join(', ', @arg_names) ~ ")"),
- $obj.is_dispatcher && !$protoguilt ??
- multi_sig_list($obj) !!
- [" Expected: " ~ $obj.signature.perl]);
- }
-
- # Signature list for multis.
- sub multi_sig_list($dispatcher) {
- my @sigs := [" Expected any of:"];
- for $dispatcher.dispatchees {
- @sigs.push(" " ~ $_.signature.perl);
- }
- @sigs
- }
-
- # Visits all of a nodes children, and dispatches appropriately.
- method visit_children($node, :$skip_selectors) {
- my $i := 0;
- while $i < +@($node) {
- unless $skip_selectors && $i % 2 {
- my $visit := $node[$i];
- if nqp::istype($visit, QAST::Op) {
- $node[$i] := self.visit_op($visit)
- }
- elsif nqp::istype($visit, QAST::Want) {
- self.visit_want($visit);
- }
- elsif nqp::istype($visit, QAST::Var) {
- self.visit_var($visit);
- }
- elsif nqp::istype($visit, QAST::Block) {
- $node[$i] := self.visit_block($visit);
- }
- elsif nqp::istype($visit, QAST::Stmts) {
- self.visit_children($visit);
- }
- elsif nqp::istype($visit, QAST::Stmt) {
- self.visit_children($visit);
- }
- }
- $i := $i + 1;
- }
- }
-
- # Locates a lexical symbol and returns its compile time value. Dies if
- # it does not exist.
- method find_lexical($name) {
- my $i := +@!block_stack;
- while $i > 0 {
- $i := $i - 1;
- my $block := @!block_stack[$i];
- my %sym := $block.symbol($name);
- if +%sym {
- if nqp::existskey(%sym, 'value') {
- return %sym<value>;
- }
- else {
- nqp::die("Optimizer: No lexical compile time value for $name");
- }
- }
- }
- nqp::die("Optimizer: No lexical $name found");
- }
-
- # Checks if a given lexical is declared, though it needn't have a compile
- # time known value.
- method is_lexical_declared($name) {
- my $i := +@!block_stack;
- while $i > 0 {
- $i := $i - 1;
- my $block := @!block_stack[$i];
- my %sym := $block.symbol($name);
- if +%sym {
- return 1;
- }
- }
- 0
- }
-
- # Inlines an immediate block.
- method inline_immediate_block($block, $outer) {
- # Sanity check.
- return $block if +@($block) != 2;
-
- # Extract interesting parts of block.
- my $decls := $block.shift;
- my $stmts := $block.shift;
-
- # Turn block into an "optimized out" stub (deserialization
- # or fixup will still want it to be there).
- $block.blocktype('declaration');
- $block[0] := QAST::Op.new( :op('die_s'),
- QAST::SVal.new( :value('INTERNAL ERROR: Execution of block eliminated by optimizer') ) );
- $outer[0].push($block);
-
- # Copy over interesting stuff in declaration section.
- for @($decls) {
- if nqp::istype($_, QAST::Op) && ($_.op eq 'p6bindsig' ||
- $_.op eq 'bind' && $_[0].name eq 'call_sig') {
- # Don't copy this binder call or setup.
- }
- elsif nqp::istype($_, QAST::Var) && ($_.name eq '$/' || $_.name eq '$!' ||
- $_.name eq '$_' || $_.name eq '$*DISPATCHER') {
- # Don't copy this variable node.
- }
- else {
- $outer[0].push($_);
- }
- }
-
- # Hand back the statements, but be sure to preserve $_
- # around them.
- $!pres_topic_counter := $!pres_topic_counter + 1;
- $outer[0].push(QAST::Var.new( :scope('local'),
- :name("pres_topic_$!pres_topic_counter"), :decl('var') ));
- return QAST::Stmts.new(
- :resultchild(1),
- QAST::Op.new( :op('bind'),
- QAST::Var.new( :name("pres_topic_$!pres_topic_counter"), :scope('local') ),
- QAST::Var.new( :name('$_'), :scope('lexical') )
- ),
- $stmts,
- QAST::Op.new( :op('bind'),
- QAST::Var.new( :name('$_'), :scope('lexical') ),
- QAST::Var.new( :name("pres_topic_$!pres_topic_counter"), :scope('local') )
- )
- );
- }
-
- # Inlines a proto.
- method inline_proto($call, $proto) {
- $call.unshift(QAST::Op.new(
- :op('p6mdthunk'),
- QAST::Var.new( :name($call.name), :scope('lexical') )));
- $call.name(nqp::null());
- $call.op('call');
- $call
- }
-
- # Inlines a call to a sub.
- method inline_call($call, $code_obj) {
- # If the code object is marked soft, can't inline it.
- if nqp::can($code_obj, 'soft') && $code_obj.soft {
- return $call;
- }
-
- # Bind the arguments to temporaries.
- my $inlined := QAST::Stmts.new();
- my @subs;
- for $call.list {
- my $temp_name := '_inline_arg_' ~ ($!inline_arg_counter := $!inline_arg_counter + 1);
- my $temp_type := $_.returns;
- $inlined.push(QAST::Op.new(
- :op('bind'),
- QAST::Var.new( :name($temp_name), :scope('local'), :returns($temp_type), :decl('var') ),
- $_));
- nqp::push(@subs, QAST::Var.new( :name($temp_name), :scope('local'), :returns($temp_type) ));
- }
-
- # Now do the inlining.
- $inlined.push($code_obj.inline_info.substitute_inline_placeholders(@subs));
- if $call.named -> $name {
- $inlined.named($name);
- }
- $inlined.node($call.node);
-
- $inlined
- }
-
- # If we decide a dispatch at compile time, this emits the direct call.
- method call_ct_chosen_multi($call, $proto, $chosen) {
- my @cands := $proto.dispatchees();
- my $idx := 0;
- for @cands {
- if $_ =:= $chosen {
- $call.unshift(QAST::Op.new(
- :op('p6mdcandthunk'),
- QAST::Var.new( :name($call.name), :scope('lexical') ),
- QAST::IVal.new( :value($idx) )
- ));
- $call.name(nqp::null());
- $call.op('call');
- #say("# Compile-time resolved a call to " ~ $proto.name);
- last;
- }
- $idx := $idx + 1;
- }
- $call := copy_returns($call, $chosen);
- $call
- }
-
- # Adds an entry to the list of things that would cause a check fail.
- method add_deadly($past_node, $message, @extras?) {
- my $mnode := $past_node.node;
- my $line := HLL::Compiler.lineof($mnode.orig, $mnode.from);
- my $key := $message ~ (+@extras ?? "\n" ~ nqp::join("\n", @extras) !! "");
- unless %!deadly{$key} {
- %!deadly{$key} := [];
- }
- %!deadly{$key}.push($line);
- }
-
- my @prim_spec_ops := ['', 'p6box_i', 'p6box_n', 'p6box_s'];
- my @prim_spec_flags := ['', 'Ii', 'Nn', 'Ss'];
- sub copy_returns($to, $from) {
- if nqp::can($from, 'returns') {
- my $ret_type := $from.returns();
- if pir::repr_get_primitive_type_spec__IP($ret_type) -> $primspec {
- $to := QAST::Want.new(
- :named($to.named),
- QAST::Op.new( :op(@prim_spec_ops[$primspec]), $to ),
- @prim_spec_flags[$primspec], $to);
- }
- $to.returns($ret_type);
- }
- $to
- }
-}