Предыдущая серия была тут - http://18delphi.blogspot.ru/2013/11/3.html
И ещё немного попрограммировал и родилось вот что:
Тестовый пример:
По-моему все ГРАНИЧНЫЕ условия - перебраны.
И ещё немного попрограммировал и родилось вот что:
unit Script.Parser;
interface
uses
Classes,
CoreObject
;
{$IfNDef NoTesting}
{$Define TestParser}
{$EndIf NoTesting}
type
TscriptParser = class(TCoreObject)
private
f_Stream : TStream;
{$IfDef TestParser}
f_GetCharLog : TStream;
// - сразу думаем о тестировании, в этот поток будем выводить
// результат работы функции GetChar
f_ReadLnLog : TStream;
// - сразу думаем о тестировании, в этот поток будем выводить
// результат работы функции ReadLn
f_TokenLog : TStream;
// - сразу думаем о тестировании, в этот поток будем выводить
// результат работы функции NextToken
{$EndIf TestParser}
f_EOF : Boolean;
f_CurrentLine : String;
f_PosInCurrentLine : Integer;
f_Token : String;
{$IfDef TestParser}
private
procedure WriteLineToLog(const aLine: AnsiString; aLog: TStream);
{$EndIf TestParser}
protected
procedure Cleanup; override;
function ReadLn: String;
protected
function GetChar(out aChar: AnsiChar): Boolean;
public
constructor Create(const aStream : TStream; const aFileName : String); overload;
constructor Create(const aFileName : String); overload;
function EOF: Boolean;
procedure NextToken;
public
property TokenString: String
read f_Token;
end;//TscriptParser
implementation
uses
System.SysUtils
;
constructor TscriptParser.Create(const aStream : TStream; const aFileName : String);
begin
inherited Create;
f_PosInCurrentLine := 1;
f_EOF := false;
f_Stream := aStream;
{$IfDef TestParser}
f_GetCharLog := TFileStream.Create(aFileName + '.GetChar.log', fmCreate);
f_ReadLnLog := TFileStream.Create(aFileName + '.ReadLn.log', fmCreate);
f_TokenLog := TFileStream.Create(aFileName + '.Token.log', fmCreate);
{$EndIf TestParser}
end;
constructor TscriptParser.Create(const aFileName : String);
var
l_FileName : String;
begin
l_FileName := ExtractFilePath(ParamStr(0)) + '\' + aFileName;
Create(TFileStream.Create(l_FileName, fmOpenRead), l_FileName);
end;
procedure TscriptParser.Cleanup;
begin
FreeAndNil(f_Stream);
{$IfDef TestParser}
FreeAndNil(f_TokenLog);
FreeAndNil(f_GetCharLog);
FreeAndNil(f_ReadLnLog);
{$EndIf TestParser}
inherited;
end;
function TscriptParser.GetChar(out aChar: AnsiChar): Boolean;
begin
if (f_Stream.Read(aChar, SizeOf(aChar)) = SizeOf(aChar)) then
begin
Result := true;
{$IfDef TestParser}
f_GetCharLog.Write(aChar, SizeOf(aChar));
{$EndIf TestParser}
end
else
Result := false;
end;
{$IfDef TestParser}
procedure TscriptParser.WriteLineToLog(const aLine: AnsiString; aLog: TStream);
const
cEOL : AnsiString = #13#10;
begin
aLog.Write(@aLine[1], Length(aLine));
aLog.Write(@cEOL[1], Length(cEOL));
end;
{$EndIf TestParser}
function TscriptParser.ReadLn: String;
{$IfDef TestParser}
var
l_Result : AnsiString;
{$EndIf TestParser}
var
l_Char : AnsiChar;
l_Line : String;
l_LineCommentPos : Integer;
begin
{$IfDef TestParser}
try
{$EndIf TestParser}
try
l_Line := '';
while GetChar(l_Char) do
begin
if (l_Char = #13) then
begin
if GetChar(l_Char) then
begin
if (l_Char = #10) then
begin
Result := l_Line;
Exit;
end//l_Char = #10
else
Assert(false, 'Что-то пошло не так, после символа 13 нет символа 10');
end//GetChar(l_Char)
else
Assert(false, 'Что-то пошло не так, после символа 13 сразу конец файла');
end;//l_Char = #13
l_Line := l_Line + l_Char;
end;//while GetChar(l_Char)
f_EOF := true;
Result := l_Line;
finally
l_LineCommentPos := Pos('//', Result);
if (l_LineCommentPos > 0) then
begin
Delete(Result, l_LineCommentPos, Length(Result) - l_LineCommentPos + 1);
end;//l_LineCommentPos > 0
end;//try..finally
{$IfDef TestParser}
finally
WriteLineToLog(Result, f_ReadLnLog);
end;//try..finally
{$EndIf TestParser}
end;
procedure TscriptParser.NextToken;
const
cWhiteSpace = [#32,#9];
begin
f_Token := '';
while true do
begin
if (f_PosInCurrentLine >= Length(f_CurrentLine)) then
begin
// - Типа текущая строка ВСЯ обработана
f_CurrentLine := '';
f_PosInCurrentLine := 1;
end;//f_PosInCurrentLine > Length(f_CurrentLine)
while(f_CurrentLine = '') do
begin
f_CurrentLine := ReadLn;
if (f_CurrentLine = '') then
if f_EOF then
Exit;
end;//while(f_NextToken = '')
while (f_PosInCurrentLine <= Length(f_CurrentLine)) do
if (f_CurrentLine[f_PosInCurrentLine] in cWhiteSpace) then
Inc(f_PosInCurrentLine)
else
break;
// - Тут пропускаем пустые символы
if (f_PosInCurrentLine <= Length(f_CurrentLine)) then
break;
end;//while true
while (f_PosInCurrentLine <= Length(f_CurrentLine)) do
if (not (f_CurrentLine[f_PosInCurrentLine] in cWhiteSpace)) then
begin
f_Token := f_Token + f_CurrentLine[f_PosInCurrentLine];
Inc(f_PosInCurrentLine);
end//not (f_CurrentLine[f_PosInCurrentLine] in cWhiteSpace)
else
break;
// - Тут накапливаем НЕ пустые символы
{$IfDef TestParser}
WriteLineToLog(f_Token, f_TokenLog);
{$EndIf TestParser}
//f_CurrentLine := '';
end;
function TscriptParser.EOF: Boolean;
begin
Result := f_EOF AND (f_CurrentLine = '');
end;
end.
Тестовый пример:
DoNothing // - пока ничего не делаем // - и тут тоже пока ничего не делаем DoNothing1 DoNothing2 // - и тут пока ничего не делаем, но проверяем ДВА токена DoNothing3 DoNothing4 // - и тут пока ничего не делаем, но проверяем ДВА токена с пробелом в НАЧАЛЕ DoNothing5 DoNothing6// - и тут пока ничего не делаем, но проверяем ДВА токена БЕЗ пробелов в конце DoNothing7// - пока ничего не делаем, но проверяем ОДИН токен БЕЗ пробелов в конце DoNothing8 DoNothing9 DoNothing10 // - и тут пока ничего не делаем, но проверяем ТРИ токена DoNothing11 DoNothing12 DoNothing13 DoNothing14 // - и тут пока ничего не делаем, но проверяем ЧЕТЫРЕ токена // - обрабатываем "чисто пустую строку" 'aString1'// - пытаемся обработать строку 'aString2 with spaces'// - пытаемся обработать строку с пробелами // - обрабатываем "чисто пустую строку" в конце файла
По-моему все ГРАНИЧНЫЕ условия - перебраны.
Комментариев нет:
Отправить комментарий