ruby2js 3.0.15 → 3.3.0
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.
- checksums.yaml +4 -4
- data/README.md +92 -7
- data/lib/ruby2js.rb +30 -3
- data/lib/ruby2js/converter.rb +23 -3
- data/lib/ruby2js/converter/args.rb +31 -1
- data/lib/ruby2js/converter/block.rb +2 -2
- data/lib/ruby2js/converter/casgn.rb +3 -2
- data/lib/ruby2js/converter/class.rb +2 -2
- data/lib/ruby2js/converter/class2.rb +117 -13
- data/lib/ruby2js/converter/cvar.rb +5 -3
- data/lib/ruby2js/converter/cvasgn.rb +5 -3
- data/lib/ruby2js/converter/def.rb +2 -2
- data/lib/ruby2js/converter/for.rb +8 -1
- data/lib/ruby2js/converter/hash.rb +22 -6
- data/lib/ruby2js/converter/if.rb +13 -2
- data/lib/ruby2js/converter/import.rb +38 -0
- data/lib/ruby2js/converter/ivar.rb +2 -0
- data/lib/ruby2js/converter/ivasgn.rb +1 -1
- data/lib/ruby2js/converter/kwbegin.rb +16 -2
- data/lib/ruby2js/converter/logical.rb +46 -1
- data/lib/ruby2js/converter/opasgn.rb +5 -2
- data/lib/ruby2js/converter/regexp.rb +27 -2
- data/lib/ruby2js/converter/return.rb +1 -1
- data/lib/ruby2js/converter/send.rb +160 -69
- data/lib/ruby2js/converter/super.rb +15 -9
- data/lib/ruby2js/converter/xnode.rb +89 -0
- data/lib/ruby2js/es2018.rb +5 -0
- data/lib/ruby2js/es2018/strict.rb +3 -0
- data/lib/ruby2js/es2019.rb +5 -0
- data/lib/ruby2js/es2019/strict.rb +3 -0
- data/lib/ruby2js/es2020.rb +5 -0
- data/lib/ruby2js/es2020/strict.rb +3 -0
- data/lib/ruby2js/es2021.rb +5 -0
- data/lib/ruby2js/es2021/strict.rb +3 -0
- data/lib/ruby2js/filter.rb +1 -0
- data/lib/ruby2js/filter/cjs.rb +2 -2
- data/lib/ruby2js/filter/esm.rb +72 -0
- data/lib/ruby2js/filter/functions.rb +218 -34
- data/lib/ruby2js/filter/matchAll.rb +49 -0
- data/lib/ruby2js/filter/node.rb +18 -10
- data/lib/ruby2js/filter/nokogiri.rb +13 -13
- data/lib/ruby2js/filter/react.rb +190 -30
- data/lib/ruby2js/filter/require.rb +1 -4
- data/lib/ruby2js/filter/rubyjs.rb +4 -4
- data/lib/ruby2js/filter/vue.rb +45 -17
- data/lib/ruby2js/filter/wunderbar.rb +63 -0
- data/lib/ruby2js/serializer.rb +25 -11
- data/lib/ruby2js/version.rb +2 -2
- metadata +16 -4
@@ -33,21 +33,99 @@ module Ruby2JS
|
|
33
33
|
class_name, @class_name = @class_name, name
|
34
34
|
class_parent, @class_parent = @class_parent, inheritance
|
35
35
|
@rbstack.push({})
|
36
|
+
constructor = []
|
37
|
+
index = 0
|
36
38
|
|
37
|
-
# capture method names for automatic self referencing
|
38
|
-
body.
|
39
|
+
# capture constructor, method names for automatic self referencing
|
40
|
+
body.each do |m|
|
39
41
|
if m.type == :def
|
40
42
|
prop = m.children.first
|
41
|
-
|
43
|
+
if prop == :initialize
|
44
|
+
constructor = m.children[2..-1]
|
45
|
+
elsif not prop.to_s.end_with? '='
|
42
46
|
@rbstack.last[prop] = s(:self)
|
43
47
|
end
|
44
48
|
end
|
45
49
|
end
|
46
50
|
|
51
|
+
# private variable declarations
|
52
|
+
if es2020
|
53
|
+
ivars = Set.new
|
54
|
+
cvars = Set.new
|
55
|
+
|
56
|
+
# find ivars and cvars
|
57
|
+
walk = proc do |ast|
|
58
|
+
ivars << ast.children.first if ast.type === :ivar
|
59
|
+
ivars << ast.children.first if ast.type === :ivasgn
|
60
|
+
cvars << ast.children.first if ast.type === :cvar
|
61
|
+
cvars << ast.children.first if ast.type === :cvasgn
|
62
|
+
|
63
|
+
ast.children.each do |child|
|
64
|
+
walk[child] if child.is_a? Parser::AST::Node
|
65
|
+
end
|
66
|
+
|
67
|
+
if ast.type == :send and ast.children.first == nil
|
68
|
+
if ast.children[1] == :attr_accessor
|
69
|
+
ast.children[2..-1].each_with_index do |child_sym, index2|
|
70
|
+
ivars << :"@#{child_sym.children.first}"
|
71
|
+
end
|
72
|
+
elsif ast.children[1] == :attr_reader
|
73
|
+
ast.children[2..-1].each_with_index do |child_sym, index2|
|
74
|
+
ivars << :"@#{child_sym.children.first}"
|
75
|
+
end
|
76
|
+
elsif ast.children[1] == :attr_writer
|
77
|
+
ast.children[2..-1].each_with_index do |child_sym, index2|
|
78
|
+
ivars << :"@#{child_sym.children.first}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
walk[@ast]
|
85
|
+
|
86
|
+
# process leading initializers in constructor
|
87
|
+
while constructor.length == 1 and constructor.first.type == :begin
|
88
|
+
constructor = constructor.first.children.dup
|
89
|
+
end
|
90
|
+
|
91
|
+
# emit additional class declarations
|
92
|
+
unless cvars.empty?
|
93
|
+
body.each do |m|
|
94
|
+
cvars.delete m.children.first if m.type == :cvasgn
|
95
|
+
end
|
96
|
+
end
|
97
|
+
cvars.to_a.sort.each do |cvar|
|
98
|
+
put(index == 0 ? @nl : @sep)
|
99
|
+
index += 1
|
100
|
+
put 'static #$' + cvar.to_s[2..-1]
|
101
|
+
end
|
102
|
+
|
103
|
+
while constructor.length > 0 and constructor.first.type == :ivasgn
|
104
|
+
put(index == 0 ? @nl : @sep)
|
105
|
+
index += 1
|
106
|
+
statement = constructor.shift
|
107
|
+
put '#'
|
108
|
+
put statement.children.first.to_s[1..-1]
|
109
|
+
put ' = '
|
110
|
+
parse statement.children.last
|
111
|
+
|
112
|
+
ivars.delete statement.children.first
|
113
|
+
end
|
114
|
+
|
115
|
+
# emit additional instance declarations
|
116
|
+
ivars.to_a.sort.each do |ivar|
|
117
|
+
put(index == 0 ? @nl : @sep)
|
118
|
+
index += 1
|
119
|
+
put '#' + ivar.to_s[1..-1]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# process class definition
|
47
124
|
post = []
|
48
125
|
skipped = false
|
49
|
-
body.
|
126
|
+
body.each do |m|
|
50
127
|
put(index == 0 ? @nl : @sep) unless skipped
|
128
|
+
index += 1
|
51
129
|
comments = comments(m)
|
52
130
|
location = output_location
|
53
131
|
skipped = false
|
@@ -67,7 +145,13 @@ module Ruby2JS
|
|
67
145
|
|
68
146
|
if @prop == :initialize
|
69
147
|
@prop = :constructor
|
70
|
-
|
148
|
+
|
149
|
+
if constructor == [] or constructor == [(:super)]
|
150
|
+
skipped = true
|
151
|
+
next
|
152
|
+
end
|
153
|
+
|
154
|
+
m = m.updated(m.type, [@prop, m.children[1], s(:begin, *constructor)])
|
71
155
|
elsif not m.is_method?
|
72
156
|
@prop = "get #{@prop}"
|
73
157
|
m = m.updated(m.type, [*m.children[0..1],
|
@@ -83,12 +167,13 @@ module Ruby2JS
|
|
83
167
|
|
84
168
|
begin
|
85
169
|
@instance_method = m
|
86
|
-
|
170
|
+
@class_method = nil
|
171
|
+
parse m # unless skipped
|
87
172
|
ensure
|
88
173
|
@instance_method = nil
|
89
174
|
end
|
90
175
|
|
91
|
-
elsif
|
176
|
+
elsif \
|
92
177
|
[:defs, :asyncs].include? m.type and m.children.first.type == :self
|
93
178
|
then
|
94
179
|
|
@@ -108,27 +193,35 @@ module Ruby2JS
|
|
108
193
|
@prop.sub! 'static', 'static async' if m.type == :asyncs
|
109
194
|
|
110
195
|
m = m.updated(:def, m.children[1..3])
|
111
|
-
|
196
|
+
begin
|
197
|
+
@instance_method = nil
|
198
|
+
@class_method = m
|
199
|
+
parse m # unless skipped
|
200
|
+
ensure
|
201
|
+
@instance_method = nil
|
202
|
+
end
|
112
203
|
|
113
204
|
elsif m.type == :send and m.children.first == nil
|
205
|
+
p = es2020 ? '#' : '_'
|
206
|
+
|
114
207
|
if m.children[1] == :attr_accessor
|
115
208
|
m.children[2..-1].each_with_index do |child_sym, index2|
|
116
209
|
put @sep unless index2 == 0
|
117
210
|
var = child_sym.children.first
|
118
|
-
put "get #{var}() {#{@nl}return this
|
119
|
-
put "set #{var}(#{var}) {#{@nl}this
|
211
|
+
put "get #{var}() {#{@nl}return this.#{p}#{var}#@nl}#@sep"
|
212
|
+
put "set #{var}(#{var}) {#{@nl}this.#{p}#{var} = #{var}#@nl}"
|
120
213
|
end
|
121
214
|
elsif m.children[1] == :attr_reader
|
122
215
|
m.children[2..-1].each_with_index do |child_sym, index2|
|
123
216
|
put @sep unless index2 == 0
|
124
217
|
var = child_sym.children.first
|
125
|
-
put "get #{var}() {#{@nl}return this
|
218
|
+
put "get #{var}() {#{@nl}return this.#{p}#{var}#@nl}"
|
126
219
|
end
|
127
220
|
elsif m.children[1] == :attr_writer
|
128
221
|
m.children[2..-1].each_with_index do |child_sym, index2|
|
129
222
|
put @sep unless index2 == 0
|
130
223
|
var = child_sym.children.first
|
131
|
-
put "set #{var}(#{var}) {#{@nl}this
|
224
|
+
put "set #{var}(#{var}) {#{@nl}this.#{p}#{var} = #{var}#@nl}"
|
132
225
|
end
|
133
226
|
elsif [:private, :protected, :public].include? m.children[1]
|
134
227
|
raise Error.new("class #{m.children[1]} is not supported", @ast)
|
@@ -143,10 +236,21 @@ module Ruby2JS
|
|
143
236
|
end
|
144
237
|
|
145
238
|
else
|
146
|
-
|
239
|
+
if m.type == :cvasgn and es2020
|
240
|
+
put 'static #$'; put m.children[0].to_s[2..-1]; put ' = '
|
241
|
+
parse m.children[1]
|
242
|
+
else
|
243
|
+
skipped = true
|
244
|
+
end
|
147
245
|
|
148
246
|
if m.type == :casgn and m.children[0] == nil
|
149
247
|
@rbstack.last[m.children[1]] = name
|
248
|
+
|
249
|
+
if es2020
|
250
|
+
put 'static '; put m.children[1].to_s; put ' = '
|
251
|
+
parse m.children[2]
|
252
|
+
skipped = false
|
253
|
+
end
|
150
254
|
elsif m.type == :alias
|
151
255
|
@rbstack.last[m.children[0]] = name
|
152
256
|
end
|
@@ -4,14 +4,16 @@ module Ruby2JS
|
|
4
4
|
# (cvar :@@a)
|
5
5
|
|
6
6
|
handle :cvar do |var|
|
7
|
+
prefix = es2020 ? '#$' : '_'
|
8
|
+
|
7
9
|
@class_name ||= nil
|
8
10
|
if @class_name
|
9
11
|
parse @class_name
|
10
|
-
put var.to_s.sub('@@', "
|
12
|
+
put var.to_s.sub('@@', ".#{prefix}")
|
11
13
|
elsif @prototype
|
12
|
-
put var.to_s.sub('@@',
|
14
|
+
put var.to_s.sub('@@', "this.#{prefix}")
|
13
15
|
else
|
14
|
-
put var.to_s.sub('@@',
|
16
|
+
put var.to_s.sub('@@', "this.constructor.#{prefix}")
|
15
17
|
end
|
16
18
|
end
|
17
19
|
end
|
@@ -7,13 +7,15 @@ module Ruby2JS
|
|
7
7
|
handle :cvasgn do |var, expression=nil|
|
8
8
|
multi_assign_declarations if @state == :statement
|
9
9
|
|
10
|
+
prefix = es2020 ? '#$' : '_'
|
11
|
+
|
10
12
|
if @class_name
|
11
13
|
parse @class_name
|
12
|
-
put var.to_s.sub('@@', "
|
14
|
+
put var.to_s.sub('@@', ".#{prefix}")
|
13
15
|
elsif @prototype
|
14
|
-
put var.to_s.sub('@@',
|
16
|
+
put var.to_s.sub('@@', "this.#{prefix}")
|
15
17
|
else
|
16
|
-
put var.to_s.sub('@@',
|
18
|
+
put var.to_s.sub('@@', "this.constructor.#{prefix}")
|
17
19
|
end
|
18
20
|
|
19
21
|
if expression
|
@@ -88,7 +88,7 @@ module Ruby2JS
|
|
88
88
|
put 'async ' if @ast.type == :async
|
89
89
|
|
90
90
|
# es2015 fat arrow support
|
91
|
-
if
|
91
|
+
if \
|
92
92
|
not name and es2015 and @state != :method and @ast.type != :defm and
|
93
93
|
not @prop
|
94
94
|
then
|
@@ -105,7 +105,7 @@ module Ruby2JS
|
|
105
105
|
else
|
106
106
|
style = :expression
|
107
107
|
end
|
108
|
-
elsif
|
108
|
+
elsif \
|
109
109
|
expr.type == :if and expr.children[1] and expr.children[2] and
|
110
110
|
EXPRESSIONS.include? expr.children[1].type and
|
111
111
|
EXPRESSIONS.include? expr.children[2].type
|
@@ -10,11 +10,18 @@ module Ruby2JS
|
|
10
10
|
# (...)
|
11
11
|
|
12
12
|
handle :for, :for_of do |var, expression, block|
|
13
|
+
if @jsx and @ast.type == :for_of
|
14
|
+
parse s(:block, s(:send, expression, :map),
|
15
|
+
s(:args, s(:arg, var.children[0])),
|
16
|
+
s(:autoreturn, block))
|
17
|
+
return
|
18
|
+
end
|
19
|
+
|
13
20
|
begin
|
14
21
|
vars = @vars.dup
|
15
22
|
next_token, @next_token = @next_token, :continue
|
16
23
|
put "for (#{es2015 ? 'let' : 'var'} "; parse var
|
17
|
-
if [:irange, :erange].include? expression.type
|
24
|
+
if expression and [:irange, :erange].include? expression.type
|
18
25
|
put ' = '; parse expression.children.first; put '; '; parse var
|
19
26
|
(expression.type == :erange ? put(' < ') : put(' <= '))
|
20
27
|
parse expression.children.last; put '; '; parse var; put '++'
|
@@ -12,10 +12,26 @@ module Ruby2JS
|
|
12
12
|
|
13
13
|
(singleton ? put('{') : puts('{'))
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
index = 0
|
16
|
+
while pairs.length > 0
|
17
|
+
node = pairs.shift
|
18
18
|
(singleton ? put(', ') : put(",#@ws")) unless index == 0
|
19
|
+
index += 1
|
20
|
+
|
21
|
+
if node.type == :kwsplat
|
22
|
+
if es2018
|
23
|
+
if node.children.first.type == :hash
|
24
|
+
pairs.unshift(*node.children.first.children)
|
25
|
+
index = 0
|
26
|
+
else
|
27
|
+
puts '...'; parse node.children.first
|
28
|
+
end
|
29
|
+
|
30
|
+
next
|
31
|
+
else
|
32
|
+
raise Error.new("kwsplat", @ast)
|
33
|
+
end
|
34
|
+
end
|
19
35
|
|
20
36
|
if not @comments[node].empty?
|
21
37
|
(puts ''; singleton = false) if singleton
|
@@ -81,13 +97,13 @@ module Ruby2JS
|
|
81
97
|
end
|
82
98
|
end
|
83
99
|
|
84
|
-
if
|
100
|
+
if \
|
85
101
|
anonfn and
|
86
102
|
left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\Z/
|
87
103
|
then
|
88
104
|
@prop = left.children.first
|
89
105
|
parse right, :method
|
90
|
-
elsif
|
106
|
+
elsif \
|
91
107
|
es2015 and left.type == :sym and right.type == :lvar and
|
92
108
|
left.children == right.children
|
93
109
|
then
|
@@ -97,7 +113,7 @@ module Ruby2JS
|
|
97
113
|
put '['
|
98
114
|
parse left
|
99
115
|
put ']'
|
100
|
-
elsif
|
116
|
+
elsif \
|
101
117
|
left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\Z/
|
102
118
|
then
|
103
119
|
put left.children.first
|
data/lib/ruby2js/converter/if.rb
CHANGED
@@ -55,8 +55,19 @@ module Ruby2JS
|
|
55
55
|
end
|
56
56
|
else
|
57
57
|
else_block ||= s(:nil)
|
58
|
-
|
59
|
-
|
58
|
+
|
59
|
+
if @jsx
|
60
|
+
if then_block.type == :begin
|
61
|
+
then_block = s(:xnode, '', *then_block.children)
|
62
|
+
end
|
63
|
+
|
64
|
+
if else_block.type == :begin
|
65
|
+
else_block = s(:xnode, '', *else_block.children)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
parse condition; put ' ? '; parse then_block
|
70
|
+
put ' : '; parse else_block
|
60
71
|
end
|
61
72
|
end
|
62
73
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Ruby2JS
|
2
|
+
class Converter
|
3
|
+
|
4
|
+
# (import str const)
|
5
|
+
|
6
|
+
# NOTE: import is a synthetic
|
7
|
+
|
8
|
+
handle :import do |path, *args|
|
9
|
+
put 'import '
|
10
|
+
|
11
|
+
args.each_with_index do |arg, index|
|
12
|
+
put ', ' unless index == 0
|
13
|
+
parse arg
|
14
|
+
end
|
15
|
+
|
16
|
+
put ' from '
|
17
|
+
put path.inspect
|
18
|
+
end
|
19
|
+
|
20
|
+
# (export const)
|
21
|
+
|
22
|
+
# NOTE: export is a synthetic
|
23
|
+
|
24
|
+
handle :export do |*args|
|
25
|
+
put 'export '
|
26
|
+
|
27
|
+
if args.first == :default
|
28
|
+
put 'default '
|
29
|
+
args.shift
|
30
|
+
end
|
31
|
+
|
32
|
+
args.each_with_index do |arg, index|
|
33
|
+
put ', ' unless index == 0
|
34
|
+
parse arg
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -7,7 +7,7 @@ module Ruby2JS
|
|
7
7
|
handle :ivasgn do |var, expression=nil|
|
8
8
|
multi_assign_declarations if @state == :statement
|
9
9
|
|
10
|
-
put "#{ var.to_s.sub('@', 'this._') }"
|
10
|
+
put "#{ var.to_s.sub('@', 'this.' + (es2020 ? '#' : '_')) }"
|
11
11
|
if expression
|
12
12
|
put " = "; parse expression
|
13
13
|
end
|
@@ -51,13 +51,27 @@ module Ruby2JS
|
|
51
51
|
puts "try {"; scope body; sput '}'
|
52
52
|
|
53
53
|
if recovers
|
54
|
-
var ||= s(:gvar, :$EXCEPTION)
|
55
54
|
|
56
55
|
if recovers.length == 1 and not recovers.first.children.first
|
56
|
+
# find reference to exception ($!)
|
57
|
+
walk = proc do |ast|
|
58
|
+
result = ast if ast.type === :gvar and ast.children.first == :$!
|
59
|
+
ast.children.each do |child|
|
60
|
+
result ||= walk[child] if child.is_a? Parser::AST::Node
|
61
|
+
end
|
62
|
+
result
|
63
|
+
end
|
64
|
+
|
57
65
|
# single catch with no exception named
|
58
|
-
|
66
|
+
if es2019 and not var and not walk[@ast]
|
67
|
+
puts " catch {"
|
68
|
+
else
|
69
|
+
var ||= s(:gvar, :$EXCEPTION)
|
70
|
+
put " catch ("; parse var; puts ") {"
|
71
|
+
end
|
59
72
|
scope recovers.first.children.last; sput '}'
|
60
73
|
else
|
74
|
+
var ||= s(:gvar, :$EXCEPTION)
|
61
75
|
put " catch ("; parse var; puts ') {'
|
62
76
|
|
63
77
|
first = true
|
@@ -14,6 +14,17 @@ module Ruby2JS
|
|
14
14
|
|
15
15
|
handle :and, :or do |left, right|
|
16
16
|
type = @ast.type
|
17
|
+
|
18
|
+
|
19
|
+
if es2020 and type == :and
|
20
|
+
node = rewrite(left, right)
|
21
|
+
if node.type == :csend
|
22
|
+
return parse right.updated(node.type, node.children)
|
23
|
+
else
|
24
|
+
left, right = node.children
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
17
28
|
op_index = operator_index type
|
18
29
|
|
19
30
|
lgroup = LOGICAL.include?( left.type ) &&
|
@@ -25,7 +36,7 @@ module Ruby2JS
|
|
25
36
|
rgroup = true if right.type == :begin
|
26
37
|
|
27
38
|
put '(' if lgroup; parse left; put ')' if lgroup
|
28
|
-
put (type==:and ? ' && ' : ' || ')
|
39
|
+
put (type==:and ? ' && ' : ((@or == :nullish and es2020) ? ' ?? ' : ' || '))
|
29
40
|
put '(' if rgroup; parse right; put ')' if rgroup
|
30
41
|
end
|
31
42
|
|
@@ -51,5 +62,39 @@ module Ruby2JS
|
|
51
62
|
put '!'; put '(' if group; parse expr; put ')' if group
|
52
63
|
end
|
53
64
|
end
|
65
|
+
|
66
|
+
# rewrite a && a.b to a&.b
|
67
|
+
def rewrite(left, right)
|
68
|
+
if left && left.type == :and
|
69
|
+
left = rewrite(*left.children)
|
70
|
+
end
|
71
|
+
|
72
|
+
if right.type != :send
|
73
|
+
s(:and, left, right)
|
74
|
+
elsif conditionally_equals(left, right.children.first)
|
75
|
+
# a && a.b => a&.b
|
76
|
+
right.updated(:csend, [left, right.children.last])
|
77
|
+
elsif conditionally_equals(left.children.last, right.children.first)
|
78
|
+
# a && b && b.c => a && b&.c
|
79
|
+
left.updated(:and, [left.children.first,
|
80
|
+
left.children.last.updated(:csend,
|
81
|
+
[left.children.last, right.children.last])])
|
82
|
+
else
|
83
|
+
s(:and, left, right)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# determine if two trees are identical, modulo conditionalilties
|
88
|
+
# in other words a.b == a&.b
|
89
|
+
def conditionally_equals(left, right)
|
90
|
+
if left == right
|
91
|
+
true
|
92
|
+
elsif !left or !right or left.type != :csend or right.type != :send
|
93
|
+
false
|
94
|
+
else
|
95
|
+
conditionally_equals(left.children.first, right.children.first) &&
|
96
|
+
conditionally_equals(left.children.last, right.children.last)
|
97
|
+
end
|
98
|
+
end
|
54
99
|
end
|
55
100
|
end
|