RubiMaVM ã JavaScript ã«ç§»æ¤ãã¦ã¿ã
VM ã«èå³ããããã®ã§ るびま ãèªãã ã
ããã¦ã RubiMaVM ã Javascript ã«ç§»æ¤ãã¦ã¿ãã
var RubiMaVM = {}; RubiMaVM.Instruction = function Instruction(code, opts) { this.code = code; this.opts = opts; }; RubiMaVM.Instruction.prototype.toString = function toString() { return this.code + ' <' + this.opts.join(', ') + '>'; }; RubiMaVM.Label = function Label(label) { this.label = label; this.pos = -1; this.id = ++ RubiMaVM.Label.id; }; RubiMaVM.Label.id = 0; RubiMaVM.Label.prototype.toString = function toString() { return this.label + ' <' + this.id + '@' + this.pos + '>'; }; RubiMaVM.Evaluator = function Evaluator() { this.stack = []; this.pc = 0; }; RubiMaVM.Evaluator.prototype.evaluate = function evaluate(sequence) { var insn; while((insn = sequence[this.pc])) { this.dispatch(insn); } return this.stack[0]; }; RubiMaVM.Evaluator.prototype.dispatch = function dispatch(insn) { var lhs, rhs; switch(insn.code) { case 'nop': break; case 'push': this.stack.push(insn.opts[0]); break; case 'pop': this.stack.pop(); break; case 'dup': this.stack.push(this.stack[this.stack.length-1]); break; case 'add': rhs = this.stack.pop(), lhs = this.stack.pop(); this.stack.push(lhs + rhs); break; case 'sub': rhs = this.stack.pop(), lhs = this.stack.pop(); this.stack.push(lhs - rhs); break; case 'mul': rhs = this.stack.pop(), lhs = this.stack.pop(); this.stack.push(lhs * rhs); break; case 'div': rhs = this.stack.pop(), lhs = this.stack.pop(); this.stack.push(lhs / rhs); break; case 'not': this.stack.push(!this.stack.pop()); break; case 'lt': var rhs = this.stack.pop(), lhs = this.stack.pop(); this.stack.push(lhs < rhs); break; case 'gt': rhs = this.stack.pop(), lhs = this.stack.pop(); this.stack.push(lhs > rhs); break; case 'lteq': rhs = this.stack.pop(), lhs = this.stack.pop(); this.stack.push(lhs <= rhs); break; case 'gteq': rhs = this.stack.pop(), lhs = this.stack.pop(); this.stack.push(lhs >= rhs); break; case 'goto': this.pc = insn.opts[0].pos; return; case 'ifne': if(this.stack.pop()) { this.pc = insn.opts[0].pos; return; } break; default: throw 'Unknown Opcode: ' + insn.code; } ++ this.pc; } RubiMaVM.Parser = {} RubiMaVM.Parser.parse = function parse(program) { var pc = 0; var labels = {}; var lines = program.split('\n'); var sequence = []; var label, lobj; for(var i = 0; i < lines.length; i ++) { var line = lines[i].match(/^\s*(.*?)\s*$/)[1]; var insn = []; if(/^:\w+$/.test(line)) { label = line; if(!(lobj = labels[label])) { lobj = labels[label] = new RubiMaVM.Label(label); } lobj.pos = pc; continue; } while(line.length > 0) { var m; if((m = line.match(/^:[a-z]+/))) { label = m[0]; if(!(lobj = labels[line])) { lobj = labels[line] = new RubiMaVM.Label(label); } insn.push(lobj); } else if((m = line.match(/^\s+/)) || (m = line.match(/^#.*/))) { // ignore } else if((m = line.match(/^[a-z]+/))) { insn.push(m[0]); } else if((m = line.match(/^\d+/))) { insn.push(+m[0]); } else { throw 'Parse Error: ' + line; } line = line.slice(m[0].length); } if(insn.length > 0) { ++ pc; sequence.push(new RubiMaVM.Instruction(insn[0], insn.slice(1))); } } return sequence; }; function main() { var program = [ ' #', ' push 1', ':label', ' push 1', ' add', ' dup', ' push 100000', ' lt', ' ifne :label', '' ].join('\n'); var sequence = RubiMaVM.Parser.parse(program); for(var i = 0; i < sequence.length; i ++) { var insn = sequence[i]; print((Array(4).join('0')+i).slice(-4) + '\t' + insn); } var result = new RubiMaVM.Evaluator().evaluate(sequence); print(result); } main();
å½ä»¤ã®ååï¼æååï¼ã§ switch-case ãã¦å½ä»¤ãã£ã¹ããããã¦ããã®ããæ°å¤ã§ switch-case ããããã£ã¹ããããã¼ãã«ã使ã£ãããã¦ã¿ãã
--- rubimavm-switchname.js 2008-09-07 14:27:12.000000000 +0900 +++ rubimavm-switchval.js 2008-09-07 14:35:55.000000000 +0900 @@ -6,9 +6,32 @@ }; RubiMaVM.Instruction.prototype.toString = function toString() { - return this.code + ' <' + this.opts.join(', ') + '>'; + return RubiMaVM.Instruction.CodeNames[this.code] + ' <' + this.opts.join(', ') + '>'; }; +RubiMaVM.Instruction.CodeNames = [ + 'nop', + 'push', + 'pop', + 'dup', + 'add', + 'sub', + 'mul', + 'div', + 'not', + 'lt', + 'gt', + 'lteq', + 'gteq', + 'goto', + 'ifne' +] + +RubiMaVM.Instruction.Code = {}; +RubiMaVM.Instruction.CodeNames.forEach(function(name,i){ + RubiMaVM.Instruction.Code[name] = i; +}); + RubiMaVM.Label = function Label(label) { this.label = label; this.pos = -1; @@ -37,56 +60,56 @@ RubiMaVM.Evaluator.prototype.dispatch = function dispatch(insn) { var lhs, rhs; switch(insn.code) { - case 'nop': + case 0: // nop break; - case 'push': + case 1: // push this.stack.push(insn.opts[0]); break; - case 'pop': + case 2: // pop this.stack.pop(); break; - case 'dup': + case 3: // dup this.stack.push(this.stack[this.stack.length-1]); break; - case 'add': + case 4: // add rhs = this.stack.pop(), lhs = this.stack.pop(); this.stack.push(lhs + rhs); break; - case 'sub': + case 5: // sub rhs = this.stack.pop(), lhs = this.stack.pop(); this.stack.push(lhs - rhs); break; - case 'mul': + case 6: // mul rhs = this.stack.pop(), lhs = this.stack.pop(); this.stack.push(lhs * rhs); break; - case 'div': + case 7: // div rhs = this.stack.pop(), lhs = this.stack.pop(); this.stack.push(lhs / rhs); break; - case 'not': + case 8: // not this.stack.push(!this.stack.pop()); break; - case 'lt': + case 9: // lt var rhs = this.stack.pop(), lhs = this.stack.pop(); this.stack.push(lhs < rhs); break; - case 'gt': + case 10: // gt rhs = this.stack.pop(), lhs = this.stack.pop(); this.stack.push(lhs > rhs); break; - case 'lteq': + case 11: // lteq rhs = this.stack.pop(), lhs = this.stack.pop(); this.stack.push(lhs <= rhs); break; - case 'gteq': + case 12: // gteq rhs = this.stack.pop(), lhs = this.stack.pop(); this.stack.push(lhs >= rhs); break; - case 'goto': + case 13: // goto this.pc = insn.opts[0].pos; return; - case 'ifne': + case 14: // ifne if(this.stack.pop()) { this.pc = insn.opts[0].pos; return; @@ -128,7 +151,11 @@ } else if((m = line.match(/^\s+/)) || (m = line.match(/^#.*/))) { // ignore } else if((m = line.match(/^[a-z]+/))) { - insn.push(m[0]); + var code = RubiMaVM.Instruction.Code[m[0]]; + if(code === undefined) { + throw 'Unknown Opcode: ' + m[0]; + } + insn.push(code); } else if((m = line.match(/^\d+/))) { insn.push(+m[0]); } else {
--- rubimavm-switchname.js 2008-09-07 14:27:12.000000000 +0900 +++ rubimavm-dispatchtable.js 2008-09-07 14:14:19.000000000 +0900 @@ -6,9 +6,32 @@ }; RubiMaVM.Instruction.prototype.toString = function toString() { - return this.code + ' <' + this.opts.join(', ') + '>'; + return RubiMaVM.Instruction.CodeNames[this.code] + ' <' + this.opts.join(', ') + '>'; }; +RubiMaVM.Instruction.CodeNames = [ + 'nop', + 'push', + 'pop', + 'dup', + 'add', + 'sub', + 'mul', + 'div', + 'not', + 'lt', + 'gt', + 'lteq', + 'gteq', + 'goto', + 'ifne' +] + +RubiMaVM.Instruction.Code = {}; +RubiMaVM.Instruction.CodeNames.forEach(function(name,i){ + RubiMaVM.Instruction.Code[name] = i; +}); + RubiMaVM.Label = function Label(label) { this.label = label; this.pos = -1; @@ -34,68 +57,86 @@ return this.stack[0]; }; +RubiMaVM.Evaluator.prototype.body_nop = function body_nop() { +}; + +RubiMaVM.Evaluator.prototype.body_push = function body_push(val) { + this.stack.push(val); +}; + +RubiMaVM.Evaluator.prototype.body_pop = function body_pop() { + this.stack.pop(); +}; + +RubiMaVM.Evaluator.prototype.body_dup = function body_dup() { + this.stack.push(this.stack[this.stack.length-1]); +}; + +RubiMaVM.Evaluator.prototype.body_add = function body_add() { + var rhs = this.stack.pop(), lhs = this.stack.pop(); + this.stack.push(lhs + rhs); +}; + +RubiMaVM.Evaluator.prototype.body_sub = function body_sub() { + var rhs = this.stack.pop(), lhs = this.stack.pop(); + this.stack.push(lhs - rhs); +}; + +RubiMaVM.Evaluator.prototype.body_mul = function body_mul() { + var rhs = this.stack.pop(), lhs = this.stack.pop(); + this.stack.push(lhs * rhs); +}; + +RubiMaVM.Evaluator.prototype.body_div = function body_div() { + var rhs = this.stack.pop(), lhs = this.stack.pop(); + this.stack.push(lhs / rhs); +}; + +RubiMaVM.Evaluator.prototype.body_not = function body_not() { + this.stack.push(!this.stack.pop()); +}; + +RubiMaVM.Evaluator.prototype.body_lt = function body_lt() { + var rhs = this.stack.pop(), lhs = this.stack.pop(); + this.stack.push(lhs < rhs); +}; + +RubiMaVM.Evaluator.prototype.body_gt = function body_gt() { + var rhs = this.stack.pop(), lhs = this.stack.pop(); + this.stack.push(lhs > rhs); +}; + +RubiMaVM.Evaluator.prototype.body_lteq = function body_lteq() { + var rhs = this.stack.pop(), lhs = this.stack.pop(); + this.stack.push(lhs <= rhs); +}; + +RubiMaVM.Evaluator.prototype.body_gteq = function body_gteq() { + var rhs = this.stack.pop(), lhs = this.stack.pop(); + this.stack.push(lhs >= rhs); +}; + +RubiMaVM.Evaluator.prototype.body_goto = function body_goto(label) { + this.pc = label.pos; + return true; +}; + +RubiMaVM.Evaluator.prototype.body_ifne = function body_ifne(label) { + if(this.stack.pop()) { + this.pc = label.pos; + return true; + } +}; + +RubiMaVM.Evaluator.DispatchTable = []; +RubiMaVM.Instruction.CodeNames.forEach(function(name, i){ + RubiMaVM.Evaluator.DispatchTable[i] = RubiMaVM.Evaluator.prototype['body_'+name]; +}); + RubiMaVM.Evaluator.prototype.dispatch = function dispatch(insn) { - var lhs, rhs; - switch(insn.code) { - case 'nop': - break; - case 'push': - this.stack.push(insn.opts[0]); - break; - case 'pop': - this.stack.pop(); - break; - case 'dup': - this.stack.push(this.stack[this.stack.length-1]); - break; - case 'add': - rhs = this.stack.pop(), lhs = this.stack.pop(); - this.stack.push(lhs + rhs); - break; - case 'sub': - rhs = this.stack.pop(), lhs = this.stack.pop(); - this.stack.push(lhs - rhs); - break; - case 'mul': - rhs = this.stack.pop(), lhs = this.stack.pop(); - this.stack.push(lhs * rhs); - break; - case 'div': - rhs = this.stack.pop(), lhs = this.stack.pop(); - this.stack.push(lhs / rhs); - break; - case 'not': - this.stack.push(!this.stack.pop()); - break; - case 'lt': - var rhs = this.stack.pop(), lhs = this.stack.pop(); - this.stack.push(lhs < rhs); - break; - case 'gt': - rhs = this.stack.pop(), lhs = this.stack.pop(); - this.stack.push(lhs > rhs); - break; - case 'lteq': - rhs = this.stack.pop(), lhs = this.stack.pop(); - this.stack.push(lhs <= rhs); - break; - case 'gteq': - rhs = this.stack.pop(), lhs = this.stack.pop(); - this.stack.push(lhs >= rhs); - break; - case 'goto': - this.pc = insn.opts[0].pos; - return; - case 'ifne': - if(this.stack.pop()) { - this.pc = insn.opts[0].pos; - return; - } - break; - default: - throw 'Unknown Opcode: ' + insn.code; + if(!RubiMaVM.Evaluator.DispatchTable[insn.code].apply(this, insn.opts)) { + ++ this.pc; } - ++ this.pc; } RubiMaVM.Parser = {} @@ -128,7 +169,11 @@ } else if((m = line.match(/^\s+/)) || (m = line.match(/^#.*/))) { // ignore } else if((m = line.match(/^[a-z]+/))) { - insn.push(m[0]); + var code = RubiMaVM.Instruction.Code[m[0]]; + if(code === undefined) { + throw 'Unknown Opcode: ' + m[0]; + } + insn.push(code); } else if((m = line.match(/^\d+/))) { insn.push(+m[0]); } else {
é度æ¯è¼
$ time js rubimavm-switchname.js 0000 push <1> 0001 push <1> 0002 add <> 0003 dup <> 0004 push <100000> 0005 lt <> 0006 ifne <:label <1@1>> 100000 real 0m3.409s user 0m3.100s sys 0m0.000s $ time js rubimavm-switchval.js 0000 push <1> 0001 push <1> 0002 add <> 0003 dup <> 0004 push <100000> 0005 lt <> 0006 ifne <:label <1@1>> 100000 real 0m2.790s user 0m2.564s sys 0m0.000s $ time js rubimavm-dispatchtable.js 0000 push <1> 0001 push <1> 0002 add <> 0003 dup <> 0004 push <100000> 0005 lt <> 0006 ifne <:label <1@1>> 100000 real 0m7.793s user 0m7.640s sys 0m0.000s
ãã£ã¹ããããã¼ãã« ï¼ æååã§switch-case ï¼ æ°å¤ã§switch-case