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

Generated by: LCOV version 1.14