GAWK シンタックスハイライト
AWK / C / vbScript(VBA) / バッチファイルのソースをHTML化(divボックス)し、コードのシンタックスハイライトを行う、サクラエディタのマクロです。
同目的の変換サイトにコピペしに行ったり、対象言語専用のアプリを使わなくても、エディタ上で完結(このページもサクラエディタで書いている)しますので、筆者にとって必須アイテムとなっています。数千行の C ファイルでも、一瞬でシンタックスハイライトを適用した HTML コードを生成しますので、ストレスはありません。
AWKスクリプト本体は、各言語用のレキシカルアナライザを間接呼び出しで使っているため、1,100行位になります。
注意!GAWK4 以降でないと動作しません(Indirect Function Calls)
GAWK4.1.4 for win32 と 同4.2.1/5.0.1 で動作確認しました。
C言語の例
3609 : /* nondec2awknum --- convert octal or hex value to double */
3610 :
3611 : /*
3612 : * Because of awk's concatenation rules and the way awk.y:yylex()
3613 : * collects a number, this routine has to be willing to stop on the
3614 : * first invalid character.
3615 : */
3616 :
3617 : AWKNUM
3618 : nondec2awknum(char *str, size_t len, char **endptr)
3619 : {
3620 : AWKNUM retval = 0.0;
3621 : char save;
3622 : short val;
3623 : char *start = str;
3624 :
3625 : if (len >= 2 && *str == '0' && (str[1] == 'x' || str[1] == 'X')) {
3626 : /*
3627 : * User called strtonum("0x") or some such,
3628 : * so just quit early.
3629 : */
3630 : if (len <= 2) {
3631 : if (endptr)
3632 : *endptr = start;
3633 : return (AWKNUM) 0.0;
3634 : }
3635 :
3636 : for (str += 2, len -= 2; len > 0; len--, str++) {
3637 : switch (*str) {
3638 : case '0':
3639 : case '1':
3640 : case '2':
3641 : case '3':
3642 : case '4':
3643 : case '5':
3644 : case '6':
3645 : case '7':
3646 : case '8':
3647 : case '9':
3648 : val = *str - '0';
3649 : break;
3650 : case 'a':
3651 : case 'b':
3652 : case 'c':
3653 : case 'd':
3654 : case 'e':
3655 : case 'f':
3656 : val = *str - 'a' + 10;
3657 : break;
3658 : case 'A':
3659 : case 'B':
3660 : case 'C':
3661 : case 'D':
3662 : case 'E':
3663 : case 'F':
3664 : val = *str - 'A' + 10;
3665 : break;
3666 : default:
3667 : if (endptr)
3668 : *endptr = str;
3669 : goto done;
3670 : }
3671 : retval = (retval * 16) + val;
3672 : }
3673 : if (endptr)
3674 : *endptr = str;
3675 : } else if (len >= 1 && *str == '0') {
3676 : for (; len > 0; len--) {
3677 : if (! isdigit((unsigned char) *str)) {
3678 : if (endptr)
3679 : *endptr = str;
3680 : goto done;
3681 : }
3682 : else if (*str == '8' || *str == '9') {
3683 : str = start;
3684 : goto decimal;
3685 : }
3686 : retval = (retval * 8) + (*str - '0');
3687 : str++;
3688 : }
3689 : if (endptr)
3690 : *endptr = str;
3691 : } else {
3692 : decimal:
3693 : save = str[len];
3694 : str[len] = '\0';
3695 : retval = strtod(str, endptr);
3696 : str[len] = save;
3697 : }
3698 : done:
3699 : return retval;
3700 : }
出典:GAWK4.2.1 for win32 (src), builtin.c より 関数 nondec2awknum() 抜粋
vbScriptの例 (当該マクロ source2html.vbs)
source2html.vbs
1 : 'サクラマクロ source2html.vbs
2 : 'ドラッグ選択したawk/c/vbs/vba/batソースをhtml化し、クリップボードに送る
3 : 'source2html.bat, mshta_clip_out.cmd, source2html.awk(gawk420以降)
4 : '上記3ファイルを同一フォルダ(source2html)に格納
5 : 'batpathを設定し、当vbsファイルを\sakura\macros\に保存
6 : 'サクラエディタにマクロを登録(コンテキストメニューに追加)
7 :
8 : Option Explicit
9 : Const batpath = "y:\source2html\source2html.bat"
10 : Dim spath, sfile, smode, sprompt, nline, snz, scmd
11 :
12 : spath = Editor.GetFilename 'fullpath
13 : sfile = Mid(spath, InStrRev(spath, "\") + 1) 'filename
14 : smode = LCase(Mid(sfile, InStrRev(sfile, ".") + 1)) 'extention
15 :
16 : Select Case smode
17 : Case "cpp", "c" ,"h"
18 : smode = "C"
19 : Case "vbs", "bas", "frm", "cls"
20 : smode = "VB"
21 : Case "bat", "cmd"
22 : smode = "BAT"
23 : Case Else
24 : smode = "AWK"
25 : End Select
26 :
27 : sprompt = smode & " モードで html に変換します。" & vbCrLf & _
28 : vbCrLf & vbCrLf & "先頭行番号を設定してください。" & vbCrLf & _
29 : vbCrLf & "(行番号不要:入力欄ブランク)"
30 :
31 : nline = InputBox(sprompt, "source2html", "1") 'head line default 1
32 :
33 : If Not IsEmpty(nline) Then
34 : snz = Editor.GetSelectedString(0)
35 : Editor.SetClipboard 0, snz
36 : scmd = batpath & " " & sfile & " " & nline 'send 2args to batfile
37 : Editor.ExecCommand scmd, 0
38 : End If
9行目 batpath は下記のバッチファイルへの絶対パスです。(筆者の環境ではRamDisc上にある)
このマクロは、選択テキストをクリップボードに送り、現在ファイルのファイル名(拡張子付き)と、行番号表示の開始番号を下記のバッチファイルに送り、そのバッチを実行します。
VB系の問題点
書きやすさとは裏腹に構造が妙に複雑なVB系言語(VB6/VBA/VBS)ですので、lex のみで解析するには限界があります。顕著なところでは、ユーザー定義の Function/Sub/Property プロシージャと配列/変数/コレクション等のオブジェクトを見分けることができません。これは配列の記号が()であること、Subは文法上()が使えない(Call除外)ことが原因で、1回の字句解析(プロシージャスコープ)で判断することは不可能です。後述 AWK の awkall モードのように、事前にモジュール全体をlexで読み、各プロシージャ名(実装部から抽出)を動的に登録する、という手段を用いれば可能となりそうです。が、モジュールをまたぐ関数とか考えると、全モジュールを読んで、スコープに配慮して.....とても複雑になりそうです。
より妥当な字句解析を行うためには、同時に構文解析が必要で、これはさらにメンドクサイです。
例) コード中の「Line」は 変数/メソッド/プロパティ/ステートメントのどれに該当するか?
source2html.bat
こちらはキャピタライズや大文字変換しません。好みの問題です。書いた通りになります。
あまりやることがないのと、エスケープ関係がよくわからないので、lex は AWK のものをほぼ使いまわしで使っています。コマンド引数の色を順番に変えてみるとか、やってみるのもいいかもしれません。
1 : @echo off
2 : rem source2html.bat
3 : rem クリップボードのソースをhtmlに変換
4 : pushd %0\..
5 : call mshta_clip_out.cmd > _temp1.txt
6 : gawk414 -v ARG1=%1 -v ARG2=%2 -f source2html.awk _temp1.txt > _temp2.txt
7 : rem call mshta_to_clip.cmd < _temp2.txt
8 : clip < _temp2.txt
9 : del _temp?.txt
10 : rem pause
このバッチファイルは、クリップボードデータからテキストファイル1を作成し、そのテキストをGAWKにかけ、結果をテキストファイル2に書き出します。最後にテキストファイル2のデータをクリップボードに送って完了します。
参考
call mshta_clip_out.cmd > _temp1.txt
は以下とほぼ等価で、置き換え可能です。
powershell get-clipboard > _temp1.txt
(相違点:EOF直前の改行を含む)
mshta_clip_out.cmd (一行書き必須)
バッチファイルと同様です。
1 : @MSHTA.EXE vbscript:Execute("s=clipboardData.getData(""text""):If Not IsNull(
s) Then CreateObject(""Scripting.FileSystemObject"").GetStandardStream(1).Wri
te(s):End If:close()")
クリップボードから標準出力へテキストデータを送ります。
AWKプログラム
※実行には GAWK4 以降必須
上記の2ファイル(source2html.bat & mshta_clip_out.cmd)と source2html.awkは同一ディレクトリに置きます。新規フォルダに保存することをお勧めします。
以下はエディタにてソースを全選択し、awkall
モードでHTML化したものです。
(awkall
モード:開始行番号入力欄に awkall
と入力 ※AWKソースのみ対応)
このモードでは、次の仕事を全自動で行います。
1.セクション/関数単位のbox領域分割(記述位置解析) |
2.ソースファイル名ヘッダ生成とID埋め込み |
3.セクション/関数名の見出しラベル生成とID埋め込み |
4.リンクの生成(画面左側サブメニュー参照) |
5.シンタックスハイライト |
上記を実現するために、2回 lex
にかけて、リンクを作成していますので、処理時間は、シンタックスハイライトのみを行うものの 1.5 倍ほどになります。
貼り付けるとこうなります。(先頭 コピペ用リンク)
source2html.awk
BEGIN
1 : # source2html.awk; sakura editor macro
2 : # エディタ上で選択された awk/c/vbs/vba/bat のソースをhtml化
3 : # ※GAWK4 以降が必要 (Indirect Function Calls)
4 :
5 : #. BEGIN:マクロvbs~batファイル経由で2つの変数を受け取り、lexを選択
6 : #@load "time";
7 : BEGIN {
8 : #ARG1: ファイル名 拡張子 (c/awk/vbs/bat等)
9 : #ARG2: 先頭行番号 (sakura macro InputBox)
10 :
11 : #_time = gettimeofday();
12 :
13 : file_name = ARG1;
14 : extention = substr(file_name, index(file_name, ".") + 1);
15 : language = tolower(extention);
16 : if (ARG2 + 0 == ARG2) { line_num = ARG2; fline = 1; }
17 : else if (ARG2 ~ /^awkall$/) {
18 : fline = line_num = flink = 1;
19 : language = "awk";
20 : }
21 : else fline = 0;
22 :
23 : if (language ~ /^c$|^h$|^cpp$/) language = "cpp"; #cpp 開発中
24 : else if (language ~ /^vbs$|^bas$|^frm$|^cls$/) language = "vb";
25 : else if (language ~ /^bat$|^cmd$/) language = "bat";
26 : else language = "awk";
27 :
28 : _asc_init(); #tab/space変換に使う(blength)
29 : _lex_init(language); #各言語の区切り方等を設定
30 : _kyw_init(language); #各言語のキーワード辞書
31 : _html_tag_init(); #htmlタグを設定
32 :
33 : #lex_xxx関数の間接呼び出し設定
34 : func_name["awk"] = "lex_awk";
35 : func_name["bat"] = "lex_bat";
36 : func_name["cpp"] = "lex_cpp";
37 : func_name["vb"] = "lex_vb";
38 : call_func = func_name[language];
39 :
40 : #link(AWK)
41 : if (flink) {
42 : _infoproc_init();
43 : while (getline < ARGV[1] > 0) {
44 : lex_awk($0, la);
45 : infoproc(la, ++nr, _sections);
46 : }
47 : close(ARGV[1]);
48 : sect_div(_sections, nr, sdiv);
49 :
50 : print OLINK file_name CLTAG file_name CLINK;
51 : for (i in sdiv)
52 : print OLINK sdiv[i][1] CLTAG sdiv[i][1] CLINK;
53 : print "\n\n\n";
54 : print O_FLE file_name CLTAG file_name CNFLE;
55 : _c_ = 1;
56 : } #LINK(AWK)
57 :
58 : line_str = ""; #$0
59 : if (!flink) printf("%s", S_DIV); #<div> (box領域開始)
60 : }
ACTION_01
61 : #. ACTION_01: #各行毎に字句解析し、html化する
62 : {
63 : line_str = tab2space($0, 4); #tab/space変換
64 :
65 : if (flink && sdiv[_c_][0] == NR) #リンク埋め込み
66 : printf("%s", O__H5 sdiv[_c_][1] CLTAG sdiv[_c_][1] CN_H5 S_DIV);
67 :
68 : if (fline) printf("%s%4d : %s", SPNLN, line_num, SPNE); #行番号
69 : @call_func(line_str, arr2D); #lex_xxx
70 : printf("%s", trans_html(arr2D)); #html化
71 :
72 : if (flink && sdiv[_c_][2] == NR) { print E_DIV; _c_++; } #box閉じる
73 : else print "";
74 :
75 : line_num++;
76 : }
END
77 : #. END:boxを閉じる
78 : END {
79 : if (!flink) print E_DIV; #</div> (box領域閉じる)
80 : #エラー
81 : if (_erct)
82 : for (i in _er) printf("No.%2d: %s<br>", i, _er[i]);
83 :
84 : #_time = gettimeofday() - _time;
85 : #printf("Elapsed Time = %d(msec)\n", _time * 1000)
86 : }
_html_tag_init()
87 : #. _html_tag_init(): #タグの雛形の定義
88 : # 戻値:
89 : # ※cssファイルにクラスを登録のこと
90 : function _html_tag_init() {
91 : O_FLE = "<h3 id=\"";
92 : CNFLE = "</h3>\n";
93 : OLINK = "<li><a href=\"#";
94 : CLINK = "</a></li>";
95 : O__H5 = "<h5 id=\"";
96 : CN_H5 = "</h5>\n";
97 : CLTAG = "\">";
98 : S_DIV = "<div class=\"CodeArea\"><code>"; #box領域開始
99 : E_DIV = "</code></div>"; #box領域終了
100 : SPNLN = "<span class=\"Dline\">"; #行番号
101 : SPN_c = "<span class=\"Dcomt\">"; #コメント
102 : SPN_r = "<span class=\"Dregx\">"; #正規表現(AWK)
103 : SPN_l = "<span class=\"Dlitr\">"; #文字列リテラル
104 : SPN_q = "<span class=\"Dquot\">"; #文字(single quote C)
105 : SPN_n = "<span class=\"D_num\">"; #数値
106 : SPN_t = "<span class=\"Dtype\">"; #型情報キーワード(C/VBA)
107 : SPN_k = "<span class=\"Dctrl\">"; #文制御キーワード
108 : SPN_x = "<span class=\"Dkext\">"; #その他の重要キーワード
109 : SPN_f = "<span class=\"Dbltf\">"; #組み込み関数
110 : SPN_v = "<span class=\"Dbltc\">"; #組み込み変数/定数(マクロC)
111 : SPN_u = "<span class=\"Duser\">"; #ユーザー定義関数
112 : SPN_i = "<span class=\"Didtf\">"; #変数等の識別子
113 : SPN_o = "<span class=\"D_ope\">"; #演算子等
114 : SPN_p = "<span class=\"Dpanc\">"; #句読文字ほか
115 : SPN_g = "<span class=\"Dlabl\">"; #GoToラベル c,vb,bat
116 : SPN_d = "<span class=\"DCdef\">"; #define Macros (C)
117 : SPN_a = "<span class=\"D_ppd\">"; #プリプロセッサ命令(C,AWK)
118 : SPN_m = "<span class=\"Dmemb\">"; #オブジェクトメンバー(VB)
119 : SPNE = "</span>"; #共通 span 閉じる
120 :
121 : }
trans_html()
122 : #. trans_html():トークンの属性に従ってタグを埋め込む
123 : # 戻値: htmlタグを追加した文字列(1行のソースコード)
124 : # ar2d:in: ex_xxx()の吐き出す2次元配列
125 : # ar2d[x][0]=属性 ar2d[x][1]=トークン
126 : function trans_html(ar2d, ct, i, tok, snz) {
127 : ct = length(ar2d);
128 : snz = tok = "";
129 : for (i = 1; i <= ct; i++) {
130 : tok = ar2d[i][1];
131 : #htmlブラウザ上で誤動作してしまう記号を置換
132 : gsub(/&/, "\\&amp", tok);
133 : gsub(/>/, "\\&gt", tok);
134 : gsub(/</, "\\&lt", tok);
135 :
136 : switch (ar2d[i][0]) {
137 : case /t/: #型
138 : snz = snz SPN_t tok SPNE;
139 : break;
140 : case /k/: #文制御
141 : snz = snz SPN_k tok SPNE;
142 : break;
143 : case /x/: #その他のキーワード(VBA)
144 : snz = snz SPN_x tok SPNE;
145 : break;
146 : case /f/: #組み込み関数
147 : snz = snz SPN_f tok SPNE;
148 : break;
149 : case /v/: #組み込み変数/定数(C:組み込みマクロ)
150 : snz = snz SPN_v tok SPNE;
151 : break;
152 : case /m/: #オブジェクトメンバー(VB)
153 : snz = snz SPN_m tok SPNE;
154 : break;
155 : case /u/: #ユーザー定義関数
156 : snz = snz SPN_u tok SPNE;
157 : break;
158 : case /i/: #その他の識別子(変数)
159 : snz = snz SPN_i tok SPNE;
160 : break;
161 : case /n/: #数値
162 : snz = snz SPN_n tok SPNE;
163 : break;
164 : case /o/: #演算子
165 : snz = snz SPN_o tok SPNE;
166 : break;
167 : case /p/: #句読記号
168 : snz = snz tok;
169 : break;
170 : case /l/: #文字列リテラル
171 : snz = snz SPN_l tok SPNE;
172 : break;
173 : case /r/: #正規表現定数(AWK)
174 : snz = snz SPN_r tok SPNE;
175 : break;
176 : case /c/: #コメント
177 : snz = snz SPN_c tok SPNE;
178 : break;
179 : case /w/: #空白(TABはスペース変換)
180 : snz = snz tok;
181 : break;
182 : case /e/: #空行
183 : snz = snz tok;
184 : break;
185 : case /q/: #シングルクォート(文字 C)
186 : snz = snz SPN_q tok SPNE;
187 : break;
188 : case /g/: #ラベル(goto他 C/VBA/BAT)
189 : snz = snz SPN_g tok SPNE;
190 : break;
191 : case /d/: #defineマクロ(解析中のCソースに実装されたマクロのみ)
192 : snz = snz SPN_d tok SPNE;
193 : break;
194 : case /a/: #プリプロセッサディレクティブ(C,AWK)
195 : snz = snz SPN_a tok SPNE;
196 : break;
197 : default:
198 : print "ERROR_lex"; #lex res[ct][0]="" ...で発生
199 : break; #...ct<1 等存在しないctにアクセスしている
200 : } # res[ct-1][0] 等を疑う
201 : }
202 : return snz;
203 : }
_lex_init()
204 : #. _lex_init():各言語の区切り文字と演算子
205 : # 戻値:
206 : # lng:in: awk/vb/cpp/bat のいずれか
207 : function _lex_init(lng, ct, ar, i, prev, op1, op2) {
208 : if (lng ~ /awk/) {
209 : prev["awk"] = "(.,.~.!~.==.!=.first.case.@.=.!.||.&&";
210 : op1["awk"] = "+,-,*,/,^,%,!,~,=,<,>,?,@,|";
211 : op2["awk"] = "++,--,+=,-=,*=,/=,%=,^=,!~,==,!=,<=,>=,&&,||,::";
212 : }
213 : else if (lng ~ /bat/) {
214 : prev["bat"] = "(.@.|.>.<.>>.<<.first";
215 : op1["bat"] = "+,-,*,/,^,%,!,~,=,<,>,&,|";
216 : op2["bat"] = ">>,<<,%%,+=,-=,==";
217 : }
218 : else if (lng ~ /cpp/) {
219 : prev["cpp"] = "(.,.~.!~.==.!=.first.@";
220 : op1["cpp"] = "+,-,*,/,^,%,!,~,=,<,>,?,&";
221 : op2["cpp"] = "++,--,+=,-=,*=,/=,%=,^=,!~,==,!=,<=,>=,\
222 : &&,||,::,->,<<,>>,&=";
223 : }
224 : else {
225 : prev["vb"] = "(.,.~.!~.==.!=.first.@";
226 : op1["vb"] = "+,-,*,/,^,\\,=,<,>"; #Mod 文字列で
227 : op2["vb"] = "<>,<=,>=";
228 : }
229 :
230 : ct = split(prev[lng], ar, ".");
231 : for (i = 1; i <= ct; i++) _prev[ar[i]] = i;
232 :
233 : ct = split(op1[lng], ar, ",");
234 : for (i = 1; i <= ct; i++) _op1[ar[i]] = i;
235 :
236 : ct = split(op2[lng], ar, ",");
237 : for (i = 1; i <= ct; i++) _op2[ar[i]] = i;
238 :
239 : _er[0]; delete _er[0]; _erct = 0; #エラー用大域変数
240 : _cont_ch = ""; #大域変数 行継続によるリテラルか正規表現の持越し
241 : }
_kyw_init()
242 : #. _kyw_init():キーワード辞書
243 : # 戻値:
244 : # lng:in: awk/vb/cpp/bat のいずれか
245 : function _kyw_init(lng, ar, i, ct,\
246 : t_str, k_str, x_str, f_str, v_str, \
247 : o_str, a_z) {
248 : # lex 識別子 正規表現 /[a-zA-Z_]/ と /[a-zA-Z0-9_]/ の代替
249 : # 高速化のためのハッシュテーブル
250 : a_z = "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,\
251 : A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,_";
252 : ct = split(a_z, ar, ",");
253 : for (i = 1; i <= ct; i++) {
254 : _a_z[ar[i]];
255 : _a_9[ar[i]];
256 : }
257 : for (i = 0; i < 10; i++) _a_9[i];
258 : if (lng ~ /^vb$/) _a_9["$"];
259 :
260 : if (lng ~ /awk/) {
261 : #GAWK文制御 ※インデント禁止
262 : k_str["awk"] = "BEGIN,BEGINFILE,break,case,continue,\
263 : default,do,else,END,ENDFILE,exit,func,function,for,if,in,next,\
264 : nextfile,return,switch,while";
265 : #GAWK組み込み関数
266 : f_str["awk"] = "delete,index,patsplit,typeof,\
267 : length,substr,match,split,sub,gsub,gensub,sprintf,strtonum,\
268 : tolower,toupper,print,printf,getline,system,close,sin,cos,\
269 : atan2,exp,log,int,sqrt,srand,rand,strftime,systime,mktime,and,\
270 : or,xor,compl,lshift,rshift,fflush,dcgettext,dcngettext,\
271 : bindtextdomain,asort,asorti,isarray";
272 : #GAWK組み込み変数辞書
273 : v_str["awk"] = "FS,OFS,NF,RS,ORS,NR,FNR,\
274 : ARGV,ARGC,ARGIND,FILENAME,ENVIRON,ERRNO,OFMT,CONVFMT,\
275 : FIELDWIDTHS,IGNORECASE,RLENGTH,RSTART,SUBSEP,PROCINFO,\
276 : TEXTDOMAIN,SYMTAB,FUNCTAB,RT,FPAT";
277 : }
278 : else if (lng ~ /bat/) {
279 : #bat文制御他キーワード
280 : k_str["bat"] = "if,for,goto,call,exit,do,in";
281 : #batその他のキーワード
282 : x_str["bat"] = "set,setlocal,setver,setx,endlocal,pause,\
283 : exist,defined";
284 : #batコマンド
285 : f_str["bat"] = "adddrv,append,attrib,break,call,cd,chcp,chdir,\
286 : chev,chkdsk,choice,clip,cls,cmd,color,command,comp,copy,date,del,\
287 : deldrv,deltree,dir,diskcomp,diskcopy,doskey,echo,erase,\
288 : expand,fc,find,findstr,forfiles,format,hostname,icacls,ipconfig,\
289 : label,lh,loadhigh,md,mem,mkdir,mklink,mode,more,mountvol,\
290 : move,path,ping,popd,print,prompt,pushd,rd,recover,reg,rem,ren,\
291 : rename,replace,rmdir,robocopy,scandisk,shift,sort,start,\
292 : subst,switch,sys,systeminfo,tasklist,taskkill,time,timeout,\
293 : title,tree,type,tzutil,ver,verify,vol,where,whoami,xcopy";
294 : #bat組み込み変数/定数
295 : v_str["bat"] = "con,enabledelayedexpansion,errorlevel,\
296 : nul,prompt,pathext";
297 : #bat比較演算子
298 : o_str["bat"] = "equ,neq,lss,leq,gtr,geq,not";
299 : }
300 : else if (lng ~ /cpp/) {
301 : #cpp型等
302 : t_str["cpp"] = "auto,bool,char,const,double,enum,extern,\
303 : float,int,long,register,short,signed,sizeod,static,struct,\
304 : typedef,union,unsigned,void,volatile";
305 : #cpp文制御
306 : k_str["cpp"] = "break,case,continue,default,do,else,for,\
307 : goto,if,return,switch,while";
308 : #cpp標準関数
309 : f_str["cpp"] = "asctime,clock,ctime,difftime,gmtime,localtime,\
310 : mktime,time,calloc,free,malloc,realloc,rand,srand,clearerr,\
311 : fclose,feof,ferror,fflush,fgetc,fgetpos,fgets,fopen,fprintf,\
312 : fputc,fputs,fread,freopen,fscanf,fseek,fsetpos,fwrite,getc,\
313 : getchar,gets,perror,printf,putc,putchar,puts,remove,rename,\
314 : rewind,scanf,setbuf,setvbuf,sprintf,sscanf,memchr,memcmp,\
315 : memcpy,memmove,memset,strcat,strchr,strcmp,strcpy,strcspn,\
316 : strerror,strlen,strncat,strncmp,strncpy,strpbrk,strrchr,\
317 : strspn,strstr,strtok,isalnum,isalpha,iscntrl,isdigit,isgraph,\
318 : islower,isprint,ispunct,isspace,isupper,isxdigit,tolower,\
319 : toupper,acos,asin,atan,atan2,ceil,cos,cosh,exp,fabs,floor,\
320 : fmod,log,log10,pow,sin,sqrt,tan,abs,atof,atoi,atol,strtod,\
321 : strtol,strtoul,system,dlclose,dlerror,dlopen,dlsym,gethrtime,\
322 : gettimeofday,gettrvtime,settimeofday";
323 : #マクロその他辞書
324 : v_str["cpp"] = "__FILE__,__LINE__,__DATE__,\
325 : __TIME__,__STDC__,NULL";
326 : }
327 : else {
328 : #vb型
329 : t_str["vb"] = "Boolean,Byte,Const,Currency,Date,Double,Friend,\
330 : Integer,Long,Private,Public,Single,Static,String,Time,Variant";
331 : #vb文制御他
332 : k_str["vb"] = "Call,Case,Do,Each,Else,ElseIf,End,Exit,For,\
333 : Function,GoSub,GoTo,If,In,Loop,Next,Resume,Select,Step,Sub,Then,\
334 : To,Until,Wend,While,With"
335 : #vbその他のキーワード
336 : x_str["vb"] = "Access,Alias,Any,Append,As,Assert,\
337 : Base,Begin,Binary,ByRef,ByVal,Close,Compare,Debug,Declare,Dim,\
338 : Enum,Erase,Error,Event,Explicit,Get,Global,Implements,\
339 : Let,Lib,Line,Lock,LSet,New,Nothing,Null,Object,\
340 : On,Open,Option,Optional,Output,ParamArray,Preserve,Print,\
341 : Property,PSet,Put,Raiseevent,Random,Read,ReDim,RSet,Scale,Set,\
342 : Shared,Type,Unlock,WithEvents,Write";
343 : #vb組み込み関数
344 : f_str["vb"] = "Abs,Array,Asc,Ascb,Ascw,Atn,CBool,CByte,CCur,\
345 : CDate,CDbl,CDec,CInt,CLng,CSng,CStr,CVerr,CVar,CallByName,\
346 : Choose,Chr,Chr$,Chrb,Chrb$,Chrw,Command,Command$,Cos,\
347 : CreateObject,CurDir,CurDir$,DDB,Date,Date$,DateAdd,DateDiff,\
348 : DatePart,DateSerial,DateValue,Day,Dir,Dir$,DoEvents,EOF,\
349 : Environ,Error,Error$,Exp,FV,FileAttr,FileDateTime,FileLen,\
350 : Filter,Fix,Format,Format$,FormatCurrency,FormatNumber,\
351 : FormatPercent,FreeFile,GetAllSettings,GetAttr,GetObject,\
352 : GetSetting,Hex,Hex$,Hour,IIf,IMESstatus,IPmt,IRR,InStr,\
353 : InStrRev,Input,Input$,Inputb,Inputb$,InputBox,Int,IsArray,\
354 : IsDate,IsEmpty,IsError,IsMissing,IsNull,IsNumeric,IsObject,\
355 : Join,LBound,LCase,LCase$,LCaseb,LCaseb$,Lof,LTrim,LTrim$,\
356 : Left,Left$,Leftb,Leftb$,Len,LenB,Loc,Log,MIRR,MacId,MacScript,\
357 : Mid,Mid$,Midb,Midb$,Minute,Month,MonthName,MsgBox,NPV,NPer,\
358 : Now,Oct,Oct$,PPmt,PV,Partition,Pmt,QBColor,RGB,RTrim,RTrim$,\
359 : Rate,Replace,Right,Right$,Rightb,Rightb$,Rnd,Round,SLN,SYD,\
360 : Second,Seek,Sgn,Shell,Sin,Space,Space$,Spc,Split,Sqr,Str,\
361 : Str$,StrComp,StrConv,StrReverse,String,String$,Switch,Tab,Tan,\
362 : Time,Time$,TimeSerial,TimeValue,Timer,Trim,Trim$,TypeName,\
363 : UBound,UCase,Val,VarType,WeekDay,WeekDayName,Year";
364 : #vb組み込み変数/定数
365 : v_str["vb"] = "False,True,vbDataObject,vbMenuText,vbButtonText,\
366 : vbYellow,vbHighLight,vbOKCancel,vbInteger,vbCyan,vbString,vbAlias,\
367 : vbEmpty,vbYesNo,vbCr,vbMsgBoxRtlReading,vbHidden,vb3DLight,vbByte,\
368 : vbOK,vbMsgBoxHelpButton,vbScrollBars,vbReadOnly,vbInfoText,vbYes,\
369 : vbObject,vbInfoBackground,vbError,vbGrayText,vbInactiveBorder,\
370 : vbDecimal,vbInactiveCaptionText,vbKatakana,vbQuestion,vbYesNoCancel,\
371 : vbArchive,vbInactiveTitleBar,vbButtonShadow,vbFormFeed,vbSystem,\
372 : vbTitleBarText,vbHighLightText,vbArray,vbAbortRetryIgnore,vbCrLf,\
373 : vbNullChar,vbMagenta,vbDefaultButton1,vbDefaultButton2,\
374 : vbDefaultButton3,vbDefaultButton4,vbExclamation,vbBlack,vbOKOnly,\
375 : vbLong,vbActiveTitleBar,vbNarrow,vbWide,vbDirectory,vbGreen,\
376 : vbMsgBoxSetForeground,vb3DDKShadow,vbRed,vbUpperCase,vbIgnore,\
377 : vbRetry,vb3DHighLight,vbApplicationModal,vbVariant,vbFromUnicode,\
378 : vbBoolean,vbRetryCancel,vbUnicode,vbNull,vbApplicationWorkspace,\
379 : vbCritical,vbDate,vbNormal,vbNullString,vbButtonFace,vbAbort,\
380 : vbUserDefinedType,vbCancel,vbInformation,vbWindowText,vbSingle,\
381 : vbSystemModal,vbLf,vbLongLong,vbWindowBackground,vbVolume,\
382 : vbVerticalTab,vbBlue,vbProperCase,vbDouble,vbTab,vbHiragana,\
383 : vbMenuBar,vbNewLine,vbActiveBorder,vbNo,vbCurrency,vbBack,\
384 : vbMsgBoxRight,vbLowerCase,vbDesktop,vbWhite,vbWindowFrame";
385 : #vb論理演算子等
386 : o_str["vb"] = "Like,Not,And,Or,Eqv,Imp,Xor,AddressOf,\
387 : TypeOf,Is,Mod";
388 : }
389 :
390 : ct = split(t_str[lng], ar, ",");
391 : if (lng ~ /vb|bat/)
392 : for (i = 1; i <= ct; i++) _type[tolower(ar[i])] = ar[i];
393 : else
394 : for (i = 1; i <= ct; i++) _type[ar[i]];
395 :
396 : ct = split(k_str[lng], ar, ",");
397 : if (lng ~ /vb|bat/)
398 : for (i = 1; i <= ct; i++) _kyw[tolower(ar[i])] = ar[i];
399 : else
400 : for (i = 1; i <= ct; i++) _kyw[ar[i]];
401 :
402 : ct = split(f_str[lng], ar, ",");
403 : if (lng ~ /vb|bat/)
404 : for (i = 1; i <= ct; i++) _bltf[tolower(ar[i])] = ar[i];
405 : else
406 : for (i = 1; i <= ct; i++) _bltf[ar[i]];
407 :
408 : ct = split(v_str[lng], ar, ",");
409 : if (lng ~ /vb|bat/)
410 : for (i = 1; i <= ct; i++) _bltv[tolower(ar[i])] = ar[i];
411 : else
412 : for (i = 1; i <= ct; i++) _bltv[ar[i]];
413 :
414 : ct = split(x_str[lng], ar, ",");
415 : if (lng ~ /vb|bat/)
416 : for (i = 1; i <= ct; i++) _kext[tolower(ar[i])] = ar[i];
417 : else
418 : for (i = 1; i <= ct; i++) _kext[ar[i]];
419 :
420 : if (lng ~ /vb|bat/) { #VB bat系 論理演算子他
421 : ct = split(o_str[lng], ar, ",");
422 : for (i = 1; i <= ct; i++) _sope[tolower(ar[i])] = ar[i];
423 : }
424 :
425 : if (lng ~ /awk/) { #AWK 先越キーワード
426 : _apri["include"];
427 : _apri["load"];
428 : _apri["namespace"];
429 : }
430 :
431 : if (lng ~ /cpp/) delete _cdef; #C 動的マクロ名辞書
432 : }
lex_awk()
433 : #. lex_awk():レキシカルアナライザ(AWK Script)
434 : # 戻値: トークン数(resの要素数)
435 : # str:in: 1行のAWKソース
436 : # res:out: 2次元配列 res[x][0]=属性 res[x][1]=トークン(/"含む)
437 : # 行継続/次行にまたがるリテラル/正規表現に対応
438 : # [属性表]
439 : # a 先越キーワード (a prioli keyword -include,load,namespace)
440 : # k 文制御キーワード (keyword)
441 : # f 組み込み関数 (function of built-in)
442 : # v 組み込み変数/定数 (variable/const-value of built-in)
443 : # u ユーザー定義関数 (user-defined function)
444 : # i 他の識別子 (identifier of variable or array)
445 : # l 文字列リテラル (literal)
446 : # r 正規表現 (regular expression)
447 : # c コメント (comment)
448 : # n 数値 (numeric)
449 : # o 演算子等 (operator)
450 : # p カンマや括弧 (punctuation)
451 : # w 空白 (white space)
452 : # e 改行のみ (empty)
453 : function lex_awk(str, res, len, i, ib, ch,\
454 : once, fcon, prev, pos, ct, tmp, ret) {
455 : ct = i = 1;
456 : prev = "first"; #行頭デリミタ
457 : delete res; #初期化
458 :
459 : len = length(str);
460 : if (!len) { res[1][0] = "e"; res[1][1] = ""; return 1; } #空行
461 : if (substr(str, len) ~ /\\/) fcon = 1; #継続フラグ
462 :
463 : while (i <= len) {
464 : ch = substr(str, i, 1); #一文字切り出す
465 : ib = i; #ib 切り出し開始位置
466 :
467 : #複数行にまたがる正規表現/リテラル 前行引き継ぎ
468 : if (!once) #最初の1度だけ実行
469 : if (!_cont_ch) once = 1;
470 : else { once = 1; ch = _cont_ch; } #i=1 " or /
471 :
472 : if (ch in _a_z) ch = "z"; #正規表現/[a-zA-Z_]/の代替
473 :
474 : switch (ch) {
475 : case /[ \t]/: #空白 link tab2spaceしない \t
476 : while (tmp = substr(str, ++i, 1)) #空白まとめる
477 : if (tmp !~ /[ \t]/) break;
478 : ret = substr(str, ib, i - ib);
479 : res[ct][0] = "w";
480 : break;
481 : case /\$/: #フィールド変数
482 : while(tmp =substr(str,++i,1))
483 : if(tmp !~ /[0-9]/)break; #$0等はバラさない
484 : ret = prev = substr(str, ib, i - ib);
485 : res[ct][0] = "v";
486 : break;
487 : case /z/: #識別子
488 : while (tmp = substr(str, ++i, 1)) #次の文字から(++i)
489 : if (!(tmp in _a_9)) break; #/[a-zA-Z0-9_]/の代替
490 : ret = prev = substr(str, ib, i - ib);
491 :
492 : if (ret in _kyw) res[ct][0] = "k"; #文制御
493 : else if (ret in _bltf) res[ct][0] = "f"; #組み込み関数
494 : else if (ret in _bltv) res[ct][0] = "v"; #組み込み変数
495 : else if (ret in _apri) res[ct][0] = "a"; #先越キーワード
496 : else if (tmp ~ /\(/) res[ct][0] = "u"; #ユーザー定義関数(仮)
497 : else res[ct][0] = "i"; #変数
498 : break;
499 : case /[0-9]/: #数値 16/10/8進数/指数表記 (+-なし)
500 : if (ch ~ /0/ && substr(str, i + 1, 1) ~ /[xX]/) {
501 : i++; #x Hex
502 : while (tmp = substr(str, ++i, 1))
503 : if (tmp ~ /[^0-9a-fA-F]/) break;
504 : }
505 : else {
506 : while (tmp = substr(str, ++i, 1))
507 : if (tmp ~ /[^0-9\.]/) break;
508 : if (tmp !~ /[eE]/) ; #nop Oct/Dec
509 : else { #指数
510 : if (substr(str, ++i, 1) ~ /[\-+]/) i++; # i +1 or +2
511 : for (; i <= len; i++) # i=i init
512 : if (substr(str, i, 1) ~ /[^0-9]/) break;
513 : }
514 : }
515 : ret = prev = substr(str, ib, i - ib);
516 : res[ct][0] = "n";
517 : break;
518 : case /"/: #文字列リテラル
519 : pos = litendpos(str, i);
520 : if (pos) { i = ++pos; _cont_ch = ""; } #両端の””含む
521 : else if (fcon) { i = len; _cont_ch = "\""; } #継続
522 : else if (_cont_ch ~ /"/ && str ~ /^"/) {
523 : i = 2; #被継続 行頭" (once参照)
524 : _cont_ch = ""; #被継続解除
525 : }
526 : else { #エラー
527 : err(NR, i);
528 : i++;
529 : _cont_ch = "";
530 : }
531 : ret = substr(str, ib, i - ib);
532 : prev = ch;
533 : res[ct][0] = "l";
534 : break;
535 : case /\//: #正規表現または除算演算子
536 : if (prev in _prev) { #正規表現定数
537 : pos = regendpos(str, i);
538 : if (pos) { i = ++pos; _cont_ch = ""; }
539 : else if (fcon) { i = len; _cont_ch = "/"; }
540 : else if (_cont_ch ~ /\// && str ~ /^\//) {
541 : i = 2;
542 : _cont_ch = "";
543 : }
544 : else {
545 : err(NR, i);
546 : i++;
547 : _cont_ch = "";
548 : }
549 : ret = substr(str, ib, i - ib);
550 : prev = ch;
551 : res[ct][0] = "r";
552 : }
553 : else { #除算演算子
554 : tmp = substr(str, ++i, 1);
555 : ret = prev = ch;
556 : res[ct][0] = "o";
557 : if (tmp ~ /=/) { i++; ret = prev = "/="; }
558 : }
559 : break;
560 : case /#/: #コメント
561 : i = len + 1; #ループ強制終了
562 : ret = substr(str, ib); #残余文字列全て
563 : res[ct][0] = "c";
564 : break;
565 : default: #演算子/句読記号
566 : ret = prev = ch; #とりあえず記憶
567 : if (ch in _op1) res[ct][0] = "o"; #演算子
568 : else res[ct][0] = "p"; #句読記号
569 : tmp = substr(str, i, 2); #2文字演算子テスト
570 : i++;
571 : if (tmp in _op2) {
572 : ret = prev = tmp; #書き換え
573 : res[ct][0] = "o";
574 : i++;
575 : }
576 : break;
577 : }
578 : res[ct++][1] = ret; #トークン格納
579 : }
580 : return ct - 1; #切り出したトークン数
581 : }
lex_bat()
582 : #. lex_bat():バッチファイルのレキシカルアナライザ
583 : # 戻値: トークン数
584 : # str:in: 1行のソース
585 : # res:out: トークンの配列
586 : function lex_bat(str, res, len,\
587 : once, fcon, fgo, fcol, i, ib, ch, prev,\
588 : pos, ct, tmp, ret, low_ret) {
589 : ct = i = 1;
590 : prev = "first";
591 : delete res; #配列を初期化
592 : len = length(str);
593 : if (substr(str, len) ~ /\^/) fcon = 1; #行継続確認
594 : if (!len) { res[1][0] = "e"; res[1][1] = ""; return ct - 1; } #空行
595 :
596 : while (i <= len) {
597 : ch = substr(str, i, 1);
598 : ib = i;
599 : if (!once) #リテラル 前行よりデリミタ引き継ぎ
600 : if (_cont_ch == "") once = 1;
601 : else { once = 1; ch = _cont_ch; } #デリミタ設定(i不動)
602 :
603 : if (ch in _a_z) ch = "z";
604 :
605 : switch (ch) {
606 : case / /: #空白 tab2space()済み
607 : while (tmp = substr(str, ++i, 1))
608 : if (tmp !~ / /) break;
609 : ret = substr(str, ib, i - ib);
610 : res[ct][0] = "w";
611 : break;
612 : case /z/: #識別子
613 : while (tmp = substr(str, ++i, 1))
614 : if (!(tmp in _a_9)) break;
615 : ret = substr(str, ib, i - ib);
616 : low_ret = tolower(ret);
617 : if (low_ret ~ /^rem$/) { #コメント
618 : i = len + 1;
619 : ret = substr(str, ib);
620 : res[ct][0] = "c";
621 : break;
622 : }
623 : if (low_ret in _kyw) {
624 : res[ct][0] = "k";
625 : if (low_ret ~ /^goto$/) fgo = 1;
626 : }
627 : else if (fgo) { fgo = 0; res[ct][0] = "g"; }
628 : else if (fcol) { fcol = 0; res[ct][0] = "g"; }
629 : else if ((prev in _prev) && (low_ret in _bltf)) res[ct][0] = "f";
630 : else if (low_ret in _bltv) res[ct][0] = "v";
631 : else if (low_ret in _kext) res[ct][0] = "x";
632 : else if (low_ret in _sope) res[ct][0] = "o";
633 : else res[ct][0] = "i";
634 : break;
635 : case /[0-9]/: #数値
636 : if (ch ~ /0/ && substr(str, i + 1, 1) ~ /[xX]/) { #Hex
637 : for (i = i + 2; i < len + 1; i++)
638 : if (substr(str, i, 1) ~ /[^0-9a-fA-F]/) break;
639 : }
640 : else {
641 : for (++i; i < len + 1; i++)
642 : if ((ch = substr(str, i, 1)) ~ /[^0-9\.]/) break;
643 : if (ch !~ /[eE]/) ;
644 : else { #指数
645 : if (substr(str, ++i, 1) ~ /[\-+]/) i++;
646 : for (; i < len + 1; i++)
647 : if (substr(str, i, 1) ~ /[^0-9]/) break;
648 : }
649 : }
650 : ret = prev = substr(str, ib, i - ib);
651 : res[ct][0] = "n";
652 : break;
653 : case /"/: #文字列リテラル
654 : pos = litendpos(str, i);
655 : if (pos) { i = ++pos; _cont_ch = ""; } #両端の””含む
656 : #行継続 大域変数に書き込み 次行は最初からリテラルで開始
657 : else if (fcon) { i = len; _cont_ch = "\""; } #大域変数設定
658 : else if (_cont_ch ~ /"/ && str ~ /^"/) { i = 2; _cont_ch = ""; }
659 : else { err(NR, i); i++; _cont_ch = ""; } #エラー スルー
660 : ret = substr(str, ib, i - ib);
661 : prev = "\"";
662 : res[ct][0] = "l";
663 : break;
664 : default: #その他の演算子や区切り文字等
665 : ret = prev = ch;
666 : if (ch in _op1) res[ct][0] = "o"; #operator
667 : else res[ct][0] = "p"; #punctuation
668 : tmp = substr(str, i, 2);
669 : i++; #1つ進める
670 : if (tmp in _op2) { #2文字演算子?
671 : i++; #もう1つ進める
672 : ret = prev = tmp; #デリミタ書き換え
673 : res[ct][0] = "o";
674 : }
675 : else if (ch ~ /:/) {
676 : if (str ~ /^[ \t]*:/) fcol = 1; #call :
677 : }
678 : break;
679 : }
680 : res[ct++][1] = ret; #トークン格納
681 : }
682 : return ct - 1;
683 : }
lex_vb()
684 : #. lex_vb():VisualBasic6.0系言語のレキシカルアナライザ
685 : # 戻値: トークン数
686 : # str:in: 1行のソース
687 : # res:out: トークンの配列
688 : function lex_vb(str, res, len,\
689 : i, ib, ch, tct, c, ind,\
690 : pos, ct, tmp, ret, lret, fok, fgo, fmb) {
691 : ct = i = 1;
692 : delete res; #配列を初期化
693 : len = length(str);
694 : # if (substr(str, len) ~ /_/) fcon = 1; #行継続確認
695 : if (!len) { res[1][0] = "e"; res[1][1] = ""; return 1; }
696 :
697 : while (i <= len) {
698 : ch = substr(str, i, 1);
699 : ib = i; #切り取り開始位置保存
700 : if (ch in _a_z) ch = "z";
701 :
702 : switch (ch) {
703 : case / /: #空白 tab2space()済み まとめる
704 : while (tmp = substr(str, ++i, 1))
705 : if (tmp !~ / /) break;
706 : ret = substr(str, ib, i - ib);
707 : res[ct][0] = "w";
708 : break;
709 : case /z/: #識別子
710 : while (tmp = substr(str, ++i, 1))
711 : if (!(tmp in _a_9)) break;
712 : ret = substr(str, ib, i - ib);
713 : lret = tolower(ret);
714 : if (fmb) { fmb = 0; res[ct][0] = "m"; } #オブジェクトメンバ
715 : else if (tmp ~ /\(/) { #組み込み関数
716 : if (lret in _bltf) { res[ct][0] = "f"; ret = _bltf[lret]; }
717 : else res[ct][0] = "i"; #ユーザ関数/配列 一般識別子
718 : } #メソッド(分類としては組み込み関数)
719 : else if (lret in _bltf) { res[ct][0] = "f"; ret = _bltf[lret]; }
720 : else if (lret in _kext) { res[ct][0] = "x"; ret = _kext[lret]; }
721 : else if (lret in _type) { res[ct][0] = "t"; ret = _type[lret]; }
722 : else if (lret in _kyw) {
723 : res[ct][0] = "k";
724 : ret = _kyw[lret];
725 : if (lret ~ /^goto$|^gosub$|^resume$/) fgo = 1;
726 : }
727 : else if (fgo) { fgo = 0; res[ct][0] = "g"; } #ラベル着地
728 : else if (lret in _bltv) { res[ct][0] = "v"; ret = _bltv[lret]; }
729 : else if (lret in _sope) { res[ct][0] = "o"; ret = _sope[lret]; }
730 : else res[ct][0] = "i";
731 : break;
732 : case /[0-9]/: #数値
733 : for (++i; i <= len; i++)
734 : if ((tmp = substr(str, i, 1)) ~ /[^0-9\.]/) break;
735 : if (tmp !~ /[eE]/) ;
736 : else { #指数
737 : if (substr(str, ++i, 1) ~ /[\-+]/) i++;
738 : for (; i <= len; i++)
739 : if (substr(str, i, 1) ~ /[^0-9]/) break;
740 : }
741 : ret = substr(str, ib, i - ib);
742 : res[ct][0] = "n";
743 : break;
744 : case /&/:
745 : tmp = substr(str, ++i, 1);
746 : if (tmp ~ /[hHoO]/) {
747 : for (++i; i < len + 1; i++)
748 : if (substr(str, i, 1) ~ /[^0-9a-fA-F]/) break;
749 : res[ct][0] = "n";
750 : }
751 : else res[ct][0] = "o";
752 : ret = substr(str, ib, i - ib);
753 : break;
754 : case /"/: #文字列リテラル
755 : c = 0;
756 : fok = 0;
757 : for (++i; i <= len + 1; i++) { #i=len+1 行末超過で判定
758 : if (substr(str, i, 1) ~ /"/) c++;
759 : else {
760 : if (c % 2 == 1) { fok = 1; break; }
761 : else c = 0;
762 : }
763 : }
764 : if (!fok) err(NR, ib); #エラー
765 : ret = substr(str, ib, i - ib);
766 : res[ct][0] = "l";
767 : break;
768 : case /\047/: #コメント
769 : i = len + 1; #強制ループ解除
770 : ret = substr(str, ib); #残りの全文字列
771 : res[ct][0] = "c";
772 : break;
773 : case /\./: #ドット演算子
774 : i++;
775 : ret = ch;
776 : res[ct][0] = "o";
777 : fmb = 1;
778 : break;
779 : default: #その他の演算子や区切り文字等
780 : ret = ch;
781 : if (ch in _op1) res[ct][0] = "o"; #operator
782 : else res[ct][0] = "p"; #punctuation
783 : tmp = substr(str, i, 2);
784 : i++; #1つ進める
785 : if (tmp in _op2) { #2文字演算子?
786 : i++; #もう1つ進める
787 : ret = tmp; #書き換え
788 : res[ct][0] = "o";
789 : }
790 : else if (ch ~ /:/) { #goto着地ラベル(仮)
791 : c = 0;
792 : tct = ct - 1;
793 : while (tct) #前方句に戻って検索
794 : if (res[tct--][0] ~ /[^w]/) { #res[0][0]に注意
795 : c++;
796 : ind = tct + 1;
797 : }
798 : if (c == 1 && res[ind][0] ~ /i/)
799 : res[ind][0] = "g";
800 : }
801 : break;
802 : }
803 : res[ct++][1] = ret; #トークン格納
804 : }
805 : return ct - 1;
806 : }
lex_cpp()
807 : #. lex_cpp():c/cppのレキシカルアナライザ
808 : # 戻値: トークン数
809 : # str:in: 1行のソース
810 : # res:out: トークンの配列
811 : function lex_cpp(str, res, len, i, ib, ch, \
812 : ret, pos, ct, tct, c, ind, tmps, nch,\
813 : once, fcon, f_go, f_cdef, f_ppd) {
814 : ct = i = 1;
815 : delete res; #2次元配列を初期化
816 : len = length(str);
817 : if (!len) { res[1][0] = "e"; res[1][1] = ""; return 1; } #空行
818 : if (substr(str, len) ~ /\\/) fcon = 1; #行継続
819 :
820 : while (i <= len) {
821 : ch = substr(str, i, 1); #一文字切り出す
822 : ib = i; #ib 切り出し開始位置
823 : #複数行にまたがるリテラル 前行引き継ぎ
824 : if (!once) #whileで最初の1度だけ実行
825 : if (_cont_ch == "") once = 1; #once 自身を設定
826 : else { once = 1; ch = _cont_ch; } #i=1 ch=" or ch=/ で開始
827 : if (ch in _a_z) ch = "z"; #/[a-zA-Z_]/ 53pcs slow
828 :
829 : switch (ch) {
830 : case / /: #空白 tab2space()済み
831 : while (tmps = substr(str, ++i, 1))
832 : if (tmps !~ / /) break;
833 : ret = substr(str, ib, i - ib);
834 : res[ct][0] = "w";
835 : break;
836 : case /z/: #識別子
837 : while (tmps = substr(str, ++i, 1))
838 : if (!(tmps in _a_9)) break; #/[a-zA-Z0-9_]/ 63pcs
839 : ret = substr(str, ib, i - ib);
840 : if (f_ppd) { #プリプロセッサ
841 : f_ppd = 0;
842 : res[ct][0] = "a";
843 : if (ret ~ /^define$/) f_cdef = 1;
844 : }
845 : else if (ret in _kyw) { #文制御
846 : res[ct][0] = "k";
847 : if (ret ~ /^goto$/) f_go = 1;
848 : }
849 : else if (ret in _type) res[ct][0] = "t"; #型
850 : else if (ret in _bltf) res[ct][0] = "f"; #標準関数
851 : else if (ret in _bltv) res[ct][0] = "v"; #標準マクロ
852 : else if (f_go) { f_go = 0; res[ct][0] = "g"; } #gotoラベル
853 : else if (f_cdef) {
854 : f_cdef = 0;
855 : _cdef[ret]; #動的にdefineマクロ辞書生成
856 : res[ct][0] = "d";
857 : }
858 : else if (ret in _cdef) res[ct][0] = "d"; #defineマクロ
859 : else if (ret ~ /^sizeof$/) res[ct][0] = "o"; #sizeof
860 : else if (tmps ~ /\(/) res[ct][0] = "u"; #ユーザー定義関数
861 : else {
862 : if (ct != 1) #数値型修飾
863 : if (res[ct - 1][0] ~ /n/) res[ct][0] = "t";
864 : else res[ct][0] = "i";
865 : else res[ct][0] = "i"; #識別子(変数)
866 : }
867 : break;
868 : case /[0-9]/: #数値
869 : if (ch ~ /0/ && substr(str, i + 1, 1) ~ /[xX]/) {
870 : i++; #x Hex
871 : while (tmps = substr(str, ++i, 1))
872 : if (tmps ~ /[^0-9a-fA-F]/) break;
873 : }
874 : else {
875 : while (tmps = substr(str, ++i, 1))
876 : if (tmps ~ /[^0-9\.]/) break;
877 : if (tmps !~ /[eE]/) ; #nop Oct/Dec
878 : else { #指数
879 : if (substr(str, ++i, 1) ~ /[\-+]/) i++; # i +1 or +2
880 : for (; i <= len; i++) # i=i init
881 : if (substr(str, i, 1) ~ /[^0-9]/) break;
882 : }
883 : }
884 : ret = substr(str, ib, i - ib);
885 : res[ct][0] = "n";
886 : break;
887 : case /"/: #文字列リテラル
888 : pos = litendpos(str, i);
889 : if (pos) { i = ++pos; _cont_ch = ""; } #両端の””含む
890 : #行継続 大域変数に書き込み 次行は最初からリテラルで開始
891 : else if (fcon) { i = len; _cont_ch = "\""; } #大域変数設定
892 : else if (_cont_ch ~ /"/ && str ~ /^"/) {
893 : i = 2;
894 : _cont_ch = "";
895 : }
896 : else { err(NR, i); i++; _cont_ch = ""; } #エラー スルー
897 : ret = substr(str, ib, i - ib);
898 : res[ct][0] = "l";
899 : break;
900 : case /\047/: #シングルクォート(文字)
901 : res[ct][0] = "q";
902 : pos = quotendpos(str, i);
903 : if (pos) { i = ++pos; ret = substr(str, ib, i - ib); }
904 : else { err(NR, i); i++; }
905 : break;
906 : case /\//: #コメント/除算演算子
907 : if (_cont_ch ~ /\//) { #前行から継続
908 : if (pos = index(str, "*/")) {
909 : i = pos + 2;
910 : _cont_ch = ""; #継続終了
911 : }
912 : else i = len + 1; # _cont_ch = "/";
913 : res[ct][0] = "c";
914 : }
915 : else {
916 : nch = substr(str, ++i, 1);
917 : if (nch ~ /\//) { #C++スタイルコメント
918 : i = len + 1;
919 : res[ct][0] = "c";
920 : }
921 : else if (nch ~ /\*/) { #Cスタイルコメント
922 : tmps = substr(str, ++i);
923 : if (pos = index(tmps, "*/")) {
924 : i += pos + 1;
925 : _cont_ch = "";
926 : }
927 : else {
928 : i = len + 1;
929 : _cont_ch = "/"; #コメント次行継続
930 : }
931 : res[ct][0] = "c";
932 : }
933 : else {
934 : res[ct][0] = "o"; #除算演算子
935 : }
936 : }
937 : ret = substr(str, ib, i - ib);
938 : break;
939 : case /#/: #プリプロセッサ ディレクティブ
940 : i++;
941 : f_ppd = 1; #フラグ設定
942 : ret = ch;
943 : res[ct][0] = "a";
944 : break;
945 : default: #その他の演算子や区切り文字等
946 : ret = ch; #とりあえず記憶
947 : if (ch in _op1) res[ct][0] = "o";
948 : else res[ct][0] = "p";
949 : tmps = substr(str, i, 2); #テスト準備
950 : i++; #1つ進める
951 : if (tmps in _op2) { #2文字演算子テスト
952 : i++; #もう1つ進める
953 : ret = tmps; #書き換え
954 : res[ct][0] = "o";
955 : }
956 : else if (ch ~ /:/) { #goto着地ラベル(仮)
957 : c = 0;
958 : tct = ct - 1;
959 : while (tct) #前方句に戻って検索
960 : if (res[tct--][0] ~ /[^w]/) { #後置※!res[0][0]
961 : c++;
962 : ind = tct + 1;
963 : }
964 : if (c == 1 && res[ind][0] ~ /i/) #3項演算/defaultと区別
965 : res[ind][0] = "g";
966 : }
967 : break;
968 : }
969 : res[ct++][1] = ret; #トークン格納
970 : }
971 : return ct - 1; #切り出したトークン数
972 : }
quotendpos()
973 : #. quotendpos():右クォートの正確な位置を返す(C)
974 : # 戻値: 右クォート位置
975 : # str:in: 文字列
976 : # pos:in: 左クォート位置
977 : function quotendpos(str, pos, rpos, escape, ch) {
978 : rpos = escape = 0;
979 : while (ch = substr(str, ++pos, 1)) { #posの次文字から
980 : if (rpos) break;
981 : switch (ch) {
982 : case /\\/:
983 : (escape) ? escape = 0 : escape = 1; #偽 フラグ設定
984 : break;
985 : case /\047/: # ' AWKソース中では使えないので8進数
986 : (escape) ? escape = 0 : rpos = pos; #偽 終端
987 : break;
988 : default:
989 : (escape) ? escape = 0 : 0; #真 フラグ解除
990 : break;
991 : }
992 : }
993 : return rpos;
994 : }
litendpos()
995 : #. litendpos():右ダブルクォートの正確な位置を返す(C/AWK)
996 : # 戻値: 右ダブルクォート位置
997 : # str:in: 文字列
998 : # pos:in: 左ダブルクォート位置
999 : function litendpos(str, pos, rpos, escape, ch) {
1000 : rpos = escape = 0;
1001 : while (ch = substr(str, ++pos, 1)) { #posの次文字から
1002 : if (rpos) break;
1003 : switch (ch) {
1004 : case /\\/:
1005 : (escape) ? escape = 0 : escape = 1; #偽 フラグ設定
1006 : break;
1007 : case /"/:
1008 : (escape) ? escape = 0 : rpos = pos; #偽 終端
1009 : break;
1010 : default:
1011 : (escape) ? escape = 0 : 0; #真 フラグ解除
1012 : break;
1013 : }
1014 : }
1015 : return rpos;
1016 : }
regendpos()
1017 : #. regendpos():正規表現末尾の正確な位置を返す(AWK)
1018 : # 戻値: 正規表現終端位置(右スラッシュ位置)
1019 : # str:in: 文字列
1020 : # pos:in: 左スラッシュ位置
1021 : function regendpos(str, pos, rpos, escape, bracket, ch) {
1022 : rpos = escape = bracket = 0;
1023 : while (ch = substr(str, ++pos, 1)) { #posの次文字から
1024 : if (rpos) break;
1025 : switch (ch) {
1026 : case /\\/:
1027 : (escape) ? escape = 0 : escape = 1; #偽 Eフラグ設定
1028 : break;
1029 : case /\[/:
1030 : (escape) ? escape = 0 : bracket = 1; #偽 Bフラグ設定 [ 内 ]
1031 : break;
1032 : case /]/:
1033 : (escape) ? escape = 0 : bracket = 0; #偽 Bフラグ解除 [ ]外
1034 : break;
1035 : case /\//:
1036 : (escape || bracket) ? escape = 0 : rpos = pos; #両フラグ 0
1037 : break;
1038 : default:
1039 : (escape) ? escape = 0 : 0; #真 Eフラグ解除
1040 : break;
1041 : }
1042 : }
1043 : return rpos;
1044 : }
err()
1045 : #. err():リテラル/正規表現のエラー
1046 : # 戻値:
1047 : # line:in: エラー行
1048 : # pos:in: エラー桁(先頭からn番目の文字)※TABを1文字
1049 : function err(line, pos) {
1050 : _er[++_erct] = "Error line: " line " position: " pos;
1051 : }
tab2space()
1052 : #. tab2space():タブをスペースに置換する
1053 : # 戻値: 置換後文字列
1054 : # str:in: ターゲット文字列(行)
1055 : # tablen:in: タブ長さ
1056 : # ※temp bug回避のために用いている
1057 : function tab2space(str, tablen, tstr, pos, temp) {
1058 : do {
1059 : if (pos = index(str, "\t")) {
1060 : temp = tstr = tstr substr(str, 1, pos - 1);
1061 : tstr = tstr sprintf("%*s", tablen - blength(temp) % tablen, "");
1062 : str = substr(str, pos + 1); #既読部切り捨て
1063 : }
1064 : else tstr = tstr str;
1065 : } while (pos)
1066 : return tstr;
1067 : }
_asc_init()
1068 : #. _asc_init():ASCII+半角カナ辞書(Shift_JIS) _asc["ル"]
1069 : # 戻値:
1070 : function _asc_init( i, hk, ar, qt) {
1071 : for (i = 0; i < 128; i++) _asc[sprintf("%c", i)] = i;
1072 : hk = "。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙゚";
1073 : qt = split(hk, ar, "");
1074 : for (i = 1; i <= qt; i++) _asc[ar[i]] = 160 + i; #Shift_JIS
1075 : _SCLP = " "; #マルチバイト文字の断片を表す文字
1076 : }
blength()
1077 : #. blength():文字列長さ疑似バイトを返す(辞書_asc)
1078 : # 戻値: 文字列表示長さ
1079 : # str:in: 検査文字列
1080 : function blength(str, i, ch, lenb) {
1081 : while (ch = substr(str, ++i, 1))
1082 : (ch in _asc) ? lenb += 1 : lenb += 2;
1083 : return lenb + 0;
1084 : }
sect_div()
1085 : #. sect_div(): infoproc()の_sections[]からs_div[][]を作成
1086 : # 戻値:
1087 : # sect:in: _sections[] infoproc()が作成するglb配列
1088 : # fnr:in: 最終行番号
1089 : # s_div:out: s_div[i][0] 開始行 sdiv[i][1] 名前 s_div[i][2] 終了行
1090 : # infoproc()から得られるデータはセクション外の余白(空行)やコメントを
1091 : # 考慮していない sect_div()はこれを補完する
1092 : function sect_div(sect, fnr, s_div, csva, ct, i) {
1093 : ct = length(sect);
1094 : for (i = 1; i <= ct; i++) {
1095 : split(sect[i], csva, ",");
1096 : if (i == 1) {
1097 : s_div[1][0] = 1;
1098 : s_div[1][1] = csva[1];
1099 : _lastline = s_div[1][2] = csva[4];
1100 : }
1101 : else {
1102 : s_div[i][0] = _lastline + 1;
1103 : s_div[i][1] = csva[1];
1104 : _lastline = s_div[i][2] = csva[4];
1105 : }
1106 : }
1107 : s_div[ct][2] = fnr; #最後の関数(セクション)はEOFまで
1108 : }
infoproc()
1109 : #. infoproc():セクションと関数の位置(行)を取得
1110 : # 戻値:
1111 : # lexa:in: レキシカルアナライザが吐き出した配列
1112 : # lnr:in: 現在行
1113 : # sections:out: sections[i] = _csv
1114 : # ..._csv セクション/関数名, 宣言行, 開始行, 終了行
1115 : function infoproc(lexa, lnr, sections, ct, i, tok, ci) {
1116 : ct = length(lexa);
1117 : for (i = 1; i <= ct; i++) {
1118 : tok = lexa[i][1];
1119 : if (tok ~ /^BEGIN$|^BEGINFILE$|^END$|^ENDFILE$|^func$|^function$/) {
1120 : _fnamed = 1; #セクション/関数名がある
1121 : switch (tok) {
1122 : case /BEGIN|END/:
1123 : _csv = tok "," lnr;
1124 : break;
1125 : case /func/:
1126 : for (ci = i + 1; ci <= ct; ci++)
1127 : if (lexa[ci][0] !~ /w/) break;
1128 : _csv = lexa[ci][1] "()" "," lnr; #関数名
1129 : break;
1130 : default:
1131 : break;
1132 : }
1133 : }
1134 : else if (tok ~ /^{$/) { #section/function { 本文開始行
1135 : _cb++;
1136 : if (_cb == 1) { # 0 → 1
1137 : if (!_fnamed) #Actionに上から順に名前を付与
1138 : _csv = sprintf("ACTION_%02d,%d", ++_panum, lnr);
1139 : _csv = _csv "," lnr;
1140 : }
1141 : }
1142 : else if (tok ~ /^}$/) { #section/function } 終了行
1143 : _cb--;
1144 : if (!_cb) { #1 → 0
1145 : sections[++_sectct] = _csv "," lnr;
1146 : _fnamed = 0;
1147 : }
1148 : }
1149 : }
1150 : }
_infoproc_init()
1151 : #. _infoproc_init():glb変数チェックのために変数化してある
1152 : # 戻値:
1153 : function _infoproc_init() {
1154 : _sectct = 0;
1155 : _csv = "";
1156 : _cb = 0;
1157 : _fnamed = 0;
1158 : _panum = 0;
1159 : _lastline = 0; #sect_div()が使用;
1160 : }
上記 _infoproc_init()
は必要ないのですが、変数確認のために置いています。
処理時間考察(掲載時のデータ)
source | line | time(ms) | line/ms | tok | tok/ms | tok/line | file_size |
---|---|---|---|---|---|---|---|
source2html(all) | 1,127 | 241 | 4.68 | 11,479 | 47.64 | 10.19 | 36,057 |
source2html.awk | 1,127 | 158 | 7.14 | 11,479 | 72.66 | 10.19 | 36,057 |
gawkapi.c | 1,583 | 114 | 13.89 | 8,906 | 78.13 | 5.63 | 34,862 |
builtin.c | 4,185 | 363 | 11.53 | 29,441 | 81.11 | 7.04 | 98,699 |
vblexer.bas | 625 | 55 | 11.37 | 4,270 | 77.64 | 6.84 | 22,478 |
AWKソースが遅いように見えますが、「tok/ms」を見ると、70~80tok/msです。source2html.awk はちょっと特殊で、キーワード辞書(文字列定数)が多すぎて「tok/ms」を押し下げているのではないかと推測しています。試しに、キーワード辞書部分だけコメントアウトすると 13ms 短縮されました。