make parser and sema work

This commit is contained in:
sfja 2026-10-20 01:27:50 +02:00
parent 8309384c0b
commit e45e72249f
16 changed files with 709 additions and 580 deletions

View File

@ -1,5 +1,13 @@
larpcc: main.c lex.c preproc.c collection.c parse.c ast_print.c resolve.c sema.c CFLAGS := -std=c23 -pedantic -Wall -Wextra -g -Werror=switch
gcc $^ -o $@ -std=c23 -pedantic -Wall -Wextra -g -fsanitize=address -Werror=switch
# CFLAGS += -fsanitize=address
c_files := $(wildcard *.c)
h_files := $(wildcard *.h)
larpcc: $(c_files) $(h_files)
gcc $(c_files) -o $@ -std=c23 -pedantic -Wall -Wextra -g -Werror=switch

12
ast.h
View File

@ -2,13 +2,9 @@
#define AST_H #define AST_H
#include "collection.h" #include "collection.h"
#include "diagnostics.h"
#include <stdio.h> #include <stdio.h>
typedef struct {
int line;
int col;
} Loc;
typedef struct AstStmt AstStmt; typedef struct AstStmt AstStmt;
typedef struct AstDecl AstDecl; typedef struct AstDecl AstDecl;
typedef struct AstDeclar AstDeclar; typedef struct AstDeclar AstDeclar;
@ -19,6 +15,7 @@ typedef struct AstStmtBlock AstStmtBlock;
struct AstStmtBlock { struct AstStmtBlock {
ARRAY_FIELD(AstStmt*, stmts); ARRAY_FIELD(AstStmt*, stmts);
Loc loc; Loc loc;
int id;
}; };
typedef struct AstDeclBlock AstDeclBlock; typedef struct AstDeclBlock AstDeclBlock;
@ -36,6 +33,7 @@ typedef enum {
struct AstStmt { struct AstStmt {
AstStmtTy ty; AstStmtTy ty;
Loc loc; Loc loc;
int id;
union { union {
AstDecl* decl; AstDecl* decl;
AstExpr* expr; AstExpr* expr;
@ -63,9 +61,11 @@ struct AstDecl {
ARRAY_FIELD(AstDeclar*, declars); ARRAY_FIELD(AstDeclar*, declars);
AstStmtBlock* function_body; AstStmtBlock* function_body;
Loc loc; Loc loc;
int id;
}; };
typedef enum { typedef enum {
AstDeclar_Anon,
AstDeclar_Ident, AstDeclar_Ident,
AstDeclar_Pointer, AstDeclar_Pointer,
AstDeclar_Array, AstDeclar_Array,
@ -75,6 +75,7 @@ typedef enum {
struct AstDeclar { struct AstDeclar {
AstDeclarTy ty; AstDeclarTy ty;
Loc loc; Loc loc;
int id;
AstExpr* init_expr; AstExpr* init_expr;
union { union {
char* ident; char* ident;
@ -102,6 +103,7 @@ typedef enum {
struct AstExpr { struct AstExpr {
AstExprTy ty; AstExprTy ty;
Loc loc; Loc loc;
int id;
union { union {
char* ident; char* ident;
char* str_val; char* str_val;

View File

@ -137,6 +137,10 @@ void ast_decl_print(FILE* f, const AstDecl* node, int depth)
void ast_declar_print(FILE* f, const AstDeclar* node, int depth) void ast_declar_print(FILE* f, const AstDeclar* node, int depth)
{ {
switch (node->ty) { switch (node->ty) {
case AstDeclar_Anon: {
fprintf(f, "Declar::Anon %d:%d", node->loc.line, node->loc.col);
return;
}
case AstDeclar_Ident: { case AstDeclar_Ident: {
fprintf( fprintf(
f, "Declar::Ident %d:%d {\n", node->loc.line, node->loc.col); f, "Declar::Ident %d:%d {\n", node->loc.line, node->loc.col);
@ -329,6 +333,7 @@ void ast_declar_visit(AstVisitor* v, AstDeclar* node)
void ast_declar_visit_body(AstVisitor* v, AstDeclar* node) void ast_declar_visit_body(AstVisitor* v, AstDeclar* node)
{ {
switch (node->ty) { switch (node->ty) {
case AstDeclar_Anon:
case AstDeclar_Ident: case AstDeclar_Ident:
break; break;
case AstDeclar_Pointer: case AstDeclar_Pointer:

View File

@ -240,7 +240,7 @@ struct KeymapEntry {
void keymap_init(Keymap* m, size_t elem_size) void keymap_init(Keymap* m, size_t elem_size)
{ {
size_t entry_size = sizeof(KeymapEntry) + (elem_size + 7ul & ~7ul); size_t entry_size = sizeof(KeymapEntry) + ((elem_size + 7ul) & ~7ul);
*m = (Keymap) { *m = (Keymap) {
ARRAY_FIELD_INIT(entries), ARRAY_FIELD_INIT(entries),

18
diagnostics.c Normal file
View File

@ -0,0 +1,18 @@
#include "diagnostics.h"
#include <stdarg.h>
#include <stdio.h>
void diag_error(Loc loc, const char* fmt, ...)
{
va_list va;
va_start(va, fmt);
fprintf(stderr,
"\x1b[1;97m%s:%d:%d: \x1b[1;91merror: \x1b[0m\n",
loc.file,
loc.line,
loc.col);
vfprintf(stderr, fmt, va);
va_end(va);
}

12
diagnostics.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef DIAGNOSTICS_H
#define DIAGNOSTICS_H
typedef struct {
int line;
int col;
const char* file;
} Loc;
void diag_error(Loc loc, const char* fmt, ...);
#endif

View File

@ -1,6 +1,6 @@
#ifndef NOT_STDIO_H #ifndef NOT_STDIO_H
#define NOT_STDIO_H #define NOT_STDIO_H
int puts(const char* __s); int puts(const char*);
#endif #endif

296
lex.c
View File

@ -1,296 +0,0 @@
#include "collection.h"
#include "parse.h"
#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* read_textfile(const char* filename)
{
FILE* fp = fopen(filename, "r");
if (!fp) {
goto couldnt_read_file;
}
if (fseek(fp, 0, SEEK_END) != 0) {
goto couldnt_read_file;
}
long file_size = ftell(fp);
if (file_size < 0) {
goto couldnt_read_file;
}
if (fseek(fp, 0, SEEK_SET) != 0) {
goto couldnt_read_file;
}
char* buffer = malloc(file_size + 1);
assert(buffer);
if (fread(buffer, sizeof(char), file_size, fp) != (size_t)file_size) {
goto couldnt_read_file;
}
buffer[file_size] = '\0';
return buffer;
couldnt_read_file:
fprintf(stderr,
"error: couldn't read file '%s': %s\n",
filename,
strerror(errno));
return nullptr;
}
const char* tokty_str(TokTy ty)
{
switch (ty) {
case Tok_Eof:
return "Eof";
case Tok_Ident:
return "Ident";
case Tok_Int:
return "Int";
case Tok_Str:
return "Str";
case Tok_Char:
return "Char";
case Tok_Whitespace:
return "Whitespace";
case Tok_HeaderName:
return "HeaderName";
case Tok_Eol:
return "Eol";
case Tok_Semicolon:
return "Semicolon";
case Tok_LParen:
return "LParen";
case Tok_RParen:
return "RParen";
case Tok_LBrace:
return "LBrace";
case Tok_RBrace:
return "RBrace";
case Tok_Asterisk:
return "Asterisk";
case Tok_Hashtag:
return "Hashtag";
case Tok_Comma:
return "Comma";
case Tok_KwInt:
return "KwInt";
case Tok_KwChar:
return "KwChar";
case Tok_KwConst:
return "KwConst";
case Tok_KwVoid:
return "KwVoid";
case Tok_EndOfMacroInvocation:
return "EndOfMacroInvocation";
}
return "<unknown>";
}
void source_iter_init(SourceIter* r, const char* text)
{
*r = (SourceIter) {
.text = text,
.i = 0,
.line = 1,
.col = 1,
};
}
Char source_iter_next(SourceIter* r)
{
char ch = r->text[r->i];
Loc loc = { r->line, r->col };
if (r->text[r->i] != '\0') {
if (ch == '\n') {
r->line += 1;
r->col = 1;
} else {
r->col += 1;
}
r->i += 1;
}
return (Char) { ch, loc };
}
void lsl_iter_init(LslIter* r, SourceIter* source_char_iter)
{
Char curr = source_iter_next(source_char_iter);
*r = (LslIter) {
.source_char_iter = source_char_iter,
.curr = curr,
};
}
Char lsl_iter_next(LslIter* r)
{
Char next = source_iter_next(r->source_char_iter);
if (r->curr.ch == '\\' && next.ch == '\n') {
r->curr = source_iter_next(r->source_char_iter);
return lsl_iter_next(r);
}
Char curr = r->curr;
r->curr = next;
return curr;
}
void lexer_init(Lexer* r, LslIter* lsl_char_iter, Arena* arena)
{
Char curr = lsl_iter_next(lsl_char_iter);
*r = (Lexer) {
.lsl_iter = lsl_char_iter,
.arena = arena,
.curr = curr,
.lex_whitespace = false,
.lex_headername = false,
};
}
static Tok tok(Lexer* l, TokTy ty, const char* text, Loc loc)
{
char* text_dup = arena_alloc(l->arena, strlen(text) + 1);
text_dup[0] = '\0';
strcpy(text_dup, text);
return (Tok) { ty, text_dup, loc };
}
static bool done(Lexer* l)
{
return l->curr.ch == '\0';
}
constexpr size_t max_token_text_len = 64;
static void step(Lexer* l, char text[], size_t* text_len)
{
assert(*text_len < max_token_text_len);
text[*text_len] = l->curr.ch;
*text_len += 1;
l->curr = lsl_iter_next(l->lsl_iter);
}
Tok lexer_next(Lexer* l)
{
Loc loc = l->curr.loc;
char text[max_token_text_len] = "";
size_t text_len = 0;
if (done(l)) {
return tok(l, Tok_Eof, text, loc);
}
bool matched = false;
while (strchr(" \t\r", l->curr.ch)) {
matched = true;
step(l, text, &text_len);
}
if (matched) {
if (l->lex_whitespace) {
return tok(l, Tok_Whitespace, text, loc);
}
return lexer_next(l);
}
while ((l->curr.ch >= 'a' && l->curr.ch <= 'z')
|| (l->curr.ch >= 'A' && l->curr.ch <= 'Z')
|| (matched && l->curr.ch >= '0' && l->curr.ch <= '9')
|| strchr("_", l->curr.ch)) {
matched = true;
step(l, text, &text_len);
}
if (matched) {
return tok(l, Tok_Ident, text, loc);
}
while ((l->curr.ch >= '1' && l->curr.ch <= '9')
|| (matched && l->curr.ch == '0')) {
matched = true;
step(l, text, &text_len);
}
if (matched) {
return tok(l, Tok_Int, text, loc);
}
if (l->lex_headername && (l->curr.ch == '"' || l->curr.ch == '<')) {
char ter = l->curr.ch == '<' ? '>' : '"';
step(l, text, &text_len);
while (l->curr.ch != '\0' && l->curr.ch != '\n' && l->curr.ch != ter) {
step(l, text, &text_len);
}
if (l->curr.ch != ter) {
fprintf(stderr,
"error: malformed header name, expected '%c' on line %d\n",
ter,
l->curr.loc.line);
return lexer_next(l);
}
step(l, text, &text_len);
return tok(l, Tok_HeaderName, text, loc);
}
if (l->curr.ch == '"') {
step(l, text, &text_len);
while (l->curr.ch != '"') {
if (l->curr.ch == '\\') {
step(l, text, &text_len);
if (done(l))
break;
}
step(l, text, &text_len);
}
if (done(l) || l->curr.ch != '"') {
fprintf(stderr,
"error: malformed string literal on line %d\n",
l->curr.loc.line);
return lexer_next(l);
}
step(l, text, &text_len);
return tok(l, Tok_Str, text, loc);
}
if (l->curr.ch == '\'') {
step(l, text, &text_len);
if (l->curr.ch == '\\') {
step(l, text, &text_len);
}
if (!done(l)) {
step(l, text, &text_len);
}
if (done(l) && l->curr.ch != '\'') {
fprintf(stderr,
"error: malformed character literal on line %d\n",
l->curr.loc.line);
return lexer_next(l);
}
step(l, text, &text_len);
return tok(l, Tok_Char, text, loc);
}
if (strchr("\n;(){},*#", l->curr.ch)) {
TokTy ty = (TokTy)l->curr.ch;
step(l, text, &text_len);
return tok(l, ty, text, loc);
}
fprintf(stderr,
"error: illegal character '%c' on line %d\n",
l->curr.ch,
l->curr.loc.line);
step(l, text, &text_len);
return lexer_next(l);
}

13
main.c
View File

@ -1,7 +1,7 @@
#include "ast.h" #include "ast.h"
#include "collection.h" #include "collection.h"
#include "parse.h" #include "parse.h"
#include "resolve.h" #include "preproc.h"
#include "sema.h" #include "sema.h"
#include <assert.h> #include <assert.h>
#include <stddef.h> #include <stddef.h>
@ -36,7 +36,7 @@ int main(int argc, char* argv[])
printf("--- tokens ---\n"); printf("--- tokens ---\n");
Loc loc = { 1, 1 }; Loc loc = { 1, 1, nullptr };
Tok tok; Tok tok;
while ((tok = tokstream_next(&tokstream)).ty != Tok_Eof) { while ((tok = tokstream_next(&tokstream)).ty != Tok_Eof) {
// printf("%s\t%s\n", tokty_str(tok.ty), tok.text); // printf("%s\t%s\n", tokty_str(tok.ty), tok.text);
@ -79,15 +79,16 @@ int main(int argc, char* argv[])
printf("--- ast ---\n"); printf("--- ast ---\n");
Parser* parser = parser_new(&tokstream, sema, &ast_arena); Parser parser;
parser_init(&parser, &tokstream, sema, &ast_arena);
// Arena sym_arena; // Arena sym_arena;
// arena_init(&sym_arena, 4096); // arena_init(&sym_arena, 4096);
// Resolver* resolver = resolver_new(&sym_arena); // Resolver* resolver = resolver_new(&sym_arena);
AstStmt* stmt; AstStmt* stmt;
while (!parser_done(parser)) { while (!parser_done(&parser)) {
stmt = parser_parse_stmt(parser); stmt = parser_parse_stmt(&parser);
ast_stmt_print(stdout, stmt, 0); ast_stmt_print(stdout, stmt, 0);
@ -98,7 +99,7 @@ int main(int argc, char* argv[])
// resolver_free(resolver); // resolver_free(resolver);
parser_free(parser); parser_deinit(&parser);
preproc_free(preproc); preproc_free(preproc);
arena_deinit(&tok_arena); arena_deinit(&tok_arena);

143
parse.c
View File

@ -9,23 +9,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
typedef struct {
char ch;
bool is_term;
unsigned short val;
} KwNodeOpt;
typedef struct {
ARRAY_FIELD(KwNodeOpt, opts);
size_t idx;
} KwNode;
typedef struct {
TokStream* tokstream;
Arena arena;
ARRAY_FIELD(KwNode, nodes);
} KwMatcher;
static KwNodeOpt* kwmatcher_create_path( static KwNodeOpt* kwmatcher_create_path(
KwMatcher* m, size_t node_idx, const char* kw) KwMatcher* m, size_t node_idx, const char* kw)
{ {
@ -97,7 +80,7 @@ static TokTy kwmatcher_match(KwMatcher* m, const char* kw)
return opt->val; return opt->val;
} }
static void kwmatcher_init(KwMatcher* m, TokStream* tokstream) void kwmatcher_init(KwMatcher* m, TokStream* tokstream)
{ {
*m = (KwMatcher) { *m = (KwMatcher) {
.tokstream = tokstream, .tokstream = tokstream,
@ -124,12 +107,12 @@ static void kwmatcher_init(KwMatcher* m, TokStream* tokstream)
} }
} }
static void kwmatcher_deinit(KwMatcher* m) void kwmatcher_deinit(KwMatcher* m)
{ {
arena_deinit(&m->arena); arena_deinit(&m->arena);
} }
static Tok kwmatcher_next(KwMatcher* m) Tok kwmatcher_next(KwMatcher* m)
{ {
Tok tok = tokstream_next(m->tokstream); Tok tok = tokstream_next(m->tokstream);
if (tok.ty != Tok_Ident) if (tok.ty != Tok_Ident)
@ -138,42 +121,30 @@ static Tok kwmatcher_next(KwMatcher* m)
tok.ty = ty; tok.ty = ty;
exit: exit:
printf(" - %-10s %-10s %d:%d\n", // printf(" - %-10s %-10s %d:%d\n",
tokty_str(tok.ty), // tokty_str(tok.ty),
tok.text, // tok.text,
tok.loc.line, // tok.loc.line,
tok.loc.col); // tok.loc.col);
return tok; return tok;
} }
typedef enum : unsigned int { static AstStmtBlock* parse_stmt_block(Parser* p);
ParserFlags_ParsingTypedef = 1 << 0, static AstStmt* parse_stmt(Parser* p);
} ParserFlags; static AstDecl* parse_decl(Parser* p, bool is_param);
static AstDeclar* parse_declar(Parser* p, bool allow_anon);
static AstSpec parse_decl_spec(Parser* p);
static AstExpr* parse_expr(Parser* p);
struct Parser { void parser_init(Parser* p, TokStream* tokstream, Sema* sema, Arena* arena)
KwMatcher kwmatcher;
Sema* sema;
Arena* arena;
Arena sym_arena;
Syms* syms;
Tok curr;
ParserFlags flags;
};
Parser* parser_new(TokStream* tokstream, Sema* sema, Arena* arena)
{ {
Parser* p = malloc(sizeof(Parser));
*p = (Parser) { *p = (Parser) {
.kwmatcher = { }, .kwmatcher = { },
.sema = sema, .sema = sema,
.arena = arena, .arena = arena,
.sym_arena = { }, .sym_arena = { },
.syms = syms_new(), .old_syms = oldsyms_new(),
.curr = { }, .curr = { },
@ -182,15 +153,22 @@ Parser* parser_new(TokStream* tokstream, Sema* sema, Arena* arena)
kwmatcher_init(&p->kwmatcher, tokstream); kwmatcher_init(&p->kwmatcher, tokstream);
p->curr = kwmatcher_next(&p->kwmatcher); p->curr = kwmatcher_next(&p->kwmatcher);
return p;
} }
void parser_free(Parser* p) void parser_deinit(Parser* p)
{ {
kwmatcher_deinit(&p->kwmatcher); kwmatcher_deinit(&p->kwmatcher);
syms_free(p->syms); oldsyms_free(p->old_syms);
free(p); }
AstStmt* parser_parse_stmt(Parser* p)
{
return parse_stmt(p);
}
bool parser_done(Parser* p)
{
return p->curr.ty == Tok_Eof;
} }
static void step(Parser* p) static void step(Parser* p)
@ -203,6 +181,8 @@ static void step(Parser* p)
{ \ { \
TYPE* val = arena_alloc(p->arena, sizeof(TYPE)); \ TYPE* val = arena_alloc(p->arena, sizeof(TYPE)); \
*val = init; \ *val = init; \
static int ids = 0; \
val->id = ids++; \
POST; \ POST; \
return val; \ return val; \
} }
@ -214,13 +194,6 @@ DEFINE_NEW(AstExpr, expr, { sema_visit_expr(p->sema, val); })
DEFINE_NEW(AstStmtBlock, stmt_block, { }) DEFINE_NEW(AstStmtBlock, stmt_block, { })
static AstStmtBlock* parse_stmt_block(Parser* p);
static AstStmt* parse_stmt(Parser* p);
static AstDecl* parse_decl(Parser* p, bool is_param);
static AstDeclar* parse_declar(Parser* p);
static AstSpec parse_decl_spec(Parser* p);
static AstExpr* parse_expr(Parser* p);
static AstStmtBlock* parse_stmt_block(Parser* p) static AstStmtBlock* parse_stmt_block(Parser* p)
{ {
Loc loc = p->curr.loc; Loc loc = p->curr.loc;
@ -265,7 +238,7 @@ static bool is_defty_ident(Parser* p, Tok tok)
{ {
if (tok.ty != Tok_Ident) if (tok.ty != Tok_Ident)
return false; return false;
OldSym* sym = syms_get(p->syms, tok.text); OldSym* sym = oldsyms_get(p->old_syms, tok.text);
return sym && sym->ty == OldSym_Typedef; return sym && sym->ty == OldSym_Typedef;
} }
@ -319,7 +292,7 @@ static AstDecl* parse_decl(Parser* p, bool is_param)
ARRAY_VAR(AstDeclar*, declars); ARRAY_VAR(AstDeclar*, declars);
AstDeclar* first = parse_declar(p); AstDeclar* first = parse_declar(p, is_param);
ARENA_ARRAY_VAR_PUSH(p->arena, declars, &first); ARENA_ARRAY_VAR_PUSH(p->arena, declars, &first);
@ -338,7 +311,7 @@ static AstDecl* parse_decl(Parser* p, bool is_param)
while (multi_allowed && p->curr.ty == ',') { while (multi_allowed && p->curr.ty == ',') {
step(p); step(p);
AstDeclar* declar = parse_declar(p); AstDeclar* declar = parse_declar(p, false);
ARENA_ARRAY_VAR_PUSH(p->arena, declars, &declar); ARENA_ARRAY_VAR_PUSH(p->arena, declars, &declar);
} }
@ -356,14 +329,14 @@ static AstDecl* parse_decl(Parser* p, bool is_param)
}); });
} }
static AstDeclar* parse_declar_l1(Parser* p); static AstDeclar* parse_declar_l1(Parser* p, bool allow_anon);
static AstDeclar* parse_declar_l2(Parser* p); static AstDeclar* parse_declar_l2(Parser* p, bool allow_anon);
static AstDeclar* parse_declar_l3(Parser* p); static AstDeclar* parse_declar_l3(Parser* p, bool allow_anon);
static AstDeclar* parse_declar_l4(Parser* p); static AstDeclar* parse_declar_l4(Parser* p, bool allow_anon);
static AstDeclar* parse_declar(Parser* p) static AstDeclar* parse_declar(Parser* p, bool allow_anon)
{ {
AstDeclar* declar = parse_declar_l1(p); AstDeclar* declar = parse_declar_l1(p, allow_anon);
if (p->curr.ty == '=') { if (p->curr.ty == '=') {
step(p); step(p);
@ -374,7 +347,7 @@ static AstDeclar* parse_declar(Parser* p)
return declar; return declar;
} }
static AstDeclar* parse_declar_l1(Parser* p) static AstDeclar* parse_declar_l1(Parser* p, bool allow_anon)
{ {
Loc loc = p->curr.loc; Loc loc = p->curr.loc;
@ -383,7 +356,7 @@ static AstDeclar* parse_declar_l1(Parser* p)
AstSpec spec = parse_decl_spec(p); AstSpec spec = parse_decl_spec(p);
AstDeclar* declar = parse_declar_l1(p); AstDeclar* declar = parse_declar_l1(p, allow_anon);
return new_declar(p, return new_declar(p,
(AstDeclar) { (AstDeclar) {
.ty = AstDeclar_Pointer, .ty = AstDeclar_Pointer,
@ -395,14 +368,14 @@ static AstDeclar* parse_declar_l1(Parser* p)
}); });
} }
return parse_declar_l2(p); return parse_declar_l2(p, allow_anon);
} }
static AstDeclar* parse_declar_l2(Parser* p) static AstDeclar* parse_declar_l2(Parser* p, bool allow_anon)
{ {
Loc loc = p->curr.loc; Loc loc = p->curr.loc;
AstDeclar* declar = parse_declar_l3(p); AstDeclar* declar = parse_declar_l3(p, allow_anon);
while (p->curr.ty == '[') { while (p->curr.ty == '[') {
step(p); step(p);
@ -430,11 +403,11 @@ static AstDeclar* parse_declar_l2(Parser* p)
return declar; return declar;
} }
static AstDeclar* parse_declar_l3(Parser* p) static AstDeclar* parse_declar_l3(Parser* p, bool allow_anon)
{ {
Loc loc = p->curr.loc; Loc loc = p->curr.loc;
AstDeclar* declar = parse_declar_l4(p); AstDeclar* declar = parse_declar_l4(p, allow_anon);
if (p->curr.ty == '(') { if (p->curr.ty == '(') {
step(p); step(p);
@ -478,10 +451,18 @@ static AstDeclar* parse_declar_l3(Parser* p)
return declar; return declar;
} }
static AstDeclar* parse_declar_l4(Parser* p) static AstDeclar* parse_declar_l4(Parser* p, bool allow_anon)
{ {
Loc loc = p->curr.loc; Loc loc = p->curr.loc;
if (allow_anon && (p->curr.ty == ',' || p->curr.ty == ')')) {
return new_declar(p,
(AstDeclar) {
.ty = AstDeclar_Anon,
.loc = loc,
});
}
if (p->curr.ty == Tok_Ident) { if (p->curr.ty == Tok_Ident) {
char* ident = arena_strdup(p->arena, p->curr.text); char* ident = arena_strdup(p->arena, p->curr.text);
step(p); step(p);
@ -496,7 +477,7 @@ static AstDeclar* parse_declar_l4(Parser* p)
if (p->curr.ty == '(') { if (p->curr.ty == '(') {
step(p); step(p);
AstDeclar* declar = parse_declar_l1(p); AstDeclar* declar = parse_declar_l1(p, allow_anon);
if (p->curr.ty != ')') { if (p->curr.ty != ')') {
fprintf( fprintf(
@ -520,7 +501,7 @@ static AstSpec parse_decl_spec(Parser* p)
while (true) { while (true) {
switch (p->curr.ty) { switch (p->curr.ty) {
case Tok_Ident: { case Tok_Ident: {
OldSym* sym = syms_get(p->syms, p->curr.text); OldSym* sym = oldsyms_get(p->old_syms, p->curr.text);
if (!sym || sym->ty != OldSym_Typedef) if (!sym || sym->ty != OldSym_Typedef)
break; break;
spec |= AstSpec_DefTy; spec |= AstSpec_DefTy;
@ -686,13 +667,3 @@ static AstExpr* parse_expr_2(Parser* p)
stderr, "error: expected expression on line %d\n", p->curr.loc.line); stderr, "error: expected expression on line %d\n", p->curr.loc.line);
exit(1); exit(1);
} }
AstStmt* parser_parse_stmt(Parser* p)
{
return parse_stmt(p);
}
bool parser_done(Parser* p)
{
return p->curr.ty == Tok_Eof;
}

119
parse.h
View File

@ -1,116 +1,51 @@
#ifndef PARSE_H #ifndef PARSE_H
#define PARSE_H #define PARSE_H
#include "ast.h"
#include "collection.h" #include "collection.h"
#include "preproc.h"
#include "resolve.h"
#include "sema.h" #include "sema.h"
#include <stddef.h>
char* read_textfile(const char* filename);
// phases:
// https://en.cppreference.com/c/language/translation_phases
// phase 1 - source chars
typedef struct { typedef struct {
char ch; char ch;
Loc loc; bool is_term;
} Char; unsigned short val;
} KwNodeOpt;
typedef struct { typedef struct {
const char* text; ARRAY_FIELD(KwNodeOpt, opts);
size_t i; size_t idx;
int line; } KwNode;
int col;
} SourceIter;
void source_iter_init(SourceIter* r, const char* text);
Char source_iter_next(SourceIter* r);
// phase 2 - logical source lines
typedef struct { typedef struct {
SourceIter* source_char_iter; TokStream* tokstream;
Char curr; Arena arena;
} LslIter; ARRAY_FIELD(KwNode, nodes);
} KwMatcher;
void lsl_iter_init(LslIter* r, SourceIter* source_iter); void kwmatcher_init(KwMatcher* m, TokStream* tokstream);
Char lsl_iter_next(LslIter* r); void kwmatcher_deinit(KwMatcher* m);
Tok kwmatcher_next(KwMatcher* m);
// phase 3 - preprocessor tokens typedef enum : unsigned int {
ParserFlags_ParsingTypedef = 1 << 0,
} ParserFlags;
typedef enum { typedef struct Parser {
Tok_Eof, KwMatcher kwmatcher;
Tok_Ident, Sema* sema;
Tok_Int,
Tok_Str,
Tok_Char,
Tok_Whitespace,
Tok_HeaderName,
Tok_Eol = '\n', // NOTE: has value 10
Tok_Semicolon = ';',
Tok_LParen = '(',
Tok_RParen = ')',
Tok_LBrace = '{',
Tok_RBrace = '}',
Tok_Comma = ',',
Tok_Asterisk = '*',
Tok_Hashtag = '#',
Tok_KwInt = 256,
Tok_KwChar,
Tok_KwConst,
Tok_KwVoid,
// used internally in preprocessor
Tok_EndOfMacroInvocation,
} TokTy;
const char* tokty_str(TokTy ty);
typedef struct {
TokTy ty;
char* text;
Loc loc;
} Tok;
typedef struct {
LslIter* lsl_iter;
Arena* arena; Arena* arena;
Char curr; Arena sym_arena;
bool lex_whitespace;
bool lex_headername;
} Lexer;
void lexer_init(Lexer* r, LslIter* lsl_iter, Arena* arena); OldSyms* old_syms;
Tok lexer_next(Lexer* l);
// phase 4 - preprocessor
typedef struct Preproc Preproc;
Preproc* preproc_new(char* filename, char* text, Arena* arena);
void preproc_free(Preproc* p);
Tok preproc_next(Preproc* p);
// phase 5, 6 - preprocessor cleanup, concat strings
typedef struct {
Preproc* preproc;
Arena* arena;
Tok curr; Tok curr;
} TokStream;
void tokstream_init(TokStream* s, Preproc* preproc, Arena* arena); ParserFlags flags;
Tok tokstream_next(TokStream* s); } Parser;
typedef struct Parser Parser; void parser_init(Parser* p, TokStream* tokstream, Sema* sema, Arena* arena);
void parser_deinit(Parser* p);
Parser* parser_new(TokStream* tokstream, Sema* sema, Arena* arena);
void parser_free(Parser* p);
AstStmt* parser_parse_stmt(Parser* p); AstStmt* parser_parse_stmt(Parser* p);
bool parser_done(Parser* p); bool parser_done(Parser* p);

372
preproc.c
View File

@ -1,11 +1,301 @@
#include "preproc.h"
#include "collection.h" #include "collection.h"
#include "parse.h"
#include <assert.h> #include <assert.h>
#include <errno.h>
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
char* read_textfile(const char* filename)
{
FILE* fp = fopen(filename, "r");
if (!fp) {
goto couldnt_read_file;
}
if (fseek(fp, 0, SEEK_END) != 0) {
goto couldnt_read_file;
}
long file_size = ftell(fp);
if (file_size < 0) {
goto couldnt_read_file;
}
if (fseek(fp, 0, SEEK_SET) != 0) {
goto couldnt_read_file;
}
char* buffer = malloc(file_size + 1);
assert(buffer);
if (fread(buffer, sizeof(char), file_size, fp) != (size_t)file_size) {
goto couldnt_read_file;
}
buffer[file_size] = '\0';
return buffer;
couldnt_read_file:
fprintf(stderr,
"error: couldn't read file '%s': %s\n",
filename,
strerror(errno));
return nullptr;
}
const char* tokty_str(TokTy ty)
{
switch (ty) {
case Tok_Eof:
return "Eof";
case Tok_Ident:
return "Ident";
case Tok_Int:
return "Int";
case Tok_Str:
return "Str";
case Tok_Char:
return "Char";
case Tok_Whitespace:
return "Whitespace";
case Tok_HeaderName:
return "HeaderName";
case Tok_Eol:
return "Eol";
case Tok_Semicolon:
return "Semicolon";
case Tok_LParen:
return "LParen";
case Tok_RParen:
return "RParen";
case Tok_LBrace:
return "LBrace";
case Tok_RBrace:
return "RBrace";
case Tok_Asterisk:
return "Asterisk";
case Tok_Hashtag:
return "Hashtag";
case Tok_Comma:
return "Comma";
case Tok_KwInt:
return "KwInt";
case Tok_KwChar:
return "KwChar";
case Tok_KwConst:
return "KwConst";
case Tok_KwVoid:
return "KwVoid";
case Tok_EndOfMacroInvocation:
return "EndOfMacroInvocation";
}
return "<unknown>";
}
void source_iter_init(SourceIter* r, const char* filename, const char* text)
{
*r = (SourceIter) {
.filename = filename,
.text = text,
.i = 0,
.line = 1,
.col = 1,
};
}
Char source_iter_next(SourceIter* r)
{
char ch = r->text[r->i];
Loc loc = { r->line, r->col, r->filename };
if (r->text[r->i] != '\0') {
if (ch == '\n') {
r->line += 1;
r->col = 1;
} else {
r->col += 1;
}
r->i += 1;
}
return (Char) { ch, loc };
}
void lsl_iter_init(LslIter* r, SourceIter* source_char_iter)
{
Char curr = source_iter_next(source_char_iter);
*r = (LslIter) {
.source_char_iter = source_char_iter,
.curr = curr,
};
}
Char lsl_iter_next(LslIter* r)
{
Char next = source_iter_next(r->source_char_iter);
if (r->curr.ch == '\\' && next.ch == '\n') {
r->curr = source_iter_next(r->source_char_iter);
return lsl_iter_next(r);
}
Char curr = r->curr;
r->curr = next;
return curr;
}
void lexer_init(Lexer* r, LslIter* lsl_char_iter, Arena* arena)
{
Char curr = lsl_iter_next(lsl_char_iter);
*r = (Lexer) {
.lsl_iter = lsl_char_iter,
.arena = arena,
.curr = curr,
.lex_whitespace = false,
.lex_headername = false,
};
}
static Tok l_tok(Lexer* l, TokTy ty, const char* text, Loc loc)
{
char* text_dup = arena_alloc(l->arena, strlen(text) + 1);
text_dup[0] = '\0';
strcpy(text_dup, text);
return (Tok) { ty, text_dup, loc };
}
static bool l_done(Lexer* l)
{
return l->curr.ch == '\0';
}
constexpr size_t max_token_text_len = 64;
static void l_step(Lexer* l, char text[], size_t* text_len)
{
assert(*text_len < max_token_text_len);
text[*text_len] = l->curr.ch;
*text_len += 1;
l->curr = lsl_iter_next(l->lsl_iter);
}
Tok lexer_next(Lexer* l)
{
Loc loc = l->curr.loc;
char text[max_token_text_len] = "";
size_t text_len = 0;
if (l_done(l)) {
return l_tok(l, Tok_Eof, text, loc);
}
bool matched = false;
while (strchr(" \t\r", l->curr.ch)) {
matched = true;
l_step(l, text, &text_len);
}
if (matched) {
if (l->lex_whitespace) {
return l_tok(l, Tok_Whitespace, text, loc);
}
return lexer_next(l);
}
while ((l->curr.ch >= 'a' && l->curr.ch <= 'z')
|| (l->curr.ch >= 'A' && l->curr.ch <= 'Z')
|| (matched && l->curr.ch >= '0' && l->curr.ch <= '9')
|| strchr("_", l->curr.ch)) {
matched = true;
l_step(l, text, &text_len);
}
if (matched) {
return l_tok(l, Tok_Ident, text, loc);
}
while ((l->curr.ch >= '1' && l->curr.ch <= '9')
|| (matched && l->curr.ch == '0')) {
matched = true;
l_step(l, text, &text_len);
}
if (matched) {
return l_tok(l, Tok_Int, text, loc);
}
if (l->lex_headername && (l->curr.ch == '"' || l->curr.ch == '<')) {
char ter = l->curr.ch == '<' ? '>' : '"';
l_step(l, text, &text_len);
while (l->curr.ch != '\0' && l->curr.ch != '\n' && l->curr.ch != ter) {
l_step(l, text, &text_len);
}
if (l->curr.ch != ter) {
fprintf(stderr,
"error: malformed header name, expected '%c' on line %d\n",
ter,
l->curr.loc.line);
return lexer_next(l);
}
l_step(l, text, &text_len);
return l_tok(l, Tok_HeaderName, text, loc);
}
if (l->curr.ch == '"') {
l_step(l, text, &text_len);
while (l->curr.ch != '"') {
if (l->curr.ch == '\\') {
l_step(l, text, &text_len);
if (l_done(l))
break;
}
l_step(l, text, &text_len);
}
if (l_done(l) || l->curr.ch != '"') {
fprintf(stderr,
"error: malformed string literal on line %d\n",
l->curr.loc.line);
return lexer_next(l);
}
l_step(l, text, &text_len);
return l_tok(l, Tok_Str, text, loc);
}
if (l->curr.ch == '\'') {
l_step(l, text, &text_len);
if (l->curr.ch == '\\') {
l_step(l, text, &text_len);
}
if (!l_done(l)) {
l_step(l, text, &text_len);
}
if (l_done(l) && l->curr.ch != '\'') {
fprintf(stderr,
"error: malformed character literal on line %d\n",
l->curr.loc.line);
return lexer_next(l);
}
l_step(l, text, &text_len);
return l_tok(l, Tok_Char, text, loc);
}
if (strchr("\n;(){},*#", l->curr.ch)) {
TokTy ty = (TokTy)l->curr.ch;
l_step(l, text, &text_len);
return l_tok(l, ty, text, loc);
}
fprintf(stderr,
"error: illegal character '%c' on line %d\n",
l->curr.ch,
l->curr.loc.line);
l_step(l, text, &text_len);
return lexer_next(l);
}
typedef struct { typedef struct {
ARRAY_FIELD(Hashmap, maps); ARRAY_FIELD(Hashmap, maps);
} PPSyms; } PPSyms;
@ -73,7 +363,7 @@ typedef struct {
size_t depth; size_t depth;
} CondStack; } CondStack;
void condstack_push(CondStack* s, bool cond) static void condstack_push(CondStack* s, bool cond)
{ {
if (cond) { if (cond) {
s->conds |= 1ul << s->depth; s->conds |= 1ul << s->depth;
@ -83,7 +373,7 @@ void condstack_push(CondStack* s, bool cond)
s->depth += 1; s->depth += 1;
} }
bool condstack_pop(CondStack* s) static bool condstack_pop(CondStack* s)
{ {
assert(s->depth > 0); assert(s->depth > 0);
s->depth -= 1; s->depth -= 1;
@ -151,7 +441,7 @@ Preproc* preproc_new(char* filename, char* text, Arena* arena)
})); }));
File* file = &p->files[0]; File* file = &p->files[0];
source_iter_init(&file->source_iter, text); source_iter_init(&file->source_iter, filename, text);
lsl_iter_init(&file->lsl_iter, &file->source_iter); lsl_iter_init(&file->lsl_iter, &file->source_iter);
lexer_init(&file->lexer, &file->lsl_iter, p->arena); lexer_init(&file->lexer, &file->lsl_iter, p->arena);
p->curr = lexer_next(&file->lexer); p->curr = lexer_next(&file->lexer);
@ -184,7 +474,7 @@ void preproc_free(Preproc* p)
free(p); free(p);
} }
static void step(Preproc* p) static void p_step(Preproc* p)
{ {
p->next_can_be_directive = p->curr.ty == Tok_Eol; p->next_can_be_directive = p->curr.ty == Tok_Eol;
@ -197,7 +487,7 @@ static void step(Preproc* p)
free(curr_file->text); free(curr_file->text);
p->files_size -= 1; p->files_size -= 1;
step(p); p_step(p);
} }
return; return;
@ -207,12 +497,12 @@ static void step(Preproc* p)
if (p->curr.ty == Tok_EndOfMacroInvocation) { if (p->curr.ty == Tok_EndOfMacroInvocation) {
ppsyms_pop_layer(&p->syms); ppsyms_pop_layer(&p->syms);
step(p); p_step(p);
return; return;
} }
} }
static void collect_arg(Preproc* p, size_t def_idx, size_t arg_idx) static void p_collect_arg(Preproc* p, size_t def_idx, size_t arg_idx)
{ {
if (arg_idx >= p->defs[def_idx].params_size) { if (arg_idx >= p->defs[def_idx].params_size) {
fprintf(stderr, fprintf(stderr,
@ -243,7 +533,7 @@ static void collect_arg(Preproc* p, size_t def_idx, size_t arg_idx)
(&p->curr), (&p->curr),
sizeof(*(&p->curr))); sizeof(*(&p->curr)));
step(p); p_step(p);
} }
if (p->curr.ty != ',' && p->curr.ty != ')') { if (p->curr.ty != ',' && p->curr.ty != ')') {
@ -264,21 +554,19 @@ static void collect_arg(Preproc* p, size_t def_idx, size_t arg_idx)
ppsyms_define(&p->syms, p->defs[def_idx].params[arg_idx], p->defs_size - 1); ppsyms_define(&p->syms, p->defs[def_idx].params[arg_idx], p->defs_size - 1);
} }
#define A(B, C) B C static void p_collect_args(Preproc* p, size_t def_idx)
static void collect_args(Preproc* p, size_t def_idx)
{ {
assert(p->curr.ty == '('); assert(p->curr.ty == '(');
step(p); p_step(p);
size_t idx = 0; size_t idx = 0;
if (p->curr.ty != Tok_Eof && p->curr.ty != ')') { if (p->curr.ty != Tok_Eof && p->curr.ty != ')') {
collect_arg(p, def_idx, idx); p_collect_arg(p, def_idx, idx);
idx += 1; idx += 1;
while (p->curr.ty == ',') { while (p->curr.ty == ',') {
step(p); p_step(p);
collect_arg(p, def_idx, idx); p_collect_arg(p, def_idx, idx);
idx += 1; idx += 1;
} }
} }
@ -296,12 +584,12 @@ static void collect_args(Preproc* p, size_t def_idx)
} }
} }
static Tok expand_tok(Preproc* p) static Tok p_expand_tok(Preproc* p)
{ {
Tok tok = p->curr; Tok tok = p->curr;
size_t* def_idx; size_t* def_idx;
if (tok.ty != Tok_Ident || !(def_idx = ppsyms_get(&p->syms, tok.text))) { if (tok.ty != Tok_Ident || !(def_idx = ppsyms_get(&p->syms, tok.text))) {
step(p); p_step(p);
return tok; return tok;
} }
@ -313,11 +601,11 @@ static Tok expand_tok(Preproc* p)
ppsyms_define(&p->syms, tok.text, 0); ppsyms_define(&p->syms, tok.text, 0);
if (p->defs[*def_idx].params) { if (p->defs[*def_idx].params) {
step(p); p_step(p);
// function-like macros without arg-lists are ignored. // function-like macros without arg-lists are ignored.
if (p->curr.ty != '(') if (p->curr.ty != '(')
return tok; return tok;
collect_args(p, *def_idx); p_collect_args(p, *def_idx);
} }
ARRAY_FIELD_PUSH(p, tok_stack, (&(Tok) { .ty = Tok_EndOfMacroInvocation })); ARRAY_FIELD_PUSH(p, tok_stack, (&(Tok) { .ty = Tok_EndOfMacroInvocation }));
@ -329,14 +617,14 @@ static Tok expand_tok(Preproc* p)
} }
// step into the new invocation // step into the new invocation
step(p); p_step(p);
return expand_tok(p); return p_expand_tok(p);
} }
static Tok do_define(Preproc* p) static Tok do_define(Preproc* p)
{ {
step(p); p_step(p);
if (p->curr.ty != Tok_Ident) { if (p->curr.ty != Tok_Ident) {
fprintf(stderr, fprintf(stderr,
"error: expected identifier on line %d\n", "error: expected identifier on line %d\n",
@ -346,13 +634,13 @@ static Tok do_define(Preproc* p)
Tok ident_tok = p->curr; Tok ident_tok = p->curr;
p->files[p->files_size - 1].lexer.lex_whitespace = true; p->files[p->files_size - 1].lexer.lex_whitespace = true;
step(p); p_step(p);
p->files[p->files_size - 1].lexer.lex_whitespace = false; p->files[p->files_size - 1].lexer.lex_whitespace = false;
Def def = { }; Def def = { };
if (p->curr.ty == '(') { if (p->curr.ty == '(') {
step(p); p_step(p);
ARRAY_VAR(char*, params); ARRAY_VAR(char*, params);
@ -361,16 +649,16 @@ static Tok do_define(Preproc* p)
goto error_expected_identifier; goto error_expected_identifier;
ARRAY_VAR_PUSH(params, &p->curr.text); ARRAY_VAR_PUSH(params, &p->curr.text);
step(p); p_step(p);
while (p->curr.ty == ',') { while (p->curr.ty == ',') {
step(p); p_step(p);
if (p->curr.ty != Tok_Ident) if (p->curr.ty != Tok_Ident)
goto error_expected_identifier; goto error_expected_identifier;
ARRAY_VAR_PUSH(params, &p->curr.text); ARRAY_VAR_PUSH(params, &p->curr.text);
step(p); p_step(p);
} }
} }
@ -379,14 +667,14 @@ static Tok do_define(Preproc* p)
stderr, "error: expected ')' on line %d\n", p->curr.loc.line); stderr, "error: expected ')' on line %d\n", p->curr.loc.line);
exit(1); exit(1);
} }
step(p); p_step(p);
def.params = params; def.params = params;
def.params_size = params_size; def.params_size = params_size;
} }
if (p->curr.ty == Tok_Whitespace) if (p->curr.ty == Tok_Whitespace)
step(p); p_step(p);
ARRAY_VAR(Tok, toks); ARRAY_VAR(Tok, toks);
@ -397,7 +685,7 @@ static Tok do_define(Preproc* p)
&toks_size, &toks_size,
(&p->curr), (&p->curr),
sizeof(*(&p->curr))); sizeof(*(&p->curr)));
step(p); p_step(p);
} }
def.toks = toks; def.toks = toks;
@ -416,7 +704,7 @@ error_expected_identifier:
static Tok do_undef(Preproc* p) static Tok do_undef(Preproc* p)
{ {
step(p); p_step(p);
if (p->curr.ty != Tok_Ident) { if (p->curr.ty != Tok_Ident) {
fprintf(stderr, fprintf(stderr,
@ -445,7 +733,7 @@ static Tok do_include(Preproc* p)
File* curr_file = &p->files[p->files_size - 1]; File* curr_file = &p->files[p->files_size - 1];
curr_file->lexer.lex_headername = true; curr_file->lexer.lex_headername = true;
step(p); p_step(p);
curr_file->lexer.lex_headername = false; curr_file->lexer.lex_headername = false;
if (p->curr.ty != Tok_HeaderName) { if (p->curr.ty != Tok_HeaderName) {
@ -456,7 +744,7 @@ static Tok do_include(Preproc* p)
} }
Tok headername_tok = p->curr; Tok headername_tok = p->curr;
step(p); p_step(p);
if (p->curr.ty != Tok_Eof && p->curr.ty != Tok_Eol) { if (p->curr.ty != Tok_Eof && p->curr.ty != Tok_Eol) {
fprintf(stderr, fprintf(stderr,
@ -497,7 +785,7 @@ static Tok do_include(Preproc* p)
})); }));
File* file = &p->files[p->files_size - 1]; File* file = &p->files[p->files_size - 1];
source_iter_init(&file->source_iter, text); source_iter_init(&file->source_iter, filename, text);
lsl_iter_init(&file->lsl_iter, &file->source_iter); lsl_iter_init(&file->lsl_iter, &file->source_iter);
lexer_init(&file->lexer, &file->lsl_iter, p->arena); lexer_init(&file->lexer, &file->lsl_iter, p->arena);
p->curr = lexer_next(&file->lexer); p->curr = lexer_next(&file->lexer);
@ -513,7 +801,7 @@ static Tok do_ifndef(Preproc* p)
{ {
int original_line = p->curr.loc.line; int original_line = p->curr.loc.line;
step(p); p_step(p);
if (p->curr.ty != Tok_Ident) { if (p->curr.ty != Tok_Ident) {
fprintf(stderr, fprintf(stderr,
"error: expected identifier on line %d\n", "error: expected identifier on line %d\n",
@ -521,7 +809,7 @@ static Tok do_ifndef(Preproc* p)
exit(1); exit(1);
} }
Tok ident = p->curr; Tok ident = p->curr;
step(p); p_step(p);
if (p->curr.ty != Tok_Eof && p->curr.ty != Tok_Eol) { if (p->curr.ty != Tok_Eof && p->curr.ty != Tok_Eol) {
fprintf(stderr, fprintf(stderr,
@ -539,10 +827,10 @@ static Tok do_ifndef(Preproc* p)
while (p->curr.ty != Tok_Eof) { while (p->curr.ty != Tok_Eof) {
while (p->curr.ty != Tok_Eof while (p->curr.ty != Tok_Eof
&& (!p->next_can_be_directive || p->curr.ty != '#')) && (!p->next_can_be_directive || p->curr.ty != '#'))
step(p); p_step(p);
if (p->curr.ty == Tok_Eof) if (p->curr.ty == Tok_Eof)
break; break;
step(p); p_step(p);
if (p->curr.ty != Tok_Ident) if (p->curr.ty != Tok_Ident)
continue; continue;
if (strcmp(p->curr.text, "endif") == 0) if (strcmp(p->curr.text, "endif") == 0)
@ -562,7 +850,7 @@ static Tok do_ifndef(Preproc* p)
static Tok do_endif(Preproc* p) static Tok do_endif(Preproc* p)
{ {
step(p); p_step(p);
if (p->curr.ty != Tok_Eof && p->curr.ty != Tok_Eol) { if (p->curr.ty != Tok_Eof && p->curr.ty != Tok_Eol) {
fprintf(stderr, fprintf(stderr,
@ -631,10 +919,10 @@ static Tok interpret_directive(Preproc* p)
Tok preproc_next(Preproc* p) Tok preproc_next(Preproc* p)
{ {
if (p->next_can_be_directive && p->curr.ty == '#') { if (p->next_can_be_directive && p->curr.ty == '#') {
step(p); p_step(p);
return interpret_directive(p); return interpret_directive(p);
} }
return expand_tok(p); return p_expand_tok(p);
} }
static Tok tokstream_next_filtered(TokStream* s) static Tok tokstream_next_filtered(TokStream* s)

110
preproc.h Normal file
View File

@ -0,0 +1,110 @@
#ifndef TOKS_H
#define TOKS_H
#include "collection.h"
#include "diagnostics.h"
#include <stddef.h>
char* read_textfile(const char* filename);
// phases:
// https://en.cppreference.com/c/language/translation_phases
// phase 1 - source chars
typedef struct {
char ch;
Loc loc;
} Char;
typedef struct {
const char* filename;
const char* text;
size_t i;
int line;
int col;
} SourceIter;
void source_iter_init(SourceIter* r, const char* filename, const char* text);
Char source_iter_next(SourceIter* r);
// phase 2 - logical source lines
typedef struct {
SourceIter* source_char_iter;
Char curr;
} LslIter;
void lsl_iter_init(LslIter* r, SourceIter* source_iter);
Char lsl_iter_next(LslIter* r);
// phase 3 - preprocessor tokens
typedef enum {
Tok_Eof,
Tok_Ident,
Tok_Int,
Tok_Str,
Tok_Char,
Tok_Whitespace,
Tok_HeaderName,
Tok_Eol = '\n', // NOTE: has value 10
Tok_Semicolon = ';',
Tok_LParen = '(',
Tok_RParen = ')',
Tok_LBrace = '{',
Tok_RBrace = '}',
Tok_Comma = ',',
Tok_Asterisk = '*',
Tok_Hashtag = '#',
Tok_KwInt = 256,
Tok_KwChar,
Tok_KwConst,
Tok_KwVoid,
// used internally in preprocessor
Tok_EndOfMacroInvocation,
} TokTy;
const char* tokty_str(TokTy ty);
typedef struct {
TokTy ty;
char* text;
Loc loc;
} Tok;
typedef struct {
LslIter* lsl_iter;
Arena* arena;
Char curr;
bool lex_whitespace;
bool lex_headername;
} Lexer;
void lexer_init(Lexer* r, LslIter* lsl_iter, Arena* arena);
Tok lexer_next(Lexer* l);
// phase 4 - preprocessor
typedef struct Preproc Preproc;
Preproc* preproc_new(char* filename, char* text, Arena* arena);
void preproc_free(Preproc* p);
Tok preproc_next(Preproc* p);
// phase 5, 6 - preprocessor cleanup, concat strings
typedef struct {
Preproc* preproc;
Arena* arena;
Tok curr;
} TokStream;
void tokstream_init(TokStream* s, Preproc* preproc, Arena* arena);
Tok tokstream_next(TokStream* s);
#endif

View File

@ -5,14 +5,14 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
struct Syms { struct OldSyms {
ARRAY_FIELD(Hashmap, maps); ARRAY_FIELD(Hashmap, maps);
}; };
Syms* syms_new() OldSyms* oldsyms_new()
{ {
Syms* s = malloc(sizeof(Syms)); OldSyms* s = malloc(sizeof(OldSyms));
*s = (Syms) { *s = (OldSyms) {
ARRAY_FIELD_INIT(maps), ARRAY_FIELD_INIT(maps),
}; };
@ -22,7 +22,7 @@ Syms* syms_new()
return s; return s;
} }
void syms_free(Syms* s) void oldsyms_free(OldSyms* s)
{ {
for (size_t i = 0; i < s->maps_size; ++i) for (size_t i = 0; i < s->maps_size; ++i)
hashmap_deinit(&s->maps[i]); hashmap_deinit(&s->maps[i]);
@ -30,26 +30,26 @@ void syms_free(Syms* s)
free(s); free(s);
} }
void syms_push_scope(Syms* s) void oldsyms_push_scope(OldSyms* s)
{ {
ARRAY_FIELD_PUSH(s, maps, &(Hashmap) { }); ARRAY_FIELD_PUSH(s, maps, &(Hashmap) { });
hashmap_init(&s->maps[s->maps_size - 1], sizeof(OldSym*)); hashmap_init(&s->maps[s->maps_size - 1], sizeof(OldSym*));
} }
void syms_pop_scope(Syms* s) void oldsyms_pop_scope(OldSyms* s)
{ {
assert(s->maps_size > 0); assert(s->maps_size > 0);
hashmap_deinit(&s->maps[s->maps_size - 1]); hashmap_deinit(&s->maps[s->maps_size - 1]);
s->maps_size -= 1; s->maps_size -= 1;
} }
void syms_define(Syms* s, const char* ident, OldSym* sym) void oldsyms_define(OldSyms* s, const char* ident, OldSym* sym)
{ {
Hashmap* m = &s->maps[s->maps_size - 1]; Hashmap* m = &s->maps[s->maps_size - 1];
hashmap_set(m, ident, &sym); hashmap_set(m, ident, &sym);
} }
OldSym* syms_get(Syms* s, const char* ident) OldSym* oldsyms_get(OldSyms* s, const char* ident)
{ {
OldSym** res; OldSym** res;
for (size_t i = 0; i < s->maps_size; ++i) for (size_t i = 0; i < s->maps_size; ++i)
@ -63,9 +63,9 @@ typedef enum : unsigned int {
} ResolverFlags; } ResolverFlags;
struct Resolver { struct Resolver {
Syms* ord_syms; OldSyms* ord_syms;
Syms* tag_syms; OldSyms* tag_syms;
Syms* label_syms; OldSyms* label_syms;
Arena* arena; Arena* arena;
@ -75,13 +75,13 @@ struct Resolver {
ResolverFlags flags; ResolverFlags flags;
}; };
Resolver* resolver_new(Arena* arena) Resolver* oldresolver_new(Arena* arena)
{ {
Resolver* r = malloc(sizeof(Resolver)); Resolver* r = malloc(sizeof(Resolver));
*r = (Resolver) { *r = (Resolver) {
.ord_syms = syms_new(), .ord_syms = oldsyms_new(),
.tag_syms = syms_new(), .tag_syms = oldsyms_new(),
.label_syms = syms_new(), .label_syms = oldsyms_new(),
.arena = arena, .arena = arena,
@ -96,24 +96,24 @@ Resolver* resolver_new(Arena* arena)
return r; return r;
} }
void resolver_free(Resolver* r) void oldresolver_free(Resolver* r)
{ {
keymap_deinit(&r->resol_map); keymap_deinit(&r->resol_map);
syms_free(r->ord_syms); oldsyms_free(r->ord_syms);
syms_free(r->tag_syms); oldsyms_free(r->tag_syms);
syms_free(r->label_syms); oldsyms_free(r->label_syms);
free(r); free(r);
} }
static AstVisitorResult visit_stmt_block(AstVisitor* v, AstStmtBlock* node) static AstVisitorResult visit_stmt_block(AstVisitor* v, AstStmtBlock* node)
{ {
Resolver* r = v->op; Resolver* r = v->op;
syms_push_scope(r->ord_syms); oldsyms_push_scope(r->ord_syms);
ast_stmt_block_visit_body(v, node); ast_stmt_block_visit_body(v, node);
syms_pop_scope(r->ord_syms); oldsyms_pop_scope(r->ord_syms);
return AstVisitor_Break; return AstVisitor_Break;
} }
@ -154,14 +154,14 @@ static AstVisitorResult visit_decl(AstVisitor* v, AstDecl* node)
ast_declar_visit(v, d->function.declar); ast_declar_visit(v, d->function.declar);
syms_push_scope(r->ord_syms); oldsyms_push_scope(r->ord_syms);
for (size_t i = 0; i < d->function.params_size; ++i) for (size_t i = 0; i < d->function.params_size; ++i)
ast_decl_visit(v, d->function.params[i]); ast_decl_visit(v, d->function.params[i]);
ast_stmt_block_visit(v, node->function_body); ast_stmt_block_visit(v, node->function_body);
syms_pop_scope(r->ord_syms); oldsyms_pop_scope(r->ord_syms);
return AstVisitor_Break; return AstVisitor_Break;
} }
@ -175,12 +175,12 @@ static AstVisitorResult visit_declar(AstVisitor* v, AstDeclar* node)
if (node->ty == AstDeclar_Function) { if (node->ty == AstDeclar_Function) {
ast_declar_visit(v, node->function.declar); ast_declar_visit(v, node->function.declar);
syms_push_scope(r->ord_syms); oldsyms_push_scope(r->ord_syms);
for (size_t i = 0; i < node->function.params_size; ++i) for (size_t i = 0; i < node->function.params_size; ++i)
ast_decl_visit(v, node->function.params[i]); ast_decl_visit(v, node->function.params[i]);
syms_pop_scope(r->ord_syms); oldsyms_pop_scope(r->ord_syms);
return AstVisitor_Break; return AstVisitor_Break;
} }
@ -195,7 +195,7 @@ static AstVisitorResult visit_declar(AstVisitor* v, AstDeclar* node)
.declar = node, .declar = node,
}, },
}; };
syms_define(r->ord_syms, node->ident, sym); oldsyms_define(r->ord_syms, node->ident, sym);
return AstVisitor_Break; return AstVisitor_Break;
} }
@ -208,7 +208,7 @@ static AstVisitorResult visit_expr(AstVisitor* v, AstExpr* node)
if (node->ty == AstExpr_Ident) { if (node->ty == AstExpr_Ident) {
OldSym* sym = syms_get(r->ord_syms, node->ident); OldSym* sym = oldsyms_get(r->ord_syms, node->ident);
if (!sym) { if (!sym) {
fprintf(stderr, fprintf(stderr,
"error: use of undeclared '%s' on line %d\n", "error: use of undeclared '%s' on line %d\n",
@ -221,7 +221,7 @@ static AstVisitorResult visit_expr(AstVisitor* v, AstExpr* node)
return AstVisitor_Continue; return AstVisitor_Continue;
} }
void resolver_resolve_top_stmt(Resolver* r, AstStmt* stmt) void oldresolver_resolve_top_stmt(Resolver* r, AstStmt* stmt)
{ {
if (stmt->ty != AstStmt_Decl) { if (stmt->ty != AstStmt_Decl) {
fprintf( fprintf(

View File

@ -19,22 +19,22 @@ typedef struct {
}; };
} OldSym; } OldSym;
typedef struct Syms Syms; typedef struct OldSyms OldSyms;
Syms* syms_new(); OldSyms* oldsyms_new();
void syms_free(Syms* s); void oldsyms_free(OldSyms* s);
void syms_push_scope(Syms* s); void oldsyms_push_scope(OldSyms* s);
void syms_pop_scope(Syms* s); void oldsyms_pop_scope(OldSyms* s);
void syms_define(Syms* s, const char* ident, OldSym* sym); void oldsyms_define(OldSyms* s, const char* ident, OldSym* sym);
OldSym* syms_get(Syms* s, const char* ident); OldSym* oldsyms_get(OldSyms* s, const char* ident);
typedef struct Resolver Resolver; typedef struct Resolver Resolver;
Resolver* resolver_new(Arena* arena); Resolver* oldresolver_new(Arena* arena);
void resolver_free(Resolver* r); void oldresolver_free(Resolver* r);
void resolver_resolve_top_stmt(Resolver* r, AstStmt* stmt); void oldresolver_resolve_top_stmt(Resolver* r, AstStmt* stmt);
#endif #endif

95
sema.c
View File

@ -55,9 +55,21 @@ void type_print(FILE* file, const Type* type)
} }
if (type->ty == Type_Pointer) { if (type->ty == Type_Pointer) {
fprintf(file, " * ");
type_spec_print(file, type->pointer.spec);
type_print(file, type->pointer.inner); type_print(file, type->pointer.inner);
fprintf(file, " *");
type_spec_print(file, type->pointer.spec);
return;
}
if (type->ty == Type_Function) {
type_print(file, type->function.return_type);
fprintf(file, "()(");
for (size_t i = 0; i < type->function.params_size; ++i) {
if (i > 0)
fprintf(file, ", ");
type_print(file, type->function.params[i]);
}
fprintf(file, ")");
return; return;
} }
@ -144,11 +156,16 @@ void sema_scope_leave(Sema* s)
static Sym* get_ord_sym(Sema* s, const char* ident) static Sym* get_ord_sym(Sema* s, const char* ident)
{ {
Sym* sym = nullptr; Sym** sym = nullptr;
for (size_t i = 0; i < s->ribs_size; ++i) for (size_t i = 0; i < s->ribs_size; ++i)
if ((sym = hashmap_get(&s->ribs[s->ribs_size - i - 1].ord_syms, ident))) if ((sym = hashmap_get(&s->ribs[s->ribs_size - i - 1].ord_syms, ident)))
break; break;
return sym; return *sym;
}
static void define_ord_sym(Sema* s, const char* ident, Sym* sym)
{
hashmap_set(&s->ribs[s->ribs_size - 1].ord_syms, ident, &sym);
} }
static void error_if_multi_spec( static void error_if_multi_spec(
@ -227,6 +244,11 @@ static void check_declar(Sema* s,
const char** out_declar_ident) const char** out_declar_ident)
{ {
switch (declar->ty) { switch (declar->ty) {
case AstDeclar_Anon: {
*out_declar_type = type;
*out_declar_ident = nullptr;
return;
}
case AstDeclar_Ident: { case AstDeclar_Ident: {
*out_declar_type = type; *out_declar_type = type;
*out_declar_ident = declar->ident; *out_declar_ident = declar->ident;
@ -284,6 +306,9 @@ static void check_declar(Sema* s,
&s->type_arena, &new_type->function, params, &declar_type); &s->type_arena, &new_type->function, params, &declar_type);
} }
assert(
new_type->function.params_size == declar->function.params_size);
// TODO: handle forward declarations // TODO: handle forward declarations
check_declar(s, check_declar(s,
@ -319,7 +344,7 @@ void sema_function_enter(Sema* s, AstSpec spec, AstDeclar* declar)
.type = type, .type = type,
}, },
}; };
hashmap_set(&rib->ord_syms, declar->ident, &sym); define_ord_sym(s, declar->ident, sym);
sema_scope_enter(s); sema_scope_enter(s);
@ -335,6 +360,9 @@ void sema_function_enter(Sema* s, AstSpec spec, AstDeclar* declar)
Type* declar_type; Type* declar_type;
check_declar(s, param_declar, decl_type, &declar_type, &declar_ident); check_declar(s, param_declar, decl_type, &declar_type, &declar_ident);
if (!declar_ident)
continue;
Rib* rib = &s->ribs[s->ribs_size - 1]; Rib* rib = &s->ribs[s->ribs_size - 1];
error_if_redefinition(&rib->ord_syms, declar->ident, declar->loc); error_if_redefinition(&rib->ord_syms, declar->ident, declar->loc);
@ -346,7 +374,7 @@ void sema_function_enter(Sema* s, AstSpec spec, AstDeclar* declar)
.idx = i, .idx = i,
}, },
}; };
hashmap_set(&rib->ord_syms, declar_ident, &sym); define_ord_sym(s, declar_ident, sym);
} }
} }
@ -387,6 +415,41 @@ void sema_visit_stmt(Sema* s, AstStmt* stmt)
} }
} }
static bool is_arg_passable_as_param(const Type* arg, const Type* param)
{
// printf("comparing ");
// type_print(stdout, arg);
// printf(" with ");
// type_print(stdout, param);
// printf("\n");
//
if (arg == param)
return true;
if (arg->ty == Type_Pointer && param->ty == Type_Pointer) {
if (!(arg->spec & TypeSpec_Const) && (param->spec & TypeSpec_Const))
return false;
return is_arg_passable_as_param(
arg->pointer.inner, param->pointer.inner);
}
if (arg->ty == Type_Char && arg->ty == param->ty)
return true;
return false;
}
static Type* get_expr_type(Sema* s, const AstExpr* expr)
{
Type** res = keymap_get(&s->expr_types, (size_t)expr);
return res ? *res : nullptr;
}
static void set_expr_type(Sema* s, const AstExpr* expr, Type* type)
{
keymap_set(&s->expr_types, (size_t)expr, &type);
}
void sema_visit_expr(Sema* s, AstExpr* expr) void sema_visit_expr(Sema* s, AstExpr* expr)
{ {
Type* type = nullptr; Type* type = nullptr;
@ -431,14 +494,26 @@ void sema_visit_expr(Sema* s, AstExpr* expr)
break; break;
}; };
case AstExpr_Call: { case AstExpr_Call: {
Type* inner_type Type* inner_type = get_expr_type(s, expr->call.expr);
= keymap_get(&s->expr_types, (size_t)expr->call.expr);
assert(inner_type); assert(inner_type);
printf("inner_type->ty == %d\n", inner_type->ty);
assert(inner_type->ty == Type_Function); assert(inner_type->ty == Type_Function);
assert(expr->call.args_size == inner_type->function.params_size);
for (size_t i = 0; i < expr->call.args_size; ++i) {
Type* arg_type = get_expr_type(s, expr->call.args[i]);
Type* param_type = inner_type->function.params[i];
assert(is_arg_passable_as_param(arg_type, param_type));
}
type = inner_type->function.return_type;
break; break;
} }
} }
keymap_set(&s->expr_types, (KeymapKey)expr, &type); // printf(" expr(%d) -> ", expr->id);
// type_print(stdout, type);
// printf("\n");
set_expr_type(s, expr, type);
} }