# Interpeterパターン # # Java言語で学ぶデザインパターンと同様の例題をRubyで記述した。 # # coded by Takehiro Kaga # # ::= program class ProgramNode def perse(context) context.skipToken("program") @commandListNode = CommandListNode.new @commandListNode.perse(context) end def toString "[program " + @commandListNode.toString + "]" end end # ::= * end class CommandListNode def initialize @list = UserList.new end def perse(context) while true if context.currentToken == nil err elsif context.currentToken == "end" context.skipToken("end") break else @commandNode = CommandNode.new @commandNode.perse(context) @list.push(@commandNode) end end end def toString "" + @list.toString end end class UserList < Array def toString char = "[" self.each{|item| temp = "" + item.toString + ", " char += temp } if char != "[" char = char.chop char = char.chop end char += "]" end end # ::= | class CommandNode def perse(context) if context.currentToken == "repeat" @node = RepeatCommandNode.new @node.perse(context) else @node = PrimitiveCommandNode.new @node.perse(context) end end def toString @node.toString end end # ::= repeat class RepeatCommandNode def initialize @commandListNode = Array.new end def perse(context) context.skipToken("repeat") @number = context.currentNumber context.nextToken @commandListNode = CommandListNode.new @commandListNode.perse(context) end def toString "[repeat " + @number + " " + @commandListNode.toString + "]" end end # ::= go | right | left class PrimitiveCommandNode def perse(context) @name = context.currentToken context.skipToken(@name) if @name != "go" and @name != "right" and @name != "left" puts "name error" end end def toString @name.to_s end end # newした時にカレントトークンをセットしておく。 # text をワード単位に取り出してアレイにセットしておく。# # skipTokenではカレントトークンが引数と一致しなければ例外を発生する。 # currentNumberはカレントトークンのストリングを数値に変換する。 class Context def initialize(text) @tokenizer = text.scan(/\w+/) nextToken end def nextToken @currentToken = @tokenizer.shift end def currentToken @currentToken end def skipToken(token) if token != @currentToken puts "skipToken Error" end nextToken end def currentNumber @number = @currentToken.to_s @number end end # ---- Main ---- # textArray = ["program end", "program go end", "program go right go right go right go right end", "program repeat 4 go right end end", "program repeat 4 repeat 3 go right go left end right end end" ] textArray.each{|text| puts "text = \"" + text + "\"" node = ProgramNode.new node.perse(Context.new(text)) puts "node = " + node.toString }