| Version 1 (modified by Austin_Hastings, 3 years ago) |
|---|
NQP-rx Tricks
Here are some tricks for parsing with NQP-rx:
Global Variables
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.
HLL::Grammar::%!MARKHASH
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.
Q:PIR {
$P0 = null
set_hll_global ['HLL';'Grammar'], '%!MARKHASH', $P0
};
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:
method code_test(%code_matchers) {
my $slam := Slam::Compiler.new;
my $past;
for %code_matchers -> $pair {
my $code := $pair.key;
say("Code: $code");
Q:PIR {
$P0 = null
set_hll_global ['HLL';'Grammar'], '%!MARKHASH', $P0
};
$past := $slam.compile: $code, :rule<EXPR>, :target<past>; #, :parsetrace;
#_dumper($past);
my $matcher := $pair.value;
assert_match($past, $matcher,
'Failed to parse {' ~ $code ~ '} as expected');
}
}
method test_postcircumfix_array_index() {
my %code_matchers;
%code_matchers<x[0]> := var( :scope<keyed>, var( :name<x> ), val( :value<0> ));
%code_matchers<x[ 1]> := var( :scope<keyed>, var( :name<x> ), val( :value<1> ));
%code_matchers<x[ 2 ]> := var( :scope<keyed>, var( :name<x> ), val( :value<2> ));
%code_matchers<x[ y ]> := var( :scope<keyed>, var( :name<x> ), var( :name<y> ));
self.code_test: %code_matchers;
}
The var(...) functions are syntactic sugar that generate a special Matcher for validating PCT trees.
HLL::Compiler
Debugging
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:
class Slam::Compiler;
method parse($source, *%adverbs) {
unless %adverbs<actions> {
%adverbs<actions> := %adverbs<target> eq 'parse'
?? pir::null__P()
!! self.parseactions;
}
my %options_map := hash(
actions => 'actions',
parsetrace => 'rxtrace',
rule => 'rule',
);
my %parse_flags;
for %options_map -> $map {
%parse_flags{$map.value} := %adverbs{$map.key}
if %adverbs.contains: $map.key;
}
my $match := self.parsegrammar.parse($source, :p(0), |%parse_flags);
self.panic('Failed to parse source')
unless $match;
$match;
}
