HSP3のスクリプトを解析して(X)HTMLへ変換するスクリプト。これでバージョン1.0とします。
このスクリプト内で説明されていない変数などもありますが、変換の流れ程度はわかっていただけるかもしれません。
命令やラベルや数値などスペースを含まないものはstrmidで一気に解析してしまい、コメントや文字列などスペースを含むことができるものはpeekで1バイトずつ(オートマトンのように)変換しています。#uselib "kernel32.dll"
#cfunc global IsDBCSLeadByteEx "IsDBCSLeadByteEx" sptr, sptr
#const global CP_ACP 0
#const global EXPAND_SIZE 1024
#const global CR 0x0D
#const global LF 0x0A
#const global CRLF 0x0A0D
#enum STATE_NORMAL = 1
#enum STATE_COMMENT
#enum STATE_MULTILINE_COMMENT
#enum STATE_STRINGS
#enum STATE_MULTILINE_STRINGS
#module
#deffunc setConvertMode int i
convertMode = i
return
#deffunc addString str s, local l
l = strlen( s )
if ( rTextSize@ <= rTextPos@ + l ) {
// バッファをオーバーしてしまうので、拡張する
rTextSize@ += EXPAND_SIZE
memExpand rText@, rTextSize@
}
poke rText@, rTextPos@, s
rTextPos@ += l
return
// 単独タグ
#defcfunc _aloneTag str tagName, str className
if ( convertMode == REPLACEMODE_XHTML ) {
if ( className == "" ) {
return "<" + tagName + " />"
} else {
return "<" + tagName + " class=\"" + className + "\" />"
}
} else {
if ( className == "" ) {
return "<" + tagName + ">"
} else {
return "<" + tagName + " class=\"" + className + "\">"
}
}
// 終了タグ
#defcfunc endTag str tagName
return "</" + tagName + ">"
// 開始タグ
#defcfunc _startTag str tagName, str className, str idName, local s
if ( idName == "" ) {
s = ""
} else {
s = " id=\"" + idName + "\""
}
if ( className == "" ) {
return "<" + tagName + s + ">"
} else {
return "<" + tagName + " class=\"" + className + "\"" + s + ">"
}
// 文字がスペースかどうか(キーワードとして利用できない文字かどうか)調べる
#defcfunc is_space int iTarget, local iResult
if IsDBCSLeadByteEx( CP_ACP, iTarget ) : return 0
if ( '0' <= iTarget ) & ( iTarget <= '9' ) : return 0
if ( 'a' <= iTarget ) & ( iTarget <= 'z' ) : return 0
if ( 'A' <= iTarget ) & ( iTarget <= 'Z' ) : return 0
if ( iTarget == '_' ) : return 0
return 1
// 文字が数字かどうか調べる
#defcfunc is_number int iTarget
return ( '0' <= iTarget ) & ( iTarget <= '9' )
#global
#define ctype aloneTag( %1, %2 = "" ) _aloneTag( %1, %2 )
#define ctype startTag( %1, %2 = "", %3 = "" ) _startTag( %1, %2, %3 )
*convertText
setConvertMode replaceMode
gosub *loadDictionary // 変換用データベースの準備
gosub *beforeConvert // 変換の準備
gosub *convert // 変換の実行
gosub *afterConvert // 変換の後処理
gosub *deleteDictionary // データベースの削除
return
// 変換の準備
*beforeConvert
textSize = strlen( text )
rTextSize = textSize
sdim rText, rTextSize // ReplacedText
if ( sandwichMode == 1 ) & ( sandwichTag != "" ) {
if ( addIdMode ) {
// コマンドライン(ファイル名が格納されているはず)から
// ファイル名を取り出してidとする
rText = startTag( sandwichTag, sandwichClass, getpath( dir_cmdline, 8 ) )
} else {
rText = startTag( sandwichTag, sandwichClass )
}
}
rTextPos = strlen( rText )
x = 0
firstCharOfArea = 1 // ラベルの判定に使用
state = STATE_NORMAL
return
// 変換の実行
*convert
repeat
textPos = cnt
c = peek( text, textPos )
switch c
case 0
break
swbreak
case CR : case LF
// 改行
gosub *startNewLine
if ( strmid( text, textPos, 2 ) == "\n" ) {
continue textPos + 2
}
swbreak
case '/'
if ( strmid( text, textPos, 2 ) == "/*" ) & ( state == STATE_NORMAL ) {
// 複数行コメント開始
gosub *startMultiLineComment
addString "/*" : x += 2
continue textPos + 2
}
if ( strmid( text, textPos, 2 ) == "//" ) & ( state == STATE_NORMAL ) {
// 単一行コメント開始
gosub *startSingleLineComment
addString "//" : x += 2
continue textPos + 2
}
addString "/" : x++
swbreak
case '*'
if ( firstCharOfArea ) & ( state == STATE_NORMAL ) {
gosub *firstCharFinished
gosub *addLabel
continue textPos
}
if ( strmid( text, textPos, 2 ) == "*/" ) & ( state == STATE_MULTILINE_COMMENT ) {
// 複数行コメントの終了
addString "*/" : x += 2
gosub *endMultiLineComment
continue textPos + 2
}
addString "*" : x++
swbreak
case ';'
if ( state == STATE_NORMAL ) {
// 単一行コメント開始
gosub *startSingleLineComment
}
addString ";" : x++
swbreak
case '{'
if ( strmid( text, textPos, 2 ) == "{\"" ) & ( state == STATE_NORMAL ) {
// 複数行文字列の開始
gosub *startMultiLineStrings
addString "{\"" : x += 2
continue textPos + 2
}
addString "{" : x++
swbreak
case '"'
if ( state == STATE_NORMAL ) {
// 単一行文字列開始
gosub *startSingleLineStrings
addString "\"" : x++
continue
}
if ( state == STATE_STRINGS ) {
// 単一行文字列終了
addString "\"" : x++
gosub *endSingleLineStrings
continue
}
if ( strmid( text, textPos, 2 ) == "\"}" ) & ( state == STATE_MULTILINE_STRINGS ) {
addString "\"}"
gosub *endMultiLineStrings
continue textPos + 2
}
addString "\"" : x++
swbreak
case '\t'
if ( replaceTabs ) & ( state != STATE_STRINGS ) & ( state != STATE_MULTILINE_STRINGS ) {
// タブをスペースへ変換する
if replaceSpaces {
s = " "
} else {
s = " "
}
repeat tabWidth - ( x \ tabWidth )
addString s : x++
loop
} else {
addString "\t" : x += tabWidth - ( x \ tabWidth )
}
swbreak
case ' '
if replaceSpaces {
// 半角スペースを変換(文字列中・コメント中でも実行)
addString " "
} else {
addString " "
}
x++
swbreak
case ':'
// 文の区切れ
addString ":" : x++
if ( state == STATE_NORMAL ) : gosub *endLine
swbreak
case ',' : case '='
// パラメータの区切れ
addString strf( "%c", c ) : x++
if ( state == STATE_NORMAL ) : gosub *endArea
swbreak
case '\\'
if ( state == STATE_STRINGS ) | ( state == STATE_MULTILINE_STRINGS ) {
if ( strmid( text, textPos, 2 ) == "\\\\" ) | ( strmid( text, textPos, 2 ) == "\\\"" ) {
addString strmid( text, textPos, 2 ) : x += 2
continue textPos + 2
}
}
addString "\\" : x++
swbreak
case '&' : addString "&" : x++ : swbreak
case '<' : addString "<" : x++ : swbreak
case '>' : addString ">" : x++ : swbreak
default
// 命令・関数などキーワードの可能性
if IsDBCSLeadByteEx( CP_ACP, c ) {
// 2バイト文字の場合
// 現在のバージョンでは変数をマークアップしないので、
// このように扱ってもOK。
addString strmid( text, textPos, 2 ) : x += 2
continue textPos + 2
}
// 1バイト文字の場合
if ( state == STATE_NORMAL ) {
// 文字列中やコメント中でなければ、数値や命令・関数としてマークアップできるかどうか調べる
if ( is_number( c ) ) | ( c == '%' ) | ( c == '$' ) {
// 数値の挿入
gosub *addNumber
continue textPos
}
if ( is_space( c ) == 0 ) | ( c == '#' ) {
// 命令・プリプロセッサ・関数etc.の挿入
gosub *addWord
continue textPos
}
}
// 命令などを構成する文字列ではなかった場合(@,|,(,)など)
gosub *firstCharFinished
addString strf( "%c", c ) : x++
swbreak
swend
loop
return
// *****************************************************************
// 変換終了後処理
*afterConvert
if ( sandwichMode == 1 ) & ( sandwichTag != "" ) {
addString endTag( sandwichTag )
}
addString "\n"
text = rText
sdim rText, 4
return
// 変数の処理
*firstCharFinished
// 空白でない文字を取り出した場合
firstCharOfArea = 0
return
*endLine
// (マルチステートメントを含む論理的な)行の終了に伴う変数の変化
*endArea
// パラメータの終了に伴う変数の変化
firstCharOfArea = 1
return
// *****************************************************************
// 単行コメントの開始
*startSingleLineComment
state = STATE_COMMENT
if markupComments {
addString startTag( "span", CLASSNAME_COMMENT )
}
return
// コメント行を終了
*endSingleLineComment
state = STATE_NORMAL
if ( markupComments ) {
addString endTag( "span" )
}
return
// 複数行コメントの開始
*startMultiLineComment
state = STATE_MULTILINE_COMMENT
if ( markupComments ) {
addString startTag( "span", CLASSNAME_COMMENT )
}
return
// 複数行コメントの終了
*endMultiLineComment
state = STATE_NORMAL
if ( markupComments ) {
addString endTag( "span" )
}
return
// 単行文字列の開始
*startSingleLineStrings
state = STATE_STRINGS
if ( markupStrings ) {
addString startTag( "span", CLASSNAME_STRING )
}
return
// 単行文字列の終了
*endSingleLineStrings
state = STATE_NORMAL
if ( markupStrings ) {
addString endTag( "span" )
}
return
// 複数行文字列の開始
*startMultiLineStrings
state = STATE_MULTILINE_STRINGS
if ( markupStrings ) {
addString startTag( "span", CLASSNAME_STRING )
}
return
// 複数行文字列の終了
*endMultiLineStrings
state = STATE_NORMAL
if ( markupStrings ) {
addString endTag( "span" )
}
return
// *****************************************************************
// textPosの位置以降をラベルとして挿入
*addLabel
lLabel = textSize - textPos
repeat textSize - textPos - 1, textPos + 1
if ( textSize <= cnt ) {
lLabel = textSize - textPos
break
}
j = peek( text, cnt )
if ( IsDBCSLeadByteEx( CP_ACP, j ) ) : continue cnt + 2
if ( cnt == textPos + 1 ) & ( is_number( j ) ) {
lLabel = cnt - textPos
break
}
if is_space( j ) {
lLabel = cnt - textPos
break
}
loop
if ( markupLabels ) & ( 1 < lLabel ) {
addString startTag( "span", CLASSNAME_LABEL )
}
addString strmid( text, textPos, lLabel )
textPos += lLabel : x += lLabel
if ( markupLabels ) & ( 1 < lLabel ) {
addString endTag( "span" )
}
return
// textPosの位置以降を数字として挿入
*addNumber
if ( markupNumbers ) {
addString startTag( "span", CLASSNAME_NUMBER )
}
repeat -1, 1
j = peek( text, textPos + cnt )
if is_number( j ) | ( j == '.' ) {
continue
}
if ( cnt == 1 ) : if ( peek( text, textPos ) == '0' ) & (( j == 'x' ) | ( j == 'b' )) {
continue
}
if ( j == 'e' ) | ( j == 'E' ) {
if ( textPos + cnt + 1 < textSize ) {
n = peek( text, textPos + cnt + 1 )
if ( n == '+' ) | ( n == '-' ) : continue cnt + 2
}
continue
}
lNumber = cnt
break
loop
addString strmid( text, textPos, lNumber )
textPos += lNumber : x += lNumber
if ( markupNumbers ) {
addString endTag( "span" )
}
return
// 命令・関数・システム変数など
*addWord
repeat -1, 1
if textPos + cnt >= textSize {
dcKey = strmid( text, textPos, cnt )
break
}
if is_space( peek( text, textPos + cnt ) ) {
dcKey = strmid( text, textPos, cnt )
break
}
loop
dc -> "Exists" dcKey
if ( vret != 0 ) {
// 辞書に載っている文字列は適切なタグで囲む
dcVal = dc( "Item", dcKey )
if ( dcVal == DCVAL_COMMAND ) & ( markupCommands == 1 ) {
addString startTag( "span", CLASSNAME_COMMAND )
}
if ( dcVal == DCVAL_FUNCTION ) & ( markupFunctions == 1 ) {
addString startTag( "span", CLASSNAME_FUNCTION )
}
if ( dcVal == DCVAL_SYSVAL ) & ( markupSysvals == 1 ) {
addString startTag( "span", CLASSNAME_SYSVAL )
}
if ( dcVal == DCVAL_PREPROCESSOR ) & ( markupPreprocessors == 1 ) {
addString startTag( "span", CLASSNAME_PREPROCESSOR )
}
if ( dcVal == DCVAL_MACRO ) & ( markupMacros == 1 ) {
addString startTag( "span", CLASSNAME_MACRO )
}
addString dcKey
if (( dcVal == DCVAL_COMMAND ) & ( markupCommands == 1 )) | (( dcVal == DCVAL_FUNCTION ) & ( markupFunctions == 1 )) {
addString endTag( "span" )
}
if (( dcVal == DCVAL_SYSVAL ) & ( markupSysvals == 1 )) | (( dcVal == DCVAL_PREPROCESSOR ) & ( markupPreprocessors == 1 )) {
addString endTag( "span" )
}
if (( dcVal == DCVAL_MACRO ) & ( markupMacros == 1 )) {
addString endTag( "span" )
}
} else {
// 辞書に載っていない文字列はそのまま
addString dcKey
}
x += strlen( dcKey )
gosub *firstCharFinished
if ( dcVal == DCVAL_COMMAND ) : gosub *endArea
textPos += strlen( dcKey )
return
// *****************************************************************
// 新しい行の開始(マルチステートメントを除く、物理的に新しい行)
*startNewLine
if ( state == STATE_COMMENT ) : gosub *endSingleLineComment
; if ( state == STATE_STRINGS ) : state == STATE_NORMAL // 本来は必要ないはず
if addBrTags {
addString aloneTag( "br" )
}
addString "\n" : x = 0
gosub *endLine
return
// *****************************************************************
// 辞書の読み込み
*loadDictionary
newcom dc, "Scripting.Dictionary"
comres vret
dc( "compareMode" ) = 1 // 大文字小文字を区別しない
dcKey = 0
dcList = DCFILE_FUNCTIONS, DCFILE_SYSVALS, DCFILE_COMMANDS, DCFILE_PREPROCESSORS, DCFILE_MACROS
dcVal = DCVAL_FUNCTION, DCVAL_SYSVAL, DCVAL_COMMAND, DCVAL_PREPROCESSOR, DCVAL_MACRO
chdir dir_source + "/" + SAVE_FOLDER
notesel note
repeat length( dcList )
exist dcList( cnt )
if ( strsize < 0 ) : continue
noteload dcList( cnt )
_cnt = cnt
repeat notemax
noteget s, cnt
if ( s != "" ) {
dc -> "Add" s, dcVal( _cnt )
}
loop
loop
sdim note, 4
return
// 辞書の削除
*deleteDictionary
delcom dc
return
ラベルや変数をローワーキャメルケースで書いてきたのですが、やはりHSPユーザはアンダースコア区切りが多いようなのでそちらに変えようかな、と思っています。
2007年7月28日土曜日
(X)HTMLコンバータ (7) スクリプトの変換
2007年7月27日金曜日
円柱のHSV色空間
#include "d3m.hsp"
#include "hspda.as"
#const H_MAX 12
#const S_MAX 4
#const V_MAX 7
#const PANEL_MAX H_MAX * S_MAX * V_MAX
#const PANEL_SIZE 150 // パネル一辺の長さ
#const PANEL_DIST 200 // 同じ角度上のパネル同士の距離(左下の座標の間)
#const OBSERVER_R 2000 // 観測者の中心からの距離
#const double PI 3.1415
#const double ANG_SPEED 0.04 // 観測者の角速度
ct = 0.0
d3setcam OBSERVER_R, 0, V_MAX * 100, 0, 0, V_MAX * 100
*init
dim panel_h, PANEL_MAX : dim panel_s, PANEL_MAX : dim panel_v, PANEL_MAX // 色(HSV指定)
dim panel_r, PANEL_MAX : ddim panel_t, PANEL_MAX : dim panel_z, PANEL_MAX // パネルの位置 円柱座標系(パネルの左下の座標)
ddim panel_far, PANEL_MAX // 奥行き
repeat H_MAX : cnt1 = cnt
h = 191 * cnt / H_MAX // HSV の H
t = PI * 2.0 * cnt / H_MAX // 角度
repeat S_MAX : cnt2 = cnt
s = 256 * cnt / S_MAX // HSV の S
r = cnt * PANEL_DIST + PANEL_DIST / 2 // 中心からの距離
repeat V_MAX
v = 256 * cnt / V_MAX // HSV の V
i = cnt1 * S_MAX * V_MAX + cnt2 * V_MAX + cnt
panel_h( i ) = h : panel_s( i ) = s : panel_v( i ) = v
panel_r( i ) = r : panel_t( i ) = t : panel_z( i ) = cnt * PANEL_DIST
loop
loop
loop
observer_t = 0.0
*main
gosub *calc
gosub *draw
wait 2
goto *main
*calc
// 観測者の回転
observer_t += ANG_SPEED
if ( PI * 2.0 < observer_t ) : observer_t -= PI * 2.0
d3setlocal 0,0,0, cos( observer_t ),sin( observer_t ),0, -sin( observer_t ),cos( observer_t ),0, 0,0,1
// 奥行きを計算。遠くにあるものほどpanel_farは小さくなる
repeat PANEL_MAX
panel_far( cnt ) = cos( panel_t( cnt ) - observer_t ) * panel_r( cnt )
loop
// 小さい順(昇順)に並べ替え
sortval panel_far, 0
return
*draw
redraw 0
hsvcolor 0, 0, ( sin( observer_t ) + 1.0 ) * 128 : boxf
repeat PANEL_MAX
sortget i, cnt // 奥から数えてcnt番目のパネルのインデックス
x = cos( panel_t( i ) ) * panel_r( i ), cos( panel_t( i ) ) * ( panel_r( i ) + PANEL_SIZE ), cos( panel_t( i ) ) * ( panel_r( i ) + PANEL_SIZE ), cos( panel_t( i ) ) * panel_r( i )
y = sin( panel_t( i ) ) * panel_r( i ), sin( panel_t( i ) ) * ( panel_r( i ) + PANEL_SIZE ), sin( panel_t( i ) ) * ( panel_r( i ) + PANEL_SIZE ), sin( panel_t( i ) ) * panel_r( i )
z = panel_z( i ) + PANEL_SIZE, panel_z( i ) + PANEL_SIZE, panel_z( i ), panel_z( i )
hsvcolor panel_h( i ), panel_s( i ), panel_v( i )
d3square x, y, z
loop
redraw 1
return
2007年7月25日水曜日
(X)HTMLコンバータ (6) hsファイルから命令や関数等の一覧を作成
「hsファイルから関数一覧を作成」と同じ考え方ですが、HHX付属のモジュールを利用することで格段に効率UPしました。#include "../hsphelp/src/hhx_db.hsp"
#define HELP_DIR "hsphelp"
// ヘルプディレクトリの存在を確認
chdir dir_exe
dirlist s, HELP_DIR, 5
if stat == 0 : dialog "ヘルプディレクトリが見つかりません", 1 : end
chdir HELP_DIR
// データベースのロード
mes "loading..."
HHX_init_load_db
if HHX_currentset_sum() ! HHX_diskset_sum() {
mes "rebuilding db..."
HHX_init_rebuild_db DBR_WRITEDB
} else {
HHX_init_extract_db
}
// コンバータ用デーベースの作成
mes "making DB for (X)HTML-converter..."
db_num = HHX_select_all()
repeat db_num
c = HHX_get_next()
db_name = hhxdata( c, C_NAME )
db_group = hhxdata( c, C_GROUP )
db_prm = hhxdata( c, C_PRM )
if instr( db_group, 0, "プリプロセッサ命令" ) >= 0 {
; プリプロセッサ
notesel list_prepro
} else : if instr( db_group, 0, "システム変数" ) >= 0 {
; システム変数
notesel list_sysval
} else : if instr( db_group, 0, "マクロ" ) >= 0 {
; マクロ
notesel list_macro
} else : if db_prm = "" {
; 引数なし(命令)
notesel list_command
} else : if peek( db_prm ) = '(' {
; 関数型
notesel list_func
} else {
; 命令型
notesel list_command
}
noteadd db_name
loop
mes "finish."
2007年7月23日月曜日
(X)HTMLコンバータ (5) 複数行文字列を取得
{"で始まり"}で終わる複数行文字列を切りだして表示するスクリプト。
変換はだいたいこんな感じで行っています。
このスクリプトの場合コメント内文字列にも律儀に反応してしまいますが、コンバータではそうはなりません。
instr後にちゃっちゃと切りだしてしてしまえば良さそうなものですが、そうしていない理由は次のようなスクリプトに対応するためです。mes {"
この行で終わってはいけない\"}
この行で終わるべき\\"}
mes {"2バイト目が'\\'の2バイト文字には
反応しない→ソ"}
これに対して、複数行コメントの場合はこうした心配は必要ありません。// 複数行文字列を切りだすスクリプト
dialog "hsp;*.as;*.hss;*.hsh", 16
if stat == 0 : end
notesel buf
noteload refstr
bufSize = notesize
repeat bufSize
bufPos = cnt
c = peek( buf, bufPos )
if ( c == '{' ) & ( bufPos + 1 < bufSize ) {
if ( peek( buf, bufPos + 1 ) == '"' ) {
k = 0
repeat
ins = instr( buf, bufPos + k, "\"}" )
if ( ins < 0 ) {
k = bufSize - bufPos
break
}
repeat
j = peek( buf, bufPos + k + ins - cnt - 1 )
if j == '\\' : continue
if (( 129 <= j ) & ( j <= 159 )) | (( 224 <= j ) & ( j <= 252 )) {
j = cnt - 1
if ( j < 0 ) : j = 0
break
}
j = cnt
break
loop
k += ins + 2 // 2 = strlen( "\"}" )
if ( j \ 2 ) {
// まだ続く
continue
}
break
loop
s = strmid( buf, bufPos, k )
mes s
continue bufPos + k
}
}
loop
stop
2007年7月21日土曜日
(X)HTMLコンバータ (4) 編集中のスクリプトを取得
編集中のスクリプトを取得するスクリプト。HSPスクリプトエディタ起動時に実行してみてください。エラー処理は適当です。
流れとしては
- タブコントロールにTCM_GETCURSELを送り現在選択されているタブのインデックスを取得
- タブのインデックスをFootyのハンドルに変換
- hsed_gettextを実行
なお、実行には別途HSPSDKのhsedsdk.asが必要です。HSPSDKはONION software/おにたま氏の著作物であり、ONION softwareウェブサイトから入手できます。
#include "hsedsdk.as"
#module "hsedsdk_getScript"
#define TCM_GETCURSEL 0x130B
#deffunc hsed_getScript var result, local hwndTab, local iTab, local footyId
hsed_capture
if stat : return 1 // エディタが起動していない
hsed_getwnd hwndTab, HGW_TAB
sendmsg hwndTab, TCM_GETCURSEL, 0, 0
if ( stat == -1 ) : return 2
iTab = stat // 何番目のタブが選択されているか
// タブのインデックスからfootyIdを取得し、スクリプトを変数にコピー
hsed_getfootyid footyId, iTab
hsed_gettext result, footyId
return stat
#global
*getText
// エディタからスクリプトを取得
text = ""
hsed_getScript text
if ( stat == 1 ) {
dialog "HSPエディタが見つかりません。", 1, PROGRAM_TITLE
end
}
if ( stat == 2 ) {
dialog "HSPエディタから正しいウィンドウハンドルを取得できませんでした。", 1, PROGRAM_TITLE
end
}
if ( stat == 3 ) {
dialog "パイプハンドルを作成できませんでした。", 1, PROGRAM_TITLE
end
}
mesbox text, ginfo_winx, ginfo_winy
stop※この例を見れば分かるように、textなど「標準命令ではない命令や関数と同じ名前を付けた変数」は命令や関数として認識されてしまいます。この問題は仕様としてスクリプト記述時に気をつけていただこうと思っています。
追記:HSPSDK3.1RC1を使った場合のモジュールはHSP開発Wikiのページをご覧ください。
(X)HTMLコンバータ (3) hsファイルから関数一覧を作成

hsファイルから関数一覧を作成するスクリプト。
関数のヘルプは%prmの次行の1文字目が必ず"("になることを利用しています。
最初はnotegetで1行ずつ調べるスクリプトを組んだのですが非常に遅かったため、instr()を利用したところ意外と高速なスクリプトができました。やはりHSP内部に任せられる処理は丸投げた方が早いようです。
このスクリプトは今後のhsファイルの仕様変更に伴い、使えなくなる可能性があります。// hsファイルから関数リストを作成 2007.07.20
#include "hspda.as"
#define HSP3_EXE "hsp3.exe" // HSP3実行ファイルの名前
#define DIR_HELP "hsphelp" // ヘルプファイルが保存されているディレクトリの名前
#const EXPAND_SIZE 1024 // funcListを拡張する単位[byte]
#module
// 指定したファイルが存在するならば1、存在しないならば0を返す
#defcfunc fileExist str fileName
exist fileName
return ( strsize >= 0 )
// 指定したディレクトリが存在するならば1、存在しないならば0を返す
#defcfunc dirExist str dirName, local s
dirlist s, dirName, 5
return stat
// 文字列を小文字に変換する
#define global ctype toLower( %1 = "" ) getpath( %1, 16 )
#global
chdir dir_exe
if ( fileExist( HSP3_EXE ) == 0 ) {
dialog "HSP3があるフォルダで実行してください。", 1
end
}
if ( dirExist( DIR_HELP ) == 0 ) {
dialog "hsphelpフォルダが見つかりません。", 1
end
}
chdir DIR_HELP
// hsファイルの一覧を取得
notesel hsFileList
dirlist hsFileList, "*.hs", 1
funcListSize = EXPAND_SIZE
sdim funcList, funcListSize
funcListPos = 0
repeat notemax
noteget tmp, cnt
if ( fileExist( tmp ) == 0 ) : continue // 念のための存在確認
notesel hsFile
noteload tmp
hsFilePos = 0
repeat
ins = instr( hsFile, hsFilePos, "%index\n" )
if ( ins < 0 ) : break
hsFilePos += ins + strlen( "%index\n" ) // strlen()は定数化した方が高速
getstr funcName, hsFile, hsFilePos
hsFilePos += strsize
ins = instr( hsFile, hsFilePos, "%prm\n" )
if ( ins < 0 ) : break
hsFilePos += ins + strlen( "%prm\n" ) // strlen()は定数化した方が高速
getstr tmp, hsFile, hsFilePos
hsFilePos += strsize
if strmid( tmp, 0, 1 ) == "(" {
if ( funcListPos + strlen( funcName + "\n" ) >= funcListSize ) {
funcListSize += EXPAND_SIZE
memExpand funcList, funcListSize
}
poke funcList, funcListPos, funcName + "\n"
funcListPos += strlen( funcName + "\n" )
}
loop
noteunsel
loop
sdim hsFile, 4
sdim hsFileList, 4
// 結果をアルファベット順に並び替えて表示
sortnote funcList
mesbox funcList, ginfo_winx, ginfo_winy
stop



