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

Documentation of CSS Preprocessor.

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.