rusby 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,54 @@
1
+ module Rusby
2
+ module Generators
3
+ module Loops
4
+ def generate_loop(ast)
5
+ statements = ast.children[1..-1].map { |node| generate(node) }.compact
6
+ "loop {\n#{statements.join("\n")}\n}"
7
+ end
8
+
9
+ def generate_each_loop(ast)
10
+ if ast.children[0].children[0].type == :lvar
11
+ generate_each_loop_plain(ast)
12
+ else
13
+ generate_each_loop_range(ast)
14
+ end
15
+ end
16
+
17
+ def generate_each_loop_plain(ast)
18
+ collection = ast.children[0].children[0].children[0]
19
+ variable = ast.children[1].children[0].children[0]
20
+ body = ast.children[2..-1].map { |node| generate(node) }.join("\n")
21
+ "for #{variable} in #{collection}.iter() {
22
+ #{body}
23
+ }"
24
+ end
25
+
26
+ def generate_each_with_index_loop(ast)
27
+ collection = ast.children[0].children[0].children[0]
28
+ variable = ast.children[1].children[0].children[0]
29
+ index = ast.children[1].children[1].children[0]
30
+ body = ast.children[2..-1].map { |node| generate(node) }.join("\n")
31
+ "for (#{index}, #{variable}) in #{collection}.iter().enumerate() {
32
+ #{body}
33
+ }"
34
+ end
35
+
36
+ def generate_each_loop_range(ast)
37
+ range = ast.children[0].children[0].children[0]
38
+ range_start = range.children[0].children[0]
39
+ range_end = "(#{range.children[1].children[0]} + 1)" # rust range is inclusive
40
+ range_variable = ast.children[1].children[0].children[0]
41
+ statements = ast.children[1..-1].map { |node| generate(node) }.compact
42
+ "for #{range_variable} in #{range_start}..#{range_end} {\n#{statements.join("\n")}\n}"
43
+ end
44
+
45
+ def generate_while(ast)
46
+ "while #{generate(ast.children[0])} {\n#{generate(ast.children[1])}\n}"
47
+ end
48
+
49
+ def generate_while_post(ast)
50
+ "while {\n#{generate(ast.children[1])};\n#{generate(ast.children[0])}\n}{}"
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,26 @@
1
+ module Rusby
2
+ module Generators
3
+ module Misc
4
+ def generate_begin(ast)
5
+ ast.children.map { |node| generate(node) }.join("\n")
6
+ end
7
+
8
+ def generate_args(_ast)
9
+ end
10
+
11
+ def generate_and(ast)
12
+ "#{generate(ast.children[0])} && #{generate(ast.children[1])}"
13
+ end
14
+
15
+ def generate_return(ast)
16
+ statements = ast.children.map { |node| generate(node) }
17
+ "return #{statements.any? ? statements.join(',') : '&-ptr'} as #{@return_type};"
18
+ end
19
+
20
+ def generate_kwbegin(ast)
21
+ statements = ast.children.map { |node| generate(node) }
22
+ statements.join(';')
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,10 @@
1
+ module Rusby
2
+ module Generators
3
+ module Strings
4
+ # string interpolation
5
+ def generate_dstr(ast)
6
+ ast.children.map { |node| "(#{generate(node)}).to_string()" }.join(' + &')
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,33 @@
1
+ module Rusby
2
+ module Generators
3
+ module Types
4
+ def generate_int(ast)
5
+ ast.children[0]
6
+ end
7
+
8
+ def generate_float(ast)
9
+ ast.children[0]
10
+ end
11
+
12
+ def generate_str(ast)
13
+ "\"#{ast.children[0]}\""
14
+ end
15
+
16
+ def generate_true(_ast)
17
+ 'true'
18
+ end
19
+
20
+ def generate_false(_ast)
21
+ 'false'
22
+ end
23
+
24
+ def generate_array(_ast)
25
+ 'Vec::new()'
26
+ end
27
+
28
+ def generate_const(ast)
29
+ '<array>' if ast.children[1] == :Array
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,19 @@
1
+ module Rusby
2
+ module Postrocessor
3
+ extend self
4
+
5
+ def apply(code, meta)
6
+ # fold the array syntax
7
+ meta[:args].each_with_index do |el, idx|
8
+ if el == 'String'
9
+ code.gsub!(/#{meta[:names][idx]}\[(.+?)\]/, "#{meta[:names][idx]}.chars().nth(\\1)")
10
+ code.gsub!(/;+/, ';')
11
+ end
12
+ end
13
+ # process array :<< and :>> operator
14
+ code.gsub!(/(?:<<|>>)(\S+)/, '.push(\1);')
15
+
16
+ code
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,15 @@
1
+ module Rusby
2
+ module Preprocessor
3
+ extend self
4
+
5
+ def apply(code)
6
+ code = code.gsub(
7
+ /(\w+)\s?=\s?Array\.new\((.*)\)\s?{\s?Array\.new\((.*)\)\s?}/,
8
+ 'rust_variable :\1' \
9
+ "\n" \
10
+ 'rust "let mut \1 = vec![vec![0; \3]; \2];"'
11
+ )
12
+ code
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,32 @@
1
+ module Rusby
2
+ module Profiler
3
+ extend self
4
+
5
+ SQRT_ITERATIONS = Math.sqrt(1000).to_i
6
+
7
+ def timeit(target_method, *args)
8
+ Benchmark.realtime { SQRT_ITERATIONS.times { target_method.call(*args) } }
9
+ end
10
+
11
+ def benchit(obj, original_method, modified_method, args)
12
+ original_method = original_method.bind(obj)
13
+
14
+ m1 = 0
15
+ m2 = 0
16
+
17
+ SQRT_ITERATIONS.times do |i|
18
+ m1 += timeit(original_method, *args)
19
+ m2 += timeit(modified_method, *args)
20
+
21
+ percent = ((i + 1).to_f / SQRT_ITERATIONS * 100).to_i
22
+ print "\r|#{'=' * percent}#{'-' * (100 - percent)}|"
23
+ end
24
+
25
+ boost = m1 > m2 ? m1 / m2 : - m2 / m1
26
+
27
+ printf "\r=> got #{'%.2fx boost'.colorize(boost > 0 ? :green : :red)} (%.2fs original vs %.2fs rust) %s\n\n", boost, m1, m2, ' ' * 80
28
+
29
+ boost
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,14 @@
1
+ module Rusby
2
+ module Proxy
3
+ extend ::FFI::Library
4
+
5
+ def self.libext
6
+ return 'dylib' if `uname` =~ /Darwin/
7
+ return 'so' if `uname` =~ /Linux/
8
+ end
9
+
10
+ def self.rusby_load(fullpath)
11
+ ffi_lib "#{fullpath}.#{libext}"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,69 @@
1
+ module Rusby
2
+ class Rust
3
+ include ::Rusby::Generators::Base
4
+
5
+ include ::Rusby::Generators::Assignments
6
+ include ::Rusby::Generators::Conditionals
7
+ include ::Rusby::Generators::Loops
8
+ include ::Rusby::Generators::Misc
9
+ include ::Rusby::Generators::Strings
10
+ include ::Rusby::Generators::Types
11
+
12
+ def initialize(return_type)
13
+ @known_methods = []
14
+ @known_variables = []
15
+ @return_type = return_type
16
+ end
17
+
18
+ def known_method?(name)
19
+ @known_methods.include?(name.to_sym)
20
+ end
21
+
22
+ def remember_method(name)
23
+ @known_methods << name.to_sym
24
+ end
25
+
26
+ def known_variable?(name)
27
+ @known_variables.include?(name.to_sym)
28
+ end
29
+
30
+ def remember_variable(name)
31
+ @known_variables << name.to_sym
32
+ end
33
+
34
+ def fold_arrays(nodes)
35
+ result = []
36
+ index_op = false
37
+ index_assignment_op = false
38
+
39
+ nodes.each do |node|
40
+ case node
41
+ when :[]
42
+ index_op = true
43
+ when :[]=
44
+ index_assignment_op = true
45
+ else
46
+ code = generate(node)
47
+ if index_op
48
+ code = "[#{code.to_s =~ /[+-]/ ? "(#{code})" : code} as usize]"
49
+ index_op = false
50
+ end
51
+ if index_assignment_op
52
+ code = "[#{code.to_s =~ /[+-]/ ? "(#{code})" : code} as usize]="
53
+ index_assignment_op = false
54
+ end
55
+ result << code
56
+ end
57
+ end
58
+
59
+ result
60
+ end
61
+
62
+ def method_missing(method_name, *args)
63
+ puts "No method for '#{method_name.to_s.sub('generate_', '')}' AST node.".colorize(:red)
64
+ puts "Please implement #{method_name} in Rusby::Rust.".colorize(:yellow)
65
+ puts "Arguments: #{args.inspect}".colorize(:yellow)
66
+ raise RuntimeError
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,3 @@
1
+ module Rusby
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rusby'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rusby"
8
+ spec.version = Rusby::VERSION
9
+ spec.authors = ["Alexander Krasnoschekov"]
10
+ spec.email = ["akrasnoschekov@gmail.com"]
11
+
12
+ spec.summary = %q{Ruby to Rust transpiler for simple performance-oriented methods.}
13
+ spec.homepage = "https://github.com/rambler-digital-solutions/rusby"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.12"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec", "~> 3.0"
24
+ spec.add_development_dependency "byebug"
25
+
26
+ spec.add_runtime_dependency 'method_source'
27
+ spec.add_runtime_dependency 'ffi'
28
+ spec.add_runtime_dependency 'hashie'
29
+ spec.add_runtime_dependency 'colorize'
30
+ spec.add_runtime_dependency 'parser'
31
+ spec.add_runtime_dependency 'ruby-prof'
32
+ end
@@ -0,0 +1,38 @@
1
+ ffi_to_rust:
2
+ String: 'let <name> = unsafe { CStr::from_ptr(<name>_pointer).to_string_lossy().into_owned() };'
3
+ Array[Fixnum]: 'let <name> = unsafe { std::slice::from_raw_parts_mut(<name>_pointer, <name>_size) };'
4
+ Array[Float]: 'let <name> = unsafe { std::slice::from_raw_parts_mut(<name>_pointer, <name>_size) };'
5
+ ffi_to_rust_types:
6
+ String: '<name>_pointer: *const c_char'
7
+ Array[Fixnum]: '<name>_pointer: *mut i32, <name>_size: usize'
8
+ Array[Float]: '<name>_pointer: *mut f64, <name>_size: usize'
9
+ rust_to_ffi:
10
+ String: 'CString::new(result).unwrap().as_ptr()'
11
+ Array[Fixnum]: 'result.as_ptr()'
12
+ Array[Float]: 'result.as_ptr()'
13
+ rust_to_ffi_types:
14
+ Array[Fixnum]: '*const i32'
15
+ Array[Float]: '*const f64'
16
+ String: '*const c_char'
17
+ rust_types:
18
+ Fixnum: 'i32'
19
+ Array[Fixnum]: '&mut [i32]'
20
+ Array[Float]: '&mut [f64]'
21
+ NilClass: '()'
22
+ String: 'String'
23
+ Float: 'f64'
24
+ ffi_types:
25
+ Fixnum: 'int'
26
+ Array[Fixnum]: 'pointer'
27
+ Array[Float]: 'pointer'
28
+ NilClass: 'void'
29
+ String: 'string'
30
+ Float: 'double'
31
+ file_header: |
32
+ use std::io::{self, Write};
33
+ use std::ffi::{CStr,CString};
34
+ use std::os::raw::c_char;
35
+ method_prefix: "\n#[allow(unused_mut)]"
36
+ exposed_method_prefix: |
37
+ #[no_mangle]
38
+ pub extern "C"
metadata ADDED
@@ -0,0 +1,223 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rusby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Alexander Krasnoschekov
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-08-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: byebug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: method_source
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: ffi
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: hashie
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: colorize
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: parser
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: ruby-prof
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description:
154
+ email:
155
+ - akrasnoschekov@gmail.com
156
+ executables: []
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - ".gitignore"
161
+ - ".travis.yml"
162
+ - CODE_OF_CONDUCT.md
163
+ - Gemfile
164
+ - Gemfile.lock
165
+ - LICENSE.txt
166
+ - README.md
167
+ - Rakefile
168
+ - bin/console
169
+ - bin/setup
170
+ - doc/img1.png
171
+ - doc/slides.key
172
+ - examples/Gemfile
173
+ - examples/Gemfile.lock
174
+ - examples/fanatic_greeter.rb
175
+ - examples/fanatic_pluser.rb
176
+ - examples/levenshtein_distance.rb
177
+ - examples/quicksort.rb
178
+ - examples/run_examples.rb
179
+ - examples/weighted_random.rb
180
+ - lib/rusby.rb
181
+ - lib/rusby/builder.rb
182
+ - lib/rusby/core.rb
183
+ - lib/rusby/ffi/bridge.rb
184
+ - lib/rusby/generators/assignments.rb
185
+ - lib/rusby/generators/base.rb
186
+ - lib/rusby/generators/conditionals.rb
187
+ - lib/rusby/generators/loops.rb
188
+ - lib/rusby/generators/misc.rb
189
+ - lib/rusby/generators/strings.rb
190
+ - lib/rusby/generators/types.rb
191
+ - lib/rusby/postprocessor.rb
192
+ - lib/rusby/preprocessor.rb
193
+ - lib/rusby/profiler.rb
194
+ - lib/rusby/proxy.rb
195
+ - lib/rusby/rust.rb
196
+ - lib/rusby/version.rb
197
+ - rusby.gemspec
198
+ - rust.yaml
199
+ homepage: https://github.com/rambler-digital-solutions/rusby
200
+ licenses:
201
+ - MIT
202
+ metadata: {}
203
+ post_install_message:
204
+ rdoc_options: []
205
+ require_paths:
206
+ - lib
207
+ required_ruby_version: !ruby/object:Gem::Requirement
208
+ requirements:
209
+ - - ">="
210
+ - !ruby/object:Gem::Version
211
+ version: '0'
212
+ required_rubygems_version: !ruby/object:Gem::Requirement
213
+ requirements:
214
+ - - ">="
215
+ - !ruby/object:Gem::Version
216
+ version: '0'
217
+ requirements: []
218
+ rubyforge_project:
219
+ rubygems_version: 2.4.5
220
+ signing_key:
221
+ specification_version: 4
222
+ summary: Ruby to Rust transpiler for simple performance-oriented methods.
223
+ test_files: []