Un traducător este... Tipuri de traducători. Conversia și difuzarea programului. Dacă setul de stări rezultat nu conține conflicte LR(1) și, prin urmare, permite construirea unui parser LR(1), atunci se spune că gramatica are proprietatea LALR(1). Scop

Traducător (traducător englez - traducător) este un program de traducere. Convertește un program scris într-una dintre limbile de nivel înalt într-un program format din instrucțiuni de mașină. De obicei, traducătorul diagnosticează și erorile, creează dicționare de identificatori, produce texte de program pentru tipărire etc. Limba în care este prezentat programul de intrare se numește limba sursă, iar programul în sine se numește cod sursă. Limba de ieșire se numește limba țintă sau cod obiect.

În general, conceptul de traducere se aplică nu numai limbajelor de programare, ci și altor limbi - atât limbaje formale de computer (cum ar fi limbaje de marcare precum HTML), cât și cele naturale (rusă, engleză etc.).

Tipuri de traducători

    Dialog. Oferă utilizarea unui limbaj de programare în modul de partajare a timpului.

    Orientat sintactic (condus sintactic). Primește ca intrare o descriere a sintaxei și semanticii limbii și a textului în limba descrisă, care este tradusă în conformitate cu descrierea dată.

    O singură trecere. Formează un modul obiect într-o vizualizare secvențială a programului sursă.

    Multi-pass. Formează un modul obiect peste mai multe vederi ale programului sursă.

    Optimizarea. Efectuează optimizarea codului în modulul obiect generat.

    Test. Un set de comenzi macro în limbaj de asamblare care vă permit să setați diverse proceduri de depanare în programele scrise în limbaj de asamblare.

    Înapoi. Pentru un program în cod mașină, acesta produce un program echivalent în orice limbaj de programare (vezi: dezasamblator, decompilator).

Traducătorii sunt implementați ca compilatori sau interpreți. În ceea ce privește realizarea muncii, compilatorul și interpretul sunt semnificativ diferite.

Compilator (Compilatorul englez - compilator, colector) citește întregul program, îl traduce și creează o versiune completă a programului în limbajul mașinii, care este apoi executată. Informațiile de intrare către compilator (codul sursă) sunt o descriere a algoritmului sau programului într-un limbaj orientat spre probleme, iar rezultatul compilatorului este o descriere echivalentă a algoritmului într-un limbaj orientat către mașină (cod obiect).

Tipuri de compilatoare

    Vectorizarea. Traduce codul sursă în cod mașină pe computerele echipate cu un procesor vectorial.

    Flexibil. Proiectat într-o manieră modulară, condus de tabele și programat într-un limbaj de nivel înalt sau implementat folosind un compilator de compilatoare.

    Dialog. Vezi: traducător de dialog.

    incremental. Retransmite fragmente de program și completări la acesta fără a recompila întregul program.

    Interpretativ (pas cu pas). Efectuează secvenţial o compilare independentă a fiecărei instrucţiuni individuale (comandă) a programului sursă.

    Compilator de compilatoare. Un traducător care acceptă o descriere formală a unui limbaj de programare și generează un compilator pentru acest limbaj.

    Depanați. Elimină anumite tipuri de erori de sintaxă.

    Rezident. Acesta rezidă permanent în RAM și este disponibil pentru reutilizare de multe sarcini.

    Autocompilare. Scris în aceeași limbă din care se realizează emisiunea.

    Universal. Bazat pe o descriere formală a sintaxei și semanticii limbajului de intrare. Componentele unui astfel de compilator sunt: ​​nucleu, încărcătoare sintactice și semantice.

Scopurile si obiectivele disciplinei. Concepte de bază și definiții. Caracteristici generale ale limbajelor de programare și ale traducătorilor. Structura generalizată a traducătorului. Opțiuni pentru interacțiunea blocurilor de traducător.

Scopurile si obiectivele disciplinei

În prezent, limbajele artificiale care folosesc reprezentarea textuală pentru a descrie un domeniu sunt utilizate pe scară largă nu numai în programare, ci și în alte domenii. Cu ajutorul lor, este descrisă structura tuturor tipurilor de documente, lumi virtuale tridimensionale, interfețe grafice cu utilizatorul și multe alte obiecte utilizate în modele și în lumea reală. Pentru ca aceste descrieri de text să fie corect compuse, iar apoi corect recunoscute și interpretate, se folosesc metode speciale de analiză și transformare a acestora. Metodele se bazează pe teoria limbilor și a gramaticilor formale, precum și pe teoria automatelor. Sistemele software concepute pentru a analiza și interpreta textele se numesc traducători.

În ciuda faptului că mii de limbi diferite și traducătorii acestora au fost dezvoltate până în prezent, procesul de creare a unor noi aplicații în acest domeniu nu se oprește. Acest lucru se datorează atât dezvoltării tehnologiei de producție a sistemelor informatice, cât și necesității de a rezolva probleme aplicate din ce în ce mai complexe. În plus, elementele teoriei limbilor și ale gramaticilor formale sunt aplicabile în alte domenii diverse, de exemplu, atunci când se descriu structuri de date, fișiere, imagini, prezentate nu în text, ci în format binar. Aceste metode sunt utile și atunci când vă dezvoltați proprii traducători, chiar și acolo unde există deja analogi corespunzători. O astfel de dezvoltare se poate datora diverselor motive, în special limitărilor funcționale, lipsei de localizare și eficiența scăzută. De exemplu, una dintre cele mai recente dezvoltări ale Microsoft este limbajul de programare C#, iar unul dintre motivele creării acestuia este dorința de a reduce popularitatea limbajului de programare Java. Există multe alte exemple în care dezvoltarea propriului traducător poate fi relevantă. Prin urmare, bazele teoriei limbilor și ale gramaticilor formale, precum și metodele practice de dezvoltare a traducătorilor, stau la baza educației inginerești în informatică și informatică.

Materialul propus atinge elementele de bază ale metodelor de dezvoltare a traducătorilor și conține informații necesare studierii logicii funcționării acestora și a aparatului matematic utilizat (teoria limbajelor formale și a gramaticilor formale, metalimbaje). Este folosit ca parte a cursurilor de curs semestriale susținute pentru diferite specialități la Facultatea de Informatică și Informatică a Universității Tehnice de Stat din Krasnoyarsk. În timpul lucrului de laborator, se realizează o cunoaștere directă cu metodele individuale de creare a traducătorilor.

Scopul disciplinei: de a oferi cunoștințe despre bazele teoriei limbilor și ale gramaticilor formale, teoria automatelor, metode de dezvoltare a traducătorilor.

Pentru a atinge acest obiectiv, în timpul predării disciplinei sunt rezolvate următoarele sarcini:

  1. Cursul de curs examinează principiile generale de organizare a procesului de traducere și structura traducătorilor. Sunt studiate fundamentele teoriei construirii traducătorilor.
  2. La orele de laborator și în timpul muncii independente, cunoștințele teoretice dobândite sunt practic consolidate: se dezvoltă un traducător pentru un limbaj de programare simplu.

Concepte de bază și definiții

Cele mai multe dintre definițiile luate în considerare sunt împrumutate de la [ARNFTS Dicționar explicativ englez-rus-german-francez privind tehnologia computerelor și prelucrarea datelor, 4132 termeni. Sub. ed. A.A. Dorodnitsyna. M.: 1978. 416 p.) ].

Traducător - un program de serviciu care convertește un program sursă furnizat într-un limbaj de programare de intrare într-un program de lucru furnizat într-un limbaj obiect.

Definiția de mai sus se aplică tuturor tipurilor de programe de difuzare. Cu toate acestea, fiecare dintre aceste programe poate avea propriile caracteristici în organizarea procesului de difuzare. În prezent, traducătorii sunt împărțiți în trei grupuri principale: asamblatori, compilatori și interpreți.

Asamblator - un program utilitar de sistem care convertește constructe simbolice în instrucțiuni în limbajul mașinii. O caracteristică specifică a asamblatorilor este că efectuează o traducere textuală a unei instrucțiuni simbolice într-o instrucțiune de mașină. Astfel, limbajul de asamblare (numit și autocode) este conceput pentru a facilita percepția sistemului de comandă al computerului și pentru a accelera programarea în acest sistem de comandă. Este mult mai ușor pentru un programator să-și amintească denumirea mnemonică a instrucțiunilor mașinii decât codul lor binar. Prin urmare, câștigul principal este obținut nu prin creșterea puterii comenzilor individuale, ci prin creșterea eficienței percepției acestora.

În același timp, limbajul de asamblare, pe lângă analogii comenzilor mașinii, conține multe directive suplimentare care facilitează, în special, gestionarea resurselor computerului, scrierea fragmentelor repetate și construirea de programe cu mai multe module. Prin urmare, expresivitatea limbajului este mult mai bogată decât un simplu limbaj de codare simbolică, ceea ce îmbunătățește foarte mult eficiența programării.

compilator - este un program de serviciu care traduce un program scris în limbajul de programare sursă în limbajul mașinii. La fel ca un asamblator, un compilator convertește un program dintr-o limbă în alta (cel mai adesea, în limba unui anumit computer). În același timp, comenzile în limba sursă diferă semnificativ în organizare și putere de comenzile în limbajul mașinii. Există limbi în care o comandă a limbii sursă este tradusă în 7-10 comenzi de mașină. Cu toate acestea, există și limbi în care fiecare comandă poate avea 100 sau mai multe comenzi de mașină (de exemplu, Prolog). În plus, limbile sursă folosesc destul de des o tastare strictă a datelor, realizată prin descrierea lor preliminară. Programarea se poate baza nu pe codarea unui algoritm, ci pe gândirea atentă la structurile sau clasele de date. Procesul de traducere din astfel de limbi se numește de obicei compilare, iar limbile sursă sunt de obicei clasificate ca limbaje de programare de nivel înalt (sau limbi de nivel înalt). Abstracția unui limbaj de programare din sistemul de comandă al computerului a condus la crearea independentă a unei largi varietati de limbaje axate pe rezolvarea unor probleme specifice. Au apărut limbi pentru calcule științifice, calcule economice, acces la baze de date și altele.

interpret - un program sau un dispozitiv care realizează traducerea operator cu operator și execuția programului original. Spre deosebire de un compilator, un interpret nu produce un program în limbaj mașină ca rezultat. După ce a recunoscut o comandă în limba sursă, o execută imediat. Atât compilatorii, cât și interpreții folosesc aceleași metode pentru analiza codului sursă al unui program. Dar interpretul vă permite să începeți procesarea datelor după ce ați scris chiar și o singură comandă. Acest lucru face ca procesul de dezvoltare și depanare a programelor să fie mai flexibil. În plus, absența codului de ieșire a mașinii face posibilă să nu „aglomerați” dispozitivele externe cu fișiere suplimentare, iar interpretul în sine poate fi adaptat destul de ușor la orice arhitectură de mașină, fiind dezvoltat o singură dată într-un limbaj de programare utilizat pe scară largă. Prin urmare, limbaje interpretate precum Java Script și VB Script au devenit larg răspândite. Dezavantajul interpreților este viteza redusă de execuție a programului. De obicei, programele interpretate rulează de 50 până la 100 de ori mai lent decât programele native.

Emulator - un program sau un instrument software și hardware care oferă capacitatea, fără reprogramare, de a executa pe un computer dat un program care utilizează coduri sau metode de efectuare a operațiunilor care sunt diferite de computerul dat. Un emulator este similar cu un interpret prin faptul că execută direct un program scris într-o anumită limbă. Cu toate acestea, cel mai adesea este limbajul mașinii sau codul intermediar. Ambele reprezintă instrucțiuni în cod binar care pot fi executate imediat după ce codul de operare este recunoscut. Spre deosebire de programele text, nu este nevoie să recunoașteți structura programului sau să selectați operanzi.

Emulatoarele sunt folosite destul de des pentru o varietate de scopuri. De exemplu, atunci când se dezvoltă noi sisteme de calcul, este creat mai întâi un emulator care rulează programe dezvoltate pentru computere care încă nu există. Acest lucru vă permite să evaluați sistemul de comandă și să dezvoltați software-ul de bază chiar înainte ca hardware-ul corespunzător să fie creat.

Foarte des, un emulator este folosit pentru a rula programe vechi pe computere noi. De obicei, calculatoarele mai noi sunt mai rapide și au periferice mai bune. Acest lucru vă permite să emulați programe mai vechi mai eficient decât să le rulați pe computere mai vechi. Un exemplu al acestei abordări este dezvoltarea emulatoarelor pentru computerul de acasă ZX Spectrum cu microprocesorul Z80. Există încă oameni cărora le place să joace pe un emulator cu programe de joc învechite, dar care nu își pierd încă atractivitatea de odinioară. Emulatorul poate fi folosit și ca un analog mai ieftin al sistemelor informatice moderne, oferind în același timp o performanță acceptabilă echivalentă cu modelele low-end ale unei anumite familii de arhitecturi. Un exemplu sunt emulatorii computerelor compatibile IBM PC implementate pe computere Apple mai puternice. O serie de emulatori scrise pentru PC-ul IBM înlocuiesc cu succes diverse console de jocuri.

Emulatorul de reprezentare intermediară, precum și interpretul, pot fi ușor transferate de la o arhitectură de computer la alta, permițând crearea de software mobil. Această proprietate este cea care a predeterminat succesul limbajului de programare Java, din care programul este tradus în cod intermediar. Mașina virtuală Java care execută acest cod nu este altceva decât un emulator care rulează sub orice sistem de operare modern.

Transcoder - un program sau un dispozitiv software care traduce programe scrise în limbajul mașină al unui computer în programe în limbajul mașină al altui computer. Dacă emulatorul este un analog mai puțin inteligent al interpretului, atunci transcoderul acționează în aceeași calitate în raport cu compilatorul. În mod similar, codul mașină sursă (și de obicei binar) sau o reprezentare intermediară este convertit în alt cod similar cu o singură instrucțiune și fără nicio analiză generală a structurii programului sursă. Transcodificatoarele sunt utile atunci când se transferă programe de la o arhitectură de computer la alta. Ele pot fi, de asemenea, utilizate pentru a reconstrui textul programului de limbaj de nivel înalt din codul binar existent.

Macroprocesor - un program care înlocuiește o secvență de caractere cu alta[Maro]. Acesta este un tip de compilator. Acesta generează textul de ieșire prin procesarea inserțiilor speciale situate în textul sursă. Aceste inserții sunt concepute într-un mod special și aparțin unor constructe ale unui limbaj numit macrolimbaj. Macroprocesoarele sunt adesea folosite ca suplimente la limbajele de programare, crescând funcționalitatea sistemelor de programare. Aproape orice asamblator conține un macroprocesor, care crește eficiența dezvoltării programelor de mașină. Astfel de sisteme de programare sunt de obicei numite macroasambleri.

Macroprocesoarele sunt folosite și cu limbaje de nivel înalt. Ele măresc funcționalitatea limbilor precum PL/1, C, C++. Macroprocesoarele sunt utilizate pe scară largă în C și C++, facilitând scrierea programelor. Un exemplu de utilizare pe scară largă a macroprocesoarelor este biblioteca de clase Microsoft Foundation Classes (MFC). Implementează hărți de mesaje și alte obiecte de program prin inserări macro. În același timp, macroprocesoarele cresc eficiența programării fără a modifica sintaxa și semantica limbajului.

Sintaxă - un set de reguli ale unui anumit limbaj care determină formarea elementelor sale. Cu alte cuvinte, asta un set de reguli pentru formarea unor secvențe semnificative din punct de vedere semantic de caractere într-o limbă dată. Sintaxa este specificată folosind reguli care descriu conceptele unui limbaj. Exemple de concepte sunt: ​​variabilă, expresie, operator, procedură. Succesiunea conceptelor și utilizarea lor acceptabilă în reguli determină structurile corecte sintactic care formează programe. Ierarhia obiectelor, și nu modul în care interacționează între ele, este definită prin sintaxă. De exemplu, o instrucțiune poate apărea doar într-o procedură, o expresie într-o instrucțiune, o variabilă poate consta dintr-un nume și indici opționali etc. Sintaxa nu este asociată cu astfel de fenomene în program precum „sărirea la o etichetă inexistentă” sau „o variabilă cu numele dat nu este definită”. Asta face semantica.

Semantică - reguli și condiții care determină relația dintre elementele limbajului și semnificațiile lor semantice, precum și interpretarea sensului semnificativ al construcțiilor sintactice ale limbii. Obiectele unui limbaj de programare nu sunt doar plasate în text în conformitate cu o anumită ierarhie, ci sunt și interconectate suplimentar prin alte concepte care formează diverse asocieri. De exemplu, o variabilă, pentru care sintaxa definește o locație validă doar în declarații și în unele instrucțiuni, are un tip specific, poate fi folosită cu un număr limitat de operații, are o adresă, o dimensiune și trebuie declarată înainte de a putea fi folosit în program.

Analizor sintactic - o componentă a compilatorului care verifică declarațiile sursă pentru conformitatea cu regulile sintactice și semantica unui anumit limbaj de programare. În ciuda numelui său, analizatorul verifică atât sintaxa, cât și semantica. Este format din mai multe blocuri, fiecare dintre ele își rezolvă propriile probleme. Acesta va fi discutat mai detaliat atunci când se descrie structura traducătorului.

Caracteristici generale ale limbajelor de programare și ale traducătorilor

Limbajul de programare este destul de diferit unul de celălalt ca scop, structură, complexitate semantică și metode de implementare. Acest lucru impune propriile caracteristici specifice dezvoltării unor traducători specifici.

Limbajele de programare sunt instrumente pentru rezolvarea problemelor din diferite domenii, ceea ce determină specificul organizării lor și diferențele de scop. Exemplele includ limbaje precum Fortran, care este orientat spre calcule științifice, C, care este destinat programării sistemelor, Prolog, care descrie în mod eficient problemele de inferență și Lisp, care este utilizat pentru procesarea recursivă a listelor. Aceste exemple pot fi continuate. Fiecare disciplină își pune propriile cerințe privind organizarea limbii în sine. Prin urmare, putem observa varietatea formelor de reprezentare a operatorilor și expresiilor, diferența în setul de operații de bază și scăderea eficienței programării la rezolvarea problemelor care nu țin de domeniul de studiu. Diferențele lingvistice se reflectă și în structura traducătorilor. Lisp și Prolog sunt cel mai adesea executate în modul de interpretare datorită faptului că folosesc generarea dinamică de tipuri de date în timpul calculelor. Traducătorii Fortran se caracterizează prin optimizarea agresivă a codului mașină rezultat, ceea ce devine posibil datorită semanticii relativ simple a constructelor de limbaj - în special, datorită absenței mecanismelor de denumire alternativă a variabilelor prin indicatori sau referințe. Prezența pointerilor în limbajul C impune cerințe specifice pentru alocarea dinamică a memoriei.

Structura unei limbi caracterizează relațiile ierarhice dintre conceptele sale, care sunt descrise prin reguli sintactice. Limbajele de programare pot diferi foarte mult unele de altele în organizarea conceptelor individuale și a relațiilor dintre ele. Limbajul de programare PL/1 permite imbricarea arbitrară a procedurilor și funcțiilor, în timp ce în C toate funcțiile trebuie să fie la nivelul de imbricare exterior. Limbajul C++ permite ca variabilele să fie declarate în orice moment al programului înainte de prima utilizare, în timp ce în Pascal variabilele trebuie definite într-o zonă specială de declarare. Luând acest lucru și mai departe este PL/1, care permite ca o variabilă să fie declarată după ce a fost utilizată. Sau puteți omite descrierea cu totul și utilizați regulile implicite. În funcție de decizia luată, traducătorul poate analiza programul într-una sau mai multe treceri, ceea ce afectează viteza de traducere.

Semantica limbajelor de programare variază foarte mult. Ele diferă nu numai prin caracteristicile de implementare ale operațiunilor individuale, ci și prin paradigmele de programare, care determină diferențe fundamentale în metodele de dezvoltare a programelor. Specificul implementării operațiunilor poate viza atât structura datelor în curs de prelucrare, cât și regulile de prelucrare a acelorași tipuri de date. Limbi precum PL/1 și APL acceptă operațiuni cu matrice și vectori. Majoritatea limbajelor funcționează în primul rând cu scalari, oferind proceduri și funcții scrise de programatori pentru procesarea tablourilor. Dar chiar și atunci când se efectuează operația de adăugare a două numere întregi, limbaje precum C și Pascal se pot comporta diferit.

Alături de programarea procedurală tradițională, numită și imperativă, există paradigme precum programarea funcțională, programarea logică și programarea orientată pe obiecte. Sper că paradigma de programare procedural-parametrică pe care am propus-o să-și ia locul în această serie [Legalov2000]. Structura conceptelor și obiectelor limbilor depinde foarte mult de paradigma aleasă, care afectează și implementarea traducătorului.

Chiar și același limbaj poate fi implementat în mai multe moduri. Acest lucru se datorează faptului că teoria gramaticilor formale permite metode diferite de analiză a acelorași propoziții. În conformitate cu aceasta, traducătorii pot obține același rezultat (program obiect) din textul sursă original în moduri diferite.

În același timp, toate limbajele de programare au o serie de caracteristici și parametri comuni. Această caracteristică comună determină, de asemenea, principiile de organizare a traducătorilor care sunt similare pentru toate limbile.

  1. Limbajele de programare sunt concepute pentru a ușura programarea. Prin urmare, operatorii și structurile lor de date sunt mai puternice decât cele din limbajele de mașină.
  2. Pentru a crește vizibilitatea programelor, în locul codurilor numerice, se folosesc reprezentări simbolice sau grafice ale constructelor limbajului, care sunt mai convenabile pentru percepția umană.
  3. Pentru orice limbă se definește:
  • O mulțime de simboluri care pot fi folosite pentru a scrie programe corecte (alfabet), elemente de bază.
  • O mulțime de programe corecte (sintaxă).
  • „Semnificația” fiecărui program corect (semantică).

Indiferent de specificul limbajului, orice traducător poate fi considerat un convertor funcțional F, oferind o mapare unică de la X la Y, unde X este un program în limba sursă, Y este un program în limbajul de ieșire. Prin urmare, procesul de traducere în sine poate fi prezentat formal destul de simplu și clar:

Formal, fiecare program corect X este un lanț de simboluri dintr-un anumit alfabet A, transformat în lanțul său corespunzător Y, compus din simboluri ale alfabetului B.

Un limbaj de programare, ca orice sistem complex, este definit printr-o ierarhie de concepte care definește relațiile dintre elementele sale. Aceste concepte sunt interconectate în conformitate cu regulile sintactice. Fiecare program construit conform acestor reguli are o structură ierarhică corespunzătoare.

În acest sens, următoarele caracteristici comune pot fi distinse suplimentar pentru toate limbile și programele acestora: fiecare limbă trebuie să conțină reguli care să permită generarea de programe corespunzătoare acestei limbi sau recunoașterea corespondenței dintre programele scrise și un anumit limbaj.

Relația dintre structura programului și limbajul de programare este numită cartografierea sintactică.

Ca exemplu, luați în considerare relația dintre o structură ierarhică și un șir de simboluri care definesc următoarea expresie aritmetică:

În majoritatea limbajelor de programare, această expresie definește o ierarhie a obiectelor programului, care poate fi afișată sub formă de arbore (Fig. 1.1.):

Cercurile reprezintă simboluri folosite ca structuri elementare, iar dreptunghiurile reprezintă concepte compuse care au o structură ierarhică și posibil recursivă. Această ierarhie este definită folosind reguli sintactice scrise într-un limbaj special numit metalimbaj (metalimbajurile vor fi discutate mai detaliat atunci când studiem gramaticile formale):

<выражение> ::= <слагаемое> | <выражение> + <слагаемое>

<слагаемое> ::= <множитель> | <слагаемое> * <множитель>

<множитель> ::= <буква> | (<выражение>)

<буква>

Notă. Semnul „::=" este citit ca „aceasta este”. Conductă „|” se citește ca „sau”.

Dacă regulile sunt scrise diferit, structura ierarhică se va schimba. Ca exemplu, putem da următorul mod de scriere a regulilor:

<выражение> ::= <операнд> | <выражение> + < операнд > | <выражение> * < операнд >

<операнд> ::= <буква> | (<выражение>)

<буква>::= a | b | c | d | eu | f | g | h | eu | j | k | l | m | n | o | p | q | r | s | t | u | v | w | x | y | z

Ca rezultat al analizei sintactice a aceleiași expresii aritmetice, se va construi o structură ierarhică, prezentată în Fig. 1.2.


De remarcat că structura ierarhică în cazul general nu poate fi în nici un fel legată de semantica expresiei. În ambele cazuri, prioritatea operațiunilor poate fi implementată în conformitate cu regulile general acceptate, când înmulțirea precede adunarea (sau invers, toate operațiunile pot avea aceeași prioritate sub orice set de reguli). Cu toate acestea, prima structură sprijină în mod clar implementarea ulterioară a priorității general acceptate, în timp ce a doua este mai potrivită pentru implementarea operațiunilor cu aceeași prioritate și executarea lor de la dreapta la stânga.

Procesul de găsire a structurii sintactice a unui program dat este numit analizare.

O structură sintactică care este corectă într-o limbă poate fi incorectă în alta. De exemplu, în limba Patra expresia dată nu va fi recunoscută. Cu toate acestea, pentru această limbă expresia postfix corectă este:

Structura sa sintactică este descrisă de regulile:

<выражение> ::= <буква> | <операнд> <операнд> <операция>

< операнд > ::= < буква > | < выражение >

< операция > ::= + | *

<буква>::= a | b | c | d | eu | f | g | h | eu | j | k | l | m | n | o | p | q | r | s | t | u | v | w | x | y | z

Arborele ierarhic care definește structura sintactică este prezentat în Fig. 1.3.

O altă trăsătură caracteristică a tuturor limbilor este semantica lor. Determină semnificația operațiilor limbajului și corectitudinea operanzilor. Lanțurile care au aceeași structură sintactică în diferite limbaje de programare pot diferi în semantică (care, de exemplu, se observă în C++, Pascal, Basic pentru fragmentul de mai sus al unei expresii aritmetice).

Cunoașterea semanticii unei limbi vă permite să o separați de sintaxa acesteia și să o utilizați pentru conversia într-o altă limbă (pentru a genera cod).

Descrierea semanticii și recunoașterea corectitudinii acesteia este de obicei partea cea mai laborioasă și mai voluminoasă a traducătorului, deoarece este necesar să se enumere și să se analizeze multe opțiuni pentru combinații valide de operații și operanzi.

Structura generalizată a traducătorului

Proprietățile și modelele comune sunt inerente atât în ​​limbaje de programare diferite, cât și în traducătorii din aceste limbi. Aceștia sunt supuși unor procese similare de conversie a textului sursă. În ciuda faptului că interacțiunea acestor procese poate fi organizată în moduri diferite, este posibil să se identifice funcții a căror implementare duce la aceleași rezultate. Să numim astfel de funcții faze ale procesului de traducere.

Având în vedere asemănarea dintre compilator și interpret, să ne uităm la fazele care există în compilator. Se evidentiaza:

  1. Faza de analiză lexicală.
  2. Faza de analiză constând din:
  • recunoașterea structurii sintactice;
  • parsare semantică, în timpul căreia se lucrează cu tabele, generând o reprezentare semantică intermediară sau un model obiect al limbajului.
  • Faza de generare a codului, care:
    • analiza semantică a componentelor unei reprezentări intermediare sau model obiect al unui limbaj;
    • traducerea unei reprezentări intermediare sau a unui model obiect în cod obiect.

    Alături de principalele faze ale procesului de traducere, sunt posibile și faze suplimentare:

      2a. Faza intermediară de explorare și optimizare a reprezentării, constând în:
    2a.1. analiza corectitudinii reprezentării intermediare;
    2a.2. optimizarea reprezentării intermediare.
      3a. Faza de optimizare a codului obiect.

    Interpretul diferă prin faptul că faza de generare a codului este de obicei înlocuită de faza de emulare a elementelor unei reprezentări intermediare sau model obiect al limbajului. În plus, interpretul de obicei nu optimizează reprezentarea intermediară, ci o emulează imediat.

    În plus, este posibil să se distingă un proces de analiză și corectare a erorilor existente în textul sursă procesat al programului care este uniform pentru toate fazele.

    Structura generalizată a compilatorului, ținând cont de fazele existente în acesta, este prezentată în Fig. 1.4.

    Este format dintr-un analizor lexical, un parser, un generator de cod și un analizor de erori. În locul unui generator de cod, interpretul folosește un emulator (Fig. 1.5), în care, pe lângă elementele reprezentării intermediare, sunt transferate datele sursă. Ieșirea emulatorului este rezultatul calculelor.

    Un analizor lexical (cunoscut și ca scanner) citește șirul de caractere introdus și le grupează în structuri elementare numite lexeme. Fiecare jeton are o clasă și o semnificație. De obicei, candidații pentru rolul lexemelor sunt constructe elementare ale limbajului, de exemplu, identificator, număr real, comentariu. Tokenurile rezultate sunt transmise parserului. Scannerul nu este o parte obligatorie a traducătorului. Cu toate acestea, vă permite să creșteți eficiența procesului de traducere. Analiza lexicală este discutată mai detaliat în subiectul: „Organizarea analizei lexicale”.

    Analizatorul parsează programul sursă folosind lexeme primite, construiește structura sintactică a programului și analiză semantică cu formarea unui model obiect al limbajului. Modelul obiect reprezintă o structură sintactică, completată de relații semantice între conceptele existente. Aceste conexiuni pot fi:

    • referințe la variabile, tipuri de date și nume de proceduri plasate în tabelele de nume;
    • conexiuni care determină succesiunea executării comenzii;
    • conexiuni care determină imbricarea elementelor modelului obiect limbaj și altele.

    Astfel, parser-ul este un bloc traducător destul de complex. Prin urmare, poate fi împărțit în următoarele componente:

    • recunoaștere;
    • bloc de analiză semantică;
    • un model de obiect, sau o reprezentare intermediară, constând dintr-un tabel de nume și o structură sintactică.

    Structura generalizată a parserului este prezentată în Fig. 1.6.

    Recunoașterea primește un lanț de jetoane și, pe baza acestuia, efectuează parsarea în conformitate cu regulile utilizate. Tokenurile, după analizarea cu succes a regulilor, sunt transferate la analizatorul semantic, care construiește un tabel de nume și înregistrează fragmente ale structurii sintactice. În plus, sunt înregistrate conexiuni semantice suplimentare între tabelul de nume și structura sintactică. Ca urmare, se formează un model obiect al programului, eliberat de legarea la sintaxa limbajului de programare. Destul de des, în locul unei structuri sintactice care copiază complet ierarhia obiectelor limbajului, este creat analogul său simplificat, care se numește reprezentare intermediară.

    Analizatorul de erori primește informații despre erorile care apar în diferite blocuri de traducător. Folosind informațiile primite, generează mesaje către utilizator. În plus, acest bloc poate încerca să corecteze eroarea pentru a continua analiza. El este responsabil și de acțiunile legate de finalizarea corectă a programului în cazul în care este imposibilă continuarea difuzării.

    Generatorul de cod construiește cod obiect mașină pe baza analizei modelului obiect sau reprezentării intermediare. Construcția codului este însoțită de o analiză semantică suplimentară legată de necesitatea de a converti comenzile generalizate în coduri ale unui anumit computer. În etapa unei astfel de analize, se determină în sfârșit posibilitatea transformării și se selectează opțiuni eficiente. Generarea codului în sine este recodificarea unei comenzi în alta.

    Opțiuni pentru interacțiunea blocurilor de traducător

    Organizarea proceselor de traducere, care determină implementarea fazelor principale, poate fi realizată în diverse moduri. Acest lucru este determinat de diferite opțiuni pentru interacțiunea blocurilor traducătoare: analizor lexical, parser și generator de cod. În ciuda aceluiași rezultat final, opțiuni diferite pentru interacțiunea blocurilor de traducător oferă opțiuni diferite pentru stocarea datelor intermediare. Există două opțiuni principale pentru interacțiunea blocurilor de traducător:

    • organizație cu mai multe treceri, în care fiecare fază este un proces independent care transferă controlul în faza următoare numai după procesarea completă a datelor sale;
    • organizare cu o singură trecere, în care toate fazele reprezintă un singur proces și își transmit date între ele în fragmente mici.

    Pe baza celor două opțiuni principale, puteți crea și diverse combinații ale acestora.

    Organizarea cu mai multe treceri a interacțiunii dintre blocurile de traducător

    Această versiune a interacțiunii blocurilor, folosind compilatorul ca exemplu, este prezentată în Fig. 1.7.


    Analizatorul lexical procesează complet textul sursă, formând un lanț de ieșire format din toate lexemele primite. Numai după aceasta controlul este transferat la parser. Analizatorul primește lanțul de lexeme generat și, pe baza acestuia, formează o reprezentare intermediară sau model obiect. După ce primește întregul model de obiect, acesta transmite controlul generatorului de cod. Generatorul de cod, bazat pe modelul obiect limbaj, construiește codul mașină necesar

    Avantajele acestei abordări includ:

    1. Izolarea fazelor individuale, ceea ce permite implementarea și utilizarea lor independentă.
    2. Capacitatea de a stoca datele obținute ca urmare a funcționării fiecărei faze pe dispozitive de stocare externe și de a le utiliza după cum este necesar.
    3. Abilitatea de a reduce cantitatea de RAM necesară pentru a opera traducătorul prin apelarea secvenţială a fazelor.

    Dezavantajele includ:

    1. Prezența unor volume mari de informații intermediare, din care doar o mică parte este necesară la un moment dat.
    2. Încetinirea vitezei de difuzare datorită execuției secvențiale a fazelor și utilizării dispozitivelor de stocare externe pentru a economisi RAM.

    Această abordare poate fi convenabilă atunci când construiți traducători din limbaje de programare cu o structură sintactică și semantică complexă (de exemplu, PL/I). În astfel de situații, traducerea este dificil de realizat într-o singură trecere, deci este mai ușor să reprezentați rezultatele trecerilor anterioare ca date intermediare suplimentare. Trebuie remarcat faptul că structurile semantice și sintactice complexe ale limbajului pot duce la treceri suplimentare necesare pentru a stabili dependențele necesare. Numărul total de permise poate fi mai mare de zece. Numărul de treceri poate fi, de asemenea, afectat de utilizarea de către program a caracteristicilor specifice ale limbajului, cum ar fi declararea variabilelor și procedurilor după ce sunt utilizate, aplicarea regulilor implicite de declarare și așa mai departe.

    Organizarea într-o singură trecere a interacțiunii dintre blocurile de traducător

    Una dintre opțiunile de interacțiune a blocurilor compilatorului cu o organizare cu o singură trecere este prezentată în Fig. 1.8.

    În acest caz, procesul de traducere decurge după cum urmează. Analizatorul lexical citește un fragment din textul sursă necesar obținerii unui lexem. După ce tokenul este format, apelează parserul și îi transmite jetonul creat ca parametru. Dacă analizatorul poate construi următorul element al reprezentării intermediare, atunci face acest lucru și transmite fragmentul construit generatorului de cod. În caz contrar, analizatorul returnează controlul scanerului, clarificând astfel că următorul token a fost procesat și că sunt necesare date noi.

    Generatorul de cod funcționează într-un mod similar. Pe baza fragmentului primit al reprezentării intermediare, creează fragmentul corespunzător de cod obiect. După aceasta, controlul revine la parser.

    La finalizarea textului sursă și la finalizarea procesării tuturor datelor intermediare de către fiecare dintre blocuri, analizatorul lexical inițiază procesul de terminare a programului.

    Cel mai adesea, traducătorii cu o singură trecere folosesc o schemă de control diferită, în care analizatorul joacă rolul blocului principal (Fig. 1.9).

    Analizatorul lexical și generatorul de cod acționează ca subrutine pe care le apelează. De îndată ce analizatorul are nevoie de un alt token, apelează scanerul. La primirea unui fragment al unei reprezentări intermediare, se face un apel către generatorul de cod. Finalizarea procesului de traducere are loc după ce ultimul token a fost primit și procesat și este inițiat de parser.

    Avantajele unei scheme cu o singură trecere includ absența unor volume mari de date intermediare, viteza mare de procesare datorită combinării fazelor într-un singur proces și absența accesului la dispozitivele de stocare externe.

    Dezavantajele includ: imposibilitatea implementării unei astfel de scheme de traducere pentru limbi cu structuri complexe și lipsa datelor intermediare care să poată fi utilizate pentru analiză și optimizare complexe.

    Această schemă este adesea folosită pentru limbaje de programare care sunt simple în structuri semantice și sintactice, atât în ​​compilatoare, cât și în interpreți. Exemple de astfel de limbi sunt Basic și Pascal. Un interpret clasic este de obicei construit folosind o schemă cu o singură trecere, deoarece execuția directă se realizează la nivelul fragmentelor individuale ale reprezentării intermediare. Organizarea interacțiunii dintre blocurile unui astfel de interpret este prezentată în Fig. 1.10.

    Interacțiuni combinate ale blocurilor de traducător

    Combinațiile de scheme de traducere cu mai multe treceri și cu o singură trecere dau naștere la o varietate de opțiuni combinate, dintre care multe sunt utilizate cu succes. Ca exemplu, putem lua în considerare unele dintre ele.

    În fig. Figura 1.11 prezintă o diagramă a interacțiunii blocurilor translatoare, împărțind întregul proces în două treceri. Prima trecere generează o reprezentare intermediară completă a programului, iar a doua generează cod. Utilizarea unei astfel de scheme vă permite să transferați cu ușurință traducătorul de la un sistem computerizat la altul prin rescrierea generatorului de cod.

    În plus, în locul unui generator de cod, este ușor să conectați un emulator de reprezentare intermediar, care vă permite pur și simplu să dezvoltați un sistem de programare într-un anumit limbaj, orientat către diverse medii de execuție. Un exemplu de astfel de organizare a interacțiunii dintre blocurile traductoare este prezentat în Fig. 1.12.


    Alături de schemele care implică înlocuirea generatorului de cod cu un emulator, există traducători care permit utilizarea lor în comun. Una dintre astfel de scheme este prezentată în Fig. 1.13.

    Procesul de traducere, inclusiv generarea codului, poate fi finalizat în orice număr de treceri (exemplul folosește traducerea cu o singură trecere prezentată mai devreme). Totuși, codul obiect generat nu este executat pe sistemul informatic corespunzător, ci este emulat pe un computer cu o arhitectură diferită. Această schemă este utilizată într-un mediu construit în jurul limbajului de programare Java. Traducatorul însuși generează cod virtual de mașină Java, care este emulat folosind mijloace speciale atât în ​​mod autonom, cât și într-un mediu de browser de Internet.

    Schema prezentată poate avea și o interpretare mai largă în raport cu orice compilator care generează cod mașină. Chestia este că majoritatea computerelor moderne sunt implementate folosind controlul microprogramelor. Un dispozitiv de control al microprogramelor poate fi considerat un program care emulează codul mașinii. Acest lucru ne permite să vorbim despre utilizarea pe scară largă a celei mai recente scheme prezentate.

    Testați întrebări și sarcini

    1. Denumiți diferențele:
      • interpret din compilator;
      • compilator de la asamblator;
      • transcoder translator;
      • emulator de la interpret;
      • sintaxă din semantică.
    1. Spuneți-ne despre cele mai recente evoluții în limbajele de programare pe care le cunoașteți. Prezentați principalele caracteristici ale acestor limbi.
    2. Dați exemple specifice de utilizare a metodelor de traducere în domenii care nu au legătură cu limbajele de programare.
    3. Dați exemple specifice de limbaje de programare compilate.
    4. Dați exemple specifice de limbaje de programare interpretate.
    5. Dați exemple specifice de limbaje de programare pentru care sunt disponibile atât compilatoare, cât și interpreți.
    6. Principalele avantaje și dezavantaje ale compilatoarelor.
    7. Principalele avantaje și dezavantaje ale interpreților.
    8. Descrieți principalele diferențe în sintaxa celor două limbaje de programare pe care le cunoașteți.
    9. Descrieți principalele diferențe în semantica celor două limbaje de programare pe care le cunoașteți.
    10. Numiți principalele faze ale procesului de traducere și scopul lor.
    11. Numiți caracteristicile specifice ale traducerii cu o singură trecere.
    12. Numiți caracteristicile specifice ale traducerii cu mai multe treceri.
    13. Dați exemple de combinații posibile de traducere cu o singură trecere și cu mai multe treceri. Spuneți-ne despre utilizarea practică a acestor scheme.

    Fiecare calculator are propriul său limbaj de programare - limbaj de comandă sau limbaj de mașină - și poate executa programe scrise doar în acest limbaj. În principiu, orice algoritm poate fi descris folosind limbajul mașinii, dar costurile de programare vor fi extrem de mari. Acest lucru se datorează faptului că limbajul mașină vă permite să descrieți și să procesați doar structuri de date primitive - biți, octeți, cuvinte. Programarea în coduri de mașină necesită detalii excesive ale programului și este accesibilă doar programatorilor care au o bună cunoaștere a structurii și funcționării computerului. Limbajele de nivel înalt (Fortran, PL/1, Pascal, C, Ada etc.) cu structuri de date dezvoltate și mijloace de procesare care nu depind de limbajul unui anumit computer au făcut posibilă depășirea acestei dificultăți.

    Limbajele algoritmice de nivel înalt permit programatorului să descrie destul de simplu și convenabil algoritmi pentru rezolvarea multor probleme aplicate. Această descriere se numește programul original, iar limbajul de nivel înalt este Limba de introducere.

    Procesor de limbaj este un program în limbajul mașinii care permite unui computer să înțeleagă și să execute programe în limbajul de intrare. Există două tipuri principale de procesoare de limbă: interpreți și traducători.

    Interpret este un program care acceptă un program în limbajul de intrare ca intrare și, pe măsură ce sunt recunoscute constructele limbajului de intrare, le implementează, producând rezultatele calculelor prescrise de programul sursă.

    Traducător este un program care acceptă un program original ca intrare și generează la ieșire un program care este echivalent funcțional cu originalul, numit obiect. Un program obiect este scris într-un limbaj obiect. Într-un caz particular, limbajul mașină poate servi ca limbaj obiect, iar în acest caz, programul obținut la ieșirea traducătorului poate fi executat imediat pe un computer (interpretat). În acest caz, computerul este un interpret al unui program obiect în codul mașinii. În general, un limbaj obiect nu trebuie să fie o mașină sau ceva apropiat de el (autocod). Un limbaj obiect poate servi ca limbaj intermediar– o limbă situată între limbajul de intrare și limbajul mașină.

    Dacă un limbaj intermediar este folosit ca limbaj obiect, atunci sunt posibile două opțiuni pentru construirea unui traducător.

    Prima opțiune este că pentru limba intermediară există (sau este în curs de dezvoltare) un alt traducător din limbajul intermediar în limbajul mașină și este folosit ca ultim bloc al traducătorului proiectat.

    A doua opțiune pentru construirea unui traducător folosind o limbă intermediară este de a construi un interpret pentru comenzile de limbaj intermediar și de a-l folosi ca ultimul bloc al traducătorului. Avantajul interpreților se manifestă în debugging și traducători interactivi, care asigură că utilizatorul poate lucra într-un mod interactiv, până la a face modificări în program fără a-l retraduce complet.

    Interpreții sunt folosiți și în emularea programelor - execuția pe o mașină tehnologică a programelor compilate pentru o altă mașină (obiect). Această opțiune, în special, este utilizată la depanarea programelor pe un computer de uz general care va fi executat pe un computer specializat.

    Un traducător care folosește o limbă apropiată de limbajul mașinii (autocod sau asamblare) ca limbă de intrare este denumit în mod tradițional asamblator. Este chemat un traducător pentru o limbă de nivel înalt compilator.

    S-au înregistrat progrese semnificative în dezvoltarea compilatorului în ultimii ani. Primele compilatoare au folosit așa-numitele metode de difuzare în direct- Acestea sunt metode predominant euristice în care, pe baza unei idei generale, pentru fiecare construcție de limbă a fost dezvoltat propriul algoritm de traducere într-un echivalent de mașină. Aceste metode au fost lente și nestructurate.

    Metodologia de proiectare pentru compilatoarele moderne se bazează pe metoda compozițională condusă sintactic prelucrarea limbajului. Compozițional în sensul că procesul de conversie a unui program sursă într-un program obiect este implementat prin alcătuirea de mapări independente funcțional cu structuri de date de intrare și de ieșire identificate în mod explicit. Aceste mapări sunt construite din luarea în considerare a programului sursă ca o compoziție a principalelor aspecte (nivele) descriere a limbajului de intrare: vocabular, sintaxă, semantică și pragmatică, și identificarea acestor aspecte din programul sursă în timpul compilării acestuia. Să luăm în considerare aceste aspecte pentru a obține un model de compilator simplificat.

    Baza oricărui limbaj natural sau artificial este alfabet– un set de caractere elementare permise în limbă (litere, cifre și caractere de serviciu). Semnele pot fi combinate în cuvinte– construcții elementare ale limbajului, considerate în text (program) ca simboluri indivizibile care au un anumit sens.


    Un cuvânt poate fi, de asemenea, un singur caracter. De exemplu, în limbajul Pascal, cuvintele sunt identificatori, cuvinte cheie, constante și delimitatori, în special operatori aritmetici și logici, paranteze, virgule și alte simboluri. Vocabularul unei limbi, împreună cu o descriere a modurilor în care sunt reprezentate, constituie vocabular limba.

    Cuvintele dintr-o limbă sunt combinate în structuri mai complexe - propoziții. În limbajele de programare, cea mai simplă propoziție este un operator. Propozițiile sunt construite din cuvinte și propoziții mai simple după regulile de sintaxă. Sintaxă limbajul este o descriere a propozițiilor corecte. Descrierea sensului propozițiilor, de ex. semnificațiile cuvintelor și conexiunile lor interne, este semantică limba. În plus, observăm că un anumit program are un anumit impact asupra traducătorului - pragmatism. Luate împreună, sintaxa, semantica și pragmatismul limbajului formează semiotica limba.

    Traducerea unui program dintr-o limbă în alta, în general, constă în schimbarea alfabetului, vocabularului și sintaxei limbajului programului, păstrând în același timp semantica acestuia. Procesul de traducere a unui program sursă într-un program obiect este de obicei împărțit în mai multe subprocese independente (faze de traducere), care sunt implementate de blocurile translator corespunzătoare. Este convenabil să se ia în considerare analiza lexicală, analiza sintactică, analiza semantică și

    sinteza programului obiect. Cu toate acestea, în multe compilatoare reale, aceste faze sunt împărțite în mai multe subfaze și pot exista și alte faze (de exemplu, optimizarea codului obiect). În fig. Figura 1.1 prezintă un model funcțional simplificat al translatorului.

    Conform acestui model, programul de intrare este supus în primul rând procesării lexicale. Scopul analizei lexicale este de a traduce programul sursă în limbajul intern al compilatorului, în care cuvintele cheie, identificatorii, etichetele și constantele sunt reduse la un singur format și înlocuite cu coduri condiționate: numerice sau simbolice, care se numesc descriptori. Fiecare descriptor constă din două părți: clasa (tipul) jetonului și un pointer către adresa de memorie unde sunt stocate informații despre tokenul specific. De obicei, aceste informații sunt organizate în tabele. Concomitent cu traducerea programului sursă în limbajul intern, la etapa analizei lexicale, controlul lexical- identificarea cuvintelor inacceptabile în program.

    Analizorul preia rezultatul analizorului lexical și traduce secvența de imagini simbol în forma unui program intermediar. Un program intermediar este în esență o reprezentare a arborelui de sintaxă a unui program. Acesta din urmă reflectă structura programului original, adică. ordinea și conexiunile dintre operatorii săi. În timpul construcției unui arbore de sintaxă, controlul sintactic– identificarea erorilor de sintaxă din program.

    Ieșirea reală a parserului poate fi secvența de comenzi necesare pentru a construi middleware-ul, pentru a accesa tabelele de directoare și pentru a emite un mesaj de diagnosticare atunci când este necesar.

    Orez. 1.1. Model funcțional simplificat al traducătorului

    Sinteza unui program obiect începe, de regulă, cu distribuirea și alocarea memoriei pentru obiectele principale ale programului. Fiecare propoziție din programul sursă este apoi examinată și sunt generate propoziții echivalente semantic în limbajul obiect. Informațiile de intrare aici sunt arborele de sintaxă al programului și tabelele de ieșire ale analizorului lexical - un tabel de identificatori, un tabel de constante și altele. Analiza arborelui ne permite să identificăm secvența comenzilor generate ale unui program obiect, iar folosind tabelul de identificatori, determinăm tipurile de comenzi care sunt valabile pentru valorile operanzilor din comenzile generate (de exemplu, ce comenzi trebuie generate: fixă ​​sau virgulă mobilă etc.).

    Generarea efectivă a unui program obiect este adesea precedată de analiza semantică, care implică diverse tipuri de procesare semantică. Un tip este verificarea convențiilor semantice într-un program. Exemple de astfel de convenții: descrierea unică a fiecărui identificator din program, definirea unei variabile se face înainte de a fi utilizată etc. Analiza semantică poate fi efectuată în fazele ulterioare ale traducerii, de exemplu, în faza de optimizare a programului, care poate fi inclusă și în traducător. Scopul optimizării este de a reduce resursele de timp sau resursele RAM necesare pentru a executa un program obiect.

    Acestea sunt principalele aspecte ale procesului de traducere din limbi de nivel înalt. Organizarea diferitelor faze de traducere și metodele practice asociate pentru descrierea lor matematică sunt discutate mai jos.

    Pentru a traduce dintr-o limbă în alta, programele, ca și oamenii, necesită un traducător sau, științific vorbind, un traducător.

    Traducător: concepte de bază

    Un astfel de program ca traducător este o reprezentare lingvistică a calculelor I ->P ->P (i). Un interpret este un program a cărui intrare este un program P cu unele date de intrare X. El execută P pe X: I(P, x) = P(x) Există un singur traducător care este capabil să execute toate programele posibile (care poate fi reprezentat în sistem formal). Aceasta este o descoperire foarte semnificativă și profundă a lui Turing. Procesorul este un interpret al programelor în limbajul mașinii. De obicei, este prea costisitor să scrieți interpreți pentru limbi de nivel înalt, astfel încât aceștia sunt traduși într-o formă mai ușor de interpretat. Unele tipuri de traducători au nume foarte ciudate. Programul traduce programe în limbaj de asamblare în limbaj mașină. Compilatorul vă permite să traduceți dintr-o limbă de nivel înalt într-o limbă de nivel inferior. Un traducător este un program care ia ca intrare un program într-o limbă S și, după procesare, produce un program în limbajul T. Astfel, ambele au aceeași semantică: P->X->Q. Astfel, pentru orice xP(x)=Q(x). Traducerea unui întreg program în ceva interpretat se numește compilare pre-execuție sau compilare AOT. Compilatoarele AOT pot fi utilizate secvenţial. Ultimul este foarte adesea un asamblator. Deci, să ne uităm la un exemplu: Cod sursă -> Compilator (traducător) -> Cod asamblare -> Asamblator (traducător) -> Cod mașină -> CPU (interpret). Compilarea dinamică sau on-line are loc atunci când o parte a unui program este tradusă în timp ce alte părți compilate anterior sunt executate. Traducătorii JIT își amintesc ce au făcut deja înainte, pentru a nu repeta codul sursă iar și iar. Ele sunt chiar capabile de compilare și recompilare adaptivă, care se bazează pe comportamentul mediului de rulare al programului. Multe limbi oferă posibilitatea de a executa cod în timp ce programul este tradus și, de asemenea, de a compila cod nou în timp ce programul rulează.

    Difuzare: etape

    Procesul de traducere constă din etape de sinteză și analiză. Schematic, acest proces arată cam așa: Cod sursă -> Analizor -> Reprezentare conceptuală -> Sintetizator (generator) -> Cod țintă. Acest lucru se datorează următoarelor motive:

    - orice altă metodă pur și simplu nu este potrivită;

    — traducerea prin cuvinte pur și simplu nu funcționează.

    Puteți folosi următoarea soluție de inginerie: dacă aveți nevoie să scrieți traducători pentru M limbi sursă și N limbi țintă, va trebui să scrieți doar M+N programe simple (semi-compilatoare), și nu MxN traducători completi (complexi). . În practică, totuși, este destul de rar ca o reprezentare conceptuală să fie suficient de expresivă și puternică pentru a acoperi toate limbile țintă și sursă existente. Deși unii utilizatori au putut să se apropie de asta. Compilatorii adevărați trec prin multe etape diferite. Prin crearea propriului compilator, nu va trebui să refaceți toată munca grea pe care programatorii au făcut-o deja în crearea generatoarelor și vizualizărilor. Puteți să traduceți limba dvs. direct în JavaScript sau C și să utilizați compilatoarele de limbaj C existente și motoarele JavaScript pentru a face restul. De asemenea, puteți utiliza vizualizări intermediare existente și mașini virtuale.

    Înregistrare cu traducător

    Un traducător poate fi un instrument tehnic sau un program care utilizează trei limbi: sursă, țintă, bază. Ele pot fi scrise sub forma unui T, plasând sursa în stânga, ținta în dreapta și baza dedesubt. Există trei tipuri de compilatoare în total.

    1. Un traducător este un autocompilator dacă limba sa sursă se potrivește cu cea de bază.
    2. Un compilator a cărui limbă țintă este egală cu limba de bază se numește auto-rezident.
    3. Dacă limbile țintă și de bază sunt diferite, atunci traducătorul este un compilator încrucișat.

    De ce este important să facem distincția între aceste tipuri de compilatoare? Chiar dacă nu construiți niciodată un compilator cu adevărat bun, este o idee bună să aflați despre tehnologia din spatele acestuia, deoarece toate conceptele utilizate în acest scop sunt folosite peste tot în limbaje de interogare a bazelor de date, formatare text, arhitecturi avansate de computer, interfețe grafice, sarcini generalizate. optimizare, traduceri automate, controlere și în mașini virtuale. De asemenea, dacă trebuie să scrieți preprocesoare, încărcătoare, asamblare, depanare sau profilere, trebuie să parcurgeți aceiași pași ca atunci când scrieți un compilator. De asemenea, puteți afla despre modalități mai bune de a scrie programe, deoarece dezvoltarea unui traducător pentru un limbaj de programare înseamnă o mai bună înțelegere a tuturor ambiguităților și subtilităților acestuia. Învățând principiile generale ale traducerii, poți deveni un bun designer de limbi străine. Dar chiar contează? Cât de cool este o limbă dacă nu poate fi implementată eficient?

    Tehnologie la scară largă

    Tehnologia compilatorului acoperă o gamă largă de domenii diferite ale informaticii. Include teoria limbajului formal, gramatica, arhitectura computerului, analizarea, calculabilitatea, seturile de instrucțiuni, CISC sau RISC, pipelining, ciclurile de ceas, nucleele etc., precum și controlul secvenței, recursiunea, execuția condiționată, descompunerea funcțională, iterația, modularitatea, sincronizare, metaprogramare, constante, domeniul de aplicare, șabloane, tip de ieșire, adnotări, prototipuri, fluxuri, cutii poștale, monade, wildcards, continuări, memorie tranzacțională, expresii regulate, polimorfism, moștenire, moduri de parametri etc. De asemenea, pentru a crea un compilator, trebuie să înțelegeți limbaje abstracte de programare, algoritmi și structuri de date, expresii regulate, algoritmi grafici și programare dinamică.

    Proiectarea compilatorului. Posibile probleme care apar la crearea unui traducător real

    Ce probleme pot apărea cu limba sursă? Este ușor de compilat? Există un preprocesor pentru asta? Cum sunt procesate tipurile? Ce grupare de treceri ale compilatorului este utilizată - cu o singură trecere sau cu mai multe treceri? De asemenea, merită o atenție deosebită gradul de optimizare dorit. O difuzare rapidă și murdară a unui program cu optimizare redusă sau deloc poate fi normală. Supraoptimizarea poate încetini compilatorul, dar în timpul execuției un cod mai bun poate merita.

    Rata de detectare a erorilor. Este necesar ca traducătorul să se oprească la prima eroare? Când ar trebui să se oprească? Ar trebui să aveți încredere în compilator pentru a corecta erorile?

    Set de instrumente necesar

    Dacă în cazul dvs. limba sursă nu este prea mică, atunci prezența unui generator de analizor și a unui scaner sunt obligatorii. Există și generatoare de coduri speciale, dar nu sunt foarte răspândite.

    În ceea ce privește tipul de cod țintă de generat, trebuie să alegeți dintre codul de mașină pur, augmentat sau virtual. De asemenea, puteți scrie o parte de intrare care creează vederi intermediare populare, cum ar fi LLVM, JVM, RTL. De asemenea, puteți face o traducere din codul sursă în codul sursă în Java Script sau C. Dacă vorbim despre formatul codului țintă, aici puteți selecta codul de mașină portabil, codul de mașină pentru imaginea memoriei, limbajul de asamblare.

    Retargeting

    Când utilizați un număr mare de generatoare, ar fi bine să aveți o parte comună de intrare. Tot din acest motiv, pentru multe piese de intrare este mai bine să aveți un singur generator.

    Componentele compilatorului

    Enumerăm principalele componente funcționale ale traducătorului, care generează codul mașinii dacă programul de ieșire este un program scris în C sau o mașină virtuală:

    — programul de intrare intră într-un analizor lexical, sau cu alte cuvinte, într-un scanner, care îl transformă într-un flux de jetoane;

    — analizatorul de sintaxă (parser) construiește un arbore de sintaxă abstractă din ele;

    — un analizor semantic descompune informațiile semantice și verifică nodurile de arbore pentru erori;

    — ca rezultat, se construiește un grafic semantic. Acest termen se referă la un arbore de sintaxă abstractă cu legături stabilite și proprietăți suplimentare;

    — generatorul de cod intermediar construiește un grafic de flux (tuplurile sunt grupate în blocuri principale);

    — optimizatorul independent de mașină realizează optimizarea locală și globală, dar rămâne în principal în cadrul subrutinelor, simplificând calculele și reducând codul redundant. Rezultatul ar trebui să fie un grafic de flux modificat;

    — pentru a conecta blocurile de bază într-un cod în linie dreaptă cu transfer de control, se utilizează un generator de cod țintă. Acesta creează un fișier obiect în asamblare cu registre vizuale, care poate să nu fie foarte eficient;

    — un optimizator-linker dependent de mașină este utilizat pentru a distribui memoria între registrele virtuale și pentru a efectua programarea instrucțiunilor. De asemenea, convertește un program scris în limbaj de asamblare într-un asamblator real folosind pipelining.

    — se utilizează subsisteme de detectare a erorilor și un manager de tabel de simboluri;

    — scanare și analiză lexicală. Scanerul este folosit pentru a converti un flux de caractere de cod sursă într-un flux de jetoane, eliminând comentariile, spațiile și extinzând macrocomenzi. Destul de des, scanerele întâmpină următoarea problemă: dacă să ia în considerare indentări, majuscule sau comentarii imbricate.

    Acele erori care pot apărea în timpul scanării se numesc lexicale. Acestea includ următoarele:

    — simboluri care lipsesc din alfabet;

    — depășirea numărului de caractere dintr-o linie sau cuvânt;

    - șir literal sau caracter neînchis;

    - sfârșitul fișierului în comentariu.

    Parsarea sau analizarea este folosită pentru a transforma o secvență de jetoane într-un arbore de sintaxă abstractă. În acest caz, fiecare nod de arbore este salvat ca obiect cu câmpuri numite. Mulți dintre ei sunt ei înșiși noduri de copac. Nu există cicluri în această etapă. Când creați un parser, trebuie mai întâi să acordați atenție nivelului de complexitate al gramaticii (LR sau LL) și să aflați dacă există reguli de dezambiguizare. Într-adevăr, unele limbi necesită analiză semantică. Erorile care apar în această etapă se numesc erori sintactice.

    Analiza semantică

    La efectuarea analizei semantice este necesar, în primul rând, să se verifice regulile de validitate și să se lege între ele părți ale arborelui de sintaxă pentru a forma un graf semantic prin inserarea unei operații pentru turnarea implicită a tipului, rezoluția referințelor de nume etc. Este clar că diferitele limbaje de programare au seturi diferite de reguli de valabilitate. La compilarea limbajelor asemănătoare Java, traducătorii pot întâmpina următoarele erori:

    — declarații multiple ale unei variabile în domeniul său de aplicare;

    — încălcarea regulilor de accesibilitate;

    — prezența trimiterilor la un nume nedeclarat;

    — număr prea mare sau, dimpotrivă, insuficient de argumente la apelarea unei metode;

    - nepotrivire de tip.

    Generaţie

    Prin generarea codului intermediar, se produce un grafic de flux, care este compus din tupluri grupate în blocuri de bază. După generarea codului, se obține codul mașină real. Primul pas în compilatoarele tradiționale pentru mașinile RISC este crearea unui asamblator cu un număr infinit de registre virtuale. Acest lucru probabil nu se va întâmpla pentru mașinile CISC.

    Limbajele de programare pot fi împărțite în compilate și interpretate.

    Un program într-un limbaj compilat, folosind un program de compilare special, este convertit (compilat) într-un set de instrucțiuni pentru un anumit tip de procesor (cod mașină) și apoi scris într-un modul executabil, care poate fi lansat pentru execuție separat. program. Cu alte cuvinte, compilatorul traduce codul sursă al programului dintr-un limbaj de programare de nivel înalt în coduri binare ale instrucțiunilor procesorului.

    Dacă un program este scris într-o limbă interpretată, atunci interpretul execută (interpretează) direct textul sursă fără traducere prealabilă. În acest caz, programul rămâne în limba originală și nu poate fi lansat fără un interpret. Putem spune că un procesor de calculator este un interpret al codului mașină.

    Pe scurt, compilatorul traduce codul sursă al unui program în limbajul mașinii imediat și în întregime, creând un program executabil separat, iar interpretul execută textul sursă în timp ce programul rulează.

    Împărțirea în limbi compilate și interpretate este oarecum arbitrară. Deci, pentru orice limbaj compilat în mod tradițional, cum ar fi Pascal, puteți scrie un interpret. În plus, majoritatea interpreților „puri” moderni nu execută constructe de limbaj în mod direct, ci mai degrabă le compilează într-o reprezentare intermediară de nivel înalt (de exemplu, cu dereferențierea variabilelor și extinderea macro).

    Un compilator poate fi creat pentru orice limbaj interpretat - de exemplu, limbajul Lisp, care este interpretat nativ, poate fi compilat fără restricții. Codul generat în timpul execuției programului poate fi, de asemenea, compilat dinamic în timpul execuției.

    De regulă, programele compilate se execută mai rapid și nu necesită programe suplimentare pentru a fi executate, deoarece sunt deja traduse în limbajul mașinii. În același timp, de fiecare dată când textul programului este modificat, acesta trebuie să fie recompilat, ceea ce creează dificultăți în timpul dezvoltării. În plus, programul compilat poate fi executat doar pe același tip de computer și, de obicei, sub același sistem de operare, pentru care a fost proiectat compilatorul. Pentru a crea un executabil pentru un alt tip de mașină, este necesară o nouă compilare.

    Limbile interpretate au câteva caracteristici suplimentare specifice (vezi mai sus), în plus, programele din ele pot fi rulate imediat după modificare, ceea ce facilitează dezvoltarea. Un program într-un limbaj interpretat poate fi deseori rulat pe diferite tipuri de mașini și sisteme de operare fără efort suplimentar.

    Cu toate acestea, programele interpretate rulează considerabil mai lent decât cele compilate și nu pot fi executate fără un program interpret suplimentar.

    Unele limbaje, cum ar fi Java și C#, se încadrează între compilat și interpretat. Și anume, programul nu este compilat în limbajul mașinii, ci într-un cod de nivel scăzut independent de mașină, bytecode. Bytecode-ul este apoi executat de mașina virtuală. Interpretarea este de obicei folosită pentru a executa bytecode, deși părți individuale ale acestuia pot fi traduse în codul mașinii direct în timpul execuției programului folosind compilarea Just-in-time (JIT) pentru a accelera programul. Pentru Java, bytecode este executat de Java Virtual Machine (JVM), pentru C# - de Common Language Runtime.

    Această abordare, într-un sens, vă permite să utilizați atât avantajele interpreților, cât și ale compilatorilor. De asemenea, merită menționat limbajul original Forth, care are atât un interpret, cât și un compilator.

    Deoarece textul scris într-un limbaj de programare este de neînțeles pentru un computer, trebuie tradus în codul mașinii. Această traducere a unui program dintr-un limbaj de programare într-un limbaj de cod de mașină se numește traducere și este realizată de programe speciale - traducători.

    Un traducător este un program de serviciu care convertește un program sursă furnizat în limbajul de programare de intrare într-un program de lucru prezentat într-un limbaj obiect.

    În prezent, traducătorii sunt împărțiți în trei grupuri principale: asamblatori, compilatori și interpreți.

    Un asamblator este un program utilitar de sistem care convertește structurile simbolice în comenzi în limbajul mașinii. O caracteristică specifică a asamblatorilor este că efectuează o traducere textuală a unei instrucțiuni simbolice într-o instrucțiune de mașină. Astfel, limbajul de asamblare (numit și autocode) este conceput pentru a facilita percepția sistemului de comandă al computerului și pentru a accelera programarea în acest sistem de comandă. Este mult mai ușor pentru un programator să-și amintească denumirea mnemonică a instrucțiunilor mașinii decât codul lor binar.

    În același timp, limbajul de asamblare, pe lângă analogii comenzilor mașinii, conține multe directive suplimentare care facilitează, în special, gestionarea resurselor computerului, scrierea fragmentelor repetate și construirea de programe cu mai multe module. Prin urmare, expresivitatea limbajului este mult mai bogată decât un simplu limbaj de codare simbolică, ceea ce îmbunătățește foarte mult eficiența programării.

    Un compilator este un program de serviciu care traduce un program scris în limbajul de programare sursă în limbajul mașinii. La fel ca un asamblator, un compilator convertește un program dintr-o limbă în alta (cel mai adesea, în limba unui anumit computer). În același timp, comenzile în limba sursă diferă semnificativ în organizare și putere de comenzile în limbajul mașinii. Există limbi în care o comandă a limbii sursă este tradusă în 7-10 comenzi de mașină. Cu toate acestea, există și limbi în care fiecare comandă poate avea 100 sau mai multe comenzi de mașină (de exemplu, Prolog). În plus, limbile sursă folosesc destul de des o tastare strictă a datelor, realizată prin descrierea lor preliminară. Programarea se poate baza nu pe codarea unui algoritm, ci pe gândirea atentă la structurile sau clasele de date. Procesul de traducere din astfel de limbi se numește de obicei compilare, iar limbile sursă sunt de obicei clasificate ca limbaje de programare de nivel înalt (sau limbi de nivel înalt). Abstracția unui limbaj de programare din sistemul de comandă al computerului a condus la crearea independentă a unei largi varietati de limbaje axate pe rezolvarea unor probleme specifice. Au apărut limbi pentru calcule științifice, calcule economice, acces la baze de date și altele.

    Interpret - un program sau un dispozitiv care realizează traducerea operator cu operator și execuția programului sursă. Spre deosebire de un compilator, un interpret nu produce un program în limbaj mașină ca rezultat. După ce a recunoscut o comandă în limba sursă, o execută imediat. Atât compilatorii, cât și interpreții folosesc aceleași metode pentru analiza codului sursă al unui program. Dar interpretul vă permite să începeți procesarea datelor după ce ați scris chiar și o singură comandă. Acest lucru face ca procesul de dezvoltare și depanare a programelor să fie mai flexibil. În plus, absența codului de ieșire a mașinii face posibilă să nu „aglomerați” dispozitivele externe cu fișiere suplimentare, iar interpretul în sine poate fi adaptat destul de ușor la orice arhitectură de mașină, fiind dezvoltat o singură dată într-un limbaj de programare utilizat pe scară largă. Prin urmare, limbaje interpretate precum Java Script și VB Script au devenit larg răspândite. Dezavantajul interpreților este viteza redusă de execuție a programului. De obicei, programele interpretate rulează de 50 până la 100 de ori mai lent decât programele native.

    Un emulator este un program sau un instrument software și hardware care oferă posibilitatea, fără reprogramare, de a executa pe un computer dat un program care utilizează coduri sau metode de efectuare a operațiunilor care sunt diferite de computerul dat. Un emulator este similar cu un interpret prin faptul că execută direct un program scris într-o anumită limbă. Cu toate acestea, cel mai adesea este limbajul mașinii sau codul intermediar. Ambele reprezintă instrucțiuni în cod binar care pot fi executate imediat după ce codul de operare este recunoscut. Spre deosebire de programele text, nu este nevoie să recunoașteți structura programului sau să selectați operanzi.

    Emulatoarele sunt folosite destul de des pentru o varietate de scopuri. De exemplu, atunci când se dezvoltă noi sisteme de calcul, este creat mai întâi un emulator care rulează programe dezvoltate pentru computere care încă nu există. Acest lucru vă permite să evaluați sistemul de comandă și să dezvoltați software-ul de bază chiar înainte ca hardware-ul corespunzător să fie creat.

    Foarte des, un emulator este folosit pentru a rula programe vechi pe computere noi. De obicei, calculatoarele mai noi sunt mai rapide și au periferice mai bune. Acest lucru vă permite să emulați programe mai vechi mai eficient decât să le rulați pe computere mai vechi.

    Un transcoder este un program sau un dispozitiv software care traduce programe scrise în limbajul mașină al unui computer în programe în limbajul mașină al altui computer. Dacă emulatorul este un analog mai puțin inteligent al interpretului, atunci transcoderul acționează în aceeași calitate în raport cu compilatorul. În mod similar, codul mașină sursă (și de obicei binar) sau o reprezentare intermediară este convertit în alt cod similar cu o singură instrucțiune și fără nicio analiză generală a structurii programului sursă. Transcodificatoarele sunt utile atunci când se transferă programe de la o arhitectură de computer la alta. Ele pot fi, de asemenea, utilizate pentru a reconstrui textul programului de limbaj de nivel înalt din codul binar existent.

    Un macroprocesor este un program care înlocuiește o secvență de caractere cu alta. Acesta este un tip de compilator. Acesta generează textul de ieșire prin procesarea inserțiilor speciale situate în textul sursă. Aceste inserții sunt concepute într-un mod special și aparțin unor constructe ale unui limbaj numit macrolimbaj. Macroprocesoarele sunt adesea folosite ca suplimente la limbajele de programare, crescând funcționalitatea sistemelor de programare. Aproape orice asamblator conține un macroprocesor, care crește eficiența dezvoltării programelor de mașină. Astfel de sisteme de programare sunt de obicei numite macroasambleri.

    Macroprocesoarele sunt folosite și cu limbaje de nivel înalt. Ele măresc funcționalitatea limbilor precum PL/1, C, C++. Macroprocesoarele sunt utilizate pe scară largă în C și C++, facilitând scrierea programelor. Macroprocesoarele îmbunătățesc eficiența programării fără a modifica sintaxa sau semantica limbajului.

    Sintaxa este un set de reguli ale unui limbaj care determină formarea elementelor sale. Cu alte cuvinte, acesta este un set de reguli pentru formarea unor secvențe semnificative din punct de vedere semantic de simboluri într-o anumită limbă. Sintaxa este specificată folosind reguli care descriu conceptele unui limbaj. Exemple de concepte sunt: ​​variabilă, expresie, operator, procedură. Succesiunea conceptelor și utilizarea lor acceptabilă în reguli determină structurile corecte sintactic care formează programe. Ierarhia obiectelor, și nu modul în care interacționează între ele, este definită prin sintaxă. De exemplu, o instrucțiune poate apărea doar într-o procedură, o expresie într-o instrucțiune, o variabilă poate consta dintr-un nume și indici opționali etc. Sintaxa nu este asociată cu astfel de fenomene în program precum „sărirea la o etichetă inexistentă” sau „o variabilă cu numele dat nu este definită”. Asta face semantica.

    Semantică - reguli și condiții care determină relațiile dintre elementele limbajului și semnificațiile lor semantice, precum și interpretarea sensului semnificativ al construcțiilor sintactice ale limbajului. Obiectele unui limbaj de programare nu sunt doar plasate în text în conformitate cu o anumită ierarhie, ci sunt și interconectate suplimentar prin alte concepte care formează diverse asocieri. De exemplu, o variabilă, pentru care sintaxa definește o locație validă doar în declarații și în unele instrucțiuni, are un tip specific, poate fi folosită cu un număr limitat de operații, are o adresă, o dimensiune și trebuie declarată înainte de a putea fi folosit în program.

    Un parser este o componentă a compilatorului care verifică declarațiile sursă pentru conformitatea cu regulile sintactice și semantica unui anumit limbaj de programare. În ciuda numelui său, analizatorul verifică atât sintaxa, cât și semantica. Este format din mai multe blocuri, fiecare dintre ele își rezolvă propriile probleme. Acesta va fi discutat mai detaliat atunci când se descrie structura traducătorului. programare în limbajul compilatorului translator

    Orice traducător îndeplinește următoarele sarcini principale:

    • - analizează programul tradus, în special determină dacă acesta conține erori de sintaxă;
    • - generează un program de ieșire (numit adesea program obiect) în limbajul de comandă al mașinii;
    • - alocă memorie pentru programul obiect.1.1 Interpreţi

    Un avantaj des citat al implementării interpretative este că permite „modul imediat”. Modul Direct vă permite să întrebați computerului o problemă precum PRINT 3.14159*3/2.1 și vă returnează răspunsul imediat ce apăsați ENTER (acest lucru vă permite să utilizați un computer de 3.000 USD ca calculator de 10 USD). În plus, interpreții au atribute speciale care facilitează depanarea. Puteți, de exemplu, să întrerupeți procesarea unui program interpret, să afișați conținutul anumitor variabile, să parcurgeți programul și apoi să continuați execuția.

    Ceea ce le place cel mai mult programatorilor la interpreți este capacitatea de a obține un răspuns rapid. Nu este nevoie de compilare aici, deoarece interpretul este întotdeauna gata să interfereze cu programul dumneavoastră. Tastați RUN și rezultatul celei mai recente modificări apare pe ecran.

    Cu toate acestea, limbile interpreților au dezavantaje. Este necesar, de exemplu, să aveți o copie a interpretului în memorie în orice moment, în timp ce multe dintre capacitățile interpretului și, prin urmare, capacitățile acestuia, pot să nu fie necesare pentru execuția unui anumit program.

    Un dezavantaj subtil al interpreților este că aceștia tind să descurajeze un stil bun de programare. Deoarece comentariile și alte detalii oficializate ocupă o cantitate semnificativă de memorie de program, oamenii tind să nu le folosească. Diavolul este mai puțin furios decât un programator care lucrează în interpretul BASIC și încearcă să introducă un program de 120K în memoria de 60K. dar cel mai rău lucru este că interpreții se mișcă încet.

    Ei petrec prea mult timp încercând să-și dea seama ce să facă în loc să facă efectiv munca. La executarea instrucțiunilor de program, interpretul trebuie mai întâi să scaneze fiecare instrucțiune pentru a-i citi conținutul (ce îmi cere această persoană să fac?) și apoi să efectueze operația solicitată. Operatorii din bucle sunt scanați excesiv.

    Luați în considerare programul: în interpretor BASIC 10 FOR N=1 TO 1000 20 PRINT N,SQR(N) 30 NEXT N prima dată când parcurgeți acest program, BAZ Interpreter trebuie să descopere ce înseamnă linia 20:

    • 1. convertiți variabila numerică N în șir
    • 2. trimiteți un șir pe ecran
    • 3. treceți la următoarea zonă de imprimare
    • 4. calculați rădăcina pătrată a lui N
    • 5. convertiți rezultatul într-un șir
    • 6. trimiteți un șir pe ecran

    În timpul celei de-a doua treceri a ciclului, toată această rezolvare se repetă din nou, deoarece toate rezultatele studierii acestei linii în urmă cu câteva milisecunde sunt complet uitate. Și așa mai departe pentru toate următoarele 998 de treceri. Este destul de evident că dacă ai putea separa cumva faza de scanare/înțelegere de faza de execuție ai avea un program mai rapid. Și tocmai pentru asta sunt compilatorii.