commit 9c5e2860156e8ccb4d34a710183dd411f1c2d5c1
Author: William Djupström <william@deepztream.com>
Date: Fri, 4 Sep 2026 22:49:55 +0200
Initialize repo
Diffstat:
| A | Makefile | | | 20 | ++++++++++++++++++++ |
| A | References | | | 1 | + |
| A | dlib.h | | | 417 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | lexer.c | | | 404 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | lexer.h | | | 81 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | main.c | | | 70 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | parser.c | | | 579 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | parser.h | | | 118 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | test_input/binop.dc | | | 8 | ++++++++ |
| A | test_input/block.dc | | | 9 | +++++++++ |
| A | test_input/func.dc | | | 4 | ++++ |
| A | test_input/input.dc | | | 19 | +++++++++++++++++++ |
| A | test_input/pepeg.dc | | | 1002 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | test_input/var.dc | | | 3 | +++ |
14 files changed, 2735 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -0,0 +1,20 @@
+.PHONY: run clean
+
+CFLAGS = -Wall -Wextra -Wswitch-enum -ggdb
+
+SOURCE_FILES = main.c
+SOURCE_FILES += lexer.c
+SOURCE_FILES += parser.c
+
+HEADER_FILES = dlib.h
+HEADER_FILES += lexer.h
+HEADER_FILES += parser.h
+
+dc: $(SOURCE_FILES) $(HEADER_FILES)
+ gcc $(CFLAGS) -o $@ $(SOURCE_FILES)
+
+run: dc
+ ./dc input.dc
+
+clean:
+ rm *.o dc
diff --git a/References b/References
@@ -0,0 +1 @@
+https://pp.ipd.kit.edu/uploads/publikationen/braun13cc.pdf
diff --git a/dlib.h b/dlib.h
@@ -0,0 +1,417 @@
+#ifndef DLIB_H
+#define DLIB_H 1
+
+#include <assert.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#define unimplemented() \
+ do { \
+ fprintf(stderr, "%s:%i:1: Error: not implemented %s\n", __FILE__, __LINE__, __func__); \
+ abort(); \
+ } while (0)
+
+#define pop_arg(argc, argv) ((argc)--, *(argv)++)
+
+#define da_append(da, new)\
+ do {\
+ if ((da)->capacity <= (da)->count) {\
+ (da)->capacity = (da)->capacity ? (da)->capacity * 2 : 16;\
+ (da)->items = realloc((da)->items, sizeof(*(da)->items) * (da)->capacity);\
+ }\
+ (da)->items[(da)->count++] = (new);\
+ } while(0)
+
+#define da_prepend(da, new)\
+ do {\
+ if ((da)->capacity <= (da)->count) {\
+ (da)->capacity = (da)->capacity ? (da)->capacity * 2 : 16;\
+ (da)->items = realloc((da)->items, sizeof(*(da)->items) * (da)->capacity);\
+ }\
+ memmove((da)->items + 1, (da)->items, sizeof(*(da)->items) * ((da)->count++));\
+ (da)->items[0] = (new);\
+ } while(0)
+
+#define da_reserve(da, len)\
+ do {\
+ if ((da)->capacity >= (len)) break;\
+ (da)->capacity = (len);\
+ (da)->items = realloc((da)->items, sizeof(*(da)->items) * (len));\
+ } while(0)
+
+#define da_expand(da, size)\
+ do {\
+ if ((da)->count >= (size)) break;\
+ da_reserve(da, size);\
+ (da)->count = (size);\
+ } while(0)
+
+#define da_delete(da, index)\
+ do {\
+ assert(index >= 0 && index < (da)->count);\
+ (da)->items[index] = (da)->items[--(da)->count];\
+ } while (0)
+
+typedef struct {
+ union {
+ char *items;
+ const char *data;
+ };
+ union {
+ ssize_t count;
+ ssize_t length;
+ };
+ ssize_t capacity;
+} Sb;
+#define String_builder Sb
+
+typedef struct {
+ union {
+ const char *items;
+ const char *data;
+ };
+ union {
+ ssize_t count;
+ ssize_t length;
+ };
+} Sv;
+#define String_view Sv
+
+typedef struct {
+ void *base;
+ void *head;
+ void *end;
+} Temp_allocator;
+
+#define TA_DEFAULT_SIZE (1024*1024)
+
+void ta_init(Temp_allocator *ta, size_t size);
+void *ta_alloc(Temp_allocator *ta, size_t size);
+
+#define sb_append_char da_append
+void sb_printf(Sb *sb, const char *fmt, ...);
+void sb_append_cstr(Sb *sb, const char *cstr);
+void sb_append_sv(Sb *sb, Sv sv);
+
+bool sb_read(Sb *sb, int fd);
+bool sb_read_file(Sb *sb, const char *file_name);
+void sb_write(int fd, Sb sb);
+
+void sb_release(Sb *sb);
+
+#define sv_literal(str) ((Sv){.items = (str), .count = (sizeof (str) - 1)})
+
+Sv sv_from_sb(Sb sb);
+Sv sv_from_cstr(const char *cstr);
+ssize_t sv_find_char(Sv sv, char c);
+
+Sv sv_chop(Sv sv, ssize_t count, Sv *rest);
+Sv sv_chop_delim(Sv sv, char c, Sv *rest);
+
+Sv sv_getline(Sv *sv);
+
+Sv sv_trim_left(Sv sv);
+Sv sv_trim_right(Sv sv);
+Sv sv_trim(Sv sv);
+
+Sv sv_substring(Sv sv, ssize_t start, ssize_t req_count);
+
+int sv_compare(Sv sv1, Sv sv2);
+bool sv_equal(Sv sv1, Sv sv2);
+bool sv_starts_with_cstr(Sv sv, const char *cstr);
+
+void sv_write(int fd, Sv sv);
+
+#ifdef DLIB_IMPLEMENTATION
+
+void
+ta_init(Temp_allocator *ta, size_t size)
+{
+ ta->base = malloc(size);
+ ta->head = ta->base;
+ ta->end = ta->base + size;
+}
+
+void *
+ta_alloc(Temp_allocator *ta, size_t size)
+{
+ if (!ta->base) {
+ ta->base = malloc(TA_DEFAULT_SIZE);
+ ta->head = ta->base;
+ ta->end = ta->base + TA_DEFAULT_SIZE;
+ }
+ void *result = ta->head;
+ ta->head += (size + 0xF) & ~0xFul;
+ if (ta->head >= ta->end) {
+ fprintf(stderr, "Error: Temp allocator out of memory\n");
+ exit(1);
+ }
+ return result;
+}
+
+void
+sb_printf(Sb *sb, const char *fmt, ...) {
+ va_list args, args_copy;
+ va_start(args, fmt);
+ va_copy(args_copy, args);
+ int len = vsnprintf(NULL, 0, fmt, args_copy) + 1;
+ ssize_t new_size = sb->count + len;
+ da_reserve(sb, new_size);
+ va_end(args_copy);
+ vsnprintf(sb->items + sb->count, len, fmt, args);
+ va_end(args);
+ sb->count = new_size - 1;
+}
+
+void
+sb_append_cstr(Sb *sb, const char *cstr)
+{
+ ssize_t len = strlen(cstr);
+ da_reserve(sb, sb->count + len);
+ strcpy(sb->items + sb->count, cstr);
+ sb->count += len;
+}
+
+void
+sb_append_sv(Sb *sb, Sv sv)
+{
+ da_reserve(sb, sb->count + sv.count);
+ memcpy(sb->items + sb->count, sv.items, sv.count);
+ sb->count += sv.count;
+}
+
+bool
+sb_read(Sb *sb, int fd)
+{
+ ssize_t read_count;
+ ssize_t buf_size = 4096;
+ char *buf = malloc(buf_size);
+ do {
+ read_count = read(fd, buf, buf_size);
+ if (read_count < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ perror("sb_read()");
+ return false;
+ }
+ if (read_count == 0) {
+ break;
+ }
+ sb_append_sv(sb, ((Sv) {.items = buf, .length = read_count}));
+ if (read_count < buf_size) {
+ break;
+ }
+ } while (1);
+ free(buf);
+ return true;
+}
+
+bool
+sb_read_file(Sb *sb, const char *file_name)
+{
+ int fd = open(file_name, O_RDONLY);
+ if (fd < 0) {
+ perror("sb_read()");
+ return false;
+ }
+ ssize_t read_count;
+ ssize_t buf_size = 4096;
+ char *buf = malloc(buf_size);
+ do {
+ read_count = read(fd, buf, buf_size);
+ if (read_count < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ perror("sb_read_file()");
+ return false;
+ }
+ if (read_count == 0) {
+ break;
+ }
+ sb_append_sv(sb, ((Sv) {.items = buf, .length = read_count}));
+ } while (1);
+ free(buf);
+ return true;
+}
+
+void
+sb_write(int fd, Sb sb)
+{
+ write(fd, sb.items, sb.length);
+}
+
+void
+sb_release(Sb *sb)
+{
+ if (sb->capacity) {
+ free(sb->items);
+ sb->items = NULL;
+ sb->count = 0;
+ sb->capacity = 0;
+ }
+}
+
+/* String view functions */
+
+void
+sv_write(int fd, Sv sv)
+{
+ write(fd, sv.items, sv.count);
+}
+
+Sv
+sv_from_sb(Sb sb)
+{
+ return (Sv) {.items = sb.items, .count = sb.count};
+}
+
+Sv
+sv_from_cstr(const char *cstr)
+{
+ return (Sv) {.items = cstr, .count = strlen(cstr)};
+}
+
+ssize_t
+sv_find_char(Sv sv, char c)
+{
+ ssize_t pos = 0;
+ while (sv.count > pos && sv.items[pos] != c)
+ pos++;
+ for (pos = 0; pos < sv.count && sv.items[pos] != c; pos++);
+ return pos < sv.count ? pos : -1;
+}
+
+Sv
+sv_chop_delim(Sv sv, char c, Sv *rest)
+{
+ ssize_t pos = sv_find_char(sv, c);
+ Sv res = sv;
+ if (pos > 0) {
+ res.count = pos;
+
+ rest->count -= pos + 1;
+ rest->items += pos + 1;
+ }
+ return res;
+}
+
+Sv
+sv_chop(Sv sv, ssize_t count, Sv *rest)
+{
+ Sv left = {0};
+ ssize_t lcount, rcount;
+ if (count > sv.count) {
+ lcount = sv.count;
+ rcount = 0;
+ } else if (-count > sv.count) {
+ lcount = 0;
+ rcount = sv.count;
+ } else if (count < 0) {
+ lcount = sv.count + count;
+ rcount = -count;
+ } else {
+ lcount = count;
+ rcount = sv.count - count;
+ }
+ left.items = sv.items;
+ left.count = lcount;
+ if (rest) {
+ rest->items = sv.items + lcount;
+ rest->count = rcount;
+ }
+ return left;
+}
+
+Sv
+sv_trim_left(Sv sv)
+{
+ while (sv.count && isspace(sv.items[0])) {
+ sv.count--;
+ sv.items++;
+ }
+ return sv;
+}
+
+Sv
+sv_trim_right(Sv sv)
+{
+ while (sv.count && isspace(sv.items[sv.count - 1])) {
+ sv.count--;
+ }
+ return sv;
+}
+
+Sv
+sv_trim(Sv sv)
+{
+ return sv_trim_right(sv_trim_left(sv));
+}
+
+Sv
+sv_substring(Sv sv, ssize_t start, ssize_t req_count)
+{
+ Sv ss = {0};
+ if (start >= sv.count) return ss;
+ ss.count = (req_count < sv.count - start) ? req_count : sv.count - start;
+ ss.items = sv.items + start;
+ return ss;
+}
+
+int
+sv_compare(Sv sv1, Sv sv2)
+{
+ if (sv1.items == sv2.items && sv1.count == sv2.count) return 0;
+ signed char diff;
+ ssize_t pos;
+ for (pos = 0; pos < sv1.count; pos++) {
+ if (pos == sv2.count) return 1;
+ if ((diff = sv1.items[pos] - sv2.items[pos])) return diff;
+ }
+ return pos < sv2.count ? -1 : 0;
+}
+
+bool
+sv_equal(Sv sv1, Sv sv2)
+{
+ if (sv1.count != sv2.count) return false;
+ if (sv1.items == sv2.items) return true;
+ ssize_t pos;
+ for (pos = 0; pos < sv1.count && sv1.items[pos] == sv2.items[pos]; pos++);
+ return pos == sv1.count;
+}
+
+bool
+sv_starts_with_cstr(Sv sv, const char *cstr)
+{
+ size_t cstr_len = strlen(cstr);
+ assert(cstr_len <= SSIZE_MAX);
+ if (sv.count < (ssize_t) cstr_len) return false;
+ ssize_t pos;
+ for (pos = 0; pos < (ssize_t) cstr_len && sv.items[pos] == cstr[pos]; pos++);
+ return pos == (ssize_t) cstr_len;
+}
+
+Sv
+sv_getline(Sv *sv)
+{
+ Sv line = sv_chop_delim(*sv, '\n', sv);
+ if (line.count && line.items[line.count - 1] == '\r') {
+ line.count--;
+ }
+ return line;
+}
+
+#endif // DLIB_IMPLEMENTATION
+
+#endif // DLIB_H
diff --git a/lexer.c b/lexer.c
@@ -0,0 +1,404 @@
+#include <fcntl.h>
+
+#include "dlib.h"
+#include "lexer.h"
+
+Sv keywords[] = {
+ [T_RETURN - T_KEYWORDS_] = sv_literal("return"),
+ [T_IF - T_KEYWORDS_] = sv_literal("if"),
+ [T_ELSE - T_KEYWORDS_] = sv_literal("else"),
+ [T_WHILE - T_KEYWORDS_] = sv_literal("while"),
+ [T_FOR - T_KEYWORDS_] = sv_literal("for"),
+ [T_SWITCH - T_KEYWORDS_] = sv_literal("switch"),
+ [T_CASE - T_KEYWORDS_] = sv_literal("case"),
+ [T_DEFAULT - T_KEYWORDS_] = sv_literal("default"),
+ [T_BREAK - T_KEYWORDS_] = sv_literal("break"),
+ [T_CONTINUE - T_KEYWORDS_] = sv_literal("continue"),
+ [T_TYPEDEF - T_KEYWORDS_] = sv_literal("typedef"),
+ [T_FUNC - T_KEYWORDS_] = sv_literal("func"),
+ [T_VAR - T_KEYWORDS_] = sv_literal("var"),
+ [T_CONST - T_KEYWORDS_] = sv_literal("const"),
+ [T_STRUCT - T_KEYWORDS_] = sv_literal("struct"),
+ [T_UNION - T_KEYWORDS_] = sv_literal("union"),
+ [T_ENUM - T_KEYWORDS_] = sv_literal("enum"),
+ [T_OPERATOR - T_KEYWORDS_] = sv_literal("operator"),
+};
+
+static_assert(T_COUNT_ - T_KEYWORDS_ == 18, "keywords[] wrong length");
+int keywords_count = (sizeof(keywords) / sizeof(keywords[0]));
+
+void
+sb_append_loc(Sb *sb, Loc loc)
+{
+ sb_printf(sb, "%.*s:%li:%li: ", (int) loc.filename.count, loc.filename.items, loc.line, loc.column);
+}
+
+void
+sb_append_token_kind(Sb *sb, Token_kind kind)
+{
+ if (kind >= 32 && kind <= 127) sb_append_char(sb, kind);
+ else if (kind == T_ERROR_) sb_append_cstr(sb, "T_ERROR_");
+ else if (kind == T_EOF) sb_append_cstr(sb, "T_EOF");
+ else if (kind == T_INVALID) sb_append_cstr(sb, "T_INVALID");
+ else if (kind == T_WHITESPACE) sb_append_cstr(sb, "T_WHITESPACE");
+ else if (kind == T_IDENT) sb_append_cstr(sb, "T_IDENT");
+ else if (kind == T_NUMBER) sb_append_cstr(sb, "T_NUMBER");
+ else if (kind == T_SQ_STRING) sb_append_cstr(sb, "T_SQ_STRING");
+ else if (kind == T_DQ_STRING) sb_append_cstr(sb, "T_DQ_STRING");
+ else if (kind == T_RETURN) sb_append_cstr(sb, "T_RETURN");
+ else if (kind == T_IF) sb_append_cstr(sb, "T_IF");
+ else if (kind == T_ELSE) sb_append_cstr(sb, "T_ELSE");
+ else if (kind == T_WHILE) sb_append_cstr(sb, "T_WHILE");
+ else if (kind == T_FOR) sb_append_cstr(sb, "T_FOR");
+ else if (kind == T_SWITCH) sb_append_cstr(sb, "T_SWITCH");
+ else if (kind == T_CASE) sb_append_cstr(sb, "T_CASE");
+ else if (kind == T_DEFAULT) sb_append_cstr(sb, "T_DEFAULT");
+ else if (kind == T_BREAK) sb_append_cstr(sb, "T_BREAK");
+ else if (kind == T_CONTINUE) sb_append_cstr(sb, "T_CONTINUE");
+ else if (kind == T_TYPEDEF) sb_append_cstr(sb, "T_TYPEDEF");
+ else if (kind == T_FUNC) sb_append_cstr(sb, "T_FUNC");
+ else if (kind == T_VAR) sb_append_cstr(sb, "T_VAR");
+ else if (kind == T_CONST) sb_append_cstr(sb, "T_CONST");
+ else if (kind == T_STRUCT) sb_append_cstr(sb, "T_STRUCT");
+ else if (kind == T_UNION) sb_append_cstr(sb, "T_UNION");
+ else if (kind == T_ENUM) sb_append_cstr(sb, "T_ENUM");
+ else if (kind == T_OPERATOR) sb_append_cstr(sb, "T_OPERATOR");
+ else sb_append_cstr(sb, "(unknown token)");
+}
+
+void
+print_loc(Loc loc)
+{
+ fprintf(stderr, "%.*s:%li:%li: ", (int) loc.filename.count, loc.filename.items, loc.line, loc.column);
+}
+
+void
+print_loc_pad(Loc loc, int min_width)
+{
+ int len = fprintf(stderr, "%.*s:%li:%li: ", (int) loc.filename.count, loc.filename.items, loc.line, loc.column);
+ if (len < min_width) {
+ fprintf(stderr, "%*s", min_width - len, "");
+ }
+}
+
+void
+debug_print_token(Token t)
+{
+ print_loc(t.loc);
+ if (t.kind == T_ERROR_) {
+ fprintf(stderr, "ERROR");
+ } else if (t.kind >= 32 && t.kind < 127) {
+ fprintf(stderr, "'%c'", t.kind);
+ } else if (t.kind == T_EOF) {
+ fprintf(stderr, "EOF");
+ } else if (t.kind == T_WHITESPACE) {
+ fprintf(stderr, "WHITESPACE(%.*s)", (int) t.sv.count, t.sv.items);
+ } else if (t.kind == T_IDENT) {
+ fprintf(stderr, "IDENT(%.*s)", (int) t.sv.count, t.sv.items);
+ } else if (t.kind == T_EOF) {
+ fprintf(stderr, "NUMBER(%.*s)", (int) t.sv.count, t.sv.items);
+ } else if (t.kind == T_SQ_STRING || t.kind == T_DQ_STRING) {
+ fprintf(stderr, "STRING(%.*s)", (int) t.sv.count, t.sv.items);
+ } else if (t.kind == T_RETURN) {
+ fprintf(stderr, "return");
+ } else if (t.kind == T_IF) {
+ fprintf(stderr, "if");
+ } else if (t.kind == T_ELSE) {
+ fprintf(stderr, "else");
+ } else if (t.kind == T_WHILE) {
+ fprintf(stderr, "while");
+ } else if (t.kind == T_FOR) {
+ fprintf(stderr, "for");
+ } else if (t.kind == T_SWITCH) {
+ fprintf(stderr, "switch");
+ } else if (t.kind == T_CASE) {
+ fprintf(stderr, "case");
+ } else if (t.kind == T_DEFAULT) {
+ fprintf(stderr, "default");
+ } else if (t.kind == T_BREAK) {
+ fprintf(stderr, "break");
+ } else if (t.kind == T_CONTINUE) {
+ fprintf(stderr, "continue");
+ } else if (t.kind == T_TYPEDEF) {
+ fprintf(stderr, "typedef");
+ } else if (t.kind == T_FUNC) {
+ fprintf(stderr, "func");
+ } else if (t.kind == T_VAR) {
+ fprintf(stderr, "var");
+ } else if (t.kind == T_CONST) {
+ fprintf(stderr, "const");
+ } else if (t.kind == T_STRUCT) {
+ fprintf(stderr, "struct");
+ } else if (t.kind == T_UNION) {
+ fprintf(stderr, "union");
+ } else if (t.kind == T_ENUM) {
+ fprintf(stderr, "enum");
+ } else if (t.kind == T_OPERATOR) {
+ fprintf(stderr, "operator");
+ } else {
+ fprintf(stderr, "unknown token 0x%02x", t.kind);
+ }
+ fprintf(stderr, "\n");
+}
+
+int
+next_char(Lexer *lexer)
+{
+ if (!lexer->source.count) return EOF;
+ char c = *lexer->source.items++;
+ lexer->source.count--;
+ if (c == '\n') {
+ lexer->current_loc.column = 1;
+ lexer->current_loc.line++;
+ } else {
+ lexer->current_loc.column++;
+ }
+ return c;
+}
+
+int
+next_char_if(Lexer *lexer, int (*pred)(int))
+{
+ if (!lexer->source.count) return EOF;
+ if (pred(*lexer->source.items)) {
+ return next_char(lexer);
+ }
+ return CHAR_INVALID;
+}
+
+int
+is_ident_char(int c)
+{
+ return c == '_' ||
+ (c >= '0' && c <= '9') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z');
+}
+
+int
+is_symbol_char(int c)
+{
+ return c == '+' ||
+ c == '-' ||
+ c == '*' ||
+ c == '/' ||
+ c == '%' ||
+ c == '(' ||
+ c == ')' ||
+ c == '[' ||
+ c == ']' ||
+ c == '<' ||
+ c == '>' ||
+ c == '{' ||
+ c == '}' ||
+ c == '=' ||
+ c == '&' ||
+ c == '|' ||
+ c == '^' ||
+ c == '~' ||
+ c == '?' ||
+ c == '!' ||
+ c == '.' ||
+ c == ',' ||
+ c == ':' ||
+ c == ';' ||
+ /* c == '`' || */
+ /* c == '#' || */
+ /* c == '@' || */
+ /* c == '$' || */
+ 0;
+}
+
+int
+skip_comment(Lexer *lexer)
+{
+ Loc start_loc = lexer->current_loc;
+ int len = 0;
+ if (sv_starts_with_cstr(lexer->source, "/*")) {
+ len = 2;
+ next_char(lexer);
+ next_char(lexer);
+ while (lexer->source.count && !sv_starts_with_cstr(lexer->source, "*/")) {
+ if (sv_starts_with_cstr(lexer->source, "/*")) {
+ len += skip_comment(lexer);
+ } else {
+ len++;
+ next_char(lexer);
+ }
+ }
+ if (!lexer->source.count) {
+ print_loc(start_loc);
+ fprintf(stderr, "Error: unterminated comment\n");
+ return -1;
+ }
+ next_char(lexer);
+ next_char(lexer);
+ len += 2;
+ } else if (sv_starts_with_cstr(lexer->source, "//")) {
+ char next;
+ while ((next = next_char(lexer)) != '\n' && next != EOF) len++;
+ }
+ return len;
+}
+
+Token
+next_token(Lexer *lexer)
+{
+ Loc start_loc = lexer->current_loc;
+ Token result = { .loc = start_loc, .sv = { .items = lexer->source.items, .count = 0 } };
+
+ if (!lexer->source.count) {
+ result.kind = T_EOF;
+ return result;
+ }
+
+ int c = next_char_if(lexer, isspace);
+ size_t len = 0;
+
+ {
+ bool whitespace = false;
+ int comment_len = 0;
+ while (isspace(c) || (comment_len = skip_comment(lexer))) {
+ if (comment_len < 0) {
+ return (Token) {
+ .kind = T_ERROR_,
+ .loc = start_loc,
+ };
+ } else if (comment_len > 0) {
+ len += comment_len;
+ } else {
+ len++;
+ }
+ whitespace = true;
+ while ((c = next_char_if(lexer, isspace)) >= 0) len++;
+ }
+ if (whitespace) {
+ result.kind = T_WHITESPACE;
+ result.sv.count = len;
+ return result;
+ }
+ }
+
+ len = 1;
+
+ c = next_char(lexer);
+
+ if (isdigit(c)) {
+ while (next_char_if(lexer, isdigit) >= 0) len++;
+ result.kind = T_NUMBER;
+ result.sv.count = len;
+ return result;
+ }
+
+ if (is_ident_char(c)) {
+ while (next_char_if(lexer, is_ident_char) >= 0) len++;
+ result.sv.count = len;
+ result.kind = get_keyword_kind(result.sv);
+ return result;
+ }
+
+ if (is_symbol_char(c)) {
+ result.kind = c;
+ result.sv.count = len;
+ return result;
+ }
+
+ if (c == '\'' || c == '"') {
+ char next;
+ while ((next = next_char(lexer)) != EOF && next != c) {
+ len++;
+ if (next == '\\') {
+ next_char(lexer);
+ len++;
+ }
+ }
+ if (next != c) {
+ print_loc(start_loc);
+ fprintf(stderr, "Error: unterminated %s quoted string\n", c == '"' ? "double" : "single");
+
+ return (Token) {
+ .kind = T_ERROR_,
+ .loc = start_loc,
+ };
+ }
+ result.kind = c == '"' ? T_DQ_STRING : T_SQ_STRING;
+ result.sv.count = ++len;
+ return result;
+ }
+
+ print_loc(start_loc);
+ fprintf(stderr, "Error: unknown character '%c'\n", c);
+
+ return (Token) {
+ .kind = T_ERROR_,
+ .loc = start_loc,
+ };
+}
+
+Token
+peek_token(Lexer *lexer)
+{
+ Lexer peek = *lexer;
+ return next_token(&peek);
+}
+
+Token_kind
+get_keyword_kind(Sv sv)
+{
+ int i = 0;
+ for (;i < keywords_count; i++) {
+ if (sv_equal(sv, keywords[i])) {
+ return i + T_KEYWORDS_;
+ }
+ }
+ return T_IDENT;
+}
+
+Token
+next_token_if(Lexer *lexer, int (*pred)(Token))
+{
+ Lexer peek = *lexer;
+ Token t = next_token(&peek);
+ if (pred(t)) {
+ *lexer = peek;
+ return t;
+ }
+ return (Token) {
+ .kind = T_INVALID,
+ .loc = lexer->current_loc,
+ };
+}
+
+void
+skip_whitespace(Lexer *lexer)
+{
+ Lexer peek = *lexer;
+ Token t = next_token(&peek);
+ if (t.kind == T_WHITESPACE) {
+ *lexer = peek;
+ }
+}
+
+Token
+expect_token_kind(Lexer *lexer, Token_kind kind)
+{
+ Token t = next_token(lexer);
+ if (t.kind != kind) {
+ Sb error = {0};
+
+ sb_append_loc(&error, t.loc);
+ sb_append_cstr(&error, "Error: expected token ");
+ sb_append_token_kind(&error, kind);
+ sb_append_cstr(&error, ", but got ");
+ sb_append_token_kind(&error, t.kind);
+
+ fprintf(stderr, "%.*s\n", (int) error.count, error.items);
+
+ sb_release(&error);
+ return (Token) {
+ .kind = T_ERROR_,
+ .loc = t.loc,
+ };
+ }
+ return t;
+}
diff --git a/lexer.h b/lexer.h
@@ -0,0 +1,81 @@
+#ifndef DC_LEXER_H
+#define DC_LEXER_H
+
+#include "dlib.h"
+
+typedef struct {
+ Sv filename;
+ size_t line, column;
+} Loc;
+
+typedef enum {
+ T_ERROR_ = 0,
+ T_EOF = 256,
+ T_INVALID,
+ T_WHITESPACE,
+ T_IDENT,
+ T_NUMBER,
+ T_SQ_STRING,
+ T_DQ_STRING,
+
+ T_KEYWORDS_,
+
+ T_RETURN = T_KEYWORDS_,
+ T_IF,
+ T_ELSE,
+ T_WHILE,
+ T_FOR,
+ T_SWITCH,
+ T_CASE,
+ T_DEFAULT,
+ T_BREAK,
+ T_CONTINUE,
+ T_TYPEDEF,
+ T_FUNC,
+ T_VAR,
+ T_CONST,
+ T_STRUCT,
+ T_UNION,
+ T_ENUM,
+ T_OPERATOR,
+
+ T_COUNT_
+} Token_kind;
+
+typedef struct {
+ Token_kind kind;
+ Loc loc;
+ Sv sv;
+} Token;
+
+typedef struct {
+ Sv source;
+ Loc current_loc;
+} Lexer;
+
+#define CHAR_INVALID (-2)
+int next_char(Lexer *lexer);
+int next_char_if(Lexer *lexer, int (*pred)(int));
+
+int is_ident_char(int c);
+int is_symbol_char(int c);
+
+int skip_comment(Lexer *lexer);
+Token next_token(Lexer *lexer);
+Token peek_token(Lexer *lexer);
+
+Token_kind get_keyword_kind(Sv sv);
+
+Token next_token_if(Lexer *lexer, int (*pred)(Token));
+Token next_token_if_kind(Lexer *lexer, Token_kind kind);
+Token expect_token_kind(Lexer *lexer, Token_kind kind);
+void skip_whitespace(Lexer *lexer);
+
+void sb_append_token_kind(Sb *sb, Token_kind kind);
+void sb_append_loc(Sb *sb, Loc loc);
+
+void print_loc(Loc loc);
+void print_loc_pad(Loc loc, int min_width);
+void debug_print_token(Token t);
+
+#endif /* DC_LEXER_H */
diff --git a/main.c b/main.c
@@ -0,0 +1,70 @@
+#define DLIB_IMPLEMENTATION
+#include "dlib.h"
+
+#include "lexer.h"
+#include "parser.h"
+
+Ast_node *
+malloc_ast()
+{
+ void *result = malloc(sizeof(Ast_node));
+ if (!result) {
+ fprintf(stderr, "malloc_ast(): Out of memory\n");
+ abort();
+ }
+ return result;
+}
+
+void
+free_ast(Ast_node *node)
+{
+ free(node);
+}
+
+void
+usage(FILE *out, const char *program_name)
+{
+ fprintf(out, "Usage: %s <input>\n", program_name);
+}
+
+int
+main(int argc, char **argv)
+{
+ assert(argc > 0);
+ char *program_name = pop_arg(argc, argv);
+ if (!argc) {
+ fprintf(stderr, "Error: Missing input file\n");
+ usage(stderr, program_name);
+ return 1;
+ }
+ Sb input_source = {0};
+ char *input_file_name = pop_arg(argc, argv);
+ if (!sb_read_file(&input_source, input_file_name)) {
+ return 1;
+ }
+ Lexer lexer = {
+ .source = sv_from_sb(input_source),
+ .current_loc = {
+ .filename = sv_from_cstr(input_file_name),
+ .line = 1,
+ .column = 1
+ }
+ };
+ Parser parser = {
+ .lexer = lexer,
+ .ast_alloc = malloc_ast,
+ .ast_free = free_ast,
+ };
+ Ast_node_list ast = parse(&parser);
+ for (int i = 0; i < ast.count; ++i) {
+ debug_print_ast_node(ast.items[i], 0);
+ }
+ /*
+ Token t;
+ while ((t = next_token(&lexer)).kind != T_EOF && t.kind != T_ERROR) {
+ if (t.kind != T_WHITESPACE) debug_print_token(t);
+ }
+ debug_print_token(t);
+ */
+ return 0;
+}
diff --git a/parser.c b/parser.c
@@ -0,0 +1,579 @@
+#include "lexer.h"
+#include "parser.h"
+
+int binop_precedence[] = {
+ [BINOP_INVALID] = 250,
+ [BINOP_ADD] = 20,
+ [BINOP_SUB] = 20,
+ [BINOP_MUL] = 10,
+ [BINOP_DIV] = 10,
+ [BINOP_MOD] = 10,
+ [BINOP_BAND] = 60,
+ [BINOP_BOR] = 80,
+ [BINOP_BXOR] = 70,
+ [BINOP_LSHIFT] = 30,
+ [BINOP_RSHIFT] = 30,
+ [BINOP_ASSIGN] = 121,
+ [BINOP_ADD_ASSIGN] = 121,
+ [BINOP_SUB_ASSIGN] = 121,
+ [BINOP_MUL_ASSIGN] = 121,
+ [BINOP_DIV_ASSIGN] = 121,
+ [BINOP_MOD_ASSIGN] = 121,
+ [BINOP_BAND_ASSIGN] = 121,
+ [BINOP_BOR_ASSIGN] = 121,
+ [BINOP_BXOR_ASSIGN] = 121,
+ [BINOP_LSHIFT_ASSIGN] = 121,
+ [BINOP_RSHIFT_ASSIGN] = 121,
+ [BINOP_LAND] = 90,
+ [BINOP_LOR] = 110,
+ [BINOP_LXOR] = 100,
+ [BINOP_LT] = 40,
+ [BINOP_LE] = 40,
+ [BINOP_EQ] = 50,
+ [BINOP_NEQ] = 50,
+ [BINOP_GE] = 40,
+ [BINOP_GT] = 40,
+ [BINOP_COMMA] = 130,
+ [BINOP_ALL_] = 140,
+};
+
+Ast_node *
+new_ast_node(Parser *p, Ast_kind kind, Token loc)
+{
+ Ast_node *result = p->ast_alloc();
+ result->kind = kind;
+ result->start_tok = loc;
+ return result;
+}
+
+Ast_node *
+parse_postfix(Parser *p, Ast_node *child)
+{
+ (void) p;
+ return child;
+}
+
+Ast_node *
+parse_primitive(Parser *p)
+{
+ Ast_node *result;
+ Token t = next_token(&p->lexer);
+ if (t.kind == T_NUMBER) {
+ result = new_ast_node(p, AST_NUMBER, t);
+ } else if (t.kind == T_IDENT) {
+ result = new_ast_node(p, AST_IDENT, t);
+ } else if (t.kind == '(') {
+ result = parse_expression(p);
+ expect_token_kind(&p->lexer, ')');
+ } else {
+ Sb sb = {0};
+ sb_append_loc(&sb, t.loc);
+ sb_append_cstr(&sb, "Error: invalid primitive ");
+ sb_append_token_kind(&sb, t.kind);
+ sb_append_char(&sb, '\n');
+ sb_write(2, sb);
+ sb_release(&sb);
+ abort();
+ }
+ return parse_postfix(p, result);
+}
+
+Ast_node *
+parse_prefix(Parser *p)
+{
+ Ast_node *result = parse_primitive(p);
+ return result;
+}
+
+Binop
+collect_binop(Parser *p)
+{
+ Lexer saved_lexer;
+ Token peek = peek_token(&p->lexer);
+ Binop result = { .tok = peek };
+ switch((int) peek.kind) {
+ case '+':
+ next_token(&p->lexer);
+ peek = peek_token(&p->lexer);
+ if (peek.kind == '=') {
+ next_token(&p->lexer);
+ result.kind = BINOP_ADD_ASSIGN;
+ result.tok.sv.count++;
+ } else {
+ result.kind = BINOP_ADD;
+ }
+ break;
+ case '-':
+ next_token(&p->lexer);
+ peek = peek_token(&p->lexer);
+ if (peek.kind == '=') {
+ next_token(&p->lexer);
+ result.kind = BINOP_SUB_ASSIGN;
+ result.tok.sv.count++;
+ } else {
+ result.kind = BINOP_SUB;
+ }
+ break;
+ case '*':
+ next_token(&p->lexer);
+ peek = peek_token(&p->lexer);
+ if (peek.kind == '=') {
+ next_token(&p->lexer);
+ result.kind = BINOP_MUL_ASSIGN;
+ result.tok.sv.count++;
+ } else {
+ result.kind = BINOP_MUL;
+ }
+ break;
+ case '/':
+ next_token(&p->lexer);
+ peek = peek_token(&p->lexer);
+ if (peek.kind == '=') {
+ next_token(&p->lexer);
+ result.kind = BINOP_DIV_ASSIGN;
+ result.tok.sv.count++;
+ } else {
+ result.kind = BINOP_DIV;
+ }
+ break;
+ case '%':
+ next_token(&p->lexer);
+ peek = peek_token(&p->lexer);
+ if (peek.kind == '=') {
+ next_token(&p->lexer);
+ result.kind = BINOP_MOD_ASSIGN;
+ result.tok.sv.count++;
+ } else {
+ result.kind = BINOP_MOD;
+ }
+ break;
+ case '&':
+ next_token(&p->lexer);
+ peek = peek_token(&p->lexer);
+ if (peek.kind == '=') {
+ next_token(&p->lexer);
+ result.kind = BINOP_BAND_ASSIGN;
+ result.tok.sv.count++;
+ } else if (peek.kind == '&') {
+ next_token(&p->lexer);
+ result.kind = BINOP_LAND;
+ result.tok.sv.count++;
+ } else {
+ result.kind = BINOP_BAND;
+ }
+ break;
+ case '|':
+ next_token(&p->lexer);
+ peek = peek_token(&p->lexer);
+ if (peek.kind == '=') {
+ next_token(&p->lexer);
+ result.kind = BINOP_BOR_ASSIGN;
+ result.tok.sv.count++;
+ } else if (peek.kind == '|') {
+ next_token(&p->lexer);
+ result.kind = BINOP_LOR;
+ result.tok.sv.count++;
+ } else {
+ result.kind = BINOP_BOR;
+ }
+ break;
+ case '^':
+ next_token(&p->lexer);
+ peek = peek_token(&p->lexer);
+ if (peek.kind == '=') {
+ next_token(&p->lexer);
+ result.kind = BINOP_BXOR_ASSIGN;
+ result.tok.sv.count++;
+ } else if (peek.kind == '^') {
+ next_token(&p->lexer);
+ result.kind = BINOP_LXOR;
+ result.tok.sv.count++;
+ } else {
+ result.kind = BINOP_BXOR;
+ }
+ break;
+ case '<':
+ next_token(&p->lexer);
+ peek = peek_token(&p->lexer);
+ if (peek.kind == '=') {
+ next_token(&p->lexer);
+ result.kind = BINOP_LE;
+ result.tok.sv.count++;
+ } else if (peek.kind == '<') {
+ next_token(&p->lexer);
+ peek = peek_token(&p->lexer);
+ if (peek.kind == '=') {
+ next_token(&p->lexer);
+ result.kind = BINOP_LSHIFT_ASSIGN;
+ result.tok.sv.count++;
+ } else {
+ result.kind = BINOP_LSHIFT;
+ result.tok.sv.count++;
+ }
+ } else {
+ result.kind = BINOP_LT;
+ }
+ break;
+ case '>':
+ next_token(&p->lexer);
+ peek = peek_token(&p->lexer);
+ if (peek.kind == '=') {
+ next_token(&p->lexer);
+ result.kind = BINOP_GE;
+ result.tok.sv.count++;
+ } else if (peek.kind == '>') {
+ next_token(&p->lexer);
+ peek = peek_token(&p->lexer);
+ if (peek.kind == '=') {
+ next_token(&p->lexer);
+ result.kind = BINOP_RSHIFT_ASSIGN;
+ result.tok.sv.count++;
+ } else {
+ result.kind = BINOP_RSHIFT;
+ result.tok.sv.count++;
+ }
+ } else {
+ result.kind = BINOP_GT;
+ }
+ break;
+ case '=':
+ next_token(&p->lexer);
+ peek = peek_token(&p->lexer);
+ if (peek.kind == '=') {
+ next_token(&p->lexer);
+ result.kind = BINOP_EQ;
+ result.tok.sv.count++;
+ } else {
+ result.kind = BINOP_ASSIGN;
+ }
+ break;
+ case '!':
+ saved_lexer = p->lexer;
+ next_token(&p->lexer);
+ peek = peek_token(&p->lexer);
+ if (peek.kind == '=') {
+ next_token(&p->lexer);
+ result.kind = BINOP_NEQ;
+ result.tok.sv.count++;
+ } else {
+ p->lexer = saved_lexer;
+ result.kind = BINOP_INVALID;
+ }
+ break;
+ case ',':
+ next_token(&p->lexer);
+ result.kind = BINOP_COMMA;
+ break;
+ default:
+ result.kind = BINOP_INVALID;
+ break;
+ }
+ return result;
+}
+
+Ast_node *
+parse_binop(Parser *p, int precedence, Ast_node *left_child)
+{
+ Lexer saved_lexer;
+ Ast_node *left;
+ Ast_node *right = NULL;
+ Ast_node *result;
+ Binop binop;
+ int next_prec;
+ if (!left_child) {
+ left = parse_prefix(p);
+ } else {
+ left = left_child;
+ }
+ do {
+ saved_lexer = p->lexer;
+ skip_whitespace(&p->lexer);
+ Binop binop_peek = collect_binop(p);
+ next_prec = binop_precedence[binop_peek.kind];
+ if (binop_peek.kind == BINOP_INVALID) {
+ break;
+ }
+ if (next_prec > precedence) {
+ p->lexer = saved_lexer;
+ break;
+ }
+ if (next_prec < precedence) {
+ p->lexer = saved_lexer;
+ skip_whitespace(&p->lexer);
+ if (right) {
+ right = parse_binop(p, next_prec, right);
+ } else {
+ left = parse_binop(p, next_prec, left);
+ }
+ continue;
+ }
+ skip_whitespace(&p->lexer);
+ if (right) {
+ result = new_ast_node(p, AST_BINOP, binop.tok);
+ result->binop.op = binop;
+ result->binop.left = left;
+ result->binop.right = right;
+ left = result;
+ }
+ binop = binop_peek;
+ if (next_prec & 1) {
+ right = parse_binop(p, next_prec, NULL);
+ } else {
+ right = parse_prefix(p);
+ }
+ } while (1);
+ if (right) {
+ result = new_ast_node(p, AST_BINOP, binop.tok);
+ result->binop.op = binop;
+ result->binop.left = left;
+ result->binop.right = right;
+ left = result;
+ }
+ return left;
+}
+
+Ast_node *
+parse_expression(Parser *p)
+{
+ Ast_node *result = parse_binop(p, binop_precedence[BINOP_ALL_], NULL);
+ return result;
+}
+
+Ast_node *
+parse_type(Parser *p)
+{
+ Token base = expect_token_kind(&p->lexer, T_IDENT);
+ return new_ast_node(p, AST_TYPE, base);
+}
+
+Ast_node *
+parse_var_declaration(Parser *p)
+{
+ Token var_name;
+ Ast_node *var_type = NULL;
+ Ast_node *var_value = NULL;
+ Ast_node *result;
+
+ var_name = expect_token_kind(&p->lexer, T_IDENT);
+ skip_whitespace(&p->lexer);
+ expect_token_kind(&p->lexer, ':');
+ skip_whitespace(&p->lexer);
+ if (peek_token(&p->lexer).kind != '=') {
+ var_type = parse_type(p);
+ skip_whitespace(&p->lexer);
+ }
+ if (peek_token(&p->lexer).kind == '=') {
+ next_token(&p->lexer);
+ skip_whitespace(&p->lexer);
+ var_value = parse_expression(p);
+ }
+ result = new_ast_node(p, AST_VAR_DECL, var_name);
+ result->var.name = var_name;
+ result->var.type = var_type;
+ result->var.value = var_value;
+
+ return result;
+}
+
+Ast_node *
+parse_var_declaration_statement(Parser *p) {
+ Token start_tok;
+ start_tok = expect_token_kind(&p->lexer, T_VAR);
+
+ skip_whitespace(&p->lexer);
+ Ast_node *result = parse_var_declaration(p);
+
+ skip_whitespace(&p->lexer);
+ expect_token_kind(&p->lexer, ';');
+
+ result->start_tok = start_tok;
+ return result;
+}
+
+Ast_node *
+parse_block(Parser *p)
+{
+ Token end_tok;
+ Token start_tok = expect_token_kind(&p->lexer, '{');
+ skip_whitespace(&p->lexer);
+ Ast_node *node, *result;
+ Ast_node_list list = {0};
+ Token peek;
+ skip_whitespace(&p->lexer);
+ while ((peek = peek_token(&p->lexer)).kind != '}') {
+ if (peek.kind == T_VAR) {
+ node = parse_var_declaration_statement(p);
+ } else if (peek.kind == ';') {
+ next_token(&p->lexer);
+ skip_whitespace(&p->lexer);
+ continue;
+ } else if (peek.kind == '{') {
+ node = parse_block(p);
+ } else if (peek.kind == T_EOF) {
+ // Leaking nodes
+ free(list.items);
+
+ print_loc(start_tok.loc);
+ fprintf(stderr, "Error: unterminated block\n");
+ return new_ast_node(p, AST_ERROR, peek);
+ } else if (peek.kind == T_ERROR_) {
+ // Leaking nodes
+ free(list.items);
+ return new_ast_node(p, AST_ERROR, peek);
+ } else {
+ node = parse_expression(p);
+ expect_token_kind(&p->lexer, ';');
+ }
+ da_append(&list, node);
+ skip_whitespace(&p->lexer);
+ }
+ end_tok = expect_token_kind(&p->lexer, '}');
+ result = new_ast_node(p, AST_BLOCK, start_tok);
+ result->block.list = list;
+ result->block.end_tok = end_tok;
+ return result;
+}
+
+Ast_node_list
+parse_func_arg_declaraction_list(Parser *p)
+{
+ Token peek;
+ Ast_node *node;
+ Ast_node_list list = {0};
+ while ((peek = peek_token(&p->lexer)).kind != ')') {
+ node = parse_var_declaration(p);
+ da_append(&list, node);
+ skip_whitespace(&p->lexer);
+ }
+ return list;
+}
+
+Ast_node *
+parse_func_declaration_statement(Parser *p)
+{
+ Ast_node *ret_type = NULL;
+ Ast_node *result;
+ Token start_tok = expect_token_kind(&p->lexer, T_FUNC);
+ skip_whitespace(&p->lexer);
+ Token name = expect_token_kind(&p->lexer, T_IDENT);
+
+ skip_whitespace(&p->lexer);
+ expect_token_kind(&p->lexer, '(');
+ skip_whitespace(&p->lexer);
+ Ast_node_list arg_list = parse_func_arg_declaraction_list(p);
+ expect_token_kind(&p->lexer, ')');
+
+ skip_whitespace(&p->lexer);
+ if (peek_token(&p->lexer).kind == '-') {
+ expect_token_kind(&p->lexer, '-');
+ expect_token_kind(&p->lexer, '>');
+ skip_whitespace(&p->lexer);
+ ret_type = parse_type(p);
+ skip_whitespace(&p->lexer);
+ }
+
+ Ast_node *body = parse_block(p);
+
+ result = new_ast_node(p, AST_FUNC_DECL, start_tok);
+ result->func.body = body;
+ result->func.ret_type = ret_type;
+ result->func.name = name;
+ result->func.arg_list = arg_list;
+ return result;
+}
+
+Ast_node_list
+parse(Parser *p)
+{
+ Ast_node *node;
+ Ast_node_list list = {0};
+ Token peek;
+ skip_whitespace(&p->lexer);
+ while ((peek = peek_token(&p->lexer)).kind != T_EOF) {
+ if (peek.kind == T_VAR) {
+ node = parse_var_declaration_statement(p);
+ } else if (peek.kind == T_FUNC) {
+ node = parse_func_declaration_statement(p);
+ } else if (peek.kind == T_ERROR_) {
+ abort();
+ } else {
+ Sb sb = {0};
+ sb_append_loc(&sb, peek.loc);
+ sb_append_cstr(&sb, "Error: invalid top level declaration at token ");
+ sb_append_token_kind(&sb, peek.kind);
+ sb_append_char(&sb, '\n');
+ sb_write(2, sb);
+ sb_release(&sb);
+ abort();
+ }
+ da_append(&list, node);
+ skip_whitespace(&p->lexer);
+ }
+ return list;
+}
+
+void
+debug_print_ast_node(Ast_node *node, int indent)
+{
+ if (!node) {
+ fprintf(stderr, "%*s", indent * 2, "");
+ fprintf(stderr, "(null)");
+ return;
+ }
+ int min_width = node->start_tok.loc.filename.count + 11;
+ print_loc_pad(node->start_tok.loc, min_width);
+ fprintf(stderr, "%*s", indent * 2, "");
+
+ switch (node->kind) {
+ case AST_TYPE:
+ fprintf(stderr, "Type(%.*s)\n", (int) node->start_tok.sv.count, node->start_tok.sv.items);
+ break;
+ case AST_VAR_DECL:
+ fprintf(stderr, "VarDecl(%.*s)\n", (int) node->var.name.sv.count, node->var.name.sv.items);
+ if (node->var.type) {
+ debug_print_ast_node(node->var.type, indent + 1);
+ }
+ if (node->var.value) {
+ debug_print_ast_node(node->var.value, indent + 1);
+ }
+ break;
+ case AST_FUNC_DECL:
+ fprintf(stderr, "FuncDecl(%.*s)\n", (int) node->func.name.sv.count, node->func.name.sv.items);
+ if (node->func.ret_type) {
+ debug_print_ast_node(node->func.ret_type, indent + 1);
+ }
+ for (int i = 0; i < node->func.arg_list.count; ++i) {
+ debug_print_ast_node(node->func.arg_list.items[i], indent + 1);
+ }
+ debug_print_ast_node(node->func.body, indent + 1);
+ break;
+ case AST_TYPE_DECL:
+ fprintf(stderr, "TypeDecl\n");
+ break;
+ case AST_BLOCK:
+ fprintf(stderr, "Block {\n");
+ for (int i = 0; i < node->block.list.count; ++i) {
+ debug_print_ast_node(node->block.list.items[i], indent + 1);
+ }
+ print_loc_pad(node->block.end_tok.loc, min_width);
+ fprintf(stderr, "%*s\n", indent * 2, "}");
+ break;
+ case AST_BINOP:
+ fprintf(stderr, "Binop(%.*s)\n", (int) node->binop.op.tok.sv.count, node->binop.op.tok.sv.items);
+ debug_print_ast_node(node->binop.left, indent + 1);
+ debug_print_ast_node(node->binop.right, indent + 1);
+ break;
+ case AST_NUMBER:
+ fprintf(stderr, "Number(%.*s)\n", (int) node->start_tok.sv.count, node->start_tok.sv.items);
+ break;
+ case AST_IDENT:
+ fprintf(stderr, "Ident(%.*s)\n", (int) node->start_tok.sv.count, node->start_tok.sv.items);
+ break;
+ case AST_ERROR:
+ fprintf(stderr, "Error\n");
+ break;
+ default:
+ fprintf(stderr, "Unreachable\n");
+ abort();
+ }
+}
diff --git a/parser.h b/parser.h
@@ -0,0 +1,118 @@
+#ifndef DC_PARSER_H
+#define DC_PARSER_H
+
+typedef enum {
+ BINOP_INVALID,
+
+ BINOP_ADD,
+ BINOP_SUB,
+ BINOP_MUL,
+ BINOP_DIV,
+ BINOP_MOD,
+ BINOP_BAND,
+ BINOP_BOR,
+ BINOP_BXOR,
+ BINOP_LSHIFT,
+ BINOP_RSHIFT,
+
+ BINOP_ASSIGN,
+
+ BINOP_ADD_ASSIGN,
+ BINOP_SUB_ASSIGN,
+ BINOP_MUL_ASSIGN,
+ BINOP_DIV_ASSIGN,
+ BINOP_MOD_ASSIGN,
+ BINOP_BAND_ASSIGN,
+ BINOP_BOR_ASSIGN,
+ BINOP_BXOR_ASSIGN,
+ BINOP_LSHIFT_ASSIGN,
+ BINOP_RSHIFT_ASSIGN,
+
+ BINOP_LAND,
+ BINOP_LOR,
+ BINOP_LXOR,
+ BINOP_LT,
+ BINOP_LE,
+ BINOP_EQ,
+ BINOP_NEQ,
+ BINOP_GE,
+ BINOP_GT,
+
+ BINOP_COMMA,
+ BINOP_ALL_,
+} Binop_kind;
+
+typedef struct {
+ Binop_kind kind;
+ Token tok;
+} Binop;
+
+typedef enum {
+ AST_ERROR = 0,
+ AST_TYPE,
+ AST_VAR_DECL,
+ AST_FUNC_DECL,
+ AST_TYPE_DECL,
+ AST_BLOCK,
+ AST_BINOP,
+ AST_NUMBER,
+ AST_IDENT,
+} Ast_kind;
+
+typedef struct Ast_node Ast_node;
+
+typedef struct {
+ Ast_node **items;
+ int count;
+ int capacity;
+} Ast_node_list;
+
+struct Ast_node {
+ Ast_kind kind;
+ Token start_tok;
+ union {
+ struct {
+ Binop op;
+ Ast_node *left;
+ Ast_node *right;
+ } binop;
+ struct {
+ Token name;
+ Ast_node *type;
+ Ast_node *value;
+ } var;
+ struct {
+ Token name;
+ Ast_node_list arg_list;
+ Ast_node *ret_type;
+ Ast_node *body;
+ } func;
+ struct {
+ Ast_node_list list;
+ Token end_tok;
+ } block;
+ };
+};
+
+typedef Ast_node *(*Ast_alloc_fn)();
+typedef void (*Ast_free_fn)(Ast_node *);
+
+typedef struct {
+ Lexer lexer;
+ Ast_alloc_fn ast_alloc;
+ Ast_free_fn ast_free;
+} Parser;
+
+Ast_node *parse_postfix(Parser *p, Ast_node *child);
+Ast_node *parse_primitive(Parser *p);
+Ast_node *parse_prefix(Parser *p);
+Ast_node *parse_binop(Parser *p, int precedence, Ast_node *left_child);
+Ast_node *parse_expression(Parser *p);
+Ast_node *parse_type(Parser *p);
+Ast_node *parse_var_declaration(Parser *p);
+
+Ast_node_list parse(Parser *p);
+
+void debug_print_ast_node(Ast_node *node, int indent);
+
+#endif /* DC_PARSER_H */
diff --git a/test_input/binop.dc b/test_input/binop.dc
@@ -0,0 +1,8 @@
+var i:= 1+2;
+var j:= 1+2+3+4;
+var k:= 1+2*3;
+var l:= 1+2*3+4;
+var m:= 1+2+3*4;
+var n:= 1+2+3*4+5;
+
+var assign := i = j + 4 = k;
diff --git a/test_input/block.dc b/test_input/block.dc
@@ -0,0 +1,9 @@
+var i := 50;
+var j : = 69;
+{
+ var k: i32 = 10;
+ var m: i64;
+ ;;;;
+}
+
+var n : i16= 1337;
diff --git a/test_input/func.dc b/test_input/func.dc
@@ -0,0 +1,4 @@
+func fib(count: i32) {
+ var a := 0;
+ var b := 1;
+}
diff --git a/test_input/input.dc b/test_input/input.dc
@@ -0,0 +1,19 @@
+// Basic 2 dimensional vector
+typedef Vec2i: struct {
+ a: int;
+ b: int;
+};
+
+/************************************************************
+ * Operator overloading for `Vec2i + Vec2i` *
+ * /* Now we have the ability to support nested comments */ *
+ * Returns a new `Vec2i` with it's elements being *
+ * the result of element-wise summation of the operands. *
+ ************************************************************/
+
+operator[infix, prec('+')] + (left: Vec2i, right: Vec2i) -> Vec2i {
+ var result: Vec2i = left;
+ result.a += right.a;
+ result.b += right.b;
+ return result;
+}
diff --git a/test_input/pepeg.dc b/test_input/pepeg.dc
@@ -0,0 +1,1002 @@
+var first := 1;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+var second := 1337;
diff --git a/test_input/var.dc b/test_input/var.dc
@@ -0,0 +1,3 @@
+var i := 10;
+var j: i32 = 0;
+var k :i64;