DSLs в Perl

Post on 22-Jun-2015

369 views 5 download

Tags:

description

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

Transcript of DSLs в Perl

DSLs в PerlDSLs в Perl

Как?Как?

Руслан Закиров <Руслан Закиров <ruz@bestpractical.comruz@bestpractical.com>>Best Practical Solutions, 2008Best Practical Solutions, 2008

Что?Что?

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

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 {};}}

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

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

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

Как?Как?

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

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

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

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

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

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

() ()

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

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

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

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

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

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

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

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

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

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

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

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

"is""is"

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

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

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

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

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

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

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

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

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

Объеденим (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 />”;}}

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

Усложним (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 {...}, },}, # ...# ... },},););

Усложним (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];}}

Усложним (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);}}

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

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

Результат (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"/>/>

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

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

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

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

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

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

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

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

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

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

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

Отладка (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...

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

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

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

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

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

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

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

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

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

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

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

Без методов (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->(); ......}}

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

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

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

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

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

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

Вложения (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'} };

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

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

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

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

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

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

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

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

Простое решение (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>";}}

Простое решение (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} };

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

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

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

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

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

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

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

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>”; }}}}

Контексты (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>

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

Бу-эфиры (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->());}}

Бу-эфиры (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>”;}}

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

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

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

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

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

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

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

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

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

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

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 (&) { ...... };};}}

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

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

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

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

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

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

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"}}

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

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

bb scalar scalara voida void

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

Пром-представленияПром-представления*{'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'; }}};};

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

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

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

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

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

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

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

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;}}

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

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

ЭкранированиеЭкранирование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;}}

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

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

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 {...} };

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

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

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

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

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

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

ВСЕВСЕ

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