@@ -75,103 +75,212 @@ def compile
75
75
76
76
private
77
77
78
+ # Shortcut for combining two procs into one that returns true if both return
79
+ # true.
78
80
def combine_and ( left , right )
79
- -> ( node ) { left . call ( node ) && right . call ( node ) }
81
+ -> ( other ) { left . call ( other ) && right . call ( other ) }
80
82
end
81
83
84
+ # Shortcut for combining two procs into one that returns true if either
85
+ # returns true.
82
86
def combine_or ( left , right )
83
- -> ( node ) { left . call ( node ) || right . call ( node ) }
87
+ -> ( other ) { left . call ( other ) || right . call ( other ) }
84
88
end
85
89
86
- def compile_node ( root )
87
- if AryPtn === root and root . rest . nil? and root . posts . empty?
88
- constant = root . constant
89
- compiled_constant = compile_node ( constant ) if constant
90
+ # Raise an error because the given node is not supported.
91
+ def compile_error ( node )
92
+ raise CompilationError , PP . pp ( node , + "" ) . chomp
93
+ end
90
94
91
- preprocessed = root . requireds . map { |required | compile_node ( required ) }
95
+ # There are a couple of nodes (string literals, dynamic symbols, and regexp)
96
+ # that contain list of parts. This can include plain string content,
97
+ # interpolated expressions, and interpolated variables. We only support
98
+ # plain string content, so this method will extract out the plain string
99
+ # content if it is the only element in the list.
100
+ def extract_string ( node )
101
+ parts = node . parts
92
102
93
- compiled_requireds = -> ( node ) do
94
- deconstructed = node . deconstruct
103
+ if parts . length == 1 && ( part = parts . first ) && part . is_a? ( TStringContent )
104
+ part . value
105
+ end
106
+ end
95
107
96
- deconstructed . length == preprocessed . length &&
97
- preprocessed
98
- . zip ( deconstructed )
99
- . all? { |( matcher , value ) | matcher . call ( value ) }
100
- end
108
+ # in [foo, bar, baz]
109
+ def compile_aryptn ( node )
110
+ compile_error ( node ) if !node . rest . nil? || node . posts . any?
101
111
102
- if compiled_constant
103
- combine_and ( compiled_constant , compiled_requireds )
104
- else
105
- compiled_requireds
106
- end
107
- elsif Binary === root and root . operator == :|
108
- combine_or ( compile_node ( root . left ) , compile_node ( root . right ) )
109
- elsif Const === root and SyntaxTree . const_defined? ( root . value )
110
- clazz = SyntaxTree . const_get ( root . value )
111
-
112
- -> ( node ) { node . is_a? ( clazz ) }
113
- elsif Const === root and Object . const_defined? ( root . value )
114
- clazz = Object . const_get ( root . value )
115
-
116
- -> ( node ) { node . is_a? ( clazz ) }
117
- elsif ConstPathRef === root and VarRef === root . parent and
118
- Const === root . parent . value and
119
- root . parent . value . value == "SyntaxTree"
120
- compile_node ( root . constant )
121
- elsif DynaSymbol === root and root . parts . empty?
112
+ constant = node . constant
113
+ compiled_constant = compile_node ( constant ) if constant
114
+
115
+ preprocessed = node . requireds . map { |required | compile_node ( required ) }
116
+
117
+ compiled_requireds = -> ( other ) do
118
+ deconstructed = other . deconstruct
119
+
120
+ deconstructed . length == preprocessed . length &&
121
+ preprocessed
122
+ . zip ( deconstructed )
123
+ . all? { |( matcher , value ) | matcher . call ( value ) }
124
+ end
125
+
126
+ if compiled_constant
127
+ combine_and ( compiled_constant , compiled_requireds )
128
+ else
129
+ compiled_requireds
130
+ end
131
+ end
132
+
133
+ # in foo | bar
134
+ def compile_binary ( node )
135
+ compile_error ( node ) if node . operator != :|
136
+
137
+ combine_or ( compile_node ( node . left ) , compile_node ( node . right ) )
138
+ end
139
+
140
+ # in Ident
141
+ # in String
142
+ def compile_const ( node )
143
+ value = node . value
144
+
145
+ if SyntaxTree . const_defined? ( value )
146
+ clazz = SyntaxTree . const_get ( value )
147
+
148
+ -> ( other ) { clazz === other }
149
+ elsif Object . const_defined? ( value )
150
+ clazz = Object . const_get ( value )
151
+
152
+ -> ( other ) { clazz === other }
153
+ else
154
+ compile_error ( node )
155
+ end
156
+ end
157
+
158
+ # in SyntaxTree::Ident
159
+ def compile_const_path_ref ( node )
160
+ parent = node . parent
161
+ compile_error ( node ) if !parent . is_a? ( VarRef ) || !parent . value . is_a? ( Const )
162
+
163
+ if parent . value . value == "SyntaxTree"
164
+ compile_node ( node . constant )
165
+ else
166
+ compile_error ( node )
167
+ end
168
+ end
169
+
170
+ # in :""
171
+ # in :"foo"
172
+ def compile_dyna_symbol ( node )
173
+ if node . parts . empty?
122
174
symbol = :""
123
175
124
- -> ( node ) { node == symbol }
125
- elsif DynaSymbol === root and parts = root . parts and parts . size == 1 and
126
- TStringContent === parts [ 0 ]
127
- symbol = parts [ 0 ] . value . to_sym
128
-
129
- -> ( node ) { node == symbol }
130
- elsif HshPtn === root and root . keyword_rest . nil?
131
- compiled_constant = compile_node ( root . constant )
132
-
133
- preprocessed =
134
- root . keywords . to_h do |keyword , value |
135
- unless keyword . is_a? ( Label )
136
- raise CompilationError , PP . pp ( root , +"" ) . chomp
137
- end
138
- [ keyword . value . chomp ( ":" ) . to_sym , compile_node ( value ) ]
139
- end
140
-
141
- compiled_keywords = -> ( node ) do
142
- deconstructed = node . deconstruct_keys ( preprocessed . keys )
143
-
144
- preprocessed . all? do |keyword , matcher |
145
- matcher . call ( deconstructed [ keyword ] )
146
- end
176
+ -> ( other ) { symbol === other }
177
+ elsif ( value = extract_string ( node ) )
178
+ symbol = value . to_sym
179
+
180
+ -> ( other ) { symbol === other }
181
+ else
182
+ compile_error ( root )
183
+ end
184
+ end
185
+
186
+ # in Ident[value: String]
187
+ # in { value: String }
188
+ def compile_hshptn ( node )
189
+ compile_error ( node ) unless node . keyword_rest . nil?
190
+ compiled_constant = compile_node ( node . constant ) if node . constant
191
+
192
+ preprocessed =
193
+ node . keywords . to_h do |keyword , value |
194
+ compile_error ( node ) unless keyword . is_a? ( Label )
195
+ [ keyword . value . chomp ( ":" ) . to_sym , compile_node ( value ) ]
147
196
end
148
197
149
- if compiled_constant
150
- combine_and ( compiled_constant , compiled_keywords )
151
- else
152
- compiled_keywords
198
+ compiled_keywords = -> ( other ) do
199
+ deconstructed = other . deconstruct_keys ( preprocessed . keys )
200
+
201
+ preprocessed . all? do |keyword , matcher |
202
+ matcher . call ( deconstructed [ keyword ] )
153
203
end
154
- elsif RegexpLiteral === root and parts = root . parts and
155
- parts . size == 1 and TStringContent === parts [ 0 ]
156
- regexp = /#{ parts [ 0 ] . value } /
157
-
158
- -> ( attribute ) { regexp . match? ( attribute ) }
159
- elsif StringLiteral === root and root . parts . empty?
160
- -> ( attribute ) { attribute == "" }
161
- elsif StringLiteral === root and parts = root . parts and
162
- parts . size == 1 and TStringContent === parts [ 0 ]
163
- value = parts [ 0 ] . value
164
- -> ( attribute ) { attribute == value }
165
- elsif SymbolLiteral === root
166
- symbol = root . value . value . to_sym
167
-
168
- -> ( attribute ) { attribute == symbol }
169
- elsif VarRef === root and Const === root . value
170
- compile_node ( root . value )
171
- elsif VarRef === root and Kw === root . value and root . value . value . nil?
172
- -> ( attribute ) { attribute . nil? }
204
+ end
205
+
206
+ if compiled_constant
207
+ combine_and ( compiled_constant , compiled_keywords )
208
+ else
209
+ compiled_keywords
210
+ end
211
+ end
212
+
213
+ # in /foo/
214
+ def compile_regexp_literal ( node )
215
+ if ( value = extract_string ( node ) )
216
+ regexp = /#{ value } /
217
+
218
+ -> ( attribute ) { regexp === attribute }
219
+ else
220
+ compile_error ( node )
221
+ end
222
+ end
223
+
224
+ # in ""
225
+ # in "foo"
226
+ def compile_string_literal ( node )
227
+ if node . parts . empty?
228
+ -> ( attribute ) { "" === attribute }
229
+ elsif ( value = extract_string ( node ) )
230
+ -> ( attribute ) { value === attribute }
231
+ else
232
+ compile_error ( node )
233
+ end
234
+ end
235
+
236
+ # in :+
237
+ # in :foo
238
+ def compile_symbol_literal ( node )
239
+ symbol = node . value . value . to_sym
240
+
241
+ -> ( attribute ) { symbol === attribute }
242
+ end
243
+
244
+ # in Foo
245
+ # in nil
246
+ def compile_var_ref ( node )
247
+ value = node . value
248
+
249
+ if value . is_a? ( Const )
250
+ compile_node ( value )
251
+ elsif value . is_a? ( Kw ) && value . value . nil?
252
+ -> ( attribute ) { nil === attribute }
253
+ else
254
+ compile_error ( node )
255
+ end
256
+ end
257
+
258
+ # Compile any kind of node. Dispatch out to the individual compilation
259
+ # methods based on the type of node.
260
+ def compile_node ( node )
261
+ case node
262
+ when AryPtn
263
+ compile_aryptn ( node )
264
+ when Binary
265
+ compile_binary ( node )
266
+ when Const
267
+ compile_const ( node )
268
+ when ConstPathRef
269
+ compile_const_path_ref ( node )
270
+ when DynaSymbol
271
+ compile_dyna_symbol ( node )
272
+ when HshPtn
273
+ compile_hshptn ( node )
274
+ when RegexpLiteral
275
+ compile_regexp_literal ( node )
276
+ when StringLiteral
277
+ compile_string_literal ( node )
278
+ when SymbolLiteral
279
+ compile_symbol_literal ( node )
280
+ when VarRef
281
+ compile_var_ref ( node )
173
282
else
174
- raise CompilationError , PP . pp ( root , + "" ) . chomp
283
+ compile_error ( node )
175
284
end
176
285
end
177
286
end
0 commit comments