- Timestamp:
- Sep 20, 2015 7:13:24 PM (9 years ago)
- Location:
- trunk/src/kmk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/kmk/Makefile.kmk
r2799 r2801 209 209 kmk_DEFS += CONFIG_WITH_EVAL_COMPILER 210 210 endif 211 ifdef CONFIG_WITH_COMPILE R211 ifdef CONFIG_WITH_COMPILE_EVERYTHING 212 212 kmk_DEFS += CONFIG_WITH_COMPILE_EVERYTHING 213 213 endif -
trunk/src/kmk/kmk_cc_exec.c
r2799 r2801 121 121 122 122 123 /** @def KMK_CC_IS_SPACE_CH124 * Checks if it's a space char. */125 #define KMK_CC_IS_SPACE_CH(a_ch) isspace((unsigned char)(a_ch))126 127 123 /** Aligns a size for the block allocator. */ 128 124 #define KMK_CC_BLOCK_ALIGN_SIZE(a_cb) ( ((a_cb) + (sizeof(void *) - 1U)) & ~(uint32_t)(sizeof(void *) - 1U) ) 125 126 /** How to declare a no-return function. 127 * Place between scope (if any) and return type. */ 128 #ifdef _MSC_VER 129 # define KMK_CC_FN_NO_RETURN declspec(noreturn) 130 #elif defined(__GNUC__) 131 # define KMK_CC_FN_NO_RETURN __attribute__((__noreturn__)) 132 #endif 129 133 130 134 … … 177 181 * All space characters, backslash (EOL escape), variable expansion dollar, 178 182 * variable assignment operator chars, recipe colon and recipe percent. */ 179 #define KMK_CC_EVAL_CH_SPACE_OR_VAR_OR_RECIPE UINT16_C(8) 180 #define KMK_CC_EVAL_IS_SPACE_OR_VAR_OR_RECIPE(a_ch) (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_SPACE_OR_VAR_OR_RECIPE) 183 #define KMK_CC_EVAL_CH_SPACE_VAR_OR_RECIPE UINT16_C(8) 184 #define KMK_CC_EVAL_IS_SPACE_VAR_OR_RECIPE(a_ch) (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_SPACE_VAR_OR_RECIPE) 185 /** Dollar character (possible variable expansion). */ 186 #define KMK_CC_EVAL_CH_DOLLAR UINT16_C(16) 187 #define KMK_CC_EVAL_IS_DOLLAR(a_ch) (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_DOLLAR) 188 /** Dollar character (possible variable expansion). */ 189 #define KMK_CC_EVAL_CH_BACKSLASH UINT16_C(32) 190 #define KMK_CC_EVAL_IS_BACKSLASH(a_ch) (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_BACKSLASH) 181 191 /** Possible EOL character. */ 182 #define KMK_CC_EVAL_CH_EOL_CANDIDATE UINT16_C( 16)192 #define KMK_CC_EVAL_CH_EOL_CANDIDATE UINT16_C(64) 183 193 #define KMK_CC_EVAL_IS_EOL_CANDIDATE(a_ch) (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_EOL_CANDIDATE) 184 194 /** First character in a keyword. */ 185 #define KMK_CC_EVAL_CH_1ST_IN_KEYWORD UINT16_C( 32)195 #define KMK_CC_EVAL_CH_1ST_IN_KEYWORD UINT16_C(128) 186 196 #define KMK_CC_EVAL_IS_1ST_IN_KEYWORD(a_ch) (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_1ST_IN_KEYWORD) 187 197 /** Second character in a keyword. */ 188 #define KMK_CC_EVAL_CH_2ND_IN_KEYWORD UINT16_C( 64)198 #define KMK_CC_EVAL_CH_2ND_IN_KEYWORD UINT16_C(256) 189 199 #define KMK_CC_EVAL_IS_2ND_IN_KEYWORD(a_ch) (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_2ND_IN_KEYWORD) 190 200 /** First character in a variable qualifier keyword or 'define'. */ 191 #define KMK_CC_EVAL_CH_1ST_IN_VARIABLE_KEYWORD UINT16_C( 128)201 #define KMK_CC_EVAL_CH_1ST_IN_VARIABLE_KEYWORD UINT16_C(512) 192 202 #define KMK_CC_EVAL_IS_1ST_IN_VARIABLE_KEYWORD(a_ch) (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_1ST_IN_VARIABLE_KEYWORD) 193 203 /** Used when parsing variable names, looking for the end of a nested 194 204 * variable reference. Matches parentheses and backslash (escaped eol). */ 195 #define KMK_CC_EVAL_CH_PAREN_OR_SLASH UINT16_C( 256)205 #define KMK_CC_EVAL_CH_PAREN_OR_SLASH UINT16_C(1024) 196 206 #define KMK_CC_EVAL_IS_PAREN_OR_SLASH(a_ch) (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_PAREN_OR_SLASH) 197 207 /** Used when parsing ifeq/ifneq (,) sequences. 198 208 * Matches parentheses, comma and dollar (for non-plain string detection). */ 199 #define KMK_CC_EVAL_CH_PAREN_COMMA_OR_DOLLAR UINT16_C( 512)209 #define KMK_CC_EVAL_CH_PAREN_COMMA_OR_DOLLAR UINT16_C(2048) 200 210 #define KMK_CC_EVAL_IS_PAREN_COMMA_OR_DOLLAR(a_ch) (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_PAREN_COMMA_OR_DOLLAR) 211 212 /** Test of space or dollar characters. */ 213 #define KMK_CC_EVAL_IS_SPACE_OR_DOLLAR(a_ch) (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & (KMK_CC_EVAL_CH_SPACE | KMK_CC_EVAL_CH_DOLLAR)) 214 /** Test of space, dollar or backslash (possible EOL escape) characters. */ 215 #define KMK_CC_EVAL_IS_SPACE_DOLLAR_OR_SLASH(a_ch) (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & (KMK_CC_EVAL_CH_SPACE | KMK_CC_EVAL_CH_DOLLAR | KMK_CC_EVAL_CH_BACKSLASH)) 216 /** Test of space, dollar, backslash (possible EOL escape) or variable 217 * assingment characters. */ 218 #define KMK_CC_EVAL_IS_SPACE_DOLLAR_SLASH_OR_ASSIGN(a_ch) \ 219 (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & (KMK_CC_EVAL_CH_SPACE | KMK_CC_EVAL_CH_SPACE_VAR_OR_RECIPE | KMK_CC_EVAL_CH_DOLLAR)) 201 220 /** @} */ 202 221 … … 282 301 { 283 302 /** The instruction opcode number (KMKCCEXPINSTR). */ 284 KMKCCEXPINSTR enmOp Code;303 KMKCCEXPINSTR enmOpcode; 285 304 } KMKCCEXPCORE; 286 305 typedef KMKCCEXPCORE *PKMKCCEXPCORE; … … 608 627 { 609 628 /** The instruction opcode number (KMKCCEVALINSTR). */ 610 KMKCCEVALINSTR enmOp Code;629 KMKCCEVALINSTR enmOpcode; 611 630 /** The line number in the source this statement is associated with. */ 612 631 unsigned iLine; … … 636 655 /** The core instruction. */ 637 656 KMKCCEVALCORE Core; 638 /** Whether the 'export' directivewas used. */657 /** Whether the 'export' qualifier was used. */ 639 658 uint8_t fExport; 640 /** Whether the 'override' directivewas used. */659 /** Whether the 'override' qualifier was used. */ 641 660 uint8_t fOverride; 642 /** Whether the 'local' directivewas used. */661 /** Whether the 'local' qualifier was used. */ 643 662 uint8_t fLocal; 663 /** Whether the 'private' qualifier was used. */ 664 uint8_t fPrivate; 644 665 /** The variable name. 645 666 * @remarks Plain text names are in variable_strcache. */ … … 1005 1026 1006 1027 1028 /** This is parallel to KMKCCEVALINSTR. */ 1029 static const char * const g_apszEvalInstrNms[] = 1030 { 1031 "jump", 1032 "assign_recursive", 1033 "assign_simple", 1034 "assign_append", 1035 "assign_prepend", 1036 "assign_if_new", 1037 "assign_define", 1038 "export", 1039 "unexport", 1040 "export_all", 1041 "unexport_all", 1042 "ifdef_plain", 1043 "ifndef_plain", 1044 "ifdef_dynamic", 1045 "ifndef_dynamic", 1046 "ifeq", 1047 "ifneq", 1048 "if1of", 1049 "ifn1of", 1050 "if", 1051 "include", 1052 "include_silent", 1053 "includedep", 1054 "includedep_queue", 1055 "includedep_flush", 1056 "recipe_no_commands", 1057 "recipe_start_normal", 1058 "recipe_start_double_colon", 1059 "recipe_start_pattern", 1060 "recipe_commands", 1061 "recipe_end", 1062 "recipe_cancel_pattern", 1063 "vpath", 1064 "vpath_clear_pattern", 1065 "vpath_clear_all", 1066 }; 1067 1007 1068 /********************************************************************************************************************************* 1008 1069 * Internal Functions * … … 1029 1090 1030 1091 /* space chars and zero terminator. */ 1031 #define MY_SPACE_BITS KMK_CC_EVAL_CH_SPACE | KMK_CC_EVAL_CH_SPACE_OR_BACKSLASH | KMK_CC_EVAL_CH_SPACE_ OR_VAR_OR_RECIPE1092 #define MY_SPACE_BITS KMK_CC_EVAL_CH_SPACE | KMK_CC_EVAL_CH_SPACE_OR_BACKSLASH | KMK_CC_EVAL_CH_SPACE_VAR_OR_RECIPE 1032 1093 KMK_CC_EVAL_BM_OR(g_abEvalCcChars, ' ', MY_SPACE_BITS); 1033 1094 KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\t', MY_SPACE_BITS); … … 1036 1097 KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\f', MY_SPACE_BITS); 1037 1098 KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\r', MY_SPACE_BITS | KMK_CC_EVAL_CH_EOL_CANDIDATE); 1038 KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\\', KMK_CC_EVAL_CH_SPACE_OR_BACKSLASH | KMK_CC_EVAL_CH_SPACE_ OR_VAR_OR_RECIPE);1099 KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\\', KMK_CC_EVAL_CH_SPACE_OR_BACKSLASH | KMK_CC_EVAL_CH_SPACE_VAR_OR_RECIPE); 1039 1100 #undef MY_SPACE_BITS 1040 1101 … … 1057 1118 1058 1119 /* Assignment punctuation and recipe stuff. */ 1059 KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '=', KMK_CC_EVAL_CH_SPACE_OR_VAR_OR_RECIPE); 1060 KMK_CC_EVAL_BM_OR(g_abEvalCcChars, ':', KMK_CC_EVAL_CH_SPACE_OR_VAR_OR_RECIPE); 1061 KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '<', KMK_CC_EVAL_CH_SPACE_OR_VAR_OR_RECIPE); 1062 KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '?', KMK_CC_EVAL_CH_SPACE_OR_VAR_OR_RECIPE); 1063 KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '+', KMK_CC_EVAL_CH_SPACE_OR_VAR_OR_RECIPE); 1064 KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '%', KMK_CC_EVAL_CH_SPACE_OR_VAR_OR_RECIPE); /* uncertain... */ 1120 KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '=', KMK_CC_EVAL_CH_SPACE_VAR_OR_RECIPE); 1121 KMK_CC_EVAL_BM_OR(g_abEvalCcChars, ':', KMK_CC_EVAL_CH_SPACE_VAR_OR_RECIPE); 1065 1122 1066 1123 /* For locating the end of variable expansion. */ … … 1076 1133 KMK_CC_EVAL_BM_OR(g_abEvalCcChars, ',', KMK_CC_EVAL_CH_PAREN_COMMA_OR_DOLLAR); 1077 1134 KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '$', KMK_CC_EVAL_CH_PAREN_COMMA_OR_DOLLAR); 1135 1136 /* Misc. */ 1137 KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '$', KMK_CC_EVAL_CH_DOLLAR); 1138 KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\\', KMK_CC_EVAL_CH_BACKSLASH); 1139 1140 /* 1141 * Check that the eval instruction names match up. 1142 */ 1143 KMK_CC_ASSERT(strcmp(g_apszEvalInstrNms[kKmkCcEvalInstr_ifneq], "ifneq") == 0); 1144 KMK_CC_ASSERT(strcmp(g_apszEvalInstrNms[kKmkCcEvalInstr_vpath_clear_all], "vpath_clear_all") == 0); 1078 1145 } 1079 1146 … … 1438 1505 /* Emit jump. */ 1439 1506 pJump = (PKMKCCEXPJUMP)((char *)pOldBlock + pOldBlock->offNext); 1440 pJump->Core.enmOp Code = kKmkCcExpInstr_Jump;1507 pJump->Core.enmOpcode = kKmkCcExpInstr_Jump; 1441 1508 pJump->pNext = pRet; 1442 1509 pOldBlock->offNext += sizeof(*pJump); … … 1508 1575 /* Emit jump. */ 1509 1576 pJump = (PKMKCCEVALJUMP)((char *)pOldBlock + pOldBlock->offNext); 1510 pJump->Core.enmOp Code = kKmkCcEvalInstr_jump;1577 pJump->Core.enmOpcode = kKmkCcEvalInstr_jump; 1511 1578 pJump->pNext = pRet; 1512 1579 pOldBlock->offNext += sizeof(*pJump); … … 1575 1642 { 1576 1643 PKMKCCEXPCORE pCore = kmk_cc_block_alloc_exp(ppBlockTail, sizeof(*pCore)); 1577 pCore->enmOp Code = kKmkCcExpInstr_Return;1644 pCore->enmOpcode = kKmkCcExpInstr_Return; 1578 1645 kmk_cc_block_realign(ppBlockTail); 1579 1646 } … … 1649 1716 uint32_t cActualArgs = cArgs <= cMaxArgs || !cMaxArgs ? cArgs : cMaxArgs; 1650 1717 PKMKCCEXPDYNFUNC pInstr = (PKMKCCEXPDYNFUNC)kmk_cc_block_alloc_exp(ppBlockTail, KMKCCEXPDYNFUNC_SIZE(cActualArgs)); 1651 pInstr->FnCore.Core.enmOp Code = kKmkCcExpInstr_DynamicFunction;1718 pInstr->FnCore.Core.enmOpcode = kKmkCcExpInstr_DynamicFunction; 1652 1719 pInstr->FnCore.cArgs = cActualArgs; 1653 1720 pInstr->FnCore.pfnFunction = pfnFunction; … … 1750 1817 uint32_t cActualArgs = cArgs <= cMaxArgs || !cMaxArgs ? cArgs : cMaxArgs; 1751 1818 PKMKCCEXPPLAINFUNC pInstr = (PKMKCCEXPPLAINFUNC)kmk_cc_block_alloc_exp(ppBlockTail, KMKCCEXPPLAINFUNC_SIZE(cActualArgs)); 1752 pInstr->FnCore.Core.enmOp Code = kKmkCcExpInstr_PlainFunction;1819 pInstr->FnCore.Core.enmOpcode = kKmkCcExpInstr_PlainFunction; 1753 1820 pInstr->FnCore.cArgs = cActualArgs; 1754 1821 pInstr->FnCore.pfnFunction = pfnFunction; … … 1821 1888 1822 1889 pInstr = (PKMKCCEXPDYNVAR)kmk_cc_block_alloc_exp(ppBlockTail, sizeof(*pInstr)); 1823 pInstr->Core.enmOp Code = kKmkCcExpInstr_DynamicVariable;1890 pInstr->Core.enmOpcode = kKmkCcExpInstr_DynamicVariable; 1824 1891 1825 1892 rc = kmk_cc_exp_compile_subprog(ppBlockTail, pchNameExpr, cchNameExpr, &pInstr->Subprog); … … 1855 1922 { 1856 1923 PKMKCCEXPPLAINVAR pInstr = (PKMKCCEXPPLAINVAR)kmk_cc_block_alloc_exp(ppBlockTail, sizeof(*pInstr)); 1857 pInstr->Core.enmOp Code = kKmkCcExpInstr_PlainVariable;1924 pInstr->Core.enmOpcode = kKmkCcExpInstr_PlainVariable; 1858 1925 pInstr->pszName = strcache2_add(&variable_strcache, pchName, cchName); 1859 1926 } … … 1872 1939 1873 1940 pInstr = (PKMKCCEXPSRPLAINVAR)kmk_cc_block_alloc_exp(ppBlockTail, sizeof(*pInstr)); 1874 pInstr->Core.enmOp Code = kKmkCcExpInstr_SearchAndReplacePlainVariable;1941 pInstr->Core.enmOpcode = kKmkCcExpInstr_SearchAndReplacePlainVariable; 1875 1942 pInstr->pszName = strcache2_add(&variable_strcache, pchName, cchName2); 1876 1943 … … 1939 2006 { 1940 2007 PKMKCCEXPCOPYSTRING pInstr = (PKMKCCEXPCOPYSTRING)kmk_cc_block_alloc_exp(ppBlockTail, sizeof(*pInstr)); 1941 pInstr->Core.enmOp Code = kKmkCcExpInstr_CopyString;2008 pInstr->Core.enmOpcode = kKmkCcExpInstr_CopyString; 1942 2009 pInstr->cchCopy = cchStr; 1943 2010 pInstr->pachSrc = pchStr; … … 2402 2469 for (;;) 2403 2470 { 2404 switch (pInstrCore->enmOp Code)2471 switch (pInstrCore->enmOpcode) 2405 2472 { 2406 2473 case kKmkCcExpInstr_CopyString: … … 2649 2716 default: 2650 2717 fatal(NULL, _("Unknown string expansion opcode: %d (%#x)"), 2651 (int)pInstrCore->enmOp Code, (int)pInstrCore->enmOpCode);2718 (int)pInstrCore->enmOpcode, (int)pInstrCore->enmOpcode); 2652 2719 return NULL; 2653 2720 } … … 2906 2973 { 2907 2974 /** The token word (lexeme). */ 2908 const char *p szWord;2975 const char *pchWord; 2909 2976 /** The length of the word (lexeme). */ 2910 2977 uint32_t cchWord; … … 2913 2980 } KMKCCEVALWORD; 2914 2981 typedef KMKCCEVALWORD *PKMKCCEVALWORD; 2982 typedef KMKCCEVALWORD const *PCKMKCCEVALWORD; 2915 2983 2916 2984 … … 3066 3134 } 3067 3135 3068 static void kmk_cc_eval_fatal(PKMKCCEVALCOMPILER pCompiler, const char *pchWhere, const char *pszMsg, ...)3136 static void KMK_CC_FN_NO_RETURN kmk_cc_eval_fatal(PKMKCCEVALCOMPILER pCompiler, const char *pchWhere, const char *pszMsg, ...) 3069 3137 { 3070 3138 va_list va; … … 3117 3185 3118 3186 3119 static void kmk_cc_eval_fatal_eol(PKMKCCEVALCOMPILER pCompiler, const char *pchEol, unsigned iLine, size_t offLine) 3187 static KMK_CC_FN_NO_RETURN void 3188 kmk_cc_eval_fatal_eol(PKMKCCEVALCOMPILER pCompiler, const char *pchEol, unsigned iLine, size_t offLine) 3120 3189 { 3121 3190 pCompiler->iLine = iLine; … … 3125 3194 kmk_cc_eval_fatal(pCompiler, pchEol, "Missing 2nd EOL character: found %#x instead of %#x\n", 3126 3195 pchEol, pCompiler->chSecondEol); 3196 } 3197 3198 3199 static void kmk_cc_eval_warn(PKMKCCEVALCOMPILER pCompiler, const char *pchWhere, const char *pszMsg, ...) 3200 { 3201 /** @todo warnings. */ 3202 (void)pchWhere; 3203 (void)pCompiler; 3204 (void)pszMsg; 3127 3205 } 3128 3206 … … 3155 3233 * @param fPlain Whether it's plain or not. If not, we'll compile it. 3156 3234 */ 3157 static void kmk_cc_eval_ do_subprogram_or_plain(PKMKCCEVALCOMPILER pCompiler, PKMKCCEXPSUBPROGORPLAIN pOperand,3158 const char *pszString, size_t cchString, int fPlain)3235 static void kmk_cc_eval_init_subprogram_or_plain(PKMKCCEVALCOMPILER pCompiler, PKMKCCEXPSUBPROGORPLAIN pOperand, 3236 const char *pszString, size_t cchString, int fPlain) 3159 3237 { 3160 3238 pOperand->fPlainIsInVarStrCache = 0; … … 3169 3247 else 3170 3248 kmk_cc_eval_compile_string_exp_subprog(pCompiler, pszString, cchString, &pOperand->u.Subprog); 3249 } 3250 3251 /** 3252 * Initializes an array of subprogram-or-plain (spp) operands from a word array. 3253 * 3254 * The words will be duplicated and the caller must therefore call 3255 * kmk_cc_block_realign() when done (it's not done here as the caller may 3256 * initialize several string operands and we don't want any unnecessary 3257 * fragmentation). 3258 * 3259 * @param pCompiler The compiler state. 3260 * @param cWords The number of words to copy. 3261 * @param paSrc The source words. 3262 * @param paDst The destination subprogram-or-plain array. 3263 */ 3264 static void kmk_cc_eval_init_spp_array_from_duplicated_words(PKMKCCEVALCOMPILER pCompiler, unsigned cWords, 3265 PKMKCCEVALWORD paSrc, PKMKCCEXPSUBPROGORPLAIN paDst) 3266 { 3267 unsigned i; 3268 for (i = 0; i < cWords; i++) 3269 { 3270 const char *pszCopy = kmk_cc_block_strdup(pCompiler->ppBlockTail, paSrc[i].pchWord, paSrc[i].cchWord); 3271 paDst[i].fPlainIsInVarStrCache = 0; 3272 paDst[i].bUser = 0; 3273 paDst[i].bUser2 = 0; 3274 if (paSrc[i].enmToken == kKmkCcEvalToken_WordWithDollar) 3275 { 3276 paDst[i].fSubprog = 1; 3277 kmk_cc_eval_compile_string_exp_subprog(pCompiler, pszCopy, paSrc[i].cchWord, &paDst[i].u.Subprog); 3278 } 3279 else 3280 { 3281 paDst[i].fSubprog = 0; 3282 paDst[i].u.Plain.cch = paSrc[i].cchWord; 3283 paDst[i].u.Plain.psz = pszCopy; 3284 } 3285 KMK_CC_EVAL_DPRINTF((" %s\n", pszCopy)); 3286 } 3171 3287 } 3172 3288 … … 3310 3426 || ((a_ch) == '\\' && (a_ch2) == (a_pCompiler)->chFirstEol) ) 3311 3427 3428 3429 /** 3430 * Common path for space skipping worker functions when escaped EOLs may be 3431 * involed. 3432 * 3433 * @returns Points to the first non-space character or end of input. 3434 * @param pchWord The current position. There is some kind of char 3435 * @param cchLeft The current number of chars left to parse in the 3436 * current line. 3437 * @param pcchLeft Where to store the updated @a cchLeft value. 3438 * @param pCompiler The compiler instance data. 3439 */ 3440 static const char *kmk_cc_eval_skip_spaces_with_esc_eol(const char *pchWord, size_t cchLeft, size_t *pcchLeft, 3441 PKMKCCEVALCOMPILER pCompiler) 3442 { 3443 /* 3444 * Skip further spaces. We unrolls 4 loops here. 3445 * ASSUMES cchEscEolSeq is either 2 or 3! 3446 */ 3447 KMK_CC_ASSERT(pCompiler->cchEscEolSeq == 2 || pCompiler->cchEscEolSeq == 3); 3448 KMK_CC_ASSERT(pCompiler->iEscEol < pCompiler->cEscEols); 3449 while (cchLeft >= 4) 3450 { 3451 /* First char. */ 3452 char ch = pchWord[0]; 3453 if (KMK_CC_EVAL_IS_SPACE(ch)) 3454 { /* maybe likely */ } 3455 else if ( ch == '\\' 3456 && pchWord[1] == pCompiler->chFirstEol) 3457 { 3458 pchWord += pCompiler->cchEscEolSeq; 3459 cchLeft -= pCompiler->cchEscEolSeq; 3460 pCompiler->iEscEol++; 3461 continue; 3462 } 3463 else 3464 { 3465 *pcchLeft = cchLeft; 3466 return pchWord; 3467 } 3468 3469 /* Second char. */ 3470 ch = pchWord[1]; 3471 if (KMK_CC_EVAL_IS_SPACE(ch)) 3472 { /* maybe likely */ } 3473 else if ( ch == '\\' 3474 && pchWord[2] == pCompiler->chFirstEol) 3475 { 3476 pchWord += 1 + pCompiler->cchEscEolSeq; 3477 cchLeft -= 1 + pCompiler->cchEscEolSeq; 3478 pCompiler->iEscEol++; 3479 continue; 3480 } 3481 else 3482 { 3483 *pcchLeft = cchLeft - 1; 3484 return pchWord + 1; 3485 } 3486 3487 /* Third char. */ 3488 ch = pchWord[2]; 3489 if (KMK_CC_EVAL_IS_SPACE(ch)) 3490 { /* maybe likely */ } 3491 else if ( ch == '\\' 3492 && pchWord[3] == pCompiler->chFirstEol 3493 && cchLeft >= 2 + pCompiler->cchEscEolSeq) 3494 { 3495 pchWord += 2 + pCompiler->cchEscEolSeq; 3496 cchLeft -= 2 + pCompiler->cchEscEolSeq; 3497 pCompiler->iEscEol++; 3498 continue; 3499 } 3500 else 3501 { 3502 *pcchLeft = cchLeft - 2; 3503 return pchWord + 2; 3504 } 3505 3506 /* Third char. */ 3507 ch = pchWord[3]; 3508 if (KMK_CC_EVAL_IS_SPACE(ch)) 3509 { 3510 pchWord += 4; 3511 cchLeft -= 4; 3512 } 3513 else if ( ch == '\\' 3514 && cchLeft >= 3 + pCompiler->cchEscEolSeq 3515 && pchWord[4] == pCompiler->chFirstEol) 3516 { 3517 pchWord += 3 + pCompiler->cchEscEolSeq; 3518 cchLeft -= 3 + pCompiler->cchEscEolSeq; 3519 pCompiler->iEscEol++; 3520 } 3521 else 3522 { 3523 *pcchLeft = cchLeft - 3; 3524 return pchWord + 3; 3525 } 3526 } 3527 3528 /* 3529 * Simple loop for the final three chars. 3530 */ 3531 while (cchLeft > 0) 3532 { 3533 /* First char. */ 3534 char ch = *pchWord; 3535 if (KMK_CC_EVAL_IS_SPACE(ch)) 3536 { 3537 pchWord += 1; 3538 cchLeft -= 1; 3539 } 3540 else if ( ch == '\\' 3541 && cchLeft > pCompiler->cchEolSeq 3542 && pchWord[1] == pCompiler->chFirstEol) 3543 { 3544 pchWord += pCompiler->cchEscEolSeq; 3545 cchLeft -= pCompiler->cchEscEolSeq; 3546 pCompiler->iEscEol++; 3547 } 3548 else 3549 break; 3550 } 3551 3552 *pcchLeft = cchLeft; 3553 return pchWord; 3554 } 3555 3556 3557 /** 3558 * Common path for space skipping worker functions when no escaped EOLs need 3559 * considering. 3560 * 3561 * @returns Points to the first non-space character or end of input. 3562 * @param pchWord The current position. There is some kind of char 3563 * @param cchLeft The current number of chars left to parse in the 3564 * current line. 3565 * @param pcchLeft Where to store the updated @a cchLeft value. 3566 * @param pCompiler The compiler instance data. 3567 */ 3568 static const char *kmk_cc_eval_skip_spaces_without_esc_eol(const char *pchWord, size_t cchLeft, size_t *pcchLeft, 3569 PKMKCCEVALCOMPILER pCompiler) 3570 { 3571 /* 3572 * 4x loop unroll. 3573 */ 3574 while (cchLeft >= 4) 3575 { 3576 if (KMK_CC_EVAL_IS_SPACE(pchWord[0])) 3577 { 3578 if (KMK_CC_EVAL_IS_SPACE(pchWord[1])) 3579 { 3580 if (KMK_CC_EVAL_IS_SPACE(pchWord[2])) 3581 { 3582 if (KMK_CC_EVAL_IS_SPACE(pchWord[3])) 3583 { 3584 pchWord += 4; 3585 cchLeft -= 4; 3586 } 3587 else 3588 { 3589 *pcchLeft = cchLeft - 3; 3590 return pchWord + 3; 3591 } 3592 } 3593 else 3594 { 3595 *pcchLeft = cchLeft - 2; 3596 return pchWord + 2; 3597 } 3598 } 3599 else 3600 { 3601 *pcchLeft = cchLeft - 1; 3602 return pchWord + 1; 3603 } 3604 } 3605 else 3606 { 3607 *pcchLeft = cchLeft; 3608 return pchWord; 3609 } 3610 } 3611 3612 /* 3613 * The last 3. Not entirely sure if this yield good code. 3614 */ 3615 switch (cchLeft & 3) 3616 { 3617 case 3: 3618 if (!KMK_CC_EVAL_IS_SPACE(*pchWord)) 3619 break; 3620 pchWord++; 3621 cchLeft--; 3622 case 2: 3623 if (!KMK_CC_EVAL_IS_SPACE(*pchWord)) 3624 break; 3625 pchWord++; 3626 cchLeft--; 3627 case 1: 3628 if (!KMK_CC_EVAL_IS_SPACE(*pchWord)) 3629 break; 3630 pchWord++; 3631 cchLeft--; 3632 case 0: 3633 break; 3634 } 3635 3636 *pcchLeft = cchLeft; 3637 return pchWord; 3638 } 3639 3640 3312 3641 /** 3313 3642 * Used to skip spaces after a word. … … 3349 3678 } while (0) 3350 3679 3351 3352 //static int kmk_cc_eval_skip_esc_eol_slow(const char *pchWord, size_t cchLeft, PKMKCCEVALCOMPILER pCompiler,3353 // const char **ppchWord, size_t *pcchLeft)3354 //{3355 // KMK_CC_ASSERT(pchWord[0] == '\\');3356 // if (pCompiler->cEscEols)3357 // {3358 // size_t cchMin = 1 + pCompiler->chFirstEol;3359 // if (cchLeft >= cchMin)3360 // {3361 // /* The simple case, no extra backslashes. */3362 // if (pchWord[1] == pCompiler->chFirstEol)3363 // {3364 // *pcchLeft = cchLeft - cchMin;3365 // *pchWord = pchWord + cchMin;3366 // return 1;3367 // }3368 //3369 // /* The unlikly case, */3370 //3371 // }3372 // }3373 // return 0;3374 //}3375 3376 3377 3680 /** 3378 3681 * The slow path of KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD. … … 3394 3697 */ 3395 3698 static const char *kmk_cc_eval_skip_spaces_after_word_slow(const char *pchWord, size_t *pcchLeft, char ch, 3396 3699 PKMKCCEVALCOMPILER pCompiler) 3397 3700 { 3398 3701 size_t cchLeft = *pcchLeft; 3702 3703 /* 3704 * It's all very simple when we don't have to consider escaped EOLs. 3705 */ 3706 if (pCompiler->iEscEol >= pCompiler->cEscEols) 3707 { 3708 if (ch != '\\') 3709 { 3710 pchWord += 1; 3711 cchLeft -= 1; 3712 } 3713 else 3714 return pchWord; 3715 return kmk_cc_eval_skip_spaces_without_esc_eol(pchWord, cchLeft, pcchLeft, pCompiler); 3716 } 3399 3717 3400 3718 /* … … 3420 3738 cchLeft -= pCompiler->cchEscEolSeq; 3421 3739 pCompiler->iEscEol++; 3740 3741 if (pCompiler->iEscEol < pCompiler->cEscEols) 3742 { /* likely */ } 3743 else return kmk_cc_eval_skip_spaces_without_esc_eol(pchWord, cchLeft, pcchLeft, pCompiler); 3422 3744 } 3423 3745 else 3424 3746 return pchWord; 3425 3426 /* 3427 * Skip further spaces. We unrolls 4 loops here. 3428 * ASSUMES cchEscEolSeq is either 2 or 3! 3429 */ 3430 KMK_CC_ASSERT(pCompiler->cchEscEolSeq == 2 || pCompiler->cchEscEolSeq == 3); 3431 while (cchLeft >= 4) 3432 { 3433 /* First char. */ 3434 ch = pchWord[0]; 3435 if (KMK_CC_EVAL_IS_SPACE(ch)) 3436 { /* maybe likely */ } 3437 else if ( ch == '\\' 3438 && pchWord[1] == pCompiler->chFirstEol) 3439 { 3440 pchWord += pCompiler->cchEscEolSeq; 3441 cchLeft -= pCompiler->cchEscEolSeq; 3442 pCompiler->iEscEol++; 3443 continue; 3444 } 3445 else 3446 { 3447 *pcchLeft = cchLeft; 3448 return pchWord; 3449 } 3450 3451 /* Second char. */ 3452 ch = pchWord[1]; 3453 if (KMK_CC_EVAL_IS_SPACE(ch)) 3454 { /* maybe likely */ } 3455 else if ( ch == '\\' 3456 && pchWord[2] == pCompiler->chFirstEol) 3457 { 3458 pchWord += 1 + pCompiler->cchEscEolSeq; 3459 cchLeft -= 1 + pCompiler->cchEscEolSeq; 3460 pCompiler->iEscEol++; 3461 continue; 3462 } 3463 else 3464 { 3465 *pcchLeft = cchLeft - 1; 3466 return pchWord + 1; 3467 } 3468 3469 /* Third char. */ 3470 ch = pchWord[2]; 3471 if (KMK_CC_EVAL_IS_SPACE(ch)) 3472 { /* maybe likely */ } 3473 else if ( ch == '\\' 3474 && pchWord[3] == pCompiler->chFirstEol 3475 && cchLeft >= 2 + pCompiler->cchEscEolSeq) 3476 { 3477 pchWord += 2 + pCompiler->cchEscEolSeq; 3478 cchLeft -= 2 + pCompiler->cchEscEolSeq; 3479 pCompiler->iEscEol++; 3480 continue; 3481 } 3482 else 3483 { 3484 *pcchLeft = cchLeft - 2; 3485 return pchWord + 2; 3486 } 3487 3488 /* Third char. */ 3489 ch = pchWord[3]; 3490 if (KMK_CC_EVAL_IS_SPACE(ch)) 3491 { 3492 pchWord += 4; 3493 cchLeft -= 4; 3494 } 3495 else if ( ch == '\\' 3496 && cchLeft >= 3 + pCompiler->cchEscEolSeq 3497 && pchWord[4] == pCompiler->chFirstEol) 3498 { 3499 pchWord += 3 + pCompiler->cchEscEolSeq; 3500 cchLeft -= 3 + pCompiler->cchEscEolSeq; 3501 pCompiler->iEscEol++; 3502 } 3503 else 3504 { 3505 *pcchLeft = cchLeft - 3; 3506 return pchWord + 3; 3507 } 3508 } 3509 3510 /* 3511 * Simple loop for the final three chars. 3512 */ 3513 while (cchLeft > 0) 3514 { 3515 /* First char. */ 3516 ch = *pchWord; 3517 if (KMK_CC_EVAL_IS_SPACE(ch)) 3518 { 3519 pchWord += 1; 3520 cchLeft -= 1; 3521 } 3522 else if ( ch == '\\' 3523 && cchLeft > pCompiler->cchEolSeq 3524 && pchWord[1] == pCompiler->chFirstEol) 3525 { 3526 pchWord += pCompiler->cchEscEolSeq; 3527 cchLeft -= pCompiler->cchEscEolSeq; 3528 pCompiler->iEscEol++; 3529 } 3530 else 3531 break; 3532 } 3533 3534 *pcchLeft = cchLeft; 3535 return pchWord; 3536 } 3537 3538 3539 #if 0 3540 /** 3541 * Used to shed trailing spaces in a string by decrementing @ a_cchLeft. 3542 * 3543 * @param a_pCompiler The compiler instance data. 3544 * @param a_pchWord The current input position, this will not be 3545 * changed. 3546 * @param a_cchLeft The number of chars left to parse. This will be 3547 * updated if there are trailing spaces. 3548 */ 3549 #define KMK_CC_EVAL_SHED_TRAILING_SPACES(a_pCompiler, a_pchWord, a_cchLeft) \ 3747 return kmk_cc_eval_skip_spaces_with_esc_eol(pchWord, cchLeft, pcchLeft, pCompiler); 3748 } 3749 3750 3751 /** 3752 * Skip zero or more spaces. 3753 * 3754 * This macro deals with a single space, if there are more or we're hittin some 3755 * possible escaped EOL sequence, work is deferred to a worker function. 3756 * 3757 * @param a_pCompiler The compiler state. 3758 * @param a_pchWord The current input position. Advanced past spaces. 3759 * @param a_cchLeft The amount of input left to parse. Will be updated. 3760 */ 3761 #define KMK_CC_EVAL_SKIP_SPACES(a_pCompiler, a_pchWord, a_cchLeft) \ 3550 3762 do { \ 3551 3763 if ((a_cchLeft) > 0) \ 3552 3764 { \ 3553 char c onst chShedTrailing1 = (a_pchWord)[(a_cchLeft) - 1]; \3554 if (KMK_CC_EVAL_IS_SPACE (chShedTrailing1)) \3765 char chSkipSpaces = *(a_pchWord); \ 3766 if (KMK_CC_EVAL_IS_SPACE_OR_BACKSLASH(chSkipSpaces)) \ 3555 3767 { \ 3556 /* If there are two or more, or any of these are potential EOL chars, call worker. */ \ 3557 if (!KMK_CC_EVAL_IS_EOL_CANDIDATE(chShedTrailing1)) \ 3768 if (chSkipSpaces != '\\') \ 3558 3769 { \ 3770 (a_pchWord) += 1; \ 3559 3771 (a_cchLeft) -= 1; \ 3560 if ((a_cchLeft) > 0) \ 3561 { \ 3562 char const chShedTrailing2 = (a_pchWord)[(a_cchLeft) - 1]; \ 3563 if (KMK_CC_EVAL_IS_SPACE(chShedTrailing)) \ 3564 (a_cchLeft) = kmk_cc_eval_shed_trailing_spaces_slow(a_pchWord, (a_cchLeft), \ 3565 chShedTrailing2, a_pCompiler); \ 3566 } \ 3772 chSkipSpaces = *(a_pchWord); \ 3773 if (KMK_CC_EVAL_IS_SPACE_OR_BACKSLASH(chSkipSpaces)) \ 3774 (a_pchWord) = kmk_cc_eval_skip_spaces_slow(a_pchWord, &(a_cchLeft), chSkipSpaces, a_pCompiler); \ 3567 3775 } \ 3568 3776 else \ 3569 (a_cchWord) = kmk_cc_eval_shed_trailing_spaces_slow(a_pchWord, (a_cchLeft), \ 3570 chShedTrailing1, a_pCompiler); \ 3777 (a_pchWord) = kmk_cc_eval_skip_spaces_slow(a_pchWord, &(a_cchLeft), chSkipSpaces, a_pCompiler); \ 3571 3778 } \ 3572 3779 } \ … … 3574 3781 3575 3782 3576 3577 /** 3578 * The slow path of KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD. 3579 * 3580 * It's called when a space or backslash is encountered as the 2nd character. 3581 * A second space is likely to be followed by more spaces, while an escaped 3582 * newline (if it checks out) is very likely to be followed by more spaces or 3583 * tabs before we get to anything worthwhile. Also, checking for an esacped 3584 * end-of-line character is a bit tedious. (It could be worse, though, as we 3585 * know that any EOL sequence inside the text area we're currently working on 3586 * must be escaped.) 3783 /** 3784 * Worker for KMK_CC_EVAL_SKIP_SPACES. 3587 3785 * 3588 3786 * @returns Points to the first non-space character or end of input. 3589 3787 * @param pchWord The current position. There is some kind of char 3590 * @param cchLeft The number of chars left to parse at @a pchWord. 3591 * @param ch The current tail character. 3788 * @param pcchLeft Pointer to the cchLeft variable, this is both 3789 * input and output. 3790 * @param ch The current character. 3592 3791 * @param pCompiler The compiler instance data. 3593 3792 */ 3594 static size_t kmk_cc_eval_shed_trailing_spaces_slow(const char *pchWord, size_t cchLeft, char ch, 3595 PKMKCCEVALCOMPILER pCompiler) 3596 { 3597 for (;;) 3598 { 3599 if (KMK_CC_EVAL_IS_EOL_CANDIDATE(chShedTrailing1)) 3600 { 3601 3793 static const char *kmk_cc_eval_skip_spaces_slow(const char *pchWord, size_t *pcchLeft, char ch, PKMKCCEVALCOMPILER pCompiler) 3794 { 3795 size_t cchLeft = *pcchLeft; 3796 #ifdef KMK_CC_STRICT 3797 size_t offWordCcStrict = pchWord - pCompiler->pszContent; 3798 #endif 3799 KMK_CC_ASSERT(cchLeft > 0); 3800 KMK_CC_ASSERT(cchLeft <= pCompiler->cchLine); 3801 KMK_CC_ASSERT(*pchWord == ch); 3802 KMK_CC_ASSERT(KMK_CC_EVAL_IS_SPACE_OR_BACKSLASH(ch)); 3803 KMK_CC_ASSERT(offWordCcStrict >= pCompiler->offLine); 3804 KMK_CC_ASSERT(offWordCcStrict < pCompiler->offLine + pCompiler->cchLine); 3805 KMK_CC_ASSERT( pCompiler->iEscEol >= pCompiler->cEscEols 3806 || offWordCcStrict <= pCompiler->paEscEols[pCompiler->iEscEol].offEsc); 3807 KMK_CC_ASSERT( pCompiler->iEscEol >= pCompiler->cEscEols 3808 || pCompiler->iEscEol == 0 3809 || offWordCcStrict >= pCompiler->paEscEols[pCompiler->iEscEol - 1].offEol + pCompiler->cchEolSeq); 3810 3811 /* 3812 * If we don't need to consider escaped EOLs, things are much much simpler. 3813 */ 3814 if (pCompiler->iEscEol >= pCompiler->cEscEols) 3815 { 3816 if (ch != '\\') 3817 { 3818 pchWord++; 3819 cchLeft--; 3602 3820 } 3603 3604 } 3605 } 3606 #endif 3821 else 3822 return pchWord; 3823 return kmk_cc_eval_skip_spaces_without_esc_eol(pchWord, cchLeft, pcchLeft, pCompiler); 3824 } 3825 3826 /* 3827 * Possible escaped EOL complications. 3828 */ 3829 if (ch != '\\') 3830 { 3831 pchWord++; 3832 cchLeft--; 3833 } 3834 else 3835 { 3836 size_t cchSkip; 3837 size_t offWord; 3838 unsigned iEscEol = pCompiler->iEscEol; 3839 if (iEscEol >= pCompiler->cEscEols) 3840 return pchWord; 3841 3842 offWord = pchWord - pCompiler->pszContent; 3843 if (offWord < pCompiler->paEscEols[iEscEol].offEsc) 3844 return pchWord; 3845 KMK_CC_ASSERT(offWord == pCompiler->paEscEols[iEscEol].offEsc); 3846 3847 cchSkip = pCompiler->paEscEols[iEscEol].offEol + pCompiler->cchEolSeq - offWord; 3848 pchWord += cchSkip; 3849 cchLeft -= cchSkip; 3850 pCompiler->iEscEol = ++iEscEol; 3851 3852 if (iEscEol < pCompiler->cEscEols) 3853 { /* likely */ } 3854 else return kmk_cc_eval_skip_spaces_without_esc_eol(pchWord, cchLeft, pcchLeft, pCompiler); 3855 } 3856 return kmk_cc_eval_skip_spaces_with_esc_eol(pchWord, cchLeft, pcchLeft, pCompiler); 3857 } 3607 3858 3608 3859 … … 3647 3898 { 3648 3899 char ch = pszContent[off]; 3649 if (!KMK_CC_EVAL_IS_SPACE_OR_VAR_OR_RECIPE(ch)) 3900 if (!KMK_CC_EVAL_IS_SPACE_DOLLAR_OR_SLASH(ch)) 3901 off++; 3902 else if (KMK_CC_EVAL_IS_SPACE(ch)) 3903 break; 3904 else if (ch == '$') 3905 { 3906 off++; 3907 if (off < offLineEnd) 3908 { 3909 char const chOpen = pszContent[off]; 3910 if (chOpen == '(' || chOpen == '{') 3911 { 3912 /* 3913 * Got a $(VAR) or ${VAR} to deal with here. This may 3914 * include nested variable references and span multiple 3915 * lines (at least for function calls). 3916 * 3917 * We scan forward till we've found the corresponding 3918 * closing parenthesis, considering any open parentheses 3919 * of the same kind as worth counting, even if there are 3920 * no dollar preceeding them, just like GNU make does. 3921 */ 3922 size_t const offStart = off - 1; 3923 char const chClose = chOpen == '(' ? ')' : '}'; 3924 unsigned cOpen = 1; 3925 off++; 3926 for (;;) 3927 { 3928 if (off < offLineEnd) 3929 { 3930 ch = pszContent[off]; 3931 if (!(KMK_CC_EVAL_IS_PAREN_OR_SLASH(ch))) 3932 off++; 3933 else 3934 { 3935 off++; 3936 if (ch == chClose) 3937 { 3938 if (--cOpen == 0) 3939 break; 3940 } 3941 else if (ch == chOpen) 3942 cOpen++; 3943 else if ( ch == '\\' 3944 && iEscEol < pCompiler->cEscEols 3945 && off == pCompiler->paEscEols[iEscEol].offEsc) 3946 { 3947 off = pCompiler->paEscEols[iEscEol].offEol + pCompiler->cchEolSeq; 3948 pCompiler->iEscEol = ++iEscEol; 3949 } 3950 } 3951 } 3952 else if (cOpen == 1) 3953 kmk_cc_eval_fatal(pCompiler, &pszContent[offStart], 3954 "Variable reference is missing '%c'", chClose); 3955 else 3956 kmk_cc_eval_fatal(pCompiler, &pszContent[offStart], 3957 "%u variable references are missing '%c'", cOpen, chClose); 3958 } 3959 } 3960 /* Single char variable name. */ 3961 else if (!KMK_CC_EVAL_IS_SPACE(chOpen)) 3962 { /* likely */ } 3963 else 3964 kmk_cc_eval_fatal(pCompiler, &pszContent[off], "Expected variable name after '$', not end of line"); 3965 } 3966 else 3967 kmk_cc_eval_fatal(pCompiler, &pszContent[off], "Expected variable name after '$', not end of line"); 3968 fPlain = 0; 3969 } 3970 /* Deal with potential escaped EOL. */ 3971 else if ( ch != '\\' 3972 || iEscEol >= pCompiler->cEscEols 3973 || off != pCompiler->paEscEols[iEscEol].offEsc ) 3650 3974 off++; 3651 3975 else 3652 { 3653 if (KMK_CC_EVAL_IS_SPACE(ch)) 3654 break; 3655 3656 if (ch == '$') 3657 { 3658 off++; 3659 if (off < offLineEnd) 3660 { 3661 char const chOpen = pszContent[off]; 3662 if (chOpen == '(' || chOpen == '{') 3663 { 3664 /* 3665 * Got a $(VAR) or ${VAR} to deal with here. This may 3666 * include nested variable references and span multiple 3667 * lines (at least for function calls). 3668 * 3669 * We scan forward till we've found the corresponding 3670 * closing parenthesis, considering any open parentheses 3671 * of the same kind as worth counting, even if there are 3672 * no dollar preceeding them, just like GNU make does. 3673 */ 3674 size_t const offStart = off - 1; 3675 char const chClose = chOpen == '(' ? ')' : '}'; 3676 unsigned cOpen = 1; 3677 for (;;) 3678 { 3679 if (off < offLineEnd) 3680 { 3681 ch = pszContent[off]; 3682 if (!(KMK_CC_EVAL_IS_PAREN_OR_SLASH(ch))) 3683 off++; 3684 else 3685 { 3686 off++; 3687 if (ch == chClose) 3688 { 3689 if (--cOpen == 0) 3690 break; 3691 } 3692 else if (ch == chOpen) 3693 cOpen++; 3694 else if ( ch == '\\' 3695 && iEscEol < pCompiler->cEscEols 3696 && off == pCompiler->paEscEols[iEscEol].offEsc) 3697 { 3698 off = pCompiler->paEscEols[iEscEol].offEsc + pCompiler->cchEolSeq; 3699 iEscEol++; 3700 pCompiler->iEscEol = iEscEol; 3701 } 3702 } 3703 } 3704 else if (cOpen == 1) 3705 kmk_cc_eval_fatal(pCompiler, &pszContent[offStart], 3706 "Variable reference is missing '%c'", chClose); 3707 else 3708 kmk_cc_eval_fatal(pCompiler, &pszContent[offStart], 3709 "%u variable references are missing '%c'", cOpen, chClose); 3710 } 3711 } 3712 /* Single char variable name. */ 3713 else if (!KMK_CC_EVAL_IS_SPACE(chOpen)) 3714 { /* likely */ } 3715 else 3716 kmk_cc_eval_fatal(pCompiler, &pszContent[off], "Expected variable name after '$', not end of line"); 3717 } 3718 else 3719 kmk_cc_eval_fatal(pCompiler, &pszContent[off], "Expected variable name after '$', not end of line"); 3720 fPlain = 0; 3721 } 3722 /* Deal with potential escaped EOL. */ 3723 else if ( ch != '\\' 3724 || iEscEol >= pCompiler->cEscEols 3725 || off != pCompiler->paEscEols[iEscEol].offEsc ) 3726 off++; 3727 else 3728 break; 3729 } 3976 break; 3730 3977 } 3731 3978 … … 3858 4105 } 3859 4106 #endif /* unused atm */ 4107 4108 4109 /** 4110 * Helper for ensuring that we've got sufficient number of words allocated. 4111 */ 4112 #define KMK_CC_EVAL_ENSURE_WORDS(a_pCompiler, a_cRequiredWords) \ 4113 do { \ 4114 if ((a_cRequiredWords) < (a_pCompiler)->cWordsAllocated) \ 4115 { /* likely */ } \ 4116 else \ 4117 { \ 4118 unsigned cEnsureWords = ((a_cRequiredWords) + 3 /*15*/) & ~(unsigned)3/*15*/; \ 4119 KMK_CC_ASSERT((a_cRequiredWords) < 0x8000); \ 4120 (a_pCompiler)->paWords = (PKMKCCEVALWORD)xmalloc(cEnsureWords * sizeof((a_pCompiler)->paWords)[0]); \ 4121 } \ 4122 } while (0) 4123 4124 /** 4125 * Parses the remainder of the line into simple words. 4126 * 4127 * The resulting words are classified as either kKmkCcEvalToken_WordPlain or 4128 * kKmkCcEvalToken_WordWithDollar. 4129 * 4130 * @returns Number of words. 4131 * @param pCompiler The compiler state. 4132 * @param pchWord Where to start, we expect this to be at a word. 4133 * @param cchLeft The number of chars left to parse on this line. 4134 * This is expected to be non-zero. 4135 */ 4136 static unsigned kmk_cc_eval_parse_words(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft) 4137 { 4138 unsigned iEscEol = pCompiler->iEscEol; 4139 unsigned cEscEols = pCompiler->cEscEols; 4140 unsigned cWords = 0; 4141 4142 /* Precoditions. */ 4143 KMK_CC_ASSERT(cchLeft > 0); 4144 KMK_CC_ASSERT(!KMK_CC_EVAL_IS_SPACE(*pchWord)); 4145 4146 /* 4147 * If we don't have to deal with escaped EOLs, the find-end-of word search 4148 * becomes a little bit simpler. Since this function will be used a lot 4149 * for simple lines with single words, this could maybe save a nano second 4150 * or two. 4151 */ 4152 if (iEscEol >= cEscEols) 4153 { 4154 do 4155 { 4156 size_t cchSkipAfter = 0; 4157 size_t cchWord = 1; 4158 KMKCCEVALTOKEN enmToken = kKmkCcEvalToken_WordPlain; 4159 4160 /* Find the end of the current word. */ 4161 while (cchWord < cchLeft) 4162 { 4163 char ch = pchWord[cchWord]; 4164 if (!KMK_CC_EVAL_IS_SPACE_OR_DOLLAR(ch)) 4165 { /* likely */ } 4166 else if (ch == '$') 4167 enmToken = kKmkCcEvalToken_WordWithDollar; 4168 else 4169 break; 4170 cchWord++; 4171 } 4172 4173 /* Add the word. */ 4174 KMK_CC_EVAL_ENSURE_WORDS(pCompiler, cWords + 1); 4175 pCompiler->paWords[cWords].pchWord = pchWord; 4176 pCompiler->paWords[cWords].cchWord = cchWord; 4177 pCompiler->paWords[cWords].enmToken = enmToken; 4178 cWords++; 4179 4180 /* Skip the work and any trailing blanks. */ 4181 cchWord += cchSkipAfter; 4182 pchWord += cchWord; 4183 cchLeft -= cchWord; 4184 KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft); 4185 } while (cchLeft > 0); 4186 } 4187 /* 4188 * Have to deal with escaped EOLs. 4189 */ 4190 else 4191 { 4192 const char *pszContent = pCompiler->pszContent; 4193 do 4194 { 4195 size_t cchSkipAfter = 0; 4196 size_t cchWord = 1; 4197 KMKCCEVALTOKEN enmToken = kKmkCcEvalToken_WordPlain; 4198 4199 /* Find the end of the current word. */ 4200 while (cchWord < cchLeft) 4201 { 4202 char ch = pchWord[cchWord]; 4203 if (!KMK_CC_EVAL_IS_SPACE_DOLLAR_OR_SLASH(ch)) 4204 { /* likely */ } 4205 else if (ch == '$') 4206 enmToken = kKmkCcEvalToken_WordWithDollar; 4207 else if (ch != '\\') 4208 break; 4209 else if ((size_t)(&pchWord[cchWord] - pszContent) == pCompiler->paEscEols[iEscEol].offEsc) 4210 { 4211 cchSkipAfter = pCompiler->paEscEols[iEscEol].offEol - pCompiler->paEscEols[iEscEol].offEsc 4212 + pCompiler->cchEolSeq; 4213 iEscEol++; 4214 break; 4215 } 4216 cchWord++; 4217 } 4218 4219 /* Add the word. */ 4220 KMK_CC_EVAL_ENSURE_WORDS(pCompiler, cWords + 1); 4221 pCompiler->paWords[cWords].pchWord = pchWord; 4222 pCompiler->paWords[cWords].cchWord = cchWord; 4223 pCompiler->paWords[cWords].enmToken = enmToken; 4224 cWords++; 4225 4226 /* Skip the work and any trailing blanks. */ 4227 cchWord += cchSkipAfter; 4228 pchWord += cchWord; 4229 cchLeft -= cchWord; 4230 KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft); 4231 } while (cchLeft > 0); 4232 } 4233 pCompiler->cWords = cWords; 4234 return cWords; 4235 } 4236 4237 3860 4238 3861 4239 … … 4154 4532 * @param cchLeft The number of chars left on the current line 4155 4533 * starting at @a pchWord. 4156 * @param fStripTrailingSpaces Whether to strip trailing spaces. 4157 */ 4158 static size_t kmk_cc_eval_prep_normal_line(PKMKCCEVALCOMPILER pCompiler, const char * const pchWord, size_t cchLeft, 4159 int fStripTrailingSpaces) 4534 */ 4535 static size_t kmk_cc_eval_prep_normal_line(PKMKCCEVALCOMPILER pCompiler, const char * const pchWord, size_t cchLeft) 4160 4536 { 4161 4537 size_t cchRet; … … 4174 4550 { 4175 4551 cchRet = cchLeft; 4176 if (fStripTrailingSpaces)4177 while (cchRet > 0 && KMK_CC_IS_SPACE_CH(pchWord[cchRet - 1]))4178 cchRet--;4179 4552 4180 4553 KMK_CC_EVAL_ENSURE_STRCOPY_SEGS(pCompiler, 1); … … 4203 4576 KMK_CC_ASSERT(offWord + cchLeft <= pCompiler->cchContent); 4204 4577 KMK_CC_ASSERT(offWord < pCompiler->paEscEols[iEscEol].offEsc); 4205 KMK_CC_ASSERT(offWord >= (iEscEol ? pCompiler->paEscEols[ cEscEols- 1].offEol + pCompiler->cchEolSeq : pCompiler->offLine));4578 KMK_CC_ASSERT(offWord >= (iEscEol ? pCompiler->paEscEols[iEscEol - 1].offEol + pCompiler->cchEolSeq : pCompiler->offLine)); 4206 4579 4207 4580 /* Make sure we've got more than enough segments to fill in. */ … … 4221 4594 pCompiler->paStrCopySegs[cSegs].cchSrcAndPrependSpace = cchRet; 4222 4595 pCompiler->paStrCopySegs[cSegs].pchSrc = pchWord; 4596 cSegs++; 4223 4597 4224 4598 offWord = pCompiler->paEscEols[iEscEol].offEol + pCompiler->cchEolSeq; … … 4263 4637 if (offWord < offEsc) 4264 4638 { 4265 if (fStripTrailingSpaces)4266 while (KMK_CC_EVAL_IS_SPACE(pszContent[offEsc - 1]))4267 offEsc--;4268 4639 cchSeg = offEsc - offWord; 4269 4640 pCompiler->paStrCopySegs[cSegs].cchSrcAndPrependSpace = fPendingSpace ? -(ssize_t)cchSeg : (ssize_t)cchSeg; … … 4331 4702 if (cchLeft) 4332 4703 { 4333 size_t cchExpr = kmk_cc_eval_prep_normal_line(pCompiler, pchWord, cchLeft, 1 /*fStripTrailingSpaces*/); 4334 PKMKCCEVALIFEXPR pInstr = (PKMKCCEVALIFEXPR)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, 4335 KMKCCEVALIFEXPR_SIZE(cchExpr)); 4704 PKMKCCEVALIFEXPR pInstr; 4705 size_t cchExpr = kmk_cc_eval_prep_normal_line(pCompiler, pchWord, cchLeft); 4706 kmk_cc_eval_strip_right_v(pCompiler->paStrCopySegs, &pCompiler->cStrCopySegs, &cchExpr); 4707 4708 pInstr = (PKMKCCEVALIFEXPR)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, KMKCCEVALIFEXPR_SIZE(cchExpr)); 4336 4709 kmk_cc_eval_strcpyv(pInstr->szExpr, pCompiler->paStrCopySegs, pCompiler->cStrCopySegs, cchExpr); 4337 4710 pInstr->cchExpr = cchExpr; 4338 pInstr->IfCore.Core.enmOp Code = kKmkCcEvalInstr_if;4711 pInstr->IfCore.Core.enmOpcode = kKmkCcEvalInstr_if; 4339 4712 pInstr->IfCore.Core.iLine = pCompiler->iLine; 4340 4713 kmk_cc_eval_do_if_core(pCompiler, &pInstr->IfCore, fInElse); … … 4369 4742 const char * const pchVarNm = pchWord; 4370 4743 int fPlain; 4744 /** @todo this isn't quite right. It is a variable name, correct. However, it 4745 * doesn't need to subscribe entirely to the rules of a variable name. 4746 * Just find the end of the word, taking variable refs into account, 4747 * and consider it what we need. */ 4371 4748 pchWord = kmk_cc_eval_skip_var_name(pCompiler, pchWord, cchLeft, &cchLeft, &fPlain); 4372 4749 KMK_CC_ASSERT(pCompiler->iEscEol == iSavedEscEol || !fPlain); … … 4376 4753 PKMKCCEVALIFDEFPLAIN pInstr; 4377 4754 pInstr = (PKMKCCEVALIFDEFPLAIN)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, sizeof(*pInstr)); 4378 pInstr->IfCore.Core.enmOp Code = fPositiveStmt ? kKmkCcEvalInstr_ifdef_plain : kKmkCcEvalInstr_ifndef_plain;4755 pInstr->IfCore.Core.enmOpcode = fPositiveStmt ? kKmkCcEvalInstr_ifdef_plain : kKmkCcEvalInstr_ifndef_plain; 4379 4756 pInstr->IfCore.Core.iLine = pCompiler->iLine; 4380 4757 pInstr->pszName = strcache2_add(&variable_strcache, pchVarNm, cchVarNm); … … 4388 4765 char *pszCopy; 4389 4766 pCompiler->iEscEol = iSavedEscEol; 4390 cchCopy = kmk_cc_eval_prep_normal_line(pCompiler, pchVarNm, cchVarNm , 0 /*fStripTrailingSpaces*/);4767 cchCopy = kmk_cc_eval_prep_normal_line(pCompiler, pchVarNm, cchVarNm); 4391 4768 4392 4769 pInstr = (PKMKCCEVALIFDEFDYNAMIC)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, sizeof(*pInstr)); … … 4396 4773 kmk_cc_block_realign(pCompiler->ppBlockTail); 4397 4774 4398 pInstr->IfCore.Core.enmOp Code = fPositiveStmt ? kKmkCcEvalInstr_ifdef_dynamic : kKmkCcEvalInstr_ifndef_dynamic;4775 pInstr->IfCore.Core.enmOpcode = fPositiveStmt ? kKmkCcEvalInstr_ifdef_dynamic : kKmkCcEvalInstr_ifndef_dynamic; 4399 4776 pInstr->IfCore.Core.iLine = pCompiler->iLine; 4400 4777 kmk_cc_eval_compile_string_exp_subprog(pCompiler, pszCopy, cchCopy, &pInstr->NameSubprog); … … 4598 4975 * Initialize the instruction. 4599 4976 */ 4600 pInstr->IfCore.Core.enmOp Code = fPositiveStmt ? kKmkCcEvalInstr_ifeq : kKmkCcEvalInstr_ifneq;4977 pInstr->IfCore.Core.enmOpcode = fPositiveStmt ? kKmkCcEvalInstr_ifeq : kKmkCcEvalInstr_ifneq; 4601 4978 pInstr->IfCore.Core.iLine = pCompiler->iLine; 4602 kmk_cc_eval_ do_subprogram_or_plain(pCompiler, &pInstr->Left, Left.pszCopy, Left.cchCopy, Left.fPlain);4603 kmk_cc_eval_ do_subprogram_or_plain(pCompiler, &pInstr->Right, Right.pszCopy, Right.cchCopy, Right.fPlain);4979 kmk_cc_eval_init_subprogram_or_plain(pCompiler, &pInstr->Left, Left.pszCopy, Left.cchCopy, Left.fPlain); 4980 kmk_cc_eval_init_subprogram_or_plain(pCompiler, &pInstr->Right, Right.pszCopy, Right.cchCopy, Right.fPlain); 4604 4981 kmk_cc_eval_do_if_core(pCompiler, &pInstr->IfCore, fInElse); 4605 4982 … … 4739 5116 * Initialize the instruction. 4740 5117 */ 4741 pInstr->IfCore.Core.enmOp Code = fPositiveStmt ? kKmkCcEvalInstr_if1of : kKmkCcEvalInstr_ifn1of;5118 pInstr->IfCore.Core.enmOpcode = fPositiveStmt ? kKmkCcEvalInstr_if1of : kKmkCcEvalInstr_ifn1of; 4742 5119 pInstr->IfCore.Core.iLine = pCompiler->iLine; 4743 kmk_cc_eval_ do_subprogram_or_plain(pCompiler, &pInstr->Left, Left.pszCopy, Left.cchCopy, Left.fPlain);4744 kmk_cc_eval_ do_subprogram_or_plain(pCompiler, &pInstr->Right, Right.pszCopy, Right.cchCopy, Right.fPlain);5120 kmk_cc_eval_init_subprogram_or_plain(pCompiler, &pInstr->Left, Left.pszCopy, Left.cchCopy, Left.fPlain); 5121 kmk_cc_eval_init_subprogram_or_plain(pCompiler, &pInstr->Right, Right.pszCopy, Right.cchCopy, Right.fPlain); 4745 5122 kmk_cc_eval_do_if_core(pCompiler, &pInstr->IfCore, fInElse); 4746 5123 … … 4780 5157 /* Emit a jump instruction that will take us from the 'True' block to the 'endif'. */ 4781 5158 PKMKCCEVALJUMP pInstr = (PKMKCCEVALJUMP)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, sizeof(*pInstr)); 4782 pInstr->Core.enmOp Code = kKmkCcEvalInstr_jump;5159 pInstr->Core.enmOpcode = kKmkCcEvalInstr_jump; 4783 5160 pInstr->Core.iLine = pCompiler->iLine; 4784 5161 pInstr->pNext = NULL; … … 4896 5273 4897 5274 4898 static int kmk_cc_eval_do_include(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, KMKCCEVALINSTR enmInst) 4899 { 5275 /** 5276 * Parses a 'include file...', 'sinclude file...', '-include file...', 5277 * 'includedep file...', 'includedep-queue file...' and 5278 * 'includedep-flush file...' 5279 * 5280 * @returns 1 to indicate we've handled a keyword (see 5281 * kmk_cc_eval_try_handle_keyword). 5282 * @param pCompiler The compiler state. 5283 * @param pchWord First char after the include directive. 5284 * @param cchLeft The number of chars left to parse on this line. 5285 * @param enmOpcode The opcode for the include directive we're parsing. 5286 */ 5287 static int kmk_cc_eval_do_include(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, KMKCCEVALINSTR enmOpcode) 5288 { 5289 KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft); 5290 if (cchLeft) 5291 { 5292 /* 5293 * Split what's left up into words. 5294 */ 5295 unsigned cWords = kmk_cc_eval_parse_words(pCompiler, pchWord, cchLeft); 5296 KMK_CC_EVAL_DPRINTF(("%s: cWords=%d\n", g_apszEvalInstrNms[enmOpcode], cWords)); 5297 if (cWords) 5298 { 5299 PKMKCCEVALINCLUDE pInstr = (PKMKCCEVALINCLUDE)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, 5300 KMKCCEVALINCLUDE_SIZE(cWords)); 5301 pInstr->Core.enmOpcode = enmOpcode; 5302 pInstr->Core.iLine = 0; 5303 pInstr->cFiles = cWords; 5304 kmk_cc_eval_init_spp_array_from_duplicated_words(pCompiler, cWords, pCompiler->paWords, pInstr->aFiles); 5305 kmk_cc_block_realign(pCompiler->ppBlockTail); 5306 } 5307 else 5308 KMK_CC_ASSERT(0); 5309 } 5310 else 5311 KMK_CC_EVAL_DPRINTF(("%s: include without args\n", g_apszEvalInstrNms[enmOpcode])); 4900 5312 return 1; 4901 5313 } … … 4904 5316 static int kmk_cc_eval_do_vpath(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft) 4905 5317 { 5318 kmk_cc_eval_fatal(pCompiler, NULL, "vpath directive is not implemented\n"); 4906 5319 return 1; 4907 5320 } … … 4910 5323 static void kmk_cc_eval_handle_command(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft) 4911 5324 { 4912 5325 kmk_cc_eval_fatal(pCompiler, pchWord, "command handling not implemented yet"); 5326 } 5327 5328 5329 static int kmk_cc_eval_handle_recipe_cont_colon(PKMKCCEVALCOMPILER pCompiler, const char *pchWord0, size_t cchWord0, 5330 const char *pchColon, size_t cchLeft, unsigned fQualifiers) 5331 { 5332 kmk_cc_eval_fatal(pCompiler, pchWord0, "recipe handling not implemented yet"); 5333 return 1; 5334 } 5335 5336 5337 static int kmk_cc_eval_handle_recipe_cont_2nd_word(PKMKCCEVALCOMPILER pCompiler, const char *pchWord0, size_t cchWord0, 5338 const char *pchWord, size_t cchLeft, unsigned fQualifiers) 5339 { 5340 kmk_cc_eval_fatal(pCompiler, pchWord, "recipe handling not implemented yet"); 5341 return 1; 4913 5342 } 4914 5343 … … 4916 5345 static void kmk_cc_eval_handle_recipe(PKMKCCEVALCOMPILER pCompiler, const char *pszEqual, const char *pchWord, size_t cchLeft) 4917 5346 { 4918 5347 kmk_cc_eval_fatal(pCompiler, pchWord, "recipe handling not implemented yet"); 5348 } 5349 5350 static void kmk_cc_eval_end_of_recipe(PKMKCCEVALCOMPILER pCompiler) 5351 { 5352 if (pCompiler->pRecipe) 5353 { 5354 /** @todo do stuff here. */ 5355 } 4919 5356 } 4920 5357 … … 4933 5370 * @param fQualifiers The qualifiers. 4934 5371 */ 4935 static int kmk_cc_eval_do_var_undefine(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, 4936 unsigned fQualifiers) 4937 { 4938 5372 static int kmk_cc_eval_do_var_undefine(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, unsigned fQualifiers) 5373 { 5374 kmk_cc_eval_fatal(pCompiler, pchWord, "undefine handling not implemented yet"); 4939 5375 return 1; 4940 5376 } … … 4958 5394 { 4959 5395 5396 KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft); 5397 kmk_cc_eval_fatal(pCompiler, pchWord, "define handling not implemented yet"); 4960 5398 return 1; 4961 5399 } 4962 5400 4963 5401 4964 static int kmk_cc_eval_try_handle_assignment(PKMKCCEVALCOMPILER pCompiler, const char *pchTmp, 4965 const char *pchWord, size_t cchLeft, unsigned fQualifiers) 4966 { 4967 return 0; 5402 static int kmk_cc_eval_handle_assignment_or_recipe(PKMKCCEVALCOMPILER pCompiler, const char *pchTmp, 5403 const char *pchWord, size_t cchLeft, unsigned fQualifiers) 5404 { 5405 /* 5406 * We're currently at a word which may or may not be a variable name 5407 * followed by an assignment operator, alternatively it must be a recipe. 5408 * We need to figure this out and deal with it in the most efficient 5409 * manner as this is a very common occurence. 5410 */ 5411 unsigned const iEscEolVarNm = pCompiler->iEscEol; 5412 int fPlainVarNm = 1; 5413 const char *pchVarNm = pchWord; 5414 size_t cchVarNm; 5415 size_t cch = 0; 5416 char ch; 5417 5418 /* 5419 * The variable name. Complicate by there being no requirement of a space 5420 * preceeding the assignment operator, as well as that the variable name 5421 * may include variable references with spaces (function++) in them. 5422 */ 5423 for (;;) 5424 { 5425 if (cch < cchLeft) 5426 { /*likely*/ } 5427 else 5428 kmk_cc_eval_fatal(pCompiler, &pchWord[cch], "Neither recipe nor variable assignment"); 5429 5430 ch = pchWord[cch]; 5431 if (!KMK_CC_EVAL_IS_SPACE_DOLLAR_SLASH_OR_ASSIGN(ch)) 5432 cch++; 5433 /* Space? */ 5434 else if (KMK_CC_EVAL_IS_SPACE(ch)) 5435 { 5436 cchVarNm = cch; 5437 pchWord += cch; 5438 cchLeft -= cch; 5439 KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft); 5440 break; 5441 } 5442 /* Variable expansion may contain spaces, so handle specially. */ 5443 else if (ch == '$') 5444 { 5445 cch++; 5446 if (cch < cchLeft) 5447 { 5448 char const chOpen = pchWord[cch]; 5449 if (chOpen == '(' || chOpen == '{') 5450 { 5451 /* 5452 * Got a $(VAR) or ${VAR} to deal with here. This may 5453 * include nested variable references and span multiple 5454 * lines (at least for function calls). 5455 * 5456 * We scan forward till we've found the corresponding 5457 * closing parenthesis, considering any open parentheses 5458 * of the same kind as worth counting, even if there are 5459 * no dollar preceeding them, just like GNU make does. 5460 */ 5461 size_t const cchStart = cch - 1; 5462 char const chClose = chOpen == '(' ? ')' : '}'; 5463 unsigned cOpen = 1; 5464 cch++; 5465 for (;;) 5466 { 5467 if (cch < cchLeft) 5468 { 5469 ch = pchWord[cch]; 5470 if (!(KMK_CC_EVAL_IS_PAREN_OR_SLASH(ch))) 5471 cch++; 5472 else 5473 { 5474 cch++; 5475 if (ch == chClose) 5476 { 5477 if (--cOpen == 0) 5478 break; 5479 } 5480 else if (ch == chOpen) 5481 cOpen++; 5482 else if ( ch == '\\' 5483 && pCompiler->iEscEol < pCompiler->cEscEols 5484 && (size_t)(&pchWord[cch] - pCompiler->pszContent) 5485 == pCompiler->paEscEols[pCompiler->iEscEol].offEsc) 5486 { 5487 cch += pCompiler->paEscEols[pCompiler->iEscEol].offEol 5488 - pCompiler->paEscEols[pCompiler->iEscEol].offEsc 5489 + pCompiler->cchEolSeq; 5490 pCompiler->iEscEol++; 5491 } 5492 } 5493 } 5494 else if (cOpen == 1) 5495 kmk_cc_eval_fatal(pCompiler, &pchWord[cchStart], "Variable reference is missing '%c'", chClose); 5496 else 5497 kmk_cc_eval_fatal(pCompiler, &pchWord[cchStart], 5498 "%u variable references are missing '%c'", cOpen, chClose); 5499 } 5500 } 5501 /* Single char variable name. */ 5502 else if (!KMK_CC_EVAL_IS_SPACE(chOpen)) 5503 { /* likely */ } 5504 else 5505 kmk_cc_eval_fatal(pCompiler, &pchWord[cch], "Expected variable name after '$', not end of line"); 5506 } 5507 else 5508 kmk_cc_eval_fatal(pCompiler, &pchWord[cch], "Neither recipe nor variable assignment"); 5509 fPlainVarNm = 0; 5510 } 5511 /* Check out potential recipe. */ 5512 else if (ch == ':') 5513 { 5514 if ( cch + 1 < cchLeft 5515 && pchWord[cch + 1] != '=') 5516 { 5517 cchVarNm = cch; 5518 pchWord += cch; 5519 cchLeft -= cch; 5520 break; 5521 } 5522 #ifdef HAVE_DOS_PATHS 5523 /* Don't confuse the first colon in: 5524 C:/Windows/System32/Kernel32.dll: C:/Windows/System32/NtDll.dll 5525 for a recipe, it is only the second one which counts. */ 5526 else if ( cch == 1 5527 && isalpha((unsigned char)pchWord[0])) 5528 cch++; 5529 #endif 5530 else 5531 return kmk_cc_eval_handle_recipe_cont_colon(pCompiler, pchWord, cch, pchWord + cch, cchLeft - cch, fQualifiers); 5532 } 5533 /* Check out assignment operator. */ 5534 else if (ch == '=') 5535 { 5536 if (cch) 5537 { 5538 char chPrev = pchWord[cch - 1]; 5539 if (chPrev == ':' || chPrev == '+' || chPrev == '?' || chPrev == '<') 5540 cch--; 5541 cchVarNm = cch; 5542 pchWord += cch; 5543 cchLeft -= cch; 5544 break; 5545 } 5546 else 5547 kmk_cc_eval_fatal(pCompiler, pchWord, "Empty variable name."); 5548 } 5549 /* Check out potential escaped EOL sequence. */ 5550 else if (ch == '\\') 5551 { 5552 unsigned const iEscEol = pCompiler->iEscEol; 5553 if (iEscEol >= pCompiler->cEscEols) 5554 cch++; 5555 else 5556 { 5557 size_t offCur = &pchWord[cch] - pCompiler->pszContent; 5558 if (offCur < pCompiler->paEscEols[iEscEol].offEol) 5559 cch++; 5560 else 5561 { 5562 cchVarNm = cch; 5563 KMK_CC_ASSERT(offCur == pCompiler->paEscEols[iEscEol].offEol); 5564 cch = pCompiler->paEscEols[iEscEol].offEol + pCompiler->cchEolSeq - offCur; 5565 pCompiler->iEscEol = iEscEol + 1; 5566 pchWord += cch; 5567 cchLeft -= cch; 5568 KMK_CC_EVAL_SKIP_SPACES(pCompiler, pchWord, cchLeft); 5569 break; 5570 } 5571 } 5572 } 5573 else 5574 KMK_CC_ASSERT(0); 5575 } 5576 5577 /* 5578 * Check for assignment operator. 5579 */ 5580 if (cchLeft) 5581 { 5582 size_t cchValue; 5583 PKMKCCEVALASSIGN pInstr; 5584 KMKCCEVALINSTR enmOpCode; 5585 int fPlainValue; 5586 char *pszValue; 5587 5588 ch = *pchWord; 5589 if (ch == '=') 5590 { 5591 enmOpCode = kKmkCcEvalInstr_assign_recursive; 5592 pchWord++; 5593 cchLeft--; 5594 } 5595 else if (cchLeft >= 2 && pchWord[1] == '=') 5596 { 5597 if (ch == ':') 5598 enmOpCode = kKmkCcEvalInstr_assign_simple; 5599 else if (ch == '+') 5600 enmOpCode = kKmkCcEvalInstr_assign_append; 5601 else if (ch == '<') 5602 enmOpCode = kKmkCcEvalInstr_assign_prepend; 5603 else if (ch == '?') 5604 enmOpCode = kKmkCcEvalInstr_assign_if_new; 5605 else 5606 return kmk_cc_eval_handle_recipe_cont_2nd_word(pCompiler, pchVarNm, cchVarNm, pchWord, cchLeft, fQualifiers); 5607 pchWord += 2; 5608 cchLeft -= 2; 5609 } 5610 else 5611 return kmk_cc_eval_handle_recipe_cont_2nd_word(pCompiler, pchVarNm, cchVarNm, pchWord, cchLeft, fQualifiers); 5612 5613 /* 5614 * Skip leading spaces, if any and prep the value for copying. 5615 */ 5616 KMK_CC_EVAL_SKIP_SPACES(pCompiler, pchWord, cchLeft); 5617 cchValue = kmk_cc_eval_prep_normal_line(pCompiler, pchWord, cchLeft); 5618 fPlainValue = memchr(pchWord, '$', cchLeft) == NULL; 5619 5620 /* 5621 * Emit the instruction. 5622 */ 5623 kmk_cc_eval_end_of_recipe(pCompiler); 5624 5625 pInstr = (PKMKCCEVALASSIGN)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, sizeof(*pInstr)); 5626 pInstr->Core.enmOpcode = enmOpCode; 5627 pInstr->Core.iLine = pCompiler->iLine; 5628 pInstr->fExport = (fQualifiers & KMK_CC_EVAL_QUALIFIER_EXPORT) != 0; 5629 pInstr->fOverride = (fQualifiers & KMK_CC_EVAL_QUALIFIER_OVERRIDE) != 0; 5630 pInstr->fPrivate = (fQualifiers & KMK_CC_EVAL_QUALIFIER_PRIVATE) != 0; 5631 pInstr->fLocal = (fQualifiers & KMK_CC_EVAL_QUALIFIER_LOCAL) != 0; 5632 5633 /* We copy the value before messing around with the variable name since 5634 we have to do more iEolEsc saves & restores the other way around. */ 5635 pszValue = kmk_cc_eval_strdup_prepped(pCompiler, cchValue); 5636 if (fPlainVarNm) 5637 pchVarNm = strcache2_add(&variable_strcache, pchVarNm, cchVarNm); 5638 else 5639 { 5640 pCompiler->iEscEol = iEscEolVarNm; 5641 cchVarNm = kmk_cc_eval_prep_normal_line_ex(pCompiler, pchVarNm, cchVarNm); 5642 pchVarNm = kmk_cc_eval_strdup_prepped(pCompiler, cchVarNm); 5643 } 5644 kmk_cc_block_realign(pCompiler->ppBlockTail); 5645 KMK_CC_EVAL_DPRINTF(("%s: '%s' '%s'\n", g_apszEvalInstrNms[enmOpCode], pchVarNm, pszValue)); 5646 5647 kmk_cc_eval_init_subprogram_or_plain(pCompiler, &pInstr->Variable, pchVarNm, cchVarNm, fPlainVarNm); 5648 kmk_cc_eval_init_subprogram_or_plain(pCompiler, &pInstr->Value, pszValue, cchValue, fPlainValue); 5649 5650 pInstr->pNext = (PKMKCCEVALCORE)kmk_cc_block_get_next_ptr(*pCompiler->ppBlockTail); 5651 } 5652 else 5653 kmk_cc_eval_fatal(pCompiler, pchWord, "Neither recipe nor variable assignment"); 5654 return 1; 4968 5655 } 4969 5656 … … 4974 5661 * The 'local' directive must be first and it does not permit any qualifiers at 4975 5662 * the moment. Should any be added later, they will have to come after 'local'. 5663 * 5664 * @returns 1 to indicate we've handled a keyword (see 5665 * kmk_cc_eval_try_handle_keyword). 5666 * @param pCompiler The compiler state. 5667 * @param pchWord First char after 'local'. 5668 * @param cchLeft The number of chars left to parse on this line. 5669 * @param fQualifiers The qualifiers. 5670 */ 5671 static int kmk_cc_eval_do_var_local(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft) 5672 { 5673 KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft); 5674 if (cchLeft) 5675 { 5676 /* 5677 * Find the end of the variable name. 5678 */ 5679 5680 } 5681 else 5682 kmk_cc_eval_fatal(pCompiler, pchWord, "Expected variable name, assignment operator and value after 'local'"); 5683 return 1; 5684 } 5685 5686 5687 /** 5688 * Parses 'export [variable]' and 'export [qualifiers] variable = value' 5689 * expressions. 5690 * 5691 * When we find the 'export' directive at the start of a line, we need to 5692 * continue parsing with till we can tell the difference between the two forms. 4976 5693 * 4977 5694 * @returns 1 to indicate we've handled a keyword (see … … 4982 5699 * @param fQualifiers The qualifiers. 4983 5700 */ 4984 static int kmk_cc_eval_do_var_local(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft)4985 {4986 4987 return 1;4988 }4989 4990 4991 /**4992 * Parses 'export [variable]' and 'export [qualifiers] variable = value'4993 * expressions.4994 *4995 * When we find the 'export' directive at the start of a line, we need to4996 * continue parsing with till we can tell the difference between the two forms.4997 *4998 * @returns 1 to indicate we've handled a keyword (see4999 * kmk_cc_eval_try_handle_keyword).5000 * @param pCompiler The compiler state.5001 * @param pchWord First char after 'define'.5002 * @param cchLeft The number of chars left to parse on this line.5003 * @param fQualifiers The qualifiers.5004 */5005 5701 static int kmk_cc_eval_handle_var_export(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, unsigned fQualifiers) 5006 5702 { 5007 5703 kmk_cc_eval_fatal(pCompiler, pchWord, "export not implemented yet"); 5008 5704 return 1; 5009 5705 } … … 5034 5730 return kmk_cc_eval_do_var_define(pCompiler, pchWord + 6, cchLeft - 6, fQualifiers); 5035 5731 5036 if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "export", 6)) /* special */5037 return kmk_cc_eval_handle_var_export(pCompiler, pchWord + 6, cchLeft - 6, fQualifiers);5038 5039 5732 if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "undefine", 8)) /* final */ 5040 5733 return kmk_cc_eval_do_var_undefine(pCompiler, pchWord + 6, cchLeft - 6, fQualifiers); 5041 5734 5735 if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "export", 6)) 5736 { 5737 if (!(fQualifiers & KMK_CC_EVAL_QUALIFIER_EXPORT)) 5738 fQualifiers |= KMK_CC_EVAL_QUALIFIER_EXPORT; 5739 else 5740 kmk_cc_eval_warn(pCompiler, pchWord, "'export' qualifier repeated"); 5741 pchWord += 6; 5742 cchLeft -= 6; 5743 continue; 5744 } 5745 5042 5746 if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "override", 8)) 5043 5747 { 5044 /** @todo warn if repeated. */ 5045 fQualifiers |= KMK_CC_EVAL_QUALIFIER_OVERRIDE; 5748 if (!(fQualifiers & KMK_CC_EVAL_QUALIFIER_OVERRIDE)) 5749 fQualifiers |= KMK_CC_EVAL_QUALIFIER_OVERRIDE; 5750 else 5751 kmk_cc_eval_warn(pCompiler, pchWord, "'override' qualifier repeated"); 5046 5752 pchWord += 8; 5047 5753 cchLeft -= 8; … … 5051 5757 if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "private", 7)) 5052 5758 { 5053 /** @todo warn if repeated. */ 5054 fQualifiers |= KMK_CC_EVAL_QUALIFIER_PRIVATE; 5759 if (!(fQualifiers & KMK_CC_EVAL_QUALIFIER_PRIVATE)) 5760 fQualifiers |= KMK_CC_EVAL_QUALIFIER_PRIVATE; 5761 else 5762 kmk_cc_eval_warn(pCompiler, pchWord, "'private' qualifier repeated"); 5055 5763 pchWord += 7; 5056 5764 cchLeft -= 7; … … 5067 5775 const char *pchEqual = (const char *)memchr(pchWord, '=', cchLeft); 5068 5776 if (pchEqual) 5069 return kmk_cc_eval_ try_handle_assignment(pCompiler, pchEqual, pchWord, cchLeft, fQualifiers);5777 return kmk_cc_eval_handle_assignment_or_recipe(pCompiler, pchEqual, pchWord, cchLeft, fQualifiers); 5070 5778 } 5071 5779 return 0; … … 5440 6148 { 5441 6149 pchTmp = (const char *)memchr(pchWord, '=', cchLeft); 5442 if ( !pchTmp 5443 || !kmk_cc_eval_try_handle_assignment(&Compiler, pchTmp, pchWord, cchLeft, 0) ) 6150 if (pchTmp) 6151 kmk_cc_eval_handle_assignment_or_recipe(&Compiler, pchTmp, pchWord, cchLeft, 0); 6152 else 5444 6153 kmk_cc_eval_handle_recipe(&Compiler, pchTmp, pchWord, cchLeft); 5445 6154 }
Note:
See TracChangeset
for help on using the changeset viewer.