use Test; use Parse::Rule::Strategies::CPS::Text; plan 82; skip_rest "nested class not yet implemented"; exit; my $c = Parse::Rule::Strategies::CPS::Text.new; sub do_match ($text, $parser) { my $rule = $parser.compile; my ($match, $backtrack) = $rule.run( input => $c.Pos.new(text => $text, pos => 0), match => Parse::Rule::Core::Match.new, ); $match; } my ($pat, $desc); sub matches ($text) { my $m = do_match($text, $pat); my $end; my $matches = defined $m and do { $end = $m.to; $end.pos == $text.chars }; ok($matches, "'$text' matches $desc"); return $m; } sub matches_not ($text) { my $m = do_match($text, $pat); my $end; my $matches = defined $m and do { $end = $m.to; $end.pos == $text.chars }; ok(!$matches, "'$text' doesn't match $desc"); } sub is_range ($match, $start, $end) { my ($ms, $me) = ($match.from, $match.to); my ($msp, $mep) = ($ms.pos, $me.pos); ok($msp == $start && $mep == $end, "capture ($msp, $mep) is ($start, $end)"); } ($desc, $pat) = ('//', $c.empty()); matches ""; matches_not "x"; ($desc, $pat) = ('/hello/', $c.literal("hello")); matches "hello"; matches_not "goodbye"; matches_not "hell"; matches_not "helloes"; ($desc, $pat) = ('/./', $c.any_char()); matches "x"; matches_not ""; matches_not "xx"; ($desc, $pat) = ('/foo bar/', $c.concat($c.literal("foo"), $c.literal("bar"))); matches "foobar"; matches_not "foo"; matches_not "bar"; matches_not ""; ($desc, $pat) = ('/foo|bar/', $c.alternate($c.literal("foo"), $c.literal("bar"))); matches "foo"; matches "bar"; matches_not "foobar"; matches_not ""; ($desc, $pat) = ('/[foo|bar] [baz|quux]/', $c.concat( $c.alternate($c.literal("foo"), $c.literal("bar")), $c.alternate($c.literal("baz"), $c.literal("quux")))); matches "foobaz"; matches "fooquux"; matches "barbaz"; matches "barquux"; matches_not "foo"; matches_not ""; matches_not "quux"; matches_not "foobarbaz"; ($desc, $pat) = ('/x*/', $c.quantify($c.literal("x"))); matches ""; matches "x"; matches "xxxxxx"; matches_not "xxxxxxy"; matches_not "yxxxxxx"; ($desc, $pat) = ('/x+/', $c.quantify($c.literal("x"), 1)); matches "x"; matches "xxxxxx"; matches_not ""; matches_not "y"; ($desc, $pat) = ('/[x|y]*/', $c.quantify($c.alternate($c.literal("x"), $c.literal("y")))); matches ""; matches "x"; matches "y"; matches_not "z"; matches "xxyxyyyxyyyxyx"; matches_not "xxyyyxyxz"; my $match; ($desc, $pat) = ('/(x)/', $c.capture($c.literal("x"), :num(0))); $match = matches "x"; is_range($match.capture_num[0], 0, 1); matches_not ""; matches_not "xx"; ($desc, $pat) = ('/xy(zz)/', $c.concat($c.literal("xy"), $c.capture($c.literal("zz"), :num(0)))); $match = matches "xyzz"; is_range($match.capture_num[0], 2, 4); ($desc, $pat) = ('/(xy) $zs := (zz)/', $c.concat($c.capture($c.literal("xy"), :num(0)), $c.capture($c.literal("zz"), :num(1), :name))); $match = matches "xyzz"; is_range($match.capture_num[0], 0, 2); is_range($match.capture_num[1], 2, 4); is_range($match.capture_name, 2, 4); ($desc, $pat) = ('/(x|y)*/', $c.quantify($c.capture($c.alternate($c.literal("x"), $c.literal("y")), :num(0)))); $match = matches "xyyx"; is_range($match.capture_num[0][0], 0, 1); is_range($match.capture_num[0][1], 1, 2); is_range($match.capture_num[0][2], 2, 3); is_range($match.capture_num[0][3], 3, 4); ($desc, $pat) = ('/[ (foo|bar) $bq:=(baz|quux) ]+/', $c.quantify( :low(1), $c.concat($c.capture($c.alternate($c.literal("foo"), $c.literal("bar")), :num(0)), $c.capture($c.alternate($c.literal("baz"), $c.literal("quux")), :num(1), :name)))); matches_not ""; matches_not "foobazbar"; $match = matches "foobazbarquuxbarbaz"; is_range($match.capture_num[0][0], 0,3); is_range($match.capture_num[0][1], 6,9); is_range($match.capture_num[0][2], 13,16); is_range($match.capture_num[1][0], 3,6); is_range($match.capture_num[1][1], 9,13); is_range($match.capture_num[1][2], 16,19); is_range($match.capture_name[0], 3,6); is_range($match.capture_name[1], 9,13); is_range($match.capture_name[2], 16,19); ($desc, $pat) = ('/[ [ x ]* ]*/', $c.quantify($c.quantify($c.literal("x")))); matches "x"; matches_not "xy"; ($desc, $pat) = ('/"(.*)".*/', $c.concat( $c.concat( $c.concat( $c.literal('"'), $c.capture(:num(0), $c.quantify($c.any_char()))), $c.literal('"')), $c.quantify($c.any_char()))); $match = matches q{"foobar"baz"quux"ziph}; is_range($match.capture_num[0], 1, 16); ($desc, $pat) = ('/"(.*?)".*/', $c.concat( $c.concat( $c.concat( $c.literal('"'), $c.capture(:num(0), $c.quantify(:minimal, $c.any_char()))), $c.literal('"')), $c.quantify($c.any_char()))); $match = matches q{"foobar"baz"quux"ziph}; is_range($match.capture_num[0], 1, 7); ($desc, $pat) = ('/ x*:x /', $c.concat( $c.mark(:name(':'), $c.concat($c.quantify($c.literal("x")), $c.commit(':'))), $c.literal("x"))); matches_not "xxx"; ($desc, $pat) = ('/ yx?y /', $c.concat( $c.concat($c.literal('y'), $c.optional($c.literal('x'))), $c.literal('y'))); matches 'yxy'; matches 'yy'; matches_not 'y'; { my sub balanced () { # XXX this is slightly wrong, but it doesn't make a difference # with the CPS runtime. This should be compiled in all calls # but the one in $pat. $c.alternate( $c.concat( $c.concat( $c.literal('('), $c.subrule(-> $m { balanced() })), $c.literal(')')), $c.empty()); } ($desc, $pat) = ('rule balanced() { \( \) | } //', balanced()); matches(''); matches('()'); matches('((()))'); matches_not('((())'); matches_not('(()))'); } # vim: ft=perl6 :