Hlavní navigace

Makro procesor GNU m4 (3)

8. 10. 2001
Doba čtení: 5 minut

Sdílet

V dnešním pokračování se naučíme pracovat s definicemi maker: vytvářet svá vlastní makra, předefinovávat je a rušit.
Vestavěná makra

Procesor GNU m4 má celou řadu vestavěných funkcí, které slouží pro rozličné účely a jejichž kombinací v definici makra se dají poměrně slušně „naprogramovat“ různé formátovací úkoly. Každé vestavěné makro si kontroluje své argumenty, takže jejich používání je bezpečnější a snáze se ladí uživatelská makra. Většina z nich je rozpoznávána pouze s parametry, to znamená, že jestliže je použijete bez zadaných argumentů, nebudou se expandovat, ale jednoduše se jejich identifikátor vypíše na výstup jako obyčejný text. Je to jistý způsob ochrany před nechtěnou expanzí, který při troše nápaditosti můžete implementovat i do svých maker.

Definování maker

Nejdůležitější je asi vědět, jak definovat nové makro. Dělá se to vestavěným příkazem:

define(nazev_makra [, hodnota])

který definuje nazev_makra na hodnotu „hodnota“. Jestliže není hodnota uvedena, použije se prázdný řetězec. Textovým výsledkem expanze makra define není nic.

Následující příklad definuje makro makro1 na hodnotu „Ahoj světe!“:

   define(`makro1', `Ahoj světe!')
=>
   makro1
=> Ahoj světe!

Jestli se divíte, jak to, že výsledkem definice nového makra je prázdný řádek a ne „nic“, je to proto, že zařádkování za define už není součástí definice makra, ale pokračování normálního vstupu, který m4 kopíruje na výstup. V m4 existují určité techniky, jak tomuto chování zabránit – budeme se jimi zabývat později.

m4 neklade žádná omezení na řetězec názvu makra. Klidně můžete makro nazvat třeba „$Moje%%makro se šíleným&názvem“. Procesor ho ovšem na vstupu nikdy nerozpozná, protože jeho název neodpovídá regulárnímu výrazu povolených identifikátorů maker. Taková makra se dají volat nepřímo, ale o tom někdy jindy.

Makro define je rozpoznáváno jen s parametry, proto:

   define
=> define

Argumenty makra

Jak již víme, makra mohou mít argumenty. V expanzivním textu makra se umístění n-tého argumentu určuje řetězcem ve tvaru $n.

   define(`zkouska', `argument 1: "$1", argument 2: "$2"')
=>
   zkouska(jedna, dvě)
=> argument 1: "jedna", argument 2: "dvě"

GNU m4 umožňuje zadat po „$“ číslo skládající se i z více než jedné číslice, a tedy pracovat s jakýmkoliv počtem argumentů. Tak tomu není u některých jiných implementací m4, které akceptují pouze jednu číslici.

Nultý argument ($0) vždy uchovává název expandovaného ma­kra:

   define(`pokus', ``Toto je makro $0'')
=>
   pokus
=> Toto je makro pokus

Dvojité uvozovky v definici makra jsou nutné, protože jinak bychom uvázli v nekonečné smyčce: první pár uvozovek je odstraněn už při čtení argumentů makra define. Makro pokus expanduje na `Toto je makro pokus' – řetězec je ale znovu procesorem zpracován, čímž dojde k ukousnutí zbývajících uvozovek. Kdyby tam uvozovky nebyly, opět by došlo k expanzi slova pokus na konci textu a vše by se rozeběhlo znova od začátku.

Počet argumentů předaných makru udává speciální identifikátor $#. Můžeme vytvořit makro, které vrátí počet obdržených parametrů:

   define(`pocet_argumentu', `$#')
=>
   pocet_argumentu(arg1, arg2, arg3)
=> 3
   pocet_argumentu()
=> 1
   pocet_argumentu
=> 0

Další speciální zápis, $*, se používá k vypsání seznamu neuvozených argumentů s čárkami mezi nimi. Například:

   define(`vypis1', `$*')
=>
   vypis1(arg1, arg2  , arg3,   arg4)
=> arg1,arg2  ,arg3,arg4

Často potřebujeme, aby každý argument v $* byl v uvozovkách. (Třeba proto, že je předáváme jinému makru a nechceme, aby se argumenty expandovaly.) K tomu účelu existuje obdoba $*, notace $@. Jinými slovy, $@ je skoro totéž jako $*, až na to, že každý jednotlivý argument uzavře do uvozovek. Přesvědčit se o tom můžeme na tomto modifikovaném příkladu:

   define(`vypis2', `$@')
=>
   vypis(arg1, arg2  , arg3,   arg4)
=> arg1,arg2  ,arg3,arg4

Kde ale závorky zmizely? Pochopitelně byly odstraněny v okamžiku, kdy m4 zpracovalo expandovaný text. Evidentněji je celá záležitost vidět na následujícím příkladu:

   define(`vypis1', `$*')
=>
   define(`vypis2', `$@')
=>
   define(`pozdrav', `ahoj')
=>
   vypis1(`neco', `cosi', `pozdrav')
=> neco,cosi,ahoj
   vypis2(`neco', `cosi', `pozdrav')
=> neco,cosi,pozdrav

Dobře si promyslete, co se vlastně stalo!

Cokoliv dalšího, kde se vyskytuje $ a m4 tomu nerozumí, se normálně kopíruje na výstup, stejně jako veškerý další text:

   define(`muj_plat', `$ 1000000')
=>
   muj_plat
=> $ 1000000

Potřebujete-li na výstup dostat něco jako „$5“, vložte mezi dolar a číslo pár uvozovek. Zabráníte tak procesoru interpretovat $ jako odkaz na argument:

   define(`muj_plat', `$`'1000000')
=>
   muj_plat
=> $1000000

ale ne:

   define(`muj_plat', `$'`1000000')
=>
   muj_plat
=>

Rušení maker

Definice makra může být odstraněna pomocí vestavěného příkazu undefine:

undefine(nazev_makra)

Název makra musí být nutně v uvozovkách, protože jinak dojde k jeho expanzi. Expanze makra undefine je prázdná. Zadáte-li jako název makra neexistující makro, nestane se jednoduše nic. Podobně jako define, i undefine je rozpoznáváno jen s argumenty.

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

Dočasná změna definice maker

Procesor m4 umožňuje dočasně změnit definici některého makra s tím, že se k jeho původní hodnotě později vrátíme. Slouží k tomu vestavěná makra pushdef a popdef:

pushdef(`nazev_makra' [, hodnota])
popdef(`nazev_makra')

Tato makra fungují na principu zásobníku (a také díky této vlastnosti jsou hojně využívána v různých situacích). Makro je dočasně předefinováno zadanou hodnotou, přičemž původní hodnota se uschová. Definice se jakoby vrství jedna na druhou, přičemž v platnosti je vždy ta nejvrchnější. Jestliže makro nebylo původně nijak definováno, pushdef pracuje přesně stejně jako define.

Jak asi správně tušíte, popdef odstraní ze zásobníku poslední (nejvyšší) definici a uvede v platnost předcházející. Má-li makro jen jednu definici, funguje popdef úplně stejně jako undefine.

   define(`makro', `jedna')
=>
   makro
=> jedna
   pushdef(`makro', `dvě')
=>
   makro
=> dvě
   pushdef(`makro', `tři')
=>
   makro
=> tři
   popdef(`makro')
=>
   makro
=> dvě
   popdef(`makro')
=>
   makro
=> jedna
   popdef(`makro')
=>
   makro
=> makro

Je-li makro s několika definicemi v zásobníku předefinováno pomocí define, přepíše se jen poslední definice ze zásobníku. Je-li takové makro zrušeno voláním undefine, odstraní se všechny definice, nejen ta poslední.

   define(`makro', `jedna')
=>
   pushdef(`makro', `dvě')
=>
   makro
=> dvě
   define(`makro', `druhá definice')
=>
   makro
=> druhá definice
   popdef(`makro')
=>
   makro
=> jedna

Ukázka na undefine:

CS24_early

   define(`makro', `jedna')
=>
   pushdef(`makro', `dvě')
=>
   makro
=> dvě
   undefine(`makro')
=>
   makro
=> makro

Makra pushdef a popdef jsou rozpoznávána jen s parametry a jejich výsledkem je prázdný řetězec.

Pokračování příště.

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.