Soorten vertalers. Alle programmeertalen hebben echter een aantal gemeenschappelijke kenmerken en parameters. Deze gemeenschappelijkheid bepaalt ook de principes van het organiseren van vertalers die voor alle talen gelijk zijn. Testvragen en taken

Vertaler Is een vertaalprogramma. Het converteert een programma dat is geschreven in een van de programmeertalen naar een binair programmabestand dat bestaat uit machine-instructies, of voert direct de acties van het programma uit.

Vertalers worden geïmplementeerd als compilers, interpreters, preprocessors en emulators. Compiler en tolk zijn heel verschillend in termen van hoe ze de klus klaren.

Compiler (Engelse compiler - compiler, verzamelaar)- leest het hele programma als geheel, maakt de vertaling en maakt een volledige versie van het programma in machinetaal, dat wil zeggen een binair bestand met een lijst met machine-instructies. Een binair bestand kan uitvoerbaar zijn, bibliotheek, object), het wordt uitgevoerd door het besturingssysteem zonder tussenkomst van de compiler.

Tolk (Engelse tolk - tolk, vertaler)- vertaalt het programma regel voor regel (één operator per keer) in machinecode (processor, besturingssysteem, andere omgevingsopdrachten), voert de vertaalde operator uit (programmaregel) en gaat dan verder naar de volgende regel programmatekst. De interpreter genereert geen uitvoerbare bestanden, maar voert zelf alle acties uit die zijn vastgelegd in de tekst van het bronprogramma.

Als het programma eenmaal is gecompileerd, is noch het originele programma, noch de compiler meer nodig. Tegelijkertijd moet het programma dat door de tolk wordt verwerkt, elke keer dat het programma wordt gestart, opnieuw worden vertaald in machinetaal.

Gecompileerde programma's werken sneller, maar geïnterpreteerde programma's zijn gemakkelijker te repareren en te wijzigen.

Elke specifieke taal is ofwel op compilatie ofwel op interpretatie gericht, afhankelijk van het doel waarvoor ze is gemaakt. Zo wordt Pascal meestal gebruikt om vrij complexe problemen op te lossen waarbij de snelheid van programma's belangrijk is. Daarom wordt deze taal meestal geïmplementeerd met behulp van een compiler.

Aan de andere kant is BASIC gemaakt als een taal voor beginnende programmeurs, voor wie regel-voor-regel programma-uitvoering onmiskenbare voordelen heeft.

Soms is er zowel een compiler als een tolk voor dezelfde taal. In dit geval kunt u de interpreter gebruiken om het programma te ontwikkelen en te testen, en vervolgens het foutopsporingsprogramma compileren om de uitvoering ervan te versnellen.

preprocessor Is een vertaler van de ene programmeertaal naar de andere zonder een uitvoerbaar bestand te maken of een programma uit te voeren.

Preprocessors zijn handig voor het uitbreiden van de mogelijkheden van de taal en het programmeergemak door, in de fase van het schrijven van een programma, een mensvriendelijker dialect van de programmeertaal te gebruiken en de vertaling ervan door de preprocessor in de tekst van een standaard programmeertaal die kan worden gecompileerd met een standaardcompiler.

emulator- software en/of hardware die werkt in een bepaald doelbesturingssysteem en hardwareplatform, ontworpen om programma's uit te voeren die in een ander besturingssysteem zijn gemaakt of die op andere hardware dan het doel draaien, maar die dezelfde bewerkingen in de doelomgeving mogelijk maken als in het gesimuleerde systeem.

Emulatietalen omvatten systemen zoals Java, .Net, Mono, waarin het, in de fase van het maken van een programma, wordt gecompileerd tot een speciale bytecode en een binair bestand verkrijgt dat geschikt is voor uitvoering in elke besturings- en hardwareomgeving, en uitvoering van de resulterende bytecode geproduceerd op de doelmachine met behulp van een eenvoudige en snelle interpreter (virtuele machine).

Reassembler, demontage- een softwaretool die is ontworpen om een ​​binaire code te decoderen met zijn weergave in de vorm van assembler-tekst of tekst van een andere programmeertaal, waarmee u het algoritme van het bronprogramma kunt analyseren en de resulterende tekst kunt gebruiken voor de noodzakelijke wijziging van het programma, bijvoorbeeld om de adressen van externe apparaten, toegang tot systeem- en netwerkbronnen te wijzigen, verborgen functies van een binaire code te onthullen (bijvoorbeeld een computervirus of ander kwaadaardig programma: een Trojaans paard, een worm, een keylogger, enz.).

tot algoritmische algoritmen, datastructuren en programmering van DBMS Y&MP 3GL 4GL 5GL-technologie prog.

Wist u, wat abstractie door parametrisering Is een programmeertechniek die het mogelijk maakt om met behulp van parameters een vrijwel onbeperkte set van verschillende berekeningen weer te geven met één programma, wat een abstractie is van deze sets.

Vertalers

Aangezien de tekst van het programma dat in Pascal is geschreven niet begrijpelijk is voor de computer, moet deze in machinetaal worden vertaald. Zo'n vertaling van een programma van een programmeertaal naar een machinecodetaal heet uitzending (vertaling - vertaling), maar het wordt uitgevoerd door speciale programma's - vertalers.

Er zijn drie soorten vertalers: tolken, compilers en assemblers.

Tolk wordt een vertaler genoemd die operator-per-operator (commando-per-commando) verwerking en uitvoering van het originele programma uitvoert.

Compiler zet (vertaalt) het gehele programma om naar een module in machinetaal, waarna het programma in het geheugen van de computer wordt geschreven en pas daarna wordt uitgevoerd.

Monteurs vertalen van een programma geschreven in assembler (autocode) naar een programma in machinetaal.

Elke vertaler lost de volgende hoofdtaken op:

Analyseert met name het programma dat wordt vertaald, bepaalt of het syntaxisfouten bevat;

Genereert een uitvoerprogramma (het wordt vaak een object of werkprogramma genoemd) in de computertaal (in sommige gevallen genereert de vertaler een uitvoerprogramma in een tussentaal, bijvoorbeeld in assembler);

Wijst geheugen toe voor het uitvoerprogramma (in het eenvoudigste geval bestaat het uit het toewijzen van elk programmafragment, variabelen, constanten, arrays en andere objecten aan hun eigen geheugenadressen).

Inleiding tot .Net en c Sharp

Een programmeur schrijft een programma in een taal die voor een programmeur begrijpelijk is, en een computer voert alleen programma's uit die in een machinecodetaal zijn geschreven. De set tools voor het schrijven, bewerken en converteren van een programma naar machinecodes en de uitvoering ervan wordt een ontwikkelomgeving genoemd.

De ontwikkelomgeving bevat:

    Teksteditor voor het invoeren en corrigeren van programmatekst

    Een compiler voor het vertalen van een programma in de taal van machine-instructies

    Tools voor het debuggen en starten van een programma voor uitvoering

    Gedeelde bibliotheken met herbruikbare programma-elementen

    Help-systeem, enz.

Het .NET-platform ("dot no"), ontwikkeld door Microsoft, omvat niet alleen een ontwikkelomgeving voor verschillende programmeertalen genaamd Visual Studio .NET, maar ook vele andere tools, zoals database-ondersteuning, e-mail en andere.

De belangrijkste taken bij de ontwikkeling van moderne software zijn:

    Draagbaarheid - de mogelijkheid om op verschillende soorten computers te draaien

    Beveiliging - onmogelijkheid van ongeoorloofde acties

    Betrouwbaarheid - storingsvrije werking onder gespecificeerde omstandigheden

    Kant-en-klare componenten gebruiken om de ontwikkeling te versnellen

    Intertaalcommunicatie is het gebruik van verschillende programmeertalen.

Al deze taken worden opgelost in het kader van het .NET-platform.

Om de draagbaarheid te garanderen, vertalen platformcompilers het programma niet in machinecode, maar in de tussentaal MSIL (Microsoft Intermediate Language) of gewoon in IL. IL bevat geen besturingssysteem- of computerspecifieke opdrachten. Het IL-programma wordt uitgevoerd door de Common Language Runtime (CLR), die al specifiek is voor elk type computer. Het vertalen van een IL-programma naar machinecodes van een specifieke computer wordt uitgevoerd door de JIT (Just In Time) -compiler.

Het programma-uitvoeringsdiagram op het .NET-platform wordt getoond in Fig. 1.

De compiler maakt een assembly van het programma - een bestand met de extensie . exe of . dll, die IL-code bevat. De uitvoering van het programma wordt georganiseerd door de CRL, die de geldigheid van bewerkingen bewaakt, geheugentoewijzing en opruiming uitvoert en runtime-fouten afhandelt. Dit zorgt voor de veiligheid en betrouwbaarheid van de programma's.

De prijs die voor deze voordelen moet worden betaald, is de verminderde prestatie van programma's en de noodzaak om .NET op de computer te installeren om kant-en-klare programma's uit te voeren.

.NET is dus een programmeerplatform.

C # (Zee scherp) Is een van de programmeertalen voor het .NET-platform. Het is opgenomen in Visual Studio - Visual Studio .NET (versies 2008, 2010, 2012). Naast C # bevat Visual Studio .NET Visual Basic .NET en Visual C ++.

Een van de redenen voor de ontwikkeling van een nieuwe taal door Microsoft is het creëren van een componentgeoriënteerde taal voor het platform .NET Framework.

Fig. 1 Programma-uitvoeringsschema in .NET

NET Framework bestaat uit twee delen:

    Ten eerste bevat het een enorme bibliotheek met klassen die vanuit C#-programma's kunnen worden aangeroepen. Er zijn veel klassen (ongeveer enkele duizenden). Dit elimineert de noodzaak om alles zelf te schrijven. Daarom draait het bij programmeren in C# om het schrijven van je eigen code die de klassen aanroept die zijn opgeslagen in het .NET Framework als dat nodig is.

    Ten tweede bevat het de .NET Runtime, die de lancering en werking van voltooide programma's regelt.

.NET is een open omgeving - externe ontwikkelaars hebben tientallen compilers voor .NET gemaakt voor Ada, COBOL, Fortran, Lisp, Oberon, Perl, Python en meer.

Het .NET-platform ontwikkelt zich actief - alle nieuwe versies van dit platform worden uitgebracht. Het menu gebruiken Project Eigenschappen ontdek de versie van het .NET-framework dat u gebruikt.

In theorie kan een .NET-programma draaien op elk besturingssysteem waarop .NET is geïnstalleerd. Maar in de praktijk is het enige officiële platform hiervoor het Windows-besturingssysteem. Er zijn echter niet-officiële .NET-implementaties voor Unix-achtige Linux, Mac OS X en andere (Mono is een gratis softwareproject van het .NET Framework).

Rapier woord

Het woord rapier in Engelse letters (transliteratie) - rapira

Het woord rapier bestaat uit 6 letters: a a en p p p

De betekenis van het woord rapier. Wat is een rapier?

Rapier (Duitse Rapier, van Franse rapière, oorspronkelijk Spaanse espadas roperas - letterlijk, "zwaard voor kleding" (dat wil zeggen niet voor harnas), vervormd in het Frans la rapiere) is voornamelijk een stotend scherp wapen, een soort zwaard ...

ru.wikipedia.org

De rapier (Duitse Rapier, van Franse rapière), een sportief stootwapen, bestaat uit een elastisch stalen lemmet en een gevest (beschermende komvormige bewaker en handgreep).

TSB. - 1969-1978

RAPIER (Duitse Rapier, van Franse rapiere). Sport steekwapen. Bestaat uit een flexibel stalen mes en gevest (beschermende komvormige beschermkap en handvat). Een rechthoekig mes dat taps toeloopt naar de bovenkant ...

Olympische Encyclopedie. - 2006

RAPIRA, Advanced Adapted Poplan-Interpreter, Editor, Archive, is een educatieve en productieprogrammeertaal. Ontwikkeld in de vroege jaren 80 in de USSR. De rapier is een middel...

Encyclopedie van programmeertalen

Rapier (SAM)

De Rapier is een grond-luchtraketsysteem ontwikkeld door de Britse strijdkrachten voor de Royal Air Force. Het is in dienst bij de legers van Australië, Groot-Brittannië, Indonesië, Singapore, Turkije, Maleisië en Zwitserland.

ru.wikipedia.org

bestrijding rapier

Combat rapier - (uit het Frans. Rapiere) - steken, snijden, snijden, steken-hakken met lange bladen X.0. met handvat, in Europa bekend vanaf de 2e helft van de 17e eeuw. Bestaat uit een recht plat of gefacetteerd stalen mes met een geslepen (voor duelleren R.) ...

Sportfolie

RAPIER SPORTS - een sportief slagwapen bestaande uit een flexibel rechthoekig mes in dwarsdoorsnede en een afneembaar handvat met een ronde komvormige bescherming.

wapen.slovaronline.com

De sportfolie is een sportief scherp wapen dat bestaat uit een flexibel rechthoekig lemmet in dwarsdoorsnede en een afneembaar handvat met een ronde komvormige beschermkap.

Petrov A. Woordenboek van scherpe wapens en bepantsering

Rapier (programmeertaal)

RAPIRA - Advanced Adapted Poplan-Interpreter, Editor, Archive - een procedurele programmeertaal. Ontwikkeld in de vroege jaren 80 in de USSR als een middel om over te stappen van eenvoudigere talen (met name de educatieve taal Robik) ...

ru.wikipedia.org

Schermen op de Olympische Zomerspelen 1896 - Foil

De folieschermwedstrijd voor heren op de Olympische Zomerspelen van 1896 werd gehouden op 7 april.

Vertaler, compiler, tolk

Acht atleten uit twee landen namen deel. Eerst streden ze in twee groepen van vier atleten ...

ru.wikipedia.org

Schermen op de Olympische Zomerspelen 1900 - Foil

De folieschermwedstrijd voor heren op de Olympische Zomerspelen van 1900 werd gehouden van 14-19 en 21 mei. 54 atleten uit tien landen namen deel.

ru.wikipedia.org

Russische taal

Rapier.

Morfemisch en spellingswoordenboek. - 2002

Schermen op de Olympische Zomerspelen 1900 - folie onder maestro

De folieschermwedstrijd tussen de mannelijke maestro's op de Olympische Zomerspelen van 1900 werd gehouden van 22 tot 25 en van 27 tot 28 mei.

59 atleten uit zeven landen namen deel.

ru.wikipedia.org

Voorbeelden van het gebruik van het woord rapier

De rapier is zo gemaakt dat hij er niet in kan, het maximum dat kan blijven is een blauwe plek.

Volgens de markeringen op de handgreep van de rapier gaan de agenten naar de schermclub en ontdekken dat de rapier daar een jaar geleden is gestolen.

Lezing: Normen en softwarelicenties

UNIX familie standaarden. C. System V Interface Definition (SVID) programmeertaalstandaarden. POSIX-commissies. X / Open, OSF en Open Groep. Softwarelicenties en documentatie.
Inhoud

  • 3.1. UNIX-familiestandaarden
    • C programmeertaalstandaarden
    • Systeem V-interfacedefinitie (SVID)
    • POSIX-commissies
    • X / Open, OSF en Open Groep
  • 3.2. Softwarelicenties en documentatie

3.1. UNIX-familiestandaarden

De reden voor de opkomst van standaarden voor het UNIX-besturingssysteem was dat het werd geport naar veel hardwareplatforms. De eerste versies draaiden op PDP-hardware, maar in 1976 en 1978 werd het systeem overgezet naar Interdata en VAX. Van 1977 tot 1981 kregen twee concurrerende branches vorm: UNIX AT&T en BSD. Waarschijnlijk waren de doelen van het ontwikkelen van de normen anders. Een daarvan is om het primaat van de versie te legitimeren, en de andere is om de overdraagbaarheid van het systeem en de applicatieprogramma's tussen verschillende hardwareplatforms te waarborgen. In dit verband praten ze over de mobiliteit van programma's. Dergelijke eigenschappen zijn relevant voor zowel de broncode van programma's als uitvoerbare programma's.

Verder materiaal wordt gegeven in chronologische volgorde van het uiterlijk van de normen.

C programmeertaalstandaarden

Deze standaard is niet direct van toepassing op UNIX. Maar aangezien C de basis is voor zowel deze familie als andere besturingssystemen, zullen we de standaard van deze programmeertaal noemen. Het begon met de uitgave in 1978 van de eerste editie van het boek door B. Kernighan en D. Ritchie. Deze standaard wordt vaak K&R genoemd. De programmeurs die dit werk schreven, werkten samen met Ken Thompson aan UNIX. Tegelijkertijd stelde de eerste de naam van het systeem voor en de tweede bedacht deze programmeertaal. Overeenkomstige tekst is beschikbaar op internet [ 45 ].

De industriestandaard voor de programmeertaal C werd echter in 1989 uitgebracht door ANSI en kreeg de naam X3. 159 - 1989. Dit is wat er over deze standaard is geschreven [ 46 ]:

"De standaard is aangenomen om de overdraagbaarheid van programma's die in C zijn geschreven tussen verschillende soorten besturingssystemen te verbeteren. Dus, naast de syntaxis en semantiek van de C-taal, bevat de standaard aanbevelingen voor de inhoud van de standaardbibliotheek. De vooraf gedefinieerde symbolische naam _STDC geeft ondersteuning aan voor de ANSI C-standaard."

In 1988 verscheen op basis van deze programmeertaalstandaard de tweede editie van het boek van Kernighan en Ritchie over C. Merk op dat bedrijven die softwareproducten produceren voor het ontwikkelen van programma's in de taal C hun eigen bibliotheeksamenstelling kunnen vormen en zelfs de samenstelling van andere taalfaciliteiten enigszins uitbreiden.

^ Systeem V-interfacedefinitie (SVID)

Een andere richting in de ontwikkeling van UNIX-standaarden houdt verband met het feit dat niet alleen enthousiastelingen nadachten over het maken van "standaarden". De belangrijkste ontwikkelaars van het systeem, met de komst van vele "varianten", besloten hun eigen documenten te publiceren. Dit is hoe de standaarden worden geproduceerd door de USG, de organisatie die documentatie voor AT&T UNIX-versies heeft geproduceerd sinds de dochteronderneming werd opgericht om het besturingssysteem te maken. Het eerste document verscheen in 1984 op basis van SVR2. Het heette SVID (System V Interface Definition). De vierdelige beschrijving werd vrijgegeven na de release van SVR4. Deze standaarden werden aangevuld met de SVVS-testsuite (System V Verification Suite). Het belangrijkste doel van deze tools was om ontwikkelaars in staat te stellen te beoordelen of hun systeem de naam System V kon claimen [ 14 ].

Merk op dat de stand van zaken met de SVID-standaard enigszins vergelijkbaar is met de standaard van de programmeertaal C. Het boek dat is uitgegeven door de auteurs van deze programmeertaal is een van de standaarden, maar niet de enige. De later uitgebrachte C-standaard is het resultaat van collectief werk, heeft de discussie bij het grote publiek gepasseerd en kan blijkbaar een leidende rol in de lijst van normen claimen. Evenzo is SVVS een reeks tests waarmee u kunt beoordelen of een systeem de naam System V waard is, slechts een van de UNIX-versies. Hierbij wordt geen rekening gehouden met de hele ervaring van het ontwikkelen van besturingssystemen van verschillende fabrikanten.

POSIX-commissies

Het werk om UNIX-standaarden vorm te geven begon in 1980 door een groep enthousiastelingen. Het doel was geformuleerd om de diensten die besturingssystemen aan applicaties leveren formeel te definiëren. Deze programmeerinterfacestandaard werd de basis van het POSIX-document (Portable Operating System Interface for Computing Environment) [ 14 ]. De eerste POSIX-werkgroep werd in 1985 gevormd uit de UNIX-georiënteerde normalisatiecommissie / usr / group, ook wel UniForum [ 47 ]. De naam POSIX werd voorgesteld door de oprichter van GNU, Richard Stallman.

Eerdere versies van POSIX definieerden veel van de systeemservices die nodig zijn voor de werking van applicatieprogramma's, die worden beschreven in de interface die is gespecificeerd voor de C-taal (systeemoproepinterface). Zijn ideeën werden gebruikt door het American National Standards Institute (ANSI) om de eerder genoemde C-taalstandaard te creëren. De oorspronkelijke set functies die in de eerste versies waren vastgelegd, was gebaseerd op UNIX AT&T (versie SVR4 [ 48 ]). Maar in de toekomst is er een scheiding van de POSIX-standaardspecificaties van dit specifieke besturingssysteem. Een benadering voor het organiseren van een systeem op basis van veel basissysteemfuncties is niet alleen toegepast in UNIX (bijvoorbeeld WinAPI van Microsoft).

In 1988 werd de 1003.1 - 1988-standaard gepubliceerd, die een API (Application Programming Interface) definieerde. Twee jaar later werd een nieuwe versie van de norm IEEE 1003.1 - 1990 aangenomen, die algemene regels definieerde voor de programma-interface, zowel voor systeemaanroepen als voor bibliotheekfuncties. Verder zijn toevoegingen eraan goedgekeurd, die services definiëren voor real-time systemen, POSIX "threads", enz. De belangrijke standaard is POSIX 1003.2 - 1992 - de definitie van de shell en hulpprogramma's.

Er is een vertaling [ 1 ] van deze twee groepen documenten, die dergelijke namen hebben gekregen: POSIX.1 (toepassingsprogramma-interface) en POSIX.2 (opdrachtinterpreter en hulpprogramma's - gebruikersinterface). Deze vertaling bevat drie hoofdstukken: Basisconcepten, Systeemservices en Hulpprogramma's. Het hoofdstuk "Systeemservices" is verdeeld in verschillende delen, die elk vergelijkbare services per functie groeperen. In een van de Basic I / O-secties beschrijft deel 7, over directorybewerkingen, bijvoorbeeld drie functies (opendir, readdir en closedir). Ze zijn gedefinieerd in vier clausules: syntaxis, beschrijving, retourwaarde en fouten.

Voor degenen die bekend zijn met de algoritmische programmeertaal C, geven we een voorbeeld van beschrijvingsfragmenten.

Programmeertalen, vertalers, compilers en tolken

In feite geeft zo'n beschrijving een idee van hoe de "System Call Interface" wordt gespecificeerd. De sectie "Syntaxis" over de readdir-functie bevat de volgende regels:

#erbij betrekken

#erbij betrekken

struct dirent * readdir (DIR * dirp);

De tweede alinea ("Beschrijving") bevat de volgende tekst:

"De typen en gegevensstructuren die worden gebruikt in directorydefinities worden gedefinieerd in het dirent.h-bestand. De interne structuur van mappen is door de implementatie gedefinieerd. Wanneer gelezen met de readdir-functie, wordt een object van het type struct dirent gegenereerd, dat de tekenreeks d_name bevat als een veld met het beëindigde teken NUL lokale bestandsnaam.

Readdir leest het huidige directory-item en zet de positiewijzer naar het volgende element. De open directory wordt gespecificeerd door de dirp-aanwijzer. Een element met lege namen wordt overgeslagen."

En hier is wat wordt gegeven in de clausule "Geretourneerde waarde":

"Readdir retourneert bij succes een aanwijzer naar een object van het type struct dirent dat de gelezen directory-ingang bevat. De readdir kan naar het statisch geheugen worden geschreven en worden overschreven door een andere dergelijke aanroep die wordt toegepast op dezelfde open directory. Readdir aanroepen voor verschillende open directory's overschrijven de gelezen informatie niet. Bij een fout of het einde van het bestand wordt een null-pointer geretourneerd. "

In de clausule "Fouten van de norm" staat het volgende:

"Readdir en closedir vinden een fout. Dirp is geen verwijzing naar een open directory."

Dit voorbeeld laat zien hoe de services die door de applicatie worden geleverd, worden beschreven. De vereisten voor het besturingssysteem (implementatie) zijn dat het "... alle verplichte hulpprogramma's, functies, header-bestanden moet ondersteunen, met het gedrag gespecificeerd in de standaard. De constante _POSIX_VERSION heeft de waarde 200112L [ 49 ]".

In de wereld van computertechnologie is er zo'n uitdrukking: "POSIX-programmering". U kunt dit leren door gebruik te maken van verschillende handleidingen voor het programmeren en besturingssystemen van UNIX-systemen (bijvoorbeeld [ 5 ]). Er is een apart boek met deze titel [ 3 ]. Merk op dat het voorwoord van dit boek zegt dat het beschrijft "... De drievoudige standaard." / IEC 9945.

Hoe kunt u controleren of een bepaald systeem voldoet aan de POSIX-standaard? Het formaliseren van een dergelijke vraag is niet zo eenvoudig als het op het eerste gezicht lijkt. In moderne versies worden 4 soorten correspondentie aangeboden (vier semantische betekenissen van het woord "correspondentie": volledig, internationaal, nationaal, uitgebreid).

In de documenten in kwestie worden lijsten van twee soorten interfacemiddelen gegeven: verplicht (indien mogelijk, wordt verondersteld compact te zijn) en optioneel. Dit laatste moet ofwel op de voorgeschreven manier worden verwerkt, ofwel een vaste waarde van de ENOSYS-code teruggeven, wat betekent dat de functie niet geïmplementeerd is.

Merk op dat de POSIX-documenten al vele jaren veranderen. Maar de ontwikkelaars van nieuwe versies proberen altijd de continuïteit met eerdere versies zoveel mogelijk te behouden, in recentere versies kan er iets nieuws verschijnen. Het document uit 2004 voegde bijvoorbeeld vier delen samen [ 50 ]:

  • Basisdefinities volume (XBD) - definitie van termen, concepten en interfaces die gemeenschappelijk zijn voor alle volumes van deze standaard;
  • System Interfaces volume (XSH) - interfaces op systeemniveau en hun binding aan de C-taal, die de verplichte interfaces tussen applicatieprogramma's en het besturingssysteem beschrijft, in het bijzonder - de specificaties van systeemaanroepen;
  • Shell and Utilities volume (XCU) - definiëren van standaard shell-interfaces (de zogenaamde POSIX-shell), evenals de basisfunctionaliteit van Unix-hulpprogramma's;
  • Rationale (informatieve) volume (XRAT) - aanvullende, inclusief historische, informatie over de standaard.

Net als de eerste edities beschrijft het document in het grootste deel de groepen van geleverde diensten. Elk element daar wordt beschreven in de volgende clausules: NAAM, SINOPSIS (Syntaxis), DISCRIPTION (Beschrijving), RETOURWAARDE (Retourwaarde), ERRORS (Fouten) en in de conclusie VOORBEELD (Voorbeelden).

Moderne versies van de standaard stellen eisen aan zowel het besturingssysteem als de applicatieprogramma's. Laten we een voorbeeld geven [ 51 ].

De functie readdir () moet een verwijzing naar een structuur teruggeven voor het volgende directory-item. Of catalogusitems met de naam point en point-to-point al dan niet worden geretourneerd, wordt niet gespecificeerd door de standaard. In dit voorbeeld zijn er vier mogelijke uitkomsten, en de vereiste voor de toepassing is dat deze voor elk van deze moet worden ontworpen.

En tot slot presenteren we een fragment uit de cursus van lezingen door Sukhomlinov ("INTRODUCTION TO ANALYSIS OF INFORMATION TECHNOLOGIES", Sukhomlinov VA Part V. Methodology and system of Standards POSIX OSE), gewijd aan de reikwijdte van normen [ 52 ]:

"De reikwijdte van de POSIX OSE (Open System Environment)-standaarden is het bieden van de volgende mogelijkheden (ook wel openheidseigenschappen genoemd) voor de ontwikkelde informatiesystemen:

  • Applicatieportabiliteit op broncodeniveau, d.w.z. de mogelijkheid bieden om programma's en gegevens die in de broncode van programmeertalen worden gepresenteerd, van het ene platform naar het andere over te dragen.
  • Systeeminteroperabiliteit, d.w.z. interconnectiviteit tussen systemen te behouden.
  • Gebruikersportabiliteit, d.w.z. gebruikers de mogelijkheid bieden om op verschillende platforms te werken zonder omscholing.
  • Aanpassingsvermogen aan nieuwe standaarden (Accommodatie van Standaarden) gerelateerd aan het behalen van de doelen van open systemen.
  • Aanpassingsvermogen aan nieuwe informatietechnologieën (accommodatie van nieuwe systeemtechnologie) op basis van de universaliteit van de classificatiestructuur van diensten en de onafhankelijkheid van het model van de implementatiemechanismen.
  • Schaalbaarheid van applicatieplatforms (Application Platform Scalability), wat de mogelijkheid weerspiegelt om applicatiesoftware over te dragen en opnieuw te gebruiken voor verschillende soorten en configuraties van applicatieplatforms.
  • Schaalbaarheid van gedistribueerde systemen (Distributed System Scalability), die de mogelijkheid weerspiegelt dat de applicatiesoftware functioneert ongeacht de ontwikkeling van de topologie en bronnen van gedistribueerde systemen.
  • Implementatie Transparantie, d.w.z. verbergen voor gebruikers achter de interfaces van de systemen van de eigenaardigheden van hun implementatie.
  • De consistentie en nauwkeurigheid van de specificaties van de functionele eisen van de gebruiker, die de volledigheid en duidelijkheid van de definitie van gebruikersbehoeften garandeert, inclusief de definitie van de samenstelling van de toegepaste normen."

Hiermee kunt u de volgende taken oplossen:

  • integratie van informatiesystemen van componenten van verschillende fabrikanten;
  • efficiëntie van implementatie en ontwikkeling, vanwege de nauwkeurigheid van specificaties en naleving van standaardoplossingen, als gevolg van het geavanceerde wetenschappelijke en technische niveau;
  • efficiëntie van het overdragen van applicatiesoftware dankzij het gebruik van gestandaardiseerde interfaces en transparantie van mechanismen voor de implementatie van systeemdiensten.

Ook definiëren de standaarden formeel zulke belangrijke concepten van besturingssystemen: gebruiker; het dossier; werkwijze; terminal; gastheer; netwerkknooppunt; tijd; taalkundige en culturele omgeving. Het geeft niet de formulering van een dergelijke definitie, maar introduceert de bewerkingen die erop worden toegepast en hun inherente kenmerken.

Er zijn meer dan drie dozijn elementen in de lijst met POSIX-standaarden. Hun namen beginnen traditioneel met de letter "P", gevolgd door een viercijferig nummer met extra tekens.

Er zijn ook algemene namen voor POSIX1, POSIX2, enz. POSIX1 is bijvoorbeeld geassocieerd met standaarden voor basis OS-interfaces (P1003.1x, waarbij in plaats van x het leeg is of tekens van a tot g; er zijn dus 7 documenten in deze groep), en POSIX3 is gerelateerd aan testmethoden (twee documenten - P2003 en P2003n).

Elke computer heeft zijn eigen programmeertaal - instructietaal of machinetaal - en kan programma's uitvoeren die alleen in die taal zijn geschreven. In principe kan elk algoritme worden beschreven met behulp van een machinetaal, maar de programmeerkosten zullen extreem hoog zijn. Dit komt door het feit dat u met machinetaal alleen primitieve gegevensstructuren kunt beschrijven en verwerken - bit, byte, woord. Programmeren in machinecodes vereist overmatige detaillering van het programma en is alleen beschikbaar voor programmeurs die goed op de hoogte zijn van de structuur en werking van computers. Talen op hoog niveau (Fortran, PL / 1, Pascal, C, Ada, enz.) Met ontwikkelde datastructuren en verwerkingsmethoden die niet afhankelijk zijn van de taal van een bepaalde computer, konden deze moeilijkheid overwinnen.

Algoritmische talen op hoog niveau stellen de programmeur in staat om algoritmen te beschrijven voor het oplossen van veel toegepaste problemen op een vrij eenvoudige en handige manier. Deze beschrijving heet het originele programma en de taal op hoog niveau is invoertaal.

Taalprocessor verwijst naar een machinetaalprogramma waarmee een computer programma's in een invoertaal kan begrijpen en uitvoeren. Er zijn twee hoofdtypen taalverwerkers: tolken en vertalers.

Tolk Is een programma dat een programma in de invoertaal als invoer accepteert en, naarmate de constructies van de invoertaal worden herkend, deze implementeert en de resultaten van berekeningen uitvoert die door het oorspronkelijke programma zijn voorgeschreven.

Vertaler Is een programma dat het originele programma bij de invoer accepteert en bij de uitvoer een programma genereert dat functioneel equivalent is aan het origineel, genaamd object... Een objectprogramma is geschreven in een objecttaal. In een bepaald geval kan een machinetaal als objecttaal dienen en in dit geval kan het aan de output van de vertaler verkregen programma onmiddellijk op een computer worden uitgevoerd (geïnterpreteerd). In dit geval is de computer een vertolker van het objectprogramma in machinecodes. Over het algemeen hoeft een objecttaal geen machinetaal te zijn of iets dat er dichtbij komt (autocode). Als een objecttaal kunnen sommige tussentaal- de taal die tussen de invoer- en machinetalen ligt.

Als een tussentaal als objecttaal wordt gebruikt, zijn er twee mogelijkheden om een ​​vertaler te construeren.

De eerste optie - voor de tussentaal is (of wordt ontwikkeld) een andere vertaler van de tussentaal naar de machinetaal, en deze wordt gebruikt als het laatste blok van de ontworpen vertaler.

De tweede optie voor het bouwen van een vertaler met een tussentaal is om een ​​intermediaire taalcommando-interpreter te bouwen en deze als het laatste blok van de vertaler te gebruiken. Het voordeel van tolken komt tot uiting in debug- en dialoogvertalers, die ervoor zorgen dat de gebruiker in een dialoogmodus werkt, tot het aanbrengen van wijzigingen in het programma zonder het volledig opnieuw te vertalen.

Tolken worden ook gebruikt bij programma-emulatie - het uitvoeren op een technologische machine van programma's die voor een andere (object)machine zijn gecompileerd. Deze optie wordt met name gebruikt bij het debuggen van programma's op een universele computer die op een gespecialiseerde computer worden uitgevoerd.

Een vertaler die een machine-achtige taal (autocode of assembler) als invoertaal gebruikt, wordt traditioneel genoemd assembler... De vertaler voor de taal op hoog niveau heet compiler.

De afgelopen jaren is er aanzienlijke vooruitgang geboekt in de bouw van compilers. De eerste compilers gebruikten de zogenaamde methoden voor live-uitzendingen- dit zijn voornamelijk heuristische methoden, waarbij op basis van een algemeen idee voor elke taalconstructie een eigen algoritme werd ontwikkeld om te vertalen naar een machine-equivalent. Deze methoden waren traag en niet-structureel.

De ontwerpmethodologie van moderne compilers is gebaseerd op: compositie-syntaxisgestuurde methode verwerkingstalen... Compositioneel in de zin dat het proces van het vertalen van het bronprogramma naar een objectprogramma wordt geïmplementeerd door een samenstelling van functioneel onafhankelijke afbeeldingen met expliciet onderscheiden input- en outputdatastructuren. Deze mappings zijn opgebouwd uit het beschouwen van het bronprogramma als een samenstelling van de belangrijkste aspecten (niveaus) van de beschrijving van de invoertaal: woordenschat, syntaxis, semantiek en pragmatiek, en het identificeren van deze aspecten van het bronprogramma tijdens het samenstellen ervan. Laten we deze aspecten eens bekijken om een ​​vereenvoudigd compilermodel te krijgen.

De basis van elke natuurlijke of kunstmatige taal is: alfabet- een reeks elementaire tekens (letters, cijfers en diensttekens) die in de taal zijn toegestaan. Tekens kunnen worden gecombineerd in: de woorden- elementaire taalconstructies, in de tekst (programma) beschouwd als ondeelbare symbolen met een bepaalde betekenis.


Een enkel teken kan ook een woord zijn. In de Pascal-taal zijn woorden bijvoorbeeld identifiers, trefwoorden, constanten en scheidingstekens, met name tekens van rekenkundige en logische bewerkingen, haakjes, komma's en andere symbolen. De woordenschat van de taal, samen met een beschrijving van de manieren waarop ze worden gepresenteerd, vormen samen vocabulaire taal.

Woorden in een taal worden gecombineerd tot complexere structuren - zinnen. In programmeertalen is de eenvoudigste zin de operator. Zinnen zijn opgebouwd uit woorden en eenvoudiger zinnen volgens de syntaxisregels. Syntaxis taal is een beschrijving van correcte zinnen. Beschrijving van de betekenis van zinnen, d.w.z. betekenissen van woorden en hun interne verbindingen, is semantiek taal. Bovendien merken we op dat een specifiek programma enig effect heeft op de vertaler - pragmatisme... Alles bij elkaar genomen, de syntaxis, semantiek en pragmatisme van de taalvorm semiotiek taal.

Het vertalen van een programma van de ene taal naar de andere bestaat in het algemeen uit het veranderen van het alfabet, de woordenschat en de syntaxis van de programmataal met behoud van de semantiek. Het proces van het vertalen van een bronprogramma naar een objectprogramma is meestal verdeeld in verschillende onafhankelijke subprocessen (vertaalfasen), die worden geïmplementeerd door de overeenkomstige blokken van de vertaler. Het is handig om de belangrijkste fasen van vertaling, lexicale analyse, parsing, semantische analyse en

synthese van een objectprogramma. In veel echte compilers zijn deze fasen echter opgesplitst in verschillende subfasen en er kunnen ook andere fasen zijn (bijvoorbeeld optimalisatie van objectcode). In afb. 1.1 toont een vereenvoudigd functioneel model van de vertaler.

Volgens dit model wordt het invoerprogramma allereerst onderworpen aan lexicale verwerking. Het doel van lexicale analyse is om het bronprogramma te vertalen naar de interne taal van de compiler, waarin trefwoorden, identifiers, labels en constanten worden teruggebracht tot hetzelfde formaat en worden vervangen door voorwaardelijke codes: numeriek of symbolisch, die descriptors worden genoemd. Elke descriptor bestaat uit twee delen: een klasse (type) van een token en een pointer naar een adres in het geheugen waar informatie over een bepaalde token is opgeslagen. Deze informatie is meestal georganiseerd in tabellen. Gelijktijdig met de vertaling van het originele programma in de interne taal in het stadium van lexicale analyse, lexicale controle- detectie van ongeldige woorden in het programma.

De parser neemt de uitvoer van de lexicale analysator en vertaalt de reeks tokenafbeeldingen naar middleware. Een middleware is in wezen een syntaxisboomweergave van een programma. Dit laatste weerspiegelt de structuur van het oorspronkelijke programma, d.w.z. orde en communicatie tussen de operators. Tijdens de constructie van de syntaxisboom, syntactische controle- identificatie van syntaxisfouten in het programma.

De feitelijke uitvoer van het parseren kan de reeks opdrachten zijn die nodig zijn om de middleware te bouwen, toegang te krijgen tot de opzoektabellen en indien nodig een diagnostisch bericht af te geven.

Rijst. 1.1. Vereenvoudigd functioneel vertalersmodel

De synthese van een objectprogramma begint in de regel met de toewijzing en toewijzing van geheugen voor de hoofdprogramma-objecten. Vervolgens wordt elke zin van het bronprogramma onderzocht en worden semantisch equivalente zinnen van de objecttaal gegenereerd. De syntaxisstructuur van het programma en de uitvoertabellen van de lexicale analysator - de tabel met identifiers, de tabel met constanten en andere - worden gebruikt als invoerinformatie. Analyse van de boom stelt u in staat om de volgorde van gegenereerde opdrachten van het objectprogramma te identificeren, en de tabel met identifiers bepaalt de soorten opdrachten die zijn toegestaan ​​voor de waarden van de operanden in de gegenereerde opdrachten (bijvoorbeeld welke instructies nodig zijn te genereren: met vaste of drijvende komma, enz.).

Het genereren van het objectprogramma zelf wordt vaak voorafgegaan door: semantische analyse die verschillende soorten semantische verwerking omvat. Een van de typen is het controleren van de semantische conventies in het programma. Voorbeelden van dergelijke conventies: de unieke beschrijving van elke identifier in het programma, de definitie van een variabele wordt gemaakt voordat deze wordt gebruikt, enz. Semantische analyse kan worden uitgevoerd in latere stadia van de vertaling, bijvoorbeeld in de fase van programma-optimalisatie, die ook in de vertaler kan worden opgenomen. Het doel van optimalisatie is om de tijd of geheugenbronnen te verminderen die nodig zijn om een ​​objectprogramma uit te voeren.

Dit zijn de belangrijkste aspecten van het vertaalproces vanuit talen op hoog niveau. De organisatie van de verschillende fasen van vertaling en de bijbehorende praktische manieren om ze wiskundig te beschrijven, worden hieronder in meer detail besproken.

Stuur uw goede werk in de kennisbank is eenvoudig. Gebruik het onderstaande formulier

Studenten, afstudeerders, jonge wetenschappers die de kennisbasis gebruiken in hun studie en werk zullen je zeer dankbaar zijn.

Geplaatst op http://www.allbest.ru

Invoering

1.1 Top-down ontleden

1.2 Bottom-up parsing

1.2.1 LR (k) - grammatica

1.2.1.1 LR (0) - grammatica

1.2.2 LALR (1) - grammatica

2. Ontwikkeling van de vertaler

2.1 Analyse van eisen

2.2 Ontwerp

2.2.1 Een lexicale analysator ontwerpen

2.2.4 Software-implementatie van de parser

2.3 Codering

2.4 Testen

Gevolgtrekking

Lijst met gebruikte bronnen

Bijlage A. Lijst van de programmatekst van de vertaler

Bijlage B. Testresultaten

Bijlage B. Schema van het vertalersprogramma

Invoering

De tijd is lang voorbij dat, voordat je een programma schreef, het nodig was om meer dan een dozijn machine-instructies te begrijpen en te onthouden. Een moderne programmeur formuleert zijn taken in programmeertalen op hoog niveau en gebruikt alleen in uitzonderlijke gevallen assembler. Zoals u weet, komen algoritmische talen pas beschikbaar voor de programmeur na het maken van vertalers uit deze talen.

Programmeertalen verschillen behoorlijk van elkaar in doel, structuur, semantische complexiteit, implementatiemethoden. Dit legt zijn eigen specifieke kenmerken op aan de ontwikkeling van specifieke vertalers.

Programmeertalen zijn hulpmiddelen voor het oplossen van problemen in verschillende vakgebieden, die de specifieke kenmerken van hun organisatie en verschillen in doel bepalen. Voorbeelden zijn talen zoals Fortran voor wetenschappelijk computergebruik, C voor systeemprogrammering, Prolog voor het effectief beschrijven van inferentieproblemen, Lisp voor recursieve lijstverwerking. Deze voorbeelden kunnen worden vervolgd. Elk van de vakgebieden heeft zijn eigen vereisten voor de organisatie van de taal zelf. Daarom kunnen we een verscheidenheid aan vormen van representatie van operatoren en uitdrukkingen opmerken, een verschil in de reeks basisbewerkingen, een afname van de programmeerefficiëntie bij het oplossen van problemen die geen verband houden met het onderwerpgebied. Taalverschillen worden weerspiegeld in de structuur van de vertalers. Lisp en Prolog worden meestal uitgevoerd in de interpretatiemodus vanwege het feit dat ze tijdens berekeningen dynamische vorming van gegevenstypen gebruiken. Fortran-vertalers worden gekenmerkt door agressieve optimalisatie van de resulterende machinecode, wat mogelijk wordt door de relatief eenvoudige semantiek van de taalconstructies - in het bijzonder door het ontbreken van mechanismen voor alternatieve naamgeving van variabelen door middel van pointers of referenties. De aanwezigheid van pointers in de C-taal stelt specifieke eisen aan dynamische geheugentoewijzing.

De structuur van een taal kenmerkt de hiërarchische relatie tussen zijn concepten, die worden beschreven door syntactische regels. Programmeertalen kunnen erg van elkaar verschillen in de organisatie van individuele concepten en in de relaties daartussen. De programmeertaal PL/1 maakt willekeurige nesting van procedures en functies mogelijk, terwijl in C alle functies zich op het buitenste niveau van nesting moeten bevinden. De C++-taal staat de declaratie van variabelen toe op elk punt in het programma vóór het eerste gebruik, en in Pascal moeten variabelen worden gedefinieerd in een speciaal beschrijvingsgebied. Verderop in de regel is PL / 1, waarmee een variabele kan worden gedeclareerd nadat deze is gebruikt. Of de beschrijving kan helemaal worden weggelaten en worden geleid door de standaardregels. Afhankelijk van de genomen beslissing kan de vertaler het programma in één of meerdere passages analyseren, wat de uitzendsnelheid beïnvloedt.

De semantiek van programmeertalen varieert sterk. Ze verschillen niet alleen in de eigenaardigheden van de implementatie van individuele operaties, maar ook in de programmeerparadigma's die fundamentele verschillen bepalen in de methoden van programma-ontwikkeling. De bijzonderheden van de uitvoering van bewerkingen kunnen betrekking hebben op zowel de structuur van de verwerkte gegevens als de regels voor het verwerken van dezelfde gegevenstypen. Talen zoals PL/1 en APL ondersteunen matrix- en vectorbewerkingen. De meeste talen werken voornamelijk met scalairen en bieden procedures en functies die zijn geschreven door programmeurs voor het verwerken van arrays. Maar zelfs bij het optellen van twee gehele getallen kunnen talen zoals C en Pascal zich anders gedragen.

Naast traditioneel procedureel programmeren, ook wel imperatief programmeren genoemd, zijn er paradigma's als functioneel programmeren, logisch programmeren en objectgeoriënteerd programmeren. De structuur van concepten en objecten van talen hangt sterk af van het gekozen paradigma, wat ook van invloed is op de implementatie van de vertaler.
Zelfs dezelfde taal kan op verschillende manieren worden geïmplementeerd. Dit komt door het feit dat de theorie van formele grammatica verschillende methoden toelaat om dezelfde zinnen te ontleden. Dienovereenkomstig kunnen vertalers op verschillende manieren hetzelfde resultaat (objectprogramma) halen uit de originele broncode.
Alle programmeertalen hebben echter een aantal gemeenschappelijke kenmerken en parameters. Deze gemeenschappelijkheid bepaalt ook de principes van het organiseren van vertalers die voor alle talen gelijk zijn.
Programmeertalen zijn ontworpen om het programmeren gemakkelijker te maken. Daarom zijn hun operators en datastructuren krachtiger dan machinetalen.
Om de zichtbaarheid van programma's te vergroten, worden in plaats van numerieke codes symbolische of grafische representaties van taalconstructies gebruikt, die handiger zijn voor de menselijke waarneming.
Voor elke taal wordt bepaald:
- veel symbolen die kunnen worden gebruikt om de juiste programma's te schrijven (alfabet), basiselementen,
- veel correcte programma's (syntaxis),
- de "betekenis" van elk correct programma (semantiek).
Ongeacht de specifieke kenmerken van de taal, kan elke vertaler worden beschouwd als een functionele converter F die een ondubbelzinnige afbeelding van X naar Y biedt, waarbij X een programma in de brontaal is en Y een programma in de uitvoertaal. Daarom kan het vertaalproces zelf formeel vrij eenvoudig en duidelijk worden gepresenteerd: Y = F (X).
Formeel is elk correct programma X een reeks karakters uit een of ander alfabet A, die wordt omgezet in de corresponderende reeks Y, samengesteld uit karakters uit het alfabet B.
Een programmeertaal, zoals elk complex systeem, wordt gedefinieerd door een hiërarchie van concepten die de relaties tussen zijn elementen definieert. Deze concepten zijn aan elkaar gerelateerd volgens syntactische regels. Elk van de programma's die volgens deze regels zijn gebouwd, heeft een overeenkomstige hiërarchische structuur.
In dit opzicht kunnen voor alle talen en hun programma's de volgende algemene kenmerken worden onderscheiden: elke taal moet regels bevatten die het mogelijk maken om programma's te genereren die overeenkomen met deze taal of om de overeenkomst tussen geschreven programma's en een bepaalde taal te herkennen.

Een ander kenmerk van alle talen is hun semantiek. Het bepaalt de betekenis van de bewerkingen van de taal, de correctheid van de operanden. Ketens die dezelfde syntactische structuur hebben in verschillende programmeertalen kunnen verschillen in semantiek (wat bijvoorbeeld wordt waargenomen in C ++, Pascal, Basic). Kennis van de semantiek van de taal stelt u in staat om het te scheiden van de syntaxis en het te gebruiken voor conversie naar een andere taal (om code te genereren).

Het doel van deze cursus is om een ​​educatieve vertaler te ontwikkelen uit een bepaalde vereenvoudigde teksttaal op hoog niveau.

1. Methoden voor het ontleden

Laten we eens kijken naar de basismethoden voor parseren.

1.1 Top-down ontleden

Bij het ontleden van boven naar beneden bewegen de tussenliggende draden langs de boom in de richting van de wortel naar de bladeren. In dit geval zullen bij het bekijken van de keten van links naar rechts natuurlijk linkse conclusies worden verkregen. Bij deterministische ontleding zal het probleem zijn welke regel moet worden toegepast om de meest linkse niet-terminal uit te breiden.

1.1.1 LL (k) - talen en grammatica's

Beschouw de output tree terwijl je de linker output van de chain ophaalt. De tussenliggende keten in het uitvoerproces bestaat uit de keten van terminals w, de meest linkse niet-terminale A, het niet-geleverde deel van x:

-S--

/ \

/ -A-x- \

/ | \

-w --- u ----

Foto 1

Om door te gaan met ontleden, is het nodig om niet-terminale A te vervangen volgens een van de regels van de vorm A: y. Als u wilt dat het parseren deterministisch is (geen backtracking), moet u deze regel op een speciale manier kiezen. Er wordt gezegd dat een grammatica de eigenschap LL (k) heeft als, om een ​​regel te selecteren, het voldoende is om alleen wAx en de eerste k-tekens van de onzichtbare tekenreeks u te beschouwen. De eerste L (links) verwijst naar de links-naar-rechts verplaatsing van de invoersport, de tweede naar de linker pin die in gebruik is.

Laten we twee sets kettingen definiëren:

a) FIRST (x) - de reeks terminalreeksen afgeleid van x, afgekort tot k tekens.

b) VOLG (A) - een reeks terminalreeksen afgekort tot k tekens, die onmiddellijk op A kunnen volgen in de uitvoerreeksen.

Een grammatica heeft de eigenschap LL (k) als uit het bestaan ​​van twee ketens van linkse gevolgtrekkingen:

S :: wAx: wzx :: wu

S :: wAx: wtx :: wv

de voorwaarde FIRST (u) = FIRST (v) impliceert z = t.

In het geval k = 1, om een ​​regel voor A te kiezen, volstaat het om alleen de niet-terminale A en a - het eerste teken van de keten u te kennen:

- je moet de regel A: x kiezen, als a is opgenomen in FIRST (x),

- u kiest regel A: e als a is opgenomen in VOLG (A).

De eigenschap LL (k) legt nogal sterke beperkingen op aan grammatica. Bijvoorbeeld, LL (2) grammatica S: aS | a heeft niet de eigenschap LL (1), aangezien EERSTE (aS) = EERSTE (a) = een. In dit geval kunt u de waarde van k verlagen met behulp van "factorisatie" (door de factor uit de haakjes te halen):

S: aA

A: S | e

Elke LL (k) -grammatica is ondubbelzinnig. Een links recursieve grammatica behoort voor geen enkele k tot klasse LL (k). Soms is het mogelijk om een ​​niet-LL (1) grammatica om te zetten in een equivalente LL (1) grammatica door linksrecursie en factorisatie te elimineren. Het probleem van het bestaan ​​van een equivalente LL (k) -grammatica voor een willekeurige niet-LL (k) -grammatica is echter onbeslisbaar.

1.1.2 Recursieve afdalingsmethode

De recursieve afdalingsmethode is gericht op die gevallen waarin de compiler is geprogrammeerd in een van de talen op hoog niveau waar recursieve procedures zijn toegestaan.

Het basisidee achter recursieve afdaling is dat elke niet-terminal in de grammatica een overeenkomstige procedure heeft die elke tekenreeks herkent die door die niet-terminal wordt gegenereerd. Deze routines bellen elkaar wanneer nodig.

Recursieve afdaling kan worden gebruikt voor elke LL (1) grammatica. Elke niet-terminal van de grammatica heeft een overeenkomstige procedure die begint met een overgang naar een berekend label en de code bevat die overeenkomt met elke regel voor de gegeven niet-terminal. Voor die invoertekens die tot de selectieset van deze regel behoren, draagt ​​de berekende tak de controle over aan de code die overeenkomt met deze regel. Voor de rest van de ingevoerde karakters wordt de controle doorgegeven aan de foutafhandelingsroutine.

De code van een regel bevat bewerkingen voor elk teken aan de rechterkant van de regel. De bewerkingen zijn gerangschikt in de volgorde waarin de tekens zich in de regel bevinden. Na de laatste bewerking bevat de code de terugkeer van de procedure.

Het gebruik van recursieve afdaling in een taal op hoog niveau maakt programmeren en debuggen eenvoudiger.

1.2 Bottom-up parsing

Denk aan bottom-up parsing, waarbij de pinnen door de boom naar de wortel bewegen. Als je de karakters van de string van links naar rechts leest, dan ziet de ontledingsboom er als volgt uit:

-S--

/ \

/ -x- \

/ | \

--w - b - u-

Afbeelding 2

De tussenuitvoer heeft de vorm xbu, waarbij x de keten van terminals en niet-terminals is waaruit het bekeken deel van de terminalketen w wordt uitgevoerd, bu het niet-bekeken deel van de terminalketen is, b het volgende teken is. Om verder te gaan met ontleden, kunt u ofwel het teken b toevoegen aan het gescande deel van de keten (voer de zogenaamde "shift" uit), of aan het einde van x zo'n keten z (x = yz) selecteren dat een van de regels van grammatica B: z kan worden toegepast op z en x worden vervangen door de keten yB (voer de zogenaamde "convolutie uit"):

-S-- -S--

/ \ / \

/ -x-b- \ / yB- \

/ | \ / | \

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

Figuur 3 - Na verschuiving Figuur 4 - Na convolutie

Als de convolutie alleen wordt toegepast op de laatste tekens van x, krijgen we de juiste pinnen van de ketting. Dit ontleden wordt LR genoemd, waarbij het teken L (Links, links) verwijst naar het bekijken van de sport van links naar rechts, en R (Rechts, rechts) verwijst naar de resulterende pinnen.

De volgorde van schuiven en vouwen is essentieel. Daarom is het voor deterministische ontleding op elk moment vereist om te kiezen tussen verschuiving en convolutie (en verschillende convolutieregels).

1.2.1 LR (k) - grammatica

Als het tijdens het LR-parseren mogelijk is om een ​​deterministische beslissing te nemen over de verschuiving / convolutie, rekening houdend met alleen de string x en de eerste k karakters van het ongelezen deel van de invoerstring u (deze k karakters worden de pre-chain), dan zou de grammatica de LR (k) -eigenschap hebben.

-S--

/ \

/ -x- \

--w ---- u--

Figuur 5

Het verschil tussen LL (k) - en LR (k) -grammatica in termen van een inferentieboom:

-S-

/ | \

/ EEN \

/ / \ \

-w --- v --- u-

Figuur 6

In het geval van LL (k) -grammatica's kan de regel die op A wordt toegepast uniek worden bepaald door w en de eerste k symbolen vu, en in het geval van LR (k) -grammatica's door w, v en de eerste k symbolen u. Deze losse redenering laat zien dat LL (k) -talen< LR(k)-языки (при k > 0).

1.2.1.1 LR (0) - grammatica

Laten we eerst de eenvoudigste grammatica's van deze klasse bekijken - LR (0). Bij het ontleden van een LR (0) -taalstring hoef je de pre-string helemaal niet te gebruiken - de keuze tussen shift en convolutie wordt gemaakt op basis van de string x. Omdat het tijdens het ontleden alleen van rechts verandert, wordt het een stapel genoemd. We nemen aan dat er geen nutteloze symbolen in de grammatica zijn en dat het beginteken niet in de rechterkant van de regels voorkomt - dan geeft de convolutie naar het beginteken de succesvolle voltooiing van het ontleden aan. Laten we proberen de reeks ketens van terminals en niet-terminals te beschrijven die op de stapel verschijnen tijdens alle LR-parsing (met andere woorden, goede afleidingen uit de grammatica).

We definiëren de volgende sets:

L (A: v) is de linkercontext van regel A: v is de verzameling stapeltoestanden, net voor de ineenstorting van v in A tijdens alle succesvolle LR-parsing. Het is duidelijk dat elke keten van L (A: v) eindigt op v. Als we de staart v van al dergelijke kettingen afsnijden, krijgen we een reeks kettingen die links van A voorkomen in het proces van alle succesvolle rechtse gevolgtrekkingen. Laten we deze set aanduiden met L (A) - de linkercontext van de niet-terminale A.

Laten we een grammatica construeren voor de verzameling L (A). De terminals van de nieuwe grammatica zullen de terminals en niet-terminals van de originele grammatica zijn, de niet-terminals van de nieuwe grammatica zullen worden aangeduid met , ... - hun waarden zijn de linker niet-terminale contexten van de originele grammatica. Als S het beginteken is van de originele grammatica, dan zal de grammatica van de linkercontext de regel bevatten : e - de linker context S bevat een lege string Voor elke regel van de originele grammatica, bijvoorbeeld A: B C d E

en voeg de regels toe aan de nieuwe grammatica:

: - L (B) inclusief L (A)

: B - L (C) omvat L (A) B

: B C d - L (E) omvat L (A) B C d

De resulterende grammatica heeft een speciale vorm (dergelijke grammatica's worden links-lineair genoemd), daarom is de verzameling linkercontexten

- zijn regelmatig. Hieruit volgt dat het behoren van een keten tot de linkercontext van een niet-terminus inductief kan worden berekend met behulp van een eindige automaat, kijkend door de keten van links naar rechts. Laten we dit proces constructief beschrijven.

Laten we een LR (0) -situatie een grammaticaregel noemen met één gemarkeerde positie tussen de symbolen aan de rechterkant van de regel. Bijvoorbeeld voor grammatica S: A; een: AAA; A: b de volgende LR (0) -situaties bestaan: S: _A; S: A_; A: _aAA; A: a_AA; A: aA_A; A: aAAA_; een: _b; een: b_. (de positie wordt aangegeven door het onderstrepingsteken).

We zeggen dat de keten x compatibel is met de situatie A: b_c als x = ab en a hoort bij L (A). (Met andere woorden, LR-uitvoer kan worden voortgezet x _... = ab _... :: abc _... :: aA _... :: S_.) In deze termen, L (A: b) is een reeks strings die overeenkomt met situatie A: b_, L (A)

- ketens consistent met situatie A: _b, voor elke regel A: b.

Laat V (u) de verzameling situaties zijn die consistent zijn met u. Laten we aantonen dat de functie V inductief is.

Als de verzameling V(u) een situatie A: b_cd bevat, dan hoort situatie A: bc_d bij V (uc). (c - terminaal of niet-terminaal; b, d - reeksen (mag leeg zijn) van terminals en niet-terminals). Er zijn geen andere situaties zoals A: b_d met niet-lege b in V (uc). Het blijft om situaties van de vorm C: _... toe te voegen aan V (uc), voor elke niet-terminale C waarvan de linkercontext uc bevat. Als situatie A: ..._ C ... (C-niet-terminaal) tot de verzameling V (uc) behoort, dan behoort uc tot L (C) en omvat V (uc) situaties van de vorm C: _... voor alle C-grammaticaregels.

V (e) bevat zowel situaties S: _... (S-startteken) als situaties A: _... als nonterminal A direct na _ optreedt in situaties die al in V (e) zijn opgenomen.

Eindelijk zijn we klaar om een ​​LR (0) grammatica te definiëren. Laat u de inhoud zijn van de stapel in het proces van LR-parsing, V (u) de verzameling LR (0) situaties consistent met u. Als V (u) een situatie bevat van de vorm А: x_ (een x-reeks van terminals en nonterminals), dan hoort u bij L (A: x) en is de convolutie van x in A toelaatbaar. Als V (u) een situatie A bevat: ..._ a. .. (a-terminal), dan is een shift toegestaan. Men spreekt van een shift-convolutieconflict als zowel shift als convolutie toelaatbaar zijn voor één keten u. Over een convolutie-convolutie-conflict gesproken als convoluties volgens verschillende regels zijn toegestaan.

Een grammatica wordt LR (0) genoemd als er geen verschuiving-vouw- of vouw-vouwconflicten zijn voor alle stapeltoestanden in het LR-uitvoerproces.

1.2.1.2 LR (k) - grammatica

LR (0) parsing gebruikt alleen de stapelstatus om te kiezen tussen shift of fold. LR (k) parsing houdt ook rekening met de eerste k karakters van het onzichtbare deel van de invoerstring (de zogenaamde avanchang). Ter onderbouwing van de methode dient men zorgvuldig de redenering van de vorige paragraaf te herhalen en daarbij de definities aan te passen.

We zullen ook de auantchain opnemen in de linkercontext van de regel. Als de rechter uitgang de uitgang wAu: wvu toepast, dan hoort het paar wv, FIRSTk (u) bij Lk (A: v), en het paar w, FIRSTk (u) hoort bij Lk (A). De reeks linkercontexten, zoals in het geval van LR (0), kan worden berekend door inductie op de linkerketen. Laten we een LR (k) -situatie een paar noemen: een grammaticaregel met een gemarkeerde positie en een avanchain van maximaal k. We zullen de regel van de pre-keten scheiden met een verticale lijn.

We zeggen dat de keten x consistent is met situatie A: b_c | t als er een LR-uitgang is: x_yz = ab_yz :: abc_z :: aA_z :: S_, en FIRSTk (z) = t. De regels voor de inductieve berekening van de verzameling toestanden Vk zijn als volgt:

Vk (e) bevat situaties S: _a | e voor alle regels S: a, waarbij S het beginteken is. Voor elke situatie A: _Ba | u uit Vk (e), elke regel B: b, en een ketting x die hoort bij FIRSTk (au), is het nodig om situatie B: _b | x op te tellen bij Vk (e).

Als situatie A: b_cd | u Vk (w) binnenkomt, dan hoort situatie A: bc_d | u bij Vk (wc). Voor elke situatie A: b_Cd | u uit Vk (wc), elke regel C: f en een ketting x behorend bij FIRSTk (du), voeg de situatie C: _f | x toe aan Vk (wc).

We gebruiken de geconstrueerde sets van LR (k) -toestanden om het probleem van de shift-convolutie op te lossen. Laat u de inhoud van de stapel zijn en x de avanchain. Het is duidelijk dat de convolutie volgens de regel A: b kan worden uitgevoerd als Vk (u) de situatie A: b_ | x bevat. Het bepalen of een shift toelaatbaar is vereist zorgvuldige aandacht als de grammatica e-rules bevat. In situatie A: b_c | t (c is niet leeg), is een verschuiving mogelijk als c begint met een klem en x bij FIRSTk (ct) hoort. Informeel gesproken kunt u het meest linkse teken aan de rechterkant van de regel op de stapel duwen om de volgende vouw voor te bereiden. Als c begint met een niet-terminal (de situatie ziet eruit als A: b_Cd | t), dan is het alleen mogelijk om een ​​teken op de stapel te duwen en een vouw in C voor te bereiden als C geen lege ketting genereert. Bijvoorbeeld in de toestand V (e) = S: _A | e; A: _AaAb | e, a, A: _ | e, a er zijn geen geldige ploegen, omdat bij het uitvoeren van terminalketens van A in een bepaalde stap, is het vereist om de regel A: e toe te passen op de niet-terminale A die zich aan het linkeruiteinde van de keten bevindt.

We definiëren de set EFFk (x), bestaande uit alle elementen van de set FIRSTk (x), in de uitvoer waarvan de niet-terminal aan het linkeruiteinde van x (indien aanwezig) niet wordt vervangen door een lege string. In deze termen is een verschuiving toelaatbaar als de verzameling Vk (u) een situatie A bevat: b_c | t, c niet leeg is en x bij EFFk (ct) hoort.

Een grammatica wordt een LR (k) -grammatica genoemd als geen LR (k)-toestand twee situaties bevat A: b_ | u en B: c_d | v zodat u bij EFFk (dv) hoort. Zo'n paar komt overeen met een convolutie-convolutie-conflict als d leeg is, en een shift-convolutie-conflict als d niet leeg is.

In de praktijk worden LR (k) -grammatica's niet gebruikt voor k> 1. Hiervoor zijn twee redenen. Ten eerste: een zeer groot aantal toestanden LR (k). Ten tweede: voor elke taal gedefinieerd door een LR (k) -grammatica, is er een LR (1) -grammatica; bovendien is er voor elke deterministische CF-taal een LR (1) -grammatica.

Het aantal LR (1) -toestanden voor praktisch interessante grammatica's is ook vrij groot. Dergelijke grammatica's hebben zelden de eigenschap LR (0). In de praktijk wordt vaak de tussenmethode tussen LR (0) en LR (1), bekend als LALR (1), gebruikt.

1.2.2 LALR (1) - grammatica

Deze twee methoden zijn gebaseerd op hetzelfde idee. Laten we een set canonieke LR (0) -staten van de grammatica construeren. Als deze set geen conflicten bevat, kan een LR (0) -parser worden gebruikt. Anders zullen we proberen de ontstane conflicten op te lossen door rekening te houden met de pre-keten met één teken. Met andere woorden, laten we proberen een LR (1)-parser te bouwen met veel LR (0)-statussen.

LALR (1) -methode (Vooruitkijken - vooruitkijken) is als volgt. Laten we een equivalentierelatie introduceren op de verzameling LR (1) -situaties: we zullen twee situaties als equivalent beschouwen als ze alleen verschillen door hun autoketens. Situaties A: Aa_Ab | e en A: Aa_Ab | a zijn bijvoorbeeld equivalent. Laten we een canonieke verzameling LR (1) -toestanden construeren en de toestanden combineren die uit een verzameling equivalente situaties bestaan.

Als de resulterende verzameling toestanden geen LR (1)-conflicten bevat, en daarom het construeren van een LR (1) -parser mogelijk maakt, dan zou de grammatica de eigenschap LALR (1) hebben.

2. Ontwikkeling van de vertaler

2.1 Analyse van eisen

In dit cursuswerk is het noodzakelijk om een ​​educatieve vertaler te ontwikkelen in de vorm van een tolk uit de taal die wordt gedefinieerd door de overeenkomstige formele grammatica. Er zijn vier hoofdfasen in de ontwikkeling van een tolk:

Het ontwerpen van een lexicale analysator;

Ontwerp van een tijdschriftenautomaat;

Software-implementatie van de parser;

Ontwikkeling van de tolkmodule.

De ontwikkeling zal worden uitgevoerd met het besturingssysteem Windows XP op een IBM-pc met een Intel Pentium IV-processor.

Op basis van de trends in softwareontwikkeling is voor de implementatie van de educatieve vertaler gekozen voor de programmeertaal C# in de Visual Studio 2010-omgeving.

2.2 Ontwerp

2.1.1 Een lexicale analysator ontwerpen

Lexicale de analyse omvat het scannen van het vertaalde (bron)programma en het herkennen van de lexemen die de zinnen van de brontekst vormen. Tokens omvatten met name trefwoorden, bedieningstekens, identifiers, constanten, speciale tekens, enz.

Het resultaat van de lexicale analysator (scanner) is een reeks tokens, en elk token wordt meestal weergegeven door een code met een vaste lengte (bijvoorbeeld een geheel getal), evenals de uitgifte van berichten over syntactische (lexicale) fouten, indien van toepassing. Als een token bijvoorbeeld een trefwoord is, geeft de code alle benodigde informatie. In het geval van bijvoorbeeld een identifier is bovendien de naam van de herkende identifier vereist, die meestal wordt vastgelegd in de identifier-tabel, die meestal wordt georganiseerd met behulp van lijsten. Voor constanten is een vergelijkbare tabel nodig.

Een lexeme kan worden beschreven door twee hoofdkenmerken. Een daarvan is het behoren van een lexeme tot een bepaalde klasse (variabelen, constanten, operaties, enz.) Het tweede attribuut definieert een specifiek element van deze klasse.

Het exacte uiterlijk van de symbooltabel (gegevensstructuur) is niet relevant voor de lexicale of parser. Beiden hoeven alleen de mogelijkheid te bieden om een ​​index te krijgen die bijvoorbeeld een bepaalde variabele op unieke wijze identificeert en de indexwaarde te retourneren om informatie over een bepaalde variabelenaam in de symbooltabel aan te vullen.

Het opzoeken van de ID-tabel heeft twee hoofdfuncties:

a) het schrijven van een nieuwe naam aan de tabel bij het verwerken van de beschrijving van variabelen;

b) zoek naar een naam die eerder in de tabel is opgenomen.

Hiermee kunt u fouten detecteren, zoals meerdere beschrijvingen van een variabele en de aanwezigheid van een onbeschreven variabele.

De ontwikkeling van een lexicale analysator bestaat voor een deel uit het modelleren van verschillende automaten voor het herkennen van identifiers, constanten, gereserveerde woorden, enz. Als verschillende soorten tokens beginnen met hetzelfde symbool of dezelfde reeks tekens, kan het nodig zijn om hun herkenning te combineren.

Bij het starten van de lexicale analysator splitsen we ons programma op in tokens, waarna elk token wordt gecontroleerd op lengte (een token mag niet meer dan 11 tekens bevatten). Nadat we deze fase met succes hebben doorlopen, controleren we de juistheid van de locatie van tokens (trefwoorden var, begin, end, for, to, do, end_for). Vervolgens analyseren we de lexemen van de variabelen - ze mogen geen getallen bevatten in hun beschrijving en worden herhaald. In de laatste fase controleren we de spelling van tokens (trefwoorden, onbekende identifiers). Als ten minste één van de controles een fout oplevert, geeft de lexicale analysator een fout weer.

Een schematisch diagram van het lexicale analyseprogramma wordt getoond in bijlage B in figuur B.1.

2.2.2 Een tijdschriftendispenser ontwerpen

Laten we de volgende grammatica instellen:

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

waarbij Vt de verzameling terminale symbolen is, Va de verzameling niet-terminale symbolen is, I de begintoestand van de grammatica is, R de verzameling grammaticaregels is.

Voor een bepaalde grammatica definiëren we sets terminale en niet-terminale symbolen:

Laten we de regels voor onze grammatica opstellen Г en ze in tabel 1 opsommen.

Tabel 1 - Grammaticaregels

Regel nr.

Linkerkant van de regel

Rechts van de regel

f ID = EX t EX d LE n;

Vervolg van tabel 1.

Regel nr.

Linkerkant van de regel

Rechts van de regel

De aanduidingen van tokens, de vertaling van tokens in codes en de lijst van grammaticale aanduidingen worden gegeven in respectievelijk tabellen 2, 3 en 4.

Tabel 2 - Symbolen van tokens

Lexeme-aanduiding

het trefwoord "begin" (het begin van de beschrijving van acties)

het trefwoord "end" (het einde van de beschrijving van acties)

trefwoord "var" (variabele beschrijving)

het trefwoord "lezen" (operator voor gegevensinvoer)

het trefwoord "schrijven" (operator voor gegevensuitvoer)

trefwoord "voor" (lus-operator)

trefwoord "naar"

trefwoord "doen"

trefwoord "end_case" (einde van de lus-instructie)

variabele type "integer"

bewerking toevoeging

aftrekken operatie

vermenigvuldiging operatie

scheidingsteken ":"

scheidingsteken ";"

het scheidingsteken "("

scheidingsteken ")"

het scheidingsteken ","

Lexeme-aanduiding

scheidingsteken "="

Tabel 3 - Vertaling van lexemen in codes

<цифра>

<буква>

Tabel 4 - Lijst met grammaticale aanduidingen

Aanwijzing

Uitleg

Programma

Beschrijving van berekeningen

Beschrijving van variabelen

Variabele lijst

Operator

Opdracht

Uitdrukking

subuitdrukking

Binaire bewerkingen

Unaire operaties

Opdrachtenlijst

ID

Constante

Laten we een deterministische stroomopwaartse herkenner bouwen.

Overweeg de volgende relaties om een ​​deterministische upstream-resolver te bouwen:

a) Als er een symbool van groep B is zodat een grammaticaregel de keten AB bevat en er is een symbool xPERB "(B), dan nemen we aan dat de relaties x NA A worden bepaald tussen de symbolen x en A

b) Als er in een bepaalde grammatica een regel is B -> bAb A, BV a, b, dan wordt tussen A en x de verhouding A SVERT x bepaald.

Al onze grammatica blijft hetzelfde, namelijk:

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

en de regels van grammatica D staan ​​in tabel 5.

Tabel 5 - Grammaticaregels

Regel nr.

Linkerkant van de regel

Rechts van de regel

f ID = EX t EX d LE n ;?

Vervolg van tabel 5.

Regel nr.

Linkerkant van de regel

Rechts van de regel

Waar? - marker van het einde van de ketting.

Laten we enkele gevallen definiëren:

a) De identificatie-ID bestaat uit een reeks letters van het Latijnse alfabet, dat wil zeggen dat we aannemen dat 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) De CO-constante bestaat uit getallen, dat wil zeggen dat we aannemen dat k = (0,1,2,3,4,5,6,7,8,9)

Om onze grammatica een strategie met gemengde prioriteit te laten zijn, moet aan de volgende voorwaarden worden voldaan:

a) Gebrek aan e-regels

b) Er zijn regels waaronder, x NA A? Een SVERT x =?

c) A -> bYg

en het is noodzakelijk dat IN NA x? VERTED x =?

dat wil zeggen dat ze in de grammatica worden uitgevoerd IN NA x of A NA x, waarbij x het predikaatsymbool is van de tekenreeks b.

a) EERSTE "(PG) = (PG?)

EERSTE "(RG) = EERSTE (DE) = (RG, v,:, ik ,;)

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

EERSTE "(DE) = EERSTE (v LV: i;) = (DE, v,:, ik ,;)

EERSTE "(LV) = EERSTE (ID, LV) = (LV, ID)

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

EERSTE "(EQ) = EERSTE (ID = EX;) = (EQ, = ,;)

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

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

EERSTE "(SB) = EERSTE ((EX) SB)? EERSTE (OP)? EERSTE (IN) = (SB, (,), OP, BO);

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

EERSTE "(UO) = (UO, -)

EERSTE "(ID) = EERSTE" (u) = (u)

EERSTE "(CO) = EERSTE" (k) = (k) EERSTE "(e) = (e)

EERSTE "(b) = (b)

EERSTE "(e) = (e)

EERSTE "(v) = (v)

EERSTE "(w) = (w)

EERSTE "(r) = (r)

EERSTE "(i) = (i)

EERSTE "(f) = (f)

EERSTE "(d) = (d)

EERSTE "(n) = (n)

EERSTE "(c) = (c)

EERSTE "(+) = (+)

EERSTE "(*) = (*)

EERSTE "(-) = (-)

EERSTE "(,) = (,)

EERSTE "(;) = (;)

EERSTE "(:) = (:)

EERSTE "(=) = (=)

EERSTE "(() = (()

EERSTE "()) = ())

EERSTE "(u) = (u)

EERSTE "(k) = (k)

b) VOLGENDE `(AL) = (?)? VOLGENDE" (PG) = (?, b, e)

VOLGENDE `(DE) = (?)? EERSTE" (AL) = (?, B, e)

VOLGENDE `(LV) = (?)? EERSTE" (:) = (?, :)

VOLGENDE `(OP) = (?)? EERSTE" (SB) = (?,;,), D, t, +, -, *)

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

VOLGENDE `(EX) = (?)? FIRST" (t)? FIRST "(d)? FIRST" (;)? FIRST "()) = (?, t, d,;,))

VOLGENDE `(BO) = (?)? EERSTE" (SB) = (?, (,), OP, BO)

VOLGENDE `(UO) = (?)? EERSTE" (SB) = (?, (,), OP, BO)

VOLGENDE `(SB) = (?)? VOLGENDE" (EX) = (?, T, d,;,), +, *, -)

VOLGENDE `(LE) = (?)? EERSTE" (e)? EERSTE "(n) = (?, E, n)

VOLGENDE `(ID) = (?)? VOLGENDE "(OP)? EERSTE" (=) = (?,;,), D, t, +, -, *, =)

VOLGENDE `(CO) = (?)? VOLGENDE "(OP) = (?,;,), D, t, +, -, *, =)

VOLGENDE `(b) = (?)? EERSTE" (LE) = (?, U, = ,;)

VOLGENDE `(e) = (?)? VOLGENDE" (AL) = (?, B)

VOLGENDE `(v) = (?)? EERSTE" (LV) = (?, U)

VOLGENDE `(w) = (?)? EERSTE" (() = (?, ()

VOLGENDE `(r) = (?)? EERSTE" (() = (?, ()

VOLGENDE `(i) = (?)? EERSTE" (;) = (?,;)

VOLGENDE `(f) = (?)? EERSTE" (ID) = (?, U)

VOLGENDE `(d) = (?)? EERSTE" (LE) = (?, U, = ,;)

VOLGENDE `(n) = (?)? EERSTE" (i) = (?, I)

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

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

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

NEXT `(;) = (?)? NEXT" (DE)? NEXT `(LE1)? NEXT" (EQ) = (?, B, e, l, u)

VOLGENDE `(:) = (?)? EERSTE" (i) = (?, I)

VOLGENDE `(=) = (?)? EERSTE" (EX) = (? (,), U, k, +, -, *)

VOLGENDE `(() = (?)? EERSTE" (DE) = (?, V,:, ik ,;)

VOLGENDE `()) = (?)? EERSTE "(;) = (?,;)

VOLGENDE `(,) = (?)? EERSTE "(LV) = (?, U)

VOLGENDE `(u) = (?)? EERSTE "(ID) = (u,?)

VOLGENDE `(k) = (?)? EERSTE (CO) = (?, K)

c) PG -> DE AL

AL NA DE = (b, e) NA DE = ((b DE), (e DE))

e NA LE = ((e LE))

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

; NA ik = ((; ik))

ik NA: = ((ik :))

: NA LV = ((: LV))

LV NA v = ((ID, v))

LV NA, = ((ID,))

NA ID = ((, u))

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

LE -> r (DE);

; NA) = ((;)))

) NA DE = (((DE))

DE NA (= (= ((v)), (:)), (i)), (;)), (e)))

(NA r = (((r))

LE -> w (DE);

; NA) = ((;)))

) GESLACHT DE = (((DE))

DE NA (= ((v)), (:)), (i)), (;)), (e)))

(NA w = (((w))

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

; NA n = ((; n))

n NA LE = ((n, LE))

NA d = (((,), =,;, f, t, d, n, w, r))? NA d = (((d), () d), (; d), (f d), (t d), (d d), (n d), (w d), (r d))

d NA EX = ((d, EX))

EX NA t = (BO, -)? NA t = ((BO t), (- t))

t NA EX = (t EX)

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

NA ID = ((= ID))

ID NA f = ((ID f))

EQ -> ID = EX;

; NA EX = ((; EX)

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

NA u = ((= u))

SB NA UO = ((,), OP, BO) NA UO = (((UO), (OP UO), (BO UO))

) NA EX = (() EX))

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

SB-> SB BO SB

SB NA BO = ((,), OP, BO) NA BO = (((BO), () BO), (OP BO), (BO BO))

BO NA SB = (+, *, -) NA SB = ((+ SB), (* SB), (-SB))

ID NA u = ((u, u))

G) PG -> DE AL

AL SCHAKELAAR PG = AL SCHAKELAAR VOLG "(PG) = ((AL?))

e CONVERT AL = e CONVERT NEXT "(AL) = ((eb), (e?))

=; VOLGENDE CONVERTEREN "(DE) = ((; b), (;?))

LV SLIKKEN LV = LV SLIKKEN VOLG "(LV) = ((LV :), (LV?))

ID-SCHAKELAAR LV = ID-SCHAKELAAR VOLG "(LV) = ((ID :), (ID?))

; CONVERSIE LE =; VOLGENDE CONVERTEREN "(LE) = ((; e), (;?), (; N))

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

; CONVERSIE LE =; VOLGENDE CONVERTEREN "(LE) = ((; e), (;?), (; N))

EQ CONVERT LE = EQ CONVERT NEXT "(LE) = ((EQ e), (EQ?), (EQ n))

EQ -> ID = EX;

; VERSIE EQ =; CONVERT NEXT "(EQ) = ((; (), (;)), (;;), (; f), (;?), (; =), (; T), (; d), (; n), (; w), (; r))

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

) CONVERT SB = SB CONVERT NEXT "(SB) = (() t), ()?), () D), ())), ();))

OP CONVERT SB = OP CONVERT NEXT "(SB) = ((OP t), (OP?), (OP d), (OP)), (OP;))

SB-> SB BO SB

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

CONVERT UO = - CONVERT NEXT "(UO) = ((-?), (-))

CONVERT BO = + CONVERT NEXT "(BO) = ((++), (+?), (+ *), (+ -))

* CONVERT BO = * CONVERT NEXT "(BO) = ((* +), (*?), (**), (*)

CONVERT BO = - CONVERT NEXT "(BO) = ((- +), (-?), (- *), (-))

ID CONTRACT OP = ID CONTRACT VOLG "(OP) = ((ID +), (ID?), (ID *), (ID-))

CO COLLECT OP = CO COLLECT FOLLOW "(OP) = ((CO +), (CO?), (CO *), (CO-), (CO;), (COd), (COt), (CO)) )

ID CONVERT ID = ID CONVERT NEXT "(ID) = ((ID)), (ID?), (ID k), (ID +), (ID-), (ID *), (ID =), (IDt ) , (IDd)))

u CONVERT ID = l CONVERT NEXT "(ID) = ((u)), (u?), (uk), (u +), (u-), (u *), (u =), (ut) , (u)))

CO VERZAMEL CO = CO VERZAMEL VOLG "(CO) = (CO +), (CO?), (CO *), (CO-), (CO;), (COd), (COt), (CO)))

k CONVERT CO = k CONVERT TRACE "(CO) = (k +), (k?), (k *), (k-), (k;), (kd), (kt), (k)))

Eén botsing gedetecteerd tijdens rijregels

OP -> ID en ID -> u ID

We voeren ID1 -> ID in, daarom herschrijven we de regel ID1 -> u ID

Daarom zullen we de convolutiebewerkingen uitvoeren.

ID1 CONVERT ID = ID1 CONVERT NEXT "(ID) = ((ID1)), (ID1?), (ID1 k), (ID1 +), (ID1-), (ID1 *), (ID1 =), (ID1t ) , (ID1d)))

Voor elk paar (x, A)? x NA A construeren we een overgangsfunctie die de overdrachtsactie bepaalt ?? (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, b, b) = (S0, bw)

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

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

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

? (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, ik,)) = (S0,) ik)

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

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

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

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

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

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

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

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

? (S0,;, d) = (S0, 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, u)

Voor elk paar (x, A)? A CONVERT X bouwt een overgangsfunctie die de actie van de convolutie bepaalt ?? * (S 0, x, bA) = (S 0, B), waarbij 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 Software-implementatie van de parser

De parser (parser) leest het bestand met tokens gegenereerd door de lexicale analysator, voert grammaticale parsering uit, geeft berichten over eventuele syntaxisfouten en maakt een tussenrecord van het bronprogramma. De ontwikkeling van een parser is gebaseerd op het ontwerp en de implementatie van een overeenkomstige, in de winkel gekochte machine.

Voor oplopende ontleding voor een deterministische oplopende herkenner, is het nodig om, na deze in de gewenste vorm te hebben gebracht, een FSM te ontwerpen met behulp van de AFTER- en CONVERT-functies met een gedetailleerde beschrijving van alle overgangen binnen de overgangsfunctie.

Bij het ontwikkelen van de FSM hebben we de transitiefuncties gebouwd die de basis zullen vormen van de parser. Alle overgangsfuncties kunnen worden onderverdeeld in twee typen:

Het vinkje van de tijdschriftenautomaat zonder het invoerteken te lezen (leeg vinkje);

Het vinkje van de winkelmachine met het lezen van het invoersymbool.

Bij het implementeren van de lexicale analysator hebben we het programma opgesplitst in tokens en deze opgeschreven in een lijst. Deze lijst verwerken we vervolgens in een parser. Bij de ingang sturen we ons programma (lijst), het beginsymbool (PG) en de markering van de onderkant van de ATM (h0), waarna de gewenste overgangsfunctie wordt geselecteerd en een recursieve aanroep wordt gedaan.

Een schematisch diagram van het parserprogramma wordt getoond in bijlage B in figuur B.2.

2.2.4 Ontwikkeling van de tolkmodule

Bij het ontwikkelen van een tolkmodule als tussenvorm van het originele programma de meest gebruikte is de postfix-notatievorm, die het gemakkelijk maakt om het uitvoeringsproces (interpretatie) van het vertaalde programma te implementeren.

Laten we eens kijken naar de basisprincipes van de vorming en uitvoering van de postfix-vorm van schrijfuitdrukkingen.

De basisregels voor het converteren van een infix-expressie naar een postfix-expressie zijn als volgt.

De gelezen operanden worden toegevoegd aan het postfix-record, de bewerkingen worden op de stapel geschreven.

Als de bewerking bovenaan de stapel een hogere (of gelijke) prioriteit heeft dan de momenteel gelezen bewerking, wordt de bewerking van de stapel toegevoegd aan het postfix-record en wordt de huidige bewerking op de stapel geduwd. Anders (met de laagste prioriteit) wordt alleen de huidige bewerking op de stapel geduwd.

Het lees-open haakje wordt op de stapel geschoven.

Na het lezen van het haakje sluiten, worden alle bewerkingen tot aan het eerste haakje openen uit de stapel gehaald en toegevoegd aan het postfix-record, waarna zowel de haakjes openen als sluiten worden weggegooid, d.w.z. worden niet op het postfix-record of op de stapel geplaatst.

Na het lezen van de volledige uitdrukking, worden de resterende bewerkingen op de stapel toegevoegd aan het postfix-record.

Met de postfix-notatie van de uitdrukking kunt u deze als volgt evalueren.

Als het token een operand is, wordt het op de stapel geduwd. Als het token een bewerking is, wordt de gespecificeerde bewerking uitgevoerd op de laatste elementen (laatste element) die op de stapel zijn geduwd, en deze elementen (element) worden op de stapel vervangen door het resultaat van de bewerking.

Als de lexicale en syntactische analyses met succes zijn geslaagd, gaan we verder met de interpretatie. Eerst maken we zinnen van woorden, dan vertalen we de uitdrukkingen in een postfix-notatie en rekenen.

Het schema van de tolkwerking wordt weergegeven in bijlage B in figuur B.3.

2.3 Codering

Het programma is geïmplementeerd in C # in de programmeeromgeving van Visual Studio 2010. De tekst van het programma is weergegeven in bijlage A.

Er zijn vijf klassen geïmplementeerd in het programma. De gebruikersinterface is geïmplementeerd met behulp van de MainForn-klasse. Met behulp van de LexAnalysis-klasse is een lexicale analysemodule geïmplementeerd, SynAnalysis is een parseermodule, Intepreter is een interpretatiemodule, ProgramisciJakPolska is een hulpklasse voor het vertalen van uitdrukkingen in omgekeerde Poolse notatie (postfix).

Het doel van de procedures en functies die in het programma zijn geïmplementeerd, wordt beschreven in tabellen 6,7,8.

Tabel 6 - Doel van procedures en functies van lexicale analyse

Vergelijkbare documenten

    Een vertaler is een programma of een technisch middel dat een programma vertaalt. Overweging van de belangrijkste kenmerken van de constructie van de lexicale analysator. Kennis van de stadia van de ontwikkeling van vertalers vanuit een beperkte subset van een taal op hoog niveau.

    scriptie, toegevoegd 08/06/2013

    Ontwerpen van lexicale en syntactische analysers van de onderwijstaal. Regels voor het converteren van logische expressies naar POLIZ. Vorming van triaden, optimalisatie van hun lijst. De logische opbouw van het programma. Testen van vertaler-tolkmodules.

    scriptie toegevoegd op 28-05-2013

    Algemene kenmerken en beoordeling van de mogelijkheden van de programmeertaal C-Sharp, de overeenkomsten en verschillen met C++ en Java. Ontwikkeling van een lexicale en syntactische analysator met behulp van deze programmeertaal. Opstellen van parseertabellen.

    scriptie, toegevoegd 06/11/2010

    Het ontwerpen van een analyseprogramma dat uit twee delen bestaat: een lexicale analyser die de broncode van een programma opsplitst in tokens en een tabel met namen invult; een parser die controleert of de tekst overeenkomt met de gegeven grammatica.

    scriptie, toegevoegd 14-06-2010

    Het schrijven van een programma dat lexicale en parsing van een invoerprogrammeertaal uitvoert, een tabel met tokens genereert met een indicatie van hun typen en waarden, en ook een syntaxisboom bouwt; de tekst van de invoertaal wordt ingevoerd vanaf het toetsenbord.

    scriptie toegevoegd 23-02-2012

    Ontwikkelingsmethodologie en gedeeltelijke implementatie van een vertaler voor de "C"-taal met behulp van de "C ++"-taal, die de originele tekenreeks opsplitst in minimaal ondeelbare taalconstructies op basis van het taalvocabulaire. Analyse van het programma.

    scriptie, toegevoegd 19/03/2012

    Compilerstructuur, classificatie en implementatievereisten. Ontwerp en implementatie van het analyserende deel van de C++ compiler. Manieren om lexicale analyse te implementeren. Algoritme van de parser. Principes van software-implementatie.

    scriptie toegevoegd 26/01/2013

    Creatie van een vertaler die de programmacode in Pascal verwerkt en, ten koste van gelijkwaardige operators, een C-programma genereert. Kenmerken van de externe specificatie en de werking van de lexicale analysator. De structuur van het programma, de output van de resultaten op het scherm.

    scriptie, toegevoegd 07/02/2011

    Methoden voor parseren. Ontwikkeling van de structuur van de educatieve vertaler in de basisprogrammeertaal Object Pascal in de objectgeoriënteerde visuele programmering Borland DELPHI 6.0 met behulp van het Windows XP-besturingssysteem.

    scriptie, toegevoegd 05/12/2013

    Software-implementatie van een desktop-applicatie met behulp van de programmeertaal C#. Ontwerp en opbouw van de gebruikersinterface, eisen daaraan en beoordeling van functionaliteit. Ontwikkeling van de gebruikershandleiding en het gebruik ervan.

Vertaler (Engelse vertaler - vertaler) is een vertaalprogramma. Het converteert een programma dat is geschreven in een van de talen op hoog niveau naar een programma dat bestaat uit machine-instructies. De vertaler voert meestal ook foutdiagnose uit, genereert woordenboeken met identificatiecodes, drukt programmateksten af, enz. De taal waarin het invoerprogramma wordt gepresenteerd, wordt de brontaal genoemd en het programma zelf wordt de broncode genoemd. De uitvoertaal wordt de doeltaal of objectcode genoemd.

Over het algemeen verwijst het concept van vertaling niet alleen naar programmeertalen, maar ook naar andere talen - zowel formele computer (zoals opmaaktalen zoals HTML) als natuurlijk (Russisch, Engels, enz.).

Soorten vertalers

    dialoog. Biedt het gebruik van een time-sharing programmeertaal.

    Syntaxisgericht (syntaxisgestuurd). Ontvangt als invoer een beschrijving van de syntaxis en semantiek van de taal en een tekst in de beschreven taal, die wordt vertaald in overeenstemming met de opgegeven beschrijving.

    Enkele doorgang. Vormt een objectmodule in één sequentiële scan van het bronprogramma.

    Multi-pass. Vormt een objectmodule na verschillende weergaven van het bronprogramma.

    Optimaliseren. Optimaliseert de code in de gegenereerde objectmodule.

    Test. Een set assembler-macro's waarmee u verschillende foutopsporingsprocedures kunt instellen in programma's die in assembler zijn geschreven.

    Rug. Voor een programma in machinecode voert het een equivalent programma uit in elke programmeertaal (zie: disassembler, decompiler).

Vertalers worden geïmplementeerd als compilers of tolken. Compiler en tolk zijn heel verschillend in termen van hoe ze de klus klaren.

Compiler (Engelse compiler - compiler, verzamelaar) leest het hele programma in zijn geheel, maakt de vertaling en maakt een volledige versie van het programma in machinetaal, die vervolgens wordt uitgevoerd. De invoerinformatie voor de compiler (broncode) is een beschrijving van het algoritme of een programma in een probleemgeoriënteerde taal, en de uitvoer van de compiler is een equivalente beschrijving van het algoritme in een machinegeoriënteerde taal (objectcode).

Compilertypen

    Vectoriseren. Vertaalt de broncode naar machinecode van computers die zijn uitgerust met een vectorprocessor.

    Flexibel. Modulair ontworpen, aangestuurd door tabellen en geprogrammeerd in taal op hoog niveau of geïmplementeerd met een compiler-compiler.

    dialoog. Zie: dialoogvertaler.

    Toenemend. Hervertaalt programmafragmenten en toevoegingen eraan zonder het hele programma opnieuw te compileren.

    Tolken (stap voor stap). Voert opeenvolgend onafhankelijke compilatie uit van elke individuele instructie (opdracht) van het bronprogramma.

    Compiler-compiler. Een vertaler die een formele beschrijving van een programmeertaal accepteert en een compiler voor deze taal genereert.

    Debuggen. Elimineert bepaalde soorten syntaxisfouten.

    Inwoner. Het bevindt zich constant in het geheugen en is beschikbaar voor hergebruik door vele taken.

    Zelf samenstellen. Het is geschreven in dezelfde taal waarin de uitzending wordt uitgevoerd.

    Universeel. Gebaseerd op een formele beschrijving van de syntaxis en semantiek van de invoertaal. De samenstellende delen van zo'n compiler zijn: de core, syntactische en semantische loaders.

Programma's hebben, net als mensen, een vertaler of vertaler nodig om van de ene taal naar de andere te vertalen.

Basisconcepten

Het programma is een taalkundige weergave van berekeningen: i → P → P (i). Een interpreter is een programma dat programma P en enkele invoergegevens x als invoer ontvangt. Het voert P op x uit: I (P, x) = P (x). Het feit dat er één vertaler is die alle mogelijke programma's kan uitvoeren (die in een formeel systeem kunnen worden weergegeven) is een zeer diepgaande en belangrijke ontdekking van Turing.

Een processor is een tolk voor machinetaalprogramma's. Het is over het algemeen te duur om tolken te schrijven voor talen op hoog niveau, dus worden ze vertaald in een vorm die gemakkelijker te interpreteren is.

Sommige soorten vertalers hebben hele vreemde namen:

  • Assembler vertaalt assembler programma's naar machinetaal.
  • De compiler vertaalt van een taal op hoog niveau naar een taal op een lager niveau.

Een vertaler is een programma dat een programma in een of andere taal S als invoer neemt en een programma in T uitvoert op een zodanige manier dat beide dezelfde semantiek hebben: P → X → Q. Dat wil zeggen, ∀x. P (x) = Q (x).

Het vertalen van een heel programma naar iets interpreteerbaars wordt compilatie vóór uitvoering of AOT-compilatie genoemd. AOT-compilers kunnen sequentieel worden gebruikt, waarvan de laatste vaak assembler is, bijvoorbeeld:

Broncode -> Compiler (vertaler) -> Assembler-code -> Assembler (vertaler) -> Machinecode -> CPU (tolk).

Online of dynamische compilatie vindt plaats wanneer een deel van een programma wordt vertaald terwijl andere eerder gecompileerde delen worden uitgevoerd. JIT-compilers onthouden wat ze al hebben gedaan, zodat ze de broncode niet steeds opnieuw hoeven te herhalen. Ze zijn zelfs in staat tot adaptieve compilatie en hercompilatie op basis van het gedrag van de runtime van het programma.

In veel talen kun je code uitvoeren tijdens het compileren en nieuwe code compileren tijdens runtime.

Uitzendstadia

De uitzending bestaat uit de fasen van analyse en synthese:

Broncode -> Analyser -> Conceptuele weergave -> Generator (Synthesizer) -> Doelcode.

Dit komt door de volgende redenen:

  • Elke andere methode zal niet werken. Woord-voor-woord vertaling werkt gewoon niet.
  • Een goede technische oplossing: als u vertalers moet schrijven voor M brontalen en N doeltalen, hoeft u alleen M + N eenvoudige programma's (semi-compilers) te schrijven, niet M × N-complex (volledige vertalers).

In de praktijk is een conceptuele representatie echter zelden expressief en krachtig genoeg om elke denkbare bron- en doeltaal te omvatten. Hoewel sommigen hier in de buurt konden komen.

Echte compilers doorlopen vele stadia. Wanneer u uw eigen compiler bouwt, hoeft u niet al het harde werk te herhalen dat mensen al hebben gedaan bij het maken van views en generatoren. U kunt uw taal rechtstreeks in JavaScript of C vertalen en bestaande JavaScript-engines en C-compilers gebruiken om de rest te doen. U kunt ook bestaande tussenvoorstellingen gebruiken en

Opname van vertaler

Een vertaler is een programma of technisch middel waarbij drie talen betrokken zijn: bron, doel en basis. Ze kunnen in T-vorm worden geschreven, waarbij het origineel aan de linkerkant, het doel aan de rechterkant en de basislijn eronder worden geplaatst.

Er zijn drie soorten compilers:

  • Een vertaler is een zelfcompiler als de brontaal overeenkomt met de basistaal.
  • Een compiler waarvan de doeltaal gelijk is aan de basistaal wordt self-resident genoemd.
  • Een vertaler is een cross-compiler als zijn doel- en basistalen verschillend zijn.

Waarom is het belangrijk?

Ook als je nooit een echte compiler maakt is het goed om de techniek erachter te kennen, want de concepten die hiervoor gebruikt worden worden overal toegepast, bijvoorbeeld in:

  • opmaak van teksten;
  • naar databanken;
  • geavanceerde computerarchitecturen;
  • gegeneraliseerd;
  • grafische interfaces;
  • scripttalen;
  • controllers;
  • virtuele machines;
  • automatische vertalingen.

Als u bovendien preprocessors, assemblers, loaders, debuggers of profilers moet schrijven, moet u dezelfde stappen doorlopen als bij het schrijven van een compiler.

U kunt ook leren hoe u programma's beter kunt schrijven, aangezien het creëren van een vertaler voor een taal betekent dat u de fijne kneepjes en dubbelzinnigheden ervan beter begrijpt. Door de algemene principes van omroep te leren, kun je ook een goede taalontwerper worden. Maakt het echt uit hoe cool een taal is als deze niet efficiënt kan worden geïmplementeerd?

Uitgebreide technologie

Compilertechnologie omvat veel verschillende gebieden van de informatica:

  • formele taaltheorie: grammatica, ontleden, berekenbaarheid;
  • computerarchitectuur: instructiesets, RISC of CISC, pipelining, cores, klokcycli, enz.;
  • programmeertaalconcepten: bijv. sequentiecontrole, voorwaardelijke uitvoering, iteratie, recursie, functionele decompositie, modulariteit, synchronisatie, metaprogrammering, bereik, constanten, subtypes, sjablonen, uitvoertype, prototypes, annotaties, streams, monaden, mailboxen, voortzettingen, wildcards, regulier uitdrukkingen, transactiegeheugen, overerving, polymorfisme, parametermodi, enz.;
  • abstracte talen en virtuele machines;
  • algoritmen en reguliere expressies, ontledingsalgoritmen, grafische algoritmen, training;
  • programmeertalen: syntaxis, semantiek (statisch en dynamisch), ondersteuning voor paradigma's (structureel, OOP, functioneel, logisch, stapel, parallellisme, metaprogrammering);
  • softwareontwikkeling (compilers zijn meestal groot en complex): lokalisatie, caching, componentisatie, API's, hergebruik, synchronisatie.

Compilerontwerp

Enkele problemen bij het ontwikkelen van een echte vertaler:

  • Problemen met de originele taal. Is het makkelijk te compileren? Is er een preprocessor? Hoe worden typen behandeld? Zijn er bibliotheken?
  • Compiler-pasgroepering: single of multi-pass?
  • De gewenste mate van optimalisatie. Een snelle en onzuivere uitzending van een programma met weinig of geen optimalisatie kan normaal zijn. Over-optimalisatie zal de compiler vertragen, maar betere runtime-code kan de moeite waard zijn.
  • De vereiste mate van foutdetectie. Kan de vertaler gewoon stoppen bij de eerste fout? Wanneer moet hij stoppen? Moet de compiler worden vertrouwd om fouten te herstellen?
  • Beschikbaarheid van hulpmiddelen. Tenzij de brontaal erg klein is, zijn een scanner en een parsergenerator vereist. Er zijn ook codegeneratorgeneratoren, maar die komen niet zo vaak voor.
  • Het soort doelcode dat moet worden gegenereerd. Kies uit pure, augmented of virtuele machinecode. Of schrijf gewoon de front-end die populaire tussenliggende views creëert, zoals LLVM, RTL of JVM. Of maak een vertaling van broncode naar broncode in C of JavaScript.
  • Indeling doelcode. U kunt een draagbaar geheugenbeeld kiezen.
  • Opnieuw targeten. Als er veel generatoren zijn, is het goed om een ​​gemeenschappelijk ingangseinde te hebben. Om dezelfde reden is het beter om één generator te hebben voor veel input-onderdelen.

Compilerarchitectuur: componenten

Dit zijn de belangrijkste functionele componenten van een vertaler die machinecode genereert (als het uitvoerprogramma een C-programma of een virtuele machine is, zijn er niet zo veel stappen nodig):

  • Het invoerprogramma (stroom van karakters) komt de scanner binnen (lexicale analysator), die het omzet in een stroom tokens.
  • De parser (parser) bouwt er een abstracte syntaxisboom van op.
  • De semantische analysator ontleedt semantische informatie en controleert de boomknooppunten op fouten. Als resultaat wordt een semantische grafiek gebouwd - een abstracte syntaxisboom met extra eigenschappen en gevestigde koppelingen.
  • De tussencodegenerator bouwt een stroomdiagram (tupels zijn gegroepeerd in hoofdblokken).
  • De machine-onafhankelijke code-optimalisatie voert zowel lokale (binnen het basisblok) als globale (over alle blokken) optimalisatie uit, waarbij het meestal binnen het kader van subroutines blijft. Vermindert overtollige code en vereenvoudigt de berekening. Het resultaat is een aangepaste stroomgrafiek.
  • De doelcodegenerator bindt basisblokken tot rechttoe rechtaan code met een overdracht van controle, waardoor een assembleertaalobjectbestand wordt gemaakt met (mogelijk ineffectieve) virtuele registers.
  • De machine-afhankelijke linker-optimizer wijst geheugen toe tussen registers en planningsinstructies. Converteert een assembler-programma naar een echte assembler met goed gebruik van pipelining.

Daarnaast worden subsystemen voor foutdetectie en een symbooltabelmanager gebruikt.

Lexicale analyse (scannen)

De scanner zet de stroom tekens uit de broncode om in een stroom tokens, waarbij spaties, opmerkingen en uitvouwbare macro's worden verwijderd.

Scanners stuiten vaak op problemen, zoals het al dan niet rekening houden met hoofdletters, inspringingen, regelinvoer en geneste opmerkingen.

Fouten die tijdens het scannen kunnen optreden, worden lexicale fouten genoemd en omvatten:

  • tekens die niet in het alfabet voorkomen;
  • het aantal tekens in een woord of regel overschrijden;
  • geen gesloten teken of letterlijke tekenreeks;
  • einde van bestand in commentaar.

Ontleden (ontleden)

De parser zet een reeks tokens om in een abstracte syntaxisboom. Elk knooppunt in de boom wordt opgeslagen als een object met benoemde velden, waarvan vele zelf boomknooppunten zijn. Er zijn geen lussen in dit stadium. Bij het maken van een parser moet u letten op de complexiteit van de grammatica (LL of LR) en nagaan of er regels zijn voor het ondubbelzinnig maken. Sommige talen vereisen wel semantische analyse.

Fouten die in dit stadium worden aangetroffen, worden syntactische fouten genoemd. Bijvoorbeeld:

  • k = 5 * (7 - y;
  • j = / 5;
  • 56 = x * 4.

semantische analyse

Tijdens het proces is het noodzakelijk om de toelaatbaarheidsregels te controleren en delen van de syntaxisboom te koppelen (oplossen van naamreferenties, invoegen van bewerkingen voor impliciete typeconversie, enz.) om een ​​semantische grafiek te vormen.

Uiteraard verschilt het geheel van ontvankelijkheidsregels van taal tot taal. Als Java-achtige talen worden gecompileerd, kunnen vertalers het volgende vinden:

  • meerdere declaraties van een variabele binnen zijn bereik;
  • verwijzingen naar een variabele vóór zijn declaratie;
  • links naar een niet-aangegeven naam;
  • schending van toegankelijkheidsregels;
  • te veel of onvoldoende aantal argumenten bij het aanroepen van de methode;
  • type komt niet overeen.

Generatie

Tussenliggende codegeneratie produceert een stroomdiagram dat is samengesteld uit tupels die zijn gegroepeerd in basisblokken.

Codegeneratie produceert echte machinecode. In traditionele compilers voor RISC-machines creëert de eerste fase een assembler met een oneindig aantal virtuele registers. Voor CISC-machines zal dit waarschijnlijk niet gebeuren.