--- /dev/null
+#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"));
+}