2010-10-24 15 views
6

Estoy tratando de escribir una gramática ANTLR para el formato serialize() de PHP, y todo parece funcionar bien, excepto por las cadenas. El problema es que el formato de cadenas en serie es:regla ANTLR para consumir un número fijo de caracteres

s:6:"length"; 

En términos de expresiones regulares, una regla como s:(\d+):".{\1}"; describiría este formato si sólo referencias hacia atrás se permitió en el "número de coincidencias" recuento (pero no lo son) .

Pero no puedo encontrar una manera de expresar esto, ya sea para un analizador léxico o analizador gramatical: la idea es hacer que el número de caracteres leídos depende de una retro-referencia que describe el número de caracteres a leer, como en las constantes Fortran Hollerith (es decir, 6HLength), no en un delimitador de cadena.

Este ejemplo del ANTLR grammar for Fortran parece indicar el camino, pero no veo cómo. Tenga en cuenta que mi lengua meta es Python, mientras que la mayor parte del doc y son ejemplos para Java:

// numeral literal 
ICON {int counter=0;} : 
    /* other alternatives */ 
    // hollerith 
    'h' ({counter>0}? NOTNL {counter--;})* {counter==0}? 
     { 
     $setType(HOLLERITH); 
     String str = $getText; 
     str = str.replaceFirst("([0-9])+h", ""); 
     $setText(str); 
     } 
    /* more alternatives */ 
    ; 

Respuesta

4

Desde la entrada como s:3:"a"b"; es válida, no se puede definir un símbolo de String en su léxico, a menos que el primero y el último comillas dobles son siempre el inicio y el final de la cadena. Pero supongo que este no es el caso.

Por lo tanto, se necesita un analizador léxico regla como esta:

SString 
    : 's:' Int ':"' (.)* '";' 
    ; 

En otras palabras: que coincida con un s:, entonces un valor integer seguido por :" entonces uno o más caracteres que pueden ser cualquier cosa, terminando con ";. Pero necesita decirle al lexer que deje de consumir cuando no se alcance el valor Int. Puedes hacerlo mezclando un código simple en tu gramática para hacerlo. Puede incrustar código simple envolviéndolo dentro de { y }. Así que primero convertir el valor de la ficha Int mantiene en una variable entera llamada chars:

SString 
    : 's:' Int {chars = int($Int.text)} ':"' (.)* '";' 
    ; 

Ahora incrustar un código dentro del bucle (.)* para detenerlo consumir tan pronto como chars se cuenta atrás hasta cero:

SString 
    : 's:' Int {chars = int($Int.text)} ':"' ({if chars == 0: break} . {chars = chars-1})* '";' 
    ; 

y eso es todo.

Un poco de gramática demo:

grammar Test; 

options { 
    language=Python; 
} 

parse 
    : (SString {print 'parsed: [\%s]' \% $SString.text})+ EOF 
    ; 

SString 
    : 's:' Int {chars = int($Int.text)} ':"' ({if chars == 0: break} . {chars = chars-1})* '";' 
    ; 

Int 
    : '0'..'9'+ 
    ; 

(tenga en cuenta que necesita para escapar de la % dentro de su gramática!)

Y un script de prueba:

import antlr3 
from TestLexer import TestLexer 
from TestParser import TestParser 

input = 's:6:"length";s:1:""";s:0:"";s:3:"end";' 
char_stream = antlr3.ANTLRStringStream(input) 
lexer = TestLexer(char_stream) 
tokens = antlr3.CommonTokenStream(lexer) 
parser = TestParser(tokens) 
parser.parse() 

que produce el siguiente resultado:

parsed: [s:6:"length";] 
parsed: [s:1:""";] 
parsed: [s:0:"";] 
parsed: [s:3:"end";] 
Cuestiones relacionadas