كيفية التعامل مع تسلسل الهروب في سلسلة حرفية في ANTLR 3؟

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

  •  21-08-2019
  •  | 
  •  

سؤال

ولقد كنت أبحث من خلال وثائق ANTLR V3 (ونسختي مضمونة من "مرجع ANTLR النهائية")، وأنا لا يمكن أن يبدو لايجاد وسيلة نظيفة لتنفيذ تسلسل الهروب في سلسلة حرفية (أنا حاليا باستخدام هدف جافا). تمنيت أن تكون قادرة على فعل شيء مثل:

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".

ومما يؤسف له، والدعوة setText(...) في جزء ESCAPE_SEQUENCE يحدد النص لرمز STRING بأكمله، والتي من الواضح ليس ما أريد.

هل هناك طريقة لتنفيذ هذه القواعد دون إضافة وسيلة للذهاب مرة أخرى من خلال السلسلة الناتجة يدويا واستبدال أحرف (على سبيل المثال، مع شيء من هذا القبيل setText(escapeString(getText())) في حكم STRING

هل كانت مفيدة؟

المحلول

وهنا هو كيف يتحقق هذا في محلل 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()));}

        )
    ;

نصائح أخرى

لANTLR4 نجا الهدف جافا ومستوى القواعد سلسلة، وأنا استخدم فئة المفرد مخصصة: CharSupport لترجمة السلسلة. كان متوفرا في ANTLR API:

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

وكما رأيت في وثائق V4 والتجارب، لم يعد معتمداinit في جزء lexer!

و(أكثر كفاءة ربما) وثمة بديل آخر هو استخدام الحجج القاعدة:

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

وأنا في حاجة لفعل ذلك، ولكن كان هدفي C وليس جافا. وهنا كيف فعلت ذلك بناء على الجواب رقم 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.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top