UPE - vnitřek kompilátoru


Štěpán Roh

Verze 0.1
Revize dokumentu 1.0 (20.8.2000)

Obecný popis překladu

Překládaný program nejprve prochází lexikálním analyzátorem, který jej rozloží na sadu lexikálních elementů, tzv. tokenů. Zároveň odstraňuje komentáře a provádí inkluzi souborů, to vše bez vědomí navazujících čístí kompilátoru. Tokeny jsou zpracovány parserem, který na základě gramatiky zdrojového textu konstruuje derivační strom a buduje tabulky symbolů. Při té příležitosti provádí některé kontroly, jako např. kontrolu parametrů, které je třeba vyhodnotit bezprostředně. Celý derivační strom (to není tak docela pravda, viz níže) je pak postoupen generátoru kódu, který generuje výsledný text v C. Rozdělení na přední a zadní část kompilátoru není tak docela rigidní, protože většina sémantických chyb je hlášena až generátorem kódu. To je částečně způsobeno odloženou kontrolou symbolů, jelikož jazyk popisující procesor nevyžaduje deklaraci symbolu před jeho použitím.

V dalších oddílech jsou popsány nejdůležitější části kompilátoru podrobněji, detialy je možno najít ve zdrojových kódech, je tam dostatečné množství komentářů.

Lexikální analyzátor

Lexikální analyzátor je napsán ve flexu(1) a je celý v souboru upecc.l. Není nijak napojen na parser, což trochu ztěžuje práci parseru. Nicméně zvládá vlastními silami inkluzi souborů včetně uchování jmen a řádků pro chybové hlášení (viz zdrojový text).

Parser

Parser je napsaný v bisonu(1) (pozor, zdrojový text neodpovídá pravidlům staršího yaccu(1)). Celý je v souboru upecc.y. Funkce pro práci s tabulkami symbolů jsou v souborech upecc_ids.h a upecc_ids.c. Funkce pro práci s derivačními stromy jsou v souborech upecc_tree.h a upecc_tree.c. Jelikož parser zpětně neovlivňuje lexikální analyzátor, tak je gramatika občas trochu chudší nebo nejednoznačná (zřejmě). Výstupem parseru je globální tabulka symbolů global_id_table, strom s definicemi instrukcí upecc_instructions a některá globální nastavení jako upecc_byte_size, upe_byte_size, upe_byte_order, upecc_num_len, upecc_ip_reg a další. Z globální tabulky symbolů vedou přes funkce odkazy na další stromy s kódem funkcí.

Popis derivačních stromů

Derivační strom se skládá z uzlů, z nichž mohou vést odkazy na první podřízený uzel, stejně jako na další uzel ve stejné úrovni. Jsou to skutečně stromy a ne DAGy, takže žádné slévání větví se nekoná. Kromě mnoha dalších informací vztahujících se k typu uzlu, se v každém uzlu nachází počet dočasných proměnných, který je potřebný v podstromu - toho využívá generátor kódu.

Tam, kde je podstromem výraz, tak se provádí zkusmé vyhodnocení podstromu a celý podtrom se popř. nahradí uzlem NODE_NUM. Je-li třeba strom vyhodnotit již v čase kompilace a nezdaří se to (má nekonstantní uzly), je vyvoláná chyba. Z tohoto důvodu je v případech nuceně konstantních výrazů nutné znát všechny symboly předem.

A nyní jednotlivé typy uzlů a nákresy vybraných podstromů :

NODE_NUM
Číselná konstanta.
NODE_VAR
Proměnná, registr, konstanta nebo alias. Jelikož není třeba deklarovat symboly předem, tak není jisté, o který se jedná.
NODE_FN
Volání funkce. Může jít o funkci, kterou uživatel explicitně uvedl ve výrazu, jakož i vestavěnou funkci nebo operátor, která tam byla zanesena parserem. Mezi speciální vestavěné funkce se počítají i cykly a jiné konstrukce. Je to vlastně velice univerzální uzel. V případě operátoru má za syny své operandy, v případě volání fce má za syny své argumenty.
NODE_VAR_DECL
Deklarace proměnné.
NODE_INSTRUCTION
Jeden blok instructions. Svůj podtrom připojí za ty již existující v upecc_instructions.
NODE_NUM_QUOTE
Číselná konstanta nebo otazník.
NODE_NUM_BIT_RANGE
Číselný bitový rozsah.

Nákres NODE_INSTRUCTION

NODE_INSTRUCTION
      |
      X--------1. větev----...----x. větev
      |
   1. maska----...----x. maska
    větev
      |
   podmínka (výraz)----kód
    maska
      |
   NODE_NUM_QUOTE----NODE_NUM_BIT_RANGE----alias NODE_VAR

Nákres obecného kódu

 1. příkaz----....----x. příkaz

Nákres podmíněného příkazu if

 NODE_FN id="*if"
      |
   podmínka (výraz)----NODE_FN id="*if-true"----NODE_FN id="*if-false"
                                |                          |
                               kód                        kód

Nákres cyklu while

 NODE_FN id="*while"
      |
   podmínka (výraz)----kód

Nákres cyklu do/while

 NODE_FN id="*do-while"
      |
   podmínka (výraz)----kód

Nákres cyklu for

 NODE_FN id="*for"
      |
    init (for_výraz)--podmínka (for_výraz)--iterátor (for_výraz)--kód
for_výraz je normální výraz nebo NODE_FN id="*for-void" pro prázdný

Nákres definice funkce

Argumenty jdou do globální tabulky symbolů jakožto položka fn.args kód funkce se připojí za deklaraci proměnných a uloří se do globální tabulky symbolů jako fn.code.

Generátor kódu

Generátor kódu se nachází v souborech upecc_cbe.h a upecc_cbe.c. Jak již bylo řečeno, tak kromě generování kódu také ověřuje některé sémantické chyby, což trochu prodlužuje jeho délku. Postupně se iteruje přes globální tabulku symbolů a na výstup se ukládá zdrojový kód v C opisující dané symboly a připadné podstromy. Také se prochází _upecc_instructions_. Stromy se ukládají rekurzivně - pro každé zanoření se volá funkce dump_statement. V této funkci se pro každý možný typ uzlu a symboly zapisuje odpovídající kód. Žádné optimalizace se neprovádí (nechávají se na kompilátoru jazyka C), pouze se zajišťuje co nejlepší využití všech dočasných proměnných.



This document was generated using AFT v5.05b