ãã®èšäºã¯ãã¯ã·ã³ãã³å·ã·ã¢ãã«ã®Rubyéçºè
ã§ããAaron Pattersonã«ãã£ãŠæžãããŸããã 圌ã¯ä»ãã7幎éRubyéçºã«æ
ç±ãåŸããŠããããã®çŽ æŽãããèšèªã«å¯Ÿããææ
ãåãã§å
±æããŸããã¿ããªã«æ¬ç€ŒïŒ æ°åãè¯ãããšãé¡ã£ãŠããŸãã ä»æ¥ã倪éœã¯é²ã®åŸãããå°ãã®éèŠããŠããã®ã§ãç§ã¯ç¢ºãã«ãã¹ãŠãå®ç§ã«æã£ãŠããŸãïŒ
ãã®èšäºã§ã¯ãRubyãšçµã¿åãããŠäœ¿çšââããããã®å€æ°ã®ã³ã³ãã€ã«ããŒã«ã«ã€ããŠèª¬æããŸãã ãããŠãäž»é¡ã«é£ã³èŸŒãããã«ãJSONããŒãµãŒãäœæããŸãã ãã§ã«ç§ã¯æ¬¡ã®ãããªäžæ©å«ãªæåã®å£°ãèããŠããŸããããŸããã¢ãŒãã³ããªãââïŒ ãã§ã«1,234,567ã®äœåãæžãããŠããŸãããïŒãããã ãã§ãïŒ ãã§ã«1,234,567åã®Ruby JSONããŒãµãŒããããŸãïŒ ãŸããJSONåæãè¡ããŸãããã®ææ³ã¯ãäžåºŠã«ãžã§ããå®äºããã®ã«ååãªã»ã©åçŽã§ãããRubyçšã«éçºãããã³ã³ãã€ã«ããŒã«ãè³¢ã䜿çšããã®ã«ååã«è€éã ããã§ãã
èªã¿ç¶ããåã«ãããã¯æ±ºããŠJSONã®è§£ææ¹æ³ã«é¢ããèšäºã§ã¯ãªããRubyã§ã®åæããã³ã³ã³ãã€ã«ããŒã«ã®äœ¿çšæ¹æ³ã«é¢ããèšäºã§ãããšããäºå®ã«æ³šç®ããããšæããŸãã
äœãå¿
èŠã§ãã
Ruby 1.9.3ã§ãã¹ãããŸãããéžæããå®è£
ã§åäœããã¯ãã§ãã äž»ã«
Racc
ã
StringScanner
ãªã©ã®ããŒã«ã䜿çšããŸãã
ã©ãã¯ïŒ
ã¢ãã©ã€ã¶ãŒãèªåçã«çæããã«ã¯ãRaccãå¿
èŠã§ãã ããã¯ãYACCã«äŒŒãå€ãã®ç¹ã§LALRã¢ãã©ã€ã¶ãŒãžã§ãã¬ãŒã¿ãŒã§ãã æåŸã®ç¥èªã¯ãYet Another Compiler CompilerãïŒå¥ã®ã³ã³ãã€ã©ã³ã³ãã€ã©ïŒãè¡šããŸãããããã¯Rubyã®ããŒãžã§ã³ã§ãããããRaccãå€æããŸããã Raccã¯ãäžé£ã®ææ³èŠåïŒæ¡åŒµåã
.y
ãã¡ã€ã«ïŒããç¶æ
ãã·ã³ã®é·ç§»èŠåãèšè¿°ããRubyãã¡ã€ã«ã«å€æããããšãçŽæããŠããŸãã åŸè
ã¯ãRaccã¹ããŒããã·ã³ïŒã©ã³ã¿ã€ã ïŒã«ãã£ãŠè§£éãããŸãã ã©ã³ã¿ã€ã ã«ã¯Rubyãä»å±ããŠããŸãããæ¡åŒµåãã.yãã®ãã¡ã€ã«ããã·ã³ã®ç¶æ
ããŒãã«ã«å€æããããŒã«ã¯ãããŸããã
gem install racc
å®è¡ããŠ
gem install racc
ã
以éãããyããã¡ã€ã«ãäœæããŸããããšã³ããŠãŒã¶ãŒã¯ããããå®è¡ã§ããŸããã ãããè¡ãã«ã¯ããŸãRubyå®è¡å¯èœã³ãŒãã«å€æããŠããããã®ã³ãŒããgemã«ããã¯ããå¿
èŠããããŸãã å®éãããã¯gem Raccã®ã¿ãã€ã³ã¹ããŒã«ããããšãæå³ãããšã³ããŠãŒã¶ãŒã«ã¯å¿
èŠãããŸããã
ããããã¹ãŠãé ã«åãŸããªãå Žåã§ãå¿é
ããªãã§ãã ããã çè«ããå®è·µã«ç§»è¡ããã³ãŒãã®èšè¿°ãéå§ãããšããã¹ãŠãæããã«ãªããŸãã
StringScannerïŒ
StringScannerã¯ãã¹ãã£ããŒã®åçã«åŸã£ãŠãæååãé çªã«åŠçã§ããã¯ã©ã¹ã§ãïŒååã瀺ããšããïŒã è¡ã®ã©ãã«ãããã«é¢ããæ
å ±ãä¿åããæ£èŠè¡šçŸãšæåã®çŽæ¥èªã¿åãã䜿çšããŠæåããæåŸãŸã§ç§»åã§ããããã«ããŸãã
ããå§ããŸãããïŒ ãŸãã
StringScanner
ãªããžã§ã¯ããäœæãã
StringScanner
䜿çšããŠããã€ãã®æåãåŠçããŸãã
irb(main):001:0> require 'strscan' => true irb(main):002:0> ss = StringScanner.new 'aabbbbb' => #<StringScanner 0/7 @ "aabbb..."> irb(main):003:0> ss.scan /a/ => "a" irb(main):004:0> ss.scan /a/ => "a" irb(main):005:0> ss.scan /a/ => nil irb(main):006:0> ss => #<StringScanner 2/7 "aa" @ "bbbbb"> irb(main):007:0>
ãã®äœçœ®ã®æ£èŠè¡šçŸãé©åããªããªã£ãããã
StringScannerïŒscanã® 3åç®ã®åŒã³åºãã§
nil
è¿ãããããšã«æ³šæããŠãã ããã ãŸãã
StringScanner
ã€ã³ã¹ã¿ã³ã¹ã«å¯ŸããŠ
inspect
ãåŒã³åºããããšãæååå
ã®ãã³ãã©ãŒã®çŸåšäœçœ®ïŒãã®å Žåã¯
2/7
ïŒã確èªã§ããŸãã
StringScannerïŒgetchã䜿çšããŠããã³ãã©ãŒãæå
ããšã«ç§»åããããšãã§ããŸãã
irb(main):006:0> ss => #<StringScanner 2/7 "aa" @ "bbbbb"> irb(main):007:0> ss.getch => "b" irb(main):008:0> ss => #<StringScanner 3/7 "aab" @ "bbbb"> irb(main):009:0>
getch
ã¡ãœããã¯æ¬¡ã®æåãè¿ãããã€ã³ã¿ãŒã1ã€é²ããŸãã
é 次æåååŠçã®åºæ¬ãç解ããã®ã§ãRaccã®äœ¿çšæ¹æ³ãèŠãŠã¿ãŸãããã
Raccã®åºæ¬
å
ã»ã©èšã£ãããã«ãRaccã¯LALRã¢ãã©ã€ã¶ãŒãžã§ãã¬ãŒã¿ãŒã§ãã ããã¯ãå¶éãããæ£èŠè¡šçŸã»ãããäœæã§ããã¡ã«ããºã ã§ãããæ¯èŒã®å®è¡éçšã§ããŸããŸãªäœçœ®ã§ä»»æã®ã³ãŒããå®è¡ã§ããã¡ã«ããºã ã§ãããšæ³å®ã§ããŸãã
äŸãèŠãŠã¿ãŸãããã 次ã®åœ¢åŒã®æ£èŠè¡šçŸã®çœ®æã確èªããããšããŸãïŒ
(a|c)*abb
ã ã€ãŸããä»»æã®æ°ã®æåãaããŸãã¯ãcãã®åŸã«ãabbããç¶ãå Žåãç»é²ããŸãã ãããRaccææ³ã«å€æããããã«ããã®æ£èŠè¡šçŸãæ§æéšåã«åå²ããŠãããå床çµã¿ç«ãŠãããšããŸãã åã
ã®ææ³èŠçŽ ã¯ãçæèŠåãŸãã¯çæç©ãšåŒã°ããŸãã ãããã£ãŠããã®è¡šçŸãå解ããŠã補åã®å€èŠ³ãšRaccã®ææ³ã®åœ¢åŒãèŠãŠã¿ãŸãããã
ãŸããææ³ãã¡ã€ã«ãäœæããŸãã ãã¡ã€ã«ã®å
é ã«ã¯ãååŸãããRubyã¯ã©ã¹ã®å®£èšãããããã®åŸã«è£œåã宣èšããããšãæå³ãã
rule
ããŒã¯ãŒããç¶ãããã®åŸã«
end
ã瀺ã
end
ããŒã¯ãŒããç¶ããŸãã
class Parser rule end
ãa | cãã®è£œåãè¿œå ããŸãã 圌女ã
a_or_c
ãšåŒã³ãŸãããïŒ
class Parser rule a_or_c : 'a' | 'c' ; end
çµæãšããŠãæåãaããŸãã¯ãcããšã®ãããã³ã°ãå®è¡ããã«ãŒã«
a_or_c
ããããŸãã æ¯èŒã1å以äžå®è¡ããããã«ã
a_or_cs
åŒã°ããååž°çãªè£œåãäœæããŸãã
class Parser rule a_or_cs : a_or_cs a_or_c | a_or_c ; a_or_c : 'a' | 'c' ; end
åè¿°ããããã«ã
a_or_cs
ã¯ååž°çã§ãããæ£èŠè¡šçŸ
(a|c)+
ãšåçã§ãã 次ã«ããabbãã®è£œåãè¿œå ããŸãã
class Parser rule a_or_cs : a_or_cs a_or_c | a_or_c ; a_or_c : 'a' | 'c' ; abb : 'a' 'b' 'b'; end
ãããŠããã¹ãŠã®åŒŠè£œäœãå®äºããŸãã
class Parser rule string : a_or_cs abb | abb ; a_or_cs : a_or_cs a_or_c | a_or_c ; a_or_c : 'a' | 'c' ; abb : 'a' 'b' 'b'; end
ãã®æçµåºåã¯ã1ã€ä»¥äžã®æåãaããŸãã¯ãcãã®åŸã«ãabbããŸãã¯ç¬ç«ããæååãabbããååšãããã¿ãŒã³ãšäžèŽããŸãã ããã¯ãã¹ãŠã
(a|c)*abb
ãšãã圢åŒã®å
ã®æ£èŠè¡šçŸãšåçã§ãã
ã¢ãŒãã³ãã§ãã€ãŸããªãïŒ
ããã¯ãæ£èŠè¡šçŸãããã¯ããã«é·ãããšãç¥ã£ãŠããŸãã ãããã1ã€ãã©ã¹ããããŸãããããã³ã°ããã»ã¹ã®ä»»æã®å Žæã§ä»»æã®Rubyã³ãŒããè¿œå ããŠå®è¡ã§ããŸãã ããšãã°ãç¬ç«ããæååãabbãã«åºäŒããã³ã«ã次ã®ãããªãã®ãå°å·ã§ããŸãã
class Parser rule string : a_or_cs abb | abb { puts " abb, !" } ; a_or_cs : a_or_cs a_or_c | a_or_c ; a_or_c : 'a' | 'c' ; abb : 'a' 'b' 'b'; end
å®è¡ããã³ãŒãã¯äžæ¬åŒ§ã§å²ã¿ããã®å®è¡ãæ
åœããã«ãŒã«ã®çŽåŸã«é
眮ããå¿
èŠããããŸãã ããã§ãç¬èªã®JSONã¢ãã©ã€ã¶ãŒãäœæããæºåãæŽããŸããããã®å Žåããã®ã¢ãã©ã€ã¶ãŒã¯ãååŸããç¥èãåããã€ãã³ãããŒã¹ã®ã€ãã³ãã§ãã
ã¢ãã©ã€ã¶ãŒãäœæãã
ã¢ãã©ã€ã¶ãŒã¯ãããŒãµãŒãåå¥ã¢ãã©ã€ã¶ãŒãããã¥ã¡ã³ãããã»ããµã®3ã€ã®ã³ã³ããŒãã³ããªããžã§ã¯ãã§æ§æãããŸãã Raccææ³ã«åºã¥ããŠæ§ç¯ãããããŒãµãŒã¯ãå
¥åã¹ããªãŒã ããã®ããŒã¿ã«ã€ããŠåå¥è§£æããã°ã©ã ã«ã¢ã¯ã»ã¹ããŸãã ããŒãµãŒã¯ãå
±éã®ããŒã¿ã¹ããªãŒã ããJSONèŠçŽ ãåé¢ãããã³ã«ã察å¿ããã€ãã³ããããã¥ã¡ã³ããã³ãã©ãŒã«éä¿¡ããŸãã ããã¥ã¡ã³ããã³ãã©ãŒã¯ãJSONããããŒã¿ãåéãããããRubyã®ããŒã¿æ§é ã«å€æããŸãã JSON圢åŒã®ãœãŒã¹ããŒã¿ãåæããããã»ã¹ã§ã¯ã以äžã®ã°ã©ãã«ç€ºãããã«ãåŒã³åºããè¡ãããŸãã
ããããããžãã¹ã«åãæãããŸãããã ãŸããåå¥è§£æã«çŠç¹ãåœãŠã次ã«ããŒãµãŒã®ææ³ãæ±ããæåŸã«ããã¥ã¡ã³ããã³ãã©ãŒãäœæããŠããã»ã¹ãå®äºããŸãã
åå¥è§£æåš
åå¥ã¢ãã©ã€ã¶ã¯IOæ©èœã«åºã¥ããŠæ§ç¯ãããŠããŸãã ãããããœãŒã¹ããŒã¿ãèªã¿åããŸãã
next_token
åŒã³åºããããã³
next_token
åå¥è§£æ
next_token
å
¥åã¹ããªãŒã ãã1ã€ã®ããŒã¯ã³ãèªã¿åãããããè¿ããŸãã
JSONä»æ§ããåçšãã次ã®ããŒã¯ã³ã®ãªã¹ãã§åäœã
ãŸã ã
- ã²ã
- æ°å
- æ¬åœ
- åœ
- ãã«
é
åããªããžã§ã¯ãã®ãããªè€éãªã¿ã€ãã®å ŽåãããŒãµãŒã責任ãè² ããŸãã
next_token
ã«ãã£ãŠè¿ãããå€ã¯next_token
ã
ããŒãµãŒã¯ãåå¥è§£æ
next_token
åŒã³åºããšãã«ãçµæãšããŠ2ã€ã®èŠçŽ ã®é
åãŸãã¯
nil
ãåãåãããšãæ³å®ããŠããŸãã é
åã®æåã®èŠçŽ ã«ã¯ããŒã¯ã³ã®ååãå«ããå¿
èŠãããã2çªç®ã®èŠçŽ ã«ã¯äœã§ãããŸããŸããïŒéåžžãããã¯åãªãããã¹ãã®äžèŽã§ãïŒã
nil
è¿ãããšã«ãã
nil
åå¥è§£æ
nil
ããŒã¯ã³ããããªãããšãå ±åããŸãã
Tokenizer
ã¬ãã·ã«ã«ã¢ãã©ã€ã¶ãŒTokenizer
ïŒ
ã¯ã©ã¹ã³ãŒããèŠãŠããããäœãããã®ãèŠãŠã¿ãŸãããïŒ
module RJSON class Tokenizer STRING = /"(?:[^"\\]|\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4}))*"/ NUMBER = /-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/ TRUE = /true/ FALSE = /false/ NULL = /null/ def initialize io @ss = StringScanner.new io.read end def next_token return if @ss.eos? case when text = @ss.scan(STRING) then [:STRING, text] when text = @ss.scan(NUMBER) then [:NUMBER, text] when text = @ss.scan(TRUE) then [:TRUE, text] when text = @ss.scan(FALSE) then [:FALSE, text] when text = @ss.scan(NULL) then [:NULL, text] else x = @ss.getch [x, x] end end end end
æåã«ãStringScanneræååãã³ãã©ãŒãšçµã¿åãããŠäœ¿çšââããããã€ãã®æ£èŠè¡šçŸã®å®£èšããããŸãã ãããã¯
json.orgãã
ååŸããå®çŸ©ã«åºã¥ããŠæ§ç¯ãããŸãã StringScannerã€ã³ã¹ã¿ã³ã¹ãã³ã³ã¹ãã©ã¯ã¿ãŒã§äœæãããŸãã äœææã«æååãå¿
èŠãªãããIOãªããžã§ã¯ãã®èªã¿åããåŒã³åºããŸãã ãã ããããã¯ãåå¥è§£æããã°ã©ã ããªããžã§ã¯ãã®IOããããŒã¿ãèªã¿åããªã代æ¿å®è£
ãé€å€ãããã®ã§ã¯ãããŸããããå¿
èŠã«å¿ããŠè¡ããŸãã
äž»ãªäœæ¥ã¯
next_token
ã¡ãœããã§è¡ãããŸãã æååãã³ãã©ãŒã«ããŒã¿ããªãå Žåã¯
nil
è¿ããŸããããã§ãªãå Žåã¯ãæ£ããæ£èŠè¡šçŸãèŠã€ãããŸã§åæ£èŠè¡šçŸããã§ãã¯ããŸãã äžèŽãèŠã€ãã£ãå Žåããã¿ãŒã³ã«äžèŽããããã¹ããšãšãã«ããŒã¯ã³åïŒäŸ
:STRING
ïŒãè¿ããŸãã ã©ã®æ£èŠè¡šçŸãäžèŽããªãå Žåããã³ãã©ãŒãã1æåãèªã¿åãããèªã¿åãããå€ãããŒã¯ã³ã®ååãšãã®å€ãšããŠåæã«è¿ãããŸãã
åå¥è§£æåšã«JSON圢åŒã®æååã®äŸã瀺ããåºåã§ååŸããããŒã¯ã³ã確èªããŸãããã
irb(main):003:0> tok = RJSON::Tokenizer.new StringIO.new '{"foo":null}' => #<RJSON::Tokenizer:0x007fa8529fbeb8 @ss=#<StringScanner 0/12 @ "{\"foo...">> irb(main):004:0> tok.next_token => ["{", "{"] irb(main):005:0> tok.next_token => [:STRING, "\"foo\""] irb(main):006:0> tok.next_token => [":", ":"] irb(main):007:0> tok.next_token => [:NULL, "null"] irb(main):008:0> tok.next_token => ["}", "}"] irb(main):009:0> tok.next_token => nil
ãã®äŸã§ã¯ãIOã䜿çšããŠããã¯ã¿ã€ãã³ã°ãå®çŸããããã«ãJSONæååã
StringIO
ãªããžã§ã¯ãã«ã©ããããŸããã 次ã«ãããã€ãã®ããŒã¯ã³ãèªã¿åã£ãŠãã ããã ã¢ãã©ã€ã¶ãŒã«ããç¥ãããŠããåããŒã¯ã³ã¯ãé
åã®æåã®èŠçŽ ã«ä»ããŠããååã§æ§æãããŠããŸãããäžæãªããŒã¯ã³ã§ã¯ããã®å Žæã¯1æåã§å ããããŠããŸãã ããšãã°ãè¡ããŒã¯ã³ã¯
[:STRING, "foo"]
ã«ãªããç¹å®ã®å ŽåãäžæãªããŒã¯ã³ã¯
['(', '(']
ãŸããæåŸã«ãå
¥åããŒã¿ããªããªããšãåºåã¯
nil
ãŸãã
ããã§ãåå¥ã¢ãã©ã€ã¶ãŒã§ã®äœæ¥ãå®äºããŸããã å
¥åã§ã®åæåäžã«ã
IO
ãªããžã§ã¯ããåãåãã1ã€ã®
next_token
ã¡ãœãããå®è£
ããŸãã ããŒãµãŒã«è¡ãããšãã§ãããã¹ãŠã
ããŒãµãŒ
æ§æã«å
¥ãæéã§ãã å§ãã«ãå°ããããäœæ¥ãå§ããŸãããã
.y
ãã¡ã€ã«ã«åºã¥ãRubyããŒã¹ã®ãã¡ã€ã«çæãå®è£
ããå¿
èŠããããŸãã
rake
ã ãã®ä»äº
1ã³ã³ãã€ã«ã¿ã¹ã¯ã«ã€ããŠèª¬æããŸãã
ãŸãã
ã次ã®ã³ãã³ãã䜿çšããŠ.y
ãã¡ã€ã«ã.rb
ãã¡ã€ã«ã«å€æããããšããã«ãŒã«ãrake-file
ã«.rb
ãŸã ã
rule '.rb' => '.y' do |t| sh "racc -l -o #{t.name} #{t.source}" end
次ã«ãçæããã
parser.rb
ãã¡ã€ã«ã«äŸåãããã³ã³ãã€ã«ãã¿ã¹ã¯ãè¿œå ããŸãã
task :compile => 'lib/rjson/parser.rb'
ææ³ãã¡ã€ã«ã¯
lib/rjson/parser.y
ã«ä¿åãããŠãã
rake compile
ãrake
rake compile
ãå®è¡
rake compile
ãrakeã¯
.rb
ã䜿çšããŠ
.y
ãã¡ã€ã«ãæ¡åŒµå
.rb
ãã¡ã€ã«ã«èªåçã«å€æããŸãã
ãããŠæåŸã«ãããã¹ããã¿ã¹ã¯ããã³ã³ãã€ã«ãã¿ã¹ã¯ã«äŸåãããããã
rake test
ãå®è¡ãããšãã³ã³ãã€ã«ãããããŒãžã§ã³ãèªåçã«çæãããŸãã
task :test => :compile
ããã§ã
.y
ãã¡ã€ã«ã®ã³ã³ãã€ã«ãšæ€èšŒã«çŽæ¥é²ãããšãã§ããŸãã
JSON.orgä»æ§ã®è§£æïŒ
ããã§ã
json.orgããã®ã°ã©ããRaccææ³åœ¢åŒã«å€æããŸãã ãªããžã§ã¯ããŸãã¯é
åã®ããããããœãŒã¹ããã¥ã¡ã³ãã®ã«ãŒãã«ããå¿
èŠããããããobject-
object
ãŸãã¯array-
array
äžèŽãã
document
äœæãäœæã
document
ã
rule document : object | array ;
次ã«ã
array
ã®ç©ãå®çŸ©ããŸãã é
åã®ç©ã¯ã空ã«ãããã1ã€ä»¥äžã®å€ãå«ããããšãã§ããŸãã
array : '[' ']' | '[' values ']' ;
å€ã®çç£ã¯ãåäžã®å€ããŸãã¯ã³ã³ãã§åºåãããè€æ°ã®å€ãšããŠååž°çã«å®çŸ©ãããŸãã
values : values ',' value | value ;
JSONä»æ§ã§ã¯ã
value
æååãæ°å€ããªããžã§ã¯ããé
åãtrueïŒtrueïŒãfalseïŒfalseïŒããŸãã¯nullïŒå€ãªãïŒãšããŠå®çŸ©ãããŠããŸãã å®çŸ©ã¯äŒŒãŠããŸãããå¯äžã®éãã¯ãNUMBERïŒæ°å€ïŒãTRUEïŒçïŒãFALSEïŒåœïŒãªã©ã®å³å€ã«å¯ŸããŠãåå¥ã¢ãã©ã€ã¶ã§å®çŸ©ããã察å¿ããããŒã¯ã³åã䜿çšããããšã§ãã
value : string | NUMBER | object | array | TRUE | FALSE | NULL ;
ãªããžã§ã¯ãïŒ
object
ïŒã®è£œåã®å®çŸ©ã«é²ã¿
object
ã ãªããžã§ã¯ãã¯ç©ºã«ããããšãããã¢ã§æ§æããããšãã§ããŸãã
object : '{' '}' | '{' pairs '}' ;
1ã€ãŸãã¯è€æ°ã®ãã¢ãããããããã¯ã³ã³ãã§åºåãå¿
èŠããããŸãã ç¹°ãè¿ããŸãããååž°çãªå®çŸ©ã䜿çšããŸãã
pairs : pairs ',' pair | pair ;
æåŸã«ãã³ãã³ã§åºåãããæååãšæ°å€ã§ãããã¢ãå®çŸ©ããŸãã
pair : string ':' value ;
Raccã«èªåœããŒã¯ã³ã«ã€ããŠéç¥ããæåã«å®çŸ©ãè¿œå ãããšãããŒãµãŒã®æºåãæŽããŸãã
class RJSON::Parser token STRING NUMBER TRUE FALSE NULL rule document : object | array ; object : '{' '}' | '{' pairs '}' ; pairs : pairs ',' pair | pair ; pair : string ':' value ; array : '[' ']' | '[' values ']' ; values : values ',' value | value ; value : string | NUMBER | object | array | TRUE | FALSE | NULL ; string : STRING ; end
ããã¥ã¡ã³ããã³ãã©ãŒ
ããã¥ã¡ã³ããã³ãã©ãŒã¯ãããŒãµãŒããã€ãã³ããåãåããŸãã 圌ã¯é©ç°çãªJSONããæ¯é¡ãªãRubyãªããžã§ã¯ããæ§ç¯ããŸãïŒ ç§ãããªãã®è£éã§æ®ãã€ãã³ãã®æ°ã§ãããç§ã¯èªåèªèº«ã5ã«å¶éããŸãïŒ
start_object
ãªããžã§ã¯ãã®å
é ã§åŒã³åºãããŸãend_object
ãªããžã§ã¯ãã®çµããã«åŒã³åºãããŸãstart_array
é
åã®å
é ã§åŒã³åºãããŸãend_array
é
åã®æåŸã§åŒã³åºãããŸãscalar
-æååãtrueãfalseãªã©ã®ã¿ãŒããã«ã±ãŒã¹ã§åŒã³åºãããŸãã
ãããã®5ã€ã®ã€ãã³ãã䜿çšããŠãå
ã®JSONæ§é ãåæ ãããªããžã§ã¯ããçµã¿ç«ãŠãŸãã
ã€ãã³ãããã©ããŒããŸã
ãã³ãã©ãŒã¯ãããŒãµãŒããã®ã€ãã³ããåã«è¿œè·¡ããŸãã çµæã¯ããªãŒæ§é ã«ãªããããã«åºã¥ããŠæçµçãªRubyãªããžã§ã¯ããæ§ç¯ããŸãã
module RJSON class Handler def initialize @stack = [[:root]] end def start_object push [:hash] end def start_array push [:array] end def end_array @stack.pop end alias :end_object :end_array def scalar(s) @stack.last << [:scalar, s] end private def push(o) @stack.last << o @stack << o end end end
ããŒãµãŒããªããžã§ã¯ãã®å
é ãæ€åºãããã³ã«ããã³ãã©ãŒã¯ããã·ã¥èšå·ä»ãã®ãªã¹ããã¹ã¿ãã¯ã®å
é ã«è¿œå ããŠãé£æ³é
åã®å
é ã瀺ããŸãã åã§ããã€ãã³ãã¯èŠªã«è¿œå ããããªããžã§ã¯ãã®çµãããæ€åºããããšã芪ã¯ã¹ã¿ãã¯ããããããããŸãã
åããŠç解ããã®ãé£ããããšãé€å€ããŸããã®ã§ãããã€ãã®äŸãèŠãŠã¿ãŸãããã å
¥åã§
{"foo":{"bar":null}}
ã®åœ¢åŒã®JSONæååãæž¡ããšã
@stack
stackã¹ã¿ãã¯å€æ°ã«æ¬¡ã®ããã«ãªããŸãã
[[:root, [:hash, [:scalar, "foo"], [:hash, [:scalar, "bar"], [:scalar, nil]]]]]
ããšãã°ã
["foo",null,true]
ãšãã圢åŒã®é
åã
@stack
ã§ååŸãããšã次ã®ããã«ãªããŸãã
[[:root, [:array, [:scalar, "foo"], [:scalar, nil], [:scalar, true]]]]
Rubyã«å€æïŒ
ãã®ããã«ããŠJSONããã¥ã¡ã³ãã®äžéè¡šçŸãååŸããããRubyã§ã®ããŒã¿æ§é ãžã®å€æã«é²ã¿ãŸãã ãããè¡ãã«ã¯ãçµæã®ããªãŒãåŠçããããã®ååž°é¢æ°ãäœæããŸãã
def result root = @stack.first.last process root.first, root.drop(1) end private def process type, rest case type when :array rest.map { |x| process(x.first, x.drop(1)) } when :hash Hash[rest.map { |x| process(x.first, x.drop(1)) }.each_slice(2).to_a] when :scalar rest.first end end
result
ã¡ãœããã¯
root
ããŒããåé€ããæ®ã£ããã®ã
process
ã¡ãœããã«æž¡ããŸãã
process
ã
hash
æåãæ€åºãããšã
process
ååž°åŒã³åºãã®åã䜿çšããŠé£æ³é
åã圢æã
process
ã ãããšåæ§ã«ãé
åã®åã«å¯Ÿããååž°åŒã³åºãã¯ãæå
array
ã«ééãããšãã«é
åãæ§ç¯ããŸãã ã¹ã«ã©ãŒå€-
scalar
åŠçãªãã§è¿ãããŸãïŒç¡éååž°ãé²ããŸãïŒã ãã³ãã©ãã
result
ãåŒã³åºããšãåºåã§å®æããRubyãªããžã§ã¯ããååŸãããŸãã
å®éã«ã©ã®ããã«æ©èœãããèŠãŠã¿ãŸãããïŒ
require 'rjson' input = StringIO.new '{"foo":"bar"}' tok = RJSON::Tokenizer.new input parser = RJSON::Parser.new tok handler = parser.parse handler.result
ãœãããŠã§ã¢ã€ã³ã¿ãŒãã§ãŒã¹ã®æ¹åïŒ
å®å
šã«æ©èœããJSONã¢ãã©ã€ã¶ãŒãèªç±ã«äœ¿çšã§ããŸãã 確ãã«ã1ã€ã®æ¬ ç¹ããããŸã-éåžžã«äŸ¿å©ãªãœãããŠã§ã¢ã€ã³ã¿ãŒãã§ã€ã¹ããããŸããã åã®äŸã䜿çšããŠæ¹åããŠã¿ãŸãããã
module RJSON def self.load(json) input = StringIO.new json tok = RJSON::Tokenizer.new input parser = RJSON::Parser.new tok handler = parser.parse handler.result end end
ã¢ãã©ã€ã¶ãŒã¯å
ã
IOãªããžã§ã¯ãã«åºã¥ããŠæ§ç¯ããããããå
¥åæã«ãœã±ãããŸãã¯ãã¡ã€ã«èšè¿°åã転éããã人ã®ããã®ã¡ãœãããè¿œå ã§ããŸãã
module RJSON def self.load_io(input) tok = RJSON::Tokenizer.new input parser = RJSON::Parser.new tok handler = parser.parse handler.result end def self.load(json) load_io StringIO.new json end end
ã€ã³ã¿ãŒãã§ãŒã¹ãããå°ã䟿å©ã«ãªã£ãããšã確èªããŸãã
require 'rjson' require 'open-uri' RJSON.load '{"foo":"bar"}'
倧声ã§èã
ããã§ãã¢ãã©ã€ã¶ãŒã®äœæ¥ãå®äºããŸããã ãã®éçšã§ã解æãšåå¥è§£æã®åºæ¬ãå«ãã³ã³ãã€ã«ãã¯ãããžãŒã«ç²Ÿéããããã«ã€ã³ã¿ãŒããªã¿ãŒã«è§ŠããŸããïŒå®éãJSONã®è§£éã«åŸäºããŠããŸããïŒã èªãã«æããã®ããããŸãïŒ
ç§ãã¡ãæžããã¢ãã©ã€ã¶ãŒã¯éåžžã«æè»ã§ããããšãããããŸããã ã§ããããšïŒ
- ãã³ãã©ãŒãã³ãã©ãŒãå®è£
ããŠãã€ãã³ããã©ãã€ã ã§äœ¿çšãã
- ç°¡çŽ åãããã€ã³ã¿ãŒãã§ã€ã¹ã䜿çšããæååãå
¥åã«æž¡ãã ãã§ã
- IOãªããžã§ã¯ããä»ããŠJSON圢åŒã§ã¹ããªãŒã ãéä¿¡ããã«ã¯
ãã®èšäºãããªãã«èªä¿¡ãäžããŠãããããšãé¡ã£ãŠããŸãããããŠãRubyã§å®è£
ãããåæããã³ã³ã³ãã€ã«æè¡ãèªåã§è©ŠããŠã¿ãŠãã ããã ãŸã ç§ã«è³ªåãããå Žåã¯ã
ã³ã¡ã³ãã§æè¿ã
ãŸã ã
PS
çµè«ãšããŠãè¿œå ã®ãããŸãããå°å
¥ããªãããã«ããã¬ãŒã³ããŒã·ã§ã³äžã«çç¥ããããã€ãã®è©³çŽ°ãæ確ã«ããããšæããŸãã
- ã¢ãã©ã€ã¶ãŒã®æçµææ³ã¯æ¬¡ã®ãšããã§ãã .yãã¡ã€ã«ã®-innerã»ã¯ã·ã§ã³ã«æ³šæããŠãã ããã ãã®ã»ã¯ã·ã§ã³ã§ç€ºããããã¹ãŠã®ãã®ã¯ãèªåçæã®çµæãšããŠååŸãããããŒãµãŒã¯ã©ã¹ã«è¿œå ãããŸãã ããã¯ããã³ãã©ãªããžã§ã¯ããããŒãµãŒã«æž¡ãæ¹æ³ã§ãã
- ããŒãµãŒã¯ãå®éã«ã¿ãŒããã«JSONããŒãããRuby ãžã®å€æãè¡ããŸãã ãã®ãããããŒãµãŒãšããã¥ã¡ã³ããã³ãã©ãŒã§JSONãRubyã«2åå€æããŸãã åŸè
ã¯æ§é ãæ
åœããåè
ã¯å³æå€ïŒtrueãfalseãªã©ïŒãæ
åœããŸãã ãã¹ãŠã®å€æãããŒãµãŒã§å®è¡ããããéã«ããŒãµãŒããå®å
šã«é€å€ããå¿
èŠãããããšã«æ³šæããããšã¯éåžžã«åççã§ãã
- æåŸã«ã åå¥è§£æããã°ã©ã ã¯ãããã¡ãªã³ã°ã䜿çšããŸãã ããããååŸã§ããéãããã¡ããŒãžã§ã³ãã¹ã±ããããŸããã ããã¯ããªãç²éã§ãããæéç¶æ
ãã·ã³ã®ããžãã¯ã䜿çšããŠé ã«æµ®ãã¶ããšãã§ããŸãã
以äžã§ãã ãæž
èŽããããšãããããŸããïŒ
1è±èª çæ-çæ
翻蚳ã«é¢ããã³ã¡ã³ãã¯ãå人ã§éã£ãŠãã ããã