maca-ruby2js 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 0.0.1 2009-06-03
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/Manifest.txt ADDED
@@ -0,0 +1,12 @@
1
+ History.txt
2
+ Manifest.txt
3
+ PostInstall.txt
4
+ README.rdoc
5
+ Rakefile
6
+ lib/ruby2js.rb
7
+ ruby_to_js.gemspec
8
+ script/console
9
+ script/destroy
10
+ script/generate
11
+ spec/spec_helper.rb
12
+ spec/transliteration_spec.rb
data/PostInstall.txt ADDED
@@ -0,0 +1,7 @@
1
+
2
+ For more information on ruby_to_js, see http://ruby_to_js.rubyforge.org
3
+
4
+ NOTE: Change this information in PostInstall.txt
5
+ You can also delete it if you don't want it.
6
+
7
+
data/README.rdoc ADDED
@@ -0,0 +1,46 @@
1
+ = ruby\_to\_js
2
+
3
+ Ruby (Sexp generated by ruby_parser actually) to JavaScript convertion.
4
+ Still in early development but can handle method definitions, classes with no inheritance, procs, lambdas, method and block calls, if, elsif,
5
+ operator prescedence...
6
+
7
+ == DESCRIPTION:
8
+
9
+ FIX (describe your package)
10
+
11
+ == SYNOPSIS:
12
+
13
+ FIX (code sample of usage)
14
+
15
+ == REQUIREMENTS:
16
+
17
+ * FIX (list of requirements)
18
+
19
+ == INSTALL:
20
+
21
+ gem install maca-ruby\_to\_js
22
+
23
+ == LICENSE:
24
+
25
+ (The MIT License)
26
+
27
+ Copyright (c) 2009 Macario Ortega
28
+
29
+ Permission is hereby granted, free of charge, to any person obtaining
30
+ a copy of this software and associated documentation files (the
31
+ 'Software'), to deal in the Software without restriction, including
32
+ without limitation the rights to use, copy, modify, merge, publish,
33
+ distribute, sublicense, and/or sell copies of the Software, and to
34
+ permit persons to whom the Software is furnished to do so, subject to
35
+ the following conditions:
36
+
37
+ The above copyright notice and this permission notice shall be
38
+ included in all copies or substantial portions of the Software.
39
+
40
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
41
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
42
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
43
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
44
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
45
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
46
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ %w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
2
+ require File.dirname(__FILE__) + '/lib/ruby2js'
3
+
4
+ # Generate all the Rake tasks
5
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
6
+ $hoe = Hoe.new('ruby2js', Ruby2JS::VERSION) do |p|
7
+ p.developer('Macario Ortega', 'macarui@gmail.com')
8
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
9
+ # p.rubyforge_name = p.name # TODO this is default value
10
+ p.url = 'http://github.com/maca/ruby2js'
11
+ p.extra_deps = [
12
+ ['sexp_processor','>= 3.0.1'],
13
+ ]
14
+ p.extra_dev_deps = [
15
+ ['newgem', ">= #{::Newgem::VERSION}"],
16
+ ['rspec', '>= 1.2.6']
17
+ ]
18
+
19
+ p.clean_globs |= %w[**/.DS_Store tmp *.log]
20
+ # path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
21
+ # p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
22
+ p.rsync_args = '-av --delete --ignore-errors'
23
+ end
24
+
25
+ require 'newgem/tasks' # load /tasks/*.rake
26
+ Dir['tasks/**/*.rake'].each { |t| load t }
27
+
28
+ # TODO - want other tests/tasks run by default? Add them to the list
29
+ # task :default => [:spec, :features]
data/lib/ruby2js.rb ADDED
@@ -0,0 +1,254 @@
1
+ require 'sexp_processor'
2
+
3
+ # $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
4
+
5
+ class Ruby2JS
6
+ VERSION = '0.0.1'
7
+ LOGICAL = :and, :not, :or
8
+ OPERATORS = [:[], :[]=], [:not], [:*, :/, :%], [:+, :-, :<<], [:and], [:or]
9
+
10
+ def initialize( sexp, vars = {} )
11
+ @sexp, @vars = sexp, vars.dup
12
+ end
13
+
14
+ def to_js
15
+ parse( @sexp, nil )
16
+ end
17
+
18
+ protected
19
+ def operator_index op
20
+ OPERATORS.index( OPERATORS.find{ |el| el.include? op } ) || -1
21
+ end
22
+
23
+ def scope( sexp, vars, ancestor = nil )
24
+ self.class.new( nil, vars ).parse( sexp, ancestor )
25
+ end
26
+
27
+ def parse sexp, ancestor = nil
28
+ return sexp unless sexp.kind_of? Array
29
+ operand = sexp.shift
30
+
31
+ case operand
32
+
33
+ when :lit, :str
34
+ lit = sexp.shift
35
+ lit.is_a?( Numeric ) ? lit.to_s : lit.to_s.inspect
36
+
37
+ when :lvar, :const
38
+ sexp.shift.to_s
39
+
40
+ when :true, :false
41
+ operand.to_s
42
+
43
+ when :nil
44
+ 'null'
45
+
46
+ when :lasgn
47
+ var = mutate_name sexp.shift
48
+ value = sexp.shift
49
+ val = value.dup if value
50
+ output = value ? "#{ 'var ' unless @vars.keys.include? var }#{ var } = #{ parse value }" : var
51
+ @vars[var] ||= []
52
+ @vars[var] << (val && val.first == :lvar ? @vars[ val.last ] : val )
53
+ output
54
+
55
+ when :gasgn
56
+ "#{ mutate_name sexp.shift } = #{ parse sexp.shift }".sub('$', '')
57
+
58
+ when :iasgn
59
+ "#{ sexp.shift.to_s.sub('@', 'this._') } = #{ parse sexp.shift }"
60
+
61
+ when :op_asgn_or
62
+ var = sexp.shift
63
+ asgn = sexp.shift
64
+ parse asgn.push( s(:or, var, asgn.pop) )
65
+
66
+ when :ivar
67
+ sexp.shift.to_s.sub('@', 'this._')
68
+
69
+ when :hash
70
+ hashy = []
71
+ hashy << [ parse( sexp.shift ), parse( sexp.shift ) ] until sexp.empty?
72
+ "{#{ hashy.map{ |k,v| k << ' : ' << v }.join(',') }}"
73
+
74
+ when :array
75
+ "[#{ sexp.map{ |a| parse a }.join(', ') }]"
76
+
77
+ when :block
78
+ sexp.map{ |e| parse e }.join('; ')
79
+
80
+ when :return
81
+ "return #{ parse sexp.shift }"
82
+
83
+ when *LOGICAL
84
+ left, right = sexp.shift, sexp.shift
85
+ op_index = operator_index operand
86
+ lgroup = LOGICAL.include?( left.first ) && op_index <= operator_index( left.first )
87
+ left = parse left
88
+ left = "(#{ left })" if lgroup
89
+ rgroup = LOGICAL.include?( right.first ) && op_index <= operator_index( right.first ) if right
90
+ right = parse right
91
+ right = "(#{ right })" if rgroup
92
+
93
+ case operand
94
+ when :and
95
+ "#{ left } && #{ right }"
96
+ when :or
97
+ "#{ left } || #{ right }"
98
+ else
99
+ "!#{ left }"
100
+ end
101
+
102
+ when :call
103
+ receiver, method, args = sexp.shift, sexp.shift, sexp.shift
104
+ return parse args, :lambda if receiver == s(:const, :Proc) and method == :new or method == :lambda && !receiver
105
+ op_index = operator_index method
106
+ group = receiver.first == :call && op_index <= operator_index( receiver[2] ) if receiver
107
+ call = args.find_node(:call)
108
+ group_args = op_index <= operator_index( call[2] ) if call
109
+
110
+ case method
111
+ when :[]
112
+ raise 'parse error' unless receiver
113
+ "#{ parse receiver }[#{ parse args }]"
114
+
115
+ when :attr_accessor
116
+ args.shift
117
+ args = args.collect do |name|
118
+ name = name.last
119
+ parse( s(:defn, name, name) ).sub(/return null\}\z/, "if (name) {self._#{ name } = name} else {self._#{ name }}}")
120
+ end.join('; ')
121
+
122
+ when *OPERATORS.flatten
123
+ method = method_name_substitution receiver, method
124
+ "#{ group ? group(receiver) : parse(receiver) } #{ method } #{ group_args ? group(args) : parse(args) }"
125
+
126
+ else
127
+ method = method_name_substitution receiver, method
128
+ "#{ parse receiver }#{ '.' if receiver }#{ method }(#{ parse args })"
129
+ end
130
+
131
+ when :arglist
132
+ sexp.map{ |e| parse e }.join(', ')
133
+
134
+ when :masgn
135
+ if sexp.size == 1
136
+ sexp = sexp.shift
137
+ sexp[0] = :arglist
138
+ parse sexp
139
+ else
140
+ sexp.first[1..-1].zip sexp.last[1..-1] { |var, val| var << val }
141
+ sexp = sexp.first
142
+ sexp[0] = :block
143
+ parse sexp
144
+ end
145
+
146
+ when :if
147
+ condition = parse sexp.shift
148
+ true_block = scope sexp.shift, @vars
149
+ elseif = parse sexp.find_node( :if, true ), :if
150
+ else_block = parse sexp.shift
151
+ output = "if (#{ condition }) {#{ true_block }}"
152
+ output.sub!('if', 'else if') if ancestor == :if
153
+ output << " #{ elseif }" if elseif
154
+ output << " else {#{ else_block }}" if else_block
155
+ output
156
+
157
+ when :while
158
+ condition = parse sexp.shift
159
+ block = scope sexp.shift, @vars
160
+ unknown = parse sexp.shift
161
+ "while (#{ condition }) {#{ block }}"
162
+
163
+ when :iter
164
+ caller = sexp.shift
165
+ args = sexp.shift
166
+ function = s(:function, args, sexp.shift)
167
+ caller.last << function
168
+ parse caller
169
+
170
+ when :function
171
+ args, body = sexp.shift, sexp.shift
172
+ body ||= s(:nil)
173
+ body = s(:scope, body) unless body.first == :scope
174
+ body = parse body
175
+ body.sub!(/return var (\w+) = ([^;]+)\z/, 'var \1 = \2; return \1')
176
+ "function(#{ parse args }) {#{ body }}"
177
+
178
+ when :defn
179
+ name = sexp.shift
180
+ sexp.unshift :function
181
+ parse( sexp ).sub('function', "function #{ name }")
182
+
183
+ when :scope
184
+ body = sexp.shift
185
+ body = s(:block, body) unless body.first == :block
186
+ body.push s(:return, body.pop) unless body.last.first == :return
187
+ body = scope body, @vars
188
+
189
+ when :class
190
+ name, inheritance, body = sexp.shift, sexp.shift, sexp.shift
191
+ unless init = body.find_node(:defn)
192
+ if block = body.find_node(:block) and block.shift
193
+ methods = block.find_nodes(:defn) or s()
194
+ init = block.delete methods.find { |m| m[1] == :initialize }
195
+ block = block.collect { |m| parse( m ).sub(/function (\w+)/, "#{ name }.prototype.\\1 = function") }.join '; '
196
+ end
197
+ end
198
+ init ||= []
199
+ "#{ parse( s(:defn, name, init[2], init[3]) ).sub(/return (?:null|(.*))\}\z/, '\1}') }#{ '; ' if block }#{ block }"
200
+
201
+ when :args
202
+ sexp.join(', ')
203
+
204
+ when :dstr
205
+ sexp.unshift s(:str, sexp.shift)
206
+ sexp.collect{ |s| parse s }.join(' + ')
207
+
208
+ when :evstr
209
+ parse sexp.shift
210
+
211
+ else
212
+ raise "unkonwn operand #{ operand.inspect }"
213
+ end
214
+ end
215
+
216
+ SUBSTITUTIONS = Hash.new().merge({
217
+ # :array => {
218
+ :size => :length,
219
+ :<< => :+,
220
+ :index => 'indexOf',
221
+ :rindex => 'lastIndexOf',
222
+ :any? => 'some',
223
+ :all? => 'every',
224
+ :find_all => 'filter',
225
+ :each_with_index => 'each',
226
+ # },
227
+ # :* => {
228
+ :to_a => 'toArray',
229
+ :to_s => 'toString',
230
+ # }
231
+ })
232
+
233
+ def method_name_substitution receiver, method
234
+ # if receiver
235
+ # receiver = last_original_assign receiver if receiver.first == :lvar
236
+ # receiver = receiver.flatten.first
237
+ # end
238
+ # SUBSTITUTIONS[ :* ][ method ] || method || SUBSTITUTIONS[ receiver ][ method ] || method
239
+ SUBSTITUTIONS[ method ] || method
240
+ end
241
+
242
+ def last_original_assign receiver
243
+ ( @vars[ receiver.last ] || [] ).first || []
244
+ end
245
+
246
+ def group( sexp )
247
+ "(#{ parse sexp })"
248
+ end
249
+
250
+ def mutate_name( name )
251
+ name
252
+ end
253
+
254
+ end
@@ -0,0 +1,43 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{ruby_to_js}
5
+ s.version = "0.0.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Macario Ortega"]
9
+ s.date = %q{2009-06-11}
10
+ s.description = %q{FIX (describe your package)}
11
+ s.email = ["macarui@gmail.com"]
12
+ s.extra_rdoc_files = ["History.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc"]
13
+ s.files = ["History.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc", "Rakefile", "lib/ruby_to_js.rb", "script/console", "script/destroy", "script/generate", "test/test_helper.rb", "test/test_ruby_to_js.rb"]
14
+ s.has_rdoc = true
15
+ s.homepage = %q{http://github.com/maca/ruby_to_js}
16
+ s.rdoc_options = ["--main", "README.rdoc"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{ruby_to_js}
19
+ s.rubygems_version = %q{1.3.1}
20
+ s.summary = %q{FIX (describe your package)}
21
+
22
+ if s.respond_to? :specification_version then
23
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
+ s.specification_version = 2
25
+
26
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27
+ s.add_runtime_dependency(%q<sexp_processor>, [">= 3.0.1"])
28
+ s.add_development_dependency(%q<newgem>, [">= 1.4.1"])
29
+ s.add_development_dependency(%q<rspec>, [">= 1.2.6"])
30
+ s.add_development_dependency(%q<hoe>, [">= 1.8.0"])
31
+ else
32
+ s.add_dependency(%q<sexp_processor>, [">= 3.0.1"])
33
+ s.add_dependency(%q<newgem>, [">= 1.4.1"])
34
+ s.add_dependency(%q<rspec>, [">= 1.2.6"])
35
+ s.add_dependency(%q<hoe>, [">= 1.8.0"])
36
+ end
37
+ else
38
+ s.add_dependency(%q<sexp_processor>, [">= 3.0.1"])
39
+ s.add_dependency(%q<newgem>, [">= 1.4.1"])
40
+ s.add_dependency(%q<rspec>, [">= 1.2.6"])
41
+ s.add_dependency(%q<hoe>, [">= 1.8.0"])
42
+ end
43
+ end
data/script/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/ruby_to_js.rb'}"
9
+ puts "Loading ruby_to_js gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,3 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require File.dirname( __FILE__ ) + '/../lib/ruby2js'
@@ -0,0 +1,323 @@
1
+ require File.dirname( __FILE__ ) + '/spec_helper'
2
+ require 'ruby_parser'
3
+
4
+ describe Ruby2JS do
5
+
6
+ def rb_parse( string )
7
+ RubyParser.new.parse string
8
+ end
9
+
10
+ def to_js( string)
11
+ Ruby2JS.new( rb_parse( string ) ).to_js
12
+ end
13
+
14
+ describe 'literals' do
15
+ it "should parse literals and strings" do
16
+ to_js( "1" ).should == '1'
17
+ to_js( "'string'" ).should == '"string"'
18
+ to_js( ":symbol" ).should == '"symbol"'
19
+ to_js( "nil" ).should == 'null'
20
+ to_js( "Constant" ).should == 'Constant'
21
+ end
22
+
23
+ it "should parse simple hash" do
24
+ to_js( "{}" ).should == '{}'
25
+ to_js( "{ :a => :b }" ).should == '{"a" : "b"}'
26
+ end
27
+
28
+ it "should parse array" do
29
+ to_js( "[]" ).should == '[]'
30
+ to_js( "[1, 2, 3]" ).should == '[1, 2, 3]'
31
+ end
32
+
33
+ it "should parse nested hash" do
34
+ to_js( "{ :a => {:b => :c} }" ).should == '{"a" : {"b" : "c"}}'
35
+ end
36
+
37
+ it "should parse array" do
38
+ to_js( "[1, [2, 3]]" ).should == '[1, [2, 3]]'
39
+ end
40
+
41
+ it "should parse global variables" do
42
+ to_js( "$a = 1" ).should == 'a = 1'
43
+ end
44
+ end
45
+
46
+ describe 'assign' do
47
+ it "should parse left assign" do
48
+ to_js( "a = 1" ).should == 'var a = 1'
49
+ to_js( "a = 'string'" ).should == 'var a = "string"'
50
+ to_js( "a = :symbol" ).should == 'var a = "symbol"'
51
+ end
52
+
53
+ it "should not output var if variable is allready declared within a context" do
54
+ to_js( "a = 1; a = 2" ).should == 'var a = 1; a = 2'
55
+ end
56
+
57
+ it "should parse mass assign" do
58
+ to_js( "a , b = 1, 2" ).should == 'var a = 1; var b = 2'
59
+ end
60
+
61
+ it "should parse" do
62
+ to_js( 'a += 1').should == 'var a = a + 1'
63
+ end
64
+
65
+ it "should do short circuit assign" do
66
+ to_js( 'a = nil; a ||= 1').should == 'var a = null; a = a || 1'
67
+ end
68
+
69
+ it "should parse tertiary operator" do
70
+ to_js( 'true ? true : false').should == "if (true) {true} else {false}"
71
+ end
72
+ end
73
+
74
+ describe 'method call' do
75
+ it "should parse method call with no args" do
76
+ to_js( "a" ).should == 'a()'
77
+ end
78
+
79
+ it "should parse method call with args" do
80
+ to_js( "a 1, 2, 3" ).should == 'a(1, 2, 3)'
81
+ end
82
+
83
+ it "should parse lvar as variable call" do
84
+ to_js( "a = 1; a" ).should == 'var a = 1; a'
85
+ end
86
+
87
+ it "should parse square bracket call" do
88
+ to_js( "a = [1]; a[0]" ).should == 'var a = [1]; a[0]'
89
+ end
90
+
91
+ it "should parse nested square bracket call" do
92
+ to_js( "a = [[1]]; a[0][0]" ).should == 'var a = [[1]]; a[0][0]'
93
+ end
94
+
95
+ it "should parse binary operation" do
96
+ to_js( "1 + 1" ).should == '1 + 1'
97
+ end
98
+
99
+ it "should call method on literal" do
100
+ to_js( "[0][0]" ).should == '[0][0]'
101
+ end
102
+
103
+ it "should nest arguments as needed" do
104
+ exp = 'a((1 + 2) * 2)'
105
+ to_js( exp ).should == exp
106
+ end
107
+
108
+ it "should chain method calls" do
109
+ exp = 'a().one().two().three()'
110
+ to_js( exp ).should == exp
111
+ end
112
+ end
113
+
114
+ describe 'boolean' do
115
+ it "should parse boolean" do
116
+ to_js( "true; false" ).should == 'true; false'
117
+ end
118
+
119
+ it "should parse logic operators" do
120
+ to_js( "true && false" ).should == 'true && false'
121
+ to_js( "true and false" ).should == 'true && false'
122
+ to_js( "true || false" ).should == 'true || false'
123
+ to_js( "true or false" ).should == 'true || false'
124
+ end
125
+
126
+ it "should parse not" do
127
+ to_js( "not true" ).should == '!true'
128
+ end
129
+
130
+ it "should parse nested logic" do
131
+ to_js( 'not (true or false)' ).should == '!(true || false)'
132
+ end
133
+
134
+ it "should parse more complex nested logic" do
135
+ logic = '!((true && false) || (false || false))'
136
+ to_js( logic ).should == "!(true && false || (false || false))"
137
+ end
138
+
139
+ it "should parse another nested login example" do
140
+ logic = '!true && true'
141
+ to_js( logic ).should == logic
142
+ end
143
+
144
+ end
145
+
146
+ describe 'expressions' do
147
+ it "should not nest" do
148
+ exp = '1 + 1 * 1'
149
+ to_js( exp ).should == exp
150
+ end
151
+
152
+ it "should parse nested expressions" do
153
+ exp = '(1 + 1) * 1'
154
+ to_js( exp ).should == exp
155
+ end
156
+
157
+ it "should parse complex nested expressions" do
158
+ exp = '1 + (1 + (1 + 1 * (2 - 1)))'
159
+ to_js( exp ).should == exp
160
+ end
161
+
162
+ it "should parse complex nested expressions with method calls" do
163
+ exp = '1 + (a() + (1 + 1 * (b() - d())))'
164
+ to_js( exp ).should == exp
165
+ end
166
+
167
+ it "should parse complex nested expressions with method calls and variables" do
168
+ exp = 'a = 5; 1 + (a + (1 + a * (b() - d())))'
169
+ to_js( exp ).should == "var " << exp
170
+ end
171
+
172
+ it "should parse nested sender" do
173
+ exp = '((1 / 2) * 4 - (1 + 1)) - 1'
174
+ to_js( exp ).should == exp
175
+ end
176
+
177
+ it "should nest arguments as needed" do
178
+ exp = 'a((1 + 2) * 2 - 1)'
179
+ to_js( exp ).should == exp
180
+ end
181
+ end
182
+
183
+ describe 'string concat' do
184
+ # it "should eval" do
185
+ # to_js('eval( "hi" )').should == 'eval("hi")'
186
+ # end
187
+
188
+ it "should parse string " do
189
+ to_js( '"time is #{ Time.now }, say #{ hello }"' ).should == '"time is " + Time.now() + ", say " + hello()'
190
+ end
191
+
192
+ it "should parse string " do
193
+ to_js( '"time is #{ Time.now }"' ).should == '"time is " + Time.now()'
194
+ end
195
+
196
+ end
197
+
198
+ describe 'control' do
199
+ it "should parse single line if" do
200
+ to_js( '1 if true' ).should == 'if (true) {1}'
201
+ end
202
+
203
+ it "should parse if else" do
204
+ to_js( 'if true; 1; else; 2; end' ).should == 'if (true) {1} else {2}'
205
+ end
206
+
207
+ it "should parse if elsif" do
208
+ to_js( 'if true; 1; elsif false; 2; else; 3; end' ).should == 'if (true) {1} else if (false) {2} else {3}'
209
+ end
210
+
211
+ it "should parse if elsif elsif" do
212
+ to_js( 'if true; 1; elsif false; 2; elsif (true or false); 3; else; nassif; end' ).should == 'if (true) {1} else if (false) {2} else if (true || false) {3} else {nassif()}'
213
+ end
214
+
215
+ it "should handle basic variable scope" do
216
+ to_js( 'a = 1; if true; a = 2; b = 1; elsif false; a = 3; b = 2; else; a = 4; b =3; end' ).should == 'var a = 1; if (true) {a = 2; var b = 1} else if (false) {a = 3; var b = 2} else {a = 4; var b = 3}'
217
+ end
218
+
219
+ it "should handle while loop" do
220
+ to_js( 'a = 0; while true; a += 1; end').should == 'var a = 0; while (true) {a = a + 1}'
221
+ end
222
+
223
+ it "should handle another while loop" do
224
+ to_js( 'a = 0; while true || false; a += 1; end').should == 'var a = 0; while (true || false) {a = a + 1}'
225
+ end
226
+ end
227
+
228
+ describe 'blocks' do
229
+ it "should parse return" do
230
+ exp = 'return 1'
231
+ to_js( exp ).should == exp
232
+ end
233
+
234
+ it "should parse proc" do
235
+ to_js('Proc.new {}').should == 'function() {return null}'
236
+ end
237
+
238
+ it "should parse lambda" do
239
+ to_js( 'lambda {}').should == 'function() {return null}'
240
+ end
241
+
242
+ it "should handle basic variable scope" do
243
+ to_js( 'a = 1; lambda { a = 2; b = 1}').should == 'var a = 1; function() {a = 2; var b = 1; return b}'
244
+ end
245
+
246
+ it "should handle one argument" do
247
+ to_js( 'lambda { |a| a + 1 }').should == 'function(a) {return a + 1}'
248
+ end
249
+
250
+ it "should handle arguments" do
251
+ to_js( 'lambda { |a,b| a + b }').should == 'function(a, b) {return a + b}'
252
+ end
253
+
254
+ it "should pass functions" do
255
+ to_js( 'run("task"){ |task| do_run task}').should == 'run("task", function(task) {return do_run(task)})'
256
+ end
257
+
258
+ it "should handle variable scope" do
259
+ to_js('a = 1; lambda {|b| c = 0; a = b - c }; lambda { |b| c = 1; a = b + c }').
260
+ should == 'var a = 1; function(b) {var c = 0; return a = b - c}; function(b) {var c = 1; return a = b + c}'
261
+ end
262
+
263
+ it "should really handle variable scope" do
264
+ to_js('a, d = 1, 2; lambda {|b| c = 0; a = b - c * d}; lambda { |b| c = 1; a = b + c * d}').
265
+ should == 'var a = 1; var d = 2; function(b) {var c = 0; return a = b - c * d}; function(b) {var c = 1; return a = b + c * d}'
266
+ end
267
+
268
+ it "should parse with explicit return" do
269
+ to_js('Proc.new {return nil}').should == 'function() {return null}'
270
+ end
271
+ end
272
+
273
+ describe 'object definition' do
274
+ it "should parse class" do
275
+ to_js('class Person; end').should == 'function Person() {}'
276
+ end
277
+
278
+ it "should parse class" do
279
+ to_js('class Person; def initialize(name); @name = name; end; end').should == 'function Person(name) {this._name = name}'
280
+ end
281
+
282
+ it "should parse class" do
283
+ to_js('class Person; def initialize(name); @name = name; end; def name; @name; end; end').
284
+ should == 'function Person(name) {this._name = name}; Person.prototype.name = function() {return this._name}'
285
+ end
286
+
287
+ it "should parse class" do
288
+ to_js('class Person; def initialize(name, surname); @name, @surname = name, surname; end; def full_name; @name + @surname; end; end').
289
+ should == 'function Person(name, surname) {this._name = name; this._surname = surname}; Person.prototype.full_name = function() {return this._name + this._surname}'
290
+ end
291
+
292
+ it "should parse class" do
293
+ to_js('class Person; attr_accessor :name; def initialize(name); @name = name; end; end').
294
+ should == 'function Person(name) {this._name = name}; Person.prototype.name = function(name) {if (name) {self._name = name} else {self._name}}'
295
+ end
296
+
297
+ it "should parse metod def" do
298
+ to_js('def method; end').should == 'function method() {return null}'
299
+ end
300
+ end
301
+
302
+ describe 'method substitutions' do
303
+ # it "should not convert name" do
304
+ # to_js('a.size').should == 'a().size()'
305
+ # end
306
+
307
+ it "should convert size to length" do
308
+ to_js('[].size').should == '[].length()'
309
+ end
310
+
311
+ it "should convert size to length after assign" do
312
+ to_js('a = []; a.size').should == 'var a = []; a.length()'
313
+ end
314
+
315
+ it "should convert size to length after several assigns" do
316
+ to_js('a = []; b = a; c = b; d = c; d.size').should == 'var a = []; var b = a; var c = b; var d = c; d.length()'
317
+ end
318
+
319
+ it "should subtitute << for + for array" do
320
+ to_js('a = []; a << []').should == 'var a = []; a + []'
321
+ end
322
+ end
323
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: maca-ruby2js
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Macario Ortega
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-11 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: sexp_processor
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 3.0.1
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: newgem
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.4.1
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: rspec
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 1.2.6
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: hoe
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 1.8.0
54
+ version:
55
+ description: Convert Ruby (sexp generated by ruby_parser) to JavaScript
56
+ email:
57
+ - macarui@gmail.com
58
+ executables: []
59
+
60
+ extensions: []
61
+
62
+ extra_rdoc_files:
63
+ - History.txt
64
+ - Manifest.txt
65
+ - PostInstall.txt
66
+ - README.rdoc
67
+ files:
68
+ - History.txt
69
+ - Manifest.txt
70
+ - PostInstall.txt
71
+ - README.rdoc
72
+ - Rakefile
73
+ - lib/ruby2js.rb
74
+ - ruby_to_js.gemspec
75
+ - script/console
76
+ - script/destroy
77
+ - script/generate
78
+ - spec/spec_helper.rb
79
+ - spec/transliteration_spec.rb
80
+ has_rdoc: true
81
+ homepage: http://github.com/maca/ruby2js
82
+ post_install_message:
83
+ rdoc_options:
84
+ - --main
85
+ - README.rdoc
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: "0"
93
+ version:
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: "0"
99
+ version:
100
+ requirements: []
101
+
102
+ rubyforge_project: ruby2js
103
+ rubygems_version: 1.2.0
104
+ signing_key:
105
+ specification_version: 2
106
+ summary: Convert Ruby (sexp generated by ruby_parser) to JavaScript
107
+ test_files: []
108
+