Предметно-ориентированныеязыки программирования
Кириллов Александр, UNDEV
[ DSL ]
Предметно-ориентированный язык - Предметно-ориентированный язык -
это язык программирования с ограниченными выразительными возможностями, ориентированный на некую конкретную предметную область
Мартин Фаулер
[ DSL ]
Зачем?
[ DSL ]
Зачем?
[ DSL ]
Ох уж эти скобки...
Зачем?
[ DSL ]BankAccount.configure do |account| account.number 1234 account.type :checking account.openDate '11.04.1974' account.balance 25382.20 account.holder do |holder| holder.lastName 'Singh' holder.firstName 'Darshan' endend
BankAccount: number: 1234 type: checking open_date: '11.04.1974' balance: 25382.20 account_holder: last_name: Singh first_name: Darshan
Ruby:
YAML:
Зачем?
[ Ключевые моменты ]
Где и кто использует DSL?
[ Ключевые моменты ]
Где и кто использует DSL:- конфигурации# TravisCI config file (.yaml)script: "bundle exec rake travis"before_script: ./bin/ci/before_build.shservices: mongodbrvm: - 1.9.3notifications: email: - [email protected] - [email protected] irc: "irc.freenode.org#locomotivecms"branches: only: - master
[ Ключевые моменты ]
Где и кто использует DSL:- конфигурации- зависимости пакетов# Gemfilesource 'http://rubygems.org'
gem 'rails', '3.2.12'group :assets do gem 'therubyracer' gem 'select2-rails', github: "argerim/select2-rails"endgroup :test do gem "minitest" gem 'mocha', require: falseend
[ Ключевые моменты ]
Где и кто использует DSL:- конфигурации- зависимости пакетов- части фреймворков# Sample resource routeresources :products do resources :comments resources :sales do get 'recent', on: :collection endend
[ Ключевые моменты ]
Где и кто использует DSL:- конфигурации- зависимости пакетов- части фреймворков- системы тестирования# Rspec exampledescribe Account do it "has a balance of zero when first created" do Account.new.balance.should eq(Money.new(0)) expect { @a = 5 }.to change { @a }.from(nil).to(5) endend
[ Ключевые моменты ]
Где и кто использует DSL:- системы тестирования# language: ruФункционал: Сложение чисел Чтобы не складывать в уме Все, у кого с этим туго Хотят автоматическое сложение целых чисел
Сценарий: Сложение двух целых чисел Допустим я ввожу число 50 И затем ввожу число 70 Если я нажимаю "+" То результатом должно быть число 120
[ Ключевые моменты ]
Где и кто использует DSL:- и еще-еще-еще...
[ Ключевые моменты ]
Язык программирования
[ Ключевые моменты ]
Язык программирования
Декларативный подходЧТО делатьвместо
КАК делать
[ Ключевые моменты ]
Язык программирования
Декларативный подход
Природа языка
[ Ключевые моменты ]
Язык программирования
Декларативный подход
Природа языка
Ограниченные выразительныевозможности
[ Ключевые моменты ]
Язык программирования
Декларативный подход
Природа языка
Ограниченные выразительныеВозможности
Ориентированность на предметную область
[ Категории DSL ]
{
Внутренний DSL: 'Lisp, Ruby, etc...',
}
[ Категории DSL ]
{
Внутренний DSL: 'Lisp, Ruby, etc...',
Внешний DSL: 'XML, RegExp, SQL, CSS'
}
[ Категории DSL ]
{
Внутренний DSL: 'Lisp, Ruby, etc...',
Внешний DSL: 'XML, RegExp, SQL, CSS',
Языковые инструментальные средства: '*'
}
[ Реализация DSL ]
[ Реализация DSL ]
[ Внутренний DSL ]
processor = Processor.new(arch: :i386, cores: 2)dsk1 = Disk.new(size: 150.GB, speed:7200)dsk2 = Disk.new(size: 70.GB, speed:7200)computer = Computer.new({ processor: processor, disks: [dsk1, dsk2]})
Применение обычного API:
> Текучий интерфейс (fluent interface)
[ Внутренний DSL ]
> Текучий интерфейс (fluent interface)
processor = Processor.new(arch: :i386, cores: 2)dsk1 = Disk.new(size: 150.GB, speed:7200)dsk2 = Disk.new(size: 70.GB, speed:7200)computer = Computer.new({ processor: processor, disks: [dsk1, dsk2]})
Применение обычного API:
...понятно програ
ммисту
непонятно специа
листу предметной об
ласти...
[ Внутренний DSL ]
computer() .processor() .cores(2) .x64() .disk() .size(150) .speed(7200) .disk() .size(70) .speed(7200)
Связывание методов:Связывание методов:
> Текучий интерфейс (fluent interface)
[ Внутренний DSL ]
$("#menu") .fadeIn('fast') .addClass("active") .css('marginRight', '10px');
Связывание методов:Связывание методов:
> Текучий интерфейс (fluent interface)
[ Внутренний DSL ]
$("#menu") .fadeIn('fast') .addClass("active") .css('marginRight', '10px');
Связывание методов:Связывание методов:
> Текучий интерфейс (fluent interface)
$("#thumbnails li").mouseenter(function(e){ $(this).find('img') .stop() .animate({opacity: 0.8}, 300) .end() .find('.viewcasestudy') .fadeIn('fast');}).fadeIn('fast');
[ Внутренний DSL ]
computer()--processor()----cores(2)----x64()--disk()----size(150)----speed(7200)--disk()----size(70)----speed(7200)
Последовательность функций:Последовательность функций:Связывание методов
> Текучий интерфейс (fluent interface)
[ Внутренний DSL ]
computer(--processor(----cores(2),----x64()––),--disk(----size(150),----speed(7200)––),--disk(----size(70),----speed(7200)--))
Вложенные функции:Вложенные функции:Связывание методовПоследовательность функций
> Текучий интерфейс (fluent interface)
[ Внутренний DSL ]
ComputerBuilder.build do |c| c.processor do |p| p.cores 2 p.x64 p.speed 2.2 end c.disk do |d| d.size 120 end c.disk do |d| d.size 70 d.speed 7200 d.sata endend
Замыкания:Замыкания:Последовательность функцийВложенные функции
Связывание методов> Текучий интерфейс (fluent interface)
[ Внутренний DSL ]
> Текучий интерфейс (fluent interface)
> Работа с синтаксическим деревом
[ Внутренний DSL ]
> Текучий интерфейс (fluent interface)
> Работа с синтаксическим деревом
> Аннотации
[ Внутренний DSL ]
> Текучий интерфейс (fluent interface)
> Работа с синтаксическим деревом
> Аннотации
> Макросы
[ Внутренний DSL ]
> Текучий интерфейс (fluent interface)
> Работа с синтаксическим деревом
> Аннотации
> Макросы
> Динамический отклик
def method_missing(m, *args, &block) puts "There's no method called #{m} here."end
[ Внешний DSL ]
> Синтаксический анализ
[ Внешний DSL ]
> Синтаксический анализ
Входной поток — это обычный текст (...обычно o_0)
[ Внешний DSL ]
> Синтаксический анализ
event door_openedevent link_clickedevent window_closed
command play_musiccommand volume_up 10command send_message "Hello, world"
Управляемая разделителями трансляцияDelimiter-Directed Translation
[ Внешний DSL ]
> Синтаксический анализ
event door_opened\nevent link_clickedevent window_closed
command play_musiccommand volume_up 10command send_message "Hello, world"
разделитель
инструкция
Управляемая разделителями трансляцияDelimiter-Directed Translation
[ Внешний DSL ]
> Синтаксический анализ
event door_openedevent link_clickedevent window_closed
command play_musiccommand volume_up 10command send_message "Hello, world"
Управляемая разделителями трансляцияDelimiter-Directed Translation
Ограничение:
Нет способов обработкииерархического контекста
[ Внешний DSL ]
> Синтаксический анализ
event door_opened link_clickedend
command play_music volume_up 10 send_message "Hello, world"end
Синтаксически управляемая трансляцияSyntax-Directed Translation
commandsblock
command endcommandDec commandDec
play_music volume_up 10
[ Внешний DSL ]
> Синтаксический анализ
Грамматика входного языка.
list : eventList commandList ;eventList : 'events' eventDec* 'end';eventDec : identifier ;commandList : 'commands' commandDec* 'end';commandDec : identifier identifier? ;
[ Внешний DSL ]
> Синтаксический анализ
Грамматика входного языка.
Форма Бэкуса-Наура
[E]BNFlist : eventList commandList ;eventList : 'events' eventDec* 'end';eventDec : identifier ;commandList : 'commands' commandDec* 'end';commandDec : identifier identifier? ;
[ Внешний DSL ]
> Синтаксический анализ
Синтаксический анализатор на основерекурсивного спускаRecursive Descent Parser
[ Внешний DSL ]
> Синтаксический анализ
Комбинатор синтаксических анализаторовParser Combinator
Комбинаторкоманд
Анализаторначала блока команд
Комбинаторсписка команд
Анализаторконца блока команд
Анализаторкоманды
Анализатораргумента команды
[ Внешний DSL ]
> Синтаксический анализ
Генератор синтаксических анализаторовParser Generator
Грамматика
a : ID INT ;ID : 'a'..'z'+ ;INT : '0'..'9'+;
Синтаксическийанализатор
Генерация
[ Для чего все это? ]
В результате мы должны иметьпостроенную семантическую модель
[ Для чего все это? ]
В результате мы должны иметьпостроенную семантическую модель
> Для использования в программном продукте
[ Для чего все это? ]
В результате мы должны иметьпостроенную семантическую модель
> Для использования в программном продукте
> Для генерации нового кода
[ Генерация кода ]
> Генерация c помощью преобразователя Transformer Generation
[ Генерация кода ]
> Генерация c помощью преобразователя Transformer Generation
> Шаблонная генерация Templated Generation
[ Генерация кода ]
> Генерация c помощью преобразователя Transformer Generation
> Шаблонная генерация Templated Generation
> Встроенный помощник Embedment Helper
[ Если Вы заинтересованы ]
http://martinfowler.com/dslCatalog/
[ Спасибо за внимание! ]
Кириллов Александрhttp://twitter.com/saratovsource