SlideShare ist ein Scribd-Unternehmen logo
1 von 37
Downloaden Sie, um offline zu lesen
Внешние языки DSL
 на funcparserlib
 Андрей Власовских, СПб Политех
          @vlasovskikh
     #devconf_ru, 2010-05-17
Domain Specific Languages

• DSL — языки, использующие специальную нотацию для задач в
  определённой предметной области
• Идея: решать задачу в терминах языка её спецификации
• Внутренние и внешние DSL




                                                          2
Внутренний DSL: Django models
 from django.db import models

 class Permission(models.Model):
   name = models.CharField(_('name'), max_length=50)
   content_type = models.ForeignKey(ContentType)
   codename = models.CharField(_('codename'),
                               max_length=100)
   class Meta:
     unique_together = ['content_type', 'codename']
     ordering = ['content_type__app_label', 'codename']



• Другие внутренние DSL: WTForms, lxml E-factory, ...


                                                          3
Внешний DSL: Graphviz DOT
     digraph {
       nodesep=0.6;
       node [shape=circle];
       n1 -> n2 -> n3 -> n1;
       n2 -> n4;
       n4 [
          label="узел 1",
          shape=box,
       ];
     }



• Другие внешние DSL: regexp, CSS, JSON, SQL, ..


                                                   4
DSL: внутренние vs внешние

• Синтаксис языка-носителя    • Любой синтаксис
• Доступ к средствам языка-   • Нет языка-носителя, все
  носителя                      средства делаем сами
• Ясно как работать: всего    • Нужно писать парсер языка
  лишь библиотека               для превращения во
                                внутреннюю структуру
                                данных




                                                       5
Задача парсера
                         '''digraph {
                           n1 -> n2 -> n3 -> n1;
      Строка               n2 -> n4;
                           n4 [shape=box];
                         }'''


                         Graph(
                           type='digraph',
                           stmts=[
                             Edge(nodes=['n1', 'n2', 'n3', 'n1'],
      Дерево*                     attrs=[]),
                             Edge(nodes=['n2', 'n4'], attrs=[]),
                             Node(id='n4',
                                  attrs=[Attr(name='shape',
                                              value='box')])])
* Abstract Syntax Tree                                              6
Считается, что писать парсеры сложно

• Требуется знание теории и средств
      –    Терминология, алгоритмы, computer science
      –    Средства со множеством деталей и тонкостей
• Проще обойти проблему, чем написать
      –    Сделать внутренний DSL
      –    Взять готовый язык с парсером




                                                        7
Есть простые подходы к парсингу

• Можно легко создавать парсеры, не углубляясь в теорию
• Теория парсинга занимается в основном оптимизацией, что уже
    не так актуально
• Простота vs быстродействие




                                                           8
Какое мне дело до парсеров?

• Языки в Веб-разработке
      – Templates, markup, URL maps, complex user input
      – Если не хватает regexp, нужны парсеры
• Столкнулся с языком — знаешь, как с ним быть
      – Разбор готовых или своих собственных DSL
      – Эксперименты с языками
• Интересный подход к парсингу на основе ФП
      – Функциональные комбинаторы парсинга

                                                          9
Функциональные комбинаторы парсинга

• Внутренний DSL для создания парсеров внешних DSL
• На основе известной нотации BNF
• Компактный и ортогональный язык
       –   Всего около 10 функций
           –
           Но можно написать парсер любого* языка
• Простой метод рекурсивного спуска




* С контекстно-свободной грамматикой
                                                     10
Библиотека funcparserlib

• Pure Python, без зависимостей, 700 SLOC, ок. 10 функций,
  документация
• Идея заимствована из OCaml и Haskell
• Есть и другие библиотеки, но с некомпактными API
       –    Pyparsing, PLY, ANTLR, LEPL, ...




                                                             11
Структура языка комбинаторов


• Проблема парсеров внешних DSL
• Структура языка комбинаторов
• funcparserlib на практике



                                        12
Весь язык комбинаторов

                     p1 + p2    p1 | p2      p >> f
                     many(p)    skip(p)      maybe(p)
    Parser




                 some(pred) forward_decl() finished


• Это весь язык комбинаторов
   – 1 класс Parser
   – 3 «конструктора» примитивных парсеров
   – 6 операций композции парсеров

                                                        13
Простой пример: язык S-Exp
• Простой язык для задания вложенных структур данных
• Похож на JSON, XML. Используется в Lisp
• Выражения состоят из символов и списков*


                           (hello
                            (this is
             Список               (a nested s-exp))
                            (good bye))

                           single-element

                           ()
                                     «Символ»
* Подмножество S-Exp
                                                       14
Два языка в одном примере

• Язык S-Exp
      – Как создать парсер внешнего DSL?
• Язык комбинаторов
      – Как устроен внутренний DSL, а не просто API?




                                                       15
Строка
                 Парсер языка S-Exp                    Список
                                                       токенов
'''
(hello                                  [
 (this is                   tokenize(s) Token('op',     '('),
       (a nested s-exp))                Token('sym',    'hello'),
 (good bye))                            Token('op',     '('),
'''                                     Token('sym',    'this'),
                                          ...
                                        Token('op',     '('),
[                                       Token('sym',    'good'),
  'hello',                              Token('sym',    'bye'),
  [                                     Token('op',     ')'),
     'this',                            Token('op',     ')'),
     'is',                              ]
     ['a', 'nested', 's-exp'],
  ],
  ['good', 'bye'],               parse(toks)
]
       Дерево
                                                                 16
Готовый токенизатор S-Exp
     from funcparserlib.lexer import (
       Spec, make_tokenizer, Token)

     def tokenize(s):         Шаблоны токенов
       'str -> [Token]'          по regexp
       specs = [
         Spec('space',   r'[ trn]+'),
         Spec('sym',     r'[A-Za-z_0-9-]+'),
         Spec('op',      r'[()]'),
       ]
       f = make_tokenizer(specs)
       return [x for x in f(s)
                 if x.type != 'space']
Токенизатор
                                  Объект-токен с
                                полями type, value
                                                     17
Preview: весь парсер S-Exp

def mksymbol(tok):
  return tok.value
def op(value):
  return some(lambda t: t.type == 'op' and
                        t.value == value)
def load(s):
  symtok = some(lambda t: t.type == 'sym')
  symbol = symtok >> mksymbol
  list = forward_decl()
  expr = symbol | list
  list.define(
    skip(op('(')) +
                                   Дальше рассмотрим,
    many(expr) +
                                     как он устроен
    skip(op(')'))
  return expr.parse(tokenize(s))

                                                        18
Парсер одного токена                 hello

• Парсер some(pred) возвращает токен t,
  если pred(t) вернула True                         Текущий
                                                     пример

      symtok = some(lambda t: t.type == 'sym')


          Парсер токена-символа


      def op(value):
        return some(lambda t: t.type == 'op' and
                                t.value == value)
      op('(')
      op(')')
                 Парсеры токенов-скобок
                                                       19
Использование парсера                     hello


• Успех

   >>> symtok.parse(tokenize('hello world'))
   Token('sym', 'hello')



• Неуспех

   >>> symtok.parse(tokenize('()'))
   Traceback (most recent call first):
     ...
   SyntaxError: 1,1-1,1: got unexpected token: op '('



                                                           20
Примитивные парсеры                     hello

• Парсеры одного токена на основе комбинатора some
• Все остальные парсеры — на их основе

           some
                     symtok             Token('sym', 'foo')




                    op('(')             Token('op', '(')
            some

                    op(')')             Token('op', ')')


                              Парсер-примитив

                                                           21
Интерпретация результатов                  hello

• p >> f возвращает парсер, применяющий f к результату p
• Можно преобразовать выходное значение как угодно

               def mksymbol(tok):
                 return tok.value

               symbol = symtok >> mksymbol

• Использование

>>> symbol.parse(tokenize('hello world'))
'hello'




                                                           22
Композиция и абстракция                        hello

• Композиция — составление парсеров из более простых
• Абстракция — cоставные парсеры выглядят как примитивы

                                        Парсер-композиция



     symbol    =     symtok      >> mksymbol         'foo'




                               symbol                'foo'


           Парсер-абстракция

                                                                23
Последовательность                    (hello)

• p1 + p2 запускает один парсер за другим, возвращает кортеж

       >>> p = op('(') + symbol + op(')')

       >>> p.parse(tokenize('(hello)'))
       (Token('op', '('), 'hello', Token('op',      ')'))


• skip(p) пропускает результат парсера p, не включая его в
  разобранную последовательность
       >>> p = skip(op('(')) + symbol + skip(op(')')))

       >>> p.parse(tokenize('(hello)'))
       'hello'

                                                             24
Повторение парсера                 (a b c)

• many(p) применяет парсер p, пока он успешен, возвращая
  список результатов

   list = skip(op('(')) + many(symbol) + skip(op(')')))


• Использование

>>> list.parse(tokenize('(a b c)'))
['a', 'b', 'c']




                                                           25
Абстракция в действии                 (a b c)

• Осмысленное сложное выражение становится примитивом




                  skip             many           skip
list   =
                op('(')   +       symbol   +    op(')')




                           list                ['foo','bar']


                                                          26
(a b c)
                  Варианты выбора                   hello
• p1 | p2 пробует второй парсер, если первый неуспешен


                  expr = symbol | list


• Использование

>>> expr.parse(tokenize('(a b c)'))
['a', 'b', 'c']

>>> expr.parse(tokenize('hello'))
'hello'



                                                         27
Рекурсивные определения: forward_decl
• Список может включать вложенные списки      (a (b (c))
• Напрямую нельзя, взаимная рекурсия             (d e f))
                                              hello
  expr = symbol | list
  list = (
    skip(op('(')) +
    many(expr) +
    skip(op(')'))


                              list = forward_decl()
• Опережающее объявление      expr = symbol | list
                              list.define(
                                skip(op('(')) +
                                many(expr) +
                                skip(op(')')))
                                                        28
Рекурсивно определённые абстракции

• Можно парсить языки с произвольной              (a (b (c))
  вложенностью элементов                             (d e f))
                                                  hello



                  skip            many               skip
list    =
                 op('(')    +     expr        +    op(')')




expr    =     symbol   |   list          ['foo',['bar', 'baz']]
                                         'foo'

                                                             29
Собранный парсер S-Exp

def mksymbol(tok):
  return tok.value
def op(value):
  return some(lambda t: t.type == 'op' and
                        t.value == value)
def load(s):
  symtok = some(lambda t: t.type == 'sym')
  symbol = symtok >> mksymbol
  list = forward_decl()
  expr = symbol | list
  list.define(
    skip(op('(')) +
    many(expr) +
    skip(op(')'))
  return expr.parse(tokenize(s))

                                             30
Примитивы, композиция и абстракция

                                        =
       Parser




                            p1 + p2   p1 | p2   p >> f
                            many(p)   skip(p)   maybe(p)



                        some(pred) forward_decl() finished


  •    Компактность — мало встроенных элементов
  •    Замыкание — композиция парсеров вновь даёт парсер
  •    Ортогональность — можно произвольно* сочетать парсеры
  •    Абстракция — составной парсер можно сделать примитивом
* Почти произвольно                                             31
funcparserlib на практике


• Проблема парсеров внешних DSL
• Структура языка комбинаторов
• funcparserlib на практике



                                         32
Кто использует funcparserlib

• @vlasovskikh
       –   Тестовые парсеры JSON и DOT в комплекте
           funcparserlib
       –   Парсер интерфейсов модулей Delphi для
           отслеживания межмодульных зависимостей
• Другие
       –   Парсер алгоритмической музыки Thixotropical
       –   Парсер разметки текстов песен на http://musi.cx/
       –   Набросок парсера математических выражений WISE

                                                              33
Пример: алгоритмическая музыка
                 Thixotropical
• Внешний DSL для вероятностной алгоритмической музыки

 channel 0                      flute1*4
   inst 0-48                      chord 0 for 4 on 2
   volume 50
                                slow1*2
 channel 1                        next slow2a slow3a
   inst 128-48
   volume 60                    loop1a
                                  chord 0 for 1 on 0
 flute1                           next flute1 chime1 gong
   chord 0 for 8 on 2
   chord 80 for 0.125 on 2


                                                            34
Пример: тексты песен на http://musi.cx/
• Внешний DSL для разметки текстов песен

 No more fear of failure
 No more suffering
 No more lies, I will arise
 From blood-filled rivers of my enemies

 >>
 Unleash   war
 Unleash   my wrath
 Unleash   revenge
 Unleash   my hell
 <<

 Unleash! (x4)
                                             35
Что умеет funcparserlib

• Комбинаторы парсинга (ок. 400 SLOC с docstrings)
• Токенизатор на regexps (ок. 100 SLOC)
• Сообщения об ошибках по методу длиннейшего разобранного
  префикса
• Оптимизация подхода для Python
• Логи с трейсом разбора для отладки, читаемые имена парсеров
• Автоопределение незавершающихся парсеров




                                                           36
Вопросы?


http://code.google.com/p/funcparserlib/

             @vlasovskikh



                                          37

Weitere ähnliche Inhalte

Was ist angesagt?

Making of external DSL for Django ORM - Павел Петлинский, Rambler&Co
Making of external DSL for Django ORM - Павел Петлинский, Rambler&CoMaking of external DSL for Django ORM - Павел Петлинский, Rambler&Co
Making of external DSL for Django ORM - Павел Петлинский, Rambler&Coit-people
 
Лекция 2. Всё, что вы хотели знать о функциях в Python.
Лекция 2. Всё, что вы хотели знать о функциях в Python.Лекция 2. Всё, что вы хотели знать о функциях в Python.
Лекция 2. Всё, что вы хотели знать о функциях в Python.Roman Brovko
 
Python AST / Николай Карелин / VPI Development Center [Python Meetup 27.03.15]
Python AST / Николай Карелин / VPI Development Center [Python Meetup 27.03.15]Python AST / Николай Карелин / VPI Development Center [Python Meetup 27.03.15]
Python AST / Николай Карелин / VPI Development Center [Python Meetup 27.03.15]Python Meetup
 
Лекция 3. Декораторы и модуль functools.
Лекция 3. Декораторы и модуль functools.Лекция 3. Декораторы и модуль functools.
Лекция 3. Декораторы и модуль functools.Roman Brovko
 
Pyton – пробуем функциональный стиль
Pyton – пробуем функциональный стильPyton – пробуем функциональный стиль
Pyton – пробуем функциональный стильPython Meetup
 
SWIG — cоздание мультиязыковых интерфейсов для C/C++ библиотек
SWIG — cоздание мультиязыковых интерфейсов для C/C++ библиотекSWIG — cоздание мультиязыковых интерфейсов для C/C++ библиотек
SWIG — cоздание мультиязыковых интерфейсов для C/C++ библиотекPython Meetup
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Python Meetup
 
Лекция 5
Лекция 5Лекция 5
Лекция 5itc73
 
Дизайн больших приложений в ФП
Дизайн больших приложений в ФПДизайн больших приложений в ФП
Дизайн больших приложений в ФПAlexander Granin
 
Лекция 11. Тестирование.
Лекция 11. Тестирование.Лекция 11. Тестирование.
Лекция 11. Тестирование.Roman Brovko
 
Александр Гладыш — Lua
Александр Гладыш — LuaАлександр Гладыш — Lua
Александр Гладыш — LuaYury Yurevich
 
Шаблоны C++ и базы данных. Сергей Федоров. CoreHard Spring 2019
Шаблоны C++ и базы данных. Сергей Федоров. CoreHard Spring 2019Шаблоны C++ и базы данных. Сергей Федоров. CoreHard Spring 2019
Шаблоны C++ и базы данных. Сергей Федоров. CoreHard Spring 2019corehard_by
 
Regexp
RegexpRegexp
Regexpkumup
 
Clojure: Lisp for the modern world (русская версия)
Clojure: Lisp for the modern world (русская версия)Clojure: Lisp for the modern world (русская версия)
Clojure: Lisp for the modern world (русская версия)Alex Ott
 
Лекция 13. Многопоточность и GIL
Лекция 13. Многопоточность и GILЛекция 13. Многопоточность и GIL
Лекция 13. Многопоточность и GILRoman Brovko
 
Python и его тормоза
Python и его тормозаPython и его тормоза
Python и его тормозаAlexander Shigin
 
Функционально декларативный дизайн на C++
Функционально декларативный дизайн на C++Функционально декларативный дизайн на C++
Функционально декларативный дизайн на C++Alexander Granin
 
"Внутренности" CPython, часть II
"Внутренности" CPython, часть II"Внутренности" CPython, часть II
"Внутренности" CPython, часть IIPython Meetup
 
Память руби изнутри
Память руби изнутриПамять руби изнутри
Память руби изнутриvasfed
 

Was ist angesagt? (20)

Making of external DSL for Django ORM - Павел Петлинский, Rambler&Co
Making of external DSL for Django ORM - Павел Петлинский, Rambler&CoMaking of external DSL for Django ORM - Павел Петлинский, Rambler&Co
Making of external DSL for Django ORM - Павел Петлинский, Rambler&Co
 
Лекция 2. Всё, что вы хотели знать о функциях в Python.
Лекция 2. Всё, что вы хотели знать о функциях в Python.Лекция 2. Всё, что вы хотели знать о функциях в Python.
Лекция 2. Всё, что вы хотели знать о функциях в Python.
 
Python AST / Николай Карелин / VPI Development Center [Python Meetup 27.03.15]
Python AST / Николай Карелин / VPI Development Center [Python Meetup 27.03.15]Python AST / Николай Карелин / VPI Development Center [Python Meetup 27.03.15]
Python AST / Николай Карелин / VPI Development Center [Python Meetup 27.03.15]
 
Лекция 3. Декораторы и модуль functools.
Лекция 3. Декораторы и модуль functools.Лекция 3. Декораторы и модуль functools.
Лекция 3. Декораторы и модуль functools.
 
Pyton – пробуем функциональный стиль
Pyton – пробуем функциональный стильPyton – пробуем функциональный стиль
Pyton – пробуем функциональный стиль
 
SWIG — cоздание мультиязыковых интерфейсов для C/C++ библиотек
SWIG — cоздание мультиязыковых интерфейсов для C/C++ библиотекSWIG — cоздание мультиязыковых интерфейсов для C/C++ библиотек
SWIG — cоздание мультиязыковых интерфейсов для C/C++ библиотек
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
 
Лекция 5
Лекция 5Лекция 5
Лекция 5
 
Дизайн больших приложений в ФП
Дизайн больших приложений в ФПДизайн больших приложений в ФП
Дизайн больших приложений в ФП
 
Лекция 11. Тестирование.
Лекция 11. Тестирование.Лекция 11. Тестирование.
Лекция 11. Тестирование.
 
Александр Гладыш — Lua
Александр Гладыш — LuaАлександр Гладыш — Lua
Александр Гладыш — Lua
 
Шаблоны C++ и базы данных. Сергей Федоров. CoreHard Spring 2019
Шаблоны C++ и базы данных. Сергей Федоров. CoreHard Spring 2019Шаблоны C++ и базы данных. Сергей Федоров. CoreHard Spring 2019
Шаблоны C++ и базы данных. Сергей Федоров. CoreHard Spring 2019
 
Regexp
RegexpRegexp
Regexp
 
Clojure: Lisp for the modern world (русская версия)
Clojure: Lisp for the modern world (русская версия)Clojure: Lisp for the modern world (русская версия)
Clojure: Lisp for the modern world (русская версия)
 
Лекция 13. Многопоточность и GIL
Лекция 13. Многопоточность и GILЛекция 13. Многопоточность и GIL
Лекция 13. Многопоточность и GIL
 
Парсим CSS
Парсим CSSПарсим CSS
Парсим CSS
 
Python и его тормоза
Python и его тормозаPython и его тормоза
Python и его тормоза
 
Функционально декларативный дизайн на C++
Функционально декларативный дизайн на C++Функционально декларативный дизайн на C++
Функционально декларативный дизайн на C++
 
"Внутренности" CPython, часть II
"Внутренности" CPython, часть II"Внутренности" CPython, часть II
"Внутренности" CPython, часть II
 
Память руби изнутри
Память руби изнутриПамять руби изнутри
Память руби изнутри
 

Ähnlich wie Внешние языки DSL на funcparserlib

Back to the future: Функциональное программирование вчера и сегодня
Back to the future: Функциональное программирование вчера и сегодняBack to the future: Функциональное программирование вчера и сегодня
Back to the future: Функциональное программирование вчера и сегодняAlexander Granin
 
2013 09 21 языки программирования
2013 09 21 языки программирования 2013 09 21 языки программирования
2013 09 21 языки программирования Yandex
 
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"Yandex
 
Универсальный сигнатурный анализ кода на C#, Java, PHP
Универсальный сигнатурный анализ кода на C#, Java, PHPУниверсальный сигнатурный анализ кода на C#, Java, PHP
Универсальный сигнатурный анализ кода на C#, Java, PHPИван Кочуркин
 
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 3...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 3...PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 3...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 3...pgdayrussia
 
Индексный поиск по регулярным выражениям (Александр Коротков)
Индексный поиск по регулярным выражениям (Александр Коротков)Индексный поиск по регулярным выражениям (Александр Коротков)
Индексный поиск по регулярным выражениям (Александр Коротков)Ontico
 
Haskell Lite - presentation for DevDay about Haskell language
Haskell Lite - presentation for DevDay about Haskell languageHaskell Lite - presentation for DevDay about Haskell language
Haskell Lite - presentation for DevDay about Haskell languageAlexander Granin
 
Лекция о языке программирования Haskell
Лекция о языке программирования HaskellЛекция о языке программирования Haskell
Лекция о языке программирования Haskellhusniyarova
 
Tech Talks @NSU: Теоретические основы программирования: проекции Футамуры-Тур...
Tech Talks @NSU: Теоретические основы программирования: проекции Футамуры-Тур...Tech Talks @NSU: Теоретические основы программирования: проекции Футамуры-Тур...
Tech Talks @NSU: Теоретические основы программирования: проекции Футамуры-Тур...Tech Talks @NSU
 
SQL Tricky (Иван Фролков)
SQL Tricky (Иван Фролков)SQL Tricky (Иван Фролков)
SQL Tricky (Иван Фролков)Ontico
 
Андрей Субботин "Локализация приложений для iOS: как не прострелить себе ногу"
Андрей Субботин "Локализация приложений для iOS: как не прострелить себе ногу"Андрей Субботин "Локализация приложений для iOS: как не прострелить себе ногу"
Андрей Субботин "Локализация приложений для iOS: как не прострелить себе ногу"Yandex
 
Функциональное программирование на F#
Функциональное программирование на F#Функциональное программирование на F#
Функциональное программирование на F#akrakovetsky
 
Romanova techforum bash
Romanova techforum bashRomanova techforum bash
Romanova techforum bashkuchinskaya
 
Лекция 1. Начало.
Лекция 1. Начало.Лекция 1. Начало.
Лекция 1. Начало.Roman Brovko
 
Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013
Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013
Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013Unigine Corp.
 

Ähnlich wie Внешние языки DSL на funcparserlib (20)

Back to the future: Функциональное программирование вчера и сегодня
Back to the future: Функциональное программирование вчера и сегодняBack to the future: Функциональное программирование вчера и сегодня
Back to the future: Функциональное программирование вчера и сегодня
 
Rgsu04
Rgsu04Rgsu04
Rgsu04
 
Rgsu04
Rgsu04Rgsu04
Rgsu04
 
2013 09 21 языки программирования
2013 09 21 языки программирования 2013 09 21 языки программирования
2013 09 21 языки программирования
 
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
 
Универсальный сигнатурный анализ кода на C#, Java, PHP
Универсальный сигнатурный анализ кода на C#, Java, PHPУниверсальный сигнатурный анализ кода на C#, Java, PHP
Универсальный сигнатурный анализ кода на C#, Java, PHP
 
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 3...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 3...PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 3...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 3...
 
Индексный поиск по регулярным выражениям (Александр Коротков)
Индексный поиск по регулярным выражениям (Александр Коротков)Индексный поиск по регулярным выражениям (Александр Коротков)
Индексный поиск по регулярным выражениям (Александр Коротков)
 
Haskell Lite - presentation for DevDay about Haskell language
Haskell Lite - presentation for DevDay about Haskell languageHaskell Lite - presentation for DevDay about Haskell language
Haskell Lite - presentation for DevDay about Haskell language
 
Лекция о языке программирования Haskell
Лекция о языке программирования HaskellЛекция о языке программирования Haskell
Лекция о языке программирования Haskell
 
Tech Talks @NSU: Теоретические основы программирования: проекции Футамуры-Тур...
Tech Talks @NSU: Теоретические основы программирования: проекции Футамуры-Тур...Tech Talks @NSU: Теоретические основы программирования: проекции Футамуры-Тур...
Tech Talks @NSU: Теоретические основы программирования: проекции Футамуры-Тур...
 
SQL Tricky (Иван Фролков)
SQL Tricky (Иван Фролков)SQL Tricky (Иван Фролков)
SQL Tricky (Иван Фролков)
 
Иван Фролков. Tricky SQL
Иван Фролков. Tricky SQLИван Фролков. Tricky SQL
Иван Фролков. Tricky SQL
 
Андрей Субботин "Локализация приложений для iOS: как не прострелить себе ногу"
Андрей Субботин "Локализация приложений для iOS: как не прострелить себе ногу"Андрей Субботин "Локализация приложений для iOS: как не прострелить себе ногу"
Андрей Субботин "Локализация приложений для iOS: как не прострелить себе ногу"
 
Функциональное программирование на F#
Функциональное программирование на F#Функциональное программирование на F#
Функциональное программирование на F#
 
Romanova techforum bash
Romanova techforum bashRomanova techforum bash
Romanova techforum bash
 
Лекция 1. Начало.
Лекция 1. Начало.Лекция 1. Начало.
Лекция 1. Начало.
 
Transpile it.pdf
Transpile it.pdfTranspile it.pdf
Transpile it.pdf
 
PascalABC.NET 2015-2016
PascalABC.NET 2015-2016PascalABC.NET 2015-2016
PascalABC.NET 2015-2016
 
Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013
Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013
Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013
 

Внешние языки DSL на funcparserlib

  • 1. Внешние языки DSL на funcparserlib Андрей Власовских, СПб Политех @vlasovskikh #devconf_ru, 2010-05-17
  • 2. Domain Specific Languages • DSL — языки, использующие специальную нотацию для задач в определённой предметной области • Идея: решать задачу в терминах языка её спецификации • Внутренние и внешние DSL 2
  • 3. Внутренний DSL: Django models from django.db import models class Permission(models.Model): name = models.CharField(_('name'), max_length=50) content_type = models.ForeignKey(ContentType) codename = models.CharField(_('codename'), max_length=100) class Meta: unique_together = ['content_type', 'codename'] ordering = ['content_type__app_label', 'codename'] • Другие внутренние DSL: WTForms, lxml E-factory, ... 3
  • 4. Внешний DSL: Graphviz DOT digraph { nodesep=0.6; node [shape=circle]; n1 -> n2 -> n3 -> n1; n2 -> n4; n4 [ label="узел 1", shape=box, ]; } • Другие внешние DSL: regexp, CSS, JSON, SQL, .. 4
  • 5. DSL: внутренние vs внешние • Синтаксис языка-носителя • Любой синтаксис • Доступ к средствам языка- • Нет языка-носителя, все носителя средства делаем сами • Ясно как работать: всего • Нужно писать парсер языка лишь библиотека для превращения во внутреннюю структуру данных 5
  • 6. Задача парсера '''digraph { n1 -> n2 -> n3 -> n1; Строка n2 -> n4; n4 [shape=box]; }''' Graph( type='digraph', stmts=[ Edge(nodes=['n1', 'n2', 'n3', 'n1'], Дерево* attrs=[]), Edge(nodes=['n2', 'n4'], attrs=[]), Node(id='n4', attrs=[Attr(name='shape', value='box')])]) * Abstract Syntax Tree 6
  • 7. Считается, что писать парсеры сложно • Требуется знание теории и средств – Терминология, алгоритмы, computer science – Средства со множеством деталей и тонкостей • Проще обойти проблему, чем написать – Сделать внутренний DSL – Взять готовый язык с парсером 7
  • 8. Есть простые подходы к парсингу • Можно легко создавать парсеры, не углубляясь в теорию • Теория парсинга занимается в основном оптимизацией, что уже не так актуально • Простота vs быстродействие 8
  • 9. Какое мне дело до парсеров? • Языки в Веб-разработке – Templates, markup, URL maps, complex user input – Если не хватает regexp, нужны парсеры • Столкнулся с языком — знаешь, как с ним быть – Разбор готовых или своих собственных DSL – Эксперименты с языками • Интересный подход к парсингу на основе ФП – Функциональные комбинаторы парсинга 9
  • 10. Функциональные комбинаторы парсинга • Внутренний DSL для создания парсеров внешних DSL • На основе известной нотации BNF • Компактный и ортогональный язык – Всего около 10 функций – Но можно написать парсер любого* языка • Простой метод рекурсивного спуска * С контекстно-свободной грамматикой 10
  • 11. Библиотека funcparserlib • Pure Python, без зависимостей, 700 SLOC, ок. 10 функций, документация • Идея заимствована из OCaml и Haskell • Есть и другие библиотеки, но с некомпактными API – Pyparsing, PLY, ANTLR, LEPL, ... 11
  • 12. Структура языка комбинаторов • Проблема парсеров внешних DSL • Структура языка комбинаторов • funcparserlib на практике 12
  • 13. Весь язык комбинаторов p1 + p2 p1 | p2 p >> f many(p) skip(p) maybe(p) Parser some(pred) forward_decl() finished • Это весь язык комбинаторов – 1 класс Parser – 3 «конструктора» примитивных парсеров – 6 операций композции парсеров 13
  • 14. Простой пример: язык S-Exp • Простой язык для задания вложенных структур данных • Похож на JSON, XML. Используется в Lisp • Выражения состоят из символов и списков* (hello (this is Список (a nested s-exp)) (good bye)) single-element () «Символ» * Подмножество S-Exp 14
  • 15. Два языка в одном примере • Язык S-Exp – Как создать парсер внешнего DSL? • Язык комбинаторов – Как устроен внутренний DSL, а не просто API? 15
  • 16. Строка Парсер языка S-Exp Список токенов ''' (hello [ (this is tokenize(s) Token('op', '('), (a nested s-exp)) Token('sym', 'hello'), (good bye)) Token('op', '('), ''' Token('sym', 'this'), ... Token('op', '('), [ Token('sym', 'good'), 'hello', Token('sym', 'bye'), [ Token('op', ')'), 'this', Token('op', ')'), 'is', ] ['a', 'nested', 's-exp'], ], ['good', 'bye'], parse(toks) ] Дерево 16
  • 17. Готовый токенизатор S-Exp from funcparserlib.lexer import ( Spec, make_tokenizer, Token) def tokenize(s): Шаблоны токенов 'str -> [Token]' по regexp specs = [ Spec('space', r'[ trn]+'), Spec('sym', r'[A-Za-z_0-9-]+'), Spec('op', r'[()]'), ] f = make_tokenizer(specs) return [x for x in f(s) if x.type != 'space'] Токенизатор Объект-токен с полями type, value 17
  • 18. Preview: весь парсер S-Exp def mksymbol(tok): return tok.value def op(value): return some(lambda t: t.type == 'op' and t.value == value) def load(s): symtok = some(lambda t: t.type == 'sym') symbol = symtok >> mksymbol list = forward_decl() expr = symbol | list list.define( skip(op('(')) + Дальше рассмотрим, many(expr) + как он устроен skip(op(')')) return expr.parse(tokenize(s)) 18
  • 19. Парсер одного токена hello • Парсер some(pred) возвращает токен t, если pred(t) вернула True Текущий пример symtok = some(lambda t: t.type == 'sym') Парсер токена-символа def op(value): return some(lambda t: t.type == 'op' and t.value == value) op('(') op(')') Парсеры токенов-скобок 19
  • 20. Использование парсера hello • Успех >>> symtok.parse(tokenize('hello world')) Token('sym', 'hello') • Неуспех >>> symtok.parse(tokenize('()')) Traceback (most recent call first): ... SyntaxError: 1,1-1,1: got unexpected token: op '(' 20
  • 21. Примитивные парсеры hello • Парсеры одного токена на основе комбинатора some • Все остальные парсеры — на их основе some symtok Token('sym', 'foo') op('(') Token('op', '(') some op(')') Token('op', ')') Парсер-примитив 21
  • 22. Интерпретация результатов hello • p >> f возвращает парсер, применяющий f к результату p • Можно преобразовать выходное значение как угодно def mksymbol(tok): return tok.value symbol = symtok >> mksymbol • Использование >>> symbol.parse(tokenize('hello world')) 'hello' 22
  • 23. Композиция и абстракция hello • Композиция — составление парсеров из более простых • Абстракция — cоставные парсеры выглядят как примитивы Парсер-композиция symbol = symtok >> mksymbol 'foo' symbol 'foo' Парсер-абстракция 23
  • 24. Последовательность (hello) • p1 + p2 запускает один парсер за другим, возвращает кортеж >>> p = op('(') + symbol + op(')') >>> p.parse(tokenize('(hello)')) (Token('op', '('), 'hello', Token('op', ')')) • skip(p) пропускает результат парсера p, не включая его в разобранную последовательность >>> p = skip(op('(')) + symbol + skip(op(')'))) >>> p.parse(tokenize('(hello)')) 'hello' 24
  • 25. Повторение парсера (a b c) • many(p) применяет парсер p, пока он успешен, возвращая список результатов list = skip(op('(')) + many(symbol) + skip(op(')'))) • Использование >>> list.parse(tokenize('(a b c)')) ['a', 'b', 'c'] 25
  • 26. Абстракция в действии (a b c) • Осмысленное сложное выражение становится примитивом skip many skip list = op('(') + symbol + op(')') list ['foo','bar'] 26
  • 27. (a b c) Варианты выбора hello • p1 | p2 пробует второй парсер, если первый неуспешен expr = symbol | list • Использование >>> expr.parse(tokenize('(a b c)')) ['a', 'b', 'c'] >>> expr.parse(tokenize('hello')) 'hello' 27
  • 28. Рекурсивные определения: forward_decl • Список может включать вложенные списки (a (b (c)) • Напрямую нельзя, взаимная рекурсия (d e f)) hello expr = symbol | list list = ( skip(op('(')) + many(expr) + skip(op(')')) list = forward_decl() • Опережающее объявление expr = symbol | list list.define( skip(op('(')) + many(expr) + skip(op(')'))) 28
  • 29. Рекурсивно определённые абстракции • Можно парсить языки с произвольной (a (b (c)) вложенностью элементов (d e f)) hello skip many skip list = op('(') + expr + op(')') expr = symbol | list ['foo',['bar', 'baz']] 'foo' 29
  • 30. Собранный парсер S-Exp def mksymbol(tok): return tok.value def op(value): return some(lambda t: t.type == 'op' and t.value == value) def load(s): symtok = some(lambda t: t.type == 'sym') symbol = symtok >> mksymbol list = forward_decl() expr = symbol | list list.define( skip(op('(')) + many(expr) + skip(op(')')) return expr.parse(tokenize(s)) 30
  • 31. Примитивы, композиция и абстракция = Parser p1 + p2 p1 | p2 p >> f many(p) skip(p) maybe(p) some(pred) forward_decl() finished • Компактность — мало встроенных элементов • Замыкание — композиция парсеров вновь даёт парсер • Ортогональность — можно произвольно* сочетать парсеры • Абстракция — составной парсер можно сделать примитивом * Почти произвольно 31
  • 32. funcparserlib на практике • Проблема парсеров внешних DSL • Структура языка комбинаторов • funcparserlib на практике 32
  • 33. Кто использует funcparserlib • @vlasovskikh – Тестовые парсеры JSON и DOT в комплекте funcparserlib – Парсер интерфейсов модулей Delphi для отслеживания межмодульных зависимостей • Другие – Парсер алгоритмической музыки Thixotropical – Парсер разметки текстов песен на http://musi.cx/ – Набросок парсера математических выражений WISE 33
  • 34. Пример: алгоритмическая музыка Thixotropical • Внешний DSL для вероятностной алгоритмической музыки channel 0 flute1*4 inst 0-48 chord 0 for 4 on 2 volume 50 slow1*2 channel 1 next slow2a slow3a inst 128-48 volume 60 loop1a chord 0 for 1 on 0 flute1 next flute1 chime1 gong chord 0 for 8 on 2 chord 80 for 0.125 on 2 34
  • 35. Пример: тексты песен на http://musi.cx/ • Внешний DSL для разметки текстов песен No more fear of failure No more suffering No more lies, I will arise From blood-filled rivers of my enemies >> Unleash war Unleash my wrath Unleash revenge Unleash my hell << Unleash! (x4) 35
  • 36. Что умеет funcparserlib • Комбинаторы парсинга (ок. 400 SLOC с docstrings) • Токенизатор на regexps (ок. 100 SLOC) • Сообщения об ошибках по методу длиннейшего разобранного префикса • Оптимизация подхода для Python • Логи с трейсом разбора для отладки, читаемые имена парсеров • Автоопределение незавершающихся парсеров 36