「Ruby/Hom2」の編集履歴(バックアップ)一覧はこちら

Ruby/Hom2」(2011/06/06 (月) 18:40:25) の最新版変更点

追加された行は緑色になります。

削除された行は赤色になります。

【元ネタ】 プログラミング言語「ほむほむ」 - ゆろよろ日記 http://d.hatena.ne.jp/yuroyoro/20110601/1306908421 オリジナルの仕様だと「関数定義の箇所で必ず改行しないといけない(逆に他の箇所では改行できない)」「文字数が凄く多くなる」という難点があったので、文字種を増やして少し文法を改変してみた。 ソース … 書いたはいいけど、どう処分すればいいのかわからない・・・。 #codehighlight(ruby){{{ # # grahom.rb -- "w" の代わりに "ほむ" を使うGrassインタープリタ # #* # 改行は無視する. 区切りには "改行以外の" 空白類を用いること. # # HOM(n) = "ほむ"×n # HOM@(n) = "ほむ"×nの後ろに "っ" or "ー" を付けたもの # EXT = "ぅ" or "!" or "?" # # HOM(m) HOM(n) -> App(m,n) (Grassでいう WWW...Wwwww....w) # HOM@(n) ... -> Abs(n, ...) (Grassでいう www...w ...) # EXT -> 関数定義の終了 (Grassでいう v ) # # 例. # ほむほむほむ ほむ → WWWw # ほむっ ほむほむ ほむほむほむほむ → wWWwwww # # ただし, "っ" or "ー" によって連結された "ほむ"×nは # 各項の長さの積と同じ数の "ほむ" として扱う. # # 例. # ほむほむっほむほむほむー = ほむほむほむほむほむほむー #* #1. Lexer class Token attr_reader :value, :token_type class Hom < Token def initialize(line) @token_type = (line !~ /む$/) ? 'A' : 'B' @value = line.split(/[っー]/).inject(1){ |v, hm| v * hm.length / 'ほむ'.length } end end class Ext < Token; end end class Lexer HOM_PAT = /^\s*(((ほむ)+[っー]?)+)/ EXT_PAT = /^\s*[ぅ!?]+/ def initialize(line) @line = line.gsub(/([^ほむっーぅ!?\s]|\n)/, '') begin nexttoken end until (empty? || @current.token_type == 'A') raise "Error: 関数定義が存在しません" if empty? end attr_reader :current def empty? @current.token_type.nil? && @line.empty? end def skip(type) raise "Error: Appの文法に間違いがあります" unless @current.token_type == type ret = @current; nexttoken; ret end def nexttoken if @line.slice!(HOM_PAT) @current = Token::Hom.new($1) elsif @line.empty? || @line.slice!(EXT_PAT) @current = Token::Ext.new else raise "Error: 構文エラー" end end end #2. Instruction -- eval(runtime) を持つ class App def initialize(m, n) @m, @n = m, n end def eval(runtime) op1, op2 = runtime.env[-@m], runtime.env[-@n] raise "Error: インデックスエラー (at #{inspect})" unless op1 && op2 op1.apply(runtime, op2) end def inspect "App(#{@m},#{@n})" end end class Abs def initialize(code, arity = 1) @code = (arity == 1) ? code : [Abs.new(code, arity-1)] end def eval(runtime) runtime.env << CE.new(@code, runtime.env.dup) end def inspect "Abs(#{@code.inspect})" end end #3. Operator -- apply(runtime, arg) を持つ class Op def inspect self.class.name.slice!(/[^:]+$/) end class In < Op def apply(runtime, arg) runtime.env << (ch = $stdin.getc) ? CHARS[ch] : arg end end class Out < Op def apply(runtime, arg) $stdout.putc(arg.char_code.chr) $stdout.flush runtime.env << arg end end class Succ < Op def apply(runtime, arg) runtime.env << CHARS[(arg.char_code + 1) & 255] end end class Char < Op def initialize(ch) @char_code = ch end attr_reader :char_code def apply(runtime, arg) runtime.env << (arg == self) ? CHURCH_TRUE : CHURCH_FALSE end def inspect "\"#{@char_code.chr}\"" end end CHARS = (0..255).map{ |ch| Char.new(ch) } end class CE def initialize(c, e) @code, @env = c, e end attr_reader :code, :env def apply(runtime, arg) runtime.save_to_dump runtime.code = @code.dup runtime.env = (@env.dup << arg) end def inspect [@code, "ENV(#{@env.length})"].inspect end end CHURCH_TRUE = CE.new( [Abs.new( [App.new(3,2)] )], [CE.new([], [])] ) CHURCH_FALSE = CE.new( [Abs.new([])], [] ) #4. Runtime class Runtime def initialize(c) @code = c @env = [Op::In.new, Op::CHARS[?w], Op::Succ.new, Op::Out.new] @dump = [ CE.new([], []), CE.new([App.new(1, 1)], []) ] end attr_accessor :code, :env def run while true if @code.size > 0 @code.shift.eval(self) else break if @dump.empty? ret = @env[-1] ce = @dump.pop @code, @env = ce.code, (ce.env << ret) end end return @env[-1] end def save_to_dump @dump << CE.new(@code, @env.dup) end end #5. Main def parse_abs(lex, arity) body = [] while lex.current.token_type == 'B' body << App.new(lex.skip('B').value, lex.skip('B').value) end return Abs.new(body, arity) end def parse(lex, dest) until lex.empty? case lex.current.token_type when 'A' then dest << parse_abs(lex, lex.skip('A').value) when 'B' then dest << App.new(lex.skip('B').value, lex.skip('B').value) else lex.nexttoken end end end code = [] parse(Lexer.new($<.read), code) r = Runtime.new(code).run print "\nresult: " + r.inspect }}}
【元ネタ】 プログラミング言語「ほむほむ」 - ゆろよろ日記 http://d.hatena.ne.jp/yuroyoro/20110601/1306908421 オリジナルの仕様だと「関数定義の箇所で必ず改行しないといけない(逆に他の箇所では改行できない)」「文字数が凄く多くなる」という難点があったので、文字種を増やして少し文法を改変してみた。 Rubyソース … http://www42.atwiki.jp/memset?cmd=upload&act=open&pageid=96&file=grahom.rb 書いたはいいけど、どう処分すればいいのかわからない・・・。 #codehighlight(ruby){{{ # # grahom.rb -- "w" の代わりに "ほむ" を使うGrassインタープリタ # #* # 改行は無視する. 区切りには "改行以外の" 空白類を用いること. # # HOM(n) = "ほむ"×n # HOM@(n) = "ほむ"×nの後ろに "っ" or "ー" を付けたもの # EXT = "ぅ" or "!" or "?" # # HOM(m) HOM(n) -> App(m,n) (Grassでいう WWW...Wwwww....w) # HOM@(n) ... -> Abs(n, ...) (Grassでいう www...w ...) # EXT -> 関数定義の終了 (Grassでいう v ) # # 例. # ほむほむほむ ほむ → WWWw # ほむっ ほむほむ ほむほむほむほむ → wWWwwww # # ただし, "っ" or "ー" によって連結された "ほむ"×nは # 各項の長さの積と同じ数の "ほむ" として扱う. # # 例. # ほむほむっほむほむほむー = ほむほむほむほむほむほむー #* #1. Lexer class Token attr_reader :value, :token_type class Hom < Token def initialize(line) @token_type = (line !~ /む$/) ? 'A' : 'B' @value = line.split(/[っー]/).inject(1){ |v, hm| v * hm.length / 'ほむ'.length } end end class Ext < Token; end end class Lexer HOM_PAT = /^\s*(((ほむ)+[っー]?)+)/ EXT_PAT = /^\s*[ぅ!?]+/ def initialize(line) @line = line.gsub(/([^ほむっーぅ!?\s]|\n)/, '') begin nexttoken end until (empty? || @current.token_type == 'A') raise "Error: 関数定義が存在しません" if empty? end attr_reader :current def empty? @current.token_type.nil? && @line.empty? end def skip(type) raise "Error: Appの文法に間違いがあります" unless @current.token_type == type ret = @current; nexttoken; ret end def nexttoken if @line.slice!(HOM_PAT) @current = Token::Hom.new($1) elsif @line.empty? || @line.slice!(EXT_PAT) @current = Token::Ext.new else raise "Error: 構文エラー" end end end #2. Instruction -- eval(runtime) を持つ class App def initialize(m, n) @m, @n = m, n end def eval(runtime) op1, op2 = runtime.env[-@m], runtime.env[-@n] raise "Error: インデックスエラー (at #{inspect})" unless op1 && op2 op1.apply(runtime, op2) end def inspect "App(#{@m},#{@n})" end end class Abs def initialize(code, arity = 1) @code = (arity == 1) ? code : [Abs.new(code, arity-1)] end def eval(runtime) runtime.env << CE.new(@code, runtime.env.dup) end def inspect "Abs(#{@code.inspect})" end end #3. Operator -- apply(runtime, arg) を持つ class Op def inspect self.class.name.slice!(/[^:]+$/) end class In < Op def apply(runtime, arg) runtime.env << (ch = $stdin.getc) ? CHARS[ch] : arg end end class Out < Op def apply(runtime, arg) $stdout.putc(arg.char_code.chr) $stdout.flush runtime.env << arg end end class Succ < Op def apply(runtime, arg) runtime.env << CHARS[(arg.char_code + 1) & 255] end end class Char < Op def initialize(ch) @char_code = ch end attr_reader :char_code def apply(runtime, arg) runtime.env << (arg == self) ? CHURCH_TRUE : CHURCH_FALSE end def inspect "\"#{@char_code.chr}\"" end end CHARS = (0..255).map{ |ch| Char.new(ch) } end class CE def initialize(c, e) @code, @env = c, e end attr_reader :code, :env def apply(runtime, arg) runtime.save_to_dump runtime.code = @code.dup runtime.env = (@env.dup << arg) end def inspect [@code, "ENV(#{@env.length})"].inspect end end CHURCH_TRUE = CE.new( [Abs.new( [App.new(3,2)] )], [CE.new([], [])] ) CHURCH_FALSE = CE.new( [Abs.new([])], [] ) #4. Runtime class Runtime def initialize(c) @code = c @env = [Op::In.new, Op::CHARS[?w], Op::Succ.new, Op::Out.new] @dump = [ CE.new([], []), CE.new([App.new(1, 1)], []) ] end attr_accessor :code, :env def run while true if @code.size > 0 @code.shift.eval(self) else break if @dump.empty? ret = @env[-1] ce = @dump.pop @code, @env = ce.code, (ce.env << ret) end end return @env[-1] end def save_to_dump @dump << CE.new(@code, @env.dup) end end #5. Main def parse_abs(lex, arity) body = [] while lex.current.token_type == 'B' body << App.new(lex.skip('B').value, lex.skip('B').value) end return Abs.new(body, arity) end def parse(lex, dest) until lex.empty? case lex.current.token_type when 'A' then dest << parse_abs(lex, lex.skip('A').value) when 'B' then dest << App.new(lex.skip('B').value, lex.skip('B').value) else lex.nexttoken end end end code = [] parse(Lexer.new($<.read), code) r = Runtime.new(code).run print "\nresult: " + r.inspect }}}

表示オプション

横に並べて表示:
変化行の前後のみ表示: