[
  {
    "path": ".gitignore",
    "content": ".eunit\ndeps\n*.o\n*.beam\n*.plt\n.*-*\nerl_crash.dump\nebin\nrel/example_project\n.concrete/DEV_MODE\n.rebar\nrb_test/*.out\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"ext/ruby/spec\"]\n\tpath = ext/ruby/spec\n\turl = https://github.com/ruby/spec.git\n[submodule \"ext/ruby/mspec\"]\n\tpath = ext/ruby/mspec\n\turl = https://github.com/ruby/mspec.git\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: erlang\notp_release:\n   - 18.0\n   - 18.1\nbefore_script:\n        - rvm install 2.3.1\n        - rvm use 2.3.1\n        - bundle install\nscript:\n        - rebar compile && ./test.rb\n\n"
  },
  {
    "path": "COPYING.txt",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 Yu Hsiang Lin.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Gemfile",
    "content": "ruby \"2.3.1\"\nsource \"https://rubygems.org\"\n\ngem 'erlport-ast_mapping', github: \"johnlinvc/erlport-ast_mapping\", branch: \"ruby_2_3\"\ngem 'guard'\ngem 'guard-shell'\ngem 'guard-rebar', github: \"johnlinvc/guard-rebar\", branch: \"feature/update_to_guard_2_13\"\n"
  },
  {
    "path": "Guardfile",
    "content": "# A sample Guardfile\n# More info at https://github.com/guard/guard#readme\n\n## Uncomment and set this to only include directories you want to watch\n# directories %w(app lib config test spec features) \\\n#  .select{|d| Dir.exists?(d) ? d : UI.warning(\"Directory #{d} does not exist\")}\n\n## Note: if you are using the `directories` clause above and you are not\n## watching the project directory ('.'), then you will want to move\n## the Guardfile to a watched dir and symlink it back, e.g.\n#\n#  $ mkdir config\n#  $ mv Guardfile config/\n#  $ ln -s config/Guardfile .\n#\n# and, you'll have to watch \"config/Guardfile\" instead of \"Guardfile\"\n\nguard 'rebar-compile', all_on_start: true do\n  watch(%r{src/.*\\.erl})\n  watch(%r{test/.*\\.erl})\nend\n\nguard :shell do\n  watch(/rb_test\\/(.*)\\.rb/) {|m| `./test.rb -v #{m[0]}` }\nend\n"
  },
  {
    "path": "README.md",
    "content": "# ErRuby - an implementation of the Ruby language on Erlang\n[![Build Status](https://travis-ci.org/johnlinvc/erruby.svg?branch=develop)](https://travis-ci.org/johnlinvc/erruby)\n## About\n\nErRuby is an implementation of the Ruby language using Erlang.\n\nIt aims to bring some concurrency features to ruby by experimenting.\n\nIt's still a work in progress. So use it at your own risk.\n\n## Install\n\n### Prerequisites\n \n- erlang vm\n- rebar2\n- ruby (2.3.1)\n\nTo install erlang & rebar on OS X, using homebrew\n\n\tbrew install erlang rebar\n\n### Building\n\nAfter getting the source of ErRuby, get the gems for parser with bundler using:\n\t\n\tbundle install\n\t\n \nThen get the deps of erlang modules by using:\n\n\trebar get-deps\n\t\nLast, compile ErRuby with:\n\n\trebar compile\n\t\n\t\nTest the build result with:\n\n\t./test.rb\n\t\nIt should output `everything pass`\n\n\n## Goals\n\n- Concurrent Features.\n- Run mspec.\n- GC.\n- Friendly installation with rvm/rbenv\n\n## Supported features\n\nCurrently it support some of the basic ruby constructs.\n\nSupported features:\n\n- `method` definition & calling.\n- singleton methods, class methods.\n- `class` and inheritance.\n- `block` and `yield`.\n- Constants.\n- Local variables.\n- Instance variables.\n- `load` & `require_relative`.\n- `Boolean` & `Integer` with basic methods.\n- `String` literal.\n- `Array` literal.\n\nUnsupported core features\n\n- class initializer, class instance variables.\n- `module` definition, `include`, `extend`.\n- variadic argument in function.\n- keyword argument in function.\n- GC.\n\n### Class & inherentance\n```ruby\nclass Foo\n  def to_s\n    \"foo\"\n  end\nend\n\nclass Bar < Foo\nend\n\nclass Alice < Bar\n  def to_s\n    \"i'm alice\"\n  end\n  def self.name\n    \"Alice\"\n  end\nend\n\nputs Foo.new.to_s # \"foo\"\nputs Bar.new.to_s # \"foo\"\nputs Alice.new.to_s # \"i'm alice\"\nputs Alice.name # \"Alice\"\n```\n\n### block\n```ruby\ndef yield_with_arg(s,x)\n  yield s,x\nend\n\nyield_with_arg(\"yield with\",\"arg\") do |ss, xx|\n  puts ss # \"yield with\"\n  puts xx # \"arg\"\nend\n\n3.times do |i|\n  puts i.to_s\n  4.times do |j|\n    puts j.to_s\n  end\nend\n\n([1,2,3]*1000).pmap do |x|\n  x+1\nend\n\n\n```\n\n\n## License\n\nErRuby is licensed to you under MIT license. See the [COPYING.txt](COPYING.txt) file for more details.\n"
  },
  {
    "path": "TODO.md",
    "content": "restructure to standard erlang style\ndesign the object system of ruby in erlang\n- everything is object, just like ruby\n- every object is a process. message invoke with message passing using the actor model.\nuse otp to run the code\n"
  },
  {
    "path": "erruby",
    "content": "#!/bin/bash\npushd `dirname $0` > /dev/null\nexport ERRUBY_PATH=`pwd`\npopd > /dev/null\nif [[ $* == *\"-f\"* ]]\nthen\nescript \"${ERRUBY_PATH}/ebin/erruby.beam\" $*\nelse\nerl -noshell +P 134217727 -pa ebin -s erruby main \"{$*}\" -s init stop\nfi\n"
  },
  {
    "path": "erruby_test/pipe_dot.rb",
    "content": "def cal\n  1 + 1\nend\nx = self|.cal\nputs(x.to_s)\nputs(x.to_s)\nputs(1|.to_s)\n1|.to_s\n\ndef str\n  \"hello future\"\nend\nputs(self|.str)\n"
  },
  {
    "path": "erruby_test/undefined_method.rb",
    "content": "class Foo\nend\nf = Foo.new\nf.bar\n"
  },
  {
    "path": "pre_compile",
    "content": "#!/bin/bash\npushd ./deps/erlport > /dev/null\nmake > /dev/null\npopd > /dev/null\n\npushd ./deps/plists > /dev/null\n./make.sh\npopd > /dev/null\n"
  },
  {
    "path": "rb_src/erruby.rb",
    "content": "#!/usr/bin/env ruby\n\nrequire 'rubygems'\nrequire 'bundler/setup'\nrequire 'erlport/ast_mapping'\n\ndef parse(src)\n  ErlPort::AstMapping.parse(src)\nend\n\ndef install_encoder\n  ErlPort::AstMapping.install_encoder\nend\n\nif __FILE__ == $0\n  p parse(\"[1,2,3]\".each_char.map(&:ord))\nend\n"
  },
  {
    "path": "rb_test/_load.rb",
    "content": "puts \"loaded\"\n"
  },
  {
    "path": "rb_test/_require_relative.rb",
    "content": "puts \"required file\"\n"
  },
  {
    "path": "rb_test/and_dot.rb",
    "content": "puts(1&.to_s)\nnil&.not_exist_method\n"
  },
  {
    "path": "rb_test/array_class.rb",
    "content": "[true, false]\n[nil, nil]\n[\"a\",\"b\",\"c\"]\n[1,2].map{|x| puts(x.to_s)}\nputs \"mul\"\n([1,2,3] * 3).map{|x| puts x.to_s}\n#a = 0\n#[1,2,3].map do |x|\n  #a = a + x\n#end\n#puts a\nputs [\"a\",\"b\",\"c\"].at(1)\nputs [\"a\",\"b\",\"c\"].first\nputs [\"a\",\"b\",\"c\"].last\n\nputs [false].empty?.to_s\nputs [].empty?.to_s\nputs [1, 2, 3].length.to_s\nputs [1, 2, 3].size.to_s\nary = [\"a\",\"b\"]\nary.concat [\"c\", \"d\"]\nputs ary.last\nary2 = ary + [\"e\", \"f\"]\nputs ary2.last\nary = [\"a\",\"b\",\"c\"]\nary.push \"d\"\nputs ary.last\nary << \"e\" << \"f\"\nputs ary.last\narray = [\"b\", \"c\"]\narray.unshift(\"a\")\nputs array.first\nputs array.shift\nputs array.first\n\nputs [1, 2, 3, 4, 5].drop(1).length.to_s\nputs [1, 2, 3, 4, 5].drop(3).length.to_s\nputs [1, 2, 3, 4, 5].drop(0).length.to_s\nputs [1, 2, 3, 4, 5].drop(5).length.to_s\nputs [1, 2, 3, 4, 5].drop(10).length.to_s\n"
  },
  {
    "path": "rb_test/boolean_class.rb",
    "content": "puts (true).to_s\nputs (false).to_s\n\nputs (!true).to_s\nputs (!false).to_s\n\nputs (true == false).to_s\nputs (true == true).to_s\nputs (false == true).to_s\nputs (false == false).to_s\n\nputs (true & true).to_s\nputs (true & false).to_s\nputs (false & true).to_s\nputs (false & false).to_s\n\nputs (true ^ true).to_s\nputs (true ^ false).to_s\nputs (false ^ true).to_s\nputs (false ^ false).to_s\n\nputs (true | true).to_s\nputs (true | false).to_s\nputs (false | true).to_s\nputs (false | false).to_s\n\nputs true.to_s\nputs false.to_s\n\nputs true.inspect\nputs false.inspect\n"
  },
  {
    "path": "rb_test/class_cross_call.rb",
    "content": "class Foo\n  def msg\n    \"hello\"\n  end\nend\n\nclass Bar\n  def say\n    puts Foo.new.msg\n  end\nend\n\nBar.new.say\n"
  },
  {
    "path": "rb_test/class_def.rb",
    "content": "class Foo\n  def message\n    \"hello world\"\n  end\n  def hello\n    puts message\n  end\nend\nclass Bar\n  def message\n    \"hello bar\"\n  end\n  def hello\n    puts message\n  end\nend\nf = Foo.new\nf.hello\nff = Foo.new\n\nBar.new.hello\n"
  },
  {
    "path": "rb_test/class_inherent.rb",
    "content": "class Foo\n  def to_s\n    \"foo\"\n  end\nend\n\nclass Bar < Foo\nend\n\nclass Alice < Bar\n  def to_s\n    \"i'm alice\"\n  end\nend\n\nputs Foo.new.to_s # \"foo\"\nputs Bar.new.to_s # \"foo\"\nputs Alice.new.to_s # \"i'm alice\"\n"
  },
  {
    "path": "rb_test/class_method.rb",
    "content": "class Foo\n  def self.bar\n    puts \"bar\"\n  end\nend\n\nFoo.bar\n"
  },
  {
    "path": "rb_test/const_def.rb",
    "content": "HELLO_CONST = \"hello const\"\nputs HELLO_CONST\n"
  },
  {
    "path": "rb_test/file.rb",
    "content": "puts __FILE__\nputs File.expand_path('../../../erruby/rb_test', __FILE__)\n"
  },
  {
    "path": "rb_test/fixnum.rb",
    "content": "puts 1.to_s\nputs 2.to_s\nputs -1.to_s\nputs 0.to_s\nputs 576460752303423488.to_s\nputs -576460752303423489.to_s\nputs (1+1).to_s\nputs (2-1).to_s\nputs (-(1)).to_s\nputs (3 % 2).to_s\nputs (4 * 2).to_s\nputs (3 / 2).to_s\nputs (4 ** 2).to_s\nputs (1 < 2).to_s\nputs (2 < 2).to_s\nputs (3 < 2).to_s\nputs (1 <= 2).to_s\nputs (2 <= 2).to_s\nputs (3 <= 2).to_s\nputs (1 > 2).to_s\nputs (2 > 2).to_s\nputs (3 > 2).to_s\nputs (1 >= 2).to_s\nputs (2 >= 2).to_s\nputs (3 >= 2).to_s\nputs (1 == 2).to_s\nputs (2 == 2).to_s\nputs (3 == 2).to_s\nputs (1 <=> 2).to_s\nputs (2 <=> 2).to_s\nputs (3 <=> 2).to_s\n"
  },
  {
    "path": "rb_test/global_var.rb",
    "content": "$Gvar = \"hello gvar\"\nputs $Gvar\n"
  },
  {
    "path": "rb_test/hello_world.rb",
    "content": "puts \"hello world\"\n"
  },
  {
    "path": "rb_test/integer_class.rb",
    "content": "puts 5.to_i.to_s\nputs 5.to_int.to_s\nputs 5.floor.to_s\nputs 5.ceil.to_s\nputs 5.truncate.to_s\nputs 5.numerator.to_s\nputs 5.ord.to_s\nputs 56.denominator.to_s\nputs 2.even?.to_s\nputs 3.even?.to_s\nputs 2.odd?.to_s\nputs 3.odd?.to_s\nputs 2.gcd(2).to_s\nputs 6.gcd(8).to_s\nputs 8.gcd(6).to_s\nputs 0.gcd(8).to_s\nputs 0.gcd(0).to_s\nputs 6.gcd(0).to_s\nputs 2.lcm(2).to_s\nputs 6.lcm(8).to_s\nputs 8.lcm(6).to_s\nputs 0.lcm(8).to_s\nputs 6.lcm(0).to_s\nputs 1.integer?.to_s\nputs 1.next.to_s\nputs (-1).next.to_s\nputs 1.succ.to_s\nputs (-1).succ.to_s\nputs \"times\"\nputs 5.times {|i| puts i.to_s}.to_s\nputs 0.times {|i| puts i.to_s}.to_s\nputs (-5).times {|i| puts i.to_s}.to_s\nputs \"upto\"\nputs (-2).upto(2){|i| puts i.to_s}.to_s\nputs (2).upto(2){|i| puts i.to_s}.to_s\nputs (3).upto(2){|i| puts i.to_s}.to_s\nputs \"downto\"\nputs (2).downto(-2){|i| puts i.to_s}.to_s\nputs (2).downto(2){|i| puts i.to_s}.to_s\nputs (5).downto(6){|i| puts i.to_s}.to_s\nputs \"abs\"\nputs (2).abs.to_s\nputs (-2).abs.to_s\nputs \"magnitude\"\nputs (2).magnitude.to_s\nputs (-2).magnitude.to_s\n"
  },
  {
    "path": "rb_test/ivar.rb",
    "content": "class Foo\n  def setup\n    @ivar = \"hello\"\n  end\n  def ivar\n    @ivar\n  end\nend\nfoo = Foo.new\nfoo.setup\nputs foo.ivar\n"
  },
  {
    "path": "rb_test/load.rb",
    "content": "load 'rb_test/_load.rb'\nload 'rb_test/_load.rb'\n"
  },
  {
    "path": "rb_test/method_def.rb",
    "content": "str = \"not this\"\ndef hello_world_method_0\n  \"hello world no arg\"\nend\n\ndef hello_world_method_1(name)\n  str = name\n  str\nend\n\ndef hello_world_method_block(n)\n  puts n\n  puts yield\nend\n\ndef yield_with_arg(s,x)\n  yield s,x\nend\n\nyield_with_arg(\"yield with\",\"arg\") do |ss, xx|\n  puts ss # \"yield with\"\n  puts xx # \"arg\"\nend\n\nputs hello_world_method_0\nputs hello_world_method_1(\"my name is erruby\")\nhello_world_method_block(\"arg\") { \"hello block\" }\n\n#hello_world_method_block(4){\"hello\"}.block1{\"this\"}.block2{\"world\"}\n"
  },
  {
    "path": "rb_test/nested_block.rb",
    "content": "3.times do |i|\n  puts i.to_s\n  4.times do |j|\n    puts j.to_s\n  end\nend\n"
  },
  {
    "path": "rb_test/nested_const.rb",
    "content": "class Foo\n  Bar = \"hello nested\"\n  class Alice\n  end\nend\nBob = \"outside bob\"\nputs \"Foo::Bar\"\nputs Foo::Bar\nFoo::Alice::Bob = \"inside Bob\"\nputs Foo::Alice::Bob\nputs Bob\n"
  },
  {
    "path": "rb_test/nil_class.rb",
    "content": "nil\n\nnil & nil\nnil & true\nnil & false\nnil & \"\"\n\nnil ^ nil\nnil ^ true\nnil ^ false\nnil ^ \"\"\n\nnil | nil\nnil | true\nnil | false\nnil | \"\"\n\nnil.inspect\n\nnil.nil?\n\nnil.to_s\n"
  },
  {
    "path": "rb_test/require_relative.rb",
    "content": "require_relative \"_require_relative\"\nrequire_relative \"_require_relative\"\n"
  },
  {
    "path": "rb_test/sysrb_out/.gitkeep",
    "content": ""
  },
  {
    "path": "rb_test/sysrb_out/and_dot.out",
    "content": "1\n"
  },
  {
    "path": "rb_test/sysrb_out/array_class.out",
    "content": "1\n2\nmul\n1\n2\n3\n1\n2\n3\n1\n2\n3\nb\na\nc\nfalse\ntrue\n3\n3\nd\nf\nd\nf\na\na\nb\n4\n2\n5\n0\n0\n"
  },
  {
    "path": "rb_test/sysrb_out/boolean_class.out",
    "content": "true\nfalse\nfalse\ntrue\nfalse\ntrue\nfalse\ntrue\ntrue\nfalse\nfalse\nfalse\nfalse\ntrue\ntrue\nfalse\ntrue\ntrue\ntrue\nfalse\ntrue\nfalse\ntrue\nfalse\n"
  },
  {
    "path": "rb_test/sysrb_out/class_cross_call.out",
    "content": "hello\n"
  },
  {
    "path": "rb_test/sysrb_out/class_def.out",
    "content": "hello world\nhello bar\n"
  },
  {
    "path": "rb_test/sysrb_out/class_inherent.out",
    "content": "foo\nfoo\ni'm alice\n"
  },
  {
    "path": "rb_test/sysrb_out/class_method.out",
    "content": "bar\n"
  },
  {
    "path": "rb_test/sysrb_out/const_def.out",
    "content": "hello const\n"
  },
  {
    "path": "rb_test/sysrb_out/file.out",
    "content": "rb_test/file.rb\n/Users/johnlinvc/Projs/erruby/erruby/rb_test\n"
  },
  {
    "path": "rb_test/sysrb_out/fixnum.out",
    "content": "1\n2\n-1\n0\n576460752303423488\n-576460752303423489\n2\n1\n-1\n1\n8\n1\n16\ntrue\nfalse\nfalse\ntrue\ntrue\nfalse\nfalse\nfalse\ntrue\nfalse\ntrue\ntrue\nfalse\ntrue\nfalse\n-1\n0\n1\n"
  },
  {
    "path": "rb_test/sysrb_out/global_var.out",
    "content": "hello gvar\n"
  },
  {
    "path": "rb_test/sysrb_out/hello_world.out",
    "content": "hello world\n"
  },
  {
    "path": "rb_test/sysrb_out/integer_class.out",
    "content": "5\n5\n5\n5\n5\n5\n5\n1\ntrue\nfalse\nfalse\ntrue\n2\n2\n2\n8\n0\n6\n2\n24\n24\n0\n0\ntrue\n2\n0\n2\n0\ntimes\n0\n1\n2\n3\n4\n5\n0\n-5\nupto\n-2\n-1\n0\n1\n2\n-2\n2\n2\n3\ndownto\n2\n1\n0\n-1\n-2\n2\n2\n2\n5\nabs\n2\n2\nmagnitude\n2\n2\n"
  },
  {
    "path": "rb_test/sysrb_out/ivar.out",
    "content": "hello\n"
  },
  {
    "path": "rb_test/sysrb_out/load.out",
    "content": "loaded\nloaded\n"
  },
  {
    "path": "rb_test/sysrb_out/method_def.out",
    "content": "yield with\narg\nhello world no arg\nmy name is erruby\narg\nhello block\n"
  },
  {
    "path": "rb_test/sysrb_out/nested_block.out",
    "content": "0\n0\n1\n2\n3\n1\n0\n1\n2\n3\n2\n0\n1\n2\n3\n"
  },
  {
    "path": "rb_test/sysrb_out/nested_const.out",
    "content": "Foo::Bar\nhello nested\ninside Bob\noutside bob\n"
  },
  {
    "path": "rb_test/sysrb_out/nil_class.out",
    "content": ""
  },
  {
    "path": "rb_test/sysrb_out/require_relative.out",
    "content": "required file\n"
  },
  {
    "path": "rb_test/sysrb_out/var_assign.out",
    "content": "hello world\n"
  },
  {
    "path": "rb_test/var_assign.rb",
    "content": "hello = \"hello world\"\nputs hello\n"
  },
  {
    "path": "rebar.config",
    "content": "{deps, [\n        {erlport, \".*\", {git, \"https://github.com/johnlinvc/erlport.git\", {branch, \"erruby\"}}, [raw]},\n        {plists, \".*\", {git, \"https://github.com/johnlinvc/plists.git\", {branch, \"master\"}}, [raw]},\n        {getopt, \"0.8.2\", {git, \"https://github.com/jcomellas/getopt.git\", {tag, \"v0.8.2\"}}}\n       ]\n}.\n{pre_hooks, [{compile, \"./pre_compile\"}]}.\n"
  },
  {
    "path": "src/erb.erl",
    "content": "-module(erb).\n-export([find_or_init_class/2]).\n\nfind_or_init_class(Name, InitFun) ->\n  case whereis(Name) of\n    undefined -> InitFun();\n    Pid -> {ok, Pid}\n  end.\n"
  },
  {
    "path": "src/erruby.app.src",
    "content": "{application, erruby,\n [\n  {description, \"\"},\n  {vsn, \"1\"},\n  {registered, []},\n  {applications, [\n                  kernel,\n                  stdlib\n                 ]},\n  {mod, { erruby_app, []}},\n  {env, []}\n ]}.\n"
  },
  {
    "path": "src/erruby.erl",
    "content": "-module(erruby).\n-include(\"rb.hrl\").\n-export([eruby/1, start_ruby/0, stop_ruby/1, parse_ast/2, main/1]).\n\nopt_spec_list() ->\n  [\n   {debug, $d, \"debug\", {integer, 0}, \"Verbose level for debugging\"},\n   {verbose, $v, \"verbose\", undefined, \"print version number and enter verbose mode\"},\n   {fast, $f, \"fast\", undefined, \"use escript instead of erl for faster bootup\"},\n   {help, $h, \"help\", undefined, \"Show this help\"}\n  ].\n\nhandle_opts({debug, DebugLevel}) ->\n  erruby_debug:set_debug_level(DebugLevel);\nhandle_opts(help ) ->\n  show_help();\nhandle_opts(verbose) ->\n  io:format(\"erruby 0.1.0~n\"),\n  erruby_debug:set_debug_level(2);\nhandle_opts(_Opts) ->\n  ok.\n\nparse_args([ArgsAtom]) when is_atom(ArgsAtom) ->\n  ArgsString = atom_to_list(ArgsAtom),\n  ArgsUntokened = string:centre(ArgsString,length(ArgsString)-2),\n  string:tokens(ArgsUntokened, \" \");\n\nparse_args(Args) ->\n  Args.\n\nmain(RawArgs) ->\n  Args = parse_args(RawArgs),\n  add_lib_path(),\n  erruby_debug:start_link(0),\n  {ok, {Opts, Extra}} = getopt(Args),\n  lists:foreach(fun handle_opts/1, Opts),\n  [SrcFileName | RubyArgs] = Extra,\n  try\n    erruby_debug:debug_2(\"input file name ~s\\n\", [SrcFileName]),\n    erruby_debug:debug_2(\"input args ~s\\n\", [RubyArgs]),\n    eruby(SrcFileName)\n  catch\n    _:known_error ->\n      erruby_debug:debug_1(\"known error ~n\", []),\n      erruby_debug:debug_1(\"~p~n\",[erlang:get_stacktrace()]);\n    _:E ->\n      io:format(\"error ~p ~n\", [E]),\n      erlang:display(erlang:get_stacktrace())\n  end.\n\neruby(SrcFileName) ->\n  {ok, Binary} = file:read_file(SrcFileName),\n  FileLines = binary:bin_to_list(Binary),\n  Ruby = start_ruby(),\n  Ast = parse_ast(Ruby, FileLines),\n  stop_ruby(Ruby),\n  erruby_vm:eval_file(Ast, SrcFileName).\n\ngetopt(Args) ->\n  getopt:parse(opt_spec_list(), Args).\n\nshow_help() ->\n  getopt:usage(opt_spec_list(), \"erruby\", \"[programfile] [arguments]\"),\n  halt(1).\n\n\ninstall_encoder(Ruby) ->\n  ruby:call(Ruby, erruby_rb_path() , 'install_encoder',[]).\n\nerruby_path() ->\n  os:getenv(\"ERRUBY_PATH\") ++ \"/ebin\".\n\nrelative_path(Path) ->\n  erruby_path() ++ Path.\n\nerruby_rb_path() ->\n  list_to_atom(relative_path(\"/../rb_src/erruby.rb\")).\n\nparse_ast(Ruby, String) ->\n  ruby:call(Ruby, erruby_rb_path(),'parse', [String]).\n\nadd_lib_path() ->\n  code:add_path(relative_path(\"/../deps/erlport/ebin\")),\n  code:add_path(relative_path(\"/../deps/getopt/ebin\")),\n  code:add_path(relative_path(\"/../deps/plists/ebin\")),\n  code:add_path(erruby_path()).\n\nstop_ruby(Ruby) ->\n  ruby:stop(Ruby).\n\nstart_ruby() ->\n  {ok, Ruby} = ruby:start(),\n  install_encoder(Ruby),\n  Ruby.\n\n"
  },
  {
    "path": "src/erruby_app.erl",
    "content": "-module(erruby_app).\n\n-behaviour(application).\n\n%% Application callbacks\n-export([start/2, stop/1]).\n\n%% ===================================================================\n%% Application callbacks\n%% ===================================================================\n\nstart(_StartType, _StartArgs) ->\n    erruby_sup:start_link().\n\nstop(_State) ->\n    ok.\n"
  },
  {
    "path": "src/erruby_array.erl",
    "content": "-module(erruby_array).\n-include(\"rb.hrl\").\n-export([install_array_classes/0, new_array/2, new_array/1]).\n-export([array_to_list/1, push/2]).\n\n%TODO find a way to define module_function\ninstall_array_classes() ->\n  {ok, ArrayClass} = erruby_class:new_class(),\n  'Array' = erruby_object:def_global_const('Array', ArrayClass),\n  erruby_object:def_method(ArrayClass, map, fun method_map/1),\n  erruby_object:def_method(ArrayClass, pmap, fun method_pmap/1),\n  erruby_object:def_method(ArrayClass, '*' , fun method_multiplication/2),\n  erruby_object:def_method(ArrayClass, '+', fun method_plus/2),\n  erruby_object:def_method(ArrayClass, 'concat', fun method_concat/2),\n  erruby_object:def_method(ArrayClass, 'at' , fun method_at/2),\n  erruby_object:def_method(ArrayClass, 'first' , fun method_first/1),\n  erruby_object:def_method(ArrayClass, 'last' , fun method_last/1),\n  erruby_object:def_method(ArrayClass, 'empty?', fun method_empty_q/1),\n  erruby_object:def_method(ArrayClass, 'length', fun method_length/1),\n  erruby_object:def_method(ArrayClass, 'size', fun method_length/1),\n  erruby_object:def_method(ArrayClass, 'push' , fun method_push/2),\n  erruby_object:def_method(ArrayClass, '<<' , fun method_push/2),\n  erruby_object:def_method(ArrayClass, 'unshift' , fun method_unshift/2),\n  erruby_object:def_method(ArrayClass, 'shift' , fun method_shift/1),\n  erruby_object:def_method(ArrayClass, 'drop' , fun method_drop/2),\n  ok.\n\ndrop_elements(_List, Count) when Count =< 0 ->\n  _List;\ndrop_elements([Head | Tail], Count) ->\n  drop_elements(Tail, Count - 1).\n\nmethod_drop(#{self := Self}=Env, IntObj) ->\n  Int = erruby_fixnum:fix_to_int(IntObj),\n  List = array_to_list(Self),\n  if\n    length(List) < Int ->\n      new_array(Env, []);\n    true ->\n      ResultList = drop_elements(List, Int),\n      new_array(Env, ResultList)\n  end.\n\nmethod_map(#{self := Self}=Env) ->\n  List = array_to_list(Self),\n  FoldFun = fun(X, EnvAcc) -> erruby_vm:yield(EnvAcc, [X]) end,\n  Envs = erruby_vm:scanl(FoldFun, Env, List),\n  Results = lists:map(fun erruby_rb:ret_val/1, Envs),\n  erruby_rb:return(Results, lists:last(Envs)).\n\nrepeat_list(_List, Count) when Count =< 0 ->\n  new_array([]);\nrepeat_list(List, 1) ->\n  List;\nrepeat_list(List, Count) ->\n  lists:append(List, repeat_list(List, Count-1)).\n\nmethod_multiplication(#{self := Self}=Env, IntObj) ->\n  Int = erruby_fixnum:fix_to_int(IntObj),\n  List = array_to_list(Self),\n  ResultList = repeat_list(List, Int),\n  new_array(Env, ResultList).\n\nmethod_plus(#{self := Self}=Env, Another) ->\n  Elements = array_to_list(Self),\n  AnotherElements = array_to_list(Another),\n  NewElements = Elements ++ AnotherElements,\n  new_array(Env, NewElements).\n\nmethod_concat(#{self := Self}=Env, Another) ->\n  Elements = array_to_list(Self),\n  AnotherElements = array_to_list(Another),\n  NewElements = Elements ++ AnotherElements,\n  Properties = erruby_object:get_properties(Self),\n  NewProperties = Properties#{ elements := NewElements},\n  erruby_object:set_properties(Self, NewProperties),\n  erruby_rb:return(Self, Env).\n\nmethod_pmap(#{self := Self}=Env) ->\n  List = array_to_list(Self),\n  MapFun = fun(X) -> erruby_vm:yield(Env, [X]) end,\n  Envs = plists:map(MapFun, List, {processes, erlang:system_info(schedulers_online)}),\n  Results = lists:map(fun erruby_rb:ret_val/1, Envs),\n  erruby_rb:return(Results, lists:last(Envs)).\n\nmethod_at(#{self := Self}=Env, IntObj) ->\n  Int = erruby_fixnum:fix_to_int(IntObj),\n  erruby_rb:return(at(Self, Int), Env).\n\nmethod_first(#{self := Self}=Env) ->\n  List = array_to_list(Self),\n  [ Head | _Tail ] = List,\n  erruby_rb:return(Head, Env).\n\nmethod_last(#{self := Self}=Env) ->\n  List = array_to_list(Self),\n  erruby_rb:return(lists:last(List), Env).\n\nmethod_empty_q(#{self := Self}=Env) ->\n  List = array_to_list(Self),\n  case  length(List) < 1 of\n    true -> erruby_boolean:new_true(Env);\n    false -> erruby_boolean:new_false(Env)\n  end.\nmethod_length(#{self := Self}=Env) ->\n  List = array_to_list(Self),\n  erruby_fixnum:new_fixnum(Env, length(List)).\n\nmethod_push(#{self := Self}=Env, Append) ->\n  push(Self, Append),\n  erruby_rb:return(Self, Env).\n\nmethod_unshift(#{self := Self}=Env, Head) ->\n  Elements = array_to_list(Self),\n  NewElements = [Head | Elements],\n  Properties = erruby_object:get_properties(Self),\n  NewProperties = Properties#{ elements := NewElements} ,\n  erruby_object:set_properties(Self, NewProperties),\n  erruby_rb:return(Self, Env).\n\nmethod_shift(#{self := Self}=Env) ->\n  Elements = array_to_list(Self),\n  [Head | Rest] = Elements,\n  Properties = erruby_object:get_properties(Self),\n  NewProperties = Properties#{ elements := Rest} ,\n  erruby_object:set_properties(Self, NewProperties),\n  erruby_rb:return(Head, Env).\n\n%TODO maybe use pid to find class\nnew_array(Env, Elements) ->\n  erruby_rb:return(new_array(Elements), Env).\n\nnew_array(Elements) ->\n  ArrayClass = erruby_object:find_global_const('Array'),\n  Properties = #{elements => Elements},\n  {ok, Array} = erruby_object:new_object(ArrayClass, Properties),\n  Array.\n\n%% @doc the Index is 0-based, not the 1-based of usual erlang\nat(Array, Index) ->\n  Properties = erruby_object:get_properties(Array),\n  #{ elements := Elements} = Properties,\n  lists:nth(Index+1, Elements).\n\npush(Array, Elem) ->\n  Elements = array_to_list(Array),\n  NewElements = Elements ++ [Elem],\n  Properties = erruby_object:get_properties(Array),\n  NewProperties = Properties#{ elements := NewElements} ,\n  erruby_object:set_properties(Array, NewProperties).\n\narray_to_list(Array) ->\n  Properties = erruby_object:get_properties(Array),\n  #{ elements := Elements} = Properties,\n  Elements.\n"
  },
  {
    "path": "src/erruby_boolean.erl",
    "content": "-module(erruby_boolean).\n-export([install_boolean_classes/0,new_true/1,new_false/1,true_instance/0,false_instance/0]).\n%TODO register the True & False class in Const\n\ninstall_boolean_classes() ->\n  {ok, TrueClass} = erruby_class:new_class(),\n  {ok, FalseClass} = erruby_class:new_class(),\n  'TrueClass' = erruby_object:def_global_const('TrueClass', TrueClass),\n  'FalseClass' = erruby_object:def_global_const('FalseClass', FalseClass),\n  install_method(TrueClass, FalseClass, '!', fun method_not/1),\n  install_method(TrueClass, FalseClass, '&', fun method_and/2),\n  install_method(TrueClass, FalseClass, '^', fun method_xor/2),\n  install_method(TrueClass, FalseClass, '|', fun method_or/2),\n  erruby_object:def_method(TrueClass, to_s, fun method_true_to_s/1),\n  erruby_object:def_method(FalseClass, to_s, fun method_false_to_s/1),\n  erruby_object:def_method(TrueClass, inspect, fun method_true_to_s/1),\n  erruby_object:def_method(FalseClass, inspect, fun method_false_to_s/1),\n  erruby_object:new_object_with_pid_symbol(erruby_boolean_true, TrueClass),\n  erruby_object:new_object_with_pid_symbol(erruby_boolean_false, FalseClass),\n  ok.\n\ninstall_method(TC, FC, Name, Func) ->\n  erruby_object:def_method(TC, Name, Func),\n  erruby_object:def_method(FC, Name, Func).\n\nnew_true(Env) -> erruby_rb:return(true_instance(), Env).\nnew_false(Env) -> erruby_rb:return(false_instance(), Env).\n\ntrue_instance() ->\n  whereis(erruby_boolean_true).\n\nfalse_instance() ->\n  whereis(erruby_boolean_false).\n\nmethod_not(#{self := Self} = Env) ->\n  True = true_instance(),\n  False = false_instance(),\n  RetVal = case Self of\n    True -> False;\n    False -> True\n  end,\n  erruby_rb:return(RetVal, Env).\n\nmethod_and(#{self := Self} = Env, Object) ->\n  Another = object_to_boolean(Object),\n  RetVal = and_op(Self, Another),\n  erruby_rb:return(RetVal, Env).\n\nobject_to_boolean(Object) ->\n  NilObject = erruby_nil:nil_instance(),\n  False = false_instance(),\n  case Object of\n    NilObject -> false_instance();\n    False -> false_instance();\n    _ -> true_instance()\n  end.\n\n\nand_op(B1,B2) ->\n  True = true_instance(),\n  False = false_instance(),\n  case B1 of\n    True -> B2;\n    False -> False\n  end.\n\nor_op(B1,B2) ->\n  True = true_instance(),\n  False = false_instance(),\n  case B1 of\n    True -> True;\n    False -> B2\n  end.\n\nnot_op(Boolean) ->\n  True = true_instance(),\n  False = false_instance(),\n  case Boolean of\n    True -> False;\n    False -> True\n  end.\n\nmethod_or(#{self := Self} = Env, Object) ->\n  Another = object_to_boolean(Object),\n  RetVal = or_op(Self, Another),\n  erruby_rb:return(RetVal, Env).\n\nmethod_xor(#{self := Self} = Env, Object) ->\n  Another = object_to_boolean(Object),\n  NotAandB = and_op(not_op(Self),Another),\n  AandNotB = and_op(Self, not_op(Another)),\n  RetVal = or_op(NotAandB, AandNotB),\n  erruby_rb:return(RetVal, Env).\n\nmethod_true_to_s(Env) ->\n  erruby_vm:new_string(\"true\", Env).\n\nmethod_false_to_s(Env) ->\n  erruby_vm:new_string(\"false\",Env).\n"
  },
  {
    "path": "src/erruby_class.erl",
    "content": "-module(erruby_class).\n-export([new_class/0, new_named_class/1, new_class/1, install_class_class_methods/0, init_class_class/0, class_name/1]).\n\n%TODO return self when calling method_class on self\n%TODO add name parameter\n%TODO should call initialize method when new\n\nnew_class() ->\n  new_named_class(undefined).\n\nnew_named_class(Name) ->\n  Properties = #{name => Name},\n  erruby_object:start_link(class_class(), Properties).\n\nclass_name(Self) ->\n  Properties = erruby_object:get_properties(Self),\n  #{name := Name} = Properties,\n  Name.\n\nnew_class(SuperClass) ->\n  Properties = #{superclass => SuperClass},\n  erruby_object:start_link(class_class(), Properties).\n\ninstall_class_class_methods() ->\n  erruby_object:def_method(class_class(), 'new', fun method_new/1),\n  ok.\n\n%FIXME new a real class\nmethod_new(#{self := Klass}=Env) ->\n  {ok, NewObject} = erruby_object:start_link(Klass),\n  erruby_rb:return(NewObject, Env).\n\ninit_class_class() ->\n  erb:find_or_init_class(erruby_class_class, fun init_class_class_internal/0).\n\ninit_class_class_internal() ->\n  Properties = #{superclass => erruby_object:object_class()},\n  {ok, Pid} = erruby_object:new_object_with_pid_symbol(erruby_class_class, erruby_object:object_class()),\n  ok = install_class_class_methods(),\n  {ok, Pid}.\n\nclass_class() ->\n  whereis(erruby_class_class).\n\n"
  },
  {
    "path": "src/erruby_debug.erl",
    "content": "-module(erruby_debug).\n-export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2]).\n-export([start_link/1, debug/3, debug_1/2,debug_2/2, debug_tmp/2, set_debug_level/1]).\n-export([print_env/1]).\n\ninit([DebugLevel]) ->\n  {ok, #{debug_level => DebugLevel}}.\n\nterminate(_Arg, _State) -> {ok, dead}.\n\ncode_change(_OldVsn, State, _Extra) -> {ok, State}.\n\ndebug(Format, Args, Level) ->\n  FullFormat = lists:concat([\"debug level \", Level, \": \", Format]),\n  gen_server:call(erruby_debug_pid, #{level => Level, format => FullFormat, args => Args}).\n\ndebug_1(Format, Args) ->\n  debug(Format, Args, 1).\n\ndebug_2(Format, Args) ->\n  debug(Format, Args, 2).\n\ndebug_tmp(Format, Args) ->\n  io:format(Format, Args).\n\nset_debug_level(Level) ->\n  gen_server:cast(erruby_debug_pid, #{new_level => Level}).\n\nstart_link(DebugLevel) -> gen_server:start_link({local, erruby_debug_pid} ,?MODULE, [DebugLevel], []).\n\nhandle_info(Info, State) ->\n  io:format(\"Got unkwon info:~n~p~n\", [Info]),\n  {ok, State}.\n\nhandle_call(#{level := Level, format := Format, args := Args}, _From, #{debug_level := DebugLevel} = State) when DebugLevel >= Level ->\n  io:format(Format, Args),\n  {reply, ok, State};\n\nhandle_call(#{level := Level}, _From, #{debug_level := DebugLevel} = State) when DebugLevel < Level ->\n  {reply, ok, State};\n\nhandle_call(_Req, _From, State) ->\n  io:format(\"handle unknow call ~p ~p ~p ~n\",[_Req, _From, State]),\n  NewState = State,\n  {reply, done, NewState}.\n\nhandle_cast(#{new_level := NewLevel}, State) ->\n  {noreply, State#{ debug_level => NewLevel } };\n\nhandle_cast(_Req, State) ->\n  io:format(\"handle unknown cast ~p ~p ~n\",[_Req, State]),\n  NewState = State,\n  {noreply, NewState}.\n\nprint_env(Env) ->\n  debug_1(\"Env: ~p ~n\",[Env]).\n"
  },
  {
    "path": "src/erruby_fixnum.erl",
    "content": "-module(erruby_fixnum).\n-export([install_fixnum_class/0, new_fixnum/2, fix_to_int/1]).\n\n%%\n%% @TODO inherent from integer & numeric\ninstall_fixnum_class() ->\n  IntegerClass = erruby_object:find_global_const('Integer'),\n  {ok, FixnumClass} = erruby_class:new_class(IntegerClass),\n  'Fixnum' = erruby_object:def_global_const('Fixnum', FixnumClass),\n  erruby_object:def_method(FixnumClass, to_s, fun method_to_s/1),\n  erruby_object:def_method(FixnumClass, '-@', fun method_neg/1),\n  erruby_object:def_method(FixnumClass, '+', fun method_add/2),\n  erruby_object:def_method(FixnumClass, '-', fun method_minus/2),\n  erruby_object:def_method(FixnumClass, '*', fun method_multiplication/2),\n  erruby_object:def_method(FixnumClass, '**', fun method_power/2),\n  erruby_object:def_method(FixnumClass, '<', fun method_less/2),\n  erruby_object:def_method(FixnumClass, '>', fun method_greater/2),\n  erruby_object:def_method(FixnumClass, '<=', fun method_less_equal/2),\n  erruby_object:def_method(FixnumClass, '>=', fun method_greater_equal/2),\n  erruby_object:def_method(FixnumClass, '==', fun method_equal/2),\n  erruby_object:def_method(FixnumClass, '<=>', fun method_cmp/2),\n  erruby_object:def_method(FixnumClass, '/', fun method_division/2),\n  erruby_object:def_method(FixnumClass, '%', fun method_module/2),\n  ok.\n\nfixnum_class() ->\n  erruby_object:find_global_const('Fixnum').\n\nnew_fixnum(Env, N) ->\n  {ok, Obj} = erruby_object:new_object(fixnum_class(), #{val => N}),\n  erruby_rb:return(Obj, Env).\n\nfix_to_int(Fixnum) ->\n  get_val(Fixnum).\n\nget_val(Fixnum) ->\n  #{val := Val} = erruby_object:get_properties(Fixnum),\n  Val.\n\nbinary_op(#{self := Self}=Env, AnotherFixnum, Fun) ->\n  Val = Fun(get_val(Self), get_val(AnotherFixnum)),\n  new_fixnum(Env, Val).\n\n%% @TODO use new_string instead\nmethod_to_s(#{self := Self}=Env) ->\n  Val = get_val(Self),\n  erruby_rb:return(integer_to_list(Val), Env).\n\n%% TODO handle case where the other is not Fixnum\nmethod_add(Env, AnotherFixnum) ->\n  binary_op(Env, AnotherFixnum, fun (A,B) -> A+B end).\n\nmethod_minus(Env, AnotherFixnum) ->\n  binary_op(Env, AnotherFixnum, fun (A,B) -> A-B end).\n\nmethod_multiplication(Env, AnotherFixnum) ->\n  binary_op(Env, AnotherFixnum, fun (A,B) -> A*B end).\n\nmethod_division(Env, AnotherFixnum) ->\n  binary_op(Env, AnotherFixnum, fun (A,B) -> trunc(A/B) end).\n\nmethod_power(Env, AnotherFixnum) ->\n  binary_op(Env, AnotherFixnum, fun (A,B) -> trunc(math:pow(A,B)) end).\n\nmethod_neg(#{self := Self}=Env) ->\n  Val = - get_val(Self),\n  new_fixnum(Env, Val).\n\nmethod_module(Env, AnotherFixnum) ->\n  binary_op(Env, AnotherFixnum, fun (A,B) -> A rem B end).\n\nbinary_cmp(#{self := Self}=Env, AnotherFixnum, Fun) ->\n  Val = get_val(Self),\n  AnotherVal = get_val(AnotherFixnum),\n  case Fun(Val,AnotherVal) of\n    true -> erruby_boolean:new_true(Env);\n    false -> erruby_boolean:new_false(Env)\n  end.\n\n%%TODO move these to the comparator module\nmethod_less(Env, AnotherFixnum) ->\n  binary_cmp(Env, AnotherFixnum, fun (X,Y) -> X < Y end).\n\nmethod_less_equal(Env, AnotherFixnum) ->\n  binary_cmp(Env, AnotherFixnum, fun (X,Y) -> X =< Y end).\n\nmethod_greater(Env, AnotherFixnum) ->\n  binary_cmp(Env, AnotherFixnum, fun (X,Y) -> X > Y end).\n\nmethod_greater_equal(Env, AnotherFixnum) ->\n  binary_cmp(Env, AnotherFixnum, fun (X,Y) -> X >= Y end).\n\nmethod_equal(Env, AnotherFixnum) ->\n  binary_cmp(Env, AnotherFixnum, fun (X,Y) -> X =:= Y end).\n\n\ncmp_helper(X,Y) when X < Y ->\n  -1;\ncmp_helper(X,Y) when X =:= Y ->\n  0;\ncmp_helper(X,Y) when X > Y ->\n  1.\n\nmethod_cmp(Env, AnotherFixnum) ->\n  binary_op(Env, AnotherFixnum, fun cmp_helper/2).\n\n"
  },
  {
    "path": "src/erruby_integer.erl",
    "content": "-module(erruby_integer).\n-export([install_integer_class/0]).\n\ninstall_integer_class() ->\n  {ok, IntegerClass} = erruby_class:new_class(),\n  'Integer' = erruby_object:def_global_const('Integer', IntegerClass),\n  erruby_object:def_method(IntegerClass, to_i, fun method_to_i/1),\n  erruby_object:def_method(IntegerClass, to_int, fun method_to_i/1),\n  erruby_object:def_method(IntegerClass, floor, fun method_to_i/1),\n  erruby_object:def_method(IntegerClass, ceil, fun method_to_i/1),\n  erruby_object:def_method(IntegerClass, truncate, fun method_to_i/1),\n  erruby_object:def_method(IntegerClass, numerator, fun method_to_i/1),\n  erruby_object:def_method(IntegerClass, ord, fun method_to_i/1),\n  erruby_object:def_method(IntegerClass, denominator, fun method_denominator/1),\n  erruby_object:def_method(IntegerClass, 'even?', fun method_even_q/1),\n  erruby_object:def_method(IntegerClass, 'odd?', fun method_odd_q/1),\n  erruby_object:def_method(IntegerClass, 'gcd', fun method_gcd/2),\n  erruby_object:def_method(IntegerClass, 'lcm', fun method_lcm/2),\n  erruby_object:def_method(IntegerClass, 'integer?', fun method_integer_q/1),\n  erruby_object:def_method(IntegerClass, 'succ', fun method_succ/1),\n  erruby_object:def_method(IntegerClass, 'next', fun method_succ/1),\n  erruby_object:def_method(IntegerClass, 'times', fun method_times/1),\n  erruby_object:def_method(IntegerClass, 'upto', fun method_upto/2),\n  erruby_object:def_method(IntegerClass, 'downto', fun method_downto/2),\n  erruby_object:def_method(IntegerClass, 'abs', fun method_abs/1),\n  erruby_object:def_method(IntegerClass, 'magnitude', fun method_abs/1),\n  ok.\n\nmethod_to_i(#{self := Self}=Env) -> erruby_rb:return(Self, Env).\n\nmethod_denominator(Env) ->\n  erruby_fixnum:new_fixnum(Env, 1).\n\n%%TODO handle Bignum\nmethod_even_q(#{self := Self}=Env) ->\n  Int = erruby_fixnum:fix_to_int(Self),\n  case Int rem 2 of\n    0 -> erruby_boolean:new_true(Env);\n    1 -> erruby_boolean:new_false(Env)\n  end.\nmethod_odd_q(#{self := Self}=Env) ->\n  Int = erruby_fixnum:fix_to_int(Self),\n  case Int rem 2 of\n    1 -> erruby_boolean:new_true(Env);\n    0 -> erruby_boolean:new_false(Env)\n  end.\n\n%%TODO use binary gcd algo instead\n%%TODO move this to rational\ngcd(X,Y) when X < Y -> gcd(Y,X);\ngcd(X,Y) when Y =:= 0 -> X;\ngcd(X,Y) -> gcd(Y, X rem Y).\n\nmethod_gcd(#{self := Self}=Env, AnotherInt) ->\n  X = abs(erruby_fixnum:fix_to_int(Self)),\n  Y = abs(erruby_fixnum:fix_to_int(AnotherInt)),\n  case min(X,Y) of\n    0 -> erruby_fixnum:new_fixnum(Env, max(X,Y));\n    _ -> erruby_fixnum:new_fixnum(Env, gcd(X,Y))\n  end.\n\nlcm(0,_Y) -> 0;\nlcm(_X,0) -> 0;\nlcm(X,Y) -> trunc(X / gcd(X,Y) * Y).\n\nmethod_lcm(#{self := Self}=Env, AnotherInt) ->\n  X = abs(erruby_fixnum:fix_to_int(Self)),\n  Y = abs(erruby_fixnum:fix_to_int(AnotherInt)),\n  case min(X,Y) of\n    0 -> erruby_fixnum:new_fixnum(Env, 0);\n    _ -> erruby_fixnum:new_fixnum(Env, lcm(X,Y))\n  end.\n\nmethod_integer_q(Env) ->\n  erruby_boolean:new_true(Env).\n\nmethod_succ(#{self := Self}=Env) ->\n  erruby_fixnum:new_fixnum(Env, erruby_fixnum:fix_to_int(Self)+1).\n\n\nyield_in_range(#{self := Self} = Env,Range) ->\n  FoldFun = fun (X, EnvAcc) ->\n                IntEnv = erruby_fixnum:new_fixnum(EnvAcc, X),\n                FixInt = erruby_rb:ret_val(IntEnv),\n                erruby_vm:yield(IntEnv, [FixInt]) end,\n  LastEnv = lists:foldl(FoldFun, Env, Range),\n  erruby_rb:return(Self, LastEnv).\n\ntimes_range(X) when X =< 0 -> [];\ntimes_range(X) -> lists:seq(0, X-1).\n\n%%TODO handle empty block case\n%%TODO handle Bigdecimal\nmethod_times(#{self := Self}=Env) ->\n  Int = erruby_fixnum:fix_to_int(Self),\n  Range = times_range(Int),\n  yield_in_range(Env,Range).\n\nupto_range(Start,End) when Start > End -> [];\nupto_range(Start,End) -> lists:seq(Start,End).\n\nmethod_upto(#{self := Self}=Env, AnotherInteger) ->\n  Int = erruby_fixnum:fix_to_int(Self),\n  AnotherInt = erruby_fixnum:fix_to_int(AnotherInteger),\n  Range = upto_range(Int,AnotherInt),\n  yield_in_range(Env,Range).\n\ndownto_range(Start,End) -> lists:reverse(upto_range(End,Start)).\n\nmethod_downto(#{self := Self}=Env, AnotherInteger) ->\n  Int = erruby_fixnum:fix_to_int(Self),\n  AnotherInt = erruby_fixnum:fix_to_int(AnotherInteger),\n  Range = downto_range(Int,AnotherInt),\n  yield_in_range(Env,Range).\n\nmethod_abs(#{self := Self}=Env) ->\n  erruby_fixnum:new_fixnum(Env, abs(erruby_fixnum:fix_to_int(Self))).\n"
  },
  {
    "path": "src/erruby_lib/erruby_file.erl",
    "content": "-module(erruby_file).\n-include(\"../rb.hrl\").\n-export([install_file_classes/0]).\n\ninstall_file_classes() ->\n  {ok, FileClass} = erruby_class:new_class(),\n  'File' = erruby_object:def_global_const('File', FileClass),\n  erruby_object:def_singleton_method(FileClass, 'expand_path', fun method_expand_path/3),\n  ok.\n\nmethod_expand_path(Env, Filename, RelativeDirOrFileName) ->\n  {ok, Cwd} = file:get_cwd(),\n  DirOrFileName = filename:absname_join(Cwd, RelativeDirOrFileName),\n  ExpanedPath = filename:absname_join(DirOrFileName, Filename),\n  FlattenedPath = flatten_path(ExpanedPath),\n  erruby_vm:new_string(FlattenedPath, Env).\n\nflatten_path(Path)->\n  Components = filename:split(Path),\n  FlattenedComponents = flatten_path_components(Components,[]),\n  filename:join(FlattenedComponents).\n\nflatten_path_components([], Acc) -> lists:reverse(Acc);\nflatten_path_components([\"..\"|T], [])->\n  flatten_path_components(T, []);\nflatten_path_components([\"..\"|T], [\"/\"])->\n  flatten_path_components(T, [\"/\"]);\nflatten_path_components([\"..\"|T], [_H|Acc])->\n  flatten_path_components(T, Acc);\nflatten_path_components([H|T], Acc)->\n  flatten_path_components(T, [H|Acc]).\n"
  },
  {
    "path": "src/erruby_nil.erl",
    "content": "-module(erruby_nil).\n-export([new_nil/1, install_nil_class/0, nil_instance/0]).\n\ninstall_nil_class() ->\n  {ok, NilClass} = erruby_class:new_class(),\n  'NilClass' = erruby_object:def_global_const('NilClass', NilClass),\n  erruby_object:def_method(NilClass, '&', fun method_and/2),\n  erruby_object:def_method(NilClass, '^', fun method_xor/2),\n  erruby_object:def_method(NilClass, '|', fun method_xor/2),\n  erruby_object:def_method(NilClass, inspect, fun method_inspect/1),\n  erruby_object:def_method(NilClass, 'nil?', fun 'method_nil_q'/1),\n  erruby_object:def_method(NilClass, 'to_s', fun 'method_to_s'/1),\n  erruby_object:new_object_with_pid_symbol(erruby_nil, NilClass),\n  ok.\n\nnew_nil(Env) ->\n  erruby_rb:return(nil_instance(), Env).\n\nnil_instance() -> whereis(erruby_nil).\n\nmethod_and(Env, _Obj) -> erruby_boolean:new_false(Env).\n\nmethod_xor(Env, Obj) ->\n  Nil = nil_instance(),\n  False = erruby_boolean:false_instance(),\n  case Obj of\n    Nil -> erruby_boolean:new_false(Env);\n    False -> erruby_boolean:new_false(Env);\n    _ -> erruby_boolean:new_true(Env)\n  end.\n\nmethod_inspect(Env) -> erruby_vm:new_string(\"nil\",Env).\n\nmethod_nil_q(Env) -> erruby_boolean:new_true(Env).\n\nmethod_to_s(Env) -> erruby_vm:new_string(\"\",Env).\n"
  },
  {
    "path": "src/erruby_object.erl",
    "content": "-module(erruby_object).\n-include(\"rb.hrl\").\n-behavior(gen_server).\n-export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2]).\n%for vm\n-export([def_method/4, find_instance_method/2, def_global_const/2, find_global_const/1, def_const/3, find_const/2, init_object_class/0,object_class/0]).\n-export([def_singleton_method/4, def_singleton_method/3]).\n-export([def_global_var/2, find_global_var/1]).\n%for other buildtin class\n-export([def_method/3, new_object_with_pid_symbol/2, new_object/2]).\n-export([def_ivar/3, find_ivar/2]).\n-export([init_main_object/0, main_object/0]).\n-export([start_link/2, start_link/1]).\n-export([get_properties/1, set_properties/2]).\n-export([get_class/1]).\n\ninit([#{class := Class, properties := Properties}]) ->\n  DefaultState = default_state(),\n  StateWithClass = add_class_to_state(DefaultState, Class),\n  {ok, add_property_to_state(StateWithClass, Properties)};\n\ninit([#{class := Class}]) ->\n  DefaultState = default_state(),\n  {ok, add_class_to_state(DefaultState, Class)};\n\ninit([]) ->\n  {ok, default_state()}.\n\nadd_class_to_state(State, Class) ->\n  State#{class => Class}.\n\nadd_property_to_state(State, Properties) ->\n  State#{properties => Properties}.\n\n%TODO in method_class return defalut object_class if no class is present\ndefault_state() ->\n  Methods = #{},\n  IVars = #{},\n  Consts = #{},\n  #{self => self(),\n    methods => Methods,\n    ivars => IVars,\n    properties => #{},\n    consts => Consts}.\n\n\n%TODO unify these?\nstart_link(Class) ->\n  gen_server:start_link(?MODULE, [#{class => Class }], []).\n\nstart_link(Class, Properties) ->\n  gen_server:start_link(?MODULE, [#{class => Class, properties => Properties}], []).\n\nterminate(_Arg, _State) ->\n  {ok, dead}.\n\ncode_change(_OldVsn, State, _Extra) -> {ok, State}.\n\nget_class(Self) ->\n  gen_server:call(Self, #{type => get_class}).\n\nfind_instance_method(Self, Name) ->\n  SingletonMethod = find_instance_method_in_singleton_class(Self, Name),\n  case SingletonMethod of\n    {ok, Method} -> Method;\n    {not_found, _} ->\n      find_instance_method_in_class(Self, Name)\n  end.\n\nfind_instance_method_in_singleton_class(Self, Name) ->\n  SingletonClass = singleton_class(Self),\n  case SingletonClass of\n    not_found -> {not_found, Name};\n    _ ->\n      Result = gen_server:call(SingletonClass, #{type => find_method, name => Name}),\n      case Result of\n        not_found -> {not_found, Name};\n        _ -> {ok, Result}\n      end\n  end.\n\nfind_instance_method_in_class(Self, Name) ->\n  %erruby_debug:debug_tmp(\"finding instance method ~p in ~p\",[ Name, Self]),\n  Klass = get_class(Self),\n  Result = gen_server:call(Klass, #{type => find_method, name => Name}),\n  case Result of\n    not_found -> {not_found, Name};\n    _ -> Result\n  end.\n\nfind_method(Self, Name) ->\n  gen_server:call(Self, #{type => find_method, name => Name}).\n\nself_or_object_class(Self) ->\n  MainObject = main_object(),\n  case Self of\n    MainObject -> object_class();\n    _ -> Self\n  end.\n\n\nsingleton_class(Self) ->\n  Properties = get_properties(Self),\n  maps:get(singleton_class, Properties, not_found).\n\nget_or_create_singleton_class(Self) ->\n  SingletonClass = singleton_class(Self),\n  case SingletonClass of\n    not_found ->\n      {ok, NewSingletonClass} = erruby_class:new_named_class(\"singleton class\"),\n      Properties = get_properties(Self),\n      NewProperties = Properties#{ singleton_class => NewSingletonClass },\n      set_properties(Self, NewProperties),\n      NewSingletonClass;\n    _ ->\n      SingletonClass\n  end.\n\n\ndef_method(Self, Name, Args, Body) ->\n  Receiver = self_or_object_class(Self),\n  gen_server:call(Receiver, #{type => def_method, name => Name, args => Args, body => Body}).\n\ndef_method(Self,Name,Func) when is_function(Func) ->\n  Receiver = self_or_object_class(Self),\n  gen_server:call(Receiver, #{type => def_method, name => Name, func => Func}).\n\ndef_singleton_method(Self, Name, Args, Body) ->\n  Receiver = get_or_create_singleton_class(Self),\n  gen_server:call(Receiver, #{type => def_method, name => Name, args => Args, body => Body}).\n\ndef_singleton_method(Self,Name,Func) when is_function(Func) ->\n  Receiver = get_or_create_singleton_class(Self),\n  gen_server:call(Receiver, #{type => def_method, name => Name, func => Func}).\n\n%TODO call def_const instead\ndef_global_const(Name, Value) ->\n  gen_server:call(object_class(), #{type => def_const, name => Name, value => Value}).\n\nfind_global_const(Name) ->\n  find_const(object_class(), Name).\n\n\n%TODO define on basic object instead\n%TODO ability to use custom getter/setter\ndef_global_var(Name, Value) ->\n  Msg = #{type => def_global_var, name => Name, value => Value},\n  gen_server:call(object_class(), Msg).\n\nfind_global_var(Name) ->\n  gen_server:call(object_class(), #{type => find_global_var, name => Name}).\n\ndef_const(Self, Name, Value) ->\n  Receiver = self_or_object_class(Self),\n  gen_server:call(Receiver, #{type => def_const, name => Name, value => Value}).\n\nfind_const(Self, Name) ->\n  erruby_debug:debug_2(\"finding on ~p for const:~p~n\",[Self, Name]),\n  gen_server:call(Self, #{type => find_const, name => Name}).\n\ndef_ivar(Self, Name, Value)->\n  gen_server:call(Self, #{type => def_ivar, name => Name, value => Value}).\n\nfind_ivar(Self, Name) ->\n  erruby_debug:debug_2(\"finding on ~p for ivar:~p~n\",[Self, Name]),\n  gen_server:call(Self, #{type => find_ivar, name => Name}).\n\nget_properties(Self) ->\n  gen_server:call(Self, #{type => get_properties}).\n\nset_properties(Self, Properties) ->\n  gen_server:call(Self, #{type => set_properties, properties => Properties}).\n\n\nhandle_info(Info, State) ->\n  io:format(\"Got unkwon info:~n~p~n\", [Info]),\n  {ok, State}.\n\nhandle_call(#{ type := def_method , name := Name, body := Body, args := Args}=_Msg, _From, #{methods := Methods} =State) ->\n  NewMethods = Methods#{ Name => #{ args => Args, body => Body, argc => length(Args) } },\n  NewState = State#{ methods := NewMethods},\n  {reply, Name, NewState};\n\nhandle_call(#{ type := def_method, name := Name, func := Func}=_Msg, _From, #{methods := Methods} = State) ->\n  NewMethods = Methods#{ Name => Func },\n  NewState = State#{ methods := NewMethods},\n  {reply, Name, NewState};\n\nhandle_call(#{ type := find_method, name := Name }, _From, #{methods := Methods} = State) ->\n  erruby_debug:debug_2(\"finding method:~p~n in State:~p~n\",[Name, State]),\n  case maps:is_key(Name,Methods) of\n    true ->\n      #{Name := Method} = Methods,\n      {reply, Method, State};\n    false ->\n      %TODO use error classes\n      %io:format(\"Method ~p not found~n\",[Name]),\n      erruby_debug:debug_2(\"finding in ancestors:~p~n\",[ancestors(State)]),\n      Method = find_method_in_ancestors(ancestors(State), Name),\n      {reply, Method, State}\n  end;\n\nhandle_call(#{ type := get_properties }, _From, #{properties := Properties}=State) ->\n  {reply, Properties, State};\n\nhandle_call(#{ type := set_properties, properties := Properties }, _From, State) ->\n  NewState = State#{ properties := Properties},\n  {reply, NewState, NewState};\n\nhandle_call(#{ type := def_ivar, name := Name, value := Value }, _From, #{ivars := IVars}=State) ->\n  NewIvars = IVars#{Name => Value},\n  NewState = State#{ivars := NewIvars},\n  {reply, Name, NewState};\n\nhandle_call(#{ type := find_ivar, name := Name }, _From, #{ivars := IVars}=State) ->\n  erruby_debug:debug_2(\"finding ivar:~p~nin State:~p~n\",[Name, State]),\n  Value = maps:get(Name, IVars, erruby_nil:nil_instance()),\n  {reply, Value, State};\n\nhandle_call(#{ type := def_const, name := Name, value := Value }, _From, #{consts := Consts}=State) ->\n  NewConsts = Consts#{Name => Value},\n  NewState = State#{consts := NewConsts},\n  {reply, Name, NewState};\n\nhandle_call(#{ type := find_const, name := Name }, _From, #{consts := Consts}=State) ->\n  erruby_debug:debug_2(\"finding const:~p~nin State:~p~n\",[Name, State]),\n  Value = maps:get(Name, Consts, not_found),\n  {reply, Value, State};\n\nhandle_call(#{ type := def_global_var, name := Name, value := Value}, _From,\n            #{properties := #{global_var_tbl := GVarTbl} } = State) ->\n  NewGVarTbl = GVarTbl#{ Name => Value},\n  #{properties := Properties} = State,\n  NewProperties = Properties#{ global_var_tbl := NewGVarTbl },\n  NewState = State#{ properties :=  NewProperties},\n  {reply, Name, NewState};\n\nhandle_call(#{ type := find_global_var, name := Name}, _From,\n            #{properties := #{global_var_tbl := GVarTbl} } = State) ->\n  Value = maps:get(Name, GVarTbl, not_found),\n  {reply, Value, State};\n\n\nhandle_call(#{ type := get_class}, _From, State) ->\n  Value = maps:get(class, State, object_class()),\n  {reply, Value, State};\n\n\nhandle_call(_Req, _From, State) ->\n  io:format(\"handle unknow call ~p ~n ~p ~n ~p ~n\",[_Req, _From, State]),\n  NewState = State,\n  {reply, done, NewState}.\n\nhandle_cast(_Req, State) ->\n  io:format(\"handle unknown cast ~p ~p ~n\",[_Req, State]),\n  NewState = State,\n  {reply, done, NewState}.\n\n%TODO support va args\nmethod_puts(Env, String) ->\n  io:format(\"~s~n\", [String]),\n  erruby_nil:new_nil(Env).\n\nappend_rb_extension(FileName) ->\n  case filename:extension(FileName) of\n    [] -> string:concat(FileName, \".rb\");\n    _ -> FileName\n  end.\n\n%TODO extract to Kernal\n%TODO raise error if file not found\nmethod_require_relative(Env, FileName) ->\n  RelativeFileName = relativeFileName(Env, FileName),\n  RelativeFileNameWithExt = append_rb_extension(RelativeFileName),\n  LoadedFeatures = find_global_var(\"$LOADED_FEATURES\"),\n  LoadedFeaturesList = erruby_array:array_to_list(LoadedFeatures),\n  Contains = lists:member( RelativeFileNameWithExt, LoadedFeaturesList),\n  case Contains of\n    true -> erruby_boolean:new_false(Env);\n    _ ->\n      load_file(Env, RelativeFileNameWithExt),\n      erruby_array:push(LoadedFeatures, RelativeFileNameWithExt),\n      erruby_boolean:new_true(Env)\n  end.\n\nrelativeFileName(Env, FileName) ->\n  SrcFile = erruby_vm:file_name(Env),\n  SrcDir = filename:dirname(SrcFile),\n  filename:join([SrcDir, FileName]).\n\nload_file(Env, RelativeFileNameWithExt) ->\n  try\n    erruby:eruby(RelativeFileNameWithExt),\n    erruby_boolean:new_true(Env)\n  catch\n    _:_E ->\n      erruby_debug:debug_2(\"cant require_relative file ~p~n\", [RelativeFileNameWithExt]),\n      erruby_boolean:new_false(Env)\n  end.\n\n%TODO raise error if file not found\n% @TODO find a better way to get filename\nmethod_load(Env, FileName)->\n  Pwd = os:getenv(\"PWD\"),\n  RelativeFileNameWithExt = filename:join([Pwd, FileName]),\n  load_file(Env, RelativeFileNameWithExt).\n\nmethod_self(#{self := Self}=Env) ->\n  erruby_rb:return(Self, Env).\n\nmethod_inspect(#{self := Self}=Env) ->\n  S = io_lib:format(\"#<Object:~p>\",[Self]),\n  erruby_vm:new_string(S,Env).\n\nmethod_to_s(#{self := Self}=Env) ->\n  S = io_lib:format(\"~p\",[Self]),\n  erruby_vm:new_string(S,Env).\n\n%TODO support property?\nnew_object_with_pid_symbol(Symbol, Class) ->\n  gen_server:start_link({local, Symbol}, ?MODULE, [#{class => Class}], []).\n\nnew_object(Class, Payload) when is_map(Payload) ->\n  start_link(Class, Payload).\n\ninit_object_class() ->\n  erb:find_or_init_class(erruby_object_class, fun init_object_class_internal/0).\n\ninit_object_class_internal() ->\n  {ok, Pid} = gen_server:start_link({local, erruby_object_class}, ?MODULE, [],[]),\n  install_object_class_methods(),\n  'Object' = def_const(Pid, 'Object', Pid),\n  set_properties(object_class(), #{global_var_tbl => #{}}),\n  def_global_var(\"$LOADED_FEATURES\", erruby_array:new_array([])),\n  {ok, Pid}.\n\ninit_main_object() ->\n  erb:find_or_init_class(erruby_main_object, fun init_main_object_internal/0).\n\ninit_main_object_internal() ->\n  new_object_with_pid_symbol(erruby_main_object, object_class()).\n\nobject_class() ->\n  whereis(erruby_object_class).\n\nmain_object() ->\n  whereis(erruby_main_object).\n\ninstall_object_class_methods() ->\n  %TODO use this after inherent is done\n  %def_method(object_class(), '==', fun method_eq/2).\n  def_method(object_class(), 'puts', fun method_puts/2),\n  def_method(object_class(), 'self', fun method_self/1),\n  def_method(object_class(), 'inspect', fun method_inspect/1),\n  def_method(object_class(), 'to_s', fun method_to_s/1),\n  def_method(object_class(), '==', fun method_eq/2),\n  def_method(object_class(), 'require_relative', fun method_require_relative/2),\n  def_method(object_class(), 'load', fun method_load/2),\n  ok.\n\n\nmethod_eq(#{self := Self}=Env, Object) ->\n  case Object of\n    Self -> erruby_boolean:new_true(Env);\n    _ -> erruby_boolean:new_false(Env)\n  end.\n\nsuper_class(#{properties := Properties}=_State) ->\n  maps:get(superclass, Properties, object_class()).\n\n%TODO handle include & extend\nancestors(State) ->\n  SuperClass = super_class(State),\n  ObjectClass = object_class(),\n  case self() of\n    ObjectClass -> [];\n    _ -> [SuperClass, ObjectClass]\n  end.\n\nfind_method_in_ancestors([], _Name) ->\n  not_found;\n\nfind_method_in_ancestors(Ancestors, Name) ->\n  [Klass | Rest] = Ancestors,\n  Method = find_method(Klass, Name),\n  case Method of\n    not_found -> find_method_in_ancestors(Rest, Name);\n    _ -> Method\n  end.\n\n"
  },
  {
    "path": "src/erruby_rb.erl",
    "content": "%%% TODO move general runtime api to here\n-module(erruby_rb).\n-export([ret_self/1,ret_val/1,return/2]).\n\nret_self( #{self := Self} = Env ) ->\n  Env#{ret_val => Self}.\n\nret_val( #{ret_val := RetVal} ) -> RetVal.\nreturn(Value, Env) -> Env#{ret_val => Value}.\n"
  },
  {
    "path": "src/erruby_sup.erl",
    "content": "-module(erruby_sup).\n\n-behaviour(supervisor).\n\n%% API\n-export([start_link/0]).\n\n%% Supervisor callbacks\n-export([init/1]).\n\n%% Helper macro for declaring children of supervisor\n-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).\n\n%% ===================================================================\n%% API functions\n%% ===================================================================\n\nstart_link() ->\n    supervisor:start_link({local, ?MODULE}, ?MODULE, []).\n\n%% ===================================================================\n%% Supervisor callbacks\n%% ===================================================================\n\ninit([]) ->\n    {ok, { {one_for_one, 5, 10}, []} }.\n\n"
  },
  {
    "path": "src/erruby_vm.erl",
    "content": "-module(erruby_vm).\n-include(\"rb.hrl\").\n-export([eval_file/2, scanl/3]).\n-export([new_nil/1, new_string/2]).\n-export([eval_method_with_exit/5, yield/2]).\n-export([file_name/1]).\n\nprint_ast(Ast) ->\n  erruby_debug:debug_1(\"Ast: ~p ~n\",[Ast]).\n\nprint_env(Env) ->\n  erruby_debug:debug_1(\"Env: ~p ~n\",[Env]).\n\nscanl(_F, Acc, []) ->\n  [Acc];\nscanl(F, Acc0, [H | T]) ->\n  Acc = apply(F, [H, Acc0]),\n  [Acc0 | scanl(F, Acc, T)].\n\neval_file(Ast, FileName) ->\n  DefaultEnv = default_env(),\n  Env = set_filename(DefaultEnv, FileName),\n  eval_ast(Ast, Env).\n\nset_filename(Env, FileName) ->\n  Env#{'FileName' => FileName}.\n\nfile_name(Env) ->\n  case maps:find('FileName', Env) of\n    {ok, Value} -> Value;\n    error ->\n      file_name(find_prev_frame(Env))\n  end.\n\neval_ast({ast,type,'begin',children, Children}, Env) ->\n  erruby_debug:debug_2(\"eval begin~n\",[]),\n  lists:foldl(fun eval_ast/2, Env, Children);\n\neval_ast({ast, type, self, children, []}, Env) ->\n  #{ self := Self } = Env,\n  erruby_rb:return(Self, Env);\n\neval_ast({ast, type, str, children, Children}, Env) ->\n  [SBin|_T] = Children,\n  new_string(binary_to_list(SBin), Env);\n\neval_ast({ast, type, nil, children, []}, Env) ->\n  erruby_nil:new_nil(Env);\n\neval_ast({ast, type, true, children, []}, Env) ->\n  erruby_boolean:new_true(Env);\n\neval_ast({ast, type, false, children, []}, Env) ->\n  erruby_boolean:new_false(Env);\n\neval_ast({ast, type, array, children, Args}, Env) ->\n  {EvaledArgs, LastEnv} = eval_args(Args, Env),\n  erruby_array:new_array(LastEnv, EvaledArgs);\n\neval_ast({ast, type, int, children, [N]}, Env) ->\n  erruby_fixnum:new_fixnum(Env, N);\n\neval_ast({ast, type, '__FILE__', children, []}, #{'FileName' := Filename } = Env) ->\n  new_string(Filename, Env);\n\neval_ast({ast, type, csend, children, Children}, Env)->\n  erruby_debug:debug_1(\"csend~n\",[]),\n  [print_ast(Ast) || Ast <- Children],\n  [Receiver | [Msg | Args]] = Children,\n  ReceiverFrame = receiver_or_self(Receiver, Env),\n  UnresolvedTarget = erruby_rb:ret_val(ReceiverFrame),\n  Target = resolve_future(UnresolvedTarget),\n  Nil = erruby_nil:nil_instance(),\n  case Target of\n    Nil -> Nil;\n    _ ->\n      {EvaledArgs, LastEnv} = eval_args(Args, ReceiverFrame),\n      Method = erruby_object:find_instance_method(Target, Msg),\n      eval_method(Target,Method, EvaledArgs, LastEnv)\n  end;\n\neval_ast({ast, type, psend, children, Children}, Env)->\n  erruby_debug:debug_1(\"psend~n\",[]),\n  [print_ast(Ast) || Ast <- Children],\n  [Receiver | [Msg | Args]] = Children,\n  ReceiverFrame = receiver_or_self(Receiver, Env),\n  UnresolvedTarget = erruby_rb:ret_val(ReceiverFrame),\n  Target = resolve_future(UnresolvedTarget),\n  {EvaledArgs, LastEnv} = eval_args(Args, ReceiverFrame),\n  Method = erruby_object:find_instance_method(Target, Msg),\n  process_eval_method(Target,Method, EvaledArgs, LastEnv);\n\n%TODO call method using method object\neval_ast({ast,type,send, children, Children}, Env) ->\n  erruby_debug:debug_1(\"send~n\",[]),\n  [print_ast(Ast) || Ast <- Children],\n  [Receiver | [Msg | Args]] = Children,\n  ReceiverFrame = receiver_or_self(Receiver, Env),\n  UnresolvedTarget = erruby_rb:ret_val(ReceiverFrame),\n  Target = resolve_future(UnresolvedTarget),\n  {EvaledArgs, LastEnv} = eval_args(Args, ReceiverFrame),\n  Method = erruby_object:find_instance_method(Target, Msg),\n  eval_method(Target,Method, EvaledArgs, LastEnv);\n\neval_ast({ast, type, block, children, [Method | [Args | [Body]]]= _Children}, Env) ->\n  Block = #{body => Body, args => Args},\n  BlockEnv = Env#{block => Block},\n  Result = eval_ast(Method, BlockEnv),\n  Result#{block => not_exist};\n\neval_ast({ast, type, yield, children, Args}, Env) ->\n  {EvaledArgs, LastEnv} = eval_args(Args, Env),\n  yield(LastEnv, EvaledArgs);\n\n%FIXME return the value\neval_ast({ast, type, lvasgn, children, Children}, Env) ->\n  [Name, ValAst] = Children,\n  NewEnv = eval_ast(ValAst, Env),\n  RetVal = erruby_rb:ret_val(NewEnv),\n  bind_lvar(Name, RetVal, NewEnv);\n\neval_ast({ast, type, lvar, children, [Name]}, Env) ->\n  erruby_debug:debug_1(\"searching lvar ~p~n in frame~p~n\", [Name, Env]),\n  #{ lvars := #{Name := Val}} = Env,\n  erruby_rb:return(Val, Env);\n\n%FIXME return the value\neval_ast({ast, type, ivasgn, children, Children}, #{self := Self}=Env) ->\n  [Name, ValAst] = Children,\n  NewEnv = eval_ast(ValAst, Env),\n  RetVal = erruby_rb:ret_val(NewEnv),\n  erruby_object:def_ivar(Self, Name, RetVal),\n  erruby_rb:return(RetVal, Env);\n\neval_ast({ast, type, ivar, children, [Name]}, #{self := Self}=Env) ->\n  Val = erruby_object:find_ivar(Self, Name),\n  erruby_rb:return(Val, Env);\n\neval_ast({ast, type, gvasgn, children, Children}, Env) ->\n  [Name, ValAst] = Children,\n  NewEnv = eval_ast(ValAst, Env),\n  RetVal = erruby_rb:ret_val(NewEnv),\n  erruby_object:def_global_var(Name, RetVal),\n  erruby_rb:return(Name, NewEnv);\n\neval_ast({ast, type, gvar, children, [Name]}, Env) ->\n  Val = erruby_object:find_global_var(Name),\n  erruby_rb:return(Val, Env);\n\n\neval_ast({ast, type, defs, children, Children}, Env) ->\n  [ReceiverAst , Name , {ast, type, args, children, Args}, Body] = Children,\n  ReceiverEnv = eval_ast(ReceiverAst, Env),\n  Receiver = erruby_rb:ret_val(ReceiverEnv),\n  erruby_object:def_singleton_method(Receiver, Name, Args, Body),\n  new_symbol(Name, Env);\n\n\neval_ast({ast, type, def, children, Children}, Env) ->\n  [Name | [ {ast, type, args, children, Args} , Body ] ] = Children,\n  #{ self := Self } = Env,\n  erruby_object:def_method(Self, Name, Args, Body),\n  new_symbol(Name, Env);\n\n%TODO figure out the Unknown field in AST\n%TODO impl ancestors\neval_ast({ast, type, class, children,\n          [NameAst,undefined,Body] = _Children}, #{ self := Self } = Env) ->\n  {_,_,const,_,[_,Name]} = NameAst,\n  NameEnv = eval_ast(NameAst,Env),\n  ClassConst = erruby_rb:ret_val(NameEnv),\n  Class = case ClassConst of\n    not_found -> {ok, NewClass} = erruby_class:new_named_class(Name),\n           erruby_object:def_const(Self, Name, NewClass),\n           NewClass;\n      _ -> ClassConst\n    end,\n  case Body of\n    undefined -> NameEnv;\n    _ ->\n      NewFrame = new_frame(NameEnv, Class),\n      ResultFrame = eval_ast(Body,NewFrame),\n      pop_frame(ResultFrame)\n  end;\n\n%TODO refactor with the one without SupperClass\neval_ast({ast, type, class, children,\n          [NameAst,SuperClassAst,Body] = _Children}, #{ self := Self } = Env) ->\n  {_,_,const,_,[_,Name]} = NameAst,\n  NameEnv = eval_ast(NameAst,Env),\n  ClassConst = erruby_rb:ret_val(NameEnv),\n  SuperClassEnv = eval_ast(SuperClassAst,NameEnv),\n  SuperClassConst = erruby_rb:ret_val(SuperClassEnv),\n  %TODO should fail when SuperClassConst is not defined\n  Class = case ClassConst of\n            not_found -> {ok, NewClass} = erruby_class:new_class(SuperClassConst),\n                   erruby_object:def_const(Self, Name, NewClass),\n                   NewClass;\n            _ -> ClassConst\n          end,\n  case Body of\n    undefined -> SuperClassEnv;\n    _ ->\n      NewFrame = new_frame(SuperClassEnv, Class),\n      ResultFrame = eval_ast(Body,NewFrame),\n      pop_frame(ResultFrame)\n  end;\n\n\n\neval_ast({ast, type, casgn, children, [ParentConstAst, Name, ValAst] }, Env) ->\n  ParentConstEnv = parent_const_env(ParentConstAst, Env),\n  ParentConst = erruby_rb:ret_val(ParentConstEnv),\n  NewEnv = eval_ast(ValAst, ParentConstEnv),\n  Val = erruby_rb:ret_val(NewEnv),\n  erruby_object:def_const(ParentConst, Name, Val),\n  NewEnv;\n\n%TODO throw error when not_found\neval_ast({ast, type, const, children, [ParentConstAst, Name]}, Env) ->\n  ParentConstEnv = parent_const_env(ParentConstAst, Env),\n  ParentConst = erruby_rb:ret_val(ParentConstEnv),\n  LocalConst = erruby_object:find_const(ParentConst, Name),\n  Const = case LocalConst of\n            not_found -> erruby_object:find_const(erruby_object:object_class(), Name);\n            _ -> LocalConst\n          end,\n  erruby_rb:return(Const, Env);\n\n\neval_ast(Ast, Env) ->\n  erruby_debug:debug_1(\"Unhandled eval~n\",[]),\n  print_ast(Ast),\n  print_env(Env).\n\n\nprocess_eval_method(Target,Method,Args,Env) ->\n  Pid = spawn(?MODULE, eval_method_with_exit, [Target,Method,Args,Env, self()]),\n  erruby_rb:return({future, Pid}, Env).\n\nresolve_future({future, Pid}) ->\n  receive\n    {future, Pid, Result} ->\n      print_env(Result),\n      %erruby_debug:debug_tmp(\"resolve_future future ~p~n\", [Result]),\n      self() ! {future, Pid, Result},\n      erruby_rb:ret_val(Result)\n  end;\n\nresolve_future(Any) ->\n  print_env(Any),\n  %erruby_debug:debug_tmp(\"resolve_future Any ~p~n\", [Any]),\n  Any.\n\neval_method_with_exit(Target,Method,Args,Env, Sender) ->\n  try\n    Result = eval_method(Target, Method, Args, Env),\n    Respond = {future, self(),Result},\n    Sender ! Respond\n  catch\n    _:E ->\n      io:format(\"error ~p ~n\", [E]),\n      erlang:display(erlang:get_stacktrace())\n  end,\n  exit(normal).\n\neval_method(Target,Method, UnresolvedArgs, Env) when is_function(Method) ->\n  NewFrame = new_frame(Env,Target),\n  Args = lists:map(fun resolve_future/1, UnresolvedArgs),\n  MethodArgs = [NewFrame | Args],\n  ResultFrame = apply(Method, MethodArgs),\n  pop_frame(ResultFrame);\n\neval_method(Target, {not_found, Name}, _, _) ->\n  %TODO raise exception instead\n  TargetClass = erruby_object:get_class(Target),\n  TargetClassName = erruby_class:class_name(TargetClass),\n  io:format(\"Undefined Method ~s for ~p~n\", [Name, TargetClassName]),\n  exit(known_error);\n\neval_method(Target,#{body := Body, args := ArgNamesAst} = _Method, Args, Env) ->\n  NewFrame = new_frame(Env,Target),\n  ArgNames = [ArgName || {ast, type, arg, children, [ArgName]} <- ArgNamesAst],\n  NameWithArgs = lists:zip( ArgNames, Args),\n  NewFrameWithArgs = lists:foldl(fun ({Name, Arg}, EnvAcc) ->  bind_lvar(Name, Arg, EnvAcc) end, NewFrame, NameWithArgs),\n  pop_frame( eval_ast(Body, NewFrameWithArgs)).\n\n\nbind_lvar(Name, Val, #{ lvars := LVars } = Env) ->\n  Env#{ lvars := LVars#{ Name => Val }}.\n\nreceiver_or_self(undefined, Env) ->\n  #{ self := Self } = Env,\n  erruby_rb:return(Self, Env);\nreceiver_or_self(Receiver, Env) ->\n  eval_ast(Receiver,Env).\n\n\nnew_string(String, Env) ->\n  erruby_rb:return(String, Env).\n\nnew_symbol(Symbol, Env) ->\n  erruby_rb:return(Symbol, Env).\n\nnew_frame(Env, Self) ->\n  Env#{lvars => #{}, ret_val => not_exist, self => Self, prev_frame => Env}.\n\nnew_nil(Env) ->\n  erruby_nil:new_nil(Env).\n\n%TODO move to another place\nfind_prev_frame(Env) ->\n  case maps:get(prev_frame, Env, no_prev_frame) of\n    no_prev_frame -> throw(cant_find_block);\n    Frame -> Frame\n  end.\n\nfind_block(Env) ->\n  case maps:get(block, Env) of\n    not_exist -> find_block(find_prev_frame(Env));\n    Block -> Block\n  end.\n\nparent_const_env(ParentConstAst, Env) ->\n  case ParentConstAst of\n    undefined -> erruby_rb:ret_self(Env);\n    {ast, type, const, children, _} -> eval_ast(ParentConstAst, Env)\n  end.\n\neval_args(ArgAsts, Env) ->\n  [_ |Envs] = scanl(fun eval_ast/2, Env, ArgAsts),\n  EvaledArgs = lists:map( fun erruby_rb:ret_val/1, Envs),\n  LastEnv = case Envs of\n              [] -> Env;\n              _ -> lists:last(Envs)\n            end,\n  {EvaledArgs, LastEnv}.\n\n\nyield(Env, Args)->\n  Block = find_block(Env),\n  #{body := Body, args := {ast, type, args, children, ArgNamesAst}} = Block,\n  ArgNames = [ArgName || {ast, type, arg, children, [ArgName]} <- ArgNamesAst],\n  NameWithArgs = lists:zip( ArgNames, Args),\n  NewFrameWithArgs = lists:foldl(fun ({Name, Arg}, EnvAcc) ->  bind_lvar(Name, Arg, EnvAcc) end, Env, NameWithArgs),\n  Result = eval_ast(Body,NewFrameWithArgs),\n  Result.\n\npop_frame(Frame) ->\n  #{ret_val := RetVal, prev_frame := PrevFrame} = Frame,\n  erruby_rb:return(RetVal, PrevFrame).\n\ndefault_env() ->\n  {ok, _ObjectClass} = erruby_object:init_object_class(),\n  {ok, _ClassClass} = erruby_class:init_class_class(),\n  {ok, MainObject} = erruby_object:init_main_object(),\n  init_builtin_class(),\n  #{self => MainObject, lvars => #{}}.\n\ninit_builtin_class() ->\n  ok = erruby_nil:install_nil_class(),\n  ok = erruby_array:install_array_classes(),\n  ok = erruby_integer:install_integer_class(),\n  ok = erruby_fixnum:install_fixnum_class(),\n  ok = erruby_boolean:install_boolean_classes(),\n  ok = erruby_file:install_file_classes(),\n  ok.\n"
  },
  {
    "path": "src/rb.hrl",
    "content": "-define(RB_DEBUG_T(T),erruby_debug:debug_tmp(\"~s:~p~n\",[??T,T])).\n"
  },
  {
    "path": "test.rb",
    "content": "#!/usr/bin/env ruby\nrequire 'optparse'\n\noptions = {}\n\nOptionParser.new do |opts|\n  opts.banner = \"Usage: test.rb [options] test_case.rb\"\n  opts.on(\"-v\", \"--[no-]verbose\", \"Run verbosely\") do |v|\n    options[:verbose] = v\n  end\n  opts.on(\"-h\", \"--help\", \"Print this help\") do\n    puts(opts)\n    exit 1\n  end\nend.parse!\n\nverbose = options[:verbose]\n\ndef run_single_mri_test(fn, verbose:false)\n  basename = File.basename(fn,'.rb')\n  outname = \"rb_test/sysrb_out/#{basename}.out\"\n  puts \"testing #{fn}\" if verbose\n  system(\"ruby #{fn} > #{outname}\")\n  system(\"./erruby #{fn} | diff #{outname} -\")\nend\n\ndef run_mri_tests(verbose:)\n  fail_case = []\n  if ARGV[0] && File.exist?(ARGV[0])\n    fn = ARGV[0]\n    basename = File.basename(fn,'.rb')\n    test_result = run_single_mri_test(fn, verbose: verbose)\n    fail_case << fn unless test_result\n  else\n    Dir.glob(\"rb_test/*.rb\") do |fn|\n      basename = File.basename(fn,'.rb')\n      next if basename.start_with?(\"_\")\n      unless run_single_mri_test(fn, verbose: verbose)\n        fail_case << fn\n      end\n    end\n  end\n  fail_case\nend\n\nfail_case = run_mri_tests(verbose: verbose)\n\nif fail_case.empty?\n  puts \"everything pass\"\n  exit 0\nelse\n  fail_case.each do |fn|\n      puts \"test #{fn} failed\"\n  end\n  exit 1\nend\n"
  }
]