Предыдущая серия была тут - http://18delphi.blogspot.com/2013/11/blog-post_15.html
Теперь я немного попрограммировал и задумался о разборе строки на токены.
Код стал таким:
Это в "некотором роде" - TDD (http://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B0_%D1%87%D0%B5%D1%80%D0%B5%D0%B7_%D1%82%D0%B5%D1%81%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5).
Когда пишешь код и СРАЗУ думаешь о его тестировании.
И о том, "где посмотреть результаты работы кода".
Желательно НА ФАЙЛОВОЙ системе, а НЕ в отладчике.
И если подобные "эталоны" складывать в CVS/SVN, то СРАЗУ можно смотреть РЕГРЕСС (http://ru.wikipedia.org/wiki/%D0%A0%D0%B5%D0%B3%D1%80%D0%B5%D1%81%D1%81%D0%B8%D0%BE%D0%BD%D0%BD%D0%BE%D0%B5_%D1%82%D0%B5%D1%81%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5).
Как это "прикрутить" к DUnit и как "автоматом сравнивать эталоны" - я чуть позже расскажу.
Теперь я немного попрограммировал и задумался о разборе строки на токены.
Код стал таким:
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; 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; 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 = '') f_Token := ''; {$IfDef TestParser} WriteLineToLog(f_Token, f_TokenLog); {$EndIf TestParser} f_CurrentLine := ''; end; function TscriptParser.EOF: Boolean; begin Result := f_EOF; end; end.
Это в "некотором роде" - TDD (http://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B0_%D1%87%D0%B5%D1%80%D0%B5%D0%B7_%D1%82%D0%B5%D1%81%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5).
Когда пишешь код и СРАЗУ думаешь о его тестировании.
И о том, "где посмотреть результаты работы кода".
Желательно НА ФАЙЛОВОЙ системе, а НЕ в отладчике.
И если подобные "эталоны" складывать в CVS/SVN, то СРАЗУ можно смотреть РЕГРЕСС (http://ru.wikipedia.org/wiki/%D0%A0%D0%B5%D0%B3%D1%80%D0%B5%D1%81%D1%81%D0%B8%D0%BE%D0%BD%D0%BD%D0%BE%D0%B5_%D1%82%D0%B5%D1%81%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5).
Как это "прикрутить" к DUnit и как "автоматом сравнивать эталоны" - я чуть позже расскажу.
Комментариев нет:
Отправить комментарий