2010-10-24 5 views
6

J'essaie d'écrire une grammaire ANTLR pour le format PHP serialize(), et tout semble fonctionner correctement, sauf pour les chaînes. Le problème est que le format des chaînes sérialisés est:Règle ANTLR pour consommer un nombre fixe de caractères

s:6:"length"; 

En termes de regexes, une règle comme s:(\d+):".{\1}"; décriraient ce format si seulement les références arrières ont été autorisés dans le « nombre de correspondances » count (mais ils ne sont pas) . Mais je ne peux pas trouver un moyen d'exprimer cela pour une grammaire de syntaxe ou de syntaxe: l'idée est de faire dépendre le nombre de caractères lus d'une référence arrière décrivant le nombre de caractères à lire, comme dans les constantes de Fortran Hollerith (c'est-à-dire 6HLength), pas sur un délimiteur de chaîne.

Cet exemple du ANTLR grammar for Fortran semble pointer le chemin, mais je ne vois pas comment. Notez que ma langue cible est Python, alors que la plupart des doc et des exemples sont pour 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 */ 
    ; 

Répondre

4

Depuis l'entrée comme s:3:"a"b"; est valide, vous ne pouvez pas définir un jeton String dans votre lexer, à moins que le premier et le dernier double citation sont toujours le début et la fin de votre chaîne. Mais je suppose que ce n'est pas le cas.

Ainsi, vous aurez besoin d'une règle de lexer comme ceci:

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

En d'autres termes: correspondre à un s:, une valeur integer suivie :" puis un ou plusieurs caractères qui peuvent être quelque chose, se terminant par ";. Mais vous devez indiquer à la lexeur d'arrêter de consommer lorsque la valeur Int n'est pas atteinte. Vous pouvez le faire en mélangeant du code clair dans votre grammaire pour le faire. Vous pouvez incorporer du code brut en l'entourant à { et }. Alors d'abord convertir la valeur tient la Int jeton dans une variable entière appelée chars:

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

embed maintenant un code dans la boucle (.)* pour arrêter de consommer dès chars est compté à zéro:

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

et c'est tout.

Une petite grammaire de démonstration:

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'+ 
    ; 

(notez que vous devez échapper à la % dans votre grammaire!)

Et un script de test:

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

qui produit la sortie suivante:

parsed: [s:6:"length";] 
parsed: [s:1:""";] 
parsed: [s:0:"";] 
parsed: [s:3:"end";] 
Questions connexes