Skip to content

Instantly share code, notes, and snippets.

@Nicd
Created September 8, 2017 09:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Nicd/3bdeb9ec01ca333ceade5323f50d5425 to your computer and use it in GitHub Desktop.
Save Nicd/3bdeb9ec01ca333ceade5323f50d5425 to your computer and use it in GitHub Desktop.
GTLT was a 2D Brainfuck-inspired esoteric language I came up with ages ago for fun
EThe answer to life, the
universe and everything
§v >}}++++++++++!{+!v
>+++++]}++++++v>--!Q -
- - ! -
^< ^{++++++<^-------<
++++++++++}
+++++++++}+++++++++
> o
{+++++++++++++++++
+++++++++++++++++
++++++++++++++ }
+++++++++++++++++
+++++++++++++++++
++++++++++++++ {v
^ < +{+} <
-> E01 bottle of§ v
- E
}
v}-{< b
+v {{< O}}<e
^(>!}!E bottles §ve
ov v§t no reeb foE<r
-} >Ehe wall, §{!}v
-- v§fo selttob E!<o
-- >E beer. Take §vn
-- v§ap ,nwod enoE<
->^>Ess it around§vt
-v+++++++++{-< §
- v}-{( E
->{-}} > v h
- {} e
- v§ E!}!{<<^§ ,E<
- >Ebottles of b§vw
- v§w eht no reeE<a
- >Eall.§--------vl
- v--------------<l
- >--------------v,
- v---{----------<
- >--------------v0
^--------}!{------<§
v§reeb fo elttob 1E<
>E. Take it down, pa
ss it around, no bot
tles of beer on the
wall.§{{!ENo bottles
of beer on the wall
, no bottles of beer
. Go to the store an
d buy some more, 99
bottles of beer on t
he wall.§Q
/* GTLT Parser 1.0
* C++ -version
* Compiles on Mac OS X 10.4.11
* with g++ 4.0.1
*
* All code copyright Nicd 2007
*/
#include <iostream>
#include <cstdlib>
#include <string>
#include <vector>
#include <fstream>
using namespace std;
// Random number between lowest and highest
int random_between(int lowest, int highest);
// Randomly return either of the two arguments
int random_from(int a, int b);
//! \return modified string s with spaces trimmed from left
std::string& triml(std::string& s);
//! \return modified string s with spaces trimmed from right
std::string& trimr(std::string& s);
//! \return modified string s with spaces trimmed from edges
std::string& trim(std::string& s);
int main(int argc, char *argv[]) {
const int START_STATE = 0; // Starting state for all memory cells
// Seed random generator
srand((unsigned)time(0));
char *file;
if(argc != 2) {
cerr << "Wrong parameter count!" << endl;
cerr << "Usage: gtlt /path/to/source.g" << endl;
return EXIT_SUCCESS;
}
else {
file = argv[1];
}
// Master vector holding all the line strings
vector<string> source;
string temp;
ifstream h;
h.open(file, ios::in);
if(!h) {
cerr << "Specified file does not exist!" << endl;
return EXIT_FAILURE;
}
// Load lines of file to a vector, like file() in PHP
while(getline(h, temp)) {
source.push_back(temp);
}
// Determine if line lenghts are ok (source is rectangular)
int test_length = 0;
vector<string>::iterator i;
for(i = source.begin(); i != source.end(); ++i) {
temp = *i;
if(test_length == 0) {
test_length = temp.length();
}
else if(test_length != temp.length()) {
cerr << "Source file is not rectangular!" << endl;
h.close();
return EXIT_FAILURE;
}
}
if(test_length == 0) {
cerr << "Source file seems to be empty." << endl;
h.close();
return EXIT_FAILURE;
}
h.close(); // We don't need the file anymore
// Initialize basic variables
string input = ""; // User input
char key = '0'; // User-entered char
char current_source = 0; // Current source value
int current_memory = 0; // Current memory value
bool echo = false; // Are we echoing?
bool verbose = false; // Are we verbose?
bool end_of_input = false; // Nothing more in input buffer?
int x = test_length; // Source x-size
int y = source.size(); // Source y-size
int sx = -1; // Source cursor x-coordinate
int sy = 0; // Source cursor y-coordinate
int mx = 0; // Memory cursor x-coordinate
int my = 0; // Memory cursor y-coordinate
int dir = 2; // Running direction:
// 1 = up
// 2 = right
// 3 = down
// 4 = left
// Naturally, we start going right
// Let's create the memory too!
vector< vector< int > > memory;
vector<int> temp_vector;
int r = 0;
temp_vector.reserve(x);
for(r = 0; r < x; ++r) {
temp_vector.push_back(START_STATE);
}
memory.reserve(y);
for(r = 0; r < y; ++r) {
memory.push_back(temp_vector);
}
// Enter main parsing loop
while(true) {
// Ignore user input when not asking for it
// cin.ignore(1);
// Move source cursor
switch(dir) {
case 1:
sy--;
break;
case 2:
sx++;
break;
case 3:
sy++;
break;
case 4:
sx--;
break;
default:
dir = 1;
sy--;
break;
}
// Check if out of bounds
if(sx < 0) {
sx = x - 1;
sy--;
if(sy < 0) {
sx = x - 1;
sy = y - 1;
}
}
if(sx > (x - 1)) {
sx = 0;
sy++;
if(sy > (y - 1)) {
break;
}
}
if(sy < 0) {
sx--;
sy = y - 1;
if(sx < 0) {
sx = x - 1;
sy = y - 1;
}
}
if(sy > (y - 1)) {
sx++;
sy = 0;
if(sx > (x - 1)) {
break;
}
}
// Get current source & memory values
current_source = static_cast<char>(source.at(sy).at(sx));
current_memory = memory.at(my).at(mx);
// Check current cell for commands
if(current_source == '§') {
echo = false;
continue;
}
// If in echo mode, write to stdout
if(echo) {
cout << current_source;
}
else {
switch(current_source) {
// Unconditial flow controls
case '<':
dir = 4;
break;
case '>':
dir = 2;
break;
case '^':
dir = 1;
break;
case 'v':
dir = 3;
break;
// Conditional flow controls
case '[':
if(current_memory != 0) {
dir = 4;
}
break;
case ']':
if(current_memory != 0) {
dir = 2;
}
break;
case '(':
if(current_memory == 0) {
dir = 4;
}
break;
case ')':
if(current_memory == 0) {
dir = 2;
}
break;
case 'O':
if(current_memory != 0) {
dir++;
}
break;
case 'o':
if(current_memory == 0) {
dir++;
}
break;
// Other flow controls
case '=':
dir = random_from(2, 4);
break;
case '|':
dir = random_from(1, 3);
break;
case '@':
dir = random_between(1, 4);
break;
// Memory controls
case '{':
mx--;
if(mx < 0) {
mx = x - 1;
my--;
if(my < 0) {
my = y - 1;
}
}
break;
case '}':
mx++;
if(mx > (x - 1)) {
mx = 0;
my++;
if(my > (y - 1)) {
my = 0;
}
}
break;
case 'A':
my--;
if(my < 0) {
mx--;
my = y - 1;
if(mx < 0) {
mx = x - 1;
}
}
break;
case 'V':
my++;
if(my > (y - 1)) {
mx++;
my = 0;
if(mx > (x - 1)) {
mx = 0;
}
}
break;
case '+':
memory[my][mx]++;
break;
case '-':
memory[my][mx]--;
break;
case '0':
memory[my][mx] = 0;
break;
// I/O controls
case '?':
// if(end_of_input) {
// memory[my][mx] = 0; // End of input
// end_of_input = false;
// break;
//}
if(input.length() != 0) {
memory[my][mx] = static_cast<int>(static_cast<char>(input.at(0)));
input.erase(0, 1);
//if(input.length() == 0) {
// end_of_input = true;
//}
}
else {
getline(cin, input);
input = trimr(input);
if(input.length() == 0) {
memory[my][mx] = 0;
}
else {
memory[my][mx] = static_cast<int>(static_cast<char>(input.at(0)));
input.erase(0, 1);
//if(input.length() == 0) {
// end_of_input = true;
//}
}
}
break;
case '!':
cout << static_cast<char>(current_memory);
break;
case ':':
// Enter raw mode for single character input
// This would use getch(); on Windows
system("stty raw");
cin.get(key);
if(static_cast<int>(key) == 10 || static_cast<int>(key) == 13) {
memory[my][mx] = 0;
}
else {
memory[my][mx] = static_cast<int>(key);
}
system("stty cooked");
break;
case 'E':
echo = true;
break;
// § handled elsewhere
// Other controls
case 'Q':
cout << endl;
return EXIT_SUCCESS;
case 'R':
sx = random_between(0, (x - 1));
sy = random_between(0, (y - 1));
break;
case 'C':
memory.clear();
for(r = 0; r < y; ++r) {
memory.push_back(temp_vector);
}
break;
case 'S':
for(i = source.begin(); i != source.end(); ++i) {
cout << *i << endl;
}
break;
case 'H':
cout << "Hello, world!";
break;
case 'D':
cout << "Source position: " << sx << ", " << sy << endl;
cout << "Memory position: " << mx << ", " << my << endl;
cout << "Memory cell value: " << current_memory << endl;
cout << "Input buffer: " << input << endl;
break;
case 'T':
verbose = true;
break;
}
}
// Echo stuff if we are being verbose
if(verbose) {
cout << "Source position: " << sx << ", " << sy << endl;
cout << "Memory position: " << mx << ", " << my << endl;
cout << "Current source value: " << current_source << endl;
cout << "Memory cell value: " << current_memory << endl;
cout << "Input buffer: " << input << endl;
}
}
cout << endl;
return EXIT_SUCCESS;
}
int random_between(int lowest, int highest) {
int random;
int range = (highest - lowest) + 1;
random = lowest + int(range * rand() / (RAND_MAX + 1.0));
return random;
}
int random_from(int a, int b) {
if(random_between(1, 2) == 1) {
return a;
}
else {
return b;
}
}
//! \return modified string ``s'' with spaces trimmed from left
std::string& triml(std::string& s) {
int pos(0);
for ( ; s[pos]==' ' || s[pos]=='\t'; ++pos );
s.erase(0, pos);
return s;
}
//! \return modified string ``s'' with spaces trimmed from right
std::string& trimr(std::string& s) {
int pos(s.size());
for ( ; pos && s[pos-1]==' ' || s[pos]=='\t'; --pos );
s.erase(pos, s.size()-pos);
return s;
}
//! \return modified string ``s'' with spaces trimmed from edges
std::string& trim(std::string& s) {
return triml(trimr(s));
}
GTLT spesifikaatio 1.2 (20.2.2008)
===
GTLT (c) Nicd 2006 - nykyisyys.
GTLT on esoteerinen (turha) ohjelmointikieli, jonka perimmäinen tarkoitus on olla
mahdoton lukea ja vielä mahdottomampi kirjoittaa. Sitä voi ajaa komentoriviltä
sitä varten kehitetyllä PHP-parserilla, tyyliin tähän (nyt saatavilla myös C++
-parseri):
>php parser.php 99bottles.g
Ohjelmointikieli perustuu mahtavaan Brainfuck -kieleen ja sitä voi ajatella ikään
kuin kaksiulotteisena Brainfuckina. Komentoja on kuitenkin enemmän ja toiminta
on joissain kohdin erilaista.
Ohjelmat
---
Jokainen ohjelma on suorakulmion muotoinen, ja sen kaikki neljä sivua ovat tasaisia.
Kaikki komennot ovat yhden kirjaimen mittaisia, tunnistamattomat kirjaimet
ohitetaan. Ohjelman suoritus alkaa vasemmasta ylänurkasta ja se lähtee oikealle,
ellei suuntakomentoihin törmätä. Ohjelman suoritus loppuu lopetuskomentoon tai
kun suoritus kulkee oikean alanurkan viimeisen komennon ohi.
GTLT-ohjelmat tallennetaan tiedostopäätteellä .g, tavalliseen tekstitiedostomuotoon.
Rivinvaihdot poistetaan eikä niitä katsota komennoiksi, mutta välilyönnit ja
sarkaimet (tabulator) säilyvät. Huomionarvoista on että sarkain tulkitaan vain
yhdeksi merkiksi, niin kuin se onkin.
Muisti
---
Jokaisella ohjelmalla on oma muistitaulukko, joka luodaan automaattisesti
ohjelman suorituksen alkaessa. Taulukko on täsmälleen saman kokoinen kuin
ohjelmatiedosto itse, tauluja on siis yhtä paljon kuin ohjelmassa merkkejä. Näin
ollen suuret ohjelmat kuluttavat automaattisesti paljon enemmän muistia.
Kaikkissa muistitauluissa on aluksi 0. Muistiin voi säilöä vain numeroita, mutta
luvulla taulussa ei ole ylä- tai alarajaa, muuta kuin mitä parseriohjelma tai sen
ohjelmointikieli asettaa.
Ohjelma ei liiku muistissa samaan tapaan kuin suoritettaessa lähdekoodia, vaan
muistissa liikutaan muistikomennoilla yksi taulu kerrallaan.
Ohjelmassa liikkuminen
---
Ohjelman suorituksen alkaessa parseriohjelma lähtee lukemaan tiedostoa
vasemmasta ylänurkasta normaaliin lukusuuntaan, oikealle. Kun rivin reuna
kohdataan, siirrytään seuraavalle riville, jälleen alkaen vasemmasta reunasta.
Lukusuuntaa voi kuitenkin itse muuttaa suuntakomennoilla, ja se voi olla mikä
tahansa neljästä perussuunnasta: ylös, alas, oikealle tai vasemmalle.
Kohdattaessa reuna, siirrytään aina suunnan mukaisesti seuraavalle (pysty)riville,
ja aloitetaan lukeminen päinvastaisesta reunasta. Esimerkiksi ylitettäessä alareuna,
lukeminen siirtyy seuraavaan pystyriviin oikealle, yläreunaan, kun taas ylitettäessä
yläreuna, lukeminen siirtyy seuraavaan pystyriviin _vasemmalle_, alareunaan.
Jos siirrytään jonkin ohjelman nurkan yli, eikä löydy enää seuraavaa riviä mihin
siirtyä, ohjelman suoritus jatkuu päinvastaisesta nurkasta. Tässä oikea alanurkka
on poikkeus, sen yli siirryttäessä ohjelman suoritus loppuu, sillä se tulkitaan
tiedoston lopuksi.
Komentolistaus
---
Suuntakomennot
< - Vaihda kulkusuunta vasemmalle
> - ... oikealle
^ - ... ylös
v - ... alas
Ehdolliset suuntakomennot (jos ehto ei täyty, kulku jatkuu aikaisempaan suuntaan)
[ - Jos tämänhetkinen muistitaulu ei ole nolla, vaihda suunta vasemmalle
] - ... oikealle
( - Jos tämänhetkinen muistitaulu on nolla, vaihda suunta vasemmalle
) - ... oikealle
O - Jos tämänhetkinen muistitaulu ei ole nolla, vaihda suunta 90 astetta
myötäpäivään
o - Jos tämänhetkinen muistitaulu on nolla, vaihda suunta 90 astetta myötäpäivään
Muita suuntakomentoja
= - Vaihda suunta joko oikealle tai vasemmalle satunnaisesti
| - ... ylös tai alas
@ - Vaihda suunta mihin tahansa suuntaan satunnaisesti
Muistikomennot
{ - Siirry seuraavaan muistitauluun vasemmalla
} - ... oikealla
A - ... ylhäällä
V - ... alhaalla
+ - Lisää yksi tämänhetkisen muistitaulun arvoon
- - Vähennä yksi tämänhetkisen muistitaulun arvosta
0 - Aseta tämänhetkisen muistitaulun arvoksi nolla
I/O -komennot
? - Ota syöte ja lisää sen ensimmäisen kirjaimen ASCII-arvo
tämänhetkiseen muistitauluun. Jos syötteessä on edellisestä operaatiosta jäljelle
jääneitä kirjaimia, ota niistä ensimmäinen ja poista se syötteestä (ei toimi PHP-
parserilla.)
: - Ota käyttäjältä yhden kirjaimen mittainen syöte ilman, että käyttäjän tarvitsee
painaa rivinvaihtoa. Suoritus jatkuu siis heti kun mitä tahansa tunnistettavaa
näppäintä on painettu. Jos painetaan suoraan rivinvaihtoa, se tulkitaan arvoksi
0. (Ei toimi PHP-parserilla.)
! - Tulosta tämänhetkisen muistitaulun sisältö ASCII-taulukon mukaisesti
E - Jatka kulkusuuntaan ja tulosta kaikki vastaan tulevat kirjaimet komentoja
suorittamatta kunnes tavataan tulostuksen lopetuskomento tai tiedoston loppu
§ - Tulostuksen lopetuskomento
Muut komennot
Q - Lopeta ohjelman suoritus välittömästi
R - Hyppää satunnaiseen kohtaan ohjelman lähdekoodissa ja jatka suoritusta
kulkusuuntaan
C - Tyhjennä kaikki muistitaulut (aseta nollaksi)
S - Tulosta ohjelman lähdekoodi
D - Tulosta debug-tietoa
T - Puhelias (talkative, verbose); tulosta debug-tietoa jokaisen ruudun suorituksen
jälkeen
H - Tulosta "Hello, world!"
Esimerkkejä
---
>Q
+++++]}+++++v
^ -{<
Asettaa muistitauluun, kohtaan (0,2) arvon 25. (Kertominen)
EThe answer to life, the
universe and everything
§v >}}++++++++++!{+!v
>+++++]}++++++v>--!Q -
- - ! -
^< ^{++++++<^-------<
Tulostaa:
The answer to life, the universe and everything
=42
++++++++++}
+++++++++}+++++++++
> o
{+++++++++++++++++
+++++++++++++++++
++++++++++++++ }
+++++++++++++++++
+++++++++++++++++
++++++++++++++ {v
^ < +{+} <
-> E01 bottle of§ v
- E
}
v}-{< b
+v {{< O}}<e
^(>!}!E bottles §ve
ov v§t no reeb foE<r
-} >Ehe wall, §{!}v
-- v§fo selttob E!<o
-- >E beer. Take §vn
-- v§ap ,nwod enoE<
->^>Ess it around§vt
-v+++++++++{-< §
- v}-{( E
->{-}} > v h
- {} e
- v§ E!}!{<<^§ ,E<
- >Ebottles of b§vw
- v§w eht no reeE<a
- >Eall.§--------vl
- v--------------<l
- >--------------v,
- v---{----------<
- >--------------v0
^--------}!{------<§
v§reeb fo elttob 1E<
>E. Take it down, pa
ss it around, no bot
tles of beer on the
wall.§{{!ENo bottles
of beer on the wall
, no bottles of beer
. Go to the store an
d buy some more, 99
bottles of beer on t
he wall.§Q
Tulostaa 99 pulloa olutta -laulun sanat (99-bottles-of-beer.net).
H
Tulostaa "Hello, world!"
PHP-parseri on löydettävissä osoitteesta http://stuff.nytsoi.net/gtlt/parser.php
Sitä ajetaan komentoriviltä ja se ottaa yhden argumentin, GTLT-tiedoston osoitteen.
GTLT:tä voi ajaa myös uudella C++ -parserilla (suositeltavaa), joka löytyy samasta
osoitteesta nimellä gtlt.cc. Se kääntyy muutoksitta ainakin tiedostossa mainituilla
käyttöjärjestelmillä.
v Kokonaislukujen jakolasku
>++++++++++}EAnna jaettava: §?}EAnna jakaja: §?v
v <
>++++++++ v> v
>}++++++++++]-{----{----v>]-{-{-v
^ }}< ^ }}<
v <
>VV v>AA} v>VV v>AA{ v
>{{]-V+V+v>]-AA+v>]-V+V+v>]-AA+v
^ AA< ^ VV< ^ AA< ^ VV<
v{{0}} <
> v
>]}}}+v > > v
{ >{{]{-}-} +v
{ ^ }o{{<0 >0 v
>}]-{+v
^ }<
^ <
v <
>A o v >AA v
>}V ]-V+AA-v>}}+{{0v>VV ]-AA+v
^ V< ^ VV<
v < <
>++++++++ v> v
>}}}++++++++++]-{++++{{++++v>]-{+{{+v
^ }}}< ^ }}}<
v <
>{{{{{!}}}}ETulos: §-!E, jää yli §{{!E.§Q
v Yksikaksi.g, mesierkki
>+++++++++++v>}}+++v
>{+!E, §}v>+ ]-}++v+
ov -^ <^ {++<+
^ < <
>EGTLT:llä koodaamme si
is!§Q
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment