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

时间:2016-03-15 15:26:14

标签: parsing pegkit

我正在为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]

任何有关如何实现这一目标的想法都将受到赞赏。

1 个答案:

答案 0 :(得分:2)

PEGKit的创作者。

我理解你的最终策略(收集/组合线作为单个字符串对象),并同意它是有道理的,但是,我不同意你提出的实现这一目标的策略(改变标记化以试图结合本质上是多重的将令牌分成单个令牌。

将线条组合成方便的字符串对象是有意义的,但改变标记化以实现这一点,当有问题的线条没有明显的“包围”时,IMO(至少不使用递归下降解析工具包PEGKit)是没有意义的引号或括号等字符。

可以passageTitle行以::作为单行Comment令牌处理,但我可能不会因为我收集它们是语义上的评论。

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

我们有两种不同的处理方式:

  1. passageTitle
  2. contentLine
  3. 在你的语法中,删除这一行,这样我们就不会将passageTitle视为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
    

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