Отвлечёмся немного от "хардкорного программирования" и устройства скриптовой машины.
Вот тут -
http://18delphi.blogspot.ru/2013/11/gui.html - я писал как искать форму и контрол по их имени
А вот тут -
http://18delphi.blogspot.ru/2013/11/gui_4.html - я писал - как в TEdit ввести текст.
А вот тут -
http://18delphi.blogspot.ru/2013/11/gui-rtti.html - я писал как сделать "то же самое" средствами "классического" RTTI.
Напомню, что у нас в итоге получилось:
PROCEDURE "Присвоить текст в текущий контрол" STRING IN aText
OBJECT VAR l_Control
FocusedControl =: l_Control
aText l_Control EditSetText
END //"Присвоить текст в текущий контрол"
Теперь пусть мы опять же хотим поработать с контролом в фокусе, но при этом мы хотим проконтролировать, что контрол в фокусе - НУЖНОГО нам класса - TEdit или TCustomEdit, ну или наследник он него.
Ну скажем - логика теста зависит от этого.
Как это сделать?
Сейчас расскажу.
Напишем для начала два "вспомогательных" слова.
Они - в ПРИНЦИПЕ полезны для GUI-Тестирования.
Но и ещё они помогут нам расширить наши примеры.
Вот эти слова:
OBJECT FUNCTION "Контрол в фокусе"
FocusedControl =: Result
END // "Контрол в фокусе"
WORDWORKER "Присвоить текст в" STRING IN aText
OBJECT VAR l_Control
WordToWork DO =: l_Control
aText l_Control EditSetText
END // "Присвоить текст в"
Про слово WORDWORKER я писал тут -
http://18delphi.blogspot.ru/2013/11/gui_9015.html и тут -
http://18delphi.blogspot.ru/2013/11/gui-2_7.html
Тогда наш пример переписывается так:
PROCEDURE "Присвоить текст в текущий контрол" STRING IN aText
"Присвоить текст {(aText)} в" "Контрол в фокусе"
END //"Присвоить текст в текущий контрол"
Конкатенативный язык (
http://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BD%D0%BA%D0%B0%D1%82%D0%B5%D0%BD%D0%B0%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%8F%D0%B7%D1%8B%D0%BA_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F).
Не правда ли?
Теперь КАК определить, что "Контрол в фокусе" - НУЖНОГО нам класса?
А вот как:
На стороне Delphi определяем слово Inherits:
interface
type
TkwInherits = class(TscriptKeyWord)
protected
procedure DoIt(aContext: TscriptContext); override;
end;//TkwInherits
implementation
procedure TkwInherits.DoIt(aContext: TscriptContext);
var
l_ClassName : String;
function localIsInherits(anObjectClass : TClass): Boolean;
begin
if (anObjectClass = nil) then
Result := false
else
if SameText(anObjectClass.ClassName, l_ClassName) then
Result := true
else
Result := localIsInherits(anObjectClass.ClassParent);
end;
var
l_Object : TObject;
begin
l_Object := aContext.PopObject;
Assert(l_Object <> nil);
l_ClassName := aContext.PopString;
aContext.PushBoolean(localIsInherits(l_Object.ClassType));
end;
initialization
ScriptEngine.RegisterKeyWord(TkwInherits, 'Inherits');
// - далее, если вызов RegisterKeyWord будет в виде RegisterKeyWord(TkwXXX, 'XXX'),
// то я буду просто так и писать RegisterKeyWord(TkwXXX).
//
// Тем более, что это у меня так и реализовано, через использование ClassName
Тогда можно написать такой код скрипта:
'TCustomEdit' "Контрол в фокусе" Inherits IF (
"Присвоить текст {(aText)} в" "Контрол в фокусе"
)
Ну или такой:
'TCustomEdit' "Контрол в фокусе" Inherits ASSERT
"Присвоить текст {(aText)} в" "Контрол в фокусе"
Поработаем ещё немного.
Введём в словарь определение вот такого слова:
CONST "Строка ввода" 'TCustomEdit'
Тогда можно написать так:
"Строка ввода" "Контрол в фокусе" Inherits IF (
"Присвоить текст {(aText)} в" "Контрол в фокусе"
)
Ещё несколько штрихов.
Для начала определим слово ЯВЛЯЕТСЯ:
BOOLEAN WORDWORKERFUNCTION ЯВЛЯЕТСЯ OBJECT IN anObject
WordToWork DO
// - вычисляем "параметр справа"
anObject
// - anObject - "параметр слева"
Inherits
// - зовём уже знакомое нам слово Inherits
=: Result
// - результат нашиз вычислений помещаем в результат функциии ЯВЛЯЕТСЯ
END // ЯВЛЯЕТСЯ
Тогда можно написать так:
"Контрол в фокусе" ЯВЛЯЕТСЯ "Строка ввода" IF (
"Присвоить текст {(aText)} в" "Контрол в фокусе"
)
Ещё определим синоним:
WordAlias "Строкой ввода" "Строка ввода"
Тогда можно написать так:
"Контрол в фокусе" ЯВЛЯЕТСЯ "Строкой ввода" IF (
"Присвоить текст {(aText)} в" "Контрол в фокусе"
)
Ещё немного поработаем над IF.
У нас помимо слова WORDWORKER - есть слово WORDWORKER2.
Которое умеет работать с двумя "параметрами справа".
Применим его.
Напишем следующее:
WORDWORKER2 если
WordToWork1 DO
// - вычислили значение "первого аргумента справа"
IF (
WordToWork2
DO
// - выполняем "второй аргумент справа"
)
END // если
И тогда наш пример можно переписать так:
если ( "Контрол в фокусе" ЯВЛЯЕТСЯ "Строкой ввода")
( "Присвоить текст {(aText)} в" "Контрол в фокусе" )
По-русски? По-моему - по русски?
Теперь если определить слово "то":
Так:
WordAlias то NOP
// - NOP - это слово, которое ничего не делает,
// но может появляться там, где грамматика требует слово
Или так:
WORDWORKER то
WordToWork DO
// - просто вычисляем "аргумент справа"
// т.е. слово "то" в данном случае является proxy-словом
END // то
То мы можем переписать пример так:
если ( "Контрол в фокусе" ЯВЛЯЕТСЯ "Строкой ввода") то
( "Присвоить текст {(aText)} в" "Контрол в фокусе" )
Ну и ещё введём синоним:
WordAlias Если если
// - у нас скриптовая машина - РЕГИСТРОЗАВИСИМАЯ, это - ОСОЗНАННО
Тогда можно написать так:
Если ( "Контрол в фокусе" ЯВЛЯЕТСЯ "Строкой ввода") то
( "Присвоить текст {(aText)} в" "Контрол в фокусе" )
Ну и "на закуску".
Ещё есть слово WORDWORKER2+.
Оно умеет работать с двумя фиксированными "параметрами справа".
И одним опциональным. Предварённым
другим ключевым словом.
Вот пример:
WORDWORKER2+
если
// - "если" - ОСНОВНОЕ ключевое слово. С которого начинается разбор грамматики
иначе
// - "иначе" - ДОПОЛНИТЕЛЬНОЕ ключевое слово. Которого может и не быть.
WordToWork1 DO
// - вычислили значение "первого аргумента справа"
IF (
WordToWork2
DO
// - выполняем "второй аргумент справа"
)
ELSE (
WordToWork2+
DO
// - выполняем третий опциональный аргумент справа, если он передан"
// Если же он НЕ ПЕРЕДАН, то нам в аргументе WordToWork2+ - передадут уже знакомое слово NOP
)
END // если
Тогда можно написать так:
Если ( "Контрол в фокусе" ЯВЛЯЕТСЯ "Строкой ввода") то
( "Присвоить текст {(aText)} в" "Контрол в фокусе" )
иначе
( false ASSERT ) // - тут просто "упадём".
По-моему - "уже перебор информации".
Поэтому - закругляюсь.
Чуть позже я расскажу - как регистрировать РЕАЛЬНЫЕ классы объектов в тестовой машине, чтобы работать с их идентификаторами, а не со "строковыми именами".
И БОЛЕЕ ТОГО - вызывать их КОНСТРУКТОРЫ. Т.е. на самом деле - скриптовая машина - МОЖЕТ обладать возможностью СОЗДАВАТЬ объекты реального приложения.
Неплохо для "тестовой машины", а?
На самом деле - наверное самые пытливые читатели отметили, для себя, что эту скриптовую машину можно использовать не только для тестирования, но и для АВТОМАТИЗАЦИИ и написания макросов пользователя.
Такой вот VBA (
http://ru.wikipedia.org/wiki/VBA) "на коленке".
Если компилировать "скриптовую обвязку" не только под IfDef Test, но и для
конечного клиентского приложения.
И ДАТЬ пользователю возможность загружать и выполнять скрипты.
P.S. Я ещё вас не утомил? А то у меня план статей разросся уже до 30-ти штук.
Кому-то вообще интересно "сделать скриптовую машину своими руками" из "модели для сборки"?
Или пора закругляться?
Учитывая, что каждая новая статья приносит с собой ещё две статьи в "план для написания".