4

我想知道是否有办法让 PKParser 在遇到语法错误之前返回到程序集的解析程度。

参考: http: //parsekit.com/

我使用的语法基本上描述了前缀符号表达式语言。

例如:

给定您的标准前缀符号表达式语法和字符串“(+ a - bc))”,我想检索匹配的 [(,+,a],因此我可以让用户了解在哪里寻找修复他们的错误,但 completeMatchFor 和 bestMatchFor 不返回任何我可以用来查找此信息的内容。

理想情况下,我想说一个'('是预期的,但对于像我正在使用的那样简单的语法来说,这不是必需的。

从作为用户手册提到的书中,似乎我需要为此创建一个自定义解析器,但我希望我可能只是错过了框架中的某些内容。

想法?

4

2 回答 2

3

ParseKit的开发者在这里。

ParseKit 中有两个功能可用于帮助提供用户可读的提示,描述输入中遇到的解析错误。

  1. -[PKParser bestMatchFor:]
  2. PKTrack班级_

听起来您知道该-bestMatchFor:方法,即使在这种情况下它没有按照您的预期进行。

我认为这PKTrack门课在这里会更有帮助。正如Metsker的书中所描述的,除了它的子解析器是必需的,并且当它的所有子解析器都不匹配时,会抛出一个错误(带有有用的错误消息)。PKTrackPKSequence

因此,这是您的示例输入的语法:

@start         = '(' expr ')' | expr;
expr           = ('+' | '-') term term;
term           = '(' expr ')' | Word;

任何连续列出的作品都是一个序列——但也可以是一个轨道。

将这些 Sequences 更改为 Tracks 的好处是,NSException如果输入不匹配,则会抛出人类可读的解析错误消息。缺点是您现在必须将工厂生成的解析器的所有用法包装在一个 try/catch 块中以捕获这些 Track 异常。

当前(或至少在此之前)的问题是PKParserFactory从未使用 Tracks 生成解析器。相反,它总是使用序列。

所以我刚刚在谷歌代码的主干头中添加了一个新选项(你需要更新)。

#define USE_TRACK 0

PKParserFactory.m

0默认情况下。如果将此定义更改为1,将使用轨道而不是序列。所以给定上面的语法和这样的无效输入:

(+ a - b c))

这个客户代码:

NSString *g = // fetch grammar above
PKParser *p = [[PKParserFactory factory] parserFromGrammar:g assembler:self];
NSString *s = @"(+ a - b c))";

@try {
    PKAssembly *res = [p parse:s];
    NSLog(@"res %@", res);
}
@catch (NSException *exception) {
    NSLog(@"Parse Error:%@", exception);
}

你会得到一个很好的人类可读错误:

Parse Error:

After : ( + a
Expected : Alternation (term)
Found : -

希望有帮助。

于 2012-04-04T05:51:21.510 回答
3

我也在纠结这个问题。为了-bestMatchFor:有助于识别错误情况,在 的公共接口中应该有方法PKAssembly指示是否有更多的标记/字符要解析。-completeMatchFor:能够确定错误状态,因为它可以访问私有-hasMore方法。也许PKAssembly-hasMore方法应该是公开的。

我看了看,PKTrack但由于我想以编程方式处理错误,所以它对我没有用。

我的结论是我要么编写自己的自定义 Track 解析器,要么更改框架并公开-hasMore. 还有其他处理错误的方法吗?

在找到更好的错误检测方法之前,我已将以下内容添加到包含自定义解析器实现的文件中:

@interface PKAssembly ()
- (BOOL)hasMore;
- (id)peek;
@end

@implementation PMParser
...
@end

在我的解析方法中:

PKAssembly*     a     = [PKTokenAssembly assemblyWithString:s];
PKAssembly*     best  = [self bestMatchFor:a];
PMParseNode*    node  = nil;
BOOL            error = NO;
NSUInteger      errorOffset = 0;

if (best == nil)  // Anything recognized?
{   
    error = YES;
}
else
{
    if ([best hasMore])  // Partial recognition?
    {
        PKToken*    t = [best peek];

        error       = YES;
        errorOffset = t.offset;
    }

    node = [best pop];
}

如果发生错误,errorOffset将包含无法识别的令牌的位置。

于 2012-07-07T13:23:41.813 回答