Emacs Lisp: Základy vyhodnocování výrazů
Většina Emacsu je napsána v programovacím jazyce nazvaném Emacs Lisp. Emacs Lisp je dialekt Lispu, který obsahuje vlastnosti speciálně navržené pro práci s textem: hledání, parsování, práce se soubory a textovými buffery, atd. To neznamená, že není možné použít Emacs Lisp i pro jiné účely. Je to plnohodnotný programovací jazyk a lze ho použít jako kterýkoli jiný programovací jazyk. V drtivé většině případů se však používá pro rozšíření a úpravu funkčností samotného Emacsu.
Proč Emacs a Emacs Lisp
Jakožto programátor, který tráví psaním zdrojového kódu v textovém editoru nemalé množství času, je pro mě důležité intimně znát co nejvíce funkcí a vlastností svého editoru, abych jej mohl co nejefektivněji využít ve svůj prospěch a ušetřit si tak práci. Čas od času zaznamenám nějaký vzor ve svém chování v editoru: často opakuji stejnou posloupnost klávesových zkratek; stále dokola píšu navzájem si velmi podobný text; nebo prohledávám dokumentaci svého editoru, abych zjistil, že funkčnost, která by se mi už pošesté hodila, není podporována. Počítače jsou vhodnější pro vykonávání přesně definovaných, opakujících se činností, tak proč je nenechat pracovat za mě?
Na světě snad neexistují dva jedinci, co by měli stejné požadavky na textový editor, nebo obecně na libovolný netriviální software, proto možnost změnit jeho chování k obrazu svému považuji za důležitou. A stejně tak považuji za důležité přidat si nové funkce, které v něm postrádám. Nechci být omezen osobními požadavky autorů daného editoru, nebo tím, co jeho autoři zatím stačili naprogramovat. Na druhou stranu nechci tuto myšlenku dotáhnout ad absurdum a vytvořit zcela nový editor jen proto, aby přesně splňoval mé požadavky. To není můj primární cíl. Můj hlavní cíl je editace zdrojového textu programu, knihy nebo například tohoto článku.
Jedním z hlavních důvodů, proč používám Emacs, je jeho flexibilita, která přesahuje flexibilitu všech ostatních, mě detailněji známých, editorů. Emacs mi dává v podstatě neomezenou možnost změny jeho chování. To více méně všechny open source programy. Tato vlastnost Emacsu však netkví v tom, že je to free software, ale v použití Emacs Lispu. Kdyby Emacs byl celý napsán v C a neobsahoval interpreter Lispu, jeho flexibilita by byla výrazně nižší a nebyl by pro mě tolik atraktivní a užitečný; za předpokladu, že by jeho autoři nepřišli na jiný způsob, jak této flexibility dosáhnout. Dalším z hlavních důvodů je, že Emacs už obsahuje mnoho funkcí, které pro svou práci potřebuji, a nemusím je tedy programovat sám.
Mezi nejdůležitější vlastnosti Emacs Lispu považuji jeho dynamičnost, garbage collection a poměrně úspornou syntaxi. Tyto vlastnosti, obsáhlá knihovna funkcí pro manipulaci s textem a schopnost Emacsu vyhodnocovat Lisp výrazy uprostřed psaní libovolného textu výrazně zkracují vývojový cyklus potřebný pro modifikaci stávajícího chování, případně vytváření nové funkčnosti. A to mi nahrává do karet. Jak už jsem řekl, tohle není můj hlavní úkol, ale pouze vedlejší produkt, na kterém nechci strávit příliš mnoho času, protože by se mi ona časová investice nemusela vrátit.
Vyhodnocování Lisp výrazů
Řekněme, že jsme uprostřed psaní nějakého C programu a potřebujeme spočítat nějaký aritmetický výraz, jehož výsledek chceme dosadit jako hodnotu nějaké konstanty v našem kódu. Pokud neumíme spočítat tento výraz zpaměti, můžeme spustit například kalkulačku nebo irb (Interactive Ruby Shell), spočítat výsledek aritmetického výrazu tam a zkopírovat ho do zdrojového kódu. Proč však spouštět jiný program, když to samé, bez kopírování, můžeme udělat přímo ve zdrojovém kódu v bufferu Emacsu, kam potřebujeme výsledek dosadit. Jsme s kurzorem v místě, kde potřebujeme výsledek výpočtu a napíšeme Lisp výraz, který nám po vyhodnocení vypíše výsledek přímo tam, kde ho potřebujeme, a poté Lisp výraz smažeme. Vyzkoušejme si to na příkladu. Mějme definici konstanty k a za operátor přiřazení napišme Lisp výraz, jehož výsledek hledáme. (Nehledejte v tomto výrazu smysl. Je to první věc, která mě napadla.)
const int k = (+ 32 1 4 (* 92 89 17) (/ 360 60));
Umístíme kurzor za výraz, za jeho poslední závorku, a zmáčkneme klávesovou zkratku C-u C-x C-e. Emacs předá poslední Lisp výraz nacházející se těsně před kurzorem interpreteru, ten ho vyhodnotí, a následně je výsledek výrazu vložen na místo kurzoru. Poté párkrát zmáčkneme klávesovou zkratku C-M-b, abychom doskákali kurzorem na začátek Lisp výrazu, a stiskneme C-M-k, což smaže celý Lisp výraz před kurzorem. Tím jsme dosáhli svého cíle, aniž bychom opustili buffer Emacsu, ve kterém jsme psali svůj C program. Výsledek našeho snažení vypadá takto.
const int k = 139239;
Cílem tohoto příkladu je demonstrovat schopnost Emacsu vyhodnocovat libovolné Lisp výrazy uprostřed libovolného textu a jejich výsledky vkládat přímo do textu. Tímto způsobem můžeme vytvořit i poměrně komplexní kus kódu, který například manipuluje s textem v otevřeném bufferu. Usoudíme-li, že původně jednoúčelový kód, který jsme právě vytvořili, by nám mohl být užitečný i v budoucnu, vytvoříme z něj funkci a uložíme ji do ~/.emacs souboru, který je Emacsem automaticky načítán při jeho startu.
Nechceme-li výsledek vyhodnocení vložit do bufferu, ale pouze ho zobrazit v echo oblasti (echo area) Emacsu, použijeme klávesovou zkratku C-x C-e místo C-u C-x C-e. Echo oblast je informační řádka sloužící k zobrazení různých zpráv, která se typicky nachází v dolní části rámce (frame) Emacsu.
Aktivní vyhodnocování Lisp výrazů na požádání je velmi užitečná vlastnost Emacsu. S její pomocí můžeme například vyhodnocovat individuální části (podvýrazy) Lisp kódu, který právě píšeme, a kontrolovat, zda vracejí výsledky, které bychom očekávali. To nám umožňuje zkrátit dobu potřebnou na vývoj daného programu a relativně brzo odhalit případné chyby, které jsme do kódu zanesli.
Uděláme-li v Lisp výrazu nějakou chybu, například se pokusíme zavolat funkci, která neexistuje, Lisp interpreter vygeneruje chybovou hlášku a dostaneme se do Emacs debuggeru. Zkusme to na následujícím výrazu.
(factorial 10)
Umístíme kurzor za výraz a stiskneme C-x C-e. Emacs otevře nové okno *Backtrace*, ve kterém se zobrazí následující text. (Text se může trochu lišit v závislosti na verzi Emacsu, kterou používáte.)
Debugger entered--Lisp error: (void-function factorial) (factorial 10) eval((factorial 10)) eval-last-sexp-1(nil) eval-last-sexp(nil) call-interactively(eval-last-sexp)
Chybové hlášení void-function factorial nám říká, že funkce factorial neexistuje. Debugger ukončíme stiskem písmene q.
Tím jsme si řekli o základních možnostech interaktivního vyhodnocování Lispu v Emacsu. Tento článek předpokládá alespoň základní znalost práce s Emacsem, a proto podrobněji nevysvětluje Emacsovou terminologii zde použitou. Další informace o klávesových zkratkách a Emacsových termínech použitých v tomto článku lze nalézt v [Richard Stallman, GNU Emacs Manual] a [Bil Lewis, Dan LaLiberte, Richard Stallman and the GNU Manual Group, GNU Emacs Lisp Reference Manual].

