module Julializer

Constants

VERSION

Public Class Methods

ruby2julia(source, config={}) click to toggle source
# File lib/julializer.rb, line 7
def ruby2julia(source, config={})
  @globals = []
  {:nullable=>false}.each do |k,v|
    instance_variable_set("@#{k}", config[k] || v)
  end
  #File.write("./__julialize_debug.log", Ripper.sexp(source).pretty_inspect)
  transpile(Ripper.sexp(source))
end

Private Class Methods

delim(s) click to toggle source
# File lib/julializer.rb, line 17
def delim(s)
  if s.kind_of?(Array) then
    s.map{|line| "#{line};" }.join
  else
    "#{s};"
  end
end
transpile(s) click to toggle source
# File lib/julializer.rb, line 25
def transpile(s)
  if s.instance_of?(String) then
    s
  elsif s.instance_of?(Array) && s[0].instance_of?(Symbol) then
    #puts "s[0]=#{s[0]}"
    #puts "s[1]=#{s[1]}"
    case s[0]
    when :program, :bodystmt  #, :else
      s[1].map{|e| delim(transpile(e))}.join

    #when :if, :elsif
    #  s[2].map{|e| delim(transpile(e))}.join

    when :assign
      delim(transpile(s[1]) + "=" + transpile(s[2]))

    when :var_field, :var_ref
      if [:@gvar, :@ivar, :@cvar].include?(s[1][0])
        @globals << s[1][1][1..-1]
        s[1][1][1..-1]
      elsif s[1][0]==:@const
        (s[0]==:var_field ? "const " : "") + s[1][1].downcase
      else
        s[1][1]
      end

    when :next
      delim("continue")

    when :break
      delim("break")

    when :void_stmt
      "" # this happens when rip "if true then # do nothing end" type of code

    when :binary
      if s[2] == :<< then
        "push!(#{transpile(s[1])}, #{transpile(s[3])})"
      elsif s[1][0] == :string_literal && s[2] == :% then
        if s[3][0] == :array then
          "@sprintf(#{transpile(s[1][1][1])},#{s[3][1].map{|e| transpile(e)}.join(',')})"
        else
          "@sprintf(#{transpile(s[1][1][1])},#{transpile(s[3])})"
        end
      else
        case s[2]
        when :or
          operator = "||"
        when :and
          operator = "&&"
        when :^
          operator = "$"
        when :**
          operator = "^"
        when :===
          operator = "==" # Prevent using === since in Julia === checks much strictly than Ruby.
        else
          operator = s[2].to_s
        end
        transpile(s[1]) + operator + transpile(s[3])
      end

    when :opassign
      delim(transpile(s[1]) + transpile(s[2]) + transpile(s[3]))

    when :unary
      case s[1]
      when :not, :!
        "!" + transpile(s[2])
      when :-@
        "(-" + transpile(s[2]) + ")"
      when :~
        "~" + transpile(s[2])
      else
        transpile(s[2])
      end

    when :paren
      "(" + transpile(s[1][0]) + ")"

    when :symbol
      s[1][1]

    when :field, :call
      case s[1][1][1]
      when "Math"
        transpile(s[3][1])
      when "Random"
        case s[3][1]
        when "rand"
          "rand()"
        else
          transpile(s[3][1])
        end
      else
        case s[3][1]
        when "dup"
          "copy(#{transpile(s[1])})"

        when "shift"
          "shift!(#{transpile(s[1])})"

        when "to_f"
          "float(#{transpile(s[1])})"

        when "to_i", "to_int"
          "trunc(Int64,parse(string(#{transpile(s[1])})))"

        when "truncate"
          "trunc(#{transpile(s[1])})"

        when "chr"
          "Char(#{transpile(s[1])})"

        when "ord"
          "Int(#{transpile(s[1])})"

        when "class"
          "typeof(#{transpile(s[1])})"

        when "to_s"
          "string(#{transpile(s[1])})"

        when "even?"
          "iseven(#{transpile(s[1])})"

        when "odd?"
          "isodd(#{transpile(s[1])})"

        when "infinite?"
          "isinf(#{transpile(s[1])})"

        when "finite?"
          "isfinite(#{transpile(s[1])})"

        when "zero?"
          "(#{transpile(s[1])}==0)"

        when "integer?"
          "(typeof(#{transpile(s[1])})==Int64)"

        when "nan?"
          "isnan(#{transpile(s[1])})"

        when "abs", "magnitude"
          "abs(#{transpile(s[1])})"

        when "ceil", "floor"
          "#{s[3][1]}(Int64,#{transpile(s[1])})"

        when "size", "count"
          "size(#{transpile(s[1])})[1]"

        when "next", "succ"
          "(#{transpile(s[1])}+1)"

        when "pred"
          "(#{transpile(s[1])}-1)"

        when "next_float"
          "(nextfloat(#{transpile(s[1])}))"

        when "prev_float"
          "(prevfloat(#{transpile(s[1])}))"

        when "round"
          "round(Int64,#{transpile(s[1])})"

        else
          "#{transpile(s[3])}(#{transpile(s[1])})"

        end
      end

    when :array
      s[1].nil? ? "[]" : "["+s[1].map{|e| transpile(e[1])}.join(",")+"]"

    when :@CHAR
      "'" + s[1][1] + "'"

    when :@ident, :@int, :@kw, :@op
      if s[0]==:@int && s[1][0..1]=="0x" then
        "(#{s[1]}+0)" # Promote to Int64 from smaller numbers for cases like spec/core/float/divide_spec.rb (otherwise 91.1/-0xffffffff fails)
      else
        s[1]
      end

    when :string_content
      s[1].nil? ? "" : 'string(' + s[1..-1].map{|e| transpile(e)}.join(",") + ')'

    when :@tstring_content
      '"' + s[1] + '"'

    # commented out for simplification
    #when :vcall

    when :command
      case s[1][1]
      when "p", "puts", "printf", "print"
        c = "print"
      else
        c = transpile(s[1])
      end
      delim(c + "(" + transpile(s[2]) + ")")

    when :method_add_block
      t = s[1][0]==:call ? s : s[1]
      if t[1][3][1]=="each"
        delim([
          "___state = start(#{t[1][1][1][1]})",
          "while !done(#{t[1][1][1][1]}, ___state)",
          "___i, ___state = next(#{t[1][1][1][1]}, ___state)",
          (s[2][1].nil? ? "" : "#{s[2][1][1][1][0][1]} = ___i"),
          s[2][2].map{|e| delim(transpile(e))}.join,
          "end"
        ])
      elsif t[1][3][1]=="downto"
        delim([
          "for #{transpile(s[2][1])} in countfrom(#{transpile(t[1][1])}, -1)",
          "if #{transpile(s[2][1])}<#{transpile(t[2][1][1])} break end",
          s[2][2].map{|e| delim(transpile(e))}.join,
          "end"
        ])
      elsif t[1][3][1]=="times"
        delim([
          "for #{transpile(s[2][1]).nil? ? "___xyz___" : transpile(s[2][1]) } in 0:#{transpile(t[1][1])}-1",
          s[2][2].map{|e| delim(transpile(e))}.join,
          "end"
        ])
      else
        transpile(s[1])
      end

    when :method_add_arg
      if transpile(s[1][1])=="array" && transpile(s[1][3])=="new"
        "fill(#{s[2][1][1][1][1]}, #{s[2][1][1][0][1][1]})"
      elsif transpile(s[1][1])=="random" && transpile(s[1][3])=="rand"
        "rand(#{transpile(s[2][1])})"
      else
        case transpile(s[1][3])
        when "slice", "slice!"
          method = transpile(s[1][3]) == "slice!" ? "splice!" : transpile(s[1][3])
          start_index=0
          if s[2][1][1][0][0] == :dot2 || s[2][1][1][0][0] == :dot3
            range = "#{transpile(s[2])}"
          elsif s[2][1][1].count == 1
            range = "#{transpile(s[2])}+1"
          else
            range = "#{transpile(s[2][1][1][0])}+1:#{start_index}+#{transpile(s[2][1][1][0])}+#{transpile(s[2][1][1][1])}+1"
          end
          "#{method}(#{transpile(s[1][1])}, #{range})"

        when "send"
          case transpile(s[2][1][1][0])
          when "==", "==="
            # Prevent using === since in Julia === checks much strictly than Ruby.
            "==(#{transpile(s[2][1][1][1])}, #{transpile(s[1][1])})"
          when "magnitude"
            "abs(#{transpile(s[1][1])})"
          when "to_i", "to_int"
            "trunc(Int64,parse(string(#{transpile(s[1][1])})))"
          when "truncate"
            "trunc(#{transpile(s[1][1])})"
          when "-@"
            "-(#{transpile(s[1][1])})"
          when "+@"
            transpile(s[1][1])
          when "%", "modulo"
            "#{transpile(s[1][1])}%#{transpile(s[2][1][1][1])}"
          when "fdiv"
            "#{transpile(s[1][1])}/#{transpile(s[2][1][1][1])}"
          when "next", "succ"
            "(#{transpile(s[1][1])}+1)"
          else
            "#{transpile(s[2])}(#{transpile(s[1][1])})"
          end
        when "index"
          "findfirst(#{transpile(s[1][1])}, #{transpile(s[2])})"
        when "to_s"
          "base(#{transpile(s[2])}, #{transpile(s[1][1])})"
        when "to_i"
          "parse(Int, #{transpile(s[1][1])}, #{transpile(s[2])})"
        when "rindex"
          "findlast(#{transpile(s[1][1])}, #{transpile(s[2])})"
        when "div"
          "floor(#{transpile(s[1][1])}/#{transpile(s[2])})"
        when "modulo"
          "#{transpile(s[1][1])}%#{transpile(s[2])}"
        when "divmod"
          "divrem(#{transpile(s[1][1])},#{transpile(s[2])})"
        when "fdiv"
          "#{transpile(s[1][1])}/#{transpile(s[2])}"
        when "eql?"
          "===(#{transpile(s[1][1])}, #{transpile(s[2])})"
        when "round"
          "round(#{transpile(s[1][1])}, #{transpile(s[2])})"
        when "atan2"
          "atan2(#{transpile(s[2])})"
        when "gcd"
          "gcd(#{transpile(s[1][1])}, #{transpile(s[2])})"
        when "lcm"
          "lcm(#{transpile(s[1][1])}, #{transpile(s[2])})"
        when "gcdlcm"
          "[gcd(#{transpile(s[1][1])}, #{transpile(s[2])}), lcm(#{transpile(s[1][1])}, #{transpile(s[2])})]"
        when "key?", "has_key?"
          "haskey(#{transpile(s[1][1])}, #{transpile(s[2])})"
        when "rand"
          "int(rand() * #{transpile(s[2])})"

        else
          transpile(s[1]) + "(" + (transpile(s[2]) || "") + ")"
        end
      end

    when :args_add_block
      s[1].map{|e| transpile(e)}.join(",")

    when :while
      delim([
        "while " + transpile(s[1]),
         transpile(s[2]),
         "end"
      ])

    when :for
      delim([
        "for #{transpile(s[1])}::Int64 = #{transpile(s[2])}",
        transpile(s[3]),
        "end"
      ])

    when :if, :elsif
      if s[0]==:elsif then
        keyword = "elseif"
      else
        keyword = "if"
      end
      additional_condition = !s[3].nil? ? transpile(s[3]) : "end"
      delim([
        "#{keyword} " + transpile(s[1]),
        s[2].map{|e| delim(transpile(e))}.join,
        "#{additional_condition}"
      ])

    when :if_mod
      delim([
        "if " + transpile(s[1]),
        transpile(s[2]),
        "end"
      ])

    when :else
      delim([
        "else",
        s[1].map{|e| delim(transpile(e))}.join,
        "end"
      ])

    when :def
      code=transpile(s[3])
      globals_code=@globals.uniq.join(",")
      @globals=[]
      delim([
        "function #{s[1][1]}(#{transpile(s[2][1])})",
        (globals_code.length>0 ? delim("global #{globals_code}") : "") + code,
        "end"
      ])

    when :params
      s[1].nil? ? "" : s[1].map{|e| e[1]}.join(", ")

    when :dot2, :dot3
      "#{transpile(s[1])}:#{transpile(s[2])}"

    when :aref, :aref_field
      aref_string = ->(type, list, index){
        # using #get function for Nullable compatible access
        type==:aref && @nullable ? "get(#{list}, #{index}, Nullable)" : list + "[#{index}]"
      }
      if !s[2].flatten.include?(:dot2) && !s[2].flatten.include?(:dot3) && s[2][1][0][0]!=:string_literal then
        aref_string.call(s[0], transpile(s[1]), transpile(s[2])+"+1")
      else
        if s[2].flatten.include?(:dot2)
          transpile(s[1]) + "[#{transpile(s[2][1][0][1])}+1:#{transpile(s[2][1][0][2])}+1]"
        elsif s[2].flatten.include?(:dot3)
          transpile(s[1]) + "[#{transpile(s[2][1][0][1])}+1:#{transpile(s[2][1][0][2])}]"
        else
          aref_string.call(s[0], transpile(s[1]), transpile(s[2]))
        end
      end

    when :const_path_ref
      if s[1][1][1]=="Float"
        case transpile(s[2])
        when "NAN"
          "NaN"
        when "INFINITY"
          "Inf"
        when "EPSILON"
          "eps()"
        when "MAX"
          "typemax(Float64)"
        when "MIN"
          "typemin(Float64)"
        else
          transpile(s[1])
        end
      else
        transpile(s[1])
      end

    when :return
      delim("return #{transpile(s[1])}")

    else
      transpile(s[1])

    end

  elsif s.instance_of?(Array) && s[0].instance_of?(Array) then
    s.map{|e| transpile(e)}.join
  end
end