Token の種類を判定する (記号)

今回は、「記号」の処理を追加します。
「記号」の定義は、「+」「-」「/」「*」「=」などの記号、全てとします。

SourceToHtml009.js


// 雛形ファイル名
var tempFileName = ".\\application\\template.html";

// 変換対象文字
var escapeChar = "<>&()|";

//*****************************************************************************
// 主処理
//*****************************************************************************
main();

function main()
{
// コマンド引数が、2つ指定されてなければ、異常終了
if (WScript.Arguments.length < 2)
{
WScript.echo("コマンド引数が、2つ指定されてないので、終了");
return;
}

//入力ファイル名を 取得する
var inFileName = WScript.Arguments(0);

// 出力ファイル名を 取得する
var outFileName = WScript.Arguments(1);

// 言語タイプを 取得する
var langType = "";
if (WScript.Arguments.length > 2)
{
langType = WScript.Arguments(2).toLowerCase();
}

// FileSystemObject を 生成
var fs = WScript.CreateObject("Scripting.FileSystemObject");

// 雛形ファイルを 読み込み専用モードで 開く
var tsTemp = fs.OpenTextFile(tempFileName, 1);

// 出力ファイルを 新規作成モードで 開く
var tsOut = fs.OpenTextFile(outFileName, 2, -1);

// 雛形ファイルの終わりまで繰り返す
while (!tsTemp.AtEndOfStream)
{
// 雛形ファイルを 1行ずつ読む
var line = tsTemp.ReadLine();

// 入力ファイル名 を 出力
if (line.indexOf("%入力ファイル名%") >= 0)
tsOut.WriteLine(line.replace("%入力ファイル名%", fs.GetFileName(inFileName)));

// 行番号 を 出力
else if (line == "%行番号%")
putLineNo(fs, tsOut, inFileName);

// プログラムソース を 出力
else if (line == "%プログラムソース%")
SourceToHtml(fs, tsOut, inFileName, langType);

// 雛形ファイルを そのまま出力
else
tsOut.WriteLine(line);
}

// 出力ファイルを 閉じる
tsOut.Close();

// 雛形ファイルを 閉じる
tsTemp.Close();
}
//*****************************************************************************
// 行番号 を 出力
//*****************************************************************************
function putLineNo(fs, tsOut, inFileName)
{
// 入力ファイルを 読み込み専用モードで 開く
var tsIn = fs.OpenTextFile(inFileName, 1);

// 行番号を初期化
var lineNo = 0;

// 入力ファイルの終わりまで繰り返す
while (!tsIn.AtEndOfStream)
{
// 入力ファイルを1行ずつ読む
tsIn.ReadLine();

// 行番号を書く
tsOut.WriteLine('<SPAN CLASS="LNO">' + FormatNum(++lineNo) + '</SPAN>');
}

// 入力ファイルを 閉じる
tsIn.Close();
}
//-----------------------------------------------------------------------------
// 数値の書式化 (例: 123 → "00123")
//-----------------------------------------------------------------------------
function FormatNum(num)
{
var s = "00000" + num;

return (s.substr(s.length - 5, 5));
}
//*****************************************************************************
// プログラムソース を 出力
//*****************************************************************************
function SourceToHtml(fs, tsOut, inFileName, langType)
{
// 入力ファイルを 読み込み専用モードで 開く
var tsIn = fs.OpenTextFile(inFileName, 1);

// 文脈オブジェクト
var context = new Object();
//現在の状態
context.state = "その他";

// 入力ファイルの終わりまで繰り返す
while (!tsIn.AtEndOfStream)
{
// 入力ファイルを 1行ずつ読む
context.lineIn = tsIn.ReadLine();

// TAB を 空白に 変換
context.lineIn = TabToSpace(context.lineIn);

// プログラムソース を 変換して 出力
switch(langType)
{
// プログラムソース を 変換して 出力 ( C, C++, Java, JavaScript 用)
case "vc6":
case "vc7":
case "bcb":
case "java":
case "javascript":
tsOut.WriteLine(CppToHtml(context));
break;

// そのまま出力
default:
tsOut.WriteLine(context.lineIn);
break;
}
}
// 入力ファイルを 閉じる
tsIn.Close();
}
//=============================================================================
// プログラムソース を 変換して 出力 ( C, C++, Java, JavaScript 用)
//=============================================================================
function CppToHtml(context)
{
// 変換出力用文字列
var lineOut = "";

// トークン
context.token = "";
// トークンの種類
context.type = "";

while (context.lineIn.length > 0)
{
// トークン
context.currToken = "";
// トークンの種類
context.currType = "";

if (context.state == "その他")
stateOtherCpp(context);

else if (context.state == "複数行コメント")
stateCommentCpp(context);

else if (context.state == "文字列")
stateStringCpp(context);

else if (context.state == "文字")
stateCharCpp(context);

// トークンの種類が変わったら
if (context.currType != context.type)
{
lineOut += putToken(context.token, context.type);
context.token = context.currToken;
context.type = context.currType;
}
// トークンの種類が同じなら
else
{
context.token += context.currToken;
}
}
lineOut += putToken(context.token, context.type);

return lineOut;
}
//-----------------------------------------------------------------------------
// 「その他」の 時
//-----------------------------------------------------------------------------
function stateOtherCpp(context)
{
// 単一行コメントか? "//"
if (context.lineIn.match(/^\/\//))
{
// 「単一行コメント」として出力
context.currToken = context.lineIn;
context.currType = "コメント";
context.state = "その他";
// 入力文字列を切り詰める
context.lineIn = "";
}
// 複数行コメントか? "/*"
else if (context.lineIn.match(/^\/\*/))
{
// 「複一行コメントの開始」として出力
context.currToken = "/*";
context.currType = "コメント";
context.state = "複数行コメント";
// 入力文字列を 2文字 切り詰める
context.lineIn = context.lineIn.substr(2, context.lineIn.length - 2);
}
// 文字列か?
else if (context.lineIn.match(/^"/))
{
// 「文字列」として出力
context.currToken = '"';
context.currType = "文字列";
context.state = "文字列";
// 入力文字列を 1文字 切り詰める
context.lineIn = context.lineIn.substr(1, context.lineIn.length - 1);
}
// 文字か?
else if (context.lineIn.match(/^'/))
{
// 「文字」として出力
context.currToken = "'";
context.currType = "文字";
context.state = "文字";
// 入力文字列を 1文字 切り詰める
context.lineIn = context.lineIn.substr(1, context.lineIn.length - 1);
}
// 識別子か?
else if (context.lineIn.match(/^[A-Za-z_][A-Za-z0-9_]*/))
{
// 「識別子」として出力
context.currToken = RegExp.lastMatch;
context.currType = "識別子";
context.state = "その他";
// 入力文字列を 切り詰める
context.lineIn = RegExp.rightContext;
}
// 数字か?
else if (context.lineIn.match(/^[0-9]/))
{
// 99e99
if (context.lineIn.match(/^[0-9]*\.?[0-9]?e{1}[+-]?[0-9]*[A-Z]{0,2}/i))
{
// 「数字」として出力
context.currToken = RegExp.lastMatch;
context.currType = "数字";
context.state = "その他";
// 入力文字列を 切り詰める
context.lineIn = RegExp.rightContext;
}
// 99.99
else if (context.lineIn.match(/^[0-9]*\.[0-9]*[A-Z]{0,2}/i))
{
// 「数字」として出力
context.currToken = RegExp.lastMatch;
context.currType = "数字";
context.state = "その他";
// 入力文字列を 切り詰める
context.lineIn = RegExp.rightContext;
}
// 0x99
else if (context.lineIn.match(/^0X[0-9A-F]*[A-Z]{0,2}/i))
{
// 「数字」として出力
context.currToken = RegExp.lastMatch;
context.currType = "数字";
context.state = "その他";
// 入力文字列を 切り詰める
context.lineIn = RegExp.rightContext;
}
// 99
else if (context.lineIn.match(/^[0-9]*[A-Z]{0,2}/i))
{
// 「数字」として出力
context.currToken = RegExp.lastMatch;
context.currType = "数字";
context.state = "その他";
// 入力文字列を 切り詰める
context.lineIn = RegExp.rightContext;
}
}
// 演算子・記号か?
else if (context.lineIn.match(/^[!-~]/))
{
// 「識別子」として出力
context.currToken = RegExp.lastMatch;
context.currType = "記号";
context.state = "その他";
// 入力文字列を 切り詰める
context.lineIn = RegExp.rightContext;
}
// それ以外なら
else
{
// そのまま出力
context.currToken = context.lineIn.substr(0, 1);
context.currType = "";
context.state = "その他";
// 入力文字列を 1文字 切り詰める
context.lineIn = context.lineIn.substr(1, context.lineIn.length - 1);
}
}
//-----------------------------------------------------------------------------
// 「複数行コメント」の 時
//-----------------------------------------------------------------------------
function stateCommentCpp(context)
{
// 終了があるか? "*/"
if (context.lineIn.match(/\*\//))
{
// 「複一行コメントの終了」として出力
context.currToken = RegExp.leftContext + "*/";
context.currType = "コメント";
context.state = "その他";
// 入力文字列を 切り詰める
context.lineIn = context.lineIn.substr(RegExp.leftContext.length + 2, context.lineIn.length - (RegExp.leftContext.length + 2));
}
else
{
// 「複一行コメント」として出力
context.currToken = context.lineIn;
context.currType = "コメント";
context.state = "複数行コメント";
// 入力文字列を 切り詰める
context.lineIn = "";
}
}
//-----------------------------------------------------------------------------
// 「文字列」の 時
//-----------------------------------------------------------------------------
function stateStringCpp(context)
{
// 文字列の終了?
var i = context.lineIn.indexOf('"');
// エスケープ?
var j = context.lineIn.indexOf("\\");

// 「"」が エスケープされていない
if (((i < j) && (i >= 0)) || (j < 0))
{
// 文字列の終了
context.lineIn.match(/"/);
context.currToken = RegExp.leftContext + '"';
context.currType = "文字列";
context.state = "その他";
// 入力文字列を 切り詰める
context.lineIn = RegExp.rightContext;
}
// 何かが エスケープされている
else if (((j < i) && (j >= 0)) || (i < 0))
{
// エスケープされた文字を出力
context.lineIn.match(/\\/);
context.currToken = RegExp.leftContext + '\\' + RegExp.rightContext.substr(0, 1);
context.currType = "文字列";
context.state = "文字列";
// 入力文字列を 切り詰める
context.lineIn = RegExp.rightContext.substr(1, RegExp.rightContext.length - 1);
}
else
{
// 行末まで出力
context.currToken = context.lineIn;
context.currType = "文字列";
context.state = "その他";
// 入力文字列を 切り詰める
context.lineIn = "";
}
}
//-----------------------------------------------------------------------------
// 「文字」の 時
//-----------------------------------------------------------------------------
function stateCharCpp(context)
{
// 文字の終了?
var i = context.lineIn.indexOf("'");
// エスケープ?
var j = context.lineIn.indexOf("\\");

// 「'」が エスケープされていない
if (((i < j) && (i >= 0)) || (j < 0))
{
// 文字の終了
context.lineIn.match(/'/);
context.currToken = RegExp.leftContext + "'";
context.currType = "文字";
context.state = "その他";
// 入力文字列を 切り詰める
context.lineIn = RegExp.rightContext;
}
// 何かが エスケープされている
else if (((j < i) && (j >= 0)) || (i < 0))
{
// 「\」を出力
context.lineIn.match(/\\/);
context.currToken = RegExp.leftContext + '\\';
context.currType = "文字";
// 入力文字列を 切り詰める
context.lineIn = RegExp.rightContext;

// エスケープされた文字を出力
context.currToken += context.lineIn.substr(0, 1);
context.currType = "文字";
context.state = "文字";
// 入力文字列を 1文字 切り詰める
context.lineIn = context.lineIn.substr(1, context.lineIn.length - 1);
}
else
{
// 行末まで出力
context.currToken = context.lineIn;
context.currType = "文字";
context.state = "その他";
// 入力文字列を 切り詰める
context.lineIn = "";
}
}
//=============================================================================
// トークンを出力
//=============================================================================
function putToken(token, type)
{
if (token.length < 1) return "";

// 変換出力用文字列
var lineOut = "";

// 1文字ずつ処理する
for (var i=0;i<token.length;i++)
{
// 1文字取り出す
var c = token.substr(i, 1);

// 変換対象の文字か?
if (escapeChar.indexOf(c) >= 0)
{
// 変換して出力
// escape : "0" → "%30"
// replace : "%30" → "&#x30;"
lineOut += escape(c).replace("%", "&#x") + ";";
}
else
{
// そのまま出力
lineOut += c;
}
}

if (type == "予約語") return '<SPAN CLASS="KEY">' + lineOut + '</SPAN>';
if (type == "コメント") return '<SPAN CLASS="COM">' + lineOut + '</SPAN>';
if (type == "文字列") return '<SPAN CLASS="STR">' + lineOut + '</SPAN>';
if (type == "文字") return '<SPAN CLASS="CHA">' + lineOut + '</SPAN>';
if (type == "識別子") return '<SPAN CLASS="IDF">' + lineOut + '</SPAN>';
if (type == "数字") return '<SPAN CLASS="NUM">' + lineOut + '</SPAN>';
if (type == "記号") return '<SPAN CLASS="DLM">' + lineOut + '</SPAN>';
return lineOut;
}
//-----------------------------------------------------------------------------
// TAB を 空白に 変換
//-----------------------------------------------------------------------------
function TabToSpace(lineIn)
{
// 変換出力用文字列
var lineOut = "";

// 現在位置
var pos = 0;

// 1文字ずつ処理する
for (var i=0;i<lineIn.length;i++)
{
// 1文字取り出す
var c = lineIn.substr(i, 1);

// TAB か?
if (c == "\t")
{
// 空白何文字分に置き換えればよいか
var num = 4 - (pos % 4);
// 現在位置を加算
pos += num;
// 変換して出力
lineOut += " ".substr(0, num);
}
else
{
// 現在位置を加算
if (escape(c).length < 4)
pos++;
else
pos += 2;
// そのまま出力
lineOut += c;
}
}

//行末の空白を削除
return lineOut.replace(/ +$/,"");
}

実行形式


Z:\>SourceToHtml009.js TEST.cpp TEST.html vc6