Upgrade to Pro — share decks privately, control downloads, hide ads and more …

事前知識なしで理解する、静的検査のいろは / Introduction about stati...

Avatar for Kuniwak Kuniwak
September 07, 2018

事前知識なしで理解する、静的検査のいろは / Introduction about static analysis without previous knowledge

私は Vim script の静的検査ツール「Vint」の開発者です。この発表では、Vint の開発を通じて学んだ静的検査ツールの仕組みと経験則を、事前知識のない方でも理解できるように詳解します。

この発表で触れるさまざまな基礎技術は、静的検査はもちろん、メタプログラミングやトランスパイラ、独自プログラミング言語の開発にも役立つ重要なものです。これらを技術の引き出しとして覚えておけば、今後の開発体験を豊かにしてくれることでしょう。

https://builderscon.io/tokyo/2018/session/0a3ee8c0-44ce-4cb9-9199-05b350887d79

Avatar for Kuniwak

Kuniwak

September 07, 2018
Tweet

More Decks by Kuniwak

Other Decks in Programming

Transcript

  1. $44ͷมߋ఺ΛՄࢹԽ͢Δπʔϧ https://github.com/mixi-inc/css-semdiff $ css-astdiff a.css b.css --verbose extra: .extra-1 {

    border: none; } missing: .missing-1 { border: none; } --------------------------------- 1 extra rules and 1 missing rules
  2. ίʔυϨϏϡʔࣗಈԽͷྫ http://alpha.mixi.co.jp/entry/2015/12/20/000000 $ css-astdiff a.css b.css --verbose extra: .extra-1 {

    border: none; } missing: .missing-1 { border: none; } --------------------------------- 1 extra rules and 1 missing rules  ߦͷ$44Λ
 ো֐θϩͰ-FTT΁
 Ҡߦͨ͠ࡍͷπʔϧ
  3. ந৅ߏจ໦ ίʔυΛ໦ߏ଄Ͱදݱͨ͠΋ͷ ଟ͘ͷ੩తղੳͷجຊ τʔΫϯྻ ίʔυͷখ͞ͳߏ੒୯ҐΛ
 ग़ݱॱʹฒ΂ͨ΋ͷ ελΠϧʹؔ͢Δ੩తղੳͰ
 Α͘࢖ΘΕΔ ੍ޚϑϩʔάϥϑ ίʔυͷ৚݅෼ذͳͲͷ


    ੍ޚߏ଄Λ࿈݁ͨ͠ϒϩοΫͰ
 දݱͨ͠΋ͷ ίʔϧάϥϑ ؔ਺ͷݺͼग़ؔ͠܎ΛάϥϑͰ
 දݱͨ͠΋ͷ ະ࢖༻ؔ਺ͷղੳͳͲʹΑ͘
 ࢖ΘΕΔ
  4. τʔΫϯྻ ίʔυͷখ͞ͳߏ੒୯ҐΛ
 ग़ݱॱʹฒ΂ͨ΋ͷ ελΠϧʹؔ͢Δ੩తղੳͰ
 Α͘࢖ΘΕΔ ੍ޚϑϩʔάϥϑ ίʔυͷ৚݅෼ذͳͲͷ
 ੍ޚߏ଄Λ࿈݁ͨ͠ϒϩοΫͰ
 දݱͨ͠΋ͷ ίʔϧάϥϑ

    ؔ਺ͷݺͼग़ؔ͠܎ΛάϥϑͰ
 දݱͨ͠΋ͷ ະ࢖༻ؔ਺ͷղੳͳͲʹΑ͘
 ࢖ΘΕΔ ந৅ߏจ໦ ίʔυΛ໦ߏ଄Ͱදݱͨ͠΋ͷ ଟ͘ͷ੩తղੳͷجຊ ࠓճղઆ͢Δ΋ͷ
  5. if isEmpty('') { // COMMENT } 1SPHSBN *G4UBUFNFOU 'VOD$BMM *EFOUJpFS

    $PNNFOU 4USJOH-JUFSBM ந৅ߏจ໦ ίʔυ
  6. if isEmpty('') { // COMMENT } 1SPHSBN *G4UBUFNFOU 4USJOH-JUFSBM 'VOD$BMM

    *EFOUJpFS $PNNFOU ந৅ߏจ໦ ίʔυ ந৅ߏจ໦͸ɺίʔυͷߏ଄Λ
 ໦ߏ଄ͷσʔλͱͯ͠දݱͨ͠΋ͷ
  7. 1ZUIPOͷந৅ߏจ໦ͷݟํ import ast source = "if is_empty(''):\n" \ " pass\n"

    node = ast.parse(source, "sample.py") print(ast.dump(node))
  8. if is_empty(''): pass ந৅ߏจ໦ 1ZUIPO Module( body=[ If( test=Call( func=Name(

    id='is_empty', ctx=Load() ), args=[ Str(s='') ], keywords=[] ), body=[ Pass() ], orelse=[] ) ] )
  9. if is_empty(''): pass ந৅ߏจ໦ 1ZUIPO Module( body=[ If( test=Call( func=Name(

    id='is_empty', ctx=Load() ), args=[ Str(s='') ], keywords=[] ), body=[ Pass() ], orelse=[] ) ] )
  10. if is_empty(''): pass ந৅ߏจ໦ 1ZUIPO Module( body=[ If( test=Call( func=Name(

    id='is_empty', ctx=Load() ), args=[ Str(s='') ], keywords=[] ), body=[ Pass() ], orelse=[] ) ] )
  11. if is_empty(''): pass ந৅ߏจ໦ 1ZUIPO Module( body=[ If( test=Call( func=Name(

    id='is_empty', ctx=Load() ), args=[ Str(s='') ], keywords=[] ), body=[ Pass() ], orelse=[] ) ] )
  12. if is_empty(''): pass ந৅ߏจ໦ 1ZUIPO Module( body=[ If( test=Call( func=Name(

    id='is_empty', ctx=Load() ), args=[ Str(s='') ], keywords=[] ), body=[ Pass() ], orelse=[] ) ] )
  13. if is_empty(''): pass ந৅ߏจ໦ 1ZUIPO Module( body=[ If( test=Call( func=Name(

    id='is_empty', ctx=Load() ), args=[ Str(s='') ], keywords=[] ), body=[ Pass() ], orelse=[] ) ] )
  14. ந৅ߏจ໦ 3VCZ [:program, [ [:if, [:method_add_arg, [:fcall, [:@ident, "is_empty", [1,

    3] ] ], [:arg_paren, [:args_add_block, [[:string_literal, [:string_content]]], false ] ] ], [[:void_stmt]], nil ] ] ] if is_empty('') end
  15. [:program, [ [:if, [:method_add_arg, [:fcall, [:@ident, "is_empty", [1, 3] ]

    ], [:arg_paren, [:args_add_block, [[:string_literal, [:string_content]]], false ] ] ], [[:void_stmt]], nil ] ] ] ந৅ߏจ໦ 3VCZ if is_empty('') end
  16. [:program, [ [:if, [:method_add_arg, [:fcall, [:@ident, "is_empty", [1, 3] ]

    ], [:arg_paren, [:args_add_block, [[:string_literal, [:string_content]]], false ] ] ], [[:void_stmt]], nil ] ] ] ந৅ߏจ໦ 3VCZ if is_empty('') end
  17. [:program, [ [:if, [:method_add_arg, [:fcall, [:@ident, "is_empty", [1, 3] ]

    ], [:arg_paren, [:args_add_block, [[:string_literal, [:string_content]]], false ] ] ], [[:void_stmt]], nil ] ] ] ந৅ߏจ໦ 3VCZ if is_empty('') end
  18. [:program, [ [:if, [:method_add_arg, [:fcall, [:@ident, "is_empty", [1, 3] ]

    ], [:arg_paren, [:args_add_block, [[:string_literal, [:string_content]]], false ] ] ], [[:void_stmt]], nil ] ] ] ந৅ߏจ໦ 3VCZ if is_empty('') end
  19. +BWB4DSJQUͷந৅ߏจ໦ͷݟํ import {default as Esprima} from "esprima"; const program =

    "if (isEmpty('')) {\n" + "}\n"; const node = Esprima.parseScript(program); console.log("%j", node);
  20. ந৅ߏจ໦ +BWB4DSJQU { "type": "Program", "body": [ { "type": "IfStatement",

    "test": { "type": "CallExpression", "callee": { "type": "Identifier", "name": "isEmpty" }, "arguments": [ { "type": "Literal", "value": "", "raw": "''" } ] }, "consequent": { "type": "BlockStatement", "body": [] }, "alternate": null } ], "sourceType": "script" } if (isEmpty('')) { }
  21. ந৅ߏจ໦ +BWB4DSJQU { "type": "Program", "body": [ { "type": "IfStatement",

    "test": { "type": "CallExpression", "callee": { "type": "Identifier", "name": "isEmpty" }, "arguments": [ { "type": "Literal", "value": "", "raw": "''" } ] }, "consequent": { "type": "BlockStatement", "body": [] }, "alternate": null } ], "sourceType": "script" } if (isEmpty('')) { }
  22. ந৅ߏจ໦ +BWB4DSJQU if (isEmpty('')) { } { "type": "Program", "body":

    [ { "type": "IfStatement", "test": { "type": "CallExpression", "callee": { "type": "Identifier", "name": "isEmpty" }, "arguments": [ { "type": "Literal", "value": "", "raw": "''" } ] }, "consequent": { "type": "BlockStatement", "body": [] }, "alternate": null } ], "sourceType": "script" }
  23. ந৅ߏจ໦ +BWB4DSJQU if (isEmpty('')) { } { "type": "Program", "body":

    [ { "type": "IfStatement", "test": { "type": "CallExpression", "callee": { "type": "Identifier", "name": "isEmpty" }, "arguments": [ { "type": "Literal", "value": "", "raw": "''" } ] }, "consequent": { "type": "BlockStatement", "body": [] }, "alternate": null } ], "sourceType": "script" }
  24. ந৅ߏจ໦ +BWB4DSJQU { "type": "Program", "body": [ { "type": "IfStatement",

    "test": { "type": "CallExpression", "callee": { "type": "Identifier", "name": "isEmpty" }, "arguments": [ { "type": "Literal", "value": "", "raw": "''" } ] }, "consequent": { "type": "BlockStatement", "body": [] }, "alternate": null } ], "sourceType": "script" } if (isEmpty('')) { }
  25. ߏจղੳͷ୅දత࣮૷ख๏ w ࠶ؼԼ߱๏ w όοΫτϥοΫ͋Γ w ૉ௚ͳ࣮૷ w 1BSTFS$PNCJOBUPS w

    1BSTFS&YQSFTTJPO(SBNNBS w όοΫτϥοΫͳ͠ w ʜ w ԋࢉࢠॱҐ๏ w ʜ ࠓճղઆ͢Δ࣮૷
  26. <ϓϩάϥϜ> := <จ>* EOF <จ> := <ifจ> | <ࣜจ> |

    <ίϝϯτจ> <ifจ> := "if " <ࣜ> " {\n" <จ>* "}" "\n" <ࣜจ> := <ࣜ> "\n" <ίϝϯτจ> := "//" [A-Z_ ]* "\n" <ࣜ> := <จࣈྻϦςϥϧ> | <ؔ਺ݺͼग़ࣜ͠> <จࣈྻϦςϥϧ> := "'" ("\'" | [A-Z_ ])* "'" <ؔ਺ݺͼग़ࣜ͠> := <ؔ਺໊> "(" <ࣜ> ")" <ؔ਺໊> := [a-z] [a-z]* ͜ͷ··ͩͱΘ͔ΓͮΒ͍ͷͰɺ۩ମྫΛग़͠·͢
  27. <ϓϩάϥϜ> := <จ>* EOF <จ> := <ifจ> | <ࣜจ> |

    <ίϝϯτจ> <ίϝϯτจ> := "//" [A-Z_ ]* "\n" ίϝϯτ͚ͩϓϩάϥϜ ద༻͞Εͨจ๏نଇ ͔͜͜ΒݟΔ // COMMENT ↩︎ EOF
  28. <ϓϩάϥϜ> := <จ>* EOF <จ> := <ifจ> | <ࣜจ> |

    <ίϝϯτจ> <ίϝϯτจ> := "//" [A-Z_ ]* "\n" ίϝϯτ͚ͩϓϩάϥϜ ద༻͞Εͨจ๏نଇ จͷ܁Γฦ͠ʹଓ͍ͯ&0'͕͘Δ // COMMENT ↩︎ EOF
  29. <ϓϩάϥϜ> := <จ>* EOF <จ> := <ifจ> | <ࣜจ> |

    <ίϝϯτจ> <ίϝϯτจ> := "//" [A-Z_ ]* "\n" ίϝϯτ͚ͩϓϩάϥϜ ద༻͞Εͨจ๏نଇ จͷத਎Λݟʹ͍͘ // COMMENT ↩︎ EOF
  30. <ϓϩάϥϜ> := <จ>* EOF <จ> := <ifจ> | <ࣜจ> |

    <ίϝϯτจ> <ίϝϯτจ> := "//" [A-Z_ ]* "\n" ίϝϯτ͚ͩϓϩάϥϜ ద༻͞Εͨจ๏نଇ จ͸͜ͷ͏ͪͷͲΕ͔ͭ // COMMENT ↩︎ EOF
  31. <ϓϩάϥϜ> := <จ>* EOF <จ> := <ifจ> | <ࣜจ> |

    <ίϝϯτจ> <ίϝϯτจ> := "//" [A-Z_ ]* "\n" ίϝϯτ͚ͩϓϩάϥϜ ద༻͞Εͨจ๏نଇ ࠓճͷจ͸ίϝϯτจ // COMMENT ↩︎ EOF
  32. <ϓϩάϥϜ> := <จ>* EOF <จ> := <ifจ> | <ࣜจ> |

    <ίϝϯτจ> <ίϝϯτจ> := "//" [A-Z_ ]* "\n" ίϝϯτจͷத਎Λݟʹ͍͘ ίϝϯτ͚ͩϓϩάϥϜ ద༻͞Εͨจ๏نଇ // COMMENT ↩︎ EOF
  33. <ϓϩάϥϜ> := <จ>* EOF <จ> := <ifจ> | <ࣜจ> |

    <ίϝϯτจ> <ίϝϯτจ> := "//" [A-Z_ ]* "\n" ʹଓ͍ͯӳେจࣈͱ@ͱۭന͕Կճ͔ଓ͍ͯɺվߦͰऴΘΔ ίϝϯτ͚ͩϓϩάϥϜ ద༻͞Εͨจ๏نଇ // COMMENT ↩︎ EOF
  34. // COMMENT ↩︎ EOF <ϓϩάϥϜ> := <จ>* EOF <จ> :=

    <ifจ> | <ࣜจ> | <ίϝϯτจ> <ίϝϯτจ> := "//" [A-Z_ ]* "\n" ίϝϯτ͚ͩϓϩάϥϜ ద༻͞Εͨจ๏نଇ
  35. // COMMENT ↩︎ EOF <ϓϩάϥϜ> := <จ>* EOF <จ> :=

    <ifจ> | <ࣜจ> | <ίϝϯτจ> <ίϝϯτจ> := "//" [A-Z_ ]* "\n" ίϝϯτ͚ͩϓϩάϥϜ ద༻͞Εͨจ๏نଇ ίʔυͱͷରԠ͕Ͱ͖ͨΒɺݟͨॱ൪ΛٯʹḪ͍ͬͯ͘
  36. // COMMENT ↩︎ EOF <ϓϩάϥϜ> := <จ>* EOF <จ> :=

    <ifจ> | <ࣜจ> | <ίϝϯτจ> <ίϝϯτจ> := "//" [A-Z_ ]* "\n" จ΋ίϝϯτจͰରԠ͕औΕͨͷͰḪΔ ίϝϯτ͚ͩϓϩάϥϜ ద༻͞Εͨจ๏نଇ
  37. <ϓϩάϥϜ> := <จ>* EOF <จ> := <ifจ> | <ࣜจ> |

    <ίϝϯτจ> <ίϝϯτจ> := "//" [A-Z_ ]* "\n" // COMMENT EOF ࠓճͷจ͸ճ܁Γฦ͚ͩ͠Ͱɺத਎͸ίϝϯτจ ίϝϯτ͚ͩϓϩάϥϜ ద༻͞Εͨจ๏نଇ
  38. <ϓϩάϥϜ> := <จ>* EOF <จ> := <ifจ> | <ࣜจ> |

    <ίϝϯτจ> <ίϝϯτจ> := "//" [A-Z_ ]* "\n" // COMMENT EOF ࠷ޙ͸&0'ͰऴΘΔ ίϝϯτ͚ͩϓϩάϥϜ ద༻͞Εͨจ๏نଇ
  39. print("TEXT") EOF ؔ਺ݺͼग़͚ͩ͠ϓϩάϥϜ ద༻͞Εͨจ๏نଇ <ϓϩάϥϜ> := <จ>* EOF <จ> :=

    <ifจ> | <ࣜจ> | <ίϝϯτจ> <ࣜจ> := <ࣜ> "\n" <ࣜ> := <จࣈྻϦςϥϧ> | <ؔ਺ݺͼग़ࣜ͠> <ؔ਺ݺͼग़͠> := <ؔ਺໊> "(" <ࣜ> ")" <ؔ਺໊> := [a-z] [a-z]*
  40. print("TEXT") EOF ؔ਺ݺͼग़͚ͩ͠ϓϩάϥϜ ద༻͞Εͨจ๏نଇ <ϓϩάϥϜ> := <จ>* EOF <จ> :=

    <ifจ> | <ࣜจ> | <ίϝϯτจ> <ࣜจ> := <ࣜ> "\n" <ࣜ> := <จࣈྻϦςϥϧ> | <ؔ਺ݺͼग़ࣜ͠> <ؔ਺ݺͼग़͠> := <ؔ਺໊> "(" <ࣜ> ")" <ؔ਺໊> := [a-z] [a-z]* ಉ͡Α͏ʹݟ͍͖ͯ·͠ΐ͏
  41. print("TEXT") EOF ؔ਺ݺͼग़͚ͩ͠ϓϩάϥϜ ద༻͞Εͨจ๏نଇ <ϓϩάϥϜ> := <จ>* EOF <จ> :=

    <ifจ> | <ࣜจ> | <ίϝϯτจ> <ࣜจ> := <ࣜ> "\n" <ࣜ> := <จࣈྻϦςϥϧ> | <ؔ਺ݺͼग़ࣜ͠> <ؔ਺ݺͼग़͠> := <ؔ਺໊> "(" <ࣜ> ")" <ؔ਺໊> := [a-z] [a-z]* <จࣈྻϦςϥϧ>
  42. <ϓϩάϥϜ> := <จ>* EOF <จ> := <ifจ> | <ࣜจ> |

    <ίϝϯτจ> <ifจ> := "if " <ࣜ> " {\n" <จ>* "}" "\n" <ࣜจ> := <ࣜ> "\n" <ίϝϯτจ> := "//" [A-Z_ ]* "\n" <ࣜ> := <จࣈྻϦςϥϧ> | <ؔ਺ݺͼग़ࣜ͠> <จࣈྻϦςϥϧ> := "'" ("\'" | [A-Z_ ])* "'" <ؔ਺ݺͼग़ࣜ͠> := <ؔ਺໊> "(" <ࣜ> ")" <ؔ਺໊> := [a-z] [a-z]* ͜ͷ͝ͱʹந৅ߏจ໦ͷϊʔυΛఆٛ͢Δ
  43. 1SPHSBN 4UBUFNFOU 4UBUFNFOU # <ϓϩάϥϜ> := <จ>* class ProgramNode(Node): def

    __init__(self, statement_nodes): # <ϓϩάϥϜ>͸ෳ਺ͷ<จ>͔ΒͳΔͷͰɺ # ؚ·ΕΔ<จ>Λ഑ྻͰอ࣋͢Δɻ self.statements = statement_nodes ϓϩάϥϜʹରԠ͢Δந৅ߏจ໦ͷϊʔυ จͷϊʔυͷ഑ྻ
  44. # <ϓϩάϥϜ> := <จ>* class ProgramNode(Node): def __init__(self, statement_nodes): #

    <ϓϩάϥϜ>͸ෳ਺ͷ<จ>͔ΒͳΔͷͰɺ # ؚ·ΕΔ<จ>Λ഑ྻͰอ࣋͢Δɻ self.statements = statement_nodes 4UBUFNFOU 4UBUFNFOU 1SPHSBN
  45. # <ϓϩάϥϜ> := <จ>* class ProgramNode(Node): def __init__(self, statement_nodes): #

    <ϓϩάϥϜ>͸ෳ਺ͷ<จ>͔ΒͳΔͷͰɺ # ؚ·ΕΔ<จ>Λ഑ྻͰอ࣋͢Δɻ self.statements = statement_nodes 1SPHSBN 4UBUFNFOU 4UBUFNFOU
  46. # <ifจ> := "if " <ࣜ> " {\n" จ* "}"

    "\n" class IfStatementNode(Node): def __init__(self, condition_node, then_statements): # if ͷޙͷ৚݅෦ͷ <ࣜ> Λอ࣋͢Δɻ self.condition_node = condition_node # { ͱ } ͷؒͷෳ਺ͷ <จ> Λ഑ྻͱͯ͠อ࣋͢Δɻ self.then_statements = then_statements JGจʹରԠ͢Δந৅ߏจ໦ͷϊʔυ
  47. # <ίϝϯτจ> := "//" [A-Z_ ]* "\n" def parse_comment_statement(text, index):

    # TODO: ͪΌΜͱ࣮૷͢Δ next_index = 1234 # TODO: Ծͷ࣮૷ node = CommentStatementNode( comment_content=" COMMENT" # TODO: Ծͷ࣮૷ ) return node, next_index ίϝϯτจʹରԠ͢Δؔ਺ͷҾ਺ͱ໭Γ஋Λ֬ೝ͠Α͏ ؔ਺ͷ಺෦͸·ͩԾͷ΋ͷͰɺ͋ͱͰ࣮૷͍ͯ͘͠
  48. # <ίϝϯτจ> := "//" [A-Z_ ]* "\n" def parse_comment_statement(text, index):

    # TODO: ͪΌΜͱ࣮૷͢Δ next_index = 1234 # TODO: Ծͷ࣮૷ node = CommentStatementNode( comment_content=" COMMENT" # TODO: Ծͷ࣮૷ ) return node, next_index ʮQBSTFʯ͸ʮߏจղੳ͢Δʯͷҙຯ
  49. # <ίϝϯτจ> := "//" [A-Z_ ]* "\n" def parse_comment_statement(text, index):

    # TODO: ͪΌΜͱ࣮૷͢Δ next_index = 1234 # TODO: Ծͷ࣮૷ node = CommentStatementNode( comment_content=" COMMENT" # TODO: Ծͷ࣮૷ ) return node, next_index ղੳ͍ͨ͠ςΩετશମ ղੳ͍ͨ͠ςΩετͷҐஔ
  50. # <ίϝϯτจ> := "//" [A-Z_ ]* "\n" def parse_comment_statement(text, index):

    # TODO: ͪΌΜͱ࣮૷͢Δ next_index = 1234 # TODO: Ծͷ࣮૷ node = CommentStatementNode( comment_content=" COMMENT" # TODO: Ծͷ࣮૷ ) return node, next_index ղੳͰ͖ͨίϝϯτจʹ
 ରԠ͢Δந৅ߏจ໦ͷϊʔυ ίϝϯτจ͕ऴΘͬͨҐஔ
  51. Ҿ਺ w ղੳ͢Δจࣈྻશମͱ࢝ΊΔҐஔ ໭Γ஋ w ղੳͰ͖ͨந৅ߏจ໦ͷϊʔυͱऴΘͬͨҐஔ w ࣦഊͨ͠Β/POFΛฦͯ͠ɺ
 Ґஔ͸ಈ͔͞ͳ͍ ͜͜ͰҾ਺ͱ໭Γ஋Λ੔ཧ͠Α͏ɿ

    ؔ਺͕૊Έ߹Θͤ΍͘͢ͳΔͱ
 ݴ͍ͬͯͨͷ͸͜ͷ෦෼ લͷؔ਺ʹΑΔղੳ͕ऴΘͬͨ
 Ґஔ͔Βɺ࣍ͷؔ਺ʹΑΔղੳΛ
 ࠶։͢ΔΠϝʔδ
  52. def parse_comment_statement(text, index): is_success, next_index = expect_keyword("//", text, index) if

    not is_success: return None, index comment_content_chars = [] while True: comment_content_char, next_index = \ expect_either(uppercase + [" ", "_"], text, next_index) if comment_content_char is None: break comment_content_chars.append(comment_content_char) is_success, next_index = expect_keyword("\n", text, next_index) if not is_success: return None, index comment_content_chars = "".join(comment_content_chars) return CommentStatementNode(comment_content_chars), next_index <ίϝϯτจ> := "//" [A-Z_ ]* "\n"
  53. def parse_comment_statement(text, index): is_success, next_index = expect_keyword("//", text, index) if

    not is_success: return None, index comment_content_chars = [] while True: comment_content_char, next_index = \ expect_either(uppercase + [" ", "_"], text, next_index) if comment_content_char is None: break comment_content_chars.append(comment_content_char) is_success, next_index = expect_keyword("\n", text, next_index) if not is_success: return None, index comment_content_chars = "".join(comment_content_chars) return CommentStatementNode(comment_content_chars), next_index <ίϝϯτจ> := "//" [A-Z_ ]* "\n" ίϝϯτจʹରԠ͢Δؔ਺
  54. def parse_comment_statement(text, index): is_success, next_index = expect_keyword("//", text, index) if

    not is_success: return None, index comment_content_chars = [] while True: comment_content_char, next_index = \ expect_either(uppercase + [" ", "_"], text, next_index) if comment_content_char is None: break comment_content_chars.append(comment_content_char) is_success, next_index = expect_keyword("\n", text, next_index) if not is_success: return None, index comment_content_chars = "".join(comment_content_chars) return CommentStatementNode(comment_content_chars), next_index ͜͜Ͱ͸͕JOEFYͷҐஔʹ͋Δ͔Ͳ͏͔֬ೝ͍ͯ͠Δ FYQFDU@LFZXPSE͸ޙͰઆ໌͢Δ͕ɺΛݟ͚ͭͨΒ
 JOEFYΛͷޙ·ͰҠಈͤ͞Δ <ίϝϯτจ> := "//" [A-Z_ ]* "\n"
  55. def parse_comment_statement(text, index): is_success, next_index = expect_keyword("//", text, index) if

    not is_success: return None, index comment_content_chars = [] while True: comment_content_char, next_index = \ expect_either(uppercase + [" ", "_"], text, next_index) if comment_content_char is None: break comment_content_chars.append(comment_content_char) is_success, next_index = expect_keyword("\n", text, next_index) if not is_success: return None, index comment_content_chars = "".join(comment_content_chars) return CommentStatementNode(comment_content_chars), next_index ͕Έ͔ͭΒͳ͚Ε͹ίϝϯτจͰ͸ͳ͍ͷͰऴྃ <ίϝϯτจ> := "//" [A-Z_ ]* "\n"
  56. def parse_comment_statement(text, index): is_success, next_index = expect_keyword("//", text, index) if

    not is_success: return None, index comment_content_chars = [] while True: comment_content_char, next_index = \ expect_either(uppercase + [" ", "_"], text, next_index) if comment_content_char is None: break comment_content_chars.append(comment_content_char) is_success, next_index = expect_keyword("\n", text, next_index) if not is_success: return None, index comment_content_chars = "".join(comment_content_chars) return CommentStatementNode(comment_content_chars), next_index ͜ͷ഑ྻʹίϝϯτ಺ͷจࣈΛ֨ೲ͍ͯ͘͠ <ίϝϯτจ> := "//" [A-Z_ ]* "\n"
  57. def parse_comment_statement(text, index): is_success, next_index = expect_keyword("//", text, index) if

    not is_success: return None, index comment_content_chars = [] while True: comment_content_char, next_index = \ expect_either(uppercase + [" ", "_"], text, next_index) if comment_content_char is None: break comment_content_chars.append(comment_content_char) is_success, next_index = expect_keyword("\n", text, next_index) if not is_success: return None, index comment_content_chars = "".join(comment_content_chars) return CommentStatementNode(comment_content_chars), next_index ίϝϯτ಺ͷจࣈ͸܁Γฦ͠ͳͷͰɺϧʔϓͰॲཧ͢Δ <ίϝϯτจ> := "//" [A-Z_ ]* "\n"
  58. def parse_comment_statement(text, index): is_success, next_index = expect_keyword("//", text, index) if

    not is_success: return None, index comment_content_chars = [] while True: comment_content_char, next_index = \ expect_either(uppercase + [" ", "_"], text, next_index) if comment_content_char is None: break comment_content_chars.append(comment_content_char) is_success, next_index = expect_keyword("\n", text, next_index) if not is_success: return None, index comment_content_chars = "".join(comment_content_chars) return CommentStatementNode(comment_content_chars), next_index JOEFYͷҐஔʹ<";@>͕͋Δ͔֬ೝ͍ͯ͠Δ FYQFDU@FJUIFSʹ͍ͭͯ͸ޙͰઆ໌͢Δ <ίϝϯτจ> := "//" [A-Z_ ]* "\n"
  59. def parse_comment_statement(text, index): is_success, next_index = expect_keyword("//", text, index) if

    not is_success: return None, index comment_content_chars = [] while True: comment_content_char, next_index = \ expect_either(uppercase + [" ", "_"], text, next_index) if comment_content_char is None: break comment_content_chars.append(comment_content_char) is_success, next_index = expect_keyword("\n", text, next_index) if not is_success: return None, index comment_content_chars = "".join(comment_content_chars) return CommentStatementNode(comment_content_chars), next_index ΋͠<";@>ʹҰக͠ͳ͔ͬͨΒɺίϝϯτͷ
 ಺༰෦෼͕ऴΘͬͨͷͰɺϧʔϓ͔Β୤ग़ <ίϝϯτจ> := "//" [A-Z_ ]* "\n"
  60. def parse_comment_statement(text, index): is_success, next_index = expect_keyword("//", text, index) if

    not is_success: return None, index comment_content_chars = [] while True: comment_content_char, next_index = \ expect_either(uppercase + [" ", "_"], text, next_index) if comment_content_char is None: break comment_content_chars.append(comment_content_char) is_success, next_index = expect_keyword("\n", text, next_index) if not is_success: return None, index comment_content_chars = "".join(comment_content_chars) return CommentStatementNode(comment_content_chars), next_index ΋͠<";@>Ͱ͋Ε͹ɺίϝϯτจࣈྻ͕·ͩଓ͔͘΋
 ͠Εͳ͍ͷͰɺจࣈΛه࿥ͯ͠ϧʔϓΛ࠶։ <ίϝϯτจ> := "//" [A-Z_ ]* "\n"
  61. def parse_comment_statement(text, index): is_success, next_index = expect_keyword("//", text, index) if

    not is_success: return None, index comment_content_chars = [] while True: comment_content_char, next_index = \ expect_either(uppercase + [" ", "_"], text, next_index) if comment_content_char is None: break comment_content_chars.append(comment_content_char) is_success, next_index = expect_keyword("\n", text, next_index) if not is_success: return None, index comment_content_chars = "".join(comment_content_chars) return CommentStatementNode(comment_content_chars), next_index ίϝϯτ಺ͷจࣈ͕ऴΘͬͨͷͰɺ࠷ޙʹ
 վߦ͕͋Δ͔Ͳ͏͔֬ೝ͍ͯ͠Δ <ίϝϯτจ> := "//" [A-Z_ ]* "\n"
  62. def parse_comment_statement(text, index): is_success, next_index = expect_keyword("//", text, index) if

    not is_success: return None, index comment_content_chars = [] while True: comment_content_char, next_index = \ expect_either(uppercase + [" ", "_"], text, next_index) if comment_content_char is None: break comment_content_chars.append(comment_content_char) is_success, next_index = expect_keyword("\n", text, next_index) if not is_success: return None, index comment_content_chars = "".join(comment_content_chars) return CommentStatementNode(comment_content_chars), next_index aO͕ͳ͚Ε͹ɺίϝϯτจͰ͸ͳ͍ͷͰ/POFͱ
 ݩͷJOEFYΛฦ͢ <ίϝϯτจ> := "//" [A-Z_ ]* "\n"
  63. def parse_comment_statement(text, index): is_success, next_index = expect_keyword("//", text, index) if

    not is_success: return None, index comment_content_chars = [] while True: comment_content_char, next_index = \ expect_either(uppercase + [" ", "_"], text, next_index) if comment_content_char is None: break comment_content_chars.append(comment_content_char) is_success, next_index = expect_keyword("\n", text, next_index) if not is_success: return None, index comment_content_chars = "".join(comment_content_chars) return CommentStatementNode(comment_content_chars), next_index ه࿥͓͍ͯͨ͠ίϝϯτ಺ͷจࣈΛ݁߹ͯ͠ɺ
 ίϝϯτͷ಺༰จࣈྻΛ෮ݩ͢Δ <ίϝϯτจ> := "//" [A-Z_ ]* "\n"
  64. def parse_comment_statement(text, index): is_success, next_index = expect_keyword("//", text, index) if

    not is_success: return None, index comment_content_chars = [] while True: comment_content_char, next_index = \ expect_either(uppercase + [" ", "_"], text, next_index) if comment_content_char is None: break comment_content_chars.append(comment_content_char) is_success, next_index = expect_keyword("\n", text, next_index) if not is_success: return None, index comment_content_chars = "".join(comment_content_chars) return CommentStatementNode(comment_content_chars), next_index ෮ݩͨ͠ίϝϯτจࣈྻ͔ΒίϝϯτจʹରԠ͢Δ
 ந৅ߏจ໦ͷϊʔυΛ࡞੒ͯ͠ɺਐΊͨJOEFYͱฦ͢ <ίϝϯτจ> := "//" [A-Z_ ]* "\n"
  65. <ίϝϯτจ> := "//" [A-Z_ ]* "\n" def parse_comment_statement(text, index): is_success,

    next_index = expect_keyword("//", text, index) if not is_success: return None, index comment_content_chars = [] while True: comment_content_char, next_index = \ expect_either(uppercase + [" ", "_"], text, next_index) if comment_content_char is None: break comment_content_chars.append(comment_content_char) is_success, next_index = expect_keyword("\n", text, next_index) if not is_success: return None, index comment_content_chars = "".join(comment_content_chars) return CommentStatementNode(comment_content_chars), next_index ίϝϯτจʹରԠ͢Δؔ਺Λॻ͚ͨ
  66. text = "abc" is_success, next_index = expect_keyword("a", text, 0) is_success,

    next_index = expect_keyword("x", text, 0) Bͷ࣍ͷJOEFYͳͷͰ JOEFYͷҐஔʢͳͷͰઌ಄ʣʹB͕͋ΔͷͰ5SVF ͳ͍৔߹͸JOEFYΛม͑ͳ͍ͷͰ JOEFYͷҐஔʢͳͷͰઌ಄ʣʹY͸ͳ͍ͷͰ'BMTF
  67. def expect_keyword(expected, text, index): expected_len = len(expected) actual = text[index:index+expected_len]

    if actual != expected: return False, index return True, index + expected_len def expect_either(expected_list, text, index): for expected in expected_list: is_success, next_index = expect_keyword(expected, text, index) if is_success: return expected, next_index return None, index
  68. def expect_keyword(expected, text, index): expected_len = len(expected) actual = text[index:index+expected_len]

    if actual != expected: return False, index return True, index + expected_len def expect_either(expected_list, text, index): for expected in expected_list: is_success, next_index = expect_keyword(expected, text, index) if is_success: return expected, next_index return None, index ৄࡉͳ࣮૷͸͋ͱͰղಡͯ͠Έ͍ͯͩ͘͞ʂ
  69. def parse_statement(text, index): if_statement_node, next_index = parse_if_statement(text, index) if if_statement_node

    is not None: return if_statement_node, next_index expr_statement_node, next_index = parse_expr_statement(text, index) if expr_statement_node is not None: return expr_statement_node, next_index comment_statement_node, next_index = parse_comment_statement(text, index) if comment_statement_node is not None: return comment_statement_node, next_index return None, index <จ> := <ifจ> | <ࣜจ> | <ίϝϯτจ>
  70. def parse_statement(text, index): if_statement_node, next_index = parse_if_statement(text, index) if if_statement_node

    is not None: return if_statement_node, next_index expr_statement_node, next_index = parse_expr_statement(text, index) if expr_statement_node is not None: return expr_statement_node, next_index comment_statement_node, next_index = parse_comment_statement(text, index) if comment_statement_node is not None: return comment_statement_node, next_index return None, index ·ͣɺJGจʹରԠ͢Δؔ਺ͰղੳͰ͖Δ͔Ͳ͏͔ࢼ͢ <จ> := <ifจ> | <ࣜจ> | <ίϝϯτจ>
  71. def parse_statement(text, index): if_statement_node, next_index = parse_if_statement(text, index) if if_statement_node

    is not None: return if_statement_node, next_index expr_statement_node, next_index = parse_expr_statement(text, index) if expr_statement_node is not None: return expr_statement_node, next_index comment_statement_node, next_index = parse_comment_statement(text, index) if comment_statement_node is not None: return comment_statement_node, next_index return None, index ΋͠ɺJGจͱͯ͠ͷղੳʹ੒ޭ͢Ε͹จ͸JGจͩͬͨͱ͍͏͜ͱ JGจʹରԠ͢Δந৅ߏจ໦ͱɺJGจͷղੳͰਐΜͩJOEFYΛฦ͢ <จ> := <ifจ> | <ࣜจ> | <ίϝϯτจ>
  72. def parse_statement(text, index): if_statement_node, next_index = parse_if_statement(text, index) if if_statement_node

    is not None: return if_statement_node, next_index expr_statement_node, next_index = parse_expr_statement(text, index) if expr_statement_node is not None: return expr_statement_node, next_index comment_statement_node, next_index = parse_comment_statement(text, index) if comment_statement_node is not None: return comment_statement_node, next_index return None, index JGจͱͯ͠ͷղੳʹࣦഊͨ͠Βɺ࣍ʹࣜจͱͯ͠ͷղੳΛࢼ͢ <จ> := <ifจ> | <ࣜจ> | <ίϝϯτจ>
  73. def parse_statement(text, index): if_statement_node, next_index = parse_if_statement(text, index) if if_statement_node

    is not None: return if_statement_node, next_index expr_statement_node, next_index = parse_expr_statement(text, index) if expr_statement_node is not None: return expr_statement_node, next_index comment_statement_node, next_index = parse_comment_statement(text, index) if comment_statement_node is not None: return comment_statement_node, next_index return None, index ΋͠ɺࣜจͱͯ͠ͷղੳʹ੒ޭ͢Ε͹จ͸ࣜจͩͬͨͱ͍͏͜ͱ ࣜจʹରԠ͢Δந৅ߏจ໦ͱɺࣜจͷղੳͰਐΜͩJOEFYΛฦ͢ <จ> := <ifจ> | <ࣜจ> | <ίϝϯτจ>
  74. def parse_statement(text, index): if_statement_node, next_index = parse_if_statement(text, index) if if_statement_node

    is not None: return if_statement_node, next_index expr_statement_node, next_index = parse_expr_statement(text, index) if expr_statement_node is not None: return expr_statement_node, next_index comment_statement_node, next_index = parse_comment_statement(text, index) if comment_statement_node is not None: return comment_statement_node, next_index return None, index ࣜจͱͯ͠ͷղੳʹ΋ࣦഊͨ͠Βɺ࠷ޙʹίϝϯτจͱͯ͠ͷղੳΛࢼ͢ <จ> := <ifจ> | <ࣜจ> | <ίϝϯτจ>
  75. def parse_statement(text, index): if_statement_node, next_index = parse_if_statement(text, index) if if_statement_node

    is not None: return if_statement_node, next_index expr_statement_node, next_index = parse_expr_statement(text, index) if expr_statement_node is not None: return expr_statement_node, next_index comment_statement_node, next_index = parse_comment_statement(text, index) if comment_statement_node is not None: return comment_statement_node, next_index return None, index ΋͠ɺίϝϯτจͱͯ͠ͷղੳʹ੒ޭ͢Ε͹จ͸ίϝϯτจͩͬͨͱ͍͏͜ͱ ίϝϯτจʹରԠ͢Δந৅ߏจ໦ͱɺίϝϯτจͷղੳͰਐΜͩJOEFYΛฦ͢ <จ> := <ifจ> | <ࣜจ> | <ίϝϯτจ>
  76. def parse_statement(text, index): if_statement_node, next_index = parse_if_statement(text, index) if if_statement_node

    is not None: return if_statement_node, next_index expr_statement_node, next_index = parse_expr_statement(text, index) if expr_statement_node is not None: return expr_statement_node, next_index comment_statement_node, next_index = parse_comment_statement(text, index) if comment_statement_node is not None: return comment_statement_node, next_index return None, index JGจɺࣜจɺίϝϯτจͷ͍ͣΕͰ΋ͳ͚Ε͹ɺ
 จͰ͸ͳ͍ͷͰɺ/POFͱݩͷJOEFYΛฦͯ͠ऴྃ <จ> := <ifจ> | <ࣜจ> | <ίϝϯτจ>
  77. def parse_statement(text, index): if_statement_node, next_index = parse_if_statement(text, index) if if_statement_node

    is not None: return if_statement_node, next_index expr_statement_node, next_index = parse_expr_statement(text, index) if expr_statement_node is not None: return expr_statement_node, next_index comment_statement_node, next_index = parse_comment_statement(text, index) if comment_statement_node is not None: return comment_statement_node, next_index return None, index <จ> := <ifจ> | <ࣜจ> | <ίϝϯτจ> ͜ͷΑ͏ʹʹରԠ͢Δؔ਺Λఆ͍ٛͯ͘͠ͱɺ
 ࠷ऴతʹϓϩάϥϜʹରԠ͢Δؔ਺ΛఆٛͰ͖Δ ͜ͷϓϩάϥϜʹରԠ͢Δؔ਺͕ɺҰൠతʹ
 ఏڙ͞Ε͍ͯΔߏจղੳثͱݺ͹ΕΔ΋ͷ
  78. 1SPHSBN *G4UBUFNFOU $PNNFOU *EFOUJpFS 4USJOH-JUFSBM 'VOD$BMM QSJOU݁Ռ 'VOD$BMMͷ͢΂ͯͷࢠΛ๚໰ͨ͠ͷͰɺ'VOD$BMM͔Β཭ΕΔ FOUFS1SPHSBN FOUFS*G4UBUFNFOU

    FOUFS'VOD$BMM FOUFS*EFOUJpFS MFBWF*EFOUJpFS FOUFS4USJOH-JUFSBM MFBWF4USJOH-JUFSBM MFBWF'VOD$BMM
  79. 1SPHSBN *G4UBUFNFOU *EFOUJpFS 4USJOH-JUFSBM 'VOD$BMM $PNNFOU QSJOU݁Ռ *G4UBUFNFOUͷ࣍ͷࢠͰ͋Δ$PNNFOUΛ๚ΕΔ FOUFS1SPHSBN FOUFS*G4UBUFNFOU

    FOUFS'VOD$BMM FOUFS*EFOUJpFS MFBWF*EFOUJpFS FOUFS4USJOH-JUFSBM MFBWF4USJOH-JUFSBM MFBWF'VOD$BMM FOUFS$PNNFOU
  80. 1SPHSBN *G4UBUFNFOU *EFOUJpFS 4USJOH-JUFSBM 'VOD$BMM $PNNFOU QSJOU݁Ռ $PNNFOUʹࢠ͸͍ͳ͍ͷͰɺ$PNNFOU͔Β཭ΕΔ FOUFS1SPHSBN FOUFS*G4UBUFNFOU

    FOUFS'VOD$BMM FOUFS*EFOUJpFS MFBWF*EFOUJpFS FOUFS4USJOH-JUFSBM MFBWF4USJOH-JUFSBM MFBWF'VOD$BMM FOUFS$PNNFOU MFBWF$PNNFOU
  81. 1SPHSBN *EFOUJpFS 4USJOH-JUFSBM 'VOD$BMM $PNNFOU *G4UBUFNFOU QSJOU݁Ռ *G4UBUFNFOUͷ͢΂ͯͷࢠΛ๚໰ͨ͠ͷͰɺ཭ΕΔ FOUFS1SPHSBN FOUFS*G4UBUFNFOU

    FOUFS'VOD$BMM FOUFS*EFOUJpFS MFBWF*EFOUJpFS FOUFS4USJOH-JUFSBM MFBWF4USJOH-JUFSBM MFBWF'VOD$BMM FOUFS$PNNFOU MFBWF$PNNFOU MFBWF*G4UBUFNFOU
  82. *EFOUJpFS 4USJOH-JUFSBM 'VOD$BMM $PNNFOU *G4UBUFNFOU 1SPHSBN QSJOU݁Ռ 1SPHSBNͷ͢΂ͯͷࢠΛ๚໰ͨ͠ͷͰɺ૸ࠪ׬ྃ FOUFS1SPHSBN FOUFS*G4UBUFNFOU

    FOUFS'VOD$BMM FOUFS*EFOUJpFS MFBWF*EFOUJpFS FOUFS4USJOH-JUFSBM MFBWF4USJOH-JUFSBM MFBWF'VOD$BMM FOUFS$PNNFOU MFBWF$PNNFOU MFBWF*G4UBUFNFOU MFBWF1SPHSBN
  83. *EFOUJpFS 4USJOH-JUFSBM 'VOD$BMM $PNNFOU *G4UBUFNFOU 1SPHSBN QSJOU݁Ռ ཁ͢Δʹɺந৅ߏจ໦Λਂ͞༏ઌ୳ࡧ͢Δؔ਺ FOUFS1SPHSBN FOUFS*G4UBUFNFOU

    FOUFS'VOD$BMM FOUFS*EFOUJpFS MFBWF*EFOUJpFS FOUFS4USJOH-JUFSBM MFBWF4USJOH-JUFSBM MFBWF'VOD$BMM FOUFS$PNNFOU MFBWF$PNNFOU MFBWF*G4UBUFNFOU MFBWF1SPHSBN
  84. class Node: def children(self): raise NotImplementedError() class ProgramNode(Node): def __init__(self,

    statement_nodes): self.statements = statement_nodes def children(self): return self.statements ந৅ߏจ໦ͷϊʔυ͕ࣗ਎ͷࢠΛฦ͢Α͏ʹཁٻ͢ΔجఈΫϥεΛ༻ҙ
  85. class Node: def children(self): raise NotImplementedError() class ProgramNode(Node): def __init__(self,

    statement_nodes): self.statements = statement_nodes def children(self): return self.statements ͦΕͧΕͷந৅ߏจ໦ͷϊʔυʹ࣮૷ͯ͠΋Β͏
  86. class Node: def children(self): raise NotImplementedError() class ProgramNode(Node): def __init__(self,

    statement_nodes): self.statements = statement_nodes def children(self): return self.statements ϓϩάϥϜͷ͢΂ͯͷࢠͷҰཡΛ഑ྻͱͯ͠ฦ͢ ࠓճ͸จͷ഑ྻҎ֎ʹࢠ͸ͳ͍ͷͰͦͷ··ฦͤ͹Α͍
  87. class IfStatementNode(Node): def __init__(self, condition_node, then_statements): self.condition_node = condition_node self.then_statements

    = then_statements def children(self): children = [self.condition_node] return children + self.then_statements ผͷέʔεͱͯ͠ɺJGจͷந৅ߏจ໦ͷϊʔυ΋ΈͯΈΑ͏
  88. class IfStatementNode(Node): def __init__(self, condition_node, then_statements): self.condition_node = condition_node self.then_statements

    = then_statements def children(self): children = [self.condition_node] return children + self.then_statements JGจͷࢠ͸৚݅෦ͷࣜͱɺ\^ͷதͷ
 ෳ਺ͷจͳͷͰɺ·ͱΊͯҰཡʹͯ͠ฦ͢
  89. class IfStatementNode(Node): def __init__(self, condition_node, then_statements): self.condition_node = condition_node self.then_statements

    = then_statements def children(self): children = [self.condition_node] return children + self.then_statements ಉ༷ʹɺ͢΂ͯͷந৅ߏจ໦ͷϊʔυʹDIJMESFOΛ࣮૷ͤ͞Δ
  90. def traverse(node, on_enter=None, on_leave=None): if on_enter is not None: on_enter(node)

    for child in node.children(): traverse(child, on_enter, on_leave) if on_leave is not None: on_leave(node)
  91. def traverse(node, on_enter=None, on_leave=None): if on_enter is not None: on_enter(node)

    for child in node.children(): traverse(child, on_enter, on_leave) if on_leave is not None: on_leave(node) ந৅ߏจ໦ͷϊʔυΛ๚ΕͨͷͰPO@FOUFSΛ࣮ߦ͢Δ ΋͠PO@FOUFS͕লུ͞Ε͍ͯΔ৔߹͸Կ΋͠ͳ͍
  92. def traverse(node, on_enter=None, on_leave=None): if on_enter is not None: on_enter(node)

    for child in node.children(): traverse(child, on_enter, on_leave) if on_leave is not None: on_leave(node) ந৅ߏจ໦ͷϊʔυ͔ΒࢠͷҰཡΛ
 औಘ͠ɺࢠΛ࠶ؼతʹ૸͍ࠪͯ͘͠
  93. def traverse(node, on_enter=None, on_leave=None): if on_enter is not None: on_enter(node)

    for child in node.children(): traverse(child, on_enter, on_leave) if on_leave is not None: on_leave(node) ࢠͷ૸͕ࠪऴΘͬͨͷͰɺࠓͷϊʔυ͔Βୀग़͢Δ
  94. def find_nodes(node, condition): found_nodes = [] def collect_nodes(entered_node): if not

    condition(entered_node): return found_nodes.append(entered_node) traverse(node, collect_nodes) return found_nodes
  95. def find_nodes(node, condition): found_nodes = [] def collect_nodes(entered_node): if not

    condition(entered_node): return found_nodes.append(entered_node) traverse(node, collect_nodes) return found_nodes ݕࡧ৚݅ʹ͋ͯ͸·Δந৅ߏจ໦ͷϊʔυΛूΊΔ഑ྻ
  96. def find_nodes(node, condition): found_nodes = [] def collect_nodes(entered_node): if not

    condition(entered_node): return found_nodes.append(entered_node) traverse(node, collect_nodes) return found_nodes ந৅ߏจ໦ͷϊʔυΛ๚ΕΔλΠϛϯάͰݕࡧ৚݅ͷ
 ؔ਺Λ࣮૷͠ɺ৚݅ʹ͋ͯ͸·ΔϊʔυΛूΊΔؔ਺
  97. def find_nodes(node, condition): found_nodes = [] def collect_nodes(entered_node): if not

    condition(entered_node): return found_nodes.append(entered_node) traverse(node, collect_nodes) return found_nodes ந৅ߏจ໦ͷϊʔυΛूΊΔؔ਺Λ
 PO@FOUFSʹࢦఆͯ͠૸ࠪΛ։࢝
  98. def find_nodes(node, condition): found_nodes = [] def collect_nodes(entered_node): if not

    condition(entered_node): return found_nodes.append(entered_node) traverse(node, collect_nodes) return found_nodes ݟ͔ͭͬͨந৅ߏจ໦ͷϊʔυͷ഑ྻΛฦ͢
  99. def find_nodes(node, condition): found_nodes = [] def collect_nodes(entered_node): if not

    condition(entered_node): return found_nodes.append(entered_node) traverse(node, collect_nodes) return found_nodes ૸ࠪؔ਺Λ࢖͏͜ͱͰɺͱͯ΋؆୯ʹ࣮૷Ͱ͖ͨ
  100. def report_namaiki_comments(text): node = parse_program(text) if node is None: print("Syntax

    Error!") return namaiki_comments = find_nodes(node, is_namaiki_comment) print("Found {count} comments!".format(count=len(namaiki_comments))) def is_namaiki_comment(entered_node): if not isinstance(entered_node, CommentStatementNode): return False comment_node = entered_node return "CATCH_ME_IF_YOU_CAN" in comment_node.comment_content ίʔυ͔ΒੜҙؾͳίϝϯτΛݟ͚ͭͯ਺Λใࠂ͢Δؔ਺
  101. def report_namaiki_comments(text): node = parse_program(text) if node is None: print("Syntax

    Error!") return namaiki_comments = find_nodes(node, is_namaiki_comment) print("Found {count} comments!".format(count=len(namaiki_comments))) def is_namaiki_comment(entered_node): if not isinstance(entered_node, CommentStatementNode): return False comment_node = entered_node return "CATCH_ME_IF_YOU_CAN" in comment_node.comment_content ίʔυΛߏจղੳͯ͠ந৅ߏจ໦Λ࡞੒͢Δ
  102. def report_namaiki_comments(text): node = parse_program(text) if node is None: print("Syntax

    Error!") return namaiki_comments = find_nodes(node, is_namaiki_comment) print("Found {count} comments!".format(count=len(namaiki_comments))) def is_namaiki_comment(entered_node): if not isinstance(entered_node, CommentStatementNode): return False comment_node = entered_node return "CATCH_ME_IF_YOU_CAN" in comment_node.comment_content ߏจղੳʹࣦഊͨ͠ΒɺߏจΤϥʔΛදࣔ
  103. def report_namaiki_comments(text): node = parse_program(text) if node is None: print("Syntax

    Error!") return namaiki_comments = find_nodes(node, is_namaiki_comment) print("Found {count} comments!".format(count=len(namaiki_comments))) def is_namaiki_comment(entered_node): if not isinstance(entered_node, CommentStatementNode): return False comment_node = entered_node return "CATCH_ME_IF_YOU_CAN" in comment_node.comment_content ૸ࠪؔ਺ͷར༻ྫͱ࣮ͯ͠૷ͨ͠ɺݕࡧؔ਺Λ࢖ͬͯੜҙؾͳίϝϯτΛूΊΔ
  104. def report_namaiki_comments(text): node = parse_program(text) if node is None: print("Syntax

    Error!") return namaiki_comments = find_nodes(node, is_namaiki_comment) print("Found {count} comments!".format(count=len(namaiki_comments))) def is_namaiki_comment(entered_node): if not isinstance(entered_node, CommentStatementNode): return False comment_node = entered_node return "CATCH_ME_IF_YOU_CAN" in comment_node.comment_content ݕࡧؔ਺ͷ৚݅ʹࢦఆͨ͠ɺੜҙؾίϝϯτ͔Ͳ͏͔൑ఆ͢Δؔ਺
  105. def report_namaiki_comments(text): node = parse_program(text) if node is None: print("Syntax

    Error!") return namaiki_comments = find_nodes(node, is_namaiki_comment) print("Found {count} comments!".format(count=len(namaiki_comments))) def is_namaiki_comment(entered_node): if not isinstance(entered_node, CommentStatementNode): return False comment_node = entered_node return "CATCH_ME_IF_YOU_CAN" in comment_node.comment_content ·ͣɺந৅ߏจ໦͕ίϝϯτจͰͳ͚Ε͹ɺ
 ੜҙؾͳίϝϯτͰ͸ͳ͍ͷͰɺ'BMTFΛฦͯ͠ऴྃ
  106. def report_namaiki_comments(text): node = parse_program(text) if node is None: print("Syntax

    Error!") return namaiki_comments = find_nodes(node, is_namaiki_comment) print("Found {count} comments!".format(count=len(namaiki_comments))) def is_namaiki_comment(entered_node): if not isinstance(entered_node, CommentStatementNode): return False comment_node = entered_node return "CATCH_ME_IF_YOU_CAN" in comment_node.comment_content ίϝϯτจͩͬͨ৔߹ɺίϝϯτͷ಺༰ʹੜҙؾͳ
 ΋ͷؚ͕·Ε͍ͯΕ͹5SVFɺͦΕҎ֎͸'BMTFΛฦ͢
  107. def report_namaiki_comments(text): node = parse_program(text) if node is None: print("Syntax

    Error!") return namaiki_comments = find_nodes(node, is_namaiki_comment) print("Found {count} comments!".format(count=len(namaiki_comments))) def is_namaiki_comment(entered_node): if not isinstance(entered_node, CommentStatementNode): return False comment_node = entered_node return "CATCH_ME_IF_YOU_CAN" in comment_node.comment_content ઌ΄Ͳͷؔ਺Ͱ5SVF͕ฦ͞Εͨந৅ߏจ໦ʢੜҙؾͳίϝϯτʣ͕
 Ұཡʹͯ͠ฦ͞ΕΔ
  108. def report_namaiki_comments(text): node = parse_program(text) if node is None: print("Syntax

    Error!") return namaiki_comments = find_nodes(node, is_namaiki_comment) print("Found {count} comments!".format(count=len(namaiki_comments))) def is_namaiki_comment(entered_node): if not isinstance(entered_node, CommentStatementNode): return False comment_node = entered_node return "CATCH_ME_IF_YOU_CAN" in comment_node.comment_content ੜҙؾͳίϝϯτͷ਺Λใࠂͯ͠ऴྃ
  109. def report_namaiki_comments(text): node = parse_program(text) if node is None: print("Syntax

    Error!") return namaiki_comments = find_nodes(node, is_namaiki_comment) print("Found {count} comments!".format(count=len(namaiki_comments))) def is_namaiki_comment(entered_node): if not isinstance(entered_node, CommentStatementNode): return False comment_node = entered_node return "CATCH_ME_IF_YOU_CAN" in comment_node.comment_content ߏจղੳثɾ૸ࠪؔ਺ɾݕࡧؔ਺Λ
 ૊Έ߹Θͤͯ࢖͏͜ͱͰɺͱͯ΋
 ؆୯ʹ੩తݕࠪΛ࣮૷Ͱ͖ͨ
  110. class NamaikiCommentPolicy(Policy): def get_violations(self, node): if not is_namaiki_comment(node): return []

    return ["FOUND YOU!"] def is_namaiki_comment(entered_node): if not isinstance(entered_node, CommentStatementNode): return False comment_node = entered_node return "CATCH_ME_IF_YOU_CAN" in comment_node.comment_content લʹ࣮૷ͨ͠ͷͱಉ͡ɺੜҙؾͳίϝϯτจΛࢦఠ͢Δݕࠪϧʔϧ
  111. class NamaikiCommentPolicy(Policy): def get_violations(self, node): if not is_namaiki_comment(node): return []

    return ["FOUND YOU!"] def is_namaiki_comment(entered_node): if not isinstance(entered_node, CommentStatementNode): return False comment_node = entered_node return "CATCH_ME_IF_YOU_CAN" in comment_node.comment_content Ҿ਺ͷந৅ߏจ໦ͷϊʔυ͕ੜҙؾͳίϝϯτจʼͳΒɺ
 ʮݟ͚ͭͨͧʂʯͱ͍͏ϝοηʔδΛฦ͢Α͏ʹ࣮૷
  112. class NamaikiCommentPolicy(Policy): def get_violations(self, node): if not is_namaiki_comment(node): return []

    return ["FOUND YOU!"] def is_namaiki_comment(entered_node): if not isinstance(entered_node, CommentStatementNode): return False comment_node = entered_node return "CATCH_ME_IF_YOU_CAN" in comment_node.comment_content ੜҙؾͳίϝϯτจΛ൑ఆ͢Δؔ਺͸લͷ΋ͷΛྲྀ༻
  113. class Policies(Policy): def __init__(self, policies): self.policies = policies def get_violations(self,

    node): return reduce( lambda violations, policy: violations + policy.get_violations(node), self.policies, [] ) ෳ਺ͷݕࠪϧʔϧΛଋͶΔݕࠪϧʔϧΛ४උ
  114. class Policies(Policy): def __init__(self, policies): self.policies = policies def get_violations(self,

    node): return reduce( lambda violations, policy: violations + policy.get_violations(node), self.policies, [] ) ݕࠪϧʔϧͱಉ͡ڞ௨ΠϯλʔϑΣʔεΛ࣮૷͢Δ
  115. class Policies(Policy): def __init__(self, policies): self.policies = policies def get_violations(self,

    node): return reduce( lambda violations, policy: violations + policy.get_violations(node), self.policies, [] ) ಺෦ͷෳ਺ͷݕࠪϧʔϧΛϧʔϓͯ͠ݕࠪͯ͠ɺ݁ՌΛ߹੒ͯ͠ฦ͢
  116. def lint(text): node = parse_program(text) if node is None: return

    ["Syntax Error!"] policies = Policies([NamaikiCommentPolicy()]) violations_buffer = [] def collect_violations(entered_node): violations = policies.get_violations(entered_node) violations_buffer.append(violations) traverse(node, collect_violations) all_violations = [] return reduce(add, violations_buffer, all_violations)
  117. def lint(text): node = parse_program(text) if node is None: return

    ["Syntax Error!"] policies = Policies([NamaikiCommentPolicy()]) violations_buffer = [] def collect_violations(entered_node): violations = policies.get_violations(entered_node) violations_buffer.append(violations) traverse(node, collect_violations) all_violations = [] return reduce(add, violations_buffer, all_violations) ෳ਺ͷݕࠪϧʔϧͰ·ͱΊͯݕࠪ͢Δؔ਺
  118. def lint(text): node = parse_program(text) if node is None: return

    ["Syntax Error!"] policies = Policies([NamaikiCommentPolicy()]) violations_buffer = [] def collect_violations(entered_node): violations = policies.get_violations(entered_node) violations_buffer.append(violations) traverse(node, collect_violations) all_violations = [] return reduce(add, violations_buffer, all_violations) ༩͑ΒΕͯจࣈྻΛߏจղੳͯ͠ந৅ߏจ໦Λ࡞੒
  119. def lint(text): node = parse_program(text) if node is None: return

    ["Syntax Error!"] policies = Policies([NamaikiCommentPolicy()]) violations_buffer = [] def collect_violations(entered_node): violations = policies.get_violations(entered_node) violations_buffer.append(violations) traverse(node, collect_violations) all_violations = [] return reduce(add, violations_buffer, all_violations) ߏจղੳʹࣦഊͨ͠ΒɺߏจղੳࣦഊͷϝοηʔδΛ݁Ռͱͯ͠ฦ͢
  120. def lint(text): node = parse_program(text) if node is None: return

    ["Syntax Error!"] policies = Policies([NamaikiCommentPolicy()]) violations_buffer = [] def collect_violations(entered_node): violations = policies.get_violations(entered_node) violations_buffer.append(violations) traverse(node, collect_violations) all_violations = [] return reduce(add, violations_buffer, all_violations) ෳ਺ͷݕࠪϧʔϧΛଋͶͯɺͭͷݕࠪϧʔϧʹ·ͱΊΔ
 ʢ͜ͷྫͰ͸ɺੜҙؾͳίϝϯτΛݕࠪ͢Δϧʔϧ͚ͩʣ
  121. def lint(text): node = parse_program(text) if node is None: return

    ["Syntax Error!"] policies = Policies([NamaikiCommentPolicy()]) violations_buffer = [] def collect_violations(entered_node): violations = policies.get_violations(entered_node) violations_buffer.append(violations) traverse(node, collect_violations) all_violations = [] return reduce(add, violations_buffer, all_violations) ݕࠪϝοηʔδΛूΊΔ഑ྻΛ४උ
  122. def lint(text): node = parse_program(text) if node is None: return

    ["Syntax Error!"] policies = Policies([NamaikiCommentPolicy()]) violations_buffer = [] def collect_violations(entered_node): violations = policies.get_violations(entered_node) violations_buffer.append(violations) traverse(node, collect_violations) all_violations = [] return reduce(add, violations_buffer, all_violations) ༩͑ΒΕͨந৅ߏจ໦ͷϊʔυ͔ΒɺݕࠪϝοηʔδΛूΊΔؔ਺
  123. def lint(text): node = parse_program(text) if node is None: return

    ["Syntax Error!"] policies = Policies([NamaikiCommentPolicy()]) violations_buffer = [] def collect_violations(entered_node): violations = policies.get_violations(entered_node) violations_buffer.append(violations) traverse(node, collect_violations) all_violations = [] return reduce(add, violations_buffer, all_violations) ந৅ߏจ໦Λ૸ࠪͯ͠ɺݕࠪϝοηʔδΛूΊ͍ͯ͘
  124. def lint(text): node = parse_program(text) if node is None: return

    ["Syntax Error!"] policies = Policies([NamaikiCommentPolicy()]) violations_buffer = [] def collect_violations(entered_node): violations = policies.get_violations(entered_node) violations_buffer.append(violations) traverse(node, collect_violations) all_violations = [] return reduce(add, violations_buffer, all_violations) ू·ͬͨݕࠪϝοηʔδΛͭͷ഑ྻʹ·ͱΊͯฦ͢
  125. 3VCZ w ߏจղੳث w 1BSTFS3VCZ
 (JU)VCͷXIJUFRVBSLQBSTFSͷMJCQBSTFSSVCZZ  w ૸ࠪؔ਺ w

    3VCPDPQ"455SBWFSTBM
 3VCPDPQͷMJCSVCPDPQBTUUSBWFSTBMSC  w ੩తݕࠪϧʔϧ w 3VCPDPQ$PQ-JOU&NQUZ&OTVSF
 3VCPDPQͷMJCSVCPDPQDPQMJOUFNQUZ@FOTVSFSC
  126. +BWB4DSJQU w ߏจղੳث w 1BSTFSʢ(JU)VCͷBDPSOKTBDPSOͷTSDTUBUFKT  w ૸ࠪؔ਺ w 5SBWFSTFUSBWFSTF


    &4-JOUͷMJCVUJMUSBWFSTFKT  w ੩తݕࠪϧʔϧ w SVMFTOPFNQUZGVODUJPO
 &4-JOUͷMJCSVMFTOPFNQUZGVODUJPOKT
  127. $44ͷந৅ߏจ໦Λൺֱ͢Δπʔϧ https://github.com/mixi-inc/css-semdiff $ css-astdiff a.css b.css --verbose extra: .extra-1 {

    border: none; } missing: .missing-1 { border: none; } --------------------------------- 1 extra rules and 1 missing rules