Repository: johnlinvc/erruby
Branch: develop
Commit: 60df66495a01
Files: 77
Total size: 63.1 KB
Directory structure:
gitextract_mz0uvchy/
├── .gitignore
├── .gitmodules
├── .travis.yml
├── COPYING.txt
├── Gemfile
├── Guardfile
├── README.md
├── TODO.md
├── erruby
├── erruby_test/
│ ├── pipe_dot.rb
│ └── undefined_method.rb
├── pre_compile
├── rb_src/
│ └── erruby.rb
├── rb_test/
│ ├── _load.rb
│ ├── _require_relative.rb
│ ├── and_dot.rb
│ ├── array_class.rb
│ ├── boolean_class.rb
│ ├── class_cross_call.rb
│ ├── class_def.rb
│ ├── class_inherent.rb
│ ├── class_method.rb
│ ├── const_def.rb
│ ├── file.rb
│ ├── fixnum.rb
│ ├── global_var.rb
│ ├── hello_world.rb
│ ├── integer_class.rb
│ ├── ivar.rb
│ ├── load.rb
│ ├── method_def.rb
│ ├── nested_block.rb
│ ├── nested_const.rb
│ ├── nil_class.rb
│ ├── require_relative.rb
│ ├── sysrb_out/
│ │ ├── .gitkeep
│ │ ├── and_dot.out
│ │ ├── array_class.out
│ │ ├── boolean_class.out
│ │ ├── class_cross_call.out
│ │ ├── class_def.out
│ │ ├── class_inherent.out
│ │ ├── class_method.out
│ │ ├── const_def.out
│ │ ├── file.out
│ │ ├── fixnum.out
│ │ ├── global_var.out
│ │ ├── hello_world.out
│ │ ├── integer_class.out
│ │ ├── ivar.out
│ │ ├── load.out
│ │ ├── method_def.out
│ │ ├── nested_block.out
│ │ ├── nested_const.out
│ │ ├── nil_class.out
│ │ ├── require_relative.out
│ │ └── var_assign.out
│ └── var_assign.rb
├── rebar.config
├── src/
│ ├── erb.erl
│ ├── erruby.app.src
│ ├── erruby.erl
│ ├── erruby_app.erl
│ ├── erruby_array.erl
│ ├── erruby_boolean.erl
│ ├── erruby_class.erl
│ ├── erruby_debug.erl
│ ├── erruby_fixnum.erl
│ ├── erruby_integer.erl
│ ├── erruby_lib/
│ │ └── erruby_file.erl
│ ├── erruby_nil.erl
│ ├── erruby_object.erl
│ ├── erruby_rb.erl
│ ├── erruby_sup.erl
│ ├── erruby_vm.erl
│ └── rb.hrl
└── test.rb
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.eunit
deps
*.o
*.beam
*.plt
.*-*
erl_crash.dump
ebin
rel/example_project
.concrete/DEV_MODE
.rebar
rb_test/*.out
================================================
FILE: .gitmodules
================================================
[submodule "ext/ruby/spec"]
path = ext/ruby/spec
url = https://github.com/ruby/spec.git
[submodule "ext/ruby/mspec"]
path = ext/ruby/mspec
url = https://github.com/ruby/mspec.git
================================================
FILE: .travis.yml
================================================
language: erlang
otp_release:
- 18.0
- 18.1
before_script:
- rvm install 2.3.1
- rvm use 2.3.1
- bundle install
script:
- rebar compile && ./test.rb
================================================
FILE: COPYING.txt
================================================
The MIT License (MIT)
Copyright (c) 2015 Yu Hsiang Lin.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Gemfile
================================================
ruby "2.3.1"
source "https://rubygems.org"
gem 'erlport-ast_mapping', github: "johnlinvc/erlport-ast_mapping", branch: "ruby_2_3"
gem 'guard'
gem 'guard-shell'
gem 'guard-rebar', github: "johnlinvc/guard-rebar", branch: "feature/update_to_guard_2_13"
================================================
FILE: Guardfile
================================================
# A sample Guardfile
# More info at https://github.com/guard/guard#readme
## Uncomment and set this to only include directories you want to watch
# directories %w(app lib config test spec features) \
# .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
## Note: if you are using the `directories` clause above and you are not
## watching the project directory ('.'), then you will want to move
## the Guardfile to a watched dir and symlink it back, e.g.
#
# $ mkdir config
# $ mv Guardfile config/
# $ ln -s config/Guardfile .
#
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
guard 'rebar-compile', all_on_start: true do
watch(%r{src/.*\.erl})
watch(%r{test/.*\.erl})
end
guard :shell do
watch(/rb_test\/(.*)\.rb/) {|m| `./test.rb -v #{m[0]}` }
end
================================================
FILE: README.md
================================================
# ErRuby - an implementation of the Ruby language on Erlang
[](https://travis-ci.org/johnlinvc/erruby)
## About
ErRuby is an implementation of the Ruby language using Erlang.
It aims to bring some concurrency features to ruby by experimenting.
It's still a work in progress. So use it at your own risk.
## Install
### Prerequisites
- erlang vm
- rebar2
- ruby (2.3.1)
To install erlang & rebar on OS X, using homebrew
brew install erlang rebar
### Building
After getting the source of ErRuby, get the gems for parser with bundler using:
bundle install
Then get the deps of erlang modules by using:
rebar get-deps
Last, compile ErRuby with:
rebar compile
Test the build result with:
./test.rb
It should output `everything pass`
## Goals
- Concurrent Features.
- Run mspec.
- GC.
- Friendly installation with rvm/rbenv
## Supported features
Currently it support some of the basic ruby constructs.
Supported features:
- `method` definition & calling.
- singleton methods, class methods.
- `class` and inheritance.
- `block` and `yield`.
- Constants.
- Local variables.
- Instance variables.
- `load` & `require_relative`.
- `Boolean` & `Integer` with basic methods.
- `String` literal.
- `Array` literal.
Unsupported core features
- class initializer, class instance variables.
- `module` definition, `include`, `extend`.
- variadic argument in function.
- keyword argument in function.
- GC.
### Class & inherentance
```ruby
class Foo
def to_s
"foo"
end
end
class Bar < Foo
end
class Alice < Bar
def to_s
"i'm alice"
end
def self.name
"Alice"
end
end
puts Foo.new.to_s # "foo"
puts Bar.new.to_s # "foo"
puts Alice.new.to_s # "i'm alice"
puts Alice.name # "Alice"
```
### block
```ruby
def yield_with_arg(s,x)
yield s,x
end
yield_with_arg("yield with","arg") do |ss, xx|
puts ss # "yield with"
puts xx # "arg"
end
3.times do |i|
puts i.to_s
4.times do |j|
puts j.to_s
end
end
([1,2,3]*1000).pmap do |x|
x+1
end
```
## License
ErRuby is licensed to you under MIT license. See the [COPYING.txt](COPYING.txt) file for more details.
================================================
FILE: TODO.md
================================================
restructure to standard erlang style
design the object system of ruby in erlang
- everything is object, just like ruby
- every object is a process. message invoke with message passing using the actor model.
use otp to run the code
================================================
FILE: erruby
================================================
#!/bin/bash
pushd `dirname $0` > /dev/null
export ERRUBY_PATH=`pwd`
popd > /dev/null
if [[ $* == *"-f"* ]]
then
escript "${ERRUBY_PATH}/ebin/erruby.beam" $*
else
erl -noshell +P 134217727 -pa ebin -s erruby main "{$*}" -s init stop
fi
================================================
FILE: erruby_test/pipe_dot.rb
================================================
def cal
1 + 1
end
x = self|.cal
puts(x.to_s)
puts(x.to_s)
puts(1|.to_s)
1|.to_s
def str
"hello future"
end
puts(self|.str)
================================================
FILE: erruby_test/undefined_method.rb
================================================
class Foo
end
f = Foo.new
f.bar
================================================
FILE: pre_compile
================================================
#!/bin/bash
pushd ./deps/erlport > /dev/null
make > /dev/null
popd > /dev/null
pushd ./deps/plists > /dev/null
./make.sh
popd > /dev/null
================================================
FILE: rb_src/erruby.rb
================================================
#!/usr/bin/env ruby
require 'rubygems'
require 'bundler/setup'
require 'erlport/ast_mapping'
def parse(src)
ErlPort::AstMapping.parse(src)
end
def install_encoder
ErlPort::AstMapping.install_encoder
end
if __FILE__ == $0
p parse("[1,2,3]".each_char.map(&:ord))
end
================================================
FILE: rb_test/_load.rb
================================================
puts "loaded"
================================================
FILE: rb_test/_require_relative.rb
================================================
puts "required file"
================================================
FILE: rb_test/and_dot.rb
================================================
puts(1&.to_s)
nil&.not_exist_method
================================================
FILE: rb_test/array_class.rb
================================================
[true, false]
[nil, nil]
["a","b","c"]
[1,2].map{|x| puts(x.to_s)}
puts "mul"
([1,2,3] * 3).map{|x| puts x.to_s}
#a = 0
#[1,2,3].map do |x|
#a = a + x
#end
#puts a
puts ["a","b","c"].at(1)
puts ["a","b","c"].first
puts ["a","b","c"].last
puts [false].empty?.to_s
puts [].empty?.to_s
puts [1, 2, 3].length.to_s
puts [1, 2, 3].size.to_s
ary = ["a","b"]
ary.concat ["c", "d"]
puts ary.last
ary2 = ary + ["e", "f"]
puts ary2.last
ary = ["a","b","c"]
ary.push "d"
puts ary.last
ary << "e" << "f"
puts ary.last
array = ["b", "c"]
array.unshift("a")
puts array.first
puts array.shift
puts array.first
puts [1, 2, 3, 4, 5].drop(1).length.to_s
puts [1, 2, 3, 4, 5].drop(3).length.to_s
puts [1, 2, 3, 4, 5].drop(0).length.to_s
puts [1, 2, 3, 4, 5].drop(5).length.to_s
puts [1, 2, 3, 4, 5].drop(10).length.to_s
================================================
FILE: rb_test/boolean_class.rb
================================================
puts (true).to_s
puts (false).to_s
puts (!true).to_s
puts (!false).to_s
puts (true == false).to_s
puts (true == true).to_s
puts (false == true).to_s
puts (false == false).to_s
puts (true & true).to_s
puts (true & false).to_s
puts (false & true).to_s
puts (false & false).to_s
puts (true ^ true).to_s
puts (true ^ false).to_s
puts (false ^ true).to_s
puts (false ^ false).to_s
puts (true | true).to_s
puts (true | false).to_s
puts (false | true).to_s
puts (false | false).to_s
puts true.to_s
puts false.to_s
puts true.inspect
puts false.inspect
================================================
FILE: rb_test/class_cross_call.rb
================================================
class Foo
def msg
"hello"
end
end
class Bar
def say
puts Foo.new.msg
end
end
Bar.new.say
================================================
FILE: rb_test/class_def.rb
================================================
class Foo
def message
"hello world"
end
def hello
puts message
end
end
class Bar
def message
"hello bar"
end
def hello
puts message
end
end
f = Foo.new
f.hello
ff = Foo.new
Bar.new.hello
================================================
FILE: rb_test/class_inherent.rb
================================================
class Foo
def to_s
"foo"
end
end
class Bar < Foo
end
class Alice < Bar
def to_s
"i'm alice"
end
end
puts Foo.new.to_s # "foo"
puts Bar.new.to_s # "foo"
puts Alice.new.to_s # "i'm alice"
================================================
FILE: rb_test/class_method.rb
================================================
class Foo
def self.bar
puts "bar"
end
end
Foo.bar
================================================
FILE: rb_test/const_def.rb
================================================
HELLO_CONST = "hello const"
puts HELLO_CONST
================================================
FILE: rb_test/file.rb
================================================
puts __FILE__
puts File.expand_path('../../../erruby/rb_test', __FILE__)
================================================
FILE: rb_test/fixnum.rb
================================================
puts 1.to_s
puts 2.to_s
puts -1.to_s
puts 0.to_s
puts 576460752303423488.to_s
puts -576460752303423489.to_s
puts (1+1).to_s
puts (2-1).to_s
puts (-(1)).to_s
puts (3 % 2).to_s
puts (4 * 2).to_s
puts (3 / 2).to_s
puts (4 ** 2).to_s
puts (1 < 2).to_s
puts (2 < 2).to_s
puts (3 < 2).to_s
puts (1 <= 2).to_s
puts (2 <= 2).to_s
puts (3 <= 2).to_s
puts (1 > 2).to_s
puts (2 > 2).to_s
puts (3 > 2).to_s
puts (1 >= 2).to_s
puts (2 >= 2).to_s
puts (3 >= 2).to_s
puts (1 == 2).to_s
puts (2 == 2).to_s
puts (3 == 2).to_s
puts (1 <=> 2).to_s
puts (2 <=> 2).to_s
puts (3 <=> 2).to_s
================================================
FILE: rb_test/global_var.rb
================================================
$Gvar = "hello gvar"
puts $Gvar
================================================
FILE: rb_test/hello_world.rb
================================================
puts "hello world"
================================================
FILE: rb_test/integer_class.rb
================================================
puts 5.to_i.to_s
puts 5.to_int.to_s
puts 5.floor.to_s
puts 5.ceil.to_s
puts 5.truncate.to_s
puts 5.numerator.to_s
puts 5.ord.to_s
puts 56.denominator.to_s
puts 2.even?.to_s
puts 3.even?.to_s
puts 2.odd?.to_s
puts 3.odd?.to_s
puts 2.gcd(2).to_s
puts 6.gcd(8).to_s
puts 8.gcd(6).to_s
puts 0.gcd(8).to_s
puts 0.gcd(0).to_s
puts 6.gcd(0).to_s
puts 2.lcm(2).to_s
puts 6.lcm(8).to_s
puts 8.lcm(6).to_s
puts 0.lcm(8).to_s
puts 6.lcm(0).to_s
puts 1.integer?.to_s
puts 1.next.to_s
puts (-1).next.to_s
puts 1.succ.to_s
puts (-1).succ.to_s
puts "times"
puts 5.times {|i| puts i.to_s}.to_s
puts 0.times {|i| puts i.to_s}.to_s
puts (-5).times {|i| puts i.to_s}.to_s
puts "upto"
puts (-2).upto(2){|i| puts i.to_s}.to_s
puts (2).upto(2){|i| puts i.to_s}.to_s
puts (3).upto(2){|i| puts i.to_s}.to_s
puts "downto"
puts (2).downto(-2){|i| puts i.to_s}.to_s
puts (2).downto(2){|i| puts i.to_s}.to_s
puts (5).downto(6){|i| puts i.to_s}.to_s
puts "abs"
puts (2).abs.to_s
puts (-2).abs.to_s
puts "magnitude"
puts (2).magnitude.to_s
puts (-2).magnitude.to_s
================================================
FILE: rb_test/ivar.rb
================================================
class Foo
def setup
@ivar = "hello"
end
def ivar
@ivar
end
end
foo = Foo.new
foo.setup
puts foo.ivar
================================================
FILE: rb_test/load.rb
================================================
load 'rb_test/_load.rb'
load 'rb_test/_load.rb'
================================================
FILE: rb_test/method_def.rb
================================================
str = "not this"
def hello_world_method_0
"hello world no arg"
end
def hello_world_method_1(name)
str = name
str
end
def hello_world_method_block(n)
puts n
puts yield
end
def yield_with_arg(s,x)
yield s,x
end
yield_with_arg("yield with","arg") do |ss, xx|
puts ss # "yield with"
puts xx # "arg"
end
puts hello_world_method_0
puts hello_world_method_1("my name is erruby")
hello_world_method_block("arg") { "hello block" }
#hello_world_method_block(4){"hello"}.block1{"this"}.block2{"world"}
================================================
FILE: rb_test/nested_block.rb
================================================
3.times do |i|
puts i.to_s
4.times do |j|
puts j.to_s
end
end
================================================
FILE: rb_test/nested_const.rb
================================================
class Foo
Bar = "hello nested"
class Alice
end
end
Bob = "outside bob"
puts "Foo::Bar"
puts Foo::Bar
Foo::Alice::Bob = "inside Bob"
puts Foo::Alice::Bob
puts Bob
================================================
FILE: rb_test/nil_class.rb
================================================
nil
nil & nil
nil & true
nil & false
nil & ""
nil ^ nil
nil ^ true
nil ^ false
nil ^ ""
nil | nil
nil | true
nil | false
nil | ""
nil.inspect
nil.nil?
nil.to_s
================================================
FILE: rb_test/require_relative.rb
================================================
require_relative "_require_relative"
require_relative "_require_relative"
================================================
FILE: rb_test/sysrb_out/.gitkeep
================================================
================================================
FILE: rb_test/sysrb_out/and_dot.out
================================================
1
================================================
FILE: rb_test/sysrb_out/array_class.out
================================================
1
2
mul
1
2
3
1
2
3
1
2
3
b
a
c
false
true
3
3
d
f
d
f
a
a
b
4
2
5
0
0
================================================
FILE: rb_test/sysrb_out/boolean_class.out
================================================
true
false
false
true
false
true
false
true
true
false
false
false
false
true
true
false
true
true
true
false
true
false
true
false
================================================
FILE: rb_test/sysrb_out/class_cross_call.out
================================================
hello
================================================
FILE: rb_test/sysrb_out/class_def.out
================================================
hello world
hello bar
================================================
FILE: rb_test/sysrb_out/class_inherent.out
================================================
foo
foo
i'm alice
================================================
FILE: rb_test/sysrb_out/class_method.out
================================================
bar
================================================
FILE: rb_test/sysrb_out/const_def.out
================================================
hello const
================================================
FILE: rb_test/sysrb_out/file.out
================================================
rb_test/file.rb
/Users/johnlinvc/Projs/erruby/erruby/rb_test
================================================
FILE: rb_test/sysrb_out/fixnum.out
================================================
1
2
-1
0
576460752303423488
-576460752303423489
2
1
-1
1
8
1
16
true
false
false
true
true
false
false
false
true
false
true
true
false
true
false
-1
0
1
================================================
FILE: rb_test/sysrb_out/global_var.out
================================================
hello gvar
================================================
FILE: rb_test/sysrb_out/hello_world.out
================================================
hello world
================================================
FILE: rb_test/sysrb_out/integer_class.out
================================================
5
5
5
5
5
5
5
1
true
false
false
true
2
2
2
8
0
6
2
24
24
0
0
true
2
0
2
0
times
0
1
2
3
4
5
0
-5
upto
-2
-1
0
1
2
-2
2
2
3
downto
2
1
0
-1
-2
2
2
2
5
abs
2
2
magnitude
2
2
================================================
FILE: rb_test/sysrb_out/ivar.out
================================================
hello
================================================
FILE: rb_test/sysrb_out/load.out
================================================
loaded
loaded
================================================
FILE: rb_test/sysrb_out/method_def.out
================================================
yield with
arg
hello world no arg
my name is erruby
arg
hello block
================================================
FILE: rb_test/sysrb_out/nested_block.out
================================================
0
0
1
2
3
1
0
1
2
3
2
0
1
2
3
================================================
FILE: rb_test/sysrb_out/nested_const.out
================================================
Foo::Bar
hello nested
inside Bob
outside bob
================================================
FILE: rb_test/sysrb_out/nil_class.out
================================================
================================================
FILE: rb_test/sysrb_out/require_relative.out
================================================
required file
================================================
FILE: rb_test/sysrb_out/var_assign.out
================================================
hello world
================================================
FILE: rb_test/var_assign.rb
================================================
hello = "hello world"
puts hello
================================================
FILE: rebar.config
================================================
{deps, [
{erlport, ".*", {git, "https://github.com/johnlinvc/erlport.git", {branch, "erruby"}}, [raw]},
{plists, ".*", {git, "https://github.com/johnlinvc/plists.git", {branch, "master"}}, [raw]},
{getopt, "0.8.2", {git, "https://github.com/jcomellas/getopt.git", {tag, "v0.8.2"}}}
]
}.
{pre_hooks, [{compile, "./pre_compile"}]}.
================================================
FILE: src/erb.erl
================================================
-module(erb).
-export([find_or_init_class/2]).
find_or_init_class(Name, InitFun) ->
case whereis(Name) of
undefined -> InitFun();
Pid -> {ok, Pid}
end.
================================================
FILE: src/erruby.app.src
================================================
{application, erruby,
[
{description, ""},
{vsn, "1"},
{registered, []},
{applications, [
kernel,
stdlib
]},
{mod, { erruby_app, []}},
{env, []}
]}.
================================================
FILE: src/erruby.erl
================================================
-module(erruby).
-include("rb.hrl").
-export([eruby/1, start_ruby/0, stop_ruby/1, parse_ast/2, main/1]).
opt_spec_list() ->
[
{debug, $d, "debug", {integer, 0}, "Verbose level for debugging"},
{verbose, $v, "verbose", undefined, "print version number and enter verbose mode"},
{fast, $f, "fast", undefined, "use escript instead of erl for faster bootup"},
{help, $h, "help", undefined, "Show this help"}
].
handle_opts({debug, DebugLevel}) ->
erruby_debug:set_debug_level(DebugLevel);
handle_opts(help ) ->
show_help();
handle_opts(verbose) ->
io:format("erruby 0.1.0~n"),
erruby_debug:set_debug_level(2);
handle_opts(_Opts) ->
ok.
parse_args([ArgsAtom]) when is_atom(ArgsAtom) ->
ArgsString = atom_to_list(ArgsAtom),
ArgsUntokened = string:centre(ArgsString,length(ArgsString)-2),
string:tokens(ArgsUntokened, " ");
parse_args(Args) ->
Args.
main(RawArgs) ->
Args = parse_args(RawArgs),
add_lib_path(),
erruby_debug:start_link(0),
{ok, {Opts, Extra}} = getopt(Args),
lists:foreach(fun handle_opts/1, Opts),
[SrcFileName | RubyArgs] = Extra,
try
erruby_debug:debug_2("input file name ~s\n", [SrcFileName]),
erruby_debug:debug_2("input args ~s\n", [RubyArgs]),
eruby(SrcFileName)
catch
_:known_error ->
erruby_debug:debug_1("known error ~n", []),
erruby_debug:debug_1("~p~n",[erlang:get_stacktrace()]);
_:E ->
io:format("error ~p ~n", [E]),
erlang:display(erlang:get_stacktrace())
end.
eruby(SrcFileName) ->
{ok, Binary} = file:read_file(SrcFileName),
FileLines = binary:bin_to_list(Binary),
Ruby = start_ruby(),
Ast = parse_ast(Ruby, FileLines),
stop_ruby(Ruby),
erruby_vm:eval_file(Ast, SrcFileName).
getopt(Args) ->
getopt:parse(opt_spec_list(), Args).
show_help() ->
getopt:usage(opt_spec_list(), "erruby", "[programfile] [arguments]"),
halt(1).
install_encoder(Ruby) ->
ruby:call(Ruby, erruby_rb_path() , 'install_encoder',[]).
erruby_path() ->
os:getenv("ERRUBY_PATH") ++ "/ebin".
relative_path(Path) ->
erruby_path() ++ Path.
erruby_rb_path() ->
list_to_atom(relative_path("/../rb_src/erruby.rb")).
parse_ast(Ruby, String) ->
ruby:call(Ruby, erruby_rb_path(),'parse', [String]).
add_lib_path() ->
code:add_path(relative_path("/../deps/erlport/ebin")),
code:add_path(relative_path("/../deps/getopt/ebin")),
code:add_path(relative_path("/../deps/plists/ebin")),
code:add_path(erruby_path()).
stop_ruby(Ruby) ->
ruby:stop(Ruby).
start_ruby() ->
{ok, Ruby} = ruby:start(),
install_encoder(Ruby),
Ruby.
================================================
FILE: src/erruby_app.erl
================================================
-module(erruby_app).
-behaviour(application).
%% Application callbacks
-export([start/2, stop/1]).
%% ===================================================================
%% Application callbacks
%% ===================================================================
start(_StartType, _StartArgs) ->
erruby_sup:start_link().
stop(_State) ->
ok.
================================================
FILE: src/erruby_array.erl
================================================
-module(erruby_array).
-include("rb.hrl").
-export([install_array_classes/0, new_array/2, new_array/1]).
-export([array_to_list/1, push/2]).
%TODO find a way to define module_function
install_array_classes() ->
{ok, ArrayClass} = erruby_class:new_class(),
'Array' = erruby_object:def_global_const('Array', ArrayClass),
erruby_object:def_method(ArrayClass, map, fun method_map/1),
erruby_object:def_method(ArrayClass, pmap, fun method_pmap/1),
erruby_object:def_method(ArrayClass, '*' , fun method_multiplication/2),
erruby_object:def_method(ArrayClass, '+', fun method_plus/2),
erruby_object:def_method(ArrayClass, 'concat', fun method_concat/2),
erruby_object:def_method(ArrayClass, 'at' , fun method_at/2),
erruby_object:def_method(ArrayClass, 'first' , fun method_first/1),
erruby_object:def_method(ArrayClass, 'last' , fun method_last/1),
erruby_object:def_method(ArrayClass, 'empty?', fun method_empty_q/1),
erruby_object:def_method(ArrayClass, 'length', fun method_length/1),
erruby_object:def_method(ArrayClass, 'size', fun method_length/1),
erruby_object:def_method(ArrayClass, 'push' , fun method_push/2),
erruby_object:def_method(ArrayClass, '<<' , fun method_push/2),
erruby_object:def_method(ArrayClass, 'unshift' , fun method_unshift/2),
erruby_object:def_method(ArrayClass, 'shift' , fun method_shift/1),
erruby_object:def_method(ArrayClass, 'drop' , fun method_drop/2),
ok.
drop_elements(_List, Count) when Count =< 0 ->
_List;
drop_elements([Head | Tail], Count) ->
drop_elements(Tail, Count - 1).
method_drop(#{self := Self}=Env, IntObj) ->
Int = erruby_fixnum:fix_to_int(IntObj),
List = array_to_list(Self),
if
length(List) < Int ->
new_array(Env, []);
true ->
ResultList = drop_elements(List, Int),
new_array(Env, ResultList)
end.
method_map(#{self := Self}=Env) ->
List = array_to_list(Self),
FoldFun = fun(X, EnvAcc) -> erruby_vm:yield(EnvAcc, [X]) end,
Envs = erruby_vm:scanl(FoldFun, Env, List),
Results = lists:map(fun erruby_rb:ret_val/1, Envs),
erruby_rb:return(Results, lists:last(Envs)).
repeat_list(_List, Count) when Count =< 0 ->
new_array([]);
repeat_list(List, 1) ->
List;
repeat_list(List, Count) ->
lists:append(List, repeat_list(List, Count-1)).
method_multiplication(#{self := Self}=Env, IntObj) ->
Int = erruby_fixnum:fix_to_int(IntObj),
List = array_to_list(Self),
ResultList = repeat_list(List, Int),
new_array(Env, ResultList).
method_plus(#{self := Self}=Env, Another) ->
Elements = array_to_list(Self),
AnotherElements = array_to_list(Another),
NewElements = Elements ++ AnotherElements,
new_array(Env, NewElements).
method_concat(#{self := Self}=Env, Another) ->
Elements = array_to_list(Self),
AnotherElements = array_to_list(Another),
NewElements = Elements ++ AnotherElements,
Properties = erruby_object:get_properties(Self),
NewProperties = Properties#{ elements := NewElements},
erruby_object:set_properties(Self, NewProperties),
erruby_rb:return(Self, Env).
method_pmap(#{self := Self}=Env) ->
List = array_to_list(Self),
MapFun = fun(X) -> erruby_vm:yield(Env, [X]) end,
Envs = plists:map(MapFun, List, {processes, erlang:system_info(schedulers_online)}),
Results = lists:map(fun erruby_rb:ret_val/1, Envs),
erruby_rb:return(Results, lists:last(Envs)).
method_at(#{self := Self}=Env, IntObj) ->
Int = erruby_fixnum:fix_to_int(IntObj),
erruby_rb:return(at(Self, Int), Env).
method_first(#{self := Self}=Env) ->
List = array_to_list(Self),
[ Head | _Tail ] = List,
erruby_rb:return(Head, Env).
method_last(#{self := Self}=Env) ->
List = array_to_list(Self),
erruby_rb:return(lists:last(List), Env).
method_empty_q(#{self := Self}=Env) ->
List = array_to_list(Self),
case length(List) < 1 of
true -> erruby_boolean:new_true(Env);
false -> erruby_boolean:new_false(Env)
end.
method_length(#{self := Self}=Env) ->
List = array_to_list(Self),
erruby_fixnum:new_fixnum(Env, length(List)).
method_push(#{self := Self}=Env, Append) ->
push(Self, Append),
erruby_rb:return(Self, Env).
method_unshift(#{self := Self}=Env, Head) ->
Elements = array_to_list(Self),
NewElements = [Head | Elements],
Properties = erruby_object:get_properties(Self),
NewProperties = Properties#{ elements := NewElements} ,
erruby_object:set_properties(Self, NewProperties),
erruby_rb:return(Self, Env).
method_shift(#{self := Self}=Env) ->
Elements = array_to_list(Self),
[Head | Rest] = Elements,
Properties = erruby_object:get_properties(Self),
NewProperties = Properties#{ elements := Rest} ,
erruby_object:set_properties(Self, NewProperties),
erruby_rb:return(Head, Env).
%TODO maybe use pid to find class
new_array(Env, Elements) ->
erruby_rb:return(new_array(Elements), Env).
new_array(Elements) ->
ArrayClass = erruby_object:find_global_const('Array'),
Properties = #{elements => Elements},
{ok, Array} = erruby_object:new_object(ArrayClass, Properties),
Array.
%% @doc the Index is 0-based, not the 1-based of usual erlang
at(Array, Index) ->
Properties = erruby_object:get_properties(Array),
#{ elements := Elements} = Properties,
lists:nth(Index+1, Elements).
push(Array, Elem) ->
Elements = array_to_list(Array),
NewElements = Elements ++ [Elem],
Properties = erruby_object:get_properties(Array),
NewProperties = Properties#{ elements := NewElements} ,
erruby_object:set_properties(Array, NewProperties).
array_to_list(Array) ->
Properties = erruby_object:get_properties(Array),
#{ elements := Elements} = Properties,
Elements.
================================================
FILE: src/erruby_boolean.erl
================================================
-module(erruby_boolean).
-export([install_boolean_classes/0,new_true/1,new_false/1,true_instance/0,false_instance/0]).
%TODO register the True & False class in Const
install_boolean_classes() ->
{ok, TrueClass} = erruby_class:new_class(),
{ok, FalseClass} = erruby_class:new_class(),
'TrueClass' = erruby_object:def_global_const('TrueClass', TrueClass),
'FalseClass' = erruby_object:def_global_const('FalseClass', FalseClass),
install_method(TrueClass, FalseClass, '!', fun method_not/1),
install_method(TrueClass, FalseClass, '&', fun method_and/2),
install_method(TrueClass, FalseClass, '^', fun method_xor/2),
install_method(TrueClass, FalseClass, '|', fun method_or/2),
erruby_object:def_method(TrueClass, to_s, fun method_true_to_s/1),
erruby_object:def_method(FalseClass, to_s, fun method_false_to_s/1),
erruby_object:def_method(TrueClass, inspect, fun method_true_to_s/1),
erruby_object:def_method(FalseClass, inspect, fun method_false_to_s/1),
erruby_object:new_object_with_pid_symbol(erruby_boolean_true, TrueClass),
erruby_object:new_object_with_pid_symbol(erruby_boolean_false, FalseClass),
ok.
install_method(TC, FC, Name, Func) ->
erruby_object:def_method(TC, Name, Func),
erruby_object:def_method(FC, Name, Func).
new_true(Env) -> erruby_rb:return(true_instance(), Env).
new_false(Env) -> erruby_rb:return(false_instance(), Env).
true_instance() ->
whereis(erruby_boolean_true).
false_instance() ->
whereis(erruby_boolean_false).
method_not(#{self := Self} = Env) ->
True = true_instance(),
False = false_instance(),
RetVal = case Self of
True -> False;
False -> True
end,
erruby_rb:return(RetVal, Env).
method_and(#{self := Self} = Env, Object) ->
Another = object_to_boolean(Object),
RetVal = and_op(Self, Another),
erruby_rb:return(RetVal, Env).
object_to_boolean(Object) ->
NilObject = erruby_nil:nil_instance(),
False = false_instance(),
case Object of
NilObject -> false_instance();
False -> false_instance();
_ -> true_instance()
end.
and_op(B1,B2) ->
True = true_instance(),
False = false_instance(),
case B1 of
True -> B2;
False -> False
end.
or_op(B1,B2) ->
True = true_instance(),
False = false_instance(),
case B1 of
True -> True;
False -> B2
end.
not_op(Boolean) ->
True = true_instance(),
False = false_instance(),
case Boolean of
True -> False;
False -> True
end.
method_or(#{self := Self} = Env, Object) ->
Another = object_to_boolean(Object),
RetVal = or_op(Self, Another),
erruby_rb:return(RetVal, Env).
method_xor(#{self := Self} = Env, Object) ->
Another = object_to_boolean(Object),
NotAandB = and_op(not_op(Self),Another),
AandNotB = and_op(Self, not_op(Another)),
RetVal = or_op(NotAandB, AandNotB),
erruby_rb:return(RetVal, Env).
method_true_to_s(Env) ->
erruby_vm:new_string("true", Env).
method_false_to_s(Env) ->
erruby_vm:new_string("false",Env).
================================================
FILE: src/erruby_class.erl
================================================
-module(erruby_class).
-export([new_class/0, new_named_class/1, new_class/1, install_class_class_methods/0, init_class_class/0, class_name/1]).
%TODO return self when calling method_class on self
%TODO add name parameter
%TODO should call initialize method when new
new_class() ->
new_named_class(undefined).
new_named_class(Name) ->
Properties = #{name => Name},
erruby_object:start_link(class_class(), Properties).
class_name(Self) ->
Properties = erruby_object:get_properties(Self),
#{name := Name} = Properties,
Name.
new_class(SuperClass) ->
Properties = #{superclass => SuperClass},
erruby_object:start_link(class_class(), Properties).
install_class_class_methods() ->
erruby_object:def_method(class_class(), 'new', fun method_new/1),
ok.
%FIXME new a real class
method_new(#{self := Klass}=Env) ->
{ok, NewObject} = erruby_object:start_link(Klass),
erruby_rb:return(NewObject, Env).
init_class_class() ->
erb:find_or_init_class(erruby_class_class, fun init_class_class_internal/0).
init_class_class_internal() ->
Properties = #{superclass => erruby_object:object_class()},
{ok, Pid} = erruby_object:new_object_with_pid_symbol(erruby_class_class, erruby_object:object_class()),
ok = install_class_class_methods(),
{ok, Pid}.
class_class() ->
whereis(erruby_class_class).
================================================
FILE: src/erruby_debug.erl
================================================
-module(erruby_debug).
-export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2]).
-export([start_link/1, debug/3, debug_1/2,debug_2/2, debug_tmp/2, set_debug_level/1]).
-export([print_env/1]).
init([DebugLevel]) ->
{ok, #{debug_level => DebugLevel}}.
terminate(_Arg, _State) -> {ok, dead}.
code_change(_OldVsn, State, _Extra) -> {ok, State}.
debug(Format, Args, Level) ->
FullFormat = lists:concat(["debug level ", Level, ": ", Format]),
gen_server:call(erruby_debug_pid, #{level => Level, format => FullFormat, args => Args}).
debug_1(Format, Args) ->
debug(Format, Args, 1).
debug_2(Format, Args) ->
debug(Format, Args, 2).
debug_tmp(Format, Args) ->
io:format(Format, Args).
set_debug_level(Level) ->
gen_server:cast(erruby_debug_pid, #{new_level => Level}).
start_link(DebugLevel) -> gen_server:start_link({local, erruby_debug_pid} ,?MODULE, [DebugLevel], []).
handle_info(Info, State) ->
io:format("Got unkwon info:~n~p~n", [Info]),
{ok, State}.
handle_call(#{level := Level, format := Format, args := Args}, _From, #{debug_level := DebugLevel} = State) when DebugLevel >= Level ->
io:format(Format, Args),
{reply, ok, State};
handle_call(#{level := Level}, _From, #{debug_level := DebugLevel} = State) when DebugLevel < Level ->
{reply, ok, State};
handle_call(_Req, _From, State) ->
io:format("handle unknow call ~p ~p ~p ~n",[_Req, _From, State]),
NewState = State,
{reply, done, NewState}.
handle_cast(#{new_level := NewLevel}, State) ->
{noreply, State#{ debug_level => NewLevel } };
handle_cast(_Req, State) ->
io:format("handle unknown cast ~p ~p ~n",[_Req, State]),
NewState = State,
{noreply, NewState}.
print_env(Env) ->
debug_1("Env: ~p ~n",[Env]).
================================================
FILE: src/erruby_fixnum.erl
================================================
-module(erruby_fixnum).
-export([install_fixnum_class/0, new_fixnum/2, fix_to_int/1]).
%%
%% @TODO inherent from integer & numeric
install_fixnum_class() ->
IntegerClass = erruby_object:find_global_const('Integer'),
{ok, FixnumClass} = erruby_class:new_class(IntegerClass),
'Fixnum' = erruby_object:def_global_const('Fixnum', FixnumClass),
erruby_object:def_method(FixnumClass, to_s, fun method_to_s/1),
erruby_object:def_method(FixnumClass, '-@', fun method_neg/1),
erruby_object:def_method(FixnumClass, '+', fun method_add/2),
erruby_object:def_method(FixnumClass, '-', fun method_minus/2),
erruby_object:def_method(FixnumClass, '*', fun method_multiplication/2),
erruby_object:def_method(FixnumClass, '**', fun method_power/2),
erruby_object:def_method(FixnumClass, '<', fun method_less/2),
erruby_object:def_method(FixnumClass, '>', fun method_greater/2),
erruby_object:def_method(FixnumClass, '<=', fun method_less_equal/2),
erruby_object:def_method(FixnumClass, '>=', fun method_greater_equal/2),
erruby_object:def_method(FixnumClass, '==', fun method_equal/2),
erruby_object:def_method(FixnumClass, '<=>', fun method_cmp/2),
erruby_object:def_method(FixnumClass, '/', fun method_division/2),
erruby_object:def_method(FixnumClass, '%', fun method_module/2),
ok.
fixnum_class() ->
erruby_object:find_global_const('Fixnum').
new_fixnum(Env, N) ->
{ok, Obj} = erruby_object:new_object(fixnum_class(), #{val => N}),
erruby_rb:return(Obj, Env).
fix_to_int(Fixnum) ->
get_val(Fixnum).
get_val(Fixnum) ->
#{val := Val} = erruby_object:get_properties(Fixnum),
Val.
binary_op(#{self := Self}=Env, AnotherFixnum, Fun) ->
Val = Fun(get_val(Self), get_val(AnotherFixnum)),
new_fixnum(Env, Val).
%% @TODO use new_string instead
method_to_s(#{self := Self}=Env) ->
Val = get_val(Self),
erruby_rb:return(integer_to_list(Val), Env).
%% TODO handle case where the other is not Fixnum
method_add(Env, AnotherFixnum) ->
binary_op(Env, AnotherFixnum, fun (A,B) -> A+B end).
method_minus(Env, AnotherFixnum) ->
binary_op(Env, AnotherFixnum, fun (A,B) -> A-B end).
method_multiplication(Env, AnotherFixnum) ->
binary_op(Env, AnotherFixnum, fun (A,B) -> A*B end).
method_division(Env, AnotherFixnum) ->
binary_op(Env, AnotherFixnum, fun (A,B) -> trunc(A/B) end).
method_power(Env, AnotherFixnum) ->
binary_op(Env, AnotherFixnum, fun (A,B) -> trunc(math:pow(A,B)) end).
method_neg(#{self := Self}=Env) ->
Val = - get_val(Self),
new_fixnum(Env, Val).
method_module(Env, AnotherFixnum) ->
binary_op(Env, AnotherFixnum, fun (A,B) -> A rem B end).
binary_cmp(#{self := Self}=Env, AnotherFixnum, Fun) ->
Val = get_val(Self),
AnotherVal = get_val(AnotherFixnum),
case Fun(Val,AnotherVal) of
true -> erruby_boolean:new_true(Env);
false -> erruby_boolean:new_false(Env)
end.
%%TODO move these to the comparator module
method_less(Env, AnotherFixnum) ->
binary_cmp(Env, AnotherFixnum, fun (X,Y) -> X < Y end).
method_less_equal(Env, AnotherFixnum) ->
binary_cmp(Env, AnotherFixnum, fun (X,Y) -> X =< Y end).
method_greater(Env, AnotherFixnum) ->
binary_cmp(Env, AnotherFixnum, fun (X,Y) -> X > Y end).
method_greater_equal(Env, AnotherFixnum) ->
binary_cmp(Env, AnotherFixnum, fun (X,Y) -> X >= Y end).
method_equal(Env, AnotherFixnum) ->
binary_cmp(Env, AnotherFixnum, fun (X,Y) -> X =:= Y end).
cmp_helper(X,Y) when X < Y ->
-1;
cmp_helper(X,Y) when X =:= Y ->
0;
cmp_helper(X,Y) when X > Y ->
1.
method_cmp(Env, AnotherFixnum) ->
binary_op(Env, AnotherFixnum, fun cmp_helper/2).
================================================
FILE: src/erruby_integer.erl
================================================
-module(erruby_integer).
-export([install_integer_class/0]).
install_integer_class() ->
{ok, IntegerClass} = erruby_class:new_class(),
'Integer' = erruby_object:def_global_const('Integer', IntegerClass),
erruby_object:def_method(IntegerClass, to_i, fun method_to_i/1),
erruby_object:def_method(IntegerClass, to_int, fun method_to_i/1),
erruby_object:def_method(IntegerClass, floor, fun method_to_i/1),
erruby_object:def_method(IntegerClass, ceil, fun method_to_i/1),
erruby_object:def_method(IntegerClass, truncate, fun method_to_i/1),
erruby_object:def_method(IntegerClass, numerator, fun method_to_i/1),
erruby_object:def_method(IntegerClass, ord, fun method_to_i/1),
erruby_object:def_method(IntegerClass, denominator, fun method_denominator/1),
erruby_object:def_method(IntegerClass, 'even?', fun method_even_q/1),
erruby_object:def_method(IntegerClass, 'odd?', fun method_odd_q/1),
erruby_object:def_method(IntegerClass, 'gcd', fun method_gcd/2),
erruby_object:def_method(IntegerClass, 'lcm', fun method_lcm/2),
erruby_object:def_method(IntegerClass, 'integer?', fun method_integer_q/1),
erruby_object:def_method(IntegerClass, 'succ', fun method_succ/1),
erruby_object:def_method(IntegerClass, 'next', fun method_succ/1),
erruby_object:def_method(IntegerClass, 'times', fun method_times/1),
erruby_object:def_method(IntegerClass, 'upto', fun method_upto/2),
erruby_object:def_method(IntegerClass, 'downto', fun method_downto/2),
erruby_object:def_method(IntegerClass, 'abs', fun method_abs/1),
erruby_object:def_method(IntegerClass, 'magnitude', fun method_abs/1),
ok.
method_to_i(#{self := Self}=Env) -> erruby_rb:return(Self, Env).
method_denominator(Env) ->
erruby_fixnum:new_fixnum(Env, 1).
%%TODO handle Bignum
method_even_q(#{self := Self}=Env) ->
Int = erruby_fixnum:fix_to_int(Self),
case Int rem 2 of
0 -> erruby_boolean:new_true(Env);
1 -> erruby_boolean:new_false(Env)
end.
method_odd_q(#{self := Self}=Env) ->
Int = erruby_fixnum:fix_to_int(Self),
case Int rem 2 of
1 -> erruby_boolean:new_true(Env);
0 -> erruby_boolean:new_false(Env)
end.
%%TODO use binary gcd algo instead
%%TODO move this to rational
gcd(X,Y) when X < Y -> gcd(Y,X);
gcd(X,Y) when Y =:= 0 -> X;
gcd(X,Y) -> gcd(Y, X rem Y).
method_gcd(#{self := Self}=Env, AnotherInt) ->
X = abs(erruby_fixnum:fix_to_int(Self)),
Y = abs(erruby_fixnum:fix_to_int(AnotherInt)),
case min(X,Y) of
0 -> erruby_fixnum:new_fixnum(Env, max(X,Y));
_ -> erruby_fixnum:new_fixnum(Env, gcd(X,Y))
end.
lcm(0,_Y) -> 0;
lcm(_X,0) -> 0;
lcm(X,Y) -> trunc(X / gcd(X,Y) * Y).
method_lcm(#{self := Self}=Env, AnotherInt) ->
X = abs(erruby_fixnum:fix_to_int(Self)),
Y = abs(erruby_fixnum:fix_to_int(AnotherInt)),
case min(X,Y) of
0 -> erruby_fixnum:new_fixnum(Env, 0);
_ -> erruby_fixnum:new_fixnum(Env, lcm(X,Y))
end.
method_integer_q(Env) ->
erruby_boolean:new_true(Env).
method_succ(#{self := Self}=Env) ->
erruby_fixnum:new_fixnum(Env, erruby_fixnum:fix_to_int(Self)+1).
yield_in_range(#{self := Self} = Env,Range) ->
FoldFun = fun (X, EnvAcc) ->
IntEnv = erruby_fixnum:new_fixnum(EnvAcc, X),
FixInt = erruby_rb:ret_val(IntEnv),
erruby_vm:yield(IntEnv, [FixInt]) end,
LastEnv = lists:foldl(FoldFun, Env, Range),
erruby_rb:return(Self, LastEnv).
times_range(X) when X =< 0 -> [];
times_range(X) -> lists:seq(0, X-1).
%%TODO handle empty block case
%%TODO handle Bigdecimal
method_times(#{self := Self}=Env) ->
Int = erruby_fixnum:fix_to_int(Self),
Range = times_range(Int),
yield_in_range(Env,Range).
upto_range(Start,End) when Start > End -> [];
upto_range(Start,End) -> lists:seq(Start,End).
method_upto(#{self := Self}=Env, AnotherInteger) ->
Int = erruby_fixnum:fix_to_int(Self),
AnotherInt = erruby_fixnum:fix_to_int(AnotherInteger),
Range = upto_range(Int,AnotherInt),
yield_in_range(Env,Range).
downto_range(Start,End) -> lists:reverse(upto_range(End,Start)).
method_downto(#{self := Self}=Env, AnotherInteger) ->
Int = erruby_fixnum:fix_to_int(Self),
AnotherInt = erruby_fixnum:fix_to_int(AnotherInteger),
Range = downto_range(Int,AnotherInt),
yield_in_range(Env,Range).
method_abs(#{self := Self}=Env) ->
erruby_fixnum:new_fixnum(Env, abs(erruby_fixnum:fix_to_int(Self))).
================================================
FILE: src/erruby_lib/erruby_file.erl
================================================
-module(erruby_file).
-include("../rb.hrl").
-export([install_file_classes/0]).
install_file_classes() ->
{ok, FileClass} = erruby_class:new_class(),
'File' = erruby_object:def_global_const('File', FileClass),
erruby_object:def_singleton_method(FileClass, 'expand_path', fun method_expand_path/3),
ok.
method_expand_path(Env, Filename, RelativeDirOrFileName) ->
{ok, Cwd} = file:get_cwd(),
DirOrFileName = filename:absname_join(Cwd, RelativeDirOrFileName),
ExpanedPath = filename:absname_join(DirOrFileName, Filename),
FlattenedPath = flatten_path(ExpanedPath),
erruby_vm:new_string(FlattenedPath, Env).
flatten_path(Path)->
Components = filename:split(Path),
FlattenedComponents = flatten_path_components(Components,[]),
filename:join(FlattenedComponents).
flatten_path_components([], Acc) -> lists:reverse(Acc);
flatten_path_components([".."|T], [])->
flatten_path_components(T, []);
flatten_path_components([".."|T], ["/"])->
flatten_path_components(T, ["/"]);
flatten_path_components([".."|T], [_H|Acc])->
flatten_path_components(T, Acc);
flatten_path_components([H|T], Acc)->
flatten_path_components(T, [H|Acc]).
================================================
FILE: src/erruby_nil.erl
================================================
-module(erruby_nil).
-export([new_nil/1, install_nil_class/0, nil_instance/0]).
install_nil_class() ->
{ok, NilClass} = erruby_class:new_class(),
'NilClass' = erruby_object:def_global_const('NilClass', NilClass),
erruby_object:def_method(NilClass, '&', fun method_and/2),
erruby_object:def_method(NilClass, '^', fun method_xor/2),
erruby_object:def_method(NilClass, '|', fun method_xor/2),
erruby_object:def_method(NilClass, inspect, fun method_inspect/1),
erruby_object:def_method(NilClass, 'nil?', fun 'method_nil_q'/1),
erruby_object:def_method(NilClass, 'to_s', fun 'method_to_s'/1),
erruby_object:new_object_with_pid_symbol(erruby_nil, NilClass),
ok.
new_nil(Env) ->
erruby_rb:return(nil_instance(), Env).
nil_instance() -> whereis(erruby_nil).
method_and(Env, _Obj) -> erruby_boolean:new_false(Env).
method_xor(Env, Obj) ->
Nil = nil_instance(),
False = erruby_boolean:false_instance(),
case Obj of
Nil -> erruby_boolean:new_false(Env);
False -> erruby_boolean:new_false(Env);
_ -> erruby_boolean:new_true(Env)
end.
method_inspect(Env) -> erruby_vm:new_string("nil",Env).
method_nil_q(Env) -> erruby_boolean:new_true(Env).
method_to_s(Env) -> erruby_vm:new_string("",Env).
================================================
FILE: src/erruby_object.erl
================================================
-module(erruby_object).
-include("rb.hrl").
-behavior(gen_server).
-export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2]).
%for vm
-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]).
-export([def_singleton_method/4, def_singleton_method/3]).
-export([def_global_var/2, find_global_var/1]).
%for other buildtin class
-export([def_method/3, new_object_with_pid_symbol/2, new_object/2]).
-export([def_ivar/3, find_ivar/2]).
-export([init_main_object/0, main_object/0]).
-export([start_link/2, start_link/1]).
-export([get_properties/1, set_properties/2]).
-export([get_class/1]).
init([#{class := Class, properties := Properties}]) ->
DefaultState = default_state(),
StateWithClass = add_class_to_state(DefaultState, Class),
{ok, add_property_to_state(StateWithClass, Properties)};
init([#{class := Class}]) ->
DefaultState = default_state(),
{ok, add_class_to_state(DefaultState, Class)};
init([]) ->
{ok, default_state()}.
add_class_to_state(State, Class) ->
State#{class => Class}.
add_property_to_state(State, Properties) ->
State#{properties => Properties}.
%TODO in method_class return defalut object_class if no class is present
default_state() ->
Methods = #{},
IVars = #{},
Consts = #{},
#{self => self(),
methods => Methods,
ivars => IVars,
properties => #{},
consts => Consts}.
%TODO unify these?
start_link(Class) ->
gen_server:start_link(?MODULE, [#{class => Class }], []).
start_link(Class, Properties) ->
gen_server:start_link(?MODULE, [#{class => Class, properties => Properties}], []).
terminate(_Arg, _State) ->
{ok, dead}.
code_change(_OldVsn, State, _Extra) -> {ok, State}.
get_class(Self) ->
gen_server:call(Self, #{type => get_class}).
find_instance_method(Self, Name) ->
SingletonMethod = find_instance_method_in_singleton_class(Self, Name),
case SingletonMethod of
{ok, Method} -> Method;
{not_found, _} ->
find_instance_method_in_class(Self, Name)
end.
find_instance_method_in_singleton_class(Self, Name) ->
SingletonClass = singleton_class(Self),
case SingletonClass of
not_found -> {not_found, Name};
_ ->
Result = gen_server:call(SingletonClass, #{type => find_method, name => Name}),
case Result of
not_found -> {not_found, Name};
_ -> {ok, Result}
end
end.
find_instance_method_in_class(Self, Name) ->
%erruby_debug:debug_tmp("finding instance method ~p in ~p",[ Name, Self]),
Klass = get_class(Self),
Result = gen_server:call(Klass, #{type => find_method, name => Name}),
case Result of
not_found -> {not_found, Name};
_ -> Result
end.
find_method(Self, Name) ->
gen_server:call(Self, #{type => find_method, name => Name}).
self_or_object_class(Self) ->
MainObject = main_object(),
case Self of
MainObject -> object_class();
_ -> Self
end.
singleton_class(Self) ->
Properties = get_properties(Self),
maps:get(singleton_class, Properties, not_found).
get_or_create_singleton_class(Self) ->
SingletonClass = singleton_class(Self),
case SingletonClass of
not_found ->
{ok, NewSingletonClass} = erruby_class:new_named_class("singleton class"),
Properties = get_properties(Self),
NewProperties = Properties#{ singleton_class => NewSingletonClass },
set_properties(Self, NewProperties),
NewSingletonClass;
_ ->
SingletonClass
end.
def_method(Self, Name, Args, Body) ->
Receiver = self_or_object_class(Self),
gen_server:call(Receiver, #{type => def_method, name => Name, args => Args, body => Body}).
def_method(Self,Name,Func) when is_function(Func) ->
Receiver = self_or_object_class(Self),
gen_server:call(Receiver, #{type => def_method, name => Name, func => Func}).
def_singleton_method(Self, Name, Args, Body) ->
Receiver = get_or_create_singleton_class(Self),
gen_server:call(Receiver, #{type => def_method, name => Name, args => Args, body => Body}).
def_singleton_method(Self,Name,Func) when is_function(Func) ->
Receiver = get_or_create_singleton_class(Self),
gen_server:call(Receiver, #{type => def_method, name => Name, func => Func}).
%TODO call def_const instead
def_global_const(Name, Value) ->
gen_server:call(object_class(), #{type => def_const, name => Name, value => Value}).
find_global_const(Name) ->
find_const(object_class(), Name).
%TODO define on basic object instead
%TODO ability to use custom getter/setter
def_global_var(Name, Value) ->
Msg = #{type => def_global_var, name => Name, value => Value},
gen_server:call(object_class(), Msg).
find_global_var(Name) ->
gen_server:call(object_class(), #{type => find_global_var, name => Name}).
def_const(Self, Name, Value) ->
Receiver = self_or_object_class(Self),
gen_server:call(Receiver, #{type => def_const, name => Name, value => Value}).
find_const(Self, Name) ->
erruby_debug:debug_2("finding on ~p for const:~p~n",[Self, Name]),
gen_server:call(Self, #{type => find_const, name => Name}).
def_ivar(Self, Name, Value)->
gen_server:call(Self, #{type => def_ivar, name => Name, value => Value}).
find_ivar(Self, Name) ->
erruby_debug:debug_2("finding on ~p for ivar:~p~n",[Self, Name]),
gen_server:call(Self, #{type => find_ivar, name => Name}).
get_properties(Self) ->
gen_server:call(Self, #{type => get_properties}).
set_properties(Self, Properties) ->
gen_server:call(Self, #{type => set_properties, properties => Properties}).
handle_info(Info, State) ->
io:format("Got unkwon info:~n~p~n", [Info]),
{ok, State}.
handle_call(#{ type := def_method , name := Name, body := Body, args := Args}=_Msg, _From, #{methods := Methods} =State) ->
NewMethods = Methods#{ Name => #{ args => Args, body => Body, argc => length(Args) } },
NewState = State#{ methods := NewMethods},
{reply, Name, NewState};
handle_call(#{ type := def_method, name := Name, func := Func}=_Msg, _From, #{methods := Methods} = State) ->
NewMethods = Methods#{ Name => Func },
NewState = State#{ methods := NewMethods},
{reply, Name, NewState};
handle_call(#{ type := find_method, name := Name }, _From, #{methods := Methods} = State) ->
erruby_debug:debug_2("finding method:~p~n in State:~p~n",[Name, State]),
case maps:is_key(Name,Methods) of
true ->
#{Name := Method} = Methods,
{reply, Method, State};
false ->
%TODO use error classes
%io:format("Method ~p not found~n",[Name]),
erruby_debug:debug_2("finding in ancestors:~p~n",[ancestors(State)]),
Method = find_method_in_ancestors(ancestors(State), Name),
{reply, Method, State}
end;
handle_call(#{ type := get_properties }, _From, #{properties := Properties}=State) ->
{reply, Properties, State};
handle_call(#{ type := set_properties, properties := Properties }, _From, State) ->
NewState = State#{ properties := Properties},
{reply, NewState, NewState};
handle_call(#{ type := def_ivar, name := Name, value := Value }, _From, #{ivars := IVars}=State) ->
NewIvars = IVars#{Name => Value},
NewState = State#{ivars := NewIvars},
{reply, Name, NewState};
handle_call(#{ type := find_ivar, name := Name }, _From, #{ivars := IVars}=State) ->
erruby_debug:debug_2("finding ivar:~p~nin State:~p~n",[Name, State]),
Value = maps:get(Name, IVars, erruby_nil:nil_instance()),
{reply, Value, State};
handle_call(#{ type := def_const, name := Name, value := Value }, _From, #{consts := Consts}=State) ->
NewConsts = Consts#{Name => Value},
NewState = State#{consts := NewConsts},
{reply, Name, NewState};
handle_call(#{ type := find_const, name := Name }, _From, #{consts := Consts}=State) ->
erruby_debug:debug_2("finding const:~p~nin State:~p~n",[Name, State]),
Value = maps:get(Name, Consts, not_found),
{reply, Value, State};
handle_call(#{ type := def_global_var, name := Name, value := Value}, _From,
#{properties := #{global_var_tbl := GVarTbl} } = State) ->
NewGVarTbl = GVarTbl#{ Name => Value},
#{properties := Properties} = State,
NewProperties = Properties#{ global_var_tbl := NewGVarTbl },
NewState = State#{ properties := NewProperties},
{reply, Name, NewState};
handle_call(#{ type := find_global_var, name := Name}, _From,
#{properties := #{global_var_tbl := GVarTbl} } = State) ->
Value = maps:get(Name, GVarTbl, not_found),
{reply, Value, State};
handle_call(#{ type := get_class}, _From, State) ->
Value = maps:get(class, State, object_class()),
{reply, Value, State};
handle_call(_Req, _From, State) ->
io:format("handle unknow call ~p ~n ~p ~n ~p ~n",[_Req, _From, State]),
NewState = State,
{reply, done, NewState}.
handle_cast(_Req, State) ->
io:format("handle unknown cast ~p ~p ~n",[_Req, State]),
NewState = State,
{reply, done, NewState}.
%TODO support va args
method_puts(Env, String) ->
io:format("~s~n", [String]),
erruby_nil:new_nil(Env).
append_rb_extension(FileName) ->
case filename:extension(FileName) of
[] -> string:concat(FileName, ".rb");
_ -> FileName
end.
%TODO extract to Kernal
%TODO raise error if file not found
method_require_relative(Env, FileName) ->
RelativeFileName = relativeFileName(Env, FileName),
RelativeFileNameWithExt = append_rb_extension(RelativeFileName),
LoadedFeatures = find_global_var("$LOADED_FEATURES"),
LoadedFeaturesList = erruby_array:array_to_list(LoadedFeatures),
Contains = lists:member( RelativeFileNameWithExt, LoadedFeaturesList),
case Contains of
true -> erruby_boolean:new_false(Env);
_ ->
load_file(Env, RelativeFileNameWithExt),
erruby_array:push(LoadedFeatures, RelativeFileNameWithExt),
erruby_boolean:new_true(Env)
end.
relativeFileName(Env, FileName) ->
SrcFile = erruby_vm:file_name(Env),
SrcDir = filename:dirname(SrcFile),
filename:join([SrcDir, FileName]).
load_file(Env, RelativeFileNameWithExt) ->
try
erruby:eruby(RelativeFileNameWithExt),
erruby_boolean:new_true(Env)
catch
_:_E ->
erruby_debug:debug_2("cant require_relative file ~p~n", [RelativeFileNameWithExt]),
erruby_boolean:new_false(Env)
end.
%TODO raise error if file not found
% @TODO find a better way to get filename
method_load(Env, FileName)->
Pwd = os:getenv("PWD"),
RelativeFileNameWithExt = filename:join([Pwd, FileName]),
load_file(Env, RelativeFileNameWithExt).
method_self(#{self := Self}=Env) ->
erruby_rb:return(Self, Env).
method_inspect(#{self := Self}=Env) ->
S = io_lib:format("#<Object:~p>",[Self]),
erruby_vm:new_string(S,Env).
method_to_s(#{self := Self}=Env) ->
S = io_lib:format("~p",[Self]),
erruby_vm:new_string(S,Env).
%TODO support property?
new_object_with_pid_symbol(Symbol, Class) ->
gen_server:start_link({local, Symbol}, ?MODULE, [#{class => Class}], []).
new_object(Class, Payload) when is_map(Payload) ->
start_link(Class, Payload).
init_object_class() ->
erb:find_or_init_class(erruby_object_class, fun init_object_class_internal/0).
init_object_class_internal() ->
{ok, Pid} = gen_server:start_link({local, erruby_object_class}, ?MODULE, [],[]),
install_object_class_methods(),
'Object' = def_const(Pid, 'Object', Pid),
set_properties(object_class(), #{global_var_tbl => #{}}),
def_global_var("$LOADED_FEATURES", erruby_array:new_array([])),
{ok, Pid}.
init_main_object() ->
erb:find_or_init_class(erruby_main_object, fun init_main_object_internal/0).
init_main_object_internal() ->
new_object_with_pid_symbol(erruby_main_object, object_class()).
object_class() ->
whereis(erruby_object_class).
main_object() ->
whereis(erruby_main_object).
install_object_class_methods() ->
%TODO use this after inherent is done
%def_method(object_class(), '==', fun method_eq/2).
def_method(object_class(), 'puts', fun method_puts/2),
def_method(object_class(), 'self', fun method_self/1),
def_method(object_class(), 'inspect', fun method_inspect/1),
def_method(object_class(), 'to_s', fun method_to_s/1),
def_method(object_class(), '==', fun method_eq/2),
def_method(object_class(), 'require_relative', fun method_require_relative/2),
def_method(object_class(), 'load', fun method_load/2),
ok.
method_eq(#{self := Self}=Env, Object) ->
case Object of
Self -> erruby_boolean:new_true(Env);
_ -> erruby_boolean:new_false(Env)
end.
super_class(#{properties := Properties}=_State) ->
maps:get(superclass, Properties, object_class()).
%TODO handle include & extend
ancestors(State) ->
SuperClass = super_class(State),
ObjectClass = object_class(),
case self() of
ObjectClass -> [];
_ -> [SuperClass, ObjectClass]
end.
find_method_in_ancestors([], _Name) ->
not_found;
find_method_in_ancestors(Ancestors, Name) ->
[Klass | Rest] = Ancestors,
Method = find_method(Klass, Name),
case Method of
not_found -> find_method_in_ancestors(Rest, Name);
_ -> Method
end.
================================================
FILE: src/erruby_rb.erl
================================================
%%% TODO move general runtime api to here
-module(erruby_rb).
-export([ret_self/1,ret_val/1,return/2]).
ret_self( #{self := Self} = Env ) ->
Env#{ret_val => Self}.
ret_val( #{ret_val := RetVal} ) -> RetVal.
return(Value, Env) -> Env#{ret_val => Value}.
================================================
FILE: src/erruby_sup.erl
================================================
-module(erruby_sup).
-behaviour(supervisor).
%% API
-export([start_link/0]).
%% Supervisor callbacks
-export([init/1]).
%% Helper macro for declaring children of supervisor
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
%% ===================================================================
%% API functions
%% ===================================================================
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
%% ===================================================================
%% Supervisor callbacks
%% ===================================================================
init([]) ->
{ok, { {one_for_one, 5, 10}, []} }.
================================================
FILE: src/erruby_vm.erl
================================================
-module(erruby_vm).
-include("rb.hrl").
-export([eval_file/2, scanl/3]).
-export([new_nil/1, new_string/2]).
-export([eval_method_with_exit/5, yield/2]).
-export([file_name/1]).
print_ast(Ast) ->
erruby_debug:debug_1("Ast: ~p ~n",[Ast]).
print_env(Env) ->
erruby_debug:debug_1("Env: ~p ~n",[Env]).
scanl(_F, Acc, []) ->
[Acc];
scanl(F, Acc0, [H | T]) ->
Acc = apply(F, [H, Acc0]),
[Acc0 | scanl(F, Acc, T)].
eval_file(Ast, FileName) ->
DefaultEnv = default_env(),
Env = set_filename(DefaultEnv, FileName),
eval_ast(Ast, Env).
set_filename(Env, FileName) ->
Env#{'FileName' => FileName}.
file_name(Env) ->
case maps:find('FileName', Env) of
{ok, Value} -> Value;
error ->
file_name(find_prev_frame(Env))
end.
eval_ast({ast,type,'begin',children, Children}, Env) ->
erruby_debug:debug_2("eval begin~n",[]),
lists:foldl(fun eval_ast/2, Env, Children);
eval_ast({ast, type, self, children, []}, Env) ->
#{ self := Self } = Env,
erruby_rb:return(Self, Env);
eval_ast({ast, type, str, children, Children}, Env) ->
[SBin|_T] = Children,
new_string(binary_to_list(SBin), Env);
eval_ast({ast, type, nil, children, []}, Env) ->
erruby_nil:new_nil(Env);
eval_ast({ast, type, true, children, []}, Env) ->
erruby_boolean:new_true(Env);
eval_ast({ast, type, false, children, []}, Env) ->
erruby_boolean:new_false(Env);
eval_ast({ast, type, array, children, Args}, Env) ->
{EvaledArgs, LastEnv} = eval_args(Args, Env),
erruby_array:new_array(LastEnv, EvaledArgs);
eval_ast({ast, type, int, children, [N]}, Env) ->
erruby_fixnum:new_fixnum(Env, N);
eval_ast({ast, type, '__FILE__', children, []}, #{'FileName' := Filename } = Env) ->
new_string(Filename, Env);
eval_ast({ast, type, csend, children, Children}, Env)->
erruby_debug:debug_1("csend~n",[]),
[print_ast(Ast) || Ast <- Children],
[Receiver | [Msg | Args]] = Children,
ReceiverFrame = receiver_or_self(Receiver, Env),
UnresolvedTarget = erruby_rb:ret_val(ReceiverFrame),
Target = resolve_future(UnresolvedTarget),
Nil = erruby_nil:nil_instance(),
case Target of
Nil -> Nil;
_ ->
{EvaledArgs, LastEnv} = eval_args(Args, ReceiverFrame),
Method = erruby_object:find_instance_method(Target, Msg),
eval_method(Target,Method, EvaledArgs, LastEnv)
end;
eval_ast({ast, type, psend, children, Children}, Env)->
erruby_debug:debug_1("psend~n",[]),
[print_ast(Ast) || Ast <- Children],
[Receiver | [Msg | Args]] = Children,
ReceiverFrame = receiver_or_self(Receiver, Env),
UnresolvedTarget = erruby_rb:ret_val(ReceiverFrame),
Target = resolve_future(UnresolvedTarget),
{EvaledArgs, LastEnv} = eval_args(Args, ReceiverFrame),
Method = erruby_object:find_instance_method(Target, Msg),
process_eval_method(Target,Method, EvaledArgs, LastEnv);
%TODO call method using method object
eval_ast({ast,type,send, children, Children}, Env) ->
erruby_debug:debug_1("send~n",[]),
[print_ast(Ast) || Ast <- Children],
[Receiver | [Msg | Args]] = Children,
ReceiverFrame = receiver_or_self(Receiver, Env),
UnresolvedTarget = erruby_rb:ret_val(ReceiverFrame),
Target = resolve_future(UnresolvedTarget),
{EvaledArgs, LastEnv} = eval_args(Args, ReceiverFrame),
Method = erruby_object:find_instance_method(Target, Msg),
eval_method(Target,Method, EvaledArgs, LastEnv);
eval_ast({ast, type, block, children, [Method | [Args | [Body]]]= _Children}, Env) ->
Block = #{body => Body, args => Args},
BlockEnv = Env#{block => Block},
Result = eval_ast(Method, BlockEnv),
Result#{block => not_exist};
eval_ast({ast, type, yield, children, Args}, Env) ->
{EvaledArgs, LastEnv} = eval_args(Args, Env),
yield(LastEnv, EvaledArgs);
%FIXME return the value
eval_ast({ast, type, lvasgn, children, Children}, Env) ->
[Name, ValAst] = Children,
NewEnv = eval_ast(ValAst, Env),
RetVal = erruby_rb:ret_val(NewEnv),
bind_lvar(Name, RetVal, NewEnv);
eval_ast({ast, type, lvar, children, [Name]}, Env) ->
erruby_debug:debug_1("searching lvar ~p~n in frame~p~n", [Name, Env]),
#{ lvars := #{Name := Val}} = Env,
erruby_rb:return(Val, Env);
%FIXME return the value
eval_ast({ast, type, ivasgn, children, Children}, #{self := Self}=Env) ->
[Name, ValAst] = Children,
NewEnv = eval_ast(ValAst, Env),
RetVal = erruby_rb:ret_val(NewEnv),
erruby_object:def_ivar(Self, Name, RetVal),
erruby_rb:return(RetVal, Env);
eval_ast({ast, type, ivar, children, [Name]}, #{self := Self}=Env) ->
Val = erruby_object:find_ivar(Self, Name),
erruby_rb:return(Val, Env);
eval_ast({ast, type, gvasgn, children, Children}, Env) ->
[Name, ValAst] = Children,
NewEnv = eval_ast(ValAst, Env),
RetVal = erruby_rb:ret_val(NewEnv),
erruby_object:def_global_var(Name, RetVal),
erruby_rb:return(Name, NewEnv);
eval_ast({ast, type, gvar, children, [Name]}, Env) ->
Val = erruby_object:find_global_var(Name),
erruby_rb:return(Val, Env);
eval_ast({ast, type, defs, children, Children}, Env) ->
[ReceiverAst , Name , {ast, type, args, children, Args}, Body] = Children,
ReceiverEnv = eval_ast(ReceiverAst, Env),
Receiver = erruby_rb:ret_val(ReceiverEnv),
erruby_object:def_singleton_method(Receiver, Name, Args, Body),
new_symbol(Name, Env);
eval_ast({ast, type, def, children, Children}, Env) ->
[Name | [ {ast, type, args, children, Args} , Body ] ] = Children,
#{ self := Self } = Env,
erruby_object:def_method(Self, Name, Args, Body),
new_symbol(Name, Env);
%TODO figure out the Unknown field in AST
%TODO impl ancestors
eval_ast({ast, type, class, children,
[NameAst,undefined,Body] = _Children}, #{ self := Self } = Env) ->
{_,_,const,_,[_,Name]} = NameAst,
NameEnv = eval_ast(NameAst,Env),
ClassConst = erruby_rb:ret_val(NameEnv),
Class = case ClassConst of
not_found -> {ok, NewClass} = erruby_class:new_named_class(Name),
erruby_object:def_const(Self, Name, NewClass),
NewClass;
_ -> ClassConst
end,
case Body of
undefined -> NameEnv;
_ ->
NewFrame = new_frame(NameEnv, Class),
ResultFrame = eval_ast(Body,NewFrame),
pop_frame(ResultFrame)
end;
%TODO refactor with the one without SupperClass
eval_ast({ast, type, class, children,
[NameAst,SuperClassAst,Body] = _Children}, #{ self := Self } = Env) ->
{_,_,const,_,[_,Name]} = NameAst,
NameEnv = eval_ast(NameAst,Env),
ClassConst = erruby_rb:ret_val(NameEnv),
SuperClassEnv = eval_ast(SuperClassAst,NameEnv),
SuperClassConst = erruby_rb:ret_val(SuperClassEnv),
%TODO should fail when SuperClassConst is not defined
Class = case ClassConst of
not_found -> {ok, NewClass} = erruby_class:new_class(SuperClassConst),
erruby_object:def_const(Self, Name, NewClass),
NewClass;
_ -> ClassConst
end,
case Body of
undefined -> SuperClassEnv;
_ ->
NewFrame = new_frame(SuperClassEnv, Class),
ResultFrame = eval_ast(Body,NewFrame),
pop_frame(ResultFrame)
end;
eval_ast({ast, type, casgn, children, [ParentConstAst, Name, ValAst] }, Env) ->
ParentConstEnv = parent_const_env(ParentConstAst, Env),
ParentConst = erruby_rb:ret_val(ParentConstEnv),
NewEnv = eval_ast(ValAst, ParentConstEnv),
Val = erruby_rb:ret_val(NewEnv),
erruby_object:def_const(ParentConst, Name, Val),
NewEnv;
%TODO throw error when not_found
eval_ast({ast, type, const, children, [ParentConstAst, Name]}, Env) ->
ParentConstEnv = parent_const_env(ParentConstAst, Env),
ParentConst = erruby_rb:ret_val(ParentConstEnv),
LocalConst = erruby_object:find_const(ParentConst, Name),
Const = case LocalConst of
not_found -> erruby_object:find_const(erruby_object:object_class(), Name);
_ -> LocalConst
end,
erruby_rb:return(Const, Env);
eval_ast(Ast, Env) ->
erruby_debug:debug_1("Unhandled eval~n",[]),
print_ast(Ast),
print_env(Env).
process_eval_method(Target,Method,Args,Env) ->
Pid = spawn(?MODULE, eval_method_with_exit, [Target,Method,Args,Env, self()]),
erruby_rb:return({future, Pid}, Env).
resolve_future({future, Pid}) ->
receive
{future, Pid, Result} ->
print_env(Result),
%erruby_debug:debug_tmp("resolve_future future ~p~n", [Result]),
self() ! {future, Pid, Result},
erruby_rb:ret_val(Result)
end;
resolve_future(Any) ->
print_env(Any),
%erruby_debug:debug_tmp("resolve_future Any ~p~n", [Any]),
Any.
eval_method_with_exit(Target,Method,Args,Env, Sender) ->
try
Result = eval_method(Target, Method, Args, Env),
Respond = {future, self(),Result},
Sender ! Respond
catch
_:E ->
io:format("error ~p ~n", [E]),
erlang:display(erlang:get_stacktrace())
end,
exit(normal).
eval_method(Target,Method, UnresolvedArgs, Env) when is_function(Method) ->
NewFrame = new_frame(Env,Target),
Args = lists:map(fun resolve_future/1, UnresolvedArgs),
MethodArgs = [NewFrame | Args],
ResultFrame = apply(Method, MethodArgs),
pop_frame(ResultFrame);
eval_method(Target, {not_found, Name}, _, _) ->
%TODO raise exception instead
TargetClass = erruby_object:get_class(Target),
TargetClassName = erruby_class:class_name(TargetClass),
io:format("Undefined Method ~s for ~p~n", [Name, TargetClassName]),
exit(known_error);
eval_method(Target,#{body := Body, args := ArgNamesAst} = _Method, Args, Env) ->
NewFrame = new_frame(Env,Target),
ArgNames = [ArgName || {ast, type, arg, children, [ArgName]} <- ArgNamesAst],
NameWithArgs = lists:zip( ArgNames, Args),
NewFrameWithArgs = lists:foldl(fun ({Name, Arg}, EnvAcc) -> bind_lvar(Name, Arg, EnvAcc) end, NewFrame, NameWithArgs),
pop_frame( eval_ast(Body, NewFrameWithArgs)).
bind_lvar(Name, Val, #{ lvars := LVars } = Env) ->
Env#{ lvars := LVars#{ Name => Val }}.
receiver_or_self(undefined, Env) ->
#{ self := Self } = Env,
erruby_rb:return(Self, Env);
receiver_or_self(Receiver, Env) ->
eval_ast(Receiver,Env).
new_string(String, Env) ->
erruby_rb:return(String, Env).
new_symbol(Symbol, Env) ->
erruby_rb:return(Symbol, Env).
new_frame(Env, Self) ->
Env#{lvars => #{}, ret_val => not_exist, self => Self, prev_frame => Env}.
new_nil(Env) ->
erruby_nil:new_nil(Env).
%TODO move to another place
find_prev_frame(Env) ->
case maps:get(prev_frame, Env, no_prev_frame) of
no_prev_frame -> throw(cant_find_block);
Frame -> Frame
end.
find_block(Env) ->
case maps:get(block, Env) of
not_exist -> find_block(find_prev_frame(Env));
Block -> Block
end.
parent_const_env(ParentConstAst, Env) ->
case ParentConstAst of
undefined -> erruby_rb:ret_self(Env);
{ast, type, const, children, _} -> eval_ast(ParentConstAst, Env)
end.
eval_args(ArgAsts, Env) ->
[_ |Envs] = scanl(fun eval_ast/2, Env, ArgAsts),
EvaledArgs = lists:map( fun erruby_rb:ret_val/1, Envs),
LastEnv = case Envs of
[] -> Env;
_ -> lists:last(Envs)
end,
{EvaledArgs, LastEnv}.
yield(Env, Args)->
Block = find_block(Env),
#{body := Body, args := {ast, type, args, children, ArgNamesAst}} = Block,
ArgNames = [ArgName || {ast, type, arg, children, [ArgName]} <- ArgNamesAst],
NameWithArgs = lists:zip( ArgNames, Args),
NewFrameWithArgs = lists:foldl(fun ({Name, Arg}, EnvAcc) -> bind_lvar(Name, Arg, EnvAcc) end, Env, NameWithArgs),
Result = eval_ast(Body,NewFrameWithArgs),
Result.
pop_frame(Frame) ->
#{ret_val := RetVal, prev_frame := PrevFrame} = Frame,
erruby_rb:return(RetVal, PrevFrame).
default_env() ->
{ok, _ObjectClass} = erruby_object:init_object_class(),
{ok, _ClassClass} = erruby_class:init_class_class(),
{ok, MainObject} = erruby_object:init_main_object(),
init_builtin_class(),
#{self => MainObject, lvars => #{}}.
init_builtin_class() ->
ok = erruby_nil:install_nil_class(),
ok = erruby_array:install_array_classes(),
ok = erruby_integer:install_integer_class(),
ok = erruby_fixnum:install_fixnum_class(),
ok = erruby_boolean:install_boolean_classes(),
ok = erruby_file:install_file_classes(),
ok.
================================================
FILE: src/rb.hrl
================================================
-define(RB_DEBUG_T(T),erruby_debug:debug_tmp("~s:~p~n",[??T,T])).
================================================
FILE: test.rb
================================================
#!/usr/bin/env ruby
require 'optparse'
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: test.rb [options] test_case.rb"
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
options[:verbose] = v
end
opts.on("-h", "--help", "Print this help") do
puts(opts)
exit 1
end
end.parse!
verbose = options[:verbose]
def run_single_mri_test(fn, verbose:false)
basename = File.basename(fn,'.rb')
outname = "rb_test/sysrb_out/#{basename}.out"
puts "testing #{fn}" if verbose
system("ruby #{fn} > #{outname}")
system("./erruby #{fn} | diff #{outname} -")
end
def run_mri_tests(verbose:)
fail_case = []
if ARGV[0] && File.exist?(ARGV[0])
fn = ARGV[0]
basename = File.basename(fn,'.rb')
test_result = run_single_mri_test(fn, verbose: verbose)
fail_case << fn unless test_result
else
Dir.glob("rb_test/*.rb") do |fn|
basename = File.basename(fn,'.rb')
next if basename.start_with?("_")
unless run_single_mri_test(fn, verbose: verbose)
fail_case << fn
end
end
end
fail_case
end
fail_case = run_mri_tests(verbose: verbose)
if fail_case.empty?
puts "everything pass"
exit 0
else
fail_case.each do |fn|
puts "test #{fn} failed"
end
exit 1
end
gitextract_mz0uvchy/ ├── .gitignore ├── .gitmodules ├── .travis.yml ├── COPYING.txt ├── Gemfile ├── Guardfile ├── README.md ├── TODO.md ├── erruby ├── erruby_test/ │ ├── pipe_dot.rb │ └── undefined_method.rb ├── pre_compile ├── rb_src/ │ └── erruby.rb ├── rb_test/ │ ├── _load.rb │ ├── _require_relative.rb │ ├── and_dot.rb │ ├── array_class.rb │ ├── boolean_class.rb │ ├── class_cross_call.rb │ ├── class_def.rb │ ├── class_inherent.rb │ ├── class_method.rb │ ├── const_def.rb │ ├── file.rb │ ├── fixnum.rb │ ├── global_var.rb │ ├── hello_world.rb │ ├── integer_class.rb │ ├── ivar.rb │ ├── load.rb │ ├── method_def.rb │ ├── nested_block.rb │ ├── nested_const.rb │ ├── nil_class.rb │ ├── require_relative.rb │ ├── sysrb_out/ │ │ ├── .gitkeep │ │ ├── and_dot.out │ │ ├── array_class.out │ │ ├── boolean_class.out │ │ ├── class_cross_call.out │ │ ├── class_def.out │ │ ├── class_inherent.out │ │ ├── class_method.out │ │ ├── const_def.out │ │ ├── file.out │ │ ├── fixnum.out │ │ ├── global_var.out │ │ ├── hello_world.out │ │ ├── integer_class.out │ │ ├── ivar.out │ │ ├── load.out │ │ ├── method_def.out │ │ ├── nested_block.out │ │ ├── nested_const.out │ │ ├── nil_class.out │ │ ├── require_relative.out │ │ └── var_assign.out │ └── var_assign.rb ├── rebar.config ├── src/ │ ├── erb.erl │ ├── erruby.app.src │ ├── erruby.erl │ ├── erruby_app.erl │ ├── erruby_array.erl │ ├── erruby_boolean.erl │ ├── erruby_class.erl │ ├── erruby_debug.erl │ ├── erruby_fixnum.erl │ ├── erruby_integer.erl │ ├── erruby_lib/ │ │ └── erruby_file.erl │ ├── erruby_nil.erl │ ├── erruby_object.erl │ ├── erruby_rb.erl │ ├── erruby_sup.erl │ ├── erruby_vm.erl │ └── rb.hrl └── test.rb
SYMBOL INDEX (33 symbols across 11 files)
FILE: erruby_test/pipe_dot.rb
function cal (line 1) | def cal
function str (line 10) | def str
FILE: erruby_test/undefined_method.rb
class Foo (line 1) | class Foo
FILE: rb_src/erruby.rb
function parse (line 7) | def parse(src)
function install_encoder (line 11) | def install_encoder
FILE: rb_test/class_cross_call.rb
class Foo (line 1) | class Foo
method msg (line 2) | def msg
class Bar (line 7) | class Bar
method say (line 8) | def say
FILE: rb_test/class_def.rb
class Foo (line 1) | class Foo
method message (line 2) | def message
method hello (line 5) | def hello
class Bar (line 9) | class Bar
method message (line 10) | def message
method hello (line 13) | def hello
FILE: rb_test/class_inherent.rb
class Foo (line 1) | class Foo
method to_s (line 2) | def to_s
class Bar (line 7) | class Bar < Foo
class Alice (line 10) | class Alice < Bar
method to_s (line 11) | def to_s
FILE: rb_test/class_method.rb
class Foo (line 1) | class Foo
method bar (line 2) | def self.bar
FILE: rb_test/ivar.rb
class Foo (line 1) | class Foo
method setup (line 2) | def setup
method ivar (line 5) | def ivar
FILE: rb_test/method_def.rb
function hello_world_method_0 (line 2) | def hello_world_method_0
function hello_world_method_1 (line 6) | def hello_world_method_1(name)
function hello_world_method_block (line 11) | def hello_world_method_block(n)
function yield_with_arg (line 16) | def yield_with_arg(s,x)
FILE: rb_test/nested_const.rb
class Foo (line 1) | class Foo
class Alice (line 3) | class Alice
FILE: test.rb
function run_single_mri_test (line 19) | def run_single_mri_test(fn, verbose:false)
function run_mri_tests (line 27) | def run_mri_tests(verbose:)
Condensed preview — 77 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (71K chars).
[
{
"path": ".gitignore",
"chars": 114,
"preview": ".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",
"chars": 183,
"preview": "[submodule \"ext/ruby/spec\"]\n\tpath = ext/ruby/spec\n\turl = https://github.com/ruby/spec.git\n[submodule \"ext/ruby/mspec\"]\n\t"
},
{
"path": ".travis.yml",
"chars": 188,
"preview": "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 "
},
{
"path": "COPYING.txt",
"chars": 1081,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2015 Yu Hsiang Lin.\n\nPermission is hereby granted, free of charge, to any person ob"
},
{
"path": "Gemfile",
"chars": 252,
"preview": "ruby \"2.3.1\"\nsource \"https://rubygems.org\"\n\ngem 'erlport-ast_mapping', github: \"johnlinvc/erlport-ast_mapping\", branch: "
},
{
"path": "Guardfile",
"chars": 813,
"preview": "# A sample Guardfile\n# More info at https://github.com/guard/guard#readme\n\n## Uncomment and set this to only include dir"
},
{
"path": "README.md",
"chars": 2197,
"preview": "# ErRuby - an implementation of the Ruby language on Erlang\n[\nputs(x.to_s)\nputs(1|.to_s)\n1|.to_s\n\ndef str\n \"hello future\"\nend\nputs(sel"
},
{
"path": "erruby_test/undefined_method.rb",
"chars": 32,
"preview": "class Foo\nend\nf = Foo.new\nf.bar\n"
},
{
"path": "pre_compile",
"chars": 139,
"preview": "#!/bin/bash\npushd ./deps/erlport > /dev/null\nmake > /dev/null\npopd > /dev/null\n\npushd ./deps/plists > /dev/null\n./make.s"
},
{
"path": "rb_src/erruby.rb",
"chars": 275,
"preview": "#!/usr/bin/env ruby\n\nrequire 'rubygems'\nrequire 'bundler/setup'\nrequire 'erlport/ast_mapping'\n\ndef parse(src)\n ErlPort:"
},
{
"path": "rb_test/_load.rb",
"chars": 14,
"preview": "puts \"loaded\"\n"
},
{
"path": "rb_test/_require_relative.rb",
"chars": 21,
"preview": "puts \"required file\"\n"
},
{
"path": "rb_test/and_dot.rb",
"chars": 36,
"preview": "puts(1&.to_s)\nnil&.not_exist_method\n"
},
{
"path": "rb_test/array_class.rb",
"chars": 804,
"preview": "[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"
},
{
"path": "rb_test/boolean_class.rb",
"chars": 551,
"preview": "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)."
},
{
"path": "rb_test/class_cross_call.rb",
"chars": 107,
"preview": "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",
"chars": 220,
"preview": "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 \"hell"
},
{
"path": "rb_test/class_inherent.rb",
"chars": 205,
"preview": "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\np"
},
{
"path": "rb_test/class_method.rb",
"chars": 59,
"preview": "class Foo\n def self.bar\n puts \"bar\"\n end\nend\n\nFoo.bar\n"
},
{
"path": "rb_test/const_def.rb",
"chars": 45,
"preview": "HELLO_CONST = \"hello const\"\nputs HELLO_CONST\n"
},
{
"path": "rb_test/file.rb",
"chars": 73,
"preview": "puts __FILE__\nputs File.expand_path('../../../erruby/rb_test', __FILE__)\n"
},
{
"path": "rb_test/fixnum.rb",
"chars": 569,
"preview": "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).t"
},
{
"path": "rb_test/global_var.rb",
"chars": 32,
"preview": "$Gvar = \"hello gvar\"\nputs $Gvar\n"
},
{
"path": "rb_test/hello_world.rb",
"chars": 19,
"preview": "puts \"hello world\"\n"
},
{
"path": "rb_test/integer_class.rb",
"chars": 1035,
"preview": "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"
},
{
"path": "rb_test/ivar.rb",
"chars": 117,
"preview": "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",
"chars": 48,
"preview": "load 'rb_test/_load.rb'\nload 'rb_test/_load.rb'\n"
},
{
"path": "rb_test/method_def.rb",
"chars": 513,
"preview": "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\n"
},
{
"path": "rb_test/nested_block.rb",
"chars": 72,
"preview": "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",
"chars": 168,
"preview": "class Foo\n Bar = \"hello nested\"\n class Alice\n end\nend\nBob = \"outside bob\"\nputs \"Foo::Bar\"\nputs Foo::Bar\nFoo::Alice::B"
},
{
"path": "rb_test/nil_class.rb",
"chars": 166,
"preview": "nil\n\nnil & nil\nnil & true\nnil & false\nnil & \"\"\n\nnil ^ nil\nnil ^ true\nnil ^ false\nnil ^ \"\"\n\nnil | nil\nnil | true\nnil | fa"
},
{
"path": "rb_test/require_relative.rb",
"chars": 74,
"preview": "require_relative \"_require_relative\"\nrequire_relative \"_require_relative\"\n"
},
{
"path": "rb_test/sysrb_out/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "rb_test/sysrb_out/and_dot.out",
"chars": 2,
"preview": "1\n"
},
{
"path": "rb_test/sysrb_out/array_class.out",
"chars": 71,
"preview": "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",
"chars": 132,
"preview": "true\nfalse\nfalse\ntrue\nfalse\ntrue\nfalse\ntrue\ntrue\nfalse\nfalse\nfalse\nfalse\ntrue\ntrue\nfalse\ntrue\ntrue\ntrue\nfalse\ntrue\nfalse"
},
{
"path": "rb_test/sysrb_out/class_cross_call.out",
"chars": 6,
"preview": "hello\n"
},
{
"path": "rb_test/sysrb_out/class_def.out",
"chars": 22,
"preview": "hello world\nhello bar\n"
},
{
"path": "rb_test/sysrb_out/class_inherent.out",
"chars": 18,
"preview": "foo\nfoo\ni'm alice\n"
},
{
"path": "rb_test/sysrb_out/class_method.out",
"chars": 4,
"preview": "bar\n"
},
{
"path": "rb_test/sysrb_out/const_def.out",
"chars": 12,
"preview": "hello const\n"
},
{
"path": "rb_test/sysrb_out/file.out",
"chars": 61,
"preview": "rb_test/file.rb\n/Users/johnlinvc/Projs/erruby/erruby/rb_test\n"
},
{
"path": "rb_test/sysrb_out/fixnum.out",
"chars": 154,
"preview": "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\n"
},
{
"path": "rb_test/sysrb_out/global_var.out",
"chars": 11,
"preview": "hello gvar\n"
},
{
"path": "rb_test/sysrb_out/hello_world.out",
"chars": 12,
"preview": "hello world\n"
},
{
"path": "rb_test/sysrb_out/integer_class.out",
"chars": 173,
"preview": "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\n"
},
{
"path": "rb_test/sysrb_out/ivar.out",
"chars": 6,
"preview": "hello\n"
},
{
"path": "rb_test/sysrb_out/load.out",
"chars": 14,
"preview": "loaded\nloaded\n"
},
{
"path": "rb_test/sysrb_out/method_def.out",
"chars": 68,
"preview": "yield with\narg\nhello world no arg\nmy name is erruby\narg\nhello block\n"
},
{
"path": "rb_test/sysrb_out/nested_block.out",
"chars": 30,
"preview": "0\n0\n1\n2\n3\n1\n0\n1\n2\n3\n2\n0\n1\n2\n3\n"
},
{
"path": "rb_test/sysrb_out/nested_const.out",
"chars": 45,
"preview": "Foo::Bar\nhello nested\ninside Bob\noutside bob\n"
},
{
"path": "rb_test/sysrb_out/nil_class.out",
"chars": 0,
"preview": ""
},
{
"path": "rb_test/sysrb_out/require_relative.out",
"chars": 14,
"preview": "required file\n"
},
{
"path": "rb_test/sysrb_out/var_assign.out",
"chars": 12,
"preview": "hello world\n"
},
{
"path": "rb_test/var_assign.rb",
"chars": 33,
"preview": "hello = \"hello world\"\nputs hello\n"
},
{
"path": "rebar.config",
"chars": 361,
"preview": "{deps, [\n {erlport, \".*\", {git, \"https://github.com/johnlinvc/erlport.git\", {branch, \"erruby\"}}, [raw]},\n "
},
{
"path": "src/erb.erl",
"chars": 165,
"preview": "-module(erb).\n-export([find_or_init_class/2]).\n\nfind_or_init_class(Name, InitFun) ->\n case whereis(Name) of\n undefin"
},
{
"path": "src/erruby.app.src",
"chars": 216,
"preview": "{application, erruby,\n [\n {description, \"\"},\n {vsn, \"1\"},\n {registered, []},\n {applications, [\n ker"
},
{
"path": "src/erruby.erl",
"chars": 2562,
"preview": "-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("
},
{
"path": "src/erruby_app.erl",
"chars": 357,
"preview": "-module(erruby_app).\n\n-behaviour(application).\n\n%% Application callbacks\n-export([start/2, stop/1]).\n\n%% ==============="
},
{
"path": "src/erruby_array.erl",
"chars": 5603,
"preview": "-module(erruby_array).\n-include(\"rb.hrl\").\n-export([install_array_classes/0, new_array/2, new_array/1]).\n-export([array_"
},
{
"path": "src/erruby_boolean.erl",
"chars": 2955,
"preview": "-module(erruby_boolean).\n-export([install_boolean_classes/0,new_true/1,new_false/1,true_instance/0,false_instance/0]).\n%"
},
{
"path": "src/erruby_class.erl",
"chars": 1324,
"preview": "-module(erruby_class).\n-export([new_class/0, new_named_class/1, new_class/1, install_class_class_methods/0, init_class_c"
},
{
"path": "src/erruby_debug.erl",
"chars": 1754,
"preview": "-module(erruby_debug).\n-export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2]).\n-expo"
},
{
"path": "src/erruby_fixnum.erl",
"chars": 3585,
"preview": "-module(erruby_fixnum).\n-export([install_fixnum_class/0, new_fixnum/2, fix_to_int/1]).\n\n%%\n%% @TODO inherent from intege"
},
{
"path": "src/erruby_integer.erl",
"chars": 4345,
"preview": "-module(erruby_integer).\n-export([install_integer_class/0]).\n\ninstall_integer_class() ->\n {ok, IntegerClass} = erruby_c"
},
{
"path": "src/erruby_lib/erruby_file.erl",
"chars": 1154,
"preview": "-module(erruby_file).\n-include(\"../rb.hrl\").\n-export([install_file_classes/0]).\n\ninstall_file_classes() ->\n {ok, FileCl"
},
{
"path": "src/erruby_nil.erl",
"chars": 1229,
"preview": "-module(erruby_nil).\n-export([new_nil/1, install_nil_class/0, nil_instance/0]).\n\ninstall_nil_class() ->\n {ok, NilClass}"
},
{
"path": "src/erruby_object.erl",
"chars": 12962,
"preview": "-module(erruby_object).\n-include(\"rb.hrl\").\n-behavior(gen_server).\n-export([init/1, terminate/2, code_change/3, handle_c"
},
{
"path": "src/erruby_rb.erl",
"chars": 257,
"preview": "%%% TODO move general runtime api to here\n-module(erruby_rb).\n-export([ret_self/1,ret_val/1,return/2]).\n\nret_self( #{sel"
},
{
"path": "src/erruby_sup.erl",
"chars": 712,
"preview": "-module(erruby_sup).\n\n-behaviour(supervisor).\n\n%% API\n-export([start_link/0]).\n\n%% Supervisor callbacks\n-export([init/1]"
},
{
"path": "src/erruby_vm.erl",
"chars": 12075,
"preview": "-module(erruby_vm).\n-include(\"rb.hrl\").\n-export([eval_file/2, scanl/3]).\n-export([new_nil/1, new_string/2]).\n-export([ev"
},
{
"path": "src/rb.hrl",
"chars": 66,
"preview": "-define(RB_DEBUG_T(T),erruby_debug:debug_tmp(\"~s:~p~n\",[??T,T])).\n"
},
{
"path": "test.rb",
"chars": 1259,
"preview": "#!/usr/bin/env ruby\nrequire 'optparse'\n\noptions = {}\n\nOptionParser.new do |opts|\n opts.banner = \"Usage: test.rb [option"
}
]
About this extraction
This page contains the full source code of the johnlinvc/erruby GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 77 files (63.1 KB), approximately 21.1k tokens, and a symbol index with 33 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.