Kirjautuminen

Haku

Tehtävät

Keskustelu: Projektit: Brainfuck-tulkki

Sivu 1 / 1

Sivun loppuun

Metabolix [26.08.2006 22:54:40]

Lainaa #

Tämä on hyvin yksinkertainen tulkki ohjelmointikielelle nimeltä brainfuck. Ylimääräisiä ominaisuuksia ei juurikaan ole, ei edes tarkistusta muistialueen ylityksistä tai muusta vastaavasta. Toiminnan pitäisi olla hyvinkin selkeä ja koodi varmasti kommentoi itse itsensä. Lisätietoja itse kielestä saa brainfuck-oppaasta.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MUISTIN_KOKO 30000

int tiedostoja = 0;
char ** tiedostolista = 0;
FILE * tiedosto = 0;
char * ohjelma = 0;
char muisti[MUISTIN_KOKO];

void tulosta_ohjeet(char * ajonimi);
int tiedosto_on_olemassa(char * nimi);
void lisaa_tiedosto_listaan(char * nimi);
int kelpo_merkki(char merkki);
int lataa_ohjelma(char * nimi);
int tarkista_syntaksi(char * nimi);
void aja_ohjelma(char * nimi);
void lopetusfunktio();

int main(int argumenttien_maara, char **argumentit)
{
	int i;

	atexit(lopetusfunktio);

	if (argumenttien_maara < 2) {
		tulosta_ohjeet(argumentit[0]);
		return EXIT_FAILURE;
	}

	for (i = 1; i < argumenttien_maara; ++i) {
		if (tiedosto_on_olemassa(argumentit[i])) {
			lisaa_tiedosto_listaan(argumentit[i]);
		}
		else {
			printf("Virhe! Tiedostoa %s ei ollut tai sen avaaminen ei onnistunut!\n", argumentit[i]);
			tulosta_ohjeet(argumentit[0]);
			return EXIT_FAILURE;
		}
	}

	for (i = 0; i < tiedostoja; ++i) {
		if (lataa_ohjelma(tiedostolista[i])
		&& tarkista_syntaksi(tiedostolista[i])) {
			aja_ohjelma(tiedostolista[i]);
		}
	}

	return EXIT_SUCCESS;
}

void tulosta_ohjeet(char * ajonimi)
{
	printf("Brainfuck-tulkki 0.1\nToiminta: %s tiedostot\n", ajonimi);
}

int tiedosto_on_olemassa(char * nimi)
{
	tiedosto = fopen(nimi, "r");
	if (!tiedosto) {
		return 0;
	}
	fclose(tiedosto);
	return 1;
}

void lisaa_tiedosto_listaan(char * nimi)
{
	/* Laajennetaan listaa */
	++tiedostoja;
	tiedostolista = (char**)realloc(tiedostolista, tiedostoja * sizeof(char*));
	if (!tiedostolista) {
		printf("Muisti loppui kesken tiedostolistan luonnin!\n");
		exit(EXIT_FAILURE);
	}

	/* Varataan muistia ja laitetaan sinne tiedoston nimi */
	tiedostolista[tiedostoja - 1] = (char*) malloc(strlen(nimi) + 1);
	if (!tiedostolista[tiedostoja - 1]) {
		printf("Muisti loppui kesken tiedostolistan luonnin!\n");
		exit(EXIT_FAILURE);
	}
	strcpy(tiedostolista[tiedostoja - 1], nimi);
}

int kelpo_merkki(char merkki)
{
	if (merkki == '<') return 1;
	if (merkki == '>') return 1;
	if (merkki == '+') return 1;
	if (merkki == '-') return 1;
	if (merkki == '.') return 1;
	if (merkki == ',') return 1;
	if (merkki == '[') return 1;
	if (merkki == ']') return 1;
	return 0;
}

int lataa_ohjelma(char * nimi)
{
	size_t koko;
	int i, j;

	tiedosto = fopen(nimi, "r");

	if (!tiedosto) {
		printf("Virhe! Tiedostoa '%s' ei saatu auki!\n", nimi);
		exit(EXIT_FAILURE);
	}

	/* Mitataan tiedosto */
	fseek(tiedosto, 0, SEEK_END);
	koko = ftell(tiedosto);
	fseek(tiedosto, 0, SEEK_SET);

	/* Vapautetaan vanha koodi ja varataan uusi */
	if (ohjelma) {
		free(ohjelma);
	}
	ohjelma = (char*)malloc(koko + 1);
	if (!ohjelma) {
		printf("Virhe! Ohjelmalle tiedostossa '%s' ei saatu muistia!\n", nimi);
		exit(EXIT_FAILURE);
	}

	/* Luetaan koko tiedosto */
	if (fread(ohjelma, koko, 1, tiedosto) != 1) {
		printf("Virhe! Tiedostoa '%s' ei saatu luettua!\n", nimi);
		exit(EXIT_FAILURE);
	}
	ohjelma[koko] = 0;

	/* Poistetaan ohjelmaan kuulumattomat merkit */
	for (i = j = 0; ohjelma[i] != 0; ++i) {
		if (kelpo_merkki(ohjelma[i])) {
			ohjelma[j] = ohjelma[i];
			++j;
		}
	}
	ohjelma[j] = 0;

	return 1;
}

int tarkista_syntaksi(char * nimi)
{
	int i, sulkuja;

	if (!ohjelma) {
		return 0;
	}

	/* Lasketaan, että sulkuja on saman verran molempiin suuntiin */
	for (i = sulkuja = 0; ohjelma[i] != 0; ++i) {
		if (ohjelma[i] == '[') {
			++sulkuja;
		}
		else if (ohjelma[i] == ']') {
			--sulkuja;
		}
	}

	if (sulkuja != 0) {
		printf("Tiedoston '%s' syntaksissa on virhe!\n", nimi);
	}

	return sulkuja == 0;
}

void aja_ohjelma(char * nimi)
{
	int kohta = 0, sulkuja;
	char *osoitin = muisti;

	if (!ohjelma) {
		return;
	}

	printf("Ajetaan '%s'\n", nimi);

	/* Nollataan muisti */
	memset(muisti, 0, MUISTIN_KOKO);

	/* Tekstin lopussa on "nollamerkki", toistetaan siihen asti */
	while (ohjelma[kohta] != 0) {
		switch (ohjelma[kohta]) {
			case '>':
				++osoitin;
				break;

			case '<':
				--osoitin;
				break;

			case '+':
				++*osoitin;
				break;

			case '-':
				--*osoitin;
				break;

			case '.':
				putchar(*osoitin);
				fflush(stdout);
				break;

			case ',':
				*osoitin = getchar();
				break;

			case '[':
				if (*osoitin == 0) {
					/* Haetaan vastaava lopetussulku */
					sulkuja = 1;
					while (sulkuja) {
						++kohta;
						if (ohjelma[kohta] == '[') {
							++sulkuja;
						}
						else if (ohjelma[kohta] == ']') {
							--sulkuja;
						}
					}
				}
				break;

			case ']':
				if (*osoitin != 0) {
					/* Haetaan vastaava aloitussulku */
					sulkuja = 1;
					while (sulkuja) {
						--kohta;
						if (ohjelma[kohta] == ']') {
							++sulkuja;
						}
						else if (ohjelma[kohta] == '[') {
							--sulkuja;
						}
					}
				}
				break;
		}
		/* Koodin seuraavaan merkkiin */
		++kohta;
	}
	printf("\nLoppu.\n");
}

void lopetusfunktio()
{
	int i;
	if (tiedostolista) {
		for (i = 0; i < tiedostoja; ++i) {
			if (tiedostolista[i]) {
				free(tiedostolista[i]);
			}
		}

		free(tiedostolista);
	}

	if (ohjelma) {
		free(ohjelma);
	}

	if (tiedosto) {
		fclose(tiedosto);
	}
}

Qman [17.10.2006 13:55:33]

Lainaa #

saisko QB:lle, tai EXE:kin kelpaa...

Codeprofile [10.12.2006 10:23:24]

Lainaa #

Mulla se sulkeutuu samantien käynnistyksen jälkeen.

EDIT: No nyt suoritin sen ohjelman suoraan komentorivin avulla. Tässä lukee vaan että "Brainfuck-tulkki 0.1".

Metabolix [10.12.2006 21:34:34]

Lainaa #

Kyllä siinä varmasti lukee myös sen käyttöohje. Et vain taida osata.

Codeprofile [13.12.2006 19:15:42]

Lainaa #

En mä ainakaan nää ruudulla mitään muuta kun vain että "Brainfuck-tulkki 0.1". Olen koklannut montaa eri tapaa, muutakun ei siihen DOS-ikkunaan tule muuta.

Metabolix [14.12.2006 05:11:01]

Lainaa #

@k-piste:~/ohjelmointi$ i586-mingw32msvc-gcc bft.c -o bft.exe -O2
@k-piste:~/ohjelmointi$ wine bft.exe
Brainfuck-tulkki 0.1
Toiminta: bft.exe tiedostot
@k-piste:~/ohjelmointi$ wine bft.exe koe.txt
Ajetaan 'koe.txt'
Oho, toimi. :o
Loppu.

Aivan uunituore käännös suoraan tuosta koodista.

osku91 [22.12.2006 21:03:39]

Lainaa #

mullaki toi ohjelma vaan vilahtaa.

Metabolix [22.12.2006 21:44:27]

Lainaa #

Ajaisit komentoriviltä, kuten tekstiohjelmat kuuluu.

osku91 [22.12.2006 22:48:19]

Lainaa #

mutta se vain ilmoittaa:
Brainfuck-tulkki 0.1
Toiminta: C:\brainfuck\brainfuck.exe tiedostot

Metabolix [22.12.2006 23:00:42]

Lainaa #

Koettakaa nyt tajuta, että se on tulkki, ei IDE! Se vain tulkkaa kooditiedoston, se ajetaan juuri tuon ohjeen mukaisesti. Koodin voi kirjoittaa vaikka Muistiolla.

osku91 [22.12.2006 23:04:01]

Lainaa #

joo sen tajusin muuta millä tiedostonimellä se tiedosto pitää oikeen tallentaa? en löydä sitä mistään tuolta koodista!

Metabolix [22.12.2006 23:10:12]

Lainaa #

Tiedostonimi annetaan parametrina, kuten tuosta ohjeesta nähdäkseni varsin selvästi ilmenee ja kuten näkee tuossa aiemmassa kommentissani olevasta esimerkistä.

osku91 [22.12.2006 23:11:55]

Lainaa #

mikä kumma on parametri?

Metabolix [22.12.2006 23:14:47]

Lainaa #

Kun tuollaista kyselet, niin brainfuck ei taida olla sinulle alkuunkaan oikea kieli. ^^

osku91 [22.12.2006 23:17:29]

Lainaa #

no kyllä nyt haluaisin silti tietää miten saan tuon toimimaan

Blaze [22.12.2006 23:18:31]

Lainaa #

Samalla tavallahan tuo toimii ku mikä tahansa muuki komentoriviohjelma.

Metabolix [22.12.2006 23:19:54]

Lainaa #

Enkö juuri kehottanut katsomaan tuosta vähän ylempää?

Metabolix kirjoitti:

@k-piste:~/ohjelmointi$ wine bft.exe koe.txt
Ajetaan 'koe.txt'

Selvästikin siis (käytännön syistä Linuxissa winellä) ajetaan bft.exe parametrilla koe.txt, tuloksena tulostuu teksti "Ajetaan 'koe.txt'". Voisiko tästä ehkä päätellä jotakin?

osku91 [22.12.2006 23:21:22]

Lainaa #

ai siis että sen tiedoston pitää olla koe.txt? miksi sitten kun laitoin sen nimeksi koe.txt heti aluksi ennen kuin edes kysyin täältä mitään niin se ei toiminut?

Metabolix [22.12.2006 23:34:50]

Lainaa #

Tallenna se minun puolestani vaikka porkkanaksi ja aja "brainfuck porkkana".

On selvä esimerkki, selviä ohjeita ja Google. Et voinut mitenkään itse selvittää, miten komentoriviohjelma toimii ja mikä on parametri? Ja johan minäkin sen tuossa edellisessä selitin, tottahan sinä sen siitä osaat lukea?

#ohjelmointiputka@IRCnet kirjoitti:

23°08'42" <@Blaze> varmaan saa paljo irti brainfuckista, jos ei osaa edes ottaa selvää, miten komentoriviohjelmat toimii

teppuli [01.07.2007 13:32:10]

Lainaa #

mul tulee aina:
C:\Dev-Cpp>brainfuck.exe testi.txt
Virhe! Tiedostoa 'testi.txt' ei saatu luettua!

Metabolix [04.08.2007 02:49:26]

Lainaa #

No kai sinulla on sellainen tiedosto ja oikeassa paikassa? Kyllä tuon pitäisi toimia, tarkistapa vielä...

pienipoika [29.06.2008 02:34:47]

Lainaa #

:DDD hajoon aina ku luen tätä keskustelua tässä koodivinkissä. mikä ihme siinä on niin vaikeeta :D

vehkis91 [07.07.2008 20:16:46]

Lainaa #

Sitä minäkin ihmettelin. Siis mikä siin on niin vaikeeta? :P

Grandi [09.03.2009 21:55:37]

Lainaa #

Itse olisin arvostanut dynaamista muistia noissa muuttujissa. Lisäksi tuon koodin olisi voinut pilkkoa pienellä parserilla ennen tulkkausta, nostaisi nopeutta melkoisesti. Muuten ihan jees.

Metabolix [09.03.2009 22:38:12]

Lainaa #

grandi kirjoitti:

Itse olisin arvostanut dynaamista muistia noissa muuttujissa.

Tarkoitatko muistitaulua? BF:n spekseissä (ainakin kaikissa löytämissäni versioissa) sanotaan, että muistia on 30000 tavua, joten näin tulkki toimii.

grandi kirjoitti:

Lisäksi tuon koodin olisi voinut pilkkoa pienellä parserilla ennen tulkkausta, nostaisi nopeutta melkoisesti.

Tietenkin voisi. Vinkin ei ole kuitenkaan tarkoitus olla valmis, huipputasoinen tulkki vaan yksinkertainen esimerkki ja erityisesti tuki BF-oppaalle. Sitäpaitsi et varmasti jaksa tehdä BF:llä niin isoa ohjelmaa, että sen suoritusnopeudella olisi oikeasti merkitystä. ;)

Olen kyllä aloittanut (ihan alkuun vain) joskus myös BF-kääntäjän, joka jäsentää koodin ja tuottaa siitä aavistuksen optimaalisempaa C:tä. Se voi muuttaa esimerkiksi pätkän "+>+++>++>-<<<+++" seuraavaan muotoon:

p[0] += 4;
p[1] += 3;
p[2] += 2;
p[3] -= 1;

Eki Lehtimäki [29.01.2010 00:10:50]

Lainaa #

teppuli kirjoitti:

mul tulee aina:
C:\Dev-Cpp>brainfuck.exe testi.txt
Virhe! Tiedostoa 'testi.txt' ei saatu luettua!

Itse kohtasin täsmälleen saman ongelman: jos lähdekooditiedosto oli tyhjä tekstitiedosto tai sisälsi yhdenkin rivivaihdon, sain saman virheilmoituksen kuin teppuli. Yhden rivin tiedostot toimivat hyvin. Voisiko ongelma olla tekstitiedoston merkistössä?

No, google löysi minulle toisen tulkin jonka kanssa ongelmaa ei esiintynyt, joten en alkanut pulman kanssa enempää voimistelemaan.


Sivun alkuun

Vastaus

Muista lukea kirjoitusohjeet.
Tietoa sivustosta