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

Documentation of CSS Preprocessor.

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.