rubyjedi-oga 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +13 -0
  3. data/LICENSE +362 -0
  4. data/README.md +317 -0
  5. data/doc/css/common.css +77 -0
  6. data/doc/css_selectors.md +935 -0
  7. data/doc/manually_creating_documents.md +67 -0
  8. data/doc/migrating_from_nokogiri.md +169 -0
  9. data/doc/xml_namespaces.md +63 -0
  10. data/ext/c/extconf.rb +11 -0
  11. data/ext/c/lexer.c +2595 -0
  12. data/ext/c/lexer.h +16 -0
  13. data/ext/c/lexer.rl +198 -0
  14. data/ext/c/liboga.c +6 -0
  15. data/ext/c/liboga.h +11 -0
  16. data/ext/java/Liboga.java +14 -0
  17. data/ext/java/org/liboga/xml/Lexer.java +1363 -0
  18. data/ext/java/org/liboga/xml/Lexer.rl +223 -0
  19. data/ext/ragel/base_lexer.rl +633 -0
  20. data/lib/oga.rb +57 -0
  21. data/lib/oga/blacklist.rb +40 -0
  22. data/lib/oga/css/lexer.rb +743 -0
  23. data/lib/oga/css/parser.rb +976 -0
  24. data/lib/oga/entity_decoder.rb +21 -0
  25. data/lib/oga/html/entities.rb +2150 -0
  26. data/lib/oga/html/parser.rb +25 -0
  27. data/lib/oga/html/sax_parser.rb +18 -0
  28. data/lib/oga/lru.rb +160 -0
  29. data/lib/oga/oga.rb +57 -0
  30. data/lib/oga/version.rb +3 -0
  31. data/lib/oga/whitelist.rb +20 -0
  32. data/lib/oga/xml/attribute.rb +136 -0
  33. data/lib/oga/xml/cdata.rb +17 -0
  34. data/lib/oga/xml/character_node.rb +37 -0
  35. data/lib/oga/xml/comment.rb +17 -0
  36. data/lib/oga/xml/default_namespace.rb +13 -0
  37. data/lib/oga/xml/doctype.rb +82 -0
  38. data/lib/oga/xml/document.rb +108 -0
  39. data/lib/oga/xml/element.rb +428 -0
  40. data/lib/oga/xml/entities.rb +122 -0
  41. data/lib/oga/xml/html_void_elements.rb +15 -0
  42. data/lib/oga/xml/lexer.rb +550 -0
  43. data/lib/oga/xml/namespace.rb +48 -0
  44. data/lib/oga/xml/node.rb +219 -0
  45. data/lib/oga/xml/node_set.rb +333 -0
  46. data/lib/oga/xml/parser.rb +631 -0
  47. data/lib/oga/xml/processing_instruction.rb +37 -0
  48. data/lib/oga/xml/pull_parser.rb +175 -0
  49. data/lib/oga/xml/querying.rb +56 -0
  50. data/lib/oga/xml/sax_parser.rb +192 -0
  51. data/lib/oga/xml/text.rb +66 -0
  52. data/lib/oga/xml/traversal.rb +50 -0
  53. data/lib/oga/xml/xml_declaration.rb +65 -0
  54. data/lib/oga/xpath/evaluator.rb +1798 -0
  55. data/lib/oga/xpath/lexer.rb +1958 -0
  56. data/lib/oga/xpath/parser.rb +622 -0
  57. data/oga.gemspec +45 -0
  58. metadata +227 -0
@@ -0,0 +1,976 @@
1
+ # This file is automatically generated by ruby-ll. Manually changing this file
2
+ # is not recommended as any changes will be lost the next time this parser is
3
+ # re-generated.
4
+ require 'll/setup'
5
+
6
+ module Oga
7
+ module CSS
8
+ ##
9
+ # AST parser for CSS expressions.
10
+ #
11
+ # This parser does _not_ build a CSS specific AST, instead it directly produces
12
+ # an XPath AST. This removes the need to transform the AST or generate
13
+ # corresponding XPath expressions as a String.
14
+ #
15
+ # Similar to {Oga::XPath::Parser} this parser only takes String instances as
16
+ # input.
17
+ #
18
+ # @api private
19
+ #
20
+ class Parser < LL::Driver
21
+ CONFIG = LL::DriverConfig.new
22
+
23
+ CONFIG.terminals = [
24
+ :$EOF, # 0
25
+ :T_IDENT, # 1
26
+ :T_PIPE, # 2
27
+ :T_LBRACK, # 3
28
+ :T_RBRACK, # 4
29
+ :T_COLON, # 5
30
+ :T_SPACE, # 6
31
+ :T_LPAREN, # 7
32
+ :T_RPAREN, # 8
33
+ :T_MINUS, # 9
34
+ :T_EQ, # 10
35
+ :T_SPACE_IN, # 11
36
+ :T_STARTS_WITH, # 12
37
+ :T_ENDS_WITH, # 13
38
+ :T_IN, # 14
39
+ :T_HYPHEN_IN, # 15
40
+ :T_GREATER, # 16
41
+ :T_TILDE, # 17
42
+ :T_PLUS, # 18
43
+ :T_NTH, # 19
44
+ :T_INT, # 20
45
+ :T_STRING, # 21
46
+ :T_ODD, # 22
47
+ :T_EVEN, # 23
48
+ :T_DOT, # 24
49
+ :T_HASH, # 25
50
+ ].freeze
51
+
52
+ CONFIG.rules = [
53
+ [3, 0, 0, 1], # 0
54
+ [3, 1, 2, 0], # 1
55
+ [3, 2, 4, 29, 6, 0, 0, 3], # 2
56
+ [3, 3, 0, 3, 1, 6], # 3
57
+ [3, 4, 8, 30, 0, 4], # 4
58
+ [3, 5, 8, 31, 0, 5], # 5
59
+ [3, 6, 0, 10], # 6
60
+ [3, 7, 0, 7], # 7
61
+ [3, 8, 0, 6, 1, 16], # 8
62
+ [3, 9, 0, 6, 1, 17], # 9
63
+ [3, 10, 0, 6, 1, 18], # 10
64
+ [3, 11, 0, 7], # 11
65
+ [3, 12, 0, 5], # 12
66
+ [3, 13, 0, 8], # 13
67
+ [3, 14, 8, 32, 1, 1], # 14
68
+ [3, 15, 1, 1, 1, 2], # 15
69
+ [3, 16, 4, 33, 6, 0, 0, 11], # 16
70
+ [3, 17, 0, 17], # 17
71
+ [3, 18, 0, 18], # 18
72
+ [3, 19, 0, 19], # 19
73
+ [3, 20, 0, 12], # 20
74
+ [3, 21, 1, 4, 0, 13, 1, 3], # 21
75
+ [3, 22, 0, 15], # 22
76
+ [3, 23, 0, 8], # 23
77
+ [3, 24, 8, 34, 0, 14], # 24
78
+ [3, 25, 1, 10], # 25
79
+ [3, 26, 1, 11], # 26
80
+ [3, 27, 1, 12], # 27
81
+ [3, 28, 1, 13], # 28
82
+ [3, 29, 1, 14], # 29
83
+ [3, 30, 1, 15], # 30
84
+ [3, 31, 1, 1, 1, 24], # 31
85
+ [3, 32, 1, 1, 1, 25], # 32
86
+ [3, 33, 8, 35, 0, 20], # 33
87
+ [3, 34, 1, 1, 1, 5], # 34
88
+ [3, 35, 1, 8, 0, 22, 1, 7], # 35
89
+ [3, 36, 0, 27], # 36
90
+ [3, 37, 0, 28], # 37
91
+ [3, 38, 0, 25], # 38
92
+ [3, 39, 0, 3], # 39
93
+ [3, 40, 1, 21], # 40
94
+ [3, 41, 1, 20], # 41
95
+ [3, 42, 8, 36, 0, 26], # 42
96
+ [3, 43, 8, 37, 0, 26, 1, 9], # 43
97
+ [3, 44, 8, 39, 8, 38, 0, 24], # 44
98
+ [3, 45, 1, 19], # 45
99
+ [3, 46, 1, 22], # 46
100
+ [3, 47, 1, 23], # 47
101
+ [3, 48, 0, 2], # 48
102
+ [3, 49, 0, 10], # 49
103
+ [3, 50, 0, 10], # 50
104
+ [3, 51, 0, 9], # 51
105
+ [3, 52, 0, 11], # 52
106
+ [3, 53, 0, 23, 0, 16], # 53
107
+ [3, 54, 0, 21], # 54
108
+ [3, 55, 0, 24], # 55
109
+ [3, 56, 0, 24], # 56
110
+ [3, 57, 0, 26], # 57
111
+ [3, 58, 0, 24], # 58
112
+ ].freeze
113
+
114
+ CONFIG.table = [
115
+ [1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0], # 0
116
+ [-1, 2, -1, 2, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 2, 2, -1, -1, -1, -1, -1, 2, 2], # 1
117
+ [-1, -1, -1, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], # 2
118
+ [-1, 4, -1, 6, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5, 5, 5, -1, -1, -1, -1, -1, 6, 6], # 3
119
+ [-1, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], # 4
120
+ [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1], # 5
121
+ [-1, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 12, 12, -1, -1, -1, -1, -1, -1, -1], # 6
122
+ [-1, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], # 7
123
+ [-1, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], # 8
124
+ [-1, -1, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], # 9
125
+ [-1, -1, -1, 16, -1, 16, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 16, 16], # 10
126
+ [-1, -1, -1, 20, -1, 19, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 17, 18], # 11
127
+ [-1, -1, -1, 21, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], # 12
128
+ [-1, 22, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], # 13
129
+ [-1, 23, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], # 14
130
+ [-1, 24, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], # 15
131
+ [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 25, 26, 27, 28, 29, 30, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], # 16
132
+ [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 31, -1], # 17
133
+ [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 32], # 18
134
+ [-1, -1, -1, -1, -1, 33, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], # 19
135
+ [-1, -1, -1, -1, -1, 34, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], # 20
136
+ [-1, -1, -1, -1, -1, -1, -1, 35, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], # 21
137
+ [-1, 39, -1, 39, -1, 39, -1, -1, -1, 38, -1, -1, -1, -1, -1, -1, 39, 39, 39, 38, 38, -1, 36, 37, 39, 39], # 22
138
+ [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 40, -1, -1, -1, -1], # 23
139
+ [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 41, -1, -1, -1, -1, -1], # 24
140
+ [-1, -1, -1, -1, -1, -1, -1, -1, -1, 43, -1, -1, -1, -1, -1, -1, -1, -1, -1, 42, 44, -1, -1, -1, -1, -1], # 25
141
+ [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 45, -1, -1, -1, -1, -1, -1], # 26
142
+ [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 46, -1, -1, -1], # 27
143
+ [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 47, -1, -1], # 28
144
+ [-1, -1, -1, -1, -1, -1, 48, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], # 29
145
+ [-1, -1, -1, 49, -1, 49, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 49, 49], # 30
146
+ [-1, -1, -1, 50, -1, 50, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 50, 50], # 31
147
+ [-1, -1, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], # 32
148
+ [-1, -1, -1, 52, -1, 52, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 52, 52], # 33
149
+ [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 53, 53, 53, 53, 53, 53, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], # 34
150
+ [-1, -1, -1, -1, -1, -1, -1, 54, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], # 35
151
+ [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 55, -1, -1, -1, -1, -1], # 36
152
+ [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 56, -1, -1, -1, -1, -1], # 37
153
+ [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 57, -1, -1, -1, -1, -1, -1], # 38
154
+ [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 58, -1, -1, -1, -1, -1], # 39
155
+ ].freeze
156
+
157
+ CONFIG.actions = [
158
+ [:_rule_0, 1], # 0
159
+ [:_rule_1, 0], # 1
160
+ [:_rule_2, 2], # 2
161
+ [:_rule_3, 2], # 3
162
+ [:_rule_4, 2], # 4
163
+ [:_rule_5, 2], # 5
164
+ [:_rule_6, 1], # 6
165
+ [:_rule_7, 1], # 7
166
+ [:_rule_8, 2], # 8
167
+ [:_rule_9, 2], # 9
168
+ [:_rule_10, 2], # 10
169
+ [:_rule_11, 1], # 11
170
+ [:_rule_12, 1], # 12
171
+ [:_rule_13, 1], # 13
172
+ [:_rule_14, 2], # 14
173
+ [:_rule_15, 2], # 15
174
+ [:_rule_16, 2], # 16
175
+ [:_rule_17, 1], # 17
176
+ [:_rule_18, 1], # 18
177
+ [:_rule_19, 1], # 19
178
+ [:_rule_20, 1], # 20
179
+ [:_rule_21, 3], # 21
180
+ [:_rule_22, 1], # 22
181
+ [:_rule_23, 1], # 23
182
+ [:_rule_24, 2], # 24
183
+ [:_rule_25, 1], # 25
184
+ [:_rule_26, 1], # 26
185
+ [:_rule_27, 1], # 27
186
+ [:_rule_28, 1], # 28
187
+ [:_rule_29, 1], # 29
188
+ [:_rule_30, 1], # 30
189
+ [:_rule_31, 2], # 31
190
+ [:_rule_32, 2], # 32
191
+ [:_rule_33, 2], # 33
192
+ [:_rule_34, 2], # 34
193
+ [:_rule_35, 3], # 35
194
+ [:_rule_36, 1], # 36
195
+ [:_rule_37, 1], # 37
196
+ [:_rule_38, 1], # 38
197
+ [:_rule_39, 1], # 39
198
+ [:_rule_40, 1], # 40
199
+ [:_rule_41, 1], # 41
200
+ [:_rule_42, 2], # 42
201
+ [:_rule_43, 3], # 43
202
+ [:_rule_44, 3], # 44
203
+ [:_rule_45, 1], # 45
204
+ [:_rule_46, 1], # 46
205
+ [:_rule_47, 1], # 47
206
+ [:_rule_48, 1], # 48
207
+ [:_rule_49, 1], # 49
208
+ [:_rule_50, 1], # 50
209
+ [:_rule_51, 1], # 51
210
+ [:_rule_52, 1], # 52
211
+ [:_rule_53, 2], # 53
212
+ [:_rule_54, 1], # 54
213
+ [:_rule_55, 1], # 55
214
+ [:_rule_56, 1], # 56
215
+ [:_rule_57, 1], # 57
216
+ [:_rule_58, 1], # 58
217
+ ].freeze
218
+
219
+ ##
220
+ # @return [Oga::LRU]
221
+ #
222
+ CACHE = LRU.new
223
+
224
+ ##
225
+ # @param [String] data
226
+ # @return [AST::Node]
227
+ #
228
+ def self.parse_with_cache(data)
229
+ return CACHE.get_or_set(data) { new(data).parse }
230
+ end
231
+
232
+ ##
233
+ # @param [String] data The input to parse.
234
+ #
235
+ def initialize(data)
236
+ @lexer = Lexer.new(data)
237
+ end
238
+
239
+ ##
240
+ # Resets the internal state of the parser.
241
+ #
242
+ def reset
243
+ @current_element = nil
244
+ end
245
+
246
+ ##
247
+ # @param [Symbol] type
248
+ # @param [Array] children
249
+ # @return [AST::Node]
250
+ #
251
+ def s(type, *children)
252
+ return AST::Node.new(type, children)
253
+ end
254
+
255
+ ##
256
+ # Yields the next token from the lexer.
257
+ #
258
+ # @yieldparam [Array]
259
+ #
260
+ def each_token
261
+ @lexer.advance do |*args|
262
+ yield args
263
+ end
264
+
265
+ yield [-1, -1]
266
+ end
267
+
268
+ ##
269
+ # Returns the node test for the current element.
270
+ #
271
+ # @return [AST::Node]
272
+ #
273
+ def current_element
274
+ @current_element ||= s(:test, nil, '*')
275
+ end
276
+
277
+ ##
278
+ # Parses the input and returns the corresponding AST.
279
+ #
280
+ # @example
281
+ # parser = Oga::CSS::Parser.new('foo.bar')
282
+ # ast = parser.parse
283
+ #
284
+ # @return [AST::Node]
285
+ #
286
+ def parse
287
+ reset
288
+
289
+ super
290
+ end
291
+
292
+ ##
293
+ # Generates the AST for a node test.
294
+ #
295
+ # @param [String] namespace
296
+ # @param [String] name
297
+ # @return [AST::Node]
298
+ #
299
+ def on_test(namespace, name)
300
+ @current_element = s(:test, namespace, name)
301
+ end
302
+
303
+ ##
304
+ # @param [String] name
305
+ # @param [AST::Node] arg
306
+ # @return [AST::Node]
307
+ #
308
+ def on_pseudo_class(name, arg = nil)
309
+ handler = "on_pseudo_class_#{name.gsub('-', '_')}"
310
+
311
+ arg ? send(handler, arg) : send(handler)
312
+ end
313
+
314
+ ##
315
+ # Generates the AST for the `root` pseudo class.
316
+ #
317
+ # @return [AST::Node]
318
+ #
319
+ def on_pseudo_class_root
320
+ s(:call, 'not', s(:axis, 'parent', s(:test, nil, '*')))
321
+ end
322
+
323
+ ##
324
+ # Generates the AST for the `nth-child` pseudo class.
325
+ #
326
+ # @param [AST::Node] arg
327
+ # @return [AST::Node]
328
+ #
329
+ def on_pseudo_class_nth_child(arg)
330
+ generate_nth_child('preceding-sibling', arg)
331
+ end
332
+
333
+ ##
334
+ # Generates the AST for the `nth-last-child` pseudo class.
335
+ #
336
+ # @param [AST::Node] arg
337
+ # @return [AST::Node]
338
+ #
339
+ def on_pseudo_class_nth_last_child(arg)
340
+ generate_nth_child('following-sibling', arg)
341
+ end
342
+
343
+ ##
344
+ # Generates the AST for the `nth-of-type` pseudo class.
345
+ #
346
+ # @param [AST::Node] arg
347
+ # @return [AST::Node]
348
+ #
349
+ def on_pseudo_class_nth_of_type(arg)
350
+ generate_nth_child('preceding-sibling', arg, current_element)
351
+ end
352
+
353
+ ##
354
+ # Generates the AST for the `nth-last-of-type` pseudo class.
355
+ #
356
+ # @param [AST::Node] arg
357
+ # @return [AST::Node]
358
+ #
359
+ def on_pseudo_class_nth_last_of_type(arg)
360
+ generate_nth_child('following-sibling', arg, current_element)
361
+ end
362
+
363
+ ##
364
+ # Generates the AST for the `:first-child` selector.
365
+ #
366
+ # @return [AST::Node]
367
+ #
368
+ def on_pseudo_class_first_child
369
+ generate_no_siblings('preceding-sibling')
370
+ end
371
+
372
+ ##
373
+ # Generates the AST for the `:last-child` selector.
374
+ #
375
+ # @return [AST::Node]
376
+ #
377
+ def on_pseudo_class_last_child
378
+ generate_no_siblings('following-sibling')
379
+ end
380
+
381
+ ##
382
+ # Generates the AST for the `:first-of-type` selector.
383
+ #
384
+ # @return [AST::Node]
385
+ #
386
+ def on_pseudo_class_first_of_type
387
+ generate_no_siblings('preceding-sibling', current_element)
388
+ end
389
+
390
+ ##
391
+ # Generates the AST for the `:last-of-type` selector.
392
+ #
393
+ # @return [AST::Node]
394
+ #
395
+ def on_pseudo_class_last_of_type
396
+ generate_no_siblings('following-sibling', current_element)
397
+ end
398
+
399
+ ##
400
+ # Generates the AST for the `:only-child` selector.
401
+ #
402
+ # @return [AST::Node]
403
+ #
404
+ def on_pseudo_class_only_child
405
+ s(:and, on_pseudo_class_first_child, on_pseudo_class_last_child)
406
+ end
407
+
408
+ ##
409
+ # Generates the AST for the `:only-of-type` selector.
410
+ #
411
+ # @return [AST::Node]
412
+ #
413
+ def on_pseudo_class_only_of_type
414
+ s(:and, on_pseudo_class_first_of_type, on_pseudo_class_last_of_type)
415
+ end
416
+
417
+ ##
418
+ # Generates the AST for the `:empty` selector.
419
+ #
420
+ # @return [AST::Node]
421
+ #
422
+ def on_pseudo_class_empty
423
+ s(:call, 'not', s(:axis, 'child', s(:type_test, 'node')))
424
+ end
425
+
426
+ ##
427
+ # Generates the AST for the `=` operator.
428
+ #
429
+ # @param [AST::Node] attr
430
+ # @param [AST::Node] value
431
+ # @return [AST::Node]
432
+ #
433
+ def on_op_eq(attr, value)
434
+ s(:eq, attr, value)
435
+ end
436
+
437
+ ##
438
+ # Generates the AST for the `~=` operator.
439
+ #
440
+ # @param [AST::Node] attr
441
+ # @param [AST::Node] value
442
+ # @return [AST::Node]
443
+ #
444
+ def on_op_space_in(attr, value)
445
+ s(
446
+ :call,
447
+ 'contains',
448
+ s(:call, 'concat', s(:string, ' '), attr, s(:string, ' ')),
449
+ s(:call, 'concat', s(:string, ' '), value, s(:string, ' '))
450
+ )
451
+ end
452
+
453
+ ##
454
+ # Generates the AST for the `^=` operator.
455
+ #
456
+ # @param [AST::Node] attr
457
+ # @param [AST::Node] value
458
+ # @return [AST::Node]
459
+ #
460
+ def on_op_starts_with(attr, value)
461
+ s(:call, 'starts-with', attr, value)
462
+ end
463
+
464
+ ##
465
+ # Generates the AST for the `$=` operator.
466
+ #
467
+ # @param [AST::Node] attr
468
+ # @param [AST::Node] value
469
+ # @return [AST::Node]
470
+ #
471
+ def on_op_ends_with(attr, value)
472
+ s(
473
+ :eq,
474
+ s(
475
+ :call,
476
+ 'substring',
477
+ attr,
478
+ s(
479
+ :add,
480
+ s(
481
+ :sub,
482
+ s(:call, 'string-length', attr),
483
+ s(:call, 'string-length', value)
484
+ ),
485
+ s(:int, 1)
486
+ ),
487
+ s(:call, 'string-length', value)
488
+ ),
489
+ value
490
+ )
491
+ end
492
+
493
+ ##
494
+ # Generates the AST for the `*=` operator.
495
+ #
496
+ # @param [AST::Node] attr
497
+ # @param [AST::Node] value
498
+ # @return [AST::Node]
499
+ #
500
+ def on_op_in(attr, value)
501
+ s(:call, 'contains', attr, value)
502
+ end
503
+
504
+ ##
505
+ # Generates the AST for the `|=` operator.
506
+ #
507
+ # @param [AST::Node] attr
508
+ # @param [AST::Node] value
509
+ # @return [AST::Node]
510
+ #
511
+ def on_op_hyphen_in(attr, value)
512
+ s(
513
+ :or,
514
+ s(:eq, attr, value),
515
+ s(
516
+ :call,
517
+ 'starts-with',
518
+ attr,
519
+ s(:call, 'concat', value, s(:string, '-'))
520
+ )
521
+ )
522
+ end
523
+
524
+ private
525
+
526
+ ##
527
+ # @param [String] count_axis
528
+ # @param [AST::Node] arg
529
+ # @param [AST::Node] count_test
530
+ # @return [AST::Node]
531
+ #
532
+ def generate_nth_child(count_axis, arg, count_test = s(:test, nil, '*'))
533
+ count_call = s(:call, 'count', s(:axis, count_axis, count_test))
534
+
535
+ # literal 2, 4, etc
536
+ if int_node?(arg)
537
+ node = s(:eq, count_call, s(:int, arg.children[0] - 1))
538
+ else
539
+ step, offset = *arg
540
+ before_count = s(:add, count_call, s(:int, 1))
541
+ compare = step_comparison(step)
542
+
543
+ # 2n+2, 2n-4, etc
544
+ if offset
545
+ mod_val = step_modulo_value(step)
546
+ node = s(
547
+ :and,
548
+ s(compare, before_count, offset),
549
+ s(:eq, s(:mod, s(:sub, before_count, offset), mod_val), s(:int, 0))
550
+ )
551
+
552
+ # 2n, n, -2n
553
+ else
554
+ node = s(:eq, s(:mod, before_count, step), s(:int, 0))
555
+ end
556
+ end
557
+
558
+ return node
559
+ end
560
+
561
+ ##
562
+ # @param [String] axis
563
+ # @param [AST::Node] test
564
+ # @return [AST::Node]
565
+ #
566
+ def generate_no_siblings(axis, test = s(:test, nil, '*'))
567
+ s(:eq, s(:call, 'count', s(:axis, axis, test)), s(:int, 0))
568
+ end
569
+
570
+ ##
571
+ # @param [AST::Node] node
572
+ # @return [TrueClass|FalseClass]
573
+ #
574
+ def int_node?(node)
575
+ node.type.equal?(:int)
576
+ end
577
+
578
+ ##
579
+ # @param [AST::Node] node
580
+ # @return [TrueClass|FalseClass]
581
+ #
582
+ def non_positive_number?(node)
583
+ node.children[0] <= 0
584
+ end
585
+
586
+ ##
587
+ # @param [AST::Node] node
588
+ # @return [Symbol]
589
+ #
590
+ def step_comparison(node)
591
+ node.children[0] >= 0 ? :gte : :lte
592
+ end
593
+
594
+ ##
595
+ # @param [AST::Node] step
596
+ # @return [AST::Node]
597
+ #
598
+ def step_modulo_value(step)
599
+ # -2n
600
+ if step and non_positive_number?(step)
601
+ mod_val = s(:int, -step.children[0])
602
+
603
+ # 2n
604
+ elsif step
605
+ mod_val = step
606
+
607
+ else
608
+ mod_val = s(:int, 1)
609
+ end
610
+
611
+ mod_val
612
+ end
613
+
614
+ def _rule_0(val)
615
+ val[0]
616
+ end
617
+
618
+ def _rule_1(val)
619
+ nil
620
+ end
621
+
622
+ def _rule_2(val)
623
+
624
+ # Single selector
625
+ if val[1].empty?
626
+ ret = val[0]
627
+
628
+ if ret.is_a?(Array)
629
+ ret = s(:path, *ret)
630
+ end
631
+
632
+ # Multiple selectors
633
+ else
634
+ steps = [val[0]]
635
+
636
+ val[1].each do |step|
637
+ # "+ foo" is broken up into two steps.
638
+ if step.is_a?(Array)
639
+ # Using Array#+ or Array#| would require allocating an extra Array
640
+ step.each { |sub| steps << sub }
641
+ else
642
+ steps << step
643
+ end
644
+ end
645
+
646
+ ret = s(:path, *steps)
647
+ end
648
+
649
+ ret
650
+
651
+ end
652
+
653
+ def _rule_3(val)
654
+ val[1]
655
+ end
656
+
657
+ def _rule_4(val)
658
+
659
+ val[1] ? s(:predicate, val[0], val[1]) : val[0]
660
+
661
+ end
662
+
663
+ def _rule_5(val)
664
+
665
+ val[1] ? s(:predicate, val[0], val[1]) : val[0]
666
+
667
+ end
668
+
669
+ def _rule_6(val)
670
+
671
+ s(:predicate, s(:axis, 'descendant', on_test(nil, '*')), val[0])
672
+
673
+ end
674
+
675
+ def _rule_7(val)
676
+ s(:axis, 'descendant', val[0])
677
+ end
678
+
679
+ def _rule_8(val)
680
+
681
+ s(:axis, 'child', val[1])
682
+
683
+ end
684
+
685
+ def _rule_9(val)
686
+
687
+ s(:axis, 'following-sibling', val[1])
688
+
689
+ end
690
+
691
+ def _rule_10(val)
692
+
693
+ [
694
+ s(
695
+ :predicate,
696
+ s(:axis, 'following-sibling', on_test(nil, '*')),
697
+ s(:int, 1)
698
+ ),
699
+ s(:axis, 'self', val[1])
700
+ ]
701
+
702
+ end
703
+
704
+ def _rule_11(val)
705
+ val[0]
706
+ end
707
+
708
+ def _rule_12(val)
709
+ val[0]
710
+ end
711
+
712
+ def _rule_13(val)
713
+ on_test(*val[0])
714
+ end
715
+
716
+ def _rule_14(val)
717
+ val[1] ? [val[0], val[1]] : [nil, val[0]]
718
+ end
719
+
720
+ def _rule_15(val)
721
+ val[1]
722
+ end
723
+
724
+ def _rule_16(val)
725
+
726
+ ret = val[0]
727
+
728
+ val[1].each do |pred|
729
+ ret = s(:and, ret, pred)
730
+ end
731
+
732
+ ret
733
+
734
+ end
735
+
736
+ def _rule_17(val)
737
+ val[0]
738
+ end
739
+
740
+ def _rule_18(val)
741
+ val[0]
742
+ end
743
+
744
+ def _rule_19(val)
745
+ val[0]
746
+ end
747
+
748
+ def _rule_20(val)
749
+ val[0]
750
+ end
751
+
752
+ def _rule_21(val)
753
+ val[1]
754
+ end
755
+
756
+ def _rule_22(val)
757
+ val[0]
758
+ end
759
+
760
+ def _rule_23(val)
761
+ s(:axis, 'attribute', on_test(*val[0]))
762
+ end
763
+
764
+ def _rule_24(val)
765
+
766
+ op_type = val[1] ? val[1][0] : nil
767
+
768
+ case op_type
769
+ # a="b"
770
+ when :eq
771
+ on_op_eq(val[0], val[1][1])
772
+
773
+ # a~="b"
774
+ when :space_in
775
+ on_op_space_in(val[0], val[1][1])
776
+
777
+ # a^="b"
778
+ when :starts_with
779
+ on_op_starts_with(val[0], val[1][1])
780
+
781
+ # a$="b"
782
+ when :ends_with
783
+ on_op_ends_with(val[0], val[1][1])
784
+
785
+ # a*="b"
786
+ when :in
787
+ on_op_in(val[0], val[1][1])
788
+
789
+ # a|="b"
790
+ when :hyphen_in
791
+ on_op_hyphen_in(val[0], val[1][1])
792
+
793
+ else
794
+ val[0]
795
+ end
796
+
797
+ end
798
+
799
+ def _rule_25(val)
800
+ :eq
801
+ end
802
+
803
+ def _rule_26(val)
804
+ :space_in
805
+ end
806
+
807
+ def _rule_27(val)
808
+ :starts_with
809
+ end
810
+
811
+ def _rule_28(val)
812
+ :ends_with
813
+ end
814
+
815
+ def _rule_29(val)
816
+ :in
817
+ end
818
+
819
+ def _rule_30(val)
820
+ :hyphen_in
821
+ end
822
+
823
+ def _rule_31(val)
824
+
825
+ axis = s(:axis, 'attribute', s(:test, nil, 'class'))
826
+
827
+ s(
828
+ :call,
829
+ 'contains',
830
+ s(:call, 'concat', s(:string, ' '), axis, s(:string, ' ')),
831
+ s(:string, " #{val[1]} ")
832
+ )
833
+
834
+ end
835
+
836
+ def _rule_32(val)
837
+
838
+ s(
839
+ :eq,
840
+ s(:axis, 'attribute', s(:test, nil, 'id')),
841
+ s(:string, val[1])
842
+ )
843
+
844
+ end
845
+
846
+ def _rule_33(val)
847
+ on_pseudo_class(val[0], val[1])
848
+ end
849
+
850
+ def _rule_34(val)
851
+ val[1]
852
+ end
853
+
854
+ def _rule_35(val)
855
+ val[1]
856
+ end
857
+
858
+ def _rule_36(val)
859
+ val[0]
860
+ end
861
+
862
+ def _rule_37(val)
863
+ val[0]
864
+ end
865
+
866
+ def _rule_38(val)
867
+ val[0]
868
+ end
869
+
870
+ def _rule_39(val)
871
+ val[0]
872
+ end
873
+
874
+ def _rule_40(val)
875
+ s(:string, val[0])
876
+ end
877
+
878
+ def _rule_41(val)
879
+ s(:int, val[0].to_i)
880
+ end
881
+
882
+ def _rule_42(val)
883
+
884
+ val[1] ? s(:nth, s(:int, 1), val[1]) : s(:nth, s(:int, 1))
885
+
886
+ end
887
+
888
+ def _rule_43(val)
889
+
890
+ val[2] ? s(:nth, s(:int, -1), val[2]) : s(:nth, s(:int, 1))
891
+
892
+ end
893
+
894
+ def _rule_44(val)
895
+
896
+ # 2n+1
897
+ if val[1] and val[2]
898
+ a = val[0]
899
+ b = val[2]
900
+
901
+ # 2n-1 gets turned into 2n+1
902
+ if b.children[0] < 0
903
+ b = s(:int, a.children[0] - (b.children[0] % a.children[0]))
904
+ end
905
+
906
+ s(:nth, a, b)
907
+
908
+ # 2n
909
+ elsif val[1]
910
+ s(:nth, val[0])
911
+
912
+ # 2
913
+ else
914
+ val[0]
915
+ end
916
+
917
+ end
918
+
919
+ def _rule_45(val)
920
+ :nth
921
+ end
922
+
923
+ def _rule_46(val)
924
+ s(:nth, s(:int, 2), s(:int, 1))
925
+ end
926
+
927
+ def _rule_47(val)
928
+ s(:nth, s(:int, 2))
929
+ end
930
+
931
+ def _rule_48(val)
932
+ val[0]
933
+ end
934
+
935
+ def _rule_49(val)
936
+ val[0]
937
+ end
938
+
939
+ def _rule_50(val)
940
+ val[0]
941
+ end
942
+
943
+ def _rule_51(val)
944
+ val[0]
945
+ end
946
+
947
+ def _rule_52(val)
948
+ val[0]
949
+ end
950
+
951
+ def _rule_53(val)
952
+ val
953
+ end
954
+
955
+ def _rule_54(val)
956
+ val[0]
957
+ end
958
+
959
+ def _rule_55(val)
960
+ val[0]
961
+ end
962
+
963
+ def _rule_56(val)
964
+ val[0]
965
+ end
966
+
967
+ def _rule_57(val)
968
+ val[0]
969
+ end
970
+
971
+ def _rule_58(val)
972
+ val[0]
973
+ end
974
+ end
975
+ end
976
+ end