in reply to Parse::RecDescent - I'm just not getting it

That's fine except for the incorrect return values

my $grammar = q{ list : '(' item(s) ')' { $item[2] } item : list <commit> { $item[1] } | word word : /[^()]*/ };

I don't like using q{...} for grammars. Here-docs provide the same functionality, but have fewer issues.

my $grammar = <<'__END_OF_GRAMMAR__'; list : '(' item(s) ')' { $item[2] } item : list <commit> { $item[1] } | word word : /[^()]*/ __END_OF_GRAMMAR__

I strongly recommend the use of use strict and use warnings in your grammar. The ones in your main program won't affect the code in your grammar.

my $grammar = <<'__END_OF_GRAMMAR__'; { use strict; use warnings; } list : '(' item(s) ')' { $item[2] } item : list <commit> { $item[1] } | word word : /[^()]*/ __END_OF_GRAMMAR__

Finally, I'm not sure if trailing <commit> directives are of any use. I'll have to look into that.

Update: If the outside parens are optional, you get:

list : item(s) item : '(' <commit> list ')' { $item[3] } | word word : /[^()]*/

And if you don't want empty strings:

list : item(s?) item : '(' <commit> list ')' { $item[3] } | word word : /[^()]+/