### a set of rules for parsing Captures in perl6

### ??? is please answer
### XXX is please do ;)

# ??? when capturing values that will be interpolated,
#      is a string the right representation?
#      ( e.g. $<value> = "%" ~ "hash" ~ "<a>" )

# ??? any twigils possible in flattened pairs?
# ??? any twigils possible in hash-access-for-named-args?

# ??? is it possible to pull a key and a value out of a flattened pair
#       during the parse?  if not, just leave $<key> and $<value>
#       blank?

# XXX need a generalized <hash_index> subrule
#       should include different brace semantics

# XXX need a generalized <quoted_value> subrule
#       should include qq{*} and friends

# XXX general pair handling should be moved to a separate grammar

grammar Capture {

    # stand-in for bracketing constructs
    # XXX add more or get unicode character classes working
    token open_group { <[<({[]> }   # should be Ps and <
    token close_group { <[>)}\]]> } # should be Pe and >
                           
    # perl6 sigils in captures
    # ??? define context here, or later in the parse?
    token is_array  { $<context> := <[@]> }
    token is_scalar { $<context> := <[$]> }
    token is_code   { $<context> := <[&]> }
    token is_hash   { $<context> := <[%]> }
    token sigil     { <is_scalar> | <is_array> | <is_hash> | <is_code> }
    token name      { [ <alpha> | _ ]+ [ <alpha> | \d | _ ]+  }
    token value     { <expression> }
    
    # the colon appears in a few different positions to indicate named-ness
    token is_named   { <[:]> }

    # adverbial pair form of named arguments
    # :$what, :what('when'), :what($when)
    token adv_name   := name;
    token adv_value  := value;
    token is_negated { <[!]> }
    token adv_twigil { <is_negated> }
    token adverbial_pair {
        [
        | <is_named> <adv_name> <open_group> <sigil> <adv_twigil>? <adv_value> <close_group>
        | <is_named> $<adv_value> := [ <sigil> <adv_twigil>? <adv_name> ]
        ]
        : { $<key>   := $<adv_name> }
        : { $<value> := $<adv_value }
    }

    # anonymous pair form of named arguments
    # foo => bar, 'foo' => bar, and 'foo', 'bar'
    # XXX same problem as bracketing characters: need character
    #       classes and smart open/close behavior
    
    token open_quote       { <['"]> } 
    token close_quote      { <['"]> } 
    token fat_comma        { =\> }
    token pair_constructor { <fat_comma> | <[,]> }
    token pair_value       { <expression( terminates => ',' )> }
    token bareword_key     { <name> }
    token quoted_key       { <open_quote> <name> <close_quote> }
    token complex_key      { <expression> }
    token pair_key {
        [
        | <bareword_key>
        | <quoted_key>
        | <complex_key>
        ]
    }

    # only args with bareword keys are parsed as named
    # ??? should this be turned into some sort of check on a general
    #       pair match?
    rule bareword_pair {
         [ $<key> := <bareword_key> ] <fat_comma> [ $<value> := <pair_value> ]
    }
    
    rule  anonymous_pair    { 
        [ $<key> := <pair_key> ] <pair_constructor> [ $<value> := <pair_value> ] 
    }
    
    # flattened pair form of named arguments
    # *$pair
    token is_flattened  { <[\[,\]]> }
    token flattened_pair {
        <is_flattened> <sigil> <twigil>? <name>
    }

    # hash-access-for-named-args form of named arguments
    # %hash:<a> goes to :a(%hash<a>)
    # ??? what's a nice catchy less-hyphenated name for this construction?
    # XXX since we're going to match hashes a lot, this should be cleaned up
    #       and factored out into a more general grammar
    token is_hash { <[%]> }
    token hashed_pair { 
        <is_hash> <name> <is_named> <open_group> <pair_key> <close_group>
        : { $<key>   := $<pair_key> }
        : { $<value> := $<is_hash> ~ $<name> ~ $<open_group> ~ $<key> ~ $<close_group> }
    }

    # generalized pairs
    token pair {
        [
        | <anonymous_pair>
        | <flattened_pair>
        | <adverbial_pair>
        | <hashed_pair>
        ]
    }

    # integrating all forms of named arguments into one token
    token named_arg {
         | <bareword_pair>
         | <adverbial_pair>
         | <hashed_pair>
         : $<arg_name>  := $<key>
         : $<arg_value> := $<value>
    }

    token positional { <expression( terminates => ',' )> }

    rule capture {
        [
        | <named>
        | <positional>
        ]*
    }
}
