harec

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

commit de0f374da023d1aade805bb0198bf45f76eba71b
parent f85f1f7cdcfccb683497172a70bfc687a0595680
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sat, 20 Mar 2021 10:42:03 -0400

Implement -D (define) option

Diffstat:
Minclude/check.h | 26++++++++++++++++++--------
Minclude/lex.h | 1+
Minclude/mod.h | 5++++-
Minclude/parse.h | 4++++
Msrc/check.c | 51+++++++++++++++++++++++++++++++++++++++++----------
Msrc/lex.c | 3++-
Msrc/main.c | 46++++++++++++++++++++++++++++++++++++++++++++--
Msrc/mod.c | 7++++---
Msrc/parse.c | 7++-----
9 files changed, 120 insertions(+), 30 deletions(-)

diff --git a/include/check.h b/include/check.h @@ -17,8 +17,19 @@ struct modcache { struct modcache *next; }; +struct ast_expression; +struct ast_unit; + +struct define { + struct identifier ident; + struct ast_type *type; + struct ast_expression *initializer; + struct define *next; +}; + struct context { struct modcache **modcache; + struct define *defines; struct type_store *store; const struct type *fntype; struct identifier *ns; @@ -88,20 +99,19 @@ struct unit { struct imports *imports; }; -struct ast_expression; -struct ast_unit; - struct scope *check(struct type_store *ts, struct build_tags *tags, + struct define *defines, const struct ast_unit *aunit, struct unit *unit); struct scope *check_internal(struct type_store *ts, - struct modcache **cache, - struct build_tags *tags, - const struct ast_unit *aunit, - struct unit *unit, - bool scan_only); + struct modcache **cache, + struct build_tags *tags, + struct define *defines, + const struct ast_unit *aunit, + struct unit *unit, + bool scan_only); struct errors { struct location loc; diff --git a/include/lex.h b/include/lex.h @@ -163,6 +163,7 @@ struct lexer { uint32_t c[2]; struct token un; struct location loc; + bool disable_labels; }; void lex_init(struct lexer *lexer, FILE *f, const char *filename); diff --git a/include/mod.h b/include/mod.h @@ -5,7 +5,10 @@ #include "type_store.h" struct modcache; +struct define; struct scope *module_resolve(struct modcache *cache[], - struct identifier *ident, struct type_store *store); + struct define *defines, + struct identifier *ident, + struct type_store *store); #endif diff --git a/include/parse.h b/include/parse.h @@ -2,10 +2,14 @@ #define HAREC_PARSE_H #include <stdio.h> +struct ast_expression; struct ast_subunit; +struct ast_type; struct lexer; void parse(struct lexer *lexer, struct ast_subunit *unit); bool parse_identifier(struct lexer *lexer, struct identifier *ident, bool trailing); +struct ast_type *parse_type(struct lexer *lexer); +struct ast_expression *parse_simple_expression(struct lexer *lexer); #endif diff --git a/src/check.c b/src/check.c @@ -3295,7 +3295,8 @@ static void load_import(struct context *ctx, struct ast_imports *import, struct type_store *ts, struct scope *scope) { - struct scope *mod = module_resolve(ctx->modcache, &import->ident, ts); + struct scope *mod = module_resolve(ctx->modcache, + ctx->defines, &import->ident, ts); switch (import->mode) { case AST_IMPORT_IDENTIFIER: @@ -3385,11 +3386,12 @@ load_import(struct context *ctx, struct ast_imports *import, struct scope * check_internal(struct type_store *ts, - struct modcache **cache, - struct build_tags *tags, - const struct ast_unit *aunit, - struct unit *unit, - bool scan_only) + struct modcache **cache, + struct build_tags *tags, + struct define *defines, + const struct ast_unit *aunit, + struct unit *unit, + bool scan_only) { struct context ctx = {0}; ctx.ns = unit->ns; @@ -3397,6 +3399,7 @@ check_internal(struct type_store *ts, ctx.store = ts; ctx.store->check_context = &ctx; ctx.modcache = cache; + ctx.defines = defines; // Top-level scope management involves: // @@ -3408,6 +3411,33 @@ check_internal(struct type_store *ts, // sub-scopes for each declaration, expression-list, etc. ctx.unit = scope_push(&ctx.scope, TR_MAX); + // Install defines (-D on the command line) + // XXX: This duplicates a lot of code with scan_const + for (struct define *def = defines; def; def = def->next) { + struct location loc = { + .path = "-D", .lineno = 1, .colno = 1, + }; + const struct type *type = type_store_lookup_atype( + ctx.store, def->type); + expect(&loc, type != NULL, "Unable to resolve type"); + struct expression *initializer = + xcalloc(1, sizeof(struct expression)); + struct errors *errors = check_expression(&ctx, + def->initializer, initializer, type, NULL); + // TODO: This could be more detailed + expect(&loc, errors == NULL, "Invalid initializer"); + expect(&loc, type_is_assignable(type, initializer->result), + "Constant type is not assignable from initializer type"); + initializer = lower_implicit_cast(type, initializer); + struct expression *value = + xcalloc(1, sizeof(struct expression)); + enum eval_result r = eval_expr(&ctx, initializer, value); + expect(&loc, r == EVAL_OK, + "Unable to evaluate constant initializer at compile time"); + scope_insert(ctx.unit, O_CONST, + &def->ident, &def->ident, type, value); + } + struct scopes *subunit_scopes; struct scopes **next = &subunit_scopes; struct imports **inext = &unit->imports; @@ -3526,11 +3556,12 @@ check_internal(struct type_store *ts, struct scope * check(struct type_store *ts, - struct build_tags *tags, - const struct ast_unit *aunit, - struct unit *unit) + struct build_tags *tags, + struct define *defines, + const struct ast_unit *aunit, + struct unit *unit) { struct modcache *modcache[MODCACHE_BUCKETS]; memset(modcache, 0, sizeof(modcache)); - return check_internal(ts, modcache, tags, aunit, unit, false); + return check_internal(ts, modcache, tags, defines, aunit, unit, false); } diff --git a/src/lex.c b/src/lex.c @@ -756,7 +756,8 @@ lex2(struct lexer *lexer, struct token *out, uint32_t c) break; default: push(lexer, c, false); - if (c <= 0x7F && (isalpha(c) || c == '_')) { + if (!lexer->disable_labels && c <= 0x7F + && (isalpha(c) || c == '_')) { return lex_label(lexer, out); } out->token = T_COLON; diff --git a/src/main.c b/src/main.c @@ -53,6 +53,42 @@ parse_stage(const char *s) } } +static struct define * +parse_define(const char *argv_0, const char *in) +{ + struct define *def = xcalloc(1, sizeof(struct define)); + + struct token tok; + struct lexer lexer; + FILE *f = fmemopen((char *)in, strlen(in), "r"); + lex_init(&lexer, f, "-D"); + + // The syntax for this parameter is: + // + // -D ident:type=value + // + // :type is lexed as a label unless we disable it here. + lexer.disable_labels = true; + + parse_identifier(&lexer, &def->ident, false); + if (lex(&lexer, &tok) != T_COLON) { + lex_finish(&lexer); + usage(argv_0); + exit(1); + } + def->type = parse_type(&lexer); + + if (lex(&lexer, &tok) != T_EQUAL) { + lex_finish(&lexer); + usage(argv_0); + exit(1); + } + def->initializer = parse_simple_expression(&lexer); + + lex_finish(&lexer); + return def; +} + int main(int argc, char *argv[]) { @@ -60,10 +96,16 @@ main(int argc, char *argv[]) struct build_tags *tags = NULL; struct unit unit = {0}; struct lexer lexer; + struct define *defines, *def; int c; - while ((c = getopt(argc, argv, "o:T:t:N:")) != -1) { + while ((c = getopt(argc, argv, "D:o:T:t:N:")) != -1) { switch (c) { + case 'D': + def = parse_define(argv[0], optarg); + def->next = defines; + defines = def; + break; case 'o': output = optarg; break; @@ -137,7 +179,7 @@ main(int argc, char *argv[]) struct type_store ts = {0}; builtin_types_init(); - check(&ts, tags, &aunit, &unit); + check(&ts, tags, defines, &aunit, &unit); if (stage == STAGE_CHECK) { return 0; } diff --git a/src/mod.c b/src/mod.c @@ -64,8 +64,9 @@ open_typedefs(struct identifier *ident) struct scope * module_resolve(struct modcache *cache[], - struct identifier *ident, - struct type_store *store) + struct define *defines, + struct identifier *ident, + struct type_store *store) { uint32_t hash = identifier_hash(FNV1A_INIT, ident); struct modcache **bucket = &cache[hash % MODCACHE_BUCKETS]; @@ -94,7 +95,7 @@ module_resolve(struct modcache *cache[], // TODO: Free unused bits struct unit u = {0}; struct scope *scope = check_internal(store, - cache, NULL, &aunit, &u, true); + cache, NULL, defines, &aunit, &u, true); bucket = &cache[hash % MODCACHE_BUCKETS]; struct modcache *item = xcalloc(1, sizeof(struct modcache)); diff --git a/src/parse.c b/src/parse.c @@ -256,8 +256,6 @@ parse_imports(struct lexer *lexer, struct ast_subunit *subunit) trleave(TR_PARSE, NULL); } -static struct ast_type *parse_type(struct lexer *lexer); - static void parse_parameter_list(struct lexer *lexer, struct ast_function_type *type) { @@ -444,7 +442,6 @@ parse_primitive_type(struct lexer *lexer) return type; } -static struct ast_expression *parse_simple_expression(struct lexer *lexer); static struct ast_expression *parse_complex_expression(struct lexer *lexer); static struct ast_expression *parse_compound_expression(struct lexer *lexer); static struct ast_expression *parse_postfix_expression(struct lexer *lexer, @@ -664,7 +661,7 @@ parse_tagged_or_tuple_type(struct lexer *lexer) assert(0); // Unreachable } -static struct ast_type * +struct ast_type * parse_type(struct lexer *lexer) { trenter(TR_PARSE, "type"); @@ -1735,7 +1732,7 @@ parse_bin_expression(struct lexer *lexer, struct ast_expression *lvalue, int i) return lvalue; } -static struct ast_expression * +struct ast_expression * parse_simple_expression(struct lexer *lexer) { return parse_bin_expression(lexer, NULL, 0);