Hlavní navigace

Programovací jazyky z vývojářského pekla++

3. 5. 2016
Doba čtení: 22 minut

Sdílet

 Autor: Shutterstock.com
Ve druhém článku o esoterických jazycích se seznámíme s jazykem v nástroji dc, dále pak s Whitespace a zmíníme se o „grafickém“ jazyku Piet či o asi nejšílenějším jazyku vůbec: Malbolge.

Obsah

1. Programovací jazyky z vývojářského pekla++

2. Jazyky, v nichž se intenzivně používají zásobníky

3. dc

4. Proměnné a řetězce v utilitě dc

5. Makra a tvorba složitějších programů v dc

6. Whitespace

7. „Zelený“ jazyk šetrný k životnímu prostředí

8. Základní operace v jazyku Whitespace

9. „Grafický“ jazyk Piet

10. Základní operace podporované interpretrem jazyka Piet

11. Malbolge: vítejte ve skutečném programátorském pekle

12. Odkazy na Internetu

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é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) :-).

Cloud23

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

  1. Esolang, the esoteric programming languages wiki
    https://esolangs.org/wiki/Main_Page
  2. Esoteric Topics in Computer Programming
    http://web.archive.org/web/20020609152409/www­.catseye.mb.ca/esoteric/in­dex.html
  3. Programming Languages designed by Wouter von Oortmerssen
    http://strlen.com/programming-languages
  4. Two-dimensional languages
    https://esolangs.org/wiki/Category:Two-dimensional_languages
  5. Piet (homepage)
    http://www.dangermouse.net/e­soteric/piet.html
  6. Piet (na Esolang)
    https://esolangs.org/wiki/Piet
  7. Piet IDE
    http://www.rapapaing.com/blog/?pa­ge_id=6
  8. JSFuck (homepage)
    http://www.jsfuck.com/
  9. JSFuck (na Esolang)
    https://esolangs.org/wiki/JSFuck
  10. JSFuck (na Wikipedii)
    https://en.wikipedia.org/wiki/JSFuck
  11. Malbolge (na Esolang)
    https://esolangs.org/wiki/Malbolge
  12. Malbolge (na Wikipedii)
    https://en.wikipedia.org/wi­ki/Malbolge
  13. Befunge (na Esolang)
    https://esolangs.org/wiki/Befunge
  14. Befunge (na Wikipedii)
    https://en.wikipedia.org/wiki/Befunge
  15. Minifuck
    https://esolangs.org/wiki/Minifuck
  16. XMLfuck
    https://esolangs.org/wiki/XMLfuck
  17. The False Programming Language
    http://strlen.com/false-language
  18. The FALSE Programming Language Manual
    http://strlen.com/false/false.txt
  19. Wouter van Oortmerssen
    http://esolangs.org/wiki/Wou­ter_van_Oortmerssen
  20. dc (computer program)
    https://en.wikipedia.org/wi­ki/Dc_%28computer_program%29
  21. dc (na Esolang)
    http://esolangs.org/wiki/Dc
  22. Whitespace – tutorial
    http://compsoc.dur.ac.uk/whi­tespace/tutorial.html
  23. Programovací jazyk Forth a zásobníkové procesory
    http://www.root.cz/clanky/programovaci-jazyk-forth-a-zasobnikove-procesory/
  24. Seriál Programovací jazyk Forth
    http://www.root.cz/serialy/pro­gramovaci-jazyk-forth/
  25. Programovací jazyk Factor
    http://www.root.cz/clanky/programovaci-jazyk-factor/
  26. Grafický metaformát PostScript
    http://www.root.cz/clanky/graficky-metaformat-postscript/

Autor článku

Pavel Tišnovský vystudoval VUT FIT a v současné době pracuje ve společnosti Red Hat, kde vyvíjí nástroje pro OpenShift.io.