V m4 lze pomocí makra eval vyhodnocovat celočíselné výrazy:
eval(výraz [, číselná soustava [, šířka]])
Příkaz eval vyhodnotí matematický výraz a výsledek vrátí jako číslo o šířce šířka zapsané ve volitelně zadané číselné soustavě.
Výrazy musí být striktně celočíselné a mohou obsahovat následující operátory (seřazené sestupně podle priority):
– | unární minus |
** | umocnění |
* / % | násobení, celočíselné dělení, modulo (zbytek po dělení) |
+ – | sčítání, odčítání |
<< >> | bitový posun vlevo, bitový posun vpravo |
== != > >= < <= | porovnávání: rovná se, nerovná se, větší, větší nebo rovno, menší, menší nebo rovno |
! | logická negace (NOT) |
~ | bitová negace (NOT) |
& | bitový součin (AND) |
^ | bitový exkluzivní součet (XOR) |
| | bitový součet (OR) |
&& | logický součin (AND) |
|| | logický součet (OR) |
Všechny operátory kromě umocnění (**) jsou asociativní zleva. Nevyhovuje-li vám pořadí vyhodnocování operátorů, můžete pochopitelně ke změně priority použít závorky.
Malá poznámka o kompatibilitě GNU m4 s ostatními verzemi: různé implementace makro procesoru používají ^ jako alternativní operátor pro umocnění. GNU m4 během svého vývoje u tohoto operátoru změnilo své chování: původně ^ znamenalo umocnění, ale nyní se tímto operátorem označuje bitový XOR.
Pár ukázek použití makra eval:
eval(1 + -1) => 0 eval(2 + 3 * 4) => 14 eval((2 + 3) * 4) => 20 eval(2 ** 8) => 256 define(`obvod_ctverce', `eval(($1) * 4)') => obvod_ctverce(6) => 24
Pozor na práci s makry v aritmetickém výrazu:
define(`konst', 10) => eval(`konst' / 4) E> stdin:2: m4: Bad expression in eval: konst / 4 eval(konst / 4) => 2
Můžeme skloubit naše znalosti o rekurzivním volání maker a aritmetické operátory obohatit o makro suma, které vypočte součet svých argumentů:
define(`suma', `ifelse($#, 0, 0, $#, 1, ``$1'', `eval(suma(shift($@)) + `$1')')') => suma => 0 suma(5) => 5 suma(3,4,1,1,2) => 11 Tady to drhne: suma(xyz) => Tady to drhne: xyz
Aby se makro suma dalo zodpovědně používat, bylo by třeba je obohatit o kontrolu, zda-li jsou všechny parametry opravdu čísly.
Relační operátory pro pravdivé vyhodnocení (true) vrací jedničku, pro nepravdu (false) nulu:
eval(10 > 8) => 1 eval((5 == 5) && (12 < 12)) => 0
Jak již bylo řečeno, všechna čísla ve výrazech musí být celá. Procesor m4 rozpoznává několik zápisů číselných konstant podle jejich prefixů. Číslo bez prefixu je chápáno jako desítkové. Předřadíte-li číslu nulu („0“), bude m4 předpokládat, že se jedná o zápis v oktálním tvaru (osmičkové soustavě). Prefix „0ד označuje hexadecimální číslo (v šestnáctkové soustavě) a „0b“ číslo v binární (dvojkové) soustavě. V m4 ovšem nejste omezeni jen na tyto čtyři číselné soustavy. Chcete-li, můžete do výrazu makra eval zapisovat číselné konstanty v jakékoliv číselné soustavě se základem mezi 2 až 36 (včetně). Musíte jen použít prefix „0r“ následovaný ihned základem soustavy v decimálním tvaru, dvojtečkou („:“) a samotnými číslicemi, kterými je konstanta tvořena. U čísel v soustavách vyšších než desítkových se jako číslice používají i písmena a, b až z (nebo A, B až Z, což je totéž).
eval(0b101) => 5 eval(010) => 8 eval(09) E> stdin:3: m4: Bad expression in eval (excess input): 09 eval(0xFF) => 255 eval(0r36:z) => 35 eval(0xF - 0b1111) => 0
Výsledek lze získat v libovolné číselné soustavě. Základ výstupní soustavy předejte makru eval v desítkovém tvaru (číslo z intervalu <2, 36>) jako druhý parametr a o víc se nemusíte starat:
eval(255, 10) => 255 eval(255, 16) => ff eval(35, 36) => z
Třetí argument makra eval určuje minimální šířku výstupu. Pokud je výsledek expanze menší, číselná hodnota se do nastavené minimální šířky doplní zleva nulami:
eval(120, 10, 10) => 0000000120 eval(-120, 10, 10) => -000000120 eval(-120, 10, 2) => -120 eval(255, 16, 5) => 000ff eval(-255, 16, 5) => -00ff
Pozor, abyste takto upravený výsledek omylem nepoužili znovu ve výrazu eval, pokud si přesně nejste jisti, co děláte:
eval(eval(200, 10, 5)) => 128 eval(eval(200, 10, 2)) => 200
Vnořený eval v prvním případě expandoval na 00200 a takovýto řetězec se stal vstupem pro druhé makro eval. 00200 je ovšem zápis čísla v oktálním tvaru, proto výsledek v desítkové soustavě bude 128, a ne 200, jak bychom asi čekali.
Zkratky pro +1/-1
Makro procesor m4 obsahuje navíc vestavěná makra incr a decr, která k číslu v parametru přičtou (resp. od něj odečtou) jedničku:
incr(číslo) decr(číslo)
Příklady:
incr(3) => 4 decr(7) => 6 define(`konst', 10) => decr(konst) => 9
Tato makra vás sice ušetří psaní několika znaků, ale pořád to jaksi není ono. Pro potřeby inkrementování nebo dekrementování hodnoty uložené v nějakém makru zkuste tyto definice:
define(`inkrementuj', `define(`$1', incr($1))') => define(`dekrementuj', `define(`$1', decr($1))') => define(`i', 10) => i => 10 inkrementuj(`i') => i => 11
I zde je ovšem třeba ošetřit stavy, kdy inkrementované makro není definováno nebo neobsahuje číslo – pilný čtenář si jistě definice po nabytí všech potřebných znalostí doplní sám.