more sema
This commit is contained in:
parent
d43dc3539d
commit
8309384c0b
1
ast.h
1
ast.h
@ -45,6 +45,7 @@ struct AstStmt {
|
||||
typedef enum : unsigned int {
|
||||
AstSpec_Int = 1 << 0,
|
||||
AstSpec_Char = 1 << 1,
|
||||
AstSpec_Void = 1 << 2,
|
||||
AstSpec_DefTy = 1 << 6,
|
||||
AstSpec_Unsigned = 1 << 7,
|
||||
AstSpec_Static = 1 << 8,
|
||||
|
||||
@ -262,7 +262,7 @@ void ast_expr_print(FILE* f, const AstExpr* node, int depth)
|
||||
}
|
||||
indent(f, depth + 1);
|
||||
}
|
||||
fprintf(f, ",]\n");
|
||||
fprintf(f, "],\n");
|
||||
|
||||
indent(f, depth);
|
||||
fprintf(f, "}");
|
||||
|
||||
2
parse.c
2
parse.c
@ -328,7 +328,7 @@ static AstDecl* parse_decl(Parser* p, bool is_param)
|
||||
|
||||
if (!is_param && first->ty == AstDeclar_Function && p->curr.ty == '{') {
|
||||
|
||||
sema_function_enter(p->sema, declars[0]);
|
||||
sema_function_enter(p->sema, spec, declars[0]);
|
||||
function_body = parse_stmt_block(p);
|
||||
sema_function_leave(p->sema);
|
||||
|
||||
|
||||
352
sema.c
352
sema.c
@ -2,8 +2,68 @@
|
||||
#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;
|
||||
@ -27,8 +87,10 @@ static void rib_deinit(Rib* r)
|
||||
|
||||
struct Sema {
|
||||
Arena sym_arena;
|
||||
Arena type_arena;
|
||||
ARRAY_FIELD(Rib, ribs);
|
||||
Hashmap labels;
|
||||
Keymap expr_types;
|
||||
};
|
||||
|
||||
Sema* sema_new(void)
|
||||
@ -36,16 +98,20 @@ 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;
|
||||
}
|
||||
@ -53,11 +119,13 @@ Sema* sema_new(void)
|
||||
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);
|
||||
}
|
||||
|
||||
@ -74,7 +142,162 @@ void sema_scope_leave(Sema* s)
|
||||
s->ribs_size -= 1;
|
||||
}
|
||||
|
||||
void sema_function_enter(Sema* s, AstDeclar* declar)
|
||||
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);
|
||||
|
||||
@ -82,9 +305,49 @@ void sema_function_enter(Sema* s, AstDeclar* declar)
|
||||
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) { }
|
||||
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)
|
||||
@ -92,15 +355,90 @@ void sema_function_leave(Sema* s)
|
||||
sema_scope_leave(s);
|
||||
}
|
||||
|
||||
void sema_visit_function_declar(Sema* s, AstDeclar* declar)
|
||||
{
|
||||
}
|
||||
|
||||
void sema_visit_stmt(Sema* s, AstStmt* stmt)
|
||||
{
|
||||
if (stmt->ty == AstStmt_Decl) { }
|
||||
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);
|
||||
}
|
||||
|
||||
55
sema.h
55
sema.h
@ -2,12 +2,65 @@
|
||||
#define SEMA_H
|
||||
|
||||
#include "ast.h"
|
||||
#include "collection.h"
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef enum {
|
||||
Type_None,
|
||||
Type_Int,
|
||||
Type_Char,
|
||||
Type_Float,
|
||||
Type_Double,
|
||||
Type_Bool,
|
||||
Type_Void,
|
||||
Type_Pointer,
|
||||
Type_Function,
|
||||
} TypeTy;
|
||||
|
||||
typedef enum {
|
||||
TypeSpec_Const = 1 << 0,
|
||||
TypeSpec_Short = 1 << 1,
|
||||
TypeSpec_Long = 1 << 2,
|
||||
TypeSpec_Long2 = 1 << 3,
|
||||
TypeSpec_Unsigned = 1 << 4,
|
||||
} TypeSpec;
|
||||
|
||||
typedef struct Type Type;
|
||||
|
||||
struct Type {
|
||||
TypeTy ty;
|
||||
TypeSpec spec;
|
||||
union {
|
||||
struct {
|
||||
Type* inner;
|
||||
TypeSpec spec;
|
||||
} pointer;
|
||||
struct {
|
||||
Type* return_type;
|
||||
ARRAY_FIELD(Type*, params);
|
||||
} function;
|
||||
};
|
||||
};
|
||||
|
||||
void type_print(FILE* file, const Type* type);
|
||||
|
||||
typedef enum {
|
||||
Sym_Declar,
|
||||
Sym_Param,
|
||||
} SymTy;
|
||||
|
||||
typedef struct {
|
||||
SymTy ty;
|
||||
union {
|
||||
struct {
|
||||
Type* type;
|
||||
} declar;
|
||||
struct {
|
||||
Type* type;
|
||||
size_t idx;
|
||||
} param;
|
||||
};
|
||||
} Sym;
|
||||
|
||||
typedef struct {
|
||||
@ -22,7 +75,7 @@ void sema_free(Sema* s);
|
||||
void sema_scope_enter(Sema* s);
|
||||
void sema_scope_leave(Sema* s);
|
||||
|
||||
void sema_function_enter(Sema* s, AstDeclar* declar);
|
||||
void sema_function_enter(Sema* s, AstSpec spec, AstDeclar* declar);
|
||||
void sema_function_leave(Sema* s);
|
||||
|
||||
void sema_visit_stmt(Sema* s, AstStmt* stmt);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user