PEGKit 在堆栈上组合匹配的符号

PEGKit combine matched symbols on stack

我正在为 PEGKit 编写语法来解析 Twine 导出的 Twee 文件。这是我第一次使用 PEGKit,我正在努力了解它的工作原理。

我有正在解析的 twee 源文件

:: Passage One
P1 Line One
P1 Line Two

:: Passage Two
P2 Line One
P2 Line Two

目前我已经弄清楚如何使用以下语法解析上述内容

@before {
    PKTokenizer *t = self.tokenizer;
    [t.symbolState add:@"::"];
    [t.commentState addSingleLineStartMarker:@"::"];

    // New lines as symbols
    [t.whitespaceState setWhitespaceChars:NO from:'\n' to:'\n'];
    [t.whitespaceState setWhitespaceChars:NO from:'\r' to:'\r'];
    [t setTokenizerState:t.symbolState from:'\n' to:'\n'];
    [t setTokenizerState:t.symbolState from:'\r' to:'\r'];
}

start                   = passage+;
passage                 = passageTitle contentLine*;
passageTitle            = passageStart Word+ eol+;
contentLine             = singleLine eol+;
singleLine              = Word+;
passageStart            = '::'!;
eol                     = '\n'! | '\r'!;

我得到的结果是

[Passage, One, P1, Line, One, P1, Line, Two, Passage, Two, P2, Line, One, P2, Line, Two]::/Passage/One/
/P1/Line/One/
/P1/Line/Two/
/
/::/Passage/Two/
/P2/Line/One/
/P2/Line/Two/
^

理想情况下,我希望解析器将与 passageTitle 匹配的单词组合成一个字符串,类似于内置 PEGKit QuotedString 语法的工作方式。我还希望将与 contentLine 匹配的词也组合起来。

所以,最终,我会把它放在堆栈上

[Passage One, P1 Line One, P1 Line Two, Passage Two, P2 Line One, P2 Line Two]

任何关于如何实现这一点的想法都将不胜感激。

这里是 PEGKit 的创建者。

我理解你的最终策略(将 collect/combine 行作为单个字符串 objects),并同意这是有道理的,但是,我不同意你提出的实现该策略的策略(改变标记化尝试将本质上是多个独立标记的内容组合成单个标记)。

将行组合成方便的字符串 objects 是有道理的,但是改变标记化来实现这一点在 IMO 中没有意义(至少不是使用递归下降解析工具包 PEGKit)当有问题的行不没有明显的 'bracketing' 个字符,例如引号或括号。

可以将以::开头的passageTitle行视为single-line Comment标记,但我可能不会'自从我收集到它们在语义上 不是 评论。

因此,您应该以更自然的方式为 PEGKit 合并多个标记,而不是通过标记器合并多个标记:在解析器委托回调中

我们这里有两种不同的情况要处理:

  1. passageTitle
  2. contentLine

在您的语法中,删除此行,这样我们就不会将 passageTitles 视为 Comment 标记(无论如何您都没有完全正确配置,但没关系):

[t.commentState addSingleLineStartMarker:@"::"];

并且在你的语法中,从你的 passageStart 规则中删除 ! 这样那些标记就不会被丢弃:

passageStart            = '::';

语法就这些了。现在在您的 Parser Delegate 回调中,为标题和内容行实现两个必要的回调方法。在每个回调中,从 PKAssembly 的堆栈中取出所有必要的标记,并将它们合并为一个字符串(反向)。

@interface TweeDelegate : NSObject
@end

@implementation TweeDelegate

- (void)parser:(PKParser *)p didMatchPassageTitle:(PKAssembly *)a {
    NSArray *toks = [a objectsAbove:[PKToken tokenWithTokenType:PKTokenTypeSymbol stringValue:@"::" doubleValue:0.0]];
    [a pop]; // discard `::`

    NSMutableString *buf = [NSMutableString string];

    for (PKToken *tok in [toks reverseObjectEnumerator]) {
        [buf appendFormat:@"%@ ", tok.stringValue];
    }

    CFStringTrimWhitespace((CFMutableStringRef)buf);

    NSLog(@"Title: %@", buf); // Passage One
}

- (void)parser:(PKParser *)p didMatchContentLine:(PKAssembly *)a {
    NSArray *toks = [a objectsAbove:nil];

    NSMutableString *buf = [NSMutableString string];

    for (PKToken *tok in [toks reverseObjectEnumerator]) {
        [buf appendFormat:@"%@ ", tok.stringValue];
    }

    CFStringTrimWhitespace((CFMutableStringRef)buf);

    NSLog(@"Content: %@", buf); // P1 Line One
}

@end

我收到以下输出:

Title: Passage One
Content: P1 Line One
Content: P1 Line Two
Title: Passage Two
Content: P2 Line One
Content: P2 Line Two

至于创建这些字符串后如何处理它们,我会留给你:)。希望对您有所帮助。