# -*- coding: utf-8 -*- module MIKU module ToRuby class << self def indent(code) code.each_line.map{|l| " #{l.chomp}\n"}.join end def progn(list, options={quoted: false, use_result: true}) if options[:use_result] progn_code = list.dup progn_last = progn_code.pop if progn_last [*progn_code.map{|n| to_ruby(n, use_result: false)}, to_ruby(progn_last, use_result: :to_return)].join else "" end else list.map{|n| to_ruby(n, use_result: false)}.join end end # rubyに変換して返す # # ==== Args # [sexp] MIKUの式(MIKU::Nodeのインスタンス) # [options] 以下の値を持つHash # quoted :: 真ならクォートコンテキスト内。シンボルが変数にならず、シンボル定数になる # use_result :: 結果を使うなら真。そのコンテキストの戻り値として使うだけなら、 :to_return を指定 def to_ruby(sexp, options={quoted: false, use_result: true}) expanded = sexp case expanded when Symbol (options[:quoted] ? ":" : "") + "#{expanded.to_s}" when Numeric expanded.to_s when String expanded.dump when TrueClass 'true' when FalseClass 'false' when NilClass 'nil' when List if options[:quoted] '[' + expanded.map{|node| to_ruby(node, quoted: true, use_result: true)}.join(", ") + ']' else operator = expanded.car case operator when :quote to_ruby(expanded[1], quoted: true, use_result: true) when :<, :>, :<=, :>=, :eql, :equal, :and, :or, :== converted = { :< => "<", :> => ">", :<= => "<=", :>= => ">=", :eql => "==", :equal => "===", :and => "and", :or => "or", :== => "=="}[operator] if 2 == expanded.size to_ruby(expanded[1], use_result: options[:use_result]) else expanded.cdr.enum_for(:each_cons, 2).map{ |a, b| "#{to_ruby(a)} #{converted} #{to_ruby(b)}" }.join(' and ') end when :eq expanded.cdr.enum_for(:each_cons, 2).map{ |a, b| "#{to_ruby(a)}.equal?(#{to_ruby(b)})" }.join(' and ') when :+, :-, :*, :/ expanded.cdr.join(" #{expanded.car.to_s} ") when :not '!(' + to_ruby(expanded[1]) + ')' when :progn "begin\n" + indent(progn(expanded.cdr.cdr, use_result: options[:use_result])) + "end\n" when :if if options[:use_result] if 3 == expanded.size if expanded[2].is_a?(List) and :progn == expanded[2].car 'if ' + to_ruby(expanded[1]) + "\n" + indent(progn(expanded[2].cdr)) + "end\n" else "(" + to_ruby(expanded[1]) + " and " + to_ruby(expanded[2]) + ")" end else else_code = expanded.cdr.cdr else_last = else_code.pop 'if ' + to_ruby(expanded[1]) + "\n" + indent(to_ruby(expanded[2])) + "else\n" + indent([*else_code.map{|n| to_ruby(n, use_result: false)}, to_ruby(else_last)].join("\n")) + "end\n" end else if 3 == expanded.size to_ruby(expanded[2], use_result: false) + ' if ' + to_ruby(expanded[1]) + "\n" else 'if ' + to_ruby(expanded[1]) + "\n" + indent(to_ruby(expanded[2])) + "else\n" + indent(expanded.cdr.cdr.map{|n| to_ruby(n, use_result: false)}.join("\n")) + "end\n" end end else if expanded[1].is_a? Symbol args = [":" + operator.to_s, *(expanded.size > 2 ? expanded.cdr.cdr.map{|node|to_ruby(node)} : [])].join(", ") "#{to_ruby(expanded[1])}.__send__(#{args})" else args = expanded.size > 2 ? expanded.cdr.cdr.map{|node|to_ruby(node)}.join(",") : "" "#{to_ruby(expanded[1])}.#{operator.to_s}(#{args})" end end end else expanded.to_s end end end end end