2010-10-24 22 views
6

Sto provando a scrivere una grammatica ANTLR per il formato serialize() di PHP e tutto sembra funzionare correttamente, tranne che per le stringhe. Il problema è che il formato delle stringhe serializzati è:Regola ANTLR per consumare un numero fisso di caratteri

s:6:"length"; 

In termini di regex una regola come s:(\d+):".{\1}"; sarebbe descrivere questo formato se solo backreferences sono stati ammessi nel "numero di partite" count (ma non lo sono) .

Ma non riesco a trovare un modo per esprimere questo per una grammatica lesser o parser: l'intera idea è di fare in modo che il numero di caratteri letti dipenda da un backreference che descrive il numero di caratteri da leggere, come nelle costanti di Fortran Hollerith (cioè), non su un delimitatore di stringa.

Questo esempio dallo ANTLR grammar for Fortran sembra indicare la strada, ma non vedo come. Si noti che la mia lingua di destinazione è Python, mentre la maggior parte del documento e gli esempi sono per 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 */ 
    ; 

risposta

4

Dal ingresso come s:3:"a"b"; è valido, non è possibile definire un gettone String nel vostro lexer, a meno che il primo e l'ultimo il doppio preventivo è sempre l'inizio e la fine della stringa. Ma immagino che questo non sia il caso.

Quindi, avrete bisogno di una regola di lexer come questo:

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

In altre parole: abbinare un s:, quindi un valore integer seguito da :" quindi uno o più caratteri che possono essere qualsiasi cosa, per finire con ";. Ma è necessario dire al lexer di smettere di consumare quando non si raggiunge il valore Int. Puoi farlo mescolando un codice semplice nella tua grammatica per farlo. È possibile incorporare codice semplice avvolgendolo all'interno di { e }. Quindi, prima di convertire il valore del token Int tiene in una variabile intera chiamata chars:

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

Ora incorporare un codice all'interno del ciclo (.)* per fermarlo consumare appena chars è contato fino a zero:

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

e il gioco è fatto.

Un po 'di grammatica 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'+ 
    ; 

(si noti che è necessario per sfuggire alla % dentro la grammatica!)

E uno script di 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() 

che produce il seguente risultato:

parsed: [s:6:"length";] 
parsed: [s:1:""";] 
parsed: [s:0:"";] 
parsed: [s:3:"end";] 
Problemi correlati