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
if ( sandwichMode == 1 ) & ( sandwichTag != "" ) {
if ( addIdMode ) {
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 ) {
addString strmid( text, textPos, 2 ) : x += 2
continue textPos + 2
}
if ( state == STATE_NORMAL ) {
if ( is_number( c ) ) | ( c == '%' ) | ( c == '$' ) {
gosub *addNumber
continue textPos
}
if ( is_space( c ) == 0 ) | ( c == '#' ) {
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
*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
*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 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ユーザはアンダースコア区切りが多いようなのでそちらに変えようかな、と思っています。