読者です 読者をやめる 読者になる 読者になる

Javaによって任意のJavaソースをhtmlにする上での罠

新人研修で出た課題なんだけど、意外と奥が深かったのでちょっとメモっておく

あんまりコードは書かないけど、これはちょっとしたメモ

  • 1行コメント//以下を緑色にする
  • 文字列リテラルの中の//は無視すること
  • Javaのキーワードは青色にする
  • 与えるJavaソースはコンパイルが通るものとする
  • コンパイラはJDK1.6.17

っていう仕様が与えられた場合、簡単そうで意外と難しかったのでメモっておく

Javaのキーワードはどうやって判別したらよいか

まず、配列や、外部ファイルなどでJavaキーワード50個くらいを確保しておく。ここまではOK
次に、Javaのキーワードがどこで出現するかを考える。

まず基本形
int a = 0;
public int b = 0;

こういうケースだと、両端空白とか先頭にあるキーワードは認めないといけないっぽい

次にちょっと発展形
if(a==0){
    return;
}else{
   //hoge
}

どうやらカッコ、中括弧、セミコロンも認めないといけないっぽい。
switch文のdefault:も考えると、コロンもあるっぽいなーと感づく。
次に、ダメなケースを考える

//int a = 0;
/* int b = 0 */
System.out.print("");
String[] strs = {
    "int",
    "short"
}

コメント行はダメなのと、printのintはキーワードじゃないのでダメ
あと、文字列リテラル内のキーワードも除外しないとダメ
これを考えると、どうやら、コメント行なのか文字列リテラルなのかどうか判断するJavaパーサを自作する必要がありそうだということがわかってくる
そういう機能は別クラスに任せるとして、とりあえずJavaキーワードかどうか判別していくことにする

正規表現を使ってJavaのキーワードかどうか判別する

正規表現には単語境界かどうか判別する\bというメタ文字があるので、それを使えば上の条件は全て網羅することができそう

//javaSourceは与えるJavaソースコード
//keywordはJavaキーワード
//colorKeywordはHTMLで色を付けたJavaキーワード(fontタグやcssで色付きclassを指定したタグで囲んだJavaキーワード)
String convert = javaSource.replace("\\b"+keyword+"\\b",colorKeyword);
でもダメなケースはある!!

例えば

public int public$int$number = 0;

むしろ、はてな記法スーパーpre記法ですら上手くいってない!!(バックエンドのvimが悪いんだけど)
大抵のエディタは単語境界メタ文字でやってるので、こういう変な変数名とかはうまく行かない。
どう直したらいいものか・・・。

文字列リテラルかどうかを判別したい

単にダブルクォーテーションを数えるとか、ダブルクォーテーションごとに分割するとかすればいいと思っちゃいますが、どうやらそうではないらしい

エスケープ文字に注意
String str = "\"//こめんと";//コメント

エスケープ文字かどうか判別する機構が必要

コメントもあるで
String str = /* \" " "" */ "てすと";//\\\\\\\\\\""""\"

当たり前だけど、コメントが先にあったらコメント有線になる(逆も然り)

シングルクォーテーションも考える必要がある
char ch = '"'; String str = "//こめんと";

どんなソースがあるかわからないので、当然こういうケースも考えないといけない

どうやらクォーテーションかコメントかを判別するパーサを作る必要がありそう

与えたソースコードに対して、行ごとに

  • シングルクォーテーション
  • ダブルクォーテーション
  • 1行コメント(//)
  • 複数行コメント(/*)

の出現位置を見ていって、モードを切り替えていくようなことをしていく作業が必要っぽい
さらに、ダブルクォーテーションの場合、エスケープ文字かどうか判別することも必要