This is my revised TAP grammar. I'm using this to drive development of the TAPx::Parser. If you're not familiar with reading this style of grammar, here are a few comments:

I'm using POSIX character classes to represent digits and printable characters. If you want to manually encode all of the Unicode characters for [:print:], be my guest :)

The following means "all printable characters except the newline".

([:print:] - "\n")

The following means, a digit followed by zero or more digits.

digit {digit}

A question mark after an atom means it's optional.

I'm not particularly gifted with grammars, so corrections welcome.

The corrected TAP grammar:

digit ::= [:digit:] character ::= ([:print:] - "\n") positiveInteger ::= ( digit - '0' ) {digit} nonNegativeInteger ::= digit {digit} tap ::= plan tests | tests plan {comment} plan ::= '1..' nonNegativeInteger "\n" lines ::= line {lines} line ::= (comment | test) "\n" tests ::= test {test} test ::= status positiveInteger? description? directive? status ::= 'not '? 'ok ' description ::= (character - (digit '#')) {character - '#'} directive ::= '#' ( 'TODO' | 'SKIP' ) ' ' {character} comment ::= '#' {character}

For purposes of forward compatability, all lines which do not match the grammar are labeled as TAPx::Parser::Result::Unknown and are not considered parse errors.

Cheers,
Ovid

New address of my CGI Course.

Replies are listed 'Best First'.
Re: Revised TAP Grammar
by ikegami (Patriarch) on Sep 14, 2006 at 11:40 UTC

    For purposes of forward compatability, all lines which do not match the grammar are labeled as TAPx::Parser::Result::Unknown and are not considered parse errors.

    In other words,

    test ::= status positiveInteger? description? directive? | unknown unknown ::= {character}

    Update: lines is never referenced.

    Update: Thinking about the unreferenced lines, I thought of a possible simplification: Requiring one and only one (no more, no less) "plan" line should not be enforced using the grammar. In other words, treat the count of plan lines as a semantic check, not a syntactic check. This leads to a slightly simpler grammar that will be easier to maintain, especially as things are added.

    tap ::= lines lines ::= line {lines} line ::= (comment | test | plan | unknown ) "\n" plan ::= '1..' nonNegativeInteger "\n" test ::= status positiveInteger? description? directive? status ::= 'not '? 'ok ' description ::= (character - (digit '#')) {character - '#'} directive ::= '#' ( 'TODO' | 'SKIP' ) ' ' {character} comment ::= '#' {character} unknown ::= {character} digit ::= [:digit:] character ::= ([:print:] - "\n") positiveInteger ::= ( digit - '0' ) {digit} nonNegativeInteger ::= digit {digit}

    This doesn't mean it can't be checked at parse-time. Make sure a plan wasn't already specified when one is encountered. Make sure a plan was specified when parsing is done.

      Hmm, you do make some good points and I appreciate it. However, I don't know that I'd want to pull the plan out of the grammar as one could use that to easily write some code to parse a bunch of regular TAP strings to validate their TAP producer. At that point, the only required semantic check would be to ensure that test numbers correspond to the correct test line. That would make semantic analysis much simpler since the grammar, as a declarative tool, is more likely to be correct.

      I like the rest of what you've done, though. Still, maybe I'm just smoking crack :)

      Cheers,
      Ovid

      New address of my CGI Course.

      I'd hate to see plans anywhere but at the beginning or end.
        You're right. That was enforced, and not anymore.
Re: Revised TAP Grammar
by GrandFather (Saint) on Sep 14, 2006 at 09:08 UTC

    I don't know anything about TAP, but:

    plan ::= '1..' nonNegativeInteger "\n"

    allows:

    1..0

    doesn't it? Is that intended, or is what you are after more like 1..n where n is > 1?

    Update: The following two lines constrain plan as described above (with a little guessing at syntax):

    pluralInteger ::= (digit - ('0', '1')) | digit digit+ plan ::= '1..' pluralInteger "\n"

    DWIM is Perl's answer to Gödel

      Yes, that is specifically allowed. '1..0' is the (undocumented, I think) plan syntax for 'skip all of these tests'.

      Cheers,
      Ovid

      New address of my CGI Course.