pyconru 2016. Осторожно, dsl!

66
Осторожно , DSL! Цыганов Иван Positive Technologies

Upload: ivan-tsyganov

Post on 16-Apr-2017

270 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: PyconRu 2016. Осторожно, DSL!

Осторожно, DSL!

Цыганов Иван Positive Technologies

Page 2: PyconRu 2016. Осторожно, DSL!

server { location / { proxy_pass http://localhost:8080/; }

location ~ \.(gif|jpg|png)$ { root /data/images; }}

Page 3: PyconRu 2016. Осторожно, DSL!

version: '2'services: db: image: postgres web: build: . command: python manage.py runserver 0.0.0.0:8000 volumes: - .:/code ports: - "8000:8000" depends_on: - db

DOCKER COMPOSE

Page 4: PyconRu 2016. Осторожно, DSL!

LOGROTATE

compress

"/var/log/httpd/access.log" /var/log/httpd/error.log { rotate 5 size=100k sharedscripts nocompress postrotate /sbin/killall -HUP httpd endscript}

/var/log/messages { rotate 5 weekly postrotate /sbin/killall -HUP syslogd endscript}

Page 5: PyconRu 2016. Осторожно, DSL!

compress

"/var/log/httpd/access.log" /var/log/httpd/error.log { rotate 5 size=100k sharedscripts nocompress postrotate /sbin/killall -HUP httpd endscript}

Page 6: PyconRu 2016. Осторожно, DSL!

config = { 'compress': True, ('/var/log/nginx/access.log', '/var/log/nginx/error.log'): { 'rotate': 5, 'size': 102400, 'compress': False, 'sharedscripts': True, 'postrotate': [ '/sbin/killall -HUP syslogd' ] }}

compress

"/var/log/httpd/access.log" /var/log/httpd/error.log { rotate 5 size=100k sharedscripts nocompress postrotate /sbin/killall -HUP httpd endscript}

Page 7: PyconRu 2016. Осторожно, DSL!

config = { 'compress': True, ('/var/log/nginx/access.log', '/var/log/nginx/error.log'): { 'rotate': 5, 'size': 102400, 'compress': False, 'sharedscripts': True, 'postrotate': [ '/sbin/killall -HUP syslogd' ] }}

compress

"/var/log/httpd/access.log" /var/log/httpd/error.log { rotate 5 size=100k sharedscripts nocompress postrotate /sbin/killall -HUP httpd endscript}

size=100k

'size': 102400,

Page 8: PyconRu 2016. Осторожно, DSL!

config = { 'compress': True, ('/var/log/nginx/access.log', '/var/log/nginx/error.log'): { 'rotate': 5, 'size': 102400, 'postrotate': [ '/sbin/killall -HUP syslogd' ] }}

'size': '100 KB',

Page 9: PyconRu 2016. Осторожно, DSL!

config = { 'compress': True, ('/var/log/nginx/access.log', '/var/log/nginx/error.log'): { 'rotate': 5, 'size': 102400, 'postrotate': [ '/sbin/killall -HUP syslogd' ] }}

'size': '100 KB',

def get_size(self, value): size_value, _, size_unit = value.partition(' ') return self.units_to_bytes(size_value, size_unit)

Page 10: PyconRu 2016. Осторожно, DSL!

config = { 'compress': True, ('/var/log/nginx/access.log', '/var/log/nginx/error.log'): { 'rotate': 5, 'size': 102400, 'postrotate': [ '/sbin/killall -HUP syslogd' ] }}

'size': '1MB + 100KB',

Page 11: PyconRu 2016. Осторожно, DSL!

config = { 'compress': True, ('/var/log/nginx/access.log', '/var/log/nginx/error.log'): { 'rotate': 5, 'size': 102400, 'postrotate': [ '/sbin/killall -HUP syslogd' ] }}

'size': '1MB + 100KB',

parse_size = re.compile( pattern=r''' (?P<Value>\d+)\s* (?P<ValueUnit>KB|MB|GB)?\s* ((?P<Operator>[-+/*])\s*)? ( (?P<Delta>\d+)\s* (?P<DeltaUnit>KB|MB|GB)? )? ''', flags=re.IGNORECASE | re.VERBOSE).search

Page 12: PyconRu 2016. Осторожно, DSL!

config = { 'compress': True, ('/var/log/nginx/access.log', '/var/log/nginx/error.log'): { 'rotate': 5, 'size': 102400, 'postrotate': [ '/sbin/killall -HUP syslogd' ] }}

'size': ‘100MB - 2 * (1MB + 100KB)’,

Page 13: PyconRu 2016. Осторожно, DSL!

Some people, when confronted with a problem, think "I know, I’ll use regular expressions." Now they have two problems.

— Jamie Zawinski

Page 14: PyconRu 2016. Осторожно, DSL!

Осторожно, DSL!

Page 15: PyconRu 2016. Осторожно, DSL!

– Мартин Фаулер

Предметно-ориентированный язык — это язык программирования с ограниченными выразительными возможностями, ориентированный на некую конкретную предметную область

Page 16: PyconRu 2016. Осторожно, DSL!

✤ SQL✤ REGEXP✤ TeX/LaTeX

Виды DSL

DSL

ВнутренниеВнешние✤ PonyORM✤ WTForm✤ Django models

Page 17: PyconRu 2016. Осторожно, DSL!

Что будет дальше?

✤ Внутренние DSL

✤ Внешние DSL

✤ Инструменты для создания анализаторов

Page 18: PyconRu 2016. Осторожно, DSL!

Внутренние DSL

Все возможности базового языка

Привычный синтаксис

Ограничен базовым языком

'size': 100MB - 2 * (1MB - 100KB)

Page 19: PyconRu 2016. Осторожно, DSL!

GB = lambda x: x*2**30 MB = lambda x: x*2**20 KB = lambda x: x*2**10

GB = 2**30 MB = 2**20 KB = 2**10

config = { (‘/var/log/nginx/access.log',): { 'size': 100*MB - 2 * (1*MB - 100*KB) }, (‘/var/log/nginx/error.log',): { 'size': 100*MB - 2 * (1*MB - 100*KB) } }

'size': MB(100) - 2*(MB(1) + KB(100))

'size': 100*MB - 2 * (1*MB - 100*KB)

Page 20: PyconRu 2016. Осторожно, DSL!

Внешние DSL

Сами выбираем синтаксис

Необходимо разрабатывать анализаторы

Page 21: PyconRu 2016. Осторожно, DSL!

compress: truerules:- files: [/var/log/nginx/access.log, /var/log/nginx/error.log] rotate: 5 size: 100MB - 2 * (1MB + 100KB) postrotate: [/sbin/killall -HUP syslogd]

config = { 'compress': True, ('/var/log/nginx/access.log', '/var/log/nginx/error.log'): { 'rotate': 5, 'size': 100*MB - 2 * (1*MB - 100*KB) 'postrotate': [ '/sbin/killall -HUP syslogd' ] }}

Page 22: PyconRu 2016. Осторожно, DSL!

Python Lex-Yacc (PLY)

Page 23: PyconRu 2016. Осторожно, DSL!

ply.lex

ply.yacc

ASTRunCode

source

Page 24: PyconRu 2016. Осторожно, DSL!

ply.lex

Type ValuePLUS + MINUS - MUL *DIV /UNIT GB|MB|KB|BDIGIT \d+LPAREN (RPAREN )

Page 25: PyconRu 2016. Осторожно, DSL!

ply.lex 2 * (1KB + 1KB)

DIGIT = 2

MUL = *

LPAREN = (

DIGIT = 1

UNIT = KB

PLUS = +

DIGIT = 1

UNIT = KB

RPAREN = )

Page 26: PyconRu 2016. Осторожно, DSL!

ply.lex 1KB1KB-MB

DIGIT = 1

DIGIT = 1

UNIT = KB

MINUS = -

UNIT = MB

UNIT = KB

Page 27: PyconRu 2016. Осторожно, DSL!

ply.yacc

expression : expression PLUS expression | expression MINUS expression | expression MUL expression | expression DIV expression

expression : LPAREN expression RPAREN

expression : DIGIT UNIT

expression : DIGIT

Page 28: PyconRu 2016. Осторожно, DSL!

ply.yacc

def p_expression(self, p): ''' expression : expression PLUS expression | expression MINUS expression | expression MUL expression | expression DIV expression ''' if p[2] == '+': p[0] = p[1] + p[3] if p[2] == '-': p[0] = p[1] - p[3] if p[2] == '*': p[0] = p[1] * p[3] if p[2] == '/': p[0] = p[1] / p[3]

Page 29: PyconRu 2016. Осторожно, DSL!

ply.yacc

precedence = ( ('left', 'PLUS', 'MINUS'), ('left', 'MUL', 'DIV'), )

def p_expression(self, p): ''' expression : expression PLUS expression | expression MINUS expression | expression MUL expression | expression DIV expression ''' if p[2] == '+': p[0] = p[1] + p[3] if p[2] == '-': p[0] = p[1] - p[3] if p[2] == '*': p[0] = p[1] * p[3] if p[2] == '/': p[0] = p[1] / p[3]

Page 30: PyconRu 2016. Осторожно, DSL!

PLY

Гибкость

Отладка

Обработка ошибок

Понятный код библиотеки

Высокий порог входа

Многословный

Page 31: PyconRu 2016. Осторожно, DSL!

funcparserlib

Page 32: PyconRu 2016. Осторожно, DSL!

def tokenize(str): specs = [ ('Spaces', (r'[ \s\t\r\n]+',)), ('PLUS', (r'\+',)), ('MINUS', (r'-',)), ('MUL', (r'\*',)), ('DIV', (r'/',)), ('UNIT', (r'GB|MB|KB|B',)), ('DIGIT', (r'\d+',)), ('LPAREN', (r'\(',)), ('RPAREN', (r'\)',)), ] return list(filter( lambda t: t.type != 'Spaces', (t for t in make_tokenizer(specs)(str)) ))

funcparserlib

Page 33: PyconRu 2016. Осторожно, DSL!

number = value_of('DIGIT') >> intunit = number + value_of('UNIT') >> to_bytesoperand = unit | numbermakeop = lambda s, f: skip_token(s) >> const(f)add = makeop('PLUS', operator.add)sub = makeop('MINUS', operator.sub)mul = makeop('MUL', operator.mul)div = makeop('DIV', operator.floordiv) mul_op = mul | divadd_op = add | sub

funcparserlib

Page 34: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

Page 35: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

2+2*2

Page 36: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

2+2*2

maybe(expr)

Page 37: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

2+2*2

term

Page 38: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

2+2*2

primary

Page 39: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

2+2*22

operand

Page 40: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

2+2*22

many(mul_op + primary)

Page 41: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

many(add_op + term)

2+2*22+

Page 42: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

primary

2+2*22+

Page 43: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

operand

2+2*22+2

Page 44: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

2+2*22+2*

many(mul_op + primary)

Page 45: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

operand

2+2*22+2*2

Page 46: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

eval_expr

2+2*22+2*2

Page 47: PyconRu 2016. Осторожно, DSL!

2+4

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

eval_expr

Page 48: PyconRu 2016. Осторожно, DSL!

6

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

maybe(expr)

Page 49: PyconRu 2016. Осторожно, DSL!

funcparserlib

Компактный

Гибкий

Для любителей функционального программирования :)

Многое приходится делать руками

Для любителей функционального программирования :)

Page 50: PyconRu 2016. Осторожно, DSL!

pyparsing

Page 51: PyconRu 2016. Осторожно, DSL!

pyparsing

plusop = oneOf('+ -') multop = oneOf('* /') digit = Word(nums)unit = digit + oneOf('GB MB KB’) operand = unit | digitparser = Forward()primary = operand | Literal('(') + parser + Literal(')') term = (primary + ZeroOrMore(multop + primary)).setParseAction(eval_expr)expr = (term + ZeroOrMore(plusop + term)).setParseAction(eval_expr)parser << Optional(expr)

Page 52: PyconRu 2016. Осторожно, DSL!

pyparsing

plusop = oneOf('+ -') multop = oneOf('* /')

digit = Word(nums)unit = digit + oneOf('GB MB KB’)

operand = unit | digit parser = operatorPrecedence( operand, [ (multop, 2, opAssoc.LEFT, calculate), (plusop, 2, opAssoc.LEFT, calculate) ])

Page 53: PyconRu 2016. Осторожно, DSL!

pyparsing

Понятный код

Базовые компоненты

Свои компоненты

Документация

Отладка

Page 54: PyconRu 2016. Осторожно, DSL!

А что насчет быстродействия?

Page 55: PyconRu 2016. Осторожно, DSL!

Скорость

Page 56: PyconRu 2016. Осторожно, DSL!

Скорость

Page 57: PyconRu 2016. Осторожно, DSL!

А что же пользователи?

Page 58: PyconRu 2016. Осторожно, DSL!

Сообщения об ошибках

Traceback (most recent call last): File "size_parser/on_ply.py", line 100, in parse return parser.parse(string) File "size_parser/on_ply.py", line 94, in parse p = self._parser.parse(data, debug=self._debug) File ".env/site-packages/ply/yacc.py", line 331, in parse return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc) File ".env/site-packages/ply/yacc.py", line 1049, in parseopt_notrack lookahead = get_token() # Get the next token File ".env/site-packages/ply/lex.py", line 396, in token raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos], lexpos), lexdata[lexpos:]) ply.lex.LexError: Illegal character '_' at index 5

Page 59: PyconRu 2016. Осторожно, DSL!

Traceback (most recent call last): File "size_parser/on_ply.py", line 100, in parse return parser.parse(string) File "size_parser/on_ply.py", line 94, in parse p = self._parser.parse(data, debug=self._debug) File ".env/site-packages/ply/yacc.py", line 331, in parse return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc) File ".env/site-packages/ply/yacc.py", line 1049, in parseopt_notrack lookahead = get_token() # Get the next token File ".env/site-packages/ply/lex.py", line 396, in token raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos], lexpos), lexdata[lexpos:]) ply.lex.LexError: Illegal character '_' at index 5

Сообщения об ошибках

Page 60: PyconRu 2016. Осторожно, DSL!

ply

def t_error(self, t): raise ParserException(t, self.source)def p_error(self, p): raise ParserException(p, self.source)

>>> parse(‘1MB+_GB-100KB’)Unexpected "_GB-100KB" at position 4:1MB+_GB-100KB ^^^

Page 61: PyconRu 2016. Осторожно, DSL!

Так что же выбрать?

Page 62: PyconRu 2016. Осторожно, DSL!

Что же выбрать?

pyparsing✤ Хочу легко все описать✤ Быстродействие не главное

Page 63: PyconRu 2016. Осторожно, DSL!

Что же выбрать?

funcparserlib

pyparsing✤ Хочу легко все описать✤ Быстродействие не главное

✤ Люблю функциональное программирование

✤ Быстродействие не главное

Page 64: PyconRu 2016. Осторожно, DSL!

Что же выбрать?

PLY

funcparserlib

pyparsing

✤ Хочу как в учебнике✤ Скорость работы - главное!

✤ Хочу легко все описать✤ Быстродействие не главное

✤ Люблю функциональное программирование

✤ Быстродействие не главное

Page 65: PyconRu 2016. Осторожно, DSL!

И что в итоге?

✤ Для простых задач попробуйте:

✤ Средства самого языка

✤ Регулярные выражения

✤ Если задача сложная:

✤ Внутренние DSL

✤ Yaml, Json, XML, …

✤ Внешние DSL

Page 66: PyconRu 2016. Осторожно, DSL!

Спасибо за внимание!Вопросы?

http://tsyganov-ivan.com/