=begin pod :kind("Type") :subkind("class") :category("domain-specific")

=TITLE class Grammar

=SUBTITLE Formal grammar made up of named regexes

    class Grammar is Match {}

Every type declared with C<grammar>, and not explicitly stating its superclass,
becomes a subclass of I<Grammar>.

    grammar Identifier {
        token TOP       { <initial> <rest>* }
        token initial   { <+myletter +[_]> }
        token rest      { <+myletter +mynumber +[_]> }
        token myletter  { <[A..Za..z]> }
        token mynumber  { <[0..9]> }
    }

    say Identifier.isa(Grammar);                # OUTPUT: «True␤»
    my $match = Identifier.parse('W4anD0eR96');
    say ~$match;                                # OUTPUT: «W4anD0eR96␤»

More L<documentation on grammars|/language/grammars> is available.

=head1 Methods

=head2 method parse

Defined as:

    method parse($target, :$rule = 'TOP',  Capture() :$args = \(), Mu :$actions = Mu, *%opt)

Parses the C<$target>, which will be coerced to L<Str|/type/Str> if it isn't
one, using C<$rule> as the starting rule. Additional C<$args> will be passed
to the starting rule if provided.

    grammar RepeatChar {
        token start($character) { $character+ }
    }

    say RepeatChar.parse('aaaaaa', :rule('start'), :args(\('a')));
    say RepeatChar.parse('bbbbbb', :rule('start'), :args(\('b')));

    # OUTPUT:
    # ｢aaaaaa｣
    # ｢bbbbbb｣

If the C<actions> named argument is provided, it will be used as an actions
object, that is, for each successful regex match, a method of the same name,
if it exists, is called on the actions object, passing the match object as the
sole positional argument.

    my $actions = class { method TOP($/) { say "7" } };
    grammar { token TOP { a { say "42" } b } }.parse('ab', :$actions);
    # OUTPUT: «42␤7␤»

Additional named arguments are used as options for matching, so you can for example specify
things like C<:pos(4)> to start parsing from the fifth (:pos is zero-based) character.
All L<matching adverbs|/language/regexes#Adverbs> are allowed, but not all of
them take effect. There are several types of adverbs that a regex can have,
some of which apply at compile time, like C<:s> and C<:i>. You cannot pass those
to C<.parse>, because the regexes have already been compiled. But, you can pass
those adverbs that affect the runtime behavior, such as C<:pos> and C<:continue>.

=for code :preamble<grammar RepeatChar {}>
say RepeatChar.parse('bbbbbb', :rule('start'), :args(\('b')), :pos(4)).Str;
# OUTPUT: «bb␤»

Method C<parse> only succeeds if the cursor has arrived at the end of the
target string when the match is over. Use L<method subparse|/routine/subparse>
if you want to be able to stop in the middle.

The top regex in the grammar will be allowed to backtrack.

Returns a L<Match object|/type/Match> on success, and L<Nil|/type/Nil> on
failure.

=head2 method subparse

Defined as:

    method subparse($target, :$rule = 'TOP', Capture() :$args = \(),  Mu :$actions = Mu, *%opt)

Does exactly the same as L<method parse|/routine/parse>, except that cursor
doesn't have to reach the end of the string to succeed. That is, it doesn't
have to match the whole string.

Note that unlike L<method parse|/routine/parse>, C<subparse> I<always> returns
a L<Match|/type/Match> object, which will be a failed match (and thus falsy),
if the grammar failed to match.

    grammar RepeatChar {
        token start($character) { $character+ }
    }

    say RepeatChar.subparse('bbbabb', :rule('start'), :args(\('b')));
    say RepeatChar.parse(   'bbbabb', :rule('start'), :args(\('b')));
    say RepeatChar.subparse('bbbabb', :rule('start'), :args(\('a')));
    say RepeatChar.subparse('bbbabb', :rule('start'), :args(\('a')), :pos(3));


    # OUTPUT:
    # ｢bbb｣
    # Nil
    # #<failed match>
    # ｢a｣

=head2 method parsefile

Defined as:

    method parsefile(Str(Cool) $filename, :$enc, *%opts)

Reads file C<$filename> encoding by C<$enc>, and parses it. All named arguments
are passed on to L<method parse|/routine/parse>.

    grammar Identifiers {
        token TOP        { [<identifier><.ws>]+ }
        token identifier { <initial> <rest>* }
        token initial    { <+myletter +[_]> }
        token rest       { <+myletter +mynumber +[_]> }
        token myletter   { <[A..Za..z]> }
        token mynumber   { <[0..9]> }
    }

    say Identifiers.parsefile('users.txt', :enc('UTF-8'))
        .Str.trim.subst(/\n/, ',', :g);

    # users.txt :
    # TimToady
    # lizmat
    # jnthn
    # moritz
    # zoffixznet
    # MasterDuke17

    # OUTPUT: «TimToady,lizmat,jnthn,moritz,zoffixznet,MasterDuke17␤»

=end pod

# vim: expandtab softtabstop=4 shiftwidth=4 ft=perl6
