HSP on JS で HSP のコードから変換された JavaScript ソース
HSP on JS では AX を独自の命令列に変換した後、JavaScript のソースコードに変換しています。
どんなソースコードに変換されるのかを紹介します。
Hello, world!
mes "Hello, world!"
for(;;) { switch(this.pc) { case 0: // mes "Hello, world!" var func = BuiltinFuncs[9][15]; if(!func) throw new HSPError(ErrorCode.UNSUPPORTED_FUNCTION); var args = []; args[0] = literals[0]; func.apply(this, args); this.pc ++; case 1: // stop var func = BuiltinFuncs[15][17]; if(!func) throw new HSPError(ErrorCode.UNSUPPORTED_FUNCTION); var args = []; func.apply(this, args); this.pc ++; case 2: // goto this.pc = 1; continue; this.pc ++; } }
コメントを追加した以外は生成されたそのままです。JavaScript には goto がありませんから、switch - case でやっているという感じです。
literals[0] は文字列 "Hello, world!" が格納された StrValue オブジェクトです。値は全て Value という不変のオブジェクトで管理していて、演算するたびに新しくオブジェクトを作っています。
定数畳み込みと条件式が定数のif
if 1 > 2 { mes "then part" } else { mes "else part" }
for(;;) { switch(this.pc) { case 0: // if 1 > 2 (goto に最適化済み) this.pc = 3; continue; this.pc ++; case 1: // mes "then part" var func = BuiltinFuncs[9][15]; if(!func) throw new HSPError(ErrorCode.UNSUPPORTED_FUNCTION); var args = []; args[0] = literals[0]; func.apply(this, args); this.pc ++; case 2: // else this.pc = 4; continue; this.pc ++; case 3: // mes "else part" var func = BuiltinFuncs[9][15]; if(!func) throw new HSPError(ErrorCode.UNSUPPORTED_FUNCTION); var args = []; args[0] = literals[1]; func.apply(this, args); this.pc ++; case 4: // stop var func = BuiltinFuncs[15][17]; if(!func) throw new HSPError(ErrorCode.UNSUPPORTED_FUNCTION); var args = []; func.apply(this, args); this.pc ++; case 5: // goto this.pc = 4; continue; this.pc ++; } }
定数同士の演算はコンパイル時に済ませてしまいます。(1 > 2 は 0 に)
また、if 命令で条件式が定数である場合は goto に変換します。
デッドコードの削除は特にやっていません。
repeat - loop
repeat 42 mes cnt loop
for(;;) { switch(this.pc) { case 0: // repeat 42 if(this.loopStack.length >= 31) { throw new HSPError(ErrorCode.TOO_MANY_NEST); } var end = 42; // ← JS のリテラルになっている if(end < 0) end = Infinity; var begin = 0; if(end == 0) { this.pc = 4; continue; } end += begin; this.loopStack.push(new LoopData(begin, end, 1)); this.pc ++; case 1: // cnt if(this.loopStack.length == 0) { stack.push(new IntValue(0)); } else { stack.push(new IntValue(this.loopStack[this.loopStack.length - 1].cnt)); } this.pc ++; case 2: // mes ... var func = BuiltinFuncs[9][15]; if(!func) throw new HSPError(ErrorCode.UNSUPPORTED_FUNCTION); var args = []; args[0] = stack.pop(); func.apply(this, args); this.pc ++; case 3: // loop if(this.loopStack.length == 0) { throw new HSPError(ErrorCode.LOOP_WITHOUT_REPEAT); } var data = this.loopStack[this.loopStack.length - 1]; data.cnt ++; if(data.cnt < data.end) { this.pc = data.pc; continue; } this.loopStack.pop(); this.pc ++; case 4: // stop var func = BuiltinFuncs[15][17]; if(!func) throw new HSPError(ErrorCode.UNSUPPORTED_FUNCTION); var args = []; func.apply(this, args); this.pc ++; case 5: // goto this.pc = 4; continue; this.pc ++; } }
repeat する回数が直接 JS のリテラルになっています。リテラルのみの式で直接 JS のリテラルになることもあります。
演算
a = 3 b = 5 c = 4 mes a * (b + c)
for(;;) { switch(this.pc) { case 0: // a = 3 var variable = variables[0]; var rhs = literals[0]; if(variable.value.getType() != rhs.getType()) { variable.reset(rhs.getType()); } variable.value.assign(0, rhs); this.pc ++; case 1: // b = 5 var variable = variables[1]; var rhs = literals[1]; if(variable.value.getType() != rhs.getType()) { variable.reset(rhs.getType()); } variable.value.assign(0, rhs); this.pc ++; case 2: // c = 3 var variable = variables[2]; var rhs = literals[2]; if(variable.value.getType() != rhs.getType()) { variable.reset(rhs.getType()); } variable.value.assign(0, rhs); this.pc ++; case 3: // mes a * (b + c) var func = BuiltinFuncs[9][15]; if(!func) throw new HSPError(ErrorCode.UNSUPPORTED_FUNCTION); var args = []; args[0] = (variables[0].value.at(0)).mul((variables[1].value.at(0)).add(variables[2].value.at(0))); func.apply(this, args); this.pc ++; case 4: // stop var func = BuiltinFuncs[15][17]; if(!func) throw new HSPError(ErrorCode.UNSUPPORTED_FUNCTION); var args = []; func.apply(this, args); this.pc ++; case 5: // goto this.pc = 4; continue; this.pc ++; } }
a * (b + c) という式が (variables[0].value.at(0)).mul((variables[1].value.at(0)).add(variables[2].value.at(0))) という複雑な式になっています。
追記(2009-02-10T16:51:37+09:00)
「定数畳み込みと条件式が定数のif」で「デッドコードの削除は特にやっていません。」と書きましたが、最近実装しました。出力される JavaScript ソースは以下のようになります。
for(;;) { switch(this.pc) { case 0: // mes "else part" var func = BuiltinFuncs[9][15]; if(!func) throw new HSPError(ErrorCode.UNSUPPORTED_FUNCTION); var args = []; args[0] = literals[0]; func.apply(this, args); this.pc ++; case 1: // stop var func = BuiltinFuncs[15][17]; if(!func) throw new HSPError(ErrorCode.UNSUPPORTED_FUNCTION); var args = []; func.apply(this, args); this.pc ++; } }