Index: /trunk/src/kmk/Makefile.kmk
===================================================================
--- /trunk/src/kmk/Makefile.kmk	(revision 2800)
+++ /trunk/src/kmk/Makefile.kmk	(revision 2801)
@@ -209,5 +209,5 @@
  kmk_DEFS += CONFIG_WITH_EVAL_COMPILER
 endif
-ifdef CONFIG_WITH_COMPILER
+ifdef CONFIG_WITH_COMPILE_EVERYTHING
  kmk_DEFS += CONFIG_WITH_COMPILE_EVERYTHING
 endif
Index: /trunk/src/kmk/kmk_cc_exec.c
===================================================================
--- /trunk/src/kmk/kmk_cc_exec.c	(revision 2800)
+++ /trunk/src/kmk/kmk_cc_exec.c	(revision 2801)
@@ -121,10 +121,14 @@
 
 
-/** @def KMK_CC_IS_SPACE_CH
- * Checks if it's a space char. */
-#define KMK_CC_IS_SPACE_CH(a_ch)                    isspace((unsigned char)(a_ch))
-
 /** Aligns a size for the block allocator. */
 #define KMK_CC_BLOCK_ALIGN_SIZE(a_cb)               ( ((a_cb) + (sizeof(void *) - 1U)) & ~(uint32_t)(sizeof(void *) - 1U) )
+
+/** How to declare a no-return function.
+ * Place between scope (if any) and return type.  */
+#ifdef _MSC_VER
+# define KMK_CC_FN_NO_RETURN                        declspec(noreturn)
+#elif defined(__GNUC__)
+# define KMK_CC_FN_NO_RETURN                        __attribute__((__noreturn__))
+#endif
 
 
@@ -177,26 +181,41 @@
  * All space characters, backslash (EOL escape), variable expansion dollar,
  * variable assignment operator chars, recipe colon and recipe percent. */
-#define KMK_CC_EVAL_CH_SPACE_OR_VAR_OR_RECIPE       UINT16_C(8)
-#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)
+#define KMK_CC_EVAL_CH_SPACE_VAR_OR_RECIPE          UINT16_C(8)
+#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)
+/** Dollar character (possible variable expansion). */
+#define KMK_CC_EVAL_CH_DOLLAR                       UINT16_C(16)
+#define KMK_CC_EVAL_IS_DOLLAR(a_ch)                 (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_DOLLAR)
+/** Dollar character (possible variable expansion). */
+#define KMK_CC_EVAL_CH_BACKSLASH                    UINT16_C(32)
+#define KMK_CC_EVAL_IS_BACKSLASH(a_ch)              (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_BACKSLASH)
 /** Possible EOL character. */
-#define KMK_CC_EVAL_CH_EOL_CANDIDATE                UINT16_C(16)
+#define KMK_CC_EVAL_CH_EOL_CANDIDATE                UINT16_C(64)
 #define KMK_CC_EVAL_IS_EOL_CANDIDATE(a_ch)          (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_EOL_CANDIDATE)
 /** First character in a keyword. */
-#define KMK_CC_EVAL_CH_1ST_IN_KEYWORD               UINT16_C(32)
+#define KMK_CC_EVAL_CH_1ST_IN_KEYWORD               UINT16_C(128)
 #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)
 /** Second character in a keyword. */
-#define KMK_CC_EVAL_CH_2ND_IN_KEYWORD               UINT16_C(64)
+#define KMK_CC_EVAL_CH_2ND_IN_KEYWORD               UINT16_C(256)
 #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)
 /** First character in a variable qualifier keyword or 'define'. */
-#define KMK_CC_EVAL_CH_1ST_IN_VARIABLE_KEYWORD      UINT16_C(128)
+#define KMK_CC_EVAL_CH_1ST_IN_VARIABLE_KEYWORD      UINT16_C(512)
 #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)
 /** Used when parsing variable names, looking for the end of a nested
  *  variable reference.  Matches parentheses and backslash (escaped eol). */
-#define KMK_CC_EVAL_CH_PAREN_OR_SLASH               UINT16_C(256)
+#define KMK_CC_EVAL_CH_PAREN_OR_SLASH               UINT16_C(1024)
 #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)
 /** Used when parsing ifeq/ifneq (,) sequences.
  * Matches parentheses, comma and dollar (for non-plain string detection). */
-#define KMK_CC_EVAL_CH_PAREN_COMMA_OR_DOLLAR        UINT16_C(512)
+#define KMK_CC_EVAL_CH_PAREN_COMMA_OR_DOLLAR        UINT16_C(2048)
 #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)
+
+/** Test of space or dollar characters. */
+#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))
+/** Test of space, dollar or backslash (possible EOL escape) characters. */
+#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))
+/** Test of space, dollar, backslash (possible EOL escape) or variable
+ * assingment characters. */
+#define KMK_CC_EVAL_IS_SPACE_DOLLAR_SLASH_OR_ASSIGN(a_ch)   \
+    (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))
 /** @} */
 
@@ -282,5 +301,5 @@
 {
     /** The instruction opcode number (KMKCCEXPINSTR). */
-    KMKCCEXPINSTR           enmOpCode;
+    KMKCCEXPINSTR           enmOpcode;
 } KMKCCEXPCORE;
 typedef KMKCCEXPCORE *PKMKCCEXPCORE;
@@ -608,5 +627,5 @@
 {
     /** The instruction opcode number (KMKCCEVALINSTR). */
-    KMKCCEVALINSTR          enmOpCode;
+    KMKCCEVALINSTR          enmOpcode;
     /** The line number in the source this statement is associated with. */
     unsigned                iLine;
@@ -636,10 +655,12 @@
     /** The core instruction. */
     KMKCCEVALCORE           Core;
-    /** Whether the 'export' directive was used. */
+    /** Whether the 'export' qualifier was used. */
     uint8_t                 fExport;
-    /** Whether the 'override' directive was used. */
+    /** Whether the 'override' qualifier was used. */
     uint8_t                 fOverride;
-    /** Whether the 'local' directive was used. */
+    /** Whether the 'local' qualifier was used. */
     uint8_t                 fLocal;
+    /** Whether the 'private' qualifier was used. */
+    uint8_t                 fPrivate;
     /** The variable name.
      * @remarks Plain text names are in variable_strcache. */
@@ -1005,4 +1026,44 @@
 
 
+/** This is parallel to KMKCCEVALINSTR.   */
+static const char * const g_apszEvalInstrNms[] =
+{
+    "jump",
+    "assign_recursive",
+    "assign_simple",
+    "assign_append",
+    "assign_prepend",
+    "assign_if_new",
+    "assign_define",
+    "export",
+    "unexport",
+    "export_all",
+    "unexport_all",
+    "ifdef_plain",
+    "ifndef_plain",
+    "ifdef_dynamic",
+    "ifndef_dynamic",
+    "ifeq",
+    "ifneq",
+    "if1of",
+    "ifn1of",
+    "if",
+    "include",
+    "include_silent",
+    "includedep",
+    "includedep_queue",
+    "includedep_flush",
+    "recipe_no_commands",
+    "recipe_start_normal",
+    "recipe_start_double_colon",
+    "recipe_start_pattern",
+    "recipe_commands",
+    "recipe_end",
+    "recipe_cancel_pattern",
+    "vpath",
+    "vpath_clear_pattern",
+    "vpath_clear_all",
+};
+
 /*********************************************************************************************************************************
 *   Internal Functions                                                                                                           *
@@ -1029,5 +1090,5 @@
 
     /* space chars and zero terminator. */
-#define MY_SPACE_BITS KMK_CC_EVAL_CH_SPACE | KMK_CC_EVAL_CH_SPACE_OR_BACKSLASH | KMK_CC_EVAL_CH_SPACE_OR_VAR_OR_RECIPE
+#define MY_SPACE_BITS KMK_CC_EVAL_CH_SPACE | KMK_CC_EVAL_CH_SPACE_OR_BACKSLASH | KMK_CC_EVAL_CH_SPACE_VAR_OR_RECIPE
     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, ' ',  MY_SPACE_BITS);
     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\t', MY_SPACE_BITS);
@@ -1036,5 +1097,5 @@
     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\f', MY_SPACE_BITS);
     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\r', MY_SPACE_BITS | KMK_CC_EVAL_CH_EOL_CANDIDATE);
-    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\\', KMK_CC_EVAL_CH_SPACE_OR_BACKSLASH | KMK_CC_EVAL_CH_SPACE_OR_VAR_OR_RECIPE);
+    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\\', KMK_CC_EVAL_CH_SPACE_OR_BACKSLASH | KMK_CC_EVAL_CH_SPACE_VAR_OR_RECIPE);
 #undef MY_SPACE_BITS
 
@@ -1057,10 +1118,6 @@
 
     /* Assignment punctuation and recipe stuff. */
-    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '=', KMK_CC_EVAL_CH_SPACE_OR_VAR_OR_RECIPE);
-    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, ':', KMK_CC_EVAL_CH_SPACE_OR_VAR_OR_RECIPE);
-    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '<', KMK_CC_EVAL_CH_SPACE_OR_VAR_OR_RECIPE);
-    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '?', KMK_CC_EVAL_CH_SPACE_OR_VAR_OR_RECIPE);
-    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '+', KMK_CC_EVAL_CH_SPACE_OR_VAR_OR_RECIPE);
-    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '%', KMK_CC_EVAL_CH_SPACE_OR_VAR_OR_RECIPE); /* uncertain... */
+    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '=', KMK_CC_EVAL_CH_SPACE_VAR_OR_RECIPE);
+    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, ':', KMK_CC_EVAL_CH_SPACE_VAR_OR_RECIPE);
 
     /* For locating the end of variable expansion.  */
@@ -1076,4 +1133,14 @@
     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, ',', KMK_CC_EVAL_CH_PAREN_COMMA_OR_DOLLAR);
     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '$', KMK_CC_EVAL_CH_PAREN_COMMA_OR_DOLLAR);
+
+    /* Misc. */
+    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '$',  KMK_CC_EVAL_CH_DOLLAR);
+    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\\', KMK_CC_EVAL_CH_BACKSLASH);
+
+    /*
+     * Check that the eval instruction names match up.
+     */
+    KMK_CC_ASSERT(strcmp(g_apszEvalInstrNms[kKmkCcEvalInstr_ifneq], "ifneq") == 0);
+    KMK_CC_ASSERT(strcmp(g_apszEvalInstrNms[kKmkCcEvalInstr_vpath_clear_all], "vpath_clear_all") == 0);
 }
 
@@ -1438,5 +1505,5 @@
     /* Emit jump. */
     pJump = (PKMKCCEXPJUMP)((char *)pOldBlock + pOldBlock->offNext);
-    pJump->Core.enmOpCode = kKmkCcExpInstr_Jump;
+    pJump->Core.enmOpcode = kKmkCcExpInstr_Jump;
     pJump->pNext = pRet;
     pOldBlock->offNext += sizeof(*pJump);
@@ -1508,5 +1575,5 @@
     /* Emit jump. */
     pJump = (PKMKCCEVALJUMP)((char *)pOldBlock + pOldBlock->offNext);
-    pJump->Core.enmOpCode = kKmkCcEvalInstr_jump;
+    pJump->Core.enmOpcode = kKmkCcEvalInstr_jump;
     pJump->pNext = pRet;
     pOldBlock->offNext += sizeof(*pJump);
@@ -1575,5 +1642,5 @@
 {
     PKMKCCEXPCORE pCore = kmk_cc_block_alloc_exp(ppBlockTail, sizeof(*pCore));
-    pCore->enmOpCode = kKmkCcExpInstr_Return;
+    pCore->enmOpcode = kKmkCcExpInstr_Return;
     kmk_cc_block_realign(ppBlockTail);
 }
@@ -1649,5 +1716,5 @@
     uint32_t            cActualArgs = cArgs <= cMaxArgs || !cMaxArgs ? cArgs : cMaxArgs;
     PKMKCCEXPDYNFUNC    pInstr  = (PKMKCCEXPDYNFUNC)kmk_cc_block_alloc_exp(ppBlockTail, KMKCCEXPDYNFUNC_SIZE(cActualArgs));
-    pInstr->FnCore.Core.enmOpCode = kKmkCcExpInstr_DynamicFunction;
+    pInstr->FnCore.Core.enmOpcode = kKmkCcExpInstr_DynamicFunction;
     pInstr->FnCore.cArgs          = cActualArgs;
     pInstr->FnCore.pfnFunction    = pfnFunction;
@@ -1750,5 +1817,5 @@
     uint32_t            cActualArgs = cArgs <= cMaxArgs || !cMaxArgs ? cArgs : cMaxArgs;
     PKMKCCEXPPLAINFUNC  pInstr  = (PKMKCCEXPPLAINFUNC)kmk_cc_block_alloc_exp(ppBlockTail, KMKCCEXPPLAINFUNC_SIZE(cActualArgs));
-    pInstr->FnCore.Core.enmOpCode = kKmkCcExpInstr_PlainFunction;
+    pInstr->FnCore.Core.enmOpcode = kKmkCcExpInstr_PlainFunction;
     pInstr->FnCore.cArgs          = cActualArgs;
     pInstr->FnCore.pfnFunction    = pfnFunction;
@@ -1821,5 +1888,5 @@
 
     pInstr = (PKMKCCEXPDYNVAR)kmk_cc_block_alloc_exp(ppBlockTail, sizeof(*pInstr));
-    pInstr->Core.enmOpCode = kKmkCcExpInstr_DynamicVariable;
+    pInstr->Core.enmOpcode = kKmkCcExpInstr_DynamicVariable;
 
     rc = kmk_cc_exp_compile_subprog(ppBlockTail, pchNameExpr, cchNameExpr, &pInstr->Subprog);
@@ -1855,5 +1922,5 @@
         {
             PKMKCCEXPPLAINVAR pInstr = (PKMKCCEXPPLAINVAR)kmk_cc_block_alloc_exp(ppBlockTail, sizeof(*pInstr));
-            pInstr->Core.enmOpCode = kKmkCcExpInstr_PlainVariable;
+            pInstr->Core.enmOpcode = kKmkCcExpInstr_PlainVariable;
             pInstr->pszName = strcache2_add(&variable_strcache, pchName, cchName);
         }
@@ -1872,5 +1939,5 @@
 
             pInstr = (PKMKCCEXPSRPLAINVAR)kmk_cc_block_alloc_exp(ppBlockTail, sizeof(*pInstr));
-            pInstr->Core.enmOpCode = kKmkCcExpInstr_SearchAndReplacePlainVariable;
+            pInstr->Core.enmOpcode = kKmkCcExpInstr_SearchAndReplacePlainVariable;
             pInstr->pszName = strcache2_add(&variable_strcache, pchName, cchName2);
 
@@ -1939,5 +2006,5 @@
     {
         PKMKCCEXPCOPYSTRING pInstr = (PKMKCCEXPCOPYSTRING)kmk_cc_block_alloc_exp(ppBlockTail, sizeof(*pInstr));
-        pInstr->Core.enmOpCode = kKmkCcExpInstr_CopyString;
+        pInstr->Core.enmOpcode = kKmkCcExpInstr_CopyString;
         pInstr->cchCopy = cchStr;
         pInstr->pachSrc = pchStr;
@@ -2402,5 +2469,5 @@
     for (;;)
     {
-        switch (pInstrCore->enmOpCode)
+        switch (pInstrCore->enmOpcode)
         {
             case kKmkCcExpInstr_CopyString:
@@ -2649,5 +2716,5 @@
             default:
                 fatal(NULL, _("Unknown string expansion opcode: %d (%#x)"),
-                      (int)pInstrCore->enmOpCode, (int)pInstrCore->enmOpCode);
+                      (int)pInstrCore->enmOpcode, (int)pInstrCore->enmOpcode);
                 return NULL;
         }
@@ -2906,5 +2973,5 @@
 {
     /** The token word (lexeme).   */
-    const char         *pszWord;
+    const char         *pchWord;
     /** The length of the word (lexeme). */
     uint32_t            cchWord;
@@ -2913,4 +2980,5 @@
 } KMKCCEVALWORD;
 typedef KMKCCEVALWORD *PKMKCCEVALWORD;
+typedef KMKCCEVALWORD const *PCKMKCCEVALWORD;
 
 
@@ -3066,5 +3134,5 @@
 }
 
-static void kmk_cc_eval_fatal(PKMKCCEVALCOMPILER pCompiler, const char *pchWhere, const char *pszMsg, ...)
+static void KMK_CC_FN_NO_RETURN kmk_cc_eval_fatal(PKMKCCEVALCOMPILER pCompiler, const char *pchWhere, const char *pszMsg, ...)
 {
     va_list  va;
@@ -3117,5 +3185,6 @@
 
 
-static void kmk_cc_eval_fatal_eol(PKMKCCEVALCOMPILER pCompiler, const char *pchEol, unsigned iLine, size_t offLine)
+static KMK_CC_FN_NO_RETURN void
+kmk_cc_eval_fatal_eol(PKMKCCEVALCOMPILER pCompiler, const char *pchEol, unsigned iLine, size_t offLine)
 {
     pCompiler->iLine   = iLine;
@@ -3125,4 +3194,13 @@
         kmk_cc_eval_fatal(pCompiler, pchEol, "Missing 2nd EOL character: found %#x instead of %#x\n",
                                    pchEol, pCompiler->chSecondEol);
+}
+
+
+static void kmk_cc_eval_warn(PKMKCCEVALCOMPILER pCompiler, const char *pchWhere, const char *pszMsg, ...)
+{
+    /** @todo warnings.   */
+    (void)pchWhere;
+    (void)pCompiler;
+    (void)pszMsg;
 }
 
@@ -3155,6 +3233,6 @@
  * @param   fPlain      Whether it's plain or not.  If not, we'll compile it.
  */
-static void kmk_cc_eval_do_subprogram_or_plain(PKMKCCEVALCOMPILER pCompiler, PKMKCCEXPSUBPROGORPLAIN pOperand,
-                                               const char *pszString, size_t cchString, int fPlain)
+static void kmk_cc_eval_init_subprogram_or_plain(PKMKCCEVALCOMPILER pCompiler, PKMKCCEXPSUBPROGORPLAIN pOperand,
+                                                 const char *pszString, size_t cchString, int fPlain)
 {
     pOperand->fPlainIsInVarStrCache  = 0;
@@ -3169,4 +3247,42 @@
     else
         kmk_cc_eval_compile_string_exp_subprog(pCompiler, pszString, cchString, &pOperand->u.Subprog);
+}
+
+/**
+ * Initializes an array of subprogram-or-plain (spp) operands from a word array.
+ *
+ * The words will be duplicated and the caller must therefore call
+ * kmk_cc_block_realign() when done (it's not done here as the caller may
+ * initialize several string operands and we don't want any unnecessary
+ * fragmentation).
+ *
+ * @param   pCompiler   The compiler state.
+ * @param   cWords      The number of words to copy.
+ * @param   paSrc       The source words.
+ * @param   paDst       The destination subprogram-or-plain array.
+ */
+static void kmk_cc_eval_init_spp_array_from_duplicated_words(PKMKCCEVALCOMPILER pCompiler, unsigned cWords,
+                                                             PKMKCCEVALWORD paSrc, PKMKCCEXPSUBPROGORPLAIN paDst)
+{
+    unsigned i;
+    for (i = 0; i < cWords; i++)
+    {
+        const char *pszCopy = kmk_cc_block_strdup(pCompiler->ppBlockTail, paSrc[i].pchWord, paSrc[i].cchWord);
+        paDst[i].fPlainIsInVarStrCache  = 0;
+        paDst[i].bUser                  = 0;
+        paDst[i].bUser2                 = 0;
+        if (paSrc[i].enmToken == kKmkCcEvalToken_WordWithDollar)
+        {
+            paDst[i].fSubprog    = 1;
+            kmk_cc_eval_compile_string_exp_subprog(pCompiler, pszCopy, paSrc[i].cchWord, &paDst[i].u.Subprog);
+        }
+        else
+        {
+            paDst[i].fSubprog    = 0;
+            paDst[i].u.Plain.cch = paSrc[i].cchWord;
+            paDst[i].u.Plain.psz = pszCopy;
+        }
+        KMK_CC_EVAL_DPRINTF(("  %s\n", pszCopy));
+    }
 }
 
@@ -3310,4 +3426,217 @@
      || ((a_ch) == '\\' && (a_ch2) == (a_pCompiler)->chFirstEol) )
 
+
+/**
+ * Common path for space skipping worker functions when escaped EOLs may be
+ * involed.
+ *
+ * @returns Points to the first non-space character or end of input.
+ * @param   pchWord         The current position. There is some kind of char
+ * @param   cchLeft         The current number of chars left to parse in the
+ *                          current line.
+ * @param   pcchLeft        Where to store the updated @a cchLeft value.
+ * @param   pCompiler       The compiler instance data.
+ */
+static const char *kmk_cc_eval_skip_spaces_with_esc_eol(const char *pchWord, size_t cchLeft, size_t *pcchLeft,
+                                                        PKMKCCEVALCOMPILER pCompiler)
+{
+    /*
+     * Skip further spaces. We unrolls 4 loops here.
+     * ASSUMES cchEscEolSeq is either 2 or 3!
+     */
+    KMK_CC_ASSERT(pCompiler->cchEscEolSeq == 2 || pCompiler->cchEscEolSeq == 3);
+    KMK_CC_ASSERT(pCompiler->iEscEol < pCompiler->cEscEols);
+    while (cchLeft >= 4)
+    {
+        /* First char. */
+        char ch = pchWord[0];
+        if (KMK_CC_EVAL_IS_SPACE(ch))
+        { /* maybe likely */ }
+        else if (   ch == '\\'
+                 && pchWord[1] == pCompiler->chFirstEol)
+        {
+            pchWord += pCompiler->cchEscEolSeq;
+            cchLeft -= pCompiler->cchEscEolSeq;
+            pCompiler->iEscEol++;
+            continue;
+        }
+        else
+        {
+            *pcchLeft = cchLeft;
+            return pchWord;
+        }
+
+        /* Second char. */
+        ch = pchWord[1];
+        if (KMK_CC_EVAL_IS_SPACE(ch))
+        { /* maybe likely */ }
+        else if (   ch == '\\'
+                 && pchWord[2] == pCompiler->chFirstEol)
+        {
+            pchWord += 1 + pCompiler->cchEscEolSeq;
+            cchLeft -= 1 + pCompiler->cchEscEolSeq;
+            pCompiler->iEscEol++;
+            continue;
+        }
+        else
+        {
+            *pcchLeft = cchLeft - 1;
+            return pchWord + 1;
+        }
+
+        /* Third char. */
+        ch = pchWord[2];
+        if (KMK_CC_EVAL_IS_SPACE(ch))
+        { /* maybe likely */ }
+        else if (   ch == '\\'
+                 && pchWord[3] == pCompiler->chFirstEol
+                 && cchLeft >= 2 + pCompiler->cchEscEolSeq)
+        {
+            pchWord += 2 + pCompiler->cchEscEolSeq;
+            cchLeft -= 2 + pCompiler->cchEscEolSeq;
+            pCompiler->iEscEol++;
+            continue;
+        }
+        else
+        {
+            *pcchLeft = cchLeft - 2;
+            return pchWord + 2;
+        }
+
+        /* Third char. */
+        ch = pchWord[3];
+        if (KMK_CC_EVAL_IS_SPACE(ch))
+        {
+            pchWord += 4;
+            cchLeft -= 4;
+        }
+        else if (   ch == '\\'
+                 && cchLeft >= 3 + pCompiler->cchEscEolSeq
+                 && pchWord[4] == pCompiler->chFirstEol)
+        {
+            pchWord += 3 + pCompiler->cchEscEolSeq;
+            cchLeft -= 3 + pCompiler->cchEscEolSeq;
+            pCompiler->iEscEol++;
+        }
+        else
+        {
+            *pcchLeft = cchLeft - 3;
+            return pchWord + 3;
+        }
+    }
+
+    /*
+     * Simple loop for the final three chars.
+     */
+    while (cchLeft > 0)
+    {
+        /* First char. */
+        char ch = *pchWord;
+        if (KMK_CC_EVAL_IS_SPACE(ch))
+        {
+            pchWord += 1;
+            cchLeft -= 1;
+        }
+        else if (   ch == '\\'
+                 && cchLeft > pCompiler->cchEolSeq
+                 && pchWord[1] == pCompiler->chFirstEol)
+        {
+            pchWord += pCompiler->cchEscEolSeq;
+            cchLeft -= pCompiler->cchEscEolSeq;
+            pCompiler->iEscEol++;
+        }
+        else
+            break;
+    }
+
+    *pcchLeft = cchLeft;
+    return pchWord;
+}
+
+
+/**
+ * Common path for space skipping worker functions when no escaped EOLs need
+ * considering.
+ *
+ * @returns Points to the first non-space character or end of input.
+ * @param   pchWord         The current position. There is some kind of char
+ * @param   cchLeft         The current number of chars left to parse in the
+ *                          current line.
+ * @param   pcchLeft        Where to store the updated @a cchLeft value.
+ * @param   pCompiler       The compiler instance data.
+ */
+static const char *kmk_cc_eval_skip_spaces_without_esc_eol(const char *pchWord, size_t cchLeft, size_t *pcchLeft,
+                                                           PKMKCCEVALCOMPILER pCompiler)
+{
+    /*
+     * 4x loop unroll.
+     */
+    while (cchLeft >= 4)
+    {
+        if (KMK_CC_EVAL_IS_SPACE(pchWord[0]))
+        {
+            if (KMK_CC_EVAL_IS_SPACE(pchWord[1]))
+            {
+                if (KMK_CC_EVAL_IS_SPACE(pchWord[2]))
+                {
+                    if (KMK_CC_EVAL_IS_SPACE(pchWord[3]))
+                    {
+                        pchWord += 4;
+                        cchLeft -= 4;
+                    }
+                    else
+                    {
+                        *pcchLeft = cchLeft - 3;
+                        return pchWord + 3;
+                    }
+                }
+                else
+                {
+                    *pcchLeft = cchLeft - 2;
+                    return pchWord + 2;
+                }
+            }
+            else
+            {
+                *pcchLeft = cchLeft - 1;
+                return pchWord + 1;
+            }
+        }
+        else
+        {
+            *pcchLeft = cchLeft;
+            return pchWord;
+        }
+    }
+
+    /*
+     * The last 3. Not entirely sure if this yield good code.
+     */
+    switch (cchLeft & 3)
+    {
+        case 3:
+            if (!KMK_CC_EVAL_IS_SPACE(*pchWord))
+                break;
+            pchWord++;
+            cchLeft--;
+        case 2:
+            if (!KMK_CC_EVAL_IS_SPACE(*pchWord))
+                break;
+            pchWord++;
+            cchLeft--;
+        case 1:
+            if (!KMK_CC_EVAL_IS_SPACE(*pchWord))
+                break;
+            pchWord++;
+            cchLeft--;
+        case 0:
+            break;
+    }
+
+    *pcchLeft = cchLeft;
+    return pchWord;
+}
+
+
 /**
  * Used to skip spaces after a word.
@@ -3349,30 +3678,4 @@
     } while (0)
 
-
-//static int kmk_cc_eval_skip_esc_eol_slow(const char *pchWord, size_t cchLeft, PKMKCCEVALCOMPILER pCompiler,
-//                                                  const char **ppchWord, size_t *pcchLeft)
-//{
-//    KMK_CC_ASSERT(pchWord[0] == '\\');
-//    if (pCompiler->cEscEols)
-//    {
-//        size_t cchMin = 1 + pCompiler->chFirstEol;
-//        if (cchLeft >= cchMin)
-//        {
-//            /* The simple case, no extra backslashes. */
-//            if (pchWord[1] == pCompiler->chFirstEol)
-//            {
-//                *pcchLeft  = cchLeft - cchMin;
-//                *pchWord   = pchWord + cchMin;
-//                return 1;
-//            }
-//
-//            /* The unlikly case,  */
-//
-//        }
-//    }
-//    return 0;
-//}
-
-
 /**
  * The slow path of KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD.
@@ -3394,7 +3697,22 @@
  */
 static const char *kmk_cc_eval_skip_spaces_after_word_slow(const char *pchWord, size_t *pcchLeft, char ch,
-                                                                    PKMKCCEVALCOMPILER pCompiler)
+                                                           PKMKCCEVALCOMPILER pCompiler)
 {
     size_t cchLeft = *pcchLeft;
+
+    /*
+     * It's all very simple when we don't have to consider escaped EOLs.
+     */
+    if (pCompiler->iEscEol >= pCompiler->cEscEols)
+    {
+        if (ch != '\\')
+        {
+            pchWord += 1;
+            cchLeft -= 1;
+        }
+        else
+            return pchWord;
+        return kmk_cc_eval_skip_spaces_without_esc_eol(pchWord, cchLeft, pcchLeft, pCompiler);
+    }
 
     /*
@@ -3420,153 +3738,42 @@
         cchLeft -= pCompiler->cchEscEolSeq;
         pCompiler->iEscEol++;
+
+        if (pCompiler->iEscEol < pCompiler->cEscEols)
+        { /* likely */ }
+        else return kmk_cc_eval_skip_spaces_without_esc_eol(pchWord, cchLeft, pcchLeft, pCompiler);
     }
     else
         return pchWord;
-
-    /*
-     * Skip further spaces. We unrolls 4 loops here.
-     * ASSUMES cchEscEolSeq is either 2 or 3!
-     */
-    KMK_CC_ASSERT(pCompiler->cchEscEolSeq == 2 || pCompiler->cchEscEolSeq == 3);
-    while (cchLeft >= 4)
-    {
-        /* First char. */
-        ch = pchWord[0];
-        if (KMK_CC_EVAL_IS_SPACE(ch))
-        { /* maybe likely */ }
-        else if (   ch == '\\'
-                 && pchWord[1] == pCompiler->chFirstEol)
-        {
-            pchWord += pCompiler->cchEscEolSeq;
-            cchLeft -= pCompiler->cchEscEolSeq;
-            pCompiler->iEscEol++;
-            continue;
-        }
-        else
-        {
-            *pcchLeft = cchLeft;
-            return pchWord;
-        }
-
-        /* Second char. */
-        ch = pchWord[1];
-        if (KMK_CC_EVAL_IS_SPACE(ch))
-        { /* maybe likely */ }
-        else if (   ch == '\\'
-                 && pchWord[2] == pCompiler->chFirstEol)
-        {
-            pchWord += 1 + pCompiler->cchEscEolSeq;
-            cchLeft -= 1 + pCompiler->cchEscEolSeq;
-            pCompiler->iEscEol++;
-            continue;
-        }
-        else
-        {
-            *pcchLeft = cchLeft - 1;
-            return pchWord + 1;
-        }
-
-        /* Third char. */
-        ch = pchWord[2];
-        if (KMK_CC_EVAL_IS_SPACE(ch))
-        { /* maybe likely */ }
-        else if (   ch == '\\'
-                 && pchWord[3] == pCompiler->chFirstEol
-                 && cchLeft >= 2 + pCompiler->cchEscEolSeq)
-        {
-            pchWord += 2 + pCompiler->cchEscEolSeq;
-            cchLeft -= 2 + pCompiler->cchEscEolSeq;
-            pCompiler->iEscEol++;
-            continue;
-        }
-        else
-        {
-            *pcchLeft = cchLeft - 2;
-            return pchWord + 2;
-        }
-
-        /* Third char. */
-        ch = pchWord[3];
-        if (KMK_CC_EVAL_IS_SPACE(ch))
-        {
-            pchWord += 4;
-            cchLeft -= 4;
-        }
-        else if (   ch == '\\'
-                 && cchLeft >= 3 + pCompiler->cchEscEolSeq
-                 && pchWord[4] == pCompiler->chFirstEol)
-        {
-            pchWord += 3 + pCompiler->cchEscEolSeq;
-            cchLeft -= 3 + pCompiler->cchEscEolSeq;
-            pCompiler->iEscEol++;
-        }
-        else
-        {
-            *pcchLeft = cchLeft - 3;
-            return pchWord + 3;
-        }
-    }
-
-    /*
-     * Simple loop for the final three chars.
-     */
-    while (cchLeft > 0)
-    {
-        /* First char. */
-        ch = *pchWord;
-        if (KMK_CC_EVAL_IS_SPACE(ch))
-        {
-            pchWord += 1;
-            cchLeft -= 1;
-        }
-        else if (   ch == '\\'
-                 && cchLeft > pCompiler->cchEolSeq
-                 && pchWord[1] == pCompiler->chFirstEol)
-        {
-            pchWord += pCompiler->cchEscEolSeq;
-            cchLeft -= pCompiler->cchEscEolSeq;
-            pCompiler->iEscEol++;
-        }
-        else
-            break;
-    }
-
-    *pcchLeft = cchLeft;
-    return pchWord;
-}
-
-
-#if 0
-/**
- * Used to shed trailing spaces in a string by decrementing @ a_cchLeft.
- *
- * @param   a_pCompiler     The compiler instance data.
- * @param   a_pchWord       The current input position, this will not be
- *                          changed.
- * @param   a_cchLeft       The number of chars left to parse.  This will be
- *                          updated if there are trailing spaces.
- */
-#define KMK_CC_EVAL_SHED_TRAILING_SPACES(a_pCompiler, a_pchWord, a_cchLeft) \
+    return kmk_cc_eval_skip_spaces_with_esc_eol(pchWord, cchLeft, pcchLeft, pCompiler);
+}
+
+
+/**
+ * Skip zero or more spaces.
+ *
+ * This macro deals with a single space, if there are more or we're hittin some
+ * possible escaped EOL sequence, work is deferred to a worker function.
+ *
+ * @param   a_pCompiler The compiler state.
+ * @param   a_pchWord   The current input position. Advanced past spaces.
+ * @param   a_cchLeft   The amount of input left to parse. Will be updated.
+ */
+#define KMK_CC_EVAL_SKIP_SPACES(a_pCompiler, a_pchWord, a_cchLeft) \
     do { \
         if ((a_cchLeft) > 0) \
         { \
-            char const chShedTrailing1 = (a_pchWord)[(a_cchLeft) - 1]; \
-            if (KMK_CC_EVAL_IS_SPACE(chShedTrailing1)) \
+            char chSkipSpaces = *(a_pchWord); \
+            if (KMK_CC_EVAL_IS_SPACE_OR_BACKSLASH(chSkipSpaces)) \
             { \
-                /* If there are two or more, or any of these are potential EOL chars, call worker. */ \
-                if (!KMK_CC_EVAL_IS_EOL_CANDIDATE(chShedTrailing1)) \
+                if (chSkipSpaces != '\\') \
                 { \
+                    (a_pchWord) += 1; \
                     (a_cchLeft) -= 1; \
-                    if ((a_cchLeft) > 0) \
-                    { \
-                        char const chShedTrailing2 = (a_pchWord)[(a_cchLeft) - 1]; \
-                        if (KMK_CC_EVAL_IS_SPACE(chShedTrailing)) \
-                            (a_cchLeft) = kmk_cc_eval_shed_trailing_spaces_slow(a_pchWord, (a_cchLeft), \
-                                                                                         chShedTrailing2, a_pCompiler); \
-                    } \
+                    chSkipSpaces = *(a_pchWord); \
+                    if (KMK_CC_EVAL_IS_SPACE_OR_BACKSLASH(chSkipSpaces)) \
+                        (a_pchWord) = kmk_cc_eval_skip_spaces_slow(a_pchWord, &(a_cchLeft), chSkipSpaces, a_pCompiler); \
                 } \
                 else \
-                    (a_cchWord) = kmk_cc_eval_shed_trailing_spaces_slow(a_pchWord, (a_cchLeft), \
-                                                                                 chShedTrailing1, a_pCompiler); \
+                    (a_pchWord) = kmk_cc_eval_skip_spaces_slow(a_pchWord, &(a_cchLeft), chSkipSpaces, a_pCompiler); \
             } \
         } \
@@ -3574,35 +3781,79 @@
 
 
-
-/**
- * The slow path of KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD.
- *
- * It's called when a space or backslash is encountered as the 2nd character.
- * A second space is likely to be followed by more spaces, while an escaped
- * newline (if it checks out) is very likely to be followed by more spaces or
- * tabs before we get to anything worthwhile.  Also, checking for an esacped
- * end-of-line character is a bit tedious. (It could be worse, though, as we
- * know that any EOL sequence inside the text area we're currently working on
- * must be escaped.)
+/**
+ * Worker for KMK_CC_EVAL_SKIP_SPACES.
  *
  * @returns Points to the first non-space character or end of input.
  * @param   pchWord             The current position. There is some kind of char
- * @param   cchLeft             The number of chars left to parse at @a pchWord.
- * @param   ch                  The current tail character.
+ * @param   pcchLeft            Pointer to the cchLeft variable, this is both
+ *                              input and output.
+ * @param   ch                  The current character.
  * @param   pCompiler           The compiler instance data.
  */
-static size_t kmk_cc_eval_shed_trailing_spaces_slow(const char *pchWord, size_t cchLeft, char ch,
-                                                    PKMKCCEVALCOMPILER pCompiler)
-{
-    for (;;)
-    {
-        if (KMK_CC_EVAL_IS_EOL_CANDIDATE(chShedTrailing1))
-        {
-
+static const char *kmk_cc_eval_skip_spaces_slow(const char *pchWord, size_t *pcchLeft, char ch, PKMKCCEVALCOMPILER pCompiler)
+{
+    size_t cchLeft = *pcchLeft;
+#ifdef KMK_CC_STRICT
+    size_t offWordCcStrict = pchWord - pCompiler->pszContent;
+#endif
+    KMK_CC_ASSERT(cchLeft > 0);
+    KMK_CC_ASSERT(cchLeft <= pCompiler->cchLine);
+    KMK_CC_ASSERT(*pchWord == ch);
+    KMK_CC_ASSERT(KMK_CC_EVAL_IS_SPACE_OR_BACKSLASH(ch));
+    KMK_CC_ASSERT(offWordCcStrict >= pCompiler->offLine);
+    KMK_CC_ASSERT(offWordCcStrict < pCompiler->offLine + pCompiler->cchLine);
+    KMK_CC_ASSERT(   pCompiler->iEscEol >= pCompiler->cEscEols
+                  || offWordCcStrict <= pCompiler->paEscEols[pCompiler->iEscEol].offEsc);
+    KMK_CC_ASSERT(   pCompiler->iEscEol >= pCompiler->cEscEols
+                  || pCompiler->iEscEol == 0
+                  || offWordCcStrict >= pCompiler->paEscEols[pCompiler->iEscEol - 1].offEol + pCompiler->cchEolSeq);
+
+    /*
+     * If we don't need to consider escaped EOLs, things are much much simpler.
+     */
+    if (pCompiler->iEscEol >= pCompiler->cEscEols)
+    {
+        if (ch != '\\')
+        {
+            pchWord++;
+            cchLeft--;
         }
-
-    }
-}
-#endif
+        else
+            return pchWord;
+        return kmk_cc_eval_skip_spaces_without_esc_eol(pchWord, cchLeft, pcchLeft, pCompiler);
+    }
+
+    /*
+     * Possible escaped EOL complications.
+     */
+    if (ch != '\\')
+    {
+        pchWord++;
+        cchLeft--;
+    }
+    else
+    {
+        size_t          cchSkip;
+        size_t          offWord;
+        unsigned        iEscEol = pCompiler->iEscEol;
+        if (iEscEol >= pCompiler->cEscEols)
+            return pchWord;
+
+        offWord = pchWord - pCompiler->pszContent;
+        if (offWord < pCompiler->paEscEols[iEscEol].offEsc)
+            return pchWord;
+        KMK_CC_ASSERT(offWord == pCompiler->paEscEols[iEscEol].offEsc);
+
+        cchSkip  = pCompiler->paEscEols[iEscEol].offEol + pCompiler->cchEolSeq - offWord;
+        pchWord += cchSkip;
+        cchLeft -= cchSkip;
+        pCompiler->iEscEol = ++iEscEol;
+
+        if (iEscEol < pCompiler->cEscEols)
+        { /* likely */ }
+        else return kmk_cc_eval_skip_spaces_without_esc_eol(pchWord, cchLeft, pcchLeft, pCompiler);
+    }
+    return kmk_cc_eval_skip_spaces_with_esc_eol(pchWord, cchLeft, pcchLeft, pCompiler);
+}
 
 
@@ -3647,85 +3898,81 @@
     {
         char ch = pszContent[off];
-        if (!KMK_CC_EVAL_IS_SPACE_OR_VAR_OR_RECIPE(ch))
+        if (!KMK_CC_EVAL_IS_SPACE_DOLLAR_OR_SLASH(ch))
+            off++;
+        else if (KMK_CC_EVAL_IS_SPACE(ch))
+            break;
+        else if (ch == '$')
+        {
+            off++;
+            if (off < offLineEnd)
+            {
+                char const chOpen = pszContent[off];
+                if (chOpen == '(' || chOpen == '{')
+                {
+                    /*
+                     * Got a $(VAR) or ${VAR} to deal with here.  This may
+                     * include nested variable references and span multiple
+                     * lines (at least for function calls).
+                     *
+                     * We scan forward till we've found the corresponding
+                     * closing parenthesis, considering any open parentheses
+                     * of the same kind as worth counting, even if there are
+                     * no dollar preceeding them, just like GNU make does.
+                     */
+                    size_t const offStart = off - 1;
+                    char const   chClose  = chOpen == '(' ? ')' : '}';
+                    unsigned     cOpen    = 1;
+                    off++;
+                    for (;;)
+                    {
+                        if (off < offLineEnd)
+                        {
+                            ch = pszContent[off];
+                            if (!(KMK_CC_EVAL_IS_PAREN_OR_SLASH(ch)))
+                                off++;
+                            else
+                            {
+                                off++;
+                                if (ch == chClose)
+                                {
+                                    if (--cOpen == 0)
+                                        break;
+                                }
+                                else if (ch == chOpen)
+                                    cOpen++;
+                                else if (   ch == '\\'
+                                         && iEscEol < pCompiler->cEscEols
+                                         && off == pCompiler->paEscEols[iEscEol].offEsc)
+                                {
+                                    off = pCompiler->paEscEols[iEscEol].offEol + pCompiler->cchEolSeq;
+                                    pCompiler->iEscEol = ++iEscEol;
+                                }
+                            }
+                        }
+                        else if (cOpen == 1)
+                            kmk_cc_eval_fatal(pCompiler, &pszContent[offStart],
+                                              "Variable reference is missing '%c'", chClose);
+                        else
+                            kmk_cc_eval_fatal(pCompiler, &pszContent[offStart],
+                                              "%u variable references are missing '%c'", cOpen, chClose);
+                    }
+                }
+                /* Single char variable name. */
+                else if (!KMK_CC_EVAL_IS_SPACE(chOpen))
+                {  /* likely */ }
+                else
+                    kmk_cc_eval_fatal(pCompiler, &pszContent[off], "Expected variable name after '$', not end of line");
+            }
+            else
+                kmk_cc_eval_fatal(pCompiler, &pszContent[off], "Expected variable name after '$', not end of line");
+            fPlain = 0;
+        }
+        /* Deal with potential escaped EOL. */
+        else if (   ch != '\\'
+                 || iEscEol >= pCompiler->cEscEols
+                 || off != pCompiler->paEscEols[iEscEol].offEsc )
             off++;
         else
-        {
-            if (KMK_CC_EVAL_IS_SPACE(ch))
-                break;
-
-            if (ch == '$')
-            {
-                off++;
-                if (off < offLineEnd)
-                {
-                    char const chOpen = pszContent[off];
-                    if (chOpen == '(' || chOpen == '{')
-                    {
-                        /*
-                         * Got a $(VAR) or ${VAR} to deal with here.  This may
-                         * include nested variable references and span multiple
-                         * lines (at least for function calls).
-                         *
-                         * We scan forward till we've found the corresponding
-                         * closing parenthesis, considering any open parentheses
-                         * of the same kind as worth counting, even if there are
-                         * no dollar preceeding them, just like GNU make does.
-                         */
-                        size_t const offStart = off - 1;
-                        char const   chClose  = chOpen == '(' ? ')' : '}';
-                        unsigned     cOpen    = 1;
-                        for (;;)
-                        {
-                            if (off < offLineEnd)
-                            {
-                                ch = pszContent[off];
-                                if (!(KMK_CC_EVAL_IS_PAREN_OR_SLASH(ch)))
-                                    off++;
-                                else
-                                {
-                                    off++;
-                                    if (ch == chClose)
-                                    {
-                                        if (--cOpen == 0)
-                                            break;
-                                    }
-                                    else if (ch == chOpen)
-                                        cOpen++;
-                                    else if (   ch == '\\'
-                                             && iEscEol < pCompiler->cEscEols
-                                             && off == pCompiler->paEscEols[iEscEol].offEsc)
-                                    {
-                                        off = pCompiler->paEscEols[iEscEol].offEsc + pCompiler->cchEolSeq;
-                                        iEscEol++;
-                                        pCompiler->iEscEol = iEscEol;
-                                    }
-                                }
-                            }
-                            else if (cOpen == 1)
-                                kmk_cc_eval_fatal(pCompiler, &pszContent[offStart],
-                                                  "Variable reference is missing '%c'", chClose);
-                            else
-                                kmk_cc_eval_fatal(pCompiler, &pszContent[offStart],
-                                                  "%u variable references are missing '%c'", cOpen, chClose);
-                        }
-                    }
-                    /* Single char variable name. */
-                    else if (!KMK_CC_EVAL_IS_SPACE(chOpen))
-                    {  /* likely */ }
-                    else
-                        kmk_cc_eval_fatal(pCompiler, &pszContent[off], "Expected variable name after '$', not end of line");
-                }
-                else
-                    kmk_cc_eval_fatal(pCompiler, &pszContent[off], "Expected variable name after '$', not end of line");
-                fPlain = 0;
-            }
-            /* Deal with potential escaped EOL. */
-            else if (   ch != '\\'
-                     || iEscEol >= pCompiler->cEscEols
-                     || off != pCompiler->paEscEols[iEscEol].offEsc )
-                off++;
-            else
-                break;
-        }
+            break;
     }
 
@@ -3858,4 +4105,135 @@
 }
 #endif /* unused atm */
+
+
+/**
+ * Helper for ensuring that we've got sufficient number of words allocated.
+ */
+#define KMK_CC_EVAL_ENSURE_WORDS(a_pCompiler, a_cRequiredWords) \
+    do { \
+        if ((a_cRequiredWords) < (a_pCompiler)->cWordsAllocated) \
+        { /* likely */ } \
+        else \
+        { \
+            unsigned cEnsureWords = ((a_cRequiredWords) + 3 /*15*/) & ~(unsigned)3/*15*/; \
+            KMK_CC_ASSERT((a_cRequiredWords) < 0x8000); \
+            (a_pCompiler)->paWords = (PKMKCCEVALWORD)xmalloc(cEnsureWords * sizeof((a_pCompiler)->paWords)[0]); \
+        } \
+    } while (0)
+
+/**
+ * Parses the remainder of the line into simple words.
+ *
+ * The resulting words are classified as either kKmkCcEvalToken_WordPlain or
+ * kKmkCcEvalToken_WordWithDollar.
+ *
+ * @returns Number of words.
+ * @param   pCompiler   The compiler state.
+ * @param   pchWord     Where to start, we expect this to be at a word.
+ * @param   cchLeft     The number of chars left to parse on this line.
+ *                      This is expected to be non-zero.
+ */
+static unsigned kmk_cc_eval_parse_words(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft)
+{
+    unsigned iEscEol  = pCompiler->iEscEol;
+    unsigned cEscEols = pCompiler->cEscEols;
+    unsigned cWords   = 0;
+
+    /* Precoditions. */
+    KMK_CC_ASSERT(cchLeft > 0);
+    KMK_CC_ASSERT(!KMK_CC_EVAL_IS_SPACE(*pchWord));
+
+    /*
+     * If we don't have to deal with escaped EOLs, the find-end-of word search
+     * becomes a little bit simpler.  Since this function will be used a lot
+     * for simple lines with single words, this could maybe save a nano second
+     * or two.
+     */
+    if (iEscEol >= cEscEols)
+    {
+        do
+        {
+            size_t          cchSkipAfter = 0;
+            size_t          cchWord      = 1;
+            KMKCCEVALTOKEN  enmToken     = kKmkCcEvalToken_WordPlain;
+
+            /* Find the end of the current word. */
+            while (cchWord < cchLeft)
+            {
+                char ch = pchWord[cchWord];
+                if (!KMK_CC_EVAL_IS_SPACE_OR_DOLLAR(ch))
+                { /* likely */ }
+                else if (ch == '$')
+                    enmToken = kKmkCcEvalToken_WordWithDollar;
+                else
+                    break;
+                cchWord++;
+            }
+
+            /* Add the word. */
+            KMK_CC_EVAL_ENSURE_WORDS(pCompiler, cWords + 1);
+            pCompiler->paWords[cWords].pchWord  = pchWord;
+            pCompiler->paWords[cWords].cchWord  = cchWord;
+            pCompiler->paWords[cWords].enmToken = enmToken;
+            cWords++;
+
+            /* Skip the work and any trailing blanks. */
+            cchWord += cchSkipAfter;
+            pchWord += cchWord;
+            cchLeft -= cchWord;
+            KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
+        } while (cchLeft > 0);
+    }
+    /*
+     * Have to deal with escaped EOLs.
+     */
+    else
+    {
+        const char *pszContent = pCompiler->pszContent;
+        do
+        {
+            size_t          cchSkipAfter = 0;
+            size_t          cchWord      = 1;
+            KMKCCEVALTOKEN  enmToken     = kKmkCcEvalToken_WordPlain;
+
+            /* Find the end of the current word. */
+            while (cchWord < cchLeft)
+            {
+                char ch = pchWord[cchWord];
+                if (!KMK_CC_EVAL_IS_SPACE_DOLLAR_OR_SLASH(ch))
+                { /* likely */ }
+                else if (ch == '$')
+                    enmToken = kKmkCcEvalToken_WordWithDollar;
+                else if (ch != '\\')
+                    break;
+                else if ((size_t)(&pchWord[cchWord] - pszContent) == pCompiler->paEscEols[iEscEol].offEsc)
+                {
+                    cchSkipAfter = pCompiler->paEscEols[iEscEol].offEol - pCompiler->paEscEols[iEscEol].offEsc
+                                 + pCompiler->cchEolSeq;
+                    iEscEol++;
+                    break;
+                }
+                cchWord++;
+            }
+
+            /* Add the word. */
+            KMK_CC_EVAL_ENSURE_WORDS(pCompiler, cWords + 1);
+            pCompiler->paWords[cWords].pchWord  = pchWord;
+            pCompiler->paWords[cWords].cchWord  = cchWord;
+            pCompiler->paWords[cWords].enmToken = enmToken;
+            cWords++;
+
+            /* Skip the work and any trailing blanks. */
+            cchWord += cchSkipAfter;
+            pchWord += cchWord;
+            cchLeft -= cchWord;
+            KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
+        } while (cchLeft > 0);
+    }
+    pCompiler->cWords = cWords;
+    return cWords;
+}
+
+
 
 
@@ -4154,8 +4532,6 @@
  * @param   cchLeft                 The number of chars left on the current line
  *                                  starting at @a pchWord.
- * @param   fStripTrailingSpaces    Whether to strip trailing spaces.
- */
-static size_t kmk_cc_eval_prep_normal_line(PKMKCCEVALCOMPILER pCompiler, const char * const pchWord, size_t cchLeft,
-                                           int fStripTrailingSpaces)
+ */
+static size_t kmk_cc_eval_prep_normal_line(PKMKCCEVALCOMPILER pCompiler, const char * const pchWord, size_t cchLeft)
 {
     size_t          cchRet;
@@ -4174,7 +4550,4 @@
     {
         cchRet = cchLeft;
-        if (fStripTrailingSpaces)
-            while (cchRet > 0 && KMK_CC_IS_SPACE_CH(pchWord[cchRet - 1]))
-                cchRet--;
 
         KMK_CC_EVAL_ENSURE_STRCOPY_SEGS(pCompiler, 1);
@@ -4203,5 +4576,5 @@
         KMK_CC_ASSERT(offWord + cchLeft <= pCompiler->cchContent);
         KMK_CC_ASSERT(offWord <  pCompiler->paEscEols[iEscEol].offEsc);
-        KMK_CC_ASSERT(offWord >= (iEscEol ? pCompiler->paEscEols[cEscEols - 1].offEol + pCompiler->cchEolSeq : pCompiler->offLine));
+        KMK_CC_ASSERT(offWord >= (iEscEol ? pCompiler->paEscEols[iEscEol - 1].offEol + pCompiler->cchEolSeq : pCompiler->offLine));
 
         /* Make sure we've got more than enough segments to fill in. */
@@ -4221,4 +4594,5 @@
         pCompiler->paStrCopySegs[cSegs].cchSrcAndPrependSpace = cchRet;
         pCompiler->paStrCopySegs[cSegs].pchSrc                = pchWord;
+        cSegs++;
 
         offWord = pCompiler->paEscEols[iEscEol].offEol + pCompiler->cchEolSeq;
@@ -4263,7 +4637,4 @@
         if (offWord < offEsc)
         {
-            if (fStripTrailingSpaces)
-                while (KMK_CC_EVAL_IS_SPACE(pszContent[offEsc - 1]))
-                    offEsc--;
             cchSeg = offEsc - offWord;
             pCompiler->paStrCopySegs[cSegs].cchSrcAndPrependSpace = fPendingSpace ? -(ssize_t)cchSeg : (ssize_t)cchSeg;
@@ -4331,10 +4702,12 @@
     if (cchLeft)
     {
-        size_t           cchExpr = kmk_cc_eval_prep_normal_line(pCompiler, pchWord, cchLeft, 1 /*fStripTrailingSpaces*/);
-        PKMKCCEVALIFEXPR pInstr  = (PKMKCCEVALIFEXPR)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail,
-                                                                             KMKCCEVALIFEXPR_SIZE(cchExpr));
+        PKMKCCEVALIFEXPR pInstr;
+        size_t           cchExpr = kmk_cc_eval_prep_normal_line(pCompiler, pchWord, cchLeft);
+        kmk_cc_eval_strip_right_v(pCompiler->paStrCopySegs, &pCompiler->cStrCopySegs, &cchExpr);
+
+        pInstr = (PKMKCCEVALIFEXPR)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, KMKCCEVALIFEXPR_SIZE(cchExpr));
         kmk_cc_eval_strcpyv(pInstr->szExpr, pCompiler->paStrCopySegs, pCompiler->cStrCopySegs, cchExpr);
         pInstr->cchExpr = cchExpr;
-        pInstr->IfCore.Core.enmOpCode = kKmkCcEvalInstr_if;
+        pInstr->IfCore.Core.enmOpcode = kKmkCcEvalInstr_if;
         pInstr->IfCore.Core.iLine     = pCompiler->iLine;
         kmk_cc_eval_do_if_core(pCompiler, &pInstr->IfCore, fInElse);
@@ -4369,4 +4742,8 @@
         const char * const  pchVarNm     = pchWord;
         int                 fPlain;
+/** @todo this isn't quite right. It is a variable name, correct. However, it
+ *        doesn't need to subscribe entirely to the rules of a variable name.
+ *        Just find the end of the word, taking variable refs into account,
+ *        and consider it what we need. */
         pchWord = kmk_cc_eval_skip_var_name(pCompiler, pchWord, cchLeft, &cchLeft, &fPlain);
         KMK_CC_ASSERT(pCompiler->iEscEol == iSavedEscEol || !fPlain);
@@ -4376,5 +4753,5 @@
             PKMKCCEVALIFDEFPLAIN    pInstr;
             pInstr = (PKMKCCEVALIFDEFPLAIN)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, sizeof(*pInstr));
-            pInstr->IfCore.Core.enmOpCode = fPositiveStmt ? kKmkCcEvalInstr_ifdef_plain : kKmkCcEvalInstr_ifndef_plain;
+            pInstr->IfCore.Core.enmOpcode = fPositiveStmt ? kKmkCcEvalInstr_ifdef_plain : kKmkCcEvalInstr_ifndef_plain;
             pInstr->IfCore.Core.iLine     = pCompiler->iLine;
             pInstr->pszName = strcache2_add(&variable_strcache, pchVarNm, cchVarNm);
@@ -4388,5 +4765,5 @@
             char                   *pszCopy;
             pCompiler->iEscEol = iSavedEscEol;
-            cchCopy = kmk_cc_eval_prep_normal_line(pCompiler, pchVarNm, cchVarNm, 0 /*fStripTrailingSpaces*/);
+            cchCopy = kmk_cc_eval_prep_normal_line(pCompiler, pchVarNm, cchVarNm);
 
             pInstr = (PKMKCCEVALIFDEFDYNAMIC)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, sizeof(*pInstr));
@@ -4396,5 +4773,5 @@
             kmk_cc_block_realign(pCompiler->ppBlockTail);
 
-            pInstr->IfCore.Core.enmOpCode = fPositiveStmt ? kKmkCcEvalInstr_ifdef_dynamic : kKmkCcEvalInstr_ifndef_dynamic;
+            pInstr->IfCore.Core.enmOpcode = fPositiveStmt ? kKmkCcEvalInstr_ifdef_dynamic : kKmkCcEvalInstr_ifndef_dynamic;
             pInstr->IfCore.Core.iLine     = pCompiler->iLine;
             kmk_cc_eval_compile_string_exp_subprog(pCompiler, pszCopy, cchCopy, &pInstr->NameSubprog);
@@ -4598,8 +4975,8 @@
          * Initialize the instruction.
          */
-        pInstr->IfCore.Core.enmOpCode = fPositiveStmt ? kKmkCcEvalInstr_ifeq : kKmkCcEvalInstr_ifneq;
+        pInstr->IfCore.Core.enmOpcode = fPositiveStmt ? kKmkCcEvalInstr_ifeq : kKmkCcEvalInstr_ifneq;
         pInstr->IfCore.Core.iLine     = pCompiler->iLine;
-        kmk_cc_eval_do_subprogram_or_plain(pCompiler, &pInstr->Left, Left.pszCopy, Left.cchCopy, Left.fPlain);
-        kmk_cc_eval_do_subprogram_or_plain(pCompiler, &pInstr->Right, Right.pszCopy, Right.cchCopy, Right.fPlain);
+        kmk_cc_eval_init_subprogram_or_plain(pCompiler, &pInstr->Left, Left.pszCopy, Left.cchCopy, Left.fPlain);
+        kmk_cc_eval_init_subprogram_or_plain(pCompiler, &pInstr->Right, Right.pszCopy, Right.cchCopy, Right.fPlain);
         kmk_cc_eval_do_if_core(pCompiler, &pInstr->IfCore, fInElse);
 
@@ -4739,8 +5116,8 @@
          * Initialize the instruction.
          */
-        pInstr->IfCore.Core.enmOpCode = fPositiveStmt ? kKmkCcEvalInstr_if1of : kKmkCcEvalInstr_ifn1of;
+        pInstr->IfCore.Core.enmOpcode = fPositiveStmt ? kKmkCcEvalInstr_if1of : kKmkCcEvalInstr_ifn1of;
         pInstr->IfCore.Core.iLine     = pCompiler->iLine;
-        kmk_cc_eval_do_subprogram_or_plain(pCompiler, &pInstr->Left, Left.pszCopy, Left.cchCopy, Left.fPlain);
-        kmk_cc_eval_do_subprogram_or_plain(pCompiler, &pInstr->Right, Right.pszCopy, Right.cchCopy, Right.fPlain);
+        kmk_cc_eval_init_subprogram_or_plain(pCompiler, &pInstr->Left, Left.pszCopy, Left.cchCopy, Left.fPlain);
+        kmk_cc_eval_init_subprogram_or_plain(pCompiler, &pInstr->Right, Right.pszCopy, Right.cchCopy, Right.fPlain);
         kmk_cc_eval_do_if_core(pCompiler, &pInstr->IfCore, fInElse);
 
@@ -4780,5 +5157,5 @@
             /* Emit a jump instruction that will take us from the 'True' block to the 'endif'. */
             PKMKCCEVALJUMP  pInstr = (PKMKCCEVALJUMP)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, sizeof(*pInstr));
-            pInstr->Core.enmOpCode = kKmkCcEvalInstr_jump;
+            pInstr->Core.enmOpcode = kKmkCcEvalInstr_jump;
             pInstr->Core.iLine     = pCompiler->iLine;
             pInstr->pNext          = NULL;
@@ -4896,6 +5273,41 @@
 
 
-static int kmk_cc_eval_do_include(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, KMKCCEVALINSTR enmInst)
-{
+/**
+ * Parses a 'include file...', 'sinclude file...', '-include file...',
+ * 'includedep file...', 'includedep-queue file...' and
+ * 'includedep-flush file...'
+ *
+ * @returns 1 to indicate we've handled a keyword (see
+ *          kmk_cc_eval_try_handle_keyword).
+ * @param   pCompiler   The compiler state.
+ * @param   pchWord     First char after the include directive.
+ * @param   cchLeft     The number of chars left to parse on this line.
+ * @param   enmOpcode   The opcode for the include directive we're parsing.
+ */
+static int kmk_cc_eval_do_include(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, KMKCCEVALINSTR enmOpcode)
+{
+    KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
+    if (cchLeft)
+    {
+        /*
+         * Split what's left up into words.
+         */
+        unsigned cWords = kmk_cc_eval_parse_words(pCompiler, pchWord, cchLeft);
+        KMK_CC_EVAL_DPRINTF(("%s: cWords=%d\n", g_apszEvalInstrNms[enmOpcode], cWords));
+        if (cWords)
+        {
+            PKMKCCEVALINCLUDE pInstr = (PKMKCCEVALINCLUDE)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail,
+                                                                                  KMKCCEVALINCLUDE_SIZE(cWords));
+            pInstr->Core.enmOpcode = enmOpcode;
+            pInstr->Core.iLine     = 0;
+            pInstr->cFiles         = cWords;
+            kmk_cc_eval_init_spp_array_from_duplicated_words(pCompiler, cWords, pCompiler->paWords, pInstr->aFiles);
+            kmk_cc_block_realign(pCompiler->ppBlockTail);
+        }
+        else
+            KMK_CC_ASSERT(0);
+    }
+    else
+        KMK_CC_EVAL_DPRINTF(("%s: include without args\n", g_apszEvalInstrNms[enmOpcode]));
     return 1;
 }
@@ -4904,4 +5316,5 @@
 static int kmk_cc_eval_do_vpath(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft)
 {
+    kmk_cc_eval_fatal(pCompiler, NULL, "vpath directive is not implemented\n");
     return 1;
 }
@@ -4910,5 +5323,21 @@
 static void kmk_cc_eval_handle_command(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft)
 {
-
+    kmk_cc_eval_fatal(pCompiler, pchWord, "command handling not implemented yet");
+}
+
+
+static int kmk_cc_eval_handle_recipe_cont_colon(PKMKCCEVALCOMPILER pCompiler, const char *pchWord0, size_t cchWord0,
+                                                const char *pchColon, size_t cchLeft, unsigned fQualifiers)
+{
+    kmk_cc_eval_fatal(pCompiler, pchWord0, "recipe handling not implemented yet");
+    return 1;
+}
+
+
+static int kmk_cc_eval_handle_recipe_cont_2nd_word(PKMKCCEVALCOMPILER pCompiler, const char *pchWord0, size_t cchWord0,
+                                                   const char *pchWord, size_t cchLeft, unsigned fQualifiers)
+{
+    kmk_cc_eval_fatal(pCompiler, pchWord, "recipe handling not implemented yet");
+    return 1;
 }
 
@@ -4916,5 +5345,13 @@
 static void kmk_cc_eval_handle_recipe(PKMKCCEVALCOMPILER pCompiler, const char *pszEqual, const char *pchWord, size_t cchLeft)
 {
-
+    kmk_cc_eval_fatal(pCompiler, pchWord, "recipe handling not implemented yet");
+}
+
+static void kmk_cc_eval_end_of_recipe(PKMKCCEVALCOMPILER pCompiler)
+{
+    if (pCompiler->pRecipe)
+    {
+        /** @todo do stuff here. */
+    }
 }
 
@@ -4933,8 +5370,7 @@
  * @param   fQualifiers The qualifiers.
  */
-static int kmk_cc_eval_do_var_undefine(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft,
-                                                unsigned fQualifiers)
-{
-
+static int kmk_cc_eval_do_var_undefine(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, unsigned fQualifiers)
+{
+    kmk_cc_eval_fatal(pCompiler, pchWord, "undefine handling not implemented yet");
     return 1;
 }
@@ -4958,12 +5394,263 @@
 {
 
+    KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
+    kmk_cc_eval_fatal(pCompiler, pchWord, "define handling not implemented yet");
     return 1;
 }
 
 
-static int kmk_cc_eval_try_handle_assignment(PKMKCCEVALCOMPILER pCompiler, const char *pchTmp,
-                                             const char *pchWord, size_t cchLeft, unsigned fQualifiers)
-{
-    return 0;
+static int kmk_cc_eval_handle_assignment_or_recipe(PKMKCCEVALCOMPILER pCompiler, const char *pchTmp,
+                                                   const char *pchWord, size_t cchLeft, unsigned fQualifiers)
+{
+    /*
+     * We're currently at a word which may or may not be a variable name
+     * followed by an assignment operator, alternatively it must be a recipe.
+     * We need to figure this out and deal with it in the most efficient
+     * manner as this is a very common occurence.
+     */
+    unsigned const  iEscEolVarNm = pCompiler->iEscEol;
+    int             fPlainVarNm  = 1;
+    const char     *pchVarNm     = pchWord;
+    size_t          cchVarNm;
+    size_t          cch = 0;
+    char            ch;
+
+    /*
+     * The variable name.  Complicate by there being no requirement of a space
+     * preceeding the assignment operator, as well as that the variable name
+     * may include variable references with spaces (function++) in them.
+     */
+    for (;;)
+    {
+        if (cch < cchLeft)
+        { /*likely*/ }
+        else
+            kmk_cc_eval_fatal(pCompiler, &pchWord[cch], "Neither recipe nor variable assignment");
+
+        ch = pchWord[cch];
+        if (!KMK_CC_EVAL_IS_SPACE_DOLLAR_SLASH_OR_ASSIGN(ch))
+            cch++;
+        /* Space? */
+        else if (KMK_CC_EVAL_IS_SPACE(ch))
+        {
+            cchVarNm = cch;
+            pchWord += cch;
+            cchLeft -= cch;
+            KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
+            break;
+        }
+        /* Variable expansion may contain spaces, so handle specially. */
+        else if (ch == '$')
+        {
+            cch++;
+            if (cch < cchLeft)
+            {
+                char const chOpen = pchWord[cch];
+                if (chOpen == '(' || chOpen == '{')
+                {
+                    /*
+                     * Got a $(VAR) or ${VAR} to deal with here.  This may
+                     * include nested variable references and span multiple
+                     * lines (at least for function calls).
+                     *
+                     * We scan forward till we've found the corresponding
+                     * closing parenthesis, considering any open parentheses
+                     * of the same kind as worth counting, even if there are
+                     * no dollar preceeding them, just like GNU make does.
+                     */
+                    size_t const cchStart = cch - 1;
+                    char const   chClose  = chOpen == '(' ? ')' : '}';
+                    unsigned     cOpen    = 1;
+                    cch++;
+                    for (;;)
+                    {
+                        if (cch < cchLeft)
+                        {
+                            ch = pchWord[cch];
+                            if (!(KMK_CC_EVAL_IS_PAREN_OR_SLASH(ch)))
+                                cch++;
+                            else
+                            {
+                                cch++;
+                                if (ch == chClose)
+                                {
+                                    if (--cOpen == 0)
+                                        break;
+                                }
+                                else if (ch == chOpen)
+                                    cOpen++;
+                                else if (   ch == '\\'
+                                         && pCompiler->iEscEol < pCompiler->cEscEols
+                                         &&    (size_t)(&pchWord[cch] - pCompiler->pszContent)
+                                            == pCompiler->paEscEols[pCompiler->iEscEol].offEsc)
+                                {
+                                    cch += pCompiler->paEscEols[pCompiler->iEscEol].offEol
+                                         - pCompiler->paEscEols[pCompiler->iEscEol].offEsc
+                                         + pCompiler->cchEolSeq;
+                                    pCompiler->iEscEol++;
+                                }
+                            }
+                        }
+                        else if (cOpen == 1)
+                            kmk_cc_eval_fatal(pCompiler, &pchWord[cchStart], "Variable reference is missing '%c'", chClose);
+                        else
+                            kmk_cc_eval_fatal(pCompiler, &pchWord[cchStart],
+                                              "%u variable references are missing '%c'", cOpen, chClose);
+                    }
+                }
+                /* Single char variable name. */
+                else if (!KMK_CC_EVAL_IS_SPACE(chOpen))
+                {  /* likely */ }
+                else
+                    kmk_cc_eval_fatal(pCompiler, &pchWord[cch], "Expected variable name after '$', not end of line");
+            }
+            else
+                kmk_cc_eval_fatal(pCompiler, &pchWord[cch], "Neither recipe nor variable assignment");
+            fPlainVarNm = 0;
+        }
+        /* Check out potential recipe. */
+        else if (ch == ':')
+        {
+            if (   cch + 1 < cchLeft
+                && pchWord[cch + 1] != '=')
+            {
+                cchVarNm = cch;
+                pchWord += cch;
+                cchLeft -= cch;
+                break;
+            }
+#ifdef HAVE_DOS_PATHS
+            /* Don't confuse the first colon in:
+                    C:/Windows/System32/Kernel32.dll: C:/Windows/System32/NtDll.dll
+               for a recipe, it is only the second one which counts. */
+            else if (   cch == 1
+                     && isalpha((unsigned char)pchWord[0]))
+                cch++;
+#endif
+            else
+                return kmk_cc_eval_handle_recipe_cont_colon(pCompiler, pchWord, cch, pchWord + cch, cchLeft - cch, fQualifiers);
+        }
+        /* Check out assignment operator. */
+        else if (ch == '=')
+        {
+            if (cch)
+            {
+                char chPrev = pchWord[cch - 1];
+                if (chPrev == ':' || chPrev == '+' || chPrev == '?' || chPrev == '<')
+                    cch--;
+                cchVarNm = cch;
+                pchWord += cch;
+                cchLeft -= cch;
+                break;
+            }
+            else
+                kmk_cc_eval_fatal(pCompiler, pchWord, "Empty variable name.");
+        }
+        /* Check out potential escaped EOL sequence. */
+        else if (ch == '\\')
+        {
+            unsigned const iEscEol = pCompiler->iEscEol;
+            if (iEscEol >= pCompiler->cEscEols)
+                cch++;
+            else
+            {
+                size_t offCur = &pchWord[cch] - pCompiler->pszContent;
+                if (offCur < pCompiler->paEscEols[iEscEol].offEol)
+                    cch++;
+                else
+                {
+                    cchVarNm = cch;
+                    KMK_CC_ASSERT(offCur == pCompiler->paEscEols[iEscEol].offEol);
+                    cch = pCompiler->paEscEols[iEscEol].offEol + pCompiler->cchEolSeq - offCur;
+                    pCompiler->iEscEol = iEscEol + 1;
+                    pchWord += cch;
+                    cchLeft -= cch;
+                    KMK_CC_EVAL_SKIP_SPACES(pCompiler, pchWord, cchLeft);
+                    break;
+                }
+            }
+        }
+        else
+            KMK_CC_ASSERT(0);
+    }
+
+    /*
+     * Check for assignment operator.
+     */
+    if (cchLeft)
+    {
+        size_t              cchValue;
+        PKMKCCEVALASSIGN    pInstr;
+        KMKCCEVALINSTR      enmOpCode;
+        int                 fPlainValue;
+        char               *pszValue;
+
+        ch = *pchWord;
+        if (ch == '=')
+        {
+            enmOpCode = kKmkCcEvalInstr_assign_recursive;
+            pchWord++;
+            cchLeft--;
+        }
+        else if (cchLeft >= 2 && pchWord[1] == '=')
+        {
+            if (ch == ':')
+                enmOpCode = kKmkCcEvalInstr_assign_simple;
+            else if (ch == '+')
+                enmOpCode = kKmkCcEvalInstr_assign_append;
+            else if (ch == '<')
+                enmOpCode = kKmkCcEvalInstr_assign_prepend;
+            else if (ch == '?')
+                enmOpCode = kKmkCcEvalInstr_assign_if_new;
+            else
+                return kmk_cc_eval_handle_recipe_cont_2nd_word(pCompiler, pchVarNm, cchVarNm, pchWord, cchLeft, fQualifiers);
+            pchWord += 2;
+            cchLeft -= 2;
+        }
+        else
+            return kmk_cc_eval_handle_recipe_cont_2nd_word(pCompiler, pchVarNm, cchVarNm, pchWord, cchLeft, fQualifiers);
+
+        /*
+         * Skip leading spaces, if any and prep the value for copying.
+         */
+        KMK_CC_EVAL_SKIP_SPACES(pCompiler, pchWord, cchLeft);
+        cchValue    = kmk_cc_eval_prep_normal_line(pCompiler, pchWord, cchLeft);
+        fPlainValue = memchr(pchWord, '$', cchLeft) == NULL;
+
+        /*
+         * Emit the instruction.
+         */
+        kmk_cc_eval_end_of_recipe(pCompiler);
+
+        pInstr = (PKMKCCEVALASSIGN)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, sizeof(*pInstr));
+        pInstr->Core.enmOpcode = enmOpCode;
+        pInstr->Core.iLine     = pCompiler->iLine;
+        pInstr->fExport        = (fQualifiers & KMK_CC_EVAL_QUALIFIER_EXPORT)   != 0;
+        pInstr->fOverride      = (fQualifiers & KMK_CC_EVAL_QUALIFIER_OVERRIDE) != 0;
+        pInstr->fPrivate       = (fQualifiers & KMK_CC_EVAL_QUALIFIER_PRIVATE)  != 0;
+        pInstr->fLocal         = (fQualifiers & KMK_CC_EVAL_QUALIFIER_LOCAL)    != 0;
+
+        /* We copy the value before messing around with the variable name since
+           we have to do more iEolEsc saves & restores the other way around. */
+        pszValue = kmk_cc_eval_strdup_prepped(pCompiler, cchValue);
+        if (fPlainVarNm)
+            pchVarNm = strcache2_add(&variable_strcache, pchVarNm, cchVarNm);
+        else
+        {
+            pCompiler->iEscEol = iEscEolVarNm;
+            cchVarNm = kmk_cc_eval_prep_normal_line_ex(pCompiler, pchVarNm, cchVarNm);
+            pchVarNm = kmk_cc_eval_strdup_prepped(pCompiler, cchVarNm);
+        }
+        kmk_cc_block_realign(pCompiler->ppBlockTail);
+        KMK_CC_EVAL_DPRINTF(("%s: '%s' '%s'\n", g_apszEvalInstrNms[enmOpCode], pchVarNm, pszValue));
+
+        kmk_cc_eval_init_subprogram_or_plain(pCompiler, &pInstr->Variable, pchVarNm, cchVarNm, fPlainVarNm);
+        kmk_cc_eval_init_subprogram_or_plain(pCompiler, &pInstr->Value, pszValue, cchValue, fPlainValue);
+
+        pInstr->pNext = (PKMKCCEVALCORE)kmk_cc_block_get_next_ptr(*pCompiler->ppBlockTail);
+    }
+    else
+        kmk_cc_eval_fatal(pCompiler, pchWord, "Neither recipe nor variable assignment");
+    return 1;
 }
 
@@ -4974,4 +5661,34 @@
  * The 'local' directive must be first and it does not permit any qualifiers at
  * the moment.  Should any be added later, they will have to come after 'local'.
+ *
+ * @returns 1 to indicate we've handled a keyword (see
+ *          kmk_cc_eval_try_handle_keyword).
+ * @param   pCompiler   The compiler state.
+ * @param   pchWord     First char after 'local'.
+ * @param   cchLeft     The number of chars left to parse on this line.
+ * @param   fQualifiers The qualifiers.
+ */
+static int kmk_cc_eval_do_var_local(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft)
+{
+    KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
+    if (cchLeft)
+    {
+        /*
+         * Find the end of the variable name.
+         */
+
+    }
+    else
+        kmk_cc_eval_fatal(pCompiler, pchWord, "Expected variable name, assignment operator and value after 'local'");
+    return 1;
+}
+
+
+/**
+ * Parses 'export [variable]' and 'export [qualifiers] variable = value'
+ * expressions.
+ *
+ * When we find the 'export' directive at the start of a line, we need to
+ * continue parsing with till we can tell the difference between the two forms.
  *
  * @returns 1 to indicate we've handled a keyword (see
@@ -4982,28 +5699,7 @@
  * @param   fQualifiers The qualifiers.
  */
-static int kmk_cc_eval_do_var_local(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft)
-{
-
-    return 1;
-}
-
-
-/**
- * Parses 'export [variable]' and 'export [qualifiers] variable = value'
- * expressions.
- *
- * When we find the 'export' directive at the start of a line, we need to
- * continue parsing with till we can tell the difference between the two forms.
- *
- * @returns 1 to indicate we've handled a keyword (see
- *          kmk_cc_eval_try_handle_keyword).
- * @param   pCompiler   The compiler state.
- * @param   pchWord     First char after 'define'.
- * @param   cchLeft     The number of chars left to parse on this line.
- * @param   fQualifiers The qualifiers.
- */
 static int kmk_cc_eval_handle_var_export(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, unsigned fQualifiers)
 {
-
+    kmk_cc_eval_fatal(pCompiler, pchWord, "export not implemented yet");
     return 1;
 }
@@ -5034,14 +5730,24 @@
                     return kmk_cc_eval_do_var_define(pCompiler, pchWord + 6, cchLeft - 6, fQualifiers);
 
-                if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "export", 6))   /* special */
-                    return kmk_cc_eval_handle_var_export(pCompiler, pchWord + 6, cchLeft - 6, fQualifiers);
-
                 if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "undefine", 8)) /* final */
                     return kmk_cc_eval_do_var_undefine(pCompiler, pchWord + 6, cchLeft - 6, fQualifiers);
 
+                if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "export", 6))
+                {
+                    if (!(fQualifiers & KMK_CC_EVAL_QUALIFIER_EXPORT))
+                        fQualifiers |= KMK_CC_EVAL_QUALIFIER_EXPORT;
+                    else
+                        kmk_cc_eval_warn(pCompiler, pchWord, "'export' qualifier repeated");
+                    pchWord += 6;
+                    cchLeft -= 6;
+                    continue;
+                }
+
                 if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "override", 8))
                 {
-                    /** @todo warn if repeated. */
-                    fQualifiers |= KMK_CC_EVAL_QUALIFIER_OVERRIDE;
+                    if (!(fQualifiers & KMK_CC_EVAL_QUALIFIER_OVERRIDE))
+                        fQualifiers |= KMK_CC_EVAL_QUALIFIER_OVERRIDE;
+                    else
+                        kmk_cc_eval_warn(pCompiler, pchWord, "'override' qualifier repeated");
                     pchWord += 8;
                     cchLeft -= 8;
@@ -5051,6 +5757,8 @@
                 if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "private", 7))
                 {
-                    /** @todo warn if repeated. */
-                    fQualifiers |= KMK_CC_EVAL_QUALIFIER_PRIVATE;
+                    if (!(fQualifiers & KMK_CC_EVAL_QUALIFIER_PRIVATE))
+                        fQualifiers |= KMK_CC_EVAL_QUALIFIER_PRIVATE;
+                    else
+                        kmk_cc_eval_warn(pCompiler, pchWord, "'private' qualifier repeated");
                     pchWord += 7;
                     cchLeft -= 7;
@@ -5067,5 +5775,5 @@
                 const char *pchEqual = (const char *)memchr(pchWord, '=', cchLeft);
                 if (pchEqual)
-                    return kmk_cc_eval_try_handle_assignment(pCompiler, pchEqual, pchWord, cchLeft, fQualifiers);
+                    return kmk_cc_eval_handle_assignment_or_recipe(pCompiler, pchEqual, pchWord, cchLeft, fQualifiers);
             }
             return 0;
@@ -5440,6 +6148,7 @@
                     {
                         pchTmp = (const char *)memchr(pchWord, '=', cchLeft);
-                        if (   !pchTmp
-                            || !kmk_cc_eval_try_handle_assignment(&Compiler, pchTmp, pchWord, cchLeft, 0) )
+                        if (pchTmp)
+                            kmk_cc_eval_handle_assignment_or_recipe(&Compiler, pchTmp, pchWord, cchLeft, 0);
+                        else
                             kmk_cc_eval_handle_recipe(&Compiler, pchTmp, pchWord, cchLeft);
                     }
