| | 1 | = NQP-rx Tricks = |
| | 2 | |
| | 3 | Here are some tricks for parsing with NQP-rx: |
| | 4 | |
| | 5 | == Global Variables == |
| | 6 | |
| | 7 | A bunch of stuff in -rx is still done with globals. This will all move to contextuals, or to attributes of a common object, or something. Eventually. |
| | 8 | |
| | 9 | === HLL::Grammar::%!MARKHASH === |
| | 10 | |
| | 11 | The MARKED/MARKER special rules, which are used by default in the <ws> rule, tracks mark info in a global hash. In order to parse a different source, you need to reset this hash. |
| | 12 | {{{ |
| | 13 | Q:PIR { |
| | 14 | $P0 = null |
| | 15 | set_hll_global ['HLL';'Grammar'], '%!MARKHASH', $P0 |
| | 16 | }; |
| | 17 | }}} |
| | 18 | |
| | 19 | I use this to enable test cases for the grammar. The code below (built with the Kakapo library) runs the parser over and over again: |
| | 20 | {{{ |
| | 21 | method code_test(%code_matchers) { |
| | 22 | my $slam := Slam::Compiler.new; |
| | 23 | my $past; |
| | 24 | |
| | 25 | for %code_matchers -> $pair { |
| | 26 | my $code := $pair.key; |
| | 27 | say("Code: $code"); |
| | 28 | |
| | 29 | Q:PIR { |
| | 30 | $P0 = null |
| | 31 | set_hll_global ['HLL';'Grammar'], '%!MARKHASH', $P0 |
| | 32 | }; |
| | 33 | |
| | 34 | $past := $slam.compile: $code, :rule<EXPR>, :target<past>; #, :parsetrace; |
| | 35 | #_dumper($past); |
| | 36 | |
| | 37 | my $matcher := $pair.value; |
| | 38 | assert_match($past, $matcher, |
| | 39 | 'Failed to parse {' ~ $code ~ '} as expected'); |
| | 40 | } |
| | 41 | } |
| | 42 | |
| | 43 | method test_postcircumfix_array_index() { |
| | 44 | my %code_matchers; |
| | 45 | %code_matchers<x[0]> := var( :scope<keyed>, var( :name<x> ), val( :value<0> )); |
| | 46 | %code_matchers<x[ 1]> := var( :scope<keyed>, var( :name<x> ), val( :value<1> )); |
| | 47 | %code_matchers<x[ 2 ]> := var( :scope<keyed>, var( :name<x> ), val( :value<2> )); |
| | 48 | |
| | 49 | %code_matchers<x[ y ]> := var( :scope<keyed>, var( :name<x> ), var( :name<y> )); |
| | 50 | |
| | 51 | self.code_test: %code_matchers; |
| | 52 | } |
| | 53 | }}} |
| | 54 | |
| | 55 | The `var(...)` functions are syntactic sugar that generate a special Matcher for validating PCT trees. |
| | 56 | |
| | 57 | == HLL::Compiler == |
| | 58 | |
| | 59 | === Debugging === |
| | 60 | |
| | 61 | The Regex code supports debugging with a `!cursor_debug` call. To enable this, the topmost regex (your grammar) has to be called with the `'rxtrace'` flag. The default compiler doesn't appear to support this, so I overrode the `parse` method: |
| | 62 | {{{ |
| | 63 | class Slam::Compiler; |
| | 64 | |
| | 65 | method parse($source, *%adverbs) { |
| | 66 | unless %adverbs<actions> { |
| | 67 | %adverbs<actions> := %adverbs<target> eq 'parse' |
| | 68 | ?? pir::null__P() |
| | 69 | !! self.parseactions; |
| | 70 | } |
| | 71 | |
| | 72 | my %options_map := hash( |
| | 73 | actions => 'actions', |
| | 74 | parsetrace => 'rxtrace', |
| | 75 | rule => 'rule', |
| | 76 | ); |
| | 77 | |
| | 78 | my %parse_flags; |
| | 79 | |
| | 80 | for %options_map -> $map { |
| | 81 | %parse_flags{$map.value} := %adverbs{$map.key} |
| | 82 | if %adverbs.contains: $map.key; |
| | 83 | } |
| | 84 | |
| | 85 | my $match := self.parsegrammar.parse($source, :p(0), |%parse_flags); |
| | 86 | |
| | 87 | self.panic('Failed to parse source') |
| | 88 | unless $match; |
| | 89 | |
| | 90 | $match; |
| | 91 | } |
| | 92 | }}} |