Token ごとに処理する その2

Token ごとに処理する その2

前回は、getTokenとは名ばかりで、
内部的には、「1文字ずつ読んで1文字ずつ返す」これまでと同じ処理だった。

今回は、文字の種類が変わるまでためこんで行き、
文字の種類が変わった時点で、それまでためこんでいた文字列を返す
getToken を作成する。

ただし今回も、いきなり全機能を実現するのは大変なので、
まず練習として、英字を"予約語"、数字を"その他"、それ以外を"コメント"として
あつかうことにする。


//*********************************************************************
// 1区切りの文字列と 文字列の種類を返す
//*********************************************************************
//現在位置を保持
private int _col = 0;

//未確定 Token
private Token mikakuteiToken = null;
//確定済み Token
private Token kakuteiToken = null;
//1つ前の確定済み Token
private Token prevToken = null;

private Token getToken(Reader reader)
{
//ファイルの終わりまで、1文字ずつ読んで
while(true)
{
char c = reader.getChar();

if (c == TAB) //TAB文字なら
{
//TAB位置まで空白で埋める
string s = "";
do
{
s += BLANK; //空白を追加
} while (!TabPos(++_col)); //TAB位置まで進んだら抜ける
addToken(s); //読み込み文字列に追加
}
else if (c == NEWLINE) //改行コードなら
{
_col = 0; //現在位置をクリア
addToken(c); //読み込み文字列に追加
}
else if (c == BLANK) //空白なら
{
_col++; //現在位置を進める
addToken(c); //読み込み文字列に追加
}
else //TABでも 改行コードでも 空白でも なければ
{
_col += getLength(c); //現在位置を文字幅だけ進める
char charPrev = reader.getPrevChar(); //一つ前の文字を取得 (現在位置は変化しない)
char charNext = reader.getNextChar(); //もう一つ次の文字を取得 (現在位置は変化しない)
addToken(charPrev, c, charNext); //読み込み文字列に追加
}

//文字種が変わったら、変わる前の文字列情報を返す
Token token;
if ((token = getPrevToken()) != null) return token;

if (c == EOF) break;
}
return null;
}
//読み込み文字列に追加
private void addToken(string s)
{
for (int i=0;i<s.Length;i++)
{
if (s[i] != EOF)
addToken(s[i]);
}
}
private void addToken(char c)
{
addToken(BLANK, c, BLANK);
}
private void addToken(char cPrev, char cCurr, char cNext)
{
//読み込んだ文字の種類を取得
Token currToken = getCurrToken(cPrev, cCurr, cNext);

//文字種が変わってなかったら、未確定Token に 取得した文字を 追加
if (currToken.tokenKind == mikakuteiToken.tokenKind)
{
mikakuteiToken.tokenString += currToken.tokenString;
return;
}

//文字種が変わっても、取得した文字が空っぽなら 何もしない
if (currToken.tokenString.Length < 1) return;

//文字種が変わったら、確定済みToken に、未確定Token を Copy
kakuteiToken = mikakuteiToken;
//未確定Token に、新しいToken を Copy
mikakuteiToken = currToken;
currToken = null;
}
//読み込んだ文字の種類を取得
private Token getCurrToken(char cPrev, char cCurr, char cNext)
{
Token currToken = new Token(cCurr.ToString(), "その他");

if ((Char.IsUpper(cCurr)) || (Char.IsLower(cCurr)))
currToken.tokenKind = "予約語";

else if (Char.IsDigit(cCurr))
currToken.tokenKind = "その他";

else
currToken.tokenKind = "コメント";

return currToken;
}
//文字種が変わったら、変わる前の文字列情報を返す
private Token getPrevToken()
{
//確定済みToken がなければ null を返す
if (kakuteiToken == null) return null;

//確定済みToken があれば 確定済みToken の Copy を返して
prevToken = new Token(kakuteiToken.tokenString, kakuteiToken.tokenKind);
//確定済みToken をクリアする
kakuteiToken = null;
return prevToken;
}


//*********************************************************************
// 文字列情報クラス
//*********************************************************************
public class Token
{
private string _tokenHtml;
private string _tokenString;
private string _tokenKind;

public Token(string argString, string argKind)
{
_tokenHtml = getHtmlString(argString);
_tokenString = argString;
_tokenKind = argKind;
}

public string tokenHtml {get{return _tokenHtml; }}
public string tokenString{get{return _tokenString;} set{_tokenString = value;}}
public string tokenKind {get{return _tokenKind; } set{_tokenKind = value;}}

// "<", ">", "&" を変換
private string getHtmlString(string s)
{
string htmlString = "";
for (int i=0;i<s.Length;i++)
{
char c = s[i];
if (c == '<') htmlString += "&lt;";
else if (c == '>') htmlString += "&gt;";
else if (c == '&') htmlString += "&amp;";
else htmlString += c;
}

return htmlString;
}
}