如何在ANTLR 3中处理字符串字面量中的转义序列?

21

我一直在查看ANTLR v3文档(以及我的可靠的“ANTLR权威指南”副本),但似乎找不到一种干净的方式来实现字符串文字中的转义序列(我目前正在使用Java目标)。我原本希望能够像这样做:

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

STRING  
    : '\'' (ESCAPE_SEQUENCE | ~('\'' | '\\'))* '\''
      { 
        // strip the quotes from the resulting token
        setText(getText().substring(1, getText().length() - 1));
      } 
    ;
例如,我想要将输入标记 "'Foo\'s House'" 转换为字符串 "Foo's House"。
不幸的是,ESCAPE_SEQUENCE 片段中的 setText(...) 调用设置了整个 STRING 标记的文本,这显然不是我想要的。
有没有一种方法可以实现这个语法,而不必在结果字符串中添加一个方法来手动替换转义序列(例如,在 STRING 规则中使用像 setText(escapeString(getText())) 这样的内容)?
4个回答

17
以下是我在编写JSON解析器时所采用的方法。
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
我使用了这种方法,但请注意,在每个步骤中我必须附加 "getText()",而不是 "escaped.getText()"。该片段将未转义的文本写入整个STRING标记,而getText()返回该文本。对我来说,escaped.getText()返回带有反斜杠的原始片段。 - Sam Martin

6

对于ANTLR4、Java目标和标准转义字符串语法,我使用了一个专用的单例类:CharSupport来翻译字符串。它在antlr API中可用:

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

根据V4文档和实验结果显示,在词法分析器部分不再支持@init!


1
在你的例子中,ESC 的定义是什么? - Jaap

4

另一种(可能更有效的)选择是使用规则参数:

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

3

我需要做类似的事情,但我的目标是C语言而不是Java。以下是我根据答案#1(以及评论)的做法,供需要相似内容的人参考:

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.


这个代码执行得很好,但最终结果被转换成了大写。你知道怎么修复代码以返回相同大小写的结果吗? - user7698505

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接