5

iOSアプリケーションの場合、置き換えのためにUNIXスタイルの変数を含む可能性のあるHTMLファイルを解析したいと思います。たとえば、HTMLは次のようになります。

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

2つのコールバックを提供する単純なParseKit文法を作成しようとしています。1つはパススルーHTML用で、もう1つは検出する変数用です。そのために、次の文法を作成しました。

@start        = Empty | content*;

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

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

私はこれに関して少なくとも2つの問題に直面しています:variable私は最初にそれをとして宣言しましたopenChar Word closeCharが、それは機能しませんでした(私はまだ理由がわかりません)。2番目の問題(およびより重要な問題)は、パーサーが検出されると停止すること<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;

次に、次の2つのコールバックをアセンブラーに実装します。

- (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 に答える