5

对于 iOS 应用程序,我想解析一个 HTML 文件,其中可能包含用于替换的 UNIX 样式变量。例如,HTML 可能如下所示:

<html>
  <head></head>
  <body>
    <h1>${title}</h1>
    <p>${paragraph1}</p>
    <img src="${image}" />
  </body>
</html>

我正在尝试创建一个简单的 ParseKit 语法,它将为我提供两个回调:一个用于传递 HTML,另一个用于它检测到的变量。为此,我创建了以下语法:

@start        = Empty | content*;

content       = variable | passThrough;
passThrough   = /[^$]+/;
variable      = '$' '{' Word closeChar;

openChar      = '${';
closeChar     = '}';

我至少面临两个问题:因为variable我最初将其声明为openChar Word closeChar,但它不起作用(我仍然不知道为什么)。第二个问题(也是更重要的)是解析器在找到时停止<img src"${image}" />(即,带引号的字符串中的变量)。

我的问题是:

  1. 如何修改语法以使其按预期工作?
  2. 使用标记器更好吗?如果是这种情况,我应该如何配置它?
4

1 回答 1

4

ParseKit 的开发者在这里。我会回答你的两个问题:

1)您采用了正确的方法,但这是一个棘手的情况。有几个小问题,你的语法需要稍微改变一下。

我开发了一种对我有用的语法:

// Tokenizer Directives
@symbolState = '"' "'"; // effectively tells the tokenizer to turn off QuoteState. 
                      // Otherwise, variables enclosed in quotes would not be found (they'd be embedded in quoted strings). 
                      // now single- & double-quotes will be recognized as individual symbols, not start- & end-markers for quoted strings

@symbols = '${'; // declare '${' as a multi-char symbol

@reportsWhitespaceTokens = YES; // tell the tokenizer to preserve/report whitespace

// Grammar
@start = content*;
content = passthru | variable;
passthru = /[^$].*/;
variable = start name end;
start = '${';
end = '}';
name = Word;

然后在你的汇编器中实现这两个回调:

- (void)parser:(PKParser *)p didMatchName:(PKAssembly *)a {
    NSLog(@"%s %@", __PRETTY_FUNCTION__, a);
    PKToken *tok = [a pop];

    NSString *name = tok.stringValue;
    // do something with name
}

- (void)parser:(PKParser *)p didMatchPassthru:(PKAssembly *)a {
    NSLog(@"%s %@", __PRETTY_FUNCTION__, a);
    PKToken *tok = [a pop];

    NSMutableString *s = a.target;
    if (!s) {
        s = [NSMutableString string];
    }

    [s appendString:tok.stringValue];

    a.target = s;
}

然后您的客户端/驱动程序代码将如下所示:

NSString *g = // fetch grammar
PKParser *p = [[PKParserFactory factory] parserFromGrammar:g assembler:self];
NSString *s = @"<img src=\"${image}\" />";
[p parse:s];
NSString *result = [p parse:s];
NSLog(@"result %@", result);

这将被打印:

result: <img src="" />

2)是的,我认为对于这种相对简单的情况,直接使用 Tokenizer 肯定会好得多。性能会大大提高。以下是使用 Tokenizer 处理任务的方法:

PKTokenizer *t = [PKTokenizer tokenizerWithString:s];
[t setTokenizerState:t.symbolState from:'"' to:'"'];
[t setTokenizerState:t.symbolState from:'\'' to:'\''];
[t.symbolState add:@"${"];
t.whitespaceState.reportsWhitespaceTokens = YES;

NSMutableString *result = [NSMutableString string];

PKToken *eof = [PKToken EOFToken];
PKToken *tok = nil;
while (eof != (tok = [t nextToken])) {
    if ([@"${" isEqualToString:tok.stringValue]) {
        tok = [t nextToken];
        NSString *varName = tok.stringValue;

        // do something with variable
    } else if ([@"}" isEqualToString:tok.stringValue]) {
        // do nothing
    } else {
        [result appendString:tok.stringValue];
    }
}
于 2012-02-16T01:39:34.003 回答