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