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.


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.

    $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;
		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.



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;