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) スクリプトの変換
登録:
コメントの投稿 (Atom)
2 件のコメント:
あのー,なんか,実行しようとしてもエラーがでて実行できないのですが,どうしたらよろしいのでしょうか。
「#Error 6 in line 506 (???)
-->パラメータの型が違います」
確かに動かないですね。スクリプトの一部を切り出す際に失敗していたようです。失礼しました。
他の個所に問題はないと思いますので、該当部分だけご自分で改良していただくのが手っ取り早いと思います。
コメントを投稿