Hogyan készítsünk saját programozási nyelvet egy egyszerű programozási nyelv írása a semmiből-1. rész

posted in: Articles | 0

Ha fejlesztő vagy, programozási nyelveket használt. Fantasztikus módja annak, hogy egy számítógép azt tegye, amit akar. Lehet, hogy még mélyre is süllyedt, összeszerelésre vagy gépi kódra programozva. Sokan soha nem akarnak visszatérni. De néhány csoda, hogyan lehet kínozni magam jobban csinál több alacsony szintű programozás? Többet akarok tudni arról, hogyan készülnek a programozási nyelvek!, Minden viccet félretéve, egy új nyelv írása nem olyan rossz, mint amilyennek hangzik, tehát ha még enyhe kíváncsiságod is van, azt javaslom, maradj körül, és nézd meg, miről van szó.

Ez a bejegyzés célja, hogy egy egyszerű merülés, hogy egy programozási nyelv lehet tenni, és hogyan lehet, hogy a saját speciális nyelv. Talán még név után magad. Ki tudja.

arra is fogadok,hogy ez hihetetlenül ijesztő feladatnak tűnik. Ne aggódj, mert ezt figyelembe vettem. Mindent megtettem, hogy mindent viszonylag egyszerűen elmagyarázzak anélkül, hogy túl sok érintőt folytatnék., A végén ezt a bejegyzést, akkor képes lesz arra, hogy saját programozási nyelv (lesz néhány rész), de van még. Tudva, hogy mi folyik a motorháztető alatt, jobb lesz a hibakeresésben. Jobban megérted az új programozási nyelveket, és hogy miért hozzák meg a döntéseket. Lehet, hogy van egy programozási nyelved, amit magadról neveztek el, ha ezt korábban nem említettem. Is, ez nagyon szórakoztató. Legalábbis nekem.

fordítók és tolmácsok

programozási nyelvek általában magas szintű. Ez azt jelenti, hogy nem a 0s és az 1s-t nézed, sem a nyilvántartásokat, sem az összeszerelési kódot., De, a számítógép csak akkor érti 0s és 1s, így szüksége van egy módja annak, hogy mozog, amit olvasni könnyen, hogy mit tud olvasni a gép könnyen. Ezt a fordítást összeállítással vagy értelmezéssel lehet elvégezni.

Az összeállítás a forrásnyelv teljes forrásfájljának célnyelvvé alakításának folyamata. A mi céljainkra, gondolkodunk összeállítása le a vadonatúj, korszerű nyelv, egészen a futtatható gépi kód.,

célom az, hogy a” mágia ” eltűnjön

értelmezés a kód végrehajtása egy forrásfájlban többé-kevésbé közvetlenül. Hagyom, hogy azt hidd, ez varázslat erre.

Szóval, hogyan megy a könnyen olvasható forrásnyelvről a nehezen érthető célnyelvre?

A Fordító fázisai

a fordító különböző módon felosztható fázisokra, de van egy módja annak, hogy ez a leggyakoribb., Csak kis mennyiségű értelme van, amikor először látja, de itt megy:

Hoppá, rossz diagramot választottam, de ez meg fog tenni. Alapvetően megkapja a forrásfájlt, olyan formátumba helyezi, amelyet a számítógép akar (fehér hely eltávolítása stb.), változtassa meg valami olyanra, amelyben a számítógép jól mozoghat, majd generálja a kódot ebből. Többről van szó. Ez egy másik alkalommal, vagy a saját kutatás, ha a kíváncsiság megöli.,

Lexikális Elemzés

más NÉVEN”, Hogy a forráskód elég”

Fontolja meg a következőket teljesen kitalált nyelv, ami gyakorlatilag csak egy számológép pontosvesszővel:

 // source.ect 3 + 3.2; 5.0 / 1.9; 6 * 2;
írja Be a teljes képernyős mód Kilépés a teljes képernyős mód

A számítógép nem kell az összes. A terek csak a kicsinyes elménknek szólnak. És új sorok? Senkinek nincs szüksége ezekre. A számítógép bekapcsolja ezt a kódot, hogy látsz egy patak tokenek, hogy tudja használni, hanem a forrás fájlt., Alapvetően tudja, hogy a 3egy egész szám, 3.2egy float, és + valami, ami a két értéken működik. Ez minden, amit a számítógépnek meg kell szereznie. A lexikális elemző feladata, hogy ezeket a tokeneket forrásprogram helyett biztosítsa.

hogyan működik ez valóban nagyon egyszerű: adjon a lexernek (kevésbé hatásvadász hangzású módszer a lexikai elemző mondására) néhány dolgot, amire számíthat, majd mondja meg, mit kell tennie, ha látja ezt a dolgot. Ezek az úgynevezett szabályok., Íme egy példa:

int cout << "I see an integer!" << endl;
írja be a teljes képernyős módot Kilépés a teljes képernyős módból

amikor egy int jön át a lexeren, és ezt a szabályt végrehajtják, akkor egy teljesen nyilvánvaló “látok egy egész számot!”felkiáltás. Nem így fogjuk használni a lexert, de hasznos látni, hogy a kód végrehajtása önkényes: nincsenek szabályok, hogy valamilyen objektumot kell készítenie, és vissza kell adnia, ez csak a szokásos régi kód. Akár több vonalat is használhat, ha fogszabályzóval körülveszi.,

egyébként egy úgynevezett FLEX-et fogunk használni a lexing elvégzéséhez. Ez megkönnyíti a dolgokat, de semmi sem akadályozza meg abban, hogy csak olyan programot készítsen, amely ezt maga végzi.

ahhoz, hogy megértsük, hogyan fogjuk használni flex, nézd meg ezt a példát:

Ez bevezet néhány új fogalmak, így menjünk át őket:

%% használják külön szakaszok a .lex fájl. Az első rész deklarációk-alapvetően változók, hogy a lexer olvashatóbb legyen., Itt importál, %{ és %}.

A második rész a szabályok, amelyeket korábban láttunk. Ezek alapvetően egy nagy if else if blokk. Végrehajtja a vonalat a leghosszabb mérkőzéssel. Így még akkor is, ha megváltoztatja az úszó és az int sorrendjét, az úszók továbbra is megegyeznek, mivel a 3.2 3 karakterének megfelelő 3több mint 1 karaktere., Vegye figyelembe, hogy ha ezen szabályok egyike sem illeszkedik, akkor az alapértelmezett szabályra kerül, egyszerűen nyomtatva a karaktert a szabványosításhoz. Ezután a yytext hivatkozhat arra, amit látott, hogy megfelel a szabálynak.

harmadik rész a kód, amely egyszerűen c vagy C++ forráskód fut a végrehajtás. yylex(); egy függvényhívás, amely fut a lexer. Azt is, hogy olvasni bemenet egy fájlból, de alapértelmezés szerint beolvassa a szabványos bemenet.

mondja, hogy ezt a két fájlt source.ect és scanner.lex., Létrehozhatunk egy C++ programot a flex paranccsal (mivel flex telepítve van), majd lefordíthatjuk ezt, és beírhatjuk a forráskódunkat, hogy elérjük a félelmetes nyomtatási nyilatkozatokat. Tegyük ezt működésbe!

Hey, cool! Csak C++ kódot írsz, amely megfelel a szabályoknak, hogy valamit megtegyél.

most, hogyan használják ezt a fordítók? Általában ahelyett, hogy valamit nyomtatna, minden szabály visszaad valamit-egy tokent! Ezeket a tokeneket a fordító következő részében lehet meghatározni…,

szintaxis Analyzer

AKA “így szép forráskód használható”

itt az ideje, hogy érezd! Amint ideérünk, elkezdjük meghatározni a Program szerkezetét. Az értelmező éppen most kapott egy adatfolyamot a tokenek, és meg kell egyeznie elemek ebben a folyamatban annak érdekében, hogy a forráskód szerkezete, amely használható. Ehhez grammarokat használ, az a dolog, amit valószínűleg látott egy elmélet osztályban,vagy hallotta, hogy furcsa barátja geeking ki. Hihetetlenül erősek, és annyi mindenbe bele lehet menni, de megadom, amit tudnod kell a mi hülye elemzőnk számára.,

alapvetően a grammarok nem terminális szimbólumokat egyeznek meg a terminális és nem terminális szimbólumok egyes kombinációival. A terminálok a fa levelei,a nem terminálok gyermekei. Ne aggódj, ha ennek nincs értelme, a kód valószínűleg érthetőbb lesz.

egy bölény nevű elemző generátort fogunk használni. Ezúttal, majd szét a fájlt szakaszokra magyarázat céljából. Először is, a nyilatkozatok:

az első résznek ismerősnek kell lennie: olyan dolgokat importálunk, amelyeket használni akarunk. Ezután egy kicsit trükkösebb lesz.,

az Unió egy “valódi” C++ típusú leképezés arra, amit a program során fogunk hívni. Tehát, ha azt látjuk, hogy intVal, akkor cserélje ki, hogy a fejedben int, és ha azt látjuk, hogy floatVal, akkor cserélje ki, hogy a fejedben float. Majd később meglátod,miért.

ezután eljutunk a szimbólumokhoz. Ezeket a fejedben terminálokként és nem terminálokként oszthatod meg, mint a korábban beszélt grammarokkal. A nagybetűk terminálokat jelentenek, így nem tovább bővülnek., Kisbetűs azt jelenti, nem terminálok, így továbbra is bővíteni. Ez csak egy egyezmény.

minden nyilatkozat (kezdve %) deklarál valamilyen szimbólumot. Először azt látjuk, hogy egy nem terminállal kezdjük program. Ezután meghatározunk néhány zsetont. A<> zárójelek határozzák meg a visszatérési típust: így aINTEGER_LITERAL terminálintValértéket ad vissza. ASEMI terminál semmit sem ad vissza., Hasonló dolgot lehet tenni a nem terminálokkal a typehasználatával, amint az a expmeghatározásakor látható, mint egy nem terminál, amely floatVal.

végül elsőbbséget élvezünk. Ismerjük a PEMDÁKAT, vagy bármilyen más rövidítést, amelyet esetleg megtanultál, ami néhány egyszerű elsőbbségi szabályt mond: a szorzás az összeadás előtt jön, stb .. Most, kijelentjük, hogy itt egy furcsa módon. Először is, az alacsonyabb a listán magasabb elsőbbséget jelent. Másodszor, kíváncsi lehet, hogy mit jelent a left., Ez asszociativitás: nagyjából, ha van a op b op c, akkor a és b menjen együtt, vagy talán b és c? A legtöbb operátorunk az előbbit csinálja, ahol a és b először együtt: ezt nevezzük bal asszociativitásnak. Egyes operátorok, mint például az exponencia, az ellenkezőjét teszik: a^b^celvárja, hogy növelje b^cmajd a^(b^c). Ezzel azonban nem foglalkozunk., Nézze meg a bölény oldalt, ha további részleteket szeretne.

Oké, valószínűleg elég unatkoztam a nyilatkozatokkal, itt vannak a nyelvtani szabályok:

Ez a nyelvtan, amiről korábban beszéltünk. Ha nem ismeri a nyelvtanokat, akkor nagyon egyszerű: a bal oldal a jobb oldalon lévő dolgok bármelyikévé válhat, elválasztva a | (logikai or). Ha több útvonalon is le tud menni, akkor ez egy nem-nem, ezt kétértelmű nyelvtannak nevezzük., Ez nem kétértelmű, mert a elsőbbségi nyilatkozatok – ha megváltoztatjuk úgy, hogy Plusz már nem maradt asszociatív, hanem deklaráljuk, mint egy token mint SEMI, azt látjuk, hogy kapunk egy shift/reduce conflict. Szeretne többet tudni? Nézze meg, hogyan működik a bölény, tipp, LR elemzési algoritmust használ.

Oké, tehát exp az egyik ilyen eset lehet: an INTEGER_LITERAL, a FLOAT_LITERAL stb. Megjegyzés Ez is rekurzív, így a exp két explehet., Ez lehetővé teszi számunkra, hogy komplex kifejezéseket használjunk, mint például a 1 + 2 / 3 * 5. Mindegyik exp, ne feledje, visszaadja az úszótípust.

ami a zárójelben van, ugyanaz, mint amit a lexerrel láttunk: tetszőleges C++ kód, de furcsa szintaktikai cukorral. Ebben az esetben speciális változók vannak előkészítve a $. A $$ változó alapvetően visszatér. $1 az, amit az első argumentum ad vissza, $2 amit a második ad vissza, stb., Az “argumentum” alatt a nyelvtani szabály egyes részeit értem: tehát a exp PLUS exp szabálynak 1 exp argumentuma, 2 PLUS argumentuma, 3 exp. Tehát a kód végrehajtásában hozzáadjuk az első kifejezés eredményét a harmadikhoz.

végül, ha visszatér a program nem terminálhoz, kinyomtatja a nyilatkozat eredményét. A program ebben az esetben egy csomó kijelentés, ahol a kijelentések egy kifejezés, amelyet pontosvessző követ.

most írjuk be a kódrészt., Ez az, ami valójában fut, amikor átmegyünk az elemzőn:

Oké, ez kezd érdekessé válni. Fő funkciónk most az első argumentum által biztosított fájlból olvasható, nem pedig a standard in-ből, és hozzáadtunk néhány hibakódot. Ez elég magától értetődő, és a kommentek jó munkát végeznek, hogy elmagyarázzák, mi folyik itt, ezért az olvasóra hagyom, hogy kitalálja ezt. Csak annyit kell tudni, hogy most visszatértünk a lexerhez, hogy megadjuk a tokeneket az elemzőnek! Itt van az új lexer:

Hé, ez valójában kisebb most!, Azt látjuk, hogy nyomtatás helyett terminálszimbólumokat adunk vissza. Ezek közül néhány, például ints és floats, először beállítjuk az értéket, mielőtt továbblépnénk (yylval a terminál szimbólum visszatérési értéke). Más, mint hogy, ez csak így az elemző egy patak terminál tokenek használni saját belátása szerint.

Cool, lehetővé teszi futtatni, akkor!

ott megyünk – elemzőnk kiírja a megfelelő értékeket! De ez nem igazán fordító, csak C++ kódot futtat, amely végrehajtja azt, amit akarunk. Ahhoz, hogy egy fordító, azt akarjuk, hogy kapcsolja be ezt a gépi kódot., Ehhez még egy kicsit hozzá kell adnunk…

A következő alkalommal…

most rájöttem, hogy ez a bejegyzés sokkal hosszabb lesz, mint gondoltam, ezért gondoltam, hogy itt fejezem be ezt. Alapvetően van egy működő lexer és elemző, tehát ez egy jó megállási pont.

a forráskódot a Githubomra helyeztem, ha kíváncsi vagy a végtermék megtekintésére. Ahogy több hozzászólás jelenik meg, ez a repo több tevékenységet fog látni.,

mivel a lexer és elemző, most már generálni egy köztes ábrázolása a kódot, hogy végre átalakítható valódi gépi kód, és megmutatom, hogy pontosan hogyan kell csinálni.

további források

ha véletlenül több információt szeretne az itt tárgyalt dolgokról, összekapcsoltam néhány dolgot az induláshoz. Sok mindenen mentem keresztül, így itt az esélyem, hogy megmutassam, hogyan kell belemerülni azokba a témákba.

Ó, egyébként, ha nem tetszett a fordító fázisai, itt van egy tényleges diagram. Még mindig kihagytam a szimbólumtáblát és a hibakezelőt., Azt is vegye figyelembe, hogy sok diagram különbözik ettől, de ez a legjobban azt mutatja, hogy mi foglalkozunk.

Vélemény, hozzászólás?

Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük