内容

名称

TAP::Parser::Grammar - Test Anything Protocol 的语法。

版本

版本 3.44

概要

use TAP::Parser::Grammar;
my $grammar = $self->make_grammar({
  iterator => $tap_parser_iterator,
  parser   => $tap_parser,
  version  => 12,
});

my $result = $grammar->tokenize;

描述

TAP::Parser::Grammar 将来自 TAP::Parser::Iterator 的行进行标记化,并构建 TAP::Parser::Result 子类来表示标记。

不要尝试直接使用此类。它没有意义。它主要存在是为了确保当 TAP 在未来某个时间扩展时,我们能够拥有可插拔的语法(此外,这些东西真的会使解析器混乱)。

方法

类方法

new

my $grammar = TAP::Parser::Grammar->new({
    iterator => $iterator,
    parser   => $parser,
    version  => $version,
});

返回 TAP::Parser 语法对象,它将解析来自指定迭代器的 TAP 流。iteratorparser 是必需的参数。如果未设置 version,则默认为 12(有关更多详细信息,请参见 "set_version")。

实例方法

set_version

$grammar->set_version(13);

告诉语法要支持哪个 TAP 语法版本。最低支持版本为 12。虽然“TAP 版本”不是有效的版本 12 语法,但它被接受,以便可以解析更高的版本号。

tokenize

my $token = $grammar->tokenize;

此方法将返回一个 TAP::Parser::Result 对象,表示当前行的 TAP。

token_types

my @types = $grammar->token_types;

返回此语法可以解析的不同类型的标记。

syntax_for

my $syntax = $grammar->syntax_for($token_type);

返回一个预编译的正则表达式,它将匹配与标记类型相对应的 TAP 块。例如(不是说你应该真正关注这一点,$grammar->syntax_for('comment') 将返回 qr/^#(.*)/

handler_for

my $handler = $grammar->handler_for($token_type);

返回一个代码引用,当传递给它一个适当的 TAP 行时,它将返回与该行相对应的词法分析标记。因此,基本的 TAP 解析循环看起来类似于以下内容

my @tokens;
my $grammar = TAP::Grammar->new;
LINE: while ( defined( my $line = $parser->_next_chunk_of_tap ) ) {
    for my $type ( $grammar->token_types ) {
        my $syntax  = $grammar->syntax_for($type);
        if ( $line =~ $syntax ) {
            my $handler = $grammar->handler_for($type);
            push @tokens => $grammar->$handler($line);
            next LINE;
        }
    }
    push @tokens => $grammar->_make_unknown_token($line);
}

TAP 语法

注意:此语法略微过时。关于它仍然有一些讨论,当我们更好地定义事物时,将提供一个新的语法。

TAP::Parser 不使用正式语法,因为 TAP 本质上是一个基于流的协议。事实上,拥有一个无限流是完全合法的。由于我们不将正则表达式应用于流,因此我们在这里不使用正式语法。相反,我们按行解析 TAP。

为了向前兼容,任何不匹配以下语法的结果目前被称为 TAP::Parser::Result::Unknown。它不是解析错误。

正式语法将类似于以下内容

(*
    For the time being, I'm cheating on the EBNF by allowing
    certain terms to be defined by POSIX character classes by
    using the following syntax:

      digit ::= [:digit:]

    As far as I am aware, that's not valid EBNF.  Sue me.  I
    didn't know how to write "char" otherwise (Unicode issues).
    Suggestions welcome.
*)

tap            ::= version? { comment | unknown } leading_plan lines
                   |
                   lines trailing_plan {comment}

version        ::= 'TAP version ' positiveInteger {positiveInteger} "\n"

leading_plan   ::= plan skip_directive? "\n"

trailing_plan  ::= plan "\n"

plan           ::= '1..' nonNegativeInteger

lines          ::= line {line}

line           ::= (comment | test | unknown | bailout ) "\n"

test           ::= status positiveInteger? description? directive?

status         ::= 'not '? 'ok '

description    ::= (character - (digit | '#')) {character - '#'}

directive      ::= todo_directive | skip_directive

todo_directive ::= hash_mark 'TODO' ' ' {character}

skip_directive ::= hash_mark 'SKIP' ' ' {character}

comment        ::= hash_mark {character}

hash_mark      ::= '#' {' '}

bailout        ::= 'Bail out!' {character}

unknown        ::= { (character - "\n") }

(* POSIX character classes and other terminals *)

digit              ::= [:digit:]
character          ::= ([:print:] - "\n")
positiveInteger    ::= ( digit - '0' ) {digit}
nonNegativeInteger ::= digit {digit}

子类化

请参阅 "子类化" 在 TAP::Parser 中 以获取子类化概述。

如果你真的想子类化 TAP::Parser 的语法,最好的方法是阅读代码。这里没有简单的方法来总结它。

另请参阅

TAP::ObjectTAP::ParserTAP::Parser::IteratorTAP::Parser::Result