使用堆栈片段使将来的帖子可在线运行


134

堆栈片段最近加入PPCG!让人想起JSFiddle,堆栈片段允许直接在帖子中运行HTML,CSS和JavaScript !

这是一个非常简单的堆栈片段:

alert('This is JavaScript')
h3 { color: red } /* This is CSS */
<h3>This is HTML</h3>

如果支持JavaScript以外的语言,Stack Exchange的此功能对我们非常有用。(可以现场测试挑战的答案,可以动态生成示例输入,等等)。

挑战

这项挑战的目标是使用Stack Snippets和JavaScript为某些编程语言编写解释器。关键是要做出可以轻松复制并在以后的PPCG问题和答案中使用的内容。

或多或少,您需要创建一个具有“运行”按钮和两个文本框的堆栈片段,一个用于代码,一个用于输入。单击运行按钮将在输入上执行代码(以您解释的语言编写)并显示结果(可能在另一个文本框中)。该代码段应类似于cjam.aditsu.net示例答案

对于大多数语言,输入和输出分别代表stdin和sdout有意义,并且命令行中可能还有另一个输入框。但是,并非所有语言都具有这种传统的I / O机制。例如,HQ9 +甚至没有输入,因此它的文本框毫无意义。因此,可以自由选择一些自由语言,围绕语言进行设计,而不是遵循此规范。主要要求是您的语言应在堆栈摘录中以该术语的公认含义“可运行”。

笔记

  • 尽管理想,不需要实现语言的每个功能。诸如读取和写入文件或导入库之类的事情可能是笨拙的或不可能的。专注于使此网站上使用的实用程序最大化的解释器。
  • 将您未编写的“ X语言到JavaScript语言”解释器发布(可以使用归因)是可以的。
  • Stack Exchange将答案限制为30,000个字符,因此,如果您的解释器可能很长,请相应地计划。
  • 最好使翻译版本尽可能容易地包含在以后的帖子中。例如,在示例答案中,提供了整个代码段的原始Markdown,并提供了放置代码和输入的明显位置。

尽管此问题旨在使口译人员更胜一筹,而不是一个适当的挑战,但它仍然是一场,因此,票数最高的答案将获胜。

当前口译员清单

(按语言名称按字母顺序排序)

  1. 光束
  2. Befunge-93
  3. 脑干
  4. 脑干
  5. CHIQRSX9 +
  6. 死鱼
  7. 死鱼(仅运行预设代码)
  8. 傅里叶
  9. FRACTRAN
  10. 你好++
  11. HQ9 +
  12. 失眠
  13. 杰普特
  14. JavaScript(示例答案)
  15. JavaScript ES2015
  16. 马氏
  17. 新文字
  18. 代码
  19. 衔尾蛇
  20. 序幕
  21. Python 2
  22. 斯塔塔
  23. TI基本
  24. 一元(转换为BF)

(这个问题可能更适合于Meta,但在这里会更明显。人们实际上可能会产生非常有用的解释器,我认为他们应该为此提供帮助。)


2
并不是很有趣,但是对于像Python这样的较难语言的更实际的答案可能就是简单地将其外包。
Sp3000

7
我不知道如何使用堆栈交换应用程序运行这些片段。
杰里·耶利米

1
我希望有人用这个挑战来解决C。这里有一个良好的开端:stackoverflow.com/questions/6142193/...
亚当·戴维斯


1
@ Sp3000:或者寻找库...如下。
ArtOfCode

Answers:


48

Python 2(无STDIN)

感谢Skulpt,编写Python解释器变得非常容易。

function out(text) {
  var output = document.getElementById("output");
  output.innerHTML = output.innerHTML + text;
}

function builtinRead(x) {
  if(Sk.builtinFiles === undefined || Sk.builtinFiles["files"][x] === undefined)
    throw "File not found: '" + x + "'";
  return Sk.builtinFiles["files"][x];
}

function run() {
  var program = document.getElementById("python-in").value;
  var output = document.getElementById("output");
  
  output.innerHTML = "";
  
  Sk.canvas = "canvas";
  Sk.pre = "output";
  
  Sk.configure({
    output: out,
    read: builtinRead
  });
  
  try {
    Sk.importMainWithBody("<stdin>", false, program);
  }
  catch(e) {
    throw new Error(e.toString());
  }
}
#python-in {
  border-radius: 3px;
  background: rgb(250, 250, 250);
  width: 95%;
  height: 200px;
}

#output {
  border-radius: 3px;
  background: lightgray;
  width: 95%;
  height: 200px;
  overflow: auto;
}

#canvas {
  border: 1px solid gray;
  border-radius: 3px;
  height: 400px;
  width: 400px;
}
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://www.skulpt.org/static/skulpt.min.js"></script>
<script src="http://www.skulpt.org/static/skulpt-stdlib.js"></script>

Python code:<br/>
<textarea id="python-in" name="python-in" rows="10" cols="80"></textarea><br/>
<button id="run-code" onclick="run()">Run</button><br/>
<br/>

Output:<br/>
<pre id="output" name="output" rows="10" cols="10" disabled></pre><br/>

Canvas for turtles:<br/>
<canvas height="400" width="400" id="canvas">Your browser does not support HTML5 Canvas!</canvas>

没有STDIN,但这是JS中Python的相当完整的实现。我会从其他地方加载库,但是找不到它们的主机。


1
哇,from random import randrange甚至可以用。真好!
门把手

20
现在,只需将Marbelous源代码加载到该解释器中,您便可以自己成为Marbelous解释器!
IngoBürk2014年

3
print 2工作以来似乎是python2 。我想值得一提。
ReyCharles 2014年

2
我不确定让Marbelous在python解释器中运行还是在Web浏览器中运行的javascript中运行,是更有趣还是更酷
vijrox 2015年

1
这不再起作用了,大概是因为<script>标签中的链接已死:(
oldmud0 2015年

33

Befunge-93

编辑:我现在重做了整个GUI。我很高兴。某种断点功能会很酷,但是这样做可能太多了。我可能会重温的开放点:

  • 允许无限局
  • 单步执行程序时显示堆栈会很整洁

由于我只是为此挑战实施了该解释器,因此并未经过广泛的测试。但是,我现在使用各种不同的程序进行了尝试,它似乎运行良好。

最新版本可以在此提琴的最新修订版中找到。

function BefungeBoard(source, constraints) {
    constraints = constraints || {
        width: 80,
        height: 25
    };

    this.constraints = constraints;
    this.grid = source.split(/\r\n|[\n\v\f\r\x85\u2028\u2029]/).map(function (line) {
        return (line + String.repeat(' ', constraints.width - line.length)).split('');
    });
    for (var i = this.grid.length; i < constraints.height; i++) {
        this.grid[i] = String.repeat(' ', constraints.width).split('');
    }

    this.pointer = {
        x: 0,
        y: 0
    };

    this.direction = Direction.RIGHT;
}

BefungeBoard.prototype.nextPosition = function () {
    var vector = this.direction.toVector(),
        nextPosition = {
            x: this.pointer.x + vector[0],
            y: this.pointer.y + vector[1]
        };

    nextPosition.x = nextPosition.x < 0 ? this.constraints.width - 1 : nextPosition.x;
    nextPosition.y = nextPosition.y < 0 ? this.constraints.height - 1 : nextPosition.y;

    nextPosition.x = nextPosition.x >= this.constraints.width ? 0 : nextPosition.x;
    nextPosition.y = nextPosition.y >= this.constraints.height ? 0 : nextPosition.y;

    return nextPosition;
};

BefungeBoard.prototype.advance = function () {
    this.pointer = this.nextPosition();
    if (this.onAdvance) {
        this.onAdvance.call(null, this.pointer);
    }
};

BefungeBoard.prototype.currentToken = function () {
    return this.grid[this.pointer.y][this.pointer.x];
};

BefungeBoard.prototype.nextToken = function () {
    var nextPosition = this.nextPosition();
    return this.grid[nextPosition.y][nextPosition.x];
};

var Direction = (function () {
    var vectors = [
        [1, 0],
        [-1, 0],
        [0, -1],
        [0, 1]
    ];

    function Direction(value) {
        this.value = value;
    }

    Direction.prototype.toVector = function () {
        return vectors[this.value];
    };

    return {
        UP: new Direction(2),
        DOWN: new Direction(3),
        RIGHT: new Direction(0),
        LEFT: new Direction(1)
    };
})();

function BefungeStack() {
    this.stack = [];
}

BefungeStack.prototype.pushAscii = function (item) {
    this.pushNumber(item.charCodeAt());
};

BefungeStack.prototype.pushNumber = function (item) {
    if (isNaN(+item)) {
        throw new Error(typeof item + " | " + item + " is not a number");
    }

    this.stack.push(+item);
};

BefungeStack.prototype.popAscii = function () {
    return String.fromCharCode(this.popNumber());
};

BefungeStack.prototype.popNumber = function () {
    return this.stack.length === 0 ? 0 : this.stack.pop();
};

function Befunge(source, constraints) {
    this.board = new BefungeBoard(source, constraints);
    this.stack = new BefungeStack();
    this.stringMode = false;
    this.terminated = false;

    this.digits = "0123456789".split('');
}

Befunge.prototype.run = function () {
    for (var i = 1; i <= (this.stepsPerTick || 10); i++) {
        this.step();
        if (this.terminated) {
            return;
        }
    }

    requestAnimationFrame(this.run.bind(this));
};

Befunge.prototype.step = function () {
    this.processCurrentToken();
    this.board.advance();
};

Befunge.prototype.processCurrentToken = function () {
    var token = this.board.currentToken();
    if (this.stringMode && token !== '"') {
        return this.stack.pushAscii(token);
    }

    if (this.digits.indexOf(token) !== -1) {
        return this.stack.pushNumber(token);
    }

    switch (token) {
        case ' ':
            while ((token = this.board.nextToken()) == ' ') {
                this.board.advance();
            }
            return;
        case '+':
            return this.stack.pushNumber(this.stack.popNumber() + this.stack.popNumber());
        case '-':
            return this.stack.pushNumber(-this.stack.popNumber() + this.stack.popNumber());
        case '*':
            return this.stack.pushNumber(this.stack.popNumber() * this.stack.popNumber());
        case '/':
            var denominator = this.stack.popNumber(),
                numerator = this.stack.popNumber(),
                result;
            if (denominator === 0) {
                result = +prompt("Illegal division by zero. Please enter the result to use:");
            } else {
                result = Math.floor(numerator / denominator);
            }

            return this.stack.pushNumber(result);
        case '%':
            var modulus = this.stack.popNumber(),
                numerator = this.stack.popNumber(),
                result;
            if (modulus === 0) {
                result = +prompt("Illegal division by zero. Please enter the result to use:");
            } else {
                result = Math.floor(numerator / modulus);
            }

            return this.stack.pushNumber(result);
        case '!':
            return this.stack.pushNumber(this.stack.popNumber() === 0 ? 1 : 0);
        case '`':
            return this.stack.pushNumber(this.stack.popNumber() < this.stack.popNumber() ? 1 : 0);
        case '>':
            this.board.direction = Direction.RIGHT;
            return;
        case '<':
            this.board.direction = Direction.LEFT;
            return;
        case '^':
            this.board.direction = Direction.UP;
            return;
        case 'v':
            this.board.direction = Direction.DOWN;
            return;
        case '?':
            this.board.direction = [Direction.RIGHT, Direction.UP, Direction.LEFT, Direction.DOWN][Math.floor(4 * Math.random())];
            return;
        case '_':
            this.board.direction = this.stack.popNumber() === 0 ? Direction.RIGHT : Direction.LEFT;
            return;
        case '|':
            this.board.direction = this.stack.popNumber() === 0 ? Direction.DOWN : Direction.UP;
            return;
        case '"':
            this.stringMode = !this.stringMode;
            return;
        case ':':
            var top = this.stack.popNumber();
            this.stack.pushNumber(top);
            return this.stack.pushNumber(top);
        case '\\':
            var first = this.stack.popNumber(),
                second = this.stack.popNumber();
            this.stack.pushNumber(first);
            return this.stack.pushNumber(second);
        case '$':
            return this.stack.popNumber();
        case '#':
            return this.board.advance();
        case 'p':
            return this.board.grid[this.stack.popNumber()][this.stack.popNumber()] = this.stack.popAscii();
        case 'g':
            return this.stack.pushAscii(this.board.grid[this.stack.popNumber()][this.stack.popNumber()]);
        case '&':
            return this.stack.pushNumber(+prompt("Please enter a number:"));
        case '~':
            return this.stack.pushAscii(prompt("Please enter a character:")[0]);
        case '.':
            return this.print(this.stack.popNumber());
        case ',':
            return this.print(this.stack.popAscii());
        case '@':
            this.terminated = true;
            return;
    }
};

Befunge.prototype.withStdout = function (printer) {
    this.print = printer;
    return this;
};

Befunge.prototype.withOnAdvance = function (onAdvance) {
    this.board.onAdvance = onAdvance;
    return this;
};

String.repeat = function (str, count) {
    var repeated = "";
    for (var i = 1; i <= count; i++) {
        repeated += str;
    }

    return repeated;
};

window['requestAnimationFrame'] = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) {
    window.setTimeout(callback, 1000 / 60);
};

(function () {
    var currentInstance = null;

    function resetInstance() {
        currentInstance = null;
    }

    function getOrCreateInstance() {
        if (currentInstance !== null && currentInstance.terminated) {
            resetInstance();
        }

        if (currentInstance === null) {
            var boardSize = Editor.getBoardSize();
            currentInstance = new Befunge(Editor.getSource(), {
                width: boardSize.width,
                height: boardSize.height
            });
            currentInstance.stepsPerTick = Editor.getStepsPerTick();

            currentInstance.withStdout(Editor.append);
            currentInstance.withOnAdvance(function (position) {
                Editor.highlight(currentInstance.board.grid, position.x, position.y);
            });
        }

        return currentInstance;
    }

    var Editor = (function (onExecute, onStep, onReset) {
        var source = document.getElementById('source'),
            sourceDisplay = document.getElementById('source-display'),
            sourceDisplayWrapper = document.getElementById('source-display-wrapper'),
            stdout = document.getElementById('stdout');
        var execute = document.getElementById('execute'),
            step = document.getElementById('step'),
            reset = document.getElementById('reset');
        var boardWidth = document.getElementById('board-width'),
            boardHeight = document.getElementById('board-height'),
            stepsPerTick = document.getElementById('steps-per-tick');

        function showEditor() {
            source.style.display = "block";
            sourceDisplayWrapper.style.display = "none";
            source.focus();
        }

        function hideEditor() {
            source.style.display = "none";
            sourceDisplayWrapper.style.display = "block";

            var computedHeight = getComputedStyle(source).height;
            sourceDisplayWrapper.style.minHeight = computedHeight;
            sourceDisplayWrapper.style.maxHeight = computedHeight;

            sourceDisplay.textContent = source.value;
        }

        function resetOutput() {
            stdout.value = null;
        }

        function escapeEntities(input) {
            return input.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
        }

        sourceDisplayWrapper.onclick = function () {
            resetOutput();
            showEditor();
            onReset && onReset.call(null);
        };
        execute.onclick = function () {
            resetOutput();
            hideEditor();
            onExecute && onExecute.call(null);
        };
        step.onclick = function () {
            hideEditor();
            onStep && onStep.call(null);
        };
        reset.onclick = function () {
            resetOutput();
            showEditor();
            onReset && onReset.call(null);
        };

        return {
            getSource: function () {
                return source.value;
            },

            append: function (content) {
                stdout.value = stdout.value + content;
            },

            highlight: function (grid, x, y) {
                var highlighted = [];
                for (var row = 0; row < grid.length; row++) {
                    highlighted[row] = [];
                    for (var column = 0; column < grid[row].length; column++) {
                        highlighted[row][column] = escapeEntities(grid[row][column]);
                    }
                }

                highlighted[y][x] = '<span class="activeToken">' + highlighted[y][x] + '</span>';
                sourceDisplay.innerHTML = highlighted.map(function (lineTokens) {
                    return lineTokens.join('');
                }).join('\n');
            },

            getBoardSize: function () {
                return {
                    width: +boardWidth.innerHTML,
                    height: +boardHeight.innerHTML
                };
            },

            getStepsPerTick: function () {
                return +stepsPerTick.innerHTML;
            }
        };
    })(function () {
        getOrCreateInstance().run();
    }, function () {
        getOrCreateInstance().step();
    }, resetInstance);
})();
.container {
    width: 100%;
}
.so-box {
    font-family:'Helvetica Neue', Arial, sans-serif;
    font-weight: bold;
    color: #fff;
    text-align: center;
    padding: .3em .7em;
    font-size: 1em;
    line-height: 1.1;
    border: 1px solid #c47b07;
    -webkit-box-shadow: 0 2px 2px rgba(0, 0, 0, 0.3), 0 2px 0 rgba(255, 255, 255, 0.15) inset;
    text-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
    background: #f88912;
    box-shadow: 0 2px 2px rgba(0, 0, 0, 0.3), 0 2px 0 rgba(255, 255, 255, 0.15) inset;
}
.control {
    display: inline-block;
    border-radius: 6px;
    float: left;
    margin-right: 25px;
    cursor: pointer;
}
.option {
    padding: 10px 20px;
    margin-right: 25px;
    float: left;
}
input, textarea {
    box-sizing: border-box;
}
textarea {
    display: block;
    white-space: pre;
    overflow: auto;
    height: 75px;
    width: 100%;
    max-width: 100%;
    min-height: 25px;
}
span[contenteditable] {
    padding: 2px 6px;
    background: #cc7801;
    color: #fff;
}
#controls-container, #options-container {
    height: auto;
    padding: 6px 0;
}
#stdout {
    height: 50px;
}
#reset {
    float: right;
}
#source-display-wrapper {
    display: none;
    width: 100%;
    height: 100%;
    overflow: auto;
    border: 1px solid black;
    box-sizing: border-box;
}
#source-display {
    font-family: monospace;
    white-space: pre;
    padding: 2px;
}
.activeToken {
    background: #f88912;
}
.clearfix:after {
    content:".";
    display: block;
    height: 0;
    clear: both;
    visibility: hidden;
}
.clearfix {
    display: inline-block;
}
* html .clearfix {
    height: 1%;
}
.clearfix {
    display: block;
}
<div class="container">
    <textarea id="source" placeholder="Enter your Befunge-93 program here" wrap="off">"39-egnufeB">:#,_@</textarea>
    <div id="source-display-wrapper">
        <div id="source-display"></div>
    </div>
</div>
<div id="controls-container" class="container clearfix">
    <input type="button" id="execute" class="control so-box" value="► Execute" />
    <input type="button" id="step" class="control so-box" value="Step" />
    <input type="button" id="reset" class="control so-box" value="Reset" />
</div>
<div id="stdout-container" class="container">
    <textarea id="stdout" placeholder="Output" wrap="off" readonly></textarea>
</div>
<div id="options-container" class="container">
    <div class="option so-box">Steps per Tick: <span id="steps-per-tick" contenteditable>500</span>

    </div>
    <div class="option so-box">Board Size: <span id="board-width" contenteditable>80</span> x <span id="board-height" contenteditable>25</span>

    </div>
</div>


真好!我们必须手动输入电路板尺寸吗?似乎,如果我们不这样做,突出显示的趋势往往遍布整个地方。(在此处使用第一个程序进行测试:en.wikipedia.org/wiki/Befunge#Sample_Befunge-93_code
Sp3000,2014年

@ Sp3000如果要更改它,则只需输入它(默认值为原始的80x25)。该程序对我来说很好用。“到处都是”是Befunge的全部宗旨。唯一不太好的事情是您必须逐个遍历电路板的所有空白,但是我会解决此问题。不过,从技术上讲这是正确的。如果您觉得有些怪异,也许您可​​以制作一份gif /视频,说明它的外观?
IngoBürk2014年

@ Sp3000一件事:如果您仅单击“执行”,则突出显示看起来很奇怪,因为它进行得太快了。如果您以前从未尝试过,只需尝试“ Step”以逐步执行program命令即可。或者,将每个刻度的步长设置为1,然后单击“执行”以获得更平滑的动画。
IngoBürk2014年

啊哈,是的,“到处都是”,我的意思是所有空白都已通过。您是否打算在将来使程序检测板子尺寸?(如果有时间的话)
Sp3000

1
@ Sp3000板的大小是解释器的属性,而不是程序的属性。最初,Befunge板具有固定大小。但是您可以允许我正在计划的无限大小。我还计划一步一步跳过连续的空格。
IngoBürk2014年

33

TI-BASIC

因为谁不喜欢TI-Basic?

我想为此做出贡献,所以我选择了一种语言(以我的愚见)比“死鱼” 稍微复杂些,但在我的控制范围内。我对我的计算器了解很多,所以我选择了它。

但是,我没有JavaScript / CSS / HTML的背景知识。这是我的第一个程序。就是说

请指出您在我的代码中发现的错误!

话虽如此,这是TI-Basic的受限版本。当我处理这些事情时,我将继续实现它们。

截至目前的功能

  • 常用控制关键字(如果,则,否则,结束)
  • 所有可用循环(同时,重复,用于)
  • 显示命令
  • JavaScript算术运算符,通过eval
  • 清单
  • 内置自动冒号器(自己动手)
  • 未初始化变量的随机值,就像您的计算器一样
  • 有限数学-sin,cos,tan,asin,acos,atan,sinh,cosh,tanh,log,ln,int,round,abs。
  • 常数pi和e作为变量
  • 提示,输入,暂停(是!)
  • 在执行过程中写入输出(感谢假名117)
  • 简单的像素开/关/清晰屏幕功能

取得自由

  • 支持多字符变量名。不幸的是,这意味着ab变量,而不是值a*b。这个单字符变量名的限制使我为计算器疯狂。
  • 忽略“ goto”和“ lbl”。除非您以某种方式进行操作,否则即使在计算器上,它们也存在故障。
  • 由于没有外部/其他文件,因此将忽略prgm,OpenLib和ExecLib

仍然需要做的事情

  • 某种用于绘图的画布(即将推出!)更多绘图内容
  • 文本窗口中的某种方式promptinput命令(建议,有人吗?)
  • 支持(其余)计算器数学运算(有BUCKETS)
  • Suppot为Ans关键字的把戏。
  • 支持其余的CTL关键字
  • 支持某种形式的getkey的方法,尽管当今大多数语言都是事件驱动的……也许它可能是最后按下的键。
  • 我不知道这是否成立,但是JavaScript似乎先思考,然后显示到文本区域,而不是一直显示。
  • 没有CSS / HTML的美感

我仍然对JavaScript不太了解。如果上述任何可能/不可能,请告知我。

function inputChanged() {
    var codeText = document.getElementById('code-block');
    if (codeText.value.charAt(0) !== ':') {
        codeText.value = ':' + codeText.value;
    }
    var cursor = codeText.selectionEnd;
    if (lastText.length < codeText.value.length) { //adding characters
        if (/(\n)$/.test(codeText.value) === true || /(\n)(\n)/.test(codeText.value) === true) {
            cursor++;
        }
        codeText.value = codeText.value.replace(/(\n)$/, '\n:');
        codeText.value = codeText.value.replace(/(\n)(\n)/, '\n:\n');
    } else { //removing characters
        if (/(\n)$/.test(codeText.value) === true || /(\n)(\n)/.test(codeText.value) === true) {
            cursor--;
        }
        codeText.value = codeText.value.replace(/(\n)$/, '');
        codeText.value = codeText.value.replace(/(\n)(\n)/, '\n');
    }
    codeText.setSelectionRange(cursor, cursor);
    lastText = codeText.value;
}
function onUserInput(e){
    var output = document.getElementById('output');
    //enter has code 13
    if(awaitingInput && e.keyCode===13){
        var lines = output.value.split('\n');
        var input = lines[lines.length-1];
        if(input.indexOf('?')!==-1){
            input = input.substring(input.indexOf('?')+1,input.length);
        }
        if(newInputVarName !== null){
            var iValue = replaceVars(input);
            variables[newInputVarName]= eval(iValue);
        }else{
           output.value = output.value.substring(0,output.value.length-1);  
        }
        awaitingInput = false;
    }
}
var lastText = ':';
var L1 = [],
    L2 = [],
    L3 = [],
    L4 = [],
    L5 = [],
    L6 = []; //note - these are special. They're like variables, and are considered in replaceVars.
var tokens = [':', '-->', 'if', 'then',
    'else', 'for', 'while', ',',
    'repeat', 'end', 'disp','prompt','input',
    'pause','pxl-on','pxl-off','clrdraw'];
var variables = [];
var loopStack = []; //holds line numbers
var ifStack = [true]; //holds true or false
var typeStack = ['if'];
var repeatStack = []; //holds current iteration for repeats. NOTE: starts at 1 to accomidate repeat 0
var awaitingInput = false;
var newInputVarName=null;
var pixels = []; //96*64
var running = false;
var lineProcessing;
function run() {
    if(running===false){
        for(var pixelX=0;pixelX<96;pixelX++){
            for(var pixelY=0;pixelY<64;pixelY++){
                pixels[(pixelX,pixelY)]=0;         //doesn't work
            }
        }
        variables = [];
        loopStack = [];
        ifStack = [true];
        typeStack = ['if'];
        repeatStack = [];
        clear();
        var code = document.getElementById('code-block');
        var lines = code.value.split('\n');
        var currentLine = 0;
        variables['e'] = Math.E;
        variables['pi'] = Math.PI;
        L1.push(3);
        L1.push(4);
        lineProcessing = setInterval(function(){
            if(!awaitingInput){
                running = true;
                if(currentLine < lines.length || loopStack.length !== 0){
                    currentLine = processLine(lines,currentLine);
                    currentLine++;
                }
                else{
                    running = false;
                    lineProcessing.clearInterval();
                }
            }
        },10);
    }
}
function processLine(lines, currentLine) {
    var tokens = tokenize(lines[currentLine]);
    if (ifStack[ifStack.length - 1] === true){
        if (tokens.indexOf('-->') !== -1) {
            var index = tokens.indexOf('-->');
            for (var i = 0; i < index; i++) {
                tokens[i] = replaceVars(tokens[i]);
            }
            var value = eval(tokens[index - 1]);
            var name = tokens[index + 1];
            var listElement = false;
            if (name.length > 4) {
                if (/L[1-6]\(/.test(name) === true) {
                    var nextP = matchParenthese(name, 2);
                    var innerStuff = name.substring(3, nextP);
                    var value2 = eval(replaceVars(innerStuff));
                    var listNum = name.substring(0, 2);
                    if (value2 === Math.round(value2)) {
                        if (value2 > 0) {
                            if (listNum === 'L1') {
                                L1[value2] = value;
                            }
                            if (listNum === 'L2') {
                                L2[value2] = value;
                            }
                            if (listNum === 'L3') {
                                L3[value2] = value;
                            }
                            if (listNum === 'L4') {
                                L4[value2] = value;
                            }
                            if (listNum === 'L5') {
                                L5[value2] = value;
                            }
                            if (listNum === 'L6') {
                                L6[value2] = value;
                            }
                        } else {
                            printE('Arrays can only have positive indexes, and by some weird quirk of Texas Instruments, indexes start at 1, not 0.');
                            throw new Error();
                        }
                    } else {
                        printE('Sorry, this is not Javascript. Array indexes must be integers.');
                        throw new Error();
                    }
                }
            }
            variables[name + ""] = value;
        }
        if (tokens.indexOf('disp') !== -1) {
            var index2 = tokens.indexOf('disp');
            for (var g = index2 + 1; g < tokens.length; g++) {
                tokens[g] = replaceVars(tokens[g]);
                if (tokens[g] === ',') {} else {
                    print(eval(tokens[g]));
                }
            }
        }
        if(tokens.indexOf('prompt')!==-1){
            var pIndex = tokens.indexOf('prompt');
            var newVar = tokens[pIndex+1];
            document.getElementById('output').value += newVar+'=?';
            awaitingInput=true;
            newInputVarName = newVar;
        }
        if(tokens.indexOf('input')!==-1){
            var pIndex = tokens.indexOf('input');
            var newVar = tokens[pIndex+1];
            document.getElementById('output').value += '?';
            awaitingInput=true;
            newInputVarName = newVar;
        }
        if(tokens.indexOf('pause')!==-1){
            awaitingInput=true;
            newInputVarName = null;
        }
        if(tokens.indexOf('pxl-on')!==-1){
            var first = tokens[2].substring(1,tokens[2].length);
            var second = tokens[4].substring(0,tokens[4].length-1);
            first = replaceVars(first);
            second = replaceVars(second);
            var valueF = eval(first);
            var valueS = eval(second);
            drawPixel(valueF,valueS,1);
            pixels[(valueF,valueS)] = 1;
        }
        if(tokens.indexOf('pxl-off')!==-1){
            var first2 = tokens[2].substring(1,tokens[2].length);
            var second2 = tokens[4].substring(0,tokens[4].length-1);
            first2 = replaceVars(first2);
            second2 = replaceVars(second2);
            var valueF2 = eval(first2);
            var valueS2 = eval(second2);
            pixels[(valueF2,valueS2)] = 0;
            drawPixel(valueF2,valueS2,0);
        }
        if(tokens.indexOf('clrdraw')!==-1){
            clearPixels();
        }
        if (tokens.indexOf('if') !== -1) {
            var index3 = tokens.indexOf('if');
            tokens[index3 + 1] = replaceVars(tokens[index3 + 1]);
            var value = eval(tokens[index3 + 1]);
            ifStack.push(value);
            typeStack.push('if');
        }
        if (tokens.indexOf('while') !== -1) {
            var index4 = tokens.indexOf('while');
            var expression = tokens[index4 + 1];
            expression = replaceVars(expression);
            var value2 = eval(expression);
            if (value2 === true) {
                typeStack.push('while');
                loopStack.push(currentLine);
            } else {
                currentLine = nextEnd(currentLine, lines);
                return currentLine;
            }
        }
        if (tokens.indexOf('for') !== -1) {
            var index5 = tokens.indexOf('for');
            var varName = tokens[index5 + 1];
            varName = varName.trim();
            varName = varName.substring(1, varName.length);
            var initValue = eval(replaceVars(tokens[index5 + 3]));
            variables[varName] = initValue;
            var endingValue = eval(replaceVars(tokens[index5 + 5]));
            var increaseVal = replaceVars(tokens[index5 + 7]);
            increaseVal = eval(increaseVal.substring(0, increaseVal.length - 1));
            var diff = endingValue - initValue;
            if (sign(diff) !== sign(increaseVal) && sign(diff) !== 0) {
                currentLine = nextEnd(currentLine, lines);
                return currentLine;
            } else {
                loopStack.push(currentLine);
                typeStack.push('for');
            }
        }
        if (tokens.indexOf('repeat') !== -1) {
            var repeatVal = eval(replaceVars(tokens[1]));
            if (repeatVal === true || repeatVal === false) {
                //basically the same as a while loop...so just replace it!
                var newLine = 'while ' + '!(' + tokens[1] + ')';
                lines[currentLine] = newLine;
                currentLine--;
                return currentLine;
            } else {
                repeatStack.push(0);
                typeStack.push('repeat');
                loopStack.push(currentLine);
            }
        }
    }
    if (tokens.indexOf('else') !== -1) {
       ifStack[ifStack.length - 1] = !ifStack[ifStack.length - 1];
    }
    if (tokens.indexOf('end') !== -1) {
        if (typeStack[typeStack.length - 1] === 'if') {
            ifStack.pop();
            typeStack.pop();
        } else if (typeStack[typeStack.length - 1] === 'while') {
            var prevLineNum = loopStack[loopStack.length - 1];
            var prevLine = lines[prevLineNum];
            var prevTokens = tokenize(prevLine);
            var whileIndex = prevTokens.indexOf('while');
            var expression2 = replaceVars(prevTokens[whileIndex + 1]);
            if (eval(expression2) === true) {
                currentLine = prevLineNum - 1;
                return currentLine;
            } else {
                loopStack.pop();
                typeStack.pop();
            }
        } else if (typeStack[typeStack.length - 1] === 'repeat') {
            var line = lines[loopStack[loopStack.length - 1]];
            var tokenized = tokenize(line);
            var numTimes = eval(replaceVars(tokenized[1]));
            var currentIteration = repeatStack[repeatStack.length - 1];
            currentIteration++;
            if (currentIteration !== numTimes) {
                repeatStack[repeatStack.length - 1] = currentIteration;
                currentLine = loopStack[loopStack.length - 1]; //once currentLine++, will be on next line after repeat;
                return currentLine;
            } else {
                repeatStack.pop();
                typeStack.pop();
                loopStack.pop();
            }
        } else if (typeStack[typeStack.length - 1] === 'for') {
            var prevLineNum2 = loopStack[loopStack.length - 1];
            var prevLine2 = lines[prevLineNum2];
            var prevTokens2 = tokenize(prevLine2);
            var forIndex = prevTokens2.indexOf('for');
            var varName2 = prevTokens2[forIndex + 1];
            varName2 = varName2.substring(1, varName2.length);
            var initVal = eval(replaceVars(prevTokens2[forIndex + 3]));
            var endVal = eval(replaceVars(prevTokens2[forIndex + 5]));
            var stepVal = replaceVars(prevTokens2[forIndex + 7]);
            stepVal = eval(stepVal.substring(0, stepVal.length - 1));
            variables[varName2] = parseInt(stepVal, 10) + parseInt(variables[varName2], 10);
            var signDiff = sign(endVal - initVal);
            var condTrue = true;
            if (signDiff === 0) {
                condTrue = false;
            }
            if (signDiff === 1) {
                if (variables[varName2] > endVal) {
                    condTrue = false;
                }
            } else {
                if (variables[varName2] < endVal) {
                    condTrue = false;
                }
            }
            if (condTrue === true) {
                currentLine = prevLineNum2;
                return currentLine;
            } else {
                typeStack.pop();
                loopStack.pop();
            }
        }
    }
    return currentLine;
}

function sign(num) {
    if (num === 0) {
        return 0;
    }
    if (num > 0) {
        return 1;
    }
    return -1;
}
function drawPixel(x,y,color){//color = 0 if off, 1 if on
    var canvas = document.getElementById('canvas');
    var width = canvas.width;
    var height = canvas.height;
    var pxWidth = width/96;
    var pxHeight = height/64;
    var ctx = canvas.getContext('2d');
    if(color===1){
        ctx.fillStyle = '#000';
    }else{
        ctx.fillStyle = '#fff';
    }
    ctx.fillRect((x/96)*width, (y/64)*height,pxWidth,pxHeight);
}
function nextEnd(line, lines) {
    for (var i = line + 1; i < lines.length; i++) {
        if (lines[i].indexOf('end') !== -1) {
            return i;
        }
    }
    return line;
}

function tokenize(line) {
    line = line.trim();
    var split = [];
    var currentSplit = '';
    var cursor = 0;
    var unknown = '';
    var allTokens = tokens;
    while (cursor < line.length) {
        var lengthOfCursor = line.length - cursor;
        while (lengthOfCursor > 0) {
            var index = allTokens.indexOf(line.substring(cursor, cursor + lengthOfCursor));
            if (index !== -1) {
                if (unknown !== '') {
                    split.push(unknown.trim());
                }
                unknown = '';
                split.push(allTokens[index]);
                cursor += lengthOfCursor - 1; //for cursor++ later
                break;
            }
            lengthOfCursor--;
            if (lengthOfCursor === 0) {
                unknown = unknown.concat(line.charAt(cursor));
            }
        }
        cursor++;
    }
    if (unknown !== '') {
        split.push(unknown.trim());
    }
    return split;
}

function replaceVars(token) {
    for (var i = 0; i < tokens.length; i++) {
        if (token === tokens[i]) {
            return token;
        }
    }
    //deals with lists
    for (var cursor = 0; cursor < token.length - 1; cursor++) {
        if (/[^a-zA-Z_]L[1-6]|^L[1-6]/.test(token.substring(cursor, cursor + 3)) === true) {
            var parenthIndex;
            for (var newCurs = cursor; newCurs < token.length; newCurs++) {
                if (token.charAt(newCurs) === '(') {
                    parenthIndex = newCurs;
                    break;
                }
            }
            var nextPIndex = matchParenthese(token, parenthIndex);
            var inner = token.substring(parenthIndex + 1, nextPIndex);
            //note recursiveness, supports L1(L1(L1(5))), for example
            var innerValue = eval(replaceVars(inner));
            var num = parseInt(token.substring(parenthIndex - 1, parenthIndex));
            var realValue;
            if (num === 1) {
                realValue = L1[innerValue];
            }
            if (num === 2) {
                realValue = L2[innerValue];
            }
            if (num === 3) {
                realValue = L3[innerValue];
            }
            if (num === 4) {
                realValue = L4[innerValue];
            }
            if (num === 5) {
                realValue = L5[innerValue];
            }
            if (num === 6) {
                realValue = L6[innerValue];
            }
            if (innerValue !== Math.round(innerValue) || innerValue < 1) {
                printE('array indexes must be integers greater or equal to 1. Why? ask Texas Instruments.');
                throw new Error();
            }
            token = token.substring(0, parenthIndex - 2) + realValue + token.substring(nextPIndex + 1);
        }
    }
    //Math stuff
    var a;
    var sinFunc = function (value) {
        return Math.sin(value);
    };
    while ((a = returnValue('sin', token, sinFunc)) !== null) {
        token = a;
    }

    var cosFunc = function (value) {
        return Math.cos(value);
    };
    while ((a = returnValue('cos', token, cosFunc)) !== null) {
        token = a;
    }

    var tanFunc = function (value) {
        return Math.tan(value);
    };
    while ((a = returnValue('tan', token, tanFunc)) !== null) {
        token = a;
    }

    var asinFunc = function (value) {
        return Math.asin(value);
    };
    while ((a = returnValue('asin', token, asinFunc)) !== null) {
        token = a;
    }

    var acosFunc = function (value) {
        return Math.acos(value);
    };
    while ((a = returnValue('acos', token, acosFunc)) !== null) {
        token = a;
    }

    var atanFunc = function (value) {
        return Math.atan(value);
    };
    while ((a = returnValue('atan', token, atanFunc)) !== null) {
        token = a;
    }
    //note my calculator doesn't have atan2...

    var absFunc = function (value) {
        return Math.abs(value);
    };
    while ((a = returnValue('abs', token, absFunc)) !== null) {
        token = a;
    }

    var roundFunc = function (value) {
        return Math.round(value);
    };
    while ((a = returnValue('round', token, roundFunc)) !== null) {
        token = a;
    }

    var intFunc = function (value) {
        return Math.floor(value);
    };
    while ((a = returnValue('int', token, intFunc)) !== null) {
        token = a;
    }

    var coshFunc = function (value) {
        return (Math.pow(Math.E, value) + Math.pow(Math.E, -1 * value)) / 2;
    };
    while ((a = returnValue('cosh', token, coshFunc)) !== null) {
        token = a;
    }

    var sinhFunc = function (value) {
        return (Math.pow(Math.E, value) - Math.pow(Math.E, -1 * value)) / 2;
    };
    while ((a = returnValue('sinh', token, sinhFunc)) !== null) {
        token = a;
    }

    var tanhFunc = function (value) {
        return (Math.pow(Math.E, value) - Math.pow(Math.E, -1 * value)) / (Math.pow(Math.E, value) + Math.pow(Math.E, -1 * value));
    };
    while ((a = returnValue('tanh', token, tanhFunc)) !== null) {
        token = a;
    }

    //if token contains new variables, then initialize them with random values
    var newReg = new RegExp('([^a-zA-Z_])([a-zA-Z_]+)([^a-zA-Z_])', 'g');
    var newReg2 = new RegExp('^([a-zA-Z_]+)([^a-zA-Z_])', 'g');
    var newReg3 = new RegExp('([^a-zA-Z_])([a-zA-Z_]+)$', 'g');
    var newReg4 = new RegExp('^([a-zA-Z_]+)$', 'g');
    var match1 = token.match(newReg);
    if (match1 !== null) {
        for (var q = 0; q < match1.length; q++) {
            initializeVar(match1[q].substring(1, match1[q].length - 1));
        }
    }

    var match2 = token.match(newReg2);
    if (match2 !== null) {
        for (var w = 0; w < match2.length; w++) {
            initializeVar(match2[w].substring(0, match2[w].length - 1));
        }
    }

    var match3 = token.match(newReg3);
    if (match3 !== null) {
        for (var e = 0; e < match3.length; e++) {
            initializeVar(match3[e].substring(1, match3[e].length));
        }
    }

    var match4 = token.match(newReg4);
    if (match4 !== null) {
        for (var r = 0; r < match4.length; r++) {
            initializeVar(match4[r].substring(0, match4[r].length));
        }
    }

    var varNames = [];
    for (var key in variables) {
        if (token.indexOf(key) !== -1) {
            var regex1 = '([^a-zA-Z_])(' + key + ')([^a-zA-Z_])';
            var reg = new RegExp(regex1, 'g');
            token = token.replace(reg, '$1' + variables[key] + '$3');
            var regex2 = '(^' + key + ')([^a-zA-Z_])';
            var reg2 = new RegExp(regex2, 'g');
            token = token.replace(reg2, variables[key] + '$2');
            var regex3 = '([^a-zA-Z_])(' + key + ')$';
            var reg3 = new RegExp(regex3, 'g');
            token = token.replace(reg3, '$1' + variables[key]);
            var reg4 = new RegExp('^' + key + '$', 'g');
            token = token.replace(reg4, variables[key]);
        }
    }
    return token;
}

function matchParenthese(token, index) {
    var pStack = [];
    for (var cursor = index + 1; cursor < token.length; cursor++) {
        if (token.charAt(cursor) === '(') {
            pStack.push(1);
        }
        if (token.charAt(cursor) === ')') {
            if (pStack.length > 0) {
                pStack.pop();
            } else {
                return cursor;
            }
        }
    }
    return index;
}

function returnValue(identifyer, token, toDoFunction) { //note: do NOT include parentheses in identifyer
    for (var cursor = 0; cursor < token.length; cursor++) {
        if (token.length > cursor + identifyer.length) {
            if (token.substring(cursor, cursor + identifyer.length) === identifyer) {
                if (cursor === 0 || /^[^a-zA-Z_]/.test(token.charAt(cursor - 1)) === true) {
                    if (nextNonWhiteChar(token, cursor + identifyer.length - 1) === '(') {
                        print(identifyer);
                        var nextParen = matchParenthese(token, cursor + identifyer.length + 1);
                        var inner = token.substring(cursor + identifyer.length + 1, nextParen);
                        inner = replaceVars(inner);
                        var value = eval(inner);
                        value = toDoFunction(value);
                        return token.substring(0, cursor) + value + token.substring(nextParen + 1, token.length);
                    }
                }
            }
        }

    }
    return null;
}

function nextNonWhiteChar(token, index) { //give non-white character
    for (var i = index + 1; i < token.length; i++) {
        if (token.charAt(i) !== ' ') {
            return token.charAt(i);
        }
    }
}
function clearPixels(){
    var canvas = document.getElementById('canvas');
    canvas.getContext('2d').fillStyle = '#fff';
    canvas.getContext('2d').fillRect(0,0,canvas.width,canvas.height);
    for(var x=0;x<96;x++){
        for(var y=0;y<64;y++){
            pixels[(x,y)] = 0;
        }
    }
}
function stop(){
    running = false;
    clearInterval(lineProcessing);
}
function initializeVar(name) {
    if (!(name in variables) && name !== 'L1' && name !== 'L2' && name !== 'L3' && name !== 'L4' && name !== 'L5' && name !== 'L6') {
        variables[name] = Math.random() * 100;
    }
}

function clear() {
    document.getElementById('output').value = '';
    document.getElementById('error').value = '';
}

function printE(str) {
    document.getElementById('error').value += str + '\n'
}

function print(str) {
    document.getElementById('output').value += str + '\n';
}
<h1>Ti-Basic Interpreter</h1>


<h3>Code</h3>

<textarea id='code-block' cols='50' rows='7' oninput='inputChanged()'>:clrdraw
:for(a,2,96,2)
:for(b,(a/2)%2,64,2)
:pxl-on(a,b)
:end
:end</textarea>
<br>
<button width='20' height='10' onclick='run()'>Run</button>
    <button width='20' height='10' onclick='stop()'>Stop</button>

<h3>Output (also input)</h3>

    <textarea id='output' cols='50' rows='7' onkeypress='onUserInput(event)'></textarea>
<br>
    <canvas id='canvas' width='192' height='128'></canvas>
<br>

<textarea id='error' cols='50' rows='7'></textarea>


关于javascript思维,然后显示,浏览器在单个线程中运行。DOM对象(实际网页)的呈现发生在同一线程上,因此,在代码产生执行之前,该页面将不会更新。解决该问题的方法是使用setTimeout函数(w3schools.com/jsref/met_win_settimeout.asp)产生,以便浏览器可以呈现您的更新,然后继续进行处理
pseudonym117

1
我正在用这段代码做事!我发现了一个错误:〜93行,您有lineProcessing.clearInterval();; 这应该是clearInterval(lineProcessing);
Conor O'Brien

@CᴏɴᴏʀO'Bʀɪᴇɴ随时进行编辑和更改。自从我看这段代码已经有一段时间了。另外,我现在有点忙于学校作业,因此可能要过一段时间才能更改任何内容。
拉伸狂人

3
@StretchManiac啊,好的。我了解-拖延ATM ^ _ ^
Conor O'Brien 2015年

26

脑干

这是基本的BF解释器。虽然没有调试器,但是有几个自定义选项。

const NUM_CELLS = 30000;
const ITERS_PER_SEC = 100000;
const TIMEOUT_MILLISECS = 5000;
const ERROR_BRACKET = "Mismatched brackets";
const ERROR_TIMEOUT = "Timeout";
const ERROR_INTERRUPT = "Interrupted by user";

var code, input, wrap, timeout, eof, loop_stack, loop_map;
var running, start_time, code_ptr, input_ptr, cell_ptr, cells, iterations;

function clear_output() {
  document.getElementById("output").value = "";
  document.getElementById("stderr").innerHTML = "";
}

function stop() {
  running = false;
  document.getElementById("run").disabled = false;
  document.getElementById("stop").disabled = true;
  document.getElementById("clear").disabled = false;
  document.getElementById("wrap").disabled = false;
  document.getElementById("timeout").disabled = false;
  document.getElementById("eof").disabled = false;
}

function interrupt() {
  error(ERROR_INTERRUPT);
}

function error(msg) {
  document.getElementById("stderr").innerHTML = msg;
  stop();
}

function run() {
  clear_output();
  
  document.getElementById("run").disabled = true;
  document.getElementById("stop").disabled = false;
  document.getElementById("clear").disabled = true;
  document.getElementById("wrap").disabled = true;
  document.getElementById("timeout").disabled = true;
  document.getElementById("eof").disabled = true;

  code = document.getElementById("code").value;
  input = document.getElementById("input").value;
  wrap = document.getElementById("wrap").value;
  timeout = document.getElementById("timeout").checked;
  eof = document.getElementById("eof").value;

  loop_stack = [];
  loop_map = {};

  for (var i = 0; i < code.length; ++i) {
    if (code[i] == "[") {
      loop_stack.push(i);

    } else if (code[i] == "]") {
      if (loop_stack.length == 0) {
        error(ERROR_BRACKET);
        return;

      } else {
        var last_bracket = loop_stack.pop();
        loop_map[last_bracket] = i;
        loop_map[i] = last_bracket;
      }
    }
  }

  if (loop_stack.length > 0) {
    error(ERROR_BRACKET);
    return;
  }

  running = true;
  start_time = Date.now();
  code_ptr = 0;
  input_ptr = 0;
  cell_ptr = Math.floor(NUM_CELLS / 2);
  cells = {};
  iterations = 0;

  bf_iter(1);
}

function bf_iter(niters) {
  if (code_ptr >= code.length || !running) {
    stop();
    return;
  }

  var iter_start_time = Date.now();

  for (var i = 0; i < niters; ++i) {
    if (cells[cell_ptr] == undefined) {
      cells[cell_ptr] = 0;
    }

    switch (code[code_ptr]) {
      case "+":
        if ((wrap == "8" && cells[cell_ptr] == 255) ||
            (wrap == "16" && cells[cell_ptr] == 65535) || 
            (wrap == "32" && cells[cell_ptr] == 2147483647)) {
            cells[cell_ptr] = 0;
        } else {
          cells[cell_ptr]++;
        }
        break;
        
      case "-":
        if (cells[cell_ptr] == 0 && wrap != "-1"){
          if (wrap == "8"){ cells[cell_ptr] = 255 }
          if (wrap == "16"){ cells[cell_ptr] = 65535 }
          if (wrap == "32"){ cells[cell_ptr] = 2147483647 }    
        } else {
          cells[cell_ptr]--;
        }
        break;
      
      case "<": cell_ptr--; break;
      case ">": cell_ptr++; break;

      case ".":
        document.getElementById("output").value += String.fromCharCode(cells[cell_ptr]);
        break;

      case ",":
        if (input_ptr >= input.length) {
          if (eof != "nochange") {
            cells[cell_ptr] = parseInt(eof);
          }
        } else {
          cells[cell_ptr] = input.charCodeAt(input_ptr);
          input_ptr++;
        }
        break;

      case "[":
        if (cells[cell_ptr] == 0) {
          code_ptr = loop_map[code_ptr];
        }
        break;

      case "]":
        if (cells[cell_ptr] != 0) {
          code_ptr = loop_map[code_ptr];
        }
        break;
    }

    code_ptr++;
    iterations++;

    if (timeout && Date.now() - start_time > TIMEOUT_MILLISECS) {
      error(ERROR_TIMEOUT);
      return;
    }
  }

  setTimeout(function() { bf_iter(ITERS_PER_SEC * (Date.now() - iter_start_time)/1000) }, 0);
}
<div style="font-size:12px;font-family:Verdana, Geneva, sans-serif;">
  <div style="float:left; width:50%;">
    Code:
    <br>
    <textarea id="code" rows="4" style="overflow:scroll;overflow-x:hidden;width:90%;">>++++++++[<+++++++++>-]<.>>+>+>++>[-]+<[>[->+<<++++>]<<]>.+++++++..+++.>>+++++++.<<<[[-]<[-]>]<+++++++++++++++.>>.+++.------.--------.>>+.>++++.</textarea>
    <br>Input:
    <br>
    <textarea id="input" rows="2" style="overflow:scroll;overflow-x:hidden;width:90%;"></textarea>
    <p>
      Wrap:
      <select id="wrap">
        <option value="8">8-bit</option>
        <option value="16">16-bit</option>
        <option value="32">32-bit</option>
        <option value="-1">None</option>
      </select>
      &nbsp;
      Timeout:
      <input id="timeout" type="checkbox" checked="true" />&nbsp; EOF:
      <select id="eof">
        <option value="nochange">Same</option>
        <option value="0">0</option>
        <option value="-1">-1</option>
      </select>
    </p>
  </div>
  <div style="float:left; width:50%;">
    Output:
    <br>
    <textarea id="output" rows="6" style="overflow:scroll;width:90%;"></textarea>
    <p>
      <input id="run" type="button" value="Run" onclick="run()" />
      <input id="stop" type="button" value="Stop" onclick="interrupt()" disabled="true" />
      <input id="clear" type="button" value="Clear" onclick="clear_output()" />
      &nbsp;
      <span id="stderr" style="color:red"></span>
    </p>
  </div>
</div>

在我的浏览器中,所有内容都包含在扩展的代码段中,但是我并不是浏览器兼容性方面的专家,因此我无法保证每个人都一样。

特征

  • 可以配置单元格换行和EOF行为
  • 可以绕过超时
  • 程序可以在执行中停止
  • 一个不错的小清除按钮
  • 所有这些都在“堆栈代码段”框中(或至少我尝试过),并在大约6k个字符处进行计时

这是给你一只猫的程序进行测试:,.[>,.]警告:不要与大文本试试)。但是这段代码很不错!
Ismael Miguel 2014年

@IsmaelMiguel怎么样+[,.]:)
seequ 2014年

1
@IsmaelMiguel就我个人而言,我不希望在循环内移动指针,这样人们就不会注意到磁带是受Javascript整数大小限制的:P
Sp3000

1
@ Sp3000好吧,如果您可以阅读文本长度,则可以确保代码可以正常运行。如果您设法获得长度为0xFFFFFFFFFFFFFFFFFF且我的代码为“ die”的文本,那么您可以向Sieg授予奖章。
Ismael Miguel 2014年

1
我建议您使用该功能的快捷方式document.getElementById()
AL 2014年

21

Marbelous *

这个解释器可能充满了错误。如果您找到任何信息,请告诉我。

这支持Marbelous的所有功能,除了#include。还有一个使用圆柱形板的选项(从板的侧面推开的大理石会重新出现在另一侧)。

该解释器还随附有许多板,这些示例在Marbelous的python解释器的回购示例中提供。这些板卡如下:

dec_out.mbl - Dp, Decout
hex_out.mbl - Hp, Hexo
fourwayincrement.mbl - Fwin
threewaysplit.mbl - 3W
bitwise_operations.mbl - Bitx, Bdif, Borr, Band, Bxor, Bnor
logical_operations.mbl - Tf, Nt, Lorr, Land, Lnor, Lxor, Cmpr, Eqal, Gteq, Lteq, Grtr, Less, Sort
replace_input.mbl - Replac
adder.mbl - Plus
arithmetic.mbl - Subt, Subo, Subful, Addo, Addful, Mult
wide_devices.mbl - Wideadditionfunc, Widesubtractfunc, Wbitleft, Wbitrght, Wbitfetchx, Mulx, Doblmult, Widemultiplyfunc

不应使用任何包含。有关这些开发板各自功能的详细信息,请参见python解释器的repo。

*有关Marbelous的更多信息:

注意:此解释器提供图形输出(256x256双缓冲,具有24位RGB颜色)。以下板可用于此目的:

{}{}{}{}{} - Set Pixel for Back Buffer (}0 - x, }1 - y, }2 - red, }3 - green, }4 - blue)
>< - Swap Buffers and Clear Back Buffer (}0 - anything)
@f@f@f - Get Pixel from Front Buffer (}0 - x, }1 - y, {0 - red, {1 - green, {2 - blue)
@b@b@b - Get Pixel from Back Buffer (}0 - x, }1 - y, {0 - red, {1 - green, {2 - blue)

请注意,Use Draw Buffers检查(图形输出所需)可能会降低解释器的速度,并且图形输出速度很慢。

更新:将缓冲区更改为使用画布而不是1x1像素div;现在绘图应该快得多。

var stdin = "";
var boards = {};
var bnames = [];
var max_tick = 1000;
var space_as_blank = false;
var cylindrical_board = false;
var print_numbers = false;
var libraries = true;
var stopped = false;
var gfx = false;
var front_buffer = null;
var back_buffer = null;
function base36(a){	return a.toString(36).toUpperCase();}
function base16(a){	return ("0"+a.toString(16).toUpperCase()).substr(-2);}
function wto16(arr, i){ return arr[i] << 8 | arr[i+1]; }
function wto32(arr, i){ return arr[i] << 24 | arr[i+1] << 16 | arr[i+2] << 8 | arr[i+3]; }
function getch(){
var p = stdin.substr(0, 1);
stdin = stdin.substr(1);
if(p === '') return -1;
else return p.charCodeAt(0) & 255;
}
function putch(obj, ch){
if(print_numbers)
	obj.stdout += ch + ' ';
else
	obj.stdout += String.fromCharCode(ch);
}
function longestMatch(search, list){
var best = null, blen = 0;
for(var i = 0, len = list.length; i < len; ++i)
	if(list[i].length > blen && search.indexOf(list[i]) === 0)
		best = list[i], blen = best.length;
return best;
}
function swapBuffers(){
front_buffer.ctx.drawImage(back_buffer.canvas, 0, 0);
back_buffer.clear();
}

function jsBoard(name, inc, outc, code){
var f = eval('(function(inp, self){return ' + code + ';})');
boards[name] = new Board(name, true, f, inc, outc);
bnames.push(name);
}
function loadDefaultBoards(){
// implement \\ // /\ \/ ++ -- >> << ~~ ]] +n -n ?? ?n ^n =n >n <n as js boards
jsBoard('\\\\', 1, 0, '{37: inp[0]}');
jsBoard('//', 1, 0, '{36: inp[0]}');
jsBoard('/\\', 1, 0, '{36: inp[0], 37: inp[0]}');
jsBoard('\\/', 1, 0, '{}');
jsBoard('++', 1, 1, '{0: (inp[0]+1)&255}');
jsBoard('--', 1, 1, '{0: (inp[0]+255)&255}');
jsBoard('>>', 1, 1, '{0: inp[0]>>1}');
jsBoard('<<', 1, 1, '{0: (inp[0]<<1)&255}');
jsBoard('~~', 1, 1, '{0: (~inp[0])&255}');
jsBoard(']]', 1, 1, '(c=getch())>-1?{0:c}:{37:inp[0]}');
jsBoard('??', 1, 1, '{0: Math.floor(Math.random()*(inp[0]+1))}');
for(var i = 0; i < 36; ++i){
	j = base36(i);
	jsBoard('+'+j, 1, 1, '{0: (inp[0]+'+i+')&255}');
	jsBoard('-'+j, 1, 1, '{0: (inp[0]-'+i+')&255}');
	jsBoard('?'+j, 1, 1, '{0: Math.floor(Math.random()*'+(i+1)+')}');
	jsBoard('='+j, 1, 1, 'inp[0]=='+i+'?{0: inp[0]}:{37: inp[0]}');
	jsBoard('>'+j, 1, 1, 'inp[0]>'+i+'?{0: inp[0]}:{37: inp[0]}');
	jsBoard('<'+j, 1, 1, 'inp[0]<'+i+'?{0: inp[0]}:{37: inp[0]}');
	if(i < 8){
		jsBoard('^'+j, 1, 1, '{0: !!(inp[0]&(1<<'+i+'))}');
	}
}
if(gfx){
	jsBoard('{}{}{}{}{}', 5, 0, 'back_buffer.set(inp[0], inp[1], inp[2], inp[3], inp[4]),{}');
	jsBoard('@f@f@f', 2, 3, 'front_buffer.get(inp[0], inp[1])');
	jsBoard('@b@b@b', 2, 3, 'back_buffer.get(inp[0], inp[1])');
	jsBoard('><', 1, 0, 'swapBuffers(), {}');
}
if(libraries){
	// dec_out.mbl - Dp, Decout
	jsBoard('Dp', 1, 0, 'putch(self,Math.floor(inp[0]/100)+0x30),putch(self,Math.floor(inp[0]/10)%10+0x30),putch(self,inp[0]%10+0x30),{}');
	jsBoard('Decout', 1, 3, '{0: inp[0]/100, 1: inp[0]/10%10, 2: inp[0]%10}');
	// hex_out.mbl - Hp, Hexo
	jsBoard('Hp', 1, 0, 's=base16(inp[0]),putch(self,s.charCodeAt(0)),putch(self,s.charCodeAt(1)),{}');
	jsBoard('Hexo', 1, 2, 's=base16(inp[0]),{0: s.charCodeAt(0), 1: s.charCodeAt(1)}');
	// fourwayincrement.mbl - Fwin
	jsBoard('Fwin', 1, 2, '{36: inp[0], 0: (inp[0]+1)&255, 1: (inp[0]+1)&255, 37: (inp[0]+2)&255}');
	// threewaysplit.mbl - 3W
	jsBoard('3W', 1, 1, '{0: inp[0], 36: inp[0], 37: inp[0]}');
	// bitwise_operations.mbl - Bitx, Bdif, Borr, Band, Bxor, Bnor
	jsBoard('Bitx', 2, 1, 'inp[1]<8?{0: !!(inp[0] & (1 << inp[1]))}:{}');
	jsBoard('Bdif', 2, 1, 'b=inp[0]^inp[1],{0: !!(b&1)+!!(b&2)+!!(b&4)+!!(b&8)+!!(b&16)+!!(b&32)+!!(b&64)+!!(b&128)}');
	jsBoard('Borr', 2, 1, '{0: inp[0]|inp[1]}');
	jsBoard('Band', 2, 1, '{0: inp[0]&inp[1]}');
	jsBoard('Bxor', 2, 1, '{0: inp[0]^inp[1]}');
	jsBoard('Bnor', 2, 1, '{0: ~(inp[0]|inp[1])}');
	// logical_operations.mbl - Tf, Nt, Lorr, Land, Lnor, Lxor, Cmpr, Eqal, Gteq, Lteq, Grtr, Less, Sort
	jsBoard('Tf', 1, 1, '{0: (inp[0]>0)|0}');
	jsBoard('Nt', 1, 1, '{0: (!inp[0])|0}');
	jsBoard('Lorr', 2, 1, '{0: (inp[0]||inp[1])|0}');
	jsBoard('Land', 2, 1, '{0: (inp[0]&&inp[1])|0}');
	jsBoard('Lnor', 2, 1, '{0: !(inp[0]||inp[1])|0}');
	jsBoard('Lxor', 2, 1, '{0: (!inp[0]!=!inp[1])|0}');
	jsBoard('Cmpr', 2, 1, '{0: inp[0]>inp[1]?1:inp[0]<inp[1]?-1:0}');
	jsBoard('Eqal', 2, 1, '{0: (inp[0] == inp[1])|0}');
	jsBoard('Gteq', 2, 1, '{0: (inp[0] >= inp[1])|0}');
	jsBoard('Lteq', 2, 1, '{0: (inp[0] <= inp[1])|0}');
	jsBoard('Grtr', 2, 1, '{0: (inp[0] > inp[1])|0}');
	jsBoard('Less', 2, 1, '{0: (inp[0] < inp[1])|0}');
	jsBoard('Sort', 2, 2, '{0: Math.min(inp[0],inp[1]), 1: Math.max(inp[0],inp[1])}');
	// replace_input.mbl - Replac
	jsBoard('Replac', 3, 1, '{0: inp[0]==inp[1]?inp[2]:inp[0]}');
	// adder.mbl - Plus
	jsBoard('Plus', 2, 1, '{0: (inp[0] + inp[1])&255}');
	// arithmetic.mbl - Subt, Subo, Subful, Addo, Addful, Mult
	jsBoard('Subt', 2, 1, '{0: (inp[0] - inp[1])&255}');
	jsBoard('Subo', 2, 1, '{0: (inp[0] - inp[1])&255, 36: (inp[0] < inp[1])|0}');
	jsBoard('Subful', 3, 1, 'a=(inp[0] - inp[1])&255,{0: (a + 256 - inp[2])&255, 36: (inp[0]<inp[1])+(a<inp[2])}');
	jsBoard('Addo', 2, 1, 'a=inp[0]+inp[1],{0: a&255, 36: (a>255)|0}');
	jsBoard('Addful', 3, 1, 'a=inp[0]+inp[1]+inp[2],{0: a&255, 36: (a>255)|0}');
	jsBoard('Mult', 2, 1, '{0: (inp[0]*inp[1])&255}');
	// wide_devices.mbl - Wideadditionfunc, Widesubtractfunc, Wbitleft, Wbitrght, Wbitfetchx, Mulx, Doblmult, Widemultiplyfunc
	jsBoard('Wideadditionfunc', 8, 4, 'c=(wto32(inp,0)+wto32(inp,4))&0xFFFFFFFF,{0:  (c&0xFF000000)>>>24, 1: (c&0x00FF0000)>>>16, 2: (c&0x0000FF00)>>>8, 3: (c&0x000000FF)}');
	jsBoard('Widesubtractfunc', 8, 4, 'c=(wto32(inp,0)-wto32(inp,4))&0xFFFFFFFF,{0: (c&0xFF000000)>>>24, 1: (c&0x00FF0000)>>>16, 2: (c&0x0000FF00)>>>8, 3: (c&0x000000FF)}');
	jsBoard('Wbitleft', 4, 4, 'c=(wto32(inp,0)<<1)&0xFFFFFFFF,{0: (c&0xFF000000)>>>24, 1: (c&0x00FF0000)>>>16, 2: (c&0x0000FF00)>>>8, 3: (c&0x000000FF)}');
	jsBoard('Wbitrght', 4, 4, 'c=wto32(inp,0)>>>1,{0: (c&0xFF000000)>>>24, 1: (c&0x00FF0000)>>>16, 2: (c&0x0000FF00)>>>8, 3: (c&0x000000FF)}');
	jsBoard('Wbitfetchx', 5, 1, 'inp[4]<32?{0:!!(wto32(inp,0)&(1<<inp[4]))}:{}');
	jsBoard('Mulx', 2, 2, 'c=(inp[0]*inp[1])&0xFFFF,{0: (c&0xFF00)>>>8, 1: (c&0x00FF)}');
	jsBoard('Doblmult', 4, 4, 'c=(wto16(inp,0)*wto16(inp,2))&0xFFFFFFFF,{0: (c&0xFF000000)>>>24, 1: (c&0x00FF0000)>>>16, 2: (c&0x0000FF00)>>>8, 3: (c&0x000000FF)}');
	jsBoard('Widemultiplyfunc', 8, 4, 'c=(wto32(inp,0)*wto32(inp,4))&0xFFFFFFFF,{0: (c&0xFF000000)>>>24, 1: (c&0x00FF0000)>>>16, 2: (c&0x0000FF00)>>>8, 3: (c&0x000000FF)}');
}
}
// most devices are implemented as js subboards
var CTypes = {
PORTAL:			1,
SYNCHRONISER:	2,
INPUT:			3,
OUTPUT:			4,
TERMINATE:		5,
SUBROUTINE:		6,
LITERAL:		7,
};
function Cell(type,value){
this.type = type;
this.value = value;
}
Cell.prototype.copy = function(other){
this.type = other.type;
this.value = other.value;
};
function Board(name, js, jsref, jsinc, jsoutc){
this.name = name;
this.stdout = "";
if(!js){
	this.cells = [];
	this.marbles = [];
	this.cols = 0;
	this.rows = 0;
	this.inputs = [];
	this.outputs = [];
	this.syncs = [];
	this.portals = [];
	this.terminators = [];
	for(var i = 0; i < 36; ++i){
		this.inputs[i] = [];
		this.outputs[i] = [];
		this.syncs[i] = [];
		this.portals[i] = [];
	}
	this.outputs[36] = []; // {<
	this.outputs[37] = []; // {>
	this.subroutines = []; // [r0,c0,board name,size,inputs,outputs]
	this.type = 0;
	this.inc = 0; 
	this.outc = 0;
}else{
	this.func = jsref;
	this.inc = jsinc;
	this.outc = jsoutc;
	this.type = 1;
}
}
Board.prototype.set = function(row,col,cell){
if(row >= this.rows){
	for(var r = this.rows; r <= row; ++r){
		this.cells[r] = [];
		this.marbles[r] = [];
	}
}
this.rows = Math.max(this.rows, row + 1);
this.cols = Math.max(this.cols, col + 1);
if(!cell){
	this.cells[row][col] = null;
	return;
}
if(!this.cells[row][col])
	this.cells[row][col] = new Cell;
this.cells[row][col].copy(cell);
if(cell.type == CTypes.LITERAL){
	this.marbles[row][col] = cell.value;
}else{
	this.marbles[row][col] = null;
}
};
Board.prototype.get = function(r,c){
return this.cells[r][c];
};
Board.prototype.init = function(){
if(this.type == 0){
	var maxin = 0, maxout = 0;
	for(var r = 0; r < this.rows; ++r){
		for(var c = 0; c < this.cols; ++c){
			if(this.cells[r][c] == null) continue;
			switch(this.cells[r][c].type){
				case CTypes.PORTAL:
					this.portals[this.cells[r][c].value].push([r,c]);
				break;
				case CTypes.SYNCHRONISER:
					this.syncs[this.cells[r][c].value].push([r,c]);
				break;
				case CTypes.INPUT:
					this.inputs[this.cells[r][c].value].push([r,c]);
					maxin = Math.max(this.cells[r][c].value + 1, maxin);
				break;
				case CTypes.OUTPUT:
					if(this.cells[r][c].value != '<' && this.cells[r][c].value != '>'){
						this.outputs[this.cells[r][c].value].push([r,c]);
						maxout = Math.max(this.cells[r][c].value + 1, maxout);
					}else{
						this.outputs[this.cells[r][c].value == '<' ? 36 : 37].push([r,c]);
					}
				break;
				case CTypes.TERMINATE:
					this.terminators.push([r,c]);
				break;
			}
		}
	}
	this.inc = maxin;
	this.outc = maxout;
}
var namelen = Math.max(1, this.inc, this.outc) * 2;
this.name = new Array(namelen + 1).join(this.name).substr(0, namelen);
};
Board.prototype.validateSubr = function(names){
if(this.type == 1) return;
for(var r = 0, len = this.cells.length; r < len; ++r){
	var str = "", start = -1;
	for(var c = 0, rlen = this.cells[r].length; c < rlen; ++c){
		if(this.cells[r][c] && this.cells[r][c].type == CTypes.SUBROUTINE){
			if(start == -1) start = c;
			str += this.cells[r][c].value;
		}else if(start != -1){
			var match;
			while(str.length && (match = longestMatch(str, names))){
				var slen = match.length / 2;
				this.subroutines.push([r,start,match,slen,boards[match].inc,boards[match].outc]);
				start += slen;
				str = str.substr(slen * 2); 
			}
			if(str.length){
				throw "No subboard could be found near `" + str + "`";
			}
			start = -1;
			str = "";
		}
	}
	var match;
	while(str.length && (match = longestMatch(str, names))){
		var slen = match.length / 2;
		this.subroutines.push([r,start,match,slen,boards[match].inc,boards[match].outc]);
		start += slen;
		str = str.substr(slen * 2);
	}
	if(str.length){
		throw "No subboard could be found near `" + str + "`";
	}
}
};
Board.prototype.runCopy = function(inp){
var b = new Board('');
b.type = 2;
b.ref = this;
b.tickNum = 0;
if(this.type == 0){
	for(var r = 0, rlen = this.marbles.length; r < rlen; ++r){
		b.marbles[r] = [];
		for(var c = 0, clen = this.marbles[r].length; c < clen; ++c){
			b.marbles[r][c] = this.marbles[r][c]; 
		}
	}
	for(var i = 0; i < this.inc; ++i){
		if(inp[i] != null){
			var k = this.inputs[i];
			for(var j = 0, l = k.length; j < l; ++j){
				b.marbles[k[j][0]][k[j][1]] = ((parseInt(inp[i])&255)+256)&255;
			}
		}
	}
	b.cols = this.cols;
	b.rows = this.rows;
	b.inc = this.inc;
	b.outc = this.outc;
	b.stdout = "";
}else{
	b.inp = inp;
}
return b;
};
Board.prototype.tick = function(){
if(this.type != 2) throw "Calling Board.tick without Board.runCopy";
if(this.tickNum == -1) throw "Copied board has already run to completion";

if(this.ref.type == 1){
	this.tickNum = -1;
	this.outp = this.ref.func(this.inp, this);
	return moved;
}

var moved = false;

var new_marbles = [];
for(var r = 0; r <= this.rows; ++r) 
	new_marbles[r] = [];

// subroutines
for(var i = 0, len = this.ref.subroutines.length; i < len; ++i){
	var r = this.ref.subroutines[i][0], c = this.ref.subroutines[i][1], 
		name = this.ref.subroutines[i][2], sz = this.ref.subroutines[i][3],
		inc = this.ref.subroutines[i][4], outc = this.ref.subroutines[i][5];
	var all = true, inp = [];
	for(var j = 0; j < inc; ++j){
		if(this.marbles[r][c+j] == null && (boards[name].type == 1 || boards[name].inputs[j].length > 0)){
			all = false; break;
		}else{
			inp[j] = this.marbles[r][c+j];
		}
	}
	if(all){
		var cb = boards[name].runCopy(inp);
		while(cb.tickNum != -1 && cb.tickNum < max_tick) cb.tick();
		if(cb.tickNum != -1) throw "Max tick count exceeded running board `" + name + "`";
		var outp = cb.out();
		if(cb.stdout != "") moved = true;
		for(var j = 0; j < outc; ++j){
			if(outp[j] != null){
				new_marbles[r+1][c+j] = ((new_marbles[r+1][c+j]||0)+(outp[j]))&255;
				moved = true;
			}
		}
		if(outp[36] != null){ // left
			var left = c-1;
			if(left < 0 && cylindrical_board){
				left = this.cols - 1;
			}
			if(left >= 0){
				new_marbles[r][left] = ((new_marbles[r][left]||0)+(outp[36]))&255;
				moved = true;
			}
		}
		if(outp[37] != null){ // right
			var right = c+sz;
			if(right >= this.cols && cylindrical_board){
				right = 0;
			}
			if(right < this.cols){
				new_marbles[r][right] = ((new_marbles[r][right]||0)+(outp[37]))&255;
				moved = true;
			}
		}
		this.stdout += cb.stdout;
	}else{
		for(var j = 0; j < inc; ++j){
			if(this.marbles[r][c+j] != null){
				new_marbles[r][c+j] = ((new_marbles[r][c+j]||0)+(this.marbles[r][c+j]))&255;
			}					
		}
	}
}

// synchronisers
for(var i = 0; i < 36; ++i){
	if(this.ref.syncs[i].length){
		var all = true;
		for(var j = 0, len = this.ref.syncs[i].length; j < len; ++j){
			if(this.marbles[this.ref.syncs[i][j][0]][this.ref.syncs[i][j][1]] == null){
				all = false; break;
			}
		}
		if(all){
			for(var j = 0, len = this.ref.syncs[i].length; j < len; ++j){
				var r = this.ref.syncs[i][j][0];
				var c = this.ref.syncs[i][j][1];
				new_marbles[r+1][c] = this.marbles[r][c];
				moved = true;
			}
		}else{
			for(var j = 0, len = this.ref.syncs[i].length; j < len; ++j){
				var r = this.ref.syncs[i][j][0], c = this.ref.syncs[i][j][1];
				if(this.marbles[r][c] != null){
					new_marbles[r][c] = ((new_marbles[r][c]||0)+( this.marbles[r][c]))&255;
				}
			}
		}
	}
}

// input literal null move, output does not
for(var r = 0; r < this.rows; ++r){
	for(var c = 0; c < this.cols; ++c){
		if(this.marbles[r][c] != null){
			var type = this.ref.cells[r][c] && this.ref.cells[r][c].type;
			if(!type || type == CTypes.INPUT || type == CTypes.LITERAL){
				new_marbles[r+1][c] = ((new_marbles[r+1][c]||0)+(this.marbles[r][c]))&255;
				moved = true;
			}else if(type == CTypes.OUTPUT){
				new_marbles[r][c] = ((new_marbles[r][c]||0)+(this.marbles[r][c]))&255;
			}
		}
	}
}	

// shift portal
for(var i = 0; i < 36; ++i){
	if(this.ref.portals[i].length){
		var p = this.ref.portals[i];
		if(p.length == 1){
			var r = p[0][0], c = p[0][1];
			if(this.marbles[r][c] != null){
				new_marbles[r+1][c] = ((new_marbles[r+1][c]||0)+( this.marbles[r][c]))&255;
				moved = true;
			}
		}else{
			var q = [];
			for(var j = 0, l = p.length; j < l; ++j){
				if(this.marbles[p[j][0]][p[j][1]] != null){
					// generate output portal - any other portal except the input
					var toWhere = Math.floor(Math.random() * (l-1));
					if(toWhere >= j) ++toWhere;
					var r = p[j][0], c = p[j][1];
					q[toWhere] = ((q[toWhere]||0)+(this.marbles[r][c]))&255;
					moved = true;
				}
			}
			for(var j = 0, l = p.length; j < l; ++j){
				if(q[j] != null){
					var r = p[j][0] + 1, c = p[j][1];
					new_marbles[r][c] = q[j];
				}
			}
		}
	}
}

// check stdout
if(new_marbles[new_marbles.length-1].length){
	var r = this.rows;
	for(var i = 0, l = new_marbles[r].length; i < l; ++i){
		if(new_marbles[r][i] != null){
			putch(this, new_marbles[r][i]);
			moved = true;
		}
	}
}
new_marbles.splice(this.rows);

if(!moved){
	this.tickNum = -1;
	return moved;
}
this.marbles = new_marbles;
// check terminator
for(var i = 0, len = this.ref.terminators.length; i < len; ++i){
	var r = this.ref.terminators[i][0], c = this.ref.terminators[i][1];
	if(new_marbles[r][c] != null){
		this.tickNum = -1;
		return moved;
	}
}
// check output
if(this.outc){
	var allOuts = true;
	for(var i = 0; i < 38; ++i){
		var o = this.ref.outputs[i];
		if(o.length){
			var occupied = false;
			for(var j = 0, len = o.length; j < len; ++j){
				if(new_marbles[o[j][0]][o[j][1]] != null){
					occupied = true;
					break;
				}
			}
			if(!occupied){
				allOuts = false; break;
			}
		}
	}
	if(allOuts){
		this.tickNum = -1;
		return moved;
	}
}
++this.tickNum;
return moved;
};
Board.prototype.out = function(){
if(this.type != 2) throw "Calling Board.out without Board.runCopy";
if(this.tickNum != -1) throw "Copied board hasn't run to completion yet";
if(this.ref.type == 1) return this.outp;
var outp = {};
for(var i = 0; i < 38; ++i){
	if(this.ref.outputs[i].length){
		outp[i] = 0;
		var o = this.ref.outputs[i], isFilled = false;
		for(var j = 0, len = o.length; j < len; ++j){
			var r = o[j][0], c = o[j][1];
			isFilled = isFilled || this.marbles[r][c] != null;
			outp[i] = ((outp[i])+( this.marbles[r][c] || 0))&255;
		}
		if(!isFilled)
			outp[i] = null;
	}
}
return outp;
};

function DrawBuffer(canvas){
this.canvas = canvas;
this.ctx = this.canvas.getContext('2d');
this.ctx.fillStyle = 'rgb(0,0,0)';
this.ctx.fillRect(0, 0, 256, 256);
}

DrawBuffer.prototype.clear = function(){
this.ctx.fillStyle = 'rgb(0,0,0)';
this.ctx.fillRect(0, 0, 256, 256);
};
DrawBuffer.prototype.set = function(x, y, r, g, b){
this.ctx.fillStyle = 'rgb(' + r + ',' + g + ',' + b + ')';
this.ctx.fillRect(x, y, 1, 1);
};
DrawBuffer.prototype.get = function(x, y){
return this.ctx.getImageData(x, y, 1, 1).data.splice(0, 3);
};
// spaces: is an empty cell denoted with whitespace?
function parseMblSrc(src,spaces){
var lines = (':MB\n'+src).split('\n');
var sb = []; // start lines of new mb subboards
for(var i = lines.length; i-- > 0;){
	// strip mid-line spaces if possible
	if(!spaces){
		lines[i] = lines[i].trim();
		lines[i] = lines[i].replace(/\s/g,'');
	}
	// index of comment
	var o = lines[i].indexOf('#');
	// remove comments
	if(o > 0){
		lines[i] = lines[i].substr(0, o).trim();
	}else if(lines[i].length == 0 || o == 0){
		if(lines[i].indexOf('include') == 1){
			throw "#include not supported";
		}
		lines.splice(i, 1);
	}
}
for(var i = lines.length; i-- > 0;){
	// identify subboards
	if(lines[i].charAt(0) == ':'){
		sb.push(i);
	}
}
sb.sort(function(a,b){return a-b;});
var mbname = '';
for(var i = 0, len = sb.length; i < len; ++i){
	var name = lines[sb[i]].substr(1).trim();
	var board;
	board = new Board(name, false);
	var endl;
	if(i == len - 1) endl = lines.length;
	else endl = sb[i+1];
	for(var j = sb[i]+1; j < endl; ++j){
		var r = j - sb[i] - 1;
		if(lines[j].length % 2 != 0){
			throw "Error near `" + lines[j] + "`";
		}
		var cells = lines[j].match(/../g);
		for(var k = 0, clen = cells.length; k < clen; ++k){
			var val;
			if(val = cells[k].match(/^@([0-9A-Z])$/)){
				board.set(r, k, new Cell(CTypes.PORTAL, parseInt(val[1], 36)));
			}else if(val = cells[k].match(/^&([0-9A-Z])$/)){
				board.set(r, k, new Cell(CTypes.SYNCHRONISER, parseInt(val[1], 36)));
			}else if(val = cells[k].match(/^}([0-9A-Z])$/)){
				board.set(r, k, new Cell(CTypes.INPUT, parseInt(val[1], 36)));
			}else if(val = cells[k].match(/^{([0-9A-Z<>])$/)){
				var value;
				if(val[1] == '<' || val[1] == '>') value = val[1];
				else value = parseInt(val[1], 36);
				board.set(r, k, new Cell(CTypes.OUTPUT, value));
			}else if(cells[k] == '!!'){
				board.set(r, k, new Cell(CTypes.TERMINATE, 0));
			}else if(val = cells[k].match(/^[0-9A-F]{2}$/)){
				board.set(r, k, new Cell(CTypes.LITERAL, parseInt(cells[k], 16)));
			}else if(cells[k].match(/^[ \.]{2}$/)){
				board.set(r, k, null);
			}else{
				board.set(r, k, new Cell(CTypes.SUBROUTINE, cells[k]));
			}
		}
	}
	board.init();
	if(name == 'MB')
		mbname = board.name;
	name = board.name;
	bnames.push(name);
	boards[name] = board;
}
// validate and connect subr
for(var p in boards){
	boards[p].validateSubr(bnames);
}
return mbname;
}
function run(){
// normal init
stopped = false;
max_tick = document.getElementById('mb-ticks').value;
space_as_blank = document.getElementById('mb-spaces').checked;
cylindrical_board = document.getElementById('mb-cylinder').checked;
print_numbers = document.getElementById('mb-numprint').checked;
libraries = document.getElementById('mb-lib').checked;
gfx = document.getElementById('mb-gfx').checked;

src = document.getElementById('mb-src').value;
stdin = document.getElementById('mb-in').value;
boards = {};
bnames = [];
loadDefaultBoards();

if(gfx){
	// prepare draw buffers
	front_buffer = new DrawBuffer(document.getElementById('mb-fb'));
	back_buffer = new DrawBuffer(document.getElementById('mb-bb'));
}else{
	if(front_buffer){
		front_buffer.clear();
		back_buffer.clear();
		front_buffer = null;
		back_buffer = null;
	}
}
try{
	var mb = parseMblSrc(src, space_as_blank);
	var rc = boards[mb].runCopy(document.getElementById('mb-args').value.split(' '));
	var tmp = function(){
		try{
			if(stopped) throw "Board execution stopped.";
			else if(rc.tickNum != -1 && rc.tickNum < max_tick){
				rc.tick();
				document.getElementById('mb-out').value = rc.stdout;
				document.getElementById('mb-out').scrollTop = document.getElementById('mb-out').scrollHeight;
				setTimeout(tmp, 0);
			}else if(rc.tickNum == -1){
				document.getElementById('mb-out').value = rc.stdout;
				document.getElementById('mb-out').scrollTop = document.getElementById('mb-out').scrollHeight;
				document.getElementById('mb-return').value = rc.out()[0] || 0;
			}else throw "Max tick count exceeded running main board.";
		}catch(e){
			document.getElementById('mb-out').value = rc.stdout + '\n' + e;
			document.getElementById('mb-out').scrollTop = document.getElementById('mb-out').scrollHeight;
		}
	};
	setTimeout(tmp, 0);
}catch(e){
	document.getElementById('mb-out').value = (rc ? rc.stdout: '') + '\n' + e;
	document.getElementById('mb-out').scrollTop = document.getElementById('mb-out').scrollHeight;
}
}
function stop(){
stopped = true;
}
<div style="font-size: 12px;font-family: Helvetica, Arial, sans-serif;">
<div style="float: left;">
	Marbelous Source:<br />
	<textarea id="mb-src" rows="4" cols="35">48 65 6C 6C 6F 20 57 6F 72 6C 64 21</textarea><br />
	Arguments: <br />
	<input id="mb-args" size="33" /> <br />
	STDIN: <br />
	<textarea id="mb-in" rows="2" cols="35"></textarea><br />
	<span style="float: left">
		Max Ticks: <input id="mb-ticks" type="number" min="1" style="width: 60px" value="1000" />
	</span>
	<span style="float: right">
		<input type="button" onclick="run()" value="Run Code" style="" />
		<input type="button" onclick="stop()" value="Stop" style="" />
	</span>
</div>
<div style="float: left; margin-left: 15px;">
	<div style="margin-bottom: 10px">
		<input type="checkbox" id="mb-spaces" />Using spaces for blank cells<br />
		<input type="checkbox" id="mb-cylinder" />Cylindrical Board<br />
		<input type="checkbox" id="mb-numprint" />Display output as decimal numbers<br />
		<input type="checkbox" id="mb-lib" checked="true" />Include Libraries<br />
		<input type="checkbox" id="mb-gfx" />Use Draw Buffers<br />
	</div>
	<div>
		STDOUT:<br />
		<textarea id="mb-out" rows="3" cols="35"></textarea><br />
		Return Code: <input id="mb-return" />
	</div>
</div>
<div style="clear: both; text-align: center; width: 550px; padding-top: 10px;" id="mb-draw-buffers">
	<div style="float: left; width: 256px;">
		Front Buffer:
		<canvas style="height: 256px; width: 256px; background: black;" width="256" height="256" id="mb-fb"></canvas>
	</div>
	<div style="float: right; width: 256px;">
		Back Buffer:
		<canvas style="height: 256px; width: 256px; background: black;" width="256" height="256" id="mb-bb"></canvas>
	</div>
</div>
</div>

一些简单的程序可以尝试:

猫(将stdin打印到stdout)

.. @0 ..
00 ]] \/
@0 /\ ..

rot13(从标准输入中读取)

.. @0 ..
00 ]] \/
@0 /\ Rt
:Rt
}0 .. @0 .. }0 }0 }0 }0 }0
-W .. -W .. .. +D -D +D -D
-X .. >C &3 &0 &1 &2 &3 &4
>C &1 >P &4 {0 {0 {0 {0 {0
>P &2 &0 \/ .. .. .. .. ..
@0 \/ \/ .. .. .. .. .. ..

图形输出演示(将随机像素设置为随机颜色,然后翻转缓冲区)

FF FF FF FF FF 00
?? ?? ?? ?? ?? ..
{} {} {} {} {} ><

这太棒了,但似乎只有单个字母名称的面板不起作用。
演员

@overactor,现在应该修复
es1024 2014年

最后一个演示给出了一个错误Error near FF FF FF FF FF 00
Khaled.K,2017年

17

JavaScript(示例答案)

使用JavaScript解释JavaScript毫无用处,但这显示了您的解释器片段应大致表现的样子。

这是裸露的版本,旨在快速用于其他帖子:

BEGIN_CODE
// Put JavaScript code here
// it may be on multiple lines
END_CODE_BEGIN_INPUT
Put input here
it may be on multiple lines
END_INPUT
<!--Interpreter code:--><script>i=document.body.innerHTML;document.body.innerHTML=''</script><script>c=i.match(/BEGIN_CODE\n([\s\S]*)\nEND_CODE_BEGIN_INPUT/);c=c&&c.length>1?c[1]:'// Error reading code. Make sure it is on the lines between BEGIN_CODE and END_CODE_BEGIN_INPUT.';i=i.match(/END_CODE_BEGIN_INPUT\n([\s\S]*)\nEND_INPUT/);i=i&&i.length>1?i[1]:'Error reading input. Make sure it is on the lines between END_CODE_BEGIN_INPUT and END_INPUT.'</script>JavaScript:<br><tt>function(x) {</tt><br><textarea id='c'rows='4'cols='60'></textarea><br><tt>}</tt><br><br>Input string (variable x):<br><textarea id='i'rows='4'cols='60'></textarea><p><input type='button'value='Run'onclick='r()'></p>Output (return value):<br><textarea id='o'rows='4'cols='60'style='background-color:#f0f0f0;'></textarea><script>document.getElementById('c').value=c;document.getElementById('i').value=i;function r(){var r,c,i;r=c=i=null;eval('var f=function(x) {'+document.getElementById('c').value+'}');document.getElementById('o').value=String(f(document.getElementById('i').value))}</script>

将这个Stack Snippet包含在另一个问题或答案中的原始Markdown是:

<!-- begin snippet: js hide: false -->
<!-- language: lang-html -->
BEGIN_CODE
// Put JavaScript code here
// it may be on multiple lines
END_CODE_BEGIN_INPUT
Put input here
it may be on multiple lines
END_INPUT
<!--Interpreter code:--><script>i=document.body.innerHTML;document.body.innerHTML=''</script><script>c=i.match(/BEGIN_CODE\n([\s\S]*)\nEND_CODE_BEGIN_INPUT/);c=c&&c.length>1?c[1]:'// Error reading code. Make sure it is on the lines between BEGIN_CODE and END_CODE_BEGIN_INPUT.';i=i.match(/END_CODE_BEGIN_INPUT\n([\s\S]*)\nEND_INPUT/);i=i&&i.length>1?i[1]:'Error reading input. Make sure it is on the lines between END_CODE_BEGIN_INPUT and END_INPUT.'</script>JavaScript:<br><tt>function(x) {</tt><br><textarea id='c'rows='4'cols='60'></textarea><br><tt>}</tt><br><br>Input string (variable x):<br><textarea id='i'rows='4'cols='60'></textarea><p><input type='button'value='Run'onclick='r()'></p>Output (return value):<br><textarea id='o'rows='4'cols='60'style='background-color:#f0f0f0;'></textarea><script>document.getElementById('c').value=c;document.getElementById('i').value=i;function r(){var r,c,i;r=c=i=null;eval('var f=function(x) {'+document.getElementById('c').value+'}');document.getElementById('o').value=String(f(document.getElementById('i').value))}</script>
<!-- end snippet -->

要使用它只是将其复制到一个新的职位,并把你的代码行间BEGIN_CODEEND_CODE_BEGIN_INPUT,和你输入上的线条END_CODE_BEGIN_INPUTEND_INPUT。其余的工作由口译员完成。注意,不需要缩进。

我会说,BEGIN...END就将来使用的便捷性而言,这种技术是目前我们必须编写这些解释器的最佳方法,因为...

  • 呈现时,只有一个代码块(HTML就是一个),并且只有一行是“多余”的。
  • 编写代码段的用户可以确切地看到将代码和输入放置在何处。他们不必搜索适当的textarea标签。
  • 阅读帖子的用户可以轻松区分代码,而无需运行代码段。当然,在运行代码段时,代码很明显。
  • 仅当开始/结束标识符出现在代码或输入中自己的行上时,它才会中断。但这是不可能的,如果绝对必要,可以更改它们。
  • 它是自包含的。所有代码都在那里。(我知道对于较大的解释器来说这可能是不可能的,但是Stack Snippets的要点之一就是要消除与JSFiddle的无效链接,因此,自包含性很好。)
  • 它可以扩展为适合任何数量的(基于文本的)输入。

下面的代码段是非最小化的常规版本,并附带示例和更多样式。


15

Brainfuck(带有基本调试器)

我看到了脑袋的答案,然后想:你知道那太棒了吗?如果我能假装我明白脑袋代码在做什么。因此,我做了一个基本的解释器和调试器。调试器的速度很慢,但是效果很好,尽管到目前为止我还没有实现断点(在待办事项列表上)。

注意:运行代码后,Brainfuck的“虚拟机”不会自动重置。手动按复位键,否则磁带上的值将与上次程序运行结束时的值相同。

调试器在全屏模式下效果最佳。

var mycode = "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.";

function Brainfuck()    {
    this.cell_max = 255;
    this.read = function()    {return 0;}
    this.reset();
}

Brainfuck.prototype.reset = function()    {
    this.stack = [0];
    this.stackptr = 0;
    
    this.printbuffer = "";
}

Brainfuck.prototype.setCode = function(code)    {
    this.code = code;
    this.codeptr = 0;
    
    this.loop_map = {};
    var loops = [];
    for(var i = 0; i < this.code.length; i++)    {
        if(this.code[i] == '[')    {
            loops.push(i);
        }    else if(this.code[i] == ']')    {
            var last = loops.pop();
            this.loop_map[last] = i;
            this.loop_map[i] = last;
        }
    }
}

Brainfuck.prototype.done = function()    {
    return !(this.codeptr < this.code.length);
}

Brainfuck.prototype.step = function()    {
    switch(this.code.charAt(this.codeptr))    {
        case '>':
            if(++this.stackptr >= this.stack.length)    {
                this.stack.push(0);
            }
            break;
        case '<':
            if(--this.stackptr < 0)    {
                this.stackptr = this.stack.length - 1;
            }
            break;
        case '+':
            if(++this.stack[this.stackptr] > this.cell_max)    {
                this.stack[this.stackptr] = 0;
            }
            break;
        case '-':
            if(--this.stack[this.stackptr] < 0)    {
                this.stack[this.stackptr] = this.cell_max;
            }
            break;
        case '.':
            this.print();
            break;
        case ',':
            this.stack[this.stackptr] = this.read();
            break;
        case '[':
            if(this.stack[this.stackptr] == 0)    {
                this.codeptr = this.loop_map[this.codeptr];
            }
            break;
        case ']':
            if(this.stack[this.stackptr] != 0)    {
                this.codeptr = this.loop_map[this.codeptr];
            }
            break;
        default:
            
    }
    while(!this.done() && '><+-.,[]'.indexOf(this.code[++this.codeptr]) == -1);
}

Brainfuck.prototype.print = function()    {
    this.printbuffer += String.fromCharCode(this.stack[this.stackptr]);
}

// UI File

var $ = document.getElementById.bind(document);
var $_ = document.createElement.bind(document);

String.prototype.decodeHTML = function() {
    var map = {"gt":">", "lt":"<", "nbsp":""};
    return this.replace(/&(#(?:x[0-9a-f]+|\d+)|[a-z]+);?/gi, function($0, $1) {
        if ($1[0] === "#") {
            return String.fromCharCode($1[1].toLowerCase() === "x" ? parseInt($1.substr(2), 16)  : parseInt($1.substr(1), 10));
        } else {
            return map.hasOwnProperty($1) ? map[$1] : $0;
        }
    });
}

var bf = new Brainfuck();

var setup = function()    {
    bf.setCode($('code').value.decodeHTML());
    bf.read = function()    {
        var stdin = $('stdin').value;
        if(bf.read.index < stdin.length)    {
            return stdin[bf.read.index++].charCodeAt(0);
        }
        return 0;
    }
    bf.read.index = 0;
}

var run = function()    {
    setup();
    
    var stdout = $('stdout');
    var func = function()    {
        for(var i = 0; i < 500000 && !bf.done(); i++)    {
            bf.step();
        }
        stdout.innerHTML = bf.printbuffer;
        setTimeout(func, 0);
    };
    func();
}

var debug = function()    {
    $('debug-area').style.display = 'block';

    setup();
    
    var running = $('running-code');
        for(var i = 0; i < bf.code.length; i++)    {
            var s = $_('span');
            s.innerHTML = bf.code[i];
            s.style.fontFamily = "Courier";
            running.appendChild(s);
        }
        running.children[bf.codeptr].setAttribute('class', 'selected');
    
    genStackTable();
}

var genStackTable = function()    {
    var stable = $('stack-table');
    var head = stable.createTHead();
        var row = head.insertRow(0);
            row.insertCell(0).innerHTML = 'ptr';
            row.insertCell(1).innerHTML = 'addr';
            row.insertCell(2).innerHTML = 'value';
            row.insertCell(3).innerHTML = 'ASCII';
    var setStackVal = function(i, num, ascii, changed)    {
        return changed === num ? function() {
            ascii.value = String.fromCharCode(num.value);
            bf.stack[i] = num.value;
        } : function()    {
            num.value = ascii.value.charCodeAt(0);
            bf.stack[i] = num.value;
        }
    }
    for(var i in bf.stack)    {
        row = stable.insertRow();
        row.insertCell(0).innerHTML = i == bf.stackptr ? '>' : '';
        row.insertCell(1).innerHTML = i;
        var cell = row.insertCell(2);
            var numinput = $_('input');
                numinput.type = 'number';
                numinput.value = bf.stack[i];
            cell.appendChild(numinput);
        cell = row.insertCell(3);
            var asciiinput = $_('input');
                asciiinput.type = 'text';
                asciiinput.value = String.fromCharCode(bf.stack[i]);
                asciiinput.setAttribute('maxlength', 1);
            cell.appendChild(asciiinput);
        
        numinput.oninput = setStackVal(i, numinput, asciiinput, numinput);
        asciiinput.oninput = setStackVal(i, numinput, asciiinput, asciiinput);
    }
}

var debugRun = function()    {
    debugRun.pause = false;
    var func = function()    {
        step();
        if(!bf.done() && !debugRun.pause)    {
            setTimeout(func, $('step-duration').value);
        }
    }
    func();
}

var debugPause = function()    {
    debugRun.pause = true;
}

var step = function()    {
    var running = $('running-code');
    running.children[bf.codeptr].setAttribute('class', '');
    bf.step();
    $('stack-table').innerHTML = '';
    genStackTable();
    if(!bf.done())    {
        running.children[bf.codeptr].setAttribute('class', 'selected');
    }
    $('stdout').innerHTML = bf.printbuffer;
}

var reset = function()    {
    bf.reset();
    bf.read.index = 0;
    $('stdout').innerHTML = '';
    $('running-code').innerHTML = '';
    $('stack-table').innerHTML = '';
    $('debug-area').style.display = 'none';
}

var setStackSize = function()    {
    var sel = $('stackSize');
    bf.cell_max = parseInt(sel.options[sel.selectedIndex].value);
}

$('code').value = mycode;
input    {
    box-sizing:border-box;
    -moz-box-sizing:border-box;
}
div.textarea {
    -moz-appearance: textfield-multiline;
    -webkit-appearance: textarea;
    border: 1px solid gray;
    font: -webkit-small-control;
    font-family:Courier;
    height: 28px;
    overflow: auto;
    padding: 2px;
    resize: both;
    width: 200px;
    white-space:pre;
}
#debug-area    {
    display:none;
}
span.selected    {
    background-color:red;
}
<div>
    <h1>Brainfuck</h1>

    <div>
        <div>Code</div>
        <textarea id="code"></textarea>
    </div>
    <div>
        <div>Stdin</div>
        <textarea id="stdin"></textarea>
    </div>
    <div>
        <div>Stdout</div>
        <div id="stdout" class="textarea"></div>
    </div>
    <div>
        <span>Cell Size</span>
        <select id="stackSize" onchange="setStackSize()">
            <option value="255">8-bit</option>
            <option value="65535">16-bit</option>
            <option value="4294967296">32-bit</option>
        </select>
    </div>
    <div>
        <button id="run" onclick="run()">Run</button>
        <button id="debug" onclick="debug()">Debug</button>
        <button id="reset" onclick="reset()">Reset</button>
    </div>
    <div id="debug-area">
        <h2>Debug</h2>
        <div>
            <button onclick="debugRun()">Run</button>
            <button onclick="debugPause()">Pause</button>
            <span>Time between instructions</span>
            <input id="step-duration" type="number" value=5 />
			<br/>
            <button onclick="step()">Step</button>
        </div>
        <div>
            <h3>Executing Code</h3>
            <div id="running-code"></div>
        </div>
        <div id="stack">
            <h3>Stack</h3>
            <table id="stack-table"></table>
        </div>
    </div>
</div>

去做:

  • 添加断点
  • 将范围检查添加到堆栈更改
  • 进度指示器
  • 使它看起来更好
  • 添加倒带进行调试
  • 将stdin更改为某种流?
  • 高尔夫代码

用法:

要在网站上的某个片段中使用,请更改该片段的第一行,以将您的代码包含为“ mycode”。

要在没有调试器的情况下运行代码,只需按运行。在更新stdout之前,它将缓冲一半的指令输出。

要调试,请先按调试按钮。步进按钮将转到下一条指令。运行按钮将运行整个程序,但一次只运行一个步骤,每一步都会更新堆栈表(这就是为什么它这么慢)。指令之间的时间由带有该名称的输入框确定。暂停按钮可暂停执行代码。再次按运行可以恢复它。

堆栈上的所有值都可以实时更改为所需的任何值。不过要小心,它不会检查是否更改了单元格中的整数值,因此,如果您以8位堆栈大小放入300,它并不会真正在乎,并且在下一次递增之前不会对其进行校正。


这很整齐。我喜欢调试器!
IngoBürk,2014年

14

杰普特

Japt(是Ja vascri pt的缩写)是我在2015年9月设计的一种语言。由于懒惰忙碌,我直到11月初才创建翻译。您可以在此处找到官方的口译员和文档

注意:如果您有任何问题,建议或错误报告,请在Japt聊天室中发布它们。

有趣的事实:这最初是主要的口译员(实际上是唯一的口译员)。如果查看修订历史记录,则可以看到此信息。


Japt有Github吗?
Downgoat

@Vɪʜᴀɴ还没有,但是我会在接下来的24小时内设置一个。
ETHproductions 2015年

2
@Vɪʜᴀɴ我应该说24分钟。;)在这里。自述文件即将发布。
ETHproductions 2015年

1
您应该使用rawgit.com而不是raw.githubusercontent.com-Chrome不喜欢MIME类型的东西。
Mama Fun Roll

1
@nicael Us q1,我相信您可以使用。
ETHproductions 2015年

13

FRACTRAN

我最喜欢的神秘语言的半优化口译员!不幸的是,本周我没有太多时间,因此其他功能将在以后发布。

var ITERS_PER_SEC = 100000
var TIMEOUT_MILLISECS = 5000

var ERROR_INPUT = "Invalid input"
var ERROR_PARSE = "Parse error: "
var ERROR_TIMEOUT = "Timeout"
var ERROR_INTERRUPT = "Interrupted by user"

var running, instructions, registers, timeout, start_time, iterations;

function clear_output() {
  document.getElementById("output").value = ""
  document.getElementById("stderr").innerHTML = ""
}

function stop() {
  running = false
  document.getElementById("run").disabled = false
  document.getElementById("stop").disabled = true
  document.getElementById("clear").disabled = false
}

function interrupt() {
  error(ERROR_INTERRUPT)
}

function error(msg) {
  document.getElementById("stderr").innerHTML = msg
  stop()
}

function factorise(n) {
  var factorisation = {}
  var divisor = 2
  
  while (n > 1) {
    if (n % divisor == 0) {
      var power = 0
      
      while (n % divisor == 0) {
        n /= divisor
        power += 1
      }
      
      if (power != 0) {
        factorisation[divisor] = power
      }
    }
    
    divisor += 1
  }
  
  return factorisation
}

function fact_accumulate(fact1, fact2) {
  for (var reg in fact2) {
    if (reg in fact1) {
      fact1[reg] += fact2[reg]
    } else {
      fact1[reg] = fact2[reg]
    }
  } 
  
  return fact1
}

function exp_to_fact(expression) {
  expression = expression.trim().split(/\s*\*\s*/)
  var factorisation = {}
  
  for (var i = 0; i < expression.length; ++i) {
    var term = expression[i].trim().split(/\s*\^\s*/)
    
    if (term.length > 2) {
      throw "error"
    }
    
    term[0] = parseInt(term[0])
    
    if (isNaN(term[0])) {
      throw "error"
    }
    
    if (term.length == 2) {
      term[1] = parseInt(term[1])
      
      if (isNaN(term[1])) {
        throw "error"
      }
    }    
    
    if (term[0] <= 1) {
      continue 
    }
    
    var fact_term = factorise(term[0])
    
    if (term.length == 2) {
      for (var reg in fact_term) {
        fact_term[reg] *= term[1] 
      }
    }
    
    factorisation = fact_accumulate(factorisation, fact_term)
  }
  
  return factorisation
}

function to_instruction(n, d) {
  instruction = []
  divisor = 2
  
  while (n > 1 || d > 1) {
    if (n % divisor == 0 || d % divisor == 0) {
       reg_offset = 0
       
       while (n % divisor == 0) {
         reg_offset += 1
         n /= divisor
       }
      
       while (d % divisor == 0) {
         reg_offset -= 1
         d /= divisor
       }
      
      if (reg_offset != 0) {
        instruction.push(Array(divisor, reg_offset))
      }
    }
    
    divisor += 1
  }
  
  return instruction
}

function run() {
  clear_output()
  
  document.getElementById("run").disabled = true
  document.getElementById("stop").disabled = false
  document.getElementById("clear").disabled = true
  
  timeout = document.getElementById("timeout").checked

  var code = document.getElementById("code").value
  var input = document.getElementById("input").value
  
  instructions = []
  
  code = code.trim().split(/[\s,]+/)
  
  for (i = 0; i < code.length; ++i){
    fraction = code[i]
    
    split_fraction = fraction.split("/")
    
    if (split_fraction.length != 2){
      error(ERROR_PARSE + fraction)
      return
    }
    
    numerator = parseInt(split_fraction[0])
    denominator = parseInt(split_fraction[1])
    
    if (isNaN(numerator) || isNaN(denominator)){
      error(ERROR_PARSE + fraction)
      return
    }
    
    instructions.push(to_instruction(numerator, denominator))
  }
  
  try {
    registers = exp_to_fact(input)
  } catch (err) {
    error(ERROR_INPUT)
    return
  }
  
  running = true
  iterations = 0
  start_time = Date.now()
  
  fractran_iter(1)
}

function regs_to_string(regs) {
  reg_list = Object.keys(regs)
  reg_list.sort(function(a,b){ return a - b })
  
  out_str = []
  
  for (var i = 0; i < reg_list.length; ++i) {
    if (regs[reg_list[i]] != 0) {
      out_str.push(reg_list[i] + "^" + regs[reg_list[i]])
    }
  }
  
  out_str = out_str.join(" * ")  
  
  if (out_str == "") {
    out_str = "1"
  }
  
  return out_str
}

function fractran_iter(niters) {
  if (!running) {
    stop()
    return
  }
  
  var iter_start_time = Date.now()
  
  for (var i = 0; i < niters; ++i)
  {
    program_complete = true
    
    for (var instr_ptr = 0; instr_ptr < instructions.length; ++instr_ptr){
      instruction = instructions[instr_ptr]
      perform_instr = true
    
      for (var j = 0; j < instruction.length; ++j)
      {
        var reg = instruction[j][0]
        var offset = instruction[j][1]
        
        if (registers[reg] == undefined) {
          registers[reg] = 0
        }
        
        if (offset < 0 && registers[reg] < -offset) {
          perform_instr = false
          break
        }         
      }
      
      if (perform_instr) {
        for (var j = 0; j < instruction.length; ++j)
        {
           var reg = instruction[j][0]
           var offset = instruction[j][1]
           
           registers[reg] += offset
        }
        
        program_complete = false
        break
      }
    }
    
    if (program_complete) {
      document.getElementById("output").value += regs_to_string(registers)
      stop()
      return
    }
    
    iterations++
    
    if (timeout && Date.now() - start_time > TIMEOUT_MILLISECS) {
      error(ERROR_TIMEOUT)
      return
    }
  }
  
  setTimeout(function() { fractran_iter(ITERS_PER_SEC * (Date.now() - iter_start_time)/1000) }, 0)
}
<div style="font-size:12px;font-family:Verdana, Geneva, sans-serif;">
  <div style="float:left; width:50%;">
    Code:
    <br>
    <textarea id="code" rows="4" style="overflow:scroll;overflow-x:hidden;width:90%;">37789/221 905293/11063 1961/533 2279/481 57293/16211 2279/611 53/559 1961/403 53/299 13/53 1/13 6557/262727 6059/284321 67/4307 67/4661 6059/3599 59/83 1/59 14279/871933 131/9701 102037079/8633 14017/673819 7729/10057 128886839/8989 13493/757301 7729/11303 89/131 1/89 31133/2603 542249/19043 2483/22879 561731/20413 2483/23701 581213/20687 2483/24523 587707/21509 2483/24797 137/191 1/137 6215941/579 6730777/965 7232447/1351 7947497/2123 193/227 31373/193 23533/37327 5401639/458 229/233 21449/229 55973/24823 55973/25787 6705901/52961 7145447/55973 251/269 24119/251 72217/27913 283/73903 281/283 293/281 293/28997 293/271 9320827/58307 9831643/75301 293/313 28213/293 103459/32651 347/104807 347/88631 337/347 349/337 349/33919 349/317 12566447/68753 13307053/107143 349/367 33197/349 135199/38419 389/137497 389/119113 389/100729 383/389 397/383 397/39911 397/373 1203/140141 2005/142523 2807/123467 4411/104411 802/94883 397/401 193/397 1227/47477 2045/47959 2863/50851 4499/53743 241/409 1/241 1/239
</textarea>
    <br>Input:
    <br>
    <textarea id="input" rows="2" style="overflow:scroll;overflow-x:hidden;width:90%;">2^47 * 193</textarea>
    <p>
      Timeout:
      <input id="timeout" type="checkbox" checked="true"></input>
    </p>
  </div>
  <div style="float:left; width:50%;">
    Output:
    <br>
    <textarea id="output" rows="6" style="overflow:scroll;width:90%;"></textarea>
    <p>
      <input id="run" type="button" value="Run" onclick="run()"></input>
      <input id="stop" type="button" value="Stop" onclick="interrupt()" disabled="true"></input>
      <input id="clear" type="button" value="Clear" onclick="clear_output()"></input>
      &nbsp;
      <span id="stderr" style="color:red"></span>
    </p>
  </div>
</div>

上面的示例程序是我的四个平方在一起的程序 -它接受输入2 n * 193,并输出3 a * 5 b * 7 c * 11 d,使得a 2 + b 2 + c 2 + d 2 = n。

去做

  • 摘要生成器
  • 也允许分数的因式分解表示法
  • 不太可怕的输入验证
  • 整理代码更好
  • 允许在特殊条件下打印寄存器(例如,仅寄存器) 2,对于Conway的prime生成器,设置)。当前,只有终止的程序才能产生任何输出。
  • 性能优化?

用法

  • 代码中的分数被除以 [\s,]+分数用分隔,允许您用换行符,空格或逗号(或制表符,或者上面的任意组合,如果您很疯狂)分隔分数。
  • 输入可以采用分解符号,例如 2^5 * 3^7 * 11
  • 片段生成器稍后会出现

1
需要更多的分号。ASI是魔鬼的作品。
nyuszika7h 2014年

10

序幕

如果人们可以在他们的浏览器中玩弄Prelude,也许我可以提高它的采用率,所以这里有一个JavaScript解释器。它基于Sp3000的漂亮的Brainfuck解释器,其杀手级功能是可中断执行和可选的超时。

var ITERS_PER_SEC = 100000;
var TIMEOUT_MILLISECS = 5000;
var ERROR_LOOP_MISMATCH = "Mismatched parentheses";
var ERROR_LOOP_MULTI = "Multiple parentheses in the same column";
var ERROR_TIMEOUT = "Timeout";
var ERROR_INTERRUPT = "Interrupted by user";

var code, input, timeout, num_input, num_output, loop_stack, loop_map;
var running, start_time, code_ptr, width, input_ptr, voices, iterations;

function clear_output() {
    document.getElementById("output").value = "";
    document.getElementById("stderr").innerHTML = "";
}

function stop() {
    running = false;
    document.getElementById("run").disabled = false;
    document.getElementById("stop").disabled = true;
    document.getElementById("clear").disabled = false;
    document.getElementById("num_input").disabled = false;
    document.getElementById("num_output").disabled = false;
    document.getElementById("timeout").disabled = false;
}

function interrupt() {
    error(ERROR_INTERRUPT);
}

function error(msg) {
    document.getElementById("stderr").innerHTML = msg;
    stop();
}

function run() {
    clear_output();

    document.getElementById("run").disabled = true;
    document.getElementById("stop").disabled = false;
    document.getElementById("clear").disabled = true;
    document.getElementById("num_input").disabled = false;
    document.getElementById("num_output").disabled = false;
    document.getElementById("timeout").disabled = false;

    code = document.getElementById("code").value;
    input = document.getElementById("input").value;
    num_input = document.getElementById("num_input").checked;
    num_output = document.getElementById("num_output").checked;
    timeout = document.getElementById("timeout").checked;

    loop_stack = [];
    loop_map = {};

    if (num_input) {
        input = input.match(/-?\d+/g).map(function (n) {
            return +n;
        });
    } else {
        input = input.split("").map(function (s) {
            return s.charCodeAt(0);
        });
    }

    code = code.split("\n");
    width = 0;
    for (var i = 0; i < code.length; ++i)
        if (code[i].length > width) 
            width = code[i].length;
    console.log(code);
    console.log(width);
    for (var i = 0; i < width; ++i) {
        var hasParens = false;
        for (var j = 0; j < code.length; ++j) {
            var char = code[j][i];
            if (char == "(" || char == ")") {
                if (hasParens) {
                    error(ERROR_LOOP_MULTI);
                    return;
                }

                hasParens = true;
                if (char == "(") loop_stack.push({
                    x: i,
                    y: j
                });
                else {
                    if (loop_stack.length == 0) {
                        error(ERROR_LOOP_MISMATCH);
                        return;
                    } else {
                        var last_parens = loop_stack.pop();
                        loop_map[last_parens.x] = {
                            x: i,
                            y: last_parens.y
                        };
                        loop_map[i] = last_parens;
                    }
                }
            }
        }
        console.log(i);
        console.log(loop_stack);
    }

    if (loop_stack.length > 0) {
        error(ERROR_LOOP_MISMATCH);
        return;
    }

    console.log(loop_map);

    running = true;
    start_time = Date.now();
    code_ptr = 0;
    input_ptr = 0;
    voices = code.map(function () {
        return [];
    });
    iterations = 0;

    prelude_iter(1);
}

function prelude_iter(niters) {
    if (code_ptr >= width || !running) {
        stop();
        return;
    }

    console.log(voices);
    var iter_start_time = Date.now();

    for (var i = 0; i < niters; ++i) {
        var previousTops = voices.map(function(v) { return v[v.length-1] || 0; });
        
        for (var j = 0; j < code.length; ++j) {
            switch (code[j][code_ptr]) {
                case "0":
                case "1":
                case "2":
                case "3":
                case "4":
                case "5":
                case "6":
                case "7":
                case "8":
                case "9":
                    voices[j].push(+code[j][code_ptr]);
                    break;
                    
                case "+":
                    var x = voices[j].pop() || 0;
                    var y = voices[j].pop() || 0;
                    voices[j].push(x + y);
                    break;

                case "-":
                    var x = voices[j].pop() || 0;
                    var y = voices[j].pop() || 0;
                    voices[j].push(y - x);
                    break;

                case "v":
                    voices[j].push(previousTops[j == code.length-1 ? 0 : j + 1]);
                    break;
                    
                case "^":
                    voices[j].push(previousTops[j == 0 ? code.length-1 : j - 1]);
                    break;
                    
                case "#":
                    voices[j].pop();
                    break;

                case "!":
                    var value = voices[j].pop() || 0; 
                    document.getElementById("output").value += num_output ? value+"\n" : String.fromCharCode(value);
                    break;

                case "?":
                    if (input_ptr >= input.length) {
                        voices[j].push(0);
                    } else {
                        voices[j].push(input[input_ptr]);
                        ++input_ptr;
                    }
                    break;
            }
        }

        if (loop_map[code_ptr])
        {
            var other = loop_map[code_ptr];
            code_ptr = previousTops[other.y] ? Math.min(code_ptr, other.x) : Math.max(code_ptr, other.x);
        }

        ++code_ptr;
        ++iterations;

        if (timeout && Date.now() - start_time > TIMEOUT_MILLISECS) {
            error(ERROR_TIMEOUT);
            return;
        }
    }

    setTimeout(function () {
        prelude_iter(ITERS_PER_SEC * (Date.now() - iter_start_time) / 1000)
    }, 0);
}
<div style="font-size:12px;font-family:Verdana, Geneva, sans-serif;">Code:
    <br>
    <textarea id="code" rows="4" style="overflow:scroll;overflow-x:hidden;width:90%;">1(  #^    #^^   (#^+!
6 9+ 05+5+^#^+! #^ ^+!
   (((1- )#^##)^^+   )7-!)1433545514232323344949145353495314235494040404232323013334492349532333344953493435343584233475234501333449530423232349495323232323495323230494)</textarea>
    <br>Input:
    <br>
    <textarea id="input" rows="2" style="overflow:scroll;overflow-x:hidden;width:90%;">5a-13 11 2e3</textarea>
    <p>Numeric Input:
        <input id="num_input" type="checkbox">&nbsp; Numeric Output:
        <input id="num_output" type="checkbox">&nbsp; Timeout:
        <input id="timeout" type="checkbox" checked="checked">&nbsp;
        <br>
        <br>
        <input id="run" type="button" value="Run" onclick="run()">
        <input id="stop" type="button" value="Stop" onclick="interrupt()" disabled="disabled">
        <input id="clear" type="button" value="Clear" onclick="clear_output()">&nbsp; <span id="stderr" style="color:red"></span>

    </p>Output:
    <br>
    <textarea id="output" rows="6" style="overflow:scroll;width:90%;"></textarea>
</div>

另外,这是一个JSFiddle

解释器中预装了我的奎因,但通过搜索我的前奏答案,您可以轻松找到更多示例程序。

Prelude的所有功能均已实现。唯一的缺点是由于JavaScript的数字类型而缺乏任意精度算法。我可能会在某个时候解决这个问题,因为我只需要支持任意整数的加法和减法。

输入和输出通常是各个字符的字节值,但是解释器带有两个标志来代替读取和/或写入(带符号)数字。

去做

  • 添加缩小版本以进行复制。
  • 支持任意精度算术。
  • 添加代码段生成器。

9

死鱼

function run() {
  var code = document.getElementById("code").value
  var unicode = document.getElementById("unicode").checked
  var n = 0
  var output = ""

  for (var i = 0; i < code.length; ++i) {
    switch (code[i]) {
      case "i":
        n++
        break
      case "d":
        n--
        break
      case "s":
        n *= n
        break
      case "o":
        if (unicode) {
          output += String.fromCharCode(n)
        } else {
          output += n
          output += "\n"
        }
        break
      default:
        output += "\n"
    }
    
    if (n == -1 || n == 256) {
      n = 0
    }
  }

  document.getElementById('output').value = output
}
<div style="font-size:12px;font-family:Verdana, Geneva, sans-serif;">
  <div style="float:left; width:50%;">
    Code:<br>
    <textarea id="code" rows="8" style="overflow:scroll;overflow-x:hidden;width:90%;" wrap="hard">iiisdsiiiiiiiioiiiiiiiiiiiiiiiiiiiiiiiiioiiiiiiiiiiiiiiiooiiiiiiiiiodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddoddddddddddddddddddddddddsiioiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiioiiiiiiiiioiioddddddddddddoddddodddoiiiiiiiiiiiiiiiiiiiiiiiiodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddodddddddddddddddddddddsdddddodddddodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddodddddddddddddddddddddddsiiiiiiiioiiiiiiiiiiiiiiiiiiiiiioiiiiiiofdddddddddddddddddddddddddddddddddddddddddddddoiiiiiiiiiiiiiiiiiiiiiiiiioiiiiiiiiiiiiiiiooiiiiiiiiiodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddoddddddddddddddddddddddddsiioiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiioiiiiiiiiioiioddddddddddddoddddodddoiiiiiiiiiiiiiiiiiiiiiiiiodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddodddddddddddddddddddddsdddddodddddodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddodddddddddddddddddddddddsiiiiiiiioiiiiiiiiiiiiiiiiiiiiiioiiiiiiofdddddddddddddddddddddddddddddddddddddddddddddoiiiiiiiiiiiiiiiiiiiiiiiiioiiiiiiiiiiiiiiiooiiiiiiiiiodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddoddddddddddddddddddddddddsiioiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiioiiiiiiiiioiioddddddddddddoddddodddoiiiiiiiiiiiiiiiiiiiiiiiiodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddoddddddddddddddddddddddddsiiiioiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiioddddoiiiiiiiiiiiiiiiiioddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddoddddddddddddddddddddddddsiiiioiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiioddddoiiioiioiiioiiiiiiiiiiodddddddddddofddddddddddddddddddddddddddddddddoiiiiiiiiiiiiiiiiiiiiiiiiioiiiiiiiiiiiiiiiooiiiiiiiiiodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddoddddddddddddddddddddddddsiioiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiioiiiiiiiiioiioddddddddddddoddddodddoiiiiiiiiiiiiiiiiiiiiiiiiodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddodddddddddddddddddddddsdddddodddddodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddodddddddddddddddddddddddsiiiiiiiioiiiiiiiiiiiiiiiiiiiiiioiiiiiio</textarea>
    <p>
      <input id="unicode" type="checkbox" checked="true" />Use ASCII/Unicode mode
    </p>
  </div>
  <div style="float:left; width:50%;">
    Output:<br>
    <textarea id="output" rows="6" style="overflow:scroll;width:90%;"></textarea>
  <p>
    <input id='run' type='button' value='Run' onclick='run()' />
  </p>
  </div>
</div>

要预先初始化代码框,只需在textarea标签之间插入一些内容即可。同样,通过添加checked="true"复选框标签,默认情况下可以启用ASCII / Unicode模式(如上所示)。

请注意,Deadfish本身在其原始规格中实际上没有ASCII输出,我只是将其放入可能允许“下一个最接近的东西”的问题中。因此,如果您担心问题的规则可能不允许使用Deadfish,则可以随时查看Deadfish〜之类的具有ASCII输出的东西。

显示的示例来自“ 生日快乐”主题。


input元素没有内容(也没有结束标签)。您应该使用<label><input />Foo</label>的,而不是<input>Foo</input>
奥里奥尔

需要更多的分号。ASI是魔鬼的作品。
nyuszika7h 2014年

@ nyuszika7h对不起,我会在有空的时候更新(太习惯于Python了,我不太用JS)
Sp3000

8

死鱼

这并不完全符合要求,这是我对如何利用堆栈片段的看法。

我不喜欢这种样板,特别是需要为另一个“运行”按钮包括自定义HTML。

在我看来:

  • 放置一个<script>标签以包括一个简单的框架和实际的解释器
  • 将脚本放入CSS区域(语法高亮显示可能会令人恐惧)
  • 使用现有的“运行代码片段”按钮运行代码
  • 在代码段框架中获取输出

现在还没有决定如何更好地指定输入。

iiisdsiiiiiiiioiiiiiiiiiiiiiiiiiiiiiiiiioiiiiiiiiiiiiiiiooiiiiiiiiiodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddoddddddddddddddddddddddddsiioiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiioiiiiiiiiioiioddddddddddddoddddodddoiiiiiiiiiiiiiiiiiiiiiiiiodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddodddddddddddddddddddddsdddddodddddodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddodddddddddddddddddddddddsiiiiiiiioiiiiiiiiiiiiiiiiiiiiiioiiiiiio
dddddddddddddddddddddddddddddddddddddddddddddoiiiiiiiiiiiiiiiiiiiiiiiiioiiiiiiiiiiiiiiiooiiiiiiiiiodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddoddddddddddddddddddddddddsiioiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiioiiiiiiiiioiioddddddddddddoddddodddoiiiiiiiiiiiiiiiiiiiiiiiiodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddodddddddddddddddddddddsdddddodddddodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddodddddddddddddddddddddddsiiiiiiiioiiiiiiiiiiiiiiiiiiiiiioiiiiiio
dddddddddddddddddddddddddddddddddddddddddddddoiiiiiiiiiiiiiiiiiiiiiiiiioiiiiiiiiiiiiiiiooiiiiiiiiiodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddoddddddddddddddddddddddddsiioiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiioiiiiiiiiioiioddddddddddddoddddodddoiiiiiiiiiiiiiiiiiiiiiiiiodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddoddddddddddddddddddddddddsiiiioiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiioddddoiiiiiiiiiiiiiiiiioddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddoddddddddddddddddddddddddsiiiioiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiioddddoiiioiioiiioiiiiiiiiiiodddddddddddo
ddddddddddddddddddddddddddddddddoiiiiiiiiiiiiiiiiiiiiiiiiioiiiiiiiiiiiiiiiooiiiiiiiiiodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddoddddddddddddddddddddddddsiioiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiioiiiiiiiiioiioddddddddddddoddddodddoiiiiiiiiiiiiiiiiiiiiiiiiodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddodddddddddddddddddddddsdddddodddddodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddodddddddddddddddddddddddsiiiiiiiioiiiiiiiiiiiiiiiiiiiiiioiiiiiio
<script src="https://bitbucket.org/manatwork/stackcode/raw/681610d0a6face9df62e82c50ec887d66d692e0f/interpret.js#Deadfish"></script>


我猜应该也应该有一种轻松地包含输入的方法
SztupY

当然,@ SztupY。现在,我还不确定是否在CSS片段或HTML片段中也包含输入内容。请勿为此使用JavaScript,因为浏览器会在之前执行它,并且某些代码可能会在JavaScript中导致无限循环。可悲的是,没有其他方法可以将其他片段复制到堆栈片段服务器中。:(
manatwork

我认为CSS中的代码很好,并且输入可以放在<script>标记之后(也许在一个小容器中,例如<pre>,这样脚本可以访问它。希望pre也不会对输入造成太大影响)。
2014年

一个<pre>(或通常任何标记)将出现显示和语法突出显示为绿色。由于JavaScript仍然能够提取输入部分,因此我不会包括额外的标记。使用CSS片段与使用HTML片段之间的主要区别在于CSS没有出现在文档中,而HTML却出现在文档中。仍未决定。
manatwork 2014年

3
另外,我是否缺少某些内容,或者您​​的代码段现在无法正常工作?
卡尔文的爱好2014年

8

JavaScript ES7

这使您可以在任何浏览器中运行ES6 / 7代码。

我确信可以100%使用的浏览器是:IE 11,Chrome 46和Safari 7。

这确实支持数组推导([for(a in b) c]

function color(n,e){return~[null,void 0].indexOf(n)?'<div class="out '+e+'"><span style="color: #808080">'+String(n)+"</span></div>":"string"==typeof n?'<div class="out '+e+'"><span class="string">"'+n+'"</span></div>':"number"==typeof n?'<div class="out '+e+'"><span class="number">'+n+"</span></div>":"function"==typeof n?'<div class="out '+e+'"><span style="color: #808080">function</span></div>':"undefined"!=typeof n.length?'<div class="out '+e+'">'+JSON.stringify(n).replace(/(["'])((?:(?=\\*)\\.|.)*?)\1/g,'<span class="string">$1$2$1</span>').replace(/(\d+)/g,'<span class="number">$1</span>')+"</div>":'<div class="out '+e+'">'+JSON.stringify(n,null,2).replace(/(["'])((?:(?=\\*)\\.|.)*?)\1/g,'<span class="string">$1$2$1</span>').replace(/(\d+)/g,'<span class="number">$1</span>').replace(/\n/g,"<br>")+"</div>"}document.getElementById("run").onclick=function(){document.getElementById("Console").innerHTML="";var res="",err=!1;try{res=eval(babel.transform(document.getElementById("code").value).code.split("\n").slice(1).join("\n"))}catch(er){res=er.toString(),err=!0}err?document.getElementById("Console").innerHTML+='<div class="out error"><span>'+res+"</span></div>":document.getElementById("Console").innerHTML+=color(res,"implicit")},function(n,e){window.console={log:function(){n.log.apply(n,arguments),document.getElementById("Console").innerHTML+=color(arguments.length-1?Array.prototype.slice.call(arguments):arguments[0],"")},warn:function(){n.warn.apply(n,arguments),document.getElementById("Console").innerHTML+=color(arguments.length-1?Array.prototype.slice.call(arguments):arguments[0],"warn")},error:function(){n.error.apply(n,arguments),document.getElementById("Console").innerHTML+=color(arguments.length-1?Array.prototype.slice.call(arguments):arguments[0],"error")}},window.alert=console.log}(window.console,window.alert);
@import url(http://fonts.googleapis.com/css?family=Open+Sans:400,300,700);@import url(https://fonts.googleapis.com/css?family=Inconsolata);*{font-family:'Open Sans',Arial,sans-serif;font-size:.9rem}.pre,code,pre,pre *{font-family:Inconsolata,monospace}:not(b,bold,strong){font-weight:300}h1,h2,h3,h4,h5,h6{font-weight:600}h1{font-size:1.4rem}h2{font-size:1.38rem}h3{font-size:1.2rem}h4{font-size:1.15rem}h5{font-size:1.11rem}h6{font-size:1.05rem}b,i,p,span{color:#404040}p{line-height:1.3rem}a{color:#605346;text-decoration:none}a:active{color:#523415}a:visited{color:#74513E}hr{border:none;border-top:1px solid #e0d7c1}blockquote{background:#EDE3D3;border-radius:0 .5rem .5rem 0;padding:.5rem .5rem .5rem 4em;margin:.5rem .2rem}code{background:#EBE7DD;border:1px solid #e0d7c1;padding:.3rem .4rem;border-radius:.2rem}.pre,pre{background:rgba(221,221,221,.3);border-radius:.3rem;padding:.5rem;font-size:1.2rem}ol{counter-reset:ol;list-style:none}li{line-height:1.5rem}ol li:before{content:counter(ol) ". ";counter-increment:ol;font-weight:700}table{background:#f2e8d9;border-radius:.75rem;border-collapse:collapse;table-layout:fixed}table.full>tbody{width:100%}thead>tr:first-child>td{border:none;border-bottom:2px solid #e0cbab!important;font-weight:700;z-index:1;text-align:center}td:not(:first-child){border-left:1px solid #eadbc3;z-index:-1}td:only-child{background:#e6dac7;text-align:center;font-weight:600;padding:.25rem;column-span:all}td{padding:.75rem}tr.sub{background:#EDE3D3}input,textarea{border-radius:.5rem;padding:.25rem;border:none;outline:0;transition:box-shadow .3s ease;background-color:#fff!important;margin:.5rem 0}input:focus,textarea:focus{box-shadow:0 0 5px rgba(81,203,238,1)}textarea{resize:vertical}button,input[type=button]{padding:.3rem .5rem;border-radius:.5rem;background-color:#faebd7;cursor:pointer;border:none;border-bottom:1.5px solid #deb887;outline:0}button:active,input[type=button]:active{border:none;border-top:1.5px solid #deb887;color:#000}
#Console{background-color:#fff;border-radius:.5rem;padding:0;height:auto!important;overflow:scroll;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;min-height:1rem}.out,.out *{font-size:1.1rem;font-family:Inconsoleta,monospace}.out *{padding-left:.3rem}.out{border-top:1px solid #F0F0F0;border-bottom:1px solid #F0F0F0;padding:.7rem 1rem}.string,.string *{color:#C41927!important}.number{color:#1D00CF}.out.implicit:before{content:"<\00B7 ";color:#888;letter-spacing:-2px}.out.error:before,.out.warn:before{display:inline-block;background-size:1.1rem;content:"";width:1.1rem;height:1.1rem}.out.warn:before{background-image:url(http://vihan.org/p/esfiddle/icons/Warn.png)}.out.error:before{background-image:url(http://vihan.org/p/esfiddle/icons/Error.png)}.out.warn,.out.warn *{color:#AA5909;background-color:}.out.implicit,.out.log{border-color:#F0F0F0;background-color:#FFF}.out.error{border-color:#FFD7D8;background-color:#FFEBEB}
body,html{width:100%;height:100%;margin:0}body{background:#F4F3F1;display:-ms-flex;display:-webkit-flex;display:flex;align-items:center;justify-content:center}body>div{background:#EAE8E4;border-radius:.4rem;margin:2rem;padding:1rem;overflow:auto;width:50%;max-height:80%;overflow-x:hidden}label{display:block}.full{width:calc(100% - 1rem);display:inline-block;height:7rem}#input{width:100%}.out.warn  { border-color: #FFEFCC; background-color: #FFFAE1; }
<div id="demo"><h1>Code</h1><textarea id="code" class="full pre" placeholder="Code here">f=n=>n+1;
console.log( f(3) );
console.warn( `Foo` )
console.error( `Bar` );
alert( `Output` );
[ f(1), f(2) ]</textarea><button id="run">Run</button><button onclick="prompt('Link:','http://vihan.org/p/esfiddle?code='+encodeURIComponent(document.getElementById('code').value))">Permalink</button></div><div><h1>Console Output</h1><pre id="Console" class="full"></pre></div><script src="http://babeljs.io/scripts/babel.js"></script>

对于永久链接,请使用ESFiddle


我正在考虑这个答案,因为当我尝试运行它时,Chrome会发出警报 ERR_NAME_NOT_RESOLVED ,然后使该标签崩溃!
2015年

1
Chrome版本57输出ReferenceError: babel is not defined
Khaled.K,2017年

6

失眠

规范关于指令8和9的语义不明确,因此未实现。

随时对其进行改进,以支持指令0到7。还有一些不完整的代码以慢动作方式运行该程序,但是它有很多错误,因此我从界面上将其删除。

function toAsciiValue(t) {
    var i = 0;
    var a = "";
    for (i = 0; i < t.length; i++) {
        a += t.charCodeAt(i);
    }
    return a;
}

// http://stackoverflow.com/questions/1219860/html-encoding-in-javascript-jquery
function escapeHTML(str)
{
    var div = document.createElement('div');
    var text = document.createTextNode(str);
    div.appendChild(text);
    return div.innerHTML;
}

var interpreter = (function () {
    var group;
    var ip;
    var gp;
    var bp;
    var code;
    var halted;
    var tid;
    var output = "";
    
    function flipbit() {
        group[gp] = group[gp] ^ (1 << bp);
    };
    
    var commands = {
        0: function () { 
            flipbit();
            bp = (bp - 1) & 0x7;
        },
        1: function () {
            flipbit();
            bp = (bp + 1) & 0x7;
        },
        2: flipbit,
        3: function () { gp++; bp = 7; },
        4: function () { gp--; bp = 7; },
        5: function () { output += ~~group[gp]; },
        6: function () { output += String.fromCharCode(~~group[gp]); },
        7: function () {}
    };
    
    var nonprint = {
        "\0": "NUL",
        "\x01": "SOH",
        "\x02": "STX",
        "\x03": "ETX",
        "\x04": "EOT",
        "\x05": "ENQ",
        "\x06": "ACK",
        "\x07": "BEL",
        "\x08": "BS",
        "\x09": "TAB",
        "\x0a": "LF",
        "\x0b": "VT",
        "\x0c": "FF",
        "\x0d": "CR",
        "\x0e": "SO",
        "\x0f": "SI",
        "\x10": "DLE",
        "\x11": "DC1",
        "\x12": "DC2",
        "\x13": "DC3",
        "\x14": "DC4",
        "\x15": "NAK",
        "\x16": "SYN",
        "\x17": "ETB",
        "\x18": "CAN",
        "\x19": "EM",
        "\x1a": "SUB",
        "\x1b": "ESC",
        "\x1c": "FS",
        "\x1d": "GS",
        "\x1e": "RS",
        "\x1f": "US",
        "\x7f": "DEL",
        "\x80": "PAD",
        "\x81": "HOP",
        "\x82": "BPH",
        "\x83": "NBH",
        "\x84": "IND",
        "\x85": "NEL",
        "\x86": "SSA",
        "\x87": "ESA",
        "\x88": "HTS",
        "\x89": "HTJ",
        "\x8a": "LTS",
        "\x8b": "PLD",
        "\x8c": "PLU",
        "\x8d": "RI",
        "\x8e": "SS2",
        "\x8f": "SS3",
        "\x90": "DCS",
        "\x91": "PU1",
        "\x92": "PU2",
        "\x93": "STS",
        "\x94": "CCH",
        "\x95": "MW",
        "\x96": "SPA",
        "\x97": "EPA",
        "\x98": "SOS",
        "\x99": "SGCI",
        "\x9a": "SCI",
        "\x9b": "CSI",
        "\x9c": "ST",
        "\x9d": "OSC",
        "\x9e": "PM",
        "\x9f": "APC"
    }
    
    function exec(opcode) {
        var instruction = commands[opcode];
        if (typeof instruction === "function") {
            instruction();
        } else {
            halted = true;
            alert("Unsupported operation: " + opcode + ". Execution halted.");
        }
    }
    
    return {
        init: function (src) {
            group = [];
            ip = 0;
            gp = 0;
            bp = 7;
            halted = false;
            code = toAsciiValue(src);
            output = "";
            tid = void 0;
            
            this.update();
        },
        output: function () {
            return output;
        },
        update: function () {
            var dispCode = code + " ";
            document.getElementById('execcode').innerHTML = 
                dispCode.substring(0, ip) + "<span class='highlight'>" + dispCode[ip] + "</span>" + dispCode.substring(ip + 1);
            
            document.getElementById('output').innerHTML = 
                escapeHTML(output).replace(/[\x00-\x1f\x7f-\x9f]/g, function ($0) { 
                    return "<span class='non-printable'>" + nonprint[$0] + "</span>"; 
                });
                
            document.getElementById('gp').textContent = gp;
            document.getElementById('bp').textContent = bp;
            
            var dispByte = ("0000000" + (~~group[gp]).toString(2)).substr(-8);
            document.getElementById('currgr').innerHTML =
                dispByte.substring(0, 7 - bp) + "<span class='highlight'>" + dispByte[7 - bp] + "</span>" + dispByte.substring(8 - bp);
        },
        step: function () {
            if (ip >= code.length) {
                alert("The program has terminated. Reload the program to run from the beginning.");
                return;
            }
            
            if (halted) {
                alert("Execution has been halted and cannot be recovered");
                return;
            }
            
            exec(code[ip]);
            if (!halted) {
                ip++;
                this.update();
            }
        },
        slow: function () {
            // TODO: Complete this feature. May need to modify run and step.
            if (typeof tid == "number") {
                clearTimeout(tid);
                tid = void 0;
            } else {
                (function auto() {
                    if (!halted && ip < code.length) {
                        interpreter.step();
                        
                        if (!halted) {
                            tid = setTimeout(auto, 200);
                        }
                    }
                })();
            }
        },
        run: function () {
            if (ip >= code.length) {
                alert("The program has terminated. Reload the program to run from the beginning.");
                return;
            }
            
            if (halted) {
                alert("Execution has been halted and cannot be recovered");
                return;
            }
            
            while (ip < code.length) {
                exec(code[ip]);
                
                if (!halted) {
                    ip++;
                } else {
                    // Break out of the loop and update the UI
                    break;
                }
            }
            
            this.update();
        }
    }
})();

document.addEventListener('DOMContentLoaded', function () {

    var $ascii = document.getElementById("ascii");
    var $workspace = document.getElementById("workspace");
    
    $ascii.textContent = toAsciiValue($workspace.value);
    
    $workspace.onkeypress = $workspace.onkeyup = function () {
        $ascii.textContent = toAsciiValue(this.value);
    }
    
    interpreter.init('');
    
    document.getElementById("load").onclick = function () {
        interpreter.init($workspace.value);
    }
    
    document.getElementById("run").onclick = function () {
        interpreter.run();
    }
    
    document.getElementById("step").onclick = function () {
        interpreter.step();
    }
    
});
body {
    font-family: 'Franklin Gothic Book', 'Trebuchet MS', 'Arial', sans-serif;
}

.code {
    font-family: monospace;
    word-wrap: break-word;
    white-space: pre-wrap;
}

pre.code, textarea.code {
    width: 100%;
}

table th {
    padding: 0 10px;
}

table .code {
    width: 100px;
    text-align: center;
}

td {
    border: 1px solid;
}

.highlight {
    background: pink;
}

.non-printable {
    background: black;
    color: white;
    border-radius: 4px;
    margin: 0 1px;
    padding: 0 1px;
}

.box {
    border: 2px groove;
    height: 6em;
}
<h1>Insomnia<sup><a href="http://esolangs.org/wiki/Insomnia">[?]</a></sup></h1>

Currently only supports commands 0-7. Semantics of command 8 and 9 (WHILE loop) is not clearly understood, so it is not implemented.<br>
This interpreter is greatly inspired by <a href="http://www.quirkster.com/iano/js/">Ian's Befunge-93 interpreter</a>.

<h3>Source code</h3>
<textarea cols="80" rows="5" id="workspace" class="code"></textarea>

<h3>ASCII sequence</h3>
<pre id="ascii" class="code"></pre>
<input type="button" id="load" value="Load program" />

<hr>

<h3>Execution</h3>
<pre id="execcode" class="code"></pre>

<input type="button" id="run" value="Run" />
<input type="button" id="step" value="Step" />
<br>

<h3>Debug</h3>
<table>
  <tr>
    <th>Group Pointer</th>
    <td class="code" id="gp"></td>
  </tr>
  <tr>
    <th>Bit Pointer</th>
    <td class="code" id="bp"></td>
  </tr>
  <tr>
    <th>Current group</th>
    <td class="code" id="currgr"></td>
  </tr>
</table>

<h3>Output</h3>
<pre id="output" class="code box"></pre>


5

HQ9 +

由于Calvin's Hobbies甚至以HQ9 +为例,所以我决定为其编写一个快速实现。好吧,部分实现,因为我决定省去累加器(您可能为此而讨厌我)。

假设翻译是部分打高尔夫球的。我不知道为什么要打高尔夫球,但事实并非如此。

(function () {
    var bottles = (function beerMe(amount) {
        return "012, 01.\n3, 412.5".replace(/\d/g, function (c) {
            return [amount || "No more", " bottles of beer", " on the wall", amount ? "Take one down, pass it around" : "Go to the store and buy some more", ((amount || 100) - 1) || "no more", amount ? "\n\n" : ""][c];
        }) + (amount ? beerMe(amount - 1) : "");
    })(99);

    document.getElementById('run').onclick = function () {
        var source = document.getElementById('source').value,
            output = '';
        source.split('').forEach(function (command) {
            var index = ['H', 'Q', '9'].indexOf(command);
            (index + 1 && (output += ['Hello, world!', source, bottles][index], !0)) || alert('illegal command: ' + command);
        });

        document.getElementById('stdout').value = output;
    };
})();
#source, #stdout {
    display: block;
    width: 100%;
}

#stdout {
    white-space: pre;
    height: 50px;
}

#stdout, #run {
    margin-top: 3px;
}
<div>
    <input type="text" id="source" placeholder="Enter your HQ9+ program here" />
    <textarea id="stdout" wrap="off" readonly></textarea>
    <input type="button" id="run" value="Execute HQ9+" />
</div>


1
您在9指令上的命令是否向后退……说去商店,每次都买些,但最后一次
假名

1
@ pseudonym117糟糕。固定。一种具有三个(有用)命令的语言,我仍然设法打印出错误的输出。:)
IngoBürk2014年

5
错误:“墙上有1瓶啤酒。”
kennytm

5
您的翻译器不支持增加累加器!
jimmy23013 2014年

7
我的意思是,您至少应该忽略+命令,而不要抛出错误。
jimmy23013 2014年

5

光束

我真的认为这种语言应该有一个解释器,对于像我这样的非程序员来说,看起来很简单。我希望我做对了。它似乎适用于我尝试过的示例。这个问题的很多答案都抄袭了很多代码。如果我在代码中犯了任何主要的罪过,我会向真正的程序员致歉:)

我在Esolangs页面中放入了“ Reverse STDIN”示例。

var ITERS_PER_SEC = 100000;
var TIMEOUT_SECS = 50;
var ERROR_INTERRUPT = "Interrupted by user";
var ERROR_TIMEOUT = "Maximum iterations exceeded";
var ERROR_LOSTINSPACE = "Beam is lost in space";

var code, store, beam, ip_x, ip_y, dir, input_ptr, mem;
var input, timeout, width, iterations, running;

function clear_output() {
document.getElementById("output").value = "";
document.getElementById("stderr").innerHTML = "";
}

function stop() {
running = false;
document.getElementById("run").disabled = false;
document.getElementById("stop").disabled = true;
document.getElementById("clear").disabled = false;
document.getElementById("timeout").disabled = false;
}

function interrupt() {
error(ERROR_INTERRUPT);
}

function error(msg) {
document.getElementById("stderr").innerHTML = msg;
stop();
}

function run() {
clear_output();
document.getElementById("run").disabled = true;
document.getElementById("stop").disabled = false;
document.getElementById("clear").disabled = true;
document.getElementById("input").disabled = false;
document.getElementById("timeout").disabled = false;

code = document.getElementById("code").value;
input = document.getElementById("input").value;
timeout = document.getElementById("timeout").checked;
	
code = code.split("\n");
width = 0;
for (var i = 0; i < code.length; ++i){
	if (code[i].length > width){ 
		width = code[i].length;
	}
}
console.log(code);
console.log(width);
	
running = true;
dir = 0;
ip_x = 0;
ip_y = 0;
input_ptr = 0;
beam = 0;
store = 0;
mem = [];
	
input = input.split("").map(function (s) {
		return s.charCodeAt(0);
	});
	
iterations = 0;

beam_iter();
}

function beam_iter() {
while (running) {
	var inst; 
	try {
		inst = code[ip_y][ip_x];
	}
	catch(err) {
		inst = "";
	}
	switch (inst) {
		case ">":
			dir = 0;
			break;
		case "<":
			dir = 1;
			break;
		case "^":
			dir = 2;
			break;
		case "v":
			dir = 3;
			break;
		case "+":
			if(++beam > 255)
				beam = 0;
			break;
		case "-":
			if(--beam < 0)
				beam = 255;
			break;
		case "@":
			document.getElementById("output").value += String.fromCharCode(beam);
			break;
		case ":":
			document.getElementById("output").value += beam;
			break;
		case "/":
			dir ^= 2;
			break;
		case "\\":
			dir ^= 3;
			break;
		case "!":
			if (beam != 0) {
				dir ^= 1;
			}
			break;
		case "?":
			if (beam == 0) {
				dir ^= 1;
			}
			break;
		case "_":
			switch (dir) {
			case 2:
				dir = 3;
				break;
			case 3:
				dir = 2;
				break;
			}
			break;
		case "|":
			switch (dir) {
			case 0:
				dir = 1;
				break;
			case 1:
				dir = 0;
				break;
			}
			break;
		case "H":
			stop();
			break;
		case "S":
			store = beam;
			break;
		case "L":
			beam = store;
			break;
		case "s":
			mem[beam] = store;
			break;
		case "g":
			store = mem[beam];
			break;
		case "P":
			mem[store] = beam;
			break;
		case "p":
			beam = mem[store];
			break;
		case "u":
			if (beam != store) {
				dir = 2;
			}
			break;
		case "n":
			if (beam != store) {
				dir = 3;
			}
			break;
		case "`":
			--store;
			break;
		case "'":
			++store;
			break;
		case ")":
			if (store != 0) {
				dir = 1;
			}
			break;
		case "(":
			if (store != 0) {
				dir = 0;
			}
			break;
		case "r":
			if (input_ptr >= input.length) {
				beam = 0;
			} else {
				beam = input[input_ptr];
				++input_ptr;
			}
			break;
		}
	// Move instruction pointer
	switch (dir) {
		case 0:
			ip_x++;
			break;
		case 1:
			ip_x--;
			break;
		case 2:
			ip_y--;
			break;
		case 3:
			ip_y++;
			break;
	}
	if (running && (ip_x < 0 || ip_y < 0 || ip_x >= width || ip_y >= code.length)) {
		error(ERROR_LOSTINSPACE);
	}
	++iterations;
	if (iterations > ITERS_PER_SEC * TIMEOUT_SECS) {
		error(ERROR_TIMEOUT);
	}
}
}
<div style="font-size:12px;font-family:Verdana, Geneva, sans-serif;">Code:
    <br>
    <textarea id="code" rows="8" style="overflow:scroll;overflow-x:hidden;width:90%;">v
(>``v
!  H(p`@`p)H
P   H
' 
r 
' 
P 
! 
>^
	</textarea>
    <br>Input:
    <br>
    <textarea id="input" rows="2" style="overflow:scroll;overflow-x:hidden;width:90%;">Reverse this string</textarea>
    <p>Timeout:
        <input id="timeout" type="checkbox" checked="checked">&nbsp;
        <br>
        <br>
        <input id="run" type="button" value="Run" onclick="run()">
        <input id="stop" type="button" value="Stop" onclick="interrupt()" disabled="disabled">
        <input id="clear" type="button" value="Clear" onclick="clear_output()">&nbsp; <span id="stderr" style="color:red"></span>
    </p>Output:
    <br>
    <textarea id="output" rows="6" style="overflow:scroll;width:90%;"></textarea>
</div>


我以前怎么没注意到?这很棒!我喜欢看到从“未实现的深渊”中解救出来的语言。;)
ETHproductions

1
希望您不要介意,但我对JS部分做了一些清理。
ETHproductions 2015年

@ETHproductions我一点都没有问题。如上所述,我不是一个真正的程序员,但我确实想要一些东西来测试您在回答“ Hello World”挑战时提出的一些想法。
MickyT

超时功能目前似乎不起作用。如果我插入一个无限循环,它将使我的浏览器崩溃,并且我还记得有一次尝试打印从!到的每个ASCII字符~,并且即使在关闭超时的情况下,它也总是在前三个字符之后超时。我将对此进行一些研究。
ETHproductions 2015年

@ETHproductions附带了我大量复制以开始的片段。必须承认,我在超时方面遇到了一些问题。
MickyT 2015年

5

K5

这是我基于JavaScript的K实现oK的一个简单的瘦包装,并引用了github存储库。我已经建立了一个更好的基于浏览器的前端。除了IO之外,ok还实现了K5的很大一部分,并且在过去已用于解决堆栈溢出方面的许多问题。该代码本身并不复杂,但是包含注释,解析器,所有原始操作,解释器和prettyprinter在内的1000行以下。

* {
  font-size:12px;
  font-family:Verdana, Geneva, sans-serif;
}
textarea {
  overflow:scroll;
  overflow-x:hidden;
  width:90%;
  font-family:Consolas, Courier New, monospace;
  font-size:10p
}
<script src="http://johnearnest.github.io/ok/oK.js"></script>
<script>
    function runk() {
    	var code = document.getElementById("code").value;
    	var env  = baseEnv();
    	var ret  = run(parse(code), baseEnv());
    	document.getElementById("output").value = format(ret);
    }
</script>

Code:
<br>
<textarea id="code" rows="8">2*!10</textarea>
<p><input id="run" type="button" value="Run" onclick="runk()"></p>
Output:
<br>
<textarea id="output" rows="6" style="overflow:scroll;width:90%;"></textarea>


5

空格

function interpret() {
    function num(sign, bits) { return sign * parseInt(bits.replace("S", "0").replace("T", "1"), 2); }
    var prog = document.getElementById("prog").value, esc = document.getElementById("esc").checked;
    if (!esc)
        prog = prog.replace(" ", "S").replace("\t", "T").replace("\n", "L");
    prog = prog.replace(/[^STL]/g, "");
    var stack = [], labels = {}, heap = [], input = document.getElementById("in").value, output = document.getElementById("out"), inPos = 0, callStack = [];
    for (var i = 0; i < prog.length; i++) {
        switch (prog[i]) {
            case "S":
                switch (prog[++i]) {
                    case "S":
                        stack.push(num(prog[++i] == "S" ? 1 : -1, prog.slice(++i, prog.indexOf("L", i) - 1)));
                        i = prog.indexOf("L", i);
                        break;
                    case "T":
                        switch (prog[++i]) {
                            case "S":
                                stack.push(stack[stack.length - num(prog[++i] == "S" ? 1 : -1, prog.slice(++i, prog.indexOf("L", i) - 1))]);
                                i = prog.indexOf("L", i);
                                break;
                            case "L":
                                var num = num(prog[++i] == "S" ? 1 : -1, prog.slice(++i, prog.indexOf("L", i) - 1));
                                i = prog.indexOf("L", i);
                                stack.splice(stack.length - num - 1, num);
                                break;
                        }
                        break;
                    case "L":
                        switch (prog[++i]) {
                            case "S":
                                stack.push(stack[stack.length - 1]);
                                break;
                            case "T":
                                stack.push.apply(stack, stack.slice(-2).reverse());
                                break;
                            case "L":
                                stack.pop();
                                break;
                        }
                        break;
                }
                break;
            case "T":
                switch (prog[++i]) {
                    case "S":
                        switch (prog[++i]) {
                            case "S":
                                switch (prog[++i]) {
                                    case "S":
                                        stack.push(stack.pop() + stack.pop());
                                        break;
                                    case "T":
                                        stack.push(stack.pop() - stack.pop());
                                        break;
                                    case "L":
                                        stack.push(stack.pop() * stack.pop());
                                        break;
                                }
                                break;
                            case "T":
                                switch (prog[++i]) {
                                    case "S":
                                        stack.push(Math.floor(stack.pop() / stack.pop()));
                                        break;
                                    case "T":
                                        stack.push(stack.pop() % stack.pop());
                                        break;
                                }
                                break;
                        }
                        break;
                }
                break;
            case "T":
                switch (prog[++i]) {
                    case "S":
                        heap[stack[stack.length - 2]] = stack.pop();
                        stack.pop();
                        break;
                    case "T":
                        stack.push(heap[stack.pop()]);
                        break;
                }
                break;
            case "L":
                switch (prog[++i]) {
                    case "S":
                        switch (prog[++i]) {
                            case "S":
                                output.value += String.fromCharCode(stack.pop());
                                break;
                            case "T":
                                output.value += stack.pop();
                                break;
                        }
                        break;
                    case "T":
                        switch (prog[++i]) {
                            case "S":
                                heap[stack.pop()] = input.charCodeAt(inPos++);
                                break;
                            case "T":
                                heap[stack.pop()] = parseInt(input.slice(inPos, input.indexOf("\n", inPos) - 1));
                                break;
                        }
                }
                break;
            case "L":
                switch (prog[++i]) {
                    case "S":
                        switch (prog[++i]) {
                            case "S":
                                labels[num(1, prog.slice(++i, prog.indexOf("L", i) - 1))] = i = prog.indexOf("L", i);
                                break;
                            case "T":
                                callStack.push(prog.indexOf("L", ++i));
                                i = labels[num(1, prog.slice(i, prog.indexOf("L", i) - 1))];
                                break;
                            case "L":
                                i = labels[num(1, prog.slice(++i, prog.indexOf("L", i) - 1))];
                                break;
                        }
                        break;
                    case "T":
                        switch (prog[++i]) {
                            case "S":
                                if (stack.pop() == 0)
                                    i = labels[num(1, prog.slice(++i, prog.indexOf("L", i) - 1))];