ИЗВЕСТИЯ
ПЕНЗЕНСКОГО ГОСУДАРСТВЕННОГО ПЕДАГОГИЧЕСКОГО УНИВЕРСИТЕТА имени В. Г. БЕЛИНСКОГО ФИЗИКО-МАТЕМАТИЧЕСКИЕ И ТЕХНИЧЕСКИЕ НАУКИ № 13 (17) 2009
IZVESTIA
PENZENSKOGO GOSUDARSTVENNOGO PEDAGOGICHESKOGO UNIVERSITETA imeni V. G. BELINSKOGO PHYSICAL, MATHEMATICAL AND TECHNICAL SCIENCES № 13 (17) 2009
УДК 004.424
РЕАЛИЗАЦИЯ ПРЕДМЕТНО-ОРИЕНТИРОВАННЫХ ЯЗЫКОВ СРЕДСТВАМИ ФРЕЙМВОРКА Xtext
© М. В. ЖУКОВ
Пензенский государственный педагогический университет имени В.Г. Белинского, кафедра прикладной математики и информатики e-mail: [email protected]
Жуков М. В. - Реализация предметно-ориентированных языков средствами Фреймворка Xtext // Известия
ПГПУ им. В. Г. Белинского. 2009. № 13 (17). С. 98-102. - Предметно-ориентированные языки (DSL - Domain Specific Language) становятся все более популярной темой в сфере программной инженерии. Данная статья представляет собой руководство по созданию DSL с помощью фреймворка Xtext, который позволяет за короткое время создать не только синтаксический анализатор для разрабатываемого языка, но и удобный текстовый редактор, проверяющий синтаксические и семантические ограничения языка; компилятор или интерпретатор для DSL. Ключевые слова: предметно-ориентированный язык, синтаксический анализатор, интерпретатор, компилятор.
Zhukov M. V. - The realization of object-oriented languages by means of framework Xtext// Izv. Penz. gos. peda-gog. univ. im.i V. G. Belinskogo. 2009. № 13 (17). P. 98-102. - Domain Specific Languages are a hot topic nowadays. In this article we will show you how to create your own DSL with the Xtext framework. Xtext gives you opportunity to create in a brief space of time not only a parser for DSL, but: a comfortable text editor, checking syntactic and semantic constrains; a compiler and an interpreter for DSL.
Keywords: domain specific language, parser, interpreter, compiler.
Предметно-ориентированный язык - это язык, ориентированный на решение частных, ограниченных рамками предметной области, задач в отличие от языков общего назначения (GPL - General Purpose Language), которые предназначены для решения широкого круга задач, возникающих перед программистом.
Несмотря на то, что множество статей было написано о том, как разрабатывался тот или иной DSL, однако очень мало литературы посвященной методологии их разработки. Наибольшего внимания заслуживает работа [2], содержащая описание следующих фаз:
- принятие решения о разработке DSL;
- анализ предметной области;
- разработка DSL;
- реализация DSL.
Одна из сложностей при создании DSL - его реализация. Этому вопросу и посвящена данная статья, в которой мы покажем, как легко реализовать свой собственный DSL с помощью Eclipse Modeling Project (EMP).
Среди DSL различают внутренние (internal) DSL, написанные на том же языке, в рамках которого они будут использоваться, и внешние (external) DSL, обладающие собственным синтаксисом и требующие
написание синтаксического анализатора для их обработки [1]. В то время как реализация internal DSL не составляет особо большого труда, реализация external DSL не так тривиальна. Поэтому мы рассмотрим реализацию именно external DSL средствами фреймворка Xtext.
Мы начнем с определения грамматики нашего DSL в Xtext. Затем, используя средства Xtext, создадим синтаксический анализатор и текстовый редактор для нашего языка. После этого покажем, как можно сгенерировать код, используя язык Xpand, и в заключение рассмотрим, как улучшить редактор с помощью Xtend.
Создание нового Xtext проекта. Для создания Xtext проекта вам необходимо:
- запустить Eclipse 3.4 c установленным oAW 3.4 (openArchitectureWare);
- выбрать Fil > New > Project > openArchitectureWare > XtextProject;
- указать имя создаваемого языка (у нас это dbmanipulator);
- нажать Finish.
В результате будет создано три проекта:
- dbmanipulator.dsl - здесь будет объявлена грамматика для нашего DSL. После запуска Xtext ге-
нератора, сюда же будет добавлен синтаксический анализатор DSL;
- dbmanipulator.dsl.editor- будет содержать DSL редактор;
- dbmanipulator.dsl.generator - здесь будет содержаться описание процедуры трансформации DSL.
Определение грамматики языка. В качестве примера мы рассмотрим DSL, предназначенный для генерации скрипов создания MySQL транзакций. Для каждой транзакции необходимо будет указать список параметров и тело. Телом транзакции могут быть либо SQL-запросы, либо последовательный вызов других транзакций.
Специфицируем грамматику языка с помощью Xtext грамматики. Для этого откройте файл dbmanipu-lator.dsl/src/dbmanipulator.xtxt и наберите текст, представленный на рис. 1:
- Body (строка 20) представляет собой либо композицию [1:n] элементов ActionCall, либо текст произвольного содержания;
- правило ActionCall определяет элемент ActionCall начинающийся с имени одного из уже объявленных элементов Action (ограничение задается с помощью конструкции [Action]) и списка идентификаторов.
Создание DSL-редактора. После того как была специфицирована грамматика языка, мы можем сразу же создать текстовый редактор для DSL: в контекстном меню Xtext редактора выберите пункт "Generate Xtext Artifacts".
Использование DSL-редактора. Проект dbmanipulator.dsl.editor построен на Eclipse plug-in архитектуре. Самый простой способ запустить его - выбрать из контекстного меню RunAs > Eclipse Application. Для тестирования DSL-редактора, в появившемся Eclipse Application, создадим новый проект (TestMyDSL) File > New > Other... > Xtext DSL Wizards > dbmanipulator Proj-
- правило Program (строки 1-5) говорит о том, что текст программы на нашем DSL должен начинаться с ключевого слова "Begin" и оканчиваться словом "End". Между ними может располагаться [0:n] элементов записанных по правилу Element;
- правило Action (строки 12-15) указывает, что элемент Action должен начинаться с фразы " Create __ac-tion", далее в угловых скобках указывается уникальное имя Action, в круглых скобках через запятую список аргументов (Argument), если он есть (знак ? - означает кратность [0:1]), и тело (Body) элемента Action в фигурных скобках;
- Argument (строки 17-18) состоит из имени аргумента и его типа, указанного через двоеточие. (Допустимы два типа аргументов: "Integer" и "Varchar(...)", см. строки 9-10);
ect. Чтобы продемонстрировать возможности сгенерированного редактора наберите текст, приведенный на рис. 2. Вы увидите, что он предоставляет следующие возможности:
- цветовую разметку синтаксиса;
- автоматическое завершение фраз (используйте CTRL-Space);
- навигацию (используйте F3, когда курсор находится на идентификаторе);
- свертывание элементов;
- проверку синтаксиса и маркировку ошибок.
Генерация кода. Теперь, когда DSL разработан,
нужно сделать так, чтобы компьютер смог понимать его. Для этого существует два подхода: написать компилятор (также называемый генератором), который трансформирует выражения DSL в другой язык, либо разработать интерпретатор. Xtext позволяет создавать как генератор (Xpand), так и интерпретатор (Ecore-model). Однако мы остановимся только на первом варианте - генерации кода.
1- Program:
2" "Begin"
3 [elements += Element)*
4 "End";
5
б- Element:
7 Action;
В
9- String BaseType:
10 "Integer" | "Varchar" "(" INT "J";
11
12" Action:
13- "Create action" "<" name = ID ">"
11 ("with arguments:" "(" (args += Argument)("," args += Argument)* ")" )?
15 "{ " body = Body "}";
16
17" Argument:
1В name = ID ":" type = BaseType;
19
20 Body: ((actionCalls += ActionCall)+ | code = STRIHG);
21
22 ActionCall: action = TActionl "(" ((args += ID)("," args += ID)*)? »)»";»;
Рис. 1. Описание грамматики DSL
1 Begin
Create_action <AddErnployee> with_arguments: ( name:Varchat(45), age:Integer )
4 {
5 "INSERT INTO Emp1oyee(name, age) VALUES (name, age);"
_6 }
Create_action -iUpdateEitip loyee> with_arguments:
(oldName :Varchar (15) , newName :Varchar(45) , age:Integer)
9 {
10- "UPDATE Employee SET name=newName, age=age
11 WHERE name = o ldName; " __}
13~ Create_action <AddAndUpdateEinp loyee> with_arguments:
14 (name :Varchar (45) , age : Integer, newName :Varchar (45) )
15 {
AddEinployee (name 9 AddAndUpdateEmployee UpdateErnployee (n e AddEmployee
4 UpdateErnployee
Рис. 2. Редактор dbmanipulator
Рассмотрим, как с помощью языка Xpand написать В workspace был сгенерирован проект dbmanipulator.dsl. шаблон, на основе которого генерируется SQL-скрипт generator, который содержит файл Main.xpt - Xpand-создания транзакции для каждого элемента Action. шаблон. Измените его так, как показано на рис. 3.
1 kIMPORT dbmanipulator»
2 3 -DEFINE main FOR Program»
4 «EXPAND code F0REACH elements . typeSelect (Action) »
5 С. EHDDEFIHE»
и 7 «DEFINE code FOR Action»
8 «FILE name + ".sql"-»
9DELIHITER Î5
10 DROP PROCEDURE IF EXISTS 'dsl" . '«name»' ? i
11 CREATE PROCEDURE %dsl%.'«name»'(«F0REACH args AS arg ITERATOR it-»
12 «arg.naine» «arg.type»«IF ! it.last Itérât ion», «ENDIF-»
13 «EHDF0REACH» )
14 BEGIN
15 «EXPAND body FOR this.body-»
16 END ÎÎ
17DELIHITER ;
18 «EHDFILE-»
19 «EHDDEFIHE»
20
2 1 «DEFINE body FOR Body-»
22 «IF code != null-»
23 «code»
24 «ELSE-»
25 «F0REACH actionCalls AS actionCall-»
2 6 CALL «actionCall.action.name»(«F0REACH actionCall.args AS arg ITERATOR it-»
27 «arg»«IF ! it.lastlteration», «EHDIF-»
оо «EHDF0REACH-» ) ;
29 «EHDF0REACH-»
30 «EHDIF-»
3 1 «EHDDEFIHE»
Рис. 3. Xpand-шаблон для dbmanipulator DSL
Процедура main (строки 3-5) будет вызвана для элемента типа dmmanipulator::Program, который является корневым элементам нашего DSL. Внутри этой процедуры, вызывается другая процедура (code) (строка 4) для каждого элемента типа Action, находящегося внутри элемента Program.
Процедура code объявлена для элементов типа Action. В этой процедуре мы создаем файл (строка 8), имя которого совпадает с именем текущего элемента Action и расширением sql. Весь текст, расположенный между строками 8 и 18, будет записан в созданный файл. Xpand содержит элементы управления (FOR, IF, ...) и средства доступа к Есоге-модели. Смотрите документацию по openArchitectureWare [3] для более подробной информации.
Чтобы посмотреть созданный шаблон в действии, необходимо запустить генератор кода. Для этого, в контекстном меню файла TestMyDSL.oaw (проект TestMyDSL) нажмите Run as > oAW Workflow. В результате будут сгенерировано три файла: AddEmployee.sql, UpdateEmployee.sql и AddAndUpdate-Employee.sql. Текст файла AddAndUpdateEmployee.sql приведен на рис. 4.
□ELI М TER $$
DROP PROCEDURE I F EXI STS ' dsl ~. ~ AddAndUpdat eEnpl oyee* SS CREATE PROCEDURE ~ dsl " . - AddAndUpdat eEnpl oyee'
(name Varchar(45), age Integer, newName Varchar(45)) ВЕЯ H
CALL AddEmpI oyeefname, age); CALL Updat eEmpI oyee( name, nev+Jame, age); EHD SS DELI M TER ;
Рис. 4. AddAndUpdateEmployee.sql
Добавление ограничений предметной области. Хотя созданный DSL редактор и позволяет пользователю избежать синтаксически неверных конструкций, пользователь по-прежнему может нарушать ограничения, накладываемые семантикой предметной области. Для добавления семантических ограничений используется язык Check.
Давайте создадим ограничение, гарантирующее, что элементы типа ActionCall будут содержать такое же количество аргументов, что и связанный с ними элемент типа Action.
Чтобы сделать это, откройте файл dbmanipula-tor.dsl/src/dbmanipulator/Checks.chk и добавьте туда текст, приведенный на рис. 5 в секции Check.chk. ActionCall указывает тип элемента, для которого предназначена проверка. Далее специфицируется сообщение, которое будет выводиться, если возвращаемое методом checkParametersCount значение будет false. Реализация метода checkParametersCount написана на языке Xtend и находится в файле dbmanipulator.dsl/ src/dbmanipulator/Extensions.ext (см. секцию Extension. ext рис. 5). Теперь, если число аргументов элемента типа ActionCall не будет соответствовать требуемому, то появится сообщение представленное в секции model.dsl (рис. 5).
Усовершенствуем наш редактор так, чтобы при вводе пользователем параметров элемента типа Action-Call автоматически предлагался список допустимых имен параметров. Для этого в файл dbmanipulator.dsl. editor/src/dbmanipulator/ContentAssist.ext добавьте код на языке Xtend приведенный на рис. 6 в секции Conten-tAssist.ext. Результат представлен в секции model.dsl.
Cj Checks.chk
nontext ActionCall ERROR "Hissing arugments count. Should be " + action.args.size:
_this . checkPacametersCount () ;_
+
EJ Extensions.ext
Boolean checkParametersCount(ActionCall actionCall): _actionCall.args.size == ActionCall.action.args.size;
Цу ¡"model.dsl
Create action <AddAndUpdateEmployee> with arguments:
(name :Varchar (45) , age : Integer, neuNaine :Varchar (45) )
15 {
Э16" ¡AddEmp loyee [ age):
uPdate|Missing arugments count. Should Ье"1?1 аЭе1 ;
18 }
Рис. 5. Check
Что дальше? Данная статья представляет лишь краткое рассмотрение возможностей, предоставляемых фреймворком Xtext при разработке и
реализации DSL. Для получения более полной информации обратитесь к документации по openArchi-tectureWare [3].
Ej *ContentAssist.ext
List [Proposal] completeActionCall_args (emf : : EOb ject ctx, String prefix) : let actionCall = (ActionCall) ctK :
let action = (Action)actionCall.eContainer.eContainer:
action.args.select(e | e.type == positionType(actionCall)) .collect(e| newProposal(e.name));
String positionType(ActionCall actionCall):
actionCall.action.args.get(actionCall.args.size).type;
§ *model.dsl
15 016
IS
19 End
С r e at e_ac t i on < AddAndUp dat e Emp1о ye e > with_ar[juments :
(name :Varchar(45), age : Integer, newName :Varchar(45)
AddEmploye=( ^ UpdateEm^loy e name
e newName
Рис. 6. Улучшение редактора
список ЛИТЕРАТУРЫ
1. Fowler M. Domain Specific Languages. http:// martinfowler.com/bliki/DomainSpecificLanguage. html
2. Mernik M., Heering J., Sloane A. When and how to develop domain-specific languages. Software Engineering Report SEN-E0517, 2005. 48p.
3. документация по openArchitectureWare http://www. eclipse.org/gmt/doc/ .