Contenu connexe
Similaire à 秀スクリプトの話 (20)
Plus de Hiroshi Tokumaru (11)
秀スクリプトの話
- 2. 徳丸浩の自己紹介
• 経歴
– 1985年 京セラ株式会社入社
– 1995年 京セラコミュニケーションシステム株式会社(KCCS)に出向・転籍
– 2008年 KCCS退職、HASHコンサルティング株式会社(現社名:EGセキュアソリューションズ株式会社)設立
• 経験したこと
– 京セラ入社当時はCAD、計算幾何学、数値シミュレーションなどを担当
– その後、企業向けパッケージソフトの企画・開発・事業化を担当
– 1999年から、携帯電話向けインフラ、プラットフォームの企画・開発を担当
Webアプリケーションのセキュリティ問題に直面、研究、社内展開、寄稿などを開始
– 2004年にKCCS社内ベンチャーとしてWebアプリケーションセキュリティ事業を立ち上げ
• 現在
– EGセキュアソリューションズ株式会社 代表 https://www.eg-secure.co.jp/
– 独立行政法人情報処理推進機構 非常勤研究員 https://www.ipa.go.jp/security/
– 著書「体系的に学ぶ 安全なWebアプリケーションの作り方」(2011年3月)
「徳丸浩のWebセキュリティ教室 」(2015年10月)
– 技術士(情報工学部門)
2
- 4. 言語処理系とわたし
• 実は若い頃からコンパイラ作りを趣味としていた
• 代表作は Pascal コンパイラ Cabezon (カベソン)
– Cabezon 自体でコンパイラが記述され、自身をコンパイルできる
– MS-DOS上で動作
– MASM 等のアセンブリ言語を生成
– 自分自身の記述のみを目標においていたので、「実用」は考えていなかった
– しかし、いくつかの大学で Pascal演習用に使われたらしい
• Cabezon使ってましたという方にお会いしたことも…
– 東欧でも、「マシンパワーが貧弱でも動くPascalコンパイラ」として教育機関
で使われたらしい(お礼のメールから)
– (もう少しちゃんと作っておけば)
• お仕事でも、JavaScriptに似たGreenScriptという処理系を作った経験
4
- 5. 秀丸マクロのサンプル
#n=1; // コメント形式1 #で始まる識別子は数値型変数
while (#n <= 10) { /* コメント形式2 while や if が使える */
call fib #n; // 関数・サブルーチンは call で呼び出す
insert str(##return) + "n"; // 関数の戻り値は、##return あるいは $$return
#n = #n + 1;
}
endmacro // マクロを停止する。これがないと関数の中身に突っ込む
fib: // 関数 fib の定義
if (##1 <= 2) { // ##1 は一番目の引数(数値)
return 1;
}
call fib ##1 - 2; // 再帰呼び出しが可能
##temp = ##return; // 戻り値を覚えておく。## で始まる識別子は数値型ローカル変数
call fib ##1 - 1;
return ##temp + ##return; // return 式; で値を返す
5
- 6. 秀丸マクロのイケテナイ点
• 全体的に独自仕様が強く、いかにも昔のマクロという感じ
• 変数に型があり、一文字が $ か # で決まる
• 関数の戻り値を疑似変数 $$return または ##return で受けとる
• 変数の宣言は必要ないので、変数名をタイプミスすると、わかりづら
いバグの原因となる
• サブルーチン・関数はただのラベルなので、上から「なだれ込む」
• 組み込みのサブルーチンは「文」と呼ばれ、ユーザー定義のサブルー
チンと呼び出し方が違う
• 組み込みの関数は、値をダイレクトに受け取れる
• サブルーチンの呼び出しネストが最大20までと厳しい
6
- 7. 関数の戻り値を疑似変数 $$return または ##return で受けとる
• 以下のように書きたいが書けない
while (foo() > 0) {
// 色々な処理
}
• こうなる(辛い)
call foo;
while (##return > 0) {
// 色々な処理
call foo;
}
7
- 10. 親言語を決める
• 秀スクリプトの記述言語(親言語)として何を選択するか?
• 求められる条件は下記の通り
– 親言語は、秀スクリプトのスーパーセットになるので、親言語を決めることは、
秀スクリプトの言語仕様を決めることに近い
– 秀丸マクロを生成することから、秀丸マクロと極端に異なるパラダイムの言語
は選択しにくい
– 秀丸マクロは変数に型があるので、親言語も変数に型があるものが望ましい
– グローバル変数を多用せざるを得ないのでグローバル変数が使いやすいもの
– Windows上のコンソールアプリケーションが開発できるもの
• 変数に型があるスクリプト言語ということで、TypeScriptを採用
– それまでTypeScriptもnode.jsも書いたことはなかったw
10
- 13. 配列について
• 数値型と文字列型配列が可能
var a1 : number[]; // 整数型の配列変数
var a2 : number[] = new Array(); // 同上
// TypeScriptとしても動作させたい時はこの記述とする
var a3 : string[]; // 文字列の配列変数
var a4 : string[] = new Array(); // 同上
// TypeScriptとしても動作させたい時はこの記述とする
• = new Array() はTypeScriptとの互換性のために用意していて、秀
スクリプトコンパイラは単に読み捨てる
• 配列の添字は整数のみで、文字列を添え字とする配列(連想配列)は
ない
13
- 14. Inside 秀スクリプト(1) 構文解析
• 手書きコンパイラの構文解析は、再帰下降型が主流…かと
• EBNF (拡張バッカスナウアーフォーム)をそのままプログラミング
• 掛け算と足し算のみの文法の構文解析の例
14
primary : Identity
| number
multiply : primay { "*" primary }
add : multiply { "+" multiply }
function primary() {
if (sym == IDENT) {
getsym();
return identity;
} else if (sym == NUMBER) {
getsym();
return number;
} else
error();
}
function multiply() {
primary();
while (sym == "*") {
getsym();
primary();
}
}
function add() {
multiply();
while (sym == "+") {
getsym();
multiply();
}
}
- 15. JavaScript(TypeScript)は演算子の優先順位が複雑…
15
演算子の種類 対応する演算子
メンバ . []
インスタンスの呼び出し/作成 () new
否定/インクリメント ! ~ - + ++ -- typeof void delete
乗算/除算 * / %
加算/減算 + -
ビットシフト << >> >>>
関係 < <= > >= in instanceof
等値 == != === !==
ビット論理積 &
ビット排他的論理和 ^
ビット論理和 |
論理積 &&
論理和 ||
条件 ?:
代入 = += -= *= /= %= <<= >>= >>>= &= ^= |=
コンマ ,
- 18. 秀スクリプトの演算子順位表
operators[0] = "!"; opPriority[0] = 2; hidePriority[0] = "5";
operators[1] = "*"; opPriority[1] = 3; hidePriority[1] = "1";
operators[2] = "/"; opPriority[2] = 3; hidePriority[2] = "1";
operators[3] = "%"; opPriority[3] = 3; hidePriority[3] = "1";
operators[4] = "+"; opPriority[4] = 4; hidePriority[4] = "2";
operators[5] = "-"; opPriority[5] = 4; hidePriority[5] = "2";
operators[6] = "<"; opPriority[6] = 5; hidePriority[6] = "3";
operators[7] = "<="; opPriority[7] = 5; hidePriority[7] = "3";
operators[8] = ">"; opPriority[8] = 5; hidePriority[8] = "3";
operators[9] = ">="; opPriority[9] = 5; hidePriority[9] = "3";
operators[10] = "=="; opPriority[10] = 6; hidePriority[10] = "3";
operators[11] = "!="; opPriority[11] = 6; hidePriority[11] = "3";
operators[12] = "&"; opPriority[12] = 7; hidePriority[12] = "1";
operators[13] = "^"; opPriority[13] = 8; hidePriority[13] = "1";
operators[14] = "|"; opPriority[14] = 9; hidePriority[14] = "1";
operators[15] = "&&"; opPriority[15] = 10; hidePriority[15] = "4";
operators[16] = "||"; opPriority[16] = 11; hidePriority[16] = "4";
18
- 26. 演算子順位構文解析の様子( * d をプッシュ)
26
5
4 #d
3 *
2 #c
1 +
0 (#a*#b)
a * b + c * d
式の処理が終わったので、この後は
スタックの中身を還元処理していく
スタック
- 31. 秀丸マクロのいやらしいところ
特に注意!
「!」の優先順位が「||」や「&&」と同じなので、例えば
if( #a || !#b)
if( !#a || #b)
のような表記はうまくいきません。このような表記は、
if( #a || (!#b) )
if( (!#a) || #b )
のように記述する必要があります。それと、「||」と
「&&」の優先順位も同じになってしまってるので、例えば
if( #a || #b && #c )
のような表記もC言語とは意味が違ってしまいます。このような表記は、
if( #a || ( #b && #c ) )
のように、必要な箇所をカッコで囲むなどして優先順位をはっきりさせるのがお勧めです。
31
この式は、実行時警告が表示される(うざい)
秀丸マクロのヘルプより引用
- 32. 2項演算子のコード生成
function genBianryOp(code1: string, op: string, code2: string): string {
var bopPriority = getHidePriority(op); // 演算子の優先順位
var priority1 = getCodePriority(code1);
var type1 = getCodeType(code1);
code1 = getCodeBody(code1);
var priority2 = getCodePriority(code2);
var type2 = getCodeType(code2);
code2 = getCodeBody(code2);
var etype = checkBinOpType(op, type1, type2); // 式の型
if (priority1 > bopPriority || ((priority1 == "4") && (bopPriority == "4")) )
code1 = "(" + code1 + ")";
if (priority2 >= bopPriority)
code2 = "(" + code2 + ")";
return bopPriority + etype + "R" + code1 + op + code2;
}
32
第1オペランドの優先順位、型、式の中身
第2オペランドの優先順位、型、式の中身
秀丸の「いやらしいところ」
の対処として括弧をつける
- 35. コード生成の例
• 以下の秀スクリプトは
function foo():number { }
while (foo() > 0) { }
• 以下のようにコンパイルされる
goto _end_foo
foo:
// ここに関数の中身
return 0;
_end_foo:
goto _LL1
_LL0:
// ここにループの中身
_LL1: // continue用ラベル
call foo ;#_0=##return; // 関数を呼び出し、一次変数 #_0 に受ける
if (#_0>0) goto _LL0 // 秀丸マクロにも while はあるが一次変数を使うためにgotoで
_LL2: // break用ラベル
35
- 37. TypeScriptと秀スクリプトの両方でコンパイルする工夫
37
• 以下のようにした
if (version() > 0) { // version()は秀丸マクロでは 正の値を返す
… // 秀スクリプト固有の処理
}
//EOF //EOFは秀スクリプトのEOFであり、これ以降を無視する
… // TypeScript 固有の処理 (TypeScript は //EOF を無視する)
… // この内容は秀スクリプト処理系は見ないので好きに書ける
// version()の定義。TypeScriptは関数の前方参照が可能
// TypeScriptとして動かした場合、version()は 0 を返すように定義
function version() : number {
return 0;
}
- 39. サブルーチンのネスト制限問題(2)
• 秀シリーズサポートフォーラムでお願いしてみた
• ockeghem さん
…「callされたサブルーチンからさらにサブルーチンを呼ぶこともできます。最高20回程度重複可
能です。」という制限にひっかかってしまい、関数呼び出しの三重にネストした程度の式でエ
ラーになってしまいます。
他ではあまりニーズはないことと思うので恐縮ではあるのですが、この 20回という制限を緩和し
てはいただけないでしょうか?
• 秀丸担当さん
サブルーチンのネストの上限は確かに20回の制限があります。
無制限にするとしたらプロセスをまたぐなどの都合があって面倒になるのですが、単純に上限の
数を増やすだけであれば簡単な修正でできると思います。
別言語からマクロを生成するというのはすごいですね。
三重ネストで20回消費するようなマクロが生成されるということだとしたら、約7倍(140回)以上
あればいいでしょうか。
それでよければ140回か、きりのいいところで200回くらいにしてみようかと思います。
39
http://www.maruo.co.jp/hidesoft/1/indexg.html より引用
- 40. 秀スクリプトのコンパイル・実行方法
(1) TypeScript+Node.jsとして実行する
– hidescript.ts をTypeScriptコンパイラによりコンパイルする。hidescript.jsが生成される
– hidescript.js をNode.js上で実行する
C:> node hidescript.js foo.hs
– foo.mac ができるので、秀丸で実行する
※ 環境変数 hidemacrodirに秀丸マクロ用フォルダ名をセットしておくと、
そのフォルダにfoo.macが書き込まれます。
(2)秀丸マクロとして実行する
– hidescript.mac を hs.mac にリネームして秀丸上でマクロ登録しておく
– foo.hsを秀丸で開く
– ショートカットキー等によりhs.macを実行する
– foo.macのウィンドウが開き、コンパイル結果が挿入、保存される
– foo.macをショートカット等で実行する
40
- 43. ,、,,,、,,,
_,,;' '" '' ;;,,
(rヽ,;''""''゛゛;,ノr)
,; i ___ 、___iヽ゛;, テスト書いてないとかお前それ@t_wadaの前でも同じ事言えんの?
,;'''|ヽ・〉〈・ノ |゙ ';,
,;''"| ▼ |゙゛';,
,;'' ヽ _人_ / ,;'_
/シ、 ヽ ⌒⌒ / リ \
| "r,,`"'''゙´ ,,ミ|
| リ、 ,リ |
| i ゛r、ノ,,r" i _ |
| `ー――-----------v ⌒´ )
(ヽ _____________ ,, _´)
(_⌒_______________ ,, ィ
T |
| |
43