@@ -5,6 +5,73 @@ module Translation
5
5
# This visitor is responsible for converting the syntax tree produced by
6
6
# Syntax Tree into the syntax tree produced by the whitequark/parser gem.
7
7
class Parser < BasicVisitor
8
+ # Heredocs are represented _very_ differently in the parser gem from how
9
+ # they are represented in the Syntax Tree AST. This class is responsible
10
+ # for handling the translation.
11
+ class HeredocBuilder
12
+ Line = Struct . new ( :value , :segments )
13
+
14
+ attr_reader :node , :segments
15
+
16
+ def initialize ( node )
17
+ @node = node
18
+ @segments = [ ]
19
+ end
20
+
21
+ def <<( segment )
22
+ if segment . type == :str && segments . last &&
23
+ segments . last . type == :str &&
24
+ !segments . last . children . first . end_with? ( "\n " )
25
+ segments . last . children . first << segment . children . first
26
+ else
27
+ segments << segment
28
+ end
29
+ end
30
+
31
+ def trim!
32
+ return unless node . beginning . value [ 2 ] == "~"
33
+ lines = [ Line . new ( +"" , [ ] ) ]
34
+
35
+ segments . each do |segment |
36
+ lines . last . segments << segment
37
+
38
+ if segment . type == :str
39
+ lines . last . value << segment . children . first
40
+ lines << Line . new ( +"" , [ ] ) if lines . last . value . end_with? ( "\n " )
41
+ end
42
+ end
43
+
44
+ lines . pop if lines . last . value . empty?
45
+ return if lines . empty?
46
+
47
+ segments . clear
48
+ lines . each do |line |
49
+ remaining = node . dedent
50
+
51
+ line . segments . each do |segment |
52
+ if segment . type == :str
53
+ if remaining > 0
54
+ whitespace = segment . children . first [ /^\s {0,#{ remaining } }/ ]
55
+ segment . children . first . sub! ( /^#{ whitespace } / , "" )
56
+ remaining -= whitespace . length
57
+ end
58
+
59
+ if node . beginning . value [ 3 ] != "'" && segments . any? &&
60
+ segments . last . type == :str &&
61
+ segments . last . children . first . end_with? ( "\\ \n " )
62
+ segments . last . children . first . gsub! ( /\\ \n \z / , "" )
63
+ segments . last . children . first . concat ( segment . children . first )
64
+ elsif !segment . children . first . empty?
65
+ segments << segment
66
+ end
67
+ else
68
+ segments << segment
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+
8
75
attr_reader :buffer , :stack
9
76
10
77
def initialize ( buffer )
@@ -665,6 +732,25 @@ def visit_command_call(node)
665
732
node . end_char
666
733
end
667
734
735
+ expression =
736
+ if node . arguments . is_a? ( ArgParen )
737
+ srange ( node . start_char , node . arguments . end_char )
738
+ elsif node . arguments . is_a? ( Args ) && node . arguments . parts . any?
739
+ last_part = node . arguments . parts . last
740
+ end_char =
741
+ if last_part . is_a? ( Heredoc )
742
+ last_part . beginning . end_char
743
+ else
744
+ last_part . end_char
745
+ end
746
+
747
+ srange ( node . start_char , end_char )
748
+ elsif node . block
749
+ srange_node ( node . message )
750
+ else
751
+ srange_node ( node )
752
+ end
753
+
668
754
call =
669
755
s (
670
756
if node . operator . is_a? ( Op ) && node . operator . value == "&."
@@ -690,14 +776,7 @@ def visit_command_call(node)
690
776
node . message == :call ? nil : srange_node ( node . message ) ,
691
777
begin_token ,
692
778
end_token ,
693
- if node . arguments . is_a? ( ArgParen ) ||
694
- ( node . arguments . is_a? ( Args ) && node . arguments . parts . any? )
695
- srange ( node . start_char , node . arguments . end_char )
696
- elsif node . block
697
- srange_node ( node . message )
698
- else
699
- srange_node ( node )
700
- end
779
+ expression
701
780
)
702
781
)
703
782
@@ -1049,7 +1128,8 @@ def visit_for(node)
1049
1128
smap_for (
1050
1129
srange_length ( node . start_char , 3 ) ,
1051
1130
srange_find_between ( node . index , node . collection , "in" ) ,
1052
- srange_search_between ( node . collection , node . statements , "do" ) ,
1131
+ srange_search_between ( node . collection , node . statements , "do" ) ||
1132
+ srange_search_between ( node . collection , node . statements , ";" ) ,
1053
1133
srange_length ( node . end_char , -3 ) ,
1054
1134
srange_node ( node )
1055
1135
)
@@ -1078,98 +1158,43 @@ def visit_hash(node)
1078
1158
)
1079
1159
end
1080
1160
1081
- # Heredocs are represented _very_ differently in the parser gem from how
1082
- # they are represented in the Syntax Tree AST. This class is responsible
1083
- # for handling the translation.
1084
- class HeredocSegments
1085
- HeredocLine = Struct . new ( :value , :segments )
1086
-
1087
- attr_reader :node , :segments
1088
-
1089
- def initialize ( node )
1090
- @node = node
1091
- @segments = [ ]
1092
- end
1093
-
1094
- def <<( segment )
1095
- if segment . type == :str && segments . last &&
1096
- segments . last . type == :str &&
1097
- !segments . last . children . first . end_with? ( "\n " )
1098
- segments . last . children . first << segment . children . first
1099
- else
1100
- segments << segment
1101
- end
1102
- end
1103
-
1104
- def trim!
1105
- return unless node . beginning . value [ 2 ] == "~"
1106
- lines = [ HeredocLine . new ( +"" , [ ] ) ]
1107
-
1108
- segments . each do |segment |
1109
- lines . last . segments << segment
1110
-
1111
- if segment . type == :str
1112
- lines . last . value << segment . children . first
1113
-
1114
- if lines . last . value . end_with? ( "\n " )
1115
- lines << HeredocLine . new ( +"" , [ ] )
1116
- end
1117
- end
1118
- end
1119
-
1120
- lines . pop if lines . last . value . empty?
1121
- return if lines . empty?
1122
-
1123
- segments . clear
1124
- lines . each do |line |
1125
- remaining = node . dedent
1126
-
1127
- line . segments . each do |segment |
1128
- if segment . type == :str
1129
- if remaining > 0
1130
- whitespace = segment . children . first [ /^\s {0,#{ remaining } }/ ]
1131
- segment . children . first . sub! ( /^#{ whitespace } / , "" )
1132
- remaining -= whitespace . length
1133
- end
1134
-
1135
- if node . beginning . value [ 3 ] != "'" && segments . any? &&
1136
- segments . last . type == :str &&
1137
- segments . last . children . first . end_with? ( "\\ \n " )
1138
- segments . last . children . first . gsub! ( /\\ \n \z / , "" )
1139
- segments . last . children . first . concat ( segment . children . first )
1140
- elsif !segment . children . first . empty?
1141
- segments << segment
1142
- end
1143
- else
1144
- segments << segment
1145
- end
1146
- end
1147
- end
1148
- end
1149
- end
1150
-
1151
1161
# Visit a Heredoc node.
1152
1162
def visit_heredoc ( node )
1153
- heredoc_segments = HeredocSegments . new ( node )
1163
+ heredoc = HeredocBuilder . new ( node )
1154
1164
1165
+ # For each part of the heredoc, if it's a string content node, split it
1166
+ # into multiple string content nodes, one for each line. Otherwise,
1167
+ # visit the node as normal.
1155
1168
node . parts . each do |part |
1156
1169
if part . is_a? ( TStringContent ) && part . value . count ( "\n " ) > 1
1157
- part
1158
- . value
1159
- . split ( "\n " )
1160
- . each { |line | heredoc_segments << s ( :str , [ "#{ line } \n " ] , nil ) }
1170
+ index = part . start_char
1171
+ lines = part . value . split ( "\n " )
1172
+
1173
+ lines . each do |line |
1174
+ length = line . length + 1
1175
+ location = smap_collection_bare ( srange_length ( index , length ) )
1176
+
1177
+ heredoc << s ( :str , [ "#{ line } \n " ] , location )
1178
+ index += length
1179
+ end
1161
1180
else
1162
- heredoc_segments << visit ( part )
1181
+ heredoc << visit ( part )
1163
1182
end
1164
1183
end
1165
1184
1166
- heredoc_segments . trim!
1185
+ # Now that we have all of the pieces on the heredoc, we can trim it if
1186
+ # it is a heredoc that supports trimming (i.e., it has a ~ on the
1187
+ # declaration).
1188
+ heredoc . trim!
1189
+
1190
+ # Generate the location for the heredoc, which goes from the declaration
1191
+ # to the ending delimiter.
1167
1192
location =
1168
1193
smap_heredoc (
1169
1194
srange_node ( node . beginning ) ,
1170
1195
srange (
1171
1196
if node . parts . empty?
1172
- node . beginning . end_char
1197
+ node . beginning . end_char + 1
1173
1198
else
1174
1199
node . parts . first . start_char
1175
1200
end ,
@@ -1178,15 +1203,15 @@ def visit_heredoc(node)
1178
1203
srange ( node . ending . start_char , node . ending . end_char - 1 )
1179
1204
)
1180
1205
1206
+ # Finally, decide which kind of heredoc node to generate based on its
1207
+ # declaration and contents.
1181
1208
if node . beginning . value . match? ( /`\w +`\z / )
1182
- s ( :xstr , heredoc_segments . segments , location )
1183
- elsif heredoc_segments . segments . length > 1
1184
- s ( :dstr , heredoc_segments . segments , location )
1185
- elsif heredoc_segments . segments . empty?
1186
- s ( :dstr , [ ] , location )
1187
- else
1188
- segment = heredoc_segments . segments . first
1209
+ s ( :xstr , heredoc . segments , location )
1210
+ elsif heredoc . segments . length == 1
1211
+ segment = heredoc . segments . first
1189
1212
s ( segment . type , segment . children , location )
1213
+ else
1214
+ s ( :dstr , heredoc . segments , location )
1190
1215
end
1191
1216
end
1192
1217
0 commit comments