445 lines
11 KiB
C
445 lines
11 KiB
C
#include "sema.h"
|
|
#include "ast.h"
|
|
#include "collection.h"
|
|
#include <assert.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
static void type_spec_print(FILE* file, TypeSpec spec)
|
|
{
|
|
|
|
struct {
|
|
TypeSpec spec;
|
|
const char* ident;
|
|
} spec_idents[] = {
|
|
{ TypeSpec_Const, "const" },
|
|
{ TypeSpec_Short, "short" },
|
|
{ TypeSpec_Long, "long" },
|
|
{ TypeSpec_Long2, "long" },
|
|
{ TypeSpec_Unsigned, "unsigned" },
|
|
};
|
|
|
|
size_t spec_idents_size = sizeof(spec_idents) / sizeof(spec_idents[0]);
|
|
|
|
for (size_t i = 0; i < spec_idents_size; ++i) {
|
|
if (spec & spec_idents[i].spec) {
|
|
fprintf(file, "%s ", spec_idents[i].ident);
|
|
}
|
|
}
|
|
}
|
|
|
|
void type_print(FILE* file, const Type* type)
|
|
{
|
|
type_spec_print(file, type->spec);
|
|
|
|
struct {
|
|
TypeTy ty;
|
|
const char* ident;
|
|
} ty_idents[] = {
|
|
{ Type_Int, "int" },
|
|
{ Type_Char, "char" },
|
|
{ Type_Float, "float" },
|
|
{ Type_Double, "double" },
|
|
{ Type_Bool, "bool" },
|
|
{ Type_Void, "void" },
|
|
};
|
|
|
|
size_t ty_idents_size = sizeof(ty_idents) / sizeof(ty_idents[0]);
|
|
|
|
for (size_t i = 0; i < ty_idents_size; ++i) {
|
|
if (type->ty == ty_idents[i].ty) {
|
|
fprintf(file, "%s", ty_idents[i].ident);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (type->ty == Type_Pointer) {
|
|
fprintf(file, " * ");
|
|
type_spec_print(file, type->pointer.spec);
|
|
type_print(file, type->pointer.inner);
|
|
return;
|
|
}
|
|
|
|
assert(false && "not implemented");
|
|
}
|
|
|
|
typedef struct {
|
|
Hashmap ord_syms;
|
|
Hashmap tag_syms;
|
|
} Rib;
|
|
|
|
static void rib_init(Rib* r)
|
|
{
|
|
*r = (Rib) {
|
|
.ord_syms = { },
|
|
.tag_syms = { },
|
|
};
|
|
hashmap_init(&r->ord_syms, sizeof(Sym*));
|
|
hashmap_init(&r->tag_syms, sizeof(Sym*));
|
|
}
|
|
|
|
static void rib_deinit(Rib* r)
|
|
{
|
|
hashmap_deinit(&r->ord_syms);
|
|
hashmap_deinit(&r->tag_syms);
|
|
}
|
|
|
|
struct Sema {
|
|
Arena sym_arena;
|
|
Arena type_arena;
|
|
ARRAY_FIELD(Rib, ribs);
|
|
Hashmap labels;
|
|
Keymap expr_types;
|
|
};
|
|
|
|
Sema* sema_new(void)
|
|
{
|
|
Sema* s = malloc(sizeof(Sema));
|
|
*s = (Sema) {
|
|
.sym_arena = { },
|
|
.type_arena = { },
|
|
ARRAY_FIELD_INIT(ribs),
|
|
.labels = { },
|
|
.expr_types = { },
|
|
};
|
|
|
|
arena_init(&s->sym_arena, 1024);
|
|
arena_init(&s->type_arena, 1024);
|
|
|
|
ARRAY_FIELD_PUSH(s, ribs, (&(Rib) { }));
|
|
rib_init(&s->ribs[0]);
|
|
|
|
hashmap_init(&s->labels, sizeof(LabelSym*));
|
|
keymap_init(&s->expr_types, sizeof(Type*));
|
|
|
|
return s;
|
|
}
|
|
|
|
void sema_free(Sema* s)
|
|
{
|
|
arena_deinit(&s->sym_arena);
|
|
arena_deinit(&s->type_arena);
|
|
for (size_t i = 0; i < s->ribs_size; ++i)
|
|
rib_deinit(&s->ribs[i]);
|
|
if (s->ribs)
|
|
free(s->ribs);
|
|
hashmap_deinit(&s->labels);
|
|
keymap_deinit(&s->expr_types);
|
|
free(s);
|
|
}
|
|
|
|
void sema_scope_enter(Sema* s)
|
|
{
|
|
ARRAY_FIELD_PUSH(s, ribs, (&(Rib) { }));
|
|
rib_init(&s->ribs[s->ribs_size - 1]);
|
|
}
|
|
|
|
void sema_scope_leave(Sema* s)
|
|
{
|
|
assert(s->ribs_size > 0);
|
|
rib_deinit(&s->ribs[s->ribs_size - 1]);
|
|
s->ribs_size -= 1;
|
|
}
|
|
|
|
static Sym* get_ord_sym(Sema* s, const char* ident)
|
|
{
|
|
Sym* sym = nullptr;
|
|
for (size_t i = 0; i < s->ribs_size; ++i)
|
|
if ((sym = hashmap_get(&s->ribs[s->ribs_size - i - 1].ord_syms, ident)))
|
|
break;
|
|
return sym;
|
|
}
|
|
|
|
static void error_if_multi_spec(
|
|
bool type_specced, Loc loc, const Type* first, TypeTy second_ty)
|
|
{
|
|
if (type_specced) {
|
|
fprintf(stderr, "error: cannot combine type '");
|
|
type_print(stderr, &(Type) { .ty = second_ty });
|
|
fprintf(stderr, "' with type '");
|
|
type_print(stderr, first);
|
|
fprintf(stderr, "' on line %d\n", loc.line);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static void decode_decl_spec(Type* type, AstSpec as, Loc loc)
|
|
{
|
|
bool type_specced = false;
|
|
|
|
if (as & AstSpec_Int) {
|
|
error_if_multi_spec(type_specced, loc, type, Type_Char);
|
|
type_specced = true;
|
|
|
|
type->ty = Type_Int;
|
|
}
|
|
if (as & AstSpec_Char) {
|
|
error_if_multi_spec(type_specced, loc, type, Type_Char);
|
|
type_specced = true;
|
|
|
|
type->ty = Type_Char;
|
|
}
|
|
if (as & AstSpec_Void) {
|
|
error_if_multi_spec(type_specced, loc, type, Type_Void);
|
|
type_specced = true;
|
|
|
|
type->ty = Type_Void;
|
|
}
|
|
if (as & (AstSpec_Enum | AstSpec_Struct | AstSpec_Union)) {
|
|
assert(false && "not implemented");
|
|
}
|
|
|
|
if (as & AstSpec_Unsigned) {
|
|
type->spec |= TypeSpec_Unsigned;
|
|
}
|
|
if (as & AstSpec_Const) {
|
|
type->spec |= TypeSpec_Const;
|
|
}
|
|
}
|
|
|
|
static Type* check_decl_type(Sema* s, AstDecl* decl)
|
|
{
|
|
Type* type = arena_alloc(&s->type_arena, sizeof(Type));
|
|
*type = (Type) { };
|
|
|
|
decode_decl_spec(type, decl->spec, decl->loc);
|
|
|
|
return type;
|
|
}
|
|
|
|
static void error_if_redefinition(Hashmap* map, const char* ident, Loc loc)
|
|
{
|
|
Sym* sym;
|
|
if ((sym = hashmap_get(map, ident))) {
|
|
fprintf(stderr,
|
|
"error: redefinition of '%s' on line %d\n",
|
|
ident,
|
|
loc.line);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static void check_declar(Sema* s,
|
|
AstDeclar* declar,
|
|
Type* type,
|
|
Type** out_declar_type,
|
|
const char** out_declar_ident)
|
|
{
|
|
switch (declar->ty) {
|
|
case AstDeclar_Ident: {
|
|
*out_declar_type = type;
|
|
*out_declar_ident = declar->ident;
|
|
return;
|
|
}
|
|
case AstDeclar_Pointer: {
|
|
Type* new_type = arena_alloc(&s->type_arena, sizeof(Type));
|
|
*new_type = (Type) {
|
|
.ty = Type_Pointer,
|
|
.spec = 0,
|
|
.pointer = {
|
|
.inner = type,
|
|
.spec = 0,
|
|
},
|
|
};
|
|
decode_decl_spec(new_type, declar->pointer.spec, declar->loc);
|
|
check_declar(s,
|
|
declar->pointer.declar,
|
|
new_type,
|
|
out_declar_type,
|
|
out_declar_ident);
|
|
return;
|
|
}
|
|
case AstDeclar_Array: {
|
|
assert(false && "not implemented");
|
|
break;
|
|
}
|
|
case AstDeclar_Function: {
|
|
Type* new_type = arena_alloc(&s->type_arena, sizeof(Type));
|
|
*new_type = (Type) {
|
|
.ty = Type_Function,
|
|
.spec = 0,
|
|
.function = {
|
|
.return_type = type,
|
|
ARRAY_FIELD_INIT(params),
|
|
},
|
|
};
|
|
|
|
for (size_t i = 0; i < declar->function.params_size; ++i) {
|
|
AstDecl* param_decl = declar->function.params[i];
|
|
|
|
assert(param_decl->declars_size == 1);
|
|
AstDeclar* param_declar = param_decl->declars[0];
|
|
|
|
Type* decl_type = check_decl_type(s, param_decl);
|
|
|
|
const char* declar_ident;
|
|
Type* declar_type;
|
|
check_declar(
|
|
s, param_declar, decl_type, &declar_type, &declar_ident);
|
|
|
|
(void)declar_ident;
|
|
|
|
ARENA_ARRAY_FIELD_PUSH(
|
|
&s->type_arena, &new_type->function, params, &declar_type);
|
|
}
|
|
|
|
// TODO: handle forward declarations
|
|
|
|
check_declar(s,
|
|
declar->pointer.declar,
|
|
new_type,
|
|
out_declar_type,
|
|
out_declar_ident);
|
|
return;
|
|
}
|
|
}
|
|
assert(false && "unreachable");
|
|
}
|
|
|
|
void sema_function_enter(Sema* s, AstSpec spec, AstDeclar* declar)
|
|
{
|
|
assert(declar->ty == AstDeclar_Function);
|
|
|
|
// clear label map
|
|
hashmap_deinit(&s->labels);
|
|
hashmap_init(&s->labels, sizeof(LabelSym*));
|
|
|
|
Type* type = arena_alloc(&s->type_arena, sizeof(Type));
|
|
*type = (Type) { };
|
|
decode_decl_spec(type, spec, declar->loc);
|
|
|
|
Rib* rib = &s->ribs[s->ribs_size - 1];
|
|
error_if_redefinition(&rib->ord_syms, declar->ident, declar->loc);
|
|
|
|
Sym* sym = arena_alloc(&s->sym_arena, sizeof(Sym));
|
|
*sym = (Sym) {
|
|
.ty = Sym_Declar,
|
|
.declar = {
|
|
.type = type,
|
|
},
|
|
};
|
|
hashmap_set(&rib->ord_syms, declar->ident, &sym);
|
|
|
|
sema_scope_enter(s);
|
|
|
|
for (size_t i = 0; i < declar->function.params_size; ++i) {
|
|
AstDecl* param_decl = declar->function.params[i];
|
|
|
|
assert(param_decl->declars_size == 1);
|
|
AstDeclar* param_declar = param_decl->declars[0];
|
|
|
|
Type* decl_type = check_decl_type(s, param_decl);
|
|
|
|
const char* declar_ident;
|
|
Type* declar_type;
|
|
check_declar(s, param_declar, decl_type, &declar_type, &declar_ident);
|
|
|
|
Rib* rib = &s->ribs[s->ribs_size - 1];
|
|
error_if_redefinition(&rib->ord_syms, declar->ident, declar->loc);
|
|
|
|
Sym* sym = arena_alloc(&s->sym_arena, sizeof(Sym));
|
|
*sym = (Sym) {
|
|
.ty = Sym_Param,
|
|
.param = {
|
|
.type = declar_type,
|
|
.idx = i,
|
|
},
|
|
};
|
|
hashmap_set(&rib->ord_syms, declar_ident, &sym);
|
|
}
|
|
}
|
|
|
|
void sema_function_leave(Sema* s)
|
|
{
|
|
sema_scope_leave(s);
|
|
}
|
|
|
|
void sema_visit_stmt(Sema* s, AstStmt* stmt)
|
|
{
|
|
if (stmt->ty == AstStmt_Decl) {
|
|
Type* type = check_decl_type(s, stmt->decl);
|
|
|
|
if (stmt->decl->function_body) {
|
|
// assume already handled
|
|
return;
|
|
}
|
|
|
|
for (size_t i = 0; i < stmt->decl->declars_size; ++i) {
|
|
AstDeclar* declar = stmt->decl->declars[i];
|
|
|
|
Type* declar_type;
|
|
const char* declar_ident;
|
|
check_declar(s, declar, type, &declar_type, &declar_ident);
|
|
|
|
Rib* rib = &s->ribs[s->ribs_size - 1];
|
|
error_if_redefinition(&rib->ord_syms, declar->ident, declar->loc);
|
|
|
|
Sym* sym = arena_alloc(&s->sym_arena, sizeof(Sym));
|
|
*sym = (Sym) {
|
|
.ty = Sym_Declar,
|
|
.declar = {
|
|
.type = declar_type,
|
|
},
|
|
};
|
|
hashmap_set(&rib->ord_syms, declar_ident, &sym);
|
|
}
|
|
}
|
|
}
|
|
|
|
void sema_visit_expr(Sema* s, AstExpr* expr)
|
|
{
|
|
Type* type = nullptr;
|
|
|
|
switch (expr->ty) {
|
|
case AstExpr_Ident: {
|
|
Sym* sym;
|
|
if ((sym = get_ord_sym(s, expr->ident)) == nullptr) {
|
|
fprintf(stderr,
|
|
"error: undefined identifier '%s' on line %d\n",
|
|
expr->ident,
|
|
expr->loc.line);
|
|
exit(1);
|
|
}
|
|
|
|
switch (sym->ty) {
|
|
case Sym_Declar:
|
|
type = sym->declar.type;
|
|
break;
|
|
case Sym_Param:
|
|
type = sym->param.type;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case AstExpr_Str: {
|
|
Type* inner_type = arena_alloc(&s->type_arena, sizeof(Type));
|
|
*inner_type = (Type) {
|
|
.ty = Type_Char,
|
|
.spec = TypeSpec_Const,
|
|
};
|
|
|
|
type = arena_alloc(&s->type_arena, sizeof(Type));
|
|
*type = (Type) {
|
|
.ty = Type_Pointer,
|
|
.pointer = {
|
|
.inner = inner_type,
|
|
.spec = 0,
|
|
},
|
|
};
|
|
break;
|
|
};
|
|
case AstExpr_Call: {
|
|
Type* inner_type
|
|
= keymap_get(&s->expr_types, (size_t)expr->call.expr);
|
|
assert(inner_type);
|
|
printf("inner_type->ty == %d\n", inner_type->ty);
|
|
assert(inner_type->ty == Type_Function);
|
|
break;
|
|
}
|
|
}
|
|
|
|
keymap_set(&s->expr_types, (KeymapKey)expr, &type);
|
|
}
|