Pokračování představování at, tentokrát praktická ukázka toho, jak se dá takový metajazyk použít ke konfiguraci. Jak může vypadat konfigurace pomocí at?
Konfigurace
@localhost {
@debug { true }
@loglevel { 50 }
@db {
@host { localhost }
@username { jakub }
@password { xxx }
@database { test }
}
}
Nám jde o to z toho získat nějaké hezké pole. Opět na začátek takový menší prolog:
<?php require 'at.php'; // zde bude nakonec načtená konfigurace $configuration = array(); // vytvoří se nová instance $at = at();
A teď už již k tomu zábavnějšímu. Jako první si trochu upravíme parser názvu makra a parametrů:
$at->fnname(function ($tokens) {
return array(trim($tokens[0]['content']), array());
});
fnname()
Metoda fnname() je getter/setter callbacku na naformátování názvu makra a jeho parametrů. Callback je opět realizován pomocí closure. Teď již k obsahu. Jako jediný argument dostane callback pole tokenů. Každý token je pole, které má na indexu type typ tokenu (který nabývá hodnot konstant: at::TEXT – textový token –, at::AT – znak @ –, at::LBRACE – znak { – a at::RBRACE – znak }), přičemž v názvu makra, resp. v textu mezi zavináčem a levou složenou závorkou, mohou být pouze textové tokeny a další zavináče.
Na indexu content každého tokenu se skrývá jeho textový obsah vyparsovaný ze vstupu. Jelikož naše konfigurace nebude nijak chytrá, vezmou se jednoduše všechny znaky mezi zavináčem a levou složenou závorkou (tj. všechny textové a zavinářové tokeny) a jejich obsah se spojí do jednoho názvu. Při pohledu na ukázku, jak bude konfigurace vypadat, je jasné, že callback dostane vždy pole s jedním textovým tokenem. Ale kdyby bylo např. makro @foo @bar @baz { ... }, dostaneme pětiprvkové pole.
fnname callback musí vracet dvouprvkové pole, kdy na prvním indexu je výsledný název makra a na druhém pole argumentů.
notfound()
Jelikož u konfigurace nemusíme vědět, jaké všechny názvy maker, resp. názvy konfiguračních hodnot, se tam mohou objevit, je nejjednodušší si to obstarat pomocí notfound callbacku:
$at->notfound(function ($block, $name) use ($at, &$configuration) {
if (count($block) === 1) {
$value = trim($block[0]);
if (strcasecmp($value, 'true') === 0) $value = TRUE;
else if (strcasecmp($value, 'false') === 0) $value = FALSE;
else if (((string) $_ = intval($value)) === $value) $value = $_;
$configuration[$name] = $value;
return ;
}
$up = $configuration;
$configuration = array();
$at->run($block);
$up[$name] = $configuration;
$configuration = $up;
});
notfound callback je volán v případě, pokud není název makra registrován pomocí fn(). Argumenty dostává stejné, které by dostala funkce, kdyby byla nalezena. My jsme si pomocí předefinování fnname zajistili, že seznam parametrů makra bude vždy prázdný, takže jako první argument bude notfound callback přijímat blok a jako druhý (zároveň poslední) jméno makra. Pokud tápete, co tam dělá to use ( ... ), pak vězte, že takhle se PHP říká, že tahle closure bude používat dané proměnné z nadřazeného kontextu. My potřebujeme mít k dispozici instanci at a odkaz na konfiguraci.
Nejdříve ve funkci zkusíme, jestli se jedná o skalární hodnotu, nebo o seznam hodnot. Pokud o hodnotu, tak ji prostě přidáme do současné konfigurace. Je tam ještě převod na nativní booleovskou hodnotu apod., ale to není zas tak důležité. Podstatné je, že argumentem bloku je předáván podstrom AST bloku daného makra. Tento strom je neinterpretovaný (říká se tomu lazy evaluation) a je to docela důležité, protože to umožňuje implementovat různé věci. Třebas tady zanořování.
Pokud se tedy jedná o seznam hodnot, zanoříme konfiguraci, projedeme předaný blok (tady je využito, že run() přijímá i naparsovaný strom), čímž se hodnoty přidávají do zanořené konfigurace atd. atp. Prostě rekurze :-)
Poslední drobek
Nakonec už jen pustíme at na konfiguraci (zase je využito __halt_compiler()):
// a jede se
$at->run(substr(file_get_contents(__FILE__), __COMPILER_HALT_OFFSET__));
// kouknout na configuraci
//var_dump($configuration);
__halt_compiler();
@localhost {
@debug { true }
@loglevel { 50 }
@db {
@host { localhost }
@username { jakub }
@password { xxx }
@database { test }
}
}
Když si jednotlivé drobečky spojíte dohromady, máte ukázku dalších možností at a praktický příklad, jak se dá vyřešit např. konfigurace. Příště o tom, jak může at rozšiřovat syntaxi jazyka.
Ze stejné skupiny
- at – rozšiřujeme syntaxi
- at – konfigurujeme
- at – text-processing metalanguage
Doposud žádný komentář
Přidat komentář