Hlavní navigace

Makro procesor GNU m4 (4)

16. 10. 2001
Doba čtení: 4 minuty

Sdílet

V dnešním dílu nás čeká nepřímé volání maker a podmínkové příkazy.
Přejmenování maker

V m4 je možno získat uvozený řetězec definice makra. Slouží k tomu vestavěné makro

defn(`nazev_makra')

Uvedenou vlastnost můžete použít pro ladicí účely, ke kopírování nebo
přejmenování makra.
Je-li makro definované uživatelem, je výsledkem expanze jeho definice uzavřená
do uvozovek. Jestliže se však defn použije na vestavěné makro procesoru
m4, makro expanduje na speciální token odkazující na vnitřní definici makra.
Tento token je smysluplný jenom jako druhý parametr k define nebo
pushdef; v jakémkoliv jiném kontextu (třeba při pokusu o jeho vypsání
na výstup) je ignorován.

Následující ukázka zobrazí definici makra makro:

   define(`makro', `Makro `$0' získalo $# argumentů')
=>
   defn(`makro')
=> Makro `$0' získalo $# argumentů

Jen tak cvičně, přejmenujme si makro undefine na vymaz:

   define(`vymaz', defn(`undefine'))
=>
   vymaz(`undefine')
=>
   undefine(`vymaz')
=> undefine(`vymaz')

Makro defn je rozpoznáváno pouze s parametry.

Nepřímé volání maker

V odstavci o definici maker jsem vám slíbil, že prozradím, jak volat makro s nepřípustným názvem. Už vám tedy nebudu déle tajit, že existuje vestavěné makro

indir(`nazev_makra' [, parametry, ...])

Volání indir donutí m4 expandovat makro s názvem v prvním parametru a
předat mu zbytek argumentů. Klidně můžete dělat tohle:

   indir(`define', `makro', `ahoj')
=>
   makro
=> ahoj

ale mnohem užitečnější je něco takového:

   define(`$vnitrni_makro', `Jen pro nepřímé volání!')
=>
   $vnitrni_makro
=> $vnitrni_makro
   indir(`$vnitrni_makro')
=> Jen pro nepřímé volání!

Jak vidíte, mechanismus můžete použít k definování „soukromých“ maker, která slouží jen pro vnitřní účely a u kterých takto nehrozí, že budou uživatelem volány omylem.

Z podobného soudku je i makro

builtin(`nazev_makra' [, parametry, ...])

sloužící k nepřímému volání vestavěných maker. Všechna standardní makra procesoru m4 si můžete předefinovat podle svých představ, přitom ale neztratíte možnost volat kterékoliv z nich právě pomocí makra builtin:

   define(`makro', `nazdar')
=>
   define(`undefine', `ahoj')
=>
   undefine(`makro')
=> ahoj
   makro
=> nazdar
   builtin(`undefine', `makro')
=>
   makro
=> makro

Ovšem, předefinujete-li si i makro builtin, nepomůže vám už ani svěcená voda :-).

Makro builtin je expandováno jen s parametry. Není tomu tak u indir, které bez argumentů zahlásí chybu:

   indir
E> stdin:1: m4: Undefined macro `'

Testování definice makra

Expanze maker na holý text, ačkoliv ji můžeme ovlivnit parametry, je užitečná jen v omezených případech. Obvykle potřebujeme něco víc: aby makro expandovalo na různé výstupy v závislosti na rozhodnutích uskutečněných za chodu, tj. potřebujeme nějaký druh podmínkových příkazů. GNU m4 poskytuje dva základní příkazy větvení. Prvním z nich je ifdef:

ifdef(`nazev_makra', výsledek1 [, výsledek2])

Příkaz umožňuje testovat, zda je nějaké makro definováno, nebo ne. Jestliže má identifikátor nazev_makra přiřazenou definiční hodnotu, ifdef expanduje na „výsledek1“. V opačném případě bude výstupem řetězec „výsledek2“. Třetí argument makra ifdef je volitelný, proto když jej neuvedete, použije se ve shodě s pravidly procesoru prázdný řetězec.

   define(`vypis_autora', `ifdef(`$autor',
                                 `Autor: indir(`$autor')',
                                 `neznámý autor')')
=>
   vypis_autora
=> neznámý autor
   define(`$autor', `Karel Zelený')
=>
   vypis_autora
=> Autor: Karel Zelený

Makro ifdef je rozpoznáváno jen s argumenty.

Porovnávání řetězců

Poněkud užitečnějším podmínkovým příkazem je vestavěné makro ifelse:

ifelse(poznámka)
ifelse(řetězec1, řetězec2, rovnají se [, nerovnají se])
ifelse(řetězec1, řetězec2, jsou rovny, ...)

Použijete-li jej jenom s jedním argumentem, nestane se nic a výsledkem bude prázdný řetězec. Z tohoto důvodu je makro ifelse občas využíváno pro různé poznámky.

   ifelse(`Tak tohle je nějaká poznámka.')
=>

Stejného účinku, ale čitelnějšího zdrojového kódu dosáhnete třeba
takto:

   define(`poznamka', `')
=>
   poznamka(`
   Tohle je dlouhatánská poznámka,
   která se na výstupu neobjeví.
   ')
=>

Má-li ifelse tři nebo čtyři parametry, provádí porovnávání prvních dvou argumentů. Jsou-li si řetězec1 a řetězec2 rovny, expanduje ifelse na řetězec „rovnají se“. V opačném případě bude výsledkem volitelný čtvrtý argument „nerovnají se“ (nebo prázdný řetězec, pokud čtvrtý parametr neuvedete).

   ifelse(`pepa', `teta', `stejné')
=>
   ifelse(`pepa', `pepa', `stejné')
=> stejné
   ifelse(`pepa', `teta', `stejné', `různé')
=> různé
   ifelse(`teta', `teta', `stejné', `různé')
=> stejné

Makro ifelse ale umí zpracovat i více argumentů než jenom čtyři. Jsou-li první dva argumenty shodné, expanduje ifelse na text třetího parametru. Jinak se vše opakuje bez prvních tří argumentů – tedy porovnává se čtvrtý a pátý parametr a v případě shody se vrátí šestý argument atd. Chybí-li makru argumenty pro další porovnávání a všechna předchozí porovnání dopadla neúspěšně, použije se pro expanzi případný poslední argument. Viz příklad:

root_podpora

   ifelse(`pepa', `teta', `1 = 2',
          `teta', `běta', `4 = 5',
          `nerovnají se')
=> nerovnají se
   ifelse(`pepa', `teta', `1 = 2',
          `teta', `běta', `4 = 5')
=>
   ifelse(`pepa', `teta', `1 = 2',
          `teta', `teta', `4 = 5',
          `nerovnají se')
=> 4 = 5
   ifelse(`teta', `teta', `1 = 2',
          `teta', `teta', `4 = 5',
          `nerovnají se')
=> 1 = 2

Pochopitelně, v praxi bude ifelse používáno situacích mnohem složitějších, než byly zde uvedené. Jeho zásadní použití tkví v rekurzivně volaných makrech, ale o tom někdy příště.

Makro ifelse je rozpoznáváno jen s argumenty.

Byl pro vás článek přínosný?

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.