Недавно решил я попробовать написать какой-нибудь интерпретатор и сделать это на языке C, остановился на языке brainfuck, потому, что его интерпретатор довольно прост в написании.
Что такое brainfuck?
Brainfuck это язык программирования известный своим минимализмом
И правда, в нем всего 8 комманд это "+", "-", ">", "<", "[", "]", ".", ",".
Инструкция | Действие |
"+" | Увеличивает значение в текущей ячейке на 1 |
"-" | Уменьшает значение текущей ячейки на 1 |
">" | Переход к следующей ячейке |
"<" | Переход к предыдущей ячейке |
"." | Вывод на экран содержания текущей ячейки |
"," | Ввод символа с клавиатуры в текущюю ячейку |
"[" и "]" | Операторы goto |
Написание самого интерпретатора
Чтобы интерпретатор работал надо подключить определённые библиотеки
stdio для printf, fopen
stdlib для exit
string для memset
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Дальше надо инициализировать массив памяти и определить инструкции:
char stack[30000]; //массив памяти
int indexx = 0; // индекс текущей ячейки
char * plus = "+";
char * minus = "-";
char * next = ">";
char * back = "<";
char * prt = ".";
char * inp = ",";
char * lp = "[";
char * llp = "]";
int rc = 0;
char * endl = "\n";
char * ex = "@";
Значек "@" будет использоваться как выход из программы
Следующий шаг это создать метод main и в его теле инициализировать массив
memset(stack, 0, 30000);
Далее проверку на кол-во аргументов командной строки
if (argc < 2) {
bf_shell();
exit(0);
}
Выходит если аргумент 1, то запускается brainfuck shell - ещё одна фича моего интерпретатора
Код brainfuck shell:
int bf_shell()
{
printf("Startimg BrainFuck Shell...\n");
char command[4096];
int i = 1;
do {
printf("BF> ");
gets(command);
for (int x = 0; x < sizeof(command); x++) {
if (command[x] == *plus) {
stack[indexx]++;
} else if (command[x] == *minus) {
stack[indexx]--;
} else if (command[x] == *next) {
indexx++;
} else if (command[x] == *back) {
indexx--;
} else if (command[x] == *prt) {
putchar(stack[indexx]);
} else if (command[x] == *inp) {
char t[2];
gets(t);
stack[indexx] = (int)t[0];
}else if (command[x] == *endl) {
i = 1;
} else if (command[x] == *ex) {
i = 0;
} else if (command[x] == *lp) {
if (!stack[indexx]) {
rc++;
while (rc) {
i++;
if (command[x] == *lp) {
rc++;
}
if (command[x] == *llp) {
rc--;
}
}
} else continue;
} else if (command[x] == *llp) {
if (!stack[indexx]) {
continue;
} else {
if (command[x] == *llp) {
rc++;
while (rc) {
x--;
if (command[x] == *lp) {
rc--;
}
if(command[x] == *llp) {
rc++;
}
}
x--;
}
}
} else {
continue;
}
}
} while (i);
return 0;
}
Дальше идёт тело метода main где интерпретатор интерпретирует символы из файла
FILE * file = fopen(argv[1], "r");
if (file == NULL) {
printf("Cann't open file %s!\n", argv[1]);
exit(1);
}
fseek(file, 0L, SEEK_END);
long size = ftell(file);
fseek(file, 0L, SEEK_SET);
char buf[size];
fread(buf, sizeof(buf), 1, file);
И сам цикл интерпретации
for (int i = 0; i < sizeof(buf); i++) {
if (buf[i] == *plus){
stack[indexx]++;
} else if (buf[i] == *minus) {
stack[indexx]--;
} else if (buf[i] == *next) {
indexx++;
} else if (buf[i] == *back) {
indexx--;
} else if (buf[i] == *prt) {
putchar(stack[indexx]);
} else if (buf[i] == *inp) {
char c;
scanf("%c", &c);
stack[indexx] = (int)c;
} else if (buf[i] == *lp) {
if (!stack[indexx]) {
rc++;
while (rc) {
i++;
if (buf[i] == *lp) {
rc++;
}
if (buf[i] == *llp) {
rc--;
}
}
} else continue;
} else if (buf[i] == *llp) {
if(!stack[indexx]) {
continue;
} else {
if (buf[i] == *llp) {
rc++;
while (rc) {
i--;
if (buf[i] == *lp) {
rc--;
}
if (buf[i] == *llp) {
rc++;
}
}
i--;
}
}
} else if (buf[i] == *ex) {
return 0;
}
}
Исходник доступен на моем гитхабе
Учтите, что в исходниках все написано в теле метода main.