2009-02-02 18 views
19

He estado buscando en la documentación de ANTLR v3 (y en mi fiel copia de "La referencia definitiva ANTLR"), y parece que no puedo encontrar una manera limpia de implementar secuencias de escape en literales de cadenas (actualmente estoy usando el objetivo de Java). Tenía la esperanza de ser capaz de hacer algo como:Cómo manejar secuencias de escape en literales de cadena en ANTLR 3?

fragment 
ESCAPE_SEQUENCE 
    : '\\' '\'' { setText("'"); } 
    ; 

STRING 
    : '\'' (ESCAPE_SEQUENCE | ~('\'' | '\\'))* '\'' 
     { 
     // strip the quotes from the resulting token 
     setText(getText().substring(1, getText().length() - 1)); 
     } 
    ; 

Por ejemplo, me gustaría que el token de entrada "'Foo\'s House'" para convertirse en la cadena "Foo's House".

Desafortunadamente, la llamada setText(...) en el fragmento ESCAPE_SEQUENCE establece el texto para el token completo STRING, que obviamente no es lo que quiero.

¿Hay alguna manera de implementar esta gramática sin agregar un método para volver a través de la cadena resultante y reemplazar manualmente las secuencias de escape (por ejemplo, con algo como setText(escapeString(getText())) en la regla)?

Respuesta

14

Así es como logré esto en el analizador JSON que escribí.

STRING  
@init{StringBuilder lBuf = new StringBuilder();} 
    : 
      '"' 
      (escaped=ESC {lBuf.append(getText());} | 
      normal=~('"'|'\\'|'\n'|'\r')  {lBuf.appendCodePoint(normal);})* 
      '"'  
      {setText(lBuf.toString());} 
    ; 

fragment 
ESC 
    : '\\' 
     ( 'n' {setText("\n");} 
     | 'r' {setText("\r");} 
     | 't' {setText("\t");} 
     | 'b' {setText("\b");} 
     | 'f' {setText("\f");} 
     | '"' {setText("\"");} 
     | '\'' {setText("\'");} 
     | '/' {setText("/");} 
     | '\\' {setText("\\");} 
     | ('u')+ i=HEX_DIGIT j=HEX_DIGIT k=HEX_DIGIT l=HEX_DIGIT 
        {setText(ParserUtil.hexToChar(i.getText(),j.getText(), 
               k.getText(),l.getText()));} 

     ) 
    ; 
+4

Usé este enfoque, pero tenga en cuenta que tuve que agregar "getText()" en lugar de "escaped.getText()" en cada paso. El fragmento escribe el texto sin esparcir en todo el token de STRING, que getText() devuelve. Para mí, escaped.getText() devuelve el fragmento original con las barras invertidas intactas. –

3

Necesitaba hacer exactamente eso, pero mi objetivo era C y no Java. He aquí cómo lo hice basado en la respuesta # 1 (y comentarios), por si alguien necesita algo por igual:

QUOTE :  '\''; 
STR 
@init{ pANTLR3_STRING unesc = GETTEXT()->factory->newRaw(GETTEXT()->factory); } 
     :  QUOTE (reg = ~('\\' | '\'') { unesc->addc(unesc, reg); } 
         | esc = ESCAPED { unesc->appendS(unesc, GETTEXT()); })+ QUOTE { SETTEXT(unesc); }; 

fragment 
ESCAPED :  '\\' 
       ('\\' { SETTEXT(GETTEXT()->factory->newStr8(GETTEXT()->factory, (pANTLR3_UINT8)"\\")); } 
       | '\'' { SETTEXT(GETTEXT()->factory->newStr8(GETTEXT()->factory, (pANTLR3_UINT8)"\'")); } 
       ) 
     ; 

HTH.

4

Otra alternativa (posiblemente más eficiente) es utilizar argumentos de reglas:

STRING 
@init { final StringBuilder buf = new StringBuilder(); } 
: 
    '"' 
    (
    ESCAPE[buf] 
    | i = ~('\\' | '"') { buf.appendCodePoint(i); } 
    )* 
    '"' 
    { setText(buf.toString()); }; 

fragment ESCAPE[StringBuilder buf] : 
    '\\' 
    ('t' { buf.append('\t'); } 
    | 'n' { buf.append('\n'); } 
    | 'r' { buf.append('\r'); } 
    | '"' { buf.append('\"'); } 
    | '\\' { buf.append('\\'); } 
    | 'u' a = HEX_DIGIT b = HEX_DIGIT c = HEX_DIGIT d = HEX_DIGIT { buf.append(ParserUtil.hexChar(a, b, c, d)); } 
    ); 
+0

Buena idea, funciona bien. Gracias por compartir. –

4

Para ANTLR4, el objetivo estándar Java y escaparon gramática cadena, he usado una clase Singleton dedicado: CharSupport para traducir cadena. Está disponible en antlr API:

STRING   : '"' 
       ( ESC 
       | ~('"'|'\\'|'\n'|'\r') 
       )* 
        '"' { 
         setText( 
          org.antlr.v4.misc.CharSupport.getStringFromGrammarStringLiteral(
           getText() 
          ) 
         ); 
        } 
       ; 

Como vi en la documentación V4 y por los experimentos, @Init ya no se admite en parte lexer!

+0

¿Cuál es la definición de ESC en su ejemplo? – Jaap

Cuestiones relacionadas