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 +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
|
+
|