fastruby 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|