秀スクリプトは、TypeScriptを大幅に縮小した文法を持ち、コンパイラによって秀丸マクロに変換され、秀丸上で実行されます。 秀スクリプトの主な特徴は下記のとおりです。
- TypeScript同様、変数に型がある
- 数値(整数)型と文字列型、これらの配列をサポート
- if、while、do … whileの制御構造
- 関数のサポート
JavaScript等で共通の /* … */ 形式と // 形式のコメントが使えます。コメントのネストはサポートしていません。
TypeScript(JavaScript)と同じような式が使えます。詳しくは下記をご覧ください。
整数型と文字列型の単純変数(スカラ)および配列が利用可能です。変数には型があります。変数の宣言時に型を明示するか、初期化により間接的に型を指定する必要があります。 変数にはグローバル変数とローカル変数があります。ローカル変数は関数の中で宣言されたものです。グローバル変数は、どこからでも(関数の中からでも)参照可能です。ブロックスコープはサポートしていません。変数や関数が同じスコープで重複するとエラーになります。
var n : number; // 整数型の単純変数
var k = foo() + 1; // foo()は整数を返す関数とすると、k も整数型の変数となる
var s : string; // 文字列型の変数
var s = "I have a pen!"; // 初期化済みの文字列型変数
var a1 : number[]; // 整数型の配列変数
var a2 : number[] = new Array(); // 同上。TypeScriptとしても動作させたい時はこの記述とする
var a3 : string[]; // 文字列の配列変数
var a4 : string[] = new Array(); // 同上。TypeScriptとしても動作させたい時はこの記述とする
var x, y, z : number; // これはエラーになります。実装上の制限により、一つのvarで宣言できる変数は一つのみです。
var n = 1; // 同一スコープで n が重複するのでエラーになります。
下記は、変数の秀スクリプトと秀丸マクロの関係を示すサンプルです。 まずは、秀スクリプト
var k = 1;
var s = "foo";
var a1 : number[];
a1[1] = 5;
var a2 : string[];
a2[3] = "bar";
秀丸マクロへのコンパイル結果は下記の通り。
#k=1;
$s="foo";
#a1[1]=5;
$a2[3]="bar";
現在のところ10進の整数値のみサポートしています。
例:
345
0
-10
ダブルクォートあるいはシンクルクォートで囲まれた文字列リテラルが使用できます。
例:
"I have a pen!"
'You are a boy.'
| 優先順位 | 結合方向 | 演算子 | 説明 |
|---|---|---|---|
| 1 | ← | ! ~ - + |
論理否定(not) ビットNOT 単項マイナス 単項プラス |
| 2 | → | * / % |
乗算 除算 剰余 |
| 3 | → | + - |
可算・文字列連結 減算 |
| 4 | → | < <= > >= |
大小比較(数値、文字列) |
| 5 | → | == != |
等値(数値、文字列) 非等値(数値、文字列) |
| 6 | → | & | ビットAND |
| 7 | → | ^ | ビットXOR |
| 8 | → | | | ビットOR |
| 9 | → | && | 論理AND |
| 10 | → | || | 論理OR |
制御構文として、JavaScript風の ifや、while、do whileが使用できます。
論理型はなく、数値型で代用します。0がFALSE、0以外がTRUEを示します。
以下の形式の if文が使えます。JavaScriptやTypeScriptと同じ形式です。
if ( 条件式 ) 文 if ( 条件式 ) 文 else 文
例:
if (foo() < 0) {
x = 0;
} else if (foo() == 0) {
x = 1;
} else {
x = 2;
}
上記は下記のように秀丸マクロにコンパイルされます(読みやすくするためにインデントを追加)
call foo ;#_0=##return;
if ( #_0<0) {
#x=0;
} else {
call foo ;#_0=##return;
if ( #_0==0) {
#x=1;
} else {
#x=2;
}
}
以下の形式の while文が使えます。JavaScriptやTypeScriptと同じ形式です。
while ( 条件式 ) 文
例:
var n = 0;
var i = 1;
while (i <= 10) {
n = n + i;
i = i + 1;
}
message("1から10の合計は " + str(n) + " です");
上記は下記のように秀丸マクロにコンパイルされます(読みやすくするためにインデントを追加)
#n=0;
#i=1;
goto _LL1
_LL0:
#n=#n+#i;
#i=#i+1;
_LL1:
if (#i<=10) goto _LL0
_LL2:
message "1から10の合計は "+str( #n)+" です";
以下の形式の do … while文が使えます。JavaScriptやTypeScriptと同じ形式です。
do 文 while ( 条件式 );
例:
var i = 1;
do {
insert(str(i));
i = i + 1;
} while (i <= 10);
上記は下記のように秀丸マクロにコンパイルされます(読みやすくするためにインデントを追加)
#i=1;
_LL0:
insert str( #i)+"\n";
#i=#i+1;
_LL1:
if (#i<=10) goto _LL0
_LL2:
break文は現在の繰り返し(whileやdo … while)を脱出します。繰り返し処理をしていない場合はコンパイルエラーになります。
continue文は現在の繰り返し(whileやdo … while)の条件評価までスキップします。繰り返し処理をしていない場合はコンパイルエラーになります。
TypeScript風の関数が使用できます。関数の引数と戻り値の型を宣言する必要があります。これらの型として、数値あるいは文字列が使えますが、配列を宣言することはできません。戻り値の型として void を指定すると、戻り値を返さないという意味になります。
関数宣言の例を以下に示します。
function factorial(n : number) : number {
if (n <= 0)
return 1;
else
return factorial(n - 1) * n;
}
秀丸マクロへのコンパイル結果は下記のとおりです。見やすくするために空白等を調整しています。
goto _end_factorial
factorial:
if (##1 <= 0) {
return 1;
} else {
call factorial ##1-1;
##_0 = ##return;
return ##_0 * ##1;
}
return;
_end_factorial:
関数のコンパイル結果がgotoが始まっています。これが必要な理由は以下のとおりです。 秀丸マクロの関数・サブルーチンは単なるラベルであり、call で呼び出すと関数・サブルーチンとして動きますが、call以外でこのラベルに至った場合でもサブルーチンの中身が実行されてしまいます。これを避けるために、gotoによりサブルーチンを迂回するコードを生成しています。
上記の例に関数の呼び出し例もあります。ご覧のように関数の再帰呼び出しが可能です。元々秀丸マクロの再帰呼び出しはcallのネストの制限が20程度となっていました。これでは言語処理系等を開発する上では制約が厳しいので、お願いして制限を200に増やしていただきました(秀丸 Ver8.73以降)。一ユーザーの希望を実現いただいて深く感謝いたします。 秀丸マクロの関数・サブルーチンはcall文により呼び出され、関数の戻り値は、##returnあるいは$$returnという変数で受け取ります。このため、秀スクリプトでは、関数の戻り値をいったん##_0, ##_1... などという一時変数に保存します。これにより、以下のような記述が可能になります。
if (foo() > bar()) {
x = 1;
}
コンパイル結果は下記となります。
call foo; #_0=##return;
call bar; #_1=##return;
if (#_0 > #_1) {
#x=1;
}
ご覧のように、秀丸マクロは条件式の中に関数呼出しを直接書くことができないため、いったん一時変数に値を受けるようにしています。
秀スクリプトから、秀丸マクロの文や関数、「内部的な値を表現するキーワード」を呼び出すことができます。現状は、秀スクリプト自体をコンパイルするために必要なものプラスアルファ程度しか登録できていませんが、随時増やす予定です。 秀スクリプト上で、組み込み関数を登録するには、以下の関数を呼び出します。
registerBuiltinFunction(関数名、型情報);
一例として、gettext(x1, y1, x2, y2) 関数は以下のように宣言します。
registerBuiltinFunction("gettext", "snnnn");
型情報のsnnnnのうち、先頭のsは戻り値の型(string)を示し、nnnnは数値型(number)の引数4つをとることを示します。 また、delete文の宣言は下記のとおりです。
registerBuiltinFunction("_delete", "v");
関数名が _delete となっているのは、deleteがTypeScriptの予約語になっているため衝突を避ける目的です。組み込み関数名の先頭文字をアンダースコアにしている場合、コンパイル時にアンダースコアを削除します。 また、型情報の v は、戻り値をとらない(void)ことと、引数がない(2文字目以降がない)ことを意味します。
TypeScriptやJavaScriptは、関数を呼び出す時点ではまだ定義されていない関数を呼び出すことができます。このような呼び出し方を前方参照と呼びます。一方、秀スクリプトは関数の前方参照には対応していません。 しかし、言語処理系のようなプログラムを開発する場合は、関数が互いに再帰に呼び出し合うような記述(相互再帰)が出てくるため、関数の前方宣言ができるようにしました。 TypeScriptとの互換性のため、関数の前方宣言には関数リテラルの構文を借りていますが、秀スクリプトは非常に素朴な言語であり関数リテラルの機能が使えるわけではありません。あくまで関数の型宣言を予めできるだけです。
まず、以下はエラーになる例です。
function bar() {
var x = foo(3); // この段階では foo()が未定義なのでエラーになる
}
// ...
function foo(x: number): number { ... } // 関数fooの定義
bar();
前方参照を避けるためには、以下のようにします。
var foo: (x: number) => number; // 関数リテラルの構文により関数の前方宣言を行う
function bar() {
var x = foo(3); // 前方宣言があるのでエラーにならない
}
// ...
foo = function (x: number): number { ... } // 関数fooの定義
bar(); // bar()経由でfoo()を呼び出す