unparser 0.6.15 → 0.8.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 +4 -4
- data/lib/unparser/anima.rb +11 -0
- data/lib/unparser/ast/local_variable_scope.rb +28 -24
- data/lib/unparser/ast.rb +18 -22
- data/lib/unparser/buffer.rb +44 -2
- data/lib/unparser/cli.rb +28 -5
- data/lib/unparser/color.rb +2 -2
- data/lib/unparser/either.rb +6 -6
- data/lib/unparser/emitter/args.rb +5 -1
- data/lib/unparser/emitter/array.rb +0 -4
- data/lib/unparser/emitter/array_pattern.rb +1 -9
- data/lib/unparser/emitter/assignment.rb +17 -8
- data/lib/unparser/emitter/begin.rb +0 -6
- data/lib/unparser/emitter/binary.rb +1 -1
- data/lib/unparser/emitter/block.rb +13 -6
- data/lib/unparser/emitter/def.rb +1 -1
- data/lib/unparser/emitter/dstr.rb +6 -5
- data/lib/unparser/emitter/dsym.rb +1 -1
- data/lib/unparser/emitter/ensure.rb +16 -0
- data/lib/unparser/emitter/flow_modifier.rb +1 -7
- data/lib/unparser/emitter/for.rb +1 -1
- data/lib/unparser/emitter/hash.rb +0 -8
- data/lib/unparser/emitter/hash_pattern.rb +1 -1
- data/lib/unparser/emitter/in_pattern.rb +9 -1
- data/lib/unparser/emitter/index.rb +0 -4
- data/lib/unparser/emitter/kwbegin.rb +1 -1
- data/lib/unparser/emitter/match_pattern.rb +6 -1
- data/lib/unparser/emitter/match_pattern_p.rb +6 -1
- data/lib/unparser/emitter/mlhs.rb +7 -1
- data/lib/unparser/emitter/op_assign.rb +0 -10
- data/lib/unparser/emitter/primitive.rb +0 -13
- data/lib/unparser/emitter/range.rb +23 -2
- data/lib/unparser/emitter/regexp.rb +5 -17
- data/lib/unparser/emitter/rescue.rb +7 -1
- data/lib/unparser/emitter/root.rb +2 -9
- data/lib/unparser/emitter/send.rb +1 -5
- data/lib/unparser/emitter/string.rb +31 -0
- data/lib/unparser/emitter/xstr.rb +8 -1
- data/lib/unparser/emitter.rb +9 -10
- data/lib/unparser/generation.rb +14 -14
- data/lib/unparser/node_details.rb +1 -0
- data/lib/unparser/node_helpers.rb +18 -9
- data/lib/unparser/util.rb +23 -0
- data/lib/unparser/validation.rb +68 -28
- data/lib/unparser/writer/array.rb +51 -0
- data/lib/unparser/writer/binary.rb +8 -4
- data/lib/unparser/writer/dynamic_string.rb +128 -135
- data/lib/unparser/writer/regexp.rb +101 -0
- data/lib/unparser/writer/resbody.rb +37 -3
- data/lib/unparser/writer/rescue.rb +3 -7
- data/lib/unparser/writer/send/unary.rb +9 -4
- data/lib/unparser/writer/send.rb +8 -14
- data/lib/unparser/writer.rb +31 -1
- data/lib/unparser.rb +149 -38
- metadata +33 -17
data/lib/unparser/validation.rb
CHANGED
@@ -7,10 +7,14 @@ module Unparser
|
|
7
7
|
:generated_node,
|
8
8
|
:generated_source,
|
9
9
|
:identification,
|
10
|
-
:
|
10
|
+
:original_ast,
|
11
11
|
:original_source
|
12
12
|
)
|
13
13
|
|
14
|
+
class PhaseException
|
15
|
+
include Anima.new(:exception, :phase)
|
16
|
+
end
|
17
|
+
|
14
18
|
# Test if source could be unparsed successfully
|
15
19
|
#
|
16
20
|
# @return [Boolean]
|
@@ -18,10 +22,11 @@ module Unparser
|
|
18
22
|
# @api private
|
19
23
|
#
|
20
24
|
# rubocop:disable Style/OperatorMethodCall
|
25
|
+
# mutant:disable
|
21
26
|
def success?
|
22
27
|
[
|
23
28
|
original_source,
|
24
|
-
|
29
|
+
original_ast,
|
25
30
|
generated_source,
|
26
31
|
generated_node
|
27
32
|
].all?(&:right?) && generated_node.from_right.==(original_node.from_right)
|
@@ -33,7 +38,7 @@ module Unparser
|
|
33
38
|
# @return [String]
|
34
39
|
#
|
35
40
|
# @api private
|
36
|
-
#
|
41
|
+
# mutant:disable
|
37
42
|
def report
|
38
43
|
message = [identification]
|
39
44
|
|
@@ -47,48 +52,57 @@ module Unparser
|
|
47
52
|
end
|
48
53
|
memoize :report
|
49
54
|
|
55
|
+
# mutant:disable
|
56
|
+
def original_node
|
57
|
+
original_ast.fmap(&:node)
|
58
|
+
end
|
59
|
+
|
50
60
|
# Create validator from string
|
51
61
|
#
|
52
62
|
# @param [String] original_source
|
53
63
|
#
|
54
64
|
# @return [Validator]
|
65
|
+
# mutant:disable
|
55
66
|
def self.from_string(original_source)
|
56
|
-
|
57
|
-
.parse_either(original_source)
|
67
|
+
original_ast = parse_ast_either(original_source)
|
58
68
|
|
59
|
-
generated_source =
|
69
|
+
generated_source = original_ast
|
60
70
|
.lmap(&method(:const_unit))
|
61
|
-
.bind(&
|
71
|
+
.bind(&method(:unparse_ast_either))
|
62
72
|
|
63
73
|
generated_node = generated_source
|
64
74
|
.lmap(&method(:const_unit))
|
65
|
-
.bind(&
|
75
|
+
.bind(&method(:parse_ast_either))
|
76
|
+
.fmap(&:node)
|
66
77
|
|
67
78
|
new(
|
68
|
-
|
69
|
-
original_source: Either::Right.new(original_source),
|
70
|
-
original_node: original_node,
|
79
|
+
generated_node: generated_node,
|
71
80
|
generated_source: generated_source,
|
72
|
-
|
81
|
+
identification: '(string)',
|
82
|
+
original_ast: original_ast,
|
83
|
+
original_source: Either::Right.new(original_source)
|
73
84
|
)
|
74
85
|
end
|
75
86
|
|
76
|
-
# Create validator from
|
87
|
+
# Create validator from ast
|
77
88
|
#
|
78
|
-
# @param [
|
89
|
+
# @param [Unparser::AST] ast
|
79
90
|
#
|
80
91
|
# @return [Validator]
|
81
|
-
|
82
|
-
|
92
|
+
#
|
93
|
+
# mutant:disable
|
94
|
+
def self.from_ast(ast:)
|
95
|
+
generated_source = Unparser.unparse_ast_either(ast)
|
83
96
|
|
84
97
|
generated_node = generated_source
|
85
98
|
.lmap(&method(:const_unit))
|
86
|
-
.bind(&
|
99
|
+
.bind(&method(:parse_ast_either))
|
100
|
+
.fmap(&:node)
|
87
101
|
|
88
102
|
new(
|
89
103
|
identification: '(string)',
|
90
104
|
original_source: generated_source,
|
91
|
-
|
105
|
+
original_ast: Either::Right.new(ast),
|
92
106
|
generated_source: generated_source,
|
93
107
|
generated_node: generated_node
|
94
108
|
)
|
@@ -99,24 +113,45 @@ module Unparser
|
|
99
113
|
# @param [Pathname] path
|
100
114
|
#
|
101
115
|
# @return [Validator]
|
116
|
+
#
|
117
|
+
# mutant:disable
|
102
118
|
def self.from_path(path)
|
103
|
-
from_string(path.read).with(identification: path.to_s)
|
119
|
+
from_string(path.read.freeze).with(identification: path.to_s)
|
104
120
|
end
|
105
121
|
|
122
|
+
# mutant:disable
|
123
|
+
def self.unparse_ast_either(ast)
|
124
|
+
Unparser.unparse_ast_either(ast)
|
125
|
+
end
|
126
|
+
private_class_method :unparse_ast_either
|
127
|
+
|
128
|
+
# mutant:disable
|
129
|
+
def self.parse_ast_either(source)
|
130
|
+
Unparser.parse_ast_either(source)
|
131
|
+
end
|
132
|
+
private_class_method :parse_ast_either
|
133
|
+
|
134
|
+
# mutant:disable
|
135
|
+
def self.const_unit(_); end
|
136
|
+
private_class_method :const_unit
|
137
|
+
|
106
138
|
private
|
107
139
|
|
140
|
+
# mutant:disable
|
108
141
|
def make_report(label, attribute_name)
|
109
142
|
["#{label}:"].concat(public_send(attribute_name).either(method(:report_exception), ->(value) { [value] }))
|
110
143
|
end
|
111
144
|
|
112
|
-
|
113
|
-
|
114
|
-
|
145
|
+
# mutant:disable
|
146
|
+
def report_exception(phase_exception)
|
147
|
+
if phase_exception
|
148
|
+
[phase_exception.inspect].concat(phase_exception.backtrace.take(20))
|
115
149
|
else
|
116
|
-
[
|
150
|
+
%w[undefined]
|
117
151
|
end
|
118
152
|
end
|
119
153
|
|
154
|
+
# mutant:disable
|
120
155
|
def node_diff_report
|
121
156
|
diff = nil
|
122
157
|
|
@@ -132,14 +167,13 @@ module Unparser
|
|
132
167
|
diff ? ['Node-Diff:', diff] : []
|
133
168
|
end
|
134
169
|
|
135
|
-
def self.const_unit(_value); end
|
136
|
-
private_class_method :const_unit
|
137
|
-
|
138
170
|
class Literal < self
|
171
|
+
# mutant:disable
|
139
172
|
def success?
|
140
173
|
original_source.eql?(generated_source)
|
141
174
|
end
|
142
175
|
|
176
|
+
# mutant:disable
|
143
177
|
def report
|
144
178
|
message = [identification]
|
145
179
|
|
@@ -155,20 +189,26 @@ module Unparser
|
|
155
189
|
|
156
190
|
private
|
157
191
|
|
192
|
+
# mutant:disable
|
158
193
|
def source_diff_report
|
159
194
|
diff = nil
|
160
195
|
|
161
196
|
original_source.fmap do |original|
|
162
197
|
generated_source.fmap do |generated|
|
163
198
|
diff = Diff.new(
|
164
|
-
original.split("\n", -1),
|
165
|
-
generated.split("\n", -1)
|
199
|
+
encode(original).split("\n", -1),
|
200
|
+
encode(generated).split("\n", -1)
|
166
201
|
).colorized_diff
|
167
202
|
end
|
168
203
|
end
|
169
204
|
|
170
205
|
diff ? ['Source-Diff:', diff] : []
|
171
206
|
end
|
207
|
+
|
208
|
+
# mutant:disable
|
209
|
+
def encode(string)
|
210
|
+
string.encode('UTF-8', invalid: :replace, undef: :replace)
|
211
|
+
end
|
172
212
|
end # Literal
|
173
213
|
end # Validation
|
174
214
|
end # Unparser
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Unparser
|
4
|
+
module Writer
|
5
|
+
class Array
|
6
|
+
include Writer, Adamantium
|
7
|
+
|
8
|
+
MAP = {
|
9
|
+
dsym: '%I',
|
10
|
+
sym: '%i',
|
11
|
+
dstr: '%W',
|
12
|
+
str: '%w'
|
13
|
+
}.freeze
|
14
|
+
private_constant(*constants(false))
|
15
|
+
|
16
|
+
def emit_compact # rubocop:disable Metrics/AbcSize
|
17
|
+
children_generic_type = array_elements_generic_type
|
18
|
+
|
19
|
+
write(MAP.fetch(children_generic_type))
|
20
|
+
|
21
|
+
parentheses('[', ']') do
|
22
|
+
delimited(children, ' ') do |child|
|
23
|
+
if n_sym?(child) || n_str?(child)
|
24
|
+
write(Util.one(child.children).to_s)
|
25
|
+
else
|
26
|
+
write('#{')
|
27
|
+
emitter(Util.one(Util.one(child.children).children)).write_to_buffer
|
28
|
+
write('}')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def array_elements_generic_type
|
37
|
+
children_types = children.to_set(&:type)
|
38
|
+
|
39
|
+
if children_types == Set[:sym, :dsym]
|
40
|
+
:dsym
|
41
|
+
elsif children_types == Set[:str, :dstr]
|
42
|
+
:dstr
|
43
|
+
elsif children_types == Set[]
|
44
|
+
:sym
|
45
|
+
else
|
46
|
+
Util.one(children_types.to_a)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end # Array
|
50
|
+
end # Writer
|
51
|
+
end # Unparser
|
@@ -39,7 +39,7 @@ module Unparser
|
|
39
39
|
tANDOP: '&&'
|
40
40
|
}.freeze
|
41
41
|
|
42
|
-
NEED_KEYWORD = %i[return break next].freeze
|
42
|
+
NEED_KEYWORD = %i[return break next match_pattern_p].freeze
|
43
43
|
|
44
44
|
private_constant(*constants(false))
|
45
45
|
|
@@ -52,9 +52,13 @@ module Unparser
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def dispatch
|
55
|
-
|
56
|
-
|
57
|
-
|
55
|
+
if node.type.eql?(:and) && left.type.equal?(:or)
|
56
|
+
emit_with(KEYWORD_TOKENS)
|
57
|
+
else
|
58
|
+
left_emitter.write_to_buffer
|
59
|
+
write(' ', MAP.fetch(effective_symbol), ' ')
|
60
|
+
visit(right)
|
61
|
+
end
|
58
62
|
end
|
59
63
|
|
60
64
|
private
|
@@ -5,116 +5,123 @@ module Unparser
|
|
5
5
|
class DynamicString
|
6
6
|
include Writer, Adamantium
|
7
7
|
|
8
|
-
PATTERNS_2 =
|
9
|
-
[
|
10
|
-
%i[str_empty begin].freeze,
|
11
|
-
%i[begin str_nl].freeze
|
12
|
-
].freeze
|
13
|
-
|
14
|
-
PATTERNS_3 =
|
15
|
-
[
|
16
|
-
%i[begin str_nl_eol str_nl_eol].freeze,
|
17
|
-
%i[str_nl_eol begin str_nl_eol].freeze,
|
18
|
-
%i[str_ws begin str_nl_eol].freeze
|
19
|
-
].freeze
|
20
|
-
|
21
8
|
FLAT_INTERPOLATION = %i[ivar cvar gvar nth_ref].to_set.freeze
|
22
9
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
10
|
+
# Amount of dstr children at which heredoc emitting is
|
11
|
+
# preferred, but not guaranteed.
|
12
|
+
HEREDOC_THRESHOLD = 8
|
13
|
+
HEREDOC_DELIMITER = 'HEREDOC'
|
14
|
+
HEREDOC_HEADER = "<<-#{HEREDOC_DELIMITER}".freeze
|
15
|
+
HEREDOC_FOOTER = "#{HEREDOC_DELIMITER}\n".freeze
|
27
16
|
|
28
|
-
|
29
|
-
emit_heredoc_footer
|
30
|
-
end
|
17
|
+
private_constant(*constants(false))
|
31
18
|
|
19
|
+
# The raise below is not reachable if unparser is correctly implemented
|
20
|
+
# but has to exist as I have to assume unparser still has bugs.
|
21
|
+
#
|
22
|
+
# But unless I had such a bug in my test corpus: I cannot enable mutant, and if I
|
23
|
+
# knew about such a bug: I'd fix it so would be back at the start.
|
24
|
+
#
|
25
|
+
# TLDR: Good case for a mutant disable.
|
26
|
+
#
|
27
|
+
# mutant:disable
|
32
28
|
def dispatch
|
33
29
|
if heredoc?
|
34
|
-
|
30
|
+
write(HEREDOC_HEADER)
|
31
|
+
buffer.push_heredoc(heredoc_body)
|
32
|
+
elsif round_tripping_segmented_source
|
33
|
+
write(round_tripping_segmented_source)
|
35
34
|
else
|
36
|
-
|
35
|
+
fail UnsupportedNodeError, "Unparser cannot round trip this node: #{node.inspect}"
|
37
36
|
end
|
38
37
|
end
|
39
38
|
|
40
39
|
private
|
41
40
|
|
42
|
-
def heredoc_header
|
43
|
-
'<<-HEREDOC'
|
44
|
-
end
|
45
|
-
|
46
41
|
def heredoc?
|
47
|
-
|
42
|
+
if children.length >= HEREDOC_THRESHOLD
|
43
|
+
round_trips_heredoc?
|
44
|
+
else
|
45
|
+
round_tripping_segmented_source.nil? # && round_trips_heredoc?
|
46
|
+
end
|
48
47
|
end
|
48
|
+
memoize :heredoc?
|
49
49
|
|
50
|
-
def
|
51
|
-
|
50
|
+
def round_trips_heredoc?
|
51
|
+
round_trips?(source: heredoc_source)
|
52
52
|
end
|
53
|
+
memoize :round_trips_heredoc?
|
53
54
|
|
54
|
-
def
|
55
|
-
|
56
|
-
emit_normal_heredoc_body
|
57
|
-
end
|
55
|
+
def round_tripping_segmented_source
|
56
|
+
each_segments(children) do |segments|
|
58
57
|
|
59
|
-
|
60
|
-
write('HEREDOC')
|
61
|
-
end
|
58
|
+
source = segmented_source(segments: segments)
|
62
59
|
|
63
|
-
|
64
|
-
if n_str?(node)
|
65
|
-
classify_str(node)
|
66
|
-
else
|
67
|
-
node.type
|
60
|
+
return source if round_trips?(source: source)
|
68
61
|
end
|
62
|
+
nil
|
69
63
|
end
|
64
|
+
memoize :round_tripping_segmented_source
|
70
65
|
|
71
|
-
def
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
66
|
+
def each_segments(array)
|
67
|
+
yield [array]
|
68
|
+
|
69
|
+
1.upto(array.length) do |take|
|
70
|
+
prefix = [array.take(take)]
|
71
|
+
suffix = array.drop(take)
|
72
|
+
each_segments(suffix) do |items|
|
73
|
+
yield(prefix + items)
|
74
|
+
end
|
80
75
|
end
|
81
76
|
end
|
82
77
|
|
83
|
-
def
|
84
|
-
|
85
|
-
end
|
78
|
+
def segmented_source(segments:)
|
79
|
+
buffer = Buffer.new
|
86
80
|
|
87
|
-
|
88
|
-
|
89
|
-
|
81
|
+
Segmented.new(
|
82
|
+
buffer:,
|
83
|
+
comments:,
|
84
|
+
explicit_encoding: nil,
|
85
|
+
local_variable_scope:,
|
86
|
+
node:,
|
87
|
+
segments:
|
88
|
+
).dispatch
|
90
89
|
|
91
|
-
|
92
|
-
/\A( |\t)+\z/.match?(node.children.first)
|
90
|
+
buffer.content
|
93
91
|
end
|
94
92
|
|
95
|
-
def
|
96
|
-
|
93
|
+
def heredoc_body
|
94
|
+
buffer = Buffer.new
|
95
|
+
|
96
|
+
writer = Heredoc.new(
|
97
|
+
buffer:,
|
98
|
+
comments:,
|
99
|
+
explicit_encoding: nil,
|
100
|
+
local_variable_scope:,
|
101
|
+
node:
|
102
|
+
)
|
103
|
+
|
104
|
+
writer.emit
|
105
|
+
buffer.content
|
97
106
|
end
|
107
|
+
memoize :heredoc_body
|
98
108
|
|
99
|
-
def
|
100
|
-
|
101
|
-
PATTERNS_3.include?(group.map(&method(:classify)))
|
102
|
-
end
|
109
|
+
def heredoc_source
|
110
|
+
"#{HEREDOC_HEADER}\n#{heredoc_body}"
|
103
111
|
end
|
112
|
+
memoize :heredoc_source
|
113
|
+
|
114
|
+
class Heredoc
|
115
|
+
include Writer, Adamantium
|
104
116
|
|
105
|
-
|
106
|
-
|
107
|
-
|
117
|
+
def emit
|
118
|
+
emit_heredoc_body
|
119
|
+
write(HEREDOC_FOOTER)
|
108
120
|
end
|
109
|
-
end
|
110
121
|
|
111
|
-
|
112
|
-
last = children.last
|
113
|
-
n_str?(last) && last.children.first[-1].eql?("\n")
|
114
|
-
end
|
122
|
+
private
|
115
123
|
|
116
|
-
|
117
|
-
buffer.root_indent do
|
124
|
+
def emit_heredoc_body
|
118
125
|
children.each do |child|
|
119
126
|
if n_str?(child)
|
120
127
|
write(escape_dynamic(child.children.first))
|
@@ -123,88 +130,74 @@ module Unparser
|
|
123
130
|
end
|
124
131
|
end
|
125
132
|
end
|
126
|
-
end
|
127
133
|
|
128
|
-
|
129
|
-
|
130
|
-
|
134
|
+
def escape_dynamic(string)
|
135
|
+
string.gsub('#', '\#')
|
136
|
+
end
|
131
137
|
|
132
|
-
|
133
|
-
if FLAT_INTERPOLATION.include?(child.type)
|
134
|
-
write('#')
|
135
|
-
visit(child)
|
136
|
-
elsif n_dstr?(child)
|
137
|
-
emit_body(child.children)
|
138
|
-
else
|
138
|
+
def emit_dynamic(child)
|
139
139
|
write('#{')
|
140
140
|
emit_dynamic_component(child.children.first)
|
141
141
|
write('}')
|
142
142
|
end
|
143
|
-
end
|
144
143
|
|
145
|
-
|
146
|
-
|
147
|
-
end
|
148
|
-
|
149
|
-
def emit_dstr
|
150
|
-
if children.empty?
|
151
|
-
write('%()')
|
152
|
-
else
|
153
|
-
segments.each_with_index do |children, index|
|
154
|
-
emit_segment(children, index)
|
155
|
-
end
|
144
|
+
def emit_dynamic_component(node)
|
145
|
+
visit(node) if node
|
156
146
|
end
|
157
|
-
end
|
158
|
-
|
159
|
-
def breakpoint?(child, current)
|
160
|
-
last_type = current.last&.type
|
147
|
+
end # Heredoc
|
161
148
|
|
162
|
-
|
163
|
-
|
164
|
-
last_type.equal?(:dstr),
|
165
|
-
n_dstr?(child) && last_type
|
166
|
-
].any?
|
167
|
-
end
|
168
|
-
|
169
|
-
def segments
|
170
|
-
segments = []
|
149
|
+
class Segmented
|
150
|
+
include Writer, Adamantium
|
171
151
|
|
172
|
-
segments
|
152
|
+
include anima.add(:segments)
|
173
153
|
|
174
|
-
|
175
|
-
if
|
176
|
-
|
154
|
+
def dispatch
|
155
|
+
if children.empty?
|
156
|
+
write('%()')
|
157
|
+
else
|
158
|
+
segments.each_with_index { |segment, index| emit_segment(segment, index) }
|
177
159
|
end
|
178
|
-
|
179
|
-
current << child
|
180
160
|
end
|
181
161
|
|
182
|
-
|
183
|
-
end
|
162
|
+
private
|
184
163
|
|
185
|
-
|
186
|
-
|
164
|
+
def emit_segment(children, index)
|
165
|
+
write(' ') unless index.zero?
|
187
166
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
167
|
+
write('"')
|
168
|
+
emit_segment_body(children)
|
169
|
+
write('"')
|
170
|
+
end
|
192
171
|
|
193
|
-
|
194
|
-
buffer.root_indent do
|
172
|
+
def emit_segment_body(children)
|
195
173
|
children.each_with_index do |child, index|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
174
|
+
case child.type
|
175
|
+
when :begin
|
176
|
+
write('#{')
|
177
|
+
visit(child.children.first) if child.children.first
|
178
|
+
write('}')
|
179
|
+
when FLAT_INTERPOLATION
|
180
|
+
write('#')
|
181
|
+
visit(child)
|
182
|
+
when :str
|
183
|
+
visit_str(children, child, index)
|
184
|
+
when :dstr
|
185
|
+
emit_segment_body(child.children)
|
205
186
|
end
|
206
187
|
end
|
207
188
|
end
|
189
|
+
|
190
|
+
def visit_str(children, child, index)
|
191
|
+
string = child.children.first
|
192
|
+
|
193
|
+
next_child = children.at(index.succ)
|
194
|
+
|
195
|
+
if next_child && next_child.type.equal?(:str)
|
196
|
+
write(string.gsub('"', '\\"'))
|
197
|
+
else
|
198
|
+
write(child.children.first.inspect[1..-2])
|
199
|
+
end
|
200
|
+
end
|
208
201
|
end
|
209
202
|
end # DynamicString
|
210
203
|
end # Writer
|