Описание формата скриптов, которые мы в будущем будем парсить. На данный момент парсер выглядит так:
unit Script.Parser; interface uses Classes, Core.Obj, Script.Interfaces ; {$IfNDef NoTesting} {$Define TestParser} {$EndIf NoTesting} type TscriptParser = class(TCoreObject) private f_Stream : TStream; f_EOF : Boolean; f_CurrentLine : String; f_PosInCurrentLine : Integer; f_Token : String; f_TokenType : TscriptTokenType; f_CurrentLineNumber : Integer; protected procedure Cleanup; override; function ReadLn: String; protected function GetChar(out aChar: AnsiChar): Boolean; public constructor Create(const aStream : TStream); overload; constructor Create(const aFileName : String); overload; class function Make(const aFileName : String): IscriptParser; {* - Фабрика интерфейса IscriptParser. } function EOF: Boolean; {* - Достигнут конец входного потока. } procedure NextToken; {* - Выбрать следующий токен из входного потока. } public property TokenString: String read f_Token; {* - текущий токен. } property TokenType: TscriptTokenType read f_TokenType; {* - тип текущего токена. } end;//TscriptParser implementation uses System.SysUtils {$IfDef TestParser} , Testing.Engine {$EndIf TestParser} ; type TscriptParserContainer = class(TCoreInterfacedObject, IscriptParser) private f_Parser : TscriptParser; private function Get_TokenType: TscriptTokenType; function Get_TokenString: String; function Get_CurrentLineNumber: Integer; procedure NextToken; {* - Выбрать следующий токен из входного потока. } protected procedure Cleanup; override; public constructor Create(aParser: TscriptParser); class function Make(aParser: TscriptParser): IscriptParser; end;//TscriptParserContainer constructor TscriptParserContainer.Create(aParser: TscriptParser); begin Assert(aParser <> nil); inherited Create; f_Parser := aParser; end; class function TscriptParserContainer.Make(aParser: TscriptParser): IscriptParser; begin Result := TscriptParserContainer.Create(aParser); end; procedure TscriptParserContainer.Cleanup; begin FreeAndNil(f_Parser); inherited; end; function TscriptParserContainer.Get_TokenType: TscriptTokenType; begin Result := f_Parser.TokenType; end; function TscriptParserContainer.Get_TokenString: String; begin Result := f_Parser.TokenString; end; function TscriptParserContainer.Get_CurrentLineNumber: Integer; begin Result := f_Parser.f_CurrentLineNumber; end; procedure TscriptParserContainer.NextToken; {* - Выбрать следующий токен из входного потока. } begin f_Parser.NextToken; end; // TscriptParser constructor TscriptParser.Create(const aStream : TStream); begin inherited Create; f_PosInCurrentLine := 1; f_EOF := false; f_Stream := aStream; end; constructor TscriptParser.Create(const aFileName : String); var l_FileName : String; begin l_FileName := aFileName; if (ExtractFilePath(l_FileName) = '') then l_FileName := ExtractFilePath(ParamStr(0)) + '\' + l_FileName; Create(TFileStream.Create(l_FileName, fmOpenRead)); end; class function TscriptParser.Make(const aFileName : String): IscriptParser; {* - Фабрика интерфейса IscriptParser. } begin Result := TscriptParserContainer.Make(Self.Create(aFileName)); end; procedure TscriptParser.Cleanup; begin FreeAndNil(f_Stream); 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} TtestEngine.CurrentTest.SocketMetric(TtestSocket.Create(Self, 'GetChar')).PutValue(aChar); // - снимаем показания с текущей контрольной точки {$EndIf TestParser} end else Result := false; end; function TscriptParser.ReadLn: String; {$IfDef TestParser} var l_Result : AnsiString; {$EndIf TestParser} var l_Char : AnsiChar; l_Line : String; l_LineCommentPos : Integer; begin Inc(f_CurrentLineNumber); {$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 TtestEngine.CurrentTest.SocketMetric(TtestSocket.Create(Self, 'ReadLn')).PutValue(Result); // - снимаем показания с текущей контрольной точки end;//try..finally {$EndIf TestParser} end; procedure TscriptParser.NextToken; const cQuote = #39; cWhiteSpace = [#32,#9]; begin f_TokenType := script_ttUnknown; f_Token := ''; try 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 // Тут накапливаем НЕ пустые символы: if (f_CurrentLine[f_PosInCurrentLine] = cQuote) then begin f_TokenType := script_ttString; Inc(f_PosInCurrentLine); while (f_PosInCurrentLine <= Length(f_CurrentLine)) do if (f_CurrentLine[f_PosInCurrentLine] <> cQuote) then begin f_Token := f_Token + f_CurrentLine[f_PosInCurrentLine]; Inc(f_PosInCurrentLine); end//not (f_CurrentLine[f_PosInCurrentLine] in cWhiteSpace) else begin Inc(f_PosInCurrentLine); break; end;//f_CurrentLine[f_PosInCurrentLine] <> cQuote end//f_CurrentLine[f_PosInCurrentLine] = '' else begin f_TokenType := script_ttToken; 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; end;//else //f_CurrentLine := ''; finally if (Self.f_TokenType = script_ttUnknown) then if Self.EOF then f_TokenType := script_ttEOF; {$IfDef TestParser} case f_TokenType of script_ttEOF: TtestEngine.CurrentTest.SocketMetric(TtestSocket.Create(Self, 'NextToken')).PutValue('Конец файла'); script_ttString: TtestEngine.CurrentTest.SocketMetric(TtestSocket.Create(Self, 'NextToken')).PutValue('Single quoted string:'); script_ttToken: // - ничего не делаем ; else Assert(false, 'Что-то пошло не так'); end;//case f_TokenType TtestEngine.CurrentTest.SocketMetric(TtestSocket.Create(Self, 'NextToken')).PutValue(f_Token); // - снимаем показания с текущей контрольной точки {$EndIf TestParser} end;//try..finally end; function TscriptParser.EOF: Boolean; begin Result := f_EOF AND (f_CurrentLine = ''); end; end.
Комментариев нет:
Отправить комментарий