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

151a0b14f5914e786e2e104cfb3a9b2f?s=47 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

151a0b14f5914e786e2e104cfb3a9b2f?s=128

Kuniwak

September 07, 2018
Tweet

Transcript

  1. ੩తݕࠪͷ͍Ζ͸ ࣄલ஌ࣝͳ͠Ͱཧղ͢Δ Kuniwak - DeNA Co.,Ltd. 2018.09.07 builderscon tokyo 2018

  2. "CPVUNF

  3. ,VOJXBL w ॴଐˠ w HJUIVCDPN,VOJXBL w RJJUBDPN,VOJXBL w ίϯϐϡʔλαΠΤϯεΛ
 ֶΜͰ͖͍ͯ·ͤΜ

    w ࣄલ஌ࣝͳ͠Ͱ੩తղੳ
 πʔϧΛ։ൃ͖ͯͨ͠
  4. ͜Ε·Ͱʹ͖ͭͬͯͨ͘
 ੩తݕࠪܥͷπʔϧͷ঺հ

  5. 7JNTDSJQUͷ੩తݕࠪπʔϧ https://github.com/Kuniwak/Vint

  6. $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
  7. मਖ਼ͷӨڹൣғΛڭ͑ͯ͘ΕΔπʔϧ https://twitter.com/orga_chem/status/674847225684475905 ˞લ৬

  8. ͜ͷൃදΛฉ͘ͱ
 Θ͔Δ͜ͱ ͸͡Ίʹ

  9. ੩తղੳΛߏ੒͢Δ͍͔ͭ͘ͷߟ͑ํ ͕Θ͔ΔΑ͏ʹͳΔ ߟ͑ํ͑͞ཧղͰ͖Ε͹ɺ੩తղੳΛ
 ࣮૷͢Δͷ͸೉͘͠ͳ͍͜ͱ͕Θ͔Δ

  10. ΋͏ͪΐͬͱ۩ମతʹ ͜ͷൃදͷ໨ඪ

  11. if isEmpty('') { //CATCH_ME_IF_YOU_CAN } CATCH_ME_IF_YOU_CANͱॻ͔Εͨੜҙؾͳ
 ίϝϯτʢจࣈྻ಺ͷ͸/(ʣΛݕ஌͢Δ
 ੩తݕࠪΛ៉ྷʹ࣮૷Ͱ͖ΔΑ͏ʹͳΔ

  12. ؆୯͗͢ͳ͍ʁͱ
 ࢥͬͨ͋ͳͨ΁

  13. ಉ͡ϓϩάϥϜͰ࣍ͷίʔυ΋
 ղੳͰ͖·͔͢ʁ fake('', ' //CATCH_ME_IF_YOU_CAN ↩︎ ') ͜Ε͸จࣈྻͳͷͰݕ஌ͯ͠͸͍͚ͳ͍ ͜ͷվߦ͸ҙਤతͰ͢

  14. ͜ͷൃදͰ͸ɺҙ஍ѱͳέʔεʹ΋
 ରԠͰ͖Δ੩తղੳΛઆ໌͠·͢

  15. ੩తղੳͱ͸ Ͱ͸ͬͦ͘͞

  16. ੩తݕࠪͱ͸ɺݕ͍ࠪͨ͠ϓϩάϥϜΛ
 ࣮ߦ͠ͳ͍··֤छͷҟৗΛݕࠪ͢Δख๏ͷ͜ͱ ݴޠ͝ͱʹ༷ʑͳ੩తݕࠪͷͨΊͷπʔϧ͕͋Δɿ w 1ZUIPOͳΒ1ZqBLFT΍NZQZͳͲ w 3VCZͳΒ3VCPDPQͳͲ w +BWB4DSJQUͳΒ&4-JOUͳͲ w

    +BWBͳΒ4QPU#VHTͳͲ
  17. ͜͜Ͱٙ໰ ͳͥ੩తʹݕ͍ࠪͨ͠ͷ͔

  18. ݕ͍ࠪͨ͠ϓϩάϥϜΛ࣮ߦͯ͠ʢಈతʣ
 ݕࠪ͢Δ৔߹ɺݕࠪதʹ௨ա͍ͯ͠ͳ͍
 ίʔυ͸ݕࠪͰ͖ͳ͍ isFoo = true if isFoo { }

    else { } ͪ͜Β͸ಈతʹݕࠪͰ͖Δ ͪ͜Β͸ಈతʹݕࠪͰ͖ͳ͍
  19. ੩తݕࠪ͸ಈతݕ͕ࠪۤखͳ
 ໢ཏతͳݕ͕ࠪಘҙ ͔͠͠੩తݕࠪ͸ಈతݕࠪ΄Ͳ
 ݕࠪ݁Ռ͕ਖ਼֬ʹͳΒͳ͍

  20. ੩తݕࠪΛ஌Δ͜ͱͰ
 ։ൃΛޮ཰ԽͰ͖Δཧ༝ ͳΜͷ໾ʹཱͭͷ͔ٙ໰ͳ͋ͳͨ΁

  21.   ίʔυϨϏϡʔͷҰ෦Λ
 ࣗಈԽͰ͖ΔΑ͏ʹͳΔ ศརͳࣾ಺πʔϧΛͭͬͯ͘
 ͋Γ͕͕ͨΒΕΔ طଘͷ੩తղੳπʔϧΛվྑɾ
 σόοάͰ͖ΔΑ͏ʹͳΔ 

  22. ίʔυϨϏϡʔࣗಈԽͷྫ 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΁
 Ҡߦͨ͠ࡍͷπʔϧ
  23. ศརͳࣾ಺πʔϧʢʁʣͷྫ https://twitter.com/orga_chem/status/674847225684475905 ऍ໌ WJTVBMJ[BUJPO͸ఘΊ·͕ͨ͠ɺ
 ґଘΛҰཡͰݟΒΕΔαʔϏε
 ͭͬͨ͘Βศརʹ࢖ΘΕ·ͨ͠

  24. શମతͳઆ໌ͷྲྀΕ ͜Ε͔Βͷ

  25. ֓೦Λཧղ͢Δ  ந৅ߏจ໦Λཧղ͢Δ  ந৅ߏจ໦ͷ࡞ΓํΛཧղ͢Δ  ந৅ߏจ໦ͷѻ͍ํΛཧղ͢Δ ࡞ΓํΛཧղ͢Δ  ؆୯ͳ੩తղੳΛ࣮૷͢Δ

     ੩తղੳͷσβΠϯύλʔϯΛ஌Δ  ࣮ࡍͷ੩తղੳΛಡΉ
  26. ந৅ߏจ໦Λཧղ͢Δ ·ͣ͸

  27. ੩తݕࠪͰऔΓѻ͏୅දతͳ
 σʔλͷछྨ͸͍͔ͭ͋͘Δ

  28. ந৅ߏจ໦ ίʔυΛ໦ߏ଄Ͱදݱͨ͠΋ͷ ଟ͘ͷ੩తղੳͷجຊ τʔΫϯྻ ίʔυͷখ͞ͳߏ੒୯ҐΛ
 ग़ݱॱʹฒ΂ͨ΋ͷ ελΠϧʹؔ͢Δ੩తղੳͰ
 Α͘࢖ΘΕΔ ੍ޚϑϩʔάϥϑ ίʔυͷ৚݅෼ذͳͲͷ


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

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

    *EFOUJpFS $PNNFOU ந৅ߏจ໦ ίʔυ
  31. if isEmpty('') { // COMMENT } ந৅ߏจ໦ ίʔυ ˞4UBUFNFOU͸ʮจʯͷҙຯͳͷͰɺ*G4UBUFNFOU͸ʮJGจʯͷҙຯͰ͢ 1SPHSBN

    4USJOH-JUFSBM 'VOD$BMM *EFOUJpFS $PNNFOU *G4UBUFNFOU
  32. if isEmpty('') { // COMMENT } 1SPHSBN *G4UBUFNFOU 4USJOH-JUFSBM *EFOUJpFS

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

    $PNNFOU *EFOUJpFS ந৅ߏจ໦ ίʔυ
  34. if isEmpty('') { // COMMENT } 1SPHSBN *G4UBUFNFOU 'VOD$BMM *EFOUJpFS

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

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

    *EFOUJpFS $PNNFOU 1SPHSBN ίʔυ
  37. if isEmpty('') { // COMMENT } 1SPHSBN *G4UBUFNFOU 4USJOH-JUFSBM 'VOD$BMM

    *EFOUJpFS $PNNFOU ந৅ߏจ໦ ίʔυ ந৅ߏจ໦͸ɺίʔυͷߏ଄Λ
 ໦ߏ଄ͷσʔλͱͯ͠දݱͨ͠΋ͷ
  38. ໦ߏ଄Ͱ͸1SPHSBN΍
 $PNNFOUͷΑ͏ͳ
 ໦Λߏ੒͢Δઅ఺ͷ͜ͱΛ
 ʮϊʔυʯͱݺͿ 1SPHSBN *G4UBUFNFOU 4USJOH-JUFSBM 'VOD$BMM *EFOUJpFS $PNNFOU

  39. ࣮ࡍͷϝδϟʔͳݴޠͷ
 ந৅ߏจ໦ΛݟͯΈΑ͏ ந৅ߏจ໦͸ͳΜͱͳ͘Θ͔ͬͨͷͰ

  40. ϝδϟʔͳݴޠͷந৅ߏจ໦͸
 ҙ֎ͱ؆୯ʹΈΒΕΔ ݴޠʹΑͬͯ͸ඪ४ϥΠϒϥϦ͚ͩͰ
 ந৅ߏจ໦ΛΈΒΕͨΓ͢Δ

  41. 1ZUIPOͷந৅ߏจ໦ͷݟํ import ast source = "if is_empty(''):\n" \ " pass\n"

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

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

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

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

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

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

    id='is_empty', ctx=Load() ), args=[ Str(s='') ], keywords=[] ), body=[ Pass() ], orelse=[] ) ] )
  48. 3VCZͷந৅ߏจ໦ͷݟํ require 'ripper' require 'pp' example = <<~END if is_empty('')

    end END pp Ripper.sexp(example)
  49. ந৅ߏจ໦ 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
  50. [: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
  51. [: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
  52. [: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
  53. [: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
  54. +BWB4DSJQUͷந৅ߏจ໦ͷݟํ import {default as Esprima} from "esprima"; const program =

    "if (isEmpty('')) {\n" + "}\n"; const node = Esprima.parseScript(program); console.log("%j", node);
  55. ந৅ߏจ໦ +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('')) { }
  56. ந৅ߏจ໦ +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('')) { }
  57. ந৅ߏจ໦ +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" }
  58. ந৅ߏจ໦ +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" }
  59. ந৅ߏจ໦ +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('')) { }
  60. ந৅ߏจ໦ͷ·ͱΊ w ந৅ߏจ໦͸ϓϩάϥϜΛ໦ߏ଄Ͱදݱͨ͠΋ͷ w ଟ͘ͷ੩తղੳʹந৅ߏจ໦͕ొ৔͢Δ w ϝδϟʔͳݴޠͰ͋Ε͹؆୯ʹΈΒΕΔ

  61. ந৅ߏจ໦Λͭ͘Δ
 ߏจղੳΛ஌Ζ͏ ந৅ߏจ໦Λ׬શʹཧղͨ͠ͷͰ

  62. ϓϩάϥϜͷςΩετ͔Βந৅ߏจ໦Λ
 ࡞੒͢ΔॲཧΛߏจղੳͱݺͿ ߏจղੳΛ஌Δͱɺطଘͷߏจղੳ
 ϥΠϒϥϦΛ࢖͏ͱ͖ͷ஫ҙ఺͕Θ͔Δ ͨͱ͑͹ɺੑೳྼԽ͠΍͍͢෦෼ͳͲ͕Θ͔Δ

  63. ߏจղੳͷ୅දత࣮૷ख๏ w ࠶ؼԼ߱๏ w όοΫτϥοΫ͋Γ w ૉ௚ͳ࣮૷ w 1BSTFS$PNCJOBUPS w

    1BSTFS&YQSFTTJPO(SBNNBS w όοΫτϥοΫͳ͠ w ʜ w ԋࢉࢠॱҐ๏ w ʜ ࠓճղઆ͢Δ࣮૷
  64. ͪΌΜͱͨ͠ఆٛ͸೉͘͠ฉ͑͜Δ͕ɺ
 ࣮ࡍͷίʔυΛΈΕ͹͙͢ʹཧղͰ͖Δ श͏ΑΓ׳ΕΑ͏

  65. จ๏نଇͷ঺հ ࠓ͔ΒߏจղੳΛ࣮૷͢Δ

  66. <ϓϩάϥϜ> := <จ>* EOF <จ> := <ifจ> | <ࣜจ> |

    <ίϝϯτจ> <ifจ> := "if " <ࣜ> " {\n" <จ>* "}" "\n" <ࣜจ> := <ࣜ> "\n" <ίϝϯτจ> := "//" [A-Z_ ]* "\n" <ࣜ> := <จࣈྻϦςϥϧ> | <ؔ਺ݺͼग़ࣜ͠> <จࣈྻϦςϥϧ> := "'" ("\'" | [A-Z_ ])* "'" <ؔ਺ݺͼग़ࣜ͠> := <ؔ਺໊> "(" <ࣜ> ")" <ؔ਺໊> := [a-z] [a-z]* ͜ͷ··ͩͱΘ͔ΓͮΒ͍ͷͰɺ۩ମྫΛग़͠·͢
  67. ίϝϯτ͚ͩϓϩάϥϜ // COMMENT ↩︎ EOF &0'͸ϑΝΠϧͷऴྃʢ&OEPG'JMFʣΛҙຯ͢Δه߸

  68. <ϓϩάϥϜ> := <จ>* EOF <จ> := <ifจ> | <ࣜจ> |

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

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

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

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

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

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

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

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

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

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

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

    <ίϝϯτจ> <ίϝϯτจ> := "//" [A-Z_ ]* "\n" // COMMENT EOF ࠷ޙ͸&0'ͰऴΘΔ ίϝϯτ͚ͩϓϩάϥϜ ద༻͞Εͨจ๏نଇ
  80. ͜͜·ͰಡΊΕ͹ɺଞͷίʔυͷ
 จ๏نଇ΋ಡΊΔΑ͏ʹͳΔ

  81. print("TEXT") EOF ؔ਺ݺͼग़͚ͩ͠ϓϩάϥϜ ద༻͞Εͨจ๏نଇ <ϓϩάϥϜ> := <จ>* EOF <จ> :=

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

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

    <ifจ> | <ࣜจ> | <ίϝϯτจ> <ࣜจ> := <ࣜ> "\n" <ࣜ> := <จࣈྻϦςϥϧ> | <ؔ਺ݺͼग़ࣜ͠> <ؔ਺ݺͼग़͠> := <ؔ਺໊> "(" <ࣜ> ")" <ؔ਺໊> := [a-z] [a-z]* <จࣈྻϦςϥϧ>
  84. EOF ۭͷϓϩάϥϜ ద༻͞Εͨจ๏نଇ <ϓϩάϥϜ> := <จ>* EOF ࣮͸͜Ε΋จ๏نଇ্͸ଥ౰ͳϓϩάϥϜ

  85. EOF ۭͷϓϩάϥϜ ద༻͞Εͨจ๏نଇ <ϓϩάϥϜ> := <จ>* EOF ܁Γฦ͠ճ

  86. ෆਖ਼ͳϓϩάϥϜ΋ΈͯΈΑ͏

  87. / INVALID EOF ෆਖ਼ͳϓϩάϥϜ ద༻͞Εͨจ๏نଇ ద༻Ͱ͖Δจ๏نଇͳ͠

  88. / INVALID EOF ෆਖ਼ͳϓϩάϥϜ ద༻͞Εͨจ๏نଇ ͳ͠ ͜ͷίʔυ͸ϓϩάϥϜͷจͷ͍ͣΕʹ΋
 ͋ͯ͸·Βͳ͍ͷͰɺϓϩάϥϜʹ͋ͯ͸·Βͳ͍ ͜ͷঢ়گΛʮߏจΤϥʔʢTZOUBYFSSPSʣʯͱ͍͏

  89. จ๏نଇ·ͱΊ w จ๏نଇ͸۩ମྫΛߟ͑ΔͱΘ͔Γ΍͍͢ w จ๏نଇʹ͋ͯ͸·Βͳ͍ͱ͖ߏจΤϥʔʹͳΔ

  90. ߏจղੳͷ࣮૷ ͖ͬ͞ͷจ๏نଇʹରԠ͢Δ

  91.  จ๏نଇͷࠨଆʢͷ෦෼ʣʹ
 ରԠ͢Δந৅ߏจ໦ͷϊʔυΛఆٛ͢Δ  ఆٛͨ͠ϊʔυ͝ͱʹղੳؔ਺Λ༻ҙ͢Δ ߏจղੳͷ࣮૷खॱ

  92. <ϓϩάϥϜ> := <จ>* EOF <จ> := <ifจ> | <ࣜจ> |

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

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

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

    <ϓϩάϥϜ>͸ෳ਺ͷ<จ>͔ΒͳΔͷͰɺ # ؚ·ΕΔ<จ>Λ഑ྻͰอ࣋͢Δɻ self.statements = statement_nodes 1SPHSBN 4UBUFNFOU 4UBUFNFOU
  96. # <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จʹରԠ͢Δந৅ߏจ໦ͷϊʔυ
  97. ಉ༷ʹ͢΂ͯͷʹରԠ͢Δ
 ந৅ߏจ໦ͷϊʔυΛఆٛ͠·͢

  98. ͢΂ͯͷϊʔυΛఆٛ͠ऴΘͬͨΒɺ
 ͜ΕΛ࡞੒͢Δؔ਺Λॻ͍͍ͯ͘

  99. ʹରԠ͢Δؔ਺ͷҾ਺ͱ໭Γ஋ʹ͸ɺ
 Α͋͘Δఆ൪ͷύλʔϯ͕͋Δ ͜ͷύλʔϯʹै͏ͱɺؔ਺Λ
 ૊Έ߹Θͤ΍͘͢ͳΔͷͰ͓֮͑ͯ͜͏

  100. ͍͖ͳΓϓϩάϥϜʹରԠ͢Δؔ਺Λ
 ॻ͘ͱઆ໌͕೉͍͠ͷͰɺίϝϯτจʹ
 ରԠ͢Δؔ਺͔Βઆ໌͢Δ

  101. # <ίϝϯτจ> := "//" [A-Z_ ]* "\n" def parse_comment_statement(text, index):

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

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

    # TODO: ͪΌΜͱ࣮૷͢Δ next_index = 1234 # TODO: Ծͷ࣮૷ node = CommentStatementNode( comment_content=" COMMENT" # TODO: Ծͷ࣮૷ ) return node, next_index ղੳͰ͖ͨίϝϯτจʹ
 ରԠ͢Δந৅ߏจ໦ͷϊʔυ ίϝϯτจ͕ऴΘͬͨҐஔ
  105. ͱΓ͋͑ͣؔ਺ͷ࢖ΘΕํΛΈͯΈΑ͏

  106. (comment_node, next_index) = parse_comment_statement("// COMMENT\n", 0) ϓϩάϥϜͷઌ಄͔Βղੳ͢Δύλʔϯ ઌ಄ͳͷͰJOEFY͸ ղੳ͕ऴΘͬͨҐஔ ίϝϯτจʹରԠ͢Δந৅ߏจ໦ͷϊʔυ

  107. (comment_node, next_index) = parse_comment_statement("'123'\n// COMMENT\n", 6) ϓϩάϥϜͷ్த͔Βղੳ͢Δύλʔϯ ղੳ͕ऴΘͬͨҐஔ ్தͷจࣈ໨͔Βղੳ͢Δ ίϝϯτจʹରԠ͢Δந৅ߏจ໦ͷϊʔυ

  108. (comment_node, next_index) = parse_comment_statement("'123'\n/ INVALID", 6) ղੳʹࣦഊ͢Δύλʔϯ ࣦഊͨ͠ΒҐஔ͸ಈ͔͞ͳ͍Ͱͷ··ʹ͢Δ ͜ͷΑ͏ʹɺࣦഊͨ͠ΒԿ΋ͳ͔ͬͨΑ͏ʹ
 ר͖໭Δ͜ͱΛʮόοΫτϥοΫʯͱݺͿ

    ղੳʹࣦഊͨ͠ͷͰ/POF ్தͷจࣈ໨͔Βղੳ͢Δ
  109. Ҿ਺ w ղੳ͢Δจࣈྻશମͱ࢝ΊΔҐஔ ໭Γ஋ w ղੳͰ͖ͨந৅ߏจ໦ͷϊʔυͱऴΘͬͨҐஔ w ࣦഊͨ͠Β/POFΛฦͯ͠ɺ
 Ґஔ͸ಈ͔͞ͳ͍ ͜͜ͰҾ਺ͱ໭Γ஋Λ੔ཧ͠Α͏ɿ

  110. Ҿ਺ w ղੳ͢Δจࣈྻશମͱ࢝ΊΔҐஔ ໭Γ஋ w ղੳͰ͖ͨந৅ߏจ໦ͷϊʔυͱऴΘͬͨҐஔ w ࣦഊͨ͠Β/POFΛฦͯ͠ɺ
 Ґஔ͸ಈ͔͞ͳ͍ ͜͜ͰҾ਺ͱ໭Γ஋Λ੔ཧ͠Α͏ɿ

    ؔ਺͕૊Έ߹Θͤ΍͘͢ͳΔͱ
 ݴ͍ͬͯͨͷ͸͜ͷ෦෼ લͷؔ਺ʹΑΔղੳ͕ऴΘͬͨ
 Ґஔ͔Βɺ࣍ͷؔ਺ʹΑΔղੳΛ
 ࠶։͢ΔΠϝʔδ
  111. ࣮ࡍͷॲཧͷྲྀΕΛΈͯΈΑ͏ Ҿ਺ͱ໭Γ஋͕ͳΜͱͳ͘Θ͔ͬͨͷͰ

  112. <ίϝϯτจ> := "//" [A-Z_ ]* "\n" ίϝϯτจͷจ๏نଇͷಡΈํͷ͓͞Β͍ɿ  ͸͡Ίʹ͕͋Δ 

    ଓ͚ͯӳେจࣈ͔@͔ۭന͕ճҎ্ଓ͘  ࠷ޙʹվߦ͕͋Δ
  113. 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"
  114. 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" ίϝϯτจʹରԠ͢Δؔ਺
  115. 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"
  116. 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"
  117. 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"
  118. 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"
  119. 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"
  120. 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"
  121. 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"
  122. 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"
  123. 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"
  124. 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"
  125. 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"
  126. <ίϝϯτจ> := "//" [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 ίϝϯτจʹରԠ͢Δؔ਺Λॻ͚ͨ
  127. FYQFDUdͳؔ਺ͷ͸ͨΒ͖ ్தͰͰ͖ͯͨFYQFDUd͕ؾʹͳΔํ΁

  128. ઌ΄ͲͷQBSTF@DPNNFOU@TUBUFNFOUͷ࣮૷࣌ʹ
 ͋Γ͕ͪͳϛε͕ɺJOEFYͷਐΊ๨Ε΍ਐΊ෯ͷؒҧ͍ FYQFDU@LFZXPSE΍FYQFDU@FJUIFS͸͜ΕΒͷؒҧ͍Λ
 ༧๷͢ΔͨΊͷϢʔςΟϦςΟؔ਺

  129. 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
  130. ൺֱͨ͠จࣈྻͷ௕͚ͩ͞JOEFYΛਐΊΔͷͰɺ
 ਐΊ෯ͷؒҧ͍ͱਐΊ๨ΕΛ๷͛Δ ศརʜ

  131. 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
  132. 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 ৄࡉͳ࣮૷͸͋ͱͰղಡͯ͠Έ͍ͯͩ͘͞ʂ
  133. ͜͜·ͰͰίϝϯτจͷ
 ղੳؔ਺Λ࣮૷Ͱ͖ͨ

  134. ࣍ʹɺจʹର͢Δؔ਺͔Βઌ΄Ͳ
 ࣮૷ͨ͠ίϝϯτจʹରԠ͢Δؔ਺Λ
 ࢖͏ํ๏ΛΈͯΈΑ͏ ϙΠϯτ͸ɺจ๏نଇͷͷӈଆʹ͕
 ͋ͬͨΒରԠ͢Δղੳؔ਺ʹ೚ͤΔ͜ͱ

  135. <จ> := <ifจ> | <ࣜจ> | <ίϝϯτจ> จͷจ๏نଇͷಡΈํͷ͓͞Β͍ɿ จ͸ɺJGจɺࣜจɺίϝϯτจͷ͍ͣΕ͔

  136. 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จ> | <ࣜจ> | <ίϝϯτจ>
  137. 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จ> | <ࣜจ> | <ίϝϯτจ>
  138. 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จ> | <ࣜจ> | <ίϝϯτจ>
  139. 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จ> | <ࣜจ> | <ίϝϯτจ>
  140. 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จ> | <ࣜจ> | <ίϝϯτจ>
  141. 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จ> | <ࣜจ> | <ίϝϯτจ>
  142. 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จ> | <ࣜจ> | <ίϝϯτจ>
  143. 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จ> | <ࣜจ> | <ίϝϯτจ>
  144. 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จ> | <ࣜจ> | <ίϝϯτจ> ͜ͷΑ͏ʹʹରԠ͢Δؔ਺Λఆ͍ٛͯ͘͠ͱɺ
 ࠷ऴతʹϓϩάϥϜʹରԠ͢Δؔ਺ΛఆٛͰ͖Δ ͜ͷϓϩάϥϜʹରԠ͢Δؔ਺͕ɺҰൠతʹ
 ఏڙ͞Ε͍ͯΔߏจղੳثͱݺ͹ΕΔ΋ͷ
  145. ࠓճͷઆ໌Ͱ৮Εͳ͔ͬͨ͜ͱ w ࣮༻తͳߏจղੳثʹ͢Δʹ͸ɺந৅ߏจ໦΍
 ߏจΤϥʔʹίʔυ্ͷҐஔ৘ใΛؚΊͨํ͕Α͍ w όοΫτϥοΫ͋Γͷ࣮૷͸ɺ୯७͔͔ͭͳΓ޿͍ൣғͷ
 จ๏نଇΛղੳͰ͖Δ͕ɺܭࢉྔ͸େ͖͍ w ࣮͸Ұ൪೉͍͠ͷ͸όάͷͳ͍จ๏نଇΛߟ͑Δ͜ͱ w

    όά͕͋Δͱߏจղੳث͕ແݶϧʔϓͨ͠Γ͢Δ
  146. ૉ௚ͳ࣮૷ͷ·ͱΊ w ࣮૷͸ҎԼͷεςοϓͰͰ͖Δɿ w ʹରԠ͢Δந৅ߏจ໦Λఆٛ͢Δ w ʹରԠ͢Δؔ਺Λఆٛ͢Δ w จ๏نଇͷӈଆʹ͕͋Δ৔߹͸ɺ
 ͦͷʹରԠ͢Δؔ਺ʹղੳΛ೚ͤΔ

  147. 1BSTFS$PNCJOBUPSͷҖྗ ΋ͬͱΤϨΨϯτͳํ๏Λνϥݟͤ

  148. ࠓճ঺հͨ͠ૉ௚ͳ࣮૷Λ1BSTFS$PNCJOBUPSͰ࣮૷͢Δͱɺ
 ಉ͡ॲཧͳͷʹڻ͘΄Ͳ࣮૷͕୹͘ͳΓɺ͔ͭ࠶ར༻ੑ্͕͕Δɿ parse_comment = sequence( string("//"), sequence( many(char(uppercase + ["_",

    " "])), string("\n") ) ) ؾʹͳΔਓ͸ɺεϥΠυ຤ඌͷࢀߟจݙ#Λࢀর
  149. ߏจղੳͷ·ͱΊ w ߏจղੳ͸ίʔυΛղੳͯ͠ந৅ߏจ໦Λ
 ࡞੒͢Δॲཧ w ͞·͟·ͳߏจղੳͷΞϧΰϦζϜ͕͋Δ w ࠓճ͸όοΫτϥοΫ͋Γͷ
 ࠶ؼԼ߱෼ੳΛૉ௚ʹ࣮૷ͨ͠

  150. ந৅ߏจ໦Λॲཧ͢Δ
 ୅දతͳ࣮૷ύλʔϯ ߏจղੳͷ͓࣍͸

  151. ͜Ε·Ͱ͸ɺந৅ߏจ໦ͷ֓ཁ΍࡞੒ํ๏ΛΈ͖ͯͨ ࣍ʹɺ͜͜Ͱ͸ந৅ߏจ໦Λ͔͋ͭ͏୅දతͳ
 ࣮૷ύλʔϯΛ঺հ͢Δ ΋ͬͱ΋සൟʹ࢖͏࣮૷ύλʔϯͱͯ͠ɺந৅ߏจ໦Λ
 ॱ൪ʹ୳ࡧ͍ͯ͘͠૸ࠪؔ਺Λ঺հ͢Δ

  152. ૸ࠪؔ਺ ந৅ߏจ໦Λ͔͋ͭ͏୅දతύλʔϯ

  153. ૸ࠪؔ਺͸ɺந৅ߏจ໦ͷϊʔυΛॱʹḷ͍ͬͯͬͯɺ
 ϊʔυʹ๚ΕΔ཭ΕΔͨͼʹॲཧΛ࣮ߦ͢Δؔ਺ traverse(node, on_enter, on_leave) PO@FOUFSͱPO@MFBWF͸ͲͪΒ΋ؔ਺Λ౉͢ ϊʔυʹ๚ΕΔ཭ΕΔͱ࣮ߦ͞ΕΔ Կ͕ى͜Δ͔Θ͔ΓͮΒ͍ͷͰਤղ͠·͢

  154. def print_enter(node): print("enter: {node}".format(node=type(node))) def print_leave(node): print("leave: {node}".format(node=type(node))) traverse(node, print_enter,

    print_leave) PO@FOUFSͱPO@MFBWF͕Θ͔ΔΑ͏ʹQSJOU͢Δؔ਺Λ༻ҙ
  155. ͜ͷUSBWFSTFʹΑͬͯQSJOU͞ΕΔ݁ՌΛΈͯΈ·͠ΐ͏ QSJOU݁Ռ 1SPHSBN *G4UBUFNFOU 4USJOH-JUFSBM 'VOD$BMM *EFOUJpFS $PNNFOU

  156. *G4UBUFNFOU 4USJOH-JUFSBM 'VOD$BMM *EFOUJpFS $PNNFOU 1SPHSBN ·ͣ͸1SPHSBNʹ๚ΕΔʢFOUFS1SPHSBNΛQSJOUʣ FOUFS1SPHSBN QSJOU݁Ռ

  157. 1SPHSBN 4USJOH-JUFSBM 'VOD$BMM *EFOUJpFS $PNNFOU *G4UBUFNFOU ࣍ʹ1SPHSBNͷࢠͰ͋Δ*G4UBUFNFOU΁๚ΕΔ FOUFS1SPHSBN FOUFS*G4UBUFNFOU QSJOU݁Ռ

  158. 1SPHSBN *G4UBUFNFOU 4USJOH-JUFSBM *EFOUJpFS $PNNFOU 'VOD$BMM ࣍ʹ*G4UBUFNFOUͷࢠͰ͋Δ'VOD$BMMΛ๚ΕΔ FOUFS1SPHSBN FOUFS*G4UBUFNFOU FOUFS'VOD$BMM

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

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

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

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

    FOUFS'VOD$BMM FOUFS*EFOUJpFS MFBWF*EFOUJpFS FOUFS4USJOH-JUFSBM MFBWF4USJOH-JUFSBM
  163. 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
  164. 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
  165. 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
  166. 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
  167. *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
  168. *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
  169. ͜ͷΑ͏ͳந৅ߏจ໦Λ૸ࠪ͢Δؔ਺͕
 ༻ҙ͞Ε͍ͯΔͱɺ͞ΒʹޙͰ঺հ͢Δ
 ੩తݕࠪͷίʔυ͕ͱͯ΋ॻ͖΍͘͢ͳΔ

  170.  ͢΂ͯͷந৅ߏจ໦ͷϊʔυʹɺࣗ਎ͷ
 ࢠͷҰཡΛฦؔ͢਺Λ௥Ճ͢Δ  ϊʔυΛ๚ΕͨΒͷࢠͷҰཡΛऔಘͯ͠ɺ
 ࠶ؼతʹ๚Ε͍ͯؔ͘਺Λ࣮૷͢Δ ૸ࠪؔ਺ͷ࣮૷खॱ

  171. 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 ந৅ߏจ໦ͷϊʔυ͕ࣗ਎ͷࢠΛฦ͢Α͏ʹཁٻ͢ΔجఈΫϥεΛ༻ҙ
  172. 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 ͦΕͧΕͷந৅ߏจ໦ͷϊʔυʹ࣮૷ͯ͠΋Β͏
  173. 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 ϓϩάϥϜͷ͢΂ͯͷࢠͷҰཡΛ഑ྻͱͯ͠ฦ͢ ࠓճ͸จͷ഑ྻҎ֎ʹࢠ͸ͳ͍ͷͰͦͷ··ฦͤ͹Α͍
  174. 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จͷந৅ߏจ໦ͷϊʔυ΋ΈͯΈΑ͏
  175. 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จͷࢠ͸৚݅෦ͷࣜͱɺ\^ͷதͷ
 ෳ਺ͷจͳͷͰɺ·ͱΊͯҰཡʹͯ͠ฦ͢
  176. 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Λ࣮૷ͤ͞Δ
  177. ४උ͕੔ͬͨͷͰɺ૸ࠪؔ਺ͷ
 ຊମΛ࣮૷͍ͯ͘͠

  178. 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)
  179. 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͕লུ͞Ε͍ͯΔ৔߹͸Կ΋͠ͳ͍
  180. 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) ந৅ߏจ໦ͷϊʔυ͔ΒࢠͷҰཡΛ
 औಘ͠ɺࢠΛ࠶ؼతʹ૸͍ࠪͯ͘͠
  181. 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) ࢠͷ૸͕ࠪऴΘͬͨͷͰɺࠓͷϊʔυ͔Βୀग़͢Δ
  182. ͜͜·ͰͰ؆୯ʹ૸ࠪؔ਺Λ࣮૷Ͱ͖ͨ ૸ࠪؔ਺͸഑ྻͰ͍͏GPS&BDIͷΑ͏ͳ
 جૅతͳؔ਺ͩͱߟ͑ΔͱΘ͔Γ΍͍͢ ͦͷͨΊɺͱͯ΋Ԡ༻ͷൣғ͕޿͍

  183. w ৚݅ʹ͋ͬͨந৅ߏจ໦ͷϊʔυͷநग़ w ม਺ؔ਺ͷείʔϓղੳ w ʜ ૸ࠪؔ਺ͰͰ͖Δ͜ͱ

  184. found = find_nodes(node, condition) ৚݅ʹ͋ͬͨந৅ߏจ໦ͷϊʔυͷநग़ ୳ࡧ͍ͨ͠ந৅ߏจ໦ ݕࡧ͍ͨ͠৚݅ͷؔ਺ ͜ͷநग़ؔ਺͸૸ࠪؔ਺Λ࢖ͬͯ
 ؆୯ʹ࣮૷Ͱ͖Δ

  185. 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
  186. 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 ݕࡧ৚݅ʹ͋ͯ͸·Δந৅ߏจ໦ͷϊʔυΛूΊΔ഑ྻ
  187. 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 ந৅ߏจ໦ͷϊʔυΛ๚ΕΔλΠϛϯάͰݕࡧ৚݅ͷ
 ؔ਺Λ࣮૷͠ɺ৚݅ʹ͋ͯ͸·ΔϊʔυΛूΊΔؔ਺
  188. 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ʹࢦఆͯ͠૸ࠪΛ։࢝
  189. 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 ݟ͔ͭͬͨந৅ߏจ໦ͷϊʔυͷ഑ྻΛฦ͢
  190. 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 ૸ࠪؔ਺Λ࢖͏͜ͱͰɺͱͯ΋؆୯ʹ࣮૷Ͱ͖ͨ
  191. ଞʹ΋ɺ૸ࠪؔ਺Λ࢖͏ͱείʔϓղੳΛ
 ࣮૷͠΍͘͢ͳΔ είʔϓղੳͱ͸ɺม਺ͷఆٛ͞Ε͍ͯΔ
 είʔϓΛղੳ͢Δ͜ͱͰɺະఆٛͷม਺΍
 ະ࢖༻ͷม਺Λݟ͚ͭΔख๏

  192. ίʔυ͕͔ͳΓ௕͘ͳΔͷͰɺ࣮ࡍͷίʔυ͸ׂѪ͢Δ͕ɺ
 େ·͔ʹҎԼͷखॱͰείʔϓΛղੳͰ͖Δɿ ؔ਺એݴͷߏจ໦΁ͷPO@FOUFSͰ
 είʔϓΦϒδΣΫτΛ࡞੒͢Δ ࡞੒ͨ͠είʔϓΦϒδΣΫτΛ
 ݱࡏͷείʔϓͷελοΫʹੵΉ ݟ͔ͭͬͨม਺એݴΛελοΫͷઌ಄ͷ
 είʔϓΦϒδΣΫτ΁Ճ͍͑ͯ͘ ؔ਺એݴͷߏจ໦͔ΒͷPO@MFBWFͰ
 ઌ಄ͷείʔϓΦϒδΣΫτΛ


    ελοΫ͔ΒऔΓআ͘    
  193. • https://github.com/estools/escope • https://github.com/rubocop-hq/rubocop
 ͷ VariableForce • https://github.com/Kuniwak/vint
 ͷ scope_plugin

    ࢀߟʹͳΔείʔϓղੳͷ࣮૷
  194. ૸ࠪؔ਺ͷ࣮૷Ͱઆ໌͠ͳ͔ͬͨ͜ͱ w ૸ࠪͷதஅ΍ࢠͷ୳ࡧͷεΩοϓػೳ w ύϑΥʔϚϯεվળͷͨΊʹॏཁ w ୈڃؔ਺Λ࢖Θͳ͍૸ࠪؔ਺ͷ࣮૷ w ݴޠʹΑͬͯ͸ؔ਺ΛҾ਺ʹ౉ͤͳ͍͜ͱ΋͋Δ w

    ͦͷ৔߹͸(P'ͷ7JTJUPSύλʔϯͰ୅ସՄೳ
  195. ૸ࠪؔ਺ͷ·ͱΊ w ந৅ߏจ໦ͷ૸ࠪؔ਺Λఆ͓ٛͯ͘͠ͱศར w ૸ࠪؔ਺ͷ࣮૷͸೉͘͠ͳ͍ w ૸ࠪؔ਺͸͞·͟·ͳॲཧ΁Ԡ༻Ͱ͖Δ

  196. ؆୯ͳ੩తղੳΛ
 ࣮૷ͯ͠ΈΑ͏ Α͏΍͘ΰʔϧ

  197. if isEmpty('') { //CATCH_ME_IF_YOU_CAN } ͓୊͸ɺੜҙؾͳίϝϯτ਺Λใࠂ͢Δ੩తղੳ report_specific_comments(text) # Found 1

    namaiki comments!
  198.  ߏจղੳͯ͠ந৅ߏจ໦Λ࡞੒͢Δ  ந৅ߏจ໦Λ૸ࠪͯ͠ྫͷίϝϯτΛूΊΔ  ूΊͨίϝϯτΛใࠂ͢Δ ࣮૷ͷྲྀΕ

  199. 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 ίʔυ͔ΒੜҙؾͳίϝϯτΛݟ͚ͭͯ਺Λใࠂ͢Δؔ਺
  200. 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 ίʔυΛߏจղੳͯ͠ந৅ߏจ໦Λ࡞੒͢Δ
  201. 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 ߏจղੳʹࣦഊͨ͠ΒɺߏจΤϥʔΛදࣔ
  202. 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 ૸ࠪؔ਺ͷར༻ྫͱ࣮ͯ͠૷ͨ͠ɺݕࡧؔ਺Λ࢖ͬͯੜҙؾͳίϝϯτΛूΊΔ
  203. 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 ݕࡧؔ਺ͷ৚݅ʹࢦఆͨ͠ɺੜҙؾίϝϯτ͔Ͳ͏͔൑ఆ͢Δؔ਺
  204. 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Λฦͯ͠ऴྃ
  205. 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Λฦ͢
  206. 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͕ฦ͞Εͨந৅ߏจ໦ʢੜҙؾͳίϝϯτʣ͕
 Ұཡʹͯ͠ฦ͞ΕΔ
  207. 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 ੜҙؾͳίϝϯτͷ਺Λใࠂͯ͠ऴྃ
  208. 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 ߏจղੳثɾ૸ࠪؔ਺ɾݕࡧؔ਺Λ
 ૊Έ߹Θͤͯ࢖͏͜ͱͰɺͱͯ΋
 ؆୯ʹ੩తݕࠪΛ࣮૷Ͱ͖ͨ
  209. ؆୯ͳ੩తݕࠪͷ࣮૷·ͱΊ ੩తݕࠪ͸ɺߏจղੳثɾ૸ࠪؔ਺ɾݕࡧؔ਺Λ
 ૊Έ߹ΘͤΔ͜ͱͰɺ͘͝؆୯ʹ࣮૷Ͱ͖Δ

  210. ෳࡶͳ੩తղੳΛ
 ෼ׂ͢ΔςΫχοΫ ΰʔϧୡ੒ͷͦͷઌ΁

  211. ઌ΄Ͳ͸ͭͷ੩తݕࠪϧʔϧͷΈΛ࣮૷͕ͨ͠ɺ
 ݱ࣮ʹ͸ෳ਺ͷݕࠪϧʔϧΛ࣮૷͢Δ͜ͱ͕ଟ͍ ͜ͷͱ͖ɺͲͷΑ͏ʹίʔυΛ෼ׂ͢Δ͔ʹΑͬͯ
 อकੑ͕େ͖͘มΘΔ

  212. ݕࠪϧʔϧʹΑΔ෼ׂΛ঺հ ༗໊ͳ੩తղੳπʔϧͷ൒਺Ҏ্͕࠾༻͍ͯ͠Δ

  213. ݕࠪϧʔϧʹΑΔ෼ׂͰ͸ɺͭͷݕࠪϧʔϧ͕
 ͭͷίϯϙʔωϯτʢΫϥεPSؔ਺ʣʹରԠ͢Δ ͜ΕʹΑͬͯݕࠪϧʔϧ͕͓ޓ͍ʹૄʹͳΓɺ
 ݕࠪϧʔϧͷ௥Ճमਖ਼࡟আΛ΍Γ΍͘͢ͳΔ

  214. class Policy: def get_violations(self, node): raise NotImplementedError() ݕࠪϧʔϧͷڞ௨ΠϯλʔϑΣʔεΛܾΊΔ

  215. class Policy: def get_violations(self, node): raise NotImplementedError() ந৅ߏจ໦ͷϊʔυ͔Βݟ͔ͭͬͨ໰୊Λɺจࣈྻͷ
 ഑ྻͱͯ͠ฦ͢ϝιουɻ͜ͷϝιου͸ɺ΄͔ͷ
 ίϯϙʔωϯτ಺Ͱ૸ࠪؔ਺͔Βݺ͹ΕΔΠϝʔδ

  216. class Policy: def get_violations(self, node): raise NotImplementedError() ྫ͑͹ɺҾ਺ʹੜҙؾͳίϝϯτจ͕͖ͨΒ
 ࢦఠϝοηʔδΛฦ࣮͢૷΍ɺJGจ͕͖ͨΒ
 ৚݅෦෼ͷࣜΛݕࠪ͢Δͧɺͱ͍͏࣮૷͕͋Γ͑Δ

    ॏཁͳͷ͸ɺݕࠪϧʔϧ͕ผͳΒผͷΫϥεʹ
 ෼ׂ࣮ͯ͠૷͢Δ͜ͱɻҰॹʹ͢Δͱอक͕େมʹͳΔ
  217. 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 લʹ࣮૷ͨ͠ͷͱಉ͡ɺੜҙؾͳίϝϯτจΛࢦఠ͢Δݕࠪϧʔϧ
  218. 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 Ҿ਺ͷந৅ߏจ໦ͷϊʔυ͕ੜҙؾͳίϝϯτจʼͳΒɺ
 ʮݟ͚ͭͨͧʂʯͱ͍͏ϝοηʔδΛฦ͢Α͏ʹ࣮૷
  219. 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 ੜҙؾͳίϝϯτจΛ൑ఆ͢Δؔ਺͸લͷ΋ͷΛྲྀ༻
  220. 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, [] ) ෳ਺ͷݕࠪϧʔϧΛଋͶΔݕࠪϧʔϧΛ४උ
  221. 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, [] ) ݕࠪϧʔϧͱಉ͡ڞ௨ΠϯλʔϑΣʔεΛ࣮૷͢Δ
  222. 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, [] ) ಺෦ͷෳ਺ͷݕࠪϧʔϧΛϧʔϓͯ͠ݕࠪͯ͠ɺ݁ՌΛ߹੒ͯ͠ฦ͢
  223. 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)
  224. 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) ෳ਺ͷݕࠪϧʔϧͰ·ͱΊͯݕࠪ͢Δؔ਺
  225. 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) ༩͑ΒΕͯจࣈྻΛߏจղੳͯ͠ந৅ߏจ໦Λ࡞੒
  226. 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) ߏจղੳʹࣦഊͨ͠ΒɺߏจղੳࣦഊͷϝοηʔδΛ݁Ռͱͯ͠ฦ͢
  227. 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) ෳ਺ͷݕࠪϧʔϧΛଋͶͯɺͭͷݕࠪϧʔϧʹ·ͱΊΔ
 ʢ͜ͷྫͰ͸ɺੜҙؾͳίϝϯτΛݕࠪ͢Δϧʔϧ͚ͩʣ
  228. 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) ݕࠪϝοηʔδΛूΊΔ഑ྻΛ४උ
  229. 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) ༩͑ΒΕͨந৅ߏจ໦ͷϊʔυ͔ΒɺݕࠪϝοηʔδΛूΊΔؔ਺
  230. 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) ந৅ߏจ໦Λ૸ࠪͯ͠ɺݕࠪϝοηʔδΛूΊ͍ͯ͘
  231. 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) ू·ͬͨݕࠪϝοηʔδΛͭͷ഑ྻʹ·ͱΊͯฦ͢
  232. text = "if isEmpty('') {\n// CATCH_ME_IF_YOU_CAN\n}\n" violations = lint(text) ෳ਺ͷݕࠪϧʔϧͰ੩తղੳͰ͖ΔΑ͏ʹͳͬͨ

  233. ݕࠪϧʔϧʹΑΔ෼ׂͷ·ͱΊ w ੩తݕࠪͷݕࠪϧʔϧ͸ݸผͷίϯϙʔωϯτ
 ͱ࣮ͯ͠૷͢Δͱอक͠΍͍͢ w ଟ͘ͷ੩తݕࠪπʔϧ͸͜ͷύλʔϯʹԊͬͯ
 ࣮૷͞Ε͍ͯΔ

  234. ଞʹ΋࣮૷ύλʔϯ͸ͨ͘͞Μ͋Δ͕ɺ
 ࣌ؒͷ౎߹ʢͱࠓͷମྗʣͰ͸঺հ͖͠Εͳ͍ ͞Βʹ஌Γ͍ͨํ͸ɺʮ7JNTDSJQUͷ-JOU࡞ऀʹ
 ΑΔ୭ಘσβΠϯύλʔϯʯͱ͍͏هࣄΛࢀর https://qiita.com/Kuniwak/items/d6a2d22711e4d7856edd

  235. ࣮ࡍͷ੩తݕࠪπʔϧͷ
 ࣮૷ΛಡΜͰΈΑ͏ ීஈ࢖͍ͬͯΔ

  236. 1ZUIPO w ߏจղੳث w BTUQBSTFʢඪ४ϥΠϒϥϦʣ w ૸ࠪؔ਺ w BTU/PEF7JTJUPSʢඪ४ϥΠϒϥϦʣ w

    ੩తݕࠪϧʔϧ w $IFDLFS53:ʢQZqBLFTͷQZqBLTDIFDLFSQZʣ
  237. 3VCZ w ߏจղੳث w 1BSTFS3VCZ
 (JU)VCͷXIJUFRVBSLQBSTFSͷMJCQBSTFSSVCZZ  w ૸ࠪؔ਺ w

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


    &4-JOUͷMJCVUJMUSBWFSTFKT  w ੩తݕࠪϧʔϧ w SVMFTOPFNQUZGVODUJPO
 &4-JOUͷMJCSVMFTOPFNQUZGVODUJPOKT
  239. ͜Ε·Ͱͷ஌ࣝʹΑͬͯ
 ࡞ΕΔΑ͏ʹͳΔ΋ͷ కΊ͘͘Γʹ

  240. ෳ਺ͷݕࠪϧʔϧʹΑΔ੩తݕࠪ https://github.com/Kuniwak/Vint

  241. $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
  242. ؆қతͳߏจղੳͰґଘؔ܎σʔλϕʔεΛ࡞੒ https://twitter.com/orga_chem/status/674847225684475905

  243. ·ͱΊ ੩తղੳΛߏ੒͢Δجຊతͳߟ͑ํΛ঺հɿ w ந৅ߏจ໦ w ߏจղੳ w ૸ࠪؔ਺ w ݕࠪϧʔϧͷ෼ׂ

    ੩తղੳ͸ҙ֎ͱ؆୯ʹ࣮૷Ͱ͖·͢
  244. ࢀߟจݙ ͍·Ͳ͖ͷϓϩάϥϜݴޠͷ࡞ΓํʢஶSBOEZʣ https://www.amazon.co.jp/dp/4839919232 .POBEJD1BSTFS$PNCJOBUPS http://www.cs.nott.ac.uk/~pszgmh/monparsing.pdf # "