您的位置:首页 > Web前端 > JavaScript

Befunge-93指令解释器的 JavaScript实现

2017-01-02 11:19 204 查看
Befunge-93指令解释器,它的指令是一个二维的形式,根据坐标的改变,执行不同的指令。

而一般的Esoteric解释器,如Brainfuck,指令都是一维的,顺序解释的。

这篇帖子我提供了一个实现,具体的原理和发展历程大家可以在维基上面搜索。

//Befunge-93
function interpret(code) {
//根据换行符来分割指令
var array = code.split("\n");
//将指令打散成二维数组
array = array.map(function(e){
var temp = [];
for(var i=0;i<e.length;i++){
temp.push(e.charAt(i));
}
return temp;
})
//要输出的内容
var output = "";
//要用到的栈
var stack = [];
//坐标(行,列)
var row = 0;
var col = 0;
//保存先前操作的引用
var prev = null;

//改变坐标的指令(上下左右)
var moveRight = function(){col++;prev = moveRight;};
var moveLeft = function(){col--;prev = moveLeft;};
var moveUp = function(){row--;prev = moveUp;};
var moveDown = function(){row++;prev = moveDown;};
//该操作从栈中弹出,并加入到输出中
var point = function(){output += stack.pop();prev();};
var semicolon = function(){
//若栈顶无内容,则压入0
if(stack[stack.length-1] === undefined){
stack.push(0);
}//否则copy栈顶元素,压入
else{
stack.push(stack[stack.length-1]);
}
prev();
};
//该操作什么也不做,或者说是继续执行先前操作
var blank = function(){prev();};
//从栈中弹出一个元素,若为0,坐标向右移动,否则向左移动
var underLine = function(){stack.pop() == 0 ? moveRight() : moveLeft();};
//从栈中弹出一个元素,若为0,坐标向下移动,否则向上移动
var line = function(){stack.pop() == 0 ? moveDown() : moveUp();};
//从栈中弹出两个元素,再压入它们的和
var add = function(){
stack.push(stack.pop() + stack.pop());
prev();
};
//从栈中弹出两个元素,再压入它们的差
var subtract = function(){
var a = stack.pop();
var b = stack.pop();
stack.push(b - a);
prev();
};
//从栈中弹出两个元素,再压入它们的乘积
var multiply = function(){stack.push(stack.pop() * stack.pop());prev();};
//从栈中弹出两个元素,再压入它们的商
var divide = function(){
var a = stack.pop();
var b = stack.pop();
//若a为0,则压入0
a ? stack.push(Math.round(b / a)) : stack.push(0);
prev();
};
//从栈中弹出两个元素,再压入它们取余的结果
var mod = function(){
var a = stack.pop();
var b = stack.pop();
a ? stack.push(b % a) : stack.push(0);
prev();
};
//弹出一个元素,若为0则压入1,若为非0则压入0
var not = function(){
stack.pop() ? stack.push(0) : stack.push(1);
prev();
};
//从栈中弹出两个元素,若b>a则压入1,否则压入0
var greater = function(){
var a = stack.pop();
var b = stack.pop();
b > a ? stack.push(1) : stack.push(0);
prev();
};
//从上下左右中随机抽取方向,移动坐标
var random = function(){
[moveRight,moveLeft,moveUp,moveDown][Math.floor(Math.random() * 4)]();
};
//仅从栈中弹出一个元素
var $ = function(){stack.pop();prev();};
//交换栈顶的2个元素
var swap = function(){
//若整个栈只有一个元素,则假定栈底为0
if(stack.length == 1){
stack.unshift(0);
}
var temp = stack[stack.length-1];
stack[stack.length-1] = stack[stack.length-2];
stack[stack.length-2] = temp;
prev();
};
//跳过下一个指令
var skip = function(){
prev();
prev();
};
//从栈中弹出两个元素,作为坐标x,y
//将array中当前坐标(x,y)存的元素的ASCII编码压入栈中
var get = function(){
var x = stack.pop();
var y = stack.pop();
stack.push((array[x][y] + "").charCodeAt(0));
prev();
};
//从栈中弹出一个元素,拼接它的字符串形式到输出中
var comma = function(){
output += String.fromCharCode(stack.pop());
prev();
};
//从栈中弹出三个元素x,y,v,x,y作为坐标
//将array中当前坐标(x,y)的元素设置为v的字符串形式
var put = function(){
var x = stack.pop();
var y = stack.pop();
var v = stack.pop();
array[x][y] = String.fromCharCode(v);
prev();
};
//将两个"符号之间的指令通通压入栈中
var mode = function(){
prev();
while(array[row][col] != "\""){
stack.push(array[row][col].charCodeAt(0));
prev();
}
prev();
};
//指令映射函数
var map = {
">" : moveRight,
"<" : moveLeft,
"^" : moveUp,
"v" : moveDown,
"." : point,
":" : semicolon,
" " : blank,
"_" : underLine,
"+" : add,
"-" : subtract,
"*" : multiply,
"/" : divide,
"%" : mod,
"!" : not,
"`" : greater,
"?" : random,
"|" : line,
"$" : $,
"\\" : swap,
"#" : skip,
"g" : get,
"," : comma,
"p" : put,
"\"" : mode
};

//只要没遇到终止符号@
while(array[row][col] != "@"){
var command = array[row][col];
//当前符号是map指令之一,就执行指令对应的函数
if(map.hasOwnProperty(command)){
map[command]();
}
else{
//当前指令是数字
if(parseInt(command) == command){
stack.push(command - 0);
}
else{
stack.push(command);
}
//默认向右移动坐标
prev ? prev() : moveRight();
}
}
//返回输出字符串
return output;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息