ruby2js 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/README.md +52 -0
- data/lib/ruby2js.rb +79 -0
- data/lib/ruby2js/converter.rb +281 -0
- data/lib/ruby2js/filter/angularrb.rb +167 -0
- data/lib/ruby2js/filter/functions.rb +41 -0
- data/lib/ruby2js/filter/return.rb +63 -0
- data/lib/ruby2js/version.rb +10 -0
- data/ruby2js.gemspec +29 -0
- metadata +65 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MThiMDkwZGYwN2EwNTUzNmM3ODY3NGExMzcwMDllMTUzODJmNWQ2MQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MzA3ODMzOWM1ZDcwMmI2YzhiNzFmZjdlNDE4NTU2ZGQ5OTkwZmIwNw==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZWI1ODMwODg3YzgxODhhZDZjMjRmYzAxM2RmMDU5MGUzZmQ1OTUyNDY5NDdi
|
10
|
+
NDgyODdkNjFlNjZmMWU3MDE1ZTYyZGY5YTJmM2NmN2Y3NjVhNDRmMjVkMjYw
|
11
|
+
NmFhMDVjZWMwNGEwZDVhNWE3MzQyNmRlNjI2YjI4YmQ5MzgyNzY=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NGU1YTAwOWZiODUyMWJmOTM3YTYyMjNmNTEwNzkwNDExNjBmZjAwMjI2ODky
|
14
|
+
YzhmNmRhMjQxYjk0ZGJjZjZlODFkZDc1MjVjNzIzZTExMThkNDBlZTRhMjM3
|
15
|
+
ZDgyZDBiZjQ2OGIxM2NmNmMwZmQ3ZjUyYWVmY2JjNzM3NzA3M2M=
|
data/README.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
ruby2js
|
2
|
+
=======
|
3
|
+
|
4
|
+
Minimal yet extensible Ruby to JavaScript conversion.
|
5
|
+
|
6
|
+
Description
|
7
|
+
---
|
8
|
+
|
9
|
+
The base package maps Ruby syntax to JavaScript semantics. For example,
|
10
|
+
a Ruby Hash literal becomes a JavaScript Object literal. Ruby symbols
|
11
|
+
become JavaScript strings. Ruby method calls become JavaScript function
|
12
|
+
calls IF there are either one or more arguments passed OR parenthesis are
|
13
|
+
used, otherwise Ruby method calls become JavaScript property accesses.
|
14
|
+
By default, methods, lambdas, and procs return `undefined`.
|
15
|
+
|
16
|
+
Filters may be provided to add Ruby-specific or Framework specific
|
17
|
+
behavior. Filters are essentially macro facilities that operate on
|
18
|
+
an AST representation of the code.
|
19
|
+
|
20
|
+
Synopsis
|
21
|
+
---
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
require 'ruby2js/filter/functions'
|
25
|
+
puts Ruby2JS.convert('"2A".to_i(16)', filters: [Ruby2JS::Filter::Functions])
|
26
|
+
```
|
27
|
+
|
28
|
+
License
|
29
|
+
---
|
30
|
+
|
31
|
+
(The MIT License)
|
32
|
+
|
33
|
+
Copyright (c) 2009,2013 Macario Ortega, Sam Ruby
|
34
|
+
|
35
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
36
|
+
a copy of this software and associated documentation files (the
|
37
|
+
'Software'), to deal in the Software without restriction, including
|
38
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
39
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
40
|
+
permit persons to whom the Software is furnished to do so, subject to
|
41
|
+
the following conditions:
|
42
|
+
|
43
|
+
The above copyright notice and this permission notice shall be
|
44
|
+
included in all copies or substantial portions of the Software.
|
45
|
+
|
46
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
47
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
48
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
49
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
50
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
51
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
52
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/lib/ruby2js.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'parser/current'
|
2
|
+
require 'ruby2js/converter'
|
3
|
+
|
4
|
+
module Ruby2JS
|
5
|
+
def self.convert(source, options={})
|
6
|
+
|
7
|
+
if Proc === source
|
8
|
+
file,line = source.source_location
|
9
|
+
source = File.read(file)
|
10
|
+
ast = find_block( parse(source), line )
|
11
|
+
elsif Parser::AST::Node === source
|
12
|
+
ast = source
|
13
|
+
source = ast.loc.expression.source_buffer.source
|
14
|
+
else
|
15
|
+
ast = parse( source )
|
16
|
+
end
|
17
|
+
|
18
|
+
if options[:filters]
|
19
|
+
filter = Parser::AST::Processor
|
20
|
+
options[:filters].reverse.each do |mod|
|
21
|
+
filter = Class.new(filter) {include mod}
|
22
|
+
end
|
23
|
+
ast = filter.new.process(ast)
|
24
|
+
end
|
25
|
+
|
26
|
+
ruby2js = Ruby2JS::Converter.new( ast )
|
27
|
+
|
28
|
+
if source.include? "\n"
|
29
|
+
ruby2js.enable_vertical_whitespace
|
30
|
+
lines = ruby2js.to_js.split("\n")
|
31
|
+
pre = ''
|
32
|
+
pending = false
|
33
|
+
blank = true
|
34
|
+
lines.each do |line|
|
35
|
+
if line.start_with? '}' or line.start_with? ']'
|
36
|
+
pre.sub!(/^ /,'')
|
37
|
+
line.sub!(/;$/,";\n")
|
38
|
+
pending = true
|
39
|
+
else
|
40
|
+
pending = false
|
41
|
+
end
|
42
|
+
|
43
|
+
line.sub! /^/, pre
|
44
|
+
if line.end_with? '{' or line.end_with? '['
|
45
|
+
pre += ' '
|
46
|
+
line.sub!(/^/,"\n") unless blank or pending
|
47
|
+
pending = true
|
48
|
+
end
|
49
|
+
|
50
|
+
blank = pending
|
51
|
+
end
|
52
|
+
lines.join("\n")
|
53
|
+
else
|
54
|
+
ruby2js.to_js
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.parse(source)
|
59
|
+
# workaround for https://github.com/whitequark/parser/issues/112
|
60
|
+
buffer = Parser::Source::Buffer.new('__SOURCE__')
|
61
|
+
buffer.raw_source = source.encode('utf-8')
|
62
|
+
Parser::CurrentRuby.new.parse(buffer)
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.find_block(ast, line)
|
66
|
+
if ast.type == :block and ast.loc.expression.line == line
|
67
|
+
return ast.children.last
|
68
|
+
end
|
69
|
+
|
70
|
+
ast.children.each do |child|
|
71
|
+
if Parser::AST::Node === child
|
72
|
+
block = find_block child, line
|
73
|
+
return block if block
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,281 @@
|
|
1
|
+
require 'parser/current'
|
2
|
+
|
3
|
+
module Ruby2JS
|
4
|
+
class Converter
|
5
|
+
LOGICAL = :and, :not, :or
|
6
|
+
OPERATORS = [:[], :[]=], [:not, :!], [:*, :/, :%], [:+, :-], [:>>, :<<],
|
7
|
+
[:<=, :<, :>, :>=], [:==, :!=], [:and, :or]
|
8
|
+
|
9
|
+
def initialize( ast, vars = {} )
|
10
|
+
@ast, @vars = ast, vars.dup
|
11
|
+
@sep = '; '
|
12
|
+
@nl = ''
|
13
|
+
end
|
14
|
+
|
15
|
+
def enable_vertical_whitespace
|
16
|
+
@sep = ";\n"
|
17
|
+
@nl = "\n"
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_js
|
21
|
+
parse( @ast, :statement )
|
22
|
+
end
|
23
|
+
|
24
|
+
def operator_index op
|
25
|
+
OPERATORS.index( OPERATORS.find{ |el| el.include? op } ) || -1
|
26
|
+
end
|
27
|
+
|
28
|
+
def scope( ast )
|
29
|
+
frame = self.class.new( nil, @vars )
|
30
|
+
frame.enable_vertical_whitespace if @nl == "\n"
|
31
|
+
frame.parse( ast, :statement )
|
32
|
+
end
|
33
|
+
|
34
|
+
def s(type, *args)
|
35
|
+
Parser::AST::Node.new(type, args)
|
36
|
+
end
|
37
|
+
|
38
|
+
def is_method?(node)
|
39
|
+
return false unless node.type == :send
|
40
|
+
return true unless node.loc
|
41
|
+
selector = node.loc.selector
|
42
|
+
return true unless selector.source_buffer
|
43
|
+
selector.source_buffer.source[selector.end_pos] == '('
|
44
|
+
end
|
45
|
+
|
46
|
+
def parse(ast, state=:expression)
|
47
|
+
return ast unless Parser::AST::Node === ast
|
48
|
+
|
49
|
+
case ast.type
|
50
|
+
|
51
|
+
when :int, :float, :str
|
52
|
+
ast.children.first.inspect
|
53
|
+
|
54
|
+
when :sym
|
55
|
+
ast.children.first.to_s.inspect
|
56
|
+
|
57
|
+
when :lvar, :gvar
|
58
|
+
ast.children.first
|
59
|
+
|
60
|
+
when :true, :false
|
61
|
+
ast.type.to_s
|
62
|
+
|
63
|
+
when :nil
|
64
|
+
'null'
|
65
|
+
|
66
|
+
when :lvasgn
|
67
|
+
var, value = ast.children
|
68
|
+
output = value ? "#{ 'var ' unless @vars.keys.include? var }#{ var } = #{ parse value }" : var
|
69
|
+
@vars[var] = true
|
70
|
+
output
|
71
|
+
|
72
|
+
when :op_asgn
|
73
|
+
var, op, value = ast.children
|
74
|
+
|
75
|
+
if [:+, :-].include?(op) and value.type==:int and value.children==[1]
|
76
|
+
if state == :statement
|
77
|
+
"#{ parse var }#{ op }#{ op }"
|
78
|
+
else
|
79
|
+
"#{ op }#{ op }#{ parse var }"
|
80
|
+
end
|
81
|
+
else
|
82
|
+
"#{ parse var } #{ op }= #{ parse value }"
|
83
|
+
end
|
84
|
+
|
85
|
+
when :casgn
|
86
|
+
cbase, var, value = ast.children
|
87
|
+
var = "#{cbase}.var" if cbase
|
88
|
+
output = "const #{ var } = #{ parse value }"
|
89
|
+
@vars[var] = true
|
90
|
+
output
|
91
|
+
|
92
|
+
when :gvasgn
|
93
|
+
name, value = ast.children
|
94
|
+
"#{ name } = #{ parse value }"
|
95
|
+
|
96
|
+
when :ivasgn
|
97
|
+
name, expression = ast.children
|
98
|
+
"#{ name.to_s.sub('@', 'this._') } = #{ parse expression }"
|
99
|
+
|
100
|
+
when :or_asgn
|
101
|
+
var, value = ast.children
|
102
|
+
"#{ parse var } = #{parse var} || #{ parse value }"
|
103
|
+
|
104
|
+
when :and_asgn
|
105
|
+
var, value = ast.children
|
106
|
+
"#{ parse var } = #{parse var} && #{ parse value }"
|
107
|
+
|
108
|
+
when :ivar
|
109
|
+
name = ast.children.first
|
110
|
+
name.to_s.sub('@', 'this._')
|
111
|
+
|
112
|
+
when :hash
|
113
|
+
hashy = ast.children.map do |node|
|
114
|
+
left, right = node.children
|
115
|
+
key = parse left
|
116
|
+
key = $1 if key =~ /\A"([a-zA-Z_$][a-zA-Z_$0-9]*)"\Z/
|
117
|
+
"#{key}: #{parse right}"
|
118
|
+
end
|
119
|
+
"{#{ hashy.join(', ') }}"
|
120
|
+
|
121
|
+
when :array
|
122
|
+
list = ast.children.map { |a| parse a }
|
123
|
+
if list.join(', ').length < 80
|
124
|
+
"[#{ list.join(', ') }]"
|
125
|
+
else
|
126
|
+
"[\n#{ list.join(",\n") }\n]"
|
127
|
+
end
|
128
|
+
|
129
|
+
when :begin
|
130
|
+
ast.children.map{ |e| parse e, :statement }.join(@sep)
|
131
|
+
|
132
|
+
when :return
|
133
|
+
"return #{ parse ast.children.first }"
|
134
|
+
|
135
|
+
when *LOGICAL
|
136
|
+
left, right = ast.children
|
137
|
+
left = left.children.first if left and left.type == :begin
|
138
|
+
right = right.children.first if right.type == :begin
|
139
|
+
op_index = operator_index ast.type
|
140
|
+
lgroup = LOGICAL.include?( left.type ) && op_index <= operator_index( left.type )
|
141
|
+
left = parse left
|
142
|
+
left = "(#{ left })" if lgroup
|
143
|
+
rgroup = LOGICAL.include?( right.type ) && op_index <= operator_index( right.type ) if right.children.length > 0
|
144
|
+
right = parse right
|
145
|
+
right = "(#{ right })" if rgroup
|
146
|
+
|
147
|
+
case ast.type
|
148
|
+
when :and
|
149
|
+
"#{ left } && #{ right }"
|
150
|
+
when :or
|
151
|
+
"#{ left } || #{ right }"
|
152
|
+
else
|
153
|
+
"!#{ left }"
|
154
|
+
end
|
155
|
+
|
156
|
+
when :send, :attr
|
157
|
+
receiver, method, *args = ast.children
|
158
|
+
if method == :new and receiver and receiver.children == [nil, :Proc]
|
159
|
+
return parse args.first
|
160
|
+
elsif method == :lambda and not receiver
|
161
|
+
return parse args.first
|
162
|
+
end
|
163
|
+
|
164
|
+
op_index = operator_index method
|
165
|
+
if op_index != -1
|
166
|
+
target = args.first
|
167
|
+
target = target.children.first if target and target.type == :begin
|
168
|
+
receiver = receiver.children.first if receiver.type == :begin
|
169
|
+
end
|
170
|
+
|
171
|
+
group_receiver = receiver.type == :send && op_index <= operator_index( receiver.children[1] ) if receiver
|
172
|
+
group_target = target.type == :send && op_index <= operator_index( target.children[1] ) if target
|
173
|
+
|
174
|
+
case method
|
175
|
+
when :!
|
176
|
+
group_receiver ||= (receiver.children.length > 1)
|
177
|
+
"!#{ group_receiver ? group(receiver) : parse(receiver) }"
|
178
|
+
|
179
|
+
when :call
|
180
|
+
"#{ parse receiver }(#{ parse args.first })"
|
181
|
+
|
182
|
+
when :[]
|
183
|
+
raise 'parse error' unless receiver
|
184
|
+
"#{ parse receiver }[#{ parse args.first }]"
|
185
|
+
|
186
|
+
when :-@, :+@
|
187
|
+
"#{ method.to_s[0] }#{ parse receiver }"
|
188
|
+
|
189
|
+
when *OPERATORS.flatten
|
190
|
+
"#{ group_receiver ? group(receiver) : parse(receiver) } #{ method } #{ group_target ? group(target) : parse(target) }"
|
191
|
+
|
192
|
+
when /=$/
|
193
|
+
"#{ parse receiver }#{ '.' if receiver }#{ method.to_s.sub(/=$/, ' =') } #{ parse args.first }"
|
194
|
+
|
195
|
+
else
|
196
|
+
if args.length == 0 and not is_method?(ast)
|
197
|
+
"#{ parse receiver }#{ '.' if receiver }#{ method }"
|
198
|
+
else
|
199
|
+
args = args.map {|a| parse a}.join(', ')
|
200
|
+
"#{ parse receiver }#{ '.' if receiver }#{ method }(#{ args })"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
when :const
|
205
|
+
receiver, name = ast.children
|
206
|
+
"#{ parse receiver }#{ '.' if receiver }#{ name }"
|
207
|
+
|
208
|
+
when :masgn
|
209
|
+
lhs, rhs = ast.children
|
210
|
+
block = []
|
211
|
+
lhs.children.zip rhs.children.zip do |var, val|
|
212
|
+
block << s(var.type, *var.children, *val)
|
213
|
+
end
|
214
|
+
parse s(:begin, *block)
|
215
|
+
|
216
|
+
when :if
|
217
|
+
condition, true_block, else_block = ast.children
|
218
|
+
if state == :statement
|
219
|
+
output = "if (#{ parse condition }) {#@nl#{ scope true_block }#@nl}"
|
220
|
+
while else_block and else_block.type == :if
|
221
|
+
condition, true_block, else_block = else_block.children
|
222
|
+
output << " else if (#{ parse condition }) {#@nl#{ scope true_block }#@nl}"
|
223
|
+
end
|
224
|
+
output << " else {#@nl#{ scope else_block }#@nl}" if else_block
|
225
|
+
output
|
226
|
+
else
|
227
|
+
"(#{ parse condition } ? #{ parse true_block } : #{ parse else_block })"
|
228
|
+
end
|
229
|
+
|
230
|
+
when :while
|
231
|
+
condition, block = ast.children
|
232
|
+
"while (#{ parse condition }) {#@nl#{ scope block }#@nl}"
|
233
|
+
|
234
|
+
when :block
|
235
|
+
call, args, block = ast.children
|
236
|
+
block ||= s(:begin)
|
237
|
+
function = s(:def, name, args, block)
|
238
|
+
parse s(:send, *call.children, function)
|
239
|
+
|
240
|
+
when :def
|
241
|
+
name, args, body = ast.children
|
242
|
+
body ||= s(:begin)
|
243
|
+
body = s(:scope, body) unless body.type == :scope
|
244
|
+
body = parse body
|
245
|
+
body.sub! /return var (\w+) = ([^;]+)\z/, "var \\1 = \\2#{@sep}return \\1"
|
246
|
+
"function#{ " #{name}" if name }(#{ parse args }) {#@nl#{ body }#@nl}"
|
247
|
+
|
248
|
+
when :scope
|
249
|
+
body = ast.children.first
|
250
|
+
body = s(:begin, body) unless body.type == :begin
|
251
|
+
block = body.children
|
252
|
+
scope body
|
253
|
+
|
254
|
+
when :class
|
255
|
+
name, inheritance, *body = ast.children
|
256
|
+
body.compact!
|
257
|
+
body = body.first.children.dup if body.length == 1 and body.first.type == :begin
|
258
|
+
methods = body.select { |a| a.type == :def }
|
259
|
+
init = (body.delete methods.find { |m| m.children.first == :initialize }) || s(:def, :initialize)
|
260
|
+
block = body.collect { |m| parse( m ).sub(/function (\w+)/, "#{ parse name }.prototype.\\1 = function") }.join @sep
|
261
|
+
"#{ parse( s(:def, parse(name), init.children[1], init.children[2]) ).sub(/return (?:null|(.*))\}\z/, '\1}') }#{ @sep if block and not block.empty?}#{ block }"
|
262
|
+
|
263
|
+
when :args
|
264
|
+
ast.children.map { |a| a.children.first }.join(', ')
|
265
|
+
|
266
|
+
when :dstr
|
267
|
+
ast.children.map{ |s| parse s }.join(' + ')
|
268
|
+
|
269
|
+
when :self
|
270
|
+
'this'
|
271
|
+
|
272
|
+
else
|
273
|
+
raise "unknown AST type #{ ast.type }"
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def group( ast )
|
278
|
+
"(#{ parse ast })"
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'parser/current'
|
2
|
+
require 'ruby2js'
|
3
|
+
|
4
|
+
module Ruby2JS
|
5
|
+
module Filter
|
6
|
+
module AngularRB
|
7
|
+
def initialize(*args)
|
8
|
+
@ngApp = nil
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
# input:
|
13
|
+
# module Angular::AppName
|
14
|
+
# use :Dependency
|
15
|
+
# ...
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# output:
|
19
|
+
# AppName = angular.module("AppName", ["Dependency"])
|
20
|
+
# ...
|
21
|
+
|
22
|
+
def on_module(node)
|
23
|
+
module_name = node.children[0]
|
24
|
+
parent_name = module_name.children[0]
|
25
|
+
|
26
|
+
return super unless parent_name and parent_name.type == :const
|
27
|
+
return super unless parent_name.children == [nil, :Angular]
|
28
|
+
|
29
|
+
@ngApp = module_name.children[1]
|
30
|
+
|
31
|
+
# find the block
|
32
|
+
block = process_all(node.children[1..-1])
|
33
|
+
while block.length == 1 and block.first.type == :begin
|
34
|
+
block = block.first.children.dup
|
35
|
+
end
|
36
|
+
|
37
|
+
# find use class method calls
|
38
|
+
uses = block.find_all do |node|
|
39
|
+
node.type == :send and node.children[0..1] == [nil, :use]
|
40
|
+
end
|
41
|
+
|
42
|
+
# convert use calls into dependencies
|
43
|
+
depends = []
|
44
|
+
uses.each do |use|
|
45
|
+
pending = []
|
46
|
+
use.children[2..-1].each do |node|
|
47
|
+
break unless [:str, :sym].include? node.type
|
48
|
+
pending << node
|
49
|
+
end
|
50
|
+
depends += pending
|
51
|
+
block.delete use
|
52
|
+
end
|
53
|
+
|
54
|
+
# build constant assignment statement
|
55
|
+
casgn = s(:casgn, nil, @ngApp, s(:send,
|
56
|
+
s(:lvar, :angular),
|
57
|
+
:module,
|
58
|
+
s(:str, @ngApp.to_s),
|
59
|
+
s(:array, *depends)))
|
60
|
+
|
61
|
+
@ngApp = nil
|
62
|
+
|
63
|
+
# replace module with a constant assign followed by the module contents
|
64
|
+
node.updated :begin, [casgn, *block]
|
65
|
+
end
|
66
|
+
|
67
|
+
# input:
|
68
|
+
# class Name < Angular::Controller
|
69
|
+
# use :$service
|
70
|
+
# ...
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# output:
|
74
|
+
# AppName.controller :Name do |$service|
|
75
|
+
# ...
|
76
|
+
# end
|
77
|
+
|
78
|
+
def on_class(node)
|
79
|
+
return super unless @ngApp
|
80
|
+
return super unless node.children.length == 3
|
81
|
+
|
82
|
+
# (const nil :Name)
|
83
|
+
name = node.children.first
|
84
|
+
return super unless name.type == :const and name.children.first == nil
|
85
|
+
|
86
|
+
# (const (const nil :Angular) :Controller)
|
87
|
+
parent = node.children[1]
|
88
|
+
return super unless parent and parent.children.length == 2
|
89
|
+
return super unless parent.children[0]
|
90
|
+
return super unless parent.children[0].type == :const
|
91
|
+
return super unless parent.children[0].children == [nil, :Angular]
|
92
|
+
return super unless [:Controller].include? parent.children[1]
|
93
|
+
|
94
|
+
# find the block
|
95
|
+
block = process_all(node.children[2..-1])
|
96
|
+
while block.length == 1 and block.first.type == :begin
|
97
|
+
block = block.first.children.dup
|
98
|
+
end
|
99
|
+
|
100
|
+
# find use class method calls
|
101
|
+
uses = block.find_all do |node|
|
102
|
+
node.type == :send and node.children[0..1] == [nil, :use]
|
103
|
+
end
|
104
|
+
|
105
|
+
# convert use calls into args
|
106
|
+
args = []
|
107
|
+
uses.each do |use|
|
108
|
+
pending = []
|
109
|
+
use.children[2..-1].each do |node|
|
110
|
+
break unless [:str, :sym].include? node.type
|
111
|
+
pending << s(:arg, *node.children)
|
112
|
+
end
|
113
|
+
args += pending
|
114
|
+
block.delete use
|
115
|
+
end
|
116
|
+
|
117
|
+
# build Appname.controller call statement
|
118
|
+
call = s(:send,
|
119
|
+
s(:const, nil, @ngApp),
|
120
|
+
:controller,
|
121
|
+
s(:sym, name.children.last))
|
122
|
+
|
123
|
+
# replace class with a block
|
124
|
+
node.updated :block, [call, s(:args, *args), s(:begin, *block)]
|
125
|
+
end
|
126
|
+
|
127
|
+
# input:
|
128
|
+
# filter :name do |input|
|
129
|
+
# ...
|
130
|
+
# end
|
131
|
+
#
|
132
|
+
# output:
|
133
|
+
# AppName.filter :name do
|
134
|
+
# return lambda {|input| return ... }
|
135
|
+
# end
|
136
|
+
def on_block(node)
|
137
|
+
return super unless @ngApp
|
138
|
+
|
139
|
+
call = node.children.first
|
140
|
+
return super unless call.children[0..1] == [nil, :filter]
|
141
|
+
|
142
|
+
# insert return
|
143
|
+
children = process_all(node.children[1..-1])
|
144
|
+
block = [children.pop || s(:nil)]
|
145
|
+
while block.length == 1 and block.first.type == :begin
|
146
|
+
block = block.first.children.dup
|
147
|
+
end
|
148
|
+
block.push s(:return, block.pop) unless block.last.type == :return
|
149
|
+
children.push (block.length == 1 ? block.first : s(:begin, *block))
|
150
|
+
|
151
|
+
# construct a function returning a function
|
152
|
+
inner = s(:block, s(:send, nil, :lambda), *children)
|
153
|
+
outer = s(:send, s(:lvar, @ngApp), :filter, *call.children[2..-1])
|
154
|
+
|
155
|
+
node.updated nil, [outer, s(:args), s(:return, inner)]
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
# construct an AST Node
|
161
|
+
def s(type, *args)
|
162
|
+
Parser::AST::Node.new type, args
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'ruby2js'
|
2
|
+
|
3
|
+
module Ruby2JS
|
4
|
+
module Filter
|
5
|
+
module Functions
|
6
|
+
|
7
|
+
# map $$ to $
|
8
|
+
def on_gvar(node)
|
9
|
+
if node.children[0] == :$$
|
10
|
+
node.updated nil, ['$']
|
11
|
+
else
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_send(node)
|
17
|
+
if node.children[1] == :to_s
|
18
|
+
s(:send, node.children[0], :toString, *node.children[2..-1])
|
19
|
+
|
20
|
+
elsif node.children[1] == :to_i
|
21
|
+
node.updated nil, [nil, :parseInt, node.children[0],
|
22
|
+
*node.children[2..-1]]
|
23
|
+
|
24
|
+
elsif node.children[1] == :to_f
|
25
|
+
node.updated nil, [nil, :parseFloat, node.children[0],
|
26
|
+
*node.children[2..-1]]
|
27
|
+
|
28
|
+
else
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# construct an AST Node
|
36
|
+
def s(type, *args)
|
37
|
+
Parser::AST::Node.new type, args
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'ruby2js'
|
2
|
+
|
3
|
+
module Ruby2JS
|
4
|
+
module Filter
|
5
|
+
module Return
|
6
|
+
EXPRESSIONS = [ :array, :float, :hash, :if, :int, :lvar, :nil, :send ]
|
7
|
+
|
8
|
+
def on_block(node)
|
9
|
+
children = process_all(node.children)
|
10
|
+
|
11
|
+
# find the block
|
12
|
+
block = [children.pop || s(:nil)]
|
13
|
+
while block.length == 1 and block.first.type == :begin
|
14
|
+
block = block.first.children.dup
|
15
|
+
end
|
16
|
+
|
17
|
+
if EXPRESSIONS.include? block.last.type
|
18
|
+
block.push s(:return, block.pop)
|
19
|
+
else
|
20
|
+
p block.last.type
|
21
|
+
end
|
22
|
+
|
23
|
+
if block.length == 1
|
24
|
+
children.push block.first
|
25
|
+
else
|
26
|
+
children.push s(:begin, *block)
|
27
|
+
end
|
28
|
+
|
29
|
+
node.updated nil, children
|
30
|
+
end
|
31
|
+
|
32
|
+
def on_def(node)
|
33
|
+
children = process_all(node.children[1..-1])
|
34
|
+
children.unshift node.children.first
|
35
|
+
|
36
|
+
# find the block
|
37
|
+
block = [children.pop || s(:nil)]
|
38
|
+
while block.length == 1 and block.first.type == :begin
|
39
|
+
block = block.first.children.dup
|
40
|
+
end
|
41
|
+
|
42
|
+
if EXPRESSIONS.include? block.last.type
|
43
|
+
block.push s(:return, block.pop)
|
44
|
+
end
|
45
|
+
|
46
|
+
if block.length == 1
|
47
|
+
children.push block.first
|
48
|
+
else
|
49
|
+
children.push s(:begin, *block)
|
50
|
+
end
|
51
|
+
|
52
|
+
node.updated nil, children
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# construct an AST Node
|
58
|
+
def s(type, *args)
|
59
|
+
Parser::AST::Node.new type, args
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/ruby2js.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "ruby2js"
|
5
|
+
s.version = "0.1.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Sam Ruby"]
|
9
|
+
s.date = "2013-11-11"
|
10
|
+
s.description = " The base package maps Ruby syntax to JavaScript semantics.\n Filters may be provided to add Ruby-specific or Framework specific\n behavior.\n"
|
11
|
+
s.email = "rubys@intertwingly.net"
|
12
|
+
s.files = ["ruby2js.gemspec", "README.md", "lib/ruby2js", "lib/ruby2js/version.rb", "lib/ruby2js/converter.rb", "lib/ruby2js/filter", "lib/ruby2js/filter/return.rb", "lib/ruby2js/filter/angularrb.rb", "lib/ruby2js/filter/functions.rb", "lib/ruby2js.rb"]
|
13
|
+
s.homepage = "http://github.com/rubys/ruby2js"
|
14
|
+
s.require_paths = ["lib"]
|
15
|
+
s.rubygems_version = "2.0.7"
|
16
|
+
s.summary = "Minimal yet extensible Ruby to JavaScript conversion."
|
17
|
+
|
18
|
+
if s.respond_to? :specification_version then
|
19
|
+
s.specification_version = 4
|
20
|
+
|
21
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
22
|
+
s.add_runtime_dependency(%q<parser>, [">= 0"])
|
23
|
+
else
|
24
|
+
s.add_dependency(%q<parser>, [">= 0"])
|
25
|
+
end
|
26
|
+
else
|
27
|
+
s.add_dependency(%q<parser>, [">= 0"])
|
28
|
+
end
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby2js
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sam Ruby
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-11-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: parser
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: ! " The base package maps Ruby syntax to JavaScript semantics.\n Filters
|
28
|
+
may be provided to add Ruby-specific or Framework specific\n behavior.\n"
|
29
|
+
email: rubys@intertwingly.net
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- ruby2js.gemspec
|
35
|
+
- README.md
|
36
|
+
- lib/ruby2js/version.rb
|
37
|
+
- lib/ruby2js/converter.rb
|
38
|
+
- lib/ruby2js/filter/return.rb
|
39
|
+
- lib/ruby2js/filter/angularrb.rb
|
40
|
+
- lib/ruby2js/filter/functions.rb
|
41
|
+
- lib/ruby2js.rb
|
42
|
+
homepage: http://github.com/rubys/ruby2js
|
43
|
+
licenses: []
|
44
|
+
metadata: {}
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ! '>='
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
requirements: []
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 2.0.7
|
62
|
+
signing_key:
|
63
|
+
specification_version: 4
|
64
|
+
summary: Minimal yet extensible Ruby to JavaScript conversion.
|
65
|
+
test_files: []
|