Module: Caps::Parser::Consumers

Included in:
Caps::Parser
Defined in:
lib/caps/parser/consumers.rb

Instance Method Summary collapse

Instance Method Details

#consume_at_ruleObject



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/caps/parser/consumers.rb', line 35

def consume_at_rule
  obj = {
    type: :at_rule,
    name: advance,
    prelude: [],
    value: nil
  }

  loop do
    p = peek
    case
    when p.semicolon? || p.eof?
      advance
      return obj

    when p.left_curly?
      obj[:block] = consume_simple_block
      return obj

    else
      obj[:prelude] << consume_component_value
    end
  end
end

#consume_component_valueObject



200
201
202
203
204
205
206
207
208
# File 'lib/caps/parser/consumers.rb', line 200

def consume_component_value
  if peek.left_curly? || peek.left_square? || peek.left_parens?
    consume_simple_block
  elsif peek.function?
    consume_function
  else
    advance
  end
end

#consume_declarationObject



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/caps/parser/consumers.rb', line 169

def consume_declaration
  obj = {
    type: :declaration,
    name: advance,
    value: [],
    important: false
  }

  consume_whitespace

  unless peek.colon?
    resync_invalid_declaration
    return nil
  end

  advance # consume colon

  consume_whitespace

  obj[:value] << consume_component_value until peek.eof? || peek.semicolon?

  if contains_important_annotation?(obj[:value])
    obj[:important] = true
    obj[:value] = remove_important_annotation(obj[:value])
  end

  obj[:value].pop while obj[:value].last.whitespace?

  obj
end

#consume_declaration_listObject



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/caps/parser/consumers.rb', line 118

def consume_declaration_list
  decls = []

  loop do
    p = peek

    case
    when p.whitespace? || p.semicolon?
      advance

    when p.eof?
      return decls

    when p.at_keyword?
      decls << consume_at_rule

    when p.ident?
      tmp = [advance]
      tmp << consume_component_value while !peek.semicolon? && !peek.eof?
      tmp_parser = Parser.new(tmp)
      tmp = tmp_parser.consume_declaration
      decls << tmp unless tmp.nil?

    else
      # Parse error. Throw away until semicolon or eof
      consume_component_value while !peek.semicolon? || semi.eof?
    end
  end
end

#consume_functionObject



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/caps/parser/consumers.rb', line 249

def consume_function
  obj = {
    type: :function,
    name: advance,
    value: []
  }

  loop do
    p = peek
    if p.eof?
      # Parse error
      break
    elsif p.right_parens?
      advance
      break
    else
      obj[:value] << consume_component_value
    end
  end

  obj
end

#consume_list_of_rules(top_level: false) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/caps/parser/consumers.rb', line 8

def consume_list_of_rules(top_level: false)
  list = []

  until peek.eof?
    p = peek
    if p.whitespace?
      advance

    elsif (p.cdo? || p.cdc?) && top_level
      advance

    elsif p.cdo? || p.cdc?
      tmp = consume_qualified_rule
      list << tmp unless tmp.nil?

    elsif p.at_keyword?
      list << consume_at_rule

    else
      tmp = consume_qualified_rule
      list << tmp unless tmp.nil?
    end
  end

  list
end

#consume_qualified_ruleObject



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/caps/parser/consumers.rb', line 60

def consume_qualified_rule
  obj = {
    type: :qualified_rule,
    prelude: [],
    value: nil
  }

  loop do
    p = peek
    case
    when p.eof?
      return nil

    when p.left_curly?
      obj[:block] = consume_simple_block
      return obj

    else
      obj[:prelude] << consume_component_value
    end
  end
end

#consume_simple_blockObject



224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/caps/parser/consumers.rb', line 224

def consume_simple_block
  block = {
    type: :simple_block,
    associated_token: advance,
    value: []
  }

  mirror_variant = mirror_variant_for(block[:associated_token])

  loop do
    p = peek
    if p.type == mirror_variant
      advance
      break
    elsif p.eof?
      # parse error
      break
    else
      block[:value] << consume_component_value
    end
  end

  block
end

#consume_style_block_contentsObject



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/caps/parser/consumers.rb', line 83

def consume_style_block_contents
  decls = []
  rules = []

  loop do
    p = peek

    case
    when p.whitespace? || p.semicolon?
      advance

    when p.eof?
      return decls + rules

    when p.at_keyword?
      rules << consume_at_rule

    when p.ident?
      tmp = [advance]
      tmp << consume_component_value while !peek.semicolon? && !peek.eof?
      tmp_parser = Parser.new(tmp)
      tmp = tmp_parser.consume_declaration
      decls << tmp unless tmp.nil?

    when p.delim? && p.value == "&"
      tmp = consume_qualified_rule
      rules << tmp unless tmp.nil?

    else
      # Parse error. Throw away until semicolon or eof
      consume_component_value while !peek.semicolon? && !peek.eof?
    end
  end
end

#contains_important_annotation?(list) ⇒ Boolean

Returns:

  • (Boolean)


152
153
154
155
156
157
158
159
# File 'lib/caps/parser/consumers.rb', line 152

def contains_important_annotation?(list)
  last_two = list.reject(&:whitespace?).slice(-2, 2)
  return false unless last_two

  last_two.map(&:type) == %i[delim ident] &&
    last_two.first.value == "!" &&
    last_two.last.value.casecmp?("important")
end

#mirror_variant_for(obj) ⇒ Object



210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/caps/parser/consumers.rb', line 210

def mirror_variant_for(obj)
  case obj.type
  when :left_curly
    :right_curly
  when :left_parens
    :right_parens
  when :left_square
    :right_square
  else
    byebug
    raise "BUG: No mirror variant for #{obj.type}!"
  end
end

#remove_important_annotation(list) ⇒ Object



161
162
163
164
165
166
167
# File 'lib/caps/parser/consumers.rb', line 161

def remove_important_annotation(list)
  idx = list.reverse.index do |i|
    i.type == :ident && i.value.casecmp?("important")
  end

  list.slice(0, list.length - idx - 2)
end

#resync_invalid_declarationObject



148
149
150
# File 'lib/caps/parser/consumers.rb', line 148

def resync_invalid_declaration
  advance until peek.eof? || peek.semicolon?
end