LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/ods - ods_formula.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 133 148 89.9 %
Date: 2024-11-21 22:18:42 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Component: ODS formula Engine
       4             :  * Purpose:
       5             :  * Author: Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (C) 2010 Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2012, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include <cctype>
      15             : #include <cmath>
      16             : 
      17             : #include "cpl_conv.h"
      18             : #include "ods_formula.h"
      19             : 
      20             : namespace
      21             : {
      22             : #include "ods_formula_parser.hpp"
      23             : 
      24             : int ods_formulalex(ods_formula_node **ppNode,
      25             :                    ods_formula_parse_context *context);
      26             : 
      27             : #include "ods_formula_parser.cpp"
      28             : } /* end of anonymous namespace */
      29             : 
      30             : #define YYSTYPE ods_formula_node *
      31             : 
      32             : static const SingleOpStruct apsSingleOp[] = {
      33             :     {"ABS", ODS_ABS, fabs},   {"SQRT", ODS_SQRT, sqrt},
      34             :     {"COS", ODS_COS, cos},    {"SIN", ODS_SIN, sin},
      35             :     {"TAN", ODS_TAN, tan},    {"ACOS", ODS_ACOS, acos},
      36             :     {"ASIN", ODS_ASIN, asin}, {"ATAN", ODS_ATAN, atan},
      37             :     {"EXP", ODS_EXP, exp},    {"LN", ODS_LN, log},
      38             :     {"LOG", ODS_LOG, log10},  {"LOG10", ODS_LOG, log10},
      39             : };
      40             : 
      41          43 : const SingleOpStruct *ODSGetSingleOpEntry(const char *pszName)
      42             : {
      43         492 :     for (size_t i = 0; i < sizeof(apsSingleOp) / sizeof(apsSingleOp[0]); i++)
      44             :     {
      45         458 :         if (EQUAL(pszName, apsSingleOp[i].pszName))
      46           9 :             return &apsSingleOp[i];
      47             :     }
      48          34 :     return nullptr;
      49             : }
      50             : 
      51           9 : const SingleOpStruct *ODSGetSingleOpEntry(ods_formula_op eOp)
      52             : {
      53          50 :     for (size_t i = 0; i < sizeof(apsSingleOp) / sizeof(apsSingleOp[0]); i++)
      54             :     {
      55          50 :         if (eOp == apsSingleOp[i].eOp)
      56           9 :             return &apsSingleOp[i];
      57             :     }
      58           0 :     return nullptr;
      59             : }
      60             : 
      61             : /************************************************************************/
      62             : /*                               swqlex()                               */
      63             : /*                                                                      */
      64             : /*      Read back a token from the input.                               */
      65             : /************************************************************************/
      66             : namespace
      67             : {
      68         788 : int ods_formulalex(YYSTYPE *ppNode, ods_formula_parse_context *context)
      69             : {
      70         788 :     const char *pszInput = context->pszNext;
      71             : 
      72         788 :     *ppNode = nullptr;
      73             : 
      74             :     /* -------------------------------------------------------------------- */
      75             :     /*      Do we have a start symbol to return?                            */
      76             :     /* -------------------------------------------------------------------- */
      77         788 :     if (context->nStartToken != 0)
      78             :     {
      79         110 :         int nRet = context->nStartToken;
      80         110 :         context->nStartToken = 0;
      81         110 :         return nRet;
      82             :     }
      83             : 
      84             :     /* -------------------------------------------------------------------- */
      85             :     /*      Skip white space.                                               */
      86             :     /* -------------------------------------------------------------------- */
      87         678 :     while (*pszInput == ' ' || *pszInput == '\t' || *pszInput == 10 ||
      88         678 :            *pszInput == 13)
      89           0 :         pszInput++;
      90             : 
      91         678 :     if (*pszInput == '\0')
      92             :     {
      93         110 :         context->pszNext = pszInput;
      94         110 :         return EOF;
      95             :     }
      96             : 
      97             :     /* -------------------------------------------------------------------- */
      98             :     /*      Handle string constants.                                        */
      99             :     /* -------------------------------------------------------------------- */
     100         568 :     if (*pszInput == '"')
     101             :     {
     102          34 :         pszInput++;
     103             : 
     104          34 :         char *token = static_cast<char *>(CPLMalloc(strlen(pszInput) + 1));
     105          34 :         int i_token = 0;
     106             : 
     107          68 :         while (*pszInput != '\0')
     108             :         {
     109          68 :             if (*pszInput == '\\' && pszInput[1] == '"')
     110           0 :                 pszInput++;
     111          68 :             else if (*pszInput == '\\' && pszInput[1] == '\'')
     112           0 :                 pszInput++;
     113          68 :             else if (*pszInput == '\'' && pszInput[1] == '\'')
     114           0 :                 pszInput++;
     115          68 :             else if (*pszInput == '"')
     116             :             {
     117          34 :                 pszInput++;
     118          34 :                 break;
     119             :             }
     120          34 :             else if (*pszInput == '\'')
     121             :             {
     122           0 :                 pszInput++;
     123           0 :                 break;
     124             :             }
     125             : 
     126          34 :             token[i_token++] = *(pszInput++);
     127             :         }
     128          34 :         token[i_token] = '\0';
     129             : 
     130          34 :         *ppNode = new ods_formula_node(token);
     131          34 :         CPLFree(token);
     132             : 
     133          34 :         context->pszNext = pszInput;
     134             : 
     135          34 :         return ODST_STRING;
     136             :     }
     137             : 
     138             :     /* -------------------------------------------------------------------- */
     139             :     /*      Handle numbers.                                                 */
     140             :     /* -------------------------------------------------------------------- */
     141         534 :     else if (*pszInput >= '0' && *pszInput <= '9')
     142             :     {
     143         137 :         const char *pszNext = pszInput + 1;
     144             : 
     145         137 :         CPLString osToken;
     146         137 :         osToken += *pszInput;
     147             : 
     148             :         // collect non-decimal part of number
     149         141 :         while (*pszNext >= '0' && *pszNext <= '9')
     150           4 :             osToken += *(pszNext++);
     151             : 
     152             :         // collect decimal places.
     153         137 :         if (*pszNext == '.')
     154             :         {
     155          48 :             osToken += *(pszNext++);
     156          96 :             while (*pszNext >= '0' && *pszNext <= '9')
     157          48 :                 osToken += *(pszNext++);
     158             :         }
     159             : 
     160             :         // collect exponent
     161         137 :         if (*pszNext == 'e' || *pszNext == 'E')
     162             :         {
     163           0 :             osToken += *(pszNext++);
     164           0 :             if (*pszNext == '-' || *pszNext == '+')
     165           0 :                 osToken += *(pszNext++);
     166           0 :             while (*pszNext >= '0' && *pszNext <= '9')
     167           0 :                 osToken += *(pszNext++);
     168             :         }
     169             : 
     170         137 :         context->pszNext = pszNext;
     171             : 
     172         226 :         if (strstr(osToken, ".") || strstr(osToken, "e") ||
     173          89 :             strstr(osToken, "E"))
     174             :         {
     175          48 :             *ppNode = new ods_formula_node(CPLAtof(osToken));
     176             :         }
     177             :         else
     178             :         {
     179          89 :             GIntBig nVal = CPLAtoGIntBig(osToken);
     180          89 :             if (osToken.size() >= 12 || nVal < INT_MIN || nVal > INT_MAX)
     181           0 :                 *ppNode = new ods_formula_node(CPLAtof(osToken));
     182             :             else
     183          89 :                 *ppNode = new ods_formula_node(static_cast<int>(nVal));
     184             :         }
     185             : 
     186         137 :         return ODST_NUMBER;
     187             :     }
     188             : 
     189             :     /* -------------------------------------------------------------------- */
     190             :     /*      Handle alpha-numerics.                                          */
     191             :     /* -------------------------------------------------------------------- */
     192         397 :     else if (*pszInput == '.' || isalnum(static_cast<unsigned char>(*pszInput)))
     193             :     {
     194         100 :         int nReturn = ODST_IDENTIFIER;
     195         100 :         const char *pszNext = pszInput + 1;
     196             : 
     197         200 :         CPLString osToken;
     198         100 :         osToken += *pszInput;
     199             : 
     200             :         // collect text characters
     201         242 :         while (isalnum(static_cast<unsigned char>(*pszNext)) ||
     202         342 :                *pszNext == '_' || ((unsigned char)*pszNext) > 127)
     203         242 :             osToken += *(pszNext++);
     204             : 
     205         100 :         context->pszNext = pszNext;
     206             : 
     207             :         /* Constants */
     208         100 :         if (EQUAL(osToken, "TRUE"))
     209             :         {
     210          10 :             *ppNode = new ods_formula_node(1);
     211          10 :             return ODST_NUMBER;
     212             :         }
     213          90 :         else if (EQUAL(osToken, "FALSE"))
     214             :         {
     215          10 :             *ppNode = new ods_formula_node(0);
     216          10 :             return ODST_NUMBER;
     217             :         }
     218             : 
     219          80 :         else if (EQUAL(osToken, "NOT"))
     220           2 :             nReturn = ODST_NOT;
     221          78 :         else if (EQUAL(osToken, "AND"))
     222           4 :             nReturn = ODST_AND;
     223          74 :         else if (EQUAL(osToken, "OR"))
     224           4 :             nReturn = ODST_OR;
     225          70 :         else if (EQUAL(osToken, "IF"))
     226           4 :             nReturn = ODST_IF;
     227             : 
     228             :         /* No-arg functions */
     229          66 :         else if (EQUAL(osToken, "PI"))
     230             :         {
     231           1 :             *ppNode = new ods_formula_node(ODS_PI);
     232           1 :             return ODST_FUNCTION_NO_ARG;
     233             :         }
     234             : 
     235             :         /* Single-arg functions */
     236          65 :         else if (EQUAL(osToken, "LEN"))
     237             :         {
     238           1 :             *ppNode = new ods_formula_node(ODS_LEN);
     239           1 :             return ODST_FUNCTION_SINGLE_ARG;
     240             :         }
     241             :         /*
     242             :         else if( EQUAL(osToken,"T") )
     243             :         {
     244             :             *ppNode = new ods_formula_node( ODS_T );
     245             :             return ODST_FUNCTION_SINGLE_ARG;
     246             :         }*/
     247             : 
     248             :         /* Tow-arg functions */
     249          64 :         else if (EQUAL(osToken, "MOD"))
     250             :         {
     251           1 :             *ppNode = new ods_formula_node(ODS_MODULUS);
     252           1 :             return ODST_FUNCTION_TWO_ARG;
     253             :         }
     254          63 :         else if (EQUAL(osToken, "LEFT"))
     255             :         {
     256           3 :             *ppNode = new ods_formula_node(ODS_LEFT);
     257           3 :             return ODST_FUNCTION_TWO_ARG;
     258             :         }
     259          60 :         else if (EQUAL(osToken, "RIGHT"))
     260             :         {
     261           3 :             *ppNode = new ods_formula_node(ODS_RIGHT);
     262           3 :             return ODST_FUNCTION_TWO_ARG;
     263             :         }
     264             : 
     265             :         /* Three-arg functions */
     266          57 :         else if (EQUAL(osToken, "MID"))
     267             :         {
     268           8 :             *ppNode = new ods_formula_node(ODS_MID);
     269           8 :             return ODST_FUNCTION_THREE_ARG;
     270             :         }
     271             : 
     272             :         /* Multiple-arg functions */
     273          49 :         else if (EQUAL(osToken, "SUM"))
     274             :         {
     275           1 :             *ppNode = new ods_formula_node(ODS_SUM);
     276           1 :             nReturn = ODST_FUNCTION_ARG_LIST;
     277             :         }
     278          48 :         else if (EQUAL(osToken, "AVERAGE"))
     279             :         {
     280           1 :             *ppNode = new ods_formula_node(ODS_AVERAGE);
     281           1 :             nReturn = ODST_FUNCTION_ARG_LIST;
     282             :         }
     283          47 :         else if (EQUAL(osToken, "MIN"))
     284             :         {
     285           1 :             *ppNode = new ods_formula_node(ODS_MIN);
     286           1 :             nReturn = ODST_FUNCTION_ARG_LIST;
     287             :         }
     288          46 :         else if (EQUAL(osToken, "MAX"))
     289             :         {
     290           1 :             *ppNode = new ods_formula_node(ODS_MAX);
     291           1 :             nReturn = ODST_FUNCTION_ARG_LIST;
     292             :         }
     293          45 :         else if (EQUAL(osToken, "COUNT"))
     294             :         {
     295           1 :             *ppNode = new ods_formula_node(ODS_COUNT);
     296           1 :             nReturn = ODST_FUNCTION_ARG_LIST;
     297             :         }
     298          44 :         else if (EQUAL(osToken, "COUNTA"))
     299             :         {
     300           1 :             *ppNode = new ods_formula_node(ODS_COUNTA);
     301           1 :             nReturn = ODST_FUNCTION_ARG_LIST;
     302             :         }
     303             : 
     304             :         else
     305             :         {
     306          43 :             const SingleOpStruct *psSingleOp = ODSGetSingleOpEntry(osToken);
     307          43 :             if (psSingleOp != nullptr)
     308             :             {
     309           9 :                 *ppNode = new ods_formula_node(psSingleOp->eOp);
     310           9 :                 nReturn = ODST_FUNCTION_SINGLE_ARG;
     311             :             }
     312             :             else
     313             :             {
     314          34 :                 *ppNode = new ods_formula_node(osToken);
     315          34 :                 nReturn = ODST_IDENTIFIER;
     316             :             }
     317             :         }
     318             : 
     319          63 :         return nReturn;
     320             :     }
     321             : 
     322             :     /* -------------------------------------------------------------------- */
     323             :     /*      Handle special tokens.                                          */
     324             :     /* -------------------------------------------------------------------- */
     325             :     else
     326             :     {
     327         297 :         context->pszNext = pszInput + 1;
     328         297 :         return *pszInput;
     329             :     }
     330             : }
     331             : } /* end of anonymous namespace */
     332             : 
     333             : /************************************************************************/
     334             : /*                        ods_formula_compile()                         */
     335             : /************************************************************************/
     336             : 
     337         110 : ods_formula_node *ods_formula_compile(const char *expr)
     338             : 
     339             : {
     340         110 :     ods_formula_parse_context context;
     341             : 
     342         110 :     context.pszInput = expr;
     343         110 :     context.pszNext = expr;
     344         110 :     context.nStartToken = ODST_START;
     345             : 
     346         110 :     if (ods_formulaparse(&context) == 0)
     347             :     {
     348         110 :         return context.poRoot;
     349             :     }
     350             : 
     351           0 :     delete context.poRoot;
     352           0 :     return nullptr;
     353             : }

Generated by: LCOV version 1.14