Konfigurace chování GScanner
u
Nastavení GScanner
u je určeno záznamem GScannerConfig
:
typedef struct _GScannerConfig { /* default: */ gchar *cset_skip_characters; /* " \t\n" */ gchar *cset_identifier_first; /* G_CSET_a_2_z "_" G_CSET_A_2_Z */ gchar *cset_identifier_nth; /* G_CSET_a_2_z "_0123456789" G_CSET_A_2_Z GCSET_LATINS G_CSET_LATINC */ gchar *cpair_comment_single; /* "#\n" */ guint case_sensitive : 1; /* FALSE */ guint skip_comment_multi : 1; /* TRUE */ guint skip_comment_single : 1; /* TRUE */ guint scan_comment_multi : 1; /* TRUE */ guint scan_identifier : 1; /* TRUE */ guint scan_identifier_1char : 1; /* FALSE */ guint scan_identifier_NULL : 1; /* FALSE */ guint scan_symbols : 1; /* TRUE */ guint scan_binary : 1; /* FALSE */ guint scan_octal : 1; /* TRUE */ guint scan_float : 1; /* TRUE */ guint scan_hex : 1; /* TRUE */ guint scan_hex_dollar : 1; /* FALSE */ guint scan_string_sq : 1; /* TRUE */ guint scan_string_dq : 1; /* TRUE */ guint numbers_2_int : 1; /* TRUE */ guint int_2_float : 1; /* FALSE */ guint identifier_2_string : 1; /* FALSE */ guint char_2_token : 1; /* TRUE */ guint symbol_2_token : 1; /* FALSE */ guint scope_0_fallback : 1; /* FALSE */ } GScannerConfig;
V deklaraci můžete vidět názvy všech položek včetně jejich typů a hodnot, které odpovídají standardnímu nastavení.
Konfigurační struktura GScanner
u se předává funkci g_scanner_new()
. Uvnitř této funkce dojde k alokaci nové GScannerConfig
a do ní se hodnoty z předané struktury zkopírují (předaný záznam slouží pouze jako šablona). Funkci g_scanner_new()
můžete klidně poskytnout konstantní konfigurační záznam a během tokenizace ještě nastavení dodatečně měnit. Kopírováním hodnot se totiž zpřetrhají veškeré vazby s předanou strukturou.
Ještě připomenu, že volání g_scanner_new()
s parametrem NULL
zapříčiní „natažení“ standardních hodnot.
Nyní se už ale pusťme do popisu všech položek konfiguračního záznamu GScannerConfig
:
Bílé znaky
Znaky, které GScanner
bude chápat jako oddělovače jednotlivých tokenů, jsou stanoveny obsahem položky:
gchar *cset_skip_characters;
Standardně to je řetězec " \t\n"
, tedy mezera, tabulátor a zařádkování.
Bílé znaky scanner nijak nepromítá do svého výstupu – jednoduše se přeskakují.
Definice identifikátorů
Pomocí dvou atributů máte možnost ovlivnit, které řetězce bude scanner pokládat za identifikátory:
gchar *cset_identifier_first;
…pole (ukončené '\0'
) povolených znaků, které se mohou vyskytovat na začátku identifikátorů. V poli jsou znaky, jež mohou být prvním písmenem identifikátorů.
gchar *cset_identifier_nth;
…řetězec definující povolené znaky, z nichž smí být tvořena těla identifikátorů (znaky obsažené v názvech identifikátorů, ale ne na prvním místě).
Na okraj bych ještě uvedl přesnou definici maker G_CSET_A_2_Z
, G_CSET_a_2_z
…:
#define G_CSET_A_2_Z "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define G_CSET_a_2_z "abcdefghijklmnopqrstuvwxyz"
Poznáváte abecedu velkých a malých písmen? :-)
Rozpoznání následující sady znaků bude asi trochu obtížnější:
#define G_CSET_LATINC "\300\301\302\303\304\305\306"\ "\307\310\311\312\313\314\315\316\317\320"\ "\321\322\323\324\325\326"\ "\330\331\332\333\334\335\336"
Jsou to znaky „ ŔÁÂĂÄĹĆÇČÉĘËĚÍÎĎĐŃŇÓÔŐÖŘŮÚŰÜÝŢ
“, tedy velká písmena s diakritikou. Bohužel, pro našince je tento výčet více než neuspokojivý, neboť neobsahuje například Š, Ť nebo Ž. Podobně je tomu i s makrem:
#define G_CSET_LATINS "\337\340\341\342\343\344\345\346"\ "\347\350\351\352\353\354\355\356\357\360"\ "\361\362\363\364\365\366"\ "\370\371\372\373\374\375\376\377"
…v překladu: „ ßŕáâăäĺćçčéęëěíîďđńňóôőöřůúűüýţÿ
“, v čemž jistě poznáváte malá písmena s diakritickými znaménky (až na některé výjimky).
Při standardním nastavení bude identifikátorem například vstup Promenna3
nebo _pomocná_proměnná_
, ale ne třeba 38let
, a podobně.
Na první pohled může vyvstat otázka, jak GScanner
zareaguje, nalezne-li uprostřed „identifikátoru“ znak, který není obsažen v řetězci cset_identifier_nth
. Když se trochu zamyslíme, je to jednoduché: výsledkem bude první část identifikátoru jako samostatný token následovaný (nejspíše) G_TOKEN_CHAR
em (náš nepovolený znak) a poté tokeny pro zbytek „identifikátoru“.
Definice jednoduchých komentářů
gchar *cpair_comment_single;
…řetězec o dvou znacích definující tzv. „jednoduché komentáře“. Prvním znakem řetězce je znak určující začátek jednoduchého komentáře v textu; druhý prvek řetězce definuje ukončovací znak jednoduchého komentáře. Standardně to je řetězec "#\n"
– komentáře začínají od znaku #
a sahají až na konec řádku ( \n
).
( GScanner
disponuje ještě druhým typem komentářů, tzv. „multi-komentáři“. Jsou v mechanizmu zadrátovány natvrdo a vypadají jako klasické céčkařské komentáře /* ... */
.)
cpair_comment_single
můžete nastavit i na NULL
a znemožnit tak rozpoznávání jednoduchých komentářů úplně.
Case-sensitivita (aneb zohledňování velikosti písmen)
guint case_sensitive;
… TRUE
, má-li se GScanner
chovat jako case-sensitivní, tj. pokládat velká a malá písmena abecedy za rozdílná (zda-li jsou např. symboly „AhOJ“ a „ahoj“ chápány jako různé).
Nakládání s komentáři
Atribut
guint scan_comment_multi;
určuje, zda má GScanner
rozpoznávat tzv. multi komentáře („ /* ...
*/
“). Je-li scan_comment_multi
rovno FALSE
, bude scanner multi komentáře zpracovávat obyčejným způsobem (po znacích) a ne jako poznámku. S hodnotou scan_comment_multi = TRUE
zapouzdří celou poznámku do jednoho tokenu. Jestli ji ale skutečně předá i uživateli na výstup, rozhodne podle volby:
guint skip_comment_multi;
Je-li skip_comment_multi = FALSE
, dojde v případě nalezení multi komentáře i k jeho předání na výstup v podobě tokenu G_TOKEN_COMMENT_MULTI
. S hodnotou TRUE
se celá sekvence „ /* ... */
“ bude přeskakovat podobně jako bílé znaky.
Jednoduché komentáře nemají atribut podobný scan_comment_multi
. Platí, že single poznámky jsou tokenizovány vždy. (Chcete-li přece jenom potlačit rozpoznávání jednoduchých komentářů, nastavte atribut cpair_comment_single
na NULL
.) Zda mají být na výstup předávány jako tokeny typu G_TOKEN_COMMENT_SINGLE
, můžete stanovit boolovskou položkou:
guint skip_comment_single;
Tokenizace identifikátorů
guint scan_identifier;
…nastavte na TRUE
, chcete-li, aby scanner rozpoznával identifikátory. Bude-li scan_identifier = FALSE
, rozloží se veškeré identifikátory na proud tokenů G_TOKEN_CHAR
. Pozor! Jelikož i symboly jsou speciálním případem identifikátorů, nastavením scan_identifier
na FALSE
znemožníte i rozeznávání definovaných symbolů.
guint scan_identifier_1char;
…touto volbou se GScanner
u sděluje, že má za identifikátory pokládat i osamocené znaky. Vyskytnou-li se na vstupu osiřelé znaky z cset_identifier_first
, GScanner
je nepřeloží na G_TOKEN_CHAR
, ale na G_TOKEN_IDENTIFIER
. Hodnota nastavení scan_identifier_1char
nemá význam, pokud je scan_identifier
rovno FALSE
.
guint scan_identifier_NULL;
Speciálním identifikátorem, pro který má GScanner
vyhrazen zvláštní token, je „ NULL
“. Atributem scan_identifier_NULL
se určuje, má-li být „ NULL
“ přeložen jako G_TOKEN_IDENTIFIER_NULL
. Při scan_identifier_NULL = FALSE
, se bude „ NULL
“ chápat jako kterýkoliv jiný identifikátor, tedy jako obyčejný G_TOKEN_IDENTIFIER
.
Rozpoznávání symbolů
Chcete-li potlačit rozpoznávání symbolů, nastavte proměnnou:
guint scan_symbols;
na hodnotu FALSE
. Veškeré symboly budou od této chvíle chápány jako pouhé G_TOKEN_IDENTIFIER
.
Dnešní článek končí, ale jistě jste si všimli, že jsme neprobrali všechny položky struktury GScannerConfig
. Zbytek si schovávám na příště.
U dalšího pokračování se těší