Current Version: 1.0.10
Project Name: csspp
expr_power.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  node::pointer_t result;
43 
44  if(rhs->is(node_type_t::INTEGER)
45  || rhs->is(node_type_t::DECIMAL_NUMBER))
46  {
47  if(rhs->get_string() != "")
48  {
49  error::instance() << f_current->get_position()
50  << "the number representing the power cannot be a dimension ("
51  << rhs->get_string()
52  << "); it has to be unitless."
54  return node::pointer_t();
55  }
56  }
57 
58  bool check_dimension(false);
59  switch(mix_node_types(lhs->get_type(), rhs->get_type()))
60  {
62  result.reset(new node(node_type_t::INTEGER, lhs->get_position()));
63  result->set_integer(static_cast<integer_t>(pow(lhs->get_integer(), rhs->get_integer())));
64  check_dimension = true;
65  break;
66 
68  result.reset(new node(node_type_t::DECIMAL_NUMBER, lhs->get_position()));
69  result->set_decimal_number(pow(lhs->get_integer(), rhs->get_decimal_number()));
70  check_dimension = true;
71  break;
72 
74  result.reset(new node(node_type_t::DECIMAL_NUMBER, lhs->get_position()));
75  result->set_decimal_number(pow(lhs->get_decimal_number(), rhs->get_integer()));
76  check_dimension = true;
77  break;
78 
80  result.reset(new node(node_type_t::DECIMAL_NUMBER, lhs->get_position()));
81  result->set_decimal_number(pow(lhs->get_decimal_number(), rhs->get_decimal_number()));
82  check_dimension = true;
83  break;
84 
86  result.reset(new node(node_type_t::PERCENT, lhs->get_position()));
87  result->set_decimal_number(pow(lhs->get_decimal_number(), rhs->get_integer()));
88  break;
89 
91  result.reset(new node(node_type_t::PERCENT, lhs->get_position()));
92  result->set_decimal_number(pow(lhs->get_decimal_number(), rhs->get_decimal_number()));
93  break;
94 
95  default:
96  error::instance() << f_current->get_position()
97  << "incompatible types between "
98  << lhs->get_type()
99  << " and "
100  << rhs->get_type()
101  << " for operator '**'."
103  return node::pointer_t();
104 
105  }
106 
107  if(check_dimension
108  && lhs->get_string() != "")
109  {
110  integer_t the_power(0);
111  if(rhs->is(node_type_t::INTEGER))
112  {
113  // integers are fine if > 0
114  the_power = rhs->get_integer();
115  }
116  else
117  {
118  decimal_number_t p(rhs->get_decimal_number());
119  decimal_number_t integral_part(0.0);
120  decimal_number_t fractional_part(modf(p, &integral_part));
121 #pragma GCC diagnostic push
122 #pragma GCC diagnostic ignored "-Wfloat-equal"
123  if(fractional_part != 0.0)
124 #pragma GCC diagnostic pop
125  {
126  // the fractional part has to be exactly 0.0 otherwise we
127  // cannot determine the new dimension
128  error::instance() << f_current->get_position()
129  << "a number with a dimension only supports integers as their power (i.e. 3px ** 2 is fine, 3px ** 2.1 is not supported)."
131  return node::pointer_t();
132  }
133  the_power = static_cast<integer_t>(integral_part);
134  }
135  if(the_power == 0)
136  {
137  error::instance() << f_current->get_position()
138  << "a number with a dimension power zero cannot be calculated (i.e. 3px ** 0 = 1 what?)."
140  return node::pointer_t();
141  }
142  // impose a limit because otherwise we may have a bit of a memory
143  // problem...
144  if(labs(the_power) > 100)
145  {
146  error::instance() << f_current->get_position()
147  << "a number with a dimension power 101 or more would generate a very large string so we refuse it at this time. You may use unitless numbers instead."
149  return node::pointer_t();
150  }
151 
152  // calculate the new dimension, if power is negative, make sure
153  // to swap the existing dimension (i.e. px / em -> em / px)
154  dimension_vector_t org_dividend;
155  dimension_vector_t org_divisor;
156  dimension_vector_t dividend;
157  dimension_vector_t divisor;
158 
159  if(the_power >= 0)
160  {
161  dimensions_to_vectors(lhs->get_position(), lhs->get_string(), org_dividend, org_divisor);
162  }
163  else
164  {
165  dimensions_to_vectors(lhs->get_position(), lhs->get_string(), org_divisor, org_dividend);
166  }
167 
168  the_power = labs(the_power);
169  for(integer_t idx(0); idx < the_power; ++idx)
170  {
171  for(auto d : org_dividend)
172  {
173  dividend.push_back(d);
174  }
175  for(auto d : org_divisor)
176  {
177  divisor.push_back(d);
178  }
179  }
180 
181  result->set_string(rebuild_dimension(dividend, divisor));
182  }
183 
184  return result;
185 }
186 
188 {
189  // power: post
190  // | post '**' post
191 
192  // no loop because we do not allow 'a ** b ** c'
193  node::pointer_t result(post());
194  if(result
195  && ((f_current->is(node_type_t::IDENTIFIER) && f_current->get_string() == "pow")
196  || f_current->is(node_type_t::POWER)))
197  {
198  // skip the power operator
199  next();
200 
201  node::pointer_t rhs(post());
202  if(!rhs)
203  {
204  return false;
205  }
206 
207  // apply the power operation
208  result = apply_power(result, rhs);
209  }
210 
211  return result;
212 }
213 
214 } // namespace csspp
215 
216 // Local Variables:
217 // mode: cpp
218 // indent-tabs-mode: nil
219 // c-basic-offset: 4
220 // tab-width: 4
221 // End:
222 
223 // vim: ts=4 sw=4 et
node::pointer_t power()
Definition: expr_power.cpp:187
std::string rebuild_dimension(dimension_vector_t const &dividend, dimension_vector_t const &divisor)
std::shared_ptr< node > pointer_t
Definition: node.h:122
int64_t integer_t
Definition: csspp.h:52
int32_t constexpr mix_node_types(node_type_t a, node_type_t b)
Definition: node.h:114
node::pointer_t post()
Definition: expr_list.cpp:184
node::pointer_t apply_power(node::pointer_t lhs, node::pointer_t rhs)
Definition: expr_power.cpp:40
double decimal_number_t
Definition: csspp.h:53
void dimensions_to_vectors(position const &pos, std::string const &dimension, dimension_vector_t &dividend, dimension_vector_t &divisor)
node::pointer_t f_current
Definition: expression.h:151
static error & instance()
Definition: error.cpp:78
std::vector< std::string > dimension_vector_t
Definition: expression.h:50

Documentation of CSS Preprocessor.

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.