Hlavní navigace

Makro procesor GNU m4 (3)

Michal Burda 8. 10. 2001

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:

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

Našli jste v článku chybu?

7. 9. 2009 12:31

Pety (neregistrovaný)

Diky za pekny serial.
Pri cteni tretiho dilu jsem narazil na nejake drobnosti, ktere by stalo za to opravit.
Tady je jejich seznam i s navrhem na opravu:

< define(`zkouska', `argument 1: "", argument 2: ""')
> define(`zkouska', `argument 1: "$1", argument 2: "$2"')

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

< Jinými slovy, $@ je skoro totéž jako $, až na ...
> Jinými slovy, $@ je skoro totéž jako $*, až na ...

< => 00000
> => $1000000

Diky za bugfixnuti.
Pety aka Poke.













DigiZone.cz: Česká televize mění schéma ČT :D

Česká televize mění schéma ČT :D

Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

Vitalia.cz: „Připluly“ z Německa a možná obsahují jed

„Připluly“ z Německa a možná obsahují jed

Lupa.cz: Slevové šílenství je tu. Kde nakoupit na Black Friday?

Slevové šílenství je tu. Kde nakoupit na Black Friday?

Podnikatel.cz: Vládu obejde, kvůli EET rovnou do sněmovny

Vládu obejde, kvůli EET rovnou do sněmovny

Root.cz: Vypadl Google a rozbilo se toho hodně

Vypadl Google a rozbilo se toho hodně

Měšec.cz: U levneELEKTRO.cz už reklamaci nevyřídíte

U levneELEKTRO.cz už reklamaci nevyřídíte

Lupa.cz: Není sleva jako sleva. Jak obchodům nenaletět?

Není sleva jako sleva. Jak obchodům nenaletět?

Vitalia.cz: Chtějí si léčit kvasinky. Lék je jen v Německu

Chtějí si léčit kvasinky. Lék je jen v Německu

Lupa.cz: Proč firmy málo chrání data? Chovají se logicky

Proč firmy málo chrání data? Chovají se logicky

Podnikatel.cz: EET zvládneme, budou horší zákony

EET zvládneme, budou horší zákony

120na80.cz: Bojíte se encefalitidy?

Bojíte se encefalitidy?

Podnikatel.cz: K EET. Štamgast už peníze na stole nenechá

K EET. Štamgast už peníze na stole nenechá

Měšec.cz: Kdy vám stát dá na stěhování 50 000 Kč?

Kdy vám stát dá na stěhování 50 000 Kč?

Vitalia.cz: Baletky propagují zdravotní superpostel

Baletky propagují zdravotní superpostel

Podnikatel.cz: Prodává přes internet. Kdy platí zdravotko?

Prodává přes internet. Kdy platí zdravotko?

DigiZone.cz: NG natáčí v Praze seriál o Einsteinovi

NG natáčí v Praze seriál o Einsteinovi

Měšec.cz: Zdravotní a sociální pojištění 2017: Připlatíte

Zdravotní a sociální pojištění 2017: Připlatíte

Vitalia.cz: Jsou čajové sáčky toxické?

Jsou čajové sáčky toxické?

Vitalia.cz: Láska na vozíku: Přitažliví jsme pro tzv. pečovatelky

Láska na vozíku: Přitažliví jsme pro tzv. pečovatelky