2012-02-15 24 views
5

Para una aplicación iOS, quiero analizar un archivo HTML que puede contener variables de estilo UNIX para su reemplazo. Por ejemplo, el HTML puede verse como:Gramática simple de ParseKit para HTML con variables de reemplazo

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

Estoy intentando crear una gramática simple ParseKit que me va a proporcionar dos devoluciones de llamada: Una pasarela para el HTML, y otro para las variables que detecte. Para ello, he creado la siguiente gramática:

@start  = Empty | content*; 

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

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

que se me presentan al menos dos problemas con esto: por variable que había declarado originalmente como openChar Word closeChar, pero no funcionó (todavía no sé por qué) El segundo problema (y más importante) es que el analizador se detiene cuando encuentra <img src"${image}" /> (es decir, una variable dentro de una cadena entrecomillada).

Mis preguntas son:

  1. ¿Cómo puedo modificar la gramática para que funcione como se esperaba?
  2. ¿Es mejor usar un tokenizer? Si ese es el caso, ¿cómo debería configurarlo?

Respuesta

4

Desarrollador de ParseKit aquí. Voy a responder a sus dos preguntas:

1) Está tomando el enfoque correcto, pero este es un caso complicado. Hay varios pequeños problemas, y tu Gramática debe cambiarse un poco.

he desarrollado una gramática que está trabajando para mí:

// 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; 

luego implementar estos dos devoluciones de llamada en su ensamblador:

- (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; 
} 

Y luego su código/controlador de cliente se verá algo como esto:

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); 

este será impreso:

result: <img src="" /> 

2) Sí, creo que sin duda sería mucho mejor utilizar el Tokenizer directamente para este caso relativamente sencillo. El rendimiento será masivamente mejor. A continuación se explica cómo abordar la tarea con el 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]; 
    } 
} 
+1

Gracias Todd! Tomaré el enfoque de tokenizer, ya que parece ser más rápido y con una implementación mucho menos compleja. Sin embargo, espero usar una gramática en algún momento. – pgb