PsParserを使ってソースを色付きHTMLに変換する

はてなにはスーパーpre記法という、ソースコードシンタックスハイライトして表示する機能があるのですが、これはPowerShellに対応していないようです。予約語の似ているRubyモードで貼り付けていこうかとも思ったのですが、PowerShell 2.0CTPではパーサを利用できるので、ファイルをパーサーに食わせて結果をハイライトつきのHTMLに変換するスクリプトを書いてみました。引数に、変換するスクリプトファイル名を渡します。エンコーディングはutf8きめうちにしているので、上手くいかなかったら修正してみてください。


$cont = Get-Content $args[0] -enc utf8

$sb = New-Object System.Text.StringBuilder
$cont | % { [void]$sb.Append($_ + "`n") }
$src = $sb.ToString()

Set-Variable err # エラー出力 / 使わないけど宣言してないと怒られるので
$parse = [System.Management.Automation.PsParser]::Tokenize($src, [ref]$err)

$html = New-Object System.Text.StringBuilder
[void]$html.Append('<html><head><link rel="stylesheet" type="text/css" href="ps.css"><title>out</title></head><body>')
[void]$html.Append("<pre style='font-family: monospace; padding: 3px;'>`n")

function TokenToHtml($t){
if($t.Type -eq "NewLine"){ [void]$html.Append("`n"); return }

$w = (Get-Src ($t.StartLine - 1) ($t.StartColumn - 1) ($t.EndLine- 1) ($t.EndColumn - 1) )
# Tokenのタイプ別に何か処理変えるなら
switch($t.Type) {
default { [void]$html.Append('<span class="Ps' + $t.Type + '">' + $w + '</span>') }
}
}

# 変換元ソースの該当箇所を取得 / 0スタートなので、Tokenの返す値から-1する
function Get-Src($s_line, $s_col, $e_line, $e_col){
for($line = $s_line; $line -le $e_line; $line++){
if($line -eq $s_line) { $s = $s_col }
else { $s = 0 }
if($line -eq $e_line) { $e = $e_col }
else { $e = $cont[$line].Length }
$ret += $cont[$line].Substring($s, $e - $s)
}
return $ret.Replace('&', '&amp;').Replace('<', '&lt;').Replace('>', '&gt;')
}

$pre = $null
$parse | % {
if($pre -ne $null -and ($pre.EndLine -ne $_.StartLine -or $pre.EndColumn -ne $_.StartColumn)){
$w = (Get-Src ($pre.EndLine - 1) ($pre.EndColumn - 1) ($_.StartLine - 1) ($_.StartColumn - 1) )
[void]$html.Append($w)
}

TokenToHtml $_
$pre = $_
}

[void]$html.Append("</pre></body></html>`n")
$html.ToString()

パーサの返すトークンには、Unknown、Command、CommandParameter、CommandArgument、Number、String、Variable、Member、LoopLabel、Attribute、Type、Operator、GroupStart、GroupEnd、Keyword、Comment、StatementSeparator、NewLine、LineContinuation、Positionという種別があるのですが、それぞれの名前に"Ps"を付けたクラスのspanタグで括って出力するので、対応するクラスをCSSに定義しておけば、好みの色で表示する事が出来ます。