Překladače 4. Lexikální analýza Obsah: vstupní formáty,symboly syntaktické grafy rozpoznání jazyka konečným automatem implementace, příklady datové typy, převod čísla, řetězce © Milan Keršláger http://www.pslib.cz/ke/slajdy 21.3.2012 http://creativecommons.org/licenses/by-nc-nd/3.0/
Lexikální analýza první fáze zpracování zdrojového kódu vstup: zdrojový kód programu zápis v nějakém programovacím jazyce různé vstupní formáty řeší se raději konverzními programy výstup: posloupnost symbolů atom, lexém, lexikální jednotka lexikální chyby
Lexikální chyby pouze v rámci jednoho symbolu neplatný znak mimo alfanumerické znaky avšak některé speciální znaky jsou přípustné neplatný identifikátor název proměnné nesmí začínat číslem neplatný řetězec chybějící ukončovací uvozovky
Vstupní formáty text prostý text (plain text), jeden či více souborů snadno čitelný, bez skrytých významů, case sensitive? vázaný text struktura používána k vytváření konstrukcí jazyka odsazování v Pythonu, jeden příkaz = jeden řádek atp. binární formát typicky generovaný kód (návrhář formulářů) dynamická struktura v paměti produkt jiného programu
Symboly též atom, lexém, lexikální jednotka, ... jsou to nejmenší části s vlastním významem klíčové slovo, číslo, operátor, název proměnné, ... skládá se ze dvou částí název (identifikace symbolu) S_BEGIN, S_NUM, S_PLUS, S_ID, ... atribut (skutečná hodnota) číslo, název proměnné, ... pozor na nevhodné atributy (typ operátoru) → obtížnější určování priority operátorů
Příklad zpracování CONST pi=3.14; VAR obvod; BEGIN obvod:=2*pi*polomer; ... END S_CONST S_ID pi S_EQ S_NUM 3.14 S_SEM S_VAR S_ID obvod S_BEGIN S_IS S_NUM 2 S_MUL S_ID polomer ... S_END // pro uložení výstupů // použit výčtový typ Tsymbol = record typ: TtypSymbolu; attr: string; end;
Skladba vstupu abeceda A..Z, a..z, 0..9, +, -, ×, /, >, <, =, (, ), ;, : rezervované identifikátory (klíčová slova) BEGIN, END, VAR, CONST, IF, THEN, ELSE, ... ostatní identifikátory (názvy proměnných) operátory aritmetické, relační, přiřazení pomocné symboly závorky, středník, ...
Syntaktické grafy slouží pro definici jazyka (v učebnicích) definuje jednotlivé lexikální symboly číslo písmeno číslo písmeno Symbol S_NUM (pouze celá čísla) Symbol S_ID < = + Symbol S_LE Symbol S_PLUS
Rozpoznávání symbolů potřebujeme konečný deterministický automat na vstupu je řetězec znaků k analýze čtení znaků + změna vnitřního stavu automatu lze využít výstupní pásku pro každý symbol má automat jiný koncový stav po načtení celého vstupu zjistíme, jaký je to symbol nejprve zákres automatu kolečka značí stavy automatu šipky nesou symbol, který způsobí změnu stavu
Konečné automaty S S_ID S : A < S S S_LT > = = S_IS S_NE S_LE písmeno číslo číslo písmeno číslo S S_ID S S_NUM Konečný automat pro S_ID Konečný automat pro S_NUM : A < S S S_LT > = = S_IS S_NE S_LE Konečný automat pro S_IS Konečný automat pro relační operace
Rozpoznání jazyka automatem stavové diagramy pro všechny symboly využijeme syntaktické grafy vzniknou konečné deterministické automaty koncové stavy představují symboly shrneme všechny diagramy do jednoho sloučíme počáteční stavy pro všechna slova jazyka může vzniknout problém determinističnosti jeden znak vede do dvou různých stavů nejlépe jazyk, který toto nemá; nicméně lze vyřešit i jinak vzniká automat rozpoznávající jazyk
Činnost konečného automatu v každém stavu se načte jeden další znak podle něho se rozhodne, kterou větví dále v koncovém stavu ještě následující znak pro kontrolu správného ukončení symbolu nenalezne-li se další větev → symbol? není větev, po které by se pokračovalo je v koncovém stavu → je to symbol (zapíšeme) hlášení chyby není-li v koncovém stavu a není kam pokračovat
Zápis vystupujících symbolů prostý text snadné hledání chyb lexikálního analyzátoru např. vázaný text pomalejší na zpracování binární soubor jednodušší načítání (např. pole nebo typ záznam) dynamická struktura v paměti vhodné pro přímé předání další fázi překladu dynamický seznam
Implementace automatu automat pro nekonečné jazyky obsahují identifikátory proměnných, čísla, řetězce... těmi běžně programujeme automat pro konečné jazyky používá se na rozlišení klíčových slov kombinujeme oba přístupy jako nekonečné jazyky → vše je identifikátor konečné jazyky → není identifikátor klíčové slovo?
Vstup analyzátoru ve formě textového souboru čtení po znacích není efektivní čteme po řádcích řádek do záznamu včetně čísla řádku, délky a pozice čteného znaku type Tznak = record rad: string; // zpracovávaný řádek pozice: byte; // pozice posl. načt. znaku delka: byte; // délka řádku cislo: word; // číslo řádku (pro chyb. hláš.) end;
Výstup analyzátoru lexikální analyzátor jako funkce každé zavolání funkce vrátí jeden symbol symbol předán na vstup syntaktického analyzátoru výstupní symboly do výčtového typu vystupující název symbolu bude index do výčtu atribut bude řetězec type TTypSymbolu = (S_BEGIN, ...) Tsymbol = record typ: TTypSymbolu; // index atrib: string; // atribut end;
Metody programování přímé stavové programování (nekonečné jazyky) změna stavu vyjádřena místem v programu tj. algoritmus sleduje strukturu jazyka tabulka přechodů (konečné jazyky) změna stavu je změnou pozice v tabulce obecně naprogramovaný automat stav reprezentován proměnnou (konečné jazyky) variace na tabulku
Stavové programování po načtení znaku změna stavu → příkaz switch (case) načtení nemění stav (smyčka) → while při změně struktury jazyka: → nutné měnit kód programu smyčka nesmí být přes více, než 1 stav v kódu programu nelze couvat (tj. vrátit se do výše položeného místa) → GOTO?
Příklad: stavové programování (1)
Příklad: stavové programování (2)
Tabulka přechodů pro regulární gramatiku pravidla ve tvaru: A → aB nebo A → a velká písmena: neterminály, malá: terminální symboly různé neterminály označují různé stavy použijeme-li indexy → řádky tabulky terminální symboly → sloupce při výpočtu se mění pozice v matici čísla uvnitř matice → číslo nového stavu je to nejobecněji naprogramovaný automat změna v jazyce mění pouze tabulku, ne však kód
Stav reprezentován proměnnou pro konečné, ale i nekonečné jazyky přechod do jiného stavu je přímo v kódu změna jazyka mění kód automatu odpadají velké tabulky v paměti → dají se však realizovat řídkými maticemi to však dále komplikuje kód lexikálního analyzátoru čtení vstupu pomocí smyčky while příkaz switch zajistí různé stavy jednotlivé case řeší přechody do jiného stavu
Uplatnění metod výběr metody podle typů symbolů přímé stavové programování je-li načtený symbol identifikátorem, je potřeba zjistit, zda to není klíčové slovo → tabulka přechodů → stav reprezentován proměnnou nevhodné je prosté porovnání řetězců řetězec se porovnává tolikrát, kolik je klíčových slov další dvě metody mají větší prostorovou složitost tj. větší nároky na paměť
Datové typy dosud uvažována jen celá kladná čísla reálná čísla, řetězce, výčtové typy, pole, pointery... lexikální analýza to nemusí řešit, ale: konstanty se řeší již v lexikální analýze jako by přibylo další klíčové slovo ovlivňují analýzu dynamicky (za běhu) reprezentace v atributu symbolu číslo jako řetězec převod na vhodnou binární reprezentaci
Převod čísla na binární tvar buď v lexikální nebo sémantické analýze číslo načteme jako řetězec volíme cílový datový typu prostorově nejnáročnější (reálné, komplexní) prostorově nejúspornější nutná bližší analýza (znaménko?, mezní hranice?, E?) problém reprezentace na dané architektuře integer 4 bajty na 32bitovém CPU, ale 8 bajtů na 64bit. může způsobit nepřenositelnost programu
Reprezentace dat (1) některé jazyky nestanovují pevně (C) → problémy s přenositelností, → sizeof() celé číslo se znaménkem (integer) 2 bajty, nejvyšší bit je znaménko, - 32768...32767 celé číslo bez znaménka (word) 2 bajty, 0...65535 celé číslo se znaménkem (short) 1 bajt, -128...127 celé číslo bez znaménka (byte, char) 0...255
Reprezentace dat (2) dlouhé celé číslo (long) 32 bitů (+-2 miliardy) velmi dlouhé číslo (long long) 64 bitů (+-9223372036854775807) racionální číslo (float) 32 bitů, -5,0...5,0 racionální s dvojitou přesností (double) 64 bitů, -5,0...5,0 velmi dlouhé racionální číslo (long double) 80 bitů, -5,0...5,0
Řetězce důležitá je jen délka řetězce optimalizace prostorové náročnosti někdy (jazyk C) lze řetězec na více řádků můžeme složit dohromady nezabýváme se však spojováním řetězců tzv. zřetězení → syntaktická analýza "Ahoj"+" "+"světe" nicméně lze v primitivních případech
Pole, záznamy jde jen o zadávání polí a záznamů každý jazyk definuje jinak příliš komplikované a sofistikované není to úloha pro lexikální analýzu předáváme dál v rozloženém tvaru jako jednotlivé symboly