wpkg test coverage results

Coverage test results of the Windows Packager by Made to Order Software Corporation.

LCOV - code coverage report
Current view: top level - libexpr - expr.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 822 824 99.8 %
Date: 2013-06-17 Functions: 64 65 98.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*    expr.c++ -- an expression evaluator implementation
       2             :  *    Copyright (C) 2007-2013  Made to Order Software Corporation
       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 along
      15             :  *    with this program; if not, write to the Free Software Foundation, Inc.,
      16             :  *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      17             :  *
      18             :  *    Authors
      19             :  *    Alexis Wilke   alexis@m2osw.com
      20             :  */
      21             : #include        "libexpr/expr.h"
      22             : 
      23             : #include        <cmath>
      24             : #include        <iostream>
      25             : #include        <sstream>
      26             : #include        <stdio.h>
      27             : #include        <stdlib.h>
      28             : #include        <string.h>
      29             : #include        <time.h>
      30             : #if defined(MO_CYGWIN) || defined(MO_FREEBSD)
      31             : #include        <sys/wait.h>
      32             : #endif
      33             : 
      34             : 
      35             : namespace libexpr
      36             : {
      37             : #ifdef _MSC_VER
      38             : namespace
      39             : {
      40             : double acosh(double flt)
      41             : {
      42             :         return log(flt + sqrt(flt * flt - 1.0));
      43             : }
      44             : 
      45             : double asinh(double flt)
      46             : {
      47             :         return log(flt + sqrt(flt * flt + 1.0));
      48             : }
      49             : 
      50             : double atanh(double flt)
      51             : {
      52             :         return log((1.0 + flt) / (1.0 - flt)) / 2.0;
      53             : }
      54             : 
      55             : double rint(double flt)
      56             : {
      57             :         if(flt < 0.0)
      58             :         {
      59             :                 return ceil(flt - 0.5);
      60             :         }
      61             :         else
      62             :         {
      63             :                 return floor(flt + 0.5);
      64             :         }
      65             : }
      66             : 
      67             : long lrint(double flt)
      68             : {
      69             :         // TODO: check for overflows?
      70             :         return static_cast<long>(rint(flt));
      71             : }
      72             : }
      73             : #endif
      74             : 
      75         599 : class expression
      76             : {
      77             : public:
      78             :         expression(expr_evaluator& evaluator);
      79             : 
      80             :         void eval(const std::string& expr, variable& result);
      81             : 
      82             : private:
      83             :         static const int TOK_ARROW              = 1000;
      84             : 
      85             :         static const int TOK_INCREMENT          = 1001;
      86             :         static const int TOK_DECREMENT          = 1002;
      87             : 
      88             :         static const int TOK_SHIFT_LEFT         = 1011;
      89             :         static const int TOK_SHIFT_RIGHT        = 1012;
      90             : 
      91             :         static const int TOK_LOGIC_AND          = 1021;
      92             :         static const int TOK_LOGIC_XOR          = 1022;
      93             :         static const int TOK_LOGIC_OR           = 1023;
      94             : 
      95             :         static const int TOK_LESS_EQUAL         = 1031;
      96             :         static const int TOK_GREATER_EQUAL      = 1032;
      97             : 
      98             :         static const int TOK_EQUAL              = 1041;
      99             :         static const int TOK_NOT_EQUAL          = 1042;
     100             : 
     101             :         static const int TOK_ASSIGN_MUL         = 1051;
     102             :         static const int TOK_ASSIGN_DIV         = 1052;
     103             :         static const int TOK_ASSIGN_MOD         = 1053;
     104             :         static const int TOK_ASSIGN_ADD         = 1054;
     105             :         static const int TOK_ASSIGN_SUB         = 1055;
     106             :         static const int TOK_ASSIGN_SHL         = 1056;
     107             :         static const int TOK_ASSIGN_SHR         = 1057;
     108             :         static const int TOK_ASSIGN_AND         = 1058;
     109             :         static const int TOK_ASSIGN_XOR         = 1059;
     110             :         static const int TOK_ASSIGN_OR          = 1060;
     111             : 
     112             :         static const int TOK_IDENTIFIER         = 1101;
     113             :         static const int TOK_STRING             = 1102;
     114             :         static const int TOK_INTEGER            = 1103;
     115             :         static const int TOK_FLOAT              = 1104;
     116             : 
     117             :         int expr_getc();
     118             :         void expr_ungetc(int c);
     119             :         void next_token();
     120             :         void number(int c);
     121             :         int backslash();
     122             :         void string();
     123             :         void character();
     124             :         void identifier(int c);
     125             :         void skip_c_comment();
     126             :         void skip_cpp_comment();
     127             : 
     128             :         void unary(variable& result);
     129             :         void array_func(variable& result);
     130             :         void postfix(variable& result);
     131             :         void prefix(variable& result);
     132             :         void multiplicative(variable& result);
     133             :         void additive(variable& result);
     134             :         void shift(variable& result);
     135             :         void relational(variable& result);
     136             :         void compare(variable& result);
     137             :         void bitwise_and(variable& result);
     138             :         void bitwise_xor(variable& result);
     139             :         void bitwise_or(variable& result);
     140             :         void logical_and(variable& result);
     141             :         void logical_xor(variable& result);
     142             :         void logical_or(variable& result);
     143             :         void conditional(variable& result);
     144             :         void assignment(variable& result);
     145             :         void comma(variable& result);
     146             : 
     147             :         expression& operator = (const expression&)
     148             :         {
     149             :                 return *this;
     150             :         }
     151             : 
     152             :         expr_evaluator&             f_evaluator;
     153             :         int                     f_token;        // the current token
     154             :         variable                f_var;          // for identifiers, strings, integers and floats
     155             :         int                     f_unget;        // next character is not EOF
     156             :         size_t                  f_pos;          // position in f_expr
     157             :         std::string             f_expr;
     158             :         int                     f_line;         // number of lines
     159             :         int                     f_character;    // character we're on within this line
     160             : };
     161             : 
     162             : 
     163             : 
     164         599 : expression::expression(expr_evaluator& evaluator)
     165             :         : f_evaluator(evaluator)
     166             :         , f_token(EOF)
     167             :         //, f_var -- auto-init
     168             :         , f_unget(EOF)
     169             :         , f_pos(0)
     170             :         //, f_expr -- auto-init
     171             :         , f_line(0)
     172         599 :         , f_character(0)
     173             : {
     174         599 : }
     175             : 
     176             : 
     177       14803 : int expression::expr_getc()
     178             : {
     179             :         int     c;
     180             : 
     181       14803 :         if(f_unget != EOF)
     182             :         {
     183        2020 :                 c = f_unget;
     184        2020 :                 f_unget = EOF;
     185        2020 :                 return c;
     186             :         }
     187             : 
     188       12783 :         if(f_pos >= f_expr.size())
     189             :         {
     190         750 :                 return EOF;
     191             :         }
     192             : 
     193       12033 :         c = f_expr[f_pos];
     194       12033 :         if(c == '\r')
     195             :         {
     196           2 :                 if(f_expr[f_pos + 1] == '\n')
     197             :                 {
     198           2 :                         ++f_pos;
     199             :                 }
     200           2 :                 ++f_line;
     201           2 :                 f_character = 1;
     202           2 :                 c = '\n';
     203             :         }
     204       12031 :         else if(c == '\n')
     205             :         {
     206           2 :                 ++f_line;
     207           2 :                 f_character = 1;
     208             :         }
     209             :         else
     210             :         {
     211       12029 :                 ++f_character;
     212             :         }
     213             : 
     214       12033 :         ++f_pos;
     215             : 
     216       14803 :         return c;
     217             : }
     218             : 
     219        2216 : void expression::expr_ungetc(int c)
     220             : {
     221        2216 :         f_unget = c;
     222        2216 : }
     223             : 
     224             : 
     225         745 : void expression::number(int c)
     226             : {
     227         745 :         f_token = TOK_INTEGER;
     228             : 
     229         745 :         long value = long();
     230             :         bool first;
     231             : 
     232         745 :         if(c == '0')
     233             :         {
     234         116 :                 c = expr_getc();
     235         116 :                 if(c == 'x' || c == 'X')
     236             :                 {
     237             :                         // hexadecimal
     238          47 :                         first = true;
     239         144 :                         for(;;)
     240             :                         {
     241         191 :                                 c = expr_getc();
     242         191 :                                 if(c >= 'a' && c <= 'f')
     243             :                                 {
     244           1 :                                         value = (value << 4) | (c - ('a' - 10));
     245             :                                 }
     246         190 :                                 else if(c >= 'A' && c <= 'F')
     247             :                                 {
     248          45 :                                         value = (value << 4) | (c - ('A' - 10));
     249             :                                 }
     250         145 :                                 else if(c >= '0' && c <= '9')
     251             :                                 {
     252          98 :                                         value = (value << 4) | (c - '0');
     253             :                                 }
     254          47 :                                 else if(first)
     255             :                                 {
     256             :                                         // would C/C++ consider this as 0 then identifer starting with x?
     257           1 :                                         throw syntax_error("expected at least one hexadecimal character after 0x");
     258             :                                 }
     259             :                                 else
     260             :                                 {
     261          46 :                                         expr_ungetc(c);
     262          46 :                                         f_var.set(value);
     263          46 :                                         return;
     264             :                                 }
     265         144 :                                 first = false;
     266             :                         }
     267             :                 }
     268             :                 // floating point?
     269          69 :                 if(c != '.')
     270             :                 {
     271             :                         // octal
     272          23 :                         for(;;)
     273             :                         {
     274          42 :                                 if(c >= '0' && c <= '7')
     275             :                                 {
     276          23 :                                         value = (value << 3) | (c - '0');
     277             :                                 }
     278          19 :                                 else if(c == '8' || c == '9')
     279             :                                 {
     280           2 :                                         throw syntax_error("invalid octal digit of 8 or 9");
     281             :                                 }
     282             :                                 else
     283             :                                 {
     284             :                                         // in this case, no extra digits means the value is zero
     285          17 :                                         expr_ungetc(c);
     286          17 :                                         f_var.set(value);
     287          17 :                                         return;
     288             :                                 }
     289          23 :                                 c = expr_getc();
     290             :                         }
     291             :                 }
     292             :         }
     293             : 
     294        1721 :         while(c >= '0' && c <= '9')
     295             :         {
     296        1042 :                 value = value * 10 + c - '0';
     297        1042 :                 c = expr_getc();
     298             :         }
     299             : 
     300         679 :         if(c != '.')
     301             :         {
     302         456 :                 expr_ungetc(c);
     303         456 :                 f_var.set(value);
     304         456 :                 return;
     305             :         }
     306             : 
     307             :         // this is a floating point value
     308         223 :         f_token = TOK_FLOAT;
     309             : 
     310         223 :         double flt = double(value);
     311             : 
     312         223 :         double divisor = 0.1;
     313             : 
     314         223 :         c = expr_getc();
     315         542 :         while(c >= '0' && c <= '9')
     316             :         {
     317         319 :                 flt += double(c - '0') * divisor;
     318         319 :                 divisor /= 10.0;
     319         319 :                 c = expr_getc();
     320             :         }
     321             : 
     322         223 :         if(c == 'e' || c == 'E')
     323             :         {
     324             :                 // exponent
     325          17 :                 double sign = 1;
     326          17 :                 c = expr_getc();
     327          17 :                 if(c == '+')
     328             :                 {
     329           3 :                         c = expr_getc();
     330             :                 }
     331          14 :                 else if(c == '-')
     332             :                 {
     333           7 :                         c = expr_getc();
     334           7 :                         sign = -1;
     335             :                 }
     336          17 :                 if(c < '0' || c > '9')
     337             :                 {
     338           3 :                         throw syntax_error("invalid floating point exponent");
     339             :                 }
     340          14 :                 long exp = long();
     341          28 :                 while(c >= '0' && c <= '9')
     342             :                 {
     343          14 :                         exp = exp * 10 + c - '0';
     344          14 :                         c = expr_getc();
     345             :                 }
     346          14 :                 flt *= pow(10.0, exp * sign);
     347             :         }
     348             : 
     349         220 :         expr_ungetc(c);
     350             : 
     351         739 :         f_var.set(flt);
     352             : }
     353             : 
     354          23 : int expression::backslash()
     355             : {
     356          23 :         int c = expr_getc();
     357             : 
     358             :         // hex
     359          23 :         if(c == 'x' || c == 'X')
     360             :         {
     361             :                 int x1, x2;
     362          10 :                 c = expr_getc();
     363          10 :                 if(c >= 'a' && c <= 'f')
     364             :                 {
     365           1 :                         x1 = c - ('a' - 10);
     366             :                 }
     367           9 :                 else if(c >= 'A' && c <= 'F')
     368             :                 {
     369           4 :                         x1 = c - ('A' - 10);
     370             :                 }
     371           5 :                 else if(c >= '0' && c <= '9')
     372             :                 {
     373           4 :                         x1 = c - '0';
     374             :                 }
     375             :                 else
     376             :                 {
     377           1 :                         throw syntax_error("unexpected digit for an hexadecimal escape sequence");
     378             :                 }
     379           9 :                 c = expr_getc();
     380           9 :                 if(c >= 'a' && c <= 'f')
     381             :                 {
     382           3 :                         x2 = c - ('a' - 10);
     383             :                 }
     384           6 :                 else if(c >= 'A' && c <= 'F')
     385             :                 {
     386           1 :                         x2 = c - ('A' - 10);
     387             :                 }
     388           5 :                 else if(c >= '0' && c <= '9')
     389             :                 {
     390           4 :                         x2 = c - '0';
     391             :                 }
     392             :                 else
     393             :                 {
     394           1 :                         expr_ungetc(c);
     395           1 :                         return x1;
     396             :                 }
     397           8 :                 return x1 * 16 + x2;
     398             :         }
     399             : 
     400             :         // octal
     401          13 :         if(c >= '0' && c <= '7')
     402             :         {
     403           4 :                 int r = c - '0';
     404           4 :                 c = expr_getc();
     405           4 :                 if(c >= '0' && c <= '7') {
     406           3 :                         r = (r << 3) | (c - '0');
     407           3 :                         c = expr_getc();
     408           6 :                         if(c >= '0' && c <= '7')
     409             :                         {
     410           1 :                                 r = (r << 3) | (c - '0');
     411             :                         }
     412             :                         else
     413             :                         {
     414           2 :                                 expr_ungetc(c);
     415             :                         }
     416             :                 }
     417             :                 else
     418             :                 {
     419           1 :                         expr_ungetc(c);
     420             :                 }
     421           4 :                 return r;
     422             :         }
     423             : 
     424             :         // char
     425           9 :         switch(c)
     426             :         {
     427           1 :         case 'a': return '\a';
     428           1 :         case 'b': return '\b';
     429           1 :         case 'e': return '\033';        // escape
     430           1 :         case 'f': return '\f';
     431           1 :         case 'n': return '\n';
     432           1 :         case 'r': return '\r';
     433           1 :         case 't': return '\t';
     434           1 :         case 'v': return '\v';
     435             :         }
     436             : 
     437             :         // anything else is returned as is (especially \\ and \")
     438          22 :         return c;
     439             : }
     440             : 
     441         408 : void expression::string()
     442             : {
     443         408 :         f_token = TOK_STRING;
     444             : 
     445         408 :         std::string result;
     446             :         int c;
     447             : 
     448        1934 :         for(;;)
     449             :         {
     450        2342 :                 c = expr_getc();
     451        2342 :                 if(c == EOF)
     452             :                 {
     453           1 :                         throw syntax_error("string not closed (missing \")");
     454             :                 }
     455        2341 :                 if(c == '"')
     456             :                 {
     457         406 :                         f_var.set(result);
     458         406 :                         return;
     459             :                 }
     460        1935 :                 if(c == '\\')
     461             :                 {
     462          20 :                         c = backslash();
     463             :                 }
     464        1934 :                 result += static_cast<char>(c);
     465         408 :         }
     466             : }
     467             : 
     468          13 : void expression::character()
     469             : {
     470          13 :         f_token = TOK_INTEGER;
     471             : 
     472          13 :         long c = expr_getc();
     473          13 :         if(c == '\\')
     474             :         {
     475           3 :                 c = backslash();
     476             :         }
     477             : 
     478          13 :         f_var.set(c);
     479             : 
     480          13 :         c = expr_getc();
     481          13 :         if(c != '\'')
     482             :         {
     483           4 :                 throw syntax_error("character not closed (missing ')");
     484             :         }
     485           9 : }
     486             : 
     487         847 : void expression::identifier(int c)
     488             : {
     489         847 :         f_token = TOK_IDENTIFIER;
     490             : 
     491         847 :         std::string result;
     492         847 :         result += static_cast<char>(c);
     493             : 
     494        1711 :         for(;;)
     495             :         {
     496        2558 :                 c = expr_getc();
     497        2558 :                 if((c < '0' || c > '9')
     498             :                 && (c < 'a' || c > 'z')
     499             :                 && (c < 'A' || c > 'Z')
     500             :                 && c != '_')
     501             :                 {
     502         847 :                         expr_ungetc(c);
     503         847 :                         break;
     504             :                 }
     505        1711 :                 result += static_cast<char>(c);
     506             :         }
     507             : 
     508             :         // special identifiers that represent a value
     509         847 :         if(result == "true")
     510             :         {
     511          51 :                 f_token = TOK_INTEGER;
     512          51 :                 f_var.set(1L);
     513             :                 return;
     514             :         }
     515         796 :         if(result == "false")
     516             :         {
     517          41 :                 f_token = TOK_INTEGER;
     518          41 :                 f_var.set(0L);
     519             :                 return;
     520             :         }
     521             : 
     522             :         // XXX should we forbid all C++ keywords here? (for, if, while, inline, etc.)
     523             : 
     524             :         // the identifier is considered to be a variable name
     525         847 :         f_var.set_name(result);
     526             : }
     527             : 
     528          68 : void expression::skip_c_comment()
     529             : {
     530             :         int c;
     531          67 :         do
     532             :         {
     533          68 :                 c = expr_getc();
     534          69 :                 while(c == '*')
     535             :                 {
     536           2 :                         c = expr_getc();
     537           2 :                         if(c == '/')
     538             :                         {
     539           1 :                                 return;
     540             :                         }
     541             :                 }
     542             :         }
     543             :         while(c != EOF);
     544             : }
     545             : 
     546          26 : void expression::skip_cpp_comment()
     547             : {
     548             :         int c;
     549          25 :         do
     550             :         {
     551          26 :                 c = expr_getc();
     552          26 :                 while(c == '\n')
     553             :                 {
     554           1 :                         return;
     555             :                 }
     556             :         } while(c != EOF);
     557             : }
     558             : 
     559        6736 : void expression::next_token()
     560             : {
     561             :         int c;
     562             : 
     563        2072 :         for(;;)
     564             :         {
     565        6736 :                 f_token = expr_getc();
     566        6736 :                 switch(f_token)
     567             :                 {
     568             :                 case ' ':
     569             :                 case '\t':
     570             :                 case '\n':      // expr_getc() transforms '\r' in '\n'
     571             :                 case '\f':
     572             :                         // skip blanks
     573        2070 :                         continue;
     574             : 
     575             :                 case '"':
     576         408 :                         string();
     577        4652 :                         return;
     578             : 
     579             :                 case '\'':
     580          13 :                         character();
     581           9 :                         return;
     582             : 
     583             :                 case '=':
     584         237 :                         c = expr_getc();
     585         237 :                         if(c == '=')
     586             :                         {
     587          21 :                                 f_token = TOK_EQUAL;
     588          21 :                                 return;
     589             :                         }
     590         216 :                         expr_ungetc(c);
     591         216 :                         return;
     592             : 
     593             :                 case '!':
     594          26 :                         c = expr_getc();
     595          26 :                         if(c == '=')
     596             :                         {
     597          17 :                                 f_token = TOK_NOT_EQUAL;
     598          17 :                                 return;
     599             :                         }
     600           9 :                         expr_ungetc(c);
     601           9 :                         return;
     602             : 
     603             :                 case '<':
     604          59 :                         c = expr_getc();
     605          59 :                         if(c == '=')
     606             :                         {
     607          16 :                                 f_token = TOK_LESS_EQUAL;
     608          16 :                                 return;
     609             :                         }
     610          43 :                         if(c == '<')
     611             :                         {
     612          24 :                                 c = expr_getc();
     613          24 :                                 if(c == '=')
     614             :                                 {
     615           5 :                                         f_token = TOK_ASSIGN_SHL;
     616           5 :                                         return;
     617             :                                 }
     618          19 :                                 f_token = TOK_SHIFT_LEFT;
     619             :                         }
     620          38 :                         expr_ungetc(c);
     621          38 :                         return;
     622             : 
     623             :                 case '>':
     624         196 :                         c = expr_getc();
     625         196 :                         if(c == '=')
     626             :                         {
     627         156 :                                 f_token = TOK_GREATER_EQUAL;
     628         156 :                                 return;
     629             :                         }
     630          40 :                         if(c == '>')
     631             :                         {
     632          21 :                                 c = expr_getc();
     633          21 :                                 if(c == '=')
     634             :                                 {
     635           5 :                                         f_token = TOK_ASSIGN_SHR;
     636           5 :                                         return;
     637             :                                 }
     638          16 :                                 f_token = TOK_SHIFT_RIGHT;
     639             :                         }
     640          35 :                         expr_ungetc(c);
     641          35 :                         return;
     642             : 
     643             :                 case '&':
     644          55 :                         c = expr_getc();
     645          55 :                         if(c == '&')
     646             :                         {
     647          36 :                                 f_token = TOK_LOGIC_AND;
     648          36 :                                 return;
     649             :                         }
     650          19 :                         if(c == '=')
     651             :                         {
     652           5 :                                 f_token = TOK_ASSIGN_AND;
     653           5 :                                 return;
     654             :                         }
     655          14 :                         expr_ungetc(c);
     656          14 :                         return;
     657             : 
     658             :                 case '^':
     659          35 :                         c = expr_getc();
     660          35 :                         if(c == '^')
     661             :                         {
     662          17 :                                 f_token = TOK_LOGIC_XOR;
     663          17 :                                 return;
     664             :                         }
     665          18 :                         if(c == '=')
     666             :                         {
     667           5 :                                 f_token = TOK_ASSIGN_XOR;
     668           5 :                                 return;
     669             :                         }
     670          13 :                         expr_ungetc(c);
     671          13 :                         return;
     672             : 
     673             :                 case '|':
     674          41 :                         c = expr_getc();
     675          41 :                         if(c == '|')
     676             :                         {
     677          22 :                                 f_token = TOK_LOGIC_OR;
     678          22 :                                 return;
     679             :                         }
     680          19 :                         if(c == '=')
     681             :                         {
     682           5 :                                 f_token = TOK_ASSIGN_OR;
     683           5 :                                 return;
     684             :                         }
     685          14 :                         expr_ungetc(c);
     686          14 :                         return;
     687             : 
     688             :                 case '+':
     689         113 :                         c = expr_getc();
     690         113 :                         if(c == '+')
     691             :                         {
     692          17 :                                 f_token = TOK_INCREMENT;
     693          17 :                                 return;
     694             :                         }
     695          96 :                         if(c == '=')
     696             :                         {
     697           5 :                                 f_token = TOK_ASSIGN_ADD;
     698           5 :                                 return;
     699             :                         }
     700          91 :                         expr_ungetc(c);
     701          91 :                         return;
     702             : 
     703             :                 case '-':
     704          79 :                         c = expr_getc();
     705          79 :                         if(c == '-')
     706             :                         {
     707          14 :                                 f_token = TOK_DECREMENT;
     708          14 :                                 return;
     709             :                         }
     710          65 :                         if(c == '=')
     711             :                         {
     712           6 :                                 f_token = TOK_ASSIGN_SUB;
     713           6 :                                 return;
     714             :                         }
     715          59 :                         if(c == '>')
     716             :                         {
     717           1 :                                 f_token = TOK_ARROW;
     718           1 :                                 return;
     719             :                         }
     720          58 :                         expr_ungetc(c);
     721          58 :                         return;
     722             : 
     723             :                 case '*':
     724         110 :                         c = expr_getc();
     725         110 :                         if(c == '=')
     726             :                         {
     727           5 :                                 f_token = TOK_ASSIGN_MUL;
     728           5 :                                 return;
     729             :                         }
     730         105 :                         expr_ungetc(c);
     731         105 :                         return;
     732             : 
     733             :                 case '/':
     734          30 :                         c = expr_getc();
     735          30 :                         if(c == '=')
     736             :                         {
     737           5 :                                 f_token = TOK_ASSIGN_DIV;
     738           5 :                                 return;
     739             :                         }
     740          25 :                         if(c == '*')
     741             :                         {
     742           1 :                                 skip_c_comment();
     743           1 :                                 continue;
     744             :                         }
     745          24 :                         if(c == '/')
     746             :                         {
     747           1 :                                 skip_cpp_comment();
     748           1 :                                 continue;
     749             :                         }
     750          23 :                         expr_ungetc(c);
     751          23 :                         return;
     752             : 
     753             :                 case '%':
     754          15 :                         c = expr_getc();
     755          15 :                         if(c == '=')
     756             :                         {
     757           5 :                                 f_token = TOK_ASSIGN_MOD;
     758           5 :                                 return;
     759             :                         }
     760          10 :                         expr_ungetc(c);
     761          10 :                         return;
     762             : 
     763             :                 }
     764             : 
     765        3249 :                 if((f_token >= '0' && f_token <= '9') || f_token == '.')
     766             :                 {
     767         745 :                         number(f_token);
     768         739 :                         return;
     769             :                 }
     770             : 
     771        2504 :                 if((f_token >= 'a' && f_token <= 'z')
     772             :                 || (f_token >= 'A' && f_token <= 'Z')
     773             :                 || f_token == '_')
     774             :                 {
     775         847 :                         identifier(f_token);
     776         847 :                         return;
     777             :                 }
     778             : 
     779             :                 // anything else is returned as is (i.e. ',', '(', ')', etc.)
     780        1657 :                 return;
     781             :         }
     782             : }
     783             : 
     784        2224 : void expression::unary(variable& result)
     785             : {
     786        2224 :         variable value;
     787             : 
     788             : //std::cerr << "Unary with " << f_token << std::endl;
     789             : 
     790        2224 :         switch(f_token)
     791             :         {
     792             :         case '!':
     793           9 :                 next_token();
     794           9 :                 prefix(value);
     795           9 :                 result.logic_not(value);
     796           9 :                 break;
     797             : 
     798             :         case '~':
     799          15 :                 next_token();
     800          15 :                 prefix(value);
     801          15 :                 result.bitwise_not(value);
     802          12 :                 break;
     803             : 
     804             :         case '+':
     805          12 :                 next_token();
     806          12 :                 prefix(value);
     807          12 :                 result.pls(value);
     808          11 :                 break;
     809             : 
     810             :         case '-':
     811          40 :                 next_token();
     812          40 :                 prefix(value);
     813          40 :                 result.neg(value);
     814          39 :                 break;
     815             : 
     816             :         case '(':
     817             :                 //next_token(); -- comma() calls this function already
     818         153 :                 comma(result);
     819         151 :                 if(f_token != ')')
     820             :                 {
     821           3 :                         std::stringstream msg;
     822           3 :                         if(f_token == TOK_IDENTIFIER)
     823             :                         {
     824           1 :                                 msg << "identifier \"" << f_var.get_name() << "\"";
     825             :                         }
     826             :                         else
     827             :                         {
     828           2 :                                 msg << "token number " << f_token;
     829             :                         }
     830           3 :                         throw syntax_error("expected ')' to close the parenthesis instead of " + msg.str());
     831             :                 }
     832         148 :                 next_token();
     833         148 :                 break;
     834             : 
     835             :         case TOK_IDENTIFIER:
     836             :                 {
     837         752 :                         std::string name(f_var.get_name());
     838         752 :                         result.set_name(name);
     839         752 :                         next_token();
     840         752 :                         if(f_token != '=' && f_token != '(' && !f_evaluator.get(name, result))
     841             :                         {
     842          16 :                                 throw undefined_variable("undefined variable \"" + name + "\" (1)");
     843         752 :                         }
     844             :                 }
     845         736 :                 break;
     846             : 
     847             :         case TOK_STRING:
     848         401 :                 result = f_var;
     849         401 :                 next_token();
     850         406 :                 while(f_token == TOK_STRING)
     851             :                 {
     852             :                         // concatenate
     853           5 :                         variable lhs(result);
     854           5 :                         result.add(lhs, f_var);
     855           5 :                         next_token();
     856           5 :                 }
     857         401 :                 break;
     858             : 
     859             :         case TOK_INTEGER:
     860             :         case TOK_FLOAT:
     861         840 :                 result = f_var;
     862         840 :                 next_token();
     863         840 :                 break;
     864             : 
     865             :         default:
     866             :                 // this includes ')', ';', EOF, etc.
     867           2 :                 break;
     868             : 
     869        2224 :         }
     870        2198 : }
     871             : 
     872             : 
     873        2224 : void expression::array_func(variable& result)
     874             : {
     875        2224 :         unary(result);
     876             : 
     877        2198 :         if(f_token == '(')
     878             :         {
     879             :                 // skip the '('
     880         255 :                 next_token();
     881             : 
     882             :                 // function call
     883         255 :                 expr_evaluator::arglist list;
     884         255 :                 if(f_token != ')')
     885             :                 {
     886         254 :                         if(f_token == EOF)
     887             :                         {
     888           1 :                                 throw syntax_error("unterminated list of parameters");
     889             :                         }
     890          20 :                         for(;;)
     891             :                         {
     892         273 :                                 variable param;
     893         273 :                                 assignment(param);
     894         273 :                                 list.push_back(param);
     895         273 :                                 if(f_token == ')')
     896             :                                 {
     897             :                                         break;
     898             :                                 }
     899          22 :                                 if(f_token != ',')
     900             :                                 {
     901           2 :                                         throw syntax_error("expected a ',' or ')' in a function list of arguments");
     902             :                                 }
     903             :                                 // skip the ','
     904         291 :                                 next_token();
     905         273 :                         }
     906             :                 }
     907             : 
     908             :                 // skip the ')'
     909         252 :                 next_token();
     910             : 
     911             :                 // call the function now
     912         252 :                 std::string name(result.get_name());
     913         252 :                 if(name.empty())
     914             :                 {
     915           1 :                         throw syntax_error("a function name must be an identifier");
     916             :                 }
     917         255 :                 f_evaluator.call_function(name, list, result);
     918             :         }
     919        2190 : }
     920             : 
     921             : 
     922        2224 : void expression::postfix(variable& result)
     923             : {
     924        2224 :         array_func(result);
     925             : 
     926             :         long adjust;
     927        2190 :         switch(f_token)
     928             :         {
     929             :         case TOK_INCREMENT:
     930           8 :                 adjust = 1;
     931           8 :                 break;
     932             : 
     933             :         case TOK_DECREMENT:
     934           6 :                 adjust = -1;
     935           6 :                 break;
     936             : 
     937             :         default:
     938        2186 :                 return;
     939             : 
     940             :         }
     941             : 
     942          14 :         next_token();
     943          14 :         std::string name(result.get_name());
     944          14 :         if(name.empty())
     945             :         {
     946           4 :                 throw expected_a_variable("expected a variable to apply ++ or -- operator");
     947             :         }
     948          10 :         variable new_value, old_value, increment;
     949          10 :         if(!f_evaluator.get(name, old_value))
     950             :         {
     951             :                 // this should not be reached by coverage tests since
     952             :                 // TOK_IDENTIFIER already checked whether the variable existed
     953             :                 throw undefined_variable("undefined variable \"" + name + "\" (2)"); // LCOV_EXCL_LINE
     954             :         }
     955          10 :         increment.set(adjust);
     956          10 :         new_value.add(old_value, increment);
     957          14 :         f_evaluator.set(name, new_value);
     958             :         // notice how result is not affected by the operation, only the variable
     959             : }
     960             : 
     961             : 
     962        2224 : void expression::prefix(variable& result)
     963             : {
     964             :         long adjust;
     965        2224 :         switch(f_token)
     966             :         {
     967             :         case TOK_INCREMENT:
     968           6 :                 adjust = 1;
     969           6 :                 next_token();
     970           6 :                 break;;
     971             : 
     972             :         case TOK_DECREMENT:
     973           5 :                 adjust = -1;
     974           5 :                 next_token();
     975           5 :                 break;
     976             : 
     977             :         default:
     978        2213 :                 adjust = 0;
     979        2213 :                 break;
     980             : 
     981             :         }
     982             : 
     983        2224 :         postfix(result);
     984             : 
     985        2186 :         if(adjust != 0)
     986             :         {
     987           9 :                 std::string name(result.get_name());
     988           9 :                 if(name.empty())
     989             :                 {
     990           4 :                         throw expected_a_variable("expected a variable to apply ++ or -- operator");
     991             :                 }
     992           5 :                 variable old_value, increment;
     993           5 :                 if(!f_evaluator.get(name, old_value))
     994             :                 {
     995             :                         // this should not be reached by coverage tests since
     996             :                         // TOK_IDENTIFIER already checked whether the variable existed
     997             :                         throw undefined_variable("undefined variable \"" + name + "\" (3)"); // LCOV_EXCL_LINE
     998             :                 }
     999           5 :                 increment.set(adjust);
    1000           5 :                 result.add(old_value, increment);
    1001           9 :                 f_evaluator.set(name, result);
    1002             :                 // notice how in this case we tweak result instead of new_value
    1003             :                 // since the operation affects the result here
    1004             :         }
    1005        2182 : }
    1006             : 
    1007             : 
    1008        2010 : void expression::multiplicative(variable& result)
    1009             : {
    1010        2010 :         prefix(result);
    1011             : 
    1012         127 :         for(;;)
    1013             :         {
    1014        2095 :                 switch(f_token)
    1015             :                 {
    1016             :                 case '*':
    1017             :                         {
    1018         105 :                         variable lhs(result), rhs;
    1019         105 :                         next_token();
    1020         105 :                         prefix(rhs);
    1021         105 :                         result.mul(lhs, rhs);
    1022             :                         }
    1023         102 :                         continue;
    1024             : 
    1025             :                 case '/':
    1026             :                         {
    1027          23 :                         variable lhs(result), rhs;
    1028          23 :                         next_token();
    1029          23 :                         prefix(rhs);
    1030          23 :                         result.div(lhs, rhs);
    1031             :                         }
    1032          20 :                         continue;
    1033             : 
    1034             :                 case '%':
    1035             :                         {
    1036          10 :                         variable lhs(result), rhs;
    1037          10 :                         next_token();
    1038          10 :                         prefix(rhs);
    1039          10 :                         result.mod(lhs, rhs);
    1040             :                         }
    1041           5 :                         continue;
    1042             : 
    1043             :                 default:;
    1044             :                         /*FALLTHROUGH*/
    1045             :                 }
    1046        1957 :                 break;
    1047             :         }
    1048        1957 : }
    1049             : 
    1050             : 
    1051        1915 : void expression::additive(variable& result)
    1052             : {
    1053        1915 :         multiplicative(result);
    1054             : 
    1055          92 :         for(;;)
    1056             :         {
    1057        1954 :                 switch(f_token)
    1058             :                 {
    1059             :                 case '+':
    1060             :                         {
    1061          79 :                         variable lhs(result), rhs;
    1062          79 :                         next_token();
    1063          77 :                         multiplicative(rhs);
    1064          79 :                         result.add(lhs, rhs);
    1065             :                         }
    1066          77 :                         continue;
    1067             : 
    1068             :                 case '-':
    1069             :                         {
    1070          18 :                         variable lhs(result), rhs;
    1071          18 :                         next_token();
    1072          18 :                         multiplicative(rhs);
    1073          18 :                         result.sub(lhs, rhs);
    1074             :                         }
    1075          15 :                         continue;
    1076             : 
    1077             :                 default:;
    1078             :                         /*FALLTHROUGH*/
    1079             :                 }
    1080        1857 :                 break;
    1081             :         }
    1082        1857 : }
    1083             : 
    1084             : 
    1085        1880 : void expression::shift(variable& result)
    1086             : {
    1087        1880 :         additive(result);
    1088             : 
    1089          29 :         for(;;)
    1090             :         {
    1091        1851 :                 switch(f_token)
    1092             :                 {
    1093             :                 case TOK_SHIFT_LEFT:    // <<
    1094             :                         {
    1095          19 :                         variable lhs(result), rhs;
    1096          19 :                         next_token();
    1097          19 :                         additive(rhs);
    1098          19 :                         result.shl(lhs, rhs);
    1099             :                         }
    1100          16 :                         continue;
    1101             : 
    1102             :                 case TOK_SHIFT_RIGHT:   // >>
    1103             :                         {
    1104          16 :                         variable lhs(result), rhs;
    1105          16 :                         next_token();
    1106          16 :                         additive(rhs);
    1107          16 :                         result.shr(lhs, rhs);
    1108             :                         }
    1109          13 :                         continue;
    1110             : 
    1111             :                 default:;
    1112             :                         /*FALLTHROUGH*/
    1113             :                 }
    1114        1816 :                 break;
    1115             :         }
    1116        1816 : }
    1117             : 
    1118             : 
    1119        1670 : void expression::relational(variable& result)
    1120             : {
    1121        1670 :         shift(result);
    1122             : 
    1123         194 :         for(;;)
    1124             :         {
    1125        1800 :                 switch(f_token)
    1126             :                 {
    1127             :                 case '<':
    1128             :                         {
    1129          19 :                         variable lhs(result), rhs;
    1130          19 :                         next_token();
    1131          19 :                         shift(rhs);
    1132          19 :                         result.lt(lhs, rhs);
    1133             :                         }
    1134          15 :                         continue;
    1135             : 
    1136             :                 case TOK_LESS_EQUAL:    // <=
    1137             :                         {
    1138          16 :                         variable lhs(result), rhs;
    1139          16 :                         next_token();
    1140          16 :                         shift(rhs);
    1141          16 :                         result.le(lhs, rhs);
    1142             :                         }
    1143          12 :                         continue;
    1144             : 
    1145             :                 case TOK_GREATER_EQUAL: // >=
    1146             :                         {
    1147         156 :                         variable lhs(result), rhs;
    1148         156 :                         next_token();
    1149         156 :                         shift(rhs);
    1150         156 :                         result.ge(lhs, rhs);
    1151             :                         }
    1152         152 :                         continue;
    1153             : 
    1154             :                 case '>':
    1155             :                         {
    1156          19 :                         variable lhs(result), rhs;
    1157          19 :                         next_token();
    1158          19 :                         shift(rhs);
    1159          19 :                         result.gt(lhs, rhs);
    1160             :                         }
    1161          15 :                         continue;
    1162             : 
    1163             :                 default:;
    1164             :                         /*FALLTHROUGH*/
    1165             :                 }
    1166        1590 :                 break;
    1167             :         }
    1168        1590 : }
    1169             : 
    1170             : 
    1171        1632 : void expression::compare(variable& result)
    1172             : {
    1173        1632 :         relational(result);
    1174             : 
    1175          30 :         for(;;)
    1176             :         {
    1177        1582 :                 switch(f_token)
    1178             :                 {
    1179             :                 case TOK_EQUAL: // ==
    1180             :                         {
    1181          21 :                         variable lhs(result), rhs;
    1182          21 :                         next_token();
    1183          21 :                         relational(rhs);
    1184          21 :                         result.eq(lhs, rhs);
    1185             :                         }
    1186          17 :                         continue;
    1187             : 
    1188             :                 case TOK_NOT_EQUAL:     // !=
    1189             :                         {
    1190          17 :                         variable lhs(result), rhs;
    1191          17 :                         next_token();
    1192          17 :                         relational(rhs);
    1193          17 :                         result.ne(lhs, rhs);
    1194             :                         }
    1195          13 :                         continue;
    1196             : 
    1197             :                 default:;
    1198             :                         /*FALLTHROUGH*/
    1199             :                 }
    1200        1544 :                 break;
    1201             :         }
    1202        1544 : }
    1203             : 
    1204             : 
    1205        1618 : void expression::bitwise_and(variable& result)
    1206             : {
    1207        1618 :         compare(result);
    1208             : 
    1209        1541 :         while(f_token == '&') {
    1210          14 :                 variable lhs(result), rhs;
    1211          14 :                 next_token();
    1212          14 :                 compare(rhs);
    1213             : 
    1214          14 :                 result.bitwise_and(lhs, rhs);
    1215          14 :         }
    1216        1527 : }
    1217             : 
    1218             : 
    1219        1605 : void expression::bitwise_xor(variable& result)
    1220             : {
    1221        1605 :         bitwise_and(result);
    1222             : 
    1223        1524 :         while(f_token == '^')
    1224             :         {
    1225          13 :                 variable lhs(result), rhs;
    1226          13 :                 next_token();
    1227          13 :                 bitwise_and(rhs);
    1228             : 
    1229          13 :                 result.bitwise_xor(lhs, rhs);
    1230          13 :         }
    1231        1511 : }
    1232             : 
    1233             : 
    1234        1591 : void expression::bitwise_or(variable& result)
    1235             : {
    1236        1591 :         bitwise_xor(result);
    1237             : 
    1238        1508 :         while(f_token == '|')
    1239             :         {
    1240          14 :                 variable lhs(result), rhs;
    1241          14 :                 next_token();
    1242          14 :                 bitwise_xor(rhs);
    1243             : 
    1244          14 :                 result.bitwise_or(lhs, rhs);
    1245          14 :         }
    1246        1494 : }
    1247             : 
    1248             : 
    1249        1555 : void expression::logical_and(variable& result)
    1250             : {
    1251        1555 :         bitwise_or(result);
    1252             : 
    1253        1494 :         while(f_token == TOK_LOGIC_AND)         // &&
    1254             :         {
    1255          36 :                 variable lhs(result), rhs;
    1256          36 :                 next_token();
    1257          36 :                 bitwise_or(rhs);
    1258             : 
    1259          36 :                 result.logic_and(lhs, rhs);
    1260          36 :         }
    1261        1458 : }
    1262             : 
    1263             : 
    1264        1538 : void expression::logical_xor(variable& result)
    1265             : {
    1266        1538 :         logical_and(result);
    1267             : 
    1268        1458 :         while(f_token == TOK_LOGIC_XOR)         // ^^
    1269             :         {
    1270          17 :                 variable lhs(result), rhs;
    1271          17 :                 next_token();
    1272          17 :                 logical_and(rhs);
    1273             : 
    1274          17 :                 result.logic_xor(lhs, rhs);
    1275          17 :         }
    1276        1441 : }
    1277             : 
    1278             : 
    1279        1516 : void expression::logical_or(variable& result)
    1280             : {
    1281        1516 :         logical_xor(result);
    1282             : 
    1283        1441 :         while(f_token == TOK_LOGIC_OR)          // ||
    1284             :         {
    1285          22 :                 variable lhs(result), rhs;
    1286          22 :                 next_token();
    1287          22 :                 logical_xor(rhs);
    1288             : 
    1289          22 :                 result.logic_or(lhs, rhs);
    1290          22 :         }
    1291        1419 : }
    1292             : 
    1293             : 
    1294        1516 : void expression::conditional(variable& result)
    1295             : {
    1296        1516 :         logical_or(result);
    1297             : 
    1298        1419 :         if(f_token == '?')
    1299             :         {
    1300           3 :                 variable if_true, if_false, test;
    1301             :                 long value;
    1302             : 
    1303             :                 // TODO: we should avoid evaluating these expressions
    1304             :                 //       depending on the value of result.
    1305             :                 //next_token(); -- comma calls this function already
    1306           3 :                 comma(if_true);
    1307           3 :                 if(f_token != ':')
    1308             :                 {
    1309           1 :                         throw syntax_error("expected ':' in conditional");
    1310             :                 }
    1311           2 :                 next_token();
    1312           2 :                 assignment(if_false);
    1313           2 :                 test.logic_not(result);
    1314           2 :                 test.get(value);
    1315           2 :                 if(value == 0) // WARNING: we use logic_not() so this is inverted
    1316             :                 {
    1317           1 :                         result = if_true;
    1318             :                 }
    1319             :                 else {
    1320           1 :                         result = if_false;
    1321           3 :                 }
    1322             :         }
    1323        1418 : }
    1324             : 
    1325             : 
    1326        1516 : void expression::assignment(variable& result)
    1327             : {
    1328        1516 :         variable value, old_value, new_value;
    1329        1516 :         std::string name;
    1330             :         int assign;
    1331             : 
    1332        1516 :         conditional(result);
    1333             : 
    1334        1418 :         switch(f_token) {
    1335             :         case '=':
    1336         216 :                 next_token();
    1337         216 :                 assignment(value);
    1338         215 :                 name = result.get_name();
    1339         215 :                 if(name.empty())
    1340             :                 {
    1341           1 :                         throw expected_a_variable("expected a variable to apply the assignment operator (1)");
    1342             :                 }
    1343         214 :                 f_evaluator.set(name, value);
    1344         214 :                 result = value;
    1345         214 :                 break;
    1346             : 
    1347             :         case TOK_ASSIGN_MUL:
    1348             :         case TOK_ASSIGN_DIV:
    1349             :         case TOK_ASSIGN_MOD:
    1350             :         case TOK_ASSIGN_ADD:
    1351             :         case TOK_ASSIGN_SUB:
    1352             :         case TOK_ASSIGN_SHL:
    1353             :         case TOK_ASSIGN_SHR:
    1354             :         case TOK_ASSIGN_AND:
    1355             :         case TOK_ASSIGN_XOR:
    1356             :         case TOK_ASSIGN_OR:
    1357          41 :                 assign = f_token;
    1358          41 :                 next_token();
    1359          41 :                 assignment(value);
    1360          41 :                 name = result.get_name();
    1361          41 :                 if(name.empty())
    1362             :                 {
    1363          10 :                         throw expected_a_variable("expected a variable to apply the assignment operator (2)");
    1364             :                 }
    1365          31 :                 if(!f_evaluator.get(name, old_value))
    1366             :                 {
    1367             :                         // this should not be reached by coverage tests since
    1368             :                         // TOK_IDENTIFIER already checked whether the variable existed
    1369             :                         throw undefined_variable("undefined variable \"" + name + "\" (4)"); // LCOV_EXCL_LINE
    1370             :                 }
    1371          31 :                 switch(assign) {
    1372             :                 case TOK_ASSIGN_MUL:
    1373           3 :                         new_value.mul(old_value, value);
    1374           3 :                         break;
    1375             : 
    1376             :                 case TOK_ASSIGN_DIV:
    1377           3 :                         new_value.div(old_value, value);
    1378           3 :                         break;
    1379             : 
    1380             :                 case TOK_ASSIGN_MOD:
    1381           3 :                         new_value.mod(old_value, value);
    1382           3 :                         break;
    1383             : 
    1384             :                 case TOK_ASSIGN_ADD:
    1385           3 :                         new_value.add(old_value, value);
    1386           3 :                         break;
    1387             : 
    1388             :                 case TOK_ASSIGN_SUB:
    1389           4 :                         new_value.sub(old_value, value);
    1390           4 :                         break;
    1391             : 
    1392             :                 case TOK_ASSIGN_SHL:
    1393           3 :                         new_value.shl(old_value, value);
    1394           3 :                         break;
    1395             : 
    1396             :                 case TOK_ASSIGN_SHR:
    1397           3 :                         new_value.shr(old_value, value);
    1398           3 :                         break;
    1399             : 
    1400             :                 case TOK_ASSIGN_AND:
    1401           3 :                         new_value.bitwise_and(old_value, value);
    1402           3 :                         break;
    1403             : 
    1404             :                 case TOK_ASSIGN_XOR:
    1405           3 :                         new_value.bitwise_xor(old_value, value);
    1406           3 :                         break;
    1407             : 
    1408             :                 case TOK_ASSIGN_OR:
    1409           3 :                         new_value.bitwise_or(old_value, value);
    1410           3 :                         break;
    1411             : 
    1412             :                 }
    1413          31 :                 f_evaluator.set(name, new_value);
    1414          31 :                 result = new_value;
    1415          31 :                 break;
    1416             : 
    1417        1516 :         }
    1418        1406 : }
    1419             : 
    1420             : 
    1421         994 : void expression::comma(variable& result)
    1422             : {
    1423         875 :         do {
    1424         994 :                 next_token();
    1425         984 :                 result.reset();
    1426         984 :                 assignment(result);
    1427             :         } while(f_token == ',');
    1428         636 : }
    1429             : 
    1430             : 
    1431         599 : void expression::eval(const std::string& expr, variable& result)
    1432             : {
    1433             :         //f_evaluator -- defined in constructor, do not reset between calls
    1434         599 :         f_token = EOF;
    1435         599 :         f_var.reset();
    1436         599 :         f_unget = EOF;
    1437         599 :         f_pos = 0;
    1438         599 :         f_expr = expr;
    1439         599 :         f_line = 1;
    1440         599 :         f_character = 1;
    1441             : 
    1442         599 :         comma(result);
    1443             : 
    1444             :         // allow any number of ';' at the end of the expression
    1445         482 :         bool has_semicolon(false);
    1446         485 :         while(f_token == ';')
    1447             :         {
    1448           3 :                 has_semicolon = true;
    1449           3 :                 next_token();
    1450             :         }
    1451             : 
    1452         482 :         if(f_token != EOF)
    1453             :         {
    1454           7 :                 if(!has_semicolon && f_token == ')')
    1455             :                 {
    1456           2 :                         throw syntax_error("missing '(', found ')' at the end of the expression");
    1457             :                 }
    1458           5 :                 throw syntax_error("expected the end of the expression, found another token instead");
    1459             :         }
    1460         475 : }
    1461             : 
    1462             : 
    1463             : 
    1464         599 : expr_evaluator::~expr_evaluator()
    1465             : {
    1466         599 : }
    1467             : 
    1468         599 : void expr_evaluator::eval(const std::string& expr, variable& result)
    1469             : {
    1470         599 :         expression e(*this);
    1471         599 :         e.eval(expr, result);
    1472         475 : }
    1473             : 
    1474             : 
    1475             : namespace {
    1476           4 : void func_acos(expr_evaluator::arglist& list, variable& result)
    1477             : {
    1478             :         double flt;
    1479           4 :         list[0].get(flt);
    1480           4 :         result.set(acos(flt));
    1481           4 : }
    1482             : 
    1483           4 : void func_acosh(expr_evaluator::arglist& list, variable& result)
    1484             : {
    1485             :         double flt;
    1486           4 :         list[0].get(flt);
    1487           4 :         result.set(acosh(flt));
    1488           4 : }
    1489             : 
    1490           4 : void func_asin(expr_evaluator::arglist& list, variable& result)
    1491             : {
    1492             :         double flt;
    1493           4 :         list[0].get(flt);
    1494           4 :         result.set(asin(flt));
    1495           4 : }
    1496             : 
    1497           4 : void func_asinh(expr_evaluator::arglist& list, variable& result)
    1498             : {
    1499             :         double flt;
    1500           4 :         list[0].get(flt);
    1501           4 :         result.set(asinh(flt));
    1502           4 : }
    1503             : 
    1504           4 : void func_atan(expr_evaluator::arglist& list, variable& result)
    1505             : {
    1506             :         double flt;
    1507           4 :         list[0].get(flt);
    1508           4 :         result.set(atan(flt));
    1509           4 : }
    1510             : 
    1511           4 : void func_atan2(expr_evaluator::arglist& list, variable& result)
    1512             : {
    1513             :         double x, y;
    1514           4 :         list[0].get(x);
    1515           4 :         list[1].get(y);
    1516           4 :         result.set(atan2(x, y));
    1517           4 : }
    1518             : 
    1519           4 : void func_atanh(expr_evaluator::arglist& list, variable& result)
    1520             : {
    1521             :         double flt;
    1522           4 :         list[0].get(flt);
    1523           4 :         result.set(atanh(flt));
    1524           4 : }
    1525             : 
    1526           7 : void func_ceil(expr_evaluator::arglist& list, variable& result)
    1527             : {
    1528             :         double flt;
    1529           7 :         list[0].get(flt);
    1530           7 :         result.set(ceil(flt));
    1531           7 : }
    1532             : 
    1533           4 : void func_cos(expr_evaluator::arglist& list, variable& result)
    1534             : {
    1535             :         double flt;
    1536           4 :         list[0].get(flt);
    1537           4 :         result.set(cos(flt));
    1538           4 : }
    1539             : 
    1540           4 : void func_cosh(expr_evaluator::arglist& list, variable& result)
    1541             : {
    1542             :         double flt;
    1543           4 :         list[0].get(flt);
    1544           4 :         result.set(cosh(flt));
    1545           4 : }
    1546             : 
    1547           1 : void func_ctime(expr_evaluator::arglist& list, variable& result)
    1548             : {
    1549             :         long t;
    1550           1 :         list[0].get(t);
    1551           1 :         time_t tt(t);
    1552           1 :         std::string r(ctime(&tt));
    1553           1 :         if(r.size() > 0 && *r.rbegin() == '\n')
    1554             :         {
    1555           1 :                 r = r.substr(0, r.size() - 1);
    1556             :         }
    1557           1 :         if(r.size() > 0 && *r.rbegin() == '\r')
    1558             :         {
    1559             :                 // IMPORTANT NOTE: this may not be accessible by the coverage
    1560             :                 // depending on whether the function generates a \\r and a \\n
    1561           0 :                 r = r.substr(0, r.size() - 1);
    1562             :         }
    1563           1 :         result.set(r);
    1564           1 : }
    1565             : 
    1566           3 : void func_exp(expr_evaluator::arglist& list, variable& result)
    1567             : {
    1568             :         double flt;
    1569           3 :         list[0].get(flt);
    1570           3 :         result.set(exp(flt));
    1571           3 : }
    1572             : 
    1573           4 : void func_fabs(expr_evaluator::arglist& list, variable& result)
    1574             : {
    1575             :         double flt;
    1576           4 :         list[0].get(flt);
    1577           4 :         result.set(fabs(flt));
    1578           4 : }
    1579             : 
    1580           7 : void func_floor(expr_evaluator::arglist& list, variable& result)
    1581             : {
    1582             :         double flt;
    1583           7 :         list[0].get(flt);
    1584           7 :         result.set(floor(flt));
    1585           7 : }
    1586             : 
    1587           4 : void func_fmod(expr_evaluator::arglist& list, variable& result)
    1588             : {
    1589             :         double nominator, denominator;
    1590           4 :         list[0].get(nominator);
    1591           4 :         list[1].get(denominator);
    1592           4 :         result.set(fmod(nominator, denominator));
    1593           4 : }
    1594             : 
    1595           4 : void func_log(expr_evaluator::arglist& list, variable& result)
    1596             : {
    1597             :         double flt;
    1598           4 :         list[0].get(flt);
    1599           4 :         result.set(log(flt));
    1600           4 : }
    1601             : 
    1602           4 : void func_log10(expr_evaluator::arglist& list, variable& result)
    1603             : {
    1604             :         double flt;
    1605           4 :         list[0].get(flt);
    1606           4 :         result.set(log10(flt));
    1607           4 : }
    1608             : 
    1609           2 : void func_lrint(expr_evaluator::arglist& list, variable& result)
    1610             : {
    1611             :         double flt;
    1612           2 :         list[0].get(flt);
    1613           2 :         result.set(lrint(flt));
    1614           2 : }
    1615             : 
    1616           4 : void func_pow(expr_evaluator::arglist& list, variable& result)
    1617             : {
    1618             :         double value, power;
    1619           4 :         list[0].get(value);
    1620           4 :         list[1].get(power);
    1621           4 :         result.set(pow(value, power));
    1622           4 : }
    1623             : 
    1624           2 : void func_rint(expr_evaluator::arglist& list, variable& result)
    1625             : {
    1626             :         double flt;
    1627           2 :         list[0].get(flt);
    1628           2 :         result.set(rint(flt));
    1629           2 : }
    1630             : 
    1631           6 : void func_shell(expr_evaluator::arglist& list, variable& result)
    1632             : {
    1633           6 :         std::string command;
    1634           6 :         std::string mode("output");
    1635           6 :         list[0].get(command);
    1636           6 :         if(list.size() == 2)
    1637             :         {
    1638           4 :                 list[1].get(mode);
    1639             :         }
    1640           6 :         if(mode == "output")
    1641             :         {
    1642             : #if defined(MO_WINDOWS)
    1643             : #       define popen _popen
    1644             : #       define pclose _pclose
    1645             : #endif
    1646           3 :                 FILE *p = popen(command.c_str(), "r");
    1647           3 :                 if(p == NULL)
    1648             :                 {
    1649           0 :                         throw libexpr_runtime_error("command \"" + command + "\" could not be started with popen().");
    1650             :                 }
    1651           3 :                 std::string output;
    1652           2 :                 for(;;)
    1653             :                 {
    1654             :                         // TODO: use wchar_t under MS-Windows and convert to UTF-8
    1655             :                         char buf[4096];
    1656           5 :                         buf[sizeof(buf) - 1] = '\0';
    1657           5 :                         char *r(fgets(buf, sizeof(buf) - 1, p));
    1658           5 :                         if(r == 0)
    1659             :                         {
    1660           3 :                                 break;
    1661             :                         }
    1662           2 :                         output += buf;
    1663             :                 }
    1664           3 :                 int e(pclose(p));
    1665             :         // unfortunately it doesn't look too good to test this to know that the command failed?!
    1666             : #if defined(MO_WINDOWS)
    1667             :                 if( e == -1 )
    1668             : #else
    1669             : #ifdef __GNUC__
    1670             : #if (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 6)
    1671             : #       pragma GCC diagnostic push
    1672             : #       pragma GCC diagnostic ignored "-Wold-style-cast"
    1673             : #endif
    1674             : #endif
    1675           3 :                 if(!WIFEXITED(e) || WEXITSTATUS(e) == 127)
    1676             : #ifdef __GNUC__
    1677             : #if (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 6)
    1678             : #       pragma GCC diagnostic pop
    1679             : #endif
    1680             : #endif
    1681             : #endif
    1682             :                 {
    1683           1 :                         throw libexpr_runtime_error("command \"" + command + "\" could not be started with popen().");
    1684             :                 }
    1685           3 :                 result.set(output);
    1686             :         }
    1687           3 :         else if(mode == "exitcode")
    1688             :         {
    1689           2 :                 long r(system(command.c_str()));
    1690           2 :                 result.set(r);
    1691             :         }
    1692             :         else
    1693             :         {
    1694           1 :                 throw function_args("the second argument to shell() must be \"output\" or \"exitcode\", not \"" + mode + "\"");
    1695           6 :         }
    1696           4 : }
    1697             : 
    1698           4 : void func_sin(expr_evaluator::arglist& list, variable& result)
    1699             : {
    1700             :         double flt;
    1701           4 :         list[0].get(flt);
    1702           4 :         result.set(sin(flt));
    1703           4 : }
    1704             : 
    1705           4 : void func_sinh(expr_evaluator::arglist& list, variable& result)
    1706             : {
    1707             :         double flt;
    1708           4 :         list[0].get(flt);
    1709           4 :         result.set(sinh(flt));
    1710           4 : }
    1711             : 
    1712           4 : void func_sqrt(expr_evaluator::arglist& list, variable& result)
    1713             : {
    1714             :         double flt;
    1715           4 :         list[0].get(flt);
    1716           4 :         result.set(sqrt(flt));
    1717           4 : }
    1718             : 
    1719           3 : void func_strlen(expr_evaluator::arglist& list, variable& result)
    1720             : {
    1721           3 :         std::string str;
    1722           3 :         list[0].get(str);
    1723           3 :         result.set(static_cast<long>(str.size()));
    1724           3 : }
    1725             : 
    1726           4 : void func_tan(expr_evaluator::arglist& list, variable& result)
    1727             : {
    1728             :         double flt;
    1729           4 :         list[0].get(flt);
    1730           4 :         result.set(tan(flt));
    1731           4 : }
    1732             : 
    1733           4 : void func_tanh(expr_evaluator::arglist& list, variable& result)
    1734             : {
    1735             :         double flt;
    1736           4 :         list[0].get(flt);
    1737           4 :         result.set(tanh(flt));
    1738           4 : }
    1739             : 
    1740           1 : void func_time(expr_evaluator::arglist& /*list*/, variable& result)
    1741             : {
    1742           1 :         result.set(static_cast<long>(time(NULL)));
    1743           1 : }
    1744             : }       // namespace <noname>
    1745             : 
    1746         110 : void expr_evaluator::call_function(std::string& name, arglist& list, variable& result)
    1747             : {
    1748             :         struct func_t {
    1749             :                 const char *    f_name;
    1750             :                 size_t          f_min;
    1751             :                 size_t          f_max;
    1752             :                 void            (*f_func)(arglist& list, variable& result);
    1753             :         };
    1754             : 
    1755             :         static const func_t functions[] = {
    1756             :                 { "acos",             1, 1, func_acos },
    1757             :                 { "acosh",            1, 1, func_acosh },
    1758             :                 { "asin",             1, 1, func_asin },
    1759             :                 { "asinh",            1, 1, func_asinh },
    1760             :                 { "atan",             1, 1, func_atan },
    1761             :                 { "atan2",            2, 2, func_atan2 },
    1762             :                 { "atanh",            1, 1, func_atanh },
    1763             :                 { "ceil",             1, 1, func_ceil },
    1764             :                 { "cos",              1, 1, func_cos },
    1765             :                 { "cosh",             1, 1, func_cosh },
    1766             :                 { "ctime",            1, 1, func_ctime },
    1767             :                 { "exp",              1, 1, func_exp },
    1768             :                 { "fabs",             1, 1, func_fabs },
    1769             :                 { "floor",            1, 1, func_floor },
    1770             :                 { "fmod",             2, 2, func_fmod },
    1771             :                 { "log",              1, 1, func_log },
    1772             :                 { "log10",            1, 1, func_log10 },
    1773             :                 { "lrint",            1, 1, func_lrint },
    1774             :                 { "pow",              2, 2, func_pow },
    1775             :                 { "rint",             1, 1, func_rint },
    1776             :                 { "shell",            1, 2, func_shell },
    1777             :                 { "sin",              1, 1, func_sin },
    1778             :                 { "sinh",             1, 1, func_sinh },
    1779             :                 { "sqrt",             1, 1, func_sqrt },
    1780             :                 { "strlen",           1, 1, func_strlen },
    1781             :                 { "tan",              1, 1, func_tan },
    1782             :                 { "tanh",             1, 1, func_tanh },
    1783             :                 { "time",             0, 0, func_time }
    1784             :         };
    1785             : 
    1786             :         // find system functions to execute and call them with arglist
    1787             : 
    1788             :         int i, j, p, r;
    1789             : 
    1790         110 :         i = 0;
    1791         110 :         j = sizeof(functions) / sizeof(functions[0]);
    1792         437 :         while(i < j) {
    1793             :                 // get the center position of the current range
    1794         436 :                 p = i + (j - i) / 2;
    1795         436 :                 r = strcmp(name.c_str(), functions[p].f_name);
    1796         436 :                 if(r == 0) {
    1797             :                         // found it! verify the number of arguments
    1798         218 :                         if(list.size() < functions[p].f_min
    1799         109 :                         || list.size() > functions[p].f_max) {
    1800           1 :                                 throw function_args("the invalid number of arguments was specified");
    1801             :                         }
    1802         108 :                         (*functions[p].f_func)(list, result);
    1803         106 :                         return;
    1804             :                 }
    1805         327 :                 if(r > 0) {
    1806             :                         // move the range up (we already checked p so use p + 1)
    1807         145 :                         p++;
    1808         145 :                         i = p;
    1809             :                 }
    1810             :                 else {
    1811             :                         // move the range down (we never check an item at position j)
    1812         182 :                         j = p;
    1813             :                 }
    1814             :         }
    1815             : 
    1816           1 :         std::stringstream msg;
    1817           1 :         msg << "cannot call undefined function \"" << name << "\"" << std::endl;
    1818           1 :         throw undefined_function(msg.str());
    1819             : }
    1820             : 
    1821             : 
    1822             : 
    1823        4170 : }               // namespace libexpr
    1824             : // vim: ts=8 sw=8

Generated by: LCOV version 1.9

The wpkg tool is an open source tool created by Made to Order Software Corporation.