Initial commit
authorRobert Pengelly <robertapengelly@hotmail.com>
Wed, 20 May 2026 05:56:31 +0000 (06:56 +0100)
committerRobert Pengelly <robertapengelly@hotmail.com>
Wed, 20 May 2026 05:56:31 +0000 (06:56 +0100)
40 files changed:
EXTENSIONS.md [new file with mode: 0644]
LICENSE [new file with mode: 0755]
Makefile.unix [new file with mode: 0755]
Makefile.w32 [new file with mode: 0755]
Makefile.wat [new file with mode: 0755]
README.md [new file with mode: 0755]
cc.c [new file with mode: 0755]
cc.h [new file with mode: 0755]
cstr.c [new file with mode: 0755]
cstr.h [new file with mode: 0755]
eval.c [new file with mode: 0755]
eval.h [new file with mode: 0755]
expr.c [new file with mode: 0755]
expr.h [new file with mode: 0755]
hashtab.c [new file with mode: 0755]
hashtab.h [new file with mode: 0755]
int64.c [new file with mode: 0644]
int64.h [new file with mode: 0644]
lex.c [new file with mode: 0755]
lex.h [new file with mode: 0755]
lib.c [new file with mode: 0755]
lib.h [new file with mode: 0755]
list.c [new file with mode: 0755]
list.h [new file with mode: 0755]
ll.c [new file with mode: 0755]
ll.h [new file with mode: 0755]
macro.c [new file with mode: 0755]
macro.h [new file with mode: 0755]
parse.c [new file with mode: 0644]
parse.h [new file with mode: 0644]
pp.c [new file with mode: 0755]
pp.h [new file with mode: 0755]
report.c [new file with mode: 0755]
report.h [new file with mode: 0755]
stdint.h [new file with mode: 0755]
sym.c [new file with mode: 0644]
token.c [new file with mode: 0755]
token.h [new file with mode: 0755]
vector.c [new file with mode: 0755]
vector.h [new file with mode: 0755]

diff --git a/EXTENSIONS.md b/EXTENSIONS.md
new file mode 100644 (file)
index 0000000..e01b57a
--- /dev/null
@@ -0,0 +1,43 @@
+## Supported Extensions
+
+This compiler supports a subset of compiler-specific extensions.
+These extensions are available when `-pedantic` is not specified.
+    
+### `__asm__` (Inline Assembly)
+
+The __asm__ keyword allows the insertion of raw assembly
+instructions into the output file.
+
+See `__asm__` Inline Assembly Syntax below for more details.
+
+### `__inline__`
+A synonym for `inline`.
+
+### `__restrict__`
+A synonym for `restrict`.
+
+## Pragma Directives
+
+All `#pragma` directives are currently ignored by the compiler. They are
+reserved for future compiler-specific features.
+
+## `__asm__` Inline Assembly Syntax
+
+This directive is a very limited GNU-style inline assembly extension.
+It allows hand-written assembly to be inserted directly into the
+generated assembly output.
+
+As Intel syntax is the primary target, arguments are written
+left-to-right rather than right-to-left.
+
+Registers should begin with `%%`, as a single `%` specifies an
+argument index to inject.
+
+`outb` and `outw` are special cases and should use operands in their
+natural left-to-right order.
+
+Usage examples:
+
+```c
+__asm__ ("mov %%eax, %0" : : "N" (port));
+__asm__ ("outb %0, %1" : : "a" (val), "d" (port));
diff --git a/LICENSE b/LICENSE
new file mode 100755 (executable)
index 0000000..f50ef62
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.\r
+\r
+Anyone is free to copy, modify, publish, use, compile, sell, or\r
+distribute this software, either in source code form or as a compiled\r
+binary, for any purpose, commercial or non-commercial, and by any\r
+means.\r
+\r
+In jurisdictions that recognize copyright laws, the author or authors\r
+of this software dedicate any and all copyright interest in the\r
+software to the public domain. We make this dedication for the benefit\r
+of the public at large and to the detriment of our heirs and\r
+successors. We intend this dedication to be an overt act of\r
+relinquishment in perpetuity of all present and future rights to this\r
+software under copyright law.\r
+\r
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\r
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\r
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\r
+OTHER DEALINGS IN THE SOFTWARE.\r
+\r
+For more information, please refer to <https://unlicense.org>\r
diff --git a/Makefile.unix b/Makefile.unix
new file mode 100755 (executable)
index 0000000..eeb7949
--- /dev/null
@@ -0,0 +1,30 @@
+#******************************************************************************
+# @file             Makefile.unix
+#******************************************************************************
+SRCDIR              ?=  $(CURDIR)
+VPATH               :=  $(SRCDIR)
+
+CC                  :=  gcc
+CFLAGS              :=  -D_FILE_OFFSET_BITS=64 -O2 -Wall -Werror -Wextra -std=c90
+
+CC_SRC              :=  $(wildcard $(SRCDIR)/*.c)
+VERSION             :=  $(shell date '+%Y%m%d')
+
+ifeq ($(OS), Windows_NT)
+all: scc.exe
+
+scc.exe: $(CC_SRC)
+
+       $(CC) $(CFLAGS) -DVERSION=$(VERSION) -o $@ $^
+else
+all: scc
+
+scc: $(CC_SRC)
+
+       $(CC) $(CFLAGS) -DVERSION=$(VERSION) -o $@ $^
+endif
+
+clean:
+
+       if [ -f scc.exe ]; then rm -rf scc.exe; fi
+       if [ -f scc ]; then rm -rf scc; fi
diff --git a/Makefile.w32 b/Makefile.w32
new file mode 100755 (executable)
index 0000000..f584116
--- /dev/null
@@ -0,0 +1,21 @@
+#******************************************************************************
+# @file             Makefile.w32
+#******************************************************************************
+SRCDIR              ?=  $(CURDIR)
+VPATH               :=  $(SRCDIR)
+
+CC                  :=  gcc
+CFLAGS              :=  -D_FILE_OFFSET_BITS=64 -O2 -Wall -Werror -Wextra -std=c90
+
+CC_SRC              :=  $(wildcard $(SRCDIR)/*.c)
+
+all: scc.exe
+
+scc.exe: $(CC_SRC)
+
+       $(CC) $(CFLAGS) -o $@ $^
+
+clean:
+
+       if exist scc.exe ( del /q scc.exe )
+       if exist scc ( del /q scc )
diff --git a/Makefile.wat b/Makefile.wat
new file mode 100755 (executable)
index 0000000..d7c8fb9
--- /dev/null
@@ -0,0 +1,31 @@
+#******************************************************************************
+# @file             Makefile.wat
+#******************************************************************************
+SRCDIR              ?=  $(CURDIR)
+VPATH               :=  $(SRCDIR)
+
+CPP_DIR             :=  $(SRCDIR)/cpp
+CC1_DIR             :=  $(SRCDIR)/cc1
+
+CPP_SRC             :=  $(wildcard $(CPP_DIR)/*.c)
+CC1_SRC             :=  $(wildcard $(CC1_DIR)/*.c)
+
+all: scc-cpp.exe scc-cc1.exe
+
+scc-cpp.exe: $(CPP_SRC)
+
+       wcl -fe=$@ $(subst /,\,$^)
+
+scc-cc1.exe: $(CC1_SRC)
+
+       wcl -fe=$@ $(subst /,\,$^)
+
+clean:
+
+       for /r %%f in (*.obj) do ( if exist %%f ( del /q %%f ) )
+
+       if exist scc-cpp.exe ( del /q scc-cpp.exe )
+       if exist scc-cc1.exe ( del /q scc-cc1.exe )
+
+       if exist scc-cpp ( del /q scc-cpp )
+       if exist scc-cc1 ( del /q scc-cc1 )
diff --git a/README.md b/README.md
new file mode 100755 (executable)
index 0000000..9b73769
--- /dev/null
+++ b/README.md
@@ -0,0 +1,124 @@
+## About
+
+    This project provides a C preprocessor and a freestanding C90
+    compiler with select extensions from later standards and
+    compiler-specific extensions.
+    
+    See the `Language Support` section below.
+    
+    It is designed for Intel 386 and is intended for use in environments
+    without a host operating system (e.g., kernels, embedded systems, or
+    bare-metal hobbyist projects).
+    
+    Note: This is a freestanding implementation. It provides the C
+          language translation but does not include a standard library,
+          startup routines (crt0), or linker scripts. Users must
+          provide these components to generate a functional executable.
+
+## Limitations
+
+    This project is currently in early development. Not all GNU
+    extensions are supported. See the `Language Support` section
+    below and `EXTENSIONS.md` for details.
+
+## Language Support
+
+    Preprocessing:
+    
+        Supports /* ... */ (block).
+        
+        If -pedantic is not specified, then
+        // (single-line) comments are also supported.
+        
+        Supports #pragma once.
+        
+        Macro token pasting (##) and stringification (#) operators.
+        
+        If -pedantic is not specified, selected preprocessor
+        extensions are enabled:
+        
+            `C99 Extensions`:
+            
+                Variadic macros (__VA_ARGS__).
+    
+    Compilation:
+    
+        All #pragma directives are currently ignored. There are
+        no immediate plans to implement specific pragma support,
+        but they are reserved for future compiler-specific features.
+        
+        Standard C90: Core language features and scope rules.
+        
+        By default, selected extensions beyond C90 are enabled:
+        
+            `C99 Extensions`:
+            
+                inline
+                restrict
+                long long
+            
+        `Compiler-Specific Extensions`:
+        
+            If -pedantic is not specified, then support for selected
+            compiler extensions.
+            
+            See `EXTENSIONS.md` for a list of supported features.
+            
+            Support for 64-bit long types on 32-bit targets via the
+            -mlong64 flag.
+            
+                Note:   When building with a 32-bit host compiler or MSVC,
+                        a bootstrap build is recommended as scc uses
+                        long in various places.
+        
+        Scoping: Strict C90—all local variable declarations must appear at
+        the beginning of a block.
+        
+        Wide character constants (L'a') and wide string literals (L"...")
+        are represented as 16-bit values. Define wchar_t as a compatible
+        16-bit type (e.g., unsigned short). Programs that define wchar_t
+        incompatibly will have implementation-defined or incorrect
+        behaviour.
+
+## Usage
+
+    To preprocess the input:
+        scc -E -o example.i example.c
+    
+    To transform the input to assembly:
+        scc -S -o example.s example.c
+    
+    To transform the input to an object:
+        scc -o example.o example.c
+    
+    The macro __STDC__ is defined when -std=c90
+    is specified.
+    
+
+## License
+
+    All source code is Public Domain.
+
+## Obtain the source code
+
+    git clone https://git.candlhat.org/scc.git
+
+## Building
+
+    BSD:
+    
+        Make sure you have gcc and gmake installed then run gmake -f Makefile.unix.
+    
+    Linux:
+    
+        Make sure you have gcc and make installed then run make -f Makefile.unix.
+    
+    macOS:
+    
+        Make sure you have xcode command line tools installed then run
+        make -f Makefile.unix.
+    
+    Windows:
+    
+        Make sure you have mingw installed and the location within your PATH variable
+        then run mingw32-make.exe -f Makefile.w32.
diff --git a/cc.c b/cc.c
new file mode 100755 (executable)
index 0000000..c8978fe
--- /dev/null
+++ b/cc.c
@@ -0,0 +1,144 @@
+/******************************************************************************
+ * @file            cc.c
+ *****************************************************************************/
+#include    <stdio.h>
+#include    <stdlib.h>
+#include    <string.h>
+
+#include    "cc.h"
+#include    "expr.h"
+#include    "int64.h"
+#include    "lex.h"
+#include    "lib.h"
+#include    "parse.h"
+#include    "pp.h"
+#include    "report.h"
+#include    "token.h"
+
+struct cc_state *state = 0;
+const char *program_name = 0;
+
+static void cleanup (void) {
+
+    if (state->ofp && state->ofp != stdout) { fclose (state->ofp); }
+    if (state->ifp && state->ifp != stdin) { fclose (state->ifp); }
+    
+    if (get_error_count () > 0) {
+    
+        if (state->ofile) {
+            remove (state->ofile);
+        }
+    
+    }
+
+}
+
+int main (int argc, char **argv) {
+
+    char *p, *root;
+    
+    if (argc && *argv) {
+    
+        program_name = *argv;
+        
+        if ((p = strrchr (program_name, '/')) || (p = strrchr (program_name, '\\'))) {
+            program_name = (p + 1);
+        }
+    
+    }
+    
+    atexit (cleanup);
+    lex_init ();
+    
+    state = xmalloc (sizeof (*state));
+    parse_args (argc, argv, 1);
+    
+    if (!state->ifile) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "no input file specified");
+        return EXIT_FAILURE;
+    
+    }
+    
+    if (!state->ofile) {
+    
+        char *ifile = (char *) state->ifile, *temp;
+        
+        if (strcmp (ifile, "-") == 0) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "-o needed when input is from standard input");
+            return EXIT_FAILURE;
+        
+        }
+        
+        /*if ((p = strrchr (ifile, '/')) || (p = strrchr (ifile, '\\'))) {
+            ifile = (p + 1);
+        }*/
+        
+        if ((p = strrchr (ifile, '.'))) {
+        
+            temp = xmalloc (p - ifile + 3);
+            
+            if (state->mode == CC_MODE_COMPILE) {
+                sprintf (temp, "%.*s.s", (int) (p - ifile), ifile);
+            } else {
+                sprintf (temp, "%.*s.i", (int) (p - ifile), ifile);
+            }
+        
+        } else {
+        
+            if ((p = strrchr (state->ifile, '/')) || (p = strrchr (state->ifile, '\\'))) {
+            
+                unsigned int len = p - state->ifile;
+                
+                root = xmalloc (len + 2);
+                sprintf (root, "%.*s/", (int) len, state->ifile);
+                
+                add_include_path (root);
+                free (root);
+            
+            }
+            
+            temp = xmalloc (strlen (ifile) + 3);
+            
+            if (state->mode == CC_MODE_COMPILE) {
+                sprintf (temp, "%s.s", ifile);
+            } else {
+                sprintf (temp, "%s.i", ifile);
+            }
+        
+        }
+        
+        state->ofile = temp;
+    
+    }
+    
+    state->ofp = stdout;
+    
+    if (state->ofile) {
+    
+        if (!(state->ofp = fopen (state->ofile, "w"))) {
+            return EXIT_FAILURE;
+        }
+    
+    }
+    
+    if (preprocess_init ()) {
+        return EXIT_FAILURE;
+    }
+    
+    if (state->mode == CC_MODE_PREPROCESS) {
+    
+        preprocess_file (state->ifile);
+        return (get_error_count () > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+    
+    }
+    
+    if (init_tokenizer (state->ifile)) {
+        return EXIT_FAILURE;
+    }
+    
+    compile_translation_unit ();
+    return (get_error_count () > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+
+}
diff --git a/cc.h b/cc.h
new file mode 100755 (executable)
index 0000000..791689c
--- /dev/null
+++ b/cc.h
@@ -0,0 +1,46 @@
+/******************************************************************************
+ * @file            cc.h
+ *****************************************************************************/
+#ifndef     _CC_H
+#define     _CC_H
+
+#include    <stdio.h>
+
+#include    "list.h"
+#include    "stdint.h"
+#include    "vector.h"
+
+struct cc_state {
+    
+    const char *ifile, *ofile;
+    FILE *ifp, *ofp;
+    
+    struct list *pplist;
+    struct vector vec_hist;
+    
+    int traditional_linemarkers;
+    int no_linemarkers;
+    int no_leading_underscore;
+    
+    int pedantic;
+    int mode;
+    int std;
+    int long64;
+    int syntax;
+    
+    long version;
+
+};
+
+#define     CC_MODE_COMPILE             0
+#define     CC_MODE_PREPROCESS          1
+
+extern struct cc_state *state;
+extern const char *program_name;
+
+#define     ASM_SYNTAX_MASM             (1 << 0)
+#define     ASM_SYNTAX_INTEL            (1 << 1)
+#define     ASM_SYNTAX_ATT              (1 << 2)
+#define     ASM_SYNTAX_NASM             (1 << 3)
+
+#endif      /* _CC_H */
diff --git a/cstr.c b/cstr.c
new file mode 100755 (executable)
index 0000000..d197860
--- /dev/null
+++ b/cstr.c
@@ -0,0 +1,69 @@
+/******************************************************************************
+ * @file            cstr.c
+ *****************************************************************************/
+#include    <stdlib.h>
+#include    <string.h>
+
+#include    "cstr.h"
+
+extern void *xrealloc (void *__ptr, unsigned int __size);
+
+static void cstr_realloc (CString *cstr, int new_size) {
+
+    int size = cstr->size_allocated;
+    
+    if (size < 8) {
+        size = 8;
+    }
+    
+    while (size < new_size) {
+        size *= 2;
+    }
+    
+    cstr->data = xrealloc (cstr->data, size);
+    cstr->size_allocated = size;
+
+}
+
+void cstr_ccat (CString *cstr, int ch) {
+
+    int size = cstr->size + 1;
+    
+    if (size > cstr->size_allocated) {
+        cstr_realloc (cstr, size);
+    }
+    
+    ((unsigned char *) cstr->data)[size - 1] = ch;
+    cstr->size = size;
+
+}
+
+void cstr_cat (CString *cstr, const char *str, int len) {
+
+    int size;
+    
+    if (len <= 0) {
+        len = strlen (str) + 1 + len;
+    }
+    
+    size = cstr->size + len;
+    
+    if (size > cstr->size_allocated) {
+        cstr_realloc (cstr, size);
+    }
+    
+    memmove (((unsigned char *) cstr->data) + cstr->size, str, len);
+    cstr->size = size;
+
+}
+
+void cstr_new (CString *cstr) {
+    memset (cstr, 0, sizeof (CString));
+}
+
+void cstr_free (CString *cstr) {
+
+    free (cstr->data);
+    cstr_new (cstr);
+
+}
diff --git a/cstr.h b/cstr.h
new file mode 100755 (executable)
index 0000000..4301ece
--- /dev/null
+++ b/cstr.h
@@ -0,0 +1,20 @@
+/******************************************************************************
+ * @file            cstr.h
+ *****************************************************************************/
+#ifndef     _CSTR_H
+#define     _CSTR_H
+
+typedef struct {
+
+    int size, size_allocated;
+    void *data;
+
+} CString;
+
+void cstr_ccat (CString *cstr, int ch);
+void cstr_cat (CString *cstr, const char *str, int len);
+
+void cstr_new (CString *cstr);
+void cstr_free (CString *cstr);
+
+#endif      /* _CSTR_H */
diff --git a/eval.c b/eval.c
new file mode 100755 (executable)
index 0000000..6e49bb0
--- /dev/null
+++ b/eval.c
@@ -0,0 +1,971 @@
+/******************************************************************************
+ * @file            eval.c
+ *****************************************************************************/
+#include    <assert.h>
+#include    <ctype.h>
+#include    <stdio.h>
+#include    <stdlib.h>
+#include    <string.h>
+
+#include    "cstr.h"
+#include    "eval.h"
+#include    "lex.h"
+#include    "lib.h"
+#include    "macro.h"
+#include    "report.h"
+#include    "stdint.h"
+
+static uint64_t eval_expr (uint64_t lhs, char *start, char **pp, int outer_prec, int brackets);
+
+static int hex_value (int ch) {
+
+    if (ch >= '0' && ch <= '9') {
+        return ch - '0';
+    }
+    
+    ch = tolower ((unsigned char) ch);
+    
+    if (ch >= 'a' && ch <= 'f') {
+        return ch - 'a' + 10;
+    }
+    
+    return -1;
+
+}
+
+static uint64_t eval_char_constant (char *start, char **pp) {
+
+    char *caret = *pp;
+    uint64_t value = 0, ch = 0;
+    int count = 0;
+    
+    (*pp)++;
+    
+    while (!is_end_of_line[(int) **pp] && **pp != '\'') {
+    
+        if (**pp == '\\') {
+        
+            (*pp)++;
+            
+            switch (**pp) {
+            
+                case 'a':
+                
+                    ch = '\a';
+                    
+                    (*pp)++;
+                    break;
+                
+                case 'b':
+                
+                    ch = '\b';
+                    
+                    (*pp)++;
+                    break;
+                
+                case 'f':
+                
+                    ch = '\f';
+                    
+                    (*pp)++;
+                    break;
+                
+                case 'n':
+                
+                    ch = '\n';
+                    
+                    (*pp)++;
+                    break;
+                
+                case 'r':
+                
+                    ch = '\r';
+                    
+                    (*pp)++;
+                    break;
+                
+                case 't':
+                
+                    ch = '\t';
+                    
+                    (*pp)++;
+                    break;
+                
+                case 'v':
+                
+                    ch = '\v';
+                    
+                    (*pp)++;
+                    break;
+                
+                case '\\':
+                
+                    ch = '\\';
+                    
+                    (*pp)++;
+                    break;
+                
+                case '\'':
+                
+                    ch = '\'';
+                    
+                    (*pp)++;
+                    break;
+                
+                case '"':
+                
+                    ch = '"';
+                    
+                    (*pp)++;
+                    break;
+                
+                case '0':   case '1':
+                case '2':   case '3':
+                case '4':   case '5':
+                case '6':   case '7': {
+                
+                    int i = 0;
+                    ch = 0;
+                    
+                    while (i < 3 && **pp >= '0' && **pp <= '7') {
+                    
+                        ch = (ch * 8) + (*((*pp)++) - '0');
+                        i++;
+                    
+                    }
+                    
+                    break;
+                
+                }
+                
+                case 'x': {
+                
+                    int h, any = 0;
+                    ch = 0;
+                    
+                    (*pp)++;
+                    
+                    while ((h = hex_value ((unsigned char) **pp)) >= 0) {
+                    
+                        ch = (ch * 16) + h;
+                        
+                        (*pp)++;
+                        any = 1;
+                    
+                    }
+                    
+                    if (!any) {
+                        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp - 1, "\\x used with no following hex digits");
+                    }
+                    
+                    break;
+                
+                
+                }
+                
+                case '\0':
+                
+                    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp, "missing terminating ' character");
+                    return value;
+                
+                default:
+                
+                    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp, "unknown escape sequence");
+                    
+                    ch = (unsigned char) **pp;
+                    (*pp)++;
+                    
+                    break;
+            
+            }
+        
+        } else {
+            ch = (unsigned char) *((*pp)++);
+        }
+        
+        value = (value << 8) | (ch & 0xff);
+        count++;
+    
+    }
+    
+    if (**pp != '\'') {
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "missing terminating ' character");
+    } else {
+        (*pp)++;
+    }
+    
+    if (count > 1) {
+        report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, caret, "multi-character character constant");
+    }
+    
+    return value;
+
+}
+
+static uint64_t eval_unary (uint64_t lhs, char *start, char **pp) {
+
+    char *caret;
+    *pp = skip_whitespace (*pp);
+    
+    if (**pp == '\'') {
+        return eval_char_constant (start, pp);
+    }
+    
+    if (isdigit ((int) **pp)) {
+    
+        uint64_t temp, temp2;
+        int ch, i;
+        
+        if ((*pp)[0] == '0' && tolower ((int) (*pp)[1]) == 'x') {
+        
+            unsigned int base = 16;
+            *pp += 2;
+            
+            while (isxdigit ((int) **pp)) {
+            
+                temp = lhs * base;
+                ch = *((*pp)++);
+                
+                if (ch >= '0' && ch <= '9') {
+                    temp2 = ch - '0';
+                } else {
+                    temp2 = (ch & 0xdf) - ('A' - 10);
+                }
+                
+                lhs = temp + temp2;
+            
+            }
+        
+        } else if ((*pp)[0] == '0') {
+        
+            unsigned int base = 8;
+            
+            while (isdigit ((int) **pp)) {
+            
+                temp = lhs * base;
+                lhs = (temp + (*((*pp)++) - '0'));
+            
+            }
+        
+        } else {
+        
+            unsigned int base = 10;
+            
+            while (isdigit ((int) **pp)) {
+            
+                temp = lhs * base;
+                lhs = (temp + (*((*pp)++) - '0'));
+            
+            }
+        
+        }
+        
+        caret = *pp;
+        
+        if (**pp == 'L') {
+        
+            (*pp)++;
+            
+            if (**pp == 'L') {
+                (*pp)++;
+            }
+            
+            if (**pp == 'U') {
+                (*pp)++;
+            }
+        
+        } else if (**pp == 'U') {
+        
+            (*pp)++;
+            
+            for (i = 0; i < 2; i++) {
+                
+                if (**pp == 'L') {
+                    (*pp)++;
+                }
+                
+            }
+        
+        }
+        
+        if (is_name_beginner ((int) **pp)) {
+        
+            while (is_name_part ((int) **pp)) {
+                (*pp)++;
+            }
+            
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "invalid suffix \"%.*s\" on integer constant", (int) (*pp - caret), caret);
+        
+        }
+        
+        return lhs;
+    
+    }
+    
+    if (is_name_beginner ((int) **pp)) {
+    
+        char *sname, *temp;
+        
+        struct hashtab_name *key;
+        struct macro *m;
+        
+        caret = *pp;
+        
+        while (is_name_part ((int) *caret)) {
+            caret++;
+        }
+        
+        if ((caret - *pp) == 7 && memcmp (*pp, "defined", 7) == 0) {
+        
+            caret = skip_whitespace (caret);
+            *pp = caret;
+            
+            if (*caret == '(') {
+            
+                caret = skip_whitespace (caret + 1);
+                *pp = caret;
+                
+                while (!is_end_of_line[(int) *caret]) {
+                
+                    if (isspace ((int) *caret) || *caret == ')') { break; }
+                    caret++;
+                
+                }
+                
+                sname = xstrndup (*pp, caret - *pp);
+                caret = skip_whitespace (caret);
+                
+                if (*caret != ')') {
+                    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "missing ')' after \"defined\"");
+                }
+            
+            } else {
+                sname = xstrndup (*pp, caret - *pp);
+            }
+            
+            if (*caret == ')') { caret++; }
+            *pp = skip_whitespace (caret);
+            
+            lhs = !(find_macro (sname) == 0);
+            return lhs;
+        
+        }
+        
+        sname = xstrndup (*pp, caret - *pp);
+        *pp = skip_whitespace (caret);
+        
+        if ((key = find_macro (sname))) {
+        
+            free (sname);
+            
+            if ((m = get_macro (key))) {
+            
+                char *line = (temp = process_macro (start, pp, m));
+                
+                lhs = eval_unary (lhs, line, &temp);
+                free (line);
+                
+                return lhs;
+            
+            }
+        
+        }
+        
+        free (sname);
+        return lhs;
+    
+    }
+    
+    if (**pp == '!') {
+    
+        uint64_t temp = 0;
+        
+        *pp = skip_whitespace (*pp + 1);
+        temp = eval_unary (temp, start, pp);
+        
+        return (temp == 0);
+    
+    }
+    
+    if (**pp == '~') {
+    
+        int flip_bits = 0;
+        
+        while (**pp == '~') {
+        
+            flip_bits = !flip_bits;
+            *pp = skip_whitespace (*pp + 1);
+        
+        }
+        
+        lhs = eval_unary (lhs, start, pp);
+        
+        if (flip_bits) {
+            lhs = ~lhs;
+        }
+        
+        return lhs;
+    
+    }
+    
+    if (**pp == '-') {
+    
+        int sign = 1;
+        
+        while (**pp == '-' || **pp == '+') {
+        
+            if (**pp == '-') { sign = !sign; }
+            *pp = skip_whitespace (*pp + 1);
+        
+        }
+        
+        lhs = eval_unary (lhs, start, pp);
+        
+        if (!sign) {
+            lhs = -lhs;
+        }
+        
+        return lhs;
+    
+    }
+    
+    if (**pp == '(') {
+    
+        char *new_line, *temp;
+        int depth = 0;
+        
+        CString cstr;
+        cstr_new (&cstr);
+        
+        caret = (*pp)++;
+        cstr_cat (&cstr, start, *pp - start);
+        
+        while (!is_end_of_line[(int) **pp]) {
+        
+            if (**pp == '(') {
+            
+                cstr_ccat (&cstr, *((*pp)++));
+                
+                depth++;
+                continue;
+            
+            }
+            
+            if (**pp == ')') {
+            
+                if (depth > 0) {
+                
+                    cstr_ccat (&cstr, *((*pp)++));
+                    
+                    depth--;
+                    continue;
+                
+                }
+                
+                break;
+            
+            }
+            
+            cstr_ccat (&cstr, *((*pp)++));
+        
+        }
+        
+        if (**pp == ')') {
+            cstr_ccat (&cstr, *((*pp)++));
+        }
+        
+        cstr_cat (&cstr, *pp, strlen (*pp));
+        cstr_ccat (&cstr, '\0');
+        
+        new_line = temp = xstrdup (cstr.data);
+        temp += (caret - start) + 1;
+        
+        lhs = eval_unary (lhs, new_line, &temp);
+        lhs = eval_expr (lhs, new_line, &temp, 14, 1);
+        
+        if (*(temp = skip_whitespace (temp)) != ')') {
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "missing ')' in expression");
+        }
+        
+        free (new_line);
+        
+        /*caret = (*pp)++;
+        
+        lhs = eval_unary (lhs, start, pp);
+        lhs = eval_expr (lhs, start, pp, 15, 1);
+        
+        if (*(*pp = skip_whitespace (*pp)) != ')') {
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "missing ')' in expression");
+        }*/
+        
+        return lhs;
+    
+    }
+    
+    return -1;
+
+}
+
+#define     OP_MUL                      0
+#define     OP_DIV                      1
+#define     OP_MOD                      2
+#define     OP_PLUS                     3
+#define     OP_MINUS                    4
+#define     OP_LT                       5
+#define     OP_GT                       6
+#define     OP_LTEQ                     7
+#define     OP_GTEQ                     8
+#define     OP_EQEQ                     9
+#define     OP_NOTEQ                    11
+#define     OP_AND                      12
+#define     OP_XOR                      13
+#define     OP_OR                       14
+#define     OP_ANDAND                   15
+#define     OP_OROR                     16
+#define     OP_LSHIFT                   17
+#define     OP_RSHIFT                   18
+#define     OP_QUEST                    19
+#define     OP_MAX                      20
+
+struct op {
+
+    char *word;
+    int kind;
+
+};
+
+static struct op *get_op (char **pp) {
+
+    static struct op kws[] = {
+    
+        {   "<=",   OP_LTEQ     },
+        {   ">=",   OP_GTEQ     },
+        {   "==",   OP_EQEQ     },
+        {   "!=",   OP_NOTEQ    },
+        {   "&&",   OP_ANDAND   },
+        {   "||",   OP_OROR     },
+        {   "<<",   OP_LSHIFT   },
+        {   ">>",   OP_RSHIFT   },
+        
+        {   "*",    OP_MUL      },
+        {   "/",    OP_DIV      },
+        {   "%",    OP_MOD      },
+        {   "+",    OP_PLUS     },
+        {   "-",    OP_MINUS    },
+        {   "<",    OP_LT       },
+        {   ">",    OP_GT       },
+        {   "&",    OP_AND      },
+        {   "^",    OP_XOR      },
+        {   "|",    OP_OR       },
+        {   "?",    OP_QUEST    }
+    
+    };
+    
+    struct op *kw;
+    unsigned int i;
+    
+    for (i = 0; i < (sizeof (kws) / sizeof (*kws)); i++) {
+    
+        kw = &kws[i];
+        
+        if (strncmp (*pp, kw->word, strlen (kw->word)) == 0) {
+        
+            *pp += strlen (kw->word);
+            return kw;
+        
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static int get_prec (int kind) {
+
+    switch (kind) {
+    
+        case OP_MUL:    case OP_DIV:    case OP_MOD:
+        
+            return 3;
+        
+        case OP_PLUS:   case OP_MINUS:
+        
+            return 4;
+        
+        case OP_LSHIFT:                 case OP_RSHIFT:
+        
+            return 5;
+        
+        case OP_LT:     case OP_GT:     case OP_LTEQ:       case OP_GTEQ:
+        
+            return 6;
+        
+        case OP_EQEQ:   case OP_NOTEQ:
+        
+            return 7;
+        
+        case OP_AND:
+        
+            return 8;
+        
+        case OP_XOR:
+        
+            return 9;
+        
+        case OP_OR:
+        
+            return 10;
+        
+        case OP_ANDAND:
+        
+            return 11;
+        
+        case OP_OROR:
+        
+            return 12;
+        
+        case OP_QUEST:
+        
+            return 13;
+        
+        default:
+        
+            break;
+    
+    } 
+    
+    return 100;
+
+}
+
+static uint64_t eval_expr (uint64_t lhs, char *start, char **pp, int outer_prec, int brackets) {
+
+    struct op *op = 0, *op2 = 0;
+    int prec, look_ahead;
+    
+    char *caret;
+    uint64_t rhs;
+    
+    for (;;) {
+    
+        *pp = skip_whitespace (*pp);
+        
+        if (brackets && **pp == ')') {
+            return lhs;
+        }
+        
+        if (is_end_of_line[(int) **pp]) {
+            break;
+        }
+        
+        if (!(op = get_op (pp)) || (prec = get_prec (op->kind)) >= outer_prec) {
+        
+            if (op) { *pp -= strlen (op->word); }
+            
+            if (outer_prec == 15) {
+                break;
+            }
+            
+            return lhs;
+        
+        }
+        
+        *pp = skip_whitespace (*pp);
+        
+        if (is_end_of_line[(int) **pp]) {
+            break;
+        }
+        
+        rhs = 0;
+        
+        if (op->kind == OP_QUEST) {
+        
+            uint64_t left = 0, right = 0;
+            
+            left = eval_unary (left, start, pp);
+            left = eval_expr (left, start, pp, 14, brackets);
+            
+            *pp = skip_whitespace (*pp);
+            assert (**pp == ':');
+            *pp = skip_whitespace (*pp + 1);
+            
+            right = eval_unary (right, start, pp);
+            right = eval_expr (right, start, pp, 14, brackets);
+            
+            if (lhs != 0) {
+                lhs = left;
+            } else {
+                lhs = right;
+            }
+            
+            continue;
+        
+        }
+        
+        caret = (*pp = skip_whitespace (*pp));
+        
+        if (is_end_of_line[(int) **pp]) {
+            break;
+        }
+        
+        rhs = eval_unary (rhs, start, pp);
+        
+        if (*pp == caret) {
+            break;
+        }
+        
+        for (;;) {
+        
+            *pp = skip_whitespace (*pp);
+            
+            if (is_end_of_line[(int) **pp] || (brackets && **pp == ')')) {
+                goto _calc;
+            }
+            
+            if (!(op2 = get_op (pp)) || (look_ahead = get_prec (op2->kind)) >= prec) {
+            
+                if (op2) { *pp -= strlen (op2->word); }
+                break;
+            
+            }
+            
+            caret = (*pp -= strlen (op2->word));
+            
+            if (is_end_of_line[(int) **pp]) {
+                break;
+            }
+            
+            rhs = eval_expr (rhs, start, pp, prec, brackets);
+            
+            if (*pp == caret) {
+                break;
+            }
+        
+        }
+        
+        if (!op2) { break; }
+        
+    _calc:
+        
+        switch (op->kind) {
+        
+            case OP_MUL:
+            
+                lhs *= rhs;
+                break;
+            
+            case OP_DIV:
+            
+                lhs /= rhs;
+                break;
+            
+            case OP_MOD:
+            
+                lhs %= rhs;
+                break;
+            
+            case OP_PLUS:
+            
+                lhs += rhs;
+                break;
+            
+            case OP_MINUS:
+            
+                lhs -= rhs;
+                break;
+            
+            case OP_LT:
+            
+                lhs = (lhs < rhs);
+                break;
+            
+            case OP_GT:
+            
+                lhs = (lhs > rhs);
+                break;
+            
+            case OP_LTEQ:
+            
+                lhs = (lhs <= rhs);
+                break;
+            
+            case OP_GTEQ:
+            
+                lhs = (lhs >= rhs);
+                break;
+            
+            case OP_EQEQ:
+            
+                lhs = (lhs == rhs);
+                break;
+            
+            case OP_NOTEQ:
+            
+                lhs = (lhs != rhs);
+                break;
+            
+            case OP_AND:
+            
+                lhs &= rhs;
+                break;
+            
+            case OP_XOR:
+            
+                lhs ^= rhs;
+                break;
+            
+            case OP_OR:
+            
+                lhs |= rhs;
+                break;
+            
+            case OP_ANDAND:
+            
+                lhs = ((lhs != 0) && (rhs != 0));
+                break;
+            
+            case OP_OROR:
+            
+                lhs = ((lhs != 0) || (rhs != 0));
+                break;
+            
+            case OP_LSHIFT:
+            
+                lhs <<= rhs;
+                break;
+            
+            case OP_RSHIFT:
+            
+                lhs >>= rhs;
+                break;
+            
+            default:
+            
+                report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "unimplemented");
+                break;
+        
+        }
+    
+    }
+    
+    caret = (*pp = skip_whitespace (*pp));
+    
+    if (!is_end_of_line[(int) **pp]) {
+    
+        if (is_name_beginner ((int) **pp) || isdigit ((int) **pp)) {
+        
+            while (is_name_part ((int) **pp)) {
+                (*pp)++;
+            }
+            
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "missing binary operator before token \"%.*s\"", (int) (*pp - caret), caret);
+            
+            while (!is_end_of_line[(int) **pp]) {
+            
+                if ((brackets && **pp == ')')) {
+                    break;
+                }
+                
+                (*pp)++;
+            
+            }
+            
+        } else if (op && ((op2 = get_op (pp)) || (brackets && **pp == ')'))) {
+        
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "operator '%s' has no right operand", op->word);
+            
+            if (op2) {
+            
+                while (!is_end_of_line[(int) **pp]) {
+                
+                    if ((brackets && **pp == ')')) {
+                        break;
+                    }
+                    
+                    (*pp)++;
+                
+                }
+            
+            }
+        
+        } else if (**pp == ')') {
+        
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "missing '(' in expression");
+            
+            while (!is_end_of_line[(int) **pp]) {
+            
+                if ((brackets && **pp == ')')) {
+                    break;
+                }
+                
+                (*pp)++;
+            
+            }
+        
+        } else {
+        
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp, "token \"%c\" is not valid in preprocessor expressions", **pp);
+            
+            while (!is_end_of_line[(int) **pp]) {
+            
+                /*if (is_name_beginner ((int) **pp)) {
+                    break;
+                }*/
+                
+                if (/*isspace ((int) **pp) || */(brackets && **pp == ')')) {
+                    break;
+                }
+                
+                (*pp)++;
+            
+            }
+        
+        }
+        
+        *pp = skip_whitespace (*pp);
+        return 0;
+    
+    }
+    
+    return lhs;
+
+}
+
+int eval (char *start, char **pp) {
+
+    struct op *op = 0;
+    uint64_t lhs = 0;
+    
+    char *caret = (*pp = skip_whitespace (*pp));
+    
+    if (!is_end_of_line[(int) **pp]) {
+    
+        lhs = eval_unary (lhs, start, pp);
+        
+        if (*pp == caret && (op = get_op (pp))) {
+        
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "operator '%s' has no left operand", op->word);
+            
+            while (!is_end_of_line[(int) **pp]) {
+                (*pp)++;
+            }
+            
+            return 0;
+        
+        }
+        
+        lhs = eval_expr (lhs, start, pp, 15, 0);
+        
+        if (!is_end_of_line[(int) **pp]) {
+            report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "#if not empty");
+        }
+        
+        return (lhs != 0);
+    
+    }
+    
+    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "#if with no expression");
+    return 0;
+
+}
diff --git a/eval.h b/eval.h
new file mode 100755 (executable)
index 0000000..9361938
--- /dev/null
+++ b/eval.h
@@ -0,0 +1,9 @@
+/******************************************************************************
+ * @file            eval.h
+ *****************************************************************************/
+#ifndef     _EVAL_H
+#define     _EVAL_H
+
+int eval (char *start, char **pp);
+
+#endif      /* _EVAL_H */
diff --git a/expr.c b/expr.c
new file mode 100755 (executable)
index 0000000..a4bd11d
--- /dev/null
+++ b/expr.c
@@ -0,0 +1,800 @@
+/******************************************************************************
+ * @file            expr.c
+ *****************************************************************************/
+#include    <assert.h>
+#include    <stdlib.h>
+#include    <string.h>
+
+#include    "expr.h"
+#include    "int64.h"
+#include    "lib.h"
+#include    "parse.h"
+#include    "report.h"
+#include    "token.h"
+
+static int64_s *eval_expr (int64_s *lhs, int outer_prec, int brackets);
+static enum token_kind *killat = 0;
+
+static void cast_integer_constant (int64_s *v, int size, int is_unsigned) {
+
+    unsigned long sign;
+    
+    if (!v) {
+        return;
+    }
+    
+    if (size <= 1) {
+    
+        v->low &= 0xffUL;
+        v->high = 0;
+        
+        if (!is_unsigned && (v->low & 0x80UL)) {
+        
+            v->low |= 0xffffff00UL;
+            v->high = 0xffffffffUL;
+        
+        }
+        
+        return;
+    
+    }
+    
+    if (size == 2) {
+    
+        v->low &= 0xffffUL;
+        v->high = 0;
+        
+        if (!is_unsigned && (v->low & 0x8000UL)) {
+            v->low |= 0xffff0000UL;
+            v->high = 0xffffffffUL;
+        }
+        
+        return;
+    
+    }
+    
+    if (size == 4) {
+    
+        sign = v->low & 0x80000000UL;
+        
+        v->high = (!is_unsigned && sign) ? 0xffffffffUL : 0;
+        return;
+    
+    }
+
+}
+
+static int should_terminate (void) {
+
+    int i;
+    
+    if (killat) {
+    
+        for (i = 0; ; i++) {
+        
+            if (killat[i]) {
+            
+                if (tok.kind == killat[i]) {
+                    return 1;
+                }
+                
+                continue;
+            
+            }
+            
+            break;
+        
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static int64_s *eval_unary (int64_s *lhs) {
+
+    if (token_is_sizeof_keyword ()) {
+    
+        int size;
+        get_token ();
+        
+        size = parse_sizeof_value ();
+        zext64 (lhs, (unsigned long) size);
+        
+        return lhs;
+    
+    }
+    
+    if (tok.kind == TOK_IDENT && resolve_enum_constant (tok.ident, lhs)) {
+    
+        get_token ();
+        return lhs;
+    
+    }
+    
+    switch (tok.kind) {
+    
+        case TOK_CCHAR:     case TOK_CINT:      case TOK_CUINT:     case TOK_CULONG:
+        case TOK_CLONG:     case TOK_CLLONG:    case TOK_CULLONG:   case TOK_LCHAR:
+        
+            lhs->high = tok.val.i.high;
+            lhs->low = tok.val.i.low;
+            
+            get_token ();
+            return lhs;
+        
+        default:
+        
+            break;
+    
+    }
+    
+    if (tok.kind == TOK_XMARK) {
+    
+        int64_s tmp = { 0 }, *tmp_p;
+        
+        get_token ();
+        tmp_p = eval_unary (&tmp);
+        
+        if (!tmp_p) {
+            return 0;
+        }
+        
+        tmp = *tmp_p;
+        zext64 (lhs, is_zero64 (tmp));
+        
+        return lhs;
+    
+    }
+    
+    if (tok.kind == TOK_TILDE) {
+    
+        int flip_bits = 0;
+        
+        while (tok.kind == TOK_TILDE) {
+        
+            flip_bits = !flip_bits;
+            get_token ();
+        
+        }
+        
+        lhs = eval_unary (lhs);
+        
+        if (!lhs) {
+            return 0;
+        }
+        
+        if (flip_bits) {
+            not64 (lhs);
+        }
+        
+        return lhs;
+    
+    }
+    
+    if (tok.kind == TOK_MINUS || tok.kind == TOK_PLUS) {
+    
+        int sign = 1;
+        
+        while (tok.kind == TOK_MINUS || tok.kind == TOK_PLUS) {
+        
+            if (tok.kind == TOK_MINUS) { sign = !sign; }
+            get_token ();
+        
+        }
+        
+        lhs = eval_unary (lhs);
+        
+        if (!lhs) {
+            return 0;
+        }
+        
+        if (!sign) {
+            neg64 (lhs);
+        }
+        
+        return lhs;
+    
+    }
+    
+    if (tok.kind == TOK_AMPER) {
+    
+        if (parse_constexpr_address_of_null_member (lhs)) {
+            return lhs;
+        }
+        
+        return 0;
+    
+    }
+    
+    if (tok.kind == TOK_LPAREN) {
+    
+        char *start = xstrdup (tok.start);
+        unsigned long caret = tok.caret - tok.start;
+        
+        get_token ();
+        
+        if (token_starts_type_name ()) {
+        
+            int cast_size = 0;
+            int cast_is_unsigned = 0;
+            int cast_is_pointer = 0;
+            
+            if (parse_cast_type_name (&cast_size, &cast_is_unsigned, &cast_is_pointer)) {
+            
+                lhs = eval_unary (lhs);
+                
+                if (!lhs) {
+                
+                    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "integer constant expression expected");
+                    lhs = 0;
+                
+                } else {
+                
+                    if (cast_is_pointer) {
+                    
+                        cast_size = 4;
+                        cast_is_unsigned = 1;
+                    
+                    }
+                    
+                    cast_integer_constant (lhs, cast_size, cast_is_unsigned);
+                
+                }
+                
+                free (start);
+                return lhs;
+            
+            }
+        
+        }
+        
+        lhs = eval_unary (lhs);
+        lhs = eval_expr (lhs, 14, 1);
+        
+        if (tok.kind != TOK_RPAREN) {
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, start + caret, "missing ')' in expression");
+        } else {
+            get_token ();
+        }
+        
+        free (start);
+        return lhs;
+    
+    }
+    
+    lhs->high = 0;
+    lhs->low = 0;
+    
+    return 0;
+
+}
+
+#define     OP_MUL                      0
+#define     OP_DIV                      1
+#define     OP_MOD                      2
+#define     OP_PLUS                     3
+#define     OP_MINUS                    4
+#define     OP_LT                       5
+#define     OP_GT                       6
+#define     OP_LTEQ                     7
+#define     OP_GTEQ                     8
+#define     OP_EQEQ                     9
+#define     OP_NOTEQ                    11
+#define     OP_AND                      12
+#define     OP_XOR                      13
+#define     OP_OR                       14
+#define     OP_ANDAND                   15
+#define     OP_OROR                     16
+#define     OP_LSHIFT                   17
+#define     OP_RSHIFT                   18
+#define     OP_QUEST                    19
+#define     OP_MAX                      20
+
+struct op {
+
+    char *word;
+    int kind;
+
+};
+
+static struct op *get_op (char *pp) {
+
+    static struct op kws[] = {
+    
+        {   "<=",   OP_LTEQ     },
+        {   ">=",   OP_GTEQ     },
+        {   "==",   OP_EQEQ     },
+        {   "!=",   OP_NOTEQ    },
+        {   "&&",   OP_ANDAND   },
+        {   "||",   OP_OROR     },
+        {   "<<",   OP_LSHIFT   },
+        {   ">>",   OP_RSHIFT   },
+        
+        {   "*",    OP_MUL      },
+        {   "/",    OP_DIV      },
+        {   "%",    OP_MOD      },
+        {   "+",    OP_PLUS     },
+        {   "-",    OP_MINUS    },
+        {   "<",    OP_LT       },
+        {   ">",    OP_GT       },
+        {   "&",    OP_AND      },
+        {   "^",    OP_XOR      },
+        {   "|",    OP_OR       },
+        {   "?",    OP_QUEST    }
+    
+    };
+    
+    struct op *kw;
+    unsigned int i;
+    
+    for (i = 0; i < (sizeof (kws) / sizeof (*kws)); i++) {
+    
+        kw = &kws[i];
+        
+        if (strncmp (pp, kw->word, strlen (kw->word)) == 0) {
+            return kw;
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static int get_prec (int kind) {
+
+    switch (kind) {
+    
+        case OP_MUL:    case OP_DIV:    case OP_MOD:
+        
+            return 3;
+        
+        case OP_PLUS:   case OP_MINUS:
+        
+            return 4;
+        
+        case OP_LSHIFT:                 case OP_RSHIFT:
+        
+            return 5;
+        
+        case OP_LT:     case OP_GT:     case OP_LTEQ:       case OP_GTEQ:
+        
+            return 6;
+        
+        case OP_EQEQ:   case OP_NOTEQ:
+        
+            return 7;
+        
+        case OP_AND:
+        
+            return 8;
+        
+        case OP_XOR:
+        
+            return 9;
+        
+        case OP_OR:
+        
+            return 10;
+        
+        case OP_ANDAND:
+        
+            return 11;
+        
+        case OP_OROR:
+        
+            return 12;
+        
+        case OP_QUEST:
+        
+            return 13;
+        
+        default:
+        
+            break;
+    
+    } 
+    
+    return 100;
+
+}
+
+static void recover_bad_const_operand (void) {
+
+    int depth = 0;
+    
+    if (tok.kind == TOK_EOF || should_terminate ()) {
+        return;
+    }
+    
+    do {
+    
+        if (tok.kind == TOK_LPAREN) {
+        
+            depth++;
+            
+            get_token ();
+            continue;
+        
+        }
+        
+        if (tok.kind == TOK_RPAREN) {
+        
+            if (depth > 0) {
+            
+                depth--;
+                
+                get_token ();
+                continue;
+            
+            }
+            
+            if (should_terminate ()) {
+            
+                get_token ();
+                continue;
+            
+            }
+            
+            break;
+        
+        }
+        
+        get_token ();
+    
+    } while (tok.kind != TOK_EOF && (depth > 0 || !should_terminate ()));
+
+}
+
+static int64_s *eval_expr (int64_s *lhs, int outer_prec, int brackets) {
+
+    struct op *op = 0, *op2 = 0;
+    
+    int64_s rhs = { 0 }, *tmp;
+    int prec, look_ahead;
+    
+    unsigned long index, lineno;
+    
+    for (;;) {
+    
+        if (brackets && tok.kind == TOK_RPAREN) {
+            return lhs;
+        }
+        
+        if (tok.kind == TOK_EOF || should_terminate ()) {
+            break;
+        }
+        
+        if (!(op = get_op (tok.ident)) || (prec = get_prec (op->kind)) >= outer_prec) {
+        
+            if (outer_prec == 15) {
+                break;
+            }
+            
+            return lhs;
+        
+        }
+        
+        get_token ();
+        
+        if (tok.kind == TOK_EOF || should_terminate ()) {
+            break;
+        }
+        
+        rhs.high = 0;
+        rhs.low = 0;
+        
+        if (op->kind == OP_QUEST) {
+        
+            int64_s left = { 0 }, right = { 0 }, *tmp;
+            
+            if (!(tmp = eval_unary (&left))) {
+                break;
+            }
+            
+            left = *eval_expr (tmp, 14, brackets);
+            
+            assert (tok.kind == TOK_COLON);
+            get_token ();
+            
+            if (!(tmp = eval_unary (&right))) {
+                break;
+            }
+            
+            right = *eval_expr (&right, 14, brackets);
+            
+            if (!is_zero64 (*lhs)) {
+            
+                lhs->high = left.high;
+                lhs->low = left.low;
+            
+            } else {
+            
+                lhs->high = right.high;
+                lhs->low = right.low;
+            
+            }
+            
+            continue;
+        
+        }
+        
+        if (!(tmp = eval_unary (&rhs))) {
+        
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "integer constant expression expected");
+            
+            recover_bad_const_operand ();
+            break;
+        
+        }
+        
+        for (;;) {
+        
+            rhs.high = tmp->high;
+            rhs.low = tmp->low;
+            
+            if (tok.kind == TOK_EOF || should_terminate () || (brackets && tok.kind == TOK_RPAREN)) {
+                goto _calc;
+            }
+            
+            if (!(op2 = get_op (tok.ident)) || (look_ahead = get_prec (op2->kind)) >= prec) {
+                break;
+            }
+            
+            /* Get the current index and line number. */
+            index = tok.caret - tok.start;
+            lineno = get_line_number ();
+            
+            /* Try evaluation. */
+            tmp = eval_expr (&rhs, prec, brackets);
+            
+            /* If the index and line number are the same them we;re done. */
+            if (index == (unsigned long) (tok.caret - tok.start) && lineno == get_line_number ()) {
+                break;
+            }
+        
+        }
+        
+        if (!op2) { break; }
+        
+    _calc:
+        
+        switch (op->kind) {
+        
+            case OP_MUL:
+            
+                mul64 (lhs, rhs);
+                break;
+            
+            case OP_DIV:
+            
+                div64 (lhs, rhs);
+                break;
+            
+            case OP_MOD:
+            
+                mod64 (lhs, rhs);
+                break;
+            
+            case OP_PLUS:
+            
+                add64 (lhs, rhs);
+                break;
+            
+            case OP_MINUS:
+            
+                sub64 (lhs, rhs);
+                break;
+            
+            case OP_LT:
+            
+                zext64 (lhs, lt64_unsigned (*lhs, rhs));
+                break;
+            
+            case OP_GT:
+            
+                zext64 (lhs, gt64_unsigned (*lhs, rhs));
+                break;
+            
+            case OP_LTEQ:
+            
+                zext64 (lhs, lte64_unsigned (*lhs, rhs));
+                break;
+            
+            case OP_GTEQ:
+            
+                zext64 (lhs, gte64_unsigned (*lhs, rhs));
+                break;
+            
+            case OP_EQEQ:
+            
+                zext64 (lhs, eq64 (*lhs, rhs));
+                break;
+            
+            case OP_NOTEQ:
+            
+                zext64 (lhs, !eq64 (*lhs, rhs));
+                break;
+            
+            case OP_AND:
+            
+                and64 (lhs, rhs);
+                break;
+            
+            case OP_XOR:
+            
+                xor64 (lhs, rhs);
+                break;
+            
+            case OP_OR:
+            
+                or64 (lhs, rhs);
+                break;
+            
+            case OP_ANDAND:
+            
+                zext64 (lhs, !is_zero64 (*lhs) && !is_zero64 (rhs));
+                break;
+            
+            case OP_OROR:
+            
+                zext64 (lhs, !is_zero64 (*lhs) || !is_zero64 (rhs));
+                break;
+            
+            case OP_LSHIFT:
+            
+                shl64 (lhs, rhs.low);
+                break;
+            
+            case OP_RSHIFT:
+            
+                shr64 (lhs, rhs.low);
+                break;
+            
+            default:
+            
+                report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "unimplemented");
+                break;
+        
+        }
+    
+    }
+    
+    if (!(tok.kind == TOK_EOF || should_terminate ())) {
+    
+        if (tok.kind >= TOK_LCHAR && tok.kind <= TOK_CULONG) {
+            
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "missing binary operator before token \"%s\"", tok.ident);
+            
+            while (!(tok.kind == TOK_EOF || should_terminate ())) {
+            
+                if (brackets && tok.kind == TOK_RPAREN) {
+                    break;
+                }
+                
+                get_token ();
+            
+            }
+            
+        } else if (op && ((op2 = get_op (tok.ident)) || (brackets && tok.kind == TOK_RPAREN))) {
+        
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "operator '%s' has no right operand", op->word);
+            
+            if (op2) {
+            
+                get_token ();
+                
+                while (!(tok.kind == TOK_EOF || should_terminate ())) {
+                
+                    if (brackets && tok.kind == TOK_RPAREN) {
+                        break;
+                    }
+                    
+                    get_token ();
+                
+                }
+            
+            }
+        
+        } else if (brackets && tok.kind == TOK_RPAREN) {
+        
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "missing '(' in expression");
+            
+            while (!(tok.kind == TOK_EOF || should_terminate ())) {
+            
+                if (brackets && tok.kind == TOK_RPAREN) {
+                    break;
+                }
+                
+                get_token ();
+            
+            }
+        
+        } else {
+        
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "integer constant expression expected");
+            
+            while (!(tok.kind == TOK_EOF || should_terminate ())) {
+            
+                if (brackets && tok.kind == TOK_RPAREN) {
+                    break;
+                }
+                
+                get_token ();
+            
+            }
+        
+        }
+        
+        lhs->high = 0;
+        lhs->low = 0;
+    
+    }
+    
+    return lhs;
+
+}
+
+int expr_const (enum token_kind *k) {
+
+    char *start = xstrdup (tok.start);
+    unsigned long caret = tok.caret - tok.start;
+    
+    int64_s wc = expr_const64 (k);
+    unsigned long sign = wc.low & 0x80000000UL;
+    
+    int c = (int) wc.low;
+    
+    if (!((wc.high == 0) || (sign && wc.high == 0xFFFFFFFFUL))) {
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, start + caret, "constant exceeds 32 bit");
+    }
+    
+    free (start);
+    return c;
+
+}
+
+int64_s expr_const64 (enum token_kind *k) {
+
+    struct op *op = 0;
+    int64_s lhs = { 0 }, *tmp;
+    
+    char *start = xstrdup (tok.start);
+    unsigned long caret = tok.caret - tok.start;
+    
+    killat = k;
+    
+    if (!(tmp = eval_unary (&lhs))) {
+    
+        if ((op = get_op (tok.ident))) {
+        
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, start + caret, "operator '%s' has no left operand", op->word);
+            
+            while (!(tok.kind == TOK_EOF || should_terminate ())) {
+                get_token ();
+            }
+            
+            free (start);
+            
+            lhs.high = 0;
+            lhs.low = 0;
+            
+            return lhs;
+        
+        }
+        
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "integer constant expression expected");
+        
+        recover_bad_const_operand ();
+        tmp = &lhs;
+    
+    }
+    
+    lhs = *eval_expr (tmp, 15, 0);
+    
+    free (start);
+    return lhs;
+
+}
diff --git a/expr.h b/expr.h
new file mode 100755 (executable)
index 0000000..8cdf350
--- /dev/null
+++ b/expr.h
@@ -0,0 +1,13 @@
+/******************************************************************************
+ * @file            expr.h
+ *****************************************************************************/
+#ifndef     _EXPR_H
+#define     _EXPR_H
+
+#include    "int64.h"
+#include    "token.h"
+
+int expr_const (enum token_kind *k);
+int64_s expr_const64 (enum token_kind *k);
+
+#endif      /* _EXPR_H */
diff --git a/hashtab.c b/hashtab.c
new file mode 100755 (executable)
index 0000000..3f26b37
--- /dev/null
+++ b/hashtab.c
@@ -0,0 +1,217 @@
+/******************************************************************************
+ * @file            hashtab.c
+ *****************************************************************************/
+#include    <stddef.h>
+#include    <stdlib.h>
+#include    <string.h>
+
+#include    "hashtab.h"
+
+static struct hashtab_entry *find_entry (struct hashtab_entry *entries, unsigned long capacity, struct hashtab_name *key);
+
+static int adjust_capacity (struct hashtab *table, unsigned long new_capacity) {
+
+    struct hashtab_entry *new_entries, *old_entries;
+    unsigned long i, new_count, old_capacity;
+    
+    if ((new_entries = malloc (sizeof (*new_entries) * new_capacity)) == NULL) {
+        return -2;
+    }
+    
+    for (i = 0; i < new_capacity; i++) {
+    
+        struct hashtab_entry *entry = &new_entries[i];
+        
+        entry->key = NULL;
+        entry->value = NULL;
+    
+    }
+    
+    old_entries = table->entries;
+    old_capacity = table->capacity;
+    
+    new_count = 0;
+    
+    for (i = 0; i < old_capacity; i++) {
+    
+        struct hashtab_entry *entry = &old_entries[i], *dest;
+        
+        if (entry->key == NULL) {
+            continue;
+        }
+        
+        dest = find_entry (new_entries, new_capacity, entry->key);
+        
+        dest->key = entry->key;
+        dest->value = entry->value;
+        
+        new_count++;
+    
+    }
+    
+    free (old_entries);
+    
+    table->capacity = new_capacity;
+    table->count = new_count;
+    table->entries = new_entries;
+    table->used = new_count;
+    
+    return 0;
+
+}
+
+static struct hashtab_entry *find_entry (struct hashtab_entry *entries, unsigned long capacity, struct hashtab_name *key) {
+
+    struct hashtab_entry *tombstone = NULL;
+    unsigned long index;
+    
+    for (index = key->hash % capacity; ; index = (index + 1) % capacity) {
+    
+        struct hashtab_entry *entry = &entries[index];
+        
+        if (entry->key == NULL) {
+        
+            if (entry->value == NULL) {
+            
+                if (tombstone == NULL) {
+                    return entry;
+                }
+                
+                return tombstone;
+            
+            } else if (tombstone == NULL) {
+                tombstone = entry;
+            }
+        
+        } else if (entry->key->bytes == key->bytes) {
+        
+            if (memcmp (entry->key->chars, key->chars, key->bytes) == 0 && entry->key->hash == key->hash) {
+                return entry;
+            }
+        
+        }
+    
+    }
+
+}
+
+static unsigned long hash_string (const void *p, unsigned long length) {
+
+    unsigned char *str = (unsigned char *) p;
+    unsigned long i;
+    
+    unsigned long result = 0;
+    
+    for (i = 0; i < length; i++) {
+        result = (((unsigned short) str[i]) << 4) + (result >> 9) + result + (result >> 3) + (((unsigned short) str[i]) << 2) - (result << 12);
+    }
+    
+    return result;
+
+}
+
+struct hashtab_name *hashtab_alloc_name (const char *str) {
+
+    unsigned long bytes = strlen (str), hash = hash_string (str, bytes);
+    struct hashtab_name *name;
+    
+    if (!(name = malloc (sizeof (*name)))) {
+        return 0;
+    }
+    
+    name->bytes = bytes;
+    name->chars = str;
+    name->hash = hash;
+    
+    return name;
+
+}
+
+struct hashtab_name *hashtab_get_key (struct hashtab *table, const char *name) {
+
+    struct hashtab_name *key;
+    struct hashtab_entry *entry;
+    
+    if (!table || table->count == 0 || !(key = hashtab_alloc_name (name))) {
+        return 0;
+    }
+    
+    entry = find_entry (table->entries, table->capacity, key);
+    free (key);
+    
+    return entry->key;
+
+}
+
+void *hashtab_get (struct hashtab *table, struct hashtab_name *key) {
+
+    struct hashtab_entry *entry;
+    
+    if (!table || table->count == 0) {
+        return 0;
+    }
+    
+    entry = find_entry (table->entries, table->capacity, key);
+    
+    if (!entry->key) {
+        return 0;
+    }
+    
+    return entry->value;
+
+}
+
+int hashtab_put (struct hashtab *table, struct hashtab_name *key, void *value) {
+
+    const int MIN_CAPACITY = 15;
+    
+    struct hashtab_entry *entry;
+    int ret = 0;
+    
+    if (table->used >= table->capacity / 2) {
+    
+        long capacity = table->capacity * 2 - 1;
+        
+        if (capacity < MIN_CAPACITY) {
+            capacity = MIN_CAPACITY;
+        }
+        
+        if ((ret = adjust_capacity (table, capacity))) {
+            return ret;
+        }
+    
+    }
+    
+    entry = find_entry (table->entries, table->capacity, key);
+    
+    if (!entry->key) {
+    
+        table->count++;
+        
+        if (!entry->value) {
+            table->used++;
+        }
+    
+    }
+    
+    entry->key = key;
+    entry->value = value;
+    
+    return 0;
+
+}
+
+void hashtab_remove (struct hashtab *table, struct hashtab_name *key) {
+
+    struct hashtab_entry *entry;
+    
+    if ((entry = find_entry (table->entries, table->capacity, key)) != NULL) {
+    
+        entry->key = 0;
+        entry->value = 0;
+        
+        --table->count;
+    
+    }
+
+}
diff --git a/hashtab.h b/hashtab.h
new file mode 100755 (executable)
index 0000000..5ab0492
--- /dev/null
+++ b/hashtab.h
@@ -0,0 +1,36 @@
+/******************************************************************************
+ * @file            hashtab.h
+ *****************************************************************************/
+#ifndef     _HASHTAB_H
+#define     _HASHTAB_H
+
+struct hashtab_name {
+
+    const char *chars;
+    unsigned long bytes, hash;
+
+};
+
+struct hashtab_entry {
+
+    struct hashtab_name *key;
+    void *value;
+
+};
+
+struct hashtab {
+
+    struct hashtab_entry *entries;
+    int capacity, count, used;
+
+};
+
+struct hashtab_name *hashtab_alloc_name (const char *str);
+struct hashtab_name *hashtab_get_key (struct hashtab *table, const char *name);
+
+void *hashtab_get (struct hashtab *table, struct hashtab_name *key);
+
+int hashtab_put (struct hashtab *table, struct hashtab_name *key, void *value);
+void hashtab_remove (struct hashtab *table, struct hashtab_name *key);
+
+#endif      /* _HASHTAB_H */
diff --git a/int64.c b/int64.c
new file mode 100644 (file)
index 0000000..8ad59f5
--- /dev/null
+++ b/int64.c
@@ -0,0 +1,713 @@
+/******************************************************************************
+ * @file            int64.c
+ *****************************************************************************/
+#include    <ctype.h>
+#include    <string.h>
+
+#include    "int64.h"
+#include    "report.h"
+
+extern const char *get_filename (void);
+extern unsigned long get_line_number (void);
+
+void add64 (int64_s *a, int64_s b) {
+
+    unsigned long raw_sum, carry;
+    
+    /* 1. Calculate the raw result */
+    raw_sum = a->low + b.low;
+
+    /* 2. Manually force 32-bit wrap around using a mask */
+    /* This ensures that any "1" that would be in the 33rd bit is stripped */
+    a->low = raw_sum & 0xFFFFFFFFUL;
+
+    /* 3. Extract the carry: if raw_sum > 0xFFFFFFFF, we had a carry */
+    carry = (raw_sum > 0xFFFFFFFFUL) ? 1 : 0;
+
+    /* 4. Update the high part */
+    a->high = (a->high + b.high + carry) & 0xFFFFFFFFUL;
+
+}
+
+void mul64 (int64_s *a, int64_s b) {
+
+    /* Extract 16-bit halves of the low words */
+    unsigned long al = a->low & 0xFFFF;
+    unsigned long ah = (a->low >> 16) & 0xFFFF;
+    unsigned long bl = b.low & 0xFFFF;
+    unsigned long bh = (b.low >> 16) & 0xFFFF;
+    
+    /* 1. Calculate the 32x32 -> 64 bit product of (a->low * b.low) */
+    unsigned long p0 = al * bl;
+    unsigned long p1 = al * bh;
+    unsigned long p2 = ah * bl;
+    unsigned long p3 = ah * bh;
+    
+    /* Handle middle-word carries */
+    unsigned long mid = (p0 >> 16) + (p1 & 0xFFFF) + (p2 & 0xFFFF);
+    
+    /* Store the new low 32 bits */
+    unsigned long new_low = ((mid << 16) | (p0 & 0xFFFF));
+    
+    /* 2. Calculate the high word */
+    /* Start with the carries/high-bits from the 32x32 multiply */
+    unsigned long new_high = p3 + (p1 >> 16) + (p2 >> 16) + (mid >> 16);
+    
+    /* 3. Add the "Cross Multiplication" of the original high word */
+    /* Mathematically: (a->high * b.low) + (b.high * a->low) */
+    /* Note: We ignore (a->high * b.high) as it overflows 64 bits */
+    new_high += (a->high * b.low);
+    new_high += (b.high * a->low);
+    
+    /* Final Assignment */
+    a->low = new_low;
+    a->high = new_high;
+
+}
+
+void shl64 (int64_s *a, int n) {
+
+    /* Handle shifts >= 32 separately */
+    if (n >= 32) {
+    
+        a->high = (a->low << (n - 32)) & 0xFFFFFFFFUL;
+        a->low = 0;
+    
+    } else if (n > 0) {
+    
+        /* Bits pushed out of low go into high */
+        a->high = ((a->high << n) | (a->low >> (32 - n))) & 0xFFFFFFFFUL;
+        a->low = (a->low << n) & 0xFFFFFFFFUL;
+    
+    }
+
+}
+
+void shr64 (int64_s *a, int n) {
+
+    /* Handle shifts >= 32 separately */
+    if (n >= 32) {
+    
+        a->low = (a->high >> (n - 32));
+        a->high = 0;
+    
+    } else if (n > 0) {
+    
+        /* Bits pushed out of high go into low */
+        a->low = ((a->low >> n) | (a->high << (32 - n))) & 0xFFFFFFFFUL;
+        a->high = (a->high >> n) & 0xFFFFFFFFUL;
+    
+    }
+
+}
+
+void sub64 (int64_s *a, int64_s b) {
+
+    unsigned long old_low = a->low, borrow;
+
+    /* 1. Perform subtraction and force 32-bit wrapping */
+    a->low = (a->low - b.low) & 0xFFFFFFFFUL;
+
+    /* 2. Determine if a borrow occurred:
+     * In unsigned arithmetic, if (a->low > old_low), we wrapped.
+     * Because we masked the result, this logic is now 100% stable.
+     */
+    borrow = (old_low < b.low) ? 1 : 0;
+
+    /* 3. Subtract high parts and the borrow, forcing 32-bit wrap */
+    /* If high - b.high - borrow is negative, the & 0xFFFFFFFFUL 
+       will wrap it correctly to the 32-bit representation */
+    a->high = (a->high - b.high - borrow) & 0xFFFFFFFFUL;
+
+}
+
+void and64 (int64_s *a, int64_s b) {
+
+    a->low &= b.low;
+    a->high &= b.high;
+
+}
+
+void or64 (int64_s *a, int64_s b) {
+
+    a->low |= b.low;
+    a->high |= b.high;
+
+}
+
+void xor64 (int64_s *a, int64_s b) {
+
+    a->low ^= b.low;
+    a->high ^= b.high;
+
+}
+
+/* Returns 1 if a > b, -1 if a < b, and 0 if a == b */
+int compare64 (int64_s a, int64_s b) {
+
+    /* Compare the high bits first (the most significant part) */
+    if (a.high > b.high) return 1;
+    if (a.high < b.high) return -1;
+    
+    /* If high bits are equal, compare the low bits */
+    if (a.low > b.low) return 1;
+    if (a.low < b.low) return -1;
+    
+    /* If both are identical */
+    return 0;
+
+}
+
+void div64 (int64_s *a, int64_s b) {
+
+    int64_s quotient = { 0, 0 };
+    int64_s remainder = { 0, 0 };
+    
+    int i;
+    
+    /* If divisor is zero, you should handle this as a fatal compiler error */
+    if (b.low == 0 && b.high == 0) {
+        return;
+    }
+    
+    /* Iterate through all 64 bits of the dividend */
+    for (i = 63; i >= 0; i--) {
+    
+        /* 1. Shift the working remainder left to make room for the next bit */
+        shl64 (&remainder, 1);
+        
+        /* 2. Bring in the i-th bit from the dividend (a) */
+        /* Use a mask to check the bit and pull it into remainder.low */
+        if (i >= 32) {
+        
+            if ((a->high >> (i - 32)) & 1UL) {
+                remainder.low |= 1;
+            }
+        
+        } else {
+        
+            if ((a->low >> i) & 1UL) {
+                remainder.low |= 1;
+            }
+        
+        }
+
+        /* 3. If remainder >= divisor, we can subtract and set quotient bit */
+        if (compare64 (remainder, b) >= 0) {
+        
+            sub64 (&remainder, b);
+            
+            /* Set the i-th bit in the quotient */
+            if (i >= 32) {
+                quotient.high |= (1UL << (i - 32));
+            } else {
+                quotient.low |= (1UL << i);
+            }
+        
+        }
+    
+    }
+    
+    /* Overwrite dividend with the resulting quotient */
+    a->low = quotient.low;
+    a->high = quotient.high;
+
+}
+
+void mod64 (int64_s *a, int64_s b) {
+
+    int64_s remainder = {0, 0};
+    int i;
+    
+    /* 1. Safety check for division by zero */
+    if (b.low == 0 && b.high == 0) {
+        return;
+    }
+    
+    /* 2. Iterate through all 64 bits */
+    for (i = 63; i >= 0; i--) {
+    
+        /* Shift remainder left by 1 */
+        shl64 (&remainder, 1);
+        
+        /* Bring in the i-th bit from 'a' */
+        if (i >= 32) {
+        
+            if ((a->high >> (i - 32)) & 1UL) {
+                remainder.low |= 1;
+            }
+        
+        } else {
+        
+            if ((a->low >> i) & 1UL) {
+                remainder.low |= 1;
+            }
+        
+        }
+
+        /* 3. Compare and Subtract (The core logic) */
+        if (compare64 (remainder, b) >= 0) {
+            sub64 (&remainder, b);
+        }
+    
+    }
+    
+    /* 4. Update 'a' with the final remainder */
+    a->low = remainder.low;
+    a->high = remainder.high;
+
+}
+
+void zext64 (int64_s *dest, unsigned long input) {
+
+    dest->low = input;
+    dest->high = 0;                     /* Clear the upper bits */
+
+}
+
+void sext64 (int64_s *dest, long input) {
+
+    dest->low = (unsigned long)input;
+    
+    /* If the 31st bit (MSB) is 1, the number is negative */
+    if (input & 0x80000000UL) {
+        dest->high = 0xFFFFFFFFUL;
+    } else {
+        dest->high = 0x00000000UL;
+    }
+
+}
+
+void mask64 (int64_s *a, int bits) {
+
+    unsigned long low_mask;
+    unsigned long high_mask;
+    unsigned long new_low, new_high;
+    
+    if (bits >= 64) {
+        return;
+    }
+    
+    if (bits <= 0) {
+    
+        if (a->high != 0 || a->low != 0) {
+            report_at (get_filename(), get_line_number(), REPORT_WARNING, "implicit truncation of 0x%08lX%08lX to 0x00000000", a->high, a->low);
+        }
+        
+        a->high = 0;
+        a->low = 0;
+        
+        return;
+    
+    }
+    
+    if (bits < 32) {
+    
+        low_mask = (1UL << bits) - 1;
+        
+        new_low = a->low & low_mask;
+        new_high = 0;
+        
+        if (a->high != 0 || (a->low & ~low_mask) != 0) {
+            report_at (get_filename(), get_line_number(), REPORT_WARNING, "implicit truncation of 0x%08lX%08lX to 0x%08lX", a->high, a->low, new_low);
+        }
+    
+    
+    } else if (bits == 32) {
+    
+        new_low = a->low;
+        new_high = 0;
+        
+        if (a->high != 0) {
+            report_at (get_filename(), get_line_number(), REPORT_WARNING, "implicit truncation of 0x%08lX%08lX to 0x%08lX", a->high, a->low, new_low);
+        }
+    
+    } else { /* 33..63 */
+    
+        high_mask = (1UL << (bits - 32)) - 1;
+        
+        new_low = a->low;
+        new_high = a->high & high_mask;
+        
+        if ((a->high & ~high_mask) != 0) {
+            report_at (get_filename(), get_line_number(), REPORT_WARNING, "implicit truncation of 0x%08lX%08lX to 0x%08lX%08lX", a->high, a->low, new_high, new_low);
+        }
+    
+    }
+    
+    a->high = new_high;
+    a->low = new_low;
+    
+    report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "???");
+
+}
+
+/* The specialized wrapper for standard 32-bit int truncation */
+void trunc64 (int64_s *a) {
+    mask64 (a, 32);
+}
+
+void inc64 (int64_s *a) {
+
+    a->low++;
+    
+    if (a->low == 0) {                  /* Carry occurred */
+        a->high++;
+    }
+
+}
+
+void dec64 (int64_s *a) {
+
+    if (a->low == 0) {                  /* Borrow needed */
+        a->high--;
+    }
+    
+    a->low--;
+
+}
+
+void not64 (int64_s *a) {
+
+    /* Flip bits and then force them back into 32-bit bounds */
+    a->low = (~a->low) & 0xFFFFFFFFUL;
+    a->high = (~a->high) & 0xFFFFFFFFUL;
+
+}
+
+void neg64 (int64_s *a) {
+
+    not64 (a);
+    inc64 (a);
+
+}
+
+int is_zero64 (int64_s a) {
+    return (a.low == 0 && a.high == 0);
+}
+
+int eq64 (int64_s a, int64_s b) {
+    return (a.low == b.low && a.high == b.high);
+}
+
+int neq64 (int64_s a, int64_s b) {
+    return !eq64 (a, b);
+}
+
+int lt64_unsigned (int64_s a, int64_s b) {
+
+    if (a.high != b.high) {
+        return a.high < b.high;
+    }
+    
+    return a.low < b.low;
+
+}
+
+int lte64_unsigned (int64_s a, int64_s b) {
+
+    if (a.high != b.high) {
+        return a.high < b.high;
+    }
+    
+    return a.low <= b.low;
+
+}
+
+int lt64_signed (int64_s a, int64_s b) {
+
+    unsigned long a_sign = (a.high >> 31) & 1;
+    unsigned long b_sign = (b.high >> 31) & 1;
+    
+    /* If signs differ, negative is less than positive */
+    if (a_sign != b_sign) {
+        return a_sign > b_sign;
+    }
+    
+    /* If signs are the same, treat as unsigned */
+    return lt64_unsigned (a, b);
+
+}
+
+/* Less than or equal to (Signed) */
+int lte64_signed (int64_s a, int64_s b) {
+
+    /* A <= B is true if A < B or A == B */
+    return lt64_signed (a, b) || eq64 (a, b);
+
+}
+
+/* Greater than or equal to (unsigned) */
+int gte64_unsigned (int64_s a, int64_s b) {
+
+    /* If it's NOT less than, it is greater than or equal to */
+    return !lt64_unsigned (a, b);
+
+}
+
+/* Greater than or equal to (signed) */
+int gte64_signed (int64_s a, int64_s b) {
+
+    /* If it's NOT less than, it is greater than or equal to */
+    return !lt64_signed (a, b);
+
+}
+
+/* Greater than (Unsigned) */
+int gt64_unsigned (int64_s a, int64_s b) {
+
+    /* If it's not less than or equal to, it must be greater than */
+    return !lte64_unsigned (a, b);
+
+}
+
+/* Greater than (Signed) */
+int gt64_signed (int64_s a, int64_s b) {
+
+    /* If it's not less than or equal to, it must be greater than */
+    return !lte64_signed (a, b);
+
+}
+
+void ld_to_u64 (int64_s *out, long double x) {
+
+    long double base = 4294967296.0L;                       /* 2^32 */
+    long double hi, lo;
+    
+    if (x <= 0.0L) {
+    
+        out->low = 0;
+        out->high = 0;
+        
+        return;
+    
+    }
+    
+    /* optional clamp */
+    if (x >= 18446744073709551615.0L) {                     /* 2^64 - 1 */
+    
+        out->low = 0xFFFFFFFFUL;
+        out->high = 0xFFFFFFFFUL;
+        
+        return;
+    
+    }
+    
+    hi = x / base;
+    out->high = (unsigned long) hi;
+    
+    lo = x - ((long double) out->high * base);
+    out->low = (unsigned long) lo;
+
+}
+
+void ld_to_i64 (int64_s *out, long double x) {
+
+    long double limit = 9223372036854775808.0L; /* 2^63 */
+    
+    if (x >= limit) {
+    
+        /* INT64_MAX */
+        out->low = 0xFFFFFFFFUL;
+        out->high = 0x7FFFFFFFUL;
+        
+        return;
+    
+    }
+    
+    if (x < -limit) {
+    
+        /* INT64_MIN */
+        out->low = 0x00000000UL;
+        out->high = 0x80000000UL;
+        
+        return;
+    
+    }
+    
+    if (x >= 0.0L) {
+    
+        ld_to_u64 (out, x);
+        return;
+    
+    }
+    
+    /* negative case */
+    {
+    
+        int64_s tmp;
+        ld_to_u64 (&tmp, -x);
+        
+        *out = tmp;
+        neg64 (out);
+    
+    }
+
+}
+
+void sign_extend_masked (int64_s *v, unsigned long mask) {
+
+    unsigned long sign;
+    
+    mask &= 0xFFFFFFFFUL;
+    sign = ((mask >> 1) + 1) & 0xFFFFFFFFUL;
+    
+    v->low &= 0xFFFFFFFFUL;
+    
+    if (v->low & sign) {
+    
+        v->low = (v->low | (~mask)) & 0xFFFFFFFFUL;
+        v->high = 0xFFFFFFFFUL;
+    
+    } else {
+    
+        v->low &= mask;
+        v->high = 0;
+    
+    }
+    
+    v->low &= 0xFFFFFFFFUL;
+    v->high &= 0xFFFFFFFFUL;
+
+}
+
+static void norm64 (int64_s *v) {
+
+    v->low &= U32_MASK;
+    v->high &= U32_MASK;
+
+}
+
+long double u64_to_ld (int64_s v) {
+
+    norm64 (&v);
+    return (long double) v.high * 4294967296.0L + (long double) v.low;
+
+}
+
+long double i64_to_ld (int64_s v) {
+
+    norm64 (&v);
+
+    if (!(v.high & 0x80000000UL)) {
+        return (long double) v.high * 4294967296.0L + (long double) v.low;
+    }
+
+    neg64 (&v);
+    return -((long double) v.high * 4294967296.0L + (long double) v.low);
+
+}
+
+int int64_is_negative (int64_s v) {
+    return (v.high & 0x80000000LU) != 0;
+}
+
+int int64_fits_uint (int64_s v) {
+
+    /* assumes unsigned int is 32-bit */
+    return v.high == 0;
+
+}
+
+int int64_fits_int (int64_s v) {
+
+    /* range: -2147483648 .. 2147483647 */
+    if (v.high == 0) {
+        return v.low <= 0x7fffffffLU;
+    }
+    
+    if ((long) v.high == -1) {
+        return v.low >= 0x80000000LU;
+    }
+    
+    return 0;
+
+}
+
+void parse_string_to_i64 (int64_s *val, const char *str) {
+
+    unsigned long l_low, l_high;
+    unsigned long res_low, res_mid;
+    unsigned long carry1, carry2;
+    
+    unsigned long old_low;
+    int base = 10, digit;
+    
+    char ch;
+    
+    val->low = 0;
+    val->high = 0;
+    
+    /* 1. Skip leading whitespace */
+    while (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r') {
+        str++;
+    }
+    
+    /* 2. Detect Base */
+    if (*str == '0') {
+    
+        base = 8;
+        str++;
+        
+        if (*str == 'x' || *str == 'X') {
+        
+            base = 16;
+            str++;
+        
+        }
+    
+    }
+    
+    /* 3. Process Digits */
+    for (;;) {
+    
+        ch = *str;
+        
+        /* Determine digit value */
+        if (ch >= '0' && ch <= '9') {
+            digit = ch - '0';
+        } else if (base == 16 && ch >= 'a' && ch <= 'f') {
+            digit = ch - 'a' + 10;
+        } else if (base == 16 && ch >= 'A' && ch <= 'F') {
+            digit = ch - 'A' + 10;
+        } else {
+            /* Not a valid digit for the current base */
+            break; 
+        }
+        
+        if (digit >= base) {
+            break;
+        }
+        
+        /* --- Math: val = (val * base) + digit --- */
+        
+        /* Multiply existing val->low by base */
+        l_low = val->low & 0xFFFF;
+        l_high = (val->low >> 16) & 0xFFFF;
+        
+        res_low = l_low * base;
+        carry1 = res_low >> 16;
+        
+        res_mid = (l_high * base) + carry1;
+        carry2 = res_mid >> 16;
+        
+        /* Update high part: (val->high * base) + carry from low part */
+        val->high = (val->high * base) + carry2;
+        
+        /* Finalize low part */
+        val->low = ((res_mid & 0xFFFF) << 16) | (res_low & 0xFFFF);
+        
+        /* Add the new digit */
+        old_low = val->low;
+        val->low += (unsigned long) digit;
+        
+        /* Handle carry from addition into the high word */
+        if (val->low < old_low) {
+            val->high++;
+        }
+        
+        str++;
+    
+    }
+
+}
diff --git a/int64.h b/int64.h
new file mode 100644 (file)
index 0000000..c3529f2
--- /dev/null
+++ b/int64.h
@@ -0,0 +1,71 @@
+/******************************************************************************
+ * @file            int64.h
+ *****************************************************************************/
+#ifndef     _INT64_H
+#define     _INT64_H
+
+typedef struct {
+
+    unsigned long low;
+    unsigned long high;
+
+} int64_s;
+
+void add64 (int64_s *a, int64_s b);
+void div64 (int64_s *a, int64_s b);
+void mod64 (int64_s *a, int64_s b);
+void mul64 (int64_s *a, int64_s b);
+void shl64 (int64_s *a, int n);
+void shr64 (int64_s *a, int n);
+void sub64 (int64_s *a, int64_s b);
+
+void and64 (int64_s *a, int64_s b);
+void or64 (int64_s *a, int64_s b);
+void xor64 (int64_s *a, int64_s b);
+
+void mask64 (int64_s *a, int bits);
+void trunc64 (int64_s *a);
+
+void inc64 (int64_s *a);
+void dec64 (int64_s *a);
+
+void not64 (int64_s *a);
+void neg64 (int64_s *a);
+
+void zext64 (int64_s *dest, unsigned long input);
+void sext64 (int64_s *dest, long input);
+
+int compare64 (int64_s a, int64_s b);
+int is_zero64 (int64_s a);
+
+int eq64 (int64_s a, int64_s b);
+int neq64 (int64_s a, int64_s b);
+
+int lt64_unsigned (int64_s a, int64_s b);
+int lte64_unsigned (int64_s a, int64_s b);
+
+int lt64_signed (int64_s a, int64_s b);
+int lte64_signed (int64_s a, int64_s b);
+
+int gte64_unsigned (int64_s a, int64_s b);
+int gte64_signed (int64_s a, int64_s b);
+
+int gt64_unsigned (int64_s a, int64_s b);
+int gt64_signed (int64_s a, int64_s b);
+
+void ld_to_u64 (int64_s *out, long double x);
+void ld_to_i64 (int64_s *out, long double x);
+
+int int64_is_negative (int64_s v);
+int int64_fits_uint (int64_s v);
+int int64_fits_int (int64_s v);
+
+void sign_extend_masked (int64_s *v, unsigned long mask);
+void parse_string_to_i64 (int64_s *val, const char *str);
+
+#define     U32_MASK                    ((((unsigned long) 0xFFFFU) << 16) | 0xFFFFU)
+
+long double u64_to_ld (int64_s v);
+long double i64_to_ld (int64_s v);
+
+#endif      /* _INT64_H */
\ No newline at end of file
diff --git a/lex.c b/lex.c
new file mode 100755 (executable)
index 0000000..c68a5aa
--- /dev/null
+++ b/lex.c
@@ -0,0 +1,35 @@
+/******************************************************************************
+ * @file            lex.c
+ *****************************************************************************/
+#include    "lex.h"
+
+char is_end_of_line[256] = { 0 };
+char lex_table[256] = { 0 };
+
+void lex_init (void) {
+
+    int i;
+    
+    is_end_of_line[0] = 1;
+    is_end_of_line[10] = 1;
+    
+    /*lex_table[36] = LEX_NAME_START | LEX_NAME_PART;*/
+    /*lex_table[46] = LEX_NAME_START | LEX_NAME_PART;*/
+    
+    for (i = 48; i < 58; i++) {
+        lex_table[i] = LEX_NAME_PART;
+    }
+    
+    /*lex_table[63] = LEX_NAME_START | LEX_NAME_PART;*/
+    
+    for (i = 65; i < 91; i++) {
+        lex_table[i] = LEX_NAME_START | LEX_NAME_PART;
+    }
+    
+    lex_table[95] = LEX_NAME_START | LEX_NAME_PART;
+    
+    for (i = 97; i < 123; i++) {
+        lex_table[i] = LEX_NAME_START | LEX_NAME_PART;
+    }
+
+}
diff --git a/lex.h b/lex.h
new file mode 100755 (executable)
index 0000000..f295e8b
--- /dev/null
+++ b/lex.h
@@ -0,0 +1,18 @@
+/******************************************************************************
+ * @file            lex.h
+ *****************************************************************************/
+#ifndef     _LEX_H
+#define     _LEX_H
+
+#define     LEX_NAME_PART               0x0001
+#define     LEX_NAME_START              0x0002
+
+extern char is_end_of_line[];
+extern char lex_table[];
+
+#define     is_name_beginner(c)         (lex_table[(c)] & LEX_NAME_START)
+#define     is_name_part(c)             (lex_table[(c)] & LEX_NAME_PART)
+
+void lex_init (void);
+
+#endif      /* _LEX_H */
diff --git a/lib.c b/lib.c
new file mode 100755 (executable)
index 0000000..71506f1
--- /dev/null
+++ b/lib.c
@@ -0,0 +1,781 @@
+/******************************************************************************
+ * @file            lib.c
+ *****************************************************************************/
+#include    <ctype.h>
+#include    <stdarg.h>
+#include    <stdio.h>
+#include    <stdlib.h>
+#include    <string.h>
+
+#include    "cc.h"
+#include    "lex.h"
+#include    "lib.h"
+#include    "report.h"
+#include    "vector.h"
+
+#if     defined (_WIN32)
+# define    PATHSEP                     ';'
+#else
+# define    PATHSEP                     ':'
+#endif
+
+struct cc_option {
+
+    const char *name;
+    int idx, flags;
+
+};
+
+#define     CC_OPTION_NO_ARG            0
+#define     CC_OPTION_HAS_ARG           1
+#define     CC_OPTION_EQUALS_ARG        3
+
+#define     CC_OPTION_NONE                                  0
+#define     CC_OPTION_COMPILE                               1
+#define     CC_OPTION_DEFINE                                2
+#define     CC_OPTION_HELP                                  3
+#define     CC_OPTION_INCLUDE                               4
+#define     CC_OPTION_NO_LEADING_UNDERSCORE                 5
+#define     CC_OPTION_NO_LINEMARKERS                        6
+#define     CC_OPTION_MASM                                  7
+#define     CC_OPTION_MLONG64                               8
+#define     CC_OPTION_OUTFILE                               9
+#define     CC_OPTION_PEDANTIC                              10
+#define     CC_OPTION_PREPOCESS                             11
+#define     CC_OPTION_STD                                   12
+#define     CC_OPTION_TRANDITIONAL_LINEMARKERS              13
+#define     CC_OPTION_UNDEF                                 14
+
+static struct cc_option opts[] = {
+
+    {   "-pedantic",                        CC_OPTION_PEDANTIC,                     CC_OPTION_NO_ARG        },
+    {   "-std",                             CC_OPTION_STD,                          CC_OPTION_EQUALS_ARG    },
+    
+    {   "-D",                               CC_OPTION_DEFINE,                       CC_OPTION_HAS_ARG       },
+    {   "-I",                               CC_OPTION_INCLUDE,                      CC_OPTION_HAS_ARG       },
+    {   "-U",                               CC_OPTION_UNDEF,                        CC_OPTION_HAS_ARG       },
+    
+    {   "-P",                               CC_OPTION_NO_LINEMARKERS,               CC_OPTION_NO_ARG        },
+    {   "-o",                               CC_OPTION_OUTFILE,                      CC_OPTION_HAS_ARG       },
+    
+    {   "-E",                               CC_OPTION_PREPOCESS,                    CC_OPTION_NO_ARG        },
+    {   "-S",                               CC_OPTION_COMPILE,                      CC_OPTION_NO_ARG        },
+    
+    {   "-fno-leading-underscore",          CC_OPTION_NO_LEADING_UNDERSCORE,        CC_OPTION_NO_ARG        },
+    {   "-mlong64",                         CC_OPTION_MLONG64,                      CC_OPTION_NO_ARG        },
+    {   "-masm",                            CC_OPTION_MASM,                         CC_OPTION_EQUALS_ARG    },
+    
+    {   "--traditional-linemarker-format",  CC_OPTION_TRANDITIONAL_LINEMARKERS,     CC_OPTION_NO_ARG        },
+    {   "--help",                           CC_OPTION_HELP,                         CC_OPTION_NO_ARG        },
+    
+    {   0,                                  0,                                      0                       }
+
+};
+
+
+static int strstart (const char *val, const char **str) {
+
+    const char *p = *str;
+    const char *q = val;
+    
+    while (*q != '\0') {
+    
+        if (*p != *q) {
+            return 0;
+        }
+        
+        ++p;
+        ++q;
+    
+    }
+    
+    *str = p;
+    return 1;
+
+}
+
+static void print_usage (void) {
+
+    if (program_name) {
+    
+        fprintf (stderr, "Usage: %s [options] file...\n\n", program_name);
+        fprintf (stderr, "Options:\n\n");
+        
+        fprintf (stderr, "    -Dname[=value]                    Define 'name' with value 'value'.\n");
+        fprintf (stderr, "    -I DIR                            Add DIR to include search path.\n");
+        fprintf (stderr, "    -Uname                            Undefine 'name'.\n");
+        
+        fprintf (stderr, "\n");
+        fprintf (stderr, "    -E                                Preprocess only; do not compile.\n");
+        fprintf (stderr, "    -S                                Compile only.\n");
+        
+        fprintf (stderr, "\n");
+        fprintf (stderr, "    -std=<standard>                   Assume that the input sources are for\n");
+        fprintf (stderr, "                                      <standard>.\n");
+        fprintf (stderr, "                                          Currently only C89 and C90 are\n");
+        fprintf (stderr, "                                          supported.\n");
+        fprintf (stderr, "    -mlong64                          Convert long to 64-bit.\n");
+        fprintf (stderr, "    -masm=<syntax>                    Output <systax> compatible syntax.\n");
+        fprintf (stderr, "                                          Defaults to MASM but also supports\n");
+        fprintf (stderr, "                                          GNU AT&T, GNU INTEL and NASM.\n");
+        fprintf (stderr, "    -P                                Don't print linemarkers.\n");
+        fprintf (stderr, "    -o OBJFILE                        Name the object-file output OBJFILE.\n");
+        
+        fprintf (stderr, "\n");
+        fprintf (stderr, "    --traditional-linemarker-format   Use #line instead of short form.\n");
+        fprintf (stderr, "    --help                            Print this help information.\n");
+        
+        fprintf (stderr, "\n");
+    
+    }
+
+}
+
+
+char *skip_whitespace (char *__p) {
+
+    while (*__p == ' ' || *__p == '\t') {
+        __p++;
+    }
+    
+    return __p;
+
+}
+
+char *symname (char **pp) {
+
+    char *p = *pp;
+    
+    if (is_name_beginner ((int) **pp)) {
+    
+        while (is_name_part ((int) **pp)) {
+            (*pp)++;
+        }
+        
+        return xstrndup (p, *pp - p);
+    
+    }
+    
+    return 0;
+
+}
+
+char *xstrdup (const char *__p) {
+
+    char *p = xmalloc (strlen (__p) + 1);
+    
+    strcpy (p, __p);
+    return p;
+
+}
+
+char *xstrndup (const char *__p, unsigned long __len) {
+
+    char *p = xmalloc (__len + 1);
+    
+    strncpy (p, __p, __len);
+    return p;
+
+}
+
+void add_include_path (const char *__p) {
+
+    char *in = xstrdup (__p);
+    char *temp = in, *p;
+    
+    do {
+    
+        for (p = temp; *p != '\0' && *p != PATHSEP; p++) {
+        
+            if (*p == '\\') {
+                *p = '/';
+            }
+        
+        }
+        
+        if ((p - temp) > 0) {
+        
+            unsigned int len = (p - temp);
+            char *path;
+            
+            if (*(p - 1) != '/') {
+            
+                path = xmalloc (2 + (p - temp) + 2);
+                sprintf (path, "-I%.*s/", len, temp);
+            
+            } else {
+            
+                path = xmalloc (2 + (p - temp) + 1);
+                sprintf (path, "-I%.*s", len, temp);
+            
+            }
+            
+            list_append (&state->pplist, path);
+        
+        }
+        
+        temp = (p + 1);
+    
+    } while (*p != '\0');
+    
+    free (in);
+
+}
+
+void dynarray_add (void *ptab, long *nb_ptr, void *data) {
+
+    long nb, nb_alloc;
+    void **pp;
+    
+    nb = *nb_ptr;
+    pp = *(void ***) ptab;
+    
+    if ((nb & (nb - 1)) == 0) {
+    
+        if (!nb) {
+            nb_alloc = 1;
+        } else {
+            nb_alloc = nb * 2;
+        }
+        
+        pp = xrealloc (pp, nb_alloc * sizeof (void *));
+        *(void ***) ptab = pp;
+    
+    }
+    
+    pp[nb++] = data;
+    *nb_ptr = nb;
+
+}
+
+void parse_args (int argc, char **argv, int optind) {
+
+    struct cc_option *popt;
+    const char *optarg, *r;
+    
+    if (argc <= optind) {
+    
+        print_usage ();
+        exit (EXIT_SUCCESS);
+    
+    }
+    
+    while (optind < argc) {
+    
+        r = argv[optind++];
+        
+        if (r[0] != '-' || r[1] == '\0') {
+        
+            if (state->ifile) {
+            
+                report_at (program_name, 0, REPORT_ERROR, "more than one file passed as input");
+                exit (EXIT_FAILURE);
+            
+            }
+            
+            state->ifile = xstrdup (r);
+            continue;
+        
+        }
+        
+        for (popt = opts; ; popt++) {
+        
+            const char *p1 = popt->name;
+            const char *r1 = r;
+            
+            if (!p1) {
+            
+                report_at (program_name, 0, REPORT_ERROR, "invalid option -- '%s'", r);
+                exit (EXIT_FAILURE);
+            
+            }
+            
+            if (!strstart (p1, &r1)) {
+                continue;
+            }
+            
+            optarg = r1;
+            
+            if (popt->flags & CC_OPTION_HAS_ARG) {
+            
+                if (!(popt->flags & ~CC_OPTION_HAS_ARG)) {
+                
+                    if (*r1 == '\0') {
+                    
+                        if (optind >= argc) {
+                        
+                            report_at (program_name, 0, REPORT_ERROR, "argument to '%s' is missing", r);
+                            exit (EXIT_FAILURE);
+                        
+                        }
+                        
+                        optarg = argv[optind++];
+                    
+                    }
+                
+                } else {
+                
+                    if (*r1 != '=') {
+                    
+                        report_at (program_name, 0, REPORT_ERROR, "argument to '%s' is missing", r);
+                        exit (EXIT_FAILURE);
+                    
+                    }
+                    
+                    optarg++;
+                
+                }
+            
+            } else if (*r1 != '\0') {
+                continue;
+            }
+            
+            break;
+        
+        }
+        
+        switch (popt->idx) {
+        
+            case CC_OPTION_COMPILE: {
+            
+                state->mode = CC_MODE_COMPILE;
+                break;
+            
+            }
+            
+            case CC_OPTION_DEFINE : {
+            
+                char *arg;
+                
+                if (!strchr (optarg, '=')) {
+                
+                    arg = xmalloc (2 + strlen (optarg) + 3);
+                    sprintf (arg, "-D%s=1", optarg);
+                
+                } else {
+                
+                    arg = xmalloc (2 + strlen (optarg) + 1);
+                    sprintf (arg, "-D%s", optarg);
+                
+                }
+                
+                list_append (&state->pplist, arg);
+                break;
+            
+            }
+            
+            case CC_OPTION_HELP: {
+            
+                print_usage ();
+                exit (EXIT_SUCCESS);
+            
+            }
+            
+            case CC_OPTION_INCLUDE: {
+            
+                add_include_path (optarg);
+                break;
+            
+            }
+            
+            case CC_OPTION_MASM: {
+            
+                if (strcmp (optarg, "intel") == 0) {
+                
+                    state->syntax = ASM_SYNTAX_INTEL;
+                    break;
+                
+                }
+                
+                if (strcmp (optarg, "att") == 0) {
+                
+                    state->syntax = ASM_SYNTAX_ATT;
+                    break;
+                
+                }
+                
+                if (strcmp (optarg, "nasm") == 0) {
+                
+                    state->syntax = ASM_SYNTAX_INTEL | ASM_SYNTAX_NASM;
+                    break;
+                
+                }
+                
+                report_at (program_name, 0, REPORT_ERROR, "unrecognised -masm= option");
+                exit (EXIT_FAILURE);
+            
+            }
+            
+            case CC_OPTION_MLONG64: {
+            
+                state->long64 = 1;
+                break;
+            
+            }
+            
+            case CC_OPTION_NO_LEADING_UNDERSCORE: {
+            
+                state->no_leading_underscore = 1;
+                break;
+            
+            }
+            
+            case CC_OPTION_NO_LINEMARKERS: {
+            
+                state->no_linemarkers = 1;
+                break;
+            
+            }
+            
+            case CC_OPTION_OUTFILE: {
+            
+                if (state->ofile) {
+                
+                    report_at (program_name, 0, REPORT_ERROR, "multiple output files provided");
+                    exit (EXIT_FAILURE);
+                
+                }
+                
+                state->ofile = xstrdup (optarg);
+                break;
+            
+            }
+            
+            case CC_OPTION_PEDANTIC: {
+            
+                state->pedantic = 1;
+                break;
+            
+            }
+            
+            case CC_OPTION_PREPOCESS: {
+            
+                state->mode = CC_MODE_PREPROCESS;
+                break;
+            
+            }
+            
+            case CC_OPTION_STD: {
+            
+                if (strcmp (optarg, "c89") == 0 || strcmp (optarg, "c90") == 0) {
+                
+                    state->std = 90;
+                    break;
+                
+                }
+                
+                report_at (program_name, 0, REPORT_ERROR, "unrecognised -std= version");
+                exit (EXIT_FAILURE);
+            
+            }
+            
+            case CC_OPTION_TRANDITIONAL_LINEMARKERS: {
+            
+                state->traditional_linemarkers = 1;
+                break;
+            
+            }
+            
+            case CC_OPTION_UNDEF: {
+            
+                char *arg = xmalloc (2 + strlen (optarg) + 1);
+                sprintf (arg, "-U%s", optarg);
+                
+                list_append (&state->pplist, arg);
+                break;
+            
+            }
+            
+            default: {
+            
+                report_at (program_name, 0, REPORT_ERROR, "unsupported option '%s'", r);
+                exit (EXIT_FAILURE);
+            
+            }
+        
+        }
+    
+    }
+    
+    if (!state->syntax) {
+        state->syntax = ASM_SYNTAX_MASM | ASM_SYNTAX_INTEL;
+    }
+    
+#if     defined (VERSION)
+    state->version = VERSION;
+#endif
+
+}
+
+void *xmalloc (unsigned long __size) {
+
+    void *ptr = malloc (__size);
+    
+    if (!ptr && __size) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "failed to allocate memory");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    memset (ptr, 0, __size);
+    return ptr;
+
+}
+
+void *xrealloc (void *__ptr, unsigned long __size) {
+
+    void *ptr = realloc (__ptr, __size);
+    
+    if (!ptr && __size) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "failed to reallocate memory)");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    return ptr;
+
+}
+
+
+static const char *filename = 0;
+static unsigned long line_number = 0;
+
+const char *get_filename (void) {
+    return filename;
+}
+
+unsigned long get_line_number (void) {
+    return line_number;
+}
+
+void get_filename_and_line_number (const char **__filename_p, unsigned long *__line_number_p) {
+
+    *__filename_p = filename;
+    *__line_number_p = line_number;
+
+}
+
+void set_filename (const char *__filename) {
+    filename = __filename;
+}
+
+void set_line_number (unsigned long __line_number) {
+    line_number = __line_number;
+}
+
+void set_filename_and_line_number (const char *__filename, unsigned long __line_number) {
+
+    filename = __filename;
+    line_number = __line_number;
+
+}
+
+
+static struct vector vec_pragmas = { 0 };
+
+#if     defined (unix) || defined (__unix) || defined (__unix__) || defined (__APPLE__)
+# include       <sys/stat.h>
+# include       <unistd.h>
+
+struct pragma_entry { unsigned long ino; };
+
+char *get_current_directory (void) {
+
+    static char cwd[4096] = { 0 };
+    memset (cwd, 0, sizeof (cwd));
+    
+    if (getcwd (cwd, sizeof (cwd))) {
+        return xstrdup (cwd);
+    }
+    
+    return 0;
+
+}
+
+int is_pragma_igored (const char *filename) {
+
+    struct stat st_buf;
+    
+    struct pragma_entry *entry;
+    int i;
+    
+    if (stat (filename, &st_buf) >= 0) {
+    
+        for (i = 0; i < vec_pragmas.length; i++) {
+        
+            entry = vec_pragmas.data[i];
+            
+            if (entry->ino == st_buf.st_ino) {
+                return 1;
+            }
+        
+        }
+        
+        entry = xmalloc (sizeof (*entry));
+        entry->ino = st_buf.st_ino;
+        
+        vec_push (&vec_pragmas, entry);
+    
+    }
+    
+    return 0;
+
+}
+#elif       defined (_WIN32)
+# include       <windows.h>
+
+struct pragma_entry {
+
+    DWORD    dwVolumeSerialNumber;
+    
+    DWORD    nFileSizeHigh;
+    DWORD    nFileSizeLow;
+
+};
+
+char *get_current_directory (void) {
+
+    static TCHAR tszBuffer[4096];
+    size_t i;
+    
+    DWORD dwRet;
+    
+    if ((dwRet = GetCurrentDirectory (sizeof (tszBuffer), tszBuffer)) == 0) {
+        return 0;
+    }
+    
+    for (i = 0; i < strlen (tszBuffer); i++) {
+    
+        if (tszBuffer[i] == '\\') {
+            tszBuffer[i] = '/';
+        }
+    
+    }
+    
+    return xstrdup (tszBuffer);
+
+}
+
+int is_pragma_igored (const char *filename) {
+
+    HANDLE handle;
+    
+    struct pragma_entry *entry;
+    int i;
+    
+    BY_HANDLE_FILE_INFORMATION file_info;
+    
+    if ((handle = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) {
+        return 0;
+    }
+    
+    if (GetFileInformationByHandle (handle, &file_info)) {
+    
+        for (i = 0; i < vec_pragmas.length; i++) {
+        
+            entry = vec_pragmas.data[i];
+            
+            if (entry->dwVolumeSerialNumber == file_info.dwVolumeSerialNumber) {
+            
+                if (entry->nFileSizeHigh == file_info.nFileSizeHigh && entry->nFileSizeLow == file_info.nFileSizeLow) {
+                
+                    CloseHandle (handle);
+                    return 1;
+                
+                }
+            
+            }
+        
+        }
+        
+        entry = xmalloc (sizeof (*entry));
+        
+        entry->dwVolumeSerialNumber = file_info.dwVolumeSerialNumber;
+        entry->nFileSizeHigh = file_info.nFileSizeHigh;
+        entry->nFileSizeLow = file_info.nFileSizeLow;
+        
+        vec_push (&vec_pragmas, entry);
+    
+    }
+    
+    CloseHandle (handle);
+    return 0;
+
+}
+#else
+char *get_current_directory (void) {
+    return xstrdup ("");
+}
+
+int is_pragma_igored (const char *filename) {
+
+    const char *entry;
+    int i;
+    
+    for (i = 0; i < vec_pragmas.length; i++) {
+    
+        entry = vec_pragmas.data[i];
+        
+        if (strcmp (filename, entry) == 0) {
+            return 1;
+        }
+    
+    }
+    
+    vec_push (&vec_pragmas, xstrdup (filename));
+    return 0;
+
+}
+#endif
+
+
+const char *real_filename = 0;
+static unsigned long real_line_number = 0;
+
+const char *get_real_filename (void) {
+    return real_filename;
+}
+
+unsigned long get_real_line_number (void) {
+    return real_line_number;
+}
+
+void set_real_filename (const char *__filename) {
+    real_filename = __filename;
+}
+
+void set_real_line_number (unsigned long __line_number) {
+    real_line_number = __line_number;
+}
+
+
+int has_include_stack (void) {
+    return state->vec_hist.length > 0;
+}
+
+void print_include_stack (void) {
+
+    struct hist_entry *entry;
+    int i;
+    
+    printf ("in file included ");
+    
+    for (i = state->vec_hist.length - 1; i >= 0; i--) {
+        
+        entry = state->vec_hist.data[i];
+        printf ("from %s:%lu", entry->filename, entry->line_number);
+        
+        if (i > 0) {
+            
+            printf (",\n");
+            printf ("                 ");
+        
+        } else {
+            printf ("\n");
+        }
+    
+    }
+
+}
diff --git a/lib.h b/lib.h
new file mode 100755 (executable)
index 0000000..2dec618
--- /dev/null
+++ b/lib.h
@@ -0,0 +1,51 @@
+/******************************************************************************
+ * @file            lib.h
+ *****************************************************************************/
+#ifndef     _LIB_H
+#define     _LIB_H
+
+struct hist_entry {
+
+    const char *filename;
+    unsigned long line_number;
+
+};
+
+char *xstrdup (const char *__p);
+char *xstrndup (const char *__p, unsigned long __len);
+
+void *xmalloc (unsigned long __size);
+void *xrealloc (void *__ptr, unsigned long __size);
+
+char *skip_whitespace (char *__p);
+char *symname (char **pp);
+
+void add_include_path (const char *__p);
+void dynarray_add (void *ptab, long *nb_ptr, void *data);
+void parse_args (int argc, char **argv, int optind);
+
+
+const char *get_filename (void);
+unsigned long get_line_number (void);
+
+void set_filename (const char *__filename);
+void set_line_number (unsigned long __line_number);
+
+void set_filename_and_line_number (const char *__filename, unsigned long __line_number);
+
+
+const char *get_real_filename (void);
+unsigned long get_real_line_number (void);
+
+void set_real_filename (const char *__filename);
+void set_real_line_number (unsigned long __line_number);
+
+
+int has_include_stack (void);
+void print_include_stack (void);
+
+
+char *get_current_directory (void);
+int is_pragma_igored (const char *filename);
+
+#endif      /* _LIB_H */
diff --git a/list.c b/list.c
new file mode 100755 (executable)
index 0000000..a7e475b
--- /dev/null
+++ b/list.c
@@ -0,0 +1,39 @@
+/******************************************************************************
+ * @file            list.c
+ *****************************************************************************/
+#include    "lib.h"
+#include    "list.h"
+
+void list_append (struct list **list, void *data) {
+
+    struct list *new = xmalloc (sizeof (*new));
+    struct list *old = (*list);
+    
+    if (old) {
+    
+        new->next = old->next;
+        old->next = new;
+    
+    } else {
+        new->next = new;
+    }
+    
+    new->data = data;
+    *list = new;
+
+}
+
+unsigned int nlist (struct list *list) {
+
+    unsigned int n = 0;
+    
+    if (list) {
+    
+        struct list *p = list;
+        do { n++; } while ((p = p->next) != list);
+    
+    }
+    
+    return n;
+
+}
diff --git a/list.h b/list.h
new file mode 100755 (executable)
index 0000000..4825c64
--- /dev/null
+++ b/list.h
@@ -0,0 +1,16 @@
+/******************************************************************************
+ * @file            list.h
+ *****************************************************************************/
+#ifndef     _LIST_H
+#define     _LIST_H
+
+struct list {
+
+    struct list *next;
+    void *data;
+
+};
+
+void list_append (struct list **list, void *data);
+
+#endif      /* _LIST_H */
diff --git a/ll.c b/ll.c
new file mode 100755 (executable)
index 0000000..b01656c
--- /dev/null
+++ b/ll.c
@@ -0,0 +1,388 @@
+/******************************************************************************
+ * @file            ll.c
+ *****************************************************************************/
+#include    <stddef.h>
+#include    <stdio.h>
+#include    <stdlib.h>
+#include    <string.h>
+
+#include    "cc.h"
+#include    "ll.h"
+#include    "report.h"
+
+struct load_line_data {
+
+    char *line, *real_line;
+    
+    unsigned long capacity, read_size;
+    unsigned long end_of_prev_real_line;
+    
+    unsigned long *new_line_number_p;
+
+};
+
+#define     CAPACITY_INCREMENT          256
+extern void get_filename_and_line_number (const char **__filename_p, unsigned long *__line_number_p);
+
+extern void *xmalloc (unsigned int __size);
+extern void *xrealloc (void *__ptr, unsigned int __size);
+
+int load_line (char **line_p, char **line_end_p, char **real_line_p, unsigned long *real_line_len_p, unsigned long *newlines_p, FILE *ifp, void **load_line_internal_data_p) {
+
+    struct load_line_data *ll_data = *load_line_internal_data_p;
+    unsigned int pos_in_line = 0, pos_in_real_line = 0, newlines = 0;
+    
+    int in_escape = 0, in_double_quote = 0, in_single_quote = 0;
+    int in_block_comment = 0, in_line_comment = 0, possible_start_or_end_of_comment = 0, skipping_spaces = 0;
+    
+    if (ll_data->end_of_prev_real_line) {
+    
+        memmove (ll_data->real_line, ll_data->real_line + ll_data->end_of_prev_real_line, ll_data->read_size - ll_data->end_of_prev_real_line);
+        ll_data->read_size -= ll_data->end_of_prev_real_line;
+    
+    }
+    
+    while (1) {
+    
+        if (pos_in_line >= ll_data->capacity || pos_in_real_line >= ll_data->capacity) {
+        
+            ll_data->capacity += CAPACITY_INCREMENT;
+            
+            ll_data->line = xrealloc (ll_data->line, ll_data->capacity + 2);
+            ll_data->real_line = xrealloc (ll_data->real_line, ll_data->capacity + 1);
+        
+        }
+        
+        if (pos_in_real_line >= ll_data->read_size) {
+        
+            ll_data->read_size = fread (ll_data->real_line + pos_in_real_line, 1, ll_data->capacity - pos_in_real_line, ifp) + pos_in_real_line;
+            
+            if (ferror (ifp)) {
+                return 4;
+            }
+            
+            ll_data->real_line[ll_data->read_size] = '\0';
+        
+        }
+        
+    copying:
+        
+        if (in_block_comment) {
+        
+            while (pos_in_real_line < ll_data->read_size) {
+            
+                if (possible_start_or_end_of_comment && ll_data->real_line[pos_in_real_line] == '/') {
+                
+                    possible_start_or_end_of_comment = 0;
+                    pos_in_real_line++;
+                    
+                    in_block_comment = 0;
+                    break;
+                
+                }
+                
+                possible_start_or_end_of_comment = 0;
+                
+                if (ll_data->real_line[pos_in_real_line] == '*') {
+                    possible_start_or_end_of_comment = 1;
+                }
+                
+                if (ll_data->real_line[pos_in_real_line] == '\n') {
+                    newlines++;
+                }
+                
+                pos_in_real_line++;
+            
+            }
+        
+        }
+        
+        if (in_line_comment) {
+        
+            while (pos_in_real_line < ll_data->read_size) {
+            
+                if (ll_data->real_line[pos_in_real_line] == '\n') {
+                
+                    in_line_comment = 0;
+                    break;
+                
+                }
+                
+                pos_in_real_line++;
+            
+            }
+        
+        }
+        
+        if (skipping_spaces) {
+        
+            while (pos_in_real_line < ll_data->read_size) {
+            
+                if (ll_data->real_line[pos_in_real_line] != ' ' && ll_data->real_line[pos_in_real_line] != '\t') {
+                
+                    skipping_spaces = 0;
+                    break;
+                
+                }
+                
+                pos_in_real_line++;
+            
+            }
+        
+        }
+        
+        while (pos_in_real_line < ll_data->read_size && pos_in_line < ll_data->capacity) {
+        
+            ll_data->line[pos_in_line] = ll_data->real_line[pos_in_real_line++];
+            
+            if (in_double_quote || in_single_quote) {
+            
+                if (in_escape) {
+                    in_escape = 0;
+                } else if (in_double_quote && ll_data->line[pos_in_line] == '"') {
+                    in_double_quote = 0;
+                } else if (in_single_quote && ll_data->line[pos_in_line] == '\'') {
+                    in_single_quote = 0;
+                } else if (ll_data->line[pos_in_line] == '\\') {
+                    in_escape = 1;
+                }
+                
+                if (ll_data->line[pos_in_line] == '\n') {
+                
+                    int pos = pos_in_line;
+                    
+                    if (pos > 0 && ll_data->line[pos - 1] == '\r') {
+                        ll_data->line[--pos] = '\n';
+                    }
+                    
+                    if (pos > 0) {
+                    
+                        if (ll_data->line[pos - 1] != '\\') {
+                    
+                            ll_data->line[pos + 1] = '\0';
+                            ll_data->end_of_prev_real_line = pos_in_real_line;
+                            
+                            *line_p = ll_data->line;
+                            *line_end_p = ll_data->line + pos;
+                            
+                            if (real_line_p) {
+                                *real_line_p = ll_data->real_line;
+                            }
+                            
+                            if (real_line_len_p) {
+                                *real_line_len_p = pos_in_real_line;
+                            }
+                            
+                            *newlines_p = newlines;
+                            return 0;
+                        
+                        } else {
+                        
+                            pos_in_line = pos - 1;
+                            
+                            newlines++;
+                            goto copying;
+                        
+                        }
+                    
+                    }
+                
+                }
+            
+            } else {
+            
+                if (possible_start_or_end_of_comment) {
+                
+                    if (ll_data->line[pos_in_line] == '*') {
+                    
+                        possible_start_or_end_of_comment = 0;
+                        ll_data->line[pos_in_line - 1] = ' ';
+                        
+                        in_block_comment = 1;
+                        goto copying;
+                    
+                    } else if (ll_data->line[pos_in_line] == '/') {
+                    
+                        possible_start_or_end_of_comment = 0;
+                        
+                        if (state->std != 90) {
+                        
+                            ll_data->line[pos_in_line - 1] = ' ';
+                            
+                            in_line_comment = 1;
+                            goto copying;
+                        
+                        }
+                    
+                    }
+                
+                }
+                
+                possible_start_or_end_of_comment = 0;
+                
+                if (ll_data->line[pos_in_line] == ' ' || ll_data->line[pos_in_line] == '\t') {
+                
+                    /*ll_data->line[pos_in_line++] = ' ';*/
+                    
+                    /*skipping_spaces = 1;
+                    goto copying;*/
+                    
+                    if (ll_data->line[pos_in_line] == '\t') {
+                    
+                        int cnt = 4 - (pos_in_line % 4);
+                        int i = 0;
+                        
+                        ll_data->capacity += CAPACITY_INCREMENT;
+                        
+                        ll_data->line = xrealloc (ll_data->line, ll_data->capacity + 2);
+                        ll_data->real_line = xrealloc (ll_data->real_line, ll_data->capacity + 1);
+                        
+                        for (; i < cnt; i++) {
+                            ll_data->line[pos_in_line++] = ' ';
+                        }
+                        
+                        continue;
+                    
+                    }
+                
+                } else if (ll_data->line[pos_in_line] == '\n') {
+                
+                    if (pos_in_line > 0 && ll_data->line[pos_in_line - 1] == '\r') {
+                        ll_data->line[--pos_in_line] = '\n';
+                    }
+                    
+                    ll_data->line[pos_in_line + 1] = '\0';
+                    ll_data->end_of_prev_real_line = pos_in_real_line;
+                    
+                    *line_p = ll_data->line;
+                    *line_end_p = ll_data->line + pos_in_line;
+                    
+                    if (real_line_p) {
+                        *real_line_p = ll_data->real_line;
+                    }
+                    
+                    if (real_line_len_p) {
+                        *real_line_len_p = pos_in_real_line;
+                    }
+                    
+                    *newlines_p = newlines;
+                    return 0;
+                
+                } else if (ll_data->line[pos_in_line] == '\\') {
+                
+                    /*ll_data->line[pos_in_line] = ' ';*/
+                    /*pos_in_line--;*/
+                    
+                    while (pos_in_real_line < ll_data->read_size) {
+                    
+                        if (ll_data->real_line[pos_in_real_line] == '\r' || ll_data->real_line[pos_in_real_line] == '\n') {
+                        
+                            if (ll_data->real_line[pos_in_real_line] == '\r') {
+                                pos_in_real_line++;
+                            }
+                            
+                            if (ll_data->real_line[pos_in_real_line] == '\n') {
+                                pos_in_real_line++;
+                            }
+                            
+                            break;
+                        
+                        }
+                        
+                        pos_in_real_line++;
+                    
+                    }
+                    
+                    newlines++;
+                    continue;
+                
+                } else if (ll_data->line[pos_in_line] == '"') {
+                    in_double_quote = 1;
+                } else if (ll_data->line[pos_in_line] == '\'') {
+                    in_single_quote = 1;
+                } else if (ll_data->line[pos_in_line] == '/') {
+                    possible_start_or_end_of_comment = 1;
+                }
+            
+            }
+            
+            pos_in_line++;
+        
+        }
+        
+        if (feof (ifp)) {
+        
+            const char *filename = 0;
+            unsigned long line_number = 0;
+            
+            if (ll_data->read_size == 0) {
+                return 1;
+            }
+            
+            ll_data->line[pos_in_line] = '\n';
+            ll_data->line[pos_in_line + 1] = '\0';
+            
+            get_filename_and_line_number (&filename, &line_number);
+            
+            if (ll_data->new_line_number_p) {
+                line_number = *(ll_data->new_line_number_p);
+            } else {
+                line_number = 0;
+            }
+            
+            report_at (filename, line_number, REPORT_WARNING, "end of file not at end of line; newline inserted");
+            
+            ll_data->end_of_prev_real_line = 0;
+            ll_data->read_size = 0;
+            
+            *line_p = ll_data->line;
+            *line_end_p = ll_data->line + pos_in_line;
+            
+            if (real_line_p) {
+                *real_line_p = ll_data->real_line;
+            }
+            
+            if (real_line_len_p) {
+                *real_line_len_p = pos_in_real_line;
+            }
+            
+            *newlines_p = newlines;
+            return 0;
+        
+        }
+    
+    }
+
+}
+
+void load_line_destroy_internal_data (void *load_line_internal_data) {
+
+    struct load_line_data *ll_data;
+    
+    if (load_line_internal_data) {
+    
+        ll_data = load_line_internal_data;
+        
+        free (ll_data->line);
+        free (ll_data->real_line);
+        free (ll_data);
+    
+    }
+
+}
+
+void *load_line_create_internal_data (unsigned long *new_line_number_p) {
+
+    struct load_line_data *ll_data = xmalloc (sizeof (*ll_data));;
+    
+    ll_data->capacity = 0;
+    ll_data->line = NULL;
+    ll_data->real_line = NULL;
+    
+    ll_data->read_size = 0;
+    ll_data->end_of_prev_real_line = 0;
+    
+    ll_data->new_line_number_p = new_line_number_p;
+    return ll_data;
+
+}
diff --git a/ll.h b/ll.h
new file mode 100755 (executable)
index 0000000..b6754ef
--- /dev/null
+++ b/ll.h
@@ -0,0 +1,13 @@
+/******************************************************************************
+ * @file            ll.h
+ *****************************************************************************/
+#ifndef     _LL_H
+#define     _LL_H
+
+#include    <stdio.h>
+int load_line (char **line_p, char **line_end_p, char **real_line_p, unsigned long *real_line_len_p, unsigned long *newlines_p, FILE *ifp, void **load_line_internal_data_p);
+
+void load_line_destroy_internal_data (void *load_line_internal_data);
+void *load_line_create_internal_data (unsigned long *new_line_number_p);
+
+#endif      /* _LL_H */
diff --git a/macro.c b/macro.c
new file mode 100755 (executable)
index 0000000..4aa45fa
--- /dev/null
+++ b/macro.c
@@ -0,0 +1,689 @@
+/******************************************************************************
+ * @file            macro.c
+ *****************************************************************************/
+#include    <ctype.h>
+#include    <stdlib.h>
+#include    <string.h>
+
+#include    "cc.h"
+#include    "cstr.h"
+#include    "hashtab.h"
+#include    "lex.h"
+#include    "lib.h"
+#include    "macro.h"
+#include    "report.h"
+#include    "vector.h"
+
+static struct hashtab hashtab_macros = { 0 };
+
+struct hashtab_name *find_macro (char *sname) {
+
+    struct hashtab_name *key;
+    
+    if ((key = hashtab_get_key (&hashtab_macros, sname))) {
+        return key;
+    }
+    
+    return 0;
+
+}
+
+struct macro *get_macro (struct hashtab_name *key) {
+    return hashtab_get (&hashtab_macros, key);
+}
+
+void add_macro (char *start, char **pp, int report_line) {
+
+    char *sname, *caret = *pp, *arg;
+    unsigned int len;
+    
+    struct hashtab_name *key;
+    struct macro *m;
+    
+    if (is_end_of_line[(int) **pp]) {
+    
+        if (report_line) {
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret - 1, "no macro name give in #define directive");
+        } else {
+            report_at (get_filename (), get_line_number (), REPORT_ERROR, "no macro name give in #define directive");
+        }
+        
+        return;
+    
+    }
+    
+    if (!(sname = symname (pp))) {
+    
+        if (report_line) {
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "macro names must be identifiers");
+        } else {
+            report_at (get_filename (), get_line_number (), REPORT_ERROR, "macro names must be identifiers");
+        }
+        
+        return;
+    
+    }
+    
+    if (strcmp (sname, "defined") == 0) {
+    
+        if (report_line) {
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "\"%s\" cannout be used as a macro name", sname);
+        } else {
+            report_at (get_filename (), get_line_number (), REPORT_ERROR, "\"%s\" cannout be used as a macro name", sname);
+        }
+        
+        return;
+    
+    }
+    
+    if (**pp != '(' && !isspace ((int) **pp)) {
+    
+        if (report_line) {
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp, "whitespace is required after macro name");
+        } else {
+            report_at (get_filename (), get_line_number (), REPORT_ERROR, "whitespace is required after macro name");
+        }
+    
+        return;
+    
+    }
+    
+    if ((key = find_macro (sname))) {
+    
+        if (report_line) {
+            report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, caret, "\"%s\" redefined", sname);
+        } else {
+            report_at (get_filename (), get_line_number (), REPORT_WARNING, "\"%s\" redefined", sname);
+        }
+        
+        if ((m = hashtab_get (&hashtab_macros, key))) {
+        
+            while ((arg = vec_pop (&m->args))) {
+                free (arg);
+            }
+            
+            free (m);
+        
+        }
+        
+        hashtab_remove (&hashtab_macros, key);
+    
+    } else {
+    
+        if (!(key = hashtab_alloc_name (sname))) {
+        
+            free (sname);
+            return;
+        
+        }
+    
+    }
+    
+    m = xmalloc (sizeof (*m));
+    m->nargs = -1;
+    
+    m->type = MACRO_USER;
+    m->name = sname;
+    
+    if (**pp == '(') {
+    
+        m->nargs = 0;
+        (*pp)++;
+        
+        while (!is_end_of_line[(int) **pp]) {
+        
+            *pp = skip_whitespace (*pp);
+            
+            if (**pp == ')') {
+                break;
+            }
+            
+            if (m->is_variadic) {
+            
+                if (report_line) {
+                    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp, "expected ')' after '...'");
+                } else {
+                    report_at (get_filename (), get_line_number (), REPORT_ERROR, "expected ')' after '...'");
+                }
+                
+                while ((arg = vec_pop (&m->args))) {
+                    free (arg);
+                }
+                
+                free (m);
+                return;
+            
+            }
+            
+            arg = *pp;
+            
+            while (!is_end_of_line[(int) *arg] && !isspace ((int) *arg)) {
+            
+                if (*arg == ',' || *arg == ')') {
+                    break;
+                }
+                
+                arg++;
+            
+            }
+            
+            if (arg - *pp == 3) {
+            
+                if (memcmp (*pp, "...", 3) == 0) {
+                
+                    if (state->std == 90) {
+                        report_line_at (get_filename (), get_line_number (), state->pedantic ? REPORT_ERROR : REPORT_WARNING, start, *pp, "anonymous variadic macros were introduced in C99");
+                    }
+                    
+                    m->is_variadic = 1;
+                    
+                    *pp = arg;
+                    continue;
+                
+                }
+            
+            }
+            
+            if ((sname = symname (pp))) {
+            
+                vec_push (&m->args, sname);
+                m->nargs++;
+                
+                *pp = skip_whitespace (*pp);
+                
+                if (**pp != ',' && **pp != ')') {
+                
+                    if (report_line) {
+                        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp, "expected ',' or, ')' after parameter");
+                    } else {
+                        report_at (get_filename (), get_line_number (), REPORT_ERROR, "expected ',' or, ')' after parameter");
+                    }
+                    
+                    goto err;
+                
+                }
+                
+                if (**pp == ')') {
+                    break;
+                }
+                
+                (*pp)++;
+                continue;
+            
+            }
+            
+            if (report_line) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp, "expected parameter name");
+            } else {
+                report_at (get_filename (), get_line_number (), REPORT_ERROR, "expected parameter name");
+            }
+            
+            goto err;
+        
+        }
+        
+        if (**pp != ')') {
+        
+            if (report_line) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp, "expected ')' before end of line");
+            } else {
+                report_at (get_filename (), get_line_number (), REPORT_ERROR, "expected ')' before end of line");
+            }
+            
+            goto err;
+        
+        }
+        
+        (*pp)++;
+    
+    }
+    
+    *pp = skip_whitespace (*pp);
+    
+    m->value = xstrdup (*pp);
+    len = strlen (m->value);
+    
+    if (is_end_of_line[(int) m->value[len - 1]]) {
+        m->value[len - 1] = '\0';
+    }
+    
+    hashtab_put (&hashtab_macros, key, m);
+    
+    if (!m->is_variadic) {
+    
+        char *haystack = m->value, *needle = "__VA_ARGS__";
+        char *p;
+        
+        while ((p = strstr (haystack, needle))) {
+        
+            haystack = (p + strlen (needle));
+            
+            if (report_line) {
+                report_line_at (get_filename (), get_line_number (), REPORT_WARNING, m->value, p, "%s can only appear in the expansion of a variadic macro", needle);
+            } else {
+                report_at (get_filename (), get_line_number (), REPORT_WARNING, "%s can only appear in the expansion of a variadic macro", needle);
+            }
+        
+        }
+    
+    }
+    
+    return;
+    
+err:
+    
+    while ((arg = vec_pop (&m->args))) {
+        free (arg);
+    }
+    
+    free (m);
+    return;
+
+}
+
+void remove_macro (char *start, char **pp, int report_line) {
+
+    char *sname, *caret = *pp;
+    
+    struct hashtab_name *key;
+    struct macro *mp;
+    
+    if (!(sname = symname (pp))) {
+    
+        if (!(sname = symname (pp))) {
+    
+        if (report_line) {
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "macro names must be identifiers");
+        } else {
+            report_at (get_filename (), get_line_number (), REPORT_ERROR, "macro names must be identifiers");
+        }
+        
+        return;
+    
+    }
+        
+        return;
+    
+    }
+    
+    if ((key = find_macro (sname))) {
+    
+        if ((mp = hashtab_get (&hashtab_macros, key))) {
+            free (mp);
+        }
+        
+        hashtab_remove (&hashtab_macros, key);
+    
+    }
+    
+    *pp = skip_whitespace (*pp);
+    
+    if (!is_end_of_line[(int) **pp]) {
+    
+        if (report_line) {
+            report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, *pp, "extra tokens at end of #undef directive");
+        } else {
+            report_at (get_filename (), get_line_number (), REPORT_WARNING, "extra tokens at end of #undef directive");
+        }
+    
+    }
+
+}
+
+static struct vector *get_macro_args (char *start, char *macro_name, char **pp) {
+
+    static struct vector args_list = { 0 };
+    
+    char *arg, saved_ch, ch;
+    int depth;
+    
+    memset (&args_list, 0, sizeof (args_list));
+    
+    if (**pp != '(') {
+        return 0;
+    }
+    
+    (*pp)++;
+    
+    for (;;) {
+    
+        *pp = skip_whitespace (*pp);
+        
+        if (is_end_of_line[(int) **pp] || **pp == ')') {
+            break;
+        }
+        
+        arg = *pp;
+        depth = 0;
+        
+        while (!is_end_of_line[(int) **pp]) {
+        
+            ch = **pp;
+            
+            if (ch == '"' || ch == '\'') {
+            
+                (*pp)++;
+                
+                while (!is_end_of_line[(int) **pp]) {
+                
+                    if (**pp == '\\') {
+                    
+                        (*pp)++;
+                        
+                        if (!is_end_of_line[(int) **pp]) {
+                            (*pp)++;
+                        }
+                        
+                        continue;
+                    
+                    }
+                    
+                    if (**pp == ch) {
+                    
+                        (*pp)++;
+                        break;
+                    
+                    }
+                    
+                    (*pp)++;
+                
+                }
+                
+                continue;
+            
+            }
+            
+            if (ch == '(') {
+            
+                depth++;
+                
+                (*pp)++;
+                continue;
+            
+            }
+            
+            if (ch == ')') {
+            
+                if (depth == 0) {
+                    break;
+                }
+                
+                depth--;
+                
+                (*pp)++;
+                continue;
+            
+            }
+            
+            if (ch == ',' && depth == 0) {
+                break;
+            }
+            
+            (*pp)++;
+        
+        }
+        
+        while (*pp > arg && ((*pp)[-1] == ' ' || (*pp)[-1] == '\t')) {
+            (*pp)--;
+        }
+        
+        saved_ch = **pp;
+        **pp = '\0';
+        
+        vec_push (&args_list, xstrdup (arg));
+        **pp = saved_ch;
+        
+        if (*(*pp = skip_whitespace (*pp)) == ',') {
+            (*pp)++;
+        }
+    
+    }
+    
+    if (**pp != ')') {
+    
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp, "unterminated argument list invoking macro \"%s\"", macro_name);
+        return 0;
+    
+    }
+    
+    (*pp)++;
+    return &args_list;
+
+}
+
+static char *process_value (struct macro *m, struct vector *args_list) {
+
+    char *line, *start, *caret, *arg;
+    int stringify = 0, len, i, j;
+    
+    CString str;
+    cstr_new (&str);
+    
+    start = (line = skip_whitespace (m->value));
+    
+again:
+    
+    while (!is_end_of_line[(int) *line]) {
+    
+        caret = line;
+        
+        if (*line == '\"' || *line == '\'') {
+        
+            char quote = *line;
+            cstr_ccat (&str, *line++);
+            
+            while (!is_end_of_line[(int) *line]) {
+            
+                cstr_ccat (&str, *line);
+                
+                if (*line == '\\') {
+                
+                    line++;
+                    
+                    if (!is_end_of_line[(int) *line]) {
+                        cstr_ccat (&str, *line);
+                    }
+                    
+                } else if (*line == quote) {
+                
+                    line++;
+                    break;
+                
+                }
+                
+                line++;
+            
+            }
+            
+            continue;
+        
+        }
+        
+        if (line[0] == '#') {
+        
+            if (stringify) {
+            
+                cstr_ccat (&str, '"');
+                stringify = 0;
+            
+            }
+            
+            if (line[1] == '#') {
+            
+                while (str.size && ((char *) str.data)[str.size - 1] == ' ') {
+                    str.size--;
+                }
+                
+                line = skip_whitespace (line + 2);
+            
+            } else {
+            
+                line = skip_whitespace (line + 1);
+                stringify = 1;
+            
+            }
+            
+            continue;
+        
+        }
+        
+        if (is_name_beginner ((int) *line)) {
+        
+            len = strlen (arg = symname (&line));
+            
+            if (args_list) {
+            
+                if (strcmp (arg, "__VA_ARGS__") == 0) {
+                
+                    if (stringify) {
+                        cstr_ccat (&str, '"');
+                    }
+                    
+                    for (i = m->nargs; i < args_list->length; i++) {
+                    
+                        len = strlen (arg = args_list->data[i]);
+                        
+                        for (j = 0; j < len; j++ ) {
+                        
+                            if (arg[j] == '"') {
+                            
+                                cstr_ccat (&str, '\\');
+                                cstr_ccat (&str, '"');
+                            
+                            } else {
+                                cstr_ccat (&str, arg[j]);
+                            }
+                        
+                        }
+                        
+                        if (i < args_list->length - 1) {
+                        
+                            cstr_ccat (&str, ',');
+                            cstr_ccat (&str, ' ');
+                        
+                        }
+                    
+                    }
+                    
+                    if (stringify) {
+                    
+                        cstr_ccat (&str, '"');
+                        stringify = 0;
+                    
+                    }
+                    
+                    continue;
+                
+                }
+                
+                for (i = 0; i < m->nargs; i++) {
+                
+                    if (strcmp (m->args.data[i], arg) == 0) {
+                    
+                        len = strlen (arg = args_list->data[i]);
+                        
+                        if (stringify) {
+                        
+                            cstr_ccat (&str, '"');
+                            
+                            for (i = 0; i < len; i++ ) {
+                            
+                                if (arg[i] == '"') {
+                                
+                                    cstr_ccat (&str, '\\');
+                                    cstr_ccat (&str, '"');
+                                
+                                } else {
+                                    cstr_ccat (&str, arg[i]);
+                                }
+                            
+                            }
+                            
+                            cstr_ccat (&str, '"');
+                            stringify = 0;
+                        
+                        } else {
+                            cstr_cat (&str, arg, len);
+                        }
+                        
+                        goto again;
+                    
+                    }
+                
+                }
+            
+            }
+            
+            if (stringify) {
+            
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "'#' is not followed by a macro paremeter");
+                break;
+            
+            }
+            
+            cstr_cat (&str, arg, len);
+        
+        } else {
+        
+            if (isprint ((int) *line) && stringify) {
+            
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "'#' is not followed by a macro paremeter");
+                break;
+            
+            }
+            
+            cstr_ccat (&str, *line);
+            line++;
+        
+        }
+    
+    }
+    
+    cstr_ccat (&str, '\0');
+    return xstrdup (str.data);
+
+}
+
+char *process_macro (char *start, char **pp, struct macro *m) {
+
+    struct vector *args_list = 0;
+    char *caret;
+    
+    if (m->nargs >= 0 || m->is_variadic) {
+    
+        args_list = 0;
+        caret = *pp;
+        
+        if ((args_list = get_macro_args (start, m->name, pp))) {
+        
+            if (args_list->length < m->nargs) {
+            
+                char *tmp = (m->nargs == 1 ? " argument" : " arguments");
+                char *tmp2 = (args_list->length == 1 ? "only " : "");
+                
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "macro \"%s\" requires %d%s, but %s%d given", m->name, m->nargs, tmp, tmp2, args_list->length);
+            
+            } else if (args_list->length > m->nargs) {
+            
+                if (!m->is_variadic) {
+                
+                    char *tmp = (args_list->length == 1 ? " argument" : " arguments");
+                    char *tmp2 = (m->nargs == 1 ? "just " : "");
+                    
+                    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "macro \"%s\" passed %d%s, but takes %s%d", m->name, args_list->length, tmp, tmp2, m->nargs);
+                
+                }
+            
+            }
+        
+        }
+    
+    }
+    
+    return process_value (m, args_list);
+
+}
+
+void push_macro (struct hashtab_name *key, struct macro *m) {
+    hashtab_put (&hashtab_macros, key, m);
+}
diff --git a/macro.h b/macro.h
new file mode 100755 (executable)
index 0000000..68f1095
--- /dev/null
+++ b/macro.h
@@ -0,0 +1,32 @@
+/******************************************************************************
+ * @file            macro.h
+ *****************************************************************************/
+#ifndef     _MACRO_H
+#define     _MACRO_H
+
+#include    "vector.h"
+
+#define     MACRO_BUILTIN               0
+#define     MACRO_USER                  1
+
+struct macro {
+
+    char *name, *value;
+    int is_variadic, type;
+    
+    struct vector args;
+    int nargs;
+
+};
+
+#include    "hashtab.h"
+struct hashtab_name *find_macro (char *sname);
+struct macro *get_macro (struct hashtab_name *key);
+
+void add_macro (char *start, char **pp, int report_line);
+void remove_macro (char *start, char **pp, int report_line);
+
+char *process_macro (char *start, char **pp, struct macro *m);
+void push_macro (struct hashtab_name *key, struct macro *m);
+
+#endif      /* _MACRO_H */
diff --git a/parse.c b/parse.c
new file mode 100644 (file)
index 0000000..45a2795
--- /dev/null
+++ b/parse.c
@@ -0,0 +1,33959 @@
+/******************************************************************************
+ * @file            parse.c
+ *****************************************************************************/
+#include    <stdio.h>
+#include    <stdlib.h>
+#include    <string.h>
+
+#include    "cc.h"
+#include    "expr.h"
+#include    "lib.h"
+#include    "parse.h"
+#include    "report.h"
+#include    "token.h"
+
+#define     SECTION_NONE                0
+#define     SECTION_TEXT                1
+#define     SECTION_DATA                2
+#define     SECTION_BSS                 3
+
+static int current_section = 0;
+static int anon_label = 1;
+static int anonymous_aggregate_owner_id = 1;
+
+static int pending_return_jump = 0;
+static int local_static_id = 1;
+
+#define     MAX_PENDING_STATEMENT_LABELS                    256
+
+static int pending_statement_labels[MAX_PENDING_STATEMENT_LABELS];
+static int pending_statement_label_count = 0;
+
+static void flush_pending_statement_labels (void);
+
+#define     DATA_NONE                   0
+#define     DATA_VOID                   4
+#define     DATA_PTR                    4
+
+static int current_return_label = 0;
+
+#define     DATA_CHAR                   (1 | (1 << 5))
+#define     DATA_SHORT                  (2 | (1 << 6))
+#define     DATA_INT                    (4 | (1 << 7))
+#define     DATA_LONG                   (4 | (1 << 8))
+#define     DATA_LLONG                  (8 | (1 << 9))
+#define     DATA_FLOAT                  (4 | (1 << 10))
+#define     DATA_DOUBLE                 (8 | (1 << 11))
+
+static int index_step_size (int size) {
+
+    if (size == DATA_CHAR || size == DATA_SHORT || size == DATA_INT ||
+        size == DATA_LONG || size == DATA_LLONG || size == DATA_FLOAT ||
+        size == DATA_DOUBLE) {
+        return size & 0x1f;
+    }
+    
+    if (size <= 0) {
+        return DATA_INT & 0x1f;
+    }
+    
+    return size;
+
+}
+
+static int rhs_last_pointer_depth = 0;
+static int rhs_last_pointed_size = 0;
+
+static void set_rhs_last_pointer_info (int depth, int size) {
+
+    rhs_last_pointer_depth = depth;
+    rhs_last_pointed_size = depth > 1 ? DATA_PTR : (size > 0 ? size : (DATA_INT & 0x1f));
+
+}
+
+static void clear_rhs_last_pointer_info (void) {
+
+    rhs_last_pointer_depth = 0;
+    rhs_last_pointed_size = 0;
+
+}
+
+static int current_function_has_return_statement = 0;
+static int current_parse_block_depth = 0;
+static int current_function_is_void = 0;
+static int current_function_is_floating = 0;
+static int current_function_return_size = DATA_NONE;
+static int current_function_return_is_unsigned = 0;
+static int current_function_returns_aggregate = 0;
+
+static struct local_symbol *pending_struct_return_lhs = 0;
+static const char *pending_struct_return_global_name = 0;
+
+static int pending_struct_return_stack_address = 0;
+static int pending_struct_return_stack_offset = 0;
+static int pending_struct_return_stack_top = 0;
+
+static int suppress_next_struct_return_scalar_store = 0;
+static int token_identifier_is_function_call_rhs_now (void);
+
+static int parsed_type_size = DATA_NONE;
+static int parsed_type_is_inline = 0;
+static int parsed_type_is_void = 0;
+static int parsed_type_is_unsigned = 0;
+static int parsed_type_is_floating = 0;
+static int parsed_type_only_qualifiers = 0;
+
+static int parsed_type_is_aggregate = 0;
+static int parsed_type_has_tag = 0;
+static char parsed_type_tag_name[128];
+
+static char last_cast_type_tag_name[128];
+static int last_cast_type_object_size = 0;
+
+static int parsed_type_is_array_typedef = 0;
+static int parsed_type_array_element_size = DATA_NONE;
+
+static long parsed_type_array_count = 1;
+
+static int declarator_has_function = 0;
+static int declarator_function_is_pointer = 0;
+static int declarator_function_param_count = 0;
+static int declarator_function_has_prototype = 0;
+static int declarator_function_is_variadic = 0;
+static int declarator_array_unsized = 0;
+
+static int declarator_is_pointer = 0;
+static int declarator_pointer_depth = 0;
+static int declarator_has_array = 0;
+
+static int global_initializer_accept_symbol_addresses = 0;
+
+static long declarator_array_count = 1;
+static long declarator_last_array_count = 1;
+
+#define     STORAGE_NONE                0
+#define     STORAGE_EXTERN              1
+#define     STORAGE_STATIC              2
+#define     STORAGE_TYPEDEF             3
+
+static int parsed_storage_class = STORAGE_NONE;
+
+#define     MAX_GLOBAL_INIT_FIELDS      262144
+#define     MAX_AGG_FIELDS              512
+
+static int parsed_field_sizes[MAX_AGG_FIELDS];
+static int parsed_field_count = 0;
+
+#define     MAX_STRING_INIT_BYTES       4096
+
+static void clear_parsed_fields (void) {
+    parsed_field_count = 0;
+}
+
+static void append_parsed_field (int size) {
+
+    if (parsed_field_count < MAX_AGG_FIELDS) {
+        parsed_field_sizes[parsed_field_count++] = size;
+    }
+
+}
+
+static int fields_storage_size (const int *sizes, int count) {
+
+    int total = 0, i;
+    
+    for (i = 0; i < count; i++) {
+    
+        if (sizes[i] < 0) {
+            total += -sizes[i];
+        } else {
+            total += sizes[i];
+        }
+    
+    }
+    
+    return total;
+
+}
+
+#define     MAX_MEMBER_INFOS            4096
+
+struct member_info_entry {
+
+    char *name;
+    int offset;
+    int size;
+    int elem_size;
+    int pointer_depth;
+    
+    int is_array;
+    int is_floating;
+    int is_unsigned;
+    
+    char *tag_name;
+    char *owner_tag_name;
+    
+    int owner_size;
+
+};
+
+static struct member_info_entry member_infos[MAX_MEMBER_INFOS];
+static int member_info_count = 0;
+
+static int postfix_member_pointer_depth = 0;
+static int postfix_member_pointed_size = 0;
+static int postfix_member_seen = 0;
+static int postfix_copy_lvalue_size = 0;
+
+static const char *postfix_copy_lvalue_tag_name = 0;
+
+static const char *last_found_member_tag_name = 0;
+static int last_found_member_is_unsigned = 0;
+
+static int postfix_member_offset = 0;
+static int postfix_member_size = 0;
+
+static int postfix_member_is_floating = 0;
+static int postfix_member_is_unsigned = 0;
+
+static void remember_member_info_ex (const char *name, int offset, int size, int elem_size, int pointer_depth, int is_array, int is_floating) {
+
+    if (!name) {
+        return;
+    }
+    
+    if (member_info_count >= MAX_MEMBER_INFOS) {
+        return;
+    }
+    
+    member_infos[member_info_count].name = xstrdup (name);
+    member_infos[member_info_count].offset = offset;
+    member_infos[member_info_count].size = size;
+    member_infos[member_info_count].elem_size = elem_size > 0 ? elem_size : size;
+    member_infos[member_info_count].pointer_depth = pointer_depth;
+    member_infos[member_info_count].is_array = is_array;
+    member_infos[member_info_count].is_floating = is_floating ? 1 : 0;
+    member_infos[member_info_count].is_unsigned = parsed_type_is_unsigned ? 1 : 0;
+    member_infos[member_info_count].tag_name = parsed_type_tag_name[0] ? xstrdup (parsed_type_tag_name) : 0;
+    member_infos[member_info_count].owner_size = 0;
+    member_infos[member_info_count].owner_tag_name = 0;
+    
+    member_info_count++;
+
+}
+
+static int find_member_info_ex (const char *name, int *offset, int *size, int *elem_size, int *pointer_depth, int *is_array, int *is_floating) {
+
+    int best;
+    int i;
+    
+    last_found_member_tag_name = 0;
+    last_found_member_is_unsigned = 0;
+    
+    if (!name) {
+        return 0;
+    }
+    
+    best = -1;
+    
+    for (i = member_info_count - 1; i >= 0; i--) {
+    
+        if (member_infos[i].name && strcmp (member_infos[i].name, name) == 0) {
+        
+            best = i;
+            break;
+        
+        }
+    
+    }
+
+    if (best < 0) {
+        return 0;
+    }
+    
+    last_found_member_tag_name = member_infos[best].tag_name;
+    last_found_member_is_unsigned = member_infos[best].is_unsigned ? 1 : 0;
+
+    if (offset) {
+        *offset = member_infos[best].offset;
+    }
+    
+    if (size) {
+        *size = member_infos[best].size;
+    }
+    
+    if (elem_size) {
+        *elem_size = member_infos[best].elem_size;
+    }
+    
+    if (pointer_depth) {
+        *pointer_depth = member_infos[best].pointer_depth;
+    }
+    
+    if (is_array) {
+        *is_array = member_infos[best].is_array;
+    }
+    
+    if (is_floating) {
+        *is_floating = member_infos[best].is_floating;
+    }
+    
+    return 1;
+
+}
+
+static int find_member_info (const char *name, int *offset, int *size) {
+    return find_member_info_ex (name, offset, size, 0, 0, 0, 0);
+}
+
+static int find_member_info_ex_bounded (const char *name, int max_size, const char *owner_tag_name, int *offset, int *size, int *elem_size, int *pointer_depth, int *is_array, int *is_floating) {
+
+    int best;
+    int i;
+    
+    last_found_member_tag_name = 0;
+    last_found_member_is_unsigned = 0;
+    
+    if (!name) {
+        return 0;
+    }
+    
+    if (max_size <= 0 && (!owner_tag_name || !owner_tag_name[0])) {
+        return find_member_info_ex (name, offset, size, elem_size, pointer_depth, is_array, is_floating);
+    }
+    
+    best = -1;
+    
+    if (owner_tag_name && owner_tag_name[0]) {
+    
+        for (i = member_info_count - 1; i >= 0; i--) {
+        
+            if (member_infos[i].name && strcmp (member_infos[i].name, name) == 0
+                && member_infos[i].owner_tag_name
+                && strcmp (member_infos[i].owner_tag_name, owner_tag_name) == 0) {
+            
+                best = i;
+                break;
+            
+            }
+        
+        }
+    
+    }
+    
+    if (best < 0) {
+    
+        for (i = member_info_count - 1; i >= 0; i--) {
+        
+            if (member_infos[i].name && strcmp (member_infos[i].name, name) == 0
+                && member_infos[i].owner_size == max_size) {
+            
+                if (best < 0 || member_infos[i].offset < member_infos[best].offset) {
+                    best = i;
+                }
+            
+            }
+        
+        }
+    
+    }
+    
+    if (best < 0) {
+    
+        for (i = member_info_count - 1; i >= 0; i--) {
+        
+            if (member_infos[i].name && strcmp (member_infos[i].name, name) == 0
+                && member_infos[i].offset + member_infos[i].size <= max_size) {
+            
+                if (best < 0 || member_infos[i].offset < member_infos[best].offset) {
+                    best = i;
+                }
+            
+            }
+        
+        }
+    
+    }
+    
+    if (best >= 0) {
+    
+        last_found_member_tag_name = member_infos[best].tag_name;
+        last_found_member_is_unsigned = member_infos[best].is_unsigned ? 1 : 0;
+        
+        if (offset) {
+            *offset = member_infos[best].offset;
+        }
+        
+        if (size) {
+            *size = member_infos[best].size;
+        }
+        
+        if (elem_size) {
+            *elem_size = member_infos[best].elem_size;
+        }
+        
+        if (pointer_depth) {
+            *pointer_depth = member_infos[best].pointer_depth;
+        }
+        
+        if (is_array) {
+            *is_array = member_infos[best].is_array;
+        }
+        
+        if (is_floating) {
+            *is_floating = member_infos[best].is_floating;
+        }
+        
+        return 1;
+    
+    }
+    
+    return find_member_info_ex (name, offset, size, elem_size, pointer_depth, is_array, is_floating);
+
+}
+
+static const char *find_member_tag_name (const char *name) {
+
+    int i;
+    
+    if (!name) {
+        return 0;
+    }
+    
+    for (i = member_info_count - 1; i >= 0; i--) {
+    
+        if (member_infos[i].name && strcmp (member_infos[i].name, name) == 0) {
+            return member_infos[i].tag_name;
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+#define     MAX_AGG_TAGS                256
+
+struct aggregate_tag_entry {
+
+    char *name;
+    
+    int is_union;
+    int size;
+    
+    int field_count;
+    int field_sizes[MAX_AGG_FIELDS];
+
+};
+
+static struct aggregate_tag_entry aggregate_tags[MAX_AGG_TAGS];
+static int aggregate_tag_count = 0;
+
+static struct aggregate_tag_entry *find_aggregate_tag (const char *name, int is_union) {
+
+    int i;
+    
+    if (!name) {
+        return 0;
+    }
+    
+    for (i = 0; i < aggregate_tag_count; i++) {
+    
+        if (aggregate_tags[i].is_union == is_union && strcmp (aggregate_tags[i].name, name) == 0) {
+            return &aggregate_tags[i];
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static void save_aggregate_tag (const char *name, int is_union, int size, const int *field_sizes, int field_count) {
+
+    struct aggregate_tag_entry *entry;
+    int i;
+    
+    if (!name) {
+        return;
+    }
+    
+    entry = find_aggregate_tag (name, is_union);
+    
+    if (!entry) {
+    
+        if (aggregate_tag_count >= MAX_AGG_TAGS) {
+            return;
+        }
+        
+        entry = &aggregate_tags[aggregate_tag_count++];
+        entry->name = xstrdup (name);
+        entry->is_union = is_union;
+    
+    }
+    
+    entry->size = size;
+    entry->field_count = 0;
+    
+    for (i = 0; i < field_count && i < MAX_AGG_FIELDS; i++) {
+        entry->field_sizes[entry->field_count++] = field_sizes[i];
+    }
+
+}
+
+static void load_aggregate_tag_fields (struct aggregate_tag_entry *entry) {
+
+    int i;
+    clear_parsed_fields ();
+    
+    if (!entry) {
+        return;
+    }
+    
+    parsed_type_size = entry->size;
+    parsed_type_is_aggregate = 1;
+    parsed_type_is_void = 0;
+    parsed_type_has_tag = 1;
+    
+    for (i = 0; i < entry->field_count; i++) {
+        append_parsed_field (entry->field_sizes[i]);
+    }
+
+}
+
+#define     MAX_TYPEDEF_NAMES            512
+
+struct typedef_entry {
+
+    char *name;
+    int size;
+    
+    int is_aggregate;
+    int is_unsigned;
+    int is_void;
+    
+    int field_count;
+    int field_sizes[MAX_AGG_FIELDS];
+    
+    int array_element_size;
+    int is_array;
+    
+    char *tag_name;
+    long array_count;
+
+};
+
+static struct typedef_entry typedef_names[MAX_TYPEDEF_NAMES];
+static int typedef_name_count = 0;
+
+static void clear_typedef_names (void) {
+
+    int i;
+    
+    for (i = 0; i < typedef_name_count; i++) {
+    
+        free (typedef_names[i].name);
+        
+        typedef_names[i].name = 0;
+        typedef_names[i].size = 0;
+        
+        typedef_names[i].tag_name = 0;
+        
+        typedef_names[i].is_aggregate = 0;
+        typedef_names[i].is_unsigned = 0;
+        
+        typedef_names[i].field_count = 0;
+        typedef_names[i].is_array = 0;
+        typedef_names[i].array_count = 1;
+        typedef_names[i].array_element_size = DATA_NONE;
+    
+    }
+    
+    typedef_name_count = 0;
+
+}
+
+static struct typedef_entry *find_typedef_name (const char *name) {
+
+    int i;
+    
+    if (!name) {
+        return 0;
+    }
+
+    for (i = 0; i < typedef_name_count; i++) {
+    
+        if (strcmp (typedef_names[i].name, name) == 0) {
+            return &typedef_names[i];
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+struct local_symbol;
+static struct local_symbol *find_local_symbol (const char *name);
+
+static int is_current_typedef_name (void) {
+
+    if (tok.kind != TOK_IDENT || !tok.ident) {
+        return 0;
+    }
+    
+    if (find_local_symbol (tok.ident)) {
+        return 0;
+    }
+    
+    return find_typedef_name (tok.ident) != 0;
+
+}
+
+static void save_typedef_name (const char *name, int size, int is_unsigned, int is_void, int is_aggregate, int is_array, long array_count, int array_element_size, const int *field_sizes, int field_count) {
+
+    struct typedef_entry *entry;
+    int i;
+    
+    if (!name || !*name) {
+        return;
+    }
+    
+    entry = find_typedef_name (name);
+    
+    if (!entry) {
+    
+        if (typedef_name_count >= MAX_TYPEDEF_NAMES) {
+        
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "too many typedef names");
+            return;
+        
+        }
+        
+        entry = &typedef_names[typedef_name_count++];
+        entry->name = xstrdup (name);
+        entry->tag_name = 0;
+    
+    }
+    
+    if (entry->tag_name) {
+    
+        free (entry->tag_name);
+        entry->tag_name = 0;
+    
+    }
+    
+    if (parsed_type_tag_name[0]) {
+        entry->tag_name = xstrdup (parsed_type_tag_name);
+    } else if (is_aggregate && name && *name) {
+        entry->tag_name = xstrdup (name);
+    }
+    
+    entry->is_void = is_void;
+    entry->size = size;
+    
+    entry->is_unsigned = is_unsigned;
+    entry->is_aggregate = is_aggregate;
+    
+    entry->field_count = 0;
+    entry->is_array = is_array ? 1 : 0;
+    entry->array_count = array_count > 0 ? array_count : 1;
+    entry->array_element_size = array_element_size > 0 ? array_element_size : DATA_INT;
+    
+    for (i = 0; i < field_count && i < MAX_AGG_FIELDS; i++) {
+        entry->field_sizes[entry->field_count++] = field_sizes[i];
+    }
+    
+    if (is_aggregate && !parsed_type_tag_name[0] && name && *name) {
+    
+        int mi;
+        
+        for (mi = 0; mi < member_info_count; mi++) {
+        
+            if (member_infos[mi].owner_size == size
+                && member_infos[mi].owner_tag_name == 0) {
+                member_infos[mi].owner_tag_name = xstrdup (name);
+            }
+        
+        }
+    
+    }
+
+}
+
+static void update_typedef_name_from_aggregate_tag (const char *name, int size, const int *field_sizes, int field_count) {
+
+    struct typedef_entry *entry;
+    int i;
+    
+    if (!name) {
+        return;
+    }
+    
+    entry = find_typedef_name (name);
+    
+    if (!entry) {
+        return;
+    }
+    
+    entry->size = size;
+    entry->is_aggregate = 1;
+    entry->is_void = 0;
+    entry->is_array = 0;
+    entry->array_count = 1;
+    entry->array_element_size = DATA_NONE;
+    entry->field_count = 0;
+    
+    if (entry->tag_name) {
+    
+        free (entry->tag_name);
+        entry->tag_name = 0;
+    
+    }
+    
+    if (name && *name) {
+        entry->tag_name = xstrdup (name);
+    }
+    
+    for (i = 0; i < field_count && i < MAX_AGG_FIELDS; i++) {
+        entry->field_sizes[entry->field_count++] = field_sizes[i];
+    }
+
+}
+
+static void load_typedef_name (struct typedef_entry *entry) {
+
+    int i;
+    clear_parsed_fields ();
+    
+    if (!entry) {
+    
+        parsed_type_size = DATA_INT & 0x1f;
+        
+        parsed_type_is_aggregate = 0;
+        parsed_type_is_unsigned = 0;
+        parsed_type_is_void = 0;
+        parsed_type_is_floating = 0;
+        parsed_type_is_array_typedef = 0;
+        parsed_type_array_count = 1;
+        parsed_type_array_element_size = DATA_NONE;
+        
+        append_parsed_field (DATA_INT & 0x1f);
+        return;
+    
+    }
+    
+    parsed_type_size = entry->size;
+    
+    parsed_type_is_aggregate = entry->is_aggregate;
+    parsed_type_is_unsigned = entry->is_unsigned;
+    parsed_type_is_void = entry->is_void;
+    parsed_type_is_floating = 0;
+    parsed_type_is_array_typedef = entry->is_array;
+    parsed_type_array_count = entry->array_count > 0 ? entry->array_count : 1;
+    parsed_type_array_element_size = entry->array_element_size > 0 ? entry->array_element_size : DATA_INT;
+    
+    parsed_type_has_tag = 0;
+    parsed_type_tag_name[0] = '\0';
+    
+    if (entry->tag_name && entry->tag_name[0]) {
+        
+        strncpy (parsed_type_tag_name, entry->tag_name, sizeof (parsed_type_tag_name) - 1);
+        parsed_type_tag_name[sizeof (parsed_type_tag_name) - 1] = '\0';
+        
+        parsed_type_has_tag = 1;
+    
+    }
+    
+    for (i = 0; i < entry->field_count; i++) {
+        append_parsed_field (entry->field_sizes[i]);
+    }
+
+}
+
+#define     MAX_ENUM_CONSTANTS          4096
+
+struct enum_const_entry {
+
+    char *name;
+    int64_s value;
+
+};
+
+static struct enum_const_entry enum_constants[MAX_ENUM_CONSTANTS];
+static int enum_constant_count = 0;
+
+static struct enum_const_entry *find_enum_constant (const char *name) {
+
+    int i;
+    
+    if (!name) {
+        return 0;
+    }
+    
+    for (i = 0; i < enum_constant_count; i++) {
+    
+        if (strcmp (enum_constants[i].name, name) == 0) {
+            return &enum_constants[i];
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static void save_enum_constant (const char *name, int64_s value, const char *start, const char *caret) {
+
+    struct enum_const_entry *entry;
+    
+    if (!name || !*name) {
+        return;
+    }
+    
+    entry = find_enum_constant (name);
+    
+    if (entry) {
+    
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "duplicate enum constant '%s'", name);
+        return;
+    
+    }
+    
+    if (enum_constant_count >= MAX_ENUM_CONSTANTS) {
+    
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "too many enum constants");
+        return;
+    
+    }
+    
+    entry = &enum_constants[enum_constant_count++];
+    entry->name = xstrdup (name);
+    entry->value = value;
+
+}
+
+int resolve_enum_constant (const char *name, int64_s *out) {
+
+    struct enum_const_entry *entry = find_enum_constant (name);
+    
+    if (!entry) {
+        return 0;
+    }
+    
+    if (out) {
+        *out = entry->value;
+    }
+    
+    return 1;
+
+}
+
+static void clear_enum_constants (void) {
+
+    int i;
+    
+    for (i = 0; i < enum_constant_count; i++) {
+    
+        free (enum_constants[i].name);
+        
+        enum_constants[i].name = 0;
+        enum_constants[i].value.low = 0;
+        enum_constants[i].value.high = 0;
+    
+    }
+    
+    enum_constant_count = 0;
+
+}
+
+#define     MAX_GLOBAL_SYMBOLS          4096
+
+#define     GLOBAL_SYMBOL_OBJECT        1
+#define     GLOBAL_SYMBOL_FUNCTION      2
+
+struct global_symbol_entry {
+
+    char *name;
+    
+    int is_unsigned, is_extern;
+    int kind, size;
+    
+    int array_element_size;
+    int is_array;
+    
+    long array_count;
+    
+    int is_floating;
+    int pointer_depth;
+    int pointed_size;
+    int returns_void;
+    
+    int param_count;
+    int has_prototype;
+    int is_variadic;
+    
+    int is_implicit;
+    int extern_emitted;
+    
+    char *tag_name;
+
+};
+
+static struct global_symbol_entry global_symbols[MAX_GLOBAL_SYMBOLS];
+static int global_symbol_count = 0;
+
+static const char *last_declarator_name_start = 0;
+static const char *last_declarator_name_caret = 0;
+
+static unsigned long last_declarator_name_line = 0;
+
+static int capture_declarator_name_location = 0;
+static int captured_declarator_name_location = 0;
+
+static const char *captured_declarator_name_start = 0;
+static const char *captured_declarator_name_caret = 0;
+
+static unsigned long captured_declarator_name_line = 0;
+
+static void clear_global_symbols (void) {
+
+    int i;
+    
+    for (i = 0; i < global_symbol_count; i++) {
+    
+        free (global_symbols[i].name);
+        
+        global_symbols[i].name = 0;
+        global_symbols[i].kind = 0;
+        global_symbols[i].size = 0;
+        
+        global_symbols[i].array_element_size = 0;
+        global_symbols[i].is_array = 0;
+        
+        global_symbols[i].is_extern = 0;
+        global_symbols[i].is_unsigned = 0;
+        global_symbols[i].is_floating = 0;
+        global_symbols[i].returns_void = 0;
+        global_symbols[i].param_count = 0;
+        global_symbols[i].has_prototype = 0;
+        global_symbols[i].is_variadic = 0;
+        global_symbols[i].is_implicit = 0;
+        global_symbols[i].extern_emitted = 0;
+        
+        if (global_symbols[i].tag_name) {
+        
+            free (global_symbols[i].tag_name);
+            global_symbols[i].tag_name = 0;
+        
+        }
+    
+    }
+    
+    global_symbol_count = 0;
+
+}
+
+static int find_global_symbol (const char *name) {
+
+    int i;
+    
+    if (!name) {
+        return -1;
+    }
+    
+    for (i = 0; i < global_symbol_count; i++) {
+    
+        if (strcmp (global_symbols[i].name, name) == 0) {
+            return i;
+        }
+    
+    }
+    
+    return -1;
+
+}
+
+static int asm_symbol_is_internal (const char *name) {
+
+    if (!name || !*name) {
+        return 1;
+    }
+    
+    if (name[0] == '.') {
+        return 1;
+    }
+    
+    if (name[0] == 'L') {
+    
+        if (name[1] >= '0' && name[1] <= '9') {
+            return 1;
+        }
+        
+        if (name[1] == 'C' && name[2] >= '0' && name[2] <= '9') {
+            return 1;
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static const char *asm_global_symbol_name (const char *name) {
+
+    static char buffers[8][512];
+    static int index = 0;
+    
+    char *out;
+    unsigned long len;
+    
+    if (asm_symbol_is_internal (name)) {
+        return name;
+    }
+    
+    index = (index + 1) & 7;
+    
+    out = buffers[index];
+    len = strlen (name);
+    
+    if (len > sizeof (buffers[0]) - 2) {
+        len = sizeof (buffers[0]) - 2;
+    }
+    
+    if (!state->no_leading_underscore) {
+    
+        out[0] = '_';
+        
+        memcpy (out + 1, name, len);
+        out[len + 1] = 0;
+    
+    } else {
+    
+        memcpy (out, name, len);
+        out[len] = 0;
+    
+    }
+    
+    return out;
+
+}
+
+static int get_global_symbol_kind (const char *name) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+        return global_symbols[i].kind;
+    }
+    
+    return 0;
+
+}
+
+static void set_global_symbol_unsigned (const char *name, int is_unsigned) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+        global_symbols[i].is_unsigned = is_unsigned ? 1 : 0;
+    }
+
+}
+
+static int get_global_symbol_unsigned (const char *name) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+        return global_symbols[i].is_unsigned;
+    }
+    
+    return 0;
+
+}
+
+static void set_global_symbol_array (const char *name, int is_array) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+        global_symbols[i].is_array = is_array ? 1 : 0;
+    }
+
+}
+
+static int get_global_symbol_array (const char *name) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+        return global_symbols[i].is_array;
+    }
+    
+    return 0;
+
+}
+
+static void set_global_symbol_array_count (const char *name, long array_count) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+        global_symbols[i].array_count = array_count;
+    }
+
+}
+
+static long get_global_symbol_array_count (const char *name) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+        return global_symbols[i].array_count;
+    }
+    
+    return 0;
+
+}
+
+static void set_global_symbol_array_element_size (const char *name, int elem_size) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+        global_symbols[i].array_element_size = elem_size > 0 ? elem_size : 0;
+    }
+
+}
+
+static int get_global_symbol_array_element_size (const char *name) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+    
+        if (global_symbols[i].array_element_size > 0) {
+            return global_symbols[i].array_element_size;
+        }
+        
+        if (global_symbols[i].tag_name && global_symbols[i].tag_name[0]) {
+        
+            struct aggregate_tag_entry *entry = find_aggregate_tag (global_symbols[i].tag_name, 0);
+            
+            if (entry && entry->size > 0) {
+                return entry->size;
+            }
+        
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static void set_global_symbol_floating (const char *name, int is_floating) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+        global_symbols[i].is_floating = is_floating ? 1 : 0;
+    }
+
+}
+
+static int get_global_symbol_floating (const char *name) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+        return global_symbols[i].is_floating;
+    }
+    
+    return 0;
+
+}
+
+static void set_global_symbol_pointer_info (const char *name, int pointer_depth, int pointed_size) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+    
+        global_symbols[i].pointer_depth = pointer_depth;
+        global_symbols[i].pointed_size = pointed_size;
+    
+    }
+
+}
+
+static int get_global_symbol_pointer_depth (const char *name) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+        return global_symbols[i].pointer_depth;
+    }
+    
+    return 0;
+
+}
+
+static int get_global_symbol_pointed_size (const char *name) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0 && global_symbols[i].pointed_size > 0) {
+        return global_symbols[i].pointed_size;
+    }
+    
+    return DATA_INT & 0x1f;
+
+}
+
+static void set_global_symbol_tag_name (const char *name, const char *tag_name) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+    
+        if (global_symbols[i].tag_name) {
+        
+            free (global_symbols[i].tag_name);
+            global_symbols[i].tag_name = 0;
+        
+        }
+        
+        if (tag_name && tag_name[0]) {
+            global_symbols[i].tag_name = xstrdup (tag_name);
+        }
+    
+    }
+
+}
+
+static const char *get_global_symbol_tag_name (const char *name) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+        return global_symbols[i].tag_name;
+    }
+    
+    return 0;
+
+}
+
+static void set_global_symbol_returns_void (const char *name, int returns_void) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+        global_symbols[i].returns_void = returns_void ? 1 : 0;
+    }
+
+}
+
+static int get_global_symbol_returns_void (const char *name) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+        return global_symbols[i].returns_void;
+    }
+    
+    return 0;
+
+}
+
+static void set_global_symbol_param_count (const char *name, int param_count, int has_prototype, int is_variadic) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+    
+        global_symbols[i].param_count = param_count < 0 ? 0 : param_count;
+        global_symbols[i].has_prototype = has_prototype ? 1 : 0;
+        global_symbols[i].is_variadic = is_variadic ? 1 : 0;
+    
+    }
+
+}
+
+static int get_global_symbol_has_prototype (const char *name) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+        return global_symbols[i].has_prototype;
+    }
+    
+    return 0;
+
+}
+
+static int get_global_symbol_is_variadic (const char *name) {
+
+    int i = find_global_symbol (name);
+
+    if (i >= 0) {
+        return global_symbols[i].is_variadic;
+    }
+
+    return 0;
+
+}
+
+static int get_global_symbol_param_count (const char *name) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+        return global_symbols[i].param_count;
+    }
+    
+    return 0;
+
+}
+
+static void set_global_symbol_size (const char *name, int size) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+        global_symbols[i].size = size;
+    }
+
+}
+
+static int get_global_symbol_size (const char *name) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0 && global_symbols[i].size > 0) {
+        return global_symbols[i].size;
+    }
+    
+    return DATA_INT & 0x1f;
+
+}
+
+static int add_global_symbol (const char *name, int kind, int is_extern, const char *line_start, const char *name_caret, unsigned long lineno) {
+
+    int old;
+    
+    if (!name || !*name) {
+        return 0;
+    }
+    
+    if (!line_start) {
+        line_start = tok.start;
+    }
+
+    if (!name_caret) {
+        name_caret = tok.caret;
+    }
+
+    if (lineno == 0) {
+        lineno = get_line_number ();
+    }
+    
+    old = find_global_symbol (name);
+    
+    if (old >= 0) {
+    
+        if (global_symbols[old].is_implicit) {
+        
+            global_symbols[old].is_extern = is_extern ? 1 : 0;
+            global_symbols[old].is_implicit = 0;
+            
+            global_symbols[old].kind = kind;
+            return 1;
+        
+        }
+        
+        if (is_extern) {
+            return 0;
+        }
+        
+        if (global_symbols[old].is_extern) {
+        
+            global_symbols[old].kind = kind;
+            global_symbols[old].is_extern = 0;
+            
+            return 1;
+        
+        }
+        
+        report_line_at (get_filename (), lineno, REPORT_ERROR, line_start, name_caret, "duplicate symbol '%s'", name);
+        return 0;
+    
+    }
+    
+    if (global_symbol_count >= MAX_GLOBAL_SYMBOLS) {
+    
+        report_line_at (get_filename (), lineno, REPORT_ERROR, line_start, name_caret, "too many global symbols");
+        return 0;
+    
+    }
+    
+    global_symbols[global_symbol_count].name = xstrdup (name);
+    global_symbols[global_symbol_count].kind = kind;
+    global_symbols[global_symbol_count].is_extern = is_extern;
+    global_symbols[global_symbol_count].is_unsigned = 0;
+    global_symbols[global_symbol_count].is_floating = 0;
+    global_symbols[global_symbol_count].is_array = 0;
+    global_symbols[global_symbol_count].returns_void = 0;
+    global_symbols[global_symbol_count].param_count = 0;
+    global_symbols[global_symbol_count].has_prototype = 0;
+    global_symbols[global_symbol_count].is_variadic = 0;
+    global_symbols[global_symbol_count].is_implicit = 0;
+    global_symbols[global_symbol_count].extern_emitted = 0;
+    global_symbols[global_symbol_count].tag_name = 0;
+    
+    global_symbol_count++;
+    return 1;
+
+}
+
+static void ensure_global_function_symbol (const char *name, const char *line_start, const char *name_caret, unsigned long lineno) {
+
+    int i;
+    
+    if (!name || !*name) {
+        return;
+    }
+    
+    i = find_global_symbol (name);
+    
+    if (i >= 0) {
+        return;
+    }
+    
+    report_line_at (get_filename (), lineno, REPORT_WARNING, line_start, name_caret, "implicit declaration of function '%s'", name);
+    
+    if (add_global_symbol (name, GLOBAL_SYMBOL_FUNCTION, 1, line_start, name_caret, lineno)) {
+    
+        i = find_global_symbol (name);
+        
+        if (i >= 0) {
+        
+            global_symbols[i].size = DATA_INT & 0x1f;
+            global_symbols[i].is_unsigned = 0;
+            global_symbols[i].is_floating = 0;
+            global_symbols[i].returns_void = 0;
+            global_symbols[i].param_count = 0;
+            global_symbols[i].has_prototype = 0;
+            global_symbols[i].is_variadic = 0;
+            global_symbols[i].is_implicit = 1;
+        
+        }
+    
+    }
+
+}
+
+#define     MAX_INLINE_FUNCTIONS        256
+
+struct inline_function_entry {
+
+    char *name;
+    char *body;
+    char *data;
+    
+    int param_count;
+    int has_prototype;
+    int returns_void;
+    int is_floating;
+    int return_size;
+    int return_label;
+    int data_emitted;
+    int expanding;
+
+    int usable;
+
+};
+
+static struct inline_function_entry inline_functions[MAX_INLINE_FUNCTIONS];
+static int inline_function_count = 0;
+
+static void clear_inline_functions (void) {
+
+    int i;
+    
+    for (i = 0; i < inline_function_count; i++) {
+    
+        free (inline_functions[i].name);
+        free (inline_functions[i].body);
+        free (inline_functions[i].data);
+        
+        inline_functions[i].name = 0;
+        inline_functions[i].body = 0;
+        inline_functions[i].data = 0;
+        inline_functions[i].param_count = 0;
+        inline_functions[i].has_prototype = 0;
+        inline_functions[i].returns_void = 0;
+        inline_functions[i].is_floating = 0;
+        inline_functions[i].return_size = 0;
+        inline_functions[i].return_label = 0;
+        inline_functions[i].data_emitted = 0;
+        inline_functions[i].expanding = 0;
+        inline_functions[i].usable = 0;
+    
+    }
+    
+    inline_function_count = 0;
+
+}
+
+static int find_inline_function (const char *name) {
+
+    int i;
+    
+    if (!name) {
+        return -1;
+    }
+    
+    for (i = 0; i < inline_function_count; i++) {
+    
+        if (strcmp (inline_functions[i].name, name) == 0) {
+            return i;
+        }
+    
+    }
+    
+    return -1;
+
+}
+
+static void remember_inline_function_signature (const char *name, int param_count, int has_prototype, int returns_void, int is_floating, int return_size) {
+
+    int i;
+    
+    if (!name || !*name) {
+        return;
+    }
+    
+    i = find_inline_function (name);
+    
+    if (i < 0) {
+    
+        if (inline_function_count >= MAX_INLINE_FUNCTIONS) {
+            return;
+        }
+        
+        i = inline_function_count++;
+        inline_functions[i].name = xstrdup (name);
+        inline_functions[i].body = 0;
+        inline_functions[i].data = 0;
+        inline_functions[i].data_emitted = 0;
+        inline_functions[i].expanding = 0;
+    
+    }
+    
+    inline_functions[i].param_count = param_count < 0 ? 0 : param_count;
+    inline_functions[i].has_prototype = has_prototype ? 1 : 0;
+    inline_functions[i].returns_void = returns_void ? 1 : 0;
+    inline_functions[i].is_floating = is_floating ? 1 : 0;
+    inline_functions[i].return_size = return_size;
+    inline_functions[i].return_label = 0;
+    inline_functions[i].data_emitted = 0;
+    inline_functions[i].expanding = 0;
+    inline_functions[i].usable = 0;
+
+}
+
+static int inline_asm_body_is_safe (const char *body) {
+
+    const char *p = body;
+    
+    if (!body) {
+        return 0;
+    }
+    
+    while (*p) {
+    
+        const char *line = p;
+        const char *eol = strchr (p, '\n');
+        
+        size_t len = eol ? (size_t) (eol - line) : strlen (line);
+        
+        while (len > 0 && (line[len - 1] == '\r' || line[len - 1] == '\n')) {
+            len--;
+        }
+        
+        while (len > 0 && (*line == ' ' || *line == '\t')) {
+        
+            line++;
+            len--;
+        
+        }
+        
+        if (len >= 4 && strncmp (line, "ret", 3) == 0) {
+            return 0;
+        }
+        
+        if (len >= 5 && strncmp (line, "leave", 5) == 0) {
+            return 0;
+        }
+        
+        if (len > 0 && line[0] == '.' && !(len >= 3 && line[1] == 'L' && line[2] >= '0' && line[2] <= '9')) {
+        
+            if (!((len == 5 && strncmp (line, ".data", 5) == 0) ||
+                  (len == 6 && strncmp (line, ".data?", 6) == 0) ||
+                  (len == 5 && strncmp (line, ".code", 5) == 0) ||
+                  (len == 5 && strncmp (line, ".text", 5) == 0) ||
+                  (len == 4 && strncmp (line, ".bss", 4) == 0))) {
+            
+                return 0;
+            
+            }
+        
+        }
+        
+        if (len >= 6 && strncmp (line, "public", 6) == 0) {
+            return 0;
+        }
+        
+        if (!eol) {
+            break;
+        }
+        
+        p = eol + 1;
+    
+    }
+    
+    return 1;
+
+}
+
+static char *extract_inline_asm_body (const char *asm_text, int return_label) {
+
+    char marker[64];
+    char *body;
+    
+    const char *start, *end;
+    size_t len;
+    
+    if (!asm_text) {
+        return 0;
+    }
+    
+    start = strstr (asm_text, "    mov ebp, esp\n");
+    
+    if (start) {
+        start += strlen ("    mov ebp, esp\n");
+    } else {
+    
+        start = strstr (asm_text, "    movl %esp, %ebp\n");
+        
+        if (start) {
+            start += strlen ("    movl %esp, %ebp\n");
+        }
+    
+    }
+    
+    if (!start) {
+        return 0;
+    }
+    
+    if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+        sprintf (marker, "\nL%d:\n", return_label);
+    } else {
+        sprintf (marker, "\n.L%d:\n", return_label);
+    }
+    
+    end = strstr (start, marker);
+    
+    if (!end) {
+
+        if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+            sprintf (marker, "L%d:\n", return_label);
+        } else {
+            sprintf (marker, ".L%d:\n", return_label);
+        }
+
+        if (strncmp (start, marker, strlen (marker)) == 0) {
+            end = start;
+        } else {
+            return 0;
+        }
+
+    }
+    
+    if (*start == '\n') {
+        start++;
+    }
+    
+    len = (size_t) (end - start);
+    
+    while (len > 0 && (start[len - 1] == '\r' || start[len - 1] == '\n')) {
+        len--;
+    }
+    
+    body = xmalloc (len + 2);
+    memcpy (body, start, len);
+    
+    body[len++] = '\n';
+    body[len] = 0;
+    
+    if (!inline_asm_body_is_safe (body)) {
+    
+        free (body);
+        return 0;
+    
+    }
+    
+    return body;
+
+}
+
+static char *read_tmp_file_text (FILE *fp) {
+
+    long len;
+    char *text;
+    
+    if (!fp) {
+        return 0;
+    }
+    
+    fflush (fp);
+    
+    if (fseek (fp, 0, SEEK_END) != 0) {
+        return 0;
+    }
+    
+    len = ftell (fp);
+    
+    if (len < 0) {
+        return 0;
+    }
+    
+    if (fseek (fp, 0, SEEK_SET) != 0) {
+        return 0;
+    }
+    
+    text = xmalloc ((size_t) len + 1);
+    
+    if (len > 0 && fread (text, 1, (size_t) len, fp) != (size_t) len) {
+        free (text);
+        return 0;
+    }
+    
+    text[len] = 0;
+    return text;
+
+}
+
+static void append_inline_text (char **dst, const char *start, size_t len) {
+
+    size_t old_len = 0;
+    char *out;
+    
+    if (!dst || !start || len == 0) {
+        return;
+    }
+    
+    if (*dst) {
+        old_len = strlen (*dst);
+    }
+    
+    out = xmalloc (old_len + len + 1);
+    
+    if (old_len) {
+        memcpy (out, *dst, old_len);
+        free (*dst);
+    }
+    
+    memcpy (out + old_len, start, len);
+    out[old_len + len] = 0;
+    *dst = out;
+
+}
+
+static void split_inline_asm_sections (char **bodyp, char **datap) {
+
+    const char *p;
+    
+    char *body = 0;
+    char *data = 0;
+    
+    int in_data = 0;
+    
+    if (!bodyp || !*bodyp) {
+        return;
+    }
+    
+    p = *bodyp;
+    
+    while (*p) {
+    
+        const char *line = p;
+        const char *eol = strchr (p, '\n');
+        const char *next = eol ? eol + 1 : p + strlen (p);
+        
+        size_t len = (size_t) (next - line);
+        
+        const char *trim = line;
+        size_t tlen = eol ? (size_t) (eol - line) : strlen (line);
+        
+        while (tlen > 0 && (trim[tlen - 1] == '\r' || trim[tlen - 1] == '\n')) {
+            tlen--;
+        }
+        
+        while (tlen > 0 && (*trim == ' ' || *trim == '\t')) {
+        
+            trim++;
+            tlen--;
+        
+        }
+        
+        if ((tlen == 5 && strncmp (trim, ".data", 5) == 0) ||
+            (tlen == 6 && strncmp (trim, ".data?", 6) == 0) ||
+            (tlen == 4 && strncmp (trim, ".bss", 4) == 0)) {
+        
+            in_data = 1;
+            append_inline_text (&data, line, len);
+        
+        } else if ((tlen == 5 && strncmp (trim, ".code", 5) == 0) ||
+                   (tlen == 5 && strncmp (trim, ".text", 5) == 0)) {
+            in_data = 0;
+        } else if (in_data) {
+            append_inline_text (&data, line, len);
+        } else {
+            append_inline_text (&body, line, len);
+        }
+        
+        p = next;
+    
+    }
+    
+    free (*bodyp);
+    
+    *bodyp = body;
+    *datap = data;
+
+}
+
+static void remember_inline_function (const char *name, const char *asm_text, int return_label, int param_count, int has_prototype, int returns_void, int is_floating, int return_size) {
+
+    char *body;
+    char *data = 0;
+    
+    int i;
+    
+    if (!name || !*name || !asm_text) {
+        return;
+    }
+    
+    body = extract_inline_asm_body (asm_text, return_label);
+    
+    if (!body) {
+        return;
+    }
+    
+    split_inline_asm_sections (&body, &data);
+    i = find_inline_function (name);
+    
+    if (i < 0) {
+    
+        if (inline_function_count >= MAX_INLINE_FUNCTIONS) {
+        
+            free (body);
+            free (data);
+            
+            return;
+        
+        }
+        
+        i = inline_function_count++;
+        
+        inline_functions[i].name = xstrdup (name);
+        inline_functions[i].body = 0;
+        inline_functions[i].data = 0;
+        inline_functions[i].data_emitted = 0;
+        inline_functions[i].expanding = 0;
+    
+    }
+    
+    free (inline_functions[i].body);
+    free (inline_functions[i].data);
+    
+    inline_functions[i].body = body;
+    inline_functions[i].data = data;
+    inline_functions[i].data_emitted = 0;
+    inline_functions[i].param_count = param_count;
+    inline_functions[i].has_prototype = has_prototype ? 1 : 0;
+    inline_functions[i].returns_void = returns_void ? 1 : 0;
+    inline_functions[i].is_floating = is_floating ? 1 : 0;
+    inline_functions[i].return_size = return_size;
+    inline_functions[i].return_label = return_label;
+    inline_functions[i].expanding = 0;
+    inline_functions[i].usable = 1;
+
+}
+
+static void emit_inline_label_reference (int label, int return_label, int call_id) {
+
+    if (label == return_label) {
+    
+        if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+            fprintf (state->ofp, "L%d", call_id);
+        } else {
+            fprintf (state->ofp, ".L%d", call_id);
+        }
+    
+    } else {
+    
+        if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+            fprintf (state->ofp, "L%d_%d", label, call_id);
+        } else {
+            fprintf (state->ofp, ".L%d_%d", label, call_id);
+        }
+    
+    }
+
+}
+
+static int emit_inline_parameter_reference_if_any (const char **pp, int argc, int stack_bytes) {
+
+    const char *p = *pp;
+    const char *q;
+    
+    int offset = 0;
+    int param_index;
+    int stack_offset;
+    
+    if (!(state->syntax & ASM_SYNTAX_MASM)) {
+        return 0;
+    }
+    
+    if (*p != '[') {
+        return 0;
+    }
+    
+    q = p + 1;
+    
+    while (*q == ' ' || *q == '\t') {
+        q++;
+    }
+    
+    if (strncmp (q, "ebp", 3) != 0) {
+        return 0;
+    }
+    
+    q += 3;
+    
+    while (*q == ' ' || *q == '\t') {
+        q++;
+    }
+    
+    if (*q != '+') {
+        return 0;
+    }
+    
+    q++;
+    
+    while (*q == ' ' || *q == '\t') {
+        q++;
+    }
+    
+    if (*q < '0' || *q > '9') {
+        return 0;
+    }
+    
+    while (*q >= '0' && *q <= '9') {
+    
+        offset = (offset * 10) + (*q - '0');
+        q++;
+    
+    }
+    
+    while (*q == ' ' || *q == '\t') {
+        q++;
+    }
+    
+    if (*q != ']') {
+        return 0;
+    }
+    
+    if (offset < 8 || ((offset - 8) % 4) != 0) {
+        return 0;
+    }
+    
+    param_index = (offset - 8) / 4;
+    
+    if (param_index < 0 || param_index >= argc) {
+        return 0;
+    }
+    
+    /*
+     * Inline arguments are copied into a compiler-owned temporary stack
+     * area before the inline body is emitted.  Parameter 0 lives at the
+     * lowest address in that area, so [ebp + 8] maps to [esp],
+     * [ebp + 12] maps to [esp + 4], [ebp + 16] maps to [esp + 8], etc.
+     * If the inlined body changes ESP temporarily, stack_bytes keeps all
+     * parameter references pointed at the same argument copies.
+     */
+    stack_offset = stack_bytes + (param_index * 4);
+    
+    if (stack_offset == 0) {
+        fprintf (state->ofp, "[esp]");
+    } else if (stack_offset > 0) {
+        fprintf (state->ofp, "[esp + %d]", stack_offset);
+    } else {
+        fprintf (state->ofp, "[esp - %d]", -stack_offset);
+    }
+    
+    *pp = q + 1;
+    return 1;
+
+}
+
+static void emit_inline_line_substituted (const char *line, size_t len, int return_label, int call_id, int argc, int stack_bytes) {
+
+    const char *p = line;
+    const char *end = line + len;
+    
+    while (p < end) {
+    
+        if ((state->syntax & ASM_SYNTAX_MASM) && *p == 'L' && p + 1 < end && p[1] >= '0' && p[1] <= '9') {
+        
+            const char *q = p + 1;
+            int label = 0;
+            
+            while (q < end && *q >= '0' && *q <= '9') {
+            
+                label = (label * 10) + (*q - '0');
+                q++;
+            
+            }
+            
+            emit_inline_label_reference (label, return_label, call_id);
+            p = q;
+            
+            continue;
+        
+        }
+        
+        if (!(state->syntax & ASM_SYNTAX_MASM) && p + 2 < end && p[0] == '.' && p[1] == 'L' && p[2] >= '0' && p[2] <= '9') {
+        
+            const char *q = p + 2;
+            int label = 0;
+            
+            while (q < end && *q >= '0' && *q <= '9') {
+            
+                label = (label * 10) + (*q - '0');
+                q++;
+            
+            }
+            
+            emit_inline_label_reference (label, return_label, call_id);
+            p = q;
+            
+            continue;
+        
+        }
+        
+        if (emit_inline_parameter_reference_if_any (&p, argc, stack_bytes)) {
+            continue;
+        }
+        
+        fputc (*p++, state->ofp);
+    
+    }
+
+}
+
+static int inline_line_stack_delta (const char *line, size_t len) {
+
+    const char *p = line;
+    long n = 0;
+    int neg = 0;
+    
+    while (len > 0 && (*p == ' ' || *p == '\t')) {
+    
+        p++;
+        len--;
+    
+    }
+    
+    while (len > 0 && (p[len - 1] == '\r' || p[len - 1] == '\n' || p[len - 1] == ' ' || p[len - 1] == '\t')) {
+        len--;
+    }
+    
+    if (len >= 4 && strncmp (p, "push", 4) == 0 && (p[4] == ' ' || p[4] == '\t')) {
+        return 4;
+    }
+    
+    if (len >= 3 && strncmp (p, "pop", 3) == 0 && (p[3] == ' ' || p[3] == '\t')) {
+        return -4;
+    }
+    
+    if (len >= 8 && (strncmp (p, "sub esp,", 8) == 0 || strncmp (p, "add esp,", 8) == 0)) {
+    
+        neg = (p[0] == 'a');
+        
+        p += 8;
+        len -= 8;
+        
+        while (len > 0 && (*p == ' ' || *p == '\t')) {
+        
+            p++;
+            len--;
+        
+        }
+        
+        while (len > 0 && *p >= '0' && *p <= '9') {
+        
+            n = (n * 10) + (*p - '0');
+            
+            p++;
+            len--;
+        
+        }
+        
+        return neg ? -(int) n : (int) n;
+    
+    }
+    
+    if (len >= 10 && (strncmp (p, "subl $", 6) == 0 || strncmp (p, "addl $", 6) == 0)) {
+    
+        neg = (p[0] == 'a');
+        
+        p += 6;
+        len -= 6;
+        
+        while (len > 0 && *p >= '0' && *p <= '9') {
+        
+            n = (n * 10) + (*p - '0');
+            
+            p++;
+            len--;
+        
+        }
+        
+        while (len > 0 && (*p == ' ' || *p == '\t')) {
+        
+            p++;
+            len--;
+        
+        }
+        
+        if (len >= 6 && strncmp (p, ", %esp", 6) == 0) {
+            return neg ? -(int) n : (int) n;
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static int inline_body_stack_delta (const char *body) {
+
+    const char *p = body;
+    int stack_bytes = 0;
+    
+    if (!body) {
+        return 0;
+    }
+    
+    while (*p) {
+    
+        const char *line = p;
+        const char *eol = strchr (p, '\n');
+        const char *next = eol ? eol + 1 : p + strlen (p);
+        size_t len = (size_t) (next - line);
+        
+        stack_bytes += inline_line_stack_delta (line, len);
+        p = next;
+    
+    }
+    
+    return stack_bytes;
+
+}
+
+static int inline_parse_mov_eax_imm (const char *line, long *value) {
+
+    const char *p = line;
+    
+    int neg = 0;
+    long v = 0;
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    if (strncmp (p, "mov eax,", 8) != 0) {
+        return 0;
+    }
+    
+    p += 8;
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    if (*p == '-') {
+    
+        neg = 1;
+        p++;
+    
+    }
+    
+    if (*p < '0' || *p > '9') {
+        return 0;
+    }
+    
+    while (*p >= '0' && *p <= '9') {
+    
+        v = (v * 10) + (*p - '0');
+        p++;
+    
+    }
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p != '\0') {
+        return 0;
+    }
+    
+    *value = neg ? -v : v;
+    return 1;
+
+}
+
+static int inline_parse_mov_edx_imm (const char *line, long *value) {
+
+    const char *p = line;
+    
+    int neg = 0;
+    long v = 0;
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    if (strncmp (p, "mov edx,", 8) != 0) {
+        return 0;
+    }
+    
+    p += 8;
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    if (*p == '-') {
+    
+        neg = 1;
+        p++;
+    
+    }
+    
+    if (*p < '0' || *p > '9') {
+        return 0;
+    }
+    
+    while (*p >= '0' && *p <= '9') {
+    
+        v = (v * 10) + (*p - '0');
+        p++;
+    
+    }
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p != '\0') {
+        return 0;
+    }
+    
+    *value = neg ? -v : v;
+    return 1;
+
+}
+
+static int inline_line_is_exact (const char *line, const char *text) {
+
+    const char *p = line;
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    if (strncmp (p, text, strlen (text)) != 0) {
+        return 0;
+    }
+    
+    p += strlen (text);
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    return *p == '\0';
+
+}
+
+
+static int inline_parse_jump_label (const char *line, const char *op, char *label, size_t label_size);
+
+static unsigned long inline_u32 (long v) {
+    return ((unsigned long) v) & 0xffffffffUL;
+}
+
+static long inline_s32 (long v) {
+
+    unsigned long u = inline_u32 (v);
+    
+    if (u & 0x80000000UL) {
+        return (long) (u - 0x100000000UL);
+    }
+    
+    return (long) u;
+
+}
+
+static int inline_fold_binary_eax_edx (const char *line, long lhs, long rhs, long *out) {
+
+    unsigned long ulhs;
+    unsigned long urhs;
+    unsigned long ures;
+    
+    if (!line || !out) {
+        return 0;
+    }
+    
+    ulhs = inline_u32 (lhs);
+    urhs = inline_u32 (rhs);
+    
+    if (inline_line_is_exact (line, "add eax, edx")) {
+        ures = ulhs + urhs;
+    } else if (inline_line_is_exact (line, "sub eax, edx")) {
+        ures = ulhs - urhs;
+    } else if (inline_line_is_exact (line, "imul eax, edx")) {
+        ures = (unsigned long) (inline_s32 (lhs) * inline_s32 (rhs));
+    } else if (inline_line_is_exact (line, "and eax, edx")) {
+        ures = ulhs & urhs;
+    } else if (inline_line_is_exact (line, "or eax, edx")) {
+        ures = ulhs | urhs;
+    } else if (inline_line_is_exact (line, "xor eax, edx")) {
+        ures = ulhs ^ urhs;
+    } else {
+        return 0;
+    }
+    
+    *out = inline_s32 ((long) ures);
+    return 1;
+
+}
+
+static int inline_fold_shift_eax_edx (const char *line, long lhs, long rhs, long *out) {
+
+    unsigned long ulhs;
+    unsigned long ures;
+    unsigned int count;
+    
+    long slhs;
+    
+    if (!line || !out) {
+        return 0;
+    }
+    
+    count = (unsigned int) (inline_u32 (rhs) & 31UL);
+    ulhs = inline_u32 (lhs);
+    
+    if (inline_line_is_exact (line, "shl eax, cl")) {
+        ures = ulhs << count;
+    } else if (inline_line_is_exact (line, "sal eax, cl")) {
+        ures = ulhs << count;
+    } else if (inline_line_is_exact (line, "sar eax, cl")) {
+    
+        slhs = inline_s32 (lhs);
+        ures = inline_u32 (slhs >> count);
+    
+    } else if (inline_line_is_exact (line, "shr eax, cl")) {
+        ures = ulhs >> count;
+    } else {
+        return 0;
+    }
+    
+    *out = inline_s32 ((long) ures);
+    return 1;
+
+}
+
+static int inline_fold_div_eax_edx (long lhs, long rhs, long *out) {
+
+    long slhs;
+    long srhs;
+    
+    if (!out) {
+        return 0;
+    }
+    
+    srhs = inline_s32 (rhs);
+    
+    if (srhs == 0) {
+        return 0;
+    }
+    
+    slhs = inline_s32 (lhs);
+    
+    *out = inline_s32 (slhs / srhs);
+    return 1;
+
+}
+
+static int inline_fold_mod_eax_edx (long lhs, long rhs, long *out) {
+
+    long slhs;
+    long srhs;
+    
+    if (!out) {
+        return 0;
+    }
+    
+    srhs = inline_s32 (rhs);
+    
+    if (srhs == 0) {
+        return 0;
+    }
+    
+    slhs = inline_s32 (lhs);
+    
+    *out = inline_s32 (slhs % srhs);
+    return 1;
+
+}
+
+static int inline_parse_cond_jump_label (const char *line, char *op, size_t op_size, char *label, size_t label_size) {
+
+    static const char *const ops[] = {
+        "jz", "jnz", "je", "jne", "ja", "jae", "jb", "jbe",
+        "jg", "jge", "jl", "jle", "js", "jns", 0
+    };
+    
+    int i;
+    
+    if (!op || op_size == 0) {
+        return 0;
+    }
+    
+    for (i = 0; ops[i]; i++) {
+    
+        if (inline_parse_jump_label (line, ops[i], label, label_size)) {
+        
+            strncpy (op, ops[i], op_size - 1);
+            op[op_size - 1] = '\0';
+            
+            return 1;
+        
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static int inline_eval_test_jump (const char *op, long value) {
+
+    long svalue = inline_s32 (value);
+    
+    if (strcmp (op, "jz") == 0 || strcmp (op, "je") == 0) {
+        return svalue == 0;
+    }
+    
+    if (strcmp (op, "jnz") == 0 || strcmp (op, "jne") == 0) {
+        return svalue != 0;
+    }
+    
+    if (strcmp (op, "js") == 0) {
+        return svalue < 0;
+    }
+    
+    if (strcmp (op, "jns") == 0) {
+        return svalue >= 0;
+    }
+    
+    return 0;
+
+}
+
+static int inline_eval_cmp_jump (const char *op, long lhs, long rhs) {
+
+    long slhs = inline_s32 (lhs);
+    long srhs = inline_s32 (rhs);
+    
+    unsigned long ulhs = inline_u32 (lhs);
+    unsigned long urhs = inline_u32 (rhs);
+    
+    if (strcmp (op, "jz") == 0 || strcmp (op, "je") == 0) {
+        return slhs == srhs;
+    }
+    
+    if (strcmp (op, "jnz") == 0 || strcmp (op, "jne") == 0) {
+        return slhs != srhs;
+    }
+    
+    if (strcmp (op, "jg") == 0) {
+        return slhs > srhs;
+    }
+    
+    if (strcmp (op, "jge") == 0) {
+        return slhs >= srhs;
+    }
+    
+    if (strcmp (op, "jl") == 0) {
+        return slhs < srhs;
+    }
+    
+    if (strcmp (op, "jle") == 0) {
+        return slhs <= srhs;
+    }
+    
+    if (strcmp (op, "ja") == 0) {
+        return ulhs > urhs;
+    }
+    
+    if (strcmp (op, "jae") == 0) {
+        return ulhs >= urhs;
+    }
+    
+    if (strcmp (op, "jb") == 0) {
+        return ulhs < urhs;
+    }
+    
+    if (strcmp (op, "jbe") == 0) {
+        return ulhs <= urhs;
+    }
+    
+    return 0;
+
+}
+
+static int inline_parse_jump_label (const char *line, const char *op, char *label, size_t label_size) {
+
+    const char *p = line;
+    size_t n = 0;
+    
+    if (!label || label_size == 0) {
+        return 0;
+    }
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    if (strncmp (p, op, strlen (op)) != 0) {
+        return 0;
+    }
+    
+    p += strlen (op);
+    
+    if (*p != ' ' && *p != '\t') {
+        return 0;
+    }
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_' || *p == '.')) {
+        return 0;
+    }
+    
+    while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') ||
+           (*p >= '0' && *p <= '9') || *p == '_' || *p == '.') {
+    
+        if (n + 1 < label_size) {
+            label[n++] = *p;
+        }
+        
+        p++;
+    }
+    
+    label[n] = '\0';
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    return *p == '\0';
+
+}
+
+
+
+static int inline_parse_any_jump_label (const char *line, char *label, size_t label_size) {
+
+    static const char *const ops[] = {
+        "jmp", "jz", "jnz", "je", "jne", "jc", "jnc",
+        "ja", "jae", "jb", "jbe", "jg", "jge", "jl", "jle",
+        "js", "jns", "jo", "jno", "jp", "jpe", "jnp", "jpo",
+        0
+    };
+    
+    int i;
+    
+    for (i = 0; ops[i]; i++) {
+    
+        if (inline_parse_jump_label (line, ops[i], label, label_size)) {
+            return 1;
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static int inline_parse_label_definition (const char *line, char *label, size_t label_size) {
+
+    const char *p = line;
+    size_t n = 0;
+    
+    if (!label || label_size == 0) {
+        return 0;
+    }
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_' || *p == '.')) {
+        return 0;
+    }
+    
+    while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') ||
+           (*p >= '0' && *p <= '9') || *p == '_' || *p == '.') {
+    
+        if (n + 1 < label_size) {
+            label[n++] = *p;
+        }
+        
+        p++;
+    }
+    
+    label[n] = '\0';
+    
+    if (*p != ':') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    return *p == '\0';
+
+}
+
+static const char *inline_find_chain_target (char **from, char **to, int count, const char *label) {
+
+    int i;
+    
+    if (!label) {
+        return 0;
+    }
+    
+    for (i = 0; i < count; i++) {
+    
+        if (from[i] && to[i] && strcmp (from[i], label) == 0) {
+            return to[i];
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static void inline_copy_string (char *dst, const char *src, size_t dst_size) {
+
+    size_t len;
+    
+    if (!dst || dst_size == 0) {
+        return;
+    }
+    
+    if (!src) {
+    
+        dst[0] = '\0';
+        return;
+    
+    }
+    
+    len = strlen (src);
+    
+    if (len >= dst_size) {
+        len = dst_size - 1;
+    }
+    
+    memcpy (dst, src, len);
+    dst[len] = '\0';
+
+}
+
+static int inline_resolve_jump_chain (char **from, char **to, int count, const char *label, char *out, size_t out_size) {
+
+    const char *cur = label;
+    const char *next;
+    
+    int changed = 0;
+    int depth = 0;
+    
+    if (!label || !out || out_size == 0) {
+        return 0;
+    }
+    
+    while (depth++ < count) {
+    
+        next = inline_find_chain_target (from, to, count, cur);
+        
+        if (!next || strcmp (next, cur) == 0) {
+            break;
+        }
+        
+        cur = next;
+        changed = 1;
+    
+    }
+    
+    if (!changed || strcmp (cur, label) == 0) {
+        return 0;
+    }
+    
+    if (out && cur && out_size > 0) {
+        inline_copy_string (out, cur, out_size);
+    }
+    
+    return 1;
+
+}
+
+static int inline_parse_esp_reference (const char *p, int *offset) {
+
+    const char *start = p;
+    
+    int off = 0;
+    int neg = 0;
+    
+    if (strncmp (p, "[esp]", 5) == 0) {
+    
+        *offset = 0;
+        return 5;
+    
+    }
+    
+    if (strncmp (p, "[esp + ", 7) == 0) {
+        p += 7;
+    } else if (strncmp (p, "[esp+", 5) == 0) {
+        p += 5;
+    } else if (strncmp (p, "[esp - ", 7) == 0) {
+        neg = 1;
+        p += 7;
+    } else if (strncmp (p, "[esp-", 5) == 0) {
+        neg = 1;
+        p += 5;
+    } else {
+        return 0;
+    }
+    
+    if (*p < '0' || *p > '9') {
+        return 0;
+    }
+    
+    while (*p >= '0' && *p <= '9') {
+    
+        off = (off * 10) + (*p - '0');
+        p++;
+    
+    }
+    
+    if (*p != ']') {
+        return 0;
+    }
+    
+    *offset = neg ? -off : off;
+    return (int) ((p + 1) - start);
+
+}
+
+static int inline_parse_store_eax_to_esp (const char *line, int *offset) {
+
+    const char *p = line;
+    int n;
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    if (strncmp (p, "mov dword ptr ", 14) != 0) {
+        return 0;
+    }
+    
+    p += 14;
+    n = inline_parse_esp_reference (p, offset);
+    
+    if (!n) {
+        return 0;
+    }
+    
+    p += n;
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    if (strncmp (p, ", eax", 5) != 0) {
+        return 0;
+    }
+    
+    p += 5;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    return *p == '\0';
+
+}
+
+static int inline_parse_load_eax_from_esp (const char *line, int *offset) {
+
+    const char *p = line;
+    int n;
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    if (strncmp (p, "mov eax, dword ptr ", 19) != 0) {
+        return 0;
+    }
+    
+    p += 19;
+    n = inline_parse_esp_reference (p, offset);
+    
+    if (!n) {
+        return 0;
+    }
+    
+    p += n;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    return *p == '\0';
+
+}
+
+static int inline_parse_load_edx_from_esp (const char *line, int *offset) {
+
+    const char *p = line;
+    int n;
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    if (strncmp (p, "mov edx, dword ptr ", 19) != 0) {
+        return 0;
+    }
+    
+    p += 19;
+    n = inline_parse_esp_reference (p, offset);
+    
+    if (!n) {
+        return 0;
+    }
+    
+    p += n;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    return *p == '\0';
+
+}
+
+static int inline_parse_addsub_esp_imm1 (const char *line, int *offset, long *delta) {
+
+    const char *p = line;
+    int n;
+    int is_sub = 0;
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    if (strncmp (p, "add dword ptr ", 14) == 0) {
+        p += 14;
+    } else if (strncmp (p, "sub dword ptr ", 14) == 0) {
+    
+        is_sub = 1;
+        p += 14;
+    
+    } else {
+        return 0;
+    }
+    
+    n = inline_parse_esp_reference (p, offset);
+    
+    if (!n) {
+        return 0;
+    }
+    
+    p += n;
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    if (strncmp (p, ", 1", 3) != 0) {
+        return 0;
+    }
+    
+    p += 3;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p != '\0') {
+        return 0;
+    }
+    
+    *delta = is_sub ? -1 : 1;
+    return 1;
+
+}
+
+static char *inline_dup_text_slice (const char *start, size_t len) {
+
+    char *out = (char *) malloc (len + 1);
+    
+    if (!out) {
+        return 0;
+    }
+    
+    memcpy (out, start, len);
+    out[len] = '\0';
+    
+    return out;
+
+}
+
+static void inline_clear_slots (int *slot_valid, long *slot_value) {
+
+    int i;
+    
+    for (i = 0; i < 64; i++) {
+    
+        slot_valid[i] = 0;
+        slot_value[i] = 0;
+    
+    }
+
+}
+
+static void emit_inline_optimized_text (const char *text, FILE *ofp) {
+
+    const char *p = text;
+    
+    char **lines;
+    char **repl;
+    
+    int *skip;
+    int *store_slot;
+    int count = 0;
+    int i;
+    
+    long slot_value[64];
+    
+    int slot_valid[64];
+    int slot_real_read[64];
+    
+    long eax_value = 0;
+    
+    int eax_valid = 0;
+    int alloc_line = -1;
+    int free_line = -1;
+    int alloc_bytes = 0;
+    int can_remove_area = 0;
+    
+    char **chain_from = 0;
+    char **chain_to = 0;
+    
+    int *label_refs = 0;
+    
+    if (!text || !ofp) {
+        return;
+    }
+    
+    for (p = text; *p; p++) {
+    
+        if (*p == '\n') {
+            count++;
+        }
+    
+    }
+    
+    if (p != text && p[-1] != '\n') {
+        count++;
+    }
+    
+    if (count <= 0) {
+        return;
+    }
+    
+    lines = (char **) calloc ((size_t) count, sizeof (*lines));
+    repl = (char **) calloc ((size_t) count, sizeof (*repl));
+    skip = (int *) calloc ((size_t) count, sizeof (*skip));
+    store_slot = (int *) malloc ((size_t) count * sizeof (*store_slot));
+    chain_from = (char **) calloc ((size_t) count, sizeof (*chain_from));
+    chain_to = (char **) calloc ((size_t) count, sizeof (*chain_to));
+    label_refs = (int *) calloc ((size_t) count, sizeof (*label_refs));
+    
+    if (!lines || !repl || !skip || !store_slot || !chain_from || !chain_to || !label_refs) {
+    
+        free (lines);
+        free (repl);
+        free (skip);
+        free (store_slot);
+        free (chain_from);
+        free (chain_to);
+        free (label_refs);
+        
+        fputs (text, ofp);
+        return;
+    
+    }
+    
+    for (i = 0; i < count; i++) {
+        store_slot[i] = -1;
+    }
+    
+    p = text;
+    i = 0;
+    
+    while (*p && i < count) {
+    
+        const char *line = p;
+        const char *eol = strchr (p, '\n');
+        const char *next = eol ? eol + 1 : p + strlen (p);
+        size_t len = (size_t) (next - line);
+        
+        lines[i++] = inline_dup_text_slice (line, len);
+        p = next;
+    
+    }
+    
+    count = i;
+    inline_clear_slots (slot_valid, slot_value);
+    
+    for (i = 0; i < 64; i++) {
+        slot_real_read[i] = 0;
+    }
+    
+    for (i = 0; i < count; i++) {
+    
+        char *buf = lines[i];
+        int offset;
+        int slot;
+        long value;
+        long mem_delta;
+        int delta;
+        
+        if (!buf) {
+            continue;
+        }
+        
+        delta = inline_line_stack_delta (buf, strlen (buf));
+        
+        if (delta > 0 && alloc_line < 0) {
+        
+            alloc_line = i;
+            alloc_bytes = delta;
+            
+            eax_valid = 0;
+            continue;
+        
+        }
+        
+        if (delta < 0 && alloc_line >= 0 && -delta == alloc_bytes) {
+        
+            free_line = i;
+            eax_valid = 0;
+            
+            continue;
+        
+        }
+        
+        if (inline_parse_mov_eax_imm (buf, &value)) {
+        
+            eax_value = value;
+            eax_valid = 1;
+            
+            continue;
+        
+        }
+        
+        if (inline_parse_store_eax_to_esp (buf, &offset)) {
+        
+            if (offset >= 0 && (offset % 4) == 0) {
+            
+                slot = offset / 4;
+                
+                if (slot >= 0 && slot < 64) {
+                
+                    store_slot[i] = slot;
+                    
+                    if (eax_valid) {
+                    
+                        slot_valid[slot] = 1;
+                        slot_value[slot] = eax_value;
+                    
+                    } else {
+                        slot_valid[slot] = 0;
+                    }
+                
+                }
+            
+            }
+            
+            continue;
+        
+        }
+        
+        if (inline_parse_load_eax_from_esp (buf, &offset)) {
+        
+            if (offset >= 0 && (offset % 4) == 0) {
+            
+                slot = offset / 4;
+                
+                if (slot >= 0 && slot < 64 && slot_valid[slot]) {
+                
+                    char tmp[128];
+                    sprintf (tmp, "    mov eax, %ld\n", slot_value[slot]);
+                    
+                    repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+                    
+                    eax_value = slot_value[slot];
+                    eax_valid = 1;
+                    
+                    continue;
+                
+                }
+                
+                if (slot >= 0 && slot < 64) {
+                    slot_real_read[slot] = 1;
+                }
+            
+            }
+            
+            eax_valid = 0;
+            continue;
+        }
+        
+        if (inline_parse_load_edx_from_esp (buf, &offset)) {
+        
+            if (offset >= 0 && (offset % 4) == 0) {
+            
+                slot = offset / 4;
+                
+                if (slot >= 0 && slot < 64 && slot_valid[slot]) {
+                
+                    char tmp[128];
+                    sprintf (tmp, "    mov edx, %ld\n", slot_value[slot]);
+                    
+                    repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+                    continue;
+                
+                }
+                
+                if (slot >= 0 && slot < 64) {
+                    slot_real_read[slot] = 1;
+                }
+            
+            }
+            
+            continue;
+        
+        }
+        
+        if (inline_parse_addsub_esp_imm1 (buf, &offset, &mem_delta)) {
+        
+            if (offset >= 0 && (offset % 4) == 0) {
+            
+                slot = offset / 4;
+                
+                if (slot >= 0 && slot < 64) {
+                
+                    if (slot_valid[slot]) {
+                        slot_value[slot] = inline_s32 (slot_value[slot] + mem_delta);
+                        skip[i] = 1;
+                    }
+                    
+                    continue;
+                }
+            }
+            
+            eax_valid = 0;
+            continue;
+        
+        }
+        
+        if (strstr (buf, "esp")) {
+        
+            inline_clear_slots (slot_valid, slot_value);
+            eax_valid = 0;
+        
+        } else if (strstr (buf, "eax")) {
+            eax_valid = 0;
+        }
+    
+    }
+    
+    if (alloc_line >= 0 && free_line > alloc_line && alloc_bytes > 0) {
+    
+        int any_remaining_esp = 0;
+        int slots = alloc_bytes / 4;
+        
+        for (i = 0; i < count; i++) {
+        
+            if (i == alloc_line || i == free_line) {
+                continue;
+            }
+            
+            if (store_slot[i] >= 0 && store_slot[i] < slots && !slot_real_read[store_slot[i]]) {
+                continue;
+            }
+            
+            if (repl[i]) {
+            
+                if (strstr (repl[i], "esp")) {
+                    any_remaining_esp = 1;
+                    break;
+                }
+            
+            } else if (lines[i] && strstr (lines[i], "esp")) {
+            
+                any_remaining_esp = 1;
+                break;
+            
+            }
+        
+        }
+        
+        if (!any_remaining_esp) {
+        
+            can_remove_area = 1;
+            skip[alloc_line] = 1;
+            skip[free_line] = 1;
+            
+            for (i = 0; i < count; i++) {
+            
+                if (store_slot[i] >= 0 && store_slot[i] < slots && !slot_real_read[store_slot[i]]) {
+                    skip[i] = 1;
+                }
+            
+            }
+            
+            /*
+             * If an argument copy was folded away, the immediately preceding
+             * constant load was only there to feed that copy.  Drop it too;
+             * otherwise multi-argument inline calls leave noise like:
+             *
+             *     mov eax, 1
+             *     mov eax, 2
+             *
+             * after both temporary argument stores have been removed.
+             */
+            for (i = 0; i < count; i++) {
+            
+                long ignored_value;
+                int j;
+                
+                if (!skip[i] || store_slot[i] < 0) {
+                    continue;
+                }
+                
+                for (j = i - 1; j >= 0; j--) {
+                
+                    if (skip[j]) {
+                        continue;
+                    }
+                    
+                    if (inline_parse_mov_eax_imm (lines[j], &ignored_value)) {
+                        skip[j] = 1;
+                    }
+                    
+                    break;
+                
+                }
+            
+            }
+        
+        }
+    
+    }
+    
+    (void) can_remove_area;
+    
+    for (i = 0; i < count; i++) {
+    
+        long first_value;
+        int j;
+        
+        if (skip[i] || !lines[i] || !inline_parse_mov_eax_imm (lines[i], &first_value)) {
+            continue;
+        }
+        
+        for (j = i + 1; j < count; j++) {
+        
+            long next_value;
+            const char *check;
+            
+            if (skip[j]) {
+                continue;
+            }
+            
+            check = repl[j] ? repl[j] : lines[j];
+            
+            if (!check) {
+                continue;
+            }
+            
+            if (!strstr (check, "eax")) {
+                continue;
+            }
+            
+            if (inline_parse_mov_eax_imm (check, &next_value) && next_value == first_value) {
+                skip[i] = 1;
+            }
+            
+            break;
+        
+        }
+    
+    }
+    
+    for (i = 0; i + 3 < count; i++) {
+    
+        long lhs;
+        long rhs;
+        
+        char cond_label[128];
+        char opbuf[16];
+        char tmp[192];
+        
+        const char *l0;
+        const char *l1;
+        const char *l2;
+        const char *l3;
+        
+        int taken;
+        
+        if (skip[i]) {
+            continue;
+        }
+        
+        l0 = repl[i] ? repl[i] : lines[i];
+        l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+        l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+        l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+        
+        if (!l0 || !l1 || !l2 || !l3) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l2, "cmp eax, edx")) {
+            continue;
+        }
+        
+        if (!inline_parse_cond_jump_label (l3, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+            continue;
+        }
+        
+        taken = inline_eval_cmp_jump (opbuf, lhs, rhs);
+        
+        if (taken) {
+        
+            sprintf (tmp, "    jmp %s\n", cond_label);
+            free (repl[i]);
+            
+            repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+            
+            skip[i + 1] = 1;
+            skip[i + 2] = 1;
+            skip[i + 3] = 1;
+        
+        } else {
+        
+            skip[i] = 1;
+            skip[i + 1] = 1;
+            skip[i + 2] = 1;
+            skip[i + 3] = 1;
+        
+        }
+    
+    }
+    
+    for (i = 0; i + 4 < count; i++) {
+    
+        long lhs;
+        long rhs;
+        
+        char false_label[128];
+        char true_label[128];
+        char opbuf[16];
+        char tmp[192];
+        
+        const char *l0;
+        const char *l1;
+        const char *l2;
+        const char *l3;
+        const char *l4;
+        
+        if (skip[i]) {
+            continue;
+        }
+        
+        l0 = repl[i] ? repl[i] : lines[i];
+        l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+        l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+        l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+        l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+        
+        if (!l0 || !l1 || !l2 || !l3 || !l4) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l2, "cmp eax, edx")) {
+            continue;
+        }
+        
+        if (!inline_parse_cond_jump_label (l3, opbuf, sizeof (opbuf), false_label, sizeof (false_label))) {
+            continue;
+        }
+        
+        if (!inline_parse_jump_label (l4, "jmp", true_label, sizeof (true_label))) {
+            continue;
+        }
+        
+        sprintf (tmp, "    jmp %s\n", inline_eval_cmp_jump (opbuf, lhs, rhs) ? false_label : true_label);
+        free (repl[i]);
+        
+        repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+        
+        skip[i + 1] = 1;
+        skip[i + 2] = 1;
+        skip[i + 3] = 1;
+        skip[i + 4] = 1;
+    
+    }
+    
+    for (i = 0; i + 4 < count; i++) {
+
+        long lhs;
+        long rhs;
+        
+        char cond_label[128];
+        char opbuf[16];
+        char tmp[192];
+        
+        const char *l0;
+        const char *l1;
+        const char *l2;
+        const char *l3;
+        const char *l4;
+        
+        int taken;
+        
+        if (skip[i]) {
+            continue;
+        }
+        
+        l0 = repl[i] ? repl[i] : lines[i];
+        l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+        l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+        l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+        l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+        
+        if (!l0 || !l1 || !l2 || !l3 || !l4) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+            continue;
+        }
+        
+        if (!inline_fold_binary_eax_edx (l2, lhs, rhs, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l3, "test eax, eax")) {
+            continue;
+        }
+        
+        if (!inline_parse_cond_jump_label (l4, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+            continue;
+        }
+        
+        taken = inline_eval_test_jump (opbuf, lhs);
+        
+        if (taken) {
+        
+            sprintf (tmp, "    jmp %s\n", cond_label);
+            free (repl[i]);
+            
+            repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+            
+            skip[i + 1] = 1;
+            skip[i + 2] = 1;
+            skip[i + 3] = 1;
+            skip[i + 4] = 1;
+        
+        } else {
+        
+            skip[i] = 1;
+            skip[i + 1] = 1;
+            skip[i + 2] = 1;
+            skip[i + 3] = 1;
+            skip[i + 4] = 1;
+        
+        }
+    
+    }
+    
+    for (i = 0; i + 5 < count; i++) {
+    
+        long lhs;
+        long rhs;
+        long cmp_rhs;
+        long folded;
+        
+        char cond_label[128];
+        char opbuf[16];
+        char tmp[192];
+        
+        const char *l0;
+        const char *l1;
+        const char *l2;
+        const char *l3;
+        const char *l4;
+        const char *l5;
+        
+        int taken;
+        
+        if (skip[i]) {
+            continue;
+        }
+        
+        l0 = repl[i] ? repl[i] : lines[i];
+        l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+        l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+        l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+        l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+        l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+        
+        if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+            continue;
+        }
+        
+        if (!inline_fold_binary_eax_edx (l2, lhs, rhs, &folded)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l3, &cmp_rhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l4, "cmp eax, edx")) {
+            continue;
+        }
+        
+        if (!inline_parse_cond_jump_label (l5, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+            continue;
+        }
+        
+        taken = inline_eval_cmp_jump (opbuf, folded, cmp_rhs);
+        
+        if (taken) {
+        
+            sprintf (tmp, "    jmp %s\n", cond_label);
+            free (repl[i]);
+            
+            repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+            
+            skip[i + 1] = 1;
+            skip[i + 2] = 1;
+            skip[i + 3] = 1;
+            skip[i + 4] = 1;
+            skip[i + 5] = 1;
+        
+        } else {
+        
+            skip[i] = 1;
+            skip[i + 1] = 1;
+            skip[i + 2] = 1;
+            skip[i + 3] = 1;
+            skip[i + 4] = 1;
+            skip[i + 5] = 1;
+        
+        }
+    
+    }
+    
+    for (i = 0; i + 5 < count; i++) {
+    
+        long lhs;
+        long rhs;
+        
+        char false_label[128];
+        char true_label[128];
+        char opbuf[16];
+        char tmp[192];
+        
+        const char *l0;
+        const char *l1;
+        const char *l2;
+        const char *l3;
+        const char *l4;
+        const char *l5;
+        
+        if (skip[i]) {
+            continue;
+        }
+        
+        l0 = repl[i] ? repl[i] : lines[i];
+        l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+        l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+        l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+        l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+        l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+        
+        if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+            continue;
+        }
+        
+        if (!inline_fold_binary_eax_edx (l2, lhs, rhs, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l3, "test eax, eax")) {
+            continue;
+        }
+        
+        if (!inline_parse_cond_jump_label (l4, opbuf, sizeof (opbuf), false_label, sizeof (false_label))) {
+            continue;
+        }
+        
+        if (!inline_parse_jump_label (l5, "jmp", true_label, sizeof (true_label))) {
+            continue;
+        }
+        
+        sprintf (tmp, "    jmp %s\n", inline_eval_test_jump (opbuf, lhs) ? false_label : true_label);
+        free (repl[i]);
+        
+        repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+        
+        skip[i + 1] = 1;
+        skip[i + 2] = 1;
+        skip[i + 3] = 1;
+        skip[i + 4] = 1;
+        skip[i + 5] = 1;
+    
+    }
+    
+    for (i = 0; i + 6 < count; i++) {
+    
+        long lhs;
+        long rhs;
+        long cmp_rhs;
+        long folded;
+        
+        int jump_taken = 0;
+        
+        char cond_label[128];
+        char fall_label[128];
+        char opbuf[16];
+        char tmp[192];
+        
+        const char *l0;
+        const char *l1;
+        const char *l2;
+        const char *l3;
+        const char *l4;
+        const char *l5;
+        const char *l6;
+        
+        if (skip[i]) {
+            continue;
+        }
+        
+        l0 = repl[i] ? repl[i] : lines[i];
+        l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+        l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+        l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+        l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+        l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+        l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6];
+        
+        if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+            continue;
+        }
+        
+        if (!inline_fold_binary_eax_edx (l2, lhs, rhs, &folded)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l3, &cmp_rhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l4, "cmp eax, edx")) {
+            continue;
+        }
+        
+        if (!inline_parse_cond_jump_label (l5, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+            continue;
+        }
+        
+        jump_taken = inline_eval_cmp_jump (opbuf, folded, cmp_rhs);
+        
+        if (!inline_parse_jump_label (l6, "jmp", fall_label, sizeof (fall_label))) {
+            continue;
+        }
+        
+        sprintf (tmp, "    jmp %s\n", jump_taken ? cond_label : fall_label);
+        free (repl[i]);
+        
+        repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+        
+        skip[i + 1] = 1;
+        skip[i + 2] = 1;
+        skip[i + 3] = 1;
+        skip[i + 4] = 1;
+        skip[i + 5] = 1;
+        skip[i + 6] = 1;
+    
+    }
+    
+    for (i = 0; i + 5 < count; i++) {
+
+        long lhs;
+        long rhs;
+        long folded;
+        
+        char cond_label[128];
+        char opbuf[16];
+        char tmp[192];
+        
+        const char *l0;
+        const char *l1;
+        const char *l2;
+        const char *l3;
+        const char *l4;
+        const char *l5;
+        
+        int taken;
+        
+        if (skip[i]) {
+            continue;
+        }
+        
+        l0 = repl[i] ? repl[i] : lines[i];
+        l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+        l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+        l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+        l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+        l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+        
+        if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l2, "mov ecx, edx")) {
+            continue;
+        }
+        
+        if (!inline_fold_shift_eax_edx (l3, lhs, rhs, &folded)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l4, "test eax, eax")) {
+            continue;
+        }
+        
+        if (!inline_parse_cond_jump_label (l5, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+            continue;
+        }
+        
+        taken = inline_eval_test_jump (opbuf, folded);
+        
+        if (taken) {
+        
+            sprintf (tmp, "    jmp %s\n", cond_label);
+            free (repl[i]);
+            
+            repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+            
+            skip[i + 1] = 1;
+            skip[i + 2] = 1;
+            skip[i + 3] = 1;
+            skip[i + 4] = 1;
+            skip[i + 5] = 1;
+        
+        } else {
+        
+            skip[i] = 1;
+            skip[i + 1] = 1;
+            skip[i + 2] = 1;
+            skip[i + 3] = 1;
+            skip[i + 4] = 1;
+            skip[i + 5] = 1;
+        
+        }
+    
+    }
+    
+    for (i = 0; i + 6 < count; i++) {
+    
+        long lhs;
+        long rhs;
+        long cmp_rhs;
+        long folded;
+        
+        char cond_label[128];
+        char opbuf[16];
+        char tmp[192];
+        
+        const char *l0;
+        const char *l1;
+        const char *l2;
+        const char *l3;
+        const char *l4;
+        const char *l5;
+        const char *l6;
+        
+        int taken;
+        
+        if (skip[i]) {
+            continue;
+        }
+        
+        l0 = repl[i] ? repl[i] : lines[i];
+        l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+        l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+        l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+        l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+        l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+        l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6];
+        
+        if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l2, "mov ecx, edx")) {
+            continue;
+        }
+        
+        if (!inline_fold_shift_eax_edx (l3, lhs, rhs, &folded)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l4, &cmp_rhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l5, "cmp eax, edx")) {
+            continue;
+        }
+        
+        if (!inline_parse_cond_jump_label (l6, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+            continue;
+        }
+        
+        taken = inline_eval_cmp_jump (opbuf, folded, cmp_rhs);
+        
+        if (taken) {
+        
+            sprintf (tmp, "    jmp %s\n", cond_label);
+            free (repl[i]);
+            
+            repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+            
+            skip[i + 1] = 1;
+            skip[i + 2] = 1;
+            skip[i + 3] = 1;
+            skip[i + 4] = 1;
+            skip[i + 5] = 1;
+            skip[i + 6] = 1;
+        
+        } else {
+        
+            skip[i] = 1;
+            skip[i + 1] = 1;
+            skip[i + 2] = 1;
+            skip[i + 3] = 1;
+            skip[i + 4] = 1;
+            skip[i + 5] = 1;
+            skip[i + 6] = 1;
+        
+        }
+    
+    }
+    
+    for (i = 0; i + 6 < count; i++) {
+    
+        long lhs;
+        long rhs;
+        long folded;
+        
+        char false_label[128];
+        char true_label[128];
+        char opbuf[16];
+        char tmp[192];
+        
+        const char *l0;
+        const char *l1;
+        const char *l2;
+        const char *l3;
+        const char *l4;
+        const char *l5;
+        const char *l6;
+        
+        if (skip[i]) {
+            continue;
+        }
+        
+        l0 = repl[i] ? repl[i] : lines[i];
+        l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+        l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+        l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+        l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+        l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+        l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6];
+        
+        if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l2, "mov ecx, edx")) {
+            continue;
+        }
+        
+        if (!inline_fold_shift_eax_edx (l3, lhs, rhs, &folded)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l4, "test eax, eax")) {
+            continue;
+        }
+        
+        if (!inline_parse_cond_jump_label (l5, opbuf, sizeof (opbuf), false_label, sizeof (false_label))) {
+            continue;
+        }
+        
+        if (!inline_parse_jump_label (l6, "jmp", true_label, sizeof (true_label))) {
+            continue;
+        }
+        
+        sprintf (tmp, "    jmp %s\n", inline_eval_test_jump (opbuf, folded) ? false_label : true_label);
+        free (repl[i]);
+        
+        repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+        
+        skip[i + 1] = 1;
+        skip[i + 2] = 1;
+        skip[i + 3] = 1;
+        skip[i + 4] = 1;
+        skip[i + 5] = 1;
+        skip[i + 6] = 1;
+    
+    }
+    
+    for (i = 0; i + 7 < count; i++) {
+    
+        long lhs;
+        long rhs;
+        long cmp_rhs;
+        long folded;
+        
+        int jump_taken = 0;
+        
+        char cond_label[128];
+        char fall_label[128];
+        char opbuf[16];
+        char tmp[192];
+        
+        const char *l0;
+        const char *l1;
+        const char *l2;
+        const char *l3;
+        const char *l4;
+        const char *l5;
+        const char *l6;
+        const char *l7;
+        
+        if (skip[i]) {
+            continue;
+        }
+        
+        l0 = repl[i] ? repl[i] : lines[i];
+        l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+        l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+        l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+        l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+        l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+        l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6];
+        l7 = repl[i + 7] ? repl[i + 7] : lines[i + 7];
+        
+        if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6 || !l7) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l2, "mov ecx, edx")) {
+            continue;
+        }
+        
+        if (!inline_fold_shift_eax_edx (l3, lhs, rhs, &folded)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l4, &cmp_rhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l5, "cmp eax, edx")) {
+            continue;
+        }
+        
+        if (!inline_parse_cond_jump_label (l6, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+            continue;
+        }
+        
+        jump_taken = inline_eval_cmp_jump (opbuf, folded, cmp_rhs);
+        
+        if (!inline_parse_jump_label (l7, "jmp", fall_label, sizeof (fall_label))) {
+            continue;
+        }
+        
+        sprintf (tmp, "    jmp %s\n", jump_taken ? cond_label : fall_label);
+        free (repl[i]);
+        
+        repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+        
+        skip[i + 1] = 1;
+        skip[i + 2] = 1;
+        skip[i + 3] = 1;
+        skip[i + 4] = 1;
+        skip[i + 5] = 1;
+        skip[i + 6] = 1;
+        skip[i + 7] = 1;
+    
+    }
+    
+    for (i = 0; i + 6 < count; i++) {
+    
+        long lhs;
+        long rhs;
+        long folded;
+        
+        char cond_label[128];
+        char opbuf[16];
+        char tmp[192];
+        
+        const char *l0;
+        const char *l1;
+        const char *l2;
+        const char *l3;
+        const char *l4;
+        const char *l5;
+        const char *l6;
+        
+        int taken;
+        
+        if (skip[i]) {
+            continue;
+        }
+        
+        l0 = repl[i] ? repl[i] : lines[i];
+        l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+        l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+        l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+        l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+        l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+        l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6];
+        
+        if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l2, "mov ecx, edx") || !inline_line_is_exact (l3, "cdq") ||
+            !inline_line_is_exact (l4, "idiv ecx")) {
+            continue;
+        }
+        
+        if (!inline_fold_div_eax_edx (lhs, rhs, &folded)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l5, "test eax, eax")) {
+            continue;
+        }
+        
+        if (!inline_parse_cond_jump_label (l6, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+            continue;
+        }
+        
+        taken = inline_eval_test_jump (opbuf, folded);
+        
+        if (taken) {
+        
+            sprintf (tmp, "    jmp %s\n", cond_label);
+            free (repl[i]);
+            
+            repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+            
+            skip[i + 1] = 1;
+            skip[i + 2] = 1;
+            skip[i + 3] = 1;
+            skip[i + 4] = 1;
+            skip[i + 5] = 1;
+            skip[i + 6] = 1;
+        
+        } else {
+        
+            skip[i] = 1;
+            skip[i + 1] = 1;
+            skip[i + 2] = 1;
+            skip[i + 3] = 1;
+            skip[i + 4] = 1;
+            skip[i + 5] = 1;
+            skip[i + 6] = 1;
+        
+        }
+    
+    }
+    
+    for (i = 0; i + 7 < count; i++) {
+    
+        long lhs;
+        long rhs;
+        long cmp_rhs;
+        long folded;
+        
+        char cond_label[128];
+        char opbuf[16];
+        char tmp[192];
+        
+        const char *l0;
+        const char *l1;
+        const char *l2;
+        const char *l3;
+        const char *l4;
+        const char *l5;
+        const char *l6;
+        const char *l7;
+        
+        int taken;
+        
+        if (skip[i]) {
+            continue;
+        }
+        
+        l0 = repl[i] ? repl[i] : lines[i];
+        l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+        l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+        l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+        l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+        l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+        l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6];
+        l7 = repl[i + 7] ? repl[i + 7] : lines[i + 7];
+        
+        if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6 || !l7) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l2, "mov ecx, edx") || !inline_line_is_exact (l3, "cdq") ||
+            !inline_line_is_exact (l4, "idiv ecx")) {
+            continue;
+        }
+        
+        if (!inline_fold_div_eax_edx (lhs, rhs, &folded)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l5, &cmp_rhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l6, "cmp eax, edx")) {
+            continue;
+        }
+        
+        if (!inline_parse_cond_jump_label (l7, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+            continue;
+        }
+        
+        taken = inline_eval_cmp_jump (opbuf, folded, cmp_rhs);
+        
+        if (taken) {
+        
+            sprintf (tmp, "    jmp %s\n", cond_label);
+            free (repl[i]);
+            
+            repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+            
+            skip[i + 1] = 1;
+            skip[i + 2] = 1;
+            skip[i + 3] = 1;
+            skip[i + 4] = 1;
+            skip[i + 5] = 1;
+            skip[i + 6] = 1;
+            skip[i + 7] = 1;
+        
+        } else {
+        
+            skip[i] = 1;
+            skip[i + 1] = 1;
+            skip[i + 2] = 1;
+            skip[i + 3] = 1;
+            skip[i + 4] = 1;
+            skip[i + 5] = 1;
+            skip[i + 6] = 1;
+            skip[i + 7] = 1;
+        
+        }
+    
+    }
+    
+    for (i = 0; i + 7 < count; i++) {
+    
+        long lhs;
+        long rhs;
+        long folded;
+        
+        char cond_label[128];
+        char opbuf[16];
+        char tmp[192];
+        
+        const char *l0;
+        const char *l1;
+        const char *l2;
+        const char *l3;
+        const char *l4;
+        const char *l5;
+        const char *l6;
+        const char *l7;
+        
+        int taken;
+        
+        if (skip[i]) {
+            continue;
+        }
+        
+        l0 = repl[i] ? repl[i] : lines[i];
+        l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+        l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+        l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+        l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+        l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+        l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6];
+        l7 = repl[i + 7] ? repl[i + 7] : lines[i + 7];
+        
+        if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6 || !l7) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l2, "mov ecx, edx") || !inline_line_is_exact (l3, "cdq") ||
+            !inline_line_is_exact (l4, "idiv ecx") || !inline_line_is_exact (l5, "mov eax, edx")) {
+            continue;
+        }
+        
+        if (!inline_fold_mod_eax_edx (lhs, rhs, &folded)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l6, "test eax, eax")) {
+            continue;
+        }
+        
+        if (!inline_parse_cond_jump_label (l7, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+            continue;
+        }
+        
+        taken = inline_eval_test_jump (opbuf, folded);
+        
+        if (taken) {
+        
+            sprintf (tmp, "    jmp %s\n", cond_label);
+            free (repl[i]);
+            
+            repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+            
+            skip[i + 1] = 1;
+            skip[i + 2] = 1;
+            skip[i + 3] = 1;
+            skip[i + 4] = 1;
+            skip[i + 5] = 1;
+            skip[i + 6] = 1;
+            skip[i + 7] = 1;
+        
+        } else {
+        
+            skip[i] = 1;
+            skip[i + 1] = 1;
+            skip[i + 2] = 1;
+            skip[i + 3] = 1;
+            skip[i + 4] = 1;
+            skip[i + 5] = 1;
+            skip[i + 6] = 1;
+            skip[i + 7] = 1;
+        
+        }
+    
+    }
+    
+    for (i = 0; i + 8 < count; i++) {
+    
+        long lhs;
+        long rhs;
+        long cmp_rhs;
+        long folded;
+        
+        char cond_label[128];
+        char opbuf[16];
+        char tmp[192];
+        
+        const char *l0;
+        const char *l1;
+        const char *l2;
+        const char *l3;
+        const char *l4;
+        const char *l5;
+        const char *l6;
+        const char *l7;
+        const char *l8;
+        
+        int taken;
+        
+        if (skip[i]) {
+            continue;
+        }
+        
+        l0 = repl[i] ? repl[i] : lines[i];
+        l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+        l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+        l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+        l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+        l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+        l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6];
+        l7 = repl[i + 7] ? repl[i + 7] : lines[i + 7];
+        l8 = repl[i + 8] ? repl[i + 8] : lines[i + 8];
+        
+        if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6 || !l7 || !l8) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l2, "mov ecx, edx") || !inline_line_is_exact (l3, "cdq") ||
+            !inline_line_is_exact (l4, "idiv ecx") || !inline_line_is_exact (l5, "mov eax, edx")) {
+            continue;
+        }
+        
+        if (!inline_fold_mod_eax_edx (lhs, rhs, &folded)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l6, &cmp_rhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l7, "cmp eax, edx")) {
+            continue;
+        }
+        
+        if (!inline_parse_cond_jump_label (l8, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+            continue;
+        }
+        
+        taken = inline_eval_cmp_jump (opbuf, folded, cmp_rhs);
+        
+        if (taken) {
+        
+            sprintf (tmp, "    jmp %s\n", cond_label);
+            free (repl[i]);
+            
+            repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+            
+            skip[i + 1] = 1;
+            skip[i + 2] = 1;
+            skip[i + 3] = 1;
+            skip[i + 4] = 1;
+            skip[i + 5] = 1;
+            skip[i + 6] = 1;
+            skip[i + 7] = 1;
+            skip[i + 8] = 1;
+        
+        } else {
+        
+            skip[i] = 1;
+            skip[i + 1] = 1;
+            skip[i + 2] = 1;
+            skip[i + 3] = 1;
+            skip[i + 4] = 1;
+            skip[i + 5] = 1;
+            skip[i + 6] = 1;
+            skip[i + 7] = 1;
+            skip[i + 8] = 1;
+        
+        }
+    
+    }
+    
+    for (i = 0; i + 2 < count; i++) {
+    
+        long lhs;
+        long rhs;
+        long folded;
+        
+        char tmp[128];
+        
+        const char *l0;
+        const char *l1;
+        const char *l2;
+        
+        if (skip[i] || skip[i + 1] || skip[i + 2]) {
+            continue;
+        }
+        
+        l0 = repl[i] ? repl[i] : lines[i];
+        l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+        l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+        
+        if (!l0 || !l1 || !l2) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+            continue;
+        }
+        
+        if (!inline_fold_binary_eax_edx (l2, lhs, rhs, &folded)) {
+            continue;
+        }
+        
+        sprintf (tmp, "    mov eax, %ld\n", folded);
+        free (repl[i]);
+        repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+        
+        skip[i + 1] = 1;
+        skip[i + 2] = 1;
+    
+    }
+    
+    for (i = 0; i + 3 < count; i++) {
+    
+        long lhs;
+        long rhs;
+        long folded;
+        
+        char tmp[128];
+        
+        const char *l0;
+        const char *l1;
+        const char *l2;
+        const char *l3;
+        
+        if (skip[i] || skip[i + 1] || skip[i + 2] || skip[i + 3]) {
+            continue;
+        }
+        
+        l0 = repl[i] ? repl[i] : lines[i];
+        l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+        l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+        l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+        
+        if (!l0 || !l1 || !l2 || !l3) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l2, "mov ecx, edx")) {
+            continue;
+        }
+        
+        if (!inline_fold_shift_eax_edx (l3, lhs, rhs, &folded)) {
+            continue;
+        }
+        
+        sprintf (tmp, "    mov eax, %ld\n", folded);
+        free (repl[i]);
+        
+        repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+        
+        skip[i + 1] = 1;
+        skip[i + 2] = 1;
+        skip[i + 3] = 1;
+    
+    }
+    
+    for (i = 0; i + 4 < count; i++) {
+    
+        long lhs;
+        long rhs;
+        long folded;
+        
+        char tmp[128];
+        
+        const char *l0;
+        const char *l1;
+        const char *l2;
+        const char *l3;
+        const char *l4;
+        
+        if (skip[i] || skip[i + 1] || skip[i + 2] || skip[i + 3] || skip[i + 4]) {
+            continue;
+        }
+        
+        l0 = repl[i] ? repl[i] : lines[i];
+        l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+        l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+        l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+        l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+        
+        if (!l0 || !l1 || !l2 || !l3 || !l4) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l2, "mov ecx, edx") || !inline_line_is_exact (l3, "cdq") ||
+            !inline_line_is_exact (l4, "idiv ecx")) {
+            continue;
+        }
+        
+        if (!inline_fold_div_eax_edx (lhs, rhs, &folded)) {
+            continue;
+        }
+        
+        sprintf (tmp, "    mov eax, %ld\n", folded);
+        free (repl[i]);
+        
+        repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+        
+        skip[i + 1] = 1;
+        skip[i + 2] = 1;
+        skip[i + 3] = 1;
+        skip[i + 4] = 1;
+    
+    }
+    
+    for (i = 0; i + 5 < count; i++) {
+    
+        long lhs;
+        long rhs;
+        long folded;
+        
+        char tmp[128];
+        
+        const char *l0;
+        const char *l1;
+        const char *l2;
+        const char *l3;
+        const char *l4;
+        const char *l5;
+        
+        if (skip[i] || skip[i + 1] || skip[i + 2] || skip[i + 3] || skip[i + 4] || skip[i + 5]) {
+            continue;
+        }
+        
+        l0 = repl[i] ? repl[i] : lines[i];
+        l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+        l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+        l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+        l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+        l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+        
+        if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l2, "mov ecx, edx") || !inline_line_is_exact (l3, "cdq") ||
+            !inline_line_is_exact (l4, "idiv ecx") || !inline_line_is_exact (l5, "mov eax, edx")) {
+            continue;
+        }
+        
+        if (!inline_fold_mod_eax_edx (lhs, rhs, &folded)) {
+            continue;
+        }
+        
+        sprintf (tmp, "    mov eax, %ld\n", folded);
+        free (repl[i]);
+        
+        repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+        
+        skip[i + 1] = 1;
+        skip[i + 2] = 1;
+        skip[i + 3] = 1;
+        skip[i + 4] = 1;
+        skip[i + 5] = 1;
+    
+    }
+    
+    /*
+     * Some compound-expression folds, such as x += 1, are reduced only by
+     * the late arithmetic pass above.  Run the constant compare/branch fold
+     * again here so newly-created sequences like:
+     *
+     *     mov eax, 9
+     *     mov edx, 9
+     *     cmp eax, edx
+     *     jne Lx
+     *
+     * are folded after the arithmetic result becomes visible.
+     */
+    for (i = 0; i + 3 < count; i++) {
+    
+        long lhs;
+        long rhs;
+        
+        char cond_label[128];
+        char opbuf[16];
+        char tmp[192];
+        
+        const char *l0;
+        const char *l1;
+        const char *l2;
+        const char *l3;
+        
+        int taken;
+        
+        if (skip[i] || skip[i + 1] || skip[i + 2] || skip[i + 3]) {
+            continue;
+        }
+        
+        l0 = repl[i] ? repl[i] : lines[i];
+        l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+        l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+        l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+        
+        if (!l0 || !l1 || !l2 || !l3) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l2, "cmp eax, edx")) {
+            continue;
+        }
+        
+        if (!inline_parse_cond_jump_label (l3, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+            continue;
+        }
+        
+        taken = inline_eval_cmp_jump (opbuf, lhs, rhs);
+        
+        if (taken) {
+        
+            sprintf (tmp, "    jmp %s\n", cond_label);
+            free (repl[i]);
+            
+            repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+            
+            skip[i + 1] = 1;
+            skip[i + 2] = 1;
+            skip[i + 3] = 1;
+        
+        } else {
+        
+            skip[i] = 1;
+            skip[i + 1] = 1;
+            skip[i + 2] = 1;
+            skip[i + 3] = 1;
+        
+        }
+    
+    }
+    
+    /*
+     * A late fold can leave useful instructions separated by skipped source
+     * lines.  For example x += 1 followed by == may become:
+     *
+     *     mov eax, 9
+     *     ; skipped old rhs/op lines
+     *     mov edx, 9
+     *     cmp eax, edx
+     *     jne Lx
+     *
+     * The normal adjacent compare fold cannot see through the skipped lines,
+     * so do one compacting pass over the remaining live instructions.
+     */
+    for (i = 0; i < count; i++) {
+    
+        int j0;
+        int j1;
+        int j2;
+        int j3;
+        
+        long lhs;
+        long rhs;
+        
+        char cond_label[128];
+        char opbuf[16];
+        char tmp[192];
+        
+        const char *l0;
+        const char *l1;
+        const char *l2;
+        const char *l3;
+        
+        int taken;
+        
+        if (skip[i]) {
+            continue;
+        }
+        
+        j0 = i;
+        j1 = j0 + 1;
+        
+        while (j1 < count && skip[j1]) {
+            j1++;
+        }
+        
+        j2 = j1 + 1;
+        
+        while (j2 < count && skip[j2]) {
+            j2++;
+        }
+        
+        j3 = j2 + 1;
+        
+        while (j3 < count && skip[j3]) {
+            j3++;
+        }
+        
+        if (j1 >= count || j2 >= count || j3 >= count) {
+            continue;
+        }
+        
+        l0 = repl[j0] ? repl[j0] : lines[j0];
+        l1 = repl[j1] ? repl[j1] : lines[j1];
+        l2 = repl[j2] ? repl[j2] : lines[j2];
+        l3 = repl[j3] ? repl[j3] : lines[j3];
+        
+        if (!l0 || !l1 || !l2 || !l3) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+            continue;
+        }
+        
+        if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+            continue;
+        }
+        
+        if (!inline_line_is_exact (l2, "cmp eax, edx")) {
+            continue;
+        }
+        
+        if (!inline_parse_cond_jump_label (l3, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+            continue;
+        }
+        
+        taken = inline_eval_cmp_jump (opbuf, lhs, rhs);
+        
+        if (taken) {
+        
+            sprintf (tmp, "    jmp %s\n", cond_label);
+            free (repl[j0]);
+            
+            repl[j0] = inline_dup_text_slice (tmp, strlen (tmp));
+            
+            skip[j1] = 1;
+            skip[j2] = 1;
+            skip[j3] = 1;
+        
+        } else {
+        
+            skip[j0] = 1;
+            skip[j1] = 1;
+            skip[j2] = 1;
+            skip[j3] = 1;
+        
+        }
+    
+    }
+    
+    for (i = 0; i < count; i++) {
+    
+        char label[128];
+        char target[128];
+        const char *line;
+        int j;
+        
+        if (skip[i]) {
+            continue;
+        }
+        
+        line = repl[i] ? repl[i] : lines[i];
+        
+        if (!line || !inline_parse_label_definition (line, label, sizeof (label))) {
+            continue;
+        }
+        
+        for (j = i + 1; j < count; j++) {
+        
+            const char *next_line;
+            
+            if (skip[j]) {
+                continue;
+            }
+            
+            next_line = repl[j] ? repl[j] : lines[j];
+            
+            if (!next_line) {
+                continue;
+            }
+            
+            if (inline_parse_jump_label (next_line, "jmp", target, sizeof (target))) {
+            
+                if (strcmp (label, target) != 0) {
+                
+                    chain_from[i] = inline_dup_text_slice (label, strlen (label));
+                    chain_to[i] = inline_dup_text_slice (target, strlen (target));
+                
+                }
+            
+            }
+            
+            break;
+        
+        }
+    
+    }
+    
+    for (i = 0; i < count; i++) {
+    
+        char target[128];
+        char resolved[128];
+        char tmp[192];
+        const char *line;
+        
+        if (skip[i]) {
+            continue;
+        }
+        
+        line = repl[i] ? repl[i] : lines[i];
+        
+        if (!line || !inline_parse_jump_label (line, "jmp", target, sizeof (target))) {
+            continue;
+        }
+        
+        if (!inline_resolve_jump_chain (chain_from, chain_to, count, target, resolved, sizeof (resolved))) {
+            continue;
+        }
+        
+        sprintf (tmp, "    jmp %s\n", resolved);
+        free (repl[i]);
+        
+        repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+    
+    }
+    
+    for (i = 0; i < count; i++) {
+    
+        char target[128];
+        const char *line;
+        int j;
+        
+        if (skip[i]) {
+            continue;
+        }
+        
+        line = repl[i] ? repl[i] : lines[i];
+        
+        if (!line || !inline_parse_any_jump_label (line, target, sizeof (target))) {
+            continue;
+        }
+        
+        for (j = 0; j < count; j++) {
+        
+            char label[128];
+            const char *def;
+            
+            if (skip[j]) {
+                continue;
+            }
+            
+            def = repl[j] ? repl[j] : lines[j];
+            
+            if (def && inline_parse_label_definition (def, label, sizeof (label)) && strcmp (label, target) == 0) {
+            
+                label_refs[j]++;
+                break;
+            
+            }
+        
+        }
+    
+    }
+    
+    for (i = 0; i < count; i++) {
+    
+        char target[128];
+        const char *line;
+        int j;
+        int target_line = -1;
+        int can_delete = 1;
+        
+        if (skip[i]) {
+            continue;
+        }
+        
+        line = repl[i] ? repl[i] : lines[i];
+        
+        if (!line || !inline_parse_jump_label (line, "jmp", target, sizeof (target))) {
+            continue;
+        }
+        
+        for (j = i + 1; j < count; j++) {
+        
+            char label[128];
+            const char *check;
+            
+            if (skip[j]) {
+                continue;
+            }
+            
+            check = repl[j] ? repl[j] : lines[j];
+            
+            if (check && inline_parse_label_definition (check, label, sizeof (label)) && strcmp (label, target) == 0) {
+            
+                target_line = j;
+                break;
+            
+            }
+        
+        }
+        
+        if (target_line < 0) {
+            continue;
+        }
+        
+        for (j = i + 1; j < target_line; j++) {
+        
+            char label[128];
+            const char *check;
+            
+            if (skip[j]) {
+                continue;
+            }
+            
+            check = repl[j] ? repl[j] : lines[j];
+            
+            if (check && inline_parse_label_definition (check, label, sizeof (label)) && label_refs[j] > 0) {
+            
+                can_delete = 0;
+                break;
+            
+            }
+        
+        }
+        
+        if (!can_delete) {
+            continue;
+        }
+        
+        for (j = i + 1; j < target_line; j++) {
+            skip[j] = 1;
+        }
+    
+    }
+    
+    for (i = 0; i < count; i++) {
+    
+        char target[128];
+        const char *line;
+        int j;
+        int target_line = -1;
+        
+        if (skip[i]) {
+            continue;
+        }
+        
+        line = repl[i] ? repl[i] : lines[i];
+        
+        if (!line || !inline_parse_jump_label (line, "jmp", target, sizeof (target))) {
+            continue;
+        }
+        
+        for (j = i + 1; j < count; j++) {
+        
+            char label[128];
+            const char *check;
+            
+            if (skip[j]) {
+                continue;
+            }
+            
+            check = repl[j] ? repl[j] : lines[j];
+            
+            if (!check) {
+                continue;
+            }
+            
+            if (inline_parse_label_definition (check, label, sizeof (label))) {
+            
+                if (strcmp (label, target) == 0) {
+                    target_line = j;
+                }
+                
+                break;
+            
+            }
+            
+            break;
+        
+        }
+        
+        if (target_line >= 0) {
+            skip[i] = 1;
+        }
+    
+    }
+    
+    /*
+     * Branch folding can create new unreachable ranges after the earlier
+     * jump cleanup has already run.  Recompute label references from the
+     * current non-skipped text and remove dead fall-through ranges between
+     * an unconditional jump and its later target.  This deliberately keeps
+     * referenced labels, so externally reachable or still-jumped-to blocks
+     * are not discarded.
+     */
+    {
+    
+        int changed;
+        int pass;
+        
+        for (pass = 0; pass < count; pass++) {
+        
+            changed = 0;
+            
+            for (i = 0; i < count; i++) {
+                label_refs[i] = 0;
+            }
+            
+            for (i = 0; i < count; i++) {
+            
+                char target[128];
+                const char *line;
+                int j;
+                
+                if (skip[i]) {
+                    continue;
+                }
+                
+                line = repl[i] ? repl[i] : lines[i];
+                
+                if (!line || !inline_parse_any_jump_label (line, target, sizeof (target))) {
+                    continue;
+                }
+                
+                for (j = 0; j < count; j++) {
+                
+                    char label[128];
+                    const char *def;
+                    
+                    if (skip[j]) {
+                        continue;
+                    }
+                    
+                    def = repl[j] ? repl[j] : lines[j];
+                    
+                    if (def && inline_parse_label_definition (def, label, sizeof (label)) && strcmp (label, target) == 0) {
+                    
+                        label_refs[j]++;
+                        break;
+                    
+                    }
+                
+                }
+            
+            }
+            
+            for (i = 0; i < count; i++) {
+            
+                char target[128];
+                const char *line;
+                int j;
+                int target_line = -1;
+                int can_delete = 1;
+                
+                if (skip[i]) {
+                    continue;
+                }
+                
+                line = repl[i] ? repl[i] : lines[i];
+                
+                if (!line || !inline_parse_jump_label (line, "jmp", target, sizeof (target))) {
+                    continue;
+                }
+                
+                for (j = i + 1; j < count; j++) {
+                
+                    char label[128];
+                    const char *check;
+                    
+                    if (skip[j]) {
+                        continue;
+                    }
+                    
+                    check = repl[j] ? repl[j] : lines[j];
+                    
+                    if (check && inline_parse_label_definition (check, label, sizeof (label)) && strcmp (label, target) == 0) {
+                    
+                        target_line = j;
+                        break;
+                    
+                    }
+                
+                }
+                
+                if (target_line < 0) {
+                    continue;
+                }
+                
+                for (j = i + 1; j < target_line; j++) {
+                
+                    char label[128];
+                    const char *check;
+                    
+                    if (skip[j]) {
+                        continue;
+                    }
+                    
+                    check = repl[j] ? repl[j] : lines[j];
+                    
+                    if (check && inline_parse_label_definition (check, label, sizeof (label)) && label_refs[j] > 0) {
+                    
+                        can_delete = 0;
+                        break;
+                    
+                    }
+                
+                }
+                
+                if (!can_delete) {
+                    continue;
+                }
+                
+                for (j = i + 1; j < target_line; j++) {
+                
+                    if (!skip[j]) {
+                    
+                        skip[j] = 1;
+                        changed = 1;
+                    
+                    }
+                
+                }
+            
+            }
+            
+            if (!changed) {
+                break;
+            }
+        
+        }
+    
+    }
+    
+    /*
+     * The second unreachable-code pass can expose a new fall-through jump,
+     * for example "jmp L9" immediately followed by "L9:".  Run this small
+     * cleanup again at the end so later branch/unreachable optimisations do
+     * not leave the already-handled form behind.
+     */
+    for (i = 0; i < count; i++) {
+    
+        char target[128];
+        const char *line;
+        int j;
+        int target_line = -1;
+        
+        if (skip[i]) {
+            continue;
+        }
+        
+        line = repl[i] ? repl[i] : lines[i];
+        
+        if (!line || !inline_parse_jump_label (line, "jmp", target, sizeof (target))) {
+            continue;
+        }
+        
+        for (j = i + 1; j < count; j++) {
+        
+            char label[128];
+            const char *check;
+            
+            if (skip[j]) {
+                continue;
+            }
+            
+            check = repl[j] ? repl[j] : lines[j];
+            
+            if (!check) {
+                continue;
+            }
+            
+            if (inline_parse_label_definition (check, label, sizeof (label))) {
+            
+                if (strcmp (label, target) == 0) {
+                    target_line = j;
+                }
+                
+                break;
+            
+            }
+            
+            break;
+        
+        }
+        
+        if (target_line >= 0) {
+            skip[i] = 1;
+        }
+    
+    }
+    
+    for (i = 0; i < count; i++) {
+    
+        if (!skip[i]) {
+            fputs (repl[i] ? repl[i] : (lines[i] ? lines[i] : ""), ofp);
+        }
+        
+        free (lines[i]);
+        free (repl[i]);
+    
+    }
+    
+    for (i = 0; i < count; i++) {
+    
+        free (chain_from[i]);
+        free (chain_to[i]);
+    
+    }
+    
+    free (lines);
+    free (repl);
+    free (skip);
+    free (store_slot);
+    free (chain_from);
+    free (chain_to);
+    free (label_refs);
+
+}
+
+static void finish_inline_buffer (FILE **tmp, FILE **saved, int optimize) {
+
+    FILE *out;
+    long size;
+    char *buf;
+    
+    if (!tmp || !saved || !*tmp || !*saved) {
+        return;
+    }
+    
+    out = *saved;
+    
+    fflush (*tmp);
+    fseek (*tmp, 0, SEEK_END);
+    
+    size = ftell (*tmp);
+    fseek (*tmp, 0, SEEK_SET);
+    
+    if (size > 0) {
+    
+        buf = (char *) malloc ((size_t) size + 1);
+        
+        if (buf) {
+        
+            if (fread (buf, 1, (size_t) size, *tmp) == (size_t) size) {
+            
+                buf[size] = '\0';
+                
+                if (optimize) {
+                    emit_inline_optimized_text (buf, out);
+                } else {
+                    fwrite (buf, 1, (size_t) size, out);
+                }
+            
+            }
+            
+            free (buf);
+        
+        }
+    
+    }
+    
+    fclose (*tmp);
+    
+    *tmp = 0;
+    *saved = 0;
+    
+    state->ofp = out;
+
+}
+
+static void emit_inline_body_substituted (const char *body, int return_label, int argc) {
+
+    const char *p = body;
+    
+    int call_id = anon_label++;
+    int stack_bytes = 0;
+    
+    if (!body || !state->ofp) {
+        return;
+    }
+    
+    while (*p) {
+    
+        const char *line = p;
+        const char *eol = strchr (p, '\n');
+        const char *next = eol ? eol + 1 : p + strlen (p);
+        size_t len = (size_t) (next - line);
+        
+        emit_inline_line_substituted (line, len, return_label, call_id, argc, stack_bytes);
+        stack_bytes += inline_line_stack_delta (line, len);
+        p = next;
+    
+    }
+    
+    if (body[0] && body[strlen (body) - 1] != '\n') {
+        fputc ('\n', state->ofp);
+    }
+    
+    if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+        fprintf (state->ofp, "L%d:\n", call_id);
+    } else {
+        fprintf (state->ofp, ".L%d:\n", call_id);
+    }
+
+}
+
+static int emit_inline_call_if_possible (const char *name, int argc, const char *reg) {
+
+    int i = find_inline_function (name);
+    
+    if (i < 0 || !inline_functions[i].usable) {
+        return 0;
+    }
+    
+    if (argc != inline_functions[i].param_count) {
+        return 0;
+    }
+    
+    if (inline_functions[i].is_floating) {
+        return 0;
+    }
+    
+    if (inline_functions[i].expanding) {
+        return 0;
+    }
+    
+    if (!state->ofp) {
+        return 1;
+    }
+    
+    if (!inline_functions[i].body && !inline_functions[i].returns_void) {
+        return 0;
+    }
+    
+    if (inline_functions[i].body && inline_body_stack_delta (inline_functions[i].body) != 0) {
+        return 0;
+    }
+    
+    if (inline_functions[i].data && !inline_functions[i].data_emitted) {
+    
+        fputs (inline_functions[i].data, state->ofp);
+        
+        if (inline_functions[i].data[0] && inline_functions[i].data[strlen (inline_functions[i].data) - 1] != '\n') {
+            fputc ('\n', state->ofp);
+        }
+        
+        if (state->syntax & ASM_SYNTAX_MASM) {
+            fprintf (state->ofp, ".code\n");
+        } else if (state->syntax & ASM_SYNTAX_NASM) {
+            fprintf (state->ofp, "section .text\n");
+        } else {
+            fprintf (state->ofp, ".text\n");
+        }
+        
+        inline_functions[i].data_emitted = 1;
+    
+    }
+    
+    if (inline_functions[i].body) {
+    
+        inline_functions[i].expanding = 1;
+        emit_inline_body_substituted (inline_functions[i].body, inline_functions[i].return_label, argc);
+        inline_functions[i].expanding = 0;
+    
+    }
+    
+    if (!inline_functions[i].returns_void && strcmp (reg, "eax") != 0) {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+            fprintf (state->ofp, "    mov %s, eax\n", reg);
+        } else {
+            fprintf (state->ofp, "    movl %%eax, %%%s\n", reg);
+        }
+    
+    }
+    
+    return 1;
+
+}
+
+#define     MAX_LOCAL_SYMBOLS           1024
+
+struct local_symbol {
+
+    char *name;
+    
+    int size;
+    int align;
+    
+    int is_floating;
+    long offset;
+    
+    char *static_label;
+    
+    int is_static, is_unsigned;
+    int is_array;
+    int array_element_size;
+    
+    int pointer_depth;
+    int pointed_size;
+    
+    char *pointed_tag_name;
+    char *tag_name;
+
+};
+
+static struct local_symbol local_symbols[MAX_LOCAL_SYMBOLS];
+
+static int local_array_pointer_step_size (const struct local_symbol *sym);
+static int global_array_pointer_step_size (const char *name);
+
+static int local_symbol_count = 0;
+
+static long current_local_stack_size = 0;
+static long current_block_cleanup_bytes = 0;
+
+static long align_up_long (long value, int align) {
+
+    long mask;
+    
+    if (align <= 1) {
+        return value;
+    }
+    
+    mask = (long) align - 1;
+    return (value + mask) & ~mask;
+
+}
+
+static int type_alignment (int size) {
+
+    if (size <= 1) {
+        return 1;
+    }
+    
+    if (size == 2) {
+        return 2;
+    }
+    
+    return 4;
+
+}
+
+static void reset_local_symbols (void) {
+
+    int i;
+    
+    for (i = 0; i < local_symbol_count; i++) {
+    
+        if (local_symbols[i].name) {
+        
+            free (local_symbols[i].name);
+            local_symbols[i].name = 0;
+        
+        }
+        
+        if (local_symbols[i].static_label) {
+        
+            free (local_symbols[i].static_label);
+            local_symbols[i].static_label = 0;
+        
+        }
+        
+        if (local_symbols[i].pointed_tag_name) {
+        
+            free (local_symbols[i].pointed_tag_name);
+            local_symbols[i].pointed_tag_name = 0;
+        
+        }
+        
+        if (local_symbols[i].tag_name) {
+        
+            free (local_symbols[i].tag_name);
+            local_symbols[i].tag_name = 0;
+        
+        }
+    
+    }
+    
+    local_symbol_count = 0;
+    current_local_stack_size = 0;
+
+}
+
+static void truncate_local_symbols (int count, long stack_size) {
+
+    int i;
+    
+    if (count < 0) {
+        count = 0;
+    }
+    
+    for (i = count; i < local_symbol_count; i++) {
+    
+        if (local_symbols[i].name) {
+        
+            free (local_symbols[i].name);
+            local_symbols[i].name = 0;
+        
+        }
+        
+        if (local_symbols[i].static_label) {
+        
+            free (local_symbols[i].static_label);
+            local_symbols[i].static_label = 0;
+        
+        }
+        
+        if (local_symbols[i].pointed_tag_name) {
+        
+            free (local_symbols[i].pointed_tag_name);
+            local_symbols[i].pointed_tag_name = 0;
+        
+        }
+        
+        if (local_symbols[i].tag_name) {
+        
+            free (local_symbols[i].tag_name);
+            local_symbols[i].tag_name = 0;
+        
+        }
+    
+    }
+    
+    local_symbol_count = count;
+    current_local_stack_size = stack_size;
+
+}
+
+static int local_symbol_exists_in_current_scope (const char *name, int start_count) {
+
+    int i;
+    
+    if (!name) {
+        return 0;
+    }
+    
+    for (i = start_count; i < local_symbol_count; i++) {
+    
+        if (local_symbols[i].name && strcmp (local_symbols[i].name, name) == 0) {
+            return 1;
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static long add_local_symbol (const char *name, int size, int align, int is_unsigned, int scope_start_count, int line, const char *start, const char *caret) {
+
+    long new_size;
+    
+    if (!name) {
+        return 0;
+    }
+    
+    if (size < 1) {
+        size = 1;
+    }
+    
+    if (align < 1) {
+        align = 1;
+    }
+    
+    if (local_symbol_exists_in_current_scope (name, scope_start_count)) {
+    
+        report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "duplicate local symbol '%s'", name);
+        return 0;
+    
+    }
+    
+    if (local_symbol_count >= MAX_LOCAL_SYMBOLS) {
+    
+        report_line_at (get_filename (), line, REPORT_ERROR, start, caret,  "too many local symbols");
+        return 0;
+    
+    }
+    
+    new_size = align_up_long (current_local_stack_size + size, align);
+    
+    /*
+     * EBP-relative locals must be backed by whole stack slots.  Without this,
+     * a byte-sized local following two pointer locals can be assigned offset
+     * -9(%ebp), while the emitted stack adjustment has only reserved 8 bytes.
+     * That is exactly what happened in cpplib/lex.c's _cpp_skip_block_comment:
+     *
+     *     cpp_mffc *mffc;   -> -4
+     *     const char *pos;  -> -8
+     *     char c;           -> -9, outside the reserved frame
+     *
+     * Keep the object's real size for loads/stores, but round the frame growth
+     * so subsequent stack allocation reserves enough bytes before the local is
+     * used.
+     */
+    new_size = align_up_long (new_size, 4);
+    
+    local_symbols[local_symbol_count].name = xstrdup (name);
+    local_symbols[local_symbol_count].size = size;
+    local_symbols[local_symbol_count].align = align;
+    local_symbols[local_symbol_count].offset = -(int) new_size;
+    local_symbols[local_symbol_count].is_static = 0;
+    local_symbols[local_symbol_count].static_label = 0;
+    local_symbols[local_symbol_count].is_unsigned = is_unsigned ? 1 : 0;
+    local_symbols[local_symbol_count].is_floating = 0;
+    local_symbols[local_symbol_count].is_array = 0;
+    local_symbols[local_symbol_count].array_element_size = 0;
+    local_symbols[local_symbol_count].pointer_depth = 0;
+    local_symbols[local_symbol_count].pointed_size = 0;
+    local_symbols[local_symbol_count].pointed_tag_name = 0;
+    local_symbols[local_symbol_count].tag_name = 0;
+    
+    local_symbol_count++;
+    current_local_stack_size = new_size;
+
+    return -(long) new_size;
+
+}
+
+static void add_static_local_symbol (const char *name, const char *label, int size, int align, int is_unsigned, int scope_start_count, int line, const char *start, const char *caret) {
+
+    if (!name || !label) {
+        return;
+    }
+    
+    if (size < 1) {
+        size = 1;
+    }
+    
+    if (align < 1) {
+        align = 1;
+    }
+    
+    if (local_symbol_exists_in_current_scope (name, scope_start_count)) {
+    
+        report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "duplicate local symbol '%s'", name);
+        return;
+    
+    }
+    
+    if (local_symbol_count >= MAX_LOCAL_SYMBOLS) {
+    
+        report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "too many local symbols");
+        return;
+    
+    }
+    
+    local_symbols[local_symbol_count].name = xstrdup (name);
+    local_symbols[local_symbol_count].static_label = xstrdup (label);
+    local_symbols[local_symbol_count].size = size;
+    local_symbols[local_symbol_count].align = align;
+    local_symbols[local_symbol_count].offset = 0;
+    local_symbols[local_symbol_count].is_static = 1;
+    local_symbols[local_symbol_count].is_unsigned = is_unsigned ? 1 : 0;
+    local_symbols[local_symbol_count].is_floating = 0;
+    local_symbols[local_symbol_count].is_array = 0;
+    local_symbols[local_symbol_count].array_element_size = 0;
+       
+    local_symbol_count++;
+
+}
+
+static struct local_symbol *find_local_symbol (const char *name);
+
+static void set_local_symbol_array (const char *name, int is_array) {
+
+    struct local_symbol *sym = find_local_symbol (name);
+    
+    if (sym) {
+        sym->is_array = is_array ? 1 : 0;
+    }
+
+}
+
+static void set_local_symbol_array_element_size (const char *name, int elem_size) {
+
+    struct local_symbol *sym = find_local_symbol (name);
+    
+    if (sym) {
+        sym->array_element_size = elem_size > 0 ? elem_size : 0;
+    }
+
+}
+
+static void set_local_symbol_floating (const char *name, int is_floating) {
+
+    int i;
+    
+    if (!name) {
+        return;
+    }
+    
+    for (i = local_symbol_count - 1; i >= 0; i--) {
+    
+        if (local_symbols[i].name && strcmp (local_symbols[i].name, name) == 0) {
+        
+            local_symbols[i].is_floating = is_floating ? 1 : 0;
+            return;
+        
+        }
+    
+    }
+
+}
+
+#define     MAX_PENDING_PARAMS          128
+
+struct pending_param {
+
+    char *name;
+    
+    int align, size;
+    int is_unsigned;
+    int is_floating;
+    int pointer_depth;
+    int pointed_size;
+    
+    char *pointed_tag_name;
+
+};
+
+static struct pending_param pending_params[MAX_PENDING_PARAMS];
+static int pending_param_count = 0;
+static int declarator_depth = 0;
+
+static int local_symbol_exists_in_current_scope (const char *name, int start_count);
+
+static void clear_pending_params (void) {
+
+    int i;
+    
+    for (i = 0; i < pending_param_count; i++) {
+    
+        if (pending_params[i].name) {
+        
+            free (pending_params[i].name);
+            pending_params[i].name = 0;
+        
+        }
+        
+        pending_params[i].size = 0;
+        pending_params[i].align = 0;
+        pending_params[i].is_unsigned = 0;
+        pending_params[i].is_floating = 0;
+        pending_params[i].pointer_depth = 0;
+        pending_params[i].pointed_size = 0;
+        
+        if (pending_params[i].pointed_tag_name) {
+        
+            free (pending_params[i].pointed_tag_name);
+            pending_params[i].pointed_tag_name = 0;
+        
+        }
+    
+    }
+    
+    pending_param_count = 0;
+
+}
+
+static void add_pending_param (const char *name, int size, int align, int is_unsigned, int is_floating, int pointer_depth, int pointed_size) {
+
+    if (!name || !*name) {
+        return;
+    }
+    
+    if (pending_param_count >= MAX_PENDING_PARAMS) {
+    
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "too many function parameters");
+        return;
+    
+    }
+    
+    if (size < 1) {
+        size = DATA_INT & 0x1f;
+    }
+    
+    if (align < 1) {
+        align = type_alignment (size);
+    }
+    
+    pending_params[pending_param_count].name = xstrdup (name);
+    pending_params[pending_param_count].size = size;
+    pending_params[pending_param_count].align = align;
+    pending_params[pending_param_count].is_unsigned = is_unsigned ? 1 : 0;
+    pending_params[pending_param_count].is_floating = is_floating ? 1 : 0;
+    pending_params[pending_param_count].pointer_depth = pointer_depth;
+    pending_params[pending_param_count].pointed_size = pointed_size > 0 ? pointed_size : 0;
+    pending_params[pending_param_count].pointed_tag_name = (pointer_depth > 0 && parsed_type_tag_name[0]) ? xstrdup (parsed_type_tag_name) : 0;
+    
+    pending_param_count++;
+
+}
+
+static struct local_symbol *find_local_symbol (const char *name) {
+
+    int i;
+    
+    if (!name) {
+        return 0;
+    }
+    
+    for (i = local_symbol_count - 1; i >= 0; i--) {
+    
+        if (local_symbols[i].name && strcmp (local_symbols[i].name, name) == 0) {
+            return &local_symbols[i];
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static void set_local_symbol_pointer_info (const char *name, int pointer_depth, int pointed_size) {
+
+    struct local_symbol *sym = find_local_symbol (name);
+    
+    if (sym) {
+    
+        sym->pointer_depth = pointer_depth;
+        sym->pointed_size = pointed_size;
+        
+        if (sym->pointed_tag_name) {
+        
+            free (sym->pointed_tag_name);
+            sym->pointed_tag_name = 0;
+        
+        }
+        
+        if (pointer_depth > 0 && parsed_type_tag_name[0]) {
+            sym->pointed_tag_name = xstrdup (parsed_type_tag_name);
+        }
+    
+    }
+
+}
+
+static void install_pending_params_as_locals (void) {
+
+    long offset = current_function_returns_aggregate ? 12 : 8;
+    int i;
+    
+    for (i = 0; i < pending_param_count; i++) {
+    
+        int slot_size;
+        
+        if (!pending_params[i].name) {
+            continue;
+        }
+        
+        if (local_symbol_count >= MAX_LOCAL_SYMBOLS) {
+        
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "too many local symbols");
+            break;
+        
+        }
+        
+        if (local_symbol_exists_in_current_scope (pending_params[i].name, 0)) {
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate parameter '%s'", pending_params[i].name);
+        }
+        
+        local_symbols[local_symbol_count].name = xstrdup (pending_params[i].name);
+        local_symbols[local_symbol_count].size = pending_params[i].size;
+        local_symbols[local_symbol_count].align = pending_params[i].align;
+        local_symbols[local_symbol_count].offset = (int) offset;
+        local_symbols[local_symbol_count].is_static = 0;
+        local_symbols[local_symbol_count].static_label = 0;
+        local_symbols[local_symbol_count].is_unsigned = pending_params[i].is_unsigned;
+        local_symbols[local_symbol_count].is_floating = pending_params[i].is_floating;
+        local_symbols[local_symbol_count].pointer_depth = pending_params[i].pointer_depth;
+        local_symbols[local_symbol_count].pointed_size = pending_params[i].pointed_size;
+        local_symbols[local_symbol_count].pointed_tag_name = pending_params[i].pointed_tag_name ? xstrdup (pending_params[i].pointed_tag_name) : 0;
+        local_symbols[local_symbol_count].tag_name = 0;
+        
+        local_symbol_count++;
+        slot_size = pending_params[i].size;
+        
+        if (slot_size < DATA_PTR) {
+            slot_size = DATA_PTR;
+        }
+        
+        slot_size = (int) align_up_long (slot_size, DATA_PTR);
+        offset += slot_size;
+    
+    }
+
+}
+
+#define     MAX_LOCAL_INITS             1024
+
+#define     LOCAL_INIT_CONST            0
+#define     LOCAL_INIT_ADDRESS          1
+#define     LOCAL_INIT_STACK            2
+#define     LOCAL_INIT_GLOBAL           3
+
+struct local_init {
+
+    long offset;
+    
+    int size;
+    int kind;
+    
+    char *symbol;
+    int64_s value;
+    
+    long source_offset;
+    int source_size;
+
+};
+
+static void switch_section (int sec) {
+
+    if (!state->ofp || current_section == sec) {
+        return;
+    }
+    
+    if (sec == SECTION_TEXT) {
+    
+        if (state->syntax & ASM_SYNTAX_NASM) {
+            fprintf (state->ofp, "section .text\n");
+        } else {
+            fprintf (state->ofp, "%s\n", (state->syntax & ASM_SYNTAX_MASM ? ".code" : ".text"));
+        }
+    
+    } else if (sec == SECTION_DATA) {
+    
+        if (state->syntax & ASM_SYNTAX_NASM) {
+            fprintf (state->ofp, "section .data\n");
+        } else {
+            fprintf (state->ofp, ".data\n");
+        }
+    
+    } else if (sec == SECTION_BSS) {
+    
+        if (state->syntax & ASM_SYNTAX_NASM) {
+            fprintf (state->ofp, "section .bss\n");
+        } else {
+            fprintf (state->ofp, "%s\n", (state->syntax & ASM_SYNTAX_MASM ? ".data?" : ".bss"));
+        }
+    
+    }
+    
+    current_section = sec;
+
+}
+
+static int token_is_ms_int_type_name (void) {
+
+    if (tok.kind != TOK_IDENT || !tok.ident) {
+        return 0;
+    }
+    
+    return  strcmp (tok.ident, "__int8") == 0 || strcmp (tok.ident, "__int16") == 0 ||
+                strcmp (tok.ident, "__int32") == 0 || strcmp (tok.ident, "__int64") == 0;
+
+}
+
+static int is_type_start (enum token_kind k) {
+
+    if (k == TOK_IDENT && token_is_ms_int_type_name ()) {
+        return 1;
+    }
+    
+    switch (k) {
+    
+        case TOK_AUTO:          case TOK_REGISTER:      case TOK_STATIC:
+        case TOK_EXTERN:        case TOK_TYPEDEF:       case TOK_INLINE:
+        case TOK_CONST:         case TOK_VOLATILE:      case TOK_RESTRICT:
+        case TOK_SIGNED:        case TOK_UNSIGNED:
+        case TOK_SHORT:         case TOK_LONG:
+        case TOK_CHAR:          case TOK_INT:           case TOK_VOID:
+        case TOK_FLOAT:         case TOK_DOUBLE:
+        case TOK_STRUCT:        case TOK_UNION:         case TOK_ENUM:
+        
+            return 1;
+        
+        default:
+        
+            break;
+    
+    }
+    
+    if (k == TOK_IDENT && is_current_typedef_name ()) {
+        return 1;
+    }
+    
+    return 0;
+
+}
+
+/*static void next (void) {
+    get_token ();
+}*/
+
+static int expect (enum token_kind k, const char *what);
+
+static void parse_declarator (char **out_name);
+static void parse_type_spec (void);
+
+int token_starts_type_name (void) {
+    return is_type_start (tok.kind);
+}
+
+int parse_cast_type_name (int *out_size, int *out_is_unsigned, int *out_is_pointer) {
+
+    int saved_type_size = parsed_type_size;
+    int saved_storage_class = parsed_storage_class;
+    int saved_is_aggregate = parsed_type_is_aggregate;
+    int saved_is_void = parsed_type_is_void;
+    int saved_is_unsigned = parsed_type_is_unsigned;
+    int saved_is_floating = parsed_type_is_floating;
+    int saved_has_tag = parsed_type_has_tag;
+    int saved_is_inline = parsed_type_is_inline;
+    int saved_field_count = parsed_field_count;
+    int saved_fields[MAX_AGG_FIELDS];
+    
+    int saved_declarator_is_pointer = declarator_is_pointer;
+    int saved_declarator_has_array = declarator_has_array;
+    int saved_declarator_has_function = declarator_has_function;
+    int saved_declarator_array_unsized = declarator_array_unsized;
+    long saved_declarator_array_count = declarator_array_count;
+    
+    char *name = 0;
+    int size = DATA_INT & 0x1f;
+    int is_unsigned = 0;
+    int is_pointer = 0;
+    int ok = 0;
+    int i;
+    
+    for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) {
+        saved_fields[i] = parsed_field_sizes[i];
+    }
+    
+    if (is_type_start (tok.kind)) {
+    
+        declarator_is_pointer = 0;
+        declarator_pointer_depth = 0;
+        declarator_has_array = 0;
+        declarator_has_function = 0;
+        declarator_array_unsized = 0;
+        declarator_array_count = 0;
+        
+        parse_type_spec ();
+        
+        size = parsed_type_size & 0x1f;
+        is_unsigned = parsed_type_is_unsigned;
+        
+        if (tok.kind != TOK_RPAREN) {
+            parse_declarator (&name);
+        }
+        
+        if (declarator_is_pointer) {
+        
+            size = DATA_PTR;
+            
+            is_unsigned = 1;
+            is_pointer = 1;
+        
+        }
+        
+        if (tok.kind == TOK_RPAREN) {
+        
+            get_token ();
+            ok = 1;
+        
+        } else {
+            expect (TOK_RPAREN, ")");
+        }
+        
+        if (name) {
+            free (name);
+        }
+    
+    }
+    
+    last_cast_type_tag_name[0] = '\0';
+    last_cast_type_object_size = parsed_type_size & 0x1f;
+    
+    if (parsed_type_tag_name[0]) {
+    
+        strncpy (last_cast_type_tag_name, parsed_type_tag_name, sizeof (last_cast_type_tag_name) - 1);
+        last_cast_type_tag_name[sizeof (last_cast_type_tag_name) - 1] = '\0';
+    
+    }
+    
+    if (parsed_type_is_aggregate && parsed_type_size > 0) {
+        last_cast_type_object_size = parsed_type_size;
+    }
+    
+    parsed_type_size = saved_type_size;
+    parsed_storage_class = saved_storage_class;
+    parsed_type_is_aggregate = saved_is_aggregate;
+    parsed_type_is_void = saved_is_void;
+    parsed_type_is_unsigned = saved_is_unsigned;
+    parsed_type_is_floating = saved_is_floating;
+    parsed_type_has_tag = saved_has_tag;
+    parsed_type_is_inline = saved_is_inline;
+    
+    clear_parsed_fields ();
+    
+    for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) {
+        parsed_field_sizes[i] = saved_fields[i];
+    }
+    
+    parsed_field_count = saved_field_count;
+    
+    declarator_is_pointer = saved_declarator_is_pointer;
+    declarator_has_array = saved_declarator_has_array;
+    declarator_has_function = saved_declarator_has_function;
+    declarator_array_unsized = saved_declarator_array_unsized;
+    declarator_array_count = saved_declarator_array_count;
+    
+    if (out_size) {
+        *out_size = size;
+    }
+    
+    if (out_is_unsigned) {
+        *out_is_unsigned = is_unsigned;
+    }
+    
+    if (out_is_pointer) {
+        *out_is_pointer = is_pointer;
+    }
+    
+    return ok;
+
+}
+
+static int parse_deref_cast_type_name (int *out_size) {
+
+    int saved_type_size = parsed_type_size;
+    int saved_storage_class = parsed_storage_class;
+    int saved_is_aggregate = parsed_type_is_aggregate;
+    int saved_is_void = parsed_type_is_void;
+    int saved_is_unsigned = parsed_type_is_unsigned;
+    int saved_is_floating = parsed_type_is_floating;
+    int saved_has_tag = parsed_type_has_tag;
+    int saved_is_inline = parsed_type_is_inline;
+    int saved_field_count = parsed_field_count;
+    int saved_fields[MAX_AGG_FIELDS];
+    int saved_declarator_is_pointer = declarator_is_pointer;
+    int saved_declarator_has_array = declarator_has_array;
+    int saved_declarator_has_function = declarator_has_function;
+    int saved_declarator_array_unsized = declarator_array_unsized;
+    
+    long saved_declarator_array_count = declarator_array_count;
+    char *name = 0;
+    
+    int size = DATA_INT & 0x1f;
+    int ok = 0;
+    int i;
+    
+    for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) {
+        saved_fields[i] = parsed_field_sizes[i];
+    }
+    
+    if (is_type_start (tok.kind)) {
+    
+        declarator_is_pointer = 0;
+        declarator_pointer_depth = 0;
+        declarator_has_array = 0;
+        declarator_has_function = 0;
+        declarator_array_unsized = 0;
+        declarator_array_count = 0;
+        
+        parse_type_spec ();
+        size = parsed_type_size & 0x1f;
+        
+        if (tok.kind != TOK_RPAREN) {
+        
+            parse_declarator (&name);
+            
+            if (declarator_is_pointer) {
+                size = DATA_PTR;
+            }
+        
+        }
+        
+        if (tok.kind == TOK_RPAREN) {
+        
+            get_token ();
+            ok = 1;
+        
+        } else {
+            expect (TOK_RPAREN, ")");
+        }
+        
+        if (name) {
+            free (name);
+        }
+    
+    }
+    
+    parsed_type_size = saved_type_size;
+    parsed_storage_class = saved_storage_class;
+    parsed_type_is_aggregate = saved_is_aggregate;
+    parsed_type_is_void = saved_is_void;
+    parsed_type_is_unsigned = saved_is_unsigned;
+    parsed_type_is_floating = saved_is_floating;
+    parsed_type_has_tag = saved_has_tag;
+    parsed_type_is_inline = saved_is_inline;
+    
+    clear_parsed_fields ();
+    
+    for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) {
+        parsed_field_sizes[i] = saved_fields[i];
+    }
+    
+    parsed_field_count = saved_field_count;
+    
+    declarator_is_pointer = saved_declarator_is_pointer;
+    declarator_has_array = saved_declarator_has_array;
+    declarator_has_function = saved_declarator_has_function;
+    declarator_array_unsized = saved_declarator_array_unsized;
+    declarator_array_count = saved_declarator_array_count;
+    
+    if (out_size) {
+        *out_size = size;
+    }
+    
+    return ok;
+
+}
+
+static int _accept (enum token_kind k) {
+
+    if (tok.kind == k) {
+    
+        get_token ();
+        return 1;
+    
+    }
+    
+    return 0;
+
+}
+
+static int expect (enum token_kind k, const char *what) {
+
+    if (tok.kind == k) {
+    
+        get_token ();
+        return 1;
+    
+    }
+    
+    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected %s", what);
+    return 0;
+
+}
+
+static int token_is_ident (void) {
+    return tok.kind == TOK_IDENT;
+}
+
+static void skip_balanced_until (enum token_kind stop1, enum token_kind stop2, enum token_kind stop3);
+static void emit_load_assignment_rhs_expression_to_reg (const char *reg);
+
+static void consume_comma_expression_tail_before_semi (void) {
+
+    while (tok.kind == TOK_COMMA) {
+    
+        get_token ();
+        emit_load_assignment_rhs_expression_to_reg ("eax");
+    
+    }
+
+}
+
+static void expect_semi_or_recover (void) {
+
+    consume_comma_expression_tail_before_semi ();
+    
+    if (expect (TOK_SEMI, ";")) {
+        return;
+    }
+    
+    skip_balanced_until (TOK_SEMI, TOK_RBRACE, TOK_EOF);
+    
+    if (tok.kind == TOK_SEMI) {
+        get_token ();
+    }
+
+}
+
+static int current_ident_is_known_value (void) {
+
+    int64_s ignored;
+    
+    if (tok.kind != TOK_IDENT) {
+        return 0;
+    }
+    
+    if (find_local_symbol (tok.ident)) {
+        return 1;
+    }
+    
+    if (find_global_symbol (tok.ident) >= 0) {
+        return 1;
+    }
+    
+    if (resolve_enum_constant (tok.ident, &ignored)) {
+        return 1;
+    }
+    
+    return 0;
+
+}
+
+static int recover_unknown_rhs_identifier (void) {
+
+    const char *name;
+    const char *start;
+    const char *caret;
+    
+    unsigned long line;
+    
+    if (tok.kind != TOK_IDENT || current_ident_is_known_value ()) {
+        return 0;
+    }
+    
+    name = tok.ident ? tok.ident : "";
+    start = tok.start;
+    caret = tok.caret;
+    line = get_line_number ();
+    
+    report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "unknown symbol '%s'", name);
+    get_token ();
+    
+    if (tok.kind == TOK_LPAREN) {
+    
+        get_token ();
+        skip_balanced_until (TOK_RPAREN, TOK_SEMI, TOK_EOF);
+        
+        if (tok.kind == TOK_RPAREN) {
+            get_token ();
+        }
+    
+    }
+    
+    return 1;
+
+}
+
+static char *take_ident (void) {
+
+    char *name;
+    
+    if (!token_is_ident ()) {
+        return 0;
+    }
+    
+    name = xstrdup (tok.ident);
+    
+    last_declarator_name_line = get_line_number ();
+    last_declarator_name_start = tok.start;
+    last_declarator_name_caret = tok.caret;
+    
+    if (capture_declarator_name_location && !captured_declarator_name_location) {
+    
+        captured_declarator_name_location = 1;
+        captured_declarator_name_line = last_declarator_name_line;
+        captured_declarator_name_start = last_declarator_name_start;
+        captured_declarator_name_caret = last_declarator_name_caret;
+    
+    }
+    
+    get_token ();
+    return name;
+
+}
+
+static void skip_balanced_until (enum token_kind stop1, enum token_kind stop2, enum token_kind stop3) {
+
+    int paren = 0, brace = 0, brack = 0;
+    
+    while (tok.kind != TOK_EOF) {
+    
+        if (paren == 0 && brace == 0 && brack == 0) {
+        
+            if (tok.kind == stop1 || tok.kind == stop2 || tok.kind == stop3) {
+                return;
+            }
+        
+        }
+        
+        if (tok.kind == TOK_LPAREN) {
+            paren++;
+        } else if (tok.kind == TOK_RPAREN) {
+        
+            if (paren > 0) {
+                paren--;
+            } else {
+            
+                if (stop1 == TOK_RPAREN || stop2 == TOK_RPAREN || stop3 == TOK_RPAREN) {
+                    return;
+                }
+            
+            }
+        
+        } else if (tok.kind == TOK_LBRACE) {
+            brace++;
+        } else if (tok.kind == TOK_RBRACE) {
+        
+            if (brace > 0) {
+                brace--;
+            } else {
+            
+                if (stop1 == TOK_RPAREN || stop2 == TOK_RPAREN || stop3 == TOK_RPAREN) {
+                    return;
+                }
+            
+            }
+        
+        } else if (tok.kind == TOK_LBRACK) {
+            brack++;
+        } else if (tok.kind == TOK_RBRACK) {
+        
+            if (brack > 0) {
+                brack--;
+            } else {
+            
+                if (stop1 == TOK_RPAREN || stop2 == TOK_RPAREN || stop3 == TOK_RPAREN) {
+                    return;
+                }
+            
+            }
+        
+        }
+        
+        get_token ();
+    
+    }
+
+}
+
+/*static void consume_redundant_rparens_before_statement (void) {
+
+    while (tok.kind == TOK_RPAREN) {
+        get_token ();
+    }
+
+}*/
+
+static void skip_initializer (void) {
+
+    if (tok.kind == TOK_LBRACE) {
+        skip_balanced_until (TOK_COMMA, TOK_SEMI, TOK_EOF);
+    } else {
+        skip_balanced_until (TOK_COMMA, TOK_SEMI, TOK_EOF);
+    }
+
+}
+
+static void parse_enum_body (void) {
+
+    int64_s value;
+    zext64 (&value, 0);
+    
+    expect (TOK_LBRACE, "{");
+    
+    while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) {
+    
+        if (!token_is_ident ()) {
+        
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected enum constant");
+            
+            get_token ();
+            continue;
+        
+        } else {
+        
+            char *name;
+            
+            const char *name_start;
+            const char *name_caret;
+            
+            name = xstrdup (tok.ident);
+            
+            name_start = tok.start;
+            name_caret = tok.caret;
+            
+            get_token ();
+            
+            if (_accept (TOK_ASSIGN)) {
+            
+                enum token_kind kill[4];
+                
+                kill[0] = TOK_COMMA;
+                kill[1] = TOK_RBRACE;
+                kill[2] = TOK_EOF;
+                kill[3] = 0;
+                
+                value = expr_const64 (kill);
+            
+            }
+            
+            save_enum_constant (name, value, name_start, name_caret);
+            free (name);
+            
+            inc64 (&value);
+        
+        }
+        
+        if (!_accept (TOK_COMMA)) {
+            break;
+        }
+    
+    }
+    
+    expect (TOK_RBRACE, "}");
+
+}
+
+static void parse_declarator (char **out_name);
+static void parse_declarator_inner (char **out_name);
+
+static void parse_type_spec (void) {
+
+    int saw = 0;
+    
+    int saw_float = 0;
+    int saw_double = 0;
+    int saw_void = 0;
+    
+    int saw_signed = 0;
+    int saw_unsigned = 0;
+    int saw_real_type = 0;
+    
+    parsed_storage_class = STORAGE_NONE;
+    parsed_type_is_aggregate = 0;
+    parsed_type_is_unsigned = 0;
+    parsed_type_has_tag = 0;
+    parsed_type_tag_name[0] = '\0';
+    parsed_type_is_array_typedef = 0;
+    parsed_type_array_count = 1;
+    parsed_type_array_element_size = DATA_NONE;
+    parsed_type_is_inline = 0;
+    parsed_type_is_void = 0;
+    parsed_type_only_qualifiers = 0;
+    parsed_type_size = DATA_NONE;
+    
+    clear_parsed_fields ();
+    
+    while (tok.kind == TOK_AUTO || tok.kind == TOK_REGISTER || tok.kind == TOK_STATIC ||
+           tok.kind == TOK_EXTERN || tok.kind == TOK_TYPEDEF || tok.kind == TOK_CONST ||
+           tok.kind == TOK_VOLATILE || tok.kind == TOK_SIGNED || tok.kind == TOK_UNSIGNED ||
+           tok.kind == TOK_SHORT || tok.kind == TOK_LONG || tok.kind == TOK_CHAR ||
+           tok.kind == TOK_INT || tok.kind == TOK_VOID || tok.kind == TOK_FLOAT ||
+           tok.kind == TOK_DOUBLE || tok.kind == TOK_INLINE || tok.kind == TOK_RESTRICT ||
+           token_is_ms_int_type_name ()) {
+        
+        saw = 1;
+        
+        if (tok.kind == TOK_SIGNED || tok.kind == TOK_UNSIGNED ||
+            tok.kind == TOK_SHORT || tok.kind == TOK_LONG || tok.kind == TOK_CHAR ||
+            tok.kind == TOK_INT || tok.kind == TOK_VOID || tok.kind == TOK_FLOAT ||
+            tok.kind == TOK_DOUBLE || token_is_ms_int_type_name ()) {
+            saw_real_type = 1;
+        }
+        
+        if (tok.kind == TOK_EXTERN) {
+        
+            if (parsed_storage_class == STORAGE_EXTERN) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'extern'");
+            } else if (parsed_storage_class != STORAGE_NONE) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple storage classes in declaration specifiers");
+            } else {
+                parsed_storage_class = STORAGE_EXTERN;
+            }
+        
+        } else if (tok.kind == TOK_STATIC) {
+        
+            if (parsed_storage_class == STORAGE_STATIC) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'static'");
+            } else if (parsed_storage_class != STORAGE_NONE) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple storage classes in declaration specifiers");
+            } else {
+                parsed_storage_class = STORAGE_STATIC;
+            }
+        
+        } else if (tok.kind == TOK_TYPEDEF) {
+        
+            if (parsed_storage_class == STORAGE_TYPEDEF) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'typedef'");
+            } else if (parsed_storage_class != STORAGE_NONE) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple storage classes in declaration specifiers");
+            } else {
+                parsed_storage_class = STORAGE_TYPEDEF;
+            }
+        
+        } else if (tok.kind == TOK_INLINE) {
+            parsed_type_is_inline = 1;
+        } else if (tok.kind == TOK_CHAR) {
+        
+            if (parsed_type_size == DATA_CHAR) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'char'");
+            } else if (parsed_type_size != DATA_NONE) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");
+            } else {
+                parsed_type_size = DATA_CHAR;
+            }
+        
+        } else if (tok.kind == TOK_SHORT) {
+        
+            if (parsed_type_size == DATA_SHORT) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'short'");
+            } else if (parsed_type_size != DATA_NONE) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");
+            } else {
+                parsed_type_size = DATA_SHORT;
+            }
+        
+        } else if (tok.kind == TOK_INT) {
+        
+            if (parsed_type_size == DATA_INT) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'int'");
+            } else if (parsed_type_size != DATA_NONE) {
+                /*report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");*/
+            } else {
+                parsed_type_size = DATA_INT;
+            }
+        
+        } else if (tok.kind == TOK_LONG) {
+        
+            if (parsed_type_size == DATA_LONG) {
+                parsed_type_size = DATA_LLONG;
+            } else if (parsed_type_size == DATA_LLONG) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "too many 'long' keywords");
+            } else if (parsed_type_size != DATA_NONE) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");
+            } else {
+                parsed_type_size = DATA_LONG;
+            }
+        
+        } else if (tok.kind == TOK_SIGNED) {
+            saw_signed = 1;
+        } else if (tok.kind == TOK_UNSIGNED) {
+            saw_unsigned = 1;
+        } else if (tok.kind == TOK_FLOAT) {
+        
+            if (parsed_type_size == DATA_FLOAT) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'float'");
+            } else if (parsed_type_size != DATA_NONE) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");
+            } else {
+            
+                parsed_type_size = DATA_FLOAT;
+                saw_float = 1;
+            
+            }
+        
+        } else if (tok.kind == TOK_DOUBLE) {
+        
+            if (parsed_type_size == DATA_DOUBLE) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'double'");
+            } else if (parsed_type_size != DATA_NONE) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");
+            } else {
+            
+                parsed_type_size = DATA_DOUBLE;
+                saw_double = 1;
+            
+            }
+        
+        } else if (tok.kind == TOK_VOID) {
+        
+            if (parsed_type_size == DATA_VOID) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'void'");
+            } else if (parsed_type_size != DATA_NONE) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");
+            } else {
+            
+                parsed_type_size = DATA_VOID;
+                parsed_type_is_void = 1;
+                
+                saw_void = 1;
+            
+            }
+        
+        } else if (token_is_ms_int_type_name ()) {
+        
+            if (strcmp (tok.ident, "__int8") == 0) {
+            
+                if (parsed_type_size != DATA_NONE) {
+                    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");
+                } else {
+                    parsed_type_size = DATA_CHAR & 0x1f;
+                }
+            
+            } else if (strcmp (tok.ident, "__int16") == 0) {
+            
+                if (parsed_type_size != DATA_NONE) {
+                    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");
+                } else {
+                    parsed_type_size = DATA_SHORT & 0x1f;
+                }
+            
+            } else if (strcmp (tok.ident, "__int32") == 0) {
+            
+                if (parsed_type_size != DATA_NONE) {
+                    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");
+                } else {
+                    parsed_type_size = DATA_INT & 0x1f;
+                }
+            
+            } else {
+            
+                if (parsed_type_size != DATA_NONE) {
+                    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");
+                } else {
+                    parsed_type_size = DATA_LLONG & 0x1f;
+                }
+            
+            }
+        
+        }
+        
+        get_token ();
+    
+    }
+    
+    /*
+     * Be tolerant of project typedef chains where the base typedef name is
+     * not already known to this parser.  In
+     *
+     *     typedef some_base new_name;
+     *     typedef some_base *new_ptr;
+     *
+     * the declarator name is new_name/new_ptr, not some_base.  Without
+     * consuming the unknown base identifier here, parse_declarator() records
+     * some_base as the typedef name and the real declarator is then parsed as
+     * junk.
+     */
+    if (parsed_storage_class == STORAGE_TYPEDEF && !saw_real_type &&
+        tok.kind == TOK_IDENT && !is_current_typedef_name ()) {
+    
+        get_token ();
+        
+        saw = 1;
+        saw_real_type = 1;
+        
+        parsed_type_size = DATA_INT;
+    
+    }
+    
+    if (parsed_type_size == DATA_NONE) {
+    
+        parsed_type_only_qualifiers = (saw && !saw_real_type) ? 1 : 0;
+        parsed_type_size = DATA_INT;
+    
+    } else if (parsed_type_size == DATA_LONG && state->long64) {
+        parsed_type_size = DATA_LLONG;
+    }
+    
+    parsed_type_size &= 0x1f;
+    
+    if (is_current_typedef_name ()) {
+
+        struct typedef_entry *entry;
+        
+        entry = find_typedef_name (tok.ident);
+        get_token ();
+        
+        load_typedef_name (entry);
+        
+        if (entry && entry->is_aggregate && entry->tag_name && entry->tag_name[0]) {
+        
+            strncpy (parsed_type_tag_name, entry->tag_name, sizeof (parsed_type_tag_name) - 1);
+            parsed_type_tag_name[sizeof (parsed_type_tag_name) - 1] = '\0';
+            
+            parsed_type_has_tag = 1;
+        
+        }
+        
+        parsed_type_only_qualifiers = 0;
+        return;
+    
+    }
+    
+    if (tok.kind == TOK_STRUCT || tok.kind == TOK_UNION) {
+    
+        int is_union = (tok.kind == TOK_UNION);
+        int has_tag = 0;
+        
+        int aggregate_size = 0;
+        int aggregate_align = 1;
+        
+        int aggregate_fields[MAX_AGG_FIELDS];
+        int aggregate_field_count = 0;
+        
+        int union_init_fields[MAX_AGG_FIELDS];
+        int union_init_field_count = 0;
+        int union_init_size = 0;
+        
+        struct aggregate_tag_entry *tag_entry = 0;
+        
+        int aggregate_storage_class = parsed_storage_class;
+        int aggregate_is_inline = parsed_type_is_inline;
+        
+        char *tag_name = 0;
+        int member_info_start = member_info_count;
+        
+        parsed_type_size = DATA_PTR;
+        parsed_type_is_void = 0;
+        
+        clear_parsed_fields ();
+        get_token ();
+        
+        if (token_is_ident ()) {
+        
+            tag_name = xstrdup (tok.ident);
+            strncpy (parsed_type_tag_name, tok.ident, sizeof (parsed_type_tag_name) - 1);
+            
+            parsed_type_tag_name[sizeof (parsed_type_tag_name) - 1] = '\0';
+            has_tag = 1;
+            
+            get_token ();
+        
+        }
+        
+        if (tok.kind == TOK_LBRACE) {
+        
+            get_token ();
+            
+            while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) {
+            
+                int member_type_size;
+                int member_type_is_floating;
+                int member_is_aggregate;
+                int member_has_tag;
+                
+                int member_fields[MAX_AGG_FIELDS];
+                int member_field_count;
+                
+                int i;
+                
+                if (!is_type_start (tok.kind)) {
+                
+                    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected member declaration");
+                    
+                    skip_balanced_until (TOK_SEMI, TOK_RBRACE, TOK_EOF);
+                    _accept (TOK_SEMI);
+                    
+                    continue;
+                
+                }
+                
+                parse_type_spec ();
+                
+                member_type_size = parsed_type_size;
+                member_type_is_floating = parsed_type_is_floating;
+                member_is_aggregate = parsed_type_is_aggregate;
+                member_has_tag = parsed_type_has_tag;
+                member_field_count = parsed_field_count;
+                
+                for (i = 0; i < member_field_count; i++) {
+                    member_fields[i] = parsed_field_sizes[i];
+                }
+                
+                if (tok.kind == TOK_SEMI) {
+
+                    if (member_is_aggregate && !member_has_tag) {
+
+                        int member_init_size = fields_storage_size (member_fields, member_field_count);
+                        
+                        if (is_union) {
+                        
+                            if (member_type_size > aggregate_size) {
+                                aggregate_size = member_type_size;
+                            }
+                            
+                            if (union_init_field_count == 0) {
+                            
+                                union_init_field_count = member_field_count;
+                                
+                                for (i = 0; i < member_field_count; i++) {
+                                    union_init_fields[i] = member_fields[i];
+                                }
+                                
+                                union_init_size = member_init_size;
+                            
+                            }
+                        
+                        } else {
+                        
+                            aggregate_size += member_type_size;
+                            
+                            for (i = 0; i < member_field_count && aggregate_field_count < MAX_AGG_FIELDS; i++) {
+                                aggregate_fields[aggregate_field_count++] = member_fields[i];
+                            }
+                            
+                            if (member_type_size > member_init_size && aggregate_field_count < MAX_AGG_FIELDS) {
+                                aggregate_fields[aggregate_field_count++] = -(member_type_size - member_init_size);
+                            }
+                        
+                        }
+                    
+                    }
+
+                } else {
+                
+                    for (;;) {
+                    
+                        char *member = 0;
+                        
+                        int member_size, repeat;
+                        int init_size;
+                        
+                        int member_info_elem_size;
+                        int member_align;
+                        
+                        parse_declarator (&member);
+                        
+                        if (declarator_is_pointer) {
+                        
+                            member_size = DATA_PTR;
+                            member_field_count = 1;
+                            member_fields[0] = DATA_PTR;
+                        
+                        } else {
+                        
+                            member_size = member_type_size;
+                            
+                            if (member_field_count <= 0) {
+                            
+                                member_field_count = 1;
+                                member_fields[0] = member_type_size;
+                            
+                            }
+                        
+                        }
+                        
+                        member_align = declarator_is_pointer ? type_alignment (DATA_PTR) : type_alignment (member_type_size);
+                        
+                        if (member_align > aggregate_align) {
+                            aggregate_align = member_align;
+                        }
+                        
+                        repeat = 1;
+                        
+                        if (declarator_has_array) {
+                        
+                            repeat = (int) declarator_array_count;
+                            member_size *= declarator_array_count;
+                        
+                        }
+                        
+                        if (_accept (TOK_COLON)) {
+                            skip_balanced_until (TOK_COMMA, TOK_SEMI, TOK_RBRACE);
+                        }
+                        
+                        if (declarator_has_array && declarator_is_pointer) {
+                            member_info_elem_size = DATA_PTR;
+                        } else {
+                        
+                            member_info_elem_size = declarator_is_pointer ?
+                                (declarator_pointer_depth > 1 ? DATA_PTR : (parsed_type_is_aggregate ? parsed_type_size : (parsed_type_size & 0x1f))) :
+                                    (declarator_has_array ? member_type_size : member_size);
+                        
+                        }
+                        
+                        init_size = fields_storage_size (member_fields, member_field_count);
+                        
+                        if (is_union) {
+                        
+                            remember_member_info_ex (member, 0, member_size, member_info_elem_size, declarator_is_pointer ? declarator_pointer_depth : 0, declarator_has_array, declarator_is_pointer ? 0 : member_type_is_floating);
+                            
+                            if (member_size > aggregate_size) {
+                                aggregate_size = member_size;
+                            }
+                            
+                            if (union_init_field_count == 0) {
+                            
+                                union_init_field_count = member_field_count;
+                                
+                                for (i = 0; i < member_field_count; i++) {
+                                    union_init_fields[i] = member_fields[i];
+                                }
+                                
+                                union_init_size = init_size;
+                            
+                            }
+                        
+                        } else {
+                        
+                            int member_offset = (int) align_up_long (aggregate_size, member_align);
+                            int padding = member_offset - aggregate_size;
+                            
+                            int r;
+                            
+                            if (padding > 0 && aggregate_field_count < MAX_AGG_FIELDS) {
+                                aggregate_fields[aggregate_field_count++] = -padding;
+                            }
+                            
+                            aggregate_size = member_offset;
+                            
+                            remember_member_info_ex (member, member_offset, member_size, member_info_elem_size, declarator_is_pointer ? declarator_pointer_depth : 0, declarator_has_array, declarator_is_pointer ? 0 : member_type_is_floating);
+                            aggregate_size += member_size;
+                            
+                            for (r = 0; r < repeat; r++) {
+                            
+                                for (i = 0; i < member_field_count && aggregate_field_count < MAX_AGG_FIELDS; i++) {
+                                    aggregate_fields[aggregate_field_count++] = member_fields[i];
+                                }
+                            
+                            }
+                            
+                            if (member_size > init_size * repeat && aggregate_field_count < MAX_AGG_FIELDS) {
+                                aggregate_fields[aggregate_field_count++] = -(member_size - init_size * repeat);
+                            }
+                        
+                        }
+                        
+                        if (member) {
+                            free (member);
+                        }
+                        
+                        if (!_accept (TOK_COMMA)) {
+                            break;
+                        }
+                    
+                    }
+                
+                }
+                
+                expect (TOK_SEMI, ";");
+            
+            }
+            
+            expect (TOK_RBRACE, "}");
+            
+            if (aggregate_size <= 0) {
+                aggregate_size = DATA_CHAR & 0x1f;
+            }
+            
+            aggregate_size = (int) align_up_long (aggregate_size, aggregate_align);
+            
+            {
+            
+                const char *owner_name = 0;
+                char anon_owner_name[64];
+                
+                int mi;
+                
+                if (has_tag) {
+                    owner_name = tag_name;
+                } else {
+                
+                    sprintf (anon_owner_name, "<anon-aggregate-%d>", anonymous_aggregate_owner_id++);
+                    owner_name = anon_owner_name;
+                
+                }
+                
+                for (mi = member_info_start; mi < member_info_count; mi++) {
+                
+                    if (member_infos[mi].owner_size == 0) {
+                        member_infos[mi].owner_size = aggregate_size;
+                    }
+                    
+                    if (owner_name && member_infos[mi].owner_tag_name == 0) {
+                        member_infos[mi].owner_tag_name = xstrdup (owner_name);
+                    }
+                
+                }
+                
+                if (!has_tag && owner_name) {
+                
+                    strncpy (parsed_type_tag_name, owner_name, sizeof (parsed_type_tag_name) - 1);
+                    parsed_type_tag_name[sizeof (parsed_type_tag_name) - 1] = '\0';
+                
+                }
+            
+            }
+            
+            parsed_type_size = aggregate_size;
+            parsed_type_is_aggregate = 1;
+            parsed_type_is_void = 0;
+            parsed_type_has_tag = has_tag;
+            
+            clear_parsed_fields ();
+
+            if (is_union) {
+            
+                int i;
+                
+                for (i = 0; i < union_init_field_count; i++) {
+                    append_parsed_field (union_init_fields[i]);
+                }
+                
+                if (aggregate_size > union_init_size) {
+                    append_parsed_field (-(aggregate_size - union_init_size));
+                }
+            
+            } else {
+            
+                int i;
+                
+                for (i = 0; i < aggregate_field_count; i++) {
+                    append_parsed_field (aggregate_fields[i]);
+                }
+            
+            }
+            
+            if (tag_name) {
+            
+                save_aggregate_tag (tag_name, is_union, parsed_type_size, parsed_field_sizes, parsed_field_count);
+                update_typedef_name_from_aggregate_tag (tag_name, parsed_type_size, parsed_field_sizes, parsed_field_count);
+            
+            }
+        
+        } else {
+        
+            tag_entry = find_aggregate_tag (tag_name, is_union);
+
+            if (tag_entry) {
+                load_aggregate_tag_fields (tag_entry);
+            } else {
+            
+                parsed_type_size = DATA_PTR;
+                append_parsed_field (DATA_PTR);
+            
+            }
+        
+        }
+        
+        if (tag_name) {
+            free (tag_name);
+        }
+        
+        parsed_storage_class = aggregate_storage_class;
+        parsed_type_is_inline = aggregate_is_inline;
+        
+        return;
+    
+    }
+    
+    if (tok.kind == TOK_ENUM) {
+    
+        parsed_type_size = DATA_INT & 0x1f;
+        parsed_type_is_unsigned = 0;
+        
+        get_token ();
+        
+        if (token_is_ident ()) {
+            get_token ();
+        }
+        
+        if (tok.kind == TOK_LBRACE) {
+            parse_enum_body ();
+        }
+        
+        append_parsed_field (DATA_INT & 0x1f);
+        return;
+    
+    }
+    
+    /*if (saw_void) {
+        parsed_type_size = DATA_INT;
+    }*/
+    
+    parsed_type_is_floating = (saw_float || saw_double) ? 1 : 0;
+    
+    if (saw_signed && saw_unsigned) {
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "both signed and unsigned specified");
+    }
+
+    if ((saw_signed || saw_unsigned) && (saw_float || saw_double || saw_void)) {
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "signed/unsigned invalid for this type");
+    }
+
+    parsed_type_is_unsigned = saw_unsigned ? 1 : 0;
+    append_parsed_field (parsed_type_size);
+    
+    if (!saw) {
+    
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected type");
+        get_token ();
+    
+    }
+
+}
+
+static int declarator_object_size (int base_size);
+
+static void parse_direct_declarator (char **out_name) {
+
+    if (_accept (TOK_LPAREN)) {
+    
+        parse_declarator_inner (out_name);
+        expect (TOK_RPAREN, ")");
+        
+        if (declarator_is_pointer && !declarator_has_function) {
+            declarator_function_is_pointer = 1;
+        }
+    
+    } else if (token_is_ident ()) {
+    
+        if (out_name && !*out_name) {
+            *out_name = take_ident ();
+        } else {
+            get_token ();
+        }
+    
+    }
+    
+    for (;;) {
+    
+        if (_accept (TOK_LBRACK)) {
+        
+            long count = 1;
+            declarator_has_array = 1;
+            
+            if (tok.kind != TOK_RBRACK) {
+            
+                enum token_kind kill[3];
+                int v;
+                
+                kill[0] = TOK_RBRACK;
+                kill[1] = TOK_EOF;
+                kill[2] = 0;
+                
+                v = expr_const (kill);
+                
+                if (v > 0) {
+                    count = v;
+                }
+            
+            } else {
+                declarator_array_unsized = 1;
+            }
+            
+            if (declarator_array_count <= 0) {
+                declarator_array_count = 1;
+            }
+            
+            declarator_array_count *= count;
+            declarator_last_array_count = count;
+            
+            expect (TOK_RBRACK, "]");
+            continue;
+        
+        }
+        
+        if (_accept (TOK_LPAREN)) {
+        
+            declarator_has_function = 1;
+            
+            if (tok.kind != TOK_RPAREN) {
+            
+                for (;;) {
+                
+                    if (tok.kind == TOK_ELLIPSIS) {
+                    
+                        declarator_function_is_variadic = 1;
+                        get_token ();
+                    
+                    } else if (is_type_start (tok.kind)) {
+                    
+                        long saved_array_count = declarator_array_count;
+                        
+                        int saved_type_size = parsed_type_size;
+                        int saved_storage_class = parsed_storage_class;
+                        int saved_is_aggregate = parsed_type_is_aggregate;
+                        int saved_is_void = parsed_type_is_void;
+                        int saved_is_unsigned = parsed_type_is_unsigned;
+                        int saved_is_floating = parsed_type_is_floating;
+                        int saved_has_tag = parsed_type_has_tag;
+                        int saved_is_pointer = declarator_is_pointer;
+                        int saved_pointer_depth = declarator_pointer_depth;
+                        int saved_has_array = declarator_has_array;
+                        int saved_has_function = declarator_has_function;
+                        int saved_function_is_pointer = declarator_function_is_pointer;
+                        int saved_function_param_count = declarator_function_param_count;
+                        int saved_function_has_prototype = declarator_function_has_prototype;
+                        int saved_array_unsized = declarator_array_unsized;
+                        int saved_is_inline = parsed_type_is_inline;
+                        
+                        char *param_name = 0;
+                        int param_base_size, param_size;
+                        
+                        int count_this_param = 0;
+                        int saw_void_param_list = 0;
+                        
+                        parse_type_spec ();
+                        param_base_size = parsed_type_size;
+                        
+                        /*
+                         * Parameter declarators must not inherit the outer
+                         * declarator state.  In particular, in declarations
+                         * such as:
+                         *
+                         *     FILE **__gtin(void);
+                         *
+                         * the function return type leaves declarator_is_pointer
+                         * set before the parameter list is parsed.  If that flag
+                         * is still set while looking at the abstract void
+                         * parameter list, void is mistaken for a real pointer
+                         * parameter and the prototype is recorded as taking one
+                         * argument.
+                         */
+                        declarator_is_pointer = 0;
+                        declarator_pointer_depth = 0;
+                        declarator_has_array = 0;
+                        declarator_has_function = 0;
+                        declarator_function_is_pointer = 0;
+                        declarator_array_unsized = 0;
+                        declarator_array_count = 1;
+                        declarator_last_array_count = 1;
+                        
+                        if (parsed_type_only_qualifiers && token_is_ident ()) {
+                            get_token ();
+                        }
+                        
+                        if (tok.kind != TOK_COMMA && tok.kind != TOK_RPAREN) {
+                            parse_declarator (&param_name);
+                        }
+                        
+                        if (!parsed_type_is_void || declarator_is_pointer || declarator_has_array || declarator_has_function || param_name) {
+                        
+                            count_this_param = 1;
+                            param_size = (declarator_is_pointer || declarator_has_array || declarator_has_function || parsed_type_is_array_typedef) ? DATA_PTR : declarator_object_size (param_base_size);
+                            
+                            if (declarator_depth == 1) {
+                            
+                                add_pending_param (param_name, param_size, type_alignment (param_size),
+                                    (declarator_is_pointer || parsed_type_is_array_typedef ? 0 : parsed_type_is_unsigned),
+                                        (declarator_is_pointer || declarator_has_array || declarator_has_function || parsed_type_is_array_typedef) ? 0 : parsed_type_is_floating,
+                                            declarator_pointer_depth, parsed_type_is_aggregate ? parsed_type_size : (parsed_type_size & 0x1f));
+                            
+                            }
+                        
+                        } else {
+                            saw_void_param_list = 1;
+                        }
+                        
+                        if (param_name) {
+                            free (param_name);
+                        }
+                        
+                        parsed_type_size = saved_type_size;
+                        parsed_storage_class = saved_storage_class;
+                        parsed_type_is_aggregate = saved_is_aggregate;
+                        parsed_type_is_void = saved_is_void;
+                        parsed_type_is_unsigned = saved_is_unsigned;
+                        parsed_type_is_floating = saved_is_floating;
+                        parsed_type_has_tag = saved_has_tag;
+                        parsed_type_is_inline = saved_is_inline;
+                        
+                        declarator_is_pointer = saved_is_pointer;
+                        declarator_pointer_depth = saved_pointer_depth;
+                        declarator_has_array = saved_has_array;
+                        declarator_has_function = saved_has_function;
+                        declarator_function_is_pointer = saved_function_is_pointer;
+                        declarator_function_param_count = saved_function_param_count + (count_this_param ? 1 : 0);
+                        declarator_function_has_prototype = saved_function_has_prototype || count_this_param || saw_void_param_list;
+                        declarator_array_count = saved_array_count;
+                        
+                        declarator_array_unsized = saved_array_unsized;
+                    
+                    } else if (token_is_ident ()) {
+                    
+                        char *maybe_type_name = xstrdup (tok.ident);
+                        int unknown_typedef_pointer = 0;
+                        int consumed_as_prototype_param = 0;
+                        
+                        get_token ();
+                        
+                        while (tok.kind == TOK_CONST || tok.kind == TOK_VOLATILE || tok.kind == TOK_RESTRICT) {
+                            get_token ();
+                        }
+                        
+                        while (tok.kind == TOK_STAR) {
+                        
+                            unknown_typedef_pointer = 1;
+                            get_token ();
+                            
+                            while (tok.kind == TOK_CONST || tok.kind == TOK_VOLATILE || tok.kind == TOK_RESTRICT) {
+                                get_token ();
+                            }
+                        
+                        }
+                        
+                        if (token_is_ident ()) {
+                        
+                            char *param_name = xstrdup (tok.ident);
+                            int param_size = unknown_typedef_pointer ? DATA_PTR : DATA_PTR;
+                            
+                            get_token ();
+                            
+                            if (declarator_depth == 1) {
+                                add_pending_param (param_name, param_size, type_alignment (param_size), 0, 0, unknown_typedef_pointer ? 1 : 0, DATA_INT & 0x1f);
+                            }
+                            
+                            free (param_name);
+                            
+                            declarator_function_param_count++;
+                            declarator_function_has_prototype = 1;
+                            consumed_as_prototype_param = 1;
+                        
+                        }
+                        
+                        if (!consumed_as_prototype_param) {
+                            declarator_function_param_count++;
+                        }
+                        
+                        if (maybe_type_name) {
+                            free (maybe_type_name);
+                        }
+                    
+                    } else {
+                        skip_balanced_until (TOK_COMMA, TOK_RPAREN, TOK_EOF);
+                    }
+                    
+                    if (!_accept (TOK_COMMA)) {
+                        break;
+                    }
+                
+                }
+            
+            }
+            
+            expect (TOK_RPAREN, ")");
+            continue;
+        }
+        
+        break;
+    
+    }
+
+}
+
+static void parse_declarator_inner (char **out_name) {
+
+    while (tok.kind == TOK_STAR) {
+    
+        declarator_is_pointer = 1;
+        declarator_pointer_depth++;
+        
+        get_token ();
+        
+        while (tok.kind == TOK_CONST || tok.kind == TOK_VOLATILE || tok.kind == TOK_RESTRICT) {
+            get_token ();
+        }
+    
+    }
+    
+    parse_direct_declarator (out_name);
+
+}
+
+static void parse_declarator (char **out_name) {
+
+    int top_level = (declarator_depth == 0);
+    
+    int saved_capture_declarator_name_location = capture_declarator_name_location;
+    int saved_captured_declarator_name_location = captured_declarator_name_location;
+
+    const char *saved_captured_declarator_name_start = captured_declarator_name_start;
+    const char *saved_captured_declarator_name_caret = captured_declarator_name_caret;
+
+    unsigned long saved_captured_declarator_name_line = captured_declarator_name_line;
+    
+    if (top_level) {
+    
+        clear_pending_params ();
+        
+        capture_declarator_name_location = 1;
+        captured_declarator_name_location = 0;
+        captured_declarator_name_start = 0;
+        captured_declarator_name_caret = 0;
+        captured_declarator_name_line = 0;
+    
+    }
+    
+    declarator_depth++;
+    
+    declarator_is_pointer = 0;
+    declarator_pointer_depth = 0;
+    declarator_has_array = 0;
+    declarator_has_function = 0;
+    declarator_function_is_pointer = 0;
+    declarator_function_param_count = 0;
+    declarator_function_has_prototype = 0;
+    declarator_function_is_variadic = 0;
+    declarator_array_unsized = 0;
+    declarator_array_count = 1;
+    declarator_last_array_count = 1;
+    
+    parse_declarator_inner (out_name);
+    
+    if (top_level && parsed_type_is_array_typedef && !declarator_is_pointer && !declarator_has_array && !declarator_has_function) {
+    
+        declarator_has_array = 1;
+        
+        declarator_array_count = parsed_type_array_count > 0 ? parsed_type_array_count : 1;
+        declarator_last_array_count = declarator_array_count;
+        
+        if (parsed_type_array_element_size > 0) {
+            parsed_type_size = parsed_type_array_element_size;
+        }
+    
+    }
+    
+    declarator_depth--;
+    
+    if (top_level) {
+    
+        if (captured_declarator_name_location) {
+        
+            last_declarator_name_line = captured_declarator_name_line;
+            last_declarator_name_start = captured_declarator_name_start;
+            last_declarator_name_caret = captured_declarator_name_caret;
+        
+        }
+        
+        capture_declarator_name_location = saved_capture_declarator_name_location;
+        captured_declarator_name_location = saved_captured_declarator_name_location;
+        captured_declarator_name_start = saved_captured_declarator_name_start;
+        captured_declarator_name_caret = saved_captured_declarator_name_caret;
+        captured_declarator_name_line = saved_captured_declarator_name_line;
+    
+    }
+
+}
+
+static void apply_typedef_array_to_declarator (void) {
+
+    if (parsed_type_is_array_typedef && !declarator_is_pointer &&
+        !declarator_has_array && !declarator_has_function) {
+    
+        declarator_has_array = 1;
+        declarator_array_count = parsed_type_array_count > 0 ? parsed_type_array_count : 1;
+        declarator_last_array_count = declarator_array_count;
+        
+        if (parsed_type_array_element_size > 0) {
+            parsed_type_size = parsed_type_array_element_size;
+        }
+    
+    }
+
+}
+
+static void parse_old_style_param_decls (void) {
+
+    while (is_type_start (tok.kind)) {
+    
+        parse_type_spec ();
+        
+        for (;;) {
+        
+            char *name = 0;
+            parse_declarator (&name);
+            
+            if (name) {
+                free (name);
+            }
+            
+            if (_accept (TOK_ASSIGN)) {
+                skip_initializer ();
+            }
+            
+            if (!_accept (TOK_COMMA)) {
+                break;
+            }
+        
+        }
+        
+        expect (TOK_SEMI, ";");
+    
+    }
+
+}
+
+static void emit_extern_symbol (const char *name, int size, int is_function);
+static void emit_extern_reference_symbol (const char *name, int size);
+
+static void emit_function_start (const char *name, int is_static) {
+
+    const char *asm_name;
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    asm_name = asm_global_symbol_name (name);
+    switch_section (SECTION_TEXT);
+    
+    if (state->syntax & ASM_SYNTAX_MASM) {
+    
+        if (!is_static) {
+            fprintf (state->ofp, "public %s\n", asm_name);
+        }
+    
+    } else if (state->syntax & ASM_SYNTAX_NASM) {
+    
+        if (!is_static) {
+            fprintf (state->ofp, "global %s\n", asm_name);
+        }
+    
+    } else {
+    
+        if (!is_static) {
+            fprintf (state->ofp, ".globl %s\n", asm_name);
+        }
+    
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        fprintf (state->ofp, "%s:\n", asm_name);
+        fprintf (state->ofp, "    push ebp\n");
+        fprintf (state->ofp, "    mov ebp, esp\n");
+    
+    } else {
+    
+        fprintf (state->ofp, "%s:\n", asm_name);
+        fprintf (state->ofp, "    pushl %%ebp\n");
+        fprintf (state->ofp, "    movl %%esp, %%ebp\n");
+    
+    }
+
+}
+
+static void emit_pending_return_jump (void) {
+
+    if (!pending_return_jump) {
+        return;
+    }
+    
+    if (!state->ofp) {
+    
+        pending_return_jump = 0;
+        return;
+    
+    }
+    
+    if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+        fprintf (state->ofp, "    jmp L%d\n", current_return_label);
+    } else {
+        fprintf (state->ofp, "    jmp .L%d\n", current_return_label);
+    }
+    
+    pending_return_jump = 0;
+
+}
+
+static void emit_stack_adjust (long bytes, int allocate) {
+
+    if (!state->ofp || bytes <= 0) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+        fprintf (state->ofp, "    %s esp, %ld\n", allocate ? "sub" : "add", bytes);
+    } else {
+        fprintf (state->ofp, "    %s $%ld, %%esp\n", allocate ? "subl" : "addl", bytes);
+    }
+
+}
+
+static void format_intel_ebp_offset (char *buf, size_t bufsz, long offset) {
+
+    if (!buf || bufsz == 0) {
+        return;
+    }
+    
+    if (offset < 0) {
+        sprintf (buf, "[ebp - %ld]", -offset);
+    } else if (offset > 0) {
+        sprintf (buf, "[ebp + %ld]", offset);
+    } else {
+        sprintf (buf, "[ebp]");
+    }
+
+}
+
+static const char *format_nasm_memory_operand (char *buf, size_t bufsz, const char *operand) {
+
+    if (!operand) {
+        return operand;
+    }
+    
+    if (!(state->syntax & ASM_SYNTAX_NASM)) {
+        return operand;
+    }
+    
+    if (operand[0] == '[') {
+        return operand;
+    }
+    
+    if (buf && bufsz > 0) {
+    
+        sprintf (buf, "[%s]", operand);
+        return buf;
+    
+    }
+    
+    return operand;
+
+}
+
+static void emit_local_store_const (long offset, int size, int64_s value) {
+
+    char memref_hi[64], memref[64];
+    unsigned long high, low;
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    low = value.low;
+    high = value.high;
+    
+    if (size > 8 && low == 0 && high == 0) {
+    
+        long pos;
+        
+        for (pos = 0; pos < size; pos += 4) {
+            emit_local_store_const (offset + pos, DATA_INT, value);
+        }
+        
+        return;
+    
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        format_intel_ebp_offset (memref, sizeof (memref), offset);
+        
+        if (size == (DATA_CHAR & 0x1f)) {
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov byte %s, %lu\n" : "    mov byte ptr %s, %lu\n"), memref, low & ((1 << 8) - 1));
+        } else if (size == (DATA_SHORT & 0x1f)) {
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov word %s, %lu\n" : "    mov word ptr %s, %lu\n"), memref, low & ((1 << 16) - 1));
+        } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_DOUBLE & 0x1f)) {
+        
+            format_intel_ebp_offset (memref_hi, sizeof (memref_hi), offset + 4);
+            
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov dword %s, %lu\n" : "    mov dword ptr %s, %lu\n"), memref, low & U32_MASK);
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov dword %s, %lu\n" : "    mov dword ptr %s, %lu\n"), memref_hi, high & U32_MASK);
+        
+        } else {
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov dword %s, %lu\n" : "    mov dword ptr %s, %lu\n"), memref, low & U32_MASK);
+        }
+    
+    } else {
+    
+        if (size == (DATA_CHAR & 0x1f)) {
+            fprintf (state->ofp, "    movb $%lu, %ld(%%ebp)\n", low & 0xffUL, offset);
+        } else if (size == (DATA_SHORT & 0x1f)) {
+            fprintf (state->ofp, "    movw $%lu, %ld(%%ebp)\n", low & 0xffffUL, offset);
+        } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_DOUBLE & 0x1f)) {
+        
+            fprintf (state->ofp, "    movl $%lu, %ld(%%ebp)\n", low & U32_MASK, offset);
+            fprintf (state->ofp, "    movl $%lu, %ld(%%ebp)\n", high & U32_MASK, offset + 4);
+        
+        } else {
+            fprintf (state->ofp, "    movl $%lu, %ld(%%ebp)\n", low & U32_MASK, offset);
+        }
+    
+    }
+
+}
+
+static void emit_local_store_address (long offset, const char *symbol) {
+
+    const char *asm_symbol;
+    char memref[64];
+    
+    if (!state->ofp || !symbol) {
+        return;
+    }
+    
+    emit_extern_reference_symbol (symbol, DATA_PTR);
+    asm_symbol = asm_global_symbol_name (symbol);
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        format_intel_ebp_offset (memref, sizeof (memref), offset);
+        fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov dword %s, %s\n" : "    mov dword ptr %s, offset %s\n"), memref, asm_symbol);
+    
+    } else {
+        fprintf (state->ofp, "    movl $%s, %ld(%%ebp)\n", asm_symbol, offset);
+    }
+
+}
+
+static void emit_local_store_stack (long dst_offset, int dst_size, long src_offset, int src_size) {
+
+    char dst[64];
+    char src[64];
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        format_intel_ebp_offset (dst, sizeof (dst), dst_offset);
+        format_intel_ebp_offset (src, sizeof (src), src_offset);
+
+        if (dst_size == (DATA_CHAR & 0x1f)) {
+        
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov al, byte %s\n" : "    mov al, byte ptr %s\n"), src);
+            fprintf (state->ofp, "    mov byte ptr %s, al\n", dst);
+        
+        } else if (dst_size == (DATA_SHORT & 0x1f)) {
+        
+            if (src_size == (DATA_CHAR & 0x1f)) {
+                fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov ax, byte %s\n" : "    movzx ax, byte ptr %s\n"), src);
+            } else {
+                fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov ax, word %s\n" : "    mov ax, word ptr %s\n"), src);
+            }
+            
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov word %s, ax\n" : "    mov word ptr %s, ax\n"), dst);
+        
+        } else {
+        
+            if (src_size == (DATA_CHAR & 0x1f)) {
+                fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    movzx eax, byte %s\n" : "    movzx eax, byte ptr %s\n"), src);
+            } else if (src_size == (DATA_SHORT & 0x1f)) {
+                fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    movzx eax, word %s\n" : "    movzx eax, word ptr %s\n"), src);
+            } else {
+                fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov eax, dword %s\n" : "    mov eax, dword ptr %s\n"), src);
+            }
+            
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov dword %s, eax\n" : "    mov dword ptr %s, eax\n"), dst);
+        
+        }
+    
+    } else {
+    
+        if (dst_size == (DATA_CHAR & 0x1f)) {
+        
+            fprintf (state->ofp, "    movb %ld(%%ebp), %%al\n", src_offset);
+            fprintf (state->ofp, "    movb %%al, %ld(%%ebp)\n", dst_offset);
+        
+        } else if (dst_size == (DATA_SHORT & 0x1f)) {
+        
+            fprintf (state->ofp, "    movw %ld(%%ebp), %%ax\n", src_offset);
+            fprintf (state->ofp, "    movw %%ax, %ld(%%ebp)\n", dst_offset);
+        
+        } else {
+        
+            fprintf (state->ofp, "    movl %ld(%%ebp), %%eax\n", src_offset);
+            fprintf (state->ofp, "    movl %%eax, %ld(%%ebp)\n", dst_offset);
+        
+        }
+    
+    }
+
+}
+
+static void emit_local_store_global (long dst_offset, int dst_size, const char *symbol) {
+
+    const char *asm_symbol;
+    char dst[64];
+    
+    if (!state->ofp || !symbol) {
+        return;
+    }
+    
+    emit_extern_reference_symbol (symbol, get_global_symbol_size (symbol));
+    asm_symbol = asm_global_symbol_name (symbol);
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        format_intel_ebp_offset (dst, sizeof (dst), dst_offset);
+        
+        if (dst_size == (DATA_CHAR & 0x1f)) {
+        
+            if (state->syntax & ASM_SYNTAX_NASM) {
+            
+                fprintf (state->ofp, "    mov al, byte [%s]\n", asm_symbol);
+                fprintf (state->ofp, "    mov byte %s, al\n", dst);
+            
+            } else {
+            
+                fprintf (state->ofp, "    mov al, byte ptr %s\n", asm_symbol);
+                fprintf (state->ofp, "    mov byte ptr %s, al\n", dst);
+            
+            }
+        
+        } else if (dst_size == (DATA_SHORT & 0x1f)) {
+        
+            if (state->syntax & ASM_SYNTAX_NASM) {
+            
+                fprintf (state->ofp, "    mov ax, word [%s]\n", asm_symbol);
+                fprintf (state->ofp, "    mov word %s, ax\n", dst);
+            
+            } else {
+            
+                fprintf (state->ofp, "    mov ax, word ptr %s\n", asm_symbol);
+                fprintf (state->ofp, "    mov word ptr %s, ax\n", dst);
+            
+            }
+        
+        } else {
+        
+            if (state->syntax & ASM_SYNTAX_NASM) {
+            
+                fprintf (state->ofp, "    mov eax, dword [%s]\n", asm_symbol);
+                fprintf (state->ofp, "    mov dword %s, eax\n", dst);
+            
+            } else {
+            
+                fprintf (state->ofp, "    mov eax, dword ptr %s\n", asm_symbol);
+                fprintf (state->ofp, "    mov dword ptr %s, eax\n", dst);
+            
+            }
+        
+        }
+    
+    } else {
+    
+        if (dst_size == (DATA_CHAR & 0x1f)) {
+        
+            fprintf (state->ofp, "    movb %s, %%al\n", asm_symbol);
+            fprintf (state->ofp, "    movb %%al, %ld(%%ebp)\n", dst_offset);
+        
+        } else if (dst_size == (DATA_SHORT & 0x1f)) {
+        
+            fprintf (state->ofp, "    movw %s, %%ax\n", asm_symbol);
+            fprintf (state->ofp, "    movw %%ax, %ld(%%ebp)\n", dst_offset);
+        
+        } else {
+        
+            fprintf (state->ofp, "    movl %s, %%eax\n", asm_symbol);
+            fprintf (state->ofp, "    movl %%eax, %ld(%%ebp)\n", dst_offset);
+        
+        }
+    
+    }
+
+}
+
+static void emit_local_initializers (struct local_init *inits, int init_count) {
+
+    int i;
+    
+    for (i = 0; i < init_count; i++) {
+    
+        if (inits[i].kind == LOCAL_INIT_ADDRESS) {
+            emit_local_store_address (inits[i].offset, inits[i].symbol);
+        } else if (inits[i].kind == LOCAL_INIT_STACK) {
+            emit_local_store_stack (inits[i].offset, inits[i].size, inits[i].source_offset, inits[i].source_size);
+        } else if (inits[i].kind == LOCAL_INIT_GLOBAL) {
+            emit_local_store_global (inits[i].offset, inits[i].size, inits[i].symbol);
+        } else {
+            emit_local_store_const (inits[i].offset, inits[i].size, inits[i].value);
+        }
+        
+        if (inits[i].symbol) {
+        
+            free (inits[i].symbol);
+            inits[i].symbol = 0;
+        
+        }
+    
+    }
+
+}
+
+static void emit_function_end (void) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+    
+        fprintf (state->ofp, "L%d:\n", current_return_label);
+        
+        if (current_function_returns_aggregate) {
+        
+            if (state->syntax & ASM_SYNTAX_NASM) {
+                fprintf (state->ofp, "    mov eax, dword [ebp + 8]\n");
+            } else {
+                fprintf (state->ofp, "    mov eax, dword ptr [ebp + 8]\n");
+            }
+        
+        }
+        
+        fprintf (state->ofp, "    leave\n");
+        fprintf (state->ofp, "    ret\n");
+    
+    } else {
+    
+        fprintf (state->ofp, ".L%d:\n", current_return_label);
+        
+        if (current_function_returns_aggregate) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    mov eax, dword ptr [ebp + 8]\n");
+            } else {
+                fprintf (state->ofp, "    movl 8(%%ebp), %%eax\n");
+            }
+        
+        }
+        
+        fprintf (state->ofp, "    leave\n");
+        fprintf (state->ofp, "    ret\n");
+    
+    }
+
+}
+
+static void emit_preserve_assignment64_regs (enum token_kind op) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        fprintf (state->ofp, "    push ebx\n");
+        
+        if (op == TOK_STAREQ || op == TOK_STAR || op == TOK_SLASHEQ || op == TOK_BSLASH || op == TOK_MODEQ || op == TOK_MOD) {
+        
+            fprintf (state->ofp, "    push esi\n");
+            fprintf (state->ofp, "    push edi\n");
+        
+        }
+    
+    } else {
+    
+        fprintf (state->ofp, "    pushl %%ebx\n");
+        
+        if (op == TOK_STAREQ || op == TOK_STAR || op == TOK_SLASHEQ || op == TOK_BSLASH || op == TOK_MODEQ || op == TOK_MOD) {
+        
+            fprintf (state->ofp, "    pushl %%esi\n");
+            fprintf (state->ofp, "    pushl %%edi\n");
+        
+        }
+    
+    }
+
+}
+
+static void emit_restore_assignment64_regs (enum token_kind op) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        if (op == TOK_STAREQ || op == TOK_STAR || op == TOK_SLASHEQ || op == TOK_BSLASH || op == TOK_MODEQ || op == TOK_MOD) {
+        
+            fprintf (state->ofp, "    pop edi\n");
+            fprintf (state->ofp, "    pop esi\n");
+        
+        }
+        
+        fprintf (state->ofp, "    pop ebx\n");
+    
+    } else {
+    
+        if (op == TOK_STAREQ || op == TOK_STAR || op == TOK_SLASHEQ || op == TOK_BSLASH || op == TOK_MODEQ || op == TOK_MOD) {
+        
+            fprintf (state->ofp, "    popl %%edi\n");
+            fprintf (state->ofp, "    popl %%esi\n");
+        
+        }
+        
+        fprintf (state->ofp, "    popl %%ebx\n");
+    
+    }
+
+}
+
+static int is_string_token (void);
+static void parse_string_initializer_values (int64_s *values, int max_values, int *count);
+
+static int parse_constexpr_null_member_address_after_lparen (int64_s *out) {
+
+    char current_tag_name[128];
+    
+    int current_object_size;
+    unsigned long total_offset = 0;
+    
+    if (tok.kind != TOK_LPAREN) {
+        return 0;
+    }
+    
+    get_token ();
+    
+    if (tok.kind != TOK_LPAREN) {
+        return 0;
+    }
+    
+    get_token ();
+    
+    if (!parse_cast_type_name (0, 0, 0)) {
+        return 0;
+    }
+    
+    current_tag_name[0] = '\0';
+    
+    if (last_cast_type_tag_name[0]) {
+    
+        strncpy (current_tag_name, last_cast_type_tag_name, sizeof (current_tag_name) - 1);
+        current_tag_name[sizeof (current_tag_name) - 1] = '\0';
+    
+    }
+    
+    current_object_size = last_cast_type_object_size;
+    
+    if (!((tok.kind == TOK_CINT || tok.kind == TOK_CUINT || tok.kind == TOK_CLONG ||
+           tok.kind == TOK_CULONG || tok.kind == TOK_CLLONG || tok.kind == TOK_CULLONG) &&
+          tok.val.i.low == 0 && tok.val.i.high == 0)) {
+        return 0;
+    }
+    
+    get_token ();
+    expect (TOK_RPAREN, ")");
+    
+    while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+    
+        enum token_kind member_op = tok.kind;
+        char *member;
+        
+        const char *member_start;
+        const char *member_caret;
+        
+        unsigned long member_line;
+        
+        int offset = 0;
+        int size = DATA_INT & 0x1f;
+        int elem_size = DATA_INT & 0x1f;
+        int pointer_depth = 0;
+        int is_array = 0;
+        
+        get_token ();
+        
+        member_start = tok.start;
+        member_caret = tok.caret;
+        member_line = get_line_number ();
+        
+        if (tok.kind != TOK_IDENT) {
+        
+            report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret,
+                            "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+            return 1;
+        
+        }
+        
+        member = xstrdup (tok.ident);
+        get_token ();
+        
+        if (!find_member_info_ex_bounded (member, current_object_size,
+                                          current_tag_name[0] ? current_tag_name : 0,
+                                          &offset, &size, &elem_size, &pointer_depth, &is_array, 0)) {
+        
+            report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret,
+                            "unknown member '%s'", member);
+            
+            free (member);
+            return 1;
+        
+        }
+        
+        free (member);
+        total_offset += (unsigned long) offset;
+        
+        if (last_found_member_tag_name && last_found_member_tag_name[0]) {
+        
+            strncpy (current_tag_name, last_found_member_tag_name, sizeof (current_tag_name) - 1);
+            current_tag_name[sizeof (current_tag_name) - 1] = '\0';
+        
+        } else {
+            current_tag_name[0] = '\0';
+        }
+        
+        while (tok.kind == TOK_LBRACK) {
+        
+            enum token_kind kill[2];
+            int64_s index;
+            
+            get_token ();
+            
+            kill[0] = TOK_RBRACK;
+            kill[1] = 0;
+            
+            index = expr_const64 (kill);
+            
+            expect (TOK_RBRACK, "]");
+            total_offset += index.low * (unsigned long) (elem_size > 0 ? elem_size : (DATA_INT & 0x1f));
+        
+        }
+        
+        if (pointer_depth > 0) {
+            current_object_size = elem_size > 0 ? elem_size : (DATA_PTR & 0x1f);
+        } else if (is_array && elem_size > 0) {
+            current_object_size = elem_size;
+        } else {
+            current_object_size = size;
+        }
+    
+    }
+    
+    expect (TOK_RPAREN, ")");
+    
+    if (out) {
+        zext64 (out, total_offset);
+    }
+    
+    return 1;
+
+}
+
+int parse_constexpr_address_of_null_member (int64_s *out) {
+
+    if (tok.kind != TOK_AMPER) {
+        return 0;
+    }
+    
+    get_token ();
+    
+    if (tok.kind != TOK_LPAREN) {
+        return 0;
+    }
+    
+    get_token ();
+    
+    if (parse_constexpr_null_member_address_after_lparen (out)) {
+        return 1;
+    }
+    
+    return 0;
+
+}
+
+int token_is_sizeof_keyword (void) {
+    return tok.kind == TOK_SIZEOF;
+}
+
+static int64_s sizeof_from_current_token (void) {
+
+    int64_s v;
+    int size;
+    
+    zext64 (&v, 0);
+    get_token ();
+    
+    size = parse_sizeof_value ();
+    zext64 (&v, (unsigned long) size);
+    
+    return v;
+
+}
+
+static int64_s const64_from_current_expr (void) {
+
+    enum token_kind kill[6];
+    int64_s v;
+    
+    kill[0] = TOK_SEMI;
+    kill[1] = TOK_COMMA;
+    kill[2] = TOK_RPAREN;
+    kill[3] = TOK_RBRACK;
+    kill[4] = TOK_RBRACE;
+    kill[5] = 0;
+    
+    v = expr_const64 (kill);
+    return v;
+
+}
+
+static int64_s const64_from_current_operand (void) {
+
+    enum token_kind kill[26];
+    
+    if (token_is_sizeof_keyword ()) {
+        return sizeof_from_current_token ();
+    }
+    
+    kill[0] = TOK_PLUS;
+    kill[1] = TOK_MINUS;
+    kill[2] = TOK_STAR;
+    kill[3] = TOK_BSLASH;
+    kill[4] = TOK_MOD;
+    kill[5] = TOK_LSH;
+    kill[6] = TOK_RSH;
+    kill[7] = TOK_AMPER;
+    kill[8] = TOK_PIPE;
+    kill[9] = TOK_CARET;
+    kill[10] = TOK_LESS;
+    kill[11] = TOK_LTEQ;
+    kill[12] = TOK_GREATER;
+    kill[13] = TOK_GTEQ;
+    kill[14] = TOK_EQEQ;
+    kill[15] = TOK_NOTEQ;
+    kill[16] = TOK_LOGAND;
+    kill[17] = TOK_LOGOR;
+    kill[18] = TOK_QMARK;
+    kill[19] = TOK_COLON;
+    kill[20] = TOK_SEMI;
+    kill[21] = TOK_COMMA;
+    kill[22] = TOK_RPAREN;
+    kill[23] = TOK_RBRACK;
+    kill[24] = TOK_RBRACE;
+    kill[25] = 0;
+    
+    return expr_const64 (kill);
+
+}
+
+static int64_s const64_from_current_foldable_expr (void) {
+
+    enum token_kind kill[17];
+    
+    if (token_is_sizeof_keyword ()) {
+        return sizeof_from_current_token ();
+    }
+    
+    kill[0] = TOK_LESS;
+    kill[1] = TOK_LTEQ;
+    kill[2] = TOK_GREATER;
+    kill[3] = TOK_GTEQ;
+    kill[4] = TOK_EQEQ;
+    kill[5] = TOK_NOTEQ;
+    kill[6] = TOK_LOGAND;
+    kill[7] = TOK_LOGOR;
+    kill[8] = TOK_QMARK;
+    kill[9] = TOK_COLON;
+    kill[10] = TOK_SEMI;
+    kill[11] = TOK_COMMA;
+    kill[12] = TOK_RPAREN;
+    kill[13] = TOK_RBRACK;
+    kill[14] = TOK_RBRACE;
+    kill[15] = 0;
+    kill[16] = 0;
+    
+    return expr_const64 (kill);
+
+}
+
+static long const_from_current_expr (void) {
+
+    int64_s v = const64_from_current_expr ();
+    return (long) v.low;
+
+}
+
+static long const_from_current_case_expr (void) {
+
+    enum token_kind kill[7];
+    int64_s v;
+    
+    if (token_is_sizeof_keyword ()) {
+    
+        v = sizeof_from_current_token ();
+        return (long) v.low;
+    
+    }
+    
+    kill[0] = TOK_COLON;
+    kill[1] = TOK_SEMI;
+    kill[2] = TOK_COMMA;
+    kill[3] = TOK_RPAREN;
+    kill[4] = TOK_RBRACK;
+    kill[5] = TOK_RBRACE;
+    kill[6] = 0;
+    
+    v = expr_const64 (kill);
+    return (long) v.low;
+
+}
+
+static int declarator_element_size_from_fields (int base_size, const int *field_sizes, int field_count);
+
+static int declarator_pointed_size_now (void) {
+
+    if (declarator_pointer_depth > 1) {
+        return DATA_PTR;
+    }
+    
+    if (parsed_type_is_aggregate) {
+        return parsed_type_size;
+    }
+    
+    return parsed_type_size & 0x1f;
+
+}
+
+static struct aggregate_tag_entry *hidden_typedef_pointer_entry_now (void) {
+
+    if (declarator_pointer_depth != 0 || declarator_is_pointer ||
+        declarator_has_array || declarator_has_function) {
+        return 0;
+    }
+    
+    if ((parsed_type_size & 0x1f) != (DATA_PTR & 0x1f) ||
+        !parsed_type_tag_name[0]) {
+        return 0;
+    }
+    
+    return find_aggregate_tag (parsed_type_tag_name, 0);
+
+}
+
+static int declarator_effective_pointer_depth_now (void) {
+
+    if (declarator_pointer_depth > 0) {
+        return declarator_pointer_depth;
+    }
+    
+    return hidden_typedef_pointer_entry_now () ? 1 : 0;
+
+}
+
+static int declarator_effective_pointed_size_now (int base_size, const int *field_sizes, int field_count) {
+
+    struct aggregate_tag_entry *entry;
+    
+    if (declarator_pointer_depth > 0 || declarator_is_pointer) {
+        return declarator_pointed_size_now ();
+    }
+    
+    entry = hidden_typedef_pointer_entry_now ();
+    
+    if (entry) {
+        return entry->size;
+    }
+    
+    return declarator_element_size_from_fields (base_size, field_sizes, field_count);
+
+}
+
+static int declarator_element_size_from_fields (int base_size, const int *field_sizes, int field_count) {
+
+    int total = 0;
+    int i;
+    
+    if (field_sizes && field_count > 0) {
+    
+        for (i = 0; i < field_count; i++) {
+            total += field_sizes[i] & 0x1f;
+        }
+        
+        if (total > 0) {
+            return total;
+        }
+    
+    }
+    
+    if (parsed_type_is_aggregate) {
+        return base_size;
+    }
+    
+    return base_size & 0x1f;
+
+}
+
+static int declarator_object_size (int base_size) {
+
+    if (declarator_has_array) {
+    
+        int elem_size = declarator_is_pointer ? DATA_PTR : base_size;
+        
+        if (declarator_array_count <= 0) {
+            return elem_size;
+        }
+        
+        return elem_size * declarator_array_count;
+    
+    }
+    
+    if (declarator_is_pointer) {
+        return DATA_PTR;
+    }
+    
+    return base_size;
+
+}
+
+static int count_brace_initializer_elements_from_assign_now (void) {
+
+    const char *p;
+    
+    int brace_depth = 0;
+    int paren_depth = 0;
+    int bracket_depth = 0;
+    int count = 0;
+    int saw_element = 0;
+    
+    if (tok.kind != TOK_ASSIGN || !tok.start) {
+        return 0;
+    }
+    
+    p = tok.start;
+    
+    while (*p && *p != '=') {
+        p++;
+    }
+    
+    if (*p != '=') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p != '{') {
+        return 0;
+    }
+    
+    p++;
+    brace_depth = 1;
+    
+    while (*p && brace_depth > 0) {
+    
+        if (*p == '\'' || *p == '"') {
+        
+            int quote = *p++;
+            saw_element = 1;
+            
+            while (*p && *p != quote) {
+            
+                if (*p == '\\' && p[1]) {
+                    p += 2;
+                } else {
+                    p++;
+                }
+            
+            }
+            
+            if (*p == quote) {
+                p++;
+            }
+            
+            continue;
+        
+        }
+        
+        if (*p == '{') {
+        
+            brace_depth++;
+            saw_element = 1;
+        
+        } else if (*p == '}') {
+        
+            if (brace_depth == 1 && paren_depth == 0 && bracket_depth == 0) {
+                break;
+            }
+            
+            brace_depth--;
+        
+        } else if (*p == '(') {
+        
+            paren_depth++;
+            saw_element = 1;
+        
+        } else if (*p == ')') {
+        
+            if (paren_depth > 0) {
+                paren_depth--;
+            }
+        
+        } else if (*p == '[') {
+        
+            bracket_depth++;
+            saw_element = 1;
+        
+        } else if (*p == ']') {
+        
+            if (bracket_depth > 0) {
+                bracket_depth--;
+            }
+        
+        } else if (*p == ',' && brace_depth == 1 && paren_depth == 0 && bracket_depth == 0) {
+        
+            if (saw_element) {
+            
+                count++;
+                saw_element = 0;
+            
+            }
+        
+        } else if (!(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) {
+            saw_element = 1;
+        }
+        
+        p++;
+    
+    }
+    
+    if (saw_element) {
+        count++;
+    }
+    
+    return count;
+
+}
+
+static int parse_sizeof_member_expr_size (int leading_stars, int *out_size) {
+
+    int size = DATA_INT & 0x1f;
+    int pointer_depth = 0;
+    int pointed_size = 0;
+    int is_array = 0;
+    int final_pointer_depth = 0;
+    int final_pointed_size = 0;
+    int final_is_array = 0;
+    
+    struct local_symbol *local;
+    
+    if (tok.kind != TOK_IDENT) {
+        return 0;
+    }
+    
+    local = find_local_symbol (tok.ident);
+    
+    if (local) {
+    
+        size = local->size;
+        
+        pointer_depth = local->pointer_depth;
+        pointed_size = local->pointed_size;
+        
+        is_array = local->is_array;
+    
+    } else if (find_global_symbol (tok.ident) >= 0) {
+    
+        size = get_global_symbol_size (tok.ident);
+        
+        pointer_depth = get_global_symbol_pointer_depth (tok.ident);
+        pointed_size = get_global_symbol_pointed_size (tok.ident);
+        
+        is_array = get_global_symbol_array (tok.ident);
+    
+    } else {
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "unknown symbol '%s'", tok.ident ? tok.ident : "");
+    }
+    
+    final_pointer_depth = pointer_depth;
+    final_pointed_size = pointed_size;
+    final_is_array = is_array;
+    
+    get_token ();
+    
+    while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+    
+        int member_pointer_depth = 0;
+        int member_pointed_size = 0;
+        int member_offset = 0;
+        int member_size = 0;
+        int member_is_array = 0;
+        
+        const char *member_tag_name = 0;
+        enum token_kind member_op = tok.kind;
+        
+        get_token ();
+        
+        if (tok.kind != TOK_IDENT) {
+        
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret,
+                            "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+            break;
+        
+        }
+        
+        if (find_member_info_ex (tok.ident, &member_offset, &member_size,
+                                 &member_pointed_size, &member_pointer_depth,
+                                 &member_is_array, 0)) {
+        
+            member_tag_name = last_found_member_tag_name ? last_found_member_tag_name : find_member_tag_name (tok.ident);
+            size = member_size;
+            
+            final_pointer_depth = member_pointer_depth;
+            final_pointed_size = member_pointed_size;
+            
+            final_is_array = member_is_array;
+        
+            if (final_pointer_depth > 0 && member_tag_name) {
+            
+                struct aggregate_tag_entry *entry = find_aggregate_tag (member_tag_name, 0);
+                
+                if (entry) {
+                    final_pointed_size = entry->size;
+                }
+            
+            }
+        
+        } else {
+        
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret,
+                            "unknown member '%s'", tok.ident ? tok.ident : "");
+        
+        }
+        
+        get_token ();
+    
+    }
+    
+    while (tok.kind == TOK_LBRACK) {
+    
+        int depth = 1;
+        get_token ();
+        
+        while (tok.kind != TOK_EOF && depth > 0) {
+        
+            if (tok.kind == TOK_LBRACK) {
+                depth++;
+            } else if (tok.kind == TOK_RBRACK) {
+            
+                depth--;
+                
+                if (depth == 0) {
+                    break;
+                }
+            
+            }
+            
+            get_token ();
+        
+        }
+        
+        if (tok.kind == TOK_RBRACK) {
+            get_token ();
+        }
+        
+        if (final_pointer_depth > 0) {
+        
+            size = final_pointer_depth > 1 ? DATA_PTR : final_pointed_size;
+            final_pointer_depth--;
+        
+        } else if (final_is_array && final_pointed_size > 0) {
+            size = final_pointed_size;
+        }
+        
+        final_is_array = 0;
+    
+    }
+    
+    if (leading_stars > 0 && final_is_array && final_pointer_depth == 0) {
+    
+        /*
+         * In expressions, an array object decays to a pointer to its first
+         * element.  sizeof(*array) must therefore return the element size,
+         * not the total array object size.  This matters for tables such as
+         * static const struct builtin_macro builtin[] where sizeof(*builtin)
+         * is used to compute the element count.
+         */
+        if (final_pointed_size > 0) {
+            size = final_pointed_size;
+        }
+    
+    } else if (leading_stars > 0 && final_pointer_depth >= leading_stars) {
+    
+        int remaining_depth = final_pointer_depth - leading_stars;
+        size = remaining_depth > 0 ? DATA_PTR : final_pointed_size;
+    
+    }
+    
+    if (size < 1) {
+        size = DATA_INT & 0x1f;
+    }
+    
+    *out_size = size;
+    return 1;
+
+}
+
+int parse_sizeof_value (void) {
+
+    int saved_type_size = parsed_type_size;
+    int saved_storage_class = parsed_storage_class;
+    int saved_is_aggregate = parsed_type_is_aggregate;
+    int saved_is_void = parsed_type_is_void;
+    int saved_is_unsigned = parsed_type_is_unsigned;
+    int saved_is_floating = parsed_type_is_floating;
+    int saved_has_tag = parsed_type_has_tag;
+    int saved_is_inline = parsed_type_is_inline;
+    int saved_field_count = parsed_field_count;
+    int saved_fields[MAX_AGG_FIELDS];
+    
+    int saved_declarator_is_pointer = declarator_is_pointer;
+    int saved_declarator_has_array = declarator_has_array;
+    int saved_declarator_has_function = declarator_has_function;
+    int saved_declarator_array_unsized = declarator_array_unsized;
+    long saved_declarator_array_count = declarator_array_count;
+    
+    int size = DATA_INT & 0x1f;
+    int i;
+    
+    for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) {
+        saved_fields[i] = parsed_field_sizes[i];
+    }
+    
+    if (_accept (TOK_LPAREN)) {
+    
+        if (is_type_start (tok.kind)) {
+        
+            char *name = 0;
+            int base_size;
+            
+            parse_type_spec ();
+            base_size = parsed_type_size;
+            
+            /*
+             * sizeof(type-name) may not contain a declarator, for example
+             * sizeof(FILE).  parse_declarator() normally clears these flags,
+             * but when the next token is ')' it is not called.  Do not let a
+             * previous declaration such as FILE **__gtin(void) make this
+             * type-name look like a pointer and collapse the size to 4.
+             */
+            declarator_is_pointer = 0;
+            declarator_pointer_depth = 0;
+            declarator_has_array = 0;
+            declarator_has_function = 0;
+            declarator_function_is_pointer = 0;
+            declarator_function_param_count = 0;
+            declarator_function_has_prototype = 0;
+            declarator_function_is_variadic = 0;
+            declarator_array_unsized = 0;
+            declarator_array_count = 1;
+            declarator_last_array_count = 1;
+            
+            if (tok.kind != TOK_RPAREN) {
+                parse_declarator (&name);
+            }
+            
+            size = declarator_object_size (base_size);
+            
+            if (name) {
+                free (name);
+            }
+            
+            expect (TOK_RPAREN, ")");
+        
+        } else if (is_string_token ()) {
+        
+            int64_s values[MAX_STRING_INIT_BYTES];
+            int value_count = 0;
+            
+            parse_string_initializer_values (values, MAX_STRING_INIT_BYTES, &value_count);
+            size = value_count;
+            expect (TOK_RPAREN, ")");
+        
+        } else {
+        
+            int depth = 1;
+            int first = 1;
+            int leading_stars = 0;
+            
+            while (tok.kind != TOK_EOF && depth > 0) {
+            
+                if (first) {
+                
+                    while (tok.kind == TOK_STAR || tok.kind == TOK_AMPER || tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) {
+                    
+                        if (tok.kind == TOK_STAR) {
+                            leading_stars++;
+                        }
+                        
+                        get_token ();
+                    
+                    }
+                
+                }
+                
+                if (first && tok.kind == TOK_IDENT) {
+                
+                    parse_sizeof_member_expr_size (leading_stars, &size);
+                    
+                    first = 0;
+                    continue;
+                
+                }
+                
+                if (first && tok.kind == TOK_LPAREN && leading_stars > 0) {
+                
+                    get_token ();
+                    
+                    if (tok.kind == TOK_IDENT) {
+                        parse_sizeof_member_expr_size (leading_stars, &size);
+                    }
+                    
+                    while (tok.kind != TOK_EOF && tok.kind != TOK_RPAREN) {
+                        get_token ();
+                    }
+                    
+                    if (tok.kind == TOK_RPAREN) {
+                        get_token ();
+                    }
+                    
+                    first = 0;
+                    continue;
+                
+                }
+                
+                first = 0;
+                
+                if (tok.kind == TOK_LPAREN) {
+                    depth++;
+                } else if (tok.kind == TOK_RPAREN) {
+                
+                    depth--;
+                    
+                    if (depth == 0) {
+                        break;
+                    }
+                
+                }
+                
+                get_token ();
+            
+            }
+            
+            expect (TOK_RPAREN, ")");
+        
+        }
+    
+    } else {
+    
+        int leading_stars = 0;
+        
+        while (tok.kind == TOK_STAR || tok.kind == TOK_AMPER || tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) {
+        
+            if (tok.kind == TOK_STAR) {
+                leading_stars++;
+            }
+            
+            get_token ();
+        
+        }
+        
+        if (is_string_token ()) {
+        
+            int64_s values[MAX_STRING_INIT_BYTES];
+            int value_count = 0;
+            
+            parse_string_initializer_values (values, MAX_STRING_INIT_BYTES, &value_count);
+            size = value_count;
+        
+        } else if (tok.kind == TOK_IDENT) {
+            parse_sizeof_member_expr_size (leading_stars, &size);
+        } else if (tok.kind == TOK_LPAREN) {
+        
+            int depth = 1;
+            
+            get_token ();
+            
+            while (tok.kind != TOK_EOF && depth > 0) {
+            
+                if (tok.kind == TOK_LPAREN) {
+                    depth++;
+                } else if (tok.kind == TOK_RPAREN) {
+                
+                    depth--;
+                    
+                    if (depth == 0) {
+                        break;
+                    }
+                
+                }
+                
+                get_token ();
+            
+            }
+            
+            expect (TOK_RPAREN, ")");
+        
+        } else if (tok.kind != TOK_EOF) {
+            get_token ();
+        }
+        
+        while (tok.kind == TOK_LBRACK || tok.kind == TOK_LPAREN) {
+        
+            enum token_kind close = (tok.kind == TOK_LBRACK) ? TOK_RBRACK : TOK_RPAREN;
+            int depth = 1;
+            
+            get_token ();
+            
+            while (tok.kind != TOK_EOF && depth > 0) {
+            
+                if ((close == TOK_RBRACK && tok.kind == TOK_LBRACK) || (close == TOK_RPAREN && tok.kind == TOK_LPAREN)) {
+                    depth++;
+                } else if (tok.kind == close) {
+                
+                    depth--;
+                    
+                    if (depth == 0) {
+                        break;
+                    }
+                
+                }
+                
+                get_token ();
+            
+            }
+            
+            expect (close, (close == TOK_RBRACK) ? "]" : ")");
+        
+        }
+    
+    }
+    
+    parsed_type_size = saved_type_size;
+    parsed_storage_class = saved_storage_class;
+    parsed_type_is_aggregate = saved_is_aggregate;
+    parsed_type_is_void = saved_is_void;
+    parsed_type_is_unsigned = saved_is_unsigned;
+    parsed_type_is_floating = saved_is_floating;
+    parsed_type_has_tag = saved_has_tag;
+    parsed_type_is_inline = saved_is_inline;
+    
+    clear_parsed_fields ();
+    
+    for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) {
+        append_parsed_field (saved_fields[i]);
+    }
+    
+    declarator_is_pointer = saved_declarator_is_pointer;
+    declarator_has_array = saved_declarator_has_array;
+    declarator_has_function = saved_declarator_has_function;
+    declarator_array_unsized = saved_declarator_array_unsized;
+    declarator_array_count = saved_declarator_array_count;
+    
+    if (size < 1) {
+        size = DATA_INT & 0x1f;
+    }
+    
+    return size;
+
+}
+
+static void make_declarator_fields (int *out_fields, int *out_count, const int *base_fields, int base_count, int base_size, int base_is_aggregate) {
+
+    int i;
+    long r;
+    
+    *out_count = 0;
+    
+    if (declarator_is_pointer) {
+    
+        out_fields[(*out_count)++] = DATA_PTR;
+        return;
+    
+    }
+    
+    if (declarator_has_array) {
+    
+        if (base_is_aggregate && base_count > 0) {
+        
+            for (r = 0; r < declarator_array_count && *out_count < MAX_AGG_FIELDS; r++) {
+            
+                for (i = 0; i < base_count && *out_count < MAX_AGG_FIELDS; i++) {
+                    out_fields[(*out_count)++] = base_fields[i];
+                }
+            
+            }
+        
+        } else {
+        
+            for (r = 0; r < declarator_array_count && *out_count < MAX_AGG_FIELDS; r++) {
+                out_fields[(*out_count)++] = (base_size & 0x1f);
+            }
+        
+        }
+        
+        return;
+
+    }
+    
+    for (i = 0; i < base_count && *out_count < MAX_AGG_FIELDS; i++) {
+        out_fields[(*out_count)++] = base_fields[i];
+    }
+    
+    if (*out_count == 0) {
+        out_fields[(*out_count)++] = (base_size & 0x1f);
+    }
+
+}
+
+static void parse_statement (void);
+
+static void emit_load_assignment_rhs_expression_to_pair (const char *lo, const char *hi, int is_unsigned);
+static void emit_load_assignment_rhs_expression_to_reg (const char *reg);
+
+static void emit_incdec_symbol_now (struct local_symbol *sym, const char *name, enum token_kind op, int line, const char *start, const char *caret);
+
+static int initializer_contains_runtime_call_now (void) {
+
+    const char *p = tok.start;
+    
+    int paren_depth = 0;
+    int bracket_depth = 0;
+    int brace_depth = 0;
+    
+    while (*p) {
+    
+        if (paren_depth == 0 && bracket_depth == 0 && brace_depth == 0 && (*p == ';' || *p == ',' || *p == '}')) {
+            break;
+        }
+        
+        if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_') {
+        
+            const char *q = p + 1;
+            
+            while ((*q >= 'A' && *q <= 'Z') || (*q >= 'a' && *q <= 'z') || (*q >= '0' && *q <= '9') || *q == '_') {
+                q++;
+            }
+            
+            while (*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n') {
+                q++;
+            }
+            
+            if (*q == '(') {
+                return 1;
+            }
+            
+            p = q;
+            continue;
+        
+        }
+        
+        if (*p == '\'' || *p == '"') {
+        
+            int quote = *p++;
+            
+            while (*p && *p != quote) {
+            
+                if (*p == '\\' && p[1]) {
+                    p += 2;
+                } else {
+                    p++;
+                }
+            
+            }
+            
+            if (*p == quote) {
+                p++;
+            }
+            
+            continue;
+        
+        }
+        
+        if (*p == '(') {
+            paren_depth++;
+        } else if (*p == ')') {
+        
+            if (paren_depth > 0) {
+                paren_depth--;
+            }
+        
+        } else if (*p == '[') {
+            bracket_depth++;
+        } else if (*p == ']') {
+        
+            if (bracket_depth > 0) {
+                bracket_depth--;
+            }
+        
+        } else if (*p == '{') {
+            brace_depth++;
+        } else if (*p == '}') {
+        
+            if (brace_depth > 0) {
+                brace_depth--;
+            }
+        
+        }
+        
+        p++;
+    
+    }
+    
+    return 0;
+
+}
+
+static int auto_init_ident_char_now (int ch) {
+    return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_';
+}
+
+static int auto_init_expr_continues_after_ident_now (void) {
+
+    const char *p;
+    const char *q;
+    
+    if (tok.kind != TOK_IDENT) {
+        return 1;
+    }
+    
+    p = tok.start;
+    q = p;
+    
+    while (auto_init_ident_char_now ((unsigned char) *q)) {
+        q++;
+    }
+    
+    while (*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n') {
+        q++;
+    }
+    
+    return !(*q == ';' || *q == ',' || *q == '}');
+
+}
+
+static int auto_initializer_needs_runtime_now (void) {
+
+    const char *p = tok.start;
+    int paren_depth = 0;
+    int bracket_depth = 0;
+    int brace_depth = 0;
+    
+    if (initializer_contains_runtime_call_now () || tok.kind == TOK_LPAREN) {
+        return 1;
+    }
+    
+    if (tok.kind == TOK_IDENT) {
+        return auto_init_expr_continues_after_ident_now ();
+    }
+    
+    if (tok.kind == TOK_AMPER) {
+    
+        p++;
+        
+        while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+            p++;
+        }
+        
+        if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) {
+            return 1;
+        }
+        
+        while (auto_init_ident_char_now ((unsigned char) *p)) {
+            p++;
+        }
+        
+        while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+            p++;
+        }
+        
+        return !(*p == ';' || *p == ',' || *p == '}');
+    
+    }
+    
+    p = tok.start;
+    
+    while (*p) {
+    
+        if (paren_depth == 0 && bracket_depth == 0 && brace_depth == 0 && (*p == ';' || *p == ',' || *p == '}')) {
+            break;
+        }
+        
+        if (*p == '[' || *p == ']' || *p == '*') {
+            return 1;
+        }
+        
+        if (*p == '(') {
+            paren_depth++;
+        } else if (*p == ')') {
+            if (paren_depth > 0) paren_depth--;
+        } else if (*p == '[') {
+            bracket_depth++;
+        } else if (*p == ']') {
+            if (bracket_depth > 0) bracket_depth--;
+        } else if (*p == '{') {
+            brace_depth++;
+        } else if (*p == '}') {
+            if (brace_depth > 0) brace_depth--;
+        }
+        
+        if (((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_') && p[0] != 'L') {
+            return 1;
+        }
+        
+        p++;
+    
+    }
+    
+    return 0;
+
+}
+
+static void parse_local_initializer_value (struct local_init *init) {
+
+    init->kind = LOCAL_INIT_CONST;
+    init->symbol = 0;
+    init->value.low = 0;
+    init->value.high = 0;
+    init->source_offset = 0;
+    init->source_size = 0;
+    
+    if (tok.kind == TOK_LBRACE) {
+    
+        get_token ();
+        parse_local_initializer_value (init);
+        
+        while (_accept (TOK_COMMA)) {
+            skip_initializer ();
+        }
+        
+        expect (TOK_RBRACE, "}");
+        return;
+    
+    }
+    
+    if (tok.kind == TOK_AMPER) {
+    
+        get_token ();
+        
+        if (tok.kind != TOK_IDENT) {
+        
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after '&'");
+            return;
+        
+        }
+        
+        init->kind = LOCAL_INIT_ADDRESS;
+        init->symbol = xstrdup (tok.ident);
+        
+        get_token ();
+        return;
+    
+    }
+    
+    if (tok.kind == TOK_IDENT) {
+    
+        struct local_symbol *src = find_local_symbol (tok.ident);
+        
+        if (src) {
+        
+            if (src->is_static && src->static_label) {
+            
+                init->kind = LOCAL_INIT_GLOBAL;
+                init->symbol = xstrdup (src->static_label);
+            
+            } else {
+            
+                init->kind = LOCAL_INIT_STACK;
+                
+                init->source_offset = src->offset;
+                init->source_size = src->size;
+            
+            }
+            
+            get_token ();
+            return;
+        
+        }
+        
+        if (find_global_symbol (tok.ident) >= 0) {
+        
+            init->kind = LOCAL_INIT_GLOBAL;
+            init->symbol = xstrdup (tok.ident);
+            
+            get_token ();
+            return;
+        
+        }
+    
+    }
+    
+    init->value = const64_from_current_expr ();
+
+}
+
+static void parse_local_string_field_initializer (struct local_init *inits, int *init_count, int max_inits, long base_offset, int field_size) {
+
+    int64_s values[MAX_STRING_INIT_BYTES];
+    int value_count = 0, i;
+    
+    parse_string_initializer_values (values, MAX_STRING_INIT_BYTES, &value_count);
+    
+    for (i = 0; i < field_size; i++) {
+    
+        if (*init_count >= max_inits) {
+            continue;
+        }
+        
+        inits[*init_count].offset = base_offset + i;
+        inits[*init_count].size = 1;
+        inits[*init_count].kind = LOCAL_INIT_CONST;
+        inits[*init_count].symbol = 0;
+        inits[*init_count].value.low = (i < value_count) ? (values[i].low & 0xffUL) : 0;
+        inits[*init_count].value.high = 0;
+        inits[*init_count].source_offset = 0;
+        inits[*init_count].source_size = 0;
+        
+        (*init_count)++;
+    
+    }
+
+}
+
+static void parse_local_aggregate_initializer_values (struct local_init *inits, int *init_count, int max_inits, long base_offset, const int *fields, int field_count) {
+
+    int field_index = 0;
+    long field_offset = 0;
+    int braced = 0;
+    
+    if (tok.kind == TOK_LBRACE) {
+        braced = 1;
+        get_token ();
+    }
+    
+    while (field_index < field_count) {
+    
+        int field_size = fields[field_index];
+        
+        if (field_size < 0) {
+        
+            field_offset += -field_size;
+            
+            field_index++;
+            continue;
+        
+        }
+        
+        if (braced && tok.kind == TOK_RBRACE) {
+        
+            if (*init_count < max_inits) {
+            
+                inits[*init_count].offset = base_offset + field_offset;
+                inits[*init_count].size = field_size;
+                inits[*init_count].kind = LOCAL_INIT_CONST;
+                inits[*init_count].symbol = 0;
+                inits[*init_count].value.low = 0;
+                inits[*init_count].value.high = 0;
+                inits[*init_count].source_offset = 0;
+                inits[*init_count].source_size = 0;
+                
+                (*init_count)++;
+            
+            }
+            
+            field_offset += field_size;
+            field_index++;
+            
+            continue;
+        
+        }
+        
+        if (*init_count < max_inits) {
+        
+            if (is_string_token ()) {
+                parse_local_string_field_initializer (inits, init_count, max_inits, base_offset + field_offset, field_size);
+            } else {
+            
+                inits[*init_count].offset = base_offset + field_offset;
+                inits[*init_count].size = field_size;
+                
+                parse_local_initializer_value (&inits[*init_count]);
+                (*init_count)++;
+            
+            }
+        
+        } else {
+            skip_initializer ();
+        }
+        
+        field_offset += field_size;
+        field_index++;
+        
+        if (braced) {
+        
+            if (!_accept (TOK_COMMA)) {
+                break;
+            }
+            
+            if (tok.kind == TOK_RBRACE) {
+                break;
+            }
+        
+        } else {
+            break;
+        }
+    
+    }
+    
+    if (braced) {
+    
+        while (field_index < field_count) {
+        
+            int field_size = fields[field_index];
+            
+            if (field_size < 0) {
+            
+                field_offset += -field_size;
+                
+                field_index++;
+                continue;
+            
+            }
+            
+            if (*init_count < max_inits) {
+            
+                inits[*init_count].offset = base_offset + field_offset;
+                inits[*init_count].size = field_size;
+                inits[*init_count].kind = LOCAL_INIT_CONST;
+                inits[*init_count].symbol = 0;
+                inits[*init_count].value.low = 0;
+                inits[*init_count].value.high = 0;
+                inits[*init_count].source_offset = 0;
+                inits[*init_count].source_size = 0;
+                
+                (*init_count)++;
+            
+            }
+            
+            field_offset += field_size;
+            field_index++;
+        
+        }
+        
+        while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) {
+        
+            skip_initializer ();
+            
+            if (!_accept (TOK_COMMA)) {
+                break;
+            }
+        }
+        
+        expect (TOK_RBRACE, "}");
+    
+    }
+
+}
+
+static void make_local_static_label (char *buf, size_t bufsz, const char *name) {
+
+    if (!buf || bufsz == 0) {
+        return;
+    }
+    
+    if (!name) {
+        name = "anon";
+    }
+    
+    if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+        sprintf (buf, "LC%d_%s", local_static_id++, name);
+    } else {
+        sprintf (buf, ".LC%d_%s", local_static_id++, name);
+    }
+
+}
+
+static void emit_global_object (const char *name, int size, int is_array, long array_count, const int *field_sizes, int field_count, const int64_s *values, char **symbols, int value_count, int is_aggregate, int is_static);
+static void emit_extern_symbol (const char *name, int size, int is_function);
+static void emit_extern_reference_symbol (const char *name, int size);
+
+static void emit_block_static_object (const char *label, int size, int is_array, long array_count, const int *field_sizes, int field_count, const int64_s *values, char **symbols, int value_count, int is_aggregate) {
+    
+    emit_global_object (label, size, is_array, array_count, field_sizes, field_count, values, symbols, value_count, is_aggregate, 1);
+    
+    /**
+     * We may have switched to .data/.data? to emit the static object while
+     * parsing a function body.  The following statements still belong in
+     * .code/.text.
+     */
+    switch_section (SECTION_TEXT);
+
+}
+
+static char *emit_string_literal_global (void);
+static int is_string_token (void);
+
+static void ensure_block_stack_allocated (long block_stack_start, long *block_stack_bytes, int *block_stack_emitted) {
+
+    long needed_stack_bytes = align_up_long (current_local_stack_size - block_stack_start, 4);
+    
+    /*
+     * The parser can emit code while it is still inside a declaration list
+     * containing comma-separated declarators and runtime initializers.  Keep
+     * one word of conservative headroom once a block frame exists, so a
+     * subsequently-discovered automatic slot in the same declaration phase is
+     * not allowed to overlap the caller frame before the next adjustment is
+     * emitted.  This fixes cases like:
+     *
+     *     cpp_mffc *old = reader->mffc;
+     *     struct if_stack *ifs, *next_ifs;
+     *     ... next_ifs = ifs->next;
+     *
+     * where generated code used -12(%ebp) after reserving only 8 bytes.
+     */
+    if (needed_stack_bytes > 0) {
+        needed_stack_bytes = align_up_long (needed_stack_bytes + 4, 4);
+    }
+    
+    if (!block_stack_bytes || !block_stack_emitted) {
+        return;
+    }
+    
+    if (!*block_stack_emitted) {
+    
+        *block_stack_bytes = needed_stack_bytes;
+        emit_stack_adjust (*block_stack_bytes, 1);
+        
+        if (current_parse_block_depth > 1) {
+            current_block_cleanup_bytes += *block_stack_bytes;
+        }
+        
+        *block_stack_emitted = (*block_stack_bytes > 0);
+    
+    } else if (needed_stack_bytes > *block_stack_bytes) {
+    
+        long extra_stack_bytes = needed_stack_bytes - *block_stack_bytes;
+        emit_stack_adjust (extra_stack_bytes, 1);
+        
+        if (current_parse_block_depth > 1) {
+            current_block_cleanup_bytes += extra_stack_bytes;
+        }
+        
+        *block_stack_bytes = needed_stack_bytes;
+    
+    }
+
+}
+
+static void parse_global_initializer_values (int64_s *values, char **symbols, int max_values, int *count);
+static void parse_string_initializer_values (int64_s *values, int max_values, int *count);
+
+static int aggregate_initializer_value_field_count (const int *field_sizes, int field_count);
+static void parse_global_initializer_values_padded_elements (int64_s *values, char **symbols, int max_values, int *count, int element_field_count);
+
+static void parse_char_array_initializer_values (int64_s *values, int max_values, int *count, long row_width);
+static int64_s parse_floating_const_expr_bits_now (int size);
+
+static void emit_store_pair_to_local64 (long offset, const char *lo, const char *hi);
+static void emit_store_reg_to_local (long offset, int size, const char *reg);
+
+static void emit_load_local_address_to_reg_now (const char *reg, long offset);
+static int emit_aggregate_copy_from_current_rhs_to_addr_reg_now (const char *addr_reg, int offset, int size);
+
+static void parse_block (void) {
+
+    int declaration_phase = 1, is_function_body = (current_parse_block_depth == 0);
+    
+    int block_local_start = local_symbol_count;
+    int block_stack_emitted = 0;
+    
+    long block_stack_start = current_local_stack_size;
+    long block_stack_bytes = 0;
+    
+    struct local_init inits[MAX_LOCAL_INITS];
+    int block_scope_start, init_count = 0;
+    
+    block_scope_start = is_function_body ? 0 : block_local_start;
+    current_parse_block_depth++;;
+    
+    expect (TOK_LBRACE, "{");
+    
+    while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) {
+    
+        if (is_type_start (tok.kind)) {
+        
+            if (!declaration_phase) {
+            
+                if (state->std == 90) {
+                    report_line_at (get_filename (), get_line_number (), (state->pedantic ? REPORT_ERROR : REPORT_WARNING), tok.start, tok.caret, "ISO C90 forbids mixed declarations and code");
+                }
+            
+            }
+            
+            parse_type_spec ();
+            
+            for (;;) {
+            
+                long object_offset = 0;
+                
+                int object_init_size = 0;
+                int object_is_auto = 0;
+                
+                char *name = 0;
+                unsigned long name_line;
+                
+                const char *name_start, *name_caret;
+                int object_align, object_size = 0;
+                
+                int object_fields[MAX_AGG_FIELDS];
+                int object_field_count = 0;
+                int init_value_count = 0;
+                
+                int64_s init_values[MAX_AGG_FIELDS];
+                int i;
+                
+                char *init_symbols[MAX_AGG_FIELDS];
+                char static_label[128];
+                
+                for (i = 0; i < MAX_AGG_FIELDS; i++) {
+                    init_symbols[i] = 0;
+                }
+                
+                parse_declarator (&name);
+                apply_typedef_array_to_declarator ();
+                
+                if (declarator_has_array && declarator_array_unsized && tok.kind == TOK_ASSIGN) {
+                
+                    int inferred_count = count_brace_initializer_elements_from_assign_now ();
+                    
+                    if (inferred_count > 0) {
+                        declarator_array_count = inferred_count;
+                    }
+                
+                }
+                
+                name_line = last_declarator_name_line;
+                name_start = last_declarator_name_start;
+                name_caret = last_declarator_name_caret;
+                
+                make_declarator_fields (object_fields, &object_field_count, parsed_field_sizes, parsed_field_count, parsed_type_size, parsed_type_is_aggregate);
+                
+                if (parsed_type_is_void && !declarator_is_pointer && !declarator_has_function) {
+                    report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "variable '%s' declared void", name ? name : "");
+                }
+                
+                if (declarator_is_pointer) {
+                
+                    object_field_count = 1;
+                    object_fields[0] = DATA_PTR;
+                
+                }
+                
+                if (parsed_storage_class == STORAGE_TYPEDEF && name) {
+                
+                    save_typedef_name (name, declarator_object_size (parsed_type_size),
+                        (declarator_is_pointer ? 0 : parsed_type_is_unsigned),
+                            (declarator_is_pointer ? 0 : parsed_type_is_void),
+                                (!declarator_is_pointer && (parsed_type_is_aggregate || declarator_has_array)),
+                                    (!declarator_is_pointer && declarator_has_array),
+                                        declarator_array_count, parsed_type_size,
+                                            object_fields, object_field_count);
+                
+                } else if (name && parsed_storage_class == STORAGE_EXTERN) {
+                
+                    if (add_global_symbol (name, (declarator_has_function && !declarator_function_is_pointer) ? GLOBAL_SYMBOL_FUNCTION : GLOBAL_SYMBOL_OBJECT, 1, name_start, name_caret, name_line)) {
+                    
+                        set_global_symbol_size (name, (declarator_has_function || declarator_is_pointer) ? DATA_PTR : declarator_object_size (parsed_type_size));
+                        set_global_symbol_pointer_info (name, declarator_effective_pointer_depth_now (),
+                            declarator_effective_pointed_size_now (parsed_type_size, object_fields, object_field_count));
+                        set_global_symbol_tag_name (name, parsed_type_tag_name);
+                        set_global_symbol_unsigned (name, (declarator_is_pointer || declarator_has_function) ? 0 : parsed_type_is_unsigned);
+                        set_global_symbol_floating (name, (declarator_is_pointer || declarator_has_function) ? 0 : parsed_type_is_floating);
+                        set_global_symbol_returns_void (name, declarator_has_function && parsed_type_is_void && !declarator_is_pointer && !declarator_function_is_pointer);
+                        
+                        if (declarator_has_function) {
+                            set_global_symbol_param_count (name, declarator_function_param_count, declarator_function_has_prototype || declarator_function_param_count > 0, declarator_function_is_variadic);
+                        }
+                        
+                        switch_section (SECTION_TEXT);
+                    
+                    }
+                
+                } else if (name && parsed_storage_class != STORAGE_STATIC) {
+                
+                    object_size = declarator_object_size (parsed_type_size);
+                    object_align = declarator_is_pointer ? type_alignment (DATA_PTR) : type_alignment (parsed_type_is_aggregate ? DATA_PTR : parsed_type_size);
+                    
+                    object_offset = add_local_symbol (name, object_size, object_align,
+                        (declarator_is_pointer ? 0 : parsed_type_is_unsigned),
+                            block_scope_start, name_line, name_start, name_caret);
+                    
+                    /*
+                     * Reserve stack space as soon as an automatic local receives
+                     * an EBP-relative slot.  Some blocks start with declarations
+                     * and then immediately execute statements that write those
+                     * locals, for example:
+                     *
+                     *     if (...) { location_t loc; unsigned int i; loc.file = ...; }
+                     *
+                     * Waiting until the declaration phase is exited proved too
+                     * fragile after runtime initializer handling was added: code
+                     * could use -4/-8/-12(%ebp) before any sub esp had been
+                     * emitted.  Keep block_stack_bytes in sync here so the later
+                     * declaration-phase transition is a no-op unless a later
+                     * unsized aggregate expands the frame.
+                     */
+                    ensure_block_stack_allocated (block_stack_start, &block_stack_bytes, &block_stack_emitted);
+                    
+                    set_local_symbol_floating (name, declarator_is_pointer ? 0 : parsed_type_is_floating);
+                    set_local_symbol_array (name, declarator_has_array);
+                    set_local_symbol_array_element_size (name, declarator_has_array ? (int)(object_size / (declarator_array_count > 0 ? declarator_array_count : 1)) : 0);
+                    set_local_symbol_pointer_info (name, declarator_effective_pointer_depth_now (),
+                        declarator_effective_pointed_size_now (parsed_type_size, object_fields, object_field_count));
+                    
+                    if (!declarator_is_pointer && parsed_type_tag_name[0]) {
+                    
+                        struct local_symbol *lsym = find_local_symbol (name);
+                        
+                        if (lsym) {
+                            lsym->tag_name = xstrdup (parsed_type_tag_name);
+                        }
+                    
+                    }
+                    
+                    object_init_size = declarator_is_pointer ? DATA_PTR : parsed_type_size;
+                    object_is_auto = 1;
+                    
+                    /*
+                     * If an earlier automatic declaration in this block had a
+                     * runtime initializer, stack space has already been emitted.
+                     * Any later automatic declaration extends current_local_stack_size,
+                     * and its initializer may immediately store to the new slot.
+                     * Reserve the extra bytes before parsing/emitting that initializer;
+                     * otherwise code can write to -8(%ebp), -12(%ebp), etc. after
+                     * only the first 4 bytes were allocated.
+                     */
+                    if (block_stack_emitted) {
+                        ensure_block_stack_allocated (block_stack_start, &block_stack_bytes, &block_stack_emitted);
+                    }
+                
+                }
+                
+                if (_accept (TOK_ASSIGN)) {
+                
+                    /*
+                     * Reserve the stack slot before parsing an automatic initializer.
+                     *
+                     * Some initializer forms which are not compile-time constants
+                     * can still reach code emission through parse_local_initializer_value().
+                     * If the stack adjustment is delayed until the first following
+                     * statement, those stores use offsets that have not yet been
+                     * reserved.  In create_definition(), this affected:
+                     *
+                     *     cpp_token *saved_cur_token = reader->cur_token;
+                     *
+                     * The generated code stored to -64(%ebp) while only -60 bytes
+                     * had been allocated, so the following call overwrote the saved
+                     * value and reader->cur_token was restored with garbage.
+                     */
+                    if (object_is_auto) {
+                        ensure_block_stack_allocated (block_stack_start, &block_stack_bytes, &block_stack_emitted);
+                    }
+                    
+                    if (parsed_storage_class == STORAGE_EXTERN) {
+                    
+                        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "extern declaration '%s' is initialized", name ? name : "");
+                        skip_initializer ();
+                    
+                    } else if (parsed_storage_class == STORAGE_STATIC && name) {
+                    
+                        if (declarator_has_array && !declarator_is_pointer && (parsed_type_size & 0x1f) == (DATA_CHAR & 0x1f) && (is_string_token () || tok.kind == TOK_LBRACE)) {
+                        
+                            if (is_string_token ()) {
+                                parse_string_initializer_values (init_values, MAX_AGG_FIELDS, &init_value_count);
+                            } else {
+                                parse_char_array_initializer_values (init_values, MAX_AGG_FIELDS, &init_value_count, declarator_last_array_count);
+                            }
+                            
+                            if (declarator_array_unsized) {
+                                declarator_array_count = init_value_count;
+                            }
+                        
+                        } else if (declarator_is_pointer && is_string_token ()) {
+                        
+                            init_values[0].low = 0;
+                            init_values[0].high = 0;
+                            
+                            init_symbols[0] = emit_string_literal_global ();
+                            init_value_count = 1;
+                        
+                        } else if (!declarator_is_pointer && parsed_type_is_floating) {
+                        
+                            init_values[0] = parse_floating_const_expr_bits_now (parsed_type_size);
+                            init_symbols[0] = 0;
+                            init_value_count = 1;
+                        
+                        } else {
+                        
+                            {
+                            
+                                int saved_accept_symbol_addresses = global_initializer_accept_symbol_addresses;
+                                global_initializer_accept_symbol_addresses = declarator_is_pointer || parsed_type_is_aggregate || declarator_has_array;
+                                
+                                if (declarator_has_array && !declarator_is_pointer && parsed_type_is_aggregate && parsed_field_count > 0 && tok.kind == TOK_LBRACE) {
+                                    parse_global_initializer_values_padded_elements (init_values, init_symbols, MAX_AGG_FIELDS, &init_value_count, aggregate_initializer_value_field_count (parsed_field_sizes, parsed_field_count));
+                                } else {
+                                    parse_global_initializer_values (init_values, init_symbols, MAX_AGG_FIELDS, &init_value_count);
+                                }
+                                
+                                global_initializer_accept_symbol_addresses = saved_accept_symbol_addresses;
+                            
+                            }
+                            
+                            if (declarator_has_array && declarator_array_unsized) {
+                            
+                                if (parsed_type_is_aggregate && object_field_count > 0) {
+                                    declarator_array_count = (init_value_count + object_field_count - 1) / object_field_count;
+                                } else {
+                                    declarator_array_count = init_value_count;
+                                }
+                            
+                            }
+                        
+                        }
+                    
+                    } else if (object_is_auto && declarator_has_array && declarator_is_pointer && tok.kind == TOK_LBRACE) {
+                    
+                        int elem_index = 0;
+                        int elem_size = DATA_PTR;
+                        int first_init_index = init_count;
+                        
+                        get_token ();
+                        
+                        while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) {
+                        
+                            if (init_count < MAX_LOCAL_INITS) {
+                            
+                                inits[init_count].offset = object_offset + (elem_index * elem_size);
+                                inits[init_count].size = elem_size;
+                                
+                                if (is_string_token ()) {
+                                
+                                    inits[init_count].kind = LOCAL_INIT_ADDRESS;
+                                    inits[init_count].symbol = emit_string_literal_global ();
+                                    inits[init_count].value.low = 0;
+                                    inits[init_count].value.high = 0;
+                                    inits[init_count].source_offset = 0;
+                                    inits[init_count].source_size = 0;
+                                    
+                                    switch_section (SECTION_TEXT);
+                                
+                                } else {
+                                    parse_local_initializer_value (&inits[init_count]);
+                                }
+                                
+                                init_count++;
+                            
+                            } else {
+                                skip_initializer ();
+                            }
+                            
+                            elem_index++;
+                            
+                            if (!_accept (TOK_COMMA)) {
+                                break;
+                            }
+                            
+                            if (tok.kind == TOK_RBRACE) {
+                                break;
+                            }
+                        
+                        }
+                        
+                        if (declarator_array_unsized && elem_index > 0 && (elem_index * elem_size) > object_size) {
+                        
+                            long old_offset = object_offset;
+                            long new_offset;
+                            long delta;
+                            int adj_i;
+                            
+                            current_local_stack_size += (elem_index * elem_size) - object_size;
+                            new_offset = -current_local_stack_size;
+                            delta = new_offset - old_offset;
+                            object_offset = new_offset;
+                            object_size = elem_index * elem_size;
+                            
+                            if (local_symbol_count > 0 && name && local_symbols[local_symbol_count - 1].name
+                                && strcmp (local_symbols[local_symbol_count - 1].name, name) == 0) {
+                            
+                                local_symbols[local_symbol_count - 1].offset = object_offset;
+                                local_symbols[local_symbol_count - 1].size = object_size;
+                            
+                            }
+                            
+                            for (adj_i = first_init_index; adj_i < init_count; adj_i++) {
+                                inits[adj_i].offset += delta;
+                            }
+                        
+                        }
+                        
+                        expect (TOK_RBRACE, "}");
+                    
+                    } else if (object_is_auto && !declarator_is_pointer && (parsed_type_is_aggregate || declarator_has_array) && tok.kind == TOK_LBRACE) {
+                        parse_local_aggregate_initializer_values (inits, &init_count, MAX_LOCAL_INITS, object_offset, object_fields, object_field_count);
+                    } else if (object_is_auto && auto_initializer_needs_runtime_now ()) {
+                    
+                        long needed_stack_bytes = align_up_long (current_local_stack_size - block_stack_start, 4);
+                        
+                        if (!block_stack_emitted) {
+                        
+                            block_stack_bytes = needed_stack_bytes;
+                            emit_stack_adjust (block_stack_bytes, 1);
+                            
+                            if (!is_function_body) {
+                                current_block_cleanup_bytes += block_stack_bytes;
+                            }
+                            
+                            block_stack_emitted = (block_stack_bytes > 0);
+                        
+                        } else if (needed_stack_bytes > block_stack_bytes) {
+                        
+                            long extra_stack_bytes = needed_stack_bytes - block_stack_bytes;
+                            emit_stack_adjust (extra_stack_bytes, 1);
+                            
+                            if (!is_function_body) {
+                                current_block_cleanup_bytes += extra_stack_bytes;
+                            }
+                            
+                            block_stack_bytes = needed_stack_bytes;
+                        
+                        }
+                        
+                        emit_local_initializers (inits, init_count);
+                        init_count = 0;
+                        
+                        if (!declarator_is_pointer && (parsed_type_is_aggregate || declarator_has_array)) {
+                        
+                            emit_load_local_address_to_reg_now ("edx", object_offset);
+                            
+                            if (!emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("edx", 0, object_size)) {
+                            
+                                if (tok.kind == TOK_IDENT && token_identifier_is_function_call_rhs_now () &&
+                                    get_global_symbol_kind (tok.ident) == GLOBAL_SYMBOL_FUNCTION &&
+                                    get_global_symbol_size (tok.ident) > (DATA_LLONG & 0x1f)) {
+                                
+                                    pending_struct_return_lhs = find_local_symbol (name);
+                                    pending_struct_return_global_name = 0;
+                                    
+                                    emit_load_assignment_rhs_expression_to_reg ("eax");
+                                
+                                } else {
+                                
+                                    emit_load_assignment_rhs_expression_to_reg ("eax");
+                                    emit_store_reg_to_local (object_offset, object_init_size, "eax");
+                                
+                                }
+                            
+                            }
+                        
+                        } else if (object_init_size == (DATA_LLONG & 0x1f) && !parsed_type_is_floating && !declarator_is_pointer) {
+                        
+                            emit_load_assignment_rhs_expression_to_pair ("eax", "edx", parsed_type_is_unsigned);
+                            emit_store_pair_to_local64 (object_offset, "eax", "edx");
+                        
+                        } else {
+                        
+                            emit_load_assignment_rhs_expression_to_reg ("eax");
+                            emit_store_reg_to_local (object_offset, object_init_size, "eax");
+                        
+                        }
+                    
+                    } else if (object_is_auto && init_count < MAX_LOCAL_INITS) {
+                    
+                        inits[init_count].offset = object_offset;
+                        inits[init_count].size = object_init_size;
+                        
+                        if (declarator_is_pointer && is_string_token ()) {
+                        
+                            inits[init_count].kind = LOCAL_INIT_ADDRESS;
+                            inits[init_count].symbol = emit_string_literal_global ();
+                            inits[init_count].value.low = 0;
+                            inits[init_count].value.high = 0;
+                            inits[init_count].source_offset = 0;
+                            inits[init_count].source_size = 0;
+                            
+                            switch_section (SECTION_TEXT);
+                        
+                        } else {
+                            parse_local_initializer_value (&inits[init_count]);
+                        }
+                        
+                        init_count++;
+                    
+                    } else {
+                        skip_initializer ();
+                    }
+                
+                }
+                
+                if (name && parsed_storage_class == STORAGE_STATIC) {
+                
+                    make_local_static_label (static_label, sizeof (static_label), name);
+                    
+                    if (add_global_symbol (static_label, GLOBAL_SYMBOL_OBJECT, 0, name_start, name_caret, name_line)) {
+                    
+                        set_global_symbol_size (static_label, declarator_is_pointer ? DATA_PTR : declarator_object_size (parsed_type_size));
+                        set_global_symbol_pointer_info (static_label, declarator_pointer_depth,
+                            declarator_is_pointer ? declarator_pointed_size_now () :
+                                declarator_element_size_from_fields (parsed_type_size, object_fields, object_field_count));
+                        set_global_symbol_unsigned (static_label, (declarator_is_pointer || declarator_has_function) ? 0 : parsed_type_is_unsigned);
+                        set_global_symbol_floating (static_label, (declarator_is_pointer || declarator_has_function) ? 0 : parsed_type_is_floating);
+                        set_global_symbol_array (static_label, declarator_has_array);
+                        set_global_symbol_array_count (static_label, declarator_has_array ? declarator_array_count : 0);
+                        set_global_symbol_array_element_size (static_label, declarator_has_array ? (int)(declarator_object_size (parsed_type_size) / (declarator_array_count > 0 ? declarator_array_count : 1)) : 0);
+                        
+                        emit_block_static_object (static_label,
+                            declarator_is_pointer ? DATA_PTR : (parsed_type_is_aggregate ? parsed_type_size : (parsed_type_size & 0x1f)),
+                                declarator_has_array, declarator_array_count,
+                                    object_fields, object_field_count,
+                                        init_values, init_symbols, init_value_count,
+                                            (!declarator_is_pointer && parsed_type_is_aggregate));
+                    
+                    }
+                    
+                    add_static_local_symbol (name, static_label,
+                        declarator_object_size (parsed_type_size),
+                            type_alignment (declarator_is_pointer ? DATA_PTR : parsed_type_size),
+                                (declarator_is_pointer ? 0 : parsed_type_is_unsigned),
+                                    block_scope_start, name_line, name_start, name_caret);
+                    
+                    set_local_symbol_floating (name, declarator_is_pointer ? 0 : parsed_type_is_floating);
+                    set_local_symbol_array (name, declarator_has_array);
+                    set_local_symbol_array_element_size (name, declarator_has_array ? (int)(declarator_object_size (parsed_type_size) / (declarator_array_count > 0 ? declarator_array_count : 1)) : 0);
+                    set_local_symbol_pointer_info (name, declarator_effective_pointer_depth_now (),
+                        declarator_effective_pointed_size_now (parsed_type_size, object_fields, object_field_count));
+                
+                }
+                
+                for (i = 0; i < MAX_AGG_FIELDS; i++) {
+                
+                    if (init_symbols[i]) {
+                    
+                        free (init_symbols[i]);
+                        init_symbols[i] = 0;
+                    
+                    }
+                
+                }
+                
+                if (name) {
+                    free (name);
+                }
+                
+                if (!_accept (TOK_COMMA)) {
+                    break;
+                }
+            
+            }
+            
+            expect (TOK_SEMI, ";");
+            
+            if (block_stack_emitted) {
+                ensure_block_stack_allocated (block_stack_start, &block_stack_bytes, &block_stack_emitted);
+            }
+            
+            continue;
+        
+        }
+        
+        if (declaration_phase) {
+        
+            declaration_phase = 0;
+            
+            {
+            
+                long needed_stack_bytes = align_up_long (current_local_stack_size - block_stack_start, 4);
+                
+                if (!block_stack_emitted) {
+                
+                    block_stack_bytes = needed_stack_bytes;
+                    emit_stack_adjust (block_stack_bytes, 1);
+                    
+                    if (!is_function_body) {
+                        current_block_cleanup_bytes += block_stack_bytes;
+                    }
+                    
+                    block_stack_emitted = (block_stack_bytes > 0);
+                
+                } else if (needed_stack_bytes > block_stack_bytes) {
+                
+                    long extra_stack_bytes = needed_stack_bytes - block_stack_bytes;
+                    emit_stack_adjust (extra_stack_bytes, 1);
+                    
+                    if (!is_function_body) {
+                        current_block_cleanup_bytes += extra_stack_bytes;
+                    }
+                    
+                    block_stack_bytes = needed_stack_bytes;
+                
+                }
+            
+            }
+            
+            emit_local_initializers (inits, init_count);
+            init_count = 0;
+        
+        }
+        
+        parse_statement ();
+    
+    }
+    
+    if (declaration_phase) {
+    
+        /**
+         * The block contained declarations only.  We still need to reserve
+         * stack space for those automatic objects, otherwise a function like
+         *
+         *     void f(void) { int a; }
+         *
+         * would produce no `sub esp, ...` at all.
+         */
+        declaration_phase = 0;
+        
+        {
+        
+            long needed_stack_bytes = align_up_long (current_local_stack_size - block_stack_start, 4);
+            
+            if (!block_stack_emitted) {
+            
+                block_stack_bytes = needed_stack_bytes;
+                emit_stack_adjust (block_stack_bytes, 1);
+                block_stack_emitted = (block_stack_bytes > 0);
+            
+            } else if (needed_stack_bytes > block_stack_bytes) {
+            
+                emit_stack_adjust (needed_stack_bytes - block_stack_bytes, 1);
+                block_stack_bytes = needed_stack_bytes;
+            
+            }
+        
+        }
+        
+        emit_local_initializers (inits, init_count);
+        init_count = 0;
+    
+    }
+    
+    expect (TOK_RBRACE, "}");
+    
+    if (!is_function_body && block_stack_emitted && block_stack_bytes > 0) {
+    
+        emit_stack_adjust (block_stack_bytes, 0);
+        
+        if (current_block_cleanup_bytes >= block_stack_bytes) {
+            current_block_cleanup_bytes -= block_stack_bytes;
+        } else {
+            current_block_cleanup_bytes = 0;
+        }
+    
+    }
+    
+    current_parse_block_depth--;
+    truncate_local_symbols (block_local_start, block_stack_start);
+
+}
+
+static int is_assignment_operator (enum token_kind k) {
+
+    switch (k) {
+    
+        case TOK_ASSIGN:
+        case TOK_PLUSEQ:
+        case TOK_MINUSEQ:
+        case TOK_STAREQ:
+        case TOK_SLASHEQ:
+        case TOK_MODEQ:
+        case TOK_ANDEQ:
+        case TOK_OREQ:
+        case TOK_XOREQ:
+        case TOK_LSHEQ:
+        case TOK_RSHEQ:
+        
+            return 1;
+        
+        default:
+        
+            return 0;
+    
+    }
+
+}
+
+static void emit_load_local_to_reg (const char *reg, long offset, int size) {
+
+    char memref[64];
+    
+    if (!state->ofp || !reg) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        format_intel_ebp_offset (memref, sizeof (memref), offset);
+        
+        if (size == (DATA_CHAR & 0x1f)) {
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    movzx %s, byte %s\n" : "    movzx %s, byte ptr %s\n"), reg, memref);
+        } else if (size == (DATA_SHORT & 0x1f)) {
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    movzx %s, word %s\n" : "    movzx %s, word ptr %s\n"), reg, memref);
+        } else {
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov %s, dword %s\n" : "    mov %s, dword ptr %s\n"), reg, memref);
+        }
+    
+    } else {
+    
+        if (size == (DATA_CHAR & 0x1f)) {
+            fprintf (state->ofp, "    movzbl %ld(%%ebp), %%%s\n", offset, reg);
+        } else if (size == (DATA_SHORT & 0x1f)) {
+            fprintf (state->ofp, "    movzwl %ld(%%ebp), %%%s\n", offset, reg);
+        } else {
+            fprintf (state->ofp, "    movl %ld(%%ebp), %%%s\n", offset, reg);
+        }
+    
+    }
+
+}
+
+static void emit_load_global_to_reg (const char *reg, const char *symbol, int size) {
+
+    const char *asm_symbol;
+    
+    if (!state->ofp || !reg || !symbol) {
+        return;
+    }
+    
+    emit_extern_reference_symbol (symbol, size);
+    asm_symbol = asm_global_symbol_name (symbol);
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        if (size == (DATA_CHAR & 0x1f)) {
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    movzx %s, byte [%s]\n" : "    movzx %s, byte ptr %s\n"), reg, asm_symbol);
+        } else if (size == (DATA_SHORT & 0x1f)) {
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    movzx %s, word [%s]\n" : "    movzx %s, word ptr %s\n"), reg, asm_symbol);
+        } else {
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov %s, dword [%s]\n" : "    mov %s, dword ptr %s\n"), reg, asm_symbol);
+        }
+    
+    } else {
+    
+        if (size == (DATA_CHAR & 0x1f)) {
+            fprintf (state->ofp, "    movzbl %s, %%%s\n", asm_symbol, reg);
+        } else if (size == (DATA_SHORT & 0x1f)) {
+            fprintf (state->ofp, "    movzwl %s, %%%s\n", asm_symbol, reg);
+        } else {
+            fprintf (state->ofp, "    movl %s, %%%s\n", asm_symbol, reg);
+        }
+    
+    }
+
+}
+
+static void emit_store_reg_to_local (long offset, int size, const char *reg) {
+
+    char memref[64];
+    
+    if (!state->ofp || !reg) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        format_intel_ebp_offset (memref, sizeof (memref), offset);
+
+        if (size == (DATA_CHAR & 0x1f)) {
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov byte %s, %cl\n" : "    mov byte ptr %s, %cl\n"), memref, reg[1]);
+        } else if (size == (DATA_SHORT & 0x1f)) {
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov word %s, %cx\n" : "    mov word ptr %s, %cx\n"), memref, reg[1]);
+        } else {
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov dword %s, %s\n" : "    mov dword ptr %s, %s\n"), memref, reg);
+        }
+    
+    } else {
+    
+        if (size == (DATA_CHAR & 0x1f)) {
+            fprintf (state->ofp, "    movb %%%cl, %ld(%%ebp)\n", reg[1], offset);
+        } else if (size == (DATA_SHORT & 0x1f)) {
+            fprintf (state->ofp, "    movw %%%cx, %ld(%%ebp)\n", reg[1], offset);
+        } else {
+            fprintf (state->ofp, "    movl %%%s, %ld(%%ebp)\n", reg, offset);
+        }
+    
+    }
+
+}
+
+static void emit_store_reg_to_global (const char *symbol, int size, const char *reg) {
+
+    const char *asm_symbol;
+    
+    if (!state->ofp || !symbol || !reg) {
+        return;
+    }
+    
+    asm_symbol = asm_global_symbol_name (symbol);
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        if (size == (DATA_CHAR & 0x1f)) {
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov byte [%s], %cl\n" : "    mov byte ptr %s, %cl\n"), asm_symbol, reg[1]);
+        } else if (size == (DATA_SHORT & 0x1f)) {
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov word [%s], %cx\n" : "    mov word ptr %s, %cx\n"), asm_symbol, reg[1]);
+        } else {
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov dword [%s], %s\n" : "    mov dword ptr %s, %s\n"), asm_symbol, reg);
+        }
+    
+    } else {
+    
+        if (size == (DATA_CHAR & 0x1f)) {
+            fprintf (state->ofp, "    movb %%%cl, %s\n", reg[1], asm_symbol);
+        } else if (size == (DATA_SHORT & 0x1f)) {
+            fprintf (state->ofp, "    movw %%%cx, %s\n", reg[1], asm_symbol);
+        } else {
+            fprintf (state->ofp, "    movl %%%s, %s\n", reg, asm_symbol);
+        }
+    
+    }
+
+}
+
+static void emit_load_local64_to_pair (long offset, const char *lo, const char *hi) {
+
+    char memref_lo[64], memref_hi[64];
+    
+    if (!state->ofp || !lo || !hi) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        format_intel_ebp_offset (memref_lo, sizeof (memref_lo), offset);
+        format_intel_ebp_offset (memref_hi, sizeof (memref_hi), offset + 4);
+        
+        fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov %s, dword %s\n" : "    mov %s, dword ptr %s\n"), lo, memref_lo);
+        fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov %s, dword %s\n" : "    mov %s, dword ptr %s\n"), hi, memref_hi);
+    
+    } else {
+    
+        fprintf (state->ofp, "    movl %ld(%%ebp), %%%s\n", offset, lo);
+        fprintf (state->ofp, "    movl %ld(%%ebp), %%%s\n", offset + 4, hi);
+    
+    }
+
+}
+
+static void emit_store_pair_to_local64 (long offset, const char *lo, const char *hi) {
+
+    char memref_lo[64], memref_hi[64];
+    
+    if (!state->ofp || !lo || !hi) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        format_intel_ebp_offset (memref_lo, sizeof (memref_lo), offset);
+        format_intel_ebp_offset (memref_hi, sizeof (memref_hi), offset + 4);
+        
+        fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov dword %s, %s\n" : "    mov dword ptr %s, %s\n"), memref_lo, lo);
+        fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov dword %s, %s\n" : "    mov dword ptr %s, %s\n"), memref_hi, hi);
+    
+    } else {
+    
+        fprintf (state->ofp, "    movl %%%s, %ld(%%ebp)\n", lo, offset);
+        fprintf (state->ofp, "    movl %%%s, %ld(%%ebp)\n", hi, offset + 4);
+    
+    }
+
+}
+
+static void emit_load_global64_to_pair (const char *lo, const char *hi, const char *symbol) {
+
+    const char *asm_symbol;
+    
+    if (!state->ofp || !lo || !hi || !symbol) {
+        return;
+    }
+    
+    emit_extern_reference_symbol (symbol, DATA_LLONG & 0x1f);
+    asm_symbol = asm_global_symbol_name (symbol);
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov %s, dword [%s]\n" : "    mov %s, dword ptr %s\n"), lo, asm_symbol);
+        fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov %s, dword [%s + 4]\n" : "    mov %s, dword ptr %s + 4\n"), hi, asm_symbol);
+    
+    } else {
+    
+        fprintf (state->ofp, "    movl %s, %%%s\n", asm_symbol, lo);
+        fprintf (state->ofp, "    movl %s+4, %%%s\n", asm_symbol, hi);
+    
+    }
+
+}
+
+static void emit_store_pair_to_global64 (const char *symbol, const char *lo, const char *hi) {
+
+    const char *asm_symbol;
+    
+    if (!state->ofp || !symbol || !lo || !hi) {
+        return;
+    }
+    
+    emit_extern_reference_symbol (symbol, DATA_LLONG & 0x1f);
+    asm_symbol = asm_global_symbol_name (symbol);
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov dword [%s], %s\n" : "    mov dword ptr %s, %s\n"), asm_symbol, lo);
+        fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov dword [%s + 4], %s\n" : "    mov dword ptr %s + 4, %s\n"), asm_symbol, hi);
+    
+    } else {
+    
+        fprintf (state->ofp, "    movl %%%s, %s\n", lo, asm_symbol);
+        fprintf (state->ofp, "    movl %%%s, %s+4\n", hi, asm_symbol);
+    
+    }
+
+}
+
+static void emit_call_identifier_to_reg_now (const char *name, const char *reg, const char *name_start, const char *name_caret, unsigned long name_line);
+static void emit_push_pending_struct_return_address_now (int stack_arg_bytes);
+
+static int emit_push_aggregate_from_addr_reg_now (const char *reg, int size) {
+
+    int offset;
+    int chunk;
+    
+    if (!reg || size <= (DATA_PTR & 0x1f)) {
+        return 0;
+    }
+    
+    if (!state->ofp) {
+        return 1;
+    }
+    
+    for (offset = size; offset > 0; ) {
+    
+        if (offset >= 4) {
+        
+            chunk = 4;
+            offset -= 4;
+        
+        } else if (offset >= 2) {
+        
+            chunk = 2;
+            offset -= 2;
+        
+        } else {
+        
+            chunk = 1;
+            offset -= 1;
+        
+        }
+        
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+        
+            if (state->syntax & ASM_SYNTAX_NASM) {
+            
+                if (chunk == 4) {
+                    fprintf (state->ofp, "    push dword [%s + %d]\n", reg, offset);
+                } else if (chunk == 2) {
+                
+                    fprintf (state->ofp, "    movzx edx, word [%s + %d]\n", reg, offset);
+                    fprintf (state->ofp, "    push edx\n");
+                
+                } else {
+                
+                    fprintf (state->ofp, "    movzx edx, byte [%s + %d]\n", reg, offset);
+                    fprintf (state->ofp, "    push edx\n");
+                
+                }
+            
+            } else {
+            
+                if (chunk == 4) {
+                    fprintf (state->ofp, "    push dword ptr [%s + %d]\n", reg, offset);
+                } else if (chunk == 2) {
+                
+                    fprintf (state->ofp, "    movzx edx, word ptr [%s + %d]\n", reg, offset);
+                    fprintf (state->ofp, "    push edx\n");
+                
+                } else {
+                
+                    fprintf (state->ofp, "    movzx edx, byte ptr [%s + %d]\n", reg, offset);
+                    fprintf (state->ofp, "    push edx\n");
+                
+                }
+            
+            }
+        
+        } else {
+        
+            if (chunk == 4) {
+                fprintf (state->ofp, "    pushl %d(%%%s)\n", offset, reg);
+            } else if (chunk == 2) {
+            
+                fprintf (state->ofp, "    movzwl %d(%%%s), %%edx\n", offset, reg);
+                fprintf (state->ofp, "    pushl %%edx\n");
+            
+            } else {
+            
+                fprintf (state->ofp, "    movzbl %d(%%%s), %%edx\n", offset, reg);
+                fprintf (state->ofp, "    pushl %%edx\n");
+            
+            }
+        
+        }
+    
+    }
+    
+    return 1;
+
+}
+
+static void emit_call_pointer_in_reg_now (const char *fn_reg, const char *result_reg);
+
+static int is_arithmetic_binary_operator (enum token_kind k) {
+
+    return  k == TOK_PLUS || k == TOK_MINUS || k == TOK_STAR || k == TOK_BSLASH ||
+                k == TOK_MOD || k == TOK_AMPER || k == TOK_PIPE || k == TOK_CARET ||
+                    k == TOK_LSH || k == TOK_RSH;
+
+}
+
+static void emit_push_reg_now (const char *reg) {
+
+    if (!state->ofp || !reg) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+        fprintf (state->ofp, "    push %s\n", reg);
+    } else {
+        fprintf (state->ofp, "    pushl %%%s\n", reg);
+    }
+
+}
+
+static void emit_pop_reg_now (const char *reg) {
+
+    if (!state->ofp || !reg) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+        fprintf (state->ofp, "    pop %s\n", reg);
+    } else {
+        fprintf (state->ofp, "    popl %%%s\n", reg);
+    }
+
+}
+
+static void emit_mov_reg_to_reg_now (const char *dst, const char *src) {
+
+    if (!state->ofp || !dst || !src || strcmp (dst, src) == 0) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+        fprintf (state->ofp, "    mov %s, %s\n", dst, src);
+    } else {
+        fprintf (state->ofp, "    movl %%%s, %%%s\n", src, dst);
+    }
+
+}
+
+static void emit_load_indexed_pointer_to_reg_now (const char *base_reg, const char *index_reg) {
+
+    if (!state->ofp || !base_reg || !index_reg) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        if (state->syntax & ASM_SYNTAX_NASM) {
+            fprintf (state->ofp, "    mov %s, dword [%s + %s * 4]\n", base_reg, base_reg, index_reg);
+        } else {
+            fprintf (state->ofp, "    mov %s, dword ptr [%s + %s * 4]\n", base_reg, base_reg, index_reg);
+        }
+    
+    } else {
+        fprintf (state->ofp, "    movl (%%%s,%%%s,4), %%%s\n", base_reg, index_reg, base_reg);
+    }
+
+}
+
+static void emit_load_indexed_char_to_reg_now (const char *base_reg, const char *index_reg, const char *dst_reg) {
+
+    if (!state->ofp || !base_reg || !index_reg || !dst_reg) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        if (state->syntax & ASM_SYNTAX_NASM) {
+            fprintf (state->ofp, "    movzx %s, byte [%s + %s]\n", dst_reg, base_reg, index_reg);
+        } else {
+            fprintf (state->ofp, "    movzx %s, byte ptr [%s + %s]\n", dst_reg, base_reg, index_reg);
+        }
+    
+    } else {
+        fprintf (state->ofp, "    movzbl (%%%s,%%%s), %%%s\n", base_reg, index_reg, dst_reg);
+    }
+
+}
+
+static void emit_load_indexed_sized_to_reg_now (const char *base_reg, const char *index_reg, const char *dst_reg, int elem_size) {
+
+    int scale = 1;
+    
+    const char *atype = "byte";
+    const char *gasop = "movzbl";
+    
+    if (!state->ofp || !base_reg || !index_reg || !dst_reg) {
+        return;
+    }
+    
+    elem_size &= 0x1f;
+    
+    if (elem_size == (DATA_SHORT & 0x1f)) {
+    
+        scale = 2;
+        
+        atype = "word";
+        gasop = "movzwl";
+    
+    } else if (elem_size == (DATA_INT & 0x1f) || elem_size == (DATA_LONG & 0x1f) || elem_size == DATA_PTR) {
+    
+        scale = 4;
+        
+        atype = "dword";
+        gasop = "movl";
+    
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        if (elem_size == (DATA_CHAR & 0x1f)) {
+            emit_load_indexed_char_to_reg_now (base_reg, index_reg, dst_reg);
+        } else if (scale == 1) {
+        
+            if (elem_size == (DATA_SHORT & 0x1f)) {
+
+                if (state->syntax & ASM_SYNTAX_NASM) {
+                    fprintf (state->ofp, "    movzx %s, word [%s + %s]\n", dst_reg, base_reg, index_reg);
+                } else {
+                    fprintf (state->ofp, "    movzx %s, word ptr [%s + %s]\n", dst_reg, base_reg, index_reg);
+                }
+
+            } else if (state->syntax & ASM_SYNTAX_NASM) {
+                fprintf (state->ofp, "    mov %s, %s [%s + %s]\n", dst_reg, atype, base_reg, index_reg);
+            } else {
+                fprintf (state->ofp, "    mov %s, %s ptr [%s + %s]\n", dst_reg, atype, base_reg, index_reg);
+            }
+        
+        } else {
+        
+            if (elem_size == (DATA_SHORT & 0x1f)) {
+
+                if (state->syntax & ASM_SYNTAX_NASM) {
+                    fprintf (state->ofp, "    movzx %s, word [%s + %s * %d]\n", dst_reg, base_reg, index_reg, scale);
+                } else {
+                    fprintf (state->ofp, "    movzx %s, word ptr [%s + %s * %d]\n", dst_reg, base_reg, index_reg, scale);
+                }
+
+            } else if (state->syntax & ASM_SYNTAX_NASM) {
+                fprintf (state->ofp, "    mov %s, %s [%s + %s * %d]\n", dst_reg, atype, base_reg, index_reg, scale);
+            } else {
+                fprintf (state->ofp, "    mov %s, %s ptr [%s + %s * %d]\n", dst_reg, atype, base_reg, index_reg, scale);
+            }
+        
+        }
+    
+    } else {
+    
+        if (scale == 1) {
+            fprintf (state->ofp, "    %s (%%%s,%%%s), %%%s\n", gasop, base_reg, index_reg, dst_reg);
+        } else {
+            fprintf (state->ofp, "    %s (%%%s,%%%s,%d), %%%s\n", gasop, base_reg, index_reg, scale, dst_reg);
+        }
+    
+    }
+
+}
+
+static void emit_load_symbol_address_to_reg_now (const char *reg, const char *symbol, long offset, int is_local) {
+
+    const char *asm_symbol;
+    
+    if (!state->ofp || !reg) {
+        return;
+    }
+    
+    if (is_local) {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+            fprintf (state->ofp, "    lea %s, [ebp%+ld]\n", reg, offset);
+        } else {
+            fprintf (state->ofp, "    leal %ld(%%ebp), %%%s\n", offset, reg);
+        }
+        
+        return;
+    
+    }
+    
+    if (!symbol) {
+        return;
+    }
+    
+    emit_extern_reference_symbol (symbol, DATA_PTR);
+    asm_symbol = asm_global_symbol_name (symbol);
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        if (state->syntax & ASM_SYNTAX_NASM) {
+            fprintf (state->ofp, "    mov %s, %s\n", reg, asm_symbol);
+        } else {
+            fprintf (state->ofp, "    mov %s, offset %s\n", reg, asm_symbol);
+        }
+    
+    } else {
+        fprintf (state->ofp, "    movl $%s, %%%s\n", asm_symbol, reg);
+    }
+
+}
+
+static void emit_add_indexed_scaled_address_to_reg_now (const char *base_reg, const char *index_reg, int elem_size) {
+
+    int raw_elem_size = elem_size;
+    int scale = 1;
+    
+    if (!state->ofp || !base_reg || !index_reg) {
+        return;
+    }
+    
+    if (raw_elem_size == DATA_SHORT || raw_elem_size == (DATA_SHORT & 0x1f)) {
+    
+        elem_size = DATA_SHORT & 0x1f;
+        scale = 2;
+    
+    } else if (raw_elem_size == DATA_INT || raw_elem_size == DATA_LONG || raw_elem_size == DATA_PTR || raw_elem_size == (DATA_INT & 0x1f)) {
+    
+        elem_size = DATA_INT & 0x1f;
+        scale = 4;
+    
+    } else if (elem_size > 1) {
+        scale = 0;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        if (scale == 0) {
+        
+            fprintf (state->ofp, "    imul %s, %d\n", index_reg, elem_size);
+            fprintf (state->ofp, "    lea %s, [%s + %s]\n", base_reg, base_reg, index_reg);
+        
+        } else if (scale == 1) {
+            fprintf (state->ofp, "    lea %s, [%s + %s]\n", base_reg, base_reg, index_reg);
+        } else {
+            fprintf (state->ofp, "    lea %s, [%s + %s * %d]\n", base_reg, base_reg, index_reg, scale);
+        }
+    
+    } else {
+    
+        if (scale == 0) {
+        
+            fprintf (state->ofp, "    imull $%d, %%%s, %%%s\n", elem_size, index_reg, index_reg);
+            fprintf (state->ofp, "    leal (%%%s,%%%s), %%%s\n", base_reg, index_reg, base_reg);
+        
+        } else if (scale == 1) {
+            fprintf (state->ofp, "    leal (%%%s,%%%s), %%%s\n", base_reg, index_reg, base_reg);
+        } else {
+            fprintf (state->ofp, "    leal (%%%s,%%%s,%d), %%%s\n", base_reg, index_reg, scale, base_reg);
+        }
+    
+    }
+
+}
+
+static int emit_parse_postfix_subscript_scaled_address_to_reg_now (const char *reg, int elem_size) {
+
+    const char *index_reg;
+    int saw_subscript = 0;
+    
+    if (!reg) {
+        reg = "eax";
+    }
+    
+    index_reg = (strcmp (reg, "ecx") == 0) ? "edx" : "ecx";
+    
+    while (tok.kind == TOK_LBRACK) {
+    
+        saw_subscript = 1;
+        get_token ();
+        
+        emit_push_reg_now (reg);
+        emit_load_assignment_rhs_expression_to_reg (index_reg);
+        expect (TOK_RBRACK, "]");
+        emit_pop_reg_now (reg);
+        
+        if (tok.kind == TOK_LBRACK) {
+            emit_load_indexed_pointer_to_reg_now (reg, index_reg);
+        } else {
+            emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size);
+        }
+    
+    }
+    
+    return saw_subscript;
+
+}
+
+static int emit_parse_postfix_subscripts_to_reg_now (const char *reg, int elem_size, int pointer_depth, int pointed_size) {
+
+    const char *index_reg;
+    int saw_subscript = 0;
+    
+    if (!reg) {
+        reg = "eax";
+    }
+    
+    index_reg = (strcmp (reg, "ecx") == 0) ? "edx" : "ecx";
+    
+    while (tok.kind == TOK_LBRACK) {
+    
+        saw_subscript = 1;
+        get_token ();
+        
+        emit_push_reg_now (reg);
+        emit_load_assignment_rhs_expression_to_reg (index_reg);
+        
+        expect (TOK_RBRACK, "]");
+        emit_pop_reg_now (reg);
+        
+        if (tok.kind == TOK_LBRACK) {
+        
+            emit_load_indexed_pointer_to_reg_now (reg, index_reg);
+            
+            if (pointer_depth > 0) {
+                pointer_depth--;
+            }
+            
+            if (pointed_size > 0) {
+                elem_size = (pointer_depth > 1) ? (DATA_PTR & 0x1f) : index_step_size (pointed_size);
+            }
+        
+        } else {
+        
+            if ((elem_size & 0x1f) > DATA_PTR) {
+                emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size);
+            } else {
+                emit_load_indexed_sized_to_reg_now (reg, index_reg, reg, elem_size);
+            }
+        
+        }
+    
+    }
+
+    return saw_subscript;
+
+}
+
+static int parse_incdec_identifier_now (enum token_kind *op, char **name, const char **name_start, const char **name_caret, unsigned long *name_line) {
+
+    if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) {
+        return 0;
+    }
+    
+    *op = tok.kind;
+    get_token ();
+    
+    if (tok.kind != TOK_IDENT) {
+    
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after %s", *op == TOK_INCR ? "++" : "--");
+        
+        *name = 0;
+        return 1;
+    }
+    
+    *name = xstrdup (tok.ident);
+    *name_start = tok.start;
+    *name_caret = tok.caret;
+    *name_line = get_line_number ();
+    
+    get_token ();
+    return 1;
+
+}
+
+static void emit_apply_postfix_member_access_to_reg_now (const char *reg);
+static void emit_apply_postfix_member_incdec_now (const char *reg, enum token_kind op);
+static void emit_load_member_from_addr_reg_now (const char *dst_reg, const char *addr_reg, int offset, int size);
+
+static struct token *clone_current_token_now (void);
+static void emit_store_reg_to_deref_reg_now (const char *addr_reg, const char *value_reg, int size);
+
+static int source_starts_prefix_incdec_parenthesized_deref_at (const char *p) {
+
+    if (!p) {
+        return 0;
+    }
+    
+    if (!((p[0] == '+' && p[1] == '+') || (p[0] == '-' && p[1] == '-'))) {
+        return 0;
+    }
+    
+    p += 2;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p != '(') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p != '*') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) {
+        return 0;
+    }
+    
+    p++;
+    
+    while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+        p++;
+    }
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    return *p == ')';
+
+}
+
+static int emit_load_prefix_incdec_parenthesized_deref_to_reg_now (const char *reg) {
+
+    enum token_kind op;
+    char *name;
+    
+    const char *name_start;
+    const char *name_caret;
+    
+    unsigned long name_line;
+    struct local_symbol *sym;
+    
+    int global_index;
+    int pointer_depth = 0;
+    int pointed_size = DATA_INT & 0x1f;
+    int lvalue_size = DATA_INT & 0x1f;
+    int step = 1;
+    
+    const char *addr_reg;
+    
+    if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) {
+        return 0;
+    }
+    
+    if (!source_starts_prefix_incdec_parenthesized_deref_at (tok.caret)) {
+        return 0;
+    }
+    
+    op = tok.kind;
+    get_token ();
+    
+    expect (TOK_LPAREN, "(");
+    expect (TOK_STAR, "*");
+    
+    name_start = tok.start;
+    name_caret = tok.caret;
+    name_line = get_line_number ();
+    
+    if (tok.kind != TOK_IDENT || !tok.ident) {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *");
+        return 1;
+    
+    }
+    
+    name = xstrdup (tok.ident);
+    get_token ();
+    
+    expect (TOK_RPAREN, ")");
+    
+    sym = find_local_symbol (name);
+    global_index = find_global_symbol (name);
+    
+    if (sym) {
+    
+        pointer_depth = sym->pointer_depth;
+        pointed_size = sym->pointed_size;
+    
+    } else if (global_index >= 0) {
+    
+        pointer_depth = get_global_symbol_pointer_depth (name);
+        pointed_size = get_global_symbol_pointed_size (name);
+    
+    } else {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    if (pointer_depth <= 0) {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "invalid indirection of non-pointer '%s'", name);
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    if (pointed_size <= 0) {
+        pointed_size = DATA_INT & 0x1f;
+    }
+    
+    if (pointer_depth > 1) {
+    
+        lvalue_size = DATA_PTR & 0x1f;
+        step = pointer_depth > 2 ? (DATA_PTR & 0x1f) : index_step_size (pointed_size);
+    
+    } else {
+    
+        lvalue_size = pointed_size & 0x1f;
+        step = 1;
+    
+    }
+    
+    if (!reg) {
+        reg = "eax";
+    }
+    
+    addr_reg = (strcmp (reg, "edx") == 0) ? "ecx" : "edx";
+    
+    if (sym) {
+    
+        if (sym->is_static && sym->static_label) {
+            emit_load_global_to_reg (addr_reg, sym->static_label, DATA_PTR);
+        } else {
+            emit_load_local_to_reg (addr_reg, sym->offset, DATA_PTR);
+        }
+    
+    } else {
+        emit_load_global_to_reg (addr_reg, name, DATA_PTR);
+    }
+    
+    emit_load_member_from_addr_reg_now (reg, addr_reg, 0, lvalue_size);
+    
+    if (state->ofp) {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+            fprintf (state->ofp, op == TOK_INCR ? "    add %s, %d\n" : "    sub %s, %d\n", reg, step);
+        } else {
+            fprintf (state->ofp, op == TOK_INCR ? "    addl $%d, %%%s\n" : "    subl $%d, %%%s\n", step, reg);
+        }
+    
+    }
+    
+    emit_store_reg_to_deref_reg_now (addr_reg, reg, lvalue_size);
+    
+    if (pointer_depth > 1) {
+        set_rhs_last_pointer_info (pointer_depth - 1, pointed_size);
+    } else {
+        clear_rhs_last_pointer_info ();
+    }
+    
+    free (name);
+    return 1;
+
+}
+
+static int emit_load_prefix_incdec_deref_to_reg_now (const char *reg) {
+
+    enum token_kind op;
+    
+    struct token *saved_tok;
+    char *name;
+    
+    const char *name_start;
+    const char *name_caret;
+    
+    unsigned long name_line;
+    
+    struct local_symbol *sym;
+    
+    int global_index;
+    int deref_size = DATA_INT & 0x1f;
+    
+    const char *addr_reg;
+    
+    if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) {
+        return 0;
+    }
+    
+    saved_tok = clone_current_token_now ();
+    op = tok.kind;
+    
+    get_token ();
+    
+    if (tok.kind != TOK_STAR) {
+    
+        unget_token (saved_tok);
+        return 0;
+    
+    }
+    
+    get_token ();
+    
+    if (tok.kind != TOK_IDENT) {
+    
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after *");
+        
+        free (saved_tok->ident);
+        free ((char *) saved_tok->start);
+        free (saved_tok);
+        
+        return 1;
+    
+    }
+    
+    name = xstrdup (tok.ident);
+    name_start = tok.start;
+    name_caret = tok.caret;
+    name_line = get_line_number ();
+    
+    get_token ();
+    
+    sym = find_local_symbol (name);
+    global_index = find_global_symbol (name);
+    
+    if (sym) {
+    
+        if (sym->pointer_depth > 1) {
+            deref_size = DATA_PTR & 0x1f;
+        } else if (sym->pointer_depth == 1 && sym->pointed_size > 0) {
+            deref_size = sym->pointed_size & 0x1f;
+        }
+    
+    } else if (global_index >= 0) {
+    
+        if (get_global_symbol_pointer_depth (name) > 1) {
+            deref_size = DATA_PTR & 0x1f;
+        } else if (get_global_symbol_pointer_depth (name) == 1 && get_global_symbol_pointed_size (name) > 0) {
+            deref_size = get_global_symbol_pointed_size (name) & 0x1f;
+        }
+    
+    } else {
+
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+        
+        free (name);
+        free (saved_tok->ident);
+        
+        free ((char *) saved_tok->start);
+        free (saved_tok);
+        
+        return 1;
+    
+    }
+    
+    if (!reg) {
+        reg = "eax";
+    }
+    
+    addr_reg = (strcmp (reg, "edx") == 0) ? "ecx" : "edx";
+    
+    if (sym) {
+    
+        if (sym->is_static && sym->static_label) {
+            emit_load_global_to_reg (addr_reg, sym->static_label, DATA_PTR);
+        } else {
+            emit_load_local_to_reg (addr_reg, sym->offset, DATA_PTR);
+        }
+    
+    } else {
+        emit_load_global_to_reg (addr_reg, name, DATA_PTR);
+    }
+    
+    emit_load_member_from_addr_reg_now (reg, addr_reg, 0, deref_size);
+    
+    if (state->ofp) {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+            fprintf (state->ofp, op == TOK_INCR ? "    add %s, 1\n" : "    sub %s, 1\n", reg);
+        } else {
+            fprintf (state->ofp, op == TOK_INCR ? "    addl $1, %%%s\n" : "    subl $1, %%%s\n", reg);
+        }
+    
+    }
+    
+    emit_store_reg_to_deref_reg_now (addr_reg, reg, deref_size);
+    
+    free (name);
+    
+    free (saved_tok->ident);
+    free ((char *) saved_tok->start);
+    
+    free (saved_tok);
+    return 1;
+
+}
+
+static int emit_load_prefix_incdec_to_reg_now (const char *reg) {
+
+    enum token_kind op;
+    char *name;
+    
+    const char *name_start, *name_caret;
+    unsigned long name_line;
+    
+    struct local_symbol *sym;
+    
+    if (emit_load_prefix_incdec_parenthesized_deref_to_reg_now (reg)) {
+        return 1;
+    }
+    
+    if (emit_load_prefix_incdec_deref_to_reg_now (reg)) {
+        return 1;
+    }
+    
+    if (!parse_incdec_identifier_now (&op, &name, &name_start, &name_caret, &name_line)) {
+        return 0;
+    }
+    
+    if (!name) {
+        return 1;
+    }
+    
+    sym = find_local_symbol (name);
+    emit_incdec_symbol_now (sym, name, op, name_line, name_start, name_caret);
+    
+    if (sym) {
+    
+        if (sym->is_static && sym->static_label) {
+            emit_load_global_to_reg (reg, sym->static_label, sym->size);
+        } else {
+            emit_load_local_to_reg (reg, sym->offset, sym->size);
+        }
+    
+    } else if (find_global_symbol (name) >= 0) {
+        emit_load_global_to_reg (reg, name, get_global_symbol_size (name));
+    }
+    
+    free (name);
+    return 1;
+
+}
+
+static int emit_load_prefix_incdec_member_to_reg_now (const char *reg) {
+
+    struct token *saved_tok;
+    
+    enum token_kind op;
+    enum token_kind member_op = TOK_EOF;
+    
+    char *member;
+    
+    const char *member_start;
+    const char *member_caret;
+    
+    unsigned long member_line;
+    
+    int offset = 0;
+    int size = DATA_INT & 0x1f;
+    
+    if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) {
+        return 0;
+    }
+    
+    saved_tok = clone_current_token_now ();
+    get_token ();
+    
+    if (tok.kind == TOK_STAR) {
+    
+        unget_token (saved_tok);
+        return 0;
+    
+    }
+    
+    unget_token (saved_tok);
+    
+    if (emit_load_prefix_incdec_parenthesized_deref_to_reg_now (reg)) {
+        return 1;
+    }
+    
+    op = tok.kind;
+    get_token ();
+    
+    if (tok.kind != TOK_LPAREN) {
+    
+        char *name;
+        
+        const char *name_start;
+        const char *name_caret;
+        
+        unsigned long name_line;
+        struct local_symbol *sym;
+        
+        int global_index;
+        
+        if (tok.kind != TOK_IDENT) {
+        
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after %s", op == TOK_INCR ? "++" : "--");
+            return 1;
+        
+        }
+        
+        name = xstrdup (tok.ident);
+        name_start = tok.start;
+        name_caret = tok.caret;
+        name_line = get_line_number ();
+        
+        get_token ();
+        
+        sym = find_local_symbol (name);
+        global_index = find_global_symbol (name);
+        
+        if (tok.kind == TOK_ARROW) {
+        
+            member_op = tok.kind;
+            get_token ();
+            
+            member_start = tok.start;
+            member_caret = tok.caret;
+            member_line = get_line_number ();
+            
+            if (tok.kind != TOK_IDENT) {
+            
+                report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+                
+                free (name);
+                return 1;
+            
+            }
+            
+            member = xstrdup (tok.ident);
+            get_token ();
+            
+            if (!find_member_info (member, &offset, &size)) {
+            
+                report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+                
+                free (member);
+                free (name);
+                
+                return 1;
+            
+            }
+            
+            free (member);
+            
+            if (sym) {
+            
+                if (sym->is_static && sym->static_label) {
+                    emit_load_global_to_reg (reg, sym->static_label, DATA_PTR);
+                } else {
+                    emit_load_local_to_reg (reg, sym->offset, DATA_PTR);
+                }
+            
+            } else if (global_index >= 0) {
+                emit_load_global_to_reg (reg, name, DATA_PTR);
+            } else {
+                report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+            }
+            
+            free (name);
+            goto emit_member_incdec;
+        
+        }
+        
+        emit_incdec_symbol_now (sym, name, op, name_line, name_start, name_caret);
+        
+        if (sym) {
+        
+            if (sym->is_static && sym->static_label) {
+                emit_load_global_to_reg (reg, sym->static_label, sym->size);
+            } else {
+                emit_load_local_to_reg (reg, sym->offset, sym->size);
+            }
+        
+        } else if (find_global_symbol (name) >= 0) {
+            emit_load_global_to_reg (reg, name, get_global_symbol_size (name));
+        }
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    get_token ();
+    
+    emit_load_assignment_rhs_expression_to_reg (reg);
+    expect (TOK_RPAREN, ")");
+    
+    if (postfix_member_seen && tok.kind != TOK_ARROW && tok.kind != TOK_DOT) {
+    
+        emit_apply_postfix_member_incdec_now (reg, op);
+        emit_load_member_from_addr_reg_now (reg, "edx", postfix_member_offset, postfix_member_size);
+        
+        return 1;
+    
+    }
+    
+    if (tok.kind != TOK_ARROW && tok.kind != TOK_DOT) {
+    
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected member after %s", op == TOK_INCR ? "++" : "--");
+        return 1;
+    
+    }
+    
+    member_op = tok.kind;
+    get_token ();
+    
+    member_start = tok.start;
+    member_caret = tok.caret;
+    member_line = get_line_number ();
+    
+    if (tok.kind != TOK_IDENT) {
+    
+        report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+        return 1;
+    
+    }
+    
+    member = xstrdup (tok.ident);
+    get_token ();
+    
+    if (!find_member_info (member, &offset, &size)) {
+    
+        report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+        
+        free (member);
+        return 1;
+    
+    }
+    
+    free (member);
+    
+emit_member_incdec:
+    
+    if (!state->ofp) {
+        return 1;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        const char *insn = op == TOK_INCR ? "inc" : "dec";
+        const char *opsize = "dword";
+        
+        if (size == (DATA_CHAR & 0x1f)) {
+            opsize = "byte";
+        } else if (size == (DATA_SHORT & 0x1f)) {
+            opsize = "word";
+        }
+        
+        if (state->syntax & ASM_SYNTAX_NASM) {
+        
+            fprintf (state->ofp, "    %s %s [%s + %d]\n", insn, opsize, reg, offset);
+            
+            if (size == (DATA_CHAR & 0x1f)) {
+                fprintf (state->ofp, "    movzx %s, byte [%s + %d]\n", reg, reg, offset);
+            } else if (size == (DATA_SHORT & 0x1f)) {
+                fprintf (state->ofp, "    movzx %s, word [%s + %d]\n", reg, reg, offset);
+            } else {
+                fprintf (state->ofp, "    mov %s, dword [%s + %d]\n", reg, reg, offset);
+            }
+        
+        } else {
+        
+            fprintf (state->ofp, "    %s %s ptr [%s + %d]\n", insn, opsize, reg, offset);
+            
+            if (size == (DATA_CHAR & 0x1f)) {
+                fprintf (state->ofp, "    movzx %s, byte ptr [%s + %d]\n", reg, reg, offset);
+            } else if (size == (DATA_SHORT & 0x1f)) {
+                fprintf (state->ofp, "    movzx %s, word ptr [%s + %d]\n", reg, reg, offset);
+            } else {
+                fprintf (state->ofp, "    mov %s, dword ptr [%s + %d]\n", reg, reg, offset);
+            }
+        
+        }
+    
+    } else {
+    
+        const char *suffix = "l";
+        
+        if (size == (DATA_CHAR & 0x1f)) {
+            suffix = "b";
+        } else if (size == (DATA_SHORT & 0x1f)) {
+            suffix = "w";
+        }
+        
+        fprintf (state->ofp, "    %s%s %d(%%%s)\n", op == TOK_INCR ? "inc" : "dec", suffix, offset, reg);
+        
+        if (size == (DATA_CHAR & 0x1f)) {
+            fprintf (state->ofp, "    movzbl %d(%%%s), %%%s\n", offset, reg, reg);
+        } else if (size == (DATA_SHORT & 0x1f)) {
+            fprintf (state->ofp, "    movzwl %d(%%%s), %%%s\n", offset, reg, reg);
+        } else {
+            fprintf (state->ofp, "    movl %d(%%%s), %%%s\n", offset, reg, reg);
+        }
+    
+    }
+    
+    return 1;
+
+}
+
+static int emit_load_prefix_incdec_to_pair_now (const char *lo, const char *hi) {
+
+    enum token_kind op;
+    char *name;
+    
+    const char *name_start, *name_caret;
+    unsigned long name_line;
+    
+    struct local_symbol *sym;
+    
+    if (!parse_incdec_identifier_now (&op, &name, &name_start, &name_caret, &name_line)) {
+        return 0;
+    }
+    
+    if (!name) {
+        return 1;
+    }
+    
+    sym = find_local_symbol (name);
+    emit_incdec_symbol_now (sym, name, op, name_line, name_start, name_caret);
+    
+    if (sym) {
+    
+        if (sym->size == (DATA_LLONG & 0x1f) && !sym->is_floating) {
+        
+            if (sym->is_static && sym->static_label) {
+                emit_load_global64_to_pair (lo, hi, sym->static_label);
+            } else {
+                emit_load_local64_to_pair (sym->offset, lo, hi);
+            }
+        
+        } else {
+        
+            if (sym->is_static && sym->static_label) {
+                emit_load_global_to_reg (lo, sym->static_label, sym->size);
+            } else {
+                emit_load_local_to_reg (lo, sym->offset, sym->size);
+            }
+            
+            if (state->ofp) {
+            
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                    fprintf (state->ofp, "    xor %s, %s\n", hi, hi);
+                } else {
+                    fprintf (state->ofp, "    xorl %%%s, %%%s\n", hi, hi);
+                }
+            
+            }
+        
+        }
+    
+    } else if (find_global_symbol (name) >= 0) {
+    
+        if (get_global_symbol_size (name) == (DATA_LLONG & 0x1f) && !get_global_symbol_floating (name)) {
+            emit_load_global64_to_pair (lo, hi, name);
+        } else {
+        
+            emit_load_global_to_reg (lo, name, get_global_symbol_size (name));
+            
+            if (state->ofp) {
+            
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                    fprintf (state->ofp, "    xor %s, %s\n", hi, hi);
+                } else {
+                    fprintf (state->ofp, "    xorl %%%s, %%%s\n", hi, hi);
+                }
+            
+            }
+        
+        }
+    
+    }
+    
+    free (name);
+    return 1;
+
+}
+
+static int expression_text_mentions_64bit_symbol (const char *p) {
+
+    char name[256];
+    int depth = 0;
+    int i;
+    
+    if (!p) {
+        return 0;
+    }
+    
+    while (*p) {
+    
+        if (*p == '(') {
+        
+            depth++;
+            p++;
+            
+            continue;
+        
+        }
+        
+        if (*p == ')') {
+        
+            if (depth <= 0) {
+                return 0;
+            }
+            
+            depth--;
+            p++;
+            
+            continue;
+        
+        }
+        
+        if (*p == ';' || *p == '{' || *p == '}') {
+            return 0;
+        }
+        
+        if ((unsigned char) *p == '_' || ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z'))) {
+        
+            i = 0;
+            
+            while ((unsigned char) *p == '_' || ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z')) || (*p >= '0' && *p <= '9')) {
+            
+                if (i + 1 < (int) sizeof (name)) {
+                    name[i++] = *p;
+                }
+                
+                p++;
+            
+            }
+            
+            name[i] = 0;
+            
+            while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+                p++;
+            }
+            
+            if (*p == '.' || (p[0] == '-' && p[1] == '>')) {
+                continue;
+            }
+            
+            {
+            
+                struct local_symbol *sym = find_local_symbol (name);
+                
+                if (sym && sym->size == (DATA_LLONG & 0x1f) && !sym->is_floating) {
+                    return 1;
+                }
+            
+            }
+            
+            if (find_global_symbol (name) >= 0 && get_global_symbol_size (name) == (DATA_LLONG & 0x1f) && !get_global_symbol_floating (name)) {
+                return 1;
+            }
+            
+            continue;
+        
+        }
+        
+        p++;
+    
+    }
+    
+    return 0;
+
+}
+
+static int current_expression_mentions_64bit_symbol_now (void) {
+
+    if (expression_text_mentions_64bit_symbol (tok.start)) {
+        return 1;
+    }
+    
+    if (expression_text_mentions_64bit_symbol (tok.caret)) {
+        return 1;
+    }
+    
+    return 0;
+
+}
+
+static const char *reg8_name_for_32 (const char *reg) {
+
+    if (strcmp (reg, "eax") == 0) {
+        return "al";
+    }
+    
+    if (strcmp (reg, "ebx") == 0) {
+        return "bl";
+    }
+    
+    if (strcmp (reg, "ecx") == 0) {
+        return "cl";
+    }
+    
+    if (strcmp (reg, "edx") == 0) {
+        return "dl";
+    }
+    
+    return "al";
+
+}
+
+static const char *reg16_name_for_32 (const char *reg) {
+
+    if (strcmp (reg, "eax") == 0) {
+        return "ax";
+    }
+    
+    if (strcmp (reg, "ebx") == 0) {
+        return "bx";
+    }
+    
+    if (strcmp (reg, "ecx") == 0) {
+        return "cx";
+    }
+    
+    if (strcmp (reg, "edx") == 0) {
+        return "dx";
+    }
+    
+    return "ax";
+
+}
+
+static void emit_extend_pair_high_from_low (const char *lo, const char *hi, int size, int is_unsigned) {
+
+    if (!state->ofp) {
+        return;
+    }
+
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+
+        if (is_unsigned) {
+        
+            if (size == (DATA_CHAR & 0x1f)) {
+                fprintf (state->ofp, "    movzx %s, %s\n", lo, reg8_name_for_32 (lo));
+            } else if (size == (DATA_SHORT & 0x1f)) {
+                fprintf (state->ofp, "    movzx %s, %s\n", lo, reg16_name_for_32 (lo));
+            }
+            
+            fprintf (state->ofp, "    xor %s, %s\n", hi, hi);
+        
+        } else {
+
+            if (size == (DATA_CHAR & 0x1f)) {
+                fprintf (state->ofp, "    movsx %s, %s\n", lo, reg8_name_for_32 (lo));
+            } else if (size == (DATA_SHORT & 0x1f)) {
+                fprintf (state->ofp, "    movsx %s, %s\n", lo, reg16_name_for_32 (lo));
+            }
+
+            fprintf (state->ofp, "    mov %s, %s\n", hi, lo);
+            fprintf (state->ofp, "    sar %s, 31\n", hi);
+
+        }
+
+    } else {
+
+        if (is_unsigned) {
+        
+            if (size == (DATA_CHAR & 0x1f)) {
+                fprintf (state->ofp, "    movzbl %%%s, %%%s\n", reg8_name_for_32 (lo), lo);
+            } else if (size == (DATA_SHORT & 0x1f)) {
+                fprintf (state->ofp, "    movzwl %%%s, %%%s\n", reg16_name_for_32 (lo), lo);
+            }
+            
+            fprintf (state->ofp, "    xorl %%%s, %%%s\n", hi, hi);
+        
+        } else {
+
+            if (size == (DATA_CHAR & 0x1f)) {
+                fprintf (state->ofp, "    movsbl %%%s, %%%s\n", reg8_name_for_32 (lo), lo);
+            } else if (size == (DATA_SHORT & 0x1f)) {
+                fprintf (state->ofp, "    movswl %%%s, %%%s\n", reg16_name_for_32 (lo), lo);
+            }
+
+            fprintf (state->ofp, "    movl %%%s, %%%s\n", lo, hi);
+            fprintf (state->ofp, "    sarl $31, %%%s\n", hi);
+
+        }
+
+    }
+
+}
+
+static void emit_apply_integer_cast_to_reg_now (const char *reg, int size, int is_unsigned) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (size != (DATA_CHAR & 0x1f) && size != (DATA_SHORT & 0x1f)) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        if (size == (DATA_CHAR & 0x1f)) {
+            fprintf (state->ofp, is_unsigned ? "    movzx %s, %s\n" : "    movsx %s, %s\n", reg, reg8_name_for_32 (reg));
+        } else {
+            fprintf (state->ofp, is_unsigned ? "    movzx %s, %s\n" : "    movsx %s, %s\n", reg, reg16_name_for_32 (reg));
+        }
+    
+    } else {
+    
+        if (size == (DATA_CHAR & 0x1f)) {
+            fprintf (state->ofp, is_unsigned ? "    movzbl %%%s, %%%s\n" : "    movsbl %%%s, %%%s\n", reg8_name_for_32 (reg), reg);
+        } else {
+            fprintf (state->ofp, is_unsigned ? "    movzwl %%%s, %%%s\n" : "    movswl %%%s, %%%s\n", reg16_name_for_32 (reg), reg);
+        }
+    
+    }
+
+}
+
+static int const_integer_expr_text_is_foldable_now (const char *p) {
+
+    int depth = 0;
+    int saw_token = 0;
+    int ch;
+    char word[64];
+    int i;
+    
+    if (!p) {
+        return 0;
+    }
+    
+    while (*p) {
+    
+        ch = (unsigned char) *p;
+        
+        if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
+        
+            p++;
+            continue;
+        
+        }
+        
+        if (depth == 0) {
+        
+            if (*p == ';' || *p == ',' || *p == '}' || *p == ']') {
+                break;
+            }
+            
+            /*
+             * This helper is only for folding a complete integer expression
+             * operand.  Do not claim that a leading parenthesized constant is
+             * foldable when it is followed by a lower-precedence operator such
+             * as the '<' in:
+             *
+             *     ((3) < (1 + n)) ? ...
+             *
+             * Returning true there makes the caller consume only '(3)' and then
+             * expect a closing parenthesis while the current token is '<'.
+             */
+            if (*p == ':' || *p == '?') {
+                return 0;
+            }
+            
+            if (*p == '<' || *p == '>' || *p == '=' || (*p == '!' && p[1] == '=')) {
+            
+                if ((p[0] == '<' && p[1] == '<') || (p[0] == '>' && p[1] == '>')) {
+                
+                    saw_token = 1;
+                    
+                    p += 2;
+                    continue;
+                
+                } else {
+                    return 0;
+                }
+            }
+            
+            if ((p[0] == '&' && p[1] == '&') || (p[0] == '|' && p[1] == '|')) {
+                return 0;
+            }
+        
+        }
+        
+        if (*p == '(') {
+        
+            depth++;
+            
+            saw_token = 1;
+            p++;
+            
+            continue;
+        
+        }
+        
+        if (*p == ')') {
+        
+            if (depth == 0) {
+                break;
+            }
+            
+            depth--;
+            p++;
+            
+            continue;
+        
+        }
+        
+        if (*p >= '0' && *p <= '9') {
+        
+            saw_token = 1;
+            p++;
+            
+            while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+                p++;
+            }
+            
+            continue;
+        
+        }
+        
+        if (*p == '\'') {
+        
+            saw_token = 1;
+            p++;
+            
+            while (*p && *p != '\'') {
+            
+                if (*p == '\\' && p[1]) {
+                    p += 2;
+                } else {
+                    p++;
+                }
+            
+            }
+            
+            if (*p == '\'') {
+                p++;
+            }
+            
+            continue;
+        
+        }
+        
+        if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_') {
+        
+            i = 0;
+            
+            while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+            
+                if (i + 1 < (int) sizeof (word)) {
+                    word[i++] = *p;
+                }
+                
+                p++;
+            
+            }
+            
+            word[i] = 0;
+            
+            if (strcmp (word, "sizeof") != 0 &&
+                strcmp (word, "char") != 0 &&
+                strcmp (word, "short") != 0 &&
+                strcmp (word, "int") != 0 &&
+                strcmp (word, "long") != 0 &&
+                strcmp (word, "signed") != 0 &&
+                strcmp (word, "unsigned") != 0 &&
+                strcmp (word, "void") != 0) {
+                return 0;
+            }
+            
+            saw_token = 1;
+            continue;
+        
+        }
+        
+        if (*p == '+' || *p == '-' || *p == '*' || *p == '/' || *p == '%' || *p == '&' || *p == '|' || *p == '^' || *p == '~' || *p == '!' || *p == '<' || *p == '>') {
+        
+            saw_token = 1;
+            p++;
+            
+            continue;
+        
+        }
+        
+        return 0;
+    
+    }
+    
+    return saw_token;
+
+}
+
+static void emit_load_const64_to_pair_now (const char *lo, const char *hi, int64_s v) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        fprintf (state->ofp, "    mov %s, %lu\n", lo, v.low & U32_MASK);
+        fprintf (state->ofp, "    mov %s, %lu\n", hi, v.high & U32_MASK);
+    
+    } else {
+    
+        fprintf (state->ofp, "    movl $%lu, %%%s\n", v.low & U32_MASK, lo);
+        fprintf (state->ofp, "    movl $%lu, %%%s\n", v.high & U32_MASK, hi);
+    
+    }
+
+}
+
+static void emit_load_const32_to_reg_now (const char *reg, int64_s v) {
+
+    flush_pending_statement_labels ();
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+        fprintf (state->ofp, "    mov %s, %lu\n", reg, v.low & U32_MASK);
+    } else {
+        fprintf (state->ofp, "    movl $%lu, %%%s\n", v.low & U32_MASK, reg);
+    }
+
+}
+
+static void emit_load_address_to_reg_now (const char *reg, const char *symbol) {
+
+    const char *asm_symbol;
+    flush_pending_statement_labels ();
+    
+    if (!state->ofp || !symbol || !*symbol) {
+        return;
+    }
+    
+    emit_extern_reference_symbol (symbol, DATA_PTR);
+    asm_symbol = asm_global_symbol_name (symbol);
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        if (state->syntax & ASM_SYNTAX_NASM) {
+            fprintf (state->ofp, "    mov %s, %s\n", reg, asm_symbol);
+        } else {
+            fprintf (state->ofp, "    mov %s, offset %s\n", reg, asm_symbol);
+        }
+    
+    } else {
+        fprintf (state->ofp, "    movl $%s, %%%s\n", asm_symbol, reg);
+    }
+
+}
+
+static void emit_load_deref_reg_now (const char *reg, int size);
+
+static int emit_load_parenthesized_indirect_member_to_reg_now (const char *reg);
+static int emit_parse_postfix_copy_source_address_now (const char *reg, struct local_symbol *src, const char *src_name, const char *name_start, const char *name_caret, unsigned long name_line);
+
+static void emit_load_postfix_lvalue_address_to_pair_now (const char *lo, const char *hi, int size) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (size > (DATA_PTR & 0x1f)) {
+    
+        emit_push_reg_now (lo);
+        
+        emit_load_deref_reg_now (lo, DATA_PTR & 0x1f);
+        emit_pop_reg_now (hi);
+        
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+        
+            if (state->syntax & ASM_SYNTAX_NASM) {
+                fprintf (state->ofp, "    mov %s, dword [%s + 4]\n", hi, hi);
+            } else {
+                fprintf (state->ofp, "    mov %s, dword ptr [%s + 4]\n", hi, hi);
+            }
+        
+        } else {
+            fprintf (state->ofp, "    movl 4(%%%s), %%%s\n", hi, hi);
+        }
+    
+    } else {
+    
+        emit_load_deref_reg_now (lo, size);
+        
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+            fprintf (state->ofp, "    xor %s, %s\n", hi, hi);
+        } else {
+            fprintf (state->ofp, "    xorl %%%s, %%%s\n", hi, hi);
+        }
+    
+    }
+
+}
+
+static void emit_store_pair_to_deref_reg_now (const char *addr_reg, const char *lo, const char *hi) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        if (state->syntax & ASM_SYNTAX_NASM) {
+        
+            fprintf (state->ofp, "    mov dword [%s], %s\n", addr_reg, lo);
+            fprintf (state->ofp, "    mov dword [%s + 4], %s\n", addr_reg, hi);
+        
+        } else {
+        
+            fprintf (state->ofp, "    mov dword ptr [%s], %s\n", addr_reg, lo);
+            fprintf (state->ofp, "    mov dword ptr [%s + 4], %s\n", addr_reg, hi);
+        
+        }
+    
+    } else {
+    
+        fprintf (state->ofp, "    movl %%%s, (%%%s)\n", lo, addr_reg);
+        fprintf (state->ofp, "    movl %%%s, 4(%%%s)\n", hi, addr_reg);
+    
+    }
+
+}
+
+static void emit_load_pair_from_deref_reg_now (const char *lo, const char *hi, const char *addr_reg) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        if (state->syntax & ASM_SYNTAX_NASM) {
+        
+            fprintf (state->ofp, "    mov %s, dword [%s]\n", lo, addr_reg);
+            fprintf (state->ofp, "    mov %s, dword [%s + 4]\n", hi, addr_reg);
+        
+        } else {
+        
+            fprintf (state->ofp, "    mov %s, dword ptr [%s]\n", lo, addr_reg);
+            fprintf (state->ofp, "    mov %s, dword ptr [%s + 4]\n", hi, addr_reg);
+        
+        }
+    
+    } else {
+    
+        fprintf (state->ofp, "    movl (%%%s), %%%s\n", addr_reg, lo);
+        fprintf (state->ofp, "    movl 4(%%%s), %%%s\n", addr_reg, hi);
+    
+    }
+
+}
+
+static void emit_copy_reg_now (const char *dst, const char *src) {
+
+    if (!state->ofp || !dst || !src || strcmp (dst, src) == 0) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+        fprintf (state->ofp, "    mov %s, %s\n", dst, src);
+    } else {
+        fprintf (state->ofp, "    movl %%%s, %%%s\n", src, dst);
+    }
+
+}
+
+static void emit_load_assignment_rhs_to_pair (const char *lo, const char *hi) {
+
+    if (_accept (TOK_LPAREN)) {
+    
+        if (token_starts_type_name ()) {
+        
+            int cast_size = 0;
+            int cast_is_unsigned = 0;
+            int cast_is_pointer = 0;
+            
+            if (parse_cast_type_name (&cast_size, &cast_is_unsigned, &cast_is_pointer)) {
+            
+                emit_load_assignment_rhs_to_pair (lo, hi);
+                
+                {
+                
+                    int applied_postfix = 0;
+                    
+                    if (cast_is_pointer) {
+                        set_rhs_last_pointer_info (1, cast_size > 0 ? cast_size : (DATA_INT & 0x1f));
+                    } else {
+                        emit_extend_pair_high_from_low (lo, hi, cast_size, cast_is_unsigned);
+                    }
+                    
+                    postfix_member_seen = 0;
+                    
+                    while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+                    
+                        applied_postfix = 1;
+                        emit_apply_postfix_member_access_to_reg_now (lo);
+                    
+                    }
+                    
+                    if (applied_postfix && postfix_member_seen) {
+                    
+                        if (postfix_member_size == (DATA_LLONG & 0x1f) && !postfix_member_is_floating) {
+                        
+                            const char *addr_reg = (strcmp (lo, "ecx") != 0 && strcmp (hi, "ecx") != 0) ? "ecx" : "esi";
+                            
+                            emit_copy_reg_now (addr_reg, lo);
+                            emit_load_pair_from_deref_reg_now (lo, hi, addr_reg);
+                        
+                        } else {
+                            emit_extend_pair_high_from_low (lo, hi, postfix_member_size, postfix_member_is_unsigned);
+                        }
+                    
+                    }
+                
+                }
+                
+                return;
+            
+            }
+        
+        }
+        
+        if (const_integer_expr_text_is_foldable_now (tok.caret)) {
+        
+            int64_s v = const64_from_current_foldable_expr ();
+            expect (TOK_RPAREN, ")");
+            
+            emit_load_const64_to_pair_now (lo, hi, v);
+            return;
+        
+        }
+        
+        if (emit_load_parenthesized_indirect_member_to_reg_now (lo)) {
+        
+            emit_extend_pair_high_from_low (lo, hi, DATA_INT & 0x1f, 1);
+            return;
+        
+        }
+        
+        emit_load_assignment_rhs_expression_to_pair (lo, hi, 1);
+        expect (TOK_RPAREN, ")");
+        
+        {
+        
+            int applied_postfix = 0;
+            postfix_member_seen = 0;
+            
+            while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+            
+                applied_postfix = 1;
+                emit_apply_postfix_member_access_to_reg_now (lo);
+            
+            }
+            
+            if (applied_postfix && postfix_member_seen) {
+            
+                if (postfix_member_size == (DATA_LLONG & 0x1f) && !postfix_member_is_floating) {
+                
+                    const char *addr_reg = (strcmp (lo, "ecx") != 0 && strcmp (hi, "ecx") != 0) ? "ecx" : "esi";
+                    
+                    emit_copy_reg_now (addr_reg, lo);
+                    emit_load_pair_from_deref_reg_now (lo, hi, addr_reg);
+                
+                } else {
+                    emit_extend_pair_high_from_low (lo, hi, postfix_member_size, postfix_member_is_unsigned);
+                }
+            
+            }
+        
+        }
+            
+        return;
+    
+    }
+    
+    if (tok.kind == TOK_PLUS || tok.kind == TOK_MINUS || tok.kind == TOK_TILDE || tok.kind == TOK_XMARK) {
+    
+        enum token_kind unary_op = tok.kind;
+        get_token ();
+        
+        emit_load_assignment_rhs_to_pair (lo, hi);
+        
+        if (state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+            
+                if (unary_op == TOK_MINUS) {
+                
+                    fprintf (state->ofp, "    not %s\n", lo);
+                    fprintf (state->ofp, "    not %s\n", hi);
+                    
+                    fprintf (state->ofp, "    add %s, 1\n", lo);
+                    fprintf (state->ofp, "    adc %s, 0\n", hi);
+                
+                } else if (unary_op == TOK_TILDE) {
+                
+                    fprintf (state->ofp, "    not %s\n", lo);
+                    fprintf (state->ofp, "    not %s\n", hi);
+                
+                } else if (unary_op == TOK_XMARK) {
+                
+                    fprintf (state->ofp, "    or %s, %s\n", lo, hi);
+                    fprintf (state->ofp, "    setz al\n");
+                    fprintf (state->ofp, "    movzx eax, al\n");
+                    
+                    if (strcmp (lo, "eax") != 0) {
+                        fprintf (state->ofp, "    mov %s, eax\n", lo);
+                    }
+                    
+                    fprintf (state->ofp, "    xor %s, %s\n", hi, hi);
+                
+                }
+            
+            } else {
+            
+                if (unary_op == TOK_MINUS) {
+                
+                    fprintf (state->ofp, "    notl %%%s\n", lo);
+                    fprintf (state->ofp, "    notl %%%s\n", hi);
+                    
+                    fprintf (state->ofp, "    addl $1, %%%s\n", lo);
+                    fprintf (state->ofp, "    adcl $0, %%%s\n", hi);
+                
+                } else if (unary_op == TOK_TILDE) {
+                
+                    fprintf (state->ofp, "    notl %%%s\n", lo);
+                    fprintf (state->ofp, "    notl %%%s\n", hi);
+                
+                } else if (unary_op == TOK_XMARK) {
+                
+                    fprintf (state->ofp, "    orl %%%s, %%%s\n", hi, lo);
+                    fprintf (state->ofp, "    setz %%al\n");
+                    fprintf (state->ofp, "    movzbl %%al, %%eax\n");
+                    
+                    if (strcmp (lo, "eax") != 0) {
+                        fprintf (state->ofp, "    movl %%eax, %%%s\n", lo);
+                    }
+                    
+                    fprintf (state->ofp, "    xorl %%%s, %%%s\n", hi, hi);
+                
+                }
+            
+            }
+        
+        }
+        
+        return;
+    
+    }
+    
+    if (emit_load_prefix_incdec_to_pair_now (lo, hi)) {
+        return;
+    }
+    
+    if (tok.kind == TOK_STAR) {
+    
+        char *name;
+        
+        const char *name_start;
+        const char *name_caret;
+        
+        unsigned long name_line;
+        struct local_symbol *src;
+        
+        int global_index;
+        
+        int deref_size = DATA_INT & 0x1f;
+        int deref_unsigned = 1;
+        
+        int postfix_incdec = 0;
+        
+        enum token_kind postfix_op = TOK_EOF;
+        get_token ();
+        
+        name_start = tok.start;
+        name_caret = tok.caret;
+        name_line = get_line_number ();
+        
+        if (tok.kind != TOK_IDENT || !tok.ident) {
+        
+            int64_s zero;
+            
+            zero.low = 0;
+            zero.high = 0;
+            
+            report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *");
+            
+            emit_load_const64_to_pair_now (lo, hi, zero);
+            return;
+        
+        }
+        
+        name = xstrdup (tok.ident);
+        get_token ();
+        
+        if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+        
+            postfix_incdec = 1;
+            postfix_op = tok.kind;
+            
+            get_token ();
+        
+        }
+        
+        src = find_local_symbol (name);
+        global_index = find_global_symbol (name);
+        
+        if (src) {
+        
+            if (src->pointer_depth > 1) {
+                deref_size = DATA_PTR & 0x1f;
+            } else if (src->pointer_depth == 1 && src->pointed_size > 0) {
+                deref_size = src->pointed_size & 0x1f;
+            }
+            
+            if (src->is_static && src->static_label) {
+                emit_load_global_to_reg (lo, src->static_label, DATA_PTR);
+            } else {
+                emit_load_local_to_reg (lo, src->offset, DATA_PTR);
+            }
+        
+        } else if (global_index >= 0) {
+        
+            if (get_global_symbol_pointer_depth (name) > 1) {
+                deref_size = DATA_PTR & 0x1f;
+            } else if (get_global_symbol_pointer_depth (name) == 1 && get_global_symbol_pointed_size (name) > 0) {
+                deref_size = get_global_symbol_pointed_size (name) & 0x1f;
+            }
+            
+            emit_load_global_to_reg (lo, name, DATA_PTR);
+        
+        } else {
+        
+            int64_s zero;
+            
+            zero.low = 0;
+            zero.high = 0;
+            
+            report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+            
+            emit_load_const64_to_pair_now (lo, hi, zero);
+            free (name);
+            
+            return;
+        
+        }
+        
+        if (deref_size == (DATA_LLONG & 0x1f)) {
+        
+            emit_push_reg_now (lo);
+            
+            emit_load_deref_reg_now (lo, DATA_INT & 0x1f);
+            emit_pop_reg_now (hi);
+            
+            if (state->ofp) {
+            
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                
+                    if (state->syntax & ASM_SYNTAX_NASM) {
+                        fprintf (state->ofp, "    mov %s, dword [%s + 4]\n", hi, hi);
+                    } else {
+                        fprintf (state->ofp, "    mov %s, dword ptr [%s + 4]\n", hi, hi);
+                    }
+                
+                } else {
+                    fprintf (state->ofp, "    movl 4(%%%s), %%%s\n", hi, hi);
+                }
+            
+            }
+        
+        } else {
+        
+            emit_load_deref_reg_now (lo, deref_size);
+            emit_extend_pair_high_from_low (lo, hi, deref_size, deref_unsigned);
+        
+        }
+        
+        if (postfix_incdec) {
+        
+            emit_push_reg_now (lo);
+            emit_push_reg_now (hi);
+            
+            emit_incdec_symbol_now (src, name, postfix_op, name_line, name_start, name_caret);
+            
+            emit_pop_reg_now (hi);
+            emit_pop_reg_now (lo);
+        
+        }
+        
+        free (name);
+        return;
+    
+    }
+    
+    if (token_is_sizeof_keyword ()) {
+    
+        int64_s v = sizeof_from_current_token ();
+        
+        if (state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+            
+                fprintf (state->ofp, "    mov %s, %lu\n", lo, v.low & U32_MASK);
+                fprintf (state->ofp, "    mov %s, %lu\n", hi, v.high & U32_MASK);
+            
+            } else {
+            
+                fprintf (state->ofp, "    movl $%lu, %%%s\n", v.low & U32_MASK, lo);
+                fprintf (state->ofp, "    movl $%lu, %%%s\n", v.high & U32_MASK, hi);
+            
+            }
+        
+        }
+        
+        return;
+    
+    }
+
+    if (tok.kind == TOK_IDENT) {
+    
+        enum token_kind postfix_op = TOK_EOF;
+        char *name = xstrdup (tok.ident);
+        
+        const char *name_start = tok.start, *name_caret = tok.caret;
+        unsigned long name_line = get_line_number ();
+        
+        int postfix_incdec = 0;
+        
+        struct local_symbol *src;
+        int64_s enum_value;
+        
+        get_token ();
+        
+        if (!find_local_symbol (name) && find_global_symbol (name) < 0 && resolve_enum_constant (name, &enum_value)) {
+        
+            emit_load_const64_to_pair_now (lo, hi, enum_value);
+            
+            free (name);
+            return;
+        
+        }
+
+        if (tok.kind == TOK_LPAREN) {
+        
+            if (!find_local_symbol (name)) {
+                ensure_global_function_symbol (name, name_start, name_caret, name_line);
+            }
+            
+            if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION && get_global_symbol_returns_void (name)) {
+                report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "void function '%s' used as a value", name);
+            }
+            
+            emit_call_identifier_to_reg_now (name, "eax", name_start, name_caret, name_line);
+            
+            if (state->ofp) {
+            
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                
+                    if (strcmp (lo, "eax") != 0) {
+                        fprintf (state->ofp, "    mov %s, eax\n", lo);
+                    }
+                    
+                    if (strcmp (hi, "edx") != 0) {
+                        fprintf (state->ofp, "    mov %s, edx\n", hi);
+                    }
+                
+                } else {
+                
+                    if (strcmp (lo, "eax") != 0) {
+                        fprintf (state->ofp, "    movl %%eax, %%%s\n", lo);
+                    }
+                    
+                    if (strcmp (hi, "edx") != 0) {
+                        fprintf (state->ofp, "    movl %%edx, %%%s\n", hi);
+                    }
+                
+                }
+            
+            }
+            
+            free (name);
+            return;
+        
+        }
+        
+        if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+        
+            postfix_incdec = 1;
+            postfix_op = tok.kind;
+            
+            get_token ();
+        
+        }
+
+        src = find_local_symbol (name);
+        
+        if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT || tok.kind == TOK_LBRACK) {
+        
+            int source_size;
+            
+            if (src || find_global_symbol (name) >= 0) {
+            
+                if (tok.kind == TOK_ARROW || tok.kind == TOK_LBRACK) {
+                
+                    if (src) {
+                    
+                        postfix_copy_lvalue_size = src->pointer_depth > 0 ? src->pointed_size : src->size;
+                        postfix_copy_lvalue_tag_name = src->pointer_depth > 0 ? src->pointed_tag_name : src->tag_name;
+                    
+                    } else {
+                    
+                        postfix_copy_lvalue_size = get_global_symbol_pointer_depth (name) > 0 ? get_global_symbol_pointed_size (name) : get_global_symbol_size (name);
+                        postfix_copy_lvalue_tag_name = 0;
+                    
+                    }
+                
+                } else if (src) {
+                
+                    postfix_copy_lvalue_size = src->size;
+                    postfix_copy_lvalue_tag_name = 0;
+                
+                } else {
+                
+                    postfix_copy_lvalue_size = get_global_symbol_size (name);
+                    postfix_copy_lvalue_tag_name = get_global_symbol_tag_name (name);
+                
+                }
+                
+                if (!emit_parse_postfix_copy_source_address_now (lo, src, name, name_start, name_caret, name_line)) {
+                
+                    free (name);
+                    return;
+                
+                }
+                
+                source_size = postfix_copy_lvalue_size;
+                
+                if (source_size <= 0) {
+                    source_size = DATA_INT & 0x1f;
+                }
+                
+                if (is_assignment_operator (tok.kind)) {
+                
+                    enum token_kind assign_op = tok.kind;
+                    
+                    const char *addr_reg = (strcmp (lo, "ecx") != 0 && strcmp (hi, "ecx") != 0) ? "ecx" : "edx";
+                    int lvalue_size = index_step_size (source_size);
+                    
+                    get_token ();
+                    
+                    if (assign_op == TOK_ASSIGN && lvalue_size == (DATA_LLONG & 0x1f)) {
+                    
+                        emit_push_reg_now (lo);
+                        
+                        emit_load_assignment_rhs_expression_to_pair (lo, hi, 1);
+                        emit_pop_reg_now (addr_reg);
+                        
+                        emit_store_pair_to_deref_reg_now (addr_reg, lo, hi);
+                        
+                        free (name);
+                        return;
+                    
+                    }
+                    
+                    if (assign_op == TOK_ASSIGN) {
+                    
+                        emit_push_reg_now (lo);
+                        
+                        emit_load_assignment_rhs_expression_to_reg (lo);
+                        emit_extend_pair_high_from_low (lo, hi, lvalue_size, 1);
+                        
+                        emit_pop_reg_now (addr_reg);
+                        emit_store_reg_to_deref_reg_now (addr_reg, lo, lvalue_size);
+                        
+                        free (name);
+                        return;
+                    
+                    }
+                    
+                    report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "compound member assignment expression not implemented in 64-bit context");
+                    
+                    free (name);
+                    return;
+                
+                }
+                
+                emit_load_postfix_lvalue_address_to_pair_now (lo, hi, source_size);
+                
+                free (name);
+                return;
+            
+            }
+        
+        }
+        
+        if (src) {
+        
+            if (src->size == (DATA_LLONG & 0x1f) && !src->is_floating) {
+            
+                if (src->is_static && src->static_label) {
+                    emit_load_global64_to_pair (lo, hi, src->static_label);
+                } else {
+                    emit_load_local64_to_pair (src->offset, lo, hi);
+                }
+            
+            } else {
+            
+                if (src->is_static && src->static_label) {
+                    emit_load_global_to_reg (lo, src->static_label, src->size);
+                } else {
+                    emit_load_local_to_reg (lo, src->offset, src->size);
+                }
+                
+                emit_extend_pair_high_from_low (lo, hi, src->size, src->is_unsigned);
+            
+            }
+            
+            if (postfix_incdec) {
+            
+                emit_push_reg_now (lo);
+                emit_push_reg_now (hi);
+                
+                emit_incdec_symbol_now (src, name, postfix_op, name_line, name_start, name_caret);
+                
+                emit_pop_reg_now (hi);
+                emit_pop_reg_now (lo);
+            
+            }
+            
+            free (name);
+            return;
+        
+        }
+        
+        if (find_global_symbol (name) >= 0) {
+        
+            if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION ||
+                get_global_symbol_array (name) ||
+                (!get_global_symbol_pointer_depth (name) &&
+                    get_global_symbol_size (name) > (DATA_PTR & 0x1f))) {
+            
+                /*
+                 * In a 64-bit/pair context, an array/function/aggregate
+                 * expression still decays to its address.  The 32-bit RHS
+                 * path already handled this, but this path was used when
+                 * the caller requested eax:edx (for example a mis-sized
+                 * pointer return).  Loading the first word of the global
+                 * table produced bogus pseudo-op table pointers such as the
+                 * first string pointer instead of &table.
+                 */
+                emit_load_address_to_reg_now (lo, name);
+                if (state->ofp) {
+                
+                    if (state->syntax & ASM_SYNTAX_INTEL) {
+                        fprintf (state->ofp, "    xor %s, %s\n", hi, hi);
+                    } else {
+                        fprintf (state->ofp, "    xorl %%%s, %%%s\n", hi, hi);
+                    }
+                
+                }
+            
+            } else if (get_global_symbol_size (name) == (DATA_LLONG & 0x1f) && !get_global_symbol_floating (name)) {
+                emit_load_global64_to_pair (lo, hi, name);
+            } else {
+            
+                emit_load_global_to_reg (lo, name, get_global_symbol_size (name));
+                emit_extend_pair_high_from_low (lo, hi, get_global_symbol_size (name), get_global_symbol_unsigned (name));
+            
+            }
+            
+            if (postfix_incdec) {
+            
+                emit_push_reg_now (lo);
+                emit_push_reg_now (hi);
+                
+                emit_incdec_symbol_now (0, name, postfix_op, name_line, name_start, name_caret);
+                
+                emit_pop_reg_now (hi);
+                emit_pop_reg_now (lo);
+            
+            }
+            
+            free (name);
+            return;
+        
+        }
+        
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+        free (name);
+        
+        if (state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+            
+                fprintf (state->ofp, "    xor %s, %s\n", lo, lo);
+                fprintf (state->ofp, "    xor %s, %s\n", hi, hi);
+            
+            } else {
+            
+                fprintf (state->ofp, "    xorl %%%s, %%%s\n", lo, lo);
+                fprintf (state->ofp, "    xorl %%%s, %%%s\n", hi, hi);
+            
+            }
+        
+        }
+        
+        return;
+    
+    }
+    
+    if (recover_unknown_rhs_identifier ()) {
+    
+        if (state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+            
+                fprintf (state->ofp, "    xor %s, %s\n", lo, lo);
+                fprintf (state->ofp, "    xor %s, %s\n", hi, hi);
+            
+            } else {
+            
+                fprintf (state->ofp, "    xorl %%%s, %%%s\n", lo, lo);
+                fprintf (state->ofp, "    xorl %%%s, %%%s\n", hi, hi);
+            
+            }
+        
+        }
+        
+        return;
+    
+    }
+    
+    {
+    
+        int64_s v = const64_from_current_operand ();
+        
+        if (state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+            
+                fprintf (state->ofp, "    mov %s, %lu\n", lo, v.low & U32_MASK);
+                fprintf (state->ofp, "    mov %s, %lu\n", hi, v.high & U32_MASK);
+            
+            } else {
+            
+                fprintf (state->ofp, "    movl $%lu, %%%s\n", v.low & U32_MASK, lo);
+                fprintf (state->ofp, "    movl $%lu, %%%s\n", v.high & U32_MASK, hi);
+            
+            }
+        
+        }
+    
+    }
+
+}
+
+static void emit_assignment_divmod64_intel (int is_unsigned, int want_mod) {
+
+    int loop_label, sub_label, nosub_label;
+    int lhs_pos_label, rhs_pos_label;
+    int qpos_label, rpos_label;
+    
+    loop_label = anon_label++;
+    sub_label = anon_label++;
+    nosub_label = anon_label++;
+    lhs_pos_label = anon_label++;
+    rhs_pos_label = anon_label++;
+    qpos_label = anon_label++;
+    rpos_label = anon_label++;
+    
+    if (state->syntax & ASM_SYNTAX_MASM) {
+    
+        fprintf (state->ofp, "    sub esp, 40\n");
+        fprintf (state->ofp, "    mov dword ptr [esp], eax\n");
+        fprintf (state->ofp, "    mov dword ptr [esp + 4], edx\n");
+        fprintf (state->ofp, "    mov dword ptr [esp + 8], ebx\n");
+        fprintf (state->ofp, "    mov dword ptr [esp + 12], ecx\n");
+        fprintf (state->ofp, "    xor eax, eax\n");
+        fprintf (state->ofp, "    mov dword ptr [esp + 16], eax\n");
+        fprintf (state->ofp, "    mov dword ptr [esp + 20], eax\n");
+        fprintf (state->ofp, "    mov dword ptr [esp + 24], eax\n");
+        fprintf (state->ofp, "    mov dword ptr [esp + 28], eax\n");
+        fprintf (state->ofp, "    mov dword ptr [esp + 32], eax\n");
+        fprintf (state->ofp, "    mov dword ptr [esp + 36], eax\n");
+        
+        if (!is_unsigned) {
+        
+            fprintf (state->ofp, "    test dword ptr [esp + 4], 80000000h\n");
+            fprintf (state->ofp, "    jz L%d\n", lhs_pos_label);
+            fprintf (state->ofp, "    not dword ptr [esp]\n");
+            fprintf (state->ofp, "    not dword ptr [esp + 4]\n");
+            fprintf (state->ofp, "    add dword ptr [esp], 1\n");
+            fprintf (state->ofp, "    adc dword ptr [esp + 4], 0\n");
+            fprintf (state->ofp, "    mov dword ptr [esp + 32], 1\n");
+            fprintf (state->ofp, "    xor dword ptr [esp + 36], 1\n");
+            fprintf (state->ofp, "L%d:\n", lhs_pos_label);
+            fprintf (state->ofp, "    test dword ptr [esp + 12], 80000000h\n");
+            fprintf (state->ofp, "    jz L%d\n", rhs_pos_label);
+            fprintf (state->ofp, "    not dword ptr [esp + 8]\n");
+            fprintf (state->ofp, "    not dword ptr [esp + 12]\n");
+            fprintf (state->ofp, "    add dword ptr [esp + 8], 1\n");
+            fprintf (state->ofp, "    adc dword ptr [esp + 12], 0\n");
+            fprintf (state->ofp, "    xor dword ptr [esp + 36], 1\n");
+            fprintf (state->ofp, "L%d:\n", rhs_pos_label);
+        
+        }
+        
+        fprintf (state->ofp, "    mov esi, 64\n");
+        fprintf (state->ofp, "L%d:\n", loop_label);
+        fprintf (state->ofp, "    shl dword ptr [esp], 1\n");
+        fprintf (state->ofp, "    rcl dword ptr [esp + 4], 1\n");
+        fprintf (state->ofp, "    rcl dword ptr [esp + 24], 1\n");
+        fprintf (state->ofp, "    rcl dword ptr [esp + 28], 1\n");
+        fprintf (state->ofp, "    shl dword ptr [esp + 16], 1\n");
+        fprintf (state->ofp, "    rcl dword ptr [esp + 20], 1\n");
+        fprintf (state->ofp, "    mov eax, dword ptr [esp + 28]\n");
+        fprintf (state->ofp, "    cmp eax, dword ptr [esp + 12]\n");
+        fprintf (state->ofp, "    ja L%d\n", sub_label);
+        fprintf (state->ofp, "    jb L%d\n", nosub_label);
+        fprintf (state->ofp, "    mov eax, dword ptr [esp + 24]\n");
+        fprintf (state->ofp, "    cmp eax, dword ptr [esp + 8]\n");
+        fprintf (state->ofp, "    jb L%d\n", nosub_label);
+        fprintf (state->ofp, "L%d:\n", sub_label);
+        fprintf (state->ofp, "    mov eax, dword ptr [esp + 8]\n");
+        fprintf (state->ofp, "    sub dword ptr [esp + 24], eax\n");
+        fprintf (state->ofp, "    mov eax, dword ptr [esp + 12]\n");
+        fprintf (state->ofp, "    sbb dword ptr [esp + 28], eax\n");
+        fprintf (state->ofp, "    or dword ptr [esp + 16], 1\n");
+        fprintf (state->ofp, "L%d:\n", nosub_label);
+        fprintf (state->ofp, "    dec esi\n");
+        fprintf (state->ofp, "    jnz L%d\n", loop_label);
+        
+        if (!is_unsigned) {
+        
+            fprintf (state->ofp, "    cmp dword ptr [esp + 36], 0\n");
+            fprintf (state->ofp, "    je L%d\n", qpos_label);
+            fprintf (state->ofp, "    not dword ptr [esp + 16]\n");
+            fprintf (state->ofp, "    not dword ptr [esp + 20]\n");
+            fprintf (state->ofp, "    add dword ptr [esp + 16], 1\n");
+            fprintf (state->ofp, "    adc dword ptr [esp + 20], 0\n");
+            fprintf (state->ofp, "L%d:\n", qpos_label);
+            fprintf (state->ofp, "    cmp dword ptr [esp + 32], 0\n");
+            fprintf (state->ofp, "    je L%d\n", rpos_label);
+            fprintf (state->ofp, "    not dword ptr [esp + 24]\n");
+            fprintf (state->ofp, "    not dword ptr [esp + 28]\n");
+            fprintf (state->ofp, "    add dword ptr [esp + 24], 1\n");
+            fprintf (state->ofp, "    adc dword ptr [esp + 28], 0\n");
+            fprintf (state->ofp, "L%d:\n", rpos_label);
+        
+        }
+        
+        if (want_mod) {
+        
+            fprintf (state->ofp, "    mov eax, dword ptr [esp + 24]\n");
+            fprintf (state->ofp, "    mov edx, dword ptr [esp + 28]\n");
+        
+        } else {
+        
+            fprintf (state->ofp, "    mov eax, dword ptr [esp + 16]\n");
+            fprintf (state->ofp, "    mov edx, dword ptr [esp + 20]\n");
+        
+        }
+    
+    } else if (state->syntax & ASM_SYNTAX_NASM) {
+    
+        fprintf (state->ofp, "    sub esp, 40\n");
+        fprintf (state->ofp, "    mov dword [esp], eax\n");
+        fprintf (state->ofp, "    mov dword [esp + 4], edx\n");
+        fprintf (state->ofp, "    mov dword [esp + 8], ebx\n");
+        fprintf (state->ofp, "    mov dword [esp + 12], ecx\n");
+        fprintf (state->ofp, "    xor eax, eax\n");
+        fprintf (state->ofp, "    mov dword [esp + 16], eax\n");
+        fprintf (state->ofp, "    mov dword [esp + 20], eax\n");
+        fprintf (state->ofp, "    mov dword [esp + 24], eax\n");
+        fprintf (state->ofp, "    mov dword [esp + 28], eax\n");
+        fprintf (state->ofp, "    mov dword [esp + 32], eax\n");
+        fprintf (state->ofp, "    mov dword [esp + 36], eax\n");
+        
+        if (!is_unsigned) {
+        
+            fprintf (state->ofp, "    test dword [esp + 4], 80000000h\n");
+            fprintf (state->ofp, "    jz L%d\n", lhs_pos_label);
+            fprintf (state->ofp, "    not dword [esp]\n");
+            fprintf (state->ofp, "    not dword [esp + 4]\n");
+            fprintf (state->ofp, "    add dword [esp], 1\n");
+            fprintf (state->ofp, "    adc dword [esp + 4], 0\n");
+            fprintf (state->ofp, "    mov dword [esp + 32], 1\n");
+            fprintf (state->ofp, "    xor dword [esp + 36], 1\n");
+            fprintf (state->ofp, "L%d:\n", lhs_pos_label);
+            fprintf (state->ofp, "    test dword [esp + 12], 80000000h\n");
+            fprintf (state->ofp, "    jz L%d\n", rhs_pos_label);
+            fprintf (state->ofp, "    not dword [esp + 8]\n");
+            fprintf (state->ofp, "    not dword [esp + 12]\n");
+            fprintf (state->ofp, "    add dword [esp + 8], 1\n");
+            fprintf (state->ofp, "    adc dword [esp + 12], 0\n");
+            fprintf (state->ofp, "    xor dword [esp + 36], 1\n");
+            fprintf (state->ofp, "L%d:\n", rhs_pos_label);
+        
+        }
+        
+        fprintf (state->ofp, "    mov esi, 64\n");
+        fprintf (state->ofp, "L%d:\n", loop_label);
+        fprintf (state->ofp, "    shl dword [esp], 1\n");
+        fprintf (state->ofp, "    rcl dword [esp + 4], 1\n");
+        fprintf (state->ofp, "    rcl dword [esp + 24], 1\n");
+        fprintf (state->ofp, "    rcl dword [esp + 28], 1\n");
+        fprintf (state->ofp, "    shl dword [esp + 16], 1\n");
+        fprintf (state->ofp, "    rcl dword [esp + 20], 1\n");
+        fprintf (state->ofp, "    mov eax, dword [esp + 28]\n");
+        fprintf (state->ofp, "    cmp eax, dword [esp + 12]\n");
+        fprintf (state->ofp, "    ja L%d\n", sub_label);
+        fprintf (state->ofp, "    jb L%d\n", nosub_label);
+        fprintf (state->ofp, "    mov eax, dword [esp + 24]\n");
+        fprintf (state->ofp, "    cmp eax, dword [esp + 8]\n");
+        fprintf (state->ofp, "    jb L%d\n", nosub_label);
+        fprintf (state->ofp, "L%d:\n", sub_label);
+        fprintf (state->ofp, "    mov eax, dword [esp + 8]\n");
+        fprintf (state->ofp, "    sub dword [esp + 24], eax\n");
+        fprintf (state->ofp, "    mov eax, dword [esp + 12]\n");
+        fprintf (state->ofp, "    sbb dword [esp + 28], eax\n");
+        fprintf (state->ofp, "    or dword [esp + 16], 1\n");
+        fprintf (state->ofp, "L%d:\n", nosub_label);
+        fprintf (state->ofp, "    dec esi\n");
+        fprintf (state->ofp, "    jnz L%d\n", loop_label);
+        
+        if (!is_unsigned) {
+        
+            fprintf (state->ofp, "    cmp dword [esp + 36], 0\n");
+            fprintf (state->ofp, "    je L%d\n", qpos_label);
+            fprintf (state->ofp, "    not dword [esp + 16]\n");
+            fprintf (state->ofp, "    not dword [esp + 20]\n");
+            fprintf (state->ofp, "    add dword [esp + 16], 1\n");
+            fprintf (state->ofp, "    adc dword [esp + 20], 0\n");
+            fprintf (state->ofp, "L%d:\n", qpos_label);
+            fprintf (state->ofp, "    cmp dword [esp + 32], 0\n");
+            fprintf (state->ofp, "    je L%d\n", rpos_label);
+            fprintf (state->ofp, "    not dword [esp + 24]\n");
+            fprintf (state->ofp, "    not dword [esp + 28]\n");
+            fprintf (state->ofp, "    add dword [esp + 24], 1\n");
+            fprintf (state->ofp, "    adc dword [esp + 28], 0\n");
+            fprintf (state->ofp, "L%d:\n", rpos_label);
+        
+        }
+        
+        if (want_mod) {
+        
+            fprintf (state->ofp, "    mov eax, dword [esp + 24]\n");
+            fprintf (state->ofp, "    mov edx, dword [esp + 28]\n");
+        
+        } else {
+        
+            fprintf (state->ofp, "    mov eax, dword [esp + 16]\n");
+            fprintf (state->ofp, "    mov edx, dword [esp + 20]\n");
+        
+        }
+    
+    } else {
+    
+        fprintf (state->ofp, "    sub esp, 40\n");
+        fprintf (state->ofp, "    mov dword ptr [esp], eax\n");
+        fprintf (state->ofp, "    mov dword ptr [esp + 4], edx\n");
+        fprintf (state->ofp, "    mov dword ptr [esp + 8], ebx\n");
+        fprintf (state->ofp, "    mov dword ptr [esp + 12], ecx\n");
+        fprintf (state->ofp, "    xor eax, eax\n");
+        fprintf (state->ofp, "    mov dword ptr [esp + 16], eax\n");
+        fprintf (state->ofp, "    mov dword ptr [esp + 20], eax\n");
+        fprintf (state->ofp, "    mov dword ptr [esp + 24], eax\n");
+        fprintf (state->ofp, "    mov dword ptr [esp + 28], eax\n");
+        fprintf (state->ofp, "    mov dword ptr [esp + 32], eax\n");
+        fprintf (state->ofp, "    mov dword ptr [esp + 36], eax\n");
+        
+        if (!is_unsigned) {
+        
+            fprintf (state->ofp, "    test dword ptr [esp + 4], 80000000h\n");
+            fprintf (state->ofp, "    jz .L%d\n", lhs_pos_label);
+            fprintf (state->ofp, "    not dword ptr [esp]\n");
+            fprintf (state->ofp, "    not dword ptr [esp + 4]\n");
+            fprintf (state->ofp, "    add dword ptr [esp], 1\n");
+            fprintf (state->ofp, "    adc dword ptr [esp + 4], 0\n");
+            fprintf (state->ofp, "    mov dword ptr [esp + 32], 1\n");
+            fprintf (state->ofp, "    xor dword ptr [esp + 36], 1\n");
+            fprintf (state->ofp, ".L%d:\n", lhs_pos_label);
+            fprintf (state->ofp, "    test dword ptr [esp + 12], 80000000h\n");
+            fprintf (state->ofp, "    jz .L%d\n", rhs_pos_label);
+            fprintf (state->ofp, "    not dword ptr [esp + 8]\n");
+            fprintf (state->ofp, "    not dword ptr [esp + 12]\n");
+            fprintf (state->ofp, "    add dword ptr [esp + 8], 1\n");
+            fprintf (state->ofp, "    adc dword ptr [esp + 12], 0\n");
+            fprintf (state->ofp, "    xor dword ptr [esp + 36], 1\n");
+            fprintf (state->ofp, ".L%d:\n", rhs_pos_label);
+        
+        }
+        
+        fprintf (state->ofp, "    mov esi, 64\n");
+        fprintf (state->ofp, ".L%d:\n", loop_label);
+        fprintf (state->ofp, "    shl dword ptr [esp], 1\n");
+        fprintf (state->ofp, "    rcl dword ptr [esp + 4], 1\n");
+        fprintf (state->ofp, "    rcl dword ptr [esp + 24], 1\n");
+        fprintf (state->ofp, "    rcl dword ptr [esp + 28], 1\n");
+        fprintf (state->ofp, "    shl dword ptr [esp + 16], 1\n");
+        fprintf (state->ofp, "    rcl dword ptr [esp + 20], 1\n");
+        fprintf (state->ofp, "    mov eax, dword ptr [esp + 28]\n");
+        fprintf (state->ofp, "    cmp eax, dword ptr [esp + 12]\n");
+        fprintf (state->ofp, "    ja .L%d\n", sub_label);
+        fprintf (state->ofp, "    jb .L%d\n", nosub_label);
+        fprintf (state->ofp, "    mov eax, dword ptr [esp + 24]\n");
+        fprintf (state->ofp, "    cmp eax, dword ptr [esp + 8]\n");
+        fprintf (state->ofp, "    jb .L%d\n", nosub_label);
+        fprintf (state->ofp, ".L%d:\n", sub_label);
+        fprintf (state->ofp, "    mov eax, dword ptr [esp + 8]\n");
+        fprintf (state->ofp, "    sub dword ptr [esp + 24], eax\n");
+        fprintf (state->ofp, "    mov eax, dword ptr [esp + 12]\n");
+        fprintf (state->ofp, "    sbb dword ptr [esp + 28], eax\n");
+        fprintf (state->ofp, "    or dword ptr [esp + 16], 1\n");
+        fprintf (state->ofp, ".L%d:\n", nosub_label);
+        fprintf (state->ofp, "    dec esi\n");
+        fprintf (state->ofp, "    jnz .L%d\n", loop_label);
+        
+        if (!is_unsigned) {
+        
+            fprintf (state->ofp, "    cmp dword ptr [esp + 36], 0\n");
+            fprintf (state->ofp, "    je .L%d\n", qpos_label);
+            fprintf (state->ofp, "    not dword ptr [esp + 16]\n");
+            fprintf (state->ofp, "    not dword ptr [esp + 20]\n");
+            fprintf (state->ofp, "    add dword ptr [esp + 16], 1\n");
+            fprintf (state->ofp, "    adc dword ptr [esp + 20], 0\n");
+            fprintf (state->ofp, ".L%d:\n", qpos_label);
+            fprintf (state->ofp, "    cmp dword ptr [esp + 32], 0\n");
+            fprintf (state->ofp, "    je .L%d\n", rpos_label);
+            fprintf (state->ofp, "    not dword ptr [esp + 24]\n");
+            fprintf (state->ofp, "    not dword ptr [esp + 28]\n");
+            fprintf (state->ofp, "    add dword ptr [esp + 24], 1\n");
+            fprintf (state->ofp, "    adc dword ptr [esp + 28], 0\n");
+            fprintf (state->ofp, ".L%d:\n", rpos_label);
+        
+        }
+        
+        if (want_mod) {
+        
+            fprintf (state->ofp, "    mov eax, dword ptr [esp + 24]\n");
+            fprintf (state->ofp, "    mov edx, dword ptr [esp + 28]\n");
+        
+        } else {
+        
+            fprintf (state->ofp, "    mov eax, dword ptr [esp + 16]\n");
+            fprintf (state->ofp, "    mov edx, dword ptr [esp + 20]\n");
+        
+        }
+    
+    }
+    
+    fprintf (state->ofp, "    add esp, 40\n");
+
+}
+
+static void emit_assignment_divmod64_att (int is_unsigned, int want_mod) {
+
+    int loop_label, sub_label, nosub_label;
+    int lhs_pos_label, rhs_pos_label;
+    int qpos_label, rpos_label;
+    
+    loop_label = anon_label++;
+    sub_label = anon_label++;
+    nosub_label = anon_label++;
+    lhs_pos_label = anon_label++;
+    rhs_pos_label = anon_label++;
+    qpos_label = anon_label++;
+    rpos_label = anon_label++;
+    
+    fprintf (state->ofp, "    subl $40, %%esp\n");
+    fprintf (state->ofp, "    movl %%eax, (%%esp)\n");
+    fprintf (state->ofp, "    movl %%edx, 4(%%esp)\n");
+    fprintf (state->ofp, "    movl %%ebx, 8(%%esp)\n");
+    fprintf (state->ofp, "    movl %%ecx, 12(%%esp)\n");
+    fprintf (state->ofp, "    xorl %%eax, %%eax\n");
+    fprintf (state->ofp, "    movl %%eax, 16(%%esp)\n");
+    fprintf (state->ofp, "    movl %%eax, 20(%%esp)\n");
+    fprintf (state->ofp, "    movl %%eax, 24(%%esp)\n");
+    fprintf (state->ofp, "    movl %%eax, 28(%%esp)\n");
+    fprintf (state->ofp, "    movl %%eax, 32(%%esp)\n");
+    fprintf (state->ofp, "    movl %%eax, 36(%%esp)\n");
+    
+    if (!is_unsigned) {
+    
+        fprintf (state->ofp, "    testl $0x80000000, 4(%%esp)\n");
+        fprintf (state->ofp, "    jz .L%d\n", lhs_pos_label);
+        fprintf (state->ofp, "    notl (%%esp)\n");
+        fprintf (state->ofp, "    notl 4(%%esp)\n");
+        fprintf (state->ofp, "    addl $1, (%%esp)\n");
+        fprintf (state->ofp, "    adcl $0, 4(%%esp)\n");
+        fprintf (state->ofp, "    movl $1, 32(%%esp)\n");
+        fprintf (state->ofp, "    xorl $1, 36(%%esp)\n");
+        fprintf (state->ofp, ".L%d:\n", lhs_pos_label);
+        fprintf (state->ofp, "    testl $0x80000000, 12(%%esp)\n");
+        fprintf (state->ofp, "    jz .L%d\n", rhs_pos_label);
+        fprintf (state->ofp, "    notl 8(%%esp)\n");
+        fprintf (state->ofp, "    notl 12(%%esp)\n");
+        fprintf (state->ofp, "    addl $1, 8(%%esp)\n");
+        fprintf (state->ofp, "    adcl $0, 12(%%esp)\n");
+        fprintf (state->ofp, "    xorl $1, 36(%%esp)\n");
+        fprintf (state->ofp, ".L%d:\n", rhs_pos_label);
+    
+    }
+    
+    fprintf (state->ofp, "    movl $64, %%esi\n");
+    fprintf (state->ofp, ".L%d:\n", loop_label);
+    fprintf (state->ofp, "    shll $1, (%%esp)\n");
+    fprintf (state->ofp, "    rcll $1, 4(%%esp)\n");
+    fprintf (state->ofp, "    rcll $1, 24(%%esp)\n");
+    fprintf (state->ofp, "    rcll $1, 28(%%esp)\n");
+    fprintf (state->ofp, "    shll $1, 16(%%esp)\n");
+    fprintf (state->ofp, "    rcll $1, 20(%%esp)\n");
+    fprintf (state->ofp, "    movl 28(%%esp), %%eax\n");
+    fprintf (state->ofp, "    cmpl 12(%%esp), %%eax\n");
+    fprintf (state->ofp, "    ja .L%d\n", sub_label);
+    fprintf (state->ofp, "    jb .L%d\n", nosub_label);
+    fprintf (state->ofp, "    movl 24(%%esp), %%eax\n");
+    fprintf (state->ofp, "    cmpl 8(%%esp), %%eax\n");
+    fprintf (state->ofp, "    jb .L%d\n", nosub_label);
+    fprintf (state->ofp, ".L%d:\n", sub_label);
+    fprintf (state->ofp, "    movl 8(%%esp), %%eax\n");
+    fprintf (state->ofp, "    subl %%eax, 24(%%esp)\n");
+    fprintf (state->ofp, "    movl 12(%%esp), %%eax\n");
+    fprintf (state->ofp, "    sbbl %%eax, 28(%%esp)\n");
+    fprintf (state->ofp, "    orl $1, 16(%%esp)\n");
+    fprintf (state->ofp, ".L%d:\n", nosub_label);
+    fprintf (state->ofp, "    decl %%esi\n");
+    fprintf (state->ofp, "    jnz .L%d\n", loop_label);
+    
+    if (!is_unsigned) {
+    
+        fprintf (state->ofp, "    cmpl $0, 36(%%esp)\n");
+        fprintf (state->ofp, "    je .L%d\n", qpos_label);
+        fprintf (state->ofp, "    notl 16(%%esp)\n");
+        fprintf (state->ofp, "    notl 20(%%esp)\n");
+        fprintf (state->ofp, "    addl $1, 16(%%esp)\n");
+        fprintf (state->ofp, "    adcl $0, 20(%%esp)\n");
+        fprintf (state->ofp, ".L%d:\n", qpos_label);
+        fprintf (state->ofp, "    cmpl $0, 32(%%esp)\n");
+        fprintf (state->ofp, "    je .L%d\n", rpos_label);
+        fprintf (state->ofp, "    notl 24(%%esp)\n");
+        fprintf (state->ofp, "    notl 28(%%esp)\n");
+        fprintf (state->ofp, "    addl $1, 24(%%esp)\n");
+        fprintf (state->ofp, "    adcl $0, 28(%%esp)\n");
+        fprintf (state->ofp, ".L%d:\n", rpos_label);
+    
+    }
+    
+    if (want_mod) {
+    
+        fprintf (state->ofp, "    movl 24(%%esp), %%eax\n");
+        fprintf (state->ofp, "    movl 28(%%esp), %%edx\n");
+    
+    } else {
+    
+        fprintf (state->ofp, "    movl 16(%%esp), %%eax\n");
+        fprintf (state->ofp, "    movl 20(%%esp), %%edx\n");
+    
+    }
+    
+    fprintf (state->ofp, "    addl $40, %%esp\n");
+
+}
+
+static void emit_assignment_divmod64 (int is_unsigned, int want_mod) {
+
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+        emit_assignment_divmod64_intel (is_unsigned, want_mod);
+    } else {
+        emit_assignment_divmod64_att (is_unsigned, want_mod);
+    }
+
+}
+
+static void emit_assignment_binary_op64 (enum token_kind op, int is_unsigned) {
+
+    int l1, l2;
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        switch (op) {
+        
+            case TOK_PLUS:      case TOK_PLUSEQ:
+            
+                fprintf (state->ofp, "    add eax, ebx\n");
+                fprintf (state->ofp, "    adc edx, ecx\n");
+                break;
+            
+            case TOK_MINUS:     case TOK_MINUSEQ:
+            
+                fprintf (state->ofp, "    sub eax, ebx\n");
+                fprintf (state->ofp, "    sbb edx, ecx\n");
+                break;
+            
+            case TOK_STAR:      case TOK_STAREQ:
+            
+                /**
+                 * Low 64 bits of (EDX:EAX * EBX).  The high half of the RHS
+                 * is ignored for now; this is enough for constants/small ints.
+                 */
+                fprintf (state->ofp, "    mov esi, eax\n");
+                fprintf (state->ofp, "    mov edi, edx\n");
+                fprintf (state->ofp, "    mul ebx\n");
+                fprintf (state->ofp, "    mov esi, edx\n");
+                fprintf (state->ofp, "    mov edx, edi\n");
+                fprintf (state->ofp, "    imul edx, ebx\n");
+                fprintf (state->ofp, "    add edx, esi\n");
+                break;
+            
+            case TOK_BSLASH:    case TOK_SLASHEQ:
+            
+                emit_assignment_divmod64 (is_unsigned, 0);
+                break;
+            
+            case TOK_MOD:       case TOK_MODEQ:
+            
+                emit_assignment_divmod64 (is_unsigned, 1);
+                break;
+            
+            case TOK_AMPER:     case TOK_ANDEQ:
+            
+                fprintf (state->ofp, "    and eax, ebx\n");
+                fprintf (state->ofp, "    and edx, ecx\n");
+                break;
+            
+            case TOK_PIPE:      case TOK_OREQ:
+            
+                fprintf (state->ofp, "    or eax, ebx\n");
+                fprintf (state->ofp, "    or edx, ecx\n");
+                break;
+            
+            case TOK_CARET:     case TOK_XOREQ:
+            
+                fprintf (state->ofp, "    xor eax, ebx\n");
+                fprintf (state->ofp, "    xor edx, ecx\n");
+                break;
+            
+            case TOK_LSH:       case TOK_LSHEQ:
+            
+                l1 = anon_label++;
+                l2 = anon_label++;
+                
+                fprintf (state->ofp, "    test ecx, ecx\n");
+                fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), l2);
+                fprintf (state->ofp, "    mov ecx, ebx\n");
+                fprintf (state->ofp, "    cmp ecx, 64\n");
+                fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jae L%d\n" : "    jae .L%d\n"), l2);
+                fprintf (state->ofp, "    cmp ecx, 32\n");
+                fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jb L%d\n" : "    jb .L%d\n"), l1);
+                fprintf (state->ofp, "    mov edx, eax\n");
+                fprintf (state->ofp, "    xor eax, eax\n");
+                fprintf (state->ofp, "    sub ecx, 32\n");
+                fprintf (state->ofp, "    shl edx, cl\n");
+                fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jmp L%d\n" : "    jmp .L%d\n"), l2 + 1);
+                fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l1);
+                fprintf (state->ofp, "    shld edx, eax, cl\n");
+                fprintf (state->ofp, "    shl eax, cl\n");
+                fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jmp L%d\n" : "    jmp .L%d\n"), l2 + 1);
+                fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l2);
+                fprintf (state->ofp, "    xor eax, eax\n");
+                fprintf (state->ofp, "    xor edx, edx\n");
+                fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l2 + 1);
+                
+                anon_label++;
+                break;
+            
+            case TOK_RSH:       case TOK_RSHEQ:
+            
+                l1 = anon_label++;
+                l2 = anon_label++;
+                
+                fprintf (state->ofp, "    test ecx, ecx\n");
+                fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), l2);
+                fprintf (state->ofp, "    mov ecx, ebx\n");
+                fprintf (state->ofp, "    cmp ecx, 64\n");
+                fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jae L%d\n" : "    jae .L%d\n"), l2);
+                fprintf (state->ofp, "    cmp ecx, 32\n");
+                fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jb L%d\n" : "    jb .L%d\n"), l1);
+                fprintf (state->ofp, "    mov eax, edx\n");
+                fprintf (state->ofp, is_unsigned ? "    xor edx, edx\n" : "    sar edx, 31\n");
+                fprintf (state->ofp, "    sub ecx, 32\n");
+                fprintf (state->ofp, is_unsigned ? "    shr eax, cl\n" : "    sar eax, cl\n");
+                fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jmp L%d\n" : "    jmp .L%d\n"), l2 + 1);
+                fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l1);
+                fprintf (state->ofp, "    shrd eax, edx, cl\n");
+                fprintf (state->ofp, is_unsigned ? "    shr edx, cl\n" : "    sar edx, cl\n");
+                fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jmp L%d\n" : "    jmp .L%d\n"), l2 + 1);
+                fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l2);
+                fprintf (state->ofp, is_unsigned ? "    xor eax, eax\n" : "    mov eax, edx\n");
+                fprintf (state->ofp, is_unsigned ? "    xor edx, edx\n" : "    sar edx, 31\n");
+                fprintf (state->ofp, is_unsigned ? "" : "    mov eax, edx\n");
+                fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l2 + 1);
+                
+                anon_label++;
+                break;
+            
+            default:
+            
+                break;
+        
+        }
+    
+    } else {
+    
+        switch (op) {
+        
+            case TOK_PLUS:      case TOK_PLUSEQ:
+            
+                fprintf (state->ofp, "    addl %%ebx, %%eax\n");
+                fprintf (state->ofp, "    adcl %%ecx, %%edx\n");
+                break;
+            
+            case TOK_MINUS:     case TOK_MINUSEQ:
+            
+                fprintf (state->ofp, "    subl %%ebx, %%eax\n");
+                fprintf (state->ofp, "    sbbl %%ecx, %%edx\n");
+                break;
+            
+            case TOK_STAR:      case TOK_STAREQ:
+            
+                fprintf (state->ofp, "    movl %%eax, %%esi\n");
+                fprintf (state->ofp, "    movl %%edx, %%edi\n");
+                fprintf (state->ofp, "    mull %%ebx\n");
+                fprintf (state->ofp, "    movl %%edx, %%esi\n");
+                fprintf (state->ofp, "    movl %%edi, %%edx\n");
+                fprintf (state->ofp, "    imull %%ebx, %%edx\n");
+                fprintf (state->ofp, "    addl %%esi, %%edx\n");
+                break;
+            
+            case TOK_BSLASH:    case TOK_SLASHEQ:
+            
+                emit_assignment_divmod64 (is_unsigned, 0);
+                break;
+            
+            case TOK_MOD:       case TOK_MODEQ:
+            
+                emit_assignment_divmod64 (is_unsigned, 1);
+                break;
+            
+            case TOK_AMPER:     case TOK_ANDEQ:
+            
+                fprintf (state->ofp, "    andl %%ebx, %%eax\n");
+                fprintf (state->ofp, "    andl %%ecx, %%edx\n");
+                break;
+            
+            case TOK_PIPE:      case TOK_OREQ:
+            
+                fprintf (state->ofp, "    orl %%ebx, %%eax\n");
+                fprintf (state->ofp, "    orl %%ecx, %%edx\n");
+                break;
+            
+            case TOK_CARET:     case TOK_XOREQ:
+            
+                fprintf (state->ofp, "    xorl %%ebx, %%eax\n");
+                fprintf (state->ofp, "    xorl %%ecx, %%edx\n");
+                break;
+            
+            case TOK_LSH:       case TOK_LSHEQ:
+            
+                l1 = anon_label++;
+                l2 = anon_label++;
+                
+                fprintf (state->ofp, "    testl %%ecx, %%ecx\n");
+                fprintf (state->ofp, "    jnz .L%d\n", l2);
+                fprintf (state->ofp, "    movl %%ebx, %%ecx\n");
+                fprintf (state->ofp, "    cmpl $64, %%ecx\n");
+                fprintf (state->ofp, "    jae .L%d\n", l2);
+                fprintf (state->ofp, "    cmpl $32, %%ecx\n");
+                fprintf (state->ofp, "    jb .L%d\n", l1);
+                fprintf (state->ofp, "    movl %%eax, %%edx\n");
+                fprintf (state->ofp, "    xorl %%eax, %%eax\n");
+                fprintf (state->ofp, "    subl $32, %%ecx\n");
+                fprintf (state->ofp, "    shll %%cl, %%edx\n");
+                fprintf (state->ofp, "    jmp .L%d\n", l2 + 1);
+                fprintf (state->ofp, ".L%d:\n", l1);
+                fprintf (state->ofp, "    shldl %%cl, %%eax, %%edx\n");
+                fprintf (state->ofp, "    shll %%cl, %%eax\n");
+                fprintf (state->ofp, "    jmp .L%d\n", l2 + 1);
+                fprintf (state->ofp, ".L%d:\n", l2);
+                fprintf (state->ofp, "    xorl %%eax, %%eax\n");
+                fprintf (state->ofp, "    xorl %%edx, %%edx\n");
+                fprintf (state->ofp, ".L%d:\n", l2 + 1);
+                
+                anon_label++;
+                break;
+            
+            case TOK_RSH:       case TOK_RSHEQ:
+            
+                l1 = anon_label++;
+                l2 = anon_label++;
+                
+                fprintf (state->ofp, "    testl %%ecx, %%ecx\n");
+                fprintf (state->ofp, "    jnz .L%d\n", l2);
+                fprintf (state->ofp, "    movl %%ebx, %%ecx\n");
+                fprintf (state->ofp, "    cmpl $64, %%ecx\n");
+                fprintf (state->ofp, "    jae .L%d\n", l2);
+                fprintf (state->ofp, "    cmpl $32, %%ecx\n");
+                fprintf (state->ofp, "    jb .L%d\n", l1);
+                fprintf (state->ofp, "    movl %%edx, %%eax\n");
+                fprintf (state->ofp, is_unsigned ? "    xorl %%edx, %%edx\n" : "    sarl $31, %%edx\n");
+                fprintf (state->ofp, "    subl $32, %%ecx\n");
+                fprintf (state->ofp, is_unsigned ? "    shrl %%cl, %%eax\n" : "    sarl %%cl, %%eax\n");
+                fprintf (state->ofp, "    jmp .L%d\n", l2 + 1);
+                fprintf (state->ofp, ".L%d:\n", l1);
+                fprintf (state->ofp, "    shrdl %%cl, %%edx, %%eax\n");
+                fprintf (state->ofp, is_unsigned ? "    shrl %%cl, %%edx\n" : "    sarl %%cl, %%edx\n");
+                fprintf (state->ofp, "    jmp .L%d\n", l2 + 1);
+                fprintf (state->ofp, ".L%d:\n", l2);
+                fprintf (state->ofp, is_unsigned ? "    xorl %%eax, %%eax\n" : "    movl %%edx, %%eax\n");
+                fprintf (state->ofp, is_unsigned ? "    xorl %%edx, %%edx\n" : "    sarl $31, %%edx\n");
+                fprintf (state->ofp, is_unsigned ? "" : "    movl %%edx, %%eax\n");
+                fprintf (state->ofp, ".L%d:\n", l2 + 1);
+                
+                anon_label++;
+                break;
+            
+            default:
+            
+                break;
+        
+        }
+    
+    }
+
+}
+
+static void emit_call_identifier_to_reg_now (const char *name, const char *reg, const char *name_start, const char *name_caret, unsigned long name_line);
+static void emit_assignment_binary_op (enum token_kind op, int is_unsigned);
+static void emit_store_member_to_addr_reg_now (const char *addr_reg, int offset, const char *value_reg, int size);
+static void emit_store_reg_to_deref_reg_now (const char *addr_reg, const char *value_reg, int size);
+static void emit_load_deref_reg_now (const char *reg, int size);
+
+static void emit_store_floating_member_to_addr_reg_now (const char *addr_reg, int offset, int size);
+static void emit_load_floating_rhs_expression_now (int result_size);
+
+static int emit_load_parenthesized_assignment_expression_to_reg_now (const char *reg, int *out_is_unsigned);
+static int token_is_floating_constant_now (void);
+
+static void emit_load_local_address_to_reg_now (const char *reg, long offset) {
+
+    char memref[64];
+    
+    if (!state->ofp || !reg) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        format_intel_ebp_offset (memref, sizeof (memref), offset);
+        fprintf (state->ofp, "    lea %s, %s\n", reg, memref);
+    
+    } else {
+        fprintf (state->ofp, "    leal %ld(%%ebp), %%%s\n", offset, reg);
+    }
+
+}
+
+static void emit_apply_postfix_member_access_to_reg_now (const char *reg) {
+
+    postfix_member_pointer_depth = 0;
+    postfix_member_pointed_size = 0;
+    postfix_member_seen = 0;
+    postfix_member_offset = 0;
+    postfix_member_size = 0;
+    postfix_member_is_floating = 0;
+    postfix_member_is_unsigned = 0;
+
+    {
+    
+        const char *current_object_tag_name = postfix_copy_lvalue_tag_name;
+        int current_object_size = postfix_copy_lvalue_size;
+        
+        if (current_object_size <= 0 && rhs_last_pointed_size > 0) {
+            current_object_size = rhs_last_pointed_size;
+        }
+        
+        while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+        
+            enum token_kind op = tok.kind;
+            char *member;
+            
+            const char *member_start;
+            const char *member_caret;
+            
+            unsigned long member_line;
+            
+            int offset = 0;
+            int size = DATA_INT & 0x1f;
+            int elem_size = DATA_INT & 0x1f;
+            int pointer_depth = 0;
+            int is_array = 0;
+            int is_floating = 0;
+            int is_unsigned = 0;
+            
+            get_token ();
+            
+            member_start = tok.start;
+            member_caret = tok.caret;
+            member_line = get_line_number ();
+            
+            if (tok.kind != TOK_IDENT) {
+            
+                report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", op == TOK_ARROW ? "->" : ".");
+                return;
+            
+            }
+            
+            member = xstrdup (tok.ident);
+            get_token ();
+            
+            if (!find_member_info_ex_bounded (member, current_object_size, current_object_tag_name, &offset, &size, &elem_size, &pointer_depth, &is_array, &is_floating)) {
+            
+                report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+                
+                free (member);
+                return;
+            
+            }
+            
+            is_unsigned = last_found_member_is_unsigned;
+            
+            {
+            
+                const char *member_tag_name = last_found_member_tag_name;
+                
+                /*
+                 * For an array member whose element type is a pointer, the
+                 * subscript step is always one pointer.  Keep this independent
+                 * of the pointed aggregate size; otherwise expressions like
+                 * ic->heads[i] scale by sizeof(*heads[i]) and are later passed
+                 * as an aggregate instead of one pointer.
+                 */
+                if (is_array && pointer_depth > 0) {
+                    elem_size = DATA_PTR;
+                } else if (pointer_depth > 1) {
+                    elem_size = DATA_PTR;
+                } else if (pointer_depth == 1 && member_tag_name) {
+                
+                    struct aggregate_tag_entry *entry = find_aggregate_tag (member_tag_name, 0);
+                    
+                    if (entry) {
+                        elem_size = entry->size;
+                    }
+                
+                }
+                
+                current_object_tag_name = member_tag_name;
+            
+            }
+            
+            free (member);
+            
+            postfix_member_seen = 1;
+            postfix_member_pointer_depth = is_array ? 1 : pointer_depth;
+            postfix_member_pointed_size = elem_size;
+            postfix_member_offset = offset;
+            postfix_member_size = size;
+            postfix_member_is_floating = is_floating;
+            postfix_member_is_unsigned = is_unsigned;
+            
+            if (pointer_depth > 0 || is_array) {
+                current_object_size = elem_size;
+            } else {
+                current_object_size = size;
+            }
+            
+            if (tok.kind == TOK_LBRACK) {
+            
+                enum token_kind assign_op;
+                int subscript_elem_size;
+                
+                if (!is_array && pointer_depth > 0) {
+                    emit_load_member_from_addr_reg_now (reg, reg, offset, DATA_PTR & 0x1f);
+                } else if (state->ofp && offset != 0) {
+                
+                    if (state->syntax & ASM_SYNTAX_INTEL) {
+                        fprintf (state->ofp, "    add %s, %d\n", reg, offset);
+                    } else {
+                        fprintf (state->ofp, "    addl $%d, %%%s\n", offset, reg);
+                    }
+                
+                }
+                
+                subscript_elem_size = index_step_size (elem_size);
+                
+                if (subscript_elem_size <= 0) {
+                    subscript_elem_size = DATA_INT & 0x1f;
+                }
+                
+                emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, subscript_elem_size);
+                
+                if (tok.kind == TOK_ARROW && subscript_elem_size <= (DATA_PTR & 0x1f)) {
+                
+                    emit_load_deref_reg_now (reg, DATA_PTR & 0x1f);
+                    
+                    postfix_member_pointer_depth = 0;
+                    postfix_member_size = DATA_PTR & 0x1f;
+                    
+                    continue;
+                
+                }
+                
+                if (is_assignment_operator (tok.kind)) {
+                
+                    assign_op = tok.kind;
+                    get_token ();
+                    
+                    if (assign_op == TOK_ASSIGN && subscript_elem_size > (DATA_LLONG & 0x1f)
+                        && tok.kind == TOK_IDENT && tok.ident
+                        && get_global_symbol_kind (tok.ident) == GLOBAL_SYMBOL_FUNCTION) {
+                    
+                        char *rhs_name = xstrdup (tok.ident);
+                        
+                        const char *rhs_start = tok.start;
+                        const char *rhs_caret = tok.caret;
+                        
+                        unsigned long rhs_line = get_line_number ();
+                        emit_push_reg_now (reg);
+                        
+                        pending_struct_return_lhs = 0;
+                        pending_struct_return_global_name = 0;
+                        pending_struct_return_stack_address = 1;
+                        pending_struct_return_stack_offset = 0;
+                        
+                        get_token ();
+                        emit_call_identifier_to_reg_now (rhs_name, reg, rhs_start, rhs_caret, rhs_line);
+                        
+                        pending_struct_return_stack_address = 0;
+                        pending_struct_return_stack_offset = 0;
+                        
+                        emit_pop_reg_now ("edx");
+                        free (rhs_name);
+                        
+                        postfix_member_seen = 0;
+                        return;
+                    
+                    }
+                    
+                    emit_push_reg_now (reg);
+                    
+                    if (assign_op == TOK_ASSIGN) {
+                    
+                        if (subscript_elem_size > (DATA_LLONG & 0x1f)) {
+                        
+                            pending_struct_return_lhs = 0;
+                            pending_struct_return_global_name = 0;
+                            pending_struct_return_stack_address = 1;
+                            pending_struct_return_stack_offset = 0;
+                            
+                            emit_load_assignment_rhs_expression_to_reg (reg);
+                            
+                            pending_struct_return_stack_address = 0;
+                            pending_struct_return_stack_offset = 0;
+                            
+                            emit_pop_reg_now ("edx");
+                            
+                            postfix_member_seen = 0;
+                            return;
+                        
+                        }
+                        
+                        emit_load_assignment_rhs_expression_to_reg (reg);
+                    
+                    } else {
+                    
+                        emit_load_deref_reg_now (reg, subscript_elem_size);
+                        emit_push_reg_now (reg);
+                        emit_load_assignment_rhs_expression_to_reg ("edx");
+                        emit_pop_reg_now (reg);
+                        emit_assignment_binary_op (assign_op, 0);
+                    
+                    }
+                    
+                    emit_pop_reg_now ("edx");
+                    emit_store_reg_to_deref_reg_now ("edx", reg, subscript_elem_size);
+                    
+                    postfix_member_seen = 0;
+                    return;
+                
+                }
+                
+                if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+                
+                    current_object_size = elem_size;
+                    continue;
+                
+                }
+                
+                /**
+                 * The expression now denotes the subscripted element, not the
+                 * whole member array.  Keep the postfix-member metadata in
+                 * step with the value in REG so argument passing does not
+                 * mistake e.g. ic->heads[i] for the entire heads[] aggregate
+                 * and push multiple words.
+                 *
+                 * If the element itself is an aggregate, REG must remain the
+                 * address of that element.  Loading *(REG) would fetch the
+                 * first word of the struct and later aggregate argument
+                 * passing would treat that word as a pointer, as happened for
+                 * instruction.types[instruction.operands].
+                 */
+                postfix_member_size = subscript_elem_size;
+                
+                /*
+                 * Subscript of an array member whose element type is a pointer
+                 * yields the pointer stored in that array slot.  The generic
+                 * aggregate-subscript path deliberately keeps struct elements as
+                 * addresses, but pointer elements must still be loaded before a
+                 * following -> member access.  Otherwise expressions such as
+                 * instruction.regs[operand]->type.reg_rex treat the address of
+                 * the regs[] slot as if it were a struct reg_entry *.
+                 */
+                if (is_array && (pointer_depth > 0 || (tok.kind == TOK_ARROW && subscript_elem_size == (DATA_PTR & 0x1f)))) {
+                
+                    emit_load_deref_reg_now (reg, DATA_PTR & 0x1f);
+                    
+                    postfix_member_pointer_depth = pointer_depth > 0 ? pointer_depth - 1 : 0;
+                    postfix_member_size = DATA_PTR & 0x1f;
+                    
+                    if (postfix_member_pointer_depth == 0 && current_object_tag_name) {
+                    
+                        struct aggregate_tag_entry *entry = find_aggregate_tag (current_object_tag_name, 0);
+                        
+                        if (entry) {
+                            current_object_size = entry->size;
+                        }
+                    
+                    }
+                    
+                    continue;
+                
+                }
+                
+                if (postfix_member_pointer_depth > 0) {
+                    postfix_member_pointer_depth--;
+                }
+                
+                if (postfix_member_pointer_depth != 0
+                    || postfix_member_size <= (DATA_PTR & 0x1f)) {
+                    emit_load_deref_reg_now (reg, subscript_elem_size);
+                }
+                
+                continue;
+            
+            }
+            
+            if (is_array && tok.kind != TOK_ARROW && tok.kind != TOK_DOT) {
+            
+                if (state->ofp && offset != 0) {
+                
+                    if (state->syntax & ASM_SYNTAX_INTEL) {
+                        fprintf (state->ofp, "    add %s, %d\n", reg, offset);
+                    } else {
+                        fprintf (state->ofp, "    addl $%d, %%%s\n", offset, reg);
+                    }
+                
+                }
+                
+                /*
+                 * A bare array member expression decays to a pointer when used
+                 * as an rvalue/function argument.  Keep REG as the element
+                 * address, but do not leave aggregate element metadata behind or
+                 * the call argument path will push the first element by value.
+                 * This broke calls such as _cpp_add_unknown2_direct(...,
+                 * macro->tokens, token_count), where tokens[0] is a struct.
+                 */
+                postfix_member_size = DATA_PTR & 0x1f;
+                postfix_member_pointer_depth = 1;
+                postfix_member_pointed_size = elem_size;
+                
+                continue;
+            
+            }
+            
+            if ((tok.kind == TOK_ARROW || tok.kind == TOK_DOT) && pointer_depth == 0) {
+            
+                if (state->ofp && offset != 0) {
+                
+                    if (state->syntax & ASM_SYNTAX_INTEL) {
+                        fprintf (state->ofp, "    add %s, %d\n", reg, offset);
+                    } else {
+                        fprintf (state->ofp, "    addl $%d, %%%s\n", offset, reg);
+                    }
+                
+                }
+                
+                continue;
+            
+            }
+            
+            if (size > (DATA_PTR & 0x1f) && tok.kind != TOK_ARROW && tok.kind != TOK_DOT) {
+            
+                if (state->ofp && offset != 0) {
+                
+                    if (state->syntax & ASM_SYNTAX_INTEL) {
+                        fprintf (state->ofp, "    add %s, %d\n", reg, offset);
+                    } else {
+                        fprintf (state->ofp, "    addl $%d, %%%s\n", offset, reg);
+                    }
+                
+                }
+                
+                continue;
+            
+            }
+            
+            if (state->ofp) {
+            
+                if (strcmp (reg, "edx") != 0) {
+                
+                    if (state->syntax & ASM_SYNTAX_INTEL) {
+                        fprintf (state->ofp, "    mov edx, %s\n", reg);
+                    } else {
+                        fprintf (state->ofp, "    movl %%%s, %%edx\n", reg);
+                    }
+                
+                }
+                
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                
+                    const char *opsize = "dword";
+                    
+                    if (size == 1) {
+                        opsize = "byte";
+                    } else if (size == 2) {
+                        opsize = "word";
+                    }
+                    
+                    if (state->syntax & ASM_SYNTAX_NASM) {
+                    
+                        if (size == 1) {
+                            fprintf (state->ofp, "    movzx %s, byte [%s + %d]\n", reg, reg, offset);
+                        } else if (size == 2) {
+                            fprintf (state->ofp, "    movzx %s, word [%s + %d]\n", reg, reg, offset);
+                        } else {
+                            fprintf (state->ofp, "    mov %s, %s [%s + %d]\n", reg, opsize, reg, offset);
+                        }
+                    
+                    } else {
+                    
+                        if (size == 1) {
+                            fprintf (state->ofp, "    movzx %s, byte ptr [%s + %d]\n", reg, reg, offset);
+                        } else if (size == 2) {
+                            fprintf (state->ofp, "    movzx %s, word ptr [%s + %d]\n", reg, reg, offset);
+                        } else {
+                            fprintf (state->ofp, "    mov %s, %s ptr [%s + %d]\n", reg, opsize, reg, offset);
+                        }
+                    
+                    }
+                
+                } else {
+                
+                    if (size == 1) {
+                        fprintf (state->ofp, "    movzbl %d(%%%s), %%%s\n", offset, reg, reg);
+                    } else if (size == 2) {
+                        fprintf (state->ofp, "    movzwl %d(%%%s), %%%s\n", offset, reg, reg);
+                    } else {
+                        fprintf (state->ofp, "    movl %d(%%%s), %%%s\n", offset, reg, reg);
+                    }
+                
+                }
+            
+            }
+        
+        }
+    
+    }
+
+}
+
+static void emit_apply_postfix_member_incdec_now (const char *reg, enum token_kind op) {
+
+    if (!postfix_member_seen) {
+        return;
+    }
+    
+    if (state->ofp) {
+    
+        const char *insn = op == TOK_INCR ? "inc" : "dec";
+        (void) reg;
+        
+        if (postfix_member_pointer_depth > 0) {
+        
+            int step = postfix_member_pointed_size;
+            const char *arith = op == TOK_INCR ? "add" : "sub";
+            
+            if (step <= 0) {
+                step = 1;
+            }
+            
+            if (step == 1) {
+                /* Falling through keeps the compact inc/dec form for char *. */
+            } else if (state->syntax & ASM_SYNTAX_INTEL) {
+            
+                if (state->syntax & ASM_SYNTAX_NASM) {
+                    fprintf (state->ofp, "    %s dword [edx + %d], %d\n", arith, postfix_member_offset, step);
+                } else {
+                    fprintf (state->ofp, "    %s dword ptr [edx + %d], %d\n", arith, postfix_member_offset, step);
+                }
+                
+                return;
+            
+            } else {
+            
+                fprintf (state->ofp, "    %sl $%d, %d(%%edx)\n", arith, step, postfix_member_offset);
+                return;
+            }
+        
+        }
+        
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+        
+            const char *opsize = "dword";
+            
+            if (postfix_member_size == 1) {
+                opsize = "byte";
+            } else if (postfix_member_size == 2) {
+                opsize = "word";
+            }
+            
+            if (state->syntax & ASM_SYNTAX_NASM) {
+                fprintf (state->ofp, "    %s %s [edx + %d]\n", insn, opsize, postfix_member_offset);
+            } else {
+                fprintf (state->ofp, "    %s %s ptr [edx + %d]\n", insn, opsize, postfix_member_offset);
+            }
+        
+        } else {
+        
+            if (postfix_member_size == 1) {
+                fprintf (state->ofp, "    %sb %d(%%edx)\n", insn, postfix_member_offset);
+            } else if (postfix_member_size == 2) {
+                fprintf (state->ofp, "    %sw %d(%%edx)\n", insn, postfix_member_offset);
+            } else {
+                fprintf (state->ofp, "    %sl %d(%%edx)\n", insn, postfix_member_offset);
+            }
+        
+        }
+    
+    }
+
+}
+
+static int rhs_text_is_plain_aggregate_lvalue_now (const char *p) {
+
+    int paren_depth = 0;
+    int bracket_depth = 0;
+    int saw_ident = 0;
+    
+    if (!p) {
+        return 1;
+    }
+    
+    while (*p) {
+    
+        unsigned char ch = (unsigned char) *p;
+        
+        if (ch == '\'' || ch == '"') {
+        
+            int quote = ch;
+            p++;
+            
+            while (*p && *p != quote) {
+            
+                if (*p == '\\' && p[1]) {
+                    p += 2;
+                } else {
+                    p++;
+                }
+            
+            }
+            
+            if (*p == quote) {
+                p++;
+            }
+            
+            continue;
+        
+        }
+        
+        if (ch == '(') {
+        
+            paren_depth++;
+            p++;
+            
+            continue;
+        
+        }
+        
+        if (ch == ')') {
+        
+            if (paren_depth == 0 && bracket_depth == 0) {
+                break;
+            }
+            
+            if (paren_depth > 0) {
+                paren_depth--;
+            }
+            
+            p++;
+            continue;
+        
+        }
+        
+        if (ch == '[') {
+        
+            bracket_depth++;
+            p++;
+            
+            continue;
+        
+        }
+        
+        if (ch == ']') {
+        
+            if (bracket_depth > 0) {
+                bracket_depth--;
+            }
+            
+            p++;
+            continue;
+        
+        }
+        
+        if (paren_depth == 0 && bracket_depth == 0) {
+        
+            if (ch == ';' || ch == ',') {
+                break;
+            }
+            
+            if (ch == '-' && p[1] == '>') {
+            
+                p += 2;
+                continue;
+            
+            }
+            
+            if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '%' ||
+                ch == '&' || ch == '|' || ch == '^' || ch == '?' || ch == ':') {
+                return 0;
+            }
+        
+        }
+        
+        if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '_') {
+            saw_ident = 1;
+        }
+        
+        p++;
+    
+    }
+
+    return saw_ident;
+
+}
+
+static int emit_aggregate_copy_from_current_rhs_to_addr_reg_now (const char *addr_reg, int offset, int size);
+static int emit_parse_postfix_copy_source_address_now (const char *reg, struct local_symbol *src, const char *src_name, const char *name_start, const char *name_caret, unsigned long name_line);
+
+static int emit_store_assignment_to_aggregate_address_now (const char *addr_reg, int size, const char *name_start, const char *name_caret, unsigned long name_line) {
+
+    enum token_kind op;
+    
+    if (!is_assignment_operator (tok.kind) || size <= (DATA_LLONG & 0x1f)) {
+        return 0;
+    }
+    
+    op = tok.kind;
+    get_token ();
+    
+    if (op == TOK_ASSIGN && tok.kind == TOK_IDENT && tok.ident && token_identifier_is_function_call_rhs_now ()) {
+    
+        char *rhs_name = xstrdup (tok.ident);
+        
+        const char *rhs_start = tok.start ? tok.start : name_start;
+        const char *rhs_caret = tok.caret ? tok.caret : name_caret;
+        
+        unsigned long rhs_line = get_line_number ();
+        emit_push_reg_now (addr_reg);
+        
+        pending_struct_return_lhs = 0;
+        pending_struct_return_global_name = 0;
+        pending_struct_return_stack_address = 1;
+        pending_struct_return_stack_offset = 0;
+        
+        get_token ();
+        emit_call_identifier_to_reg_now (rhs_name, "eax", rhs_start, rhs_caret, rhs_line);
+        
+        pending_struct_return_stack_address = 0;
+        pending_struct_return_stack_offset = 0;
+        
+        emit_pop_reg_now ("edx");
+        free (rhs_name);
+        
+        return 1;
+    
+    }
+    
+    report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "aggregate assignment expression not implemented");
+    
+    skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+    return 1;
+
+}
+
+static int emit_store_assignment_to_postfix_member_now (const char *reg) {
+
+    enum token_kind op;
+    int assign_member_offset;
+    int assign_member_size;
+    
+    if (!postfix_member_seen || !is_assignment_operator (tok.kind)) {
+        return 0;
+    }
+    
+    op = tok.kind;
+    assign_member_offset = postfix_member_offset;
+    assign_member_size = postfix_member_size;
+    
+    get_token ();
+    
+    if (state->ofp) {
+    
+        if (op == TOK_ASSIGN) {
+        
+            if (postfix_member_is_floating || token_is_floating_constant_now ()) {
+            
+                emit_push_reg_now ("edx");
+                emit_load_floating_rhs_expression_now (assign_member_size);
+                
+                emit_pop_reg_now ("edx");
+                emit_store_floating_member_to_addr_reg_now ("edx", assign_member_offset, assign_member_size);
+                
+                return 1;
+            
+            }
+            
+            if (assign_member_size > (DATA_LLONG & 0x1f) &&
+                tok.kind == TOK_IDENT && tok.ident && token_identifier_is_function_call_rhs_now ()) {
+            
+                char *rhs_name = xstrdup (tok.ident);
+                
+                const char *rhs_start = tok.start;
+                const char *rhs_caret = tok.caret;
+                
+                unsigned long rhs_line = get_line_number ();
+                emit_push_reg_now ("edx");
+                
+                pending_struct_return_lhs = 0;
+                pending_struct_return_global_name = 0;
+                pending_struct_return_stack_address = 1;
+                pending_struct_return_stack_offset = assign_member_offset;
+                
+                get_token ();
+                emit_call_identifier_to_reg_now (rhs_name, reg, rhs_start, rhs_caret, rhs_line);
+                
+                pending_struct_return_stack_address = 0;
+                pending_struct_return_stack_offset = 0;
+                
+                emit_pop_reg_now ("edx");
+                free (rhs_name);
+                
+                return 1;
+            
+            }
+            
+            if (emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("edx", assign_member_offset, assign_member_size)) {
+                return 1;
+            }
+            
+            emit_push_reg_now ("edx");
+            
+            emit_load_assignment_rhs_expression_to_reg (reg);
+            emit_pop_reg_now ("edx");
+        
+        } else {
+        
+            emit_push_reg_now ("edx");
+            emit_push_reg_now (reg);
+            
+            emit_load_assignment_rhs_expression_to_reg ("edx");
+            emit_pop_reg_now (reg);
+            
+            emit_assignment_binary_op (op, 0);
+            emit_pop_reg_now ("edx");
+        
+        }
+        
+        emit_store_member_to_addr_reg_now ("edx", assign_member_offset, reg, assign_member_size);
+    
+    } else {
+        emit_load_assignment_rhs_expression_to_reg (reg);
+    }
+    
+    return 1;
+
+}
+
+static int parenthesized_function_designator_call_now (void) {
+
+    const char *p;
+    
+    if (tok.kind != TOK_IDENT || !tok.caret || !tok.ident) {
+        return 0;
+    }
+    
+    p = tok.caret + strlen (tok.ident);
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    if (*p != ')') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    return *p == '(';
+
+}
+
+static void emit_load_deref_reg_now (const char *reg, int size) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        if (state->syntax & ASM_SYNTAX_NASM) {
+        
+            if (size == (DATA_CHAR & 0x1f)) {
+                fprintf (state->ofp, "    movzx %s, byte [%s]\n", reg, reg);
+            } else if (size == (DATA_SHORT & 0x1f)) {
+                fprintf (state->ofp, "    movzx %s, word [%s]\n", reg, reg);
+            } else {
+                fprintf (state->ofp, "    mov %s, dword [%s]\n", reg, reg);
+            }
+        
+        } else {
+        
+            if (size == (DATA_CHAR & 0x1f)) {
+                fprintf (state->ofp, "    movzx %s, byte ptr [%s]\n", reg, reg);
+            } else if (size == (DATA_SHORT & 0x1f)) {
+                fprintf (state->ofp, "    movzx %s, word ptr [%s]\n", reg, reg);
+            } else {
+                fprintf (state->ofp, "    mov %s, dword ptr [%s]\n", reg, reg);
+            }
+        
+        }
+    
+    } else {
+    
+        if (size == (DATA_CHAR & 0x1f)) {
+            fprintf (state->ofp, "    movzbl (%%%s), %%%s\n", reg, reg);
+        } else if (size == (DATA_SHORT & 0x1f)) {
+            fprintf (state->ofp, "    movzwl (%%%s), %%%s\n", reg, reg);
+        } else {
+            fprintf (state->ofp, "    movl (%%%s), %%%s\n", reg, reg);
+        }
+    
+    }
+
+}
+
+static void emit_store_reg_to_deref_reg_now (const char *addr_reg, const char *value_reg, int size) {
+
+    if (suppress_next_struct_return_scalar_store) {
+    
+        suppress_next_struct_return_scalar_store = 0;
+        return;
+    
+    }
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        if (state->syntax & ASM_SYNTAX_NASM) {
+        
+            if (size == (DATA_CHAR & 0x1f)) {
+                fprintf (state->ofp, "    mov byte [%s], %cl\n", addr_reg, value_reg[1]);
+            } else if (size == (DATA_SHORT & 0x1f)) {
+                fprintf (state->ofp, "    mov word [%s], %cx\n", addr_reg, value_reg[1]);
+            } else {
+                fprintf (state->ofp, "    mov dword [%s], %s\n", addr_reg, value_reg);
+            }
+        
+        } else {
+        
+            if (size == (DATA_CHAR & 0x1f)) {
+                fprintf (state->ofp, "    mov byte ptr [%s], %cl\n", addr_reg, value_reg[1]);
+            } else if (size == (DATA_SHORT & 0x1f)) {
+                fprintf (state->ofp, "    mov word ptr [%s], %cx\n", addr_reg, value_reg[1]);
+            } else {
+                fprintf (state->ofp, "    mov dword ptr [%s], %s\n", addr_reg, value_reg);
+            }
+        
+        }
+    
+    } else {
+    
+        if (size == (DATA_CHAR & 0x1f)) {
+            fprintf (state->ofp, "    movb %%%cl, (%%%s)\n", value_reg[1], addr_reg);
+        } else if (size == (DATA_SHORT & 0x1f)) {
+            fprintf (state->ofp, "    movw %%%cx, (%%%s)\n", value_reg[1], addr_reg);
+        } else {
+            fprintf (state->ofp, "    movl %%%s, (%%%s)\n", value_reg, addr_reg);
+        }
+    
+    }
+
+}
+
+static int emit_handle_subscript_after_loaded_pointer_to_reg_now (const char *reg, int pointer_depth, int pointed_size) {
+
+    int subscript_elem_size;
+    
+    if (tok.kind != TOK_LBRACK) {
+        return 0;
+    }
+    
+    subscript_elem_size = pointer_depth > 1 ? (DATA_PTR & 0x1f) : pointed_size;
+    
+    if (subscript_elem_size <= 0) {
+        subscript_elem_size = DATA_INT & 0x1f;
+    }
+    
+    emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, subscript_elem_size);
+    
+    if (is_assignment_operator (tok.kind)) {
+    
+        enum token_kind assign_op = tok.kind;
+        
+        get_token ();
+        emit_push_reg_now (reg);
+        
+        if (assign_op == TOK_ASSIGN) {
+            emit_load_assignment_rhs_expression_to_reg (reg);
+        } else {
+        
+            emit_load_deref_reg_now (reg, subscript_elem_size);
+            emit_push_reg_now (reg);
+            
+            emit_load_assignment_rhs_expression_to_reg ("edx");
+            emit_pop_reg_now (reg);
+            
+            emit_assignment_binary_op (assign_op, 0);
+        
+        }
+        
+        emit_pop_reg_now ("edx");
+        
+        emit_store_reg_to_deref_reg_now ("edx", reg, subscript_elem_size);
+        clear_rhs_last_pointer_info ();
+        
+        return 1;
+    
+    }
+    
+    emit_load_deref_reg_now (reg, subscript_elem_size);
+    
+    if (pointer_depth > 1) {
+        set_rhs_last_pointer_info (pointer_depth - 1, pointed_size);
+    } else {
+        clear_rhs_last_pointer_info ();
+    }
+    
+    return 1;
+
+}
+
+static void emit_load_member_from_addr_reg_now (const char *dst_reg, const char *addr_reg, int offset, int size) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        if (state->syntax & ASM_SYNTAX_NASM) {
+        
+            if (size == (DATA_CHAR & 0x1f)) {
+                fprintf (state->ofp, "    movzx %s, byte [%s + %d]\n", dst_reg, addr_reg, offset);
+            } else if (size == (DATA_SHORT & 0x1f)) {
+                fprintf (state->ofp, "    movzx %s, word [%s + %d]\n", dst_reg, addr_reg, offset);
+            } else {
+                fprintf (state->ofp, "    mov %s, dword [%s + %d]\n", dst_reg, addr_reg, offset);
+            }
+        
+        } else {
+        
+            if (size == (DATA_CHAR & 0x1f)) {
+                fprintf (state->ofp, "    movzx %s, byte ptr [%s + %d]\n", dst_reg, addr_reg, offset);
+            } else if (size == (DATA_SHORT & 0x1f)) {
+                fprintf (state->ofp, "    movzx %s, word ptr [%s + %d]\n", dst_reg, addr_reg, offset);
+            } else {
+                fprintf (state->ofp, "    mov %s, dword ptr [%s + %d]\n", dst_reg, addr_reg, offset);
+            }
+        
+        }
+    
+    } else {
+    
+        if (size == (DATA_CHAR & 0x1f)) {
+            fprintf (state->ofp, "    movzbl %d(%%%s), %%%s\n", offset, addr_reg, dst_reg);
+        } else if (size == (DATA_SHORT & 0x1f)) {
+            fprintf (state->ofp, "    movzwl %d(%%%s), %%%s\n", offset, addr_reg, dst_reg);
+        } else {
+            fprintf (state->ofp, "    movl %d(%%%s), %%%s\n", offset, addr_reg, dst_reg);
+        }
+    
+    }
+
+}
+
+static int emit_load_address_of_parenthesized_postfix_to_reg_now (const char *reg) {
+
+    char *name;
+    
+    enum token_kind member_op;
+    struct local_symbol *src;
+    
+    int global_index;
+    
+    const char *current_object_tag_name = 0;
+    int current_object_size = 0;
+    
+    if (tok.kind != TOK_LPAREN) {
+        return 0;
+    }
+    
+    get_token ();
+    
+    if (tok.kind == TOK_LPAREN) {
+    
+        int64_s offset_value;
+        
+        if (parse_constexpr_null_member_address_after_lparen (&offset_value)) {
+        
+            emit_load_const32_to_reg_now (reg, offset_value);
+            return 1;
+        
+        }
+    
+    }
+    
+    if (tok.kind != TOK_IDENT) {
+    
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after '&('");
+        
+        expect (TOK_RPAREN, ")");
+        return 1;
+    
+    }
+    
+    name = xstrdup (tok.ident);
+    get_token ();
+    
+    src = find_local_symbol (name);
+    global_index = find_global_symbol (name);
+    
+    if (src) {
+    
+        if (src->is_array) {
+        
+            current_object_size = src->pointed_size > 0 ? src->pointed_size : src->size;
+            current_object_tag_name = src->pointed_tag_name;
+            
+            if (src->is_static && src->static_label) {
+                emit_load_symbol_address_to_reg_now (reg, src->static_label, 0, 0);
+            } else {
+                emit_load_symbol_address_to_reg_now (reg, 0, src->offset, 1);
+            }
+        
+        } else if (src->pointer_depth > 0) {
+        
+            current_object_size = src->pointed_size > 0 ? src->pointed_size : DATA_PTR;
+            current_object_tag_name = src->pointed_tag_name;
+            
+            if (src->is_static && src->static_label) {
+                emit_load_global_to_reg (reg, src->static_label, DATA_PTR);
+            
+            } else {
+                emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+            }
+        
+        } else if (src->is_static && src->static_label) {
+        
+            current_object_size = src->size;
+            emit_load_address_to_reg_now (reg, src->static_label);
+        
+        } else {
+        
+            current_object_size = src->size;
+            
+            /**
+             * A typedef such as section_t can hide the pointer depth from this
+             * older parser.  For &(p->member), the base must be the pointer
+             * value stored in the local, not the address of the local slot.
+             *
+             * Keep the pointed aggregate tag as well.  Without that, address-of
+             * member expressions such as &symbol->next can fall back to an
+             * unqualified member-name lookup and pick another struct's "next"
+             * field.  That emitted symbol + 24 instead of symbol + 36 for
+             * struct symbol::next, corrupting symbol->section in pdas.
+             */
+            if (tok.kind == TOK_ARROW && src->size == (DATA_PTR & 0x1f)) {
+            
+                if (src->pointed_tag_name) {
+                
+                    struct aggregate_tag_entry *entry = find_aggregate_tag (src->pointed_tag_name, 0);
+                    current_object_tag_name = src->pointed_tag_name;
+                    
+                    if (entry) {
+                        current_object_size = entry->size;
+                    }
+                
+                }
+                
+                emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+            
+            } else {
+                emit_load_local_address_to_reg_now (reg, src->offset);
+            }
+        
+        }
+    
+    } else if (global_index >= 0) {
+    
+        if (get_global_symbol_array (name)) {
+        
+            current_object_size = get_global_symbol_pointed_size (name);
+            emit_load_symbol_address_to_reg_now (reg, name, 0, 0);
+        
+        } else if (get_global_symbol_pointer_depth (name) > 0 || (tok.kind == TOK_ARROW && get_global_symbol_size (name) == (DATA_PTR & 0x1f))) {
+        
+            current_object_size = get_global_symbol_pointed_size (name);
+            
+            if (current_object_size <= 0) {
+                current_object_size = DATA_PTR & 0x1f;
+            }
+            
+            emit_load_global_to_reg (reg, name, DATA_PTR);
+        
+        } else {
+        
+            current_object_size = get_global_symbol_size (name);
+            emit_load_address_to_reg_now (reg, name);
+        
+        }
+    
+    } else {
+    
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "unknown symbol '%s'", name);
+        free (name);
+        
+        expect (TOK_RPAREN, ")");
+        return 1;
+    
+    }
+    
+    if (tok.kind == TOK_LBRACK) {
+    
+        int elem_size = DATA_INT & 0x1f;
+        
+        if (src) {
+        
+            elem_size = src->is_array ? (src->pointer_depth ? DATA_PTR : (src->array_element_size > 0 ? src->array_element_size : src->pointed_size)) :
+                (src->pointer_depth > 1 ? DATA_PTR : src->pointed_size);
+        
+        } else if (global_index >= 0) {
+        
+            elem_size = get_global_symbol_array (name) ?
+                (get_global_symbol_pointer_depth (name) ? DATA_PTR : (get_global_symbol_array_element_size (name) > 0 ? get_global_symbol_array_element_size (name) : get_global_symbol_pointed_size (name))) :
+                    (get_global_symbol_pointer_depth (name) > 1 ? DATA_PTR : get_global_symbol_pointed_size (name));
+        
+        }
+        
+        if (elem_size <= 0) {
+            elem_size = DATA_INT & 0x1f;
+        }
+        
+        emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size);
+    
+    }
+    
+    free (name);
+    
+    while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+    
+        char *member;
+        
+        const char *member_start;
+        const char *member_caret;
+        
+        unsigned long member_line;
+        
+        int offset;
+        int size;
+        int elem_size;
+        int pointer_depth;
+        
+        member_op = tok.kind;
+        get_token ();
+        
+        member_start = tok.start;
+        member_caret = tok.caret;
+        member_line = get_line_number ();
+        
+        if (tok.kind != TOK_IDENT) {
+        
+            report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+            
+            expect (TOK_RPAREN, ")");
+            return 1;
+        
+        }
+        
+        member = xstrdup (tok.ident);
+        get_token ();
+        
+        offset = 0;
+        size = DATA_INT & 0x1f;
+        elem_size = DATA_INT & 0x1f;
+        pointer_depth = 0;
+        
+        if (!find_member_info_ex_bounded (member, current_object_size, current_object_tag_name, &offset, &size, &elem_size, &pointer_depth, 0, 0)) {
+        
+            report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+            
+            free (member);
+            expect (TOK_RPAREN, ")");
+            
+            return 1;
+        
+        }
+        
+        current_object_tag_name = last_found_member_tag_name;
+        free (member);
+        
+        if (tok.kind == TOK_LBRACK) {
+        
+            if (pointer_depth > 0) {
+            
+                emit_load_member_from_addr_reg_now (reg, reg, offset, DATA_PTR & 0x1f);
+                emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size);
+            
+            } else {
+            
+                if (state->ofp && offset != 0) {
+                
+                    if (state->syntax & ASM_SYNTAX_INTEL) {
+                        fprintf (state->ofp, "    add %s, %d\n", reg, offset);
+                    } else {
+                        fprintf (state->ofp, "    addl $%d, %%%s\n", offset, reg);
+                    }
+                
+                }
+                
+                emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size);
+            
+            }
+        
+        } else {
+        
+            if (state->ofp && offset != 0) {
+            
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                    fprintf (state->ofp, "    add %s, %d\n", reg, offset);
+                } else {
+                    fprintf (state->ofp, "    addl $%d, %%%s\n", offset, reg);
+                }
+            
+            }
+        
+        }
+        
+        if (pointer_depth > 0) {
+            current_object_size = elem_size > 0 ? elem_size : DATA_PTR;
+        } else {
+            current_object_size = size;
+        }
+    
+    }
+    
+    expect (TOK_RPAREN, ")");
+    return 1;
+
+}
+
+static void emit_store_member_to_addr_reg_now (const char *addr_reg, int offset, const char *value_reg, int size) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (offset != 0) {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+            fprintf (state->ofp, "    add %s, %d\n", addr_reg, offset);
+        } else {
+            fprintf (state->ofp, "    addl $%d, %%%s\n", offset, addr_reg);
+        }
+    
+    }
+    
+    emit_store_reg_to_deref_reg_now (addr_reg, value_reg, size);
+
+}
+
+static void emit_store_floating_member_to_addr_reg_now (const char *addr_reg, int offset, int size) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (offset != 0) {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+            fprintf (state->ofp, "    add %s, %d\n", addr_reg, offset);
+        } else {
+            fprintf (state->ofp, "    addl $%d, %%%s\n", offset, addr_reg);
+        }
+    
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+        fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? "    fstp %s [%s]\n" : "    fstp %s ptr [%s]\n", size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", addr_reg);
+    } else {
+        fprintf (state->ofp, "    fstp%s (%%%s)\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", addr_reg);
+    }
+
+}
+
+static void emit_load_symbol_address_for_copy_now (const char *reg, struct local_symbol *sym, const char *name) {
+
+    if (sym) {
+    
+        if (sym->is_static && sym->static_label) {
+            emit_load_address_to_reg_now (reg, sym->static_label);
+        } else {
+            emit_load_local_address_to_reg_now (reg, sym->offset);
+        }
+    
+    } else {
+        emit_load_address_to_reg_now (reg, name);
+    }
+
+}
+
+static void emit_copy_fixed_size_now (int size) {
+
+    int offset = 0;
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    while (size - offset >= 4) {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+        
+            fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? "    mov ecx, dword [eax + %d]\n" : "    mov ecx, dword ptr [eax + %d]\n", offset);
+            fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? "    mov dword [edx + %d], ecx\n" : "    mov dword ptr [edx + %d], ecx\n", offset);
+        
+        } else {
+        
+            fprintf (state->ofp, "    movl %d(%%eax), %%ecx\n", offset);
+            fprintf (state->ofp, "    movl %%ecx, %d(%%edx)\n", offset);
+        
+        }
+        
+        offset += 4;
+    
+    }
+    
+    while (size - offset >= 2) {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+        
+            fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? "    mov cx, word [eax + %d]\n" : "    mov cx, word ptr [eax + %d]\n", offset);
+            fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? "    mov word [edx + %d], cx\n" : "    mov word ptr [edx + %d], cx\n", offset);
+        
+        } else {
+        
+            fprintf (state->ofp, "    movw %d(%%eax), %%cx\n", offset);
+            fprintf (state->ofp, "    movw %%cx, %d(%%edx)\n", offset);
+        
+        }
+        
+        offset += 2;
+    
+    }
+    
+    while (size - offset >= 1) {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+        
+            fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? "    mov cl, byte [eax + %d]\n" : "    mov cl, byte ptr [eax + %d]\n", offset);
+            fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? "    mov byte [edx + %d], cl\n" : "    mov byte ptr [edx + %d], cl\n", offset);
+        
+        } else {
+        
+            fprintf (state->ofp, "    movb %d(%%eax), %%cl\n", offset);
+            fprintf (state->ofp, "    movb %%cl, %d(%%edx)\n", offset);
+        
+        }
+        
+        offset++;
+    
+    }
+
+}
+
+static void emit_memcpy_symbol_to_symbol_now (struct local_symbol *dst, const char *dst_name, struct local_symbol *src, const char *src_name, int size) {
+
+    emit_load_symbol_address_for_copy_now ("eax", src, src_name);
+    emit_load_symbol_address_for_copy_now ("edx", dst, dst_name);
+    emit_copy_fixed_size_now (size);
+
+}
+
+static int token_identifier_is_function_call_rhs_now (void) {
+
+    struct token *saved_tok;
+    int is_call;
+    
+    if (tok.kind != TOK_IDENT || !tok.ident) {
+        return 0;
+    }
+    
+    /**
+     * Do not inspect tok.start/tok.caret to decide whether this identifier is
+     * followed by a call.  After macro substitution the spelling in the source
+     * can be a different length from tok.ident, e.g.
+     *
+     *     #define cc_parse_type ccpartype
+     *     member.type = cc_parse_type(reader);
+     *
+     * tok.ident is "ccpartype" but tok.caret still points into the original
+     * text "cc_parse_type(reader)".  The old spelling-based test missed the
+     * '(' and the aggregate-copy fast path treated the function symbol as an
+     * object, leaving the argument list unconsumed and producing "expected ;".
+     */
+    saved_tok = xmalloc (sizeof (*saved_tok));
+    *saved_tok = tok;
+    
+    if (tok.ident) {
+        saved_tok->ident = xstrdup (tok.ident);
+    }
+    
+    if (tok.start) {
+    
+        const char *old_start = tok.start;
+        const char *old_caret = tok.caret;
+        
+        saved_tok->start = xstrdup (old_start);
+        
+        if (old_caret && old_caret >= old_start) {
+            saved_tok->caret = saved_tok->start + (old_caret - old_start);
+        } else {
+            saved_tok->caret = saved_tok->start;
+        }
+    
+    }
+    
+    get_token ();
+    
+    is_call = (tok.kind == TOK_LPAREN);
+    unget_token (saved_tok);
+    
+    return is_call;
+
+}
+
+static struct token *clone_current_token_now (void) {
+
+    struct token *saved_tok = xmalloc (sizeof (*saved_tok));
+    *saved_tok = tok;
+    
+    if (tok.ident) {
+        saved_tok->ident = xstrdup (tok.ident);
+    }
+    
+    if (tok.start) {
+    
+        const char *old_start = tok.start;
+        const char *old_caret = tok.caret;
+        
+        saved_tok->start = xstrdup (old_start);
+        
+        if (old_caret && old_caret >= old_start) {
+            saved_tok->caret = saved_tok->start + (old_caret - old_start);
+        } else {
+            saved_tok->caret = saved_tok->start;
+        }
+    
+    }
+    
+    return saved_tok;
+
+}
+
+static int emit_aggregate_copy_from_current_rhs_to_addr_reg_now (const char *addr_reg, int offset, int size) {
+
+    struct local_symbol *rhs_sym;
+    char *rhs_name;
+    
+    const char *rhs_start;
+    const char *rhs_caret;
+    
+    unsigned long rhs_line;
+    
+    int rhs_global_index;
+    int rhs_size;
+    
+    int source_size;
+    int source_ready;
+    
+    /*
+     * Only use the aggregate-copy fast path when the destination is
+     * itself an aggregate object.  Pointer members can legally be assigned
+     * an array object, e.g. fp->intBuffer = buffer1; in pdpclib.  The RHS
+     * symbol then has a large array size, but the LHS is only a pointer, so
+     * emitting a fixed-size aggregate copy corrupts the FILE object.
+     */
+    if (size < (DATA_LLONG & 0x1f)) {
+        return 0;
+    }
+    
+    if (tok.kind == TOK_STAR) {
+    
+        struct token *saved_tok = clone_current_token_now ();
+        struct local_symbol *ptr_sym;
+        
+        char *ptr_name;
+        
+        int ptr_global_index;
+        int ptr_depth;
+        int ptr_pointed_size;
+        
+        get_token ();
+        
+        if (tok.kind != TOK_IDENT || !tok.ident) {
+        
+            unget_token (saved_tok);
+            return 0;
+        
+        }
+        
+        ptr_name = xstrdup (tok.ident);
+        ptr_sym = find_local_symbol (ptr_name);
+        ptr_global_index = find_global_symbol (ptr_name);
+        
+        if (!ptr_sym && ptr_global_index < 0) {
+        
+            free (ptr_name);
+            
+            unget_token (saved_tok);
+            return 0;
+        
+        }
+        
+        ptr_depth = ptr_sym ? ptr_sym->pointer_depth : get_global_symbol_pointer_depth (ptr_name);
+        ptr_pointed_size = ptr_sym ? ptr_sym->pointed_size : get_global_symbol_pointed_size (ptr_name);
+        
+        if (!ptr_sym && ptr_global_index >= 0
+            && get_global_symbol_kind (ptr_name) == GLOBAL_SYMBOL_FUNCTION) {
+        
+            const char *call_start = tok.start;
+            const char *call_caret = tok.caret;
+            unsigned long call_line = get_line_number ();
+            
+            get_token ();
+            
+            if (tok.kind != TOK_LPAREN || ptr_depth <= 0
+                || (ptr_depth == 1 && ptr_pointed_size > 0 && ptr_pointed_size < size)) {
+            
+                free (ptr_name);
+                
+                unget_token (saved_tok);
+                return 0;
+            
+            }
+            
+            emit_push_reg_now (addr_reg);
+            emit_call_identifier_to_reg_now (ptr_name, "eax", call_start, call_caret, call_line);
+            emit_pop_reg_now ("edx");
+            
+            if (offset != 0 && state->ofp) {
+            
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                    fprintf (state->ofp, "    add edx, %d\n", offset);
+                } else {
+                    fprintf (state->ofp, "    addl $%d, %%edx\n", offset);
+                }
+            
+            }
+            
+            emit_copy_fixed_size_now (size);
+            
+            free (ptr_name);
+            return 1;
+        
+        }
+        
+        /*
+         * For aggregate initialization from a dereferenced pointer, keep the
+         * full pointed-to object size.  The old code masked pointed_size with
+         * 0x1f as if it were a DATA_* scalar type.  That turns e.g.
+         *
+         *     struct hashtab old_hashtab = *hashtab;
+         *
+         * from a 44-byte copy into a rejected aggregate fast path because
+         * 44 & 0x1f == 12.  The initializer then falls back to scalar code
+         * and copies only the first word, leaving old_hashtab mostly
+         * uninitialized and crashing in rehash().
+         */
+        source_size = ptr_depth > 1 ? (DATA_PTR & 0x1f) : ptr_pointed_size;
+        
+        if (ptr_depth <= 0 || source_size < size) {
+        
+            free (ptr_name);
+            
+            unget_token (saved_tok);
+            return 0;
+        
+        }
+        
+        emit_push_reg_now (addr_reg);
+        
+        if (ptr_sym) {
+        
+            if (ptr_sym->is_static && ptr_sym->static_label) {
+                emit_load_global_to_reg ("eax", ptr_sym->static_label, DATA_PTR);
+            } else {
+                emit_load_local_to_reg ("eax", ptr_sym->offset, DATA_PTR);
+            }
+        
+        } else {
+            emit_load_global_to_reg ("eax", ptr_name, DATA_PTR);
+        }
+        
+        get_token ();
+        
+        if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+        
+            enum token_kind member_op = tok.kind;
+            char *member;
+            
+            int member_offset = 0;
+            int member_size = DATA_INT & 0x1f;
+            int member_elem_size = DATA_INT & 0x1f;
+            int member_pointer_depth = 0;
+            int member_is_array = 0;
+            int member_incdec = 0;
+            
+            enum token_kind member_incdec_op = TOK_EOF;
+            const char *object_tag_name = ptr_sym ? ptr_sym->pointed_tag_name : 0;
+            
+            int object_size = ptr_sym ? ptr_sym->pointed_size : get_global_symbol_pointed_size (ptr_name);
+            get_token ();
+            
+            if (tok.kind != TOK_IDENT || !tok.ident) {
+            
+                free (ptr_name);
+                
+                unget_token (saved_tok);
+                return 0;
+            
+            }
+            
+            member = xstrdup (tok.ident);
+            get_token ();
+            
+            if (!find_member_info_ex_bounded (member, object_size, object_tag_name,
+                                              &member_offset, &member_size, &member_elem_size,
+                                              &member_pointer_depth, &member_is_array, 0)) {
+            
+                free (member);
+                free (ptr_name);
+                
+                unget_token (saved_tok);
+                return 0;
+            
+            }
+            
+            if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+            
+                member_incdec = 1;
+                member_incdec_op = tok.kind;
+                
+                get_token ();
+            
+            }
+            
+            if (member_op != TOK_ARROW || member_pointer_depth <= 0 || member_elem_size < size) {
+            
+                free (member);
+                free (ptr_name);
+                
+                unget_token (saved_tok);
+                return 0;
+            
+            }
+            
+            emit_push_reg_now (addr_reg);
+            
+            if (ptr_sym) {
+            
+                if (ptr_sym->is_static && ptr_sym->static_label) {
+                    emit_load_global_to_reg ("ecx", ptr_sym->static_label, DATA_PTR);
+                } else {
+                    emit_load_local_to_reg ("ecx", ptr_sym->offset, DATA_PTR);
+                }
+            
+            } else {
+                emit_load_global_to_reg ("ecx", ptr_name, DATA_PTR);
+            }
+            
+            if (state->ofp) {
+            
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                
+                    fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ?
+                        "    mov eax, dword [ecx + %d]\n" :
+                        "    mov eax, dword ptr [ecx + %d]\n", member_offset);
+                    
+                    if (member_incdec) {
+                    
+                        fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ?
+                            "    %s dword [ecx + %d], %d\n" :
+                            "    %s dword ptr [ecx + %d], %d\n",
+                            member_incdec_op == TOK_INCR ? "add" : "sub",
+                            member_offset, member_elem_size);
+                    
+                    }
+                
+                } else {
+                
+                    fprintf (state->ofp, "    movl %d(%%ecx), %%eax\n", member_offset);
+                    
+                    if (member_incdec) {
+                    
+                        fprintf (state->ofp, "    %sl $%d, %d(%%ecx)\n",
+                            member_incdec_op == TOK_INCR ? "add" : "sub",
+                            member_elem_size, member_offset);
+                    
+                    }
+                
+                }
+            
+            }
+            
+            emit_pop_reg_now ("edx");
+            
+            if (offset != 0 && state->ofp) {
+            
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                    fprintf (state->ofp, "    add edx, %d\n", offset);
+                } else {
+                    fprintf (state->ofp, "    addl $%d, %%edx\n", offset);
+                }
+            
+            }
+            
+            emit_copy_fixed_size_now (size);
+            
+            free (member);
+            free (ptr_name);
+            
+            return 1;
+        
+        }
+        
+        emit_pop_reg_now ("edx");
+        
+        if (offset != 0 && state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    add edx, %d\n", offset);
+            } else {
+                fprintf (state->ofp, "    addl $%d, %%edx\n", offset);
+            }
+        
+        }
+        
+        emit_copy_fixed_size_now (size);
+        
+        free (ptr_name);
+        return 1;
+    
+    }
+    
+    if (tok.kind == TOK_LPAREN) {
+    
+        struct token *saved_tok = clone_current_token_now ();
+        struct local_symbol *ptr_sym;
+        
+        char *ptr_name;
+        
+        const char *ptr_start;
+        const char *ptr_caret;
+        
+        unsigned long ptr_line;
+        
+        int ptr_global_index;
+        int ptr_depth;
+        
+        const char *ptr_tag_name;
+        
+        int current_object_size;
+        int have_direct_object_pointer;
+        
+        get_token ();
+        
+        if (tok.kind != TOK_STAR) {
+        
+            unget_token (saved_tok);
+            return 0;
+        
+        }
+        
+        get_token ();
+        
+        if (tok.kind != TOK_IDENT || !tok.ident) {
+        
+            unget_token (saved_tok);
+            return 0;
+        
+        }
+        
+        ptr_name = xstrdup (tok.ident);
+        ptr_start = tok.start;
+        ptr_caret = tok.caret;
+        ptr_line = get_line_number ();
+        ptr_sym = find_local_symbol (ptr_name);
+        ptr_global_index = find_global_symbol (ptr_name);
+        
+        if (!ptr_sym && ptr_global_index < 0) {
+        
+            free (ptr_name);
+            
+            unget_token (saved_tok);
+            return 0;
+        
+        }
+        
+        ptr_depth = ptr_sym ? ptr_sym->pointer_depth : get_global_symbol_pointer_depth (ptr_name);
+        ptr_tag_name = ptr_sym ? ptr_sym->pointed_tag_name : 0;
+        
+        current_object_size = ptr_sym ? ptr_sym->pointed_size : get_global_symbol_pointed_size (ptr_name);
+        source_size = current_object_size;
+        
+        if (ptr_tag_name) {
+        
+            struct aggregate_tag_entry *entry = find_aggregate_tag (ptr_tag_name, 0);
+            
+            if (entry) {
+            
+                current_object_size = entry->size;
+                source_size = current_object_size;
+            
+            }
+        
+        }
+        
+        get_token ();
+        
+        if (tok.kind != TOK_RPAREN || ptr_depth <= 0) {
+        
+            free (ptr_name);
+            
+            unget_token (saved_tok);
+            return 0;
+        
+        }
+        
+        get_token ();
+        
+        if (tok.kind != TOK_ARROW && tok.kind != TOK_DOT) {
+        
+            free (ptr_name);
+            
+            unget_token (saved_tok);
+            return 0;
+        
+        }
+        
+        emit_push_reg_now (addr_reg);
+        
+        if (ptr_sym) {
+        
+            if (ptr_sym->is_static && ptr_sym->static_label) {
+                emit_load_global_to_reg ("eax", ptr_sym->static_label, DATA_PTR);
+            } else {
+                emit_load_local_to_reg ("eax", ptr_sym->offset, DATA_PTR);
+            }
+        
+        } else {
+            emit_load_global_to_reg ("eax", ptr_name, DATA_PTR);
+        }
+        
+        emit_load_deref_reg_now ("eax", DATA_PTR);
+        have_direct_object_pointer = 1;
+        
+        while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT || tok.kind == TOK_LBRACK) {
+        
+            if (tok.kind == TOK_LBRACK) {
+            
+                emit_parse_postfix_subscript_scaled_address_to_reg_now ("eax", DATA_PTR);
+                
+                current_object_size = DATA_PTR;
+                continue;
+            
+            }
+            
+            {
+            
+                enum token_kind member_op = tok.kind;
+                char *member;
+                
+                int member_offset = 0;
+                int member_size = DATA_INT & 0x1f;
+                int member_elem_size = DATA_INT & 0x1f;
+                int member_pointer_depth = 0;
+                int member_is_array = 0;
+                
+                get_token ();
+                
+                if (tok.kind != TOK_IDENT || !tok.ident) {
+                
+                    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret,
+                        "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+                    
+                    free (ptr_name);
+                    return 0;
+                
+                }
+                
+                member = xstrdup (tok.ident);
+                get_token ();
+                
+                if (!find_member_info_ex_bounded (member, current_object_size, ptr_tag_name,
+                                                  &member_offset, &member_size, &member_elem_size,
+                                                  &member_pointer_depth, &member_is_array, 0)) {
+                
+                    report_line_at (get_filename (), ptr_line, REPORT_ERROR, ptr_start, ptr_caret,
+                        "unknown member '%s'", member);
+                    
+                    free (member);
+                    free (ptr_name);
+                    
+                    return 0;
+                
+                }
+                
+                ptr_tag_name = last_found_member_tag_name;
+                
+                if (member_op == TOK_ARROW) {
+                
+                    if (have_direct_object_pointer) {
+                        have_direct_object_pointer = 0;
+                    } else {
+                        emit_load_deref_reg_now ("eax", DATA_PTR);
+                    }
+                
+                } else {
+                    have_direct_object_pointer = 0;
+                }
+                
+                if (member_offset != 0 && state->ofp) {
+                
+                    if (state->syntax & ASM_SYNTAX_INTEL) {
+                        fprintf (state->ofp, "    add eax, %d\n", member_offset);
+                    } else {
+                        fprintf (state->ofp, "    addl $%d, %%eax\n", member_offset);
+                    }
+                
+                }
+                
+                current_object_size = member_size;
+                source_size = member_size;
+                
+                free (member);
+            
+            }
+        
+        }
+        
+        if (source_size < size) {
+        
+            free (ptr_name);
+            return 0;
+        
+        }
+        
+        emit_pop_reg_now ("edx");
+        
+        if (offset != 0 && state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    add edx, %d\n", offset);
+            } else {
+                fprintf (state->ofp, "    addl $%d, %%edx\n", offset);
+            }
+        
+        }
+        
+        emit_copy_fixed_size_now (size);
+        
+        free (ptr_name);
+        return 1;
+    
+    }
+    
+    if (tok.kind != TOK_IDENT) {
+        return 0;
+    }
+    
+    /*
+     * A function-call RHS that returns a struct/union must be parsed by
+     * the normal expression code.  The aggregate-copy fast path is only
+     * for copying an already-existing aggregate object by address.
+     */
+    if (token_identifier_is_function_call_rhs_now ()) {
+        return 0;
+    }
+    
+    if (!rhs_text_is_plain_aggregate_lvalue_now (tok.caret ? tok.caret : tok.start)) {
+        return 0;
+    }
+    
+    rhs_name = xstrdup (tok.ident);
+    rhs_start = tok.start;
+    rhs_caret = tok.caret;
+    rhs_line = get_line_number ();
+
+    rhs_sym = find_local_symbol (rhs_name);
+    rhs_global_index = find_global_symbol (rhs_name);
+    
+    if (!rhs_sym && rhs_global_index < 0) {
+    
+        free (rhs_name);
+        return 0;
+    
+    }
+    
+    rhs_size = rhs_sym ? rhs_sym->size : get_global_symbol_size (rhs_name);
+    
+    {
+    
+        struct token *saved_rhs_tok = clone_current_token_now ();
+        get_token ();
+        
+        if (rhs_size <= (DATA_LLONG & 0x1f) && tok.kind != TOK_DOT && tok.kind != TOK_ARROW && tok.kind != TOK_LBRACK) {
+        
+            free (rhs_name);
+            
+            unget_token (saved_rhs_tok);
+            return 0;
+        
+        }
+        
+        unget_token (saved_rhs_tok);
+    
+    }
+    
+    emit_push_reg_now (addr_reg);
+    get_token ();
+    
+    source_ready = 0;
+    source_size = rhs_size;
+    
+    if (tok.kind == TOK_DOT || tok.kind == TOK_ARROW || tok.kind == TOK_LBRACK) {
+    
+        if (!emit_parse_postfix_copy_source_address_now ("eax", rhs_sym, rhs_name, rhs_start, rhs_caret, rhs_line)) {
+        
+            free (rhs_name);
+            return 0;
+        
+        }
+        
+        source_size = postfix_copy_lvalue_size;
+        source_ready = 1;
+    
+    }
+    
+    if (source_size < size) {
+    
+        free (rhs_name);
+        return 0;
+    
+    }
+    
+    if (!source_ready) {
+        emit_load_symbol_address_for_copy_now ("eax", rhs_sym, rhs_name);
+    }
+    
+    emit_pop_reg_now ("edx");
+    
+    if (offset != 0 && state->ofp) {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+            fprintf (state->ofp, "    add edx, %d\n", offset);
+        } else {
+            fprintf (state->ofp, "    addl $%d, %%edx\n", offset);
+        }
+    
+    }
+    
+    emit_copy_fixed_size_now (size);
+    
+    free (rhs_name);
+    return 1;
+
+}
+
+static void emit_load_member_address_for_copy_now (const char *reg, struct local_symbol *sym, const char *name, enum token_kind member_op, int offset) {
+
+    if (sym) {
+    
+        if (member_op == TOK_DOT || sym->is_array) {
+        
+            if (sym->is_static && sym->static_label) {
+                emit_load_address_to_reg_now (reg, sym->static_label);
+            } else {
+                emit_load_local_address_to_reg_now (reg, sym->offset);
+            }
+        
+        } else if (sym->is_static && sym->static_label) {
+            emit_load_global_to_reg (reg, sym->static_label, DATA_PTR);
+        } else {
+            emit_load_local_to_reg (reg, sym->offset, DATA_PTR);
+        }
+    
+    } else {
+    
+        if (member_op == TOK_DOT || get_global_symbol_array (name)) {
+            emit_load_address_to_reg_now (reg, name);
+        } else {
+            emit_load_global_to_reg (reg, name, DATA_PTR);
+        }
+    
+    }
+    
+    if (offset != 0 && state->ofp) {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+            fprintf (state->ofp, "    add %s, %d\n", reg, offset);
+        } else {
+            fprintf (state->ofp, "    addl $%d, %%%s\n", offset, reg);
+        }
+    
+    }
+
+}
+
+static int emit_parse_postfix_copy_source_address_now (const char *reg, struct local_symbol *src, const char *src_name, const char *name_start, const char *name_caret, unsigned long name_line) {
+
+    int have_address = 0;
+    int have_direct_object_pointer = 0;
+    int last_elem_size = DATA_INT & 0x1f;
+    int last_lvalue_size = DATA_INT & 0x1f;
+    
+    const char *current_object_tag_name = 0;
+    int current_object_size = 0;
+    
+    postfix_copy_lvalue_size = DATA_INT & 0x1f;
+    
+    if (!reg) {
+        reg = "eax";
+    }
+    
+    if (tok.kind == TOK_LPAREN) {
+    
+        if (src) {
+            return 0;
+        }
+        
+        ensure_global_function_symbol (src_name, name_start, name_caret, name_line);
+        emit_call_identifier_to_reg_now (src_name, reg, name_start, name_caret, name_line);
+        
+        have_address = 1;
+        have_direct_object_pointer = 1;
+    
+    }
+    
+    if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+    
+        enum token_kind postfix_op = tok.kind;
+        
+        int pointer_depth;
+        int pointed_size;
+        
+        get_token ();
+        
+        if (tok.kind != TOK_LBRACK) {
+            return 0;
+        }
+        
+        if (src) {
+        
+            pointer_depth = src->pointer_depth;
+            pointed_size = src->pointed_size;
+            
+            if (src->is_static && src->static_label) {
+                emit_load_global_to_reg (reg, src->static_label, DATA_PTR);
+            } else {
+                emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+            }
+        
+        } else if (find_global_symbol (src_name) >= 0) {
+        
+            pointer_depth = get_global_symbol_pointer_depth (src_name);
+            pointed_size = get_global_symbol_pointed_size (src_name);
+            
+            emit_load_global_to_reg (reg, src_name, DATA_PTR);
+        
+        } else {
+        
+            report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", src_name);
+            return 0;
+        
+        }
+        
+        if (pointer_depth <= 0) {
+        
+            report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "subscripted value is not a pointer");
+            return 0;
+        }
+        
+        last_elem_size = pointer_depth > 1 ? (DATA_PTR & 0x1f) : (pointed_size > 0 ? (pointed_size & 0x1f) : (DATA_INT & 0x1f));
+        last_lvalue_size = last_elem_size;
+        
+        have_address = 1;
+        have_direct_object_pointer = 0;
+        
+        emit_incdec_symbol_now (src, src_name, postfix_op, name_line, name_start, name_caret);
+    
+    }
+    
+    while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT || tok.kind == TOK_LBRACK) {
+    
+        if (tok.kind == TOK_LBRACK) {
+        
+            int elem_size = last_elem_size;
+            
+            if (!have_address) {
+            
+                if (src) {
+                
+                    if (src->is_array) {
+                        emit_load_symbol_address_for_copy_now (reg, src, src_name);
+                    } else if (src->is_static && src->static_label) {
+                        emit_load_global_to_reg (reg, src->static_label, DATA_PTR);
+                    } else {
+                        emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+                    }
+                
+                    elem_size = src->is_array ? (src->pointer_depth ? DATA_PTR : (src->array_element_size > 0 ? src->array_element_size : src->pointed_size)) :
+                        (src->pointer_depth > 1 ? DATA_PTR : src->pointed_size);
+                
+                } else {
+                
+                    if (get_global_symbol_array (src_name)) {
+                        emit_load_address_to_reg_now (reg, src_name);
+                    } else {
+                        emit_load_global_to_reg (reg, src_name, DATA_PTR);
+                    }
+                    
+                    elem_size = get_global_symbol_array (src_name) ?
+                        (get_global_symbol_pointer_depth (src_name) ? DATA_PTR : (get_global_symbol_array_element_size (src_name) > 0 ? get_global_symbol_array_element_size (src_name) : get_global_symbol_pointed_size (src_name))) :
+                            (get_global_symbol_pointer_depth (src_name) > 1 ? DATA_PTR : get_global_symbol_pointed_size (src_name));
+                
+                }
+                
+                if ((elem_size & 0x1f) == 0) {
+                    elem_size = DATA_INT & 0x1f;
+                }
+                
+                have_address = 1;
+            
+            }
+            
+            emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size);
+            
+            last_elem_size = elem_size;
+            last_lvalue_size = elem_size;
+            
+            current_object_size = elem_size;
+            continue;
+        
+        }
+        
+        {
+        
+            enum token_kind member_op = tok.kind;
+            char *member;
+            
+            const char *member_start;
+            const char *member_caret;
+            
+            unsigned long member_line;
+            
+            int member_offset = 0;
+            int member_size = DATA_INT & 0x1f;
+            int member_elem_size = DATA_INT & 0x1f;
+            int member_pointer_depth = 0;
+            int member_is_array = 0;
+            
+            get_token ();
+            
+            member_start = tok.start;
+            member_caret = tok.caret;
+            member_line = get_line_number ();
+            
+            if (tok.kind != TOK_IDENT) {
+            
+                report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret,
+                    "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+                
+                return 0;
+            
+            }
+            
+            member = xstrdup (tok.ident);
+            get_token ();
+            
+            if (current_object_size <= 0) {
+            
+                if (src) {
+                
+                    if (member_op == TOK_ARROW && src->pointer_depth > 0) {
+                    
+                        current_object_size = src->pointed_size;
+                        current_object_tag_name = src->pointed_tag_name;
+                    
+                    } else {
+                    
+                        current_object_size = src->size;
+                        current_object_tag_name = src->tag_name;
+                    
+                    }
+                
+                } else {
+                
+                    if (member_op == TOK_ARROW && get_global_symbol_pointer_depth (src_name) > 0) {
+                    
+                        current_object_size = get_global_symbol_pointed_size (src_name);
+                        current_object_tag_name = get_global_symbol_tag_name (src_name);
+                    
+                    } else {
+                    
+                        current_object_size = get_global_symbol_size (src_name);
+                        current_object_tag_name = get_global_symbol_tag_name (src_name);
+                    
+                    }
+                
+                }
+            
+            }
+            
+            if (!find_member_info_ex_bounded (member, current_object_size, current_object_tag_name, &member_offset, &member_size, &member_elem_size, &member_pointer_depth, &member_is_array, 0)) {
+            
+                report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret,
+                    "unknown member '%s'", member);
+                
+                free (member);
+                return 0;
+            
+            }
+            
+            {
+            
+                const char *member_tag_name = last_found_member_tag_name;
+                
+                if (member_is_array && member_pointer_depth > 0) {
+                
+                    /*
+                     * For an array member whose element type is a pointer,
+                     * the subscript step is one pointer.  Do not replace it
+                     * with the pointed aggregate size from the member tag.
+                     */
+                    member_elem_size = DATA_PTR;
+                
+                } else if (member_pointer_depth > 1) {
+                    member_elem_size = DATA_PTR;
+                } else if (member_pointer_depth == 1 && member_tag_name) {
+                
+                    struct aggregate_tag_entry *entry = find_aggregate_tag (member_tag_name, 0);
+                    
+                    if (entry) {
+                        member_elem_size = entry->size;
+                    }
+                
+                }
+                
+                current_object_tag_name = member_tag_name;
+            
+            }
+            
+            free (member);
+            
+            if (!have_address) {
+            
+                emit_load_member_address_for_copy_now (reg, src, src_name, member_op, 0);
+                have_address = 1;
+            
+            } else if (member_op == TOK_ARROW) {
+            
+                if (have_direct_object_pointer) {
+                    have_direct_object_pointer = 0;
+                } else {
+                    emit_load_deref_reg_now (reg, DATA_PTR);
+                }
+            
+            } else {
+                have_direct_object_pointer = 0;
+            }
+            
+            if (tok.kind == TOK_LBRACK && member_pointer_depth > 0 && !member_is_array) {
+                emit_load_member_from_addr_reg_now (reg, reg, member_offset, DATA_PTR & 0x1f);
+            } else if (state->ofp && member_offset != 0) {
+            
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                    fprintf (state->ofp, "    add %s, %d\n", reg, member_offset);
+                } else {
+                    fprintf (state->ofp, "    addl $%d, %%%s\n", member_offset, reg);
+                }
+            
+            }
+            
+            last_elem_size = member_elem_size;
+            last_lvalue_size = member_size;
+            
+            if (member_pointer_depth > 0 || member_is_array) {
+                current_object_size = member_elem_size;
+            } else {
+                current_object_size = member_size;
+            }
+        
+        }
+    
+    }
+    
+    postfix_copy_lvalue_size = last_lvalue_size;
+    return have_address;
+
+}
+
+static void emit_load_assignment_rhs_to_reg (const char *reg);
+
+static void emit_load_floating_deref_reg_now (const char *reg, int size) {
+
+    if (!state->ofp) {
+        return;
+    }
+
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+
+        if (size == (DATA_FLOAT & 0x1f)) {
+            fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? "    fld dword [%s]\n" : "    fld dword ptr [%s]\n", reg);
+        } else {
+            fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? "    fld qword [%s]\n" : "    fld qword ptr [%s]\n", reg);
+        }
+
+    } else {
+
+        if (size == (DATA_FLOAT & 0x1f)) {
+            fprintf (state->ofp, "    flds (%%%s)\n", reg);
+        } else {
+            fprintf (state->ofp, "    fldl (%%%s)\n", reg);
+        }
+
+    }
+
+}
+
+static void emit_load_floating_member_from_addr_reg_now (const char *reg, int offset, int size) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        if (size == (DATA_FLOAT & 0x1f)) {
+            fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? "    fld dword [%s + %d]\n" : "    fld dword ptr [%s + %d]\n", reg, offset);
+        } else {
+            fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? "    fld qword [%s + %d]\n" : "    fld qword ptr [%s + %d]\n", reg, offset);
+        }
+    
+    } else {
+    
+        if (size == (DATA_FLOAT & 0x1f)) {
+            fprintf (state->ofp, "    flds %d(%%%s)\n", offset, reg);
+        } else {
+            fprintf (state->ofp, "    fldl %d(%%%s)\n", offset, reg);
+        }
+    
+    }
+
+}
+
+static int expression_text_has_pluseq_before_delim_now (const char *p) {
+
+    int depth = 0;
+    int quote;
+    
+    if (!p) {
+        return 0;
+    }
+    
+    while (*p) {
+    
+        if (depth == 0 && (*p == ',' || *p == ')' || *p == ';' || *p == ']' || *p == '}')) {
+            return 0;
+        }
+        
+        if (*p == '\'' || *p == '"') {
+        
+            quote = *p++;
+            
+            while (*p && *p != quote) {
+            
+                if (*p == '\\' && p[1]) {
+                    p += 2;
+                } else {
+                    p++;
+                }
+            
+            }
+            
+            if (*p == quote) {
+                p++;
+            }
+            
+            continue;
+        
+        }
+        
+        if (p[0] == '+' && p[1] == '=') {
+            return 1;
+        }
+        
+        if (*p == '(' || *p == '[' || *p == '{') {
+            depth++;
+        } else if (*p == ')' || *p == ']' || *p == '}') {
+        
+            if (depth > 0) {
+                depth--;
+            }
+        
+        }
+        
+        p++;
+    
+    }
+    
+    return 0;
+
+}
+
+static int emit_parse_va_arg_address_to_reg_now (const char *reg, int size) {
+
+    char *name;
+    
+    const char *name_start;
+    const char *name_caret;
+    
+    unsigned long name_line;
+    struct local_symbol *src;
+    
+    int global_index;
+    int64_s inc;
+    
+    int outer_paren = 0;
+    int deref_lvalue = 0;
+    
+    if (tok.kind == TOK_LPAREN) {
+    
+        outer_paren = 1;
+        get_token ();
+    
+    }
+    
+    if (tok.kind == TOK_LPAREN) {
+    
+        get_token ();
+        
+        if (tok.kind == TOK_STAR) {
+        
+            deref_lvalue = 1;
+            get_token ();
+        
+        }
+        
+        if (tok.kind != TOK_IDENT) {
+            return 0;
+        }
+        
+        name = xstrdup (tok.ident);
+        name_start = tok.start;
+        name_caret = tok.caret;
+        name_line = get_line_number ();
+        
+        get_token ();
+        expect (TOK_RPAREN, ")");
+    
+    } else {
+    
+        if (tok.kind == TOK_STAR) {
+        
+            deref_lvalue = 1;
+            get_token ();
+        
+        }
+        
+        if (tok.kind != TOK_IDENT) {
+            return 0;
+        }
+        
+        name = xstrdup (tok.ident);
+        name_start = tok.start;
+        name_caret = tok.caret;
+        name_line = get_line_number ();
+        
+        get_token ();
+    
+    }
+    
+    if (tok.kind != TOK_PLUSEQ) {
+    
+        free (name);
+        return 0;
+    
+    }
+    
+    get_token ();
+    
+    if (token_is_sizeof_keyword ()) {
+        inc = sizeof_from_current_token ();
+    } else {
+    
+        inc.low = (unsigned long) size;
+        inc.high = 0;
+        
+        emit_load_assignment_rhs_to_reg (reg);
+    
+    }
+    
+    src = find_local_symbol (name);
+    global_index = find_global_symbol (name);
+    
+    if (!src && global_index < 0) {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    if (deref_lvalue) {
+    
+        if (src) {
+        
+            if (src->is_static && src->static_label) {
+                emit_load_global_to_reg ("edx", src->static_label, DATA_PTR);
+            } else {
+                emit_load_local_to_reg ("edx", src->offset, DATA_PTR);
+            }
+        
+        } else {
+            emit_load_global_to_reg ("edx", name, DATA_PTR);
+        }
+        
+        if (state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? "    mov %s, dword [edx]\n" : "    mov %s, dword ptr [edx]\n", reg);
+            } else {
+                fprintf (state->ofp, "    movl (%%edx), %%%s\n", reg);
+            }
+        
+        }
+        
+        if (state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+            
+                fprintf (state->ofp, "    add %s, %lu\n", reg, inc.low & U32_MASK);
+                fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? "    mov dword [edx], %s\n" : "    mov dword ptr [edx], %s\n", reg);
+            
+            } else {
+            
+                fprintf (state->ofp, "    addl $%lu, %%%s\n", inc.low & U32_MASK, reg);
+                fprintf (state->ofp, "    movl %%%s, (%%edx)\n", reg);
+            
+            }
+        
+        }
+    
+    } else {
+    
+        if (src) {
+        
+            if (src->is_static && src->static_label) {
+                emit_load_global_to_reg (reg, src->static_label, DATA_PTR);
+            } else {
+                emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+            }
+        
+        } else {
+            emit_load_global_to_reg (reg, name, DATA_PTR);
+        }
+        
+        if (state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    add %s, %lu\n", reg, inc.low & U32_MASK);
+            } else {
+                fprintf (state->ofp, "    addl $%lu, %%%s\n", inc.low & U32_MASK, reg);
+            }
+        
+        }
+        
+        if (src) {
+        
+            if (src->is_static && src->static_label) {
+                emit_store_reg_to_global (src->static_label, DATA_PTR, reg);
+            } else {
+                emit_store_reg_to_local (src->offset, DATA_PTR, reg);
+            }
+        
+        } else {
+            emit_store_reg_to_global (name, DATA_PTR, reg);
+        }
+    
+    }
+    
+    if (tok.kind == TOK_COMMA) {
+    
+        get_token ();
+        
+        if (_accept (TOK_LPAREN)) {
+        
+            if (tok.kind == TOK_STAR) {
+                get_token ();
+            }
+            
+            if (tok.kind == TOK_IDENT) {
+                get_token ();
+            }
+            
+            expect (TOK_RPAREN, ")");
+        
+        } else {
+        
+            if (tok.kind == TOK_STAR) {
+                get_token ();
+            }
+            
+            if (tok.kind == TOK_IDENT) {
+                get_token ();
+            }
+        
+        }
+        
+        if (tok.kind == TOK_MINUS) {
+        
+            get_token ();
+            
+            if (token_is_sizeof_keyword ()) {
+                (void) sizeof_from_current_token ();
+            } else {
+                skip_balanced_until (outer_paren ? TOK_RPAREN : TOK_SEMI, TOK_COMMA, TOK_EOF);
+            }
+        
+        }
+    
+    }
+    
+    if (outer_paren) {
+        expect (TOK_RPAREN, ")");
+    }
+    
+    if (state->ofp) {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+            fprintf (state->ofp, "    sub %s, %lu\n", reg, inc.low & U32_MASK);
+        } else {
+            fprintf (state->ofp, "    subl $%lu, %%%s\n", inc.low & U32_MASK, reg);
+        }
+    
+    }
+    
+    free (name);
+    return 1;
+
+}
+
+static int emit_load_parenthesized_indirect_member_to_reg_now (const char *reg) {
+
+    const char *p;
+    char *name;
+    
+    const char *name_start;
+    const char *name_caret;
+    
+    unsigned long name_line;
+    struct local_symbol *src;
+    
+    int global_index;
+    int current_object_size;
+    
+    const char *current_tag_name;
+    
+    int first_member;
+    int last_member_size;
+    int last_member_pointer_depth;
+    int last_member_elem_size;
+    
+    if (tok.kind != TOK_STAR || !tok.caret) {
+        return 0;
+    }
+    
+    p = tok.caret + 1;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) {
+        return 0;
+    }
+    
+    p++;
+    
+    while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+        p++;
+    }
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p != ')') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p != '.' && !(*p == '-' && p[1] == '>')) {
+        return 0;
+    }
+    
+    get_token ();
+    
+    name_start = tok.start;
+    name_caret = tok.caret;
+    name_line = get_line_number ();
+    
+    if (tok.kind != TOK_IDENT || !tok.ident) {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *");
+        return 1;
+    
+    }
+    
+    name = xstrdup (tok.ident);
+    get_token ();
+    
+    expect (TOK_RPAREN, ")");
+    
+    src = find_local_symbol (name);
+    global_index = find_global_symbol (name);
+    
+    if (src) {
+    
+        if (src->is_static && src->static_label) {
+            emit_load_global_to_reg (reg, src->static_label, DATA_PTR);
+        } else {
+            emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+        }
+        
+        current_object_size = src->pointed_size;
+        current_tag_name = src->pointed_tag_name;
+    
+    } else if (global_index >= 0) {
+    
+        emit_load_global_to_reg (reg, name, DATA_PTR);
+        
+        current_object_size = get_global_symbol_pointed_size (name);
+        current_tag_name = 0;
+    
+    } else {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    free (name);
+    
+    emit_load_deref_reg_now (reg, DATA_PTR);
+    first_member = 1;
+    
+    last_member_size = DATA_INT & 0x1f;
+    last_member_pointer_depth = 0;
+    last_member_elem_size = DATA_INT & 0x1f;
+    
+    while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+    
+        enum token_kind member_op = tok.kind;
+        char *member;
+        
+        const char *member_start;
+        const char *member_caret;
+        
+        unsigned long member_line;
+        
+        int member_offset = 0;
+        int member_size = DATA_INT & 0x1f;
+        int member_elem_size = DATA_INT & 0x1f;
+        int member_pointer_depth = 0;
+        int member_is_array = 0;
+        
+        get_token ();
+        
+        member_start = tok.start;
+        member_caret = tok.caret;
+        member_line = get_line_number ();
+        
+        if (tok.kind != TOK_IDENT || !tok.ident) {
+        
+            report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+            return 1;
+        
+        }
+        
+        member = xstrdup (tok.ident);
+        get_token ();
+        
+        if (!find_member_info_ex_bounded (member, current_object_size, current_tag_name, &member_offset, &member_size, &member_elem_size, &member_pointer_depth, &member_is_array, 0)) {
+        
+            report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+            
+            free (member);
+            return 1;
+        
+        }
+        
+        free (member);
+        
+        if (!first_member && member_op == TOK_ARROW) {
+            emit_load_deref_reg_now (reg, DATA_PTR);
+        }
+        
+        if (member_offset != 0 && state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    add %s, %d\n", reg, member_offset);
+            } else {
+                fprintf (state->ofp, "    addl $%d, %%%s\n", member_offset, reg);
+            }
+        
+        }
+        
+        current_object_size = member_size;
+        current_tag_name = last_found_member_tag_name;
+        
+        last_member_size = member_size;
+        last_member_pointer_depth = member_pointer_depth;
+        last_member_elem_size = member_elem_size;
+        
+        first_member = 0;
+    
+    }
+    
+    emit_load_deref_reg_now (reg, last_member_size);
+    
+    if (last_member_pointer_depth > 0) {
+        set_rhs_last_pointer_info (last_member_pointer_depth, last_member_elem_size);
+    } else {
+        clear_rhs_last_pointer_info ();
+    }
+    
+    return 1;
+
+}
+
+static int source_starts_parenthesized_deref_postfix_incdec_subscript_at (const char *p) {
+
+    if (!p || *p != '*') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) {
+        return 0;
+    }
+    
+    p++;
+    
+    while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+        p++;
+    }
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p != ')') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (!((p[0] == '+' && p[1] == '+') || (p[0] == '-' && p[1] == '-'))) {
+        return 0;
+    }
+    
+    p += 2;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    return 1;
+
+}
+
+static int source_starts_lparen_deref_subscript_at (const char *p) {
+
+    if (!p || *p != '(') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p != '*') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) {
+        return 0;
+    }
+    
+    p++;
+    
+    while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+        p++;
+    }
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p != ')') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    return *p == '[';
+
+}
+
+static int source_starts_lparen_deref_postfix_incdec_at (const char *p) {
+
+    if (!p) {
+        return 0;
+    }
+    
+    /*
+     * Most callers pass the caret at the opening parenthesis, but some
+     * token paths leave it just after the '('.  Accept both positions so
+     * a statement such as:
+     *
+     *     (*parameter_count)++;
+     *
+     * is always routed through the dereferenced-object inc/dec emitter, not
+     * the ordinary pointer-variable inc/dec path.  The latter scales by the
+     * pointed-to size and would emit addl $4 for int *, corrupting counts.
+     */
+    if (*p == '(') {
+        p++;
+    }
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p != '*') {
+        return 0;
+    }
+    
+    return source_starts_parenthesized_deref_postfix_incdec_subscript_at (p);
+
+}
+
+static int emit_load_parenthesized_deref_postfix_incdec_subscript_to_reg_now (const char *reg) {
+
+    enum token_kind op;
+    char *name;
+    
+    const char *name_start;
+    const char *name_caret;
+    
+    unsigned long name_line;
+    struct local_symbol *src;
+    
+    int global_index;
+    int pointer_depth = 0;
+    int pointed_size = DATA_INT & 0x1f;
+    int elem_size = DATA_INT & 0x1f;
+    int lvalue_size = DATA_INT & 0x1f;
+    int step = 1;
+    
+    const char *addr_reg;
+    
+    if (tok.kind != TOK_STAR) {
+        return 0;
+    }
+    
+    if (!source_starts_parenthesized_deref_postfix_incdec_subscript_at (tok.caret)) {
+        return 0;
+    }
+    
+    get_token ();
+    
+    name_start = tok.start;
+    name_caret = tok.caret;
+    name_line = get_line_number ();
+    
+    if (tok.kind != TOK_IDENT || !tok.ident) {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *");
+        return 1;
+    
+    }
+    
+    name = xstrdup (tok.ident);
+    get_token ();
+    
+    if (tok.kind != TOK_RPAREN) {
+    
+        free (name);
+        return 0;
+    
+    }
+    
+    get_token ();
+    
+    if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) {
+    
+        free (name);
+        return 0;
+    
+    }
+    
+    op = tok.kind;
+    get_token ();
+    
+    src = find_local_symbol (name);
+    global_index = find_global_symbol (name);
+    
+    if (src) {
+    
+        pointer_depth = src->pointer_depth;
+        pointed_size = src->pointed_size;
+    
+    } else if (global_index >= 0) {
+    
+        pointer_depth = get_global_symbol_pointer_depth (name);
+        pointed_size = get_global_symbol_pointed_size (name);
+    
+    } else {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    if (pointer_depth <= 0) {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "invalid indirection of non-pointer '%s'", name);
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    if (pointed_size <= 0) {
+        pointed_size = DATA_INT & 0x1f;
+    }
+    
+    if (pointer_depth > 1) {
+    
+        lvalue_size = DATA_PTR & 0x1f;
+        
+        elem_size = (pointer_depth > 2) ? (DATA_PTR & 0x1f) : index_step_size (pointed_size);
+        step = elem_size;
+    
+    } else {
+    
+        lvalue_size = pointed_size & 0x1f;
+        
+        elem_size = pointed_size & 0x1f;
+        step = 1;
+    
+    }
+    
+    if (!reg) {
+        reg = "eax";
+    }
+    
+    addr_reg = (strcmp (reg, "edx") == 0) ? "ecx" : "edx";
+    
+    if (src) {
+    
+        if (src->is_static && src->static_label) {
+            emit_load_global_to_reg (addr_reg, src->static_label, DATA_PTR);
+        } else {
+            emit_load_local_to_reg (addr_reg, src->offset, DATA_PTR);
+        }
+    
+    } else {
+        emit_load_global_to_reg (addr_reg, name, DATA_PTR);
+    }
+    
+    emit_load_member_from_addr_reg_now (reg, addr_reg, 0, lvalue_size);
+    emit_push_reg_now (reg);
+    
+    if (state->ofp) {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+            fprintf (state->ofp, op == TOK_INCR ? "    add %s, %d\n" : "    sub %s, %d\n", reg, step);
+        } else {
+            fprintf (state->ofp, op == TOK_INCR ? "    addl $%d, %%%s\n" : "    subl $%d, %%%s\n", step, reg);
+        }
+    
+    }
+    
+    emit_store_reg_to_deref_reg_now (addr_reg, reg, lvalue_size);
+    emit_pop_reg_now (reg);
+    
+    if (tok.kind == TOK_LBRACK) {
+        emit_parse_postfix_subscripts_to_reg_now (reg, elem_size, pointer_depth - 1, pointed_size);
+    }
+    
+    if (pointer_depth > 1) {
+        set_rhs_last_pointer_info (pointer_depth - 1, pointed_size);
+    } else {
+        clear_rhs_last_pointer_info ();
+    }
+    
+    free (name);
+    return 1;
+
+}
+
+static int source_starts_deref_parenthesized_deref_postfix_incdec_assignment_at (const char *p) {
+
+    if (!p) {
+        return 0;
+    }
+    
+    if (*p == '*') {
+        p++;
+    }
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p != '(') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p != '*') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) {
+        return 0;
+    }
+    
+    p++;
+    
+    while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+        p++;
+    }
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p != ')') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (!((p[0] == '+' && p[1] == '+') || (p[0] == '-' && p[1] == '-'))) {
+        return 0;
+    }
+    
+    p += 2;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    return *p == '=';
+
+}
+
+static int source_starts_deref_parenthesized_deref_postfix_incdec_value_at (const char *p) {
+
+    if (!p || *p != '*') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p != '(') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p != '(') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p != '*') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) {
+        return 0;
+    }
+    
+    p++;
+    
+    while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+        p++;
+    }
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p != ')') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (!((p[0] == '+' && p[1] == '+') || (p[0] == '-' && p[1] == '-'))) {
+        return 0;
+    }
+    
+    p += 2;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    return *p == ')';
+
+}
+
+static int emit_load_deref_parenthesized_deref_postfix_incdec_to_reg_now (const char *reg) {
+
+    enum token_kind op;
+    char *name;
+    
+    const char *name_start;
+    const char *name_caret;
+    
+    unsigned long name_line;
+    struct local_symbol *src;
+    
+    int global_index;
+    int pointer_depth = 0;
+    int pointed_size = DATA_INT & 0x1f;
+    int deref_size = DATA_INT & 0x1f;
+    int step = DATA_PTR & 0x1f;
+    
+    if (tok.kind != TOK_STAR) {
+        return 0;
+    }
+    
+    if (!source_starts_deref_parenthesized_deref_postfix_incdec_value_at (tok.caret)) {
+        return 0;
+    }
+    
+    expect (TOK_STAR, "*");
+    expect (TOK_LPAREN, "(");
+    expect (TOK_LPAREN, "(");
+    expect (TOK_STAR, "*");
+    
+    name_start = tok.start;
+    name_caret = tok.caret;
+    name_line = get_line_number ();
+    
+    if (tok.kind != TOK_IDENT || !tok.ident) {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *");
+        return 1;
+    
+    }
+    
+    name = xstrdup (tok.ident);
+    get_token ();
+    
+    expect (TOK_RPAREN, ")");
+    
+    if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) {
+    
+        free (name);
+        return 0;
+    
+    }
+    
+    op = tok.kind;
+    get_token ();
+    
+    expect (TOK_RPAREN, ")");
+    
+    src = find_local_symbol (name);
+    global_index = find_global_symbol (name);
+    
+    if (src) {
+    
+        pointer_depth = src->pointer_depth;
+        pointed_size = src->pointed_size;
+    
+    } else if (global_index >= 0) {
+    
+        pointer_depth = get_global_symbol_pointer_depth (name);
+        pointed_size = get_global_symbol_pointed_size (name);
+    
+    } else {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    if (pointer_depth <= 0) {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "invalid indirection of non-pointer '%s'", name);
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    if (pointed_size <= 0) {
+        pointed_size = DATA_INT & 0x1f;
+    }
+    
+    deref_size = pointer_depth > 1 ? (pointed_size & 0x1f) : (DATA_INT & 0x1f);
+    step = pointer_depth > 2 ? (DATA_PTR & 0x1f) : index_step_size (pointed_size);
+    
+    if (deref_size <= 0) {
+        deref_size = DATA_INT & 0x1f;
+    }
+    
+    if (step <= 0) {
+        step = DATA_PTR & 0x1f;
+    }
+    
+    if (!reg) {
+        reg = "eax";
+    }
+    
+    if (src) {
+    
+        if (src->is_static && src->static_label) {
+            emit_load_global_to_reg ("ecx", src->static_label, DATA_PTR);
+        } else {
+            emit_load_local_to_reg ("ecx", src->offset, DATA_PTR);
+        }
+    
+    } else {
+        emit_load_global_to_reg ("ecx", name, DATA_PTR);
+    }
+    
+    emit_load_member_from_addr_reg_now (reg, "ecx", 0, DATA_PTR & 0x1f);
+    emit_push_reg_now (reg);
+    
+    if (state->ofp) {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+            fprintf (state->ofp, op == TOK_INCR ? "    add %s, %d\n" : "    sub %s, %d\n", reg, step);
+        } else {
+            fprintf (state->ofp, op == TOK_INCR ? "    addl $%d, %%%s\n" : "    subl $%d, %%%s\n", step, reg);
+        }
+    
+    }
+    
+    emit_store_reg_to_deref_reg_now ("ecx", reg, DATA_PTR & 0x1f);
+    emit_pop_reg_now (reg);
+    emit_load_deref_reg_now (reg, deref_size);
+    
+    if (pointer_depth > 2) {
+        set_rhs_last_pointer_info (pointer_depth - 2, pointed_size);
+    } else {
+        clear_rhs_last_pointer_info ();
+    }
+    
+    free (name);
+    return 1;
+
+}
+
+static int emit_store_to_deref_parenthesized_deref_postfix_incdec_now (const char *reg) {
+
+    enum token_kind op;
+    char *name;
+    
+    const char *name_start;
+    const char *name_caret;
+    
+    unsigned long name_line;
+    struct local_symbol *src;
+    
+    int global_index;
+    int pointer_depth = 0;
+    int pointed_size = DATA_INT & 0x1f;
+    int store_size = DATA_INT & 0x1f;
+    int step = DATA_PTR & 0x1f;
+    
+    if (tok.kind != TOK_STAR) {
+        return 0;
+    }
+    
+    if (!source_starts_deref_parenthesized_deref_postfix_incdec_assignment_at (tok.caret)) {
+        return 0;
+    }
+    
+    get_token ();
+    
+    expect (TOK_LPAREN, "(");
+    expect (TOK_STAR, "*");
+    
+    name_start = tok.start;
+    name_caret = tok.caret;
+    name_line = get_line_number ();
+    
+    if (tok.kind != TOK_IDENT || !tok.ident) {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *");
+        return 1;
+    
+    }
+    
+    name = xstrdup (tok.ident);
+    get_token ();
+    
+    expect (TOK_RPAREN, ")");
+    
+    if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) {
+    
+        free (name);
+        return 0;
+    
+    }
+    
+    op = tok.kind;
+    get_token ();
+    
+    expect (TOK_ASSIGN, "=");
+    
+    src = find_local_symbol (name);
+    global_index = find_global_symbol (name);
+    
+    if (src) {
+    
+        pointer_depth = src->pointer_depth;
+        pointed_size = src->pointed_size;
+    
+    } else if (global_index >= 0) {
+    
+        pointer_depth = get_global_symbol_pointer_depth (name);
+        pointed_size = get_global_symbol_pointed_size (name);
+    
+    } else {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    if (pointer_depth <= 0) {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "invalid indirection of non-pointer '%s'", name);
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    if (pointed_size <= 0) {
+        pointed_size = DATA_INT & 0x1f;
+    }
+    
+    store_size = pointed_size & 0x1f;
+    step = pointed_size & 0x1f;
+    
+    if (step <= 0) {
+        step = DATA_PTR & 0x1f;
+    }
+    
+    if (!reg) {
+        reg = "eax";
+    }
+    
+    if (src) {
+    
+        if (src->is_static && src->static_label) {
+            emit_load_global_to_reg ("ecx", src->static_label, DATA_PTR);
+        } else {
+            emit_load_local_to_reg ("ecx", src->offset, DATA_PTR);
+        }
+    
+    } else {
+        emit_load_global_to_reg ("ecx", name, DATA_PTR);
+    }
+    
+    emit_load_member_from_addr_reg_now ("edx", "ecx", 0, DATA_PTR & 0x1f);
+    emit_push_reg_now ("edx");
+    
+    if (state->ofp) {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+            fprintf (state->ofp, op == TOK_INCR ? "    add edx, %d\n" : "    sub edx, %d\n", step);
+        } else {
+            fprintf (state->ofp, op == TOK_INCR ? "    addl $%d, %%edx\n" : "    subl $%d, %%edx\n", step);
+        }
+    
+    }
+    
+    emit_store_reg_to_deref_reg_now ("ecx", "edx", DATA_PTR & 0x1f);
+    
+    emit_load_assignment_rhs_expression_to_reg (reg);
+    emit_pop_reg_now ("edx");
+    emit_store_reg_to_deref_reg_now ("edx", reg, store_size);
+    
+    free (name);
+    return 1;
+
+}
+
+static void emit_load_assignment_rhs_to_reg (const char *reg) {
+
+    clear_rhs_last_pointer_info ();
+    
+    if (tok.kind == TOK_LPAREN && source_starts_lparen_deref_postfix_incdec_at (tok.caret)) {
+    
+        get_token ();
+        
+        if (emit_load_parenthesized_deref_postfix_incdec_subscript_to_reg_now (reg)) {
+            return;
+        }
+    
+    }
+    
+    /*
+     * Do not special-case parenthesized assignment expressions here.
+     *
+     * The normal parenthesized-expression path below already recurses back
+     * into emit_load_assignment_rhs_expression_to_reg(), and the identifier
+     * operand parser below already _accepts assignment operators.  Using the
+     * old source-text detector here was wrong for nested grouping such as:
+     *
+     *     ((((reta <<= 1)) & 0x10))
+     *
+     * because it greedily treated all leading '(' tokens as belonging to the
+     * assignment expression, including parentheses that actually enclose the
+     * later binary '&' expression.
+     */
+    if (_accept (TOK_LPAREN)) {
+    
+        char *paren_call_name = 0;
+        
+        const char *paren_call_start = 0;
+        const char *paren_call_caret = 0;
+        
+        unsigned long paren_call_line = 0;
+        
+        /*
+         * Function pointer call designator: (*fp)(args).
+         *
+         * If this is left to the normal parenthesized-expression path, the
+         * inner unary * path treats *fp as a data dereference and emits:
+         *
+         *     movl off(%ebp), %eax
+         *     movl (%eax), %eax
+         *
+         * That is correct for reading *p as an object, but wrong for a
+         * function designator.  In a call, the value of fp is already the
+         * target address, so load fp and call through it without the extra
+         * data load.  Keep this narrow: only handle the exact token form
+         * (*identifier)(...), and let every other parenthesized expression use
+         * the existing parser.
+         */
+        if (tok.kind == TOK_STAR && tok.caret) {
+        
+            const char *p = tok.caret + 1;
+            const char *name_start;
+            const char *name_caret;
+            
+            unsigned long name_line;
+            char *name;
+            
+            struct local_symbol *src;
+            
+            int global_index;
+            int looks_like_call = 0;
+            
+            if (emit_load_parenthesized_deref_postfix_incdec_subscript_to_reg_now (reg)) {
+                return;
+            }
+            
+            while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+                p++;
+            }
+            
+            if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_') {
+            
+                p++;
+                
+                while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+                    p++;
+                }
+                
+                while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+                    p++;
+                }
+                
+                if (*p == ')') {
+                
+                    p++;
+                    
+                    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+                        p++;
+                    }
+                    
+                    looks_like_call = (*p == '(');
+                
+                }
+            
+            }
+            
+            if (looks_like_call) {
+            
+                get_token ();
+                
+                name_start = tok.start;
+                name_caret = tok.caret;
+                name_line = get_line_number ();
+                
+                if (tok.kind != TOK_IDENT || !tok.ident) {
+                
+                    report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *");
+                    return;
+                
+                }
+                
+                name = xstrdup (tok.ident);
+                get_token ();
+                
+                expect (TOK_RPAREN, ")");
+                
+                src = find_local_symbol (name);
+                global_index = find_global_symbol (name);
+                
+                if (src) {
+                
+                    if (src->is_static && src->static_label) {
+                        emit_load_global_to_reg (reg, src->static_label, DATA_PTR);
+                    } else {
+                        emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+                    }
+                
+                } else if (global_index >= 0) {
+                    emit_load_global_to_reg (reg, name, DATA_PTR);
+                } else {
+                
+                    report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+                    
+                    free (name);
+                    return;
+                
+                }
+                
+                free (name);
+                
+                emit_call_pointer_in_reg_now (reg, reg);
+                clear_rhs_last_pointer_info ();
+                
+                return;
+            
+            }
+        
+        }
+        
+        if (emit_load_parenthesized_indirect_member_to_reg_now (reg)) {
+            return;
+        }
+        
+        if (tok.kind == TOK_IDENT && tok.ident &&
+        
+            find_global_symbol (tok.ident) >= 0 &&
+            get_global_symbol_kind (tok.ident) == GLOBAL_SYMBOL_FUNCTION) {
+        
+            paren_call_name = xstrdup (tok.ident);
+            paren_call_start = tok.start;
+            paren_call_caret = tok.caret;
+            paren_call_line = get_line_number ();
+        
+        }
+        
+        if (!is_type_start (tok.kind) && parenthesized_function_designator_call_now ()) {
+        
+            char *call_name = xstrdup (tok.ident);
+            
+            const char *call_start = tok.start;
+            const char *call_caret = tok.caret;
+            
+            unsigned long call_line = get_line_number ();
+            get_token ();
+            
+            expect (TOK_RPAREN, ")");
+            
+            if (!find_local_symbol (call_name)) {
+                ensure_global_function_symbol (call_name, call_start, call_caret, call_line);
+            }
+            
+            emit_call_identifier_to_reg_now (call_name, reg, call_start, call_caret, call_line);
+            free (call_name);
+            
+            return;
+        
+        }
+        
+        if (is_type_start (tok.kind)) {
+        
+            int saved_type_size = parsed_type_size;
+            int saved_storage_class = parsed_storage_class;
+            int saved_is_aggregate = parsed_type_is_aggregate;
+            int saved_is_void = parsed_type_is_void;
+            int saved_is_unsigned = parsed_type_is_unsigned;
+            int saved_is_floating = parsed_type_is_floating;
+            int saved_has_tag = parsed_type_has_tag;
+            int saved_is_inline = parsed_type_is_inline;
+            int saved_field_count = parsed_field_count;
+            int saved_fields[MAX_AGG_FIELDS];
+            int saved_declarator_is_pointer = declarator_is_pointer;
+            int saved_declarator_pointer_depth = declarator_pointer_depth;
+            int saved_declarator_has_array = declarator_has_array;
+            int saved_declarator_has_function = declarator_has_function;
+            int saved_declarator_array_unsized = declarator_array_unsized;
+            long saved_declarator_array_count = declarator_array_count;
+            
+            char *cast_name = 0;
+            
+            int cast_base_size;
+            int cast_is_unsigned = 0;
+            int cast_pointer_depth = 0;
+            int cast_pointed_size = 0;
+            
+            int i;
+            
+            for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) {
+                saved_fields[i] = parsed_field_sizes[i];
+            }
+            
+            declarator_is_pointer = 0;
+            declarator_pointer_depth = 0;
+            declarator_has_array = 0;
+            declarator_has_function = 0;
+            declarator_array_unsized = 0;
+            declarator_array_count = 0;
+            
+            parse_type_spec ();
+            
+            cast_base_size = parsed_type_size & 0x1f;
+            cast_is_unsigned = parsed_type_is_unsigned;
+            
+            if (tok.kind != TOK_RPAREN) {
+                parse_declarator (&cast_name);
+            }
+            
+            if (declarator_is_pointer) {
+            
+                cast_pointer_depth = declarator_pointer_depth > 0 ? declarator_pointer_depth : 1;
+                cast_pointed_size = cast_pointer_depth > 1 ? (DATA_PTR & 0x1f) : cast_base_size;
+                
+                if (cast_pointed_size <= 0) {
+                    cast_pointed_size = DATA_INT & 0x1f;
+                }
+            
+            }
+            
+            if (cast_name) {
+                free (cast_name);
+            }
+            
+            expect (TOK_RPAREN, ")");
+            
+            parsed_type_size = saved_type_size;
+            parsed_storage_class = saved_storage_class;
+            parsed_type_is_aggregate = saved_is_aggregate;
+            parsed_type_is_void = saved_is_void;
+            parsed_type_is_unsigned = saved_is_unsigned;
+            parsed_type_is_floating = saved_is_floating;
+            parsed_type_has_tag = saved_has_tag;
+            parsed_type_is_inline = saved_is_inline;
+            
+            clear_parsed_fields ();
+            
+            for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) {
+                parsed_field_sizes[i] = saved_fields[i];
+            }
+            
+            parsed_field_count = saved_field_count;
+            
+            declarator_is_pointer = saved_declarator_is_pointer;
+            declarator_pointer_depth = saved_declarator_pointer_depth;
+            declarator_has_array = saved_declarator_has_array;
+            declarator_has_function = saved_declarator_has_function;
+            declarator_array_unsized = saved_declarator_array_unsized;
+            declarator_array_count = saved_declarator_array_count;
+            
+            emit_load_assignment_rhs_to_reg (reg);
+            
+            while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+                emit_apply_postfix_member_access_to_reg_now (reg);
+            }
+            
+            if (tok.kind == TOK_LBRACK) {
+            
+                int cast_subscript_elem_size = cast_pointer_depth > 1 ? (DATA_PTR & 0x1f) : cast_pointed_size;
+                
+                if (cast_subscript_elem_size <= 0) {
+                    cast_subscript_elem_size = DATA_INT & 0x1f;
+                }
+                
+                emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, cast_subscript_elem_size);
+                emit_load_deref_reg_now (reg, cast_subscript_elem_size);
+            
+            }
+            
+            if (cast_pointer_depth > 0) {
+                set_rhs_last_pointer_info (cast_pointer_depth, cast_pointed_size);
+            } else {
+            
+                emit_apply_integer_cast_to_reg_now (reg, cast_base_size, cast_is_unsigned);
+                clear_rhs_last_pointer_info ();
+            
+            }
+            
+            return;
+        
+        }
+        
+        if (const_integer_expr_text_is_foldable_now (tok.caret)) {
+        
+            int64_s v = const64_from_current_foldable_expr ();
+            expect (TOK_RPAREN, ")");
+            
+            emit_load_const32_to_reg_now (reg, v);
+            return;
+        
+        }
+        
+        emit_load_assignment_rhs_expression_to_reg (reg);
+        
+        if (postfix_member_seen && (tok.kind == TOK_INCR || tok.kind == TOK_DECR)) {
+        
+            enum token_kind postfix_op = tok.kind;
+            get_token ();
+            emit_apply_postfix_member_incdec_now (reg, postfix_op);
+        
+        }
+        
+        while (tok.kind == TOK_COMMA) {
+        
+            get_token ();
+            emit_load_assignment_rhs_expression_to_reg (reg);
+        
+        }
+        
+        if (is_assignment_operator (tok.kind)) {
+        
+            get_token ();
+            emit_load_assignment_rhs_expression_to_reg (reg);
+        
+        }
+        
+        if (paren_call_name && tok.kind == TOK_RPAREN) {
+        
+            get_token ();
+            
+            if (tok.kind == TOK_LPAREN) {
+            
+                if (!find_local_symbol (paren_call_name)) {
+                    ensure_global_function_symbol (paren_call_name, paren_call_start, paren_call_caret, paren_call_line);
+                }
+                
+                emit_call_identifier_to_reg_now (paren_call_name, reg, paren_call_start, paren_call_caret, paren_call_line);
+                free (paren_call_name);
+                
+                return;
+            
+            }
+        
+        } else {
+            expect (TOK_RPAREN, ")");
+        }
+        
+        if (paren_call_name) {
+            free (paren_call_name);
+        }
+        
+        if (tok.kind == TOK_LBRACK) {
+        
+            int subscript_pointer_depth = rhs_last_pointer_depth;
+            int subscript_elem_size = rhs_last_pointed_size;
+            
+            if (subscript_elem_size <= 0) {
+                subscript_elem_size = DATA_INT & 0x1f;
+            }
+            
+            emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, subscript_elem_size);
+            
+            if (is_assignment_operator (tok.kind)) {
+            
+                enum token_kind assign_op = tok.kind;
+                
+                get_token ();
+                emit_push_reg_now (reg);
+                
+                if (assign_op == TOK_ASSIGN && subscript_elem_size > (DATA_LLONG & 0x1f) &&
+                    tok.kind == TOK_IDENT && tok.ident && token_identifier_is_function_call_rhs_now ()) {
+                
+                    char *rhs_name = xstrdup (tok.ident);
+                    
+                    const char *rhs_start = tok.start;
+                    const char *rhs_caret = tok.caret;
+                    
+                    unsigned long rhs_line = get_line_number ();
+                    
+                    pending_struct_return_lhs = 0;
+                    pending_struct_return_global_name = 0;
+                    pending_struct_return_stack_address = 1;
+                    pending_struct_return_stack_offset = 0;
+                    
+                    get_token ();
+                    emit_call_identifier_to_reg_now (rhs_name, reg, rhs_start, rhs_caret, rhs_line);
+                    
+                    pending_struct_return_stack_address = 0;
+                    pending_struct_return_stack_offset = 0;
+                    
+                    emit_pop_reg_now ("edx");
+                    free (rhs_name);
+                    
+                    set_rhs_last_pointer_info (0, 0);
+                    return;
+                
+                } else if (assign_op == TOK_ASSIGN) {
+                
+                    if (subscript_elem_size > (DATA_LLONG & 0x1f)) {
+                    
+                        pending_struct_return_lhs = 0;
+                        pending_struct_return_global_name = 0;
+                        pending_struct_return_stack_address = 1;
+                        pending_struct_return_stack_offset = 0;
+                        
+                        emit_load_assignment_rhs_expression_to_reg (reg);
+                        
+                        pending_struct_return_stack_address = 0;
+                        pending_struct_return_stack_offset = 0;
+                        
+                        emit_pop_reg_now ("edx");
+                        set_rhs_last_pointer_info (0, 0);
+                        
+                        return;
+                    
+                    }
+                    
+                    emit_load_assignment_rhs_expression_to_reg (reg);
+                
+                } else {
+                
+                    emit_load_deref_reg_now (reg, subscript_elem_size);
+                    emit_push_reg_now (reg);
+                    
+                    emit_load_assignment_rhs_expression_to_reg ("edx");
+                    emit_pop_reg_now (reg);
+                    
+                    emit_assignment_binary_op (assign_op, 0);
+                
+                }
+                
+                emit_pop_reg_now ("edx");
+                
+                emit_store_reg_to_deref_reg_now ("edx", reg, subscript_elem_size);
+                set_rhs_last_pointer_info (0, 0);
+                
+                return;
+            
+            }
+            
+            emit_load_deref_reg_now (reg, subscript_elem_size);
+            
+            if (subscript_pointer_depth > 0) {
+                subscript_pointer_depth--;
+            }
+            
+            if (subscript_pointer_depth > 0) {
+                set_rhs_last_pointer_info (subscript_pointer_depth, subscript_elem_size);
+            } else {
+                set_rhs_last_pointer_info (0, 0);
+            }
+        
+        }
+        
+        if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+            emit_apply_postfix_member_access_to_reg_now (reg);
+        }
+        
+        if (postfix_member_seen && tok.kind == TOK_LPAREN) {
+        
+            emit_call_pointer_in_reg_now (reg, reg);
+            
+            set_rhs_last_pointer_info (0, 0);
+            return;
+        
+        }
+        
+        if (postfix_member_seen) {
+        
+            set_rhs_last_pointer_info (postfix_member_pointer_depth, postfix_member_pointed_size);
+            
+            if (is_assignment_operator (tok.kind)) {
+            
+                enum token_kind op = tok.kind;
+                
+                int assign_member_offset = postfix_member_offset;
+                int assign_member_size = postfix_member_size;
+                
+                get_token ();
+                
+                if (state->ofp) {
+                
+                    if (op == TOK_ASSIGN) {
+                    
+                        if (postfix_member_is_floating || token_is_floating_constant_now ()) {
+                        
+                            emit_push_reg_now ("edx");
+                            emit_load_floating_rhs_expression_now (assign_member_size);
+                            
+                            emit_pop_reg_now ("edx");
+                            emit_store_floating_member_to_addr_reg_now ("edx", assign_member_offset, assign_member_size);
+                            
+                            return;
+                        
+                        }
+                        
+                        if (assign_member_size > (DATA_LLONG & 0x1f) &&
+                            tok.kind == TOK_IDENT && tok.ident && token_identifier_is_function_call_rhs_now ()) {
+                        
+                            char *rhs_name = xstrdup (tok.ident);
+                            
+                            const char *rhs_start = tok.start;
+                            const char *rhs_caret = tok.caret;
+                            
+                            unsigned long rhs_line = get_line_number ();
+                            emit_push_reg_now ("edx");
+                            
+                            pending_struct_return_lhs = 0;
+                            pending_struct_return_global_name = 0;
+                            pending_struct_return_stack_address = 1;
+                            pending_struct_return_stack_offset = assign_member_offset;
+                            
+                            get_token ();
+                            emit_call_identifier_to_reg_now (rhs_name, reg, rhs_start, rhs_caret, rhs_line);
+                            
+                            pending_struct_return_stack_address = 0;
+                            pending_struct_return_stack_offset = 0;
+                            
+                            emit_pop_reg_now ("edx");
+                            free (rhs_name);
+                            
+                            return;
+                        
+                        }
+                        
+                        if (emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("edx", assign_member_offset, assign_member_size)) {
+                            return;
+                        }
+                        
+                        emit_push_reg_now ("edx");
+                        
+                        emit_load_assignment_rhs_expression_to_reg (reg);
+                        emit_pop_reg_now ("edx");
+                    
+                    } else {
+                    
+                        /*
+                         * emit_apply_postfix_member_access_to_reg_now() has already
+                         * loaded the member value into reg and left the containing
+                         * object address in edx.  Do not load the member again from
+                         * reg: for p->m |= x that treats the old value of m as a
+                         * pointer and dereferences it, which corrupts/segfaults code
+                         * such as section->symbol->flags |= SYMBOL_FLAG_SECTION_SYMBOL.
+                         */
+                        emit_push_reg_now ("edx");
+                        emit_push_reg_now (reg);
+                        
+                        emit_load_assignment_rhs_expression_to_reg ("edx");
+                        emit_pop_reg_now (reg);
+                        
+                        emit_assignment_binary_op (op, 0);
+                        emit_pop_reg_now ("edx");
+                    
+                    }
+                    
+                    emit_store_member_to_addr_reg_now ("edx", assign_member_offset, reg, assign_member_size);
+                
+                } else {
+                    emit_load_assignment_rhs_expression_to_reg (reg);
+                }
+            
+            }
+        
+        }
+        
+        /*
+         * This routine parses one primary operand for the outer binary
+         * expression parser.  After returning, the caller can still consume
+         * any trailing operator, e.g. the / b in:
+         *
+         *     (a + b - 1) / b
+         */
+        return;
+    
+    } else if (tok.kind == TOK_AMPER) {
+    
+        char *name;
+        
+        const char *name_start;
+        const char *name_caret;
+        
+        unsigned long name_line;
+        struct local_symbol *src;
+        
+        get_token ();
+        
+        if (tok.kind == TOK_LPAREN) {
+        
+            if (emit_load_address_of_parenthesized_postfix_to_reg_now (reg)) {
+                return;
+            }
+        
+        }
+        
+        name_start = tok.start;
+        name_caret = tok.caret;
+        name_line = get_line_number ();
+        
+        if (tok.kind != TOK_IDENT) {
+        
+            int64_s zero;
+            
+            zero.low = 0;
+            zero.high = 0;
+            
+            report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after '&'");
+            
+            emit_load_const32_to_reg_now (reg, zero);
+            return;
+        
+        }
+        
+        name = xstrdup (tok.ident);
+        get_token ();
+        
+        src = find_local_symbol (name);
+        
+        if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+        
+            int global_index = find_global_symbol (name);
+            
+            const char *current_object_tag_name = 0;
+            int current_object_size = 0;
+            
+            if (src) {
+            
+                if (tok.kind == TOK_DOT || src->is_array) {
+                
+                    current_object_size = src->is_array && src->pointed_size > 0 ? src->pointed_size : src->size;
+                    current_object_tag_name = src->is_array ? src->pointed_tag_name : 0;
+                    
+                    if (src->is_static && src->static_label) {
+                        emit_load_address_to_reg_now (reg, src->static_label);
+                    } else {
+                        emit_load_local_address_to_reg_now (reg, src->offset);
+                    }
+                
+                } else if (src->is_static && src->static_label) {
+                
+                    current_object_size = src->pointed_size > 0 ? src->pointed_size : DATA_PTR;
+                    current_object_tag_name = src->pointed_tag_name;
+                    
+                    emit_load_global_to_reg (reg, src->static_label, DATA_PTR);
+                
+                } else {
+                
+                    current_object_size = src->pointed_size > 0 ? src->pointed_size : DATA_PTR;
+                    current_object_tag_name = src->pointed_tag_name;
+                    
+                    emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+                
+                }
+            
+            } else if (global_index >= 0) {
+            
+                if (tok.kind == TOK_DOT || get_global_symbol_array (name)) {
+                
+                    current_object_size = get_global_symbol_array (name) && get_global_symbol_pointed_size (name) > 0 ? get_global_symbol_pointed_size (name) : get_global_symbol_size (name);
+                    current_object_tag_name = get_global_symbol_tag_name (name);
+                    
+                    emit_load_address_to_reg_now (reg, name);
+                
+                } else {
+                
+                    current_object_size = get_global_symbol_pointed_size (name) > 0 ? get_global_symbol_pointed_size (name) : DATA_PTR;
+                    current_object_tag_name = get_global_symbol_tag_name (name);
+                    
+                    emit_load_global_to_reg (reg, name, DATA_PTR);
+                
+                }
+            
+            } else {
+            
+                report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+                
+                free (name);
+                return;
+            
+            }
+            
+            while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+            
+                enum token_kind member_op = tok.kind;
+                char *member;
+                
+                const char *member_start;
+                const char *member_caret;
+                
+                unsigned long member_line;
+                
+                int offset = 0;
+                int member_size = DATA_INT & 0x1f;
+                int elem_size = DATA_INT & 0x1f;
+                int pointer_depth = 0;
+                
+                get_token ();
+                
+                member_start = tok.start;
+                member_caret = tok.caret;
+                member_line = get_line_number ();
+                
+                if (tok.kind != TOK_IDENT) {
+                
+                    report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+                    
+                    free (name);
+                    return;
+                
+                }
+                
+                member = xstrdup (tok.ident);
+                get_token ();
+                
+                if (!find_member_info_ex_bounded (member, current_object_size, current_object_tag_name, &offset, &member_size, &elem_size, &pointer_depth, 0, 0)) {
+                
+                    report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+                    
+                    free (member);
+                    free (name);
+                    
+                    return;
+                
+                }
+                
+                current_object_tag_name = last_found_member_tag_name;
+                free (member);
+                
+                if (state->ofp && offset != 0) {
+                
+                    if (state->syntax & ASM_SYNTAX_INTEL) {
+                        fprintf (state->ofp, "    add %s, %d\n", reg, offset);
+                    } else {
+                        fprintf (state->ofp, "    addl $%d, %%%s\n", offset, reg);
+                    }
+                
+                }
+                
+                if (tok.kind == TOK_LBRACK) {
+                
+                    if (pointer_depth > 0) {
+                    
+                        emit_load_member_from_addr_reg_now (reg, reg, 0, DATA_PTR & 0x1f);
+                        emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size);
+                    
+                    } else {
+                        emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size);
+                    }
+                
+                } else if (pointer_depth > 0 && (tok.kind == TOK_ARROW || tok.kind == TOK_DOT)) {
+                    emit_load_member_from_addr_reg_now (reg, reg, 0, DATA_PTR & 0x1f);
+                }
+                
+                if (pointer_depth > 0) {
+                    current_object_size = elem_size > 0 ? elem_size : DATA_PTR;
+                } else {
+                    current_object_size = member_size;
+                }
+            
+            }
+            
+            free (name);
+            return;
+        
+        }
+        
+        if (tok.kind == TOK_LBRACK) {
+        
+            if (src) {
+            
+                int elem_size = src->is_array ? (src->pointer_depth ? DATA_PTR : (src->array_element_size > 0 ? src->array_element_size : src->pointed_size)) :
+                    (src->pointer_depth > 1 ? DATA_PTR : src->pointed_size);
+                
+                if (elem_size <= 0) {
+                    elem_size = DATA_INT & 0x1f;
+                }
+                
+                if (src->is_array) {
+                
+                    if (src->is_static && src->static_label) {
+                        emit_load_address_to_reg_now (reg, src->static_label);
+                    } else {
+                        emit_load_local_address_to_reg_now (reg, src->offset);
+                    }
+                
+                } else if (src->is_static && src->static_label) {
+                    emit_load_global_to_reg (reg, src->static_label, DATA_PTR);
+                } else {
+                    emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+                }
+                
+                emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size);
+                
+                if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+                
+                    const char *current_object_tag_name = src->pointed_tag_name;
+                    int current_object_size = elem_size;
+                    
+                    while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+                    
+                        enum token_kind member_op = tok.kind;
+                        char *member;
+                        
+                        const char *member_start;
+                        const char *member_caret;
+                        
+                        unsigned long member_line;
+                        
+                        int offset = 0;
+                        int member_size = DATA_INT & 0x1f;
+                        int member_elem_size = DATA_INT & 0x1f;
+                        int member_pointer_depth = 0;
+                        
+                        get_token ();
+                        
+                        member_start = tok.start;
+                        member_caret = tok.caret;
+                        member_line = get_line_number ();
+                        
+                        if (tok.kind != TOK_IDENT) {
+                        
+                            report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+                            
+                            free (name);
+                            return;
+                        
+                        }
+                        
+                        member = xstrdup (tok.ident);
+                        get_token ();
+                        
+                        if (!find_member_info_ex_bounded (member, current_object_size, current_object_tag_name, &offset, &member_size, &member_elem_size, &member_pointer_depth, 0, 0)) {
+                        
+                            report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+                            
+                            free (member);
+                            free (name);
+                            
+                            return;
+                        
+                        }
+                        
+                        current_object_tag_name = last_found_member_tag_name;
+                        free (member);
+                        
+                        if (state->ofp && offset != 0) {
+                        
+                            if (state->syntax & ASM_SYNTAX_INTEL) {
+                                fprintf (state->ofp, "    add %s, %d\n", reg, offset);
+                            } else {
+                                fprintf (state->ofp, "    addl $%d, %%%s\n", offset, reg);
+                            }
+                        
+                        }
+                        
+                        if (tok.kind == TOK_LBRACK) {
+                        
+                            if (member_pointer_depth > 0) {
+                                emit_load_member_from_addr_reg_now (reg, reg, 0, DATA_PTR & 0x1f);
+                            }
+                            
+                            emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, member_elem_size > 0 ? member_elem_size : DATA_INT & 0x1f);
+                        
+                        } else if (member_pointer_depth > 0 && (tok.kind == TOK_ARROW || tok.kind == TOK_DOT)) {
+                            emit_load_member_from_addr_reg_now (reg, reg, 0, DATA_PTR & 0x1f);
+                        }
+                        
+                        if (member_pointer_depth > 0) {
+                            current_object_size = member_elem_size > 0 ? member_elem_size : DATA_PTR;
+                        } else {
+                            current_object_size = member_size;
+                        }
+                    
+                    }
+                
+                }
+                
+                free (name);
+                return;
+            
+            }
+            
+            if (find_global_symbol (name) >= 0) {
+            
+                int elem_size = get_global_symbol_array (name) ?
+                    (get_global_symbol_pointer_depth (name) ? DATA_PTR : (get_global_symbol_array_element_size (name) > 0 ? get_global_symbol_array_element_size (name) : get_global_symbol_pointed_size (name))) :
+                        (get_global_symbol_pointer_depth (name) > 1 ? DATA_PTR : get_global_symbol_pointed_size (name));
+                
+                if (elem_size <= 0) {
+                    elem_size = DATA_INT & 0x1f;
+                }
+                
+                if (get_global_symbol_array (name)) {
+                    emit_load_address_to_reg_now (reg, name);
+                } else {
+                    emit_load_global_to_reg (reg, name, DATA_PTR);
+                }
+                
+                emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size);
+                
+                free (name);
+                return;
+            
+            }
+        
+        }
+        
+        if (src) {
+        
+            if (src->is_static && src->static_label) {
+                emit_load_address_to_reg_now (reg, src->static_label);
+            } else {
+                emit_load_local_address_to_reg_now (reg, src->offset);
+            }
+            
+            free (name);
+            return;
+        
+        }
+        
+        if (find_global_symbol (name) >= 0) {
+        
+            emit_load_address_to_reg_now (reg, name);
+            
+            free (name);
+            return;
+        
+        }
+        
+        {
+        
+            int64_s zero;
+            
+            zero.low = 0;
+            zero.high = 0;
+            
+            report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+            
+            free (name);
+            emit_load_const32_to_reg_now (reg, zero);
+        
+        }
+        
+        return;
+    
+    } else if (tok.kind == TOK_STAR) {
+    
+        int deref_had_parens = 0;
+        int deref_size = DATA_CHAR & 0x1f;
+        int deref_pointer_depth = 0;
+        int deref_pointed_size = 0;
+        
+        if (emit_load_deref_parenthesized_deref_postfix_incdec_to_reg_now (reg)) {
+            return;
+        }
+        
+        if (emit_store_to_deref_parenthesized_deref_postfix_incdec_now (reg)) {
+            return;
+        }
+        
+        get_token ();
+        
+        if (tok.kind == TOK_IDENT) {
+        
+            char *lhs_name = xstrdup (tok.ident);
+            
+            const char *lhs_start = tok.start;
+            const char *lhs_caret = tok.caret;
+            
+            unsigned long lhs_line = get_line_number ();
+            struct local_symbol *lhs_sym;
+            
+            enum token_kind lhs_postfix_op = TOK_EOF;
+            enum token_kind member_op = TOK_EOF;
+            
+            char *member;
+            
+            const char *member_start;
+            const char *member_caret;
+            
+            unsigned long member_line;
+            
+            int lhs_has_postfix = 0;
+            int offset = 0;
+            int member_size = DATA_PTR & 0x1f;
+            int member_elem_size = DATA_INT & 0x1f;
+            int member_pointer_depth = 0;
+            
+            get_token ();
+            
+            if (tok.kind == TOK_LPAREN &&
+                find_global_symbol (lhs_name) >= 0 &&
+                get_global_symbol_kind (lhs_name) == GLOBAL_SYMBOL_FUNCTION) {
+            
+                int fptr_depth = get_global_symbol_pointer_depth (lhs_name);
+                int fpointed_size = get_global_symbol_pointed_size (lhs_name);
+                
+                emit_call_identifier_to_reg_now (lhs_name, reg, lhs_start, lhs_caret, lhs_line);
+                
+                if (fptr_depth > 1) {
+                    deref_size = DATA_PTR;
+                } else if (fptr_depth == 1 && fpointed_size > 0) {
+                    deref_size = fpointed_size;
+                }
+                
+                emit_load_deref_reg_now (reg, deref_size);
+                
+                free (lhs_name);
+                return;
+            
+            }
+            
+            if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+            
+                member_op = tok.kind;
+                get_token ();
+                
+                member_start = tok.start;
+                member_caret = tok.caret;
+                member_line = get_line_number ();
+                
+                if (tok.kind != TOK_IDENT) {
+                
+                    report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+                    
+                    free (lhs_name);
+                    return;
+                
+                }
+                
+                member = xstrdup (tok.ident);
+                get_token ();
+                
+                if (!find_member_info_ex (member, &offset, &member_size, &member_elem_size, &member_pointer_depth, 0, 0)) {
+                
+                    report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+                    
+                    free (member);
+                    free (lhs_name);
+                    
+                    return;
+                
+                }
+                
+                free (member);
+                
+                if (member_pointer_depth > 1) {
+                    deref_size = DATA_PTR;
+                } else if (member_pointer_depth == 1 && member_elem_size > 0) {
+                    deref_size = member_elem_size;
+                }
+                
+                if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+                
+                    lhs_has_postfix = 1;
+                    lhs_postfix_op = tok.kind;
+                    
+                    get_token ();
+                
+                }
+                
+                if (tok.kind == TOK_ASSIGN) {
+                
+                    lhs_sym = find_local_symbol (lhs_name);
+                    
+                    if (lhs_sym) {
+                    
+                        if (lhs_sym->is_static && lhs_sym->static_label) {
+                            emit_load_global_to_reg ("edx", lhs_sym->static_label, DATA_PTR);
+                        } else {
+                            emit_load_local_to_reg ("edx", lhs_sym->offset, DATA_PTR);
+                        }
+                    
+                    } else if (find_global_symbol (lhs_name) >= 0) {
+                        emit_load_global_to_reg ("edx", lhs_name, DATA_PTR);
+                    } else {
+                        report_line_at (get_filename (), lhs_line, REPORT_ERROR, lhs_start, lhs_caret, "unknown symbol '%s'", lhs_name);
+                    }
+                    
+                    if (state->ofp) {
+                    
+                        if (state->syntax & ASM_SYNTAX_INTEL) {
+                        
+                            if (state->syntax & ASM_SYNTAX_NASM) {
+                            
+                                fprintf (state->ofp, "    mov ecx, dword [edx + %d]\n", offset);
+                                
+                                if (lhs_has_postfix) {
+                                    fprintf (state->ofp, "    %s dword [edx + %d]\n", lhs_postfix_op == TOK_INCR ? "inc" : "dec", offset);
+                                }
+                            
+                            } else {
+                            
+                                fprintf (state->ofp, "    mov ecx, dword ptr [edx + %d]\n", offset);
+                                
+                                if (lhs_has_postfix) {
+                                    fprintf (state->ofp, "    %s dword ptr [edx + %d]\n", lhs_postfix_op == TOK_INCR ? "inc" : "dec", offset);
+                                }
+                            
+                            }
+                        
+                        } else {
+                        
+                            fprintf (state->ofp, "    movl %d(%%edx), %%ecx\n", offset);
+                            
+                            if (lhs_has_postfix) {
+                                fprintf (state->ofp, "    %sl %d(%%edx)\n", lhs_postfix_op == TOK_INCR ? "inc" : "dec", offset);
+                            }
+                        
+                        }
+                    
+                    }
+                    
+                    emit_push_reg_now ("ecx");
+                    get_token ();
+                    
+                    emit_load_assignment_rhs_expression_to_reg (reg);
+                    emit_pop_reg_now ("edx");
+                    emit_store_reg_to_deref_reg_now ("edx", reg, deref_size);
+                    
+                    free (lhs_name);
+                    return;
+                
+                }
+                
+                lhs_sym = find_local_symbol (lhs_name);
+                
+                if (lhs_sym) {
+                
+                    if (member_op == TOK_DOT) {
+                    
+                        if (lhs_sym->is_static && lhs_sym->static_label) {
+                            emit_load_address_to_reg_now (reg, lhs_sym->static_label);
+                        } else {
+                            emit_load_local_address_to_reg_now (reg, lhs_sym->offset);
+                        }
+                    
+                    } else if (lhs_sym->is_static && lhs_sym->static_label) {
+                        emit_load_global_to_reg (reg, lhs_sym->static_label, DATA_PTR);
+                    } else {
+                        emit_load_local_to_reg (reg, lhs_sym->offset, DATA_PTR);
+                    }
+                
+                } else if (find_global_symbol (lhs_name) >= 0) {
+                
+                    if (member_op == TOK_DOT) {
+                        emit_load_address_to_reg_now (reg, lhs_name);
+                    } else {
+                        emit_load_global_to_reg (reg, lhs_name, DATA_PTR);
+                    }
+                
+                } else {
+                    report_line_at (get_filename (), lhs_line, REPORT_ERROR, lhs_start, lhs_caret, "unknown symbol '%s'", lhs_name);
+                }
+                
+                if (state->ofp) {
+                
+                    if (state->syntax & ASM_SYNTAX_INTEL) {
+                    
+                        if (state->syntax & ASM_SYNTAX_NASM) {
+                            fprintf (state->ofp, "    mov %s, dword [%s + %d]\n", reg, reg, offset);
+                        } else {
+                            fprintf (state->ofp, "    mov %s, dword ptr [%s + %d]\n", reg, reg, offset);
+                        }
+                    
+                    } else {
+                        fprintf (state->ofp, "    movl %d(%%%s), %%%s\n", offset, reg, reg);
+                    }
+                
+                }
+                
+                if (lhs_has_postfix) {
+                    /* Already applied above only for assignment forms. */
+                }
+                
+                emit_load_deref_reg_now (reg, deref_size);
+                
+                free (lhs_name);
+                return;
+            
+            }
+            
+            if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+            
+                lhs_has_postfix = 1;
+                lhs_postfix_op = tok.kind;
+                
+                get_token ();
+            
+            }
+            
+            if (tok.kind == TOK_ASSIGN) {
+            
+                int lhs_deref_is_unsigned = 1;
+                lhs_sym = find_local_symbol (lhs_name);
+                
+                if (lhs_sym) {
+                
+                    if (lhs_sym->pointer_depth > 1) {
+                        deref_size = DATA_PTR & 0x1f;
+                    } else if (lhs_sym->pointer_depth == 1 && lhs_sym->pointed_size > 0) {
+                    
+                        /**
+                         * Keep the full pointed-to object size here.  This
+                         * assignment path also handles aggregate lvalues such
+                         * as *hashtab = old_hashtab;  Masking with 0x1f turns
+                         * a 44-byte struct hashtab into 12 and emits a partial
+                         * copy, corrupting pdas hashtab state during rehash().
+                         */
+                        deref_size = lhs_sym->pointed_size;
+                    
+                    }
+                    
+                    if (member_op == TOK_DOT) {
+                    
+                        if (lhs_sym->is_static && lhs_sym->static_label) {
+                            emit_load_address_to_reg_now ("edx", lhs_sym->static_label);
+                        } else {
+                            emit_load_local_address_to_reg_now ("edx", lhs_sym->offset);
+                        }
+                    
+                    } else if (lhs_sym->is_static && lhs_sym->static_label) {
+                        emit_load_global_to_reg ("edx", lhs_sym->static_label, DATA_PTR);
+                    } else {
+                        emit_load_local_to_reg ("edx", lhs_sym->offset, DATA_PTR);
+                    }
+                
+                } else if (find_global_symbol (lhs_name) >= 0) {
+                
+                    if (get_global_symbol_pointer_depth (lhs_name) > 1) {
+                        deref_size = DATA_PTR & 0x1f;
+                    } else if (get_global_symbol_pointer_depth (lhs_name) == 1 && get_global_symbol_pointed_size (lhs_name) > 0) {
+                        deref_size = get_global_symbol_pointed_size (lhs_name);
+                    }
+                    
+                    if (member_op == TOK_DOT) {
+                        emit_load_address_to_reg_now ("edx", lhs_name);
+                    } else {
+                        emit_load_global_to_reg ("edx", lhs_name, DATA_PTR);
+                    }
+                
+                } else {
+                    report_line_at (get_filename (), lhs_line, REPORT_ERROR, lhs_start, lhs_caret, "unknown symbol '%s'", lhs_name);
+                }
+                
+                emit_push_reg_now ("edx");
+                
+                if (lhs_has_postfix) {
+                    emit_incdec_symbol_now (lhs_sym, lhs_name, lhs_postfix_op, lhs_line, lhs_start, lhs_caret);
+                }
+                
+                get_token ();
+                
+                if (deref_size > (DATA_LLONG & 0x1f)) {
+                
+                    emit_pop_reg_now ("edx");
+                    
+                    if (!emit_store_assignment_to_aggregate_address_now ("edx", deref_size, lhs_start, lhs_caret, lhs_line)) {
+                        report_line_at (get_filename (), lhs_line, REPORT_ERROR, lhs_start, lhs_caret, "aggregate assignment expression not implemented");
+                    }
+                
+                } else if (deref_size == (DATA_LLONG & 0x1f)) {
+                
+                    emit_load_assignment_rhs_expression_to_pair ("eax", "edx", lhs_deref_is_unsigned);
+                    emit_pop_reg_now ("ecx");
+                    
+                    emit_store_pair_to_deref_reg_now ("ecx", "eax", "edx");
+                
+                } else {
+                
+                    emit_load_assignment_rhs_expression_to_reg (reg);
+                    emit_pop_reg_now ("edx");
+                    
+                    emit_store_reg_to_deref_reg_now ("edx", reg, deref_size);
+                
+                }
+                
+                free (lhs_name);
+                return;
+            
+            }
+            
+            lhs_sym = find_local_symbol (lhs_name);
+            
+            if (lhs_sym) {
+            
+                if (lhs_sym->is_static && lhs_sym->static_label) {
+                    emit_load_global_to_reg (reg, lhs_sym->static_label, DATA_PTR);
+                } else {
+                    emit_load_local_to_reg (reg, lhs_sym->offset, DATA_PTR);
+                }
+                
+                if (lhs_has_postfix) {
+                
+                    emit_push_reg_now (reg);
+                    emit_incdec_symbol_now (lhs_sym, lhs_name, lhs_postfix_op, lhs_line, lhs_start, lhs_caret);
+                    emit_pop_reg_now (reg);
+                
+                }
+                
+                if (lhs_sym->pointer_depth > 1) {
+                    deref_size = DATA_PTR;
+                } else if (lhs_sym->pointer_depth == 1) {
+                    deref_size = lhs_sym->pointed_size;
+                }
+                
+                emit_load_deref_reg_now (reg, deref_size);
+                
+                if (lhs_sym->pointer_depth > 1) {
+                    set_rhs_last_pointer_info (lhs_sym->pointer_depth - 1, lhs_sym->pointed_size);
+                } else {
+                    clear_rhs_last_pointer_info ();
+                }
+                
+                if (emit_handle_subscript_after_loaded_pointer_to_reg_now (reg, lhs_sym->pointer_depth > 0 ? lhs_sym->pointer_depth - 1 : 0, lhs_sym->pointed_size)) {
+                
+                    free (lhs_name);
+                    return;
+                
+                }
+                
+                free (lhs_name);
+                return;
+            
+            }
+            
+            if (find_global_symbol (lhs_name) >= 0) {
+            
+                emit_load_global_to_reg (reg, lhs_name, DATA_PTR);
+                
+                if (lhs_has_postfix) {
+                
+                    emit_push_reg_now (reg);
+                    emit_incdec_symbol_now (0, lhs_name, lhs_postfix_op, lhs_line, lhs_start, lhs_caret);
+                    emit_pop_reg_now (reg);
+                
+                }
+                
+                if (get_global_symbol_pointer_depth (lhs_name) > 1) {
+                    deref_size = DATA_PTR;
+                } else if (get_global_symbol_pointer_depth (lhs_name) == 1) {
+                    deref_size = get_global_symbol_pointed_size (lhs_name);
+                }
+                
+                emit_load_deref_reg_now (reg, deref_size);
+                
+                if (get_global_symbol_pointer_depth (lhs_name) > 1) {
+                    set_rhs_last_pointer_info (get_global_symbol_pointer_depth (lhs_name) - 1, get_global_symbol_pointed_size (lhs_name));
+                } else {
+                    clear_rhs_last_pointer_info ();
+                }
+                
+                if (emit_handle_subscript_after_loaded_pointer_to_reg_now (reg, get_global_symbol_pointer_depth (lhs_name) > 0 ? get_global_symbol_pointer_depth (lhs_name) - 1 : 0, get_global_symbol_pointed_size (lhs_name))) {
+                
+                    free (lhs_name);
+                    return;
+                
+                }
+                
+                free (lhs_name);
+                return;
+            
+            }
+            
+            report_line_at (get_filename (), lhs_line, REPORT_ERROR, lhs_start, lhs_caret, "unknown symbol '%s'", lhs_name);
+            free (lhs_name);
+            
+            return;
+        
+        }
+        
+        if (tok.kind == TOK_LPAREN) {
+        
+            deref_had_parens = 1;
+            get_token ();
+            
+            if (is_type_start (tok.kind)) {
+            
+                if (parse_deref_cast_type_name (&deref_size)) {
+                
+                    int handled_va_arg = 0;
+                    
+                    if (expression_text_has_pluseq_before_delim_now (tok.start)) {
+                        handled_va_arg = emit_parse_va_arg_address_to_reg_now (reg, deref_size);
+                    }
+                    
+                    if (!handled_va_arg) {
+                    
+                        /*
+                         * This is the address operand of a casted dereference,
+                         * e.g. *(size_t *)ptr.  Do not parse a full binary
+                         * expression here: in
+                         *
+                         *     *(size_t *)ptr + sizeof(size_t)
+                         *
+                         * the + belongs to the outer expression after the
+                         * dereference, not to the address being dereferenced.
+                         */
+                        emit_load_assignment_rhs_to_reg (reg);
+                    
+                    }
+                    
+                     
+                    /*
+                     * parse_deref_cast_type_name() consumes the cast parentheses
+                     * only.  Do not consume a following ')' here: in expressions
+                     * such as:
+                     *
+                     *     __munmap(ptr, *(size_t *)ptr + sizeof(size_t))
+                     *
+                     * that ')' belongs to the surrounding call.  Consuming it
+                     * here makes the caller report a false "expected )".
+                     */
+                    
+                    if (tok.kind == TOK_ASSIGN) {
+                    
+                        get_token ();
+                        
+                        emit_push_reg_now (reg);
+                        emit_load_assignment_rhs_expression_to_reg (reg);
+                        
+                        emit_pop_reg_now ("edx");
+                        emit_store_reg_to_deref_reg_now ("edx", reg, deref_size);
+                        
+                        return;
+                    
+                    }
+                    
+                    goto dereference_loaded_address;
+                
+                }
+            
+            }
+            
+            if (tok.kind == TOK_AMPER) {
+            
+                char *name;
+                
+                const char *name_start;
+                const char *name_caret;
+                
+                unsigned long name_line;
+                struct local_symbol *dst;
+                
+                int global_index;
+                int dst_size;
+                
+                get_token ();
+                
+                name_start = tok.start;
+                name_caret = tok.caret;
+                name_line = get_line_number ();
+                
+                if (tok.kind == TOK_IDENT) {
+                
+                    name = xstrdup (tok.ident);
+                    
+                    get_token ();
+                    expect (TOK_RPAREN, ")");
+                    
+                    if (is_assignment_operator (tok.kind)) {
+                    
+                        get_token ();
+                        emit_load_assignment_rhs_expression_to_reg (reg);
+                        
+                        dst = find_local_symbol (name);
+                        global_index = find_global_symbol (name);
+                        
+                        if (dst) {
+                        
+                            dst_size = dst->size;
+                            
+                            if (dst->is_static && dst->static_label) {
+                                emit_store_reg_to_global (dst->static_label, dst_size, reg);
+                            } else {
+                                emit_store_reg_to_local (dst->offset, dst_size, reg);
+                            }
+                        
+                        } else if (global_index >= 0) {
+                        
+                            dst_size = get_global_symbol_size (name);
+                            emit_store_reg_to_global (name, dst_size, reg);
+                        
+                        } else {
+                            report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+                        }
+                        
+                        free (name);
+                        return;
+                    
+                    }
+                    
+                    if (find_local_symbol (name) || find_global_symbol (name) >= 0) {
+                    
+                        dst = find_local_symbol (name);
+                        
+                        if (dst) {
+                        
+                            if (dst->is_static && dst->static_label) {
+                                emit_load_address_to_reg_now (reg, dst->static_label);
+                            } else {
+                                emit_load_local_address_to_reg_now (reg, dst->offset);
+                            }
+                        
+                        } else {
+                            emit_load_address_to_reg_now (reg, name);
+                        }
+                        
+                        free (name);
+                        goto dereference_loaded_address;
+                    
+                    }
+                    
+                    report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+                    free (name);
+                    
+                    return;
+                
+                }
+            
+            }
+        
+        }
+        
+        if (deref_had_parens) {
+        
+            emit_load_assignment_rhs_expression_to_reg (reg);
+            
+            if (rhs_last_pointer_depth > 1) {
+                deref_size = DATA_PTR;
+            } else if (rhs_last_pointer_depth == 1) {
+                deref_size = rhs_last_pointed_size;
+            }
+            
+            expect (TOK_RPAREN, ")");
+            
+            /*
+             * A parenthesized dereferenced function pointer is still a function
+             * designator when it is followed by an argument list.  For example:
+             *
+             *     (*generate_func)(outfile, pos)
+             *
+             * The inner expression already loaded the function pointer value into
+             * reg.  Do not dereference that value as data before the call; call
+             * through it directly.
+             */
+            if (tok.kind == TOK_LPAREN) {
+            
+                emit_call_pointer_in_reg_now (reg, reg);
+                
+                clear_rhs_last_pointer_info ();
+                return;
+            
+            }
+            
+            if (tok.kind == TOK_ASSIGN) {
+            
+                emit_push_reg_now (reg);
+                get_token ();
+                
+                emit_load_assignment_rhs_expression_to_reg (reg);
+                emit_pop_reg_now ("edx");
+                emit_store_reg_to_deref_reg_now ("edx", reg, deref_size);
+                
+                return;
+            
+            }
+            
+            if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+            
+                enum token_kind member_op = tok.kind;
+                enum token_kind postfix_op = TOK_EOF;
+                
+                char *member;
+                
+                const char *member_start;
+                const char *member_caret;
+                
+                unsigned long member_line;
+                
+                int offset = 0;
+                int member_size = DATA_PTR & 0x1f;
+                int member_elem_size = DATA_INT & 0x1f;
+                int member_pointer_depth = 0;
+                
+                get_token ();
+                
+                member_start = tok.start;
+                member_caret = tok.caret;
+                member_line = get_line_number ();
+                
+                if (tok.kind != TOK_IDENT) {
+                
+                    report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+                    return;
+                
+                }
+                
+                member = xstrdup (tok.ident);
+                get_token ();
+                
+                if (!find_member_info_ex (member, &offset, &member_size, &member_elem_size, &member_pointer_depth, 0, 0)) {
+                
+                    report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+                    
+                    free (member);
+                    return;
+                
+                }
+                
+                free (member);
+                
+                if (member_pointer_depth > 1) {
+                    deref_size = DATA_PTR;
+                } else if (member_pointer_depth == 1 && member_elem_size > 0) {
+                    deref_size = member_elem_size;
+                }
+                
+                if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+                
+                    postfix_op = tok.kind;
+                    get_token ();
+                
+                }
+                
+                if (state->ofp) {
+                
+                    if (state->syntax & ASM_SYNTAX_INTEL) {
+                    
+                        if (state->syntax & ASM_SYNTAX_NASM) {
+                            fprintf (state->ofp, "    mov edx, dword [%s + %d]\n", reg, offset);
+                        } else {
+                            fprintf (state->ofp, "    mov edx, dword ptr [%s + %d]\n", reg, offset);
+                        }
+                        
+                        if (postfix_op == TOK_INCR || postfix_op == TOK_DECR) {
+                        
+                            if (state->syntax & ASM_SYNTAX_NASM) {
+                                fprintf (state->ofp, "    %s dword [%s + %d]\n", postfix_op == TOK_INCR ? "inc" : "dec", reg, offset);
+                            } else {
+                                fprintf (state->ofp, "    %s dword ptr [%s + %d]\n", postfix_op == TOK_INCR ? "inc" : "dec", reg, offset);
+                            }
+                        
+                        }
+                    
+                    } else {
+                    
+                        fprintf (state->ofp, "    movl %d(%%%s), %%edx\n", offset, reg);
+                        
+                        if (postfix_op == TOK_INCR || postfix_op == TOK_DECR) {
+                            fprintf (state->ofp, "    %sl %d(%%%s)\n", postfix_op == TOK_INCR ? "inc" : "dec", offset, reg);
+                        }
+                    
+                    }
+                
+                }
+                
+                if (tok.kind == TOK_ASSIGN) {
+                
+                    emit_push_reg_now ("edx");
+                    get_token ();
+                    
+                    emit_load_assignment_rhs_expression_to_reg (reg);
+                    emit_pop_reg_now ("edx");
+                    emit_store_reg_to_deref_reg_now ("edx", reg, deref_size);
+                    
+                    return;
+                
+                }
+                
+                if (state->ofp && strcmp (reg, "edx") != 0) {
+                
+                    if (state->syntax & ASM_SYNTAX_INTEL) {
+                        fprintf (state->ofp, "    mov %s, edx\n", reg);
+                    } else {
+                        fprintf (state->ofp, "    movl %%edx, %%%s\n", reg);
+                    }
+                
+                }
+                
+                goto dereference_loaded_address;
+            
+            }
+        
+        } else {
+            emit_load_assignment_rhs_to_reg (reg);
+        }
+        
+    dereference_loaded_address:
+        
+        deref_pointer_depth = rhs_last_pointer_depth;
+        deref_pointed_size = rhs_last_pointed_size;
+        
+        emit_load_deref_reg_now (reg, deref_size);
+        
+        if (deref_pointer_depth > 1) {
+            set_rhs_last_pointer_info (deref_pointer_depth - 1, deref_pointed_size);
+        } else {
+            clear_rhs_last_pointer_info ();
+        }
+        
+        if (tok.kind == TOK_LBRACK) {
+        
+            int subscript_pointer_depth = deref_pointer_depth > 0 ? deref_pointer_depth - 1 : 0;
+            int subscript_elem_size = subscript_pointer_depth > 1 ? (DATA_PTR & 0x1f) : deref_pointed_size;
+            
+            if (subscript_elem_size <= 0) {
+                subscript_elem_size = DATA_INT & 0x1f;
+            }
+            
+            emit_parse_postfix_subscripts_to_reg_now (reg, subscript_elem_size, subscript_pointer_depth, deref_pointed_size);
+            
+            if (is_assignment_operator (tok.kind)) {
+            
+                enum token_kind assign_op = tok.kind;
+                
+                get_token ();
+                emit_push_reg_now (reg);
+                
+                if (assign_op == TOK_ASSIGN && subscript_elem_size > (DATA_LLONG & 0x1f) &&
+                    tok.kind == TOK_IDENT && tok.ident && token_identifier_is_function_call_rhs_now ()) {
+                
+                    char *rhs_name = xstrdup (tok.ident);
+                    
+                    const char *rhs_start = tok.start;
+                    const char *rhs_caret = tok.caret;
+                    
+                    unsigned long rhs_line = get_line_number ();
+                    
+                    pending_struct_return_lhs = 0;
+                    pending_struct_return_global_name = 0;
+                    pending_struct_return_stack_address = 1;
+                    pending_struct_return_stack_offset = 0;
+                    
+                    get_token ();
+                    emit_call_identifier_to_reg_now (rhs_name, reg, rhs_start, rhs_caret, rhs_line);
+                    
+                    pending_struct_return_stack_address = 0;
+                    pending_struct_return_stack_offset = 0;
+                    
+                    emit_pop_reg_now ("edx");
+                    free (rhs_name);
+                    
+                    set_rhs_last_pointer_info (0, 0);
+                    return;
+                
+                } else if (assign_op == TOK_ASSIGN) {
+                
+                    if (subscript_elem_size > (DATA_LLONG & 0x1f)) {
+                    
+                        pending_struct_return_lhs = 0;
+                        pending_struct_return_global_name = 0;
+                        pending_struct_return_stack_address = 1;
+                        pending_struct_return_stack_offset = 0;
+                        
+                        emit_load_assignment_rhs_expression_to_reg (reg);
+                        
+                        pending_struct_return_stack_address = 0;
+                        pending_struct_return_stack_offset = 0;
+                        
+                        emit_pop_reg_now ("edx");
+                        set_rhs_last_pointer_info (0, 0);
+                        return;
+                    
+                    }
+                    
+                    emit_load_assignment_rhs_expression_to_reg (reg);
+                
+                } else {
+                
+                    emit_load_deref_reg_now (reg, subscript_elem_size);
+                    emit_push_reg_now (reg);
+                    
+                    emit_load_assignment_rhs_expression_to_reg ("edx");
+                    emit_pop_reg_now (reg);
+                    
+                    emit_assignment_binary_op (assign_op, 0);
+                
+                }
+                
+                emit_pop_reg_now ("edx");
+                
+                emit_store_reg_to_deref_reg_now ("edx", reg, subscript_elem_size);
+                clear_rhs_last_pointer_info ();
+                
+                return;
+            
+            }
+            
+            emit_load_deref_reg_now (reg, subscript_elem_size);
+            
+            if (subscript_pointer_depth > 1) {
+                set_rhs_last_pointer_info (subscript_pointer_depth - 1, deref_pointed_size);
+            } else {
+                clear_rhs_last_pointer_info ();
+            }
+        
+        }
+        
+        return;
+    
+    } else if (tok.kind == TOK_TILDE) {
+    
+        get_token ();
+        emit_load_assignment_rhs_to_reg (reg);
+        
+        if (state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    not %s\n", reg);
+            } else {
+                fprintf (state->ofp, "    notl %%%s\n", reg);
+            }
+        
+        }
+        
+        return;
+    
+    } else if (tok.kind == TOK_XMARK) {
+    
+        get_token ();
+        emit_load_assignment_rhs_to_reg (reg);
+        
+        if (state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+            
+                fprintf (state->ofp, "    test %s, %s\n", reg, reg);
+                
+                if (strcmp (reg, "eax") != 0) {
+                
+                    fprintf (state->ofp, "    setz al\n");
+                    fprintf (state->ofp, "    movzx eax, al\n");
+                    fprintf (state->ofp, "    mov %s, eax\n", reg);
+                
+                } else {
+                
+                    fprintf (state->ofp, "    setz al\n");
+                    fprintf (state->ofp, "    movzx eax, al\n");
+                
+                }
+            
+            } else {
+            
+                fprintf (state->ofp, "    testl %%%s, %%%s\n", reg, reg);
+                fprintf (state->ofp, "    setz %%al\n");
+                fprintf (state->ofp, "    movzbl %%al, %%eax\n");
+                
+                if (strcmp (reg, "eax") != 0) {
+                    fprintf (state->ofp, "    movl %%eax, %%%s\n", reg);
+                }
+            
+            }
+        
+        }
+        
+        return;
+    
+    } else if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+    
+        if (emit_load_prefix_incdec_member_to_reg_now (reg)) {
+            return;
+        }
+        
+        if (emit_load_prefix_incdec_to_reg_now (reg)) {
+            return;
+        }
+    
+    }
+    
+    if (tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) {
+    
+        int negate = 0;
+        
+        while (tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) {
+        
+            if (tok.kind == TOK_MINUS) {
+                negate = !negate;
+            }
+            
+            get_token ();
+        
+        }
+        
+        emit_load_assignment_rhs_to_reg (reg);
+        
+        if (negate && state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    neg %s\n", reg);
+            } else {
+                fprintf (state->ofp, "    negl %%%s\n", reg);
+            }
+        
+        }
+        
+        return;
+    
+    }
+    
+    if (token_is_sizeof_keyword ()) {
+    
+        int64_s v = sizeof_from_current_token ();
+        
+        if (state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    mov %s, %lu\n", reg, v.low & U32_MASK);
+            } else {
+                fprintf (state->ofp, "    movl $%lu, %%%s\n", v.low & U32_MASK, reg);
+            }
+        
+        }
+        
+        return;
+    
+    }
+    
+    if (is_string_token ()) {
+    
+        char *label = emit_string_literal_global ();
+        
+        switch_section (SECTION_TEXT);
+        emit_load_address_to_reg_now (reg, label);
+        
+        free (label);
+        
+        /*
+         * A string literal is a primary expression and may still have
+         * postfix operators applied to it.  In particular, macro-expanded
+         * string constants are commonly subscripted in calls, e.g.
+         *
+         *     tebc (LINKAGE_EDITOR_PROGRAM_NAME[i])
+         *
+         * where LINKAGE_EDITOR_PROGRAM_NAME expands to a string literal.
+         * The old path returned immediately after loading the literal
+         * address, leaving the '[' token for the caller and causing a false
+         * "expected )".  Treat the literal as a char array here and consume
+         * any following subscripts.
+         */
+        if (tok.kind == TOK_LBRACK) {
+        
+            emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, DATA_CHAR & 0x1f);
+            emit_load_deref_reg_now (reg, DATA_CHAR & 0x1f);
+            
+            set_rhs_last_pointer_info (0, 0);
+        
+        } else {
+            set_rhs_last_pointer_info (1, DATA_CHAR & 0x1f);
+        }
+        
+        return;
+    
+    }
+    
+    switch (tok.kind) {
+    
+        case TOK_CCHAR:     case TOK_CINT:      case TOK_CUINT:     case TOK_CULONG:
+        case TOK_CLONG:     case TOK_CLLONG:    case TOK_CULLONG:   case TOK_LCHAR:
+        {
+        
+            int64_s v;
+            
+            int trailing_op = 0;
+            size_t len = tok.ident ? strlen (tok.ident) : 0;
+            
+            v.high = tok.val.i.high;
+            v.low = tok.val.i.low;
+            
+            if (len > 1 && tok.ident[len - 1] == '+') {
+                trailing_op = TOK_PLUS;
+            } else if (len > 1 && tok.ident[len - 1] == '-') {
+                trailing_op = TOK_MINUS;
+            }
+            
+            if (trailing_op) {
+            
+                free (tok.ident);
+                
+                tok.ident = xstrdup (trailing_op == TOK_PLUS ? "+" : "-");
+                tok.kind = (enum token_kind) trailing_op;
+                
+                if (tok.caret) {
+                    tok.caret--;
+                }
+            
+            } else {
+                get_token ();
+            }
+            
+            emit_load_const32_to_reg_now (reg, v);
+            return;
+        
+        }
+        
+        case TOK_CFLOAT:    case TOK_CDOUBLE:    case TOK_CLDOUBLE:
+        {
+        
+            int trailing_op = 0;
+            size_t len = tok.ident ? strlen (tok.ident) : 0;
+            int64_s v;
+            
+            v.high = 0;
+            v.low = tok.val.ld != 0.0L ? 1 : 0;
+            
+            if (len > 1 && tok.ident[len - 1] == '+') {
+                trailing_op = TOK_PLUS;
+            } else if (len > 1 && tok.ident[len - 1] == '-') {
+                trailing_op = TOK_MINUS;
+            }
+            
+            if (trailing_op) {
+            
+                free (tok.ident);
+                
+                tok.ident = xstrdup (trailing_op == TOK_PLUS ? "+" : "-");
+                tok.kind = (enum token_kind) trailing_op;
+                
+                if (tok.caret) {
+                    tok.caret--;
+                }
+            
+            } else {
+                get_token ();
+            }
+            
+            emit_load_const32_to_reg_now (reg, v);
+            return;
+        
+        }
+        
+        default:
+        
+            break;
+    
+    }
+    
+    if (tok.kind == TOK_IDENT) {
+    
+        enum token_kind postfix_op = TOK_EOF;
+        char *name = xstrdup (tok.ident);
+        
+        const char *name_start = tok.start, *name_caret = tok.caret;
+        unsigned long name_line = get_line_number ();
+        
+        struct local_symbol *src;
+        int postfix_incdec = 0;
+        
+        get_token ();
+        
+        {
+        
+            int64_s enum_value;
+            
+            if (!find_local_symbol (name) && find_global_symbol (name) < 0 && resolve_enum_constant (name, &enum_value)) {
+            
+                emit_load_const32_to_reg_now (reg, enum_value);
+                
+                set_rhs_last_pointer_info (0, DATA_INT & 0x1f);
+                free (name);
+                
+                return;
+            
+            }
+        
+        }
+        
+        if (tok.kind == TOK_LBRACK) {
+        
+            src = find_local_symbol (name);
+            
+            if (src) {
+            
+                int elem_size;
+                
+                {
+                
+                    elem_size = src->is_array ? (src->pointer_depth ? DATA_PTR : (src->array_element_size > 0 ? src->array_element_size : src->pointed_size)) :
+                        (src->pointer_depth > 1 ? DATA_PTR : src->pointed_size);
+                    
+                    if (src->is_array) {
+                    
+                        if (src->is_static && src->static_label) {
+                            emit_load_symbol_address_to_reg_now (reg, src->static_label, 0, 0);
+                        } else {
+                            emit_load_symbol_address_to_reg_now (reg, 0, src->offset, 1);
+                        }
+                    
+                    } else if (src->is_static && src->static_label) {
+                        emit_load_global_to_reg (reg, src->static_label, DATA_PTR);
+                    } else {
+                        emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+                    }
+                    
+                    emit_parse_postfix_subscripts_to_reg_now (reg, elem_size, src->pointer_depth, src->pointed_size);
+                    postfix_copy_lvalue_size = index_step_size (elem_size);
+                
+                }
+                
+                emit_apply_postfix_member_access_to_reg_now (reg);
+                
+                if (!postfix_member_seen && emit_store_assignment_to_aggregate_address_now (reg, elem_size, name_start, name_caret, name_line)) {
+                
+                    free (name);
+                    return;
+                
+                }
+                
+                if (postfix_member_seen) {
+                    set_rhs_last_pointer_info (postfix_member_pointer_depth, postfix_member_pointed_size);
+                } else {
+                    set_rhs_last_pointer_info (src->pointer_depth > 0 ? src->pointer_depth - 1 : 0, src->pointed_size);
+                }
+                
+                free (name);
+                return;
+            
+            }
+            
+            if (find_global_symbol (name) >= 0) {
+            
+                int elem_size;
+                
+                {
+                
+                    elem_size = get_global_symbol_array (name) ?
+                        (get_global_symbol_pointer_depth (name) ? DATA_PTR : get_global_symbol_pointed_size (name)) :
+                            (get_global_symbol_pointer_depth (name) > 1 ? DATA_PTR : get_global_symbol_pointed_size (name));
+                    
+                    if (get_global_symbol_array (name)) {
+                        emit_load_symbol_address_to_reg_now (reg, name, 0, 0);
+                    } else {
+                        emit_load_global_to_reg (reg, name, DATA_PTR);
+                    }
+                    
+                    emit_parse_postfix_subscripts_to_reg_now (reg, elem_size, get_global_symbol_pointer_depth (name), get_global_symbol_pointed_size (name));
+                    postfix_copy_lvalue_size = index_step_size (elem_size);
+                
+                }
+                
+                emit_apply_postfix_member_access_to_reg_now (reg);
+                
+                if (!postfix_member_seen && emit_store_assignment_to_aggregate_address_now (reg, elem_size, name_start, name_caret, name_line)) {
+                
+                    free (name);
+                    return;
+                
+                }
+                
+                free (name);
+                
+                return;
+            
+            }
+        
+        }
+        
+        if (tok.kind == TOK_LPAREN) {
+        
+            if (!find_local_symbol (name)) {
+                ensure_global_function_symbol (name, name_start, name_caret, name_line);
+            }
+            
+            if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION && get_global_symbol_returns_void (name)) {
+                report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "void function '%s' used as a value", name);
+            }
+            
+            emit_call_identifier_to_reg_now (name, reg, name_start, name_caret, name_line);
+            
+            if (tok.kind == TOK_LBRACK) {
+            
+                int subscript_pointer_depth = get_global_symbol_pointer_depth (name);
+                int subscript_elem_size = get_global_symbol_pointed_size (name);
+                
+                if (subscript_elem_size <= 0) {
+                    subscript_elem_size = DATA_INT & 0x1f;
+                }
+                
+                emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, subscript_elem_size);
+                
+                if (is_assignment_operator (tok.kind)) {
+                
+                    enum token_kind assign_op = tok.kind;
+                    
+                    get_token ();
+                    emit_push_reg_now (reg);
+                    
+                    if (assign_op == TOK_ASSIGN) {
+                    
+                        if (subscript_elem_size > (DATA_LLONG & 0x1f)) {
+                        
+                            pending_struct_return_lhs = 0;
+                            pending_struct_return_global_name = 0;
+                            pending_struct_return_stack_address = 1;
+                            pending_struct_return_stack_offset = 0;
+                            
+                            emit_load_assignment_rhs_expression_to_reg (reg);
+                            
+                            pending_struct_return_stack_address = 0;
+                            pending_struct_return_stack_offset = 0;
+                            
+                            emit_pop_reg_now ("edx");
+                            postfix_member_seen = 0;
+                            
+                            return;
+                        
+                        }
+                        
+                        emit_load_assignment_rhs_expression_to_reg (reg);
+                    
+                    } else {
+                    
+                        emit_load_deref_reg_now (reg, subscript_elem_size);
+                        emit_push_reg_now (reg);
+                        
+                        emit_load_assignment_rhs_expression_to_reg ("edx");
+                        emit_pop_reg_now (reg);
+                        
+                        emit_assignment_binary_op (assign_op, 0);
+                    
+                    }
+                    
+                    emit_pop_reg_now ("edx");
+                    emit_store_member_to_addr_reg_now ("edx", 0, reg, subscript_elem_size);
+                    set_rhs_last_pointer_info (0, 0);
+                    
+                    free (name);
+                    return;
+                
+                }
+                
+                emit_load_deref_reg_now (reg, subscript_elem_size);
+                
+                if (subscript_pointer_depth > 0) {
+                    subscript_pointer_depth--;
+                }
+                
+                if (subscript_pointer_depth > 0) {
+                    set_rhs_last_pointer_info (subscript_pointer_depth, subscript_elem_size);
+                } else {
+                    set_rhs_last_pointer_info (0, 0);
+                }
+            
+            }
+            
+            emit_apply_postfix_member_access_to_reg_now (reg);
+            
+            if (postfix_member_seen && tok.kind == TOK_LPAREN) {
+            
+                emit_call_pointer_in_reg_now (reg, reg);
+                
+                set_rhs_last_pointer_info (0, 0);
+                free (name);
+                
+                return;
+            
+            }
+            
+            if (postfix_member_seen) {
+                set_rhs_last_pointer_info (postfix_member_pointer_depth, postfix_member_pointed_size);
+            }
+            
+            free (name);
+            return;
+        
+        }
+        
+        if (is_assignment_operator (tok.kind)) {
+        
+            enum token_kind assign_op = tok.kind;
+            struct local_symbol *dst;
+            int global_index;
+            int dst_size;
+            int dst_is_floating;
+            
+            get_token ();
+            
+            dst = find_local_symbol (name);
+            global_index = find_global_symbol (name);
+            
+            if (!dst && global_index < 0) {
+            
+                report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+                
+                if (state->ofp) {
+                
+                    if (state->syntax & ASM_SYNTAX_INTEL) {
+                        fprintf (state->ofp, "    xor %s, %s\n", reg, reg);
+                    } else {
+                        fprintf (state->ofp, "    xorl %%%s, %%%s\n", reg, reg);
+                    }
+                
+                }
+                
+                free (name);
+                return;
+            
+            }
+            
+            dst_size = dst ? dst->size : get_global_symbol_size (name);
+            dst_is_floating = dst ? dst->is_floating : get_global_symbol_floating (name);
+            
+            if (dst_is_floating) {
+            
+                report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "floating assignment expression not implemented");
+                skip_balanced_until (TOK_RPAREN, TOK_SEMI, TOK_EOF);
+                
+                free (name);
+                return;
+            
+            }
+            
+            if (dst_size == (DATA_LLONG & 0x1f)) {
+            
+                report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "64-bit assignment expression not implemented");
+                skip_balanced_until (TOK_RPAREN, TOK_SEMI, TOK_EOF);
+                
+                free (name);
+                return;
+            
+            }
+            
+            if (assign_op == TOK_ASSIGN) {
+                emit_load_assignment_rhs_expression_to_reg (reg);
+            } else {
+            
+                if (dst) {
+                
+                    if (dst->is_static && dst->static_label) {
+                        emit_load_global_to_reg (reg, dst->static_label, dst->size);
+                    } else {
+                        emit_load_local_to_reg (reg, dst->offset, dst->size);
+                    }
+                
+                } else {
+                    emit_load_global_to_reg (reg, name, dst_size);
+                }
+                
+                emit_load_assignment_rhs_expression_to_reg ("edx");
+                
+                if (strcmp (reg, "eax") != 0 && state->ofp) {
+                
+                    if (state->syntax & ASM_SYNTAX_INTEL) {
+                        fprintf (state->ofp, "    mov eax, %s\n", reg);
+                    } else {
+                        fprintf (state->ofp, "    movl %%%s, %%eax\n", reg);
+                    }
+                
+                }
+                
+                emit_assignment_binary_op (assign_op, dst ? dst->is_unsigned : get_global_symbol_unsigned (name));
+                
+                if (strcmp (reg, "eax") != 0 && state->ofp) {
+                
+                    if (state->syntax & ASM_SYNTAX_INTEL) {
+                        fprintf (state->ofp, "    mov %s, eax\n", reg);
+                    } else {
+                        fprintf (state->ofp, "    movl %%eax, %%%s\n", reg);
+                    }
+                
+                }
+            
+            }
+            
+            if (dst) {
+            
+                if (dst->is_static && dst->static_label) {
+                    emit_store_reg_to_global (dst->static_label, dst->size, reg);
+                } else {
+                    emit_store_reg_to_local (dst->offset, dst->size, reg);
+                }
+                
+                if (dst->is_array || dst->pointer_depth > 0) {
+                    set_rhs_last_pointer_info (dst->is_array ? 1 : dst->pointer_depth, dst->is_array ? local_array_pointer_step_size (dst) : dst->pointed_size);
+                } else {
+                    clear_rhs_last_pointer_info ();
+                }
+            
+            } else {
+            
+                emit_store_reg_to_global (name, dst_size, reg);
+                
+                if (get_global_symbol_array (name) || get_global_symbol_pointer_depth (name) > 0) {
+                    set_rhs_last_pointer_info (get_global_symbol_array (name) ? 1 : get_global_symbol_pointer_depth (name), get_global_symbol_array (name) ? global_array_pointer_step_size (name) : get_global_symbol_pointed_size (name));
+                } else {
+                    clear_rhs_last_pointer_info ();
+                }
+            
+            }
+            
+            free (name);
+            return;
+        
+        }
+        
+        if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+        
+            postfix_incdec = 1;
+            postfix_op = tok.kind;
+            
+            get_token ();
+        
+        }
+        
+        src = find_local_symbol (name);
+        
+        if (postfix_incdec && tok.kind == TOK_LBRACK) {
+        
+            int pointer_depth = 0;
+            int pointed_size = 0;
+            int elem_size = DATA_INT & 0x1f;
+            int is_unsigned = 1;
+            int known = 0;
+            
+            if (src) {
+            
+                known = 1;
+                
+                pointer_depth = src->pointer_depth;
+                pointed_size = src->pointed_size;
+                
+                is_unsigned = src->is_unsigned;
+                
+                if (src->is_static && src->static_label) {
+                    emit_load_global_to_reg (reg, src->static_label, DATA_PTR);
+                } else {
+                    emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+                }
+            
+            } else if (find_global_symbol (name) >= 0) {
+            
+                known = 1;
+                
+                pointer_depth = get_global_symbol_pointer_depth (name);
+                pointed_size = get_global_symbol_pointed_size (name);
+                
+                is_unsigned = get_global_symbol_unsigned (name);
+                emit_load_global_to_reg (reg, name, DATA_PTR);
+            
+            }
+            
+            if (!known) {
+            
+                report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+                
+                free (name);
+                return;
+            
+            }
+            
+            if (pointer_depth <= 0) {
+            
+                report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "subscripted value is not a pointer");
+                
+                free (name);
+                return;
+            
+            }
+            
+            if (pointer_depth > 1) {
+                elem_size = DATA_PTR & 0x1f;
+            } else if (pointed_size > 0) {
+                elem_size = pointed_size & 0x1f;
+            }
+            
+            emit_push_reg_now (reg);
+            
+            emit_incdec_symbol_now (src, name, postfix_op, name_line, name_start, name_caret);
+            emit_pop_reg_now (reg);
+            
+            emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size);
+            
+            if (is_assignment_operator (tok.kind)) {
+            
+                enum token_kind assign_op = tok.kind;
+                get_token ();
+                
+                emit_push_reg_now (reg);
+                
+                if (assign_op == TOK_ASSIGN) {
+                    emit_load_assignment_rhs_expression_to_reg (reg);
+                } else {
+                
+                    emit_load_deref_reg_now (reg, elem_size);
+                    emit_push_reg_now (reg);
+                    
+                    emit_load_assignment_rhs_expression_to_reg ("edx");
+                    emit_pop_reg_now (reg);
+                    
+                    emit_assignment_binary_op (assign_op, is_unsigned);
+                
+                }
+                
+                emit_pop_reg_now ("edx");
+                emit_store_reg_to_deref_reg_now ("edx", reg, elem_size);
+                
+                clear_rhs_last_pointer_info ();
+                free (name);
+                
+                return;
+            
+            }
+            
+            emit_load_deref_reg_now (reg, elem_size);
+            
+            if (pointer_depth > 1) {
+                set_rhs_last_pointer_info (pointer_depth - 1, pointed_size);
+            } else {
+                clear_rhs_last_pointer_info ();
+            }
+            
+            free (name);
+            return;
+        
+        }
+        
+        if (src) {
+        
+            if (src->is_array) {
+            
+                if (src->is_static && src->static_label) {
+                    emit_load_address_to_reg_now (reg, src->static_label);
+                } else {
+                    emit_load_local_address_to_reg_now (reg, src->offset);
+                }
+            
+            } else if (tok.kind == TOK_DOT) {
+            
+                if (src->is_static && src->static_label) {
+                    emit_load_address_to_reg_now (reg, src->static_label);
+                } else {
+                    emit_load_local_address_to_reg_now (reg, src->offset);
+                }
+            
+            } else if (src->is_static && src->static_label) {
+                emit_load_global_to_reg (reg, src->static_label, src->size);
+            } else {
+                emit_load_local_to_reg (reg, src->offset, src->size);
+            }
+            
+            if (src->pointer_depth > 0) {
+            
+                /*
+                 * Keep the pointed-to aggregate information even when the
+                 * arrow is not immediately visible.  Macro expansions commonly
+                 * parenthesize pointer operands, e.g. ((unknown)->type).
+                 * The inner expression sees ')' after 'unknown', and the
+                 * outer parenthesized path sees the later '->'.  If we clear
+                 * the tag here, that later member lookup falls back to an
+                 * unrelated member named 'type' and emits offset 0 instead of
+                 * the struct cpp_unknown offset.
+                 */
+                postfix_copy_lvalue_size = src->pointed_size;
+                postfix_copy_lvalue_tag_name = src->pointed_tag_name;
+            
+            } else {
+            
+                postfix_copy_lvalue_size = src->size;
+                postfix_copy_lvalue_tag_name = src->tag_name;
+            
+            }
+            
+            emit_apply_postfix_member_access_to_reg_now (reg);
+            
+            if (postfix_member_seen && tok.kind == TOK_LPAREN) {
+            
+                emit_call_pointer_in_reg_now (reg, reg);
+                
+                set_rhs_last_pointer_info (0, 0);
+                free (name);
+                
+                return;
+            
+            }
+            
+            if (emit_store_assignment_to_postfix_member_now (reg)) {
+            
+                set_rhs_last_pointer_info (postfix_member_pointer_depth, postfix_member_pointed_size);
+                
+                free (name);
+                return;
+            
+            }
+            
+            if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+            
+                postfix_incdec = 1;
+                postfix_op = tok.kind;
+                
+                get_token ();
+            
+            }
+            
+            if (postfix_incdec) {
+            
+                if (postfix_member_seen) {
+                    emit_apply_postfix_member_incdec_now (reg, postfix_op);
+                } else {
+                
+                    emit_push_reg_now (reg);
+                    emit_incdec_symbol_now (src, name, postfix_op, name_line, name_start, name_caret);
+                    emit_pop_reg_now (reg);
+                
+                }
+            
+            }
+            
+            if (postfix_member_seen) {
+                set_rhs_last_pointer_info (postfix_member_pointer_depth, postfix_member_pointed_size);
+            } else {
+                set_rhs_last_pointer_info (src->is_array ? 1 : src->pointer_depth, src->is_array ? local_array_pointer_step_size (src) : src->pointed_size);
+            }
+            
+            free (name);
+            return;
+        
+        }
+        
+        if (find_global_symbol (name) >= 0) {
+        
+            if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION ||
+                get_global_symbol_array (name) ||
+                (!get_global_symbol_pointer_depth (name) &&
+                    get_global_symbol_size (name) > (DATA_PTR & 0x1f)) ||
+                tok.kind == TOK_DOT) {
+                emit_load_address_to_reg_now (reg, name);
+            } else {
+                emit_load_global_to_reg (reg, name, get_global_symbol_size (name));
+            }
+            
+            if (tok.kind == TOK_ARROW && get_global_symbol_pointer_depth (name) > 0) {
+            
+                postfix_copy_lvalue_size = get_global_symbol_pointed_size (name);
+                postfix_copy_lvalue_tag_name = get_global_symbol_tag_name (name);
+            
+            } else {
+            
+                postfix_copy_lvalue_size = get_global_symbol_size (name);
+                postfix_copy_lvalue_tag_name = 0;
+            
+            }
+            
+            emit_apply_postfix_member_access_to_reg_now (reg);
+            
+            if (postfix_member_seen && tok.kind == TOK_LPAREN) {
+            
+                emit_call_pointer_in_reg_now (reg, reg);
+                
+                set_rhs_last_pointer_info (0, 0);
+                free (name);
+                
+                return;
+            
+            }
+            
+            if (emit_store_assignment_to_postfix_member_now (reg)) {
+            
+                set_rhs_last_pointer_info (postfix_member_pointer_depth, postfix_member_pointed_size);
+                
+                free (name);
+                return;
+            
+            }
+            
+            if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+            
+                postfix_incdec = 1;
+                postfix_op = tok.kind;
+                
+                get_token ();
+            
+            }
+            
+            if (postfix_incdec) {
+            
+                if (postfix_member_seen) {
+                    emit_apply_postfix_member_incdec_now (reg, postfix_op);
+                } else {
+                
+                    emit_push_reg_now (reg);
+                    emit_incdec_symbol_now (0, name, postfix_op, name_line, name_start, name_caret);
+                    emit_pop_reg_now (reg);
+                
+                }
+            
+            }
+            
+            if (postfix_member_seen) {
+                set_rhs_last_pointer_info (postfix_member_pointer_depth, postfix_member_pointed_size);
+            } else {
+                set_rhs_last_pointer_info (get_global_symbol_array (name) ? 1 : get_global_symbol_pointer_depth (name), get_global_symbol_array (name) ? global_array_pointer_step_size (name) : get_global_symbol_pointed_size (name));
+            }
+            
+            free (name);
+            return;
+        
+        }
+        
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+        free (name);
+        
+        if (state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    xor %s, %s\n", reg, reg);
+            } else {
+                fprintf (state->ofp, "    xorl %%%s, %%%s\n", reg, reg);
+            }
+        
+        }
+        
+        return;
+    
+    }
+    
+    if (recover_unknown_rhs_identifier ()) {
+    
+        if (state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    xor %s, %s\n", reg, reg);
+            } else {
+                fprintf (state->ofp, "    xorl %%%s, %%%s\n", reg, reg);
+            }
+        
+        }
+        
+        return;
+    
+    }
+    
+    {
+    
+        int64_s v = const64_from_current_operand ();
+        
+        if (state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    mov %s, %lu\n", reg, v.low & U32_MASK);
+            } else {
+                fprintf (state->ofp, "    movl $%lu, %%%s\n", v.low & U32_MASK, reg);
+            }
+        
+        }
+    
+    }
+
+}
+
+static int local_array_pointer_step_size (const struct local_symbol *sym) {
+
+    if (!sym || !sym->is_array) {
+        return 0;
+    }
+    
+    if (sym->array_element_size > 0) {
+        return index_step_size (sym->array_element_size);
+    }
+    
+    if (sym->pointed_size > 0 && sym->pointed_size < sym->size) {
+        return index_step_size (sym->pointed_size);
+    }
+    
+    return index_step_size (sym->size);
+
+}
+
+static int global_array_pointer_step_size (const char *name) {
+
+    long count;
+    int pointed_size;
+    
+    if (!name || !get_global_symbol_array (name)) {
+        return 0;
+    }
+    
+    if (get_global_symbol_array_element_size (name) > 0) {
+        return get_global_symbol_array_element_size (name);
+    }
+    
+    count = get_global_symbol_array_count (name);
+    
+    if (count > 0) {
+        return (int) (get_global_symbol_size (name) / count);
+    }
+    
+    pointed_size = get_global_symbol_pointed_size (name);
+    
+    if (pointed_size > 0 && pointed_size < get_global_symbol_size (name)) {
+        return index_step_size (pointed_size);
+    }
+    
+    return index_step_size (get_global_symbol_size (name));
+
+}
+
+static void emit_scale_reg_by_const_now (const char *reg, int scale) {
+
+    if (!state->ofp || scale <= 1) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+        fprintf (state->ofp, "    imul %s, %d\n", reg, scale);
+    } else {
+        fprintf (state->ofp, "    imull $%d, %%%s, %%%s\n", scale, reg, reg);
+    }
+
+}
+
+static void emit_divide_eax_by_const_now (int divisor) {
+
+    if (!state->ofp || divisor <= 1) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        fprintf (state->ofp, "    mov ecx, %d\n", divisor);
+        fprintf (state->ofp, "    cdq\n");
+        fprintf (state->ofp, "    idiv ecx\n");
+    
+    } else {
+    
+        fprintf (state->ofp, "    movl $%d, %%ecx\n", divisor);
+        fprintf (state->ofp, "    cdq\n");
+        fprintf (state->ofp, "    idivl %%ecx\n");
+    
+    }
+
+}
+
+static void emit_assignment_binary_op (enum token_kind op, int is_unsigned) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        switch (op) {
+        
+            case TOK_PLUS:      case TOK_PLUSEQ:
+            
+                fprintf (state->ofp, "    add eax, edx\n");
+                break;
+            
+            case TOK_MINUS:     case TOK_MINUSEQ:
+            
+                fprintf (state->ofp, "    sub eax, edx\n");
+                break;
+            
+            case TOK_STAR:      case TOK_STAREQ:
+            
+                fprintf (state->ofp, "    imul eax, edx\n");
+                break;
+            
+            case TOK_BSLASH:    case TOK_SLASHEQ:
+            
+                fprintf (state->ofp, "    mov ecx, edx\n");
+                
+                if (is_unsigned) {
+                
+                    fprintf (state->ofp, "    xor edx, edx\n");
+                    fprintf (state->ofp, "    div ecx\n");
+                
+                } else {
+                
+                    fprintf (state->ofp, "    cdq\n");
+                    fprintf (state->ofp, "    idiv ecx\n");
+                
+                }
+                
+                break;
+            
+            case TOK_MOD:
+            
+                fprintf (state->ofp, "    mov ecx, edx\n");
+                
+                if (is_unsigned) {
+                
+                    fprintf (state->ofp, "    xor edx, edx\n");
+                    fprintf (state->ofp, "    div ecx\n");
+                
+                } else {
+                
+                    fprintf (state->ofp, "    cdq\n");
+                    fprintf (state->ofp, "    idiv ecx\n");
+                
+                }
+                
+                fprintf (state->ofp, "    mov eax, edx\n");
+                break;
+            
+            case TOK_MODEQ:
+            
+                fprintf (state->ofp, "    mov ecx, edx\n");
+                
+                if (is_unsigned) {
+                
+                    fprintf (state->ofp, "    xor edx, edx\n");
+                    fprintf (state->ofp, "    div ecx\n");
+                
+                } else {
+                
+                    fprintf (state->ofp, "    cdq\n");
+                    fprintf (state->ofp, "    idiv ecx\n");
+                
+                }
+                
+                fprintf (state->ofp, "    mov eax, edx\n");
+                break;
+            
+            case TOK_AMPER:     case TOK_ANDEQ:
+            
+                fprintf (state->ofp, "    and eax, edx\n");
+                break;
+            
+            case TOK_PIPE:      case TOK_OREQ:
+            
+                fprintf (state->ofp, "    or eax, edx\n");
+                break;
+            
+            case TOK_CARET:     case TOK_XOREQ:
+            
+                fprintf (state->ofp, "    xor eax, edx\n");
+                break;
+            
+            case TOK_LSH:       case TOK_LSHEQ:
+            
+                fprintf (state->ofp, "    mov ecx, edx\n");
+                fprintf (state->ofp, "    shl eax, cl\n");
+                
+                break;
+            
+            case TOK_RSH:       case TOK_RSHEQ:
+            
+                fprintf (state->ofp, "    mov ecx, edx\n");
+                fprintf (state->ofp, is_unsigned ? "    shr eax, cl\n" : "    sar eax, cl\n");
+                
+                break;
+            
+            default:
+            
+                break;
+        
+        }
+    
+    } else {
+    
+        switch (op) {
+        
+            case TOK_PLUS:      case TOK_PLUSEQ:
+            
+                fprintf (state->ofp, "    addl %%edx, %%eax\n");
+                break;
+            
+            case TOK_MINUS:     case TOK_MINUSEQ:
+            
+                fprintf (state->ofp, "    subl %%edx, %%eax\n");
+                break;
+            
+            case TOK_STAR:      case TOK_STAREQ:
+            
+                fprintf (state->ofp, "    imull %%edx, %%eax\n");
+                break;
+            
+            case TOK_BSLASH:    case TOK_SLASHEQ:
+            
+                fprintf (state->ofp, "    movl %%edx, %%ecx\n");
+                
+                if (is_unsigned) {
+                
+                    fprintf (state->ofp, "    xorl %%edx, %%edx\n");
+                    fprintf (state->ofp, "    divl %%ecx\n");
+                
+                } else {
+                
+                    fprintf (state->ofp, "    cdq\n");
+                    fprintf (state->ofp, "    idivl %%ecx\n");
+                
+                }
+                
+                break;
+            
+            case TOK_MOD:
+            
+                fprintf (state->ofp, "    movl %%edx, %%ecx\n");
+                
+                if (is_unsigned) {
+                
+                    fprintf (state->ofp, "    xorl %%edx, %%edx\n");
+                    fprintf (state->ofp, "    divl %%ecx\n");
+                
+                } else {
+                
+                    fprintf (state->ofp, "    cdq\n");
+                    fprintf (state->ofp, "    idivl %%ecx\n");
+                
+                }
+                
+                fprintf (state->ofp, "    movl %%edx, %%eax\n");
+                break;
+            
+            case TOK_MODEQ:
+            
+                fprintf (state->ofp, "    movl %%edx, %%ecx\n");
+                
+                if (is_unsigned) {
+                
+                    fprintf (state->ofp, "    xorl %%edx, %%edx\n");
+                    fprintf (state->ofp, "    divl %%ecx\n");
+                
+                } else {
+                
+                    fprintf (state->ofp, "    cdq\n");
+                    fprintf (state->ofp, "    idivl %%ecx\n");
+                
+                }
+                
+                fprintf (state->ofp, "    movl %%edx, %%eax\n");
+                break;
+            
+            case TOK_AMPER:     case TOK_ANDEQ:
+            
+                fprintf (state->ofp, "    andl %%edx, %%eax\n");
+                break;
+            
+            case TOK_PIPE:      case TOK_OREQ:
+            
+                fprintf (state->ofp, "    orl %%edx, %%eax\n");
+                break;
+            
+            case TOK_CARET:     case TOK_XOREQ:
+            
+                fprintf (state->ofp, "    xorl %%edx, %%eax\n");
+                break;
+            
+            case TOK_LSH:       case TOK_LSHEQ:
+            
+                fprintf (state->ofp, "    movl %%edx, %%ecx\n");
+                fprintf (state->ofp, "    sall %%cl, %%eax\n");
+                
+                break;
+            
+            case TOK_RSH:       case TOK_RSHEQ:
+            
+                fprintf (state->ofp, "    movl %%edx, %%ecx\n");
+                fprintf (state->ofp, is_unsigned ? "    shrl %%cl, %%eax\n" : "    sarl %%cl, %%eax\n");
+                
+                break;
+            
+            default:
+            
+                break;
+        
+        }
+    
+    }
+
+}
+
+static int token_is_floating_constant_now (void) {
+    return tok.kind == TOK_CFLOAT || tok.kind == TOK_CDOUBLE || tok.kind == TOK_CLDOUBLE;
+}
+
+static int64_s floating_constant_to_bits_now (int size) {
+
+    int64_s r;
+    
+    unsigned long bits32;
+    unsigned char bytes[8];
+    
+    int i;
+    
+    r.low = 0;
+    r.high = 0;
+    
+    if (size == (DATA_FLOAT & 0x1f)) {
+    
+        float f;
+        
+        if (tok.kind == TOK_CFLOAT) {
+            f = tok.val.f;
+        } else if (tok.kind == TOK_CLDOUBLE) {
+            f = (float) tok.val.ld;
+        } else {
+            f = (float) tok.val.d;
+        }
+        
+        memcpy (&bits32, &f, sizeof (f));
+        
+        r.low = bits32;
+        get_token ();
+        
+        return r;
+    
+    }
+    
+    {
+    
+        double d;
+        
+        if (tok.kind == TOK_CFLOAT) {
+            d = (double) tok.val.f;
+        } else if (tok.kind == TOK_CLDOUBLE) {
+            d = (double) tok.val.ld;
+        } else {
+            d = tok.val.d;
+        }
+        
+        memset (bytes, 0, sizeof (bytes));
+        memcpy (bytes, &d, sizeof (d));
+        
+        for (i = 0; i < 4; i++) {
+            r.low |= ((unsigned long) bytes[i]) << (i * 8);
+        }
+        
+        for (i = 0; i < 4; i++) {
+            r.high |= ((unsigned long) bytes[i + 4]) << (i * 8);
+        }
+        
+        get_token ();
+        return r;
+    
+    }
+
+}
+
+static long double floating_constant_to_ld_now (void) {
+
+    long double v;
+    
+    if (tok.kind == TOK_CFLOAT) {
+        v = (long double) tok.val.f;
+    } else if (tok.kind == TOK_CLDOUBLE) {
+        v = tok.val.ld;
+    } else {
+        v = (long double) tok.val.d;
+    }
+    
+    get_token ();
+    return v;
+
+}
+
+static long double parse_floating_const_expr_value_now (void);
+static long double parse_floating_const_term_now (void);
+static long double parse_floating_const_primary_now (void);
+
+static long double parse_floating_const_primary_now (void) {
+
+    long double v;
+    int64_s iv;
+    
+    if (_accept (TOK_LPAREN)) {
+        v = parse_floating_const_expr_value_now ();
+        expect (TOK_RPAREN, ")");
+        return v;
+    }
+    
+    if (_accept (TOK_PLUS)) {
+        return parse_floating_const_primary_now ();
+    }
+    
+    if (_accept (TOK_MINUS)) {
+        return -parse_floating_const_primary_now ();
+    }
+    
+    if (token_is_floating_constant_now ()) {
+        return floating_constant_to_ld_now ();
+    }
+    
+    iv = const64_from_current_operand ();
+    v = (long double) iv.low;
+    
+    if (iv.high != 0) {
+        v += ((long double) iv.high) * 4294967296.0L;
+    }
+    
+    return v;
+
+}
+
+static long double parse_floating_const_term_now (void) {
+
+    long double v;
+    enum token_kind op;
+    long double rhs;
+    
+    v = parse_floating_const_primary_now ();
+    
+    while (tok.kind == TOK_STAR || tok.kind == TOK_BSLASH) {
+    
+        op = tok.kind;
+        get_token ();
+        rhs = parse_floating_const_primary_now ();
+        
+        if (op == TOK_STAR) {
+            v *= rhs;
+        } else {
+            v /= rhs;
+        }
+    
+    }
+    
+    return v;
+
+}
+
+static long double parse_floating_const_expr_value_now (void) {
+
+    long double v;
+    enum token_kind op;
+    long double rhs;
+    
+    v = parse_floating_const_term_now ();
+    
+    while (tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) {
+    
+        op = tok.kind;
+        get_token ();
+        rhs = parse_floating_const_term_now ();
+        
+        if (op == TOK_PLUS) {
+            v += rhs;
+        } else {
+            v -= rhs;
+        }
+    
+    }
+    
+    return v;
+
+}
+
+static int64_s parse_floating_const_expr_bits_now (int size) {
+
+    long double acc;
+    int64_s r;
+    
+    acc = parse_floating_const_expr_value_now ();
+    r.low = 0;
+    r.high = 0;
+    
+    if (size == (DATA_FLOAT & 0x1f)) {
+    
+        float f;
+        unsigned long bits32;
+        
+        f = (float) acc;
+        bits32 = 0;
+        memcpy (&bits32, &f, sizeof (f));
+        r.low = bits32;
+        return r;
+    
+    }
+    
+    {
+    
+        double d;
+        unsigned char bytes[8];
+        int i;
+        
+        d = (double) acc;
+        memset (bytes, 0, sizeof (bytes));
+        memcpy (bytes, &d, sizeof (d));
+        
+        for (i = 0; i < 4; i++) {
+            r.low |= ((unsigned long) bytes[i]) << (i * 8);
+        }
+        
+        for (i = 0; i < 4; i++) {
+            r.high |= ((unsigned long) bytes[i + 4]) << (i * 8);
+        }
+        
+        return r;
+    
+    }
+
+}
+
+static void emit_load_floating_const_bits_now (int size, int64_s v) {
+
+    int lab;
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    lab = anon_label++;
+    
+    if (state->syntax & ASM_SYNTAX_MASM) {
+    
+        fprintf (state->ofp, ".data\n");
+        fprintf (state->ofp, "LC%d_flt:\n", lab);
+        fprintf (state->ofp, "    dd %lu\n", v.low & U32_MASK);
+        
+        if (size == (DATA_DOUBLE & 0x1f)) {
+            fprintf (state->ofp, "    dd %lu\n", v.high & U32_MASK);
+        }
+        
+        fprintf (state->ofp, ".code\n");
+        fprintf (state->ofp, "    fld %s ptr LC%d_flt\n", size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", lab);
+    
+    } else {
+    
+        if (state->syntax & ASM_SYNTAX_NASM) {
+        
+            fprintf (state->ofp, "section .data\n");
+            fprintf (state->ofp, "LC%d_flt:\n", lab);
+            fprintf (state->ofp, "    dd %lu\n", v.low & U32_MASK);
+            
+            if (size == (DATA_DOUBLE & 0x1f)) {
+                fprintf (state->ofp, "    dd %lu\n", v.high & U32_MASK);
+            }
+            
+            fprintf (state->ofp, "section .text\n");
+            fprintf (state->ofp, "    fld %s [LC%d_flt]\n", size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", lab);
+        
+        } else {
+        
+            fprintf (state->ofp, ".data\n");
+            fprintf (state->ofp, ".LC%d_flt:\n", lab);
+            fprintf (state->ofp, "    .long %lu\n", v.low & U32_MASK);
+            
+            if (size == (DATA_DOUBLE & 0x1f)) {
+                fprintf (state->ofp, "    .long %lu\n", v.high & U32_MASK);
+            }
+            
+            fprintf (state->ofp, ".text\n");
+            
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    fld %s ptr .LC%d_flt\n", size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", lab);
+            } else {
+                fprintf (state->ofp, "    fld%s .LC%d_flt\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", lab);
+            }
+        
+        }
+    
+    }
+
+}
+
+static void emit_load_floating_symbol_now (struct local_symbol *sym, const char *name, int size) {
+
+    char memref[64];
+    const char *label;
+    const char *asm_name;
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    asm_name = asm_global_symbol_name (name);
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        if (sym) {
+        
+            if (sym->is_static && sym->static_label) {
+                fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    fld %s [%s]\n" : "    fld %s ptr %s\n"), size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", sym->static_label);
+            } else {
+            
+                format_intel_ebp_offset (memref, sizeof (memref), sym->offset);
+                fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    fld %s %s\n" : "    fld %s ptr %s\n"), size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", memref);
+            
+            }
+        
+        } else {
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    fld %s [%s]\n" : "    fld %s ptr %s\n"), size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", asm_name);
+        }
+    
+    } else {
+    
+        if (sym) {
+        
+            label = (sym->is_static && sym->static_label) ? sym->static_label : 0;
+            
+            if (label) {
+                fprintf (state->ofp, "    fld%s %s\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", label);
+            } else {
+                fprintf (state->ofp, "    fld%s %ld(%%ebp)\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", sym->offset);
+            }
+        
+        } else {
+            fprintf (state->ofp, "    fld%s %s\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", asm_name);
+        }
+    
+    }
+
+}
+
+static void emit_load_floating_member_symbol_now (struct local_symbol *sym, const char *name, int offset, int size) {
+
+    char memref[64];
+    char labelref[256];
+    
+    const char *label;
+    const char *asm_name;
+    const char *opsize;
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    opsize = size == (DATA_FLOAT & 0x1f) ? "dword" : "qword";
+    asm_name = asm_global_symbol_name (name);
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        if (sym) {
+        
+            if (sym->is_static && sym->static_label) {
+            
+                if (offset) {
+                
+                    sprintf (labelref, "%s + %d", sym->static_label, offset);
+                    label = labelref;
+                
+                } else {
+                    label = sym->static_label;
+                }
+                
+                fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    fld %s %s\n" : "    fld %s ptr %s\n"), opsize, label);
+            
+            } else {
+            
+                format_intel_ebp_offset (memref, sizeof (memref), sym->offset + offset);
+                fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    fld %s %s\n" : "    fld %s ptr %s\n"), opsize, memref);
+            
+            }
+        
+        } else {
+        
+            if (offset) {
+            
+                sprintf (labelref, "%s + %d", asm_name, offset);
+                label = labelref;
+            
+            } else {
+                label = asm_name;
+            }
+            
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    fld %s %s\n" : "    fld %s ptr %s\n"), opsize, label);
+        
+        }
+    
+    } else {
+    
+        if (sym) {
+        
+            label = (sym->is_static && sym->static_label) ? sym->static_label : 0;
+            
+            if (label) {
+            
+                if (offset) {
+                
+                    sprintf (labelref, "%s+%d", label, offset);
+                    label = labelref;
+                
+                }
+                
+                fprintf (state->ofp, "    fld%s %s\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", label);
+            
+            } else {
+                fprintf (state->ofp, "    fld%s %ld(%%ebp)\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", sym->offset + offset);
+            }
+        
+        } else {
+        
+            if (offset) {
+            
+                sprintf (labelref, "%s+%d", asm_name, offset);
+                asm_name = labelref;
+            
+            }
+            
+            fprintf (state->ofp, "    fld%s %s\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", asm_name);
+        
+        }
+    
+    }
+
+}
+
+static void emit_duplicate_floating_stack_top_now (void) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_NASM) {
+        fprintf (state->ofp, "    fld st0\n");
+    } else if (state->syntax & ASM_SYNTAX_INTEL) {
+        fprintf (state->ofp, "    fld st(0)\n");
+    } else {
+        fprintf (state->ofp, "    fld %%st(0)\n");
+    }
+
+}
+
+static void emit_store_floating_symbol_now (struct local_symbol *sym, const char *name, int size) {
+
+    char memref[64];
+    const char *label;
+    const char *asm_name;
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    asm_name = asm_global_symbol_name (name);
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        if (sym) {
+        
+            if (sym->is_static && sym->static_label) {
+                fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    fstp %s %s\n" : "    fstp %s ptr %s\n"), size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", sym->static_label);
+            } else {
+            
+                format_intel_ebp_offset (memref, sizeof (memref), sym->offset);
+                fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    fstp %s %s\n" : "    fstp %s ptr %s\n"), size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", memref);
+            
+            }
+        
+        } else {
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    fstp %s %s\n" : "    fstp %s ptr %s\n"), size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", asm_name);
+        }
+    
+    } else {
+    
+        if (sym) {
+        
+            label = (sym->is_static && sym->static_label) ? sym->static_label : 0;
+            
+            if (label) {
+                fprintf (state->ofp, "    fstp%s %s\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", label);
+            } else {
+                fprintf (state->ofp, "    fstp%s %ld(%%ebp)\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", sym->offset);
+            }
+        
+        } else {
+            fprintf (state->ofp, "    fstp%s %s\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", asm_name);
+        }
+    
+    }
+
+}
+
+static void emit_load_any_symbol_as_floating_now (struct local_symbol *src, const char *name, int size, int is_floating) {
+
+    if (is_floating) {
+    
+        emit_load_floating_symbol_now (src, name, size);
+        return;
+    
+    }
+    
+    if (src) {
+    
+        if (src->is_static && src->static_label) {
+            emit_load_global_to_reg ("eax", src->static_label, src->size);
+        } else {
+            emit_load_local_to_reg ("eax", src->offset, src->size);
+        }
+    
+    } else {
+        emit_load_global_to_reg ("eax", name, size);
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        fprintf (state->ofp, "    sub esp, 4\n");
+        
+        if (state->syntax & ASM_SYNTAX_NASM) {
+        
+            fprintf (state->ofp, "    mov dword [esp], eax\n");
+            fprintf (state->ofp, "    fild dword [esp]\n");
+        
+        } else {
+        
+            fprintf (state->ofp, "    mov dword ptr [esp], eax\n");
+            fprintf (state->ofp, "    fild dword ptr [esp]\n");
+        
+        }
+        
+        fprintf (state->ofp, "    add esp, 4\n");
+    
+    } else {
+    
+        fprintf (state->ofp, "    subl $4, %%esp\n");
+        fprintf (state->ofp, "    movl %%eax, (%%esp)\n");
+        fprintf (state->ofp, "    fildl (%%esp)\n");
+        fprintf (state->ofp, "    addl $4, %%esp\n");
+    
+    }
+
+}
+
+static int emit_load_floating_prefix_incdec_now (void) {
+
+    enum token_kind op;
+    char *name;
+    
+    const char *name_start, *name_caret;
+    unsigned long name_line;
+    
+    struct local_symbol *sym;
+    int size;
+    int is_floating;
+    
+    if (!parse_incdec_identifier_now (&op, &name, &name_start, &name_caret, &name_line)) {
+        return 0;
+    }
+    
+    if (!name) {
+        return 1;
+    }
+    
+    sym = find_local_symbol (name);
+    emit_incdec_symbol_now (sym, name, op, name_line, name_start, name_caret);
+    
+    if (sym) {
+    
+        size = sym->size;
+        is_floating = sym->is_floating;
+        
+        emit_load_any_symbol_as_floating_now (sym, name, size, is_floating);
+    
+    } else if (find_global_symbol (name) >= 0) {
+    
+        size = get_global_symbol_size (name);
+        is_floating = get_global_symbol_floating (name);
+        
+        emit_load_any_symbol_as_floating_now (0, name, size, is_floating);
+    
+    }
+    
+    free (name);
+    return 1;
+
+}
+
+static void emit_load_floating_rhs_expression_now (int result_size);
+static void emit_floating_binary_now (enum token_kind k);
+
+static int floating_assignment_operator_supported_now (enum token_kind op);
+static int is_value_compare_operator (enum token_kind k);
+
+static void emit_statement_label (int label);
+static void emit_statement_label_raw (int label);
+static void emit_statement_jump (int label);
+
+static int floating_rhs_result_in_eax_bool = 0;
+
+static void emit_eax_bool_to_floating_stack_now (void) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        fprintf (state->ofp, "    sub esp, 4\n");
+        
+        if (state->syntax & ASM_SYNTAX_NASM) {
+        
+            fprintf (state->ofp, "    mov dword [esp], eax\n");
+            fprintf (state->ofp, "    fild dword [esp]\n");
+        
+        } else {
+        
+            fprintf (state->ofp, "    mov dword ptr [esp], eax\n");
+            fprintf (state->ofp, "    fild dword ptr [esp]\n");
+        
+        }
+        
+        fprintf (state->ofp, "    add esp, 4\n");
+    
+    } else {
+    
+        fprintf (state->ofp, "    subl $4, %%esp\n");
+        fprintf (state->ofp, "    movl %%eax, (%%esp)\n");
+        fprintf (state->ofp, "    fildl (%%esp)\n");
+        fprintf (state->ofp, "    addl $4, %%esp\n");
+    
+    }
+    
+    floating_rhs_result_in_eax_bool = 0;
+
+}
+
+static const char *floating_compare_true_setcc_now (enum token_kind op) {
+
+    switch (op) {
+    
+        case TOK_LESS:
+        
+            return "setb";
+        
+        case TOK_LTEQ:
+        
+            return "setbe";
+        
+        case TOK_GREATER:
+        
+            return "seta";
+        
+        case TOK_GTEQ:
+        
+            return "setae";
+        
+        case TOK_EQEQ:
+        
+            return "sete";
+        
+        case TOK_NOTEQ:
+        
+            return "setne";
+        
+        default:
+        
+            return "setz";
+    
+    }
+
+}
+
+static void emit_floating_compare_to_eax_now (enum token_kind op) {
+
+    const char *setcc;
+    
+    if (!state->ofp) {
+        floating_rhs_result_in_eax_bool = 1;
+        return;
+    }
+    
+    setcc = floating_compare_true_setcc_now (op);
+    
+    if (state->syntax & ASM_SYNTAX_NASM) {
+    
+        fprintf (state->ofp, "    fxch st1\n");
+        fprintf (state->ofp, "    fcompp\n");
+        fprintf (state->ofp, "    fnstsw ax\n");
+        fprintf (state->ofp, "    sahf\n");
+        fprintf (state->ofp, "    %s al\n", setcc);
+        fprintf (state->ofp, "    movzx eax, al\n");
+    
+    } else if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        fprintf (state->ofp, "    fxch st(1)\n");
+        fprintf (state->ofp, "    fcompp\n");
+        fprintf (state->ofp, "    fnstsw ax\n");
+        fprintf (state->ofp, "    sahf\n");
+        fprintf (state->ofp, "    %s al\n", setcc);
+        fprintf (state->ofp, "    movzx eax, al\n");
+    
+    } else {
+    
+        fprintf (state->ofp, "    fxch %%st(1)\n");
+        fprintf (state->ofp, "    fcompp\n");
+        fprintf (state->ofp, "    fnstsw %%ax\n");
+        fprintf (state->ofp, "    sahf\n");
+        fprintf (state->ofp, "    %s %%al\n", setcc);
+        fprintf (state->ofp, "    movzbl %%al, %%eax\n");
+    
+    }
+    
+    floating_rhs_result_in_eax_bool = 1;
+
+}
+
+static void emit_load_floating_rhs_operand_now (int result_size) {
+
+    if (tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) {
+    
+        enum token_kind unary_op = tok.kind;
+        get_token ();
+        
+        emit_load_floating_rhs_operand_now (result_size);
+        
+        if (unary_op == TOK_MINUS) {
+            fprintf (state->ofp, "    fchs\n");
+        }
+        
+        return;
+    
+    }
+    
+    if (tok.kind == TOK_STAR) {
+    
+        int deref_size = result_size;
+        get_token ();
+        
+        if (tok.kind == TOK_LPAREN) {
+        
+            get_token ();
+            
+            if (is_type_start (tok.kind) && parse_deref_cast_type_name (&deref_size)) {
+            
+                if (!expression_text_has_pluseq_before_delim_now (tok.start) ||
+                    !emit_parse_va_arg_address_to_reg_now ("eax", deref_size)) {
+                    emit_load_assignment_rhs_to_reg ("eax");
+                }
+                
+                emit_load_floating_deref_reg_now ("eax", deref_size);
+                return;
+            
+            }
+            
+            emit_load_assignment_rhs_expression_to_reg ("eax");
+            expect (TOK_RPAREN, ")");
+            
+            emit_load_floating_deref_reg_now ("eax", deref_size);
+            return;
+        
+        }
+        
+        emit_load_assignment_rhs_to_reg ("eax");
+        emit_load_floating_deref_reg_now ("eax", deref_size);
+        
+        return;
+    
+    }
+    
+    if (_accept (TOK_LPAREN)) {
+    
+        if (token_starts_type_name ()) {
+        
+            int cast_size = 0;
+            int cast_is_unsigned = 0;
+            int cast_is_pointer = 0;
+            
+            if (parse_cast_type_name (&cast_size, &cast_is_unsigned, &cast_is_pointer)) {
+            
+                emit_load_floating_rhs_operand_now (result_size);
+                return;
+            
+            }
+        
+        }
+        
+        emit_load_floating_rhs_expression_now (result_size);
+        expect (TOK_RPAREN, ")");
+        
+        if (floating_rhs_result_in_eax_bool && tok.kind != TOK_QMARK) {
+            emit_eax_bool_to_floating_stack_now ();
+        }
+        
+        return;
+    
+    }
+    
+    if (emit_load_floating_prefix_incdec_now ()) {
+        return;
+    }
+    
+    if (token_is_sizeof_keyword ()) {
+    
+        int64_s v = sizeof_from_current_token ();
+        
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+        
+            fprintf (state->ofp, "    sub esp, 4\n");
+            
+            if (state->syntax & ASM_SYNTAX_NASM) {
+                
+                fprintf (state->ofp, "    mov dword [esp], %lu\n", v.low & U32_MASK);
+                fprintf (state->ofp, "    fild dword [esp]\n");
+            
+            } else {
+            
+                fprintf (state->ofp, "    mov dword ptr [esp], %lu\n", v.low & U32_MASK);
+                fprintf (state->ofp, "    fild dword ptr [esp]\n");
+            
+            }
+            
+            fprintf (state->ofp, "    add esp, 4\n");
+        
+        } else {
+        
+            fprintf (state->ofp, "    subl $4, %%esp\n");
+            fprintf (state->ofp, "    movl $%lu, (%%esp)\n", v.low & U32_MASK);
+            fprintf (state->ofp, "    fildl (%%esp)\n");
+            fprintf (state->ofp, "    addl $4, %%esp\n");
+        
+        }
+        
+        return;
+    
+    }
+    
+    if (tok.kind == TOK_IDENT) {
+    
+        char *name = xstrdup (tok.ident);
+        
+        const char *name_start = tok.start, *name_caret = tok.caret;
+        unsigned long name_line = get_line_number ();
+        
+        struct local_symbol *src = find_local_symbol (name);
+        get_token ();
+        
+        if (is_assignment_operator (tok.kind)) {
+        
+            enum token_kind assign_op = tok.kind;
+            
+            int dst_size = 0;
+            int dst_is_floating = 0;
+            int have_dst = 0;
+            
+            if (src) {
+            
+                dst_size = src->size;
+                
+                dst_is_floating = src->is_floating;
+                have_dst = 1;
+            
+            } else if (find_global_symbol (name) >= 0) {
+            
+                dst_size = get_global_symbol_size (name);
+                
+                dst_is_floating = get_global_symbol_floating (name);
+                have_dst = 1;
+            
+            }
+            
+            if (have_dst && dst_is_floating && floating_assignment_operator_supported_now (assign_op)) {
+            
+                get_token ();
+                
+                if (assign_op == TOK_ASSIGN) {
+                    emit_load_floating_rhs_expression_now (dst_size);
+                } else {
+                
+                    emit_load_floating_symbol_now (src, name, dst_size);
+                    emit_load_floating_rhs_expression_now (dst_size);
+                    
+                    emit_floating_binary_now (assign_op);
+                
+                }
+                
+                emit_duplicate_floating_stack_top_now ();
+                emit_store_floating_symbol_now (src, name, dst_size);
+                
+                free (name);
+                return;
+            
+            }
+        
+        }
+        
+        if (tok.kind == TOK_LPAREN) {
+        
+            if (!find_local_symbol (name)) {
+                ensure_global_function_symbol (name, name_start, name_caret, name_line);
+            }
+            
+            if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION && get_global_symbol_returns_void (name)) {
+                report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "void function '%s' used as a value", name);
+            }
+            
+            emit_call_identifier_to_reg_now (name, "eax", name_start, name_caret, name_line);
+            
+            if (!get_global_symbol_floating (name)) {
+            
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                
+                    fprintf (state->ofp, "    sub esp, 4\n");
+                    
+                    if (state->syntax & ASM_SYNTAX_NASM) {
+                    
+                        fprintf (state->ofp, "    mov dword [esp], eax\n");
+                        fprintf (state->ofp, "    fild dword [esp]\n");
+                    
+                    } else {
+                    
+                        fprintf (state->ofp, "    mov dword ptr [esp], eax\n");
+                        fprintf (state->ofp, "    fild dword ptr [esp]\n");
+                    
+                    }
+                    
+                    fprintf (state->ofp, "    add esp, 4\n");
+                
+                } else {
+                
+                    fprintf (state->ofp, "    subl $4, %%esp\n");
+                    fprintf (state->ofp, "    movl %%eax, (%%esp)\n");
+                    fprintf (state->ofp, "    fildl (%%esp)\n");
+                    fprintf (state->ofp, "    addl $4, %%esp\n");
+                
+                }
+            
+            }
+            
+            free (name);
+            return;
+        
+        }
+        
+        if (tok.kind == TOK_ARROW && (src || find_global_symbol (name) >= 0)) {
+        
+            char *member;
+            
+            const char *member_start;
+            const char *member_caret;
+            
+            unsigned long member_line;
+            
+            int member_offset = 0;
+            int member_size = result_size;
+            int member_elem_size = result_size;
+            int member_pointer_depth = 0;
+            int member_is_array = 0;
+            int member_is_floating = 0;
+            int base_size;
+            
+            const char *base_tag_name;
+            
+            if (src) {
+            
+                base_size = src->pointed_size;
+                base_tag_name = src->pointed_tag_name;
+            
+            } else {
+            
+                base_size = get_global_symbol_pointed_size (name);
+                base_tag_name = get_global_symbol_tag_name (name);
+            
+            }
+            
+            get_token ();
+            
+            member_start = tok.start;
+            member_caret = tok.caret;
+            member_line = get_line_number ();
+            
+            if (tok.kind != TOK_IDENT) {
+            
+                report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after ->");
+                
+                free (name);
+                return;
+            
+            }
+            
+            member = xstrdup (tok.ident);
+            get_token ();
+            
+            if (!find_member_info_ex_bounded (member, base_size, base_tag_name, &member_offset, &member_size, &member_elem_size, &member_pointer_depth, &member_is_array, &member_is_floating)) {
+            
+                report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+                
+                free (member);
+                free (name);
+                
+                return;
+            
+            }
+            
+            free (member);
+            
+            if (src) {
+            
+                if (src->is_static && src->static_label) {
+                    emit_load_global_to_reg ("eax", src->static_label, DATA_PTR & 0x1f);
+                } else {
+                    emit_load_local_to_reg ("eax", src->offset, DATA_PTR & 0x1f);
+                }
+            
+            } else {
+                emit_load_global_to_reg ("eax", name, DATA_PTR & 0x1f);
+            }
+            
+            if (member_is_floating) {
+                emit_load_floating_member_from_addr_reg_now ("eax", member_offset, member_size);
+            } else {
+            
+                emit_load_member_from_addr_reg_now ("eax", "eax", member_offset, member_size);
+                emit_eax_bool_to_floating_stack_now ();
+            
+            }
+            
+            free (name);
+            return;
+        
+        }
+        
+        if (tok.kind == TOK_DOT && (src || find_global_symbol (name) >= 0)) {
+        
+            char *member;
+            
+            const char *member_start;
+            const char *member_caret;
+            
+            unsigned long member_line;
+            
+            int member_offset = 0;
+            int member_size = result_size;
+            
+            get_token ();
+            
+            member_start = tok.start;
+            member_caret = tok.caret;
+            member_line = get_line_number ();
+            
+            if (tok.kind != TOK_IDENT) {
+            
+                report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after .");
+                
+                free (name);
+                return;
+            
+            }
+            
+            member = xstrdup (tok.ident);
+            get_token ();
+            
+            if (!find_member_info (member, &member_offset, &member_size)) {
+            
+                report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+                
+                free (member);
+                free (name);
+                
+                return;
+            
+            }
+            
+            free (member);
+            
+            if (member_size == (DATA_FLOAT & 0x1f) || member_size == (DATA_DOUBLE & 0x1f)) {
+                emit_load_floating_member_symbol_now (src, name, member_offset, member_size);
+            } else {
+            
+                if (src) {
+                
+                    if (src->is_static && src->static_label) {
+                        emit_load_global_to_reg ("eax", src->static_label, src->size);
+                    } else {
+                        emit_load_local_to_reg ("eax", src->offset, src->size);
+                    }
+                
+                } else {
+                    emit_load_global_to_reg ("eax", name, get_global_symbol_size (name));
+                }
+                
+                emit_apply_postfix_member_access_to_reg_now ("eax");
+                emit_eax_bool_to_floating_stack_now ();
+            
+            }
+            
+            free (name);
+            return;
+        
+        }
+        
+        if (src) {
+        
+            if (src->is_floating) {
+                emit_load_floating_symbol_now (src, name, src->size);
+            } else {
+            
+                if (src->is_static && src->static_label) {
+                    emit_load_global_to_reg ("eax", src->static_label, src->size);
+                } else {
+                    emit_load_local_to_reg ("eax", src->offset, src->size);
+                }
+                
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                
+                    fprintf (state->ofp, "    sub esp, 4\n");
+                    
+                    if (state->syntax & ASM_SYNTAX_NASM) {
+                    
+                        fprintf (state->ofp, "    mov dword [esp], eax\n");
+                        fprintf (state->ofp, "    fild dword [esp]\n");
+                    
+                    } else {
+                    
+                        fprintf (state->ofp, "    mov dword ptr [esp], eax\n");
+                        fprintf (state->ofp, "    fild dword ptr [esp]\n");
+                    
+                    }
+                    
+                    fprintf (state->ofp, "    add esp, 4\n");
+                
+                } else {
+                
+                    fprintf (state->ofp, "    subl $4, %%esp\n");
+                    fprintf (state->ofp, "    movl %%eax, (%%esp)\n");
+                    fprintf (state->ofp, "    fildl (%%esp)\n");
+                    fprintf (state->ofp, "    addl $4, %%esp\n");
+                
+                }
+            
+            }
+            
+            if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+            
+                enum token_kind postfix_op = tok.kind;
+                get_token ();
+                
+                emit_incdec_symbol_now (src, name, postfix_op, name_line, name_start, name_caret);
+            
+            }
+            
+            free (name);
+            return;
+        
+        }
+        
+        if (find_global_symbol (name) >= 0) {
+        
+            if (get_global_symbol_floating (name)) {
+                emit_load_floating_symbol_now (0, name, get_global_symbol_size (name));
+            } else {
+            
+                emit_load_global_to_reg ("eax", name, get_global_symbol_size (name));
+                
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                
+                    fprintf (state->ofp, "    sub esp, 4\n");
+                    
+                    if (state->syntax & ASM_SYNTAX_NASM) {
+                    
+                        fprintf (state->ofp, "    mov dword [esp], eax\n");
+                        fprintf (state->ofp, "    fild dword [esp]\n");
+                    
+                    } else {
+                    
+                        fprintf (state->ofp, "    mov dword ptr [esp], eax\n");
+                        fprintf (state->ofp, "    fild dword ptr [esp]\n");
+                    
+                    }
+                    
+                    fprintf (state->ofp, "    add esp, 4\n");
+                
+                } else {
+                
+                    fprintf (state->ofp, "    subl $4, %%esp\n");
+                    fprintf (state->ofp, "    movl %%eax, (%%esp)\n");
+                    fprintf (state->ofp, "    fildl (%%esp)\n");
+                    fprintf (state->ofp, "    addl $4, %%esp\n");
+                
+                }
+            
+            }
+            
+            if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+            
+                enum token_kind postfix_op = tok.kind;
+                get_token ();
+                
+                emit_incdec_symbol_now (0, name, postfix_op, name_line, name_start, name_caret);
+            
+            }
+            
+            free (name);
+            return;
+        
+        }
+        
+        free (name);
+    
+    }
+    
+    if (token_is_floating_constant_now ()) {
+    
+        emit_load_floating_const_bits_now (result_size, floating_constant_to_bits_now (result_size));
+        return;
+    
+    }
+    
+    if (recover_unknown_rhs_identifier ()) {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+            fprintf (state->ofp, "    fldz\n");
+        } else {
+            fprintf (state->ofp, "    fldz\n");
+        }
+        return;
+    
+    }
+    
+    {
+    
+        int64_s v = const64_from_current_operand ();
+        
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+        
+            fprintf (state->ofp, "    sub esp, 4\n");
+            
+            if (state->syntax & ASM_SYNTAX_NASM) {
+            
+                fprintf (state->ofp, "    mov dword [esp], %lu\n", v.low & U32_MASK);
+                fprintf (state->ofp, "    fild dword [esp]\n");
+            
+            } else {
+            
+                fprintf (state->ofp, "    mov dword ptr [esp], %lu\n", v.low & U32_MASK);
+                fprintf (state->ofp, "    fild dword ptr [esp]\n");
+            
+            }
+            
+            fprintf (state->ofp, "    add esp, 4\n");
+        
+        } else {
+        
+            fprintf (state->ofp, "    subl $4, %%esp\n");
+            fprintf (state->ofp, "    movl $%lu, (%%esp)\n", v.low & U32_MASK);
+            fprintf (state->ofp, "    fildl (%%esp)\n");
+            fprintf (state->ofp, "    addl $4, %%esp\n");
+        
+        }
+    
+    }
+
+}
+
+static int token_is_floating_binary_now (enum token_kind k) {
+    return k == TOK_PLUS || k == TOK_MINUS || k == TOK_STAR || k == TOK_BSLASH;
+}
+
+static void emit_floating_binary_now (enum token_kind k) {
+
+    const char *st1;
+    const char *st0;
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_NASM) {
+    
+        st1 = "st1";
+        st0 = "st0";
+    
+    } else if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        st1 = "st(1)";
+        st0 = "st(0)";
+    
+    } else {
+    
+        st1 = "%st(1)";
+        st0 = "%st";
+    
+    }
+    
+    switch (k) {
+    
+        case TOK_PLUS:      case TOK_PLUSEQ:
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    faddp %s, %s\n", st1, st0);
+            } else {
+                fprintf (state->ofp, "    faddp %s, %s\n", st0, st1);
+            }
+            break;
+        
+        case TOK_MINUS:     case TOK_MINUSEQ:
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    fsubp %s, %s\n", st1, st0);
+            } else {
+                fprintf (state->ofp, "    fsubp %s, %s\n", st0, st1);
+            }
+            break;
+        
+        case TOK_STAR:      case TOK_STAREQ:
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    fmulp %s, %s\n", st1, st0);
+            } else {
+                fprintf (state->ofp, "    fmulp %s, %s\n", st0, st1);
+            }
+            break;
+        
+        case TOK_BSLASH:    case TOK_SLASHEQ:
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    fdivp %s, %s\n", st1, st0);
+            } else {
+                fprintf (state->ofp, "    fdivp %s, %s\n", st0, st1);
+            }
+            break;
+        
+        default:
+        
+            break;
+    
+    }
+
+}
+
+static void emit_scale_reg_for_pointer_compound_assignment_now (const char *reg, struct local_symbol *lhs, const char *name, enum token_kind op) {
+
+    int pointer_depth;
+    int pointed_size;
+    int elem_size;
+    
+    if (op != TOK_PLUSEQ && op != TOK_MINUSEQ) {
+        return;
+    }
+    
+    pointer_depth = lhs ? lhs->pointer_depth : get_global_symbol_pointer_depth (name);
+    pointed_size = lhs ? lhs->pointed_size : get_global_symbol_pointed_size (name);
+    
+    if (pointer_depth <= 0) {
+        return;
+    }
+    
+    elem_size = pointer_depth > 1 ? DATA_PTR : pointed_size;
+    
+    if (elem_size > 1) {
+        emit_scale_reg_by_const_now (reg, elem_size);
+    }
+
+}
+
+static int floating_assignment_operator_supported_now (enum token_kind op) {
+    return op == TOK_ASSIGN || op == TOK_PLUSEQ || op == TOK_MINUSEQ || op == TOK_STAREQ || op == TOK_SLASHEQ;
+}
+
+static void emit_load_floating_rhs_expression_now (int result_size) {
+
+    enum token_kind op;
+    
+    int false_label;
+    int end_label;
+    
+    floating_rhs_result_in_eax_bool = 0;
+    emit_load_floating_rhs_operand_now (result_size);
+    
+    while (token_is_floating_binary_now (tok.kind)) {
+    
+        if (floating_rhs_result_in_eax_bool) {
+            emit_eax_bool_to_floating_stack_now ();
+        }
+        
+        op = tok.kind;
+        get_token ();
+        
+        emit_load_floating_rhs_operand_now (result_size);
+        
+        if (floating_rhs_result_in_eax_bool) {
+            emit_eax_bool_to_floating_stack_now ();
+        }
+        
+        emit_floating_binary_now (op);
+    
+    }
+    
+    if (is_value_compare_operator (tok.kind)) {
+    
+        op = tok.kind;
+        get_token ();
+        
+        emit_load_floating_rhs_operand_now (result_size);
+        
+        while (token_is_floating_binary_now (tok.kind)) {
+        
+            enum token_kind rhs_op = tok.kind;
+            get_token ();
+            
+            emit_load_floating_rhs_operand_now (result_size);
+            emit_floating_binary_now (rhs_op);
+        
+        }
+        
+        emit_floating_compare_to_eax_now (op);
+    
+    }
+    
+    if (tok.kind == TOK_QMARK) {
+    
+        false_label = anon_label++;
+        end_label = anon_label++;
+        
+        if (!floating_rhs_result_in_eax_bool) {
+        
+            if (state->ofp) {
+            
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                
+                    fprintf (state->ofp, "    sub esp, 4\n");
+                    
+                    if (state->syntax & ASM_SYNTAX_NASM) {
+                    
+                        fprintf (state->ofp, "    fstp dword [esp]\n");
+                        fprintf (state->ofp, "    mov eax, dword [esp]\n");
+                    
+                    } else {
+                    
+                        fprintf (state->ofp, "    fstp dword ptr [esp]\n");
+                        fprintf (state->ofp, "    mov eax, dword ptr [esp]\n");
+                    
+                    }
+                    
+                    fprintf (state->ofp, "    add esp, 4\n");
+                    fprintf (state->ofp, "    test eax, eax\n");
+                
+                } else {
+                
+                    fprintf (state->ofp, "    subl $4, %%esp\n");
+                    fprintf (state->ofp, "    fstps (%%esp)\n");
+                    fprintf (state->ofp, "    movl (%%esp), %%eax\n");
+                    fprintf (state->ofp, "    addl $4, %%esp\n");
+                    fprintf (state->ofp, "    testl %%eax, %%eax\n");
+                
+                }
+            
+            }
+        
+        } else if (state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    test eax, eax\n");
+            } else {
+                fprintf (state->ofp, "    testl %%eax, %%eax\n");
+            }
+        
+        }
+        
+        if (state->ofp) {
+        
+            if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+                fprintf (state->ofp, "    jz L%d\n", false_label);
+            } else {
+                fprintf (state->ofp, "    jz .L%d\n", false_label);
+            }
+        
+        }
+        
+        floating_rhs_result_in_eax_bool = 0;
+        get_token ();
+        
+        emit_load_floating_rhs_expression_now (result_size);
+        expect (TOK_COLON, ":");
+        
+        emit_statement_jump (end_label);
+        emit_statement_label (false_label);
+        
+        emit_load_floating_rhs_expression_now (result_size);
+        emit_statement_label (end_label);
+        
+        floating_rhs_result_in_eax_bool = 0;
+    
+    }
+
+}
+
+static void emit_load_assignment_rhs_expression_to_reg (const char *reg);
+static int rhs_current_operand_is_floating_now (void);
+
+static int current_argument_is_bare_identifier_now (void) {
+
+    const char *p;
+    
+    if (tok.kind != TOK_IDENT || !tok.caret) {
+        return 0;
+    }
+    
+    p = tok.caret + tok.len;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    return *p == ',' || *p == ')';
+
+}
+
+static int emit_push_aggregate_argument_now (const char *name, struct local_symbol *sym) {
+
+    int size;
+    int offset;
+    int chunk;
+    
+    char memref[64];
+    
+    if (!name || !sym || sym->is_array || sym->pointer_depth > 0 || sym->is_floating) {
+        return 0;
+    }
+    
+    /**
+     * Struct/union locals keep their real byte size here.  Masking with
+     * 0x1f is only valid for scalar DATA_* encodings; it turns e.g. a
+     * 72-byte struct cpu_flags argument into an 8-byte argument.
+     */
+    size = sym->size;
+    
+    /*
+     * Do not treat plain 64-bit scalar locals as aggregate arguments.
+     * The old test used only size > 4, so a call such as:
+     *
+     *     bytearray_write_4_bytes (..., result, endianess)
+     *
+     * where result is uint_fast64_t/address_type pushed both halves of
+     * result.  The callee expects an unsigned long here, so the extra high
+     * word shifted the following arguments and pdld wrote broken relocation
+     * bytes.  Real structs/unions either carry an aggregate tag here, or are
+     * larger than the built-in long long scalar size.
+     */
+    if (size <= (DATA_PTR & 0x1f) || (size <= (DATA_LLONG & 0x1f) && !sym->tag_name)) {
+        return 0;
+    }
+    
+    if (!state->ofp) {
+        return 1;
+    }
+    
+    for (offset = size; offset > 0; ) {
+    
+        if (offset >= 4) {
+        
+            chunk = 4;
+            offset -= 4;
+        
+        } else if (offset >= 2) {
+        
+            chunk = 2;
+            offset -= 2;
+        
+        } else {
+        
+            chunk = 1;
+            offset -= 1;
+        
+        }
+        
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+        
+            if (sym->is_static && sym->static_label) {
+            
+                if (chunk == 4) {
+                    fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    push dword [%s + %d]\n" : "    push dword ptr %s + %d\n"), asm_global_symbol_name (sym->static_label), offset);
+                } else if (chunk == 2) {
+                    fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    movzx eax, word [%s + %d]\n" : "    movzx eax, word ptr %s + %d\n"), asm_global_symbol_name (sym->static_label), offset); fprintf (state->ofp, "    push eax\n");
+                } else {
+                    fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    movzx eax, byte [%s + %d]\n" : "    movzx eax, byte ptr %s + %d\n"), asm_global_symbol_name (sym->static_label), offset); fprintf (state->ofp, "    push eax\n");
+                }
+            
+            } else {
+            
+                format_intel_ebp_offset (memref, sizeof (memref), sym->offset + offset);
+                
+                if (chunk == 4) {
+                    fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    push dword %s\n" : "    push dword ptr %s\n"), memref);
+                } else if (chunk == 2) {
+                    fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    movzx eax, word %s\n" : "    movzx eax, word ptr %s\n"), memref); fprintf (state->ofp, "    push eax\n");
+                } else {
+                    fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    movzx eax, byte %s\n" : "    movzx eax, byte ptr %s\n"), memref); fprintf (state->ofp, "    push eax\n");
+                }
+            
+            }
+        
+        } else {
+        
+            if (sym->is_static && sym->static_label) {
+            
+                if (chunk == 4) {
+                    fprintf (state->ofp, "    pushl %s+%d\n", asm_global_symbol_name (sym->static_label), offset);
+                } else if (chunk == 2) {
+                    fprintf (state->ofp, "    movzwl %s+%d, %%eax\n", asm_global_symbol_name (sym->static_label), offset); fprintf (state->ofp, "    pushl %%eax\n");
+                } else {
+                    fprintf (state->ofp, "    movzbl %s+%d, %%eax\n", asm_global_symbol_name (sym->static_label), offset); fprintf (state->ofp, "    pushl %%eax\n");
+                }
+            
+            } else {
+            
+                if (chunk == 4) {
+                    fprintf (state->ofp, "    pushl %ld(%%ebp)\n", sym->offset + offset);
+                } else if (chunk == 2) {
+                    fprintf (state->ofp, "    movzwl %ld(%%ebp), %%eax\n", sym->offset + offset); fprintf (state->ofp, "    pushl %%eax\n");
+                } else {
+                    fprintf (state->ofp, "    movzbl %ld(%%ebp), %%eax\n", sym->offset + offset); fprintf (state->ofp, "    pushl %%eax\n");
+                }
+            
+            }
+        
+        }
+    
+    }
+    
+    return 1;
+
+}
+
+static int emit_push_global_aggregate_argument_now (const char *name) {
+
+    int size;
+    int offset;
+    int chunk;
+
+    if (!name || get_global_symbol_kind (name) != GLOBAL_SYMBOL_OBJECT ||
+        get_global_symbol_array (name) || get_global_symbol_pointer_depth (name) > 0 ||
+        get_global_symbol_floating (name)) {
+        return 0;
+    }
+    
+    size = get_global_symbol_size (name);
+    
+    if (size <= (DATA_PTR & 0x1f)) {
+        return 0;
+    }
+    
+    if (!state->ofp) {
+        return 1;
+    }
+    
+    for (offset = size; offset > 0; ) {
+    
+        if (offset >= 4) {
+        
+            chunk = 4;
+            offset -= 4;
+        
+        } else if (offset >= 2) {
+        
+            chunk = 2;
+            offset -= 2;
+        
+        } else {
+        
+            chunk = 1;
+            offset -= 1;
+        
+        }
+        
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+        
+            if (chunk == 4) {
+            
+                fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ?
+                    "    push dword [%s + %d]\n" :
+                    "    push dword ptr %s + %d\n"), asm_global_symbol_name (name), offset);
+            
+            } else if (chunk == 2) {
+            
+                fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ?
+                    "    movzx eax, word [%s + %d]\n" :
+                    "    movzx eax, word ptr %s + %d\n"), asm_global_symbol_name (name), offset);
+                fprintf (state->ofp, "    push eax\n");
+            
+            } else {
+            
+                fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ?
+                    "    movzx eax, byte [%s + %d]\n" :
+                    "    movzx eax, byte ptr %s + %d\n"), asm_global_symbol_name (name), offset);
+                fprintf (state->ofp, "    push eax\n");
+            
+            }
+        
+        } else {
+        
+            if (chunk == 4) {
+                fprintf (state->ofp, "    pushl %s+%d\n", asm_global_symbol_name (name), offset);
+            } else if (chunk == 2) {
+            
+                fprintf (state->ofp, "    movzwl %s+%d, %%eax\n", asm_global_symbol_name (name), offset);
+                fprintf (state->ofp, "    pushl %%eax\n");
+            
+            } else {
+            
+                fprintf (state->ofp, "    movzbl %s+%d, %%eax\n", asm_global_symbol_name (name), offset);
+                fprintf (state->ofp, "    pushl %%eax\n");
+            
+            }
+        
+        }
+    
+    }
+    
+    return 1;
+
+}
+
+static void emit_call_pointer_in_reg_now (const char *fn_reg, const char *result_reg) {
+
+    int argc = 0;
+    int total_arg_bytes = 0;
+    int arg_bytes;
+    int arg_is_floating;
+    int i;
+    int ch;
+    
+    FILE **arg_tmp_ofps = 0;
+    FILE **new_arg_tmp_ofps = 0;
+    FILE *arg_saved_ofp = 0;
+    FILE *arg_tmp_ofp = 0;
+    
+    if (tok.kind != TOK_LPAREN) {
+        return;
+    }
+    
+    if (state->ofp) {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+            fprintf (state->ofp, "    push %s\n", fn_reg);
+        } else {
+            fprintf (state->ofp, "    pushl %%%s\n", fn_reg);
+        }
+    
+    }
+    
+    get_token ();
+    
+    if (tok.kind != TOK_RPAREN) {
+    
+        for (;;) {
+        
+            arg_saved_ofp = 0;
+            arg_tmp_ofp = 0;
+            
+            if (state->ofp) {
+            
+                arg_tmp_ofp = tmpfile ();
+                
+                if (arg_tmp_ofp) {
+                
+                    arg_saved_ofp = state->ofp;
+                    state->ofp = arg_tmp_ofp;
+                
+                }
+            
+            }
+            
+            postfix_member_seen = 0;
+            postfix_member_size = 0;
+            postfix_member_pointer_depth = 0;
+            postfix_member_pointed_size = 0;
+            
+            if (tok.kind == TOK_IDENT && tok.ident && current_argument_is_bare_identifier_now () && find_local_symbol (tok.ident) && emit_push_aggregate_argument_now (tok.ident, find_local_symbol (tok.ident))) {
+            
+                struct local_symbol *arg_sym = find_local_symbol (tok.ident);
+                
+                arg_bytes = arg_sym ? (arg_sym->size & 0x1f) : DATA_PTR;
+                arg_is_floating = 0;
+                
+                get_token ();
+            
+            } else {
+            
+                arg_is_floating = rhs_current_operand_is_floating_now ();
+                arg_bytes = arg_is_floating ? (DATA_DOUBLE & 0x1f) : DATA_PTR;
+                
+                if (arg_is_floating) {
+                    emit_load_floating_rhs_expression_now (DATA_DOUBLE & 0x1f);
+                } else {
+                    emit_load_assignment_rhs_expression_to_reg ("eax");
+                }
+                
+                if (state->ofp) {
+                
+                    if (!arg_is_floating && postfix_member_seen && postfix_member_pointer_depth == 0 && postfix_member_size > (DATA_PTR & 0x1f)) {
+                    
+                        arg_bytes = postfix_member_size;
+                        emit_push_aggregate_from_addr_reg_now ("eax", arg_bytes);
+                    
+                    } else if (!arg_is_floating && postfix_member_size > (DATA_PTR & 0x1f)) {
+                    
+                        arg_bytes = postfix_member_size;
+                        emit_push_aggregate_from_addr_reg_now ("eax", arg_bytes);
+                    
+                    } else if (arg_is_floating) {
+                    
+                        if (state->syntax & ASM_SYNTAX_INTEL) {
+                        
+                            fprintf (state->ofp, "    sub esp, 8\n");
+                            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    fstp qword [esp]\n" : "    fstp qword ptr [esp]\n"));
+                        
+                        } else {
+                        
+                            fprintf (state->ofp, "    subl $8, %%esp\n");
+                            fprintf (state->ofp, "    fstpl (%%esp)\n");
+                        
+                        }
+                    
+                    } else {
+                    
+                        if (state->syntax & ASM_SYNTAX_INTEL) {
+                            fprintf (state->ofp, "    push eax\n");
+                        } else {
+                            fprintf (state->ofp, "    pushl %%eax\n");
+                        }
+                    
+                    }
+                
+                }
+            
+            }
+            
+            total_arg_bytes += arg_bytes;
+            
+            if (arg_saved_ofp) {
+            
+                fflush (arg_tmp_ofp);
+                
+                state->ofp = arg_saved_ofp;
+                new_arg_tmp_ofps = (FILE **) realloc (arg_tmp_ofps, sizeof (*arg_tmp_ofps) * (argc + 1));
+                
+                if (new_arg_tmp_ofps) {
+                
+                    arg_tmp_ofps = new_arg_tmp_ofps;
+                    arg_tmp_ofps[argc] = arg_tmp_ofp;
+                    arg_tmp_ofp = 0;
+                
+                }
+            
+            }
+            
+            if (arg_tmp_ofp) {
+            
+                fclose (arg_tmp_ofp);
+                arg_tmp_ofp = 0;
+            
+            }
+            
+            argc++;
+            
+            if (!_accept (TOK_COMMA)) {
+                break;
+            }
+        
+        }
+    
+    }
+    
+    expect (TOK_RPAREN, ")");
+    
+    if (state->ofp) {
+    
+        for (i = argc - 1; i >= 0; i--) {
+        
+            if (arg_tmp_ofps && arg_tmp_ofps[i]) {
+            
+                fseek (arg_tmp_ofps[i], 0, SEEK_SET);
+                
+                while ((ch = fgetc (arg_tmp_ofps[i])) != EOF) {
+                    fputc (ch, state->ofp);
+                }
+                
+                fclose (arg_tmp_ofps[i]);
+                arg_tmp_ofps[i] = 0;
+            
+            }
+        
+        }
+        
+        if (arg_tmp_ofps) {
+        
+            free (arg_tmp_ofps);
+            arg_tmp_ofps = 0;
+        
+        }
+        
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+        
+            if (state->syntax & ASM_SYNTAX_NASM) {
+                fprintf (state->ofp, "    mov ecx, dword [esp + %d]\n", total_arg_bytes);
+            } else {
+                fprintf (state->ofp, "    mov ecx, dword ptr [esp + %d]\n", total_arg_bytes);
+            }
+            
+            fprintf (state->ofp, "    call ecx\n");
+            fprintf (state->ofp, "    add esp, %d\n", total_arg_bytes + (DATA_PTR & 0x1f));
+            
+            if (strcmp (result_reg, "eax") != 0) {
+                fprintf (state->ofp, "    mov %s, eax\n", result_reg);
+            }
+        
+        } else {
+        
+            fprintf (state->ofp, "    movl %d(%%esp), %%ecx\n", total_arg_bytes);
+            fprintf (state->ofp, "    call *%%ecx\n");
+            fprintf (state->ofp, "    addl $%d, %%esp\n", total_arg_bytes + (DATA_PTR & 0x1f));
+            
+            if (strcmp (result_reg, "eax") != 0) {
+                fprintf (state->ofp, "    movl %%eax, %%%s\n", result_reg);
+            }
+        
+        }
+    
+    }
+    
+    if (arg_tmp_ofps) {
+    
+        for (i = 0; i < argc; i++) {
+        
+            if (arg_tmp_ofps[i]) {
+                fclose (arg_tmp_ofps[i]);
+            }
+        
+        }
+        
+        free (arg_tmp_ofps);
+    
+    }
+
+}
+
+static void emit_sub_esp_now (int bytes) {
+
+    if (!state->ofp || bytes <= 0) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+        fprintf (state->ofp, "    sub esp, %d\n", bytes);
+    } else {
+        fprintf (state->ofp, "    subl $%d, %%esp\n", bytes);
+    }
+
+}
+
+static void emit_push_pending_struct_return_address_now (int stack_arg_bytes) {
+
+    if (!state->ofp || (!pending_struct_return_lhs && !pending_struct_return_global_name && !pending_struct_return_stack_address && !pending_struct_return_stack_top)) {
+        return;
+    }
+    
+    if (pending_struct_return_stack_top) {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+        
+            if (state->syntax & ASM_SYNTAX_NASM) {
+                fprintf (state->ofp, "    lea eax, [esp + %d]\n", stack_arg_bytes);
+            } else {
+                fprintf (state->ofp, "    lea eax, dword ptr [esp + %d]\n", stack_arg_bytes);
+            }
+        
+        } else {
+            fprintf (state->ofp, "    leal %d(%%esp), %%eax\n", stack_arg_bytes);
+        }
+    
+    } else if (pending_struct_return_stack_address) {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+        
+            if (state->syntax & ASM_SYNTAX_NASM) {
+                fprintf (state->ofp, "    mov eax, dword [esp + %d]\n", stack_arg_bytes);
+            } else {
+                fprintf (state->ofp, "    mov eax, dword ptr [esp + %d]\n", stack_arg_bytes);
+            }
+            
+            if (pending_struct_return_stack_offset) {
+                fprintf (state->ofp, "    add eax, %d\n", pending_struct_return_stack_offset);
+            }
+        
+        } else {
+        
+            fprintf (state->ofp, "    movl %d(%%esp), %%eax\n", stack_arg_bytes);
+            
+            if (pending_struct_return_stack_offset) {
+                fprintf (state->ofp, "    addl $%d, %%eax\n", pending_struct_return_stack_offset);
+            }
+        
+        }
+    
+    } else if (pending_struct_return_lhs) {
+    
+        if (pending_struct_return_lhs->is_static && pending_struct_return_lhs->static_label) {
+            emit_load_address_to_reg_now ("eax", pending_struct_return_lhs->static_label);
+        } else {
+            emit_load_local_address_to_reg_now ("eax", pending_struct_return_lhs->offset);
+        }
+    
+    } else {
+        emit_load_address_to_reg_now ("eax", pending_struct_return_global_name);
+    }
+    
+    emit_push_reg_now ("eax");
+
+}
+
+static void emit_call_identifier_to_reg_now (const char *name, const char *reg, const char *name_start, const char *name_caret, unsigned long name_line) {
+
+    int argc = 0;
+    int inline_index;
+    int use_inline = 0;
+    int expected_inline_args = 0;
+    int inline_arg_bytes = 0;
+    int total_arg_bytes = 0;
+    int arg_bytes;
+    int arg_is_floating;
+    int i;
+    int ch;
+    
+    struct local_symbol *saved_pending_struct_return_lhs = pending_struct_return_lhs;
+    struct local_symbol *call_sym = 0;
+    
+    const char *saved_pending_struct_return_global_name = pending_struct_return_global_name;
+    const char *asm_name;
+    
+    int saved_pending_struct_return_stack_address = pending_struct_return_stack_address;
+    int saved_pending_struct_return_stack_offset = pending_struct_return_stack_offset;
+    int saved_pending_struct_return_stack_top = pending_struct_return_stack_top;
+    
+    FILE *inline_saved_ofp = 0;
+    FILE *inline_tmp_ofp = 0;
+    FILE **arg_tmp_ofps = 0;
+    FILE **new_arg_tmp_ofps = 0;
+    FILE *arg_saved_ofp = 0;
+    FILE *arg_tmp_ofp = 0;
+    
+    if (tok.kind != TOK_LPAREN) {
+        return;
+    }
+    
+    inline_index = find_inline_function (name);
+    
+    if (inline_index >= 0 &&
+        inline_functions[inline_index].usable &&
+        !inline_functions[inline_index].is_floating &&
+        !inline_functions[inline_index].expanding &&
+        (inline_functions[inline_index].body || inline_functions[inline_index].returns_void) &&
+        (!inline_functions[inline_index].body || inline_body_stack_delta (inline_functions[inline_index].body) == 0)) {
+    
+        use_inline = 1;
+        expected_inline_args = inline_functions[inline_index].param_count;
+        inline_arg_bytes = expected_inline_args * 4;
+        
+        if (state->ofp) {
+        
+            inline_tmp_ofp = tmpfile ();
+            
+            if (inline_tmp_ofp) {
+            
+                inline_saved_ofp = state->ofp;
+                state->ofp = inline_tmp_ofp;
+            
+            }
+            
+            if (inline_arg_bytes > 0) {
+            
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                    fprintf (state->ofp, "    sub esp, %d\n", inline_arg_bytes);
+                } else {
+                    fprintf (state->ofp, "    subl $%d, %%esp\n", inline_arg_bytes);
+                }
+            
+            }
+        
+        }
+    
+    }
+    
+    get_token ();
+    
+    if (tok.kind != TOK_RPAREN) {
+    
+        for (;;) {
+        
+            arg_saved_ofp = 0;
+            arg_tmp_ofp = 0;
+            
+            /*
+             * cdecl wants the right-most argument nearest the call site and
+             * the left-most argument at [ebp + 8] in the callee.  The old
+             * code emitted each push immediately while parsing left-to-right,
+             * which reversed the parameter slots for normal calls.  Capture
+             * each non-inline argument's evaluation/push code and replay the
+             * completed argument blocks right-to-left just before CALL.
+             *
+             * Inline calls keep using the temporary argument frame below: that
+             * frame intentionally stores argument 0 at [esp], argument 1 at
+             * [esp + 4], etc., so do not reverse inline argument copies here.
+             */
+            if (!use_inline && state->ofp) {
+            
+                arg_tmp_ofp = tmpfile ();
+                
+                if (arg_tmp_ofp) {
+                    arg_saved_ofp = state->ofp;
+                    state->ofp = arg_tmp_ofp;
+                }
+            
+            }
+            
+            postfix_member_seen = 0;
+            postfix_member_size = 0;
+            postfix_member_pointer_depth = 0;
+            postfix_member_pointed_size = 0;
+            
+            if (!use_inline && tok.kind == TOK_IDENT && tok.ident && current_argument_is_bare_identifier_now () && find_local_symbol (tok.ident) && emit_push_aggregate_argument_now (tok.ident, find_local_symbol (tok.ident))) {
+            
+                struct local_symbol *arg_sym = find_local_symbol (tok.ident);
+                
+                arg_bytes = arg_sym ? arg_sym->size : DATA_PTR;
+                arg_is_floating = 0;
+                
+                get_token ();
+            
+            } else if (!use_inline && tok.kind == TOK_IDENT && tok.ident && current_argument_is_bare_identifier_now () && emit_push_global_aggregate_argument_now (tok.ident)) {
+            
+                arg_bytes = get_global_symbol_size (tok.ident);
+                arg_is_floating = 0;
+                
+                get_token ();
+            
+            } else if (!use_inline && tok.kind == TOK_IDENT && tok.ident && token_identifier_is_function_call_rhs_now () &&
+                       get_global_symbol_kind (tok.ident) == GLOBAL_SYMBOL_FUNCTION &&
+                       get_global_symbol_size (tok.ident) > (DATA_LLONG & 0x1f)) {
+            
+                char *arg_call_name = xstrdup (tok.ident);
+                
+                const char *arg_call_start = tok.start;
+                const char *arg_call_caret = tok.caret;
+                
+                unsigned long arg_call_line = get_line_number ();
+                
+                arg_bytes = get_global_symbol_size (arg_call_name);
+                arg_is_floating = 0;
+                
+                emit_sub_esp_now (arg_bytes);
+                
+                pending_struct_return_lhs = 0;
+                pending_struct_return_global_name = 0;
+                pending_struct_return_stack_address = 0;
+                pending_struct_return_stack_offset = 0;
+                pending_struct_return_stack_top = 1;
+                
+                get_token ();
+                emit_call_identifier_to_reg_now (arg_call_name, "eax", arg_call_start, arg_call_caret, arg_call_line);
+                
+                pending_struct_return_stack_top = 0;
+                free (arg_call_name);
+            
+            } else {
+            
+                arg_is_floating = rhs_current_operand_is_floating_now ();
+                arg_bytes = arg_is_floating ? (DATA_DOUBLE & 0x1f) : DATA_PTR;
+                
+                if (arg_is_floating) {
+                    emit_load_floating_rhs_expression_now (DATA_DOUBLE & 0x1f);
+                } else {
+                    emit_load_assignment_rhs_expression_to_reg ("eax");
+                }
+                
+                if (state->ofp) {
+                
+                    if (!use_inline && !arg_is_floating && postfix_member_seen && postfix_member_pointer_depth == 0 && postfix_member_size > (DATA_PTR & 0x1f)) {
+                    
+                        arg_bytes = postfix_member_size;
+                        emit_push_aggregate_from_addr_reg_now ("eax", arg_bytes);
+                    
+                    } else if (!use_inline && !arg_is_floating && postfix_member_size > (DATA_PTR & 0x1f)) {
+                    
+                        arg_bytes = postfix_member_size;
+                        emit_push_aggregate_from_addr_reg_now ("eax", arg_bytes);
+                    
+                    } else if (use_inline) {
+                    
+                        if (argc < expected_inline_args) {
+                        
+                            if (state->syntax & ASM_SYNTAX_INTEL) {
+                                fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov dword [esp + %d], eax\n" : "    mov dword ptr [esp + %d], eax\n"), argc * 4);
+                            } else {
+                                fprintf (state->ofp, "    movl %%eax, %d(%%esp)\n", argc * 4);
+                            }
+                        
+                        }
+                    
+                    } else if (arg_is_floating) {
+                    
+                        if (state->syntax & ASM_SYNTAX_INTEL) {
+                        
+                            fprintf (state->ofp, "    sub esp, 8\n");
+                            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    fstp qword [esp]\n" : "    fstp qword ptr [esp]\n"));
+                        
+                        } else {
+                        
+                            fprintf (state->ofp, "    subl $8, %%esp\n");
+                            fprintf (state->ofp, "    fstpl (%%esp)\n");
+                        
+                        }
+                    
+                    } else {
+                    
+                        if (state->syntax & ASM_SYNTAX_INTEL) {
+                            fprintf (state->ofp, "    push eax\n");
+                        } else {
+                            fprintf (state->ofp, "    pushl %%eax\n");
+                        }
+                    
+                    }
+                
+                }
+            
+            }
+            
+            if (!use_inline) {
+                total_arg_bytes += arg_bytes;
+            }
+            
+            if (arg_saved_ofp) {
+            
+                fflush (arg_tmp_ofp);
+                state->ofp = arg_saved_ofp;
+                
+                new_arg_tmp_ofps = (FILE **) realloc (arg_tmp_ofps, sizeof (*arg_tmp_ofps) * (argc + 1));
+                
+                if (new_arg_tmp_ofps) {
+                
+                    arg_tmp_ofps = new_arg_tmp_ofps;
+                    arg_tmp_ofps[argc] = arg_tmp_ofp;
+                    arg_tmp_ofp = 0;
+                
+                }
+            
+            }
+            
+            if (arg_tmp_ofp) {
+            
+                fclose (arg_tmp_ofp);
+                arg_tmp_ofp = 0;
+            
+            }
+            
+            argc++;
+            
+            if (!_accept (TOK_COMMA)) {
+                break;
+            }
+        
+        }
+    
+    }
+    
+    expect (TOK_RPAREN, ")");
+    
+    if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION && get_global_symbol_has_prototype (name) && ((get_global_symbol_is_variadic (name) && argc < get_global_symbol_param_count (name)) || (!get_global_symbol_is_variadic (name) && argc != get_global_symbol_param_count (name)))) {
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "wrong number of arguments to function '%s'", name);
+    }
+    
+    if (use_inline) {
+    
+        if (argc == expected_inline_args && emit_inline_call_if_possible (name, argc, reg)) {
+        
+            if (state->ofp && inline_arg_bytes > 0) {
+            
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                    fprintf (state->ofp, "    add esp, %d\n", inline_arg_bytes);
+                } else {
+                    fprintf (state->ofp, "    addl $%d, %%esp\n", inline_arg_bytes);
+                }
+            
+            }
+            
+            /*
+             * The peephole inline optimiser currently understands the
+             * single-argument case well, but its stack-slot liveness pass is
+             * too aggressive for multi-argument inline calls.  It can fold
+             * constants correctly in simple examples, but it may also remove
+             * the temporary argument frame and leave confusing label-only
+             * fragments.  Keep multi-argument inline expansion conservative:
+             * emit the substituted inline body exactly as generated, with the
+             * argument copies still present.
+             */
+            finish_inline_buffer (&inline_tmp_ofp, &inline_saved_ofp, expected_inline_args <= 1);
+            return;
+        
+        }
+        
+        if (state->ofp && inline_arg_bytes > 0) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    add esp, %d\n", inline_arg_bytes);
+            } else {
+                fprintf (state->ofp, "    addl $%d, %%esp\n", inline_arg_bytes);
+            }
+        
+        }
+        
+        finish_inline_buffer (&inline_tmp_ofp, &inline_saved_ofp, 0);
+        return;
+    
+    }
+    
+    if (emit_inline_call_if_possible (name, argc, reg)) {
+    
+        if (arg_tmp_ofps) {
+        
+            for (i = 0; i < argc; i++) {
+            
+                if (arg_tmp_ofps[i]) {
+                    fclose (arg_tmp_ofps[i]);
+                }
+            
+            }
+            
+            free (arg_tmp_ofps);
+        
+        }
+        
+        return;
+    }
+    
+    call_sym = find_local_symbol (name);
+    
+    if (!call_sym) {
+    
+        if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_OBJECT) {
+            emit_extern_reference_symbol (name, DATA_PTR);
+        } else {
+            emit_extern_symbol (name, DATA_PTR, 1);
+        }
+        
+        asm_name = asm_global_symbol_name (name);
+    
+    } else {
+        asm_name = 0;
+    }
+    
+    if (state->ofp) {
+    
+        for (i = argc - 1; i >= 0; i--) {
+        
+            if (arg_tmp_ofps && arg_tmp_ofps[i]) {
+            
+                fseek (arg_tmp_ofps[i], 0, SEEK_SET);
+                
+                while ((ch = fgetc (arg_tmp_ofps[i])) != EOF) {
+                    fputc (ch, state->ofp);
+                }
+                
+                fclose (arg_tmp_ofps[i]);
+                arg_tmp_ofps[i] = 0;
+            
+            }
+        
+        }
+        
+        if (arg_tmp_ofps) {
+        
+            free (arg_tmp_ofps);
+            arg_tmp_ofps = 0;
+        
+        }
+        
+        if (saved_pending_struct_return_lhs || saved_pending_struct_return_global_name ||
+            saved_pending_struct_return_stack_address || saved_pending_struct_return_stack_top) {
+        
+            pending_struct_return_lhs = saved_pending_struct_return_lhs;
+            pending_struct_return_global_name = saved_pending_struct_return_global_name;
+            pending_struct_return_stack_address = saved_pending_struct_return_stack_address;
+            pending_struct_return_stack_offset = saved_pending_struct_return_stack_offset;
+            pending_struct_return_stack_top = saved_pending_struct_return_stack_top;
+            
+            emit_push_pending_struct_return_address_now (total_arg_bytes);
+            total_arg_bytes += DATA_PTR & 0x1f;
+        
+        }
+        
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+        
+            if (call_sym) {
+            
+                emit_load_local_to_reg ("ecx", call_sym->offset, DATA_PTR);
+                fprintf (state->ofp, "    call ecx\n");
+            
+            } else if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_OBJECT) {
+            
+                emit_load_global_to_reg ("ecx", name, DATA_PTR);
+                fprintf (state->ofp, "    call ecx\n");
+            
+            } else {
+                fprintf (state->ofp, "    call %s\n", asm_name);
+            }
+            
+            if (total_arg_bytes > 0) {
+                fprintf (state->ofp, "    add esp, %d\n", total_arg_bytes);
+            }
+            
+            if (strcmp (reg, "eax") != 0) {
+                fprintf (state->ofp, "    mov %s, eax\n", reg);
+            }
+        
+        } else {
+        
+            if (call_sym) {
+            
+                emit_load_local_to_reg ("ecx", call_sym->offset, DATA_PTR);
+                fprintf (state->ofp, "    call *%%ecx\n");
+            
+            } else if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_OBJECT) {
+            
+                emit_load_global_to_reg ("ecx", name, DATA_PTR);
+                fprintf (state->ofp, "    call *%%ecx\n");
+            
+            } else {
+                fprintf (state->ofp, "    call %s\n", asm_name);
+            }
+            
+            if (total_arg_bytes > 0) {
+                fprintf (state->ofp, "    addl $%d, %%esp\n", total_arg_bytes);
+            }
+            
+            if (strcmp (reg, "eax") != 0) {
+                fprintf (state->ofp, "    movl %%eax, %%%s\n", reg);
+            }
+        
+        }
+    
+    }
+    
+    set_rhs_last_pointer_info (get_global_symbol_pointer_depth (name), get_global_symbol_pointed_size (name));
+    
+    pending_struct_return_lhs = 0;
+    pending_struct_return_global_name = 0;
+    pending_struct_return_stack_address = 0;
+    pending_struct_return_stack_offset = 0;
+    pending_struct_return_stack_top = 0;
+    
+    if (arg_tmp_ofps) {
+    
+        for (i = 0; i < argc; i++) {
+        
+            if (arg_tmp_ofps[i]) {
+                fclose (arg_tmp_ofps[i]);
+            }
+        
+        }
+        
+        free (arg_tmp_ofps);
+    
+    }
+
+}
+
+static void emit_statement_label (int label);
+static void emit_statement_jump (int label);
+
+#define     MAX_GOTO_LABELS        256
+#define     MAX_GOTO_REFS          512
+
+struct goto_label_entry {
+
+    char *name;
+    
+    int label;
+    int defined;
+    int referenced;
+
+    long defined_stack_size;
+    unsigned long line;
+    
+    const char *start;
+    const char *caret;
+
+};
+
+static struct goto_label_entry goto_labels[MAX_GOTO_LABELS];
+static int goto_label_count = 0;
+
+struct goto_ref_entry {
+
+    int label_index;
+    int ref_label;
+    
+    long stack_size;
+
+};
+
+static struct goto_ref_entry goto_refs[MAX_GOTO_REFS];
+static int goto_ref_count = 0;
+
+static int current_break_label = -1;
+static int current_continue_label = -1;
+
+static long current_break_cleanup_base = 0;
+static long current_continue_cleanup_base = 0;
+
+#define     MAX_SWITCH_CASES        256
+
+struct switch_case_entry {
+
+    long value;
+    int label;
+
+};
+
+struct switch_context {
+
+    struct switch_case_entry cases[MAX_SWITCH_CASES];
+    int case_count;
+    int default_label;
+    int break_label;
+
+};
+
+static struct switch_context *current_switch_context = 0;
+static int statement_ends_control_flow;
+
+static void reset_goto_labels (void) {
+
+    int i;
+    
+    for (i = 0; i < goto_label_count; i++) {
+    
+        if (goto_labels[i].name) {
+            free (goto_labels[i].name);
+        }
+        
+        goto_labels[i].name = 0;
+        goto_labels[i].label = 0;
+        goto_labels[i].defined = 0;
+        goto_labels[i].referenced = 0;
+        goto_labels[i].defined_stack_size = 0;
+        goto_labels[i].line = 0;
+        goto_labels[i].start = 0;
+        goto_labels[i].caret = 0;
+    
+    }
+    
+    goto_label_count = 0;
+    goto_ref_count = 0;
+
+}
+
+static int find_goto_label (const char *name) {
+
+    int i;
+    
+    for (i = 0; i < goto_label_count; i++) {
+    
+        if (goto_labels[i].name && strcmp (goto_labels[i].name, name) == 0) {
+            return i;
+        }
+    
+    }
+    
+    return -1;
+
+}
+
+static int get_goto_label (const char *name, unsigned long line, const char *start, const char *caret) {
+
+    int i = find_goto_label (name);
+    
+    if (i >= 0) {
+        return i;
+    }
+    
+    if (goto_label_count >= MAX_GOTO_LABELS) {
+    
+        report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "too many goto labels");
+        return -1;
+    
+    }
+    
+    i = goto_label_count++;
+    
+    goto_labels[i].name = xstrdup (name);
+    goto_labels[i].label = anon_label++;
+    goto_labels[i].defined = 0;
+    goto_labels[i].referenced = 0;
+    goto_labels[i].defined_stack_size = 0;
+    goto_labels[i].line = line;
+    goto_labels[i].start = start;
+    goto_labels[i].caret = caret;
+    
+    return i;
+
+}
+
+static void define_goto_label (const char *name, unsigned long line, const char *start, const char *caret) {
+
+    int i = get_goto_label (name, line, start, caret);
+    
+    if (i < 0) {
+        return;
+    }
+    
+    if (goto_labels[i].defined) {
+    
+        report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "duplicate label '%s'", name);
+        return;
+    
+    }
+    
+    goto_labels[i].defined = 1;
+    goto_labels[i].defined_stack_size = current_local_stack_size;
+    
+    /**
+     * A C label marks the next statement.  If the previous statement was a
+     * return, the compiler may have deferred the jump to the common return
+     * label so it can avoid redundant jumps.  Flush it before emitting the
+     * user label, otherwise the user label will point at the deferred return
+     * jump instead of the labelled statement.
+     */
+    if (pending_return_jump) {
+        emit_pending_return_jump ();
+    }
+    
+    emit_statement_label (goto_labels[i].label);
+
+}
+
+static void reference_goto_label (const char *name, unsigned long line, const char *start, const char *caret) {
+
+    int i = get_goto_label (name, line, start, caret);
+    
+    if (i < 0) {
+        return;
+    }
+    
+    goto_labels[i].referenced = 1;
+    
+    /*
+     * If this is a forward goto, do not jump directly to the final C
+     * label.  The target may be after automatic declarations in an inner
+     * block.  Because SCC emits stack allocation when those declarations are
+     * parsed, a direct branch can bypass the allocation and then use invalid
+     * EBP-relative locals.  Emit a per-reference trampoline after the function
+     * epilogue once the target stack depth is known.
+     */
+    if (!goto_labels[i].defined) {
+    
+        if (goto_ref_count < MAX_GOTO_REFS) {
+        
+            int ref_label = anon_label++;
+            
+            goto_refs[goto_ref_count].label_index = i;
+            goto_refs[goto_ref_count].ref_label = ref_label;
+            goto_refs[goto_ref_count].stack_size = current_local_stack_size;
+            goto_ref_count++;
+            
+            emit_statement_jump (ref_label);
+            return;
+        
+        }
+    
+    }
+    
+    emit_statement_jump (goto_labels[i].label);
+
+}
+
+static void check_goto_labels (void) {
+
+    int i;
+    
+    for (i = 0; i < goto_label_count; i++) {
+    
+        if (goto_labels[i].referenced && !goto_labels[i].defined) {
+            report_line_at (get_filename (), goto_labels[i].line, REPORT_ERROR, goto_labels[i].start, goto_labels[i].caret, "undefined label '%s'", goto_labels[i].name);
+        }
+    
+    }
+
+}
+
+static void emit_goto_trampolines (void) {
+
+    int i;
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    for (i = 0; i < goto_ref_count; i++) {
+    
+        int label_index = goto_refs[i].label_index;
+        long delta;
+        
+        if (label_index < 0 || label_index >= goto_label_count) {
+            continue;
+        }
+        
+        if (!goto_labels[label_index].defined) {
+            continue;
+        }
+        
+        emit_statement_label_raw (goto_refs[i].ref_label);
+        delta = goto_labels[label_index].defined_stack_size - goto_refs[i].stack_size;
+        
+        if (delta > 0) {
+            emit_stack_adjust (delta, 1);
+        } else if (delta < 0) {
+            emit_stack_adjust (-delta, 0);
+        }
+        
+        emit_statement_jump (goto_labels[label_index].label);
+    
+    }
+
+}
+
+static void queue_pending_statement_label (int label) {
+
+    if (label < 0) {
+        return;
+    }
+    
+    if (pending_statement_label_count >= MAX_PENDING_STATEMENT_LABELS) {
+        flush_pending_statement_labels ();
+    }
+    
+    if (pending_statement_label_count < MAX_PENDING_STATEMENT_LABELS) {
+        pending_statement_labels[pending_statement_label_count++] = label;
+    } else {
+        emit_statement_label (label);
+    }
+
+}
+
+static void add_switch_case_label (long value, unsigned long line, const char *start, const char *caret) {
+
+    int i;
+    
+    if (!current_switch_context) {
+    
+        report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "case label not within a switch statement");
+        return;
+    
+    }
+    
+    for (i = 0; i < current_switch_context->case_count; i++) {
+    
+        if (current_switch_context->cases[i].value == value) {
+        
+            report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "duplicate case value");
+            return;
+        
+        }
+    
+    }
+    
+    if (current_switch_context->case_count >= MAX_SWITCH_CASES) {
+    
+        report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "too many case labels in switch statement");
+        return;
+    
+    }
+    
+    current_switch_context->cases[current_switch_context->case_count].value = value;
+    current_switch_context->cases[current_switch_context->case_count].label = anon_label++;
+    
+    queue_pending_statement_label (current_switch_context->cases[current_switch_context->case_count].label);
+    current_switch_context->case_count++;
+
+}
+
+static void set_switch_default_label (unsigned long line, const char *start, const char *caret) {
+
+    if (!current_switch_context) {
+    
+        report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "default label not within a switch statement");
+        return;
+    
+    }
+    
+    if (current_switch_context->default_label >= 0) {
+    
+        report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "duplicate default label");
+        return;
+    
+    }
+    
+    current_switch_context->default_label = anon_label++;
+    queue_pending_statement_label (current_switch_context->default_label);
+
+}
+
+static void emit_switch_dispatch (struct switch_context *sw) {
+
+    int i;
+    int target;
+    
+    if (!state->ofp || !sw) {
+        return;
+    }
+    
+    for (i = 0; i < sw->case_count; i++) {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+        
+            fprintf (state->ofp, "    cmp eax, %ld\n", sw->cases[i].value);
+            
+            if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+                fprintf (state->ofp, "    je L%d\n", sw->cases[i].label);
+            } else {
+                fprintf (state->ofp, "    je .L%d\n", sw->cases[i].label);
+            }
+        
+        } else {
+        
+            fprintf (state->ofp, "    cmpl $%ld, %%eax\n", sw->cases[i].value);
+            fprintf (state->ofp, "    je .L%d\n", sw->cases[i].label);
+        
+        }
+    
+    }
+    
+    target = (sw->default_label >= 0) ? sw->default_label : sw->break_label;
+    emit_statement_jump (target);
+
+}
+
+static void parse_switch_statement (void) {
+
+    struct switch_context sw;
+    struct switch_context *old_switch_context;
+    
+    int old_break_label;
+    int old_continue_label;
+    
+    long old_break_cleanup_base;
+    long old_continue_cleanup_base;
+    
+    int saved_ends_control_flow;
+    
+    FILE *saved_ofp;
+    FILE *body_tmp = 0;
+    
+    char *body_text = 0;
+    int body_pending_return_jump = 0;
+    
+    sw.case_count = 0;
+    sw.default_label = -1;
+    sw.break_label = anon_label++;
+    
+    get_token ();
+    expect (TOK_LPAREN, "(");
+    
+    emit_load_assignment_rhs_expression_to_reg ("eax");
+    
+    if (tok.kind != TOK_RPAREN) {
+        skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+    }
+    
+    expect (TOK_RPAREN, ")");
+    
+    old_switch_context = current_switch_context;
+    old_break_label = current_break_label;
+    old_continue_label = current_continue_label;
+    old_break_cleanup_base = current_break_cleanup_base;
+    old_continue_cleanup_base = current_continue_cleanup_base;
+    
+    current_switch_context = &sw;
+    current_break_label = sw.break_label;
+    current_break_cleanup_base = current_block_cleanup_bytes;
+    
+    saved_ofp = state->ofp;
+    
+    if (saved_ofp) {
+    
+        body_tmp = tmpfile ();
+        
+        if (body_tmp) {
+            state->ofp = body_tmp;
+        }
+    
+    }
+    
+    parse_statement ();
+    saved_ends_control_flow = statement_ends_control_flow;
+    
+    if (body_tmp) {
+    
+        body_text = read_tmp_file_text (body_tmp);
+        
+        fclose (body_tmp);
+        body_tmp = 0;
+        
+        state->ofp = saved_ofp;
+        
+        body_pending_return_jump = pending_return_jump;
+        pending_return_jump = 0;
+        
+        emit_switch_dispatch (&sw);
+        
+        if (body_text) {
+        
+            fputs (body_text, state->ofp);
+            free (body_text);
+        
+        }
+        
+        pending_return_jump = body_pending_return_jump;
+    
+    } else {
+        state->ofp = saved_ofp;
+    }
+    
+    emit_statement_label (sw.break_label);
+    
+    current_switch_context = old_switch_context;
+    current_break_label = old_break_label;
+    current_continue_label = old_continue_label;
+    current_break_cleanup_base = old_break_cleanup_base;
+    current_continue_cleanup_base = old_continue_cleanup_base;
+    
+    /*
+     * A break inside the switch only leaves the switch.  Do not propagate the
+     * break statement's statement_ends_control_flow flag to the enclosing
+     * statement, otherwise code like:
+     *
+     *     if (x) { switch (y) { case 1: break; } } else { ... }
+     *
+     * is compiled without the jump over the else block and the true branch
+     * falls through into the else body.
+     */
+    (void)saved_ends_control_flow;
+    statement_ends_control_flow = 0;
+
+}
+
+static int is_value_compare_operator (enum token_kind k) {
+
+    return  k == TOK_LESS || k == TOK_LTEQ || k == TOK_GREATER ||
+                k == TOK_GTEQ || k == TOK_EQEQ || k == TOK_NOTEQ;
+
+}
+
+static const char *value_compare_set_mnemonic (enum token_kind op, int is_unsigned) {
+
+    switch (op) {
+    
+        case TOK_LESS:
+        
+            return is_unsigned ? "setb"  : "setl";
+        
+        case TOK_LTEQ:
+        
+            return is_unsigned ? "setbe" : "setle";
+        
+        case TOK_GREATER:
+        
+            return is_unsigned ? "seta"  : "setg";
+        
+        case TOK_GTEQ:
+        
+            return is_unsigned ? "setae" : "setge";
+        
+        case TOK_EQEQ:
+        
+            return "sete";
+        
+        case TOK_NOTEQ:
+        
+            return "setne";
+        
+        default:
+        
+            return "setne";
+    
+    }
+
+}
+
+static void emit_compare_eax_edx_to_reg (enum token_kind op, const char *reg, int is_unsigned) {
+
+    const char *setcc;
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    setcc = value_compare_set_mnemonic (op, is_unsigned);
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        fprintf (state->ofp, "    cmp eax, edx\n");
+        fprintf (state->ofp, "    %s al\n", setcc);
+        fprintf (state->ofp, "    movzx eax, al\n");
+        
+        if (strcmp (reg, "eax") != 0) {
+            fprintf (state->ofp, "    mov %s, eax\n", reg);
+        }
+    
+    } else {
+    
+        fprintf (state->ofp, "    cmpl %%edx, %%eax\n");
+        fprintf (state->ofp, "    %s %%al\n", setcc);
+        fprintf (state->ofp, "    movzbl %%al, %%eax\n");
+        
+        if (strcmp (reg, "eax") != 0) {
+            fprintf (state->ofp, "    movl %%eax, %%%s\n", reg);
+        }
+    
+    }
+
+}
+
+static int rhs_current_operand_is_unsigned_now (void);
+
+static int source_lhs_has_char_pointer_cast_before_now (const char *p) {
+
+    const char *q;
+    int limit = 160;
+    
+    if (!p) {
+        return 0;
+    }
+    
+    q = p;
+    
+    while (limit-- > 0 && q > tok.start - 4096) {
+    
+        q--;
+        
+        if (*q != '(') {
+            continue;
+        }
+        
+        if ((strncmp (q, "(char", 5) == 0 || strncmp (q, "(unsigned char", 14) == 0 || strncmp (q, "(signed char", 12) == 0)
+            && strchr (q, '*') && strchr (q, ')') && strchr (q, '*') < p && strchr (q, ')') < p) {
+            return 1;
+        }
+        
+        if (*q == ';' || *q == ',' || *q == '\n') {
+            break;
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static int arithmetic_operator_precedence_now (enum token_kind op) {
+
+    if (op == TOK_PIPE) {
+        return 1;
+    }
+    
+    if (op == TOK_CARET) {
+        return 2;
+    }
+    
+    if (op == TOK_AMPER) {
+        return 3;
+    }
+    
+    if (op == TOK_LSH || op == TOK_RSH) {
+        return 4;
+    }
+    
+    if (op == TOK_PLUS || op == TOK_MINUS) {
+        return 5;
+    }
+    
+    if (op == TOK_STAR || op == TOK_BSLASH || op == TOK_MOD) {
+        return 6;
+    }
+    
+    return 0;
+
+}
+
+static int emit_load_assignment_binary_expression_prec_to_reg (const char *reg, int min_prec) {
+
+    int is_unsigned;
+    int expr_pointer_depth;
+    int expr_pointed_size;
+    
+    is_unsigned = rhs_current_operand_is_unsigned_now ();
+    emit_load_assignment_rhs_to_reg (reg);
+    
+    /*
+     * Some statement-condition paths can leave a postfix member chain after
+     * the primary operand, e.g. inside parenthesized logical RHS terms such as
+     *     && (reg->type.dword || reg->type.debug)
+     * Consume that postfix here before the binary/logical expression parser
+     * decides whether the operand is complete.  Otherwise the enclosing
+     * parenthesized-expression code sees the still-pending "->"/"." token and
+     * reports a false "expected )".
+     */
+    while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+        emit_apply_postfix_member_access_to_reg_now (reg);
+    }
+    
+    /*
+     * The postfix member helper deliberately leaves aggregates and 64-bit
+     * scalar members as an address because the pair/aggregate paths may need
+     * the full object.  This single-register expression path, however, only
+     * has EAX/EDX-style 32-bit arithmetic available.  When such a member is
+     * used inside ordinary arithmetic/comparison code (for example the
+     * CHECK_READ macro in pdld with address_type fields), load the low word
+     * from the member address instead of adding the member address itself.
+     */
+    if (postfix_member_seen
+        && postfix_member_pointer_depth == 0
+        && postfix_member_size == (DATA_LLONG & 0x1f)
+        && !postfix_member_is_floating) {
+    
+        emit_load_deref_reg_now (reg, DATA_INT & 0x1f);
+        postfix_member_size = DATA_INT & 0x1f;
+    
+    }
+    
+    if (postfix_member_seen && postfix_member_is_unsigned) {
+        is_unsigned = 1;
+    }
+    
+    expr_pointer_depth = rhs_last_pointer_depth;
+    expr_pointed_size = rhs_last_pointed_size;
+    
+    while (is_arithmetic_binary_operator (tok.kind) && arithmetic_operator_precedence_now (tok.kind) >= min_prec) {
+        
+        enum token_kind op;
+        
+        int prec;
+        int rhs_is_unsigned;
+        int lhs_pointer_depth;
+        int lhs_pointed_size;
+        int rhs_pointer_depth;
+        int rhs_pointed_size;
+        int scale_rhs = 0;
+        int scale_lhs = 0;
+        
+        op = tok.kind;
+        prec = arithmetic_operator_precedence_now (op);
+        
+        lhs_pointer_depth = expr_pointer_depth;
+        lhs_pointed_size = expr_pointed_size;
+        
+        if ((op == TOK_PLUS || op == TOK_MINUS) && lhs_pointer_depth > 0 &&
+            source_lhs_has_char_pointer_cast_before_now (tok.caret)) {
+            lhs_pointed_size = DATA_CHAR & 0x1f;
+        }
+        
+        get_token ();
+        rhs_is_unsigned = rhs_current_operand_is_unsigned_now ();
+        
+        if (rhs_is_unsigned) {
+            is_unsigned = 1;
+        }
+        
+        if (strcmp (reg, "eax") != 0 && state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    mov eax, %s\n", reg);
+            } else {
+                fprintf (state->ofp, "    movl %%%s, %%eax\n", reg);
+            }
+        
+        }
+        
+        emit_push_reg_now ("eax");
+        rhs_is_unsigned = emit_load_assignment_binary_expression_prec_to_reg ("edx", prec + 1);
+        
+        if (rhs_is_unsigned) {
+            is_unsigned = 1;
+        }
+        
+        rhs_pointer_depth = rhs_last_pointer_depth;
+        rhs_pointed_size = rhs_last_pointed_size;
+        
+        if ((op == TOK_PLUS || op == TOK_MINUS) && lhs_pointer_depth > 0 && rhs_pointer_depth == 0) {
+            scale_rhs = index_step_size (lhs_pointed_size);
+        } else if (op == TOK_PLUS && lhs_pointer_depth == 0 && rhs_pointer_depth > 0) {
+            scale_lhs = index_step_size (rhs_pointed_size);
+        }
+        
+        if (scale_rhs > 1) {
+            emit_scale_reg_by_const_now ("edx", scale_rhs);
+        }
+        
+        emit_pop_reg_now ("eax");
+        
+        if (scale_lhs > 1) {
+            emit_scale_reg_by_const_now ("eax", scale_lhs);
+        }
+        
+        emit_assignment_binary_op (op, is_unsigned);
+        
+        if (op == TOK_MINUS && lhs_pointer_depth > 0 && rhs_pointer_depth > 0 && index_step_size (lhs_pointed_size) > 1) {
+            emit_divide_eax_by_const_now (index_step_size (lhs_pointed_size));
+        }
+        
+        if (op == TOK_PLUS && lhs_pointer_depth == 0 && rhs_pointer_depth > 0) {
+        
+            expr_pointer_depth = rhs_pointer_depth;
+            expr_pointed_size = rhs_pointed_size;
+        
+        } else if (op == TOK_MINUS && lhs_pointer_depth > 0 && rhs_pointer_depth > 0) {
+        
+            expr_pointer_depth = 0;
+            expr_pointed_size = 0;
+        
+        } else if (op != TOK_PLUS && op != TOK_MINUS) {
+        
+            expr_pointer_depth = 0;
+            expr_pointed_size = 0;
+        
+        }
+        
+        if (strcmp (reg, "eax") != 0 && state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    mov %s, eax\n", reg);
+            } else {
+                fprintf (state->ofp, "    movl %%eax, %%%s\n", reg);
+            }
+        
+        }
+    
+    }
+    
+    set_rhs_last_pointer_info (expr_pointer_depth, expr_pointed_size);
+    return is_unsigned;
+
+}
+
+static void emit_load_assignment_binary_expression_to_reg (const char *reg) {
+
+    if (const_integer_expr_text_is_foldable_now (tok.caret)) {
+    
+        int64_s v = const64_from_current_foldable_expr ();
+        emit_load_const32_to_reg_now (reg, v);
+        
+        return;
+    
+    }
+    
+    emit_load_assignment_binary_expression_prec_to_reg (reg, 1);
+
+}
+
+static void emit_load_assignment_compare_expression_to_reg (const char *reg) {
+
+    enum token_kind op;
+    
+    int lhs_pointer_depth;
+    int is_unsigned;
+    
+    is_unsigned = rhs_current_operand_is_unsigned_now ();
+    
+    if (emit_load_assignment_binary_expression_prec_to_reg (reg, 1)) {
+        is_unsigned = 1;
+    }
+    
+    lhs_pointer_depth = rhs_last_pointer_depth;
+    
+    if (is_value_compare_operator (tok.kind)) {
+    
+        op = tok.kind;
+        get_token ();
+        
+        if (strcmp (reg, "eax") != 0 && state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    mov eax, %s\n", reg);
+            } else {
+                fprintf (state->ofp, "    movl %%%s, %%eax\n", reg);
+            }
+        
+        }
+        
+        emit_push_reg_now ("eax");
+        
+        if (rhs_current_operand_is_unsigned_now ()) {
+            is_unsigned = 1;
+        }
+        
+        emit_load_assignment_binary_expression_to_reg ("edx");
+        
+        if (lhs_pointer_depth > 0 || rhs_last_pointer_depth > 0) {
+            is_unsigned = 1;
+        }
+        
+        emit_pop_reg_now ("eax");
+        emit_compare_eax_edx_to_reg (op, reg, is_unsigned);
+    
+    }
+
+}
+
+static void emit_test_reg_jump_zero_now (const char *reg, int label) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        fprintf (state->ofp, "    test %s, %s\n", reg, reg);
+        fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jz L%d\n" : "    jz .L%d\n"), label);
+    
+    } else {
+    
+        fprintf (state->ofp, "    testl %%%s, %%%s\n", reg, reg);
+        fprintf (state->ofp, "    jz .L%d\n", label);
+    
+    }
+
+}
+
+static void emit_test_reg_jump_nonzero_now (const char *reg, int label) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        fprintf (state->ofp, "    test %s, %s\n", reg, reg);
+        fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), label);
+    
+    } else {
+    
+        fprintf (state->ofp, "    testl %%%s, %%%s\n", reg, reg);
+        fprintf (state->ofp, "    jnz .L%d\n", label);
+    
+    }
+
+}
+
+static void emit_mov_imm_to_reg_now (const char *reg, long value) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+        fprintf (state->ofp, "    mov %s, %ld\n", reg, value);
+    } else {
+        fprintf (state->ofp, "    movl $%ld, %%%s\n", value, reg);
+    }
+
+}
+
+static void emit_floating_stack_to_int_reg_now (const char *reg) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        fprintf (state->ofp, "    sub esp, 4\n");
+        fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? "    fistp dword [esp]\n" : "    fistp dword ptr [esp]\n");
+        fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? "    mov %s, dword [esp]\n" : "    mov %s, dword ptr [esp]\n", reg);
+        fprintf (state->ofp, "    add esp, 4\n");
+    
+    } else {
+    
+        fprintf (state->ofp, "    subl $4, %%esp\n");
+        fprintf (state->ofp, "    fistpl (%%esp)\n");
+        fprintf (state->ofp, "    movl (%%esp), %%%s\n", reg);
+        fprintf (state->ofp, "    addl $4, %%esp\n");
+    
+    }
+
+}
+
+static void emit_load_assignment_rhs_expression_to_reg (const char *reg) {
+
+    int false_label;
+    int true_label;
+    int end_label;
+    
+    enum token_kind logop;
+    
+    if (rhs_current_operand_is_floating_now ()) {
+    
+        emit_load_floating_rhs_expression_now (DATA_DOUBLE & 0x1f);
+        emit_floating_stack_to_int_reg_now (reg);
+        
+        return;
+    
+    }
+    
+    emit_load_assignment_compare_expression_to_reg (reg);
+    
+    while (tok.kind == TOK_LOGAND || tok.kind == TOK_LOGOR) {
+    
+        logop = tok.kind;
+        false_label = anon_label++;
+        true_label = anon_label++;
+        end_label = anon_label++;
+        
+        get_token ();
+        
+        if (logop == TOK_LOGAND) {
+        
+            emit_test_reg_jump_zero_now (reg, false_label);
+            emit_load_assignment_rhs_expression_to_reg (reg);
+            emit_test_reg_jump_zero_now (reg, false_label);
+            emit_mov_imm_to_reg_now (reg, 1);
+            emit_statement_jump (end_label);
+            emit_statement_label (false_label);
+            emit_mov_imm_to_reg_now (reg, 0);
+            emit_statement_label (end_label);
+        
+        } else {
+        
+            emit_test_reg_jump_nonzero_now (reg, true_label);
+            emit_load_assignment_rhs_expression_to_reg (reg);
+            emit_test_reg_jump_nonzero_now (reg, true_label);
+            emit_mov_imm_to_reg_now (reg, 0);
+            emit_statement_jump (end_label);
+            emit_statement_label (true_label);
+            emit_mov_imm_to_reg_now (reg, 1);
+            emit_statement_label (end_label);
+        
+        }
+    
+    }
+    
+    if (tok.kind == TOK_QMARK) {
+    
+        false_label = anon_label++;
+        end_label = anon_label++;
+        
+        get_token ();
+        
+        if (state->ofp) {
+        
+            if (strcmp (reg, "eax") != 0) {
+            
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                    fprintf (state->ofp, "    mov eax, %s\n", reg);
+                } else {
+                    fprintf (state->ofp, "    movl %%%s, %%eax\n", reg);
+                }
+            
+            }
+            
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+            
+                fprintf (state->ofp, "    test eax, eax\n");
+                
+                if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+                    fprintf (state->ofp, "    jz L%d\n", false_label);
+                } else {
+                    fprintf (state->ofp, "    jz .L%d\n", false_label);
+                }
+            
+            } else {
+            
+                fprintf (state->ofp, "    testl %%eax, %%eax\n");
+                fprintf (state->ofp, "    jz .L%d\n", false_label);
+            
+            }
+        
+        }
+        
+        emit_load_assignment_rhs_expression_to_reg (reg);
+        expect (TOK_COLON, ":");
+        emit_statement_jump (end_label);
+        emit_statement_label (false_label);
+        emit_load_assignment_rhs_expression_to_reg (reg);
+        emit_statement_label (end_label);
+    
+    }
+
+}
+
+static void emit_statement_cmp64_to_eax (enum token_kind op, int is_unsigned);
+
+static int is_assignment64_binary_operator (enum token_kind k) {
+
+    return  is_arithmetic_binary_operator (k) ||
+                k == TOK_LESS || k == TOK_LTEQ || k == TOK_GREATER ||
+                    k == TOK_GTEQ || k == TOK_EQEQ || k == TOK_NOTEQ ||
+                        k == TOK_LOGAND || k == TOK_LOGOR;
+
+}
+
+static void emit_assignment64_bool_result_to_pair_now (const char *lo, const char *hi) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        if (strcmp (lo, "eax") != 0) {
+            fprintf (state->ofp, "    mov %s, eax\n", lo);
+        }
+        
+        fprintf (state->ofp, "    xor %s, %s\n", hi, hi);
+    
+    } else {
+    
+        if (strcmp (lo, "eax") != 0) {
+            fprintf (state->ofp, "    movl %%eax, %%%s\n", lo);
+        }
+        
+        fprintf (state->ofp, "    xorl %%%s, %%%s\n", hi, hi);
+    
+    }
+
+}
+
+static void emit_assignment64_logical_op_to_pair_now (enum token_kind op, const char *lo, const char *hi) {
+
+    int true_label;
+    int false_label;
+    int end_label;
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    true_label = anon_label++;
+    false_label = anon_label++;
+    end_label = anon_label++;
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        fprintf (state->ofp, "    test edx, edx\n");
+        fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), op == TOK_LOGOR ? true_label : end_label);
+        fprintf (state->ofp, "    test eax, eax\n");
+        fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), op == TOK_LOGOR ? true_label : end_label);
+        
+        if (op == TOK_LOGAND) {
+        
+            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jmp L%d\n" : "    jmp .L%d\n"), false_label);
+            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), end_label);
+            fprintf (state->ofp, "    test ecx, ecx\n");
+            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), true_label);
+            fprintf (state->ofp, "    test ebx, ebx\n");
+            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), true_label);
+            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), false_label);
+            fprintf (state->ofp, "    xor eax, eax\n");
+            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jmp L%d\n" : "    jmp .L%d\n"), end_label + 1);
+            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), true_label);
+            fprintf (state->ofp, "    mov eax, 1\n");
+            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), end_label + 1);
+        
+        } else {
+        
+            fprintf (state->ofp, "    test ecx, ecx\n");
+            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), true_label);
+            fprintf (state->ofp, "    test ebx, ebx\n");
+            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), true_label);
+            fprintf (state->ofp, "    xor eax, eax\n");
+            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jmp L%d\n" : "    jmp .L%d\n"), end_label);
+            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), true_label);
+            fprintf (state->ofp, "    mov eax, 1\n");
+            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), end_label);
+        
+        }
+    
+    } else {
+    
+        fprintf (state->ofp, "    testl %%edx, %%edx\n");
+        fprintf (state->ofp, op == TOK_LOGOR ? "    jnz .L%d\n" : "    jnz .L%d\n", op == TOK_LOGOR ? true_label : end_label);
+        fprintf (state->ofp, "    testl %%eax, %%eax\n");
+        fprintf (state->ofp, op == TOK_LOGOR ? "    jnz .L%d\n" : "    jnz .L%d\n", op == TOK_LOGOR ? true_label : end_label);
+        
+        if (op == TOK_LOGAND) {
+        
+            fprintf (state->ofp, "    jmp .L%d\n", false_label);
+            fprintf (state->ofp, ".L%d:\n", end_label);
+            fprintf (state->ofp, "    testl %%ecx, %%ecx\n");
+            fprintf (state->ofp, "    jnz .L%d\n", true_label);
+            fprintf (state->ofp, "    testl %%ebx, %%ebx\n");
+            fprintf (state->ofp, "    jnz .L%d\n", true_label);
+            fprintf (state->ofp, ".L%d:\n", false_label);
+            fprintf (state->ofp, "    xorl %%eax, %%eax\n");
+            fprintf (state->ofp, "    jmp .L%d\n", end_label + 1);
+            fprintf (state->ofp, ".L%d:\n", true_label);
+            fprintf (state->ofp, "    movl $1, %%eax\n");
+            fprintf (state->ofp, ".L%d:\n", end_label + 1);
+        
+        } else {
+        
+            fprintf (state->ofp, "    testl %%ecx, %%ecx\n");
+            fprintf (state->ofp, "    jnz .L%d\n", true_label);
+            fprintf (state->ofp, "    testl %%ebx, %%ebx\n");
+            fprintf (state->ofp, "    jnz .L%d\n", true_label);
+            fprintf (state->ofp, "    xorl %%eax, %%eax\n");
+            fprintf (state->ofp, "    jmp .L%d\n", end_label);
+            fprintf (state->ofp, ".L%d:\n", true_label);
+            fprintf (state->ofp, "    movl $1, %%eax\n");
+            fprintf (state->ofp, ".L%d:\n", end_label);
+        
+        }
+    
+    }
+    
+    anon_label++;
+    emit_assignment64_bool_result_to_pair_now (lo, hi);
+
+}
+
+static void emit_assignment64_compare_op_to_pair_now (enum token_kind op, const char *lo, const char *hi, int is_unsigned) {
+
+    emit_statement_cmp64_to_eax (op, is_unsigned);
+    emit_assignment64_bool_result_to_pair_now (lo, hi);
+
+}
+
+static void emit_load_assignment_rhs_expression_to_pair (const char *lo, const char *hi, int is_unsigned) {
+
+    enum token_kind op;
+    
+    int result_pair_is_eax_edx = (strcmp (lo, "eax") == 0 && strcmp (hi, "edx") == 0);
+    int current_pair_is_eax_edx = result_pair_is_eax_edx;
+    
+    if (const_integer_expr_text_is_foldable_now (tok.start)) {
+    
+        int64_s v = const64_from_current_foldable_expr ();
+        emit_load_const64_to_pair_now (lo, hi, v);
+        
+        return;
+    
+    }
+    
+    emit_load_assignment_rhs_to_pair (lo, hi);
+    
+    while (is_assignment64_binary_operator (tok.kind)) {
+    
+        op = tok.kind;
+        get_token ();
+        
+        if (!current_pair_is_eax_edx) {
+        
+            emit_mov_reg_to_reg_now ("eax", lo);
+            emit_mov_reg_to_reg_now ("edx", hi);
+            
+            current_pair_is_eax_edx = 1;
+        
+        }
+        
+        emit_preserve_assignment64_regs (op);
+        
+        /*
+         * The right operand of a 64-bit shift is a plain integer shift
+         * count, not a 64-bit value.  Loading it through the 64-bit primary
+         * path loses precedence for cases such as:
+         *
+         *     ((address_type)1) << (CHAR_BIT * rel->howto->size)
+         *
+         * Worse, the RHS loader uses EAX internally for nested expressions;
+         * preserve the 64-bit LHS in EDX:EAX while the count is evaluated
+         * into EBX.
+         */
+        if (op == TOK_LSH || op == TOK_RSH || op == TOK_LSHEQ || op == TOK_RSHEQ) {
+        
+            emit_push_reg_now ("eax");
+            emit_push_reg_now ("edx");
+            
+            emit_load_assignment_binary_expression_to_reg ("ebx");
+            
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    xor ecx, ecx\n");
+            } else {
+                fprintf (state->ofp, "    xorl %%ecx, %%ecx\n");
+            }
+            
+            emit_pop_reg_now ("edx");
+            emit_pop_reg_now ("eax");
+        
+        } else {
+        
+            /*
+             * The generic 64-bit RHS loader uses EAX:EDX as scratch even
+             * when asked to leave the final value in EBX:ECX.  Preserve the
+             * left operand around RHS evaluation; otherwise expressions such
+             * as:
+             *
+             *     result &= (((address_type)1) << n) - 1
+             *
+             * end up applying the operator to the RHS twice, because the
+             * computed mask clobbers the original result in EAX:EDX.
+             */
+            emit_push_reg_now ("eax");
+            emit_push_reg_now ("edx");
+            
+            emit_load_assignment_rhs_to_pair ("ebx", "ecx");
+            
+            emit_pop_reg_now ("edx");
+            emit_pop_reg_now ("eax");
+        
+        }
+        
+        if (op == TOK_LOGAND || op == TOK_LOGOR) {
+        
+            emit_assignment64_logical_op_to_pair_now (op, lo, hi);
+            current_pair_is_eax_edx = result_pair_is_eax_edx;
+        
+        } else if (is_value_compare_operator (op)) {
+        
+            emit_assignment64_compare_op_to_pair_now (op, lo, hi, is_unsigned);
+            current_pair_is_eax_edx = result_pair_is_eax_edx;
+        
+        } else {
+        
+            emit_assignment_binary_op64 (op, is_unsigned);
+            current_pair_is_eax_edx = 1;
+        
+        }
+        
+        emit_restore_assignment64_regs (op);
+    
+    }
+    
+    if (current_pair_is_eax_edx && !result_pair_is_eax_edx) {
+    
+        emit_mov_reg_to_reg_now (lo, "eax");
+        emit_mov_reg_to_reg_now (hi, "edx");
+        
+        current_pair_is_eax_edx = 0;
+    
+    }
+    
+    if (tok.kind == TOK_QMARK) {
+    
+        int false_label = anon_label++;
+        int end_label = anon_label++;
+        
+        get_token ();
+        
+        if (state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+            
+                fprintf (state->ofp, "    test %s, %s\n", hi, hi);
+                fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), false_label + 2);
+                fprintf (state->ofp, "    test %s, %s\n", lo, lo);
+                fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jz L%d\n" : "    jz .L%d\n"), false_label);
+                fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), false_label + 2);
+            
+            } else {
+            
+                fprintf (state->ofp, "    testl %%%s, %%%s\n", hi, hi);
+                fprintf (state->ofp, "    jnz .L%d\n", false_label + 2);
+                fprintf (state->ofp, "    testl %%%s, %%%s\n", lo, lo);
+                fprintf (state->ofp, "    jz .L%d\n", false_label);
+                fprintf (state->ofp, ".L%d:\n", false_label + 2);
+            
+            }
+        
+        }
+        
+        anon_label++;
+        
+        emit_load_assignment_rhs_expression_to_pair (lo, hi, is_unsigned);
+        expect (TOK_COLON, ":");
+        
+        emit_statement_jump (end_label);
+        emit_statement_label (false_label);
+        emit_load_assignment_rhs_expression_to_pair (lo, hi, is_unsigned);
+        emit_statement_label (end_label);
+    
+    }
+
+}
+
+static void emit_incdec_integral_symbol_now (struct local_symbol *sym, const char *name, int size, enum token_kind op) {
+
+    char memref[64];
+    char nasm_memref[256];
+    
+    const char *symbol;
+    const char *mnemonic;
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (size == (DATA_LLONG & 0x1f)) {
+    
+        if (sym) {
+        
+            if (sym->is_static && sym->static_label) {
+                emit_load_global64_to_pair ("eax", "edx", sym->static_label);
+            } else {
+                emit_load_local64_to_pair (sym->offset, "eax", "edx");
+            }
+        
+        } else {
+            emit_load_global64_to_pair ("eax", "edx", name);
+        }
+        
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+        
+            if (op == TOK_INCR) {
+            
+                fprintf (state->ofp, "    add eax, 1\n");
+                fprintf (state->ofp, "    adc edx, 0\n");
+            
+            } else {
+            
+                fprintf (state->ofp, "    sub eax, 1\n");
+                fprintf (state->ofp, "    sbb edx, 0\n");
+            
+            }
+        
+        } else {
+        
+            if (op == TOK_INCR) {
+            
+                fprintf (state->ofp, "    addl $1, %%eax\n");
+                fprintf (state->ofp, "    adcl $0, %%edx\n");
+            
+            } else {
+            
+                fprintf (state->ofp, "    subl $1, %%eax\n");
+                fprintf (state->ofp, "    sbbl $0, %%edx\n");
+            
+            }
+        
+        }
+        
+        if (sym) {
+        
+            if (sym->is_static && sym->static_label) {
+                emit_store_pair_to_global64 (sym->static_label, "eax", "edx");
+            } else {
+                emit_store_pair_to_local64 (sym->offset, "eax", "edx");
+            }
+        
+        } else {
+            emit_store_pair_to_global64 (name, "eax", "edx");
+        }
+        
+        return;
+    
+    }
+    
+    if (size != (DATA_CHAR & 0x1f) && size != (DATA_SHORT & 0x1f) && size != (DATA_INT & 0x1f) && size != DATA_PTR) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        mnemonic = op == TOK_INCR ? "add" : "sub";
+        
+        if (sym && !sym->is_static) {
+        
+            format_intel_ebp_offset (memref, sizeof (memref), sym->offset);
+            symbol = memref;
+        
+        } else if (sym && sym->static_label) {
+            symbol = asm_global_symbol_name (sym->static_label);
+        } else {
+            symbol = asm_global_symbol_name (name);
+        }
+        
+        if (state->syntax & ASM_SYNTAX_NASM) {
+            symbol = format_nasm_memory_operand (nasm_memref, sizeof (nasm_memref), symbol);
+        }
+        
+        if (size == (DATA_CHAR & 0x1f)) {
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    %s byte %s, 1\n" : "    %s byte ptr %s, 1\n"), mnemonic, symbol);
+        } else if (size == (DATA_SHORT & 0x1f)) {
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    %s word %s, 1\n" : "    %s word ptr %s, 1\n"), mnemonic, symbol);
+        } else {
+            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    %s dword %s, 1\n" : "    %s dword ptr %s, 1\n"), mnemonic, symbol);
+        }
+    
+    } else {
+    
+        if (sym && !sym->is_static) {
+        
+            if (size == (DATA_CHAR & 0x1f)) {
+                fprintf (state->ofp, op == TOK_INCR ? "    incb %ld(%%ebp)\n" : "    decb %ld(%%ebp)\n", sym->offset);
+            } else if (size == (DATA_SHORT & 0x1f)) {
+                fprintf (state->ofp, op == TOK_INCR ? "    incw %ld(%%ebp)\n" : "    decw %ld(%%ebp)\n", sym->offset);
+            } else {
+                fprintf (state->ofp, op == TOK_INCR ? "    incl %ld(%%ebp)\n" : "    decl %ld(%%ebp)\n", sym->offset);
+            }
+        
+        } else {
+        
+            symbol = asm_global_symbol_name ((sym && sym->static_label) ? sym->static_label : name);
+            
+            if (size == (DATA_CHAR & 0x1f)) {
+                fprintf (state->ofp, op == TOK_INCR ? "    incb %s\n" : "    decb %s\n", symbol);
+            } else if (size == (DATA_SHORT & 0x1f)) {
+                fprintf (state->ofp, op == TOK_INCR ? "    incw %s\n" : "    decw %s\n", symbol);
+            } else {
+                fprintf (state->ofp, op == TOK_INCR ? "    incl %s\n" : "    decl %s\n", symbol);
+            }
+        
+        }
+    
+    }
+
+}
+
+static void emit_incdec_pointer_symbol_now (struct local_symbol *sym, const char *name, enum token_kind op, int step) {
+
+    const char *mnemonic = op == TOK_INCR ? "add" : "sub";
+    const char *symbol;
+    
+    char memref[64];
+    char nasm_memref[128];
+    
+    if (step <= 0) {
+        step = 1;
+    }
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (step == 1) {
+    
+        emit_incdec_integral_symbol_now (sym, name, DATA_PTR, op);
+        return;
+    
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        if (sym && !sym->is_static) {
+        
+            format_intel_ebp_offset (memref, sizeof (memref), sym->offset);
+            symbol = memref;
+        
+        } else if (sym && sym->static_label) {
+            symbol = asm_global_symbol_name (sym->static_label);
+        } else {
+            symbol = asm_global_symbol_name (name);
+        }
+        
+        if (state->syntax & ASM_SYNTAX_NASM) {
+        
+            symbol = format_nasm_memory_operand (nasm_memref, sizeof (nasm_memref), symbol);
+            fprintf (state->ofp, "    %s dword %s, %d\n", mnemonic, symbol, step);
+        
+        } else {
+            fprintf (state->ofp, "    %s dword ptr %s, %d\n", mnemonic, symbol, step);
+        }
+    
+    } else {
+    
+        if (sym && !sym->is_static) {
+            fprintf (state->ofp, "    %sl $%d, %ld(%%ebp)\n", mnemonic, step, sym->offset);
+        } else {
+        
+            symbol = asm_global_symbol_name ((sym && sym->static_label) ? sym->static_label : name);
+            fprintf (state->ofp, "    %sl $%d, %s\n", mnemonic, step, symbol);
+        
+        }
+    
+    }
+
+}
+
+static void emit_incdec_symbol_now (struct local_symbol *sym, const char *name, enum token_kind op, int line, const char *start, const char *caret) {
+
+    int global_index;
+    int is_floating;
+    int pointer_depth;
+    int pointed_size;
+    
+    int size;
+    
+    if (!sym) {
+    
+        global_index = find_global_symbol (name);
+        
+        if (global_index < 0) {
+        
+            report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "unknown symbol '%s'", name);
+            return;
+        
+        }
+        
+        if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION) {
+        
+            report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "function '%s' cannot be incremented or decremented", name);
+            return;
+        
+        }
+    
+    }
+    
+    size = sym ? sym->size : get_global_symbol_size (name);
+    is_floating = sym ? sym->is_floating : get_global_symbol_floating (name);
+    
+    pointer_depth = sym ? sym->pointer_depth : get_global_symbol_pointer_depth (name);
+    pointed_size = sym ? sym->pointed_size : get_global_symbol_pointed_size (name);
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (is_floating) {
+    
+        emit_load_floating_symbol_now (sym, name, size);
+        fprintf (state->ofp, "    fld1\n");
+        
+        if (op == TOK_INCR) {
+            emit_floating_binary_now (TOK_PLUS);
+        } else {
+            emit_floating_binary_now (TOK_MINUS);
+        }
+        
+        emit_store_floating_symbol_now (sym, name, size);
+        return;
+    
+    }
+    
+    if (pointer_depth > 0) {
+    
+        emit_incdec_pointer_symbol_now (sym, name, op, pointed_size);
+        return;
+    
+    }
+    
+    emit_incdec_integral_symbol_now (sym, name, size, op);
+
+}
+
+static int parse_prefix_incdec_statement (void) {
+
+    enum token_kind op;
+    char *name;
+    
+    const char *name_start, *name_caret;
+    unsigned long name_line;
+    
+    struct local_symbol *sym;
+    
+    int deref_size = DATA_INT & 0x1f;
+    int indirect = 0;
+    
+    if (emit_load_prefix_incdec_parenthesized_deref_to_reg_now ("eax")) {
+    
+        expect_semi_or_recover ();
+        return 1;
+    
+    }
+    
+    if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) {
+        return 0;
+    }
+    
+    op = tok.kind;
+    get_token ();
+    
+    if (tok.kind == TOK_STAR) {
+    
+        indirect = 1;
+        get_token ();
+    
+    }
+    
+    if (tok.kind != TOK_IDENT) {
+    
+        if (indirect) {
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after *");
+        } else {
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after %s", op == TOK_INCR ? "++" : "--");
+        }
+        
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        expect_semi_or_recover ();
+        
+        return 1;
+    
+    }
+    
+    name = xstrdup (tok.ident);
+    name_start = tok.start;
+    name_caret = tok.caret;
+    name_line = get_line_number ();
+    
+    get_token ();
+
+    sym = find_local_symbol (name);
+    
+    if (indirect) {
+    
+        if (sym) {
+        
+            if (sym->pointer_depth > 1) {
+                deref_size = DATA_PTR & 0x1f;
+            } else if (sym->pointer_depth == 1 && sym->pointed_size > 0) {
+                deref_size = sym->pointed_size & 0x1f;
+            }
+            
+            if (sym->is_static && sym->static_label) {
+                emit_load_global_to_reg ("edx", sym->static_label, DATA_PTR);
+            } else {
+                emit_load_local_to_reg ("edx", sym->offset, DATA_PTR);
+            }
+        
+        } else if (find_global_symbol (name) >= 0) {
+        
+            if (get_global_symbol_pointer_depth (name) > 1) {
+                deref_size = DATA_PTR & 0x1f;
+            } else if (get_global_symbol_pointer_depth (name) == 1 && get_global_symbol_pointed_size (name) > 0) {
+                deref_size = get_global_symbol_pointed_size (name) & 0x1f;
+            }
+            
+            emit_load_global_to_reg ("edx", name, DATA_PTR);
+        
+        } else {
+        
+            report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+            
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+            expect_semi_or_recover ();
+            
+            free (name);
+            return 1;
+        
+        }
+        
+        emit_push_reg_now ("edx");
+        emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size);
+        
+        if (state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, op == TOK_INCR ? "    add eax, 1\n" : "    sub eax, 1\n");
+            } else {
+                fprintf (state->ofp, op == TOK_INCR ? "    addl $1, %%eax\n" : "    subl $1, %%eax\n");
+            }
+        
+        }
+        
+        emit_pop_reg_now ("edx");
+        emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size);
+        
+        expect_semi_or_recover ();
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    emit_incdec_symbol_now (sym, name, op, name_line, name_start, name_caret);
+    
+    expect_semi_or_recover ();
+    free (name);
+    
+    return 1;
+
+}
+
+static int parse_parenthesized_pointer_member_indirect_assignment_statement (void) {
+
+    char *name = 0;
+    char *member = 0;
+    
+    struct local_symbol *sym;
+    
+    int global_index;
+    int parens = 0;
+    int offset = 0;
+    int member_size = DATA_PTR & 0x1f;
+    int member_elem_size = DATA_INT & 0x1f;
+    int member_pointer_depth = 0;
+    int deref_size = DATA_INT & 0x1f;
+    int step = 1;
+    
+    enum token_kind postfix_op = TOK_EOF;
+    enum token_kind op;
+    
+    const char *name_start;
+    const char *name_caret;
+    const char *member_start;
+    const char *member_caret;
+    
+    unsigned long name_line;
+    unsigned long member_line;
+    
+    if (tok.kind != TOK_LPAREN) {
+        return 0;
+    }
+    
+    while (tok.kind == TOK_LPAREN) {
+        ++parens;
+        get_token ();
+    }
+    
+    if (tok.kind != TOK_STAR) {
+    
+        if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+        
+            enum token_kind prefix_op = tok.kind;
+            char *prefix_name;
+            
+            const char *prefix_start;
+            const char *prefix_caret;
+            
+            unsigned long prefix_line;
+            
+            struct local_symbol *prefix_sym;
+            
+            int prefix_global_index;
+            int prefix_deref_size = DATA_INT & 0x1f;
+            
+            enum token_kind prefix_assign_op;
+            get_token ();
+            
+            if (tok.kind != TOK_IDENT) {
+            
+                skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+                
+                expect_semi_or_recover ();
+                return 1;
+            
+            }
+            
+            prefix_name = xstrdup (tok.ident);
+            prefix_start = tok.start;
+            prefix_caret = tok.caret;
+            prefix_line = get_line_number ();
+            
+            get_token ();
+            
+            while (tok.kind == TOK_RPAREN && parens > 0) {
+            
+                --parens;
+                get_token ();
+            
+            }
+            
+            if (parens != 0 || !is_assignment_operator (tok.kind)) {
+            
+                free (prefix_name);
+                
+                skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+                expect_semi_or_recover ();
+                
+                return 1;
+            
+            }
+            
+            prefix_assign_op = tok.kind;
+            get_token ();
+            
+            prefix_sym = find_local_symbol (prefix_name);
+            prefix_global_index = find_global_symbol (prefix_name);
+            
+            if (!prefix_sym && prefix_global_index < 0) {
+            
+                report_line_at (get_filename (), prefix_line, REPORT_ERROR, prefix_start, prefix_caret, "unknown symbol '%s'", prefix_name);
+                
+                skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+                expect_semi_or_recover ();
+                
+                free (prefix_name);
+                return 1;
+            
+            }
+            
+            if (prefix_sym) {
+            
+                if (prefix_sym->pointer_depth > 1) {
+                    prefix_deref_size = DATA_PTR & 0x1f;
+                } else if (prefix_sym->pointer_depth == 1 && prefix_sym->pointed_size > 0) {
+                    prefix_deref_size = prefix_sym->pointed_size & 0x1f;
+                }
+            
+            } else {
+            
+                if (get_global_symbol_pointer_depth (prefix_name) > 1) {
+                    prefix_deref_size = DATA_PTR & 0x1f;
+                } else if (get_global_symbol_pointer_depth (prefix_name) == 1 && get_global_symbol_pointed_size (prefix_name) > 0) {
+                    prefix_deref_size = get_global_symbol_pointed_size (prefix_name) & 0x1f;
+                }
+            
+            }
+            
+            if (prefix_deref_size == 0) {
+                prefix_deref_size = DATA_INT & 0x1f;
+            }
+            
+            emit_incdec_symbol_now (prefix_sym, prefix_name, prefix_op, prefix_line, prefix_start, prefix_caret);
+            
+            if (state->ofp) {
+            
+                if (prefix_sym) {
+                
+                    if (prefix_sym->is_static && prefix_sym->static_label) {
+                        emit_load_global_to_reg ("ecx", prefix_sym->static_label, DATA_PTR);
+                    } else {
+                        emit_load_local_to_reg ("ecx", prefix_sym->offset, DATA_PTR);
+                    }
+                
+                } else {
+                    emit_load_global_to_reg ("ecx", prefix_name, DATA_PTR);
+                }
+                
+                emit_push_reg_now ("ecx");
+                
+                if (prefix_assign_op == TOK_ASSIGN) {
+                    emit_load_assignment_rhs_expression_to_reg ("eax");
+                } else {
+                
+                    emit_pop_reg_now ("edx");
+                    emit_push_reg_now ("edx");
+                    
+                    emit_load_member_from_addr_reg_now ("eax", "edx", 0, prefix_deref_size);
+                    emit_push_reg_now ("eax");
+                    
+                    emit_load_assignment_rhs_expression_to_reg ("edx");
+                    emit_pop_reg_now ("eax");
+                    
+                    emit_assignment_binary_op (prefix_assign_op, 0);
+                
+                }
+                
+                emit_pop_reg_now ("edx");
+                emit_store_reg_to_deref_reg_now ("edx", "eax", prefix_deref_size);
+            
+            } else {
+                skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+            }
+            
+            expect_semi_or_recover ();
+            
+            free (prefix_name);
+            return 1;
+        
+        }
+        
+        if (tok.kind == TOK_IDENT) {
+        
+            name = xstrdup (tok.ident);
+            name_start = tok.start;
+            name_caret = tok.caret;
+            name_line = get_line_number ();
+            
+            get_token ();
+            
+            if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+            
+                postfix_op = tok.kind;
+                get_token ();
+            
+            }
+            
+            while (tok.kind == TOK_RPAREN && parens > 0) {
+            
+                --parens;
+                get_token ();
+            
+            }
+            
+            if (parens != 0 || !is_assignment_operator (tok.kind)) {
+            
+                free (name);
+                
+                skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+                expect_semi_or_recover ();
+                
+                return 1;
+            
+            }
+            
+            op = tok.kind;
+            get_token ();
+            
+            sym = find_local_symbol (name);
+            global_index = find_global_symbol (name);
+            
+            if (!sym && global_index < 0) {
+            
+                report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+                
+                skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+                expect_semi_or_recover ();
+                
+                free (name);
+                return 1;
+            
+            }
+            
+            if (sym) {
+            
+                if (sym->pointer_depth > 1) {
+                
+                    deref_size = DATA_PTR & 0x1f;
+                    step = DATA_PTR & 0x1f;
+                
+                } else if (sym->pointer_depth == 1) {
+                
+                    deref_size = sym->pointed_size & 0x1f;
+                    step = sym->pointed_size > 0 ? sym->pointed_size : 1;
+                
+                }
+            
+            } else {
+            
+                if (get_global_symbol_pointer_depth (name) > 1) {
+                
+                    deref_size = DATA_PTR & 0x1f;
+                    step = DATA_PTR & 0x1f;
+                
+                } else if (get_global_symbol_pointer_depth (name) == 1) {
+                
+                    deref_size = get_global_symbol_pointed_size (name) & 0x1f;
+                    step = get_global_symbol_pointed_size (name) > 0 ? get_global_symbol_pointed_size (name) : 1;
+                
+                }
+            
+            }
+            
+            if (deref_size == 0) {
+                deref_size = DATA_INT & 0x1f;
+            }
+            
+            if (state->ofp) {
+            
+                if (sym) {
+                
+                    if (sym->is_static && sym->static_label) {
+                        emit_load_global_to_reg ("ecx", sym->static_label, DATA_PTR);
+                    } else {
+                        emit_load_local_to_reg ("ecx", sym->offset, DATA_PTR);
+                    }
+                
+                } else {
+                    emit_load_global_to_reg ("ecx", name, DATA_PTR);
+                }
+                
+                emit_push_reg_now ("ecx");
+                
+                if (postfix_op == TOK_INCR || postfix_op == TOK_DECR) {
+                
+                    if (sym) {
+                    
+                        if (sym->is_static && sym->static_label) {
+                        
+                            if (state->syntax & ASM_SYNTAX_INTEL) {
+                                fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    %s dword [%s], %d\n" : "    %s dword ptr [%s], %d\n"), postfix_op == TOK_INCR ? "add" : "sub", sym->static_label, step);
+                            } else {
+                                fprintf (state->ofp, "    %sl $%d, %s\n", postfix_op == TOK_INCR ? "add" : "sub", step, sym->static_label);
+                            }
+                        
+                        } else {
+                        
+                            if (state->syntax & ASM_SYNTAX_INTEL) {
+                            
+                                char memref[64];
+                                
+                                format_intel_ebp_offset (memref, sizeof (memref), sym->offset);
+                                fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    %s dword %s, %d\n" : "    %s dword ptr %s, %d\n"), postfix_op == TOK_INCR ? "add" : "sub", memref, step);
+                                
+                            } else {
+                                fprintf (state->ofp, "    %sl $%d, %ld(%%ebp)\n", postfix_op == TOK_INCR ? "add" : "sub", step, sym->offset);
+                            }
+                        
+                        }
+                    
+                    } else {
+                    
+                        if (state->syntax & ASM_SYNTAX_INTEL) {
+                            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    %s dword [%s], %d\n" : "    %s dword ptr [%s], %d\n"), postfix_op == TOK_INCR ? "add" : "sub", name, step);
+                        } else {
+                            fprintf (state->ofp, "    %sl $%d, %s\n", postfix_op == TOK_INCR ? "add" : "sub", step, name);
+                        }
+                    
+                    }
+                
+                }
+                
+                if (op == TOK_ASSIGN) {
+                    emit_load_assignment_rhs_expression_to_reg ("eax");
+                } else {
+                
+                    emit_pop_reg_now ("edx");
+                    emit_push_reg_now ("edx");
+                    emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size);
+                    emit_push_reg_now ("eax");
+                    emit_load_assignment_rhs_expression_to_reg ("edx");
+                    emit_pop_reg_now ("eax");
+                    emit_assignment_binary_op (op, 0);
+                
+                }
+                
+                emit_pop_reg_now ("edx");
+                emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size);
+            
+            } else {
+                skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+            }
+            
+            expect_semi_or_recover ();
+            
+            free (name);
+            return 1;
+        
+        }
+        
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        
+        expect_semi_or_recover ();
+        return 1;
+    
+    }
+    
+    get_token ();
+    
+    while (tok.kind == TOK_LPAREN) {
+    
+        ++parens;
+        get_token ();
+    
+    }
+    
+    if (tok.kind != TOK_IDENT) {
+    
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        
+        expect_semi_or_recover ();
+        return 1;
+    
+    }
+    
+    name = xstrdup (tok.ident);
+    name_start = tok.start;
+    name_caret = tok.caret;
+    name_line = get_line_number ();
+    
+    get_token ();
+    
+    if (state->ofp) {
+    
+        if (tok.kind == TOK_LPAREN) {
+            emit_call_identifier_to_reg_now (name, "eax", name_start, name_caret, name_line);
+        } else {
+        
+            sym = find_local_symbol (name);
+            global_index = find_global_symbol (name);
+            
+            if (sym) {
+            
+                if (sym->is_static && sym->static_label) {
+                    emit_load_global_to_reg ("eax", sym->static_label, DATA_PTR);
+                } else {
+                    emit_load_local_to_reg ("eax", sym->offset, DATA_PTR);
+                }
+            
+            } else if (global_index >= 0) {
+                emit_load_global_to_reg ("eax", name, DATA_PTR);
+            } else {
+                report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+            }
+        
+        }
+        
+        emit_load_deref_reg_now ("eax", DATA_PTR);
+        
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+            fprintf (state->ofp, "    mov edx, eax\n");
+        } else {
+            fprintf (state->ofp, "    movl %%eax, %%edx\n");
+        }
+    
+    } else if (tok.kind == TOK_LPAREN) {
+        emit_call_identifier_to_reg_now (name, "eax", name_start, name_caret, name_line);
+    }
+    
+    while (tok.kind == TOK_RPAREN && parens > 0) {
+    
+        --parens;
+        get_token ();
+    
+    }
+    
+    if (tok.kind != TOK_ARROW && tok.kind != TOK_DOT) {
+    
+        if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+        
+            postfix_op = tok.kind;
+            get_token ();
+            
+            sym = find_local_symbol (name);
+            global_index = find_global_symbol (name);
+            
+            if (sym) {
+            
+                if (sym->pointer_depth > 1) {
+                
+                    deref_size = DATA_PTR & 0x1f;
+                    step = DATA_PTR & 0x1f;
+                
+                } else if (sym->pointer_depth == 1) {
+                
+                    deref_size = sym->pointed_size & 0x1f;
+                    step = 1;
+                
+                }
+                
+                if (deref_size == 0) {
+                    deref_size = DATA_INT & 0x1f;
+                }
+                
+                if (sym->is_static && sym->static_label) {
+                    emit_load_global_to_reg ("edx", sym->static_label, DATA_PTR);
+                } else {
+                    emit_load_local_to_reg ("edx", sym->offset, DATA_PTR);
+                }
+            
+            } else if (global_index >= 0) {
+            
+                if (get_global_symbol_pointer_depth (name) > 1) {
+                
+                    deref_size = DATA_PTR & 0x1f;
+                    step = DATA_PTR & 0x1f;
+                
+                } else if (get_global_symbol_pointer_depth (name) == 1) {
+                
+                    deref_size = get_global_symbol_pointed_size (name) & 0x1f;
+                    step = 1;
+                
+                }
+                
+                if (deref_size == 0) {
+                    deref_size = DATA_INT & 0x1f;
+                }
+                
+                emit_load_global_to_reg ("edx", name, DATA_PTR);
+            
+            } else {
+                report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+            }
+            
+            if (state->ofp) {
+            
+                emit_load_deref_reg_now ("edx", deref_size);
+
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                    fprintf (state->ofp, "    %s edx, %d\n", postfix_op == TOK_INCR ? "add" : "sub", step);
+                } else {
+                    fprintf (state->ofp, "    %sl $%d, %%edx\n", postfix_op == TOK_INCR ? "add" : "sub", step);
+                }
+                
+                if (sym) {
+                
+                    if (sym->is_static && sym->static_label) {
+                        emit_load_global_to_reg ("eax", sym->static_label, DATA_PTR);
+                    } else {
+                        emit_load_local_to_reg ("eax", sym->offset, DATA_PTR);
+                    }
+                
+                } else if (global_index >= 0) {
+                    emit_load_global_to_reg ("eax", name, DATA_PTR);
+                }
+                
+                emit_store_reg_to_deref_reg_now ("eax", "edx", deref_size);
+            
+            }
+            
+            expect_semi_or_recover ();
+            
+            free (name);
+            return 1;
+        
+        }
+        
+        free (name);
+        
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        expect_semi_or_recover ();
+        
+        return 1;
+    
+    }
+    
+    get_token ();
+    
+    member_start = tok.start;
+    member_caret = tok.caret;
+    member_line = get_line_number ();
+    
+    if (tok.kind != TOK_IDENT) {
+    
+        report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name");
+        free (name);
+        
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        
+        expect_semi_or_recover ();
+        return 1;
+    
+    }
+    
+    member = xstrdup (tok.ident);
+    get_token ();
+    
+    if (!find_member_info_ex (member, &offset, &member_size, &member_elem_size, &member_pointer_depth, 0, 0)) {
+    
+        report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+        
+        free (member);
+        free (name);
+        
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        expect_semi_or_recover ();
+        
+        return 1;
+    
+    }
+    
+    free (member);
+    
+    if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+    
+        postfix_op = tok.kind;
+        get_token ();
+    
+    }
+    
+    if (!is_assignment_operator (tok.kind)) {
+    
+        free (name);
+        
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        expect_semi_or_recover ();
+        
+        return 1;
+    
+    }
+    
+    op = tok.kind;
+    get_token ();
+    
+    if (member_pointer_depth > 1) {
+        deref_size = DATA_PTR & 0x1f;
+    } else if (member_pointer_depth == 1 && member_elem_size > 0) {
+        deref_size = member_elem_size & 0x1f;
+    } else if (member_size > 0) {
+        deref_size = member_size & 0x1f;
+    }
+    
+    if (deref_size == 0) {
+        deref_size = DATA_INT & 0x1f;
+    }
+    
+    step = member_elem_size > 0 ? member_elem_size : 1;
+    
+    if (state->ofp) {
+    
+        emit_load_member_from_addr_reg_now ("ecx", "edx", offset, DATA_PTR & 0x1f);
+        
+        if (postfix_op == TOK_INCR || postfix_op == TOK_DECR) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+            
+                if (state->syntax & ASM_SYNTAX_NASM) {
+                    fprintf (state->ofp, "    %s dword [edx + %d], %d\n", postfix_op == TOK_INCR ? "add" : "sub", offset, step);
+                } else {
+                    fprintf (state->ofp, "    %s dword ptr [edx + %d], %d\n", postfix_op == TOK_INCR ? "add" : "sub", offset, step);
+                }
+            
+            } else {
+                fprintf (state->ofp, "    %sl $%d, %d(%%edx)\n", postfix_op == TOK_INCR ? "add" : "sub", step, offset);
+            }
+        
+        }
+        
+        emit_push_reg_now ("ecx");
+        
+        if (op == TOK_ASSIGN) {
+            emit_load_assignment_rhs_expression_to_reg ("eax");
+        } else {
+        
+            emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size);
+            emit_push_reg_now ("eax");
+            emit_load_assignment_rhs_expression_to_reg ("edx");
+            emit_pop_reg_now ("eax");
+            emit_assignment_binary_op (op, 0);
+        
+        }
+        
+        emit_pop_reg_now ("edx");
+        emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size);
+    
+    } else {
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+    }
+    
+    expect_semi_or_recover ();
+    
+    free (name);
+    return 1;
+
+}
+
+static int paren_text_starts_type_name_now (void) {
+
+    const char *p;
+    
+    char name[128];
+    int n = 0;
+    
+    if (tok.caret) {
+        p = tok.caret;
+    } else if (tok.start) {
+        p = tok.start;
+    } else {
+        return 0;
+    }
+    
+    if (*p != '(') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p == '(') {
+    
+        p++;
+        
+        while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+            p++;
+        }
+    
+    }
+    
+    if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) {
+        return 0;
+    }
+    
+    while (((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') && n < (int) sizeof (name) - 1) {
+        name[n++] = *p++;
+    }
+    
+    name[n] = '\0';
+    
+    if (strcmp (name, "char") == 0 || strcmp (name, "short") == 0 ||
+        strcmp (name, "int") == 0 || strcmp (name, "long") == 0 ||
+        strcmp (name, "signed") == 0 || strcmp (name, "unsigned") == 0 ||
+        strcmp (name, "void") == 0 || strcmp (name, "struct") == 0 ||
+        strcmp (name, "union") == 0 || strcmp (name, "enum") == 0) {
+        return 1;
+    }
+    
+    return find_typedef_name (name) != 0;
+
+}
+
+static int parse_cast_indirect_assignment_statement (void) {
+
+    int saved_type_size = parsed_type_size;
+    int saved_storage_class = parsed_storage_class;
+    int saved_is_aggregate = parsed_type_is_aggregate;
+    int saved_is_void = parsed_type_is_void;
+    int saved_is_unsigned = parsed_type_is_unsigned;
+    int saved_is_floating = parsed_type_is_floating;
+    int saved_has_tag = parsed_type_has_tag;
+    int saved_is_inline = parsed_type_is_inline;
+    int saved_field_count = parsed_field_count;
+    int saved_fields[MAX_AGG_FIELDS];
+    int saved_declarator_is_pointer = declarator_is_pointer;
+    int saved_declarator_pointer_depth = declarator_pointer_depth;
+    int saved_declarator_has_array = declarator_has_array;
+    int saved_declarator_has_function = declarator_has_function;
+    int saved_declarator_array_unsized = declarator_array_unsized;
+    long saved_declarator_array_count = declarator_array_count;
+    
+    char *cast_name = 0;
+    int base_size;
+    int deref_size;
+    int pointer_depth;
+    int i;
+    
+    enum token_kind op;
+    int has_outer_paren = 0;
+    
+    if (tok.kind != TOK_LPAREN) {
+        return 0;
+    }
+    
+    get_token ();
+    
+    /*
+     * Accept the common casted-dereference lvalue spelling:
+     *
+     *     *((char *)ptr + n) = v;
+     *
+     * parse_indirect_assignment_statement() enters here at the outer '(';
+     * the actual cast type begins after the inner '('.
+     */
+    if (tok.kind == TOK_LPAREN) {
+    
+        has_outer_paren = 1;
+        get_token ();
+    
+    }
+    
+    if (!is_type_start (tok.kind)) {
+        return 0;
+    }
+    
+    for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) {
+        saved_fields[i] = parsed_field_sizes[i];
+    }
+    
+    parse_type_spec ();
+    base_size = parsed_type_size & 0x1f;
+    
+    if (tok.kind != TOK_RPAREN) {
+        parse_declarator (&cast_name);
+    }
+    
+    pointer_depth = declarator_is_pointer ? (declarator_pointer_depth > 0 ? declarator_pointer_depth : 1) : 0;
+    deref_size = pointer_depth > 1 ? (DATA_PTR & 0x1f) : base_size;
+    
+    if (deref_size <= 0) {
+        deref_size = DATA_INT & 0x1f;
+    }
+    
+    if (cast_name) {
+        free (cast_name);
+    }
+    
+    expect (TOK_RPAREN, ")");
+    
+    parsed_type_size = saved_type_size;
+    parsed_storage_class = saved_storage_class;
+    parsed_type_is_aggregate = saved_is_aggregate;
+    parsed_type_is_void = saved_is_void;
+    parsed_type_is_unsigned = saved_is_unsigned;
+    parsed_type_is_floating = saved_is_floating;
+    parsed_type_has_tag = saved_has_tag;
+    parsed_type_is_inline = saved_is_inline;
+    
+    clear_parsed_fields ();
+    
+    for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) {
+        parsed_field_sizes[i] = saved_fields[i];
+    }
+    
+    parsed_field_count = saved_field_count;
+    
+    declarator_is_pointer = saved_declarator_is_pointer;
+    declarator_pointer_depth = saved_declarator_pointer_depth;
+    declarator_has_array = saved_declarator_has_array;
+    declarator_has_function = saved_declarator_has_function;
+    declarator_array_unsized = saved_declarator_array_unsized;
+    declarator_array_count = saved_declarator_array_count;
+    
+    if (state->ofp) {
+    
+        /*
+         * Parse the address expression inside the outer dereference without
+         * allowing the first operand parser to consume the assignment operator.
+         *
+         * For:
+         *
+         *     *(size_t *)ptr = size;
+         *
+         * emit_load_assignment_rhs_to_reg() sees the identifier "ptr" followed
+         * by '=' and treats it as an assignment expression, generating
+         * "ptr = size" instead of using ptr as the destination address.  Load a
+         * plain identifier operand directly, then let the explicit '+'/'-' loop
+         * below handle the casted pointer arithmetic case:
+         *
+         *     *((char *)ptr + *actualRead) = '\n';
+         */
+        if (tok.kind == TOK_IDENT) {
+        
+            char *addr_name = xstrdup (tok.ident);
+            
+            const char *addr_start = tok.start;
+            const char *addr_caret = tok.caret;
+            
+            unsigned long addr_line = get_line_number ();
+            
+            struct local_symbol *addr_sym;
+            int addr_global_index;
+            
+            get_token ();
+            
+            addr_sym = find_local_symbol (addr_name);
+            addr_global_index = find_global_symbol (addr_name);
+            
+            if (addr_sym) {
+            
+                if (addr_sym->is_static && addr_sym->static_label) {
+                    emit_load_global_to_reg ("edx", addr_sym->static_label, DATA_PTR);
+                } else {
+                    emit_load_local_to_reg ("edx", addr_sym->offset, DATA_PTR);
+                }
+            
+            } else if (addr_global_index >= 0) {
+                emit_load_global_to_reg ("edx", addr_name, DATA_PTR);
+            } else {
+                report_line_at (get_filename (), addr_line, REPORT_ERROR, addr_start, addr_caret, "unknown symbol '%s'", addr_name);
+            }
+            
+            free (addr_name);
+        
+        } else {
+            emit_load_assignment_rhs_to_reg ("edx");
+        }
+        
+        while (tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) {
+        
+            enum token_kind addr_op = tok.kind;
+            get_token ();
+            
+            emit_push_reg_now ("edx");
+            emit_load_assignment_rhs_to_reg ("eax");
+            
+            if (deref_size > 1) {
+                emit_scale_reg_by_const_now ("eax", deref_size);
+            }
+            
+            emit_pop_reg_now ("edx");
+            
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    %s edx, eax\n", addr_op == TOK_PLUS ? "add" : "sub");
+            } else {
+                fprintf (state->ofp, "    %sl %%eax, %%edx\n", addr_op == TOK_PLUS ? "add" : "sub");
+            }
+        
+        }
+    
+    } else {
+        skip_balanced_until (TOK_RPAREN, TOK_SEMI, TOK_EOF);
+    }
+    
+    if (has_outer_paren) {
+        expect (TOK_RPAREN, ")");
+    }
+    
+    if (!is_assignment_operator (tok.kind)) {
+    
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        
+        expect_semi_or_recover ();
+        return 1;
+    
+    }
+    
+    op = tok.kind;
+    get_token ();
+    
+    if (op == TOK_ASSIGN) {
+    
+        emit_push_reg_now ("edx");
+        emit_load_assignment_rhs_expression_to_reg ("eax");
+        emit_pop_reg_now ("edx");
+    
+    } else {
+    
+        emit_push_reg_now ("edx");
+        emit_load_deref_reg_now ("eax", deref_size);
+        emit_push_reg_now ("eax");
+        emit_load_assignment_rhs_expression_to_reg ("edx");
+        emit_pop_reg_now ("eax");
+        emit_assignment_binary_op (op, 0);
+        emit_pop_reg_now ("edx");
+    
+    }
+    
+    emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size);
+    expect_semi_or_recover ();
+    
+    return 1;
+
+}
+
+static int parse_indirect_assignment_statement (void) {
+
+    char *name;
+    
+    const char *name_start;
+    const char *name_caret;
+    
+    unsigned long name_line;
+    struct local_symbol *lhs;
+    
+    int global_index;
+    int deref_size = DATA_INT & 0x1f;
+    
+    enum token_kind op;
+    
+    if (tok.kind != TOK_STAR) {
+        return 0;
+    }
+    
+    if (emit_store_to_deref_parenthesized_deref_postfix_incdec_now ("eax")) {
+    
+        expect_semi_or_recover ();
+        return 1;
+    
+    }
+    
+    get_token ();
+    
+    if (tok.kind == TOK_STAR) {
+    
+        get_token ();
+        
+        if (tok.kind == TOK_IDENT) {
+        
+            name = xstrdup (tok.ident);
+            
+            name_start = tok.start;
+            name_caret = tok.caret;
+            
+            name_line = get_line_number ();
+            get_token ();
+            
+            if (is_assignment_operator (tok.kind)) {
+            
+                op = tok.kind;
+                get_token ();
+                
+                lhs = find_local_symbol (name);
+                global_index = find_global_symbol (name);
+                
+                if (!lhs && global_index < 0) {
+                
+                    report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+                    
+                    skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+                    expect_semi_or_recover ();
+                    
+                    free (name);
+                    return 1;
+                
+                }
+                
+                if (lhs) {
+                
+                    if (lhs->pointer_depth > 2) {
+                        deref_size = DATA_PTR & 0x1f;
+                    } else if (lhs->pointer_depth == 2) {
+                        deref_size = lhs->pointed_size & 0x1f;
+                    }
+                
+                } else {
+                
+                    if (get_global_symbol_pointer_depth (name) > 2) {
+                        deref_size = DATA_PTR & 0x1f;
+                    } else if (get_global_symbol_pointer_depth (name) == 2) {
+                        deref_size = get_global_symbol_pointed_size (name) & 0x1f;
+                    }
+                
+                }
+                
+                if (deref_size <= 0) {
+                    deref_size = DATA_INT & 0x1f;
+                }
+                
+                if (state->ofp) {
+                
+                    if (lhs) {
+                    
+                        if (lhs->is_static && lhs->static_label) {
+                            emit_load_global_to_reg ("ecx", lhs->static_label, DATA_PTR);
+                        } else {
+                            emit_load_local_to_reg ("ecx", lhs->offset, DATA_PTR);
+                        }
+                    
+                    } else {
+                        emit_load_global_to_reg ("ecx", name, DATA_PTR);
+                    }
+                    
+                    emit_load_deref_reg_now ("ecx", DATA_PTR & 0x1f);
+                    
+                    if (op == TOK_ASSIGN) {
+                    
+                        emit_push_reg_now ("ecx");
+                        
+                        emit_load_assignment_rhs_expression_to_reg ("eax");
+                        emit_pop_reg_now ("ecx");
+                    
+                    } else {
+                    
+                        emit_push_reg_now ("ecx");
+                        
+                        emit_load_member_from_addr_reg_now ("eax", "ecx", 0, deref_size);
+                        emit_push_reg_now ("eax");
+                        
+                        emit_load_assignment_rhs_expression_to_reg ("edx");
+                        emit_pop_reg_now ("eax");
+                        
+                        emit_assignment_binary_op (op, 0);
+                        emit_pop_reg_now ("ecx");
+                    
+                    }
+                    
+                    emit_store_reg_to_deref_reg_now ("ecx", "eax", deref_size);
+                
+                } else {
+                    skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+                }
+                
+                expect_semi_or_recover ();
+                
+                free (name);
+                return 1;
+            
+            }
+            
+            free (name);
+        
+        }
+        
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        
+        expect_semi_or_recover ();
+        return 1;
+    
+    }
+    
+    if (tok.kind != TOK_IDENT) {
+    
+        if (tok.kind == TOK_LPAREN) {
+        
+            if (paren_text_starts_type_name_now () ||
+                (tok.start && tok.start[0] == '(' && tok.start[1] == '(')) {
+                return parse_cast_indirect_assignment_statement ();
+            }
+            
+            return parse_parenthesized_pointer_member_indirect_assignment_statement ();
+        
+        }
+        
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        expect_semi_or_recover ();
+        
+        return 1;
+    
+    }
+    
+    name = xstrdup (tok.ident);
+    name_start = tok.start;
+    name_caret = tok.caret;
+    name_line = get_line_number ();
+    
+    get_token ();
+    
+    if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+    
+        enum token_kind postfix_op = tok.kind;
+        int step = 1;
+        
+        get_token ();
+        
+        if (!is_assignment_operator (tok.kind)) {
+        
+            free (name);
+            
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+            expect_semi_or_recover ();
+            
+            return 1;
+        
+        }
+        
+        op = tok.kind;
+        get_token ();
+        
+        lhs = find_local_symbol (name);
+        global_index = find_global_symbol (name);
+        
+        if (!lhs && global_index < 0) {
+        
+            report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+            
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+            expect_semi_or_recover ();
+            
+            free (name);
+            return 1;
+        
+        }
+        
+        if (lhs) {
+        
+            if (lhs->pointer_depth > 1) {
+            
+                deref_size = DATA_PTR & 0x1f;
+                step = DATA_PTR & 0x1f;
+            
+            } else if (lhs->pointer_depth == 1) {
+            
+                deref_size = lhs->pointed_size & 0x1f;
+                step = lhs->pointed_size > 0 ? lhs->pointed_size : 1;
+            
+            }
+        
+        } else {
+            
+            if (get_global_symbol_pointer_depth (name) > 1) {
+            
+                deref_size = DATA_PTR & 0x1f;
+                step = DATA_PTR & 0x1f;
+            
+            } else if (get_global_symbol_pointer_depth (name) == 1) {
+            
+                deref_size = get_global_symbol_pointed_size (name) & 0x1f;
+                step = get_global_symbol_pointed_size (name) > 0 ? get_global_symbol_pointed_size (name) : 1;
+            
+            }
+        
+        }
+        
+        if (deref_size == 0) {
+            deref_size = DATA_INT & 0x1f;
+        }
+        
+        if (state->ofp) {
+        
+            if (lhs) {
+            
+                if (lhs->is_static && lhs->static_label) {
+                    emit_load_global_to_reg ("ecx", lhs->static_label, DATA_PTR);
+                } else {
+                    emit_load_local_to_reg ("ecx", lhs->offset, DATA_PTR);
+                }
+            
+            } else {
+                emit_load_global_to_reg ("ecx", name, DATA_PTR);
+            }
+            
+            emit_push_reg_now ("ecx");
+            
+            if (lhs) {
+            
+                if (lhs->is_static && lhs->static_label) {
+                
+                    if (state->syntax & ASM_SYNTAX_INTEL) {
+                        fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    %s dword [%s], %d\n" : "    %s dword ptr [%s], %d\n"), postfix_op == TOK_INCR ? "add" : "sub", lhs->static_label, step);
+                    } else {
+                        fprintf (state->ofp, "    %sl $%d, %s\n", postfix_op == TOK_INCR ? "add" : "sub", step, lhs->static_label);
+                    }
+                
+                } else {
+                
+                    if (state->syntax & ASM_SYNTAX_INTEL) {
+                    
+                        char memref[64];
+                        
+                        format_intel_ebp_offset (memref, sizeof (memref), lhs->offset);
+                        fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    %s dword %s, %d\n" : "    %s dword ptr %s, %d\n"), postfix_op == TOK_INCR ? "add" : "sub", memref, step);
+                    
+                    } else {
+                        fprintf (state->ofp, "    %sl $%d, %ld(%%ebp)\n", postfix_op == TOK_INCR ? "add" : "sub", step, lhs->offset);
+                    }
+                
+                }
+            
+            } else {
+                
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                    fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    %s dword [%s], %d\n" : "    %s dword ptr [%s], %d\n"), postfix_op == TOK_INCR ? "add" : "sub", name, step);
+                } else {
+                    fprintf (state->ofp, "    %sl $%d, %s\n", postfix_op == TOK_INCR ? "add" : "sub", step, name);
+                }
+            
+            }
+            
+            if (op == TOK_ASSIGN) {
+                emit_load_assignment_rhs_expression_to_reg ("eax");
+            } else {
+            
+                emit_pop_reg_now ("edx");
+                emit_push_reg_now ("edx");
+                emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size);
+                emit_push_reg_now ("eax");
+                emit_load_assignment_rhs_expression_to_reg ("edx");
+                emit_pop_reg_now ("eax");
+                emit_assignment_binary_op (op, 0);
+            
+            }
+            
+            emit_pop_reg_now ("edx");
+            emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size);
+        
+        } else {
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        }
+        
+        expect_semi_or_recover ();
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+    
+        enum token_kind member_op = tok.kind;
+        enum token_kind postfix_op = TOK_EOF;
+        
+        char *member;
+        
+        const char *member_start;
+        const char *member_caret;
+        
+        unsigned long member_line;
+        
+        int offset = 0;
+        int member_size = DATA_PTR & 0x1f;
+        int member_elem_size = DATA_INT & 0x1f;
+        int member_pointer_depth = 0;
+        int step;
+        
+        get_token ();
+        
+        member_start = tok.start;
+        member_caret = tok.caret;
+        member_line = get_line_number ();
+        
+        if (tok.kind != TOK_IDENT) {
+        
+            report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+            
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+            expect_semi_or_recover ();
+            
+            free (name);
+            return 1;
+        
+        }
+        
+        member = xstrdup (tok.ident);
+        get_token ();
+        
+        if (!find_member_info_ex (member, &offset, &member_size, &member_elem_size, &member_pointer_depth, 0, 0)) {
+        
+            report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+            
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+            expect_semi_or_recover ();
+            
+            free (member);
+            free (name);
+            
+            return 1;
+        
+        }
+        
+        free (member);
+        
+        if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+        
+            postfix_op = tok.kind;
+            get_token ();
+        
+        }
+        
+        if (!is_assignment_operator (tok.kind)) {
+        
+            free (name);
+            
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+            expect_semi_or_recover ();
+            
+            return 1;
+        
+        }
+        
+        op = tok.kind;
+        get_token ();
+        
+        lhs = find_local_symbol (name);
+        global_index = find_global_symbol (name);
+        
+        if (!lhs && global_index < 0) {
+        
+            report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+            
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+            expect_semi_or_recover ();
+            
+            free (name);
+            return 1;
+        
+        }
+        
+        if (member_pointer_depth > 1) {
+            deref_size = DATA_PTR & 0x1f;
+        } else if (member_pointer_depth == 1 && member_elem_size > 0) {
+            deref_size = member_elem_size & 0x1f;
+        }
+        
+        if (deref_size == 0) {
+            deref_size = DATA_INT & 0x1f;
+        }
+        
+        step = member_elem_size > 0 ? member_elem_size : 1;
+        
+        if (state->ofp) {
+        
+            if (lhs) {
+            
+                if (member_op == TOK_ARROW) {
+                
+                    if (lhs->is_static && lhs->static_label) {
+                        emit_load_global_to_reg ("edx", lhs->static_label, DATA_PTR);
+                    } else {
+                        emit_load_local_to_reg ("edx", lhs->offset, DATA_PTR);
+                    }
+                
+                } else {
+                
+                    if (lhs->is_static && lhs->static_label) {
+                        emit_load_address_to_reg_now ("edx", lhs->static_label);
+                    } else {
+                        emit_load_local_address_to_reg_now ("edx", lhs->offset);
+                    }
+                
+                }
+            
+            } else {
+            
+                if (member_op == TOK_ARROW) {
+                    emit_load_global_to_reg ("edx", name, DATA_PTR);
+                } else {
+                    emit_load_address_to_reg_now ("edx", name);
+                }
+            
+            }
+            
+            emit_load_member_from_addr_reg_now ("ecx", "edx", offset, DATA_PTR & 0x1f);
+            
+            if (postfix_op == TOK_INCR || postfix_op == TOK_DECR) {
+            
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                
+                    if (state->syntax & ASM_SYNTAX_NASM) {
+                        fprintf (state->ofp, "    %s dword [edx + %d], %d\n", postfix_op == TOK_INCR ? "add" : "sub", offset, step);
+                    } else {
+                        fprintf (state->ofp, "    %s dword ptr [edx + %d], %d\n", postfix_op == TOK_INCR ? "add" : "sub", offset, step);
+                    }
+                
+                } else {
+                    fprintf (state->ofp, "    %sl $%d, %d(%%edx)\n", postfix_op == TOK_INCR ? "add" : "sub", step, offset);
+                }
+            
+            }
+            
+            emit_push_reg_now ("ecx");
+            
+            if (op == TOK_ASSIGN) {
+                emit_load_assignment_rhs_expression_to_reg ("eax");
+            } else {
+            
+                emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size);
+                emit_push_reg_now ("eax");
+                emit_load_assignment_rhs_expression_to_reg ("edx");
+                emit_pop_reg_now ("eax");
+                emit_assignment_binary_op (op, 0);
+            
+            }
+            
+            emit_pop_reg_now ("edx");
+            emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size);
+        
+        } else {
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        }
+        
+        expect_semi_or_recover ();
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    if (!is_assignment_operator (tok.kind)) {
+    
+        free (name);
+        
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        expect_semi_or_recover ();
+        
+        return 1;
+    
+    }
+    
+    op = tok.kind;
+    get_token ();
+    
+    lhs = find_local_symbol (name);
+    global_index = find_global_symbol (name);
+    
+    if (!lhs && global_index < 0) {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+        
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        expect_semi_or_recover ();
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    if (lhs) {
+    
+        if (lhs->pointer_depth > 1) {
+            deref_size = DATA_PTR & 0x1f;
+        } else if (lhs->pointer_depth == 1) {
+        
+            /**
+             * Keep aggregate pointed-to sizes intact for assignments like
+             * *hashtab = old_hashtab;  Masking with 0x1f corrupts struct
+             * copies whose size is greater than 31 bytes.
+             */
+            deref_size = lhs->pointed_size;
+        
+        }
+    
+    } else {
+    
+        if (get_global_symbol_pointer_depth (name) > 1) {
+            deref_size = DATA_PTR & 0x1f;
+        } else if (get_global_symbol_pointer_depth (name) == 1) {
+            deref_size = get_global_symbol_pointed_size (name);
+        }
+    
+    }
+    
+    if (deref_size == 0) {
+        deref_size = DATA_INT & 0x1f;
+    }
+    
+    if (state->ofp) {
+    
+        if (lhs) {
+        
+            if (lhs->is_static && lhs->static_label) {
+                emit_load_global_to_reg ("edx", lhs->static_label, DATA_PTR);
+            } else {
+                emit_load_local_to_reg ("edx", lhs->offset, DATA_PTR);
+            }
+        
+        } else {
+            emit_load_global_to_reg ("edx", name, DATA_PTR);
+        }
+        
+        if (op == TOK_ASSIGN) {
+        
+            if (deref_size != (DATA_LLONG & 0x1f) &&
+                emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("edx", 0, deref_size)) {
+            
+                expect_semi_or_recover ();
+                
+                free (name);
+                return 1;
+            
+            }
+            
+            emit_push_reg_now ("edx");
+            
+            if (deref_size == (DATA_LLONG & 0x1f)) {
+            
+                emit_load_assignment_rhs_expression_to_pair ("eax", "edx", 1);
+                emit_pop_reg_now ("ecx");
+            
+            } else {
+            
+                emit_load_assignment_rhs_expression_to_reg ("eax");
+                emit_pop_reg_now ("edx");
+            
+            }
+        
+        } else {
+        
+            emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size);
+            
+            emit_push_reg_now ("edx");
+            emit_push_reg_now ("eax");
+            
+            emit_load_assignment_rhs_expression_to_reg ("edx");
+            emit_pop_reg_now ("eax");
+            
+            emit_assignment_binary_op (op, 0);
+            emit_pop_reg_now ("edx");
+        
+        }
+        
+        if (deref_size == (DATA_LLONG & 0x1f)) {
+            emit_store_pair_to_deref_reg_now ("ecx", "eax", "edx");
+        } else {
+            emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size);
+        }
+    
+    } else {
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+    }
+    
+    expect_semi_or_recover ();
+    
+    free (name);
+    return 1;
+
+}
+
+static int source_starts_parenthesized_star_now (void) {
+
+    const char *p = tok.caret ? tok.caret : tok.start;
+    
+    if (!p) {
+        return 0;
+    }
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    if (*p != '(') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    return *p == '*';
+
+}
+
+static int parse_parenthesized_indirect_member_assignment_statement (void) {
+
+    enum token_kind member_op;
+    enum token_kind op;
+    
+    char *member;
+    
+    const char *member_start;
+    const char *member_caret;
+    
+    unsigned long member_line;
+    
+    int member_offset = 0;
+    int member_size = DATA_INT & 0x1f;
+    int saw_close = 0;
+    
+    if (tok.kind != TOK_LPAREN || !source_starts_parenthesized_star_now ()) {
+        return 0;
+    }
+    
+    get_token ();
+    
+    if (tok.kind != TOK_STAR) {
+        return 0;
+    }
+    
+    get_token ();
+    
+    /*
+     * Parse only the object expression inside the parenthesized dereference.
+     * For macro-expanded lvalues such as
+     *
+     *     (*(__gtin()))->field = value;
+     *
+     * the normal assignment-expression loader can consume the complete
+     * "->field = value" tail as a value expression.  That emits only
+     * member loads and drops the store.
+     */
+    if (tok.kind == TOK_LPAREN) {
+    
+        int parens = 0;
+        char *inner_name = 0;
+        
+        const char *inner_start = 0;
+        const char *inner_caret = 0;
+        
+        unsigned long inner_line = 0;
+        struct local_symbol *inner_lhs;
+        
+        while (tok.kind == TOK_LPAREN) {
+        
+            parens++;
+            get_token ();
+        
+        }
+        
+        if (tok.kind == TOK_IDENT) {
+        
+            inner_name = xstrdup (tok.ident);
+            inner_start = tok.start;
+            inner_caret = tok.caret;
+            inner_line = get_line_number ();
+            
+            get_token ();
+            
+            if (tok.kind == TOK_LPAREN) {
+                emit_call_identifier_to_reg_now (inner_name, "edx", inner_start, inner_caret, inner_line);
+            } else {
+            
+                inner_lhs = find_local_symbol (inner_name);
+                
+                if (inner_lhs) {
+                
+                    if (inner_lhs->is_static && inner_lhs->static_label) {
+                        emit_load_global_to_reg ("edx", inner_lhs->static_label, DATA_PTR);
+                    } else {
+                        emit_load_local_to_reg ("edx", inner_lhs->offset, DATA_PTR);
+                    }
+                
+                } else {
+                    emit_load_global_to_reg ("edx", inner_name, DATA_PTR);
+                }
+            
+            }
+            
+            free (inner_name);
+            
+            while (parens > 0 && tok.kind == TOK_RPAREN) {
+            
+                saw_close = 1;
+                parens--;
+                
+                get_token ();
+            
+            }
+        
+        } else {
+            emit_load_assignment_rhs_expression_to_reg ("edx");
+        }
+    
+    } else {
+        emit_load_assignment_rhs_expression_to_reg ("edx");
+    }
+    
+    while (tok.kind == TOK_RPAREN) {
+    
+        saw_close = 1;
+        get_token ();
+    
+    }
+    
+    if (!saw_close) {
+    
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        expect_semi_or_recover ();
+        
+        return 1;
+    
+    }
+    
+    if (tok.kind != TOK_ARROW && tok.kind != TOK_DOT) {
+    
+        int deref_size = DATA_INT & 0x1f;
+        int step = 1;
+        
+        if (rhs_last_pointer_depth > 1) {
+        
+            deref_size = DATA_PTR & 0x1f;
+            step = DATA_PTR & 0x1f;
+        
+        } else if (rhs_last_pointer_depth == 1 && rhs_last_pointed_size > 0) {
+            deref_size = rhs_last_pointed_size & 0x1f;
+        }
+        
+        if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+        
+            op = tok.kind;
+            get_token ();
+            
+            if (state->ofp) {
+            
+                emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size);
+                
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                    fprintf (state->ofp, "    %s eax, %d\n", op == TOK_INCR ? "add" : "sub", step);
+                } else {
+                    fprintf (state->ofp, "    %sl $%d, %%eax\n", op == TOK_INCR ? "add" : "sub", step);
+                }
+                
+                emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size);
+            
+            }
+            
+            expect_semi_or_recover ();
+            return 1;
+        
+        }
+        
+        if (!is_assignment_operator (tok.kind)) {
+        
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+            expect_semi_or_recover ();
+            
+            return 1;
+        
+        }
+        
+        op = tok.kind;
+        get_token ();
+        
+        if (state->ofp) {
+        
+            if (op == TOK_ASSIGN) {
+            
+                emit_push_reg_now ("edx");
+                emit_load_assignment_rhs_expression_to_reg ("eax");
+                emit_pop_reg_now ("edx");
+            
+            } else {
+            
+                emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size);
+                emit_push_reg_now ("edx");
+                emit_push_reg_now ("eax");
+                emit_load_assignment_rhs_expression_to_reg ("edx");
+                emit_pop_reg_now ("eax");
+                emit_assignment_binary_op (op, 0);
+                emit_pop_reg_now ("edx");
+            
+            }
+            
+            emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size);
+        
+        } else {
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        }
+        
+        expect_semi_or_recover ();
+        return 1;
+    
+    }
+    
+    if (state->ofp) {
+        emit_load_deref_reg_now ("edx", DATA_PTR);
+    }
+    
+    member_op = tok.kind;
+    get_token ();
+    
+    member_start = tok.start;
+    member_caret = tok.caret;
+    member_line = get_line_number ();
+    
+    if (tok.kind != TOK_IDENT) {
+    
+        report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+        
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        expect_semi_or_recover ();
+        
+        return 1;
+    
+    }
+    
+    member = xstrdup (tok.ident);
+    get_token ();
+    
+    if (!find_member_info (member, &member_offset, &member_size)) {
+    
+        report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+        free (member);
+        
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        expect_semi_or_recover ();
+        
+        return 1;
+    
+    }
+    
+    free (member);
+    
+    if (!is_assignment_operator (tok.kind)) {
+    
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        expect_semi_or_recover ();
+        
+        return 1;
+    
+    }
+    
+    op = tok.kind;
+    get_token ();
+    
+    if (state->ofp) {
+    
+        if (op == TOK_ASSIGN) {
+        
+            emit_push_reg_now ("edx");
+            emit_load_assignment_rhs_expression_to_reg ("eax");
+            emit_pop_reg_now ("edx");
+        
+        } else {
+        
+            emit_load_member_from_addr_reg_now ("eax", "edx", member_offset, member_size);
+            emit_push_reg_now ("edx");
+            emit_push_reg_now ("eax");
+            emit_load_assignment_rhs_expression_to_reg ("edx");
+            emit_pop_reg_now ("eax");
+            emit_assignment_binary_op (op, 0);
+            emit_pop_reg_now ("edx");
+        
+        }
+        
+        emit_store_member_to_addr_reg_now ("edx", member_offset, "eax", member_size);
+    
+    } else {
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+    }
+    
+    expect_semi_or_recover ();
+    return 1;
+
+}
+
+static int token_text_looks_like_postfix_call_now (void) {
+
+    const char *p;
+    
+    int saw_postfix;
+    int paren_depth;
+    int bracket_depth;
+    
+    if (tok.caret) {
+        p = tok.caret;
+    } else if (tok.start) {
+        p = tok.start;
+    } else {
+        return 0;
+    }
+    
+    saw_postfix = 0;
+    paren_depth = 0;
+    bracket_depth = 0;
+    
+    while (*p && *p != ';' && *p != '\n') {
+    
+        if (*p == '(') {
+        
+            if (saw_postfix && paren_depth == 0 && bracket_depth == 0) {
+                return 1;
+            }
+            
+            paren_depth++;
+        
+        } else if (*p == ')') {
+        
+            if (paren_depth > 0) {
+                paren_depth--;
+            }
+        
+        } else if (*p == '[') {
+            bracket_depth++;
+        } else if (*p == ']') {
+        
+            if (bracket_depth > 0) {
+                bracket_depth--;
+            }
+        
+        } else if (paren_depth == 0 && bracket_depth == 0 && *p == '.') {
+            saw_postfix = 1;
+        } else if (paren_depth == 0 && bracket_depth == 0 && *p == '-' && p[1] == '>') {
+        
+            saw_postfix = 1;
+            p++;
+        
+        } else if (paren_depth == 0 && bracket_depth == 0 && *p == '=') {
+            return 0;
+        }
+        
+        p++;
+    
+    }
+    
+    return 0;
+
+}
+
+static int parse_parenthesized_deref_subscript_statement (void) {
+
+    char *name;
+    
+    const char *name_start;
+    const char *name_caret;
+    
+    unsigned long name_line;
+    struct local_symbol *src;
+    
+    int global_index;
+    int pointer_depth;
+    int pointed_size;
+    
+    if (tok.kind != TOK_LPAREN || !source_starts_lparen_deref_subscript_at (tok.caret)) {
+        return 0;
+    }
+    
+    get_token ();
+    
+    if (tok.kind != TOK_STAR) {
+        return 0;
+    }
+    
+    get_token ();
+    
+    name_start = tok.start;
+    name_caret = tok.caret;
+    name_line = get_line_number ();
+    
+    if (tok.kind != TOK_IDENT || !tok.ident) {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *");
+        return 1;
+    
+    }
+    
+    name = xstrdup (tok.ident);
+    get_token ();
+    
+    expect (TOK_RPAREN, ")");
+    
+    src = find_local_symbol (name);
+    global_index = find_global_symbol (name);
+    
+    pointer_depth = 0;
+    pointed_size = DATA_INT & 0x1f;
+    
+    if (src) {
+    
+        pointer_depth = src->pointer_depth;
+        pointed_size = src->pointed_size;
+        
+        if (src->is_static && src->static_label) {
+            emit_load_global_to_reg ("eax", src->static_label, DATA_PTR);
+        } else {
+            emit_load_local_to_reg ("eax", src->offset, DATA_PTR);
+        }
+    
+    } else if (global_index >= 0) {
+    
+        pointer_depth = get_global_symbol_pointer_depth (name);
+        pointed_size = get_global_symbol_pointed_size (name);
+        
+        emit_load_global_to_reg ("eax", name, DATA_PTR);
+    
+    } else {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    if (pointer_depth <= 0) {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "invalid indirection of non-pointer '%s'", name);
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    emit_load_deref_reg_now ("eax", DATA_PTR & 0x1f);
+    emit_handle_subscript_after_loaded_pointer_to_reg_now ("eax", pointer_depth - 1, pointed_size);
+    
+    free (name);
+    return 1;
+
+}
+
+static int parse_identifier_assignment_statement (void) {
+
+    char *name;
+    int global_index;
+    
+    const char *name_start, *name_caret;
+    unsigned long name_line;
+    
+    enum token_kind op;
+    struct local_symbol *lhs;
+    
+    int lhs_size;
+    int lhs_is_floating;
+    
+    if (tok.kind != TOK_IDENT) {
+        return 0;
+    }
+    
+    if (token_text_looks_like_postfix_call_now ()) {
+        return 0;
+    }
+    
+    name = xstrdup (tok.ident);
+    name_start = tok.start;
+    name_caret = tok.caret;
+    name_line = get_line_number ();
+    
+    get_token ();
+    
+    if (tok.kind == TOK_COLON) {
+    
+        define_goto_label (name, name_line, name_start, name_caret);
+        
+        get_token ();
+        free (name);
+        
+        parse_statement ();
+        return 1;
+    
+    }
+    
+    if (tok.kind == TOK_DOT || tok.kind == TOK_ARROW) {
+    
+        enum token_kind member_op = tok.kind;
+        char *member;
+        
+        const char *member_start;
+        const char *member_caret;
+        
+        unsigned long member_line;
+        
+        int member_offset = 0;
+        int member_size = DATA_INT & 0x1f;
+        int member_elem_size = DATA_INT & 0x1f;
+        int member_pointer_depth = 0;
+        int member_is_floating = 0;
+        
+        get_token ();
+        
+        member_start = tok.start;
+        member_caret = tok.caret;
+        member_line = get_line_number ();
+        
+        if (tok.kind != TOK_IDENT) {
+        
+            report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+            
+            free (name);
+            return 1;
+        
+        }
+        
+        member = xstrdup (tok.ident);
+        get_token ();
+        
+        lhs = find_local_symbol (name);
+        global_index = find_global_symbol (name);
+        
+        if (!find_member_info_ex_bounded (member,
+                member_op == TOK_DOT
+                    ? (lhs ? lhs->size : (global_index >= 0 ? get_global_symbol_size (name) : 0))
+                    : (lhs ? lhs->pointed_size : (global_index >= 0 ? get_global_symbol_pointed_size (name) : 0)),
+                member_op == TOK_DOT
+                    ? (lhs ? lhs->tag_name : (global_index >= 0 ? get_global_symbol_tag_name (name) : 0))
+                    : (lhs ? lhs->pointed_tag_name : 0),
+                &member_offset, &member_size, &member_elem_size, &member_pointer_depth, 0, &member_is_floating)) {
+        
+            report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+            
+            free (member);
+            free (name);
+            
+            return 1;
+        
+        }
+        
+        free (member);
+        
+        if (!is_assignment_operator (tok.kind)) {
+        
+            if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+            
+                enum token_kind postfix_op = tok.kind;
+                int step = member_pointer_depth > 0 && member_elem_size > 0 ? member_elem_size : 1;
+                
+                get_token ();
+                
+                lhs = find_local_symbol (name);
+                global_index = find_global_symbol (name);
+                
+                if (!lhs && global_index < 0) {
+                
+                    report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+                    
+                    skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+                    expect (TOK_SEMI, ";");
+                    
+                    free (name);
+                    return 1;
+                
+                }
+                
+                if (state->ofp) {
+                
+                    if (member_op == TOK_ARROW) {
+                    
+                        if (lhs) {
+                        
+                            if (lhs->is_static && lhs->static_label) {
+                                emit_load_global_to_reg ("edx", lhs->static_label, DATA_PTR);
+                            } else {
+                                emit_load_local_to_reg ("edx", lhs->offset, DATA_PTR);
+                            }
+                        
+                        } else {
+                            emit_load_global_to_reg ("edx", name, DATA_PTR);
+                        }
+                    
+                    } else {
+                    
+                        if (lhs) {
+                        
+                            if (lhs->is_static && lhs->static_label) {
+                                emit_load_address_to_reg_now ("edx", lhs->static_label);
+                            } else {
+                                emit_load_local_address_to_reg_now ("edx", lhs->offset);
+                            }
+                        
+                        } else {
+                            emit_load_address_to_reg_now ("edx", name);
+                        }
+                    
+                    }
+                    
+                    if (state->syntax & ASM_SYNTAX_INTEL) {
+                    
+                        const char *opname = postfix_op == TOK_INCR ? "add" : "sub";
+                        const char *opsize = member_size == 1 ? "byte" : (member_size == 2 ? "word" : "dword");
+                        
+                        if (state->syntax & ASM_SYNTAX_NASM) {
+                            fprintf (state->ofp, "    %s %s [edx + %d], %d\n", opname, opsize, member_offset, step);
+                        } else {
+                            fprintf (state->ofp, "    %s %s ptr [edx + %d], %d\n", opname, opsize, member_offset, step);
+                        }
+                    
+                    } else {
+                    
+                        const char *suffix = member_size == 1 ? "b" : (member_size == 2 ? "w" : "l");
+                        fprintf (state->ofp, "    %s%s $%d, %d(%%edx)\n", postfix_op == TOK_INCR ? "add" : "sub", suffix, step, member_offset);
+                    
+                    }
+                
+                }
+                
+                expect_semi_or_recover ();
+                
+                free (name);
+                return 1;
+            
+            }
+            
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+            expect (TOK_SEMI, ";");
+            
+            free (name);
+            return 1;
+        
+        }
+        
+        op = tok.kind;
+        get_token ();
+        
+        if (!lhs && global_index < 0) {
+        
+            report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+            
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+            expect (TOK_SEMI, ";");
+            
+            free (name);
+            return 1;
+        
+        }
+        
+        if (state->ofp) {
+        
+            if (member_op == TOK_ARROW) {
+            
+                if (lhs) {
+                
+                    if (lhs->is_static && lhs->static_label) {
+                        emit_load_global_to_reg ("edx", lhs->static_label, DATA_PTR);
+                    } else {
+                        emit_load_local_to_reg ("edx", lhs->offset, DATA_PTR);
+                    }
+                
+                } else {
+                    emit_load_global_to_reg ("edx", name, DATA_PTR);
+                }
+            
+            } else {
+            
+                if (lhs) {
+                
+                    if (lhs->is_static && lhs->static_label) {
+                        emit_load_address_to_reg_now ("edx", lhs->static_label);
+                    } else {
+                        emit_load_local_address_to_reg_now ("edx", lhs->offset);
+                    }
+                
+                } else {
+                    emit_load_address_to_reg_now ("edx", name);
+                }
+            
+            }
+            
+            if (op == TOK_ASSIGN) {
+            
+                emit_push_reg_now ("edx");
+                
+                if (member_is_floating || token_is_floating_constant_now ()) {
+                    emit_load_floating_rhs_expression_now (member_size);
+                } else {
+                    emit_load_assignment_rhs_expression_to_reg ("eax");
+                }
+                
+                emit_pop_reg_now ("edx");
+            
+            } else {
+            
+                emit_load_member_from_addr_reg_now ("eax", "edx", member_offset, member_size);
+                emit_push_reg_now ("edx");
+                emit_push_reg_now ("eax");
+                
+                if (tok.kind == TOK_TILDE) {
+                
+                    int64_s rhs_const;
+                    get_token ();
+                    
+                    if (tok.kind == TOK_LPAREN) {
+                    
+                        get_token ();
+                        
+                        rhs_const = const64_from_current_foldable_expr ();
+                        expect (TOK_RPAREN, ")");
+                    
+                    } else {
+                        rhs_const = const64_from_current_foldable_expr ();
+                    }
+                    
+                    rhs_const.low = (~rhs_const.low) & U32_MASK;
+                    rhs_const.high = (~rhs_const.high) & U32_MASK;
+                    
+                    emit_load_const32_to_reg_now ("edx", rhs_const);
+                
+                } else if (const_integer_expr_text_is_foldable_now (tok.start) || const_integer_expr_text_is_foldable_now (tok.caret)) {
+                
+                    int64_s rhs_const = const64_from_current_foldable_expr ();
+                    emit_load_const32_to_reg_now ("edx", rhs_const);
+                
+                } else {
+                    emit_load_assignment_rhs_expression_to_reg ("edx");
+                }
+                
+                emit_pop_reg_now ("eax");
+                emit_assignment_binary_op (op, 0);
+                emit_pop_reg_now ("edx");
+            
+            }
+            
+            if ((member_is_floating || token_is_floating_constant_now ()) && op == TOK_ASSIGN) {
+                emit_store_floating_member_to_addr_reg_now ("edx", member_offset, member_size);
+            } else {
+                emit_store_member_to_addr_reg_now ("edx", member_offset, "eax", member_size);
+            }
+        
+        } else {
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        }
+        
+        expect_semi_or_recover ();
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    if (tok.kind == TOK_LBRACK) {
+    
+        int elem_size = DATA_INT & 0x1f;
+        int elem_pointer_depth = 0;
+        int elem_pointed_size = DATA_INT & 0x1f;
+        
+        lhs = find_local_symbol (name);
+        global_index = find_global_symbol (name);
+        
+        if (!lhs && global_index < 0) {
+        
+            report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+            
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+            expect_semi_or_recover ();
+            
+            free (name);
+            return 1;
+        
+        }
+        
+        if (lhs) {
+        
+            elem_size = lhs->is_array ? (lhs->pointer_depth ? DATA_PTR : lhs->pointed_size) :
+                (lhs->pointer_depth > 1 ? DATA_PTR : lhs->pointed_size);
+            
+            elem_pointer_depth = lhs->pointer_depth > 0 ? lhs->pointer_depth - 1 : 0;
+            elem_pointed_size = lhs->pointed_size;
+        
+        } else {
+        
+            elem_size = get_global_symbol_array (name) ?
+                (get_global_symbol_pointer_depth (name) ? DATA_PTR : (get_global_symbol_array_element_size (name) > 0 ? get_global_symbol_array_element_size (name) : get_global_symbol_pointed_size (name))) :
+                    (get_global_symbol_pointer_depth (name) > 1 ? DATA_PTR : get_global_symbol_pointed_size (name));
+            
+            elem_pointer_depth = get_global_symbol_pointer_depth (name) > 0 ? get_global_symbol_pointer_depth (name) - 1 : 0;
+            elem_pointed_size = get_global_symbol_pointed_size (name);
+        
+        }
+        
+        if ((elem_size & 0x1f) == 0) {
+            elem_size = DATA_INT & 0x1f;
+        }
+        
+        if (state->ofp) {
+        
+            if (lhs) {
+            
+                if (lhs->is_array) {
+                
+                    if (lhs->is_static && lhs->static_label) {
+                        emit_load_symbol_address_to_reg_now ("edx", lhs->static_label, 0, 0);
+                    } else {
+                        emit_load_symbol_address_to_reg_now ("edx", 0, lhs->offset, 1);
+                    }
+                
+                } else if (lhs->is_static && lhs->static_label) {
+                    emit_load_global_to_reg ("edx", lhs->static_label, DATA_PTR);
+                } else {
+                    emit_load_local_to_reg ("edx", lhs->offset, DATA_PTR);
+                }
+            
+            } else if (get_global_symbol_array (name)) {
+                emit_load_symbol_address_to_reg_now ("edx", name, 0, 0);
+            } else {
+                emit_load_global_to_reg ("edx", name, DATA_PTR);
+            }
+            
+            emit_parse_postfix_subscript_scaled_address_to_reg_now ("edx", elem_size);
+        
+        } else {
+            emit_parse_postfix_subscript_scaled_address_to_reg_now ("edx", elem_size);
+        }
+        
+        if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+        
+            op = tok.kind;
+            get_token ();
+            
+            if (state->ofp) {
+            
+                int inc_amount = (elem_pointer_depth > 0 && (elem_pointed_size & 0x1f) > 0) ?
+                    (elem_pointed_size & 0x1f) : 1;
+                
+                emit_load_member_from_addr_reg_now ("eax", "edx", 0, elem_size);
+                
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                    fprintf (state->ofp, "    %s eax, %d\n", op == TOK_INCR ? "add" : "sub", inc_amount);
+                } else {
+                    fprintf (state->ofp, "    %sl $%d, %%eax\n", op == TOK_INCR ? "add" : "sub", inc_amount);
+                }
+                
+                emit_store_reg_to_deref_reg_now ("edx", "eax", elem_size);
+            
+            }
+            
+            expect_semi_or_recover ();
+            
+            free (name);
+            return 1;
+        
+        }
+        
+        if (!is_assignment_operator (tok.kind)) {
+        
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+            expect_semi_or_recover ();
+            
+            free (name);
+            return 1;
+        
+        }
+        
+        op = tok.kind;
+        get_token ();
+        
+        if (state->ofp) {
+        
+            if (op == TOK_ASSIGN) {
+            
+                emit_push_reg_now ("edx");
+                emit_load_assignment_rhs_expression_to_reg ("eax");
+                emit_pop_reg_now ("edx");
+            
+            } else {
+            
+                emit_load_member_from_addr_reg_now ("eax", "edx", 0, elem_size);
+                emit_push_reg_now ("edx");
+                emit_push_reg_now ("eax");
+                emit_load_assignment_rhs_expression_to_reg ("edx");
+                emit_pop_reg_now ("eax");
+                emit_assignment_binary_op (op, 0);
+                emit_pop_reg_now ("edx");
+            
+            }
+            
+            emit_store_reg_to_deref_reg_now ("edx", "eax", elem_size);
+        
+        } else {
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        }
+        
+        expect_semi_or_recover ();
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    if (!is_assignment_operator (tok.kind)) {
+    
+        if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+        
+            op = tok.kind;
+            get_token ();
+            
+            lhs = find_local_symbol (name);
+            emit_incdec_symbol_now (lhs, name, op, name_line, name_start, name_caret);
+            
+            expect_semi_or_recover ();
+            free (name);
+            
+            return 1;
+        
+        }
+        
+        if (tok.kind == TOK_LPAREN) {
+        
+            if (!find_local_symbol (name)) {
+                ensure_global_function_symbol (name, name_start, name_caret, name_line);
+            }
+            
+            emit_call_identifier_to_reg_now (name, "eax", name_start, name_caret, name_line);
+            
+            if (tok.kind == TOK_LBRACK) {
+            
+                int elem_size = get_global_symbol_pointed_size (name);
+                
+                if ((elem_size & 0x1f) == 0) {
+                    elem_size = DATA_INT & 0x1f;
+                }
+                
+                if (state->ofp) {
+                
+                    if (state->syntax & ASM_SYNTAX_INTEL) {
+                        fprintf (state->ofp, "    mov edx, eax\n");
+                    } else {
+                        fprintf (state->ofp, "    movl %%eax, %%edx\n");
+                    }
+                
+                }
+                
+                emit_parse_postfix_subscript_scaled_address_to_reg_now ("edx", elem_size);
+                
+                if (is_assignment_operator (tok.kind)) {
+                
+                    op = tok.kind;
+                    get_token ();
+                    
+                    if (state->ofp) {
+                    
+                        if (op == TOK_ASSIGN) {
+                        
+                            emit_push_reg_now ("edx");
+                            emit_load_assignment_rhs_expression_to_reg ("eax");
+                            emit_pop_reg_now ("edx");
+                        
+                        } else {
+                        
+                            emit_load_member_from_addr_reg_now ("eax", "edx", 0, elem_size);
+                            emit_push_reg_now ("edx");
+                            emit_push_reg_now ("eax");
+                            emit_load_assignment_rhs_expression_to_reg ("edx");
+                            emit_pop_reg_now ("eax");
+                            emit_assignment_binary_op (op, 0);
+                            emit_pop_reg_now ("edx");
+                        
+                        }
+                        
+                        emit_store_reg_to_deref_reg_now ("edx", "eax", elem_size);
+                    
+                    } else {
+                        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+                    }
+                    
+                    expect_semi_or_recover ();
+                    
+                    free (name);
+                    return 1;
+                
+                }
+            
+            }
+            
+            if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+            
+                enum token_kind member_op = tok.kind;
+                char *member;
+                
+                const char *member_start;
+                const char *member_caret;
+                
+                unsigned long member_line;
+                
+                int member_offset = 0;
+                int member_size = DATA_PTR & 0x1f;
+                
+                get_token ();
+                
+                member_start = tok.start;
+                member_caret = tok.caret;
+                member_line = get_line_number ();
+                
+                if (tok.kind != TOK_IDENT) {
+                
+                    report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+                    
+                    skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+                    expect_semi_or_recover ();
+                    
+                    free (name);
+                    return 1;
+                
+                }
+                
+                member = xstrdup (tok.ident);
+                get_token ();
+                
+                if (!find_member_info (member, &member_offset, &member_size)) {
+                
+                    report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+                    free (member);
+                    
+                    skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+                    expect_semi_or_recover ();
+                    
+                    free (name);
+                    return 1;
+                
+                }
+                
+                free (member);
+                
+                if (member_op == TOK_DOT && state->ofp) {
+                    emit_load_deref_reg_now ("eax", DATA_PTR);
+                }
+                
+                if (is_assignment_operator (tok.kind)) {
+                
+                    op = tok.kind;
+                    get_token ();
+                    
+                    if (state->ofp) {
+                    
+                        if (state->syntax & ASM_SYNTAX_INTEL) {
+                            fprintf (state->ofp, "    mov edx, eax\n");
+                        } else {
+                            fprintf (state->ofp, "    movl %%eax, %%edx\n");
+                        }
+                        
+                        if (op == TOK_ASSIGN) {
+                        
+                            emit_push_reg_now ("edx");
+                            emit_load_assignment_rhs_expression_to_reg ("eax");
+                            emit_pop_reg_now ("edx");
+                        
+                        } else {
+                        
+                            emit_load_member_from_addr_reg_now ("eax", "edx", member_offset, member_size);
+                            emit_push_reg_now ("edx");
+                            emit_push_reg_now ("eax");
+                            emit_load_assignment_rhs_expression_to_reg ("edx");
+                            emit_pop_reg_now ("eax");
+                            emit_assignment_binary_op (op, 0);
+                            emit_pop_reg_now ("edx");
+                        
+                        }
+                        
+                        emit_store_member_to_addr_reg_now ("edx", member_offset, "eax", member_size);
+                    
+                    } else {
+                        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+                    }
+                    
+                    expect_semi_or_recover ();
+                    free (name);
+                    
+                    return 1;
+                
+                }
+                
+                if (tok.kind == TOK_LPAREN) {
+                
+                    FILE **arg_tmp_ofps = 0;
+                    FILE **new_arg_tmp_ofps;
+                    FILE *arg_tmp_ofp;
+                    FILE *arg_saved_ofp;
+                    
+                    int argc = 0;
+                    int i;
+                    int ch;
+                    int total_arg_bytes = 0;
+                    
+                    if (state->ofp) {
+                    
+                        emit_load_member_from_addr_reg_now ("ecx", "eax", member_offset, DATA_PTR & 0x1f);
+                        emit_push_reg_now ("ecx");
+                    
+                    }
+                    
+                    get_token ();
+                    
+                    if (tok.kind != TOK_RPAREN) {
+                    
+                        for (;;) {
+                        
+                            arg_tmp_ofp = 0;
+                            arg_saved_ofp = 0;
+                            
+                            if (state->ofp) {
+                            
+                                arg_tmp_ofp = tmpfile ();
+                                
+                                if (arg_tmp_ofp) {
+                                    arg_saved_ofp = state->ofp;
+                                    state->ofp = arg_tmp_ofp;
+                                }
+                            
+                            }
+                            
+                            emit_load_assignment_rhs_expression_to_reg ("eax");
+                            
+                            if (state->ofp) {
+                                emit_push_reg_now ("eax");
+                            }
+                            
+                            if (arg_saved_ofp) {
+                            
+                                fflush (arg_tmp_ofp);
+                                state->ofp = arg_saved_ofp;
+                                
+                                new_arg_tmp_ofps = (FILE **) realloc (arg_tmp_ofps, sizeof (*arg_tmp_ofps) * (argc + 1));
+                                
+                                if (new_arg_tmp_ofps) {
+                                
+                                    arg_tmp_ofps = new_arg_tmp_ofps;
+                                    arg_tmp_ofps[argc] = arg_tmp_ofp;
+                                    arg_tmp_ofp = 0;
+                                
+                                }
+                            
+                            }
+                            
+                            if (arg_tmp_ofp) {
+                                fclose (arg_tmp_ofp);
+                            }
+                            
+                            argc++;
+                            total_arg_bytes += DATA_PTR & 0x1f;
+                            
+                            if (!_accept (TOK_COMMA)) {
+                                break;
+                            }
+                        
+                        }
+                    
+                    }
+                    
+                    expect (TOK_RPAREN, ")");
+                    
+                    if (state->ofp) {
+                    
+                        for (i = argc - 1; i >= 0; i--) {
+                        
+                            if (arg_tmp_ofps && arg_tmp_ofps[i]) {
+                            
+                                fseek (arg_tmp_ofps[i], 0, SEEK_SET);
+                                
+                                while ((ch = fgetc (arg_tmp_ofps[i])) != EOF) {
+                                    fputc (ch, state->ofp);
+                                }
+                                
+                                fclose (arg_tmp_ofps[i]);
+                                arg_tmp_ofps[i] = 0;
+                            
+                            }
+                        
+                        }
+                        
+                        if (arg_tmp_ofps) {
+                        
+                            free (arg_tmp_ofps);
+                            arg_tmp_ofps = 0;
+                        
+                        }
+                        
+                        if (state->syntax & ASM_SYNTAX_INTEL) {
+                        
+                            if (state->syntax & ASM_SYNTAX_NASM) {
+                                fprintf (state->ofp, "    mov ecx, dword [esp + %d]\n", total_arg_bytes);
+                            } else {
+                                fprintf (state->ofp, "    mov ecx, dword ptr [esp + %d]\n", total_arg_bytes);
+                            }
+                            
+                            fprintf (state->ofp, "    call ecx\n");
+                            fprintf (state->ofp, "    add esp, %d\n", total_arg_bytes + (DATA_PTR & 0x1f));
+                        
+                        } else {
+                        
+                            fprintf (state->ofp, "    movl %d(%%esp), %%ecx\n", total_arg_bytes);
+                            fprintf (state->ofp, "    call *%%ecx\n");
+                            fprintf (state->ofp, "    addl $%d, %%esp\n", total_arg_bytes + (DATA_PTR & 0x1f));
+                        
+                        }
+                    
+                    }
+                    
+                    expect_semi_or_recover ();
+                    free (name);
+                    
+                    return 1;
+                
+                }
+                
+                skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+                
+                expect_semi_or_recover ();
+                free (name);
+                
+                return 1;
+            
+            }
+            
+            expect_semi_or_recover ();
+            free (name);
+            
+            return 1;
+        
+        }
+        
+        if (!find_local_symbol (name) && find_global_symbol (name) < 0) {
+        
+            int64_s ignored;
+            
+            if (!resolve_enum_constant (name, &ignored)) {
+                report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+            }
+        
+        }
+        
+        free (name);
+        
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        expect (TOK_SEMI, ";");
+        
+        return 1;
+    
+    }
+    
+    op = tok.kind;
+    get_token ();
+    
+    lhs = find_local_symbol (name);
+    global_index = find_global_symbol (name);
+    
+    if (!lhs && global_index < 0) {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+        
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        expect (TOK_SEMI, ";");
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    if (state->ofp) {
+    
+        lhs_size = lhs ? lhs->size : get_global_symbol_size (name);
+        lhs_is_floating = lhs ? lhs->is_floating : get_global_symbol_floating (name);
+        
+        if (op == TOK_ASSIGN && lhs_size > (DATA_LLONG & 0x1f) && tok.kind == TOK_IDENT) {
+            
+            char *rhs_name = xstrdup (tok.ident);
+            
+            struct local_symbol *rhs_sym;
+            int rhs_global_index;
+            
+            get_token ();
+            
+            rhs_sym = find_local_symbol (rhs_name);
+            rhs_global_index = find_global_symbol (rhs_name);
+            
+            if (rhs_sym || rhs_global_index >= 0) {
+            
+                if (tok.kind == TOK_LPAREN && rhs_global_index >= 0 && get_global_symbol_kind (rhs_name) == GLOBAL_SYMBOL_FUNCTION) {
+                
+                    pending_struct_return_lhs = lhs;
+                    pending_struct_return_global_name = lhs ? 0 : name;
+                    
+                    emit_call_identifier_to_reg_now (rhs_name, "eax", name_start, name_caret, name_line);
+                
+                } else if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT || tok.kind == TOK_LBRACK) {
+                
+                    if (emit_parse_postfix_copy_source_address_now ("eax", rhs_sym, rhs_name, name_start, name_caret, name_line)) {
+                    
+                        emit_load_symbol_address_for_copy_now ("edx", lhs, name);
+                        emit_copy_fixed_size_now (lhs_size);
+                    
+                    }
+                
+                } else {
+                    emit_memcpy_symbol_to_symbol_now (lhs, name, rhs_sym, rhs_name, lhs_size);
+                }
+            
+            } else {
+                report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", rhs_name);
+            }
+            
+            free (rhs_name);
+            expect_semi_or_recover ();
+            
+            free (name);
+            return 1;
+        
+        }
+        
+        if (lhs_is_floating) {
+        
+            if (!floating_assignment_operator_supported_now (op)) {
+            
+                report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "invalid operands to floating assignment operator");
+                skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+            
+            } else {
+            
+                if (op == TOK_ASSIGN) {
+                    emit_load_floating_rhs_expression_now (lhs_size);
+                } else {
+                
+                    emit_load_floating_symbol_now (lhs, name, lhs_size);
+                    emit_load_floating_rhs_expression_now (lhs_size);
+                    emit_floating_binary_now (op);
+                
+                }
+                
+                emit_store_floating_symbol_now (lhs, name, lhs_size);
+            
+            }
+        
+        } else if (lhs_size == (DATA_LLONG & 0x1f)) {
+        
+            if (op == TOK_ASSIGN) {
+                emit_load_assignment_rhs_expression_to_pair ("eax", "edx", lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name));
+            } else {
+            
+                if (lhs) {
+                
+                    if (lhs->is_static && lhs->static_label) {
+                        emit_load_global64_to_pair ("eax", "edx", lhs->static_label);
+                    } else {
+                        emit_load_local64_to_pair (lhs->offset, "eax", "edx");
+                    }
+                
+                } else {
+                    emit_load_global64_to_pair ("eax", "edx", name);
+                }
+                
+                /*
+                 * Compound assignments need the complete RHS expression.
+                 * Using emit_load_assignment_rhs_to_pair() only consumes one
+                 * primary operand, so e.g.
+                 *
+                 *     final_value += symbol->frag->address + left_value;
+                 *
+                 * leaves the second + operand for the statement parser and
+                 * reports "expected ;".  Evaluate the full RHS in eax:edx,
+                 * copy it to the RHS pair ebx:ecx, then restore the original
+                 * LHS value before applying the compound operator.
+                 */
+                emit_push_reg_now ("eax");
+                emit_push_reg_now ("edx");
+                
+                emit_load_assignment_rhs_expression_to_pair ("eax", "edx", lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name));
+                
+                emit_mov_reg_to_reg_now ("ebx", "eax");
+                emit_mov_reg_to_reg_now ("ecx", "edx");
+                
+                emit_pop_reg_now ("edx");
+                emit_pop_reg_now ("eax");
+                
+                emit_preserve_assignment64_regs (op);
+                emit_assignment_binary_op64 (op, lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name));
+                emit_restore_assignment64_regs (op);
+            
+            }
+            
+            if (lhs) {
+            
+                if (lhs->is_static && lhs->static_label) {
+                    emit_store_pair_to_global64 (lhs->static_label, "eax", "edx");
+                } else {
+                    emit_store_pair_to_local64 (lhs->offset, "eax", "edx");
+                }
+            
+            } else {
+                emit_store_pair_to_global64 (name, "eax", "edx");
+            }
+        
+        } else {
+        
+            if (op == TOK_ASSIGN) {
+                emit_load_assignment_rhs_expression_to_reg ("eax");
+            } else {
+            
+                if (lhs) {
+                
+                    if (lhs->is_static && lhs->static_label) {
+                        emit_load_global_to_reg ("eax", lhs->static_label, lhs->size);
+                    } else {
+                        emit_load_local_to_reg ("eax", lhs->offset, lhs->size);
+                    }
+                
+                } else {
+                    emit_load_global_to_reg ("eax", name, lhs_size);
+                }
+                
+                /*
+                 * Compound assignments still need the full RHS expression,
+                 * not just a single operand.  Otherwise cases such as:
+                 *
+                 *     processed += (int)(t - stream->upto) - 1;
+                 *
+                 * leave the trailing "- 1" unconsumed and the statement
+                 * parser reports "expected ;".  Preserve the current LHS
+                 * value in eax while the RHS expression is parsed, because
+                 * RHS binary-expression code may use eax internally even
+                 * when the requested result register is edx.
+                 */
+                emit_push_reg_now ("eax");
+                emit_load_assignment_rhs_expression_to_reg ("edx");
+                
+                emit_scale_reg_for_pointer_compound_assignment_now ("edx", lhs, name, op);
+                emit_pop_reg_now ("eax");
+                
+                emit_assignment_binary_op (op, lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name));
+            
+            }
+            
+            if (lhs) {
+            
+                if (lhs->is_static && lhs->static_label) {
+                    emit_store_reg_to_global (lhs->static_label, lhs->size, "eax");
+                } else {
+                    emit_store_reg_to_local (lhs->offset, lhs->size, "eax");
+                }
+            
+            } else {
+                emit_store_reg_to_global (name, lhs_size, "eax");
+            }
+        
+        }
+    
+    } else {
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+    }
+    
+    expect_semi_or_recover ();
+    
+    free (name);
+    return 1;
+
+}
+
+static void emit_statement_label_raw (int label) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+        fprintf (state->ofp, "L%d:\n", label);
+    } else {
+        fprintf (state->ofp, ".L%d:\n", label);
+    }
+
+}
+
+static void flush_pending_statement_labels (void) {
+
+    int i;
+    int count = pending_statement_label_count;
+    
+    /**
+     * A queued case/default/C label marks the next statement.  If the
+     * previous statement was a return, its jump to the common epilogue may
+     * still be pending.  Emit that jump before placing the next label,
+     * otherwise the label lands between the return value setup and the
+     * deferred jump.
+     *
+     * Also flush the deferred return before any other emitted control-flow
+     * boundary even when there are no queued labels.  A switch body is emitted
+     * into a temporary stream and then replayed before the switch break label;
+     * if the last case returns, there may be no following case/default label to
+     * force this flush.
+     */
+    if (pending_return_jump) {
+        emit_pending_return_jump ();
+    }
+    
+    if (count <= 0) {
+        return;
+    }
+    
+    pending_statement_label_count = 0;
+    
+    for (i = 0; i < count; i++) {
+        emit_statement_label_raw (pending_statement_labels[i]);
+    }
+
+}
+
+static void emit_statement_label (int label) {
+
+    flush_pending_statement_labels ();
+    emit_statement_label_raw (label);
+
+}
+
+static void emit_statement_jump (int label) {
+
+    flush_pending_statement_labels ();
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+        fprintf (state->ofp, "    jmp L%d\n", label);
+    } else {
+        fprintf (state->ofp, "    jmp .L%d\n", label);
+    }
+
+}
+
+static int token_is_integer_constant_now (enum token_kind k) {
+    return k == TOK_CINT || k == TOK_CLONG || k == TOK_CLLONG || k == (enum token_kind) (TOK_CINT + 1) || k == (enum token_kind) (TOK_CLONG + 1) || k == (enum token_kind) (TOK_CLLONG + 1);
+}
+
+static int token_is_integer_unsigned_constant_now (enum token_kind k) {
+    return k == (enum token_kind) (TOK_CINT + 1) || k == (enum token_kind) (TOK_CLONG + 1) || k == (enum token_kind) (TOK_CLLONG + 1);
+}
+
+#define     MAX_INLINE_ASM_INPUTS        16
+
+struct inline_asm_input_operand {
+
+    char constraint[16];
+    char subst[64];
+    int restore_reg;
+    const char *restore_name;
+
+};
+
+static void inline_asm_copy_trimmed_text (char *dst, size_t dst_size, const char *start, const char *end) {
+
+    size_t len;
+    
+    if (!dst || dst_size == 0) {
+        return;
+    }
+    
+    dst[0] = 0;
+    
+    if (!start || !end || end < start) {
+        return;
+    }
+    
+    while (start < end && (*start == ' ' || *start == '\t' || *start == '\r' || *start == '\n')) {
+        start++;
+    }
+    
+    while (end > start && (end[-1] == ' ' || end[-1] == '\t' || end[-1] == '\r' || end[-1] == '\n')) {
+        end--;
+    }
+    
+    len = (size_t) (end - start);
+    
+    if (len >= dst_size) {
+        len = dst_size - 1;
+    }
+    
+    memcpy (dst, start, len);
+    dst[len] = 0;
+
+}
+
+static void inline_asm_unquote_string_token (char *dst, size_t dst_size) {
+
+    const char *s;
+    char quote;
+    size_t n = 0;
+    
+    if (!dst || dst_size == 0) {
+        return;
+    }
+    
+    dst[0] = 0;
+    
+    if (!is_string_token ()) {
+        return;
+    }
+    
+    s = tok.ident;
+    
+    if (tok.kind == TOK_LSTR && *s == 'L') {
+        s++;
+    }
+    
+    quote = *s;
+    
+    if (quote != '"') {
+        return;
+    }
+    
+    s++;
+    
+    while (*s && *s != quote && n + 1 < dst_size) {
+    
+        if (*s == '\\' && s[1]) {
+        
+            s++;
+            
+            switch (*s) {
+            
+                case 'n':
+                
+                    dst[n++] = '\n'; s++;
+                    break;
+                
+                case 'r':
+                
+                    dst[n++] = '\r'; s++;
+                    break;
+                
+                case 't':
+                
+                    dst[n++] = '\t'; s++;
+                    break;
+                
+                case '\\':
+                
+                    dst[n++] = '\\'; s++;
+                    break;
+                
+                case '"':
+                
+                    dst[n++] = '"'; s++;
+                    break;
+                
+                default:
+                
+                    dst[n++] = *s++;
+                    break;
+            
+            }
+        
+        } else {
+            dst[n++] = *s++;
+        }
+    
+    }
+    
+    dst[n] = 0;
+
+}
+
+static const char *inline_asm_constraint_reg32 (const char *constraint) {
+
+    if (!constraint) {
+        return 0;
+    }
+    
+    if (strchr (constraint, 'a')) {
+        return "eax";
+    }
+    
+    if (strchr (constraint, 'b')) {
+        return "ebx";
+    }
+    
+    if (strchr (constraint, 'c')) {
+        return "ecx";
+    }
+    
+    if (strchr (constraint, 'd')) {
+        return "edx";
+    }
+    
+    if (strchr (constraint, 'S')) {
+        return "esi";
+    }
+    
+    if (strchr (constraint, 'D')) {
+        return "edi";
+    }
+    
+    return 0;
+
+}
+
+static const char *inline_asm_reg_for_template (const char *reg32) {
+
+    if (!reg32) {
+        return "";
+    }
+    
+    if (strcmp (reg32, "eax") == 0) {
+        return "al";
+    }
+    
+    if (strcmp (reg32, "ebx") == 0) {
+        return "bl";
+    }
+    
+    if (strcmp (reg32, "ecx") == 0) {
+        return "cl";
+    }
+    
+    if (strcmp (reg32, "edx") == 0) {
+        return "dx";
+    }
+    
+    if (strcmp (reg32, "esi") == 0) {
+        return "esi";
+    }
+    
+    if (strcmp (reg32, "edi") == 0) {
+        return "edi";
+    }
+    
+    return reg32;
+
+}
+
+static int inline_asm_reg_needs_restore (const char *reg32) {
+    return reg32 && (strcmp (reg32, "ebx") == 0 || strcmp (reg32, "esi") == 0 || strcmp (reg32, "edi") == 0);
+}
+
+static int inline_asm_template_is_out (const char *templ) {
+
+    if (!templ) {
+        return 0;
+    }
+    
+    while (*templ == ' ' || *templ == '\t') {
+        templ++;
+    }
+    
+    return  strncmp (templ, "outb ", 5) == 0 || strncmp (templ, "outw ", 5) == 0 ||
+                strncmp (templ, "outl ", 5) == 0 || strncmp (templ, "out ", 4) == 0;
+
+}
+
+static int inline_asm_is_reg32_name (const char *s, size_t len) {
+
+    return  (len == 3 && strncmp (s, "eax", 3) == 0) ||
+                (len == 3 && strncmp (s, "ebx", 3) == 0) ||
+                    (len == 3 && strncmp (s, "ecx", 3) == 0) ||
+                        (len == 3 && strncmp (s, "edx", 3) == 0) ||
+                            (len == 3 && strncmp (s, "esi", 3) == 0) ||
+                                (len == 3 && strncmp (s, "edi", 3) == 0);
+
+}
+
+static int inline_asm_rewrite_narrow_movzx (char *line, size_t line_size) {
+
+    char rewritten[512];
+    
+    const char *p;
+    const char *reg_start;
+    const char *reg_end;
+    const char *src;
+    
+    size_t reg_len;
+    size_t indent_len;
+    
+    if (!line || line_size == 0 || !(state->syntax & ASM_SYNTAX_INTEL)) {
+        return 0;
+    }
+    
+    p = line;
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    indent_len = (size_t) (p - line);
+    
+    if (strncmp (p, "mov", 3) != 0 || (p[3] != ' ' && p[3] != '\t')) {
+        return 0;
+    }
+    
+    p += 3;
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    reg_start = p;
+    
+    while ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9')) {
+        p++;
+    }
+    
+    reg_end = p;
+    reg_len = (size_t) (reg_end - reg_start);
+    
+    if (!inline_asm_is_reg32_name (reg_start, reg_len)) {
+        return 0;
+    }
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    if (*p != ',') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    src = p;
+    
+    /**
+     * Only change true narrow memory loads.  This deliberately does not
+     * touch register/register moves, immediates, or lines that have already
+     * been rewritten.
+     */
+    if (strncmp (src, "byte ptr ", 9) != 0 && strncmp (src, "word ptr ", 9) != 0) {
+        return 0;
+    }
+    
+    if (indent_len + 6 + reg_len + 2 + strlen (src) + 1 >= sizeof (rewritten)) {
+        return 0;
+    }
+    
+    memcpy (rewritten, line, indent_len);
+    rewritten[indent_len] = 0;
+    
+    strcat (rewritten, "movzx ");
+    strncat (rewritten, reg_start, reg_len);
+    strcat (rewritten, ", ");
+    strcat (rewritten, src);
+    
+    inline_copy_string (line, rewritten, line_size);
+    return 1;
+
+}
+
+static const char *inline_asm_intel_ptr_name (int size) {
+
+    if (size <= 1) {
+        return "byte";
+    }
+    
+    if (size == 2) {
+        return "word";
+    }
+    
+    if (size == 8) {
+        return "qword";
+    }
+    
+    return "dword";
+
+}
+
+static int inline_asm_format_identifier_operand (char *dst, size_t dst_size, const char *name) {
+
+    struct local_symbol *sym;
+    char memref[64];
+    const char *asm_name;
+    
+    if (!dst || dst_size == 0 || !name || !*name) {
+        return 0;
+    }
+    
+    dst[0] = 0;
+    sym = find_local_symbol (name);
+    
+    if (sym) {
+    
+        if (sym->is_static && sym->static_label) {
+        
+            asm_name = asm_global_symbol_name (sym->static_label);
+            
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                sprintf (dst, (state->syntax & ASM_SYNTAX_NASM ? "%s %s" : "%s ptr %s"), inline_asm_intel_ptr_name (sym->size), asm_name);
+            } else {
+                sprintf (dst, "%s", asm_name);
+            }
+            
+            return 1;
+        
+        }
+        
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+        
+            format_intel_ebp_offset (memref, sizeof (memref), sym->offset);
+            sprintf (dst, (state->syntax & ASM_SYNTAX_NASM ? "%s %s" : "%s ptr %s"), inline_asm_intel_ptr_name (sym->size), memref);
+        
+        } else {
+            sprintf (dst, "%ld(%%ebp)", sym->offset);
+        }
+        
+        return 1;
+    
+    }
+    
+    if (find_global_symbol (name) >= 0) {
+    
+        asm_name = asm_global_symbol_name (name);
+        
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+            sprintf (dst, (state->syntax & ASM_SYNTAX_NASM ? "%s %s" : "%s ptr %s"), inline_asm_intel_ptr_name (get_global_symbol_size (name)), asm_name);
+        } else {
+            sprintf (dst, "%s", asm_name);
+        }
+        
+        return 1;
+    
+    }
+    
+    return 0;
+
+}
+
+static void inline_asm_emit_input_load (const char *constraint, int input_index, struct inline_asm_input_operand *inputs, const char *templ) {
+
+    char expr_text[64];
+    
+    const char *expr_start;
+    const char *expr_end;
+    const char *reg32;
+    const char *subst;
+    
+    expr_text[0] = 0;
+    expr_start = tok.caret;
+    
+    inputs[input_index].restore_reg = 0;
+    inputs[input_index].restore_name = 0;
+    
+    if (strchr (constraint, 'N')) {
+    
+        if (token_is_integer_constant_now (tok.kind)) {
+        
+            skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+            expr_end = tok.caret;
+            
+            inline_asm_copy_trimmed_text (expr_text, sizeof (expr_text), expr_start, expr_end);
+            inline_copy_string (inputs[input_index].subst, expr_text, sizeof (inputs[input_index].subst));
+            
+            return;
+        
+        }
+        
+        if (inline_asm_template_is_out (templ)) {
+        
+            /**
+             * GCC's "N" constraint is really an 8-bit immediate port.
+             * For the OUT templates, allow a non-constant here as a
+             * convenience and lower it through DX, because x86 OUT cannot
+             * encode a variable port as an immediate.
+             */
+            emit_load_assignment_rhs_expression_to_reg ("edx");
+            inline_copy_string (inputs[input_index].subst, "dx", sizeof (inputs[input_index].subst));
+            
+            return;
+        
+        }
+        
+        if (tok.kind == TOK_IDENT && tok.ident && inline_asm_format_identifier_operand (expr_text, sizeof (expr_text), tok.ident)) {
+        
+            skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+            inline_copy_string (inputs[input_index].subst, expr_text, sizeof (inputs[input_index].subst));
+            
+            return;
+        
+        }
+        
+        skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+        expr_end = tok.caret;
+        
+        inline_asm_copy_trimmed_text (expr_text, sizeof (expr_text), expr_start, expr_end);
+        inline_copy_string (inputs[input_index].subst, expr_text, sizeof (inputs[input_index].subst));
+        
+        return;
+    
+    }
+    
+    reg32 = inline_asm_constraint_reg32 (constraint);
+    
+    if (reg32) {
+    
+        subst = inline_asm_reg_for_template (reg32);
+        
+        if (inline_asm_reg_needs_restore (reg32) && state->ofp) {
+        
+            fprintf (state->ofp, "    push %s\n", reg32);
+            
+            inputs[input_index].restore_reg = 1;
+            inputs[input_index].restore_name = reg32;
+        
+        }
+        
+        if (token_is_integer_constant_now (tok.kind)) {
+        
+            skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+            expr_end = tok.caret;
+            
+            inline_asm_copy_trimmed_text (expr_text, sizeof (expr_text), expr_start, expr_end);
+            
+            if (state->ofp) {
+                fprintf (state->ofp, "    mov %s, %s\n", subst, expr_text[0] ? expr_text : "0");
+            }
+        
+        } else {
+            emit_load_assignment_rhs_expression_to_reg (reg32);
+        }
+        
+        inline_copy_string (inputs[input_index].subst, subst, sizeof (inputs[input_index].subst));
+        return;
+    
+    }
+    
+    if (tok.kind == TOK_IDENT && tok.ident && inline_asm_format_identifier_operand (expr_text, sizeof (expr_text), tok.ident)) {
+    
+        skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+        inline_copy_string (inputs[input_index].subst, expr_text, sizeof (inputs[input_index].subst));
+        
+        return;
+    
+    }
+    
+    skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+    expr_end = tok.caret;
+    
+    inline_asm_copy_trimmed_text (expr_text, sizeof (expr_text), expr_start, expr_end);
+    inline_copy_string (inputs[input_index].subst, expr_text, sizeof (inputs[input_index].subst));
+
+}
+
+static void inline_asm_emit_template (const char *templ, struct inline_asm_input_operand *inputs, int input_count) {
+
+    char line[512];
+    size_t n = 0;
+    
+    const char *p;
+    
+    if (!state->ofp || !templ) {
+        return;
+    }
+    
+    for (p = templ; *p && n + 1 < sizeof (line); p++) {
+    
+        if (*p == '%' && p[1] == '%') {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                p++;
+            } else {
+            
+                line[n++] = '%';
+                p++;
+            
+            }
+        
+        } else if (*p == '%' && p[1] >= '0' && p[1] <= '9') {
+        
+            int index = p[1] - '0';
+            const char *subst = "";
+            
+            if (index >= 0 && index < input_count) {
+                subst = inputs[index].subst;
+            }
+            
+            while (*subst && n + 1 < sizeof (line)) {
+                line[n++] = *subst++;
+            }
+            
+            p++;
+        
+        } else {
+            line[n++] = *p;
+        }
+    
+    }
+    
+    line[n] = 0;
+    
+    if ((strncmp (line, "outb ", 5) == 0 || strncmp (line, "outw ", 5) == 0 || strncmp (line, "outl ", 5) == 0 || strncmp (line, "out ", 4) == 0)) {
+    
+        char *args;
+        char *comma;
+        char *lhs;
+        char *rhs;
+        
+        args = strchr (line, ' ');
+        
+        if (args) {
+        
+            args++;
+            comma = strchr (args, ',');
+            
+            if (comma) {
+            
+                *comma = 0;
+                
+                lhs = args;
+                rhs = comma + 1;
+                
+                while (*lhs == ' ' || *lhs == '\t') {
+                    lhs++;
+                }
+                
+                while (*rhs == ' ' || *rhs == '\t') {
+                    rhs++;
+                }
+                
+                fprintf (state->ofp, "    out %s, %s\n", rhs, lhs);
+                
+                while (input_count-- > 0) {
+                
+                    if (inputs[input_count].restore_reg && inputs[input_count].restore_name) {
+                        fprintf (state->ofp, "    pop %s\n", inputs[input_count].restore_name);
+                    }
+                
+                }
+                
+                return;
+            
+            }
+        
+        }
+    
+    }
+    
+    if (line[0]) {
+    
+        inline_asm_rewrite_narrow_movzx (line, sizeof (line));
+        fprintf (state->ofp, "    %s\n", line);
+    
+    }
+    
+    while (input_count-- > 0) {
+    
+        if (inputs[input_count].restore_reg && inputs[input_count].restore_name) {
+            fprintf (state->ofp, "    pop %s\n", inputs[input_count].restore_name);
+        }
+    
+    }
+
+}
+
+static int parse_inline_asm_statement (void) {
+
+    char templ[256];
+    
+    struct inline_asm_input_operand inputs[MAX_INLINE_ASM_INPUTS];
+    int input_count = 0;
+    
+    if (tok.kind != TOK_ASM) {
+        return 0;
+    }
+    
+    memset (inputs, 0, sizeof (inputs));
+    
+    get_token ();
+    expect (TOK_LPAREN, "(");
+    
+    if (!is_string_token ()) {
+    
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected asm template string");
+        
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        expect (TOK_SEMI, ";");
+        
+        return 1;
+    
+    }
+    
+    inline_asm_unquote_string_token (templ, sizeof (templ));
+    get_token ();
+    
+    if (_accept (TOK_COLON)) {
+    
+        if (tok.kind != TOK_COLON) {
+            skip_balanced_until (TOK_COLON, TOK_RPAREN, TOK_EOF);
+        }
+        
+        if (_accept (TOK_COLON)) {
+        
+            while (tok.kind != TOK_RPAREN && tok.kind != TOK_EOF) {
+            
+                char constraint[16];
+                
+                if (!is_string_token ()) {
+                
+                    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected asm input constraint string");
+                    
+                    skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+                    break;
+                
+                }
+                
+                inline_asm_unquote_string_token (constraint, sizeof (constraint));
+                get_token ();
+                
+                expect (TOK_LPAREN, "(");
+                
+                if (input_count < MAX_INLINE_ASM_INPUTS) {
+                
+                    inline_copy_string (inputs[input_count].constraint, constraint, sizeof (inputs[input_count].constraint));
+                    inline_asm_emit_input_load (constraint, input_count, inputs, templ);
+                    
+                    input_count++;
+                
+                } else {
+                    skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+                }
+                
+                expect (TOK_RPAREN, ")");
+                
+                if (!_accept (TOK_COMMA)) {
+                    break;
+                }
+            
+            }
+        
+        }
+    
+    }
+    
+    expect (TOK_RPAREN, ")");
+    expect (TOK_SEMI, ";");
+    
+    inline_asm_emit_template (templ, inputs, input_count);
+    return 1;
+
+}
+
+static int token_is_const_condition_operand_now (void) {
+    return token_is_sizeof_keyword () || token_is_integer_constant_now (tok.kind);
+}
+
+static int token_is_const_floating_condition_operand_now (void) {
+    return token_is_floating_constant_now ();
+}
+
+static int ident_char_now (int ch) {
+    return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_';
+}
+
+static int ident_start_now (int ch) {
+    return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '_';
+}
+
+static int prefix_incdec_target_is_floating_now (void) {
+
+    const char *p;
+    const char *q;
+    
+    char *name;
+    int len;
+    
+    struct local_symbol *src;
+    int ret = 0;
+    
+    if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) {
+        return 0;
+    }
+    
+    p = tok.caret;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (!ident_start_now ((unsigned char) *p)) {
+        return 0;
+    }
+    
+    q = p + 1;
+    
+    while (ident_char_now ((unsigned char) *q)) {
+        q++;
+    }
+    
+    len = (int) (q - p);
+    name = xmalloc ((unsigned long) len + 1);
+    
+    memcpy (name, p, (unsigned long) len);
+    name[len] = 0;
+    
+    src = find_local_symbol (name);
+    
+    if (src) {
+        ret = src->is_floating ? 1 : 0;
+    } else if (find_global_symbol (name) >= 0) {
+        ret = get_global_symbol_floating (name) ? 1 : 0;
+    }
+    
+    free (name);
+    return ret;
+
+}
+
+static int source_parenthesized_floating_condition_now (const char *p) {
+
+    char name[128];
+    int i;
+    
+    if (!p) {
+        return 0;
+    }
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    if (*p != '(') {
+        return 0;
+    }
+    
+    while (*p == '(' || *p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) {
+        return 0;
+    }
+    
+    i = 0;
+    
+    while (((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') && i < (int) sizeof (name) - 1) {
+        name[i++] = *p++;
+    }
+    
+    name[i] = 0;
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    if (*p == '.') {
+        return 0;
+    }
+    
+    if (*p == '(' && find_global_symbol (name) >= 0 && get_global_symbol_floating (name)) {
+        return 1;
+    }
+    
+    return 0;
+
+}
+
+static int rhs_current_operand_is_floating_now (void) {
+
+    if (token_is_floating_constant_now ()) {
+        return 1;
+    }
+    
+    if (tok.kind == TOK_LPAREN && (source_parenthesized_floating_condition_now (tok.caret) || source_parenthesized_floating_condition_now (tok.start))) {
+        return 1;
+    }
+    
+    if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+        return prefix_incdec_target_is_floating_now ();
+    }
+    
+    if (tok.kind == TOK_IDENT && tok.ident) {
+    
+        struct local_symbol *src = find_local_symbol (tok.ident);
+        const char *p = tok.caret ? tok.caret + tok.len : 0;
+        
+        while (p && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) {
+            p++;
+        }
+        
+        if (p && *p == '(' && find_global_symbol (tok.ident) >= 0) {
+            return get_global_symbol_floating (tok.ident) ? 1 : 0;
+        }
+        
+        if (p && ((p[0] == '-' && p[1] == '>') || p[0] == '.')) {
+        
+            const char *q = p + (p[0] == '-' ? 2 : 1);
+            char member[128];
+            
+            int n = 0;
+            int offset = 0;
+            int size = 0;
+            int elem_size = 0;
+            int pointer_depth = 0;
+            int is_array = 0;
+            int is_floating = 0;
+            int base_size = 0;
+            
+            const char *base_tag_name = 0;
+            
+            while (*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n') {
+                q++;
+            }
+            
+            while (((*q >= 'A' && *q <= 'Z') || (*q >= 'a' && *q <= 'z') || (*q >= '0' && *q <= '9') || *q == '_') && n < (int) sizeof (member) - 1) {
+                member[n++] = *q++;
+            }
+            
+            member[n] = '\0';
+            
+            if (n > 0) {
+            
+                if (p[0] == '-') {
+                
+                    if (src) {
+                    
+                        base_size = src->pointed_size;
+                        base_tag_name = src->pointed_tag_name;
+                    
+                    } else if (find_global_symbol (tok.ident) >= 0) {
+                    
+                        base_size = get_global_symbol_pointed_size (tok.ident);
+                        base_tag_name = get_global_symbol_tag_name (tok.ident);
+                    
+                    }
+                
+                } else {
+                
+                    if (src) {
+                    
+                        base_size = src->size;
+                        base_tag_name = src->tag_name;
+                    
+                    } else if (find_global_symbol (tok.ident) >= 0) {
+                    
+                        base_size = get_global_symbol_size (tok.ident);
+                        base_tag_name = get_global_symbol_tag_name (tok.ident);
+                    
+                    }
+                
+                }
+                
+                if (find_member_info_ex_bounded (member, base_size, base_tag_name, &offset, &size, &elem_size, &pointer_depth, &is_array, &is_floating)) {
+                    return is_floating ? 1 : 0;
+                }
+            
+            }
+        
+        }
+        
+        if (src) {
+            return src->is_floating ? 1 : 0;
+        }
+        
+        if (find_global_symbol (tok.ident) >= 0) {
+            return get_global_symbol_floating (tok.ident) ? 1 : 0;
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static int64_s floating_ld_to_bits_now (int size, long double value) {
+
+    int64_s r;
+    
+    unsigned long bits32;
+    unsigned char bytes[8];
+    
+    int i;
+    
+    r.low = 0;
+    r.high = 0;
+    
+    if (size == (DATA_FLOAT & 0x1f)) {
+    
+        float f = (float) value;
+        
+        bits32 = 0;
+        
+        memcpy (&bits32, &f, sizeof (f));
+        r.low = bits32;
+        
+        return r;
+    
+    }
+    
+    {
+    
+        double d = (double) value;
+        
+        memset (bytes, 0, sizeof (bytes));
+        memcpy (bytes, &d, sizeof (d));
+        
+        for (i = 0; i < 4; i++) {
+            r.low |= ((unsigned long) bytes[i]) << (i * 8);
+        }
+        
+        for (i = 0; i < 4; i++) {
+            r.high |= ((unsigned long) bytes[i + 4]) << (i * 8);
+        }
+        
+        return r;
+    
+    }
+
+}
+
+static void emit_load_floating_ld_now (int size, long double value) {
+    emit_load_floating_const_bits_now (size, floating_ld_to_bits_now (size, value));
+}
+
+static int int64_statement_truth_value (int64_s v) {
+    return v.low != 0 || v.high != 0;
+}
+
+static long statement_int64_signed_high (int64_s v) {
+
+    unsigned long h = v.high & U32_MASK;
+    
+    if (h & 0x80000000UL) {
+        return -((long) ((~h + 1UL) & U32_MASK));
+    }
+    
+    return (long) h;
+
+}
+
+static int statement_cmp_const64_unsigned (int64_s left, int64_s right) {
+
+    unsigned long lh = left.high & U32_MASK;
+    unsigned long rh = right.high & U32_MASK;
+    unsigned long ll = left.low & U32_MASK;
+    unsigned long rl = right.low & U32_MASK;
+    
+    if (lh < rh) return -1;
+    if (lh > rh) return 1;
+    if (ll < rl) return -1;
+    if (ll > rl) return 1;
+    
+    return 0;
+
+}
+
+static int statement_cmp_const64_signed (int64_s left, int64_s right) {
+
+    long lh = statement_int64_signed_high (left);
+    long rh = statement_int64_signed_high (right);
+    
+    unsigned long ll = left.low & U32_MASK;
+    unsigned long rl = right.low & U32_MASK;
+
+    if (lh < rh) return -1;
+    if (lh > rh) return 1;
+    if (ll < rl) return -1;
+    if (ll > rl) return 1;
+    
+    return 0;
+
+}
+
+static int statement_compare_const64_true (int64_s left, enum token_kind op, int64_s right, int is_unsigned) {
+
+    int c = is_unsigned ? statement_cmp_const64_unsigned (left, right) : statement_cmp_const64_signed (left, right);
+    
+    switch (op) {
+    
+        case TOK_LESS:
+        
+            return c < 0;
+        
+        case TOK_LTEQ:
+        
+            return c <= 0;
+        
+        case TOK_GREATER:
+        
+            return c > 0;
+        
+        case TOK_GTEQ:
+        
+            return c >= 0;
+        
+        case TOK_EQEQ:
+        
+            return c == 0;
+        
+        case TOK_NOTEQ:
+        
+            return c != 0;
+        
+        default:
+        
+            return int64_statement_truth_value (left);
+    
+    }
+
+}
+
+static int rhs_current_operand_is_unsigned_now (void) {
+
+    if (token_is_sizeof_keyword ()) {
+        return 1;
+    }
+    
+    if (token_is_integer_unsigned_constant_now (tok.kind)) {
+        return 1;
+    }
+    
+    if (tok.kind == TOK_IDENT && tok.ident) {
+    
+        struct local_symbol *src = find_local_symbol (tok.ident);
+        
+        if (src) {
+            return src->is_unsigned ? 1 : 0;
+        }
+        
+        if (find_global_symbol (tok.ident) >= 0) {
+            return get_global_symbol_unsigned (tok.ident) ? 1 : 0;
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static int token_is_statement_compare_operator (enum token_kind k) {
+    return k == TOK_LESS || k == TOK_LTEQ || k == TOK_GREATER || k == TOK_GTEQ || k == TOK_EQEQ || k == TOK_NOTEQ;
+}
+
+static int statement_condition_ident_call_at (const char *p) {
+
+    int len;
+    
+    if (tok.kind != TOK_IDENT || !tok.ident || !p) {
+        return 0;
+    }
+    
+    len = (int) strlen (tok.ident);
+    
+    while (*p && *p != '\n') {
+    
+        if ((p == tok.start || !ident_char_now ((unsigned char) p[-1])) && strncmp (p, tok.ident, (unsigned long) len) == 0 && !ident_char_now ((unsigned char) p[len])) {
+        
+            p += len;
+            
+            while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+                p++;
+            }
+            
+            return *p == '(';
+        
+        }
+        
+        p++;
+    
+    }
+    
+    return 0;
+
+}
+
+static int statement_condition_starts_with_ident_call_now (void) {
+
+    if (statement_condition_ident_call_at (tok.caret)) {
+        return 1;
+    }
+    
+    if (statement_condition_ident_call_at (tok.start)) {
+        return 1;
+    }
+    
+    return 0;
+
+}
+
+static const char *statement_false_jump_mnemonic (enum token_kind op, int is_unsigned) {
+
+    switch (op) {
+    
+        case TOK_LESS:
+        
+            return is_unsigned ? "jae" : "jge";
+        
+        case TOK_LTEQ:
+        
+            return is_unsigned ? "ja"  : "jg";
+        
+        case TOK_GREATER:
+        
+            return is_unsigned ? "jbe" : "jle";
+        
+        case TOK_GTEQ:
+        
+            return is_unsigned ? "jb"  : "jl";
+        
+        case TOK_EQEQ:
+        
+            return "jne";
+        
+        case TOK_NOTEQ:
+        
+            return "je";
+        
+        default:
+        
+            return "jz";
+    
+    }
+
+}
+
+static void emit_statement_const32_to_eax (int64_s v) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+        fprintf (state->ofp, "    mov eax, %lu\n", v.low & U32_MASK);
+    } else {
+        fprintf (state->ofp, "    movl $%lu, %%eax\n", v.low & U32_MASK);
+    }
+
+}
+
+static void emit_statement_cmp_eax_edx_jump_if_false (enum token_kind op, int is_unsigned, int label) {
+
+    const char *jmp;
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    jmp = statement_false_jump_mnemonic (op, is_unsigned);
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        fprintf (state->ofp, "    cmp eax, edx\n");
+        fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    %s L%d\n" : "    %s .L%d\n"), jmp, label);
+    
+    } else {
+    
+        fprintf (state->ofp, "    cmpl %%edx, %%eax\n");
+        fprintf (state->ofp, "    %s .L%d\n", jmp, label);
+    
+    }
+
+}
+
+static void emit_statement_cmp64_to_eax (enum token_kind op, int is_unsigned) {
+
+    int true_label;
+    int false_label;
+    int end_label;
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    true_label = anon_label++;
+    false_label = anon_label++;
+    end_label = anon_label++;
+    
+    if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+    
+        switch (op) {
+        
+            case TOK_LESS:
+            
+                fprintf (state->ofp, "    cmp edx, ecx\n");
+                fprintf (state->ofp, is_unsigned ? "    jb L%d\n" : "    jl L%d\n", true_label);
+                fprintf (state->ofp, is_unsigned ? "    ja L%d\n" : "    jg L%d\n", false_label);
+                fprintf (state->ofp, "    cmp eax, ebx\n");
+                fprintf (state->ofp, "    jb L%d\n", true_label);
+                break;
+            
+            case TOK_LTEQ:
+            
+                fprintf (state->ofp, "    cmp edx, ecx\n");
+                fprintf (state->ofp, is_unsigned ? "    jb L%d\n" : "    jl L%d\n", true_label);
+                fprintf (state->ofp, is_unsigned ? "    ja L%d\n" : "    jg L%d\n", false_label);
+                fprintf (state->ofp, "    cmp eax, ebx\n");
+                fprintf (state->ofp, "    jbe L%d\n", true_label);
+                break;
+            
+            case TOK_GREATER:
+            
+                fprintf (state->ofp, "    cmp edx, ecx\n");
+                fprintf (state->ofp, is_unsigned ? "    ja L%d\n" : "    jg L%d\n", true_label);
+                fprintf (state->ofp, is_unsigned ? "    jb L%d\n" : "    jl L%d\n", false_label);
+                fprintf (state->ofp, "    cmp eax, ebx\n");
+                fprintf (state->ofp, "    ja L%d\n", true_label);
+                break;
+            
+            case TOK_GTEQ:
+            
+                fprintf (state->ofp, "    cmp edx, ecx\n");
+                fprintf (state->ofp, is_unsigned ? "    ja L%d\n" : "    jg L%d\n", true_label);
+                fprintf (state->ofp, is_unsigned ? "    jb L%d\n" : "    jl L%d\n", false_label);
+                fprintf (state->ofp, "    cmp eax, ebx\n");
+                fprintf (state->ofp, "    jae L%d\n", true_label);
+                break;
+            
+            case TOK_EQEQ:
+            
+                fprintf (state->ofp, "    cmp edx, ecx\n");
+                fprintf (state->ofp, "    jne L%d\n", false_label);
+                fprintf (state->ofp, "    cmp eax, ebx\n");
+                fprintf (state->ofp, "    je L%d\n", true_label);
+                break;
+            
+            case TOK_NOTEQ:
+            
+                fprintf (state->ofp, "    cmp edx, ecx\n");
+                fprintf (state->ofp, "    jne L%d\n", true_label);
+                fprintf (state->ofp, "    cmp eax, ebx\n");
+                fprintf (state->ofp, "    jne L%d\n", true_label);
+                break;
+            
+            default:
+            
+                fprintf (state->ofp, "    test edx, edx\n");
+                fprintf (state->ofp, "    jnz L%d\n", true_label);
+                fprintf (state->ofp, "    test eax, eax\n");
+                fprintf (state->ofp, "    jnz L%d\n", true_label);
+                break;
+        
+        }
+        
+        fprintf (state->ofp, "L%d:\n", false_label);
+        fprintf (state->ofp, "    xor eax, eax\n");
+        fprintf (state->ofp, "    jmp L%d\n", end_label);
+        fprintf (state->ofp, "L%d:\n", true_label);
+        fprintf (state->ofp, "    mov eax, 1\n");
+        fprintf (state->ofp, "L%d:\n", end_label);
+    
+    } else if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        switch (op) {
+        
+            case TOK_LESS:
+            
+                fprintf (state->ofp, "    cmp edx, ecx\n");
+                fprintf (state->ofp, is_unsigned ? "    jb .L%d\n" : "    jl .L%d\n", true_label);
+                fprintf (state->ofp, is_unsigned ? "    ja .L%d\n" : "    jg .L%d\n", false_label);
+                fprintf (state->ofp, "    cmp eax, ebx\n");
+                fprintf (state->ofp, "    jb .L%d\n", true_label);
+                break;
+            
+            case TOK_LTEQ:
+            
+                fprintf (state->ofp, "    cmp edx, ecx\n");
+                fprintf (state->ofp, is_unsigned ? "    jb .L%d\n" : "    jl .L%d\n", true_label);
+                fprintf (state->ofp, is_unsigned ? "    ja .L%d\n" : "    jg .L%d\n", false_label);
+                fprintf (state->ofp, "    cmp eax, ebx\n");
+                fprintf (state->ofp, "    jbe .L%d\n", true_label);
+                break;
+            
+            case TOK_GREATER:
+            
+                fprintf (state->ofp, "    cmp edx, ecx\n");
+                fprintf (state->ofp, is_unsigned ? "    ja .L%d\n" : "    jg .L%d\n", true_label);
+                fprintf (state->ofp, is_unsigned ? "    jb .L%d\n" : "    jl .L%d\n", false_label);
+                fprintf (state->ofp, "    cmp eax, ebx\n");
+                fprintf (state->ofp, "    ja .L%d\n", true_label);
+                break;
+            
+            case TOK_GTEQ:
+            
+                fprintf (state->ofp, "    cmp edx, ecx\n");
+                fprintf (state->ofp, is_unsigned ? "    ja .L%d\n" : "    jg .L%d\n", true_label);
+                fprintf (state->ofp, is_unsigned ? "    jb .L%d\n" : "    jl .L%d\n", false_label);
+                fprintf (state->ofp, "    cmp eax, ebx\n");
+                fprintf (state->ofp, "    jae .L%d\n", true_label);
+                break;
+            
+            case TOK_EQEQ:
+            
+                fprintf (state->ofp, "    cmp edx, ecx\n");
+                fprintf (state->ofp, "    jne .L%d\n", false_label);
+                fprintf (state->ofp, "    cmp eax, ebx\n");
+                fprintf (state->ofp, "    je .L%d\n", true_label);
+                break;
+            
+            case TOK_NOTEQ:
+            
+                fprintf (state->ofp, "    cmp edx, ecx\n");
+                fprintf (state->ofp, "    jne .L%d\n", true_label);
+                fprintf (state->ofp, "    cmp eax, ebx\n");
+                fprintf (state->ofp, "    jne .L%d\n", true_label);
+                break;
+            
+            default:
+            
+                fprintf (state->ofp, "    test edx, edx\n");
+                fprintf (state->ofp, "    jnz .L%d\n", true_label);
+                fprintf (state->ofp, "    test eax, eax\n");
+                fprintf (state->ofp, "    jnz .L%d\n", true_label);
+                break;
+        
+        }
+        
+        fprintf (state->ofp, ".L%d:\n", false_label);
+        fprintf (state->ofp, "    xor eax, eax\n");
+        fprintf (state->ofp, "    jmp .L%d\n", end_label);
+        fprintf (state->ofp, ".L%d:\n", true_label);
+        fprintf (state->ofp, "    mov eax, 1\n");
+        fprintf (state->ofp, ".L%d:\n", end_label);
+    
+    } else {
+    
+        switch (op) {
+        
+            case TOK_LESS:
+            
+                fprintf (state->ofp, "    cmpl %%ecx, %%edx\n");
+                fprintf (state->ofp, is_unsigned ? "    jb .L%d\n" : "    jl .L%d\n", true_label);
+                fprintf (state->ofp, is_unsigned ? "    ja .L%d\n" : "    jg .L%d\n", false_label);
+                fprintf (state->ofp, "    cmpl %%ebx, %%eax\n");
+                fprintf (state->ofp, "    jb .L%d\n", true_label);
+                break;
+            
+            case TOK_LTEQ:
+            
+                fprintf (state->ofp, "    cmpl %%ecx, %%edx\n");
+                fprintf (state->ofp, is_unsigned ? "    jb .L%d\n" : "    jl .L%d\n", true_label);
+                fprintf (state->ofp, is_unsigned ? "    ja .L%d\n" : "    jg .L%d\n", false_label);
+                fprintf (state->ofp, "    cmpl %%ebx, %%eax\n");
+                fprintf (state->ofp, "    jbe .L%d\n", true_label);
+                break;
+            
+            case TOK_GREATER:
+            
+                fprintf (state->ofp, "    cmpl %%ecx, %%edx\n");
+                fprintf (state->ofp, is_unsigned ? "    ja .L%d\n" : "    jg .L%d\n", true_label);
+                fprintf (state->ofp, is_unsigned ? "    jb .L%d\n" : "    jl .L%d\n", false_label);
+                fprintf (state->ofp, "    cmpl %%ebx, %%eax\n");
+                fprintf (state->ofp, "    ja .L%d\n", true_label);
+                break;
+            
+            case TOK_GTEQ:
+            
+                fprintf (state->ofp, "    cmpl %%ecx, %%edx\n");
+                fprintf (state->ofp, is_unsigned ? "    ja .L%d\n" : "    jg .L%d\n", true_label);
+                fprintf (state->ofp, is_unsigned ? "    jb .L%d\n" : "    jl .L%d\n", false_label);
+                fprintf (state->ofp, "    cmpl %%ebx, %%eax\n");
+                fprintf (state->ofp, "    jae .L%d\n", true_label);
+                break;
+            
+            case TOK_EQEQ:
+            
+                fprintf (state->ofp, "    cmpl %%ecx, %%edx\n");
+                fprintf (state->ofp, "    jne .L%d\n", false_label);
+                fprintf (state->ofp, "    cmpl %%ebx, %%eax\n");
+                fprintf (state->ofp, "    je .L%d\n", true_label);
+                break;
+            
+            case TOK_NOTEQ:
+            
+                fprintf (state->ofp, "    cmpl %%ecx, %%edx\n");
+                fprintf (state->ofp, "    jne .L%d\n", true_label);
+                fprintf (state->ofp, "    cmpl %%ebx, %%eax\n");
+                fprintf (state->ofp, "    jne .L%d\n", true_label);
+                break;
+            
+            default:
+            
+                fprintf (state->ofp, "    testl %%edx, %%edx\n");
+                fprintf (state->ofp, "    jnz .L%d\n", true_label);
+                fprintf (state->ofp, "    testl %%eax, %%eax\n");
+                fprintf (state->ofp, "    jnz .L%d\n", true_label);
+                break;
+        
+        }
+        
+        fprintf (state->ofp, ".L%d:\n", false_label);
+        fprintf (state->ofp, "    xorl %%eax, %%eax\n");
+        fprintf (state->ofp, "    jmp .L%d\n", end_label);
+        fprintf (state->ofp, ".L%d:\n", true_label);
+        fprintf (state->ofp, "    movl $1, %%eax\n");
+        fprintf (state->ofp, ".L%d:\n", end_label);
+    
+    }
+
+}
+
+static const char *statement_float_false_jump_mnemonic (enum token_kind op) {
+
+    switch (op) {
+    
+        case TOK_LESS:
+        
+            return "jae";
+        
+        case TOK_LTEQ:
+        
+            return "ja";
+        
+        case TOK_GREATER:
+        
+            return "jbe";
+        
+        case TOK_GTEQ:
+        
+            return "jb";
+        
+        case TOK_EQEQ:
+        
+            return "jne";
+        
+        case TOK_NOTEQ:
+        
+            return "je";
+        
+        default:
+        
+            return "jz";
+    
+    }
+
+}
+
+static void emit_statement_floating_compare_jump_if_false (enum token_kind op, int label) {
+
+    const char *jmp;
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    jmp = statement_float_false_jump_mnemonic (op);
+    
+    if (state->syntax & ASM_SYNTAX_NASM) {
+    
+        fprintf (state->ofp, "    fxch st1\n");
+        fprintf (state->ofp, "    fcompp\n");
+        fprintf (state->ofp, "    fnstsw ax\n");
+        fprintf (state->ofp, "    sahf\n");
+        fprintf (state->ofp, "    %s L%d\n", jmp, label);
+    
+    } else if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        fprintf (state->ofp, "    fxch st(1)\n");
+        fprintf (state->ofp, "    fcompp\n");
+        fprintf (state->ofp, "    fnstsw ax\n");
+        fprintf (state->ofp, "    sahf\n");
+        fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    %s L%d\n" : "    %s .L%d\n"), jmp, label);
+    
+    } else {
+    
+        fprintf (state->ofp, "    fxch %%st(1)\n");
+        fprintf (state->ofp, "    fcompp\n");
+        fprintf (state->ofp, "    fnstsw %%ax\n");
+        fprintf (state->ofp, "    sahf\n");
+        fprintf (state->ofp, "    %s .L%d\n", jmp, label);
+    
+    }
+
+}
+
+static int statement_compare_floating_const_true (long double left, enum token_kind op, long double right) {
+
+    switch (op) {
+    
+        case TOK_LESS:
+        
+            return left < right;
+        
+        case TOK_LTEQ:
+        
+            return left <= right;
+        
+        case TOK_GREATER:
+        
+            return left > right;
+        
+        case TOK_GTEQ:
+        
+            return left >= right;
+        
+        case TOK_EQEQ:
+        
+            return left == right;
+        
+        case TOK_NOTEQ:
+        
+            return left != right;
+        
+        default:
+        
+            return left != 0.0L;
+    
+    }
+
+}
+
+static void emit_statement_test_eax_jump_if_false (int label) {
+
+    if (!state->ofp) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        fprintf (state->ofp, "    test eax, eax\n");
+        fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jz L%d\n" : "    jz .L%d\n"), label);
+    
+    } else {
+    
+        fprintf (state->ofp, "    testl %%eax, %%eax\n");
+        fprintf (state->ofp, "    jz .L%d\n", label);
+    
+    }
+
+}
+
+static void emit_statement_test_pair_jump_if_false (const char *lo, const char *hi, int label) {
+
+    int nonzero_label;
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    nonzero_label = anon_label++;
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        fprintf (state->ofp, "    test %s, %s\n", hi, hi);
+        fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), nonzero_label);
+        fprintf (state->ofp, "    test %s, %s\n", lo, lo);
+        fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jz L%d\n" : "    jz .L%d\n"), label);
+        fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), nonzero_label);
+    
+    } else {
+    
+        fprintf (state->ofp, "    testl %%%s, %%%s\n", hi, hi);
+        fprintf (state->ofp, "    jnz .L%d\n", nonzero_label);
+        fprintf (state->ofp, "    testl %%%s, %%%s\n", lo, lo);
+        fprintf (state->ofp, "    jz .L%d\n", label);
+        fprintf (state->ofp, ".L%d:\n", nonzero_label);
+    
+    }
+
+}
+
+static int statement_condition_constant_known = 0;
+static int statement_condition_constant_value = 0;
+static int statement_ends_control_flow = 0;
+
+static void parse_statement (void);
+static void parse_statement_suppressed (void);
+static void emit_statement_jump_if_false (int label);
+
+static void replay_tmp_file_to_output (FILE *tmp, FILE *out) {
+
+    int ch;
+    
+    if (!tmp || !out) {
+        return;
+    }
+    
+    fflush (tmp);
+    fseek (tmp, 0, SEEK_SET);
+    
+    while ((ch = fgetc (tmp)) != EOF) {
+        fputc (ch, out);
+    }
+
+}
+
+static void parse_for_header_expression_until (enum token_kind end_token) {
+
+    char *name;
+    int global_index;
+    
+    const char *name_start, *name_caret;
+    unsigned long name_line;
+    
+    enum token_kind op;
+    struct local_symbol *lhs;
+    
+    int lhs_size;
+    int lhs_is_floating;
+    
+#define     FINISH_FOR_HEADER_EXPR(free_name)                                   \
+    do {                                                                        \
+        if (tok.kind == TOK_COMMA) {                                            \
+            get_token ();                                                       \
+            if (free_name) {                                                    \
+                free (name);                                                    \
+            }                                                                   \
+            parse_for_header_expression_until (end_token);                      \
+            return;                                                             \
+        }                                                                       \
+        if (tok.kind != end_token) {                                            \
+            skip_balanced_until (end_token, TOK_EOF, TOK_EOF);                  \
+        }                                                                       \
+        expect (end_token, end_token == TOK_SEMI ? ";" : ")");                  \
+        if (free_name) {                                                        \
+            free (name);                                                        \
+        }                                                                       \
+        return;                                                                 \
+    } while (0)
+    
+    if (tok.kind == end_token) {
+        return;
+    }
+    
+    if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+    
+        op = tok.kind;
+        get_token ();
+        
+        if (tok.kind == TOK_IDENT) {
+        
+            name = xstrdup (tok.ident);
+            name_start = tok.start;
+            name_caret = tok.caret;
+            name_line = get_line_number ();
+            get_token ();
+            
+            lhs = find_local_symbol (name);
+            emit_incdec_symbol_now (lhs, name, op, name_line, name_start, name_caret);
+            free (name);
+        
+        } else {
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after %s", op == TOK_INCR ? "++" : "--");
+        }
+        
+        FINISH_FOR_HEADER_EXPR (0);
+    
+    }
+    
+    if (tok.kind != TOK_IDENT) {
+    
+        if (state->ofp) {
+            emit_load_assignment_rhs_expression_to_reg ("eax");
+        } else {
+            skip_balanced_until (end_token, TOK_EOF, TOK_EOF);
+        }
+        
+        FINISH_FOR_HEADER_EXPR (0);
+    
+    }
+    
+    name = xstrdup (tok.ident);
+    name_start = tok.start;
+    name_caret = tok.caret;
+    name_line = get_line_number ();
+    
+    get_token ();
+    
+    if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+    
+        op = tok.kind;
+        get_token ();
+        
+        lhs = find_local_symbol (name);
+        emit_incdec_symbol_now (lhs, name, op, name_line, name_start, name_caret);
+        
+        FINISH_FOR_HEADER_EXPR (1);
+    
+    }
+    
+    if (tok.kind == TOK_LPAREN) {
+    
+        if (!find_local_symbol (name)) {
+            ensure_global_function_symbol (name, name_start, name_caret, name_line);
+        }
+        
+        emit_call_identifier_to_reg_now (name, "eax", name_start, name_caret, name_line);
+        FINISH_FOR_HEADER_EXPR (1);
+    
+    }
+    
+    if (!is_assignment_operator (tok.kind)) {
+    
+        if (!find_local_symbol (name) && find_global_symbol (name) < 0) {
+        
+            int64_s ignored;
+            
+            if (!resolve_enum_constant (name, &ignored)) {
+                report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+            }
+        
+        }
+        
+        if (tok.kind == TOK_COMMA) {
+        
+            get_token ();
+            free (name);
+            
+            parse_for_header_expression_until (end_token);
+            return;
+        
+        }
+        
+        skip_balanced_until (end_token, TOK_EOF, TOK_EOF);
+        expect (end_token, end_token == TOK_SEMI ? ";" : ")");
+        
+        free (name);
+        return;
+    
+    }
+    
+    op = tok.kind;
+    get_token ();
+    
+    lhs = find_local_symbol (name);
+    global_index = find_global_symbol (name);
+    
+    if (!lhs && global_index < 0) {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+        FINISH_FOR_HEADER_EXPR (1);
+    
+    }
+    
+    if (state->ofp) {
+    
+        lhs_size = lhs ? lhs->size : get_global_symbol_size (name);
+        lhs_is_floating = lhs ? lhs->is_floating : get_global_symbol_floating (name);
+        
+        if (lhs_is_floating) {
+        
+            if (!floating_assignment_operator_supported_now (op)) {
+            
+                report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "invalid operands to floating assignment operator");
+                skip_balanced_until (end_token, TOK_EOF, TOK_EOF);
+            
+            } else {
+            
+                if (op == TOK_ASSIGN) {
+                    emit_load_floating_rhs_expression_now (lhs_size);
+                } else {
+                
+                    emit_load_floating_symbol_now (lhs, name, lhs_size);
+                    emit_load_floating_rhs_expression_now (lhs_size);
+                    emit_floating_binary_now (op);
+                
+                }
+                
+                emit_store_floating_symbol_now (lhs, name, lhs_size);
+            
+            }
+        
+        } else if (lhs_size == (DATA_LLONG & 0x1f)) {
+        
+            if (op == TOK_ASSIGN) {
+                emit_load_assignment_rhs_expression_to_pair ("eax", "edx", lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name));
+            } else {
+            
+                if (lhs) {
+                
+                    if (lhs->is_static && lhs->static_label) {
+                        emit_load_global64_to_pair ("eax", "edx", lhs->static_label);
+                    } else {
+                        emit_load_local64_to_pair (lhs->offset, "eax", "edx");
+                    }
+                
+                } else {
+                    emit_load_global64_to_pair ("eax", "edx", name);
+                }
+                
+                /*
+                 * Compound assignments need the complete RHS expression.
+                 * Using emit_load_assignment_rhs_to_pair() only consumes one
+                 * primary operand, so e.g.
+                 *
+                 *     final_value += symbol->frag->address + left_value;
+                 *
+                 * leaves the second + operand for the statement parser and
+                 * reports "expected ;".  Evaluate the full RHS in eax:edx,
+                 * copy it to the RHS pair ebx:ecx, then restore the original
+                 * LHS value before applying the compound operator.
+                 */
+                emit_push_reg_now ("eax");
+                emit_push_reg_now ("edx");
+                
+                emit_load_assignment_rhs_expression_to_pair ("eax", "edx", lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name));
+                
+                emit_mov_reg_to_reg_now ("ebx", "eax");
+                emit_mov_reg_to_reg_now ("ecx", "edx");
+                
+                emit_pop_reg_now ("edx");
+                emit_pop_reg_now ("eax");
+                
+                emit_preserve_assignment64_regs (op);
+                emit_assignment_binary_op64 (op, lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name));
+                emit_restore_assignment64_regs (op);
+            
+            }
+            
+            if (lhs) {
+            
+                if (lhs->is_static && lhs->static_label) {
+                    emit_store_pair_to_global64 (lhs->static_label, "eax", "edx");
+                } else {
+                    emit_store_pair_to_local64 (lhs->offset, "eax", "edx");
+                }
+            
+            } else {
+                emit_store_pair_to_global64 (name, "eax", "edx");
+            }
+        
+        } else {
+        
+            if (op == TOK_ASSIGN) {
+                emit_load_assignment_rhs_expression_to_reg ("eax");
+            } else {
+            
+                if (lhs) {
+                
+                    if (lhs->is_static && lhs->static_label) {
+                        emit_load_global_to_reg ("eax", lhs->static_label, lhs->size);
+                    } else {
+                        emit_load_local_to_reg ("eax", lhs->offset, lhs->size);
+                    }
+                
+                } else {
+                    emit_load_global_to_reg ("eax", name, lhs_size);
+                }
+                
+                emit_load_assignment_rhs_to_reg ("edx");
+                emit_assignment_binary_op (op, lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name));
+            
+            }
+            
+            if (lhs) {
+            
+                if (lhs->is_static && lhs->static_label) {
+                    emit_store_reg_to_global (lhs->static_label, lhs->size, "eax");
+                } else {
+                    emit_store_reg_to_local (lhs->offset, lhs->size, "eax");
+                }
+            
+            } else {
+                emit_store_reg_to_global (name, lhs_size, "eax");
+            }
+        
+        }
+    
+    } else {
+        skip_balanced_until (end_token, TOK_EOF, TOK_EOF);
+    }
+    
+    FINISH_FOR_HEADER_EXPR (1);
+
+#undef      FINISH_FOR_HEADER_EXPR
+
+}
+
+static void parse_for_statement (void) {
+
+    int loop_label = anon_label++;
+    int body_label = anon_label++;
+    int continue_label = anon_label++;
+    int break_label = anon_label++;
+    int old_break_label = current_break_label;
+    int old_continue_label = current_continue_label;
+    
+    long old_break_cleanup_base = current_break_cleanup_base;
+    long old_continue_cleanup_base = current_continue_cleanup_base;
+    
+    int cond_known = 0;
+    int cond_value = 1;
+    
+    FILE *saved_ofp;
+    FILE *step_tmp = 0;
+    
+    get_token ();
+    expect (TOK_LPAREN, "(");
+    
+    if (tok.kind == TOK_SEMI) {
+        get_token ();
+    } else {
+        parse_for_header_expression_until (TOK_SEMI);
+    }
+    
+    emit_statement_label (loop_label);
+    
+    if (tok.kind == TOK_SEMI) {
+        get_token ();
+    } else {
+    
+        emit_statement_jump_if_false (break_label);
+        
+        cond_known = statement_condition_constant_known;
+        cond_value = statement_condition_constant_value;
+        
+        if (tok.kind != TOK_SEMI) {
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        }
+        
+        expect (TOK_SEMI, ";");
+    
+    }
+    
+    if (tok.kind == TOK_RPAREN) {
+        get_token ();
+    } else {
+    
+        if (state->ofp) {
+            step_tmp = tmpfile ();
+        }
+        
+        if (step_tmp) {
+        
+            saved_ofp = state->ofp;
+            
+            state->ofp = step_tmp;
+            parse_for_header_expression_until (TOK_RPAREN);
+            
+            state->ofp = saved_ofp;
+        
+        } else {
+            parse_for_header_expression_until (TOK_RPAREN);
+        }
+    
+    }
+    
+    current_break_label = break_label;
+    current_continue_label = continue_label;
+    current_break_cleanup_base = current_block_cleanup_bytes;
+    current_continue_cleanup_base = current_block_cleanup_bytes;
+    
+    if (cond_known && !cond_value) {
+        parse_statement_suppressed ();
+    } else {
+    
+        emit_statement_jump (body_label);
+        emit_statement_label (body_label);
+        
+        parse_statement ();
+        emit_statement_label (continue_label);
+        
+        if (step_tmp) {
+        
+            replay_tmp_file_to_output (step_tmp, state->ofp);
+            fclose (step_tmp);
+            
+            step_tmp = 0;
+        
+        }
+        
+        emit_statement_jump (loop_label);
+    
+    }
+    
+    if (step_tmp) {
+        fclose (step_tmp);
+    }
+    
+    emit_statement_label (break_label);
+    
+    current_break_label = old_break_label;
+    current_continue_label = old_continue_label;
+    current_break_cleanup_base = old_break_cleanup_base;
+    current_continue_cleanup_base = old_continue_cleanup_base;
+    
+    statement_ends_control_flow = 0;
+
+}
+
+static void parse_while_statement (void) {
+
+    int loop_label = anon_label++;
+    int body_label = anon_label++;
+    int break_label = anon_label++;
+    int old_break_label = current_break_label;
+    int old_continue_label = current_continue_label;
+    
+    long old_break_cleanup_base = current_break_cleanup_base;
+    long old_continue_cleanup_base = current_continue_cleanup_base;
+    
+    int cond_known;
+    int cond_value;
+    
+    get_token ();
+    expect (TOK_LPAREN, "(");
+    
+    emit_statement_label (loop_label);
+    emit_statement_jump_if_false (break_label);
+    
+    cond_known = statement_condition_constant_known;
+    cond_value = statement_condition_constant_value;
+    
+    if (tok.kind != TOK_RPAREN) {
+        skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+    }
+    
+    expect (TOK_RPAREN, ")");
+    
+    current_break_label = break_label;
+    current_continue_label = loop_label;
+    current_break_cleanup_base = current_block_cleanup_bytes;
+    current_continue_cleanup_base = current_block_cleanup_bytes;
+    
+    if (cond_known && !cond_value) {
+        parse_statement_suppressed ();
+    } else {
+    
+        emit_statement_jump (body_label);
+        emit_statement_label (body_label);
+        
+        parse_statement ();
+        
+        if (!statement_ends_control_flow) {
+            emit_statement_jump (loop_label);
+        }
+    
+    }
+    
+    emit_statement_label (break_label);
+    
+    current_break_label = old_break_label;
+    current_continue_label = old_continue_label;
+    current_break_cleanup_base = old_break_cleanup_base;
+    current_continue_cleanup_base = old_continue_cleanup_base;
+    
+    statement_ends_control_flow = 0;
+
+}
+
+static void parse_do_statement (void) {
+
+    int body_label = anon_label++;
+    int cond_label = anon_label++;
+    int break_label = anon_label++;
+    int old_break_label = current_break_label;
+    int old_continue_label = current_continue_label;
+    
+    long old_break_cleanup_base = current_break_cleanup_base;
+    long old_continue_cleanup_base = current_continue_cleanup_base;
+    
+    int cond_known = 0;
+    int cond_value = 0;
+    
+    get_token ();
+    
+    current_break_label = break_label;
+    current_continue_label = cond_label;
+    current_break_cleanup_base = current_block_cleanup_bytes;
+    current_continue_cleanup_base = current_block_cleanup_bytes;
+    
+    emit_statement_jump (body_label);
+    
+    emit_statement_label (body_label);
+    parse_statement ();
+    
+    emit_statement_label (cond_label);
+    
+    if (_accept (TOK_WHILE)) {
+    
+        expect (TOK_LPAREN, "(");
+        emit_statement_jump_if_false (break_label);
+        
+        cond_known = statement_condition_constant_known;
+        cond_value = statement_condition_constant_value;
+        
+        if (tok.kind != TOK_RPAREN) {
+            skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+        }
+        
+        expect (TOK_RPAREN, ")");
+        expect (TOK_SEMI, ";");
+        
+        if (!cond_known || cond_value) {
+            emit_statement_jump (body_label);
+        }
+    
+    } else {
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected while after do statement");
+    }
+    
+    emit_statement_label (break_label);
+    
+    current_break_label = old_break_label;
+    current_continue_label = old_continue_label;
+    current_break_cleanup_base = old_break_cleanup_base;
+    current_continue_cleanup_base = old_continue_cleanup_base;
+    
+    statement_ends_control_flow = 0;
+
+}
+
+static int source_parenthesized_condition_has_logical_now (void) {
+
+    const char *p = tok.caret;
+    int depth = 0;
+    
+    if (!p || *p != '(') {
+        return 0;
+    }
+    
+    for (; *p; p++) {
+    
+        if (*p == '(') {
+        
+            depth++;
+            continue;
+        
+        }
+        
+        if (*p == ')') {
+        
+            depth--;
+            
+            if (depth == 0) {
+                return 0;
+            }
+            
+            continue;
+        
+        }
+        
+        if (depth == 1 && ((*p == '&' && p[1] == '&') || (*p == '|' && p[1] == '|'))) {
+            return 1;
+        }
+        
+        if (*p == '"' || *p == '\'') {
+        
+            int quote = *p++;
+            
+            while (*p && *p != quote) {
+            
+                if (*p == '\\' && p[1]) {
+                    p++;
+                }
+                
+                p++;
+            
+            }
+            
+            if (!*p) {
+                return 0;
+            }
+        
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static int statement_condition_emit_logical_tail (int label) {
+
+    int skip_label;
+    
+    if (tok.kind == TOK_LOGAND) {
+    
+        get_token ();
+        
+        emit_statement_jump_if_false (label);
+        return 1;
+    
+    }
+    
+    if (tok.kind == TOK_LOGOR) {
+    
+        skip_label = anon_label++;
+        get_token ();
+        
+        if (state->ofp) {
+        
+            if (state->syntax & ASM_SYNTAX_INTEL) {
+                fprintf (state->ofp, "    test eax, eax\n");
+                fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), skip_label);
+            } else {
+                fprintf (state->ofp, "    testl %%eax, %%eax\n");
+                fprintf (state->ofp, "    jnz .L%d\n", skip_label);
+            }
+        
+        }
+        
+        emit_statement_jump_if_false (label);
+        emit_statement_label (skip_label);
+        
+        return 1;
+    
+    }
+    
+    return 0;
+
+}
+
+static int statement_condition_fold_logical_tail (int label) {
+
+    enum token_kind logop;
+    
+    while (tok.kind == TOK_LOGOR || tok.kind == TOK_LOGAND) {
+    
+        logop = tok.kind;
+        
+        /*
+         * Constant short-circuit cases can discard the rest of the condition.
+         *
+         *     true  || anything  -> true
+         *     false && anything  -> false
+         *
+         * Leave tok on ')' so the caller's expect(TOK_RPAREN) still works.
+         */
+        if ((logop == TOK_LOGOR && statement_condition_constant_value) || (logop == TOK_LOGAND && !statement_condition_constant_value)) {
+        
+            skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+            return 1;
+        
+        }
+        
+        /*
+         * The left side is non-decisive:
+         *
+         *     false || rhs  -> rhs
+         *     true  && rhs  -> rhs
+         *
+         * So consume the logical operator and emit/fold the RHS normally.
+         */
+        get_token ();
+        
+        statement_condition_constant_known = 0;
+        statement_condition_constant_value = 0;
+        
+        emit_statement_jump_if_false (label);
+        return 1;
+    
+    }
+    
+    return 0;
+
+}
+
+static int source_starts_with_parenthesized_assignment_at (const char *p) {
+
+    int parens = 0;
+    
+    if (!p || *p != '(') {
+        return 0;
+    }
+    
+    while (*p == '(') {
+    
+        parens++;
+        p++;
+        
+        while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+            p++;
+        }
+    
+    }
+    
+    if (parens <= 0 || !((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) {
+        return 0;
+    }
+    
+    p++;
+    
+    while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+        p++;
+    }
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    while (parens > 0 && *p == ')') {
+    
+        parens--;
+        p++;
+        
+        while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+            p++;
+        }
+    
+    }
+    
+    /*
+     * A condition assignment normally looks like:
+     *
+     *     if ((x = y))
+     *
+     * At this point the scanner has consumed the opening paren(s) and the
+     * identifier.  For the common form above, the assignment operator appears
+     * before the closing paren, so `parens' is still non-zero.  The previous
+     * guard returned 0 before checking for '=', which made the statement
+     * condition parser treat the expression as just `(x' and then report a
+     * false "expected )" at the assignment operator.  Check for assignment
+     * operators first, while still rejecting comparison operators.
+     */
+    if (*p == '=') {
+        return p[1] != '=';
+    }
+    
+    if (parens != 0) {
+        return 0;
+    }
+    
+    if ((*p == '+' || *p == '-' || *p == '*' || *p == '/' || *p == '%' ||
+         *p == '&' || *p == '|' || *p == '^') && p[1] == '=') {
+        return 1;
+    }
+    
+    if ((*p == '<' && p[1] == '<' && p[2] == '=') ||
+        (*p == '>' && p[1] == '>' && p[2] == '=')) {
+        return 1;
+    }
+    
+    return 0;
+
+}
+
+static int source_starts_with_parenthesized_assignment_now (void) {
+
+    /**
+     * Only test the current token text.  tok.start can point at the start of
+     * the whole source line, so scanning the whole line misclassifies a for
+     * condition such as:
+     *
+     *     for (len = 0; (len < max_len) && str[len]; len++);
+     *
+     * as a parenthesized assignment because of the earlier ``(len = 0`` in the
+     * same line.  This helper is a look-ahead guard; it must not match any
+     * parenthesized assignment except the one beginning at the current token.
+     */
+    if (source_starts_with_parenthesized_assignment_at (tok.caret)) {
+        return 1;
+    }
+    
+    return 0;
+
+}
+
+static int parenthesized_assignment_open_parens = 0;
+static int parenthesized_assignment_closed_parens = 0;
+
+static void consume_parenthesized_assignment_remaining_closes (void) {
+
+    while (parenthesized_assignment_closed_parens < parenthesized_assignment_open_parens && tok.kind == TOK_RPAREN) {
+    
+        get_token ();
+        parenthesized_assignment_closed_parens++;
+    
+    }
+
+}
+
+static int emit_load_parenthesized_assignment_expression_to_reg_now (const char *reg, int *out_is_unsigned) {
+
+    char *name;
+    int global_index;
+    
+    const char *name_start, *name_caret;
+    unsigned long name_line;
+    
+    enum token_kind op;
+    struct local_symbol *lhs;
+    
+    int lhs_size;
+    int lhs_is_floating;
+    int lhs_is_unsigned;
+    int paren_count = 0;
+    
+    parenthesized_assignment_open_parens = 0;
+    parenthesized_assignment_closed_parens = 0;
+    
+    if (out_is_unsigned) {
+        *out_is_unsigned = 0;
+    }
+    
+    if (tok.kind != TOK_LPAREN || !source_starts_with_parenthesized_assignment_now ()) {
+        return 0;
+    }
+    
+    while (tok.kind == TOK_LPAREN) {
+    
+        paren_count++;
+        get_token ();
+    
+    }
+    
+    if (tok.kind != TOK_IDENT) {
+        return 0;
+    }
+    
+    parenthesized_assignment_open_parens = paren_count;
+    
+    name = xstrdup (tok.ident);
+    name_start = tok.start;
+    name_caret = tok.caret;
+    name_line = get_line_number ();
+    
+    get_token ();
+    
+    while (parenthesized_assignment_closed_parens < parenthesized_assignment_open_parens && tok.kind == TOK_RPAREN) {
+    
+        get_token ();
+        parenthesized_assignment_closed_parens++;
+    
+    }
+    
+    if (tok.kind == TOK_DOT || tok.kind == TOK_ARROW) {
+    
+        enum token_kind member_op = tok.kind;
+        char *member;
+        
+        const char *member_start;
+        const char *member_caret;
+        
+        unsigned long member_line;
+        
+        int member_offset = 0;
+        int member_size = DATA_INT & 0x1f;
+        
+        get_token ();
+        
+        member_start = tok.start;
+        member_caret = tok.caret;
+        member_line = get_line_number ();
+        
+        if (tok.kind != TOK_IDENT) {
+        
+            report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+            
+            free (name);
+            return 1;
+        
+        }
+        
+        member = xstrdup (tok.ident);
+        get_token ();
+        
+        if (!find_member_info (member, &member_offset, &member_size)) {
+        
+            report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+            
+            free (member);
+            free (name);
+            
+            return 1;
+        
+        }
+        
+        free (member);
+        
+        if (!is_assignment_operator (tok.kind)) {
+        
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+            expect (TOK_SEMI, ";");
+            
+            free (name);
+            return 1;
+        
+        }
+        
+        op = tok.kind;
+        get_token ();
+        
+        lhs = find_local_symbol (name);
+        global_index = find_global_symbol (name);
+        
+        if (!lhs && global_index < 0) {
+        
+            report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+            
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+            expect (TOK_SEMI, ";");
+            
+            free (name);
+            return 1;
+        
+        }
+        
+        if (state->ofp) {
+        
+            if (member_op == TOK_ARROW) {
+            
+                if (lhs) {
+                
+                    if (lhs->is_static && lhs->static_label) {
+                        emit_load_global_to_reg ("edx", lhs->static_label, DATA_PTR);
+                    } else {
+                        emit_load_local_to_reg ("edx", lhs->offset, DATA_PTR);
+                    }
+                
+                } else {
+                    emit_load_global_to_reg ("edx", name, DATA_PTR);
+                }
+            
+            } else {
+            
+                if (lhs) {
+                
+                    if (lhs->is_static && lhs->static_label) {
+                        emit_load_address_to_reg_now ("edx", lhs->static_label);
+                    } else {
+                        emit_load_local_address_to_reg_now ("edx", lhs->offset);
+                    }
+                
+                } else {
+                    emit_load_address_to_reg_now ("edx", name);
+                }
+            
+            }
+            
+            if (op == TOK_ASSIGN) {
+            
+                emit_push_reg_now ("edx");
+                emit_load_assignment_rhs_expression_to_reg ("eax");
+                emit_pop_reg_now ("edx");
+            
+            } else {
+            
+                emit_load_member_from_addr_reg_now ("eax", "edx", member_offset, member_size);
+                emit_push_reg_now ("edx");
+                emit_push_reg_now ("eax");
+                
+                if (tok.kind == TOK_TILDE) {
+                
+                    int64_s rhs_const;
+                    get_token ();
+                    
+                    if (tok.kind == TOK_LPAREN) {
+                    
+                        get_token ();
+                        
+                        rhs_const = const64_from_current_foldable_expr ();
+                        expect (TOK_RPAREN, ")");
+                    
+                    } else {
+                        rhs_const = const64_from_current_foldable_expr ();
+                    }
+                    
+                    rhs_const.low = (~rhs_const.low) & U32_MASK;
+                    rhs_const.high = (~rhs_const.high) & U32_MASK;
+                    
+                    emit_load_const32_to_reg_now ("edx", rhs_const);
+                
+                } else if (const_integer_expr_text_is_foldable_now (tok.start) || const_integer_expr_text_is_foldable_now (tok.caret)) {
+                
+                    int64_s rhs_const = const64_from_current_foldable_expr ();
+                    emit_load_const32_to_reg_now ("edx", rhs_const);
+                
+                } else {
+                    emit_load_assignment_rhs_expression_to_reg ("edx");
+                }
+                
+                emit_pop_reg_now ("eax");
+                emit_assignment_binary_op (op, 0);
+                emit_pop_reg_now ("edx");
+            
+            }
+            
+            emit_store_member_to_addr_reg_now ("edx", member_offset, "eax", member_size);
+        
+        } else {
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        }
+        
+        expect_semi_or_recover ();
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    if (!is_assignment_operator (tok.kind)) {
+    
+        free (name);
+        return 0;
+    
+    }
+    
+    op = tok.kind;
+    get_token ();
+    
+    lhs = find_local_symbol (name);
+    global_index = find_global_symbol (name);
+    
+    if (!lhs && global_index < 0) {
+    
+        report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+        
+        skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+        expect (TOK_RPAREN, ")");
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    lhs_size = lhs ? lhs->size : get_global_symbol_size (name);
+    lhs_is_floating = lhs ? lhs->is_floating : get_global_symbol_floating (name);
+    lhs_is_unsigned = lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name);
+    
+    if (out_is_unsigned) {
+        *out_is_unsigned = lhs_is_unsigned;
+    }
+    
+    if (state->ofp) {
+    
+        if (lhs_is_floating) {
+        
+            report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "floating assignment expression in condition not implemented");
+            skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+        
+        } else if (lhs_size == (DATA_LLONG & 0x1f)) {
+        
+            if (op == TOK_ASSIGN) {
+                emit_load_assignment_rhs_expression_to_pair ("eax", "edx", lhs_is_unsigned);
+            } else {
+            
+                if (lhs) {
+                
+                    if (lhs->is_static && lhs->static_label) {
+                        emit_load_global64_to_pair ("eax", "edx", lhs->static_label);
+                    } else {
+                        emit_load_local64_to_pair (lhs->offset, "eax", "edx");
+                    }
+                
+                } else {
+                    emit_load_global64_to_pair ("eax", "edx", name);
+                }
+                
+                /*
+                 * Compound assignments need the complete RHS expression.
+                 * Using emit_load_assignment_rhs_to_pair() only consumes one
+                 * primary operand, so e.g.
+                 *
+                 *     final_value += symbol->frag->address + left_value;
+                 *
+                 * leaves the second + operand for the statement parser and
+                 * reports "expected ;".  Evaluate the full RHS in eax:edx,
+                 * copy it to the RHS pair ebx:ecx, then restore the original
+                 * LHS value before applying the compound operator.
+                 */
+                emit_push_reg_now ("eax");
+                emit_push_reg_now ("edx");
+                
+                emit_load_assignment_rhs_expression_to_pair ("eax", "edx", lhs_is_unsigned);
+                
+                emit_mov_reg_to_reg_now ("ebx", "eax");
+                emit_mov_reg_to_reg_now ("ecx", "edx");
+                
+                emit_pop_reg_now ("edx");
+                emit_pop_reg_now ("eax");
+                
+                emit_preserve_assignment64_regs (op);
+                emit_assignment_binary_op64 (op, lhs_is_unsigned);
+                emit_restore_assignment64_regs (op);
+            
+            }
+            
+            if (lhs) {
+            
+                if (lhs->is_static && lhs->static_label) {
+                    emit_store_pair_to_global64 (lhs->static_label, "eax", "edx");
+                } else {
+                    emit_store_pair_to_local64 (lhs->offset, "eax", "edx");
+                }
+            
+            } else {
+                emit_store_pair_to_global64 (name, "eax", "edx");
+            }
+        
+        } else {
+        
+            if (op == TOK_ASSIGN) {
+                emit_load_assignment_rhs_expression_to_reg (reg);
+            } else {
+            
+                if (lhs) {
+                
+                    if (lhs->is_static && lhs->static_label) {
+                        emit_load_global_to_reg (reg, lhs->static_label, lhs->size);
+                    } else {
+                        emit_load_local_to_reg (reg, lhs->offset, lhs->size);
+                    }
+                
+                } else {
+                    emit_load_global_to_reg (reg, name, lhs_size);
+                }
+                
+                emit_load_assignment_rhs_to_reg ("edx");
+                
+                if (strcmp (reg, "eax") != 0 && state->ofp) {
+                
+                    if (state->syntax & ASM_SYNTAX_INTEL) {
+                        fprintf (state->ofp, "    mov eax, %s\n", reg);
+                    } else {
+                        fprintf (state->ofp, "    movl %%%s, %%eax\n", reg);
+                    }
+                
+                }
+                
+                emit_assignment_binary_op (op, lhs_is_unsigned);
+                
+                if (strcmp (reg, "eax") != 0 && state->ofp) {
+                
+                    if (state->syntax & ASM_SYNTAX_INTEL) {
+                        fprintf (state->ofp, "    mov %s, eax\n", reg);
+                    } else {
+                        fprintf (state->ofp, "    movl %%eax, %%%s\n", reg);
+                    }
+                
+                }
+            
+            }
+            
+            if (lhs) {
+            
+                if (lhs->is_static && lhs->static_label) {
+                    emit_store_reg_to_global (lhs->static_label, lhs->size, reg);
+                } else {
+                    emit_store_reg_to_local (lhs->offset, lhs->size, reg);
+                }
+            
+            } else {
+                emit_store_reg_to_global (name, lhs_size, reg);
+            }
+        
+        }
+    
+    } else {
+        skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+    }
+    
+    consume_parenthesized_assignment_remaining_closes ();
+    
+    free (name);
+    return 1;
+
+}
+
+static void emit_statement_jump_if_false (int label) {
+
+    enum token_kind op;
+    int is_unsigned;
+    
+    flush_pending_statement_labels ();
+    
+    statement_condition_constant_known = 0;
+    statement_condition_constant_value = 0;
+    
+    if (tok.kind == TOK_LPAREN && source_parenthesized_condition_has_logical_now ()) {
+    
+        /*
+         * Parse the whole parenthesized logical expression as an expression.
+         * The older recursive condition parser stopped after the first operand
+         * of nested logical groups such as:
+         *
+         *     a && (b || c || d)
+         *
+         * and then the outer caller immediately expected the closing ')',
+         * leaving the inner '||' tokens unconsumed and reporting a false
+         * "expected )" at the first member expression.
+         */
+        get_token ();
+        
+        emit_load_assignment_rhs_expression_to_reg ("eax");
+        expect (TOK_RPAREN, ")");
+        
+        if (tok.kind == TOK_LOGOR) {
+        
+            int skip_label = anon_label++;
+            
+            if (state->ofp) {
+            
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                
+                    fprintf (state->ofp, "    test eax, eax\n");
+                    fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), skip_label);
+                
+                } else {
+                
+                    fprintf (state->ofp, "    testl %%eax, %%eax\n");
+                    fprintf (state->ofp, "    jnz .L%d\n", skip_label);
+                
+                }
+            
+            }
+            
+            get_token ();
+            
+            emit_statement_jump_if_false (label);
+            emit_statement_label (skip_label);
+            
+            return;
+        
+        }
+        
+        emit_statement_test_eax_jump_if_false (label);
+        statement_condition_emit_logical_tail (label);
+        
+        return;
+    
+    }
+    
+    if (emit_load_parenthesized_assignment_expression_to_reg_now ("eax", &is_unsigned)) {
+    
+        while (is_arithmetic_binary_operator (tok.kind)) {
+        
+            op = tok.kind;
+            get_token ();
+            
+            if (rhs_current_operand_is_unsigned_now ()) {
+                is_unsigned = 1;
+            }
+            
+            emit_load_assignment_rhs_to_reg ("edx");
+            emit_assignment_binary_op (op, is_unsigned);
+        
+        }
+        
+        if (token_is_statement_compare_operator (tok.kind)) {
+        
+            op = tok.kind;
+            get_token ();
+            
+            if (rhs_current_operand_is_unsigned_now ()) {
+                is_unsigned = 1;
+            }
+            
+            emit_load_assignment_rhs_expression_to_reg ("edx");
+            consume_parenthesized_assignment_remaining_closes ();
+            emit_statement_cmp_eax_edx_jump_if_false (op, is_unsigned, label);
+            
+            return;
+        
+        }
+        
+        consume_parenthesized_assignment_remaining_closes ();
+        emit_statement_test_eax_jump_if_false (label);
+        return;
+    
+    }
+    
+    if (statement_condition_starts_with_ident_call_now ()) {
+    
+        is_unsigned = rhs_current_operand_is_unsigned_now ();
+        emit_load_assignment_rhs_expression_to_reg ("eax");
+        
+        if (token_is_statement_compare_operator (tok.kind)) {
+        
+            op = tok.kind;
+            get_token ();
+            
+            if (rhs_current_operand_is_unsigned_now ()) {
+                is_unsigned = 1;
+            }
+            
+            emit_load_assignment_rhs_expression_to_reg ("edx");
+            emit_statement_cmp_eax_edx_jump_if_false (op, is_unsigned, label);
+            statement_condition_emit_logical_tail (label);
+            
+            return;
+        
+        }
+        
+        emit_statement_test_eax_jump_if_false (label);
+        statement_condition_emit_logical_tail (label);
+        
+        return;
+    
+    }
+    
+    /*
+     * Fold simple constant conditions at parse time.  This avoids emitting:
+     *
+     *     mov eax, 1
+     *     test eax, eax
+     *     jz ...
+     *
+     * for things like if (1), and avoids cmp/jcc for things like
+     * if (1 < 10), if (5 > 2), if (1ULL < 10LL), etc.
+     */
+    if (token_is_const_floating_condition_operand_now () || rhs_current_operand_is_floating_now ()) {
+    
+        long double left_float = 0.0L;
+        
+        int left_float_constant = 0;
+        int float_size = DATA_DOUBLE & 0x1f;
+        
+        if (token_is_const_floating_condition_operand_now ()) {
+        
+            left_float = parse_floating_const_expr_value_now ();
+            left_float_constant = 1;
+        
+        } else {
+            emit_load_floating_rhs_expression_now (float_size);
+        }
+        
+        if (token_is_statement_compare_operator (tok.kind)) {
+        
+            op = tok.kind;
+            get_token ();
+            
+            if (left_float_constant && (token_is_floating_constant_now () || token_is_const_condition_operand_now ())) {
+            
+                long double right_float;
+                
+                if (token_is_floating_constant_now ()) {
+                    right_float = parse_floating_const_expr_value_now ();
+                } else {
+                
+                    int64_s right_int = const64_from_current_operand ();
+                    
+                    right_float = (long double) right_int.low;
+                    
+                    if (right_int.high != 0) {
+                        right_float += ((long double) right_int.high) * 4294967296.0L;
+                    }
+                
+                }
+                
+                statement_condition_constant_known = 1;
+                statement_condition_constant_value = statement_compare_floating_const_true (left_float, op, right_float) ? 1 : 0;
+                
+                if (statement_condition_fold_logical_tail (label)) {
+                    return;
+                }
+                
+                return;
+            
+            }
+            
+            if (left_float_constant) {
+                emit_load_floating_ld_now (float_size, left_float);
+            }
+            
+            emit_load_floating_rhs_expression_now (float_size);
+            emit_statement_floating_compare_jump_if_false (op, label);
+            
+            return;
+        
+        }
+        
+        if (left_float_constant) {
+        
+            statement_condition_constant_known = 1;
+            statement_condition_constant_value = left_float != 0.0L ? 1 : 0;
+            
+            if (statement_condition_fold_logical_tail (label)) {
+                return;
+            }
+            
+            return;
+        
+        }
+        
+        emit_load_floating_ld_now (float_size, 0.0L);
+        emit_statement_floating_compare_jump_if_false (TOK_NOTEQ, label);
+        
+        return;
+    
+    }
+    
+    if (token_is_const_condition_operand_now () || const_integer_expr_text_is_foldable_now (tok.caret) || const_integer_expr_text_is_foldable_now (tok.start)) {
+    
+        int fold_whole_expr = const_integer_expr_text_is_foldable_now (tok.caret) || const_integer_expr_text_is_foldable_now (tok.start);
+        int left_unsigned = rhs_current_operand_is_unsigned_now ();
+        
+        int64_s left;
+        
+        if (fold_whole_expr) {
+            left = const64_from_current_foldable_expr ();
+        } else {
+            left = const64_from_current_operand ();
+        }
+        
+        if (is_arithmetic_binary_operator (tok.kind)) {
+        
+            emit_statement_const32_to_eax (left);
+            
+            while (is_arithmetic_binary_operator (tok.kind)) {
+            
+                op = tok.kind;
+                get_token ();
+                
+                if (rhs_current_operand_is_unsigned_now ()) {
+                    left_unsigned = 1;
+                }
+                
+                emit_load_assignment_rhs_to_reg ("edx");
+                emit_assignment_binary_op (op, left_unsigned);
+            
+            }
+            
+            if (token_is_statement_compare_operator (tok.kind)) {
+            
+                op = tok.kind;
+                get_token ();
+                
+                is_unsigned = left_unsigned;
+                
+                if (rhs_current_operand_is_unsigned_now ()) {
+                    is_unsigned = 1;
+                }
+                
+                emit_load_assignment_rhs_expression_to_reg ("edx");
+                emit_statement_cmp_eax_edx_jump_if_false (op, is_unsigned, label);
+                
+                return;
+            
+            }
+            
+            emit_statement_test_eax_jump_if_false (label);
+            return;
+        
+        }
+        
+        if (token_is_statement_compare_operator (tok.kind)) {
+        
+            op = tok.kind;
+            get_token ();
+            
+            is_unsigned = left_unsigned;
+            
+            if (rhs_current_operand_is_unsigned_now ()) {
+                is_unsigned = 1;
+            }
+            
+            if (token_is_floating_constant_now ()) {
+            
+                long double left_float;
+                long double right_float;
+                
+                left_float = (long double) left.low;
+                
+                if (left.high != 0) {
+                    left_float += ((long double) left.high) * 4294967296.0L;
+                }
+                
+                right_float = parse_floating_const_expr_value_now ();
+                
+                statement_condition_constant_known = 1;
+                statement_condition_constant_value = statement_compare_floating_const_true (left_float, op, right_float) ? 1 : 0;
+                
+                if (statement_condition_fold_logical_tail (label)) {
+                    return;
+                }
+                
+                return;
+            
+            }
+            
+            if (token_is_const_condition_operand_now ()) {
+            
+                int64_s right;
+                
+                if (const_integer_expr_text_is_foldable_now (tok.start)) {
+                    right = const64_from_current_foldable_expr ();
+                } else {
+                    right = const64_from_current_operand ();
+                }
+                
+                statement_condition_constant_known = 1;
+                statement_condition_constant_value = statement_compare_const64_true (left, op, right, is_unsigned) ? 1 : 0;
+                
+                if (statement_condition_fold_logical_tail (label)) {
+                    return;
+                }
+                
+                return;
+            
+            }
+            
+            if (rhs_current_operand_is_floating_now ()) {
+            
+                long double left_float;
+                
+                left_float = (long double) left.low;
+                
+                if (left.high != 0) {
+                    left_float += ((long double) left.high) * 4294967296.0L;
+                }
+                
+                emit_load_floating_ld_now (DATA_DOUBLE & 0x1f, left_float);
+                emit_load_floating_rhs_expression_now (DATA_DOUBLE & 0x1f);
+                emit_statement_floating_compare_jump_if_false (op, label);
+                
+                return;
+            
+            }
+            
+            emit_statement_const32_to_eax (left);
+            emit_load_assignment_rhs_expression_to_reg ("edx");
+            
+            emit_statement_cmp_eax_edx_jump_if_false (op, is_unsigned, label);
+            statement_condition_emit_logical_tail (label);
+            
+            return;
+        
+        }
+        
+        statement_condition_constant_known = 1;
+        statement_condition_constant_value = int64_statement_truth_value (left) ? 1 : 0;
+        
+        if (statement_condition_fold_logical_tail (label)) {
+            return;
+        }
+        
+        return;
+    
+    }
+    
+    /*
+     * A leading unary ! always yields an int truth value.  Do not route it
+     * through the 64-bit condition path just because the operand text happens
+     * to mention a symbol that the coarse scanner thinks is 64-bit; that path
+     * expects an unconverted 64-bit value and will compare EDX:EAX against
+     * uninitialised ECX:EBX for a plain if (!p).
+     */
+    if (tok.kind == TOK_XMARK) {
+    
+        emit_load_assignment_rhs_expression_to_reg ("eax");
+        emit_statement_test_eax_jump_if_false (label);
+        
+        statement_condition_emit_logical_tail (label);
+        return;
+    
+    }
+    
+    if (current_expression_mentions_64bit_symbol_now ()) {
+    
+        is_unsigned = rhs_current_operand_is_unsigned_now ();
+        emit_load_assignment_rhs_expression_to_pair ("eax", "edx", is_unsigned);
+        
+        if (token_is_statement_compare_operator (tok.kind)) {
+        
+            op = tok.kind;
+            get_token ();
+            
+            if (rhs_current_operand_is_unsigned_now ()) {
+                is_unsigned = 1;
+            }
+            
+            if (state->ofp) {
+            
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                
+                    fprintf (state->ofp, "    push ebx\n");
+                    fprintf (state->ofp, "    push eax\n");
+                    fprintf (state->ofp, "    push edx\n");
+                
+                } else {
+                
+                    fprintf (state->ofp, "    pushl %%ebx\n");
+                    fprintf (state->ofp, "    pushl %%eax\n");
+                    fprintf (state->ofp, "    pushl %%edx\n");
+                
+                }
+            
+            }
+            
+            emit_load_assignment_rhs_expression_to_pair ("eax", "edx", is_unsigned);
+            
+            if (state->ofp) {
+            
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                
+                    fprintf (state->ofp, "    mov ebx, eax\n");
+                    fprintf (state->ofp, "    mov ecx, edx\n");
+                    fprintf (state->ofp, "    pop edx\n");
+                    fprintf (state->ofp, "    pop eax\n");
+                
+                } else {
+                
+                    fprintf (state->ofp, "    movl %%eax, %%ebx\n");
+                    fprintf (state->ofp, "    movl %%edx, %%ecx\n");
+                    fprintf (state->ofp, "    popl %%edx\n");
+                    fprintf (state->ofp, "    popl %%eax\n");
+                
+                }
+            
+            }
+            
+            emit_statement_cmp64_to_eax (op, is_unsigned);
+            
+            if (state->ofp) {
+            
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                    fprintf (state->ofp, "    pop ebx\n");
+                } else {
+                    fprintf (state->ofp, "    popl %%ebx\n");
+                }
+            
+            }
+            
+            emit_statement_test_eax_jump_if_false (label);
+            return;
+        
+        }
+        
+        /*
+         * No explicit comparison follows, so this is just the truth value of
+         * the 64-bit expression in EDX:EAX.  Do not route it through the
+         * generic 64-bit comparison helper as TOK_NOTEQ: that helper compares
+         * EDX:EAX against ECX:EBX, and there is no RHS pair here.  This broke
+         * conditions such as:
+         *
+         *     if ((entry_point = coff_calculate_entry_point ())) return;
+         *
+         * after the assignment stored the result and left ECX holding the
+         * destination address instead of zero.
+         */
+        emit_statement_test_pair_jump_if_false ("eax", "edx", label);
+        statement_condition_emit_logical_tail (label);
+        
+        return;
+    
+    }
+    
+    is_unsigned = rhs_current_operand_is_unsigned_now ();
+    emit_load_assignment_rhs_expression_to_reg ("eax");
+    
+    if (token_is_statement_compare_operator (tok.kind)) {
+    
+        op = tok.kind;
+        get_token ();
+        
+        if (rhs_current_operand_is_unsigned_now ()) {
+            is_unsigned = 1;
+        }
+        
+        emit_load_assignment_rhs_expression_to_reg ("edx");
+        emit_statement_cmp_eax_edx_jump_if_false (op, is_unsigned, label);
+        statement_condition_emit_logical_tail (label);
+        
+        return;
+    
+    }
+    
+    emit_statement_test_eax_jump_if_false (label);
+    statement_condition_emit_logical_tail (label);
+
+}
+
+static int token_text_starts_void_cast_now (void) {
+
+    const char *p = tok.caret;
+    
+    if (!p || *p != '(') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    if (strncmp (p, "void", 4) != 0) {
+        return 0;
+    }
+    
+    p += 4;
+    
+    if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+        return 0;
+    }
+    
+    while (*p == ' ' || *p == '\t') {
+        p++;
+    }
+    
+    return *p == ')';
+
+}
+
+static int parse_void_cast_discard_statement (void) {
+
+    if (tok.kind != TOK_LPAREN || !token_text_starts_void_cast_now ()) {
+        return 0;
+    }
+    
+    get_token ();
+    
+    expect (TOK_VOID, "void");
+    expect (TOK_RPAREN, ")");
+    
+    if (tok.kind != TOK_SEMI) {
+    
+        emit_load_assignment_rhs_expression_to_reg ("eax");
+        
+        if (tok.kind != TOK_SEMI) {
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        }
+    
+    }
+    
+    expect (TOK_SEMI, ";");
+    return 1;
+
+}
+
+static int token_text_looks_like_postfix_assignment_now (void) {
+
+    const char *p;
+    int saw_postfix;
+    int paren_depth;
+    int bracket_depth;
+    
+    if (tok.caret) {
+        p = tok.caret;
+    } else if (tok.start) {
+        p = tok.start;
+    } else {
+        return 0;
+    }
+    
+    saw_postfix = 0;
+    paren_depth = 0;
+    bracket_depth = 0;
+    
+    while (*p && *p != ';' && *p != '\n') {
+    
+        if (*p == '(') {
+        
+            /**
+             * A postfix/member expression followed by a top-level '(' is a
+             * function call through that member, not a postfix assignment.
+             * This pre-scan must reject it before the destructive postfix
+             * assignment parser consumes the tokens.
+             */
+            if (saw_postfix && paren_depth == 0 && bracket_depth == 0) {
+                return 0;
+            }
+            
+            paren_depth++;
+        
+        } else if (*p == ')') {
+            if (paren_depth > 0) paren_depth--;
+        } else if (*p == '[') {
+        
+            saw_postfix = 1;
+            bracket_depth++;
+        
+        } else if (*p == ']') {
+            if (bracket_depth > 0) bracket_depth--;
+        } else if (paren_depth == 0 && bracket_depth == 0 && *p == ',') {
+            return 0;
+        } else if (paren_depth == 0 && bracket_depth == 0 && *p == '.') {
+            saw_postfix = 1;
+        } else if (paren_depth == 0 && bracket_depth == 0 && *p == '-' && p[1] == '>') {
+        
+            saw_postfix = 1;
+            p++;
+        
+        } else if (paren_depth == 0 && bracket_depth == 0 && *p == '=') {
+        
+            if (p[1] == '=') {
+                return 0;
+            }
+            
+            return saw_postfix;
+        
+        }
+        
+        p++;
+    
+    }
+    
+    return 0;
+
+}
+
+static int source_starts_with_parenthesized_indirect_call_now (void) {
+
+    const char *p = tok.caret;
+    
+    int member_depth = 1;
+    int saw_member = 0;
+    int depth;
+    
+    if (!p || *p != '(') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p != '*') {
+        return 0;
+    }
+    
+    depth = 1;
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    /*
+     * Accept both
+     *
+     *     (*obj->member)(...)
+     *     (*(obj->member))(...)
+     *
+     * but keep this recognizer deliberately narrow.  It must not claim an
+     * arbitrary parenthesized expression, otherwise normal statement parsing
+     * and C labels can be pulled into this special call path.
+     */
+    if (*p == '(') {
+    
+        depth++;
+        
+        member_depth = 2;
+        p++;
+    
+    }
+    
+    while (*p && depth > 0) {
+    
+        if (*p == '(') {
+            depth++;
+        } else if (*p == ')') {
+        
+            depth--;
+            
+            if (depth == 0) {
+                break;
+            }
+        
+        } else if (depth == member_depth && *p == '-' && p[1] == '>') {
+        
+            saw_member = 1;
+            p++;
+        
+        } else if (depth == member_depth && *p == '.') {
+            saw_member = 1;
+        }
+        
+        p++;
+    
+    }
+    
+    if (depth != 0 || !saw_member) {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    return *p == '(';
+
+}
+
+static int parse_postfix_assignment_statement_now (void) {
+
+    char *name;
+    
+    struct local_symbol *src;
+    int global_index;
+    
+    enum token_kind assign_op;
+    int lvalue_size;
+    
+    if (tok.kind != TOK_IDENT || !token_text_looks_like_postfix_assignment_now ()) {
+        return 0;
+    }
+    
+    name = xstrdup (tok.ident);
+    
+    {
+    
+        const char *name_start = tok.start;
+        const char *name_caret = tok.caret;
+        
+        unsigned long name_line = get_line_number ();
+        get_token ();
+        
+        src = find_local_symbol (name);
+        global_index = find_global_symbol (name);
+        
+        if (!src && global_index < 0) {
+        
+            free (name);
+            return 0;
+        
+        }
+        
+        if (!emit_parse_postfix_copy_source_address_now ("edx", src, name, name_start, name_caret, name_line)) {
+        
+            free (name);
+            return 0;
+        
+        }
+    
+    }
+    
+    free (name);
+    
+    if (!is_assignment_operator (tok.kind)) {
+        return 0;
+    }
+    
+    assign_op = tok.kind;
+    lvalue_size = index_step_size (postfix_copy_lvalue_size);
+    
+    if (lvalue_size <= 0) {
+        lvalue_size = DATA_INT & 0x1f;
+    }
+    
+    get_token ();
+    
+    if (assign_op == TOK_ASSIGN) {
+    
+        if (token_is_floating_constant_now ()) {
+        
+            emit_push_reg_now ("edx");
+            emit_load_floating_rhs_expression_now (lvalue_size);
+            
+            emit_pop_reg_now ("edx");
+            emit_store_floating_member_to_addr_reg_now ("edx", 0, lvalue_size);
+            
+            return 1;
+        
+        }
+        
+        if (lvalue_size == (DATA_LLONG & 0x1f)) {
+        
+            emit_push_reg_now ("edx");
+            
+            emit_load_assignment_rhs_expression_to_pair ("eax", "edx", 1);
+            emit_pop_reg_now ("ecx");
+            
+            emit_store_pair_to_deref_reg_now ("ecx", "eax", "edx");
+            return 1;
+        
+        }
+        
+        if (lvalue_size > (DATA_LLONG & 0x1f)
+            && tok.kind == TOK_IDENT && tok.ident && token_identifier_is_function_call_rhs_now ()
+            && get_global_symbol_kind (tok.ident) == GLOBAL_SYMBOL_FUNCTION
+            && get_global_symbol_size (tok.ident) > (DATA_LLONG & 0x1f)) {
+        
+            char *rhs_name = xstrdup (tok.ident);
+            const char *rhs_start = tok.start;
+            const char *rhs_caret = tok.caret;
+            unsigned long rhs_line = get_line_number ();
+            
+            emit_push_reg_now ("edx");
+            
+            pending_struct_return_lhs = 0;
+            pending_struct_return_global_name = 0;
+            pending_struct_return_stack_address = 1;
+            pending_struct_return_stack_offset = 0;
+            
+            get_token ();
+            emit_call_identifier_to_reg_now (rhs_name, "eax", rhs_start, rhs_caret, rhs_line);
+            
+            pending_struct_return_stack_address = 0;
+            pending_struct_return_stack_offset = 0;
+            
+            emit_pop_reg_now ("edx");
+            
+            free (rhs_name);
+            return 1;
+        
+        }
+        
+        if (emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("edx", 0, lvalue_size)) {
+            return 1;
+        }
+        
+        /**
+         * Keep the computed lvalue address across the RHS evaluation.
+         * The RHS emitter is free to use edx for indexing, calls, and
+         * arithmetic, so storing through edx after it can corrupt memory.
+         */
+        emit_push_reg_now ("edx");
+        
+        emit_load_assignment_rhs_expression_to_reg ("eax");
+        emit_pop_reg_now ("edx");
+
+    } else {
+    
+        emit_push_reg_now ("edx");
+        
+        emit_load_member_from_addr_reg_now ("eax", "edx", 0, lvalue_size);
+        emit_push_reg_now ("eax");
+        
+        emit_load_assignment_rhs_expression_to_reg ("edx");
+        emit_pop_reg_now ("eax");
+        
+        emit_assignment_binary_op (assign_op, 0);
+        emit_pop_reg_now ("edx");
+    
+    }
+    
+    emit_store_reg_to_deref_reg_now ("edx", "eax", lvalue_size);
+    return 1;
+
+}
+
+static int parse_parenthesized_member_function_pointer_call_statement (void) {
+
+    struct local_symbol *src;
+    char *name;
+    
+    int global_index;
+    int wrapped_member_expr = 0;
+    
+    const char *name_start;
+    const char *name_caret;
+    
+    unsigned long name_line;
+    int base_size = DATA_INT & 0x1f;
+    
+    const char *base_tag_name = 0;
+    
+    if (tok.kind != TOK_LPAREN || !source_starts_with_parenthesized_indirect_call_now ()) {
+        return 0;
+    }
+    
+    get_token ();
+    
+    if (tok.kind != TOK_STAR) {
+        return 0;
+    }
+    
+    get_token ();
+    
+    if (tok.kind == TOK_LPAREN) {
+    
+        wrapped_member_expr = 1;
+        get_token ();
+    
+    }
+    
+    if (tok.kind != TOK_IDENT || !tok.ident) {
+        return 0;
+    }
+    
+    name = xstrdup (tok.ident);
+    name_start = tok.start;
+    name_caret = tok.caret;
+    name_line = get_line_number ();
+    
+    get_token ();
+    
+    src = find_local_symbol (name);
+    global_index = find_global_symbol (name);
+    
+    if (!src && global_index < 0) {
+    
+        free (name);
+        return 0;
+    
+    }
+    
+    if (tok.kind == TOK_ARROW || tok.kind == TOK_LBRACK) {
+    
+        if (src) {
+        
+            base_size = src->pointer_depth > 0 ? src->pointed_size : src->size;
+            base_tag_name = src->pointer_depth > 0 ? src->pointed_tag_name : 0;
+        
+        } else {
+        
+            base_size = get_global_symbol_pointer_depth (name) > 0 ? get_global_symbol_pointed_size (name) : get_global_symbol_size (name);
+            base_tag_name = 0;
+        
+        }
+    
+    } else if (tok.kind == TOK_DOT) {
+    
+        if (src) {
+        
+            base_size = src->size;
+            base_tag_name = 0;
+        
+        } else {
+        
+            base_size = get_global_symbol_size (name);
+            base_tag_name = 0;
+        
+        }
+    
+    } else {
+    
+        free (name);
+        return 0;
+    
+    }
+    
+    postfix_copy_lvalue_size = base_size;
+    postfix_copy_lvalue_tag_name = base_tag_name;
+    
+    if (!emit_parse_postfix_copy_source_address_now ("ecx", src, name, name_start, name_caret, name_line)) {
+    
+        free (name);
+        return 0;
+    
+    }
+    
+    free (name);
+    
+    if (wrapped_member_expr) {
+        expect (TOK_RPAREN, ")");
+    }
+    
+    expect (TOK_RPAREN, ")");
+    
+    if (tok.kind != TOK_LPAREN) {
+        return 0;
+    }
+    
+    emit_load_deref_reg_now ("ecx", DATA_PTR & 0x1f);
+    emit_call_pointer_in_reg_now ("ecx", "eax");
+    
+    expect_semi_or_recover ();
+    return 1;
+
+}
+
+static void parse_statement_suppressed (void) {
+
+    FILE *save_ofp = state->ofp;
+    
+    state->ofp = 0;
+    parse_statement ();
+    state->ofp = save_ofp;
+
+}
+
+static void parse_statement (void) {
+
+    statement_ends_control_flow = 0;
+    flush_pending_statement_labels ();
+    
+    if (parse_inline_asm_statement ()) {
+        return;
+    }
+    
+    if (parse_prefix_incdec_statement ()) {
+        return;
+    }
+    
+    if (parse_void_cast_discard_statement ()) {
+        return;
+    }
+    
+    
+    if (tok.kind == TOK_LPAREN && source_starts_lparen_deref_postfix_incdec_at (tok.caret)) {
+    
+        get_token ();
+        
+        if (emit_load_parenthesized_deref_postfix_incdec_subscript_to_reg_now ("eax")) {
+        
+            expect_semi_or_recover ();
+            return;
+        
+        }
+    
+    }
+    
+    if (parse_parenthesized_deref_subscript_statement ()) {
+    
+        expect_semi_or_recover ();
+        return;
+    
+    }
+    
+    if (parse_parenthesized_member_function_pointer_call_statement ()) {
+        return;
+    }
+    
+    if (tok.kind == TOK_LPAREN) {
+    
+        int parenthesized_assignment_is_unsigned;
+        
+        if (emit_load_parenthesized_assignment_expression_to_reg_now ("eax", &parenthesized_assignment_is_unsigned)) {
+        
+            expect_semi_or_recover ();
+            return;
+        
+        }
+    
+    }
+    
+    if (tok.kind == TOK_LPAREN && parse_parenthesized_indirect_member_assignment_statement ()) {
+        return;
+    }
+    
+    if (parse_indirect_assignment_statement ()) {
+        return;
+    }
+    
+    if (parse_postfix_assignment_statement_now ()) {
+    
+        expect_semi_or_recover ();
+        return;
+    
+    }
+    
+    if (parse_identifier_assignment_statement ()) {
+        return;
+    }
+    
+    if (tok.kind == TOK_RETURN) {
+    
+        int has_value = 0;
+        long v = 0;
+        
+        const char *ret_start = tok.start;
+        const char *ret_caret = tok.caret;
+        
+        int ret_line = get_line_number ();
+        
+        current_function_has_return_statement = 1;
+        get_token ();
+        
+        if (tok.kind != TOK_SEMI) {
+        
+            if (current_function_is_void) {
+            
+                FILE *saved_return_ofp = state->ofp;
+                state->ofp = 0;
+                
+                emit_load_assignment_rhs_expression_to_reg ("eax");
+                state->ofp = saved_return_ofp;
+            
+            } else if (current_function_is_floating) {
+            
+                if (state->ofp && pending_return_jump) {
+                    emit_pending_return_jump ();
+                }
+                
+                emit_load_floating_rhs_expression_now (current_function_return_size);
+            
+            } else {
+            
+                if (state->ofp && pending_return_jump) {
+                    emit_pending_return_jump ();
+                }
+                
+                if (state->ofp) {
+                
+                    if (current_function_returns_aggregate) {
+                    
+                        if (state->syntax & ASM_SYNTAX_INTEL) {
+                            fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? "    mov edx, dword [ebp + 8]\n" : "    mov edx, dword ptr [ebp + 8]\n"));
+                        } else {
+                            fprintf (state->ofp, "    movl 8(%%ebp), %%edx\n");
+                        }
+                        
+                        if (!emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("edx", 0, current_function_return_size)) {
+                            emit_load_assignment_rhs_expression_to_reg ("eax");
+                        }
+                    
+                    } else if (current_function_return_size == (DATA_LLONG & 0x1f)) {
+                        emit_load_assignment_rhs_expression_to_pair ("eax", "edx", current_function_return_is_unsigned);
+                    } else if (current_expression_mentions_64bit_symbol_now ()) {
+                        emit_load_assignment_rhs_expression_to_pair ("eax", "edx", current_function_return_is_unsigned);
+                    } else {
+                        emit_load_assignment_rhs_expression_to_reg ("eax");
+                    }
+                
+                } else {
+                    v = const_from_current_expr ();
+                }
+            
+            }
+            
+            has_value = 1;
+        
+        }
+        
+        if (current_function_is_void && has_value) {
+            report_line_at (get_filename (), ret_line, REPORT_WARNING, ret_start, ret_caret, "return with a value in void function");
+        } else if (!current_function_is_void && !has_value) {
+            report_line_at (get_filename (), ret_line, REPORT_WARNING, ret_start, ret_caret, "return with no value in non-void function");
+        }
+        
+        if (state->ofp) {
+        
+            if (pending_return_jump) {
+                emit_pending_return_jump ();
+            }
+            
+            if (!current_function_is_floating && !has_value) {
+            
+                if (state->syntax & ASM_SYNTAX_INTEL) {
+                
+                    fprintf (state->ofp, "    mov eax, %ld\n", v);
+                    
+                    if (current_function_return_size == (DATA_LLONG & 0x1f)) {
+                        fprintf (state->ofp, "    cdq\n");
+                    }
+                
+                } else {
+                
+                    fprintf (state->ofp, "    movl $%ld, %%eax\n", v);
+                    
+                    if (current_function_return_size == (DATA_LLONG & 0x1f)) {
+                        fprintf (state->ofp, "    cdq\n");
+                    }
+                
+                }
+            
+            }
+            
+            pending_return_jump = 1;
+            statement_ends_control_flow = 1;
+        
+        }
+        
+        expect (TOK_SEMI, ";");
+        return;
+    
+    }
+    
+    if (tok.kind == TOK_LBRACE) {
+    
+        parse_block ();
+        return;
+    
+    }
+    
+    if (tok.kind == TOK_IF) {
+    
+        int else_label = anon_label++;
+        int end_label = anon_label++;
+        
+        get_token ();
+        expect (TOK_LPAREN, "(");
+        
+        /*
+         * Compile the full if-condition as an expression before testing it.
+         * The older specialised condition emitter jumps to the false label as
+         * soon as the left side of a logical OR is false, which skips the RHS.
+         * That miscompiled expressions such as:
+         *
+         *     if ((base && !base->dword) || (index && ...))
+         *
+         * and made pdas reject valid 32-bit AT&T base/index operands.
+         */
+        emit_statement_jump_if_false (else_label);
+        
+        if (tok.kind != TOK_RPAREN) {
+            skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+        }
+        
+        expect (TOK_RPAREN, ")");
+        
+        if (statement_condition_constant_known) {
+        
+            if (statement_condition_constant_value) {
+            
+                parse_statement ();
+                
+                if (_accept (TOK_ELSE)) {
+                    parse_statement_suppressed ();
+                }
+            
+            } else {
+            
+                parse_statement_suppressed ();
+                
+                if (_accept (TOK_ELSE)) {
+                    parse_statement ();
+                }
+            
+            }
+            
+            return;
+        
+        }
+        
+        parse_statement ();
+        
+        {
+        
+            int then_ends_control_flow = statement_ends_control_flow;
+            
+            if (_accept (TOK_ELSE)) {
+            
+                if (pending_return_jump) {
+                
+                    emit_pending_return_jump ();
+                    then_ends_control_flow = 1;
+                
+                } else if (!then_ends_control_flow) {
+                    emit_statement_jump (end_label);
+                }
+                
+                emit_statement_label (else_label);
+                parse_statement ();
+                
+                if (pending_return_jump) {
+                
+                    emit_pending_return_jump ();
+                    statement_ends_control_flow = 1;
+                
+                }
+                
+                emit_statement_label (end_label);
+                statement_ends_control_flow = then_ends_control_flow && statement_ends_control_flow;
+            
+            } else {
+            
+                if (pending_return_jump) {
+                    emit_pending_return_jump ();
+                }
+                
+                emit_statement_label (else_label);
+                statement_ends_control_flow = 0;
+            
+            }
+        
+        }
+        
+        return;
+    
+    }
+    
+    if (tok.kind == TOK_SWITCH) {
+    
+        parse_switch_statement ();
+        return;
+    
+    }
+    
+    if (tok.kind == TOK_WHILE) {
+    
+        parse_while_statement ();
+        return;
+    
+    }
+    
+    if (tok.kind == TOK_DO) {
+    
+        parse_do_statement ();
+        return;
+    
+    }
+    
+    if (tok.kind == TOK_FOR) {
+    
+        parse_for_statement ();
+        return;
+    
+    }
+    
+    if (tok.kind == TOK_GOTO) {
+    
+        const char *label_start;
+        const char *label_caret;
+        
+        unsigned long label_line;
+        char *label_name = 0;
+        
+        get_token ();
+        
+        label_start = tok.start;
+        label_caret = tok.caret;
+        label_line = get_line_number ();
+        
+        if (tok.kind == TOK_IDENT) {
+        
+            label_name = xstrdup (tok.ident);
+            get_token ();
+            
+            /*
+             * Do not eagerly pop block-local temporary storage for a forward
+             * goto.  A label can legally be later in the same block after
+             * nested statements, and the earlier text scan was too
+             * conservative: it treated the first nested '}' as the end of the
+             * current block.  That produced an extra add to esp before
+             * `goto skip;` in i386_gen.c's process_bitfield_init(), corrupting
+             * the stack before the local label was reached.
+             */
+            
+            reference_goto_label (label_name, label_line, label_start, label_caret);
+            statement_ends_control_flow = 1;
+            
+            free (label_name);
+        
+        } else {
+            report_line_at (get_filename (), label_line, REPORT_ERROR, label_start, label_caret, "expected label name after goto");
+        }
+        
+        expect (TOK_SEMI, ";");
+        return;
+    
+    }
+    
+    if (tok.kind == TOK_BREAK || tok.kind == TOK_CONTINUE) {
+    
+        enum token_kind jump_kind = tok.kind;
+        int target_label = -1;
+        
+        get_token ();
+        
+        if (jump_kind == TOK_BREAK) {
+            target_label = current_break_label;
+        } else {
+            target_label = current_continue_label;
+        }
+        
+        if (target_label >= 0) {
+        
+            {
+            
+                long cleanup_base = (jump_kind == TOK_BREAK) ? current_break_cleanup_base : current_continue_cleanup_base;
+                long cleanup_bytes = current_block_cleanup_bytes - cleanup_base;
+                
+                if (cleanup_bytes > 0) {
+                    emit_stack_adjust (cleanup_bytes, 0);
+                }
+            
+            }
+            
+            emit_statement_jump (target_label);
+            statement_ends_control_flow = 1;
+        
+        }
+        
+        if (tok.kind != TOK_SEMI) {
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        }
+        
+        expect (TOK_SEMI, ";");
+        return;
+    
+    }
+    
+    if (tok.kind == TOK_CASE) {
+    
+        long value;
+        unsigned long case_line = get_line_number ();
+        
+        const char *case_start = tok.start;
+        const char *case_caret = tok.caret;
+        
+        get_token ();
+        
+        value = const_from_current_case_expr ();
+        add_switch_case_label (value, case_line, case_start, case_caret);
+        
+        if (tok.kind != TOK_COLON) {
+            skip_balanced_until (TOK_COLON, TOK_EOF, TOK_EOF);
+        }
+        
+        expect (TOK_COLON, ":");
+        statement_ends_control_flow = 0;
+        
+        return;
+    
+    }
+    
+    if (tok.kind == TOK_DEFAULT) {
+    
+        unsigned long default_line = get_line_number ();
+        
+        const char *default_start = tok.start;
+        const char *default_caret = tok.caret;
+        
+        get_token ();
+        
+        set_switch_default_label (default_line, default_start, default_caret);
+        expect (TOK_COLON, ":");
+        
+        statement_ends_control_flow = 0;
+        return;
+    
+    }
+    
+    if (tok.kind != TOK_SEMI && tok.kind != TOK_RBRACE && tok.kind != TOK_EOF) {
+    
+        if (parse_postfix_assignment_statement_now ()) {
+        
+            if (tok.kind == TOK_SEMI) {
+            
+                get_token ();
+                return;
+            
+            }
+            
+            skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+            
+            expect (TOK_SEMI, ";");
+            return;
+        
+        }
+        
+        emit_load_assignment_rhs_expression_to_reg ("eax");
+        
+        if (tok.kind == TOK_SEMI) {
+        
+            get_token ();
+            return;
+        
+        }
+        
+        skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+        
+        expect (TOK_SEMI, ";");
+        return;
+    
+    }
+    
+    expect (TOK_SEMI, ";");
+
+}
+
+static void parse_function_body (const char *name, int storage_class, int is_inline, int return_is_void, int return_is_floating, int return_is_unsigned, int return_size, unsigned long function_line, const char *function_start, const char *function_caret) {
+
+    int old_return_label = current_return_label;
+    int should_emit = 1;
+    int emit_body = 1;
+    int emit_public = 1;
+    int emit_inline_definition_to_output = 1;
+    
+    FILE *saved_ofp;
+    FILE *inline_tmp = 0;
+    
+    char *inline_asm_text = 0;
+    int capture_inline_body = 0;
+    
+    int saved_declarator_function_param_count;
+    int saved_declarator_function_has_prototype;
+    int saved_declarator_function_is_variadic;
+    int saved_current_section = current_section;
+    
+    int old_function_is_void;
+    int old_function_is_floating;
+    int old_function_return_size;
+    int old_function_return_is_unsigned;
+    int old_function_returns_aggregate;
+    int old_function_has_return_statement;
+    
+    char *function_filename_copy = 0;
+    
+    /**
+     * Inline definitions are compiled into a temporary assembler buffer so
+     * calls in this translation unit can be expanded at the call site.
+     *
+     *   inline int f(...) { ... }
+     *   static inline int f(...) { ... }
+     *       Remember the generated body for inline expansion.  Do not emit an
+     *       out-of-line copy unless a later rule explicitly asks for one.
+     *
+     *   extern inline int f(...) { ... }
+     *       Remember the generated body and also emit the normal public
+     *       definition.
+     *
+     *   int f(...) { ... }
+     *       Normal external definition.  Emit it as public.
+     */
+    if (storage_class == STORAGE_STATIC) {
+        emit_public = 0;
+    } else if (is_inline && storage_class != STORAGE_EXTERN) {
+        emit_public = 0;
+    }
+    
+    if (is_inline && storage_class != STORAGE_EXTERN && !return_is_floating) {
+        emit_inline_definition_to_output = 0;
+    }
+    
+    saved_ofp = state->ofp;
+    
+    saved_declarator_function_param_count = declarator_function_param_count;
+    saved_declarator_function_has_prototype = declarator_function_has_prototype;
+    saved_declarator_function_is_variadic = declarator_function_is_variadic;
+    
+    if (get_filename ()) {
+        function_filename_copy = xstrdup (get_filename ());
+    }
+    
+    capture_inline_body = is_inline && saved_ofp != 0;
+    
+    if (capture_inline_body) {
+    
+        inline_tmp = tmpfile ();
+        
+        if (!inline_tmp) {
+            capture_inline_body = 0;
+        }
+    
+    }
+    
+    if (emit_body) {
+    
+        should_emit = add_global_symbol (name, GLOBAL_SYMBOL_FUNCTION, 0, last_declarator_name_start, last_declarator_name_caret, last_declarator_name_line);
+        
+        set_global_symbol_size (name, return_is_void ? DATA_VOID : return_size);
+        set_global_symbol_unsigned (name, 0);
+        set_global_symbol_floating (name, return_is_floating);
+        set_global_symbol_returns_void (name, return_is_void);
+        set_global_symbol_param_count (name, saved_declarator_function_param_count, saved_declarator_function_has_prototype || saved_declarator_function_param_count > 0, saved_declarator_function_is_variadic);
+        
+        if (is_inline) {
+        
+            remember_inline_function_signature (name, saved_declarator_function_param_count, saved_declarator_function_has_prototype || saved_declarator_function_param_count > 0,
+                return_is_void, return_is_floating, return_size);
+        
+        }
+    
+    }
+    
+    old_function_has_return_statement = current_function_has_return_statement;
+    old_function_is_void = current_function_is_void;
+    old_function_is_floating = current_function_is_floating;
+    old_function_return_size = current_function_return_size;
+    old_function_return_is_unsigned = current_function_return_is_unsigned;
+    old_function_returns_aggregate = current_function_returns_aggregate;
+    
+    current_function_is_void = return_is_void;
+    current_function_is_floating = return_is_floating;
+    current_function_return_is_unsigned = return_is_unsigned;
+    current_function_return_size = return_size;
+    current_function_returns_aggregate = (!return_is_void && !return_is_floating && return_size > (DATA_LLONG & 0x1f));
+    current_function_has_return_statement = 0;
+    
+    reset_local_symbols ();
+    reset_goto_labels ();
+    
+    install_pending_params_as_locals ();
+    clear_pending_params ();
+    
+    if (!emit_body || !should_emit) {
+        state->ofp = 0;
+    } else if (capture_inline_body) {
+        state->ofp = inline_tmp;
+    }
+    
+    current_return_label = anon_label++;
+    pending_return_jump = 0;
+    
+    emit_function_start (name, !emit_public);
+    
+    parse_old_style_param_decls ();
+    parse_block ();
+    check_goto_labels ();
+    
+    if (!current_function_is_void && !current_function_has_return_statement) {
+        report_line_at (function_filename_copy ? function_filename_copy : get_filename (), function_line, REPORT_WARNING, function_start, function_caret, "control reaches end of non-void function");
+    }
+    
+    pending_return_jump = 0;
+    
+    emit_function_end ();
+    emit_goto_trampolines ();
+    
+    if (capture_inline_body && inline_tmp) {
+    
+        inline_asm_text = read_tmp_file_text (inline_tmp);
+        
+        if (inline_asm_text) {
+        
+            if (emit_inline_definition_to_output) {
+                fputs (inline_asm_text, saved_ofp);
+            }
+            
+            remember_inline_function (name, inline_asm_text, current_return_label,
+                saved_declarator_function_param_count, saved_declarator_function_has_prototype || saved_declarator_function_param_count > 0,
+                    return_is_void, return_is_floating, return_size);
+        
+        }
+        
+        fclose (inline_tmp);
+        inline_tmp = 0;
+    
+    }
+    
+    reset_local_symbols ();
+    reset_goto_labels ();
+    
+    current_function_has_return_statement = old_function_has_return_statement;
+    current_function_is_void = old_function_is_void;
+    current_function_is_floating = old_function_is_floating;
+    current_function_return_size = old_function_return_size;
+    current_function_return_is_unsigned = old_function_return_is_unsigned;
+    current_function_returns_aggregate = old_function_returns_aggregate;
+    
+    current_return_label = old_return_label;
+    state->ofp = saved_ofp;
+    
+    if (capture_inline_body) {
+        current_section = saved_current_section;
+    }
+    
+    if (function_filename_copy) {
+        free (function_filename_copy);
+    }
+    
+    if (inline_asm_text) {
+        free (inline_asm_text);
+    }
+
+}
+
+static int parse_possible_knr_function (void) {
+
+    char *name;
+    
+    const char *name_start;
+    const char *name_caret;
+    
+    unsigned long name_line;
+    
+    if (!token_is_ident ()) {
+        return 0;
+    }
+    
+    name_line = get_line_number ();
+    
+    name_start = tok.start;
+    name_caret = tok.caret;
+    
+    name = take_ident ();
+    
+    if (!_accept (TOK_LPAREN)) {
+    
+        free (name);
+        return 0;
+    
+    }
+    
+    if (tok.kind != TOK_RPAREN) {
+    
+        for (;;) {
+        
+            if (token_is_ident ()) {
+            
+                add_pending_param (tok.ident, DATA_INT & 0x1f, type_alignment (DATA_INT & 0x1f), 0, 0, 0, 0);
+                get_token ();
+            
+            } else {
+                skip_balanced_until (TOK_COMMA, TOK_RPAREN, TOK_EOF);
+            }
+            
+            if (!_accept (TOK_COMMA)) {
+                break;
+            }
+        
+        }
+    
+    }
+    
+    expect (TOK_RPAREN, ")");
+    
+    if (is_type_start (tok.kind) || tok.kind == TOK_LBRACE) {
+    
+        parse_function_body (name, STORAGE_NONE, 0, 0, 0, 0, DATA_INT & 0x1f, name_line, name_start, name_caret);
+        
+        free (name);
+        return 1;
+    
+    }
+    
+    free (name);
+    return 0;
+
+}
+
+static int is_string_token (void) {
+    return tok.kind == TOK_PPSTR || tok.kind == TOK_LSTR;
+}
+
+static void append_global_init_byte (int64_s *values, int max_values, int *count, unsigned int value) {
+
+    if (*count < max_values) {
+        zext64 (&values[*count], value & 0xffU);
+    }
+    
+    (*count)++;
+
+}
+
+static int parse_octal_escape_value (const char **ps) {
+
+    const char *s = *ps;
+    int value = 0, i;
+    
+    for (i = 0; i < 3; i++) {
+    
+        if (*s < '0' || *s > '7') {
+            break;
+        }
+        
+        value = value * 8 + (*s - '0');
+        s++;
+    
+    }
+    
+    *ps = s;
+    return value;
+
+}
+
+static int parse_hex_escape_value (const char **ps) {
+
+    const char *s = *ps;
+    
+    int value = 0;
+    int any = 0;
+    
+    while ((*s >= '0' && *s <= '9') || (*s >= 'a' && *s <= 'f') || (*s >= 'A' && *s <= 'F')) {
+    
+        int digit;
+        
+        if (*s >= '0' && *s <= '9') {
+            digit = *s - '0';
+        } else if (*s >= 'a' && *s <= 'f') {
+            digit = *s - 'a' + 10;
+        } else {
+            digit = *s - 'A' + 10;
+        }
+        
+        value = (value << 4) + digit;
+        any = 1;
+        
+        s++;
+    
+    }
+    
+    if (!any) {
+        value = 'x';
+    }
+    
+    *ps = s;
+    return value;
+
+}
+
+static void parse_string_initializer_values (int64_s *values, int max_values, int *count) {
+
+    while (is_string_token ()) {
+    
+        const char *s;
+        int quote;
+        
+        s = tok.ident;
+        
+        if (tok.kind == TOK_LSTR && *s == 'L') {
+            s++;
+        }
+        
+        quote = *s;
+        
+        if (quote != '"') {
+        
+            get_token ();
+            continue;
+        
+        }
+        
+        s++;
+        
+        while (*s && *s != '"') {
+        
+            unsigned int ch;
+            
+            if (*s == '\\') {
+            
+                s++;
+                
+                switch (*s) {
+                
+                    case 'a':
+                    
+                        ch = '\a';
+                        
+                        s++;
+                        break;
+                    
+                    case 'b':
+                    
+                        ch = '\b';
+                        
+                        s++;
+                        break;
+                    
+                    case 'f':
+                    
+                        ch = '\f';
+                        
+                        s++;
+                        break;
+                    
+                    case 'n':
+                    
+                        ch = '\n';
+                        
+                        s++;
+                        break;
+                    
+                    case 'r':
+                    
+                        ch = '\r';
+                        
+                        s++;
+                        break;
+                    
+                    case 't':
+                    
+                        ch = '\t';
+                        
+                        s++;
+                        break;
+                    
+                    case 'v':
+                    
+                        ch = '\v';
+                        
+                        s++;
+                        break;
+                    
+                    case '\\':
+                    
+                        ch = '\\';
+                        
+                        s++;
+                        break;
+                    
+                    case '\'':
+                    
+                        ch = '\'';
+                        
+                        s++;
+                        break;
+                    
+                    case '"':
+                    
+                        ch = '"';
+                        
+                        s++;
+                        break;
+                    
+                    case '?':
+                    
+                        ch = '?';
+                        
+                        s++;
+                        break;
+                    
+                    case 'x':
+                    
+                        s++;
+                        
+                        ch = (unsigned int) parse_hex_escape_value (&s);
+                        break;
+                    
+                    case '0': case '1': case '2': case '3':
+                    case '4': case '5': case '6': case '7':
+                    
+                        ch = (unsigned int) parse_octal_escape_value (&s);
+                        break;
+                    
+                    case '\0':
+                    
+                        ch = 0;
+                        break;
+                    
+                    default:
+                    
+                        ch = (unsigned char) *s;
+                        
+                        if (*s) {
+                            s++;
+                        }
+                        
+                        break;
+                
+                }
+            
+            } else {
+                ch = (unsigned char) *s++;
+            }
+            
+            append_global_init_byte (values, max_values, count, ch);
+        
+        }
+        
+        get_token ();
+    
+    }
+    
+    append_global_init_byte (values, max_values, count, 0);
+
+}
+
+static void parse_char_array_initializer_values (int64_s *values, int max_values, int *count, long row_width) {
+
+    if (tok.kind == TOK_LBRACE) {
+    
+        get_token ();
+        
+        while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) {
+        
+            parse_char_array_initializer_values (values, max_values, count, row_width);
+            
+            if (!_accept (TOK_COMMA)) {
+                break;
+            }
+        
+        }
+        
+        expect (TOK_RBRACE, "}");
+        return;
+    
+    }
+    
+    if (is_string_token ()) {
+    
+        int64_s tmp[MAX_STRING_INIT_BYTES];
+        int tmp_count = 0;
+        
+        int i;
+        int n;
+        
+        parse_string_initializer_values (tmp, MAX_STRING_INIT_BYTES, &tmp_count);
+        
+        if (row_width <= 0) {
+            n = tmp_count;
+        } else {
+            n = (tmp_count < row_width) ? tmp_count : (int) row_width;
+        }
+        
+        for (i = 0; i < n; i++) {
+            append_global_init_byte (values, max_values, count, (unsigned int) (tmp[i].low & 0xff));
+        }
+        
+        while (row_width > 0 && i < row_width) {
+        
+            append_global_init_byte (values, max_values, count, 0);
+            i++;
+        
+        }
+        
+        return;
+    
+    }
+    
+    if (tok.kind != TOK_COMMA && tok.kind != TOK_SEMI && tok.kind != TOK_RBRACE && tok.kind != TOK_EOF) {
+    
+        if (*count < max_values) {
+            values[*count] = const64_from_current_expr ();
+        } else {
+            const64_from_current_expr ();
+        }
+        
+        (*count)++;
+    
+    }
+
+}
+
+static int global_initializer_parenthesized_string_now (void) {
+
+    const char *p = tok.caret;
+    
+    if (!p || *p != '(') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p == '"') {
+        return 1;
+    }
+    
+    if ((*p == 'L' || *p == 'u' || *p == 'U') && p[1] == '"') {
+        return 1;
+    }
+    
+    if (*p == 'u' && p[1] == '8' && p[2] == '"') {
+        return 1;
+    }
+    
+    return 0;
+
+}
+
+static int global_initializer_cast_before_string_now (void) {
+
+    const char *p = tok.caret;
+    int depth;
+    
+    if (!p || *p != '(') {
+        return 0;
+    }
+    
+    p++;
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (!(ident_start_now ((unsigned char) *p))) {
+        return 0;
+    }
+    
+    depth = 1;
+    
+    while (*p && depth > 0) {
+    
+        if (*p == '(') {
+            depth++;
+        } else if (*p == ')') {
+            depth--;
+        }
+        
+        p++;
+    
+    }
+    
+    if (depth != 0) {
+        return 0;
+    }
+    
+    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+        p++;
+    }
+    
+    if (*p == '"') {
+        return 1;
+    }
+    
+    if ((*p == 'L' || *p == 'u' || *p == 'U') && p[1] == '"') {
+        return 1;
+    }
+    
+    if (*p == 'u' && p[1] == '8' && p[2] == '"') {
+        return 1;
+    }
+    
+    return 0;
+
+}
+
+static void append_global_zero_initializer_value (int64_s *values, char **symbols, int max_values, int *count) {
+
+    if (*count < max_values) {
+    
+        values[*count].low = 0;
+        values[*count].high = 0;
+        
+        if (symbols) {
+            symbols[*count] = 0;
+        }
+    
+    }
+
+    (*count)++;
+
+}
+
+static int aggregate_initializer_value_field_count (const int *field_sizes, int field_count) {
+
+    int count = 0;
+    int i;
+    
+    for (i = 0; i < field_count; i++) {
+    
+        if (field_sizes[i] > 0) {
+            count++;
+        }
+    
+    }
+    
+    return count;
+
+}
+
+static void parse_global_initializer_values_padded_elements (int64_s *values, char **symbols, int max_values, int *count, int element_field_count) {
+
+    if (element_field_count <= 0 || tok.kind != TOK_LBRACE) {
+    
+        parse_global_initializer_values (values, symbols, max_values, count);
+        return;
+    
+    }
+    
+    get_token ();
+    
+    while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) {
+    
+        int before = *count;
+        parse_global_initializer_values (values, symbols, max_values, count);
+        
+        if (*count > before) {
+        
+            while ((*count - before) < element_field_count) {
+                append_global_zero_initializer_value (values, symbols, max_values, count);
+            }
+        
+        }
+        
+        if (!_accept (TOK_COMMA)) {
+            break;
+        }
+    
+    }
+    
+    expect (TOK_RBRACE, "}");
+
+}
+
+static void parse_global_initializer_values (int64_s *values, char **symbols, int max_values, int *count) {
+
+    if (tok.kind == TOK_LBRACE) {
+    
+        get_token ();
+        
+        while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) {
+        
+            parse_global_initializer_values (values, symbols, max_values, count);
+            
+            if (!_accept (TOK_COMMA)) {
+                break;
+            }
+        
+        }
+        
+        expect (TOK_RBRACE, "}");
+        return;
+    
+    }
+    
+    if (tok.kind != TOK_COMMA && tok.kind != TOK_SEMI && tok.kind != TOK_RBRACE && tok.kind != TOK_EOF) {
+    
+        if (is_string_token ()) {
+        
+            if (*count < max_values) {
+            
+                values[*count].low = 0;
+                values[*count].high = 0;
+                
+                if (symbols) {
+                    symbols[*count] = emit_string_literal_global ();
+                }
+            
+            } else {
+                get_token ();
+            }
+            
+            (*count)++;
+        
+        } else if (tok.kind == TOK_LPAREN && global_initializer_parenthesized_string_now ()) {
+        
+            get_token ();
+            
+            if (*count < max_values) {
+            
+                values[*count].low = 0;
+                values[*count].high = 0;
+                
+                if (symbols) {
+                    symbols[*count] = emit_string_literal_global ();
+                } else {
+                
+                    int64_s ignored_values[MAX_AGG_FIELDS];
+                    int ignored_count = 0;
+                    
+                    parse_string_initializer_values (ignored_values, MAX_AGG_FIELDS, &ignored_count);
+                
+                }
+            
+            } else {
+            
+                int64_s ignored_values[MAX_AGG_FIELDS];
+                int ignored_count = 0;
+                
+                parse_string_initializer_values (ignored_values, MAX_AGG_FIELDS, &ignored_count);
+            
+            }
+            
+            expect (TOK_RPAREN, ")");
+            (*count)++;
+        
+        } else if (tok.kind == TOK_LPAREN && global_initializer_cast_before_string_now ()) {
+        
+            int cast_size = 0;
+            int cast_is_unsigned = 0;
+            int cast_is_pointer = 0;
+            
+            get_token ();
+            
+            if (token_starts_type_name () && parse_cast_type_name (&cast_size, &cast_is_unsigned, &cast_is_pointer) && is_string_token ()) {
+            
+                if (*count < max_values) {
+                
+                    values[*count].low = 0;
+                    values[*count].high = 0;
+                    
+                    if (symbols) {
+                        symbols[*count] = emit_string_literal_global ();
+                    }
+                
+                } else {
+                    get_token ();
+                }
+                
+                (*count)++;
+            
+            } else {
+            
+                if (*count < max_values) {
+                
+                    values[*count] = const64_from_current_expr ();
+                    
+                    if (symbols) {
+                        symbols[*count] = 0;
+                    }
+                
+                } else {
+                    const64_from_current_expr ();
+                }
+                
+                (*count)++;
+            
+            }
+        
+        } else if (tok.kind == TOK_AMPER) {
+        
+            get_token ();
+            
+            if (tok.kind != TOK_IDENT) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after '&'");
+            } else {
+            
+                char addr_symbol[256];
+                char *base_name = xstrdup (tok.ident);
+                long addr_offset = 0;
+                int sym_index = find_global_symbol (base_name);
+                long array_count = get_global_symbol_array_count (base_name);
+                long base_size = sym_index >= 0 ? global_symbols[sym_index].size : 0;
+                long elem_size = 0;
+                
+                if (array_count > 0 && base_size > 0) {
+                    elem_size = base_size / array_count;
+                }
+                
+                get_token ();
+                
+                while (tok.kind == TOK_LBRACK) {
+                
+                    int64_s index_value;
+                    long index;
+                    
+                    get_token ();
+                    
+                    index_value.low = 0;
+                    index_value.high = 0;
+                    
+                    if (tok.kind != TOK_RBRACK) {
+                        index_value = const64_from_current_expr ();
+                    }
+                    
+                    index = (long) index_value.low;
+                    
+                    if (elem_size > 0) {
+                        addr_offset += index * elem_size;
+                    }
+                    
+                    expect (TOK_RBRACK, "]");
+                
+                }
+                
+                if (*count < max_values) {
+                
+                    values[*count].low = 0;
+                    values[*count].high = 0;
+                    
+                    if (symbols) {
+                    
+                        if (addr_offset != 0) {
+                        
+                            sprintf (addr_symbol, "%s+%ld", base_name, addr_offset);
+                            symbols[*count] = xstrdup (addr_symbol);
+                        
+                        } else {
+                            symbols[*count] = xstrdup (base_name);
+                        }
+                    
+                    }
+                
+                }
+                
+                (*count)++;
+                free (base_name);
+            
+            }
+        
+        } else if (global_initializer_accept_symbol_addresses && tok.kind == TOK_IDENT && find_global_symbol (tok.ident) >= 0) {
+        
+            if (*count < max_values) {
+            
+                values[*count].low = 0;
+                values[*count].high = 0;
+                
+                if (symbols) {
+                    symbols[*count] = xstrdup (tok.ident);
+                }
+            
+            }
+            
+            (*count)++;
+            get_token ();
+        
+        } else if (*count < max_values) {
+        
+            values[*count] = const64_from_current_expr ();
+                        
+            if (symbols) {
+                symbols[*count] = 0;
+            }
+            
+            (*count)++;
+        
+        } else {
+            const64_from_current_expr ();
+        }
+    
+    }
+
+}
+
+static int int64_is_zero_value (int64_s value) {
+    return value.low == 0 && value.high == 0;
+}
+
+static void emit_global_scalar (int64_s value, int size) {
+
+    unsigned long high = value.high;
+    unsigned long low = value.low;
+    
+    if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+    
+        if (size == (DATA_CHAR & 0x1f)) {
+            fprintf (state->ofp, "    db %ld\n", low & 0xFFUL);
+        } else if (size == (DATA_SHORT & 0x1f)) {
+            fprintf (state->ofp, "    dw %ld\n", low & 0xFFFFUL);
+        } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_DOUBLE & 0x1f)) {
+        
+            fprintf (state->ofp, "    dd %lu\n", low & U32_MASK);
+            fprintf (state->ofp, "    dd %lu\n", high & U32_MASK);
+        
+        } else {
+            fprintf (state->ofp, "    dd %lu\n", low & U32_MASK);
+        }
+    
+    } else {
+    
+        if (size == (DATA_CHAR & 0x1f)) {
+            fprintf (state->ofp, "    .byte %ld\n", low & 0xFFUL);
+        } else if (size == (DATA_SHORT & 0x1f)) {
+            fprintf (state->ofp, "    .word %ld\n", low & 0xFFFFUL);
+        } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_DOUBLE & 0x1f)) {
+        
+            fprintf (state->ofp, "    .long %lu\n", low & U32_MASK);
+            fprintf (state->ofp, "    .long %lu\n", high & U32_MASK);
+        
+        } else {
+            fprintf (state->ofp, "    .long %lu\n", low & U32_MASK);
+        }
+    
+    }
+
+}
+
+static void emit_global_address (const char *symbol, int size) {
+
+    const char *asm_symbol;
+    int64_s zero;
+    
+    if (!symbol || !*symbol) {
+    
+        zero.low = 0;
+        zero.high = 0;
+        
+        emit_global_scalar (zero, size);
+        return;
+    
+    }
+    
+    if (size != DATA_PTR) {
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "address initializer requires pointer-sized object");
+    }
+    
+    emit_extern_reference_symbol (symbol, DATA_PTR);
+    asm_symbol = asm_global_symbol_name (symbol);
+    
+    if (state->syntax & ASM_SYNTAX_MASM) {
+        fprintf (state->ofp, "    dd offset %s\n", asm_symbol);
+    } else if (state->syntax & ASM_SYNTAX_NASM) {
+        fprintf (state->ofp, "    dd %s\n", asm_symbol);
+    } else {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+            fprintf (state->ofp, "    .long offset %s\n", asm_symbol);
+        } else {
+            fprintf (state->ofp, "    .long %s\n", asm_symbol);
+        }
+    
+    }
+
+}
+
+static void emit_global_value (const int64_s *value, const char *symbol, int size) {
+
+    int64_s zero;
+    
+    if (symbol) {
+    
+        emit_global_address (symbol, size);
+        return;
+    
+    }
+    
+    if (value) {
+    
+        emit_global_scalar (*value, size);
+        return;
+    
+    }
+    
+    zero.low = 0;
+    zero.high = 0;
+    
+    emit_global_scalar (zero, size);
+
+}
+
+static void emit_global_space (long bytes) {
+
+    if (bytes <= 0) {
+        return;
+    }
+    
+    if (state->syntax & ASM_SYNTAX_MASM) {
+        fprintf (state->ofp, "    db %ld dup (0)\n", bytes);
+    } else if (state->syntax & ASM_SYNTAX_NASM) {
+        fprintf (state->ofp, "    resb %ld\n", bytes);
+    } else {
+        fprintf (state->ofp, "    .space %ld\n", bytes);
+    }
+
+}
+
+static int field_size_bytes (int field_size) {
+
+    if (field_size < 0) {
+        return -field_size;
+    }
+    
+    return field_size;
+
+}
+
+static int global_initializer_is_all_zero (const int64_s *values, char **symbols, int value_count) {
+
+    int i;
+
+    if (symbols) {
+        for (i = 0; i < value_count; i++) {
+            if (symbols[i]) {
+                return 0;
+            }
+        }
+    }
+
+    if (!values || value_count <= 0) {
+        return 1;
+    }
+
+    for (i = 0; i < value_count; i++) {
+        if (!int64_is_zero_value (values[i])) {
+            return 0;
+        }
+    }
+
+    return 1;
+
+}
+
+static void emit_global_label (const char *name, int is_static) {
+
+    const char *asm_name = asm_global_symbol_name (name);
+    
+    if (state->syntax & ASM_SYNTAX_MASM) {
+    
+        if (!is_static) {
+            fprintf (state->ofp, "public %s\n", asm_name);
+        }
+        
+        fprintf (state->ofp, "%s:\n", asm_name);
+    
+    } else if (state->syntax & ASM_SYNTAX_NASM) {
+    
+        if (!is_static) {
+            fprintf (state->ofp, "global %s\n", asm_name);
+        }
+        
+        fprintf (state->ofp, "%s:\n", asm_name);
+    
+    } else {
+    
+        if (!is_static) {
+            fprintf (state->ofp, ".globl %s\n", asm_name);
+        }
+        
+        fprintf (state->ofp, "%s:\n", asm_name);
+    
+    }
+
+}
+
+static void emit_global_object (const char *name, int size, int is_array, long array_count, const int *field_sizes, int field_count, const int64_s *values, char **symbols, int value_count, int is_aggregate, int is_static) {
+
+    int64_s zero;
+    
+    int value_index, use_bss, i;
+    long emitted, total;
+
+    zero.low = 0;
+    zero.high = 0;
+    
+    if (!state->ofp || !name || !*name) {
+        return;
+    }
+    
+    if (size <= 0) {
+        size = DATA_INT & 0x1f;
+    }
+    
+    if (array_count <= 0) {
+        array_count = 1;
+    }
+    
+    total = size * array_count;
+    use_bss = global_initializer_is_all_zero (values, symbols, value_count);
+
+    if (use_bss) {
+    
+        switch_section (SECTION_BSS);
+        
+        emit_global_label (name, is_static);
+        emit_global_space (total);
+        
+        return;
+    
+    }
+    
+    switch_section (SECTION_DATA);
+    emit_global_label (name, is_static);
+    
+    value_index = 0;
+    emitted = 0;
+    
+    if (!is_array && !is_aggregate) {
+    
+        if (value_count > 0) {
+            emit_global_value (&values[0], symbols ? symbols[0] : 0, size);
+        } else {
+            emit_global_value (0, 0, size);
+        }
+        
+        return;
+    
+    }
+    
+    if (is_aggregate && field_count > 0) {
+    
+        while (emitted < total) {
+        
+            for (i = 0; i < field_count && emitted < total; i++) {
+            
+                int fsize = field_sizes[i];
+                
+                if (fsize < 0) {
+                
+                    emit_global_space (-fsize);
+                    
+                    emitted += -fsize;
+                    continue;
+                
+                }
+                
+                if (value_index < value_count) {
+                
+                    emit_global_value (&values[value_index], symbols ? symbols[value_index] : 0, fsize);
+                    value_index++;
+                
+                } else {
+                    emit_global_scalar (zero, fsize);
+                }
+                
+                emitted += field_size_bytes (fsize);
+            
+            }
+        
+        }
+        
+        emit_global_space (total - emitted);
+        return;
+    
+    }
+    
+    if (is_array) {
+    
+        int elem_size = size;
+        
+        if (field_count > 0 && field_sizes[0] > 0) {
+            elem_size = field_sizes[0];
+        }
+        
+        while (emitted + elem_size <= total) {
+        
+            if (value_index < value_count) {
+            
+                emit_global_value (&values[value_index], symbols ? symbols[value_index] : 0, elem_size);
+                value_index++;
+            
+            } else {
+                emit_global_scalar (zero, elem_size);
+            }
+            
+            emitted += elem_size;
+        
+        }
+        
+        emit_global_space (total - emitted);
+        return;
+    
+    }
+    
+    emit_global_space (total);
+
+}
+
+static char *emit_string_literal_global (void) {
+
+    int64_s values[MAX_AGG_FIELDS];
+    
+    char label[64];
+    int value_count = 0, i;
+    
+    if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+        sprintf (label, "L%d", anon_label++);
+    } else {
+        sprintf (label, ".L%d", anon_label++);
+    }
+    
+    parse_string_initializer_values (values, MAX_AGG_FIELDS, &value_count);
+    
+    /*
+     * Dead statement parsing suppresses output by temporarily clearing
+     * state->ofp.  Still consume the literal so the token stream remains
+     * correct, but do not try to emit a .data label through a NULL FILE *.
+     * This is needed for optimised-away bodies such as:
+     *
+     *     while (0) { printf ("Hello\n"); }
+     */
+    if (!state->ofp) {
+        return xstrdup (label);
+    }
+    
+    if (current_section == SECTION_TEXT) {
+    
+        char skip_label[64];
+        
+        if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+            sprintf (skip_label, "L%d", anon_label++);
+        } else {
+            sprintf (skip_label, ".L%d", anon_label++);
+        }
+        
+        fprintf (state->ofp, "    jmp %s\n", skip_label);
+        
+        switch_section (SECTION_DATA);
+        emit_global_label (label, 1);
+        
+        for (i = 0; i < value_count; i++) {
+            emit_global_scalar (values[i], DATA_CHAR & 0x1f);
+        }
+        
+        switch_section (SECTION_TEXT);
+        emit_global_label (skip_label, 1);
+        
+        return xstrdup (label);
+    
+    }
+    
+    switch_section (SECTION_DATA);
+    emit_global_label (label, 1);
+    
+    for (i = 0; i < value_count; i++) {
+        emit_global_scalar (values[i], DATA_CHAR & 0x1f);
+    }
+    
+    return xstrdup (label);
+
+}
+
+static const char *masm_extern_type_name (int size, int is_function) {
+
+    if (is_function) {
+        return "PROC";
+    }
+
+    if (size == (DATA_CHAR & 0x1f)) {
+        return "BYTE";
+    }
+
+    if (size == (DATA_SHORT & 0x1f)) {
+        return "WORD";
+    }
+
+    if (size == (DATA_LLONG & 0x1f) || size == (DATA_DOUBLE & 0x1f)) {
+        return "QWORD";
+    }
+
+    return "DWORD";
+
+}
+
+static void emit_extern_line (const char *name, int size, int is_function) {
+
+    const char *asm_name = asm_global_symbol_name (name);
+    
+    if (state->syntax & ASM_SYNTAX_MASM) {
+        fprintf (state->ofp, "extrn %s:%s\n", asm_name, masm_extern_type_name (size, is_function));
+    } else if (state->syntax & ASM_SYNTAX_NASM) {
+        fprintf (state->ofp, "extern %s\n", asm_name);
+    } else {
+        fprintf (state->ofp, ".extern %s\n", asm_name);
+    }
+
+}
+
+static void emit_extern_symbol (const char *name, int size, int is_function) {
+
+    int i;
+    
+    (void) size;
+    (void) is_function;
+    
+    if (!state->ofp || !name || !*name) {
+        return;
+    }
+    
+    i = find_global_symbol (name);
+    
+    /**
+     * Do not write assembler externs at the point of use.  A symbol can be
+     * declared/called before its real definition appears later in the same
+     * translation unit; NASM then rejects "extern foo" followed by "foo:" as
+     * an inconsistent redefinition.  Just mark that generated code referenced
+     * the external-looking symbol, and emit the actual assembler externs once
+     * the whole file has been parsed and we know which names stayed external.
+     */
+    if (i < 0 || !global_symbols[i].is_extern) {
+        return;
+    }
+    
+    global_symbols[i].extern_emitted = 1;
+
+}
+
+static void emit_pending_extern_symbols (void) {
+
+    int i;
+    
+    if (!state->ofp) {
+        return;
+    }
+    
+    for (i = 0; i < global_symbol_count; i++) {
+    
+        if (!global_symbols[i].is_extern || !global_symbols[i].extern_emitted) {
+            continue;
+        }
+        
+        emit_extern_line (global_symbols[i].name,
+            global_symbols[i].size > 0 ? global_symbols[i].size : (DATA_INT & 0x1f),
+                global_symbols[i].kind == GLOBAL_SYMBOL_FUNCTION);
+    
+    }
+
+}
+
+static void emit_extern_reference_symbol (const char *name, int size) {
+    emit_extern_symbol (name, size, get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION);
+}
+
+static void parse_external_after_type (void) {
+
+    for (;;) {
+    
+        char **init_symbols = 0;
+        int64_s *init_values = 0;
+        
+        char *name = 0;
+        int init_value_count = 0, i;
+        
+        int object_fields[MAX_AGG_FIELDS];
+        int object_field_count = 0;
+        
+        const char *name_start, *name_caret;
+        unsigned long name_line;
+        
+        int saved_field_count = parsed_field_count;
+        
+        int declaration_is_inline;
+        int declaration_storage;
+        
+        int decl_is_pointer;
+        int decl_pointer_depth;
+        int decl_has_array;
+        int decl_has_function;
+        int decl_function_is_pointer;
+        int decl_function_param_count;
+        int decl_function_has_prototype;
+        int decl_function_is_variadic;
+        int decl_array_unsized;
+        
+        long decl_array_count;
+        long decl_last_array_count;
+        
+        for (i = 0; i < saved_field_count; i++) {
+            object_fields[i] = parsed_field_sizes[i];
+        }
+        
+        object_field_count = saved_field_count;
+        
+        declaration_is_inline = parsed_type_is_inline;
+        declaration_storage = parsed_storage_class;
+        
+        parse_declarator (&name);
+        apply_typedef_array_to_declarator ();
+        
+        decl_is_pointer = declarator_is_pointer;
+        decl_pointer_depth = declarator_pointer_depth;
+        decl_has_array = declarator_has_array;
+        decl_has_function = declarator_has_function;
+        decl_function_is_pointer = declarator_function_is_pointer;
+        decl_function_param_count = declarator_function_param_count;
+        decl_function_has_prototype = declarator_function_has_prototype;
+        decl_function_is_variadic = declarator_function_is_variadic;
+        decl_array_unsized = declarator_array_unsized;
+        decl_array_count = declarator_array_count;
+        decl_last_array_count = declarator_last_array_count;
+        
+        name_start = last_declarator_name_start;
+        name_caret = last_declarator_name_caret;
+        
+        name_line = last_declarator_name_line;
+        
+        if (declaration_storage == STORAGE_TYPEDEF) {
+        
+            if (name) {
+            
+                make_declarator_fields (object_fields, &object_field_count, parsed_field_sizes, parsed_field_count, parsed_type_size, parsed_type_is_aggregate);
+                
+                save_typedef_name (name, declarator_object_size (parsed_type_size),
+                    (declarator_is_pointer ? 0 : parsed_type_is_unsigned),
+                        (declarator_is_pointer ? 0 : parsed_type_is_void),
+                            (!declarator_is_pointer && (parsed_type_is_aggregate || declarator_has_array)),
+                                (!declarator_is_pointer && declarator_has_array),
+                                    declarator_array_count, parsed_type_size,
+                                        object_fields, object_field_count);
+            
+            }
+            
+            if (_accept (TOK_ASSIGN)) {
+            
+                report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "typedef '%s' is initialized", name ? name : "");
+                skip_initializer ();
+            
+            }
+            
+            if (name) {
+                free (name);
+            }
+            
+            if (!_accept (TOK_COMMA)) {
+                break;
+            }
+            
+            continue;
+        
+        }
+        
+        if (name && tok.kind == TOK_LBRACE) {
+        
+            parse_function_body (name, declaration_storage, declaration_is_inline,
+                (parsed_type_is_void && !declarator_is_pointer),
+                    (declarator_is_pointer ? 0 : parsed_type_is_floating),
+                        (declarator_is_pointer ? 0 : parsed_type_is_unsigned),
+                            (declarator_is_pointer ? DATA_PTR : parsed_type_size),
+                                name_line, name_start, name_caret);
+            
+            free (name);
+            return;
+        
+        }
+        
+        if (parsed_type_is_void && !declarator_is_pointer && !declarator_has_function) {
+            report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "variable '%s' declared void", name ? name : "");
+        }
+        
+        if (declarator_is_pointer) {
+        
+            object_field_count = 1;
+            object_fields[0] = DATA_PTR;
+        
+        }
+        
+        init_values = xmalloc (sizeof (*init_values) * MAX_GLOBAL_INIT_FIELDS);
+        init_symbols = xmalloc (sizeof (*init_symbols) * MAX_GLOBAL_INIT_FIELDS);
+        
+        for (i = 0; i < MAX_GLOBAL_INIT_FIELDS; i++) {
+            init_symbols[i] = 0;
+        }
+        
+        if (_accept (TOK_ASSIGN)) {
+        
+            if (declarator_has_array && !declarator_is_pointer && (parsed_type_size & 0x1f) == (DATA_CHAR & 0x1f) && (is_string_token () || tok.kind == TOK_LBRACE)) {
+            
+                if (is_string_token ()) {
+                    parse_string_initializer_values (init_values, MAX_GLOBAL_INIT_FIELDS, &init_value_count);
+                } else {
+                    parse_char_array_initializer_values (init_values, MAX_GLOBAL_INIT_FIELDS, &init_value_count, declarator_last_array_count);
+                }
+                
+                if (decl_array_unsized) {
+                    decl_array_count = init_value_count;
+                }
+            
+            } else if (declarator_is_pointer && is_string_token ()) {
+            
+                init_values[0].low = 0;
+                init_values[0].high = 0;
+                
+                init_symbols[0] = emit_string_literal_global ();
+                init_value_count = 1;
+            
+            } else if (!declarator_is_pointer && parsed_type_is_floating) {
+            
+                init_values[0] = parse_floating_const_expr_bits_now (parsed_type_size);
+                init_symbols[0] = 0;
+                init_value_count = 1;
+            
+            } else {
+            
+                {
+                
+                    int saved_accept_symbol_addresses = global_initializer_accept_symbol_addresses;
+                    global_initializer_accept_symbol_addresses = declarator_is_pointer || declarator_has_function || parsed_type_is_aggregate || declarator_has_array;
+                    
+                    if (declarator_has_array && !declarator_is_pointer && parsed_type_is_aggregate && parsed_field_count > 0 && tok.kind == TOK_LBRACE) {
+                        parse_global_initializer_values_padded_elements (init_values, init_symbols, MAX_GLOBAL_INIT_FIELDS, &init_value_count, aggregate_initializer_value_field_count (parsed_field_sizes, parsed_field_count));
+                    } else {
+                        parse_global_initializer_values (init_values, init_symbols, MAX_GLOBAL_INIT_FIELDS, &init_value_count);
+                    }
+                    
+                    global_initializer_accept_symbol_addresses = saved_accept_symbol_addresses;
+                
+                }
+                
+                if (decl_has_array && decl_array_unsized) {
+                
+                    if (parsed_type_is_aggregate && object_field_count > 0) {
+                        decl_array_count = (init_value_count + object_field_count - 1) / object_field_count;
+                    } else {
+                        decl_array_count = init_value_count;
+                    }
+                
+                }
+            
+            }
+        
+        }
+        
+        if (init_value_count > MAX_GLOBAL_INIT_FIELDS) {
+        
+            report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "too many initializer values for '%s'", name ? name : "");
+            init_value_count = MAX_GLOBAL_INIT_FIELDS;
+        
+        }
+        
+        declarator_is_pointer = decl_is_pointer;
+        declarator_pointer_depth = decl_pointer_depth;
+        declarator_has_array = decl_has_array;
+        declarator_has_function = decl_has_function;
+        declarator_function_is_pointer = decl_function_is_pointer;
+        declarator_function_param_count = decl_function_param_count;
+        declarator_function_has_prototype = decl_function_has_prototype;
+        declarator_function_is_variadic = decl_function_is_variadic;
+        declarator_array_unsized = decl_array_unsized;
+        declarator_array_count = decl_array_count;
+        declarator_last_array_count = decl_last_array_count;
+        
+        if (name) {
+        
+            if (declarator_has_function && !declarator_function_is_pointer && !declarator_has_array) {
+            
+                /*
+                 * A file-scope function declaration is a declaration even
+                 * without an explicit extern storage class.  Keep it in the
+                 * global symbol table so a later call does not fall back to
+                 * the implicit-function-declaration path.
+                 *
+                 * Store prototypes as extern-like declarations here.  A real
+                 * function body will later turn the symbol into a definition
+                 * through add_global_symbol(), which already handles replacing
+                 * an extern declaration with the definition.
+                 */
+                if (add_global_symbol (name, GLOBAL_SYMBOL_FUNCTION, 1, name_start, name_caret, name_line)) {
+                
+                    set_global_symbol_size (name, declarator_is_pointer ? DATA_PTR : (parsed_type_is_void ? DATA_VOID : parsed_type_size));
+                    set_global_symbol_pointer_info (name, declarator_effective_pointer_depth_now (),
+                        declarator_effective_pointed_size_now (parsed_type_size, object_fields, object_field_count));
+                    set_global_symbol_tag_name (name, parsed_type_tag_name);
+                    set_global_symbol_unsigned (name, 0);
+                    set_global_symbol_floating (name, declarator_is_pointer ? 0 : parsed_type_is_floating);
+                    set_global_symbol_returns_void (name, parsed_type_is_void && !declarator_is_pointer && !declarator_function_is_pointer);
+                    set_global_symbol_param_count (name, declarator_function_param_count, declarator_function_has_prototype || declarator_function_param_count > 0, declarator_function_is_variadic);
+                
+                }
+            
+            } else if (declaration_storage == STORAGE_EXTERN && init_value_count == 0) {
+            
+                if (add_global_symbol (name, GLOBAL_SYMBOL_OBJECT, 1, name_start, name_caret, name_line)) {
+                
+                    set_global_symbol_size (name, declarator_is_pointer ? DATA_PTR : declarator_object_size (parsed_type_size));
+                    set_global_symbol_pointer_info (name, declarator_effective_pointer_depth_now (),
+                        declarator_effective_pointed_size_now (parsed_type_size, object_fields, object_field_count));
+                    set_global_symbol_tag_name (name, parsed_type_tag_name);
+                    set_global_symbol_unsigned (name, declarator_is_pointer ? 0 : parsed_type_is_unsigned);
+                    set_global_symbol_array (name, declarator_has_array);
+                    set_global_symbol_array_count (name, declarator_has_array ? declarator_array_count : 0);
+                    set_global_symbol_array_element_size (name, declarator_has_array ? (int)(declarator_object_size (parsed_type_size) / (declarator_array_count > 0 ? declarator_array_count : 1)) : 0);
+                
+                }
+            
+            } else {
+            
+                if (add_global_symbol (name, GLOBAL_SYMBOL_OBJECT, 0, name_start, name_caret, name_line)) {
+                
+                    set_global_symbol_size (name, declarator_is_pointer ? DATA_PTR : declarator_object_size (parsed_type_size));
+                    set_global_symbol_pointer_info (name, declarator_effective_pointer_depth_now (),
+                        declarator_effective_pointed_size_now (parsed_type_size, object_fields, object_field_count));
+                    set_global_symbol_tag_name (name, parsed_type_tag_name);
+                    set_global_symbol_unsigned (name, declarator_is_pointer ? 0 : parsed_type_is_unsigned);
+                    set_global_symbol_floating (name, declarator_is_pointer ? 0 : parsed_type_is_floating);
+                    set_global_symbol_array (name, declarator_has_array);
+                    set_global_symbol_array_count (name, declarator_has_array ? declarator_array_count : 0);
+                    set_global_symbol_array_element_size (name, declarator_has_array ? (int)(declarator_object_size (parsed_type_size) / (declarator_array_count > 0 ? declarator_array_count : 1)) : 0);
+                    
+                    emit_global_object (name, declarator_is_pointer ? DATA_PTR : (declarator_has_array ? parsed_type_size : (parsed_type_is_aggregate ? parsed_type_size : (parsed_type_size & 0x1f))),
+                        declarator_has_array, declarator_array_count, object_fields, object_field_count,
+                            init_values, init_symbols, init_value_count, (!declarator_is_pointer && parsed_type_is_aggregate),
+                                declaration_storage == STORAGE_STATIC);
+                
+                }
+            
+            }
+            
+            free (name);
+        
+        }
+        
+        for (i = 0; i < MAX_GLOBAL_INIT_FIELDS; i++) {
+        
+            if (init_symbols[i]) {
+            
+                free (init_symbols[i]);
+                init_symbols[i] = 0;
+            
+            }
+        
+        }
+        
+        free (init_symbols);
+        free (init_values);
+        
+        if (!_accept (TOK_COMMA)) {
+            break;
+        }
+    
+    }
+    
+    if (tok.kind == TOK_SEMI) {
+        get_token ();
+    } else if (is_type_start (tok.kind)) {
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "missing ';' after declaration");
+    } else {
+        expect (TOK_SEMI, ";");
+    }
+
+}
+
+void compile_translation_unit (void) {
+
+    if (state->ofp) {
+    
+        if (state->syntax & ASM_SYNTAX_MASM) {
+        
+            fprintf (state->ofp, ".386\n");
+            fprintf (state->ofp, ".model flat, c\n");
+        
+        } else if (state->syntax & ASM_SYNTAX_NASM) {
+        
+            fprintf (state->ofp, "cpu 386\n");
+            fprintf (state->ofp, "bits 32\n");
+        
+        } else if (state->syntax & ASM_SYNTAX_INTEL) {
+            fprintf (state->ofp, ".intel_syntax noprefix\n");
+        }
+    
+    }
+    
+    current_section = SECTION_NONE;
+    
+    clear_global_symbols ();
+    clear_inline_functions ();
+    clear_enum_constants ();
+    clear_typedef_names ();
+    
+    get_token ();
+    
+    while (tok.kind != TOK_EOF) {
+    
+        if (is_type_start (tok.kind)) {
+        
+            parse_type_spec ();
+            parse_external_after_type ();
+            
+            continue;
+        
+        }
+        
+        if (parse_possible_knr_function ()) {
+            continue;
+        }
+        
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected external declaration");
+        get_token ();
+    
+    }
+    
+    emit_pending_extern_symbols ();
+    
+    if (state->ofp) {
+    
+        if (state->syntax & ASM_SYNTAX_MASM) {
+            fprintf (state->ofp, "end\n");
+        }
+    
+    }
+
+}
diff --git a/parse.h b/parse.h
new file mode 100644 (file)
index 0000000..ca59939
--- /dev/null
+++ b/parse.h
@@ -0,0 +1,18 @@
+/******************************************************************************
+ * @file            parse.h
+ *****************************************************************************/
+#ifndef     _PARSE_H
+#define     _PARSE_H
+
+void compile_translation_unit (void);
+
+int token_is_sizeof_keyword (void);
+int parse_sizeof_value (void);
+
+int token_starts_type_name (void);
+int parse_cast_type_name (int *out_size, int *out_is_unsigned, int *out_is_pointer);
+
+int parse_constexpr_address_of_null_member (int64_s *out);
+int resolve_enum_constant (const char *name, int64_s *out);
+
+#endif      /* _PARSE_H */
\ No newline at end of file
diff --git a/pp.c b/pp.c
new file mode 100755 (executable)
index 0000000..18de06d
--- /dev/null
+++ b/pp.c
@@ -0,0 +1,2031 @@
+/******************************************************************************
+ * @file            pp.c
+ *****************************************************************************/
+#include    <ctype.h>
+#include    <stdio.h>
+#include    <stdlib.h>
+#include    <string.h>
+#include    <time.h>
+
+#include    "cc.h"
+#include    "eval.h"
+#include    "hashtab.h"
+#include    "lex.h"
+#include    "lib.h"
+#include    "ll.h"
+#include    "macro.h"
+#include    "pp.h"
+#include    "report.h"
+#include    "token.h"
+#include    "vector.h"
+
+static void hash_line (const char *filename, unsigned long line_number);
+static int flags = 0;
+
+static int iflevel = 0;
+int ignore_line = 0;
+
+struct vector vec_include_paths = { 0 };
+struct vector vec_ifstack = { 0 };
+
+static int handler_if (char *start, char **pp) {
+
+    struct cond *cond;
+    int ret = 1;
+    
+    if (!ignore_line) {
+    
+        cond = xmalloc (sizeof (*cond));
+        
+        cond->ignore_line = ignore_line;
+        cond->directive = xstrdup ("if");
+        
+        cond->filename = xstrdup (get_filename ());
+        cond->line_number = get_line_number ();
+        
+        vec_push (&vec_ifstack, cond);
+        ret = !eval (start, pp);
+        
+        cond->need_else = !ret;
+    
+    } else {
+        iflevel++;
+    }
+    
+    return ret;
+
+}
+
+static int handler_ifdef (char *start, char **pp) {
+
+    struct cond *cond;
+    
+    char *sname, *caret;
+    int ret = 1;
+    
+    if (!ignore_line) {
+    
+        cond = xmalloc (sizeof (*cond));
+        
+        cond->ignore_line = ignore_line;
+        cond->directive = xstrdup ("ifdef");
+        
+        cond->filename = xstrdup (get_filename ());
+        cond->line_number = get_line_number ();
+        
+        vec_push (&vec_ifstack, cond);
+        *pp = skip_whitespace (*pp);
+        
+        if (is_name_beginner ((int) **pp)) {
+        
+            caret = (*pp);
+            
+            while (is_name_part ((int) **pp)) {
+                (*pp)++;
+            }
+            
+            sname = xstrndup (caret, *pp - caret);
+            ret = (find_macro (sname) == NULL);
+            
+            free (sname);
+        
+        }
+        
+        *pp = skip_whitespace (*pp);
+        
+        if (!is_end_of_line[(int) **pp]) {
+            report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, *pp, "extra tokens at end of #ifdef directive");
+        }
+        
+        cond->need_else = !ret;
+    
+    } else {
+        iflevel++;
+    }
+    
+    return ret;
+
+}
+
+static int handler_ifndef (char *start, char **pp) {
+
+    struct cond *cond;
+    
+    char *sname, *caret;
+    int ret = 1;
+    
+    if (!ignore_line) {
+    
+        cond = xmalloc (sizeof (*cond));
+        
+        cond->ignore_line = ignore_line;
+        cond->directive = xstrdup ("ifndef");
+        
+        cond->filename = xstrdup (get_filename ());
+        cond->line_number = get_line_number ();
+        
+        vec_push (&vec_ifstack, cond);
+        *pp = skip_whitespace (*pp);
+        
+        if (is_name_beginner ((int) **pp)) {
+        
+            caret = (*pp);
+            
+            while (is_name_part ((int) **pp)) {
+                (*pp)++;
+            }
+            
+            sname = xstrndup (caret, *pp - caret);
+            ret = !(find_macro (sname) == NULL);
+            
+            free (sname);
+        
+        }
+        
+        *pp = skip_whitespace (*pp);
+        
+        if (!is_end_of_line[(int) **pp]) {
+            report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, *pp, "extra tokens at end of #ifndef directive");
+        }
+        
+        cond->need_else = !ret;
+    
+    } else {
+        iflevel++;
+    }
+    
+    return ret;
+
+}
+
+static int handler_elif (char *start, char **pp) {
+
+    struct cond *cond;
+    int ret = 1;
+    
+    if (!iflevel) {
+    
+        if (vec_ifstack.length == 0) {
+        
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, skip_whitespace (start + 1), "#elif without #if");
+            return ret;
+        
+        } else {
+        
+            cond = vec_ifstack.data[vec_ifstack.length - 1];
+            
+            if (cond->has_else > 0) {
+            
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, skip_whitespace (start + 1), "#elif after #else");
+                return ret;
+            
+            }
+        
+        }
+        
+        if (ignore_line) {
+            ret = !eval (start, pp);
+        }
+        
+        cond->need_else = !ret || !ignore_line;
+    
+    }
+    
+    return ret;
+
+}
+
+static int handler_else (char *start, char **pp) {
+
+    struct cond *cond;
+    int ret = 1;
+    
+    if (!iflevel) {
+    
+        if (vec_ifstack.length == 0) {
+        
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, skip_whitespace (start + 1), "#else without #if");
+            return ret;
+        
+        } else {
+        
+            cond = vec_ifstack.data[vec_ifstack.length - 1];
+            
+            if (cond->has_else > 0) {
+            
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, skip_whitespace (start + 1), "#else after #else");
+                return ret;
+            
+            }
+            
+            cond->has_else++;
+        
+        }
+        
+        *pp = skip_whitespace (*pp);
+        
+        if (!is_end_of_line[(int) **pp]) {
+            report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, *pp, "extra tokens at end of #else directive");
+        }
+        
+        ret = cond->need_else;
+    
+    }
+    
+    return ret;
+
+}
+
+static int handler_endif (char *start, char **pp) {
+
+    struct cond *cond;
+    int ret = 1;
+    
+    if (!iflevel) {
+    
+        if ((cond = vec_pop (&vec_ifstack))) {
+        
+            ret = cond->ignore_line;
+            
+            free (cond->filename);
+            free (cond->directive);
+            
+            free (cond);
+        
+        } else {
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, skip_whitespace (start + 1), "#endif without #if");
+        }
+        
+        *pp = skip_whitespace (*pp);
+        
+        if (!is_end_of_line[(int) **pp]) {
+            report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, *pp, "extra tokens at end of #endif directive");
+        }
+    
+    } else {
+        iflevel--;
+    }
+    
+    return ret;
+
+}
+
+static struct pp_pseudo_op_entry cond_pseudo_op_table[] = {
+
+    {   "if",           &handler_if,            },
+    {   "ifdef",        &handler_ifdef          },
+    {   "ifndef",       &handler_ifndef         },
+    {   "elif",         &handler_elif           },
+    {   "else",         &handler_else           },
+    {   "endif",        &handler_endif          },
+    
+    {   0,              0                       }
+
+};
+
+static struct hashtab hashtab_cond_pseudo_ops = { 0 };
+static int includes = 0;
+
+static void install_cond_pseudo_op_table (struct pp_pseudo_op_entry *table) {
+
+    struct pp_pseudo_op_entry *entry;
+    struct hashtab_name *key;
+    
+    for (entry = table; entry->name; entry++) {
+    
+        if (hashtab_get_key (&hashtab_cond_pseudo_ops, entry->name)) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "duplicate entry '%s'", entry->name);
+            continue;
+        
+        }
+        
+        if (!(key = hashtab_alloc_name (entry->name))) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "failed to allocate memory for '%s'", entry->name);
+            continue;
+        
+        }
+        
+        hashtab_put (&hashtab_cond_pseudo_ops, key, entry);
+    
+    }
+
+}
+
+struct pp_pseudo_op_entry *find_cond_directive (char *name) {
+
+    struct hashtab_name *key;
+    struct pp_pseudo_op_entry *entry;
+    
+    if ((key = hashtab_get_key (&hashtab_cond_pseudo_ops, name))) {
+    
+        if ((entry = hashtab_get (&hashtab_cond_pseudo_ops, key))) {
+            return entry;
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+
+static struct hashtab hashtab_pseudo_ops = { 0 };
+
+static int handler_define (char *start, char **pp) {
+
+    add_macro (start, pp, 1);
+    return 1;
+
+}
+
+static int handler_error (char *start, char **pp) {
+
+    unsigned int len = strlen (*pp);
+    char *temp, *type = "error";
+    
+    if ((*pp)[len - 1] == '\n') {
+        (*pp)[len - 1] = '\0';
+    }
+    
+    temp = xmalloc (1 + strlen (type) + 1 + strlen (*pp) + 1);
+    sprintf (temp, "#%s %s", type, *pp);
+    
+    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, skip_whitespace (start + 1), "%s", temp);
+    free (temp);
+    
+    return 1;
+
+}
+
+static int handler_include (char *start, char **pp) {
+
+    const char *orig_fn = get_filename ();
+    unsigned long orig_ln = get_line_number ();
+    
+    char *caret, *sname, ch;
+    int i;
+    
+    char *inc_path, *tmp;
+    FILE *fp;
+    
+    struct hashtab_name *key;
+    struct macro *m;
+    
+    char *current_directory,*p;
+    int len;
+    
+    if (**pp != '"' && **pp != '<') {
+    
+        report_line_at (orig_fn, orig_ln, REPORT_ERROR, start, *pp, "#include expects \"FILENAME\" or <FILENAME>");
+        return 1;
+    
+    }
+    
+    ch = (**pp == '"' ? '"' : '>');
+    caret = (*pp)++;
+    
+    while (!is_end_of_line[(int) **pp]) {
+    
+        if (**pp == ch) { break; }
+        (*pp)++;
+    
+    }
+    
+    if (**pp != ch) {
+    
+        report_line_at (orig_fn, orig_ln, REPORT_ERROR, start, caret, "#include expects \"FILENAME\" or <FILENAME>");
+        return 1;
+    
+    } else {
+        (*pp)++;
+    }
+    
+    sname = xstrndup (caret + 1, *pp - caret - 2);
+    
+    if (ch == '"') {
+    
+#if     defined (unix) || defined (__unix) || defined (__unix__) || defined (__APPLE__)
+        if (sname[0] == '/') {
+#else
+        if ((isalpha ((int) sname[0]) && sname[1] == ':') || sname[0] == '/' || sname[0] == '\\') {
+#endif
+        
+            if ((fp = fopen (sname, "r"))) {
+            
+                flags = 1;
+                fclose (fp);
+                
+                includes++;
+                preprocess_file (sname);
+                
+                includes--;
+                
+                flags = 2;
+                goto end;
+            
+            }
+            
+            goto error;
+        
+        } else if ((p = strrchr (orig_fn, '/')) || (p = strrchr (orig_fn, '\\'))) {
+        
+            len = p - orig_fn;
+            
+            tmp = xmalloc (len + 1 + strlen (sname) + 1);
+            sprintf (tmp, "%.*s/%s", len, orig_fn, sname);
+            
+            if ((fp = fopen (tmp, "r"))) {
+            
+                flags = 1;
+                fclose (fp);
+                
+                includes++;
+                preprocess_file (tmp);
+                
+                includes--;
+                free (tmp);
+                
+                flags = 2;
+                goto end;
+            
+            }
+            
+            free (tmp);
+        
+        }
+        
+#if     defined (unix) || defined (__unix) || defined (__unix__) || defined (__APPLE__)
+        if (orig_fn[0] == '/') {
+#else
+        if ((isalpha ((int) orig_fn[0]) && orig_fn[1] == ':') || orig_fn[0] == '/' || orig_fn[0] == '\\') {
+#endif
+        
+            if ((current_directory = get_current_directory ())) {
+            
+                if (strcmp (current_directory, "") == 0) {
+                
+                    tmp = xmalloc (strlen (sname) + 1);
+                    sprintf (tmp, "%s", sname);
+                
+                } else {
+                
+                    tmp = xmalloc (strlen (current_directory) + 1 + strlen (sname) + 1);
+                    sprintf (tmp, "%s/%s", current_directory, sname);
+                
+                }
+                
+                free (current_directory);
+                
+                if ((fp = fopen (tmp, "r"))) {
+                
+                    flags = 1;
+                    fclose (fp);
+                    
+                    includes++;
+                    preprocess_file (tmp);
+                    
+                    includes--;
+                    free (tmp);
+                    
+                    flags = 2;
+                    goto end;
+                
+                }
+                
+                free (tmp);
+            
+            }
+        
+        } else {
+        
+            if ((fp = fopen (sname, "r"))) {
+            
+                flags = 1;
+                fclose (fp);
+                
+                includes++;
+                preprocess_file (sname);
+                
+                includes--;
+                
+                flags = 2;
+                goto end;
+            
+            }
+        
+        }
+    
+    }
+    
+    if (vec_include_paths.length > 0) {
+    
+        for (i = 0; i < vec_include_paths.length; i++) {
+        
+            inc_path = vec_include_paths.data[i];
+            
+            tmp = xmalloc (strlen (inc_path) + strlen (sname) + 1);
+            sprintf (tmp, "%s%s", inc_path, sname);
+            
+            if ((fp = fopen (tmp, "r"))) {
+            
+                flags = 1;
+                fclose (fp);
+                
+                includes++;
+                preprocess_file (tmp);
+                
+                includes--;
+                free (tmp);
+                
+                flags = 2;
+                goto end;
+            
+            }
+            
+            free (tmp);
+        
+        }
+    
+    }
+    
+error:
+    
+    report_line_at (orig_fn, orig_ln, REPORT_ERROR, start, caret, "failed to open '%s' for reading", sname);
+    
+end:
+    
+    set_filename_and_line_number (orig_fn, orig_ln);
+    
+    if ((key = find_macro ("__FILE__"))) {
+    
+        if ((m = get_macro (key)) && m->type == MACRO_BUILTIN) {
+        
+            free (m->value);
+            
+            m->value = xmalloc (1 + strlen (orig_fn) + 2);
+            sprintf (m->value, "\"%s\"", orig_fn);
+        
+        }
+    
+    }
+    
+    if ((key = find_macro ("__LINE__"))) {
+    
+        if ((m = get_macro (key)) && m->type == MACRO_BUILTIN) {
+        
+            free (m->value);
+            
+            m->value = xmalloc (16);
+            sprintf (m->value, "%lu", orig_ln);
+        
+        }
+    
+    }
+    
+    free (sname);
+    return 0;
+
+}
+
+static int handler_undef (char *start, char **pp) {
+
+    remove_macro (start, pp, 1);
+    return 1;
+
+}
+
+static int handler_warning (char *start, char **pp) {
+
+    unsigned int len = strlen (*pp);
+    char *temp, *type = "warning";
+    
+    if ((*pp)[len - 1] == '\n') {
+        (*pp)[len - 1] = '\0';
+    }
+    
+    temp = xmalloc (1 + strlen (type) + 1 + strlen (*pp) + 1);
+    sprintf (temp, "#%s %s", type, *pp);
+    
+    report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, skip_whitespace (start + 1), "%s", temp);
+    free (temp);
+    
+    return 1;
+
+}
+
+static struct pp_pseudo_op_entry pseudo_op_table[] = {
+
+    {   "define",       &handler_define         },
+    {   "error",        &handler_error          },
+    {   "undef",        &handler_undef          },
+    {   "warning",      &handler_warning        },
+    
+    {   0,              0                       }
+
+};
+
+static void install_pp_pseudo_op_table (struct pp_pseudo_op_entry *table) {
+
+    struct pp_pseudo_op_entry *entry;
+    struct hashtab_name *key;
+    
+    for (entry = table; entry->name; entry++) {
+    
+        if (hashtab_get_key (&hashtab_pseudo_ops, entry->name)) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "duplicate entry '%s'", entry->name);
+            continue;
+        
+        }
+        
+        if (!(key = hashtab_alloc_name (entry->name))) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "failed to allocate memory for '%s'", entry->name);
+            continue;
+        
+        }
+        
+        hashtab_put (&hashtab_pseudo_ops, key, entry);
+    
+    }
+
+}
+
+struct pp_pseudo_op_entry *find_directive (char *name) {
+
+    struct hashtab_name *key;
+    struct pp_pseudo_op_entry *entry;
+    
+    if ((key = hashtab_get_key (&hashtab_pseudo_ops, name))) {
+    
+        if ((entry = hashtab_get (&hashtab_pseudo_ops, key))) {
+            return entry;
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+
+static void init_builtin_macros (void) {
+
+    static char *builtins[] = { "__FILE__", "__LINE__" };
+    char *name;
+    
+    struct hashtab_name *key;
+    struct macro *m;
+    
+    unsigned int cnt = (sizeof (builtins) / sizeof (*builtins));
+    unsigned i;
+    
+    for (i = 0; i < cnt; i++) {
+    
+        name = xstrdup (builtins[i]);
+        
+        if ((key = hashtab_alloc_name (name))) {
+        
+            m = xmalloc (sizeof (*m));
+            m->type = MACRO_BUILTIN;
+            
+            m->name = name;
+            m->value = xstrdup ("");
+            
+            m->nargs = -1;
+            push_macro (key, m);
+        
+        }
+    
+    }
+    
+    name = xstrdup ("__SCC__");
+    
+    if ((key = hashtab_alloc_name (name))) {
+    
+        m = xmalloc (sizeof (*m));
+        m->type = MACRO_BUILTIN;
+        
+        m->name = name;
+        m->value = "1";
+        
+        m->nargs = -1;
+        push_macro (key, m);
+    
+    }
+    
+#if     defined (VERSION)
+
+    name = xstrdup ("__SCC_VERSION__");
+    
+    if ((key = hashtab_alloc_name (name))) {
+    
+        m = xmalloc (sizeof (*m));
+        m->type = MACRO_BUILTIN;
+        
+        m->name = name;
+        
+        m->value = xmalloc (10);
+        sprintf (m->value, "%ldL", state->version);
+        
+        m->nargs = -1;
+        push_macro (key, m);
+    
+    }
+    
+#endif
+    
+    if (state->std) {
+    
+        name = xstrdup ("__STDC__");
+        
+        if ((key = hashtab_alloc_name (name))) {
+        
+            m = xmalloc (sizeof (*m));
+            m->type = MACRO_BUILTIN;
+            
+            m->name = name;
+            m->value = "1";
+            
+            m->nargs = -1;
+            push_macro (key, m);
+        
+        }
+    
+    }
+
+}
+
+static void skip_pp_quoted (char **pp, char *end) {
+
+    char quote = **pp;
+    (*pp)++;
+    
+    while (*pp < end) {
+    
+        if (**pp == '\\') {
+        
+            (*pp)++;
+            
+            if (*pp < end) {
+                (*pp)++;
+            }
+            
+            continue;
+        
+        }
+        
+        if (**pp == quote) {
+        
+            (*pp)++;
+            break;
+        
+        }
+        
+        (*pp)++;
+    
+    }
+
+}
+
+static int line_has_unclosed_function_macro (char *line, char *line_end) {
+
+    char *p = line, *q, *r, *sname;
+    int depth;
+    
+    struct hashtab_name *key;
+    struct macro *m;
+    
+    while (p < line_end) {
+    
+        if (*p == '"' || *p == '\'') {
+        
+            skip_pp_quoted (&p, line_end);
+            continue;
+        
+        }
+        
+        if (!is_name_beginner ((int) *p)) {
+        
+            p++;
+            continue;
+        
+        }
+        
+        q = p;
+        sname = symname (&q);
+        
+        if ((key = find_macro (sname)) && (m = get_macro (key)) && m->nargs >= 0) {
+        
+            r = skip_whitespace (q);
+            
+            if (r < line_end && *r == '(') {
+            
+                depth = 0;
+                
+                while (r < line_end) {
+                
+                    if (*r == '"' || *r == '\'') {
+                    
+                        skip_pp_quoted (&r, line_end);
+                        continue;
+                    
+                    }
+                    
+                    if (*r == '(') {
+                        depth++;
+                    } else if (*r == ')') {
+                    
+                        depth--;
+                        
+                        if (depth == 0) {
+                            break;
+                        }
+                    
+                    }
+                    
+                    r++;
+                
+                }
+                
+                free (sname);
+                
+                if (depth > 0) {
+                    return 1;
+                }
+                
+                p = r;
+                continue;
+            
+            }
+        
+        }
+        
+        free (sname);
+        p = q;
+    
+    }
+    
+    return 0;
+
+}
+
+static char *collect_macro_continuation (char *line, char **line_end_p, FILE *fp, void **load_line_internal_data_p, unsigned long *new_line_number_p) {
+
+    char *buf, *next, *next_end;
+    unsigned long len, next_len, cap, newlines;
+    
+    len = *line_end_p - line;
+    cap = len + 1;
+    
+    buf = xmalloc (cap + 1);
+    memcpy (buf, line, len);
+    
+    buf[len] = '\0';
+    
+    while (line_has_unclosed_function_macro (buf, buf + len)) {
+    
+        if (load_line (&next, &next_end, 0, 0, &newlines, fp, load_line_internal_data_p)) {
+            break;
+        }
+        
+        next_len = next_end - next;
+        
+        if (len + 1 + next_len + 1 > cap) {
+        
+            cap = len + 1 + next_len + 1 + 256;
+            buf = xrealloc (buf, cap + 1);
+        
+        }
+        
+        buf[len++] = ' ';
+        memcpy (buf + len, next, next_len);
+        
+        len += next_len;
+        buf[len] = '\0';
+        
+        *new_line_number_p += newlines + 1;
+    
+    }
+    
+    *line_end_p = buf + len;
+    return buf;
+
+}
+
+static void init_date_time_macros (void) {
+
+    char *timep, *buf, *name;
+    time_t now;
+    
+    struct hashtab_name *key;
+    struct macro *m;
+    
+    time (&now);
+    timep = ctime (&now);
+    
+    name = xstrdup ("__TIME__");
+    
+    if ((key = hashtab_alloc_name (name))) {
+    
+        buf = xmalloc (11);
+        sprintf (buf, "\"%.8s\"", timep + 11);;
+        
+        m = xmalloc (sizeof (*m));
+        m->type = MACRO_BUILTIN;
+        
+        m->name = name;
+        m->value = buf;
+        
+        m->nargs = -1;
+        push_macro (key, m);
+    
+    }
+    
+    name = xstrdup ("__DATE__");
+    
+    if ((key = hashtab_alloc_name (name))) {
+    
+        buf = xmalloc (14);
+        sprintf (buf, "\"%.3s %.2s %.4s\"", timep + 4, timep + 8, timep + 20);
+        
+        m = xmalloc (sizeof (*m));
+        m->type = MACRO_BUILTIN;
+        
+        m->name = name;
+        m->value = buf;
+        
+        m->nargs = -1;
+        push_macro (key, m);
+    
+    }
+
+}
+
+int preprocess_init (void) {
+
+    struct list *item;
+    char *opt, *nopt, *p;
+    
+    if (state->mode == CC_MODE_PREPROCESS) {
+    
+        hash_line (state->ifile, 0);
+        hash_line ("<built-in>", 0);
+    
+    }
+    
+    init_builtin_macros ();
+    init_date_time_macros ();
+    
+    if (state->mode == CC_MODE_PREPROCESS) {
+        hash_line ("<command-line>", 0);
+    }
+    
+    set_filename_and_line_number (xstrdup ("<command-line>"), 1);
+    
+    if (state->pplist) {
+    
+        item = state->pplist;
+        
+        do {
+        
+            item = item->next;
+            
+            if (!(opt = item->data)) {
+                continue;
+            }
+            
+            if (opt[0] != '-') {
+            
+                report_at (program_name, 0, REPORT_ERROR, "unrecognised option '%s'", opt);
+                continue;
+            
+            }
+            
+            switch (opt[1]) {
+            
+                case 'D':
+                
+                    opt = nopt = xstrdup (opt + 2);
+                    
+                    if ((p = strrchr (nopt, '='))) {
+                        *p++ = ' ';
+                    }
+                    
+                    add_macro (nopt, &nopt, 0);
+                    free (opt);
+                    
+                    break;
+                
+                case 'I':
+                
+                    vec_push (&vec_include_paths, xstrdup (opt + 2));
+                    break;
+                
+                case 'U':
+                
+                    opt = nopt = xstrdup (opt + 2);
+                    
+                    remove_macro (nopt, &nopt, 0);
+                    free (opt);
+                    
+                    break;
+                
+                default:
+                
+                    report_at (program_name, 0, REPORT_ERROR, "unrecognised option '%s'", opt);
+                    break;
+            
+            }
+        
+        } while (item != state->pplist);
+    
+    }
+    
+    install_cond_pseudo_op_table (cond_pseudo_op_table);
+    install_pp_pseudo_op_table (pseudo_op_table);
+    
+    return get_error_count () > 0;
+
+}
+
+
+static struct hashtab hashtab_seen_macros = { 0 };
+static struct vector vector_tokens = { 0 };
+
+struct vector *preprocess_line (char **line, char *real_start, char *real_caret, int in_macro) {
+
+    char *caret = *line, *start;
+    char *sname, ch;
+    
+    struct hashtab_name *key;
+    struct macro *m;
+    
+    struct token *tok;
+    
+    while (!is_end_of_line[(int) *caret]) {
+    
+        start = caret;
+        
+        if (isspace ((int) *caret)) {
+        
+            while (isspace ((int) *caret)) {
+            
+                if (state->mode == CC_MODE_PREPROCESS && state->ofp) {
+                    fprintf (state->ofp, " ");
+                }
+                
+                caret++;
+            
+            }
+            
+            continue;
+        
+        }
+        
+        if (*caret == 'L' && (*(caret + 1) == '\'' || *(caret + 1) == '"' || *(caret + 1) == '\\')) {
+        
+            ch = *(caret + 1);
+            caret += 2;
+            
+            while (!is_end_of_line[(int) *caret]) {
+            
+                if (*caret == '\\') {
+                
+                    caret++;
+                    
+                    if (!is_end_of_line[(int) *caret]) {
+                        caret++;
+                    }
+                    
+                    continue;
+                
+                }
+                
+                if (*caret == ch) { break; }
+                caret++;
+            
+            }
+            
+            if (*caret != ch) {
+            
+                char *temp = xmalloc ((caret - start) + 2);
+                sprintf (temp, "%.*s%c", (int) (caret - start), start, ch);
+                
+                report_line_at (get_filename (), get_line_number (), REPORT_WARNING, *line, start, "missing terminating %c character", ch);
+                
+                if (state->mode == CC_MODE_PREPROCESS) {
+                
+                    if (state->ofp) {
+                        fprintf (state->ofp, "%s", temp);
+                    }
+                    
+                    free (temp);
+                    continue;
+                
+                }
+                
+                tok = xmalloc (sizeof (*tok));
+                tok->kind = (ch == '"' ? TOK_LSTR : TOK_LCHAR);
+                
+                tok->start = (in_macro == 2) ? *line : (real_start ? real_start : *line);
+                tok->caret = (in_macro == 2) ? start : (real_caret ? real_caret : start);
+                
+                tok->ident = temp;
+                tok->len = (caret - start);
+                
+                vec_push (&vector_tokens, tok);
+                continue;
+            
+            }
+            
+            caret++;
+            
+            if (state->mode == CC_MODE_PREPROCESS) {
+            
+                if (state->ofp) {
+                    fprintf (state->ofp, "%.*s", (int) (caret - start), start);
+                }
+                
+                continue;
+            
+            }
+            
+            tok = xmalloc (sizeof (*tok));
+            tok->kind = (ch == '"' ? TOK_LSTR : TOK_LCHAR);
+            
+            tok->start = (in_macro == 2) ? *line : (real_start ? real_start : *line);
+            tok->caret = (in_macro == 2) ? start : (real_caret ? real_caret : start);
+            
+            tok->ident = xstrndup (start, (int) (caret - start));
+            tok->len = (caret - start);
+            
+            vec_push (&vector_tokens, tok);
+            continue;
+        
+        }
+        
+        if (*caret == '"' || *caret == '\'') {
+        
+            ch = *caret++;
+            
+            while (!is_end_of_line[(int) *caret]) {
+            
+                if (*caret == '\\') {
+                
+                    caret++;
+                    
+                    if (!is_end_of_line[(int) *caret]) {
+                        caret++;
+                    }
+                    
+                    continue;
+                
+                }
+                
+                if (*caret == ch) { break; }
+                caret++;
+            
+            }
+            
+            if (*caret != ch) {
+            
+                char *temp = xmalloc ((caret - start) + 2);
+                sprintf (temp, "%.*s%c", (int) (caret - start), start, ch);
+                
+                report_line_at (get_filename (), get_line_number (), REPORT_WARNING, *line, start, "missing terminating %c character", ch);
+                
+                if (state->mode == CC_MODE_PREPROCESS) {
+                
+                    if (state->ofp) {
+                        fprintf (state->ofp, "%s", temp);
+                    }
+                    
+                    free (temp);
+                    continue;
+                
+                }
+                
+                tok = xmalloc (sizeof (*tok));
+                tok->kind = (ch == '"' ? TOK_PPSTR : TOK_CCHAR);
+                
+                tok->start = (in_macro == 2) ? *line : (real_start ? real_start : *line);
+                tok->caret = (in_macro == 2) ? start : (real_caret ? real_caret : start);
+                
+                tok->ident = temp;
+                tok->len = (caret - start);
+                
+                vec_push (&vector_tokens, tok);
+                continue;
+            
+            }
+            
+            caret++;
+            
+            if (state->mode == CC_MODE_PREPROCESS) {
+            
+                if (state->ofp) {
+                    fprintf (state->ofp, "%.*s", (int) (caret - start), start);
+                }
+                
+                continue;
+            
+            }
+            
+            tok = xmalloc (sizeof (*tok));
+            tok->kind = (ch == '"' ? TOK_PPSTR : TOK_CCHAR);
+            
+            tok->start = (in_macro == 2) ? *line : (real_start ? real_start : *line);
+            tok->caret = (in_macro == 2) ? start : (real_caret ? real_caret : start);
+            
+            tok->ident = xstrndup (start, (int) (caret - start));
+            tok->len = (caret - start);
+            
+            vec_push (&vector_tokens, tok);
+            continue;
+        
+        }
+        
+        if (is_name_beginner ((int) *caret)) {
+        
+            sname = symname (&caret);
+            
+            if (!(key = hashtab_get_key (&hashtab_seen_macros, sname))) {
+            
+                char *pm;
+                
+                if ((key = find_macro (sname))) {
+                
+                    hashtab_put (&hashtab_seen_macros, key, sname);
+                    
+                    if ((m = get_macro (key))) {
+                    
+                        if (m->nargs >= 0) {
+                            caret = skip_whitespace (caret);
+                        }
+                        
+                        if ((pm = process_macro (start, &caret, m))) {
+                        
+                            if (m->nargs < 0 && !is_end_of_line[(int) *caret]) {
+                            
+                                char *p = skip_whitespace (pm);
+                                char *q = p;
+                                char *alias = 0;
+                                char *tail = skip_whitespace (caret);
+                                
+                                struct hashtab_name *alias_key;
+                                struct macro *alias_macro;
+                                
+                                if (is_name_beginner ((int) *p)) {
+                                
+                                    alias = symname (&q);
+                                    q = skip_whitespace (q);
+                                    
+                                    if (is_end_of_line[(int) *q]
+                                        && *tail == '('
+                                        && (alias_key = find_macro (alias))
+                                        && (alias_macro = get_macro (alias_key))
+                                        && alias_macro->nargs >= 0) {
+                                    
+                                        char *combined;
+                                        size_t pm_len = strlen (pm);
+                                        size_t tail_len = strlen (caret);
+                                        
+                                        combined = xmalloc (pm_len + tail_len + 1);
+                                        memcpy (combined, pm, pm_len);
+                                        memcpy (combined + pm_len, caret, tail_len + 1);
+                                        
+                                        pm = combined;
+                                        caret += tail_len;
+                                    
+                                    }
+                                    
+                                    free (alias);
+                                
+                                }
+                            
+                            }
+                            
+                            preprocess_line (&pm, real_start ? real_start : *line, real_caret ? real_caret : start, m->nargs >= 0 ? 2 : 1);
+                        
+                        }
+                        
+                        while (!is_end_of_line[(int) *caret]) {
+                        
+                            if (!isspace (*caret)) {
+                                break;
+                            }
+                            
+                            if (state->mode == CC_MODE_PREPROCESS) {
+                                putc (*caret, state->ofp);
+                            }
+                            
+                            caret++;
+                        
+                        }
+                    
+                    }
+                    
+                    hashtab_remove (&hashtab_seen_macros, key);
+                    
+                    /*while (!is_end_of_line[(int) *caret]) {
+                    
+                        if (isspace ((int) *caret)) {
+                            break;
+                        }
+                        
+                        if (state->mode == CC_MODE_PREPROCESS) {
+                            fputc (*caret, state->ofp);
+                        }
+                        
+                        caret++;
+                    
+                    }*/
+                    
+                    continue;
+                
+                }
+            
+            }
+            
+            free (sname);
+            
+            if (state->mode == CC_MODE_PREPROCESS) {
+            
+                if (state->ofp) {
+                    fprintf (state->ofp, "%.*s", (int) (caret - start), start);
+                }
+                
+                continue;
+            
+            }
+            
+            tok = xmalloc (sizeof (*tok));
+            tok->kind = TOK_IDENT;
+            
+            tok->start = (in_macro == 2) ? *line : (real_start ? real_start : *line);
+            tok->caret = (in_macro == 2) ? start : (real_caret ? real_caret : start);
+            
+            tok->ident = xstrndup (start, (int) (caret - start));
+            tok->len = (caret - start);
+            
+            vec_push (&vector_tokens, tok);
+            continue;
+        
+        }
+        
+        if (caret[0] == '0' && tolower ((int) caret[1]) == 'x') {
+        
+            caret += 2;
+            
+            while (isxdigit ((int) *caret)) {
+                caret++;
+            }
+            
+            if (state->mode == CC_MODE_PREPROCESS) {
+            
+                if (state->ofp) {
+                    fprintf (state->ofp, "%.*s", (int) (caret - start), start);
+                }
+                
+                continue;
+            
+            }
+            
+            if (toupper ((int) *caret) == 'U') {
+            
+                caret++;
+                
+                if (toupper ((int) *caret) == 'L') {
+                    caret++;
+                }
+                
+                if (toupper ((int) *caret) == 'L') {
+                    caret++;
+                }
+            
+            } else if (toupper ((int) *caret) == 'L') {
+            
+                caret++;
+                
+                if (toupper ((int) *caret) == 'L') {
+                    caret++;
+                }
+                
+                if (toupper ((int) *caret) == 'U') {
+                    caret++;
+                }
+            
+            }
+            
+            tok = xmalloc (sizeof (*tok));
+            tok->kind = TOK_PPNUM;
+            
+            tok->start = (in_macro == 2) ? *line : (real_start ? real_start : *line);
+            tok->caret = (in_macro == 2) ? start : (real_caret ? real_caret : start);
+            
+            tok->ident = xstrndup (start, (int) (caret - start));
+            tok->len = (caret - start);
+            
+            vec_push (&vector_tokens, tok);
+            
+            if (is_name_part ((int) *caret)) {
+            
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, *line, caret, "unexpected '%c' character", *caret);
+                
+                while (is_name_part ((int) *caret)) {
+                    caret++;
+                }
+            
+            }
+            
+            continue;
+        
+        }
+        
+        if (isdigit ((int) *caret) || (caret[0] == '.' && isdigit ((int) caret[1]))) {
+        
+            int saw_dot = 0, saw_exp = 0;
+            
+            while (!is_end_of_line[(int) *caret]) {
+            
+                if (isdigit ((int) *caret)) {
+                
+                    caret++;
+                    continue;
+                
+                }
+                
+                if (*caret == '.' && !saw_dot && !saw_exp) {
+                
+                    saw_dot = 1;
+                    caret++;
+                    
+                    continue;
+                
+                }
+                
+                if ((*caret == 'e' || *caret == 'E' || *caret == 'p' || *caret == 'P') && !saw_exp) {
+                
+                    saw_exp = 1;
+                    caret++;
+                    
+                    if (*caret == '+' || *caret == '-') {
+                        caret++;
+                    }
+                    
+                    continue;
+                
+                }
+                
+                break;
+            
+            }
+            
+            if (state->mode == CC_MODE_PREPROCESS) {
+            
+                if (state->ofp) {
+                    fprintf (state->ofp, "%.*s", (int) (caret - start), start);
+                }
+                
+                continue;
+            
+            }
+            
+            if (saw_dot || saw_exp) {
+            
+                if (tolower ((int) *caret) == 'f') {
+                    caret++;
+                } else if (toupper ((int) *caret) == 'L') {
+                    caret++;
+                }
+            
+            } else {
+            
+                if (toupper ((int) *caret) == 'U') {
+                
+                    caret++;
+                    
+                    if (toupper ((int) *caret) == 'L') {
+                        caret++;
+                    }
+                    
+                    if (toupper ((int) *caret) == 'L') {
+                        caret++;
+                    }
+                
+                } else if (toupper ((int) *caret) == 'L') {
+                
+                    caret++;
+                    
+                    if (toupper ((int) *caret) == 'L') {
+                        caret++;
+                    }
+                    
+                    if (toupper ((int) *caret) == 'U') {
+                        caret++;
+                    }
+                
+                }
+            
+            }
+            
+            tok = xmalloc (sizeof (*tok));
+            tok->kind = TOK_PPNUM;
+            
+            tok->start = (in_macro == 2) ? *line : (real_start ? real_start : *line);
+            tok->caret = (in_macro == 2) ? start : (real_caret ? real_caret : start);
+            
+            tok->ident = xstrndup (start, (int) (caret - start));
+            tok->len = (caret - start);
+            
+            vec_push (&vector_tokens, tok);
+            
+            if (is_name_part ((int) *caret)) {
+            
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, *line, caret, "unexpected '%c' character", *caret);
+                
+                while (is_name_part ((int) *caret)) {
+                    caret++;
+                }
+            
+            }
+            
+            continue;
+        
+        }
+        
+        if (ispunct ((int) *caret)) {
+        
+            struct punct *punct;
+            
+            if (state->mode == CC_MODE_PREPROCESS) {
+            
+                caret++;
+                
+                if (state->ofp) {
+                    fprintf (state->ofp, "%c", *start);
+                }
+                
+                continue;
+            
+            }
+            
+            tok = xmalloc (sizeof (*tok));
+            
+            if ((punct = find_punct (caret))) {
+            
+                tok->kind = punct->kind;
+                
+                tok->ident = xstrdup (punct->name);
+                tok->len = strlen (punct->name);
+                
+                caret += tok->len;
+            
+            } else {
+            
+                tok->kind = *caret;
+                
+                tok->ident = xstrndup (caret, 1);;
+                tok->len = 1;
+                
+                caret++;
+            
+            }
+            
+            tok->start = (in_macro == 2) ? *line : (real_start ? real_start : *line);
+            tok->caret = (in_macro == 2) ? start : (real_caret ? real_caret : start);
+            
+            vec_push (&vector_tokens, tok);
+            continue;
+        
+        }
+        
+        report_line_at (get_filename (), get_line_number (), REPORT_INTERNAL_ERROR, *line, caret, "Do we see this???");
+        caret++;
+    
+    }
+    
+    *line = caret;
+    
+    if (!in_macro) {
+    
+        if (state->mode == CC_MODE_PREPROCESS) {
+        
+            if (state->ofp) {
+                fputc ('\n', state->ofp);
+            }
+            
+            return 0;
+        
+        }
+        
+        return &vector_tokens;
+    
+    }
+    
+    return 0;
+
+}
+
+static void hash_line (const char *filename, unsigned long line_number) {
+
+    if (state->no_linemarkers) {
+        return;
+    }
+    
+    if (state->traditional_linemarkers) {
+    
+        if (line_number > 0) {
+            fprintf (state->ofp, "#line %lu \"%s\"\n", line_number, filename);
+        }
+    
+    } else {
+    
+        if (flags) {
+        
+            fprintf (state->ofp, "# %lu \"%s\" %d\n", line_number, filename, flags);
+            flags = 0;
+        
+        } else {
+            fprintf (state->ofp, "# %lu \"%s\"\n", line_number, filename);
+        }
+    
+    }
+
+}
+
+void preprocess_file (const char *ifile) {
+
+    char *line, *line_end;
+    FILE *fp;
+    
+    char *start, *sname, *caret;
+    int need_hash = 0;
+    
+    unsigned long newlines;
+    unsigned long new_line_number;
+    
+    unsigned long consumed_extra;
+    unsigned long old_new_line_number;
+    
+    struct pp_pseudo_op_entry *poe;
+    void *load_line_internal_data = NULL;
+    
+    struct cond *cond;
+    int cond_idx;
+    
+    struct hashtab_name *key;
+    struct macro *m;
+    
+    unsigned long blanks = 1;
+    
+    if (!ifile || strcmp (ifile, "-") == 0) {
+    
+        set_filename (xstrdup ("<stdin>"));
+        fp = stdin;
+    
+    } else {
+    
+        set_filename (xstrdup (ifile));
+        
+        if (!(fp = fopen (ifile, "r"))) {
+        
+            report_at (program_name, 0, REPORT_FATAL_ERROR, "failed to open '%s' for reading", ifile);
+            return;
+        
+        }
+    
+    }
+    
+    hash_line (get_filename (), 1);
+    
+    set_line_number (0);
+    new_line_number = 1;
+    
+    if ((key = find_macro ("__FILE__"))) {
+    
+        const char *filename = get_filename ();;
+        
+        if (filename && (m = get_macro (key)) && m->type == MACRO_BUILTIN) {
+        
+            free (m->value);
+            
+            m->value = xmalloc (1 + strlen (filename) + 2);
+            sprintf (m->value, "\"%s\"", filename);
+        
+        }
+    
+    }
+    
+    load_line_internal_data = load_line_create_internal_data (&new_line_number);
+    
+    while (!load_line (&line, &line_end, 0, 0, &newlines, fp, &load_line_internal_data)) {
+    
+        set_line_number (new_line_number);
+        new_line_number += newlines + 1;
+        
+        consumed_extra = newlines;
+        
+        if ((key = find_macro ("__LINE__"))) {
+        
+            if ((m = get_macro (key)) && m->type == MACRO_BUILTIN) {
+            
+                free (m->value);
+                
+                m->value = xmalloc (16);
+                sprintf (m->value, "%lu", get_line_number ());
+            
+            }
+        
+        }
+        
+        start = line;
+        caret = skip_whitespace (line);
+        
+        if (caret >= line_end) {
+        
+            if (!state->no_linemarkers) {
+            
+                blanks += (newlines + 1);
+                need_hash = 1;
+            
+            }
+            
+            continue;
+        
+        }
+        
+        if (ignore_line && *caret != '#') {
+        
+            if (!state->no_linemarkers) {
+            
+                blanks += (newlines + 1);
+                need_hash = 1;
+            
+            }
+            
+            continue;
+        
+        }
+        
+        while ((line = skip_whitespace (line)) < line_end) {
+        
+            caret = line;
+            
+            if (line[0] == '/' && line[1] == '/') {
+            
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "C++ style comments are not allowed in ISO C90");
+                
+                if (!state->no_linemarkers) {
+                
+                    blanks += (newlines + 1);
+                    need_hash = 1;
+                
+                }
+                
+                break;
+            
+            }
+            
+            if (*caret == '#') {
+            
+                caret = (line = skip_whitespace (caret + 1));
+                
+                if (is_name_beginner ((int) *line)) {
+                
+                    while (is_name_part ((int) *line)) {
+                        line++;
+                    }
+                    
+                    sname = xstrndup (caret, line - caret);
+                    line = skip_whitespace (line);
+                    
+                    if ((poe = find_cond_directive (sname))) {
+                    
+                        ignore_line = poe->handler (start, &line);
+                        
+                        if (!state->no_linemarkers) {
+                        
+                            blanks += (newlines + 1);
+                            need_hash = 1;
+                        
+                        }
+                        
+                        free (sname);
+                        continue;
+                    
+                    }
+                    
+                    if (!ignore_line) {
+                        
+                        if (strcmp (sname, "pragma") == 0) {
+                        
+                            caret = skip_whitespace (line);
+                            free (sname);
+                            
+                            if (is_name_beginner ((int) *line)) {
+                                
+                                sname = symname (&line);
+                                
+                                if (strcmp (sname, "once") == 0) {
+                                    
+                                    free (sname);
+                                    
+                                    if (!state->no_linemarkers) {
+                                    
+                                        blanks += (newlines + 1);
+                                        need_hash = 1;
+                                    
+                                    }
+                                    
+                                    if (includes) {
+                                    
+                                        if (is_pragma_igored (ifile)) {
+                                            break;
+                                        }
+                                    
+                                    } else {
+                                        report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, caret, "#pragma once in main file");
+                                    }
+                                    
+                                    continue;
+                                
+                                }
+                            
+                            }
+                            
+                            caret = (line = skip_whitespace (start));
+                            goto handle_line;
+                        
+                        }
+                        
+                        if (strcmp (sname, "include") == 0) {
+                        
+                            int ret = handler_include (start, &line);
+                            free (sname);
+                            
+                            if (flags == 2) {
+                            
+                                if (!state->no_linemarkers) {
+                                
+                                    hash_line (get_filename (), new_line_number);
+                                    
+                                    need_hash = 0;
+                                    blanks = 1;
+                                
+                                }
+                            
+                            } else {
+                            
+                                if (!state->no_linemarkers) {
+                                
+                                    blanks += (newlines + 1);
+                                    need_hash = 1;
+                                
+                                }
+                            
+                            }
+                            
+                            if (ret) {
+                                break;
+                            }
+                            
+                            continue;
+                        
+                        }
+                        
+                        if ((poe = find_directive (sname))) {
+                        
+                            int ret = poe->handler (start, &line);
+                            free (sname);
+                            
+                            if (!state->no_linemarkers) {
+                            
+                                blanks += (newlines + 1);
+                                need_hash = 1;
+                            
+                            }
+                            
+                            if (ret) {
+                                break;
+                            }
+                            
+                            continue;
+                        
+                        }
+                        
+                        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "unknown preprocessor directive '#%s'", sname);
+                    
+                    }
+                    
+                    if (ignore_line && !state->no_linemarkers) {
+                    
+                        blanks += (newlines + 1);
+                        need_hash = 1;
+                    
+                    }
+                    
+                    free (sname);
+                    continue;
+                
+                }
+                
+                if (ignore_line && !state->no_linemarkers) {
+                
+                    blanks += (newlines + 1);
+                    need_hash = 1;
+                
+                }
+                
+                if (!is_end_of_line[(int) *caret] && !ignore_line) {
+                    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "unknown preprocessor directive '#%c'", *caret);
+                }
+                
+                continue;
+            
+            }
+            
+        handle_line:
+            
+            if (!ignore_line) {
+            
+                if (!state->no_linemarkers && need_hash) {
+                
+                    if (blanks < 10) {
+                    
+                        while (blanks > 1) {
+                        
+                            putc ('\n', state->ofp);
+                            blanks--;
+                        
+                        }
+                    
+                    } else {
+                    
+                        hash_line (get_filename (), get_line_number ());
+                        blanks = 1;
+                    
+                    }
+                    
+                    need_hash = 0;
+                
+                }
+                
+                blanks = 1;
+                
+                if (line < line_end) {
+                
+                    if (line_has_unclosed_function_macro (start, line_end)) {
+                    
+                        old_new_line_number = new_line_number;
+                        
+                        start = collect_macro_continuation (start, &line_end, fp, &load_line_internal_data, &new_line_number);
+                        consumed_extra += new_line_number - old_new_line_number;
+                    
+                    }
+                    
+                    preprocess_line (&start, 0, 0, 0);
+                    
+                    if (!state->no_linemarkers && consumed_extra) {
+                    
+                        blanks += consumed_extra;
+                        need_hash = 1;
+                    
+                    }
+                
+                }
+            
+            }
+            
+            break;
+        
+        }
+    
+    }
+    
+/*end:*/
+    
+    load_line_destroy_internal_data (load_line_internal_data);
+    
+    if (!includes) {
+    
+        for (cond_idx = 0; cond_idx < vec_ifstack.length; cond_idx++) {
+        
+            cond = vec_ifstack.data[cond_idx];
+            report_at (cond->filename, cond->line_number, REPORT_ERROR, "unterminated #%s statement", cond->directive);
+            
+            free (cond->filename);
+            free (cond->directive);
+            
+            free (cond);
+        
+        }
+    
+    }
+    
+    if (fp != stdin) { fclose (fp); }
+
+}
diff --git a/pp.h b/pp.h
new file mode 100755 (executable)
index 0000000..80a385d
--- /dev/null
+++ b/pp.h
@@ -0,0 +1,41 @@
+/******************************************************************************
+ * @file            pp.h
+ *****************************************************************************/
+#ifndef     _PP_H
+#define     _PP_H
+
+struct cond {
+
+    char *directive, *filename;
+    
+    int ignore_line;
+    unsigned long line_number;
+    
+    int need_else;
+    int has_else;
+
+};
+
+int preprocess_init (void);
+void preprocess_file (const char *ifile);
+
+struct pp_pseudo_op_entry {
+
+    const char *name;
+    int (*handler) (char *start, char **pp);
+
+};
+
+extern int ignore_line;
+
+struct pp_pseudo_op_entry *find_cond_directive (char *name);
+struct pp_pseudo_op_entry *find_directive (char *name);
+
+#include    "vector.h"
+
+extern struct vector vec_ifstack;
+extern struct vector vec_include_paths;
+
+struct vector *preprocess_line (char **line, char *real_start, char *real_caret, int in_macro);
+
+#endif      /* _PP_H */
diff --git a/report.c b/report.c
new file mode 100755 (executable)
index 0000000..caea66a
--- /dev/null
+++ b/report.c
@@ -0,0 +1,285 @@
+/******************************************************************************
+ * @file            report.c
+ *****************************************************************************/
+#include    <stdarg.h>
+#include    <stdio.h>
+#include    <stdlib.h>
+#include    <string.h>
+
+#include    "ll.h"
+#include    "report.h"
+
+extern char *xstrdup (const char *__p);
+unsigned long errors = 0;
+
+#ifndef     __PDOS__
+#if     defined (_WIN32)
+# include   <windows.h>
+static int OriginalConsoleColor = -1;
+#endif
+
+static void reset_console_color (void) {
+
+#if     defined (unix) || defined (__unix) || defined (__unix__) || defined (__APPLE__)
+
+    fprintf (stderr, "\033[0m");
+
+#elif   defined (_WIN32)
+
+    HANDLE hStdError = GetStdHandle (STD_ERROR_HANDLE);
+    
+    if (OriginalConsoleColor == -1) { return; }
+    
+    SetConsoleTextAttribute (hStdError, OriginalConsoleColor);
+    OriginalConsoleColor = -1;
+
+#endif
+
+}
+
+static void set_console_color (int color) {
+
+#if     defined (unix) || defined (__unix) || defined (__unix__) || defined (__APPLE__)
+
+    fprintf (stderr, "\033[%dm", color);
+
+#elif   defined (_WIN32)
+
+    HANDLE hStdError = GetStdHandle (STD_ERROR_HANDLE);
+    WORD wColor;
+    
+    if (OriginalConsoleColor == -1) {
+    
+        CONSOLE_SCREEN_BUFFER_INFO csbi;
+        
+        if (!GetConsoleScreenBufferInfo (hStdError, &csbi)) {
+            return;
+        }
+        
+        OriginalConsoleColor = csbi.wAttributes;
+    
+    }
+    
+    wColor = (OriginalConsoleColor & 0xF0) + (color & 0xF);
+    SetConsoleTextAttribute (hStdError, wColor);
+
+#endif
+
+}
+#endif
+
+extern int has_include_stack (void);
+extern void print_include_stack (void);
+
+static void output_message (const char *filename, unsigned long lineno, unsigned long idx, int type, const char *fmt, va_list ap) {
+
+    if (has_include_stack ()) {
+        print_include_stack ();
+    }
+    
+    if (filename) {
+    
+        if (lineno == 0) {
+            fprintf (stderr, "%s: ", filename);
+        } else {
+            fprintf (stderr, "%s:", filename);
+        }
+    
+    }
+    
+    if (lineno > 0) {
+    
+        if (idx == 0) {
+            fprintf (stderr, "%lu: ", lineno);
+        } else {
+            fprintf (stderr, "%lu:", lineno);
+        }
+    
+    }
+    
+    if (idx > 0) {
+        fprintf (stderr, "%lu: ", idx);
+    }
+    
+    if (type == REPORT_ERROR || type == REPORT_FATAL_ERROR) {
+    
+#ifndef     __PDOS__
+        set_console_color (COLOR_ERROR);
+#endif
+        
+        if (type == REPORT_ERROR) {
+            fprintf (stderr, "error:");
+        } else {
+            fprintf (stderr, "fatal error:");
+        }
+    
+    } else if (type == REPORT_INTERNAL_ERROR) {
+    
+#ifndef     __PDOS__
+        set_console_color (COLOR_INTERNAL_ERROR);
+#endif
+        
+        fprintf (stderr, "internal error:");
+    
+    } else if (type == REPORT_WARNING) {
+    
+#ifndef     __PDOS__
+        set_console_color (COLOR_WARNING);
+#endif
+        
+        fprintf (stderr, "warning:");
+    
+    }
+    
+#ifndef     __PDOS__
+    reset_console_color ();
+#endif
+    
+    fprintf (stderr, " ");
+    vfprintf (stderr, fmt, ap);
+    fprintf (stderr, "\n");
+    
+    if (type != REPORT_WARNING) {
+        ++errors;
+    }
+
+}
+
+unsigned int get_error_count (void) {
+    return errors;
+}
+
+void report_at (const char *filename, unsigned long lineno, int type, const char *fmt, ...) {
+
+    va_list ap;
+    
+    va_start (ap, fmt);
+    output_message (filename, lineno, 0, type, fmt, ap);
+    va_end (ap);
+
+}
+
+static char *get_actual_line (const char *filename, unsigned long lineno) {
+
+    char *line, *line_end, *ret;
+    void *load_line_internal_data;
+    
+    unsigned long newlines;
+    unsigned long new_line_number = 1;
+    
+    FILE *fp;
+    
+    if ((fp = fopen (filename, "r"))) {
+    
+        load_line_internal_data = load_line_create_internal_data (&new_line_number);
+        
+        while (!load_line (&line, &line_end, 0, 0, &newlines, fp, &load_line_internal_data)) {
+        
+            if (new_line_number == lineno) {
+            
+                if (line[line_end - line] == '\n') {
+                    line[line_end - line] = '\0';
+                }
+                
+                ret = xstrdup (line);
+                
+                load_line_destroy_internal_data (load_line_internal_data);
+                fclose (fp);
+                
+                return ret;
+            
+            }
+            
+            new_line_number += newlines + 1;
+        
+        }
+        
+        load_line_destroy_internal_data (load_line_internal_data);
+        fclose (fp);
+    
+    }
+    
+    return 0;
+
+}
+
+void report_line_at (const char *filename, unsigned long lineno, int type, const char *str, const char *caret, const char *fmt, ...) {
+
+    unsigned long ident = 1, idx = 0, len, i;
+    va_list ap;
+    
+    char *actual_line = get_actual_line (filename, lineno);
+    
+    /*if (!(actual_line = get_actual_line (filename, lineno))) {
+    
+        filename = get_real_filename ();
+        lineno += (get_real_line_number () - lineno);
+    
+    }*/
+    
+    if (str && caret) { idx = (caret - str) + 1; }
+    
+    va_start (ap, fmt);
+    output_message (filename, lineno, idx, type, fmt, ap);
+    va_end (ap);
+    
+    if (str && caret) {
+    
+        if (actual_line) {
+        
+            if (lineno > 0) {
+                ident = fprintf (stderr, " %8lu | ", lineno);
+            } else {
+                ident = fprintf (stderr, "%*s", 12, "");
+            }
+            
+            fprintf (stderr, "%s", actual_line);
+            
+            if (actual_line[strlen (actual_line) - 1] != '\n') {
+                fprintf (stderr, "\n");
+            }
+            
+            len = (idx + ident) - 1;
+            
+            for (i = 0; i < ident; i++) {
+                putc (' ', stderr);
+            }
+            
+            for (; i < len; i++) {
+                putc ('-', stderr);
+            }
+            
+            fprintf (stderr, "^\n");
+            free (actual_line);
+        
+        }/* else {
+        
+            if (lineno > 0) {
+                ident = fprintf (stderr, " %8lu | ", lineno);
+            } else {
+                ident = fprintf (stderr, "%*s", 12, "");
+            }
+            
+            fprintf (stderr, "%s", str);
+            
+            if (str[strlen (str) - 1] != '\n') {
+                fprintf (stderr, "\n");
+            }
+            
+            len = (idx + ident) - 1;
+            
+            for (i = 0; i < ident; i++) {
+                putc (' ', stderr);
+            }
+            
+            for (; i < len; i++) {
+                putc ('-', stderr);
+            }
+            
+            fprintf (stderr, "^\n");
+        
+        }*/
+    
+    }
+
+}
diff --git a/report.h b/report.h
new file mode 100755 (executable)
index 0000000..eb184b1
--- /dev/null
+++ b/report.h
@@ -0,0 +1,27 @@
+/******************************************************************************
+ * @file            report.h
+ *****************************************************************************/
+#ifndef     _REPORT_H
+#define     _REPORT_H
+
+#if     defined (_WIN32)
+# define    COLOR_ERROR                 12
+# define    COLOR_WARNING               13
+# define    COLOR_INTERNAL_ERROR        19
+#else
+# define    COLOR_ERROR                 91
+# define    COLOR_INTERNAL_ERROR        94
+# define    COLOR_WARNING               95
+#endif
+
+#define     REPORT_WARNING              0
+#define     REPORT_ERROR                1
+#define     REPORT_FATAL_ERROR          3
+#define     REPORT_INTERNAL_ERROR       4
+
+unsigned int get_error_count (void);
+
+void report_at (const char *filename, unsigned long lineno, int type, const char *fmt, ...);
+void report_line_at (const char *filename, unsigned long lineno, int type, const char *str, const char *caret, const char *fmt, ...);
+
+#endif      /* _REPORT_H */
diff --git a/stdint.h b/stdint.h
new file mode 100755 (executable)
index 0000000..1b97ab8
--- /dev/null
+++ b/stdint.h
@@ -0,0 +1,49 @@
+/******************************************************************************
+ * @file            stdint.h
+ *****************************************************************************/
+#ifndef     _STDINT_H_INCLUDED
+#ifndef     _STDINT_H
+#ifndef     _STDINT_H_
+
+#define     _STDINT_H_INCLUDED
+#define     _STDINT_H
+#define     _STDINT_H_
+
+#include    <limits.h>
+
+/* Add all data types (even though we don't use them) as the project seems to fail to build on some systems. */
+typedef     signed char                 int8_t;
+typedef     unsigned char               uint8_t;
+
+typedef     signed short                int16_t;
+typedef     unsigned short              uint16_t;
+
+#if     INT_MAX > 32767
+typedef     signed int                  int32_t;
+typedef     unsigned int                uint32_t;
+#else
+typedef     signed long                 int32_t;
+typedef     unsigned long               uint32_t;
+#endif
+
+#ifndef     _INT64_T
+#define     _INT64_T
+#if     defined (NO_LONG_LONG) || ((ULONG_MAX >> 16) >> 16) == 0xffffffff
+typedef     signed long                 int64_t;
+#else
+typedef     signed long long            int64_t;
+#endif
+#endif      /* _INT64_T */
+
+#ifndef     _UINT64_T
+#define     _UINT64_T
+#if     defined (NO_LONG_LONG) || ((ULONG_MAX >> 16) >> 16) == 0xffffffff
+typedef     unsigned long               uint64_t;
+#else
+typedef     unsigned long long          uint64_t;
+#endif
+#endif      /* _UINT64_T */
+
+#endif      /* _STDINT_H_ */
+#endif      /* _STDINT_H */
+#endif      /* _STDINT_H_INCLUDED */
diff --git a/sym.c b/sym.c
new file mode 100644 (file)
index 0000000..eab3bee
--- /dev/null
+++ b/sym.c
@@ -0,0 +1,7 @@
+/******************************************************************************
+ * @file            sym.c
+ *****************************************************************************/
+#include    "cc.h"
+
+
\ No newline at end of file
diff --git a/token.c b/token.c
new file mode 100755 (executable)
index 0000000..8e8335e
--- /dev/null
+++ b/token.c
@@ -0,0 +1,2195 @@
+/******************************************************************************
+ * @file            token.c
+ *****************************************************************************/
+#include    <ctype.h>
+#include    <errno.h>
+#include    <math.h>
+#include    <stdlib.h>
+#include    <string.h>
+
+#include    "cc.h"
+#include    "cstr.h"
+#include    "hashtab.h"
+#include    "int64.h"
+#include    "lex.h"
+#include    "lib.h"
+#include    "ll.h"
+#include    "macro.h"
+#include    "pp.h"
+#include    "report.h"
+#include    "token.h"
+#include    "vector.h"
+
+static unsigned long new_line_number = 1, newlines = 0;
+static char *start = 0, *line = 0;
+
+static char *line_end = 0;
+static void *load_line_internal_data = 0;
+
+static struct vector vec_history = { 0 };
+static unsigned long real_new_line_number = 1;
+
+struct history {
+
+    FILE *ifp;
+    
+    unsigned long new_line_number;
+    unsigned long newlines;
+    
+    const char *filename;
+    unsigned long line_number;
+    
+    char *start;
+    char *line;
+    
+    char *line_end;
+    void *load_line_internal_data;
+    
+    struct vector *vec_tokens;
+    unsigned long real_new_line_number;
+
+};
+
+static struct vector *vec_tokens = 0;
+
+static int handler_include (char *start, char **pp) {
+
+    char *caret, *sname, ch;
+    int i;
+    
+    char *inc_path, *tmp;
+    FILE *fp;
+    
+    char *current_directory,*p;
+    int len;
+    
+    struct history *history;
+    
+    if (**pp != '"' && **pp != '<') {
+    
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp, "#include expects \"FILENAME\" or <FILENAME>");
+        return 1;
+    
+    }
+    
+    ch = (**pp == '"' ? '"' : '>');
+    caret = (*pp)++;
+    
+    while (!is_end_of_line[(int) **pp]) {
+    
+        if (**pp == ch) { break; }
+        (*pp)++;
+    
+    }
+    
+    if (**pp != ch) {
+    
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "#include expects \"FILENAME\" or <FILENAME>");
+        return 1;
+    
+    } else {
+        (*pp)++;
+    }
+    
+    sname = xstrndup (caret + 1, *pp - caret - 2);
+    
+    if (ch == '"') {
+    
+#if     defined (unix) || defined (__unix) || defined (__unix__) || defined (__APPLE__)
+        if (sname[0] == '/') {
+#else
+        if ((isalpha ((int) sname[0]) && sname[1] == ':') || sname[0] == '/' || sname[0] == '\\') {
+#endif
+        
+            if ((fp = fopen (sname, "r"))) {
+            
+                fclose (fp);
+                
+                history = xmalloc (sizeof (*history));
+                history->ifp = state->ifp;
+                
+                history->new_line_number = new_line_number;
+                history->newlines = newlines;
+                
+                history->filename = get_filename ();
+                history->line_number = get_line_number ();
+                
+                history->start = start;
+                history->line = line;
+                
+                history->line_end = line_end;
+                history->load_line_internal_data = load_line_internal_data;
+                
+                history->vec_tokens = vec_tokens;
+                history->real_new_line_number = real_new_line_number;
+                
+                if (init_tokenizer (sname)) {
+                
+                    free (history);
+                    return 1;
+                
+                }
+                
+                vec_tokens = 0;
+                
+                vec_push (&vec_history, history);
+                return 0;
+            
+            }
+            
+            goto error;
+        
+        } else if ((p = strrchr (get_filename (), '/')) || (p = strrchr (get_filename (), '\\'))) {
+        
+            len = p - get_filename ();
+            
+            tmp = xmalloc (len + 1 + strlen (sname) + 1);
+            sprintf (tmp, "%.*s/%s", len, get_filename (), sname);
+            
+            if ((fp = fopen (tmp, "r"))) {
+            
+                fclose (fp);
+                
+                history = xmalloc (sizeof (*history));
+                history->ifp = state->ifp;
+                
+                history->new_line_number = new_line_number;
+                history->newlines = newlines;
+                
+                history->filename = get_filename ();
+                history->line_number = get_line_number ();
+                
+                history->start = start;
+                history->line = line;
+                
+                history->line_end = line_end;
+                history->load_line_internal_data = load_line_internal_data;
+                
+                history->vec_tokens = vec_tokens;
+                history->real_new_line_number = real_new_line_number;
+                
+                if (init_tokenizer (tmp)) {
+                
+                    free (history);
+                    return 1;
+                
+                }
+                
+                vec_tokens = 0;
+                
+                vec_push (&vec_history, history);
+                return 0;
+            
+            }
+            
+            free (tmp);
+        
+        }
+        
+#if     defined (unix) || defined (__unix) || defined (__unix__) || defined (__APPLE__)
+        if (get_filename ()[0] == '/') {
+#else
+        if ((isalpha ((int) get_filename ()[0]) && get_filename ()[1] == ':') || get_filename ()[0] == '/' || get_filename ()[0] == '\\') {
+#endif
+        
+            if ((current_directory = get_current_directory ())) {
+            
+                if (strcmp (current_directory, "") == 0) {
+                
+                    tmp = xmalloc (strlen (sname) + 1);
+                    sprintf (tmp, "%s", sname);
+                
+                } else {
+                
+                    tmp = xmalloc (strlen (current_directory) + 1 + strlen (sname) + 1);
+                    sprintf (tmp, "%s/%s", current_directory, sname);
+                
+                }
+                
+                free (current_directory);
+                
+                if ((fp = fopen (tmp, "r"))) {
+                
+                    fclose (fp);
+                    
+                    history = xmalloc (sizeof (*history));
+                    history->ifp = state->ifp;
+                    
+                    history->new_line_number = new_line_number;
+                    history->newlines = newlines;
+                    
+                    history->filename = get_filename ();
+                    history->line_number = get_line_number ();
+                    
+                    history->start = start;
+                    history->line = line;
+                    
+                    history->line_end = line_end;
+                    history->load_line_internal_data = load_line_internal_data;
+                    
+                    history->vec_tokens = vec_tokens;
+                    history->real_new_line_number = real_new_line_number;
+                    
+                    if (init_tokenizer (tmp)) {
+                    
+                        free (history);
+                        return 1;
+                    
+                    }
+                    
+                    vec_tokens = 0;
+                    
+                    vec_push (&vec_history, history);
+                    return 0;
+                
+                }
+                
+                free (tmp);
+            
+            }
+        
+        } else {
+        
+            if ((fp = fopen (sname, "r"))) {
+            
+                history = xmalloc (sizeof (*history));
+                history->ifp = state->ifp;
+                
+                history->new_line_number = new_line_number;
+                history->newlines = newlines;
+                
+                history->filename = get_filename ();
+                history->line_number = get_line_number ();
+                
+                history->start = start;
+                history->line = line;
+                
+                history->line_end = line_end;
+                history->load_line_internal_data = load_line_internal_data;
+                
+                history->vec_tokens = vec_tokens;
+                history->real_new_line_number = real_new_line_number;
+                
+                if (init_tokenizer (sname)) {
+                
+                    free (history);
+                    return 1;
+                
+                }
+                
+                vec_tokens = 0;
+                
+                vec_push (&vec_history, history);
+                return 0;
+            
+            }
+        
+        }
+    
+    }
+    
+    if (vec_include_paths.length > 0) {
+    
+        for (i = 0; i < vec_include_paths.length; i++) {
+        
+            inc_path = vec_include_paths.data[i];
+            
+            tmp = xmalloc (strlen (inc_path) + strlen (sname) + 1);
+            sprintf (tmp, "%s%s", inc_path, sname);
+            
+            if ((fp = fopen (tmp, "r"))) {
+            
+                fclose (fp);
+                
+                history = xmalloc (sizeof (*history));
+                history->ifp = state->ifp;
+                
+                history->new_line_number = new_line_number;
+                history->newlines = newlines;
+                
+                history->filename = get_filename ();
+                history->line_number = get_line_number ();
+                
+                history->start = start;
+                history->line = line;
+                
+                history->line_end = line_end;
+                history->load_line_internal_data = load_line_internal_data;
+                
+                history->vec_tokens = vec_tokens;
+                history->real_new_line_number = real_new_line_number;
+                
+                if (init_tokenizer (tmp)) {
+                
+                    free (history);
+                    return 1;
+                
+                }
+                
+                vec_tokens = 0;
+                
+                vec_push (&vec_history, history);
+                return 0;
+            
+            }
+            
+            free (tmp);
+        
+        }
+    
+    }
+    
+error:
+    
+    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "failed to open '%s' for reading", sname);
+    return 0;
+
+}
+
+static int get_line (void) {
+
+    const char *filename;
+    unsigned long line_number = 0, flags, ln;
+    
+    char *saved_line, *caret, *temp, *name;
+    int ch, ret;
+    
+    struct pp_pseudo_op_entry *poe;
+    struct hashtab_name *key;
+    struct macro *m;
+    
+    if (!load_line_internal_data || !state->ifp) {
+        return 0;
+    }
+    
+    while (!load_line (&line, &line_end, 0, 0, &newlines, state->ifp, &load_line_internal_data)) {
+    
+        set_real_line_number (real_new_line_number);
+        set_line_number (new_line_number);
+        
+        if ((key = find_macro ("__LINE__"))) {
+        
+            if ((m = get_macro (key)) && m->type == MACRO_BUILTIN) {
+            
+                free (m->value);
+                
+                m->value = xmalloc (16);
+                sprintf (m->value, "%lu", new_line_number);
+            
+            }
+        
+        }
+        
+        real_new_line_number += newlines + 1;
+        new_line_number += newlines + 1;
+        
+        start = saved_line = line;
+        
+        while (line < line_end) {
+        
+            caret = (line = skip_whitespace (line));
+            
+            if (line[0] == '/' && line[1] == '/') {
+            
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "C++ style comments are not allowed in ISO C90");
+                break;
+            
+            }
+            
+            if (*line == '#') {
+            
+                caret = (line = skip_whitespace (line + 1));
+                
+                if (is_name_beginner ((int) *line)) {
+                
+                    name = symname (&line);
+                    
+                    if (strcmp (name, "line") == 0) {
+                    
+                        caret = (line = skip_whitespace (line));
+                        free (name);
+                        
+                        if (!isdigit ((int) *line)) {
+                        
+                            report_line_at (get_real_filename (), get_real_line_number (), REPORT_ERROR, start, caret, "expected line number");
+                            continue;
+                        
+                        }
+                        
+                        line_number = 0;
+                        
+                        while (isdigit ((int) *line)) {
+                        
+                            line_number = line_number * 10 + (*line - '0');
+                            line++;
+                        
+                        }
+                        
+                        caret = (line = skip_whitespace (line));
+                        
+                        if (*line != '"') {
+                        
+                            report_line_at (get_real_filename (), get_real_line_number (), REPORT_ERROR, start, caret, "expected filename");
+                            continue;
+                        
+                        }
+                        
+                        ch = *line++;
+                        
+                        while (!is_end_of_line[(int) *line]) {
+                        
+                            if (*line == '\\') {
+                            
+                                line++;
+                                
+                                if (*line == ch) {
+                                    (line)++;
+                                }
+                                
+                                continue;
+                            
+                            }
+                            
+                            if (*line == ch) { break; }
+                            (line)++;
+                        
+                        }
+                        
+                        if (*line != ch) {
+                        
+                            report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, caret, "missing terminating %c character", ch);
+                            
+                            temp = xmalloc (line - caret);
+                            sprintf (temp, "%.*s", (int) (line - caret), caret + 1);
+                            
+                            new_line_number = line_number;
+                            set_filename (temp);
+                        
+                        } else {
+                        
+                            new_line_number = line_number;
+                            set_filename (xstrndup (caret + 1, line - caret - 1));
+                            
+                            line++;
+                        
+                        }
+                        
+                        caret = skip_whitespace (line);
+                        
+                        if (!is_end_of_line[(int) *caret]) {
+                            report_line_at (get_real_filename (), get_real_line_number (), REPORT_WARNING, start, caret, "extra tokens at end of #line directive");
+                        }
+                        
+                        continue;
+                    
+                    }
+                    
+                    line = skip_whitespace (line);
+                    
+                    if ((poe = find_cond_directive (name))) {
+                    
+                        ignore_line = poe->handler (start, &line);
+                        
+                        free (name);
+                        break;
+                    
+                    }
+                    
+                    if (!ignore_line) {
+                        
+                        if (strcmp (name, "pragma") == 0) {
+                        
+                            caret = skip_whitespace (line);
+                            free (name);
+                            
+                            if (is_name_beginner ((int) *line)) {
+                                
+                                name = symname (&line);
+                                
+                                if (strcmp (name, "once") == 0) {
+                                    
+                                    free (name);
+                                    
+                                    if (vec_history.length > 0) {
+                                    
+                                        if (is_pragma_igored (get_real_filename ())) {
+                                            goto cleanup;
+                                        }
+                                    
+                                    } else {
+                                        report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, caret, "#pragma once in main file");
+                                    }
+                                    
+                                    continue;
+                                
+                                }
+                            
+                            }
+                            
+                            caret = (line = skip_whitespace (start));
+                            break;
+                        
+                        }
+                        
+                        if (strcmp (name, "include") == 0) {
+                        
+                            ret = handler_include (start, &line);
+                            free (name);
+                            
+                            if (ret) {
+                                break;
+                            }
+                            
+                            continue;
+                        
+                        }
+                        
+                        if ((poe = find_directive (name))) {
+                        
+                            ret = poe->handler (start, &line);
+                            free (name);
+                            
+                            if (ret) {
+                                break;
+                            }
+                            
+                            continue;
+                        
+                        }
+                        
+                        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "unknown preprocessor directive '#%s'", name);
+                    
+                    }
+                    
+                    free (name);
+                    
+                    while (!is_end_of_line[(int) *line]) {
+                        line++;
+                    }
+                    
+                    continue;
+                
+                }
+                
+                if (!isdigit ((int) *line)) {
+                
+                    report_line_at (get_real_filename (), get_real_line_number (), REPORT_ERROR, start, caret, "expected line number");
+                    continue;
+                
+                }
+                
+                line_number = 0;
+                
+                while (isdigit ((int) *line)) {
+                
+                    line_number = line_number * 10 + (*line - '0');
+                    line++;
+                
+                }
+                
+                caret = (line = skip_whitespace (line));
+                
+                if (*line != '"') {
+                
+                    report_line_at (get_real_filename (), get_real_line_number (), REPORT_ERROR, start, caret, "expected filename");
+                    continue;
+                
+                }
+                
+                ch = *line++;
+                
+                while (!is_end_of_line[(int) *line]) {
+                
+                    if (*line == '\\') {
+                    
+                        line++;
+                        
+                        if (*line == ch) {
+                            (line)++;
+                        }
+                        
+                        continue;
+                    
+                    }
+                    
+                    if (*line == ch) { break; }
+                    (line)++;
+                
+                }
+                
+                filename = get_filename ();
+                ln = get_line_number ();
+                
+                if (*line != ch) {
+                
+                    report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, caret, "missing terminating %c character", ch);
+                    
+                    temp = xmalloc (line - caret);
+                    sprintf (temp, "%.*s", (int) (line - caret), caret + 1);
+                    
+                    new_line_number = line_number;
+                    set_filename (temp);
+                
+                } else {
+                
+                    new_line_number = line_number;
+                    set_filename (xstrndup (caret + 1, line - caret - 1));
+                    
+                    line++;
+                
+                }
+                
+                caret = (line = skip_whitespace (line));
+                
+                if (!is_end_of_line[(int) *line]) {
+                
+                    if (!isdigit ((int) *line)) {
+                    
+                        report_line_at (get_real_filename (), get_real_line_number (), REPORT_ERROR, start, caret, "expected flags");
+                        continue;
+                    
+                    }
+                    
+                    flags = 0;
+                    
+                    while (isdigit ((int) *line)) {
+                    
+                        flags = flags * 10 + (*line - '0');
+                        line++;
+                    
+                    }
+                    
+                    switch (flags) {
+                    
+                        case 1: {
+                        
+                            struct hist_entry *entry = xmalloc (sizeof (*entry));
+                            
+                            entry->filename = filename;
+                            entry->line_number = ln;
+                            
+                            vec_push (&state->vec_hist, entry);
+                            break;
+                        
+                        }
+                        
+                        case 2: {
+                        
+                            struct hist_entry *entry;
+                            
+                            if (state->vec_hist.length == 0) {
+                                report_line_at (get_real_filename (), get_real_line_number (), REPORT_ERROR, start, caret, "invalid line marker flag '%d': cannot pop empty include stack", flags);
+                            } else {
+                            
+                                entry = vec_pop (&state->vec_hist);
+                                
+                                if (strcmp (entry->filename, get_filename ())) {
+                                
+                                    report_at (get_real_filename (), get_real_line_number (), REPORT_WARNING, "file \"%s\" linemaker ignored due to incorrect nesting", get_filename ());
+                                    
+                                    entry->line_number += newlines;
+                                    entry->line_number++;
+                                    
+                                    new_line_number = entry->line_number;
+                                    set_filename (entry->filename);
+                                    
+                                    /*state->corrupted = 1;*/
+                                
+                                }
+                                
+                                free (entry);
+                            
+                            }
+                            
+                            break;
+                        
+                        }
+                        
+                        default:
+                        
+                            break;
+                    
+                    }
+                
+                }
+                
+                continue;
+            
+            }
+            
+            if (!ignore_line) {
+            
+                line = saved_line;
+                return 1;
+            
+            }
+            
+            break;
+        
+        }
+    
+    }
+    
+cleanup:
+    
+    load_line_destroy_internal_data (load_line_internal_data);
+    load_line_internal_data = 0;
+    
+    if (vec_history.length == 0) {
+    
+        struct cond *cond;
+        long i;
+        
+        for (i = 0; i < vec_ifstack.length; i++) {
+        
+            cond = vec_ifstack.data[i];
+            report_at (cond->filename, cond->line_number, REPORT_ERROR, "unterminated #%s statement", cond->directive);
+            
+            free (cond->filename);
+            free (cond->directive);
+            
+            free (cond);
+        
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static int chrpos (char *s, int ch) {
+
+    char *p = strchr (s, ch);
+    return p ? p - s : -1;
+
+}
+
+#include    "stdint.h"
+
+static int hex_char (uint64_t *out, struct token *tok, int idx) {
+
+    const char *s = tok->ident + idx + 1;
+    
+    int f = 0, i = 0, h;
+    uint64_t n = 0;
+    
+    while (*s && isxdigit ((int) *s)) {
+    
+        h = chrpos ("0123456789abcdef", tolower ((int) *s));
+        
+        f = 1;
+        i++;
+        
+        n = n * 16 + h;
+        s++;
+    
+    }
+    
+    if (!f) {
+    
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok->start, tok->caret + idx - 1, "\\x used with no following hex digits");
+        return i;
+    
+    }
+    
+    if (n > ((1U << 8) - 1)) {
+    
+        uint64_t val = n % ((1U << 8) - 1);
+        
+        report_line_at (get_filename (), get_line_number (), REPORT_WARNING, tok->start, tok->caret + idx - 1, "hex escape sequence out of range");
+        *out = *out * (1U << 8) + val;
+    
+    } else {
+        *out = *out * (1U << 8) + n;
+    }
+    
+    return i;
+
+}
+
+static void scan_ch (struct token *tok, int is_long) {
+
+    const char *s = tok->ident;
+    int has_warned = 0, i;
+    
+    int64_s i64;
+    uint64_t n = 0;
+    
+    if (is_long) {
+        s++;
+    }
+    
+    s++;
+    
+    while (*s) {
+    
+        if (*s == '\\') {
+        
+            switch (*(++s)) {
+            
+                case 'a':
+                
+                    n = '\a';
+                    break;
+                
+                case 'b':
+                
+                    n = '\b';
+                    break;
+                
+                case 'f':
+                
+                    n = '\f';
+                    break;
+                
+                case 'n':
+                
+                    n = '\n';
+                    break;
+                
+                case 'r':
+                
+                    n = '\r';
+                    break;
+                
+                case 't':
+                
+                    n = '\t';
+                    break;
+                
+                case 'v':
+                
+                    n = '\v';
+                    break;
+                
+                case '\\':
+                
+                    n = '\\';
+                    break;
+                
+                case '"':
+                
+                    n = '"' | (1U << 8);
+                    break;
+                
+                case '\'':
+                
+                    n = '\'';
+                    break;
+                
+                case '0':   case '1':
+                case '2':   case '3':
+                case '4':   case '5':
+                case '6':   case '7':
+                
+                    for (i = 0; isdigit ((int) *s) && *s < '8'; s++) {
+                    
+                        if (++i > 3) {
+                            break;
+                        }
+                        
+                        n = n * 8 + (*s - '0');
+                    
+                    }
+                    
+                    s--;
+                    break;
+                
+                case 'x':
+                
+                    s += hex_char (&n, tok, s - tok->ident);
+                    break;
+                
+                default:
+                
+                    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok->start, tok->caret + (s - tok->ident), "unknown escape sequence");
+                    
+                    n = ' ';
+                    break;
+            
+            }
+        
+        } else {
+            n = *s;
+        }
+        
+        zext64 (&i64, 1U << (is_long ? 16 : 8));
+        mul64 (&tok->val.i, i64);
+        
+        zext64 (&i64, n & ((1U << (is_long ? 16 : 8)) - 1));
+        add64 (&tok->val.i, i64);
+        
+        if (*(++s) == '\'') {
+            break;
+        }
+        
+        if (!has_warned) {
+        
+            report_line_at (get_filename (), get_line_number (), REPORT_WARNING, tok->start, tok->caret, "multi-character character constant");
+            has_warned = 1;
+        
+        }
+    
+    }
+
+}
+
+/*static void scan_int (struct token *tok) {
+
+    const char *s = tok->ident;
+    int radix = 10, k;
+    
+    if (*s == '0') {
+    
+        radix = 8;
+        s++;
+        
+        if (*s == 'x') {
+        
+            radix = 16;
+            s++;
+        
+        }
+    
+    }
+    
+    while (*s != '\0' && (k = chrpos ("0123456789abcdef", tolower ((int) *s))) >= 0) {
+    
+        if (k >= radix) {
+        
+            int idx = s - tok->ident;
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok->start, tok->caret + idx, "invalid digit in integer literal");
+        
+        }
+        
+        tok->val.d = tok->val.d * radix + k;
+        s++;
+    
+    }
+
+}*/
+
+
+int tok_ident = TOK_IDENT;
+
+struct keyword {
+
+    char *name;
+    
+    int std;
+    long verison;
+    
+    enum token_kind kind;
+
+};
+
+struct punct *find_punct (char *p) {
+
+    static struct punct kws[] = {
+    
+        {   "<<=",  TOK_LSHEQ       },
+        {   ">>=",  TOK_RSHEQ       },
+        {   "...",  TOK_ELLIPSIS    },
+        
+        {   "!=",   TOK_NOTEQ       },
+        {   "%=",   TOK_MODEQ       },
+        {   "&&",   TOK_LOGAND      },
+        {   "&=",   TOK_ANDEQ       },
+        {   "*=",   TOK_STAREQ      },
+        {   "++",   TOK_INCR        },
+        {   "+=",   TOK_PLUSEQ      },
+        {   "--",   TOK_DECR        },
+        {   "-=",   TOK_MINUSEQ     },
+        {   "->",   TOK_ARROW       },
+        {   "/=",   TOK_SLASHEQ     },
+        {   "<<",   TOK_LSH         },
+        {   "<=",   TOK_LTEQ        },
+        {   "==",   TOK_EQEQ        },
+        {   ">>",   TOK_RSH         },
+        {   ">=",   TOK_GTEQ        },
+        {   "^=",   TOK_XOREQ       },
+        {   "||",   TOK_LOGOR       },
+        {   "|=",   TOK_OREQ        },
+        
+        {   "!",    TOK_XMARK       },
+        {   "%",    TOK_MOD         },
+        {   "&",    TOK_AMPER       },
+        {   "(",    TOK_LPAREN      },
+        {   ")",    TOK_RPAREN      },
+        {   "*",    TOK_STAR        },
+        {   "+",    TOK_PLUS        },
+        {   ",",    TOK_COMMA       },
+        {   "-",    TOK_MINUS       },
+        {   ".",    TOK_DOT         },
+        {   "/",    TOK_BSLASH      },
+        {   ":",    TOK_COLON       },
+        {   ";",    TOK_SEMI        },
+        {   "<",    TOK_LESS        },
+        {   "=",    TOK_ASSIGN      },
+        {   ">",    TOK_GREATER     },
+        {   "?",    TOK_QMARK       },
+        {   "[",    TOK_LBRACK      },
+        {   "]",    TOK_RBRACK      },
+        {   "^",    TOK_CARET       },
+        {   "{",    TOK_LBRACE      },
+        {   "|",    TOK_PIPE        },
+        {   "}",    TOK_RBRACE      },
+        {   "~",    TOK_TILDE       }
+    
+    };
+    
+    struct punct *kw;
+    unsigned long i;
+    
+    for (i = 0; i < (sizeof (kws) / sizeof (*kws)); i++) {
+    
+        kw = &kws[i];
+        
+        if (strncmp (p, kw->name, strlen (kw->name)) == 0) {
+            return kw;
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+#ifndef     VERSION
+# define        VERSION                 0L
+#endif
+
+static int find_kind (const char *start, const char *caret, const char *p) {
+
+    static struct keyword kws[] = {
+    
+        /* Compiler Specific keywords */
+        {   "__asm__",          0,          VERSION,    TOK_ASM         },
+        {   "__inline__",       0,          VERSION,    TOK_INLINE      },
+        {   "__restrict__",     0,          VERSION,    TOK_RESTRICT    },
+        
+        /* C90 keywords */
+        {   "auto",             90,         VERSION,    TOK_AUTO        },
+        {   "break",            90,         VERSION,    TOK_BREAK       },
+        {   "case",             90,         VERSION,    TOK_CASE        },
+        {   "char",             90,         VERSION,    TOK_CHAR        },
+        {   "const",            90,         VERSION,    TOK_CONST       },
+        {   "continue",         90,         VERSION,    TOK_CONTINUE    },
+        {   "default",          90,         VERSION,    TOK_DEFAULT     },
+        {   "do",               90,         VERSION,    TOK_DO          },
+        {   "double",           90,         VERSION,    TOK_DOUBLE      },
+        {   "else",             90,         VERSION,    TOK_ELSE        },
+        {   "enum",             90,         VERSION,    TOK_ENUM        },
+        {   "extern",           90,         VERSION,    TOK_EXTERN      },
+        {   "float",            90,         VERSION,    TOK_FLOAT       },
+        {   "for",              90,         VERSION,    TOK_FOR         },
+        {   "goto",             90,         VERSION,    TOK_GOTO        },
+        {   "if",               90,         VERSION,    TOK_IF          },
+        {   "int",              90,         VERSION,    TOK_INT         },
+        {   "long",             90,         VERSION,    TOK_LONG        },
+        {   "register",         90,         VERSION,    TOK_REGISTER    },
+        {   "return",           90,         VERSION,    TOK_RETURN      },
+        {   "short",            90,         VERSION,    TOK_SHORT       },
+        {   "signed",           90,         VERSION,    TOK_SIGNED      },
+        {   "sizeof",           90,         VERSION,    TOK_SIZEOF      },
+        {   "static",           90,         VERSION,    TOK_STATIC      },
+        {   "struct",           90,         VERSION,    TOK_STRUCT      },
+        {   "switch",           90,         VERSION,    TOK_SWITCH      },
+        {   "typedef",          90,         VERSION,    TOK_TYPEDEF     },
+        {   "union",            90,         VERSION,    TOK_UNION       },
+        {   "unsigned",         90,         VERSION,    TOK_UNSIGNED    },
+        {   "void",             90,         VERSION,    TOK_VOID        },
+        {   "volatile",         90,         VERSION,    TOK_VOLATILE    },
+        {   "while",            90,         VERSION,    TOK_WHILE       },
+        
+        /* C99 keywords */
+        {   "inline",           99,         VERSION,    TOK_INLINE      },
+        {   "restrict",         99,         VERSION,    TOK_RESTRICT    }
+    
+    };
+    
+    struct keyword *kw;
+    unsigned long i;
+    
+    for (i = 0; i < (sizeof (kws) / sizeof (*kws)); i++) {
+    
+        kw = &kws[i];
+        
+        if (strcmp (p, kw->name) == 0 && kw->verison <= state->version) {
+        
+            if (state->std) {
+            
+                if (state->pedantic) {
+                
+                    if (!kw->std || kw->std > state->std) {
+                        break;
+                    }
+                
+                }
+                
+                if (kw->std) {
+                
+                    if (kw->std > state->std) {
+                        break;
+                    }
+                    
+                    return kw->kind;
+                
+                }
+                
+                report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, caret, "'%s' is a compiler-specific keyword so compatiblity isn't guarenteed", kw->name);
+            
+            }
+            
+            return kw->kind;
+        
+        }
+    
+    }
+    
+    return TOK_IDENT;
+
+}
+
+#define     BN_SIZE                     4
+
+/* bn = (bn << shift) | or_val */
+static int bn_lshift (unsigned int *bn, int shift, int or_val) {
+
+    int i;
+    unsigned int v;
+    
+    if (bn[BN_SIZE - 1] >> (32 - shift)) {
+        return shift;
+    }
+    
+    for (i = 0; i < BN_SIZE; i++) {
+    
+        v = bn[i];
+        
+        bn[i] = (v << shift) | or_val;
+        or_val = v >> (32 - shift);
+    
+    }
+    
+    return 0;
+
+}
+
+float strtof (const char *nptr, char **endptr) {
+
+    const char *s = nptr;
+    float res = 0.0f;
+    
+    int sign = 1;
+    int exp = 0;
+    int has_digits = 0;
+    
+    while (isspace ((unsigned char) *s)) {
+        s++;
+    }
+    
+    if (*s == '-') {
+    
+        sign = -1;
+        s++;
+    
+    } else if (*s == '+') {
+        s++;
+    }
+    
+    /* Integer part */
+    while (isdigit ((unsigned char) *s)) {
+    
+        res = res * 10.0f + (*s++ - '0');
+        has_digits = 1;
+    
+    }
+    
+    /* Fractional part */
+    if (*s == '.') {
+    
+        float factor = 0.1f;
+        s++;
+        
+        while (isdigit ((unsigned char) *s)) {
+        
+            res += (*s++ - '0') * factor;
+            factor *= 0.1f;
+            
+            has_digits = 1;
+        
+        }
+    
+    }
+    
+    if (!has_digits) {
+    
+        if (endptr) {
+            *endptr = (char *) nptr;
+        }
+        
+        return 0.0f;
+    
+    }
+    
+    /* Exponent part */
+    if (*s == 'e' || *s == 'E') {
+    
+        const char *backtrack = s;
+        
+        int exp_sign = 1;
+        int e_val = 0;
+        
+        s++;
+        
+        if (*s == '-') {
+        
+            exp_sign = -1;
+            s++;
+        
+        } else if (*s == '+') {
+            s++;
+        }
+        
+        if (isdigit ((unsigned char) *s)) {
+        
+            while (isdigit ((unsigned char) *s)) {
+            
+                e_val = e_val * 10 + (*s - '0');
+                s++;
+            
+            }
+            
+            exp = e_val * exp_sign;
+        
+        } else {
+            s = backtrack; 
+        }
+    
+    }
+    
+    if (exp != 0) {
+    
+        float base = (exp > 0) ? 10.0f : 0.1f;
+        int abs_exp = (exp > 0) ? exp : -exp;
+        
+        for (; abs_exp > 0; abs_exp--) {
+            res *= base;
+        }
+    
+    }
+    
+    if (endptr) {
+        *endptr = (char *) s;
+    }
+    
+    return res * sign;
+
+}
+
+long double strtold (const char *nptr, char **endptr) {
+
+    const char *s = nptr;
+    
+    long double res = 0.0L;
+    int sign = 1, exp = 0, has_digits = 0;
+    
+    while (isspace ((unsigned char) *s)) {
+        s++;
+    }
+    
+    if (*s == '-') {
+    
+        sign = -1;
+        s++;
+    
+    } else if (*s == '+') {
+        s++;
+    }
+    
+    while (isdigit ((unsigned char) *s)) {
+    
+        res = res * 10.0L + (*s++ - '0');
+        has_digits = 1;
+    
+    }
+    
+    if (*s == '.') {
+    
+        long double factor = 0.1L;
+        s++;
+        
+        while (isdigit ((unsigned char) *s)) {
+            
+            res += (long double) (*s++ - '0') * factor;
+            factor *= 0.1L;
+            
+            has_digits = 1;
+        
+        }
+    
+    }
+    
+    if (!has_digits) {
+    
+        if (endptr) {
+            *endptr = (char *) nptr;
+        }
+        
+        return 0.0L;
+    
+    }
+    
+    if (*s == 'e' || *s == 'E') {
+    
+        const char *back = s;
+        int esign = 1, eval = 0;
+        
+        s++;
+        
+        if (*s == '-') {
+        
+            esign = -1;
+            s++;
+        
+        } else if (*s == '+') {
+            s++;
+        }
+        
+        if (isdigit ((unsigned char) *s)) {
+        
+            while (isdigit ((unsigned char) *s)) {
+                eval = eval * 10 + (*s++ - '0');
+            }
+            
+            exp = eval * esign;
+        
+        } else {
+            s = back;
+        }
+    
+    }
+    
+    /* Apply exponent in safe chunks to avoid intermediate overflow/underflow */
+    if (exp != 0) {
+    
+        int abs_exp = (exp > 0) ? exp : -exp;
+        
+        while (abs_exp > 0) {
+        
+            int chunk = (abs_exp > 4000) ? 4000 : abs_exp;
+            
+            long double multiplier = 1.0L;
+            long double base = 10.0L;
+            
+            int i = chunk;
+            
+            /* Binary Exponentiation for the current chunk */
+            while (i > 0) {
+            
+                if (i & 1) {
+                    multiplier *= base;
+                }
+                
+                base *= base;
+                i >>= 1;
+            
+            }
+            
+            if (exp > 0) {
+                res *= multiplier;
+            } else {
+                res /= multiplier;
+            }
+            
+            abs_exp -= chunk;
+        
+        }
+    
+    }
+    
+    if (endptr) {
+        *endptr = (char *) s;
+    }
+    
+    return res * sign;
+
+}
+
+static void parse_number (struct token *tok) {
+
+    const char *p = tok->ident, *q;
+    
+    unsigned int bn[BN_SIZE] = { 0 };
+    long double d;
+    
+    CString cstr;
+    int b, t, shift, frac_bits, s, exp_val, ch;
+    
+    cstr_new (&cstr);
+    
+    t = (ch = *p++);
+    ch = *p++;
+    
+    cstr_ccat (&cstr, t);
+    b = 10;
+    
+    if (t == '0') {
+    
+        if (tolower ((int) ch) == 'x') {
+        
+            cstr.size--;
+            
+            ch = *p++;
+            b = 16;
+        
+        }
+    
+    }
+    
+    for (;;) {
+    
+        if (ch >= 'a' && ch <= 'f') {
+            t = ch - 'a' + 10;
+        } else if (ch >= 'A' && ch <= 'F') {
+            t = ch - 'A' + 10;
+        } else if (isdigit ((int) ch)) {
+            t = ch - '0';
+        } else {
+            break;
+        }
+        
+        if (t >= b) {
+            break;
+        }
+        
+        cstr_ccat (&cstr, ch);
+        ch = *p++;
+    
+    }
+    
+    if (ch == '.' || ((ch == 'e' || ch == 'E') && b == 10) || ((ch == 'p' || ch == 'P') && (b == 16 || b == 2))) {
+    
+        if (b != 10) {
+        
+            frac_bits = 0;
+            cstr_ccat (&cstr, '\0');
+            
+            if (b == 16) {
+                shift = 4;
+            } else {
+                shift = 1;
+            }
+            
+            q = cstr.data;
+            
+            for (;;) {
+            
+                t = *q++;
+                
+                if (t == '\0') {
+                    break;
+                } else if (t >= 'a') {
+                    t = t - 'a' + 10;
+                } else if (t >= 'A') {
+                    t = t - 'A' + 10;
+                } else {
+                    t = t - '0';
+                }
+                
+                frac_bits -= bn_lshift (bn, shift, t);
+            
+            }
+            
+            if (ch == '.') {
+            
+                ch = *p++;
+                
+                for (;;) {
+                
+                    t = ch;
+                    
+                    if (t >= 'a' && t <= 'f') {
+                        t = t - 'a' + 10;
+                    } else if (t >= 'A' && t <= 'F') {
+                        t = t - 'A' + 10;
+                    } else if (t >= '0' && t <= '9') {
+                        t = t - '0';
+                    } else {
+                        break;
+                    }
+                    
+                    if (t >= b) {
+                        report_at (get_filename (), get_line_number (), REPORT_ERROR, "invalid digit");
+                    }
+                    
+                    frac_bits -= bn_lshift (bn, shift, t);
+                    frac_bits += shift;
+                    
+                    ch = *p++;
+                
+                }
+            
+            }
+            
+            if (ch != 'p' && ch != 'P') {
+                report_at (get_filename (), get_line_number (), REPORT_ERROR, "expected exponent");
+            }
+            
+            ch = *p++;
+            
+            s = 1;
+            exp_val = 0;
+            
+            if (ch == '+') {
+                ch = *p++;
+            } else if (ch == '-') {
+            
+                s = -1;
+                ch = *p++;
+            
+            }
+            
+            if (ch < '0' || ch > '9') {
+                report_at (get_filename (), get_line_number (), REPORT_ERROR, "expected exponent digits");
+            }
+            
+            while (ch >= '0' && ch <= '9') {
+            
+                if (exp_val < 100000000) {
+                    exp_val = exp_val * 10 + ch - '0';
+                }
+                
+                ch = *p++;
+            
+            }
+            
+            exp_val = exp_val * s;
+            
+            d = (long double) bn[3] * 79228162514264337593543950336.0L + (long double) bn[2] * 18446744073709551616.0L + (long double) bn[1] * 4294967296.0L + (long double) bn[0];
+            d = ldexp (d, exp_val - frac_bits);
+            
+            t = toupper ((int) ch);
+            
+            if (t == 'F') {
+            
+                ch = *p++;
+                
+                tok->kind = TOK_CFLOAT;
+                tok->val.f = (float) d;
+            
+            } else if (t == 'L') {
+            
+                ch = *p++;
+                
+                tok->kind = TOK_CLDOUBLE;
+                tok->val.ld = d;
+            
+            } else {
+            
+                tok->kind = TOK_CDOUBLE;
+                tok->val.d = (double) d;
+            
+            }
+        
+        } else {
+        
+            if (ch == '.') {
+            
+                cstr_ccat (&cstr, ch);
+                ch = *p++;
+                
+                while (ch >= '0' && ch <= '9') {
+                
+                    cstr_ccat (&cstr, ch);
+                    ch = *p++;
+                
+                }
+            
+            }
+            
+            if (ch == 'e' || ch == 'E') {
+            
+                cstr_ccat (&cstr, ch);
+                ch = *p++;
+                
+                if (ch == '-' || ch == '+') {
+                
+                    cstr_ccat (&cstr, ch);
+                    ch = *p++;
+                
+                }
+                
+                if (ch < '0' || ch >= '9') {
+                    report_at (get_filename (), get_line_number (), REPORT_ERROR, "expected exponent digits");
+                }
+                
+                while (ch >= '0' && ch <=  '9') {
+                
+                    cstr_ccat (&cstr, ch);
+                    ch = *p++;
+                
+                }
+            
+            }
+            
+            cstr_ccat (&cstr, '\0');
+            
+            t = toupper ((int) ch);
+            errno = 0;
+            
+            if (t == 'F') {
+            
+                ch = *p++;
+                
+                tok->kind = TOK_CFLOAT;
+                tok->val.f = strtof (cstr.data, 0);
+            
+            } else if (t == 'L') {
+            
+                ch = *p++;
+                
+                tok->kind = TOK_CLDOUBLE;
+                tok->val.ld = strtold (cstr.data, 0);
+            
+            } else {
+            
+                tok->kind = TOK_CDOUBLE;
+                tok->val.d = strtod (cstr.data, 0);
+            
+            }
+        
+        }
+    
+    } else {
+    
+        int64_s n = { 0 }, n1 = { 0 }, n2, n3, n4, n5;
+        
+        const char *p1;
+        int ov = 0, lcount, ucount;
+        
+        cstr_ccat (&cstr, '\0');
+        q = cstr.data;
+        
+        if (b == 10 && *q == '0') {
+        
+            b = 8;
+            q++;
+        
+        }
+        
+        for (;;) {
+        
+            t = *q++;
+            
+            if (t == '\0') {
+                break;
+            }
+            
+            if (t >= 'a') {
+                t = t - 'a' + 10;
+            } else if (t >= 'A') {
+                t = t - 'A' + 10;
+            } else {
+                t = t - '0';
+            }
+            
+            if (t >= b) {
+                report_at (get_filename (), get_line_number (), REPORT_ERROR, "invalid digit");
+            }
+            
+            zext64 (&n2, b);
+            zext64 (&n3, t);
+            
+            n1 = n;
+            
+            mul64 (&n, n2);
+            add64 (&n, n3);
+            
+            parse_string_to_i64 (&n4, "0x1000000000000000");
+            n5 = n;
+            
+            if (gte64_unsigned (n1, n4)) {
+            
+                div64 (&n5, n2);
+                
+                if (neq64 (n5, n1)) {
+                    ov = 1;
+                }
+            
+            }
+        
+        }
+        
+        lcount = ucount = 0;
+        p1 = p;
+        
+        for (;;) {
+        
+            t = toupper ((int) ch);
+            
+            if (t == 'L') {
+            
+                if (lcount >= 2) {
+                    report_at (get_filename (), get_line_number (), REPORT_ERROR, "three 'L's in integer constant");
+                }
+                
+                if (lcount && *(p - 1) != ch) {
+                    report_at (get_filename (), get_line_number (), REPORT_ERROR, "incorrect integer suffix: %s", p1);
+                }
+                
+                lcount++;
+                ch = *p++;
+            
+            } else if (t == 'U') {
+            
+                if (ucount >= 1) {
+                    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok->start, tok->caret, "two 'U's in integer constant");
+                }
+                
+                ucount++;
+                ch = *p++;
+            
+            } else {
+                break;
+            }
+        
+        }
+        
+        /*if (pp_expr) {
+            lcount = 2;
+        }*/
+        
+        if (ucount == 0 && b == 10) {
+        
+            parse_string_to_i64 (&n4, "0x80000000");
+            
+            if (gte64_unsigned (n, n4)) {
+                lcount = 1 + 1;
+            }
+            
+            parse_string_to_i64 (&n4, " 0x8000000000000000");
+            
+            if (gte64_unsigned (n, n4)) {
+                ov = 1, ucount = 1;
+            }
+        
+        } else {
+        
+            parse_string_to_i64 (&n4, "0x100000000");
+            parse_string_to_i64 (&n5, "0x80000000");
+            
+            if (gte64_unsigned (n, n4)) {
+                lcount = 1 + 1;
+            } else if (gte64_unsigned (n, n5)) {
+                ucount = 1;
+            }
+            
+            parse_string_to_i64 (&n4, " 0x8000000000000000");
+            
+            if (gte64_unsigned (n, n4)) {
+                ucount = 1;
+            }
+        
+        }
+        
+        if (ov) {
+            report_at (get_filename (), get_line_number (), REPORT_WARNING, "integer constant overflow");
+        }
+        
+        tok->kind = TOK_CINT;
+        
+        if (lcount) {
+        
+            tok->kind = (state->long64 ? TOK_CLLONG : TOK_CLONG);
+            
+            if (lcount == 2) {
+            
+                if (state->std == 90) {
+                
+                    const char *caret = tok->caret;
+                    caret += (p1 - tok->ident);
+                    
+                    report_line_at (get_filename (), get_line_number (), state->pedantic ? REPORT_ERROR : REPORT_WARNING, tok->start, caret - 1, "use of C99 long long integer constant");
+                
+                }
+                
+                tok->kind = TOK_CLLONG;
+            
+            }
+        
+        }
+        
+        if (ucount) {
+            tok->kind++;
+        }
+        
+        tok->val.i = n;
+        
+    }
+
+}
+
+static int tok_index = 0;
+struct token tok = { 0 };
+
+void unget_token (struct token *saved_tok) {
+
+    if (tok_index > 0) {
+        tok_index--;
+    }
+    
+    tok.val = saved_tok->val;
+    tok.kind = saved_tok->kind;
+    
+    tok.start = saved_tok->start;
+    tok.caret = saved_tok->caret;
+    
+    tok.ident = saved_tok->ident;
+    tok.len = saved_tok->len;
+    
+    tok.is_unget = 1;
+    free (saved_tok);
+
+}
+
+static void token_skip_pp_quoted (char **pp, char *end) {
+
+    char quote = **pp;
+    (*pp)++;
+    
+    while (*pp < end) {
+    
+        if (**pp == '\\') {
+        
+            (*pp)++;
+            
+            if (*pp < end) {
+                (*pp)++;
+            }
+            
+            continue;
+        
+        }
+        
+        if (**pp == quote) {
+        
+            (*pp)++;
+            break;
+        
+        }
+        
+        (*pp)++;
+    
+    }
+
+}
+
+static int token_line_has_unclosed_function_macro (char *line_start, char *end) {
+
+    char *p = line_start, *q, *r, *sname;
+    int depth;
+    
+    struct hashtab_name *key;
+    struct macro *m;
+    
+    while (p < end) {
+    
+        if (*p == '"' || *p == '\'') {
+        
+            token_skip_pp_quoted (&p, end);
+            continue;
+        
+        }
+        
+        if (!is_name_beginner ((int) *p)) {
+        
+            p++;
+            continue;
+        
+        }
+        
+        q = p;
+        sname = symname (&q);
+        
+        if ((key = find_macro (sname)) && (m = get_macro (key)) && m->nargs >= 0) {
+        
+            r = skip_whitespace (q);
+            
+            if (r < end && *r == '(') {
+            
+                depth = 0;
+                
+                while (r < end) {
+                
+                    if (*r == '"' || *r == '\'') {
+                    
+                        token_skip_pp_quoted (&r, end);
+                        continue;
+                    
+                    }
+                    
+                    if (*r == '(') {
+                        depth++;
+                    } else if (*r == ')') {
+                    
+                        depth--;
+                        
+                        if (depth == 0) {
+                            break;
+                        }
+                    
+                    }
+                    
+                    r++;
+                
+                }
+                
+                free (sname);
+                
+                if (depth > 0) {
+                    return 1;
+                }
+                
+                p = r;
+                continue;
+            
+            }
+        
+        }
+        
+        free (sname);
+        p = q;
+    
+    }
+    
+    return 0;
+
+}
+
+static char *token_collect_macro_continuation (char *line_start, char **line_end_p) {
+
+    char *buf, *next, *next_end;
+    unsigned long len, next_len, cap, extra_newlines;
+    
+    len = *line_end_p - line_start;
+    cap = len + 1;
+    
+    buf = xmalloc (cap + 1);
+    memcpy (buf, line_start, len);
+    buf[len] = '\0';
+    
+    while (token_line_has_unclosed_function_macro (buf, buf + len)) {
+    
+        if (load_line (&next, &next_end, 0, 0, &extra_newlines, state->ifp, &load_line_internal_data)) {
+            break;
+        }
+        
+        next_len = next_end - next;
+        
+        if (len + 1 + next_len + 1 > cap) {
+        
+            cap = len + 1 + next_len + 1 + 256;
+            buf = xrealloc (buf, cap + 1);
+        
+        }
+        
+        buf[len++] = ' ';
+        
+        memcpy (buf + len, next, next_len);
+        len += next_len;
+        
+        buf[len] = '\0';
+        new_line_number += extra_newlines + 1;
+    
+    }
+    
+    *line_end_p = buf + len;
+    return buf;
+
+}
+
+void get_token (void) {
+
+    struct history *history;
+    
+    struct hashtab_name *key;
+    struct macro *m;
+    
+    if (tok.is_unget) {
+    
+        if (tok.start) {
+            free ((char *) tok.start);
+        }
+    
+    }
+    
+    if (tok.ident) { free (tok.ident); }
+    memset (&tok, 0, sizeof (tok));
+    
+    for (;;) {
+        
+        if (!line || skip_whitespace (line) >= line_end) {
+        
+            if (vec_tokens) {
+            
+                struct token *tok;
+                
+                if (tok_index < vec_tokens->length) {
+                    break;
+                }
+                
+                while ((tok = vec_pop (vec_tokens))) {
+                    free (tok);
+                }
+            
+            }
+            
+            if (!get_line ()) {
+            
+                free ((char *) get_filename ());
+                
+                if ((history = vec_pop (&vec_history))) {
+                
+                    state->ifp = history->ifp;
+                    
+                    new_line_number = history->new_line_number;
+                    newlines = history->newlines;
+                    
+                    start = history->start;
+                    line = history->line;
+                    
+                    line_end = history->line_end;
+                    load_line_internal_data = history->load_line_internal_data;
+                    
+                    vec_tokens = history->vec_tokens;
+                    real_new_line_number = history->real_new_line_number;
+                    
+                    set_filename_and_line_number (history->filename, history->line_number);
+                    
+                    if ((key = find_macro ("__FILE__"))) {
+                    
+                        if ((m = get_macro (key)) && m->type == MACRO_BUILTIN) {
+                        
+                            free (m->value);
+                            
+                            m->value = xmalloc (1 + strlen (history->filename) + 2);
+                            sprintf (m->value, "\"%s\"", history->filename);
+                        
+                        }
+                    
+                    }
+                    
+                    if ((key = find_macro ("__LINE__"))) {
+                    
+                        if ((m = get_macro (key)) && m->type == MACRO_BUILTIN) {
+                        
+                            free (m->value);
+                            
+                            m->value = xmalloc (16);
+                            sprintf (m->value, "%lu", history->line_number);
+                        
+                        }
+                    
+                    }
+                    
+                    free (history);
+                    continue;
+                
+                }
+                
+                return;
+            
+            }
+            
+            continue;
+        
+        }
+        
+        if (token_line_has_unclosed_function_macro (start, line_end)) {
+        
+            start = token_collect_macro_continuation (start, &line_end);
+            line = start;
+        
+        }
+        
+        vec_tokens = preprocess_line (&line, 0, 0, 0);
+        
+        tok_index = 0;
+        break;
+    
+    }
+    
+    if (tok_index < vec_tokens->length) {
+    
+        memcpy (&tok, vec_tokens->data[tok_index++], sizeof (tok));
+        
+        if (tok.kind == TOK_LCHAR || tok.kind == TOK_CCHAR) {
+        
+            scan_ch (&tok, tok.kind == TOK_LCHAR);
+            return;
+        
+        }
+        
+        if (tok.kind == TOK_PPNUM) {
+        
+            parse_number (&tok);;
+            return;
+        
+        }
+        
+        if (tok.kind == TOK_IDENT) {
+        
+            tok.kind = find_kind (tok.start, tok.caret, tok.ident);;
+            return;
+        
+        }
+    
+    }
+
+}
+
+int init_tokenizer (const char *ifile) {
+
+    const char *tmp;
+    
+    if (!ifile || strcmp (ifile, "-") == 0) {
+    
+        tmp = xstrdup ("<stdin>");
+        
+        set_real_filename (tmp);
+        set_filename (tmp);
+        
+        state->ifp = stdin;
+    
+    } else {
+    
+        tmp = xstrdup (ifile);
+        
+        set_real_filename (tmp);
+        set_filename (tmp);
+        
+        if (!(state->ifp = fopen (ifile, "r"))) {
+        
+            report_at (program_name, 0, REPORT_FATAL_ERROR, "failed to open '%s' for reading", ifile);
+            return 1;
+        
+        }
+    
+    }
+    
+    set_real_line_number (0);
+    set_line_number (0);
+    
+    new_line_number = 1, newlines = 0;
+    start = 0, line = 0;
+
+    line_end = 0;
+    real_new_line_number = 1;
+    
+    load_line_internal_data = load_line_create_internal_data (&new_line_number);
+    return 0;
+
+}
diff --git a/token.h b/token.h
new file mode 100755 (executable)
index 0000000..cbf67e4
--- /dev/null
+++ b/token.h
@@ -0,0 +1,171 @@
+/******************************************************************************
+ * @file            token.h
+ *****************************************************************************/
+#ifndef     _TOKEN_H
+#define     _TOKEN_H
+
+enum token_kind {
+
+    TOK_EOF = 0,
+    
+    /* Single character punctionation. */
+    TOK_XMARK = 33,
+    TOK_HASH = 35,
+    TOK_DOLLAR,
+    TOK_MOD,
+    TOK_AMPER,
+    TOK_LPAREN = 40,
+    TOK_RPAREN,
+    TOK_STAR,
+    TOK_PLUS,
+    TOK_COMMA,
+    TOK_MINUS,
+    TOK_DOT,
+    TOK_BSLASH,
+    TOK_COLON = 58,
+    TOK_SEMI,
+    TOK_LESS,
+    TOK_ASSIGN,
+    TOK_GREATER,
+    TOK_QMARK,
+    TOK_LBRACK = 91,
+    TOK_FSLASH,
+    TOK_RBRACK,
+    TOK_CARET,
+    TOK_UNDERSCORE,
+    TOK_BACKTICK,
+    TOK_LBRACE = 123,
+    TOK_PIPE,
+    TOK_RBRACE,
+    TOK_TILDE,
+    
+    /* Multi-character punctuation. */
+    TOK_EQEQ = 128,
+    TOK_NOTEQ,
+    TOK_LTEQ,
+    TOK_GTEQ,
+    
+    TOK_INCR,
+    TOK_DECR,
+    
+    TOK_LOGAND,
+    TOK_LOGOR,
+    TOK_LSH,
+    TOK_RSH,
+    TOK_PLUSEQ,
+    TOK_MINUSEQ,
+    TOK_STAREQ,
+    TOK_SLASHEQ,
+    TOK_MODEQ,
+    TOK_LSHEQ,
+    TOK_RSHEQ,
+    TOK_ANDEQ,
+    TOK_XOREQ,
+    TOK_OREQ,
+    TOK_ELLIPSIS,
+    TOK_ARROW,
+    
+    /* Start of keywords. */
+    TOK_ASM = 256,
+    TOK_AUTO,
+    TOK_BREAK,
+    TOK_CASE,
+    TOK_CHAR,
+    TOK_CONST,
+    TOK_CONTINUE,
+    TOK_DEFAULT,
+    TOK_DO,
+    TOK_DOUBLE,
+    TOK_ELSE,
+    TOK_ENUM,
+    TOK_EXTERN,
+    TOK_FLOAT,
+    TOK_FOR,
+    TOK_GOTO,
+    TOK_IF,
+    TOK_INLINE,
+    TOK_INT,
+    TOK_LONG,
+    TOK_OFFSETOF,
+    TOK_REGISTER,
+    TOK_RESTRICT,
+    TOK_RETURN,
+    TOK_SHORT,
+    TOK_SIGNED,
+    TOK_SIZEOF,
+    TOK_STATIC,
+    TOK_STRUCT,
+    TOK_SWITCH,
+    TOK_TYPEDEF,
+    TOK_TYPEOF,
+    TOK_UNION,
+    TOK_UNSIGNED,
+    TOK_VOID,
+    TOK_VOLATILE,
+    TOK_WHILE,
+    
+    TOK_PPSTR,
+    TOK_PPNUM,
+    TOK_LSTR,
+    
+    TOK_LCHAR,
+    TOK_CCHAR,
+    TOK_CINT,
+    TOK_CUINT,
+    TOK_CLLONG,
+    TOK_CULLONG,
+    TOK_CLONG,
+    TOK_CULONG,
+    
+    TOK_CFLOAT,
+    TOK_CDOUBLE,
+    TOK_CLDOUBLE,
+    
+    TOK_IDENT
+    
+};
+
+#include    "int64.h"
+
+struct token {
+
+    const char *start;
+    const char *caret;
+    
+    char *ident;
+    unsigned long len;
+    
+    enum token_kind kind;
+    
+    union {
+    
+        long double ld;
+        
+        double d;
+        float f;
+        
+        int64_s i;
+    
+    } val;
+    
+    int is_unget;
+
+};
+
+extern struct token tok;
+extern int tok_ident;
+
+void get_token (void);
+void unget_token (struct token *saved_tok);
+
+struct punct {
+
+    char *name;
+    enum token_kind kind;
+
+};
+
+struct punct *find_punct (char *p);
+int init_tokenizer (const char *ifile);
+
+#endif      /* _TOKEN_H */
diff --git a/vector.c b/vector.c
new file mode 100755 (executable)
index 0000000..80e6377
--- /dev/null
+++ b/vector.c
@@ -0,0 +1,42 @@
+/******************************************************************************
+ * @file            vector.c
+ *****************************************************************************/
+#include    <stddef.h>
+#include    <stdlib.h>
+
+#include    "vector.h"
+
+extern void *xrealloc (void *__ptr, unsigned long __size);
+
+void vec_adjust (struct vector *vec, long length) {
+
+    if (vec->capacity <= length) {
+    
+        if (vec->capacity == 0) {
+            vec->capacity = 16;
+        } else {
+            vec->capacity <<= 1;
+        }
+        
+        vec->data = xrealloc (vec->data, sizeof (*(vec->data)) * vec->capacity);
+    
+    }
+
+}
+
+void *vec_pop (struct vector *vec) {
+
+    if (!vec || !vec->length) {
+        return 0;
+    }
+    
+    return vec->data[--vec->length];
+
+}
+
+void vec_push (struct vector *vec, void *elem) {
+
+    vec_adjust (vec, vec->length);
+    vec->data[vec->length++] = elem;
+
+}
diff --git a/vector.h b/vector.h
new file mode 100755 (executable)
index 0000000..7ba6848
--- /dev/null
+++ b/vector.h
@@ -0,0 +1,19 @@
+/******************************************************************************
+ * @file            vector.h
+ *****************************************************************************/
+#ifndef     _VECTOR_H
+#define     _VECTOR_H
+
+struct vector {
+
+    void **data;
+    long capacity, length;
+
+};
+
+void vec_adjust (struct vector *vec, long length);
+void vec_push (struct vector *vec, void *elem);
+
+void *vec_pop (struct vector *vec);
+
+#endif      /* _VECTOR_H */