From 5b038666c3e911ffeb78a4d656044e5bd076705b Mon Sep 17 00:00:00 2001 From: Kimplul Date: Thu, 23 Mar 2023 16:14:51 +0200 Subject: integrate parser work --- src/debug.c | 246 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 src/debug.c (limited to 'src/debug.c') diff --git a/src/debug.c b/src/debug.c new file mode 100644 index 0000000..e18ae70 --- /dev/null +++ b/src/debug.c @@ -0,0 +1,246 @@ +#include +#include +#include +#include +#include +#include + +#include + +const char *issue_level_str(enum issue_level level) +{ + switch (level) { + case SRC_INFO: return "info"; + case SRC_WARN: return "warn"; + case SRC_ERROR: return "error"; + } + + return "unknown"; +} + +static const char *find_lineno(const char *buf, size_t no) +{ + if (no == 1) + return buf; + + char c; + while ((c = *buf)) { + buf++; + + if (c == '\n') + no--; + + if (no == 1) + break; + } + + return buf; +} + +static void _issue(struct src_issue issue, const char *fmt, va_list args) +{ + /* get start and end of current line in buffer */ + const char *line_start = find_lineno(issue.fctx.fbuf, issue.loc.first_line); + const char *line_end = strchr(line_start, '\n'); + if (!line_end) + line_end = strchr(line_start, 0); + + const int line_len = line_end - line_start; + + fprintf(stderr, "%s:%i:%i: %s: ", issue.fctx.fname, + issue.loc.first_line, + issue.loc.first_col, + issue_level_str(issue.level)); + + vfprintf(stderr, fmt, args); + fputc('\n', stderr); + + int lineno_len = snprintf(NULL, 0, "%i", issue.loc.first_line); + fputc(' ', stderr); + fprintf(stderr, "%i | ", issue.loc.first_line); + + fprintf(stderr, "%.*s\n", line_len, line_start); + + for (int i = 0; i < lineno_len + 2; ++i) + fputc(' ', stderr); + + fprintf(stderr, "| "); + + for (int i = 0; i < issue.loc.first_col - 1; ++i) + fputc(line_start[i] == '\t' ? '\t' : ' ', stderr); + + for (int i = issue.loc.first_col; i < issue.loc.last_col; ++i) { + if (i == issue.loc.first_col) + fputc('^', stderr); + else + fputc('~', stderr); + } + + fputc('\n', stderr); +} + +void src_issue(struct src_issue issue, const char *err_msg, ...) +{ + va_list args; + va_start(args, err_msg); + _issue(issue, err_msg, args); + va_end(args); +} + +/* TODO: should really implement these better, the bad error messages are + * starting to play a role in debugging */ +void semantic_error(struct file_ctx fctx, struct ast_node *node, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + struct src_issue issue; + issue.level = SRC_ERROR; + issue.loc = node->loc; + issue.fctx = fctx; + _issue(issue, fmt, args); + va_end(args); +} + +void semantic_warn(struct file_ctx fctx, struct ast_node *node, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + struct src_issue issue; + issue.level = SRC_WARN; + issue.loc = node->loc; + issue.fctx = fctx; + _issue(issue, fmt, args); + va_end(args); +} + +void semantic_info(struct file_ctx fctx, struct ast_node *node, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + struct src_issue issue; + issue.level = SRC_INFO; + issue.loc = node->loc; + issue.fctx = fctx; + _issue(issue, fmt, args); + va_end(args); +} + +void internal_error(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + fprintf(stderr, "internal error: "); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); +} + +static void _type_str(FILE *fp, struct ast_node *type) +{ + if (!type) + return; + + assert(type->node_type == AST_TYPE); + + switch (type->_type.kind) { + case AST_TYPE_POINTER: + fputc('\'', fp); + break; + + case AST_TYPE_ID: { + struct ast_node *id = type->_type.id; + fprintf(fp, "%s", id->_id.id); + break; + } + + case AST_TYPE_TEMPLATE: { + struct ast_node *template = type->_type.template.template; + assert(template); + + struct ast_node *template_id = template->_template.id; + struct ast_node *template_act = type->_type.template.actual; + if (template_act) { + fprintf(fp, "%s as ", template_id->_id.id); + _type_str(fp, template_act); + } + else { + fprintf(fp, "%s", template_id->_id.id); + } + break; + } + + case AST_TYPE_ALIAS: { + struct ast_node *alias = type->_type.alias.alias; + assert(alias); + + struct ast_node *alias_id = alias->_alias.id; + struct ast_node *alias_act = type->_type.alias.actual; + if (alias_act) { + /* deeply nested aliases look pretty funny here */ + fprintf(fp, "%s aka ", alias_id->_id.id); + _type_str(fp, alias_act); + } + else { + fprintf(fp, "%s", alias_id->_id.id); + } + break; + } + } + + _type_str(fp, type->_type.next); +} + +char *type_str(struct ast_node *node) +{ + /* maybe hacky? */ + if (!node) + return strdup("void"); + + char *buf = NULL; size_t size = 0; + FILE *memstream = open_memstream(&buf, &size); + + /* TODO: improve template detection */ + /* we were given a plain type, pass it directly along to _type_str */ + if (node->node_type == AST_TYPE) + _type_str(memstream, node); + else + /* otherwise, try to fish out the type of the node */ + _type_str(memstream, node->type); + + fclose(memstream); + return buf; +} + +static void _call_str(FILE *f, struct ast_node *call) +{ + struct ast_node *id = call->_call.id; + const char *id_str = id->_id.id; + fprintf(f, "%s", id_str); + + struct ast_node *args = call->_call.args; + fprintf(f, "("); + + while (args) { + char *type = type_str(args); + fprintf(f, "%s", type); + free(type); + + args = args->next; + if (args) + fprintf(f, ", "); + else + break; + } + + fprintf(f, ")"); +} + +char *call_str(struct ast_node *call) +{ + assert(call->node_type == AST_CALL); + char *buf = NULL; size_t size = 0; + FILE *memstream = open_memstream(&buf, &size); + _call_str(memstream, call); + fclose(memstream); + return buf; +} -- cgit v1.3