Здравствуйте, не могу найти ссылку на скачивание курса «Визуальное моделирование: теория и практика»
Номер платежа 6400454020565 |
Пример предметно-ориентированного визуального языка
Пример визуального языка
В предыдущих лекциях уже были рассмотрены некоторые визуальные языки - UML и BPMN. Однако они слишком большие и сложные, чтобы на их примере учиться создавать собственные DSL. Поэтому в этой лекции будет представлен небольшой, "игрушечный" визуальный язык. Он позволяет описать множество компонент с атрибутами и методами, набор сообщений, которыми могут обмениваться экземпляры компонент, а также конечные автоматы, описывающие поведение каждой компоненты. В дальнейшем я буду называть этот язык SCL (Simple Component Language). Пример SCL-модели показан на рис. 12.1.
На рис. 12.1, а представлены три компоненты - Client, Server и Monitor, - а также список сигналов, которыми они могут обмениваться. Экземпляры компонент взаимодействуют друг с другом через посылку/прием сигналов из этого списка. В SCL для упрощения у компонент нет интерфейсов, а все сигналы глобальные. Все компоненты их "видят", а значит, могут использовать.
Поведение компонент представляется с помощью конечных автоматов - пример для компоненты Server показан на рис. 12.1, б. Монитор (экземпляр компоненты Monitor) создается автоматически, при запуске всей системы, о чем свидетельствует на рис. 12.1, а первый параметр после его имени, равный единице. Монитор создает экземпляр компоненты Server (далее - сервер) и экземпляр компоненты Client (далее - клиент). Экземпляры этих компонент создаются монитором, а не по умолчанию, при запуске системы. Поэтому первый параметр после имен этих компонент равен нулю. В этой небольшой "игрушечной" системе может существовать не более одного клиента и сервера, на что указывают вторые параметры после имен этих компонент, равные единице.
На рис. 12.1, б представлено поведение сервера. Сервер, после того, как его создаст монитор, оказывается в состоянии Start и вызывает свою процедуру Init(). Эта процедура выполняет все служебные действия по инициализации сервера. Далее он переходит в состояние Idle, в котором готов обслужить запросы клиента. При получении такого запроса (сигнал Request) сервер оповещает клиента с помощью сигнала Respond о том, что запрос до него дошел и требуется дополнительная информация. Клиент посылает либо сигнал Info1, либо сигнал Info2. В зависимости от этого сервер или принимает запрос на обработку, или нет. В первом случае он посылает клиенту сигнал Accept и вызывает свою процедуру-обработчик запроса AcceptHandle(), во втором случае он посылает клиенту сигнал Reject. В обоих случаях сервер переходит в состояние Idle и готов обрабатывать следующие запросы. В этом же состоянии сервер может обработать сигнал монитора Terminate, присылаемый ему при необходимости прекратить работу. В этом случае, для корректного завершения своей работы сервер вызывает процедуру TerminateHandle(). После ее завершения сервер переходит в состояние End и окончательно завершается. Нетрудно продолжить этот пример, дописав конечные автоматы для клиента и монитора.
Наверное, про этот язык понятно все или почти все прямо из приведенного выше примера. Однако так происходит потому, что SCL очень прост. Если в него добавить связи между компонентами, параметры сигналов, ветвления в переходах и т. д., то он бы существенно усложнился и возникла бы потребность в том, чтобы создать его точное описание - определить синтаксис, семантику и прагматику. Это и будет проделано ниже в учебных целях.
Абстрактный синтаксис в форме грамматик Бэкуса-Наура
Здесь не будет дано точных определений формального языка, грамматики, грамматики в форме Бэкуса-Наура. Подробную информацию по этим вопросам можно получить в книгах [12.4], [12.5]. Я представлю лишь объяснения на примерах, достаточные для того, чтобы разобраться с грамматикой SCL и создавать что-то подобное им самостоятельно.
Грамматика языка в форме Бэкуса-Наура задает структуру текстов, которые можно создавать с помощью этого языка. Строгое определение этой структуры позволяет формальную обработку таких текстов - обнаружение синтаксических ошибок, валидацию более сложных ограничений, генерацию программного кода и т.д.
Структура текстов определяется иерархически, в виде правил. В случае с SCL любой текст (визуальная модель) должен состоять из имени модели, списка сигналов и набора компонент:
<model> :: = <model name><signal_list>+ <component>+
Конструкция <model> называется головной - с нее грамматика начинается. Список сигналов обозначается как <signal_list>. Таких списков, равно как и компонент (<component>), в модели может быть сколько угодно, но обязательно должен быть хоть один, что изображается значком + рядом с тем элементом грамматики, которого "может быть один или много". Список сигналов устроен следующим образом. Он состоит из строк, обозначающих сигналы, которые могут быть посланы и получены экземплярами компонент:
<signal_list> :: = <signal>+ <signal> :: = <signal name>
Конструкции, подобные <signal name> и <model name> обычно называются идентификаторами. Они являются именами (переменных, процедур, сигналов и пр.), задаваемыми пользователями языка при разработке модели. В случае языка SCL идентификаторы являются терминалами, так как не подвергаются дальнейшему разбору в ее грамматике. А нетерминалами называются конструкции, которые являются составными и поддаются дальнейшему разбору. Примерами нетерминалов являются <model>, <signal_list>, <signal>, <component>. В SCL-грамматике идентификаторы обозначаются как обычные нетерминалы, но с добавлением подчеркивания имени.
Разобранный выше фрагмент грамматики языка SCL можно представить деревом, как показано на рис. 12.2.
Продолжим изучать грамматику языка SCL. Конструкция <component> определяется следующим образом:
<component> :: = <component name> <parameters> <method>* [<statechart>] <attribute>*
Видно, что компонента состоит из имени ( <component name> ), параметров ( <parameters> ), набора методов ( <method> ) и атрибутов ( <attribute> ), а также конечного автомата ( <statechart> ). Сразу за нетерминалами <method> и <attribute> следует значок '* ', который указывает на то, что как методов, так и атрибутов в компоненте может быть произвольное количество, в том числе и не быть вовсе. Последняя оговорка отличает символ '* ' от '+ '. Нетерминал <statechart> взят в квадратные скобки. Это означает, что его может не быть, т. е. конечный автомат может у компоненты отсутствовать.
У компоненты есть два параметра, следующих в круглых скобках за ее именем. Первый параметр указывает на то, сколько экземпляров компонент создается при запуске системы, второй - сколько экземпляров одновременно может существовать в системе:
<parameters>::= (<init>, <num>)
Круглые скобки и запятая в представленном выше правиле являются новым видом терминальных элементов, в отличие от угловых и квадратных скобок, которые являются частью нотации самой грамматики. Круглые скобки и другие подобные элементы появляются в связи с тем, что графические символы в SCL могут быть "нагружены" текстом, посредством которого определяются различные свойства графических конструкций (т. е. текст для SCL - это не просто строка).
Конечный автомат включает в себя ссылку на компоненту, к которой он относится ( <component_ref> ), а также набор состояний ( <state>+ ):
<statechart> ::= <component_ref> <state>+
Прокомментируем элемент <component_ref>. Эта конструкция похожа на идентификатор, т. е. тоже является строковым значением и терминалом, но смысл у нее иной. Она ссылается на другую, существующую в модели, конструкцию. Будем называть такие конструкции ссылками. Они, так же как и идентификаторы, являются терминалами в SCL-грамматике. Ссылки будут обозначаться именами, оканчивающимися на ref.
Состояние устроено так:
<state> ::= <state name> <transition>*
Видно, что у него есть имя, а также исходящие переходы, которые переводят экземпляры данной компоненты из этого состояния в другие.
Переход, в свою очередь, устроен следующим образом:
<transition> :: = [<input>]/<action> {; <action>}* <target_state_ref>
Он инициируется определенным сигналом, который принимается и обрабатывается компонентой в этом состоянии (конструкция <input> ), содержит в себе ряд действий ( <action> ) и завершается новым состоянием, в которое экземпляр компоненты переходит, обработав данный входной сигнал (конструкция <target_state_ref> ). В конструкции <transition> содержатся два терминала нового типа, которые еще не рассматривались в этой лекции. Это значок '/ ', который отделяет входной сигнал от действий по его обработке, и символ '; ', разделяющий действия в переходе в том случае, если их более одного. Эти терминалы относятся к тому же типу, что и круглые скобки. Отметим, что фигурные скобки, так же как и квадратные, являются частью грамматики. Они группируют аргумент для операции, обозначаемой символом '*': следующее действие в переходе должно быть отделено символом ';' от предыдущего, в то время как последнее действие не должно иметь после себя этот разделитель.
Действие в переходе может быть либо посылкой сигнала другой компоненте, либо вызовом метода данного объекта:
<action> :: = <method_invocation>| <send>
Значок '|' SCL-грамматики обозначает альтернативу.
Ниже представлена вся SCL-грамматика целиком, с некоторыми добавлениями, которые мне кажутся очевидными.
<model> :: = <model name><signal list>+ <component>+ <component> :: = <component name> <parameters> <method>* [<statechart>] <attribute>* <parameters>::= (<init>, <num>) <statechart > ::= <component_ref><state>+ <state> ::= <state name> <transition>* <transition> :: = [<input>]/<action> (; <action>)* <target_state> <action> :: = <method_invocation>| <send> <method> :: = < method name>() <attribute> ::= <attribute name> <attribute_type> <attribute_type> :: = <attribute type name> <target_state> :: = <state_ref> <send>::= <signal_ref> <input>::= <signal_ref> <method_invocation > :: = <method_ref>()