вторник, 5 ноября 2013 г.

GUI-тестирование "по-русски". Ремарка о том, "как это всё устроено"

Предыдущая серия была тут - http://18delphi.blogspot.com/2013/11/gui_2510.html

Теперь пару слов о том как это устроено.

(Пишу "с листа" по-памяти)

Есть парсер, который разбивает входной поток на токены.

Интерфейс парсера примерно такой:
type
 TscriptTokenType = (ttInteger, ttString, ttKeyWord);

 IscriptParser = interface
  function IsEOF : Boolean;
  procedure NextToken;
  function TokenType: TscriptTokenType;
  function IntegerToken: Integer;
  function StringToken: String;
  function KeyWordToken: TscriptKeyWord;
 end;//IscriptParser

Есть скриптовая машина - которая парсит текст при помощи парсера и компилирует его.

Основная процедура скриптовой машины примерно такая:
type
  TscriptContext = class
   function PopInteger: Integer;
   procedure PushInteger(aValue : Integer);
   function PopString: String;
   procedure PushString(const aValue : String);
   function PopObject: TObject;
   procedure PushObject(aValue : TObject);
   ...
  end;//TscriptContext
  
  TscriptKeyWord = class
   function IsImmediate: Boolean; virtual;
    {* - является ли слово НЕПОСРЕДСТВЕННО выполняемым? Таким как PROCEDURE, FUNCTION, VAR, CONST etc. }
   procedure DoIt(aContext: TscriptContext); virtual; abstract;
    {* - собственно код выполнения слова. }
  end;//TscriptKeyWord

  TscriptCode = class
   procedure CompileInteger(aValue: Integer);
   procedure CompileString(const aValue: String);
   procedure CompileKeyWord(aValue: TscriptKeyWord);
   procedure Run; // - выполняет скомпилированный код
   procedure RunInContext(aContext: TscriptContext); // - выполняет скомпилированный код в указанном контексте
   ...
  end;//TscriptCode
  
  TscriptCompileContext = class(TscriptContext)
   property Parser: IscriptParser;
    {* - Текущий парсер. }
   property Code: TscriptCode;
    {* - Текущий компилируемый код. }
  end;//TscriptCompileContext

procedure TscriptEngine.Compile(const aParser : IscriptParser; aCode : TscriptCode);
var
 l_Context : TscriptCompileContext;
begin
 l_Context := TscriptCompileContext.Create;
 try
  l_Context.Parser := aParser; 
  // - Это чтобы слова НЕПОСРЕДСТВЕННОГО выполнения могли выполняться и управлять при этом текущим парсером
  l_Context.Code := aCode; // - место куда компилируется код
  while not aParser.IsEOF do
  begin
   Case aParser.TokenType of
    ttInteger : 
     aCode.CompileInteger(aParser.IntegerToken); // - скомпилируем целое значение
    ttStringr : 
     aCode.CompileString(aParser.StringToken); // - скомпилируем строковое значение
    ttKeyWord :
    begin
     if aParser.KeyWordToken.IsImmediate then
      // - слово НЕПОСРЕДСТВЕННОГО выполнения, такое как PROCEDURE, FUNCTION, VAR, CONST etc. 
      //   ("пользовательские" слова - тоже могут быть такими)
      aParser.KeyWordToken.DoIt(l_Context) 
      // - выполним данное слово в контексте компиляции, 
      //   при этом такие слова могут управлять как парсером, так и компилируемым кодом
     else
      aCode.CompileKeyWord(aParser.KeyWordToken); // - скомпилируем вызов слова
    end;//ttKeyWord
    else
     Assert(false); // - мало ли, что ещё появится (а оно - кстати есть)
   end;//Case aParser.TokenType
   aParser.NextToken;
  end;//while not aParser.IsEOF
 finally
  FreeAndNil(l_Context);
 end;//try..finally
end;//TscriptEngine.Compile

И собственно процесс компиляции и запуска:

procedure RunCode(const aParser : IscriptParser);
var
 l_Code : TscriptCode;
 l_ScriptEngine : TscriptEngine;
begin
 l_Code := TscriptCode.Create;
 try
  l_ScriptEngine := TscriptEngine.Create;
  try
   l_ScriptEngine.Compile(aParser, l_Code);
   // - компилируем код из входного потока
  finally
   FreeAndNil(l_ScriptEngine);
  end;//try..finally
  l_Code.Run; 
  // - можно выполнить код - сразу, 
  //   но в принципе - можно его запомнить куда-нибудь во внешний объект 
  //   и выполнять несколько раз
 finally
  FreeAndNil(l_Code);
 end;//try..finally
end;

Продолжение ОБЯЗАТЕЛЬНО следует. Я уже написал план из ещё восьми статей. Надеюсь "в конце пути" - заинтересованные читатели смогут сами собрать скриптовую/тестовую машину из запчастей.

Комментариев нет:

Отправить комментарий