Obsah
1. Programovací jazyky z vývojářského pekla++
2. Jazyky, v nichž se intenzivně používají zásobníky
4. Proměnné a řetězce v utilitě dc
5. Makra a tvorba složitějších programů v dc
7. „Zelený“ jazyk šetrný k životnímu prostředí
8. Základní operace v jazyku Whitespace
10. Základní operace podporované interpretrem jazyka Piet
11. Malbolge: vítejte ve skutečném programátorském pekle
1. Programovací jazyky z vývojářského pekla++
Většina programovacích jazyků, s nimiž jsme se alespoň ve stručnosti seznámili v předchozím článku a kterým se budeme věnovat i v článku dnešním, evidentně není zaměřena na praktické použití, ale jedná se spíše o programátorské vtípky, popř. o výsledek snahy navrhnout takový jazyk, pro nějž by bylo možné napsat překladač či interpret s velikostí pár stovek bajtů. Výjimkou je zvláštní makrojazyk, jenž je použitý v utilitě nazvané dc (což vzletně znamená Desk Calculator). Tato utilita sice používá podobně nečitelný styl zápisu algoritmů jako mnohé další esoterické jazyky, ovšem byla a vlastně dodnes je používaná v praxi, i když ji mnoho uživatelů raději nahrazuje „lidštějším“ nástrojem pojmenovaným bc (pravděpodobně oba tyto nástroje naleznete i na vašem systému). Stručnému popisu utility dc se budeme věnovat ve třetí, čtvrté i v páté kapitole.
Utilita dc je založena na použití zásobníků, do nichž se ukládají zpracovávané hodnoty (či makra), nad nimiž se následně provádí různé operace. Podobný přístup, i když implementovaný mnohem „šílenějším“ (a méně praktickým) způsobem, byl použit i při návrhu jazyků Whitespace a Piet. V jazyku Whitespace, který bude popsán v šesté a sedmé kapitole, se všechny zásobníkové operace zapisují kombinací bílých znaků (mezery, Taby apod.), zatímco v jazyku (jedná se skutečně ještě o jazyk?) nazvaném Piet jsou zásobníkové operace zakódovány do barev pixelů a samotný program se vytváří kreslením bitmapy. Tímto velmi elegantním jazykem se budeme zabývat v deváté a desáté kapitole. Zcela na závěr si ponecháme snad nejšílenější jazyk vůbec. Ten je nazván příznačně Malbolge a zajímavé je, že programy psané v tomto jazyku většinou nejsou vytvořeny přímo vývojáři, ale jedná se o výsledek práce jiných programů (psaných samozřejmě v civilizovanějších nástrojích).
2. Jazyky, v nichž se intenzivně používají zásobníky
Minule jsme se mj. zmínili i o jazycích FALSE a Befunge. I přesto, že se tyto jazyky od sebe v mnoha ohledech liší, mají jednu důležitou vlastnost společnou – výpočty, tj. aritmetické operace, logické operace a rozhodovací (řídicí) konstrukce jsou prováděny s hodnotami uloženými na zásobníku. Díky tomu bylo možné jazyky značně zjednodušit, protože se o transformaci výrazů z dnes běžné infixové podoby do podoby postfixové (taktéž známé pod názvem Převrácená Polská Notace/Reverse Polish Notation – RPN) musí postarat sám programátor. To ve skutečnosti není nijak složité, ostatně s prakticky stejným problémem se musí potýkat i ti vývojáři, kteří pracují ve Forthu, PostScriptu či v jazycích Joy a Factor (podle názoru některých vývojářů se všechny čtyři zmíněné nástroje už nachází na hranici mezi „civilizovanými“ programovacími jazyky a jazyky esoterickými, i když autor článku je velkým fandou zásobníkových jazyků, takže tento názor nesdílí :-).
Ovšem i další esoterické programovací jazyky jsou založeny na použití zásobníku operandů či dokonce většího množství zásobníků. Kromě již zmíněných jazyků se jedná o nástroj dc, jazyk Whitespace i o „grafický“ jazyk Piet, s nimiž se již dnes seznámíme v navazujících kapitolách. Zde si jen připomeňme, že při použití zásobníku operandů jsou hodnoty na zásobník ukládány explicitně (zápis 42 většinou znamená uložení této hodnoty na vrchol zásobníku operandů), aritmetické a logické operace používají implicitní adresování operandů (vždy se jedná o hodnotu na vrcholu zásobníku či těsně pod ním) a kromě toho se většinou setkáme i s několika pomocnými operacemi pro manipulaci s obsahem zásobníku. Tyto operace bývají nazývány dup (zduplikování hodnoty uložené na vrcholu zásobníku), drop (odstranění hodnoty z vrcholu zásobníku operandů), swap (prohození dvou nejvyšších prvků) a rot (rotace tří nejvyšších prvků). Tyto názvy mají dnes již vlastně historický původ, protože byly použity v programovacím jazyku Forth.
Poznámka: vrchol zásobníku se označuje zkratkou TOS neboli Top Of Stack.
3. dc
Nástroj nazvaný dc (Desk Calculator) je po všech stránkách pozoruhodná aplikace určená primárně k provádění výpočtů s numerickými hodnotami s neomezenou (nastavitelnou) přesností a rozsahem. Syntaxe použitých příkazů a operací totiž vede k psaní velmi „zahuštěného“ kódu, což je vlastně v ostrém kontrastu s některými dalšími esoterickými jazyky, kde je tomu mnohdy přesně naopak (Brainfuck či JSFuck jsou dobrým příkladem druhého extrému). Na druhou stranu se však ukazuje, že spojení zásobníkově orientovaného jazyka (které s sebou přináší i v předchozí kapitole zmíněnou obrácenou polskou notaci) a propracovaného systému maker umožňuje vytvářet i dosti složité programové konstrukce a dokonce i celé komplexní nadstavbové aplikace. Další významnou předností je, že dc je zahrnuta do standardu POSIX, je tedy možné počítat s tím, že každá unixová distribuce bude dc obsahovat, bez ohledu na použitou platformu.
Při práci s nástrojem dc se nevyhneme častým manipulacím se zásobníky, ať už s využitím explicitně zadaných příkazů (duplikace hodnot v zásobníku) či implicitními operacemi (například při provádění matematických výpočtů). Mezi základní operace, které lze se zásobníkem provádět, patří:
# | Operace | Význam |
---|---|---|
1 | c | vymazání veškerého obsahu zásobníku |
2 | d | poslední vložená položka v zásobníku je zduplikována |
3 | r | výměna hodnot mezi nejvyšší a druhou nejvyšší hodnotou na zásobníku |
4 | p | výpis čísla uloženého na vrcholu zásobníku |
5 | f | výpis obsahu celého zásobníku |
(operace d odpovídá výše zmíněnému příkazu dup, operace r pak příkazu swap)
Zásobníky jsou v dc použity zejména z toho důvodu, aby se zjednodušilo zadávání a provádění aritmetických operací. Ve výpisu níže jsou uvedeny příklady zápisu některých základních i složitějších aritmetických výrazů a jejich kombinací spolu s příkazem pro tisk obsahu zásobníku (p – print) a pro vyčištění zásobníku (c – clear). Už na těchto příkladech si povšimněte, že jednoslovní příkazy je možné psát vedle sebe bez nutnosti použití bílých znaků:
10 20 + p c 30 10 20 * pc 200 10 20*pc 200 10 20 -pc -10 10 20 / pc 0 (celočíselné dělení - není nastaven radix) 10 20 % pc 10 (dělení modulo) 10 20 + 30 * pc 900 5 4 3 2 1 * * * * p 120 5 4 3 2 1****p 120
Kromě výše uvedené pětice základních operací jsou v utilitě dc podporovány i operace další, zejména dělení se zbytkem (to v infixové notaci zapsat nelze), výpočet libovolné mocniny a druhé odmocniny. Následuje příklad použití těchto operací (příkaz f vypíše obsah celého zásobníku):
10 3 ~ f 1 3 celočíselný podíl je roven třem, zbytek je 1 2 10 ^ f 1024 výpočet desáté mocniny hodnoty 2 256 v f 16 výpočet druhé odmocniny hodnoty 256
Při pohledu na výsledky výše uvedených aritmetických výrazů jste si pravděpodobně všimli, že všechny vypočtené hodnoty byly celočíselné. To však neznamená, že dc pracuje pouze s celými čísly, jde pouze o počáteční nastavení. Přesnost výpočtů (resp. výsledků operací) se řídí takzvaným radixem, který udává maximální počet číslic uvedených za desetinnou tečkou. Radix se nastavuje pomocí příkazu k, který očekává na zásobníku jedno číslo udávající radix. Pokud na zásobníku žádné číslo uvedeno není, je to při vyvolání tohoto příkazu považováno za chybu. Vše si můžeme ozřejmit na následujícím příkladu, který počítá druhou odmocninu z deseti při použití různého radixu:
10 v p 3 1 k 10 v p 3.1 2 k 10 v p 3.16 10 k 10 v p 3.1622776601 30 k 10 v p 3.162277660168379331998893544432 c k dc: stack empty
Samozřejmě lze vynechat zbytečné mezery a psát programy jako skutečný muž :-) například následujícím způsobem:
30k10vp 3.162277660168379331998893544432
Číselná soustava vstupujících číselných hodnot se mění příkazem i, který na vrcholu zásobníku očekává číslo udávající základ soustavy. Podobně se číselná soustava tisknutých číselných hodnot mění příkazem o. V následujícím příkladu je proveden převod některých čísel ze soustavy desítkové do dalších soustav (jak je ze zápisů patrné, s menším počtem „úhozů“ snad ani nejdou konverze zapsat):
1 2 127 128 255 256 f 256 255 128 127 2 1 2 o f 100000000 11111111 10000000 1111111 10 1 8 o f 400 377 200 177 2 1 16 o f 100 FF 80 7F 2 1 2of 100000000 11111111 10000000 1111111 10 1 8of 400 377 200 177 2 1 16of 100 FF 80 7F 2 1
4. Proměnné a řetězce v utilitě dc
Na proměnné, které v nástroji dc taktéž existují, se můžeme dívat ze dvou stran. Na jednu stranu se proměnné chovají v mnoha ohledech podobně jako v jiných programovacích jazycích, tj. obsahují jedinou numerickou či řetězcovou hodnotu – dc si přitom datový typ uložené hodnoty pamatuje. Na druhou stranu však může být každá proměnná využita jako pojmenovaný zásobník, což má velký význam, zejména díky tomu, že proměnných může být teoreticky použito pouze 256, protože jsou pojmenovány pouze jedním ASCII znakem – většinou se však používá jen 26 proměnných, což odpovídá počtu písmen anglické abecedy. Vzhledem k faktu, že se každá proměnná chová jako samostatný zásobník, je možné jejich využití například v makrech bez toho, aby se obsah proměnné změnil (samozřejmě za předpokladu, že makro korektně odstraní všechny mezivýsledky výpočtů). Pro práci s proměnnými jsou určeny následující operace:
# | Kód příkazu (operace) | Význam příkazu |
---|---|---|
1 | s[název proměnné] | vyjmutí numerické či řetězcové hodnoty z vrcholu zásobníku operandů a uložení této hodnoty v proměnné zadané svým názvem |
2 | l[název proměnné] | přečtení hodnoty z proměnné zadané svým názvem a uložení této hodnoty na vrchol zásobníku operandů |
3 | S[název proměnné] | vyjmutí numerické či řetězcové hodnoty z vrcholu zásobníku operandů a uložení této hodnoty na vrchol zásobníku zadaného svým názvem – proměnná se zde tedy chová jako zásobník |
4 | L[název proměnné] | vyjmutí posledně vložené hodnoty ze zásobníku zadaného svým jménem a uložení této hodnoty na vrchol zásobníku operandů |
Práci s proměnnými si můžeme ukázat na následujících jednoduchých příkladech:
# pokus o zápis tří hodnot do proměnné 'a' pomocí operace 's': 1sa 2sa 3sa # zásobník operandů je prázdný - důkaz: f # načtení hodnoty proměnné 'a' pomocí operace 'l': la f 3 la f 3 3 la f 3 3 3 # proměnná se při těchto operacích chová jako "jednohodnotová" proměnná # (na zásobník operandů je třikrát po sobě zapsáno jedno číslo) q
Při operacích S a L však bude situace odlišná:
# pokus o zápis tří hodnot do proměnné 'a' pomocí operace 'S': 1Sa 2Sa 3Sa # zásobník operandů je prázdný - důkaz: f # načtení hodnoty proměnné 'a' pomocí operace 'L': La f 3 La f 2 3 La f 1 2 3 # proměnná se při těchto operacích chová jako pojmenovaný zásobník # (na zásobník operandů jsou zapsány tři odlišné hodnoty) q
Utilita dc obsahuje i několik instrukcí určených pro práci s řetězci. Řetězce mohou být uloženy jak na zásobník operandů, tak i do libovolné proměnné pomocí výše uvedených čtyř operací. S řetězci je prakticky možné provádět pouze dvě řetězcové operace. První operací je tisk řetězce na standardní výstup, čehož se často používá například při interaktivní práci s nějakým skriptem nebo pro výpis chyb nastalých při výpočtu. Druhou operací je provedení řetězce jako makra, tj. znaky v řetězci jsou chápány jako příkazy jazyka dc a jsou po zadání některého „spouštěcího“ příkazu provedeny. Speciální operací je konstruktor řetězce, což je vlastně příkaz sestávající ze dvou znaků pro levou a pravou hranatou závorku, přičemž všechny znaky, které se nachází uvnitř těchto závorek, jsou považovány za součást řetězce.
Následuje velmi jednoduchý příklad práce s řetězci:
[Hello world] p Hello world q
Využití proměnných a příkazu n pro tisk bez vložení znaku pro konec řádku (opticky dochází ke spojení tří řetězců, ve skutečnosti však taková operace není podporovaná):
# Naplnění proměnných 'a', 'b' a 'c' [Hello]sa [ ]sb [world]sc # Přesun hodnot proměnných 'a', 'b' a 'c' na zásobník operandů lclbla # Tisk tří hodnot na zásobníku operandů bez odřádkování nnn Hello world # Ukončení aplikace # (ve skutečnosti je následující znak umístěn za vytištěný řetězec) q
5. Makra a tvorba složitějších programů v dc
Jak již bylo naznačeno ve čtvrté kapitole, je možné řetězce použít pro zápis maker. V tomto případě se využívají následující operace:
# | Kód příkazu (operace) | Význam příkazu |
---|---|---|
1 | x | pokud je na vrcholu zásobníku operandů uložen řetězec, je chápán jako makro, které je ihned provedeno |
2 | >[název proměnné] | porovná a odstraní dvě hodnoty uložené na vrcholu zásobníku operandů. Pokud je hodnota v TOS větší, provede se kód uložený v proměnné zadané svým názvem |
3 | !>[název proměnné] | podobné předchozí operaci s tím, že se makro provede, pokud je TOS menší nebo rovno druhé hodnotě |
4 | <[název proměnné] | spuštění makra, pokud je TOS menší než druhá hodnota |
5 | !<[název proměnné] | spuštění makra, pokud je TOS větší nebo rovno druhé hodnotě |
6 | =[název proměnné] | spuštění makra v případě, že jsou si obě hodnoty uložené na vrcholu zásobníku operandů rovny |
7 | !=[název proměnné] | spuštění makra v případě, že si hodnoty nejsou rovny |
8 | q | ukončení běhu makra, včetně makra volajícího |
9 | Q | ukončení n volání maker, přičemž hodnota n je uložena na vrcholu zásobníku |
10 | ? | načtení řetězce ze standardního vstupu a spuštění tohoto řetězce jako makra |
Při tvorbě maker je nutné si dávat pozor na to, že samotný „skript“ makra je řetězec, musí tedy být umístěn v hranatých závorkách. To je uvedeno na následujícím jednoduchém příkladu, který vypíše, zda je číslo uložené na zásobníku nulové. Skript je uložen v proměnné nazvané z (od slova „zero“, samozřejmě si můžete vybrat libovolnou jinou proměnnou), text s příkazem pro tisk nulové hodnoty v proměnné t (od slova „text“) a konečně text s příkazem pro tisk nenulové hodnoty v proměnné n (od sousloví „non-zero“):
# příkaz pro tisk prvního řetězce je uložen jako makro do proměnné 't' [[na zasobniku je nula]n]st # příkaz pro tisk druhého řetězce je uložen jako makro do proměnné 'n' [[na zasobniku je nenulova hodnota]n]sn # makro provádějící dva testy - na rovnost nuly a na nerovnost nuly # (všimněte si duplikace původní hodnoty, první test by tuto hodnotu zničil) # toto makro je uloženo do proměnné 'z' [d0=t0!=n]sz # vyzkoušení funkce makra 10lzx 0lzx
Jak je z předchozího zápisu patrné, je tvorba maker určena pouze pro otrlé, a to jste ještě neviděli příklady uvedené v následujících odstavcích :-)
Podívejme se na složitější makra, která byla vytvořena pokročilými uživateli utility dc.
Fibonacciho posloupnost:
1 sa 1 sb 2 sc [la lb + p lb sa sb lc 1 + d sc 13 >z] sz la p sx lp p sx lz x
Výpočet největšího společného dělitele dvou čísel:
?[dSarLa%d0<a]dsax+p
Výpočet faktoriálu:
[d1-d1<F*]dsFxp
Příklad výpočtu 10!:
10[d1-d1<F*]dsFxp 3628800
Samozřejmě nejsme omezeni rozsahem základních datových typů, takže se můžeme bez obav pustit do výpočtu 1000!:
1000[d1-d1<F*]dsFxp 402387260077093773543702433923003985719374864210714632543799910429938\ 512398629020592044208486969404800479988610197196058631666872994808558\ 901323829669944590997424504087073759918823627727188732519779505950995\ 276120874975462497043601418278094646496291056393887437886487337119181\ 045825783647849977012476632889835955735432513185323958463075557409114\ 262417474349347553428646576611667797396668820291207379143853719588249\ 808126867838374559731746136085379534524221586593201928090878297308431\ 392844403281231558611036976801357304216168747609675871348312025478589\ 320767169132448426236131412508780208000261683151027341827977704784635\ 868170164365024153691398281264810213092761244896359928705114964975419\ 909342221566832572080821333186116811553615836546984046708975602900950\ 537616475847728421889679646244945160765353408198901385442487984959953\ 319101723355556602139450399736280750137837615307127761926849034352625\ 200015888535147331611702103968175921510907788019393178114194545257223\ 865541461062892187960223838971476088506276862967146674697562911234082\ 439208160153780889893964518263243671616762179168909779911903754031274\ 622289988005195444414282012187361745992642956581746628302955570299024\ 324153181617210465832036786906117260158783520751516284225540265170483\ 304226143974286933061690897968482590125458327168226458066526769958652\ 682272807075781391858178889652208164348344825993266043367660176999612\ 831860788386150279465955131156552036093988180612138558600301435694527\ 224206344631797460594682573103790084024432438465657245014402821885252\ 470935190620929023136493273497565513958720559654228749774011413346962\ 715422845862377387538230483865688976461927383814900140767310446640259\ 899490222221765904339901886018566526485061799702356193897017860040811\ 889729918311021171229845901641921068884387121855646124960798722908519\ 296819372388642614839657382291123125024186649353143970137428531926649\ 875337218940694281434118520158014123344828015051399694290153483077644\ 569099073152433278288269864602789864321139083506217095002597389863554\ 277196742822248757586765752344220207573630569498825087968928162753848\ 863396909959826280956121450994871701244516461260379029309120889086942\ 028510640182154399457156805941872748998094254742173582401063677404595\ 741785160829230135358081840096996372524230560855903700624271243416909\ 004153690105933983835777939410970027753472000000000000000000000000000\ 000000000000000000000000000000000000000000000000000000000000000000000\ 000000000000000000000000000000000000000000000000000000000000000000000\ 000000000000000000000000000000000000000000000000000000000000000000000\ 000000000000000
Výpočet nekonečné řady prvočísel (program si musíte zastavit sami přes CTRL+C):
2p3p[dl!d2+s!%0=@l!l^!<#]s#[s/0ds^]s@[p]s&[ddvs^3s!l#x0<&2+l.x]ds.x 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
6. Whitespace
Dalším skutečně neobvykle pojatým programovacím jazykem, na který v žádném případě nemůžeme v tomto miniseriálku zapomenout, je jazyk nazvaný Whitespace. Jméno tohoto programovacího jazyka, který byl příhodně vydán na Apríla (v roce 2003), naznačuje jeho nejdůležitější vlastnost – ve Whitespace se totiž algoritmy zapisují skutečně pouze s využitím bílých znaků mezera, tab a konec řádku, resp. přesněji řečeno jejich kombinací (konkrétní význam jednotlivých kombinací bude vysvětlen v osmé kapitole). Všechny ostatní znaky kromě bílých znaků jsou interpretrem jazyka Whitespace ignorovány, což přináší jednu velmi zajímavou vlastnost – algoritmus naprogramovaný ve Whitespace je většinou možné, a to navíc poměrně nenápadně, vložit jak do běžného textu (například v HTML se vícenásobné bílé znaky zapsané za sebou ve většině značek ignorují), tak i do algoritmu zapsaného v jiném programovacím jazyce (opět totiž platí, že většina jazyků, až na malé výjimky typu Python, bílé znaky ignoruje).
Mimochodem: povšimněte si, že je možné prakticky bez problémů smíchat zápis algoritmu naprogramovaného v minule zmíněném Brainfucku s algoritmem napsaným v jazyku Whitespace, a to z toho prostého důvodu, že Brainfuck rozlišuje pouze osmici znaků „<“, „>“, „+“, „-“, „.“ (tečka), „,“ (čárka), „[“ a „]“, přičemž ostatní znaky podle specifikace ignoruje. Přesně stejná vlastnost platí i pro kombinaci Whitespace+FALSE (ve skutečnosti jazyk FALSE již rozlišuje poněkud větší množinu znaků, včetně jednoznakových názvů proměnných, nicméně bílé znaky většina interpretrů ignoruje) či Whitespace+JSFuck (v JSFucku se používají znaky „(“, „)“, „[“, „]“, „+“ a „!“). S trochou nadsázky se tedy dá říci, že autoři těchto jazyků mysleli na vzájemnou kompatibilitu :-)
Interpretry jazyka Whitespace již byly implementovány v mnoha různých programovacích jazycích, především v C, C++, Pythonu, Perlu, Ruby, ale i Haskellu či Elispu (Emacs Lispu).
7. „Zelený“ jazyk šetrný k životnímu prostředí
Je možná poněkud zarážející, že se o programovací jazyk Whitespace více nezajímají členové různých ekologických hnutí. Díky tomu, že algoritmy psané ve Whitespace se skládají jen z mezer, tabů a konců řádků, se při tisku programů ušetří velké množství toneru a při jejich prohlížení na monitoru stejně není nic vidět, takže je možné monitor nechat vypnutý a šetřit tak elektrickou energii. Možná je právě Whitespace tím nejvhodnějším jazykem pro implementaci různých softwarových projektů postavených na EU grantech :-)
Navíc je možné bez uzardění tvrdit, že libovolný kód napsaný ve Whitespace je čistý (clean code).
8. Základní operace v jazyku Whitespace
Podívejme se nyní na to, jakým způsobem se vlastně zapisují programy v jazyce Whitespace. Začněme hodnotami, s nimiž se v programech přímo pracuje a které reprezentují (vnitřní) stav programu. Jazyk Whitespace dokáže zpracovávat pouze celá čísla se znaménkem, která mohou mít prakticky libovolný rozsah. Pro reprezentaci čísel se používá binární kód, který je doplněný o značku pro konec čísla. V následující tabulce i v tabulkách uvedených později, budeme všechny tři znaky rozpoznávané jazykem Whitespace zapisovat jejich jménem uvedeným v hranatých závorkách:
# | Znak | Význam |
---|---|---|
1 | [Space] | bit s hodnotou 0 |
2 | [Tab] | bit s hodnotou 1 |
3 | [LF] | konec zápisu čísla |
4 | [Space] | kladné znaménko (pokud je zapsána jako první znak) |
5 | [Tab] | záporné znaménko (pokud je zapsána jako první znak) |
To například znamená, že konstantu 42 můžeme zapsat do programu takto:
[Space][Tab][Space][Tab][Space][Tab][Space][LF]
Konstanta –42 bude zapsána takto:
[Tab][Tab][Space][Tab][Space][Tab][Space][LF]
Samotný programový kód se skládá ze sekvence příkazů, přičemž každý příkaz je rozdělen na prefix (zde nazývaný Instruction Modification Parameter – IMP), za nímž následuje kód konkrétní operace. Nejdříve si vypišme všechny podporované prefixy (IMP). Těch existuje pět a zajímavé je, že mají proměnnou délku jeden až dva znaky:
# | Znak(y) | Význam |
---|---|---|
1 | [Space] | operace pro manipulaci se zásobníkem |
2 | [Tab][Space] | aritmetické operace |
3 | [Tab][Tab] | přístup na haldu (heap) |
4 | [LF] | řízení běhu programu |
5 | [Tab][LF] | vstupně/výstupní operace |
Operace pro manipulaci se zásobníkem existují čtyři (ze základních operací známých například z Forthu chybí pouze rot):
# | Znaky | Operace se zásobníkem |
---|---|---|
1 | [Space] | číslo následující za příkazem je vloženo na zásobník |
2 | [LF][Space] | obdoba příkazu dup (viz kapitolu číslo 2) |
3 | [LF][Tab] | obdoba příkazu swap (viz kapitolu číslo 2) |
4 | [LF][LF] | obdoba příkazu drop (viz kapitolu číslo 2) |
Následuje tabulka se všemi (pěti) aritmetickými operacemi (oba operandy musí být nejdříve umístěny na zásobník, kam je posléze uložen i výsledek):
# | Znak(y) | Aritmetická operace |
---|---|---|
1 | [Space][Space] | součet |
2 | [Space][Tab] | rozdíl |
3 | [Space][LF] | součin |
4 | [Tab][Space] | celočíselný podíl |
5 | [Tab][Tab] | výpočet modulo |
Pro práci s haldou existují pouze dvě operace, které vždy očekávají, že adresa na haldě, kam se má provést uložení či načtení, bude na vrcholu zásobníku (načtení) či na druhé nejvyšší pozici (uložení):
# | Znak(y) | Operace |
---|---|---|
1 | [Space] | uložení hodnoty z TOS na specifikovanou adresu |
2 | [Tab] | načtení hodnoty ze specifikované adresy na TOS |
Poměrně komplikované jsou operace slouží k řízení běhu programu. Prvních pět operací očekává adresu, která je reprezentována číslem:
# | Znak(y) | Operace |
---|---|---|
1 | [Space][Space] | definice návěští (cíle skoku) |
2 | [Space][Tab] | zavolání podprogramu |
3 | [Space][LF] | nepodmíněný skok na zadané návěští |
4 | [Tab][Space] | podmíněný skok v případě, že TOS==0 |
5 | [Tab][Tab] | podmíněný skok v případě, že v TOS je záporné číslo |
6 | [Tab][LF] | návrat z podprogramu |
7 | [LF][LF] | ukončení celého programu |
Poslední skupinu operací tvoří vstupně/výstupní operace:
# | Znak(y) | Operace |
---|---|---|
1 | [Space][Space] | znak, jehož kód je uložený na TOS, je vytisknut |
2 | [Space][Tab] | číslo uložené na TOS je vypsáno |
3 | [Tab][Space] | načtení znaku a uložení na haldu |
4 | [Tab][Tab] | načtení čísla a uložení na haldu |
9. „Grafický“ jazyk Piet
Předposledním esoterickým programovacím jazykem, s nímž se v dnešním článku setkáme, je jazyk nazvaný Piet, jehož autorem je David Morgan-Mar (ostatně podívejte se i na jeho další esoterické jazyky a taktéž implementace řadicích algoritmů). Jméno tohoto velmi neobvyklého programovacího jazyka je odvozeno od jména malíře Pieta Mondriana (původně se programovací jazyk Piet měl skutečně jmenovat Mondrian, ovšem toto jméno již bylo v době návrhu obsazeno). Jazyk Piet se v určitém ohledu podobá výše zmíněnému jazyku Whitespace, protože základní příkazy, z nichž se algoritmy skládají, obsahují aritmetické operace prováděné nad operandy uloženými na zásobníku operandů, operace s prvky na zásobníku (duplikace atd.), operace pro vstup a výstup dat a konečně zde nalezneme i několik operací určených pro řízení běhu programu.

Obrázek 1: Jeden z obrazů Pieta Mondriana.
Ovšem zatímco Whitespace byl programovací jazyk, v němž se algoritmy zapisovaly lineárním způsobem (tj. příkaz po příkazu, stejně jako je tomu u psaného textu), je tomu v případě jazyku Piet zcela jinak. Kód pro jazyk Piet se totiž zapisuje do dvourozměrné mřížky, podobně jako tomu bylo v minule zmíněných jazycích nazvaných Minifuck-2D a Befunge. To mj. znamená, že podobně jako v případě Minifucku-2D, je i v jazyku Befunge nutné si kromě pozice aktuálně prováděného příkazu pamatovat i směr, ve kterém se má načíst další příkaz; tento směr je uložen ve stavové proměnné nazvané DP – Direction Pointer doplněné o stavový příznak CC – Codel Chooser (tento bitový příznak zjednodušeně řečeno volí směr vlevo–vpravo či nahoru–dolů).

Obrázek 2: Další z obrazů Pieta Mondriana.
To však zdaleka není vše – ona mřížka, do níž se zadávají jednotlivé příkazy tvořící program, je v programovacím jazyce Piet tvořena běžnou bitmapou (rastrovým obrázkem), což znamená, že se vlastně programy netvoří v textovém editoru, ale v bitmapovém grafickém editoru (může se použít například i primitivní Malování či na Linuxu mtPaint, lepší je však online IDE). Vzhledem k tomu, že se rozeznává pouze dvacet barvových odstínů (přesněji řečeno osmnáct odstínů + černá a bílá barva) a zbylé odstíny se typicky zcela ignorují (záleží na konkrétním interpretru), znamená to, že je možné zápis programu „vkreslit“ do již existujícího obrázku či naopak vytvořit společně s algoritmem i vhodný demonstrační obrázek, splashscreen atd.
10. Základní operace podporované interpretrem jazyka Piet
V předchozí kapitole jsme si řekli, že v programech (tedy v bitmapě) se rozeznává jen dvacet barevných odstínů. První dvě barvy – černá a bílá – mají speciální význam, ovšem dalších osmnáct barvových odstínů je tvořeno kombinací šesti barev (červená, žlutá, zelená, azurová, modrá a fialová) a tří úrovní (světlá/normální/tmavá). Základním stavebním prvkem programů jsou bloky pixelů s konstantní barvou. Samotné příkazy nejsou vždy zapsány stejnou barvou, ale záleží na způsobu změny barvy, tj. zda se změní barva či světlost. Co se týče změny barvy, je možné „poskočit“ o jeden až pět odstínů, přičemž například změna z červené na žlutou představuje jeden skok, ovšem změna červené na fialovou již pět skoků. V případě světlosti se může jednat o skok o jednu či dvě úrovně:

Obrázek 3: Program typu „Hello World“ naprogramovaný v jazyce Piet (autorem je James Dessart).
Jednoduše zjistíme, že s využitím tohoto způsobu kódování je možné reprezentovat osmnáct základních příkazů:
Barva/světlost | Beze změny | Jedna úroveň | Dvě úrovně |
---|---|---|---|
Beze změny | × | push | pop |
Jedna úroveň | add | sub | mul |
Dvě úrovně | div | mod | not |
Tři úrovně | gt | pointer | switch |
Čtyři úrovně | dup | roll | input number |
Pět úrovní | input char | output number | output char |
Tyto příkazy si můžeme rozdělit do několika kategorií:
# | Aritmetická operace | Význam |
---|---|---|
1 | add | součet |
2 | sub | rozdíl |
3 | mul | součin |
4 | div | podíl |
5 | mod | dělení modulo |
# | Logická operace | Význam |
---|---|---|
1 | gt | porovnání dvou hodnot na zásobníku a vrácení 0 či 1 podle jejich velikosti |
2 | not | logická negace (0 → 1, 1 → 0) |
# | Operace se zásobníkem | Význam |
---|---|---|
1 | dup | duplikace (má stejný význam jako stejně pojmenovaný příkaz ve Forthu) |
2 | roll | dokáže přesunou druhou položku na zásobníku na n-tou pozici (lze realizovat rot, swap atd.) |
3 | push | zápis čísla (zakódovaného do barev) na zásobník (to se neprovede implicitně) |
4 | pop | odstranění položky z vrcholu zásobníku |
# | Vstupně-výstupní operace | Význam |
---|---|---|
1 | input number | načtení čísla ze standardního vstupu |
2 | input char | načtení znaku ze standardního vstupu |
3 | output number | zápis čísla (z TOS) na standardní výstup |
4 | output char | zápis znaku (z TOS) na standardní výstup |
# | Řízení běhu programu | Význam |
---|---|---|
1 | pointer | načte číslo z vrcholu zásobníku o otočí DP o tolik násobků 90°, kolik odpovídá přečtenému číslu |
2 | switch | načte číslo z vrcholu zásobníku a změní příznak CC tolikrát, kolik odpovídá přečtenému číslu |
Povšimněte si, že u řízení běhu programu je nejdříve nutné vyhodnotit podmínku pomocí příkazů gt a not a posléze na základě výsledku změnit hodnotu DP příkazem pointer (otočení o n×90°) či hodnotu CC příkazem switch.
Při řízení běhu programu má speciální význam i černá barva (černé pixely), neboť ty slouží jako „zarážka“. Pokud interpret narazí na černý pixel, pokouší se měnit CC a DP tak dlouho, dokud nenarazí na barvový odstín, který může interpretovat. Jestliže se tato operace nezdaří ani na osmý pokus (vyčerpá se všech 4×2 kombinací), program skončí.
Ukázky dalších programů naleznete zde. Povšimněte se například geniálního interpretru jazyka Brainfuck.

Obrázek 4: Generátor prvočísel (autorem je Sylvain Tintillier).
11. Malbolge: vítejte ve skutečném programátorském pekle
Poslední esoterický jazyk, s nímž se dnes seznámíme, se jmenuje Malbolge. Tento jazyk je skutečně šílený (ostatně Melebolge je název osmé úrovně pekla v Dantově Božské komedii), a to šílený natolik, že je prakticky nemožné v jazyku Malbolge napsat smysluplný program. Většina programů, která až doposud byla „vyvinuta“, vznikla s využitím generátorů kódu, které tak dlouho zkoušely různé kombinace příkazů, až se dobraly ke kýženému výsledku – jedná se tak vlastně o ultimátní využití TDD (test-driven development) :-).
Zvláštní je, že virtuální stroj jazyka Malbolge je založen na ternárním kódu. To ostatně není vůbec špatný nápad, ostatně v SSSR byl minimálně jeden podobný reálný počítač realizován. Každé slovo virtuálního stroje má šířku deseti tritů, což znamená, že celkem je možné realizovat 310-1=59048 kombinací, což nám prozradí i dc:
3 10^1-pq
Vzhledem k tomu, že i adresy jsou reprezentovány jedním slovem, znamená to, že adresovat lze maximálně 59048 buněk (slov). V instrukčním kódu nalezneme i instrukci nazvanou „crazy operation“, což je vlastně rozšíření klasických bitových kombinací (ovšem ne přímočaře, to by bylo moc jednoduché, takže se spíše jedná o náhodný výběr tří tritových operací). Další zajímavostí je, že po provedení každé operace se ta buňka paměti, která je adresovaná indexem uloženým v registru C, zakóduje na základě tabulky, což je jeden ze způsobu automodifikace kódu.
12. Odkazy na Internetu
- Esolang, the esoteric programming languages wiki
https://esolangs.org/wiki/Main_Page - Esoteric Topics in Computer Programming
http://web.archive.org/web/20020609152409/www.catseye.mb.ca/esoteric/index.html - Programming Languages designed by Wouter von Oortmerssen
http://strlen.com/programming-languages - Two-dimensional languages
https://esolangs.org/wiki/Category:Two-dimensional_languages - Piet (homepage)
http://www.dangermouse.net/esoteric/piet.html - Piet (na Esolang)
https://esolangs.org/wiki/Piet - Piet IDE
http://www.rapapaing.com/blog/?page_id=6 - JSFuck (homepage)
http://www.jsfuck.com/ - JSFuck (na Esolang)
https://esolangs.org/wiki/JSFuck - JSFuck (na Wikipedii)
https://en.wikipedia.org/wiki/JSFuck - Malbolge (na Esolang)
https://esolangs.org/wiki/Malbolge - Malbolge (na Wikipedii)
https://en.wikipedia.org/wiki/Malbolge - Befunge (na Esolang)
https://esolangs.org/wiki/Befunge - Befunge (na Wikipedii)
https://en.wikipedia.org/wiki/Befunge - Minifuck
https://esolangs.org/wiki/Minifuck - XMLfuck
https://esolangs.org/wiki/XMLfuck - The False Programming Language
http://strlen.com/false-language - The FALSE Programming Language Manual
http://strlen.com/false/false.txt - Wouter van Oortmerssen
http://esolangs.org/wiki/Wouter_van_Oortmerssen - dc (computer program)
https://en.wikipedia.org/wiki/Dc_%28computer_program%29 - dc (na Esolang)
http://esolangs.org/wiki/Dc - Whitespace – tutorial
http://compsoc.dur.ac.uk/whitespace/tutorial.html - Programovací jazyk Forth a zásobníkové procesory
http://www.root.cz/clanky/programovaci-jazyk-forth-a-zasobnikove-procesory/ - Seriál Programovací jazyk Forth
http://www.root.cz/serialy/programovaci-jazyk-forth/ - Programovací jazyk Factor
http://www.root.cz/clanky/programovaci-jazyk-factor/ - Grafický metaformát PostScript
http://www.root.cz/clanky/graficky-metaformat-postscript/