Kompilyator dizaynining birinchi bosqichi bo'lgan leksik tahlilni chuqur o'rganish. Tokenlar, leksemalar, regular ifodalar, chekli avtomatlar va ularning amaliy qo'llanilishi haqida bilib oling.
Kompilyatorlar Dizayni: Leksik Tahlil Asoslari
Kompilyator dizayni - zamonaviy dasturiy ta'minot ishlab chiqishning asosini tashkil etuvchi kompyuter fanlarining qiziqarli va muhim sohasidir. Kompilyator - bu odam o'qiy oladigan dastlabki kod va mashina bajaradigan buyruqlar o'rtasidagi ko'prikdir. Ushbu maqolada kompilyatsiya jarayonining dastlabki bosqichi bo'lgan leksik tahlilning asoslari chuqur o'rganiladi. Biz uning maqsadi, asosiy tushunchalari va butun dunyodagi bo'lajak kompilyator dizaynerlari va dasturiy ta'minot muhandislari uchun amaliy ahamiyatini ko'rib chiqamiz.
Leksik Tahlil nima?
Leksik tahlil, shuningdek, skanerlash yoki tokenizatsiya deb ham ataladi, kompilyatorning birinchi bosqichidir. Uning asosiy vazifasi dastlabki kodni belgilar oqimi sifatida o'qish va ularni leksemalar deb ataladigan mazmunli ketma-ketliklarga guruhlashdir. Keyin har bir leksema o'z rolidan kelib chiqqan holda tasniflanadi va natijada tokenlar ketma-ketligi hosil bo'ladi. Buni keyingi qayta ishlash uchun kirish ma'lumotlarini tayyorlaydigan dastlabki saralash va belgilash jarayoni deb tasavvur qiling.
Tasavvur qiling, sizda `x = y + 5;` jumlasi bor. Leksik analizator uni quyidagi tokenlarga ajratadi:
- Identifikator: `x`
- O'zlashtirish operatori: `=`
- Identifikator: `y`
- Qo'shish operatori: `+`
- Butun son literali: `5`
- Nuqtali vergul: `;`
Leksik analizator asosan dasturlash tilining ushbu asosiy qurilish bloklarini aniqlaydi.
Leksik Tahlildagi Asosiy Tushunchalar
Tokenlar va Leksemalar
Yuqorida aytib o'tilganidek, token - bu leksemaning tasniflangan ifodasidir. Leksema - bu dastlabki kodda tokenga mos keladigan naqshga (pattern) mos keladigan belgilarning haqiqiy ketma-ketligidir. Pythondagi quyidagi kod parchasini ko'rib chiqing:
if x > 5:
print("x is greater than 5")
Ushbu parchadan tokenlar va leksemalarga ba'zi misollar:
- Token: KALIT_SO'Z, Leksema: `if`
- Token: IDENTIFIKATOR, Leksema: `x`
- Token: MUNOSABAT_OPERATORI, Leksema: `>`
- Token: BUTUN_SON_LITERALI, Leksema: `5`
- Token: IKKI_NUQTA, Leksema: `:`
- Token: KALIT_SO'Z, Leksema: `print`
- Token: SATR_LITERALI, Leksema: `"x is greater than 5"`
Token leksemaning *kategoriyasini* ifodalaydi, leksema esa dastlabki koddagi *haqiqiy satrdir*. Kompilyatsiyaning keyingi bosqichi bo'lgan parser dastur strukturasini tushunish uchun tokenlardan foydalanadi.
Regular Ifodalar
Regular ifodalar (regex) belgilar naqshlarini tavsiflash uchun kuchli va ixcham yozuvdir. Ular leksik tahlilda leksemalar ma'lum tokenlar sifatida tan olinishi uchun mos kelishi kerak bo'lgan naqshlarni aniqlashda keng qo'llaniladi. Regular ifodalar nafaqat kompilyator dizaynida, balki kompyuter fanlarining ko'plab sohalarida, matnni qayta ishlashdan tortib tarmoq xavfsizligigacha bo'lgan fundamental tushunchadir.
Quyida ba'zi keng tarqalgan regular ifoda belgilari va ularning ma'nolari keltirilgan:
- `.` (nuqta): Yangi qator belgisidan tashqari har qanday bitta belgiga mos keladi.
- `*` (yulduzcha): Oldingi elementga nol yoki undan ko'p marta mos keladi.
- `+` (plyus): Oldingi elementga bir yoki undan ko'p marta mos keladi.
- `?` (so'roq belgisi): Oldingi elementga nol yoki bir marta mos keladi.
- `[]` (kvadrat qavslar): Belgilar sinfini belgilaydi. Masalan, `[a-z]` har qanday kichik harfga mos keladi.
- `[^]` (inkor etilgan kvadrat qavslar): Inkor etilgan belgilar sinfini belgilaydi. Masalan, `[^0-9]` raqam bo'lmagan har qanday belgiga mos keladi.
- `|` (vertikal chiziq): Alternativani (YOKI) ifodalaydi. Masalan, `a|b` `a` yoki `b` ga mos keladi.
- `()` (qavslar): Elementlarni birga guruhlaydi va ularni ushlaydi.
- `\` (teskari sleş): Maxsus belgilarni ekrannaydi. Masalan, `\.` haqiqiy nuqtaga mos keladi.
Keling, regular ifodalardan tokenlarni aniqlash uchun qanday foydalanish mumkinligiga oid ba'zi misollarni ko'rib chiqaylik:
- Butun son literali: `[0-9]+` (Bir yoki undan ko'p raqam)
- Identifikator: `[a-zA-Z_][a-zA-Z0-9_]*` (Harf yoki pastki chiziq bilan boshlanadi, keyin nol yoki undan ko'p harf, raqam yoki pastki chiziq keladi)
- O'nli kasr literali: `[0-9]+\.[0-9]+` (Bir yoki undan ko'p raqam, undan keyin nuqta, undan keyin bir yoki undan ko'p raqam) Bu soddalashtirilgan misol; yanada mustahkam regex darajalar va ixtiyoriy ishoralarni ham o'z ichiga oladi.
Turli dasturlash tillarida identifikatorlar, butun son literallari va boshqa tokenlar uchun turli qoidalar bo'lishi mumkin. Shuning uchun, mos keladigan regular ifodalarni shunga mos ravishda sozlash kerak. Masalan, ba'zi tillar identifikatorlarda Unicode belgilariga ruxsat berishi mumkin, bu esa murakkabroq regexni talab qiladi.
Chekli Avtomatlar
Chekli avtomatlar (FA) regular ifodalar bilan aniqlangan naqshlarni tanib olish uchun ishlatiladigan abstrakt mashinalardir. Ular leksik analizatorlarni amalga oshirishda asosiy tushunchadir. Chekli avtomatlarning ikki asosiy turi mavjud:
- Deterministik Chekli Avtomat (DKA): Har bir holat va kirish belgisi uchun boshqa holatga aniq bitta o'tish mavjud. DKA-larni amalga oshirish va bajarish osonroq, lekin ularni to'g'ridan-to'g'ri regular ifodalardan qurish murakkabroq bo'lishi mumkin.
- Nodeterministik Chekli Avtomat (NKA): Har bir holat va kirish belgisi uchun boshqa holatlarga nol, bir yoki bir nechta o'tishlar bo'lishi mumkin. NKA-larni regular ifodalardan qurish osonroq, lekin bajarish uchun murakkabroq algoritmlarni talab qiladi.
Leksik tahlildagi odatiy jarayon quyidagilarni o'z ichiga oladi:
- Har bir token turi uchun regular ifodalarni NKAga aylantirish.
- NKA ni DKA ga aylantirish.
- DKA ni jadvalga asoslangan skaner sifatida amalga oshirish.
Keyin DKA kirish oqimini skanerlash va tokenlarni aniqlash uchun ishlatiladi. DKA boshlang'ich holatdan boshlanadi va kirish ma'lumotlarini belgima-belgi o'qiydi. Joriy holat va kirish belgisiga asoslanib, u yangi holatga o'tadi. Agar DKA belgilar ketma-ketligini o'qib bo'lgach, qabul qiluvchi holatga yetsa, bu ketma-ketlik leksema sifatida tan olinadi va tegishli token yaratiladi.
Leksik Tahlil Qanday Ishlaydi
Leksik analizator quyidagicha ishlaydi:
- Dastlabki kodni o'qish: Lekser dastlabki kodni kirish fayli yoki oqimidan belgima-belgi o'qiydi.
- Leksemalarni aniqlash: Lekser haqiqiy leksemalarni tashkil etuvchi belgilar ketma-ketligini aniqlash uchun regular ifodalardan (yoki, aniqrog'i, regular ifodalardan olingan DKA dan) foydalanadi.
- Tokenlarni yaratish: Topilgan har bir leksema uchun lekser token yaratadi, u leksemaning o'zini va uning token turini (masalan, IDENTIFIKATOR, BUTUN_SON_LITERALI, OPERATOR) o'z ichiga oladi.
- Xatolarni qayta ishlash: Agar lekser belgilangan naqshlarning hech biriga mos kelmaydigan belgilar ketma-ketligiga duch kelsa (ya'ni, uni tokenlashtirib bo'lmasa), u leksik xatolik haqida xabar beradi. Bu yaroqsiz belgi yoki noto'g'ri shakllangan identifikator bo'lishi mumkin.
- Tokenlarni Parserga uzatish: Lekser tokenlar oqimini kompilyatorning keyingi bosqichi bo'lgan parserga uzatadi.
Ushbu oddiy C kodi parchasini ko'rib chiqing:
int main() {
int x = 10;
return 0;
}
Leksik analizator ushbu kodni qayta ishlaydi va quyidagi tokenlarni yaratadi (soddalashtirilgan):
- KALIT_SO'Z: `int`
- IDENTIFIKATOR: `main`
- CHAP_QAVS: `(`
- O'NG_QAVS: `)`
- CHAP_JINGALAK_QAVS: `{`
- KALIT_SO'Z: `int`
- IDENTIFIKATOR: `x`
- O'ZLASHTIRISH_OPERATORI: `=`
- BUTUN_SON_LITERALI: `10`
- NUQTALI_VERGUL: `;`
- KALIT_SO'Z: `return`
- BUTUN_SON_LITERALI: `0`
- NUQTALI_VERGUL: `;`
- O'NG_JINGALAK_QAVS: `}`
Leksik Analizatorning Amaliy Realizatsiyasi
Leksik analizatorni amalga oshirishning ikkita asosiy yondashuvi mavjud:
- Qo'lda amalga oshirish: Lekser kodini qo'lda yozish. Bu ko'proq nazorat va optimallashtirish imkoniyatlarini beradi, ammo ko'proq vaqt talab etadi va xatolarga moyil.
- Lekser generatorlaridan foydalanish: Lex (Flex), ANTLR yoki JFlex kabi vositalardan foydalanish, ular regular ifoda spetsifikatsiyalari asosida lekser kodini avtomatik ravishda yaratadi.
Qo'lda Amalga Oshirish
Qo'lda amalga oshirish odatda holat mashinasini (DKA) yaratishni va kirish belgilariga asoslangan holatlar o'rtasida o'tish uchun kod yozishni o'z ichiga oladi. Bu yondashuv leksik tahlil jarayoni ustidan nozik nazorat qilish imkonini beradi va muayyan ishlash talablari uchun optimallashtirilishi mumkin. Biroq, u regular ifodalar va chekli avtomatlarni chuqur tushunishni talab qiladi va uni saqlash va tuzatish qiyin bo'lishi mumkin.
Quyida qo'lda yozilgan lekserning Pythonda butun son literallarini qanday qayta ishlashi mumkinligiga oid konseptual (va juda soddalashtirilgan) misol keltirilgan:
def lexer(input_string):
tokens = []
i = 0
while i < len(input_string):
if input_string[i].isdigit():
# Raqam topildi, butun sonni tuzishni boshlaymiz
num_str = ""
while i < len(input_string) and input_string[i].isdigit():
num_str += input_string[i]
i += 1
tokens.append(("INTEGER", int(num_str)))
i -= 1 # Oxirgi inkrement uchun tuzatish
elif input_string[i] == '+':
tokens.append(("PLUS", "+"))
elif input_string[i] == '-':
tokens.append(("MINUS", "-"))
# ... (boshqa belgilar va tokenlarni qayta ishlash)
i += 1
return tokens
Bu oddiy misol, lekin u kirish satrini qo'lda o'qish va belgi naqshlariga asoslanib tokenlarni aniqlashning asosiy g'oyasini ko'rsatadi.
Lekser Generatorlari
Lekser generatorlari - bu leksik analizatorlarni yaratish jarayonini avtomatlashtiradigan vositalardir. Ular kirish sifatida spetsifikatsiya faylini oladi, u har bir token turi uchun regular ifodalarni va token tan olinganda bajariladigan harakatlarni belgilaydi. Keyin generator maqsadli dasturlash tilida lekser kodini ishlab chiqaradi.
Quyida ba'zi mashhur lekser generatorlari keltirilgan:
- Lex (Flex): Keng qo'llaniladigan lekser generatori, ko'pincha parser generatori bo'lgan Yacc (Bison) bilan birgalikda ishlatiladi. Flex o'zining tezligi va samaradorligi bilan mashhur.
- ANTLR (ANother Tool for Language Recognition): Shuningdek, lekser generatorini o'z ichiga olgan kuchli parser generatori. ANTLR keng doiradagi dasturlash tillarini qo'llab-quvvatlaydi va murakkab grammatikalar va lekserlarni yaratishga imkon beradi.
- JFlex: Java uchun maxsus ishlab chiqilgan lekser generatori. JFlex samarali va yuqori darajada sozlanadigan lekserlarni yaratadi.
Lekser generatoridan foydalanish bir qancha afzalliklarni beradi:
- Ishlab chiqish vaqtini qisqartirish: Lekser generatorlari leksik analizatorni ishlab chiqish uchun zarur bo'lgan vaqt va kuchni sezilarli darajada kamaytiradi.
- Aniqlikni oshirish: Lekser generatorlari yaxshi aniqlangan regular ifodalarga asoslangan lekserlarni ishlab chiqaradi, bu esa xatolar xavfini kamaytiradi.
- Saqlash osonligi: Lekser spetsifikatsiyasini o'qish va saqlash odatda qo'lda yozilgan kodga qaraganda osonroq.
- Ishlash unumdorligi: Zamonaviy lekser generatorlari yuqori darajada optimallashtirilgan lekserlarni ishlab chiqaradi, ular a'lo darajadagi ishlash unumdorligiga erisha oladi.
Quyida butun sonlar va identifikatorlarni tanib olish uchun oddiy Flex spetsifikatsiyasiga misol keltirilgan:
%%
[0-9]+ { printf("BUTUN_SON: %s\n", yytext); }
[a-zA-Z_][a-zA-Z0-9_]* { printf("IDENTIFIKATOR: %s\n", yytext); }
[ \t\n]+ ; // Bo'shliqlarni e'tiborsiz qoldirish
. { printf("NOQONUNIY BELGI: %s\n", yytext); }
%%
Ushbu spetsifikatsiya ikkita qoidani belgilaydi: biri butun sonlar uchun va ikkinchisi identifikatorlar uchun. Flex ushbu spetsifikatsiyani qayta ishlaganda, u ushbu tokenlarni taniydigan lekser uchun C kodini yaratadi. `yytext` o'zgaruvchisi mos kelgan leksemani o'z ichiga oladi.
Leksik Tahlilda Xatolarni Qayta Ishlash
Xatolarni qayta ishlash leksik tahlilning muhim jihatidir. Lekser yaroqsiz belgiga yoki noto'g'ri shakllangan leksemaga duch kelganda, u foydalanuvchiga xatolik haqida xabar berishi kerak. Umumiy leksik xatoliklarga quyidagilar kiradi:
- Yaroqsiz belgilar: Tilning alifbosiga kirmaydigan belgilar (masalan, identifikatorlarda ruxsat etilmagan tilda `$` belgisi).
- Tugatılmagan satrlar: Mos keluvchi qo'shtirnoq bilan yopilmagan satrlar.
- Noto'g'ri sonlar: To'g'ri shakllantirilmagan sonlar (masalan, bir nechta o'nli kasr nuqtasiga ega son).
- Maksimal uzunlikdan oshib ketish: Ruxsat etilgan maksimal uzunlikdan oshib ketadigan identifikatorlar yoki satr literallari.
Leksik xatolik aniqlanganda, lekser quyidagilarni bajarishi kerak:
- Xato haqida xabar berish: Xato sodir bo'lgan qator va ustun raqamini, shuningdek xatoning tavsifini o'z ichiga olgan xato xabarini yaratish.
- Qayta tiklashga harakat qilish: Xatodan tiklanishga va kirish ma'lumotlarini skanerlashni davom ettirishga harakat qilish. Bu yaroqsiz belgilarni o'tkazib yuborishni yoki joriy tokenni tugatishni o'z ichiga olishi mumkin. Maqsad - zanjirli xatolarning oldini olish va foydalanuvchiga iloji boricha ko'proq ma'lumot berish.
Xato xabarlari aniq va ma'lumotga boy bo'lishi kerak, bu dasturchiga muammoni tezda aniqlash va tuzatishga yordam beradi. Masalan, tugallanmagan satr uchun yaxshi xato xabari shunday bo'lishi mumkin: `Xatolik: 10-qator, 25-ustunda tugallanmagan satr literali`.
Leksik Tahlilning Kompilyatsiya Jarayonidagi Roli
Leksik tahlil - kompilyatsiya jarayonidagi hal qiluvchi birinchi qadamdir. Uning natijasi bo'lgan tokenlar oqimi keyingi bosqich, parser (sintaktik analizator) uchun kirish ma'lumotlari bo'lib xizmat qiladi. Parser dasturning grammatik tuzilishini ifodalovchi abstrakt sintaksis daraxtini (AST) qurish uchun tokenlardan foydalanadi. Aniq va ishonchli leksik tahlilsiz, parser dastlabki kodni to'g'ri talqin qila olmaydi.
Leksik tahlil va sintaktik tahlil o'rtasidagi munosabatni quyidagicha umumlashtirish mumkin:
- Leksik tahlil: Dastlabki kodni tokenlar oqimiga ajratadi.
- Sintaktik tahlil (Parsing): Tokenlar oqimining tuzilishini tahlil qiladi va abstrakt sintaksis daraxtini (AST) quradi.
Keyin AST kompilyatorning keyingi bosqichlari, masalan, semantik tahlil, oraliq kod generatsiyasi va kodni optimallashtirish tomonidan yakuniy bajariladigan kodni ishlab chiqarish uchun ishlatiladi.
Leksik Tahlilda Ilg'or Mavzular
Ushbu maqolada leksik tahlilning asoslari yoritilgan bo'lsa-da, o'rganishga arziydigan bir nechta ilg'or mavzular mavjud:
- Unicode qo'llab-quvvatlashi: Identifikatorlar va satr literallarida Unicode belgilarini qayta ishlash. Bu murakkabroq regular ifodalar va belgilarni tasniflash usullarini talab qiladi.
- Ichki o'rnatilgan tillar uchun leksik tahlil: Boshqa tillar ichiga o'rnatilgan tillar uchun leksik tahlil (masalan, Java ichidagi SQL). Bu ko'pincha kontekstga qarab turli lekserlar o'rtasida almashishni o'z ichiga oladi.
- Inkremental leksik tahlil: Dastlabki kodning faqat o'zgargan qismlarini samarali qayta skanerlay oladigan leksik tahlil, bu interaktiv ishlab chiqish muhitlarida foydalidir.
- Kontekstga bog'liq leksik tahlil: Token turining atrofdagi kontekstga bog'liq bo'lgan leksik tahlil. Bu til sintaksisidagi noaniqliklarni bartaraf etish uchun ishlatilishi mumkin.
Xalqarolashtirish Masalalari
Global foydalanish uchun mo'ljallangan til uchun kompilyatorni loyihalashda leksik tahlil uchun ushbu xalqarolashtirish jihatlarini hisobga oling:
- Belgilar kodirovkasi: Turli alifbolar va belgilar to'plamlarini qayta ishlash uchun turli belgilar kodirovkalarini (UTF-8, UTF-16, etc.) qo'llab-quvvatlash.
- Mahalliy formatlash: Mahalliyga xos son va sana formatlarini qayta ishlash. Masalan, o'nli kasr ajratuvchisi ba'zi joylarda nuqta (`.`) o'rniga vergul (`,`) bo'lishi mumkin.
- Unicode normalizatsiyasi: Izchil taqqoslash va moslashtirishni ta'minlash uchun Unicode satrlarini normallashtirish.
Xalqarolashtirishni to'g'ri hal qilmaslik turli tillarda yozilgan yoki turli belgilar to'plamidan foydalanadigan dastlabki kod bilan ishlashda noto'g'ri tokenizatsiya va kompilyatsiya xatolariga olib kelishi mumkin.
Xulosa
Leksik tahlil kompilyator dizaynining fundamental jihatidir. Ushbu maqolada muhokama qilingan tushunchalarni chuqur anglash kompilyatorlar, interpretatorlar yoki boshqa tilni qayta ishlash vositalarini yaratish yoki ular bilan ishlash bilan shug'ullanadigan har bir kishi uchun muhimdir. Tokenlar va leksemalarni tushunishdan tortib, regular ifodalar va chekli avtomatlarni o'zlashtirishgacha, leksik tahlil bilimi kompilyator qurish dunyosiga chuqurroq kirib borish uchun mustahkam poydevor yaratadi. Lekser generatorlarini qo'llash va xalqarolashtirish jihatlarini hisobga olgan holda, ishlab chiquvchilar keng doiradagi dasturlash tillari va platformalar uchun mustahkam va samarali leksik analizatorlarni yaratishlari mumkin. Dasturiy ta'minot ishlab chiqish rivojlanishda davom etar ekan, leksik tahlil tamoyillari global miqyosda tilni qayta ishlash texnologiyasining asosiy toshi bo'lib qoladi.