Кіріспе

Компьютерлік бағдарламалауда бір реттік өтетін компилятор — әрбір компиляциялық бөліктің элементтерін бір ғана рет қарап өтіп, әрбір элементті тікелей түпкілікті машиналық кодқа аударатын компилятор. Бұл, бағдарламаны бастапқы кодтан машиналық кодқа дейінгі аралық кезеңдерде бір немесе бірнеше аралық түрлерге түрлендіретін және әрбір кезеңде компиляциялық бөліктің барлығын қайта өңдейтін көп реттік өтетін компилятордан өзгеше. Бұл компилятордың логикалық жұмыс істеу қағидасын көрсетеді, бастапқы файлды бір рет оқуды емес. Мысалы, бастапқы файл бір рет уақытша жадқа оқылып, содан кейін сол көшірмесі бірнеше рет қаралуы мүмкін. IBM 1130 Fortran компиляторы бастапқы кодты жадында сақтап, көп рет өңдеуді қолданды; ал диск жеке жады болмаған жүйелерде ассемблерге бастапқы кодты карта оқырманға/перфораторға екі рет беру қажет болды.

Қасиеттері

Бір өтетін компиляторлар көп өтетін компиляторлардан кішідірек және жылдамырақ. Бір өтетін компиляторлар қолда бар ақпараттың шектеулі көлеміне байланысты көп өтетін компиляторлар сияқты тиімді бағдарламаларды жасауға қабілетсіз. Көптеген тиімді компиляторлық оңтайландырулар негізгі блок, цикл (әсіресе ішкі циклдар), кіші бағдарлама немесе бүкіл модуль бойынша бірнеше рет өтуді қажет етеді. Кейбіреулері бүкіл бағдарлама бойынша өтуді талап етеді. Кейбір бағдарламалау тілдерін олардың құрылымына байланысты бір рет өтетін компилятормен құрастыру мүмкін емес. Мысалы, PL/I бағдарламаның кез келген жерінде, тіпті әлі жарияланбаған элементтерге сілтеме жасалғаннан кейін деректерді жариялауға рұқсат береді, сондықтан бүкіл бағдарлама тексерілгенше код жасау мүмкін емес. Тілдің анықтамасы құрастырылатын бастапқы кодты жасайтын алдын ала процессор операторларын да қамтиды: бірнеше өтулер міндетті. Керісінше, көптеген бағдарламалау тілдері бір өтетін компиляторлармен құрастыру үшін арнайы жасалған және бір өтетін компиляцияға мүмкіндік беретін арнайы құрылымдарды қамтиды.

Қиындықтар

Негізгі мәселе – алға сілтемелер жасау. Көздік файлдың бір бөлігіндегі символдың дұрыс түсіндірілуі, файлда одан кейін келетін басқа символдардың болуына немесе болмауына байланысты болуы мүмкін. Олар кездеспейінше, ағымдағы символ үшін дұрыс код жасалмайды. Бұл контекстке тәуелділік мәселесі, ал оның ауқымы жақын орналасқан символдардан бастап, көздік мәтіннің кез келген үлкен көлеміне дейін жете алады.

Жергілікті жағдай

Болжам жасайық, < символы "кемде" салыстыру үшін танылады, мысалы, "көпте" салыстыруға қарама-қарсы. Таңба кодтау шектеулеріне байланысты, ≤ глифі стандартты кодтауда қолжетімді болмауы мүмкін, сондықтан "<=" құрамалы бейнелеуге рұқсат етіледі. Бұл контекст келесі символмен анықталғанымен, "<" кездескен кезде ол белгісіз болады. Сол сияқты, "=" символы әрқашан "=" дегенді білдірмейді, мысалы, ол құрамалы символдың бөлігі болғанда. Басқа құрамалы символдарға "<" арнайы символы қолжетімді болмаған жағдайда ".lt." кіруі мүмкін. Глиф ¬ ("жоқ") таңбасының коды қолжетімсіз болған жағдайда, "<>" "¬=" немесе "тең емес" үшін тағы бір мүмкіндік бар. Кейбір жүйелер ¬ үшін ~ немесе ! символдарын да қолданады. Бір тәсіл – "<" символынан кейін сканерлеуді алға жылжыту, ал "=" символына тап болғанда кері қайту. Бұл, әрине, мәтіннің осы бөлігін екі рет қарауды білдіреді, одан аулақ болу керек. Мұндай жағдайда, бастапқы файл карта оқу құрылғысы сияқты кері оқуды қолдамайтын құрылғыдан алынуы мүмкін. Ертерек шешім қабылдаудың орнына, кейіннен жойылған болуы мүмкін, лексикалық талдаушы кванттық суперпозиция ұғымына ұқсас бірнеше интерпретацияларды сақтай алады, содан кейін анықтайтын символ байқалғанда ғана нақты таңдауға келеді. Мысалы, COBOL компиляторлары ондық тұрақтылардағы және жазбалардың соңындағы нүктелерді ажырату үшін бір өтеді. Мұндай схема бір өтімді компиляторда қолжетімді емес. Атаулар да осыған ұқсас. Көптеген тілдер өздерін бір таңбалы атаулармен шектемейді, сондықтан "x" таңбасы бір таңбалы атау ретінде "текст" сияқты атаудағы "x" таңбасынан өте өзгеше, енді контекст тікелей жапсарлас таңбалардан асып түседі. Лексикалық талдаушының міндеті – тілдің белгілеріне бірізділікпен берілген бастапқы ағынның элементтерін бөлу. Бұл тек сөздер ғана емес, өйткені "<" және "<=" де белгілер болып табылады. Атаулар әдетте әріппен басталады және әріптермен, сандармен және " " сияқты бірнеше қосымша символдармен жалғасады. Сандарды көрсетуге рұқсат етілген синтаксис таңқаларлықтай күрделі, мысалы, +3.14159E+0 жарамды болуы мүмкін. Токендер арасында кез келген сандағы бос орынға рұқсат ету әдеттегідей, ал Фортран белгілі бір токендер ішіндегі бос орындарға рұқсат беруде (және елемеуде) ерекше, сондықтан "GO TO" және "GOTO" "<=" және "< =" сияқты эквивалентті. Алайда, кейбір жүйелер белгілі бір токендерді шектеу үшін бос орындарды талап етуі мүмкін, ал басқалары, мысалы Python, бағдарлама блоктарының аясын көрсету үшін жетекші бос орындарды қолданады, олар әдетте Бастау және Аяқтау немесе ұқсас маркерлермен көрсетіледі.

Өкінішті шешімдер

Жоғарыда сипатталғандай, кейбір өрістері кейінірек түзетілуі мүмкін екендігі ескеріліп код жасалғанмен, мұндай код тізбектерінің көлемі тұрақты деген тұжырым жасалды. Бірақ бұл әрқашан дұрыс болмайды. Көптеген компьютерлерде әртүрлі көлемде жадты пайдаланатын операциялар қарастырылған, әсіресе салыстырмалы адрестеу. Мысалы, егер мақсат 128 немесе +127 адрестеу қадамының ішінде болса, сегіз биттік адрестік өріс қолданылуы мүмкін, әйтпесе, оған жету үшін әлдеқайда үлкен адрестік өріс қажет болады. Осылайша, егер код қысқа адрестік өріспен жасалса, кейіннен оны қайта қарап, ұзын өріс қолдану үшін түзету қажет болуы мүмкін. Нәтижесінде, өзгертілгеннен кейін бұрынғы кодтағы сілтемелерді де түзету қажет болады. Сол сияқты, өзгеріске қатысты кері сілтемелерді де, тіпті белгілі адрестерге жасалған сілтемелерді де түзету керек. Сонымен қатар, түзету туралы ақпараттың өзі дұрыс түзетілуі тиіс. Басқа жағынан, жақындық нақты болмаған жағдайларда ұзын адрестерді пайдалануға болады, бірақ мұндай код енді тиімді болмайды.

Бір рет өткізілетін ретті кіріс, реттіліксіз шығыс

Бір ғана мәлімдемедегі оптимизациялау мүмкіндіктерінің кейбірі айтылды. Бірнеше мәлімдемелер бойынша оптимизациялау үшін, мұндай мәлімдемелердің мазмұны кодты шығару алдында талданып, өңделуі мүмкін дерек құрылымында сақталуын қажет етеді. Мұндай жағдайда, түзетулерге рұқсат етілгенімен, уақытша кодты жасау кедергі келтіреді. Шегінде, компилятор бүкіл бағдарламаны ішкі форматта бейнелейтін дерек құрылымын жасайды, бірақ бастапқы файлды басынан соңына дейін толық қараудың қажеті жоқ деген пікірді айтуға болады. Бұл мүмкіндік компиляторды жариялайтын PR құжатында көрсетілуі мүмкін. Сондықтан, компилятор өзінің кодын бір қадаммен, тіпті бастапқы кодтың әр бөлігі оқылған бойда жасауға қабілетсіз. Шығыс деректер тізбектей жазылуы мүмкін, бірақ тек егер бөлімнің шығысы сол бөлімге қатысты барлық күтіліп тұрған түзетулер аяқталғаннан кейін ғана шығарылса.

Рәсімдер мен функциялар

Пайдалану алдындағы декларация процедуралар мен функцияларды орындау үшін де оңай талап болып табылады, бұл процедуралар ішінде процедураларды орналастыруға да қатысты. ALGOL, Pascal, PL/I және басқалары сияқты, MATLAB және (1995 жылдан бастап) Fortran функцияға (немесе процедураға) басқа функцияның (немесе процедураның) анықтамасын қамтуға мүмкіндік береді, бірақ бұл жүйелер оларды қамтитын функция ішінде ғана көрінетін, бірақ оларды қамтитын процедура аяқталғаннан кейін анықтауға болады. Бірақ рекурсияға рұқсат берілгенде, мәселе туындайды. Әрқайсысы екіншісін шақыратын екі процедураны пайдалану алдында екеуі де жариялауға болмайды. Біріншіден, бастапқы файлда бірінші болу керек. Бұл маңызды емес, егер, белгісіз айнымалымен кездескен кезде, кездескеннен жеткілікті түрде компилятор белгісіз процедураны шақыру үшін қолайлы кодты шығара алса, әрине, "жөндеу" аппаратымен, процедураның анықтамасы кездескен кезде қайта оралып, дұрыс мекенжайды толтыру үшін. Бұл, мысалы, параметрлері жоқ процедура үшін орын алады. Функцияны шақырудан қайтарылған нәтиже шақырудан ажыратылатын типте болуы мүмкін, бірақ бұл әрқашан дұрыс болмауы мүмкін: функция жылжымалы нүкте нәтижесін қайтара алады, бірақ оның мәні бүтін санға беріледі. Pascal бұл мәселені "алдын ала жариялауды" талап ету арқылы шешеді. Процедураның немесе функцияның бір декларациясы бірінші берілуі тиіс, бірақ процедураның немесе функцияның денесі орнына "forward" кілт сөзін беру керек. Содан кейін басқа процедура немесе функция жарияланып, оның құрамын анықтауға болады. Кейбір кезде "forward" процедура немесе функция функцияның денесімен бірге қайта жарияланады. Параметрлері бар процедураны (немесе функцияны) шақыру үшін олардың түрі белгілі болады (олар пайдалану алдында жарияланады), бірақ олардың процедураны шақырудағы қолданылуы болмауы мүмкін. Мысалы, Fortran барлық параметрлерді сілтеме арқылы (яғни мекенжай бойынша) береді, сондықтан кодты жасаудың бірден қиындығы жоқ (әрқашанғыдай, нақты мекенжайлар кейіннен түзетіледі), бірақ Pascal және басқа тілдер бағдарламашының таңдауы бойынша параметрлерді әр түрлі әдістермен беруге мүмкіндік береді (сілтеме арқылы, немесе мәні бойынша, тіпті "атымен") және бұл тек процедураның анықтамасымен белгіленеді, ол анықтаманы кездестіргенге дейін белгісіз. Паскаль үшін, параметрлер спецификациясында "Var" префиксі оны сілтеме арқылы қабылдауға тиіс екенін білдіреді, оның болмауы мәні арқылы білдіреді. Бірінші жағдайда компилятор параметрдің мекенжайын беретін кодты, ал екіншісінде әртүрлі кодты, әдетте стек арқылы, мәннің көшірмесін беретін кодты құруы керек. Әдеттегідей, "жөндеу" механизмін қолдануға болады, бірақ ол өте күрделі болады. Көп жолды компиляторлар, әрине, барлық қажетті ақпаратты алға-артқа ауыстыра алады, бірақ бір жолды компиляторлар жасай алмайды. Кодты жасауды сканерлеу алға жылжу кезінде тоқтату мүмкін (және оның нәтижелері ішкі сақтауда сақталады) қажетті нысан кездескенше, және бұл кодты жасау сатысы жақын арада жететіндіктен, бастапқыдан екінші рет өту деп қарастырылмайды, ол тек біраз уақыт тоқтап тұрды. Бірақ бұл күрделі мәселе. Оның орнына арнайы конструкция енгізіледі, ол арқылы параметрді пайдаланудың процедураның анықтамасы оның кейінгі толық анықтамасынан "алдыңғы" деп жарияланады, сондықтан компилятор оны пайдаланудан бұрын, қажет болған жағдайда, білуі мүмкін. Бірінші Fortranнан (1957) бастап бағдарламаның бөліктерін бөлек құрастыру мүмкін болды, бұл процедуралар мен функциялардың кітапханаларын құруды қолдайды. Компиляцияланатын бастапқы файлдағы функцияларды шақыратын процедураға белгілі емес функция қайтарған нәтиже түрін білуі керек, егер тек нәтижеді табу үшін дұрыс орынға қарайтын кодты жасау үшін. Бастапқыда, тек бүтін сандар мен жылжымалы нүктелі айнымалылар болған кезде, таңдау жасырын декларациялау ережелеріне қалдырылуы мүмкін еді, бірақ өлшемдер мен типтердің көбеюімен шақыру процедурасына функция үшін типтік декларация қажет болады. Бұл ерекше емес, процедураның ішінде мәлімделген айнымалыға ұқсас нысанда. Бір жолды құрастырудың қазіргі кезеңінде бір нысан туралы ақпарат қажет, сондықтан ол үшін дұрыс кодты қазір, егер кейіннен адресді түзетумен жасалса, қажет. Қажетті ақпарат бастапқы файлда немесе жеке жинақталған код файлында кездеседі ме, жоқ па, ақпаратты қандай да бір протокол ұсынады. Процедураның (немесе функцияның) барлық шақырулары бір-бірімен үйлесімділік үшін тексеріледі ме, жоқ па, бұл бөлек мәселе. Algol-дан шыққан тілдерде бұл тексеру әдетте қатаң болады, бірақ басқа жүйелер бейжай болуы мүмкін. Параметрлердің саны мен түріндегі қателер әдетте бағдарламаны құлатуға әкеледі, опциялық параметрлері бар процедураларды есепке алмайынша. Бағдарламаның бөліктерін бөлек құрастыруға рұқсат беретін және кейін "байланыстырылатын" жүйелер параметрлер мен нәтижелердің дұрыс түрін және санын да тексеруі керек, өйткені қателер жасау оңайрақ, бірақ көбінесе жасалмайды. Кейбір тілдер (мысалы, Algol) "жаңарту" немесе "кеңейту" немесе "көтеру" деген формальды ұғымға ие, онда процедура, мысалы, қос нақтылық параметрін күтетін болса, оны бір нақтылық айнымалысымен шақырылуы мүмкін, және бұл жағдайда компилятор бір нақтылық айнымалысын уақытша қос нақтылық айнымалысына сақтайтын кодты жасайды, ол нақты параметр болады. Дегенмен, бұл параметрді беру механизмін көшіруге және шығаруға өзгертеді, бұл нәтижесінде мінез-құлқының айқын айырмашылықтарына әкелуі мүмкін. Тәжірибесіздікке айналатын нәрсе - процедура бір нақтылық айнымалысының мекенжайын алатын кезде, ол қос нақтылық параметрін күтеді, немесе басқа өлшемдердегі өзгерістер. Процедураның ішінде параметрдің мәні оқылғанда, оның берілген параметрінен гөрі көбірек сақтау оқылады және нәтижесіндегі мән жақсартуға ұқсамайды. Жағдай одан да нашар, егер процедура өзінің параметрінің мәнін өзгертетін болса: бірдеңе зардап шегеді. Бұл қателерді табу және түзету үшін көп шыдамдылық жұмсалуы мүмкін.

Алдын ала өңдеуші рекурсиясы

Күрделі деректер жиынтығын жариялағанда, Odd және Even функцияларын қолдануға мүмкіндік тууы мүмкін. Мысалы, егер деректер жиынтығы X-тің сақталу көлемі байттардың тақ саны болса, Odd(ByteSize(X)) тестінің бақылауымен оған бір байттық элемент қосылып, осылайша жұп санды жасауға болады. Жоғарыда келтірілгендей Odd және Even функцияларының эквивалентті жариялануын ескере отырып, «алдын ала» жариялау қажет болмауы мүмкін, себебі параметрлердің қолданылуы алдын ала процессорға белгілі, ол деректерді сілтеме арқылы немесе мән арқылы берудің мүмкіндігін қарастырмауы тиіс. Дегенмен, осы функцияларды олардың анықтамасынан тыс, бастапқы кодта олардың нақты анықтамасынан кейін ғана шақыруға болады, өйткені шақырудың нәтижесі белгілі болуы керек. Әрине, егер алдын ала процессор бастапқы файлды бірнеше рет өңдесе, бұл басқаша болуы мүмкін.

Залал келтіретін деп саналатын алдын ала декларациялар

Кез келген адам, үлкен бағдарламадағы процедуралардың жарияланымдары мен қолданылуының, сондай-ақ оның процедуралар жинағы кітапханаларын пайдалануы арасындағы үйлесімділікті сақтауға тырысқан болса, әсіресе өзгерістерге ұшыраған жағдайда, шақырылған бірақ ағымдағы жинақтауда анықталмаған процедуралар үшін алдын ала жарияланымдарды немесе осыған ұқсас қосымша жарияланымдарды қолдануда қиындыққа тап болар. Әсіресе, әр түрлі бастапқы файлдардағы қашық жерлер арасындағы синхрондылықты сақтау үшін ұқыптылық қажет. Белгілі бір сөзді қолданатын жарияланымдарды табу оңай, бірақ егер пайдалы жарияланымдар қарапайым жарияланымдардан ажыратылмаса, міндет қиынға соғады. Бір реттік жинақтау мақсатынан бас тарту осы қиындықты жойса, көбінесе жылдам жинақтаудың пайдасы жеткіліксіз болып көрінеді.