Предметно-ориентированные
 языки программирования
                  Кириллов Александр, UNDEV
[ DSL ]
Предметно-ориентированный язык -

      это язык программирования с ограниченными
      выразительными возможностями,
      ориентированный на некую конкретную
      предметную область
                                   Мартин Фаулер
[ DSL ]
Зачем?
[ DSL ]
Зачем?
[ DSL ]
Зачем?




                          бк и...
                    ско
                эти
             уж
          Ох
[ DSL ]           BankAccount.configure do |account|
                    account.number 1234
                    account.type :checking
Зачем?    Ruby:
                    account.openDate '11.04.1974'
                    account.balance 25382.20
                    account.holder do |holder|
                      holder.lastName 'Singh'
                      holder.firstName 'Darshan'
                    end
                  end



                  BankAccount:
                    number: 1234
                    type: checking
                    open_date: '11.04.1974'
          YAML:     balance: 25382.20
                    account_holder:
                      last_name: Singh
                      first_name: Darshan
[ Ключевые моменты ]
Где и кто использует DSL?
[ Ключевые моменты ]
Где и кто использует DSL:
- конфигурации
# TravisCI config file (.yaml)
script: "bundle exec rake travis"
before_script: ./bin/ci/before_build.sh
services: mongodb
rvm:
  - 1.9.3
notifications:
  email:
     - didier@nocoffee.fr
     - mario@mariovisic.com
  irc: "irc.freenode.org#locomotivecms"
branches:
  only:
     - master
[ Ключевые моменты ]
Где и кто использует DSL:
- конфигурации
- зависимости пакетов
# Gemfile
source 'http://rubygems.org'

gem 'rails', '3.2.12'
group :assets do
  gem 'therubyracer'
  gem 'select2-rails', github: "argerim/select2-rails"
end
group :test do
  gem "minitest"
  gem 'mocha', require: false
end
[ Ключевые моменты ]
Где и кто использует DSL:
- конфигурации
- зависимости пакетов
- части фреймворков
# Sample resource route
resources :products do
  resources :comments
  resources :sales do
    get 'recent', on: :collection
  end
end
[ Ключевые моменты ]
Где и кто использует DSL:
- конфигурации
- зависимости пакетов
- части фреймворков
- системы тестирования
# Rspec example
describe 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)
  end
end
[ Ключевые моменты ]
Где и кто использует 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 ]
> Текучий интерфейс (fluent interface)


 Применение обычного API:

 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]
 })
[ Внутренний DSL ]
> Текучий интерфейс (fluent interface)


 Применение обычного API:


                                           ту
 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,
        неп[dsk1, dsk2]
    disks:
 })
[ Внутренний DSL ]
> Текучий интерфейс (fluent interface)

 Связывание методов:

 computer()
   .processor()
     .cores(2)
     .x64()
   .disk()
     .size(150)
     .speed(7200)
   .disk()
     .size(70)
     .speed(7200)
[ Внутренний DSL ]
> Текучий интерфейс (fluent interface)

 Связывание методов:

 $("#menu")
   .fadeIn('fast')
   .addClass("active")
   .css('marginRight', '10px');
[ Внутренний DSL ]
> Текучий интерфейс (fluent interface)

 Связывание методов:

 $("#menu")
   .fadeIn('fast')
   .addClass("active")
   .css('marginRight', '10px');

 $("#thumbnails li").mouseenter(function(e){
    $(this).find('img')
       .stop()
       .animate({opacity: 0.8}, 300)
    .end()
    .find('.viewcasestudy')
       .fadeIn('fast');
 }).fadeIn('fast');
[ Внутренний DSL ]
> Текучий интерфейс (fluent interface)
              Связывание методов
 Последовательность функций:

 computer()
 --processor()
 ----cores(2)
 ----x64()
 --disk()
 ----size(150)
 ----speed(7200)
 --disk()
 ----size(70)
 ----speed(7200)
[ Внутренний DSL ]
> Текучий интерфейс (fluent interface)
                             Связывание методов
              Последовательность функций
 Вложенные функции:

 computer(
 --processor(
 ----cores(2),
 ----x64()
 ––),
 --disk(
 ----size(150),
 ----speed(7200)
 ––),
 --disk(
 ----size(70),
 ----speed(7200)
 --)
 )
[ Внутренний DSL ]
> Текучий интерфейс (fluent interface)
                                    Связывание методов
                       Последовательность функций
              Вложенные функции
 Замыкания:
 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
   end
 end
[ Внутренний 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 ]
> Синтаксический анализ

 Управляемая разделителями трансляция
 Delimiter-Directed Translation

 event door_opened
 event link_clicked
 event window_closed

 command play_music
 command volume_up 10
 command send_message "Hello, world"
[ Внешний DSL ]
> Синтаксический анализ

 Управляемая разделителями трансляция
 Delimiter-Directed Translation
                            разделитель
 event door_openedn
 event link_clicked
 event window_closed

 command play_music
 command volume_up 10
 command send_message "Hello, world"
             инструкция
[ Внешний DSL ]
> Синтаксический анализ

 Управляемая разделителями трансляция
 Delimiter-Directed Translation
                                    Ограничение:
 event door_opened
 event link_clicked
 event window_closed                Нет способов обработки
                                    иерархического контекста
 command play_music
 command volume_up 10
 command send_message "Hello, world"
[ Внешний DSL ]
> Синтаксический анализ

 Синтаксически управляемая трансляция
 Syntax-Directed Translation
                                             commands
 event                                         block
   door_opened
   link_clicked
 end                       command   commandDec   commandDec   end


 command
                                     play_music    volume_up   10
   play_music
   volume_up 10
   send_message "Hello, world"
 end
[ Внешний DSL ]
> Синтаксический анализ

Грамматика входного языка.




list            :   eventList    commandList        ;
eventList       :   'events'     eventDec*     'end';
eventDec        :   identifier                      ;
commandList     :   'commands'   commandDec*   'end';
commandDec      :   identifier   identifier?        ;
[ Внешний DSL ]
> Синтаксический анализ

Грамматика входного языка.

Форма Бэкуса-Наура


[E]BNF
list            :   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

Предметно-ориентированные языки программирования (DSL)

  • 1.
  • 2.
    [ DSL ] Предметно-ориентированныйязык - это язык программирования с ограниченными выразительными возможностями, ориентированный на некую конкретную предметную область Мартин Фаулер
  • 3.
  • 4.
  • 5.
    [ DSL ] Зачем? бк и... ско эти уж Ох
  • 6.
    [ DSL ] BankAccount.configure do |account| account.number 1234 account.type :checking Зачем? Ruby: account.openDate '11.04.1974' account.balance 25382.20 account.holder do |holder| holder.lastName 'Singh' holder.firstName 'Darshan' end end BankAccount: number: 1234 type: checking open_date: '11.04.1974' YAML: balance: 25382.20 account_holder: last_name: Singh first_name: Darshan
  • 7.
    [ Ключевые моменты] Где и кто использует DSL?
  • 8.
    [ Ключевые моменты] Где и кто использует DSL: - конфигурации # TravisCI config file (.yaml) script: "bundle exec rake travis" before_script: ./bin/ci/before_build.sh services: mongodb rvm: - 1.9.3 notifications: email: - didier@nocoffee.fr - mario@mariovisic.com irc: "irc.freenode.org#locomotivecms" branches: only: - master
  • 9.
    [ Ключевые моменты] Где и кто использует DSL: - конфигурации - зависимости пакетов # Gemfile source 'http://rubygems.org' gem 'rails', '3.2.12' group :assets do gem 'therubyracer' gem 'select2-rails', github: "argerim/select2-rails" end group :test do gem "minitest" gem 'mocha', require: false end
  • 10.
    [ Ключевые моменты] Где и кто использует DSL: - конфигурации - зависимости пакетов - части фреймворков # Sample resource route resources :products do resources :comments resources :sales do get 'recent', on: :collection end end
  • 11.
    [ Ключевые моменты] Где и кто использует DSL: - конфигурации - зависимости пакетов - части фреймворков - системы тестирования # Rspec example describe 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) end end
  • 12.
    [ Ключевые моменты] Где и кто использует DSL: - системы тестирования # language: ru Функционал: Сложение чисел Чтобы не складывать в уме Все, у кого с этим туго Хотят автоматическое сложение целых чисел Сценарий: Сложение двух целых чисел Допустим я ввожу число 50 И затем ввожу число 70 Если я нажимаю "+" То результатом должно быть число 120
  • 13.
    [ Ключевые моменты] Где и кто использует DSL: - и еще-еще-еще...
  • 14.
    [ Ключевые моменты] Язык программирования
  • 15.
    [ Ключевые моменты] Язык программирования Декларативный подход ЧТО делать вместо КАК делать
  • 16.
    [ Ключевые моменты] Язык программирования Декларативный подход Природа языка
  • 17.
    [ Ключевые моменты] Язык программирования Декларативный подход Природа языка Ограниченные выразительные возможности
  • 18.
    [ Ключевые моменты] Язык программирования Декларативный подход Природа языка Ограниченные выразительные Возможности Ориентированность на предметную область
  • 19.
    [ Категории DSL] { Внутренний DSL: 'Lisp, Ruby, etc...', }
  • 20.
    [ Категории DSL] { Внутренний DSL: 'Lisp, Ruby, etc...', Внешний DSL: 'XML, RegExp, SQL, CSS' }
  • 21.
    [ Категории DSL] { Внутренний DSL: 'Lisp, Ruby, etc...', Внешний DSL: 'XML, RegExp, SQL, CSS', Языковые инструментальные средства: '*' }
  • 22.
  • 23.
  • 24.
    [ Внутренний DSL] > Текучий интерфейс (fluent interface) Применение обычного API: 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] })
  • 25.
    [ Внутренний DSL] > Текучий интерфейс (fluent interface) Применение обычного API: ту 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, неп[dsk1, dsk2] disks: })
  • 26.
    [ Внутренний DSL] > Текучий интерфейс (fluent interface) Связывание методов: computer() .processor() .cores(2) .x64() .disk() .size(150) .speed(7200) .disk() .size(70) .speed(7200)
  • 27.
    [ Внутренний DSL] > Текучий интерфейс (fluent interface) Связывание методов: $("#menu") .fadeIn('fast') .addClass("active") .css('marginRight', '10px');
  • 28.
    [ Внутренний DSL] > Текучий интерфейс (fluent interface) Связывание методов: $("#menu") .fadeIn('fast') .addClass("active") .css('marginRight', '10px'); $("#thumbnails li").mouseenter(function(e){ $(this).find('img') .stop() .animate({opacity: 0.8}, 300) .end() .find('.viewcasestudy') .fadeIn('fast'); }).fadeIn('fast');
  • 29.
    [ Внутренний DSL] > Текучий интерфейс (fluent interface) Связывание методов Последовательность функций: computer() --processor() ----cores(2) ----x64() --disk() ----size(150) ----speed(7200) --disk() ----size(70) ----speed(7200)
  • 30.
    [ Внутренний DSL] > Текучий интерфейс (fluent interface) Связывание методов Последовательность функций Вложенные функции: computer( --processor( ----cores(2), ----x64() ––), --disk( ----size(150), ----speed(7200) ––), --disk( ----size(70), ----speed(7200) --) )
  • 31.
    [ Внутренний DSL] > Текучий интерфейс (fluent interface) Связывание методов Последовательность функций Вложенные функции Замыкания: 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 end end
  • 32.
    [ Внутренний DSL] > Текучий интерфейс (fluent interface) > Работа с синтаксическим деревом
  • 33.
    [ Внутренний DSL] > Текучий интерфейс (fluent interface) > Работа с синтаксическим деревом > Аннотации
  • 34.
    [ Внутренний DSL] > Текучий интерфейс (fluent interface) > Работа с синтаксическим деревом > Аннотации > Макросы
  • 35.
    [ Внутренний DSL] > Текучий интерфейс (fluent interface) > Работа с синтаксическим деревом > Аннотации def method_missing(m, *args, &block) > Макросы puts "There's no method called #{m} here." end > Динамический отклик
  • 36.
    [ Внешний DSL] > Синтаксический анализ
  • 37.
    [ Внешний DSL] > Синтаксический анализ Входной поток — это обычный текст (...обычно o_0)
  • 38.
    [ Внешний DSL] > Синтаксический анализ Управляемая разделителями трансляция Delimiter-Directed Translation event door_opened event link_clicked event window_closed command play_music command volume_up 10 command send_message "Hello, world"
  • 39.
    [ Внешний DSL] > Синтаксический анализ Управляемая разделителями трансляция Delimiter-Directed Translation разделитель event door_openedn event link_clicked event window_closed command play_music command volume_up 10 command send_message "Hello, world" инструкция
  • 40.
    [ Внешний DSL] > Синтаксический анализ Управляемая разделителями трансляция Delimiter-Directed Translation Ограничение: event door_opened event link_clicked event window_closed Нет способов обработки иерархического контекста command play_music command volume_up 10 command send_message "Hello, world"
  • 41.
    [ Внешний DSL] > Синтаксический анализ Синтаксически управляемая трансляция Syntax-Directed Translation commands event block door_opened link_clicked end command commandDec commandDec end command play_music volume_up 10 play_music volume_up 10 send_message "Hello, world" end
  • 42.
    [ Внешний DSL] > Синтаксический анализ Грамматика входного языка. list : eventList commandList ; eventList : 'events' eventDec* 'end'; eventDec : identifier ; commandList : 'commands' commandDec* 'end'; commandDec : identifier identifier? ;
  • 43.
    [ Внешний DSL] > Синтаксический анализ Грамматика входного языка. Форма Бэкуса-Наура [E]BNF list : eventList commandList ; eventList : 'events' eventDec* 'end'; eventDec : identifier ; commandList : 'commands' commandDec* 'end'; commandDec : identifier identifier? ;
  • 44.
    [ Внешний DSL] > Синтаксический анализ Синтаксический анализатор на основе рекурсивного спуска Recursive Descent Parser
  • 45.
    [ Внешний DSL] > Синтаксический анализ Комбинатор синтаксических анализаторов Parser Combinator Комбинатор команд Анализатор Анализатор начала блока команд конца блока команд Комбинатор списка команд Анализатор Анализатор команды аргумента команды
  • 46.
    [ Внешний DSL] > Синтаксический анализ Генератор синтаксических анализаторов Parser Generator Генерация Грамматика Синтаксический a : ID INT ; анализатор ID : 'a'..'z'+ ; INT : '0'..'9'+;
  • 47.
    [ Для чеговсе это? ] В результате мы должны иметь построенную семантическую модель
  • 48.
    [ Для чеговсе это? ] В результате мы должны иметь построенную семантическую модель > Для использования в программном продукте
  • 49.
    [ Для чеговсе это? ] В результате мы должны иметь построенную семантическую модель > Для использования в программном продукте > Для генерации нового кода
  • 50.
    [ Генерация кода] > Генерация c помощью преобразователя Transformer Generation
  • 51.
    [ Генерация кода] > Генерация c помощью преобразователя Transformer Generation > Шаблонная генерация Templated Generation
  • 52.
    [ Генерация кода] > Генерация c помощью преобразователя Transformer Generation > Шаблонная генерация Templated Generation > Встроенный помощник Embedment Helper
  • 53.
    [ Если Вызаинтересованы ] http://martinfowler.com/dslCatalog/
  • 54.
    [ Спасибо завнимание! ] Кириллов Александр http://twitter.com/saratovsource