Sample Site

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言語の例

source2html_01.jpg 既存のC言語ソースファイルの必要な部分を選択し、コンテキストメニューから 「source2html」をクリック source2html_02.jpg InputBox 行番号表示は任意の番号から開始(デフォルト:1) 先頭行番号を入力してOKをクリック クリップボードにhtml化されたソース(box)がコピーされる 当ページに貼り付けると以下のようになる
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_03.jpg ドラッグ選択し、コンテキストメニューから「source2html」をクリック source2html_04.jpg InputBoxは「VBモード」となる VBモードでは、組み込み定数や関数等の名前をすべて小文字で記述していても、 キーワードはキャピタライゼーション的に正しく変換される (弊害:ステートメントキーワード等と同名な変数はNG 例 line → Line) 貼り付けると以下のようになる

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
  • ACTION_01
  • END
  • _html_tag_init()
  • trans_html()
  • _lex_init()
  • _kyw_init()
  • lex_awk()
  • lex_bat()
  • lex_vb()
  • lex_cpp()
  • quotendpos()
  • litendpos()
  • regendpos()
  • err()
  • tab2space()
  • _asc_init()
  • blength()
  • sect_div()
  • infoproc()
  • _infoproc_init()
  • 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 短縮されました。