VirtualBox

Changeset 2768 in kBuild for trunk/src/kmk/kmk_cc_exec.c


Ignore:
Timestamp:
Jan 31, 2015 4:49:17 AM (10 years ago)
Author:
bird
Message:

Initial code for the string expansion 'compiler'.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/kmk/kmk_cc_exec.c

    r2767 r2768  
    5353 * these chains when destroying programs, we work with blocks of instructions.
    5454 */
    55 typedef struct kmk_cc_exp_block
     55typedef struct kmk_cc_block
    5656{
    5757    /** The pointer to the next block (LIFO). */
    58     struct kmk_cc_exp_block     *pNext;
     58    struct kmk_cc_block        *pNext;
    5959    /** The size of this block. */
    60     uint32_t                     cbBlock;
    61 } KMKCCEXPBLOCK;
    62 typedef KMKCCEXPBLOCK *PKMKCCEXPBLOCK;
     60    uint32_t                    cbBlock;
     61    /** The offset of the next free byte in the block.  When set to cbBlock the
     62     *  block is 100% full. */
     63    uint32_t                    offNext;
     64} KMKCCBLOCK;
     65typedef KMKCCBLOCK *PKMKCCBLOCK;
    6366
    6467/** Expansion instructions. */
     
    7982    kKmkCcExpInstr_Jump,
    8083    /** We're done, return.  Has no specific structure. */
    81     kKmkCcExpInstr_Done,
     84    kKmkCcExpInstr_Return,
    8285    /** The end of valid instructions (exclusive). */
    8386    kKmkCcExpInstr_End
     
    98101    /** Max expanded size. */
    99102    uint32_t                cbMax;
     103    /** Recent average size. */
     104    uint32_t                cbAvg;
    100105} KMKCCEXPSUBPROG;
    101106typedef KMKCCEXPSUBPROG *PKMKCCEXPSUBPROG;
     
    117122    KMKCCEXPCORE            Core;
    118123    /** The variable strcache entry for this variable. */
    119     struct strcache2_entry *pNameEntry;
     124    struct strcache2_entry const *pNameEntry;
    120125} KMKCCEXPPLAINVAR;
    121126typedef KMKCCEXPPLAINVAR *PKMKCCEXPPLAINVAR;
     
    140145    KMKCCEXPCORE            Core;
    141146    /** Number of arguments. */
    142     uint8_t                 cArgs;
     147    uint32_t                cArgs;
    143148    /** Where to continue after this instruction.  This is necessary since the
    144149     * instruction is of variable size and we don't even know if we're still in the
     
    170175} KMKCCEXPPLAINFUNC;
    171176typedef KMKCCEXPPLAINFUNC *PKMKCCEXPPLAINFUNC;
     177/** Calculates the size of an KMKCCEXPPLAINFUNC with a_cArgs. */
     178#define KMKCCEXPPLAINFUNC_SIZE(a_cArgs)  (sizeof(KMKCCEXPFUNCCORE) + (a_cArgs + 1) * sizeof(const char *))
    172179
    173180typedef struct kmk_cc_exp_dyn_function
     
    198205} KMKCCEXPDYNFUNC;
    199206typedef KMKCCEXPDYNFUNC *PKMKCCEXPDYNFUNC;
     207/** Calculates the size of an KMKCCEXPPLAINFUNC with a_cArgs. */
     208#define KMKCCEXPDYNFUNC_SIZE(a_cArgs)  (  sizeof(KMKCCEXPFUNCCORE) \
     209                                          + (a_cArgs) * sizeof(((PKMKCCEXPDYNFUNC)(uintptr_t)42)->aArgs[0]) )
    200210
    201211typedef struct kmk_cc_exp_jump
     
    214224{
    215225    /** Pointer to the first instruction for this program. */
    216     PKMKCCEXPCORE   pFirstInstr;
     226    PKMKCCEXPCORE           pFirstInstr;
    217227    /** List of blocks for this program (LIFO). */
    218     PKMKCCEXPBLOCK  pBlockTail;
     228    PKMKCCBLOCK             pBlockTail;
    219229    /** Max expanded size. */
    220     uint32_t        cbMax;
    221 } KMKCCEXPANDPROG;
     230    uint32_t                cbMax;
     231    /** Recent average size. */
     232    uint32_t                cbAvg;
     233} KMKCCEXPPROG;
    222234/** Pointer to a string expansion program. */
    223 typedef KMKCCEXPANDPROG *PKMKCCEXPANDPROG;
     235typedef KMKCCEXPPROG *PKMKCCEXPPROG;
    224236
    225237
     
    229241
    230242
     243/*******************************************************************************
     244*   Internal Functions                                                         *
     245*******************************************************************************/
     246static int kmk_cc_exp_compile_subprog(PKMKCCBLOCK *ppBlockTail, const char *pchStr, uint32_t cchStr, PKMKCCEXPSUBPROG pSubProg);
     247
     248
    231249/**
    232250 * Initializes global variables for the 'compiler'.
     
    235253{
    236254}
     255
     256
     257/**
     258 * For the first allocation using the block allocator.
     259 *
     260 * @returns Pointer to the first allocation (@a cbFirst in size).
     261 * @param   ppBlockTail         Where to return the pointer to the first block.
     262 * @param   cbFirst             The size of the first allocation.
     263 * @param   cbHint              Hint about how much memory we might be needing.
     264 */
     265static void *kmk_cc_block_alloc_first(PKMKCCBLOCK *ppBlockTail, size_t cbFirst, size_t cbHint)
     266{
     267    uint32_t        cbBlock;
     268    PKMKCCBLOCK     pNewBlock;
     269
     270    assert(((cbFirst + sizeof(void *) - 1) & (sizeof(void *) - 1)) == 0);
     271
     272    /*
     273     * Turn the hint into a block size.
     274     */
     275    if (cbHint <= 512)
     276        cbBlock = 512;
     277    else if (cbHint < 2048)
     278        cbBlock = 1024;
     279    else if (cbHint < 3072)
     280        cbBlock = 2048;
     281    else
     282        cbBlock = 4096;
     283
     284    /*
     285     * Allocate and initialize the first block.
     286     */
     287    pNewBlock = (PKMKCCBLOCK)xmalloc(cbBlock);
     288    pNewBlock->cbBlock = cbBlock;
     289    pNewBlock->offNext = sizeof(*pNewBlock) + cbFirst;
     290    pNewBlock->pNext   = NULL;
     291    *ppBlockTail = pNewBlock;
     292
     293    return pNewBlock + 1;
     294}
     295
     296
     297/**
     298 * Used for getting the address of the next instruction.
     299 *
     300 * @returns Pointer to the next allocation.
     301 * @param   pBlockTail          The allocator tail pointer.
     302 */
     303static void *kmk_cc_block_get_next_ptr(PKMKCCBLOCK pBlockTail)
     304{
     305    return (char *)pBlockTail + pBlockTail->offNext;
     306}
     307
     308
     309/**
     310 * Realigns the allocator after doing byte or string allocations.
     311 *
     312 * @param   ppBlockTail         Pointer to the allocator tail pointer.
     313 */
     314static void kmk_cc_block_realign(PKMKCCBLOCK *ppBlockTail)
     315{
     316    PKMKCCBLOCK pBlockTail = *ppBlockTail;
     317    if (pBlockTail->offNext & (sizeof(void *) - 1))
     318    {
     319        pBlockTail->offNext = (pBlockTail->offNext + sizeof(void *) - 1) & ~(sizeof(void *) - 1);
     320        assert(pBlockTail->cbBlock - pBlockTail->offNext >= sizeof(KMKCCEXPJUMP));
     321    }
     322}
     323
     324
     325/**
     326 * Grows the allocation with another block, byte allocator case.
     327 *
     328 * @returns Pointer to the byte allocation.
     329 * @param   ppBlockTail         Pointer to the allocator tail pointer.
     330 * @param   cb                  The number of bytes to allocate.
     331 */
     332static void *kmk_cc_block_byte_alloc_grow(PKMKCCBLOCK *ppBlockTail, uint32_t cb)
     333{
     334    PKMKCCBLOCK     pOldBlock  = *ppBlockTail;
     335    PKMKCCBLOCK     pPrevBlock = pOldBlock->pNext;
     336    PKMKCCBLOCK     pNewBlock;
     337    uint32_t        cbBlock;
     338
     339    /*
     340     * Check if there accidentally is some space left in the previous block first.
     341     */
     342    if (   pPrevBlock
     343        && pPrevBlock->cbBlock - pPrevBlock->offNext >= cb)
     344    {
     345        void *pvRet = (char *)pPrevBlock + pPrevBlock->offNext;
     346        pPrevBlock->offNext += cb;
     347        return pvRet;
     348    }
     349
     350    /*
     351     * Allocate a new block.
     352     */
     353
     354    /* Figure the block size. */
     355    cbBlock = pOldBlock->cbBlock;
     356    while (cbBlock - sizeof(KMKCCEXPJUMP) - sizeof(*pNewBlock) < cb)
     357        cbBlock *= 2;
     358
     359    /* Allocate and initialize the block it with the new instruction already accounted for. */
     360    pNewBlock = (PKMKCCBLOCK)xmalloc(cbBlock);
     361    pNewBlock->cbBlock = cbBlock;
     362    pNewBlock->offNext = sizeof(*pNewBlock) + cb;
     363    pNewBlock->pNext   = pOldBlock;
     364    *ppBlockTail = pNewBlock;
     365
     366    return pNewBlock + 1;
     367}
     368
     369
     370/**
     371 * Make a byte allocation.
     372 *
     373 * Must call kmk_cc_block_realign() when done doing byte and string allocations.
     374 *
     375 * @returns Pointer to the byte allocation (byte aligned).
     376 * @param   ppBlockTail         Pointer to the allocator tail pointer.
     377 * @param   cb                  The number of bytes to allocate.
     378 */
     379static void *kmk_cc_block_byte_alloc(PKMKCCBLOCK *ppBlockTail, uint32_t cb)
     380{
     381    PKMKCCBLOCK pBlockTail = *ppBlockTail;
     382    uint32_t    cbLeft = pBlockTail->cbBlock - pBlockTail->offNext;
     383
     384    assert(cbLeft >= sizeof(KMKCCEXPJUMP));
     385    if (cbLeft >= cb + sizeof(KMKCCEXPJUMP))
     386    {
     387        void *pvRet = (char *)pBlockTail + pBlockTail->offNext;
     388        pBlockTail->offNext += cb;
     389        return pvRet;
     390    }
     391    return kmk_cc_block_byte_alloc_grow(ppBlockTail, cb);
     392}
     393
     394
     395/**
     396 * Duplicates the given string in a byte allocation.
     397 *
     398 * Must call kmk_cc_block_realign() when done doing byte and string allocations.
     399 *
     400 * @returns Pointer to the byte allocation (byte aligned).
     401 * @param   ppBlockTail         Pointer to the allocator tail pointer.
     402 * @param   cb                  The number of bytes to allocate.
     403 */
     404static const char *kmk_cc_block_strdup(PKMKCCBLOCK *ppBlockTail, const char *pachStr, uint32_t cchStr)
     405{
     406    char *pszCopy;
     407    if (cchStr)
     408    {
     409        pszCopy = kmk_cc_block_byte_alloc(ppBlockTail, cchStr + 1);
     410        memcpy(pszCopy, pachStr, cchStr);
     411        pszCopy[cchStr] = '\0';
     412        return pszCopy;
     413    }
     414    return "";
     415}
     416
     417
     418/**
     419 * Grows the allocation with another block, string expansion program case.
     420 *
     421 * @returns Pointer to a string expansion instruction core.
     422 * @param   ppBlockTail         Pointer to the allocator tail pointer.
     423 * @param   cb                  The number of bytes to allocate.
     424 */
     425static PKMKCCEXPCORE kmk_cc_block_alloc_exp_grow(PKMKCCBLOCK *ppBlockTail, uint32_t cb)
     426{
     427    PKMKCCBLOCK     pOldBlock = *ppBlockTail;
     428    PKMKCCBLOCK     pNewBlock;
     429    PKMKCCEXPCORE   pRet;
     430    PKMKCCEXPJUMP   pJump;
     431
     432    /* Figure the block size. */
     433    uint32_t cbBlock = pOldBlock->cbBlock;
     434    while (cbBlock - sizeof(KMKCCEXPJUMP) - sizeof(*pNewBlock) < cb)
     435        cbBlock *= 2;
     436
     437    /* Allocate and initialize the block it with the new instruction already accounted for. */
     438    pNewBlock = (PKMKCCBLOCK)xmalloc(cbBlock);
     439    pNewBlock->cbBlock = cbBlock;
     440    pNewBlock->offNext = sizeof(*pNewBlock) + cb;
     441    pNewBlock->pNext   = pOldBlock;
     442    *ppBlockTail = pNewBlock;
     443
     444    pRet = (PKMKCCEXPCORE)(pNewBlock + 1);
     445
     446    /* Emit jump. */
     447    pJump = (PKMKCCEXPJUMP)((char *)pOldBlock + pOldBlock->offNext);
     448    pJump->Core.enmOpCode = kKmkCcExpInstr_Jump;
     449    pJump->pNext = pRet;
     450    pOldBlock->offNext += sizeof(*pJump);
     451    assert(pOldBlock->offNext <= pOldBlock->cbBlock);
     452
     453    return pRet;
     454}
     455
     456
     457/**
     458 * Allocates a string expansion instruction of size @a cb.
     459 *
     460 * @returns Pointer to a string expansion instruction core.
     461 * @param   ppBlockTail         Pointer to the allocator tail pointer.
     462 * @param   cb                  The number of bytes to allocate.
     463 */
     464static PKMKCCEXPCORE kmk_cc_block_alloc_exp(PKMKCCBLOCK *ppBlockTail, uint32_t cb)
     465{
     466    PKMKCCBLOCK pBlockTail = *ppBlockTail;
     467    uint32_t    cbLeft = pBlockTail->cbBlock - pBlockTail->offNext;
     468
     469    assert(cbLeft >= sizeof(KMKCCEXPJUMP));
     470    assert(((cb + sizeof(void *) - 1) & (sizeof(void *) - 1)) == 0 || cb == sizeof(KMKCCEXPCORE));
     471
     472    if (cbLeft >= cb + sizeof(KMKCCEXPJUMP))
     473    {
     474        PKMKCCEXPCORE pRet = (PKMKCCEXPCORE)((char *)pBlockTail + pBlockTail->offNext);
     475        pBlockTail->offNext += cb;
     476        return pRet;
     477    }
     478    return kmk_cc_block_alloc_exp_grow(ppBlockTail, cb);
     479}
     480
     481
     482/**
     483 * Frees all memory used by an allocator.
     484 *
     485 * @param   ppBlockTail         The allocator tail pointer.
     486 */
     487static void kmk_cc_block_free_list(PKMKCCBLOCK pBlockTail)
     488{
     489    while (pBlockTail)
     490    {
     491        PKMKCCBLOCK pThis = pBlockTail;
     492        pBlockTail = pBlockTail->pNext;
     493        free(pThis);
     494    }
     495}
     496
     497
     498/**
     499 * Counts the number of dollar chars in the string.
     500 *
     501 * @returns Number of dollar chars.
     502 * @param   pchStr      The string to search (does not need to be zero
     503 *                      terminated).
     504 * @param   cchStr      The length of the string.
     505 */
     506static uint32_t kmk_cc_count_dollars(const char *pchStr, uint32_t cchStr)
     507{
     508    uint32_t cDollars = 0;
     509    const char *pch;
     510    while ((pch = memchr(pchStr, '$', cchStr)) != NULL)
     511    {
     512        cDollars++;
     513        cchStr -= pch - pchStr + 1;
     514        pchStr  = pch + 1;
     515    }
     516    return cDollars;
     517}
     518
     519
     520/**
     521 * Emits a kKmkCcExpInstr_Return.
     522 *
     523 * @returns 0 on success, non-zero on failure.
     524 * @param   ppBlockTail         Pointer to the allocator tail pointer.
     525 */
     526static int kmk_cc_exp_emit_return(PKMKCCBLOCK *ppBlockTail)
     527{
     528    PKMKCCEXPCORE pCore = kmk_cc_block_alloc_exp(ppBlockTail, sizeof(*pCore));
     529    pCore->enmOpCode = kKmkCcExpInstr_Return;
     530    return 0;
     531}
     532
     533
     534/**
     535 * Emits a function call instruction taking arguments that needs expanding.
     536 *
     537 * @returns 0 on success, non-zero on failure.
     538 * @param   ppBlockTail     Pointer to the allocator tail pointer.
     539 * @param   pszFunction     The function name (const string from function.c).
     540 * @param   pchArgs         Pointer to the arguments expression string, leading
     541 *                          any blanks has been stripped.
     542 * @param   cchArgs         The length of the arguments expression string.
     543 * @param   cArgs           Number of arguments found.
     544 * @param   chOpen          The char used to open the function call.
     545 * @param   chClose         The char used to close the function call.
     546 * @param   pfnFunction     The function implementation.
     547 * @param   cMaxArgs        Maximum number of arguments the function takes.
     548 */
     549static int kmk_cc_exp_emit_dyn_function(PKMKCCBLOCK *ppBlockTail, const char *pszFunction,
     550                                        const char *pchArgs, uint32_t cchArgs, uint32_t cArgs, char chOpen, char chClose,
     551                                        make_function_ptr_t pfnFunction, unsigned char cMaxArgs)
     552{
     553    uint32_t iArg;
     554
     555    /*
     556     * The function instruction has variable size.  The maximum argument count
     557     * isn't quite like the minium one.  Zero means no limit.  While a non-zero
     558     * value means that any commas beyond the max will be taken to be part of
     559     * the final argument.
     560     */
     561    uint32_t            cActualArgs = cArgs <= cMaxArgs || !cMaxArgs ? cArgs : cMaxArgs;
     562    PKMKCCEXPDYNFUNC    pInstr  = (PKMKCCEXPDYNFUNC)kmk_cc_block_alloc_exp(ppBlockTail, KMKCCEXPDYNFUNC_SIZE(cActualArgs));
     563    pInstr->Core.Core.enmOpCode = kKmkCcExpInstr_DynamicFunction;
     564    pInstr->Core.cArgs          = cActualArgs;
     565    pInstr->Core.pfnFunction    = pfnFunction;
     566    pInstr->Core.pszFuncName    = pszFunction;
     567
     568    /*
     569     * Parse the arguments.  Plain arguments gets duplicated in the program
     570     * memory so that they are terminated and no extra processing is necessary
     571     * later on.  ASSUMES that the function implementations do NOT change
     572     * argument memory.  Other arguments the compiled into their own expansion
     573     * sub programs.
     574     */
     575    iArg = 0;
     576    for (;;)
     577    {
     578        /* Find the end of the argument. Check for $. */
     579        char     ch         = '\0';
     580        uint8_t  fDollar    = 0;
     581        int32_t  cDepth     = 0;
     582        uint32_t cchThisArg = 0;
     583        while (cchThisArg < cchArgs)
     584        {
     585            ch = pchArgs[cchThisArg];
     586            if (ch == chClose)
     587            {
     588                assert(cDepth > 0);
     589                if (cDepth > 0)
     590                    cDepth--;
     591            }
     592            else if (ch == chOpen)
     593                cDepth++;
     594            else if (ch == ',' && cDepth == 0)
     595                break;
     596            else if (ch == '$')
     597                fDollar = 1;
     598            cchThisArg++;
     599        }
     600
     601        pInstr->aArgs[iArg].fPlain = fDollar;
     602        if (fDollar)
     603        {
     604            /* Compile it. */
     605            int rc;
     606            kmk_cc_block_realign(ppBlockTail);
     607            rc = kmk_cc_exp_compile_subprog(ppBlockTail, pchArgs, cchThisArg, &pInstr->aArgs[iArg].u.SubProg);
     608            if (rc != 0)
     609                return rc;
     610        }
     611        else
     612        {
     613            /* Duplicate it. */
     614            pInstr->aArgs[iArg].u.Plain.pszArg = kmk_cc_block_strdup(ppBlockTail, pchArgs, cchThisArg);
     615        }
     616        iArg++;
     617        if (ch != ',')
     618            break;
     619        pchArgs += cchThisArg + 1;
     620        cchArgs -= cchThisArg + 1;
     621    }
     622    assert(iArg == cActualArgs);
     623
     624    /*
     625     * Realign the allocator and take down the address of the next instruction.
     626     */
     627    kmk_cc_block_realign(ppBlockTail);
     628    pInstr->Core.pNext = (PKMKCCEXPCORE)kmk_cc_block_get_next_ptr(*ppBlockTail);
     629    return 0;
     630}
     631
     632
     633/**
     634 * Emits a function call instruction taking plain arguments.
     635 *
     636 * @returns 0 on success, non-zero on failure.
     637 * @param   ppBlockTail     Pointer to the allocator tail pointer.
     638 * @param   pszFunction     The function name (const string from function.c).
     639 * @param   pchArgs         Pointer to the arguments string, leading any blanks
     640 *                          has been stripped.
     641 * @param   cchArgs         The length of the arguments string.
     642 * @param   cArgs           Number of arguments found.
     643 * @param   chOpen          The char used to open the function call.
     644 * @param   chClose         The char used to close the function call.
     645 * @param   pfnFunction     The function implementation.
     646 * @param   cMaxArgs        Maximum number of arguments the function takes.
     647 */
     648static int kmk_cc_exp_emit_plain_function(PKMKCCBLOCK *ppBlockTail, const char *pszFunction,
     649                                          const char *pchArgs, uint32_t cchArgs, uint32_t cArgs, char chOpen, char chClose,
     650                                          make_function_ptr_t pfnFunction, unsigned char cMaxArgs)
     651{
     652    uint32_t iArg;
     653
     654    /*
     655     * The function instruction has variable size.  The maximum argument count
     656     * isn't quite like the minium one.  Zero means no limit.  While a non-zero
     657     * value means that any commas beyond the max will be taken to be part of
     658     * the final argument.
     659     */
     660    uint32_t            cActualArgs = cArgs <= cMaxArgs || !cMaxArgs ? cArgs : cMaxArgs;
     661    PKMKCCEXPPLAINFUNC  pInstr  = (PKMKCCEXPPLAINFUNC)kmk_cc_block_alloc_exp(ppBlockTail, KMKCCEXPPLAINFUNC_SIZE(cActualArgs));
     662    pInstr->Core.Core.enmOpCode = kKmkCcExpInstr_PlainFunction;
     663    pInstr->Core.cArgs          = cActualArgs;
     664    pInstr->Core.pfnFunction    = pfnFunction;
     665    pInstr->Core.pszFuncName    = pszFunction;
     666
     667    /*
     668     * Parse the arguments.  Plain arguments gets duplicated in the program
     669     * memory so that they are terminated and no extra processing is necessary
     670     * later on.  ASSUMES that the function implementations do NOT change
     671     * argument memory.
     672     */
     673    iArg = 0;
     674    for (;;)
     675    {
     676        /* Find the end of the argument. */
     677        char     ch         = '\0';
     678        int32_t  cDepth     = 0;
     679        uint32_t cchThisArg = 0;
     680        while (cchThisArg < cchArgs)
     681        {
     682            ch = pchArgs[cchThisArg];
     683            if (ch == chClose)
     684            {
     685                assert(cDepth > 0);
     686                if (cDepth > 0)
     687                    cDepth--;
     688            }
     689            else if (ch == chOpen)
     690                cDepth++;
     691            else if (ch == ',' && cDepth == 0)
     692                break;
     693            cchThisArg++;
     694        }
     695
     696        /* Duplicate it. */
     697        pInstr->apszArgs[iArg++] = kmk_cc_block_strdup(ppBlockTail, pchArgs, cchThisArg);
     698        if (ch != ',')
     699            break;
     700        pchArgs += cchThisArg + 1;
     701        cchArgs -= cchThisArg + 1;
     702    }
     703
     704    assert(iArg == cActualArgs);
     705    pInstr->apszArgs[iArg] = NULL;
     706
     707    /*
     708     * Realign the allocator and take down the address of the next instruction.
     709     */
     710    kmk_cc_block_realign(ppBlockTail);
     711    pInstr->Core.pNext = (PKMKCCEXPCORE)kmk_cc_block_get_next_ptr(*ppBlockTail);
     712    return 0;
     713}
     714
     715
     716/**
     717 * Emits a kKmkCcExpInstr_DynamicVariable.
     718 *
     719 * @returns 0 on success, non-zero on failure.
     720 * @param   ppBlockTail         Pointer to the allocator tail pointer.
     721 * @param   pchNameExpr         The name of the variable (ASSUMED presistent
     722 *                              thru-out the program life time).
     723 * @param   cchNameExpr         The length of the variable name. If zero,
     724 *                              nothing will be emitted.
     725 */
     726static int kmk_cc_exp_emit_dyn_variable(PKMKCCBLOCK *ppBlockTail, const char *pchNameExpr, uint32_t cchNameExpr)
     727{
     728    PKMKCCEXPDYNVAR pInstr;
     729    int rc;
     730    assert(cchNameExpr > 0);
     731
     732    pInstr = (PKMKCCEXPDYNVAR)kmk_cc_block_alloc_exp(ppBlockTail, sizeof(*pInstr));
     733    pInstr->Core.enmOpCode = kKmkCcExpInstr_DynamicVariable;
     734
     735    rc = kmk_cc_exp_compile_subprog(ppBlockTail, pchNameExpr, cchNameExpr, &pInstr->SubProg);
     736
     737    pInstr->pNext = (PKMKCCEXPCORE)kmk_cc_block_get_next_ptr(*ppBlockTail);
     738    return rc;
     739}
     740
     741
     742/**
     743 * Emits a kKmkCcExpInstr_PlainVariable.
     744 *
     745 * @returns 0 on success, non-zero on failure.
     746 * @param   ppBlockTail         Pointer to the allocator tail pointer.
     747 * @param   pchName             The name of the variable.  (Does not need to be
     748 *                              valid beyond the call.)
     749 * @param   cchName             The length of the variable name. If zero,
     750 *                              nothing will be emitted.
     751 */
     752static int kmk_cc_exp_emit_plain_variable(PKMKCCBLOCK *ppBlockTail, const char *pchName, uint32_t cchName)
     753{
     754    if (cchName > 0)
     755    {
     756        PKMKCCEXPPLAINVAR pInstr = (PKMKCCEXPPLAINVAR)kmk_cc_block_alloc_exp(ppBlockTail, sizeof(*pInstr));
     757        pInstr->Core.enmOpCode = kKmkCcExpInstr_PlainVariable;
     758        pInstr->pNameEntry = strcache2_get_entry(&variable_strcache, strcache2_add(&variable_strcache, pchName, cchName));
     759    }
     760    return 0;
     761}
     762
     763
     764/**
     765 * Emits a kKmkCcExpInstr_CopyString.
     766 *
     767 * @returns 0 on success, non-zero on failure.
     768 * @param   ppBlockTail         Pointer to the allocator tail pointer.
     769 * @param   pchStr              The string to emit (ASSUMED presistent thru-out
     770 *                              the program life time).
     771 * @param   cchStr              The number of chars to copy. If zero, nothing
     772 *                              will be emitted.
     773 */
     774static int kmk_cc_exp_emit_copy_string(PKMKCCBLOCK *ppBlockTail, const char *pchStr, uint32_t cchStr)
     775{
     776    if (cchStr > 0)
     777    {
     778        PKMKCCEXPCOPYSTRING pInstr = (PKMKCCEXPCOPYSTRING)kmk_cc_block_alloc_exp(ppBlockTail, sizeof(*pInstr));
     779        pInstr->Core.enmOpCode = kKmkCcExpInstr_CopyString;
     780        pInstr->cchCopy = cchStr;
     781        pInstr->pachSrc = pchStr;
     782    }
     783    return 0;
     784}
     785
     786
     787/**
     788 * String expansion compilation function common to both normal and sub programs.
     789 *
     790 * @returns 0 on success, non-zero on failure.
     791 * @param   ppBlockTail         Pointer to the allocator tail pointer.
     792 * @param   pchStr              The expression to compile.
     793 * @param   cchStr              The length of the expression to compile.
     794 */
     795static int kmk_cc_exp_compile_common(PKMKCCBLOCK *ppBlockTail, const char *pchStr, uint32_t cchStr)
     796{
     797    /*
     798     * Process the string.
     799     */
     800    while (cchStr > 0)
     801    {
     802        /* Look for dollar sign, marks variable expansion or dollar-escape. */
     803        int         rc;
     804        const char *pchDollar = memchr(pchStr, '$', cchStr);
     805        if (pchDollar)
     806        {
     807            /*
     808             * Check for multiple dollar chars.
     809             */
     810            uint32_t offDollar = (uint32_t)(pchDollar - pchStr);
     811            uint32_t cDollars  = 1;
     812            while (   offDollar + cDollars < cchStr
     813                   && pchStr[offDollar + cDollars] == '$')
     814                cDollars++;
     815
     816            /*
     817             * Emit a string copy for any preceeding stuff, including half of
     818             * the dollars we found (dollar escape: $$ -> $).
     819             * (kmk_cc_exp_emit_copy_string ignore zero length strings).
     820             */
     821            rc = kmk_cc_exp_emit_copy_string(ppBlockTail, pchStr, offDollar + cDollars / 2);
     822            if (rc != 0)
     823                return rc;
     824            pchStr += offDollar + cDollars;
     825            cchStr -= offDollar + cDollars;
     826
     827            /*
     828             * Odd number of dollar chars means there is a variable to expand
     829             * or function to call.
     830             */
     831            if (cDollars & 1)
     832            {
     833                if (cchStr > 0)
     834                {
     835                    char const chOpen = *pchStr;
     836                    if (chOpen == '(' || chOpen == '{')
     837                    {
     838                        /* There are several alternative ways of finding the ending
     839                           parenthesis / braces.  GNU make only consideres open &
     840                           close chars of the one we're processing, and it does not
     841                           matter whether the opening paren / braces are preceeded by
     842                           any dollar char.  Simple and efficient.  */
     843                        make_function_ptr_t pfnFunction;
     844                        const char         *pszFunction;
     845                        unsigned char       cMaxArgs;
     846                        unsigned char       cMinArgs;
     847                        char                fExpandArgs;
     848                        char const          chClose   = chOpen == '(' ? ')' : '}';
     849                        char                ch        = 0;
     850                        uint32_t            cchName   = 0;
     851                        uint32_t            cDepth    = 1;
     852                        uint32_t            cMaxDepth = 1;
     853                        cDollars = 0;
     854
     855                        pchStr++;
     856                        cchStr--;
     857
     858                        /* First loop: Identify potential function calls and dynamic expansion. */
     859                        assert(!func_char_map[chOpen]); assert(!func_char_map[chClose]); assert(!func_char_map['$']);
     860                        while (cchName < cchStr)
     861                        {
     862                            ch = pchStr[cchName];
     863                            if (!func_char_map[(int)ch])
     864                                break;
     865                            cchName++;
     866                        }
     867                        if (   cchName >= MIN_FUNCTION_LENGTH
     868                            && cchName <= MAX_FUNCTION_LENGTH
     869                            && (isblank(ch) || ch == chClose || cchName == cchStr)
     870                            && (pfnFunction = lookup_function_for_compiler(pchStr, cchName, &cMinArgs, &cMaxArgs,
     871                                                                           &fExpandArgs, &pszFunction)) != NULL)
     872                        {
     873                            /*
     874                             * It's a function invocation, we should count parameters while
     875                             * looking for the end.
     876                             * Note! We use cchName for the length of the argument list.
     877                             */
     878                            uint32_t cArgs = 1;
     879                            if (ch != chClose)
     880                            {
     881                                /* Skip leading spaces before the first arg. */
     882                                cchName++;
     883                                while (cchName < cchStr && isblank((unsigned char)pchStr[cchName]))
     884                                    cchName++;
     885
     886                                pchStr += cchName;
     887                                cchStr -= cchName;
     888                                cchName = 0;
     889
     890                                while (cchName < cchStr)
     891                                {
     892                                    ch = pchStr[cchName];
     893                                    if (ch == ',')
     894                                    {
     895                                        if (cDepth == 1)
     896                                            cArgs++;
     897                                    }
     898                                    else if (ch == chClose)
     899                                    {
     900                                        if (!--cDepth)
     901                                            break;
     902                                    }
     903                                    else if (ch == chOpen)
     904                                    {
     905                                        if (++cDepth > cMaxDepth)
     906                                            cMaxDepth = cDepth;
     907                                    }
     908                                    else if (ch == '$')
     909                                        cDollars++;
     910                                    cchName++;
     911                                }
     912                            }
     913                            else
     914                            {
     915                                pchStr += cchName;
     916                                cchStr -= cchName;
     917                                cchName = 0;
     918                            }
     919                            if (cArgs < cMinArgs)
     920                            {
     921                                fatal(NULL, _("Function '%.*s' takes a minimum of %d arguments: %d given"),
     922                                      pszFunction, (int)cMinArgs, (int)cArgs);
     923                                return -1; /* not reached */
     924                            }
     925                            if (cDepth != 0)
     926                            {
     927                                fatal(NULL, chOpen == '('
     928                                      ? _("Missing closing parenthesis calling '%s'") : _("Missing closing braces calling '%s'"),
     929                                      pszFunction);
     930                                return -1; /* not reached */
     931                            }
     932                            if (cMaxDepth > 16 && fExpandArgs)
     933                            {
     934                                fatal(NULL, _("Too many levels of nested function arguments expansions: %s"), pszFunction);
     935                                return -1; /* not reached */
     936                            }
     937                            if (!fExpandArgs || cDollars == 0)
     938                                rc = kmk_cc_exp_emit_plain_function(ppBlockTail, pszFunction, pchStr, cchName,
     939                                                                    cArgs, chOpen, chClose, pfnFunction, cMaxArgs);
     940                            else
     941                                rc = kmk_cc_exp_emit_dyn_function(ppBlockTail, pszFunction, pchStr, cchName,
     942                                                                  cArgs, chOpen, chClose, pfnFunction, cMaxArgs);
     943                        }
     944                        else
     945                        {
     946                            /*
     947                             * Variable, find the end while checking whether anything needs expanding.
     948                             */
     949                            if (ch == chClose)
     950                                cDepth = 0;
     951                            else if (cchName < cchStr)
     952                            {
     953                                if (ch != '$')
     954                                {
     955                                    /* Second loop: Look for things that needs expanding. */
     956                                    while (cchName < cchStr)
     957                                    {
     958                                        ch = pchStr[cchName];
     959                                        if (ch == chClose)
     960                                        {
     961                                            if (!--cDepth)
     962                                                break;
     963                                        }
     964                                        else if (ch == chOpen)
     965                                        {
     966                                            if (++cDepth > cMaxDepth)
     967                                                cMaxDepth = cDepth;
     968                                        }
     969                                        else if (ch == '$')
     970                                            break;
     971                                        cchName++;
     972                                    }
     973                                }
     974                                if (ch == '$')
     975                                {
     976                                    /* Third loop: Something needs expanding, just find the end. */
     977                                    cDollars = 1;
     978                                    cchName++;
     979                                    while (cchName < cchStr)
     980                                    {
     981                                        ch = pchStr[cchName];
     982                                        if (ch == chClose)
     983                                        {
     984                                            if (!--cDepth)
     985                                                break;
     986                                        }
     987                                        else if (ch == chOpen)
     988                                        {
     989                                            if (++cDepth > cMaxDepth)
     990                                                cMaxDepth = cDepth;
     991                                        }
     992                                        cchName++;
     993                                    }
     994                                }
     995                            }
     996                            if (cDepth > 0) /* After warning, we just assume they're all there. */
     997                                error(NULL, chOpen == '(' ? _("Missing closing parenthesis ") : _("Missing closing braces"));
     998                            if (cMaxDepth >= 16)
     999                            {
     1000                                fatal(NULL, _("Too many levels of nested variable expansions: '%.*s'"), (int)cchName + 2, pchStr - 1);
     1001                                return -1; /* not reached */
     1002                            }
     1003                            if (cDollars == 0)
     1004                                rc = kmk_cc_exp_emit_plain_variable(ppBlockTail, pchStr, cchName);
     1005                            else
     1006                                rc = kmk_cc_exp_emit_dyn_variable(ppBlockTail, pchStr, cchName);
     1007                        }
     1008                        pchStr += cchName + 1;
     1009                        cchStr -= cchName + (cDepth == 0);
     1010                    }
     1011                    else
     1012                    {
     1013                        /* Single character variable name. */
     1014                        rc = kmk_cc_exp_emit_plain_variable(ppBlockTail, pchStr, 1);
     1015                        pchStr++;
     1016                        cchStr--;
     1017                    }
     1018                    if (rc != 0)
     1019                        return rc;
     1020                }
     1021                else
     1022                {
     1023                    error(NULL, _("Unexpected end of string after $"));
     1024                    break;
     1025                }
     1026            }
     1027        }
     1028        else
     1029        {
     1030            /*
     1031             * Nothing more to expand, the remainder is a simple string copy.
     1032             */
     1033            rc = kmk_cc_exp_emit_copy_string(ppBlockTail, pchStr, cchStr);
     1034            if (rc != 0)
     1035                return rc;
     1036            break;
     1037        }
     1038    }
     1039
     1040    /*
     1041     * Emit final instruction.
     1042     */
     1043    return kmk_cc_exp_emit_return(ppBlockTail);
     1044}
     1045
     1046
     1047/**
     1048 * Compiles a string expansion sub program.
     1049 *
     1050 * The caller typically make a call to kmk_cc_block_get_next_ptr after this
     1051 * function returns to figure out where to continue executing.
     1052 *
     1053 * @returns 0 on success, non-zero on failure.
     1054 * @param   ppBlockTail         Pointer to the allocator tail pointer.
     1055 * @param   pchStr              Pointer to the string to compile an expansion
     1056 *                              program for (ASSUMED to be valid for the
     1057 *                              lifetime of the program).
     1058 * @param   cchStr              The length of the string to compile. Expected to
     1059 *                              be at least on char long.
     1060 * @param   pSubProg            The sub program structure to initialize.
     1061 */
     1062static int kmk_cc_exp_compile_subprog(PKMKCCBLOCK *ppBlockTail, const char *pchStr, uint32_t cchStr, PKMKCCEXPSUBPROG pSubProg)
     1063{
     1064    assert(cchStr);
     1065    pSubProg->pFirstInstr = (PKMKCCEXPCORE)kmk_cc_block_get_next_ptr(*ppBlockTail);
     1066    pSubProg->cbMax = 0;
     1067    pSubProg->cbAvg = 0;
     1068    return kmk_cc_exp_compile_common(ppBlockTail, pchStr, cchStr);
     1069}
     1070
     1071
     1072/**
     1073 * Compiles a string expansion program.
     1074 *
     1075 * @returns Pointer to the program on success, NULL on failure.
     1076 * @param   pchStr              Pointer to the string to compile an expansion
     1077 *                              program for (ASSUMED to be valid for the
     1078 *                              lifetime of the program).
     1079 * @param   cchStr              The length of the string to compile. Expected to
     1080 *                              be at least on char long.
     1081 */
     1082static PKMKCCEXPPROG kmk_cc_exp_compile(const char *pchStr, uint32_t cchStr)
     1083{
     1084    /*
     1085     * Estimate block size, allocate one and initialize it.
     1086     */
     1087    PKMKCCEXPPROG   pProg;
     1088    PKMKCCBLOCK     pBlock;
     1089    pProg = kmk_cc_block_alloc_first(&pBlock, sizeof(*pProg),
     1090                                     (kmk_cc_count_dollars(pchStr, cchStr) + 8)  * 16);
     1091    if (pProg)
     1092    {
     1093        int rc = 0;
     1094
     1095        pProg->pBlockTail   = pBlock;
     1096        pProg->pFirstInstr  = (PKMKCCEXPCORE)kmk_cc_block_get_next_ptr(pBlock);
     1097        pProg->cbMax        = 0;
     1098        pProg->cbAvg        = 0;
     1099
     1100        /*
     1101         * Join forces with the sub program compilation code.
     1102         */
     1103        if (kmk_cc_exp_compile_common(&pProg->pBlockTail, pchStr, cchStr) == 0)
     1104            return pProg;
     1105        kmk_cc_block_free_list(pProg->pBlockTail);
     1106    }
     1107    return NULL;
     1108}
     1109
    2371110
    2381111
     
    2581131struct kmk_cc_expandprog *kmk_cc_compile_variable_for_expand(struct variable *pVar)
    2591132{
    260     assert(!pVar->evalprog);
    261 
    262     //memchr()
    263 
    264 
    265 
    266 
    267     return NULL;
     1133    assert(!pVar->expandprog);
     1134    if (   !pVar->expandprog
     1135        && pVar->value_length > 0
     1136        && pVar->recursive)
     1137    {
     1138        assert(strlen(pVar->value) == pVar->value_length);
     1139#if 0 /** @todo test & debug this code. Write interpreters! */
     1140        pVar->expandprog = kmk_cc_exp_compile(pVar->value, pVar->value_length);
     1141#endif
     1142    }
     1143    return pVar->expandprog;
    2681144}
    2691145
Note: See TracChangeset for help on using the changeset viewer.

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