Změna lexikální struktury názvů maker
Makro
changeword(...)
se v procesoru m4 vyskytuje jako „experimentální“ už nejméně sedm let. Normálně není k dispozici, ale pokud si při kompilaci m4 zapnete volbu –enable-changeword (./configure –enable-changeword), budete jej moci používat. Problémem může být, že se podle autorů běh makro procesoru zpomalí asi sedmkrát.
O co vlastně jde? Pomocí tohoto makra máte možnost změnit regulární výraz, podle kterého makro procesor rozpoznává, jaký řetězec může být potenciálně identifikátorem makra a jaký ne. Standardně je možným identifikátorem makra vše, co vyhovuje regulárnímu výrazu
[_a-zA-Z][_a-zA-Zo-9]*
Můžete chtít například přiřadit nějakou definici i číselným konstantám:
changeword(`[_a-zA-Z0-9]+') => define(`1', `0') => 1 => 0
…a podobně.
Jelikož m4 vyhledává identifikátory maker po znacích, je zde jedno omezení: Pokud vašemu regulárnímu výrazu vyhoví například slovo „abc“, musí mu vyhovět i „a“ a „ab“.
Další, asi nejpodstatnější výhodou, kterou changeword přináší, je možnost zavedení nějaké escape sekvence před samotný identifikátor makra. Regulární výraz předaný changewordu může obsahovat část uzavřenou v kulatých závorkách. Makro procesor vyhledává řetězce, které vyhoví celému regulárnímu výrazu, ale provede expanzi makra, jehož název odpovídá právě části v kulatých závorkách. Potom lze dělat třeba tohle:
changeword(`\\\([_a-zA-Z0-9]+\)') => define(`makro', `Ahoj!') => define(`makro', `Ahoj!') \define(`makro', `Ahoj!') => makro => makro \makro => Ahoj!
(Tipnu si, že zrovna taková vlastnost vám až dosud asi nejvíce chyběla. :-)
Jistou možností, jak zabránit nechtěné expanzi maker a kde se obejdete bez changewordu, je volat m4 s parametrem -P. Tento parametr přejmenuje všechna vestavěná makra tak, že před jejich jména přidá prefix m4_ (např. define na m4_define, __file__ na m4___file__ nebo m4wrap na m4_m4wrap). Pak už stačí jenom tuto konvenci důsledně dodržovat a všechna makra pojmenovávat obdobně, a získáte poměrně solidní ochranu před nechtěnou expanzí (bez sedminásobného zpomalení).
Ladění a trasování
V makro procesoru m4 je vestavěna malá podpora i pro ladění. Můžete si nechat například zobrazit definici nějakého makra přímo na standardní chybový výstup:
dumpdef(...)
Makro dumpdef zobrazí definice těch maker, jejichž názvy uvedete jako argumenty. Pokud nezadáte žádný parametr, vypíše dumpdef na chybový výstup definice všech maker.
define(`makro', `Ahoj!') => dumpdef(`makro') => E> makro: ahoj dumpdef(`define') => E> define: <define>
Jak jste v ukázce viděli, speciální odkaz na vnitřní reprezentaci významu makra se vypíše jako název ve špičatých závorkách (<define>).
Trasování expanze maker se zapíná a vypíná pomocí vestavěných příkazů
traceon(...) traceoff(...)
Volání traceon a traceoff bez argumentů zapíná a vypíná trasování všech maker. Pokud jim jako parametr předáte názvy maker, bude se akce týkat jenom jich.
Údaje o trasování makra se vypisují na standardní chybový výstup. Kdykoliv je trasované makro zavoláno, zobrazí se konfigurovatelné informace o jeho volání.
define(`makro', `Ahoj!') => traceon(`makro') => makro => Ahoj! E> m4trace: -1- makro makro => Ahoj! E> m4trace: -1- makro makro(makro(makro)) => Ahoj!Ahoj!Ahoj! E> m4trace: -3- makro E> m4trace: -2- makro E> m4trace: -1- makro
Číslo mezi pomlčkami určuje hloubku expanze. Hloubka roste s tím, jak se volání makra vyskytují v argumentech jiných maker. Hloubka expanze rovna jedné znamená vnější volání z úrovně zpracování textu.
Formát ladicích informací
Pomocí volby
m4 -d příznaky ...
zadané při spouštění makro procesoru nebo voláním vestavěného makra
debugmode([příznaky])
můžete ovlivňovat, jaké informace chcete při ladění dostávat. „Příznaky“ je řetězec znaků, kde každý z nich zapíná určitou vlastnost. Významy různých příznaků shrnuje následující tabulka:
t | Trasuj volání všech maker. Pokud zapnete tuto volbu, budou se trasovat všechna makra. Jinak se budou trasovat jen ta makra, která explicitně uvedete ve volání traceon. |
a | Zobrazuj parametry volání maker. |
e | Zobrazuj výsledky expanze maker, pokud výsledkem není prázdný řetězec. |
q | Na výstupu uzavři případné argumenty makra a výsledek expanze do aktuálních uvozovek. |
c | Informaci o trasování vypisuj do tří řádků: první řádek se zobrazí v okamžiku, kdy je volání makra na vstupu nalezeno, druhý řádek informuje o volání makra s načtenými argumenty a třetí řádek ukazuje volání makra spolu s výsledkem expanze. |
x | Ke každé informaci o trasování přidej jednoznačný identifikátor (řetězec id číslo). Tato volba je užitečná zejména v kombinaci s „c“, protože tak lépe poznáte, které řádky ladicích informací patří k jednomu volání makra. |
f | Na každém řádku ladicích informací zobrazuj název aktuálního vstupního souboru. |
l | Na každém řádku ladicích informací zobrazuj číslo řádku vstupního souboru. |
p | Vypiš zprávu, pokud je vkládaný soubor (include) nalezen díky mechanismu hledání cesty (podle proměnné prostředí M4PATH apod.). Užitečné v případech, kdy nemáme jistotu, odkud vlastně m4 zdrojové soubory načítá. |
i | Vypiš zprávu pokaždé, když je aktuální vstupní soubor změněn (začne se číst z jiného souboru). |
V | Zkratka pro všechny předchozí volby. Chcete-li zapnout všechno, stačí uvést jenom „V“ místo „taeqcxflpi“… |
Jak již bylo řečeno, volby se zapisují za parametr -d při spouštění makro procesoru nebo jako parametr vestavěného makra debugmode. Pokud prvním znakem parametru makra debugmode je „+“, jsou zadané příznaky přidány k současnému nastavení. Pokud jako první znak uvedete „-“, budou vyjmenované příznaky naopak z aktuálního nastavení odebrány. Prázdný řetězec jako argument makra debugmode všechny příznaky vypíná (což se zdá jako standardní nastavení).
Rychlá inicializace knihoven maker
Poslední kapitolou, kterou si v našem seriálu probereme, je možnost vytváření knihoven maker. Když budete dostatečně pilní, může se stát, že vytvoříte rozsáhlou knihovnu maker pro m4. S velkým archivem nastává problém, že se musí celý znova načítat při každém spuštění makro procesoru. To si vezme jistou (se zrychlováním počítačů stále menší) část počítačového času.
Představme si, že máte vytvořen soubor maker nazvaný třeba jako fajnmakra.m4. Při zpracovávání různých souborů jej neustále používáte:
m4 fajnmakra.m4 vstup1.m4 > vystup1.m4 m4 fajnmakra.m4 vstup2.m4 > vystup2.m4 ...
Pak je lepší si soubor fajnmakra.m4 uložit do tzv. „zmraženého“ souboru (frozen file), který sice není moc čitelný pro uživatele, ale zato m4 jej umí načíst rychlostí blesku. Zmražená verze knihovny maker se vytváří takto:
m4 -F fajnmakra.m4f fajnmakra.m4
Použití je jednoduché:
m4 -R fajnmakra.m4f vstup1.m4 > vystup1.m4 m4 -R fajnmakra.m4f vstup2.m4 > vystup2.m4 ...
Popišme si trochu podrobněji, co vlastně volby -F a -R znamenají.
Zavoláte-li m4 s parametrem -F, makro procesor načte nejprve zadaný vstupní soubor a poté uloží do souboru specifikovaného těsně za -F celý svůj vnitřní stav. Volbou -R určujete, že před zahájením své práce na nějakém vstupu má makro procesor načíst a obnovit svůj vnitřní stav podle zadaného souboru. Z toho vyplývá, že po m4 nemůžete chtít, aby načetl dva zmražené soubory najednou – druhým by se přepsal stav získaný z prvního.
Sadu několika knihovních souborů můžete transformovat do jednoho frozen souboru sadou příkazů:
m4 -F makra1.m4f makra1.m4 m4 -R makra1.m4f -F makra12.m4f makra2.m4 m4 -R makra12.m4f -F makra123.m4f makra3.m4
…a používat:
m4 -R makra123.m4f vstup1.m4 > vystup1.m4 m4 -R makra123.m4f vstup2.m4 > vystup2.m4 ...
Ukládání vnitřního stavu makro procesoru do souboru je nutno dělat opatrně, protože bohužel není zajištěno, že vše bude pokaždé fungovat správně. Například příznaky pro trasování se do mraženého souboru neukládají, stejně tak jako regulární výraz nastavený changewordem. Na druhou stranu spolehlivě funguje ukládání všech definicí, včetně těch, které jsou na zásobníku (pushdef), ukládají se i řetězce určující uvozovky a komentáře, obsahy bufferů s dočasně uloženým výstupem a číslo aktuálního výstupního bufferu.
Jak vidíte, makro procesor GNU m4 umí být velkým pomocníkem. Ačkoliv obsahuje řadu věcí, ke kterým bych měl výhrady (např. problémy s předáváním znaků, sloužících coby uvozovky, jako parametr nějakému makru), dokáže v mnoha případech ulehčit práci. Je velmi dobře konfigurovatelný a se svou sadou vestavěných funkcí umožní naprogramovat téměř cokoliv (pokud ne, naprogramujte to jako externí program, který pak můžete volat pomocí esycmd… ;-)