Предыдущая серия была тут - 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;
Продолжение ОБЯЗАТЕЛЬНО следует. Я уже написал план из ещё восьми статей. Надеюсь "в конце пути" - заинтересованные читатели смогут сами собрать скриптовую/тестовую машину из запчастей.
Комментариев нет:
Отправить комментарий