トークン読み込み用クラス

これまでC# で作成してきた「SourceToHTML」を、Delphi に焼きなおしてみます。

TokenReader.pas


//*******************************************************************************************************
// トークンごとに その種類と、内容を返す
//*******************************************************************************************************
unit TokenReader;

interface
uses
TextReader, Token, Context;

type
TTokenReader = class
private
//入力用クラス
_reader: TTextReader;

//現在位置を保持
_col: Integer;

procedure addToken();
function getPrevToken():TToken;
function TabPos(col:Integer):Boolean;
function getLength(c:WideChar):Integer;

protected
//文脈情報 クラス (前後のトークン、前後の文字を保持しておく)
_context: TContext;

constructor Create(reader:TTextReader; langType:string);virtual;
procedure getNextToken();virtual;
function IsLetter(c:WideChar):Boolean;
function IsDigit(c:WideChar):Boolean;
function IsSymbol(c:WideChar):Boolean;

public
class function CreateTokenReader(reader:TTextReader; langType:string):TTokenReader;
function getToken():TToken;
end;

const
EOF: WideChar = #0;
NEWLINE: WideChar = #13;
TAB: WideChar = #9;
BLANK: WideChar = ' ';

LETTERS: WideString = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
DIGITS: WideString = '0123456789';
SYMBOLS: WideString = '!"#$%&''()*+,-./:;<=>?@[\]^`{|}~';

implementation
uses
TokenReaderCase, TokenReaderCSharp, TokenReaderVB, TokenReaderDelphi, TokenReaderPlsql, TokenReaderTsql;
//---------------------------------------------------------------------------------------------------
// ファクトリー
//---------------------------------------------------------------------------------------------------
class function TTokenReader.CreateTokenReader(reader:TTextReader; langType:string):TTokenReader;
begin
if (langType = 'cs7') then result := TTokenReaderCSharp.Create(reader, langType) // VC#.NET
else if (langType = 'vc6') then result := TTokenReaderCase.Create(reader, langType) // VC++
else if (langType = 'vc7') then result := TTokenReaderCase.Create(reader, langType) // VC++.NET
else if (langType = 'java') then result := TTokenReaderCase.Create(reader, langType) // VJ++, VJ#.NET, Java
else if (langType = 'js') then result := TTokenReaderCase.Create(reader, langType) // JavaScript
else if (langType = 'bcb') then result := TTokenReaderCase.Create(reader, langType) // C++Builder
else if (langType = 'vb6') then result := TTokenReaderVB.Create(reader, langType) // VB
else if (langType = 'vb7') then result := TTokenReaderVB.Create(reader, langType) // VB.NET
else if (langType = 'del') then result := TTokenReaderDelphi.Create(reader, langType) // Delphi
else if (langType = 'psq') then result := TTokenReaderPlsql.Create(reader, langType) // PL/SQL
else if (langType = 'tsq') then result := TTokenReaderTsql.Create(reader, langType) // T-SQL
else result := TTokenReader.Create(reader, langType); // other
end;
//---------------------------------------------------------------------------------------------------
// 初期化
//---------------------------------------------------------------------------------------------------
constructor TTokenReader.Create(reader:TTextReader; langType:string);
begin
//入力用クラス
_reader := reader;

//文脈情報 クラス (前後のトークン、前後の文字を保持しておく)
_context := TContext.Create();

//文字を取得 (1つ先読みする)
_context.nextChar := _reader.getChar();

//現在位置を保持
_col := 0;
end;
//---------------------------------------------------------------------------------------------------
// トークンごとに その種類と、内容を返す
//---------------------------------------------------------------------------------------------------
function TTokenReader.getToken():TToken;
var
token:TToken;
begin
//ファイルの終わりまで、1文字ずつ読む
repeat
//文字を取得 (1つ先読みする)
_context.prevChar := _context.currChar; //現在の文字を、前の文字に複写
_context.currChar := _context.nextChar; //次の文字を、現在の文字に複写
_context.nextChar := _reader.getChar(); //次の文字を先読み

if (_context.currChar = TAB) then //TAB文字なら
begin
_context.currChar := BLANK; //空白に置換
repeat
addToken(); //TAB位置まで空白で埋める
inc(_col);
until(TabPos(_col));
end
else
begin
if (_context.currChar = BLANK) then //空白なら
inc(_col) //現在位置を1つ進める

else if (_context.currChar = NEWLINE) then //改行コードなら
_col := 0 //現在位置をクリア

else if (_context.currChar <> EOF) then //TABでも 改行コードでも 空白でも なければ
_col := _col + getLength(_context.currChar); //現在位置を文字幅だけ進める

addToken(); //読み込み文字列に追加
end;

//文字種が変わったら、変わる前のトークンを返す
token := getPrevToken();
if (token <> nil) then
begin
result := token;
exit;
end;

until(_context.currChar = EOF);

result := nil;
end;
//---------------------------------------------------------------------------------------------------
// 読み込んだ文字を トークンに追加
//---------------------------------------------------------------------------------------------------
procedure TTokenReader.addToken();
begin
//未確定トークン がクリアされていれば、ファイルの終わり
if (_context.currToken = nil) then exit;

//読み込んだ文字の種類を取得、状態を更新 (nextToken にセットする)
getNextToken();

//文字種が変わってなかったら、未確定トークンに 取得した文字を 追加
if ((_context.nextToken.tokenKind = _context.currToken.tokenKind) And (_context.currChar <> EOF)) then
begin
_context.currToken.tokenString := _context.currToken.tokenString + _context.nextToken.tokenString;
exit;
end;

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

//文字種が変わったら、確定済みトークン に、未確定トークン を Copy
_context.prevToken.tokenKind := _context.currToken.tokenKind;
_context.prevToken.tokenString := _context.currToken.tokenString;
_context.prevToken.IsFixed := true;

//未確定トークン に、新しいトークン を Copy
_context.currToken.tokenKind := _context.nextToken.tokenKind;
_context.currToken.tokenString := _context.nextToken.tokenString;

//ファイルの終わりに達したら、未確定トークン をクリア
if (_context.currChar = EOF) then _context.currToken := nil;
end;
//---------------------------------------------------------------------------------------------------
// 文字を取得・状態を更新
//---------------------------------------------------------------------------------------------------
procedure TTokenReader.getNextToken();
begin
_context.nextToken.tokenKind := 'その他';
_context.nextToken.tokenString := _context.currChar;
end;
//---------------------------------------------------------------------------------------------------
// 文字種が変わったら、変わる前の文字列情報を返す
//---------------------------------------------------------------------------------------------------
function TTokenReader.getPrevToken():TToken;
begin
//確定済みでなければ 何もしない
if (not _context.prevToken.IsFixed) then
begin
result := nil;
exit;
end;

//1つ前のトークンを未確定状態にして
_context.prevToken.IsFixed := false;

//1つ前のトークンを返す
result := _context.prevToken;
end;
//---------------------------------------------------------------------------------------------------
// 現在位置が、TAB 位置かどうかを返す
//---------------------------------------------------------------------------------------------------
function TTokenReader.TabPos(col:Integer):Boolean;
begin
result := ((col mod 4) = 0);
end;
//---------------------------------------------------------------------------------------------------
// 取得した文字のByte数を返す
//---------------------------------------------------------------------------------------------------
function TTokenReader.getLength(c:WideChar):Integer;
var
s:AnsiString;
begin
s := c;
result := Length(s);
end;
//---------------------------------------------------------------------------------------------------
// 英字か?
//---------------------------------------------------------------------------------------------------
function TTokenReader.IsLetter(c:WideChar):Boolean;
begin
result := (Pos(c, LETTERS) > 0);
end;
//---------------------------------------------------------------------------------------------------
// 数字か?
//---------------------------------------------------------------------------------------------------
function TTokenReader.IsDigit(c:WideChar):Boolean;
begin
result := (Pos(c, DIGITS) > 0);
end;
//---------------------------------------------------------------------------------------------------
// 記号か?
//---------------------------------------------------------------------------------------------------
function TTokenReader.IsSymbol(c:WideChar):Boolean;
begin
result := (Pos(c, SYMBOLS) > 0);
end;

end.