Plot project

This commit is contained in:
KK
2026-06-22 08:55:24 +02:00
parent bd97674f50
commit 25248c9055
16 changed files with 1750 additions and 0 deletions

87
a/ast.c Normal file
View File

@@ -0,0 +1,87 @@
typedef enum {
TYPE_NONE,
TYPE_VOID,
TYPE_BOOL,
TYPE_CHAR,
TYPE_SCHAR,
TYPE_UCHAR,
TYPE_SHORT,
TYPE_USHORT,
TYPE_INT,
TYPE_UINT,
TYPE_LONG,
TYPE_ULONG,
TYPE_LLONG,
TYPE_ULLONG,
TYPE_FLOAT,
TYPE_DOUBLE,
TYPE_LDOUBLE,
TYPE_POINTER,
TYPE_ARRAY,
TYPE_FUNCTION,
TYPE_STRUCT,
TYPE_UNION,
TYPE_ENUM,
} Type_Kind;
typedef struct Type Type;
struct Type {
Type_Kind kind;
int size;
int align;
Type *base;
};
typedef enum Ast_Kind {
AST_NONE,
AST_ERROR,
AST_PROGRAM,
AST_INT,
AST_UNARY,
AST_BINARY,
AST_FUNCTION,
AST_BLOCK,
} Ast_Kind;
typedef struct Ast Ast;
struct Ast {
Ast_Kind kind;
Token *pos;
Ast *first;
Ast *last;
union {
uint64_t u;
struct {
Token_Kind op;
Ast *l;
Ast *r;
};
char *error;
};
};
Ast *create_ast(Token *token, Ast_Kind kind) {
Ast *result = calloc(1, sizeof(Ast));
result->pos = token;
result->kind = kind;
return result;
}
Ast *create_binary_expr(Token *token, Token_Kind op, Ast *left, Ast *right) {
Ast *result = create_ast(token, AST_BINARY);
result->op = op;
result->l = left;
result->r = right;
return result;
}
Type base_type_int = {TYPE_INT, .size = sizeof(int), .align = __alignof(int)};
Type *type_int = &base_type_int;

154
a/base.c Normal file
View File

@@ -0,0 +1,154 @@
#define panicf(...) base_panicf(__FILE__, __LINE__, __VA_ARGS__)
#define len(x) (sizeof((x))/sizeof((x)[0]))
#define ilen(x) ((int)len(x))
_Noreturn
void base_panicf(char *file, int line, const char *fmt, ...) {
fprintf(stderr, "%s:%d: ", file, line);
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
fflush(stderr);
fflush(stdout);
exit(1);
}
#define Vec(T) struct { T *data; int len; int cap; }
typedef Vec(void) VecVoid;
#define vec_push(arr, elem) \
(vec_grow((VecVoid *)(arr), sizeof((arr)->data[0]), 1), (arr)->data[(arr)->len++] = (elem))
#define vec_insert(arr, idx, elem) do { \
assert((idx) >= 0 && (idx) <= (arr)->len); \
vec_grow((VecVoid *)(arr), sizeof((arr)->data[0]), 1); \
memmove(&(arr)->data[(idx) + 1], &(arr)->data[(idx)], sizeof((arr)->data[0]) * ((arr)->len - (idx))); \
(arr)->data[(idx)] = (elem); \
(arr)->len += 1; \
} while (0)
#define vec_pop(arr) \
(assert((arr)->len > 0), (arr)->data[--(arr)->len])
#define vec_del(arr, idx) do { \
assert((idx) >= 0 && (idx) < (arr)->len); \
memmove(&(arr)->data[(idx)], &(arr)->data[(idx) + 1], sizeof((arr)->data[0]) * ((arr)->len - (idx) - 1)); \
(arr)->len -= 1; \
} while (0)
#define vec_swap_del(arr, idx) do { \
assert((idx) >= 0 && (idx) < (arr)->len); \
(arr)->data[(idx)] = (arr)->data[(arr)->len - 1]; \
(arr)->len -= 1; \
} while (0)
#define vec_free(arr) do { \
free((arr)->data); \
(arr)->data = NULL; \
(arr)->len = 0; \
(arr)->cap = 0; \
} while (0)
void vec_grow(VecVoid *array, int elem_size, int add_len) {
if (array->len + add_len > array->cap) {
int cap = array->cap ? array->cap : 15;
int new_cap = (cap + add_len) * 2;
assert(new_cap > array->len + add_len);
array->data = realloc(array->data, elem_size * new_cap);
assert(array->data);
array->cap = new_cap;
}
}
void vec_test(void) {
Vec(int) int_vec = {0};
assert(int_vec.data == NULL);
assert(int_vec.len == 0);
assert(int_vec.cap == 0);
for (int i = 0; i < 32; i += 1) {
vec_push(&int_vec, i * 3);
assert(int_vec.len == i + 1);
assert(int_vec.cap >= int_vec.len);
assert(int_vec.data[int_vec.len - 1] == i * 3);
}
for (int i = 0; i < 32; i += 1) {
assert(int_vec.data[i] == i * 3);
}
int cap_after_ints = int_vec.cap;
vec_push(&int_vec, 12345);
assert(int_vec.len == 33);
assert(int_vec.data[32] == 12345);
assert(int_vec.cap >= cap_after_ints);
vec_insert(&int_vec, 0, 111);
assert(int_vec.len == 34);
assert(int_vec.data[0] == 111);
assert(int_vec.data[1] == 0);
vec_insert(&int_vec, 5, 222);
assert(int_vec.len == 35);
assert(int_vec.data[5] == 222);
assert(int_vec.data[6] == 12);
vec_insert(&int_vec, int_vec.len, 333);
assert(int_vec.len == 36);
assert(int_vec.data[int_vec.len - 1] == 333);
int popped = vec_pop(&int_vec);
assert(popped == 333);
assert(int_vec.len == 35);
vec_del(&int_vec, 5);
assert(int_vec.len == 34);
assert(int_vec.data[5] == 12);
int before_swap_del = int_vec.data[int_vec.len - 1];
vec_swap_del(&int_vec, 1);
assert(int_vec.len == 33);
assert(int_vec.data[1] == before_swap_del);
vec_free(&int_vec);
assert(int_vec.data == NULL);
assert(int_vec.len == 0);
assert(int_vec.cap == 0);
Vec(char *) str_vec = {0};
vec_push(&str_vec, "a");
vec_push(&str_vec, "bb");
vec_push(&str_vec, "ccc");
assert(str_vec.len == 3);
assert(strcmp(str_vec.data[0], "a") == 0);
assert(strcmp(str_vec.data[1], "bb") == 0);
assert(strcmp(str_vec.data[2], "ccc") == 0);
vec_insert(&str_vec, 1, "inserted");
assert(str_vec.len == 4);
assert(strcmp(str_vec.data[1], "inserted") == 0);
assert(strcmp(str_vec.data[2], "bb") == 0);
assert(strcmp(vec_pop(&str_vec), "ccc") == 0);
assert(str_vec.len == 3);
vec_del(&str_vec, 1);
assert(str_vec.len == 2);
assert(strcmp(str_vec.data[0], "a") == 0);
assert(strcmp(str_vec.data[1], "bb") == 0);
vec_swap_del(&str_vec, 0);
assert(str_vec.len == 1);
assert(strcmp(str_vec.data[0], "bb") == 0);
vec_free(&str_vec);
assert(str_vec.data == NULL);
assert(str_vec.len == 0);
assert(str_vec.cap == 0);
printf("vector tests passed\n");
}

10
a/build.sh Normal file
View File

@@ -0,0 +1,10 @@
set -euo pipefail
if [[ ! -e build ]]; then
mkdir build
fi
cd build
clang -o meta $(realpath ../meta.c) -g -Wall -Wextra -Wshadow -fdiagnostics-absolute-paths -Wno-missing-field-initializers
./meta > ../meta_gen.c
clang -o main $(realpath ../main.c) -g -Wall -Wextra -Wshadow -fdiagnostics-absolute-paths -Wno-missing-field-initializers
./main

52
a/emit_asm_x64.c Normal file
View File

@@ -0,0 +1,52 @@
void emit_expr(FILE *file, Ast *n) {
switch (n->kind) {
case AST_INT: {
fprintf(file, " mov rax, %lu\n", n->u);
} break;
case AST_BINARY: {
emit_expr(file, n->l);
fprintf(file, " push rax\n");
emit_expr(file, n->r);
fprintf(file, " pop rcx\n");
if (n->op == TOK_PLUS) {
fprintf(file, " add rax, rcx\n");
} else if (n->op == TOK_STAR) {
fprintf(file, " imul rax, rcx\n");
} else if (n->op == TOK_MINUS) {
fprintf(file, " mov rdx, rax\n");
fprintf(file, " mov rax, rcx\n");
fprintf(file, " sub rax, rdx\n");
} else {
panicf("error");
}
} break;
default: panicf("error");
}
}
void emit_program(FILE *file, Ast *n) {
fprintf(file, ".intel_syntax noprefix\n");
fprintf(file, ".global main\n");
fprintf(file, "main:\n");
emit_expr(file, n);
fprintf(file, " ret\n");
}
void emit_expr_test(char *expr, int value) {
Token_Array tokens = lex_file("expr", expr, strlen(expr));
Parser parser = {tokens.data, tokens.data + tokens.len};
Ast *ast = parse_expr(&parser, 0);
FILE *file = fopen("out.s", "w");
emit_program(file, ast);
fclose(file);
int result = system("clang out.s -o out");
assert(result == 0);
result = system("./out");
assert(WEXITSTATUS(result) == value);
}
void emit_x64_test(void) {
emit_expr_test("10+5*2-10", 10+5*2-10);
printf("x64 tests passed\n");
}

364
a/lex.c Normal file
View File

@@ -0,0 +1,364 @@
typedef struct Token {
Token_Kind kind;
int len;
char *str;
char *file;
int line, column;
struct {
uint8_t preproc : 1;
};
union {
uint64_t u;
char *intern;
};
} Token;
typedef Vec(Token) Token_Array;
typedef struct Lexer {
char *at;
char *end;
char *file;
int line;
int column;
uint8_t preproc;
} Lexer;
uint64_t hash_bytes(char *data, size_t len) {
uint64_t h = 1469598103934665603ull;
for (size_t i = 0; i < len; i++) {
h ^= (unsigned char)data[i];
h *= 1099511628211ull;
}
return h;
}
char *global_intern_table[4096];
char intern_arena[4096*6];
int intern_arena_len;
char *lex_alloc_string(int len) {
char *result = intern_arena + intern_arena_len;
intern_arena_len += len + 1;
assert(intern_arena_len < ilen(intern_arena));
return result;
}
char *make_intern(char *string, int len) {
uint64_t hash = hash_bytes(string, len);
int index = hash % ilen(global_intern_table);
for (int i = 0; i < ilen(global_intern_table); i += 1) {
if (global_intern_table[index] == NULL) {
global_intern_table[index] = lex_alloc_string(len + 1);
memcpy(global_intern_table[index], string, len);
global_intern_table[index][len] = 0;
return global_intern_table[index];
} else if (global_intern_table[index] && (memcmp(global_intern_table[index], string, len) == 0)) {
return global_intern_table[index];
}
index += 1;
index = index % ilen(global_intern_table);
}
assert(!"invalid codepath");
return NULL;
}
bool lex_is_keyword(char *string) {
bool result = string >= lex_first_keyword && string <= lex_last_keyword;
return result;
}
void lex_advance(Lexer *lex) {
if (lex->at >= lex->end) {
return;
}
if (*lex->at == '\n') {
lex->line++;
lex->preproc = false;
lex->column = 0;
} else {
lex->column++;
}
lex->at += 1;
}
void eat_whitespace(Lexer *lex) {
while (lex->at < lex->end) {
switch (*lex->at) {
case ' ': case '\t': case '\r': case '\n':
lex_advance(lex);
break;
default:
return;
}
}
}
Lexer make_lexer(char *file, char *src, int len) {
Lexer lex = {
.at = src,
.end = src + len,
.file = file,
.line = 0,
.column = 0,
};
return lex;
}
bool lex_peek_is(Lexer *lex, char c) {
return lex->at < lex->end && *lex->at == c;
}
bool lex_match(Lexer *lex, char c) {
if (lex_peek_is(lex, c)) {
lex_advance(lex);
return true;
}
return false;
}
Token_Kind lex_repeat_or_assign(Lexer *lex, char repeated_char, Token_Kind single, Token_Kind repeated, Token_Kind assigned) {
if (lex_match(lex, repeated_char)) return repeated;
if (lex_match(lex, '=')) return assigned;
return single;
}
Token_Kind lex_assign_variant(Lexer *lex, Token_Kind single, Token_Kind assigned) {
return lex_match(lex, '=') ? assigned : single;
}
Token_Kind lex_shift_family(Lexer *lex, char repeated_char, Token_Kind single, Token_Kind single_eq, Token_Kind doubled, Token_Kind doubled_eq) {
if (lex_match(lex, repeated_char)) {
return lex_match(lex, '=') ? doubled_eq : doubled;
}
return lex_match(lex, '=') ? single_eq : single;
}
Token lex_token(Lexer *lex) {
eat_whitespace(lex);
Token t = {
.str = lex->at,
.line = lex->line,
.column = lex->column,
.file = lex->file,
.preproc = lex->preproc,
};
if (lex->at >= lex->end) {
t.kind = TOK_EOF;
t.len = 0;
return t;
}
char c = *lex->at;
lex_advance(lex);
switch (c) {
case 0: t.kind = TOK_EOF; break;
case '(': t.kind = TOK_LPAREN; break;
case ')': t.kind = TOK_RPAREN; break;
case '[': t.kind = TOK_LBRACKET; break;
case ']': t.kind = TOK_RBRACKET; break;
case '{': t.kind = TOK_LBRACE; break;
case '}': t.kind = TOK_RBRACE; break;
case ',': t.kind = TOK_COMMA; break;
case '.': t.kind = TOK_DOT; break;
case ':': t.kind = TOK_COLON; break;
case ';': t.kind = TOK_SEMICOLON; break;
case '?': t.kind = TOK_QUESTION; break;
case '+': t.kind = lex_repeat_or_assign(lex, '+', TOK_PLUS, TOK_INC, TOK_PLUS_ASSIGN); break;
case '-': {
if (lex_match(lex, '-')) t.kind = TOK_DEC;
else if (lex_match(lex, '=')) t.kind = TOK_MINUS_ASSIGN;
else if (lex_match(lex, '>')) t.kind = TOK_ARROW;
else t.kind = TOK_MINUS;
} break;
case '*': t.kind = lex_assign_variant(lex, TOK_STAR, TOK_MUL_ASSIGN); break;
case '/': t.kind = lex_assign_variant(lex, TOK_SLASH, TOK_DIV_ASSIGN); break;
case '%': t.kind = lex_assign_variant(lex, TOK_PERCENT, TOK_MOD_ASSIGN); break;
case '=': t.kind = lex_assign_variant(lex, TOK_ASSIGN, TOK_EQ); break;
case '<': t.kind = lex_shift_family(lex, '<', TOK_LT, TOK_LEQ, TOK_LSHIFT, TOK_LSHIFT_ASSIGN); break;
case '>': t.kind = lex_shift_family(lex, '>', TOK_GT, TOK_GEQ, TOK_RSHIFT, TOK_RSHIFT_ASSIGN); break;
case '!': t.kind = lex_assign_variant(lex, TOK_NOT, TOK_NEQ); break;
case '~': t.kind = TOK_BITNOT; break;
case '&': t.kind = lex_repeat_or_assign(lex, '&', TOK_BITAND, TOK_AND, TOK_AND_ASSIGN); break;
case '|': t.kind = lex_repeat_or_assign(lex, '|', TOK_BITOR, TOK_OR, TOK_OR_ASSIGN); break;
case '^': t.kind = lex_assign_variant(lex, TOK_BITXOR, TOK_XOR_ASSIGN); break;
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': {
t.kind = TOK_INT;
while (lex->at < lex->end && isdigit(*lex->at)) {
lex_advance(lex);
}
t.u = strtoull(t.str, NULL, 10);
} break;
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j':
case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't':
case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J':
case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T':
case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
case '_': {
t.kind = TOK_IDENT;
while (lex->at < lex->end && (isalnum(*lex->at) || *lex->at == '_')) {
lex_advance(lex);
}
} break;
case '#': {
t.kind = TOK_HASH;
lex->preproc = t.preproc = true;
while (lex->at < lex->end && isalpha(*lex->at)) {
lex_advance(lex);
}
} break;
default: panicf("unrecognized character: '%c', can't match with any of the token kinds", c);
}
t.len = (int)(lex->at - t.str);
if (t.kind == TOK_IDENT) {
t.intern = make_intern(t.str, t.len);
if (lex_is_keyword(t.intern)) {
if (t.intern == keyword_while) t.kind = TOK_while;
if (t.intern == keyword_break) t.kind = TOK_break;
if (t.intern == keyword_case) t.kind = TOK_case;
if (t.intern == keyword_char) t.kind = TOK_char;
if (t.intern == keyword_const) t.kind = TOK_const;
if (t.intern == keyword_continue) t.kind = TOK_continue;
if (t.intern == keyword_default) t.kind = TOK_default;
if (t.intern == keyword_do) t.kind = TOK_do;
if (t.intern == keyword_double) t.kind = TOK_double;
if (t.intern == keyword_else) t.kind = TOK_else;
if (t.intern == keyword_enum) t.kind = TOK_enum;
if (t.intern == keyword_extern) t.kind = TOK_extern;
if (t.intern == keyword_float) t.kind = TOK_float;
if (t.intern == keyword_for) t.kind = TOK_for;
if (t.intern == keyword_goto) t.kind = TOK_goto;
if (t.intern == keyword_if) t.kind = TOK_if;
if (t.intern == keyword_inline) t.kind = TOK_inline;
if (t.intern == keyword_int) t.kind = TOK_int;
if (t.intern == keyword_long) t.kind = TOK_long;
if (t.intern == keyword_register) t.kind = TOK_register;
if (t.intern == keyword_restrict) t.kind = TOK_restrict;
if (t.intern == keyword_return) t.kind = TOK_return;
if (t.intern == keyword_short) t.kind = TOK_short;
if (t.intern == keyword_signed) t.kind = TOK_signed;
if (t.intern == keyword_sizeof) t.kind = TOK_sizeof;
if (t.intern == keyword_static) t.kind = TOK_static;
if (t.intern == keyword_struct) t.kind = TOK_struct;
if (t.intern == keyword_switch) t.kind = TOK_switch;
if (t.intern == keyword_typedef) t.kind = TOK_typedef;
if (t.intern == keyword_union) t.kind = TOK_union;
if (t.intern == keyword_unsigned) t.kind = TOK_unsigned;
if (t.intern == keyword_void) t.kind = TOK_void;
if (t.intern == keyword_volatile) t.kind = TOK_volatile;
if (t.intern == keyword_auto) t.kind = TOK_auto;
}
}
return t;
}
Token_Array lex_file(char *file, char *src, int len) {
Lexer lex = make_lexer(file, src, len);
Token_Array result = {0};
for (;;) {
Token token = lex_token(&lex);
vec_push(&result, token);
if (token.kind == TOK_EOF) {
break;
}
}
return result;
}
void assert_token(Token t, Token_Kind kind, char *text, int line, int column) {
assert(t.kind == kind);
assert(t.line == line);
assert(t.column == column);
assert(t.len == (int)strlen(text));
assert(strncmp(t.str, text, t.len) == 0);
}
void lex_test(void) {
char *src = "12 + 34 * 6\n- 7 % 2 / 1 == 1 != 2 <= 3 >= 4 && 3 || 4 << 1 >> 2";
Lexer lex = make_lexer("test.c", src, (int)strlen(src));
assert_token(lex_token(&lex), TOK_INT, "12", 0, 0);
assert_token(lex_token(&lex), TOK_PLUS, "+", 0, 3);
assert_token(lex_token(&lex), TOK_INT, "34", 0, 5);
assert_token(lex_token(&lex), TOK_STAR, "*", 0, 10);
assert_token(lex_token(&lex), TOK_INT, "6", 0, 12);
assert_token(lex_token(&lex), TOK_MINUS, "-", 1, 0);
assert_token(lex_token(&lex), TOK_INT, "7", 1, 2);
assert_token(lex_token(&lex), TOK_PERCENT, "%", 1, 4);
assert_token(lex_token(&lex), TOK_INT, "2", 1, 6);
assert_token(lex_token(&lex), TOK_SLASH, "/", 1, 8);
assert_token(lex_token(&lex), TOK_INT, "1", 1, 10);
assert_token(lex_token(&lex), TOK_EQ, "==", 1, 12);
assert_token(lex_token(&lex), TOK_INT, "1", 1, 15);
assert_token(lex_token(&lex), TOK_NEQ, "!=", 1, 17);
assert_token(lex_token(&lex), TOK_INT, "2", 1, 20);
assert_token(lex_token(&lex), TOK_LEQ, "<=", 1, 22);
assert_token(lex_token(&lex), TOK_INT, "3", 1, 25);
assert_token(lex_token(&lex), TOK_GEQ, ">=", 1, 27);
assert_token(lex_token(&lex), TOK_INT, "4", 1, 30);
assert_token(lex_token(&lex), TOK_AND, "&&", 1, 32);
assert_token(lex_token(&lex), TOK_INT, "3", 1, 35);
assert_token(lex_token(&lex), TOK_OR, "||", 1, 37);
assert_token(lex_token(&lex), TOK_INT, "4", 1, 40);
assert_token(lex_token(&lex), TOK_LSHIFT, "<<", 1, 42);
assert_token(lex_token(&lex), TOK_INT, "1", 1, 45);
assert_token(lex_token(&lex), TOK_RSHIFT, ">>", 1, 47);
assert_token(lex_token(&lex), TOK_INT, "2", 1, 50);
Token eof = lex_token(&lex);
assert(eof.kind == TOK_EOF);
assert(eof.len == 0);
assert(eof.line == 1);
assert(eof.column == 51);
Token_Array array = lex_file("test.c", src, (int)strlen(src));
assert(array.len == 28);
char *intern_a = make_intern("hello", 5);
char *intern_b = make_intern("hello", 5);
char *intern_c = make_intern("world", 5);
assert(strcmp(intern_a, "hello") == 0);
assert(strcmp(intern_b, "hello") == 0);
assert(strcmp(intern_c, "world") == 0);
assert(intern_a == intern_b);
assert(intern_a != intern_c);
char *ident_src = "foo _bar baz123 if for while if_ x9";
Lexer ident_lex = make_lexer("ident_test.c", ident_src, (int)strlen(ident_src));
Token foo = lex_token(&ident_lex);
assert_token(foo, TOK_IDENT, "foo", 0, 0);
assert(strcmp(foo.intern, "foo") == 0);
Token bar = lex_token(&ident_lex);
assert_token(bar, TOK_IDENT, "_bar", 0, 4);
assert(strcmp(bar.intern, "_bar") == 0);
Token baz123 = lex_token(&ident_lex);
assert_token(baz123, TOK_IDENT, "baz123", 0, 9);
assert(strcmp(baz123.intern, "baz123") == 0);
Token kw_if = lex_token(&ident_lex);
assert_token(kw_if, TOK_if, "if", 0, 16);
Token kw_for = lex_token(&ident_lex);
assert_token(kw_for, TOK_for, "for", 0, 19);
Token kw_while = lex_token(&ident_lex);
assert_token(kw_while, TOK_while, "while", 0, 23);
Token ident_if_ = lex_token(&ident_lex);
assert_token(ident_if_, TOK_IDENT, "if_", 0, 29);
Token ident_x9 = lex_token(&ident_lex);
assert_token(ident_x9, TOK_IDENT, "x9", 0, 33);
printf("lexer tests passed\n");
}

32
a/main.c Normal file
View File

@@ -0,0 +1,32 @@
/*
- [ ] Compile simple int main program
- [x] Lex identifiers, keywords
- [ ] Add parsing of this
- [ ] Emit it
- [ ] Print error tokens location properly in lexer / parser and make it easy (not from source code)
- [ ] New line splicing, first source preprocessing stage. In order to properly handle '\\' backslash new line, we most likely need to preprocess the source in a initial pass. So at some point we need to introduce a stage that will create a buffer without wrong characters with a line / column mapping data structure.
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include "base.c"
#include "meta_gen.c"
#include "lex.c"
#include "ast.c"
#include "parser.c"
#include "emit_asm_x64.c"
int main() {
lex_init_keywords();
vec_test();
lex_test();
parser_test();
emit_x64_test();
}

170
a/meta.c Normal file
View File

@@ -0,0 +1,170 @@
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "base.c"
int main() {
typedef struct {
char *name;
char *serialized_operator;
bool keyword;
} Task;
Task kinds[] = {
{"EOF"},
{"ERROR"},
{"IDENT"},
{"INT"},
{"FLOAT"},
{"CHAR"},
{"STRING"},
{"LPAREN", .serialized_operator = "("},
{"RPAREN", .serialized_operator = ")"},
{"LBRACKET", .serialized_operator = "["},
{"RBRACKET", .serialized_operator = "]"},
{"LBRACE", .serialized_operator = "{"},
{"RBRACE", .serialized_operator = "}"},
{"COMMA", .serialized_operator = ","},
{"DOT", .serialized_operator = "."},
{"ARROW", .serialized_operator = "->"},
{"ELLIPSIS", .serialized_operator = "..."},
{"COLON", .serialized_operator = ":"},
{"SEMICOLON", .serialized_operator = ";"},
{"QUESTION", .serialized_operator = "?"},
{"HASH", .serialized_operator = "#"},
{"HASHHASH", .serialized_operator = "##"},
{"PLUS", .serialized_operator = "+"},
{"MINUS", .serialized_operator = "-"},
{"STAR", .serialized_operator = "*"},
{"SLASH", .serialized_operator = "/"},
{"PERCENT", .serialized_operator = "%"},
{"INC", .serialized_operator = "++"},
{"DEC", .serialized_operator = "--"},
{"ASSIGN", .serialized_operator = "="},
{"PLUS_ASSIGN", .serialized_operator = "+="},
{"MINUS_ASSIGN", .serialized_operator = "-="},
{"MUL_ASSIGN", .serialized_operator = "*="},
{"DIV_ASSIGN", .serialized_operator = "/="},
{"MOD_ASSIGN", .serialized_operator = "%="},
{"LSHIFT_ASSIGN", .serialized_operator = "<<="},
{"RSHIFT_ASSIGN", .serialized_operator = ">>="},
{"AND_ASSIGN", .serialized_operator = "&="},
{"XOR_ASSIGN", .serialized_operator = "^="},
{"OR_ASSIGN", .serialized_operator = "|="},
{"EQ", .serialized_operator = "=="},
{"NEQ", .serialized_operator = "!="},
{"LT", .serialized_operator = "<"},
{"LEQ", .serialized_operator = "<="},
{"GT", .serialized_operator = ">"},
{"GEQ", .serialized_operator = ">="},
{"NOT", .serialized_operator = "!"},
{"BITNOT", .serialized_operator = "~"},
{"BITAND", .serialized_operator = "&"},
{"BITOR", .serialized_operator = "|"},
{"BITXOR", .serialized_operator = "^"},
{"AND", .serialized_operator = "&&"},
{"OR", .serialized_operator = "||"},
{"LSHIFT", .serialized_operator = "<<"},
{"RSHIFT", .serialized_operator = ">>"},
{"auto", .keyword = true},
{"break", .keyword = true},
{"case", .keyword = true},
{"char", .keyword = true},
{"const", .keyword = true},
{"continue", .keyword = true},
{"default", .keyword = true},
{"do", .keyword = true},
{"double", .keyword = true},
{"else", .keyword = true},
{"enum", .keyword = true},
{"extern", .keyword = true},
{"float", .keyword = true},
{"for", .keyword = true},
{"goto", .keyword = true},
{"if", .keyword = true},
{"inline", .keyword = true},
{"int", .keyword = true},
{"long", .keyword = true},
{"register", .keyword = true},
{"restrict", .keyword = true},
{"return", .keyword = true},
{"short", .keyword = true},
{"signed", .keyword = true},
{"sizeof", .keyword = true},
{"static", .keyword = true},
{"struct", .keyword = true},
{"switch", .keyword = true},
{"typedef", .keyword = true},
{"union", .keyword = true},
{"unsigned", .keyword = true},
{"void", .keyword = true},
{"volatile", .keyword = true},
{"while", .keyword = true},
};
printf("// auto generated by meta.c\n");
printf("typedef enum {\n");
for (int i = 0; i < ilen(kinds); i += 1) {
printf(" TOK_%s,\n", kinds[i].name);
}
printf("} Token_Kind;\n");
printf("char *token_to_op(Token_Kind kind) {\n");
printf(" switch (kind) {\n");
for (int i = 0; i < ilen(kinds); i += 1) {
if (kinds[i].serialized_operator) {
printf(" case TOK_%s: return \"%s\";\n", kinds[i].name, kinds[i].serialized_operator);
}
}
printf(" default: return 0;\n");
printf(" }\n");
printf("}\n");
printf("char *token_to_name(Token_Kind kind) {\n");
printf(" switch (kind) {\n");
for (int i = 0; i < ilen(kinds); i += 1) {
if (kinds[i].name) {
printf(" case TOK_%s: return \"%s\";\n", kinds[i].name, kinds[i].name);
}
}
printf(" default: return \"<invalid-token-kind>\";\n");
printf(" }\n");
printf("}\n");
{
printf("// \n");
printf("// KEYWORDS\n");
printf("// \n");
for (int i = 0; i < ilen(kinds); i += 1) {
if (kinds[i].keyword) {
printf("char *keyword_%s;\n", kinds[i].name);
}
}
Task *first = NULL;
Task *last = NULL;
printf("char *lex_first_keyword = NULL;\n");
printf("char *lex_last_keyword = NULL;\n");
printf("char *make_intern(char *string, int len);\n");
printf("#define lex_add_keyword(x) make_intern(x, ilen(x) - 1)\n");
printf("void lex_init_keywords(void) {\n");
for (int i = 0; i < ilen(kinds); i += 1) {
if (kinds[i].keyword) {
if (!first) first = kinds + i;
last = kinds + i;
printf(" keyword_%s = lex_add_keyword(\"%s\");\n", kinds[i].name, kinds[i].name);
}
}
printf("#define TOK_FIRST_KEYWORD TOK_%s\n", first->name);
printf("#define TOK_LAST_KEYWORD TOK_%s\n", last->name);
printf(" lex_first_keyword = keyword_%s;\n", first->name);
printf(" lex_last_keyword = keyword_%s;\n", last->name);
printf("}\n");
}
}

320
a/meta_gen.c Normal file
View File

@@ -0,0 +1,320 @@
// auto generated by meta.c
typedef enum {
TOK_EOF,
TOK_ERROR,
TOK_IDENT,
TOK_INT,
TOK_FLOAT,
TOK_CHAR,
TOK_STRING,
TOK_LPAREN,
TOK_RPAREN,
TOK_LBRACKET,
TOK_RBRACKET,
TOK_LBRACE,
TOK_RBRACE,
TOK_COMMA,
TOK_DOT,
TOK_ARROW,
TOK_ELLIPSIS,
TOK_COLON,
TOK_SEMICOLON,
TOK_QUESTION,
TOK_HASH,
TOK_HASHHASH,
TOK_PLUS,
TOK_MINUS,
TOK_STAR,
TOK_SLASH,
TOK_PERCENT,
TOK_INC,
TOK_DEC,
TOK_ASSIGN,
TOK_PLUS_ASSIGN,
TOK_MINUS_ASSIGN,
TOK_MUL_ASSIGN,
TOK_DIV_ASSIGN,
TOK_MOD_ASSIGN,
TOK_LSHIFT_ASSIGN,
TOK_RSHIFT_ASSIGN,
TOK_AND_ASSIGN,
TOK_XOR_ASSIGN,
TOK_OR_ASSIGN,
TOK_EQ,
TOK_NEQ,
TOK_LT,
TOK_LEQ,
TOK_GT,
TOK_GEQ,
TOK_NOT,
TOK_BITNOT,
TOK_BITAND,
TOK_BITOR,
TOK_BITXOR,
TOK_AND,
TOK_OR,
TOK_LSHIFT,
TOK_RSHIFT,
TOK_auto,
TOK_break,
TOK_case,
TOK_char,
TOK_const,
TOK_continue,
TOK_default,
TOK_do,
TOK_double,
TOK_else,
TOK_enum,
TOK_extern,
TOK_float,
TOK_for,
TOK_goto,
TOK_if,
TOK_inline,
TOK_int,
TOK_long,
TOK_register,
TOK_restrict,
TOK_return,
TOK_short,
TOK_signed,
TOK_sizeof,
TOK_static,
TOK_struct,
TOK_switch,
TOK_typedef,
TOK_union,
TOK_unsigned,
TOK_void,
TOK_volatile,
TOK_while,
} Token_Kind;
char *token_to_op(Token_Kind kind) {
switch (kind) {
case TOK_LPAREN: return "(";
case TOK_RPAREN: return ")";
case TOK_LBRACKET: return "[";
case TOK_RBRACKET: return "]";
case TOK_LBRACE: return "{";
case TOK_RBRACE: return "}";
case TOK_COMMA: return ",";
case TOK_DOT: return ".";
case TOK_ARROW: return "->";
case TOK_ELLIPSIS: return "...";
case TOK_COLON: return ":";
case TOK_SEMICOLON: return ";";
case TOK_QUESTION: return "?";
case TOK_HASH: return "#";
case TOK_HASHHASH: return "##";
case TOK_PLUS: return "+";
case TOK_MINUS: return "-";
case TOK_STAR: return "*";
case TOK_SLASH: return "/";
case TOK_PERCENT: return "%";
case TOK_INC: return "++";
case TOK_DEC: return "--";
case TOK_ASSIGN: return "=";
case TOK_PLUS_ASSIGN: return "+=";
case TOK_MINUS_ASSIGN: return "-=";
case TOK_MUL_ASSIGN: return "*=";
case TOK_DIV_ASSIGN: return "/=";
case TOK_MOD_ASSIGN: return "%=";
case TOK_LSHIFT_ASSIGN: return "<<=";
case TOK_RSHIFT_ASSIGN: return ">>=";
case TOK_AND_ASSIGN: return "&=";
case TOK_XOR_ASSIGN: return "^=";
case TOK_OR_ASSIGN: return "|=";
case TOK_EQ: return "==";
case TOK_NEQ: return "!=";
case TOK_LT: return "<";
case TOK_LEQ: return "<=";
case TOK_GT: return ">";
case TOK_GEQ: return ">=";
case TOK_NOT: return "!";
case TOK_BITNOT: return "~";
case TOK_BITAND: return "&";
case TOK_BITOR: return "|";
case TOK_BITXOR: return "^";
case TOK_AND: return "&&";
case TOK_OR: return "||";
case TOK_LSHIFT: return "<<";
case TOK_RSHIFT: return ">>";
default: return 0;
}
}
char *token_to_name(Token_Kind kind) {
switch (kind) {
case TOK_EOF: return "EOF";
case TOK_ERROR: return "ERROR";
case TOK_IDENT: return "IDENT";
case TOK_INT: return "INT";
case TOK_FLOAT: return "FLOAT";
case TOK_CHAR: return "CHAR";
case TOK_STRING: return "STRING";
case TOK_LPAREN: return "LPAREN";
case TOK_RPAREN: return "RPAREN";
case TOK_LBRACKET: return "LBRACKET";
case TOK_RBRACKET: return "RBRACKET";
case TOK_LBRACE: return "LBRACE";
case TOK_RBRACE: return "RBRACE";
case TOK_COMMA: return "COMMA";
case TOK_DOT: return "DOT";
case TOK_ARROW: return "ARROW";
case TOK_ELLIPSIS: return "ELLIPSIS";
case TOK_COLON: return "COLON";
case TOK_SEMICOLON: return "SEMICOLON";
case TOK_QUESTION: return "QUESTION";
case TOK_HASH: return "HASH";
case TOK_HASHHASH: return "HASHHASH";
case TOK_PLUS: return "PLUS";
case TOK_MINUS: return "MINUS";
case TOK_STAR: return "STAR";
case TOK_SLASH: return "SLASH";
case TOK_PERCENT: return "PERCENT";
case TOK_INC: return "INC";
case TOK_DEC: return "DEC";
case TOK_ASSIGN: return "ASSIGN";
case TOK_PLUS_ASSIGN: return "PLUS_ASSIGN";
case TOK_MINUS_ASSIGN: return "MINUS_ASSIGN";
case TOK_MUL_ASSIGN: return "MUL_ASSIGN";
case TOK_DIV_ASSIGN: return "DIV_ASSIGN";
case TOK_MOD_ASSIGN: return "MOD_ASSIGN";
case TOK_LSHIFT_ASSIGN: return "LSHIFT_ASSIGN";
case TOK_RSHIFT_ASSIGN: return "RSHIFT_ASSIGN";
case TOK_AND_ASSIGN: return "AND_ASSIGN";
case TOK_XOR_ASSIGN: return "XOR_ASSIGN";
case TOK_OR_ASSIGN: return "OR_ASSIGN";
case TOK_EQ: return "EQ";
case TOK_NEQ: return "NEQ";
case TOK_LT: return "LT";
case TOK_LEQ: return "LEQ";
case TOK_GT: return "GT";
case TOK_GEQ: return "GEQ";
case TOK_NOT: return "NOT";
case TOK_BITNOT: return "BITNOT";
case TOK_BITAND: return "BITAND";
case TOK_BITOR: return "BITOR";
case TOK_BITXOR: return "BITXOR";
case TOK_AND: return "AND";
case TOK_OR: return "OR";
case TOK_LSHIFT: return "LSHIFT";
case TOK_RSHIFT: return "RSHIFT";
case TOK_auto: return "auto";
case TOK_break: return "break";
case TOK_case: return "case";
case TOK_char: return "char";
case TOK_const: return "const";
case TOK_continue: return "continue";
case TOK_default: return "default";
case TOK_do: return "do";
case TOK_double: return "double";
case TOK_else: return "else";
case TOK_enum: return "enum";
case TOK_extern: return "extern";
case TOK_float: return "float";
case TOK_for: return "for";
case TOK_goto: return "goto";
case TOK_if: return "if";
case TOK_inline: return "inline";
case TOK_int: return "int";
case TOK_long: return "long";
case TOK_register: return "register";
case TOK_restrict: return "restrict";
case TOK_return: return "return";
case TOK_short: return "short";
case TOK_signed: return "signed";
case TOK_sizeof: return "sizeof";
case TOK_static: return "static";
case TOK_struct: return "struct";
case TOK_switch: return "switch";
case TOK_typedef: return "typedef";
case TOK_union: return "union";
case TOK_unsigned: return "unsigned";
case TOK_void: return "void";
case TOK_volatile: return "volatile";
case TOK_while: return "while";
default: return "<invalid-token-kind>";
}
}
//
// KEYWORDS
//
char *keyword_auto;
char *keyword_break;
char *keyword_case;
char *keyword_char;
char *keyword_const;
char *keyword_continue;
char *keyword_default;
char *keyword_do;
char *keyword_double;
char *keyword_else;
char *keyword_enum;
char *keyword_extern;
char *keyword_float;
char *keyword_for;
char *keyword_goto;
char *keyword_if;
char *keyword_inline;
char *keyword_int;
char *keyword_long;
char *keyword_register;
char *keyword_restrict;
char *keyword_return;
char *keyword_short;
char *keyword_signed;
char *keyword_sizeof;
char *keyword_static;
char *keyword_struct;
char *keyword_switch;
char *keyword_typedef;
char *keyword_union;
char *keyword_unsigned;
char *keyword_void;
char *keyword_volatile;
char *keyword_while;
char *lex_first_keyword = NULL;
char *lex_last_keyword = NULL;
char *make_intern(char *string, int len);
#define lex_add_keyword(x) make_intern(x, ilen(x) - 1)
void lex_init_keywords(void) {
keyword_auto = lex_add_keyword("auto");
keyword_break = lex_add_keyword("break");
keyword_case = lex_add_keyword("case");
keyword_char = lex_add_keyword("char");
keyword_const = lex_add_keyword("const");
keyword_continue = lex_add_keyword("continue");
keyword_default = lex_add_keyword("default");
keyword_do = lex_add_keyword("do");
keyword_double = lex_add_keyword("double");
keyword_else = lex_add_keyword("else");
keyword_enum = lex_add_keyword("enum");
keyword_extern = lex_add_keyword("extern");
keyword_float = lex_add_keyword("float");
keyword_for = lex_add_keyword("for");
keyword_goto = lex_add_keyword("goto");
keyword_if = lex_add_keyword("if");
keyword_inline = lex_add_keyword("inline");
keyword_int = lex_add_keyword("int");
keyword_long = lex_add_keyword("long");
keyword_register = lex_add_keyword("register");
keyword_restrict = lex_add_keyword("restrict");
keyword_return = lex_add_keyword("return");
keyword_short = lex_add_keyword("short");
keyword_signed = lex_add_keyword("signed");
keyword_sizeof = lex_add_keyword("sizeof");
keyword_static = lex_add_keyword("static");
keyword_struct = lex_add_keyword("struct");
keyword_switch = lex_add_keyword("switch");
keyword_typedef = lex_add_keyword("typedef");
keyword_union = lex_add_keyword("union");
keyword_unsigned = lex_add_keyword("unsigned");
keyword_void = lex_add_keyword("void");
keyword_volatile = lex_add_keyword("volatile");
keyword_while = lex_add_keyword("while");
#define TOK_FIRST_KEYWORD TOK_auto
#define TOK_LAST_KEYWORD TOK_while
lex_first_keyword = keyword_auto;
lex_last_keyword = keyword_while;
}

211
a/parser.c Normal file
View File

@@ -0,0 +1,211 @@
typedef struct Parser {
Token *at;
Token *end;
} Parser;
Token *next_token(Parser *p) {
if (p->at < p->end) {
return p->at++;
}
return p->at;
}
Token *match_token(Parser *p, Token_Kind kind) {
if (p->at->kind == kind) {
return next_token(p);
}
return NULL;
}
bool is_keyword(Token *token) {
bool result = (token->kind >= TOK_FIRST_KEYWORD && token->kind <= TOK_LAST_KEYWORD);
return result;
}
Token *match_keyword(Parser *p, char *keyword) {
if (is_keyword(p->at) && p->at->intern == keyword) {
return next_token(p);
}
return NULL;
}
Token *expect_token(Parser *p, Token_Kind kind) {
if (p->at->kind == kind) {
return next_token(p);
}
panicf("expected token kind: %s, got instead: %s", token_to_name(p->at->kind), token_to_name(kind));
}
Ast *parse_expr(Parser *p, int power_of_binding_to_right);
Ast *parse_atom(Parser *p) {
Token *token = p->at;
Ast *n = NULL;
if (match_token(p, TOK_INT)) {
n = create_ast(token, AST_INT);
n->u = token->u;
} else if (match_token(p, TOK_LPAREN)) {
// @todo: do a comma list here
n = parse_expr(p, 0);
expect_token(p, TOK_RPAREN);
} else {
panicf("unknown token in %s. %.*s (%s/%d), ", __FUNCTION__, token->len, token->str, token_to_name(token->kind), token->kind);
}
return n;
}
int get_binding_power(Token *tok) {
switch (tok->kind) {
case TOK_STAR: case TOK_SLASH: case TOK_PERCENT: return 120;
case TOK_PLUS: case TOK_MINUS: return 110;
case TOK_LSHIFT: case TOK_RSHIFT: return 100;
case TOK_LT: case TOK_LEQ: case TOK_GT: case TOK_GEQ: return 90;
case TOK_EQ: case TOK_NEQ: return 80;
case TOK_BITAND: return 70;
case TOK_BITXOR: return 60;
case TOK_BITOR: return 50;
case TOK_AND: return 40;
case TOK_OR: return 30;
default: return 0;
}
}
Ast *parse_valid_left_binding(Parser *p, Token *tok, Ast *left) {
switch (tok->kind) {
case TOK_PLUS: case TOK_MINUS: case TOK_STAR: case TOK_SLASH: case TOK_PERCENT:
case TOK_EQ: case TOK_NEQ: case TOK_LT: case TOK_LEQ: case TOK_GT: case TOK_GEQ: case TOK_BITAND:
case TOK_BITOR: case TOK_BITXOR: case TOK_AND: case TOK_OR: case TOK_LSHIFT: case TOK_RSHIFT: {
return create_binary_expr(tok, tok->kind, left, parse_expr(p, get_binding_power(tok)));
} break;
default: panicf("unknown token in %s. %.*s (%s/%d), ", __FUNCTION__, tok->len, tok->str, token_to_name(tok->kind), tok->kind);
}
return NULL;
}
Ast *parse_expr(Parser *p, int power_of_binding_to_right) {
Ast *n = parse_atom(p);
while (get_binding_power(p->at) > power_of_binding_to_right) {
Token *tok = next_token(p);
n = parse_valid_left_binding(p, tok, n);
}
return n;
}
Type *parse_declspec(Parser *p) {
if (match_token(p, TOK_int)) {
return type_int;
} else {
panicf("%s:%d: error: unknown token while parsing declspec", p->at->file, p->at->line);
}
}
Ast *parse_program(Parser *p) {
Ast *result = create_ast(p->at, AST_PROGRAM);
while (p->at->kind != TOK_EOF) {
Type *type = parse_declspec(p);
}
return result;
}
int64_t eval_expr(Ast *n) {
switch (n->kind) {
case AST_INT: return (int64_t)n->u;
case AST_BINARY: {
int64_t left = eval_expr(n->l);
int64_t right = eval_expr(n->r);
switch (n->op) {
case TOK_PLUS: return left + right;
case TOK_MINUS: return left - right;
case TOK_STAR: return left * right;
case TOK_SLASH: return left / right;
case TOK_PERCENT: return left % right;
case TOK_EQ: return left == right;
case TOK_NEQ: return left != right;
case TOK_LT: return left < right;
case TOK_LEQ: return left <= right;
case TOK_GT: return left > right;
case TOK_GEQ: return left >= right;
case TOK_BITAND: return left & right;
case TOK_BITOR: return left | right;
case TOK_BITXOR: return left ^ right;
case TOK_AND: return left && right;
case TOK_OR: return left || right;
case TOK_LSHIFT: return left << right;
case TOK_RSHIFT: return left >> right;
default: panicf("invalid token kind in eval_expr, binary");
}
} break;
default: panicf("invalid ast kind in eval_expr");
}
}
void print_expr(Ast *n) {
switch (n->kind) {
case AST_INT: printf("%lu", n->u); break;
case AST_BINARY: {
print_expr(n->l);
printf(" %s ", token_to_op(n->op));
print_expr(n->r);
} break;
default: panicf("encountered invalid ast kind in %s of kind: %d\n", __FUNCTION__, n->kind);
}
}
void parser_test(void) {
#define TEST_EVAL(expr) do { \
Token_Array tokens = lex_file("eval_test", (#expr), strlen((#expr)));\
Parser p = {tokens.data, tokens.data + tokens.len};\
Ast *result = parse_expr(&p, 0);\
int64_t left = eval_expr(result);\
int64_t right = (expr);\
if (left != right) {\
printf("%s:%d expected: %ld, got: %ld\n expression: ", __FILE__, __LINE__, left, right);\
print_expr(result);\
printf("\n");\
}\
} while (0)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wparentheses"
TEST_EVAL(32+5-4);
TEST_EVAL(16/2/2);
TEST_EVAL(9*4/6);
TEST_EVAL(17%5);
TEST_EVAL(5125-42|(4&3)^2|2%1242);
TEST_EVAL((45%2)^(23&3));
TEST_EVAL(1<16*2);
TEST_EVAL(16>1+2);
TEST_EVAL(4<=2+2);
TEST_EVAL(5>=2+2);
TEST_EVAL(4==2+2);
TEST_EVAL(5!=2+2);
TEST_EVAL(1+1+1+1+2-2-3-4-5);
TEST_EVAL(5%2^5&6|3);
TEST_EVAL(6&3);
TEST_EVAL(6|3);
TEST_EVAL(6^3);
TEST_EVAL(1&&2);
TEST_EVAL(0||3);
TEST_EVAL(1||0&&0);
TEST_EVAL(8<<2);
TEST_EVAL(32>>3);
TEST_EVAL((2+3)*(4+5));
TEST_EVAL(9>3&1);
TEST_EVAL(8|1<4);
TEST_EVAL(7<=3+4);
TEST_EVAL(8>=2*4);
TEST_EVAL(4==2+2);
TEST_EVAL(5!=2+2);
TEST_EVAL(1&&2);
TEST_EVAL(0||3);
TEST_EVAL(1||0&&0);
TEST_EVAL(8<<2);
TEST_EVAL(32>>3);
TEST_EVAL(1+2<<3);
TEST_EVAL(16>>1+1);
#pragma clang diagnostic pop
printf("parser tests passed\n");
}