Perl5の静的解析入門 機械と人間双方の歩み寄りによる平和編
YAPC::Tokyo 2019 Room1 14:00 -
Perl5ͷ੩తղੳೖػցͱਓؒํͷาΈدΓʹΑΔฏฤYAPC::Tokyo 2019໘ന๏ਓΧϠοΫ macopy a.k.a @mackee_w#yapcjapan #yapcjapanRoom11
View Slide
͜ͷεϥΠυ2
͜ͷτʔΫͷ༰3
ಘΒΕΔࣝɾମݧ (1)•ਖ਼نදݱͰPerlίʔυΛύʔε͢ΔPPR.pmͷ͍ํ• ۀͰͷ۩ମతͳ༻ྫ•ਓؒͱػցʹ͔Γʹ͍͘ίʔυͱ͠ํͷఏҊ4
ಘΒΕΔࣝɾମݧ (2)•PerlίʔυͷؔʹܕγάωνϟΛ͚Δ•ؔͷܕγάωνϟΛPPRͰൈ͖ग़͢5
ͷྲྀΕ1. ੩తղੳͱ2. PPIΛ͏ྫ3. PPRͷઆ໌ͱྫ4. ػցͱฏͷ5. ੩తܕ͚ͷೖΓޱ6
ࢲͱɾɾɾʁ• ໘ന๏ਓΧϠοΫ ιʔγϟϧήʔϜࣄۀ෦• ࣄݴޠPerl5ͱGo• झຯ3DϓϦϯλ࡞Γ• աڈͷτʔΫ: ήʔϜӡ༻ͷͱ͔Ϛελσʔλͷͱ͔WebSocket7
ΠϯλʔωοτͰ͜͏͍͏ΞΠίϯͰ͢8
[Ad]͜ͷτʔΫۀ࣌ؒʹॻ͖·ͨ͠9
੩తղੳͱҰମɾɾɾʁ10
static analysis੩తղੳ11
ίʔυΛͨͩͷจࣈྻͱͯ͠ѻ͏12
Γํ 1ਖ਼نදݱͰҾֻ͚ͬͨΓී௨ͷςΩετͱͯ͠ѻ͏use Path::Tiny;my $target_package = "Example";my $script = path("$target_package.pm")->slurp;my $is_valid_package = $script =~ /\Apackage $target_package;/;13
͜͏͍͏ͷ͕ग़ͯ͘Δͱ؆୯ʹ٧ΉuseExample;14
ΑΓ࣮֬ͳΓํͳ͍ͷ͔ʂ15
Γํ 2perl͕Δ͜ͱΛ్த·ͰͬͯɺநߏจΛར༻͢Δ16
ίϥϜ: ࠷ۙͷݴޠͩͱݴޠຊମͷASTΛར༻Ͱ͖Δ͜ͱ͕͋Δ•Goͷ go/parser go/token go/types go/ast• GoίϯύΠϥ͜ΕΒΛ͍ͬͯΔ• ͦ͏Ͱͳ͍Β͍͠•Rubyͷ RubyVM::AST• 2.6.0͔Βར༻Մೳ17
੩తղੳͷϝϦοτ (1)•ίʔυͷݟͨ··Λѻ͑Δ• நߏจҎલͰ͋ΕίϝϯτͷதΛѻ͑Δ͜ͱ•ݴޠػೳΛ͑ͨදݱྗ• ޙ͔Β੩తܕ͚ͱ͔18
࣮ߦग़དྷΔঢ়ଶʹͳΔʹͭΕͯιʔείʔυ্ͷใൈ͚མ͍ͪͯ͘19
࣮ߦ࣌ղੳͰར༻Ͱ͖ͳ͍ใ•ۭന, վߦ, ίϝϯτ• ಛघͳॻ͖ํͰར༻Ͱ͖Δ߹͋Δ(ྫ: Pythonͷdocstring)20
࣮ߦ࣌ղੳͰར༻Ͱ͖ͳ͍ใ•τʔΫϯͷҐஔલޙͷτʔΫϯ• ར༻͞Ε͍ͯΔม͕࣮ࡍʹͲ͜Ͱએݴ͞Ε͍ͯΔ͔ͱ͔• ؔจͷҐஔͳͲελοΫτϨʔεͰར༻͢ΔͨΊʹ͍͍ͯΔ21
੩తղੳͷϝϦοτ (2)•࣮ߦͤͣʹίʔυΛѻ͑Δ• BEGINͰϠόΠ͜ͱ͍ͯͯ͠େৎ• ٯʹݴ͏ͱBEGINͰେࣄͳ͜ͱ͍ͯͯ͠ݕ͍͠(ޙड़)22
੩తղੳͷϝϦοτ (3)•ͯ͢ͷ࣮ߦύεʹ͍ͭͯௐ্͛ΒΕΔ߹͕͋Δ• ࣮ߦ࣌ʹܕόϦσʔγϣϯ͢Δ߹ɺ࣮ߦ͢Δ·Ͱޭ͢Δ͔Ͳ͏͔͔Βͳ͍23
੩తղੳͷσϝϦοτ•PerlͷύʔεΛࣗલ(Ϟδϡʔϧ͏͕)ͰΒͳ͍ͱ͍͚ͳ͍͔Βେม•ͦͦߏΛಡΜͰ͍ͬͯͲ͏ʹ͔͢Δͷେม•perlͷؾ࣋ͪʹͳΒͳ͍ͱ͍͚ͳͯ͘େม24
ࢲ͕੩తղੳ͍ͨ͠ಈػػցͷྗΛआΓͯؒҧ͍ͷͳ͍ίʔυΛॻ͖͍ͨʂ25
Perl5Ͱ੩తղੳ͢ΔͨΊͷπʔϧ26
Perl5੩తղੳքͷୈҰਓऀPPI27
PPIParse, Analyze and Manipulate Perl (withoutperl)•Pure Perl͚ͩͰPerlίʔυΛύʔεͯ͠੩తղੳͰར༻Ͱ͖Δܗʹ͢ΔϞδϡʔϧ28
༻ྫ (1)use PPI;use PPI::Dumper;my $document = PPI::Document->new(\'my $v1 = $v2;');PPI::Dumper->new($document)->print;29
༻ྫ (2)PPI::DocumentPPI::Statement::VariablePPI::Token::Word 'my'PPI::Token::Whitespace ' 'PPI::Token::Symbol '$v1'PPI::Token::Whitespace ' 'PPI::Token::Operator '='PPI::Token::Whitespace ' 'PPI::Token::Symbol '$v2'PPI::Token::Structure ';'30
༻ྫ (3)my ($stmt) = $document->children;my $op = $stmt->find_first("PPI::Token::Operator");$op->content; # => "="31
PPIͷಛ (1)•PDOM(ύʔεޙͷߏͷ͜ͱ)͔Βίʔυʹॻ͖͢͜ͱ͕ग़དྷΔ$document->prune("PPI::Token::Whitespace");$document->serialize; # => "my$v1=$v2;"32
PPIͷಛ (2)•ॻ͖ͨ͢ΊʹτʔΫϯεϖʔεɺ;ͳͲอ࣋ͨ͠··•͋͘·Ͱղੳ͢ΔͨΊͷߏͰ͋ͬͯɺ࣮ߦ͢ΔͨΊͷߏͰͳ͍33
PPIͷσϝϦοτ•݁ߏ͍• 1175ߦͷ࣮ࡍͷۀͷίʔυΛύʔεͨ݁͠Ռ• 6.80/s11 Macbook Pro 15inch mid 2015 Ͱܭଌ34
ͦͷଞͷϞδϡʔϧ•Compiler::Lexer ಠࣗͷCݴޠͰॻ͔ΕͨτʔΧφΠβΛ༻•Perl::Lexer perlຊମͷ෦APIΛୟ͍ͯτʔΫϯྻΛऔΓग़͢35
PPRͷհͱ͍ํ36
PPRPattern-based Perl Recognizer•PerlίʔυͷύλʔϯΛఆٛͯ͠ݕͰ͖Δਖ਼نදݱΛ࡞ΕΔϞδϡʔϧ37
༻ྫ# my $hoge = "fuga"; ͷม໊ΛҾֻ͚ͬΔʹmy $matcher = qr{my (?&PerlOWS)((?&PerlVariableScalar)) (?&PerlOWS)(?&PerlAssignmentOperator) (?&PerlOWS)(?&PerlString) (?&PerlOWS) ;$PPR::GRAMMAR}x38
༻ྫmy ($var) =grep { defined } $script =~ $matcher;͜ΕͰ $var ʹ "$hoge" ͕ೖΔ39
ศརʂʂʂ40
ʮΘʔʂ͍͖ͳΓϠόΠਖ਼نදݱ͕ग़͖ͯͨͧʂʂʯ41
͍ͬͯͩ͘͞ղઆ͠·͢42
PPRPerlίʔυͷτʔΫϯʹϚον͢Δਖ਼نදݱͷύλʔϯूPerlίʔυྫ PPRͷύλʔϯmy (?&PerlBuiltinFunction)$var (?&PerlVariableScalar)= (?&PerlAssignmentOperator)"hogehoge" (?&PerlString)43
සग़ύλʔϯ•(?&PerlOWS)• θϩจࣈҎ্ͷPerl্ͷۭനจࣈ• ۭനվߦΛؚΉ44
࣮༻ྫ45
ฐࣾͩͱ͜͏͍͏όϦσʔγϣϯΛΑ͍ͬͯ͘Δ͚ΕͲsub do_something {my $self = shift;state $rule = Data::Validator->new(a1 => "Str",b1 => "UInt",);my $args = $rule->validate(@_);my ($a1, $b1) = $args->{qw/a1 b1/};# do something...}46
ϓϥΠϕʔτϝιου͡Όͳ͔ͬͨΒͪΌΜͱόϦσʔγϣϯͯ͠ΔΑͶνΣοΫ47
εΫϦϓτ͔ΒؔఆٛΛൈ͖ग़͢my $script = ...;my $sub_matcher = qr{((?&PerlSubroutineDeclaration))$PPR::GRAMMAR}x;my @decls = grep { defined } $script =~ m{$sub_matcher}gx;48
ؔఆ͔ٛΒ໊ؔͱϒϩοΫΛൈ͖ग़͢my $subname_matcher = qr{\Asub (?&PerlOWS)((?&PerlQualifiedIdentifier)) (?&PerlOWS)((?&PerlBlock))$PPR::GRAMMAR}x;for my $decl (@decls) {my ($subname, $block) =grep { defined } $decl =~ $subname_matcher;49
໊͕ؔΞϯμʔείΞ࢝·ΓͳΒεΩοϓ͢Δfor my $decl (@decls) {my ($subname, $block) =grep { defined } $decl =~ $subname_matcher;next if $subname =~ /\A_/;50
validatorͷఆٛmy $validator_matcher = qr{((state) (?&PerlOWS)(?&PerlVariableScalar) (?&PerlOWS)(?&PerlAssignmentOperator) (?&PerlOWS)Data::Validator->new (?&PerlOWS)(?&PerlParenthesesList) (?&PerlOWS))$PPR::GRAMMAR}x;51
validatorͷఆ͕ٛ͋Δ͔ΛௐΔfor my $decl (@decls) {...next if $subname =~ /\A_/;my ($validator) =grep { defined } $block =~ $validator_matcher;52
͋ͱࣽΔͳΓম͘ͳΓ# warningΛग़͢ͳΓif (!defined $validator) {warn "$subname Ͱ Data::Validator ͕ΘΕͯͳ͍Αʂ";}# ςετͰ͚ͤ͜͞ΔͳΓuse Test::More;fail "$subname Ͱ Data::Validator ͕ΘΕͯͳ͍Αʂ";53
͜ͷνΣοΫͰൈ͚͍ͯΔͱ͜Ζ•ҾΛऔΒͳ͍ؔΛޡݕ•AttributeSubroutine signature•ҾόϦσʔγϣϯҎ֎ͷvalidator54
ᘳΛࢦͣ͞ʹࣗͷ࣋ͭίʔυ͚ͩͰ௨Εྑ͍͋ΔఔαϘΔ55
͞ΒʹԠ༻Ҋ•Smart::Args Λ͏Α͏ʹஔ͢Δ• όϦσʔλͷதͷݸʑͷఆٛ·Ͱόϥͯ͠ൈ͖ग़ͤΔ•LSPͳͲͰҾϦετΛग़͢56
PPRਖ਼نදݱͰίʔυͷҰ෦Λൈ͖ग़ͯ͠ݕࠪஔʹ͑Δ57
ͦ͏͍͏తͷطଘͷϞδϡʔϧ•Perl::Critic• PPIΛͬͨνΣοΧʔ•Perl::Lint• Compiler::LexerΛͬͨνΣοΧʔ58
੩తղੳͰίʔυͷ࣏҆ΛΑ͍ͯ͘͘͠ʹʁ59
ػցͱਓؒʹྑ͍ίʔυͱཆΪϓεͱͯ͠ͷ੩తղੳ60
perlೲಘ͕Ͱ͖Δڍಈ͢Δ͠ڻ͖MAXͷڍಈΛى͜͢͜ͱ͋Δ61
Only perl can parse Perl (1)•Only perl can parse Perl ͳ෦•PPIͷυΩϡϝϯτΑΓ@result = (dothis $foo, $bar);# Which of the following is it equivalent to?@result = (dothis($foo), $bar);@result = dothis($foo, $bar);62
Only perl can parse Perl (2)$ perl -MO=Deparse -e '@result = (dothis $foo, $bar);'@result = ($foo->dothis, $bar);-e syntax OK$ perl -MO=Deparse -e 'sub dothis {} @result = (dothis $foo, $bar);'sub dothis {}@result = dothis($foo, $bar);-e syntax OK63
Γ͍ͨํʹ໌ࣔ͢Δʂʂʂ@result = dothis($foo, $bar);•͍ΖΜͳॻ͖ํ͕Ͱ͖Δݴޠ͔ͩΒͦ͜ɺΓ͍ͨ͜ͱΛओு͢Δ64
Only perl can parse Perl (3)sub hoge { "hoge" }sub f {{hoge() => "fuga"}}# hashref or list ?my @result = f();65
Only perl can parse Perl (4)use DDP; p @result;# [# [0] "hoge",# [1] "fuga"# ]ϦετʹͳΔͷ͕ਖ਼ղ66
Only perl can parse Perl (5)hashrefʹ͢Δʹʁ•return Λ͚Δ•ϑΝοτΧϯϚͷࠨลΛจࣈྻʹ͢Δ•+{} ʹ͢Δ67
ͭ·ΓͪΌΜͱreturnॻ͚ղܾ͢Δsub hoge { "hoge" }sub f {return {hoge() => "fuga"};}# hashref desu!!!my @result = f();68
Only perl can parse Perl (6)•1ͭͷه߸͕ෳͷҙຯʹΘΕ͍͍ͯͯ͠• είʔϓͱϋογϡϦϑΝϨϯεͷ {}• আࢉͱਖ਼نදݱͷ /69
ʮ͜Ε͔ͩΒPerlʂʯ70
!͚ΕͲίʔυͷ໎͍ࢥߟͷ໎͍Ұݺٵ͓͍ͯɺ͏গ͠ߟ͑Α͏71
άϨούϏϦςΟgreppability72
greppability•ૂͬͨίʔυΛgrepͰݕͰ͖Δ͔Ͳ͏͔• ΄͔ͷఆ͕ٛ͋ͬͨΒڭ͍͑ͯͩ͘͞$ hw 'Greeter->say_hello' lib | \xargs perl -pie 's/Greeter->say_hello/Greeter->send_hello'73
greppability͕͍ίʔυ•ϝιου໊ΛಈతʹΈཱ͍ͯͯΔmy @params = qr/str vit dex int/;my $total = sum(map {my $method = $_ . "_factor";$person->$method;} @params);74
ϝιου໊ΛಈతʹΈཱͯͨͱ͖ͷฐ•ʮ͜ͷϝιουͲ͜Ͱ͍ͬͯΔʁʯ͕͘͠ͳΔ•ϝιουݺͼग़͠Λ੩తղੳͰநग़͢Δͱ͖ʹఆͰ͖ͳ͘ͳΔͱʹ͔͘ػցʹ༏͘͠ͳ͍ʂʂʂ75
ͤΊͯจࣈྻ࿈݁ΛΊΔmy @methods = qr/str_factorvit_factordex_factorint_factor/;my $total = sum(map { $person->$_ } @methods);•ϝιου໊͕ϢχʔΫͰ͋ΕgrepʹҾ͔͔ͬͬͯ͘Δ76
ಈతܕ͚ݴޠͰ৺ʹ੩తܕ͚ίϯύΠϥΛ࣋ͭ•ίʔυΛॻ͍ͯܕ͕੩తʹܾఆ͢Δ͔Λߟ͑Δ•ΠϛϡʔλϒϧมͳͲϞμϯݴޠʹ͋Δػೳ੍ݶΛಋೖ͢Δͱྑ͍77
ͦͷଞ੩తղੳͰࠔΔͷ•࣮ߦ࣌ʹuse͢Δͷ͕ܾ·Δͱ͔ BEGINಈ͔͞ͳ͍ͱ֬ఆ͠ͳ͍ͱ͔•Mo+([uo]se)? ͷ has method ͦΕΛղऍ͢ΔͷΛ࡞Ε·ͩܕͷ੩తղܾ͕Ͱ͖Δ߹͕͋Δ78
ػցʹ༏͚͠ΕಡΉਓؒʹ͍͍ͩͨ༏͍͠•ॻ͘ਓؒʹݫ͘͠ͳΔ͜ͱ͕͋Δ• ॻ͘ͷҰճ͔ͭҰਓɺಡΉͷn(n>=1)ճ͔ͭmਓ•ػցʹ༏͚͠ΕػցͷࢧԉΛड͚Δ͜ͱ͕ग़དྷΔ79
ػցʹਓؒʹ༏͍͠PerlίʔυPerlͷαϒηοτʹͳΔ=> ੩తղੳͰྑ͘ͳ͍ίʔυΛݕͰ͖Δ80
ਓؒʹཉࡶ೦͕͋ΔͨΊɺࣗݾΛ͢Δͷ͍͠•ʮλΠϓଟ͘ͳΔ͔ΒखΛൈ͜͏ʯͬͯͳΔ͜ͱ͕͋Δ•ػցʹҙͯ͠Β͏• returnෆݕͷϙϦγʔ Perl::Criticʹ͋Δ81
ϋεϧʔϧ•Rubyͷࣄྫ Querly•ʮ͜͏͍͏ॻ͖ํ͏ͪͩͱڻ͖͕͍ͬͺ͍͔ͩΒνΣοΫ͍ͨ͠ΑͶʯ•PerlͰPPRͰݕՄೳ82
ͦͷଞϋεϧʔϧͷྫ•Plack::RequestͰ $c->req->param(...)Θͳ͍•DBIx::Class Ͱ ResultSet->search Λ arrayίϯςΩετͰड͚ͳ͍ͳͲͳͲ83
Ұ୴࣭λΠϜ84
͔͜͜Β͍࣌ؒͬͺ͍·ͰΓൈ͚·͢85
PPRΛ͍ͬͯͨ͘ΊʹPPRΛͬͱਂ͘Ζ͏ʂ86
PPRͷυΩϡϝϯτʹ͑Δਖ਼نදݱͷϧʔϧ͕ྻڍ͞Ε͍ͯΔ͕...87
υΩϡϝϯτಡΜͰ۩ମతʹԿʹϚον͢Δ͔Θ͔ΒΜͱ͜ΖΛίʔυͰಡΜͰΈΔ88
$PPR::GRAMMAR ΛݟͯΈΔmy $stmt_matcher = qr{\G (?&PerlOWS)((?&PerlStatement))$PPR::GRAMMAR # <= ίϨ}x;my @stmts = grep { defined } $script =~ m{$stmt_matcher}gcx;89
90
!1761ߦͱʹμϝʔδ͕དྷΔߦͷਖ਼نදݱ91
ਖ਼نදݱ͓͚!ʹཱ͔ͪ͏92
ಡΈํ (1)•(? ... ) ϧʔϧఆٛ•(?&PerlStatementSequence) ϧʔϧݺͼग़͠93
ಡΈํ (2)(?\x{FEFF}?+ # Optional BOM marker(?&PerlStatementSequence)) # End of ruleͶɺ؆୯Ͱ͠ΐʁ94
ಡΈํ (3)ͱ͖Ͳ͖͜͏͍͏পʹग़͘Θ͚͢ΕͲ(?# Optimized to match any Perl builtin name, without backtracking...(?=[^\W\d]) # Skip if possible(?>s(?>e(?>t(?>(?>(?>(?>hos|ne)t|gr)en|s(?>erven|ockop))t|p(?>r(?>iority|otoent)|went|grp))| g(?>et(?>p(?>r(?>oto(?>byn(?>umber|ame)|ent)|iority)|w(?>ent|nam|uid)|eername|grp|pid)|| r(?>e(?>ad(?>lin[ek]|pipe|dir)?|(?>quir|vers|nam)e|winddir|turn|set|cv|do|f)|index|mdir| c(?>h(?>o(?>m?p|wn)|r(?>oot)?|dir|mod)|o(?>n(?>tinue|nect)|s)|lose(?>dir)?|aller|rypt)| e(?>nd(?>(?>hos|ne)t|p(?>roto|w)|serv|gr)ent|x(?>i(?>sts|t)|ec|p)|ach|val(?>bytes)?+|of| l(?>o(?>c(?>al(?>time)?|k)|g)|i(?>sten|nk)|(?>sta|as)t|c(?>first)?|ength)| u(?>n(?>(?>lin|pac)k|shift|def|tie)|c(?>first)?|mask|time)| p(?>r(?>ototype|intf?)|ack(?>age)?|o[ps]|ipe|ush)| d(?>bm(?>close|open)|e(?>fined|lete)|ump|ie|o)| f(?>or(?>m(?>line|at)|k)|ileno|cntl|c|lock)| t(?>i(?>mes?|ed?)|ell(?>dir)?|runcate)95
Regexp::OptimizerͰੜ͞ΕͯΔΈ͍͔ͨͩΒେৎʂ96
͍͍ײ͡ͷࢹ֮ԽΛ͢Δ•ఆ͕ٛผͷͲΕΛࢀর͍ͯ͠Δ͔• (?&PerlVariable) • (?&PerlScalarAccess) ͱ• (?&PerlHashAccess) ͱ• (?&PerlArrayAccess) ͷ OR ͩΑ Έ͍ͨͳ97
͍͍ײ͡ͷՄࢹԽ (1)•PPRͷਖ਼نදݱͷจࣈྻΛൈ͖ग़͢• ਖ਼نදݱϦϑΝϨϯεͩͱ͍Ζ͍Ζ͍ͬͭͯ͘͘ΔͷͰίʔυ্ͷςΩετͦͷ··͕΄͍͠• PPRͰൈ͖ग़͢98
our $GRAMMAR = qr{ ... };# ίί ^^^^^^^^^99
͍͍ײ͡ͷՄࢹԽ (2)my $stmt_regexp = qr{our (?&PerlOWS) \$GRAMMAR (?&PerlOWS) = (?&PerlOWS) ((?&PerlRegex))$PPR::GRAMMAR}x;my ($regexp) = grep { defined } $ppr_script =~ /$stmt_regexp/gcx;# => "qr{ ... }"100
͍͍ײ͡ͷՄࢹԽ (3)•PPIx::Regexp Ͱ ਖ਼نදݱͷநߏจʹ͢Δuse PPIx::Regexp;my $tree = PPIx::Regexp->new($regexp);101
͍͍ײ͡ͷՄࢹԽ (4)•͋ͱ࠶ؼͰτʔΫϯΛऩू͍ͯ͘͠sub traverse {my ($tree, $bucket, $name) = @_;my $literal = "";for my $child ($tree->children) {if (ref $child eq "PPIx::Regexp::Token::Literal") {$literal .= $child->content;}elsif (ref $child eq "PPIx::Regexp::Token::Whitespace") {# skip}else {if (length($literal) > 1 && $literal ne '\n') {$bucket->{$name}->{'"' . $literal . '"'} = 1;}$literal = "";}102
͍͍ײ͡ͷՄࢹԽ (5)if (ref $child eq "PPIx::Regexp::Token::Recursion") {next if $child->name eq "PerlOWS";$bucket->{$name}->{$child->content} = 1;}if ($child->can("children")) {my $next_name = $name;if (ref $child eq "PPIx::Regexp::Structure::NamedCapture") {next if $child->name =~ /^PPR_/;$next_name = $name ? $name . " > (?&" . $child->name . ")" : "(?&" . $child->name . ")";#PPIx::Regexp::Dumper->new($child)->print;}traverse($child, $bucket, $next_name);}}}103
͍͍ײ͡ͷՄࢹԽ (6)(?&PerlPackageDeclaration)|-- "\}"|-- "package"|-- (?&PerlBlock)|-- (?&PerlNWS)|-- (?&PerlQualifiedIdentifier)`-- (?&PerlVersionNumber)(?&PerlParenthesesList)|-- "\("`-- (?&PerlExpression)(?&PerlPod)`-- "=cut"104
ࣗ༝ࣗࡏʹτʔΫϯΛൈ͖ग़ͤΔʂPPRͷதΘ͔ͬͨʂ105
͜͜·ͰདྷΔͱܕΛॻ͍͍ͯͨ͘ͳΓ·ͤΜ͔ʁ106
ਐత੩తܕ͚•ಈతܕ͚ݴޠʹ෦తʹ੩తܕ͚Λಋೖ͢Δ• TypeScript(JavaScript) Hack(PHP) ͳͲ107
•ޙ͢ΔʹҎԼͷཁૉ͕ඞཁ• ܕ͕ॻ͚ΔΑ͏ʹͳΔ (PythonͷPEP484 Type Hints)• ੩తղੳͰܕΛௐΔ͜ͱ͕ग़དྷΔ• ܕγεςϜΛ࣮͢Δ108
ܕ͢Ͱʹॻ͚Δpackage Greeter {use Function::Parameters;use Function::Return;use Types::Standard qw/Str Int Bool/;method say_hello(Str :$name, Int :$times) :Return(Bool) {say $self . ": Hello " . $name for 1..$times;return 1;}}Greeter->say_hello(name => "shinpei0213", times => 10);109
͜ͷؔͷγάωνϟΛPPRͰղੳ͢Δ110
ίʔυͱͯ͠ಡΜͰ͍Ε͜͏ग़དྷΔ͚ΕͲmy $pinfo = Function::Parameters::info \&Greeter::say_hello;my $rinfo = Function::Return::info \&Greeter::say_hello;111
ޙʑͷ͜ͱΛߟ͑Δͱ੩తղੳͰऔΓ͍ͨ112
ͬͯΈΑ͏ͱ͢Δ͕ͦͦPerlͷίʔυͱͯ͠͏·͘ೝࣝͯ͘͠Εͳ͍my $doc_matcher = qr{\A (?&PerlDocument) \Z$PPR::GRAMMAR}x;say !!($script =~ $doc_matcher); # => ""(false)113
keyword pluginmethod say_hello(Str :$name, Int :$times) :Return(Bool) {say $self . ": Hello " . $name for 1..$times;return 1;}keyword plugin perl 5.12.0 ͔Β͑Δจ๏֦ுػೳ114
PPRͦ͏͍͏ͷߟྀग़དྷΔ࡞Γʹͳ͍ͬͯΔ•ͦͦkeyword pluginΛ؆୯ʹॻ͚ΔKeyword::Declare ͷͨΊʹ࡞ΒΕͨϞδϡʔϧ•keywordͷϧʔϧΛ֦ுͰ͖Δ115
Function::ParametersʹରԠ͢Δ (1)my $FP_GRAMMAR = qr{(?(DEFINE)(?method (?&PerlOWS)(?&PerlIdentifier) (?&PerlOWS) # => say_hello(?&kw_balanced_parens) (?&PerlOWS) # => (Str :$name, Int :$times)(?:(?&PerlAttributes) (?&PerlOWS) # => :Return(Bool))?+(?&PerlBlock) # => { ... })(?(?&PerlFunctionParametersMethod))(?\( (?: [^()]++ | (?&kw_balanced_parens) )*+ \)))$PPR::GRAMMAR}x;116
Function::ParametersʹରԠ͢Δ (2)qr{(?method (?&PerlOWS)(?&PerlIdentifier) (?&PerlOWS) # => say_hello(?&kw_balanced_parens) (?&PerlOWS) # => (Str :$name, Int :$times)(?:(?&PerlAttributes) (?&PerlOWS) # => :Return(Bool))?+(?&PerlBlock) # => { ... })}117
Function::ParametersʹରԠ͢Δ (3)qr{(? # <= keyword֦ுͱͯ͠ొ(?&PerlFunctionParametersMethod))(?\( (?: [^()]++ | (?&kw_balanced_parens) )*+ \))}118
Function::ParametersʹରԠ͢Δ (4)my $doc_matcher = qr{\A (?&PerlDocument) \Z$FP_GRAMMAR}x;say !!($script =~ $doc_matcher); # => 1Perlίʔυͱͯ͠ೝࣝͯ͘͠Εͨʂ ͜ΕͰউͭΔʂ119
DEMO120
ܕ͕ݟΕͨ͋ͱͷ͍Έͪ•ܕγεςϜΛ࣮ͯ͠ਐత੩తܕ͚•Language Server Protocolʹར༻͢Δ• ΤσΟλ/IDEͰิϝιουใ͕ར༻Ͱ͖ΔΑ͏ʹͳΔ121
PPIͱPPRͷ͍͚•ίʔυͷͯ͢Λར༻͢ΔͳΒPPI•ίʔυͷҰ෦Λൈ͖ग़ͨ͠Γ༗Δແ͠ΛݟΔͳΒPPR• ਖ਼نදݱͳͷͰϦϑΝΫλϦϯάʹར༻Մೳ122
·ͱΊ•Perl5ेʹ੩తղੳ͕Ͱ͖Δ͜ͱΛࣔ͠·ͨ͠•۩ମతͳΓํ͍ಓʹ͍ͭͯ͠·ͨ͠•ػցͱਓؒͷڞଘʹ͍ͭͯ͠·ͨ͠123
Any Questions?124
Q: ࣏҆Λྑ͘͢ΔͨΊͷίʔυ͕࣏͕҆ѱ͍•A1: ਖ਼نදݱίʔυͷ࣏͕҆ѱ͘ͳΔҰͭͷཁૉͰ͕͢ɺίϝϯτϧʔϧΛ׆༻͠·͠ΐ͏125
Q: ࣏҆Λྑ͘͢ΔͨΊͷίʔυ͕࣏͕҆ѱ͍my $matcher = qr{# ͜͜ʹίϝϯτ͕ॻ͚·͢(?(DEFINE)(?# ͜͜ʹ͖ͳύλʔϯΛ࡞ͬͯύλʔϯʹ໊લΛ͚ͭΑ͏!))}x;126
Q: ͳΜͰͦ͜·Ͱͯ͠PerlΛ͏ΜͰ͔͢ (1)•A1: 10ສߦҎ্͋ΔPerlίʔυ͕͋ΓɺࠓͪΌΜͱಈ͍͍ͯͯɺͪΌΜͱอक͞Ε͍ͯΔ߹ʹଞͷݴޠʹஔ͖͑Δ͔Ͳ͏͔• ଞͷݴޠͷਐతܕ͚ͷ࣮͢Δಈػ127
Q: ͳΜͰͦ͜·Ͱͯ͠PerlΛ͏ΜͰ͔͢ (2)•A2: Perl5͔ͳΓջͷਂ͍ݴޠ• hackable• จ๏͕͔ͳΓࣗ༝ͰɺϞμϯͬΆ͍ॻ͖ํ͕Ͱ͖Δ• keyword plugin128