JS_RandezvousでBrainfuckインタプリタ

サンプルとして作ってみた。ロジックはほぼJSDeferredのサンプルと同じ。

せっかくなので実行中に停止する機能とステップ実行も付けておいた。

Task.define();
$(function() {
      var brf = new Task(
          function() {
              this.jRun = $("#run").click(brf.run).text("Run");
              this.jPause = $("#pause").click(brf.pause).text("Pause");
              this.jStep = $("#step").click(brf.step).text("Step");
              this.jSource = $("#source");
              this.jSourceView = $("#source_view");
          },
          loop(
              true,
              accept(
                  "run",
                  function() {
                      this.source = $("#source").val();
                      this.pos = 0;
                      this.memory = [0];
                      this.pointer = 0;;
                      this.stack = [true];
                      this.output = [];
                      this.running = true;

                      this.jRun.click(brf.run).text("Abort");
                      this.jPause.click(brf.pause).removeAttr("disabled");
                      this.jSource.hide();
                      var sourceView = this.jSourceView.empty();
                      this.elem = $.map(
                          this.jSource.val().split(""),
                          function(c) {
                              if (c == "\n") c =  $("<br/>");
                              return $("<span/>").append(c).appendTo(sourceView);
                          });
                      this.jSourceView.show();
                  }
              ),
              loop(
                  function() {return this.pos < this.source.length},
                  function() {
                      if (this.source[this.pos] == ",") {
                          this.jStep.removeAttr("disabled");
                          this.jStep.text(",");
                      }
                  },
                  select(
                      accept(
                          "run",
                          function() {
                              return exit();
                          }),
                      accept(
                          "pause",
                          function() {
                              this.running = ! this.running;

                              if (this.running) {
                                  this.jPause.text("Pause");
                                  this.jStep.attr("disabled", true);
                              } else {
                                  this.jPause.text("Continue");
                                  this.jStep.removeAttr("disabled");
                              }
                          }),
                      when(function() {return ! this.running || this.source[this.pos] == ",";})
                          .accept("step", function() {
                              this.putchar = $("#putchar_value").val()[0];
                              this.jStep.attr("disabled", this.running);
                              this.jStep.text("step");
                          }),
                      when(function() {return this.running && this.source[this.pos] != ","})
                          .delay(0)
                  ),
                  function() {
                      if (this.elem[this.pos - 1])
                          this.elem[this.pos - 1].removeClass("em");

                      switch(this.source[this.pos]) {
                      case "+":
                          if (! this.stack[0]) break;
                          this.memory[this.pointer]++;
                          break;
                      case "-":
                          if (! this.stack[0]) break;
                          this.memory[this.pointer]--;
                          break;
                      case ">":
                          if (! this.stack[0]) break;
                          this.pointer++;
                          this.memory[this.pointer] =
                              this.memory[this.pointer] || 0;
                          break;
                      case "<":
                          if (! this.stack[0]) break;
                          this.pointer--;
                          this.memory[this.pointer] =
                              this.memory[this.pointer] || 0;
                          break;
                      case ".":
                          if (! this.stack[0]) break;
                          this.output.push(
                              String.fromCharCode(this.memory[this.pointer]));
                          break;
                      case ",":
                          if (! this.stack[0]) break;
                          this.memory[this.pointer] =
                              String.charCodeAt(this.putchar);
                          break;
                      case "[":
                          if (this.memory[this.pointer] == 0)
                              this.stack.unshift(false);
                          else this.stack.unshift(this.pos);
                          break;
                      case "]":
                          var s = this.stack.shift();
                          if (s) this.pos = s - 1;
                          break;
                      case "\n":
                          break;
                      default:
                          throw new Error("Unkown character \""
                                          + this.source[this.pos]
                                          + "\" at " + (this.pos + 1));
                      }

                      this.elem[this.pos].addClass("em");
                      var m = this.memory.concat();
		      m.splice(this.pointer, 1,
                               "*" + (this.memory[this.pointer] || 0));
                      $("#status").text(m.join(", "));
                      $("#output").text(this.output.join(""));

                      this.pos++;
                  }
              ),
              exception(
                  function(e){
                      alert("Error: " + e.message);}
              ),
              function() {
                  this.jPause.attr("disabled", true);
                  this.jPause.text("Pause");
                  this.jStep.attr("disabled", true);
                  this.jRun.click(brf.run).text("Run");
                  this.jSource.show();
                  this.jSourceView.hide();
              }
          )
      );
  });

機能が増えてる割りにはJSDeferred版よりは見通しがいいかなと思うんだけど、どうかな。