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ěší