Current Version: 1.0.10
Project Name: csspp
nth_child.cpp
Go to the documentation of this file.
1 // CSS Preprocessor
2 // Copyright (C) 2015-2016 Made to Order Software Corp.
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 
48 #include "csspp/nth_child.h"
49 
50 #include <cstdio>
51 #include <iostream>
52 
53 namespace csspp
54 {
55 
56 namespace
57 {
58 
59 int const g_shift_a = 0;
60 int const g_shift_b = 32;
61 
62 } // no name namespace
63 
65  : f_a(static_cast<repeat_integer_t>(an_plus_b >> g_shift_a))
66  , f_b(static_cast<repeat_integer_t>(an_plus_b >> g_shift_b))
67 {
68 }
69 
71  : f_a(a)
72  , f_b(b)
73 {
74 }
75 
76 // got an error if "get_error().empty()" is false
77 nth_child::nth_child(std::string const & an_plus_b)
78 {
79  parse(an_plus_b);
80 }
81 
83 {
84  f_a = a;
85 }
86 
88 {
89  f_b = b;
90 }
91 
93 {
94  return f_a;
95 }
96 
98 {
99  return f_b;
100 }
101 
103 {
104  return ((static_cast<integer_t>(f_a) & 0xFFFFFFFF) << g_shift_a) | ((static_cast<integer_t>(f_b) & 0xFFFFFFFF) << g_shift_b);
105 }
106 
107 std::string nth_child::get_error() const
108 {
109  return f_error;
110 }
111 
112 bool nth_child::parse(std::string const & an_plus_b)
113 {
114  class in_t
115  {
116  public:
117  enum class token_t
118  {
119  EOF_TOKEN,
120  ODD,
121  EVEN,
122  INTEGER,
123  PLUS,
124  MINUS,
125  N,
126  UNKNOWN
127  };
128 
129  in_t(std::string in)
130  : f_in(in)
131  {
132  }
133 
134  wide_char_t getc()
135  {
136  if(f_unget != '\0')
137  {
138  wide_char_t const c(f_unget);
139  f_unget = '\0';
140  return c;
141  }
142 
143  if(f_pos < f_in.length())
144  {
145  // TBD: as far as I know, we have no need to test
146  // whether we have UTF-8 characters in this
147  // case
148  wide_char_t const c(f_in[f_pos]);
149  ++f_pos;
150  return c;
151  }
152  return EOF;
153  }
154 
155  void ungetc(wide_char_t c)
156  {
157  f_unget = c;
158  }
159 
160  token_t token(integer_t & value)
161  {
162  value = 0;
163 
164  // read the next non-space character
165  wide_char_t c;
166  do
167  {
168  c = getc();
169  }
170  while(c == ' ');
171 
172  switch(c)
173  {
174  case EOF:
175  return token_t::EOF_TOKEN;
176 
177  case '0':
178  case '1':
179  case '2':
180  case '3':
181  case '4':
182  case '5':
183  case '6':
184  case '7':
185  case '8':
186  case '9':
187  for(;;)
188  {
189  value = value * 10 + c - '0';
190  c = getc();
191  if(c < '0' || c > '9')
192  {
193  ungetc(c);
194  return token_t::INTEGER;
195  }
196  }
197  /*NOTREACHED*/
198 
199  case 'n':
200  case 'N':
201  return token_t::N;
202 
203  case 'e':
204  case 'E':
205  c = getc();
206  if(c == 'v' || c == 'V')
207  {
208  c = getc();
209  if(c == 'e' || c == 'E')
210  {
211  c = getc();
212  if(c == 'n' || c == 'N')
213  {
214  return token_t::EVEN;
215  }
216  }
217  }
218  break;
219 
220  case 'o':
221  case 'O':
222  c = getc();
223  if(c == 'd' || c == 'D')
224  {
225  c = getc();
226  if(c == 'd' || c == 'D')
227  {
228  return token_t::ODD;
229  }
230  }
231  break;
232 
233  case '+':
234  return token_t::PLUS;
235 
236  case '-':
237  return token_t::MINUS;
238 
239  }
240 
241  return token_t::UNKNOWN;
242  }
243 
244  private:
245  std::string f_in;
246  size_t f_pos = 0;
247  wide_char_t f_unget = '\0';
248  };
249 
250  f_error = "";
251 
252  in_t in(an_plus_b);
253 
254  integer_t first_int(0);
255  in_t::token_t token(in.token(first_int));
256 
257  // EVEN
258  if(token == in_t::token_t::EVEN)
259  {
260  f_a = 2;
261  f_b = 0;
262  if(in.token(first_int) == in_t::token_t::EOF_TOKEN)
263  {
264  return true;
265  }
266  f_error = "'even' cannot be followed by anything else in an An+B expression.";
267  return false;
268  }
269 
270  // ODD
271  if(token == in_t::token_t::ODD)
272  {
273  f_a = 2;
274  f_b = 1;
275  if(in.token(first_int) == in_t::token_t::EOF_TOKEN)
276  {
277  return true;
278  }
279  f_error = "'odd' cannot be followed by anything else in an An+B expression.";
280  return false;
281  }
282 
283  // optional sign (+ or -)
284  int first_sign(1);
285  if(token == in_t::token_t::PLUS)
286  {
287  token = in.token(first_int);
288  }
289  else if(token == in_t::token_t::MINUS)
290  {
291  token = in.token(first_int);
292  first_sign = -1;
293  }
294 
295  // in this case we must have an integer
296  if(token != in_t::token_t::INTEGER)
297  {
298  f_error = "In an An+B expression, we expect an optional signed followed by a number or 'even' or 'odd'.";
299  return false;
300  }
301 
302  integer_t second_int(0);
303  token = in.token(second_int);
304  if(token == in_t::token_t::EOF_TOKEN)
305  {
306  // just a number, this is 'B' by itself
307  f_a = 0;
308  f_b = first_int * first_sign;
309  return true;
310  }
311 
312  if(token != in_t::token_t::N)
313  {
314  // first number must be followed by 'n'
315  f_error = "The first number has to be followed by the 'n' character.";
316  return false;
317  }
318 
319  int second_sign(1);
320  token = in.token(second_int);
321  if(token == in_t::token_t::PLUS)
322  {
323  // ...
324  }
325  else if(token == in_t::token_t::MINUS)
326  {
327  second_sign = -1;
328  }
329  else
330  {
331  // the sign is mandatory between An and B
332  f_error = "A sign (+ or -) is expected between the 'An' and the 'B' parts in 'An+B'.";
333  return false;
334  }
335 
336  token = in.token(second_int);
337  if(token != in_t::token_t::INTEGER)
338  {
339  // B has to be an integer
340  f_error = "The value B must be a valid integer in 'An+B'.";
341  return false;
342  }
343 
344  f_a = first_int * first_sign;
345  f_b = second_int * second_sign;
346 
347  if(in.token(second_int) == in_t::token_t::EOF_TOKEN)
348  {
349  return true;
350  }
351 
352  f_error = "An 'An+B' expression cannot be followed by anything else.";
353  return false;
354 }
355 
356 std::string nth_child::to_string() const
357 {
358  // TODO: add code to reduce these because B should always be between 0 and A - 1
359  // and A can always be positive (but I need to do some testing.)
360  if(f_a == 2 && f_b == 0)
361  {
362  // 2n+0 or "2n", which is shorter than "even"
363  return "2n";
364  }
365  if(f_a == 2 && f_b == 1)
366  {
367  // 2n+1 or "odd"
368  return "odd";
369  }
370 
371  // we return f_b in priority because of a and b are zero "0"
372  // is shorter than "0n"
373  if(f_a == 0)
374  {
375  return std::to_string(f_b);
376  }
377 
378  if(f_b == 0)
379  {
380  return std::to_string(f_a) + "n";
381  }
382 
383  return std::to_string(f_a) + "n" + (f_b >= 0 ? "+" : "") + std::to_string(f_b);
384 }
385 
386 } // namespace csspp
387 
388 // Local Variables:
389 // mode: cpp
390 // indent-tabs-mode: nil
391 // c-basic-offset: 4
392 // tab-width: 4
393 // End:
394 
395 // vim: ts=4 sw=4 et
void set_b(repeat_integer_t b)
Definition: nth_child.cpp:87
repeat_integer_t f_b
Definition: nth_child.h:48
int32_t wide_char_t
Definition: csspp.h:49
nth_child(integer_t an_plus_b=1)
Definition: nth_child.cpp:64
repeat_integer_t f_a
Definition: nth_child.h:47
int64_t integer_t
Definition: csspp.h:52
integer_t get_nth() const
Definition: nth_child.cpp:102
bool parse(std::string const &an_plus_b)
Definition: nth_child.cpp:112
repeat_integer_t get_a() const
Definition: nth_child.cpp:92
std::string f_error
Definition: nth_child.h:46
std::string to_string() const
Definition: nth_child.cpp:356
int32_t repeat_integer_t
Definition: nth_child.h:25
std::string get_error() const
Definition: nth_child.cpp:107
repeat_integer_t get_b() const
Definition: nth_child.cpp:97
void set_a(repeat_integer_t a)
Definition: nth_child.cpp:82

Documentation of CSS Preprocessor.

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.