Pregunta

He estado revisando la documentación de ANTLR v3 (y mi copia de confianza de " La referencia definitiva de ANTLR "), y parece que no puedo encontrar una manera limpia de implementar secuencias de escape en literales de cadena (actualmente estoy usando el objetivo de Java). Tenía la esperanza de poder hacer algo como:

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 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 todo el token 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 <=>)?

¿Fue útil?

Solución

Así es como lo logré 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()));}

        )
    ;

Otros consejos

Para ANTLR4, el objetivo de Java y la gramática de cadenas con escape estándar, utilicé una clase singleton dedicada: CharSupport para traducir cadenas. Está disponible en la API de antlr:

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

Como vi en la documentación de V4 y por experimentos, ¡@init ya no es compatible con la parte lexer!

Otra alternativa (posiblemente más eficiente) es usar 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)); }
    );

Necesitaba hacer exactamente eso, pero mi objetivo era C y no Java. Así es como lo hice en función de la respuesta n. ° 1 (y comentario), en caso de que alguien necesite algo similar:

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top