Пишем интерпретатор для своего эзотерического языкаИсточник: habrahabr
За основу я взял язык Brainfuck, он настолько мал, что можно немного расширив получить практически новый и достаточно функциональный язык программирования. И при этом не потерять изюминку исходного языка - мой язык будет все так же терзать мозг программиста, как и его родитель! Итак, Brainfuck. Вкратце, идея такая, есть N регистров/ячеек. У программиста есть доступ к ним всем но перемещения по ним делаются явным образом. Т.е. из ячейки 2 нельзя перейти к ячейке 7 сразу, нужно последовательно. "Ключевые слова" языка:
Добавленные "ключевые слова":
Так как все множество ключевых слов состоит из ANCII символов, имеем: // Добавленные ключевые слова Без ограничения общности возьмем ограниченное количество ячеек, скажем 256 и в случае попытки перейти к недопустимой ячейке будем переходить к самой первой ячейке ( если переход влево) или к самой последней ( если переход вправо). Добавим: long reg[ regSize ]; // Сами регистры void resetRegisters(); // Функция для обнуления регистров
void printRegistres(); // Показать состояние регистров Теперь, скажем имеем test.bf, как входной файл, в котором находится код на моем языке или на родном Brainfuck. Интерпретатор должен обеспечивать "обратную совместимость". Опять же, без ограничения общности, можем хранить весь код в некотором ограниченном массиве. Т.е. интепретатор будет работать с файлами ограниченного размера, скажем так:
Интерпритатор читает весь код сразу. В один символьный массив, для этого будем использовать функцию readCode(). После прочтения не пустого текста m_realCodeSize будет содержать точное количество символов в коде, без учета комментариев, комментарии отбрасываются во время чтения. int main( int argc, char** argv ) Далее определим пару функций для цикла while и копирования стека и собственно выполнения функции. bool runFunction( unsigned long from, void copyRegistersTo( long* source, long* destination ); Вторая собственно будет выполнять функцию, а возвращаемое значение запишется в retVal, которое в свою очередь присвоится регистру, на котором была вызвана функция. Возвращаемым значением будем считать первый регистр стека функции после ее окончания. Кстати, о цикле while, в общем случае цикл может продолжаться бесконечно. Но, чтобы не столкнуться с проблемой зависания интерпретатора, введем переменную отвечающую за максимальное количество циклов.
Реализуем сначала обратную совместимость. Пусть пока наш интерпретатор сможет выполнять только код Brainfuck-а. unsigned long findLoopEnd( const unsigned long from ) Второй и третий параметры первой функции обязательны. Третий параметр нужен для того, чтобы ориентироваться с какой ячейкой работать, второй нужен потому что регистры каждой функции отличаются, а операции над ними одинаковы. Вторая функция исходя из названия находит конец цикла, т.е. символ соответствуюйщий '['. Таким образом имеем интерпретатор для языка Brainfuck.
На код выше мой интерпретатор выведет сумму двух введённых чисел в виде а+b=c. Удачного… программирования! |