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 makra:
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:
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ě.