Кіріспе

Компьютерлік ғылымда таңбалар тізбегін токендер тізбегіне түрлендіру. Лексикалық токенизация – мәтінді "лексер" бағдарламасымен анықталған санаттарға жататын (семантикалық немесе синтаксикалық) мағыналы лексикалық токендерге түрлендіру. Табиғи тілдерде бұл санаттарға зат есімдер, етістіктер, сын есімдер, тыныш белгілер және т.б. кіреді. Бағдарламалау тілінде санаттарға идентификаторлар, операторлар, топтастыру таңбалары және дерек түрлері жатады. Лексикалық токенизация Үлкен тілдік модельдерде (LLM) қолданылатын токенизация түрімен байланысты, бірақ екі ерекшелігі бар. Біріншіден, лексикалық токенизация көбінесе лексикалық грамматикаға негізделеді, ал LLM токенизаторлары көбінесе ықтималдыққа негізделеді. Екіншіден, LLM токенизаторлары токендерді сандық мәндерге түрлендіретін қосымша қадамды орындайды.

Ережеге негізделген бағдарламалар

Ережеге негізделген бағдарлама, лексикалық токендеуді орындайтын, токендеуші немесе сканер деп аталады, бірақ сканер лексикалық талдаудың алғашқы кезеңі үшін де қолданылады. Лексер компилятордың алдыңғы бөлігіндегі өңдеудің бірінші фазасын құрайды. Талдау көбінесе бір рет жүзеге асырылады. Лексерлер мен синтаксистік талдағыштар (пазерлер) көбінесе компиляторлар үшін қолданылады, бірақ әдемі басып шығару құралдары (prettyprinters) немесе линтерлер сияқты басқа компьютерлік тіл құралдары үшін де қолданылуы мүмкін. Лексикалық талдауды екі кезеңге бөлуге болады: сканерлеу, ол кіріс жолын лексемалар деп аталатын синтаксистік бірліктерге бөліп, оларды токен түрлеріне жіктейді; және бағалау, ол лексемаларды өңделген мәндерге түрлендіреді. Лексерлер әдетте өте қарапайым болады, көп бөлігі күрделілік синтаксистік немесе семантикалық талдау фазаларына ауыстырылады және көбінесе лексер генераторы арқылы жасалуы мүмкін, атап айтқанда lex немесе оның туындылары. Дегенмен, лексорлар кейде кіріс деректерін жеңілдету және синтаксистік талдағыштың жұмысын жеңілдету үшін сөз тіркесі құрылымдарын өңдеу сияқты кейбір күрделіліктерді қамтуы мүмкін, сондай-ақ олар қолдау үшін қосымша мүмкіндіктерді немесе өнімділікті арттыру үшін жартылай немесе толығымен қолмен жазылуы мүмкін.

"lexeme" сөзінің мағынасын түсіндіру

Ережеге негізделген табиғи тілдік өңдеуде "лексема" деп аталатын нәрсе лингвистикадағы "лексема" деп аталатын нәрсеге сәйкес келмейді. Ережеге негізделген табиғи тілдік өңдеуде "лексема" деп аталатын нәрсе тек ағылшын тілі сияқты аналитикалық тілдерде ғана лингвистикалық эквивалентке тең болуы мүмкін, бірақ ағылшын тілінен мүлдем өзгеше, жоғары синтетикалық тілдерде, мысалы, біріктіру тілдерінде емес. Ережеге негізделген табиғи тілдік өңдеуде лексема деп аталатын нәрсе лингвистикадағы "сөз" деп аталатын нәрсеге көбірек ұқсайды (компьютерлік архитектурадағы "сөз" ұғымымен шатастырмау керек), бірақ кейбір жағдайларда "морфема" деп аталатын нәрсеге де ұқсауы мүмкін.

Лексикалық грамматика

Бағдарламалау тілінің сипаттамасы көбінесе лексикалық синтаксисті анықтайтын лексикалық грамматика деп аталатын ережелер жиынтығын қамтиды. Лексикалық синтаксис әдетте реттелген тіл болып табылады, грамматикалық ережелер реттелген өрнектерден тұрады; олар токеннің мүмкін таңбалар тізбегінің (лексемалардың) жиынтығын анықтайды. Лексер жолдарды таниды, және табылған әр түрлі жол үшін лексикалық бағдарлама әрекет етеді, ең қарапайымында токен жасайды. Екі маңызды жалпы лексикалық категория – бос орын және түсініктемелер. Бұлар да грамматикада анықталады және лексормен өңделеді, бірақ жойылуы мүмкін (ешқандай токен жасамастан) және маңызды емес деп есептеледі, көбінесе екі токенді бөліп тұрады (мысалы, if x орнына ifx). Бұл ережеден екі маңызды ерекшелік бар. Біріншіден, блоктың енгізілу арқылы белгіленетін тілдерде бастапқы бос орын маңызды, себебі ол блок құрылымын анықтайды және әдетте лексор деңгейінде өңделеді; төмендегі фразалық құрылымды қараңыз. Екіншіден, лексорлардың кейбір қолданылуларында түсініктемелер мен бос орындар сақталуы керек – мысалы, әдемі басып шығару құралына (prettyprinter) түсініктемелерді шығару қажет, ал кейбір түзету құралдары бағдарламашыға бастапқы кодты көрсететін хабарламалар жіберуі мүмкін. 1960-шы жылдары, әсіресе ALGOL үшін, бос орын мен түсініктемелер жолдарды қайта құру кезеңінде (компилятордың алдыңғы бөлігінің бастапқы кезеңі) жойылды, бірақ бұл жеке кезең жойылды және қазір олар лексормен өңделеді.

Сканнер

Бірінші кезең, сканер, әдетте шекті күйдегі машинаға (FSM) негізделеді. Ол өзінде өңдейтін белгілердің кез келгенінде кездесуі мүмкін таңбалардың ықтимал тізбектері туралы ақпаратты кодтаған (осы таңбалар тізбектерінің жеке мысалдары лексемалар деп аталады). Мысалы, бүтін сан лексемасы сандық цифрлық таңбалардың кез келген тізбегін қамтуы мүмкін. Көп жағдайда, бірінші бос орынсыз таңбаны келесі белгінің түрін анықтау үшін пайдалануға болады, содан кейін кіріс таңбалары бірінен соң бірі өңделеді, осы белгіге қабылданатын таңбалар жиынтығына жатпайтын таңбаға жеткенше (бұл максималды жұту немесе ең ұзын сәйкес ережесі деп аталады). Кейбір тілдерде лексема жасау ережелері күрделірек болуы мүмкін және бұрын оқылған таңбалар бойынша кері оралуды қажет етуі мүмкін. Мысалы, C тілінде 'L' әрпімен басталатын идентификаторды және кең әріптік жол константасын ажырату үшін бір 'L' таңбасы жеткіліксіз.

Кедергілер

Әдетте лексикалық токендеу сөз деңгейінде жүреді. Дегенмен, кейде "сөз" дегеннің мағынасын анықтау қиынға түседі. Токендеуші көбінесе қарапайым эвристикаға сүйенеді, мысалы:
Токендердің алынған тізіміне пунктуация мен бос орын кіруі немесе кірмеуі мүмкін. Барлық тізбектелген әріптер бір токенді құрайды; сандар да солай. Токендер бос орын сияқты (пробел немесе жолдың үзілісі) немесе пунктуациялық белгілермен бөлінеді. Сөздер арасында бос орын қолданылатын тілдерде (мысалы, латын әліпбиін және көптеген бағдарламалау тілдерін пайдаланатын тілдерде) бұл тәсіл салыстырмалы түрде қарапайым. Алайда, тіпті мұндай жағдайларда да қысқартулар, дефиспен байланысқан сөздер, эмодзилер және URI сияқты күрделі конструкциялар (кейбір жағдайларда бір токен ретінде қарастырылуы мүмкін) сияқты көптеген ерекше жағдайлар кездеседі. Классикалық мысал – "New York based", мұнда қарапайым токендеуші оны пробел бойынша бөліп жіберуі мүмкін, бірақ дұрыс бөлу (әлдебір дәрежеде) дефис бойынша болуы керек. Токендеу, әсіресе, ежегі грек, қытай немесе тай тілдері сияқты сөздердің шекаралары жоқ scriptio continua түрінде жазылған тілдер үшін қиын. Агглютинативті тілдер, мысалы корей тілі де токендеу тапсырмасын күрделендіреді. Осындай қиындықтарды шешу үшін күрделі эвристиканы жасақтау, жиі кездесетін ерекше жағдайлардың тізімін пайдалану немесе токендерді кейінірек өңдеу кезеңінде коллокацияларды анықтайтын тілдік модельге бейімдеу сияқты әдістер қолданылады.

Лексер генераторы

Лексерлер көбінесе лексерлік генератор арқылы жасалады, бұл парсер генераторларына ұқсас, және мұндай құралдар көбінесе жиынтықта келеді. Ең танымалдары – yacc парсер генераторымен жұптастырылған lex, немесе олардың көптеген қайта іске асырылымдары, мысалы flex (көбінесе GNU Bison-мен біріктіріледі). Бұл генераторлар доменге тән тілдің бір түрі болып табылады, олар лексикалық сипаттаманы – әдетте, белгілі бір белгілеулермен тұрақты өрнектерді – қабылдап, лексерді шығарады. Бұл құралдар өте жылдам дамуға мүмкіндік береді, бұл бастапқы кезеңде өте маңызды, әрі жұмыс істейтін лексерді алу үшін де, тілдің сипаттамасы жиі өзгергенде де тиімді. Сонымен қатар, олар көбінесе алдын ала және кейіннен тексерулер сияқты, қолмен бағдарламалауға қиын болатын қосымша мүмкіндіктерді ұсынады. Дегенмен, автоматты түрде жасалған лексер икемділікten кем болуы мүмкін, сондықтан кейбір қолмен түзетулер немесе толығымен қолмен жазылған лексер қажет болуы мүмкін. Лексердің өнімділігі маңызды мәселе, сондықтан оңтайландыруға көңіл бөлу керек, әсіресе тұрақты тілдерде, онда лексер жиі орындалады (мысалы, C немесе HTML). lex/flex арқылы жасалған лексерлер жеткілікті түрде жылдам, бірақ жақсырақ генераторларды қолдану арқылы екі-үш есе жақсартуға болады. Кейде қолмен жазылған лексерлер қолданылады, бірақ қазіргі заманғы лексер генераторлары қолмен кодталғандарға қарағанда жылдам лексерлерді шығарады. lex/flex генераторлар тобы кестелік тәсілді қолданады, бұл тікелей кодталған тәсілге қарағанда тиімсіз. Соңғы тәсілде генератор тікелей goto операторлары арқылы келесі күйлерге өтетін механизмді жасайды. re2c сияқты құралдар flex арқылы жасалған механизмдерге қарағанда екі-үш есе жылдам механизмдерді жасауға қабілетті. Жалпы алғанда, осы соңғы құралдар жасаған механизмдерден жақсы жұмыс істейтін анализаторларды қолмен жазу қиын.

Сөз тіркесі

Лексикалық талдау негізінен кіріс таңбалар ағынын белгілерге бөледі, таңбаларды қарапайым топтастырып, санатқа жіктейді. Дегенмен, лексикалық талдау әлдеқайда күрделі болуы мүмкін; ең жай жағдайда, лексикалық анализатор белгілерді жоюы немесе қосымша белгілерді енгізуі мүмкін. Белгілерді, әсіресе бос орындар мен түсініктемелерді, компиляторға қажет болмаған жағдайда, жою жиі кездеседі. Кемрақ жағдайда, қосымша белгілер енгізілуі мүмкін. Бұл, негізінен, белгілерді операторларға немесе операторларды блоктарға топтастыру үшін жасалады, соның арқасында синтаксистік талдау жеңілдеді.

Жолдың жалғасы

Сызық жалғасы – кейбір тілдерде кездесетін мүмкіндік, онда жаңа жол әдетте операторды аяқтаушы болып саналады. Көбінесе, жолды кері қисық сызықпен (тура сол ішінде жаңа жолмен бірге) аяқтау жолдың жалғасуына себеп болады – келесі жол алдыңғы жолға біріктіріледі. Бұл әдетте лексикалық анализаторда (lexer) жасалады: кері қисық сызық пен жаңа жол жойылмайды, керісінше, жаңа жол жекелеген элемент ретінде қарастырылмайды. Мысалдарға bash, басқа да shell сценарийлері және Python жатады.

Үш нүктелі нүктелер

Көптеген тілдерде нүкте-үтір сөйлемді аяқтаушы ретінде қолданылады. Көбінесе бұл міндетті, бірақ кейбір тілдерде нүкте-үтір көп жағдайда қосымша болып табылады. Бұл негізінен лексикалық талдаушы (лексер) деңгейінде жасалады, онда лексикалық талдаушы кіріс символдары ағынында болмаса да, белгілер ағынына нүкте-үтір шығарады, бұл нүкте-үтір енгізу немесе автоматты нүкте-үтір енгізу деп аталады. Мұндай жағдайларда нүкте-үтірлер тілдің формальды сөз тіркесі грамматикасының бөлігі болып табылады, бірақ кіріс мәтінінде кездеспеуі мүмкін, себебі оларды лексикалық талдаушы енгізе алады. Қосымша нүкте-үтірлер немесе басқа да аяқтағыштар немесе бөлігіштер кейде, әсіресе соңғы үтірлер немесе нүкте-үтірлер жағдайында, синтаксистік талдаушы (парсер) деңгейінде өңделеді. Нүкте-үтір енгізу BCPL және оның алыс туысы Go тілінің ерекшелігі болып табылады, бірақ ол B немесе C тілінде жоқ. Нүкте-үтір енгізу JavaScript тілінде де бар, бірақ ережелер біршама күрделі және көп сынға ұшырайды; қателерден сақтану үшін кейбіреулер нүкте-үтірді әрқашан қолдануды ұсынады, ал басқалары күмәнді мәлімдемелердің басында "қорғаныс нүкте-үтірлері" деп аталатын бастапқы нүкте-үтірлерді қолданады. Нүкте-үтір енгізу (нүкте-үтірмен аяқталатын сөйлемдерде) және жол жалғастыру (жаңа жолмен аяқталатын сөйлемдерде) бір-бірін толықтыратын сияқты: нүкте-үтір енгізу белгіні қосады, тіпті жаңа жолдар әдетте белгіні тудырмаса, ал жол жалғастыру белгіні тудырмайды, тіпті жаңа жолдар әдетте белгіні тудырса да.

Офф-сайд ережесі

Off side ережесі (ішкі жиектермен анықталатын блоктар) Python сияқты, лексерде іске асырылуы мүмкін, онда ішкі жиектің артуы лексерден INDENT токенін шығарады, ал ішкі жиектің кемуі лексерден бір немесе бірнеше DEDENT токенін шығарады. Бұл токендер жақшаларды пайдаланатын тілдердегі ашық жақша { және жабық жақша } сияқты болып табылады, және фразалық грамматика жақшалар немесе ішкі жиектер қолданылса, одан тәуелді емес екенін білдіреді. Бұл лексердің күйді сақтауын қажет етеді, атап айтқанда, ішкі жиек деңгейлерінің стегін, соның салдарынан, ішкі жиектер өзгеретін кезде өзгерістерді анықтауға болады, демек, лексикалық грамматика контекстсіз емес: INDENT–DEDENT бұрынғы ішкі жиек деңгейлерінің контексттік ақпаратына тәуелді.

Контекстілік лексика

Жалпы лексикалық грамматика контекстсіз немесе шамамен солай болады, сондықтан артқа немесе алға қарауға немесе қайта жол салуға қажеттілік туындамайды, бұл қарапайым, таза және тиімді іске асыруға мүмкіндік береді. Бұл сонымен қатар лексерден парсерге бір жақты байланыс орнатуға мүмкіндік береді, лексерге қайта ақпарат жіберудің қажеті болмайды. Дегенмен, кейбір ерекшеліктер бар. Мысалдардың ішінде: Go тіліндегі нүктелі үтірлерді қою, ол бір таңбаға кері қарауды талап етеді; Python тіліндегі тізбектес жолдарды біріктіру, ол жолдарды буферде сақтауды талап етеді (келесі таңба да жол екенін анықтау үшін, оны шығарудан бұрын); және Python тіліндегі кіріспе ережесі, ол кіріспе деңгейінің санын (немесе әрбір кіріспе деңгейі үшін стек) сақтауды талап етеді. Бұл мысалдардың барлығы тек лексикалық контекстті қажет етеді, және олар лексердің жұмысын біршама қиындатса да, парсер мен келесі кезеңдерге көрінбейді. Күрделі мысал – C тіліндегі лексерлік түзету, онда таңбалар тізбегінің токен класын семантикалық талдау кезеңіне дейін анықтау мүмкін емес, себебі типтердің және айнымалылардың атаулары лексикалық тұрғыдан бірдей, бірақ әртүрлі токен кластарын құрайды. Сондықтан, түзету кезінде лексер семантикалық талдаушыны (мысалы, символдар кестесін) шақырып, тізбекке тип аты қажет пе екенін тексереді. Бұл жағдайда ақпарат тек парсерден ғана емес, сонымен қатар семантикалық талдаушыдан лексерге де жіберілуі керек, бұл дизайнды күрделендіреді.