maca-ruby2js 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/History.txt +4 -0
- data/Manifest.txt +12 -0
- data/PostInstall.txt +7 -0
- data/README.rdoc +46 -0
- data/Rakefile +29 -0
- data/lib/ruby2js.rb +254 -0
- data/ruby_to_js.gemspec +43 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/transliteration_spec.rb +323 -0
- metadata +108 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
data/PostInstall.txt
ADDED
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
|
data/ruby_to_js.gemspec
ADDED
@@ -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)
|
data/spec/spec_helper.rb
ADDED
@@ -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
|
+
|