VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/misc/expreval.cpp@ 93174

Last change on this file since 93174 was 93174, checked in by vboxsync, 3 years ago

iprt/RTExprEval: Early code for a 'simple' expression evaluator a la /bin/expr and such. [scm+doxygen] bugref:9781

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 79.1 KB
Line 
1/* $Id: expreval.cpp 93174 2022-01-11 01:09:45Z vboxsync $ */
2/** @file
3 * expreval - Expressions evaluator.
4 */
5
6/*
7 * Copyright (C) 2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include "internal/iprt.h"
32#include <iprt/expreval.h>
33
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36#include <iprt/ctype.h>
37#include <iprt/err.h>
38#include <iprt/mem.h>
39#include <iprt/path.h>
40#include <iprt/string.h>
41
42
43/*********************************************************************************************************************************
44* Defined Constants And Macros *
45*********************************************************************************************************************************/
46/** The max length of a string representation of a number. */
47#define EXPR_NUM_LEN ((sizeof("-9223372036854775802") + 4) & ~3)
48
49/** The max operator stack depth. */
50#define EXPR_MAX_OPERATORS 72
51/** The max operand depth. */
52#define EXPR_MAX_OPERANDS 128
53/** the max variable recursion. */
54#define EXPR_MAX_VAR_RECURSION 20
55
56/** Check if @a a_ch is a valid separator for a alphabetical binary
57 * operator, omitting isspace. */
58#define EXPR_IS_OP_SEPARATOR_NO_SPACE(a_ch) \
59 (RT_C_IS_PUNCT((a_ch)) && (a_ch) != '@' && (a_ch) != '_')
60
61/** Check if @a a_ch is a valid separator for a alphabetical binary operator. */
62#define EXPR_IS_OP_SEPARATOR(a_ch) \
63 (RT_C_IS_SPACE((a_ch)) || EXPR_IS_OP_SEPARATOR_NO_SPACE(a_ch))
64
65
66/*********************************************************************************************************************************
67* Structures and Typedefs *
68*********************************************************************************************************************************/
69/** The 64-bit signed integer type we're using. */
70typedef int64_t EXPRINT64;
71
72/** Pointer to a evaluator instance. */
73typedef struct EXPR *PEXPR;
74
75
76/**
77 * Operand variable type.
78 */
79typedef enum
80{
81 /** Invalid zero entry. */
82 kExprVar_Invalid = 0,
83 /** A number. */
84 kExprVar_Num,
85 /** A string in need of expanding (perhaps). */
86 kExprVar_String,
87 /** A simple string that doesn't need expanding. */
88 kExprVar_SimpleString,
89 /** A quoted string in need of expanding (perhaps). */
90 kExprVar_QuotedString,
91 /** A simple quoted string that doesn't need expanding. */
92 kExprVar_QuotedSimpleString,
93 /** The end of the valid variable types. */
94 kExprVar_End
95} EXPRVARTYPE;
96
97/**
98 * Operand variable.
99 */
100typedef struct
101{
102 /** The variable type. */
103 EXPRVARTYPE enmType;
104 /** The variable. */
105 union
106 {
107 /** Pointer to the string. */
108 char *psz;
109 /** The variable. */
110 EXPRINT64 i;
111 } uVal;
112} EXPRVAR;
113/** Pointer to a operand variable. */
114typedef EXPRVAR *PEXPRVAR;
115/** Pointer to a const operand variable. */
116typedef EXPRVAR const *PCEXPRVAR;
117
118/**
119 * Operator return statuses.
120 */
121typedef enum
122{
123 kExprRet_Error = -1,
124 kExprRet_Ok = 0,
125 kExprRet_Operator,
126 kExprRet_Operand,
127 kExprRet_EndOfExpr,
128 kExprRet_End
129} EXPRRET;
130
131/**
132 * Operator.
133 */
134typedef struct
135{
136 /** The operator. */
137 char szOp[11];
138 /** The length of the operator string. */
139 uint8_t cchOp;
140 /** The pair operator.
141 * This is used with '(' and '?'. */
142 char chPair;
143 /** The precedence. Higher means higher. */
144 char iPrecedence;
145 /** The number of arguments it takes. */
146 signed char cArgs;
147 /** Pointer to the method implementing the operator. */
148 EXPRRET (*pfn)(PEXPR pThis);
149} EXPROP;
150/** Pointer to a const operator. */
151typedef EXPROP const *PCEXPROP;
152
153
154/** Magic value for RTEXPREVALINT::u32Magic.
155 * @todo fixme */
156#define RTEXPREVAL_MAGIC UINT32_C(0x12345678)
157
158/**
159 * Expression evaluator instance.
160 */
161typedef struct RTEXPREVALINT
162{
163 /** Magic number (RTEXPREVAL_MAGIC). */
164 uint32_t u32Magic;
165 /** Reference counter. */
166 uint32_t volatile cRefs;
167 /** RTEXPREVAL_XXX. */
168 uint64_t fFlags;
169 /** Name for logging purposes (copy) */
170 char *pszName;
171 /** User argument to callbacks. */
172 void *pvUser;
173 /** Callback for getting variables or checking if they exists. */
174 PFNRTEXPREVALQUERYVARIABLE pfnQueryVariable;
175} RTEXPREVALINT;
176
177/**
178 * An expression being evaluated.
179 */
180typedef struct EXPR
181{
182 /** The full expression. */
183 const char *pszExpr;
184 /** The current location. */
185 const char *psz;
186 /** Error info keeper. */
187 PRTERRINFO pErrInfo;
188 /** Pointer to the instance we evaluating under. */
189 RTEXPREVALINT *pEvaluator;
190 /** Pending binary operator. */
191 PCEXPROP pPending;
192 /** Top of the operator stack. */
193 int iOp;
194 /** Top of the operand stack. */
195 int iVar;
196 /** The operator stack. */
197 PCEXPROP apOps[EXPR_MAX_OPERATORS];
198 /** The operand stack. */
199 EXPRVAR aVars[EXPR_MAX_OPERANDS];
200} EXPR;
201
202
203/*********************************************************************************************************************************
204* Global Variables *
205*********************************************************************************************************************************/
206/** Operator start character map.
207 * This indicates which characters that are starting operators and which aren't.
208 *
209 * Bit 0: Indicates that this char is used in operators.
210 * Bit 1: When bit 0 is clear, this indicates whitespace.
211 * When bit 1 is set, this indicates whether the operator can be used
212 * immediately next to an operand without any clear separation.
213 * Bits 2 thru 7: Index into g_aExprOps of the first operator starting with
214 * this character.
215 */
216static uint8_t g_abOpStartCharMap[256] = {0};
217/** Whether we've initialized the map. */
218static int g_fExprInitializedMap = 0;
219
220
221/*********************************************************************************************************************************
222* Internal Functions *
223*********************************************************************************************************************************/
224static void expr_unget_op(PEXPR pThis);
225static EXPRRET expr_get_binary_or_eoe_or_rparen(PEXPR pThis);
226
227
228
229
230/**
231 * Displays an error message.
232 *
233 * The total string length must not exceed 256 bytes.
234 *
235 * @returns kExprRet_Error
236 * @param pThis The evaluator instance.
237 * @param pszError The message format string.
238 * @param ... The message format args.
239 */
240static EXPRRET expr_error(PEXPR pThis, const char *pszError, ...)
241{
242 va_list va;
243 va_start(va, pszError);
244 RTErrInfoSetV(pThis->pErrInfo, VERR_PARSE_ERROR, pszError, va);
245 va_end(va);
246 return kExprRet_Error;
247}
248
249
250/**
251 * Converts a number to a string.
252 *
253 * @returns pszDst.
254 * @param pszDst The string buffer to write into. Assumes length of EXPR_NUM_LEN.
255 * @param iSrc The number to convert.
256 */
257static char *expr_num_to_string(char *pszDst, EXPRINT64 iSrc)
258{
259 char szTmp[64]; /* RTStrFormatNumber assumes this as a minimum size. */
260 AssertCompile(EXPR_NUM_LEN < sizeof(szTmp));
261 size_t cchTmp = RTStrFormatNumber(szTmp, iSrc, 10 /*uBase*/, 0 /*cchWidth*/, 0 /*cchPrecision*/,
262 RTSTR_F_64BIT | RTSTR_F_VALSIGNED);
263 return (char *)memcpy(pszDst, szTmp, cchTmp + 1);
264}
265
266
267/**
268 * Attempts to convert a (simple) string into a number.
269 *
270 * @returns status code.
271 * @param pThis The evaluator instance.
272 * @param piDst Where to store the numeric value on success.
273 * @param pszSrc The string to try convert.
274 * @param fQuiet Whether we should be quiet or grumpy on failure.
275 */
276static EXPRRET expr_string_to_num(PEXPR pThis, EXPRINT64 *piDst, const char *pszSrc, int fQuiet)
277{
278 EXPRRET rc = kExprRet_Ok;
279 char const *psz = pszSrc;
280 EXPRINT64 i;
281 unsigned uBase;
282 int fNegative;
283
284 /*
285 * Skip blanks.
286 */
287 while (RT_C_IS_BLANK(*psz))
288 psz++;
289 const char *const pszFirst = psz;
290
291 /*
292 * Check for '-'.
293 *
294 * At this point we will not need to deal with operators, this is
295 * just an indicator of negative numbers. If some operator ends up
296 * here it's because it came from a string expansion and thus shall
297 * not be interpreted. If this turns out to be an stupid restriction
298 * it can be fixed, but for now it stays like this.
299 */
300 fNegative = *psz == '-';
301 if (fNegative)
302 psz++;
303
304 /*
305 * Determin base.
306 * Recognize some exsotic prefixes here in addition to the two standard ones.
307 */
308 uint64_t const fFlags = pThis->pEvaluator->fFlags;
309 uBase = fFlags & RTEXPREVAL_F_DEFAULT_BASE_16 ? 16 : 10;
310 char const ch0 = psz[0];
311 if (ch0 == '0')
312 {
313 char const ch1 = psz[1];
314 switch (ch1)
315 {
316 case '\0':
317 break;
318
319 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': /* C-style octal */
320 if (fFlags & RTEXPREVAL_F_C_OCTAL)
321 {
322 uBase = 8;
323 psz++;
324 }
325 break;
326
327 case '8':
328 case '9':
329 break;
330
331 case 'x':
332 case 'X':
333 uBase = 16;
334 psz += 2;
335 break;
336 case 'y': case 'Y': /* windbg, VBoxDbg */
337 case 'b': case 'B': /* python and others */
338 uBase = 2;
339 psz += 2;
340 break;
341 case 'n': case 'N': /* windbg */
342 case 'i': case 'I': /* VBoxDbg */
343 uBase = 10;
344 psz += 2;
345 break;
346 case 't': case 'T': /* windbg, VBoxDbg */
347 case 'o': case 'O': /* python and others */
348 uBase = 8;
349 psz += 2;
350 break;
351 }
352 }
353
354 /*
355 * Convert digits.
356 */
357 i = 0;
358 for (;;)
359 {
360 unsigned iDigit;
361 int ch = *psz;
362 switch (ch)
363 {
364 case '0': iDigit = 0; break;
365 case '1': iDigit = 1; break;
366 case '2': iDigit = 2; break;
367 case '3': iDigit = 3; break;
368 case '4': iDigit = 4; break;
369 case '5': iDigit = 5; break;
370 case '6': iDigit = 6; break;
371 case '7': iDigit = 7; break;
372 case '8': iDigit = 8; break;
373 case '9': iDigit = 9; break;
374 case 'a':
375 case 'A': iDigit = 10; break;
376 case 'b':
377 case 'B': iDigit = 11; break;
378 case 'c':
379 case 'C': iDigit = 12; break;
380 case 'd':
381 case 'D': iDigit = 13; break;
382 case 'e':
383 case 'E': iDigit = 14; break;
384 case 'F': iDigit = 15; break;
385 case 'f':
386 /* Make 'false' -> 0: */
387 if ( psz != pszFirst
388 || strncmp(psz + 1, RT_STR_TUPLE("alse")) != 0)
389 {
390 iDigit = 15;
391 break;
392 }
393 psz += sizeof("false") - 1;
394 RT_FALL_THROUGH();
395
396 default:
397 /* Make 'true' evaluate to 1: */
398 if (psz == pszFirst && strncmp(psz, RT_STR_TUPLE("true")) == 0)
399 {
400 psz += sizeof("true") - 1;
401 i = 1;
402 }
403
404 /*
405 * Is the rest white space?
406 */
407 while (RT_C_IS_SPACE(*psz))
408 psz++;
409 if (*psz != '\0')
410 {
411 iDigit = uBase;
412 break;
413 }
414 RT_FALL_THROUGH();
415
416 case '\0':
417 if (fNegative)
418 i = -i;
419 *piDst = i;
420 return rc;
421 }
422 if (iDigit >= uBase)
423 {
424 if (fNegative)
425 i = -i;
426 *piDst = i;
427 if (!fQuiet)
428 expr_error(pThis, "Invalid %u-base number \"%.80s\"", uBase, pszSrc);
429 return kExprRet_Error;
430 }
431
432 /* add the digit and advance */
433 /** @todo check for overflow? */
434 i *= uBase;
435 i += iDigit;
436 psz++;
437 }
438 /* not reached */
439}
440
441
442/**
443 * Checks if the variable is a string or not.
444 *
445 * @returns 1 if it's a string, 0 otherwise.
446 * @param pVar The variable.
447 */
448static int expr_var_is_string(PCEXPRVAR pVar)
449{
450 return pVar->enmType >= kExprVar_String;
451}
452
453
454/**
455 * Checks if the variable contains a string that was quoted
456 * in the expression.
457 *
458 * @returns 1 if if was a quoted string, otherwise 0.
459 * @param pVar The variable.
460 */
461static int expr_var_was_quoted(PCEXPRVAR pVar)
462{
463 return pVar->enmType >= kExprVar_QuotedString;
464}
465
466
467/**
468 * Deletes a variable.
469 *
470 * @param pVar The variable.
471 */
472static void expr_var_delete(PEXPRVAR pVar)
473{
474 if (expr_var_is_string(pVar))
475 {
476 RTMemTmpFree(pVar->uVal.psz);
477 pVar->uVal.psz = NULL;
478 }
479 pVar->enmType = kExprVar_Invalid;
480}
481
482
483/**
484 * Initializes a new variables with a sub-string value.
485 *
486 * @returns kExprRet_Ok or kExprRet_Error.
487 * @param pThis The evaluator expression instance.
488 * @param pVar The new variable.
489 * @param psz The start of the string value.
490 * @param cch The number of chars to copy.
491 * @param enmType The string type.
492 */
493static EXPRRET expr_var_init_substring(PEXPR pThis, PEXPRVAR pVar, const char *psz, size_t cch, EXPRVARTYPE enmType)
494{
495 /* convert string needing expanding into simple ones if possible. */
496 if ( enmType == kExprVar_String
497 && !memchr(psz, '$', cch))
498 enmType = kExprVar_SimpleString;
499 else if ( enmType == kExprVar_QuotedString
500 && !memchr(psz, '$', cch))
501 enmType = kExprVar_QuotedSimpleString;
502
503 pVar->enmType = enmType;
504 pVar->uVal.psz = (char *)RTMemTmpAlloc(cch + 1);
505 if (RT_LIKELY(pVar->uVal.psz))
506 {
507 memcpy(pVar->uVal.psz, psz, cch);
508 pVar->uVal.psz[cch] = '\0';
509 return kExprRet_Ok;
510 }
511 pVar->enmType = kExprVar_End;
512 RTErrInfoSetF(pThis->pErrInfo, VERR_NO_TMP_MEMORY, "Failed to allocate %zu bytes", cch + 1);
513 return kExprRet_Error;
514}
515
516
517#if 0 /* unused */
518/**
519 * Initializes a new variables with a string value.
520 *
521 * @returns kExprRet_Ok or kExprRet_Error.
522 * @param pVar The new variable.
523 * @param psz The string value.
524 * @param enmType The string type.
525 */
526static EXPRRET expr_var_init_string(PEXPRVAR pVar, const char *psz, EXPRVARTYPE enmType)
527{
528 return expr_var_init_substring(pVar, psz, strlen(psz), enmType);
529}
530
531
532/**
533 * Assigns a sub-string value to a variable.
534 *
535 * @returns kExprRet_Ok or kExprRet_Error.
536 * @param pVar The new variable.
537 * @param psz The start of the string value.
538 * @param cch The number of chars to copy.
539 * @param enmType The string type.
540 */
541static void expr_var_assign_substring(PEXPRVAR pVar, const char *psz, size_t cch, EXPRVARTYPE enmType)
542{
543 expr_var_delete(pVar);
544 return expr_var_init_substring(pVar, psz, cch, enmType);
545}
546
547
548/**
549 * Assignes a string value to a variable.
550 *
551 * @returns kExprRet_Ok or kExprRet_Error.
552 * @param pVar The variable.
553 * @param psz The string value.
554 * @param enmType The string type.
555 */
556static void expr_var_assign_string(PEXPRVAR pVar, const char *psz, EXPRVARTYPE enmType)
557{
558 expr_var_delete(pVar);
559 return expr_var_init_string(pVar, psz, enmType);
560}
561#endif /* unused */
562
563
564/**
565 * Finds the end of the current variable expansion, taking nested expansoins
566 * into account.
567 *
568 * This is somewhat similar to the code down in expr_get_unary_or_operand.
569 *
570 * @returns kExprRet_Ok or kExprRet_Error.
571 * @param pThis The evaluator expression instance.
572 * @param pchSrc Pointer to the dollar of the variable expansion.
573 * @param cchSrc The length of the variable expansion expression.
574 * @param pcchVarRef Where to return the length of the variable expansion.
575 * @param pfNested Where to return whether it's a nested (@c true) or plain
576 * one.
577 *
578 * @note This code would be somewhat simpler if we only used one style of
579 * wrappers (either parentheses or curly brackets).
580 */
581DECL_NO_INLINE(static, EXPRRET) expr_expand_find_end(PEXPR pThis, const char *pchSrc, size_t cchSrc,
582 size_t *pcchVarRef, bool *pfNested)
583{
584 const char * const pchStart = pchSrc;
585 char achPars[EXPR_MAX_VAR_RECURSION];
586
587 /*
588 * Push the initial expression.
589 */
590 Assert(cchSrc >= 2);
591 Assert(pchSrc[0] == '$');
592 Assert(pchSrc[1] == '(' || pchSrc[1] == '}');
593 unsigned cPars = 1;
594 char chEndPar = pchSrc[1] == '(' ? ')' : '}';
595 achPars[0] = chEndPar;
596 pchSrc += 2;
597 cchSrc -= 2;
598
599 /*
600 * Parse the rest of the string till we've back at cPars == 0.
601 */
602 *pfNested = false;
603 while (cchSrc > 0)
604 {
605 char ch2;
606 char const ch = *pchSrc;
607 if ( ch == '$'
608 && cchSrc >= 2
609 && ( (ch2 = pchSrc[1]) == '('
610 || ch2 == '{'))
611 {
612 if (cPars < RT_ELEMENTS(achPars))
613 achPars[cPars++] = chEndPar = ch2 == '(' ? ')' : '}';
614 else
615 {
616 *pcchVarRef = 0;
617 return expr_error(pThis, "Too deep nesting of variable expansions");
618 }
619 *pfNested = true;
620 pchSrc += 2;
621 cchSrc -= 2;
622 }
623 else
624 {
625 pchSrc += 1;
626 cchSrc -= 1;
627 if (ch == chEndPar)
628 {
629 if (--cPars == 0)
630 {
631 *pcchVarRef = pchSrc - pchStart;
632 return kExprRet_Ok;
633 }
634 chEndPar = achPars[cPars];
635 }
636 Assert(chEndPar != '\0');
637 }
638 }
639 *pcchVarRef = 0;
640 return expr_error(pThis, "Unbalanced variable expansions: %.*s", pchStart, pchSrc - pchStart);
641}
642
643
644/**
645 * Returns the given string with all variables references replaced.
646 *
647 * @returns Pointer to expanded string on success (RTMemTmpFree), NULL on
648 * failure (error already set).
649 * @param pThis The evaluator expression instance.
650 * @param pchSrc The string to expand.
651 * @param cchSrc The length of the string to expand.
652 * @param cDepth The recursion depth, starting at zero.
653 *
654 * @note This code would be somewhat simpler if we only used one style of
655 * wrappers (either parentheses or curly brackets).
656 */
657static char *expr_expand_string(PEXPR pThis, const char *pchSrc, size_t cchSrc, unsigned cDepth)
658{
659 if (cDepth < EXPR_MAX_VAR_RECURSION)
660 {
661 size_t cbRetAlloc = RT_ALIGN_Z(cchSrc + 1 + 16, 16);
662 char *pszRet = (char *)RTMemTmpAlloc(cbRetAlloc);
663 if (pszRet)
664 {
665 size_t offRet = 0;
666 while (cchSrc > 0)
667 {
668 /*
669 * Look for the next potential variable reference.
670 */
671 const char *pchDollar = (const char *)memchr(pchSrc, '$', cchSrc);
672 size_t cchPlain = pchDollar ? pchDollar - pchSrc : cchSrc;
673 size_t cchNext = cchPlain;
674
675 if (pchDollar)
676 {
677 /* Treat lone $ w/o a following ( or { as plain text. */
678 if ( cchPlain + 1 >= cchSrc
679 && pchDollar[0] == '$'
680 && ( cchPlain + 1 == cchSrc
681 || (pchDollar[1] != '{' && pchDollar[1] != '(')) )
682 {
683 cchPlain += 1;
684 cchNext += 1;
685 pchDollar += 1;
686 }
687 /* Eat up escaped dollars: $$ -> $ */
688 else
689 while (cchNext + 2 <= cchSrc && pchDollar[1] == '$' && pchDollar[0] == '$')
690 {
691 cchPlain += 1;
692 cchNext += 2;
693 pchDollar += 2;
694 }
695 }
696
697 /* Finally copy out plain text.*/
698 if (cchPlain > 0)
699 {
700 if (cchPlain >= cbRetAlloc - offRet)
701 {
702 size_t const cbNeeded = RT_ALIGN_Z(offRet + cchPlain + (!pchDollar ? 1 : offRet <= 64 ? 16 : 64), 16);
703 void *pvNew = RTMemTmpAlloc(cbNeeded);
704 if (pvNew)
705 memcpy(pvNew, pszRet, offRet);
706 RTMemTmpFree(pszRet);
707 pszRet = (char *)pvNew;
708 if (pvNew)
709 cbRetAlloc = cbNeeded;
710 else
711 {
712 RTErrInfoSetF(pThis->pErrInfo, VERR_NO_TMP_MEMORY, "Failed to allocate %zu bytes", cbNeeded);
713 return NULL;
714 }
715 }
716
717 memcpy(&pszRet[offRet], pchSrc, cchPlain);
718 offRet += cchPlain;
719 pszRet[offRet] = '\0';
720 pchSrc += cchNext;
721 cchSrc -= cchNext;
722 if (!cchSrc)
723 break;
724
725 /* If we don't have ${ or $( loop, just loop. */
726 if ( cchSrc < 2
727 || pchSrc[0] != '$'
728 || (pchSrc[1] != '{' && pchSrc[1] != '('))
729 continue;
730 }
731
732 /*
733 * If we get down here we have a ${ or $( at pchSrc. The fun part now is
734 * finding the end of it and recursively dealing with any sub-expansions first.
735 */
736 Assert(pchSrc[0] == '$' && (pchSrc[1] == '{' || pchSrc[1] == '('));
737 size_t cchVarRef;
738 bool fNested;
739 if (expr_expand_find_end(pThis, pchSrc, cchSrc, &cchVarRef, &fNested) == kExprRet_Ok)
740 {
741 /* Lookup the variable. Simple when it's a plain one, for nested ones we
742 first have to expand the variable name itself before looking it up. */
743 char *pszValue;
744 int vrc;
745 if (!fNested)
746 vrc = pThis->pEvaluator->pfnQueryVariable(&pchSrc[2], cchSrc - 3, pThis->pEvaluator->pvUser, &pszValue);
747 else
748 {
749 char *pszName = expr_expand_string(pThis, &pchSrc[2], cchSrc - 3, cDepth + 1);
750 if (!pszName)
751 {
752 RTMemTmpFree(pszRet);
753 return NULL;
754 }
755 vrc = pThis->pEvaluator->pfnQueryVariable(pszName, strlen(pszName), pThis->pEvaluator->pvUser, &pszValue);
756 RTMemTmpFree(pszName);
757 }
758
759 /* Treat variables that aren't found as empty strings for now.
760 This may need to become configurable later. */
761 char *pszValueFree = pszValue;
762 static char s_szNotFound[] = "";
763 if (vrc == VERR_NOT_FOUND)
764 {
765 pszValue = s_szNotFound;
766 vrc = VINF_SUCCESS;
767 }
768
769 if (RT_SUCCESS(vrc))
770 {
771 /*
772 * Append the value to the return string.
773 */
774 size_t cchValue = strlen(pszValue);
775 if (cchValue > 0)
776 {
777 if (cchValue >= cbRetAlloc - offRet)
778 {
779 size_t const cbNeeded = RT_ALIGN_Z(offRet + cchValue + (!pchDollar ? 1 : offRet <= 64 ? 16 : 64),
780 16);
781 void *pvNew = RTMemTmpAlloc(cbNeeded);
782 if (pvNew)
783 memcpy(pvNew, pszRet, offRet);
784 RTMemTmpFree(pszRet);
785 pszRet = (char *)pvNew;
786 if (pvNew)
787 cbRetAlloc = cbNeeded;
788 else
789 {
790 RTErrInfoSetF(pThis->pErrInfo, VERR_NO_TMP_MEMORY, "Failed to allocate %zu bytes", cbNeeded);
791 RTStrFree(pszValueFree);
792 return NULL;
793 }
794 }
795
796 memcpy(&pszRet[offRet], pszValue, cchValue);
797 offRet += cchValue;
798 pszRet[offRet] = '\0';
799 }
800 pchSrc += cchVarRef;
801 cchSrc -= cchVarRef;
802 RTStrFree(pszValueFree);
803 continue;
804 }
805 }
806 RTMemTmpFree(pszRet);
807 return NULL;
808 }
809 return pszRet;
810 }
811 RTErrInfoSetF(pThis->pErrInfo, VERR_NO_TMP_MEMORY, "Failed to allocate %zu bytes", cbRetAlloc);
812 }
813 else
814 RTErrInfoSet(pThis->pErrInfo, VERR_TOO_MUCH_DATA, "Too deeply nested variable expression");
815 return NULL;
816}
817
818
819/**
820 * Simplifies a string variable.
821 *
822 * @returns kExprRet_Ok or kExprRet_Error.
823 * @param pThis The evaluator expression instance.
824 * @param pVar The variable.
825 */
826static EXPRRET expr_var_make_simple_string(PEXPR pThis, PEXPRVAR pVar)
827{
828 switch (pVar->enmType)
829 {
830 case kExprVar_Num:
831 {
832 char *psz = (char *)RTMemTmpAlloc(EXPR_NUM_LEN);
833 if (psz)
834 {
835 expr_num_to_string(psz, pVar->uVal.i);
836 pVar->uVal.psz = psz;
837 pVar->enmType = kExprVar_SimpleString;
838 }
839 else
840 {
841 RTErrInfoSetF(pThis->pErrInfo, VERR_NO_TMP_MEMORY, "Failed to allocate %zu bytes", EXPR_NUM_LEN);
842 return kExprRet_Error;
843 }
844 break;
845 }
846
847 case kExprVar_String:
848 case kExprVar_QuotedString:
849 {
850 Assert(strchr(pVar->uVal.psz, '$'));
851 char *psz = expr_expand_string(pThis, pVar->uVal.psz, strlen(pVar->uVal.psz), 0);
852 if (psz)
853 {
854 RTMemTmpFree(pVar->uVal.psz);
855 pVar->uVal.psz = psz;
856
857 pVar->enmType = pVar->enmType == kExprVar_String
858 ? kExprVar_SimpleString
859 : kExprVar_QuotedSimpleString;
860 }
861 else
862 return kExprRet_Error;
863 break;
864 }
865
866 case kExprVar_SimpleString:
867 case kExprVar_QuotedSimpleString:
868 /* nothing to do. */
869 break;
870
871 default:
872 AssertMsgFailed(("%d\n", pVar->enmType));
873 }
874 return kExprRet_Ok;
875}
876
877
878#if 0 /* unused */
879/**
880 * Turns a variable into a string value.
881 *
882 * @param pVar The variable.
883 */
884static void expr_var_make_string(PEXPRVAR pVar)
885{
886 switch (pVar->enmType)
887 {
888 case kExprVar_Num:
889 expr_var_make_simple_string(pVar);
890 break;
891
892 case kExprVar_String:
893 case kExprVar_SimpleString:
894 case kExprVar_QuotedString:
895 case kExprVar_QuotedSimpleString:
896 /* nothing to do. */
897 break;
898
899 default:
900 AssertMsgFailed(("%d\n", pVar->enmType));
901 }
902}
903#endif /* unused */
904
905
906/**
907 * Initializes a new variables with a integer value.
908 *
909 * @param pVar The new variable.
910 * @param i The integer value.
911 */
912static void expr_var_init_num(PEXPRVAR pVar, EXPRINT64 i)
913{
914 pVar->enmType = kExprVar_Num;
915 pVar->uVal.i = i;
916}
917
918
919/**
920 * Assigns a integer value to a variable.
921 *
922 * @param pVar The variable.
923 * @param i The integer value.
924 */
925static void expr_var_assign_num(PEXPRVAR pVar, EXPRINT64 i)
926{
927 expr_var_delete(pVar);
928 expr_var_init_num(pVar, i);
929}
930
931
932/**
933 * Turns the variable into a number.
934 *
935 * @returns status code.
936 * @param pThis The evaluator instance.
937 * @param pVar The variable.
938 */
939static EXPRRET expr_var_make_num(PEXPR pThis, PEXPRVAR pVar)
940{
941 switch (pVar->enmType)
942 {
943 case kExprVar_Num:
944 /* nothing to do. */
945 break;
946
947 case kExprVar_String:
948 {
949 EXPRRET rc = expr_var_make_simple_string(pThis, pVar);
950 if (rc != kExprRet_Ok)
951 return rc;
952 RT_FALL_THROUGH();
953 }
954 case kExprVar_SimpleString:
955 {
956 EXPRINT64 i;
957 EXPRRET rc = expr_string_to_num(pThis, &i, pVar->uVal.psz, 0 /* fQuiet */);
958 if (rc < kExprRet_Ok)
959 return rc;
960 expr_var_assign_num(pVar, i);
961 break;
962 }
963
964 case kExprVar_QuotedString:
965 case kExprVar_QuotedSimpleString:
966 return expr_error(pThis, "Cannot convert a quoted string to a number");
967
968 default:
969 AssertMsgFailedReturn(("%d\n", pVar->enmType), kExprRet_Error);
970 }
971
972 return kExprRet_Ok;
973}
974
975
976/**
977 * Try to turn the variable into a number.
978 *
979 * @returns status code.
980 * @param pThis The instance.
981 * @param pVar The variable.
982 */
983static EXPRRET expr_var_try_make_num(PEXPR pThis, PEXPRVAR pVar)
984{
985 EXPRRET rc;
986 switch (pVar->enmType)
987 {
988 case kExprVar_Num:
989 /* nothing to do. */
990 break;
991
992 case kExprVar_String:
993 rc = expr_var_make_simple_string(pThis, pVar);
994 if (rc != kExprRet_Ok)
995 return rc;
996 RT_FALL_THROUGH();
997 case kExprVar_SimpleString:
998 {
999 EXPRINT64 i;
1000 rc = expr_string_to_num(pThis, &i, pVar->uVal.psz, 1 /* fQuiet */);
1001 if (rc < kExprRet_Ok)
1002 return rc;
1003 expr_var_assign_num(pVar, i);
1004 break;
1005 }
1006
1007 case kExprVar_QuotedString:
1008 case kExprVar_QuotedSimpleString:
1009 /* can't do this */
1010 return kExprRet_Error;
1011
1012 default:
1013 AssertMsgFailedReturn(("%d\n", pVar->enmType), kExprRet_Error);
1014 }
1015
1016 return kExprRet_Ok;
1017}
1018
1019
1020/**
1021 * Initializes a new variables with a boolean value.
1022 *
1023 * @param pVar The new variable.
1024 * @param f The boolean value.
1025 */
1026static void expr_var_init_bool(PEXPRVAR pVar, int f)
1027{
1028 pVar->enmType = kExprVar_Num;
1029 pVar->uVal.i = !!f;
1030}
1031
1032
1033/**
1034 * Assigns a boolean value to a variable.
1035 *
1036 * @param pVar The variable.
1037 * @param f The boolean value.
1038 */
1039static void expr_var_assign_bool(PEXPRVAR pVar, int f)
1040{
1041 expr_var_delete(pVar);
1042 expr_var_init_bool(pVar, f);
1043}
1044
1045
1046/**
1047 * Turns the variable into an boolean.
1048 *
1049 * @returns the boolean interpretation.
1050 * @param pThis The instance.
1051 * @param pVar The variable.
1052 */
1053static EXPRRET expr_var_make_bool(PEXPR pThis, PEXPRVAR pVar)
1054{
1055 EXPRRET rc = kExprRet_Ok;
1056
1057 switch (pVar->enmType)
1058 {
1059 case kExprVar_Num:
1060 pVar->uVal.i = !!pVar->uVal.i;
1061 break;
1062
1063 case kExprVar_String:
1064 rc = expr_var_make_simple_string(pThis, pVar);
1065 if (rc != kExprRet_Ok)
1066 break;
1067 RT_FALL_THROUGH();
1068 case kExprVar_SimpleString:
1069 {
1070 /*
1071 * Try convert it to a number. If that fails, check for 'true' or
1072 * 'false', if neither then use python / GNU make logic wrt strings.
1073 */
1074 EXPRINT64 iVal;
1075 char const *psz = pVar->uVal.psz;
1076 while (RT_C_IS_BLANK(*psz))
1077 psz++;
1078 if ( *psz
1079 && expr_string_to_num(pThis, &iVal, psz, 1 /* fQuiet */) >= kExprRet_Ok)
1080 expr_var_assign_bool(pVar, iVal != 0);
1081 else if ( strncmp(psz, RT_STR_TUPLE("true")) == 0
1082 && *RTStrStripL(&psz[sizeof("true") - 1]) == '\0')
1083 expr_var_assign_bool(pVar, true);
1084 else if ( strncmp(psz, RT_STR_TUPLE("false")) == 0
1085 && *RTStrStripL(&psz[sizeof("false") - 1]) == '\0')
1086 expr_var_assign_bool(pVar, false);
1087 else
1088 expr_var_assign_bool(pVar, *psz != '\0');
1089 break;
1090 }
1091
1092 case kExprVar_QuotedString:
1093 rc = expr_var_make_simple_string(pThis, pVar);
1094 if (rc != kExprRet_Ok)
1095 break;
1096 RT_FALL_THROUGH();
1097 case kExprVar_QuotedSimpleString:
1098 /*
1099 * Use python / GNU make boolean logic: non-empty string means true.
1100 * No stripping here, as the string is quoted as should be taken exactly as given.
1101 */
1102 expr_var_assign_bool(pVar, *pVar->uVal.psz != '\0');
1103 break;
1104
1105 default:
1106 AssertMsgFailed(("%d\n", pVar->enmType));
1107 }
1108
1109 return rc;
1110}
1111
1112
1113/**
1114 * Pops a varable off the stack and deletes it.
1115 * @param pThis The evaluator instance.
1116 */
1117static void expr_pop_and_delete_var(PEXPR pThis)
1118{
1119 expr_var_delete(&pThis->aVars[pThis->iVar]);
1120 pThis->iVar--;
1121}
1122
1123
1124
1125/**
1126 * Tries to make the variables the same type.
1127 *
1128 * This will not convert numbers to strings, unless one of them
1129 * is a quoted string.
1130 *
1131 * this will try convert both to numbers if neither is quoted. Both
1132 * conversions will have to suceed for this to be commited.
1133 *
1134 * All strings will be simplified.
1135 *
1136 * @returns status code. Done complaining on failure.
1137 *
1138 * @param pThis The evaluator instance.
1139 * @param pVar1 The first variable.
1140 * @param pVar2 The second variable.
1141 * @param pszOp The operator requesting this (for errors).
1142 */
1143static EXPRRET expr_var_unify_types(PEXPR pThis, PEXPRVAR pVar1, PEXPRVAR pVar2, const char *pszOp)
1144{
1145/** @todo Add flag for selecting preference here when forcing types */
1146
1147
1148 /*
1149 * Try make the variables the same type before comparing.
1150 */
1151 if ( !expr_var_was_quoted(pVar1)
1152 && !expr_var_was_quoted(pVar2))
1153 {
1154 if ( expr_var_is_string(pVar1)
1155 || expr_var_is_string(pVar2))
1156 {
1157 if (!expr_var_is_string(pVar1))
1158 expr_var_try_make_num(pThis, pVar2);
1159 else if (!expr_var_is_string(pVar2))
1160 expr_var_try_make_num(pThis, pVar1);
1161 else
1162 {
1163 /*
1164 * Both are strings, simplify them then see if both can be made into numbers.
1165 */
1166 EXPRRET rc = expr_var_make_simple_string(pThis, pVar1);
1167 if (rc == kExprRet_Ok)
1168 rc = expr_var_make_simple_string(pThis, pVar2);
1169 if (rc == kExprRet_Ok)
1170 {
1171 EXPRINT64 iVar1;
1172 EXPRINT64 iVar2;
1173 if ( expr_string_to_num(pThis, &iVar1, pVar1->uVal.psz, 1 /* fQuiet */) >= kExprRet_Ok
1174 && expr_string_to_num(pThis, &iVar2, pVar2->uVal.psz, 1 /* fQuiet */) >= kExprRet_Ok)
1175 {
1176 expr_var_assign_num(pVar1, iVar1);
1177 expr_var_assign_num(pVar2, iVar2);
1178 }
1179 }
1180 else
1181 return rc;
1182 }
1183 }
1184 }
1185 else
1186 {
1187 EXPRRET rc = expr_var_make_simple_string(pThis, pVar1);
1188 if (rc == kExprRet_Ok)
1189 rc = expr_var_make_simple_string(pThis, pVar2);
1190 if (rc == kExprRet_Ok)
1191 { /* likely */ }
1192 else
1193 return rc;
1194 }
1195
1196 /*
1197 * Complain if they aren't the same type now.
1198 */
1199 if (expr_var_is_string(pVar1) != expr_var_is_string(pVar2))
1200 return expr_error(pThis, "Unable to unify types for \"%s\"", pszOp);
1201 return kExprRet_Ok;
1202}
1203
1204
1205
1206/*********************************************************************************************************************************
1207* Operators *
1208*********************************************************************************************************************************/
1209
1210/**
1211 * Is variable defined, unary.
1212 *
1213 * @returns Status code.
1214 * @param pThis The instance.
1215 */
1216static EXPRRET expr_op_defined(PEXPR pThis)
1217{
1218 PEXPRVAR pVar = &pThis->aVars[pThis->iVar];
1219
1220 EXPRRET rc = expr_var_make_simple_string(pThis, pVar);
1221 if (rc == kExprRet_Ok)
1222 {
1223 int vrc = pThis->pEvaluator->pfnQueryVariable(pVar->uVal.psz, strlen(pVar->uVal.psz), pThis->pEvaluator->pvUser, NULL);
1224 expr_var_assign_bool(pVar, vrc != VERR_NOT_FOUND);
1225 }
1226
1227 return rc;
1228}
1229
1230
1231/**
1232 * Does file(/dir/whatever) exist, unary.
1233 *
1234 * @returns Status code.
1235 * @param pThis The instance.
1236 */
1237static EXPRRET expr_op_exists(PEXPR pThis)
1238{
1239 EXPRRET rc;
1240 PEXPRVAR pVar = &pThis->aVars[pThis->iVar];
1241
1242 if (pThis->pEvaluator->fFlags & RTEXPREVAL_F_EXISTS_OP)
1243 {
1244 rc = expr_var_make_simple_string(pThis, pVar);
1245 if (rc == kExprRet_Ok)
1246 expr_var_assign_bool(pVar, RTPathExists(pVar->uVal.psz) == 0);
1247 }
1248 else
1249 rc = expr_error(pThis, "The 'exists' operator is not accessible");
1250
1251 return rc;
1252}
1253
1254
1255/**
1256 * Convert to boolean.
1257 *
1258 * @returns Status code.
1259 * @param pThis The instance.
1260 */
1261static EXPRRET expr_op_bool(PEXPR pThis)
1262{
1263 return expr_var_make_bool(pThis, &pThis->aVars[pThis->iVar]);
1264}
1265
1266
1267/**
1268 * Convert to number, works on quoted strings too.
1269 *
1270 * @returns Status code.
1271 * @param pThis The instance.
1272 */
1273static EXPRRET expr_op_num(PEXPR pThis)
1274{
1275 PEXPRVAR pVar = &pThis->aVars[pThis->iVar];
1276
1277 /* unquote the string */
1278 if (pVar->enmType == kExprVar_QuotedSimpleString)
1279 pVar->enmType = kExprVar_SimpleString;
1280 else if (pVar->enmType == kExprVar_QuotedString)
1281 pVar->enmType = kExprVar_String;
1282
1283 return expr_var_make_num(pThis, pVar);
1284}
1285
1286
1287/**
1288 * Performs a strlen() on the simplified/converted string argument.
1289 *
1290 * @returns Status code.
1291 * @param pThis The instance.
1292 */
1293static EXPRRET expr_op_strlen(PEXPR pThis)
1294{
1295 PEXPRVAR pVar = &pThis->aVars[pThis->iVar];
1296 EXPRRET rc = expr_var_make_simple_string(pThis, pVar);
1297 if (rc == kExprRet_Ok)
1298 expr_var_assign_num(pVar, strlen(pVar->uVal.psz));
1299
1300 return rc;
1301}
1302
1303
1304/**
1305 * Convert to string (simplified and quoted)
1306 *
1307 * @returns Status code.
1308 * @param pThis The instance.
1309 */
1310static EXPRRET expr_op_str(PEXPR pThis)
1311{
1312 PEXPRVAR pVar = &pThis->aVars[pThis->iVar];
1313 EXPRRET rc = expr_var_make_simple_string(pThis, pVar);
1314 if (rc == kExprRet_Ok)
1315 pVar->enmType = kExprVar_QuotedSimpleString;
1316
1317 return rc;
1318}
1319
1320
1321/**
1322 * Pluss (dummy / make_integer)
1323 *
1324 * @returns Status code.
1325 * @param pThis The instance.
1326 */
1327static EXPRRET expr_op_pluss(PEXPR pThis)
1328{
1329 return expr_var_make_num(pThis, &pThis->aVars[pThis->iVar]);
1330}
1331
1332
1333/**
1334 * Minus (negate)
1335 *
1336 * @returns Status code.
1337 * @param pThis The instance.
1338 */
1339static EXPRRET expr_op_minus(PEXPR pThis)
1340{
1341 PEXPRVAR pVar = &pThis->aVars[pThis->iVar];
1342 EXPRRET rc = expr_var_make_num(pThis, pVar);
1343 if (rc >= kExprRet_Ok)
1344 pVar->uVal.i = -pVar->uVal.i;
1345
1346 return rc;
1347}
1348
1349
1350
1351/**
1352 * Bitwise NOT.
1353 *
1354 * @returns Status code.
1355 * @param pThis The instance.
1356 */
1357static EXPRRET expr_op_bitwise_not(PEXPR pThis)
1358{
1359 PEXPRVAR pVar = &pThis->aVars[pThis->iVar];
1360 EXPRRET rc = expr_var_make_num(pThis, pVar);
1361 if (rc >= kExprRet_Ok)
1362 pVar->uVal.i = ~pVar->uVal.i;
1363
1364 return rc;
1365}
1366
1367
1368/**
1369 * Logical NOT.
1370 *
1371 * @returns Status code.
1372 * @param pThis The instance.
1373 */
1374static EXPRRET expr_op_logical_not(PEXPR pThis)
1375{
1376 PEXPRVAR pVar = &pThis->aVars[pThis->iVar];
1377 EXPRRET rc = expr_var_make_bool(pThis, pVar);
1378 if (rc == kExprRet_Ok)
1379 pVar->uVal.i = !pVar->uVal.i;
1380
1381 return rc;
1382}
1383
1384
1385/**
1386 * Multiplication.
1387 *
1388 * @returns Status code.
1389 * @param pThis The instance.
1390 */
1391static EXPRRET expr_op_multiply(PEXPR pThis)
1392{
1393 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1394 EXPRRET rc = expr_var_make_num(pThis, pVar1);
1395 if (rc >= kExprRet_Ok)
1396 {
1397 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1398 rc = expr_var_make_num(pThis, pVar2);
1399 if (rc >= kExprRet_Ok)
1400 pVar1->uVal.i *= pVar2->uVal.i;
1401 }
1402 expr_pop_and_delete_var(pThis);
1403 return rc;
1404}
1405
1406
1407
1408/**
1409 * Division.
1410 *
1411 * @returns Status code.
1412 * @param pThis The instance.
1413 */
1414static EXPRRET expr_op_divide(PEXPR pThis)
1415{
1416 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1417 EXPRRET rc = expr_var_make_num(pThis, pVar1);
1418 if (rc >= kExprRet_Ok)
1419 {
1420 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1421 rc = expr_var_make_num(pThis, pVar2);
1422 if (rc >= kExprRet_Ok)
1423 pVar1->uVal.i /= pVar2->uVal.i;
1424 }
1425 expr_pop_and_delete_var(pThis);
1426 return rc;
1427}
1428
1429
1430
1431/**
1432 * Modulus.
1433 *
1434 * @returns Status code.
1435 * @param pThis The instance.
1436 */
1437static EXPRRET expr_op_modulus(PEXPR pThis)
1438{
1439 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1440 EXPRRET rc = expr_var_make_num(pThis, pVar1);
1441 if (rc >= kExprRet_Ok)
1442 {
1443 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1444 rc = expr_var_make_num(pThis, pVar2);
1445 if (rc >= kExprRet_Ok)
1446 pVar1->uVal.i %= pVar2->uVal.i;
1447 }
1448 expr_pop_and_delete_var(pThis);
1449 return rc;
1450}
1451
1452
1453/**
1454 * Addition (numeric).
1455 *
1456 * @returns Status code.
1457 * @param pThis The instance.
1458 */
1459static EXPRRET expr_op_add(PEXPR pThis)
1460{
1461 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1462 EXPRRET rc = expr_var_make_num(pThis, pVar1);
1463 if (rc >= kExprRet_Ok)
1464 {
1465 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1466 rc = expr_var_make_num(pThis, pVar2);
1467 if (rc >= kExprRet_Ok)
1468 pVar1->uVal.i += pVar2->uVal.i;
1469 }
1470 expr_pop_and_delete_var(pThis);
1471 return rc;
1472}
1473
1474
1475/**
1476 * Subtract (numeric).
1477 *
1478 * @returns Status code.
1479 * @param pThis The instance.
1480 */
1481static EXPRRET expr_op_sub(PEXPR pThis)
1482{
1483 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1484 EXPRRET rc = expr_var_make_num(pThis, pVar1);
1485 if (rc >= kExprRet_Ok)
1486 {
1487 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1488 rc = expr_var_make_num(pThis, pVar2);
1489 if (rc >= kExprRet_Ok)
1490 pVar1->uVal.i -= pVar2->uVal.i;
1491 }
1492 expr_pop_and_delete_var(pThis);
1493 return rc;
1494}
1495
1496
1497/**
1498 * Bitwise left shift.
1499 *
1500 * @returns Status code.
1501 * @param pThis The instance.
1502 */
1503static EXPRRET expr_op_shift_left(PEXPR pThis)
1504{
1505 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1506 EXPRRET rc = expr_var_make_num(pThis, pVar1);
1507 if (rc >= kExprRet_Ok)
1508 {
1509 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1510 rc = expr_var_make_num(pThis, pVar2);
1511 if (rc >= kExprRet_Ok)
1512 pVar1->uVal.i <<= pVar2->uVal.i;
1513 }
1514 expr_pop_and_delete_var(pThis);
1515 return rc;
1516}
1517
1518
1519/**
1520 * Bitwise right shift.
1521 *
1522 * @returns Status code.
1523 * @param pThis The instance.
1524 */
1525static EXPRRET expr_op_shift_right(PEXPR pThis)
1526{
1527 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1528 EXPRRET rc = expr_var_make_num(pThis, pVar1);
1529 if (rc >= kExprRet_Ok)
1530 {
1531 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1532 rc = expr_var_make_num(pThis, pVar2);
1533 if (rc >= kExprRet_Ok)
1534 pVar1->uVal.i >>= pVar2->uVal.i;
1535 }
1536 expr_pop_and_delete_var(pThis);
1537 return rc;
1538}
1539
1540
1541/**
1542 * Less than or equal, version string.
1543 *
1544 * @returns Status code.
1545 * @param pThis The instance.
1546 */
1547static EXPRRET expr_op_ver_less_or_equal_than(PEXPR pThis)
1548{
1549 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1550 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1551 EXPRRET rc = expr_var_unify_types(pThis, pVar1, pVar2, "vle");
1552 if (rc >= kExprRet_Ok)
1553 {
1554 if (!expr_var_is_string(pVar1))
1555 expr_var_assign_bool(pVar1, pVar1->uVal.i <= pVar2->uVal.i);
1556 else
1557 expr_var_assign_bool(pVar1, RTStrVersionCompare(pVar1->uVal.psz, pVar2->uVal.psz) <= 0);
1558 }
1559 expr_pop_and_delete_var(pThis);
1560 return rc;
1561}
1562
1563
1564/**
1565 * Less than or equal.
1566 *
1567 * @returns Status code.
1568 * @param pThis The instance.
1569 */
1570static EXPRRET expr_op_less_or_equal_than(PEXPR pThis)
1571{
1572 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1573 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1574 EXPRRET rc = expr_var_unify_types(pThis, pVar1, pVar2, "<=");
1575 if (rc >= kExprRet_Ok)
1576 {
1577 if (!expr_var_is_string(pVar1))
1578 expr_var_assign_bool(pVar1, pVar1->uVal.i <= pVar2->uVal.i);
1579 else
1580 expr_var_assign_bool(pVar1, strcmp(pVar1->uVal.psz, pVar2->uVal.psz) <= 0);
1581 }
1582 expr_pop_and_delete_var(pThis);
1583 return rc;
1584}
1585
1586
1587/**
1588 * Less than, version string.
1589 *
1590 * @returns Status code.
1591 * @param pThis The instance.
1592 */
1593static EXPRRET expr_op_ver_less_than(PEXPR pThis)
1594{
1595 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1596 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1597 EXPRRET rc = expr_var_unify_types(pThis, pVar1, pVar2, "vlt");
1598 if (rc >= kExprRet_Ok)
1599 {
1600 if (!expr_var_is_string(pVar1))
1601 expr_var_assign_bool(pVar1, pVar1->uVal.i < pVar2->uVal.i);
1602 else
1603 expr_var_assign_bool(pVar1, RTStrVersionCompare(pVar1->uVal.psz, pVar2->uVal.psz) < 0);
1604 }
1605 expr_pop_and_delete_var(pThis);
1606 return rc;
1607}
1608
1609
1610/**
1611 * Less than.
1612 *
1613 * @returns Status code.
1614 * @param pThis The instance.
1615 */
1616static EXPRRET expr_op_less_than(PEXPR pThis)
1617{
1618 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1619 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1620 EXPRRET rc = expr_var_unify_types(pThis, pVar1, pVar2, "<");
1621 if (rc >= kExprRet_Ok)
1622 {
1623 if (!expr_var_is_string(pVar1))
1624 expr_var_assign_bool(pVar1, pVar1->uVal.i < pVar2->uVal.i);
1625 else
1626 expr_var_assign_bool(pVar1, strcmp(pVar1->uVal.psz, pVar2->uVal.psz) < 0);
1627 }
1628 expr_pop_and_delete_var(pThis);
1629 return rc;
1630}
1631
1632
1633/**
1634 * Greater or equal than, version string.
1635 *
1636 * @returns Status code.
1637 * @param pThis The instance.
1638 */
1639static EXPRRET expr_op_ver_greater_or_equal_than(PEXPR pThis)
1640{
1641 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1642 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1643 EXPRRET rc = expr_var_unify_types(pThis, pVar1, pVar2, "vge");
1644 if (rc >= kExprRet_Ok)
1645 {
1646 if (!expr_var_is_string(pVar1))
1647 expr_var_assign_bool(pVar1, pVar1->uVal.i >= pVar2->uVal.i);
1648 else
1649 expr_var_assign_bool(pVar1, RTStrVersionCompare(pVar1->uVal.psz, pVar2->uVal.psz) >= 0);
1650 }
1651 expr_pop_and_delete_var(pThis);
1652 return rc;
1653}
1654
1655
1656/**
1657 * Greater or equal than.
1658 *
1659 * @returns Status code.
1660 * @param pThis The instance.
1661 */
1662static EXPRRET expr_op_greater_or_equal_than(PEXPR pThis)
1663{
1664 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1665 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1666 EXPRRET rc = expr_var_unify_types(pThis, pVar1, pVar2, ">=");
1667 if (rc >= kExprRet_Ok)
1668 {
1669 if (!expr_var_is_string(pVar1))
1670 expr_var_assign_bool(pVar1, pVar1->uVal.i >= pVar2->uVal.i);
1671 else
1672 expr_var_assign_bool(pVar1, strcmp(pVar1->uVal.psz, pVar2->uVal.psz) >= 0);
1673 }
1674 expr_pop_and_delete_var(pThis);
1675 return rc;
1676}
1677
1678
1679/**
1680 * Greater than, version string.
1681 *
1682 * @returns Status code.
1683 * @param pThis The instance.
1684 */
1685static EXPRRET expr_op_ver_greater_than(PEXPR pThis)
1686{
1687 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1688 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1689 EXPRRET rc = expr_var_unify_types(pThis, pVar1, pVar2, "vgt");
1690 if (rc >= kExprRet_Ok)
1691 {
1692 if (!expr_var_is_string(pVar1))
1693 expr_var_assign_bool(pVar1, pVar1->uVal.i > pVar2->uVal.i);
1694 else
1695 expr_var_assign_bool(pVar1, RTStrVersionCompare(pVar1->uVal.psz, pVar2->uVal.psz) > 0);
1696 }
1697 expr_pop_and_delete_var(pThis);
1698 return rc;
1699}
1700
1701
1702/**
1703 * Greater than.
1704 *
1705 * @returns Status code.
1706 * @param pThis The instance.
1707 */
1708static EXPRRET expr_op_greater_than(PEXPR pThis)
1709{
1710 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1711 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1712 EXPRRET rc = expr_var_unify_types(pThis, pVar1, pVar2, ">");
1713 if (rc >= kExprRet_Ok)
1714 {
1715 if (!expr_var_is_string(pVar1))
1716 expr_var_assign_bool(pVar1, pVar1->uVal.i > pVar2->uVal.i);
1717 else
1718 expr_var_assign_bool(pVar1, strcmp(pVar1->uVal.psz, pVar2->uVal.psz) > 0);
1719 }
1720 expr_pop_and_delete_var(pThis);
1721 return rc;
1722}
1723
1724
1725/**
1726 * Equal, version strings.
1727 *
1728 * @returns Status code.
1729 * @param pThis The instance.
1730 */
1731static EXPRRET expr_op_ver_equal(PEXPR pThis)
1732{
1733 EXPRRET rc = kExprRet_Ok;
1734 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1735 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1736 int const fIsString1 = expr_var_is_string(pVar1);
1737
1738 /*
1739 * The same type?
1740 */
1741 if (fIsString1 == expr_var_is_string(pVar2))
1742 {
1743 if (!fIsString1)
1744 /* numbers are simple */
1745 expr_var_assign_bool(pVar1, pVar1->uVal.i == pVar2->uVal.i);
1746 else
1747 {
1748 /* try a normal string compare. */
1749 rc = expr_var_make_simple_string(pThis, pVar1);
1750 if (rc == kExprRet_Ok)
1751 rc = expr_var_make_simple_string(pThis, pVar2);
1752 if (rc == kExprRet_Ok)
1753 {
1754 if (!RTStrVersionCompare(pVar1->uVal.psz, pVar2->uVal.psz))
1755 expr_var_assign_bool(pVar1, 1);
1756 /* try convert and compare as number instead. */
1757 else if ( expr_var_try_make_num(pThis, pVar1) >= kExprRet_Ok
1758 && expr_var_try_make_num(pThis, pVar2) >= kExprRet_Ok)
1759 expr_var_assign_bool(pVar1, pVar1->uVal.i == pVar2->uVal.i);
1760 /* ok, they really aren't equal. */
1761 else
1762 expr_var_assign_bool(pVar1, 0);
1763 }
1764 }
1765 }
1766 else
1767 {
1768 /*
1769 * If the type differs, there are now two options:
1770 * 1. Try convert the string to a valid number and compare the numbers.
1771 * 2. Convert the non-string to a number and compare the strings.
1772 */
1773 if ( expr_var_try_make_num(pThis, pVar1) >= kExprRet_Ok
1774 && expr_var_try_make_num(pThis, pVar2) >= kExprRet_Ok)
1775 expr_var_assign_bool(pVar1, pVar1->uVal.i == pVar2->uVal.i);
1776 else
1777 {
1778 rc = expr_var_make_simple_string(pThis, pVar1);
1779 if (rc == kExprRet_Ok)
1780 rc = expr_var_make_simple_string(pThis, pVar2);
1781 if (rc == kExprRet_Ok)
1782 expr_var_assign_bool(pVar1, RTStrVersionCompare(pVar1->uVal.psz, pVar2->uVal.psz) == 0);
1783 }
1784 }
1785
1786 expr_pop_and_delete_var(pThis);
1787 return rc;
1788}
1789
1790
1791/**
1792 * Not equal, version string.
1793 *
1794 * @returns Status code.
1795 * @param pThis The instance.
1796 */
1797static EXPRRET expr_op_ver_not_equal(PEXPR pThis)
1798{
1799 EXPRRET rc = expr_op_ver_equal(pThis);
1800 if (rc >= kExprRet_Ok)
1801 rc = expr_op_logical_not(pThis);
1802 return rc;
1803}
1804
1805
1806/**
1807 * Equal.
1808 *
1809 * @returns Status code.
1810 * @param pThis The instance.
1811 */
1812static EXPRRET expr_op_equal(PEXPR pThis)
1813{
1814 EXPRRET rc = kExprRet_Ok;
1815 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1816 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1817 int const fIsString1 = expr_var_is_string(pVar1);
1818
1819 /*
1820 * The same type?
1821 */
1822 if (fIsString1 == expr_var_is_string(pVar2))
1823 {
1824 if (!fIsString1)
1825 /* numbers are simple */
1826 expr_var_assign_bool(pVar1, pVar1->uVal.i == pVar2->uVal.i);
1827 else
1828 {
1829 /* try a normal string compare. */
1830 rc = expr_var_make_simple_string(pThis, pVar1);
1831 if (rc == kExprRet_Ok)
1832 rc = expr_var_make_simple_string(pThis, pVar2);
1833 if (rc == kExprRet_Ok)
1834 {
1835 if (!strcmp(pVar1->uVal.psz, pVar2->uVal.psz))
1836 expr_var_assign_bool(pVar1, 1);
1837 /* try convert and compare as number instead. */
1838 else if ( expr_var_try_make_num(pThis, pVar1) >= kExprRet_Ok
1839 && expr_var_try_make_num(pThis, pVar2) >= kExprRet_Ok)
1840 expr_var_assign_bool(pVar1, pVar1->uVal.i == pVar2->uVal.i);
1841 /* ok, they really aren't equal. */
1842 else
1843 expr_var_assign_bool(pVar1, 0);
1844 }
1845 }
1846 }
1847 else
1848 {
1849 /*
1850 * If the type differs, there are now two options:
1851 * 1. Convert the string to a valid number and compare the numbers.
1852 * 2. Convert an empty string to a 'false' boolean value and compare
1853 * numerically. This one is a bit questionable, so we don't try this.
1854 */
1855 /** @todo this needs to be redone, both because we're hiding alloc errors
1856 * here but also because this should be controlled by a flag. */
1857 if ( expr_var_try_make_num(pThis, pVar1) >= kExprRet_Ok
1858 && expr_var_try_make_num(pThis, pVar2) >= kExprRet_Ok)
1859 expr_var_assign_bool(pVar1, pVar1->uVal.i == pVar2->uVal.i);
1860 else
1861 rc = expr_error(pThis, "Cannot compare strings and numbers");
1862 }
1863
1864 expr_pop_and_delete_var(pThis);
1865 return rc;
1866}
1867
1868
1869/**
1870 * Not equal.
1871 *
1872 * @returns Status code.
1873 * @param pThis The instance.
1874 */
1875static EXPRRET expr_op_not_equal(PEXPR pThis)
1876{
1877 EXPRRET rc = expr_op_equal(pThis);
1878 if (rc >= kExprRet_Ok)
1879 rc = expr_op_logical_not(pThis);
1880 return rc;
1881}
1882
1883
1884/**
1885 * Bitwise AND.
1886 *
1887 * @returns Status code.
1888 * @param pThis The instance.
1889 */
1890static EXPRRET expr_op_bitwise_and(PEXPR pThis)
1891{
1892 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1893 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1894
1895 EXPRRET rc = expr_var_make_num(pThis, pVar1);
1896 if (rc >= kExprRet_Ok)
1897 {
1898 rc = expr_var_make_num(pThis, pVar2);
1899 if (rc >= kExprRet_Ok)
1900 pVar1->uVal.i &= pVar2->uVal.i;
1901 }
1902
1903 expr_pop_and_delete_var(pThis);
1904 return rc;
1905}
1906
1907
1908/**
1909 * Bitwise XOR.
1910 *
1911 * @returns Status code.
1912 * @param pThis The instance.
1913 */
1914static EXPRRET expr_op_bitwise_xor(PEXPR pThis)
1915{
1916 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1917 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1918
1919 EXPRRET rc = expr_var_make_num(pThis, pVar1);
1920 if (rc >= kExprRet_Ok)
1921 {
1922 rc = expr_var_make_num(pThis, pVar2);
1923 if (rc >= kExprRet_Ok)
1924 pVar1->uVal.i ^= pVar2->uVal.i;
1925 }
1926
1927 expr_pop_and_delete_var(pThis);
1928 return rc;
1929}
1930
1931
1932/**
1933 * Bitwise OR.
1934 *
1935 * @returns Status code.
1936 * @param pThis The instance.
1937 */
1938static EXPRRET expr_op_bitwise_or(PEXPR pThis)
1939{
1940 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1941 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1942
1943 EXPRRET rc = expr_var_make_num(pThis, pVar1);
1944 if (rc >= kExprRet_Ok)
1945 {
1946 rc = expr_var_make_num(pThis, pVar2);
1947 if (rc >= kExprRet_Ok)
1948 pVar1->uVal.i |= pVar2->uVal.i;
1949 }
1950
1951 expr_pop_and_delete_var(pThis);
1952 return rc;
1953}
1954
1955
1956/**
1957 * Logical AND.
1958 *
1959 * @returns Status code.
1960 * @param pThis The instance.
1961 */
1962static EXPRRET expr_op_logical_and(PEXPR pThis)
1963{
1964 bool fResult = false;
1965 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1966 EXPRRET rc = expr_var_make_bool(pThis, pVar1);
1967 if ( rc == kExprRet_Ok
1968 && pVar1->uVal.i != 0)
1969 {
1970 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1971 rc = expr_var_make_bool(pThis, pVar2);
1972 if (rc == kExprRet_Ok && pVar2->uVal.i != 0)
1973 fResult = true;
1974 }
1975 expr_var_assign_bool(pVar1, fResult);
1976 expr_pop_and_delete_var(pThis);
1977 return rc;
1978}
1979
1980
1981/**
1982 * Logical OR.
1983 *
1984 * @returns Status code.
1985 * @param pThis The instance.
1986 */
1987static EXPRRET expr_op_logical_or(PEXPR pThis)
1988{
1989 bool fResult = false;
1990 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1991 EXPRRET rc = expr_var_make_bool(pThis, pVar1);
1992 if (rc == kExprRet_Ok)
1993 {
1994 if (pVar1->uVal.i)
1995 fResult = true;
1996 else
1997 {
1998 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1999 rc = expr_var_make_bool(pThis, pVar2);
2000 if (rc == kExprRet_Ok && pVar2->uVal.i != 0)
2001 fResult = true;
2002 }
2003 }
2004 expr_var_assign_bool(pVar1, fResult);
2005 expr_pop_and_delete_var(pThis);
2006 return rc;
2007}
2008
2009
2010/**
2011 * Left parenthesis.
2012 *
2013 * @returns Status code.
2014 * @param pThis The instance.
2015 */
2016static EXPRRET expr_op_left_parenthesis(PEXPR pThis)
2017{
2018 /*
2019 * There should be a right parenthesis operator lined up for us now,
2020 * eat it. If not found there is an inbalance.
2021 */
2022 EXPRRET rc = expr_get_binary_or_eoe_or_rparen(pThis);
2023 if ( rc == kExprRet_Operator
2024 && pThis->apOps[pThis->iOp]->szOp[0] == ')')
2025 {
2026 /* pop it and get another one which we can leave pending. */
2027 pThis->iOp--;
2028 rc = expr_get_binary_or_eoe_or_rparen(pThis);
2029 if (rc >= kExprRet_Ok)
2030 expr_unget_op(pThis);
2031 }
2032 else
2033 rc = expr_error(pThis, "Missing ')'");
2034
2035 return rc;
2036}
2037
2038
2039/**
2040 * Right parenthesis, dummy that's never actually called.
2041 *
2042 * @returns Status code.
2043 * @param pThis The instance.
2044 */
2045static EXPRRET expr_op_right_parenthesis(PEXPR pThis)
2046{
2047 RT_NOREF_PV(pThis);
2048 AssertFailed();
2049 return kExprRet_Ok;
2050}
2051
2052
2053
2054
2055
2056/**
2057 * The operator table.
2058 *
2059 * This table is NOT ordered by precedence, but for linear search
2060 * allowing for first match to return the correct operator. This
2061 * means that || must come before |, or else | will match all.
2062 */
2063static const EXPROP g_aExprOps[] =
2064{
2065#define EXPR_OP(szOp, iPrecedence, cArgs, pfn) { szOp, sizeof(szOp) - 1, '\0', iPrecedence, cArgs, pfn }
2066 /* Name, iPrecedence, cArgs, pfn */
2067 EXPR_OP("defined", 90, 1, expr_op_defined),
2068 EXPR_OP("exists", 90, 1, expr_op_exists),
2069 EXPR_OP("bool", 90, 1, expr_op_bool),
2070 EXPR_OP("num", 90, 1, expr_op_num),
2071 EXPR_OP("strlen", 90, 1, expr_op_strlen),
2072 EXPR_OP("str", 90, 1, expr_op_str),
2073 EXPR_OP("+", 80, 1, expr_op_pluss),
2074 EXPR_OP("-", 80, 1, expr_op_minus),
2075 EXPR_OP("~", 80, 1, expr_op_bitwise_not),
2076 EXPR_OP("*", 75, 2, expr_op_multiply),
2077 EXPR_OP("/", 75, 2, expr_op_divide),
2078 EXPR_OP("%", 75, 2, expr_op_modulus),
2079 EXPR_OP("+", 70, 2, expr_op_add),
2080 EXPR_OP("-", 70, 2, expr_op_sub),
2081 EXPR_OP("<<", 65, 2, expr_op_shift_left),
2082 EXPR_OP(">>", 65, 2, expr_op_shift_right),
2083 EXPR_OP("<=", 60, 2, expr_op_less_or_equal_than),
2084 EXPR_OP("<", 60, 2, expr_op_less_than),
2085 EXPR_OP(">=", 60, 2, expr_op_greater_or_equal_than),
2086 EXPR_OP(">", 60, 2, expr_op_greater_than),
2087 EXPR_OP("vle", 60, 2, expr_op_ver_less_or_equal_than),
2088 EXPR_OP("vlt", 60, 2, expr_op_ver_less_than),
2089 EXPR_OP("vge", 60, 2, expr_op_ver_greater_or_equal_than),
2090 EXPR_OP("vgt", 60, 2, expr_op_ver_greater_than),
2091 EXPR_OP("==", 55, 2, expr_op_equal),
2092 EXPR_OP("veq", 55, 2, expr_op_ver_equal),
2093 EXPR_OP("!=", 55, 2, expr_op_not_equal),
2094 EXPR_OP("vne", 55, 2, expr_op_ver_not_equal),
2095 EXPR_OP("!", 80, 1, expr_op_logical_not),
2096 EXPR_OP("^", 45, 2, expr_op_bitwise_xor),
2097 EXPR_OP("&&", 35, 2, expr_op_logical_and),
2098 EXPR_OP("&", 50, 2, expr_op_bitwise_and),
2099 EXPR_OP("||", 30, 2, expr_op_logical_or),
2100 EXPR_OP("|", 40, 2, expr_op_bitwise_or),
2101 { "(", 1, ')', 10, 1, expr_op_left_parenthesis },
2102 { ")", 1, '(', 10, 0, expr_op_right_parenthesis },
2103 /* { "?", 1, ':', 5, 2, expr_op_question },
2104 { ":", 1, '?', 5, 2, expr_op_colon }, -- too weird for now. */
2105#undef EXPR_OP
2106};
2107
2108/** Dummy end of expression fake. */
2109static const EXPROP g_ExprEndOfExpOp =
2110{
2111 "", 0, '\0', 0, 0, NULL
2112};
2113
2114
2115/**
2116 * Initializes the opcode character map if necessary.
2117 */
2118static void expr_map_init(void)
2119{
2120 unsigned i;
2121 if (g_fExprInitializedMap)
2122 return;
2123
2124 /*
2125 * Initialize it.
2126 */
2127 for (i = 0; i < sizeof(g_aExprOps) / sizeof(g_aExprOps[0]); i++)
2128 {
2129 unsigned int ch = (unsigned int)g_aExprOps[i].szOp[0];
2130 if (!g_abOpStartCharMap[ch])
2131 {
2132 g_abOpStartCharMap[ch] = (i << 2) | 1;
2133 if (!RT_C_IS_ALPHA(ch))
2134 g_abOpStartCharMap[ch] |= 2; /* Need no clear separation from operands. */
2135 }
2136 }
2137
2138 /* whitespace (assumes C-like locale because I'm lazy): */
2139#define SET_WHITESPACE(a_ch) do { \
2140 Assert(g_abOpStartCharMap[(unsigned char)(a_ch)] == 0); \
2141 g_abOpStartCharMap[(unsigned char)(a_ch)] |= 2; \
2142 } while (0)
2143 SET_WHITESPACE(' ');
2144 SET_WHITESPACE('\t');
2145 SET_WHITESPACE('\n');
2146 SET_WHITESPACE('\r');
2147 SET_WHITESPACE('\v');
2148 SET_WHITESPACE('\f');
2149
2150 g_fExprInitializedMap = 1;
2151}
2152
2153
2154/**
2155 * Looks up a character in the map.
2156 *
2157 * @returns the value for that char, see g_abOpStartCharMap for details.
2158 * @param ch The character.
2159 */
2160DECLINLINE(unsigned char) expr_map_get(char ch)
2161{
2162 return g_abOpStartCharMap[(unsigned char)ch];
2163}
2164
2165
2166/**
2167 * Searches the operator table given a potential operator start char.
2168 *
2169 * @returns Pointer to the matching operator. NULL if not found.
2170 * @param psz Pointer to what can be an operator.
2171 * @param uchVal The expr_map_get value.
2172 * @param fUnary Whether it must be an unary operator or not.
2173 */
2174static PCEXPROP expr_lookup_op(char const *psz, unsigned char uchVal, int fUnary)
2175{
2176 char ch = *psz;
2177 unsigned i;
2178 Assert((uchVal & 2) == (RT_C_IS_ALPHA(ch) ? 0 : 2));
2179
2180 for (i = uchVal >> 2; i < sizeof(g_aExprOps) / sizeof(g_aExprOps[0]); i++)
2181 {
2182 /* compare the string... */
2183 if (g_aExprOps[i].szOp[0] != ch)
2184 continue;
2185 switch (g_aExprOps[i].cchOp)
2186 {
2187 case 1:
2188 break;
2189 case 2:
2190 if (g_aExprOps[i].szOp[1] != psz[1])
2191 continue;
2192 break;
2193 default:
2194 if (strncmp(&g_aExprOps[i].szOp[1], psz + 1, g_aExprOps[i].cchOp - 1))
2195 continue;
2196 break;
2197 }
2198
2199 /* ... and the operator type. */
2200 if (fUnary == (g_aExprOps[i].cArgs == 1))
2201 {
2202 /* Check if we've got the needed operand separation: */
2203 if ( (uchVal & 2)
2204 || EXPR_IS_OP_SEPARATOR(psz[g_aExprOps[i].cchOp]))
2205 {
2206 /* got a match! */
2207 return &g_aExprOps[i];
2208 }
2209 }
2210 }
2211
2212 return NULL;
2213}
2214
2215
2216/**
2217 * Ungets a binary operator.
2218 *
2219 * The operator is poped from the stack and put in the pending position.
2220 *
2221 * @param pThis The evaluator instance.
2222 */
2223static void expr_unget_op(PEXPR pThis)
2224{
2225 Assert(pThis->pPending == NULL);
2226 Assert(pThis->iOp >= 0);
2227
2228 pThis->pPending = pThis->apOps[pThis->iOp];
2229 pThis->apOps[pThis->iOp] = NULL;
2230 pThis->iOp--;
2231}
2232
2233
2234
2235/**
2236 * Get the next token, it should be a binary operator, or the end of
2237 * the expression, or a right parenthesis.
2238 *
2239 * The operator is pushed onto the stack and the status code indicates
2240 * which of the two we found.
2241 *
2242 * @returns status code. Will grumble on failure.
2243 * @retval kExprRet_EndOfExpr if we encountered the end of the expression.
2244 * @retval kExprRet_Operator if we encountered a binary operator or right
2245 * parenthesis. It's on the operator stack.
2246 *
2247 * @param pThis The evaluator instance.
2248 */
2249static EXPRRET expr_get_binary_or_eoe_or_rparen(PEXPR pThis)
2250{
2251 /*
2252 * See if there is anything pending first.
2253 */
2254 PCEXPROP pOp = pThis->pPending;
2255 if (pOp)
2256 pThis->pPending = NULL;
2257 else
2258 {
2259 /*
2260 * Eat more of the expression.
2261 */
2262 char const *psz = pThis->psz;
2263
2264 /* spaces */
2265 unsigned char uchVal;
2266 char ch;
2267 while (((uchVal = expr_map_get((ch = *psz))) & 3) == 2)
2268 psz++;
2269
2270 /* see what we've got. */
2271 if (ch)
2272 {
2273 if (uchVal & 1)
2274 pOp = expr_lookup_op(psz, uchVal, 0 /* fUnary */);
2275 if (!pOp)
2276 return expr_error(pThis, "Expected binary operator, found \"%.42s\"...", psz);
2277 psz += pOp->cchOp;
2278 }
2279 else
2280 pOp = &g_ExprEndOfExpOp;
2281 pThis->psz = psz;
2282 }
2283
2284 /*
2285 * Push it.
2286 */
2287 if (pThis->iOp >= EXPR_MAX_OPERATORS - 1)
2288 return expr_error(pThis, "Operator stack overflow");
2289 pThis->apOps[++pThis->iOp] = pOp;
2290
2291 return pOp->iPrecedence
2292 ? kExprRet_Operator
2293 : kExprRet_EndOfExpr;
2294}
2295
2296
2297
2298/**
2299 * Get the next token, it should be an unary operator or an operand.
2300 *
2301 * This will fail if encountering the end of the expression since
2302 * it is implied that there should be something more.
2303 *
2304 * The token is pushed onto the respective stack and the status code
2305 * indicates which it is.
2306 *
2307 * @returns status code. On failure we'll be done bitching already.
2308 * @retval kExprRet_Operator if we encountered an unary operator.
2309 * It's on the operator stack.
2310 * @retval kExprRet_Operand if we encountered an operand operator.
2311 * It's on the operand stack.
2312 *
2313 * @param pThis The evaluator instance.
2314 */
2315static EXPRRET expr_get_unary_or_operand(PEXPR pThis)
2316{
2317 EXPRRET rc;
2318 unsigned char uchVal;
2319 PCEXPROP pOp;
2320 char const *psz = pThis->psz;
2321 char ch;
2322
2323 /*
2324 * Eat white space and make sure there is something after it.
2325 */
2326 while (((uchVal = expr_map_get((ch = *psz))) & 3) == 2)
2327 psz++;
2328 if (ch == '\0')
2329 return expr_error(pThis, "Unexpected end of expression");
2330
2331 /*
2332 * Is it an operator?
2333 */
2334 pOp = NULL;
2335 if (uchVal & 1)
2336 pOp = expr_lookup_op(psz, uchVal, 1 /* fUnary */);
2337 if (pOp)
2338 {
2339 /*
2340 * Push the operator onto the stack.
2341 */
2342 if (pThis->iVar < EXPR_MAX_OPERANDS - 1)
2343 {
2344 pThis->apOps[++pThis->iOp] = pOp;
2345 rc = kExprRet_Operator;
2346 }
2347 else
2348 rc = expr_error(pThis, "Operator stack overflow");
2349 psz += pOp->cchOp;
2350 }
2351 else if (pThis->iVar < EXPR_MAX_OPERANDS - 1)
2352 {
2353 /*
2354 * It's an operand. Figure out where it ends and
2355 * push it onto the stack.
2356 */
2357 const char *pszStart;
2358
2359 rc = kExprRet_Ok;
2360 if (ch == '"')
2361 {
2362 pszStart = ++psz;
2363 while ((ch = *psz) != '\0' && ch != '"')
2364 psz++;
2365 rc = expr_var_init_substring(pThis, &pThis->aVars[++pThis->iVar], pszStart, psz - pszStart, kExprVar_QuotedString);
2366 if (ch != '\0')
2367 psz++;
2368 }
2369 else if (ch == '\'')
2370 {
2371 pszStart = ++psz;
2372 while ((ch = *psz) != '\0' && ch != '\'')
2373 psz++;
2374 rc = expr_var_init_substring(pThis, &pThis->aVars[++pThis->iVar], pszStart, psz - pszStart,
2375 kExprVar_QuotedSimpleString);
2376 if (ch != '\0')
2377 psz++;
2378 }
2379 else
2380 {
2381 char achPars[EXPR_MAX_VAR_RECURSION];
2382 int iPar = -1;
2383 char chEndPar = '\0';
2384
2385 pszStart = psz;
2386 while ((ch = *psz) != '\0')
2387 {
2388 char ch2;
2389
2390 /* $(adsf) or ${asdf} needs special handling. */
2391 if ( ch == '$'
2392 && ( (ch2 = psz[1]) == '('
2393 || ch2 == '{'))
2394 {
2395 psz++;
2396 if (iPar > (int)(sizeof(achPars) / sizeof(achPars[0])))
2397 {
2398 rc = expr_error(pThis, "Too deep nesting of variable expansions");
2399 break;
2400 }
2401 achPars[++iPar] = chEndPar = ch2 == '(' ? ')' : '}';
2402 }
2403 else if (ch == chEndPar)
2404 {
2405 iPar--;
2406 chEndPar = iPar >= 0 ? achPars[iPar] : '\0';
2407 }
2408 else if (!chEndPar)
2409 {
2410 uchVal = expr_map_get(ch);
2411 if (uchVal == 0)
2412 { /*likely*/ }
2413 else if ((uchVal & 3) == 2 /*isspace*/)
2414 break;
2415 else if ( (uchVal & 1)
2416 && psz != pszStart /* not at the start */
2417 && ( (uchVal & 2) /* operator without separator needs */
2418 || EXPR_IS_OP_SEPARATOR_NO_SPACE(psz[-1])))
2419 {
2420 pOp = expr_lookup_op(psz, uchVal, 0 /* fUnary */);
2421 if (pOp)
2422 break;
2423 }
2424 }
2425
2426 /* next */
2427 psz++;
2428 }
2429
2430 if (rc == kExprRet_Ok)
2431 rc = expr_var_init_substring(pThis, &pThis->aVars[++pThis->iVar], pszStart, psz - pszStart, kExprVar_String);
2432 }
2433 }
2434 else
2435 rc = expr_error(pThis, "Operand stack overflow");
2436 pThis->psz = psz;
2437
2438 return rc;
2439}
2440
2441
2442/**
2443 * Evaluates the current expression.
2444 *
2445 * @returns status code.
2446 *
2447 * @param pThis The instance.
2448 */
2449static EXPRRET expr_eval(PEXPR pThis)
2450{
2451 EXPRRET rc;
2452 PCEXPROP pOp;
2453
2454 /*
2455 * The main loop.
2456 */
2457 for (;;)
2458 {
2459 /*
2460 * Eat unary operators until we hit an operand.
2461 */
2462 do
2463 rc = expr_get_unary_or_operand(pThis);
2464 while (rc == kExprRet_Operator);
2465 if (rc < kExprRet_Ok)
2466 break;
2467
2468 /*
2469 * Look for a binary operator, right parenthesis or end of expression.
2470 */
2471 rc = expr_get_binary_or_eoe_or_rparen(pThis);
2472 if (rc < kExprRet_Ok)
2473 break;
2474 expr_unget_op(pThis);
2475
2476 /*
2477 * Pop operators and apply them.
2478 *
2479 * Parenthesis will be handed via precedence, where the left parenthesis
2480 * will go pop the right one and make another operator pending.
2481 */
2482 while ( pThis->iOp >= 0
2483 && pThis->apOps[pThis->iOp]->iPrecedence >= pThis->pPending->iPrecedence)
2484 {
2485 pOp = pThis->apOps[pThis->iOp--];
2486 Assert(pThis->iVar + 1 >= pOp->cArgs);
2487 rc = pOp->pfn(pThis);
2488 if (rc < kExprRet_Ok)
2489 break;
2490 }
2491 if (rc < kExprRet_Ok)
2492 break;
2493
2494 /*
2495 * Get the next binary operator or end of expression.
2496 * There should be no right parenthesis here.
2497 */
2498 rc = expr_get_binary_or_eoe_or_rparen(pThis);
2499 if (rc < kExprRet_Ok)
2500 break;
2501 pOp = pThis->apOps[pThis->iOp];
2502 if (!pOp->iPrecedence)
2503 break; /* end of expression */
2504 if (!pOp->cArgs)
2505 {
2506 rc = expr_error(pThis, "Unexpected \"%s\"", pOp->szOp);
2507 break;
2508 }
2509 }
2510
2511 return rc;
2512}
2513
2514
2515/**
2516 * Destroys the given instance.
2517 *
2518 * @param pThis The instance to destroy.
2519 */
2520static void expr_destroy(PEXPR pThis)
2521{
2522 while (pThis->iVar >= 0)
2523 {
2524 expr_var_delete(pThis->aVars);
2525 pThis->iVar--;
2526 }
2527 RTMemTmpFree(pThis);
2528}
2529
2530
2531/**
2532 * Instantiates an expression evaluator.
2533 *
2534 * @returns The instance.
2535 */
2536static PEXPR expr_create(RTEXPREVALINT *pThis, const char *pch, size_t cch, PRTERRINFO pErrInfo)
2537{
2538 cch = RTStrNLen(pch, cch);
2539
2540 PEXPR pExpr = (PEXPR)RTMemTmpAllocZ(sizeof(*pExpr) + cch + 1);
2541 if (pExpr)
2542 {
2543 pExpr->psz = pExpr->pszExpr = (char *)memcpy(pExpr + 1, pch, cch);
2544 pExpr->pErrInfo = pErrInfo;
2545 pExpr->pEvaluator = pThis;
2546 pExpr->pPending = NULL;
2547 pExpr->iVar = -1;
2548 pExpr->iOp = -1;
2549
2550 expr_map_init();
2551 }
2552 return pExpr;
2553}
2554
2555
2556
2557/*********************************************************************************************************************************
2558* API *
2559*********************************************************************************************************************************/
2560
2561/** @callback_method_impl{PFNRTEXPREVALQUERYVARIABLE, Stub} */
2562static DECLCALLBACK(int) rtExprEvalDummyQueryVariable(const char *pchName, size_t cchName, void *pvUser, char **ppszValue)
2563{
2564 RT_NOREF(pchName, cchName, pvUser);
2565 if (ppszValue)
2566 *ppszValue = NULL;
2567 return VERR_NOT_FOUND;
2568}
2569
2570
2571RTDECL(int) RTExprEvalCreate(PRTEXPREVAL phEval, uint64_t fFlags, const char *pszName,
2572 void *pvUser, PFNRTEXPREVALQUERYVARIABLE pfnQueryVariable)
2573{
2574 AssertPtrReturn(phEval, VERR_INVALID_POINTER);
2575 *phEval = NULL;
2576 AssertPtrNullReturn(pfnQueryVariable, VERR_INVALID_POINTER);
2577 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
2578 AssertReturn(!(fFlags & ~0), VERR_INVALID_FLAGS);
2579
2580 char *pszNameCopy = RTStrDup(pszName);
2581 if (pszNameCopy)
2582 {
2583 RTEXPREVALINT *pThis = (RTEXPREVALINT *)RTMemAllocZ(sizeof(*pThis));
2584 if (pThis)
2585 {
2586 pThis->u32Magic = RTEXPREVAL_MAGIC;
2587 pThis->cRefs = 1;
2588 pThis->fFlags = fFlags;
2589 pThis->pszName = pszNameCopy;
2590 pThis->pvUser = pvUser;
2591 pThis->pfnQueryVariable = pfnQueryVariable ? pfnQueryVariable : rtExprEvalDummyQueryVariable;
2592 *phEval = pThis;
2593 return VINF_SUCCESS;
2594
2595 }
2596 return VERR_NO_MEMORY;
2597 }
2598 return VERR_NO_STR_MEMORY;
2599}
2600
2601
2602RTDECL(uint32_t) RTExprEvalRetain(RTEXPREVAL hEval)
2603{
2604 RTEXPREVALINT *pThis = hEval;
2605 AssertPtrReturn(pThis, UINT32_MAX);
2606 AssertReturn(pThis->u32Magic == RTEXPREVAL_MAGIC, UINT32_MAX);
2607 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
2608 Assert(cRefs > 1);
2609 Assert(cRefs < 512);
2610 return cRefs;
2611}
2612
2613
2614RTDECL(uint32_t) RTExprEvalRelease(RTEXPREVAL hEval)
2615{
2616 RTEXPREVALINT *pThis = hEval;
2617 AssertPtrReturn(pThis, UINT32_MAX);
2618 AssertReturn(pThis->u32Magic == RTEXPREVAL_MAGIC, UINT32_MAX);
2619 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
2620 Assert(cRefs < 512);
2621 if (cRefs == 0)
2622 {
2623 pThis->u32Magic = ~RTEXPREVAL_MAGIC;
2624 RTMemFree(pThis);
2625 return 0;
2626 }
2627 return cRefs;
2628}
2629
2630
2631RTDECL(int) RTExprEvalToBool(RTEXPREVAL hEval, const char *pch, size_t cch, bool *pfResult, PRTERRINFO pErrInfo)
2632{
2633 AssertPtrReturn(pfResult, VERR_INVALID_POINTER);
2634 *pfResult = false;
2635 RTEXPREVALINT *pThis = hEval;
2636 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
2637 AssertReturn(pThis->u32Magic == RTEXPREVAL_MAGIC, VERR_INVALID_HANDLE);
2638
2639 /*
2640 * Instantiate the expression evaluator and let it have a go at it.
2641 */
2642 int rc;
2643 PEXPR pExpr = expr_create(pThis, pch, cch, pErrInfo);
2644 if (pExpr)
2645 {
2646 if (expr_eval(pExpr) >= kExprRet_Ok)
2647 {
2648 /*
2649 * Convert the result (on top of the stack) to boolean and
2650 * set our return value accordingly.
2651 */
2652 if ( expr_var_make_bool(pExpr, &pExpr->aVars[0]) == kExprRet_Ok
2653 && pExpr->aVars[0].uVal.i)
2654 *pfResult = true;
2655 rc = VINF_SUCCESS;
2656 }
2657 else
2658 rc = VERR_PARSE_ERROR; /** @todo better errors? */
2659 expr_destroy(pExpr);
2660 }
2661 else
2662 rc = VERR_NO_TMP_MEMORY;
2663 return rc;
2664}
2665
2666
2667RTDECL(int) RTExprEvalToInteger(RTEXPREVAL hEval, const char *pch, size_t cch, int64_t *piResult, PRTERRINFO pErrInfo)
2668{
2669 AssertPtrReturn(piResult, VERR_INVALID_POINTER);
2670 *piResult = INT64_MAX;
2671 RTEXPREVALINT *pThis = hEval;
2672 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
2673 AssertReturn(pThis->u32Magic == RTEXPREVAL_MAGIC, VERR_INVALID_HANDLE);
2674
2675 /*
2676 * Instantiate the expression evaluator and let it have a go at it.
2677 */
2678 int rc;
2679 PEXPR pExpr = expr_create(pThis, pch, cch, pErrInfo);
2680 if (pExpr)
2681 {
2682 if (expr_eval(pExpr) >= kExprRet_Ok)
2683 {
2684 /*
2685 * Convert the result (on top of the stack) to boolean and
2686 * set our return value accordingly.
2687 */
2688 PEXPRVAR pVar = &pExpr->aVars[0];
2689 EXPRRET rcExpr = expr_var_make_num(pExpr, pVar);
2690 if (rcExpr >= kExprRet_Ok)
2691 {
2692 *piResult = pVar->uVal.i;
2693 rc = VINF_SUCCESS;
2694 }
2695 else
2696 rc = VERR_PARSE_ERROR; /** @todo better error! */
2697 }
2698 else
2699 rc = VERR_PARSE_ERROR; /** @todo better errors? */
2700 expr_destroy(pExpr);
2701 }
2702 else
2703 rc = VERR_NO_TMP_MEMORY;
2704 return rc;
2705}
2706
2707
2708RTDECL(int) RTExprEvalToString(RTEXPREVAL hEval, const char *pch, size_t cch, char **ppszResult, PRTERRINFO pErrInfo)
2709{
2710 AssertPtrReturn(ppszResult, VERR_INVALID_POINTER);
2711 *ppszResult = NULL;
2712 RTEXPREVALINT *pThis = hEval;
2713 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
2714 AssertReturn(pThis->u32Magic == RTEXPREVAL_MAGIC, VERR_INVALID_HANDLE);
2715
2716 /*
2717 * Instantiate the expression evaluator and let it have a go at it.
2718 */
2719 int rc;
2720 PEXPR pExpr = expr_create(pThis, pch, cch, pErrInfo);
2721 if (pExpr)
2722 {
2723 if (expr_eval(pExpr) >= kExprRet_Ok)
2724 {
2725 /*
2726 * Convert the result (on top of the stack) to a string
2727 * and copy it out the variable buffer.
2728 */
2729 PEXPRVAR pVar = &pExpr->aVars[0];
2730 if (expr_var_make_simple_string(pExpr, pVar) == kExprRet_Ok)
2731 rc = RTStrDupEx(ppszResult, pVar->uVal.psz);
2732 else
2733 rc = VERR_NO_TMP_MEMORY;
2734 }
2735 else
2736 rc = VERR_PARSE_ERROR;
2737 expr_destroy(pExpr);
2738 }
2739 else
2740 rc = VERR_NO_TMP_MEMORY;
2741
2742 return rc;
2743}
2744
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette