ruby2js 3.6.1 → 4.0.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 +1 -7
- data/lib/ruby2js.rb +32 -9
- data/lib/ruby2js/converter.rb +8 -2
- data/lib/ruby2js/converter/assign.rb +159 -0
- data/lib/ruby2js/converter/begin.rb +7 -2
- data/lib/ruby2js/converter/case.rb +7 -2
- data/lib/ruby2js/converter/class.rb +77 -21
- data/lib/ruby2js/converter/class2.rb +39 -11
- data/lib/ruby2js/converter/def.rb +6 -2
- data/lib/ruby2js/converter/dstr.rb +8 -3
- data/lib/ruby2js/converter/hash.rb +9 -5
- data/lib/ruby2js/converter/hide.rb +13 -0
- data/lib/ruby2js/converter/if.rb +10 -2
- data/lib/ruby2js/converter/import.rb +18 -3
- data/lib/ruby2js/converter/kwbegin.rb +9 -2
- data/lib/ruby2js/converter/literal.rb +2 -2
- data/lib/ruby2js/converter/module.rb +37 -5
- data/lib/ruby2js/converter/opasgn.rb +8 -0
- data/lib/ruby2js/converter/send.rb +41 -2
- data/lib/ruby2js/converter/vasgn.rb +5 -0
- data/lib/ruby2js/demo.rb +53 -0
- data/lib/ruby2js/es2022.rb +5 -0
- data/lib/ruby2js/es2022/strict.rb +3 -0
- data/lib/ruby2js/filter.rb +9 -1
- data/lib/ruby2js/filter/active_functions.rb +1 -0
- data/lib/ruby2js/filter/cjs.rb +2 -0
- data/lib/ruby2js/filter/esm.rb +44 -10
- data/lib/ruby2js/filter/functions.rb +84 -95
- data/lib/ruby2js/filter/{wunderbar.rb → jsx.rb} +29 -7
- data/lib/ruby2js/filter/react.rb +191 -56
- data/lib/ruby2js/filter/require.rb +100 -5
- data/lib/ruby2js/filter/return.rb +13 -1
- data/lib/ruby2js/filter/stimulus.rb +185 -0
- data/lib/ruby2js/jsx.rb +291 -0
- data/lib/ruby2js/namespace.rb +75 -0
- data/lib/ruby2js/version.rb +3 -3
- metadata +12 -4
@@ -18,7 +18,9 @@ module Ruby2JS
|
|
18
18
|
node.type == :def and node.children.first == :method_missing
|
19
19
|
end
|
20
20
|
|
21
|
-
if
|
21
|
+
if not name
|
22
|
+
put 'class'
|
23
|
+
elsif name.type == :const and name.children.first == nil
|
22
24
|
put 'class '
|
23
25
|
parse name
|
24
26
|
put '$' if proxied
|
@@ -38,7 +40,8 @@ module Ruby2JS
|
|
38
40
|
begin
|
39
41
|
class_name, @class_name = @class_name, name
|
40
42
|
class_parent, @class_parent = @class_parent, inheritance
|
41
|
-
@rbstack.push(
|
43
|
+
@rbstack.push(@namespace.getOwnProps)
|
44
|
+
@rbstack.last.merge!(@namespace.find(inheritance)) if inheritance
|
42
45
|
constructor = []
|
43
46
|
index = 0
|
44
47
|
|
@@ -46,10 +49,17 @@ module Ruby2JS
|
|
46
49
|
body.each do |m|
|
47
50
|
if m.type == :def
|
48
51
|
prop = m.children.first
|
49
|
-
if prop == :initialize
|
52
|
+
if prop == :initialize and !@rbstack.last[:initialize]
|
50
53
|
constructor = m.children[2..-1]
|
51
|
-
elsif
|
52
|
-
@rbstack.last[prop] = s(:self)
|
54
|
+
elsif prop.to_s.end_with? '='
|
55
|
+
@rbstack.last[prop.to_s[0..-2].to_sym] = s(:autobind, s(:self))
|
56
|
+
else
|
57
|
+
@rbstack.last[prop] = m.is_method? ? s(:autobind, s(:self)) : s(:self)
|
58
|
+
end
|
59
|
+
elsif m.type == :send and m.children[0..1] == [nil, :async]
|
60
|
+
if m.children[2].type == :def
|
61
|
+
prop = m.children[2].children.first
|
62
|
+
@rbstack.last[prop] = s(:autobind, s(:self))
|
53
63
|
end
|
54
64
|
end
|
55
65
|
end
|
@@ -146,10 +156,10 @@ module Ruby2JS
|
|
146
156
|
end
|
147
157
|
end
|
148
158
|
|
149
|
-
if m.type == :def || m.type == :async
|
159
|
+
if m.type == :def || m.type == :defm || m.type == :async
|
150
160
|
@prop = m.children.first
|
151
161
|
|
152
|
-
if @prop == :initialize
|
162
|
+
if @prop == :initialize and !@rbstack.last[:initialize]
|
153
163
|
@prop = :constructor
|
154
164
|
|
155
165
|
if constructor == [] or constructor == [(:super)]
|
@@ -234,13 +244,27 @@ module Ruby2JS
|
|
234
244
|
else
|
235
245
|
if m.children[1] == :include
|
236
246
|
m = m.updated(:begin, m.children[2..-1].map {|mname|
|
237
|
-
|
238
|
-
s(:attr, name, :prototype), mname)
|
247
|
+
@namespace.defineProps @namespace.find(mname)
|
248
|
+
s(:assign, s(:attr, name, :prototype), mname)
|
249
|
+
})
|
239
250
|
end
|
240
251
|
|
241
252
|
skipped = true
|
242
253
|
end
|
243
254
|
|
255
|
+
elsif es2022 and \
|
256
|
+
m.type == :send and m.children.first.type == :self and \
|
257
|
+
m.children[1].to_s.end_with? '='
|
258
|
+
|
259
|
+
put 'static '
|
260
|
+
parse m.updated(:lvasgn, [m.children[1].to_s.sub('=', ''),
|
261
|
+
m.children[2]])
|
262
|
+
|
263
|
+
elsif m.type == :defineProps
|
264
|
+
skipped = true
|
265
|
+
@namespace.defineProps m.children.first
|
266
|
+
@rbstack.last.merge! m.children.first
|
267
|
+
|
244
268
|
else
|
245
269
|
if m.type == :cvasgn and !underscored_private
|
246
270
|
put 'static #$'; put m.children[0].to_s[2..-1]; put ' = '
|
@@ -263,7 +287,7 @@ module Ruby2JS
|
|
263
287
|
end
|
264
288
|
|
265
289
|
if skipped
|
266
|
-
post << [m, comments]
|
290
|
+
post << [m, comments] unless m.type == :defineProps
|
267
291
|
else
|
268
292
|
comments.reverse.each {|comment| insert location, comment}
|
269
293
|
end
|
@@ -300,6 +324,10 @@ module Ruby2JS
|
|
300
324
|
else
|
301
325
|
parse m.updated(:send, [@class_name, *m.children[1..-1]])
|
302
326
|
end
|
327
|
+
elsif m.type == :block and m.children.first.children.first == nil
|
328
|
+
# class method calls passing a block
|
329
|
+
parse s(:block, s(:send, name, *m.children.first.children[1..-1]),
|
330
|
+
*m.children[1..-1])
|
303
331
|
else
|
304
332
|
parse m, :statement
|
305
333
|
end
|
@@ -345,7 +373,7 @@ module Ruby2JS
|
|
345
373
|
ensure
|
346
374
|
@class_name = class_name
|
347
375
|
@class_parent = class_parent
|
348
|
-
@rbstack.pop
|
376
|
+
@namespace.defineProps @rbstack.pop
|
349
377
|
end
|
350
378
|
end
|
351
379
|
end
|
@@ -6,7 +6,7 @@ module Ruby2JS
|
|
6
6
|
# (arg :x)
|
7
7
|
# (...)
|
8
8
|
|
9
|
-
handle :def, :defm, :async do |name, args, body=nil|
|
9
|
+
handle :def, :defm, :async, :deff do |name, args, body=nil|
|
10
10
|
body ||= s(:begin)
|
11
11
|
|
12
12
|
add_implicit_block = false
|
@@ -106,7 +106,7 @@ module Ruby2JS
|
|
106
106
|
# es2015 fat arrow support
|
107
107
|
if \
|
108
108
|
not name and es2015 and @state != :method and @ast.type != :defm and
|
109
|
-
not @prop
|
109
|
+
@ast.type != :deff and not @prop
|
110
110
|
then
|
111
111
|
expr = body
|
112
112
|
expr = expr.children.first while expr.type == :autoreturn
|
@@ -118,6 +118,10 @@ module Ruby2JS
|
|
118
118
|
if EXPRESSIONS.include? expr.type
|
119
119
|
if expr.type == :send and expr.children[0..1] == [nil, :raise]
|
120
120
|
style = :statement
|
121
|
+
elsif expr.type == :send and expr.children.length == 2 and
|
122
|
+
expr.children.first == nil and @rbstack.last and
|
123
|
+
@rbstack.last[expr.children[1]]&.type == :autobind
|
124
|
+
style = :statement
|
121
125
|
else
|
122
126
|
style = :expression
|
123
127
|
end
|
@@ -10,6 +10,11 @@ module Ruby2JS
|
|
10
10
|
# (...))
|
11
11
|
|
12
12
|
handle :dstr, :dsym do |*children|
|
13
|
+
if @state == :expression and children.empty?
|
14
|
+
puts '""'
|
15
|
+
return
|
16
|
+
end
|
17
|
+
|
13
18
|
if es2015
|
14
19
|
# gather length of string parts; if long enough, newlines will
|
15
20
|
# not be escaped (poor man's HEREDOC)
|
@@ -26,7 +31,7 @@ module Ruby2JS
|
|
26
31
|
else
|
27
32
|
put str
|
28
33
|
end
|
29
|
-
|
34
|
+
elsif child != s(:begin)
|
30
35
|
put '${'
|
31
36
|
parse child
|
32
37
|
put '}'
|
@@ -40,8 +45,8 @@ module Ruby2JS
|
|
40
45
|
children.each_with_index do |child, index|
|
41
46
|
put ' + ' unless index == 0
|
42
47
|
|
43
|
-
if child.type == :begin and child.children.length
|
44
|
-
child = child.children.first
|
48
|
+
if child.type == :begin and child.children.length <= 1
|
49
|
+
child = child.children.first || s(:str, '')
|
45
50
|
end
|
46
51
|
|
47
52
|
if child.type == :send
|
@@ -73,7 +73,7 @@ module Ruby2JS
|
|
73
73
|
if right.type == :hash
|
74
74
|
right.children.each do |pair|
|
75
75
|
next unless Parser::AST::Node === pair.children.last
|
76
|
-
if [
|
76
|
+
if %i[block def defm async].include? pair.children.last.type
|
77
77
|
if @comments[pair.children.last]
|
78
78
|
(puts ''; singleton = false) if singleton
|
79
79
|
comments(pair.children.last).each do |comment|
|
@@ -120,22 +120,26 @@ module Ruby2JS
|
|
120
120
|
|
121
121
|
if \
|
122
122
|
anonfn and
|
123
|
-
left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\
|
123
|
+
left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\z/
|
124
124
|
then
|
125
125
|
@prop = left.children.first
|
126
126
|
parse right, :method
|
127
127
|
elsif \
|
128
|
-
es2015 and left.type == :sym and right.type == :lvar
|
129
|
-
|
128
|
+
es2015 and left.type == :sym and (right.type == :lvar or
|
129
|
+
(right.type == :send and right.children.first == nil)) and
|
130
|
+
left.children.last == right.children.last
|
130
131
|
then
|
131
132
|
parse right
|
133
|
+
elsif right.type == :defm and %i[sym str].include? left.type and es2015
|
134
|
+
@prop = left.children.first.to_s
|
135
|
+
parse right
|
132
136
|
else
|
133
137
|
if not [:str, :sym].include? left.type and es2015
|
134
138
|
put '['
|
135
139
|
parse left
|
136
140
|
put ']'
|
137
141
|
elsif \
|
138
|
-
left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\
|
142
|
+
left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\z/
|
139
143
|
then
|
140
144
|
put left.children.first
|
141
145
|
else
|
data/lib/ruby2js/converter/if.rb
CHANGED
@@ -64,10 +64,18 @@ module Ruby2JS
|
|
64
64
|
if else_block.type == :begin
|
65
65
|
else_block = s(:xnode, '', *else_block.children)
|
66
66
|
end
|
67
|
+
else
|
68
|
+
if then_block.type == :begin
|
69
|
+
then_block = s(:kwbegin, then_block)
|
70
|
+
end
|
71
|
+
|
72
|
+
if else_block.type == :begin
|
73
|
+
else_block = s(:kwbegin, else_block)
|
74
|
+
end
|
67
75
|
end
|
68
76
|
|
69
|
-
parse condition; put ' ? '; parse then_block
|
70
|
-
put ' : '; parse else_block
|
77
|
+
parse condition; put ' ? '; parse then_block, @state
|
78
|
+
put ' : '; parse else_block, @state
|
71
79
|
end
|
72
80
|
end
|
73
81
|
end
|
@@ -28,14 +28,29 @@ module Ruby2JS
|
|
28
28
|
put path.inspect
|
29
29
|
else
|
30
30
|
# import (x) from "file.js"
|
31
|
-
default_import = !args.first.is_a?(Array) && [
|
31
|
+
default_import = !args.first.is_a?(Array) && %i[const send attr str].include?(args.first.type)
|
32
|
+
|
33
|
+
if default_import and args.length > 1
|
34
|
+
parse args.shift
|
35
|
+
put ', '
|
36
|
+
default_import = false
|
37
|
+
end
|
38
|
+
|
32
39
|
args = args.first if args.first.is_a?(Array)
|
33
40
|
|
41
|
+
if args.first.type == :array
|
42
|
+
args = args.first.children
|
43
|
+
end
|
44
|
+
|
34
45
|
# handle the default name or { ConstA, Const B } portion
|
35
46
|
put "{ " unless default_import
|
36
47
|
args.each_with_index do |arg, index|
|
37
48
|
put ', ' unless index == 0
|
38
|
-
|
49
|
+
if arg.type == :str
|
50
|
+
put arg.children.first # useful for '*'
|
51
|
+
else
|
52
|
+
parse arg
|
53
|
+
end
|
39
54
|
end
|
40
55
|
put " }" unless default_import
|
41
56
|
|
@@ -43,7 +58,7 @@ module Ruby2JS
|
|
43
58
|
|
44
59
|
# should there be an as clause? e.g., import React as *
|
45
60
|
if path.is_a?(Array) && !path[0].is_a?(String) && path[0].type == :pair && path[0].children[0].children[0] == :as
|
46
|
-
put " as #{path[0].children[1].children
|
61
|
+
put " as #{path[0].children[1].children.last}"
|
47
62
|
|
48
63
|
# advance to the next kwarg, aka from
|
49
64
|
from_kwarg_position = 1
|
@@ -6,7 +6,7 @@ module Ruby2JS
|
|
6
6
|
# (resbody nil nil
|
7
7
|
# (send nil :b)) nil)
|
8
8
|
handle :rescue do |*statements|
|
9
|
-
|
9
|
+
parse s(:kwbegin, s(:rescue, *statements)), @state
|
10
10
|
end
|
11
11
|
|
12
12
|
# (kwbegin
|
@@ -19,7 +19,14 @@ module Ruby2JS
|
|
19
19
|
|
20
20
|
handle :kwbegin do |*children|
|
21
21
|
block = children.first
|
22
|
-
|
22
|
+
|
23
|
+
if @state == :expression
|
24
|
+
parse s(:send, s(:block, s(:send, nil, :proc), s(:args),
|
25
|
+
s(:begin, s(:autoreturn, *children))), :[])
|
26
|
+
return
|
27
|
+
end
|
28
|
+
|
29
|
+
if block&.type == :ensure
|
23
30
|
block, finally = block.children
|
24
31
|
else
|
25
32
|
finally = nil
|
@@ -20,8 +20,8 @@ module Ruby2JS
|
|
20
20
|
def number_format(number)
|
21
21
|
return number.to_s unless es2021
|
22
22
|
parts = number.to_s.split('.')
|
23
|
-
parts[0].gsub
|
24
|
-
parts[1].gsub
|
23
|
+
parts[0] = parts[0].gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1_")
|
24
|
+
parts[1] = parts[1].gsub(/(\d\d\d)(?=\d)/, "\\1_") if parts[1]
|
25
25
|
parts.join('.')
|
26
26
|
end
|
27
27
|
end
|
@@ -4,10 +4,22 @@ module Ruby2JS
|
|
4
4
|
# (module
|
5
5
|
# (const nil :A)
|
6
6
|
# (...)
|
7
|
+
#
|
8
|
+
# Note: modules_hash is an anonymous modules as a value in a hash; the
|
9
|
+
# name has already been output so should be ignored other than
|
10
|
+
# in determining the namespace.
|
11
|
+
|
12
|
+
handle :module, :module_hash do |name, *body|
|
13
|
+
extend = @namespace.enter(name)
|
7
14
|
|
8
|
-
handle :module do |name, *body|
|
9
15
|
if body == [nil]
|
10
|
-
|
16
|
+
if @ast.type == :module and not extend
|
17
|
+
parse @ast.updated(:casgn, [*name.children, s(:hash)])
|
18
|
+
else
|
19
|
+
parse @ast.updated(:hash, [])
|
20
|
+
end
|
21
|
+
|
22
|
+
@namespace.leave
|
11
23
|
return
|
12
24
|
end
|
13
25
|
|
@@ -15,8 +27,20 @@ module Ruby2JS
|
|
15
27
|
body = body.first.children
|
16
28
|
end
|
17
29
|
|
18
|
-
if body.length > 0 and body.all? {|child|
|
19
|
-
|
30
|
+
if body.length > 0 and body.all? {|child|
|
31
|
+
%i[def module].include? child.type or
|
32
|
+
(es2015 and child.type == :class and child.children[1] == nil)}
|
33
|
+
|
34
|
+
if extend
|
35
|
+
parse s(:assign, name, @ast.updated(:class_module,
|
36
|
+
[nil, nil, *body])), :statement
|
37
|
+
elsif @ast.type == :module_hash
|
38
|
+
parse @ast.updated(:class_module, [nil, nil, *body])
|
39
|
+
else
|
40
|
+
parse @ast.updated(:class_module, [name, nil, *body])
|
41
|
+
end
|
42
|
+
|
43
|
+
@namespace.leave
|
20
44
|
return
|
21
45
|
end
|
22
46
|
|
@@ -47,6 +71,8 @@ module Ruby2JS
|
|
47
71
|
symbols << node.children.first
|
48
72
|
elsif node.type == :class and node.children.first.children.first == nil
|
49
73
|
symbols << node.children.first.children.last
|
74
|
+
elsif node.type == :module
|
75
|
+
symbols << node.children.first.children.last
|
50
76
|
end
|
51
77
|
end
|
52
78
|
|
@@ -55,11 +81,17 @@ module Ruby2JS
|
|
55
81
|
|
56
82
|
body = s(:send, s(:block, s(:send, nil, :proc), s(:args),
|
57
83
|
s(:begin, *body)), :[])
|
58
|
-
if name
|
84
|
+
if not name
|
85
|
+
parse body
|
86
|
+
elsif extend
|
87
|
+
parse s(:assign, name, body)
|
88
|
+
elsif name.children.first == nil
|
59
89
|
parse s(:lvasgn, name.children.last, body)
|
60
90
|
else
|
61
91
|
parse s(:send, name.children.first, "#{name.children.last}=", body)
|
62
92
|
end
|
93
|
+
|
94
|
+
@namespace.leave
|
63
95
|
end
|
64
96
|
end
|
65
97
|
end
|
@@ -12,6 +12,14 @@ module Ruby2JS
|
|
12
12
|
var = s(:lvar, var.children.first) if var.type == :lvasgn
|
13
13
|
var = s(:cvar, var.children.first) if var.type == :cvasgn
|
14
14
|
|
15
|
+
if var.type == :lvar
|
16
|
+
name = var.children.first
|
17
|
+
receiver = @rbstack.map {|rb| rb[name]}.compact.last
|
18
|
+
if receiver
|
19
|
+
var = s(:attr, nil, name)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
15
23
|
if \
|
16
24
|
[:+, :-].include?(op) and value.type==:int and
|
17
25
|
(value.children==[1] or value.children==[-1])
|
@@ -41,6 +41,20 @@ module Ruby2JS
|
|
41
41
|
# strip '!' and '?' decorations
|
42
42
|
method = method.to_s[0..-2] if method =~ /\w[!?]$/
|
43
43
|
|
44
|
+
# anonymous class
|
45
|
+
if method == :new and receiver and receiver.children == [nil, :Class] and
|
46
|
+
args.last.type == :def and args.last.children.first == nil
|
47
|
+
|
48
|
+
parent = (args.length > 1) ? args.first : nil
|
49
|
+
|
50
|
+
if es2015
|
51
|
+
return parse s(:class2, nil, parent, *args.last.children[2..-1])
|
52
|
+
else
|
53
|
+
return parse s(:kwbegin, s(:class, s(:const, nil, :$$), parent,
|
54
|
+
*args.last.children[2..-1]), s(:const, nil, :$$))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
44
58
|
# three ways to define anonymous functions
|
45
59
|
if method == :new and receiver and receiver.children == [nil, :Proc]
|
46
60
|
return parse args.first, @state
|
@@ -118,8 +132,13 @@ module Ruby2JS
|
|
118
132
|
|
119
133
|
# resolve anonymous receivers against rbstack
|
120
134
|
receiver ||= @rbstack.map {|rb| rb[method]}.compact.last
|
135
|
+
autobind = nil
|
121
136
|
|
122
137
|
if receiver
|
138
|
+
if receiver.type == :autobind
|
139
|
+
autobind = receiver = receiver.children.first
|
140
|
+
end
|
141
|
+
|
123
142
|
group_receiver = receiver.type == :send &&
|
124
143
|
op_index < operator_index( receiver.children[1] ) if receiver
|
125
144
|
group_receiver ||= GROUP_OPERATORS.include? receiver.type
|
@@ -176,9 +195,15 @@ module Ruby2JS
|
|
176
195
|
receiver.type == :send and
|
177
196
|
receiver.children[1] == :+@ and
|
178
197
|
Parser::AST::Node === receiver.children[0] and
|
179
|
-
receiver.children[0].type
|
198
|
+
%i(class module).include? receiver.children[0].type
|
180
199
|
then
|
181
|
-
|
200
|
+
if receiver.children[0].type == :class
|
201
|
+
parse receiver.children[0].updated(:class_extend)
|
202
|
+
else
|
203
|
+
mod = receiver.children[0]
|
204
|
+
parse s(:assign, mod.children[0],
|
205
|
+
mod.updated(nil, [nil, *mod.children[1..-1]]))
|
206
|
+
end
|
182
207
|
else
|
183
208
|
put method.to_s[0]; parse receiver
|
184
209
|
end
|
@@ -281,6 +306,12 @@ module Ruby2JS
|
|
281
306
|
else
|
282
307
|
put 'await ' if @ast.type == :await
|
283
308
|
|
309
|
+
if method == :bind and receiver&.type == :send
|
310
|
+
if receiver.children.length == 2 and receiver.children.first == nil
|
311
|
+
receiver = receiver.updated(:attr) # prevent autobind
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
284
315
|
if not ast.is_method? and ast.type != :send!
|
285
316
|
if receiver
|
286
317
|
(group_receiver ? group(receiver) : parse(receiver))
|
@@ -303,6 +334,14 @@ module Ruby2JS
|
|
303
334
|
compact { puts "("; parse_all(*args, join: ",#@ws"); sput ')' }
|
304
335
|
end
|
305
336
|
end
|
337
|
+
|
338
|
+
if autobind and not ast.is_method? and ast.type != :attr
|
339
|
+
if @state == :statement
|
340
|
+
put '()'
|
341
|
+
else
|
342
|
+
put '.bind('; parse(autobind); put ')'
|
343
|
+
end
|
344
|
+
end
|
306
345
|
end
|
307
346
|
end
|
308
347
|
|