dsls в perl

94
DSLs в Perl DSLs в Perl Как? Как? Руслан Закиров < Руслан Закиров <[email protected] [email protected]> Best Practical Solutions, 2008 Best Practical Solutions, 2008

Upload: ruslan-zakirov

Post on 22-Jun-2015

377 views

Category:

Technology


5 download

DESCRIPTION

Технология реализации DSL в Perl без использования Devel::Declare. О том как это работает и какие трюки Perl можно использовать.

TRANSCRIPT

Page 1: DSLs в Perl

DSLs в PerlDSLs в Perl

Как?Как?

Руслан Закиров <Руслан Закиров <[email protected]@bestpractical.com>>Best Practical Solutions, 2008Best Practical Solutions, 2008

Page 2: DSLs в Perl

Что?Что?

Page 3: DSLs в Perl

under 'user/*' => run {under 'user/*' => run { my $u = U->load($1);my $u = U->load($1); ......}}

Page 4: DSLs в Perl

under 'user/*' => run {under 'user/*' => run { … … abort(404) unless $u;abort(404) unless $u; on 'profile' => run {};on 'profile' => run {}; on 'stats' => run {};on 'stats' => run {}; on 'blog' => run {};on 'blog' => run {};}}

Page 5: DSLs в Perl

Зачем?Зачем?

Page 6: DSLs в Perl

ВыразительноВыразительно

Page 7: DSLs в Perl

УправляемоУправляемо

Page 8: DSLs в Perl

Как?Как?

Page 9: DSLs в Perl

ПрототипыПрототипы

Page 10: DSLs в Perl

ПрототипыПрототипы

($)($)(&)(&)(@)(@)

Page 11: DSLs в Perl

Прототипы (завтрак)Прототипы (завтрак)

sub set($$) {...}sub set($$) {...}set key => $val;set key => $val;

Page 12: DSLs в Perl

Прототипы (запястья)Прототипы (запястья)

() ()

Page 13: DSLs в Perl

ПрототипыПрототипы

(&)(&)блокблок

функцияфункция

Page 14: DSLs в Perl

ПрототипыПрототипы

grep {} @_grep {} @_sub grep(&@) {}sub grep(&@) {}

Page 15: DSLs в Perl

МетодыМетоды

Page 16: DSLs в Perl

МетодыМетоды

$o = new X k => $v;$o = new X k => $v;

Page 17: DSLs в Perl

МетодыМетоды

perl -e 'title is „foo”;'perl -e 'title is „foo”;'

Page 18: DSLs в Perl

МетодыМетоды

Невозможно найти Невозможно найти метод "title" в пакете метод "title" в пакете

"is""is"

Page 19: DSLs в Perl

МетодыМетоды

может может ввммы забыли ы забыли загрузить "is"?загрузить "is"?

Page 20: DSLs в Perl

МетодыМетоды

этап исполненияэтап исполнения

Page 21: DSLs в Perl

МетодыМетоды

perl -e 'sub foo { title perl -e 'sub foo { title is „foo” }'is „foo” }'нет ошибкинет ошибки

Page 22: DSLs в Perl

МетодыМетоды

my $a = {};my $a = {};local *is::AUTOLOAD = sub {local *is::AUTOLOAD = sub { shift; # isshift; # is $a->{$AUTLOAD} = join ' ', @_$a->{$AUTLOAD} = join ' ', @_};};$call->(); # что-то$call->(); # что-то

Page 23: DSLs в Perl

ОбъединимОбъединим

Page 24: DSLs в Perl

Объеденим (img_simple.pl)Объеденим (img_simple.pl)

sub img(&) {sub img(&) { my $code = shift; my $code = shift; my %attr;my %attr; local *is::AUTOLOAD = sub {...};local *is::AUTOLOAD = sub {...}; $code->();$code->(); my $attrs = join ' ',my $attrs = join ' ', map $_.'=”'.$attr{$_},map $_.'=”'.$attr{$_}, keys %attr;keys %attr; print „<img $attrs />”;print „<img $attrs />”;}}

Page 25: DSLs в Perl

Слишком просто?Слишком просто?

Page 26: DSLs в Perl

Усложним (img_strict.pl)Усложним (img_strict.pl)my %attr_checks = (my %attr_checks = ( img => {img => { _mandatory => [qw(src alt)],_mandatory => [qw(src alt)], src => {src => { canonicalizer => sub {...},canonicalizer => sub {...}, provides => sub {...},provides => sub {...}, },}, # ...# ... },},););

Page 27: DSLs в Perl

Усложним (img_strict.pl)Усложним (img_strict.pl)

canonicalizer => sub {canonicalizer => sub { return "/static/images/$_[0]"return "/static/images/$_[0]" unless $_[0] =~ /^\//;unless $_[0] =~ /^\//; return $_[0];return $_[0];}}

Page 28: DSLs в Perl

Усложним (img_strict.pl)Усложним (img_strict.pl)

provides => sub {provides => sub { return unless my ($w, $h) = return unless my ($w, $h) = ($_[0] =~ /-(\d+)x(\d+)\./);($_[0] =~ /-(\d+)x(\d+)\./); return (width => $w, height => $h);return (width => $w, height => $h);}}

Page 29: DSLs в Perl

Результат (img_strict.pl)Результат (img_strict.pl)

img {img { alt is 'feed',alt is 'feed', src is 'f-14x14.png'src is 'f-14x14.png'};};

Page 30: DSLs в Perl

Результат (img_strict.pl)Результат (img_strict.pl)

<img<img width="14"width="14" height="14"height="14" alt="feed"alt="feed" src="/s/i/f-14x14.png"src="/s/i/f-14x14.png"/>/>

Page 31: DSLs в Perl

ОтладкаОтладка

Page 32: DSLs в Perl

Отладка (carp.pl)Отладка (carp.pl)

нет атрибута нет атрибута 'boo' у тега 'img' 'boo' у тега 'img' at carp.pl at carp.pl line 10line 10

Page 33: DSLs в Perl

Отладка (carp.pl)Отладка (carp.pl)

local $Carp::CarpLevel = 1;local $Carp::CarpLevel = 1;Carp::croak(...);Carp::croak(...);

Page 34: DSLs в Perl

Отладка (carp.pl)Отладка (carp.pl)

нет атрибута нет атрибута 'boo' у тега 'img' 'boo' у тега 'img' at carp.pl at carp.pl line 16line 16место вызоваместо вызова

Page 35: DSLs в Perl

Отладка (carp.pl)Отладка (carp.pl)

main::main::__ANON____ANON__() () called at carp.pl line 13called at carp.pl line 13

Page 36: DSLs в Perl

Отладка (carp.pl)Отладка (carp.pl)

sub img(&) {sub img(&) { local *__ANON__local *__ANON__ = "img_impl";= "img_impl";......}}

Page 37: DSLs в Perl

Отладка (carp.pl)Отладка (carp.pl)

bla-bla at carp.pl bla-bla at carp.pl line 17line 17main::main::img_implimg_impl() called at...() called at...main::img('CODE(...)') called at...main::img('CODE(...)') called at...

Page 38: DSLs в Perl

Грабли №1Грабли №1

Page 39: DSLs в Perl

Грабли №1Грабли №1

page {...};page {...};sub page(&) {...};sub page(&) {...};

Page 40: DSLs в Perl

Грабли №1Грабли №1

Невозможно вызвать Невозможно вызвать метод "page" без метод "page" без пакета или объектапакета или объекта

Page 41: DSLs в Perl

Грабли №2Грабли №2

Page 42: DSLs в Perl

Грабли №2Грабли №2

sub page(&) {sub page(&) { local *title::page = sub {}local *title::page = sub {} shift->();shift->();};};pagepage { { pagepage title „qwe” }; title „qwe” };

Page 43: DSLs в Perl

Грабли №2Грабли №2

Методы Методы проигрывают проигрывают протипампротипам

Page 44: DSLs в Perl

Избавляемся от Избавляемся от методовметодов

Page 45: DSLs в Perl

Без методов (wo_methods.pl)Без методов (wo_methods.pl)our %ATTR;our %ATTR;sub attrs(&) {sub attrs(&) { %ATTR = shift->()%ATTR = shift->()}}sub img(&) {sub img(&) { local %ATTR;local %ATTR; $code->();$code->(); ......}}

Page 46: DSLs в Perl

Без методов (wo_methods.pl)Без методов (wo_methods.pl)

img { attrs {img { attrs { alt => 'boo', src => 'href' alt => 'boo', src => 'href' } };} };

Page 47: DSLs в Perl

Вложенные Вложенные структурыструктуры

Page 48: DSLs в Perl

Вложения (div_simple.pl)Вложения (div_simple.pl)

<div><div><div><div>что-точто-то

</div></div></div></div>

Page 49: DSLs в Perl

Вложения (div_simple.pl)Вложения (div_simple.pl)

sub div(&) {sub div(&) { my $code = shift;my $code = shift; my $inside = $code->();my $inside = $code->(); return "<div>$inside</div>";return "<div>$inside</div>";}}print div { div {'some'} };print div { div {'some'} };

Page 50: DSLs в Perl

Если что-то Если что-то сложнее?сложнее?

Page 51: DSLs в Perl

УсложняемУсложняем

div {div { div {1};div{2}div {1};div{2}};};

Page 52: DSLs в Perl

УсложняемУсложняем

<div><div><div>2</div><div>2</div></div></div>

Page 53: DSLs в Perl

УсложняемУсложняем

Фигня вышлаФигня вышла

Page 54: DSLs в Perl

Простое решениеПростое решение

Page 55: DSLs в Perl

Простое решение (div_sol1.pl)Простое решение (div_sol1.pl)

sub div(&) {sub div(&) { my $code = shift;my $code = shift; my @inside = $code->();my @inside = $code->(); return "<div>@inside</div>";return "<div>@inside</div>";}}

Page 56: DSLs в Perl

Простое решение (div_sol1.pl)Простое решение (div_sol1.pl)

print div { div {1}, div {2} },print div { div {1}, div {2} }, div { div {3}, div {4} };div { div {3}, div {4} };

Page 57: DSLs в Perl

Код не вставишьКод не вставишь:(:(

Page 58: DSLs в Perl

явный print явный print отстойотстой

Page 59: DSLs в Perl

запятые в топкузапятые в топку

Page 60: DSLs в Perl

Да прибудут с Да прибудут с вами контекстывами контексты

Page 61: DSLs в Perl

Контексты (context1.pl)Контексты (context1.pl)

unless ( defined wantarray ) {unless ( defined wantarray ) { # void# void} elsif ( wantarray ) {} elsif ( wantarray ) { # array# array} else {} else { # scalar# scalar}}

Page 62: DSLs в Perl

КонтекстыКонтексты

sub div(&) {sub div(&) { my $res = join '', shift->();my $res = join '', shift->(); unless ( defined wantarray ) {unless ( defined wantarray ) { print „<div>$res</div>”;print „<div>$res</div>”; } else {} else { return „<div>$res</div>”;return „<div>$res</div>”; }}}}

Page 63: DSLs в Perl

Контексты (context1.pl)Контексты (context1.pl)

div { div {1}; div {2} };div { div {1}; div {2} };

<div>1</div><div>1</div><div><div>2</div></div><div><div>2</div></div>

Page 64: DSLs в Perl

БуферизацияБуферизация

Page 65: DSLs в Perl

Бу-эфиры (buffers1.pl)Бу-эфиры (buffers1.pl)

sub buffered {sub buffered { my $buf = '';my $buf = ''; local *STDOUT;local *STDOUT; open STDOUT, '>', \$buf;open STDOUT, '>', \$buf; return $buf . join('', shift->());return $buf . join('', shift->());}}

Page 66: DSLs в Perl

Бу-эфиры (buffers1.pl)Бу-эфиры (buffers1.pl)

sub div(&) {sub div(&) { my $res = buffered(shift); my $res = buffered(shift); return „<div>$res</div>”return „<div>$res</div>” if defined wantarray;if defined wantarray; print „<div>$res</div>”;print „<div>$res</div>”;}}

Page 67: DSLs в Perl

Бу-эфиры (buffers1.pl)Бу-эфиры (buffers1.pl)

div {div { div{'menu'};div{'menu'}; my $some = 'some';my $some = 'some'; div{$some},div{$some}, div{'tail'}div{'tail'}};};

Page 68: DSLs в Perl

ЯХУ! :)ЯХУ! :)

Page 69: DSLs в Perl

Грабли №3Грабли №3

Page 70: DSLs в Perl

Грабли №3Грабли №3

div {div { 'some';'some'; my $some = 'some';my $some = 'some'; div{$some}div{$some}};};

Page 71: DSLs в Perl

Грабли №3Грабли №3

print 'some';print 'some';outs('some');outs('some');x {'some'}x {'some'}{ my $x=...; 'some', div{$x} }{ my $x=...; 'some', div{$x} }

Page 72: DSLs в Perl

Установка Установка функцийфункций

Page 73: DSLs в Perl

Установка функцийУстановка функций

my @tags = qw(a b);my @tags = qw(a b);foreach my $t ( @tags ) {foreach my $t ( @tags ) { no strict 'refs';no strict 'refs'; *{'main::'.$t} = sub (&) {*{'main::'.$t} = sub (&) { ...... };};}}

Page 74: DSLs в Perl

Установка функцийУстановка функций

sub import;sub import;Exporter;Exporter;Symbol;Symbol;И прочиеИ прочие

Page 75: DSLs в Perl

Прото-цепочкиПрото-цепочки

Page 76: DSLs в Perl

Это не химияЭто не химияИ не биологияИ не биология

Page 77: DSLs в Perl

ЭтоЭто(&;$)(&;$)

Page 78: DSLs в Perl

Прото-цепочкиПрото-цепочки

sub a(&;$) {sub a(&;$) { print 'a ', context wantarray, "\n"print 'a ', context wantarray, "\n"}}sub b(&;$) {sub b(&;$) { print 'b ', context wantarray, "\n"print 'b ', context wantarray, "\n"}}

Page 79: DSLs в Perl

Прото-цепочкиПрото-цепочки

a {} b {};a {} b {};

bb scalar scalara voida void

Page 80: DSLs в Perl

Промежуточное Промежуточное прдставлениепрдставление

Page 81: DSLs в Perl

Пром-представленияПром-представления*{'main::'.$t} = sub (&;$) {*{'main::'.$t} = sub (&;$) { my ($code, $next) = @_;my ($code, $next) = @_; unless ( defined wantarray ) {unless ( defined wantarray ) { return _tag($t, $code, $next);return _tag($t, $code, $next); } else {} else { return return bless subbless sub { { return _tag($t, $code, $next)return _tag($t, $code, $next) }, 'MyTag';}, 'MyTag'; }}};};

Page 82: DSLs в Perl

Пром-представленияПром-представления

package MyTag;package MyTag;use overload '""' => sub {use overload '""' => sub { return buffered($_[0])return buffered($_[0])};};1;1;

Page 83: DSLs в Perl

Пром-представленияПром-представления

a {'head'}a {'head'} b {'middle'}b {'middle'}

c {'tail'};c {'tail'};im_represent.plim_represent.pl

Page 84: DSLs в Perl

ЭкранированиеЭкранирование

Page 85: DSLs в Perl

ЭкранированиеЭкранирование

sub _escape {sub _escape { return unless defined $_[0];return unless defined $_[0]; my $v = shift;my $v = shift; $v = „$v”$v = „$v”;; $v =~ s/.../.../g;$v =~ s/.../.../g; return $v;return $v;}}

Page 86: DSLs в Perl

ЭкранированиеЭкранирование

sub escape(@_) {sub escape(@_) { return mapreturn map blessed($_)blessed($_) && $_->isa('MyTag')&& $_->isa('MyTag') ? $_ : _escape($_),? $_ : _escape($_), @_;@_;}}

Page 87: DSLs в Perl

ЭкранированиеЭкранированиеsub buffered {sub buffered { ...... return join '', $buf, return join '', $buf, escape(@tail);escape(@tail);}}sub _tag {sub _tag { my ($tag, $code, $next) = @_;my ($tag, $code, $next) = @_; ...... print join '', $res, escape $next;print join '', $res, escape $next;}}

Page 88: DSLs в Perl

Все работаетВсе работаетescaping.plescaping.pl

Page 89: DSLs в Perl

На закуску к экранамНа закуску к экранам

sub outs(@) { print _escape(join '', @_) }sub outs(@) { print _escape(join '', @_) }

sub raw(@) { print join '', @_ }sub raw(@) { print join '', @_ }

# грабли №3# грабли №3div { div { outs('some');outs('some'); div {...} }; div {...} };

Page 90: DSLs в Perl

MyTD готов.MyTD готов.Осталось Осталось только...только...

Page 91: DSLs в Perl

Недостающие частиНедостающие части

объединить все примерыобъединить все примерызапаковать всезапаковать все

sub template($$);sub template($$);# template 'index' => run {};# template 'index' => run {};sub show($@);sub show($@);# show 'index', arg => $arg, ...;# show 'index', arg => $arg, ...;

Page 92: DSLs в Perl

ПосмотретьПосмотреть

Template::DeclareTemplate::DeclareJifty::DispatcherJifty::DispatcherJifty::Param::SchemaJifty::Param::SchemaJifty::DBI::SchemaJifty::DBI::SchemaObject::DeclareObject::DeclareB::*B::*

Page 93: DSLs в Perl

ВСЕВСЕ

Page 94: DSLs в Perl

Вопросы?Вопросы?