unparser 0.4.9 → 0.5.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/bin/unparser +1 -1
- data/lib/unparser.rb +98 -61
- data/lib/unparser/ast.rb +0 -1
- data/lib/unparser/ast/local_variable_scope.rb +6 -76
- data/lib/unparser/buffer.rb +19 -16
- data/lib/unparser/cli.rb +26 -39
- data/lib/unparser/color.rb +0 -3
- data/lib/unparser/comments.rb +0 -26
- data/lib/unparser/constants.rb +4 -53
- data/lib/unparser/diff.rb +0 -17
- data/lib/unparser/dsl.rb +0 -32
- data/lib/unparser/emitter.rb +23 -425
- data/lib/unparser/emitter/alias.rb +2 -8
- data/lib/unparser/emitter/args.rb +45 -0
- data/lib/unparser/emitter/argument.rb +8 -166
- data/lib/unparser/emitter/array.rb +27 -0
- data/lib/unparser/emitter/array_pattern.rb +29 -0
- data/lib/unparser/emitter/assignment.rb +36 -127
- data/lib/unparser/emitter/begin.rb +9 -84
- data/lib/unparser/emitter/binary.rb +7 -20
- data/lib/unparser/emitter/block.rb +57 -41
- data/lib/unparser/emitter/case.rb +6 -48
- data/lib/unparser/emitter/case_guard.rb +27 -0
- data/lib/unparser/emitter/case_match.rb +40 -0
- data/lib/unparser/emitter/cbase.rb +1 -3
- data/lib/unparser/emitter/class.rb +6 -26
- data/lib/unparser/emitter/const_pattern.rb +24 -0
- data/lib/unparser/emitter/def.rb +7 -51
- data/lib/unparser/emitter/defined.rb +2 -12
- data/lib/unparser/emitter/dstr.rb +22 -0
- data/lib/unparser/emitter/dsym.rb +41 -0
- data/lib/unparser/emitter/flipflop.rb +11 -10
- data/lib/unparser/emitter/float.rb +29 -0
- data/lib/unparser/emitter/flow_modifier.rb +8 -55
- data/lib/unparser/emitter/for.rb +5 -19
- data/lib/unparser/emitter/hash.rb +74 -0
- data/lib/unparser/emitter/hash_pattern.rb +67 -0
- data/lib/unparser/emitter/hookexe.rb +5 -11
- data/lib/unparser/emitter/if.rb +9 -73
- data/lib/unparser/emitter/in_match.rb +21 -0
- data/lib/unparser/emitter/in_pattern.rb +34 -0
- data/lib/unparser/emitter/index.rb +21 -88
- data/lib/unparser/emitter/kwbegin.rb +31 -0
- data/lib/unparser/emitter/lambda.rb +0 -8
- data/lib/unparser/emitter/masgn.rb +20 -0
- data/lib/unparser/emitter/match.rb +3 -17
- data/lib/unparser/emitter/match_alt.rb +23 -0
- data/lib/unparser/emitter/match_as.rb +21 -0
- data/lib/unparser/emitter/match_rest.rb +26 -0
- data/lib/unparser/emitter/match_var.rb +19 -0
- data/lib/unparser/emitter/mlhs.rb +40 -0
- data/lib/unparser/emitter/module.rb +3 -9
- data/lib/unparser/emitter/op_assign.rb +12 -27
- data/lib/unparser/emitter/pin.rb +19 -0
- data/lib/unparser/emitter/primitive.rb +93 -0
- data/lib/unparser/emitter/range.rb +35 -0
- data/lib/unparser/emitter/regexp.rb +35 -0
- data/lib/unparser/emitter/repetition.rb +17 -57
- data/lib/unparser/emitter/rescue.rb +1 -97
- data/lib/unparser/emitter/root.rb +17 -1
- data/lib/unparser/emitter/send.rb +10 -219
- data/lib/unparser/emitter/simple.rb +33 -0
- data/lib/unparser/emitter/splat.rb +2 -18
- data/lib/unparser/emitter/super.rb +1 -29
- data/lib/unparser/emitter/undef.rb +1 -9
- data/lib/unparser/emitter/variable.rb +1 -31
- data/lib/unparser/emitter/xstr.rb +72 -0
- data/lib/unparser/emitter/yield.rb +1 -9
- data/lib/unparser/generation.rb +250 -0
- data/lib/unparser/node_details.rb +21 -0
- data/lib/unparser/node_details/send.rb +62 -0
- data/lib/unparser/node_helpers.rb +45 -6
- data/lib/unparser/validation.rb +37 -35
- data/lib/unparser/writer.rb +15 -0
- data/lib/unparser/writer/binary.rb +99 -0
- data/lib/unparser/writer/dynamic_string.rb +229 -0
- data/lib/unparser/writer/resbody.rb +40 -0
- data/lib/unparser/writer/rescue.rb +39 -0
- data/lib/unparser/writer/send.rb +124 -0
- data/lib/unparser/{emitter → writer}/send/attribute_assignment.rb +11 -26
- data/lib/unparser/writer/send/binary.rb +27 -0
- data/lib/unparser/writer/send/conditional.rb +25 -0
- data/lib/unparser/writer/send/regular.rb +33 -0
- data/lib/unparser/{emitter → writer}/send/unary.rb +10 -17
- metadata +63 -31
- data/lib/unparser/emitter/empty.rb +0 -23
- data/lib/unparser/emitter/ensure.rb +0 -37
- data/lib/unparser/emitter/literal.rb +0 -10
- data/lib/unparser/emitter/literal/array.rb +0 -29
- data/lib/unparser/emitter/literal/dynamic.rb +0 -53
- data/lib/unparser/emitter/literal/dynamic_body.rb +0 -132
- data/lib/unparser/emitter/literal/execute_string.rb +0 -38
- data/lib/unparser/emitter/literal/hash.rb +0 -156
- data/lib/unparser/emitter/literal/primitive.rb +0 -145
- data/lib/unparser/emitter/literal/range.rb +0 -36
- data/lib/unparser/emitter/literal/regexp.rb +0 -114
- data/lib/unparser/emitter/literal/singleton.rb +0 -26
- data/lib/unparser/emitter/meta.rb +0 -16
- data/lib/unparser/emitter/redo.rb +0 -25
- data/lib/unparser/emitter/resbody.rb +0 -76
- data/lib/unparser/emitter/retry.rb +0 -25
- data/lib/unparser/emitter/send/binary.rb +0 -57
- data/lib/unparser/emitter/send/conditional.rb +0 -40
- data/lib/unparser/emitter/send/regular.rb +0 -40
- data/lib/unparser/preprocessor.rb +0 -159
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Unparser
|
4
|
+
module NodeDetails
|
5
|
+
include Constants, NodeHelpers
|
6
|
+
|
7
|
+
def self.included(descendant)
|
8
|
+
descendant.class_eval do
|
9
|
+
include Adamantium::Flat, Concord.new(:node)
|
10
|
+
|
11
|
+
extend DSL
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def children
|
18
|
+
node.children
|
19
|
+
end
|
20
|
+
end # NodeDetails
|
21
|
+
end # Unparser
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Unparser
|
4
|
+
module NodeDetails
|
5
|
+
class Send
|
6
|
+
include NodeDetails
|
7
|
+
|
8
|
+
ASSIGN_SUFFIX = '='.freeze
|
9
|
+
NON_ASSIGN_RANGE = (0..-2).freeze
|
10
|
+
|
11
|
+
private_constant(*constants(false))
|
12
|
+
|
13
|
+
children :receiver, :selector
|
14
|
+
|
15
|
+
public :receiver, :selector
|
16
|
+
|
17
|
+
def selector_binary_operator?
|
18
|
+
BINARY_OPERATORS.include?(selector)
|
19
|
+
end
|
20
|
+
|
21
|
+
def binary_syntax_allowed?
|
22
|
+
selector_binary_operator? && arguments.one? && !n_splat?(arguments.first)
|
23
|
+
end
|
24
|
+
|
25
|
+
def selector_unary_operator?
|
26
|
+
UNARY_OPERATORS.include?(selector)
|
27
|
+
end
|
28
|
+
|
29
|
+
def assignment_operator?
|
30
|
+
assignment? && !selector_binary_operator? && !selector_unary_operator?
|
31
|
+
end
|
32
|
+
|
33
|
+
def arguments?
|
34
|
+
arguments.any?
|
35
|
+
end
|
36
|
+
|
37
|
+
def non_assignment_selector
|
38
|
+
if assignment?
|
39
|
+
string_selector[NON_ASSIGN_RANGE]
|
40
|
+
else
|
41
|
+
string_selector
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def assignment?
|
46
|
+
string_selector[-1].eql?(ASSIGN_SUFFIX)
|
47
|
+
end
|
48
|
+
memoize :assignment?
|
49
|
+
|
50
|
+
def arguments
|
51
|
+
children[2..-1]
|
52
|
+
end
|
53
|
+
memoize :arguments
|
54
|
+
|
55
|
+
def string_selector
|
56
|
+
selector.to_s
|
57
|
+
end
|
58
|
+
memoize :string_selector
|
59
|
+
|
60
|
+
end # Send
|
61
|
+
end # NodeDetails
|
62
|
+
end # Unparser
|
@@ -11,9 +11,6 @@ module Unparser
|
|
11
11
|
# @return [Parser::AST::Node]
|
12
12
|
#
|
13
13
|
# @api private
|
14
|
-
#
|
15
|
-
# ignore :reek:UncommunicativeMethodName
|
16
|
-
# ignore :reek:UtilityFunction
|
17
14
|
def s(type, *children)
|
18
15
|
Parser::AST::Node.new(type, children)
|
19
16
|
end
|
@@ -26,12 +23,54 @@ module Unparser
|
|
26
23
|
# @param [Array] children
|
27
24
|
#
|
28
25
|
# @api private
|
29
|
-
#
|
30
|
-
# ignore :reek:UncommunicativeMethodName
|
31
|
-
# ignore :reek:UtilityFunction
|
32
26
|
def n(type, children = [])
|
33
27
|
Parser::AST::Node.new(type, children)
|
34
28
|
end
|
35
29
|
|
30
|
+
def n?(type, node)
|
31
|
+
node.type.equal?(type)
|
32
|
+
end
|
33
|
+
|
34
|
+
%i[
|
35
|
+
arg
|
36
|
+
args
|
37
|
+
array
|
38
|
+
array_pattern
|
39
|
+
empty_else
|
40
|
+
begin
|
41
|
+
block
|
42
|
+
cbase
|
43
|
+
const
|
44
|
+
dstr
|
45
|
+
ensure
|
46
|
+
hash
|
47
|
+
hash_pattern
|
48
|
+
in_pattern
|
49
|
+
int
|
50
|
+
kwsplat
|
51
|
+
lambda
|
52
|
+
match_rest
|
53
|
+
pair
|
54
|
+
rescue
|
55
|
+
send
|
56
|
+
shadowarg
|
57
|
+
splat
|
58
|
+
str
|
59
|
+
sym
|
60
|
+
].each do |type|
|
61
|
+
name = "n_#{type}?"
|
62
|
+
define_method(name) do |node|
|
63
|
+
n?(type, node)
|
64
|
+
end
|
65
|
+
private(name)
|
66
|
+
end
|
67
|
+
|
68
|
+
def unwrap_single_begin(node)
|
69
|
+
if n_begin?(node) && node.children.one?
|
70
|
+
node.children.first
|
71
|
+
else
|
72
|
+
node
|
73
|
+
end
|
74
|
+
end
|
36
75
|
end # NodeHelpers
|
37
76
|
end # Unparser
|
data/lib/unparser/validation.rb
CHANGED
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
module Unparser
|
4
4
|
# Validation of unparser results
|
5
|
-
#
|
6
|
-
# ignore :reek:TooManyMethods
|
7
5
|
class Validation
|
8
6
|
include Adamantium::Flat, Anima.new(
|
9
7
|
:generated_node,
|
@@ -25,7 +23,7 @@ module Unparser
|
|
25
23
|
original_node,
|
26
24
|
generated_source,
|
27
25
|
generated_node
|
28
|
-
].all?(&:right?) && generated_node.from_right
|
26
|
+
].all?(&:right?) && generated_node.from_right.==(original_node.from_right)
|
29
27
|
end
|
30
28
|
|
31
29
|
# Return error report
|
@@ -55,16 +53,14 @@ module Unparser
|
|
55
53
|
def self.from_string(original_source)
|
56
54
|
original_node = Unparser
|
57
55
|
.parse_either(original_source)
|
58
|
-
.fmap(&Preprocessor.method(:run))
|
59
56
|
|
60
57
|
generated_source = original_node
|
61
58
|
.lmap(&method(:const_unit))
|
62
|
-
.bind(&method(:unparse_either))
|
59
|
+
.bind(&Unparser.method(:unparse_either))
|
63
60
|
|
64
61
|
generated_node = generated_source
|
65
62
|
.lmap(&method(:const_unit))
|
66
63
|
.bind(&Unparser.method(:parse_either))
|
67
|
-
.fmap(&Preprocessor.method(:run))
|
68
64
|
|
69
65
|
new(
|
70
66
|
identification: '(string)',
|
@@ -86,21 +82,10 @@ module Unparser
|
|
86
82
|
|
87
83
|
private
|
88
84
|
|
89
|
-
# Create a labeled report from
|
90
|
-
#
|
91
|
-
# @param [String] label
|
92
|
-
# @param [Symbol] attribute_name
|
93
|
-
#
|
94
|
-
# @return [Array<String>]
|
95
85
|
def make_report(label, attribute_name)
|
96
86
|
["#{label}:"].concat(public_send(attribute_name).either(method(:report_exception), ->(value) { [value] }))
|
97
87
|
end
|
98
88
|
|
99
|
-
# Report optional exception
|
100
|
-
#
|
101
|
-
# @param [Exception, nil] exception
|
102
|
-
#
|
103
|
-
# @return [Array<String>]
|
104
89
|
def report_exception(exception)
|
105
90
|
if exception
|
106
91
|
[exception.inspect].concat(exception.backtrace.take(20))
|
@@ -109,9 +94,6 @@ module Unparser
|
|
109
94
|
end
|
110
95
|
end
|
111
96
|
|
112
|
-
# Report the node diff
|
113
|
-
#
|
114
|
-
# @return [Array<String>]
|
115
97
|
def node_diff_report
|
116
98
|
diff = nil
|
117
99
|
|
@@ -127,23 +109,43 @@ module Unparser
|
|
127
109
|
diff ? ['Node-Diff:', diff] : []
|
128
110
|
end
|
129
111
|
|
130
|
-
# Create unit represented as nil
|
131
|
-
#
|
132
|
-
# @param [Object] _value
|
133
|
-
#
|
134
|
-
# @return [nil]
|
135
112
|
def self.const_unit(_value); end
|
136
113
|
private_class_method :const_unit
|
137
114
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
115
|
+
class Literal < self
|
116
|
+
def success?
|
117
|
+
original_source.eql?(generated_source)
|
118
|
+
end
|
119
|
+
|
120
|
+
def report
|
121
|
+
message = [identification]
|
122
|
+
|
123
|
+
message.concat(make_report('Original-Source', :original_source))
|
124
|
+
message.concat(make_report('Generated-Source', :generated_source))
|
125
|
+
message.concat(make_report('Original-Node', :original_node))
|
126
|
+
message.concat(make_report('Generated-Node', :generated_node))
|
127
|
+
message.concat(node_diff_report)
|
128
|
+
message.concat(source_diff_report)
|
129
|
+
|
130
|
+
message.join("\n")
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def source_diff_report
|
136
|
+
diff = nil
|
137
|
+
|
138
|
+
original_source.fmap do |original|
|
139
|
+
generated_source.fmap do |generated|
|
140
|
+
diff = Diff.new(
|
141
|
+
original.split("\n", -1),
|
142
|
+
generated.split("\n", -1)
|
143
|
+
).colorized_diff
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
diff ? ['Source-Diff:', diff] : []
|
148
|
+
end
|
149
|
+
end # Literal
|
148
150
|
end # Validation
|
149
151
|
end # Unparser
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Unparser
|
4
|
+
module Writer
|
5
|
+
include Generation, NodeHelpers
|
6
|
+
|
7
|
+
def self.included(descendant)
|
8
|
+
descendant.class_eval do
|
9
|
+
include Anima.new(:buffer, :comments, :node, :local_variable_scope)
|
10
|
+
|
11
|
+
extend DSL
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end # Writer
|
15
|
+
end # Unparser
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Unparser
|
4
|
+
module Writer
|
5
|
+
class Binary
|
6
|
+
include Writer, Adamantium::Flat
|
7
|
+
|
8
|
+
children :left, :right
|
9
|
+
|
10
|
+
OPERATOR_TOKENS =
|
11
|
+
{
|
12
|
+
and: '&&',
|
13
|
+
or: '||'
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
KEYWORD_TOKENS =
|
17
|
+
{
|
18
|
+
and: 'and',
|
19
|
+
or: 'or'
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
KEYWORD_SYMBOLS =
|
23
|
+
{
|
24
|
+
and: :kAND,
|
25
|
+
or: :kOR
|
26
|
+
}.freeze
|
27
|
+
|
28
|
+
OPERATOR_SYMBOLS =
|
29
|
+
{
|
30
|
+
and: :tANDOP,
|
31
|
+
or: :tOROP
|
32
|
+
}.freeze
|
33
|
+
|
34
|
+
MAP =
|
35
|
+
{
|
36
|
+
kAND: 'and',
|
37
|
+
kOR: 'or',
|
38
|
+
tOROP: '||',
|
39
|
+
tANDOP: '&&'
|
40
|
+
}.freeze
|
41
|
+
|
42
|
+
NEED_KEYWORD = %i[return break next].freeze
|
43
|
+
|
44
|
+
private_constant(*constants(false))
|
45
|
+
|
46
|
+
def emit_operator
|
47
|
+
emit_with(OPERATOR_TOKENS)
|
48
|
+
end
|
49
|
+
|
50
|
+
def symbol_name
|
51
|
+
true
|
52
|
+
end
|
53
|
+
|
54
|
+
def dispatch
|
55
|
+
left_emitter.write_to_buffer
|
56
|
+
write(' ', MAP.fetch(effective_symbol), ' ')
|
57
|
+
visit(right)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def effective_symbol
|
63
|
+
if NEED_KEYWORD.include?(right.type) || NEED_KEYWORD.include?(left.type)
|
64
|
+
return keyword_symbol
|
65
|
+
end
|
66
|
+
|
67
|
+
unless left_emitter.symbol_name
|
68
|
+
return operator_symbol
|
69
|
+
end
|
70
|
+
|
71
|
+
keyword_symbol
|
72
|
+
end
|
73
|
+
|
74
|
+
def emit_with(map)
|
75
|
+
visit(left)
|
76
|
+
write(' ', map.fetch(node.type), ' ')
|
77
|
+
visit(right)
|
78
|
+
end
|
79
|
+
|
80
|
+
def keyword_symbol
|
81
|
+
KEYWORD_SYMBOLS.fetch(node.type)
|
82
|
+
end
|
83
|
+
|
84
|
+
def operator_symbol
|
85
|
+
OPERATOR_SYMBOLS.fetch(node.type)
|
86
|
+
end
|
87
|
+
|
88
|
+
def left_emitter
|
89
|
+
emitter(left)
|
90
|
+
end
|
91
|
+
memoize :left_emitter
|
92
|
+
|
93
|
+
def right_emitter
|
94
|
+
emitter(right)
|
95
|
+
end
|
96
|
+
memoize :right_emitter
|
97
|
+
end # Binary
|
98
|
+
end # Writer
|
99
|
+
end # Unparser
|
@@ -0,0 +1,229 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Unparser
|
4
|
+
module Writer
|
5
|
+
class DynamicString
|
6
|
+
include Writer, Adamantium::Flat
|
7
|
+
|
8
|
+
PATTERNS_2 = IceNine.deep_freeze(
|
9
|
+
[
|
10
|
+
%i[str_empty begin],
|
11
|
+
%i[begin str_nl]
|
12
|
+
]
|
13
|
+
)
|
14
|
+
|
15
|
+
PATTERNS_3 = IceNine.deep_freeze(
|
16
|
+
[
|
17
|
+
%i[begin str_nl_eol str_nl_eol],
|
18
|
+
%i[str_nl_eol begin str_nl_eol],
|
19
|
+
%i[str_ws begin str_nl_eol]
|
20
|
+
]
|
21
|
+
)
|
22
|
+
|
23
|
+
FLAT_INTERPOLATION = %i[ivar cvar gvar].to_set.freeze
|
24
|
+
|
25
|
+
private_constant(*constants(false))
|
26
|
+
|
27
|
+
def emit_heredoc_reminder
|
28
|
+
return unless heredoc?
|
29
|
+
|
30
|
+
emit_heredoc_body
|
31
|
+
emit_heredoc_footer
|
32
|
+
end
|
33
|
+
|
34
|
+
def dispatch
|
35
|
+
if heredoc?
|
36
|
+
emit_heredoc_header
|
37
|
+
else
|
38
|
+
emit_dstr
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def heredoc_header
|
45
|
+
need_squiggly? ? '<<~HEREDOC' : '<<-HEREDOC'
|
46
|
+
end
|
47
|
+
|
48
|
+
def heredoc?
|
49
|
+
children.empty? || (nl_last_child? && heredoc_pattern?)
|
50
|
+
end
|
51
|
+
|
52
|
+
def emit_heredoc_header
|
53
|
+
write(heredoc_header)
|
54
|
+
end
|
55
|
+
|
56
|
+
def emit_heredoc_body
|
57
|
+
nl
|
58
|
+
if need_squiggly?
|
59
|
+
emit_squiggly_heredoc_body
|
60
|
+
else
|
61
|
+
emit_normal_heredoc_body
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def emit_heredoc_footer
|
66
|
+
write('HEREDOC')
|
67
|
+
end
|
68
|
+
|
69
|
+
def classify(node)
|
70
|
+
if n_str?(node)
|
71
|
+
classify_str(node)
|
72
|
+
else
|
73
|
+
node.type
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def classify_str(node)
|
78
|
+
if str_nl?(node)
|
79
|
+
:str_nl
|
80
|
+
elsif node.children.first.end_with?("\n")
|
81
|
+
:str_nl_eol
|
82
|
+
elsif str_ws?(node)
|
83
|
+
:str_ws
|
84
|
+
elsif str_empty?(node)
|
85
|
+
:str_empty
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def str_nl?(node)
|
90
|
+
node.eql?(s(:str, "\n"))
|
91
|
+
end
|
92
|
+
|
93
|
+
def str_empty?(node)
|
94
|
+
node.eql?(s(:str, ''))
|
95
|
+
end
|
96
|
+
|
97
|
+
def str_ws?(node)
|
98
|
+
/\A( |\t)+\z/.match?(node.children.first)
|
99
|
+
end
|
100
|
+
|
101
|
+
def heredoc_pattern?
|
102
|
+
heredoc_pattern_2? || heredoc_pattern_3?
|
103
|
+
end
|
104
|
+
|
105
|
+
def heredoc_pattern_3?
|
106
|
+
children.each_cons(3).any? do |group|
|
107
|
+
PATTERNS_3.include?(group.map(&method(:classify)))
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def heredoc_pattern_2?
|
112
|
+
children.each_cons(2).any? do |group|
|
113
|
+
PATTERNS_2.include?(group.map(&method(:classify)))
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def nl_last_child?
|
118
|
+
last = children.last
|
119
|
+
n_str?(last) && last.children.first[-1].eql?("\n")
|
120
|
+
end
|
121
|
+
|
122
|
+
def need_squiggly?
|
123
|
+
children.any?(s(:str, ''))
|
124
|
+
end
|
125
|
+
|
126
|
+
def emit_squiggly_heredoc_body
|
127
|
+
buffer.indent
|
128
|
+
children.each do |child|
|
129
|
+
if n_str?(child)
|
130
|
+
write(escape_dynamic(child.children.first))
|
131
|
+
else
|
132
|
+
emit_dynamic(child)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
buffer.unindent
|
136
|
+
end
|
137
|
+
|
138
|
+
def emit_normal_heredoc_body
|
139
|
+
buffer.root_indent do
|
140
|
+
children.each do |child|
|
141
|
+
if n_str?(child)
|
142
|
+
write(escape_dynamic(child.children.first))
|
143
|
+
else
|
144
|
+
emit_dynamic(child)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def escape_dynamic(string)
|
151
|
+
string.gsub('#', '\#')
|
152
|
+
end
|
153
|
+
|
154
|
+
def emit_dynamic(child)
|
155
|
+
if FLAT_INTERPOLATION.include?(child.type)
|
156
|
+
write('#')
|
157
|
+
visit(child)
|
158
|
+
elsif n_dstr?(child)
|
159
|
+
emit_body(child.children)
|
160
|
+
else
|
161
|
+
write('#{')
|
162
|
+
emit_dynamic_component(child.children.first)
|
163
|
+
write('}')
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def emit_dynamic_component(node)
|
168
|
+
visit(node) if node
|
169
|
+
end
|
170
|
+
|
171
|
+
def emit_dstr
|
172
|
+
segments.each_with_index do |children, index|
|
173
|
+
emit_segment(children, index)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def breakpoint?(child, current)
|
178
|
+
last_type = current.last&.type
|
179
|
+
|
180
|
+
[
|
181
|
+
n_str?(child) && last_type.equal?(:str) && current.none?(&method(:n_begin?)),
|
182
|
+
last_type.equal?(:dstr),
|
183
|
+
n_dstr?(child) && last_type
|
184
|
+
].any?
|
185
|
+
end
|
186
|
+
|
187
|
+
def segments
|
188
|
+
segments = []
|
189
|
+
|
190
|
+
segments << current = []
|
191
|
+
|
192
|
+
children.each do |child|
|
193
|
+
if breakpoint?(child, current)
|
194
|
+
segments << current = []
|
195
|
+
end
|
196
|
+
|
197
|
+
current << child
|
198
|
+
end
|
199
|
+
|
200
|
+
segments
|
201
|
+
end
|
202
|
+
|
203
|
+
def emit_segment(children, index)
|
204
|
+
write(' ') unless index.zero?
|
205
|
+
|
206
|
+
write('"')
|
207
|
+
emit_body(children)
|
208
|
+
write('"')
|
209
|
+
end
|
210
|
+
|
211
|
+
def emit_body(children)
|
212
|
+
buffer.root_indent do
|
213
|
+
children.each_with_index do |child, index|
|
214
|
+
if n_str?(child)
|
215
|
+
string = child.children.first
|
216
|
+
if string.eql?("\n") && children.fetch(index.pred).type.equal?(:begin)
|
217
|
+
write("\n")
|
218
|
+
else
|
219
|
+
write(string.inspect[1..-2])
|
220
|
+
end
|
221
|
+
else
|
222
|
+
emit_dynamic(child)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end # DynamicString
|
228
|
+
end # Writer
|
229
|
+
end # Unparser
|