Kääntäjä on... Kääntäjätyypit. Ohjelman muuntaminen ja lähettäminen. Kääntäjä: peruskäsitteet. LL(k) - kielet ja kieliopit

Kääntääkseen kielestä toiselle ohjelmat, kuten ihmiset, vaativat kääntäjän tai tieteellisesti puhuen kääntäjän.

Kääntäjä: peruskäsitteet

Tällainen ohjelma kääntäjänä on lingvistinen esitys laskuista I ->P ->P (i). Tulkki on ohjelma, jonka syötteenä on ohjelma P jossa on syötetietoa X. Se suorittaa P:n X:llä: I(P, x) = P(x) On vain yksi kääntäjä, joka pystyy suorittamaan kaiken mahdolliset ohjelmat(joka voidaan esittää muodollisessa järjestelmässä). Tämä on Turingin erittäin merkittävä ja syvällinen löytö. Prosessori on konekielisten ohjelmien tulkki. Kirjoita tulkkeja kielille korkeatasoinen, ovat yleensä liian kalliita, joten ne käännetään muotoon, joka on helpompi tulkita. Joillakin kääntäjätyypeillä on hyvin outoja nimiä. Ohjelma kääntää assembler-ohjelmat kielelle konekieli. Kääntäjän avulla voit kääntää korkean tason kielestä alemman tason kielelle. Kääntäjä on ohjelma, joka ottaa syötteeksi ohjelman jollain kielellä S ja tuottaa käsittelyn jälkeen ohjelman kielellä T. Molemmilla on siis sama semantiikka: P->X->Q. Siten mille tahansa xP(x)=Q(x). Koko ohjelman kääntämistä tulkituksi kutsutaan esisuorituksen käännökseksi tai AOT-käännökseksi. AOT-kääntäjiä voidaan käyttää peräkkäin. Viimeinen on hyvin usein kokoaja. Joten katsotaanpa esimerkkiä: Lähdekoodi -> Kääntäjä (kääntäjä) -> Kokoonpanokoodi -> Kokoonpanoohjelma (kääntäjä) -> Konekoodi -> CPU (tulkki). Dynaaminen tai online-käännös tapahtuu, kun osa ohjelmasta käännetään samalla kun muut aiemmin käännetyt osat suoritetaan. JIT-kääntäjät muistavat, mitä he ovat jo tehneet aiemmin, jotta ne eivät toista lähdekoodia uudestaan ​​​​ja uudestaan. Ne pystyvät jopa adaptiiviseen käännökseen ja uudelleenkäännökseen, joka perustuu ohjelman ajonaikaisen ympäristön käyttäytymiseen. Monet kielet tarjoavat mahdollisuuden suorittaa koodia ohjelman käännöksen aikana ja myös kääntää uutta koodia ohjelman ollessa käynnissä.

Lähetys: vaiheet

Käännösprosessi koostuu synteesi- ja analyysivaiheista. Kaavamaisesti tämä prosessi näyttää suunnilleen tältä: Lähdekoodi -> Analyzer -> Käsitteellinen esitys -> Syntetisaattori (generaattori) -> Kohdekoodi. Tämä johtuu seuraavista syistä:

- mikään muu menetelmä ei yksinkertaisesti sovi;

— sanoilla käännös ei yksinkertaisesti toimi.

Voit käyttää seuraavaa suunnitteluratkaisua: jos haluat kirjoittaa kääntäjiä M lähdekielelle ja N kohdekielelle, sinun tarvitsee kirjoittaa vain M+N yksinkertaisia ​​ohjelmia(puolikääntäjät) MxN täysien (monimutkaisten) kääntäjien sijaan. Käytännössä on kuitenkin melko harvinaista, että käsitteellinen esitys on tarpeeksi ilmeikäs ja voimakas kattamaan kaikki olemassa olevat kohde- ja lähdekielet. Vaikka jotkut käyttäjät pääsivät lähelle tätä. Todelliset kääntäjät käyvät läpi monia eri vaiheita. Kun luot oman kääntäjän, sinun ei tarvitse tehdä uudelleen kaikkea sitä kovaa työtä, jonka ohjelmoijat ovat jo tehneet luodessaan generaattoreita ja näkymiä. Voit kääntää kielesi suoraan JavaScriptiksi tai C:ksi ja käyttää olemassa olevia C-kielen kääntäjiä ja JavaScript-moottoreita tehdäksesi loput. Voit myös käyttää olemassa olevia välinäkymiä ja virtuaalikoneita.

Kääntäjän tallennus

Kääntäjä voi olla tekninen työkalu tai ohjelma, joka käyttää kolmea kieltä: lähde, kohde, perusta. Ne voidaan kirjoittaa T-kirjaimella sijoittamalla lähde vasemmalle, kohde oikealle ja kanta alapuolelle. Kääntäjätyyppejä on yhteensä kolmenlaisia.

  1. Kääntäjä on itsekääntäjä, jos sen lähdekieli vastaa peruskieltä.
  2. Kääntäjää, jonka kohdekieli on yhtä suuri kuin peruskieli, kutsutaan itse asuvaksi.
  3. Jos kohde- ja peruskieli ovat erilaiset, kääntäjä on ristikääntäjä.

Miksi on tärkeää tehdä ero tämäntyyppisten kääntäjien välillä? Vaikka et koskaan rakentaisikaan todella hyvää kääntäjää, on hyvä idea oppia sen taustalla olevasta tekniikasta, koska kaikki tähän tarkoitukseen käytetyt käsitteet ovat kaikkialla tietokantakyselykielissä, tekstin muotoilussa ja edistyneissä. tietokonearkkitehtuurit ah, graafiset käyttöliittymät, yleiset optimointiongelmat, konekäännökset, ohjaimet ja virtuaalikoneet. Lisäksi, jos sinun on kirjoitettava esiprosessoreita, lataajia, kokoajia, virheenkorjaajia tai profiloijia, sinun on suoritettava kaikki samat vaiheet kuin kirjoittaessasi kääntäjää. Voit myös oppia parempia tapoja kirjoittaa ohjelmia, koska kääntäjän kehittäminen ohjelmointikielelle tarkoittaa parempaa ymmärrystä sen moniselitteisyydestä ja hienovaraisuudesta. Oppimalla yleisradiotoiminnan yleiset periaatteet voit tulla hyvä suunnittelija Kieli. Mutta onko sillä oikeasti väliä? Kuinka siistiä kieli on, jos sitä ei voida toteuttaa tehokkaasti?

Laajamittainen tekniikka

Kääntäjäteknologia kattaa laajan valikoiman tietojenkäsittelytieteen eri osa-alueita. Se sisältää muodollisen kielen teorian, kieliopin, tietokonearkkitehtuurin, jäsentämisen, laskettavuuden, käskyjoukot, CISC:n tai RISC:n, liukuhihnan, kellojaksot, ytimet jne. sekä sekvenssiohjauksen, rekursion, ehdollisen suorituksen, funktionaalisen hajotuksen, iteroinnin, modulaarisuuden, synkronointi, metaohjelmointi, vakiot, laajuus, mallit, lähtötyyppi, huomautukset, prototyypit, streamit, postilaatikot, monadit, jokerimerkit, jatkot, tapahtumamuisti, säännölliset lausekkeet, polymorfismi, periytyminen, parametritilat jne. . Kääntäjän luomiseksi sinun on myös ymmärrettävä abstrakteja ohjelmointikieliä, algoritmeja ja tietorakenteita, säännöllisiä lausekkeita, graafisia algoritmeja ja dynaamista ohjelmointia.

Kääntäjän suunnittelu. Mahdolliset ongelmat, joka syntyy luotaessa oikeaa kääntäjää

Mitä ongelmia lähdekielen kanssa voi ilmetä? Onko helppo koota? Onko tähän olemassa esiprosessoria? Miten tyyppejä käsitellään? Millaista kääntäjän kulkujen ryhmittelyä käytetään - yksivaiheisia vai monikertaisia? Myös haluttu optimointiaste ansaitsee erityistä huomiota. Nopea ja likainen ohjelman lähetys ilman optimointia tai vain vähän optimointia voi olla normaalia. Liiallinen optimointi voi kuitenkin hidastaa kääntäjää ajon aikana paras koodi saattaa olla sen arvoista.

Virheiden havaitsemisprosentti. Onko kääntäjän pakko pysähtyä ensimmäiseen virheeseen? Milloin hänen pitäisi lopettaa? Pitäisikö sinun luottaa kääntäjään korjaamaan virheet?

Tarvittava työkalusarja

Jos sinun tapauksessasi lähdekieli ei ole liian pieni, analysaattorigeneraattori ja skanneri ovat välttämättömiä. On myös erityisiä koodigeneraattoreita, mutta ne eivät ole kovin yleisiä.

Mitä tulee luotavan kohdekoodin tyyppiin, sinun on valittava puhdas, lisätty tai virtuaalikonekoodi. Voit myös kirjoittaa syöttöosan, joka luo suosittuja välinäkymiä, kuten LLVM, JVM, RTL. Voit myös tehdä käännöksen lähteestä lähdekoodiin Java Scriptillä tai C:llä. Jos puhumme kohdekoodimuodosta, tässä voit valita kannettavan konekoodin, muistikuvan konekoodin, kokoonpanokielen.

Uudelleenkohdistaminen

Käytettäessä suurta määrää generaattoreita olisi kiva saada yhteinen tuloosa. Myös tästä syystä monille tuloosille on parempi olla yksi generaattori.

Kääntäjän komponentit

Listataan tärkeimmät toiminnalliset komponentit kääntäjä, joka luo konekoodin, jos tulosohjelma on C-kielellä kirjoitettu ohjelma tai virtuaalikone:

— syöttöohjelma siirtyy leksikaaliseen analysaattoriin tai toisin sanoen skanneriin, joka muuntaa sen merkkivirraksi;

— syntaksianalysaattori (parser) rakentaa niistä abstraktin syntaksipuun;

- semanttinen analysaattori hajoaa semanttista tietoa ja tarkistaa puun solmut virheiden varalta;

— tuloksena muodostuu semanttinen graafi. Tämä termi viittaa abstraktiin syntaksipuuhun, jossa on perustettuja linkkejä ja lisäominaisuuksia;

— välikoodigeneraattori rakentaa vuokaavion (tuples ryhmitellään päälohkoihin);

— koneesta riippumaton optimoija suorittaa paikallista ja globaalia optimointia, mutta pysyy pääosin aliohjelmien puitteissa yksinkertaistaen laskelmia ja vähentäen redundanttia koodia. Tuloksena tulisi olla muokattu vuokaavio;

— Peruslohkojen yhdistämiseksi suoraviivaiseksi koodiksi ohjauksen siirrolla käytetään kohdekoodigeneraattoria. Se luo objektitiedoston assemblerissä visuaalisilla rekistereillä, mikä ei välttämättä ole kovin tehokasta;

— koneriippuvaista optimoija-linkkeriä käytetään jakamaan muistia virtuaalirekisterien välillä ja suorittamaan käskyjen ajoitusta. Se myös muuntaa kokoonpanokielellä kirjoitetun ohjelman todelliseksi kokoonpanokieleksi liukuhihnan avulla.

— käytetään virheenilmaisualijärjestelmiä ja symbolitaulukon hallintaa;

— skannaus ja leksikaalinen analyysi. Skanneria käytetään muuttamaan lähdekoodin merkkivirta merkkivirraksi, poistamalla kommentit, välilyönnit ja laajentavat makrot. Melko usein skannerit kohtaavat seuraavan ongelman: otetaanko huomioon sisennykset, kirjainkoot vai sisäkkäiset kommentit.

Skannauksen aikana mahdollisesti ilmeneviä virheitä kutsutaan leksikaalisiksi. Näitä ovat seuraavat:

— aakkosista puuttuvat symbolit;

— rivin tai sanan merkkien lukumäärän ylittäminen;

- ei-suljettu merkkijono literaali tai merkki;

- tiedoston loppu kommentissa.

Jäsentämistä tai jäsentämistä käytetään merkkijonon muuntamiseen abstraktiksi syntaksipuuksi. Tässä tapauksessa jokainen puusolmu tallennetaan objektina, jolla on nimetyt kentät. Monet heistä ovat itse puun solmuja. Tässä vaiheessa ei ole jaksoja. Kun luot jäsentimen, sinun on ensin kiinnitettävä huomiota kieliopin monimutkaisuustasoon (LR tai LL) ja selvitettävä, onko olemassa yksiselitteisyyssääntöjä. Itse asiassa jotkut kielet vaativat semanttista analyysiä. Tässä vaiheessa esiintyviä virheitä kutsutaan syntaktisiksi virheiksi.

Semanttinen analyysi

Semanttista analyysiä suoritettaessa on ensinnäkin tarkistettava kelpoisuussäännöt ja linkitettävä syntaksipuun osat semanttiseksi graafiksi lisäämällä operaatio implisiittiseen tyyppivaluon, nimiviittauksen erotteluun jne. On selvää, että eri ohjelmointikielillä on erilaiset kelpoisuussäännöt. Käännettäessä Java-kaltaisia ​​kieliä kääntäjät voivat kohdata seuraavat virheet:

— sen soveltamisalaan kuuluvan muuttujan useat ilmoitukset;

— esteettömyyssääntöjen rikkominen;

— viittaukset ilmoittamattomaan nimeen;

— liian suuri tai päinvastoin riittämätön argumenttien määrä menetelmää kutsuttaessa;

- kelpaamaton syöte.

Sukupolvi

Luomalla välikoodia tuotetaan vuokaavio, joka koostuu peruslohkoihin ryhmitellyistä monikoista. Koodin luomisen jälkeen saadaan oikea konekoodi. Ensimmäinen askel RISC-koneiden perinteisissä kääntäjissä on luoda kokoaja, jossa on ääretön määrä virtuaalisia rekistereitä. Tämä ei todennäköisesti tapahdu CISC-koneissa.

Ohjelmointikielet voidaan jakaa käännettyihin ja tulkittuihin.

Käännetyllä kielellä oleva ohjelma muunnetaan (käännetään) erityistä kääntäjäohjelmaa käyttäen ohjejoukoksi tämän tyyppistä prosessori (konekoodi) ja kirjoitetaan sitten suoritettavaan moduuliin, joka voidaan käynnistää suoritettavaksi erillinen ohjelma. Toisin sanoen kääntäjä kääntää ohjelman lähdekoodin korkean tason ohjelmointikielestä prosessorin käskyjen binäärikoodeiksi.

Jos ohjelma on kirjoitettu tulkitulla kielellä, tulkki suorittaa (tulkitaan) suoraan lähdetekstin ilman edeltävää käännöstä. Tässä tapauksessa ohjelma pysyy kohdassa alkuperäinen kieli eikä sitä voida käynnistää ilman tulkkia. Voimme sanoa, että tietokoneen prosessori on konekoodin tulkki.

Lyhyesti sanottuna kääntäjä kääntää ohjelman lähdekoodin konekielelle välittömästi ja kokonaisuudessaan luoden samalla erillisen suoritettava ohjelma, ja tulkki suorittaa lähdetekstin suoraan ohjelman suorituksen aikana.

Jako koottuihin ja tulkkaisiin kieliin on hieman mielivaltaista. Joten mille tahansa perinteisesti käännetylle kielelle, kuten Pascalille, voit kirjoittaa tulkin. Lisäksi useimmat nykyaikaiset "puhtaat" tulkit eivät suorita kielirakenteita suoraan, vaan pikemminkin kokoavat ne johonkin korkean tason väliesitykseen (esimerkiksi muuttujan viittausten poistamisella ja makrolaajennuksella).

Kääntäjä voidaan luoda mille tahansa tulkittavalle kielelle - esimerkiksi Lisp-kieli, joka on natiivitulkkaus, voidaan kääntää ilman rajoituksia. Ohjelman suorituksen aikana luotu koodi voidaan myös kääntää dynaamisesti suorituksen aikana.

Pääsääntöisesti käännetyt ohjelmat suoritetaan nopeammin eivätkä vaadi lisäohjelmia suorittaakseen, koska ne on jo käännetty konekielelle. Samanaikaisesti joka kerta kun ohjelman tekstiä muutetaan, se on käännettävä uudelleen, mikä aiheuttaa vaikeuksia kehityksen aikana. Lisäksi käännetty ohjelma voidaan suorittaa vain samantyyppisellä tietokoneella ja yleensä samalla käyttöjärjestelmällä, jota varten kääntäjä on suunniteltu. Luoda suoritettava tiedosto erityyppistä konetta varten tarvitaan uusi kokoelma.

Tulkituilla kielillä on tiettyjä erityispiirteitä lisäominaisuuksia(katso yllä), lisäksi niissä olevat ohjelmat voidaan käynnistää heti muutoksen jälkeen, mikä helpottaa kehitystä. Tulkitulla kielellä olevaa ohjelmaa voidaan usein ajaa erityyppisissä koneissa ja käyttöjärjestelmissä ilman lisäponnistuksia.

Tulkitut ohjelmat toimivat kuitenkin huomattavasti hitaammin kuin käännetyt, eikä niitä voida suorittaa ilman lisätulkkiohjelmaa.

Jotkut kielet, kuten Java ja C#, ovat käännetyn ja tulkitun välissä. Ohjelmaa ei nimittäin ole käännetty konekieleksi, vaan matalan tason koneriippumattomaksi koodiksi, tavukoodiksi. Tämän jälkeen virtuaalikone suorittaa tavukoodin. Tulkintaa käytetään yleensä tavukoodin suorittamiseen, vaikka sen yksittäiset osat voidaan kääntää konekoodiksi suoraan ohjelman suorituksen aikana JIT-käännön (JIT) avulla ohjelman nopeuttamiseksi. Javassa tavukoodin suorittaa Java Virtual Machine (JVM), C#:lle - Common Language Runtime.

Tämä lähestymistapa antaa tietyssä mielessä mahdollisuuden käyttää sekä tulkkien että kääntäjien etuja. On myös syytä mainita alkuperäinen Forth-kieli, jossa on sekä tulkki että kääntäjä.

Koska ohjelmointikielellä kirjoitettu teksti on tietokoneelle käsittämätöntä, se on käännettävä konekoodiksi. Tätä ohjelman käännöstä ohjelmointikielestä konekoodikieleksi kutsutaan käännökseksi, ja sen suorittavat erikoisohjelmat - kääntäjät.

Kääntäjä - palveluohjelma, joka muuntaa syöttöohjelmointikielellä tarjotun lähdeohjelman objektikielellä toimitetuksi työohjelmaksi.

Tällä hetkellä kääntäjät jaetaan kolmeen pääryhmään: kokoajat, kääntäjät ja tulkit.

Assembler on järjestelmän apuohjelma, joka muuntaa symboliset rakenteet konekielisiksi komentoiksi. Assemblereiden erityispiirre on, että ne kääntävät sanatarkasti yhden symbolisen käskyn yhdeksi konekäskyksi. Siten kokoonpanokieli (kutsutaan myös autokoodiksi) on suunniteltu helpottamaan tietokoneen komentojärjestelmän havaitsemista ja nopeuttamaan ohjelmointia tässä komentojärjestelmässä. Ohjelmoijan on paljon helpompi muistaa konekäskyjen muistomerkki kuin niiden binäärikoodi.

Samaan aikaan kokoonpanokieli sisältää konekäskyjen analogien lisäksi monia lisädirektiivejä, jotka helpottavat erityisesti tietokoneresurssien hallintaa, toistuvien fragmenttien kirjoittamista ja monimoduuliohjelmien rakentamista. Siksi kielen ilmaisukyky on paljon rikkaampi kuin pelkkä symbolinen koodauskieli, mikä parantaa huomattavasti ohjelmoinnin tehokkuutta.

Kääntäjä on palveluohjelma, joka kääntää lähdeohjelmointikielellä kirjoitetun ohjelman konekieleksi. Aivan kuten kokoaja, kääntäjä muuntaa ohjelman kielestä toiselle (useimmiten tietyn tietokoneen kielelle). Samaan aikaan lähdekielen komennot eroavat merkittävästi organisaatioltaan ja teholtaan konekielisistä komennoista. On kieliä, joissa yksi lähdekielen komento käännetään 7-10 konekäskyksi. On kuitenkin myös kieliä, joissa jokaisessa komennossa voi olla 100 tai enemmän konekäskyä (esimerkiksi Prolog). Lisäksi lähdekielet käyttävät melko usein tiukkaa tietojen kirjoittamista, jotka suoritetaan niiden alustavan kuvauksen kautta. Ohjelmointi ei välttämättä perustu algoritmin koodaamiseen, vaan tietorakenteiden tai luokkien tarkkaan miettimiseen. Tällaisista kielistä kääntämistä kutsutaan yleensä kääntämiseksi, ja lähdekielet luokitellaan yleensä korkean tason ohjelmointikieliksi (tai korkean tason kieliksi). Ohjelmointikielen poistaminen tietokoneen komentojärjestelmästä johti useiden ratkaisukeskeisten kielten itsenäiseen luomiseen erityisiä tehtäviä. Kielet ovat ilmestyneet tieteellisiin laskelmiin, taloudellisiin laskelmiin, tietokantoihin pääsyyn ja muihin.

Tulkki - ohjelma tai laite, joka suorittaa operaattorikohtaisen käännöksen ja lähdeohjelman suorittamisen. Toisin kuin kääntäjä, tulkki ei tuota konekielistä ohjelmaa tulosteena. Kun se on tunnistanut komennon lähdekielellä, se suorittaa sen välittömästi. Sekä kääntäjät että tulkit käyttävät samoja menetelmiä ohjelman lähdekoodin analysointiin. Mutta tulkin avulla voit aloittaa tietojen käsittelyn yhden komennon kirjoittamisen jälkeen. Tämä tekee ohjelmien kehittämis- ja virheenkorjausprosessista joustavamman. Lisäksi ulostulokonekoodin puuttuminen mahdollistaa sen, että ulkoisia laitteita ei "täytetä" lisätiedostoilla, ja itse tulkki on melko helposti sovitettavissa mihin tahansa konearkkitehtuuriin, koska se on kehitetty vain kerran laajasti käytetyllä ohjelmointikielellä. Siksi tulkitut kielet, kuten Java Script ja VB Script, ovat yleistyneet. Tulkkien haittana on ohjelman alhainen suoritusnopeus. Tyypillisesti tulkitut ohjelmat toimivat 50-100 kertaa hitaammin kuin alkuperäiset ohjelmat.

Emulaattori on ohjelma tai ohjelmisto- ja laitteistotyökalu, joka mahdollistaa ilman uudelleenohjelmointia suorittaa tietyllä tietokoneella ohjelman, joka käyttää koodeja tai menetelmiä toimintojen suorittamiseen, jotka eroavat annetusta tietokoneesta. Emulaattori on samanlainen kuin tulkki siinä mielessä, että se suorittaa suoraan tietyllä kielellä kirjoitetun ohjelman. Useimmiten se on kuitenkin konekieli tai välikoodi. Molemmat edustavat binäärikoodin ohjeita, jotka voidaan suorittaa välittömästi toimintakoodin tunnistamisen jälkeen. Toisin kuin tekstiohjelmissa, ei tarvitse tunnistaa ohjelman rakennetta tai valita operandeja.

Emulaattoreita käytetään melko usein erilaisiin tarkoituksiin. Esimerkiksi uusia laskentajärjestelmiä kehitettäessä luodaan ensin emulaattori, joka ajaa ohjelmia, jotka on kehitetty tietokoneille, joita ei vielä ole olemassa. Näin voit arvioida komentojärjestelmän ja kehittää perusohjelmistoa jo ennen vastaavan laitteiston luomista.

Hyvin usein emulaattoria käytetään vanhojen ohjelmien suorittamiseen uusissa tietokoneissa. Tyypillisesti uudemmat tietokoneet ovat nopeampia ja niissä on paremmat oheislaitteet. Näin voit emuloida vanhempia ohjelmia tehokkaammin kuin ajaa niitä vanhemmissa tietokoneissa.

Transkooderi - ohjelma tai ohjelmistolaite, kääntää yhden tietokoneen konekielellä kirjoitetut ohjelmat toisen tietokoneen konekielisiksi ohjelmiksi. Jos emulaattori on tulkin vähemmän älykäs analogi, niin transkooderi toimii samassa kapasiteetissa kääntäjään nähden. Vastaavasti lähdekoodi (ja yleensä binäärinen) konekoodi tai väliesitys muunnetaan toiseksi vastaavaksi koodiksi yhdellä käskyllä ​​ja ilman lähdeohjelman rakenteen yleistä analyysiä. Transkooderit ovat hyödyllisiä siirrettäessä ohjelmia tietokonearkkitehtuurista toiseen. Niiden avulla voidaan myös rekonstruoida korkean tason kieliohjelmatekstiä olemassa olevasta binäärikoodista.

Makroprosessori on ohjelma, joka korvaa yhden merkkisarjan toisella. Tämä on eräänlainen kääntäjä. Se tuottaa tulostekstiä prosessoimalla erityiset insertit sijaitsee lähdetekstissä. Nämä liitteet on suunniteltu erityisellä tavalla ja kuuluvat makrokieleksi kutsutun kielen rakenteisiin. Makroprosessoreita käytetään usein ohjelmointikielten lisäosina, mikä lisää ohjelmointijärjestelmien toimivuutta. Lähes kaikki kokoonpanolaitteet sisältävät makroprosessorin, mikä lisää koneohjelmien kehittämisen tehokkuutta. Tällaisia ​​ohjelmointijärjestelmiä kutsutaan yleensä makrokokoajiksi.

Makroprosessoreita käytetään myös korkean tason kielillä. Ne lisäävät kielten, kuten PL/1, C, C++, toimivuutta. Makroprosessorit ovat erityisen laajasti käytössä C- ja C++-kielissä, mikä helpottaa ohjelmien kirjoittamista. Makroprosessorit parantavat ohjelmoinnin tehokkuutta muuttamatta kielen syntaksia tai semantiikkaa.

Syntaksi on joukko kielen sääntöjä, jotka määräävät sen elementtien muodostumisen. Toisin sanoen tämä on joukko sääntöjä semanttisesti merkittävien symbolijonojen muodostamiseksi tietyllä kielellä. Syntaksi määritetään säännöillä, jotka kuvaavat kielen käsitteitä. Esimerkkejä käsitteistä ovat: muuttuja, lauseke, operaattori, menettely. Käsitteiden järjestys ja niiden hyväksyttävä käyttö säännöissä määrittää syntaktisesti oikeat rakenteet, jotka muodostavat ohjelmia. Syntaksin avulla määritellään objektien hierarkia, ei tapa, jolla ne ovat vuorovaikutuksessa keskenään. Esimerkiksi lause voi esiintyä vain proseduurissa, lauseke käskyssä, muuttuja voi koostua nimestä ja valinnaisista indekseistä jne. Syntaksia ei liitetä sellaisiin ohjelman ilmiöihin kuin "hyppy ei-olemassa olevaan nimikkeeseen" tai "muuttujaa, jolla on annettu nimi, ei ole määritelty". Tätä semantiikka tekee.

Semantiikka - säännöt ja ehdot, jotka määrittävät kielen elementtien väliset suhteet ja niiden semanttiset merkitykset sekä kielen syntaktisten rakenteiden merkityksellisen merkityksen tulkinnan. Ohjelmointikielen objektit eivät vain sijoiteta tekstiin tietyn hierarkian mukaisesti, vaan ne myös liitetään toisiinsa muiden käsitteiden kautta, jotka muodostavat erilaisia ​​assosiaatioita. Esimerkiksi muuttuja, jolle syntaksi määrittää kelvollisen sijainnin vain ilmoituksissa ja joillakin lauseilla on tiettyä tyyppiä, voidaan käyttää rajoitetulla määrällä toimintoja, sillä on osoite, koko ja se on kuvattava ennen kuin sitä voidaan käyttää ohjelmassa.

Jäsentäjä on kääntäjäkomponentti, joka tarkistaa lähdelausekkeiden yhteensopivuuden tietyn ohjelmointikielen syntaktisten sääntöjen ja semantiikan kanssa. Nimestään huolimatta analysaattori tarkistaa sekä syntaksin että semantiikan. Se koostuu useista lohkoista, joista jokainen ratkaisee omat ongelmansa. Sitä käsitellään tarkemmin kuvattaessa kääntäjän rakennetta. kääntäjän kääntäjäkieliohjelmointi

Jokainen kääntäjä suorittaa seuraavat päätehtävät:

  • - analysoi käännetyn ohjelman, erityisesti määrittää, onko siinä syntaksivirheitä;
  • - luo tulosohjelman (kutsutaan usein objektiohjelmaksi) koneen komentokielellä;
  • - varaa muistia objektiohjelmalle.1.1 Tulkit

Yksi tulkinnan toteutuksen usein mainittu etu on, että se sallii "välittömän tilan". Suoratilassa voit kysyä tietokoneelta ongelman, kuten PRINT 3.14159*3/2.1, ja palauttaa vastauksen heti, kun painat ENTER-näppäintä (tämän avulla voit käyttää 3 000 dollarin tietokonetta 10 dollarin laskimena). Lisäksi tulkeilla on erityisiä ominaisuuksia, jotka helpottavat virheenkorjausta. Voit esimerkiksi keskeyttää tulkkiohjelman käsittelyn, näyttää tiettyjen muuttujien sisällön, selata ohjelmaa ja jatkaa sen jälkeen suorittamista.

Ohjelmoijat pitävät tulkkeissa eniten kyvystä saada nopea vastaus. Tässä ei ole tarvetta kääntää, koska tulkki on aina valmis häiritsemään ohjelmaasi. Kirjoita RUN ja tulos on sinun viimeinen mahdollisuus ilmestyy näytölle.

Tulkkikielillä on kuitenkin haittoja. On esimerkiksi välttämätöntä, että kopio tulkista on aina muistissa, kun taas monet tulkin ominaisuudet ja siten sen ominaisuudet eivät välttämättä ole välttämättömiä tietyn ohjelman suorittamiseksi.

Tulkkien hienovarainen haitta on, että heillä on tapana estää hyvä ohjelmointityyli. Koska kommentit ja muut formalisoidut yksityiskohdat vievät huomattavan määrän ohjelmamuistia, ihmiset eivät yleensä käytä niitä. Paholainen on vähemmän väkivaltainen kuin tulkin BASICissa työskentelevä ohjelmoija, joka yrittää saada 120 000 ohjelman 60 000 muistiin. mutta pahinta on, että tulkit liikkuvat hitaasti.

He viettävät liian paljon aikaa yrittäessään selvittää, mitä tehdä sen sijaan, että he todella tekisivät työtä. Esiintyessään ohjelman operaattorit, tulkin on ensin skannattava jokainen lause lukeakseen sen sisältö (mitä tämä henkilö pyytää minua tekemään?) ja sitten suoritettava pyydetty toiminto. Silmukoissa olevia operaattoreita tarkistetaan liikaa.

Harkitse ohjelmaa: tulkissa BASIC 10 FOR N=1 - 1000 20 PRINT N,SQR(N) 30 NEXT N kun käyt tämän ohjelman läpi ensimmäisen kerran, BASIC Interpreterin on selvitettävä, mitä rivi 20 tarkoittaa:

  • 1. Muunna numeerinen muuttuja N merkkijonoksi
  • 2. Lähetä merkkijono näytölle
  • 3. siirry seuraavalle tulostusalueelle
  • 4. laskea Neliöjuuri alkaen N
  • 5. Muunna tulos merkkijonoksi
  • 6. Lähetä merkkijono näytölle

Jakson toisella kierroksella kaikki tämä ratkaisu toistetaan uudelleen, koska kaikki tämän rivin tutkimisen tulokset muutama millisekunti sitten unohdetaan kokonaan. Ja niin edelleen kaikille seuraaville 998 passille. On aivan selvää, että jos voisit jotenkin erottaa skannaus/ymmärtämisvaiheen suoritusvaiheesta, saisit nopeamman ohjelman. Ja tätä varten kääntäjät ovat juuri sitä varten.

Kurinalan tavoitteet ja tavoitteet. Peruskäsitteet ja määritelmät. Yleiset ominaisuudet ohjelmointikielet ja kääntäjät. Kääntäjän yleinen rakenne. Vaihtoehdot kääntäjälohkojen vuorovaikutukseen.

Kurinalan tavoitteet ja tavoitteet

Tällä hetkellä keinotekoisia kieliä, jotka käyttävät tekstin esittämistä aihealueen kuvaamiseen, käytetään laajasti ohjelmoinnin lisäksi myös muilla alueilla. Niiden avulla kuvataan kaikenlaisten dokumenttien rakennetta, kolmiulotteisia virtuaalimaailmoja, graafisia käyttöliittymiä ja monia muita malleissa ja todellisessa maailmassa käytettävien objektien rakennetta. Jotta nämä tekstikuvaukset voitaisiin laatia oikein ja sitten tunnistaa ja tulkita oikein, käytetään erityisiä menetelmiä niiden analysointiin ja muuntamiseen. Menetelmät perustuvat kielten ja muodollisten kielioppien teoriaan sekä automaattien teoriaan. Ohjelmistojärjestelmiä, jotka on suunniteltu analysoimaan ja tulkitsemaan tekstejä, kutsutaan kääntäjiksi.

Huolimatta siitä, että tähän mennessä on kehitetty tuhansia eri kieliä ja niiden kääntäjiä, uusien sovellusten luominen tällä alalla ei pysähdy. Tämä johtuu sekä tietokonejärjestelmien tuotantotekniikan kehityksestä että tarpeesta ratkaista yhä monimutkaisempia sovellettavia ongelmia. Lisäksi kielten teorian ja muodollisten kielioppien elementtejä voidaan soveltaa muillakin eri aloilla, esimerkiksi kuvattaessa tietorakenteita, tiedostoja, kuvia, jotka esitetään ei tekstinä, vaan binäärimuodossa. Nämä menetelmät ovat hyödyllisiä myös omien kääntäjien kehittämisessä, vaikka vastaavia analogeja olisi jo olemassa. Tällainen kehitys voi johtua useista syistä, erityisesti toiminnallisista rajoituksista, lokalisoinnin puutteesta ja alhaisesta tehokkuudesta. Esimerkiksi yksi viimeisimmistä kehityksestä Microsoft on C#-ohjelmointikieli, ja yksi syy sen luomiseen on halu vähentää Java-ohjelmointikielen suosiota. On monia muita esimerkkejä, joissa oman kääntäjän kehittäminen voi olla merkityksellistä. Siksi kielten teorian ja muodollisten kielioppien perusteet sekä käytännölliset kääntäjien kehittämismenetelmät ovat tietojenkäsittelytieteen ja tietojenkäsittelytieteen insinöörikoulutuksen perusta.

Ehdotettu materiaali käsittelee kääntäjien kehittämismenetelmien perusteita ja sisältää tietoa, jota tarvitaan niiden toiminnan logiikan ja käytetyn matemaattisen laitteen (muodollisten kielten teoria ja kielioppi, metakielet) tutkimiseen. Sitä käytetään osana lukukauden pituisia luentokursseja, joita pidetään Krasnojarskin valtion teknillisen yliopiston tietotekniikan ja tietojenkäsittelytieteen tiedekunnan eri erikoisaloilla. Laboratoriotöiden aikana tehdään suora tutustuminen yksittäisiin kääntäjien luomismenetelmiin.

Kurssin tarkoitus: antaa tietoa kielten teorian ja muodollisten kielioppien perusteista, automaattien teoriasta, kääntäjien kehittämismenetelmistä.

Tämän tavoitteen saavuttamiseksi tieteenalan opetuksen aikana ratkaistaan ​​seuraavat tehtävät:

  1. Luentokurssi kattaa yleiset periaatteet käännösprosessin ja kääntäjien rakenteen järjestäminen. Tutkitaan kääntäjien rakentamisen teorian perusteita.
  2. Laboratoriotunneilla ja niiden aikana itsenäinen työ hankitun teoreettisen tiedon käytännön konsolidointi suoritetaan: yksinkertaisen ohjelmointikielen kääntäjää kehitetään.

Peruskäsitteet ja määritelmät

Suurin osa käsitellyistä määritelmistä on lainattu julkaisusta [ARNFTS Englanti-venäjä-saksa-ranska selittävä sanakirja tietotekniikasta ja tietojenkäsittelystä, 4132 termiä. Alla. toim. A.A. Dorodnitsyna. M.: 1978. 416 s.) ].

Kääntäjä - palveluohjelma, joka muuntaa syöteohjelmointikielellä tarjotun lähdeohjelman objektikielellä toimitetuksi työohjelmaksi.

Yllä oleva määritelmä koskee kaikentyyppisiä lähetysohjelmia. Jokaisella näistä ohjelmista voi kuitenkin olla omat ominaisuutensa lähetysprosessin järjestämisessä. Tällä hetkellä kääntäjät jaetaan kolmeen pääryhmään: kokoajat, kääntäjät ja tulkit.

Kokoaja - järjestelmän apuohjelma, joka muuntaa symboliset konstruktit konekielisiksi käskyiksi. Assemblereiden erityispiirre on, että ne kääntävät sanatarkasti yhden symbolisen käskyn yhdeksi konekäskyksi. Siten kokoonpanokieli (kutsutaan myös autokoodiksi) on suunniteltu helpottamaan tietokoneen komentojärjestelmän havaitsemista ja nopeuttamaan ohjelmointia tässä komentojärjestelmässä. Ohjelmoijan on paljon helpompi muistaa konekäskyjen muistomerkki kuin niiden binäärikoodi. Siksi suurin hyöty ei saavuteta lisäämällä yksittäisten komentojen tehoa, vaan lisäämällä niiden havainnoinnin tehokkuutta.

Samaan aikaan kokoonpanokieli sisältää konekäskyjen analogien lisäksi monia lisädirektiivejä, jotka helpottavat erityisesti tietokoneresurssien hallintaa, toistuvien fragmenttien kirjoittamista ja monimoduuliohjelmien rakentamista. Siksi kielen ilmaisukyky on paljon rikkaampi kuin pelkkä symbolinen koodauskieli, mikä parantaa huomattavasti ohjelmoinnin tehokkuutta.

Kääntäjä - on palveluohjelma, joka kääntää lähdeohjelmointikielellä kirjoitetun ohjelman konekieleksi. Aivan kuten kokoaja, kääntäjä muuntaa ohjelman kielestä toiselle (useimmiten tietyn tietokoneen kielelle). Samaan aikaan lähdekielen komennot eroavat merkittävästi organisaatioltaan ja teholtaan konekielisistä komennoista. On kieliä, joissa yksi lähdekielen komento käännetään 7-10 konekäskyksi. On kuitenkin myös kieliä, joissa jokaisessa komennossa voi olla 100 tai enemmän konekäskyä (esimerkiksi Prolog). Lisäksi lähdekielet käyttävät melko usein tiukkaa tietojen kirjoittamista, jotka suoritetaan niiden alustavan kuvauksen kautta. Ohjelmointi ei välttämättä perustu algoritmin koodaamiseen, vaan tietorakenteiden tai luokkien tarkkaan miettimiseen. Tällaisista kielistä kääntämistä kutsutaan yleensä kääntämiseksi, ja lähdekielet luokitellaan yleensä korkean tason ohjelmointikieliksi (tai korkean tason kieliksi). Ohjelmointikielen poistaminen tietokoneen komentojärjestelmästä johti itsenäiseen luomiseen monenlaisia ​​kieliä, jotka keskittyivät tiettyjen ongelmien ratkaisemiseen. Kielet ovat ilmestyneet tieteellisiin laskelmiin, taloudellisiin laskelmiin, tietokantoihin pääsyyn ja muihin.

Tulkki - ohjelma tai laite, joka suorittaa operaattorikohtaisen kääntämisen ja alkuperäisen ohjelman suorittamisen. Toisin kuin kääntäjä, tulkki ei tuota konekielistä ohjelmaa tulosteena. Kun se on tunnistanut komennon lähdekielellä, se suorittaa sen välittömästi. Sekä kääntäjät että tulkit käyttävät samoja menetelmiä ohjelman lähdekoodin analysointiin. Mutta tulkin avulla voit aloittaa tietojen käsittelyn yhden komennon kirjoittamisen jälkeen. Tämä tekee ohjelmien kehittämis- ja virheenkorjausprosessista joustavamman. Lisäksi ulostulokonekoodin puuttuminen mahdollistaa sen, että ulkoisia laitteita ei "täytetä" lisätiedostoilla, ja itse tulkki on melko helposti sovitettavissa mihin tahansa konearkkitehtuuriin, koska se on kehitetty vain kerran laajasti käytetyllä ohjelmointikielellä. Siksi tulkitut kielet, kuten Java Script ja VB Script, ovat yleistyneet. Tulkkien haittana on ohjelman alhainen suoritusnopeus. Tyypillisesti tulkitut ohjelmat toimivat 50-100 kertaa hitaammin kuin alkuperäiset ohjelmat.

Emulaattori - ohjelma tai ohjelmisto ja laitteistotyökalu, joka tarjoaa mahdollisuuden ilman uudelleenohjelmointia suorittaa tietyllä tietokoneella ohjelmaa, joka käyttää koodeja tai menetelmiä suorittaakseen erilaisia ​​toimintoja kuin annetussa tietokoneessa. Emulaattori on samanlainen kuin tulkki siinä mielessä, että se suorittaa suoraan tietyllä kielellä kirjoitetun ohjelman. Useimmiten se on kuitenkin konekieli tai välikoodi. Molemmat edustavat binäärikoodin ohjeita, jotka voidaan suorittaa välittömästi toimintakoodin tunnistamisen jälkeen. Toisin kuin tekstiohjelmissa, ei tarvitse tunnistaa ohjelman rakennetta tai valita operandeja.

Emulaattoreita käytetään melko usein erilaisiin tarkoituksiin. Esimerkiksi uusia laskentajärjestelmiä kehitettäessä luodaan ensin emulaattori, joka ajaa ohjelmia, jotka on kehitetty tietokoneille, joita ei vielä ole olemassa. Näin voit arvioida komentojärjestelmän ja kehittää perusohjelmistoa jo ennen vastaavan laitteiston luomista.

Hyvin usein emulaattoria käytetään vanhojen ohjelmien suorittamiseen uusissa tietokoneissa. Tyypillisesti uudemmat tietokoneet ovat nopeampia ja niissä on paremmat oheislaitteet. Näin voit emuloida vanhempia ohjelmia tehokkaammin kuin ajaa niitä vanhemmissa tietokoneissa. Esimerkki tästä lähestymistavasta on emulaattorien kehittäminen ZX Spectrum -kotitietokoneelle, jossa on Z80-mikroprosessori. On edelleen ihmisiä, jotka haluavat pelata emulaattorilla vanhentuneilla, mutta silti menettämättömillä peliohjelmilla. Emulaattoria voidaan käyttää myös enemmän halpa analogi moderni tietokonejärjestelmät, samalla kun se tarjoaa hyväksyttävän suorituskyvyn, joka vastaa tietyn arkkitehtuuriperheen halvempia malleja. Esimerkki on IBM PC -emulaattorit yhteensopivia tietokoneita, toteutettu tehokkaammissa Apple-tietokoneissa. Useat IBM PC:lle kirjoitetut emulaattorit korvaavat onnistuneesti erilaisia ​​pelikonsoleita.

Väliesitysemulaattori, samoin kuin tulkki, voidaan helposti siirtää tietokonearkkitehtuurista toiseen, mikä mahdollistaa mobiiliohjelmistojen luomisen. Juuri tämä ominaisuus määräsi Java-ohjelmointikielen menestyksen, josta ohjelma käännetään välikoodiksi. Suoritetaan tätä koodia virtuaalinen java kone ei ole muuta kuin emulaattori, joka käyttää mitä tahansa modernia käyttöjärjestelmä.

Transkooderi - ohjelma tai ohjelmistolaite, joka kääntää yhden tietokoneen konekielellä kirjoitetut ohjelmat toisen tietokoneen konekielisiksi ohjelmiksi. Jos emulaattori on tulkin vähemmän älykäs analogi, niin transkooderi toimii samassa kapasiteetissa kääntäjään nähden. Vastaavasti lähdekoodi (ja yleensä binäärinen) konekoodi tai väliesitys muunnetaan toiseksi vastaavaksi koodiksi yhdellä käskyllä ​​ja ilman lähdeohjelman rakenteen yleistä analyysiä. Transkooderit ovat hyödyllisiä siirrettäessä ohjelmia tietokonearkkitehtuurista toiseen. Niiden avulla voidaan myös rekonstruoida korkean tason kieliohjelmatekstiä olemassa olevasta binäärikoodista.

Makroprosessori - ohjelma, joka korvaa yhden merkkisarjan toisella[Ruskea]. Tämä on eräänlainen kääntäjä. Se luo tulostekstin käsittelemällä lähdetekstissä olevia erityisiä lisäyksiä. Nämä liitteet on suunniteltu erityisellä tavalla ja kuuluvat makrokieleksi kutsutun kielen rakenteisiin. Makroprosessoreita käytetään usein ohjelmointikielten lisäosina, mikä lisää ohjelmointijärjestelmien toimivuutta. Lähes kaikki kokoonpanolaitteet sisältävät makroprosessorin, mikä lisää koneohjelmien kehittämisen tehokkuutta. Tällaisia ​​ohjelmointijärjestelmiä kutsutaan yleensä makrokokoajiksi.

Makroprosessoreita käytetään myös korkean tason kielillä. Ne lisäävät kielten, kuten PL/1, C, C++, toimivuutta. Makroprosessorit ovat erityisen laajasti käytössä C- ja C++-kielissä, mikä helpottaa ohjelmien kirjoittamista. Esimerkki makroprosessorien laajasta käytöstä on Microsoft Foundation Classes (MFC) -luokkakirjasto. Se toteuttaa viestikartat ja muut ohjelmaobjektit makrolisäkkeiden avulla. Samalla makroprosessorit lisäävät ohjelmoinnin tehokkuutta muuttamatta kielen syntaksia ja semantiikkaa.

Syntaksi - tietyn kielen säännöt, jotka määräävät sen elementtien muodostumisen. Toisin sanoen tämä joukko sääntöjä semanttisesti merkittävien merkkijonojen muodostamiseksi tietyllä kielellä. Syntaksi määritetään säännöillä, jotka kuvaavat kielen käsitteitä. Esimerkkejä käsitteistä ovat: muuttuja, lauseke, operaattori, menettely. Käsitteiden järjestys ja niiden hyväksyttävä käyttö säännöissä määrittää syntaktisesti oikeat rakenteet, jotka muodostavat ohjelmia. Syntaksin avulla määritellään objektien hierarkia, ei tapa, jolla ne ovat vuorovaikutuksessa keskenään. Esimerkiksi lause voi esiintyä vain proseduurissa, lauseke käskyssä, muuttuja voi koostua nimestä ja valinnaisista indekseistä jne. Syntaksia ei liitetä sellaisiin ohjelman ilmiöihin kuin "hyppy ei-olemassa olevaan nimikkeeseen" tai "muuttujaa, jolla on annettu nimi, ei ole määritelty". Tätä semantiikka tekee.

Semantiikka - säännöt ja ehdot, jotka määrittävät kielen elementtien ja niiden semanttisten merkityksien välisen suhteen sekä kielen syntaktisten rakenteiden merkityksellisen merkityksen tulkinnan. Ohjelmointikielen objektit eivät vain sijoiteta tekstiin tietyn hierarkian mukaisesti, vaan ne myös liitetään toisiinsa muiden käsitteiden kautta, jotka muodostavat erilaisia ​​assosiaatioita. Esimerkiksi muuttuja, jolle syntaksi määrittää kelvollisen sijainnin vain ilmoituksissa ja joissakin lausekkeissa, on tietyn tyyppinen, sitä voidaan käyttää rajoitetulla määrällä operaatioita, sillä on osoite, koko ja se on ilmoitettava ennen kuin se voidaan käyttää ohjelmassa.

Syntaktinen analysaattori - kääntäjäkomponentti, joka tarkistaa lähdelausekkeiden yhteensopivuuden tietyn ohjelmointikielen syntaktisten sääntöjen ja semantiikan kanssa. Nimestään huolimatta analysaattori tarkistaa sekä syntaksin että semantiikan. Se koostuu useista lohkoista, joista jokainen ratkaisee omat ongelmansa. Sitä käsitellään tarkemmin kuvattaessa kääntäjän rakennetta.

Ohjelmointikielten ja kääntäjien yleiset ominaisuudet

Ohjelmointikielet eroavat toisistaan ​​melkoisesti tarkoituksen, rakenteen, semanttisen monimutkaisuuden ja toteutusmenetelmien osalta. Tämä asettaa omat erityispiirteensä tiettyjen kääntäjien kehittämiselle.

Ohjelmointikielet ovat työkaluja eri aihealueiden ongelmien ratkaisemiseen, mikä määrittää niiden organisaation erityispiirteet ja käyttötarkoituksen erot. Esimerkkejä ovat sellaiset kielet kuin Fortran, joka on suuntautunut tieteellisiin laskelmiin, C, joka on tarkoitettu järjestelmäohjelmointiin, Prolog, joka kuvaa tehokkaasti päättelyongelmia ja Lisp, jota käytetään rekursiiviseen listankäsittelyyn. Näitä esimerkkejä voidaan jatkaa. Jokainen aihealue asettaa omat vaatimuksensa itse kielen organisoinnille. Siksi voimme huomata operaattoreiden ja lausekkeiden esitysmuotojen moninaisuuden, eron joukossa perustoiminnot, ohjelmoinnin tehokkuuden lasku, kun ratkaistaan ​​ongelmia, jotka eivät liity asiaan aihealue. Kielelliset erot heijastuvat myös kääntäjien rakenteeseen. Lisp ja Prolog suoritetaan useimmiten tulkintatilassa, koska ne käyttävät dynaamista tietotyyppien generointia laskelmissa. Fortran-kääntäjille on ominaista tuloksena olevan konekoodin aggressiivinen optimointi, joka tulee mahdolliseksi kielirakenteiden suhteellisen yksinkertaisen semantiikan ansiosta - erityisesti siksi, että ei ole olemassa mekanismeja muuttujien vaihtoehtoiseen nimeämiseen osoittimien tai viitteiden avulla. Osoittimien läsnäolo C-kielessä esittelee erityisvaatimukset Vastaanottaja dynaaminen jakelu muisti.

Kielen rakenne luonnehtii sen käsitteiden välisiä hierarkkisia suhteita, joita kuvataan syntaktisilla säännöillä. Ohjelmointikielet voivat poiketa suuresti toisistaan ​​yksittäisten käsitteiden organisoinnissa ja niiden välisissä suhteissa. Ohjelmointikieli PL/1 sallii proseduurien ja funktioiden mielivaltaisen sisäkkäisyyden, kun taas C:ssä kaikkien funktioiden on oltava uloimmalla sisäkkätasolla. C++-kieli sallii muuttujien määrittämisen missä tahansa ohjelman kohdassa ennen sen ensimmäistä käyttöä, ja Pascalissa muuttujat on määriteltävä erikoisalue kuvaukset. Tätä vielä pidemmälle vie PL/1, joka mahdollistaa muuttujan ilmoittamisen sen käytön jälkeen. Tai voit jättää kuvauksen kokonaan pois ja käyttää oletussääntöjä. Riippuen tehty päätös, kääntäjä voi analysoida ohjelman yhdellä tai useammalla kierrolla, mikä vaikuttaa käännösnopeuteen.

Ohjelmointikielten semantiikka vaihtelee suuresti. Ne eroavat paitsi yksittäisten toimintojen toteutuspiirteistä, myös ohjelmointiparadigmoista, jotka määrittävät perustavanlaatuiset erot ohjelmien kehittämismenetelmissä. Toiminnan toteutuksen erityispiirteet voivat koskea sekä käsiteltävien tietojen rakennetta että samantyyppisten tietojen käsittelyn sääntöjä. Kielet, kuten PL/1 ja APL, tukevat matriisi- ja vektoritoimintoja. Useimmat kielet toimivat ensisijaisesti skalaarien kanssa tarjoten ohjelmoijien kirjoittamia proseduureja ja toimintoja taulukoiden käsittelyä varten. Mutta jopa suoritettaessa kahden kokonaisluvun lisäämistä, kielet, kuten C ja Pascal, voivat käyttäytyä eri tavalla.

Perinteisen menettelyohjelmoinnin, jota kutsutaan myös pakolliseksi, ohella on olemassa sellaisia ​​paradigmoja kuin toiminnallinen ohjelmointi, logiikkaohjelmointi ja olioohjelmointi. Toivon, että ehdottamani proseduuriparametrinen ohjelmointiparadigma ottaa paikkansa tässä sarjassa [Legalov2000]. Kielten käsitteiden ja objektien rakenne riippuu voimakkaasti valitusta paradigmasta, mikä vaikuttaa myös kääntäjän toteutukseen.

Jopa sama kieli voidaan toteuttaa useilla tavoilla. Tämä johtuu siitä, että muodollisten kielioppien teoria sallii erilaisia ​​tapoja jäsentää samoja lauseita. Tämän mukaisesti kääntäjät eri tavoilla voi saada saman tuloksen (objektiohjelman) alkuperäisestä lähdetekstistä.

Samaan aikaan kaikilla ohjelmointikielillä on useita yhteisiä ominaisuuksia ja parametreja. Tämä yhteisyys määrittää myös kääntäjien järjestämisen periaatteet, jotka ovat samanlaiset kaikille kielille.

  1. Ohjelmointikielet on suunniteltu helpottamaan ohjelmointia. Siksi niiden operaattorit ja tietorakenteet ovat tehokkaampia kuin konekielissä.
  2. Ohjelmien näkyvyyden lisäämiseksi käytetään numeeristen koodien sijasta kielirakenteiden symbolisia tai graafisia esityksiä, jotka ovat helpompia ihmisen havainnolle.
  3. Jokaiselle kielelle se on määritelty:
  • Paljon symboleja, joilla voidaan kirjoittaa oikeita ohjelmia (aakkoset), peruselementtejä.
  • Paljon oikeita ohjelmia (syntaksi).
  • Jokaisen oikean ohjelman "merkitys" (semantiikka).

Riippumatta kielen erityispiirteistä mitä tahansa kääntäjää voidaan pitää toiminnallisena muuntimena F, joka tarjoaa ainutlaatuisen mappauksen X:stä Y:hen, missä X on ohjelma lähdekielellä, Y on ohjelma tulostuskielellä. Siksi itse käännösprosessi voidaan esittää muodollisesti melko yksinkertaisesti ja selkeästi:

Muodollisesti jokainen oikea ohjelma X on jonkin aakkoston A merkkijono, joka on muutettu sitä vastaavaksi merkkijonoksi Y, joka koostuu aakkosten B merkeistä.

Ohjelmointikieli kuin mikä tahansa monimutkainen järjestelmä, määritellään käsitehierarkialla, joka määrittelee sen elementtien väliset suhteet. Nämä käsitteet liittyvät toisiinsa syntaktisten sääntöjen mukaisesti. Jokaisella näiden sääntöjen mukaan rakennetulla ohjelmalla on vastaava hierarkkinen rakenne.

Tässä suhteessa voidaan lisäksi erottaa seuraavat yhteiset piirteet kaikille kielille ja niiden ohjelmille: jokaisen kielen tulee sisältää säännöt, jotka mahdollistavat tätä kieltä vastaavien ohjelmien luomisen tai kirjoitettujen ohjelmien ja tietyn kielen välisen vastaavuuden tunnistamisen.

Ohjelmarakenteen ja ohjelmointikielen välistä suhdetta kutsutaan syntaktinen kartoitus.

Harkitse esimerkkinä hierarkkisen rakenteen ja seuraavan aritmeettisen lausekkeen määrittelevän merkkijonon välistä suhdetta:

Useimmissa ohjelmointikielissä tämä ilmaus määrittelee ohjelmaobjektien hierarkian, joka voidaan näyttää puuna (kuva 1.1.):

Ympyrät edustavat alkeisrakenteina käytettyjä symboleja ja suorakulmiot yhdistelmäkäsitteitä, joilla on hierarkkinen ja mahdollisesti rekursiivinen rakenne. Tämä hierarkia määritellään käyttämällä syntaktisia sääntöjä, jotka on kirjoitettu erityisellä kielellä, jota kutsutaan metakieleksi (metakielistä keskustellaan tarkemmin muodollisia kielioppeja opiskellessa):

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

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

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

<буква>

Huomautus. Merkki "::=" luetaan "tämä on". Putki "|" luetaan "tai".

Jos säännöt on kirjoitettu eri tavalla, hierarkkinen rakenne muuttuu. Esimerkkinä on seuraavalla menetelmällä sääntömerkinnät:

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

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

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

Tuloksena jäsentäminen Käyttämällä samaa aritmeettista lauseketta rakennetaan hierarkkinen rakenne, joka näkyy kuvassa. 1.2.


On syytä huomata, että hierarkkinen rakenne ei yleensä voi millään tavalla liittyä lausekkeen semantiikkaan. Molemmissa tapauksissa operaatioiden prioriteetti voidaan toteuttaa yleisesti hyväksyttyjen sääntöjen mukaisesti, kun kertolasku edeltää yhteenlaskua (tai päinvastoin, kaikilla operaatioilla voi olla sama prioriteetti minkä tahansa säännön mukaan). Ensimmäinen rakenne tukee kuitenkin selvästi yleisesti hyväksytyn prioriteetin jatkototeutusta, kun taas toinen sopii paremmin operaatioiden toteuttamiseen samalla prioriteetilla ja niiden suorittamiseen oikealta vasemmalle.

Syntaktisen rakenteen löytämisprosessi annettu ohjelma nimeltään jäsentäminen.

Yhdellä kielellä oikea syntaktinen rakenne voi olla väärä toisessa. Esimerkiksi Forth-kielellä annettua lauseketta ei tunnisteta. Kuitenkin tälle kielelle oikea postfix-lauseke on:

Sen syntaktista rakennetta kuvaavat säännöt:

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

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

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

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

Syntaktisen rakenteen määrittelevä hierarkkinen puu on esitetty kuvassa. 1.3.

Toinen kaikille kielille tyypillinen piirre on niiden semantiikka. Se määrittää kielioperaatioiden merkityksen ja operandien oikeellisuuden. Ketjut, joilla on sama syntaktinen rakenne eri ohjelmointikielissä, voivat poiketa semantiikasta (mitä havaitaan esimerkiksi C++:ssa, Pascalissa, Basicissa yllä olevassa aritmeettisen lausekkeen fragmentissa).

Kielen semantiikan tunteminen antaa sinun erottaa sen syntaksistaan ​​ja käyttää sitä muuntamiseen toiselle kielelle (koodin luomiseen).

Semantiikan kuvaus ja sen oikeellisuuden tunnistaminen on yleensä kääntäjän työvoimavaltaisin ja laajin osa, koska on tarpeen luetella ja analysoida monia vaihtoehtoja kelvollisille operaatioiden ja operandien yhdistelmille.

Yleistetty kääntäjän rakenne

Yhteiset ominaisuudet ja mallit ovat luontaisia ​​sekä eri ohjelmointikielille että näiden kielten kääntäjille. He käyvät läpi samanlaisia ​​prosesseja lähdetekstin muuntamiseksi. Huolimatta siitä, että näiden prosessien vuorovaikutus voidaan järjestää eri tavoin, on mahdollista tunnistaa toimintoja, joiden toteutus johtaa samoihin tuloksiin. Kutsukaamme tällaisia ​​toimintoja käännösprosessin vaiheiksi.

Kun otetaan huomioon kääntäjän ja tulkin samankaltaisuus, tarkastellaan kääntäjän vaiheita. Se korostaa:

  1. Leksisen analyysin vaihe.
  2. Jäsennysvaihe, joka koostuu:
  • syntaktisen rakenteen tunnistaminen;
  • semanttinen jäsennys, jonka aikana työskennellään taulukoiden kanssa ja luodaan semanttinen väliesitys tai kielen objektimalli.
  • Koodin generointivaihe, joka:
    • kielen väliesityksen tai objektimallin komponenttien semanttinen analyysi;
    • väliesityksen tai objektimallin kääntäminen kohdekoodiksi.

    Käännösprosessin päävaiheiden ohella lisävaiheet ovat mahdollisia:

      2a. Esityksen välitutkinta- ja optimointivaihe, joka koostuu:
    2a.1. väliesityksen oikeellisuuden analysointi;
    2a.2. väliesityksen optimointi.
      3a. Objektikoodin optimointivaihe.

    Tulkki eroaa siinä, että koodin generointivaihe korvataan yleensä kielen väliesityksen tai objektimallin elementtien emulointivaiheella. Lisäksi tulkki ei yleensä optimoi väliesitystä, vaan emuloi sitä välittömästi.

    Lisäksi on mahdollista erottaa ohjelman käsitellyssä lähdetekstissä olevien virheiden analysointi- ja korjausprosessi, joka on yhtenäinen kaikissa vaiheissa.

    Kääntäjän yleinen rakenne siinä olevat vaiheet huomioiden on esitetty kuvassa. 1.4.

    Se koostuu leksikaalisesta analysaattorista, jäsentimestä, koodigeneraattorista ja virheanalysaattorista. Tulkki käyttää koodigeneraattorin sijaan emulaattoria (kuva 1.5), johon väliesityksen elementtien lisäksi siirretään lähdedata. Emulaattorin tulos on laskelmien tulos.

    Leksikaalinen analysaattori (tunnetaan myös nimellä skanneri) lukee syötetyn merkkijonon ja ryhmittelee ne perusrakenteiksi, joita kutsutaan lekseemiksi. Jokaisella merkillä on luokka ja merkitys. Tyypillisesti lekseemin rooliin ehdokkaita ovat alkeiskielen konstruktit, esimerkiksi tunniste, reaaliluku, kommentti. Tuloksena saadut tunnukset välitetään jäsentäjälle. Skanneri ei ole kääntäjän pakollinen osa. Sen avulla voit kuitenkin lisätä käännösprosessin tehokkuutta. Leksikaalista analyysiä käsitellään tarkemmin aiheessa "Leksikaalisen analyysin organisointi".

    Jäsentäjä jäsentää lähdeohjelman sisääntulevien lekseemien avulla, rakentaa ohjelman syntaktisen rakenteen ja semanttisen analyysin muodostamalla kielen objektimallin. Objektimalli edustaa syntaktista rakennetta, jota täydentävät olemassa olevien käsitteiden väliset semanttiset suhteet. Nämä liitännät voivat olla:

    • nimitaulukoihin sijoitetut viittaukset muuttujiin, tietotyyppeihin ja proseduurien nimiin;
    • yhteydet, jotka määrittävät komennon suoritusjärjestyksen;
    • yhteydet, jotka määrittävät kieliobjektimallin elementtien sisäkkäisyyden ja muut.

    Siten jäsentäjä on melko monimutkainen kääntäjälohko. Siksi se voidaan jakaa seuraaviin komponentteihin:

    • tunnistaja;
    • semanttinen analyysi lohko;
    • objektimalli tai väliesitys, joka koostuu nimitaulukosta ja syntaktisesta rakenteesta.

    Jäsentimen yleinen rakenne on esitetty kuvassa. 1.6.

    Tunnistaja vastaanottaa lekseemiketjun ja suorittaa sen perusteella jäsennyn käytettyjen sääntöjen mukaisesti. Tokenit, kun sääntöjen jäsentäminen onnistuu, siirretään semanttiseen analysaattoriin, joka rakentaa nimitaulukon ja tallentaa fragmentteja syntaktisesta rakenteesta. Lisäksi nimitaulukon ja syntaktisen rakenteen välille tallennetaan ylimääräisiä semanttisia yhteyksiä. Tuloksena muodostuu ohjelman oliomalli, joka vapautetaan sitoutumisesta ohjelmointikielen syntaksiin. Melko usein kieliobjektien hierarkiaa täysin kopioivan syntaktisen rakenteen sijaan luodaan sen yksinkertaistettu analogi, jota kutsutaan väliesityksenä.

    Virheanalysaattori vastaanottaa tietoa virheistä, joita esiintyy eri kääntäjälohkoissa. Saatujen tietojen perusteella se luo viestejä käyttäjälle. Lisäksi tämä lohko voi yrittää korjata virheen jatkaakseen jäsentämistä. Hän vastaa myös ohjelman oikeaan loppuunsaattamiseen liittyvistä toimista siinä tapauksessa, että lähetystä ei voida jatkaa.

    Koodigeneraattori rakentaa objektikonekoodin objektimallin tai väliesityksen analyysin perusteella. Koodin rakentamiseen liittyy ylimääräinen semanttinen analyysi, joka liittyy tarpeeseen muuntaa yleistetyt komennot tietyn tietokoneen koodeiksi. Tällaisen analyysin vaiheessa muunnoksen mahdollisuus lopulta määritetään ja tehokkaita vaihtoehtoja. Itse koodin luominen on yhden komennon uudelleenkoodausta toiseen.

    Vaihtoehdot kääntäjälohkojen vuorovaikutukseen

    Käännösprosessien organisointi, joka määrää päävaiheiden toteutuksen, voidaan toteuttaa eri tavoin. Tämän määräävät erilaiset kääntäjälohkojen vuorovaikutusvaihtoehdot: leksikaalinen analysaattori, jäsentäjä ja koodigeneraattori. Samasta lopputuloksesta huolimatta erilaisia ​​vaihtoehtoja Kääntäjälohkojen vuorovaikutus tarjoaa erilaisia ​​vaihtoehtoja välitietojen tallentamiseen. Kääntäjälohkojen vuorovaikutukseen on kaksi päävaihtoehtoa:

    • monivaiheinen organisaatio, jossa jokainen vaihe on itsenäinen prosessi, joka siirtää ohjauksen seuraavaan vaiheeseen vasta tietojensa täydellisen käsittelyn jälkeen;
    • yhden kierron organisaatio, jossa kaikki vaiheet edustavat yhtä prosessia ja välittävät tietoja toisilleen pieninä osina.

    Kahden päävaihtoehdon perusteella voit myös luoda niistä erilaisia ​​yhdistelmiä.

    Kääntäjälohkojen välisen vuorovaikutuksen monivaiheinen organisointi

    Tämä versio lohkojen vuorovaikutuksesta, esimerkkinä kääntäjää, on esitetty kuvassa 1.7.


    Leksikaalinen analysaattori käsittelee lähdetekstin kokonaan muodostaen tulosketjun, joka koostuu kaikista vastaanotetuista lekseemista. Vasta tämän jälkeen ohjaus siirtyy jäsentimelle. Jäsentäjä vastaanottaa generoidun lekseemiketjun ja muodostaa sen perusteella väliesityksen tai objektimallin. Vastaanotettuaan koko objektimallin se siirtää ohjauksen koodigeneraattorille. Koodigeneraattori rakentaa tarvittavan konekoodin kieliobjektimallin perusteella

    Tämän lähestymistavan etuja ovat:

    1. Yksittäisten vaiheiden eristäminen, mikä mahdollistaa niiden itsenäisen toteutuksen ja käytön.
    2. Mahdollisuus tallentaa kunkin vaiheen toiminnan tuloksena saatuja tietoja ulkoisille tallennuslaitteille ja käyttää niitä tarpeen mukaan.
    3. Mahdollisuus pienentää äänenvoimakkuutta RAM-muisti, tarvitaan kääntäjän toimintaan vaiheiden peräkkäisen kutsumisen vuoksi.

    Haittoja ovat mm.

    1. Saatavuus suuria määriä välitiedot, joista Tämä hetki Vain pieni osa ajasta tarvitaan.
    2. Lähetysnopeuden hidastuminen johtuu vaiheiden peräkkäisestä suorittamisesta ja ulkoisten tallennuslaitteiden käytöstä RAM-muistin säästämiseksi.

    Tämä lähestymistapa voi olla kätevä, kun rakennetaan kääntäjiä ohjelmointikielistä, joilla on monimutkainen syntaktinen ja semanttinen rakenne (esimerkiksi PL/I). Tällaisissa tilanteissa käännös on vaikea suorittaa yhdellä kertaa, joten on helpompi esittää aikaisempien läpimenojen tulokset lisävälitietoina. On huomattava, että kielen monimutkaiset semanttiset ja syntaktiset rakenteet voivat johtaa ylimääräisiin läpimenoihin, joita tarvitaan vaadittujen riippuvuuksien määrittämiseen. Passien kokonaismäärä voi olla yli kymmenen. Läpivientien määrään voi vaikuttaa myös ohjelman tiettyjen kieliominaisuuksien käyttö, kuten muuttujien ja menettelyjen ilmoittaminen niiden käytön jälkeen, oletusilmoitussääntöjen soveltaminen ja niin edelleen.

    Kääntäjälohkojen välisen vuorovaikutuksen yksivaiheinen organisointi

    Yksi vaihtoehdoista kääntäjälohkojen vuorovaikutukselle yksivaiheisen organisaation kanssa on esitetty kuvassa. 1.8.

    Tässä tapauksessa käännösprosessi etenee seuraavasti. Leksikaalinen analysaattori lukee osan lähdetekstistä, joka tarvitaan yhden lekseemin saamiseksi. Kun merkki on muodostettu, se kutsuu jäsentimen ja välittää sille luodun tunnuksen parametrina. Jos jäsentäjä voi rakentaa väliesityksen seuraavan elementin, se tekee niin ja välittää muodostetun fragmentin koodigeneraattorille. Muussa tapauksessa jäsentäjä palauttaa ohjauksen skannerille, mikä tekee selväksi, että seuraava merkki on käsitelty ja uutta tietoa tarvitaan.

    Koodigeneraattori toimii samalla tavalla. Väliesityksen vastaanotetun fragmentin perusteella se luo vastaavan objektikoodin fragmentin. Tämän jälkeen ohjaus palaa jäsentäjälle.

    Kun lähdeteksti on valmis ja kaikki välitiedot on käsitelty kussakin lohkossa, leksikaalinen analysaattori aloittaa ohjelman lopetusprosessin.

    Useimmiten yksivaiheiset kääntäjät käyttävät erilaista ohjausjärjestelmää, jossa jäsentäjä toimii päälohkona (kuva 1.9).

    Leksikaalinen analysaattori ja koodigeneraattori toimivat alirutiineina, joita se kutsuu. Heti kun jäsentäjä tarvitsee toisen tunnuksen, se kutsuu skannerin. Väliesityksen fragmenttia vastaanotettaessa soitetaan koodigeneraattorille. Käännösprosessi valmistuu sen jälkeen, kun viimeinen token on vastaanotettu ja käsitelty, ja jäsentäjä aloittaa sen.

    Yhden kierron järjestelmän etuja ovat suurten välitietomäärien puuttuminen, suuri käsittelynopeus, joka johtuu vaiheiden yhdistämisestä yhdessä prosessissa, ja pääsyn puuttuminen ulkoisiin tallennuslaitteisiin.

    Haittoja ovat: mahdottomuus toteuttaa tällaista käännösjärjestelmää rakenteeltaan monimutkaisille kielille ja välitietojen puute, jota voidaan käyttää monimutkaiseen analyysiin ja optimointiin.

    Tätä järjestelmää käytetään usein ohjelmointikielissä, jotka ovat yksinkertaisia ​​semanttisissa ja syntaktisissa rakenteissa, sekä kääntäjissä että tulkkeissa. Esimerkkejä tällaisista kielistä ovat Basic ja Pascal. Klassinen tulkki rakennetaan yleensä yhden kierroksen menetelmällä, koska suora suoritus suoritetaan väliesityksen yksittäisten fragmenttien tasolla. Tällaisen tulkin lohkojen välisen vuorovaikutuksen organisointi on esitetty kuvassa. 1.10.

    Kääntäjälohkojen yhdistetyt vuorovaikutukset

    Monivaiheisten ja yksivaiheisten käännösjärjestelmien yhdistelmät synnyttävät useita yhdistettyjä vaihtoehtoja, joista monia käytetään onnistuneesti. Esimerkkinä voimme tarkastella joitain niistä.

    Kuvassa Kuvassa 1.11 on kaavio kääntäjälohkojen vuorovaikutuksesta jakaen koko prosessin kahteen vaiheeseen. Ensimmäinen läpimeno tuottaa ohjelman täydellisen väliesityksen ja toinen koodin. Tällaisen järjestelmän avulla voit helposti siirtää kääntäjän tietokonejärjestelmästä toiseen kirjoittamalla koodigeneraattorin uudelleen.

    Lisäksi koodigeneraattorin sijaan on helppo liittää väliesitysemulaattori, jonka avulla voit yksinkertaisesti kehittää ohjelmointijärjestelmää tietyllä kielellä, joka on suunnattu erilaisiin suoritusympäristöihin. Esimerkki tällaisesta kääntäjälohkojen välisen vuorovaikutuksen järjestämisestä on esitetty kuvassa. 1.12.


    Niiden järjestelmien lisäksi, joissa koodigeneraattori korvataan emulaattorilla, on olemassa kääntäjiä, jotka sallivat ne jakaminen. Yksi sellaisista kaavioista on esitetty kuvassa. 1.13.

    Käännösprosessi, mukaan lukien koodin luominen, voidaan suorittaa millä tahansa määrällä ajoja (esimerkissä käytetään aiemmin esitettyä yksivaiheista käännöstä). Luotua objektikoodia ei kuitenkaan suoriteta vastaavalle laskentajärjestelmä, mutta sitä emuloidaan tietokoneella, jolla on erilainen arkkitehtuuri. Tätä mallia käytetään Java-ohjelmointikielen ympärille rakennetussa ympäristössä. Kääntäjä itse luo Java-virtuaalikonekoodin, jota emuloidaan erityisillä keinoilla sekä itsenäisesti että Internet-selainympäristössä.

    Esitetyllä kaaviolla voi olla myös laajempi tulkinta suhteessa mihin tahansa konekoodia generoivaan kääntäjään. Asia on, että useimmat nykyaikaiset tietokoneet toteutetaan mikroohjelmaohjauksella. Mikroohjelman ohjauslaitetta voidaan pitää konekoodia emuloivana ohjelmana. Tämän ansiosta voimme puhua viimeisimmän esitellyn järjestelmän laajasta käytöstä.

    Testikysymykset ja -tehtävät

    1. Nimeä erot:
      • tulkki kääntäjästä;
      • kääntäjä kokoonpanosta;
      • kääntäjä transkooderi;
      • emulaattori tulkista;
      • syntaksi semantiikasta.
    1. Kerro meille tuntemiesi ohjelmointikielten uusimmasta kehityksestä. Kerro näiden kielten tärkeimmät ominaisuudet.
    2. Anna konkreettisia esimerkkejä käännösmenetelmien käytöstä alueilla, jotka eivät liity ohjelmointikieliin.
    3. Anna konkreettisia esimerkkejä käännetyistä ohjelmointikielistä.
    4. Anna konkreettisia esimerkkejä tulkitetuista ohjelmointikielistä.
    5. Anna konkreettisia esimerkkejä ohjelmointikielistä, joille on saatavilla sekä kääntäjiä että tulkkeja.
    6. Kääntäjien tärkeimmät edut ja haitat.
    7. Tulkkien tärkeimmät edut ja haitat.
    8. Kuvaile tärkeimmät erot kahden tuntemasi ohjelmointikielen syntaksissa.
    9. Kuvaile tärkeimmät erot kahden tuntemasi ohjelmointikielen semantiikan välillä.
    10. Nimeä käännösprosessin päävaiheet ja niiden tarkoitus.
    11. Nimeä yksikertaisen kääntämisen erityispiirteet.
    12. Nimeä monivaiheisen kääntämisen erityispiirteet.
    13. Anna esimerkkejä mahdollisista yhden ja monikierroksen muunnoksen yhdistelmistä. Kerro meille näiden järjestelmien käytännön käytöstä.

    Lähetä hyvä työsi tietokanta on yksinkertainen. Käytä alla olevaa lomaketta

    Opiskelijat, jatko-opiskelijat, nuoret tutkijat, jotka käyttävät tietopohjaa opinnoissaan ja työssään, ovat sinulle erittäin kiitollisia.

    Julkaistu osoitteessa http://www.allbest.ru

    Johdanto

    1.1 Ylhäältä alas -analyysi

    1.2 Alhaalta ylös -analyysi

    1.2.1 LR(k) - kieliopit

    1.2.1.1 LR(0) - kieliopit

    1.2.2 LALR(1) - kieliopit

    2. Kääntäjän kehittäminen

    2.1 Vaatimusanalyysi

    2.2 Suunnittelu

    2.2.1 Leksikaalisen analysaattorin suunnittelu

    2.2.4 Jäsentimen ohjelmistototeutus

    2.3 Koodaus

    2.4 Testaus

    Johtopäätös

    Luettelo käytetyistä lähteistä

    Liite A. Käännösohjelman tekstin listaus

    Liite B. Testitulokset

    Liite B. Kääntäjän ohjelmakaavio

    Johdanto

    Kaukana ovat ajat, jolloin ennen ohjelman kirjoittamista piti ymmärtää ja muistaa kymmeniä koneen ohjeita. Nykyaikainen ohjelmoija muotoilee tehtävänsä korkean tason ohjelmointikielillä ja käyttää kokoonpanokieltä vain poikkeustapauksissa. Kuten tiedetään, algoritmiset kielet tulevat ohjelmoijan saataville vasta sen jälkeen, kun näistä kielistä on luotu kääntäjät.

    Ohjelmointikielet eroavat toisistaan ​​melkoisesti tarkoituksen, rakenteen, semanttisen monimutkaisuuden ja toteutusmenetelmien osalta. Tämä asettaa omat erityispiirteensä tiettyjen kääntäjien kehittämiselle.

    Ohjelmointikielet ovat työkaluja eri aihealueiden ongelmien ratkaisemiseen, mikä määrittää niiden organisaation erityispiirteet ja käyttötarkoituksen erot. Esimerkkejä ovat sellaiset kielet kuin Fortran, joka on suuntautunut tieteellisiin laskelmiin, C, joka on tarkoitettu järjestelmäohjelmointiin, Prolog, joka kuvaa tehokkaasti päättelyongelmia ja Lisp, jota käytetään rekursiiviseen listankäsittelyyn. Näitä esimerkkejä voidaan jatkaa. Jokainen aihealue asettaa omat vaatimuksensa itse kielen organisoinnille. Tästä syystä voidaan havaita operaattorien ja lausekkeiden esitysmuotojen moninaisuus, perusoperaatioiden joukon ero ja ohjelmoinnin tehokkuuden heikkeneminen aihealueeseen kuulumattomien ongelmien ratkaisussa. Kielelliset erot heijastuvat myös kääntäjien rakenteeseen. Lisp ja Prolog suoritetaan useimmiten tulkintatilassa, koska ne käyttävät dynaamista tietotyyppien generointia laskelmissa. Fortran-kääntäjille on ominaista tuloksena olevan konekoodin aggressiivinen optimointi, joka tulee mahdolliseksi kielirakenteiden suhteellisen yksinkertaisen semantiikan ansiosta - erityisesti siksi, että ei ole olemassa mekanismeja muuttujien vaihtoehtoiseen nimeämiseen osoittimien tai viitteiden avulla. Osoittimien läsnäolo C-kielessä asettaa erityisiä vaatimuksia dynaamiselle muistin allokoinnille.

    Kielen rakenne luonnehtii sen käsitteiden välisiä hierarkkisia suhteita, joita kuvataan syntaktisilla säännöillä. Ohjelmointikielet voivat poiketa suuresti toisistaan ​​yksittäisten käsitteiden organisoinnissa ja niiden välisissä suhteissa. Ohjelmointikieli PL/1 sallii proseduurien ja funktioiden mielivaltaisen sisäkkäisyyden, kun taas C:ssä kaikkien funktioiden on oltava uloimmalla sisäkkätasolla. C++-kieli sallii muuttujien ilmoittamisen missä tahansa ohjelman kohdassa ennen sen ensimmäistä käyttöä, kun taas Pascalissa muuttujat on määritettävä erityisellä määritysalueella. Tätä vielä pidemmälle vie PL/1, joka mahdollistaa muuttujan ilmoittamisen sen käytön jälkeen. Tai voit jättää kuvauksen kokonaan pois ja käyttää oletussääntöjä. Päätöksestä riippuen kääntäjä voi analysoida ohjelman yhdessä tai useammassa ajossa, mikä vaikuttaa käännösnopeuteen.

    Ohjelmointikielten semantiikka vaihtelee suuresti. Ne eroavat paitsi yksittäisten toimintojen toteutuspiirteistä, myös ohjelmointiparadigmoista, jotka määrittävät perustavanlaatuiset erot ohjelmien kehittämismenetelmissä. Toiminnan toteutuksen erityispiirteet voivat koskea sekä käsiteltävien tietojen rakennetta että samantyyppisten tietojen käsittelyn sääntöjä. Kielet, kuten PL/1 ja APL, tukevat matriisi- ja vektoritoimintoja. Useimmat kielet toimivat ensisijaisesti skalaarien kanssa tarjoten ohjelmoijien kirjoittamia proseduureja ja toimintoja taulukoiden käsittelyä varten. Mutta jopa suoritettaessa kahden kokonaisluvun lisäämistä, kielet, kuten C ja Pascal, voivat käyttäytyä eri tavalla.

    Perinteisen prosessiohjelmoinnin, jota kutsutaan myös pakolliseksi, ohella on sellaisia ​​paradigmoja kuin toiminnallinen ohjelmointi, logiikkaohjelmointi ja olio-ohjelmointi. Kielten käsitteiden ja objektien rakenne riippuu voimakkaasti valitusta paradigmasta, mikä vaikuttaa myös kääntäjän toteutukseen.
    Jopa sama kieli voidaan toteuttaa useilla tavoilla. Tämä johtuu siitä, että muodollisten kielioppien teoria sallii erilaisia ​​tapoja jäsentää samoja lauseita. Tämän mukaisesti kääntäjät voivat saada saman tuloksen (objektiohjelman) alkuperäisestä lähdetekstistä eri tavoin.
    Samaan aikaan kaikilla ohjelmointikielillä on useita yhteisiä ominaisuuksia ja parametreja. Tämä yhteisyys määrittää myös kääntäjien järjestämisen periaatteet, jotka ovat samanlaiset kaikille kielille.
    Ohjelmointikielet on suunniteltu helpottamaan ohjelmointia. Siksi niiden operaattorit ja tietorakenteet ovat tehokkaampia kuin konekielissä.
    Ohjelmien näkyvyyden lisäämiseksi käytetään numeeristen koodien sijasta kielirakenteiden symbolisia tai graafisia esityksiä, jotka ovat helpompia ihmisen havainnolle.
    Jokaiselle kielelle se on määritelty:
    - monia symboleja, joilla voidaan kirjoittaa oikeita ohjelmia (aakkosia), peruselementtejä,
    - monta oikeaa ohjelmaa (syntaksi),
    - jokaisen oikean ohjelman "merkitys" (semantiikka).
    Riippumatta kielen erityispiirteistä mitä tahansa kääntäjää voidaan pitää toiminnallisena muuntimena F, joka tarjoaa ainutlaatuisen mappauksen X:stä Y:hen, missä X on ohjelma lähdekielellä, Y on ohjelma tulostuskielellä. Siksi itse käännösprosessi voidaan esittää muodollisesti melko yksinkertaisesti ja selkeästi: Y = F(X).
    Muodollisesti jokainen oikea ohjelma X on jonkin aakkoston A symbolien ketju, joka on muunnettu sitä vastaavaksi ketjuksi Y, joka koostuu aakkosten B symboleista.
    Ohjelmointikieli, kuten mikä tahansa monimutkainen järjestelmä, määritellään käsitehierarkialla, joka määrittelee sen elementtien väliset suhteet. Nämä käsitteet liittyvät toisiinsa syntaktisten sääntöjen mukaisesti. Jokaisella näiden sääntöjen mukaan rakennetulla ohjelmalla on vastaava hierarkkinen rakenne.
    Tässä suhteessa voidaan lisäksi erottaa seuraavat yhteiset piirteet kaikille kielille ja niiden ohjelmille: jokaisen kielen tulee sisältää säännöt, jotka mahdollistavat tätä kieltä vastaavien ohjelmien luomisen tai kirjoitettujen ohjelmien ja tietyn kielen välisen vastaavuuden tunnistamisen.

    Toinen kaikille kielille tyypillinen piirre on niiden semantiikka. Se määrittää kielioperaatioiden merkityksen ja operandien oikeellisuuden. Ketjut, joilla on sama syntaktinen rakenne eri ohjelmointikielissä, voivat poiketa semantiikan suhteen (mitä havaitaan esimerkiksi C++:ssa, Pascalissa, Basicissa). Kielen semantiikan tunteminen antaa sinun erottaa sen syntaksistaan ​​ja käyttää sitä muuntamiseen toiselle kielelle (koodin luomiseen).

    Tämän kurssityön tarkoituksena on kehittää opetuksellinen kääntäjä annetusta yksinkertaistetusta tekstin kieli korkeatasoinen.

    1. Kielioppianalyysin menetelmät

    Katsotaanpa kieliopin jäsentämisen perusmenetelmiä.

    1.1 Ylhäältä alas -analyysi

    Kun jäsennetään ylhäältä alas, välijohdot liikkuvat puuta pitkin juurista lehtiin. Tässä tapauksessa katsottaessa ketjua vasemmalta oikealle saadaan luonnollisesti vasenkätisiä johtopäätöksiä. Deterministisessä jäsennyksessä ongelmana on, mitä sääntöä sovelletaan vasemmanpuoleisimman epäterminaalin ratkaisemiseen.

    1.1.1 LL(k) - kielet ja kieliopit

    Harkitse päättelypuuta ketjun vasemman tulosteen saamiseksi. Päättelyprosessin väliketju koostuu pääteketjusta w, vasemmanpuoleisesta ei-päätteestä A, alipäätellystä osasta x:

    -S--

    / \

    / -A-x-\

    / | \

    -w---u----

    Kuvio 1

    Jäsentämisen jatkamiseksi on välttämätöntä korvata ei-pääte A jonkin muodon A:y säännön mukaisesti. Jos haluat jäsentämisen olevan determinististä (ei palautuksia), tämä sääntö on valittava erityisellä tavalla. Kieliopin sanotaan olevan ominaisuus LL(k), jos säännön valitsemiseksi riittää, että huomioidaan vain wax ja tutkimattoman merkkijonon u ensimmäiset k merkkiä. Ensimmäinen kirjain L (vasen) viittaa syöttöketjun katselemiseen vasemmalta oikealle, toinen tarkoittaa vasemman lähdön käyttöä.

    Määritellään kaksi ketjua:

    a) FIRST(x) on x:stä johdettu päätemerkkijono, joka on lyhennetty k merkkiin.

    b) FOLLOW(A) - joukko pääteketjuja lyhennettynä k merkkiin, joka voi välittömästi seurata A:ta tulosteketjuissa.

    Kieliopin ominaisuus on LL(k), jos kahden vasemmanpuoleisen päättelyketjun olemassaolosta:

    S:: vaha: wzx:: wu

    S:: vaha: wtx:: wv

    ehdosta FIRST(u)=FIRST(v) seuraa z=t.

    Tapauksessa k=1 A:n säännön valitsemiseksi riittää, että tunnet vain ei-pääteisen A ja a - ketjun u ensimmäisen merkin:

    - sääntö A:x tulee valita, jos a sisältyy kohtaan FIRST(x),

    - Sääntö A:e tulee valita, jos a sisältyy FOLLOW(A) -kohtaan.

    Ominaisuus LL(k) asettaa melko voimakkaita rajoituksia kielioppiin. Esimerkiksi LL(2) kielioppi S: aS | a:lla ei ole LL(1)-ominaisuutta, koska FIRST(aS)=FIRST(a)=a. SISÄÄN tässä tapauksessa voit pienentää k:n arvoa käyttämällä "faktorointia" (poistamalla tekijän suluista):

    S: aA

    A: S | e

    Mikä tahansa LL(k)-kielioppi on yksiselitteinen. Vasen rekursiivinen kielioppi ei kuulu minkään k:n luokkaan LL(k). Joskus on mahdollista muuntaa ei-LL(1)-kielioppi vastaavaksi LL(1)-kieliopiksi poistamalla vasemmanpuoleinen rekursio ja faktorointi. Kuitenkin ongelmaa vastaavan LL(k)-kieliopin olemassaolosta mielivaltaiselle ei-LL(k)-kieliopille ei voida ratkaista.

    1.1.2 Rekursiivinen laskeutumismenetelmä

    Rekursiivinen laskeutumismenetelmä on tarkoitettu niihin tapauksiin, joissa kääntäjä on ohjelmoitu jollakin korkean tason kielistä, jolloin rekursiivisten proseduurien käyttö on sallittua.

    Rekursiivisen laskeutumisen pääidea on, että jokaisella kieliopin ei-päätteellä on vastaava menettely, joka tunnistaa minkä tahansa tämän ei-terminaalin muodostaman ketjun. Nämä menettelyt kutsuvat toisiaan tarvittaessa.

    Rekursiivista laskeutumista voidaan käyttää missä tahansa LL(1)-kieliopissa. Jokaisella kieliopin ei-päätteellä on vastaava proseduuri, joka alkaa siirtymällä laskettuun etikettiin ja sisältää koodin, joka vastaa tämän ei-päätteen kutakin sääntöä. Niille syötesymboleille, jotka kuuluvat tietyn säännön valintajoukkoon, laskettu siirtymä siirtää ohjauksen kyseistä sääntöä vastaavalle koodille. Muiden syöttösymbolien osalta ohjaus siirtyy virheenkäsittelymenettelyyn.

    Minkä tahansa säännön koodi sisältää toiminnot jokaiselle säännön oikealla puolella olevalle merkille. Toiminnot on järjestetty siihen järjestykseen, jossa symbolit esiintyvät säännössä. Viimeisen toiminnon jälkeen koodi sisältää palautuksen proseduurista.

    Rekursiivisen laskeutumisen käyttäminen korkean tason kielellä helpottaa ohjelmointia ja virheenkorjausta.

    1.2 Alhaalta ylös -analyysi

    Tarkastellaan alhaalta ylös jäsentämistä, jossa välitappeja siirretään pitkin puuta juurta kohti. Jos luet merkkijonon merkit vasemmalta oikealle, jäsennyspuu näyttää tältä:

    -S--

    / \

    /-x-\

    / | \

    --w--b--u-

    Kuva 2

    Välilähdön muoto on xbu, jossa x on pääte- ja ei-päätteiden ketju, josta tulostuu pääteketjun w katsottu osa, bu on pääteketjun katsomaton osa, b on seuraava symboli. Analyysin jatkamiseksi voit joko lisätä merkin b katsottuun ketjun osaan (suorita ns. "siirto") tai valita x:n lopusta sellaisen ketjun z (x=yz), joka kieliopin sääntöjä B:z voidaan soveltaa z:hen ja korvata x ketjuun yB (suorita ns. "konvoluutio"):

    -S-- -S--

    / \ / \

    /-x-b-\ /yB-\

    / | \ / | \

    --w--b--u- --w--b--u-

    Kuva 3 - Siirron jälkeen Kuva 4 - Konvoluution jälkeen

    Jos konvoluutiota sovelletaan vain x:n viimeisiin merkkeihin, saamme ketjun oikeat tulosteet. Tätä jäsentämistä kutsutaan LR:ksi, jossa symboli L (vasen, vasen) viittaa ketjun katselemiseen vasemmalta oikealle ja R (oikea, oikea) viittaa tuloksena oleviin lähtöihin.

    Siirto- ja taittotoimintojen järjestys on olennainen. Siksi deterministinen jäsennys vaatii valinnan siirron ja konvoluution (ja erilaisten konvoluutiosääntöjen) välillä joka hetki.

    1.2.1 LR(k) - kieliopit

    Jos LR-jäsennysprosessissa on mahdollista tehdä deterministinen päätös siirrosta/pienennyksestä, kun otetaan huomioon vain merkkijono x ja syötemerkkijonon u näkymättömän osan ensimmäiset k merkkiä (näitä k merkkiä kutsutaan ennakkomerkkijonoksi ), kieliopin sanotaan olevan LR(k)-ominaisuus.

    -S--

    / \

    /-x-\

    --w----u--

    Kuva 5

    Ero LL(k)- ja LR(k)-kielioppien välillä päättelypuun kannalta:

    -S-

    / | \

    /A\

    / / \ \

    -w---v---u-

    Kuva 6

    LL(k)-kielioppien tapauksessa A:han sovellettava sääntö voidaan määrittää yksiselitteisesti w:llä ja vu:n ensimmäisellä k merkillä, ja LR(k)-kielioppien tapauksessa w,v:llä ja ensimmäisellä k:lla. hahmot u. Tämä ei-tiukka päättely osoittaa, että LL(k)-kielet< LR(k)-языки (при k > 0).

    1.2.1.1 LR(0) - kieliopit

    Tarkastellaan ensin tämän luokan yksinkertaisimpia kielioppeja - LR(0). Kun jäsentät merkkijonoa LR(0)-kielellä, etenemisketjua ei tarvitse käyttää ollenkaan - valinta shiftin ja foldin välillä tehdään ketjun x perusteella. Koska jäsentämisen aikana se muuttuu vain oikeasta päästä, sitä kutsutaan pinoksi. Oletetaan, että kieliopissa ei ole hyödyttömiä symboleja ja alkusymboli ei näy sääntöjen oikealla puolella - silloin konvoluutio alkusymboliin merkitsee jäsentämisen onnistumista. Yritetään kuvata terminaalien ja ei-päätteiden ketjuja, jotka näkyvät pinossa kaikkien LR-jäsennysten aikana (toisin sanoen kaikki kieliopin oikeanpuoleiset päätelmät).

    Määrittelemme seuraavat joukot:

    L(A:v) - säännön A:v vasen konteksti - pinon tilojen joukko juuri ennen kuin v taitetaan A:ksi kaikkien onnistuneiden LR-jäsennysten aikana. Ilmeisesti jokainen L(A:v):n ketju päättyy v:ään. Jos kaikkien tällaisten ketjujen häntä v katkaistaan, saadaan A:n vasemmalla puolella esiintyvä ketjujen joukko kaikkien onnistuneiden oikeanpuoleisten päättelyjen aikana. Merkitään tämä joukko L(A) - ei-terminaalin A vasen konteksti.

    Muodostetaan kielioppi joukolle L(A). Uuden kieliopin terminaalit ovat alkuperäisen kieliopin päätteitä ja uuden kieliopin ei-päätteitä merkitään ,... - niiden arvot ovat alkuperäisen kieliopin ei-terminaalien vasenta kontekstia. Jos S on alkuperäisen kieliopin alkusymboli, vasemmanpuoleisen kontekstin kielioppi sisältää säännön : e - vasen konteksti S sisältää tyhjän ketjun jokaiselle alkuperäisen kieliopin säännölle, esimerkiksi A: B C d E

    ja lisää säännöt uuteen kielioppiin:

    : - L(B) sisältää L(A)

    : B - L(C) sisältää L(A) B:n

    : B C d - L(E) sisältää L(A) B C d

    Tuloksena oleva kielioppi on erityinen tyyppi(tällaisia ​​kielioppeja kutsutaan vasemmanpuoleiseksi lineaariseksi), siksi vasemmistoisten kontekstien joukot

    -säännöllinen. Tästä seuraa, että se, kuuluuko merkkijono ei-terminaalin vasempaan kontekstiin, voidaan laskea induktiivisesti käyttämällä äärellisen tilan konetta, joka skannaa merkkijonoa vasemmalta oikealle. Kuvailkaamme tätä prosessia rakentavasti.

    Kutsutaan LR(0)-tilannetta kielioppisääntöön, jossa on yksi merkitty paikka säännön oikean puolen symbolien välissä. Esimerkiksi kielioppi S:A; A:aAA; A:b seuraavat LR(0)-tilanteet on olemassa: S:_A; S:A_; A:_aAA; A:a_AA; A:aA_A; A:aAA_; A:_b; A:b_. (sijainti on merkitty alaviivalla).

    Sanotaan, että ketju x on yhdenmukainen tilanteen A:b_c kanssa, jos x=ab ja a kuuluu ryhmään L(A). (Toisin sanoen LR-lähtöä voidaan jatkaa x_... = ab_...:: abc_...:: aA_...:: S_.) Näissä termeissä L(A:b) on joukko merkkijonoista, jotka vastaavat tilannetta A:b_, L(A)

    - tilanteen A:_b mukaiset ketjut mille tahansa säännölle A:b.

    Olkoon V(u) tilanteiden joukko, jotka ovat yhdenmukaisia ​​u:n kanssa. Osoitetaan, että funktio V on induktiivinen.

    Jos joukko V(u) sisältää tilanteen A:b_cd, niin tilanne A:bc_d kuuluu ryhmään V(uc). (c - terminaalinen tai ei-pääte; b, d - terminaalien ja ei-terminaalien sekvenssit (voivat olla tyhjiä). Ei ole muita tilanteita muotoa A:b_d, jossa V(uc) ei ole tyhjä b. Jäljelle jää tilanteet muodossa C:_... V(uc) jokaiselle ei-päätteiselle C:lle, jonka vasen konteksti sisältää uc. Jos tilanne A:..._C... (C-ei-pääte) kuuluu joukkoon V(uc), niin uc kuuluu ryhmään L(C) ja V(uc) sisältää tilanteet muotoa C:_... varten. kaikki kieliopin C-säännöt.

    V(e) sisältää tilanteet S:_... (S-alkumerkki), sekä tilanteet A:_... jos ei-pääte A esiintyy välittömästi _:n jälkeen tilanteissa, jotka jo sisältyvät V(e) -kohtaan.

    Lopuksi olemme valmiita määrittelemään LR(0)-kieliopin. Olkoon u pinon sisältö LR-jäsennyksen aikana ja V(u) u:n mukaisten LR(0)-tilanteiden joukko. Jos V(u) sisältää tilanteen, joka on muotoa A:x_ (terminaalien ja ei-päätteiden x-jono), niin u kuuluu L(A:x) ja x:n konvoluutio A:ksi on sallittu ) sisältää tilanteen A:..._a .. (a-pääte), silloin siirto on sallittu. Siirto-konvoluutioristiriidan sanotaan olevan olemassa, jos sekä siirto että konvoluutio ovat sallittuja samalle merkkijonolle u. Konvoluutio-pelkistyskonfliktin sanotaan olevan olemassa, jos eri sääntöjen mukaiset konvoluutiot ovat sallittuja.

    Kielioppia kutsutaan nimellä LR(0), jos LR-päättelyn aikana ei ole siirto-vähennys- tai fold-reduce-konflikteja kaikille pinon tiloille.

    1.2.1.2 LR(k) - kieliopit

    Vain pinon tilaa käytetään päätettäessä siirron ja taittamisen välillä LR(0)-jäsennyksessä. LR(k)-jäsennys ottaa huomioon myös syöteketjun näkymätön osan (ns. ennakkoketjun) ensimmäiset k merkkiä. Menetelmän perustelemiseksi sinun tulee toistaa huolellisesti edellisen kappaleen perustelu ja tehdä muutoksia määritelmiin.

    Sisällytämme myös ennakkoketjun sääntöjen vasempaan kontekstiin. Jos oikea tulos käyttää lähtöä wAu: wvu, niin pari wv,FIRSTk(u) kuuluu Lk(A:v) ja pari w,FIRSTk(u) kuuluu Lk(A:han). Vasemmanpuoleisten kontekstien joukko, kuten LR(0):n tapauksessa, voidaan laskea käyttämällä vasemman ketjun induktiota. Kutsutaanpa LR(k)-tilannetta pariksi: kielioppisääntö, jolla on merkitty paikka ja etuketju, jonka pituus on enintään k. Erottelemme säännön ennakkoketjusta pystyviivalla.

    Sanotaan, että ketju x on yhdenmukainen tilanteen A:b_c|t kanssa, jos on LR-ulostulo: x_yz = ab_yz:: abc_z:: aA_z:: S_ ja FIRSTk(z) = t. Säännöt tilajoukon Vk induktiivista laskemista varten ovat seuraavat:

    Vk(e) sisältää tilanteet S:_a|e kaikille säännöille S:a, joissa S on aloitusmerkki. Jokaiselle Vk(e)-tilanteelle A:_Ba|u, jokaiselle säännölle B:b ja ketjulle x, joka kuuluu FIRSTk(au:aan), on tarpeen lisätä tilanne B:_b|x kohtaan Vk(e).

    Jos Vк(w) sisältää tilanteen A:b_cd|u, niin tilanne A:bc_d|u kuuluu Vk(wc:lle). Jokaista tilannetta A:b_Cd|u varten Vk(wc), jokaista sääntöä C:f ja ketjua x, joka kuuluu FIRSTk(du):iin, on tarpeen lisätä tilanne C:_f|x kohtaan Vk(wc).

    Käytämme rakennettuja LR(k)-tilojen joukkoja siirtokonvoluutio-ongelman ratkaisemiseen. Olkoon u pinon sisältö ja x yläketju. Ilmeisesti säännön A:b mukainen konvoluutio voidaan suorittaa, jos Vk(u) sisältää tilanteen A:b_|x. Siirron sallivuuden päättäminen vaatii huolellisuutta, jos kielioppi sisältää e-säännöt. Tilanteessa A:b_c|t (c ei ole tyhjä), siirto on mahdollinen, jos c alkaa terminaalista ja x kuuluu kohtaan FIRSTk(ct). Epävirallisesti puhuen, voit työntää säännön oikean puolen vasemmanpuoleisimman symbolin pinoon valmistaen seuraavan konvoluution. Jos c alkaa ei-päätteellä (tilanne näyttää A:b_Cd|t), niin symbolin työntäminen pinoon valmistautuessa konvoluutio C:ksi on mahdollista vain, jos C ei synnytä tyhjää ketjua. Esimerkiksi tilassa V(e)= S:_A|e; A:_AaAb|e,a, A:_|e,a ei ole sallittuja siirtymiä, koska Kun pääteketjuja johdetaan A:sta, on jossain vaiheessa sovellettava sääntöä A:e ketjun vasemmassa päässä sijaitsevaan ei-pääteiseen A:han.

    Määritellään joukon FIRSTk(x) kaikista alkioista koostuva joukko EFFk(x), jonka derivoinnin aikana x:n vasemmassa päässä olevaa nonterminaalia (jos sellainen on) ei korvata tyhjällä ketjulla. Näissä termeissä siirto on sallittu, jos joukossa Vk(u) on tilanne A:b_c|t, c ei ole tyhjä ja x kuuluu ryhmään EFFk(ct).

    Kielioppia kutsutaan LR(k)-kieliopiksi, jos mikään LR(k)-tila ei sisällä kahta tilannetta A:b_|u ja B:c_d|v siten, että u kuuluu ryhmään EFFk(dv). Tällainen pari vastaa taitto-vähennyskonfliktia, jos d on tyhjä, ja shift-fold-konfliktia, jos d ei ole tyhjä.

    Käytännössä LR(k)-kielioppeja ei käytetä k>1:lle. Tähän on kaksi syytä. Ensinnäkin: erittäin suuri määrä LR(k)-tiloja. Toiseksi: jokaiselle LR(k)-kieliopin määrittelemälle kielelle on olemassa LR(1)-kielioppi; Lisäksi jokaiselle deterministiselle KS-kielelle on olemassa LR(1)-kielioppi.

    LR(1)-tilojen lukumäärä käytännössä mielenkiintoisia kielioppeja on myös melko suuri. Tällaisilla kieliopeilla on harvoin LR(0)-ominaisuus. Käytännössä käytetään useammin LR(0) ja LR(1) välissä olevaa menetelmää, joka tunnetaan nimellä LALR(1).

    1.2.2 LALR(1) - kieliopit

    Nämä kaksi menetelmää perustuvat samaan ajatukseen. Muodostetaan joukko kieliopin kanonisia LR(0)-tiloja. Jos tämä joukko ei sisällä ristiriitoja, voidaan käyttää LR(0)-jäsennintä. Muuten pyrimme ratkaisemaan syntyneet ristiriidat yhden merkin ennakkoketjua harkiten. Toisin sanoen yritetään rakentaa LR(1)-jäsennin, jossa on useita LR(0)-tiloja.

    LALR(1)-menetelmä (Look Ahead) on seuraava. Otetaan käyttöön ekvivalenssirelaatio LR(1)-tilanteiden joukkoon: katsomme kahta tilannetta ekvivalentiksi, jos ne eroavat toisistaan ​​vain johtavissa ketjuissaan. Esimerkiksi tilanteet A:Aa_Ab|e ja A:Aa_Ab|a ovat vastaavia. Muodostetaan kanoninen joukko LR(1)-tiloja ja yhdistetään tilat, jotka koostuvat joukosta ekvivalenttitilanteita.

    Jos tuloksena oleva tilajoukko ei sisällä LR(1)-ristiriitoja ja sen vuoksi sallii LR(1)-jäsentimen rakentamisen, kieliopin sanotaan olevan LALR(1)-ominaisuus.

    2. Kääntäjän kehittäminen

    2.1 Vaatimusanalyysi

    Tässä kurssityössä on tarpeen kehittää opetuskääntäjä tulkin muodossa vastaavan muodollisen kieliopin määrittelemästä kielestä. Tulkin kehittämisessä on neljä päävaihetta:

    Leksikaalisen analysaattorin suunnittelu;

    Myyntiautomaatin suunnittelu;

    Ohjelmiston toteutus jäsentäjä;

    Tulkintamoduulin kehittäminen.

    Kehitys toteutetaan leikkaussalin avulla Windows-järjestelmät XP päällä henkilökohtainen tietokone IBM PC Intel Pentium IV -prosessorilla.

    Perustuu kehitystrendeihin ohjelmisto Opetuskääntäjän toteuttamiseksi valittiin Visual Studio 2010 -ympäristössä C#-ohjelmointikieli.

    2.2 Suunnittelu

    2.1.1 Leksikaalisen analysaattorin suunnittelu

    Leksikaalinen analyysi sisältää käännetyn (lähde)ohjelman skannauksen ja lähdetekstin lauseet muodostavien lekseemien tunnistamisen. Tokeneita ovat erityisesti avainsanat, operaatiomerkit, tunnisteet, vakiot, erikoismerkit jne.

    Leksikaalisen analysaattorin (skannerin) työn tulos on merkkijono, jossa jokaista merkkiä yleensä edustaa jokin kiinteäpituinen koodi (esimerkiksi kokonaisluku), sekä syntaktisista (leksikaalisista) virheistä kertovien viestien generointi. , jos mitään. Jos merkki on esimerkiksi avainsana, sen koodi antaa kaiken tarvittavat tiedot. Esimerkiksi tunnisteen tapauksessa vaaditaan lisäksi tunnistetun tunnisteen nimi, joka yleensä kirjataan tunnistetaulukkoon, joka on järjestetty pääsääntöisesti listoilla. Samanlainen taulukko tarvitaan vakioille.

    Lekseema voidaan kuvata kahdella pääpiirteellä. Yksi niistä on, että lekseemi kuuluu tiettyyn luokkaan (muuttujat, vakiot, operaatiot jne. Toinen attribuutti määrittelee tämän luokan tietyn elementin).

    Symbolitaulukon erityisellä muodolla (tietorakenteella) ei ole väliä lekserin tai jäsentimen kannalta. Molempien tarvitsee vain tarjota kyky hankkia indeksi, joka yksilöi esimerkiksi tietyn muuttujan, ja palauttaa indeksin arvon täydentääkseen tietoja tietystä muuttujan nimestä symbolitaulukossa.

    Tunnustaulukon tarkastelu suorittaa kaksi päätoimintoa:

    a) tallennetaan uusi nimi taulukkoon muuttujakuvauksia käsiteltäessä;

    b) taulukkoon aiemmin tallennetun nimen etsiminen.

    Tämän avulla voit tunnistaa virheelliset tilanteet, kuten muuttujan useat kuvaukset ja kuvaamattoman muuttujan olemassaolon.

    Leksikaalisen analysaattorin kehittäminen koostuu osittain erilaisten automaattien simuloinnista tunnistamaan tunnisteita, vakioita, varattuja sanoja jne. Jos tunnukset eri tyyppejä alkaa samalla merkillä tai samalla merkkijonolla, niiden tunnistaminen voi olla tarpeen yhdistää.

    Ajamalla leksikaalista analysaattoria jaamme ohjelmamme tokeneihin, minkä jälkeen jokainen merkki läpäisee pituustarkistuksen (merkki ei saa olla yli 11 merkkiä). Kun tämä vaihe on suoritettu onnistuneesti, tarkistamme merkkien oikean sijainnin (avainsanat var, begin, end, for, to, do, end_for). Sitten analysoimme muuttujalekseemejä - niiden kuvauksessa ei saa olla numeroita eikä niitä saa toistaa. Päällä viimeinen vaihe Tarkistamme lekseemien (avainsanat, tuntemattomat tunnisteet) oikeinkirjoituksen. Jos vähintään yksi tarkistuksista epäonnistuu, leksikaalinen analysaattori tulostaa virheen.

    Leksikaalisen analysaattoriohjelman kaavio on esitetty liitteessä B kuvassa B.1.

    2.2.2 Myyntiautomaatin suunnittelu

    Määritellään seuraava kielioppi:

    G: (Vt, Va, I, R),

    missä Vt on päätemerkkien joukko, Va on ei-päätemerkkien joukko, I on kieliopin alkutila, R on kieliopin sääntöjen joukko.

    Tätä kielioppia varten määrittelemme joukot terminaalisia ja ei-päätemerkkejä:

    Laaditaan säännöt kielioppimme G:lle ja esitetään ne taulukossa 1.

    Taulukko 1 - Kielioppisäännöt

    Sääntö nro

    Säännön vasen puoli

    Säännön oikea puoli

    f ID = EX t EX d LE n;

    Taulukon 1 jatko.

    Sääntö nro

    Säännön vasen puoli

    Säännön oikea puoli

    Lekseemien nimitykset, lekseemien kääntäminen koodeiksi ja luettelo kielioppinimikkeistä on esitetty taulukoissa 2, 3 ja 4.

    Taulukko 2 - Lekseemien nimitykset

    Tunnusmerkintä

    avainsana "aloittaa" (toimintojen kuvauksen alku)

    avainsana "loppu" (toiminnan lopun kuvaus)

    avainsana "var" (muuttujan kuvaus)

    avainsana "lue" (tietojen syöttöoperaattori)

    avainsana "kirjoita" (tiedonsyöttöoperaattori)

    avainsana "for" (silmukkalause)

    avainsana "to"

    avainsana "tee"

    avainsana "end_case" (silmukan loppulause)

    muuttujatyyppi "integer"

    lisäystoiminto

    vähennysoperaatio

    kertolaskutoiminto

    erotinmerkki ":"

    erotinmerkki ";"

    erotinmerkki "("

    erotinmerkki ")"

    erotinmerkki ","

    Tunnusmerkintä

    erotinmerkki "="

    Taulukko 3 - Lekseemien kääntäminen koodeiksi

    <цифра>

    <буква>

    Taulukko 4 - Luettelo kielioppisymboleista

    Nimitys

    Selitys

    Ohjelmoida

    Laskelmien kuvaus

    Muuttujien kuvaus

    Luettelo muuttujista

    Operaattori

    Tehtävä

    Ilmaisu

    Alalauseke

    Binäärioperaatiot

    Unaariset operaatiot

    Luettelo tehtävistä

    Tunniste

    Jatkuva

    Rakennetaan deterministinen alhaalta ylös -tunnistin.

    Harkitse seuraavia suhteita luodaksesi deterministisen alhaalta ylös -tunnistimen:

    a) Jos ryhmässä B on sellainen symboli, että jokin kieliopin sääntö sisältää ketjun AB ja on symboli xFIRST"(B), niin oletetaan, että suhteet x A:N JÄLKEEN määritetään symbolien x ja A välillä.

    b) Jos tietyssä kielioppissa on sääntö B -> bAb A, BV a, b, niin suhde A COVER x määräytyy A:n ja x:n välillä.

    Kaikki kielioppimme pysyy samana, eli:

    G: (Vt, Va, I, R),

    ja kieliopin G säännöt on annettu taulukossa 5.

    Taulukko 5 - Kielioppisäännöt

    Sääntö nro

    Säännön vasen puoli

    Säännön oikea puoli

    f ID = EX t EX d LE n;?

    Taulukon 5 jatkoa.

    Sääntö nro

    Säännön vasen puoli

    Säännön oikea puoli

    Missä? - merkki ketjun päähän.

    Määrittelemme joitain tapauksia:

    a) Tunnus koostuu useista latinalaisten aakkosten kirjaimista, eli oletetaan, että u = ( a, b, c, d, e, f,g, h, i,j,k, l,m, n , o, p, q, r, s, t, u, v, w, x, y, z)

    b) CO-vakio koostuu luvuista, eli oletetaan, että k = (0,1,2,3,4,5,6,7,8,9)

    Jotta kielioppimme olisi sekoitettu ensisijaisuusstrategia, seuraavien ehtojen on täytyttävä:

    a) Sähköisten sääntöjen puute

    b) Onko olemassa sääntöjä, joiden mukaan x A:N JÄLKEEN? A VERT x = ?

    c) A -> bYg

    ja on välttämätöntä, että IN AFTER x? B VERT x = ?

    eli kielioppissa ne suoritetaan IN AFTER x tai A AFTER x, missä x on ketjun b predikaattisymboli.

    a) ENSIMMÄINEN"(PG)=(PG?)

    ENSIMMÄINEN"(RG) = ENSIMMÄINEN(DE) = (RG, v,:, i,;)

    FIRST" (AL) = FIRST (b LE e)= (AL, b, e)

    FIRST" (DE) = FIRST (v LV: i;) = (DE, v,:, i,;)

    FIRST" (LV) = FIRST (ID, LV) = (LV, ID)

    FIRST" (OP) =(OP, ID, CO)

    ENSIMMÄINEN" (EQ) = ENSIMMÄINEN(ID = EX;) = (EQ, =,;)

    FIRST" (EX) = (EX, SB, -)

    FIRST" (BO) =(B0, +,*,-)

    ENSIMMÄINEN" (SB) = ENSIMMÄINEN((EX)SB) ? ENSIMMÄINEN(OP) ? ENSIMMÄINEN(BO)=(SB, (,), OP, BO);

    ENSIMMÄINEN" (LE) = ENSIMMÄINEN(EQ) = (LE, (,), =,;, f, t, d, n, w, r)

    FIRST" (UO) = (UO,-)

    FIRST" (ID) = FIRST" (u) = (u)

    FIRST" (CO) = ENSIMMÄINEN" (k) = (k)FIRST" (e) =( e)

    ENSIMMÄINEN" (b) =( b)

    ENSIMMÄINEN" (e) =( e)

    ENSIMMÄINEN" (v) =( v)

    ENSIMMÄINEN" (w) =( w)

    ENSIMMÄINEN" (r) =( r)

    ENSIMMÄINEN" (i) =( i)

    ENSIMMÄINEN" (f) =( f)

    ENSIMMÄINEN" (d) = (d)

    ENSIMMÄINEN" (n) =( n)

    ENSIMMÄINEN" (c) =( c)

    ENSIMMÄINEN" (+) =( +)

    ENSIMMÄINEN" (*) =( *)

    ENSIMMÄINEN" (-) =( -)

    ENSIMMÄINEN" (,) =(,)

    ENSIMMÄINEN" (;) =(;)

    ENSIMMÄINEN" (:) =(:)

    ENSIMMÄINEN" (=) = ( = )

    ENSIMMÄINEN" (() =( ()

    ENSIMMÄINEN" ()) =() )

    ENSIMMÄINEN" (u) = (u)

    ENSIMMÄINEN" (k) = (k)

    b) TRACE "(AL) = (?)" (PG)=(?,b,e)

    SEURAAVA ` (DE) = (?)?FIRST"(AL)= (?, b, e)

    SEURAAVA ` (LV) = (?)?FIRST"(:)= (?,:)

    SEURAAVA ` (OP) = (?)?FIRST"(SB)= (?,;,), d, t, +, -, *)

    TRACK ` (EQ) = (?)?FIRST"(LE)=(?, (,),;, f, =, t, d, n,w,r )

    RAITA ` (EX) = (?)?ENSIMMÄINEN"(t)?ENSIMMÄINEN"(d)?ENSIMMÄINEN"(;)?ENSIMMÄINEN"())=(?, t,d,;,))

    SEURAAVA ` (BO) = (?)?FIRST"(SB)= (?, (,), OP, BO)

    SEURAAVA ` (UO) = (?)?FIRST"(SB)= (?, (,), OP, BO)

    TRACE` (SB) = (?) TRACE"(EX)= (?, t,d,;,), +, *, -)

    RAITA ` (LE) = (?) ?ENSIMMÄINEN"(e) ?ENSIMMÄINEN"(n) = (?, e, n)

    TRACE `(ID)= (?)? SEURAAVA" (OP) ? ENSIMMÄINEN" (=) =(?,;,), d, t, +, -, *, =)

    TRACE `(CO) = (?)? TRACE" (OP)= (?,;,), d, t, +, -, *, =)

    SEURAAVA ` (b) =(?)?ENSIMMÄINEN"(LE)= (?, u, =,;)

    TRACE` (e) =(?) TRACE"(AL)= (?, b)

    SEURAAVA ` (v) =(?)?ENSIMMÄINEN"(LV)= (?, u)

    SEURAAVA ` (w) =(?)?ENSIMMÄINEN"(()= (?, ()

    SEURAAVA ` (r) =(?)?ENSIMMÄINEN"(() = (?, ()

    SEURAAVA ` (i) =(?)?ENSIMMÄINEN"(;)= (?,; )

    SEURAAVA ` (f) =(?)?ENSIMMÄINEN"(ID) = (?, u)

    SEURAAVA ` (d) =(?)?ENSIMMÄINEN"(LE)= (?, u, =,;)

    SEURAAVA ` (n) =(?)?ENSIMMÄINEN"(i) = (?, i )

    TRACE ` (+) =(?) TRACE"(IN) = (?, +,*,-)

    TRACE ` (-) =(?) TRACE"(IN) = (?, +,*,-)

    TRACE ` (*) =(?) TRACE"(IN) = (?, +,*,-)

    TRACK ` (;) =(?)? TRACK" (DE)? TRACK `(LE1)? TRACK" (EQ) = (?, b, e, l, u)

    SEURAAVA ` (:) =(?)?ENSIMMÄINEN"(i)= (?, i )

    SEURAAVA ` (=) = (?)?ENSIMMÄINEN"(EX) = (? (,), u, k, +, -, *)

    SEURAAVA ` (() =(?)?FIRST"(DE)= (?, v,:, i,;)

    TRACE ` ()) =(?)? ENSIMMÄINEN"(;) = (?,; )

    TRACE ` (,) =(?)? FIRST"(LV) = (?, u)

    TRACE `(u) =(?)? FIRST" (ID)= ( u, ?)

    TRACE `(k) =(?)? ENSIMMÄINEN (CO)= (?, k)

    c) PG ->DE AL

    AL DE:N JÄLKEEN = (b,e) DE:N JÄLKEEN = ((b DE), (e DE) )

    e LE:N JÄLKEEN = ((e LE))

    VASEN b = ((,), =,;, f, t, d, n, w, r) b = (((b), ()b), (=b), (;b), ( fb), (tb), (db), (nb), (wb), (rb))

    ;i:n JÄLKEEN = ((; i))

    i JÄLKEEN: = ( (i:) )

    : LV:n JÄLKEEN = ( (: LV) )

    LV JÄLKEEN v = ( (ID, v) )

    LV JÄLKEEN, = ((ID,))

    ID:N JÄLKEEN = ((,u))

    LE AFTER EQ = ((,), =,;, f, t, d, n, w, r ) EQ = (((EQ), () EQ), (= EQ), (; EQ), ( f EQ), (t EQ), (d EQ), (n EQ), (w EQ), (r EQ))

    LE -> r (DE);

    ; JÄLKEEN) = ((;)))

    ) DE:N JÄLKEEN = (((DE))

    DE JÄLKEEN (= (= ((v)), (:)), (i)), (;)), (e)))

    (JÄLKEEN r = (((r))

    LE -> w (DE);

    ; JÄLKEEN) = ((;)))

    ) VIIMEINEN DE = (((DE))

    DE JÄLKEEN (= ((v)), (:)), (i)), (;)), (e)))

    (W:n JÄLKEEN = (((w))

    LE -> f ID = EX t EX d LE n;

    ; n JÄLKEEN = ((;n))

    n LE:N JÄLKEEN = ( (n, LE))

    LE d = (((,), =,;, f, t, d, n, w, r)) ? d:n JÄLKEEN = (((d), ()d), (;d), (f d), (t d), (d d), (n d), (v d), (r d))

    d EX:N JÄLKEEN = ((d, EX))

    EX t:n JÄLKEEN = (BO, -) ? t:n JÄLKEEN = ((BO t), (- t))

    t EXIN JÄLKEEN = ( t EX)

    EX AFTER = = ((BO, -) ? JÄLKEEN = = ((BO =), (- =))

    ID:N JÄLKEEN = ((= ID))

    ID f = ((ID f))

    EQ -> ID = EX;

    ; EXIN JÄLKEEN = ((; EX )

    EX AFTER = = (BO, -) ? JÄLKEEN = = ((BO =), (- =))

    u:n JÄLKEEN = ( (=u))

    SB UO:N JÄLKEEN = ( (,), OP, BO ) UO:N JÄLKEEN = (((UO), (OP UO), (BO UO) )

    ) EXIN JÄLKEEN = ( ()EX) )

    EX AFTER (= (BO, -) ? AFTER (= ((BO (),), (- ()))

    SB->SB BO SB

    SB BO:N JÄLKEEN = ((,), OP, BO) BO:N JÄLKEEN = (((BO), ()BO), (OP BO), (BO BO))

    BO SB:N JÄLKEEN = (+,*,-) SB:N JÄLKEEN = ((+SB), (*SB), (-SB))

    ID u:n JÄLKEEN = ((u, u))

    G) PG ->DE AL

    AL COLLECTION PG = AL COLLECTION TRACE" (PG) = ((AL ?))

    e COLLECTION AL = e KERÄYKSEN JÄLJÄ"(AL)= ((eb), (e?))

    =; KERÄYSRAITA"(DE) = ((;b), (;?))

    LV KERÄYS LV = LV KERÄYSPOLKU" (LV) = ((LV:), (LV?))

    TUNNUSTEN KERÄYS LV = TUNNUSTEN KERÄYSRAITA" (LV) = ((ID:), (ID ?))

    ; SUORA LE =; KERÄYSRAITA" (LE) = ((; e), (;?), (; n))

    LE -> f ID = EX t EX d LE n;

    ; SUORA LE =; KERÄYSRAITA" (LE) = ((; e), (;?), (; n))

    EQ COLLECTION LE = EQ COVER TRACE" (LE) = ((EQ e), (EQ?), (EQ n))

    EQ -> ID = EX;

    ; COLAPSE EQ =; KERÄYSRAITA" (EQ) = ((; (), (;)), (;;), (;f), (;?), (;=), (;t), (;d), (; n), (;w), (;r))

    SB COLLECTION EX = SB COVER TRACE" (EX) = ((SB t), (SB?), (SB d), (SB)), (SB;), (SB(), (SB=), (SBf ), (SBn), (SBw), (SBr) )

    ) KERÄYS SB = SB KERÄYSJÄLJÄ" (SB) = (() t), ()?), () d), ())), ();))

    OP COLLECTION SB = OP COLLECTION TRACE" (SB) = ((OP t), (OP ?), (OP d), (OP)), (OP;))

    SB->SB BO SB

    SB COLLECTION SB = SB COVER TRACE" (SB) = ((SB, t), (SBd), (SB;). (SB)), (SB+), (SB-), (SB*), (SB? ) }

    COLLECTION UO = - KERÄYSRATA" (UO) = ( (-?), (--))

    KOKOELMA BO = + KERÄYSRAITA" (BO) = ((++), (+?), (+*), (+-))

    * KOKOELMA BO = * KOKOELMARAITA" (BO) = ((*+), (*?), (**), (*-))

    KOKOELMA BO = - KERÄYSRAITA" (BO) = ((-+), (-?), (-*), (--))

    TUNNUSTEN KERÄÄMINEN OP = ID KAITTAJÄLJÄ" (OP) = ((ID+), (ID?), (ID*), (ID-))

    KANSI OP = KANNEN JÄLKI" (OP) = ((CO+), (CO?), (CO*), (CO-), (CO;), (COd), (COt), (CO)))

    TUNNUSKERÄYN ID = TUNNUSTEN KERÄYKIRJA" (ID) = ((ID)), (ID ?), (ID k), (ID+), (ID-), (ID*), (ID=), (IDt) , (IDd)))

    u KOKOELMIEN TUNNUS = l KERÄYSRAITA" (ID) = ((u)), (u?), (uk), (u+), (u-), (u*), (u=), (ut), (ud)))

    KANSI CO = KANSIJÄLKI" (CO) = (CO+), (CO?), (CO*), (CO-), (CO;), (COd), (COt), (CO)))

    k KERÄYS CO = k KERÄYSJÄLJÄ" (CO) = (k+), (k?), (k*), (k-), (k;), (kd), (kt), (k)))

    Yksi ristiriitatilanne havaittiin sääntöjen romahtamisen yhteydessä

    OP -> ID ja ID -> u ID

    Kirjoitamme ID1 -> ID, joten kirjoitamme uudelleen säännön ID1 -> u ID

    Siksi suoritamme konvoluutiooperaatioita.

    ID1 KERÄYDEN ID = ID1 KERÄYSREITTI" (ID) = ((ID1)), (ID1 ?), (ID1 k), (ID1+), (ID1-), (ID1*), (ID1=), (ID1t) , (ID1d)))

    Jokaiselle parille (x, A)? x A:N JÄLKEEN rakennamme siirtymäfunktion, joka määrittää siirtotoiminnon??(S 0 , x, A) = (S 0 , A)

    ? (S0, b, DE) = (S0, DEb)

    ? (S0, e, DE) = (S0, DEe)

    ? (S0, e, LE) = (S0, LEe)

    ? (S0,), b) = (S0, b))

    ? (S0,;, b) = (S0, b;)

    ? (S0, (, b) = (S0, b()

    ? (S0, =, b) = (S0, b=)

    ? (S0, f, b) = (S0, bf)

    ? (S0, t, b) = (S0, bt)

    ? (S0, d, b) = (S0, bd)

    ? (S0, n, b) = (S0, bn)

    ? (S0, w, b) = (S0, bw)

    ? (S0, r, b) = (S0, br)

    ? (S0,;, i) = (S0, i;)

    ? (S0, i,:) = (S0, i:)

    ? (S0,:LV) = (S0,LV:)

    ? (S0, ID, v) = (S0, vID)

    ? (S0,ID,) = (S0,ID)

    ? (S0, u) = (S0, u,)

    ? (S0, (, EQ)= (S0, EQ()

    ? (S0,), EQ)= (S0, EQ))

    ? (S0, =, EQ)= (S0, EQ=)

    ? (S0,;, EQ)= (S0, EQ;)

    ? (S0, f, EQ)= (S0, EQf)

    ? (S0, t, EQ) = (S0, EQt)

    ? (S0, d, EQ) = (S0, EQd)

    ? (S0, n, EQ) = (S0, EQn)

    ? (S0, w, EQ)= (S0, EQw)

    ? (S0, r, EQ) = (S0, EQr)

    ? (S0,;,)) = (S0,);)

    ? (S0, (, DE) = (S0, DE()

    ? (S0, v,)) = (S0,)v)

    ? (S0,;,)) = (S0,);)

    ? (S0, i,)) = (S0,)i)

    ? (S0,:,)) = (S0,):)

    ? (S0, e,)) = (S0,)e)

    ? (S0, (, r) = (S0, r()

    ? (S0, (, w) = (S0, w()

    ? (SO,;, n) = (SO, n;)

    ? (S0, n, LE) = (S0, LEn)

    ? (S0, (, d) = (S0, d()

    ? (S0,), d) = (S0, d))

    ? (SO,;, d) = (SO, d;)

    ? (S0, f, d) = (S0, df)

    ? (S0, t, d) = (S0, dt)

    ? (S0, d, d) = (S0, dd)

    ? (S0, n, d) = (S0, dn)

    ? (S0, w, d) = (S0, dw)

    ? (S0, r, d) = (S0, dr)

    ? (S0, d, EX) = (S0, EXd)

    ? (S0, BO, t) = (S0, tBO)

    ? (S0, -, t) = (S0, t-)

    ? (S0, t, EX) = (S0, EXt)

    ? (S0, BO, =) = (S0, =BO)

    ? (S0, -, =) = (S0, =-)

    ? (S0, =, ID) = (S0, ID=)

    ? (S0, ID, f) = (S0, fID)

    ? (S0,;, EX) = (S0, EX;)

    ? (S0, =, u) = (S0, u=)

    ? (S0, (, UO) = (S0, UO()

    ? (S0, OP, UO) = (S0, UO OP)

    ? (S0, BO, UO) = (S0, UO BO)

    ? (S0,), EX) = (S0, EX))

    ? (S0, BO, () = (S0, (BO)

    ? (S0, BO, -) = (S0, -BO)

    ? (S0, (, BO) = (S0, BO()

    ? (S0,),BO) = (S0,)BO)

    ? (S0, OP, BO) = (S0, BOOP)

    ? (S0, +, SB) = (S0, SB+)

    ? (S0, *, SB) = (S0, SB*)

    ? (S0, -, SB) = (S0, SB-)

    ? (S0, u, u) = (S0, uu)

    Jokaiselle parille (x, A)? Ja CONVERT x rakennamme siirtymäfunktion, joka määrittää konvoluution toiminnan?? * (S0, x, bA) = (S0, B), missä B->bA

    ? * (S 0 , AL, ?) = (S 0 , PG)

    ? * (S 0 , e, b) = (S 0 , AL)

    ? * (S 0 , n, ?) = (S 0 , AL)

    ? * (S 0 ,;, b) = (S 0 , DE)

    ? * (S 0 ,;, ?) = (S 0 , DE)

    ? * (S 0 ,;, e) = (S 0 , DE)

    ? * (S 0 , LV,:) = (S 0 , LV)

    ? * (S 0 , LV, ?) = (S 0 , LV)

    ? * (S 0 , ID, ?) = (S 0 , LV)

    ? * (S 0 , ID, e) = (S 0 , LV)

    ? * (S 0 ,;, e) = (S 0 , LE)

    ? * (S 0 ,;, ?) = (S 0 , LE)

    ? * (S 0 ,;, n) = (S 0 , LE)

    ? * (S 0 , EQ, n) = (S 0 , LE)

    ? * (S 0 , EQ, e) = (S 0 , LE)

    ? * (S 0 , EQ, ?) = (S 0 , LE)

    ? * (S 0 ,;, e) = (S 0 , LE)

    ? * (S 0 ,;, ?) = (S 0 , LE)

    ? * (S 0 ,;, () = (S 0 , EQ)

    ? * (S 0 ,;,)) = (S 0 , EQ)

    ? * (S 0 ,;, f) = (S 0 , EQ)

    ? * (S 0 ,;, =) = (S 0 , EQ)

    ? * (S 0 ,;, t) = (S 0 , EQ)

    ? * (S 0 ,;, d) = (S 0 , EQ)

    ? * (S 0 ,;, n) = (S 0 , EQ)

    ? * (S 0 ,;, w) = (S 0 , EQ)

    ? * (S 0 ,;, r) = (S 0 , EQ)

    ? * (S 0 , SB, ?) = (S 0 , EX)

    ? * (S 0 , SB, d) = (S 0 , EX)

    ? * (S 0 , SB,)) = (S 0 , EX)

    ? * (S 0 , SB,;) = (S 0 , EX)

    ? * (S 0 , SB, w) = (S 0 , EX)

    ? * (S 0 , SB, r) = (S 0 , EX)

    ? * (S 0 , SB, f) = (S 0 , EX)

    ? * (S 0 , SB, =) = (S 0 , EX)

    ? * (S 0 , SB, t) = (S 0 , EX)

    ? * (S 0 , SB, ?) = (S 0 , SB)

    ? * (S 0 , SB, () = (S 0 , SB)

    ? * (S 0 , SB,)) = (S 0 , SB)

    ? * (S 0 , SB, u) = (S 0 , SB)

    ? * (S 0 , SB, k) = (S 0 , SB)

    ? * (S 0 , SB, +) = (S 0 , SB)

    ? * (S 0 , SB, -) = (S 0 , SB)

    ? * (S 0 , SB, *) = (S 0 , SB)

    ? * (S 0 , SB, e) = (S 0 , SB)

    ? * (S 0 ,), t) = (S 0 , SB)

    ? * (S 0 ,), ?) = (S 0 , SB)

    ? * (S 0 ,), t) = (S 0 , SB)

    (S 0 ,),)) = (S 0 , SB)

    ? * (S 0 ,),;) = (S 0 , SB)

    ? * (S 0 , -, ?) = (S 0 , UO)

    ? * (S 0 , -, -) = (S 0 , UO)

    ? * (S 0 , +, +) = (S 0 , BO)

    ? * (S 0 , +, ?) = (S 0 , BO)

    ? * (S 0 , +, *) = (S 0 , BO)

    ? * (S 0 , -, +) = (S 0 , BO)

    ? * (S 0 , -, ?) = (S 0 , BO)

    ? * (S 0 , -, *) = (S 0 , BO)

    ? * (S 0 , -, -)) = (S 0 , BO)

    ? * (S 0 , *, +) = (S 0 , BO)

    ? * (S 0 , *, ?) = (S 0 , BO)

    ? * (S 0 , *, *) = (S 0 , BO)

    ? * (S 0 , *, -)) = (S 0 , BO)

    ? * (S 0 , u, +) = (S 0 , BO)

    ? * (S 0 , u, ?) = (S 0 , BO)

    ? * (S 0 , u, *) = (S 0 , BO)

    ? * (S 0 , u, -)) = (S 0 , BO)

    ? * (S 0 , k, +) = (S 0 , BO)

    ? * (S 0 , k, ?) = (S 0 , BO)

    ? * (S 0 , k, *) = (S 0 , BO)

    ? * (S 0 , k, -)) = (S 0 , BO)

    ? * (S 0 , CO, ?) = (S 0 , OP)

    ? * (S 0 , CO, +) = (S 0 , OP)

    ? * (S 0 , CO, *) = (S 0 , OP)

    ? * (S 0 , CO, -) = (S 0 , OP)

    ? * (S 0 , CO,;) = (S 0 , OP)

    ? * (S 0 , CO, d) = (S 0 , OP)

    ? * (S 0 , CO, t) = (S 0 , OP)

    ? * (S 0 , ID, -) = (S 0 , OP)

    ? * (S 0 , ID, *) = (S 0 , OP)

    ? * (S 0 , ID, ?) = (S 0 , OP)

    ? * (S 0 , ID, () = (S 0 , OP)

    ? * (S 0 , ID,)) = (S 0 , OP)

    ? * (S 0 , ID, u) = (S 0 , OP)

    ? * (S 0 , ID, k) = (S 0 , OP)

    ? * (S 0 , ID, -) = (S 0 , OP)

    ? * (S 0 , ID, +) = (S 0 , OP)

    ? * (S 0 , u,)) = (S 0 , I OP)

    ? * (S 0 , ID1, *) = (S 0 , ID)

    ? * (S 0 , ID1, ?) = (S 0 , ID)

    ? * (S 0 , ID1, () = (S 0 , ID)

    ? * (S 0 , ID1,)) = (S 0 , ID)

    ? * (S 0 , ID1, u) = (S 0 , ID)

    ? * (S 0 , ID1, k) = (S 0 , ID)

    ? * (S 0 , ID1, -) = (S 0 , ID)

    ? * (S 0 , ID1, +) = (S 0 , ID)

    ? * (S 0 , u,)) = (S 0 , ID)

    ? * (S 0 , u, ?) = (S 0 , BO)

    ? * (S 0 , u, k) = (S 0 , ID)

    ? * (S 0 , u, *)) = (S 0 , ID)

    ? * (S 0 , u, -)) = (S 0 , ID)

    ? * (S 0 , u, +)) = (S 0 , ID)

    ? * (S 0 , u, d)) = (S 0 , ID)

    ? * (S 0 , u, t)) = (S 0 , ID)

    ? * (S 0 , u, =)) = (S 0 , ID)

    ? * (S 0 , CO, ?) = (S 0 , CO)

    ? * (S 0 , CO, +) = (S 0 , CO)

    ? * (S 0 , CO, -) = (S 0 , CO)

    ? * (S 0 , CO, *) = (S 0 , CO)

    ? * (S 0 , CO,;) = (S 0 , CO)

    ? * (S 0 , CO, d) = (S 0 , CO)

    ? * (S 0 , CO, t) = (S 0 , CO)

    ? * (S 0 , CO,)) = (S 0 , CO)

    ? * (S 0 , k, +) = (S 0 , CO)

    ? * (S 0 , k, -) = (S 0 , CO)

    ? * (S 0 , k, *) = (S 0 , CO)

    ? * (S 0 , k,;) = (S 0 , CO)

    ?? * (S 0 , k, d) = (S 0 , CO)

    ? * (S 0 , k, t) = (S 0 , CO)

    ? * (S 0 , k,)) = (S 0 , CO)

    ? * (S 0 , k, () = (S 0 , CO)

    2.2.3 Jäsentimen ohjelmistototeutus

    Syntaktinen analysaattori (parser) lukee leksikaalisen analysaattorin luoman lekseemitiedoston, suorittaa kieliopin jäsentämisen, näyttää viestejä mahdollisista syntaksivirheistä ja luo välimuodon lähdeohjelman tallentamiseen. Jäsentimen kehittämisen lähtökohtana on vastaavan aikakauslehtikoneen suunnittelu ja toteutus.

    Alhaalta ylös -jäsennykseen deterministiselle alhaalta ylös -jäsentämiselle sen jälkeen, kun se on pienennetty muotoon oikea tyyppi Toimintoja AFTER ja ROLL on käytettävä myymäläkoneen suunnitteluun, jossa on yksityiskohtainen kuvaus kaikista siirtymätoiminnon sisällä olevista siirtymistä.

    Myyntiautomaattia kehitettäessä rakensimme siirtymäfunktioita, jotka ovat jäsentimen perusta. Kaikki siirtymätoiminnot voidaan jakaa kahteen tyyppiin:

    Aikakauslehtikoneen toimintajakso ilman syöttösymbolin lukemista (tyhjäjakso);

    Aikakauslehtikoneen toiminnan tuntomerkki syöttösymbolin lukemisella.

    Leksikaalista analysaattoria toteutettaessa jaoimme ohjelman lekseemiksi ja kirjoitimme ne listaksi. Käsittelemme sitten tämän luettelon jäsentimessä. Lähetämme sisääntuloon ohjelmamme (listan), alkusymbolin (PG) ja myymäläkoneen alamerkin (h0), jonka jälkeen valitaan haluttu siirtymätoiminto ja tehdään rekursiivinen kutsu.

    Jäseninohjelman kaavio on esitetty liitteessä B kuvassa B.2.

    2.2.4 Tulkkausmoduulin kehittäminen

    Kun kehitetään tulkkausmoduulia alkuperäisen ohjelman välimuotona Useimmiten käytetään postfix-muotoista merkintää, mikä tekee käännetyn ohjelman suoritusprosessin (tulkinta) toteuttamisen melko helppoa.

    Tarkastellaan perusperiaatteita postfix-muodon muodostamiseen ja suorittamiseen ilmaisujen kirjoitusmuodossa.

    Perussäännöt infix-lausekkeen muuntamiseksi postfix-lausekkeeksi ovat seuraavat.

    Lukuoperandit lisätään postfix-merkintään ja operaatiot kirjoitetaan pinoon.

    Jos pinon yläosassa olevalla toiminnolla on korkeampi (tai yhtä suuri) prioriteetti kuin tällä hetkellä luettavalla toiminnolla, pinon toiminto lisätään postfix-merkintään ja nykyinen toiminto työnnetään pinoon. Muussa tapauksessa (alemmalla prioriteetilla) vain nykyinen toiminto työnnetään pinoon.

    Lukemisen aloitussulku työnnetään pinoon.

    Sulkutuen lukemisen jälkeen kaikki toiminnot ensimmäiseen avaustukeen asti ponnataan pinosta ja lisätään postfix-merkintään, minkä jälkeen sekä avaus- että sulkemistuki hylätään, ts. ei ole sijoitettu postfix-tietueeseen tai pinoon.

    Kun koko lauseke on luettu, loput pinon toiminnot lisätään postfix-merkintään.

    Lausekkeen postfix-merkintä mahdollistaa sen laskemisen seuraavasti.

    Jos merkki on operandi, se kirjoitetaan pinoon. Jos merkki on operaatio, määritetty operaatio suoritetaan viimeisille pinoon kirjoitetuille elementeille (viimeiselle elementille), ja ne elementit (elementti) korvataan pinossa operaation tuloksella.

    Jos leksikaaliset ja syntaktiset analyysit on suoritettu onnistuneesti, siirrytään tulkintaan. Ensin tehdään sanoista lauseita, sitten käännetään lausekkeet postfix-merkinnöiksi ja lasketaan.

    Tulkin toimintakaavio on esitetty liitteessä B kuvassa B.3.

    2.3 Koodaus

    Ohjelma on toteutettu ympäristössä C#-kielellä Visuaalinen ohjelmointi Studio 2010. Ohjelman teksti on esitetty liitteessä A.

    Ohjelma sisältää viisi luokkaa. Käyttöliittymä on toteutettu MainForn-luokalla. LexAnalysis-luokan avulla toteutetaan leksikaalinen analyysimoduuli, SynAnalysis on syntaktinen analyysimoduuli, Interpreter on tulkintamoduuli, ProgramisciJakPolska on apuluokka lausekkeiden muuntamiseen käänteisiksi. Puolan sisääntulo(postfix).

    Ohjelmassa toteutettujen menettelyjen ja toimintojen tarkoitus on kuvattu taulukoissa 6,7,8.

    Taulukko 6 - Leksikaalisen analyysin menettelyjen tarkoitus ja toiminnot

    Samanlaisia ​​asiakirjoja

      Kääntäjä ohjelmana tai teknisenä välineenä, joka kääntää ohjelman. Pohditaan leksikaalisen analysaattorin rakentamisen pääpiirteitä. Johdatus kääntäjän kehittämisen vaiheisiin korkean tason kielen rajoitetusta osajoukosta.

      kurssityö, lisätty 6.8.2013

      Leksikaalinen ja jäsentäjäsuunnittelu opetuskieli. Muunnossäännöt loogisia lausekkeita POLIZissa. Triadien muodostaminen, niiden luettelon optimointi. Ohjelman looginen rakenne. Kääntäjä-tulkkimoduulien testaus.

      kurssityö, lisätty 28.5.2013

      C-Sharp-ohjelmointikielen yleiset ominaisuudet ja ominaisuuksien arviointi, sen samankaltaiset ja erityispiirteet C++:sta ja Javasta. Leksikaalisen ja syntaktisen analysaattorin kehittäminen tällä ohjelmointikielellä. Jäsennystaulukoiden laatiminen.

      kurssityö, lisätty 11.6.2010

      Analysaattoriohjelman suunnittelu, joka koostuu kahdesta osasta: leksikaalisesta analysaattorista, joka jakaa ohjelman lähdetekstin lekseemiksi ja täyttää nimitaulukon; jäsentäjä, joka tarkistaa tekstin yhteensopivuuden tietyn kieliopin kanssa.

      kurssityö, lisätty 14.6.2010

      Kirjoitetaan ohjelma, joka suorittaa syöttöohjelmointikielen leksikaalisen ja syntaktisen analyysin, luo lekseemitaulukon, joka ilmaisee niiden tyypit ja arvot, ja rakentaa myös syntaksipuun; syöttökielen teksti syötetään näppäimistöltä.

      kurssityö, lisätty 23.2.2012

      Metodologia C-kielen kääntäjän kehittämiseen ja osittaiseen toteuttamiseen C++-kielellä, joka jakaa alkuperäisen merkkiketjun minimaalisiksi jakamattomiksi kielikonstruktioiksi kielen sanaston perusteella. Ohjelman toiminnan analyysi.

      kurssityö, lisätty 19.3.2012

      Rakenne, luokitus ja kääntäjän toteutuksen vaatimukset. C++-kääntäjän analysoivan osan suunnittelu ja toteutus. Menetelmät leksikaalisen analyysin toteuttamiseksi. Algoritmi jäsentimen toiminnalle. Ohjelmiston toteutuksen periaatteet.

      kurssityö, lisätty 26.1.2013

      Kääntäjän luominen, joka käsittelee ohjelmakoodia Pascalissa ja käyttää vastaavia operaattoreita, luo ohjelman C-kielellä. Erikoisuudet ulkoinen erittely ja leksikaalisen analysaattorin työ. Ohjelman rakenne, tulosten näyttäminen näytöllä.

      kurssityö, lisätty 7.2.2011

      Kieliopillisen analyysin menetelmät. Opetuskääntäjän rakenteen kehittäminen perusohjelmointikielellä Object Pascal olio-visuaalisessa ohjelmointiympäristössä Borland DELPHI 6.0 Windows XP käyttöjärjestelmällä.

      kurssityö, lisätty 12.5.2013

      Työpöytäsovelluksen ohjelmistototeutus C#-ohjelmointikielellä. Suunnittelu ja rakenne käyttöliittymä, sen vaatimukset ja toimivuuden arviointi. Käyttöoppaan kehittäminen ja käyttö.

    OSA 7. Kääntäminen, kokoaminen ja tulkkaus

    Ohjelma on käskysarja, joka on suunniteltu tietokoneen suoritettavaksi. Tällä hetkellä ohjelmat on muotoiltu tekstiksi, joka kirjoitetaan tiedostoihin. Tämä teksti on tulosta ohjelmoijan toiminnasta ja yksityiskohdista huolimatta muodollinen kieli, jäännökset ohjelma ohjelmoijalle.

    Ohjelman luontiprosessi sisältää useita vaiheita. Ohjelman suunnitteluvaihetta seuraa ohjelmointivaihe. Tässä vaiheessa ohjelma kirjoitetaan. Ohjelmoijille tämä teksti on helpompi ymmärtää kuin binäärikoodi, koska erilaiset muistiinpanot ja nimet sisältävät lisätietoja.

    Ohjelman lähdetiedosto (kutsutaan myös lähdemoduuliksi) käsitellään kääntäjä , joka kääntää ohjelman ohjelmointikielestä koneellisesti luettavaksi koodisarjaksi.

    Kääntäjä - ohjelma tai tekninen työkalu, joka suorittaa ohjelman lähetys. Koneohjelma, joka kääntää kielestä toiseen ja erityisesti ohjelmointikielestä toiseen. Käsittelyohjelma, joka on suunniteltu muuttamaan lähdeohjelma objektimoduuliksi.

    Kääntäjä yleensä myös diagnosoi virheet, luo tunnistesanakirjoja, tuottaa ohjelmatekstejä tulostettaviksi jne.

    Ohjelman lähetys - jollakin ohjelmointikielellä esitetyn ohjelman muuntaminen toisella kielellä ja tietyssä mielessä ensimmäistä vastaavaksi ohjelmaksi.

    Kieltä, jolla syöttöohjelma esitetään, kutsutaan alkuperäinen kieli ja itse ohjelma - lähdekoodi. Tulostuskieli on ns Kohdekieli tai objektikoodi.

    Kääntäjätyypit

    Kääntäjät jaetaan:

    · Osoite. Toimiva laite, joka muuntaa virtuaaliosoitteen Virtuaalinen osoite) oikeaan osoitteeseen (englanniksi) Muistin osoite).

    · Dialogi. Tarjoaa ohjelmointikielen käytön aikajakotilassa.

    · Multipass. Muodostaa objektimoduulin useille lähdeohjelman näkymille.

    · Takaisin. Sama kuin dekääntäjä. Katso myös: purkaja, purkaja.

    · Yksittäinen passi. Muodostaa objektimoduulin lähdeohjelman yhdessä peräkkäisessä katselussa.

    · Optimointi. Suorittaa koodin optimoinnin luodussa objektimoduulissa.

    · Syntaktinen (syntaktinen). Vastaanottaa syötteenä kuvauksen kielen ja tekstin syntaksista ja semantiikasta kuvatulla kielellä, joka käännetään annetun kuvauksen mukaisesti.

    · Testata. Joukko kokoonpanokielen makrokomentoja, joiden avulla voit määrittää erilaisia ​​virheenkorjaustoimenpiteitä kokoonpanokielellä kirjoitetuissa ohjelmissa.



    Kääntäjät toteutetaan muodossa kääntäjät tai tulkit . Työn tekemisen kannalta kääntäjä ja tulkki eroavat merkittävästi toisistaan.

    Kääntäjä(Englanti) kääntäjä- kääntäjä, kerääjä) - kääntäjä, joka muuntaa lähdekielellä kirjoitetun ohjelman objektimoduuliksi. Ohjelma, joka kääntää ohjelmatekstin korkean tason kielellä vastaavaksi konekieliseksi ohjelmaksi.

    · Ohjelma, joka on suunniteltu kääntämään korkean tason kielen absoluuttiseksi koodiksi tai joskus kokoonpanokieleksi. Kääntäjälle syötettävä tieto (lähdekoodi) on algoritmin tai ohjelman kuvaus ongelmakeskeisellä kielellä, ja kääntäjän tulos on vastaava kuvaus algoritmista konesuuntautuneella kielellä (oliokoodi).

    Kokoelma-lähdekielellä kirjoitetun ohjelman kääntäminen objektimoduuliksi. Suorittaa kääntäjä.

    Kokoa - lähettää koneen ohjelma ongelmalähtöisestä kielestä konesuuntautuneeksi kieleksi.

    Kääntäjä lukee koko ohjelman täysin, kääntää sen ja luo ohjelmasta täydellisen version konekielellä, joka sitten suoritetaan.

    Tulkki(Englanti) tulkki- tulkki, tulkki) kääntää ja suorittaa ohjelman rivi riviltä. Tulkki ottaa ohjelmatekstistä seuraavan kielen operaattorin, analysoi sen rakenteen ja suorittaa sen sitten välittömästi (yleensä analyysin jälkeen operaattori käännetään joksikin väliesitykseen tai jopa konekoodiksi tehokkaampaa jatkosuoritusta varten). Vasta sen jälkeen, kun nykyinen käsky on suoritettu onnistuneesti, tulkki siirtyy seuraavaan. Lisäksi, jos sama operaattori suoritetaan ohjelmassa useita kertoja, tulkki suorittaa sen ikään kuin se kohdataan ensimmäisen kerran. Tämän seurauksena ohjelmia, jotka vaativat täytäntöönpanoa suuri määrä laskelmat tulevat olemaan hitaita. Lisäksi ohjelman suorittamiseksi toisella tietokoneella on oltava myös tulkki - loppujen lopuksi ilman sitä teksti on vain joukko merkkejä.



    Toisella tavalla voimme sanoa, että tulkki simuloi jonkin verran laskennallista virtuaalikone, mille perusohjeet Prosessorin peruskomennot eivät palvele, vaan ohjelmointikielen operaattorit.

    Erot kokoamisen ja tulkinnan välillä.

    1. Kun ohjelma on käännetty, lähdeohjelmaa tai kääntäjää ei enää tarvita. Samalla tulkin käsittelemän ohjelman täytyy uudelleen siirtää konekielelle aina, kun ohjelma käynnistetään.

    2. Käännetyt ohjelmat toimivat nopeammin, mutta tulkittuja on helpompi korjata ja muuttaa.

    3. Jokainen tietty kieli on suunnattu joko kokoamiseen tai tulkintaan - riippuen tarkoituksesta, jota varten se on luotu. Esimerkiksi, Pascal käytetään yleensä melko monimutkaisten ongelmien ratkaisemiseen, joissa ohjelman nopeus on tärkeä. Siksi tämä kieli toteutetaan yleensä käyttämällä kääntäjä.

    Toisella puolella, PERUS luotiin kieleksi aloitteleville ohjelmoijille, joille ohjelman rivi riviltä suorituksesta on kiistattomia etuja.

    Lähes kaikki matalan tason ja kolmannen sukupolven ohjelmointikielet, kuten assembly, C tai Modula-2, käännetään ja paljon muuta korkean tason kieliä, kuten Python tai SQL, tulkitaan.

    Joskus se on yhdelle kielelle ja kääntäjä, ja tulkki. Tässä tapauksessa voit käyttää tulkkia ohjelman kehittämiseen ja testaamiseen ja kääntää sitten virheenkorjausohjelma sen suoritusnopeuden parantamiseksi. Käännös- ja tulkkausprosessit tunkeutuvat toisiinsa: tulkit voivat kääntää (mukaan lukien dynaaminen käännös), ja kääntäjät voivat vaatia tulkintaa metaohjelmointikonstruktioille (esimerkiksi kokoonpanokielen makroille, ehdollinen käännös C-kielellä tai malleille C++-kielellä).

    4. Lähetys ja tulkinta - erilaisia ​​prosesseja: Kääntäminen käsittelee ohjelmien kääntämistä kielestä toiselle, kun taas tulkkaus vastaa ohjelmien suorittamisesta. Koska kääntämisen tarkoituksena on kuitenkin yleensä valmistella ohjelma tulkkausta varten, näitä prosesseja tarkastellaan yleensä yhdessä.

    Johtopäätös: Kääntäjän haittapuolena on monimutkaisten rakenteiden tietojen käsittelyyn keskittyneiden ohjelmointikielten kääntämisen työlisyys, jotka ovat usein tuntemattomia etukäteen tai muuttuvat dynaamisesti ohjelman ajon aikana. Sitten joudut lisäämään konekoodiin paljon lisätarkistuksia, analysoimaan käyttöjärjestelmän resurssien saatavuutta, sieppaamaan ja vapauttamaan ne dynaamisesti, muodostamaan ja käsittelemään tietokoneen muistissa. monimutkaisia ​​esineitä, joka on melko vaikea toteuttaa kovakoodattujen konekäskyjen tasolla ja lähes mahdoton tehtävän kannalta.

    Tulkin avulla on päinvastoin mahdollista pysäyttää ohjelma milloin tahansa, tutkia muistin sisältöä, järjestää vuoropuhelua käyttäjän kanssa, suorittaa mielivaltaisesti monimutkaisia ​​muunnoksia ja samalla jatkuvasti seurata ohjelman tilaa. ympäröivään ohjelmisto- ja laitteistoympäristöön, jolloin saavutetaan korkea toimintavarmuus. Jokaista käskyä suoritettaessa tulkki tarkistaa monet käyttöjärjestelmän ominaisuudet ja tiedottaa tarvittaessa kehittäjälle mahdollisimman yksityiskohtaisesti esiin tulevista ongelmista. Lisäksi tulkkia on erittäin kätevä käyttää ohjelmoinnin oppimisen työkaluna, koska sen avulla voit ymmärtää minkä tahansa kielen yksittäisen operaattorin toimintaperiaatteet.


    Kokoamisprosessi on jaettu useisiin vaiheisiin:

    1. Esiprosessori. Lähdeohjelma käsitellään korvaamalla olemassa olevat makrot ja otsikkotiedostot.

    2. Leksinen ja syntaktinen analyysi. Ohjelma muunnetaan merkkiketjuksi ja sitten sisäiseksi puuesitykseen.

    3. Globaali optimointi. Ohjelman sisäistä esitystapaa muutetaan toistuvasti ohjelman koon ja suoritusajan pienentämiseksi.

    4. Koodin luominen. Sisäinen esitys muunnetaan prosessorin käskylohkoiksi, jotka muunnetaan kokoonpanotekstiksi tai objektikoodiksi.

    5. Kokoonpano. Jos kokoonpanoteksti luodaan, se kootaan objektikoodin saamiseksi.

    6. Kokoonpano. Assembler yhdistää useita objektitiedostoja suoritettavaksi tiedostoksi tai kirjastoksi.

    Vaiheessa leksikaalinen analyysi (LA) syöttöohjelma, joka on merkkivirta, on jaettu lekseemeihin - sanoihin kielen määritelmien mukaisesti. Pääasiallinen formalismi leksikaalisten analysaattoreiden toteutuksen taustalla ovat äärelliset tilakoneet ja säännölliset lausekkeet. Leksikaalinen analysaattori voi toimia kahdessa päätilassa: joko jäsentimen jokaisen merkin jälkeen kutsumana alirutiinina tai täydellisenä läpimenona, jonka tuloksena muodostuu merkkitiedosto. Lekseemien eristämisprosessissa LA voi itsenäisesti rakentaa nimi- ja vakiotaulukoita ja antaa arvot kullekin lekseemille seuraavan kerran, kun sitä käytetään. Tässä tapauksessa nimitaulukko rakennetaan myöhemmissä vaiheissa (esimerkiksi jäsennysprosessin aikana).

    LA-vaiheessa havaitaan joitain (yksinkertaisia) virheitä (virheelliset merkit, virheellinen numeroiden, tunnisteiden tallennus jne.).

    Katsotaanpa tarkemmin leksikaalisen analyysin vaihetta.

    Leksikaalisen analyysin päätehtävä - tauko sijoita teksti, joka koostuu yksittäisten merkkien sarjasta, sanasarjasta tai lekseemista, ts. valitse nämä sanat jatkuvasta merkkijonosta. Tästä näkökulmasta katsottuna kaikki syöttösekvenssin merkit on jaettu merkkeihin, jotka kuuluvat joihinkin lekseemiin, ja merkkeihin, jotka erottavat lekseemit (erottimet). Joissakin tapauksissa merkkien välillä ei ehkä ole erottimia. Toisaalta joillakin kielillä merkit voivat sisältää merkityksettömiä merkkejä (esimerkiksi välilyöntiä Fortranissa). C:ssä erottimen merkkien erotusarvo voidaan estää ("\" rivin lopussa "..." sisällä).

    Tyypillisesti kaikki lekseemit on jaettu luokkiin. Esimerkkejä tällaisista luokista ovat numerot (kokonaisluku, oktaali, heksadesimaali, reaali jne.), tunnisteet, merkkijonot. Avainsanat ja välimerkit (joita joskus kutsutaan erottimeksi) korostetaan erikseen. Tyypillisesti avainsanat ovat joitain rajallisia tunnisteiden osajoukkoja. Joillakin kielillä (esim. PL/1) lekseemin merkitys voi riippua sen kontekstista, eikä leksikaalista analyysiä voida suorittaa erillään syntaktisesta analyysistä.

    Analyysin myöhempien vaiheiden kannalta leksikaalinen analysaattori tuottaa kahdentyyppistä tietoa: leksikaalisen jälkeen toimivalle syntaktiselle analysaattorille tieto lekseemiluokkien, erottimien ja avainsanojen järjestyksestä on välttämätöntä ja kontekstuaalinen analyysi, työskennellessään syntaktisen jälkeen, tieto yksittäisten lekseemien erityisistä merkityksistä (tunnisteet, numerot jne.) on tärkeää.

    Täten, yleinen kaava Näin leksikaalinen analysaattori toimii. Ensin poimitaan yksi merkki (ehkä käyttämällä erotinmerkkejä). Avainsanat tunnistetaan joko poimimalla ne suoraan tekstistä tai poimimalla ensin tunniste ja tarkistamalla sitten, kuuluuko se avainsanajoukkoon.

    Jos valittu lekseema on erotin, niin se (tarkemmin sanottuna osa sen attribuutista) annetaan leksikaalisen analyysin tuloksena. Jos valittu lekseema on avainsana, niin sitten vastaavan merkki avainsana. Jos valittu tunnus on tunniste, tunniste-attribuutti myönnetään ja itse tunniste tallennetaan erikseen. Lopuksi, jos valittu merkki kuuluu johonkin muuhun merkkiluokkiin (esimerkiksi merkki on numero, merkkijono jne.), palautetaan vastaavan luokan attribuutti ja tunnuksen arvo tallennetaan erikseen. .

    Leksikaalinen analysaattori voi olla joko itsenäinen käännösvaihe tai "anna tunnus" -periaatteella toimiva aliohjelma. Ensimmäisessä tapauksessa (kuva 3.1, a) analysaattorin ulostulo on lekseemitiedosto, toisessa (kuva 3.1, b) lekseemi annetaan aina, kun analysaattoria käytetään (tässä tapauksessa pääsääntöisesti lekseemiluokka-attribuutti palautetaan "leksikaalisen analysaattorin" tuloksena ja tunnuksen arvo välitetään globaalin muuttujan kautta). Token-arvojen käsittelyssä jäsentäjä voi joko yksinkertaisesti tulostaa kunkin tunnuksen arvon, jolloin objektitaulukoiden (tunnisteet, merkkijonot, numerot jne.) rakentaminen siirretään myöhempään vaiheeseen, tai se voi rakentaa objektitaulukot. itse. Tässä tapauksessa tunnuksen arvo on osoitin vastaavaan taulukkoon.

    Riisi. 3.1:

    Leksikaalisen analysaattorin toiminta määritellään jollain äärellistilakoneella. Äärillisen tilan koneen suora kuvaus on kuitenkin käytännön kannalta hankalaa. Siksi leksikaalisen analysaattorin määrittämiseen käytetään yleensä joko säännöllistä lauseketta tai oikea-lineaarista kielioppia. Kaikilla kolmella formalismilla (äärelliset tilat, säännölliset lausekkeet ja oikea-lineaariset kieliopit) on sama ilmaisuvoima. Erityisesti mukaan tavallinen ilme tai oikea-lineaarinen kielioppi, voidaan rakentaa äärellinen automaatti, joka tunnistaa saman kielen.

    Jäsentämisen päätehtävä - ohjelman rakenteen analyysi. Pääsääntöisesti rakenne ymmärretään kielen yhteydettömässä kielioppissa jäsentämistä vastaavana puuna. Tällä hetkellä käytetään useimmiten joko LL(1)-analyysiä (ja sen varianttia - rekursiivinen laskeutuminen) tai LR(1)-analyysiä ja sen muunnelmia (LR(0), SLR(1), LALR(1) ja muut). Rekursiivista laskeutumista käytetään useammin jäsentimen manuaalisessa ohjelmoinnissa, LR(1) - käytettäessä automaatiojärjestelmiä jäsentimien rakentamiseen.

    Jäsennyksen tulos on syntaksipuu, jossa on linkit nimitaulukkoon. Jäsennysprosessi paljastaa myös ohjelman rakenteeseen liittyviä virheitä.

    Kontekstianalyysin vaiheessa riippuvuudet tunnistetaan ohjelman osien välillä, joita ei voida kuvata yhteydettömällä syntaksilla. Nämä ovat pääasiassa "kuvaus-käyttö" -yhteyksiä, erityisesti objektityyppien analysointia, laajuuksien analysointia, parametrien vastaavuutta, etikettejä ja muita. Kontekstianalyysin prosessissa rakennetaan symbolitaulukko, jota voidaan pitää nimitaulukkona, jota täydennetään tiedoilla objektien kuvauksista (ominaisuuksista).

    Pääasiallinen kontekstuaalianalyysissä käytetty formalismi on attribuuttien kieliopit. Kontekstianalyysivaiheen tulos on määritelty ohjelmapuu. Tieto objekteista voidaan joko hajauttaa itse puuhun tai keskittää erillisiin symbolitaulukoihin. Kontekstianalyysin aikana voidaan havaita myös esineiden virheelliseen käyttöön liittyviä virheitä.

    Sitten ohjelma voi olla siirretty sisäiseen edustukseen . Tämä tehdään optimointitarkoituksiin ja/tai koodin luomisen helpottamiseksi. Toinen tarkoitus ohjelman muuntamiseksi sisäiseksi esitykseksi on halu saada kannettava kääntäjä. Tällöin vain viimeinen vaihe (koodin luominen) on koneesta riippuvainen. Sisäisenä esityksenä voidaan käyttää etu- tai jälkiliitteen merkintää, suunnattua kuvaajaa, kolmois-, nelinkertaisia ​​ja muita.

    Optimointivaiheita voi olla useita . Optimoinnit yleensä jaetaan koneista riippuvaisiin ja koneista riippumattomiin, paikallisiin ja globaaleihin. Koodin luontivaiheen aikana suoritetaan koneriippuvaista optimointia. Globaali optimointi yrittää ottaa huomioon koko ohjelman rakenteen, paikalliset - vain sen pienet fragmentit. Globaali optimointi perustuu globaaliin virtausanalyysiin, joka suoritetaan ohjelmakaaviolle ja edustaa olennaisesti tämän kaavion muunnosa. Tässä tapauksessa voidaan ottaa huomioon sellaiset ohjelman ominaisuudet kuin prosessien välinen analyysi, modulaarinen analyysi, muuttujien elinkaaren alueiden analyysi jne.

    Lopuksi, koodin luominen- lähetyksen viimeinen vaihe. Tuloksena on joko kokoonpanomoduuli tai objekti- (tai lataus)moduuli. Koodin luontiprosessin aikana voidaan suorittaa joitain vaiheita. paikalliset optimoinnit, kuten rekisterin allokointi, pitkien tai lyhyiden haarojen valinta, ohjekustannusten huomioiminen tiettyä käskysekvenssiä valittaessa. Koodin luomiseen on kehitetty erilaisia ​​menetelmiä, kuten päätöstaulukoita, kuvioiden sovittamista, mm dynaaminen ohjelmointi, erilaisia ​​syntaktisia menetelmiä.

    Tietyt kääntäjän vaiheet voivat tietysti olla joko kokonaan poissa tai yhdistellä. Yksinkertaisimmassa tapauksessa yksivaiheisessa kääntäjässä ei ole väliesityksen generointivaihetta ja optimointia, loput vaiheet yhdistetään yhdeksi, eikä ole olemassa eksplisiittistä syntaksipuuta.