]> pd.if.org Git - lice/blobdiff - parse.c
autocommit for files dated 2014-11-17 20:13:30
[lice] / parse.c
diff --git a/parse.c b/parse.c
new file mode 100644 (file)
index 0000000..0189305
--- /dev/null
+++ b/parse.c
@@ -0,0 +1,1720 @@
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "lice.h"
+#include "lexer.h"
+#include "conv.h"
+#include "init.h"
+#include "parse.h"
+#include "opt.h"
+
+static ast_t       *parse_expression(void);
+static ast_t       *parse_expression_postfix(void);
+static ast_t       *parse_expression_multiplicative(void);
+static ast_t       *parse_expression_conditional(void);
+static ast_t       *parse_expression_cast(void);
+static data_type_t *parse_expression_cast_type(void);
+static ast_t       *parse_expression_unary(void);
+static ast_t       *parse_expression_comma(void);
+
+static ast_t       *parse_statement_compound(void);
+static void         parse_statement_declaration(list_t *);
+static ast_t       *parse_statement(void);
+
+
+static data_type_t *parse_declaration_specification(storage_t *);
+static data_type_t *parse_declarator(char **, data_type_t *, list_t *, cdecl_t);
+static void         parse_declaration(list_t *, ast_t *(*)(data_type_t *, char *));
+
+static data_type_t *parse_function_parameter(char **, bool);
+static data_type_t *parse_function_parameters(list_t *, data_type_t *);
+
+
+table_t *parse_typedefs = &SENTINEL_TABLE;
+
+static bool parse_type_check(lexer_token_t *token);
+
+static void parse_semantic_lvalue(ast_t *ast) {
+    switch (ast->type) {
+        case AST_TYPE_VAR_LOCAL:
+        case AST_TYPE_VAR_GLOBAL:
+        case AST_TYPE_DEREFERENCE:
+        case AST_TYPE_STRUCT:
+            return;
+    }
+    compile_error("expected lvalue, `%s' isn't a valid lvalue", ast_string(ast));
+}
+
+static void parse_semantic_notvoid(data_type_t *type) {
+    if (type->type == TYPE_VOID)
+        compile_error("void not allowed in expression");
+}
+
+void parse_expect(char punct) {
+    lexer_token_t *token = lexer_next();
+    if (!lexer_ispunct(token, punct))
+        compile_error("expected `%c`, got %s instead", punct, lexer_token_string(token));
+}
+
+void parse_semantic_assignable(data_type_t *to, data_type_t *from) {
+    from = ast_array_convert(from);
+    if ((conv_capable(to)   || to->type   == TYPE_POINTER) &&
+        (conv_capable(from) || from->type == TYPE_POINTER)) {
+
+        return;
+    }
+
+    if (ast_struct_compare(to, from))
+        return;
+
+    compile_error("incompatible types '%s' and '%s' in assignment", ast_type_string(to), ast_type_string(from));
+}
+
+static bool parse_identifer_check(lexer_token_t *token, const char *identifier) {
+    return token->type == LEXER_TOKEN_IDENTIFIER && !strcmp(token->string, identifier);
+}
+
+long parse_evaluate(ast_t *ast);
+int parse_evaluate_offsetof(ast_t *ast, int offset) {
+    if (ast->type == AST_TYPE_STRUCT)
+        return parse_evaluate_offsetof(ast->structure, ast->ctype->offset + offset);
+    return parse_evaluate(ast) + offset;
+}
+
+long parse_evaluate(ast_t *ast) {
+    long cond;
+
+    switch (ast->type) {
+        case AST_TYPE_LITERAL:
+            if (ast_type_isinteger(ast->ctype))
+                return ast->integer;
+            compile_error("not a valid integer constant expression `%s'", ast_string(ast));
+            break;
+
+        case '+':             return parse_evaluate(ast->left) +  parse_evaluate(ast->right);
+        case '-':             return parse_evaluate(ast->left) -  parse_evaluate(ast->right);
+        case '*':             return parse_evaluate(ast->left) *  parse_evaluate(ast->right);
+        case '/':             return parse_evaluate(ast->left) /  parse_evaluate(ast->right);
+        case '<':             return parse_evaluate(ast->left) <  parse_evaluate(ast->right);
+        case '>':             return parse_evaluate(ast->left) >  parse_evaluate(ast->right);
+        case '^':             return parse_evaluate(ast->left) ^  parse_evaluate(ast->right);
+        case '%':             return parse_evaluate(ast->left) %  parse_evaluate(ast->right);
+        case '|':             return parse_evaluate(ast->left) |  parse_evaluate(ast->right);
+
+        case AST_TYPE_AND:    return parse_evaluate(ast->left) && parse_evaluate(ast->right);
+        case AST_TYPE_OR:     return parse_evaluate(ast->left) || parse_evaluate(ast->right);
+        case AST_TYPE_EQUAL:  return parse_evaluate(ast->left) == parse_evaluate(ast->right);
+        case AST_TYPE_LEQUAL: return parse_evaluate(ast->left) <= parse_evaluate(ast->right);
+        case AST_TYPE_GEQUAL: return parse_evaluate(ast->left) >= parse_evaluate(ast->right);
+        case AST_TYPE_NEQUAL: return parse_evaluate(ast->left) != parse_evaluate(ast->right);
+        case AST_TYPE_LSHIFT: return parse_evaluate(ast->left) << parse_evaluate(ast->right);
+        case AST_TYPE_RSHIFT: return parse_evaluate(ast->left) >> parse_evaluate(ast->right);
+
+
+        /* Deal with unary operations differently */
+        case '!':                        return !parse_evaluate(ast->unary.operand);
+        case '~':                        return ~parse_evaluate(ast->unary.operand);
+        case AST_TYPE_NEGATE:            return -parse_evaluate(ast->unary.operand);
+        case AST_TYPE_EXPRESSION_CAST:   return  parse_evaluate(ast->unary.operand);
+
+        /* Branches too */
+        case AST_TYPE_EXPRESSION_TERNARY:
+            cond = parse_evaluate(ast->ifstmt.cond);
+            if (cond)
+                return ast->ifstmt.then ? parse_evaluate(ast->ifstmt.cond) : cond;
+            return parse_evaluate(ast->ifstmt.last);
+
+        /* Deal with logical right shift specifically */
+        case AST_TYPE_LRSHIFT:
+            return ((unsigned long)parse_evaluate(ast->left)) >> parse_evaluate(ast->right);
+
+        case AST_TYPE_CONVERT:
+            return parse_evaluate(ast->unary.operand);
+
+        /*
+         * Allow constant evaluation for things like offsetof, although
+         * we could just implement __builtin_offsetof, this works easier
+         * and also allows the existing idiom to work.
+         */
+        case AST_TYPE_ADDRESS:
+            if (ast->unary.operand->type == AST_TYPE_STRUCT)
+                return parse_evaluate_offsetof(ast->unary.operand, 0);
+            goto error;
+
+        case AST_TYPE_DEREFERENCE:
+            if (ast->unary.operand->ctype->type == TYPE_POINTER)
+                return parse_evaluate(ast->unary.operand);
+            goto error;
+
+        default:
+        error:
+            compile_error("not a valid integer constant expression `%s'", ast_string(ast));
+    }
+    return -1;
+}
+
+bool parse_next(int ch) {
+    lexer_token_t *token = lexer_next();
+    if (lexer_ispunct(token, ch))
+        return true;
+    lexer_unget(token);
+    return false;
+}
+
+static list_t *parse_function_args(list_t *parameters) {
+    list_t          *list = list_create();
+    list_iterator_t *it   = list_iterator(parameters);
+    for (;;) {
+        if (parse_next(')'))
+            break;
+        ast_t       *arg  = parse_expression_assignment();
+        data_type_t *type = list_iterator_next(it);
+
+        if (!type) {
+            type = ast_type_isfloating(arg->ctype)
+                        ? ast_data_table[AST_DATA_DOUBLE]
+                        : ast_type_isinteger(arg->ctype)
+                            ? ast_data_table[AST_DATA_INT]
+                            : arg->ctype;
+        }
+
+        // todo semantic check
+        parse_semantic_assignable(ast_array_convert(type), ast_array_convert(arg->ctype));
+
+        if (type->type != arg->ctype->type)
+            arg = ast_type_convert(type, arg);
+
+        list_push(list, arg);
+
+        lexer_token_t *token = lexer_next();
+        if (lexer_ispunct(token, ')'))
+            break;
+        if (!lexer_ispunct(token, ','))
+            compile_error("unexpected token `%s'", lexer_token_string(token));
+    }
+    return list;
+}
+
+static ast_t *parse_va_start(void) {
+    ast_t *ap = parse_expression_assignment();
+    parse_expect(')');
+    return ast_va_start(ap);
+}
+
+static ast_t *parse_va_arg(void) {
+    ast_t *ap = parse_expression_assignment();
+    parse_expect(',');
+    data_type_t *type = parse_expression_cast_type();
+    parse_expect(')');
+    return ast_va_arg(type, ap);
+}
+
+static ast_t *parse_function_call(char *name, ast_t *function) {
+    if (!strcmp(name, "__builtin_va_start"))
+        return parse_va_start();
+    if (!strcmp(name, "__builtin_va_arg"))
+        return parse_va_arg();
+
+    if (function) {
+        data_type_t *declaration = function->ctype;
+        if (declaration->type != TYPE_FUNCTION)
+            compile_error("expected a function name, `%s' isn't a function", name);
+        list_t *list = parse_function_args(declaration->parameters);
+        return ast_call(declaration, name, list);
+    }
+    compile_warn("implicit declaration of function ‘%s’", name);
+    list_t *list = parse_function_args(&SENTINEL_LIST);
+    return ast_call(ast_prototype(ast_data_table[AST_DATA_INT], list_create(), true), name, list);
+}
+
+static ast_t *parse_function_pointer_call(ast_t *function) {
+    list_t *list = parse_function_args(function->ctype->pointer->parameters);
+    return ast_pointercall(function, list);
+}
+
+static ast_t *parse_generic(char *name) {
+    ast_t *ast = table_find(ast_localenv ? ast_localenv : ast_globalenv, name);
+
+    if (!ast || ast->ctype->type == TYPE_FUNCTION)
+        return ast_designator(name, ast);
+
+    return ast;
+}
+
+static ast_t *parse_number_integer(char *string) {
+    char *p    = string;
+    int   base = 10;
+
+    if (!strncasecmp(string, "0x", 2)) {
+        base = 16;
+        p++;
+        p++;
+    } else if (!strncasecmp(string, "0b", 2)){
+        if (!opt_extension_test(EXTENSION_BINARYCONSTANTS))
+            compile_error("binary constants only supported in -std=lice or -std=gnuc");
+        base = 2;
+        p++;
+        p++;
+    } else if (string[0] == '0' && string[1] != '\0') {
+        base = 8;
+        p++;
+    }
+
+    char *digits = p;
+    while (isxdigit(*p)) {
+        if (base == 10 && isalpha(*p))
+            compile_error("invalid character in decimal literal");
+        if (base == 8 && !('0' <= *p && *p <= '7'))
+            compile_error("invalid character in octal literal");
+        if (base == 2 && (*p != '0' && *p != '1'))
+            compile_error("invalid character in binary literal");
+        p++;
+    }
+
+    if (!strcasecmp(p, "l"))
+        return ast_new_integer(ast_data_table[AST_DATA_LONG], strtol(string, NULL, base));
+    if (!strcasecmp(p, "ul") || !strcasecmp(p, "lu") || !strcasecmp(p, "u"))
+        return ast_new_integer(ast_data_table[AST_DATA_ULONG], strtoul(string, NULL, base));
+    if (!strcasecmp(p, "ll"))
+        return ast_new_integer(ast_data_table[AST_DATA_LLONG], strtoll(string, NULL, base));
+    if (!strcasecmp(p, "ull") || !strcasecmp(p, "llu"))
+        return ast_new_integer(ast_data_table[AST_DATA_ULLONG], strtoull(string, NULL, base));
+    if (*p != '\0')
+        compile_error("invalid suffix for literal");
+
+    long value = strtol(digits, NULL, base);
+    return (value & ~(long)UINT_MAX)
+                ? ast_new_integer(ast_data_table[AST_DATA_LONG], value)
+                : ast_new_integer(ast_data_table[AST_DATA_INT], value);
+}
+
+static ast_t *parse_number_floating(char *string) {
+    char *p = string;
+    char *end;
+
+    while (p[1])
+        p++;
+
+    ast_t *ast;
+    if (*p == 'l' || *p == 'L')
+        ast = ast_new_floating(ast_data_table[AST_DATA_LDOUBLE], strtold(string, &end));
+    else if (*p == 'f' || *p == 'F')
+        ast = ast_new_floating(ast_data_table[AST_DATA_FLOAT], strtof(string, &end));
+    else {
+        ast = ast_new_floating(ast_data_table[AST_DATA_DOUBLE], strtod(string, &end));
+        p++;
+    }
+
+    if (end != p)
+        compile_error("malformatted float literal");
+
+    return ast;
+}
+
+static ast_t *parse_number(char *string) {
+    return strpbrk(string, ".pP") ||
+                /*
+                 * 0xe.. is integer type, sadly it's hard to check for that
+                 * without additonal logic.
+                 */
+                (strncasecmp(string, "0x", 2) && strpbrk(string, "eE"))
+                    ? parse_number_floating(string)
+                    : parse_number_integer(string);
+}
+
+static int parse_operation_reclassify(int punct) {
+    switch (punct) {
+        case LEXER_TOKEN_LSHIFT: return AST_TYPE_LSHIFT;
+        case LEXER_TOKEN_RSHIFT: return AST_TYPE_RSHIFT;
+        case LEXER_TOKEN_EQUAL:  return AST_TYPE_EQUAL;
+        case LEXER_TOKEN_GEQUAL: return AST_TYPE_GEQUAL;
+        case LEXER_TOKEN_LEQUAL: return AST_TYPE_LEQUAL;
+        case LEXER_TOKEN_NEQUAL: return AST_TYPE_NEQUAL;
+        case LEXER_TOKEN_AND:    return AST_TYPE_AND;
+        case LEXER_TOKEN_OR:     return AST_TYPE_OR;
+        default:
+            break;
+    }
+    return punct;
+}
+
+static int parse_operation_compound_operator(lexer_token_t *token) {
+    if (token->type != LEXER_TOKEN_PUNCT)
+        return 0;
+
+    switch (token->punct) {
+        case LEXER_TOKEN_COMPOUND_RSHIFT: return LEXER_TOKEN_RSHIFT;
+        case LEXER_TOKEN_COMPOUND_LSHIFT: return LEXER_TOKEN_LSHIFT;
+        case LEXER_TOKEN_COMPOUND_ADD:    return '+';
+        case LEXER_TOKEN_COMPOUND_AND:    return '&';
+        case LEXER_TOKEN_COMPOUND_DIV:    return '/';
+        case LEXER_TOKEN_COMPOUND_MOD:    return '%';
+        case LEXER_TOKEN_COMPOUND_MUL:    return '*';
+        case LEXER_TOKEN_COMPOUND_OR:     return '|';
+        case LEXER_TOKEN_COMPOUND_SUB:    return '-';
+        case LEXER_TOKEN_COMPOUND_XOR:    return '^';
+        default:
+            return 0;
+    }
+    return -1;
+}
+
+static ast_t *parse_expression_statement(void) {
+    ast_t *ast = parse_statement_compound();
+    parse_expect(')');
+    data_type_t *type = ast_data_table[AST_DATA_VOID];
+    if (list_length(ast->compound) > 0) {
+        ast_t *last = list_tail(ast->compound);
+        if (last)
+            type = last->ctype;
+    }
+    ast->ctype = type;
+    return ast;
+}
+
+static ast_t *parse_expression_primary(void) {
+    lexer_token_t *token;
+
+    if (!(token = lexer_next()))
+        return NULL;
+
+    if (lexer_ispunct(token, '(')) {
+
+        if (parse_next('{')) {
+            if (!opt_extension_test(EXTENSION_STATEMENTEXPRS))
+                compile_error("statement expressions only supported in -std=gnuc or -std=licec");
+            return parse_expression_statement();
+        }
+
+        ast_t *ast = parse_expression();
+        parse_expect(')');
+        return ast;
+    }
+
+    switch (token->type) {
+        case LEXER_TOKEN_IDENTIFIER:
+            return parse_generic(token->string);
+        case LEXER_TOKEN_NUMBER:
+            return parse_number(token->string);
+        case LEXER_TOKEN_CHAR:
+            return ast_new_integer(ast_data_table[AST_DATA_INT], token->character);
+        case LEXER_TOKEN_STRING:
+            return ast_new_string(token->string);
+        case LEXER_TOKEN_PUNCT:
+            lexer_unget(token);
+            return NULL;
+        default:
+            break;
+    }
+
+    compile_ice("parse_expression_primary");
+    return NULL;
+}
+
+static ast_t *parse_expression_subscript(ast_t *ast) {
+    ast_t *subscript = parse_expression();
+    if (!subscript)
+        compile_error("expected subscript operand");
+    parse_expect(']');
+    ast_t *node = conv_usual('+', ast, subscript);
+    return ast_new_unary(AST_TYPE_DEREFERENCE, node->ctype->pointer, node);
+}
+
+static data_type_t *parse_sizeof_operand(void) {
+    lexer_token_t *token = lexer_next();
+
+    if (lexer_ispunct(token, '(') && parse_type_check(lexer_peek())) {
+        data_type_t *next = parse_function_parameter(NULL, true);
+        parse_expect(')');
+        return next;
+    }
+
+    lexer_unget(token);
+    ast_t *expression = parse_expression_unary();
+
+    if (opt_std_test(STANDARD_GNUC) || opt_std_test(STANDARD_LICEC)) {
+        if (expression->type == AST_TYPE_DESIGNATOR)
+            return expression->function.call.functionpointer->ctype;
+        return expression->ctype;
+    } else {
+        if (expression->ctype->size == 0)
+            compile_error("invalid operand to sizeof operator");
+        return expression->ctype;
+    }
+
+    return NULL;
+}
+
+static ast_t *parse_sizeof(void) {
+    data_type_t *type = parse_sizeof_operand();
+    if (type->type == TYPE_VOID || type->type == TYPE_FUNCTION)
+        return ast_new_integer(ast_data_table[AST_DATA_LONG], 1);
+    return ast_new_integer(ast_data_table[AST_DATA_LONG], type->size);
+}
+
+static int parse_alignment(data_type_t *type) {
+    int size = type->type == TYPE_ARRAY ? type->pointer->size : type->size;
+    return MIN(size, ARCH_ALIGNMENT);
+}
+
+static ast_t *parse_alignof(void) {
+    parse_expect('(');
+    data_type_t *type = parse_function_parameter(NULL, true);
+    parse_expect(')');
+    return ast_new_integer(ast_data_table[AST_DATA_LONG], parse_alignment(type));
+}
+
+static ast_t *parse_structure_field(ast_t *structure) {
+    if (structure->ctype->type != TYPE_STRUCTURE)
+        compile_error("expected structure type, `%s' isn't structure type", ast_string(structure));
+    lexer_token_t *name = lexer_next();
+    if (name->type != LEXER_TOKEN_IDENTIFIER)
+        compile_error("expected field name, got `%s' instead", lexer_token_string(name));
+
+    data_type_t *field = table_find(structure->ctype->fields, name->string);
+    if (!field)
+        compile_error("structure has no such field `%s'", lexer_token_string(name));
+    return ast_structure_reference(field, structure, name->string);
+}
+
+static void parse_static_assert(void) {
+    parse_expect('(');
+    int eval = parse_expression_evaluate();
+    parse_expect(',');
+    lexer_token_t *token = lexer_next();
+    if (token->type != LEXER_TOKEN_STRING)
+        compile_error("expected string");
+    parse_expect(')');
+    parse_expect(';');
+    if (!eval)
+        compile_error("static assertion failed: %s", token->string);
+}
+
+static ast_t *parse_label_address(void) {
+    lexer_token_t *token = lexer_next();
+    if (token->type != LEXER_TOKEN_IDENTIFIER)
+        compile_error("expected identifier");
+    ast_t *address = ast_label_address(token->string);
+    list_push(ast_gotos, address);
+    return address;
+}
+
+static ast_t *parse_expression_postfix_tail(ast_t *ast) {
+    if (!ast)
+        return NULL;
+    for (;;) {
+        if (parse_next('(')) {
+            data_type_t *type = ast->ctype;
+            if (type->type == TYPE_POINTER && type->pointer->type == TYPE_FUNCTION)
+                return parse_function_pointer_call(ast);
+            if (ast->type != AST_TYPE_DESIGNATOR)
+                compile_error("expected function name");
+            ast = parse_function_call(ast->function.name, ast->function.call.functionpointer);
+            continue;
+        }
+        if (ast->type == AST_TYPE_DESIGNATOR && !ast->function.call.functionpointer)
+            compile_error("undefined variable: %s", ast->function.name);
+
+        if (parse_next('[')) {
+            ast = parse_expression_subscript(ast);
+            continue;
+        }
+
+        if (parse_next('.')) {
+            ast = parse_structure_field(ast);
+            continue;
+        }
+
+        if (parse_next(LEXER_TOKEN_ARROW)) {
+            if (ast->ctype->type != TYPE_POINTER)
+                compile_error("expected pointer type");
+            ast = ast_new_unary(AST_TYPE_DEREFERENCE, ast->ctype->pointer, ast);
+            ast = parse_structure_field(ast);
+            continue;
+        }
+
+        lexer_token_t *peek = lexer_peek();
+        if (parse_next(LEXER_TOKEN_INCREMENT) || parse_next(LEXER_TOKEN_DECREMENT)) {
+            parse_semantic_lvalue(ast);
+            int operation = lexer_ispunct(peek, LEXER_TOKEN_INCREMENT)
+                ?   AST_TYPE_POST_INCREMENT
+                :   AST_TYPE_POST_DECREMENT;
+
+            return ast_new_unary(operation, ast->ctype, ast);
+        }
+
+        return ast;
+    }
+
+    return NULL;
+}
+
+static ast_t *parse_expression_postfix(void) {
+    return parse_expression_postfix_tail(parse_expression_primary());
+}
+
+static ast_t *parse_expression_unary_address(void) {
+    ast_t *operand = parse_expression_cast();
+    if (operand->type == AST_TYPE_DESIGNATOR)
+        return ast_designator_convert(operand);
+    parse_semantic_lvalue(operand);
+    return ast_new_unary(AST_TYPE_ADDRESS, ast_pointer(operand->ctype), operand);
+}
+
+static ast_t *parse_expression_unary_deref(void) {
+    ast_t *operand = parse_expression_cast();
+    operand = ast_designator_convert(operand);
+    data_type_t *type = ast_array_convert(operand->ctype);
+    if (type->type != TYPE_POINTER)
+        compile_error("invalid type argument of unary ‘*’ (have ‘%s’)", ast_type_string(type));
+    if (type->pointer->type == TYPE_FUNCTION)
+        return operand;
+
+    return ast_new_unary(AST_TYPE_DEREFERENCE, operand->ctype->pointer, operand);
+}
+
+static ast_t *parse_expression_unary_plus(void) {
+    return parse_expression_cast();
+}
+
+static ast_t *parse_expression_unary_minus(void) {
+    ast_t *expression = parse_expression_cast();
+    // todo: semantic
+    return ast_new_unary(AST_TYPE_NEGATE, expression->ctype, expression);
+}
+
+static ast_t *parse_expression_unary_bitcomp(void) {
+    ast_t *expression = parse_expression_cast();
+    expression = ast_designator_convert(expression);
+    if (!ast_type_isinteger(expression->ctype))
+        compile_error("invalid argument type ‘%s‘ to bit-complement", ast_type_string(expression->ctype));
+
+    return ast_new_unary('~', expression->ctype, expression);
+}
+
+static ast_t *parse_expression_unary_not(void) {
+    ast_t *operand = parse_expression_cast();
+    operand = ast_designator_convert(operand);
+    return ast_new_unary('!', ast_data_table[AST_DATA_INT], operand);
+}
+
+static ast_t *parse_expression_unary(void) {
+    lexer_token_t *token = lexer_peek();
+    if (parse_identifer_check(token, "sizeof")) {
+        lexer_next();
+        return parse_sizeof();
+    }
+
+
+    if (parse_identifer_check(token, "__alignof__"))
+        goto alignof;
+
+    if (parse_identifer_check(token, "_Alignof")) {
+        if (!opt_std_test(STANDARD_C11) && !opt_std_test(STANDARD_LICEC))
+            compile_error("_Alignof not supported in this version of the standard, try -std=c11 or -std=licec");
+
+        alignof:
+        lexer_next();
+        return parse_alignof();
+    }
+
+    if (parse_next(LEXER_TOKEN_INCREMENT) || parse_next(LEXER_TOKEN_DECREMENT)) {
+        ast_t *operand = parse_expression_unary();
+        operand = ast_designator_convert(operand);
+        parse_semantic_lvalue(operand);
+        int operation = lexer_ispunct(token, LEXER_TOKEN_INCREMENT)
+                            ? AST_TYPE_PRE_INCREMENT
+                            : AST_TYPE_PRE_DECREMENT;
+
+        return ast_new_unary(operation, operand->ctype, operand);
+    }
+
+    /* &&label for computed goto */
+    if (parse_next(LEXER_TOKEN_AND))
+        return parse_label_address();
+
+    /* a more managable method for unary parsers */
+    static ast_t *(*const parsers['~'-'!'+1])(void) = {
+        [0]       = &parse_expression_unary_not,
+        ['&'-'!'] = &parse_expression_unary_address,
+        ['*'-'!'] = &parse_expression_unary_deref,
+        ['+'-'!'] = &parse_expression_unary_plus,
+        ['-'-'!'] = &parse_expression_unary_minus,
+        ['~'-'!'] = &parse_expression_unary_bitcomp
+    };
+
+    for (const char *test = "!&*+-~"; *test; test++)
+        if (parse_next(*test))
+            return parsers[*test-'!']();
+
+    return parse_expression_postfix();
+}
+
+ast_t *parse_expression_compound_literal(data_type_t *type) {
+    char   *name = ast_label();
+    list_t *init = init_entry(type);
+    ast_t  *ast  = ast_variable_local(type, name);
+    ast->variable.init = init;
+    return ast;
+}
+
+static data_type_t *parse_expression_cast_type(void) {
+    data_type_t *basetype = parse_declaration_specification(NULL);
+    return parse_declarator(NULL, basetype, NULL, CDECL_CAST);
+}
+
+static ast_t *parse_expression_cast(void) {
+    lexer_token_t *token = lexer_next();
+    if (lexer_ispunct(token, '(') && parse_type_check(lexer_peek())) {
+        data_type_t *type = parse_expression_cast_type();
+        parse_expect(')');
+        if (lexer_ispunct(lexer_peek(), '{')) {
+            ast_t *ast = parse_expression_compound_literal(type);
+            return parse_expression_postfix_tail(ast);
+        }
+        return ast_new_unary(AST_TYPE_EXPRESSION_CAST, type, parse_expression_cast());
+    }
+    lexer_unget(token);
+    return parse_expression_unary();
+}
+
+static ast_t *parse_expression_multiplicative(void) {
+    ast_t *ast = ast_designator_convert(parse_expression_cast());
+    for (;;) {
+             if (parse_next('*')) ast = conv_usual('*', ast, ast_designator_convert(parse_expression_cast()));
+        else if (parse_next('/')) ast = conv_usual('/', ast, ast_designator_convert(parse_expression_cast()));
+        else if (parse_next('%')) ast = conv_usual('%', ast, ast_designator_convert(parse_expression_cast()));
+        else break;
+    }
+    return ast;
+}
+
+static ast_t *parse_expression_additive(void) {
+    ast_t *ast = parse_expression_multiplicative();
+    for (;;) {
+             if (parse_next('+')) ast = conv_usual('+', ast, parse_expression_multiplicative());
+        else if (parse_next('-')) ast = conv_usual('-', ast, parse_expression_multiplicative());
+        else break;
+    }
+    return ast;
+}
+
+static ast_t *parse_expression_shift(void) {
+    ast_t *ast = parse_expression_additive();
+    for (;;) {
+        lexer_token_t *token = lexer_next();
+        int operation;
+        if (lexer_ispunct(token, LEXER_TOKEN_LSHIFT))
+            operation = AST_TYPE_LSHIFT;
+        else if (lexer_ispunct(token, LEXER_TOKEN_RSHIFT))
+            operation = ast->ctype->sign
+                            ? AST_TYPE_RSHIFT
+                            : AST_TYPE_LRSHIFT;
+        else {
+            lexer_unget(token);
+            break;
+        }
+        ast_t *right = parse_expression_additive();
+        // todo ensure integer
+        data_type_t *result = conv_senority(ast->ctype, right->ctype);
+        ast = ast_new_binary(result, operation, ast, right);
+    }
+    return ast;
+}
+
+static ast_t *parse_expression_relational(void) {
+    ast_t *ast = parse_expression_shift();
+    for (;;) {
+             if (parse_next('<'))                ast = conv_usual('<',             ast, parse_expression_shift());
+        else if (parse_next('>'))                ast = conv_usual('>',             ast, parse_expression_shift());
+        else if (parse_next(LEXER_TOKEN_LEQUAL)) ast = conv_usual(AST_TYPE_LEQUAL, ast, parse_expression_shift());
+        else if (parse_next(LEXER_TOKEN_GEQUAL)) ast = conv_usual(AST_TYPE_GEQUAL, ast, parse_expression_shift());
+        else break;
+    }
+    return ast;
+}
+
+static ast_t *parse_expression_equality(void) {
+    ast_t *ast = parse_expression_relational();
+    if (parse_next(LEXER_TOKEN_EQUAL))
+        return conv_usual(AST_TYPE_EQUAL, ast, parse_expression_equality());
+    if (parse_next(LEXER_TOKEN_NEQUAL))
+        return conv_usual(AST_TYPE_NEQUAL, ast, parse_expression_equality());
+    return ast;
+}
+
+static ast_t *parse_expression_bitand(void) {
+    ast_t *ast = parse_expression_equality();
+    while (parse_next('&'))
+        ast = conv_usual('&', ast, parse_expression_equality());
+    return ast;
+}
+
+static ast_t *parse_expression_bitxor(void) {
+    ast_t *ast = parse_expression_bitand();
+    while (parse_next('^'))
+        ast = conv_usual('^', ast, parse_expression_bitand());
+    return ast;
+}
+
+static ast_t *parse_expression_bitor(void) {
+    ast_t *ast = parse_expression_bitxor();
+    while (parse_next('|'))
+        ast = conv_usual('|', ast, parse_expression_bitxor());
+    return ast;
+}
+
+static ast_t *parse_expression_logand(void) {
+    ast_t *ast = parse_expression_bitor();
+    while (parse_next(LEXER_TOKEN_AND))
+        ast = ast_new_binary(ast_data_table[AST_DATA_INT], AST_TYPE_AND, ast, parse_expression_bitor());
+    return ast;
+}
+
+static ast_t *parse_expression_logor(void) {
+    ast_t *ast = parse_expression_logand();
+    while (parse_next(LEXER_TOKEN_OR))
+        ast = ast_new_binary(ast_data_table[AST_DATA_INT], AST_TYPE_OR, ast, parse_expression_logand());
+    return ast;
+}
+
+ast_t *parse_expression_assignment(void) {
+    ast_t         *ast   = parse_expression_logor();
+    lexer_token_t *token = lexer_next();
+
+    if (!token)
+        return ast;
+
+    if (lexer_ispunct(token, '?')) {
+        ast_t *then = parse_expression_comma();
+        parse_expect(':');
+        ast_t *last = parse_expression_conditional();
+        ast_t *operand = (opt_extension_test(EXTENSION_OMITOPCOND)) ? last : then;
+        return ast_ternary(operand->ctype, ast, then, last);
+    }
+
+    int compound   = parse_operation_compound_operator(token);
+    int reclassify = parse_operation_reclassify(compound);
+    if (lexer_ispunct(token, '=') || compound) {
+        ast_t *value = parse_expression_assignment();
+        if (lexer_ispunct(token, '=') || compound)
+            parse_semantic_lvalue(ast);
+
+        ast_t *right = compound ? conv_usual(reclassify, ast, value) : value;
+        if (conv_capable(right->ctype) && ast->ctype->type != right->ctype->type)
+            right = ast_type_convert(ast->ctype, right);
+        return ast_new_binary(ast->ctype, '=', ast, right);
+    }
+
+    lexer_unget(token);
+    return ast;
+}
+
+static ast_t *parse_expression_comma(void) {
+    ast_t *ast = parse_expression_assignment();
+    while (parse_next(',')) {
+        ast_t *expression = parse_expression_assignment();
+        ast = ast_new_binary(expression->ctype, ',', ast, expression);
+    }
+    return ast;
+}
+
+static ast_t *parse_expression(void) {
+    ast_t *ast = parse_expression_comma();
+    if (!ast)
+        compile_error("expression expected");
+    return ast;
+}
+
+static ast_t *parse_expression_optional(void) {
+    return parse_expression_comma();
+}
+
+static ast_t *parse_expression_condition(void) {
+    ast_t *cond = parse_expression();
+    if (ast_type_isfloating(cond->ctype))
+        return ast_type_convert(ast_data_table[TYPE_BOOL], cond);
+    return cond;
+}
+
+static ast_t *parse_expression_conditional(void) {
+    ast_t *ast = parse_expression_logor();
+    if (!parse_next('?'))
+        return ast;
+    ast_t *then = parse_expression_comma();
+    parse_expect(':');
+    ast_t *last = parse_expression_conditional();
+    return ast_ternary(last->ctype, ast, then, last);
+}
+
+int parse_expression_evaluate(void) {
+    return parse_evaluate(parse_expression_conditional());
+}
+
+static bool parse_type_check(lexer_token_t *token) {
+    if (token->type != LEXER_TOKEN_IDENTIFIER)
+        return false;
+
+    static const char *keywords[] = {
+        "char",     "short",  "int",      "long",     "float",    "double",
+        "struct",   "union",  "signed",   "unsigned", "enum",     "void",
+        "typedef",  "extern", "static",   "auto",     "register", "const",
+        "volatile", "inline", "restrict", "typeof",   "_Bool",
+
+        /*
+         * Ironically there is also another way to specific some keywords
+         * in C due to compilers being sneaky. Thus some code may use
+         * things like, __static__, or __typeof__, which are compilers
+         * reserved implementations of those keywords before the standard
+         * was ratified.
+         */
+        "__static__",
+        "__typeof__",
+    };
+
+    for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++)
+        if (!strcmp(keywords[i], token->string))
+            return true;
+
+    if (table_find(parse_typedefs, token->string))
+        return true;
+
+    return false;
+}
+
+/* struct / union */
+static char *parse_memory_tag(void) {
+    lexer_token_t *token = lexer_next();
+    if (token->type == LEXER_TOKEN_IDENTIFIER)
+        return token->string;
+    lexer_unget(token);
+    return NULL;
+}
+
+static int parse_memory_fields_padding(int offset, data_type_t *type) {
+    int size = type->type == TYPE_ARRAY ? type->pointer->size : type->size;
+    size = MIN(size, ARCH_ALIGNMENT);
+    return (offset % size == 0) ? 0 : size - offset % size;
+}
+
+static void parse_memory_fields_flexiblize(list_t *fields) {
+    list_iterator_t *it = list_iterator(fields);
+    while (!list_iterator_end(it)) {
+        pair_t      *pair = list_iterator_next(it);
+        char        *name = pair->first;
+        data_type_t *type = pair->second;
+
+        if (type->type != TYPE_ARRAY)
+            continue;
+
+        if (!opt_std_test(STANDARD_GNUC) && !opt_std_test(STANDARD_LICEC)) {
+            if (type->length == 0)
+                type->length = -1;
+        }
+
+        if (type->length == -1) {
+            if (!list_iterator_end(it))
+                compile_error("flexible field %s can only appear as the last field in a structure", name);
+            if (list_length(fields) == 1)
+                compile_error("flexible field %s as only field in structure is illegal", name);
+            type->size = 0;
+        }
+    }
+}
+
+static void parse_memory_fields_squash(table_t *table, data_type_t *unnamed, int offset) {
+    for (list_iterator_t *it = list_iterator(table_keys(unnamed->fields)); !list_iterator_end(it); ) {
+        char         *name = list_iterator_next(it);
+        data_type_t  *type = ast_type_copy(table_find(unnamed->fields, name));
+        type->offset += offset;
+        table_insert(table, name, type);
+    }
+}
+
+static int parse_bitfield_maybe(char *name, data_type_t *through) {
+    if (!parse_next(':'))
+        return -1;
+    if (!ast_type_isinteger(through))
+        compile_error("invalid type for bitfield size %s", ast_type_string(through));
+    int evaluate = parse_expression_evaluate();
+    if (evaluate < 0 || through->size * 8 < evaluate)
+        compile_error("invalid bitfield size %s: %d", ast_type_string(through), evaluate);
+    if (evaluate == 0 && name)
+        compile_error("zero sized bitfield cannot be named");
+    return evaluate;
+}
+
+static list_t *parse_memory_fields_intermediate(void) {
+    list_t *list = list_create();
+    for (;;) {
+
+        lexer_token_t *next = lexer_next();
+        if (parse_identifer_check(next, "_Static_assert")) {
+            if (!opt_std_test(STANDARD_C11) && !opt_std_test(STANDARD_LICEC))
+                compile_error("_Static_assert not supported in this version of the standard, try -std=c11 or -std=licec");
+
+            parse_static_assert();
+            continue;
+        }
+
+        lexer_unget(next);
+
+        if (!parse_type_check(lexer_peek()))
+            break;
+
+        data_type_t *basetype = parse_declaration_specification(NULL);
+
+        if (basetype->type == TYPE_STRUCTURE && parse_next(';')) {
+            list_push(list, pair_create(NULL, basetype));
+            continue;
+        }
+
+        for (;;) {
+            char        *name      = NULL;
+            data_type_t *fieldtype = parse_declarator(&name, basetype, NULL, CDECL_TYPEONLY);
+            parse_semantic_notvoid(fieldtype);
+
+            /* copy though for bitfield */
+            fieldtype                = ast_type_copy(fieldtype);
+            fieldtype->bitfield.size = parse_bitfield_maybe(name, fieldtype);
+
+            list_push(list, pair_create(name, fieldtype));
+
+            if (parse_next(','))
+                continue;
+            if (lexer_ispunct(lexer_peek(), '}'))
+                compile_warn("no semicolon at end of struct or union");
+            else
+                parse_expect(';');
+            break;
+        }
+    }
+    parse_expect('}');
+    return list;
+}
+
+static void parse_bitfield_update(int *offset, int *bitfield) {
+    *offset  += (*bitfield + 8) / 8;
+    *bitfield = -1;
+}
+
+static table_t *parse_struct_offset(list_t *fields, int *size) {
+    int              offset   = 0;
+    int              bitfield = -1;
+    list_iterator_t *it       = list_iterator(fields);
+    table_t         *table    = table_create(NULL);
+
+    while (!list_iterator_end(it)) {
+        pair_t      *pair = list_iterator_next(it);
+        char        *name = pair->first;
+        data_type_t *type = pair->second;
+
+        if (!name && type->type == TYPE_STRUCTURE) {
+            parse_bitfield_update(&offset, &bitfield);
+            parse_memory_fields_squash(table, type, offset);
+            offset += type->size;
+            continue;
+        }
+        if (type->bitfield.size == 0) {
+            parse_bitfield_update(&offset, &bitfield);
+            offset += parse_memory_fields_padding(offset, type);
+            bitfield = 0;
+            continue;
+        }
+        if (type->bitfield.size >= 0) {
+            /* bitfield space */
+            int space = type->size * 8 - bitfield;
+            if (0 <= bitfield && type->bitfield.size <= space) {
+                type->bitfield.offset = bitfield;
+                type->offset          = offset;
+            } else {
+                parse_bitfield_update(&offset, &bitfield);
+                offset               += parse_memory_fields_padding(offset, type);
+                type->offset          = offset;
+                type->bitfield.offset = 0;
+            }
+            bitfield = type->bitfield.size;
+        } else {
+            parse_bitfield_update(&offset, &bitfield);
+            offset += parse_memory_fields_padding(offset, type);
+            type->offset = offset;
+            offset += type->size;
+        }
+        if (name)
+            table_insert(table, name, type); /* no copy needed */
+    }
+    parse_bitfield_update(&offset, &bitfield); /* stage it */
+    *size = offset;
+    return table;
+}
+
+static table_t *parse_union_offset(list_t *fields, int *size) {
+    int              maxsize = 0;
+    list_iterator_t *it      = list_iterator(fields);
+    table_t         *table   = table_create(NULL);
+
+    while (!list_iterator_end(it)) {
+        pair_t      *pair = list_iterator_next(it);
+        char        *name = pair->first;
+        data_type_t *type = pair->second;
+
+        maxsize = MAX(maxsize, type->size);
+        if (!name && type->type == TYPE_STRUCTURE) {
+            parse_memory_fields_squash(table, type, 0);
+            continue;
+        }
+        type->offset = 0;
+        if (type->bitfield.size >= 0)
+            type->bitfield.offset = 0;
+        if (name)
+            table_insert(table, name, type);
+    }
+    *size = maxsize;
+    return table;
+}
+
+static table_t *parse_memory_fields(int *size, bool isstruct) {
+    if (!parse_next('{'))
+        return NULL;
+    list_t *fields = parse_memory_fields_intermediate();
+
+    /* make flexible if it can be made flexible */
+    parse_memory_fields_flexiblize(fields);
+
+    return (isstruct)
+                ? parse_struct_offset(fields, size)
+                : parse_union_offset(fields, size);
+}
+
+static data_type_t *parse_tag_definition(table_t *table, bool isstruct) {
+    char        *tag = parse_memory_tag();
+    data_type_t *r;
+
+    if (tag) {
+        if (!(r = table_find(table, tag))) {
+            r = ast_structure_new(NULL, 0, isstruct);
+            table_insert(table, tag, r);
+        }
+    } else {
+        r = ast_structure_new(NULL, 0, isstruct);
+    }
+
+    int      size   = 0;
+    table_t *fields = parse_memory_fields(&size, isstruct);
+
+    if (r && !fields)
+        return r;
+
+    if (r && fields) {
+        r->fields = fields;
+        r->size   = size;
+        return r;
+    }
+
+    return NULL;
+}
+
+/* enum */
+data_type_t *parse_enumeration(void) {
+    lexer_token_t *token = lexer_next();
+    if (token->type == LEXER_TOKEN_IDENTIFIER)
+        token = lexer_next();
+    if (!lexer_ispunct(token, '{')) {
+        lexer_unget(token);
+        return ast_data_table[AST_DATA_INT];
+    }
+    int accumulate = 0;
+    for (;;) {
+        token = lexer_next();
+        if (lexer_ispunct(token, '}'))
+            break;
+
+        if (token->type != LEXER_TOKEN_IDENTIFIER)
+            compile_error("expected identifier before ‘%s’ token", lexer_token_string(token));
+
+        char *name = token->string;
+        token = lexer_next();
+        if (lexer_ispunct(token, '='))
+            accumulate = parse_expression_evaluate();
+        else
+            lexer_unget(token);
+
+        ast_t *constval = ast_new_integer(ast_data_table[AST_DATA_INT], accumulate++);
+        table_insert(ast_localenv ? ast_localenv : ast_globalenv, name, constval);
+        token = lexer_next();
+        if (lexer_ispunct(token, ','))
+            continue;
+        if (lexer_ispunct(token, '}'))
+            break;
+
+        compile_ice("parse_enumeration");
+    }
+    return ast_data_table[AST_DATA_INT];
+}
+
+data_type_t *parse_typeof(void) {
+    parse_expect('(');
+    data_type_t *type = parse_type_check(lexer_peek())
+                            ? parse_expression_cast_type()
+                            : parse_expression_comma()->ctype;
+    parse_expect(')');
+    return type;
+}
+
+data_type_t *parse_structure(void) {
+    return parse_tag_definition(ast_structures, true);
+}
+
+data_type_t *parse_union(void) {
+    return parse_tag_definition(ast_unions, false);
+}
+
+data_type_t *parse_typedef_find(const char *key) {
+    return table_find(parse_typedefs, key);
+}
+
+/* declarator */
+static data_type_t *parse_declaration_specification(storage_t *rstorage) {
+    lexer_token_t *token   = lexer_peek();
+    if (!token || token->type != LEXER_TOKEN_IDENTIFIER)
+        compile_ice("declaration specification");
+
+    data_type_t *decl_spec(storage_t *);
+    return decl_spec(rstorage);
+}
+
+static data_type_t *parse_function_parameter(char **name, bool next) {
+    data_type_t *basetype;
+    storage_t    storage;
+
+    basetype = parse_declaration_specification(&storage);
+    return parse_declarator(name, basetype, NULL, next ? CDECL_TYPEONLY : CDECL_PARAMETER);
+}
+
+static ast_t *parse_statement_if(void) {
+    lexer_token_t *token;
+    ast_t  *cond;
+    ast_t *then;
+    ast_t *last;
+
+    parse_expect('(');
+    cond = parse_expression_condition();
+    parse_expect(')');
+
+
+    then  = parse_statement();
+    token = lexer_next();
+
+    if (!token || token->type != LEXER_TOKEN_IDENTIFIER || strcmp(token->string, "else")) {
+        lexer_unget(token);
+        return ast_if(cond, then, NULL);
+    }
+
+    last = parse_statement();
+    return ast_if(cond, then, last);
+}
+
+static ast_t *parse_statement_declaration_semicolon(void) {
+    lexer_token_t *token = lexer_next();
+    if (lexer_ispunct(token, ';'))
+        return NULL;
+    lexer_unget(token);
+    list_t *list = list_create();
+    parse_statement_declaration(list);
+    return list_shift(list);
+}
+
+static ast_t *parse_statement_for(void) {
+    parse_expect('(');
+    ast_localenv = table_create(ast_localenv);
+    ast_t *init = parse_statement_declaration_semicolon();
+    ast_t *cond = parse_expression_optional();
+    if (cond && ast_type_isfloating(cond->ctype))
+        cond = ast_type_convert(ast_data_table[AST_DATA_BOOL], cond);
+    parse_expect(';');
+    ast_t *step = parse_expression_optional();
+    parse_expect(')');
+    ast_t *body = parse_statement();
+    ast_localenv = table_parent(ast_localenv);
+    return ast_for(init, cond, step, body);
+}
+
+static ast_t *parse_statement_while(void) {
+    parse_expect('(');
+    ast_t *cond = parse_expression_condition();
+    parse_expect(')');
+    ast_t *body = parse_statement();
+    return ast_while(cond, body);
+}
+
+static ast_t *parse_statement_do(void) {
+    ast_t         *body  = parse_statement();
+    lexer_token_t *token = lexer_next();
+
+    if (!parse_identifer_check(token, "while"))
+        compile_error("expected ‘while’ before ‘%s’ token", lexer_token_string(token));
+
+    parse_expect('(');
+    ast_t *cond = parse_expression_condition();
+    parse_expect(')');
+    parse_expect(';');
+
+    return ast_do(cond, body);
+}
+
+static ast_t *parse_statement_break(void) {
+    parse_expect(';');
+    return ast_make(AST_TYPE_STATEMENT_BREAK);
+}
+
+static ast_t *parse_statement_continue(void) {
+    parse_expect(';');
+    return ast_make(AST_TYPE_STATEMENT_CONTINUE);
+}
+
+static ast_t *parse_statement_switch(void) {
+    parse_expect('(');
+    ast_t *expression = parse_expression();
+
+    if (!ast_type_isinteger(expression->ctype)) {
+        compile_error(
+            "switch statement requires expression of integer type (‘%s‘ invalid)",
+            ast_type_string(expression->ctype)
+        );
+    }
+
+    parse_expect(')');
+    ast_t *body = parse_statement();
+    return ast_switch(expression, body);
+}
+
+static ast_t *parse_statement_case(void) {
+    int begin = parse_expression_evaluate();
+    int end;
+    lexer_token_t *token = lexer_next();
+    if (parse_identifer_check(token, "..."))
+        end = parse_expression_evaluate();
+    else {
+        end = begin;
+        lexer_unget(token);
+    }
+    parse_expect(':');
+    if (begin > end)
+        compile_warn("empty case range specified");
+    return ast_case(begin, end);
+}
+
+static ast_t *parse_statement_default(void) {
+    parse_expect(':');
+    return ast_make(AST_TYPE_STATEMENT_DEFAULT);
+}
+
+static ast_t *parse_statement_return(void) {
+    ast_t *val = parse_expression_optional();
+    parse_expect(';');
+    if (val)
+        return ast_return(ast_type_convert(ast_data_table[AST_DATA_FUNCTION]->returntype, val));
+    return ast_return(NULL);
+}
+
+static ast_t *parse_statement_goto(void) {
+    /* deal with computed goto */
+    if (parse_next('*')) {
+        ast_t *expression = parse_expression_cast();
+        if (expression->ctype->type != TYPE_POINTER)
+            compile_error("expected pointer type for computed goto");
+        return ast_goto_computed(expression);
+    }
+
+    /* otherwise the traditional route */
+
+    lexer_token_t *token = lexer_next();
+    if (!token || token->type != LEXER_TOKEN_IDENTIFIER)
+        compile_error("expected label in goto statement");
+    parse_expect(';');
+
+    ast_t *node = ast_goto(token->string);
+    list_push(ast_gotos, node);
+
+    return node;
+}
+
+static void parse_label_backfill(void) {
+    for (list_iterator_t *it = list_iterator(ast_gotos); !list_iterator_end(it); ) {
+        ast_t *source      = list_iterator_next(it);
+        char  *label       = source->gotostmt.label;
+        ast_t *destination = table_find(ast_labels, label);
+
+        if (!destination)
+            compile_error("undefined label ‘%s‘", label);
+        if (destination->gotostmt.where)
+            source->gotostmt.where = destination->gotostmt.where;
+        else
+            source->gotostmt.where = destination->gotostmt.where = ast_label();
+    }
+}
+
+static ast_t *parse_label(lexer_token_t *token) {
+    parse_expect(':');
+    char  *label = token->string;
+    ast_t *node  = ast_new_label(label);
+
+    if (table_find(ast_labels, label))
+        compile_error("duplicate label ‘%s‘", label);
+    table_insert(ast_labels, label, node);
+
+    return node;
+}
+
+static ast_t *parse_statement(void) {
+    lexer_token_t *token = lexer_next();
+    ast_t         *ast;
+
+    if (lexer_ispunct        (token, '{'))        return parse_statement_compound();
+    if (parse_identifer_check(token, "if"))       return parse_statement_if();
+    if (parse_identifer_check(token, "for"))      return parse_statement_for();
+    if (parse_identifer_check(token, "while"))    return parse_statement_while();
+    if (parse_identifer_check(token, "do"))       return parse_statement_do();
+    if (parse_identifer_check(token, "return"))   return parse_statement_return();
+    if (parse_identifer_check(token, "switch"))   return parse_statement_switch();
+    if (parse_identifer_check(token, "case"))     return parse_statement_case();
+    if (parse_identifer_check(token, "default"))  return parse_statement_default();
+    if (parse_identifer_check(token, "break"))    return parse_statement_break();
+    if (parse_identifer_check(token, "continue")) return parse_statement_continue();
+    if (parse_identifer_check(token, "goto"))     return parse_statement_goto();
+
+    if (token->type == LEXER_TOKEN_IDENTIFIER && lexer_ispunct(lexer_peek(), ':'))
+        return parse_label(token);
+
+    lexer_unget(token);
+
+    ast = parse_expression_optional();
+    parse_expect(';');
+
+    return ast;
+}
+
+static void parse_statement_declaration(list_t *list){
+    lexer_token_t *token = lexer_peek();
+    if (!token)
+        compile_error("statement declaration with unexpected ending");
+    if (parse_type_check(token)) {
+        parse_declaration(list, ast_variable_local);
+    } else {
+        lexer_token_t *next = lexer_next();
+        if (parse_identifer_check(next, "_Static_assert"))
+            return parse_static_assert();
+        else
+            lexer_unget(next);
+        ast_t *statement = parse_statement();
+        if (statement)
+            list_push(list, statement);
+    }
+}
+
+static ast_t *parse_statement_compound(void) {
+    ast_localenv = table_create(ast_localenv);
+    list_t *statements = list_create();
+    for (;;) {
+        lexer_token_t *token = lexer_next();
+        if (lexer_ispunct(token, '}'))
+            break;
+
+        lexer_unget(token);
+        parse_statement_declaration(statements);
+    }
+    ast_localenv = table_parent(ast_localenv);
+    return ast_compound(statements);
+}
+
+static data_type_t *parse_function_parameters(list_t *paramvars, data_type_t *returntype) {
+    bool           typeonly   = !paramvars;
+    list_t        *paramtypes = list_create();
+    lexer_token_t *token      = lexer_next();
+    lexer_token_t *next       = lexer_next();
+
+    if (parse_identifer_check(token, "void") && lexer_ispunct(next, ')'))
+        return ast_prototype(returntype, paramtypes, false);
+    lexer_unget(next);
+    if (lexer_ispunct(token, ')'))
+        return ast_prototype(returntype, paramtypes, true);
+    lexer_unget(token);
+
+    for (;;) {
+        token = lexer_next();
+        if (parse_identifer_check(token, "...")) {
+            if (list_length(paramtypes) == 0)
+                compile_ice("parse_function_parameters");
+            parse_expect(')');
+            return ast_prototype(returntype, paramtypes, true);
+        } else {
+            lexer_unget(token);
+        }
+
+        char        *name;
+        data_type_t *ptype = parse_function_parameter(&name, typeonly);
+        parse_semantic_notvoid(ptype);
+
+        if (ptype->type == TYPE_ARRAY)
+            ptype = ast_pointer(ptype->pointer);
+        list_push(paramtypes, ptype);
+
+        if (!typeonly)
+            list_push(paramvars, ast_variable_local(ptype, name));
+
+        lexer_token_t *token = lexer_next();
+        if (lexer_ispunct(token, ')'))
+            return ast_prototype(returntype, paramtypes, false);
+
+        if (!lexer_ispunct(token, ','))
+            compile_ice("parse_function_parameters");
+    }
+}
+
+static ast_t *parse_function_definition(data_type_t *functype, char *name, list_t *parameters) {
+    ast_localenv                      = table_create(ast_localenv);
+    ast_locals                        = list_create();
+    ast_data_table[AST_DATA_FUNCTION] = functype;
+
+    table_insert(ast_localenv, "__func__", ast_new_string(name));
+
+    ast_t *body = parse_statement_compound();
+    ast_t *r    = ast_function(functype, name, parameters, body, ast_locals);
+
+    table_insert(ast_globalenv, name, r);
+
+    ast_data_table[AST_DATA_FUNCTION] = NULL;
+    ast_localenv                      = NULL;
+    ast_locals                        = NULL;
+
+    return r;
+}
+
+static bool parse_function_definition_check(void) {
+    list_t *buffer = list_create();
+    int     nests  = 0;
+    bool    paren  = false;
+    bool    ready  = true;
+
+    for (;;) {
+
+        lexer_token_t *token = lexer_next();
+        list_push(buffer, token);
+
+        if (!token)
+            compile_error("function definition with unexpected ending");
+
+        if (nests == 0 && paren && lexer_ispunct(token, '{'))
+            break;
+
+        if (nests == 0 && (lexer_ispunct(token, ';')
+                         ||lexer_ispunct(token, ',')
+                         ||lexer_ispunct(token, '=')))
+        {
+            ready = false;
+            break;
+        }
+
+        if (lexer_ispunct(token, '('))
+            nests++;
+
+        if (lexer_ispunct(token, ')')) {
+            if (nests == 0)
+                compile_error("unmatched parenthesis");
+            paren = true;
+            nests--;
+        }
+    }
+
+    while (list_length(buffer) > 0)
+        lexer_unget(list_pop(buffer));
+
+    return ready;
+}
+
+static ast_t *parse_function_definition_intermediate(void) {
+    char        *name;
+    storage_t    storage;
+    list_t      *parameters = list_create();
+    data_type_t *basetype   = ast_data_table[AST_DATA_INT];
+
+    if (parse_type_check(lexer_peek()))
+        basetype = parse_declaration_specification(&storage);
+    else
+        compile_warn("return type defaults to ’int’");
+
+    ast_localenv = table_create(ast_globalenv);
+    ast_labels   = table_create(NULL);
+    ast_gotos    = list_create();
+
+    data_type_t *functype = parse_declarator(&name, basetype, parameters, CDECL_BODY);
+    functype->isstatic = !!(storage == STORAGE_STATIC);
+    ast_variable_global(functype, name);
+    parse_expect('{');
+    ast_t *value = parse_function_definition(functype, name, parameters);
+
+    parse_label_backfill();
+
+    ast_localenv = NULL;
+    return value;
+}
+
+static data_type_t *parse_declarator_direct_restage(data_type_t *basetype, list_t *parameters) {
+    lexer_token_t *token = lexer_next();
+    if (lexer_ispunct(token, '[')) {
+        int length;
+        token = lexer_next();
+        if (lexer_ispunct(token, ']'))
+            length = -1;
+        else {
+            lexer_unget(token);
+            length = parse_expression_evaluate();
+            parse_expect(']');
+        }
+
+        data_type_t *type = parse_declarator_direct_restage(basetype, parameters);
+        if (type->type == TYPE_FUNCTION)
+            compile_error("array of functions");
+        return ast_array(type, length);
+    }
+    if (lexer_ispunct(token, '(')) {
+        if (basetype->type == TYPE_FUNCTION)
+            compile_error("function returning function");
+        if (basetype->type == TYPE_ARRAY)
+            compile_error("function returning array");
+        return parse_function_parameters(parameters, basetype);
+    }
+    lexer_unget(token);
+    return basetype;
+}
+
+static void parse_qualifiers_skip(void) {
+    for (;;) {
+        lexer_token_t *token = lexer_next();
+        if (parse_identifer_check(token, "const")
+         || parse_identifer_check(token, "volatile")
+         || parse_identifer_check(token, "restrict")) {
+            continue;
+        }
+        lexer_unget(token);
+        return;
+    }
+}
+
+static data_type_t *parse_declarator_direct(char **rname, data_type_t *basetype, list_t *parameters, cdecl_t context) {
+    lexer_token_t *token = lexer_next();
+    lexer_token_t *next  = lexer_peek();
+
+    if (lexer_ispunct(token, '(') && !parse_type_check(next) && !lexer_ispunct(next, ')')) {
+        data_type_t *stub = ast_type_stub();
+        data_type_t *type = parse_declarator_direct(rname, stub, parameters, context);
+        parse_expect(')');
+        *stub = *parse_declarator_direct_restage(basetype, parameters);
+        return type;
+    }
+
+    if (lexer_ispunct(token, '*')) {
+        parse_qualifiers_skip();
+        data_type_t *stub = ast_type_stub();
+        data_type_t *type = parse_declarator_direct(rname, stub, parameters, context);
+        *stub = *ast_pointer(basetype);
+        return type;
+    }
+
+    if (token->type == LEXER_TOKEN_IDENTIFIER) {
+        if (context == CDECL_CAST)
+            compile_error("wasn't expecting identifier `%s'", lexer_token_string(token));
+        *rname = token->string;
+        return parse_declarator_direct_restage(basetype, parameters);
+    }
+
+    if (context == CDECL_BODY || context == CDECL_PARAMETER)
+        compile_error("expected identifier, `(` or `*` for declarator");
+
+    lexer_unget(token);
+
+    return parse_declarator_direct_restage(basetype, parameters);
+}
+
+static void parse_array_fix(data_type_t *type) {
+    if (type->type == TYPE_ARRAY) {
+        parse_array_fix(type->pointer);
+        type->size = type->length * type->pointer->size;
+    } else if (type->type == TYPE_POINTER) {
+        parse_array_fix(type->pointer);
+    } else if (type->type == TYPE_FUNCTION) {
+        parse_array_fix(type->returntype);
+    }
+}
+
+static data_type_t *parse_declarator(char **rname, data_type_t *basetype, list_t *parameters, cdecl_t context) {
+    data_type_t *type = parse_declarator_direct(rname, basetype, parameters, context);
+    parse_array_fix(type);
+    return type;
+}
+
+static void parse_declaration(list_t *list, ast_t *(*make)(data_type_t *, char *)) {
+    storage_t      storage;
+    data_type_t   *basetype = parse_declaration_specification(&storage);
+    lexer_token_t *token    = lexer_next();
+
+    if (lexer_ispunct(token, ';'))
+        return;
+
+    lexer_unget(token);
+
+    for (;;) {
+        char        *name = NULL;
+        data_type_t *type = parse_declarator(&name, ast_type_copy_incomplete(basetype), NULL, CDECL_BODY);
+
+        if (storage == STORAGE_STATIC)
+            type->isstatic = true;
+
+        token = lexer_next();
+        if (lexer_ispunct(token, '=')) {
+            if (storage == STORAGE_TYPEDEF)
+                compile_error("invalid use of typedef");
+            parse_semantic_notvoid(type);
+            ast_t *var = make(type, name);
+            list_push(list, ast_declaration(var, init_entry(var->ctype)));
+            token = lexer_next();
+        } else if (storage == STORAGE_TYPEDEF) {
+            table_insert(parse_typedefs, name, type);
+        } else if (type->type == TYPE_FUNCTION) {
+            make(type, name);
+        } else {
+            ast_t *var = make(type, name);
+            if (storage != STORAGE_EXTERN)
+                list_push(list, ast_declaration(var, NULL));
+        }
+        if (lexer_ispunct(token, ';'))
+            return;
+        if (!lexer_ispunct(token, ','))
+            compile_ice("confused %X", token);
+    }
+}
+
+list_t *parse_run(void) {
+    list_t *list = list_create();
+    for (;;) {
+        if (!lexer_peek())
+            return list;
+        if (parse_function_definition_check())
+            list_push(list, parse_function_definition_intermediate());
+        else
+            parse_declaration(list, &ast_variable_global);
+    }
+    return NULL;
+}
+
+void parse_init(void) {
+    data_type_t *voidp = ast_pointer(ast_data_table[AST_DATA_VOID]);
+    data_type_t *type  = ast_prototype(voidp, list_create(), true);
+    data_type_t *addr  = ast_prototype(voidp, list_default(ast_data_table[AST_DATA_ULONG]), true);
+
+    table_insert(ast_globalenv, "__builtin_va_start",       ast_variable_global(type, "__builtin_va_start"));
+    table_insert(ast_globalenv, "__builtin_va_arg",         ast_variable_global(type, "__builtin_va_arg"));
+    table_insert(ast_globalenv, "__builtin_return_address", ast_variable_global(addr, "__buitlin_return_address"));
+}