From 4199ad8758797606d465bc7d9cef1942adbd3e2d Mon Sep 17 00:00:00 2001 From: Dan Hirsch <thequux@upstandinghackers.com> Date: Wed, 4 Dec 2013 04:44:07 +0100 Subject: [PATCH] All tests except attr_bool pass --- src/bindings/perl/hammer.i | 38 +++++-- src/bindings/perl/t/hammer.t | 200 ++++++++++++++++++++++++++++++++++- 2 files changed, 229 insertions(+), 9 deletions(-) diff --git a/src/bindings/perl/hammer.i b/src/bindings/perl/hammer.i index 4903657..68f73dd 100644 --- a/src/bindings/perl/hammer.i +++ b/src/bindings/perl/hammer.i @@ -38,7 +38,7 @@ AV* av = (AV*) SvRV($input); size_t amax = av_top_index(av) + 1; // I want the length, not the top index... // TODO: is this array copied? - $1 = malloc(amax * sizeof(*$1)); + $1 = malloc((amax+1) * sizeof(*$1)); $1[amax] = NULL; for (int i = 0; i < amax; i++) { int res = SWIG_ConvertPtr(*av_fetch(av, i, 0), &($1[i]), SWIGTYPE_p_HParser_, 0|0); @@ -72,12 +72,33 @@ %rename("token") h_token; %rename("%(regex:/^h_(.*)/\\1/)s", regextarget=1) "^h_u?int(64|32|16|8)"; -%rename("end_p") h_end_p; -%rename("left") h_left; -%rename("middle") h_middle; -%rename("right") h_right; -%rename("int_range") h_int_range; -%rename("whitespace") h_whitespace; + +%define %combinator %rename("%(regex:/^h_(.*)$/\\1/)s") %enddef + +%combinator h_end_p; +%combinator h_left; +%combinator h_middle; +%combinator h_right; +%combinator h_int_range; +%combinator h_whitespace; +%combinator h_nothing_p; + +%combinator h_butnot; +%combinator h_difference; +%combinator h_xor; +%combinator h_many; +%combinator h_many1; +%combinator h_sepBy; +%combinator h_sepBy1; +%combinator h_repeat_n; +%combinator h_ignore; +%combinator h_optional; +%combinator h_epsilon_p; +%combinator h_and; +%combinator h_not; +%combinator h_indirect; +%combinator h_bind_indirect; + %include "../swig/hammer.i"; @@ -86,7 +107,7 @@ // All values that this function returns have a refcount of exactly 1. SV *ret; if (token == NULL) { - return newSV(0); // TODO: croak. + return newSV(0); // Same as TT_NONE } switch (token->token_type) { case TT_NONE: @@ -230,4 +251,5 @@ return h__not_in(join('',@_)); } + %} diff --git a/src/bindings/perl/t/hammer.t b/src/bindings/perl/t/hammer.t index 424aa9c..2c7f744 100644 --- a/src/bindings/perl/t/hammer.t +++ b/src/bindings/perl/t/hammer.t @@ -1,9 +1,17 @@ # -*- cperl -*- use warnings; use strict; -use Test::More tests => 21; +use Data::Dumper; +use Test::More tests => 41; use hammer; +# differences from C version: + +# - in takes any number of arguments, which are concatenated. This +# makes ch_range irrelevant. +# +# - foo + sub check_parse_eq { my ($parser, $input, $expected) = @_; @@ -15,6 +23,7 @@ sub check_parse_eq { diag($@); ok($@ eq ""); } else { + #diag(Dumper($actual)); is_deeply($actual, $expected); } } @@ -189,3 +198,192 @@ subtest "end_p" => sub { check_parse_eq($parser, 'a', ['a']); check_parse_failed($parser, 'aa'); }; + +subtest "nothing_p" => sub { + my $parser = hammer::nothing_p(); + check_parse_failed($parser, ""); + check_parse_failed($parser, "foo"); +}; + +subtest "sequence" => sub { + my $parser = hammer::sequence(hammer::ch('a'), hammer::ch('b')); + check_parse_eq($parser, "ab", ['a','b']); + check_parse_failed($parser, 'a'); + check_parse_failed($parser, 'b'); +}; + +subtest "sequence-whitespace" => sub { + my $parser = hammer::sequence(hammer::ch('a'), + hammer::whitespace(hammer::ch('b'))); + check_parse_eq($parser, "ab", ['a', 'b']); + check_parse_eq($parser, "a b", ['a', 'b']); + check_parse_eq($parser, "a b", ['a', 'b']); + check_parse_failed($parser, "a c"); +}; + +subtest "choice" => sub { # test 25 + my $parser = hammer::choice(hammer::ch('a'), + hammer::ch('b')); + check_parse_eq($parser, 'a', 'a'); + check_parse_eq($parser, 'b', 'b'); + check_parse_failed($parser, 'c'); +}; + +subtest "butnot" => sub { + my $parser = hammer::butnot(hammer::ch('a'), hammer::token('ab')); + check_parse_eq($parser, 'a', 'a'); + check_parse_eq($parser, 'aa', 'a'); + check_parse_failed($parser, 'ab'); +}; + +subtest "butnot-range" => sub { + my $parser = hammer::butnot(hammer::ch_range('0', '9'), hammer::ch('6')); + check_parse_eq($parser, '4', '4'); + check_parse_failed($parser, '6'); +}; + +subtest "difference" => sub { + my $parser = hammer::difference(hammer::token('ab'), + hammer::ch('a')); + check_parse_eq($parser, 'ab', 'ab'); + check_parse_failed($parser, 'a'); +}; + +subtest "xor" => sub { + my $parser = hammer::xor(hammer::in('0'..'6'), + hammer::in('5'..'9')); + check_parse_eq($parser, '0', '0'); + check_parse_eq($parser, '9', '9'); + check_parse_failed($parser, '5'); + check_parse_failed($parser, 'a'); +}; + +subtest "many" => sub { # test 30 + my $parser = hammer::many(hammer::in('ab')); + check_parse_eq($parser, '', []); + check_parse_eq($parser, 'a', ['a']); + check_parse_eq($parser, 'b', ['b']); + check_parse_eq($parser, 'aabbaba', [qw/a a b b a b a/]); +}; + +subtest "many1" => sub { + my $parser = hammer::many1(hammer::in('ab')); + check_parse_eq($parser, 'a', ['a']); + check_parse_eq($parser, 'b', ['b']); + check_parse_eq($parser, 'aabbaba', [qw/a a b b a b a/]); + check_parse_failed($parser, ''); + check_parse_failed($parser, 'daabbabadef'); +}; +subtest "repeat_n" => sub { + my $parser = hammer::repeat_n(hammer::in('ab'), 2); + check_parse_eq($parser, 'abdef', ['a','b']); + check_parse_failed($parser, 'adef'); +}; + +subtest "optional" => sub { + my $parser = hammer::sequence(hammer::ch('a'), + hammer::optional(hammer::in('bc')), + hammer::ch('d')); + check_parse_eq($parser, 'abd', [qw/a b d/]); + check_parse_eq($parser, 'abd', [qw/a b d/]); + check_parse_eq($parser, 'ad', ['a',undef,'d']); + check_parse_failed($parser, 'aed'); + check_parse_failed($parser, 'ab'); + check_parse_failed($parser, 'ac'); +}; + +subtest "ignore" => sub { + my $parser = hammer::sequence(hammer::ch('a'), + hammer::ignore(hammer::ch('b')), + hammer::ch('c')); + check_parse_eq($parser, "abc", ['a','c']); + check_parse_failed($parser, 'ac'); +}; + +subtest "sepBy" => sub { # Test 35 + my $parser = hammer::sepBy(hammer::in('1'..'3'), + hammer::ch(',')); + check_parse_eq($parser, '1,2,3', ['1','2','3']); + check_parse_eq($parser, '1,3,2', ['1','3','2']); + check_parse_eq($parser, '1,3', ['1','3']); + check_parse_eq($parser, '3', ['3']); + check_parse_eq($parser, '', []); +}; + +subtest "sepBy1" => sub { + my $parser = hammer::sepBy1(hammer::in("123"), + hammer::ch(',')); + check_parse_eq($parser, '1,2,3', ['1','2','3']); + check_parse_eq($parser, '1,3,2', ['1','3','2']); + check_parse_eq($parser, '1,3', ['1','3']); + check_parse_eq($parser, '3', ['3']); + check_parse_failed($parser, ''); +}; + +subtest "epsilon" => sub { + check_parse_eq(hammer::sequence(hammer::ch('a'), + hammer::epsilon_p(), + hammer::ch('b')), + 'ab', ['a','b']); + check_parse_eq(hammer::sequence(hammer::epsilon_p(), + hammer::ch('a')), + 'a', ['a']); + check_parse_eq(hammer::sequence(hammer::ch('a'), + hammer::epsilon_p()), + 'a', ['a']); +}; + + +TODO: { + local $TODO = "not implemented"; + subtest "attr_bool" => sub { + fail; + } +}; + +subtest "and" => sub { + check_parse_eq(hammer::sequence(hammer::and(hammer::ch('0')), + hammer::ch('0')), + '0', ['0']); + check_parse_failed(hammer::sequence(hammer::and(hammer::ch('0')), + hammer::ch('1')), + '0'); + my $parser = hammer::sequence(hammer::ch('1'), + hammer::and(hammer::ch('2'))); + check_parse_eq($parser, '12', ['1']); + check_parse_failed($parser, '1'); + check_parse_failed($parser, '13'); +}; + +subtest "not" => sub { # test 40 + # This is not how you'd *actually* write the parser for this + # language; in case of Packrat, it's better to swap the order of the + # arguments, and for other backends, the problem doesn't appear at + # all. + my $parser = hammer::sequence(hammer::ch('a'), + hammer::choice(hammer::ch('+'), + hammer::token('++')), + hammer::ch('b')); + check_parse_eq($parser, 'a+b', ['a','+','b']); + check_parse_failed($parser, 'a++b'); # ordered choice + + $parser = hammer::sequence(hammer::ch('a'), + hammer::choice(hammer::sequence(hammer::ch('+'), + hammer::not(hammer::ch('+'))), + hammer::token('++')), + hammer::ch('b')); + check_parse_eq($parser, 'a+b', ['a',['+'],'b']); + check_parse_eq($parser, 'a++b', ['a', '++', 'b']); +}; + +subtest "rightrec" => sub { + my $parser = hammer::indirect(); + hammer::bind_indirect($parser, + hammer::choice(hammer::sequence(hammer::ch('a'), + $parser), + hammer::epsilon_p)); + check_parse_eq($parser, 'a', ['a']); + check_parse_eq($parser, 'aa', ['a', ['a']]); + check_parse_eq($parser, 'aaa', ['a', ['a', ['a']]]); +}; + -- GitLab