summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarson Fleming <cflems@cflems.net>2026-07-17 12:40:59 -0700
committerCarson Fleming <cflems@cflems.net>2026-07-17 12:40:59 -0700
commit424be65858999a894a18e1972fa9649fbc4c1df8 (patch)
treea878fe623546a2e3fcaf204d5977991995b21c3c
parent155d1971b7cd56d3206808c36c6b40357bc31cbb (diff)
downloadccc-424be65858999a894a18e1972fa9649fbc4c1df8.tar.gz
improve type system sorta
-rw-r--r--ast.c15
-rw-r--r--ast.h18
-rw-r--r--codegen.c145
-rw-r--r--parser.c67
-rw-r--r--scope.c183
-rw-r--r--scope.h31
6 files changed, 286 insertions, 173 deletions
diff --git a/ast.c b/ast.c
index 24a37cf..f14e1ec 100644
--- a/ast.c
+++ b/ast.c
@@ -5,15 +5,6 @@
static void expr_destroy(struct expr_node* node);
static void stmt_destroy(struct stmt_node* node);
-static void type_destroy(struct type_node* node) {
- free(node->def);
-}
-
-static void var_decl_destroy(struct var_decl_node* node) {
- free(node->ident);
- type_destroy(&node->type);
-}
-
static void var_ref_destroy(struct var_ref_node* node) {
free(node->ident);
}
@@ -21,7 +12,6 @@ static void var_ref_destroy(struct var_ref_node* node) {
static void lval_destroy(struct lval_node* node) {
switch (node->type) {
case LVAL_VAR_DECL:
- var_decl_destroy(&node->inner.var_decl);
break;
case LVAL_VAR_REF:
var_ref_destroy(&node->inner.var_ref);
@@ -50,16 +40,12 @@ static void group_destroy(struct group_node* node) {
static void args_decl_destroy(struct args_decl_node* node) {
if (node != NULL) {
- var_decl_destroy(node->decl);
free(node->decl);
args_decl_destroy(node->next);
}
}
static void fn_decl_destroy(struct fn_decl_node* node) {
- type_destroy(&node->return_type);
- free(node->name);
-
args_decl_destroy(node->args);
free(node->args);
@@ -152,7 +138,6 @@ static void stmt_destroy(struct stmt_node* node) {
expr_destroy(&node->inner.expr);
break;
case STMT_VAR_DECL:
- var_decl_destroy(&node->inner.var_decl);
break;
case STMT_RETURN:
return_destroy(&node->inner.return_);
diff --git a/ast.h b/ast.h
index a2d576b..1eda481 100644
--- a/ast.h
+++ b/ast.h
@@ -6,12 +6,9 @@
struct stmt_node;
struct expr_node;
-struct type_node {
- bool is_unsigned;
- bool is_short;
- bool is_long;
+struct type_ref_node {
unsigned char ptr_level;
- struct type_def* def;
+ const struct type_def* def_ref;
};
struct int_lit_node {
@@ -35,8 +32,8 @@ struct var_ref_node {
};
struct var_decl_node {
- struct type_node type;
- char* ident;
+ struct type_ref_node type;
+ struct var_def* def_ref;
};
struct lval_node {
@@ -108,10 +105,7 @@ struct expr_node {
struct binary_node binary;
} inner;
- struct {
- bool is_signed;
- unsigned long long sz;
- } evaluated_type;
+ const struct type_def* resolved_type;
};
struct group_node {
@@ -125,7 +119,7 @@ struct args_decl_node {
};
struct fn_decl_node {
- struct type_node return_type;
+ struct type_ref_node return_type;
char* name;
struct args_decl_node* args;
struct group_node body;
diff --git a/codegen.c b/codegen.c
index 44fccce..e1e534c 100644
--- a/codegen.c
+++ b/codegen.c
@@ -15,8 +15,8 @@
}
struct lval_def {
+ const struct type_def* type;
struct storage_location loc;
- unsigned long long sz;
};
static const struct storage_location RV_LOC = {
@@ -36,39 +36,42 @@ static struct scope* scope;
static const struct fn_decl_node* active_fn;
static unsigned long long branch_counter = 0;
-static struct lval_def allocate_register(unsigned long long sz) {
+static struct lval_def allocate_register(const struct type_def* type) {
return (struct lval_def) {
.loc = {
.type = STO_REG,
.reg = &RAX, /* TODO: no real register coloring happening LOL */
},
- .sz = sz,
+ .type = type,
};
}
-static struct lval_def allocate_stack(FILE* outfile, unsigned long long sz) {
- fprintf(outfile, "\tsub rsp, %llu\n", sz);
- scope->bp_offset += sz;
+static struct lval_def allocate_stack(
+ FILE* outfile,
+ const struct type_def* type
+) {
+ fprintf(outfile, "\tsub rsp, %llu\n", type->sz);
+ scope->bp_offset += type->sz;
return (struct lval_def) {
.loc = {
.type = STO_STACK,
.bp_offset = scope->bp_offset,
},
- .sz = sz,
+ .type = type,
};
}
static struct lval_def allocate_temporary(
FILE* outfile,
- unsigned long long sz
+ const struct type_def* type
) {
- return allocate_stack(outfile, sz);
+ return allocate_stack(outfile, type);
}
static void deallocate_temporary(FILE* outfile, const struct lval_def* tmp) {
if (tmp->loc.type == STO_STACK) {
- fprintf(outfile, "\tadd rsp, %llu\n", tmp->sz);
- scope->bp_offset -= tmp->sz;
+ fprintf(outfile, "\tadd rsp, %llu\n", tmp->type->sz);
+ scope->bp_offset -= tmp->type->sz;
}
}
@@ -146,32 +149,32 @@ static void emit_mov(
switch (dst->loc.type) {
case STO_REG:
- if (src->type == STO_REG && dst->sz < 4) {
+ if (src->type == STO_REG && dst->type->sz < 4) {
fprintf(outfile, "\tmovzx ");
emit_storage_loc(outfile, &dst->loc, FULL_REG_SZ);
} else {
fprintf(outfile, "\tmov ");
- emit_storage_loc(outfile, &dst->loc, dst->sz);
+ emit_storage_loc(outfile, &dst->loc, dst->type->sz);
}
fprintf(outfile, ", ");
- emit_storage_loc(outfile, src, dst->sz);
+ emit_storage_loc(outfile, src, dst->type->sz);
break;
case STO_STACK:
if (src->type == STO_STACK) {
/* `mov mem, mem` is illegal in x86_64 */
- struct lval_def tmp = allocate_register(dst->sz);
+ struct lval_def tmp = allocate_register(dst->type);
emit_mov(outfile, &tmp, src);
emit_mov(outfile, dst, &tmp.loc);
return;
}
fprintf(outfile, "\tmov ");
- if (src->type == STO_IMM) emit_size_const(outfile, dst->sz);
+ if (src->type == STO_IMM) emit_size_const(outfile, dst->type->sz);
- emit_storage_loc(outfile, &dst->loc, dst->sz);
+ emit_storage_loc(outfile, &dst->loc, dst->type->sz);
fprintf(outfile, ", ");
- emit_storage_loc(outfile, src, dst->sz);
+ emit_storage_loc(outfile, src, dst->type->sz);
break;
case STO_LABEL:
case STO_IMM:
@@ -186,11 +189,11 @@ static void emit_cmp_zero(FILE* outfile, const struct lval_def* lval) {
fprintf(outfile, "\tcmp ");
switch (lval->loc.type) {
case STO_REG:
- emit_storage_loc(outfile, &lval->loc, lval->sz);
+ emit_storage_loc(outfile, &lval->loc, lval->type->sz);
break;
case STO_STACK:
- emit_size_const(outfile, lval->sz);
- emit_storage_loc(outfile, &lval->loc, lval->sz);
+ emit_size_const(outfile, lval->type->sz);
+ emit_storage_loc(outfile, &lval->loc, lval->type->sz);
break;
case STO_LABEL:
case STO_IMM:
@@ -201,17 +204,17 @@ static void emit_cmp_zero(FILE* outfile, const struct lval_def* lval) {
fprintf(outfile, ", 0\n");
}
-/* TODO: move this utility to the type checker and use evaluated_type */
-static unsigned long long get_type_size(const struct type_node* type) {
+/* TODO: move this utility to the type checker and use resolved_type */
+static unsigned long long get_type_size(const struct type_ref_node* type) {
if (type->ptr_level > 0) return PTR_SIZE;
- return type->def->sz;
+ return type->def_ref->sz;
}
static inline struct lval_def make_stack_lval(
FILE* outfile,
- const struct type_node* type
+ const struct type_def* type
) {
- return allocate_stack(outfile, get_type_size(type));
+ return allocate_stack(outfile, type);
}
static void emit_expr(
@@ -270,8 +273,8 @@ static void emit_str_lit(
}
}
-static struct var_def get_var(const char* name) {
- struct var_def var_def;
+static struct var_def* get_var(const char* name) {
+ struct var_def* var_def;
if (!scope_get_var(scope, &var_def, name))
CGEN_PANIC("reference to undefined variable %s", name);
return var_def;
@@ -283,25 +286,19 @@ static void emit_var_ref(
const struct lval_def* dst
) {
if (dst != NULL) {
- struct var_def var_def = get_var(node->ident);
- emit_mov(outfile, dst, &var_def.loc);
+ emit_mov(outfile, dst, &get_var(node->ident)->loc);
}
}
static void emit_stmt(FILE* outfile, const struct stmt_node* node);
-static struct var_def emit_var_decl(
+static struct var_def* emit_var_decl(
FILE* outfile,
const struct var_decl_node* node
) {
- /* TODO: type checker should define vars, we just set their loc */
- struct lval_def var_dst = make_stack_lval(outfile, &node->type);
- struct var_def var_def = {
- .name = node->ident,
- .loc = var_dst.loc,
- .sz = var_dst.sz,
- };
- scope_define_var(scope, var_def);
+ struct lval_def var_dst = make_stack_lval(outfile, node->type.def_ref);
+ struct var_def* var_def = node->def_ref;
+ var_def->loc = var_dst.loc;
return var_def;
}
@@ -309,14 +306,20 @@ static struct lval_def emit_lval(
FILE* outfile,
const struct lval_node* node
) {
- struct var_def var_def;
+ struct var_def* var_def;
switch (node->type) {
case LVAL_VAR_DECL:
var_def = emit_var_decl(outfile, &node->inner.var_decl);
- return (struct lval_def) {.loc = var_def.loc, .sz = var_def.sz};
+ return (struct lval_def) {
+ .loc = var_def->loc,
+ .type = var_def->resolved_type,
+ };
case LVAL_VAR_REF:
var_def = get_var(node->inner.var_ref.ident);
- return (struct lval_def) {.loc = var_def.loc, .sz = var_def.sz};
+ return (struct lval_def) {
+ .loc = var_def->loc,
+ .type = var_def->resolved_type,
+ };
}
CGEN_PANIC("unknown lval type: %d", node->type);
}
@@ -343,7 +346,7 @@ static void emit_call(
struct args_eval_node* arg_eval = node->args;
while (arg_decl != NULL && arg_eval != NULL) {
struct lval_def arg_dst =
- make_stack_lval(outfile, &arg_decl->decl->type);
+ make_stack_lval(outfile, arg_decl->decl->type.def_ref);
emit_expr(outfile, arg_eval->expr, &arg_dst);
arg_decl = arg_decl->next;
@@ -368,14 +371,14 @@ static void emit_call(
struct lval_def arg_dst;
if (arg_regnum < CC_N_REGS)
arg_dst = (struct lval_def) {
+ .type = arg_decl->decl->type.def_ref,
.loc = (struct storage_location) {
.type = STO_REG,
.reg = CALLING_CONV[arg_regnum++],
},
- .sz = type_sz,
};
else
- arg_dst = make_stack_lval(outfile, &arg_decl->decl->type);
+ arg_dst = make_stack_lval(outfile, arg_decl->decl->type.def_ref);
emit_mov(outfile, &arg_dst, &(struct storage_location) {
.type = STO_STACK,
@@ -411,8 +414,11 @@ static void emit_unary(
switch (node->op) {
case UNARY_NEG:
fprintf(outfile, "\tneg ");
- if (dst->loc.type == STO_STACK) emit_size_const(outfile, dst->sz);
- emit_storage_loc(outfile, &dst->loc, dst->sz);
+
+ if (dst->loc.type == STO_STACK)
+ emit_size_const(outfile, dst->type->sz);
+
+ emit_storage_loc(outfile, &dst->loc, dst->type->sz);
fprintf(outfile, "\n");
break;
}
@@ -431,29 +437,29 @@ static void emit_binary(
}
/* LHS goes in RAX explicitly because imul and idiv are weird */
- struct lval_def rhs_dst = allocate_temporary(outfile, dst->sz);
+ struct lval_def rhs_dst = allocate_temporary(outfile, dst->type);
emit_expr(outfile, node->rhs, &rhs_dst);
struct lval_def lhs_dst = (struct lval_def) {
.loc = MULDIV_LOC,
- .sz = dst->sz,
+ .type = dst->type,
};
emit_expr(outfile, node->lhs, &lhs_dst);
switch (node->op) {
case BINARY_ADD:
fprintf(outfile, "\tadd ");
- emit_storage_loc(outfile, &lhs_dst.loc, dst->sz);
+ emit_storage_loc(outfile, &lhs_dst.loc, dst->type->sz);
fprintf(outfile, ", ");
break;
case BINARY_SUB:
fprintf(outfile, "\tsub ");
- emit_storage_loc(outfile, &lhs_dst.loc, dst->sz);
+ emit_storage_loc(outfile, &lhs_dst.loc, dst->type->sz);
fprintf(outfile, ", ");
break;
case BINARY_MUL:
fprintf(outfile, "\timul ");
if (rhs_dst.loc.type == STO_STACK)
- emit_size_const(outfile, dst->sz);
+ emit_size_const(outfile, dst->type->sz);
break;
case BINARY_DIV:
/* nothing in the top half reg */
@@ -465,11 +471,11 @@ static void emit_binary(
fprintf(outfile, "\tidiv ");
if (rhs_dst.loc.type == STO_STACK)
- emit_size_const(outfile, dst->sz);
+ emit_size_const(outfile, dst->type->sz);
break;
}
- emit_storage_loc(outfile, &rhs_dst.loc, dst->sz);
+ emit_storage_loc(outfile, &rhs_dst.loc, dst->type->sz);
fprintf(outfile, "\n");
/* TODO: deal with RDX overflow shit for imul and idiv */
@@ -517,10 +523,10 @@ static void emit_expr(
static void emit_return(FILE* outfile, const struct return_node* node) {
if (active_fn == NULL) CGEN_PANIC("must be inside a function to return");
- unsigned long long return_type_sz = get_type_size(&active_fn->return_type);
+ const struct type_def* return_type = active_fn->return_type.def_ref;
if (node->ret_val != NULL) {
- if (return_type_sz == 0)
+ if (return_type->sz == 0)
CGEN_PANIC(
"returning a value from void function %s", active_fn->name);
@@ -529,9 +535,9 @@ static void emit_return(FILE* outfile, const struct return_node* node) {
node->ret_val,
&(struct lval_def) {
.loc = RV_LOC,
- .sz = return_type_sz,
+ .type = return_type,
});
- } else if (return_type_sz > 0) {
+ } else if (return_type > 0) {
CGEN_PANIC(
"non-void function %s should return a value", active_fn->name);
}
@@ -561,8 +567,9 @@ static void emit_group(FILE* outfile, const struct group_node* node) {
}
static void emit_if(FILE* outfile, const struct if_node* node) {
- /* TODO: use type checked cond result size; using full reg size causes a segfault */
- struct lval_def cond_result = allocate_temporary(outfile, FULL_REG_SZ);
+ /* TODO: make type checking real so we aren't passing a null pointer here */
+ struct lval_def cond_result =
+ allocate_temporary(outfile, node->cond->resolved_type);
emit_expr(outfile, node->cond, &cond_result);
emit_cmp_zero(outfile, &cond_result);
@@ -622,17 +629,11 @@ static void emit_fn_decl(FILE* outfile, const struct fn_decl_node* node) {
long long spilled_bp_ofs = -16; // return address + old bp
unsigned char arg_regnum = 0;
struct args_decl_node* arg_decl = node->args;
- while (arg_decl != NULL) {
+ for (; arg_decl != NULL; arg_decl = arg_decl->next) {
+ struct var_def* arg_def = arg_decl->decl->def_ref;
struct lval_def arg_dst =
- make_stack_lval(outfile, &arg_decl->decl->type);
- /* TODO: type checker should define vars, we just set their loc */
- scope_define_var(
- scope,
- (struct var_def) {
- .name = arg_decl->decl->ident,
- .loc = arg_dst.loc,
- .sz = arg_dst.sz,
- });
+ make_stack_lval(outfile, arg_decl->decl->type.def_ref);
+ arg_def->loc = arg_dst.loc;
struct storage_location arg_src;
if (arg_regnum < CC_N_REGS) {
@@ -645,11 +646,9 @@ static void emit_fn_decl(FILE* outfile, const struct fn_decl_node* node) {
.type = STO_STACK,
.bp_offset = spilled_bp_ofs,
};
- spilled_bp_ofs -= arg_dst.sz;
+ spilled_bp_ofs -= arg_decl->decl->type.def_ref->sz;
}
emit_mov(outfile, &arg_dst, &arg_src);
-
- arg_decl = arg_decl->next;
}
emit_group_contents(outfile, &node->body);
diff --git a/parser.c b/parser.c
index 9a3e7f3..56fbadd 100644
--- a/parser.c
+++ b/parser.c
@@ -61,15 +61,25 @@ static void expect_kw(const char* kw) {
tok.data.ident = NULL;
}
-static void parse_type(struct type_node* p_node) {
+static void parse_type_ref(struct type_ref_node* p_node) {
/* TODO: modifiers, void rules, arrays, etc. */
/* TODO: struct, union, enum */
expect(TK_IDENT);
- struct type_def* type_def = protected_alloc(sizeof(struct type_def));
- if (!scope_get_type(scope, type_def, tok.data.ident))
+ const struct type_def* type_def;
+ if (!scope_get_type(
+ scope,
+ &type_def,
+ &(struct type_key) {
+ .name = tok.data.ident,
+ /* TODO: parse modifiers */
+ .how_long = 0,
+ .marked_signed = 0,
+ .marked_unsigned = 0,
+ }))
PARSER_PANIC("unknown type name: %s", tok.data.ident);
- p_node->def = type_def;
+ free(tok.data.ident);
+ p_node->def_ref = type_def;
peek_or_panic();
p_node->ptr_level = 0;
@@ -143,15 +153,16 @@ static void parse_args_eval(struct args_eval_node** pp_arg) {
static void parse_expr_call(struct expr_node* p_node) {
switch (p_node->type) {
case EXPR_VAR_REF:
- struct var_def var_def;
+ struct var_def* var_def;
if (!scope_get_var(scope, &var_def, p_node->inner.var_ref.ident))
PARSER_PANIC(
"%s is not a known function", p_node->inner.var_ref.ident);
- if (var_def.loc.type != STO_FN)
+ /* TODO: I would like to include functions in the resolve type rather than checking storage */
+ if (var_def->loc.type != STO_FN)
PARSER_PANIC("called object is not a function");
- p_node->inner.call.called_fn_ref = var_def.loc.decl;
+ p_node->inner.call.called_fn_ref = var_def->loc.decl;
break;
default:
PARSER_PANIC("expression is not callable");
@@ -264,14 +275,16 @@ static void parse_expr(struct expr_node* p_node) {
}
static void parse_var_decl(struct var_decl_node* p_node) {
- parse_type(&p_node->type);
+ parse_type_ref(&p_node->type);
expect(TK_IDENT);
- p_node->ident = tok.data.ident;
- scope_define_var(scope, (struct var_def) {
- .name = p_node->ident,
+
+ p_node->def_ref = scope_define_var(scope, (struct var_def) {
+ .name = tok.data.ident,
.loc.type = STO_UNRESOLVED,
- .sz = 0, /* unknown */
+ .resolved_type = p_node->type.def_ref,
});
+ if (p_node->def_ref == NULL)
+ PARSER_PANIC("redefinition of '%s'", tok.data.ident);
}
static void parse_stmt(struct stmt_node* p_node);
@@ -372,7 +385,15 @@ static void parse_stmt(struct stmt_node* p_node) {
p_node->type = STMT_RETURN;
parse_return(&p_node->inner.return_);
break;
- } else if (scope_get_type(scope, NULL, tok.data.ident)) {
+ } else if (scope_get_type(
+ scope,
+ NULL,
+ &(struct type_key) {
+ .name = tok.data.ident,
+ .how_long = 0,
+ .marked_signed = false,
+ .marked_unsigned = false,
+ })) {
p_node->type = STMT_VAR_DECL;
parse_var_decl(&p_node->inner.var_decl);
break;
@@ -400,18 +421,20 @@ static void parse_args_decl(struct args_decl_node** pp_decl) {
}
static void parse_fn_decl(struct fn_decl_node* p_node) {
- parse_type(&p_node->return_type);
+ parse_type_ref(&p_node->return_type);
expect(TK_IDENT);
- p_node->name = tok.data.ident;
- scope_define_var(scope, (struct var_def) {
- .name = p_node->name,
- .loc = {
- .type = STO_FN,
- .decl = p_node,
- },
- });
+ if (scope_define_var(scope, (struct var_def) {
+ .name = tok.data.ident,
+ .loc = {
+ .type = STO_FN,
+ .decl = p_node,
+ },
+ }) == NULL)
+ PARSER_PANIC("redefinition of '%s'", tok.data.ident)
+
+ p_node->name = tok.data.ident;
expect(TK_LPAREN);
diff --git a/scope.c b/scope.c
index 2bb686a..119fc77 100644
--- a/scope.c
+++ b/scope.c
@@ -11,34 +11,77 @@ static void scope_init(struct scope* scope) {
scope->var_cap = DEFAULT_SIZE;
}
+static void type_destroy(struct type_def* type) {
+ free(type->key.name);
+}
+
+static void var_destroy(struct var_def* var) {
+ free(var->name);
+}
+
void scope_destroy(struct scope* scope) {
for (unsigned long long i = 0; i < scope->type_cap; i++) {
- if (scope->types[i] != NULL) free(scope->types[i]);
+ if (scope->types[i] != NULL) {
+ type_destroy(scope->types[i]);
+ free(scope->types[i]);
+ }
}
free(scope->types);
for (unsigned long long i = 0; i < scope->var_cap; i++) {
- if (scope->vars[i] != NULL) free(scope->vars[i]);
+ if (scope->vars[i] != NULL) {
+ var_destroy(scope->vars[i]);
+ free(scope->vars[i]);
+ }
}
free(scope->vars);
}
-unsigned long long hash_name(const char* name) {
+static inline unsigned long long advance_hash(
+ unsigned long long hash,
+ unsigned long long val,
+ unsigned long long cap
+) {
+ return ((hash << 5) - hash + val) % cap;;
+}
+
+static unsigned long long hash_name(const char* name, unsigned long long cap) {
unsigned long long hash = 0, i = 0;
- while (name[i] != 0) hash = (hash << 5) - hash + name[i++];
+ while (name[i] != 0) hash = advance_hash(hash, name[i++], cap);
return hash;
}
+unsigned long long hash_type_key(
+ const struct type_key* key,
+ unsigned long long cap
+) {
+ unsigned long long hash = hash_name(key->name, cap);
+ hash = advance_hash(hash, key->how_long, cap);
+ hash = advance_hash(hash, key->marked_signed, cap);
+ hash = advance_hash(hash, key->marked_unsigned, cap);
+ return hash;
+}
+
+static bool type_keys_equal(
+ const struct type_key* a,
+ const struct type_key* b
+) {
+ return a->how_long == b->how_long
+ && a->marked_signed == b->marked_signed
+ && a->marked_unsigned == b->marked_unsigned
+ && strcmp(a->name, b->name) == 0;
+}
+
static struct type_def** type_cell(
const struct scope* scope,
- const char* name
+ const struct type_key* key
) {
- unsigned long long orig_idx = hash_name(name) % scope->type_cap;
+ unsigned long long orig_idx = hash_type_key(key, scope->type_cap);
unsigned long long idx = orig_idx;
do {
if (scope->types[idx] == NULL
- || strcmp(name, scope->types[idx]->name) == 0)
+ || type_keys_equal(key, &scope->types[idx]->key))
return &scope->types[idx];
} while ((idx = (idx + 1) % scope->type_cap) != orig_idx);
return NULL;
@@ -57,7 +100,7 @@ static void rehash_types(struct scope* scope) {
for (unsigned long long i = 0; i < old_cap; i++) {
if (old_types[i] == NULL) continue;
- struct type_def** cell = type_cell(scope, old_types[i]->name);
+ struct type_def** cell = type_cell(scope, &old_types[i]->key);
if (cell == NULL) {
fprintf(stderr, "ccc: types rehash failed, likely a bug\n");
exit(1);
@@ -71,7 +114,7 @@ static struct var_def** var_cell(
const struct scope* scope,
const char* name
) {
- unsigned long long orig_idx = hash_name(name) % scope->var_cap;
+ unsigned long long orig_idx = hash_name(name, scope->var_cap);
unsigned long long idx = orig_idx;
do {
@@ -117,106 +160,164 @@ void scope_pop(struct scope** p_scope) {
}
void scope_install_default_types(struct scope* scope) {
+ /* TODO: don't let people make void variables */
scope_define_type(scope, (struct type_def) {
- .name = "void",
- .sz = 0,
- .is_primitive = true,
+ .key = {
+ .name = "void",
+ .how_long = 0,
+ .marked_signed = false,
+ .marked_unsigned = false,
+ },
.is_signed = false,
+ .is_floating = false,
+ .sz = 0,
});
+
scope_define_type(scope, (struct type_def) {
- .name = "bool",
+ .key = {
+ .name = "bool",
+ .how_long = 0,
+ .marked_signed = false,
+ .marked_unsigned = false,
+ },
.sz = 1,
- .is_primitive = true,
.is_signed = false,
+ .is_floating = false,
});
+
scope_define_type(scope, (struct type_def) {
- .name = "char",
+ .key = {
+ .name = "char",
+ .how_long = 0,
+ .marked_signed = false,
+ .marked_unsigned = false,
+ },
.sz = 1,
- .is_primitive = true,
.is_signed = true, /* implementation defined babyyyyyy */
+ .is_floating = false,
});
+
scope_define_type(scope, (struct type_def) {
- .name = "short",
+ .key = {
+ .name = "short",
+ .how_long = 0,
+ .marked_signed = false,
+ .marked_unsigned = false,
+ },
.sz = 2,
- .is_primitive = true,
.is_signed = true,
+ .is_floating = false,
});
+
scope_define_type(scope, (struct type_def) {
- .name = "int",
+ .key = {
+ .name = "int",
+ .how_long = 0,
+ .marked_signed = false,
+ .marked_unsigned = false,
+ },
.sz = 4,
- .is_primitive = true,
.is_signed = true,
+ .is_floating = false,
});
+
scope_define_type(scope, (struct type_def) {
- .name = "float",
- .sz = 4,
- .is_primitive = true,
- .is_signed = true, /* floats can't be unsigned but wtv */
+ .key = {
+ .name = "long",
+ .how_long = 0,
+ .marked_signed = false,
+ .marked_unsigned = false,
+ },
+ .sz = 8,
+ .is_signed = true,
+ .is_floating = false,
});
+
scope_define_type(scope, (struct type_def) {
- .name = "long",
- .sz = 8,
- .is_primitive = true,
+ .key = {
+ .name = "float",
+ .how_long = 0,
+ .marked_signed = false,
+ .marked_unsigned = false,
+ },
+ .sz = 4,
.is_signed = true,
+ .is_floating = true,
});
+
scope_define_type(scope, (struct type_def) {
- .name = "double",
+ .key = {
+ .name = "double",
+ .how_long = 0,
+ .marked_signed = false,
+ .marked_unsigned = false,
+ },
.sz = 8,
- .is_primitive = true,
- .is_signed = true, /* doubles also can't be unsigned */
+ .is_signed = true,
+ .is_floating = true,
});
}
bool scope_get_type(
const struct scope* scope,
- struct type_def* p_entry,
- const char* name
+ const struct type_def** p_entry,
+ const struct type_key* key
) {
for (; scope != NULL; scope = scope->next_out) {
- struct type_def** cell = type_cell(scope, name);
+ struct type_def** cell = type_cell(scope, key);
if (cell == NULL || *cell == NULL) continue;
- if (p_entry != NULL) *p_entry = **cell;
+ if (p_entry != NULL) *p_entry = *cell;
return true;
}
return false;
}
-void scope_define_type(struct scope* scope, struct type_def type) {
- struct type_def** cell = type_cell(scope, type.name);
+const struct type_def* scope_define_type(
+ struct scope* scope,
+ struct type_def type
+) {
+ struct type_def** cell = type_cell(scope, &type.key);
while (cell == NULL) {
rehash_types(scope);
- cell = type_cell(scope, type.name);
+ cell = type_cell(scope, &type.key);
}
+ /* redefinition leaks memory, so refuse */
+ if (*cell != NULL) return NULL;
+
*cell = calloc(1, sizeof(struct type_def));
if (*cell == NULL) {
fprintf(stderr, "ccc: out of memory\n");
exit(1);
}
**cell = type;
+ return *cell;
}
bool scope_get_var(
const struct scope* scope,
- struct var_def* p_entry,
+ struct var_def** p_entry,
const char* name
) {
for (; scope != NULL; scope = scope->next_out) {
struct var_def** cell = var_cell(scope, name);
if (cell == NULL || *cell == NULL) continue;
- if (p_entry != NULL) *p_entry = **cell;
+ if (p_entry != NULL) *p_entry = *cell;
return true;
}
return false;
}
-void scope_define_var(struct scope* scope, struct var_def var) {
+struct var_def* scope_define_var(struct scope* scope, struct var_def var) {
struct var_def** cell = var_cell(scope, var.name);
while (cell == NULL) {
rehash_vars(scope);
cell = var_cell(scope, var.name);
}
+ /* redefinition leaks memory, so refuse */
+ if (*cell != NULL) return NULL;
+
if (*cell == NULL) {
*cell = calloc(1, sizeof(struct var_def));
if (*cell == NULL) {
@@ -224,6 +325,6 @@ void scope_define_var(struct scope* scope, struct var_def var) {
exit(1);
}
}
- /* technically C allows redefinition :/ */
**cell = var;
+ return *cell;
}
diff --git a/scope.h b/scope.h
index dfbb2c1..1d3c8ac 100644
--- a/scope.h
+++ b/scope.h
@@ -7,6 +7,8 @@ struct storage_location {
STO_LABEL,
STO_STACK,
STO_IMM,
+ /* I would like to solve functions using the type system
+ * and kill STO_FN and STO_UNRESOLVED in favor of STO_LABEL. */
STO_FN,
STO_UNRESOLVED,
} type;
@@ -19,17 +21,24 @@ struct storage_location {
};
};
+struct type_key {
+ char* name;
+ unsigned char how_long;
+ bool marked_unsigned;
+ bool marked_signed;
+};
+
struct type_def {
- const char* name;
- unsigned long long sz;
- bool is_primitive;
+ struct type_key key;
bool is_signed;
+ bool is_floating;
+ unsigned long long sz;
};
struct var_def {
- const char* name;
+ char* name;
struct storage_location loc;
- unsigned long long sz;
+ const struct type_def* resolved_type;
};
struct scope {
@@ -51,13 +60,15 @@ void scope_destroy(struct scope* scope);
void scope_install_default_types(struct scope* scope);
bool scope_get_type(
const struct scope* scope,
- struct type_def* p_entry,
- const char* name);
-void scope_define_type(struct scope* scope, struct type_def type);
+ const struct type_def** p_entry,
+ const struct type_key* key);
+const struct type_def* scope_define_type(
+ struct scope* scope,
+ struct type_def type);
bool scope_get_var(
const struct scope* scope,
- struct var_def* p_entry,
+ struct var_def** p_entry,
const char* name);
-void scope_define_var(struct scope* scope, struct var_def var);
+struct var_def* scope_define_var(struct scope* scope, struct var_def var);
#endif \ No newline at end of file