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

Documentation of CSS Preprocessor.

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.