source: trunk/src/kmk/ifcond.c@ 1720

Last change on this file since 1720 was 1719, checked in by bird, 17 years ago

kmk: More if conditionals code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.2 KB
Line 
1#ifdef CONFIG_WITH_IF_CONDITIONALS
2/* $Id: ifcond.c 1719 2008-09-04 02:49:36Z bird $ */
3/** @file
4 * ifcond - C like if expressions.
5 */
6
7/*
8 * Copyright (c) 2008 knut st. osmundsen <bird-src-spam@anduin.net>
9 *
10 * This file is part of kBuild.
11 *
12 * kBuild is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * kBuild is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with kBuild; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 *
26 */
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include "make.h"
32#include <assert.h>
33
34#include <glob.h>
35
36#include "dep.h"
37#include "filedef.h"
38#include "job.h"
39#include "commands.h"
40#include "variable.h"
41#include "rule.h"
42#include "debug.h"
43#include "hash.h"
44#include <ctype.h>
45#ifdef _MSC_VER
46# include <stdint.h>
47#endif
48#include <stdarg.h>
49
50
51/*******************************************************************************
52* Defined Constants And Macros *
53*******************************************************************************/
54/** The max length of a string representation of a number. */
55#define IFCOND_NUM_LEN ((sizeof("-9223372036854775802") + 4) & ~3)
56
57/** The max operator stack depth. */
58#define IFCOND_MAX_OPERATORS 72
59/** The max operand depth. */
60#define IFCOND_MAX_OPERANDS 128
61
62
63/*******************************************************************************
64* Structures and Typedefs *
65*******************************************************************************/
66/** The 64-bit signed integer type we're using. */
67#ifdef _MSC_VER
68typedef __int64 IFCONDINT64;
69#else
70# include <stdint.h>
71typedef int64_t IFCONDINT64;
72#endif
73
74/** Pointer to a evaluator instance. */
75typedef struct IFCOND *PIFCOND;
76
77
78/**
79 * Operand variable type.
80 */
81typedef enum
82{
83 /** Invalid zero entry. */
84 kIfCondVar_Invalid = 0,
85 /** A number. */
86 kIfCondVar_Num,
87 /** A string in need of expanding (perhaps). */
88 kIfCondVar_String,
89 /** A simple string that doesn't need expanding. */
90 kIfCondVar_SimpleString,
91 /** The end of the valid variable types. */
92 kIfCondVar_End
93} IFCONDVARTYPE;
94
95/**
96 * Operand variable.
97 */
98typedef struct
99{
100 /** The variable type. */
101 IFCONDVARTYPE enmType;
102 /** The variable. */
103 union
104 {
105 /** Pointer to the string. */
106 char *psz;
107 /** The variable. */
108 IFCONDINT64 i;
109 } uVal;
110} IFCONDVAR;
111/** Pointer to a operand variable. */
112typedef IFCONDVAR *PIFCONDVAR;
113/** Pointer to a const operand variable. */
114typedef IFCONDVAR const *PCIFCONDVAR;
115
116/**
117 * Operator return statuses.
118 */
119typedef enum
120{
121 kIfCondRet_Error = -1,
122 kIfCondRet_Ok = 0,
123 kIfCondRet_Operator,
124 kIfCondRet_Operand,
125 kIfCondRet_EndOfExpr,
126 kIfCondRet_End
127} IFCONDRET;
128
129/**
130 * Operator.
131 */
132typedef struct
133{
134 /** The operator. */
135 char szOp[11];
136 /** The length of the operator string. */
137 char cchOp;
138 /** The pair operator.
139 * This is used with '(' and '?'. */
140 char chPair;
141 /** The precedence. Higher means higher. */
142 char iPrecedence;
143 /** The number of arguments it takes. */
144 signed char cArgs;
145 /** Pointer to the method implementing the operator. */
146 IFCONDRET (*pfn)(PIFCOND pThis);
147} IFCONDOP;
148/** Pointer to a const operator. */
149typedef IFCONDOP const *PCIFCONDOP;
150
151/**
152 * Expression evaluator instance.
153 */
154typedef struct IFCOND
155{
156 /** The full expression. */
157 const char *pszExpr;
158 /** The current location. */
159 const char *psz;
160 /** The current file location, used for errors. */
161 const struct floc *pFileLoc;
162 /** Pending binary operator. */
163 PCIFCONDOP pPending;
164 /** Top of the operator stack. */
165 int iOp;
166 /** Top of the operand stack. */
167 int iVar;
168 /** The operator stack. */
169 PCIFCONDOP apOps[IFCOND_MAX_OPERATORS];
170 /** The operand stack. */
171 IFCONDVAR aVars[IFCOND_MAX_OPERANDS];
172} IFCOND;
173
174
175/*******************************************************************************
176* Global Variables *
177*******************************************************************************/
178/** Operator start character map.
179 * This indicates which characters that are starting operators and which aren't. */
180static char g_auchOpStartCharMap[256];
181/** Whether we've initialized the map. */
182static int g_fIfCondInitializedMap = 0;
183
184
185/*******************************************************************************
186* Internal Functions *
187*******************************************************************************/
188static void ifcond_unget_op(PIFCOND pThis);
189static IFCONDRET ifcond_get_binary_or_eoe_or_rparen(PIFCOND pThis);
190
191
192
193
194/**
195 * Displays an error message.
196 *
197 * The total string length must not exceed 256 bytes.
198 *
199 * @param pThis The evaluator instance.
200 * @param pszError The message format string.
201 * @param ... The message format args.
202 */
203static void ifcond_error(PIFCOND pThis, const char *pszError, ...)
204{
205 char szTmp[256];
206 va_list va;
207
208 va_start(va, pszError);
209 vsprintf(szTmp, pszError, va);
210 va_end(va);
211
212 fatal(pThis->pFileLoc, "%s", szTmp);
213}
214
215
216/**
217 * Converts a number to a string.
218 *
219 * @returns pszDst.
220 * @param pszDst The string buffer to write into. Assumes length of IFCOND_NUM_LEN.
221 * @param iSrc The number to convert.
222 */
223static char *ifcond_num_to_string(char *pszDst, IFCONDINT64 iSrc)
224{
225 static const char s_szDigits[17] = "0123456789abcdef";
226 char szTmp[IFCOND_NUM_LEN];
227 char *psz = &szTmp[IFCOND_NUM_LEN - 1];
228 int fNegative;
229
230 fNegative = iSrc < 0;
231 if (fNegative)
232 {
233 /** @todo this isn't right for INT64_MIN. */
234 iSrc = -iSrc;
235 }
236
237 *psz = '\0';
238 do
239 {
240#if 0
241 *--psz = s_szDigits[iSrc & 0xf];
242 iSrc >>= 4;
243#else
244 *--psz = s_szDigits[iSrc % 10];
245 iSrc /= 10;
246#endif
247 } while (iSrc);
248
249#if 0
250 *--psz = 'x';
251 *--psz = '0';
252#endif
253
254 if (fNegative)
255 *--psz = '-';
256
257 /* copy it into the output buffer. */
258 psz++;
259 return (char *)memcpy(pszDst, psz, &szTmp[IFCOND_NUM_LEN] - psz);
260}
261
262
263/**
264 * Attempts to convert a (simple) string into a number.
265 *
266 * @returns status code.
267 * @param pThis The evaluator instance. This is optional when fQuiet is true.
268 * @param piSrc Where to store the numeric value on success.
269 * @param pszSrc The string to try convert.
270 * @param fQuiet Whether we should be quiet or grumpy on failure.
271 */
272static IFCONDRET ifcond_string_to_num(PIFCOND pThis, IFCONDINT64 *piDst, const char *pszSrc, int fQuiet)
273{
274 IFCONDRET rc = kIfCondRet_Ok;
275 char const *psz = pszSrc;
276 IFCONDINT64 i;
277 unsigned uBase;
278 int fNegative;
279
280
281 /*
282 * Skip blanks.
283 */
284 while (isblank(*psz))
285 psz++;
286
287 /*
288 * Check for '-'.
289 *
290 * At this point we will not need to deal with operators, this is
291 * just an indicator of negative numbers. If some operator ends up
292 * here it's because it came from a string expansion and thus shall
293 * not be interpreted. If this turns out to be an stupid restriction
294 * it can be fixed, but for now it stays like this.
295 */
296 fNegative = *psz == '-';
297 if (fNegative)
298 psz++;
299
300 /*
301 * Determin base .
302 * .
303 * Recognize some exsotic prefixes here in addition to the two standard ones.
304 */
305 if (*psz != '0' || psz[1] == '\0' || isblank((unsigned int)psz[1]))
306 uBase = 10;
307 else if (psz[1] == 'x' || psz[1] == 'X')
308 {
309 uBase = 16;
310 psz += 2;
311 }
312 else if (psz[1] == 'b' || psz[1] == 'B')
313 {
314 uBase = 2;
315 psz += 2;
316 }
317 else if (psz[1] == 'd' || psz[1] == 'D')
318 {
319 uBase = 10;
320 psz += 2;
321 }
322 else if (psz[1] == 'o' || psz[1] == 'O')
323 {
324 uBase = 8;
325 psz += 2;
326 }
327 else if (isdigit(psz[1]) && psz[1] != '9' && psz[1] != '8')
328 {
329 uBase = 8;
330 psz++;
331 }
332 else
333 uBase = 10;
334
335 /*
336 * Convert until we hit a non-digit.
337 */
338 i = 0;
339 for (;;)
340 {
341 int iDigit;
342 int ch = *psz;
343 switch (ch)
344 {
345 case '0': iDigit = 0; break;
346 case '1': iDigit = 1; break;
347 case '2': iDigit = 2; break;
348 case '3': iDigit = 3; break;
349 case '4': iDigit = 4; break;
350 case '5': iDigit = 5; break;
351 case '6': iDigit = 6; break;
352 case '7': iDigit = 7; break;
353 case '8': iDigit = 8; break;
354 case '9': iDigit = 9; break;
355 case 'a':
356 case 'A': iDigit = 10; break;
357 case 'b':
358 case 'B': iDigit = 11; break;
359 case 'c':
360 case 'C': iDigit = 12; break;
361 case 'd':
362 case 'D': iDigit = 13; break;
363 case 'e':
364 case 'E': iDigit = 14; break;
365 case 'f':
366 case 'F': iDigit = 15; break;
367
368 default:
369 /* is the rest white space? */
370 while (isspace((unsigned int)*psz))
371 psz++;
372 if (*psz != '\0')
373 {
374 iDigit = uBase;
375 break;
376 }
377 /* fall thru */
378
379 case '\0':
380 if (fNegative)
381 i = -i;
382 *piDst = i;
383 return rc;
384 }
385 if (iDigit >= uBase)
386 {
387 if (fNegative)
388 i = -i;
389 *piDst = i;
390 if (!fQuiet)
391 ifcond_error(pThis, "Invalid a number \"%.80s\"", pszSrc);
392 return kIfCondRet_Error;
393 }
394
395 /* add the digit and advance */
396 i *= uBase;
397 i += iDigit;
398 psz++;
399 }
400 /* not reached */
401}
402
403
404/**
405 * Checks if the variable is a string or not.
406 *
407 * @returns 1 if it's a string, 0 otherwise.
408 * @param pVar The variable.
409 */
410static int ifcond_var_is_string(PCIFCONDVAR pVar)
411{
412 return pVar->enmType >= kIfCondVar_String;
413}
414
415
416/**
417 * Deletes a variable.
418 *
419 * @param pVar The variable.
420 */
421static void ifcond_var_delete(PIFCONDVAR pVar)
422{
423 if (ifcond_var_is_string(pVar))
424 {
425 free(pVar->uVal.psz);
426 pVar->uVal.psz = NULL;
427 }
428 pVar->enmType = kIfCondVar_Invalid;
429}
430
431
432/**
433 * Initializes a new variables with a sub-string value.
434 *
435 * @param pVar The new variable.
436 * @param psz The start of the string value.
437 * @param cch The number of chars to copy.
438 * @param enmType The string type.
439 */
440static void ifcond_var_init_substring(PIFCONDVAR pVar, const char *psz, size_t cch, IFCONDVARTYPE enmType)
441{
442 if ( enmType != kIfCondVar_SimpleString
443 && memchr(psz, '$', cch))
444 pVar->enmType = kIfCondVar_String;
445 else
446 pVar->enmType = kIfCondVar_SimpleString;
447 pVar->uVal.psz = xmalloc(cch + 1);
448 memcpy(pVar->uVal.psz, psz, cch);
449 pVar->uVal.psz[cch] = '\0';
450}
451
452
453#if 0 /* unused */
454/**
455 * Initializes a new variables with a string value.
456 *
457 * @param pVar The new variable.
458 * @param psz The string value.
459 * @param enmType The string type.
460 */
461static void ifcond_var_init_string(PIFCONDVAR pVar, const char *psz, IFCONDVARTYPE enmType)
462{
463 ifcond_var_init_substring(pVar, psz, strlen(psz), enmType);
464}
465
466
467/**
468 * Assigns a sub-string value to a variable.
469 *
470 * @param pVar The new variable.
471 * @param psz The start of the string value.
472 * @param cch The number of chars to copy.
473 * @param enmType The string type.
474 */
475static void ifcond_var_assign_substring(PIFCONDVAR pVar, const char *psz, size_t cch, IFCONDVARTYPE enmType)
476{
477 ifcond_var_delete(pVar);
478 ifcond_var_init_substring(pVar, psz, cch, enmType);
479}
480
481
482/**
483 * Assignes a string value to a variable.
484 *
485 * @param pVar The variable.
486 * @param psz The string value.
487 * @param enmType The string type.
488 */
489static void ifcond_var_assign_string(PIFCONDVAR pVar, const char *psz, IFCONDVARTYPE enmType)
490{
491 ifcond_var_delete(pVar);
492 ifcond_var_init_string(pVar, psz, enmType);
493}
494#endif /* unused */
495
496
497/**
498 * Simplifies a string variable.
499 *
500 * @param pVar The variable.
501 */
502static void ifcond_var_make_simple_string(PIFCONDVAR pVar)
503{
504 switch (pVar->enmType)
505 {
506 case kIfCondVar_Num:
507 {
508 char *psz = (char *)xmalloc(IFCOND_NUM_LEN);
509 ifcond_num_to_string(psz, pVar->uVal.i);
510 pVar->uVal.psz = psz;
511 pVar->enmType = kIfCondVar_SimpleString;
512 break;
513 }
514
515 case kIfCondVar_String:
516 {
517 char *psz;
518 assert(strchr(pVar->uVal.psz, '$'));
519
520 psz = allocated_variable_expand(pVar->uVal.psz);
521 free(pVar->uVal.psz);
522 pVar->uVal.psz = psz;
523
524 pVar->enmType = kIfCondVar_SimpleString;
525 break;
526 }
527
528 case kIfCondVar_SimpleString:
529 /* nothing to do. */
530 break;
531
532 default:
533 assert(0);
534 }
535}
536
537
538#if 0 /* unused */
539/**
540 * Turns a variable into a string value.
541 *
542 * @param pVar The variable.
543 */
544static void ifcond_var_make_string(PIFCONDVAR pVar)
545{
546 switch (pVar->enmType)
547 {
548 case kIfCondVar_Num:
549 ifcond_var_make_simple_string(pVar);
550
551 case kIfCondVar_String:
552 case kIfCondVar_SimpleString:
553 /* nothing to do. */
554 break;
555
556 default:
557 assert(0);
558 }
559}
560#endif /* unused */
561
562
563/**
564 * Initializes a new variables with a integer value.
565 *
566 * @param pVar The new variable.
567 * @param i The integer value.
568 */
569static void ifcond_var_init_num(PIFCONDVAR pVar, IFCONDINT64 i)
570{
571 pVar->enmType = kIfCondVar_Num;
572 pVar->uVal.i = i;
573}
574
575
576/**
577 * Assigns a integer value to a variable.
578 *
579 * @param pVar The variable.
580 * @param i The integer value.
581 */
582static void ifcond_var_assign_num(PIFCONDVAR pVar, IFCONDINT64 i)
583{
584 ifcond_var_delete(pVar);
585 ifcond_var_init_num(pVar, i);
586}
587
588
589/**
590 * Turns the variable into a number.
591 *
592 * @returns status code.
593 * @param pThis The evaluator instance.
594 * @param pVar The variable.
595 */
596static IFCONDRET ifcond_var_make_num(PIFCOND pThis, PIFCONDVAR pVar)
597{
598 switch (pVar->enmType)
599 {
600 case kIfCondVar_Num:
601 /* nothing to do. */
602 break;
603
604 case kIfCondVar_String:
605 ifcond_var_make_simple_string(pVar);
606 /* fall thru */
607 case kIfCondVar_SimpleString:
608 {
609 IFCONDINT64 i;
610 IFCONDRET rc = ifcond_string_to_num(pThis, &i, pVar->uVal.psz, 0 /* fQuiet */);
611 if (rc < kIfCondRet_Ok)
612 return rc;
613 ifcond_var_assign_num(pVar, i);
614 break;
615 }
616
617 default:
618 assert(0);
619 return kIfCondRet_Error;
620 }
621
622 return kIfCondRet_Ok;
623}
624
625
626/**
627 * Initializes a new variables with a boolean value.
628 *
629 * @param pVar The new variable.
630 * @param f The boolean value.
631 */
632static void ifcond_var_init_bool(PIFCONDVAR pVar, int f)
633{
634 pVar->enmType = kIfCondVar_Num;
635 pVar->uVal.i = !!f;
636}
637
638
639/**
640 * Assigns a boolean value to a variable.
641 *
642 * @param pVar The variable.
643 * @param f The boolean value.
644 */
645static void ifcond_var_assign_bool(PIFCONDVAR pVar, int f)
646{
647 ifcond_var_delete(pVar);
648 ifcond_var_init_bool(pVar, f);
649}
650
651
652/**
653 * Turns the variable into an boolean.
654 *
655 * @returns the boolean interpretation.
656 * @param pVar The variable.
657 */
658static int ifcond_var_make_bool(PIFCONDVAR pVar)
659{
660 switch (pVar->enmType)
661 {
662 case kIfCondVar_Num:
663 pVar->uVal.i = !!pVar->uVal.i;
664 break;
665
666 case kIfCondVar_String:
667 ifcond_var_make_simple_string(pVar);
668 /* fall thru */
669 case kIfCondVar_SimpleString:
670 {
671 /*
672 * Try convert it to a number. If that fails, use the
673 * GNU make boolean logic - not empty string means true.
674 */
675 IFCONDINT64 iVal;
676 char const *psz = pVar->uVal.psz;
677 while (isblank((unsigned char)*psz))
678 psz++;
679 if ( *psz
680 && ifcond_string_to_num(NULL, &iVal, psz, 1 /* fQuiet */) >= kIfCondRet_Ok)
681 ifcond_var_assign_bool(pVar, iVal != 0);
682 else
683 ifcond_var_assign_bool(pVar, *psz != '\0');
684 break;
685 }
686
687 default:
688 assert(0);
689 break;
690 }
691
692 return pVar->uVal.i;
693}
694
695
696/**
697 * Pops a varable off the stack and deletes it.
698 * @param pThis The evaluator instance.
699 */
700static void ifcond_pop_and_delete_var(PIFCOND pThis)
701{
702 ifcond_var_delete(&pThis->aVars[pThis->iVar]);
703 pThis->iVar--;
704}
705
706
707/**
708 * Bitwise OR.
709 *
710 * @returns Status code.
711 * @param pThis The instance.
712 */
713static IFCONDRET ifcond_op_bitwise_or(PIFCOND pThis)
714{
715 IFCONDRET rc;
716 assert(pThis->iVar >= 1);
717
718 rc = ifcond_var_make_num(pThis, &pThis->aVars[pThis->iVar - 1]);
719 if (rc >= kIfCondRet_Ok)
720 {
721 rc = ifcond_var_make_num(pThis, &pThis->aVars[pThis->iVar]);
722 if (rc >= kIfCondRet_Ok)
723 pThis->aVars[pThis->iVar - 1].uVal.i |= pThis->aVars[pThis->iVar].uVal.i;
724 }
725
726 ifcond_pop_and_delete_var(pThis);
727 return kIfCondRet_Ok;
728}
729
730
731/**
732 * Logical AND.
733 *
734 * @returns Status code.
735 * @param pThis The instance.
736 */
737static IFCONDRET ifcond_op_logical_and(PIFCOND pThis)
738{
739 assert(pThis->iVar >= 1);
740 if ( ifcond_var_make_bool(&pThis->aVars[pThis->iVar - 1])
741 && ifcond_var_make_bool(&pThis->aVars[pThis->iVar]))
742 ifcond_var_assign_bool(&pThis->aVars[pThis->iVar - 1], 1);
743 else
744 ifcond_var_assign_bool(&pThis->aVars[pThis->iVar - 1], 0);
745
746 ifcond_pop_and_delete_var(pThis);
747 return kIfCondRet_Ok;
748}
749
750
751/**
752 * Logical OR.
753 *
754 * @returns Status code.
755 * @param pThis The instance.
756 */
757static IFCONDRET ifcond_op_logical_or(PIFCOND pThis)
758{
759 assert(pThis->iVar >= 1);
760 if ( ifcond_var_make_bool(&pThis->aVars[pThis->iVar - 1])
761 || ifcond_var_make_bool(&pThis->aVars[pThis->iVar]))
762 ifcond_var_assign_bool(&pThis->aVars[pThis->iVar - 1], 1);
763 else
764 ifcond_var_assign_bool(&pThis->aVars[pThis->iVar - 1], 0);
765
766 ifcond_pop_and_delete_var(pThis);
767 return kIfCondRet_Ok;
768}
769
770
771/**
772 * Left parenthesis.
773 *
774 * @returns Status code.
775 * @param pThis The instance.
776 */
777static IFCONDRET ifcond_op_left_parenthesis(PIFCOND pThis)
778{
779 /*
780 * There should be a right parenthesis operator lined up for us now,
781 * eat it. If not found there is an inbalance.
782 */
783 IFCONDRET rc = ifcond_get_binary_or_eoe_or_rparen(pThis);
784 if ( rc == kIfCondRet_Operator
785 && pThis->apOps[pThis->iOp]->szOp[0] == ')')
786 {
787 /* pop it and get another one which we can leave pending. */
788 pThis->iOp--;
789 rc = ifcond_get_binary_or_eoe_or_rparen(pThis);
790 if (rc >= kIfCondRet_Ok)
791 ifcond_unget_op(pThis);
792 }
793 else
794 {
795 ifcond_error(pThis, "Missing ')'");
796 rc = kIfCondRet_Error;
797 }
798
799 return rc;
800}
801
802
803/**
804 * Right parenthesis, dummy that's never actually called.
805 *
806 * @returns Status code.
807 * @param pThis The instance.
808 */
809static IFCONDRET ifcond_op_right_parenthesis(PIFCOND pThis)
810{
811 return kIfCondRet_Ok;
812}
813
814
815
816
817
818/**
819 * The operator table.
820 *
821 * This table is NOT ordered by precedence, but for linear search
822 * allowing for first match to return the correct operator. This
823 * means that || must come before |, or else | will match all.
824 */
825static const IFCONDOP g_aIfCondOps[] =
826{
827#define IFCOND_OP(szOp, iPrecedence, cArgs, pfn) { szOp, sizeof(szOp) - 1, '\0', iPrecedence, cArgs, pfn }
828 /* Name, iPrecedence, cArgs, pfn */
829#if 0
830 IFCOND_OP("defined", 90, 1, ifcond_op_defined),
831 IFCOND_OP("+", 80, 1, ifcond_op_pluss),
832 IFCOND_OP("-", 80, 1, ifcond_op_minus),
833 IFCOND_OP("~", 80, 1, ifcond_op_bitwise_not),
834 IFCOND_OP("*", 75, 2, ifcond_op_multiply),
835 IFCOND_OP("/", 75, 2, ifcond_op_divide),
836 IFCOND_OP("%", 75, 2, ifcond_op_mod),
837 IFCOND_OP("+", 70, 2, ifcond_op_add),
838 IFCOND_OP("-", 70, 2, ifcond_op_sub),
839 IFCOND_OP("<<", 65, 2, ifcond_op_shift_left),
840 IFCOND_OP(">>", 65, 2, ifcond_op_shift_right),
841 IFCOND_OP("<=", 60, 2, ifcond_op_less_or_equal_than),
842 IFCOND_OP("<", 60, 2, ifcond_op_less_than),
843 IFCOND_OP(">=", 60, 2, ifcond_op_greater_or_equal_than),
844 IFCOND_OP(">", 60, 2, ifcond_op_greater_than),
845 IFCOND_OP("==", 55, 2, ifcond_op_equal),
846 IFCOND_OP("!=", 55, 2, ifcond_op_not_equal),
847 IFCOND_OP("!", 80, 1, ifcond_op_logical_not),
848 IFCOND_OP("^", 45, 2, ifcond_op_bitwise_xor),
849#endif
850 IFCOND_OP("&&", 35, 2, ifcond_op_logical_and),
851 /*IFCOND_OP("&", 50, 2, ifcond_op_bitwise_and),*/
852 IFCOND_OP("||", 30, 2, ifcond_op_logical_or),
853 IFCOND_OP("|", 40, 2, ifcond_op_bitwise_or),
854 { "(", 1, ')', 10, 1, ifcond_op_left_parenthesis },
855 { ")", 1, '(', 10, 0, ifcond_op_right_parenthesis },
856 /* { "?", 1, ':', 5, 2, ifcond_op_question },
857 { ":", 1, '?', 5, 2, ifcond_op_colon }, -- too weird for now. */
858#undef IFCOND_OP
859};
860
861/** Dummy end of expression fake. */
862static const IFCONDOP g_IfCondEndOfExpOp =
863{
864 "", 0, '\0', 0, 0, NULL
865};
866
867
868/**
869 * Initializes the opcode character map if necessary.
870 */
871static void ifcond_map_init(void)
872{
873 int i;
874 if (g_fIfCondInitializedMap)
875 return;
876
877 /*
878 * Initialize it.
879 */
880 memset(&g_auchOpStartCharMap, 0, sizeof(g_auchOpStartCharMap));
881 for (i = 0; i < sizeof(g_aIfCondOps) / sizeof(g_aIfCondOps[0]); i++)
882 {
883 unsigned int ch = (unsigned int)g_aIfCondOps[i].szOp[0];
884 if (!g_auchOpStartCharMap[ch])
885 g_auchOpStartCharMap[ch] = (i << 1) | 1;
886 }
887
888 g_fIfCondInitializedMap = 1;
889}
890
891
892/**
893 * Looks up a character in the map.
894 *
895 * @returns the value for that char.
896 * @retval 0 if not a potential opcode start char.
897 * @retval non-zero if it's a potential operator. The low bit is always set
898 * while the remaining 7 bits is the index into the operator table
899 * of the first match.
900 *
901 * @param ch The character.
902 */
903static unsigned char ifcond_map_get(char ch)
904{
905 return g_auchOpStartCharMap[(unsigned int)ch];
906}
907
908
909/**
910 * Searches the operator table given a potential operator start char.
911 *
912 * @returns Pointer to the matching operator. NULL if not found.
913 * @param psz Pointer to what can be an operator.
914 * @param uchVal The ifcond_map_get value.
915 * @param fUnary Whether it must be an unary operator or not.
916 */
917static PCIFCONDOP ifcond_lookup_op(char const *psz, unsigned char uchVal, int fUnary)
918{
919 char ch = *psz;
920 int i;
921
922 for (i = uchVal >> 1; i < sizeof(g_aIfCondOps) / sizeof(g_aIfCondOps[0]); i++)
923 {
924 /* compare the string... */
925 switch (g_aIfCondOps[i].cchOp)
926 {
927 case 1:
928 if (g_aIfCondOps[i].szOp[0] != ch)
929 continue;
930 break;
931 case 2:
932 if ( g_aIfCondOps[i].szOp[0] != ch
933 || g_aIfCondOps[i].szOp[1] != psz[1])
934 continue;
935 break;
936 default:
937 if ( g_aIfCondOps[i].szOp[0] != ch
938 || strncmp(&g_aIfCondOps[i].szOp[1], psz + 1, g_aIfCondOps[i].cchOp - 1))
939 continue;
940 break;
941 }
942
943 /* ... and the operator type. */
944 if (fUnary == (g_aIfCondOps[i].cArgs == 1))
945 {
946 /* got a match! */
947 return &g_aIfCondOps[i];
948 }
949 }
950
951 return NULL;
952}
953
954
955/**
956 * Ungets a binary operator.
957 *
958 * The operator is poped from the stack and put in the pending position.
959 *
960 * @param pThis The evaluator instance.
961 */
962static void ifcond_unget_op(PIFCOND pThis)
963{
964 assert(pThis->pPending == NULL);
965 assert(pThis->iOp >= 0);
966
967 pThis->pPending = pThis->apOps[pThis->iOp];
968 pThis->apOps[pThis->iOp] = NULL;
969 pThis->iOp--;
970}
971
972
973
974/**
975 * Get the next token, it should be a binary operator, or the end of
976 * the expression, or a right parenthesis.
977 *
978 * The operator is pushed onto the stack and the status code indicates
979 * which of the two we found.
980 *
981 * @returns status code. Will grumble on failure.
982 * @retval kIfCondRet_EndOfExpr if we encountered the end of the expression.
983 * @retval kIfCondRet_Operator if we encountered a binary operator or right
984 * parenthesis. It's on the operator stack.
985 *
986 * @param pThis The evaluator instance.
987 */
988static IFCONDRET ifcond_get_binary_or_eoe_or_rparen(PIFCOND pThis)
989{
990 /*
991 * See if there is anything pending first.
992 */
993 PCIFCONDOP pOp = pThis->pPending;
994 if (pOp)
995 pThis->pPending = NULL;
996 else
997 {
998 /*
999 * Eat more of the expression.
1000 */
1001 char const *psz = pThis->psz;
1002
1003 /* spaces */
1004 while (isspace((unsigned int)*psz))
1005 psz++;
1006 /* see what we've got. */
1007 if (*psz)
1008 {
1009 unsigned char uchVal = ifcond_map_get(*psz);
1010 if (uchVal)
1011 pOp = ifcond_lookup_op(psz, uchVal, 0 /* fUnary */);
1012 if (!pOp)
1013 {
1014 ifcond_error(pThis, "Expected binary operator, found \"%.42s\"...", psz);
1015 return kIfCondRet_Error;
1016 }
1017 psz += pOp->cchOp;
1018 }
1019 else
1020 pOp = &g_IfCondEndOfExpOp;
1021 pThis->psz = psz;
1022 }
1023
1024 /*
1025 * Push it.
1026 */
1027 if (pThis->iOp >= IFCOND_MAX_OPERATORS - 1)
1028 {
1029 ifcond_error(pThis, "Operator stack overflow");
1030 return kIfCondRet_Error;
1031 }
1032 pThis->apOps[++pThis->iOp] = pOp;
1033
1034 return pOp->iPrecedence
1035 ? kIfCondRet_Operator
1036 : kIfCondRet_EndOfExpr;
1037}
1038
1039
1040
1041/**
1042 * Get the next token, it should be an unary operator or an operand.
1043 *
1044 * This will fail if encountering the end of the expression since
1045 * it is implied that there should be something more.
1046 *
1047 * The token is pushed onto the respective stack and the status code
1048 * indicates which it is.
1049 *
1050 * @returns status code. On failure we'll be done bitching already.
1051 * @retval kIfCondRet_Operator if we encountered an unary operator.
1052 * It's on the operator stack.
1053 * @retval kIfCondRet_Operand if we encountered an operand operator.
1054 * It's on the operand stack.
1055 *
1056 * @param This The evaluator instance.
1057 */
1058static IFCONDRET ifcond_get_unary_or_operand(PIFCOND pThis)
1059{
1060 IFCONDRET rc;
1061 unsigned char uchVal;
1062 PCIFCONDOP pOp;
1063 char const *psz = pThis->psz;
1064
1065 /*
1066 * Eat white space and make sure there is something after it.
1067 */
1068 while (isspace((unsigned int)*psz))
1069 psz++;
1070 if (!*psz)
1071 {
1072 ifcond_error(pThis, "Unexpected end of expression");
1073 return kIfCondRet_Error;
1074 }
1075
1076 /*
1077 * Is it an operator?
1078 */
1079 pOp = NULL;
1080 uchVal = ifcond_map_get(*psz);
1081 if (uchVal)
1082 pOp = ifcond_lookup_op(psz, uchVal, 1 /* fUnary */);
1083 if (pOp)
1084 {
1085 /*
1086 * Push the operator onto the stack.
1087 */
1088 if (pThis->iVar < IFCOND_MAX_OPERANDS - 1)
1089 {
1090 pThis->apOps[++pThis->iOp] = pOp;
1091 rc = kIfCondRet_Operator;
1092 }
1093 else
1094 {
1095 ifcond_error(pThis, "Operator stack overflow");
1096 rc = kIfCondRet_Error;
1097 }
1098 psz += pOp->cchOp;
1099 }
1100 else if (pThis->iVar < IFCOND_MAX_OPERANDS - 1)
1101 {
1102 /*
1103 * It's an operand. Figure out where it ends and
1104 * push it onto the stack.
1105 */
1106 const char *pszStart = psz;
1107
1108 rc = kIfCondRet_Ok;
1109 if (*psz == '"')
1110 {
1111 pszStart++;
1112 while (*psz && *psz != '"')
1113 psz++;
1114 ifcond_var_init_substring(&pThis->aVars[++pThis->iVar], pszStart, psz - pszStart, kIfCondVar_String);
1115 }
1116 else if (*psz == '\'')
1117 {
1118 pszStart++;
1119 while (*psz && *psz != '\'')
1120 psz++;
1121 ifcond_var_init_substring(&pThis->aVars[++pThis->iVar], pszStart, psz - pszStart, kIfCondVar_SimpleString);
1122 }
1123 else
1124 {
1125 char achPars[20];
1126 int iPar = -1;
1127 char chEndPar = '\0';
1128 char ch;
1129
1130 while ((ch = *psz) != '\0')
1131 {
1132 /* $(adsf) or ${asdf} needs special handling. */
1133 if ( ch == '$'
1134 && ( psz[1] == '('
1135 || psz[1] == '{'))
1136 {
1137 psz++;
1138 if (iPar > sizeof(achPars) / sizeof(achPars[0]))
1139 {
1140 ifcond_error(pThis, "Too deep nesting of variable expansions");
1141 rc = kIfCondRet_Error;
1142 break;
1143 }
1144 achPars[++iPar] = chEndPar = ch == '(' ? ')' : '}';
1145 }
1146 else if (ch == chEndPar)
1147 {
1148 iPar--;
1149 chEndPar = iPar >= 0 ? achPars[iPar] : '\0';
1150 }
1151 else if (!chEndPar)
1152 {
1153 /** @todo combine isspace and ifcond_map_get! */
1154 unsigned chVal = ifcond_map_get(ch);
1155 if (chVal)
1156 {
1157 PCIFCONDOP pOp = ifcond_lookup_op(psz, uchVal, 0 /* fUnary */);
1158 if (pOp)
1159 break;
1160 }
1161 if (isspace((unsigned char)ch))
1162 break;
1163 }
1164
1165 /* next */
1166 psz++;
1167 }
1168
1169 if (rc == kIfCondRet_Ok)
1170 ifcond_var_init_substring(&pThis->aVars[++pThis->iVar], pszStart, psz - pszStart, kIfCondVar_String);
1171 }
1172 }
1173 else
1174 {
1175 ifcond_error(pThis, "Operand stack overflow");
1176 rc = kIfCondRet_Error;
1177 }
1178 pThis->psz = psz;
1179
1180 return rc;
1181}
1182
1183
1184/**
1185 * Evaluates the current expression.
1186 *
1187 * @returns status code.
1188 *
1189 * @param pThis The instance.
1190 */
1191static IFCONDRET ifcond_eval(PIFCOND pThis)
1192{
1193 IFCONDRET rc;
1194 PCIFCONDOP pOp;
1195
1196 /*
1197 * The main loop.
1198 */
1199 for (;;)
1200 {
1201 /*
1202 * Eat unary operators until we hit an operand.
1203 */
1204 do rc = ifcond_get_unary_or_operand(pThis);
1205 while (rc == kIfCondRet_Operator);
1206 if (rc < kIfCondRet_Error)
1207 break;
1208
1209 /*
1210 * Look for a binary operator, right parenthesis or end of expression.
1211 */
1212 rc = ifcond_get_binary_or_eoe_or_rparen(pThis);
1213 if (rc < kIfCondRet_Error)
1214 break;
1215 ifcond_unget_op(pThis);
1216
1217 /*
1218 * Pop operators and apply them.
1219 *
1220 * Parenthesis will be handed via precedence, where the left parenthesis
1221 * will go pop the right one and make another operator pending.
1222 */
1223 while ( pThis->iOp >= 0
1224 && pThis->apOps[pThis->iOp]->iPrecedence >= pThis->pPending->iPrecedence)
1225 {
1226 pOp = pThis->apOps[pThis->iOp--];
1227 rc = pOp->pfn(pThis);
1228 if (rc < kIfCondRet_Error)
1229 break;
1230 }
1231 if (rc < kIfCondRet_Error)
1232 break;
1233
1234 /*
1235 * Get the next binary operator or end of expression.
1236 * There should be no right parenthesis here.
1237 */
1238 rc = ifcond_get_binary_or_eoe_or_rparen(pThis);
1239 if (rc < kIfCondRet_Error)
1240 break;
1241 pOp = pThis->apOps[pThis->iOp];
1242 if (!pOp->iPrecedence)
1243 break; /* end of expression */
1244 if (!pOp->cArgs)
1245 {
1246 ifcond_error(pThis, "Unexpected \"%s\"", pOp->szOp);
1247 rc = kIfCondRet_Error;
1248 break;
1249 }
1250 }
1251
1252 return rc;
1253}
1254
1255
1256/**
1257 * Destroys the given instance.
1258 *
1259 * @param pThis The instance to destroy.
1260 */
1261static void ifcond_destroy(PIFCOND pThis)
1262{
1263 while (pThis->iVar >= 0)
1264 {
1265 ifcond_var_delete(pThis->aVars);
1266 pThis->iVar--;
1267 }
1268 free(pThis);
1269}
1270
1271
1272/**
1273 * Instantiates an expression evaluator.
1274 *
1275 * @returns The instance.
1276 *
1277 * @param pszExpr What to parse.
1278 * This must stick around until ifcond_destroy.
1279 */
1280static PIFCOND ifcond_create(char const *pszExpr)
1281{
1282 PIFCOND pThis = (PIFCOND)xmalloc(sizeof(*pThis));
1283 pThis->pszExpr = pszExpr;
1284 pThis->psz = pszExpr;
1285 pThis->pFileLoc = NULL;
1286 pThis->pPending = NULL;
1287 pThis->iVar = -1;
1288 pThis->iOp = -1;
1289
1290 ifcond_map_init();
1291 return pThis;
1292}
1293
1294
1295/**
1296 * Evaluates the given if expression.
1297 *
1298 * @returns -1, 0 or 1.
1299 * @retval -1 if the expression is invalid.
1300 * @retval 0 if the expression is true
1301 * @retval 1 if the expression is false.
1302 *
1303 * @param line The expression. Can modify this as we like.
1304 * @param flocp The file location, used for errors.
1305 */
1306int ifcond(char *line, const struct floc *flocp)
1307{
1308 /*
1309 * Instantiate the expression evaluator and let
1310 * it have a go at it.
1311 */
1312 int rc = -1;
1313 PIFCOND pIfCond = ifcond_create(line);
1314 pIfCond->pFileLoc = flocp;
1315 if (ifcond_eval(pIfCond) >= kIfCondRet_Ok)
1316 {
1317 /*
1318 * Convert the result (on top of the stack) to boolean and
1319 * set our return value accordingly.
1320 */
1321 if (ifcond_var_make_bool(&pIfCond->aVars[0]))
1322 rc = 0;
1323 else
1324 rc = 1;
1325 }
1326 ifcond_destroy(pIfCond);
1327
1328 return rc;
1329}
1330
1331
1332#endif /* CONFIG_WITH_IF_CONDITIONALS */
Note: See TracBrowser for help on using the repository browser.