summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore14
-rw-r--r--Makefile20
-rw-r--r--README.txt76
-rw-r--r--chibicc.h472
-rw-r--r--codegen.c771
-rw-r--r--docs/BUILDING_COREUTILS_WITH_DIETCC.txt18
-rw-r--r--docs/CHIBICC_MODS.txt14
-rw-r--r--docs/DIETCC.txt19
-rw-r--r--docs/LANGUAGE.txt88
-rw-r--r--docs/LICENSE592
-rw-r--r--hashmap.c165
-rw-r--r--main.c72
-rw-r--r--parse.c3378
-rw-r--r--preprocess.c1151
-rw-r--r--scripts/dietc_helpers.h5
-rwxr-xr-xscripts/dietcc171
-rw-r--r--scripts/stdincludes/alloca.h4
-rw-r--r--scripts/stdincludes/dietc_defines.h24
-rw-r--r--scripts/stdincludes/regex.h698
-rw-r--r--scripts/stdincludes/stdarg.h46
-rw-r--r--scripts/stdincludes/stddef.h6
-rw-r--r--strings.c31
-rw-r--r--tests/array_of_structs.c23
-rw-r--r--tests/coreutils.c16
-rw-r--r--tests/debug.c7
-rw-r--r--tests/extern_array.c6
-rw-r--r--tests/fgets.c6
-rw-r--r--tests/ldbl.c9
-rw-r--r--tests/local_array.c4
-rw-r--r--tests/local_const_array.c4
-rw-r--r--tests/no_includes.c15
-rw-r--r--tests/ptr_arith.c4
-rw-r--r--tests/struct_return.c16
-rw-r--r--tests/super_simple.c5
-rw-r--r--tests/test.c21
-rw-r--r--tests/union_init.c10
-rw-r--r--tests/unnamed_fields.c13
-rw-r--r--tests/unsized_extern_array.c4
-rw-r--r--tests/va_start.c21
-rw-r--r--tokenize.c805
-rw-r--r--type.c321
-rw-r--r--type_helpers.c78
-rw-r--r--typegen.c304
-rw-r--r--unicode.c189
44 files changed, 9716 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1853630
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,14 @@
+**/*~
+**/\#*
+**/*.o
+**/*.s
+**/a.out
+/tmp*
+/thirdparty
+/chibicc
+/test/*.exe
+/stage2
+build
+chibicc
+.*.sw*
+dietc
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..79e825b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,20 @@
+CFLAGS=-std=c11 -g -fno-common -Wall -Wno-switch
+CFLAGS+=-DDIETC_ROOT="\"$(PWD)\""
+# CFLAGS=$(CFLAGS) -fsanitize=address
+
+SRCS=$(wildcard *.c)
+OBJS=$(addprefix build/,$(SRCS:.c=.o))
+
+dietc: $(OBJS)
+ $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
+
+build/%.o: %.c chibicc.h
+ @mkdir -p build
+ $(CC) $(CFLAGS) -c $(word 1,$^) -o $@
+
+# Misc.
+
+clean:
+ rm -rf dietc build
+
+.PHONY: clean
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..ca59161
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,76 @@
+# dietC: a C backend for chibicc
+
+What: desugar C code
+
+Why: easier program analysis and transformation
+
+How: fork of Rui Ueyama's great chibicc compiler, replacing the x86 assembly
+backend with a C backend
+
+Quick Example:
+
+ $ cat tests/super_simple.c
+ int main(void) {
+ int x = 1;
+ int y = 2;
+ return y;
+ }
+ $ make
+ ...
+ $ ./dietc tests/super_simple.c
+ #include "/home/matthew/repos/dietc/scripts/dietc_helpers.h"
+ typedef int Type_1 ;
+ ...
+ extern Type_5 main ;
+ ...
+ Type_1 main ( ) {
+ Type_1 t1 ;
+ ...
+ t3 = 1 ;
+ * t5 = t3 ;
+ ...
+ return t1 ;
+ }
+
+Selling points:
+
+ - Guaranteed space-separated tokens
+ - Extremely restricted grammar, super easy to parse; see docs/LANGUAGE.txt
+ - Maintains all type information
+ - Comes with a wrapper to replace GCC in existing build scripts; see
+ docs/DIETCC.txt
+
+Known to be unsupported:
+
+- dietC is ... optimistic about identifier conflicts with its generated
+ labels. It likely will not work if you run it on its own output. This is very
+ fixable, will do soon.
+- chibicc does not have good support for long double; specifically, long
+ doubles cannot be used in constant expressions. This is fixable, but requires
+ non-trivial modifications to the chibicc side of the code.
+- chibicc does not parse const, volatile, etc. type modifiers. it would not be
+ too hard to support this if desired.
+
+Why not cilly?
+
+ - cilly performs both not enough and too much lowering.
+ - it often compiles a struct field acces a->foo into a literal offset like
+ *(a + 16).
+ - even after simplification it has multiple control flow constructs: loop,
+ etc.
+ - it's written in OCAML, and hard to parse the result outside of OCAML
+
+Why not C--, assembly, QBE?
+
+ - all these are strictly lower level than cilly. They preserve almost no type
+ information at all.
+
+Why not LLVM?
+
+ - LLVM has breaking changes approximately every 5ns and preservation of
+ high-level type information is not guaranteed.
+ - mostly forced to use LLVM's C++ interfaces and then compile with LLVM
+
+License:
+ - the underlying chibicc code is licensed under the MIT license
+ - my modifications are licensed under the AGPLv3
diff --git a/chibicc.h b/chibicc.h
new file mode 100644
index 0000000..397c4fb
--- /dev/null
+++ b/chibicc.h
@@ -0,0 +1,472 @@
+#define _POSIX_C_SOURCE 200809L
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <glob.h>
+#include <libgen.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdnoreturn.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#define MAX(x, y) ((x) < (y) ? (y) : (x))
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+
+#ifndef __GNUC__
+# define __attribute__(x)
+#endif
+
+typedef struct Type Type;
+typedef struct Node Node;
+typedef struct Member Member;
+typedef struct Relocation Relocation;
+typedef struct Hideset Hideset;
+
+//
+// strings.c
+//
+
+typedef struct {
+ char **data;
+ int capacity;
+ int len;
+} StringArray;
+
+void strarray_push(StringArray *arr, char *s);
+char *format(char *fmt, ...) __attribute__((format(printf, 1, 2)));
+
+//
+// tokenize.c
+//
+
+// Token
+typedef enum {
+ TK_IDENT, // Identifiers
+ TK_PUNCT, // Punctuators
+ TK_KEYWORD, // Keywords
+ TK_STR, // String literals
+ TK_NUM, // Numeric literals
+ TK_PP_NUM, // Preprocessing numbers
+ TK_EOF, // End-of-file markers
+} TokenKind;
+
+typedef struct {
+ char *name;
+ int file_no;
+ char *contents;
+
+ // For #line directive
+ char *display_name;
+ int line_delta;
+} File;
+
+// Token type
+typedef struct Token Token;
+struct Token {
+ TokenKind kind; // Token kind
+ Token *next; // Next token
+ int64_t val; // If kind is TK_NUM, its value
+ long double fval; // If kind is TK_NUM, its value
+ char *loc; // Token location
+ int len; // Token length
+ Type *ty; // Used if TK_NUM or TK_STR
+ char *str; // String literal contents including terminating '\0'
+
+ File *file; // Source location
+ char *filename; // Filename
+ int line_no; // Line number
+ int line_delta; // Line number
+ bool at_bol; // True if this token is at beginning of line
+ bool has_space; // True if this token follows a space character
+ Hideset *hideset; // For macro expansion
+ Token *origin; // If this is expanded from a macro, the original token
+};
+
+noreturn void error(char *fmt, ...) __attribute__((format(printf, 1, 2)));
+noreturn void error_at(char *loc, char *fmt, ...) __attribute__((format(printf, 2, 3)));
+noreturn void error_tok(Token *tok, char *fmt, ...) __attribute__((format(printf, 2, 3)));
+void warn_tok(Token *tok, char *fmt, ...) __attribute__((format(printf, 2, 3)));
+bool equal(Token *tok, char *op);
+Token *skip(Token *tok, char *op);
+bool consume(Token **rest, Token *tok, char *str);
+void convert_pp_tokens(Token *tok);
+File **get_input_files(void);
+File *new_file(char *name, int file_no, char *contents);
+Token *tokenize_string_literal(Token *tok, Type *basety);
+Token *tokenize(File *file);
+Token *tokenize_file(char *filename);
+
+#define unreachable() \
+ error("internal error at %s:%d", __FILE__, __LINE__)
+
+//
+// parse.c
+//
+
+// Variable or function
+typedef struct Obj Obj;
+struct Obj {
+ Obj *next;
+ char *name; // Variable name
+ Type *ty; // Type
+ Token *tok; // representative token
+ bool is_local; // local or global/function
+ int align; // alignment
+
+ // Local variable
+ int offset;
+
+ // Global variable or function
+ bool is_function;
+ bool is_definition;
+ bool is_static;
+
+ // Global variable
+ bool is_tentative;
+ bool is_tls;
+ char *init_data;
+ Relocation *rel;
+
+ // Function
+ bool is_inline;
+ Obj *params;
+ Node *body;
+ Obj *locals;
+ Obj *va_area;
+ Obj *alloca_bottom;
+ int stack_size;
+
+ // Static inline function
+ bool is_live;
+ bool is_root;
+ StringArray refs;
+
+ // helper
+ bool is_param;
+};
+
+// Global variable can be initialized either by a constant expression
+// or a pointer to another global variable. This struct represents the
+// latter.
+typedef struct Relocation Relocation;
+struct Relocation {
+ Relocation *next;
+ int offset;
+ char **label;
+ long addend;
+};
+
+// AST node
+typedef enum {
+ ND_NULL_EXPR, // Do nothing
+ ND_ADD, // +
+ ND_SUB, // -
+ ND_MUL, // *
+ ND_DIV, // /
+ ND_NEG, // unary -
+ ND_MOD, // %
+ ND_BITAND, // &
+ ND_BITOR, // |
+ ND_BITXOR, // ^
+ ND_SHL, // <<
+ ND_SHR, // >>
+ ND_EQ, // ==
+ ND_NE, // !=
+ ND_LT, // <
+ ND_LE, // <=
+ ND_ASSIGN, // =
+ ND_COND, // ?:
+ ND_COMMA, // ,
+ ND_MEMBER, // . (struct member access)
+ ND_ADDR, // unary &
+ ND_DEREF, // unary *
+ ND_NOT, // !
+ ND_BITNOT, // ~
+ ND_LOGAND, // &&
+ ND_LOGOR, // ||
+ ND_RETURN, // "return"
+ ND_IF, // "if"
+ ND_FOR, // "for" or "while"
+ ND_DO, // "do"
+ ND_SWITCH, // "switch"
+ ND_CASE, // "case"
+ ND_BLOCK, // { ... }
+ ND_GOTO, // "goto"
+ ND_GOTO_EXPR, // "goto" labels-as-values
+ ND_LABEL, // Labeled statement
+ ND_LABEL_VAL, // [GNU] Labels-as-values
+ ND_FUNCALL, // Function call
+ ND_EXPR_STMT, // Expression statement
+ ND_STMT_EXPR, // Statement expression
+ ND_VAR, // Variable
+ ND_VLA_PTR, // VLA designator
+ ND_NUM, // Integer
+ ND_CAST, // Type cast
+ ND_MEMZERO, // Zero-clear a stack variable
+ ND_ASM, // "asm"
+ ND_CAS, // Atomic compare-and-swap
+ ND_EXCH, // Atomic exchange
+} NodeKind;
+
+// AST node type
+struct Node {
+ NodeKind kind; // Node kind
+ Node *next; // Next node
+ Type *ty; // Type, e.g. int or pointer to int
+ Token *tok; // Representative token
+
+ Node *lhs; // Left-hand side
+ Node *rhs; // Right-hand side
+
+ // "if" or "for" statement
+ Node *cond;
+ Node *then;
+ Node *els;
+ Node *init;
+ Node *inc;
+
+ // "break" and "continue" labels
+ char *brk_label;
+ char *cont_label;
+
+ // Block or statement expression
+ Node *body;
+
+ // Struct member access
+ Member *member;
+
+ // Function call
+ Type *func_ty;
+ Node *args;
+ bool pass_by_stack;
+ Obj *ret_buffer;
+
+ // Goto or labeled statement, or labels-as-values
+ char *label;
+ char *unique_label;
+ Node *goto_next;
+
+ // Switch
+ Node *case_next;
+ Node *default_case;
+
+ // Case
+ long begin;
+ long end;
+
+ // "asm" string literal
+ char *asm_str;
+
+ // Atomic compare-and-swap
+ Node *cas_addr;
+ Node *cas_old;
+ Node *cas_new;
+
+ // Atomic op= operators
+ Obj *atomic_addr;
+ Node *atomic_expr;
+
+ // Variable
+ Obj *var;
+
+ // Numeric literal
+ int64_t val;
+ long double fval;
+};
+
+Node *new_cast(Node *expr, Type *ty);
+int64_t const_expr(Token **rest, Token *tok);
+Obj *parse(Token *tok);
+
+//
+// type.c
+//
+
+typedef enum {
+ TY_VOID,
+ TY_BOOL,
+ TY_CHAR,
+ TY_SHORT,
+ TY_INT,
+ TY_LONG,
+ TY_FLOAT,
+ TY_DOUBLE,
+ TY_LDOUBLE,
+ TY_ENUM,
+ TY_PTR,
+ TY_FUNC,
+ TY_ARRAY,
+ TY_VLA, // variable-length array
+ TY_STRUCT,
+ TY_UNION,
+} TypeKind;
+
+struct Type {
+ TypeKind kind;
+ int size; // sizeof() value
+ int align; // alignment
+ bool is_unsigned; // unsigned or signed
+ bool is_atomic; // true if _Atomic
+ Type *origin; // for type compatibility check
+
+ // Pointer-to or array-of type. We intentionally use the same member
+ // to represent pointer/array duality in C.
+ //
+ // In many contexts in which a pointer is expected, we examine this
+ // member instead of "kind" member to determine whether a type is a
+ // pointer or not. That means in many contexts "array of T" is
+ // naturally handled as if it were "pointer to T", as required by
+ // the C spec.
+ Type *base;
+
+ // Declaration
+ Token *name;
+ Token *name_pos;
+
+ // Array
+ int array_len;
+
+ // Variable-length array
+ Node *vla_len; // # of elements
+ Obj *vla_size; // sizeof() value
+
+ // Struct
+ Member *members;
+ bool is_flexible;
+ bool is_packed;
+
+ // Function type
+ Type *return_ty;
+ Type *params;
+ bool is_variadic;
+ Type *next;
+
+ // ID for printing
+ int id;
+ int hashing;
+ int typedecling;
+ Type *pointer_type;
+};
+
+// Struct member
+struct Member {
+ Member *next;
+ Type *ty;
+ Token *tok; // for error message
+ Token *name;
+ int idx;
+ int align;
+ int offset;
+
+ // Bitfield
+ bool is_bitfield;
+ int bit_offset;
+ int bit_width;
+};
+
+extern Type *ty_void;
+extern Type *ty_bool;
+
+extern Type *ty_char;
+extern Type *ty_short;
+extern Type *ty_int;
+extern Type *ty_long;
+
+extern Type *ty_uchar;
+extern Type *ty_ushort;
+extern Type *ty_uint;
+extern Type *ty_ulong;
+
+extern Type *ty_float;
+extern Type *ty_double;
+extern Type *ty_ldouble;
+
+bool is_integer(Type *ty);
+bool is_flonum(Type *ty);
+bool is_numeric(Type *ty);
+bool is_compatible(Type *t1, Type *t2);
+Type *copy_type(Type *ty);
+Type *pointer_to(Type *base);
+Type *func_type(Type *return_ty);
+Type *array_of(Type *base, int size);
+Type *vla_of(Type *base, Node *expr);
+Type *enum_type(void);
+Type *struct_type(void);
+void add_type(Node *node);
+
+//
+// codegen.c
+//
+
+void codegen(Obj *prog, FILE *out);
+int align_to(int n, int align);
+
+//
+// typegen.c
+//
+
+void typegen(Obj *prog, FILE *out);
+
+//
+// type_helpers.c
+//
+
+int definitely_same_type(Type *type1, Type *type2);
+unsigned long hash_type(Type *type);
+
+//
+// unicode.c
+//
+
+int encode_utf8(char *buf, uint32_t c);
+uint32_t decode_utf8(char **new_pos, char *p);
+bool is_ident1(uint32_t c);
+bool is_ident2(uint32_t c);
+int display_width(char *p, int len);
+
+//
+// hashmap.c
+//
+
+typedef struct {
+ char *key;
+ int keylen;
+ void *val;
+} HashEntry;
+
+typedef struct {
+ HashEntry *buckets;
+ int capacity;
+ int used;
+} HashMap;
+
+void *hashmap_get(HashMap *map, char *key);
+void *hashmap_get2(HashMap *map, char *key, int keylen);
+void hashmap_put(HashMap *map, char *key, void *val);
+void hashmap_put2(HashMap *map, char *key, int keylen, void *val);
+void hashmap_delete(HashMap *map, char *key);
+void hashmap_delete2(HashMap *map, char *key, int keylen);
+void hashmap_test(void);
+
+//
+// main.c
+//
+
+extern bool opt_fpic;
+extern bool opt_fcommon;
+extern char *base_file;
+
+//
+// preprocess.c
+//
+
+Token *preprocess(Token *tok);
diff --git a/codegen.c b/codegen.c
new file mode 100644
index 0000000..9eaac4a
--- /dev/null
+++ b/codegen.c
@@ -0,0 +1,771 @@
+#include "chibicc.h"
+#include <unistd.h>
+
+#define GP_MAX 6
+#define FP_MAX 8
+
+int is_bad_number(double x) {
+ return (x != x) || (x == 1./0.) || (-x == 1./0.);
+}
+
+static FILE *output_file;
+static int RETURN_TMP;
+
+static void printnoln(char *fmt, ...);
+static void println(char *fmt, ...);
+static void print_tok(Token *tok);
+
+FILE *BUFFER;
+int DO_BUFFER;
+
+static void flush_buffer() {
+ rewind(BUFFER);
+ int c;
+ while ((c = fgetc(BUFFER)) != EOF)
+ fputc(c, output_file);
+ rewind(BUFFER);
+ ftruncate(fileno(BUFFER), 0);
+}
+
+void print_label(char *label) {
+ printnoln("_l");
+ for (; *label; label++) {
+ if (*label == '.') printnoln("_");
+ else printnoln("%c", *label);
+ }
+}
+
+static void print_tok(Token *tok) {
+ if (tok->str) printnoln("%s", tok->str);
+ else {
+ assert(tok->loc);
+ for (int i = 0; i < tok->len; i++)
+ printnoln("%c", tok->loc[i]);
+ }
+}
+
+void print_obj(Obj *obj) {
+ assert(obj->offset >= 0);
+ if (obj->is_local || strchr(obj->name, '.')) {
+ printnoln("_");
+ for (char *c = obj->name; *c; c++)
+ if (*c == '.') printnoln("_");
+ else printnoln("%c", *c);
+ printnoln("_%d", obj->offset);
+ } else {
+ printnoln("%s", obj->name);
+ }
+}
+
+const void print_type(Type *type) {
+ if (type->kind == TY_FUNC) {
+ assert(type->pointer_type);
+ print_type(type->pointer_type);
+ } else {
+ assert(type->id);
+ printnoln("Type_%d", type->id);
+ }
+}
+static int depth;
+static Obj *current_fn;
+
+static void gen_expr(Node *node, int to_tmp);
+static void gen_stmt(Node *node);
+
+__attribute__((format(printf, 1, 2)))
+static void println(char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ if (DO_BUFFER)
+ vfprintf(BUFFER, fmt, ap);
+ else
+ vfprintf(output_file, fmt, ap);
+ va_end(ap);
+ if (DO_BUFFER)
+ fprintf(BUFFER, "\n");
+ else
+ fprintf(output_file, "\n");
+}
+
+__attribute__((format(printf, 1, 2)))
+static void printnoln(char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ if (DO_BUFFER)
+ vfprintf(BUFFER, fmt, ap);
+ else
+ vfprintf(output_file, fmt, ap);
+ va_end(ap);
+}
+
+static int count(void) {
+ static int i = 1;
+ return i++;
+}
+
+// Round up `n` to the nearest multiple of `align`. For instance,
+// align_to(5, 8) returns 8 and align_to(11, 8) returns 16.
+int align_to(int n, int align) {
+ return (n + align - 1) / align * align;
+}
+
+void decltmp(Type *type, int c) {
+ DO_BUFFER = 0;
+ printnoln("\t");
+ print_type(type);
+ println(" t%d ;", c);
+ DO_BUFFER = 1;
+}
+
+void decltmpptr(Type *type, int c) {
+ assert(type->pointer_type);
+ decltmp(type->pointer_type, c);
+}
+
+// Compute the absolute address of a given node.
+// It's an error if a given node does not reside in memory.
+static void gen_addr(Node *node, int to_tmp) {
+ switch (node->kind) {
+ case ND_VAR:
+ decltmpptr(node->ty, to_tmp);
+ printnoln("\tt%d = & ", to_tmp);
+ print_obj(node->var);
+ println(" ;");
+ return;
+ case ND_DEREF:
+ gen_expr(node->lhs, to_tmp);
+ return;
+ case ND_COMMA:
+ gen_expr(node->lhs, count());
+ gen_addr(node->rhs, to_tmp);
+ return;
+ case ND_MEMBER: {
+ int s = count();
+ gen_addr(node->lhs, s);
+ decltmpptr(node->ty, to_tmp);
+ printnoln("\tt%d = FIELDPTR ( t%d, ", to_tmp, s);
+ if (node->member->name)
+ print_tok(node->member->name);
+ else
+ printnoln("___dietc_f%d", node->member->idx);
+ println(" ) ;");
+ return;
+ }
+ case ND_ASSIGN:
+ case ND_COND:
+ if (node->ty->kind == TY_STRUCT || node->ty->kind == TY_UNION) {
+ gen_expr(node, to_tmp);
+ return;
+ }
+ break;
+ case ND_VLA_PTR: assert(0);
+ }
+
+ error_tok(node->tok, "not an lvalue");
+}
+
+// Generate code for a given node.
+static void gen_expr(Node *node, int to_tmp) {
+ // println(" .loc %d %d", node->tok->file->file_no, node->tok->line_no);
+
+ switch (node->kind) {
+ case ND_NULL_EXPR:
+ return;
+ case ND_NUM: {
+ decltmp(node->ty, to_tmp);
+ switch (node->ty->kind) {
+ case TY_FLOAT:
+ case TY_DOUBLE:
+ case TY_LDOUBLE:
+ println("\tt%d = %Lf ;", to_tmp, node->fval);
+ return;
+ default:
+ println("\tt%d = %ld ;", to_tmp, node->val);
+ return;
+ }
+ }
+ case ND_NEG: {
+ int c = count();
+ gen_expr(node->lhs, c);
+ decltmp(node->ty, to_tmp);
+ println("\tt%d = - t%d ;", to_tmp, c);
+ return;
+ }
+ case ND_VAR: {
+ // we inline the *getaddr(node)
+ if (node->ty->kind == TY_ARRAY) {
+ decltmpptr(node->ty->base, to_tmp);
+ printnoln("\tt%d = ", to_tmp);
+ print_obj(node->var);
+ println(" ;");
+ } else {
+ decltmp(node->ty, to_tmp);
+ printnoln("\tt%d = ", to_tmp);
+ print_obj(node->var);
+ println(" ;");
+ }
+ return;
+ }
+ case ND_MEMBER: {
+ int c = count();
+ gen_addr(node, c);
+ // TODO: what to do in this case?
+ if (node->ty->kind == TY_ARRAY) {
+ decltmpptr(node->ty->base, to_tmp);
+ println("\tt%d = * t%d ;", to_tmp, c);
+ } else {
+ decltmp(node->ty, to_tmp);
+ println("\tt%d = * t%d ;", to_tmp, c);
+ }
+
+ if (node->member->is_bitfield)
+ fprintf(stderr, "WARNING: bitfields ignored");
+ return;
+ }
+ case ND_DEREF: {
+ int c = count();
+ gen_expr(node->lhs, c);
+ // TODO: deref a pointer to an array; is the temporary a pointer, or an
+ // array??
+ if (node->ty->kind == TY_ARRAY) {
+ decltmpptr(node->ty->base, to_tmp);
+ println("\tt%d = * t%d ;", to_tmp, c);
+ } else {
+ decltmp(node->ty, to_tmp);
+ println("\tt%d = * t%d ;", to_tmp, c);
+ }
+ return;
+ }
+ case ND_ADDR:
+ gen_addr(node->lhs, to_tmp);
+ return;
+ case ND_ASSIGN: {
+ int lhsa = count();
+ gen_addr(node->lhs, lhsa);
+ gen_expr(node->rhs, to_tmp);
+
+ if (node->lhs->kind == ND_MEMBER && node->lhs->member->is_bitfield)
+ fprintf(stderr, "WARNING: bitfields ignored");
+
+ println("\t* t%d = t%d ;", lhsa, to_tmp);
+ return;
+ }
+ case ND_STMT_EXPR:
+ for (Node *n = node->body; n; n = n->next) {
+ if (n->next || n->kind != ND_EXPR_STMT)
+ gen_stmt(n);
+ else
+ gen_expr(n->lhs, to_tmp);
+ }
+ return;
+ case ND_COMMA:
+ gen_expr(node->lhs, count());
+ gen_expr(node->rhs, to_tmp);
+ return;
+ case ND_CAST: {
+ if (definitely_same_type(node->lhs->ty, node->ty))
+ return gen_expr(node->lhs, to_tmp);
+ int c = count();
+ if (node->ty->kind == TY_VOID) {
+ gen_expr(node->lhs, c);
+ println("\t( Type_%d ) t%d ;", node->ty->id, c);
+ } else if (node->lhs->ty->kind == TY_UNION) {
+ // union *tuptr = &union
+ int uptr = count();
+ gen_addr(node->lhs, uptr);
+ // T * tc = ( T * ) tuptr
+ decltmpptr(node->ty, c);
+ println("\tt%d = ( Type_%d ) t%d ;",
+ c, node->ty->pointer_type->id, uptr);
+ // dereference it
+ decltmp(node->ty, to_tmp);
+ println("\tt%d = * t%d ;", to_tmp, c);
+ } else {
+ gen_expr(node->lhs, c);
+ decltmp(node->ty, to_tmp);
+ println("\tt%d = ( Type_%d ) t%d ;", to_tmp, node->ty->id, c);
+ }
+ return;
+ }
+ case ND_MEMZERO:
+ printnoln("\tMEMZERO ( "); print_obj(node->var); println(" ) ;");
+ return;
+ case ND_COND: {
+ int c = count(), cond = count(), condfalse = count(),
+ tmp1 = count(), tmp2 = count();
+ gen_expr(node->cond, cond);
+ decltmp(ty_int, condfalse);
+ if (node->ty->kind != TY_VOID)
+ decltmp(node->ty, to_tmp);
+ println("\tt%d = ! t%d ;", condfalse, cond);
+ println("\tif ( t%d ) goto _L_else_%d ;", condfalse, c);
+ gen_expr(node->then, tmp1);
+ if (node->ty->kind != TY_VOID)
+ println("\tt%d = t%d ;", to_tmp, tmp1);
+ println("\tgoto _L_end_%d ;", c);
+ println("\t_L_else_%d :", c);
+ gen_expr(node->els, tmp2);
+ if (node->ty->kind != TY_VOID)
+ println("\tt%d = t%d ;", to_tmp, tmp2);
+ println("\t_L_end_%d :", c);
+ return;
+ }
+ case ND_NOT: {
+ int c = count();
+ gen_expr(node->lhs, c);
+ decltmp(ty_int, to_tmp);
+ println("\tt%d = ! t%d ;", to_tmp, c);
+ return;
+ }
+ case ND_BITNOT: {
+ int c = count();
+ gen_expr(node->lhs, c);
+ decltmp(node->ty, to_tmp);
+ println("\tt%d = ~ t%d ;", to_tmp, c);
+ return;
+ }
+ case ND_LOGAND: {
+ int c = count(), lhs = count(), lhsfalse = count(),
+ rhs = count(), rhsfalse = count();
+ decltmp(ty_int, to_tmp);
+ decltmp(ty_int, lhsfalse);
+ decltmp(ty_int, rhsfalse);
+ gen_expr(node->lhs, lhs);
+ println("\tt%d = 0 ;", to_tmp);
+ println("\tt%d = ! t%d ;", lhsfalse, lhs);
+ println("\tif ( t%d ) goto _L_false_%d ;", lhsfalse, c);
+ gen_expr(node->rhs, rhs);
+ println("\tt%d = ! t%d ;", rhsfalse, rhs);
+ println("\tif ( t%d ) goto _L_false_%d ;", rhsfalse, c);
+ println("\tt%d = 1 ;", to_tmp);
+ println("\tgoto _L_end_%d;", c);
+ println("\t_L_false_%d :", c);
+ println("\t_L_end_%d :", c);
+ return;
+ }
+ case ND_LOGOR: {
+ int c = count(), lhs = count(), rhs = count();
+ decltmp(ty_int, to_tmp);
+ println("\tt%d = 0 ;", to_tmp);
+ gen_expr(node->lhs, lhs);
+ println("\tif ( t%d ) goto _L_true_%d ;", lhs, c);
+ gen_expr(node->rhs, rhs);
+ println("\tif ( t%d ) goto _L_true_%d ;", rhs, c);
+ println("\tgoto _L_end_%d ;", c);
+ println("\t_L_true_%d :", c);
+ println("\tt%d = 1 ;", to_tmp);
+ println("\t_L_end_%d :", c);
+ return;
+ }
+ case ND_FUNCALL: {
+ int fnc = count();
+ gen_expr(node->lhs, fnc);
+
+ int n_args = 0;
+ for (Node *arg = node->args; arg; arg = arg->next) n_args++;
+
+ int *args = calloc(n_args, sizeof(int)),
+ arg_i = 0;
+ for (Node *arg = node->args; arg; arg = arg->next, arg_i++) {
+ args[arg_i] = count();
+ gen_expr(arg, args[arg_i]);
+ }
+ if (node->ty->kind != TY_VOID) {
+ decltmp(node->ty, to_tmp);
+ printnoln("\tt%d = ", to_tmp);
+ } else {
+ printnoln("\t");
+ }
+ printnoln("t%d ( ", fnc);
+ for (int i = 0; i < n_args; i++) {
+ if (i) printnoln(", ");
+ printnoln("t%d ", args[i]);
+ }
+ println(") ;");
+ return;
+ }
+ case ND_LABEL_VAL: assert(0);
+ case ND_CAS: assert(0);
+ case ND_EXCH: assert(0);
+ }
+
+ int rhsc = count(), lhsc = count();
+ gen_expr(node->rhs, rhsc);
+ gen_expr(node->lhs, lhsc);
+
+ char *op = NULL;
+ switch (node->kind) {
+ case ND_ADD: op = "+"; break;
+ case ND_SUB: op = "-"; break;
+ case ND_MUL: op = "*"; break;
+ case ND_DIV: op = "/"; break;
+ case ND_MOD: op = "%"; break;
+ case ND_BITAND: op = "&"; break;
+ case ND_BITOR: op = "|"; break;
+ case ND_BITXOR: op = "^"; break;
+ case ND_EQ: op = "=="; break;
+ case ND_NE: op = "!="; break;
+ case ND_LT: op = "<"; break;
+ case ND_LE: op = "<="; break;
+ case ND_SHL: op = "<<"; break;
+ case ND_SHR: op = ">>"; break;
+ }
+
+ decltmp(node->ty, to_tmp);
+ if (node->ty->base) {
+ println("\tt%d = PTR_BINARY ( t%d , %s , t%d ) ;", to_tmp, lhsc, op, rhsc);
+ } else {
+ println("\tt%d = BINARY ( t%d , %s , t%d ) ;", to_tmp, lhsc, op, rhsc);
+ }
+}
+
+static void gen_stmt(Node *node) {
+ switch (node->kind) {
+ case ND_IF: {
+ int cond = count(), condfalse = count(), c = count();
+ gen_expr(node->cond, cond);
+ decltmp(ty_int, condfalse);
+ println("\tt%d = ! t%d ;", condfalse, cond);
+ println("\tif ( t%d ) goto _L_else_%d ;", condfalse, c);
+ gen_stmt(node->then);
+ println("\tgoto _L_end_%d ;", c);
+ println("\t_L_else_%d :", c);
+ if (node->els)
+ gen_stmt(node->els);
+ println("\t_L_end_%d :", c);
+ return;
+ }
+ case ND_FOR: {
+ int c = count(), cond = count(), condfalse = count();
+ if (node->init)
+ gen_stmt(node->init);
+ println("\t_L_begin_%d :", c);
+ if (node->cond) {
+ gen_expr(node->cond, cond);
+ decltmp(ty_int, condfalse);
+ println("\tt%d = ! t%d ;", condfalse, cond);
+ printnoln("\tif ( t%d ) goto ", condfalse);
+ print_label(node->brk_label);
+ println(" ;");
+ }
+ gen_stmt(node->then);
+ printnoln("\t"); print_label(node->cont_label); println(" :");
+ if (node->inc)
+ gen_expr(node->inc, count());
+ println("\tgoto _L_begin_%d ;", c);
+ printnoln("\t"); print_label(node->brk_label); println(" :");
+ return;
+ }
+ case ND_DO: {
+ int c = count(), cond = count();
+ println("\t_L_begin_%d :", c);
+ gen_stmt(node->then);
+ printnoln("\t"); print_label(node->cont_label); println(" :");
+ gen_expr(node->cond, cond);
+ println("\tif ( t%d ) goto _L_begin_%d ;", cond, c);
+ printnoln("\t"); print_label(node->brk_label); println(" :");
+ return;
+ }
+ case ND_SWITCH: {
+ int cond = count(), eqtmp = count();
+ gen_expr(node->cond, cond);
+ decltmp(ty_int, eqtmp);
+
+ for (Node *n = node->case_next; n; n = n->case_next) {
+ if (n->begin == n->end) {
+ println("\tt%d = BINARY ( t%d , == , %ld ) ;", eqtmp, cond, n->begin);
+ printnoln("\tif ( t%d ) goto ", eqtmp);
+ print_label(n->label); println(" ;");
+ continue;
+ }
+
+ // [GNU] Case ranges
+ assert(!"unimplemented");
+ }
+
+ if (node->default_case) {
+ printnoln("\tgoto "); print_label(node->default_case->label); println(" ;");
+ }
+
+ printnoln("\tgoto "); print_label(node->brk_label); println(" ;");
+ gen_stmt(node->then);
+ printnoln("\t"); print_label(node->brk_label); println(" :");
+ return;
+ }
+ case ND_CASE:
+ printnoln("\t"); print_label(node->label); println(" :");
+ gen_stmt(node->lhs);
+ return;
+ case ND_BLOCK:
+ for (Node *n = node->body; n; n = n->next)
+ gen_stmt(n);
+ return;
+ case ND_GOTO:
+ printnoln("\tgoto "); print_label(node->unique_label); println(" ;");
+ return;
+ case ND_GOTO_EXPR: assert(0); return;
+ case ND_LABEL:
+ printnoln("\t"); print_label(node->unique_label); println(" :");
+ gen_stmt(node->lhs);
+ return;
+ case ND_RETURN:
+ if (node->lhs) {
+ int expr = count();
+ gen_expr(node->lhs, expr);
+ println("\tt%d = t%d ;", RETURN_TMP, expr);
+ }
+ println("\tgoto _L_RETURN ;");
+ return;
+ case ND_EXPR_STMT:
+ gen_expr(node->lhs, count());
+ return;
+ case ND_ASM: assert(0); return;
+ }
+
+ error_tok(node->tok, "invalid statement");
+}
+
+// Assign offsets to local variables.
+static void assign_lvar_offsets(Obj *prog) {
+ for (Obj *fn = prog; fn; fn = fn->next) {
+ if (!fn->is_function)
+ continue;
+
+ // If a function has many parameters, some parameters are
+ // inevitably passed by stack rather than by register.
+ // The first passed-by-stack parameter resides at RBP+16.
+ int c = 1;
+
+ for (Obj *var = fn->params; var; var = var->next)
+ var->offset = c++;
+ for (Obj *var = fn->locals; var; var = var->next)
+ if (!(var->offset))
+ var->offset = c++;
+ }
+}
+
+void emit_constant(int pos, char *data, Relocation **rel, Type *type) {
+ switch (type->kind) {
+ case TY_STRUCT: {
+ int i = 0;
+ printnoln("{ ");
+ for (struct Member *m = type->members; m; m = m->next, i++) {
+ if (i) printnoln(", ");
+ assert(!m->is_bitfield);
+ emit_constant(pos + m->offset, data, rel, m->ty);
+ }
+ printnoln("} ");
+ break;
+ }
+ case TY_UNION: {
+ struct Member *biggest = type->members;
+ for (struct Member *m = type->members; m; m = m->next)
+ if (m->ty->size > biggest->ty->size) biggest = m;
+ assert(biggest);
+ printnoln("{ ");
+ emit_constant(pos, data, rel, biggest->ty);
+ printnoln("} ");
+ break;
+ }
+ case TY_ARRAY: {
+ printnoln("{ ");
+ for (int i = 0; i < type->array_len; i++) {
+ if (i) printnoln(", ");
+ emit_constant(pos, data, rel, type->base);
+ pos += type->base->size;
+ }
+ printnoln("} ");
+ break;
+ }
+ case TY_CHAR:
+ assert(type->size == 1);
+ // we don't output ' ' so lexing can be done by splitting on whitespace
+ if (data[pos] > ' ' && data[pos] <= '~' && data[pos] != '\'' && data[pos] != '\\')
+ printnoln("'%c' ", data[pos++]);
+ else
+ printnoln("%d ", (int)data[pos++]);
+ break;
+ case TY_BOOL:
+ assert(type->size == 1);
+ printnoln("%d ", *((char*)(data+pos)));
+ break;
+ case TY_SHORT:
+ assert(type->size == sizeof(short));
+ printnoln("%d ", *((short*)(data+pos)));
+ break;
+ case TY_INT:
+ assert(type->size == sizeof(int));
+ printnoln("%d ", *((int*)(data+pos)));
+ break;
+ case TY_LONG:
+ assert(type->size == sizeof(long));
+ printnoln("%ld ", *((long*)(data+pos)));
+ break;
+ case TY_FLOAT: {
+ assert(type->size == sizeof(float));
+ float v = *((float*)(data + pos));
+ if (v != v) printnoln("0./0. ");
+ else if (v == 1./0.) printnoln("1./0. ");
+ else if (-v == 1./0.) printnoln("-1./0. ");
+ else printnoln("%f ", v);
+ break;
+ } case TY_DOUBLE: {
+ assert(type->size == sizeof(double));
+ double v = *((double*)(data + pos));
+ if (v != v) printnoln("0./0. ");
+ else if (v == 1./0.) printnoln("1./0. ");
+ else if (-v == 1./0.) printnoln("-1./0. ");
+ else printnoln("%lf ", v);
+ break;
+ } case TY_LDOUBLE: {
+ assert(type->size == sizeof(long double));
+ long double v = *((long double*)(data + pos));
+ if (v != v) printnoln("0./0. ");
+ else if (v == 1./0.) printnoln("1./0. ");
+ else if (-v == 1./0.) printnoln("-1./0. ");
+ else printnoln("%Lf ", v);
+ break;
+ } case TY_PTR:
+ assert(type->size == 8);
+ if (*rel && (*rel)->offset == pos) {
+ // TODO: should addend be divided by (*rel)->ty->size?
+ printnoln("( void * ) ( & ");
+ if (strchr(*((*rel)->label), '.')) {
+ printnoln("_");
+ for (char *c = *((*rel)->label); *c; c++) {
+ if (*c == '.') printnoln("_");
+ else printnoln("%c", *c);
+ }
+ printnoln("_0 ) + %ld ", (*rel)->addend);
+ } else {
+ printnoln("%s ) + %ld ", *((*rel)->label), (*rel)->addend);
+ }
+ *rel = (*rel)->next;
+ } else {
+ // TODO: actually read the absolute address out
+ printnoln("( void * ) ( %ld ) + 0 ", *((uint64_t*)(data+pos)));
+ }
+ break;
+ case TY_ENUM:
+ switch (type->size) {
+ case sizeof(int):
+ printnoln("%d ", *((int*)(data+pos)));
+ break;
+ case sizeof(long):
+ printnoln("%ld ", *((long*)(data+pos)));
+ break;
+ default: assert(0);
+ }
+ break;
+ default: fprintf(stderr, "Got: %d\n", type->kind); assert(0);
+ }
+}
+
+static void emit_data(Obj *prog) {
+ for (Obj *var = prog; var; var = var->next) {
+ if (var->is_static && var->is_function) {
+ printnoln("static Type_%d ", var->ty->id);
+ print_obj(var); println(" ;");
+ } else if (var->is_static) {
+ printnoln("static Type_%d ", var->ty->id);
+ print_obj(var); println(" ;");
+ } else {
+ printnoln("extern Type_%d ", var->ty->id);
+ print_obj(var); println(" ;");
+ }
+ }
+
+ for (Obj *var = prog; var; var = var->next) {
+ if (var->is_function || !var->is_definition)
+ continue;
+
+ if (var->is_static) printnoln("static ");
+ print_type(var->ty); printnoln(" "); print_obj(var);
+
+ // Common symbol
+ assert(!(opt_fcommon && var->is_tentative));
+
+ // .data or .tdata
+ if (var->init_data) {
+ printnoln(" = ");
+ Relocation *rel = var->rel;
+ emit_constant(0, var->init_data, &rel, var->ty);
+ }
+ println(";");
+ }
+}
+
+static void emit_text(Obj *prog) {
+ for (Obj *fn = prog; fn; fn = fn->next) {
+ if (!fn->is_function || !fn->is_definition)
+ continue;
+
+ // No code is emitted for "static inline" functions
+ // if no one is referencing them.
+ if (!fn->is_live)
+ continue;
+
+ current_fn = fn;
+
+ if (fn->is_static)
+ printnoln("static ");
+
+ print_type(fn->ty->return_ty); printnoln(" ");
+ printnoln("%s ( ", fn->name);
+ int p = 0;
+ for (Obj *param = fn->params; param; param = param->next, p++) {
+ if (p) printnoln(", ");
+ print_type(param->ty); printnoln(" "); print_obj(param);
+ param->is_param = 1;
+ printnoln(" ");
+ }
+ if (fn->va_area) {
+ if (p) printnoln(", ... ");
+ // else printnoln("... ");
+ }
+ println(") {");
+
+ RETURN_TMP = 0;
+ if (fn->ty->return_ty->kind != TY_VOID) {
+ RETURN_TMP = count();
+ decltmp(fn->ty->return_ty, RETURN_TMP);
+ }
+
+ for (Obj *var = fn->locals; var; var = var->next) {
+ if (!(var->ty->id)) continue;
+ if (var->is_param) continue;
+ if (var == fn->va_area) continue;
+ if (var == fn->alloca_bottom) continue;
+ printnoln("\t");
+ print_type(var->ty);
+ printnoln(" ");
+ print_obj(var);
+ println(" ;");
+ }
+
+ // Emit code
+ DO_BUFFER = 1;
+ gen_stmt(fn->body);
+ assert(depth == 0);
+ flush_buffer();
+ DO_BUFFER = 0;
+
+ println("\t_L_RETURN :");
+ if (fn->ty->return_ty->kind != TY_VOID) {
+ println("\treturn t%d ;", RETURN_TMP);
+ } else {
+ println("\treturn ;");
+ }
+
+ println("}");
+ }
+}
+
+void codegen(Obj *prog, FILE *out) {
+ output_file = out;
+
+ BUFFER = tmpfile();
+ assign_lvar_offsets(prog);
+ emit_data(prog);
+ emit_text(prog);
+}
diff --git a/docs/BUILDING_COREUTILS_WITH_DIETCC.txt b/docs/BUILDING_COREUTILS_WITH_DIETCC.txt
new file mode 100644
index 0000000..79fa2ea
--- /dev/null
+++ b/docs/BUILDING_COREUTILS_WITH_DIETCC.txt
@@ -0,0 +1,18 @@
+As a test for dietc, we've ensured that we can build a mostly-working version
+of GNU coreutils. Note that this has only been checked on GCC-on-Debian; likely
+won't work on other platforms.
+
+$ cd /dietc/root/directory
+$ make
+$ cd /tmp/
+$ curl -OL https://ftp.gnu.org/gnu/coreutils/coreutils-9.3.tar.xz
+$ tar -xvf coreutils-9.3.tar.xz
+$ cd coreutils-9.3
+$ vim lib/config.hin
+ rewrite "# define _GL_INLINE inline"
+ to "# define _GL_INLINE extern inline"
+$ vim lib/malloc/scratch_buffer.h
+ add "#include <lib/scratch_buffer.h>"
+$ CC=/home/matthew/repos/dietc/scripts/dietcc ./configure
+...
+$ make -j8
diff --git a/docs/CHIBICC_MODS.txt b/docs/CHIBICC_MODS.txt
new file mode 100644
index 0000000..ab733ba
--- /dev/null
+++ b/docs/CHIBICC_MODS.txt
@@ -0,0 +1,14 @@
+we're attempting to stay as close to mainline chibicc as possible
+
+at a high level, the major changes made are:
+- strip out most of proprocess.c, as we're OK with using GCC's preprocessor
+ (still need preprocess.c, as it handles parsing of literals)
+- add a typegen.c, which runs before codegen & outputs the typedefs
+- replace codegen.c to output C instead of assembly
+
+to support these, ended up making some changes to the rest of the files. these
+include:
+ - in type.c:usual_arith_conv,
+ - don't cast if they're already the right type
+ - for pointer arithmetic, leave it as pointer + long
+ - in parse.c:is_const_expr, also allow modulos to be const
diff --git a/docs/DIETCC.txt b/docs/DIETCC.txt
new file mode 100644
index 0000000..2b3762f
--- /dev/null
+++ b/docs/DIETCC.txt
@@ -0,0 +1,19 @@
+The basic idea is to replace
+
+ gcc ... file.c ...
+
+with
+
+ gcc -E ... file.c ... -o preproc.c
+ dietc preproc.c > diet.c
+ pass1 diet.c > diet.c
+ pass2 diet.c > diet.c
+ ...
+ gcc ... diet.c ...
+
+To assist with this, we provide `scripts/dietcc`, which provides a drop-in
+replacement for `gcc` that can run your patches. In your build scripts, simply
+replace `gcc` with the path of `dietcc`.
+
+You can insert your own compilation pass into dietcc; read its Python source
+for examples.
diff --git a/docs/LANGUAGE.txt b/docs/LANGUAGE.txt
new file mode 100644
index 0000000..124e100
--- /dev/null
+++ b/docs/LANGUAGE.txt
@@ -0,0 +1,88 @@
+# Full language reference for dietC
+
+(Some of these may be a bit out-of-date; let me know if you find something
+inaccurate!)
+
+Literals:
+ ints & floats as output by printf
+ chars, but no escaping (special characters are encoded as ints)
+
+File layout:
+ #include "/path/to/dietc/scripts/dietc_helpers.h"
+ [type defining lines]
+ [global declarations & definitions]
+
+Type defining lines:
+ struct Struct_%d { Type_%d [ident] ; Type_%d [ident] ; [...] } ;
+ union Union_%d { Type_%d [ident] ; Type_%d [ident] ; [...] } ;
+ typedef basic_type Type_%d ;
+ typedef Type_%d * Type_%d ;
+ typedef Type_%d Type_%d [ %d ] ;
+ typedef struct Struct_%d Type_%d ;
+ typedef union Union_%d Type_%d ;
+ typedef Type_%d Type_%d ( Type_%d , Type_%d , [...] ) ;
+ typedef Type_%d Type_%d ( Type_%d , Type_%d , ... ) ;
+
+global declarations & definitions:
+ extern Type_%d [ident] ;
+ static Type_%d [ident] = [globlit] ;
+ Type_%d [ident] = [globlit] ;
+ Type_%d [ident] ( Type_%d , [...] ) {\n[body]\n}
+ Type_%d [ident] ( Type_%d , ... ) {\n[body]\n}
+
+globlit:
+ [literal]
+ { [globlit] , [...] }
+ ( void * ) ( & [ident] ) + [literal]
+ ( void * ) [int literal]
+
+body:
+ [declaration]
+ [declaration]
+ [...]
+ [instruction]
+ [instruction]
+ [...]
+ [return]
+
+return:
+ _L_RETURN :
+ return [ident] ;
+ or
+ _L_RETURN :
+ return ;
+
+declaration:
+ Type_%d [ident] ;
+
+instruction:
+ MEMZERO ( [ident] ) ;
+ MEMCPY ( [ident] , [ident] ) ;
+ [ident] :
+ if ( [ident] ) goto [ident] ;
+ goto [ident] ;
+ [ident] = [ident] ;
+ [ident] = [literal] ;
+ * [ident] = [ident] ;
+ [ident] = [preop] [ident] ;
+ [ident] = ( Type_%d ) [ident] ;
+ ( Type_%d ) [ident] ;
+ // ^ only when (void)x; left in for autoconf
+ [ident] = BINARY ( [ident] , [binop] , [ident] ) ;
+ [ident] = [ident] ( [ident] , [...] ) ;
+ [ident] ( [ident] , [...] ) ;
+ [ident] = FIELDPTR ( [ident] , [fname] ) ;
+
+preop:
+ !, ~, *, &
+
+binop:
+ +, -, *, /, %, &, |, ^, ==, !=, <, <=, <<, >>
+
+Other guarantees:
+ instructions in function bodies, and *only* those instructions, are indented
+ with one tab character
+ all tokens are space-separated; you can tokenize by splitting each line on
+ space characters
+ all instructions are one-line
+ MEMZERO and MEMCPY are always called on globals & locals; never the heap
diff --git a/docs/LICENSE b/docs/LICENSE
new file mode 100644
index 0000000..ea94cc9
--- /dev/null
+++ b/docs/LICENSE
@@ -0,0 +1,592 @@
+license for chibicc:
+
+MIT License
+
+Copyright (c) 2019 Rui Ueyama
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+license for the dietC modifications to chibicc:
+
+GNU AFFERO GENERAL PUBLIC LICENSE
+
+Version 3, 19 November 2007
+
+Copyright © 2007 Free Software Foundation, Inc. <https://fsf.org/> Everyone is
+permitted to copy and distribute verbatim copies of this license document, but
+changing it is not allowed. Preamble
+
+The GNU Affero General Public License is a free, copyleft license for software
+and other kinds of works, specifically designed to ensure cooperation with the
+community in the case of network server software.
+
+The licenses for most software and other practical works are designed to take
+away your freedom to share and change the works. By contrast, our General
+Public Licenses are intended to guarantee your freedom to share and change all
+versions of a program--to make sure it remains free software for all its users.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom to
+distribute copies of free software (and charge for them if you wish), that you
+receive source code or can get it if you want it, that you can change the
+software or use pieces of it in new free programs, and that you know you can do
+these things.
+
+Developers that use our General Public Licenses protect your rights with two
+steps: (1) assert copyright on the software, and (2) offer you this License
+which gives you legal permission to copy, distribute and/or modify the
+software.
+
+A secondary benefit of defending all users' freedom is that improvements made
+in alternate versions of the program, if they receive widespread use, become
+available for other developers to incorporate. Many developers of free software
+are heartened and encouraged by the resulting cooperation. However, in the case
+of software used on network servers, this result may fail to come about. The
+GNU General Public License permits making a modified version and letting the
+public access it on a server without ever releasing its source code to the
+public.
+
+The GNU Affero General Public License is designed specifically to ensure that,
+in such cases, the modified source code becomes available to the community. It
+requires the operator of a network server to provide the source code of the
+modified version running there to the users of that server. Therefore, public
+use of a modified version, on a publicly accessible server, gives the public
+access to the source code of the modified version.
+
+An older license, called the Affero General Public License and published by
+Affero, was designed to accomplish similar goals. This is a different license,
+not a version of the Affero GPL, but Affero has released a new version of the
+Affero GPL which permits relicensing under this license.
+
+The precise terms and conditions for copying, distribution and modification
+follow. TERMS AND CONDITIONS 0. Definitions.
+
+"This License" refers to version 3 of the GNU Affero General Public License.
+
+"Copyright" also means copyright-like laws that apply to other kinds of works,
+such as semiconductor masks.
+
+"The Program" refers to any copyrightable work licensed under this License.
+Each licensee is addressed as "you". "Licensees" and "recipients" may be
+individuals or organizations.
+
+To "modify" a work means to copy from or adapt all or part of the work in a
+fashion requiring copyright permission, other than the making of an exact copy.
+The resulting work is called a "modified version" of the earlier work or a work
+"based on" the earlier work.
+
+A "covered work" means either the unmodified Program or a work based on the
+Program.
+
+To "propagate" a work means to do anything with it that, without permission,
+would make you directly or secondarily liable for infringement under applicable
+copyright law, except executing it on a computer or modifying a private copy.
+Propagation includes copying, distribution (with or without modification),
+making available to the public, and in some countries other activities as well.
+
+To "convey" a work means any kind of propagation that enables other parties to
+make or receive copies. Mere interaction with a user through a computer
+network, with no transfer of a copy, is not conveying.
+
+An interactive user interface displays "Appropriate Legal Notices" to the
+extent that it includes a convenient and prominently visible feature that (1)
+displays an appropriate copyright notice, and (2) tells the user that there is
+no warranty for the work (except to the extent that warranties are provided),
+that licensees may convey the work under this License, and how to view a copy
+of this License. If the interface presents a list of user commands or options,
+such as a menu, a prominent item in the list meets this criterion. 1. Source
+Code.
+
+The "source code" for a work means the preferred form of the work for making
+modifications to it. "Object code" means any non-source form of a work.
+
+A "Standard Interface" means an interface that either is an official standard
+defined by a recognized standards body, or, in the case of interfaces specified
+for a particular programming language, one that is widely used among developers
+working in that language.
+
+The "System Libraries" of an executable work include anything, other than the
+work as a whole, that (a) is included in the normal form of packaging a Major
+Component, but which is not part of that Major Component, and (b) serves only
+to enable use of the work with that Major Component, or to implement a Standard
+Interface for which an implementation is available to the public in source code
+form. A "Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system (if any) on
+which the executable work runs, or a compiler used to produce the work, or an
+object code interpreter used to run it.
+
+The "Corresponding Source" for a work in object code form means all the source
+code needed to generate, install, and (for an executable work) run the object
+code and to modify the work, including scripts to control those activities.
+However, it does not include the work's System Libraries, or general-purpose
+tools or generally available free programs which are used unmodified in
+performing those activities but which are not part of the work. For example,
+Corresponding Source includes interface definition files associated with source
+files for the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require, such as
+by intimate data communication or control flow between those subprograms and
+other parts of the work.
+
+The Corresponding Source need not include anything that users can regenerate
+automatically from other parts of the Corresponding Source.
+
+The Corresponding Source for a work in source code form is that same work. 2.
+Basic Permissions.
+
+All rights granted under this License are granted for the term of copyright on
+the Program, and are irrevocable provided the stated conditions are met. This
+License explicitly affirms your unlimited permission to run the unmodified
+Program. The output from running a covered work is covered by this License only
+if the output, given its content, constitutes a covered work. This License
+acknowledges your rights of fair use or other equivalent, as provided by
+copyright law.
+
+You may make, run and propagate covered works that you do not convey, without
+conditions so long as your license otherwise remains in force. You may convey
+covered works to others for the sole purpose of having them make modifications
+exclusively for you, or provide you with facilities for running those works,
+provided that you comply with the terms of this License in conveying all
+material for which you do not control copyright. Those thus making or running
+the covered works for you must do so exclusively on your behalf, under your
+direction and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+Conveying under any other circumstances is permitted solely under the
+conditions stated below. Sublicensing is not allowed; section 10 makes it
+unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+No covered work shall be deemed part of an effective technological measure
+under any applicable law fulfilling obligations under article 11 of the WIPO
+copyright treaty adopted on 20 December 1996, or similar laws prohibiting or
+restricting circumvention of such measures.
+
+When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention is
+effected by exercising rights under this License with respect to the covered
+work, and you disclaim any intention to limit operation or modification of the
+work as a means of enforcing, against the work's users, your or third parties'
+legal rights to forbid circumvention of technological measures. 4. Conveying
+Verbatim Copies.
+
+You may convey verbatim copies of the Program's source code as you receive it,
+in any medium, provided that you conspicuously and appropriately publish on
+each copy an appropriate copyright notice; keep intact all notices stating that
+this License and any non-permissive terms added in accord with section 7 apply
+to the code; keep intact all notices of the absence of any warranty; and give
+all recipients a copy of this License along with the Program.
+
+You may charge any price or no price for each copy that you convey, and you may
+offer support or warranty protection for a fee. 5. Conveying Modified Source
+Versions.
+
+You may convey a work based on the Program, or the modifications to produce it
+from the Program, in the form of source code under the terms of section 4,
+provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified it, and
+ giving a relevant date. b) The work must carry prominent notices stating
+ that it is released under this License and any conditions added under
+ section 7. This requirement modifies the requirement in section 4 to "keep
+ intact all notices". c) You must license the entire work, as a whole,
+ under this License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts, regardless
+ of how they are packaged. This License gives no permission to license the
+ work in any other way, but it does not invalidate such permission if you
+ have separately received it. d) If the work has interactive user
+ interfaces, each must display Appropriate Legal Notices; however, if the
+ Program has interactive interfaces that do not display Appropriate Legal
+ Notices, your work need not make them do so.
+
+A compilation of a covered work with other separate and independent works,
+which are not by their nature extensions of the covered work, and which are not
+combined with it such as to form a larger program, in or on a volume of a
+storage or distribution medium, is called an "aggregate" if the compilation and
+its resulting copyright are not used to limit the access or legal rights of the
+compilation's users beyond what the individual works permit. Inclusion of a
+covered work in an aggregate does not cause this License to apply to the other
+parts of the aggregate. 6. Conveying Non-Source Forms.
+
+You may convey a covered work in object code form under the terms of sections 4
+and 5, provided that you also convey the machine-readable Corresponding Source
+under the terms of this License, in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product (including
+ a physical distribution medium), accompanied by the Corresponding Source
+ fixed on a durable physical medium customarily used for software
+ interchange. b) Convey the object code in, or embodied in, a physical
+ product (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as long as you
+ offer spare parts or customer support for that product model, to give
+ anyone who possesses the object code either (1) a copy of the Corresponding
+ Source for all the software in the product that is covered by this License,
+ on a durable physical medium customarily used for software interchange, for
+ a price no more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the Corresponding Source from a
+ network server at no charge. c) Convey individual copies of the object
+ code with a copy of the written offer to provide the Corresponding Source.
+ This alternative is allowed only occasionally and noncommercially, and only
+ if you received the object code with such an offer, in accord with
+ subsection 6b. d) Convey the object code by offering access from a
+ designated place (gratis or for a charge), and offer equivalent access to
+ the Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the Corresponding
+ Source along with the object code. If the place to copy the object code is
+ a network server, the Corresponding Source may be on a different server
+ (operated by you or a third party) that supports equivalent copying
+ facilities, provided you maintain clear directions next to the object code
+ saying where to find the Corresponding Source. Regardless of what server
+ hosts the Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements. e) Convey
+ the object code using peer-to-peer transmission, provided you inform other
+ peers where the object code and Corresponding Source of the work are being
+ offered to the general public at no charge under subsection 6d.
+
+A separable portion of the object code, whose source code is excluded from the
+Corresponding Source as a System Library, need not be included in conveying the
+object code work.
+
+A "User Product" is either (1) a "consumer product", which means any tangible
+personal property which is normally used for personal, family, or household
+purposes, or (2) anything designed or sold for incorporation into a dwelling.
+In determining whether a product is a consumer product, doubtful cases shall be
+resolved in favor of coverage. For a particular product received by a
+particular user, "normally used" refers to a typical or common use of that
+class of product, regardless of the status of the particular user or of the way
+in which the particular user actually uses, or expects or is expected to use,
+the product. A product is a consumer product regardless of whether the product
+has substantial commercial, industrial or non-consumer uses, unless such uses
+represent the only significant mode of use of the product.
+
+"Installation Information" for a User Product means any methods, procedures,
+authorization keys, or other information required to install and execute
+modified versions of a covered work in that User Product from a modified
+version of its Corresponding Source. The information must suffice to ensure
+that the continued functioning of the modified object code is in no case
+prevented or interfered with solely because modification has been made.
+
+If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as part of a
+transaction in which the right of possession and use of the User Product is
+transferred to the recipient in perpetuity or for a fixed term (regardless of
+how the transaction is characterized), the Corresponding Source conveyed under
+this section must be accompanied by the Installation Information. But this
+requirement does not apply if neither you nor any third party retains the
+ability to install modified object code on the User Product (for example, the
+work has been installed in ROM).
+
+The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates for a
+work that has been modified or installed by the recipient, or for the User
+Product in which it has been modified or installed. Access to a network may be
+denied when the modification itself materially and adversely affects the
+operation of the network or violates the rules and protocols for communication
+across the network.
+
+Corresponding Source conveyed, and Installation Information provided, in accord
+with this section must be in a format that is publicly documented (and with an
+implementation available to the public in source code form), and must require
+no special password or key for unpacking, reading or copying. 7. Additional
+Terms.
+
+"Additional permissions" are terms that supplement the terms of this License by
+making exceptions from one or more of its conditions. Additional permissions
+that are applicable to the entire Program shall be treated as though they were
+included in this License, to the extent that they are valid under applicable
+law. If additional permissions apply only to part of the Program, that part may
+be used separately under those permissions, but the entire Program remains
+governed by this License without regard to the additional permissions.
+
+When you convey a copy of a covered work, you may at your option remove any
+additional permissions from that copy, or from any part of it. (Additional
+permissions may be written to require their own removal in certain cases when
+you modify the work.) You may place additional permissions on material, added
+by you to a covered work, for which you have or can give appropriate copyright
+permission.
+
+Notwithstanding any other provision of this License, for material you add to a
+covered work, you may (if authorized by the copyright holders of that material)
+supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the terms of
+ sections 15 and 16 of this License; or b) Requiring preservation of
+ specified reasonable legal notices or author attributions in that material
+ or in the Appropriate Legal Notices displayed by works containing it; or c)
+ Prohibiting misrepresentation of the origin of that material, or requiring
+ that modified versions of such material be marked in reasonable ways as
+ different from the original version; or d) Limiting the use for publicity
+ purposes of names of licensors or authors of the material; or e) Declining
+ to grant rights under trademark law for use of some trade names,
+ trademarks, or service marks; or f) Requiring indemnification of licensors
+ and authors of that material by anyone who conveys the material (or
+ modified versions of it) with contractual assumptions of liability to the
+ recipient, for any liability that these contractual assumptions directly
+ impose on those licensors and authors.
+
+All other non-permissive additional terms are considered "further restrictions"
+within the meaning of section 10. If the Program as you received it, or any
+part of it, contains a notice stating that it is governed by this License along
+with a term that is a further restriction, you may remove that term. If a
+license document contains a further restriction but permits relicensing or
+conveying under this License, you may add to a covered work material governed
+by the terms of that license document, provided that the further restriction
+does not survive such relicensing or conveying.
+
+If you add terms to a covered work in accord with this section, you must place,
+in the relevant source files, a statement of the additional terms that apply to
+those files, or a notice indicating where to find the applicable terms.
+
+Additional terms, permissive or non-permissive, may be stated in the form of a
+separately written license, or stated as exceptions; the above requirements
+apply either way. 8. Termination.
+
+You may not propagate or modify a covered work except as expressly provided
+under this License. Any attempt otherwise to propagate or modify it is void,
+and will automatically terminate your rights under this License (including any
+patent licenses granted under the third paragraph of section 11).
+
+However, if you cease all violation of this License, then your license from a
+particular copyright holder is reinstated (a) provisionally, unless and until
+the copyright holder explicitly and finally terminates your license, and (b)
+permanently, if the copyright holder fails to notify you of the violation by
+some reasonable means prior to 60 days after the cessation.
+
+Moreover, your license from a particular copyright holder is reinstated
+permanently if the copyright holder notifies you of the violation by some
+reasonable means, this is the first time you have received notice of violation
+of this License (for any work) from that copyright holder, and you cure the
+violation prior to 30 days after your receipt of the notice.
+
+Termination of your rights under this section does not terminate the licenses
+of parties who have received copies or rights from you under this License. If
+your rights have been terminated and not permanently reinstated, you do not
+qualify to receive new licenses for the same material under section 10. 9.
+Acceptance Not Required for Having Copies.
+
+You are not required to accept this License in order to receive or run a copy
+of the Program. Ancillary propagation of a covered work occurring solely as a
+consequence of using peer-to-peer transmission to receive a copy likewise does
+not require acceptance. However, nothing other than this License grants you
+permission to propagate or modify any covered work. These actions infringe
+copyright if you do not accept this License. Therefore, by modifying or
+propagating a covered work, you indicate your acceptance of this License to do
+so. 10. Automatic Licensing of Downstream Recipients.
+
+Each time you convey a covered work, the recipient automatically receives a
+license from the original licensors, to run, modify and propagate that work,
+subject to this License. You are not responsible for enforcing compliance by
+third parties with this License.
+
+An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered work
+results from an entity transaction, each party to that transaction who receives
+a copy of the work also receives whatever licenses to the work the party's
+predecessor in interest had or could give under the previous paragraph, plus a
+right to possession of the Corresponding Source of the work from the
+predecessor in interest, if the predecessor has it or can get it with
+reasonable efforts.
+
+You may not impose any further restrictions on the exercise of the rights
+granted or affirmed under this License. For example, you may not impose a
+license fee, royalty, or other charge for exercise of rights granted under this
+License, and you may not initiate litigation (including a cross-claim or
+counterclaim in a lawsuit) alleging that any patent claim is infringed by
+making, using, selling, offering for sale, or importing the Program or any
+portion of it. 11. Patents.
+
+A "contributor" is a copyright holder who authorizes use under this License of
+the Program or a work on which the Program is based. The work thus licensed is
+called the contributor's "contributor version".
+
+A contributor's "essential patent claims" are all patent claims owned or
+controlled by the contributor, whether already acquired or hereafter acquired,
+that would be infringed by some manner, permitted by this License, of making,
+using, or selling its contributor version, but do not include claims that would
+be infringed only as a consequence of further modification of the contributor
+version. For purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of this
+License.
+
+Each contributor grants you a non-exclusive, worldwide, royalty-free patent
+license under the contributor's essential patent claims, to make, use, sell,
+offer for sale, import and otherwise run, modify and propagate the contents of
+its contributor version.
+
+In the following three paragraphs, a "patent license" is any express agreement
+or commitment, however denominated, not to enforce a patent (such as an express
+permission to practice a patent or covenant not to sue for patent
+infringement). To "grant" such a patent license to a party means to make such
+an agreement or commitment not to enforce a patent against the party.
+
+If you convey a covered work, knowingly relying on a patent license, and the
+Corresponding Source of the work is not available for anyone to copy, free of
+charge and under the terms of this License, through a publicly available
+network server or other readily accessible means, then you must either (1)
+cause the Corresponding Source to be so available, or (2) arrange to deprive
+yourself of the benefit of the patent license for this particular work, or (3)
+arrange, in a manner consistent with the requirements of this License, to
+extend the patent license to downstream recipients. "Knowingly relying" means
+you have actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work in a
+country, would infringe one or more identifiable patents in that country that
+you have reason to believe are valid.
+
+If, pursuant to or in connection with a single transaction or arrangement, you
+convey, or propagate by procuring conveyance of, a covered work, and grant a
+patent license to some of the parties receiving the covered work authorizing
+them to use, propagate, modify or convey a specific copy of the covered work,
+then the patent license you grant is automatically extended to all recipients
+of the covered work and works based on it.
+
+A patent license is "discriminatory" if it does not include within the scope of
+its coverage, prohibits the exercise of, or is conditioned on the non-exercise
+of one or more of the rights that are specifically granted under this License.
+You may not convey a covered work if you are a party to an arrangement with a
+third party that is in the business of distributing software, under which you
+make payment to the third party based on the extent of your activity of
+conveying the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory patent
+license (a) in connection with copies of the covered work conveyed by you (or
+copies made from those copies), or (b) primarily for and in connection with
+specific products or compilations that contain the covered work, unless you
+entered into that arrangement, or that patent license was granted, prior to 28
+March 2007.
+
+Nothing in this License shall be construed as excluding or limiting any implied
+license or other defenses to infringement that may otherwise be available to
+you under applicable patent law. 12. No Surrender of Others' Freedom.
+
+If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not excuse
+you from the conditions of this License. If you cannot convey a covered work so
+as to satisfy simultaneously your obligations under this License and any other
+pertinent obligations, then as a consequence you may not convey it at all. For
+example, if you agree to terms that obligate you to collect a royalty for
+further conveying from those to whom you convey the Program, the only way you
+could satisfy both those terms and this License would be to refrain entirely
+from conveying the Program. 13. Remote Network Interaction; Use with the GNU
+General Public License.
+
+Notwithstanding any other provision of this License, if you modify the Program,
+your modified version must prominently offer all users interacting with it
+remotely through a computer network (if your version supports such interaction)
+an opportunity to receive the Corresponding Source of your version by providing
+access to the Corresponding Source from a network server at no charge, through
+some standard or customary means of facilitating copying of software. This
+Corresponding Source shall include the Corresponding Source for any work
+covered by version 3 of the GNU General Public License that is incorporated
+pursuant to the following paragraph.
+
+Notwithstanding any other provision of this License, you have permission to
+link or combine any covered work with a work licensed under version 3 of the
+GNU General Public License into a single combined work, and to convey the
+resulting work. The terms of this License will continue to apply to the part
+which is the covered work, but the work with which it is combined will remain
+governed by version 3 of the GNU General Public License. 14. Revised Versions
+of this License.
+
+The Free Software Foundation may publish revised and/or new versions of the GNU
+Affero General Public License from time to time. Such new versions will be
+similar in spirit to the present version, but may differ in detail to address
+new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program specifies
+that a certain numbered version of the GNU Affero General Public License "or
+any later version" applies to it, you have the option of following the terms
+and conditions either of that numbered version or of any later version
+published by the Free Software Foundation. If the Program does not specify a
+version number of the GNU Affero General Public License, you may choose any
+version ever published by the Free Software Foundation.
+
+If the Program specifies that a proxy can decide which future versions of the
+GNU Affero General Public License can be used, that proxy's public statement of
+acceptance of a version permanently authorizes you to choose that version for
+the Program.
+
+Later license versions may give you additional or different permissions.
+However, no additional obligations are imposed on any author or copyright
+holder as a result of your choosing to follow a later version. 15. Disclaimer
+of Warranty.
+
+THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
+LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER
+PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE
+QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
+DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION. 16. Limitation of Liability.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
+COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS
+PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
+INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
+INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
+PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY
+HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of
+Sections 15 and 16.
+
+If the disclaimer of warranty and limitation of liability provided above cannot
+be given local legal effect according to their terms, reviewing courts shall
+apply local law that most closely approximates an absolute waiver of all civil
+liability in connection with the Program, unless a warranty or assumption of
+liability accompanies a copy of the Program in return for a fee.
+
+END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest possible
+use to the public, the best way to achieve this is to make it free software
+which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest to attach
+them to the start of each source file to most effectively state the exclusion
+of warranty; and each file should have at least the "copyright" line and a
+pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If your software can interact with users remotely through a computer network,
+you should also make sure that it provides a way for users to get its source.
+For example, if your program is a web application, its interface could display
+a "Source" link that leads users to an archive of the code. There are many ways
+you could offer source, and different solutions will be better for different
+programs; see section 13 for the specific requirements.
+
+You should also get your employer (if you work as a programmer) or school, if
+any, to sign a "copyright disclaimer" for the program, if necessary. For more
+information on this, and how to apply and follow the GNU AGPL, see
+<https://www.gnu.org/licenses/>.
diff --git a/hashmap.c b/hashmap.c
new file mode 100644
index 0000000..a83934d
--- /dev/null
+++ b/hashmap.c
@@ -0,0 +1,165 @@
+// This is an implementation of the open-addressing hash table.
+
+#include "chibicc.h"
+
+// Initial hash bucket size
+#define INIT_SIZE 16
+
+// Rehash if the usage exceeds 70%.
+#define HIGH_WATERMARK 70
+
+// We'll keep the usage below 50% after rehashing.
+#define LOW_WATERMARK 50
+
+// Represents a deleted hash entry
+#define TOMBSTONE ((void *)-1)
+
+static uint64_t fnv_hash(char *s, int len) {
+ uint64_t hash = 0xcbf29ce484222325;
+ for (int i = 0; i < len; i++) {
+ hash *= 0x100000001b3;
+ hash ^= (unsigned char)s[i];
+ }
+ return hash;
+}
+
+// Make room for new entires in a given hashmap by removing
+// tombstones and possibly extending the bucket size.
+static void rehash(HashMap *map) {
+ // Compute the size of the new hashmap.
+ int nkeys = 0;
+ for (int i = 0; i < map->capacity; i++)
+ if (map->buckets[i].key && map->buckets[i].key != TOMBSTONE)
+ nkeys++;
+
+ int cap = map->capacity;
+ while ((nkeys * 100) / cap >= LOW_WATERMARK)
+ cap = cap * 2;
+ assert(cap > 0);
+
+ // Create a new hashmap and copy all key-values.
+ HashMap map2 = {};
+ map2.buckets = calloc(cap, sizeof(HashEntry));
+ map2.capacity = cap;
+
+ for (int i = 0; i < map->capacity; i++) {
+ HashEntry *ent = &map->buckets[i];
+ if (ent->key && ent->key != TOMBSTONE)
+ hashmap_put2(&map2, ent->key, ent->keylen, ent->val);
+ }
+
+ assert(map2.used == nkeys);
+ *map = map2;
+}
+
+static bool match(HashEntry *ent, char *key, int keylen) {
+ return ent->key && ent->key != TOMBSTONE &&
+ ent->keylen == keylen && memcmp(ent->key, key, keylen) == 0;
+}
+
+static HashEntry *get_entry(HashMap *map, char *key, int keylen) {
+ if (!map->buckets)
+ return NULL;
+
+ uint64_t hash = fnv_hash(key, keylen);
+
+ for (int i = 0; i < map->capacity; i++) {
+ HashEntry *ent = &map->buckets[(hash + i) % map->capacity];
+ if (match(ent, key, keylen))
+ return ent;
+ if (ent->key == NULL)
+ return NULL;
+ }
+ unreachable();
+}
+
+static HashEntry *get_or_insert_entry(HashMap *map, char *key, int keylen) {
+ if (!map->buckets) {
+ map->buckets = calloc(INIT_SIZE, sizeof(HashEntry));
+ map->capacity = INIT_SIZE;
+ } else if ((map->used * 100) / map->capacity >= HIGH_WATERMARK) {
+ rehash(map);
+ }
+
+ uint64_t hash = fnv_hash(key, keylen);
+
+ for (int i = 0; i < map->capacity; i++) {
+ HashEntry *ent = &map->buckets[(hash + i) % map->capacity];
+
+ if (match(ent, key, keylen))
+ return ent;
+
+ if (ent->key == TOMBSTONE) {
+ ent->key = key;
+ ent->keylen = keylen;
+ return ent;
+ }
+
+ if (ent->key == NULL) {
+ ent->key = key;
+ ent->keylen = keylen;
+ map->used++;
+ return ent;
+ }
+ }
+ unreachable();
+}
+
+void *hashmap_get(HashMap *map, char *key) {
+ return hashmap_get2(map, key, strlen(key));
+}
+
+void *hashmap_get2(HashMap *map, char *key, int keylen) {
+ HashEntry *ent = get_entry(map, key, keylen);
+ return ent ? ent->val : NULL;
+}
+
+void hashmap_put(HashMap *map, char *key, void *val) {
+ hashmap_put2(map, key, strlen(key), val);
+}
+
+void hashmap_put2(HashMap *map, char *key, int keylen, void *val) {
+ HashEntry *ent = get_or_insert_entry(map, key, keylen);
+ ent->val = val;
+}
+
+void hashmap_delete(HashMap *map, char *key) {
+ hashmap_delete2(map, key, strlen(key));
+}
+
+void hashmap_delete2(HashMap *map, char *key, int keylen) {
+ HashEntry *ent = get_entry(map, key, keylen);
+ if (ent)
+ ent->key = TOMBSTONE;
+}
+
+void hashmap_test(void) {
+ HashMap *map = calloc(1, sizeof(HashMap));
+
+ for (int i = 0; i < 5000; i++)
+ hashmap_put(map, format("key %d", i), (void *)(size_t)i);
+ for (int i = 1000; i < 2000; i++)
+ hashmap_delete(map, format("key %d", i));
+ for (int i = 1500; i < 1600; i++)
+ hashmap_put(map, format("key %d", i), (void *)(size_t)i);
+ for (int i = 6000; i < 7000; i++)
+ hashmap_put(map, format("key %d", i), (void *)(size_t)i);
+
+ for (int i = 0; i < 1000; i++)
+ assert((size_t)hashmap_get(map, format("key %d", i)) == i);
+ for (int i = 1000; i < 1500; i++)
+ assert(hashmap_get(map, "no such key") == NULL);
+ for (int i = 1500; i < 1600; i++)
+ assert((size_t)hashmap_get(map, format("key %d", i)) == i);
+ for (int i = 1600; i < 2000; i++)
+ assert(hashmap_get(map, "no such key") == NULL);
+ for (int i = 2000; i < 5000; i++)
+ assert((size_t)hashmap_get(map, format("key %d", i)) == i);
+ for (int i = 5000; i < 6000; i++)
+ assert(hashmap_get(map, "no such key") == NULL);
+ for (int i = 6000; i < 7000; i++)
+ hashmap_put(map, format("key %d", i), (void *)(size_t)i);
+
+ assert(hashmap_get(map, "no such key") == NULL);
+ printf("OK\n");
+}
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..af10c1d
--- /dev/null
+++ b/main.c
@@ -0,0 +1,72 @@
+#include "chibicc.h"
+
+typedef enum {
+ FILE_NONE, FILE_C, FILE_ASM, FILE_OBJ, FILE_AR, FILE_DSO,
+} FileType;
+
+char *base_file;
+
+StringArray include_paths;
+bool opt_fcommon = false;
+bool opt_fpic;
+
+static FILE *open_file(char *path) {
+ if (!path || strcmp(path, "-") == 0)
+ return stdout;
+
+ FILE *out = fopen(path, "w");
+ if (!out)
+ error("cannot open output file: %s: %s", path, strerror(errno));
+ return out;
+}
+
+static Token *must_tokenize_file(char *path) {
+ Token *tok = tokenize_file(path);
+ if (!tok)
+ error("%s: %s", path, strerror(errno));
+ return tok;
+}
+
+static Token *append_tokens(Token *tok1, Token *tok2) {
+ if (!tok1 || tok1->kind == TK_EOF)
+ return tok2;
+
+ Token *t = tok1;
+ while (t->next->kind != TK_EOF)
+ t = t->next;
+ t->next = tok2;
+ return tok1;
+}
+
+static void cc1(char *base_file) {
+ Token *tok = NULL;
+
+ // Tokenize and parse.
+ Token *tok2 = must_tokenize_file(base_file);
+ tok = append_tokens(tok, tok2);
+ tok = preprocess(tok);
+
+ Obj *prog = parse(tok);
+
+ // Open a temporary output buffer.
+ char *buf;
+ size_t buflen;
+ FILE *output_buf = open_memstream(&buf, &buflen);
+
+ // Traverse the AST to emit assembly.
+ typegen(prog, output_buf);
+ codegen(prog, output_buf);
+ fclose(output_buf);
+
+ // Write the asembly text to a file.
+ FILE *out = open_file("/dev/stdout");
+ fwrite(buf, buflen, 1, out);
+ fclose(out);
+}
+
+int main(int argc, char **argv) {
+ assert(argc == 2);
+ base_file = argv[1];
+ cc1(argv[1]);
+ return 0;
+}
diff --git a/parse.c b/parse.c
new file mode 100644
index 0000000..0963095
--- /dev/null
+++ b/parse.c
@@ -0,0 +1,3378 @@
+// This file contains a recursive descent parser for C.
+//
+// Most functions in this file are named after the symbols they are
+// supposed to read from an input token list. For example, stmt() is
+// responsible for reading a statement from a token list. The function
+// then construct an AST node representing a statement.
+//
+// Each function conceptually returns two values, an AST node and
+// remaining part of the input tokens. Since C doesn't support
+// multiple return values, the remaining tokens are returned to the
+// caller via a pointer argument.
+//
+// Input tokens are represented by a linked list. Unlike many recursive
+// descent parsers, we don't have the notion of the "input token stream".
+// Most parsing functions don't change the global state of the parser.
+// So it is very easy to lookahead arbitrary number of tokens in this
+// parser.
+
+#include "chibicc.h"
+
+// Scope for local variables, global variables, typedefs
+// or enum constants
+typedef struct {
+ Obj *var;
+ Type *type_def;
+ Type *enum_ty;
+ int enum_val;
+} VarScope;
+
+// Represents a block scope.
+typedef struct Scope Scope;
+struct Scope {
+ Scope *next;
+
+ // C has two block scopes; one is for variables/typedefs and
+ // the other is for struct/union/enum tags.
+ HashMap vars;
+ HashMap tags;
+};
+
+// Variable attributes such as typedef or extern.
+typedef struct {
+ bool is_typedef;
+ bool is_static;
+ bool is_extern;
+ bool is_inline;
+ bool is_tls;
+ int align;
+} VarAttr;
+
+// This struct represents a variable initializer. Since initializers
+// can be nested (e.g. `int x[2][2] = {{1, 2}, {3, 4}}`), this struct
+// is a tree data structure.
+typedef struct Initializer Initializer;
+struct Initializer {
+ Initializer *next;
+ Type *ty;
+ Token *tok;
+ bool is_flexible;
+
+ // If it's not an aggregate type and has an initializer,
+ // `expr` has an initialization expression.
+ Node *expr;
+
+ // If it's an initializer for an aggregate type (e.g. array or struct),
+ // `children` has initializers for its children.
+ Initializer **children;
+
+ // Only one member can be initialized for a union.
+ // `mem` is used to clarify which member is initialized.
+ Member *mem;
+};
+
+// For local variable initializer.
+typedef struct InitDesg InitDesg;
+struct InitDesg {
+ InitDesg *next;
+ int idx;
+ Member *member;
+ Obj *var;
+};
+
+// All local variable instances created during parsing are
+// accumulated to this list.
+static Obj *locals;
+
+// Likewise, global variables are accumulated to this list.
+static Obj *globals;
+
+static Scope *scope = &(Scope){};
+
+// Points to the function object the parser is currently parsing.
+static Obj *current_fn;
+
+// Lists of all goto statements and labels in the curent function.
+static Node *gotos;
+static Node *labels;
+
+// Current "goto" and "continue" jump targets.
+static char *brk_label;
+static char *cont_label;
+
+// Points to a node representing a switch if we are parsing
+// a switch statement. Otherwise, NULL.
+static Node *current_switch;
+
+static Obj *builtin_alloca;
+
+static bool is_typename(Token *tok);
+static Type *declspec(Token **rest, Token *tok, VarAttr *attr);
+static Type *typename(Token **rest, Token *tok);
+static Type *enum_specifier(Token **rest, Token *tok);
+static Type *typeof_specifier(Token **rest, Token *tok);
+static Type *type_suffix(Token **rest, Token *tok, Type *ty);
+static Type *declarator(Token **rest, Token *tok, Type *ty);
+static Node *declaration(Token **rest, Token *tok, Type *basety, VarAttr *attr);
+static void array_initializer2(Token **rest, Token *tok, Initializer *init, int i);
+static void struct_initializer2(Token **rest, Token *tok, Initializer *init, Member *mem);
+static void initializer2(Token **rest, Token *tok, Initializer *init);
+static Initializer *initializer(Token **rest, Token *tok, Type *ty, Type **new_ty);
+static Node *lvar_initializer(Token **rest, Token *tok, Obj *var);
+static void gvar_initializer(Token **rest, Token *tok, Obj *var);
+static Node *compound_stmt(Token **rest, Token *tok);
+static Node *stmt(Token **rest, Token *tok);
+static Node *expr_stmt(Token **rest, Token *tok);
+static Node *expr(Token **rest, Token *tok);
+static int64_t eval(Node *node);
+static int64_t eval2(Node *node, char ***label);
+static int64_t eval_rval(Node *node, char ***label);
+static bool is_const_expr(Node *node);
+static Node *assign(Token **rest, Token *tok);
+static Node *logor(Token **rest, Token *tok);
+static double eval_double(Node *node);
+static Node *conditional(Token **rest, Token *tok);
+static Node *logand(Token **rest, Token *tok);
+static Node *bitor(Token **rest, Token *tok);
+static Node *bitxor(Token **rest, Token *tok);
+static Node *bitand(Token **rest, Token *tok);
+static Node *equality(Token **rest, Token *tok);
+static Node *relational(Token **rest, Token *tok);
+static Node *shift(Token **rest, Token *tok);
+static Node *add(Token **rest, Token *tok);
+static Node *new_add(Node *lhs, Node *rhs, Token *tok);
+static Node *new_sub(Node *lhs, Node *rhs, Token *tok);
+static Node *mul(Token **rest, Token *tok);
+static Node *cast(Token **rest, Token *tok);
+static Member *get_struct_member(Type *ty, Token *tok);
+static Type *struct_decl(Token **rest, Token *tok);
+static Type *union_decl(Token **rest, Token *tok);
+static Node *postfix(Token **rest, Token *tok);
+static Node *funcall(Token **rest, Token *tok, Node *node);
+static Node *unary(Token **rest, Token *tok);
+static Node *primary(Token **rest, Token *tok);
+static Token *parse_typedef(Token *tok, Type *basety);
+static bool is_function(Token *tok);
+static Token *function(Token *tok, Type *basety, VarAttr *attr);
+static Token *global_variable(Token *tok, Type *basety, VarAttr *attr);
+
+static int align_down(int n, int align) {
+ return align_to(n - align + 1, align);
+}
+
+static void enter_scope(void) {
+ Scope *sc = calloc(1, sizeof(Scope));
+ sc->next = scope;
+ scope = sc;
+}
+
+static void leave_scope(void) {
+ scope = scope->next;
+}
+
+// Find a variable by name.
+static VarScope *find_var(Token *tok) {
+ for (Scope *sc = scope; sc; sc = sc->next) {
+ VarScope *sc2 = hashmap_get2(&sc->vars, tok->loc, tok->len);
+ if (sc2)
+ return sc2;
+ }
+ return NULL;
+}
+
+static Type *find_tag(Token *tok) {
+ for (Scope *sc = scope; sc; sc = sc->next) {
+ Type *ty = hashmap_get2(&sc->tags, tok->loc, tok->len);
+ if (ty)
+ return ty;
+ }
+ return NULL;
+}
+
+static Node *new_node(NodeKind kind, Token *tok) {
+ Node *node = calloc(1, sizeof(Node));
+ node->kind = kind;
+ node->tok = tok;
+ return node;
+}
+
+static Node *new_binary(NodeKind kind, Node *lhs, Node *rhs, Token *tok) {
+ Node *node = new_node(kind, tok);
+ node->lhs = lhs;
+ node->rhs = rhs;
+ return node;
+}
+
+static Node *new_unary(NodeKind kind, Node *expr, Token *tok) {
+ Node *node = new_node(kind, tok);
+ node->lhs = expr;
+ return node;
+}
+
+static Node *new_num(int64_t val, Token *tok) {
+ Node *node = new_node(ND_NUM, tok);
+ node->val = val;
+ return node;
+}
+
+static Node *new_long(int64_t val, Token *tok) {
+ Node *node = new_node(ND_NUM, tok);
+ node->val = val;
+ node->ty = ty_long;
+ return node;
+}
+
+static Node *new_ulong(long val, Token *tok) {
+ Node *node = new_node(ND_NUM, tok);
+ node->val = val;
+ node->ty = ty_ulong;
+ return node;
+}
+
+static Node *new_var_node(Obj *var, Token *tok) {
+ Node *node = new_node(ND_VAR, tok);
+ node->var = var;
+ return node;
+}
+
+static Node *new_vla_ptr(Obj *var, Token *tok) {
+ Node *node = new_node(ND_VLA_PTR, tok);
+ node->var = var;
+ return node;
+}
+
+Node *new_cast(Node *expr, Type *ty) {
+ add_type(expr);
+
+ Node *node = calloc(1, sizeof(Node));
+ node->kind = ND_CAST;
+ node->tok = expr->tok;
+ node->lhs = expr;
+ node->ty = copy_type(ty);
+ return node;
+}
+
+static VarScope *push_scope(char *name) {
+ VarScope *sc = calloc(1, sizeof(VarScope));
+ hashmap_put(&scope->vars, name, sc);
+ return sc;
+}
+
+static Initializer *new_initializer(Type *ty, bool is_flexible) {
+ Initializer *init = calloc(1, sizeof(Initializer));
+ init->ty = ty;
+
+ if (ty->kind == TY_ARRAY) {
+ if (is_flexible && ty->size < 0) {
+ init->is_flexible = true;
+ return init;
+ }
+
+ init->children = calloc(ty->array_len, sizeof(Initializer *));
+ for (int i = 0; i < ty->array_len; i++)
+ init->children[i] = new_initializer(ty->base, false);
+ return init;
+ }
+
+ if (ty->kind == TY_STRUCT || ty->kind == TY_UNION) {
+ // Count the number of struct members.
+ int len = 0;
+ for (Member *mem = ty->members; mem; mem = mem->next)
+ len++;
+
+ init->children = calloc(len, sizeof(Initializer *));
+
+ for (Member *mem = ty->members; mem; mem = mem->next) {
+ if (is_flexible && ty->is_flexible && !mem->next) {
+ Initializer *child = calloc(1, sizeof(Initializer));
+ child->ty = mem->ty;
+ child->is_flexible = true;
+ init->children[mem->idx] = child;
+ } else {
+ init->children[mem->idx] = new_initializer(mem->ty, false);
+ }
+ }
+ return init;
+ }
+
+ return init;
+}
+
+static Obj *new_var(char *name, Type *ty) {
+ Obj *var = calloc(1, sizeof(Obj));
+ var->name = name;
+ var->ty = ty;
+ var->align = ty->align;
+ push_scope(name)->var = var;
+ return var;
+}
+
+static Obj *new_lvar(char *name, Type *ty) {
+ Obj *var = new_var(name, ty);
+ var->is_local = true;
+ var->next = locals;
+ locals = var;
+ return var;
+}
+
+static Obj *new_gvar(char *name, Type *ty) {
+ Obj *var = new_var(name, ty);
+ var->next = globals;
+ var->is_static = true;
+ var->is_definition = true;
+ globals = var;
+ return var;
+}
+
+static char *new_unique_name(void) {
+ static int id = 0;
+ return format(".L..%d", id++);
+}
+
+static Obj *new_anon_gvar(Type *ty) {
+ return new_gvar(new_unique_name(), ty);
+}
+
+static Obj *new_string_literal(char *p, Type *ty) {
+ Obj *var = new_anon_gvar(ty);
+ var->init_data = p;
+ return var;
+}
+
+static char *get_ident(Token *tok) {
+ if (tok->kind != TK_IDENT)
+ error_tok(tok, "expected an identifier");
+ return strndup(tok->loc, tok->len);
+}
+
+static Type *find_typedef(Token *tok) {
+ if (tok->kind == TK_IDENT) {
+ VarScope *sc = find_var(tok);
+ if (sc)
+ return sc->type_def;
+ }
+ return NULL;
+}
+
+static void push_tag_scope(Token *tok, Type *ty) {
+ hashmap_put2(&scope->tags, tok->loc, tok->len, ty);
+}
+
+// declspec = ("void" | "_Bool" | "char" | "short" | "int" | "long"
+// | "typedef" | "static" | "extern" | "inline"
+// | "_Thread_local" | "__thread"
+// | "signed" | "unsigned"
+// | struct-decl | union-decl | typedef-name
+// | enum-specifier | typeof-specifier
+// | "const" | "volatile" | "auto" | "register" | "restrict"
+// | "__restrict" | "__restrict__" | "_Noreturn")+
+//
+// The order of typenames in a type-specifier doesn't matter. For
+// example, `int long static` means the same as `static long int`.
+// That can also be written as `static long` because you can omit
+// `int` if `long` or `short` are specified. However, something like
+// `char int` is not a valid type specifier. We have to accept only a
+// limited combinations of the typenames.
+//
+// In this function, we count the number of occurrences of each typename
+// while keeping the "current" type object that the typenames up
+// until that point represent. When we reach a non-typename token,
+// we returns the current type object.
+static Type *declspec(Token **rest, Token *tok, VarAttr *attr) {
+ // We use a single integer as counters for all typenames.
+ // For example, bits 0 and 1 represents how many times we saw the
+ // keyword "void" so far. With this, we can use a switch statement
+ // as you can see below.
+ enum {
+ VOID = 1 << 0,
+ BOOL = 1 << 2,
+ CHAR = 1 << 4,
+ SHORT = 1 << 6,
+ INT = 1 << 8,
+ LONG = 1 << 10,
+ FLOAT = 1 << 12,
+ DOUBLE = 1 << 14,
+ OTHER = 1 << 16,
+ SIGNED = 1 << 17,
+ UNSIGNED = 1 << 18,
+ };
+
+ Type *ty = ty_int;
+ int counter = 0;
+ bool is_atomic = false;
+
+ while (is_typename(tok)) {
+ // Handle storage class specifiers.
+ if (equal(tok, "typedef") || equal(tok, "static") || equal(tok, "extern") ||
+ equal(tok, "inline") || equal(tok, "_Thread_local") || equal(tok, "__thread")) {
+ if (!attr)
+ error_tok(tok, "storage class specifier is not allowed in this context");
+
+ if (equal(tok, "typedef"))
+ attr->is_typedef = true;
+ else if (equal(tok, "static"))
+ attr->is_static = true;
+ else if (equal(tok, "extern"))
+ attr->is_extern = true;
+ else if (equal(tok, "inline"))
+ attr->is_inline = true;
+ else
+ attr->is_tls = true;
+
+ if (attr->is_typedef &&
+ attr->is_static + attr->is_extern + attr->is_inline + attr->is_tls > 1)
+ error_tok(tok, "typedef may not be used together with static,"
+ " extern, inline, __thread or _Thread_local");
+ tok = tok->next;
+ continue;
+ }
+
+ // These keywords are recognized but ignored.
+ if (consume(&tok, tok, "const") || consume(&tok, tok, "volatile") ||
+ consume(&tok, tok, "auto") || consume(&tok, tok, "register") ||
+ consume(&tok, tok, "restrict") || consume(&tok, tok, "__restrict") ||
+ consume(&tok, tok, "__restrict__") || consume(&tok, tok, "_Noreturn"))
+ continue;
+
+ if (equal(tok, "_Atomic")) {
+ tok = tok->next;
+ if (equal(tok , "(")) {
+ ty = typename(&tok, tok->next);
+ tok = skip(tok, ")");
+ }
+ is_atomic = true;
+ continue;
+ }
+
+ if (equal(tok, "_Alignas")) {
+ if (!attr)
+ error_tok(tok, "_Alignas is not allowed in this context");
+ tok = skip(tok->next, "(");
+
+ if (is_typename(tok))
+ attr->align = typename(&tok, tok)->align;
+ else
+ attr->align = const_expr(&tok, tok);
+ tok = skip(tok, ")");
+ continue;
+ }
+
+ // Handle user-defined types.
+ Type *ty2 = find_typedef(tok);
+ if (equal(tok, "struct") || equal(tok, "union") || equal(tok, "enum") ||
+ equal(tok, "typeof") || ty2) {
+ if (counter)
+ break;
+
+ if (equal(tok, "struct")) {
+ ty = struct_decl(&tok, tok->next);
+ } else if (equal(tok, "union")) {
+ ty = union_decl(&tok, tok->next);
+ } else if (equal(tok, "enum")) {
+ ty = enum_specifier(&tok, tok->next);
+ } else if (equal(tok, "typeof")) {
+ ty = typeof_specifier(&tok, tok->next);
+ } else {
+ ty = ty2;
+ tok = tok->next;
+ }
+
+ counter += OTHER;
+ continue;
+ }
+
+ // Handle built-in types.
+ if (equal(tok, "void"))
+ counter += VOID;
+ else if (equal(tok, "_Bool"))
+ counter += BOOL;
+ else if (equal(tok, "char"))
+ counter += CHAR;
+ else if (equal(tok, "short"))
+ counter += SHORT;
+ else if (equal(tok, "int"))
+ counter += INT;
+ else if (equal(tok, "long"))
+ counter += LONG;
+ else if (equal(tok, "float"))
+ counter += FLOAT;
+ else if (equal(tok, "double"))
+ counter += DOUBLE;
+ else if (equal(tok, "signed"))
+ counter |= SIGNED;
+ else if (equal(tok, "unsigned"))
+ counter |= UNSIGNED;
+ else
+ unreachable();
+
+ switch (counter) {
+ case VOID:
+ ty = ty_void;
+ break;
+ case BOOL:
+ ty = ty_bool;
+ break;
+ case CHAR:
+ case SIGNED + CHAR:
+ ty = ty_char;
+ break;
+ case UNSIGNED + CHAR:
+ ty = ty_uchar;
+ break;
+ case SHORT:
+ case SHORT + INT:
+ case SIGNED + SHORT:
+ case SIGNED + SHORT + INT:
+ ty = ty_short;
+ break;
+ case UNSIGNED + SHORT:
+ case UNSIGNED + SHORT + INT:
+ ty = ty_ushort;
+ break;
+ case INT:
+ case SIGNED:
+ case SIGNED + INT:
+ ty = ty_int;
+ break;
+ case UNSIGNED:
+ case UNSIGNED + INT:
+ ty = ty_uint;
+ break;
+ case LONG:
+ case LONG + INT:
+ case LONG + LONG:
+ case LONG + LONG + INT:
+ case SIGNED + LONG:
+ case SIGNED + LONG + INT:
+ case SIGNED + LONG + LONG:
+ case SIGNED + LONG + LONG + INT:
+ ty = ty_long;
+ break;
+ case UNSIGNED + LONG:
+ case UNSIGNED + LONG + INT:
+ case UNSIGNED + LONG + LONG:
+ case UNSIGNED + LONG + LONG + INT:
+ ty = ty_ulong;
+ break;
+ case FLOAT:
+ ty = ty_float;
+ break;
+ case DOUBLE:
+ ty = ty_double;
+ break;
+ case LONG + DOUBLE:
+ ty = ty_ldouble;
+ break;
+ default:
+ error_tok(tok, "invalid type");
+ }
+
+ tok = tok->next;
+ }
+
+ if (is_atomic) {
+ ty = copy_type(ty);
+ ty->is_atomic = true;
+ }
+
+ *rest = tok;
+ return ty;
+}
+
+// func-params = ("void" | param ("," param)* ("," "...")?)? ")"
+// param = declspec declarator
+static Type *func_params(Token **rest, Token *tok, Type *ty) {
+ if (equal(tok, "void") && equal(tok->next, ")")) {
+ *rest = tok->next->next;
+ return func_type(ty);
+ }
+
+ Type head = {};
+ Type *cur = &head;
+ bool is_variadic = false;
+
+ while (!equal(tok, ")")) {
+ if (tok && tok->str) {
+ printf("TOKSTR: %s\n", tok->str);
+ }
+ if (cur != &head)
+ tok = skip(tok, ",");
+
+ if (equal(tok, "...")) {
+ is_variadic = true;
+ tok = tok->next;
+ skip(tok, ")");
+ break;
+ }
+
+ Type *ty2 = declspec(&tok, tok, NULL);
+ ty2 = declarator(&tok, tok, ty2);
+
+ Token *name = ty2->name;
+
+ if (ty2->kind == TY_ARRAY) {
+ // "array of T" is converted to "pointer to T" only in the parameter
+ // context. For example, *argv[] is converted to **argv by this.
+ ty2 = pointer_to(ty2->base);
+ ty2->name = name;
+ } else if (ty2->kind == TY_FUNC) {
+ // Likewise, a function is converted to a pointer to a function
+ // only in the parameter context.
+ ty2 = pointer_to(ty2);
+ ty2->name = name;
+ }
+
+ cur = cur->next = copy_type(ty2);
+ }
+
+ if (cur == &head)
+ is_variadic = true;
+
+ ty = func_type(ty);
+ ty->params = head.next;
+ ty->is_variadic = is_variadic;
+ *rest = tok->next;
+ return ty;
+}
+
+// array-dimensions = ("static" | "restrict")* const-expr? "]" type-suffix
+static Type *array_dimensions(Token **rest, Token *tok, Type *ty) {
+ while (equal(tok, "static") || equal(tok, "restrict"))
+ tok = tok->next;
+
+ if (equal(tok, "]")) {
+ ty = type_suffix(rest, tok->next, ty);
+ return array_of(ty, -1);
+ }
+
+ Node *expr = conditional(&tok, tok);
+ tok = skip(tok, "]");
+ ty = type_suffix(rest, tok, ty);
+
+ if (ty->kind == TY_VLA || !is_const_expr(expr)) {
+ error_tok(tok, "VLA found!");
+ return vla_of(ty, expr);
+ }
+ int len = eval(expr);
+ if (len < 0) error_tok(tok, "negative array length");
+ return array_of(ty, len);
+}
+
+// type-suffix = "(" func-params
+// | "[" array-dimensions
+// | ε
+static Type *type_suffix(Token **rest, Token *tok, Type *ty) {
+ if (equal(tok, "("))
+ return func_params(rest, tok->next, ty);
+
+ if (equal(tok, "["))
+ return array_dimensions(rest, tok->next, ty);
+
+ *rest = tok;
+ return ty;
+}
+
+// pointers = ("*" ("const" | "volatile" | "restrict")*)*
+static Type *pointers(Token **rest, Token *tok, Type *ty) {
+ while (consume(&tok, tok, "*")) {
+ ty = pointer_to(ty);
+ while (equal(tok, "const") || equal(tok, "volatile") || equal(tok, "restrict") ||
+ equal(tok, "__restrict") || equal(tok, "__restrict__"))
+ tok = tok->next;
+ }
+ *rest = tok;
+ return ty;
+}
+
+// declarator = pointers ("(" ident ")" | "(" declarator ")" | ident) type-suffix
+static Type *declarator(Token **rest, Token *tok, Type *ty) {
+ ty = pointers(&tok, tok, ty);
+
+ if (equal(tok, "(")) {
+ Token *start = tok;
+ Type dummy = {};
+ declarator(&tok, start->next, &dummy);
+ tok = skip(tok, ")");
+ ty = type_suffix(rest, tok, ty);
+ return declarator(&tok, start->next, ty);
+ }
+
+ Token *name = NULL;
+ Token *name_pos = tok;
+
+ if (tok->kind == TK_IDENT) {
+ name = tok;
+ tok = tok->next;
+ }
+
+ ty = type_suffix(rest, tok, ty);
+ ty->name = name;
+ ty->name_pos = name_pos;
+ return ty;
+}
+
+// abstract-declarator = pointers ("(" abstract-declarator ")")? type-suffix
+static Type *abstract_declarator(Token **rest, Token *tok, Type *ty) {
+ ty = pointers(&tok, tok, ty);
+
+ if (equal(tok, "(")) {
+ Token *start = tok;
+ Type dummy = {};
+ abstract_declarator(&tok, start->next, &dummy);
+ tok = skip(tok, ")");
+ ty = type_suffix(rest, tok, ty);
+ return abstract_declarator(&tok, start->next, ty);
+ }
+
+ return type_suffix(rest, tok, ty);
+}
+
+// type-name = declspec abstract-declarator
+static Type *typename(Token **rest, Token *tok) {
+ Type *ty = declspec(&tok, tok, NULL);
+ return abstract_declarator(rest, tok, ty);
+}
+
+static bool is_end(Token *tok) {
+ return equal(tok, "}") || (equal(tok, ",") && equal(tok->next, "}"));
+}
+
+static bool consume_end(Token **rest, Token *tok) {
+ if (equal(tok, "}")) {
+ *rest = tok->next;
+ return true;
+ }
+
+ if (equal(tok, ",") && equal(tok->next, "}")) {
+ *rest = tok->next->next;
+ return true;
+ }
+
+ return false;
+}
+
+// enum-specifier = ident? "{" enum-list? "}"
+// | ident ("{" enum-list? "}")?
+//
+// enum-list = ident ("=" num)? ("," ident ("=" num)?)* ","?
+static Type *enum_specifier(Token **rest, Token *tok) {
+ Type *ty = enum_type();
+
+ // Read a struct tag.
+ Token *tag = NULL;
+ if (tok->kind == TK_IDENT) {
+ tag = tok;
+ tok = tok->next;
+ }
+
+ if (tag && !equal(tok, "{")) {
+ Type *ty = find_tag(tag);
+ if (!ty)
+ error_tok(tag, "unknown enum type");
+ if (ty->kind != TY_ENUM)
+ error_tok(tag, "not an enum tag");
+ *rest = tok;
+ return ty;
+ }
+
+ tok = skip(tok, "{");
+
+ // Read an enum-list.
+ int i = 0;
+ int val = 0;
+ while (!consume_end(rest, tok)) {
+ if (i++ > 0)
+ tok = skip(tok, ",");
+
+ char *name = get_ident(tok);
+ tok = tok->next;
+
+ if (equal(tok, "="))
+ val = const_expr(&tok, tok->next);
+
+ VarScope *sc = push_scope(name);
+ sc->enum_ty = ty;
+ sc->enum_val = val++;
+ }
+
+ if (tag)
+ push_tag_scope(tag, ty);
+ return ty;
+}
+
+// typeof-specifier = "(" (expr | typename) ")"
+static Type *typeof_specifier(Token **rest, Token *tok) {
+ tok = skip(tok, "(");
+
+ Type *ty;
+ if (is_typename(tok)) {
+ ty = typename(&tok, tok);
+ } else {
+ Node *node = expr(&tok, tok);
+ add_type(node);
+ ty = node->ty;
+ }
+ *rest = skip(tok, ")");
+ return ty;
+}
+
+// Generate code for computing a VLA size.
+static Node *compute_vla_size(Type *ty, Token *tok) {
+ Node *node = new_node(ND_NULL_EXPR, tok);
+ if (ty->base)
+ node = new_binary(ND_COMMA, node, compute_vla_size(ty->base, tok), tok);
+
+ if (ty->kind != TY_VLA)
+ return node;
+
+ Node *base_sz;
+ if (ty->base->kind == TY_VLA)
+ base_sz = new_var_node(ty->base->vla_size, tok);
+ else
+ base_sz = new_num(ty->base->size, tok);
+
+ ty->vla_size = new_lvar("", ty_ulong);
+ Node *expr = new_binary(ND_ASSIGN, new_var_node(ty->vla_size, tok),
+ new_binary(ND_MUL, ty->vla_len, base_sz, tok),
+ tok);
+ return new_binary(ND_COMMA, node, expr, tok);
+}
+
+static Node *new_alloca(Node *sz) {
+ Node *node = new_unary(ND_FUNCALL, new_var_node(builtin_alloca, sz->tok), sz->tok);
+ node->func_ty = builtin_alloca->ty;
+ node->ty = builtin_alloca->ty->return_ty;
+ node->args = sz;
+ add_type(sz);
+ return node;
+}
+
+// declaration = declspec (declarator ("=" expr)? ("," declarator ("=" expr)?)*)? ";"
+static Node *declaration(Token **rest, Token *tok, Type *basety, VarAttr *attr) {
+ Node head = {};
+ Node *cur = &head;
+ int i = 0;
+
+ while (!equal(tok, ";")) {
+ if (i++ > 0)
+ tok = skip(tok, ",");
+
+ Type *ty = declarator(&tok, tok, basety);
+ if (ty->kind == TY_VOID)
+ error_tok(tok, "variable declared void");
+ if (!ty->name)
+ error_tok(ty->name_pos, "variable name omitted");
+
+ if (attr && attr->is_static) {
+ // static local variable
+ Obj *var = new_anon_gvar(ty);
+ push_scope(get_ident(ty->name))->var = var;
+ if (equal(tok, "="))
+ gvar_initializer(&tok, tok->next, var);
+ continue;
+ }
+
+ // Generate code for computing a VLA size. We need to do this
+ // even if ty is not VLA because ty may be a pointer to VLA
+ // (e.g. int (*foo)[n][m] where n and m are variables.)
+ cur = cur->next = new_unary(ND_EXPR_STMT, compute_vla_size(ty, tok), tok);
+
+ if (ty->kind == TY_VLA) {
+ if (equal(tok, "="))
+ error_tok(tok, "variable-sized object may not be initialized");
+
+ // Variable length arrays (VLAs) are translated to alloca() calls.
+ // For example, `int x[n+2]` is translated to `tmp = n + 2,
+ // x = alloca(tmp)`.
+ Obj *var = new_lvar(get_ident(ty->name), ty);
+ Token *tok = ty->name;
+ Node *expr = new_binary(ND_ASSIGN, new_vla_ptr(var, tok),
+ new_alloca(new_var_node(ty->vla_size, tok)),
+ tok);
+
+ cur = cur->next = new_unary(ND_EXPR_STMT, expr, tok);
+ continue;
+ }
+
+ Obj *var = new_lvar(get_ident(ty->name), ty);
+ if (attr && attr->align)
+ var->align = attr->align;
+
+ if (equal(tok, "=")) {
+ Node *expr = lvar_initializer(&tok, tok->next, var);
+ cur = cur->next = new_unary(ND_EXPR_STMT, expr, tok);
+ }
+
+ if (var->ty->size < 0)
+ error_tok(ty->name, "variable has incomplete type");
+ if (var->ty->kind == TY_VOID)
+ error_tok(ty->name, "variable declared void");
+ }
+
+ Node *node = new_node(ND_BLOCK, tok);
+ node->body = head.next;
+ *rest = tok->next;
+ return node;
+}
+
+static Token *skip_excess_element(Token *tok) {
+ if (equal(tok, "{")) {
+ tok = skip_excess_element(tok->next);
+ return skip(tok, "}");
+ }
+
+ assign(&tok, tok);
+ return tok;
+}
+
+// string-initializer = string-literal
+static void string_initializer(Token **rest, Token *tok, Initializer *init) {
+ if (init->is_flexible)
+ *init = *new_initializer(array_of(init->ty->base, tok->ty->array_len), false);
+
+ int len = MIN(init->ty->array_len, tok->ty->array_len);
+
+ switch (init->ty->base->size) {
+ case 1: {
+ char *str = tok->str;
+ for (int i = 0; i < len; i++)
+ init->children[i]->expr = new_num(str[i], tok);
+ break;
+ }
+ case 2: {
+ uint16_t *str = (uint16_t *)tok->str;
+ for (int i = 0; i < len; i++)
+ init->children[i]->expr = new_num(str[i], tok);
+ break;
+ }
+ case 4: {
+ uint32_t *str = (uint32_t *)tok->str;
+ for (int i = 0; i < len; i++)
+ init->children[i]->expr = new_num(str[i], tok);
+ break;
+ }
+ default:
+ unreachable();
+ }
+
+ *rest = tok->next;
+}
+
+// array-designator = "[" const-expr "]"
+//
+// C99 added the designated initializer to the language, which allows
+// programmers to move the "cursor" of an initializer to any element.
+// The syntax looks like this:
+//
+// int x[10] = { 1, 2, [5]=3, 4, 5, 6, 7 };
+//
+// `[5]` moves the cursor to the 5th element, so the 5th element of x
+// is set to 3. Initialization then continues forward in order, so
+// 6th, 7th, 8th and 9th elements are initialized with 4, 5, 6 and 7,
+// respectively. Unspecified elements (in this case, 3rd and 4th
+// elements) are initialized with zero.
+//
+// Nesting is allowed, so the following initializer is valid:
+//
+// int x[5][10] = { [5][8]=1, 2, 3 };
+//
+// It sets x[5][8], x[5][9] and x[6][0] to 1, 2 and 3, respectively.
+//
+// Use `.fieldname` to move the cursor for a struct initializer. E.g.
+//
+// struct { int a, b, c; } x = { .c=5 };
+//
+// The above initializer sets x.c to 5.
+static void array_designator(Token **rest, Token *tok, Type *ty, int *begin, int *end) {
+ *begin = const_expr(&tok, tok->next);
+ if (*begin >= ty->array_len)
+ error_tok(tok, "array designator index exceeds array bounds");
+
+ if (equal(tok, "...")) {
+ *end = const_expr(&tok, tok->next);
+ if (*end >= ty->array_len)
+ error_tok(tok, "array designator index exceeds array bounds");
+ if (*end < *begin)
+ error_tok(tok, "array designator range [%d, %d] is empty", *begin, *end);
+ } else {
+ *end = *begin;
+ }
+
+ *rest = skip(tok, "]");
+}
+
+// struct-designator = "." ident
+static Member *struct_designator(Token **rest, Token *tok, Type *ty) {
+ Token *start = tok;
+ tok = skip(tok, ".");
+ if (tok->kind != TK_IDENT)
+ error_tok(tok, "expected a field designator");
+
+ for (Member *mem = ty->members; mem; mem = mem->next) {
+ // Anonymous struct member
+ if (mem->ty->kind == TY_STRUCT && !mem->name) {
+ if (get_struct_member(mem->ty, tok)) {
+ *rest = start;
+ return mem;
+ }
+ continue;
+ }
+
+ // Regular struct member
+ if (mem->name->len == tok->len && !strncmp(mem->name->loc, tok->loc, tok->len)) {
+ *rest = tok->next;
+ return mem;
+ }
+ }
+
+ error_tok(tok, "struct has no such member");
+}
+
+// designation = ("[" const-expr "]" | "." ident)* "="? initializer
+static void designation(Token **rest, Token *tok, Initializer *init) {
+ if (equal(tok, "[")) {
+ if (init->ty->kind != TY_ARRAY)
+ error_tok(tok, "array index in non-array initializer");
+
+ int begin, end;
+ array_designator(&tok, tok, init->ty, &begin, &end);
+
+ Token *tok2;
+ for (int i = begin; i <= end; i++)
+ designation(&tok2, tok, init->children[i]);
+ array_initializer2(rest, tok2, init, begin + 1);
+ return;
+ }
+
+ if (equal(tok, ".") && init->ty->kind == TY_STRUCT) {
+ Member *mem = struct_designator(&tok, tok, init->ty);
+ designation(&tok, tok, init->children[mem->idx]);
+ init->expr = NULL;
+ struct_initializer2(rest, tok, init, mem->next);
+ return;
+ }
+
+ if (equal(tok, ".") && init->ty->kind == TY_UNION) {
+ Member *mem = struct_designator(&tok, tok, init->ty);
+ init->mem = mem;
+ designation(rest, tok, init->children[mem->idx]);
+ return;
+ }
+
+ if (equal(tok, "."))
+ error_tok(tok, "field name not in struct or union initializer");
+
+ if (equal(tok, "="))
+ tok = tok->next;
+ initializer2(rest, tok, init);
+}
+
+// An array length can be omitted if an array has an initializer
+// (e.g. `int x[] = {1,2,3}`). If it's omitted, count the number
+// of initializer elements.
+static int count_array_init_elements(Token *tok, Type *ty) {
+ bool first = true;
+ Initializer *dummy = new_initializer(ty->base, true);
+
+ int i = 0, max = 0;
+
+ while (!consume_end(&tok, tok)) {
+ if (!first)
+ tok = skip(tok, ",");
+ first = false;
+
+ if (equal(tok, "[")) {
+ i = const_expr(&tok, tok->next);
+ if (equal(tok, "..."))
+ i = const_expr(&tok, tok->next);
+ tok = skip(tok, "]");
+ designation(&tok, tok, dummy);
+ } else {
+ initializer2(&tok, tok, dummy);
+ }
+
+ i++;
+ max = MAX(max, i);
+ }
+ return max;
+}
+
+// array-initializer1 = "{" initializer ("," initializer)* ","? "}"
+static void array_initializer1(Token **rest, Token *tok, Initializer *init) {
+ tok = skip(tok, "{");
+
+ if (init->is_flexible) {
+ int len = count_array_init_elements(tok, init->ty);
+ *init = *new_initializer(array_of(init->ty->base, len), false);
+ }
+
+ bool first = true;
+
+ if (init->is_flexible) {
+ int len = count_array_init_elements(tok, init->ty);
+ *init = *new_initializer(array_of(init->ty->base, len), false);
+ }
+
+ for (int i = 0; !consume_end(rest, tok); i++) {
+ if (!first)
+ tok = skip(tok, ",");
+ first = false;
+
+ if (equal(tok, "[")) {
+ int begin, end;
+ array_designator(&tok, tok, init->ty, &begin, &end);
+
+ Token *tok2;
+ for (int j = begin; j <= end; j++)
+ designation(&tok2, tok, init->children[j]);
+ tok = tok2;
+ i = end;
+ continue;
+ }
+
+ if (i < init->ty->array_len)
+ initializer2(&tok, tok, init->children[i]);
+ else
+ tok = skip_excess_element(tok);
+ }
+}
+
+// array-initializer2 = initializer ("," initializer)*
+static void array_initializer2(Token **rest, Token *tok, Initializer *init, int i) {
+ if (init->is_flexible) {
+ int len = count_array_init_elements(tok, init->ty);
+ *init = *new_initializer(array_of(init->ty->base, len), false);
+ }
+
+ for (; i < init->ty->array_len && !is_end(tok); i++) {
+ Token *start = tok;
+ if (i > 0)
+ tok = skip(tok, ",");
+
+ if (equal(tok, "[") || equal(tok, ".")) {
+ *rest = start;
+ return;
+ }
+
+ initializer2(&tok, tok, init->children[i]);
+ }
+ *rest = tok;
+}
+
+// struct-initializer1 = "{" initializer ("," initializer)* ","? "}"
+static void struct_initializer1(Token **rest, Token *tok, Initializer *init) {
+ tok = skip(tok, "{");
+
+ Member *mem = init->ty->members;
+ bool first = true;
+
+ while (!consume_end(rest, tok)) {
+ if (!first)
+ tok = skip(tok, ",");
+ first = false;
+
+ if (equal(tok, ".")) {
+ mem = struct_designator(&tok, tok, init->ty);
+ designation(&tok, tok, init->children[mem->idx]);
+ mem = mem->next;
+ continue;
+ }
+
+ if (mem) {
+ initializer2(&tok, tok, init->children[mem->idx]);
+ mem = mem->next;
+ } else {
+ tok = skip_excess_element(tok);
+ }
+ }
+}
+
+// struct-initializer2 = initializer ("," initializer)*
+static void struct_initializer2(Token **rest, Token *tok, Initializer *init, Member *mem) {
+ bool first = true;
+
+ for (; mem && !is_end(tok); mem = mem->next) {
+ Token *start = tok;
+
+ if (!first)
+ tok = skip(tok, ",");
+ first = false;
+
+ if (equal(tok, "[") || equal(tok, ".")) {
+ *rest = start;
+ return;
+ }
+
+ initializer2(&tok, tok, init->children[mem->idx]);
+ }
+ *rest = tok;
+}
+
+static void union_initializer(Token **rest, Token *tok, Initializer *init) {
+ // Unlike structs, union initializers take only one initializer,
+ // and that initializes the first union member by default.
+ // You can initialize other member using a designated initializer.
+ if (equal(tok, "{") && equal(tok->next, ".")) {
+ Member *mem = struct_designator(&tok, tok->next, init->ty);
+ init->mem = mem;
+ designation(&tok, tok, init->children[mem->idx]);
+ *rest = skip(tok, "}");
+ return;
+ }
+
+ init->mem = init->ty->members;
+
+ if (equal(tok, "{")) {
+ initializer2(&tok, tok->next, init->children[0]);
+ consume(&tok, tok, ",");
+ *rest = skip(tok, "}");
+ } else {
+ initializer2(rest, tok, init->children[0]);
+ }
+}
+
+// initializer = string-initializer | array-initializer
+// | struct-initializer | union-initializer
+// | assign
+static void initializer2(Token **rest, Token *tok, Initializer *init) {
+ if (init->ty->kind == TY_ARRAY && tok->kind == TK_STR) {
+ string_initializer(rest, tok, init);
+ return;
+ }
+
+ if (init->ty->kind == TY_ARRAY) {
+ if (equal(tok, "{"))
+ array_initializer1(rest, tok, init);
+ else
+ array_initializer2(rest, tok, init, 0);
+ return;
+ }
+
+ if (init->ty->kind == TY_STRUCT) {
+ if (equal(tok, "{")) {
+ struct_initializer1(rest, tok, init);
+ return;
+ }
+
+ // A struct can be initialized with another struct. E.g.
+ // `struct T x = y;` where y is a variable of type `struct T`.
+ // Handle that case first.
+ Node *expr = assign(rest, tok);
+ add_type(expr);
+ if (expr->ty->kind == TY_STRUCT) {
+ init->expr = expr;
+ return;
+ }
+
+ struct_initializer2(rest, tok, init, init->ty->members);
+ return;
+ }
+
+ if (init->ty->kind == TY_UNION) {
+ union_initializer(rest, tok, init);
+ return;
+ }
+
+ if (equal(tok, "{")) {
+ // An initializer for a scalar variable can be surrounded by
+ // braces. E.g. `int x = {3};`. Handle that case.
+ initializer2(&tok, tok->next, init);
+ *rest = skip(tok, "}");
+ return;
+ }
+
+ init->expr = assign(rest, tok);
+}
+
+static Type *copy_struct_type(Type *ty) {
+ ty = copy_type(ty);
+
+ Member head = {};
+ Member *cur = &head;
+ for (Member *mem = ty->members; mem; mem = mem->next) {
+ Member *m = calloc(1, sizeof(Member));
+ *m = *mem;
+ cur = cur->next = m;
+ }
+
+ ty->members = head.next;
+ return ty;
+}
+
+static Initializer *initializer(Token **rest, Token *tok, Type *ty, Type **new_ty) {
+ Initializer *init = new_initializer(ty, true);
+ initializer2(rest, tok, init);
+
+ if ((ty->kind == TY_STRUCT || ty->kind == TY_UNION) && ty->is_flexible) {
+ ty = copy_struct_type(ty);
+
+ Member *mem = ty->members;
+ while (mem->next)
+ mem = mem->next;
+ mem->ty = init->children[mem->idx]->ty;
+ ty->size += mem->ty->size;
+
+ *new_ty = ty;
+ return init;
+ }
+
+ *new_ty = init->ty;
+ return init;
+}
+
+static Node *init_desg_expr(InitDesg *desg, Token *tok) {
+ if (desg->var)
+ return new_var_node(desg->var, tok);
+
+ if (desg->member) {
+ Node *node = new_unary(ND_MEMBER, init_desg_expr(desg->next, tok), tok);
+ node->member = desg->member;
+ return node;
+ }
+
+ Node *lhs = init_desg_expr(desg->next, tok);
+ Node *rhs = new_num(desg->idx, tok);
+ return new_unary(ND_DEREF, new_add(lhs, rhs, tok), tok);
+}
+
+static Node *create_lvar_init(Initializer *init, Type *ty, InitDesg *desg, Token *tok) {
+ if (ty->kind == TY_ARRAY) {
+ Node *node = new_node(ND_NULL_EXPR, tok);
+ for (int i = 0; i < ty->array_len; i++) {
+ InitDesg desg2 = {desg, i};
+ Node *rhs = create_lvar_init(init->children[i], ty->base, &desg2, tok);
+ node = new_binary(ND_COMMA, node, rhs, tok);
+ }
+ return node;
+ }
+
+ if (ty->kind == TY_STRUCT && !init->expr) {
+ Node *node = new_node(ND_NULL_EXPR, tok);
+
+ for (Member *mem = ty->members; mem; mem = mem->next) {
+ InitDesg desg2 = {desg, 0, mem};
+ Node *rhs = create_lvar_init(init->children[mem->idx], mem->ty, &desg2, tok);
+ node = new_binary(ND_COMMA, node, rhs, tok);
+ }
+ return node;
+ }
+
+ if (ty->kind == TY_UNION) {
+ Member *mem = init->mem ? init->mem : ty->members;
+ InitDesg desg2 = {desg, 0, mem};
+ return create_lvar_init(init->children[mem->idx], mem->ty, &desg2, tok);
+ }
+
+ if (!init->expr)
+ return new_node(ND_NULL_EXPR, tok);
+
+ Node *lhs = init_desg_expr(desg, tok);
+ return new_binary(ND_ASSIGN, lhs, init->expr, tok);
+}
+
+// A variable definition with an initializer is a shorthand notation
+// for a variable definition followed by assignments. This function
+// generates assignment expressions for an initializer. For example,
+// `int x[2][2] = {{6, 7}, {8, 9}}` is converted to the following
+// expressions:
+//
+// x[0][0] = 6;
+// x[0][1] = 7;
+// x[1][0] = 8;
+// x[1][1] = 9;
+static Node *lvar_initializer(Token **rest, Token *tok, Obj *var) {
+ Initializer *init = initializer(rest, tok, var->ty, &var->ty);
+ InitDesg desg = {NULL, 0, NULL, var};
+
+ // If a partial initializer list is given, the standard requires
+ // that unspecified elements are set to 0. Here, we simply
+ // zero-initialize the entire memory region of a variable before
+ // initializing it with user-supplied values.
+ Node *lhs = new_node(ND_MEMZERO, tok);
+ lhs->var = var;
+
+ Node *rhs = create_lvar_init(init, var->ty, &desg, tok);
+ return new_binary(ND_COMMA, lhs, rhs, tok);
+}
+
+static uint64_t read_buf(char *buf, int sz) {
+ if (sz == 1)
+ return *buf;
+ if (sz == 2)
+ return *(uint16_t *)buf;
+ if (sz == 4)
+ return *(uint32_t *)buf;
+ if (sz == 8)
+ return *(uint64_t *)buf;
+ unreachable();
+}
+
+static void write_buf(char *buf, uint64_t val, int sz) {
+ if (sz == 1)
+ *buf = val;
+ else if (sz == 2)
+ *(uint16_t *)buf = val;
+ else if (sz == 4)
+ *(uint32_t *)buf = val;
+ else if (sz == 8)
+ *(uint64_t *)buf = val;
+ else
+ unreachable();
+}
+
+static Relocation *
+write_gvar_data(Relocation *cur, Initializer *init, Type *ty, char *buf, int offset) {
+ if (ty->kind == TY_ARRAY) {
+ int sz = ty->base->size;
+ for (int i = 0; i < ty->array_len; i++)
+ cur = write_gvar_data(cur, init->children[i], ty->base, buf, offset + sz * i);
+ return cur;
+ }
+
+ if (ty->kind == TY_STRUCT) {
+ for (Member *mem = ty->members; mem; mem = mem->next) {
+ if (mem->is_bitfield) {
+ Node *expr = init->children[mem->idx]->expr;
+ if (!expr)
+ break;
+
+ char *loc = buf + offset + mem->offset;
+ uint64_t oldval = read_buf(loc, mem->ty->size);
+ uint64_t newval = eval(expr);
+ uint64_t mask = (1L << mem->bit_width) - 1;
+ uint64_t combined = oldval | ((newval & mask) << mem->bit_offset);
+ write_buf(loc, combined, mem->ty->size);
+ } else {
+ cur = write_gvar_data(cur, init->children[mem->idx], mem->ty, buf,
+ offset + mem->offset);
+ }
+ }
+ return cur;
+ }
+
+ if (ty->kind == TY_UNION) {
+ if (!init->mem)
+ return cur;
+ return write_gvar_data(cur, init->children[init->mem->idx],
+ init->mem->ty, buf, offset);
+ }
+
+ if (!init->expr)
+ return cur;
+
+ if (ty->kind == TY_FLOAT) {
+ *(float *)(buf + offset) = eval_double(init->expr);
+ return cur;
+ }
+
+ if (ty->kind == TY_DOUBLE) {
+ *(double *)(buf + offset) = eval_double(init->expr);
+ return cur;
+ }
+
+ char **label = NULL;
+ uint64_t val = eval2(init->expr, &label);
+
+ if (!label) {
+ write_buf(buf + offset, val, ty->size);
+ return cur;
+ }
+
+ Relocation *rel = calloc(1, sizeof(Relocation));
+ rel->offset = offset;
+ rel->label = label;
+ rel->addend = val;
+ cur->next = rel;
+ return cur->next;
+}
+
+// Initializers for global variables are evaluated at compile-time and
+// embedded to .data section. This function serializes Initializer
+// objects to a flat byte array. It is a compile error if an
+// initializer list contains a non-constant expression.
+static void gvar_initializer(Token **rest, Token *tok, Obj *var) {
+ Initializer *init = initializer(rest, tok, var->ty, &var->ty);
+
+ Relocation head = {};
+ char *buf = calloc(1, var->ty->size);
+ write_gvar_data(&head, init, var->ty, buf, 0);
+ var->init_data = buf;
+ var->rel = head.next;
+}
+
+// Returns true if a given token represents a type.
+static bool is_typename(Token *tok) {
+ static HashMap map;
+
+ if (map.capacity == 0) {
+ static char *kw[] = {
+ "void", "_Bool", "char", "short", "int", "long", "struct", "union",
+ "typedef", "enum", "static", "extern", "_Alignas", "signed", "unsigned",
+ "const", "volatile", "auto", "register", "restrict", "__restrict",
+ "__restrict__", "_Noreturn", "float", "double", "typeof", "inline",
+ "_Thread_local", "__thread", "_Atomic",
+ };
+
+ for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++)
+ hashmap_put(&map, kw[i], (void *)1);
+ }
+
+ return hashmap_get2(&map, tok->loc, tok->len) || find_typedef(tok);
+}
+
+// asm-stmt = "asm" ("volatile" | "inline")* "(" string-literal ")"
+static Node *asm_stmt(Token **rest, Token *tok) {
+ Node *node = new_node(ND_ASM, tok);
+ tok = tok->next;
+
+ while (equal(tok, "volatile") || equal(tok, "inline"))
+ tok = tok->next;
+
+ tok = skip(tok, "(");
+ if (tok->kind != TK_STR || tok->ty->base->kind != TY_CHAR)
+ error_tok(tok, "expected string literal");
+ node->asm_str = tok->str;
+ *rest = skip(tok->next, ")");
+ return node;
+}
+
+// stmt = "return" expr? ";"
+// | "if" "(" expr ")" stmt ("else" stmt)?
+// | "switch" "(" expr ")" stmt
+// | "case" const-expr ("..." const-expr)? ":" stmt
+// | "default" ":" stmt
+// | "for" "(" expr-stmt expr? ";" expr? ")" stmt
+// | "while" "(" expr ")" stmt
+// | "do" stmt "while" "(" expr ")" ";"
+// | "asm" asm-stmt
+// | "goto" (ident | "*" expr) ";"
+// | "break" ";"
+// | "continue" ";"
+// | ident ":" stmt
+// | "{" compound-stmt
+// | expr-stmt
+static Node *stmt(Token **rest, Token *tok) {
+ if (equal(tok, "return")) {
+ Node *node = new_node(ND_RETURN, tok);
+ if (consume(rest, tok->next, ";"))
+ return node;
+
+ Node *exp = expr(&tok, tok->next);
+ *rest = skip(tok, ";");
+
+ add_type(exp);
+ Type *ty = current_fn->ty->return_ty;
+ if (ty->kind != TY_STRUCT && ty->kind != TY_UNION)
+ exp = new_cast(exp, current_fn->ty->return_ty);
+
+ node->lhs = exp;
+ return node;
+ }
+
+ if (equal(tok, "if")) {
+ Node *node = new_node(ND_IF, tok);
+ tok = skip(tok->next, "(");
+ node->cond = expr(&tok, tok);
+ tok = skip(tok, ")");
+ node->then = stmt(&tok, tok);
+ if (equal(tok, "else"))
+ node->els = stmt(&tok, tok->next);
+ *rest = tok;
+ return node;
+ }
+
+ if (equal(tok, "switch")) {
+ Node *node = new_node(ND_SWITCH, tok);
+ tok = skip(tok->next, "(");
+ node->cond = expr(&tok, tok);
+ tok = skip(tok, ")");
+
+ Node *sw = current_switch;
+ current_switch = node;
+
+ char *brk = brk_label;
+ brk_label = node->brk_label = new_unique_name();
+
+ node->then = stmt(rest, tok);
+
+ current_switch = sw;
+ brk_label = brk;
+ return node;
+ }
+
+ if (equal(tok, "case")) {
+ if (!current_switch)
+ error_tok(tok, "stray case");
+
+ Node *node = new_node(ND_CASE, tok);
+ int begin = const_expr(&tok, tok->next);
+ int end;
+
+ if (equal(tok, "...")) {
+ // [GNU] Case ranges, e.g. "case 1 ... 5:"
+ end = const_expr(&tok, tok->next);
+ if (end < begin)
+ error_tok(tok, "empty case range specified");
+ } else {
+ end = begin;
+ }
+
+ tok = skip(tok, ":");
+ node->label = new_unique_name();
+ node->lhs = stmt(rest, tok);
+ node->begin = begin;
+ node->end = end;
+ node->case_next = current_switch->case_next;
+ current_switch->case_next = node;
+ return node;
+ }
+
+ if (equal(tok, "default")) {
+ if (!current_switch)
+ error_tok(tok, "stray default");
+
+ Node *node = new_node(ND_CASE, tok);
+ tok = skip(tok->next, ":");
+ node->label = new_unique_name();
+ node->lhs = stmt(rest, tok);
+ current_switch->default_case = node;
+ return node;
+ }
+
+ if (equal(tok, "for")) {
+ Node *node = new_node(ND_FOR, tok);
+ tok = skip(tok->next, "(");
+
+ enter_scope();
+
+ char *brk = brk_label;
+ char *cont = cont_label;
+ brk_label = node->brk_label = new_unique_name();
+ cont_label = node->cont_label = new_unique_name();
+
+ if (is_typename(tok)) {
+ Type *basety = declspec(&tok, tok, NULL);
+ node->init = declaration(&tok, tok, basety, NULL);
+ } else {
+ node->init = expr_stmt(&tok, tok);
+ }
+
+ if (!equal(tok, ";"))
+ node->cond = expr(&tok, tok);
+ tok = skip(tok, ";");
+
+ if (!equal(tok, ")"))
+ node->inc = expr(&tok, tok);
+ tok = skip(tok, ")");
+
+ node->then = stmt(rest, tok);
+
+ leave_scope();
+ brk_label = brk;
+ cont_label = cont;
+ return node;
+ }
+
+ if (equal(tok, "while")) {
+ Node *node = new_node(ND_FOR, tok);
+ tok = skip(tok->next, "(");
+ node->cond = expr(&tok, tok);
+ tok = skip(tok, ")");
+
+ char *brk = brk_label;
+ char *cont = cont_label;
+ brk_label = node->brk_label = new_unique_name();
+ cont_label = node->cont_label = new_unique_name();
+
+ node->then = stmt(rest, tok);
+
+ brk_label = brk;
+ cont_label = cont;
+ return node;
+ }
+
+ if (equal(tok, "do")) {
+ Node *node = new_node(ND_DO, tok);
+
+ char *brk = brk_label;
+ char *cont = cont_label;
+ brk_label = node->brk_label = new_unique_name();
+ cont_label = node->cont_label = new_unique_name();
+
+ node->then = stmt(&tok, tok->next);
+
+ brk_label = brk;
+ cont_label = cont;
+
+ tok = skip(tok, "while");
+ tok = skip(tok, "(");
+ node->cond = expr(&tok, tok);
+ tok = skip(tok, ")");
+ *rest = skip(tok, ";");
+ return node;
+ }
+
+ if (equal(tok, "asm"))
+ return asm_stmt(rest, tok);
+
+ if (equal(tok, "goto")) {
+ if (equal(tok->next, "*")) {
+ // [GNU] `goto *ptr` jumps to the address specified by `ptr`.
+ Node *node = new_node(ND_GOTO_EXPR, tok);
+ node->lhs = expr(&tok, tok->next->next);
+ *rest = skip(tok, ";");
+ return node;
+ }
+
+ Node *node = new_node(ND_GOTO, tok);
+ node->label = get_ident(tok->next);
+ node->goto_next = gotos;
+ gotos = node;
+ *rest = skip(tok->next->next, ";");
+ return node;
+ }
+
+ if (equal(tok, "break")) {
+ if (!brk_label)
+ error_tok(tok, "stray break");
+ Node *node = new_node(ND_GOTO, tok);
+ node->unique_label = brk_label;
+ *rest = skip(tok->next, ";");
+ return node;
+ }
+
+ if (equal(tok, "continue")) {
+ if (!cont_label)
+ error_tok(tok, "stray continue");
+ Node *node = new_node(ND_GOTO, tok);
+ node->unique_label = cont_label;
+ *rest = skip(tok->next, ";");
+ return node;
+ }
+
+ if (tok->kind == TK_IDENT && equal(tok->next, ":")) {
+ Node *node = new_node(ND_LABEL, tok);
+ node->label = strndup(tok->loc, tok->len);
+ node->unique_label = new_unique_name();
+ node->lhs = stmt(rest, tok->next->next);
+ node->goto_next = labels;
+ labels = node;
+ return node;
+ }
+
+ if (equal(tok, "{"))
+ return compound_stmt(rest, tok->next);
+
+ return expr_stmt(rest, tok);
+}
+
+// compound-stmt = (typedef | declaration | stmt)* "}"
+static Node *compound_stmt(Token **rest, Token *tok) {
+ Node *node = new_node(ND_BLOCK, tok);
+ Node head = {};
+ Node *cur = &head;
+
+ enter_scope();
+
+ while (!equal(tok, "}")) {
+ if (is_typename(tok) && !equal(tok->next, ":")) {
+ VarAttr attr = {};
+ Type *basety = declspec(&tok, tok, &attr);
+
+ if (attr.is_typedef) {
+ tok = parse_typedef(tok, basety);
+ continue;
+ }
+
+ if (is_function(tok)) {
+ tok = function(tok, basety, &attr);
+ continue;
+ }
+
+ if (attr.is_extern) {
+ tok = global_variable(tok, basety, &attr);
+ continue;
+ }
+
+ cur = cur->next = declaration(&tok, tok, basety, &attr);
+ } else {
+ cur = cur->next = stmt(&tok, tok);
+ }
+ add_type(cur);
+ }
+
+ leave_scope();
+
+ node->body = head.next;
+ *rest = tok->next;
+ return node;
+}
+
+// expr-stmt = expr? ";"
+static Node *expr_stmt(Token **rest, Token *tok) {
+ if (equal(tok, ";")) {
+ *rest = tok->next;
+ return new_node(ND_BLOCK, tok);
+ }
+
+ Node *node = new_node(ND_EXPR_STMT, tok);
+ node->lhs = expr(&tok, tok);
+ *rest = skip(tok, ";");
+ return node;
+}
+
+// expr = assign ("," expr)?
+static Node *expr(Token **rest, Token *tok) {
+ Node *node = assign(&tok, tok);
+
+ if (equal(tok, ","))
+ return new_binary(ND_COMMA, node, expr(rest, tok->next), tok);
+
+ *rest = tok;
+ return node;
+}
+
+static int64_t eval(Node *node) {
+ return eval2(node, NULL);
+}
+
+// Evaluate a given node as a constant expression.
+//
+// A constant expression is either just a number or ptr+n where ptr
+// is a pointer to a global variable and n is a postiive/negative
+// number. The latter form is accepted only as an initialization
+// expression for a global variable.
+static int64_t eval2(Node *node, char ***label) {
+ add_type(node);
+
+ if (is_flonum(node->ty))
+ return eval_double(node);
+
+ switch (node->kind) {
+ case ND_ADD:
+ return eval2(node->lhs, label) + eval(node->rhs);
+ case ND_SUB:
+ return eval2(node->lhs, label) - eval(node->rhs);
+ case ND_MUL:
+ return eval(node->lhs) * eval(node->rhs);
+ case ND_DIV:
+ if (node->ty->is_unsigned)
+ return (uint64_t)eval(node->lhs) / eval(node->rhs);
+ return eval(node->lhs) / eval(node->rhs);
+ case ND_NEG:
+ return -eval(node->lhs);
+ case ND_MOD:
+ if (node->ty->is_unsigned)
+ return (uint64_t)eval(node->lhs) % eval(node->rhs);
+ return eval(node->lhs) % eval(node->rhs);
+ case ND_BITAND:
+ return eval(node->lhs) & eval(node->rhs);
+ case ND_BITOR:
+ return eval(node->lhs) | eval(node->rhs);
+ case ND_BITXOR:
+ return eval(node->lhs) ^ eval(node->rhs);
+ case ND_SHL:
+ return eval(node->lhs) << eval(node->rhs);
+ case ND_SHR:
+ if (node->ty->is_unsigned && node->ty->size == 8)
+ return (uint64_t)eval(node->lhs) >> eval(node->rhs);
+ return eval(node->lhs) >> eval(node->rhs);
+ case ND_EQ:
+ return eval(node->lhs) == eval(node->rhs);
+ case ND_NE:
+ return eval(node->lhs) != eval(node->rhs);
+ case ND_LT:
+ if (node->lhs->ty->is_unsigned)
+ return (uint64_t)eval(node->lhs) < eval(node->rhs);
+ return eval(node->lhs) < eval(node->rhs);
+ case ND_LE:
+ if (node->lhs->ty->is_unsigned)
+ return (uint64_t)eval(node->lhs) <= eval(node->rhs);
+ return eval(node->lhs) <= eval(node->rhs);
+ case ND_COND:
+ return eval(node->cond) ? eval2(node->then, label) : eval2(node->els, label);
+ case ND_COMMA:
+ return eval2(node->rhs, label);
+ case ND_NOT:
+ return !eval(node->lhs);
+ case ND_BITNOT:
+ return ~eval(node->lhs);
+ case ND_LOGAND:
+ return eval(node->lhs) && eval(node->rhs);
+ case ND_LOGOR:
+ return eval(node->lhs) || eval(node->rhs);
+ case ND_CAST: {
+ int64_t val = eval2(node->lhs, label);
+ if (is_integer(node->ty)) {
+ switch (node->ty->size) {
+ case 1: return node->ty->is_unsigned ? (uint8_t)val : (int8_t)val;
+ case 2: return node->ty->is_unsigned ? (uint16_t)val : (int16_t)val;
+ case 4: return node->ty->is_unsigned ? (uint32_t)val : (int32_t)val;
+ }
+ }
+ return val;
+ }
+ case ND_ADDR:
+ return eval_rval(node->lhs, label);
+ case ND_LABEL_VAL:
+ *label = &node->unique_label;
+ return 0;
+ case ND_MEMBER:
+ if (!label)
+ error_tok(node->tok, "not a compile-time constant");
+ if (node->ty->kind != TY_ARRAY)
+ error_tok(node->tok, "invalid initializer");
+ return eval_rval(node->lhs, label) + node->member->offset;
+ case ND_VAR:
+ if (!label)
+ error_tok(node->tok, "not a compile-time constant");
+ if (node->var->ty->kind != TY_ARRAY && node->var->ty->kind != TY_FUNC)
+ error_tok(node->tok, "invalid initializer");
+ *label = &node->var->name;
+ return 0;
+ case ND_NUM:
+ return node->val;
+ }
+
+ error_tok(node->tok, "not a compile-time constant");
+}
+
+static int64_t eval_rval(Node *node, char ***label) {
+ switch (node->kind) {
+ case ND_VAR:
+ if (node->var->is_local)
+ error_tok(node->tok, "not a compile-time constant");
+ *label = &node->var->name;
+ return 0;
+ case ND_DEREF:
+ return eval2(node->lhs, label);
+ case ND_MEMBER:
+ return eval_rval(node->lhs, label) + node->member->offset;
+ }
+
+ error_tok(node->tok, "invalid initializer");
+}
+
+static bool is_const_expr(Node *node) {
+ add_type(node);
+
+ switch (node->kind) {
+ case ND_ADD:
+ case ND_SUB:
+ case ND_MUL:
+ case ND_DIV:
+ case ND_MOD:
+ case ND_BITAND:
+ case ND_BITOR:
+ case ND_BITXOR:
+ case ND_SHL:
+ case ND_SHR:
+ case ND_EQ:
+ case ND_NE:
+ case ND_LT:
+ case ND_LE:
+ case ND_LOGAND:
+ case ND_LOGOR:
+ return is_const_expr(node->lhs) && is_const_expr(node->rhs);
+ case ND_COND:
+ if (!is_const_expr(node->cond))
+ return false;
+ return is_const_expr(eval(node->cond) ? node->then : node->els);
+ case ND_COMMA:
+ return is_const_expr(node->rhs);
+ case ND_NEG:
+ case ND_NOT:
+ case ND_BITNOT:
+ case ND_CAST:
+ return is_const_expr(node->lhs);
+ case ND_NUM:
+ return true;
+ }
+
+ return false;
+}
+
+int64_t const_expr(Token **rest, Token *tok) {
+ Node *node = conditional(rest, tok);
+ return eval(node);
+}
+
+static double eval_double(Node *node) {
+ add_type(node);
+
+ if (is_integer(node->ty)) {
+ if (node->ty->is_unsigned)
+ return (unsigned long)eval(node);
+ return eval(node);
+ }
+
+ switch (node->kind) {
+ case ND_ADD:
+ return eval_double(node->lhs) + eval_double(node->rhs);
+ case ND_SUB:
+ return eval_double(node->lhs) - eval_double(node->rhs);
+ case ND_MUL:
+ return eval_double(node->lhs) * eval_double(node->rhs);
+ case ND_DIV:
+ return eval_double(node->lhs) / eval_double(node->rhs);
+ case ND_NEG:
+ return -eval_double(node->lhs);
+ case ND_COND:
+ return eval_double(node->cond) ? eval_double(node->then) : eval_double(node->els);
+ case ND_COMMA:
+ return eval_double(node->rhs);
+ case ND_CAST:
+ if (is_flonum(node->lhs->ty))
+ return eval_double(node->lhs);
+ return eval(node->lhs);
+ case ND_NUM:
+ return node->fval;
+ }
+
+ error_tok(node->tok, "not a compile-time constant");
+}
+
+// Convert op= operators to expressions containing an assignment.
+//
+// In general, `A op= C` is converted to ``tmp = &A, *tmp = *tmp op B`.
+// However, if a given expression is of form `A.x op= C`, the input is
+// converted to `tmp = &A, (*tmp).x = (*tmp).x op C` to handle assignments
+// to bitfields.
+static Node *to_assign(Node *binary) {
+ add_type(binary->lhs);
+ add_type(binary->rhs);
+ Token *tok = binary->tok;
+
+ // Convert `A.x op= C` to `tmp = &A, (*tmp).x = (*tmp).x op C`.
+ if (binary->lhs->kind == ND_MEMBER) {
+ Obj *var = new_lvar("", pointer_to(binary->lhs->lhs->ty));
+
+ Node *expr1 = new_binary(ND_ASSIGN, new_var_node(var, tok),
+ new_unary(ND_ADDR, binary->lhs->lhs, tok), tok);
+
+ Node *expr2 = new_unary(ND_MEMBER,
+ new_unary(ND_DEREF, new_var_node(var, tok), tok),
+ tok);
+ expr2->member = binary->lhs->member;
+
+ Node *expr3 = new_unary(ND_MEMBER,
+ new_unary(ND_DEREF, new_var_node(var, tok), tok),
+ tok);
+ expr3->member = binary->lhs->member;
+
+ Node *expr4 = new_binary(ND_ASSIGN, expr2,
+ new_binary(binary->kind, expr3, binary->rhs, tok),
+ tok);
+
+ return new_binary(ND_COMMA, expr1, expr4, tok);
+ }
+
+ // If A is an atomic type, Convert `A op= B` to
+ //
+ // ({
+ // T1 *addr = &A; T2 val = (B); T1 old = *addr; T1 new;
+ // do {
+ // new = old op val;
+ // } while (!atomic_compare_exchange_strong(addr, &old, new));
+ // new;
+ // })
+ if (binary->lhs->ty->is_atomic) {
+ Node head = {};
+ Node *cur = &head;
+
+ Obj *addr = new_lvar("", pointer_to(binary->lhs->ty));
+ Obj *val = new_lvar("", binary->rhs->ty);
+ Obj *old = new_lvar("", binary->lhs->ty);
+ Obj *new = new_lvar("", binary->lhs->ty);
+
+ cur = cur->next =
+ new_unary(ND_EXPR_STMT,
+ new_binary(ND_ASSIGN, new_var_node(addr, tok),
+ new_unary(ND_ADDR, binary->lhs, tok), tok),
+ tok);
+
+ cur = cur->next =
+ new_unary(ND_EXPR_STMT,
+ new_binary(ND_ASSIGN, new_var_node(val, tok), binary->rhs, tok),
+ tok);
+
+ cur = cur->next =
+ new_unary(ND_EXPR_STMT,
+ new_binary(ND_ASSIGN, new_var_node(old, tok),
+ new_unary(ND_DEREF, new_var_node(addr, tok), tok), tok),
+ tok);
+
+ Node *loop = new_node(ND_DO, tok);
+ loop->brk_label = new_unique_name();
+ loop->cont_label = new_unique_name();
+
+ Node *body = new_binary(ND_ASSIGN,
+ new_var_node(new, tok),
+ new_binary(binary->kind, new_var_node(old, tok),
+ new_var_node(val, tok), tok),
+ tok);
+
+ loop->then = new_node(ND_BLOCK, tok);
+ loop->then->body = new_unary(ND_EXPR_STMT, body, tok);
+
+ Node *cas = new_node(ND_CAS, tok);
+ cas->cas_addr = new_var_node(addr, tok);
+ cas->cas_old = new_unary(ND_ADDR, new_var_node(old, tok), tok);
+ cas->cas_new = new_var_node(new, tok);
+ loop->cond = new_unary(ND_NOT, cas, tok);
+
+ cur = cur->next = loop;
+ cur = cur->next = new_unary(ND_EXPR_STMT, new_var_node(new, tok), tok);
+
+ Node *node = new_node(ND_STMT_EXPR, tok);
+ node->body = head.next;
+ return node;
+ }
+
+ // Convert `A op= B` to ``tmp = &A, *tmp = *tmp op B`.
+ Obj *var = new_lvar("", pointer_to(binary->lhs->ty));
+
+ Node *expr1 = new_binary(ND_ASSIGN, new_var_node(var, tok),
+ new_unary(ND_ADDR, binary->lhs, tok), tok);
+
+ Node *expr2 =
+ new_binary(ND_ASSIGN,
+ new_unary(ND_DEREF, new_var_node(var, tok), tok),
+ new_binary(binary->kind,
+ new_unary(ND_DEREF, new_var_node(var, tok), tok),
+ binary->rhs,
+ tok),
+ tok);
+
+ return new_binary(ND_COMMA, expr1, expr2, tok);
+}
+
+// assign = conditional (assign-op assign)?
+// assign-op = "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "&=" | "|=" | "^="
+// | "<<=" | ">>="
+static Node *assign(Token **rest, Token *tok) {
+ Node *node = conditional(&tok, tok);
+
+ if (equal(tok, "="))
+ return new_binary(ND_ASSIGN, node, assign(rest, tok->next), tok);
+
+ if (equal(tok, "+="))
+ return to_assign(new_add(node, assign(rest, tok->next), tok));
+
+ if (equal(tok, "-="))
+ return to_assign(new_sub(node, assign(rest, tok->next), tok));
+
+ if (equal(tok, "*="))
+ return to_assign(new_binary(ND_MUL, node, assign(rest, tok->next), tok));
+
+ if (equal(tok, "/="))
+ return to_assign(new_binary(ND_DIV, node, assign(rest, tok->next), tok));
+
+ if (equal(tok, "%="))
+ return to_assign(new_binary(ND_MOD, node, assign(rest, tok->next), tok));
+
+ if (equal(tok, "&="))
+ return to_assign(new_binary(ND_BITAND, node, assign(rest, tok->next), tok));
+
+ if (equal(tok, "|="))
+ return to_assign(new_binary(ND_BITOR, node, assign(rest, tok->next), tok));
+
+ if (equal(tok, "^="))
+ return to_assign(new_binary(ND_BITXOR, node, assign(rest, tok->next), tok));
+
+ if (equal(tok, "<<="))
+ return to_assign(new_binary(ND_SHL, node, assign(rest, tok->next), tok));
+
+ if (equal(tok, ">>="))
+ return to_assign(new_binary(ND_SHR, node, assign(rest, tok->next), tok));
+
+ *rest = tok;
+ return node;
+}
+
+// conditional = logor ("?" expr? ":" conditional)?
+static Node *conditional(Token **rest, Token *tok) {
+ Node *cond = logor(&tok, tok);
+
+ if (!equal(tok, "?")) {
+ *rest = tok;
+ return cond;
+ }
+
+ if (equal(tok->next, ":")) {
+ // [GNU] Compile `a ?: b` as `tmp = a, tmp ? tmp : b`.
+ add_type(cond);
+ Obj *var = new_lvar("", cond->ty);
+ Node *lhs = new_binary(ND_ASSIGN, new_var_node(var, tok), cond, tok);
+ Node *rhs = new_node(ND_COND, tok);
+ rhs->cond = new_var_node(var, tok);
+ rhs->then = new_var_node(var, tok);
+ rhs->els = conditional(rest, tok->next->next);
+ return new_binary(ND_COMMA, lhs, rhs, tok);
+ }
+
+ Node *node = new_node(ND_COND, tok);
+ node->cond = cond;
+ node->then = expr(&tok, tok->next);
+ tok = skip(tok, ":");
+ node->els = conditional(rest, tok);
+ return node;
+}
+
+// logor = logand ("||" logand)*
+static Node *logor(Token **rest, Token *tok) {
+ Node *node = logand(&tok, tok);
+ while (equal(tok, "||")) {
+ Token *start = tok;
+ node = new_binary(ND_LOGOR, node, logand(&tok, tok->next), start);
+ }
+ *rest = tok;
+ return node;
+}
+
+// logand = bitor ("&&" bitor)*
+static Node *logand(Token **rest, Token *tok) {
+ Node *node = bitor(&tok, tok);
+ while (equal(tok, "&&")) {
+ Token *start = tok;
+ node = new_binary(ND_LOGAND, node, bitor(&tok, tok->next), start);
+ }
+ *rest = tok;
+ return node;
+}
+
+// bitor = bitxor ("|" bitxor)*
+static Node *bitor(Token **rest, Token *tok) {
+ Node *node = bitxor(&tok, tok);
+ while (equal(tok, "|")) {
+ Token *start = tok;
+ node = new_binary(ND_BITOR, node, bitxor(&tok, tok->next), start);
+ }
+ *rest = tok;
+ return node;
+}
+
+// bitxor = bitand ("^" bitand)*
+static Node *bitxor(Token **rest, Token *tok) {
+ Node *node = bitand(&tok, tok);
+ while (equal(tok, "^")) {
+ Token *start = tok;
+ node = new_binary(ND_BITXOR, node, bitand(&tok, tok->next), start);
+ }
+ *rest = tok;
+ return node;
+}
+
+// bitand = equality ("&" equality)*
+static Node *bitand(Token **rest, Token *tok) {
+ Node *node = equality(&tok, tok);
+ while (equal(tok, "&")) {
+ Token *start = tok;
+ node = new_binary(ND_BITAND, node, equality(&tok, tok->next), start);
+ }
+ *rest = tok;
+ return node;
+}
+
+// equality = relational ("==" relational | "!=" relational)*
+static Node *equality(Token **rest, Token *tok) {
+ Node *node = relational(&tok, tok);
+
+ for (;;) {
+ Token *start = tok;
+
+ if (equal(tok, "==")) {
+ node = new_binary(ND_EQ, node, relational(&tok, tok->next), start);
+ continue;
+ }
+
+ if (equal(tok, "!=")) {
+ node = new_binary(ND_NE, node, relational(&tok, tok->next), start);
+ continue;
+ }
+
+ *rest = tok;
+ return node;
+ }
+}
+
+// relational = shift ("<" shift | "<=" shift | ">" shift | ">=" shift)*
+static Node *relational(Token **rest, Token *tok) {
+ Node *node = shift(&tok, tok);
+
+ for (;;) {
+ Token *start = tok;
+
+ if (equal(tok, "<")) {
+ node = new_binary(ND_LT, node, shift(&tok, tok->next), start);
+ continue;
+ }
+
+ if (equal(tok, "<=")) {
+ node = new_binary(ND_LE, node, shift(&tok, tok->next), start);
+ continue;
+ }
+
+ if (equal(tok, ">")) {
+ node = new_binary(ND_LT, shift(&tok, tok->next), node, start);
+ continue;
+ }
+
+ if (equal(tok, ">=")) {
+ node = new_binary(ND_LE, shift(&tok, tok->next), node, start);
+ continue;
+ }
+
+ *rest = tok;
+ return node;
+ }
+}
+
+// shift = add ("<<" add | ">>" add)*
+static Node *shift(Token **rest, Token *tok) {
+ Node *node = add(&tok, tok);
+
+ for (;;) {
+ Token *start = tok;
+
+ if (equal(tok, "<<")) {
+ node = new_binary(ND_SHL, node, add(&tok, tok->next), start);
+ continue;
+ }
+
+ if (equal(tok, ">>")) {
+ node = new_binary(ND_SHR, node, add(&tok, tok->next), start);
+ continue;
+ }
+
+ *rest = tok;
+ return node;
+ }
+}
+
+// In C, `+` operator is overloaded to perform the pointer arithmetic.
+// If p is a pointer, p+n adds not n but sizeof(*p)*n to the value of p,
+// so that p+n points to the location n elements (not bytes) ahead of p.
+// In other words, we need to scale an integer value before adding to a
+// pointer value. This function takes care of the scaling.
+static Node *new_add(Node *lhs, Node *rhs, Token *tok) {
+ add_type(lhs);
+ add_type(rhs);
+
+ // num + num
+ if (is_numeric(lhs->ty) && is_numeric(rhs->ty))
+ return new_binary(ND_ADD, lhs, rhs, tok);
+
+ if (lhs->ty->base && rhs->ty->base)
+ error_tok(tok, "invalid operands");
+
+ // Canonicalize `num + ptr` to `ptr + num`.
+ if (!lhs->ty->base && rhs->ty->base) {
+ Node *tmp = lhs;
+ lhs = rhs;
+ rhs = tmp;
+ }
+
+ // VLA + num
+ if (lhs->ty->base->kind == TY_VLA) {
+ rhs = new_binary(ND_MUL, rhs, new_var_node(lhs->ty->base->vla_size, tok), tok);
+ return new_binary(ND_ADD, lhs, rhs, tok);
+ }
+
+ // ptr + num
+ rhs = new_binary(ND_MUL, rhs, new_long(lhs->ty->base->size, tok), tok);
+ return new_binary(ND_ADD, lhs, rhs, tok);
+}
+
+// Like `+`, `-` is overloaded for the pointer type.
+static Node *new_sub(Node *lhs, Node *rhs, Token *tok) {
+ add_type(lhs);
+ add_type(rhs);
+
+ // num - num
+ if (is_numeric(lhs->ty) && is_numeric(rhs->ty))
+ return new_binary(ND_SUB, lhs, rhs, tok);
+
+ // VLA + num
+ if (lhs->ty->base->kind == TY_VLA) {
+ rhs = new_binary(ND_MUL, rhs, new_var_node(lhs->ty->base->vla_size, tok), tok);
+ add_type(rhs);
+ Node *node = new_binary(ND_SUB, lhs, rhs, tok);
+ node->ty = lhs->ty;
+ return node;
+ }
+
+ // ptr - num
+ if (lhs->ty->base && is_integer(rhs->ty)) {
+ rhs = new_binary(ND_MUL, rhs, new_long(lhs->ty->base->size, tok), tok);
+ add_type(rhs);
+ Node *node = new_binary(ND_SUB, lhs, rhs, tok);
+ node->ty = lhs->ty;
+ return node;
+ }
+
+ // ptr - ptr, which returns how many elements are between the two.
+ if (lhs->ty->base && rhs->ty->base) {
+ Node *node = new_binary(ND_SUB, lhs, rhs, tok);
+ node->ty = ty_long;
+ return new_binary(ND_DIV, node, new_num(lhs->ty->base->size, tok), tok);
+ }
+
+ error_tok(tok, "invalid operands");
+}
+
+// add = mul ("+" mul | "-" mul)*
+static Node *add(Token **rest, Token *tok) {
+ Node *node = mul(&tok, tok);
+
+ for (;;) {
+ Token *start = tok;
+
+ if (equal(tok, "+")) {
+ node = new_add(node, mul(&tok, tok->next), start);
+ continue;
+ }
+
+ if (equal(tok, "-")) {
+ node = new_sub(node, mul(&tok, tok->next), start);
+ continue;
+ }
+
+ *rest = tok;
+ return node;
+ }
+}
+
+// mul = cast ("*" cast | "/" cast | "%" cast)*
+static Node *mul(Token **rest, Token *tok) {
+ Node *node = cast(&tok, tok);
+
+ for (;;) {
+ Token *start = tok;
+
+ if (equal(tok, "*")) {
+ node = new_binary(ND_MUL, node, cast(&tok, tok->next), start);
+ continue;
+ }
+
+ if (equal(tok, "/")) {
+ node = new_binary(ND_DIV, node, cast(&tok, tok->next), start);
+ continue;
+ }
+
+ if (equal(tok, "%")) {
+ node = new_binary(ND_MOD, node, cast(&tok, tok->next), start);
+ continue;
+ }
+
+ *rest = tok;
+ return node;
+ }
+}
+
+// cast = "(" type-name ")" cast | unary
+static Node *cast(Token **rest, Token *tok) {
+ if (equal(tok, "(") && is_typename(tok->next)) {
+ Token *start = tok;
+ Type *ty = typename(&tok, tok->next);
+ tok = skip(tok, ")");
+
+ // compound literal
+ if (equal(tok, "{"))
+ return unary(rest, start);
+
+ // type cast
+ Node *node = new_cast(cast(rest, tok), ty);
+ node->tok = start;
+ return node;
+ }
+
+ return unary(rest, tok);
+}
+
+// unary = ("+" | "-" | "*" | "&" | "!" | "~") cast
+// | ("++" | "--") unary
+// | "&&" ident
+// | postfix
+static Node *unary(Token **rest, Token *tok) {
+ if (equal(tok, "+"))
+ return cast(rest, tok->next);
+
+ if (equal(tok, "-"))
+ return new_unary(ND_NEG, cast(rest, tok->next), tok);
+
+ if (equal(tok, "&")) {
+ Node *lhs = cast(rest, tok->next);
+ add_type(lhs);
+ if (lhs->kind == ND_MEMBER && lhs->member->is_bitfield)
+ error_tok(tok, "cannot take address of bitfield");
+ return new_unary(ND_ADDR, lhs, tok);
+ }
+
+ if (equal(tok, "*")) {
+ // [https://www.sigbus.info/n1570#6.5.3.2p4] This is an oddity
+ // in the C spec, but dereferencing a function shouldn't do
+ // anything. If foo is a function, `*foo`, `**foo` or `*****foo`
+ // are all equivalent to just `foo`.
+ Node *node = cast(rest, tok->next);
+ add_type(node);
+ if (node->ty->kind == TY_FUNC)
+ return node;
+ return new_unary(ND_DEREF, node, tok);
+ }
+
+ if (equal(tok, "!"))
+ return new_unary(ND_NOT, cast(rest, tok->next), tok);
+
+ if (equal(tok, "~"))
+ return new_unary(ND_BITNOT, cast(rest, tok->next), tok);
+
+ // Read ++i as i+=1
+ if (equal(tok, "++"))
+ return to_assign(new_add(unary(rest, tok->next), new_num(1, tok), tok));
+
+ // Read --i as i-=1
+ if (equal(tok, "--"))
+ return to_assign(new_sub(unary(rest, tok->next), new_num(1, tok), tok));
+
+ // [GNU] labels-as-values
+ if (equal(tok, "&&")) {
+ Node *node = new_node(ND_LABEL_VAL, tok);
+ node->label = get_ident(tok->next);
+ node->goto_next = gotos;
+ gotos = node;
+ *rest = tok->next->next;
+ return node;
+ }
+
+ return postfix(rest, tok);
+}
+
+// struct-members = (declspec declarator ("," declarator)* ";")*
+static void struct_members(Token **rest, Token *tok, Type *ty) {
+ Member head = {};
+ Member *cur = &head;
+ int idx = 0;
+
+ while (!equal(tok, "}")) {
+ VarAttr attr = {};
+ Type *basety = declspec(&tok, tok, &attr);
+ bool first = true;
+
+ // Anonymous struct member
+ if ((basety->kind == TY_STRUCT || basety->kind == TY_UNION) &&
+ consume(&tok, tok, ";")) {
+ Member *mem = calloc(1, sizeof(Member));
+ mem->ty = basety;
+ mem->idx = idx++;
+ mem->align = attr.align ? attr.align : mem->ty->align;
+ cur = cur->next = mem;
+ continue;
+ }
+
+ // Regular struct members
+ while (!consume(&tok, tok, ";")) {
+ if (!first)
+ tok = skip(tok, ",");
+ first = false;
+
+ Member *mem = calloc(1, sizeof(Member));
+ mem->ty = declarator(&tok, tok, basety);
+ mem->name = mem->ty->name;
+ mem->idx = idx++;
+ mem->align = attr.align ? attr.align : mem->ty->align;
+
+ if (consume(&tok, tok, ":")) {
+ mem->is_bitfield = true;
+ mem->bit_width = const_expr(&tok, tok);
+ }
+
+ cur = cur->next = mem;
+ }
+ }
+
+ // If the last element is an array of incomplete type, it's
+ // called a "flexible array member". It should behave as if
+ // if were a zero-sized array.
+ if (cur != &head && cur->ty->kind == TY_ARRAY && cur->ty->array_len < 0) {
+ cur->ty = array_of(cur->ty->base, 0);
+ ty->is_flexible = true;
+ }
+
+ *rest = tok->next;
+ ty->members = head.next;
+}
+
+// attribute = ("__attribute__" "(" "(" "packed" ")" ")")*
+static Token *attribute_list(Token *tok, Type *ty) {
+ while (consume(&tok, tok, "__attribute__")) {
+ tok = skip(tok, "(");
+ tok = skip(tok, "(");
+
+ bool first = true;
+
+ while (!consume(&tok, tok, ")")) {
+ if (!first)
+ tok = skip(tok, ",");
+ first = false;
+
+ if (consume(&tok, tok, "packed")) {
+ ty->is_packed = true;
+ continue;
+ }
+
+ if (consume(&tok, tok, "aligned")) {
+ tok = skip(tok, "(");
+ ty->align = const_expr(&tok, tok);
+ tok = skip(tok, ")");
+ continue;
+ }
+
+ error_tok(tok, "unknown attribute");
+ }
+
+ tok = skip(tok, ")");
+ }
+
+ return tok;
+}
+
+// struct-union-decl = attribute? ident? ("{" struct-members)?
+static Type *struct_union_decl(Token **rest, Token *tok) {
+ Type *ty = struct_type();
+ tok = attribute_list(tok, ty);
+
+ // Read a tag.
+ Token *tag = NULL;
+ if (tok->kind == TK_IDENT) {
+ tag = tok;
+ tok = tok->next;
+ }
+
+ if (tag && !equal(tok, "{")) {
+ *rest = tok;
+
+ Type *ty2 = find_tag(tag);
+ if (ty2)
+ return ty2;
+
+ ty->size = -1;
+ push_tag_scope(tag, ty);
+ return ty;
+ }
+
+ tok = skip(tok, "{");
+
+ // Construct a struct object.
+ struct_members(&tok, tok, ty);
+ *rest = attribute_list(tok, ty);
+
+ if (tag) {
+ // If this is a redefinition, overwrite a previous type.
+ // Otherwise, register the struct type.
+ Type *ty2 = hashmap_get2(&scope->tags, tag->loc, tag->len);
+ if (ty2) {
+ *ty2 = *ty;
+ return ty2;
+ }
+
+ push_tag_scope(tag, ty);
+ }
+
+ return ty;
+}
+
+// struct-decl = struct-union-decl
+static Type *struct_decl(Token **rest, Token *tok) {
+ Type *ty = struct_union_decl(rest, tok);
+ ty->kind = TY_STRUCT;
+
+ if (ty->size < 0)
+ return ty;
+
+ // Assign offsets within the struct to members.
+ int bits = 0;
+
+ for (Member *mem = ty->members; mem; mem = mem->next) {
+ if (mem->is_bitfield && mem->bit_width == 0) {
+ // Zero-width anonymous bitfield has a special meaning.
+ // It affects only alignment.
+ bits = align_to(bits, mem->ty->size * 8);
+ } else if (mem->is_bitfield) {
+ int sz = mem->ty->size;
+ if (bits / (sz * 8) != (bits + mem->bit_width - 1) / (sz * 8))
+ bits = align_to(bits, sz * 8);
+
+ mem->offset = align_down(bits / 8, sz);
+ mem->bit_offset = bits % (sz * 8);
+ bits += mem->bit_width;
+ } else {
+ if (!ty->is_packed)
+ bits = align_to(bits, mem->align * 8);
+ mem->offset = bits / 8;
+ bits += mem->ty->size * 8;
+ }
+
+ if (!ty->is_packed && ty->align < mem->align)
+ ty->align = mem->align;
+ }
+
+ ty->size = align_to(bits, ty->align * 8) / 8;
+ return ty;
+}
+
+// union-decl = struct-union-decl
+static Type *union_decl(Token **rest, Token *tok) {
+ Type *ty = struct_union_decl(rest, tok);
+ ty->kind = TY_UNION;
+
+ if (ty->size < 0)
+ return ty;
+
+ // If union, we don't have to assign offsets because they
+ // are already initialized to zero. We need to compute the
+ // alignment and the size though.
+ for (Member *mem = ty->members; mem; mem = mem->next) {
+ if (ty->align < mem->align)
+ ty->align = mem->align;
+ if (ty->size < mem->ty->size)
+ ty->size = mem->ty->size;
+ }
+ ty->size = align_to(ty->size, ty->align);
+ return ty;
+}
+
+// Find a struct member by name.
+static Member *get_struct_member(Type *ty, Token *tok) {
+ for (Member *mem = ty->members; mem; mem = mem->next) {
+ // Anonymous struct member
+ if ((mem->ty->kind == TY_STRUCT || mem->ty->kind == TY_UNION) &&
+ !mem->name) {
+ if (get_struct_member(mem->ty, tok))
+ return mem;
+ continue;
+ }
+
+ // Regular struct member
+ if (mem->name->len == tok->len &&
+ !strncmp(mem->name->loc, tok->loc, tok->len))
+ return mem;
+ }
+ return NULL;
+}
+
+// Create a node representing a struct member access, such as foo.bar
+// where foo is a struct and bar is a member name.
+//
+// C has a feature called "anonymous struct" which allows a struct to
+// have another unnamed struct as a member like this:
+//
+// struct { struct { int a; }; int b; } x;
+//
+// The members of an anonymous struct belong to the outer struct's
+// member namespace. Therefore, in the above example, you can access
+// member "a" of the anonymous struct as "x.a".
+//
+// This function takes care of anonymous structs.
+static Node *struct_ref(Node *node, Token *tok) {
+ add_type(node);
+ if (node->ty->kind != TY_STRUCT && node->ty->kind != TY_UNION)
+ error_tok(node->tok, "not a struct nor a union");
+
+ Type *ty = node->ty;
+
+ for (;;) {
+ Member *mem = get_struct_member(ty, tok);
+ if (!mem)
+ error_tok(tok, "no such member");
+ node = new_unary(ND_MEMBER, node, tok);
+ node->member = mem;
+ if (mem->name)
+ break;
+ ty = mem->ty;
+ }
+ return node;
+}
+
+// Convert A++ to `(typeof A)((A += 1) - 1)`
+static Node *new_inc_dec(Node *node, Token *tok, int addend) {
+ add_type(node);
+ return new_cast(new_add(to_assign(new_add(node, new_num(addend, tok), tok)),
+ new_num(-addend, tok), tok),
+ node->ty);
+}
+
+// postfix = "(" type-name ")" "{" initializer-list "}"
+// = ident "(" func-args ")" postfix-tail*
+// | primary postfix-tail*
+//
+// postfix-tail = "[" expr "]"
+// | "(" func-args ")"
+// | "." ident
+// | "->" ident
+// | "++"
+// | "--"
+static Node *postfix(Token **rest, Token *tok) {
+ if (equal(tok, "(") && is_typename(tok->next)) {
+ // Compound literal
+ Token *start = tok;
+ Type *ty = typename(&tok, tok->next);
+ tok = skip(tok, ")");
+
+ if (scope->next == NULL) {
+ Obj *var = new_anon_gvar(ty);
+ gvar_initializer(rest, tok, var);
+ return new_var_node(var, start);
+ }
+
+ Obj *var = new_lvar("", ty);
+ Node *lhs = lvar_initializer(rest, tok, var);
+ Node *rhs = new_var_node(var, tok);
+ return new_binary(ND_COMMA, lhs, rhs, start);
+ }
+
+ Node *node = primary(&tok, tok);
+
+ for (;;) {
+ if (equal(tok, "(")) {
+ node = funcall(&tok, tok->next, node);
+ continue;
+ }
+
+ if (equal(tok, "[")) {
+ // x[y] is short for *(x+y)
+ Token *start = tok;
+ Node *idx = expr(&tok, tok->next);
+ tok = skip(tok, "]");
+ node = new_unary(ND_DEREF, new_add(node, idx, start), start);
+ continue;
+ }
+
+ if (equal(tok, ".")) {
+ node = struct_ref(node, tok->next);
+ tok = tok->next->next;
+ continue;
+ }
+
+ if (equal(tok, "->")) {
+ // x->y is short for (*x).y
+ node = new_unary(ND_DEREF, node, tok);
+ node = struct_ref(node, tok->next);
+ tok = tok->next->next;
+ continue;
+ }
+
+ if (equal(tok, "++")) {
+ node = new_inc_dec(node, tok, 1);
+ tok = tok->next;
+ continue;
+ }
+
+ if (equal(tok, "--")) {
+ node = new_inc_dec(node, tok, -1);
+ tok = tok->next;
+ continue;
+ }
+
+ *rest = tok;
+ return node;
+ }
+}
+
+// funcall = (assign ("," assign)*)? ")"
+static Node *funcall(Token **rest, Token *tok, Node *fn) {
+ add_type(fn);
+
+ if (fn->ty->kind != TY_FUNC &&
+ (fn->ty->kind != TY_PTR || fn->ty->base->kind != TY_FUNC))
+ error_tok(fn->tok, "not a function");
+
+ Type *ty = (fn->ty->kind == TY_FUNC) ? fn->ty : fn->ty->base;
+ Type *param_ty = ty->params;
+
+ Node head = {};
+ Node *cur = &head;
+
+ while (!equal(tok, ")")) {
+ if (cur != &head)
+ tok = skip(tok, ",");
+
+ Node *arg = assign(&tok, tok);
+ add_type(arg);
+
+ if (!param_ty && !ty->is_variadic)
+ error_tok(tok, "too many arguments");
+
+ if (param_ty) {
+ if (param_ty->kind != TY_STRUCT && param_ty->kind != TY_UNION)
+ arg = new_cast(arg, param_ty);
+ param_ty = param_ty->next;
+ } else if (arg->ty->kind == TY_FLOAT) {
+ // If parameter type is omitted (e.g. in "..."), float
+ // arguments are promoted to double.
+ arg = new_cast(arg, ty_double);
+ }
+
+ cur = cur->next = arg;
+ }
+
+ if (param_ty)
+ error_tok(tok, "too few arguments");
+
+ *rest = skip(tok, ")");
+
+ Node *node = new_unary(ND_FUNCALL, fn, tok);
+ node->func_ty = ty;
+ node->ty = ty->return_ty;
+ node->args = head.next;
+
+ // If a function returns a struct, it is caller's responsibility
+ // to allocate a space for the return value.
+ if (node->ty->kind == TY_STRUCT || node->ty->kind == TY_UNION)
+ node->ret_buffer = new_lvar("", node->ty);
+ return node;
+}
+
+// generic-selection = "(" assign "," generic-assoc ("," generic-assoc)* ")"
+//
+// generic-assoc = type-name ":" assign
+// | "default" ":" assign
+static Node *generic_selection(Token **rest, Token *tok) {
+ Token *start = tok;
+ tok = skip(tok, "(");
+
+ Node *ctrl = assign(&tok, tok);
+ add_type(ctrl);
+
+ Type *t1 = ctrl->ty;
+ if (t1->kind == TY_FUNC)
+ t1 = pointer_to(t1);
+ else if (t1->kind == TY_ARRAY)
+ t1 = pointer_to(t1->base);
+
+ Node *ret = NULL;
+
+ while (!consume(rest, tok, ")")) {
+ tok = skip(tok, ",");
+
+ if (equal(tok, "default")) {
+ tok = skip(tok->next, ":");
+ Node *node = assign(&tok, tok);
+ if (!ret)
+ ret = node;
+ continue;
+ }
+
+ Type *t2 = typename(&tok, tok);
+ tok = skip(tok, ":");
+ Node *node = assign(&tok, tok);
+ if (is_compatible(t1, t2))
+ ret = node;
+ }
+
+ if (!ret)
+ error_tok(start, "controlling expression type not compatible with"
+ " any generic association type");
+ return ret;
+}
+
+// primary = "(" "{" stmt+ "}" ")"
+// | "(" expr ")"
+// | "sizeof" "(" type-name ")"
+// | "sizeof" unary
+// | "_Alignof" "(" type-name ")"
+// | "_Alignof" unary
+// | "_Generic" generic-selection
+// | "__builtin_types_compatible_p" "(" type-name, type-name, ")"
+// | "__builtin_reg_class" "(" type-name ")"
+// | ident
+// | str
+// | num
+static Node *primary(Token **rest, Token *tok) {
+ Token *start = tok;
+
+ if (equal(tok, "(") && equal(tok->next, "{")) {
+ // This is a GNU statement expresssion.
+ Node *node = new_node(ND_STMT_EXPR, tok);
+ node->body = compound_stmt(&tok, tok->next->next)->body;
+ *rest = skip(tok, ")");
+ return node;
+ }
+
+ if (equal(tok, "(")) {
+ Node *node = expr(&tok, tok->next);
+ *rest = skip(tok, ")");
+ return node;
+ }
+
+ if (equal(tok, "sizeof") && equal(tok->next, "(") && is_typename(tok->next->next)) {
+ Type *ty = typename(&tok, tok->next->next);
+ *rest = skip(tok, ")");
+
+ if (ty->kind == TY_VLA) {
+ if (ty->vla_size)
+ return new_var_node(ty->vla_size, tok);
+
+ Node *lhs = compute_vla_size(ty, tok);
+ Node *rhs = new_var_node(ty->vla_size, tok);
+ return new_binary(ND_COMMA, lhs, rhs, tok);
+ }
+
+ return new_ulong(ty->size, start);
+ }
+
+ if (equal(tok, "sizeof")) {
+ Node *node = unary(rest, tok->next);
+ add_type(node);
+ if (node->ty->kind == TY_VLA)
+ return new_var_node(node->ty->vla_size, tok);
+ return new_ulong(node->ty->size, tok);
+ }
+
+ if (equal(tok, "_Alignof") && equal(tok->next, "(") && is_typename(tok->next->next)) {
+ Type *ty = typename(&tok, tok->next->next);
+ *rest = skip(tok, ")");
+ return new_ulong(ty->align, tok);
+ }
+
+ if (equal(tok, "_Alignof")) {
+ Node *node = unary(rest, tok->next);
+ add_type(node);
+ return new_ulong(node->ty->align, tok);
+ }
+
+ if (equal(tok, "_Generic"))
+ return generic_selection(rest, tok->next);
+
+ if (equal(tok, "__builtin_types_compatible_p")) {
+ tok = skip(tok->next, "(");
+ Type *t1 = typename(&tok, tok);
+ tok = skip(tok, ",");
+ Type *t2 = typename(&tok, tok);
+ *rest = skip(tok, ")");
+ return new_num(is_compatible(t1, t2), start);
+ }
+
+ if (equal(tok, "__builtin_reg_class")) {
+ tok = skip(tok->next, "(");
+ Type *ty = typename(&tok, tok);
+ *rest = skip(tok, ")");
+
+ if (is_integer(ty) || ty->kind == TY_PTR)
+ return new_num(0, start);
+ if (is_flonum(ty))
+ return new_num(1, start);
+ return new_num(2, start);
+ }
+
+ if (equal(tok, "__builtin_compare_and_swap")) {
+ Node *node = new_node(ND_CAS, tok);
+ tok = skip(tok->next, "(");
+ node->cas_addr = assign(&tok, tok);
+ tok = skip(tok, ",");
+ node->cas_old = assign(&tok, tok);
+ tok = skip(tok, ",");
+ node->cas_new = assign(&tok, tok);
+ *rest = skip(tok, ")");
+ return node;
+ }
+
+ if (equal(tok, "__builtin_atomic_exchange")) {
+ Node *node = new_node(ND_EXCH, tok);
+ tok = skip(tok->next, "(");
+ node->lhs = assign(&tok, tok);
+ tok = skip(tok, ",");
+ node->rhs = assign(&tok, tok);
+ *rest = skip(tok, ")");
+ return node;
+ }
+
+ if (tok->kind == TK_IDENT) {
+ // Variable or enum constant
+ VarScope *sc = find_var(tok);
+ *rest = tok->next;
+
+ // For "static inline" function
+ if (sc && sc->var && sc->var->is_function) {
+ if (current_fn)
+ strarray_push(&current_fn->refs, sc->var->name);
+ else
+ sc->var->is_root = true;
+ }
+
+ if (sc) {
+ if (sc->var)
+ return new_var_node(sc->var, tok);
+ if (sc->enum_ty)
+ return new_num(sc->enum_val, tok);
+ }
+
+ if (equal(tok->next, "("))
+ error_tok(tok, "implicit declaration of a function");
+ error_tok(tok, "undefined variable");
+ }
+
+ if (tok->kind == TK_STR) {
+ Obj *var = new_string_literal(tok->str, tok->ty);
+ *rest = tok->next;
+ return new_var_node(var, tok);
+ }
+
+ if (tok->kind == TK_NUM) {
+ Node *node;
+ if (is_flonum(tok->ty)) {
+ node = new_node(ND_NUM, tok);
+ node->fval = tok->fval;
+ } else {
+ node = new_num(tok->val, tok);
+ }
+
+ node->ty = tok->ty;
+ *rest = tok->next;
+ return node;
+ }
+
+ error_tok(tok, "expected an expression");
+}
+
+static Token *parse_typedef(Token *tok, Type *basety) {
+ bool first = true;
+
+ while (!consume(&tok, tok, ";")) {
+ if (!first)
+ tok = skip(tok, ",");
+ first = false;
+
+ Type *ty = declarator(&tok, tok, basety);
+ if (!ty->name)
+ error_tok(ty->name_pos, "typedef name omitted");
+ push_scope(get_ident(ty->name))->type_def = ty;
+ }
+ return tok;
+}
+
+static void create_param_lvars(Type *param) {
+ if (param) {
+ create_param_lvars(param->next);
+ if (!param->name)
+ error_tok(param->name_pos, "parameter name omitted");
+ new_lvar(get_ident(param->name), param);
+ }
+}
+
+// This function matches gotos or labels-as-values with labels.
+//
+// We cannot resolve gotos as we parse a function because gotos
+// can refer a label that appears later in the function.
+// So, we need to do this after we parse the entire function.
+static void resolve_goto_labels(void) {
+ for (Node *x = gotos; x; x = x->goto_next) {
+ for (Node *y = labels; y; y = y->goto_next) {
+ if (!strcmp(x->label, y->label)) {
+ x->unique_label = y->unique_label;
+ break;
+ }
+ }
+
+ if (x->unique_label == NULL)
+ error_tok(x->tok->next, "use of undeclared label");
+ }
+
+ gotos = labels = NULL;
+}
+
+static Obj *find_func(char *name) {
+ Scope *sc = scope;
+ while (sc->next)
+ sc = sc->next;
+
+ VarScope *sc2 = hashmap_get(&sc->vars, name);
+ if (sc2 && sc2->var && sc2->var->is_function)
+ return sc2->var;
+ return NULL;
+}
+
+static void mark_live(Obj *var) {
+ if (!var->is_function || var->is_live)
+ return;
+ var->is_live = true;
+
+ for (int i = 0; i < var->refs.len; i++) {
+ Obj *fn = find_func(var->refs.data[i]);
+ if (fn)
+ mark_live(fn);
+ }
+}
+
+static Token *function(Token *tok, Type *basety, VarAttr *attr) {
+ Type *ty = declarator(&tok, tok, basety);
+ if (!ty->name)
+ error_tok(ty->name_pos, "function name omitted");
+ char *name_str = get_ident(ty->name);
+
+ Obj *fn = find_func(name_str);
+ if (fn) {
+ // Redeclaration
+ if (!fn->is_function)
+ error_tok(tok, "redeclared as a different kind of symbol");
+ if (fn->is_definition && equal(tok, "{"))
+ /* MASOT error_tok(tok, "redefinition of %s", name_str) */;
+ if (!fn->is_static && attr->is_static)
+ error_tok(tok, "static declaration follows a non-static declaration");
+ fn->is_definition = fn->is_definition || equal(tok, "{");
+ fn->is_static = attr->is_static || (attr->is_inline && attr->is_extern);
+ } else {
+ fn = new_gvar(name_str, ty);
+ fn->is_function = true;
+ fn->is_definition = equal(tok, "{");
+ fn->is_static = attr->is_static || (attr->is_inline && !attr->is_extern);
+ fn->is_static = attr->is_static || (attr->is_inline && attr->is_extern);
+ fn->is_inline = attr->is_inline;
+ }
+
+ fn->is_root = !(fn->is_static && fn->is_inline);
+
+ if (consume(&tok, tok, ";"))
+ return tok;
+
+ current_fn = fn;
+ locals = NULL;
+ enter_scope();
+ create_param_lvars(ty->params);
+
+ // A buffer for a struct/union return value is passed
+ // as the hidden first parameter.
+ // Type *rty = ty->return_ty;
+ // if ((rty->kind == TY_STRUCT || rty->kind == TY_UNION) && rty->size > 16)
+ // new_lvar("", pointer_to(rty));
+
+ fn->params = locals;
+
+ if (ty->is_variadic)
+ fn->va_area = new_lvar("__va_area__", array_of(ty_char, 136));
+ fn->alloca_bottom = new_lvar("__alloca_size__", pointer_to(ty_char));
+
+ tok = skip(tok, "{");
+
+ // [https://www.sigbus.info/n1570#6.4.2.2p1] "__func__" is
+ // automatically defined as a local variable containing the
+ // current function name.
+ push_scope("__func__")->var =
+ new_string_literal(fn->name, array_of(ty_char, strlen(fn->name) + 1));
+
+ // [GNU] __FUNCTION__ is yet another name of __func__.
+ push_scope("__FUNCTION__")->var =
+ new_string_literal(fn->name, array_of(ty_char, strlen(fn->name) + 1));
+
+ fn->body = compound_stmt(&tok, tok);
+ fn->locals = locals;
+ leave_scope();
+ resolve_goto_labels();
+ return tok;
+}
+
+static Token *global_variable(Token *tok, Type *basety, VarAttr *attr) {
+ bool first = true;
+
+ while (!consume(&tok, tok, ";")) {
+ if (!first)
+ tok = skip(tok, ",");
+ first = false;
+
+ Type *ty = declarator(&tok, tok, basety);
+ if (!ty->name)
+ error_tok(ty->name_pos, "variable name omitted");
+
+ Obj *var = new_gvar(get_ident(ty->name), ty);
+ var->is_definition = !attr->is_extern;
+ var->is_static = attr->is_static;
+ var->is_tls = attr->is_tls;
+ if (attr->align)
+ var->align = attr->align;
+
+ if (equal(tok, "="))
+ gvar_initializer(&tok, tok->next, var);
+ else if (!attr->is_extern && !attr->is_tls)
+ var->is_tentative = true;
+ }
+ return tok;
+}
+
+// Lookahead tokens and returns true if a given token is a start
+// of a function definition or declaration.
+static bool is_function(Token *tok) {
+ if (equal(tok, ";"))
+ return false;
+
+ Type dummy = {};
+ Type *ty = declarator(&tok, tok, &dummy);
+ return ty->kind == TY_FUNC;
+}
+
+// Remove redundant tentative definitions.
+static void scan_globals(void) {
+ Obj head;
+ Obj *cur = &head;
+
+ for (Obj *var = globals; var; var = var->next) {
+ if (!var->is_tentative) {
+ cur = cur->next = var;
+ continue;
+ }
+
+ // Find another definition of the same identifier.
+ Obj *var2 = globals;
+ for (; var2; var2 = var2->next)
+ if (var != var2 && var2->is_definition && !strcmp(var->name, var2->name))
+ break;
+
+ // If there's another definition, the tentative definition
+ // is redundant
+ if (!var2)
+ cur = cur->next = var;
+ }
+
+ cur->next = NULL;
+ globals = head.next;
+}
+
+static void declare_builtin_functions(void) {
+ Type *ty = func_type(pointer_to(ty_void));
+ ty->params = copy_type(ty_int);
+ builtin_alloca = new_gvar("alloca", ty);
+ builtin_alloca->is_definition = false;
+}
+
+// program = (typedef | function-definition | global-variable)*
+Obj *parse(Token *tok) {
+ declare_builtin_functions();
+ globals = NULL;
+
+ while (tok->kind != TK_EOF) {
+ VarAttr attr = {};
+ Type *basety = declspec(&tok, tok, &attr);
+
+ // Typedef
+ if (attr.is_typedef) {
+ tok = parse_typedef(tok, basety);
+ continue;
+ }
+
+ // Function
+ if (is_function(tok)) {
+ tok = function(tok, basety, &attr);
+ continue;
+ }
+
+ // Global variable
+ tok = global_variable(tok, basety, &attr);
+ }
+
+ for (Obj *var = globals; var; var = var->next)
+ if (var->is_root)
+ mark_live(var);
+
+ // Remove redundant tentative definitions.
+ scan_globals();
+ return globals;
+}
diff --git a/preprocess.c b/preprocess.c
new file mode 100644
index 0000000..abc095f
--- /dev/null
+++ b/preprocess.c
@@ -0,0 +1,1151 @@
+// This file implements the C preprocessor.
+//
+// The preprocessor takes a list of tokens as an input and returns a
+// new list of tokens as an output.
+//
+// The preprocessing language is designed in such a way that that's
+// guaranteed to stop even if there is a recursive macro.
+// Informally speaking, a macro is applied only once for each token.
+// That is, if a macro token T appears in a result of direct or
+// indirect macro expansion of T, T won't be expanded any further.
+// For example, if T is defined as U, and U is defined as T, then
+// token T is expanded to U and then to T and the macro expansion
+// stops at that point.
+//
+// To achieve the above behavior, we attach for each token a set of
+// macro names from which the token is expanded. The set is called
+// "hideset". Hideset is initially empty, and every time we expand a
+// macro, the macro name is added to the resulting tokens' hidesets.
+//
+// The above macro expansion algorithm is explained in this document
+// written by Dave Prossor, which is used as a basis for the
+// standard's wording:
+// https://github.com/rui314/chibicc/wiki/cpp.algo.pdf
+
+#include "chibicc.h"
+
+typedef struct MacroParam MacroParam;
+struct MacroParam {
+ MacroParam *next;
+ char *name;
+};
+
+typedef struct MacroArg MacroArg;
+struct MacroArg {
+ MacroArg *next;
+ char *name;
+ bool is_va_args;
+ Token *tok;
+};
+
+typedef Token *macro_handler_fn(Token *);
+
+typedef struct Macro Macro;
+struct Macro {
+ char *name;
+ bool is_objlike; // Object-like or function-like
+ MacroParam *params;
+ char *va_args_name;
+ Token *body;
+ macro_handler_fn *handler;
+};
+
+// `#if` can be nested, so we use a stack to manage nested `#if`s.
+typedef struct CondIncl CondIncl;
+struct CondIncl {
+ CondIncl *next;
+ enum { IN_THEN, IN_ELIF, IN_ELSE } ctx;
+ Token *tok;
+ bool included;
+};
+
+typedef struct Hideset Hideset;
+struct Hideset {
+ Hideset *next;
+ char *name;
+};
+
+static HashMap macros;
+static CondIncl *cond_incl;
+static HashMap pragma_once;
+
+static Token *preprocess2(Token *tok);
+static Macro *find_macro(Token *tok);
+
+static bool is_hash(Token *tok) {
+ return tok->at_bol && equal(tok, "#");
+}
+
+// Some preprocessor directives such as #include allow extraneous
+// tokens before newline. This function skips such tokens.
+static Token *skip_line(Token *tok) {
+ if (tok->at_bol)
+ return tok;
+ warn_tok(tok, "extra token");
+ while (tok->at_bol)
+ tok = tok->next;
+ return tok;
+}
+
+static Token *copy_token(Token *tok) {
+ Token *t = calloc(1, sizeof(Token));
+ *t = *tok;
+ t->next = NULL;
+ return t;
+}
+
+static Token *new_eof(Token *tok) {
+ Token *t = copy_token(tok);
+ t->kind = TK_EOF;
+ t->len = 0;
+ return t;
+}
+
+static Hideset *new_hideset(char *name) {
+ Hideset *hs = calloc(1, sizeof(Hideset));
+ hs->name = name;
+ return hs;
+}
+
+static Hideset *hideset_union(Hideset *hs1, Hideset *hs2) {
+ Hideset head = {};
+ Hideset *cur = &head;
+
+ for (; hs1; hs1 = hs1->next)
+ cur = cur->next = new_hideset(hs1->name);
+ cur->next = hs2;
+ return head.next;
+}
+
+static bool hideset_contains(Hideset *hs, char *s, int len) {
+ for (; hs; hs = hs->next)
+ if (strlen(hs->name) == len && !strncmp(hs->name, s, len))
+ return true;
+ return false;
+}
+
+static Hideset *hideset_intersection(Hideset *hs1, Hideset *hs2) {
+ Hideset head = {};
+ Hideset *cur = &head;
+
+ for (; hs1; hs1 = hs1->next)
+ if (hideset_contains(hs2, hs1->name, strlen(hs1->name)))
+ cur = cur->next = new_hideset(hs1->name);
+ return head.next;
+}
+
+static Token *add_hideset(Token *tok, Hideset *hs) {
+ Token head = {};
+ Token *cur = &head;
+
+ for (; tok; tok = tok->next) {
+ Token *t = copy_token(tok);
+ t->hideset = hideset_union(t->hideset, hs);
+ cur = cur->next = t;
+ }
+ return head.next;
+}
+
+// Append tok2 to the end of tok1.
+static Token *append(Token *tok1, Token *tok2) {
+ if (tok1->kind == TK_EOF)
+ return tok2;
+
+ Token head = {};
+ Token *cur = &head;
+
+ for (; tok1->kind != TK_EOF; tok1 = tok1->next)
+ cur = cur->next = copy_token(tok1);
+ cur->next = tok2;
+ return head.next;
+}
+
+static Token *skip_cond_incl2(Token *tok) {
+ while (tok->kind != TK_EOF) {
+ if (is_hash(tok) &&
+ (equal(tok->next, "if") || equal(tok->next, "ifdef") ||
+ equal(tok->next, "ifndef"))) {
+ tok = skip_cond_incl2(tok->next->next);
+ continue;
+ }
+ if (is_hash(tok) && equal(tok->next, "endif"))
+ return tok->next->next;
+ tok = tok->next;
+ }
+ return tok;
+}
+
+// Skip until next `#else`, `#elif` or `#endif`.
+// Nested `#if` and `#endif` are skipped.
+static Token *skip_cond_incl(Token *tok) {
+ while (tok->kind != TK_EOF) {
+ if (is_hash(tok) &&
+ (equal(tok->next, "if") || equal(tok->next, "ifdef") ||
+ equal(tok->next, "ifndef"))) {
+ tok = skip_cond_incl2(tok->next->next);
+ continue;
+ }
+
+ if (is_hash(tok) &&
+ (equal(tok->next, "elif") || equal(tok->next, "else") ||
+ equal(tok->next, "endif")))
+ break;
+ tok = tok->next;
+ }
+ return tok;
+}
+
+// Double-quote a given string and returns it.
+static char *quote_string(char *str) {
+ int bufsize = 3;
+ for (int i = 0; str[i]; i++) {
+ if (str[i] == '\\' || str[i] == '"')
+ bufsize++;
+ bufsize++;
+ }
+
+ char *buf = calloc(1, bufsize);
+ char *p = buf;
+ *p++ = '"';
+ for (int i = 0; str[i]; i++) {
+ if (str[i] == '\\' || str[i] == '"')
+ *p++ = '\\';
+ *p++ = str[i];
+ }
+ *p++ = '"';
+ *p++ = '\0';
+ return buf;
+}
+
+static Token *new_str_token(char *str, Token *tmpl) {
+ char *buf = quote_string(str);
+ return tokenize(new_file(tmpl->file->name, tmpl->file->file_no, buf));
+}
+
+// Copy all tokens until the next newline, terminate them with
+// an EOF token and then returns them. This function is used to
+// create a new list of tokens for `#if` arguments.
+static Token *copy_line(Token **rest, Token *tok) {
+ Token head = {};
+ Token *cur = &head;
+
+ for (; !tok->at_bol; tok = tok->next)
+ cur = cur->next = copy_token(tok);
+
+ cur->next = new_eof(tok);
+ *rest = tok;
+ return head.next;
+}
+
+static Token *new_num_token(int val, Token *tmpl) {
+ char *buf = format("%d\n", val);
+ return tokenize(new_file(tmpl->file->name, tmpl->file->file_no, buf));
+}
+
+static Token *read_const_expr(Token **rest, Token *tok) {
+ tok = copy_line(rest, tok);
+
+ Token head = {};
+ Token *cur = &head;
+
+ while (tok->kind != TK_EOF) {
+ // "defined(foo)" or "defined foo" becomes "1" if macro "foo"
+ // is defined. Otherwise "0".
+ if (equal(tok, "defined")) {
+ Token *start = tok;
+ bool has_paren = consume(&tok, tok->next, "(");
+
+ if (tok->kind != TK_IDENT)
+ error_tok(start, "macro name must be an identifier");
+ Macro *m = find_macro(tok);
+ tok = tok->next;
+
+ if (has_paren)
+ tok = skip(tok, ")");
+
+ cur = cur->next = new_num_token(m ? 1 : 0, start);
+ continue;
+ }
+
+ cur = cur->next = tok;
+ tok = tok->next;
+ }
+
+ cur->next = tok;
+ return head.next;
+}
+
+// Read and evaluate a constant expression.
+static long eval_const_expr(Token **rest, Token *tok) {
+ Token *start = tok;
+ Token *expr = read_const_expr(rest, tok->next);
+ expr = preprocess2(expr);
+
+ if (expr->kind == TK_EOF)
+ error_tok(start, "no expression");
+
+ // [https://www.sigbus.info/n1570#6.10.1p4] The standard requires
+ // we replace remaining non-macro identifiers with "0" before
+ // evaluating a constant expression. For example, `#if foo` is
+ // equivalent to `#if 0` if foo is not defined.
+ for (Token *t = expr; t->kind != TK_EOF; t = t->next) {
+ if (t->kind == TK_IDENT) {
+ Token *next = t->next;
+ *t = *new_num_token(0, t);
+ t->next = next;
+ }
+ }
+
+ // Convert pp-numbers to regular numbers
+ convert_pp_tokens(expr);
+
+ Token *rest2;
+ long val = const_expr(&rest2, expr);
+ if (rest2->kind != TK_EOF)
+ error_tok(rest2, "extra token");
+ return val;
+}
+
+static Macro *find_macro(Token *tok) {
+ if (tok->kind != TK_IDENT)
+ return NULL;
+ return hashmap_get2(&macros, tok->loc, tok->len);
+}
+
+static Macro *add_macro(char *name, bool is_objlike, Token *body) {
+ Macro *m = calloc(1, sizeof(Macro));
+ m->name = name;
+ m->is_objlike = is_objlike;
+ m->body = body;
+ hashmap_put(&macros, name, m);
+ return m;
+}
+
+static MacroParam *read_macro_params(Token **rest, Token *tok, char **va_args_name) {
+ MacroParam head = {};
+ MacroParam *cur = &head;
+
+ while (!equal(tok, ")")) {
+ if (cur != &head)
+ tok = skip(tok, ",");
+
+ if (equal(tok, "...")) {
+ *va_args_name = "__VA_ARGS__";
+ *rest = skip(tok->next, ")");
+ return head.next;
+ }
+
+ if (tok->kind != TK_IDENT)
+ error_tok(tok, "expected an identifier");
+
+ if (equal(tok->next, "...")) {
+ *va_args_name = strndup(tok->loc, tok->len);
+ *rest = skip(tok->next->next, ")");
+ return head.next;
+ }
+
+ MacroParam *m = calloc(1, sizeof(MacroParam));
+ m->name = strndup(tok->loc, tok->len);
+ cur = cur->next = m;
+ tok = tok->next;
+ }
+
+ *rest = tok->next;
+ return head.next;
+}
+
+static void read_macro_definition(Token **rest, Token *tok) {
+ if (tok->kind != TK_IDENT)
+ error_tok(tok, "macro name must be an identifier");
+ char *name = strndup(tok->loc, tok->len);
+ tok = tok->next;
+
+ if (!tok->has_space && equal(tok, "(")) {
+ // Function-like macro
+ char *va_args_name = NULL;
+ MacroParam *params = read_macro_params(&tok, tok->next, &va_args_name);
+
+ Macro *m = add_macro(name, false, copy_line(rest, tok));
+ m->params = params;
+ m->va_args_name = va_args_name;
+ } else {
+ // Object-like macro
+ add_macro(name, true, copy_line(rest, tok));
+ }
+}
+
+static MacroArg *read_macro_arg_one(Token **rest, Token *tok, bool read_rest) {
+ Token head = {};
+ Token *cur = &head;
+ int level = 0;
+
+ for (;;) {
+ if (level == 0 && equal(tok, ")"))
+ break;
+ if (level == 0 && !read_rest && equal(tok, ","))
+ break;
+
+ if (tok->kind == TK_EOF)
+ error_tok(tok, "premature end of input");
+
+ if (equal(tok, "("))
+ level++;
+ else if (equal(tok, ")"))
+ level--;
+
+ cur = cur->next = copy_token(tok);
+ tok = tok->next;
+ }
+
+ cur->next = new_eof(tok);
+
+ MacroArg *arg = calloc(1, sizeof(MacroArg));
+ arg->tok = head.next;
+ *rest = tok;
+ return arg;
+}
+
+static MacroArg *
+read_macro_args(Token **rest, Token *tok, MacroParam *params, char *va_args_name) {
+ Token *start = tok;
+ tok = tok->next->next;
+
+ MacroArg head = {};
+ MacroArg *cur = &head;
+
+ MacroParam *pp = params;
+ for (; pp; pp = pp->next) {
+ if (cur != &head)
+ tok = skip(tok, ",");
+ cur = cur->next = read_macro_arg_one(&tok, tok, false);
+ cur->name = pp->name;
+ }
+
+ if (va_args_name) {
+ MacroArg *arg;
+ if (equal(tok, ")")) {
+ arg = calloc(1, sizeof(MacroArg));
+ arg->tok = new_eof(tok);
+ } else {
+ if (pp != params)
+ tok = skip(tok, ",");
+ arg = read_macro_arg_one(&tok, tok, true);
+ }
+ arg->name = va_args_name;;
+ arg->is_va_args = true;
+ cur = cur->next = arg;
+ } else if (pp) {
+ error_tok(start, "too many arguments");
+ }
+
+ skip(tok, ")");
+ *rest = tok;
+ return head.next;
+}
+
+static MacroArg *find_arg(MacroArg *args, Token *tok) {
+ for (MacroArg *ap = args; ap; ap = ap->next)
+ if (tok->len == strlen(ap->name) && !strncmp(tok->loc, ap->name, tok->len))
+ return ap;
+ return NULL;
+}
+
+// Concatenates all tokens in `tok` and returns a new string.
+static char *join_tokens(Token *tok, Token *end) {
+ // Compute the length of the resulting token.
+ int len = 1;
+ for (Token *t = tok; t != end && t->kind != TK_EOF; t = t->next) {
+ if (t != tok && t->has_space)
+ len++;
+ len += t->len;
+ }
+
+ char *buf = calloc(1, len);
+
+ // Copy token texts.
+ int pos = 0;
+ for (Token *t = tok; t != end && t->kind != TK_EOF; t = t->next) {
+ if (t != tok && t->has_space)
+ buf[pos++] = ' ';
+ strncpy(buf + pos, t->loc, t->len);
+ pos += t->len;
+ }
+ buf[pos] = '\0';
+ return buf;
+}
+
+// Concatenates all tokens in `arg` and returns a new string token.
+// This function is used for the stringizing operator (#).
+static Token *stringize(Token *hash, Token *arg) {
+ // Create a new string token. We need to set some value to its
+ // source location for error reporting function, so we use a macro
+ // name token as a template.
+ char *s = join_tokens(arg, NULL);
+ return new_str_token(s, hash);
+}
+
+// Concatenate two tokens to create a new token.
+static Token *paste(Token *lhs, Token *rhs) {
+ // Paste the two tokens.
+ char *buf = format("%.*s%.*s", lhs->len, lhs->loc, rhs->len, rhs->loc);
+
+ // Tokenize the resulting string.
+ Token *tok = tokenize(new_file(lhs->file->name, lhs->file->file_no, buf));
+ if (tok->next->kind != TK_EOF)
+ error_tok(lhs, "pasting forms '%s', an invalid token", buf);
+ return tok;
+}
+
+static bool has_varargs(MacroArg *args) {
+ for (MacroArg *ap = args; ap; ap = ap->next)
+ if (!strcmp(ap->name, "__VA_ARGS__"))
+ return ap->tok->kind != TK_EOF;
+ return false;
+}
+
+// Replace func-like macro parameters with given arguments.
+static Token *subst(Token *tok, MacroArg *args) {
+ Token head = {};
+ Token *cur = &head;
+
+ while (tok->kind != TK_EOF) {
+ // "#" followed by a parameter is replaced with stringized actuals.
+ if (equal(tok, "#")) {
+ MacroArg *arg = find_arg(args, tok->next);
+ if (!arg)
+ error_tok(tok->next, "'#' is not followed by a macro parameter");
+ cur = cur->next = stringize(tok, arg->tok);
+ tok = tok->next->next;
+ continue;
+ }
+
+ // [GNU] If __VA_ARG__ is empty, `,##__VA_ARGS__` is expanded
+ // to the empty token list. Otherwise, its expaned to `,` and
+ // __VA_ARGS__.
+ if (equal(tok, ",") && equal(tok->next, "##")) {
+ MacroArg *arg = find_arg(args, tok->next->next);
+ if (arg && arg->is_va_args) {
+ if (arg->tok->kind == TK_EOF) {
+ tok = tok->next->next->next;
+ } else {
+ cur = cur->next = copy_token(tok);
+ tok = tok->next->next;
+ }
+ continue;
+ }
+ }
+
+ if (equal(tok, "##")) {
+ if (cur == &head)
+ error_tok(tok, "'##' cannot appear at start of macro expansion");
+
+ if (tok->next->kind == TK_EOF)
+ error_tok(tok, "'##' cannot appear at end of macro expansion");
+
+ MacroArg *arg = find_arg(args, tok->next);
+ if (arg) {
+ if (arg->tok->kind != TK_EOF) {
+ *cur = *paste(cur, arg->tok);
+ for (Token *t = arg->tok->next; t->kind != TK_EOF; t = t->next)
+ cur = cur->next = copy_token(t);
+ }
+ tok = tok->next->next;
+ continue;
+ }
+
+ *cur = *paste(cur, tok->next);
+ tok = tok->next->next;
+ continue;
+ }
+
+ MacroArg *arg = find_arg(args, tok);
+
+ if (arg && equal(tok->next, "##")) {
+ Token *rhs = tok->next->next;
+
+ if (arg->tok->kind == TK_EOF) {
+ MacroArg *arg2 = find_arg(args, rhs);
+ if (arg2) {
+ for (Token *t = arg2->tok; t->kind != TK_EOF; t = t->next)
+ cur = cur->next = copy_token(t);
+ } else {
+ cur = cur->next = copy_token(rhs);
+ }
+ tok = rhs->next;
+ continue;
+ }
+
+ for (Token *t = arg->tok; t->kind != TK_EOF; t = t->next)
+ cur = cur->next = copy_token(t);
+ tok = tok->next;
+ continue;
+ }
+
+ // If __VA_ARG__ is empty, __VA_OPT__(x) is expanded to the
+ // empty token list. Otherwise, __VA_OPT__(x) is expanded to x.
+ if (equal(tok, "__VA_OPT__") && equal(tok->next, "(")) {
+ MacroArg *arg = read_macro_arg_one(&tok, tok->next->next, true);
+ if (has_varargs(args))
+ for (Token *t = arg->tok; t->kind != TK_EOF; t = t->next)
+ cur = cur->next = t;
+ tok = skip(tok, ")");
+ continue;
+ }
+
+ // Handle a macro token. Macro arguments are completely macro-expanded
+ // before they are substituted into a macro body.
+ if (arg) {
+ Token *t = preprocess2(arg->tok);
+ t->at_bol = tok->at_bol;
+ t->has_space = tok->has_space;
+ for (; t->kind != TK_EOF; t = t->next)
+ cur = cur->next = copy_token(t);
+ tok = tok->next;
+ continue;
+ }
+
+ // Handle a non-macro token.
+ cur = cur->next = copy_token(tok);
+ tok = tok->next;
+ continue;
+ }
+
+ cur->next = tok;
+ return head.next;
+}
+
+// If tok is a macro, expand it and return true.
+// Otherwise, do nothing and return false.
+static bool expand_macro(Token **rest, Token *tok) {
+ if (hideset_contains(tok->hideset, tok->loc, tok->len))
+ return false;
+
+ Macro *m = find_macro(tok);
+ if (!m)
+ return false;
+
+ // Built-in dynamic macro application such as __LINE__
+ if (m->handler) {
+ *rest = m->handler(tok);
+ (*rest)->next = tok->next;
+ return true;
+ }
+
+ // Object-like macro application
+ if (m->is_objlike) {
+ Hideset *hs = hideset_union(tok->hideset, new_hideset(m->name));
+ Token *body = add_hideset(m->body, hs);
+ for (Token *t = body; t->kind != TK_EOF; t = t->next)
+ t->origin = tok;
+ *rest = append(body, tok->next);
+ (*rest)->at_bol = tok->at_bol;
+ (*rest)->has_space = tok->has_space;
+ return true;
+ }
+
+ // If a funclike macro token is not followed by an argument list,
+ // treat it as a normal identifier.
+ if (!equal(tok->next, "("))
+ return false;
+
+ // Function-like macro application
+ Token *macro_token = tok;
+ MacroArg *args = read_macro_args(&tok, tok, m->params, m->va_args_name);
+ Token *rparen = tok;
+
+ // Tokens that consist a func-like macro invocation may have different
+ // hidesets, and if that's the case, it's not clear what the hideset
+ // for the new tokens should be. We take the interesection of the
+ // macro token and the closing parenthesis and use it as a new hideset
+ // as explained in the Dave Prossor's algorithm.
+ Hideset *hs = hideset_intersection(macro_token->hideset, rparen->hideset);
+ hs = hideset_union(hs, new_hideset(m->name));
+
+ Token *body = subst(m->body, args);
+ body = add_hideset(body, hs);
+ for (Token *t = body; t->kind != TK_EOF; t = t->next)
+ t->origin = macro_token;
+ *rest = append(body, tok->next);
+ (*rest)->at_bol = macro_token->at_bol;
+ (*rest)->has_space = macro_token->has_space;
+ return true;
+}
+
+char *search_include_paths(char *filename) {
+ if (filename[0] == '/')
+ return filename;
+
+ static HashMap cache;
+ char *cached = hashmap_get(&cache, filename);
+ if (cached)
+ return cached;
+
+ return NULL;
+}
+
+static char *search_include_next(char *filename) {
+ return NULL;
+}
+
+// Read an #include argument.
+static char *read_include_filename(Token **rest, Token *tok, bool *is_dquote) {
+ // Pattern 1: #include "foo.h"
+ if (tok->kind == TK_STR) {
+ // A double-quoted filename for #include is a special kind of
+ // token, and we don't want to interpret any escape sequences in it.
+ // For example, "\f" in "C:\foo" is not a formfeed character but
+ // just two non-control characters, backslash and f.
+ // So we don't want to use token->str.
+ *is_dquote = true;
+ *rest = skip_line(tok->next);
+ return strndup(tok->loc + 1, tok->len - 2);
+ }
+
+ // Pattern 2: #include <foo.h>
+ if (equal(tok, "<")) {
+ // Reconstruct a filename from a sequence of tokens between
+ // "<" and ">".
+ Token *start = tok;
+
+ // Find closing ">".
+ for (; !equal(tok, ">"); tok = tok->next)
+ if (tok->at_bol || tok->kind == TK_EOF)
+ error_tok(tok, "expected '>'");
+
+ *is_dquote = false;
+ *rest = skip_line(tok->next);
+ return join_tokens(start->next, tok);
+ }
+
+ // Pattern 3: #include FOO
+ // In this case FOO must be macro-expanded to either
+ // a single string token or a sequence of "<" ... ">".
+ if (tok->kind == TK_IDENT) {
+ Token *tok2 = preprocess2(copy_line(rest, tok));
+ return read_include_filename(&tok2, tok2, is_dquote);
+ }
+
+ error_tok(tok, "expected a filename");
+}
+
+// Detect the following "include guard" pattern.
+//
+// #ifndef FOO_H
+// #define FOO_H
+// ...
+// #endif
+static char *detect_include_guard(Token *tok) {
+ // Detect the first two lines.
+ if (!is_hash(tok) || !equal(tok->next, "ifndef"))
+ return NULL;
+ tok = tok->next->next;
+
+ if (tok->kind != TK_IDENT)
+ return NULL;
+
+ char *macro = strndup(tok->loc, tok->len);
+ tok = tok->next;
+
+ if (!is_hash(tok) || !equal(tok->next, "define") || !equal(tok->next->next, macro))
+ return NULL;
+
+ // Read until the end of the file.
+ while (tok->kind != TK_EOF) {
+ if (!is_hash(tok)) {
+ tok = tok->next;
+ continue;
+ }
+
+ if (equal(tok->next, "endif") && tok->next->next->kind == TK_EOF)
+ return macro;
+
+ if (equal(tok, "if") || equal(tok, "ifdef") || equal(tok, "ifndef"))
+ tok = skip_cond_incl(tok->next);
+ else
+ tok = tok->next;
+ }
+ return NULL;
+}
+
+static Token *include_file(Token *tok, char *path, Token *filename_tok) {
+ // Check for "#pragma once"
+ if (hashmap_get(&pragma_once, path))
+ return tok;
+
+ // If we read the same file before, and if the file was guarded
+ // by the usual #ifndef ... #endif pattern, we may be able to
+ // skip the file without opening it.
+ static HashMap include_guards;
+ char *guard_name = hashmap_get(&include_guards, path);
+ if (guard_name && hashmap_get(&macros, guard_name))
+ return tok;
+
+ Token *tok2 = tokenize_file(path);
+ if (!tok2)
+ error_tok(filename_tok, "%s: cannot open file: %s", path, strerror(errno));
+
+ guard_name = detect_include_guard(tok2);
+ if (guard_name)
+ hashmap_put(&include_guards, path, guard_name);
+
+ return append(tok2, tok);
+}
+
+// Read #line arguments
+static void read_line_marker(Token **rest, Token *tok) {
+ Token *start = tok;
+ tok = preprocess(copy_line(rest, tok));
+
+ if (tok->kind != TK_NUM || tok->ty->kind != TY_INT)
+ error_tok(tok, "invalid line marker");
+ start->file->line_delta = tok->val - start->line_no;
+
+ tok = tok->next;
+ if (tok->kind == TK_EOF)
+ return;
+
+ if (tok->kind != TK_STR)
+ error_tok(tok, "filename expected");
+ start->file->display_name = tok->str;
+}
+
+// Visit all tokens in `tok` while evaluating preprocessing
+// macros and directives.
+static Token *preprocess2(Token *tok) {
+ Token head = {};
+ Token *cur = &head;
+
+ while (tok->kind != TK_EOF) {
+ // If it is a macro, expand it.
+ if (expand_macro(&tok, tok))
+ continue;
+
+ // Pass through if it is not a "#".
+ if (!is_hash(tok)) {
+ tok->line_delta = tok->file->line_delta;
+ tok->filename = tok->file->display_name;
+ cur = cur->next = tok;
+ tok = tok->next;
+ continue;
+ }
+
+ Token *start = tok;
+ tok = tok->next;
+
+ if (equal(tok, "include")) {
+ assert(0);
+ }
+
+ if (equal(tok, "include_next")) {
+ bool ignore;
+ char *filename = read_include_filename(&tok, tok->next, &ignore);
+ char *path = search_include_next(filename);
+ tok = include_file(tok, path ? path : filename, start->next->next);
+ continue;
+ }
+
+ if (equal(tok, "define")) {
+ read_macro_definition(&tok, tok->next);
+ continue;
+ }
+
+ if (equal(tok, "undef")) {
+ assert(0);
+ }
+
+ if (equal(tok, "if")) {
+ assert(0);
+ }
+
+ if (equal(tok, "ifdef")) {
+ assert(0);
+ }
+
+ if (equal(tok, "ifndef")) {
+ assert(0);
+ }
+
+ if (equal(tok, "elif")) {
+ if (!cond_incl || cond_incl->ctx == IN_ELSE)
+ error_tok(start, "stray #elif");
+ cond_incl->ctx = IN_ELIF;
+
+ if (!cond_incl->included && eval_const_expr(&tok, tok))
+ cond_incl->included = true;
+ else
+ tok = skip_cond_incl(tok);
+ continue;
+ }
+
+ if (equal(tok, "else")) {
+ if (!cond_incl || cond_incl->ctx == IN_ELSE)
+ error_tok(start, "stray #else");
+ cond_incl->ctx = IN_ELSE;
+ tok = skip_line(tok->next);
+
+ if (cond_incl->included)
+ tok = skip_cond_incl(tok);
+ continue;
+ }
+
+ if (equal(tok, "endif")) {
+ if (!cond_incl)
+ error_tok(start, "stray #endif");
+ cond_incl = cond_incl->next;
+ tok = skip_line(tok->next);
+ continue;
+ }
+
+ if (equal(tok, "line")) {
+ read_line_marker(&tok, tok->next);
+ continue;
+ }
+
+ if (tok->kind == TK_PP_NUM) {
+ read_line_marker(&tok, tok);
+ continue;
+ }
+
+ if (equal(tok, "pragma") && equal(tok->next, "once")) {
+ hashmap_put(&pragma_once, tok->file->name, (void *)1);
+ tok = skip_line(tok->next->next);
+ continue;
+ }
+
+ if (equal(tok, "pragma")) {
+ do {
+ tok = tok->next;
+ } while (!tok->at_bol);
+ continue;
+ }
+
+ if (equal(tok, "error"))
+ error_tok(tok, "error");
+
+ // `#`-only line is legal. It's called a null directive.
+ if (tok->at_bol)
+ continue;
+
+ error_tok(tok, "invalid preprocessor directive");
+ }
+
+ cur->next = tok;
+ return head.next;
+}
+
+void define_macro(char *name, char *buf) {
+ Token *tok = tokenize(new_file("<built-in>", 1, buf));
+ add_macro(name, true, tok);
+}
+
+void undef_macro(char *name) {
+ hashmap_delete(&macros, name);
+}
+
+static Macro *add_builtin(char *name, macro_handler_fn *fn) {
+ Macro *m = add_macro(name, true, NULL);
+ m->handler = fn;
+ return m;
+}
+
+static Token *file_macro(Token *tmpl) {
+ while (tmpl->origin)
+ tmpl = tmpl->origin;
+ return new_str_token(tmpl->file->display_name, tmpl);
+}
+
+static Token *line_macro(Token *tmpl) {
+ while (tmpl->origin)
+ tmpl = tmpl->origin;
+ int i = tmpl->line_no + tmpl->file->line_delta;
+ return new_num_token(i, tmpl);
+}
+
+// __COUNTER__ is expanded to serial values starting from 0.
+static Token *counter_macro(Token *tmpl) {
+ static int i = 0;
+ return new_num_token(i++, tmpl);
+}
+
+// __TIMESTAMP__ is expanded to a string describing the last
+// modification time of the current file. E.g.
+// "Fri Jul 24 01:32:50 2020"
+static Token *timestamp_macro(Token *tmpl) {
+ struct stat st;
+ if (stat(tmpl->file->name, &st) != 0)
+ return new_str_token("??? ??? ?? ??:??:?? ????", tmpl);
+
+ char buf[30];
+ ctime_r(&st.st_mtime, buf);
+ buf[24] = '\0';
+ return new_str_token(buf, tmpl);
+}
+
+static Token *base_file_macro(Token *tmpl) {
+ return new_str_token(base_file, tmpl);
+}
+
+// __DATE__ is expanded to the current date, e.g. "May 17 2020".
+static char *format_date(struct tm *tm) {
+ static char mon[][4] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+ };
+
+ return format("\"%s %2d %d\"", mon[tm->tm_mon], tm->tm_mday, tm->tm_year + 1900);
+}
+
+// __TIME__ is expanded to the current time, e.g. "13:34:03".
+static char *format_time(struct tm *tm) {
+ return format("\"%02d:%02d:%02d\"", tm->tm_hour, tm->tm_min, tm->tm_sec);
+}
+
+void init_macros(void) {
+ // Define predefined macros
+ define_macro("_LP64", "1");
+ define_macro("__C99_MACRO_WITH_VA_ARGS", "1");
+ define_macro("__ELF__", "1");
+ define_macro("__LP64__", "1");
+ define_macro("__SIZEOF_DOUBLE__", "8");
+ define_macro("__SIZEOF_FLOAT__", "4");
+ define_macro("__SIZEOF_INT__", "4");
+ define_macro("__SIZEOF_LONG_DOUBLE__", "8");
+ define_macro("__SIZEOF_LONG_LONG__", "8");
+ define_macro("__SIZEOF_LONG__", "8");
+ define_macro("__SIZEOF_POINTER__", "8");
+ define_macro("__SIZEOF_PTRDIFF_T__", "8");
+ define_macro("__SIZEOF_SHORT__", "2");
+ define_macro("__SIZEOF_SIZE_T__", "8");
+ define_macro("__SIZE_TYPE__", "unsigned long");
+ define_macro("__STDC_HOSTED__", "1");
+ define_macro("__STDC_NO_COMPLEX__", "1");
+ define_macro("__STDC_UTF_16__", "1");
+ define_macro("__STDC_UTF_32__", "1");
+ define_macro("__STDC_VERSION__", "201112L");
+ define_macro("__STDC__", "1");
+ define_macro("__USER_LABEL_PREFIX__", "");
+ define_macro("__alignof__", "_Alignof");
+ define_macro("__amd64", "1");
+ define_macro("__amd64__", "1");
+ define_macro("__chibicc__", "1");
+ define_macro("__const__", "const");
+ define_macro("__gnu_linux__", "1");
+ define_macro("__inline__", "inline");
+ define_macro("__linux", "1");
+ define_macro("__linux__", "1");
+ define_macro("__signed__", "signed");
+ define_macro("__typeof__", "typeof");
+ define_macro("__unix", "1");
+ define_macro("__unix__", "1");
+ define_macro("__volatile__", "volatile");
+ define_macro("__x86_64", "1");
+ define_macro("__x86_64__", "1");
+ define_macro("linux", "1");
+ define_macro("unix", "1");
+
+ add_builtin("__FILE__", file_macro);
+ add_builtin("__LINE__", line_macro);
+ add_builtin("__COUNTER__", counter_macro);
+ add_builtin("__TIMESTAMP__", timestamp_macro);
+ add_builtin("__BASE_FILE__", base_file_macro);
+
+ time_t now = time(NULL);
+ struct tm *tm = localtime(&now);
+ define_macro("__DATE__", format_date(tm));
+ define_macro("__TIME__", format_time(tm));
+}
+
+typedef enum {
+ STR_NONE, STR_UTF8, STR_UTF16, STR_UTF32, STR_WIDE,
+} StringKind;
+
+static StringKind getStringKind(Token *tok) {
+ if (!strcmp(tok->loc, "u8"))
+ return STR_UTF8;
+
+ switch (tok->loc[0]) {
+ case '"': return STR_NONE;
+ case 'u': return STR_UTF16;
+ case 'U': return STR_UTF32;
+ case 'L': return STR_WIDE;
+ }
+ unreachable();
+}
+
+// Concatenate adjacent string literals into a single string literal
+// as per the C spec.
+static void join_adjacent_string_literals(Token *tok) {
+ // First pass: If regular string literals are adjacent to wide
+ // string literals, regular string literals are converted to a wide
+ // type before concatenation. In this pass, we do the conversion.
+ for (Token *tok1 = tok; tok1->kind != TK_EOF;) {
+ if (tok1->kind != TK_STR || tok1->next->kind != TK_STR) {
+ tok1 = tok1->next;
+ continue;
+ }
+
+ StringKind kind = getStringKind(tok1);
+ Type *basety = tok1->ty->base;
+
+ for (Token *t = tok1->next; t->kind == TK_STR; t = t->next) {
+ StringKind k = getStringKind(t);
+ if (kind == STR_NONE) {
+ kind = k;
+ basety = t->ty->base;
+ } else if (k != STR_NONE && kind != k) {
+ error_tok(t, "unsupported non-standard concatenation of string literals");
+ }
+ }
+
+ if (basety->size > 1)
+ for (Token *t = tok1; t->kind == TK_STR; t = t->next)
+ if (t->ty->base->size == 1)
+ *t = *tokenize_string_literal(t, basety);
+
+ while (tok1->kind == TK_STR)
+ tok1 = tok1->next;
+ }
+
+ // Second pass: concatenate adjacent string literals.
+ for (Token *tok1 = tok; tok1->kind != TK_EOF;) {
+ if (tok1->kind != TK_STR || tok1->next->kind != TK_STR) {
+ tok1 = tok1->next;
+ continue;
+ }
+
+ Token *tok2 = tok1->next;
+ while (tok2->kind == TK_STR)
+ tok2 = tok2->next;
+
+ int len = tok1->ty->array_len;
+ for (Token *t = tok1->next; t != tok2; t = t->next)
+ len = len + t->ty->array_len - 1;
+
+ char *buf = calloc(tok1->ty->base->size, len);
+
+ int i = 0;
+ for (Token *t = tok1; t != tok2; t = t->next) {
+ memcpy(buf + i, t->str, t->ty->size);
+ i = i + t->ty->size - t->ty->base->size;
+ }
+
+ *tok1 = *copy_token(tok1);
+ tok1->ty = array_of(tok1->ty->base, len);
+ tok1->str = buf;
+ tok1->next = tok2;
+ tok1 = tok2;
+ }
+}
+
+// Entry point function of the preprocessor.
+Token *preprocess(Token *tok) {
+ tok = preprocess2(tok);
+ if (cond_incl)
+ error_tok(cond_incl->tok, "unterminated conditional directive");
+ convert_pp_tokens(tok);
+ join_adjacent_string_literals(tok);
+
+ for (Token *t = tok; t; t = t->next)
+ t->line_no += t->line_delta;
+ return tok;
+}
diff --git a/scripts/dietc_helpers.h b/scripts/dietc_helpers.h
new file mode 100644
index 0000000..a5e00ac
--- /dev/null
+++ b/scripts/dietc_helpers.h
@@ -0,0 +1,5 @@
+extern void *memset(void *, int, long unsigned int);
+#define BINARY(a, op, b) a op b
+#define PTR_BINARY(a, op, b) ((void*)(a) op (unsigned long)(b))
+#define MEMZERO(obj) memset(&obj, 0, sizeof(obj))
+#define FIELDPTR(sptr, field) (&(sptr -> field))
diff --git a/scripts/dietcc b/scripts/dietcc
new file mode 100755
index 0000000..8898df2
--- /dev/null
+++ b/scripts/dietcc
@@ -0,0 +1,171 @@
+#!/bin/python3
+import subprocess
+import sys
+import re
+import tempfile
+import os
+
+if sys.argv[1:] == ["--version"]:
+ print("DIETC")
+ sys.exit(0)
+
+# Have to do this because ./configure seems to do some nasty stuff
+# https://lists.gnu.org/archive/html/bug-make/2022-11/msg00041.html
+if "-E" in sys.argv[1:]:
+ os.system(f"gcc {' '.join(sys.argv[1:])}")
+ sys.exit(0)
+
+# https://stackoverflow.com/questions/595305
+dietc_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+
+def check_result(spr):
+ if not spr.returncode: return spr.stdout
+ print("Failed to run gcc wrapper:", ' '.join(sys.argv), file=sys.stderr)
+ if spr.stderr:
+ print(spr.stderr.decode("utf-8"), file=sys.stderr)
+ assert False
+
+def preprocess_pass(c, meta):
+ with tempfile.TemporaryDirectory() as tmpdir:
+ t_file = open(f"{tmpdir}/file.c", "wb")
+ t_file.write(f"#include \"{dietc_dir}/scripts/stdincludes/dietc_defines.h\"\n".encode("utf-8"))
+ t_file.write(c)
+ t_file.flush()
+ dirname = os.path.dirname(os.path.realpath(meta["c_path"]))
+ return check_result(
+ subprocess.run(f"gcc -I{dirname} -E {meta['argstr']} {t_file.name}",
+ shell=True, capture_output=True))
+
+def strip_after_preprocess_pass(c, meta):
+ c = re.sub(rb"\[\[[^\]]*\]\]", b"", c)
+ c = re.sub(rb"__asm__", b"", c)
+ return c
+
+def dietc_pass(c, meta):
+ with tempfile.TemporaryDirectory() as tmpdir:
+ t_file = open(f"{tmpdir}/file.c", "wb")
+ t_file.write(c)
+ t_file.flush()
+ dirname = os.path.dirname(os.path.realpath(meta["c_path"]))
+ return check_result(
+ subprocess.run(f"timeout 5s {dietc_dir}/dietc {t_file.name}",
+ shell=True, capture_output=True))
+
+def final_cleanup_pass(dietc, meta):
+ # (1) treat builtins as builtins
+ patterns = {
+ rb"extern Type_[0-9]* __builtin_[^ ]* ;\n": b"",
+ rb"extern Type_[0-9]* __(div|mul)[ds]c3 ;\n": b"",
+ rb"extern Type_[0-9]* __btowc_alias ;\n": b"",
+ }
+ for pattern, replace in patterns.items():
+ dietc = re.sub(pattern, b"", dietc)
+
+ # (2) builtins must be called directly
+ # NOTE: if passes mess this up, e.g., by doing optimizations, we could be in
+ # trouble here
+ pattern = rb"\t(t[0-9]*) = (__builtin_[^ ]*|alloca) ;\n"
+ match_ = re.search(pattern, dietc)
+ while match_ is not None:
+ temp = match_.groups()[0]
+ builtin = match_.groups()[1]
+ # (2a) delete the match
+ dietc = dietc.replace(b"\t" + temp + b" = " + builtin + b" ;\n", b"")
+ # (2b) delete the declaration of the builtin
+ dietc = re.sub(b"\tType_[0-9]* " + temp + b" ;\n", b"", dietc)
+ # (2c) replace uses of the temp with the builtin
+ dietc = dietc.replace(b" " + temp + b" ", b" " + builtin + b" ")
+ dietc = dietc.replace(b"\t" + temp + b" ", b"\t" + builtin + b" ")
+
+ match_ = re.search(pattern, dietc)
+
+ # (3) __builtin_va_arg_pack* must be called *super* directly, and must be in
+ # an always-inline method
+ # NOTE: if passes mess this up, e.g., by doing optimizations, we could be in
+ # trouble here
+ pattern = rb"\t(t[0-9]*) = (__builtin_va_arg_pack[^ ]*) \( \) ;\n"
+ match_ = re.search(pattern, dietc)
+ any_arg_pack = (match_ is not None)
+ while match_ is not None:
+ temp = match_.groups()[0]
+ builtin = match_.groups()[1]
+ # (2a) delete the match
+ dietc = dietc.replace(b"\t" + temp + b" = " + builtin + b" ( ) ;\n", b"")
+ # (2b) delete the declaration of the result
+ dietc = re.sub(b"\tType_[0-9]* " + temp + b" ;\n", b"", dietc)
+ # (2c) replace uses of the temp with the builtin
+ dietc = dietc.replace(b" " + temp + b" ", b" " + builtin + b" ( ) ")
+
+ match_ = re.search(pattern, dietc)
+
+ # (3b) any functions using __builtin_va_arg_pack* must be inlined
+ if any_arg_pack or b"\t__builtin_va_start " in dietc:
+ lines = dietc.split(b"\n")
+ inline_is = set()
+ last_decl_i = 0
+ for i, line in enumerate(lines):
+ if not line.startswith(b"\t"):
+ last_decl_i = i
+ elif b"__builtin_va_arg_pack" in line:
+ inline_is.add(last_decl_i)
+ elif b"\t__builtin_va_start " in line:
+ # second argument of __builtin_va_start must be last named argument to
+ # function
+ fn, _po, arg1, _c, arg2, _pc, _sc = line.split()
+ # {, ), ..., <,>, [arg]
+ real_arg2 = lines[last_decl_i].split()[-5]
+ lines[i] = b"\t" + b" ".join([fn, _po, arg1, _c, real_arg2, _pc, _sc])
+ for i in sorted(inline_is):
+ if "static" in lines[i]:
+ lines[i] = b"inline __attribute__ ((__gnu_inline__)) " + lines[i]
+ else:
+ lines[i] = b"extern inline __attribute__ ((__gnu_inline__)) " + lines[i]
+ dietc = b"\n".join(lines)
+
+ return dietc
+
+PASSES = [preprocess_pass,
+ strip_after_preprocess_pass,
+ dietc_pass,
+ final_cleanup_pass]
+
+def main():
+ args = sys.argv[1:]
+ args.insert(0, f"-I{dietc_dir}/scripts/stdincludes")
+ # first, parse out any DietC passes
+ dietc_passes = []
+ while "--dietc-pass" in args:
+ i = args.index("--dietc-pass")
+ args.pop(i)
+ dietc_passes.append(args.pop(i))
+
+ # then process all of the C files
+ out_dir = tempfile.TemporaryDirectory()
+ c_files = [arg for arg in args if arg.endswith(".c")]
+ diet_files = []
+ for i, c_file in enumerate(c_files):
+ subargs = [arg for arg in args if not arg.endswith(".c")]
+ if "-o" in subargs:
+ i = subargs.index("-o")
+ subargs.pop(i)
+ subargs.pop(i)
+ argstr = " ".join(subargs)
+
+ last = open(c_file, "rb").read()
+ for dietpass in PASSES:
+ last = dietpass(last, {"c_path": c_file, "argstr": argstr})
+
+ diet_files.append(f"{out_dir.name}/file{i}.c")
+ with open(diet_files[-1], "wb") as f:
+ f.write(last)
+
+ # then reconstruct the arguments, using the processed C files
+ renaming = dict({c: diet for c, diet in zip(c_files, diet_files)})
+ args = [renaming.get(arg, arg) for arg in args]
+ if c_files and "-o" not in args:
+ if "-c" in args:
+ args += ["-o", c_files[0].replace(".c", ".o")]
+ gcc = subprocess.run(f"gcc {' '.join(args)} -Wno-int-to-pointer-cast -Wno-builtin-declaration-mismatch", shell=True)
+ check_result(gcc)
+
+main()
diff --git a/scripts/stdincludes/alloca.h b/scripts/stdincludes/alloca.h
new file mode 100644
index 0000000..cb1abd8
--- /dev/null
+++ b/scripts/stdincludes/alloca.h
@@ -0,0 +1,4 @@
+extern void *__builtin_alloca(unsigned long long);
+#define alloca __builtin_alloca
+
+#include_next <alloca.h>
diff --git a/scripts/stdincludes/dietc_defines.h b/scripts/stdincludes/dietc_defines.h
new file mode 100644
index 0000000..2ca19c9
--- /dev/null
+++ b/scripts/stdincludes/dietc_defines.h
@@ -0,0 +1,24 @@
+#define __attribute__(...)
+#define __attribute(...)
+#undef __GNUC__
+#undef __GNUC__
+#undef __has_builtin
+
+#define __signed__ signed
+#define __typeof__ typeof
+#define __typeof typeof
+
+#define __asm__(...)
+#define __asm(...)
+#define __extension__
+#define __inline inline
+#define __extern_always_inline extern inline
+#define _FORTIFY_SOURCE 0
+#define __restrict
+#define __restrict__
+#define _Restrict
+#define __PRETTY_FUNCTION__ __FUNCTION__
+#define volatile(...)
+#define __volatile__(...)
+
+#define _Static_assert(...)
diff --git a/scripts/stdincludes/regex.h b/scripts/stdincludes/regex.h
new file mode 100644
index 0000000..1930c74
--- /dev/null
+++ b/scripts/stdincludes/regex.h
@@ -0,0 +1,698 @@
+/* Definitions for data structures and routines for the regular
+ expression library.
+ Copyright (C) 1985, 1989-2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _REGEX_H
+#define _REGEX_H 1
+
+#include <sys/types.h>
+
+/* Allow the use in C++ code. */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Define __USE_GNU to declare GNU extensions that violate the
+ POSIX name space rules. */
+#ifdef _GNU_SOURCE
+# define __USE_GNU 1
+#endif
+
+#ifdef _REGEX_LARGE_OFFSETS
+
+/* Use types and values that are wide enough to represent signed and
+ unsigned byte offsets in memory. This currently works only when
+ the regex code is used outside of the GNU C library; it is not yet
+ supported within glibc itself, and glibc users should not define
+ _REGEX_LARGE_OFFSETS. */
+
+/* The type of object sizes. */
+typedef size_t __re_size_t;
+
+/* The type of object sizes, in places where the traditional code
+ uses unsigned long int. */
+typedef size_t __re_long_size_t;
+
+#else
+
+/* The traditional GNU regex implementation mishandles strings longer
+ than INT_MAX. */
+typedef unsigned int __re_size_t;
+typedef unsigned long int __re_long_size_t;
+
+#endif
+
+/* The following two types have to be signed and unsigned integer type
+ wide enough to hold a value of a pointer. For most ANSI compilers
+ ptrdiff_t and size_t should be likely OK. Still size of these two
+ types is 2 for Microsoft C. Ugh... */
+typedef long int s_reg_t;
+typedef unsigned long int active_reg_t;
+
+/* The following bits are used to determine the regexp syntax we
+ recognize. The set/not-set meanings are chosen so that Emacs syntax
+ remains the value 0. The bits are given in alphabetical order, and
+ the definitions shifted by one from the previous bit; thus, when we
+ add or remove a bit, only one other definition need change. */
+typedef unsigned long int reg_syntax_t;
+
+#ifdef __USE_GNU
+/* If this bit is not set, then \ inside a bracket expression is literal.
+ If set, then such a \ quotes the following character. */
+# define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1)
+
+/* If this bit is not set, then + and ? are operators, and \+ and \? are
+ literals.
+ If set, then \+ and \? are operators and + and ? are literals. */
+# define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
+
+/* If this bit is set, then character classes are supported. They are:
+ [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:],
+ [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
+ If not set, then character classes are not supported. */
+# define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
+
+/* If this bit is set, then ^ and $ are always anchors (outside bracket
+ expressions, of course).
+ If this bit is not set, then it depends:
+ ^ is an anchor if it is at the beginning of a regular
+ expression or after an open-group or an alternation operator;
+ $ is an anchor if it is at the end of a regular expression, or
+ before a close-group or an alternation operator.
+
+ This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
+ POSIX draft 11.2 says that * etc. in leading positions is undefined.
+ We already implemented a previous draft which made those constructs
+ invalid, though, so we haven't changed the code back. */
+# define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
+
+/* If this bit is set, then special characters are always special
+ regardless of where they are in the pattern.
+ If this bit is not set, then special characters are special only in
+ some contexts; otherwise they are ordinary. Specifically,
+ * + ? and intervals are only special when not after the beginning,
+ open-group, or alternation operator. */
+# define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
+
+/* If this bit is set, then *, +, ?, and { cannot be first in an re or
+ immediately after an alternation or begin-group operator. */
+# define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
+
+/* If this bit is set, then . matches newline.
+ If not set, then it doesn't. */
+# define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
+
+/* If this bit is set, then . doesn't match NUL.
+ If not set, then it does. */
+# define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
+
+/* If this bit is set, nonmatching lists [^...] do not match newline.
+ If not set, they do. */
+# define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
+
+/* If this bit is set, either \{...\} or {...} defines an
+ interval, depending on RE_NO_BK_BRACES.
+ If not set, \{, \}, {, and } are literals. */
+# define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
+
+/* If this bit is set, +, ? and | aren't recognized as operators.
+ If not set, they are. */
+# define RE_LIMITED_OPS (RE_INTERVALS << 1)
+
+/* If this bit is set, newline is an alternation operator.
+ If not set, newline is literal. */
+# define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
+
+/* If this bit is set, then '{...}' defines an interval, and \{ and \}
+ are literals.
+ If not set, then '\{...\}' defines an interval. */
+# define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
+
+/* If this bit is set, (...) defines a group, and \( and \) are literals.
+ If not set, \(...\) defines a group, and ( and ) are literals. */
+# define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
+
+/* If this bit is set, then \<digit> matches <digit>.
+ If not set, then \<digit> is a back-reference. */
+# define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
+
+/* If this bit is set, then | is an alternation operator, and \| is literal.
+ If not set, then \| is an alternation operator, and | is literal. */
+# define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
+
+/* If this bit is set, then an ending range point collating higher
+ than the starting range point, as in [z-a], is invalid.
+ If not set, then when ending range point collates higher than the
+ starting range point, the range is ignored. */
+# define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
+
+/* If this bit is set, then an unmatched ) is ordinary.
+ If not set, then an unmatched ) is invalid. */
+# define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
+
+/* If this bit is set, succeed as soon as we match the whole pattern,
+ without further backtracking. */
+# define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1)
+
+/* If this bit is set, do not process the GNU regex operators.
+ If not set, then the GNU regex operators are recognized. */
+# define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1)
+
+/* If this bit is set, turn on internal regex debugging.
+ If not set, and debugging was on, turn it off.
+ This only works if regex.c is compiled -DDEBUG.
+ We define this bit always, so that all that's needed to turn on
+ debugging is to recompile regex.c; the calling code can always have
+ this bit set, and it won't affect anything in the normal case. */
+# define RE_DEBUG (RE_NO_GNU_OPS << 1)
+
+/* If this bit is set, a syntactically invalid interval is treated as
+ a string of ordinary characters. For example, the ERE 'a{1' is
+ treated as 'a\{1'. */
+# define RE_INVALID_INTERVAL_ORD (RE_DEBUG << 1)
+
+/* If this bit is set, then ignore case when matching.
+ If not set, then case is significant. */
+# define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1)
+
+/* This bit is used internally like RE_CONTEXT_INDEP_ANCHORS but only
+ for ^, because it is difficult to scan the regex backwards to find
+ whether ^ should be special. */
+# define RE_CARET_ANCHORS_HERE (RE_ICASE << 1)
+
+/* If this bit is set, then \{ cannot be first in a regex or
+ immediately after an alternation, open-group or \} operator. */
+# define RE_CONTEXT_INVALID_DUP (RE_CARET_ANCHORS_HERE << 1)
+
+/* If this bit is set, then no_sub will be set to 1 during
+ re_compile_pattern. */
+# define RE_NO_SUB (RE_CONTEXT_INVALID_DUP << 1)
+#endif
+
+/* This global variable defines the particular regexp syntax to use (for
+ some interfaces). When a regexp is compiled, the syntax used is
+ stored in the pattern buffer, so changing this does not affect
+ already-compiled regexps. */
+extern reg_syntax_t re_syntax_options;
+
+#ifdef __USE_GNU
+/* Define combinations of the above bits for the standard possibilities.
+ (The [[[ comments delimit what gets put into the Texinfo file, so
+ don't delete them!) */
+/* [[[begin syntaxes]]] */
+# define RE_SYNTAX_EMACS 0
+
+# define RE_SYNTAX_AWK \
+ (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \
+ | RE_NO_BK_PARENS | RE_NO_BK_REFS \
+ | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \
+ | RE_DOT_NEWLINE | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CHAR_CLASSES \
+ | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS)
+
+# define RE_SYNTAX_GNU_AWK \
+ ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \
+ | RE_INVALID_INTERVAL_ORD) \
+ & ~(RE_DOT_NOT_NULL | RE_CONTEXT_INDEP_OPS \
+ | RE_CONTEXT_INVALID_OPS ))
+
+# define RE_SYNTAX_POSIX_AWK \
+ (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \
+ | RE_INTERVALS | RE_NO_GNU_OPS \
+ | RE_INVALID_INTERVAL_ORD)
+
+# define RE_SYNTAX_GREP \
+ ((RE_SYNTAX_POSIX_BASIC | RE_NEWLINE_ALT) \
+ & ~(RE_CONTEXT_INVALID_DUP | RE_DOT_NOT_NULL))
+
+# define RE_SYNTAX_EGREP \
+ ((RE_SYNTAX_POSIX_EXTENDED | RE_INVALID_INTERVAL_ORD | RE_NEWLINE_ALT) \
+ & ~(RE_CONTEXT_INVALID_OPS | RE_DOT_NOT_NULL))
+
+/* POSIX grep -E behavior is no longer incompatible with GNU. */
+# define RE_SYNTAX_POSIX_EGREP \
+ RE_SYNTAX_EGREP
+
+/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */
+# define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
+
+# define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
+
+/* Syntax bits common to both basic and extended POSIX regex syntax. */
+# define _RE_SYNTAX_POSIX_COMMON \
+ (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \
+ | RE_INTERVALS | RE_NO_EMPTY_RANGES)
+
+# define RE_SYNTAX_POSIX_BASIC \
+ (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM | RE_CONTEXT_INVALID_DUP)
+
+/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
+ RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this
+ isn't minimal, since other operators, such as \`, aren't disabled. */
+# define RE_SYNTAX_POSIX_MINIMAL_BASIC \
+ (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
+
+# define RE_SYNTAX_POSIX_EXTENDED \
+ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \
+ | RE_NO_BK_PARENS | RE_NO_BK_VBAR \
+ | RE_CONTEXT_INVALID_OPS | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INDEP_OPS is
+ removed and RE_NO_BK_REFS is added. */
+# define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \
+ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \
+ | RE_NO_BK_PARENS | RE_NO_BK_REFS \
+ | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD)
+/* [[[end syntaxes]]] */
+
+/* Maximum number of duplicates an interval can allow. POSIX-conforming
+ systems might define this in <limits.h>, but we want our
+ value, so remove any previous define. */
+# ifdef _REGEX_INCLUDE_LIMITS_H
+# include <limits.h>
+# endif
+# ifdef RE_DUP_MAX
+# undef RE_DUP_MAX
+# endif
+
+/* RE_DUP_MAX is 2**15 - 1 because an earlier implementation stored
+ the counter as a 2-byte signed integer. This is no longer true, so
+ RE_DUP_MAX could be increased to (INT_MAX / 10 - 1), or to
+ ((SIZE_MAX - 9) / 10) if _REGEX_LARGE_OFFSETS is defined.
+ However, there would be a huge performance problem if someone
+ actually used a pattern like a\{214748363\}, so RE_DUP_MAX retains
+ its historical value. */
+# define RE_DUP_MAX (0x7fff)
+#endif
+
+
+/* POSIX 'cflags' bits (i.e., information for 'regcomp'). */
+
+/* If this bit is set, then use extended regular expression syntax.
+ If not set, then use basic regular expression syntax. */
+#define REG_EXTENDED 1
+
+/* If this bit is set, then ignore case when matching.
+ If not set, then case is significant. */
+#define REG_ICASE (1 << 1)
+
+/* If this bit is set, then anchors do not match at newline
+ characters in the string.
+ If not set, then anchors do match at newlines. */
+#define REG_NEWLINE (1 << 2)
+
+/* If this bit is set, then report only success or fail in regexec.
+ If not set, then returns differ between not matching and errors. */
+#define REG_NOSUB (1 << 3)
+
+
+/* POSIX 'eflags' bits (i.e., information for regexec). */
+
+/* If this bit is set, then the beginning-of-line operator doesn't match
+ the beginning of the string (presumably because it's not the
+ beginning of a line).
+ If not set, then the beginning-of-line operator does match the
+ beginning of the string. */
+#define REG_NOTBOL 1
+
+/* Like REG_NOTBOL, except for the end-of-line. */
+#define REG_NOTEOL (1 << 1)
+
+/* Use PMATCH[0] to delimit the start and end of the search in the
+ buffer. */
+#define REG_STARTEND (1 << 2)
+
+
+/* If any error codes are removed, changed, or added, update the
+ '__re_error_msgid' table in regcomp.c. */
+
+typedef enum
+{
+ _REG_ENOSYS = -1, /* This will never happen for this implementation. */
+ _REG_NOERROR = 0, /* Success. */
+ _REG_NOMATCH, /* Didn't find a match (for regexec). */
+
+ /* POSIX regcomp return error codes. (In the order listed in the
+ standard.) */
+ _REG_BADPAT, /* Invalid pattern. */
+ _REG_ECOLLATE, /* Invalid collating element. */
+ _REG_ECTYPE, /* Invalid character class name. */
+ _REG_EESCAPE, /* Trailing backslash. */
+ _REG_ESUBREG, /* Invalid back reference. */
+ _REG_EBRACK, /* Unmatched left bracket. */
+ _REG_EPAREN, /* Parenthesis imbalance. */
+ _REG_EBRACE, /* Unmatched \{. */
+ _REG_BADBR, /* Invalid contents of \{\}. */
+ _REG_ERANGE, /* Invalid range end. */
+ _REG_ESPACE, /* Ran out of memory. */
+ _REG_BADRPT, /* No preceding re for repetition op. */
+
+ /* Error codes we've added. */
+ _REG_EEND, /* Premature end. */
+ _REG_ESIZE, /* Too large (e.g., repeat count too large). */
+ _REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */
+} reg_errcode_t;
+
+#if defined _XOPEN_SOURCE || defined __USE_XOPEN2K
+# define REG_ENOSYS _REG_ENOSYS
+#endif
+#define REG_NOERROR _REG_NOERROR
+#define REG_NOMATCH _REG_NOMATCH
+#define REG_BADPAT _REG_BADPAT
+#define REG_ECOLLATE _REG_ECOLLATE
+#define REG_ECTYPE _REG_ECTYPE
+#define REG_EESCAPE _REG_EESCAPE
+#define REG_ESUBREG _REG_ESUBREG
+#define REG_EBRACK _REG_EBRACK
+#define REG_EPAREN _REG_EPAREN
+#define REG_EBRACE _REG_EBRACE
+#define REG_BADBR _REG_BADBR
+#define REG_ERANGE _REG_ERANGE
+#define REG_ESPACE _REG_ESPACE
+#define REG_BADRPT _REG_BADRPT
+#define REG_EEND _REG_EEND
+#define REG_ESIZE _REG_ESIZE
+#define REG_ERPAREN _REG_ERPAREN
+
+/* This data structure represents a compiled pattern. Before calling
+ the pattern compiler, the fields 'buffer', 'allocated', 'fastmap',
+ and 'translate' can be set. After the pattern has been compiled,
+ the fields 're_nsub', 'not_bol' and 'not_eol' are available. All
+ other fields are private to the regex routines. */
+
+#ifndef RE_TRANSLATE_TYPE
+# define __RE_TRANSLATE_TYPE unsigned char *
+# ifdef __USE_GNU
+# define RE_TRANSLATE_TYPE __RE_TRANSLATE_TYPE
+# endif
+#endif
+
+#ifdef __USE_GNU
+# define __REPB_PREFIX(name) name
+#else
+# define __REPB_PREFIX(name) __##name
+#endif
+
+struct re_pattern_buffer
+{
+ /* Space that holds the compiled pattern. The type
+ 'struct re_dfa_t' is private and is not declared here. */
+ struct re_dfa_t *__REPB_PREFIX(buffer);
+
+ /* Number of bytes to which 'buffer' points. */
+ __re_long_size_t __REPB_PREFIX(allocated);
+
+ /* Number of bytes actually used in 'buffer'. */
+ __re_long_size_t __REPB_PREFIX(used);
+
+ /* Syntax setting with which the pattern was compiled. */
+ reg_syntax_t __REPB_PREFIX(syntax);
+
+ /* Pointer to a fastmap, if any, otherwise zero. re_search uses the
+ fastmap, if there is one, to skip over impossible starting points
+ for matches. */
+ char *__REPB_PREFIX(fastmap);
+
+ /* Either a translate table to apply to all characters before
+ comparing them, or zero for no translation. The translation is
+ applied to a pattern when it is compiled and to a string when it
+ is matched. */
+ __RE_TRANSLATE_TYPE __REPB_PREFIX(translate);
+
+ /* Number of subexpressions found by the compiler. */
+ size_t re_nsub;
+
+ /* Zero if this pattern cannot match the empty string, one else.
+ Well, in truth it's used only in 're_search_2', to see whether or
+ not we should use the fastmap, so we don't set this absolutely
+ perfectly; see 're_compile_fastmap' (the "duplicate" case). */
+ unsigned __REPB_PREFIX(can_be_null) : 1;
+
+ /* If REGS_UNALLOCATED, allocate space in the 'regs' structure
+ for 'max (RE_NREGS, re_nsub + 1)' groups.
+ If REGS_REALLOCATE, reallocate space if necessary.
+ If REGS_FIXED, use what's there. */
+#ifdef __USE_GNU
+# define REGS_UNALLOCATED 0
+# define REGS_REALLOCATE 1
+# define REGS_FIXED 2
+#endif
+ unsigned __REPB_PREFIX(regs_allocated) : 2;
+
+ /* Set to zero when 're_compile_pattern' compiles a pattern; set to
+ one by 're_compile_fastmap' if it updates the fastmap. */
+ unsigned __REPB_PREFIX(fastmap_accurate) : 1;
+
+ /* If set, 're_match_2' does not return information about
+ subexpressions. */
+ unsigned __REPB_PREFIX(no_sub) : 1;
+
+ /* If set, a beginning-of-line anchor doesn't match at the beginning
+ of the string. */
+ unsigned __REPB_PREFIX(not_bol) : 1;
+
+ /* Similarly for an end-of-line anchor. */
+ unsigned __REPB_PREFIX(not_eol) : 1;
+
+ /* If true, an anchor at a newline matches. */
+ unsigned __REPB_PREFIX(newline_anchor) : 1;
+};
+
+typedef struct re_pattern_buffer regex_t;
+
+/* Type for byte offsets within the string. POSIX mandates this. */
+#ifdef _REGEX_LARGE_OFFSETS
+/* POSIX 1003.1-2008 requires that regoff_t be at least as wide as
+ ptrdiff_t and ssize_t. We don't know of any hosts where ptrdiff_t
+ is wider than ssize_t, so ssize_t is safe. ptrdiff_t is not
+ visible here, so use ssize_t. */
+typedef ssize_t regoff_t;
+#else
+/* The traditional GNU regex implementation mishandles strings longer
+ than INT_MAX. */
+typedef int regoff_t;
+#endif
+
+
+#ifdef __USE_GNU
+/* This is the structure we store register match data in. See
+ regex.texinfo for a full description of what registers match. */
+struct re_registers
+{
+ __re_size_t num_regs;
+ regoff_t *start;
+ regoff_t *end;
+};
+
+
+/* If 'regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
+ 're_match_2' returns information about at least this many registers
+ the first time a 'regs' structure is passed. */
+# ifndef RE_NREGS
+# define RE_NREGS 30
+# endif
+#endif
+
+
+/* POSIX specification for registers. Aside from the different names than
+ 're_registers', POSIX uses an array of structures, instead of a
+ structure of arrays. */
+typedef struct
+{
+ regoff_t rm_so; /* Byte offset from string's start to substring's start. */
+ regoff_t rm_eo; /* Byte offset from string's start to substring's end. */
+} regmatch_t;
+
+/* Declarations for routines. */
+
+#ifndef _REGEX_NELTS
+# if (defined __STDC_VERSION__ && 199901L <= __STDC_VERSION__ \
+ && !defined __STDC_NO_VLA__)
+# define _REGEX_NELTS(n) n
+# else
+# define _REGEX_NELTS(n)
+# endif
+#endif
+
+#if defined __GNUC__ && 4 < __GNUC__ + (6 <= __GNUC_MINOR__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wvla"
+#endif
+
+#ifndef _Attr_access_
+# ifdef __attr_access
+# define _Attr_access_(arg) __attr_access (arg)
+# elif defined __GNUC__ && 10 <= __GNUC__
+# define _Attr_access_(x) __attribute__ ((__access__ x))
+# else
+# define _Attr_access_(x)
+# endif
+#endif
+
+#ifdef __USE_GNU
+/* Sets the current default syntax to SYNTAX, and return the old syntax.
+ You can also simply assign to the 're_syntax_options' variable. */
+extern reg_syntax_t re_set_syntax (reg_syntax_t __syntax);
+
+/* Compile the regular expression PATTERN, with length LENGTH
+ and syntax given by the global 're_syntax_options', into the buffer
+ BUFFER. Return NULL if successful, and an error string if not.
+
+ To free the allocated storage, you must call 'regfree' on BUFFER.
+ Note that the translate table must either have been initialized by
+ 'regcomp', with a malloc'ed value, or set to NULL before calling
+ 'regfree'. */
+extern const char *re_compile_pattern (const char *__pattern, size_t __length,
+ struct re_pattern_buffer *__buffer)
+ _Attr_access_ ((__read_only__, 1, 2));
+
+
+/* Compile a fastmap for the compiled pattern in BUFFER; used to
+ accelerate searches. Return 0 if successful and -2 if was an
+ internal error. */
+extern int re_compile_fastmap (struct re_pattern_buffer *__buffer);
+
+
+/* Search in the string STRING (with length LENGTH) for the pattern
+ compiled into BUFFER. Start searching at position START, for RANGE
+ characters. Return the starting position of the match, -1 for no
+ match, or -2 for an internal error. Also return register
+ information in REGS (if REGS and BUFFER->no_sub are nonzero). */
+extern regoff_t re_search (struct re_pattern_buffer *__buffer,
+ const char *__String, regoff_t __length,
+ regoff_t __start, regoff_t __range,
+ struct re_registers *__regs)
+ _Attr_access_ ((__read_only__, 2, 3));
+
+
+/* Like 're_search', but search in the concatenation of STRING1 and
+ STRING2. Also, stop searching at index START + STOP. */
+extern regoff_t re_search_2 (struct re_pattern_buffer *__buffer,
+ const char *__string1, regoff_t __length1,
+ const char *__string2, regoff_t __length2,
+ regoff_t __start, regoff_t __range,
+ struct re_registers *__regs,
+ regoff_t __stop)
+ _Attr_access_ ((__read_only__, 2, 3))
+ _Attr_access_ ((__read_only__, 4, 5));
+
+
+/* Like 're_search', but return how many characters in STRING the regexp
+ in BUFFER matched, starting at position START. */
+extern regoff_t re_match (struct re_pattern_buffer *__buffer,
+ const char *__String, regoff_t __length,
+ regoff_t __start, struct re_registers *__regs)
+ _Attr_access_ ((__read_only__, 2, 3));
+
+
+/* Relates to 're_match' as 're_search_2' relates to 're_search'. */
+extern regoff_t re_match_2 (struct re_pattern_buffer *__buffer,
+ const char *__string1, regoff_t __length1,
+ const char *__string2, regoff_t __length2,
+ regoff_t __start, struct re_registers *__regs,
+ regoff_t __stop)
+ _Attr_access_ ((__read_only__, 2, 3))
+ _Attr_access_ ((__read_only__, 4, 5));
+
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+ ENDS. Subsequent matches using BUFFER and REGS will use this memory
+ for recording register information. STARTS and ENDS must be
+ allocated with malloc, and must each be at least 'NUM_REGS * sizeof
+ (regoff_t)' bytes long.
+
+ If NUM_REGS == 0, then subsequent matches should allocate their own
+ register data.
+
+ Unless this function is called, the first search or match using
+ BUFFER will allocate its own register data, without
+ freeing the old data. */
+extern void re_set_registers (struct re_pattern_buffer *__buffer,
+ struct re_registers *__regs,
+ __re_size_t __num_regs,
+ regoff_t *__starts, regoff_t *__ends);
+#endif /* Use GNU */
+
+#if defined _REGEX_RE_COMP || (defined _LIBC && defined __USE_MISC)
+/* 4.2 bsd compatibility. */
+extern char *re_comp (const char *);
+extern int re_exec (const char *);
+#endif
+
+/* For plain 'restrict', use glibc's __restrict if defined.
+ Otherwise, GCC 2.95 and later have "__restrict"; C99 compilers have
+ "restrict", and "configure" may have defined "restrict".
+ Other compilers use __restrict, __restrict__, and _Restrict, and
+ 'configure' might #define 'restrict' to those words, so pick a
+ different name. */
+#ifndef _Restrict_
+# if defined __restrict \
+ || 2 < __GNUC__ + (95 <= __GNUC_MINOR__) \
+ || __clang_major__ >= 3
+# define _Restrict_ __restrict
+# elif 199901L <= __STDC_VERSION__ || defined restrict
+# define _Restrict_ restrict
+# else
+# define _Restrict_
+# endif
+#endif
+/* For the ISO C99 syntax
+ array_name[restrict]
+ use glibc's __restrict_arr if available.
+ Otherwise, GCC 3.1 and clang support this syntax (but not in C++ mode).
+ Other ISO C99 compilers support it as well. */
+#ifndef _Restrict_arr_
+# ifdef __restrict_arr
+# define _Restrict_arr_ __restrict_arr
+# elif ((199901L <= __STDC_VERSION__ \
+ || 3 < __GNUC__ + (1 <= __GNUC_MINOR__) \
+ || __clang_major__ >= 3) \
+ && !defined __cplusplus)
+# define _Restrict_arr_ _Restrict_
+# else
+# define _Restrict_arr_
+# endif
+#endif
+
+/* POSIX compatibility. */
+extern int regcomp (regex_t *_Restrict_ __preg,
+ const char *_Restrict_ __pattern,
+ int __cflags);
+
+extern int regexec (const regex_t *_Restrict_ __preg,
+ const char *_Restrict_ __String, size_t __nmatch,
+ regmatch_t __pmatch[],
+ int __eflags);
+
+extern size_t regerror (int __errcode, const regex_t *_Restrict_ __preg,
+ char *_Restrict_ __errbuf, size_t __errbuf_size)
+ _Attr_access_ ((__write_only__, 3, 4));
+
+extern void regfree (regex_t *__preg);
+
+#if defined __GNUC__ && 4 < __GNUC__ + (6 <= __GNUC_MINOR__)
+# pragma GCC diagnostic pop
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* C++ */
+
+#endif /* regex.h */
diff --git a/scripts/stdincludes/stdarg.h b/scripts/stdincludes/stdarg.h
new file mode 100644
index 0000000..73563b4
--- /dev/null
+++ b/scripts/stdincludes/stdarg.h
@@ -0,0 +1,46 @@
+#ifndef _STDARG_H
+#define _STDARG_H
+
+typedef struct {
+ unsigned int gp_offset; unsigned int fp_offset;
+ void *overflow_arg_area; void *reg_save_area;
+} __va_elem;
+typedef __va_elem va_list;
+typedef va_list __builtin_va_list;
+typedef va_list __gnuc_va_list;
+
+extern void __builtin_va_start(void *ap, void *last);
+#define va_start(ap, last) do { void *_ap = &ap; __builtin_va_start(_ap, last); } while (0)
+#define va_end(ap)
+static void *__va_arg_mem(__va_elem *ap, int sz, int align) {
+ unsigned long p = ap->overflow_arg_area;
+ if (align > 8)
+ p = (p + 15) / 16 * 16;
+ ap->overflow_arg_area = ((unsigned long)p + sz + 7) / 8 * 8;
+ return (void*)p;
+}
+static void *__va_arg_gp(__va_elem *ap, int sz, int align) {
+ if (ap->gp_offset >= 48)
+ return __va_arg_mem(ap, sz, align);
+
+ void *r = ap->reg_save_area + ap->gp_offset;
+ ap->gp_offset += 8;
+ return r;
+}
+static void *__va_arg_fp(__va_elem *ap, int sz, int align) {
+ if (ap->fp_offset >= 112)
+ return __va_arg_mem(ap, sz, align);
+
+ void *r = ap->reg_save_area + ap->fp_offset;
+ ap->fp_offset += 8;
+ return r;
+}
+#define va_arg(ap, ty) \
+ ({ \
+ int klass = __builtin_reg_class(ty); \
+ *(ty *)(klass == 0 ? __va_arg_gp(&ap, sizeof(ty), _Alignof(ty)) : \
+ klass == 1 ? __va_arg_fp(&ap, sizeof(ty), _Alignof(ty)) : \
+ __va_arg_mem(&ap, sizeof(ty), _Alignof(ty))); \
+ })
+
+#endif
diff --git a/scripts/stdincludes/stddef.h b/scripts/stdincludes/stddef.h
new file mode 100644
index 0000000..4d71d7b
--- /dev/null
+++ b/scripts/stdincludes/stddef.h
@@ -0,0 +1,6 @@
+#define offsetof(type, member) ((unsigned long)&(((type *)0)->member))
+#define __builtin_offsetof(type, member) ((unsigned long)&(((type *)0)->member))
+#define alignof _Alignof
+#define __alignof__ _Alignof
+
+#include_next <stddef.h>
diff --git a/strings.c b/strings.c
new file mode 100644
index 0000000..9e67428
--- /dev/null
+++ b/strings.c
@@ -0,0 +1,31 @@
+#include "chibicc.h"
+
+void strarray_push(StringArray *arr, char *s) {
+ if (!arr->data) {
+ arr->data = calloc(8, sizeof(char *));
+ arr->capacity = 8;
+ }
+
+ if (arr->capacity == arr->len) {
+ arr->data = realloc(arr->data, sizeof(char *) * arr->capacity * 2);
+ arr->capacity *= 2;
+ for (int i = arr->len; i < arr->capacity; i++)
+ arr->data[i] = NULL;
+ }
+
+ arr->data[arr->len++] = s;
+}
+
+// Takes a printf-style format string and returns a formatted string.
+char *format(char *fmt, ...) {
+ char *buf;
+ size_t buflen;
+ FILE *out = open_memstream(&buf, &buflen);
+
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(out, fmt, ap);
+ va_end(ap);
+ fclose(out);
+ return buf;
+}
diff --git a/tests/array_of_structs.c b/tests/array_of_structs.c
new file mode 100644
index 0000000..0ab53b9
--- /dev/null
+++ b/tests/array_of_structs.c
@@ -0,0 +1,23 @@
+struct option {
+ char *name;
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+static struct option const options[] = {
+ {"whatisup", 1, 0, 0},
+ {"helloworld", 2, 0, 0},
+ {"version", 3, 0, 0},
+ {"help", 4, 0, 0},
+ {"etc", 5, 0, 0},
+ {0, 0, 0, 0},
+};
+
+void puts(char *x);
+
+int main() {
+ for (struct option *opt = options; opt->name; opt++) {
+ puts(opt->name);
+ }
+}
diff --git a/tests/coreutils.c b/tests/coreutils.c
new file mode 100644
index 0000000..60b9176
--- /dev/null
+++ b/tests/coreutils.c
@@ -0,0 +1,16 @@
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31))
+
+int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
diff --git a/tests/debug.c b/tests/debug.c
new file mode 100644
index 0000000..95ad579
--- /dev/null
+++ b/tests/debug.c
@@ -0,0 +1,7 @@
+void __open_too_many_args(void);
+void __open_missing_mode(void);
+
+int open () {
+ __open_too_many_args ();
+ return 0;
+}
diff --git a/tests/extern_array.c b/tests/extern_array.c
new file mode 100644
index 0000000..c24500f
--- /dev/null
+++ b/tests/extern_array.c
@@ -0,0 +1,6 @@
+extern const int foo[];
+extern void baz(void *);
+
+void bar() {
+ baz((void*)foo);
+}
diff --git a/tests/fgets.c b/tests/fgets.c
new file mode 100644
index 0000000..914eecc
--- /dev/null
+++ b/tests/fgets.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+void foo(FILE *s) {
+ char *x;
+ fgets(x, 0, s);
+}
diff --git a/tests/ldbl.c b/tests/ldbl.c
new file mode 100644
index 0000000..f3d8cae
--- /dev/null
+++ b/tests/ldbl.c
@@ -0,0 +1,9 @@
+// #include <float.h>
+int main (void) {
+ typedef int check[sizeof (long double) == sizeof (double)
+ // && LDBL_MANT_DIG == DBL_MANT_DIG
+ // && LDBL_MAX_EXP == DBL_MAX_EXP
+ // && LDBL_MIN_EXP == DBL_MIN_EXP
+ ? 1 : -1];
+ return 0;
+}
diff --git a/tests/local_array.c b/tests/local_array.c
new file mode 100644
index 0000000..3d07d04
--- /dev/null
+++ b/tests/local_array.c
@@ -0,0 +1,4 @@
+int lookup_arr(int i) {
+ int arr[3];
+ return arr[i];
+}
diff --git a/tests/local_const_array.c b/tests/local_const_array.c
new file mode 100644
index 0000000..38de887
--- /dev/null
+++ b/tests/local_const_array.c
@@ -0,0 +1,4 @@
+int lookup_arr(int i) {
+ int arr[1] = {1};
+ return arr[i];
+}
diff --git a/tests/no_includes.c b/tests/no_includes.c
new file mode 100644
index 0000000..89796d0
--- /dev/null
+++ b/tests/no_includes.c
@@ -0,0 +1,15 @@
+typedef struct ___dietc_va_list { char ___dietc_va_list[24]; } va_list;
+
+void foo(int x, int y) {
+ x = y;
+ va_list z;
+ va_list a;
+ z = a;
+}
+
+int main() {
+ int x = 5;
+ int y = 7;
+ x = y;
+ return x;
+}
diff --git a/tests/ptr_arith.c b/tests/ptr_arith.c
new file mode 100644
index 0000000..1f0d0aa
--- /dev/null
+++ b/tests/ptr_arith.c
@@ -0,0 +1,4 @@
+int main(void) {
+ int *x;
+ x[5] = 1;
+}
diff --git a/tests/struct_return.c b/tests/struct_return.c
new file mode 100644
index 0000000..3d98574
--- /dev/null
+++ b/tests/struct_return.c
@@ -0,0 +1,16 @@
+struct foo {
+ int xyz1;
+ int xyz2;
+ int xyz3;
+ int xyz4;
+ int xyz5;
+ int xyz6;
+ int xyz7;
+ int xyz8;
+};
+
+extern struct foo baz(int bar);
+
+struct foo baz(int bar) {
+ return (struct foo){1};
+}
diff --git a/tests/super_simple.c b/tests/super_simple.c
new file mode 100644
index 0000000..8f1a336
--- /dev/null
+++ b/tests/super_simple.c
@@ -0,0 +1,5 @@
+int main(void) {
+ int x = 1;
+ int y = 2;
+ return y;
+}
diff --git a/tests/test.c b/tests/test.c
new file mode 100644
index 0000000..2e23b8b
--- /dev/null
+++ b/tests/test.c
@@ -0,0 +1,21 @@
+#include <stdio.h>
+
+struct foo {
+ long bar;
+ char baz;
+};
+
+struct foo bar = {1,2};
+
+struct foo *barptr[2] = {&bar, &bar};
+struct foo **barptrptr[2] = {&barptr[0], &barptr[1]};
+
+int main(void) {
+ int x = 5;
+ if (x)
+ x++;
+ long y = 6;
+ y++;
+ printf("Hello, World!\n");
+ return 0;
+}
diff --git a/tests/union_init.c b/tests/union_init.c
new file mode 100644
index 0000000..80cee1d
--- /dev/null
+++ b/tests/union_init.c
@@ -0,0 +1,10 @@
+union foo {
+ int x;
+ long y;
+};
+
+int foo() {
+ static union foo initval;
+ union foo v = initval;
+ return 0;
+}
diff --git a/tests/unnamed_fields.c b/tests/unnamed_fields.c
new file mode 100644
index 0000000..d259d44
--- /dev/null
+++ b/tests/unnamed_fields.c
@@ -0,0 +1,13 @@
+struct xyz {
+ int z;
+ struct {
+ int x;
+ int y;
+ };
+};
+
+int main() {
+ struct xyz abc;
+ abc.x = 5;
+ return abc.x;
+}
diff --git a/tests/unsized_extern_array.c b/tests/unsized_extern_array.c
new file mode 100644
index 0000000..112a590
--- /dev/null
+++ b/tests/unsized_extern_array.c
@@ -0,0 +1,4 @@
+extern const unsigned int arr[];
+int lookup_arr(int i) {
+ return arr[i];
+}
diff --git a/tests/va_start.c b/tests/va_start.c
new file mode 100644
index 0000000..8644950
--- /dev/null
+++ b/tests/va_start.c
@@ -0,0 +1,21 @@
+// https://en.cppreference.com/w/cpp/utility/variadic/va_start
+
+#include <stdio.h>
+#include <stdarg.h>
+
+int add_nums(int count, ...)
+{
+ int result = 0;
+ va_list args;
+ va_start(args, count);
+ for (int i = 0; i < count; ++i) {
+ result += va_arg(args, int);
+ }
+ va_end(args);
+ return result;
+}
+
+int main()
+{
+ printf("%d\n", add_nums(4, 25, 25, 50, 50));
+}
diff --git a/tokenize.c b/tokenize.c
new file mode 100644
index 0000000..5c49c02
--- /dev/null
+++ b/tokenize.c
@@ -0,0 +1,805 @@
+#include "chibicc.h"
+
+// Input file
+static File *current_file;
+
+// A list of all input files.
+static File **input_files;
+
+// True if the current position is at the beginning of a line
+static bool at_bol;
+
+// True if the current position follows a space character
+static bool has_space;
+
+// Reports an error and exit.
+void error(char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+// Reports an error message in the following format.
+//
+// foo.c:10: x = y + 1;
+// ^ <error message here>
+static void verror_at(char *filename, char *input, int line_no,
+ char *loc, char *fmt, va_list ap) {
+ // Find a line containing `loc`.
+ char *line = loc;
+ while (input < line && line[-1] != '\n')
+ line--;
+
+ char *end = loc;
+ while (*end && *end != '\n')
+ end++;
+
+ // Print out the line.
+ int indent = fprintf(stderr, "%s:%d: ", filename, line_no);
+ fprintf(stderr, "%.*s\n", (int)(end - line), line);
+
+ // Show the error message.
+ int pos = display_width(line, loc - line) + indent;
+
+ fprintf(stderr, "%*s", pos, ""); // print pos spaces.
+ fprintf(stderr, "^ ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+}
+
+void error_at(char *loc, char *fmt, ...) {
+ int line_no = 1;
+ for (char *p = current_file->contents; p < loc; p++)
+ if (*p == '\n')
+ line_no++;
+
+ va_list ap;
+ va_start(ap, fmt);
+ verror_at(current_file->name, current_file->contents, line_no, loc, fmt, ap);
+ exit(1);
+}
+
+void error_tok(Token *tok, char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ verror_at(tok->file->name, tok->file->contents, tok->line_no, tok->loc, fmt, ap);
+ exit(1);
+}
+
+void warn_tok(Token *tok, char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ verror_at(tok->file->name, tok->file->contents, tok->line_no, tok->loc, fmt, ap);
+ va_end(ap);
+}
+
+// Consumes the current token if it matches `op`.
+bool equal(Token *tok, char *op) {
+ return memcmp(tok->loc, op, tok->len) == 0 && op[tok->len] == '\0';
+}
+
+// Ensure that the current token is `op`.
+Token *skip(Token *tok, char *op) {
+ if (!equal(tok, op))
+ error_tok(tok, "expected '%s'", op);
+ return tok->next;
+}
+
+bool consume(Token **rest, Token *tok, char *str) {
+ if (equal(tok, str)) {
+ *rest = tok->next;
+ return true;
+ }
+ *rest = tok;
+ return false;
+}
+
+// Create a new token.
+static Token *new_token(TokenKind kind, char *start, char *end) {
+ Token *tok = calloc(1, sizeof(Token));
+ tok->kind = kind;
+ tok->loc = start;
+ tok->len = end - start;
+ tok->file = current_file;
+ tok->filename = current_file->display_name;
+ tok->at_bol = at_bol;
+ tok->has_space = has_space;
+
+ at_bol = has_space = false;
+ return tok;
+}
+
+static bool startswith(char *p, char *q) {
+ return strncmp(p, q, strlen(q)) == 0;
+}
+
+// Read an identifier and returns the length of it.
+// If p does not point to a valid identifier, 0 is returned.
+static int read_ident(char *start) {
+ char *p = start;
+ uint32_t c = decode_utf8(&p, p);
+ if (!is_ident1(c))
+ return 0;
+
+ for (;;) {
+ char *q;
+ c = decode_utf8(&q, p);
+ if (!is_ident2(c))
+ return p - start;
+ p = q;
+ }
+}
+
+static int from_hex(char c) {
+ if ('0' <= c && c <= '9')
+ return c - '0';
+ if ('a' <= c && c <= 'f')
+ return c - 'a' + 10;
+ return c - 'A' + 10;
+}
+
+// Read a punctuator token from p and returns its length.
+static int read_punct(char *p) {
+ static char *kw[] = {
+ "<<=", ">>=", "...", "==", "!=", "<=", ">=", "->", "+=",
+ "-=", "*=", "/=", "++", "--", "%=", "&=", "|=", "^=", "&&",
+ "||", "<<", ">>", "##",
+ };
+
+ for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++)
+ if (startswith(p, kw[i]))
+ return strlen(kw[i]);
+
+ return ispunct(*p) ? 1 : 0;
+}
+
+static bool is_keyword(Token *tok) {
+ static HashMap map;
+
+ if (map.capacity == 0) {
+ static char *kw[] = {
+ "return", "if", "else", "for", "while", "int", "sizeof", "char",
+ "struct", "union", "short", "long", "void", "typedef", "_Bool",
+ "enum", "static", "goto", "break", "continue", "switch", "case",
+ "default", "extern", "_Alignof", "_Alignas", "do", "signed",
+ "unsigned", "const", "volatile", "auto", "register", "restrict",
+ "__restrict", "__restrict__", "_Noreturn", "float", "double",
+ "typeof", "asm", "_Thread_local", "__thread", "_Atomic",
+ "__attribute__",
+ };
+
+ for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++)
+ hashmap_put(&map, kw[i], (void *)1);
+ }
+
+ return hashmap_get2(&map, tok->loc, tok->len);
+}
+
+static int read_escaped_char(char **new_pos, char *p) {
+ if ('0' <= *p && *p <= '7') {
+ // Read an octal number.
+ int c = *p++ - '0';
+ if ('0' <= *p && *p <= '7') {
+ c = (c << 3) + (*p++ - '0');
+ if ('0' <= *p && *p <= '7')
+ c = (c << 3) + (*p++ - '0');
+ }
+ *new_pos = p;
+ return c;
+ }
+
+ if (*p == 'x') {
+ // Read a hexadecimal number.
+ p++;
+ if (!isxdigit(*p))
+ error_at(p, "invalid hex escape sequence");
+
+ int c = 0;
+ for (; isxdigit(*p); p++)
+ c = (c << 4) + from_hex(*p);
+ *new_pos = p;
+ return c;
+ }
+
+ *new_pos = p + 1;
+
+ // Escape sequences are defined using themselves here. E.g.
+ // '\n' is implemented using '\n'. This tautological definition
+ // works because the compiler that compiles our compiler knows
+ // what '\n' actually is. In other words, we "inherit" the ASCII
+ // code of '\n' from the compiler that compiles our compiler,
+ // so we don't have to teach the actual code here.
+ //
+ // This fact has huge implications not only for the correctness
+ // of the compiler but also for the security of the generated code.
+ // For more info, read "Reflections on Trusting Trust" by Ken Thompson.
+ // https://github.com/rui314/chibicc/wiki/thompson1984.pdf
+ switch (*p) {
+ case 'a': return '\a';
+ case 'b': return '\b';
+ case 't': return '\t';
+ case 'n': return '\n';
+ case 'v': return '\v';
+ case 'f': return '\f';
+ case 'r': return '\r';
+ // [GNU] \e for the ASCII escape character is a GNU C extension.
+ case 'e': return 27;
+ default: return *p;
+ }
+}
+
+// Find a closing double-quote.
+static char *string_literal_end(char *p) {
+ char *start = p;
+ for (; *p != '"'; p++) {
+ if (*p == '\n' || *p == '\0')
+ error_at(start, "unclosed string literal");
+ if (*p == '\\')
+ p++;
+ }
+ return p;
+}
+
+static Token *read_string_literal(char *start, char *quote) {
+ char *end = string_literal_end(quote + 1);
+ char *buf = calloc(1, end - quote);
+ int len = 0;
+
+ for (char *p = quote + 1; p < end;) {
+ if (*p == '\\')
+ buf[len++] = read_escaped_char(&p, p + 1);
+ else
+ buf[len++] = *p++;
+ }
+
+ Token *tok = new_token(TK_STR, start, end + 1);
+ tok->ty = array_of(ty_char, len + 1);
+ tok->str = buf;
+ return tok;
+}
+
+// Read a UTF-8-encoded string literal and transcode it in UTF-16.
+//
+// UTF-16 is yet another variable-width encoding for Unicode. Code
+// points smaller than U+10000 are encoded in 2 bytes. Code points
+// equal to or larger than that are encoded in 4 bytes. Each 2 bytes
+// in the 4 byte sequence is called "surrogate", and a 4 byte sequence
+// is called a "surrogate pair".
+static Token *read_utf16_string_literal(char *start, char *quote) {
+ char *end = string_literal_end(quote + 1);
+ uint16_t *buf = calloc(2, end - start);
+ int len = 0;
+
+ for (char *p = quote + 1; p < end;) {
+ if (*p == '\\') {
+ buf[len++] = read_escaped_char(&p, p + 1);
+ continue;
+ }
+
+ uint32_t c = decode_utf8(&p, p);
+ if (c < 0x10000) {
+ // Encode a code point in 2 bytes.
+ buf[len++] = c;
+ } else {
+ // Encode a code point in 4 bytes.
+ c -= 0x10000;
+ buf[len++] = 0xd800 + ((c >> 10) & 0x3ff);
+ buf[len++] = 0xdc00 + (c & 0x3ff);
+ }
+ }
+
+ Token *tok = new_token(TK_STR, start, end + 1);
+ tok->ty = array_of(ty_ushort, len + 1);
+ tok->str = (char *)buf;
+ return tok;
+}
+
+// Read a UTF-8-encoded string literal and transcode it in UTF-32.
+//
+// UTF-32 is a fixed-width encoding for Unicode. Each code point is
+// encoded in 4 bytes.
+static Token *read_utf32_string_literal(char *start, char *quote, Type *ty) {
+ char *end = string_literal_end(quote + 1);
+ uint32_t *buf = calloc(4, end - quote);
+ int len = 0;
+
+ for (char *p = quote + 1; p < end;) {
+ if (*p == '\\')
+ buf[len++] = read_escaped_char(&p, p + 1);
+ else
+ buf[len++] = decode_utf8(&p, p);
+ }
+
+ Token *tok = new_token(TK_STR, start, end + 1);
+ tok->ty = array_of(ty, len + 1);
+ tok->str = (char *)buf;
+ return tok;
+}
+
+static Token *read_char_literal(char *start, char *quote, Type *ty) {
+ char *p = quote + 1;
+ if (*p == '\0')
+ error_at(start, "unclosed char literal");
+
+ int c;
+ if (*p == '\\')
+ c = read_escaped_char(&p, p + 1);
+ else
+ c = decode_utf8(&p, p);
+
+ char *end = strchr(p, '\'');
+ if (!end)
+ error_at(p, "unclosed char literal");
+
+ Token *tok = new_token(TK_NUM, start, end + 1);
+ tok->val = c;
+ tok->ty = ty;
+ return tok;
+}
+
+static bool convert_pp_int(Token *tok) {
+ char *p = tok->loc;
+
+ // Read a binary, octal, decimal or hexadecimal number.
+ int base = 10;
+ if (!strncasecmp(p, "0x", 2) && isxdigit(p[2])) {
+ p += 2;
+ base = 16;
+ } else if (!strncasecmp(p, "0b", 2) && (p[2] == '0' || p[2] == '1')) {
+ p += 2;
+ base = 2;
+ } else if (*p == '0') {
+ base = 8;
+ }
+
+ int64_t val = strtoul(p, &p, base);
+
+ // Read U, L or LL suffixes.
+ bool l = false;
+ bool u = false;
+
+ if (startswith(p, "LLU") || startswith(p, "LLu") ||
+ startswith(p, "llU") || startswith(p, "llu") ||
+ startswith(p, "ULL") || startswith(p, "Ull") ||
+ startswith(p, "uLL") || startswith(p, "ull")) {
+ p += 3;
+ l = u = true;
+ } else if (!strncasecmp(p, "lu", 2) || !strncasecmp(p, "ul", 2)) {
+ p += 2;
+ l = u = true;
+ } else if (startswith(p, "LL") || startswith(p, "ll")) {
+ p += 2;
+ l = true;
+ } else if (*p == 'L' || *p == 'l') {
+ p++;
+ l = true;
+ } else if (*p == 'U' || *p == 'u') {
+ p++;
+ u = true;
+ }
+
+ if (p != tok->loc + tok->len)
+ return false;
+
+ // Infer a type.
+ Type *ty;
+ if (base == 10) {
+ if (l && u)
+ ty = ty_ulong;
+ else if (l)
+ ty = ty_long;
+ else if (u)
+ ty = (val >> 32) ? ty_ulong : ty_uint;
+ else
+ ty = (val >> 31) ? ty_long : ty_int;
+ } else {
+ if (l && u)
+ ty = ty_ulong;
+ else if (l)
+ ty = (val >> 63) ? ty_ulong : ty_long;
+ else if (u)
+ ty = (val >> 32) ? ty_ulong : ty_uint;
+ else if (val >> 63)
+ ty = ty_ulong;
+ else if (val >> 32)
+ ty = ty_long;
+ else if (val >> 31)
+ ty = ty_uint;
+ else
+ ty = ty_int;
+ }
+
+ tok->kind = TK_NUM;
+ tok->val = val;
+ tok->ty = ty;
+ return true;
+}
+
+// The definition of the numeric literal at the preprocessing stage
+// is more relaxed than the definition of that at the later stages.
+// In order to handle that, a numeric literal is tokenized as a
+// "pp-number" token first and then converted to a regular number
+// token after preprocessing.
+//
+// This function converts a pp-number token to a regular number token.
+static void convert_pp_number(Token *tok) {
+ // Try to parse as an integer constant.
+ if (convert_pp_int(tok))
+ return;
+
+ // If it's not an integer, it must be a floating point constant.
+ char *end;
+ long double val = strtold(tok->loc, &end);
+
+ Type *ty;
+ if (*end == 'f' || *end == 'F') {
+ ty = ty_float;
+ end++;
+ } else if (*end == 'l' || *end == 'L') {
+ ty = ty_ldouble;
+ end++;
+ } else {
+ ty = ty_double;
+ }
+
+ if (tok->loc + tok->len != end)
+ error_tok(tok, "invalid numeric constant");
+
+ tok->kind = TK_NUM;
+ tok->fval = val;
+ tok->ty = ty;
+}
+
+void convert_pp_tokens(Token *tok) {
+ for (Token *t = tok; t->kind != TK_EOF; t = t->next) {
+ if (is_keyword(t))
+ t->kind = TK_KEYWORD;
+ else if (t->kind == TK_PP_NUM)
+ convert_pp_number(t);
+ }
+}
+
+// Initialize line info for all tokens.
+static void add_line_numbers(Token *tok) {
+ char *p = current_file->contents;
+ int n = 1;
+
+ do {
+ if (p == tok->loc) {
+ tok->line_no = n;
+ tok = tok->next;
+ }
+ if (*p == '\n')
+ n++;
+ } while (*p++);
+}
+
+Token *tokenize_string_literal(Token *tok, Type *basety) {
+ Token *t;
+ if (basety->size == 2)
+ t = read_utf16_string_literal(tok->loc, tok->loc);
+ else
+ t = read_utf32_string_literal(tok->loc, tok->loc, basety);
+ t->next = tok->next;
+ return t;
+}
+
+// Tokenize a given string and returns new tokens.
+Token *tokenize(File *file) {
+ current_file = file;
+
+ char *p = file->contents;
+ Token head = {};
+ Token *cur = &head;
+
+ at_bol = true;
+ has_space = false;
+
+ while (*p) {
+ // Skip line comments.
+ if (startswith(p, "//")) {
+ p += 2;
+ while (*p != '\n')
+ p++;
+ has_space = true;
+ continue;
+ }
+
+ // Skip block comments.
+ if (startswith(p, "/*")) {
+ char *q = strstr(p + 2, "*/");
+ if (!q)
+ error_at(p, "unclosed block comment");
+ p = q + 2;
+ has_space = true;
+ continue;
+ }
+
+ // Skip newline.
+ if (*p == '\n') {
+ p++;
+ at_bol = true;
+ has_space = false;
+ continue;
+ }
+
+ // Skip whitespace characters.
+ if (isspace(*p)) {
+ p++;
+ has_space = true;
+ continue;
+ }
+
+ // Numeric literal
+ if (isdigit(*p) || (*p == '.' && isdigit(p[1]))) {
+ char *q = p++;
+ for (;;) {
+ if (p[0] && p[1] && strchr("eEpP", p[0]) && strchr("+-", p[1]))
+ p += 2;
+ else if (isalnum(*p) || *p == '.')
+ p++;
+ else
+ break;
+ }
+ cur = cur->next = new_token(TK_PP_NUM, q, p);
+ continue;
+ }
+
+ // String literal
+ if (*p == '"') {
+ cur = cur->next = read_string_literal(p, p);
+ p += cur->len;
+ continue;
+ }
+
+ // UTF-8 string literal
+ if (startswith(p, "u8\"")) {
+ cur = cur->next = read_string_literal(p, p + 2);
+ p += cur->len;
+ continue;
+ }
+
+ // UTF-16 string literal
+ if (startswith(p, "u\"")) {
+ cur = cur->next = read_utf16_string_literal(p, p + 1);
+ p += cur->len;
+ continue;
+ }
+
+ // Wide string literal
+ if (startswith(p, "L\"")) {
+ cur = cur->next = read_utf32_string_literal(p, p + 1, ty_int);
+ p += cur->len;
+ continue;
+ }
+
+ // UTF-32 string literal
+ if (startswith(p, "U\"")) {
+ cur = cur->next = read_utf32_string_literal(p, p + 1, ty_uint);
+ p += cur->len;
+ continue;
+ }
+
+ // Character literal
+ if (*p == '\'') {
+ cur = cur->next = read_char_literal(p, p, ty_int);
+ cur->val = (char)cur->val;
+ p += cur->len;
+ continue;
+ }
+
+ // UTF-16 character literal
+ if (startswith(p, "u'")) {
+ cur = cur->next = read_char_literal(p, p + 1, ty_ushort);
+ cur->val &= 0xffff;
+ p += cur->len;
+ continue;
+ }
+
+ // Wide character literal
+ if (startswith(p, "L'")) {
+ cur = cur->next = read_char_literal(p, p + 1, ty_int);
+ p += cur->len;
+ continue;
+ }
+
+ // UTF-32 character literal
+ if (startswith(p, "U'")) {
+ cur = cur->next = read_char_literal(p, p + 1, ty_uint);
+ p += cur->len;
+ continue;
+ }
+
+ // Identifier or keyword
+ int ident_len = read_ident(p);
+ if (ident_len) {
+ cur = cur->next = new_token(TK_IDENT, p, p + ident_len);
+ p += cur->len;
+ continue;
+ }
+
+ // Punctuators
+ int punct_len = read_punct(p);
+ if (punct_len) {
+ cur = cur->next = new_token(TK_PUNCT, p, p + punct_len);
+ p += cur->len;
+ continue;
+ }
+
+ error_at(p, "invalid token");
+ }
+
+ cur = cur->next = new_token(TK_EOF, p, p);
+ add_line_numbers(head.next);
+ return head.next;
+}
+
+// Returns the contents of a given file.
+static char *read_file(char *path) {
+ FILE *fp;
+
+ if (strcmp(path, "-") == 0) {
+ // By convention, read from stdin if a given filename is "-".
+ fp = stdin;
+ } else {
+ fp = fopen(path, "r");
+ if (!fp)
+ return NULL;
+ }
+
+ char *buf;
+ size_t buflen;
+ FILE *out = open_memstream(&buf, &buflen);
+
+ // Read the entire file.
+ for (;;) {
+ char buf2[4096];
+ int n = fread(buf2, 1, sizeof(buf2), fp);
+ if (n == 0)
+ break;
+ fwrite(buf2, 1, n, out);
+ }
+
+ if (fp != stdin)
+ fclose(fp);
+
+ // Make sure that the last line is properly terminated with '\n'.
+ fflush(out);
+ if (buflen == 0 || buf[buflen - 1] != '\n')
+ fputc('\n', out);
+ fputc('\0', out);
+ fclose(out);
+ return buf;
+}
+
+File **get_input_files(void) {
+ return input_files;
+}
+
+File *new_file(char *name, int file_no, char *contents) {
+ File *file = calloc(1, sizeof(File));
+ file->name = name;
+ file->display_name = name;
+ file->file_no = file_no;
+ file->contents = contents;
+ return file;
+}
+
+// Replaces \r or \r\n with \n.
+static void canonicalize_newline(char *p) {
+ int i = 0, j = 0;
+
+ while (p[i]) {
+ if (p[i] == '\r' && p[i + 1] == '\n') {
+ i += 2;
+ p[j++] = '\n';
+ } else if (p[i] == '\r') {
+ i++;
+ p[j++] = '\n';
+ } else {
+ p[j++] = p[i++];
+ }
+ }
+
+ p[j] = '\0';
+}
+
+// Removes backslashes followed by a newline.
+static void remove_backslash_newline(char *p) {
+ int i = 0, j = 0;
+
+ // We want to keep the number of newline characters so that
+ // the logical line number matches the physical one.
+ // This counter maintain the number of newlines we have removed.
+ int n = 0;
+
+ while (p[i]) {
+ if (p[i] == '\\' && p[i + 1] == '\n') {
+ i += 2;
+ n++;
+ } else if (p[i] == '\n') {
+ p[j++] = p[i++];
+ for (; n > 0; n--)
+ p[j++] = '\n';
+ } else {
+ p[j++] = p[i++];
+ }
+ }
+
+ for (; n > 0; n--)
+ p[j++] = '\n';
+ p[j] = '\0';
+}
+
+static uint32_t read_universal_char(char *p, int len) {
+ uint32_t c = 0;
+ for (int i = 0; i < len; i++) {
+ if (!isxdigit(p[i]))
+ return 0;
+ c = (c << 4) | from_hex(p[i]);
+ }
+ return c;
+}
+
+// Replace \u or \U escape sequences with corresponding UTF-8 bytes.
+static void convert_universal_chars(char *p) {
+ char *q = p;
+
+ while (*p) {
+ if (startswith(p, "\\u")) {
+ uint32_t c = read_universal_char(p + 2, 4);
+ if (c) {
+ p += 6;
+ q += encode_utf8(q, c);
+ } else {
+ *q++ = *p++;
+ }
+ } else if (startswith(p, "\\U")) {
+ uint32_t c = read_universal_char(p + 2, 8);
+ if (c) {
+ p += 10;
+ q += encode_utf8(q, c);
+ } else {
+ *q++ = *p++;
+ }
+ } else if (p[0] == '\\') {
+ *q++ = *p++;
+ *q++ = *p++;
+ } else {
+ *q++ = *p++;
+ }
+ }
+
+ *q = '\0';
+}
+
+Token *tokenize_file(char *path) {
+ char *p = read_file(path);
+ if (!p)
+ return NULL;
+
+ // UTF-8 texts may start with a 3-byte "BOM" marker sequence.
+ // If exists, just skip them because they are useless bytes.
+ // (It is actually not recommended to add BOM markers to UTF-8
+ // texts, but it's not uncommon particularly on Windows.)
+ if (!memcmp(p, "\xef\xbb\xbf", 3))
+ p += 3;
+
+ canonicalize_newline(p);
+ remove_backslash_newline(p);
+ convert_universal_chars(p);
+
+ // Save the filename for assembler .file directive.
+ static int file_no;
+ File *file = new_file(path, file_no + 1, p);
+
+ // Save the filename for assembler .file directive.
+ input_files = realloc(input_files, sizeof(char *) * (file_no + 2));
+ input_files[file_no] = file;
+ input_files[file_no + 1] = NULL;
+ file_no++;
+
+ return tokenize(file);
+}
diff --git a/type.c b/type.c
new file mode 100644
index 0000000..2e43bf2
--- /dev/null
+++ b/type.c
@@ -0,0 +1,321 @@
+#include "chibicc.h"
+
+Type *ty_void = &(Type){TY_VOID, 1, 1};
+Type *ty_bool = &(Type){TY_BOOL, 1, 1};
+
+Type *ty_char = &(Type){TY_CHAR, 1, 1};
+Type *ty_short = &(Type){TY_SHORT, 2, 2};
+Type *ty_int = &(Type){TY_INT, 4, 4};
+Type *ty_long = &(Type){TY_LONG, 8, 8};
+
+Type *ty_uchar = &(Type){TY_CHAR, 1, 1, true};
+Type *ty_ushort = &(Type){TY_SHORT, 2, 2, true};
+Type *ty_uint = &(Type){TY_INT, 4, 4, true};
+Type *ty_ulong = &(Type){TY_LONG, 8, 8, true};
+
+Type *ty_float = &(Type){TY_FLOAT, 4, 4};
+Type *ty_double = &(Type){TY_DOUBLE, 8, 8};
+Type *ty_ldouble = &(Type){TY_LDOUBLE, 16, 16};
+
+static Type *new_type(TypeKind kind, int size, int align) {
+ Type *ty = calloc(1, sizeof(Type));
+ ty->kind = kind;
+ ty->size = size;
+ ty->align = align;
+ return ty;
+}
+
+bool is_integer(Type *ty) {
+ TypeKind k = ty->kind;
+ return k == TY_BOOL || k == TY_CHAR || k == TY_SHORT ||
+ k == TY_INT || k == TY_LONG || k == TY_ENUM;
+}
+
+bool is_flonum(Type *ty) {
+ return ty->kind == TY_FLOAT || ty->kind == TY_DOUBLE ||
+ ty->kind == TY_LDOUBLE;
+}
+
+bool is_numeric(Type *ty) {
+ return is_integer(ty) || is_flonum(ty);
+}
+
+bool is_compatible(Type *t1, Type *t2) {
+ if (t1 == t2)
+ return true;
+
+ if (t1->origin)
+ return is_compatible(t1->origin, t2);
+
+ if (t2->origin)
+ return is_compatible(t1, t2->origin);
+
+ if (t1->kind != t2->kind)
+ return false;
+
+ switch (t1->kind) {
+ case TY_CHAR:
+ case TY_SHORT:
+ case TY_INT:
+ case TY_LONG:
+ return t1->is_unsigned == t2->is_unsigned;
+ case TY_FLOAT:
+ case TY_DOUBLE:
+ case TY_LDOUBLE:
+ return true;
+ case TY_PTR:
+ return is_compatible(t1->base, t2->base);
+ case TY_FUNC: {
+ if (!is_compatible(t1->return_ty, t2->return_ty))
+ return false;
+ if (t1->is_variadic != t2->is_variadic)
+ return false;
+
+ Type *p1 = t1->params;
+ Type *p2 = t2->params;
+ for (; p1 && p2; p1 = p1->next, p2 = p2->next)
+ if (!is_compatible(p1, p2))
+ return false;
+ return p1 == NULL && p2 == NULL;
+ }
+ case TY_ARRAY:
+ if (!is_compatible(t1->base, t2->base))
+ return false;
+ return t1->array_len < 0 && t2->array_len < 0 &&
+ t1->array_len == t2->array_len;
+ }
+ return false;
+}
+
+Type *copy_type(Type *ty) {
+ Type *ret = calloc(1, sizeof(Type));
+ *ret = *ty;
+ ret->origin = ty;
+ return ret;
+}
+
+Type *pointer_to(Type *base) {
+ Type *ty = new_type(TY_PTR, 8, 8);
+ ty->base = base;
+ ty->is_unsigned = true;
+ return ty;
+}
+
+Type *func_type(Type *return_ty) {
+ // The C spec disallows sizeof(<function type>), but
+ // GCC allows that and the expression is evaluated to 1.
+ Type *ty = new_type(TY_FUNC, 1, 1);
+ ty->return_ty = return_ty;
+ return ty;
+}
+
+Type *array_of(Type *base, int len) {
+ Type *ty = new_type(TY_ARRAY, base->size * len, base->align);
+ ty->base = base;
+ ty->array_len = len;
+ return ty;
+}
+
+Type *vla_of(Type *base, Node *len) {
+ Type *ty = new_type(TY_VLA, 8, 8);
+ ty->base = base;
+ ty->vla_len = len;
+ return ty;
+}
+
+Type *enum_type(void) {
+ return new_type(TY_ENUM, 4, 4);
+}
+
+Type *struct_type(void) {
+ return new_type(TY_STRUCT, 0, 1);
+}
+
+static Type *get_common_type(Type *ty1, Type *ty2) {
+ if (ty1->base)
+ return pointer_to(ty1->base);
+
+ if (ty1->kind == TY_FUNC)
+ return pointer_to(ty1);
+ if (ty2->kind == TY_FUNC)
+ return pointer_to(ty2);
+
+ if (ty1->kind == TY_LDOUBLE || ty2->kind == TY_LDOUBLE)
+ return ty_ldouble;
+ if (ty1->kind == TY_DOUBLE || ty2->kind == TY_DOUBLE)
+ return ty_double;
+ if (ty1->kind == TY_FLOAT || ty2->kind == TY_FLOAT)
+ return ty_float;
+
+ if (ty1->size < 4)
+ ty1 = ty_int;
+ if (ty2->size < 4)
+ ty2 = ty_int;
+
+ if (ty1->size != ty2->size)
+ return (ty1->size < ty2->size) ? ty2 : ty1;
+
+ if (ty2->is_unsigned)
+ return ty2;
+ return ty1;
+}
+
+// For many binary operators, we implicitly promote operands so that
+// both operands have the same type. Any integral type smaller than
+// int is always promoted to int. If the type of one operand is larger
+// than the other's (e.g. "long" vs. "int"), the smaller operand will
+// be promoted to match with the other.
+//
+// This operation is called the "usual arithmetic conversion".
+static void usual_arith_conv(Node **lhs, Node **rhs) {
+ Type *ty = get_common_type((*lhs)->ty, (*rhs)->ty);
+ if (ty == (*lhs)->ty && ty == (*rhs)->ty)
+ return;
+ if (ty->base && is_numeric((*rhs)->ty)) {
+ if ((*rhs)->ty != ty_ulong)
+ *rhs = new_cast(*rhs, ty_ulong);
+ return;
+ }
+ *lhs = new_cast(*lhs, ty);
+ *rhs = new_cast(*rhs, ty);
+}
+
+void add_type(Node *node) {
+ if (node && node->kind == ND_VAR && node->ty && node->ty->base && node->ty->kind == TY_ARRAY)
+ node->ty = pointer_to(node->ty->base);
+
+ if (!node || node->ty)
+ return;
+
+ add_type(node->lhs);
+ add_type(node->rhs);
+ add_type(node->cond);
+ add_type(node->then);
+ add_type(node->els);
+ add_type(node->init);
+ add_type(node->inc);
+
+ for (Node *n = node->body; n; n = n->next)
+ add_type(n);
+ for (Node *n = node->args; n; n = n->next)
+ add_type(n);
+
+ switch (node->kind) {
+ case ND_NUM:
+ node->ty = ty_int;
+ return;
+ case ND_ADD:
+ case ND_SUB:
+ case ND_MUL:
+ case ND_DIV:
+ case ND_MOD:
+ case ND_BITAND:
+ case ND_BITOR:
+ case ND_BITXOR:
+ usual_arith_conv(&node->lhs, &node->rhs);
+ if (node->lhs->ty->kind != TY_ARRAY)
+ node->ty = node->lhs->ty;
+ else
+ node->ty = pointer_to(node->lhs->ty->base);
+ return;
+ case ND_NEG: {
+ Type *ty = get_common_type(ty_int, node->lhs->ty);
+ node->lhs = new_cast(node->lhs, ty);
+ node->ty = ty;
+ return;
+ }
+ case ND_ASSIGN:
+ if (node->lhs->ty->kind == TY_ARRAY)
+ error_tok(node->lhs->tok, "not an lvalue");
+ if (node->lhs->ty->kind != TY_STRUCT)
+ node->rhs = new_cast(node->rhs, node->lhs->ty);
+ node->ty = node->lhs->ty;
+ return;
+ case ND_EQ:
+ case ND_NE:
+ case ND_LT:
+ case ND_LE:
+ usual_arith_conv(&node->lhs, &node->rhs);
+ node->ty = ty_int;
+ return;
+ case ND_FUNCALL:
+ node->ty = node->func_ty->return_ty;
+ return;
+ case ND_NOT:
+ case ND_LOGOR:
+ case ND_LOGAND:
+ node->ty = ty_int;
+ return;
+ case ND_BITNOT:
+ case ND_SHL:
+ case ND_SHR:
+ node->ty = node->lhs->ty;
+ return;
+ case ND_VAR:
+ case ND_VLA_PTR:
+ node->ty = node->var->ty;
+ return;
+ case ND_COND:
+ if (node->then->ty->kind == TY_VOID || node->els->ty->kind == TY_VOID) {
+ node->ty = ty_void;
+ } else {
+ usual_arith_conv(&node->then, &node->els);
+ node->ty = node->then->ty;
+ }
+ return;
+ case ND_COMMA:
+ node->ty = node->rhs->ty;
+ return;
+ case ND_MEMBER:
+ node->ty = node->member->ty;
+ return;
+ case ND_ADDR: {
+ Type *ty = node->lhs->ty;
+ if (ty->kind == TY_ARRAY)
+ node->ty = pointer_to(ty->base);
+ else
+ node->ty = pointer_to(ty);
+ return;
+ }
+ case ND_DEREF:
+ if (!node->lhs->ty->base)
+ error_tok(node->tok, "invalid pointer dereference");
+ if (node->lhs->ty->base->kind == TY_VOID)
+ error_tok(node->tok, "dereferencing a void pointer");
+
+ node->ty = node->lhs->ty->base;
+ return;
+ case ND_STMT_EXPR:
+ if (node->body) {
+ Node *stmt = node->body;
+ while (stmt->next)
+ stmt = stmt->next;
+ if (stmt->kind == ND_EXPR_STMT) {
+ node->ty = stmt->lhs->ty;
+ return;
+ }
+ }
+ /* MASOT error_tok(node->tok, "statement expression returning void is not supported"); */
+ node->ty = ty_void;
+ return;
+ case ND_LABEL_VAL:
+ node->ty = pointer_to(ty_void);
+ return;
+ case ND_CAS:
+ add_type(node->cas_addr);
+ add_type(node->cas_old);
+ add_type(node->cas_new);
+ node->ty = ty_bool;
+
+ if (node->cas_addr->ty->kind != TY_PTR)
+ error_tok(node->cas_addr->tok, "pointer expected");
+ if (node->cas_old->ty->kind != TY_PTR)
+ error_tok(node->cas_old->tok, "pointer expected");
+ return;
+ case ND_EXCH:
+ if (node->lhs->ty->kind != TY_PTR)
+ error_tok(node->cas_addr->tok, "pointer expected");
+ node->ty = node->lhs->ty->base;
+ return;
+ }
+}
diff --git a/type_helpers.c b/type_helpers.c
new file mode 100644
index 0000000..0f6b022
--- /dev/null
+++ b/type_helpers.c
@@ -0,0 +1,78 @@
+#include <stddef.h>
+#include "chibicc.h"
+
+unsigned long hash_type(Type *type) {
+ unsigned long hash = 6997;
+ if (!type) return hash;
+ if (type->hashing) return hash;
+ type->hashing = 1;
+#define EXTEND_HASH(v) hash = (hash * 33) ^ (size_t)(v)
+ EXTEND_HASH(type->kind);
+ EXTEND_HASH(type->size);
+ EXTEND_HASH(type->align);
+ EXTEND_HASH(type->is_unsigned);
+ EXTEND_HASH(type->is_atomic);
+ // EXTEND_HASH(hash_type(type->origin));
+
+ EXTEND_HASH(hash_type(type->base));
+ EXTEND_HASH(type->array_len);
+ EXTEND_HASH(type->vla_len);
+ EXTEND_HASH(type->vla_size);
+ EXTEND_HASH(type->members);
+
+ // TODO: assume the members pointers are actually same?
+ // for (Member *m = type->members; m; m = m->next) {
+ // EXTEND_HASH(hash_type(m->ty));
+ // EXTEND_HASH(m->name); // token
+ // EXTEND_HASH(m->idx);
+ // EXTEND_HASH(m->align);
+ // EXTEND_HASH(m->offset);
+ // EXTEND_HASH(m->is_bitfield);
+ // EXTEND_HASH(m->bit_offset);
+ // EXTEND_HASH(m->bit_width);
+ // }
+ EXTEND_HASH(type->is_flexible);
+ EXTEND_HASH(type->is_packed);
+
+ EXTEND_HASH(hash_type(type->return_ty));
+ EXTEND_HASH(hash_type(type->params));
+ EXTEND_HASH(type->is_variadic);
+ // EXTEND_HASH(type->next);
+
+ type->hashing = 0;
+ return hash;
+}
+
+int definitely_same_type(Type *type1, Type *type2) {
+ if (!type1 || !type2) return type1 == type2;
+ if (type1->hashing != type2->hashing) return 0;
+ if (type1->hashing) return type1 == type2;
+ type1->hashing = 1;
+ type2->hashing = 1;
+#define FIELD_CHECK(field) if (type1->field != type2->field) goto disequal
+#define RECURSE_CHECK(field) if (!definitely_same_type(type1->field, type2->field)) goto disequal
+ FIELD_CHECK(kind);
+ FIELD_CHECK(size);
+ FIELD_CHECK(align);
+ FIELD_CHECK(is_unsigned);
+ FIELD_CHECK(is_atomic);
+ // RECURSE_CHECK(origin);
+ RECURSE_CHECK(base);
+ FIELD_CHECK(array_len);
+ FIELD_CHECK(vla_len);
+ FIELD_CHECK(vla_size);
+ FIELD_CHECK(members);
+ FIELD_CHECK(is_flexible);
+ FIELD_CHECK(is_packed);
+ RECURSE_CHECK(return_ty);
+ FIELD_CHECK(params);
+ FIELD_CHECK(is_variadic);
+ // RECURSE_CHECK(next);
+ type1->hashing = 0;
+ type2->hashing = 0;
+ return 1;
+disequal:
+ type1->hashing = 0;
+ type2->hashing = 0;
+ return 0;
+}
diff --git a/typegen.c b/typegen.c
new file mode 100644
index 0000000..75b3cff
--- /dev/null
+++ b/typegen.c
@@ -0,0 +1,304 @@
+#include "chibicc.h"
+
+static Type **HASH_MAP = 0;
+static int CAP_HASH_MAP = 0;
+static int N_HASH_MAP = 0;
+Type *hash_lookup(Type *type) {
+ if (!CAP_HASH_MAP) return 0;
+
+ size_t idx = hash_type(type) % CAP_HASH_MAP;
+ while (HASH_MAP[idx]) {
+ if (definitely_same_type(HASH_MAP[idx], type)) {
+ return HASH_MAP[idx];
+ }
+ if (++idx == CAP_HASH_MAP) idx = 0;
+ }
+ return 0;
+}
+
+void hash_insert(Type *type) {
+ if ((N_HASH_MAP + 1) >= (CAP_HASH_MAP / 2)) {
+ // RESIZE AND REHASH
+ int old_cap = CAP_HASH_MAP;
+ Type **old_hash_map = HASH_MAP;
+
+ CAP_HASH_MAP = (CAP_HASH_MAP + 1) * 4;
+ N_HASH_MAP = 0;
+ HASH_MAP = calloc(CAP_HASH_MAP, sizeof(HASH_MAP[0]));
+ for (int i = 0; i < old_cap; i++) {
+ if (old_hash_map[i])
+ hash_insert(old_hash_map[i]);
+ }
+ if (old_hash_map) free(old_hash_map);
+ }
+
+ N_HASH_MAP++;
+ size_t idx = hash_type(type) % CAP_HASH_MAP;
+ while (HASH_MAP[idx]) {
+ if (++idx == CAP_HASH_MAP) idx = 0;
+ }
+ HASH_MAP[idx] = type;
+}
+
+static int count(void) {
+ static int i = 1;
+ return i++;
+}
+
+static void printnoln(char *fmt, ...);
+static void println(char *fmt, ...);
+
+static void print_tok(Token *tok) {
+ if (tok->str) printnoln("%s", tok->str);
+ else {
+ assert(tok->loc);
+ for (int i = 0; i < tok->len; i++)
+ printnoln("%c", tok->loc[i]);
+ }
+}
+
+const void addrdecl(Type *type);
+const void typedecl(Type *type) {
+ if (type->id) return;
+ if (type->kind == TY_ARRAY)
+ addrdecl(type->base);
+ Type *hashed = hash_lookup(type);
+ if (hashed && !(hashed->typedecling)) {
+ assert(hashed->id);
+ type->id = hashed->id;
+ if (hashed->pointer_type)
+ type->pointer_type = hashed->pointer_type;
+ if (hashed->return_ty)
+ type->return_ty = hashed->return_ty;
+ if (hashed->params)
+ type->params = hashed->params;
+ return;
+ }
+ type->typedecling = 1;
+ type->id = count();
+ hash_insert(type);
+ switch (type->kind) {
+ case TY_FLOAT:
+ println("typedef float Type_%d ;", type->id);
+ break;
+ case TY_DOUBLE:
+ println("typedef double Type_%d ;", type->id);
+ break;
+ case TY_LDOUBLE:
+ println("typedef long double Type_%d ;", type->id);
+ break;
+ case TY_INT:
+ if (type->is_unsigned)
+ println("typedef unsigned int Type_%d ;", type->id);
+ else
+ println("typedef int Type_%d ;", type->id);
+ break;
+ case TY_LONG:
+ if (type->is_unsigned)
+ println("typedef unsigned long Type_%d ;", type->id);
+ else
+ println("typedef long Type_%d ;", type->id);
+ break;
+ case TY_SHORT:
+ if (type->is_unsigned)
+ println("typedef unsigned short Type_%d ;", type->id);
+ else
+ println("typedef short Type_%d ;", type->id);
+ break;
+ case TY_VOID:
+ println("typedef void Type_%d ;", type->id);
+ break;
+ case TY_BOOL:
+ println("typedef _Bool Type_%d ;", type->id);
+ break;
+ case TY_CHAR:
+ println("typedef char Type_%d ;", type->id);
+ break;
+ case TY_PTR:
+ typedecl(type->base);
+ println("typedef Type_%d * Type_%d ;", type->base->id, type->id);
+ break;
+ case TY_FUNC: {
+ typedecl(type->return_ty);
+ for (Type *p = type->params; p; p = p->next)
+ typedecl(p);
+ printnoln("typedef Type_%d Type_%d ( ", type->return_ty->id, type->id);
+
+ int i = 0;
+ for (Type *p = type->params; p; p = p->next, i++)
+ if (i) printnoln(", Type_%d ", p->id);
+ else printnoln("Type_%d ", p->id);
+
+ if (type->is_variadic) {
+ if (i) printnoln(", ... ");
+ // else printnoln("... ");
+ }
+ println(") ;");
+ addrdecl(type);
+ break;
+ }
+ case TY_ARRAY:
+ typedecl(type->base);
+ if (type->array_len == -1)
+ println("typedef Type_%d Type_%d [ ] ;", type->base->id, type->id);
+ else
+ println("typedef Type_%d Type_%d [ %d ] ;", type->base->id, type->id,
+ type->array_len);
+ break;
+ case TY_STRUCT:
+ case TY_UNION:
+ if (type->kind == TY_STRUCT)
+ println("typedef struct Struct_%d Type_%d ;", type->id, type->id);
+ else
+ println("typedef union Union_%d Type_%d ;", type->id, type->id);
+
+ for (Member *m = type->members; m; m = m->next)
+ typedecl(m->ty);
+
+ if (type->kind == TY_STRUCT) printnoln("struct Struct_%d { ", type->id);
+ else printnoln("union Union_%d { ", type->id);
+ for (Member *m = type->members; m; m = m->next) {
+ printnoln("Type_%d ", m->ty->id);
+ if (m->name)
+ print_tok(m->name);
+ else if (m->ty->kind == TY_STRUCT || m->ty->kind == TY_UNION)
+ printnoln("___dietc_f%d", m->idx);
+ else
+ printnoln("__field_%d", count());
+ printnoln(" ; ");
+ }
+ println("} ;");
+ break;
+ case TY_ENUM:
+ println("typedef enum Enum_%d { EN_%d } Type_%d ;", type->id, type->id, type->id);
+ break;
+ case TY_VLA:
+ assert(!"unimplemented vla?");
+ break;
+ default:
+ assert(!"unimplemented?");
+ break;
+ }
+ type->typedecling = 0;
+}
+
+static FILE *output_file;
+static Obj *current_fn;
+
+__attribute__((format(printf, 1, 2)))
+static void println(char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(output_file, fmt, ap);
+ va_end(ap);
+ fprintf(output_file, "\n");
+}
+
+__attribute__((format(printf, 1, 2)))
+static void printnoln(char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(output_file, fmt, ap);
+ va_end(ap);
+}
+
+const void addrdecl(Type *type) {
+ if (type->pointer_type) return;
+ type->pointer_type = pointer_to(type);
+ typedecl(type->pointer_type);
+}
+
+static void gen_addr_decls(Node *node) {
+ switch (node->kind) {
+ case ND_VAR:
+ addrdecl(node->ty);
+ return;
+ case ND_COMMA:
+ gen_addr_decls(node->rhs);
+ return;
+ case ND_MEMBER:
+ gen_addr_decls(node->lhs);
+ addrdecl(node->ty);
+ return;
+ }
+}
+
+// Generate code for a given node.
+static void gen_typedecls(Node *node) {
+ if (!node) return;
+ if (node->ty) typedecl(node->ty);
+ gen_typedecls(node->lhs);
+ gen_typedecls(node->rhs);
+ gen_typedecls(node->cond);
+ gen_typedecls(node->then);
+ gen_typedecls(node->els);
+ gen_typedecls(node->init);
+ gen_typedecls(node->inc);
+ for (Node *n = node->body; n; n = n->next)
+ gen_typedecls(n);
+ for (Node *arg = node->args; arg; arg = arg->next)
+ gen_typedecls(arg);
+ if (node->kind == ND_SWITCH)
+ for (Node *n = node->case_next; n; n = n->case_next)
+ gen_typedecls(n);
+
+ switch (node->kind) {
+ case ND_VAR:
+ if (node->ty->kind == TY_ARRAY)
+ addrdecl(node->ty->base);
+ break;
+ case ND_CAST:
+ if (node->lhs->ty->kind == TY_UNION)
+ gen_addr_decls(node->lhs);
+ addrdecl(node->ty);
+ break;
+ case ND_MEMBER:
+ gen_addr_decls(node);
+ if (node->ty->kind == TY_ARRAY)
+ addrdecl(node->ty->base);
+ break;
+ case ND_ADDR:
+ case ND_ASSIGN:
+ gen_addr_decls(node->lhs);
+ break;
+ }
+}
+
+static void typedecl_data(Obj *prog) {
+ for (Obj *var = prog; var; var = var->next) {
+ // if (var->is_function || !var->is_definition)
+ // continue;
+ typedecl(var->ty);
+ }
+}
+
+static void typedecl_text(Obj *prog) {
+ for (Obj *fn = prog; fn; fn = fn->next) {
+ if (!fn->is_function || !fn->is_definition)
+ continue;
+
+ // No code is emitted for "static inline" functions
+ // if no one is referencing them.
+ if (!fn->is_live)
+ continue;
+
+ current_fn = fn;
+
+ typedecl(fn->ty);
+ gen_typedecls(fn->body);
+
+ // ensure function params & locals are typegened
+ for (Obj *var = fn->locals; var; var = var->next) {
+ typedecl(var->ty);
+ }
+ }
+}
+
+void typegen(Obj *prog, FILE *out) {
+ output_file = out;
+
+ printnoln("#include \"%s/scripts/dietc_helpers.h\"\n", DIETC_ROOT);
+ typedecl(ty_int);
+ typedecl_data(prog);
+ typedecl_text(prog);
+}
diff --git a/unicode.c b/unicode.c
new file mode 100644
index 0000000..2eeeeed
--- /dev/null
+++ b/unicode.c
@@ -0,0 +1,189 @@
+#include "chibicc.h"
+
+// Encode a given character in UTF-8.
+int encode_utf8(char *buf, uint32_t c) {
+ if (c <= 0x7F) {
+ buf[0] = c;
+ return 1;
+ }
+
+ if (c <= 0x7FF) {
+ buf[0] = 0b11000000 | (c >> 6);
+ buf[1] = 0b10000000 | (c & 0b00111111);
+ return 2;
+ }
+
+ if (c <= 0xFFFF) {
+ buf[0] = 0b11100000 | (c >> 12);
+ buf[1] = 0b10000000 | ((c >> 6) & 0b00111111);
+ buf[2] = 0b10000000 | (c & 0b00111111);
+ return 3;
+ }
+
+ buf[0] = 0b11110000 | (c >> 18);
+ buf[1] = 0b10000000 | ((c >> 12) & 0b00111111);
+ buf[2] = 0b10000000 | ((c >> 6) & 0b00111111);
+ buf[3] = 0b10000000 | (c & 0b00111111);
+ return 4;
+}
+
+// Read a UTF-8-encoded Unicode code point from a source file.
+// We assume that source files are always in UTF-8.
+//
+// UTF-8 is a variable-width encoding in which one code point is
+// encoded in one to four bytes. One byte UTF-8 code points are
+// identical to ASCII. Non-ASCII characters are encoded using more
+// than one byte.
+uint32_t decode_utf8(char **new_pos, char *p) {
+ if ((unsigned char)*p < 128) {
+ *new_pos = p + 1;
+ return *p;
+ }
+
+ char *start = p;
+ int len;
+ uint32_t c;
+
+ if ((unsigned char)*p >= 0b11110000) {
+ len = 4;
+ c = *p & 0b111;
+ } else if ((unsigned char)*p >= 0b11100000) {
+ len = 3;
+ c = *p & 0b1111;
+ } else if ((unsigned char)*p >= 0b11000000) {
+ len = 2;
+ c = *p & 0b11111;
+ } else {
+ error_at(start, "invalid UTF-8 sequence");
+ }
+
+ for (int i = 1; i < len; i++) {
+ if ((unsigned char)p[i] >> 6 != 0b10)
+ error_at(start, "invalid UTF-8 sequence");
+ c = (c << 6) | (p[i] & 0b111111);
+ }
+
+ *new_pos = p + len;
+ return c;
+}
+
+static bool in_range(uint32_t *range, uint32_t c) {
+ for (int i = 0; range[i] != -1; i += 2)
+ if (range[i] <= c && c <= range[i + 1])
+ return true;
+ return false;
+}
+
+// [https://www.sigbus.info/n1570#D] C11 allows not only ASCII but
+// some multibyte characters in certan Unicode ranges to be used in an
+// identifier.
+//
+// This function returns true if a given character is acceptable as
+// the first character of an identifier.
+//
+// For example, ¾ (U+00BE) is a valid identifier because characters in
+// 0x00BE-0x00C0 are allowed, while neither ⟘ (U+27D8) nor ' '
+// (U+3000, full-width space) are allowed because they are out of range.
+bool is_ident1(uint32_t c) {
+ static uint32_t range[] = {
+ '_', '_', 'a', 'z', 'A', 'Z', '$', '$',
+ 0x00A8, 0x00A8, 0x00AA, 0x00AA, 0x00AD, 0x00AD, 0x00AF, 0x00AF,
+ 0x00B2, 0x00B5, 0x00B7, 0x00BA, 0x00BC, 0x00BE, 0x00C0, 0x00D6,
+ 0x00D8, 0x00F6, 0x00F8, 0x00FF, 0x0100, 0x02FF, 0x0370, 0x167F,
+ 0x1681, 0x180D, 0x180F, 0x1DBF, 0x1E00, 0x1FFF, 0x200B, 0x200D,
+ 0x202A, 0x202E, 0x203F, 0x2040, 0x2054, 0x2054, 0x2060, 0x206F,
+ 0x2070, 0x20CF, 0x2100, 0x218F, 0x2460, 0x24FF, 0x2776, 0x2793,
+ 0x2C00, 0x2DFF, 0x2E80, 0x2FFF, 0x3004, 0x3007, 0x3021, 0x302F,
+ 0x3031, 0x303F, 0x3040, 0xD7FF, 0xF900, 0xFD3D, 0xFD40, 0xFDCF,
+ 0xFDF0, 0xFE1F, 0xFE30, 0xFE44, 0xFE47, 0xFFFD,
+ 0x10000, 0x1FFFD, 0x20000, 0x2FFFD, 0x30000, 0x3FFFD, 0x40000, 0x4FFFD,
+ 0x50000, 0x5FFFD, 0x60000, 0x6FFFD, 0x70000, 0x7FFFD, 0x80000, 0x8FFFD,
+ 0x90000, 0x9FFFD, 0xA0000, 0xAFFFD, 0xB0000, 0xBFFFD, 0xC0000, 0xCFFFD,
+ 0xD0000, 0xDFFFD, 0xE0000, 0xEFFFD, -1,
+ };
+
+ return in_range(range, c);
+}
+
+// Returns true if a given character is acceptable as a non-first
+// character of an identifier.
+bool is_ident2(uint32_t c) {
+ static uint32_t range[] = {
+ '0', '9', '$', '$', 0x0300, 0x036F, 0x1DC0, 0x1DFF, 0x20D0, 0x20FF,
+ 0xFE20, 0xFE2F, -1,
+ };
+
+ return is_ident1(c) || in_range(range, c);
+}
+
+// Returns the number of columns needed to display a given
+// character in a fixed-width font.
+//
+// Based on https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
+static int char_width(uint32_t c) {
+ static uint32_t range1[] = {
+ 0x0000, 0x001F, 0x007f, 0x00a0, 0x0300, 0x036F, 0x0483, 0x0486,
+ 0x0488, 0x0489, 0x0591, 0x05BD, 0x05BF, 0x05BF, 0x05C1, 0x05C2,
+ 0x05C4, 0x05C5, 0x05C7, 0x05C7, 0x0600, 0x0603, 0x0610, 0x0615,
+ 0x064B, 0x065E, 0x0670, 0x0670, 0x06D6, 0x06E4, 0x06E7, 0x06E8,
+ 0x06EA, 0x06ED, 0x070F, 0x070F, 0x0711, 0x0711, 0x0730, 0x074A,
+ 0x07A6, 0x07B0, 0x07EB, 0x07F3, 0x0901, 0x0902, 0x093C, 0x093C,
+ 0x0941, 0x0948, 0x094D, 0x094D, 0x0951, 0x0954, 0x0962, 0x0963,
+ 0x0981, 0x0981, 0x09BC, 0x09BC, 0x09C1, 0x09C4, 0x09CD, 0x09CD,
+ 0x09E2, 0x09E3, 0x0A01, 0x0A02, 0x0A3C, 0x0A3C, 0x0A41, 0x0A42,
+ 0x0A47, 0x0A48, 0x0A4B, 0x0A4D, 0x0A70, 0x0A71, 0x0A81, 0x0A82,
+ 0x0ABC, 0x0ABC, 0x0AC1, 0x0AC5, 0x0AC7, 0x0AC8, 0x0ACD, 0x0ACD,
+ 0x0AE2, 0x0AE3, 0x0B01, 0x0B01, 0x0B3C, 0x0B3C, 0x0B3F, 0x0B3F,
+ 0x0B41, 0x0B43, 0x0B4D, 0x0B4D, 0x0B56, 0x0B56, 0x0B82, 0x0B82,
+ 0x0BC0, 0x0BC0, 0x0BCD, 0x0BCD, 0x0C3E, 0x0C40, 0x0C46, 0x0C48,
+ 0x0C4A, 0x0C4D, 0x0C55, 0x0C56, 0x0CBC, 0x0CBC, 0x0CBF, 0x0CBF,
+ 0x0CC6, 0x0CC6, 0x0CCC, 0x0CCD, 0x0CE2, 0x0CE3, 0x0D41, 0x0D43,
+ 0x0D4D, 0x0D4D, 0x0DCA, 0x0DCA, 0x0DD2, 0x0DD4, 0x0DD6, 0x0DD6,
+ 0x0E31, 0x0E31, 0x0E34, 0x0E3A, 0x0E47, 0x0E4E, 0x0EB1, 0x0EB1,
+ 0x0EB4, 0x0EB9, 0x0EBB, 0x0EBC, 0x0EC8, 0x0ECD, 0x0F18, 0x0F19,
+ 0x0F35, 0x0F35, 0x0F37, 0x0F37, 0x0F39, 0x0F39, 0x0F71, 0x0F7E,
+ 0x0F80, 0x0F84, 0x0F86, 0x0F87, 0x0F90, 0x0F97, 0x0F99, 0x0FBC,
+ 0x0FC6, 0x0FC6, 0x102D, 0x1030, 0x1032, 0x1032, 0x1036, 0x1037,
+ 0x1039, 0x1039, 0x1058, 0x1059, 0x1160, 0x11FF, 0x135F, 0x135F,
+ 0x1712, 0x1714, 0x1732, 0x1734, 0x1752, 0x1753, 0x1772, 0x1773,
+ 0x17B4, 0x17B5, 0x17B7, 0x17BD, 0x17C6, 0x17C6, 0x17C9, 0x17D3,
+ 0x17DD, 0x17DD, 0x180B, 0x180D, 0x18A9, 0x18A9, 0x1920, 0x1922,
+ 0x1927, 0x1928, 0x1932, 0x1932, 0x1939, 0x193B, 0x1A17, 0x1A18,
+ 0x1B00, 0x1B03, 0x1B34, 0x1B34, 0x1B36, 0x1B3A, 0x1B3C, 0x1B3C,
+ 0x1B42, 0x1B42, 0x1B6B, 0x1B73, 0x1DC0, 0x1DCA, 0x1DFE, 0x1DFF,
+ 0x200B, 0x200F, 0x202A, 0x202E, 0x2060, 0x2063, 0x206A, 0x206F,
+ 0x20D0, 0x20EF, 0x302A, 0x302F, 0x3099, 0x309A, 0xA806, 0xA806,
+ 0xA80B, 0xA80B, 0xA825, 0xA826, 0xFB1E, 0xFB1E, 0xFE00, 0xFE0F,
+ 0xFE20, 0xFE23, 0xFEFF, 0xFEFF, 0xFFF9, 0xFFFB, 0x10A01, 0x10A03,
+ 0x10A05, 0x10A06, 0x10A0C, 0x10A0F, 0x10A38, 0x10A3A, 0x10A3F, 0x10A3F,
+ 0x1D167, 0x1D169, 0x1D173, 0x1D182, 0x1D185, 0x1D18B, 0x1D1AA, 0x1D1AD,
+ 0x1D242, 0x1D244, 0xE0001, 0xE0001, 0xE0020, 0xE007F, 0xE0100, 0xE01EF,
+ -1,
+ };
+
+ if (in_range(range1, c))
+ return 0;
+
+ static uint32_t range2[] = {
+ 0x1100, 0x115F, 0x2329, 0x2329, 0x232A, 0x232A, 0x2E80, 0x303E,
+ 0x3040, 0xA4CF, 0xAC00, 0xD7A3, 0xF900, 0xFAFF, 0xFE10, 0xFE19,
+ 0xFE30, 0xFE6F, 0xFF00, 0xFF60, 0xFFE0, 0xFFE6, 0x1F000, 0x1F644,
+ 0x20000, 0x2FFFD, 0x30000, 0x3FFFD, -1,
+ };
+
+ if (in_range(range2, c))
+ return 2;
+ return 1;
+}
+
+// Returns the number of columns needed to display a given
+// string in a fixed-width font.
+int display_width(char *p, int len) {
+ char *start = p;
+ int w = 0;
+ while (p - start < len) {
+ uint32_t c = decode_utf8(&p, p);
+ w += char_width(c);
+ }
+ return w;
+}
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback