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