LCOV - code coverage report
Current view: top level - lib - expr_additive.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 150 150 100.0 %
Date: 2017-03-23 22:48:20 Functions: 5 5 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // CSS Preprocessor
       2             : // Copyright (C) 2015-2017  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             : 
      18             : /** \file
      19             :  * \brief Implementation of the CSS Preprocessor expression.
      20             :  *
      21             :  * The CSS Preprocessor expression class is used to reduce a list
      22             :  * of nodes by applying expressions to the various values.
      23             :  *
      24             :  * \sa \ref expression_rules
      25             :  */
      26             : 
      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             : 
      40             : namespace
      41             : {
      42             : 
      43      751695 : node_type_t additive_operator(node::pointer_t n)
      44             : {
      45      751695 :     switch(n->get_type())
      46             :     {
      47             :     case node_type_t::ADD:
      48             :     case node_type_t::SUBTRACT:
      49       87996 :         return n->get_type();
      50             : 
      51             :     default:
      52      663699 :         return node_type_t::UNKNOWN;
      53             : 
      54             :     }
      55             : }
      56             : 
      57       87995 : node::pointer_t add(node::pointer_t lhs, node::pointer_t rhs, bool subtract)
      58             : {
      59       87995 :     node_type_t type(node_type_t::UNKNOWN);
      60       87995 :     bool test_dimensions(true);
      61       87995 :     integer_t ai(0);
      62       87995 :     integer_t bi(0);
      63       87995 :     decimal_number_t af(0.0);
      64       87995 :     decimal_number_t bf(0.0);
      65       87995 :     bool swapped(false);
      66             : 
      67       87995 :     switch(mix_node_types(lhs->get_type(), rhs->get_type()))
      68             :     {
      69             :     case mix_node_types(node_type_t::STRING, node_type_t::STRING):
      70       87843 :         if(!subtract)
      71             :         {
      72             :             // string concatenation
      73      175684 :             node::pointer_t result(new node(node_type_t::STRING, lhs->get_position()));
      74       87842 :             result->set_string(lhs->get_string() + rhs->get_string());
      75       87842 :             return result;
      76             :         }
      77           1 :         break;
      78             : 
      79             :     case mix_node_types(node_type_t::INTEGER, node_type_t::INTEGER):
      80          36 :         ai = lhs->get_integer();
      81          36 :         bi = rhs->get_integer();
      82          36 :         type = node_type_t::INTEGER;
      83          36 :         break;
      84             : 
      85             :     case mix_node_types(node_type_t::DECIMAL_NUMBER, node_type_t::DECIMAL_NUMBER):
      86          40 :         af = lhs->get_decimal_number();
      87          40 :         bf = rhs->get_decimal_number();
      88          40 :         type = node_type_t::DECIMAL_NUMBER;
      89          40 :         break;
      90             : 
      91             :     case mix_node_types(node_type_t::DECIMAL_NUMBER, node_type_t::INTEGER):
      92          20 :         af = lhs->get_decimal_number();
      93          20 :         bf = static_cast<decimal_number_t>(rhs->get_integer());
      94          20 :         type = node_type_t::DECIMAL_NUMBER;
      95          20 :         break;
      96             : 
      97             :     case mix_node_types(node_type_t::INTEGER, node_type_t::DECIMAL_NUMBER):
      98           8 :         af = static_cast<decimal_number_t>(lhs->get_integer());
      99           8 :         bf = rhs->get_decimal_number();
     100           8 :         type = node_type_t::DECIMAL_NUMBER;
     101           8 :         break;
     102             : 
     103             :     case mix_node_types(node_type_t::PERCENT, node_type_t::PERCENT):
     104          18 :         af = lhs->get_decimal_number();
     105          18 :         bf = rhs->get_decimal_number();
     106          18 :         type = node_type_t::PERCENT;
     107          18 :         test_dimensions = false;
     108          18 :         break;
     109             : 
     110             :     case mix_node_types(node_type_t::INTEGER, node_type_t::COLOR):
     111             :     case mix_node_types(node_type_t::DECIMAL_NUMBER, node_type_t::COLOR):
     112           8 :         std::swap(lhs, rhs);
     113           8 :         swapped = true;
     114             :         /*FALLTHROUGH*/
     115             :     case mix_node_types(node_type_t::COLOR, node_type_t::INTEGER):
     116             :     case mix_node_types(node_type_t::COLOR, node_type_t::DECIMAL_NUMBER):
     117          16 :         if(rhs->get_string() == "")
     118             :         {
     119             :             decimal_number_t offset;
     120           8 :             if(rhs->is(node_type_t::INTEGER))
     121             :             {
     122           4 :                 offset = static_cast<decimal_number_t>(rhs->get_integer());
     123             :             }
     124             :             else
     125             :             {
     126           4 :                 offset = rhs->get_decimal_number();
     127             :             }
     128           8 :             color c(lhs->get_color());
     129             :             color_component_t red;
     130             :             color_component_t green;
     131             :             color_component_t blue;
     132             :             color_component_t alpha;
     133           8 :             c.get_color(red, green, blue, alpha);
     134           8 :             if(subtract)
     135             :             {
     136           4 :                 if(swapped)
     137             :                 {
     138           2 :                     red   = offset - red;
     139           2 :                     green = offset - green;
     140           2 :                     blue  = offset - blue;
     141           2 :                     alpha = offset - alpha;
     142             :                 }
     143             :                 else
     144             :                 {
     145           2 :                     red   -= offset;
     146           2 :                     green -= offset;
     147           2 :                     blue  -= offset;
     148           2 :                     alpha -= offset;
     149             :                 }
     150             :             }
     151             :             else
     152             :             {
     153           4 :                 red   += offset;
     154           4 :                 green += offset;
     155           4 :                 blue  += offset;
     156           4 :                 alpha += offset;
     157             :             }
     158           8 :             c.set_color(red, green, blue, alpha);
     159          16 :             node::pointer_t result(new node(node_type_t::COLOR, lhs->get_position()));
     160           8 :             result->set_color(c);
     161           8 :             return result;
     162             :         }
     163           8 :         error::instance() << rhs->get_position()
     164           8 :                 << "color offsets (numbers added with + or - operators) must be unit less values, "
     165           8 :                 << (rhs->is(node_type_t::INTEGER)
     166           4 :                             ? static_cast<decimal_number_t>(rhs->get_integer())
     167          20 :                             : rhs->get_decimal_number())
     168          16 :                 << rhs->get_string()
     169           8 :                 << " is not acceptable."
     170           8 :                 << error_mode_t::ERROR_ERROR;
     171           8 :         return node::pointer_t();
     172             : 
     173             :     case mix_node_types(node_type_t::COLOR, node_type_t::COLOR):
     174             :         {
     175          13 :             color lc(lhs->get_color());
     176          13 :             color const rc(rhs->get_color());
     177             :             color_component_t lred;
     178             :             color_component_t lgreen;
     179             :             color_component_t lblue;
     180             :             color_component_t lalpha;
     181             :             color_component_t rred;
     182             :             color_component_t rgreen;
     183             :             color_component_t rblue;
     184             :             color_component_t ralpha;
     185          13 :             lc.get_color(lred, lgreen, lblue, lalpha);
     186          13 :             rc.get_color(rred, rgreen, rblue, ralpha);
     187          13 :             if(subtract)
     188             :             {
     189           3 :                 lred   -= rred;
     190           3 :                 lgreen -= rgreen;
     191           3 :                 lblue  -= rblue;
     192           3 :                 lalpha -= ralpha;
     193             :             }
     194             :             else
     195             :             {
     196          10 :                 lred   += rred;
     197          10 :                 lgreen += rgreen;
     198          10 :                 lblue  += rblue;
     199          10 :                 lalpha += ralpha;
     200             :             }
     201          13 :             lc.set_color(lred, lgreen, lblue, lalpha);
     202          26 :             node::pointer_t result(new node(node_type_t::COLOR, lhs->get_position()));
     203          13 :             result->set_color(lc);
     204          13 :             return result;
     205             :         }
     206             : 
     207             :     default:
     208           1 :         break;
     209             : 
     210             :     }
     211             : 
     212         124 :     if(type == node_type_t::UNKNOWN)
     213             :     {
     214           2 :         node_type_t lt(lhs->get_type());
     215           2 :         node_type_t rt(rhs->get_type());
     216             : 
     217           2 :         error::instance() << lhs->get_position()
     218           2 :                 << "incompatible types between "
     219           2 :                 << lt
     220           6 :                 << (lt == node_type_t::IDENTIFIER || lt == node_type_t::STRING ? " (" + lhs->get_string() + ")" : "")
     221           2 :                 << " and "
     222           2 :                 << rt
     223           7 :                 << (rt == node_type_t::IDENTIFIER || rt == node_type_t::STRING ? " (" + rhs->get_string() + ")" : "")
     224           2 :                 << " for operator '"
     225           4 :                 << (subtract ? "-" : "+")
     226           2 :                 << "'."
     227           3 :                 << error_mode_t::ERROR_ERROR;
     228           2 :         return node::pointer_t();
     229             :     }
     230             : 
     231         122 :     if(test_dimensions)
     232             :     {
     233         202 :         std::string const ldim(lhs->get_string());
     234         202 :         std::string const rdim(rhs->get_string());
     235         104 :         if(ldim != rdim)
     236             :         {
     237           6 :             error::instance() << lhs->get_position()
     238           6 :                     << "incompatible dimensions: \""
     239           6 :                     << ldim
     240           6 :                     << "\" and \""
     241           6 :                     << rdim
     242           6 :                     << "\" cannot be used as is with operator '"
     243          12 :                     << (subtract ? "-" : "+")
     244           6 :                     << "'."
     245           6 :                     << error_mode_t::ERROR_ERROR;
     246           6 :             return node::pointer_t();
     247             :         }
     248             :     }
     249             : 
     250         232 :     node::pointer_t result(new node(type, lhs->get_position()));
     251         116 :     if(type != node_type_t::PERCENT)
     252             :     {
     253             :         // do not lose the dimension
     254          98 :         result->set_string(lhs->get_string());
     255             :     }
     256             : 
     257         116 :     switch(type)
     258             :     {
     259             :     case node_type_t::INTEGER:
     260          30 :         if(subtract)
     261             :         {
     262           3 :             result->set_integer(ai - bi);
     263             :         }
     264             :         else
     265             :         {
     266          27 :             result->set_integer(ai + bi);
     267             :         }
     268          30 :         break;
     269             : 
     270             :     case node_type_t::DECIMAL_NUMBER:
     271             :     case node_type_t::PERCENT:
     272          86 :         if(subtract)
     273             :         {
     274          40 :             result->set_decimal_number(af - bf);
     275             :         }
     276             :         else
     277             :         {
     278          46 :             result->set_decimal_number(af + bf);
     279             :         }
     280          86 :         break;
     281             : 
     282             :     default:
     283             :         throw csspp_exception_logic("expression.cpp:add(): 'type' set to a value which is not handled here."); // LCOV_EXCL_LINE
     284             : 
     285             :     }
     286             : 
     287         116 :     return result;
     288             : }
     289             : 
     290             : } // no name namespace
     291             : 
     292      664267 : node::pointer_t expression::additive()
     293             : {
     294             :     //  additive: multiplicative
     295             :     //          | additive '+' multiplicative
     296             :     //          | additive '-' multiplicative
     297             : 
     298     1328534 :     node::pointer_t result(multiplicative());
     299      664267 :     if(!result)
     300             :     {
     301         551 :         return node::pointer_t();
     302             :     }
     303             : 
     304      663716 :     node_type_t op(additive_operator(f_current));
     305      839674 :     while(op != node_type_t::UNKNOWN)
     306             :     {
     307             :         // skip the additive operator
     308       87996 :         next();
     309             : 
     310      175975 :         node::pointer_t rhs(multiplicative());
     311       87996 :         if(!rhs)
     312             :         {
     313           1 :             return node::pointer_t();
     314             :         }
     315             : 
     316             :         // apply the additive operation
     317       87995 :         result = add(result, rhs, op == node_type_t::SUBTRACT);
     318       87995 :         if(!result)
     319             :         {
     320          16 :             return node::pointer_t();
     321             :         }
     322             : 
     323       87979 :         op = additive_operator(f_current);
     324             :     }
     325             : 
     326      663699 :     return result;
     327             : }
     328             : 
     329           9 : } // namespace csspp
     330             : 
     331             : // Local Variables:
     332             : // mode: cpp
     333             : // indent-tabs-mode: nil
     334             : // c-basic-offset: 4
     335             : // tab-width: 4
     336             : // End:
     337             : 
     338             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.12