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 ++;
    }
}