Egy egyszerű fordító
Egy egyszerű fordító
A tervezési fordítóprogramok írt könyvet. A következő szöveg nem helyettesíti őket, de remélhetőleg biztosítja alapvető ismereteket a szerkezet az fordítóprogramok. Amikor elhatároztam, hogy írok egy fordító, próbáltam egyszerre megvalósítani egy csomó lehetőséget, sokkal több, mint amire szükség van, hogy írjon a fordító is. Ez lehetséges, de a kapott fordító tartalmaz sok részletet, hogy nehéz megérteni. A részleteket kapcsolódó nyelvi képességeket, a másik része - a processzor 8086 (80386 kényelmes, de akkor nem tudtam, és 32 bites operációs rendszerek nem terjedt). És persze, sokkal lehetett volna jobban csinálni. Egy bizonyos ponton, összementekről a fordító, de a nagyon fiatal, ő nem. Jelentősen csökkenti a fordító érhető egyszerűsítésével a beviteli nyelvet, hogy itt történik. Van itt egy játék fordító gyakorlati célokra nem alkalmas, de van beépítve, ugyanúgy, mint az igazi.
901 FORMAT (1X, 'N =', I2 /) N = 0 10 DO 20 I = 1,5 N = N + január 20 CONTINUE write (6901) N STOP END
Fejes ciklus összhangban a címkén 10 elgépelés (dot helyett vessző a billentyűzeten közel vannak), de úgy véli, Fortran, hogy ezt a feladatot egy állandó 1.5 változó nevű DO20I. Ennek megfelelően, a nyomtatás az 1. számú és az 5 nem fog megjelenni.
Nem csak a szkenner, de a fordító egészében függ a forrás nyelv, így a legjobb, hogy menjen a saját, és mérlegelje meghatározott nyelv - Háttér.
Program háttere nyelv egy sor meghatározások:
- állandók
- típusok (struktúrák)
- globális változók
- függvény
relatív pozícióját a program csak egyetlen feltétel - minden tárgy előtt kell definiálni azt használják, a program befejezi a fő funkciója. Egyes tárgyak, például tartalmaz egy obyavneniya elég, de a minimális verziója akkor nem lehet végrehajtani.
Meghatározása állandók, szerkezete és működése a legfontosabb raspoznaetmsya az első szót (meghatározza, struct, és kezdjük esetében), és a meghatározása a funkció kezdődik a globális változó nevét és típusát eltérnek egymástól jelenlétében és távollétében zárójelben a név után. Ie neve után a típus szükséges prochital @ szimbólumot (ha van ilyen), a név maga és az azt követő szó róla. Ha ez a szó - zárójelben meg kell elemezni funkciót, vagy - elemzése a globális változót. Más nyelvekben a funkció határozza meg az első szó a Pascal, például a funkció elindítja a szó funkciót, és a változók listája a szó var.
Így a felső szintű fordító állhat egy egyszerű ciklus:
míg TRUE do Scan (@Buff); válasszuk az esetben strcmp (@Buff "kezdődik") = 0: // elemezni a fő funkciója kilépés esetén strcmp (@Buff "határozza meg") = 0: // Parse esetben strcmp állandók (@Buff "struct") = 0: Elemzési // alapértelmezett szerkezetek: // Feldolgozási funkció vagy globális változó end end
Természetesen az egyes ágak select'a tartalmaznia kell egy bizonyos kódot. Elemzési állandók és szerkezetek meglehetősen egyszerű (különösen a kibocsátott rekurzív felépítése - ellentétben Pascal azok meghatározásai nem ágyazható), az eredmény elemzés az új bejegyzést a táblázatban a globális nevek, nincs kód generálódik. Elemzése bonyolultabb funkciókat. Ez egy fejléc elemzésekor paraméterek listáját (ami nagyon hasonlít a struktúrájának elemzése) és analízise benne a funkciók az üzemeltető. Ha a Ctrl funkció van írva, fordítása egyetlen szolgáltató, összeállítása az egész funkció csökkent a sorozat három sor:
míg strcmp (@Scan (@Buff), "vége") = 0 do Ctrl (@Buff) !; vég
További azonban meg kell változtatni ezt a ciklust és a Ctrl funkció:
// Scan (@Buff); míg strcmp (@Buff, "vége") = 0 do Ctrl (@Buff) !; // Ctrl el kell olvasnia a következő nyilatkozatot a szó végére
szó F (); szót G () F (); vég
Miután a záró zárójel tud felelni pontosvesszővel (F), vagy az elején az első operátor funkció (G). Miután elolvasta a záró zárójel szó után kell figyelembe venni, és ha ez nem egy pontosvessző, emlékszik rá, és lépni a nyilatkozat összeállítása, a második megvalósítási van kialakítva, hogy ezt a ciklust. Megváltoztatja a nyelvet, akkor vissza az első lehetőség:
szó F (); szót G () jelentése F (); vég
Itt, miután a záró zárójel mindig egy szó (vagy egy pontosvessző).
Minden nyilatkozatokat, amelyek felmerülhetnek a funkció által elismert első szó, így a felső szinten Ctrl lehet elhelyezni az alábbiak szerint:
Használata Expr ciklus feldolgozása közben funkció az alábbiak szerint végezzük:
// kódja generációs @A: nop; a ciklus kezdetén // hívás funkció Expr // ha az eredményeket zagpuzka Al // kód termelési vagy AL, AL // JNZ @B // JMP. // @B: nop míg strcmp (@Buff, "vége") = 0 do Ctrl (@Buff) ;! vége // generációs kód JMP @A @C: nop // alkalmazkodási átmeneti JMP. (Jmp @C)
Ezek nem minden szükséges lépést is végre kell hajtania a készítmények összeállításának hurok / kilépési üzemeltetők és az elemzés egyes cikluson belül a lokális változók. De ez egy részlet, a lényeg az, hogy a ciklus függvényében tartalmaz egy operátor szekvencia, és a funkciót is Ctrl lehet használni fordítani őket ugyanúgy, ahogy összeállításakor a teljes funkció.
Hurok feltétel nem összeállítani a legjobb módja. Legalább a csapat generált felesleges (vagy AL, AL.), De most az egyszerűség sokkal fontosabb, mint a hatékonyság. Szintén ez a töredék nem egyértelmű, mivel kell összeállítani egy komplex állapot, amely tartalmazza a logikai üzemeltetők és / vagy, akkor töltsön ki egy rövid értékelést a feltételeit, vagy ha végre. Ezt a problémát meg kell oldani írásban Expr funkciót. Úgy tűnik, a teljes számítást nyilvántartásból tárolja az eredményt a legegyszerűbb, az egyes nyelvek (például az eredeti Pascal) és kész. Ez úgy történik, és a kibocsátott, de nem ez a legjobb megoldás.
void Expr (szó PRTY; char @Buff; OpInfo @ Op1) válasszuk az esetben strcmp (@Buff "(") = 0: // vyradenie esetén strcmp zárójelben (@Buff, "„") = 0: // karakter konstans esetében strcmp (@Buff, "#") = 0: // karakter konstans esetén isdigit (Buff) = 0: // numerikus konstans alapértelmezett: // változó, függvényhívás végéig, míg TRUE szövegszerkesztést be; szó P2; válasszuk esetben strcmp (@Buff, "|") = 0: Bejelentkezés = OR; P2 = 1; esetben strcmp (@Buff, "") = 0: Bejelentkezés =; és P2 = 1; esetben strcmp (@Buff, "
Érdemes megjegyezni, hogy ez a funkció van írva Expr mindig szól felesleges szó. Ez lehet egy pontosvessző, kettőspont, majd tegye.
A kialakított eszmék és ez hogyan valósulnak meg a „játék” compiler. Egyszerűsítés el elsősorban egyszerűsítésével a nyelvet. Csak egy adattípus - egész (de a feltételek minősülnek logikai), az azonosító az új típusú nem lehetséges, csak egydimenziós tömbök, függvények és nincs helyi változók, nincs utalás változó. És minden olyan kísérletet, hogy optimalizálja. Valójában fordító kezdődik a Read funkció:
meghatározzák @emNOMEMORY „Out of Memory” határozza @emSIZE „Túl hosszú identifikatop” határozza @emEOF „File End” határozza @emNUMBER „Hiba a konstans” határozza @emNAME „Ppopuscheno neve” határozza @emEMPTY „üres tömböt” határozza @emBRACKET „Ppopuschena konzol "határozza @emCOMMA" Ppopuschena vessző "határozza @emSEMICOLON" Ppopuschena pontosvessző "határozza @emTHENEXP" Ppopuscheno majd "határozza @emDOEXP" Ppopuscheno do "határozzák @emDUP" Povtopnoe leírása "határozza @emLVALUE" Ppopuschena változók "határozza @emTYPE" mismatch típus "meghatározása @emUNDEFINED" változókat nem határozza meg a "define @emUNDEFOPR" opepatsy nem határozza meg a "define @emASSIGN" Ppopuscheno = „define tbSIZE 8192 meghatározzák dbSIZE 8192 meghatározzák dtSIZE 128 meghatározzák idSIZE 16 meghatározzák opor meghatározzák opAND 1 de 2 finom opLT 3 határozzák Ople 4 meghatározzák opEQ 5 meghatározzák opNE 6 meghatározzák opGE 7 meghatározzák opGT 8 meghatározza opADD 9 meghatározzák opSUB 10 meghatározzák opMUL 11 meghatározzák opDIV 12 meghatározzák opNOT 13 meghatározzák opNEG 14 meghatározzák ptZERO 0 meghatározzák ptBOOL 1 meghatározza ptCOMP 2 meghatározza ptADD 3 határozzák ptMUL 4 meghatározni ptLVALUE 5 meghatározzák ttWORD 0 meghatározzák ttBOOL 1 struct ADATOK char neve [idSIZE]; szó OFS; szó Index; end ADATOK [dtSIZE]; szó nData; szó OFS; char szöveg [tbSIZE]; szó hText; szó ntext; szó pText; char neve [128]; szó vonal; szó Label; char Dest [dbSIZE]; szó hDest; szó pDEST; érvényteleníti @Ptr (szó Seg, OFS) void @ P1 = @ OFS; érvényteleníti @@ P2 = @ P1; visszatérés @ P2; end szó isalpha (char Ch), ha ( 'A' 0, akkor december S; vége szót P = 0; ha n> = 10 | S> 0, akkor P = str (N / 10, S, @ Buff); end char @ D = "0123456789"; Buff [P] = D [N% 10]; visszatérő P + 1; véget char @Str (szó N; szó S) char @ P = "00000"; P [str (N, S, @P)] = # 0; return @P; vége void stop (char @EM) putc (# 13); helyezi (@name); putc ( '('); helyezi (@Str (line, 0)); putc ( ')'), ha (strlen (@EM)> 0), akkor minden kimenet ( ":"); helyezi (@EM); vége közel van (hDest); bezár (hText); ASM mov ax, 4C00H ASM int 21H end szó Val (char @Buff) char @D = "0123456789"; szó P = 0; szót L = 0; szót H = 0, míg Csiszolási [P] = # 0 do szó S = 0 ;! míg a D [S !] = Csiszolási [P] do inc S; ha S> = 10 majd megáll (@emNUMBER); end end S = 10 * L + S; L = S% 256; S = 10 * H + S / 256; H 256 = S%; S = S / 256; ha S> 0, akkor a stop (@emNUMBER); end növ P; szélső 256 * H + L; vége char read (), ha pText> = ntext majd ntext = olvasni (hText @ szöveg, tbSIZE), ha ntext = idSIZE majd stop (@emSIZE); end Next (); end ha P = 0, akkor Buff [P] = Read (); inc P; Tovább (); válasszuk az esetben Buff [0 ] = '': ha Read () = '=' majd a Next (); return @strcpy (@Buff, "> ="); end end end Csiszolási [P] = # 0; visszatérés @Buff; véget void Save (char Ch) ha pDEST> = dbSIZE majd Stop (@emNOMEMORY); véget Dest [pDEST] = CH; inc pDEST; véget void Decl (char @Inst) szót I = 0; míg Inst [I] = # 0 do mentése (Inst [I]) !; inc I; véget Save (# 13); Save (# 10); véget void Code (szó l; char @Inst) ha L = 0, akkor a Mentés ( '@') !; char @ P = @ Str (L, 5); szót i = 0; míg a P [I] = # 0 do mentése (P [I]) !; inc I; véget Save ( ':'); Save ( ''); más szóval I = 0; míg én = nData majd megáll (@emUNDEFINED); végén, ha adatok [N] .Index> 0, akkor, ha strcmp (@Scan (@Buff), "[") = 0, akkor a Stop (@emBRACKET) !; végén, ha Expr (ptZERO, @ Scan (@Buff)) = ttWORD majd megáll (@emTYPE) !; végén, ha strcmp (@Buff, "]") = 0, akkor a Stop (@emBRACKET) !; ér véget, ha PRTY
= PtLVALUE akkor ha Flag = 0, akkor a Stop (@emLVALUE); szélső N; end while true do szó Op; szót P; válasszuk az esetben strcmp (@Buff, "|") = 0: Op = opor; P = ptBOOL; esetben strcmp (@Buff, "") = 0: Op = opAND; P = ptBOOL; esetben strcmp (@Buff, "=") = 0: Op = opGE; P = ptCOMP; esetben strcmp (@Buff, ">") = 0: Op = opGT; P = ptCOMP; esetben strcmp (@Buff, "+") = 0: Op = opADD; P = ptADD; esetben strcmp (@Buff, "-") = 0: Op = opSUB; P = ptADD; esetben strcmp (@Buff, "*") = 0: Op = opMUL; P = ptMUL; esetben strcmp (@Buff, "/") = 0: Op = opDIV; P = ptMUL; alapértelmezett: P = ptZERO; végén, ha P = nData majd megáll (@emUNDEFINED); végén, ha strcmp (@Buff, "=") = 0, akkor a Stop (@emASSIGN) !; végén, ha adatok [N] .Index> 0, akkor kód (0, "push AX"); végén, ha Expr (ptZERO, @ Scan (@Buff)) = ttWORD majd megáll (@emTYPE) !; végén, ha adatok [N] .Index> 0, akkor kód (0, "pop BX"); Kód (0, "shl BX, 1"); Kód (0, @ strcat (@strcat (@strcpy (@Buff, "mov DS: [BX +"), @ Str (Adatok [N] .Ofs, 0)), "], AX")); mást Kód (0, @ strcat (@strcat (@strcpy (@Buff, "mov DS: ["), @ Str (Adatok [N] .Ofs, 0)), "], AX")); end end end kezdődik byte @ Size = @ Ptr (GetPSP (), 128); char @ Parm = @ Ptr (GetPSP (), 129); char neve [128]; szót i = 0; míg én = dtSIZE majd megáll (@emNOMEMORY); végi adatok [nData] .Ofs = 2 * OFS; strcpy (@Data [nData] .name, @ Buff); Az adatok [nData] .Index = 0; szót N = 1; ha strcmp (@Scan (@Buff), "[") = 0, akkor N = Val (@Scan (@Buff)); ha N
A bemeneti / kimeneti eszközök végrehajtását egyetlen szolgáltató írási, kimenetre a nyomtatási szolgáltató és a számok üres végez soremeléssel. Annak bizonyítására, akkor futtathatunk a következő tömb rendezési program:
szót Buff [16]; szó Temp; szót; szó J; szót N; kezdeni N = 10; I = 0; 0, míg én I = I-1; J = 0; míg a J Buff [J + 1], majd Temp = Csiszolási [J + 1]; Buff [J + 1] = Csiszolási [J]; Buff [J] = Temp; end J = J + 1; end end blank I = 0; míg én
Teljes verzió a fordító nehezebb, de ez tette az egyszerűsítés. Ezek a kapcsolódó kötelező elhelyezése az operandusok a nyilvántartásokban, és ez az elrendezés fix:
- minden apifmeticheskie és logikai opepatsii végre csak nyilvántartások
- pe.pvyy pazmeschaetsya operandus egy terül AL (byte), AX (word) vagy DX: AX (dupla szó)
- vto.poy pazmeschaetsya operandus regiszter BL (bájt), BX (szó), vagy CX: BX (dupla szó)
- Lásd Eredmények opepatsii pazmeschaetsya a nyilvántartásokban AL (byte), AX (word) vagy DX: AX (dupla szó)
- DI regiszter kezelésére használható akarat tömb elem
- nyilvántartások ES: DI használt mutatók pazimenovaniya
- ha szükséges nyilvántartás nem áll rendelkezésre, az értékét a verembe vpemenno
- Variációk Függvényhívások PARAMÉTEREK pepedayutsya chepez verem PARAMÉTEREK távolítani a stack peped zavepsheniem funkciók következtetéseket rutin visszatér a nyilvántartások AL (byte), AX (word) vagy DX: AX (dupla szó) annak érdekében, upposcheniya Lásd Eredmények funkció hosszúsága csak 1, 2 vagy 4 bájt.
- A globális változók találhatók a szegmens regiszterek DS adpesuemom
Basic fordító blokkolja a következő:
érvényteleníti a Stop (char @EM) // Zavepshenie összeállítása. véget void Code (szó l; char @S) // A kialakulását a sor kódot. véget char Read () // Read karaktert. véget void Next () // pepehod a következő karaktert. véget char @Scan (char @Buff) // olvassa el a szavakat. end void Init () // inicializálja (a Expr). véget void push (szó R) // bejegyzés nyilvántartás a verem. véget void Pop (szó R) // Kivonat a verem. véget void LDAX (OpInfo @ Op1) // Zagpuzka első operandus (DX :) AX. véget void LDBX (OpInfo @ Op1) // Zagpuzka a második operandus a (CX :) BX. véget void LPTR (OpInfo @ Op1) // Zagpuzka mutatót ES: DI. véget void STAX (OpInfo @ Op1) // Rekord eredményeket lásd a memóriában. end szó Comp (szó méret; char @Jump) // spavneniyu. véget void Test (szó pType1; szó nPtr1; OpInfo @ Op2) // ellenőrzése. // kompatibilitási típusok a végén void Expr (szó P; char @Buff; OpInfo @ Op1) // Fordítsuk. // expressziós vége void Ctrl (char @Buff) // Fordítsuk blokk. végén kezdődik. while (strcmp (@Scan (@Buff), "kezdődik")! = 0) do if (strcmp (@Buff "határozza meg") = 0), akkor // Pazbop állandó. hurok végén, ha (strcmp (@Buff, "struct") = 0), akkor // Pazbop struktúrák. hurkolt végét P = FindType (@Buff); if (P
Meg kell mondani néhány szót a fordító módosítását. Made in ez a hiba nem fordulhat elő, miután egy összeállítás. Ie Ha használ egy fordító lefordítja a szöveget fordítóprogram „(a korrigált és frissített A), és így nem ismeri fel szintaktikai vagy szemantikai hiba fordítóprogram” valószínűleg tudta összeállítani, hanem a kapott fordítóprogram 'lehet kivitelezhetetlen.
Még egy utolsó megjegyzés. A fordító generál egy listát, kicsit különbözik a célja az assembly TASM. Ha a használni kívánt TASM / TLINK, az elején meg kell adni a húr
CODE szegmens feltételezzük CS: CODE org 100H @ 00001: nop
CODE végződik végén @ 00001
Honlap létre uCoz rendszerben