Token の種類を判定する (演算子・区切り記号)

前回は、「+」「-」「/」「*」「=」など、記号全てを「区切り記号」として
定義したが、今回は少し改良して、あらかじめ「演算子」として登録されているものを
演算子」として扱い、それ以外の記号を「区切り記号」とする。

SourceToHtml10.plx


use strict;
#******************************************************************************
# C# → HTML 変換処理
#******************************************************************************
# ヘッダ部
fileCopy(".\\Template\\header.txt");

# 演算子の取得
my $operators;
getOperators();

# ソース部の変換
my $state = "その他";
my $kind = "";

my $line;
putLine() while ($line = getLine());

# フッタ部
fileCopy(".\\Template\\footer.txt");

#==============================================================================
# 演算子の取得
#==============================================================================
sub getOperators
{
open(F, ".\\cs7\\ope.txt") || die "open: $!";
$operators = <F>;
close(F);
}
#==============================================================================
# テンプレートファイルのコピー
#==============================================================================
sub fileCopy
{
my ($fileName) = @_;

open(F, $fileName) || die "open: $!";
print while (<F>);
close(F);
}
#==============================================================================
# 1行ずつ読み込む
#==============================================================================
sub getLine
{
# 1行読み込む
$_ = <>;

# TABを空白に変換
my $pos;
my $num;
while(($pos = index($_, "\t")) >= 0) #TABがあるか
{
$num = 4 - ($pos % 4); #空白何文字分に置き換えればよいか
$_ = substr($_,0,$pos).(' ' x $num).substr($_,$pos+1); #空白に置き換え
}

# 行末の空白を削除
s/ +$//;

# 変換結果を返す
return $_;
}
#==============================================================================
# 1行ずつ書き込む
#==============================================================================
sub putLine
{
# 改行コードを取り除く
chomp($line);

while(length($line))
{
if ($state eq "その他")
{
# 単一行コメントか?
if ($line =~ /^\/\//)
{
putToken($&, "単一行コメント");
$line = $';

stateComSingle();
}

# 複数行コメントか?
elsif ($line =~ /^\/\*/)
{
putToken($&, "複数行コメント");
$line = $';

stateComMulti();
}

# 逐語的文字列か?
elsif ($line =~ /^@"/)
{
putToken($&, "逐語的文字列");
$line = $';

stateStringLit();
}

# 文字列か?
elsif ($line =~ /^"/)
{
putToken($&, "文字列");
$line = $';

stateString();
}

# 文字か?
elsif ($line =~ /^'/)
{
putToken($&, "文字");
$line = $';

stateChar();
}

# 識別子か?
elsif ($line =~ /^[A-Za-z_]/)
{
stateIdent();
}

# 数字か?
elsif ($line =~ /^[0-9]/)
{
stateNumber();
}

# 記号か?
elsif ($line =~ /^\S/)
{
stateDelim();
}

# その他
else
{
# 1文字取得
$line =~ /./;
putToken($&, "その他");

# 1文字切り詰める
$line = $';
}
}
elsif ($state eq "複数行コメント") { stateComMulti(); }
elsif ($state eq "逐語的文字列") { stateStringLit(); }
elsif ($state eq "文字列") { stateString(); }
elsif ($state eq "文字") { stateChar(); }
}

# 改行
putToken("\n", $state);
}
#------------------------------------------------------------------------------
# 単一行コメント
#------------------------------------------------------------------------------
sub stateComSingle
{
putToken($line, "単一行コメント");
$line = "";
$state = "その他";
}
#------------------------------------------------------------------------------
# 複数行コメント
#------------------------------------------------------------------------------
sub stateComMulti
{
if ($line =~ /\*\//) # 終了があるか?
{
putToken($`.$&, "複数行コメント");
$line = $';
$state = "その他";
}
else
{
putToken($line, "複数行コメント");
$line = "";
$state = "複数行コメント";
}
}
#------------------------------------------------------------------------------
# 逐語的文字列
#------------------------------------------------------------------------------
sub stateStringLit
{
if ($line =~ /"/) # 終了があるか?
{
putToken($`.$&, "逐語的文字列");
$line = $';

my $after = $';
if ($after =~ /^"/) # 実は " か?
{
putToken($&, "逐語的文字列");
$line = $';
$state = "逐語的文字列";
}
else
{
$state = "その他";
}
}
else
{
putToken($line, "逐語的文字列");
$line = "";
$state = "逐語的文字列";
}
}
#------------------------------------------------------------------------------
# 文字列
#------------------------------------------------------------------------------
sub stateString
{
my $i = index($line, "\""); # 終了
my $j = index($line, "\\"); # エスケープ

if ((($i < $j) && ($i >= 0)) || ($j < 0)) # 終了
{
$line =~ /"/;
putToken($`.$&, "文字列");
$line = $';
$state = "その他";
}

elsif ((($j < $i) && ($j >= 0)) || ($i < 0)) # エスケープ
{
$line =~ /\\/;
putToken($`.$&, "文字列");
$line = $';

if ($line =~ /./)
{
putToken($&, "文字列");
$line = $';
$state = "文字列";
}
}

else
{
putToken($line, "文字列");
$line = "";
$state = "その他";
}
}
#------------------------------------------------------------------------------
# 文字
#------------------------------------------------------------------------------
sub stateChar
{
my $i = index($line, "'"); # 終了
my $j = index($line, "\\"); # エスケープ

if ((($i < $j) && ($i >= 0)) || ($j < 0)) # 終了
{
$line =~ /'/;
putToken($`.$&, "文字");
$line = $';
$state = "その他";
}

elsif ((($j < $i) && ($j >= 0)) || ($i < 0)) # エスケープ
{
$line =~ /\\/;
putToken($`.$&, "文字");
$line = $';

if ($line =~ /./)
{
putToken($&, "文字");
$line = $';
$state = "文字";
}
}

else
{
putToken($line, "文字");
$line = "";
$state = "その他";
}
}
#------------------------------------------------------------------------------
# 識別子
#------------------------------------------------------------------------------
sub stateIdent
{
$line =~ /^\w/;
putToken($&, "識別子");
$line = $';
$state = "その他";
}
#------------------------------------------------------------------------------
# 数字
#------------------------------------------------------------------------------
sub stateNumber
{
if ($line =~ /^[0-9]*\.?[0-9]?e{1}[+-]?[0-9]*[A-Z]{0,2}/i) #99e99
{
putToken($&, "数字");
$line = $';
$state = "その他";
}

elsif ($line =~ /^[0-9]*\.[0-9]*[A-Z]{0,2}/i) #99.99
{
putToken($&, "数字");
$line = $';
$state = "その他";
}

elsif ($line =~ /^0X[0-9A-F]*[A-Z]{0,2}/i) #0X99
{
putToken($&, "数字");
$line = $';
$state = "その他";
}

elsif ($line =~ /^[0-9]*[A-Z]{0,2}/i) #99
{
putToken($&, "数字");
$line = $';
$state = "その他";
}
}
#------------------------------------------------------------------------------
# 記号
#------------------------------------------------------------------------------
sub stateDelim
{
my $kbn;
(index($operators, $&) < 0) ? ($kbn = "記号") : ($kbn = "演算子");

$line =~ /^\S/;
putToken($&, $kbn);
$line = $';
$state = "その他";
}
#==============================================================================
# トークンを書き込む
#==============================================================================
sub putToken
{
$_ = shift; #追加する文字列
my $kind_next = shift; #追加するトークンの種類

# トークンの種類が変わったら、書き込む
if ($kind ne $kind_next)
{
print "</SPAN>" unless (($kind eq "その他") || ($kind eq ""));

if ($kind_next eq "複数行コメント") { print "<SPAN CLASS=\"COM\">"; }
elsif ($kind_next eq "単一行コメント") { print "<SPAN CLASS=\"COM\">"; }
elsif ($kind_next eq "逐語的文字列") { print "<SPAN CLASS=\"STR\">"; }
elsif ($kind_next eq "文字列") { print "<SPAN CLASS=\"STR\">"; }
elsif ($kind_next eq "文字") { print "<SPAN CLASS=\"CHA\">"; }
elsif ($kind_next eq "識別子") { print "<SPAN CLASS=\"IDW\">"; }
elsif ($kind_next eq "数字") { print "<SPAN CLASS=\"NUM\">"; }
elsif ($kind_next eq "記号") { print "<SPAN CLASS=\"DLM\">"; }
elsif ($kind_next eq "演算子") { print "<SPAN CLASS=\"OPE\">"; }
}
$kind = $kind_next;

# <, >, &, |, (, ) を置換
s/&/&#x26;/g; # &
s/</&#x3C;/g; # <
s/>/&#x3E;/g; # >
s/\(/&#x28;/g; # ( はてな
s/\)/&#x29;/g; # ) はてな
s/\|/&#x7C;/g; # | はてな

print $_;
}

実行形式


C:\Perl5>jperl SourceToHtml10.plx input.txt > output.txt