Current Version: 1.0.33
Project Name: csspp
expr_list.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
26#include "csspp/expression.h"
27
28#include "csspp/exception.h"
29#include "csspp/parser.h"
30#include "csspp/unicode_range.h"
31
32#include <algorithm>
33#include <cmath>
34#include <iostream>
35
36namespace csspp
37{
38
40{
41 // we have a label if we have:
42 // <identifier> <ws>* ':'
44 || f_pos >= f_node->size())
45 {
46 return false;
47 }
48
49 return f_node->get_child(f_pos)->is(node_type_t::COLON);
50}
51
53{
54 // expression-list: array
55 // | map
56 //
57 // array: assignment
58 // | array ',' assignment
59 //
60 // map: IDENTIFIER ':' assignment
61 // | IDENTIFIER ':'
62 // | map ',' IDENTIFIER ':' assignment
63 // | map ',' IDENTIFIER ':'
64 //
65
66 if(is_label())
67 {
68 node::pointer_t map(new node(node_type_t::MAP, f_current->get_position()));
69 bool found_end(false);
70 while(!found_end && is_label())
71 {
73
74 // skip the IDENTIFIER (f_current == ':')
75 next();
76
77 // skip the ':'
78 next();
79
80 node::pointer_t result;
82 {
83 // empty entries are viewed as valid and set to NULL
84 // (see below for the NULL_TOKEN allocation)
85
86 // skip the ','
87 next();
88 }
90 {
91 // map ends with just a label, make sure we add a NULL too
92 result.reset();
93 }
94 else
95 {
96 result = assignment();
97
99 {
100 next();
101 }
102 else
103 {
104 // no comma, we must have reached the end of the list
105 found_end = true;
106 }
107 }
108
109 if(!result)
110 {
111 // maps need to have an even number of entries, but
112 // the value of an entry does not need to be provided
113 // in which case we want to put NULL in there
114 result.reset(new node(node_type_t::NULL_TOKEN, f_current->get_position()));
115 }
116
117 // add both at the same time
118 map->add_child(name);
119 map->add_child(result);
120 }
121 return map;
122 }
123 else
124 {
125 node::pointer_t result(assignment());
126
127 if(result
129 {
130 // in CSS Preprocessor, a list of expressions is an ARRAY
131 // (contrary to C/C++ which just return the last expression)
132 node::pointer_t array(new node(node_type_t::ARRAY, f_current->get_position()));
133 array->add_child(result);
134
135 while(f_current->is(node_type_t::COMMA))
136 {
137 // skip the ','
138 next();
139
140 result = assignment();
141 if(!result)
142 {
143 break;
144 }
145 array->add_child(result);
146 }
147
148 // the result is the array in this case
149 return array;
150 }
151
152 return result;
153 }
154}
155
157{
158 // assignment: conditional
159 // | IDENTIFIER ':=' conditional
160
162 if(!result)
163 {
164 return node::pointer_t();
165 }
166
167 if(result->is(node_type_t::IDENTIFIER)
169 {
170 next();
171
173 f_variables[result->get_string()] = value;
174
175 // the return value of an assignment is the value of
176 // the variable
177 result = value;
178 }
179
180 return result;
181}
182
184{
185 // post: unary
186 // | post '[' expression ']'
187 // | post '.' IDENTIFIER
188
189 // TODO: add support to access color members (i.e. $c.red <=> red($c))
190
191 node::pointer_t result(unary());
192 if(!result)
193 {
194 return node::pointer_t();
195 }
196
197 node::pointer_t index;
198 for(;;)
199 {
201 {
202 // compile the index expression
203 expression index_expr(f_current);
205 index_expr.next();
206 node::pointer_t i(index_expr.expression_list());
207 if(!i)
208 {
209 return node::pointer_t();
210 }
211
212 // skip the '['
213 next();
214
215 if(i->is(node_type_t::INTEGER))
216 {
217 if(result->is(node_type_t::ARRAY)
218 || result->is(node_type_t::LIST))
219 {
220 // index is 1 based (not like in C/C++)
221 integer_t idx(i->get_integer());
222 if(idx < 0)
223 {
224 // negative numbers get items from the item
225 idx = result->size() + idx;
226 }
227 else
228 {
229 --idx;
230 }
231 if(static_cast<size_t>(idx) >= result->size())
232 {
233 error::instance() << f_current->get_position()
234 << "index "
235 << i->get_integer()
236 << " is out of range. The allowed range is 1 to "
237 << static_cast<int>(result->size())
238 << "."
240 return node::pointer_t();
241 }
242 result = result->get_child(idx);
243 }
244 else if(result->is(node_type_t::MAP))
245 {
246 // index is 1 based (not like in C/C++)
247 // maps are defined as <property name> ':' <property value>
248 // so the numeric index being used to access the property
249 // value it has to be x 2 + 1 (C index: 1, 3, 5...)
250 // if negative we first have to "invert" the index
251 integer_t idx(i->get_integer());
252 if(idx < 0)
253 {
254 // negative numbers get items from the item
255 idx = result->size() / 2 + idx;
256 }
257 else
258 {
259 --idx;
260 }
261 idx = idx * 2 + 1;
262 if(static_cast<size_t>(idx) >= result->size())
263 {
264 error::instance() << f_current->get_position()
265 << "index "
266 << i->get_integer()
267 << " is out of range. The allowed range is 1 to "
268 << static_cast<int>(result->size()) / 2
269 << "."
271 return node::pointer_t();
272 }
273 result = result->get_child(idx);
274 }
275 else
276 {
277 error::instance() << f_current->get_position()
278 << "unsupported type "
279 << result->get_type()
280 << " for the 'array[<index>]' operation."
282 return node::pointer_t();
283 }
284 }
285 else if(i->is(node_type_t::STRING)
286 || i->is(node_type_t::IDENTIFIER))
287 {
288 // nothing more to skip, the string is a child in
289 // a separate list
290 index = i;
291 goto field_index;
292 }
293 else
294 {
295 error::instance() << f_current->get_position()
296 << "an integer, an identifier, or a string was expected as the index (defined in '[ ... ]'). A "
297 << i->get_type()
298 << " was not expected."
300 return node::pointer_t();
301 }
302 }
303 else if(f_current->is(node_type_t::PERIOD))
304 {
305 // skip the '.'
306 next();
307
309 {
310 error::instance() << f_current->get_position()
311 << "only an identifier is expected after a '.'."
313 return node::pointer_t();
314 }
315 index = f_current;
316
317 // skip the index (identifier)
318 next();
319
320field_index:
321 if(result->is(node_type_t::MAP))
322 {
323 // in this case the index is a string or an identifier
324 std::string const idx(index->get_string());
325 size_t const max_item(result->size());
326 if((max_item & 1) != 0)
327 {
328 throw csspp_exception_logic("expression.cpp:expression::post(): number of items in a map has to be even."); // LCOV_EXCL_LINE
329 }
330 bool found(false);
331 for(size_t j(0); j < max_item; j += 2)
332 {
333 node::pointer_t item_name(result->get_child(j));
334 if(!item_name->is(node_type_t::IDENTIFIER))
335 {
336 throw csspp_exception_logic("expression.cpp:expression::post(): a map has the name of an entry which is not an identifier."); // LCOV_EXCL_LINE
337 }
338 if(item_name->get_string() == idx)
339 {
340 result = result->get_child(j + 1);
341 found = true;
342 break;
343 }
344 }
345 if(!found)
346 {
347 // TBD: should this be acceptable and we return NULL instead?
348 error::instance() << f_current->get_position()
349 << "'map[\""
350 << idx
351 << "\"]' is not set."
353 return node::pointer_t();
354 }
355 }
356 else
357 {
358 error::instance() << f_current->get_position()
359 << "unsupported left handside type "
360 << result->get_type()
361 << " for the '<map>.<identifier>' operation."
363 return node::pointer_t();
364 }
365 }
366 else
367 {
368 break;
369 }
370 }
371
372 return result;
373}
374
375} // namespace csspp
376
377// Local Variables:
378// mode: cpp
379// indent-tabs-mode: nil
380// c-basic-offset: 4
381// tab-width: 4
382// End:
383
384// vim: ts=4 sw=4 et
static error & instance()
Definition error.cpp:77
node::pointer_t conditional()
bool is_label() const
Definition expr_list.cpp:39
node::pointer_t f_current
Definition expression.h:155
node::pointer_t expression_list()
Definition expr_list.cpp:52
expression_variables_interface * f_variable_handler
Definition expression.h:158
void set_variable_handler(expression_variables_interface *handler)
node::pointer_t assignment()
node::pointer_t unary()
node::pointer_t post()
variable_vector_t f_variables
Definition expression.h:156
node::pointer_t f_node
Definition expression.h:152
std::shared_ptr< node > pointer_t
Definition node.h:132
The namespace of all the classes in the CSS Preprocessor.
Definition csspp.h:48
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.