Comment gérer les séquences d'échappement dans les littéraux de chaîne dans ANTLR 3?

StackOverflow https://stackoverflow.com/questions/504402

  •  21-08-2019
  •  | 
  •  

Question

J'ai parcouru la documentation d'ANTLR v3 (et ma copie fidèle de & "La référence définitive d'ANTLR &";) et je n'arrive pas à trouver un moyen propre d'implémenter des séquences d'échappement. en littéraux de chaîne (j'utilise actuellement la cible Java). J'espérais pouvoir faire quelque chose comme:

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

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

Par exemple, je souhaiterais que le jeton d'entrée & "; 'Foo\'s House' &"; pour devenir la chaîne " Foo's House ".

Malheureusement, l'appel setText(...) dans le fragment ESCAPE_SEQUENCE définit le texte du jeton STRING complet, ce qui n'est évidemment pas ce que je veux.

Existe-t-il un moyen d'implémenter cette grammaire sans ajouter de méthode pour revenir en arrière dans la chaîne résultante et remplacer manuellement les séquences d'échappement (par exemple, par quelque chose comme setText(escapeString(getText())) dans la <=> règle)?

Était-ce utile?

La solution

Voici comment j'ai accompli cela dans l'analyseur JSON que j'ai écrit.

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

        )
    ;

Autres conseils

Pour ANTLR4, la cible Java et la grammaire des chaînes à caractères d'échappement standard, j'ai utilisé une classe singleton dédiée: CharSupport pour traduire une chaîne. Il est disponible dans l’API antlr:

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

Comme je l'ai vu dans la documentation V4 et par des expériences, @init n'est plus supporté dans la partie lexer!

Une autre alternative (peut-être plus efficace) consiste à utiliser les arguments de la règle:

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)); }
    );

Je devais faire cela, mais ma cible était C et non Java. Voici comment je l'ai fait en me basant sur la réponse n ° 1 (et le commentaire), au cas où quelqu'un aurait besoin de quelque chose de similaire:

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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top