fastruby 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/AUTHORS +2 -0
- data/LICENSE +674 -0
- data/README +142 -0
- data/Rakefile +47 -0
- data/TODO +31 -0
- data/benchmarks/benchmark.rb +68 -0
- data/benchmarks/benchmark2.rb +70 -0
- data/benchmarks/benchmark3.rb +70 -0
- data/benchmarks/benchmark4.rb +94 -0
- data/benchmarks/benchmark5.rb +80 -0
- data/benchmarks/benchmark6.rb +64 -0
- data/examples/example1.rb +12 -0
- data/examples/example2.rb +14 -0
- data/examples/example3.rb +12 -0
- data/examples/example4.rb +17 -0
- data/lib/fastruby/builder.rb +97 -0
- data/lib/fastruby/exceptions.rb +24 -0
- data/lib/fastruby/getlocals.rb +47 -0
- data/lib/fastruby/inline_extension.rb +87 -0
- data/lib/fastruby/logging.rb +37 -0
- data/lib/fastruby/object.rb +168 -0
- data/lib/fastruby/translator.rb +903 -0
- data/lib/fastruby.rb +24 -0
- data/spec/base_spec.rb +357 -0
- data/spec/block_spec.rb +418 -0
- data/spec/control_spec.rb +71 -0
- data/spec/expression_spec.rb +59 -0
- data/spec/integrity_spec.rb +74 -0
- data/spec/literal_spec.rb +95 -0
- data/spec/variable_spec.rb +63 -0
- metadata +127 -0
@@ -0,0 +1,97 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
This file is part of the fastruby project, http://github.com/tario/fastruby
|
4
|
+
|
5
|
+
Copyright (c) 2011 Roberto Dario Seminara <robertodarioseminara@gmail.com>
|
6
|
+
|
7
|
+
fastruby is free software: you can redistribute it and/or modify
|
8
|
+
it under the terms of the gnu general public license as published by
|
9
|
+
the free software foundation, either version 3 of the license, or
|
10
|
+
(at your option) any later version.
|
11
|
+
|
12
|
+
fastruby is distributed in the hope that it will be useful,
|
13
|
+
but without any warranty; without even the implied warranty of
|
14
|
+
merchantability or fitness for a particular purpose. see the
|
15
|
+
gnu general public license for more details.
|
16
|
+
|
17
|
+
you should have received a copy of the gnu general public license
|
18
|
+
along with fastruby. if not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
=end
|
21
|
+
require "fastruby/translator"
|
22
|
+
require "fastruby/inline_extension"
|
23
|
+
require "fastruby/logging"
|
24
|
+
|
25
|
+
module FastRuby
|
26
|
+
module BuilderModule
|
27
|
+
def build(signature, method_name)
|
28
|
+
tree = self.method_tree[method_name]
|
29
|
+
locals = self.method_locals[method_name]
|
30
|
+
options = self.method_options[method_name]
|
31
|
+
mname = "_" + method_name.to_s + signature.map(&:internal_value).map(&:to_s).join
|
32
|
+
|
33
|
+
FastRuby.logger.info mname.to_s
|
34
|
+
|
35
|
+
begin
|
36
|
+
if (self.instance_method(mname))
|
37
|
+
FastRuby.logger.info "NOT Building #{self}::#{method_name} for signature #{signature.inspect}, it's already done"
|
38
|
+
return
|
39
|
+
end
|
40
|
+
rescue NameError
|
41
|
+
FastRuby.logger.info "Building #{self}::#{method_name} for signature #{signature.inspect}"
|
42
|
+
end
|
43
|
+
|
44
|
+
context = FastRuby::Context.new
|
45
|
+
context.locals = locals
|
46
|
+
context.options = options
|
47
|
+
|
48
|
+
args_tree = tree[2]
|
49
|
+
|
50
|
+
# create random method name
|
51
|
+
context.alt_method_name = mname
|
52
|
+
|
53
|
+
(1..signature.size).each do |i|
|
54
|
+
arg = args_tree[i]
|
55
|
+
context.infer_lvar_map[arg] = signature[i]
|
56
|
+
end
|
57
|
+
|
58
|
+
context.infer_self = signature[0]
|
59
|
+
|
60
|
+
c_code = context.to_c(tree)
|
61
|
+
|
62
|
+
inline :C do |builder|
|
63
|
+
builder.inc << context.extra_code
|
64
|
+
builder.include "<node.h>"
|
65
|
+
builder.c c_code
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
ret = instance_method(mname)
|
70
|
+
|
71
|
+
ret.extend MethodExtent
|
72
|
+
ret.yield_signature = context.yield_signature
|
73
|
+
|
74
|
+
ret
|
75
|
+
end
|
76
|
+
|
77
|
+
module MethodExtent
|
78
|
+
attr_accessor :yield_signature
|
79
|
+
end
|
80
|
+
|
81
|
+
def method_tree
|
82
|
+
@method_tree = Hash.new unless @method_tree
|
83
|
+
@method_tree
|
84
|
+
end
|
85
|
+
|
86
|
+
def method_locals
|
87
|
+
@method_locals = Hash.new unless @method_locals
|
88
|
+
@method_locals
|
89
|
+
end
|
90
|
+
|
91
|
+
def method_options
|
92
|
+
@method_options = Hash.new unless @method_options
|
93
|
+
@method_options
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
This file is part of the fastruby project, http://github.com/tario/fastruby
|
4
|
+
|
5
|
+
Copyright (c) 2011 Roberto Dario Seminara <robertodarioseminara@gmail.com>
|
6
|
+
|
7
|
+
fastruby is free software: you can redistribute it and/or modify
|
8
|
+
it under the terms of the gnu general public license as published by
|
9
|
+
the free software foundation, either version 3 of the license, or
|
10
|
+
(at your option) any later version.
|
11
|
+
|
12
|
+
fastruby is distributed in the hope that it will be useful,
|
13
|
+
but without any warranty; without even the implied warranty of
|
14
|
+
merchantability or fitness for a particular purpose. see the
|
15
|
+
gnu general public license for more details.
|
16
|
+
|
17
|
+
you should have received a copy of the gnu general public license
|
18
|
+
along with fastruby. if not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
=end
|
21
|
+
module FastRuby
|
22
|
+
class TypeMismatchAssignmentException < Exception
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
This file is part of the fastruby project, http://github.com/tario/fastruby
|
4
|
+
|
5
|
+
Copyright (c) 2011 Roberto Dario Seminara <robertodarioseminara@gmail.com>
|
6
|
+
|
7
|
+
fastruby is free software: you can redistribute it and/or modify
|
8
|
+
it under the terms of the gnu general public license as published by
|
9
|
+
the free software foundation, either version 3 of the license, or
|
10
|
+
(at your option) any later version.
|
11
|
+
|
12
|
+
fastruby is distributed in the hope that it will be useful,
|
13
|
+
but without any warranty; without even the implied warranty of
|
14
|
+
merchantability or fitness for a particular purpose. see the
|
15
|
+
gnu general public license for more details.
|
16
|
+
|
17
|
+
you should have received a copy of the gnu general public license
|
18
|
+
along with fastruby. if not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
=end
|
21
|
+
require "rubygems"
|
22
|
+
require "sexp_processor"
|
23
|
+
require "set"
|
24
|
+
|
25
|
+
module FastRuby
|
26
|
+
class GetLocalsProcessor < SexpProcessor
|
27
|
+
|
28
|
+
attr_reader :locals
|
29
|
+
|
30
|
+
def initialize
|
31
|
+
super()
|
32
|
+
self.require_empty = false
|
33
|
+
@locals = Set.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def process_lasgn(tree)
|
37
|
+
@locals << tree[1]
|
38
|
+
tree.dup
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.get_locals(tree)
|
42
|
+
processor = GetLocalsProcessor.new
|
43
|
+
processor.process(tree)
|
44
|
+
processor.locals
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
This file is part of the fastruby project, http://github.com/tario/fastruby
|
4
|
+
|
5
|
+
Copyright (c) 2011 Roberto Dario Seminara <robertodarioseminara@gmail.com>
|
6
|
+
|
7
|
+
fastruby is free software: you can redistribute it and/or modify
|
8
|
+
it under the terms of the gnu general public license as published by
|
9
|
+
the free software foundation, either version 3 of the license, or
|
10
|
+
(at your option) any later version.
|
11
|
+
|
12
|
+
fastruby is distributed in the hope that it will be useful,
|
13
|
+
but without any warranty; without even the implied warranty of
|
14
|
+
merchantability or fitness for a particular purpose. see the
|
15
|
+
gnu general public license for more details.
|
16
|
+
|
17
|
+
you should have received a copy of the gnu general public license
|
18
|
+
along with fastruby. if not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
=end
|
21
|
+
module Inline
|
22
|
+
class C
|
23
|
+
attr_reader :inc
|
24
|
+
|
25
|
+
def generate(src, options={})
|
26
|
+
options = {:expand_types=>options} unless Hash === options
|
27
|
+
|
28
|
+
expand_types = options[:expand_types]
|
29
|
+
singleton = options[:singleton]
|
30
|
+
result = self.strip_comments(src)
|
31
|
+
|
32
|
+
signature = parse_signature(src, !expand_types)
|
33
|
+
function_name = signature['name']
|
34
|
+
method_name = options[:method_name]
|
35
|
+
method_name ||= test_to_normal function_name
|
36
|
+
return_type = signature['return']
|
37
|
+
arity = options[:arity] || signature['arity']
|
38
|
+
|
39
|
+
raise ArgumentError, "too many arguments" if arity > MAGIC_ARITY_THRESHOLD
|
40
|
+
|
41
|
+
if expand_types then
|
42
|
+
prefix = "static VALUE #{function_name}("
|
43
|
+
if arity <= MAGIC_ARITY then
|
44
|
+
prefix += "int argc, VALUE *argv, VALUE self"
|
45
|
+
else
|
46
|
+
prefix += "VALUE self"
|
47
|
+
prefix += signature['args'].map { |arg, type| ", VALUE _#{arg}"}.join
|
48
|
+
end
|
49
|
+
prefix += ") {\n"
|
50
|
+
prefix += signature['args'].map { |arg, type|
|
51
|
+
" #{type} #{arg} = #{ruby2c(type)}(_#{arg});\n"
|
52
|
+
}.join
|
53
|
+
|
54
|
+
# replace the function signature (hopefully) with new sig (prefix)
|
55
|
+
result.sub!(/[^;\/\"\>]+#{function_name}\s*\([^\{]+\{/, "\n" + prefix)
|
56
|
+
result.sub!(/\A\n/, '') # strip off the \n in front in case we added it
|
57
|
+
unless return_type == "void" then
|
58
|
+
raise SyntaxError, "Couldn't find return statement for #{function_name}" unless
|
59
|
+
result =~ /return/
|
60
|
+
else
|
61
|
+
result.sub!(/\s*\}\s*\Z/, "\nreturn Qnil;\n}")
|
62
|
+
end
|
63
|
+
else
|
64
|
+
prefix = "static #{return_type} #{function_name}("
|
65
|
+
result.sub!(/[^;\/\"\>]+#{function_name}\s*\(/, prefix)
|
66
|
+
result.sub!(/\A\n/, '') # strip off the \n in front in case we added it
|
67
|
+
end
|
68
|
+
|
69
|
+
delta = if result =~ /\A(static.*?\{)/m then
|
70
|
+
$1.split(/\n/).size
|
71
|
+
else
|
72
|
+
warn "WAR\NING: Can't find signature in #{result.inspect}\n" unless $TESTING
|
73
|
+
0
|
74
|
+
end
|
75
|
+
|
76
|
+
file, line = caller[1].split(/:/)
|
77
|
+
result = "# line #{line.to_i + delta} \"#{file}\"\n" + result unless $DEBUG and not $TESTING
|
78
|
+
|
79
|
+
@src << result
|
80
|
+
@sig[function_name] = [arity,singleton,method_name]
|
81
|
+
|
82
|
+
return result if $TESTING
|
83
|
+
end # def generate
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
This file is part of the fastruby project, http://github.com/tario/fastruby
|
4
|
+
|
5
|
+
Copyright (c) 2011 Roberto Dario Seminara <robertodarioseminara@gmail.com>
|
6
|
+
|
7
|
+
fastruby is free software: you can redistribute it and/or modify
|
8
|
+
it under the terms of the gnu general public license as published by
|
9
|
+
the free software foundation, either version 3 of the license, or
|
10
|
+
(at your option) any later version.
|
11
|
+
|
12
|
+
fastruby is distributed in the hope that it will be useful,
|
13
|
+
but without any warranty; without even the implied warranty of
|
14
|
+
merchantability or fitness for a particular purpose. see the
|
15
|
+
gnu general public license for more details.
|
16
|
+
|
17
|
+
you should have received a copy of the gnu general public license
|
18
|
+
along with fastruby. if not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
=end
|
21
|
+
require "logger"
|
22
|
+
|
23
|
+
module FastRuby
|
24
|
+
module SingletonLogger
|
25
|
+
def logger
|
26
|
+
unless @logger
|
27
|
+
@logger = Logger.new(ENV['FASTRUBY_LOG'] || "/dev/stdout")
|
28
|
+
@logger.level = (ENV['FASTRUBY_LOG_LEVEL']||"2").to_i
|
29
|
+
end
|
30
|
+
@logger
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class << self
|
35
|
+
include SingletonLogger
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
This file is part of the fastruby project, http://github.com/tario/fastruby
|
4
|
+
|
5
|
+
Copyright (c) 2011 Roberto Dario Seminara <robertodarioseminara@gmail.com>
|
6
|
+
|
7
|
+
fastruby is free software: you can redistribute it and/or modify
|
8
|
+
it under the terms of the gnu general public license as published by
|
9
|
+
the free software foundation, either version 3 of the license, or
|
10
|
+
(at your option) any later version.
|
11
|
+
|
12
|
+
fastruby is distributed in the hope that it will be useful,
|
13
|
+
but without any warranty; without even the implied warranty of
|
14
|
+
merchantability or fitness for a particular purpose. see the
|
15
|
+
gnu general public license for more details.
|
16
|
+
|
17
|
+
you should have received a copy of the gnu general public license
|
18
|
+
along with fastruby. if not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
=end
|
21
|
+
require "fastruby/translator"
|
22
|
+
require "fastruby/builder"
|
23
|
+
require "fastruby/getlocals"
|
24
|
+
require "ruby_parser"
|
25
|
+
require "inline"
|
26
|
+
|
27
|
+
# clean rubyinline cache
|
28
|
+
system("rm -fr #{ENV["HOME"]}/.ruby_inline/*")
|
29
|
+
|
30
|
+
class Object
|
31
|
+
def self.fastruby(rubycode, *options_hashes)
|
32
|
+
tree = RubyParser.new.parse rubycode
|
33
|
+
|
34
|
+
options_hash = {:validate_lvar_types => true}
|
35
|
+
options_hashes.each do |opt|
|
36
|
+
options_hash.merge!(opt)
|
37
|
+
end
|
38
|
+
|
39
|
+
if tree[0] != :defn
|
40
|
+
raise ArgumentError, "Only definition of methods are accepted"
|
41
|
+
end
|
42
|
+
|
43
|
+
method_name = tree[1]
|
44
|
+
args_tree = tree[2]
|
45
|
+
|
46
|
+
hashname = "$hash" + rand(1000000).to_s
|
47
|
+
|
48
|
+
hash = Hash.new
|
49
|
+
|
50
|
+
locals = Set.new
|
51
|
+
locals << :self
|
52
|
+
|
53
|
+
FastRuby::GetLocalsProcessor.get_locals(RubyParser.new.parse(rubycode)).each do |local|
|
54
|
+
locals << local
|
55
|
+
end
|
56
|
+
|
57
|
+
self_ = self
|
58
|
+
hash.instance_eval{@klass = self_}
|
59
|
+
hash.instance_eval{@method_name = method_name}
|
60
|
+
|
61
|
+
class_eval do
|
62
|
+
class << self
|
63
|
+
include FastRuby::BuilderModule
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
self_.method_tree[method_name] = tree
|
68
|
+
self_.method_locals[method_name] = locals
|
69
|
+
self_.method_options[method_name] = options_hash
|
70
|
+
|
71
|
+
def hash.build(key)
|
72
|
+
@klass.build(key, @method_name)
|
73
|
+
end
|
74
|
+
|
75
|
+
eval("#{hashname} = hash")
|
76
|
+
|
77
|
+
value_cast = ( ["VALUE"]*(args_tree.size+1) ).join(",")
|
78
|
+
|
79
|
+
main_signature_argument = args_tree[1..-1].first || "self"
|
80
|
+
|
81
|
+
strmethodargs = ""
|
82
|
+
strmethodargs_class = (["self"] + args_tree[1..-1]).map{|arg| "CLASS_OF(#{arg.to_s})"}.join(",")
|
83
|
+
|
84
|
+
if args_tree.size > 1
|
85
|
+
strmethodargs = "self,block,#{args_tree[1..-1].map(&:to_s).join(",") }"
|
86
|
+
else
|
87
|
+
strmethodargs = "self,block"
|
88
|
+
end
|
89
|
+
|
90
|
+
strmethod_signature = (["self"] + args_tree[1..-1]).map { |arg|
|
91
|
+
"sprintf(method_name+strlen(method_name), \"%lu\", CLASS_OF(#{arg}));\n"
|
92
|
+
}.join
|
93
|
+
|
94
|
+
c_code = "VALUE #{method_name}(#{args_tree[1..-1].map{|arg| "VALUE #{arg}" }.join(",")}) {
|
95
|
+
VALUE method_hash = (VALUE)#{hash.internal_value};
|
96
|
+
VALUE klass = (VALUE)#{self.internal_value};
|
97
|
+
|
98
|
+
char method_name[0x100];
|
99
|
+
|
100
|
+
method_name[0] = '_';
|
101
|
+
method_name[1] = 0;
|
102
|
+
|
103
|
+
sprintf(method_name+1, \"#{method_name}\");
|
104
|
+
#{strmethod_signature}
|
105
|
+
|
106
|
+
NODE* body;
|
107
|
+
ID id;
|
108
|
+
|
109
|
+
id = rb_intern(method_name);
|
110
|
+
body = rb_method_node(klass,id);
|
111
|
+
|
112
|
+
if (body == 0) {
|
113
|
+
VALUE argv_class[] = {#{strmethodargs_class} };
|
114
|
+
VALUE signature = rb_ary_new4(#{args_tree.size},argv_class);
|
115
|
+
|
116
|
+
rb_funcall(method_hash, #{:build.to_i}, 1, signature);
|
117
|
+
body = rb_method_node(klass,id);
|
118
|
+
}
|
119
|
+
|
120
|
+
if (nd_type(body) == NODE_CFUNC) {
|
121
|
+
int argc = body->nd_argc;
|
122
|
+
|
123
|
+
VALUE block = Qfalse;
|
124
|
+
|
125
|
+
if (rb_block_given_p()) {
|
126
|
+
struct {
|
127
|
+
void *block_function_address;
|
128
|
+
void *block_function_param;
|
129
|
+
} block_struct;
|
130
|
+
|
131
|
+
block_struct.block_function_address = re_yield;
|
132
|
+
block_struct.block_function_param = 0;
|
133
|
+
|
134
|
+
block = (VALUE)&block_struct;
|
135
|
+
}
|
136
|
+
|
137
|
+
if (argc == #{args_tree.size}) {
|
138
|
+
return ((VALUE(*)(#{value_cast}))body->nd_cfnc)(#{strmethodargs});
|
139
|
+
} else if (argc == -1) {
|
140
|
+
VALUE argv[] = {#{(["block"]+args_tree[1..-1]).map(&:to_s).join(",")} };
|
141
|
+
return ((VALUE(*)(int,VALUE*,VALUE))body->nd_cfnc)(#{args_tree.size},argv,self);
|
142
|
+
} else if (argc == -2) {
|
143
|
+
VALUE argv[] = {#{(["block"]+args_tree[1..-1]).map(&:to_s).join(",")} };
|
144
|
+
return ((VALUE(*)(VALUE,VALUE))body->nd_cfnc)(self, rb_ary_new4(#{args_tree.size},argv));
|
145
|
+
} else {
|
146
|
+
rb_raise(rb_eArgError, \"wrong number of arguments (#{args_tree.size-1} for %d)\", argc);
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
return Qnil;
|
151
|
+
}"
|
152
|
+
|
153
|
+
inline :C do |builder|
|
154
|
+
builder.include "<node.h>"
|
155
|
+
builder.inc << "static VALUE re_yield(int argc, VALUE* argv, VALUE param) {
|
156
|
+
return rb_yield_splat(rb_ary_new4(argc,argv));
|
157
|
+
}"
|
158
|
+
builder.c c_code
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
inline :C do |builder|
|
163
|
+
builder.c "VALUE internal_value() {
|
164
|
+
return INT2FIX(self);
|
165
|
+
}"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|