Как стать автором
Обновить

Стековый язык программирования на JavaScript в 34 строки

Время на прочтение3 мин
Количество просмотров2.4K

Информация для тех кто не знает что такое стековый язык:

Следующая строка подло скопирована с Wikipedia
Стековый язык программирования — это язык программирования, в котором для передачи параметров используется машинная модель стека.

О языке

Язык будет очень простым, так например функции могут принимать только один параметр, но тем не менее будет возможность сопоставления с образцом. Присутствует всего лишь один тип, а именно число.

Функция с сопоставлением будет определяться следующим образом:
value name = operations
Где value это шаблон для сопоставления, a operations это тело функции.

Если же определяется функция без сопоставления то она будет записана следующим образом:
name = operations
Но в теле функции можно использовать константу op значением которой будет операнд с котором была вызвана функция то-есть вершина стека.

Что-бы ввести в стек значение нужно просто ввести число.

А что-бы перевести верхний элемент стека в переменную нужно написать:
-> name
Из стандартных операция будут только + — * /

Так например функция увеличения элемента с вершины стека на 1 будет записана так:
++ = op 1 +

А вот для реализации функция факториала потребуется сопоставление с образцом и кодом будет:
0 ! = 1
! = op 1 - ! op *




Реализация

Будет написан только интерпретатор, который будет реализован как контекст.
Работу с этим контекстом будет осуществлять отдельный скрипт. В подробности которого мы вдаваться не будем. Скажу лишь то что он выводит в «консоль» вершину стека или 'nil'

Первые шаги

Первым делом мы как и полагается опишем контекст как конструктор и обозначим его HContext

HContext = function(){
this.stack = []; //Стек
this.vars = []; // Переменные
this.funs = []; //Функции

}


Затем создадим самую главную функцию 'run' аргументом которой будет код, а сама функция разделять его на лексемы и затем если окажется что это определение функции она вызовет нужную функцию иначе она вызовет функцию выполнения кода.

this.run = function(code){
lexems = code.split(' ');
if(lexems.oneOf('=')){
this.define(lexems);}else{
this.exec(lexems,'');}}

Примечание:те кто внимательно читали код могли заметить метод oneOf, это метод из так называемого мной extend.js который был написан мной после того как я прочитал фразу «Если тебе что то нужно, но этого еще нет, то создай это сам.», в дальнейшем функции из этой библиотеки также будут применяться.

Теперь напишем функцию exec которая будет выполнять код. В самом начале если аргумент op не определен то присвоим ему значение 3.

Затем создадим цикл который будет шагать по массиву из лексем и в зависимости от лексемы выполнять различные операции.
Если это 'op' — положить значение op в стек.
Если это число — положить его в стек.
Если есть переменная с таким именем — положить её значение в стек.
Если есть функция с таким именем — вызвать её. Функция call
Если одна из математических операция — вызовем её. Функция mathOp
А если это -> — увеличим счетчик на 1 и присвоим переменной имя которой есть текущая лексема значение с вершины стека.

this.exec = function(code,op){
op = op || 0;
for(var cp=0;cp<code.length;cp++){
if(code[cp]=='op'){this.stack.push(op)}
else if(code[cp].isNum()){this.stack.push(parseFloat(code[cp]))}
else if(this.vars[code[cp]] != undefined){this.stack.push(this.vars[code[cp]])}
else if(this.funs[code[cp]] != undefined){this.call(code[cp])}
else if(this.mathOps.oneOf(code[cp])){this.mathOp(code[cp])}
else if(code[cp] == '->'){cp++;this.vars[code[cp]]=this.stack.pop();}}}//


Математические операции


Создадим массив с возможными математическими операциями.
this.mathOps = ['+','-','*','/'];

А теперь определим функцию. которая будет брать два верхних значения из стека и класть результат операции на верх стека.

this.mathOp = function(op){
nums = [this.stack.pop(),this.stack.pop()];
this.stack.push(eval(nums[1] + ' ' + op + ' ' + nums[0]));}


Функции

А теперь поговорим о функциях, как уже было сказано будет сопоставления но его реализация будет очень простой за счет простой модели хранения функций.
Функции будут храниться в списке funs, и при этом каждая функция будет списком в котором элементом с ключом 'op' будет функция без сопоставления, а все остальные будут с сопоставлением и при этом ключ и будет шаблоном для сопоставления.

Напишем метод определения функции:

this.define = function(code){
eq = code.pos('='); //Определим позицию знака равно
op = code[eq-2] || 'op'; //Шаблоном будет элементом с ключом на 2 меньше знака = но если его нет то 'op'
name = code[eq-1]; //Именем будет элементом с ключом на 1
cmds = code.slice(eq+1); //А телом будут все элементы с ключом на 1 больше
if(this.funs[name] == undefined){this.funs[name] = [];} //Если функция с данным именем отсутствует, то определим
this.funs[name][op] = cmds; //Теперь занесем это в массив функций}


А теперь будем вызывать функции, для этого выделим метод call (Большинство, а особенно программисты на ассемблере точно не найдут название смешным)

this.call = function(fun){
op = this.stack.pop(); //Получаем операнд
cd = this.funs[fun][op] || this.funs[fun]['op']; //Ищем функцию с совпадением иначе берем универсальную
this.exec(cd,op);} //И выполняем её код


Попробовать:

Псевдо-консоль находится тут. Там же расположены примеры.
Теги:
Хабы:
Всего голосов 39: ↑31 и ↓8+23
Комментарии9

Публикации

Истории

Работа

Ближайшие события

7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн
7 – 8 ноября
Конференция «Матемаркетинг»
МоскваОнлайн
15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань