@@ -75,103 +75,212 @@ def compile
7575
7676 private
7777
78+ # Shortcut for combining two procs into one that returns true if both return
79+ # true.
7880 def combine_and ( left , right )
79- -> ( node ) { left . call ( node ) && right . call ( node ) }
81+ -> ( other ) { left . call ( other ) && right . call ( other ) }
8082 end
8183
84+ # Shortcut for combining two procs into one that returns true if either
85+ # returns true.
8286 def combine_or ( left , right )
83- -> ( node ) { left . call ( node ) || right . call ( node ) }
87+ -> ( other ) { left . call ( other ) || right . call ( other ) }
8488 end
8589
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
9094
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
92102
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
95107
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?
101111
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?
122174 symbol = :""
123175
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 ) ]
147196 end
148197
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 ] )
153203 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 )
173282 else
174- raise CompilationError , PP . pp ( root , + "" ) . chomp
283+ compile_error ( node )
175284 end
176285 end
177286 end
0 commit comments