Hlavní navigace

GLib: Lexikální scanner (3)

5. 4. 2001
Doba čtení: 5 minut

Sdílet

V dnešním díle se podíváme na bohaté možnosti ovlivňování chování lexikálního scanneru.

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_ZG_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.

ict ve školství 24

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

Autor článku

Michal Burda vystudoval informatiku a aplikovanou matematiku a nyní pracuje na Ostravské univerzitě jako odborný asistent. Zajímá se o data mining, Javu a Linux.