harec

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit e495ace2dbaff1913aa0b862ae0624227cb78abd
parent 0ac9fdde69a7648cd61a1d9f92194bdc6df1b7c5
Author: Drew DeVault <sir@cmpwn.com>
Date:   Fri, 22 Jan 2021 10:24:43 -0500

Refactor check in preparation for modules

Diffstat:
Mdocs/env.txt | 3++-
Minclude/check.h | 6+++---
Minclude/identifier.h | 2++
Minclude/util.h | 7+++++++
Msrc/check.c | 104++++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/gen.c | 17-----------------
Msrc/identifier.c | 38++++++++++++++++++++++++++++++++++++++
Msrc/main.c | 6++++--
Msrc/mod.c | 48++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/util.c | 25+++++++++++++++++++++++--
10 files changed, 179 insertions(+), 77 deletions(-)

diff --git a/docs/env.txt b/docs/env.txt @@ -7,7 +7,8 @@ The following environment variables are used: HARECACHE Path to the module cache, defaults to ~/.cache/hare. For each imported module, harec will construct the path $HARECACHE/$modname/$modversion.td - to look up the module API. + to look up the module API, where $modname has namespace delimiters '::' + replaced with path separators '/'. HARE.$MOD.VERSION Sets the module version to reference for given module $MOD. For example, diff --git a/include/check.h b/include/check.h @@ -9,8 +9,8 @@ struct expression; struct scope; struct context { - struct type_store store; - const struct type *current_fntype; + struct type_store *store; + const struct type *fntype; struct identifier *ns; struct scope *unit; struct scope *scope; @@ -74,7 +74,7 @@ struct unit { struct ast_expression; struct ast_unit; -void check(struct context *ctx, +void check(struct type_store *ts, const struct ast_unit *aunit, struct unit *unit); diff --git a/include/identifier.h b/include/identifier.h @@ -11,6 +11,8 @@ struct identifier { char *identifier_unparse(const struct identifier *ident); int identifier_unparse_static( const struct identifier *ident, char *buf, size_t len); +const char *ident_to_path(const struct identifier *ident); +char *ident_to_sym(const struct identifier *ident); void identifier_dup(struct identifier *new, const struct identifier *ident); bool identifier_eq(const struct identifier *a, const struct identifier *b); diff --git a/include/util.h b/include/util.h @@ -15,4 +15,11 @@ void *xrealloc(void *p, size_t s); #define calloc(a, b) (void *)sizeof(struct { static_assert(0, "Use xcalloc instead"); int _; }); #define realloc(a, b) (void *)sizeof(struct { static_assert(0, "Use xrealloc instead"); int _; }); +struct pathspec { + const char *var; + const char *path; +}; + +char *getpath(const struct pathspec *paths, size_t npaths); + #endif diff --git a/src/check.c b/src/check.c @@ -112,7 +112,7 @@ check_expr_access(struct context *ctx, type_storage_unparse(itype->storage)); expr->access.index = lower_implicit_cast( &builtin_type_size, expr->access.index); - expr->result = type_store_lookup_with_flags(&ctx->store, + expr->result = type_store_lookup_with_flags(ctx->store, atype->array.members, atype->flags | atype->array.members->flags); break; case ACCESS_FIELD: @@ -202,7 +202,7 @@ check_expr_assign(struct context *ctx, !(object->result->pointer.flags & PTR_NULLABLE), "Cannot dereference nullable pointer type"); expect(&aexpr->loc, - type_is_assignable(&ctx->store, + type_is_assignable(ctx->store, object->result->pointer.referent, value->result), "Value type is not assignable to pointer type"); @@ -212,7 +212,7 @@ check_expr_assign(struct context *ctx, expect(&aexpr->loc, !(object->result->flags & TYPE_CONST), "Cannot assign to const object"); expect(&aexpr->loc, - type_is_assignable(&ctx->store, object->result, value->result), + type_is_assignable(ctx->store, object->result, value->result), "rvalue type is not assignable to lvalue"); value = lower_implicit_cast(object->result, value); } @@ -289,8 +289,8 @@ check_expr_binding(struct context *ctx, const struct type *type = NULL; if (abinding->type) { type = type_store_lookup_atype( - &ctx->store, abinding->type); - type = type_store_lookup_with_flags(&ctx->store, + ctx->store, abinding->type); + type = type_store_lookup_with_flags(ctx->store, type, type->flags | abinding->flags); } @@ -302,14 +302,14 @@ check_expr_binding(struct context *ctx, check_expression(ctx, abinding->initializer, initializer, type); if (!type) { - type = type_store_lookup_with_flags(&ctx->store, + type = type_store_lookup_with_flags(ctx->store, initializer->result, abinding->flags); } expect(&aexpr->loc, type->size != 0 && type->size != SIZE_UNDEFINED, "Cannot create binding for type of zero or undefined size"); expect(&aexpr->loc, - type_is_assignable(&ctx->store, type, initializer->result), + type_is_assignable(ctx->store, type, initializer->result), "Initializer is not assignable to binding type"); binding->initializer = lower_implicit_cast(type, initializer); @@ -370,7 +370,7 @@ lower_vaargs(struct context *ctx, // XXX: This error handling is minimum-effort and bad const struct type *hint = type_store_lookup_array( - &ctx->store, type, SIZE_UNDEFINED); + ctx->store, type, SIZE_UNDEFINED); check_expression(ctx, &val, vaargs, hint); assert(vaargs->result->storage == TYPE_STORAGE_ARRAY); expect(&val.loc, vaargs->result->array.members == type, @@ -427,7 +427,7 @@ check_expr_call(struct context *ctx, check_expression(ctx, aarg->value, arg->value, param->type); expect(&aarg->value->loc, - type_is_assignable(&ctx->store, + type_is_assignable(ctx->store, param->type, arg->value->result), "Argument is not assignable to parameter type"); arg->value = lower_implicit_cast(param->type, arg->value); @@ -456,7 +456,7 @@ check_expr_cast(struct context *ctx, struct expression *value = expr->cast.value = xcalloc(1, sizeof(struct expression)); const struct type *secondary = expr->cast.secondary = - type_store_lookup_atype(&ctx->store, aexpr->cast.type); + type_store_lookup_atype(ctx->store, aexpr->cast.type); check_expression(ctx, aexpr->cast.value, value, secondary); expect(&aexpr->cast.type->loc, type_is_castable(secondary, value->result), @@ -515,7 +515,7 @@ check_expr_array(struct context *ctx, type = value->result; } else { expect(&item->value->loc, - type_is_assignable(&ctx->store, type, value->result), + type_is_assignable(ctx->store, type, value->result), "Array members must be of a uniform type"); cur->value = lower_implicit_cast(type, cur->value); } @@ -538,10 +538,10 @@ check_expr_array(struct context *ctx, && hint->array.length != SIZE_UNDEFINED && hint->array.length >= len, "Cannot expand array into destination type"); - expr->result = type_store_lookup_array(&ctx->store, + expr->result = type_store_lookup_array(ctx->store, type, hint->array.length); } else { - expr->result = type_store_lookup_array(&ctx->store, type, len); + expr->result = type_store_lookup_array(ctx->store, type, len); } } @@ -825,7 +825,7 @@ check_expr_measure(struct context *ctx, break; case M_SIZE: expr->measure.type = type_store_lookup_atype( - &ctx->store, aexpr->measure.type); + ctx->store, aexpr->measure.type); break; case M_OFFSET: assert(0); // TODO @@ -846,13 +846,13 @@ check_expr_return(struct context *ctx, if (aexpr->_return.value) { struct expression *rval = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->_return.value, - rval, ctx->current_fntype->func.result); + rval, ctx->fntype->func.result); expect(&aexpr->_return.value->loc, - type_is_assignable(&ctx->store, ctx->current_fntype->func.result, rval->result), + type_is_assignable(ctx->store, ctx->fntype->func.result, rval->result), "Return value is not assignable to function result type"); - if (ctx->current_fntype->func.result != rval->result) { + if (ctx->fntype->func.result != rval->result) { rval = lower_implicit_cast( - ctx->current_fntype->func.result, rval); + ctx->fntype->func.result, rval); } expr->_return.value = rval; } @@ -905,7 +905,7 @@ check_expr_slice(struct context *ctx, // TODO: Assert that array type has a well-defined length } - expr->result = type_store_lookup_slice(&ctx->store, + expr->result = type_store_lookup_slice(ctx->store, expr->slice.object->result->array.members); trleave(TR_CHECK, NULL); @@ -940,7 +940,7 @@ check_expr_struct(struct context *ctx, tfield->field.type = afield->field.type; sexpr->value = xcalloc(1, sizeof(struct expression)); check_expression(ctx, afield->field.initializer, sexpr->value, - type_store_lookup_atype(&ctx->store, tfield->field.type)); + type_store_lookup_atype(ctx->store, tfield->field.type)); if (afield->next) { *tnext = tfield = xcalloc( @@ -954,7 +954,7 @@ check_expr_struct(struct context *ctx, afield = afield->next; } - expr->result = type_store_lookup_atype(&ctx->store, &stype); + expr->result = type_store_lookup_atype(ctx->store, &stype); tfield = &stype.struct_union; sexpr = &expr->_struct; @@ -964,7 +964,7 @@ check_expr_struct(struct context *ctx, // TODO: Use more specific error location expect(&aexpr->loc, field, "No field by this name exists for this type"); expect(&aexpr->loc, - type_is_assignable(&ctx->store, field->type, sexpr->value->result), + type_is_assignable(ctx->store, field->type, sexpr->value->result), "Cannot initialize struct field from value of this type"); sexpr->field = field; sexpr->value = lower_implicit_cast(field->type, sexpr->value); @@ -1086,7 +1086,7 @@ check_expr_unarithm(struct context *ctx, break; case UN_ADDRESS: expr->result = type_store_lookup_pointer( - &ctx->store, operand->result, 0); + ctx->store, operand->result, 0); break; case UN_DEREF: expect(&aexpr->unarithm.operand->loc, @@ -1181,7 +1181,7 @@ check_const(struct context *ctx, const struct ast_decl *adecl) { const struct type *type = type_store_lookup_atype( - &ctx->store, adecl->constant.type); + ctx->store, adecl->constant.type); struct declaration *decl = xcalloc(1, sizeof(struct declaration)); const struct scope_object *obj = scope_lookup( ctx->unit, &adecl->constant.ident); @@ -1210,9 +1210,9 @@ check_function(struct context *ctx, .func = afndecl->prototype, }; const struct type *fntype = type_store_lookup_atype( - &ctx->store, &fn_atype); + ctx->store, &fn_atype); assert(fntype); // Invariant - ctx->current_fntype = fntype; + ctx->fntype = fntype; expect(&adecl->loc, fntype->func.variadism != VARIADISM_C, @@ -1239,10 +1239,10 @@ check_function(struct context *ctx, .name = params->name, }; const struct type *type = type_store_lookup_atype( - &ctx->store, params->type); + ctx->store, params->type); if (fntype->func.variadism == VARIADISM_HARE && !params->next) { - type = type_store_lookup_slice(&ctx->store, type); + type = type_store_lookup_slice(ctx->store, type); } scope_insert(decl->func.scope, O_BIND, &ident, &ident, type, NULL); @@ -1253,7 +1253,7 @@ check_function(struct context *ctx, check_expression(ctx, afndecl->body, body, fntype->func.result); expect(&afndecl->body->loc, - body->terminates || type_is_assignable(&ctx->store, fntype->func.result, body->result), + body->terminates || type_is_assignable(ctx->store, fntype->func.result, body->result), "Result value is not assignable to function result type"); if (!body->terminates && fntype->func.result != body->result) { body = lower_implicit_cast(fntype->func.result, body); @@ -1272,7 +1272,7 @@ check_function(struct context *ctx, } scope_pop(&ctx->scope, TR_CHECK); - ctx->current_fntype = NULL; + ctx->fntype = NULL; trleave(TR_CHECK, NULL); return decl; } @@ -1287,7 +1287,7 @@ check_global(struct context *ctx, } const struct type *type = type_store_lookup_atype( - &ctx->store, agdecl->type); + ctx->store, agdecl->type); // TODO: Free initialier struct expression *initializer = @@ -1295,7 +1295,7 @@ check_global(struct context *ctx, check_expression(ctx, agdecl->init, initializer, type); expect(&agdecl->init->loc, - type_is_assignable(&ctx->store, type, initializer->result), + type_is_assignable(ctx->store, type, initializer->result), "Constant type is not assignable from initializer type"); initializer = lower_implicit_cast(type, initializer); @@ -1325,7 +1325,7 @@ check_type(struct context *ctx, const struct ast_decl *adecl) { const struct type *type = - type_store_lookup_atype(&ctx->store, adecl->type.type); + type_store_lookup_atype(ctx->store, adecl->type.type); struct declaration *decl = xcalloc(1, sizeof(struct declaration)); decl->type = DECL_TYPE; decl->_type = type; @@ -1378,7 +1378,7 @@ scan_const(struct context *ctx, const struct ast_global_decl *decl) assert(!decl->symbol); // Invariant const struct type *type = type_store_lookup_atype( - &ctx->store, decl->type); + ctx->store, decl->type); // TODO: // - Free the initializer // - Defer if we can't evaluate it now (for forward references) @@ -1386,7 +1386,7 @@ scan_const(struct context *ctx, const struct ast_global_decl *decl) xcalloc(1, sizeof(struct expression)); check_expression(ctx, decl->init, initializer, type); - expect(&decl->init->loc, type_is_assignable(&ctx->store, type, initializer->result), + expect(&decl->init->loc, type_is_assignable(ctx->store, type, initializer->result), "Constant type is not assignable from initializer type"); initializer = lower_implicit_cast(type, initializer); @@ -1414,7 +1414,7 @@ scan_function(struct context *ctx, const struct ast_function_decl *decl) .func = decl->prototype, }; const struct type *fntype = type_store_lookup_atype( - &ctx->store, &fn_atype); + ctx->store, &fn_atype); assert(fntype); // TODO: Forward references struct identifier ident = {0}; @@ -1436,7 +1436,7 @@ scan_global(struct context *ctx, const struct ast_global_decl *decl) trenter(TR_SCAN, "global"); const struct type *type = type_store_lookup_atype( - &ctx->store, decl->type); + ctx->store, decl->type); assert(type); // TODO: Forward references struct identifier ident = {0}; @@ -1455,7 +1455,7 @@ scan_type(struct context *ctx, const struct ast_type_decl *decl) { trenter(TR_SCAN, "type"); const struct type *type = - type_store_lookup_atype(&ctx->store, decl->type); + type_store_lookup_atype(ctx->store, decl->type); struct identifier ident = {0}; mkident(ctx, &ident, &decl->ident); @@ -1472,7 +1472,7 @@ scan_type(struct context *ctx, const struct ast_type_decl *decl) .alias = decl->ident, }; const struct type *alias = - type_store_lookup_atype(&ctx->store, &atype); + type_store_lookup_atype(ctx->store, &atype); struct expression *expr = xcalloc(sizeof(struct expression), 1); @@ -1528,11 +1528,12 @@ scan_declarations(struct context *ctx, const struct ast_decls *decls) } void -check(struct context *ctx, const struct ast_unit *aunit, struct unit *unit) +check(struct type_store *ts, const struct ast_unit *aunit, struct unit *unit) { - builtin_types_init(); - ctx->store.check_context = ctx; - ctx->ns = unit->ns; + struct context ctx = {0}; + ctx.ns = unit->ns; + ctx.store = ts; + ctx.store->check_context = &ctx; // Top-level scope management involves: // @@ -1542,7 +1543,7 @@ check(struct context *ctx, const struct ast_unit *aunit, struct unit *unit) // // Further down the call frame, subsequent functions will create // sub-scopes for each declaration, expression-list, etc. - ctx->unit = scope_push(&ctx->scope, TR_MAX); + ctx.unit = scope_push(&ctx.scope, TR_MAX); struct scopes *subunit_scopes; struct scopes **next = &subunit_scopes; @@ -1550,15 +1551,14 @@ check(struct context *ctx, const struct ast_unit *aunit, struct unit *unit) // First pass populates the type graph for (const struct ast_subunit *su = &aunit->subunits; su; su = su->next) { - scope_push(&ctx->scope, TR_SCAN); + scope_push(&ctx.scope, TR_SCAN); for (struct ast_imports *imports = su->imports; imports; imports = imports->next) { struct scope *mod; switch (imports->mode) { case AST_IMPORT_IDENTIFIER: - mod = module_resolve( - &imports->ident, &ctx->store); + mod = module_resolve(&imports->ident, ts); break; case AST_IMPORT_ALIAS: assert(0); // TODO @@ -1569,10 +1569,10 @@ check(struct context *ctx, const struct ast_unit *aunit, struct unit *unit) (void)mod; // TODO: Populate subunit scope } - scan_declarations(ctx, &su->decls); + scan_declarations(&ctx, &su->decls); *next = xcalloc(1, sizeof(struct scopes)); - (*next)->scope = scope_pop(&ctx->scope, TR_SCAN); + (*next)->scope = scope_pop(&ctx.scope, TR_SCAN); next = &(*next)->next; } @@ -1581,9 +1581,9 @@ check(struct context *ctx, const struct ast_unit *aunit, struct unit *unit) struct declarations **next_decl = &unit->declarations; for (const struct ast_subunit *su = &aunit->subunits; su; su = su->next) { - ctx->scope = scope->scope; - trenter(TR_CHECK, "scope %p", ctx->scope); - next_decl = check_declarations(ctx, &su->decls, next_decl); + ctx.scope = scope->scope; + trenter(TR_CHECK, "scope %p", ctx.scope); + next_decl = check_declarations(&ctx, &su->decls, next_decl); trleave(TR_CHECK, NULL); scope = scope->next; } diff --git a/src/gen.c b/src/gen.c @@ -17,23 +17,6 @@ static void gen_expression(struct gen_context *ctx, const struct expression *expr, const struct qbe_value *out); -static char * -ident_to_sym(const struct identifier *ident) -{ - if (ident->ns) { - char *ns = ident_to_sym(ident->ns); - if (!ns) { - return NULL; - } - int n = snprintf(NULL, 0, "%s.%s", ns, ident->name); - char *str = xcalloc(1, n + 1); - snprintf(str, n + 1, "%s.%s", ns, ident->name); - free(ns); - return str; - } - return strdup(ident->name); -} - static struct gen_scope_context * push_scope(struct gen_context *ctx, enum scope_class class, diff --git a/src/identifier.c b/src/identifier.c @@ -60,6 +60,44 @@ identifier_unparse_static(const struct identifier *ident, char *buf, size_t len) return n; } +const char * +ident_to_path(const struct identifier *ident) +{ + static char path[PATH_MAX+1]; + memset(path, 0, sizeof(path)); + while (ident) { + size_t len = strlen(ident->name); + size_t oldlen = strlen(path); + if (oldlen != 0) { + len++; + } + memmove(path + len, path, oldlen); + memcpy(path, ident->name, strlen(ident->name)); + if (oldlen != 0) { + path[strlen(ident->name)] = '/'; + } + ident = ident->ns; + } + return path; +} + +char * +ident_to_sym(const struct identifier *ident) +{ + if (ident->ns) { + char *ns = ident_to_sym(ident->ns); + if (!ns) { + return NULL; + } + int n = snprintf(NULL, 0, "%s.%s", ns, ident->name); + char *str = xcalloc(1, n + 1); + snprintf(str, n + 1, "%s.%s", ns, ident->name); + free(ns); + return str; + } + return strdup(ident->name); +} + void identifier_dup(struct identifier *new, const struct identifier *ident) { diff --git a/src/main.c b/src/main.c @@ -12,6 +12,7 @@ #include "parse.h" #include "qbe.h" #include "typedef.h" +#include "type_store.h" #include "util.h" static void @@ -127,8 +128,9 @@ main(int argc, char *argv[]) return 0; } - struct context ctx = {0}; - check(&ctx, &aunit, &unit); + struct type_store ts = {0}; + builtin_types_init(); + check(&ts, &aunit, &unit); if (stage == STAGE_CHECK) { return 0; } diff --git a/src/mod.c b/src/mod.c @@ -1,12 +1,60 @@ #include <assert.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include "check.h" #include "identifier.h" +#include "lex.h" #include "mod.h" +#include "parse.h" #include "scope.h" #include "type_store.h" +#include "util.h" + +static const char * +open_typedefs(struct identifier *ident) +{ + char *sym = ident_to_sym(ident); + char versenv[PATH_MAX+1]; + snprintf(versenv, sizeof(versenv), "HARE.%s.VERSION", sym); + + char *version = getenv(versenv); + if (!version) { + version = sym; + } else { + version = strdup(version); + free(sym); + } + + const struct pathspec paths[] = { + {.var = "HARECACHE", .path = "/%s/%s.td"}, + {.var = "XDG_CACHE_HOME", .path = "/hare/%s/%s.td"}, + {.var = "HOME", .path = "/.cache/hare/%s/%s.td"} + }; + char *pathfmt = getpath(paths, sizeof(paths) / sizeof(paths[0])); + assert(pathfmt); + + static char path[PATH_MAX+1]; + const char *ipath = ident_to_path(ident); + snprintf(path, sizeof(path), pathfmt, ipath, version); + free(version); + + return path; +} struct scope * module_resolve(struct identifier *ident, struct type_store *store) { + struct lexer lexer = {0}; + struct ast_unit aunit = {0}; + + const char *path = open_typedefs(ident); + FILE *f = fopen(path, "r"); + lex_init(&lexer, f, path); + parse(&lexer, &aunit.subunits); + lex_finish(&lexer); + (void)aunit; + assert(0); // TODO } diff --git a/src/util.c b/src/util.c @@ -1,7 +1,11 @@ #include <stdlib.h> #include <stdint.h> -// Do not include this header: -//#include "util.h" +#include <string.h> +#include "util.h" +// Remove safety macros: +#undef malloc +#undef calloc +#undef realloc uint32_t fnv1a(uint32_t hash, unsigned char c) @@ -48,3 +52,20 @@ xrealloc(void *p, size_t s) } return p; } + +char * +getpath(const struct pathspec *paths, size_t npaths) { + for (size_t i = 0; i < npaths; i++) { + const char *var = ""; + if (paths[i].var) { + var = getenv(paths[i].var); + } + if (var) { + char *out = calloc(1, + strlen(var) + strlen(paths[i].path) + 1); + strcat(strcat(out, var), paths[i].path); + return out; + } + } + return NULL; +}