Hlavní navigace

GLib: Lexikální scanner (5)

Michal Burda

Dnes se ve výkladu o lexikálním scanneru pohneme zase o kus dál. Probereme si podrobně obory platnosti symbolů a proces čtení tokenů.

Co už umíme

Nejprve malá rekapitulace. Prozatím jsme si řekli, co to je GScanner a jak funguje, jak se inicializuje pro čtení ze souboru nebo textového bufferu a co všechno můžeme jeho konfigurací ovlivnit. V dnešním dílu se podíváme zblízka na práci s tabulkami symbolů a proces získávání tokenů ze zdroje.

Tabulky symbolů a obory platnosti

Symboly, jak už víme, jsou speciální identifikátory, které chceme od ostatních odlišit, protože pro nás mají zásadní význam. Například u tokenizace zdrojových kódů programovacího jazyka by symbolem typicky byla klíčová slova.

Symboly můžeme sdružovat do logických celků zvaných obory platnosti (scopes). V každém okamžiku má GScanner aktivní právě jeden obor platnosti. Mezi obory se můžeme libovolně přepínat a podle syntaxe čteného zdroje zpřístupňovat nebo naopak znepřístupňovat jednotlivé sady symbolů.

Obory platnosti jsou označovány číselnou konstantou ( guint) a rozhodnete-li se je používat, je vhodné si pro ně zřídit symbolické konstanty ( #define ...).

Jak již jsem psal v minulém dílu, první obor platnosti (scope = 0) může mít speciální význam. GScanner lze nastavit tak, aby tento obor platnosti považoval za univerzální a hledal v něm symboly pokaždé, když neuspěje v aktuálním oboru. (Viz volba scope0_fallback struktury GScannerConfig.)

Symboly se vnitřně ukládají do hash tabulky. Záznam symbolu tvoří jeho identifikátor (řetězec znaků), pointer na hodnotu a označení oboru platnosti (scope_id).

Aktuální obor platnosti se vybírá funkcí

guint g_scanner_set_scope(GScanner *scanner,
                          guint scope_id);

Po vytvoření nového GScanner u je aktuálním oborem 0.

Pro registraci symbolu ve scanneru se používá volání

void g_scanner_scope_add_symbol(GScanner *scanner,
                                guint scope_id,
                                const gchar *symbol,
                                gpointer value);

…, kde argumentem scanner je GScanner, kterému chceme symbol přidat, scope_id označení oboru platnosti, symbol identifikátor přidávaného symbolu a value hodnota symbolu. Jestliže symbol symbol v oboru platnosti scope_id již existuje, dojde pouze k přiřazení nové hodnoty  value.

Opačnou akci, tedy odstranění symbolu z tabulky, provedete pomocí

void g_scanner_scope_remove_symbol(GScanner *scanner,
                                   guint scope_id,
                                   const gchar *symbol);

Opět se musí kromě identifikátoru symbol uvést i číslo oboru platnosti scope_id.

Funkce

gpointer g_scanner_scope_lookup_symbol(GScanner *scanner,
                                       guint scope_id,
                                       const gchar *symbol);

…se používá ke zjištění hodnoty symbol u. Prohledá se výhradně obor platnosti scope_id. V případě neúspěchu vrátí funkce NULL.

Obdobnou činnost provádí i rutina

gpointer g_scanner_lookup_symbol(GScanner *scanner,
                                 const gchar *symbol);

Jejím úkolem je vyhledat v aktuálním oboru platnosti hodnotu symbolu symbol. Je-li nastavena vlastnost scope0_fallback konfiguračního záznamu GScannerConfig, prohledá se v případě neúspěchu i obor platnosti 0. Nemožnost nalezení symbolu indikuje funkce návratovou hodnotou NULL.

Máte-li chuť zavolat nějakou funkci func na každý záznam o symbolu ve zvoleném oboru platnosti scope_id, uvažujte o rutině

void g_scanner_scope_foreach_symbol(GScanner *scanner,
                                    guint scope_id,
                                    GHFunc func,
                                    gpointer user_data);

void (*GHFunc) (gpointer key, gpointer value, gpointer user_data);

Uživatelská funkce func je typu GHFunc (viz hash tabulky) a musí přebírat tři parametry: klíč (řetězec – identifikátor), hodnotu symbolu a uživatelská data  user_data.

Pro zpětnou kompatibilitu s předchozími verzemi knihovny GLib (a také pro ušetření psaní na klávesnici) jsou definována makra

#define g_scanner_add_symbol(scanner, symbol, value)
#define g_scanner_remove_symbol(scanner, symbol)
#define g_scanner_foreach_symbol(scanner, func, data)

Pro všechny tři platí, že pracují výhradně s univerzálním oborem platnosti (stejné jako volání předchozích funkcí se scope_id = 0).

Očekáváte-li při inicializaci scanneru, že budete do tabulky symbolů přidávat mnoho záznamů, můžete celý proces optimalizovat co do rychlosti „zmražením“ tabulky symbolů:

void g_scanner_freeze_symbol_table(GScanner *scanner);

Po zavolání této funkce bude přidávání symbolů do tabulky (lépe: obecně jakékoliv operace s tabulkou symbolů) probíhat výrazně rychleji, protože se dočasně odstaví jisté vnitřní mechanismy hash tabulek. Po provedení všech změn ale musíte tabulku „odmrazit“ voláním

void g_scanner_thaw_symbol_table(GScanner *scanner);

…, čímž se veškeré režijní úkony spojené se změnami provedou najednou.

Příklad:

...
g_scanner_freeze_symbol_table(scanner);
vloz_velmi_mnoho_symbolu(scanner);
g_scanner_thaw_symbol_table(scanner);
...

Čtení

Pro čtení tokenů ze vstupního zdroje jsou v  GScanner u im­plementovány dvě funkce:

GTokenType g_scanner_get_next_token(GScanner *scanner);
GTokenType g_scanner_peek_next_token(GScanner *scanner);

První z nich načte jeden token a posune ukazovátko aktuální pozice ve zdroji za něj, kdežto tou druhou se jen „podíváte“, jaký je následující token, ale ukazovátko zůstane na svém původním místě.

Volání g_scanner_get_nex­t_token() se dá pro názornost přirovnat k céčkovské funkci getc() a g_scanner_peek_nex­t_token() k posloupnosti příkazů:

c = getc(file);
ungetc(c, file);

O každém funkcí g_scanner_get_nex­t_token() právě načteném tokenu jsou ve struktuře GScanner uchovávány čtyři údaje: informace o typu ( token), hodnota tokenu ( value), řádek ( line) a pozice ( position) na řádku.

Přistupovat k nim můžete buď přímo, nebo pomocí k tomu určených funkcí:

GTokenType g_scanner_cur_token(GScanner *scanner);
GTokenValue g_scanner_cur_value(GScanner *scanner);
guint g_scanner_cur_line(GScanner *scanner);
guint g_scanner_cur_position(GScanner *scanner);

V případě hodnot načtených pomocí g_scanner_peek_nex­t_token() je v rozhraní knihovny GLib jistá nesymetričnost, protože se k datům lze dostat jen přímo přes strukturu GScanner  – pro ně jsou určeny atributy next_token, next_value, next_linenext_position.

Pokračování příště.

Našli jste v článku chybu?
Měšec.cz: Air Bank zruší TOP3 garanci a zdražuje kurzy

Air Bank zruší TOP3 garanci a zdražuje kurzy

Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

DigiZone.cz: Test Philips 24PFS5231 s Bluetooth repro

Test Philips 24PFS5231 s Bluetooth repro

Měšec.cz: Kdy vám stát dá na stěhování 50 000 Kč?

Kdy vám stát dá na stěhování 50 000 Kč?

Lupa.cz: Insolvenční řízení kvůli cookies? Vítejte v ČR

Insolvenční řízení kvůli cookies? Vítejte v ČR

Vitalia.cz: Když přijdete o oko, přijdete na rok o řidičák

Když přijdete o oko, přijdete na rok o řidičák

Vitalia.cz: Pamlsková vyhláška bude platit jen na základkách

Pamlsková vyhláška bude platit jen na základkách

Podnikatel.cz: Zavře krám u #EET Malá pokladna a Teeta?

Zavře krám u #EET Malá pokladna a Teeta?

Vitalia.cz: Jsou čajové sáčky toxické?

Jsou čajové sáčky toxické?

120na80.cz: Na ucho teplý, nebo studený obklad?

Na ucho teplý, nebo studený obklad?

Vitalia.cz: Spor o mortadelu: podle Lidlu falšovaná nebyla

Spor o mortadelu: podle Lidlu falšovaná nebyla

Lupa.cz: Propustili je z Avastu, už po nich sahá ESET

Propustili je z Avastu, už po nich sahá ESET

Podnikatel.cz: Udávání a účtenková loterie, hloupá komedie

Udávání a účtenková loterie, hloupá komedie

Podnikatel.cz: K EET. Štamgast už peníze na stole nenechá

K EET. Štamgast už peníze na stole nenechá

Podnikatel.cz: Podnikatelům dorazí varování od BSA

Podnikatelům dorazí varování od BSA

Vitalia.cz: I církev dnes vyrábí potraviny

I církev dnes vyrábí potraviny

Podnikatel.cz: 1. den EET? Problémy s pokladnami

1. den EET? Problémy s pokladnami

Vitalia.cz: „Připluly“ z Německa a možná obsahují jed

„Připluly“ z Německa a možná obsahují jed

Vitalia.cz: Proč vás každý zubař posílá na dentální hygienu

Proč vás každý zubař posílá na dentální hygienu

Měšec.cz: mBank cenzuruje, zrušila mFórum

mBank cenzuruje, zrušila mFórum