2007年7月28日土曜日

(X)HTMLコンバータ (7) スクリプトの変換

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 = strlentext )
    rTextSize = textSize
    sdim rText, rTextSize       // ReplacedText

    if ( sandwichMode == 1 ) & ( sandwichTag != "" ) {
        if ( addIdMode ) {
            // コマンドライン(ファイル名が格納されているはず)から
            // ファイル名を取り出してidとする
            rText = startTag( sandwichTag, sandwichClass, getpathdir_cmdline, 8 ) )
        } else {
            rText = startTag( sandwichTag, sandwichClass )
        }
    }
    rTextPos = strlen( rText )

    x = 0
    firstCharOfArea = 1    // ラベルの判定に使用
    state = STATE_NORMAL

    return

// 変換の実行
*convert
    repeat
        textPos = cnt
        c = peektext, textPos )
        switch c
        case 0
            break
            swbreak
        case CR : case LF
            // 改行
            gosub *startNewLine
            if ( strmidtext, textPos, 2 ) == "\n" ) {
                continue textPos + 2
            }
            swbreak
        case '/'
            if ( strmidtext, textPos, 2 ) == "/*" ) & ( state == STATE_NORMAL ) {
                // 複数行コメント開始
                gosub *startMultiLineComment
                addString "/*" : x += 2
                continue textPos + 2
            }
            if ( strmidtext, 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 ( strmidtext, 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 ( strmidtext, 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 ( strmidtext, 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 = "&nbsp;"
                } else {
                    s = " "
                }
                repeat tabWidth - ( x \ tabWidth )
                    addString s : x++
                loop
            } else {
                addString "\t" : x += tabWidth - ( x \ tabWidth )
            }
            swbreak
        case ' '
            if replaceSpaces {
                // 半角スペースを変換(文字列中・コメント中でも実行)
                addString "&nbsp;"
            } 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 ( strmidtext, textPos, 2 ) == "\\\\" ) | ( strmidtext, textPos, 2 ) == "\\\"" ) {
                    addString strmidtext, textPos, 2 ) : x += 2
                    continue textPos + 2
                }
            }
            addString "\\" : x++
            swbreak
        case '&' : addString "&amp;"  : x++ : swbreak
        case '<' : addString "&lt;"   : x++ : swbreak
        case '>' : addString "&gt;"   : x++ : swbreak
        default
            // 命令・関数などキーワードの可能性
            if IsDBCSLeadByteEx( CP_ACP, c ) {
                // 2バイト文字の場合

                // 現在のバージョンでは変数をマークアップしないので、
                // このように扱ってもOK。
                addString strmidtext, 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 = peektextcnt )
        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 strmidtext, 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 = peektext, textPos + cnt )
        if is_number( j ) | ( j == '.' ) {
            continue
        }
        if ( cnt == 1 ) : if ( peektext, textPos ) == '0' ) & (( j == 'x' ) | ( j == 'b' )) {
            continue
        }
        if ( j == 'e' ) | ( j == 'E' ) {
            if ( textPos + cnt + 1 < textSize ) {
                n = peektext, textPos + cnt + 1 )
                if ( n == '+' ) | ( n == '-' ) : continue cnt + 2
            }
            continue
        }
        lNumber = cnt
        break
    loop
    addString strmidtext, textPos, lNumber )
    textPos += lNumber : x += lNumber
    if ( markupNumbers ) {
        addString endTag( "span" )
    }
    return

// 命令・関数・システム変数など
*addWord
    repeat -1, 1
        if textPos + cnt >= textSize {
            dcKey = strmidtext, textPos, cnt )
            break
        }
        if is_space( peektext, textPos + cnt ) ) {
            dcKey = strmidtext, 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月27日金曜日

円柱のHSV色空間


最近文字列ばかりだったので、一息入れる意味で3D。

奥行きの計算は厳密なものではなく近似したものです。

#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,  00, 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,0cos( observer_t ),sin( observer_t ),0, -sin( observer_t ),cos( observer_t ),00,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 00, ( 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スクリプトエディタ起動時に実行してみてください。エラー処理は適当です。
流れとしては

  1. タブコントロールにTCM_GETCURSELを送り現在選択されているタブのインデックスを取得
  2. タブのインデックスをFootyのハンドルに変換
  3. hsed_gettextを実行
といったところです。TCM_GETCURSELはちょくとさんのサイトを参考とさせていただきました。

なお、実行には別途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 textginfo_winxginfo_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_winxginfo_winy
    stop