csspool 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/.gemtest +0 -0
  2. data/CHANGELOG.rdoc +6 -0
  3. data/Manifest.txt +10 -14
  4. data/README.rdoc +8 -21
  5. data/Rakefile +36 -2
  6. data/lib/csspool/collection.rb +2 -2
  7. data/lib/csspool/css/charset.rb +8 -2
  8. data/lib/csspool/css/declaration.rb +13 -2
  9. data/lib/csspool/css/document_handler.rb +3 -3
  10. data/lib/csspool/css/import_rule.rb +15 -3
  11. data/lib/csspool/css/media.rb +8 -2
  12. data/lib/csspool/css/parser.rb +1090 -0
  13. data/lib/csspool/css/parser.y +347 -0
  14. data/lib/csspool/css/rule_set.rb +8 -3
  15. data/lib/csspool/css/tokenizer.rb +228 -0
  16. data/lib/csspool/css/tokenizer.rex +96 -0
  17. data/lib/csspool/css.rb +1 -0
  18. data/lib/csspool/node.rb +29 -1
  19. data/lib/csspool/sac/document.rb +3 -3
  20. data/lib/csspool/sac/parser.rb +6 -112
  21. data/lib/csspool/terms/function.rb +1 -1
  22. data/lib/csspool/terms/ident.rb +1 -1
  23. data/lib/csspool/terms/number.rb +1 -1
  24. data/lib/csspool/terms/rgb.rb +1 -4
  25. data/lib/csspool/terms/string.rb +1 -1
  26. data/lib/csspool/visitors/children.rb +50 -0
  27. data/lib/csspool/visitors/comparable.rb +9 -3
  28. data/lib/csspool/visitors/iterator.rb +80 -0
  29. data/lib/csspool/visitors/to_css.rb +61 -45
  30. data/lib/csspool/visitors.rb +2 -0
  31. data/lib/csspool.rb +6 -3
  32. data/test/css/test_parser.rb +412 -0
  33. data/test/css/test_tokenizer.rb +320 -0
  34. data/test/helper.rb +2 -2
  35. data/test/sac/test_parser.rb +3 -8
  36. data/test/sac/test_terms.rb +128 -34
  37. data/test/test_collection.rb +1 -1
  38. data/test/test_parser.rb +1 -1
  39. data/test/visitors/test_children.rb +20 -0
  40. data/test/visitors/test_comparable.rb +31 -1
  41. data/test/visitors/test_each.rb +19 -0
  42. data/test/visitors/test_to_css.rb +125 -1
  43. metadata +90 -68
  44. data/lib/csspool/lib_croco/cr_additional_sel.rb +0 -46
  45. data/lib/csspool/lib_croco/cr_attr_sel.rb +0 -16
  46. data/lib/csspool/lib_croco/cr_doc_handler.rb +0 -24
  47. data/lib/csspool/lib_croco/cr_num.rb +0 -13
  48. data/lib/csspool/lib_croco/cr_parser.rb +0 -11
  49. data/lib/csspool/lib_croco/cr_parsing_location.rb +0 -17
  50. data/lib/csspool/lib_croco/cr_pseudo.rb +0 -14
  51. data/lib/csspool/lib_croco/cr_rgb.rb +0 -18
  52. data/lib/csspool/lib_croco/cr_selector.rb +0 -34
  53. data/lib/csspool/lib_croco/cr_simple_sel.rb +0 -54
  54. data/lib/csspool/lib_croco/cr_term.rb +0 -97
  55. data/lib/csspool/lib_croco/glist.rb +0 -21
  56. data/lib/csspool/lib_croco.rb +0 -78
  57. data/lib/csspool/visitable.rb +0 -18
@@ -0,0 +1,347 @@
1
+ class CSSPool::CSS::Parser
2
+
3
+ token CHARSET_SYM IMPORT_SYM STRING SEMI IDENT S COMMA LBRACE RBRACE STAR HASH
4
+ token LSQUARE RSQUARE EQUAL INCLUDES DASHMATCH RPAREN FUNCTION GREATER PLUS
5
+ token SLASH NUMBER MINUS LENGTH PERCENTAGE EMS EXS ANGLE TIME FREQ URI
6
+ token IMPORTANT_SYM MEDIA_SYM
7
+
8
+ rule
9
+ document
10
+ : { @handler.start_document }
11
+ stylesheet
12
+ { @handler.end_document }
13
+ ;
14
+ stylesheet
15
+ : charset stylesheet
16
+ | import stylesheet
17
+ | charset
18
+ | import
19
+ | body
20
+ ;
21
+ charset
22
+ : CHARSET_SYM STRING SEMI { @handler.charset interpret_string(val[1]), {} }
23
+ ;
24
+ import
25
+ : IMPORT_SYM import_location medium SEMI {
26
+ @handler.import_style [val[2]].flatten, val[1]
27
+ }
28
+ | IMPORT_SYM import_location SEMI {
29
+ @handler.import_style [], val[1]
30
+ }
31
+ ;
32
+ import_location
33
+ : import_location S
34
+ | STRING { result = Terms::String.new interpret_string val.first }
35
+ | URI { result = Terms::URI.new interpret_uri val.first }
36
+ ;
37
+ medium
38
+ : medium COMMA IDENT {
39
+ result = [val.first, Terms::Ident.new(interpret_identifier val.last)]
40
+ }
41
+ | IDENT {
42
+ result = Terms::Ident.new interpret_identifier val.first
43
+ }
44
+ ;
45
+ body
46
+ : ruleset body
47
+ | media body
48
+ | ruleset
49
+ | media
50
+ ;
51
+ media
52
+ : start_media body RBRACE { @handler.end_media val.first }
53
+ ;
54
+ start_media
55
+ : MEDIA_SYM medium LBRACE {
56
+ result = [val[1]].flatten
57
+ @handler.start_media result
58
+ }
59
+ | MEDIA_SYM LBRACE { result = [] }
60
+ ;
61
+ ruleset
62
+ : start_selector declarations RBRACE {
63
+ @handler.end_selector val.first
64
+ }
65
+ | start_selector RBRACE {
66
+ @handler.end_selector val.first
67
+ }
68
+ ;
69
+ start_selector
70
+ : S start_selector { result = val.last }
71
+ | selectors LBRACE {
72
+ @handler.start_selector val.first
73
+ }
74
+ ;
75
+ selectors
76
+ : selector COMMA selectors
77
+ {
78
+ # FIXME: should always garantee array
79
+ sel = Selector.new(val.first, {})
80
+ result = [sel, val[2]].flatten
81
+ }
82
+ | selector
83
+ {
84
+ result = [Selector.new(val.first, {})]
85
+ }
86
+ ;
87
+ selector
88
+ : simple_selector combinator selector
89
+ {
90
+ val = val.flatten
91
+ val[2].combinator = val.delete_at 1
92
+ result = val
93
+ }
94
+ | simple_selector
95
+ ;
96
+ combinator
97
+ : S { result = :s }
98
+ | GREATER { result = :> }
99
+ | PLUS { result = :+ }
100
+ ;
101
+ simple_selector
102
+ : element_name hcap {
103
+ selector = val.first
104
+ selector.additional_selectors = val.last
105
+ result = [selector]
106
+ }
107
+ | element_name { result = val }
108
+ | hcap
109
+ {
110
+ ss = Selectors::Simple.new nil, nil
111
+ ss.additional_selectors = val.flatten
112
+ result = [ss]
113
+ }
114
+ ;
115
+ element_name
116
+ : IDENT { result = Selectors::Type.new interpret_identifier val.first }
117
+ | STAR { result = Selectors::Universal.new val.first }
118
+ ;
119
+ hcap
120
+ : hash { result = val }
121
+ | class { result = val }
122
+ | attrib { result = val }
123
+ | pseudo { result = val }
124
+ | hash hcap { result = val.flatten }
125
+ | class hcap { result = val.flatten }
126
+ | attrib hcap { result = val.flatten }
127
+ | pseudo hcap { result = val.flatten }
128
+ ;
129
+ hash
130
+ : HASH {
131
+ result = Selectors::Id.new interpret_identifier val.first.sub(/^#/, '')
132
+ }
133
+ class
134
+ : '.' IDENT {
135
+ result = Selectors::Class.new interpret_identifier val.last
136
+ }
137
+ ;
138
+ attrib
139
+ : LSQUARE IDENT EQUAL IDENT RSQUARE {
140
+ result = Selectors::Attribute.new(
141
+ interpret_identifier(val[1]),
142
+ interpret_identifier(val[3]),
143
+ Selectors::Attribute::EQUALS
144
+ )
145
+ }
146
+ | LSQUARE IDENT EQUAL STRING RSQUARE {
147
+ result = Selectors::Attribute.new(
148
+ interpret_identifier(val[1]),
149
+ interpret_string(val[3]),
150
+ Selectors::Attribute::EQUALS
151
+ )
152
+ }
153
+ | LSQUARE IDENT INCLUDES STRING RSQUARE {
154
+ result = Selectors::Attribute.new(
155
+ interpret_identifier(val[1]),
156
+ interpret_string(val[3]),
157
+ Selectors::Attribute::INCLUDES
158
+ )
159
+ }
160
+ | LSQUARE IDENT INCLUDES IDENT RSQUARE {
161
+ result = Selectors::Attribute.new(
162
+ interpret_identifier(val[1]),
163
+ interpret_identifier(val[3]),
164
+ Selectors::Attribute::INCLUDES
165
+ )
166
+ }
167
+ | LSQUARE IDENT DASHMATCH IDENT RSQUARE {
168
+ result = Selectors::Attribute.new(
169
+ interpret_identifier(val[1]),
170
+ interpret_identifier(val[3]),
171
+ Selectors::Attribute::DASHMATCH
172
+ )
173
+ }
174
+ | LSQUARE IDENT DASHMATCH STRING RSQUARE {
175
+ result = Selectors::Attribute.new(
176
+ interpret_identifier(val[1]),
177
+ interpret_string(val[3]),
178
+ Selectors::Attribute::DASHMATCH
179
+ )
180
+ }
181
+ | LSQUARE IDENT RSQUARE {
182
+ result = Selectors::Attribute.new(
183
+ interpret_identifier(val[1]),
184
+ nil,
185
+ Selectors::Attribute::SET
186
+ )
187
+ }
188
+ ;
189
+ pseudo
190
+ : ':' IDENT {
191
+ result = Selectors::PseudoClass.new(
192
+ interpret_identifier(val[1])
193
+ )
194
+ }
195
+ | ':' FUNCTION RPAREN {
196
+ result = Selectors::PseudoClass.new(
197
+ interpret_identifier(val[1].sub(/\($/, '')),
198
+ ''
199
+ )
200
+ }
201
+ | ':' FUNCTION IDENT RPAREN {
202
+ result = Selectors::PseudoClass.new(
203
+ interpret_identifier(val[1].sub(/\($/, '')),
204
+ interpret_identifier(val[2])
205
+ )
206
+ }
207
+ ;
208
+ declarations
209
+ : declaration declarations
210
+ | declaration
211
+ ;
212
+ declaration
213
+ : property ':' expr prio SEMI
214
+ { @handler.property val.first, val[2], val[3] }
215
+ | property ':' S expr prio SEMI
216
+ { @handler.property val.first, val[3], val[4] }
217
+ ;
218
+ prio
219
+ : IMPORTANT_SYM { result = true }
220
+ | { result = false }
221
+ ;
222
+ property
223
+ : IDENT { result = interpret_identifier val[0] }
224
+ ;
225
+ operator
226
+ : COMMA
227
+ | SLASH
228
+ | EQUAL
229
+ ;
230
+ expr
231
+ : term operator expr {
232
+ result = [val.first, val.last].flatten
233
+ val.last.first.operator = val[1]
234
+ }
235
+ | term expr { result = val.flatten }
236
+ | term { result = val }
237
+ ;
238
+ term
239
+ : ident
240
+ | numeric
241
+ | string
242
+ | uri
243
+ | hexcolor
244
+ | function
245
+ ;
246
+ function
247
+ : function S { result = val.first }
248
+ | FUNCTION expr RPAREN {
249
+ name = interpret_identifier val.first.sub(/\($/, '')
250
+ if name == 'rgb'
251
+ result = Terms::Rgb.new(*val[1])
252
+ else
253
+ result = Terms::Function.new name, val[1]
254
+ end
255
+ }
256
+ ;
257
+ hexcolor
258
+ : hexcolor S { result = val.first }
259
+ | HASH { result = Terms::Hash.new val.first.sub(/^#/, '') }
260
+ ;
261
+ uri
262
+ : uri S { result = val.first }
263
+ | URI { result = Terms::URI.new interpret_uri val.first }
264
+ string
265
+ : string S { result = val.first }
266
+ | STRING { result = Terms::String.new interpret_string val.first }
267
+ ;
268
+ numeric
269
+ : unary_operator numeric {
270
+ result = val[1]
271
+ val[1].unary_operator = val.first
272
+ }
273
+ | NUMBER {
274
+ result = Terms::Number.new numeric val.first
275
+ }
276
+ | PERCENTAGE {
277
+ result = Terms::Number.new numeric(val.first), nil, '%'
278
+ }
279
+ | LENGTH {
280
+ unit = val.first.gsub(/[\s\d.]/, '')
281
+ result = Terms::Number.new numeric(val.first), nil, unit
282
+ }
283
+ | EMS {
284
+ result = Terms::Number.new numeric(val.first), nil, 'em'
285
+ }
286
+ | EXS {
287
+ result = Terms::Number.new numeric(val.first), nil, 'ex'
288
+ }
289
+ | ANGLE {
290
+ unit = val.first.gsub(/[\s\d.]/, '')
291
+ result = Terms::Number.new numeric(val.first), nil, unit
292
+ }
293
+ | TIME {
294
+ unit = val.first.gsub(/[\s\d.]/, '')
295
+ result = Terms::Number.new numeric(val.first), nil, unit
296
+ }
297
+ | FREQ {
298
+ unit = val.first.gsub(/[\s\d.]/, '')
299
+ result = Terms::Number.new numeric(val.first), nil, unit
300
+ }
301
+ ;
302
+ unary_operator
303
+ : MINUS { result = :minus }
304
+ | PLUS { result = :plus }
305
+ ;
306
+ ident
307
+ : ident S { result = val.first }
308
+ | IDENT { result = Terms::Ident.new interpret_identifier val.first }
309
+ ;
310
+
311
+ ---- inner
312
+
313
+ def numeric thing
314
+ thing = thing.gsub(/[^\d.]/, '')
315
+ Integer(thing) rescue Float(thing)
316
+ end
317
+
318
+ def interpret_identifier s
319
+ interpret_escapes s
320
+ end
321
+
322
+ def interpret_uri s
323
+ interpret_escapes s.match(/^url\((.*)\)$/mu)[1].strip.match(/^(['"]?)((?:\\.|.)*)\1$/mu)[2]
324
+ end
325
+
326
+ def interpret_string s
327
+ interpret_escapes s.match(/^(['"])((?:\\.|.)*)\1$/mu)[2]
328
+ end
329
+
330
+ def interpret_escapes s
331
+ token_exp = /\\([0-9a-fA-F]{1,6}(?:\r\n|\s)?)|\\(.)|(.)/mu
332
+ characters = s.scan(token_exp).map do |u_escape, i_escape, ident|
333
+ if u_escape
334
+ code = u_escape.chomp.to_i 16
335
+ code = 0xFFFD if code > 0x10FFFF
336
+ [code].pack('U')
337
+ elsif i_escape
338
+ if i_escape == "\n"
339
+ ''
340
+ else
341
+ i_escape
342
+ end
343
+ else
344
+ ident
345
+ end
346
+ end.join ''
347
+ end
@@ -1,11 +1,16 @@
1
1
  module CSSPool
2
2
  module CSS
3
- class RuleSet < Struct.new(:selectors, :declarations, :media)
4
- include CSSPool::Visitable
3
+ class RuleSet < CSSPool::Node
4
+ attr_accessor :selectors
5
+ attr_accessor :declarations
6
+ attr_accessor :media
5
7
 
6
8
  def initialize selectors, declarations = [], media = []
9
+ @selectors = selectors
10
+ @declarations = declarations
11
+ @media = media
12
+
7
13
  selectors.each { |sel| sel.rule_set = self }
8
- super
9
14
  end
10
15
  end
11
16
  end
@@ -0,0 +1,228 @@
1
+ #--
2
+ # DO NOT MODIFY!!!!
3
+ # This file is automatically generated by rex 1.0.5
4
+ # from lexical definition file "lib/csspool/css/tokenizer.rex".
5
+ #++
6
+
7
+ module CSSPool
8
+ module CSS
9
+ class Tokenizer < Parser
10
+ require 'strscan'
11
+
12
+ class ScanError < StandardError ; end
13
+
14
+ attr_reader :lineno
15
+ attr_reader :filename
16
+ attr_accessor :state
17
+
18
+ def scan_setup(str)
19
+ @ss = StringScanner.new(str)
20
+ @lineno = 1
21
+ @state = nil
22
+ end
23
+
24
+ def action
25
+ yield
26
+ end
27
+
28
+ def scan_str(str)
29
+ scan_setup(str)
30
+ do_parse
31
+ end
32
+ alias :scan :scan_str
33
+
34
+ def load_file( filename )
35
+ @filename = filename
36
+ open(filename, "r") do |f|
37
+ scan_setup(f.read)
38
+ end
39
+ end
40
+
41
+ def scan_file( filename )
42
+ load_file(filename)
43
+ do_parse
44
+ end
45
+
46
+
47
+ def next_token
48
+ return if @ss.eos?
49
+
50
+ # skips empty actions
51
+ until token = _next_token or @ss.eos?; end
52
+ token
53
+ end
54
+
55
+ def _next_token
56
+ text = @ss.peek(1)
57
+ @lineno += 1 if text == "\n"
58
+ token = case @state
59
+ when nil
60
+ case
61
+ when (text = @ss.scan(/url\([\s]*("([^\n\r\f\\"]|\\\n|\r\n|\r|\f|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*"|'([^\n\r\f\\']|\\\n|\r\n|\r|\f|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*')[\s]*\)/i))
62
+ action { [:URI, st(text)] }
63
+
64
+ when (text = @ss.scan(/url\([\s]*([!#\$%&*-~]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*[\s]*\)/i))
65
+ action { [:URI, st(text)] }
66
+
67
+ when (text = @ss.scan(/U\+[0-9a-fA-F?]{1,6}(-[0-9a-fA-F]{1,6})?/i))
68
+ action {[:UNICODE_RANGE, st(text)] }
69
+
70
+ when (text = @ss.scan(/[\s]*\/\*(.|[\s]*)*?\*\/[\s]*/i))
71
+ action { next_token }
72
+
73
+ when (text = @ss.scan(/[-@]?([_A-Za-z]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])([_A-Za-z0-9-]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f]|[.])*\(\s*/i))
74
+ action { [:FUNCTION, st(text)] }
75
+
76
+ when (text = @ss.scan(/[\s]*@import[\s]*/i))
77
+ action { [:IMPORT_SYM, st(text)] }
78
+
79
+ when (text = @ss.scan(/[\s]*@page[\s]*/i))
80
+ action { [:PAGE_SYM, st(text)] }
81
+
82
+ when (text = @ss.scan(/[\s]*@charset[\s]*/i))
83
+ action { [:CHARSET_SYM, st(text)] }
84
+
85
+ when (text = @ss.scan(/[\s]*@media[\s]*/i))
86
+ action { [:MEDIA_SYM, st(text)] }
87
+
88
+ when (text = @ss.scan(/[\s]*!([\s]*|[\s]*\/\*(.|[\s]*)*?\*\/[\s]*)important[\s]*/i))
89
+ action { [:IMPORTANT_SYM, st(text)] }
90
+
91
+ when (text = @ss.scan(/[-@]?([_A-Za-z]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])([_A-Za-z0-9-]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*/i))
92
+ action { [:IDENT, st(text)] }
93
+
94
+ when (text = @ss.scan(/\#([_A-Za-z0-9-]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])+/i))
95
+ action { [:HASH, st(text)] }
96
+
97
+ when (text = @ss.scan(/[\s]*~=[\s]*/i))
98
+ action { [:INCLUDES, st(text)] }
99
+
100
+ when (text = @ss.scan(/[\s]*\|=[\s]*/i))
101
+ action { [:DASHMATCH, st(text)] }
102
+
103
+ when (text = @ss.scan(/[\s]*\^=[\s]*/i))
104
+ action { [:PREFIXMATCH, st(text)] }
105
+
106
+ when (text = @ss.scan(/[\s]*\$=[\s]*/i))
107
+ action { [:SUFFIXMATCH, st(text)] }
108
+
109
+ when (text = @ss.scan(/[\s]*\*=[\s]*/i))
110
+ action { [:SUBSTRINGMATCH, st(text)] }
111
+
112
+ when (text = @ss.scan(/[\s]*!=[\s]*/i))
113
+ action { [:NOT_EQUAL, st(text)] }
114
+
115
+ when (text = @ss.scan(/[\s]*=[\s]*/i))
116
+ action { [:EQUAL, st(text)] }
117
+
118
+ when (text = @ss.scan(/[\s]*\)/i))
119
+ action { [:RPAREN, st(text)] }
120
+
121
+ when (text = @ss.scan(/[\s]*\[[\s]*/i))
122
+ action { [:LSQUARE, st(text)] }
123
+
124
+ when (text = @ss.scan(/[\s]*\]/i))
125
+ action { [:RSQUARE, st(text)] }
126
+
127
+ when (text = @ss.scan(/[\s]*\+[\s]*/i))
128
+ action { [:PLUS, st(text)] }
129
+
130
+ when (text = @ss.scan(/[\s]*\{[\s]*/i))
131
+ action { [:LBRACE, st(text)] }
132
+
133
+ when (text = @ss.scan(/[\s]*\}[\s]*/i))
134
+ action { [:RBRACE, st(text)] }
135
+
136
+ when (text = @ss.scan(/[\s]*>[\s]*/i))
137
+ action { [:GREATER, st(text)] }
138
+
139
+ when (text = @ss.scan(/[\s]*,[\s]*/i))
140
+ action { [:COMMA, st(',')] }
141
+
142
+ when (text = @ss.scan(/[\s]*;[\s]*/i))
143
+ action { [:SEMI, st(';')] }
144
+
145
+ when (text = @ss.scan(/\*/i))
146
+ action { [:STAR, st(text)] }
147
+
148
+ when (text = @ss.scan(/[\s]*~[\s]*/i))
149
+ action { [:TILDE, st(text)] }
150
+
151
+ when (text = @ss.scan(/\:not\([\s]*/i))
152
+ action { [:NOT, st(text)] }
153
+
154
+ when (text = @ss.scan(/[\s]*([0-9]*\.[0-9]+|[0-9]+)em[\s]*/i))
155
+ action { [:EMS, st(text)] }
156
+
157
+ when (text = @ss.scan(/[\s]*([0-9]*\.[0-9]+|[0-9]+)ex[\s]*/i))
158
+ action { [:EXS, st(text)] }
159
+
160
+ when (text = @ss.scan(/[\s]*([0-9]*\.[0-9]+|[0-9]+)(px|cm|mm|in|pt|pc)[\s]*/i))
161
+ action { [:LENGTH, st(text)] }
162
+
163
+ when (text = @ss.scan(/[\s]*([0-9]*\.[0-9]+|[0-9]+)(deg|rad|grad)[\s]*/i))
164
+ action { [:ANGLE, st(text)] }
165
+
166
+ when (text = @ss.scan(/[\s]*([0-9]*\.[0-9]+|[0-9]+)(ms|s)[\s]*/i))
167
+ action { [:TIME, st(text)] }
168
+
169
+ when (text = @ss.scan(/[\s]*([0-9]*\.[0-9]+|[0-9]+)[k]?hz[\s]*/i))
170
+ action { [:FREQ, st(text)] }
171
+
172
+ when (text = @ss.scan(/[\s]*([0-9]*\.[0-9]+|[0-9]+)%[\s]*/i))
173
+ action { [:PERCENTAGE, st(text)] }
174
+
175
+ when (text = @ss.scan(/[\s]*([0-9]*\.[0-9]+|[0-9]+)[\s]*/i))
176
+ action { [:NUMBER, st(text)] }
177
+
178
+ when (text = @ss.scan(/[\s]*\/\/[\s]*/i))
179
+ action { [:DOUBLESLASH, st(text)] }
180
+
181
+ when (text = @ss.scan(/[\s]*\/[\s]*/i))
182
+ action { [:SLASH, st('/')] }
183
+
184
+ when (text = @ss.scan(/<!--/i))
185
+ action { [:CDO, st(text)] }
186
+
187
+ when (text = @ss.scan(/-->/i))
188
+ action { [:CDC, st(text)] }
189
+
190
+ when (text = @ss.scan(/[\s]*\-(?![-@]?([_A-Za-z]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])([_A-Za-z0-9-]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*)[\s]*/i))
191
+ action { [:MINUS, st(text)] }
192
+
193
+ when (text = @ss.scan(/[\s]*\+[\s]*/i))
194
+ action { [:PLUS, st(text)] }
195
+
196
+ when (text = @ss.scan(/[\s]+/i))
197
+ action { [:S, st(text)] }
198
+
199
+ when (text = @ss.scan(/("([^\n\r\f\\"]|\\\n|\r\n|\r|\f|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*"|'([^\n\r\f\\']|\\\n|\r\n|\r|\f|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*')/i))
200
+ action { [:STRING, st(text)] }
201
+
202
+ when (text = @ss.scan(/("([^\n\r\f\\"]|\\\n|\r\n|\r|\f|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*|'([^\n\r\f\\']|\\\n|\r\n|\r|\f|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*)/i))
203
+ action { [:INVALID, st(text)] }
204
+
205
+ when (text = @ss.scan(/./i))
206
+ action { [st(text), st(text)] }
207
+
208
+ else
209
+ text = @ss.string[@ss.pos .. -1]
210
+ raise ScanError, "can not match: '" + text + "'"
211
+ end # if
212
+
213
+ else
214
+ raise ScanError, "undefined state: '" + state.to_s + "'"
215
+ end # case state
216
+ token
217
+ end # def _next_token
218
+
219
+ def st o
220
+ @st ||= Hash.new { |h,k| h[k] = k }
221
+ @st[o]
222
+ end # class
223
+
224
+ end
225
+ end
226
+ end
227
+
228
+ # vim: syntax=lex
@@ -0,0 +1,96 @@
1
+ module CSSPool
2
+ module CSS
3
+ class Tokenizer < Parser
4
+
5
+ macro
6
+ nl \n|\r\n|\r|\f
7
+ w [\s]*
8
+ nonascii [^\0-\177]
9
+ num ([0-9]*\.[0-9]+|[0-9]+)
10
+ unicode \\[0-9A-Fa-f]{1,6}(\r\n|[\s])?
11
+
12
+ escape {unicode}|\\[^\n\r\f0-9A-Fa-f]
13
+ nmchar [_A-Za-z0-9-]|{nonascii}|{escape}
14
+ nmstart [_A-Za-z]|{nonascii}|{escape}
15
+ ident [-@]?({nmstart})({nmchar})*
16
+ func [-@]?({nmstart})({nmchar}|[.])*
17
+ name ({nmchar})+
18
+ string1 "([^\n\r\f\\"]|\\{nl}|{nonascii}|{escape})*"
19
+ string2 '([^\n\r\f\\']|\\{nl}|{nonascii}|{escape})*'
20
+ string ({string1}|{string2})
21
+ invalid1 "([^\n\r\f\\"]|\\{nl}|{nonascii}|{escape})*
22
+ invalid2 '([^\n\r\f\\']|\\{nl}|{nonascii}|{escape})*
23
+ invalid ({invalid1}|{invalid2})
24
+ comment \/\*(.|{w})*?\*\/
25
+
26
+ rule
27
+
28
+ # [:state] pattern [actions]
29
+
30
+ url\({w}{string}{w}\) { [:URI, st(text)] }
31
+ url\({w}([!#\$%&*-~]|{nonascii}|{escape})*{w}\) { [:URI, st(text)] }
32
+ U\+[0-9a-fA-F?]{1,6}(-[0-9a-fA-F]{1,6})? {[:UNICODE_RANGE, st(text)] }
33
+ {w}{comment}{w} { next_token }
34
+
35
+ {func}\(\s* { [:FUNCTION, st(text)] }
36
+ {w}@import{w} { [:IMPORT_SYM, st(text)] }
37
+ {w}@page{w} { [:PAGE_SYM, st(text)] }
38
+ {w}@charset{w} { [:CHARSET_SYM, st(text)] }
39
+ {w}@media{w} { [:MEDIA_SYM, st(text)] }
40
+ {w}!({w}|{w}{comment}{w})important{w} { [:IMPORTANT_SYM, st(text)] }
41
+ {ident} { [:IDENT, st(text)] }
42
+ \#{name} { [:HASH, st(text)] }
43
+ {w}~={w} { [:INCLUDES, st(text)] }
44
+ {w}\|={w} { [:DASHMATCH, st(text)] }
45
+ {w}\^={w} { [:PREFIXMATCH, st(text)] }
46
+ {w}\$={w} { [:SUFFIXMATCH, st(text)] }
47
+ {w}\*={w} { [:SUBSTRINGMATCH, st(text)] }
48
+ {w}!={w} { [:NOT_EQUAL, st(text)] }
49
+ {w}={w} { [:EQUAL, st(text)] }
50
+ {w}\) { [:RPAREN, st(text)] }
51
+ {w}\[{w} { [:LSQUARE, st(text)] }
52
+ {w}\] { [:RSQUARE, st(text)] }
53
+ {w}\+{w} { [:PLUS, st(text)] }
54
+ {w}\{{w} { [:LBRACE, st(text)] }
55
+ {w}\}{w} { [:RBRACE, st(text)] }
56
+ {w}>{w} { [:GREATER, st(text)] }
57
+ {w},{w} { [:COMMA, st(',')] }
58
+ {w};{w} { [:SEMI, st(';')] }
59
+ \* { [:STAR, st(text)] }
60
+ {w}~{w} { [:TILDE, st(text)] }
61
+ \:not\({w} { [:NOT, st(text)] }
62
+ {w}{num}em{w} { [:EMS, st(text)] }
63
+ {w}{num}ex{w} { [:EXS, st(text)] }
64
+
65
+ {w}{num}(px|cm|mm|in|pt|pc){w} { [:LENGTH, st(text)] }
66
+ {w}{num}(deg|rad|grad){w} { [:ANGLE, st(text)] }
67
+ {w}{num}(ms|s){w} { [:TIME, st(text)] }
68
+ {w}{num}[k]?hz{w} { [:FREQ, st(text)] }
69
+
70
+ {w}{num}%{w} { [:PERCENTAGE, st(text)] }
71
+ {w}{num}{w} { [:NUMBER, st(text)] }
72
+ {w}\/\/{w} { [:DOUBLESLASH, st(text)] }
73
+ {w}\/{w} { [:SLASH, st('/')] }
74
+ <!-- { [:CDO, st(text)] }
75
+ --> { [:CDC, st(text)] }
76
+ {w}\-(?!{ident}){w} { [:MINUS, st(text)] }
77
+ {w}\+{w} { [:PLUS, st(text)] }
78
+
79
+
80
+ [\s]+ { [:S, st(text)] }
81
+ {string} { [:STRING, st(text)] }
82
+ {invalid} { [:INVALID, st(text)] }
83
+ . { [st(text), st(text)] }
84
+
85
+ inner
86
+
87
+ def st o
88
+ @st ||= Hash.new { |h,k| h[k] = k }
89
+ @st[o]
90
+ end
91
+
92
+ end
93
+ end
94
+ end
95
+
96
+ # vim: syntax=lex
data/lib/csspool/css.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'csspool/css/tokenizer'
1
2
  require 'csspool/css/charset'
2
3
  require 'csspool/css/import_rule'
3
4
  require 'csspool/css/media'