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 : }
|