Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl6: Convert Match object to JSON-serializable Hash

I am currently gettin' my hands dirty on some Perl6. Specifically I am trying to write a Fortran parser based on grammars (the Fortran::Grammar module)

For testing purposes, I would like to have the possiblity to convert a Match object into a JSON-serializable Hash.

Googling / official Perl6 documentation didn't help. My apologies if I overlooked something.

My attempts so far:

  • I know that one can convert a Match $m to a Hash via $m.hash. But this keeps nested Match objects.
  • Since this just has to be solvable via recursion, I tried but gave up in favor of asking first for the existance of a simpler/existing solution here
  • Dealing with Match objects' contents is obviously best accomplished via make/made. I would love to have a super simple Actions object to hand to .parse with a default method for all matches that basically just does a make $/.hash or something the like. I just have no idea on how to specify a default method.
like image 569
NobodyInPerson Avatar asked Jan 04 '23 14:01

NobodyInPerson


1 Answers

Here's an action class method from one of my Perl 6 projects, which does what you describe.

It does almost the same as what Christoph posted, but is written more verbosely (and I've added copious amounts of comments to make it easier to understand):

#| Fallback action method that produces a Hash tree from named captures.
method FALLBACK ($name, $/) {

    # Unless an embedded { } block in the grammar already called make()...
    unless $/.made.defined {

        # If the Match has named captures, produce a hash with one entry
        # per capture:
        if $/.hash -> %captures {
            make hash do for %captures.kv -> $k, $v {

                # The key of the hash entry is the capture's name.
                $k => $v ~~ Array 

                    # If the capture was repeated by a quantifier, the
                    # value becomes a list of what each repetition of the
                    # sub-rule produced:
                    ?? $v.map(*.made).cache 

                    # If the capture wasn't quantified, the value becomes
                    # what the sub-rule produced:
                    !! $v.made
            }
        }

        # If the Match has no named captures, produce the string it matched:
        else { make ~$/ }
    }
}

Notes:

  • This totally ignores positional captures (i.e. those made with ( ) inside the grammar) - only named captures (e.g. <foo> or <foo=bar>) are used to build the Hash tree. It could be amended to handle them too, depending on what you want to do with them. Keep in mind that:
    • $/.hash gives the named captures, as a Map.
    • $/.list gives the positional captures, as a List.
    • $/.caps (or $/.pairs) gives both the named and positional captures, as a sequence of name=>submatch and/or index=>submatch pairs.
  • It allows you to override the AST generation for specific rules, either by adding a { make ... } block inside the rule in the grammar (assuming that you never intentionally want to make an undefined value), or by adding a method with the rule's name to the action class.
like image 148
smls Avatar answered Jan 07 '23 04:01

smls