VirtualBox

儲存庫 kBuild 的更動 1719


忽略:
時間撮記:
2008-9-4 上午02:49:36 (16 年 以前)
作者:
bird
訊息:

kmk: More if conditionals code.

位置:
trunk/src/kmk
檔案:
修改 5 筆資料

圖例:

未更動
新增
刪除
  • trunk/src/kmk/Makefile.kmk

    r1716 r1719  
    159159        CONFIG_WITH_NANOTS \
    160160        CONFIG_WITH_SET_CONDITIONALS \
    161         CONFIG_WITH_IF_CONDITIONALS \
    162161        CONFIG_WITH_DATE \
    163162        CONFIG_WITH_FILE_SIZE \
  • trunk/src/kmk/ifcond.c

    r1715 r1719  
    11#ifdef CONFIG_WITH_IF_CONDITIONALS
    2 
     2/* $Id$ */
     3/** @file
     4 * ifcond - C like if expressions.
     5 */
     6
     7/*
     8 * Copyright (c) 2008 knut st. osmundsen <[email protected]>
     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*******************************************************************************/
    331#include "make.h"
    432#include <assert.h>
     
    1442#include "debug.h"
    1543#include "hash.h"
    16 
    17 
    18 
    19 
    20 int ifcond_eval(char *line, const struct floc *flocp)
    21 {
    22     error (flocp, _("if conditionals are not implemented yet"));
    23    
    24     return -1;
     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;
    251329}
    261330
  • trunk/src/kmk/make.h

    r1715 r1719  
    729729
    730730#ifdef CONFIG_WITH_IF_CONDITIONALS
    731 extern int ifcond_eval(char *line, const struct floc *flocp);
    732 #endif
    733 
     731extern int ifcond(char *line, const struct floc *flocp);
     732#endif
     733
  • trunk/src/kmk/read.c

    r1715 r1719  
    21732173  else if (cmdtype == c_ifcond)
    21742174    {
    2175       int rval = ifcond_eval (line, flocp);
     2175      int rval = ifcond (line, flocp);
    21762176      if (rval == -1)
    21772177          return rval;
  • trunk/src/kmk/testcase-if1of.kmk

    r1109 r1719  
    7878
    7979all_recursive:
    80         $(ECHO) "if1of and ifn1of works fine"
     80        $(ECHO) "if1of and ifn1of work fine"
注意: 瀏覽 TracChangeset 來幫助您使用更動檢視器

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