スクリプト言語

AutoHotkeyスクリプトは、基本的にAutoHotkey専用のカスタム言語で書かれた、プログラムが従うべき命令の集合体です。この言語は、他のいくつかのスクリプト言語と類似していますが、独自の長所と落とし穴も持っています。本書では、その言語について説明するとともに、よくある落とし穴を指摘するようにしています。

AutoHotkeyで利用される様々な概念についてのより一般的な説明については、概念と規約を参照してください。

目次

一般的な慣習

名前:変数名と関数名は大文字と小文字を区別しません(例えば、CurrentDatecurrentdateと同じです)。最大文字数や使用可能文字数などの詳細は、名前を参照してください。

型なし変数:変数には明示的に定義された型がなく、任意の型の値を任意の変数に格納することができます(定数および組み込み変数を除く)。数値は、状況に応じて自動的に文字列(テキスト)に変換されたり、その逆も可能です。

宣言は任意: 関数のページに記載がある場合を除き、変数は宣言する必要はありません。ただし、値が与えられる前に変数を読み取ろうとするとエラーとみなされます。

スペースの殆どは無視:インデント(先頭の空白)は、読みやすいコードを書くために重要ですが、プログラムでは必要とされないので、一般的には無視されます。スペースとタブは、行末と式内では通常無視されます(引用符の間を除く)。しかし、スペースは次のようなケースでは意味を持ちます:

改行の意味:改行は一般的にステートメントの区切りとして機能し、前の関数呼び出しやその他のステートメントを終了させます。(ステートメントとは、単に実行されるべき何らかの動作を表現する、言語の最小の独立した要素です。) ただし、行の継続は例外です(下記参照)。

行の継続:長い行を小さな行の集まりに分割して、読みやすさや保守性を向上させることができます。これは前処理によって実現されるものであり、言語の一部ではありません。3つの方式:

コメント

コメントとは、スクリプト内のテキストの一部で、プログラムによって無視される部分です。一般的には、説明を加えたり、コードの一部を無効にしたりするために使用されます。

スクリプトは、行頭のセミコロンでコメントすることができます。事例:

; この行全体がコメントです。

コメントは行末に追加することもできますが、その場合、セミコロンの左側には少なくとも1つのスペースまたはタブが必要です。事例:

Run "Notepad"  ; これは、関数呼び出しと同じ行にあるコメントです。

さらに、この/* および */記号は、この例のように、セクション全体をコメントアウトするために使用することができます:

/*
MsgBox "This line is commented out (disabled)."
MsgBox "Common mistake:" */ " this does not end the comment."
MsgBox "This line is commented out."
*/
MsgBox "This line is not commented out."
/* これも有効ですが、他のコードがその行を共有することはできません。*/
MsgBox "This line is not commented out."

タブ、スペースは除く。/* は行頭に表示する必要があります。 */は、行頭または行末にのみ表示することができます。*/を省略することも有効です。 この場合、ファイルの残りはコメントアウトされます。

コメントはスクリプトをファイルから読み込む際にフィルタリングされるため、パフォーマンスやメモリ使用量に影響を与えることはありません。

は、1つ以上の変数演算子関数呼び出しの組み合わせです。例えば、101+1MyVarは有効な表現です。通常、式は1つ以上の値を入力とし、1つ以上の演算を行い、結果として値を生成します。式の値を求める作業を評価といいます。例えば、1+1という式は、数字2に評価されます。

簡単な式をつなぎ合わせて、どんどん複雑な式にすることができます。例えば、Discount/100で割引率を分数に変換した場合、1 - Discount/100で残額を表す分数を計算し、Price * (1 - Discount/100)を適用してネット価格を算出します。

は、数値オブジェクト文字列です。リテラル値とは、スクリプトに物理的に書かれた値で、コードを見ればわかる値です。

文字列 / テキスト

文字列の一般的な説明については、文字列を参照してください。

ストリングまたは文字列とは、単なるテキスト値のことです。式中、リテラルテキストは、変数名や他の式と区別するために、シングルクォーテーションまたはダブルクォーテーションで囲む必要があります。これはしばしば引用リテラル文字列、あるいは単に引用文字列と呼ばれる。例えば、"これは引用符で囲まれた文字列です"そして'これはそうです'という具合です。

引用符で囲まれた文字列の中に実際の引用符を含めるには、`"または`' エスケープシーケンスを使用するか、反対のタイプの引用符でその文字を囲みます。事例:'She said, "An apple a day."'

引用符で囲まれた文字列には、`t(タブ)、`n(ラインフィード)、`r(キャリッジリターン)といった他のエスケープシーケンスを含めることができます。

変数

変数の基本的な説明と一般的な詳細については、変数を参照してください。

変数は、変数名を書くだけで式の中で使用することができます。例えば、A_ScreenWidth/2です。ただし、引用符で囲まれた文字列の中では、変数を使用することができない。その代わりに、変数やその他の値をテキストと結合させるための処理として 連結。式中の値を連結する方法は2つあります:

暗黙の連結は、自動連結とも呼ばれる。いずれの場合も、変数とドットの前にあるスペースは必須です。

また、フォーマット機能もこの目的に使用できます。事例:

MsgBox Format("You are using AutoHotkey v{1} {2}-bit.", A_AhkVersion, A_PtrSize*8)

MyVar := "Some text"のように、変数に値を代入するには、:= 代入演算子を使用します。

式中のパーセント記号は、動的な変数参照を作成するために使用されますが、これらはほとんど必要ありません。

キーワード定数

定数とは、単に変更不可能な値で、シンボリックな名前が与えられているものです。AutoHotkeyには現在、以下の定数があります:

名前説明
False0Integerブール値 false。"off"、"no "などの意味を持つこともある。
True1Integerブール値の真偽。「オン」「イエス」などの意味を持つこともある。

読み取り専用の組み込み変数とは異なり、動的参照で返すことはできません。

演算子

演算子は、+:=などの記号や記号群、またはandornotisincontainsのいずれかの単語の形をとる。1つ、2つ、3つの値を入力として受け取り、結果として値を返します。演算子の入力として使用される値または部分式をオペランドと呼ぶ。

単項演算子と二項演算子には同じ記号を持つものがあり、その場合、演算子の意味は2つの値の前、後、間のいずれに書かれるかに依存します。例えば、x-yは引き算を行い、-xxの符号を反転させる(負の値から正の値を生成し、その逆も同様)ことを意味します。

乗算(*)や除算(/)などの優先順位が等しい演算子は、演算子テーブルで特に指定がない限り、左から右の順番で評価されます。一方、加算(+)のような優先順位の低い演算子は、乗算(*)のような優先順位の高い演算子の後に評価されます。例えば、3 + 2 * 2は、3 + (2 * 2)と評価されます。この例のように、括弧を使用して優先順位を上書きすることができます:(3 + 2) * 2

関数呼び出し

機能の一般的な説明と関連する用語については、機能を参照してください。

関数は、さまざまな入力を受け取り、何らかのアクションや計算を行い、その後 返り値を返す。関数の入力は、次のように呼ばれます。 パラメータ引数などです。関数が 呼び出されるには、対象となる関数の後に、括弧で囲まれたパラメータを記述するだけでよい。例えば、GetKeyState("Shift")は、Shiftが押されている場合は1を、それ以外の場合は0を返す(評価される)。

注:関数と開いた括弧の間には、スペースを入れてはいけません。

プログラミングの初心者にとって、括弧の必要性は、最初は不可解で冗長に見えるかもしれませんが、括弧は、関数呼び出しが他の操作と組み合わされることを可能にするものなのです。例えば、GetKeyState("Shift", "P") and GetKeyState("Ctrl", "P")という式は、両方のキーが物理的に押されている場合にのみ1を返します。

関数呼び出し式は通常、リテラル関数名で始まりますが、呼び出しの対象は、関数オブジェクトを生成する任意の式にすることができます。GetKeyState("Shift")という式では、GetKeyStateは実際には変数参照ですが、通常は組み込み関数を含む読み取り専用変数を指します。

関数呼び出し文

関数の戻り値が必要なく、関数名が行頭に書かれている場合(またはelseホットキーに続くなど、ステートメントを許可する他のコンテキスト)、括弧を省略することができます。この場合、行の残りは関数のパラメータリストとして扱われます。事例:

result := MsgBox("This one requires parentheses.",, "OKCancel")
MsgBox "This one doesn't. The result was " result "."

同じ文脈でメソッドを呼び出す場合、括弧を省略することもできますが、対象となるオブジェクトがmyVar.myMethodmyVar.myProp.myMethodのように変数や直接名前の付いたプロパティの場合のみです。

関数呼び出し式と同様に、関数呼び出し文のターゲットは定義済みの関数である必要はなく、代わりに関数オブジェクトを含む変数にすることができます。

関数呼び出し文は、複数の行にまたがることができます。

関数呼び出し文には、以下の制限があります:

オプションのパラメータ

オプションのパラメータは、単に空白にすることもできますが、後続のパラメータがすべて省略されない限り、区切りカンマは必要です。例えば、Run関数は1つから4つのパラメータを受け取ることができます。以下のすべてが有効です:

Run "notepad.exe", "C:\"
Run "notepad.exe",, "Min"
Run("notepad.exe", , , &notepadPID)

関数呼び出し配列リテラルオブジェクトリテラル内で、キーワードunsetを使用すると、パラメータまたは値を明示的に省略することができます。アンセット式は、以下のいずれかの効果を持ちます:

unsetキーワードは、関数定義において、パラメータがオプションであり、デフォルト値がないことを示すために使用することもできます。関数が実行されるとき、パラメータが省略された場合、そのパラメータに対応するローカル変数にはがありません。

maybe演算子(var?)は、変数が値を持つかどうかによって、その変数を渡したり省略したりするのに使われます。例えば、Array(MyVar?)Array(IsSet(MyVar) ? MyVar : unset)と等価です。

オブジェクトの演算子

このほかにも、以下に示すように、上記の分類に当てはまらない、あるいは表現の他の部分の意味に影響を与えるような記号が表現に使用されています。これらはすべて、何らかの形でオブジェクトに関係するものです。各コンストラクトがどのような働きをするのか、詳しく説明するためには、さらに多くの概念を導入する必要があり、このセクションの範囲外です。

Alpha.Betaメンバーアクセスと呼ばれることが多い。Alphaは普通の変数で、関数呼び出しや、オブジェクトを返す他の部分式に置き換えることができる。評価されると、オブジェクトに「プロパティBetaの値を教えて」「この値をプロパティBetaに格納して」「Betaという名前のメソッドを呼び出して」という要求が送られます。つまり、Betaはオブジェクトに対して意味を持つ名前であり、ローカル変数やグローバル変数ではありません。

Alpha.Beta()は、上記の通り、メソッド呼び出しです。特定の場合、括弧を省略することができます。「関数呼び出しステートメント」を参照してください。

Alpha.Beta[Param]は、リクエストに追加のパラメータを含む特別な会員アクセスの形態です。Betaが単純な名前であるのに対し、Paramは通常の変数や部分式、あるいはカンマで区切られた部分式のリスト(関数のパラメータリストと同じ)です。変則的な呼び出しが可能です。

Alpha.%vBeta%Alpha.%vBeta%[Param]、およびAlpha.%vBeta%()もメンバーアクセスですが、vBetaは変数または副式です。これにより、スクリプトの実行中にプロパティやメソッドの名前を決定することができます。この方法でメソッドを呼び出す場合は、括弧を付ける必要があります。

Alpha[Index]は、Indexをパラメータとして、Alphaデフォルトのプロパティにアクセスします。この場合、AlphaIndexはいずれも変数であり、事実上あらゆる部分式に置き換えることができる。この構文は、通常、ArrayMapの要素を取得するために使用されます。

[A, B, C]は、初期内容A、B、C(この場合はすべて変数)を持つ配列を作成し、Aは要素1です。

{Prop1: Value1, Prop2:Value2}文字通りProp1Prop2という名前のプロパティを持つObjectを作成します。値は、後で前述のメンバーアクセス構文を使用して取得することができます。プロパティ名を式として評価する場合は、パーセント記号で囲みます。例えば:{%NameVar%: ValueVar}

MyFunc(Params*)は、可変長引数関数呼び出しです。アスタリスクは、関数のパラメータリストの最後にある閉じ括弧の直前でなければなりません。Paramsは、Arrayまたは他の列挙可能なオブジェクトを返す変数または部分式でなければなりません。Params*はどこでも使えるわけではありませんが、配列リテラル([A, B, C, ArrayToAppend*])やプロパティのパラメータリスト(Alpha.Beta[Params*]Alpha[Params*])で使用することが可能です。

表現文

すべての表現が行内で単独で使用できるわけではありません。例えば、21*2"Some text"だけで構成された行は、何の意味もなさない。式のは、それ自体で使用される式で、通常はその副作用のために使用されます。副作用のある表現のほとんどはこの方法で使えるので、一般にこの項の詳細を覚える必要はない。

ステートメントとして使用できるのは、次の種類の式です:

x := yのような代入、x += yのような複合代入、++xx--のようなインクリメント/デクリメント演算子。

既知の制限事項: x++x--については、現在、変数名と演算子の間にスペースを入れることができません。

MyFunc(Params)のような関数呼び出し。ただし、単独の関数呼び出しには、関数宣言と混同されるため、(行末や次の行で)開波括弧{を付けることはできません。

MyObj.MyMethod()のようなメソッド呼び出しです。

MyObj[Index]のように角括弧を使ったメンバーアクセスは、関数呼び出しのような副作用がある場合があります。

3項式、x ? CallIfTrue() : CallIfFalse()。ただし、式(または条件)を常に括弧で囲むという、以下のルールを利用するのが無難です。

既知の制限事項: 関数呼び出し文があいまいなため、変数名とスペースで始まる条件(他の演算子も含む)は、括弧で囲む必要があります。例えば、(x + 1) ? y : zx+1 ? y : z は、式ですが、x + 1 ? y : zは、関数呼び出し文です。

注:条件は、継続行と解釈されるため、!やその他の式演算子で始めることはできません。

始まる表現(。しかし、通常、同じ行に一致する)がなければ、その行は継続セクションの開始と解釈されます。

%varname% := 1のように、ダブルデフで始まる式。これは主に、実装の複雑さによるものです。

簡略化のため、上記のいずれかで始まる表現(後述のものは除く)も可とします。例えば、MyFunc()+1は現在許可されていますが、+1は何の効果もなく、その結果は破棄されます。将来、エラーチェックの強化により、このような表現が無効となる可能性があります。

関数呼び出し文は式文と似ていますが、技術的には純粋な式ではありません。例えば、MsgBox "Hello, world!",myGui.Showまたはx.y.z "my parameter"などです。

制御フロー文

制御フローの一般的な説明については、制御フローを参照してください。

ステートメントは、グループ化され ブロックは、C言語やJavaScriptなどのように中括弧{}で囲むことで実現できますが、通常、中括弧は行頭に表示する必要があります。制御フロー文は、ブロック全体に適用することも、1つの文だけに適用することも可能です。

制御フロー文の本体は、常に1つの文のグループです。ブロックは、制御フロー文とその本体と同様に、1つの文のグループとしてカウントされます。また、以下の関連する文は、その本体とともに互いにグループ化されている:IfElseLoop/ForUntilまたはElseTryCatchまたは/およびElseまたは/およびFinally。つまり、これらの文のグループを全体として使用する場合、必ずしも中括弧で囲む必要はありません(ただし、コーディングスタイルによっては、わかりやすくするために中括弧を必ず入れるものもあります)。

制御フロー文は、本文を持つため、必ず関連する文または文のグループが続く必要があります:IfElseLoopWhileForTryCatchFinally

以下のような制御フロー文が存在:

コントロールフローとその他の文との比較

制御フロー文は、関数呼び出し文といくつかの点で異なっています:

ループ文

ループ文にはいくつかの種類があります:

Breakは、ループを抜ける(終了する)ことで、ループのボディの次の行に効果的にジャンプすることができます。

Continueは、現在のループ反復の残りをスキップして、新しいループを開始します。

Untilは、式がtrueと評価されたときにループを終了させる。式は、各反復の後に評価されます。

ラベルは、ContinueBreakのループに「命名する」ために使うことができます。これにより、Gotoを使用しなくても、スクリプトを簡単に続行したり、任意の数の入れ子ループから抜け出したりすることができます。

組込変数A_Indexには、現在のループの繰り返し回数が格納されます。ループ本体が最初に実行されるときに1を含む。2回目には2が入り、以下同様です。内側ループが外側ループで囲まれている場合、内側ループが優先されます。A_Indexは、すべての種類のループの内部で機能しますが、ループの外部では0を含みます。

一部のループタイプでは、他の組み込み変数が現在のループアイテム(レジストリキー/値、ファイル、部分文字列、テキスト行)に関する情報を返します。これらの変数は、A_LoopFileNameやA_LoopReadLineなど、A_Loopで始まる名前を持っています。これらの値は常に、該当するタイプの最も最近に開始された(しかしまだ停止していない)ループに対応します。例えば、A_LoopFieldは、ファイルやレジストリのループ内で使用されていても、最も内側の解析ループの現在の部分文字列を返します。

t := "column 1`tcolumn 2`nvalue 1`tvalue 2"
Loop Parse t, "`n"
{
    rowtext := A_LoopField
    rownum := A_Index  ; 以下、2つ目のループで使用するために保存します。
    Loop Parse rowtext, "`t"
    {
        MsgBox rownum ":" A_Index " = " A_LoopField
    }
}

ループ変数は、ループ内から呼び出される関数など、ループ本体の外でも使用することができます。

非制御

ディレクティブ、ラベル、ダブルコロンホットキーとホットストリングタグ、および割り当てのない宣言は、スクリプトがファイルからロードされるときに処理されるので、制御フローの対象にはなりません。つまり、スクリプトが制御フロー文を実行する前に、無条件で効果を発揮するのです。同様に、#HotIf指示文は、制御フローに影響を与えることはできません。ホットキーの条件は、コード内で#HotIfディレクティブに遭遇したときではなく、押されるたびに評価されます。

スクリプトの構成

グローバルコード

スクリプトが読み込まれた後、自動実行スレッドは、スクリプトの先頭行で実行を開始し、ReturnExitAppExitなどの停止指示があるまで実行を続ける。スクリプトの物理的な端は、Exitとしても機能します。

グローバルコード(グローバルスコープ内のコード)とは、関数やクラス定義の内部にない実行可能なコードのことです。そこで参照される変数は、(適切な宣言があれば)どの関数からもアクセスできるため、グローバル変数と呼ばれる。このようなコードは、新しく起動したスレッドに適用される設定や、ホットキーやその他の機能で使用するグローバル変数の初期化などによく使用されます。

起動時(スクリプトを起動した直後)に実行されるコードは、ファイルの先頭に配置されることが多い。しかし、このようなコードは、関数やクラス定義の間(内部ではない)に、ファイル全体に配置することができます。これは、各関数やクラス定義の本体が、実行中に遭遇するたびにスキップされるからです。場合によっては、スクリプトの目的全体をグローバルコードで遂行することもあります。

関連: スクリプトの起動(自動実行のスレッド)

サブルーチン

サブルーチンsubまたはprocedure)は、要求に応じて実行できる再利用可能なコードブロックです。サブルーチンは、関数(後述)を定義することで作成されます。これらの用語は、関数がサブルーチンの唯一のタイプであるAutoHotkey v2では一般的に互換性があります。

関数

関連: 関数(関数の定義についてのすべて)

便利な定義済み関数を呼び出すだけでなく、スクリプトは独自の関数を定義することができます。これらの機能は、一般的に2通りの使い方があります:

  1. 関数は、スクリプト自身から呼び出すことができます。この種の機能は、繰り返しを避けるため、コードをより管理しやすくするため、あるいは他の目的で使用されるかもしれません。
  2. 関数は、ユーザーがホットキーを押すなど、何らかの事象に反応してプログラムから呼び出されることがあります。例えば、各ホットキーには、そのホットキーが押されたときに実行される関数が関連付けられています。

関数を定義する方法は複数あります:

関数内の変数は、以下の場合を除き、デフォルトでその関数のローカル変数となります:

関数は、オプションでパラメータを受け取ることができます。パラメータは、括弧の中に列挙することで定義されます。事例:

MyFunction(FirstParameter, Second, &Third, Fourth:="")
{
    ;...
    return "a value"
}

関数呼び出しと同様に、関数名とオープン括弧の間にはスペースを入れてはいけません。

クローズパンテシスとオープンブレースの間の改行は任意です。2つの間に空白やコメントをいくら入れてもよい。

ByRef マーカ(&)は、呼び出し側が変数参照を渡さなければならないことを示します。関数内部では、パラメータへの参照は、実際には呼び出し元の変数にアクセスすることになります。これは、&を省略して関数内で明示的にパラメータを再参照する方法(例、%Third%)と似ていますが、この場合はパーセント記号が省略されています。パラメータがオプションで、呼び出し側がそれを省略した場合、パラメータは通常のローカル変数として動作します。

オプションのパラメータは、パラメータ名の後に:=を付けて指定し、デフォルト値を指定します。この値は、リテラルな引用符で囲まれた文字列、数値、truefalse、またはunsetでなければなりません。

関数は、値を返すことができます。そうでない場合、デフォルトの戻り値は空文字列となる。

関数定義は、その関数への呼び出しの前にある必要はありません。

詳しくは機能をご覧ください。

#Includeディレクティブ

#Include指令は、指定されたファイルの内容がこの正確な位置に存在するかのようにスクリプトを動作させるものです。これは、コードを別のファイルに整理したり、他のユーザーが書いたスクリプトライブラリを利用するためによく使われます。

インクルードファイルには、スクリプト起動時に実行されるグローバルコードを含めることができますが、メインスクリプトファイルのコードと同様に、そのようなコードは、#Include指令の前に自動実行スレッドが終了(無条件でReturnなど)されない場合にのみ実行されます。先行Returnにより実行できないコードがある場合、デフォルトで警告が表示されます。

C/C++とは異なり、#Includeは、そのファイルが既に以前の指令によってインクルードされていた場合、何もしません。同じファイルの内容を複数回インクルードする場合は、#IncludeAgainを使用します。

スクリプトの共有を容易にするために、#Includeはライブラリスクリプトのいくつかの標準的な場所を検索することができます。詳しくは、スクリプトライブラリフォルダを参照してください。

その他

ダイナミックな変数

動的変数参照は、テキスト値を受け取り、それを変数名として解釈します。

注:動的参照によって変数を作成することはできませんが、既存の変数を割り当てることは可能です。これには、スクリプトが非動的な参照を含むすべての変数が含まれ、たとえ値が割り当てられていない場合でも、その変数が含まれます。

動的変数参照の最も一般的な形式は、二重参照またはdouble-derefと呼ばれます。二重参照を行う前に、対象となる変数名を第二の変数に格納します。そして、この第2変数を用いて、二重参照を用いて間接的に対象変数に値を代入することができる。事例:

target := 42
second := "target"
MsgBox  second   ; 通常の(単一の)変数の参照⇒ターゲット
MsgBox %second%  ; ダブルデフ⇒42

現在、secondの場合は必ず変数名を含む必要があり、任意の式はサポートされていません。

動的変数参照は、1つまたは複数のリテラルテキストと1つまたは複数の変数の内容を取り、それらを結合して1つの変数名を形成することもできます。これは、名前とパーセントで囲まれた変数の断片を、空白を入れずに順番に書いていくだけです。例えば、MyArray%A_Index%や、または MyGrid%X%_%Y%。後述する擬似配列にアクセスするために使用します。

これらのテクニックは、オブジェクトのプロパティやメソッドにも適用できます。事例:

clr := {}
for n, component in ["red", "green", "blue"]
    clr.%component% := Random(0, 255)
MsgBox clr.red "," clr.green "," clr.blue

疑似配列

擬似配列は、実際には単なる離散変数の束ですが、配列の要素のように使用できるような命名パターンを持っています。事例:

MyArray1 := "A"
MyArray2 := "B"
MyArray3 := "C"
Loop 3
    MsgBox MyArray%A_Index%  ; Aを表示し、次にBを表示し、次にCを表示します。

最終的な変数名を形成するために使用される「インデックス」は数値である必要はなく、代わりに文字やキーワードを使用することができます。

これらの理由から、一般的には擬似配列ではなく、ArrayMapを使用することが推奨されます:

ラベル

ラベルはコード行を識別するもので、Gotoターゲットとして使用したり、ループから抜け出したり継続するループを指定したりすることができます。ラベルは、名前とコロンで構成されています:

this_is_a_label:

空白とコメントを除き、ラベルと同じ行に他のコードを記述することはできません。詳細は ラベルをご覧ください。