読者です 読者をやめる 読者になる 読者になる

blechmusikの日記

キー・カスタマイズ・ソフトウェア "DvorakJ" の覚え書きをはじめとして様々なことを書いています。

JScriptのスクリプトをAutoHotkey_Lのスクリプトから利用する

 階乗を求める以下のようなJScriptのスクリプトを作成したとする。

var number = 20
var fact = (function (x) {
    if (x < 1) return 1;
    return x * arguments.callee(x - 1);
})(number);

 このスクリプトのソースをfact.jsと名付け、factの値をメッセージダイアログで表示するAutoHotkey_Lのスクリプトを作成すれば、次のようになる。

sc := ComObjCreate("ScriptControl")
sc.Language := "JScript"

FileRead, script, fact.js
sc.ExecuteStatement(script)
MsgBox,% sc.Eval("fact")

 それではnumberの値をAutoHotkey_Lのスクリプトで改変するにはどうすればよいだろうか。JScriptのスクリプトではnumber変数を定義せず、AutoHotkey_Lのスクリプトでnumber変数の値を決めてそれをJScriptのスクリプトに渡すには、AutoHotkey_Lのスクリプトの中にJScriptのスクリプトソースをヒアドキュメントを使って書き、変数の展開を併せて利用すればよい。

sc := ComObjCreate("ScriptControl")
sc.Language := "JScript"

number := 20

script =
(
    var fact = (function (x) {
        if (x < 1) return 1;
        return x * arguments.callee(x - 1);
    })(%number%);
)

sc.ExecuteStatement(script)
MsgBox,% sc.Eval("fact")

 numberの値を次のように変更すれば、10と20の階乗の結果を求めることができる。

sc := ComObjCreate("ScriptControl")
sc.Language := "JScript"

Loop,2
{
    number := A_Index * 10

    script =
    (
        var fact = (function (x) {
            if (x < 1) return 1;
            return x * arguments.callee(x - 1);
        })(%number%);
    )
    
    sc.ExecuteStatement(script)
    MsgBox,% sc.Eval("fact")
}

 ただしこのようにヒアドキュメントを使って変数を展開する場合は、scriptの定義を何度もしなければならない。numberの部分だけを書き換えるには、ヒアドキュメントを使うとしても変数の展開を抑制する必要があろう。
 そこで、ヒアドキュメント内の%number%をと書くことで変数の自動的な展開を抑制し、numberの値を任意の時点で書き換えてみる。

sc := ComObjCreate("ScriptControl")
sc.Language := "JScript"

number := 20

script =
(
    var fact = (function (x) {
        if (x < 1) return 1;
        return x * arguments.callee(x - 1);
    })(<number>);
)

pattern := "<(\w+?)>"
if (RegExMatch(script, pattern, $)) {
    value_of_var := %$1%
    script := RegExReplace(script, pattern, value_of_var)
}

sc.ExecuteStatement(script)
MsgBox,% sc.Eval("fact")

このスクリプトは意図したとおりに動作する。
次に、変数の値を展開する部分を関数にまとめ、script変数の定義後にnumber変数を定義してみよう。

sc := ComObjCreate("ScriptControl")
sc.Language := "JScript"

script =
(
    var fact = (function (x) {
        if (x < 1) return 1;
        return x * arguments.callee(x - 1);
    })(<number>);
)
number := 20
sc.ExecuteStatement(replace_pattern(script))
MsgBox,% sc.Eval("fact")

replace_pattern(script){
    global
    local pattern := "<(\w+?)>"
    local value_of_var

    if (RegExMatch(script, pattern, $)) {
        value_of_var := %$1%
        return RegExReplace(script, pattern, value_of_var)
    } else {
        return script
    }
}

これらの処理を踏まえると、ヒアドキュメントを使用しなくても同様のことを達成できることに気がつくだろう。
以下のJScriptスクリプトをfact-rev.jsというファイル名で保存し、FileReadコマンドを使ってそのファイルを読み取り、number変数を展開してみる。

var fact = (function (x) {
    if (x < 1) return 1;
    return x * arguments.callee(x - 1);
})(<number>);
sc := ComObjCreate("ScriptControl")
sc.Language := "JScript"

FileRead, script, fact-rev.js
number := 20
sc.ExecuteStatement(replace_pattern(script))
MsgBox,% sc.Eval("fact")

replace_pattern(script){
    global
    local pattern := "<(\w+?)>"
    local value_of_var

    if (RegExMatch(script, pattern, $)) {
        value_of_var := %$1%
        return RegExReplace(script, pattern, value_of_var)
    } else {
        return script
    }
}

正しく動作しているようだ。ここで再度Loopコマンドを使い、10と20の階乗の結果を表示してみよう。

sc := ComObjCreate("ScriptControl")
sc.Language := "JScript"

FileRead, script, fact-rev.js
loop, 2
{
    number := A_Index * 10
    sc.ExecuteStatement(replace_pattern(script))
    MsgBox,% sc.Eval("fact")
}

replace_pattern(script){
    global
    local pattern := "<(\w+?)>"
    local value_of_var

    if (RegExMatch(script, pattern, $)) {
        value_of_var := %$1%
        return RegExReplace(script, pattern, value_of_var)
    } else {
        return script
    }
}

意図した通りの結果を得られた。
最後に、COMオブジェクトの処理も関数でまとめてみようか。このように書けば、JScriptのスクリプトをAutoHotkey_Lのスクリプトから手軽に利用できるといえよう。

FileRead, script, fact-rev.js
loop,2
{
    number := A_Index * 10
    MsgBox,% get_value_from_script(scirpt, "fact")
}

get_value_from_script(scirpt, variable_name){
    global
    local sc := ComObjCreate("ScriptControl")

    sc.Language := "JScript"
    sc.ExecuteStatement(replace_pattern(script))
    return sc.Eval(variable_name)
}

replace_pattern(script){
    global
    local pattern := "<(\w+?)>"
    local value_of_var

    if (RegExMatch(script, pattern, $)) {
        value_of_var := %$1%
        return RegExReplace(script, pattern, value_of_var)
    } else {
        return script
    }
}