UWSCスクリプトのフォーマッター

公式掲示板にインデントをタブで貼り付ける人がいるので、フォーマッターを考えた。
フォーマッターと言っても、インデントを整えるだけ。
でも、構文解析してますよ!



構文解析エンジン

wshでやろうと思ったので、javascriptのパーサージェネレーターを探したところ、見つけた。
JavaScript で構文解析: Days on the Moon
俄然やる気に。

ということでスクリプト

<?xml version="1.0" standalone="yes" ?>
<package>
	<job id="Formatter">
		<?job error="true" debug="true"?>
		<runtime>
			<description>UWSCスクリプトファイルのインデントを行います</description>
			<unnamed name="target" helpstring="対象スクリプト" many="false" required="false" />
		</runtime>
		<resource id="indent">  </resource>
		<script language="JScript" src="gin.js" />
		<object id="_fso" progid="Scripting.FileSystemObject"/>
		<script language="JScript">
		<![CDATA[
			WScript.Quit((function() {
				// wscriptの場合は引数必須。cscriptの場合は引数なければ標準入力
				var tarPath = null, tarScript;
				if(WScript.Arguments.length) tarPath = WScript.Arguments(0);
				if(tarPath != null) {
					try {
						var tarFile = _fso.OpenTextFile(tarPath);
						tarScript = tarFile.ReadAll();
						tarFile.Close();
					} catch(e) {
						WScript.Echo(e.name, e.number, e.message);
						return e.number;
					}
				} else if(WScript.FullName.toLowerCase().slice(-11) == "cscript.exe") {
					try {
						tarScript = WScript.StdIn.ReadAll();
					} catch(e) {
						if(e.number != -2146828226) WScript.Echo(e.name, e.number, e.message);
						return e.number;
					}
				} else {
					return 1;
				}

				// 構文解析
				tarScript = tarScript.replace(/\n\r/g, "\n\0\r").replace(/^\r/, "\t\r");
				var indent = getResource("indent")
				var uwscParser = new Gin.Grammar({
					Statement:	/ (Sep (IndentNon | IndentStart | IndentSep | IndentEnd | Comment | All | $EPS):push ('\r\n' | ';'))* /,
					Sep:		/ <[ \t ]*> /,
					All:		/ <([^\r\n";]|"[^\r\n"]*")+> /,
					Tail:		/ Sep (Comment | $EPS) /,
					IndentNon:	/ <[iI][fF][ \t ][\S \t ]+[ \t ][tT][hH][eE][nN]> Sep Name (All | $EPS) /,
					IndentStart:/ ((<[mM][oO][dD][uU][lL][eE]> | <[cC][lL][aA][sS][sS]> | <[tT][eE][xX][tT][bB][lL][oO][cC][kK]> ) Sep Name | <[tT][rR][yY]> | (<[pP][rR][oO][cC][eE][dD][uU][rR][eE]> | <[fF][uU][nN][cC][tT][iI][oO][nN]> | <[fF][oO][rR]> | <[wW][hH][iI][lL][eE]> | <[sS][eE][lL][eE][cC][tT]> | <[wW][iI][tT][hH]> ) Sep All | <[iI][fF][bB]?[ \t ][\S \t ]+[ \t ][tT][hH][eE][nN]> | <[rR][eE][pP][eE][aA][tT]> | <[cC][oO][mM][_][eE][rR][rR][_][iI][gG][nN]> ) Tail ::inc /,
					IndentSep:	/ (<[fF][iI][nN][aA][lL][lL][yY]> | <[eE][xX][cC][eE][pP][tT]> | <[eE][lL][sS][eE][iI][fF]> Sep All | <[eE][lL][sS][eE]> | <[cC][aA][sS][eE]> Sep All | <[dD][eE][fF][aA][uU][lL][tT]>) Tail ::sep /,
					IndentEnd: / ( <[eE][nN][dD][mM][oO][dD][uU][lL][eE]> | <[eE][nN][dD][cC][lL][aA][sS][sS]> | <[eE][nN][dD][wW][iI][tT][hH]> | <[eE][nN][dD][tT][eE][xX][tT][bB][lL][oO][cC][kK]> | <[eE][nN][dD][tT][rR][yY]> | <[fF][eE][nN][dD]> | <[eE][nN][dD][iI][fF]> | <[nN][eE][xX][tT]> | <[wW][eE][nN][dD]> | <[uU][nN][tT][iI][lL]> Sep All | <[sS][eE][lL][eE][nN][dD]> | <[cC][oO][mM][_][eE][rR][rR][_][rR][eE][tT]> ) Tail ::dec /, 
					Dim:		/ <[dD][iI][mM]> Sep Params /,
					Comment:	/ '\/\/' <[^\r\n]*> /,
					Params:		/ Name (Sep '=' Sep Val | $EPS) (Sep ',' Sep Name (Sep '=' Sep Val | $EPS))* /,
					Name:		/ <[a-zA-Z#%&~|\@{}][0-9a-zA-Z#%&~|\@{}]*> /,
					Val:		/ $DECIMAL | '"' <[^"]*> '"' /
				}, "Statement", '_\r\n');
				var ua = {
					_s: [],
					_is: [],
					_i: 0,
					_n: 0,
					inc: function() { this._i++; this._n=-1; },
					dec: function() { this._i--; },
					sep: function() { this._n=-1; },
					push: function(v) { this._s.push(v); this._is.push(this._i + this._n); this._n=0; }
				};
				// フォーマット開始
				var match = uwscParser.parse(tarScript, ua);
				if(match && match.full) {
					// 結果出力
					var i, j, line;
					var join = function(v) {
						var ret = v;
						if(v instanceof Array) {
							ret = "";
							for(var i = 0; i < v.length; i++) ret += join(v[i]);
						}
						return ret;
					};
					for(i = 0; i < ua._s.length; i++) {
						line = join(ua._s[i]);
						line = line.replace(/^[ \t ]+/, '');
						for(j = 0; j < ua._is[i]; j++) line = indent + line;
						WScript.Echo(line);
					}
				} else {
					WScript.Echo("スクリプト解析失敗")
				}
			})());
		]]>
		</script>
	</job>
</package>

拡張子「.wsf」で保存してください。
また、同フォルダーに、上記リンクのgin.jsが必要です。

使い方

cscript //nolog 〜.wsf (対象スクリプトファイル)

とした場合は、対象スクリプトファイルのインデントを整えて、標準出力に出します。


ファイル名を指定しなかった場合は、標準入力からスクリプトを受け取り、標準出力に出します。
標準入力経由の場合、入力の終端記号Ctrl+Zを入れてあげてください。
掲示板からのコピペ用かな)


予約語の記述がかなり適当です。
もうちょっときれいに書きたいものです。(書き方未調査)


SELECTのCASEは、SELECTと同列です。
私はこの書き方が好みなので。
もし、一段下げるなら、SELECTとSELENDを二段にすればOK。