Přinášíme ksplang,
nejlepší programovací jazyk

Založeno na metodě pánů Pejska a Kočičky

O jazyku

Jazyk byl kolektivně vytvořen řešiteli KSP v úloze 36-1-5, kde měli možnost navrhnout nezávisle na sobě jednotlivé instrukce.

Program v ksplangu se skládá z textových instrukcí oddělených whitespacem (mezery, nové řádky, …). Na velikosti znaků v zápisech instrukcí nezáleží. Jazyk ksplang je zásobníkovým jazykem. To znamená, že jedinou paměť, kterou má program k dispozici, je zásobník, který si můžete představit jako posloupnost čísel uspořádanou shora dolů. Nová čísla přidáváme na vrchol zásobníku, odebíráme opět z vrcholu.

Jazyk ksplang počítá s 64‑bitovými celými čísly se znaménkem reprezentovaných ve dvojkovém doplňku (tedy rozsah od -263 do 263-1). Přetečení čísla při výpočtu způsobí ukončení programu chybou. Zásobník má omezený počet prvků, jeho překročení taktéž ukončí program chybou.

V tomto popisu jazyka zásobník zapisujeme zleva doprava (tj. nejlevější prvek je na dně zásobníku), popřípadě mluvíme o horních či spodních prvcích zásobníku. Odebírá se tedy vždy seshora, popřípadě zprava.

Rychlý přehled instrukcí

Manipulace se zásobníkem

ID Zápis Popis
0 praise N-krát přidá "Mám rád KSP" na zásobník. více
1 pop Odebere horní hodnotu. více
2 pop2 Odebere druhou horní hodnotu. více
3 max Ponechá větší z dvou horních čísel na zásobníku. více
4 L-swap Vymění čísla na koncích zásobníku. více
5 lroll Odroluje prvních N hodnot na stacku o K pozic doprava. více
6 -ff Pokud je na vrcholu stacku 2, 4, tak nic, jinak vyprázdní stack a naplní ho celý -2^63. více
7 swap Odeber hodnotu, prohoď novou top hodnotu s hodnotou na odebraném indexu. více
8 kPi Hodnoty z desetinného rozvoje pi. více

Aritmetické

ID Zápis Popis
9 ++ Přičte k číslu na vrchu zásobníku 1. více
10 u Univerzální; přečte typ operace, a pak udělá součet/absolutní rozdíl/součin/vydělí či vymodulí/faktoriál/sign. více
11 REM Truncated division modulo (jako a % b v C). více
12 % Euklidovské modulo (jako a % abs(b) v Pythonu). více
13 tetr Tetrace. více
14 ^^ Tetrace (opačné pořadí parametrů oproti tetr). více
15 m Medián z horních k čísel, k je číslo na vrcholu zásobníku. více
16 CS Ciferný součet. více
17 lensum Suma délek dvou čísel na zásobníku. více
18 bitshift Bitový posun doleva. více
19 And Bitový AND dvou čísel. více
20 sum Součet celého stacku. více
21 gcd gcd dvou čísel. více
22 d gcd n čísel. více
23 qeq Najde celočíselná řešení kvadratické rovnice. více
24 funkcia Provede prvočíselný rozklad dvou čísel a sloučí je až na společná prvočísla, modulo 1000000007. více
25 bulkxor Přečte N a poté na N dvojic interpretovaných jako x > 0 aplikuje xor a vyrobí N nul/jedniček více

Control flow

ID Zápis Popis
26 BRZ Skok pokud je na vrcholu zásobníku nula. více
27 call Přidá IP+1 na zásobník + skok. více
28 GOTO Nepodmíněný skok. více
29 j Relativní skok. více
30 rev Skočí dopředu, otočí zásobník a vykoná blok kódu pozpátku. více

Další

ID Zápis Popis
31 SPANEK Ohodnotí úlohu dvojnásobkem bodů a jde spát… více
32 deez Načte n hodnot, přeloží je na instrukce a vyhodnotí podprogram, poté přidá výsledný zásobník na konec programu. více

Detailní popis instrukcí

praise - Mám rád KSP

Nahradí číslo n na vrchu zásobníku n-krát po sobě jdoucí hláškou Unicode znaky „Mám rád KSP“.

Pro záporné n skončí chybou.

Příklad:

1 -> 77 225 109 32 114 225 100 32 75 83 80

pop - odebrání

Odebere vrchní hodnotu ze zásobníku.

Příklad:

1 2 3 -> 1 2

pop2 - odebrání druhého čísla

Odebere druhou hodnotu odshora zásobníku.

Příklad:

1 2 3 4 -> 1 2 4

L-swap - prohození začátku a konce

Prohodí číslo na začátku a na konci zásobníku.

Pokud je zásobník prázdný, nic se nestane.

Příklad:

1 2 3 4 -> 4 2 3 1

lroll - rolování pole

Odebere ze zásobníku 2 čísla - n a x. Následně vezme dalších n prvků, posune je o x směrem k vrcholu zásobníku a prvních x prvků je posunuto na pozice n .. n - x.

Pokud na zásobníku není alespoň n prvků, program skončí chybou. Pro záporná n program skončí chybou. Pro n = 0 nebo x = 0 nedojde k žádnému posunu.

Pro záporná x operace rotuje prvky druhým směrem. Pro x větší než n či menší než -n dojde k rotaci několikrát (tedy dojde k rotaci o x mod n).

Příklad:

pro 1 2 3 4 1 4 provede rolování 4 prvků o 1, výsledek je tedy 4 1 2 3

pro 1 2 3 4 -1 4 provede rolování 4 prvků o 1 na druhou stranu, výsledek je tedy 2 3 4 1

pro 0 1 2 3 4 2 4 provede rolování 4 prvků o 2, výsledek je tedy 0 3 4 1 2

-ff

Pokud na vrchu zásobníku nejsou čísla 2 a pak 4, tak vyprázdní celý zásobník a kompletně jej naplní hodnotami  − 263 (nejmenší int64)

Vyžaduje, aby na zásobníku byly alespoň dvě hodnoty, jinak program skončí chybou.

swap - adresovaný swap

Odebere z vrchu zásobníku hodnotu i a další hodnotu prohodí s hodnotou na indexu i. Indexuje od nuly od začátku zásobníku.

Pro záporná i skončí chybou. Pro příliš velká i (odkazující mimo zásobník) skončí chybou.

Příklad:

1 2 3 4 5 6 7 8 3 -> 1 2 3 8 5 6 7 4

kPi

Prohledá zásobník odshora a první hodnota k, která zároveň leží na indexu k je nahrazena k-tým číslem z desetinného rozvoje π. Zásobník indexujeme od začátku od nuly, π indexujeme také od nuly, nulté číslo je 3, první 1, druhé 4, atd. Pokud na zásobníku žádné odpovídající k nenajde, nahradí za číslice π celý zásobník.

Příklad:

1 2 3 4 5 -> 3 1 4 1 5

2 2 2 2 2 -> 2 2 4 2 2

0 1 2 3 4 -> 0 1 2 3 5

++ - inkrementace

Nahradí číslo navrchu zásobníku o jedna větším číslem.

Pokud na zásobníku není alespoň 1 číslo, skončí chybou.

Příklad:

3 -> 4

max

Z vrchních dvou čísel ponechá na zásobníku jen to větší.

Pokud na zásobníku nejsou alespoň 2 čísla, skončí chybou.

Příklad:

4 2 -> 4

u - Univerzální aritmetická instrukce

Odebere ze zásobníku identifikátor instrukce. Pokud je

  • 0: nahradí další dvě čísla jejich součtem
  • 1: nahradí další dvě čísla absolutní velikostí jejich rozdílu
  • 2: nahradí další dvě čísla jejich součinem
  • 3: vydělí další dvě čísla – to, které bylo v zásobníku víc nahoře, tím, které bylo víc dole
    • pokud je podíl celočíselný, nahradí je podílem
    • pokud není, nahradí je zbytkem po dělení (jako % v jazyce C)
  • 4: načte další číslo v zásobníku a nahradí ho faktoriálem jeho absolutní hodnoty.
  • 5: načte další číslo v zásobníku a nahradí ho
    • -1, pokud bylo záporné
    • 0, pokud bylo 0
    • 1, pokud bylo kladné.
  • Jinak končí program s chybou

REM - truncated division modulo

Nahradí vrchní 2 čísla zbytkem po dělení prvního čísla druhým číslem.

Pro (nejen) záporná čísla se chová jako Céčkový remainder operator % – zachová záporné znaménko ale jinak se chová stejně jako pro kladná čísla.

Příklad:

3 1 -> 1

-3 1 -> 1

3 -1 -> -1

-3 -1 -> -1

% - Euklidovské modulo

Nahradí vrchní 2 čísla zbytkem po dělení prvního čísla druhým číslem.

Pro záporná čísla se chová jako Pythoní a % abs(b) – výstup je vždy kladné číslo.

Poznámka: Doporučujeme prozkoumat článek Modulo na anglické wikipedii o různých definicích pro záporná čísla a také tabulku ve stejném článku o tom, které jazyky používají kterou definici.

Příklad:

3 1 -> 1

-3 1 -> 1

3 -1 -> 2

-3 -1 -> 2

tetr - tetrace

Nahradí vrchní 2 čísla jejich tetrací. První číslo je umocňované číslo, druhé je počet opakování.

Pro počet opakování 0 je výsledkem číslo 1. Pro záporný počet opakování program skončí chybou.

^^ - tetrace

Nahradí vrchní 2 čísla jejich tetrací. První číslo je počet opakování, druhé je umocňované číslo.

Pro počet opakování 0 je výsledkem číslo 1. Pro záporný počet opakování program skončí chybou.

m - medián

Přidá na zásobník medián z vrchních x čísel, kde x je poslední číslo na zásobníku (medián se samozřejmě počítá i z čísla x). Pokud je x sudé, tak je mediánem průměr z dvou prostředních čísel, zaokrouhlený na celé číslo směrem k nule.

Pokud x není kladné, program skončí chybou.

CS - ciferný součet

Přidá na zásobník ciferný součet vrchního čísla (v desítkové soustavě).

Příklad:

18 -> 18 9

lensum

Nahradí vrchní 2 čísla součtem jejich délek.

Délka je definovaná jako počet celočíselných dělení 10 se zaokrouhlením k nule, které je potřeba udělat, než se dojde k 0. Ekvivalentně jde o počet znaků při zapsání absolutní hodnoty čísla jako textový řetězec v desítkové soustavě, s výjimkou 0, pro kterou je délka 0.

Příklad:

0 0 -> 0

3 2 -> 2

-3 2 -> 2

-22 22 -> 4

bitshift - bitový posun doleva

Nahradí vrchní 2 čísla bitovým posunem druhého čísla doleva o počet bitů rovným prvnímu číslu. Upozorňujeme, že záporná čísla jsou reprezentována metodou two’s complement. Pokud tedy posunete jedničku do prvního (nejlevějšího) bitu, dostanete záporné číslo.

Přetečení v této instrukci neukončí program, bity prostě zmizí. Pokud je první číslo (počet bitů) záporné, program skončí chybou.

Příklad:

2 1 -> 4

3 1 -> 6

And - bitový AND dvou čísel

Nahradí vrchní 2 čísla výsledkem bitové operace AND mezi těmito dvěma čísly. Upozorňujeme, že záporná čísla jsou reprezentována metodou two’s complement.

Příklad:

5 3 -> 1

sum - suma zásobníku

Nahradí celý zásobík součtem všech hodnot.

Výsledná suma se jako vždy musí vejít do 64-bitového signed integeru. Mezivýsledky se vejít nemusí.

„Každý správný dort je potřeba pořádně promíchat.“

gcd - největší společný dělitel 2 čísel

Nahradí vrchní 2 čísla jejich největším společným dělitelem.

Výsledek je vždy kladný. Pokud se výsledek nevejde do znaménkového 64-bitového integeru, program skončí chybou (jmenovitě gcd(0, -2^63) = 2^63, což je o jedna větší než maximální hodnota).

d - největší společný dělitel n čísel

Odebere číslo n ze zásobníku. Pak nahradí dalších n čísel jejich největším dělitelem.

Pokud n není kladné, program skončí chybou.

Výsledek je vždy kladný. Pokud se výsledek nevejde do znaménkového 64-bitového integeru, program skončí chybou (jmenovitě gcd(0, -2^63) = 2^63).

qeq - řešení kvadratické rovnice

Instrukce řeší kvadratickou rovnici ax2 + bx + c = 0 ze zadaných parametrů a, b, c pro celočíselné x. Tři koeficienty jsou ze zásobníku odstraněny a jsou přidány všechna celočíselná řešení (seřazené od nejmenšího). Pokud rovnice celočíselné řešení nemá, na zásobník se nic nepřidá; pokud má jeden kořen přidá se jedno číslo, a pokud má dva celočíselné kořeny tak se přidají dvě čísla.

funkcia

Odoberie 2 čísla zo zásobníka a obe rozloží na prvočísla. Následne z rozkladov zmaže všetky prvočísla, ktoré delia obe dve čísla. Výsledkom je súčin všetkých ostatných prvočísel vrátane exponentov, modulo 1 000 000 007. Pokiaľ je množina prvočísel prázdna, výsledkom je nula.

Napríklad pre čísla 100 = 2^2 * 5^2 a 54 = 3^3 * 2 sa zmaže 2, pretože je v rozkladoch oboch čísel. Zostane nám 5^2 * 3^3, teda 675. Modulo miliarda sedem pre takto malé číslo nič nezmení.

bulkxor - hromadný XOR

Instrukce odebere z vrcholu zásobníku číslo n. Poté odebere 2n čísel, každé odebrané číslo interpretuje jako 1 pokud je větší než 0, nebo 0, pokud je nula či menší. Poté provede na každou dvojici čísel operaci XOR a výsledky přidá na zásobník.

Příklad:

1 -1 3 3 2 -> 1 0

n je 2, první dvojice 3, 3 se interpretuje jako 1 xor 1 = 0. Druhá dvojice 1, -1 se interpretuje jako 1 xor 0 = 1.

BRZ - branch if zero

Přečte (nesmaže) ze zásobníku číslo c. Pokud je c rovno nula, přečte (opět nesmaže) ještě druhé číslo i a skočí na instrukci i, v opačném případě pokračuje další instrukcí. Instrukce jsou indexované od nuly.

Všechny control flow instrukce končí s chybou, pokud je adresa skoku mimo rozsah programu. Minimum je 0, maximum je délka programu - 1.

Pokud program běží pozadu, nic se nemění.

Příklad:

Se zásobníkem 0, 0 program brz vyrobí nekonečný cyklus.

Se zásobníkem 0, 1 program brz skončí normálně a nijak nezmění zásobník.

call - uložení IP + skok

Přečte ze zásobníku i a přidá na zásobník index následující instrukce (IP + 1). Následně skočí na i.

Všechny control flow instrukce končí s chybou, pokud je adresa skoku mimo rozsah programu. Minimum je 0, maximum je délka programu - 1.

Pokud program běží pozadu, ukládá se IP - 1.

GOTO - absolutní skok

Přečte hodnotu i ze zásobníku a skočí na instrukci i.

Všechny control flow instrukce končí s chybou, pokud je adresa skoku mimo rozsah programu. Minimum je 0, maximum je délka programu - 1.

Pokud program běží pozadu, nic se nemění.

j - relativní skok

Přečte číslo z vrchu zásobníku a skočí o specifikovaný počet instrukcí po směru vykonávání programu. Pokud je číslo záporné skáče proti směru vykonávání programu. Nula je následující instrukce, -1 je zpět na tuto instrukci, 1 jednu instrukci přeskočí, atd.

Všechny control flow instrukce končí s chybou, pokud je adresa skoku mimo rozsah programu. Minimum je 0, maximum je délka programu - 1.

rev

Nejdříve spočítá vzdálenost relativního skoku n (viz další odstavec). Poté skočí o n instrukcí dále, otočí celý zásobník a začne vykonávat program v opačném směru. Jakmile dorazí opět k instrukci rev, ze které skočil, vrátí se do původního směru a pokračuje vykonáváním instrukce, která je za tou, na kterou jsme původně skočili (tedy n+1-té instrukce po rev). Narozdíl od instrukce j odpovídá této instrukci rev hodnota 0, instrukci následující za revem hodnota 1, a tak dále. Návratová instrukce n+1 musí v čase vykonání této instrukce existovat, a to i když by se program měl ukončit před návratem do revu.

Pro výpočet n se využívají dvě nebo tři hodnoty ze zásobníku, které jsou odebrány. První odebraná hodnota je a, druhá odebraná hodnota je b, a pokud se a nerovná nule, tak se ještě odebere třetí hodnota c. Tyto dvě či tři využité hodnoty nesmí být záporné, jinak dojde k ukončení programu chybou. Pokud se a nerovná nule, tak n je největším z celočíselných nezáporných výsledků kvadratické rovnice ax2 + bx + c = 0. Pokud žádný takový výsledek neexistuje, b se stává hodnotou n. Pokud se a rovná nule (a tedy by nešlo o kvadratickou rovnici), b se stává hodnotou n.

Instrukce rev je možné vnořovat. Druhá instrukce rev tedy zase povede k běhu dopředu, a tak dále. K ukončení bloku dojde pouze při navštívení poslední neukončené rev instrukce (jako na zásobníku). Bloky rev je tedy nutno ukončovat ve stejném pořadí jako byly zahájeny, jinak se vykonávaná instrukce rev chová jako normální instrukce a začne nový otočený blok. Je povoleno ukončit program i bez ukončení všech bloků.

Pokud program běží pozadu, standardní instrukce místo přičtení 1 k IP od něj 1 odečítají. Relativní skoky taktéž probíhají v opačném směru. Instrukce call ukládá IP - 1. Pokud program takto dojde na instrukci -1, normálně skončí.

Všechny control flow instrukce končí s chybou, pokud je adresa skoku mimo rozsah programu. Minimum je 0, maximum je délka programu - 1. V tomto případě je navíc potřeba, aby návratová instrukce existovala už při vykonání rev.

Příklad:

Na zásobníku je 1, 2, 3, 4, 2, 0. Program je rev ++ pop pop. Vypočítaná vzdálenost skoku n je 2, tedy skočíme na první instrukci pop. Zásobník se otočí na 4, 3, 2, 1 a program se začne vykonávat v opačném směru, proběhne tedy pop a poté increment, a zásobník je 4, 3, 3. Další instrukce je (poslední aktivní) rev, tedy dojde opět k otočení zásobníku na 3, 3, 4 a skoku na návratovou instrukci pop, od které už se program vykonává zase směrem dopředu. Výsledný zásobník tedy bude 3, 3.

Směr vykonávání programu s instrukcí rev

SPANEK

Ohodnotí tuto úlohu dvojnásobkem možných bodů a pak se na neurčitou dobu uspí.

Upozorňujeme, že skončí-li program chybou či nedoběhne včas, tak nedojde k přidělení žádných bodů.

deez

Odebere ze zásobníku číslo n. Poté odebere n prvků, přeloží je pomocí ID v tabulce rychlého přehledu instrukcí na samostatný program (v pořadí odshora zásobníku).

Tento podprogram je poté vyhodnocen s prázdným zásobníkem, a po jeho skončení se výsledné hodnoty na zásobníku přeloží na instrukce, které jsou přidány na konec původního programu.

Pokud je nějaká hodnota mimo rozsah ID instrukcí, program skončí chybou.