Belgilangan sonli belgilarni iste'mol qilish uchun ANTLR qoidasi

Men PHP serialize() formati uchun ANTLR grammatikasini yozishga harakat qilyapman va satrlardan tashqari hamma narsa yaxshi ishlaydi. Muammo shundaki, ketma-ketlashtirilgan satrlar formati:

s:6:"length";

Regexlar nuqtai nazaridan, s:(\d+):".{\1}"; kabi qoida bu formatni tavsiflaydi, agar "mosliklar soni" sonida faqat qayta havolalarga ruxsat berilsa (lekin ular yo'q).

Lekin men buni lekser yoki parser grammatikasi uchun ifodalashning yo'lini topa olmayapman: butun g'oya o'qiladigan belgilar sonini Fortran Hollerith doimiylarida (ya'ni 6HLength) o'qilishi kerak bo'lgan belgilar sonini tavsiflovchi ma'lumotnomaga bog'liq qilishdir. string chegaralovchida emas.

Fortran uchun ANTLR grammatikasi dan olingan ushbu misol yo'lni ko'rsatayotganga o'xshaydi, lekin Qanday qilib ko'rmayapman. E'tibor bering, mening maqsad tilim Python, ko'pchilik hujjat va misollar Java uchun:

// numeral literal
ICON {int counter=0;} :
    /* other alternatives */
    // hollerith
    'h' ({counter>0}? NOTNL {counter--;})* {counter==0}?
      {
      $setType(HOLLERITH);
      String str = $getText;
      str = str.replaceFirst("([0-9])+h", "");
      $setText(str);
      }
    /* more alternatives */
    ;

person FGM    schedule 24.10.2010    source manba


Javoblar (1)


s:3:"a"b"; kabi kiritish toʻgʻri boʻlgani uchun, birinchi va oxirgi qoʻsh qoʻshtirnoq har doim satrning boshi va oxiri boʻlmasa, lekseringizda String belgisini aniqlay olmaysiz. Lekin menimcha, bu unday emas.

Shunday qilib, sizga shunday lekser qoidasi kerak bo'ladi:

SString
  :  's:' Int ':"' ( . )* '";'
  ;

Boshqacha qilib aytganda: s:, keyin integer qiymatidan keyin :" va keyin "; bilan tugaydigan har qanday narsa bo'lishi mumkin bo'lgan bir yoki bir nechta belgilarni moslang. Lekin Int qiymatiga erishilmasa, lekserga iste'mol qilishni to'xtatishni aytishingiz kerak. Buni grammatikangizdagi oddiy kodni aralashtirish orqali qilishingiz mumkin. Oddiy kodni { va } ichiga oʻrash orqali joylashtirishingiz mumkin. Shunday qilib, avval Int tokeni ega bo'lgan qiymatni chars deb nomlangan butun o'zgaruvchiga aylantiring:

SString
  :  's:' Int {chars = int($Int.text)} ':"' ( . )* '";'
  ;

Endi chars nolgacha sanalgandan so'ng uni iste'mol qilishni to'xtatish uchun ( . )* tsikliga ba'zi kodlarni kiriting:

SString
  :  's:' Int {chars = int($Int.text)} ':"' ( {if chars == 0: break} . {chars = chars-1} )* '";'
  ;

va tamom.

Kichkina demo grammatika:

grammar Test;

options {
  language=Python;
}

parse
  :  (SString {print 'parsed: [\%s]' \% $SString.text})+ EOF
  ;

SString
  :  's:' Int {chars = int($Int.text)} ':"' ( {if chars == 0: break} . {chars = chars-1} )* '";'
  ;

Int
  :  '0'..'9'+
  ;

(esda tutingki, grammatikangizdagi % dan qochishingiz kerak!)

Va sinov skripti:

import antlr3
from TestLexer import TestLexer
from TestParser import TestParser

input = 's:6:"length";s:1:""";s:0:"";s:3:"end";'
char_stream = antlr3.ANTLRStringStream(input)
lexer = TestLexer(char_stream)
tokens = antlr3.CommonTokenStream(lexer)
parser = TestParser(tokens)
parser.parse()

quyidagi mahsulotlarni ishlab chiqaradi:

parsed: [s:6:"length";]
parsed: [s:1:""";]
parsed: [s:0:"";]
parsed: [s:3:"end";]
person Bart Kiers    schedule 25.10.2010