diff options
-rw-r--r-- | ast.c | 48 | ||||
-rw-r--r-- | ast.h | 5 | ||||
-rw-r--r-- | eval.c | 32 | ||||
-rw-r--r-- | eval.h | 3 | ||||
-rw-r--r-- | lib.c | 100 |
5 files changed, 125 insertions, 63 deletions
@@ -370,6 +370,54 @@ ut_new_func(struct ut_state *s, struct ut_op *decl, struct ut_scope *scope) } static void +exception_free(struct json_object *v, void *ud) +{ + free(ud); +} + +__attribute__((format(printf, 3, 0))) struct json_object * +ut_new_exception(struct ut_state *s, uint32_t off, const char *fmt, ...) +{ + struct ut_op *op, *failing_op; + va_list ap; + ssize_t sz; + char *p; + + sz = ALIGN(sizeof(*op)); + + if (s->filename) + sz += ALIGN(strlen(s->filename) + 1); + + failing_op = ut_get_op(s, off); + + op = xalloc(sz); + op->type = T_EXCEPTION; + op->off = failing_op ? failing_op->off : 0; + + if (s->filename) { + p = (char *)op + ALIGN(sizeof(*op)); + op->tag.data = strcpy(p, s->filename); + } + + va_start(ap, fmt); + sz = xvasprintf(&p, fmt, ap); + va_end(ap); + + op->val = xjs_new_string_len(p, sz); + free(p); + + json_object_set_userdata(op->val, op, exception_free); + + if (s->error.code == UT_ERROR_EXCEPTION) + json_object_put(s->error.info.exception); + + s->error.code = UT_ERROR_EXCEPTION; + s->error.info.exception = op->val; + + return json_object_get(op->val); +} + +static void scope_free(struct json_object *v, void *ud) { struct ut_scope *sc = ud; @@ -165,6 +165,11 @@ struct json_object *ut_new_double(double v); struct json_object *ut_new_null(void); struct json_object *ut_new_regexp(const char *source, bool icase, bool newline, bool global, char **err); +__attribute__((format(printf, 3, 0))) +struct json_object *ut_new_exception(struct ut_state *s, uint32_t off, const char *fmt, ...); + +#define ut_exception ut_new_exception + struct ut_scope *ut_new_scope(struct ut_state *s, struct ut_scope *parent); struct ut_scope *ut_parent_scope(struct ut_scope *scope); struct ut_scope *ut_acquire_scope(struct ut_scope *scope); @@ -26,38 +26,6 @@ #include <stdarg.h> #include <regex.h> -char exception_tag_space[sizeof(struct ut_op) + sizeof(struct ut_op *)]; -static struct ut_op *exception_tag = (struct ut_op *)exception_tag_space; - -__attribute__((format(printf, 3, 0))) struct json_object * -ut_exception(struct ut_state *state, uint32_t off, const char *fmt, ...) -{ - struct json_object *msg; - va_list ap; - char *s; - int len; - - va_start(ap, fmt); - len = xvasprintf(&s, fmt, ap); - va_end(ap); - - msg = xjs_new_string_len(s, len); - free(s); - - exception_tag->type = T_EXCEPTION; - exception_tag->tree.operand[0] = off; - - json_object_set_userdata(msg, exception_tag, NULL); - - if (state->error.code == UT_ERROR_EXCEPTION) - json_object_put(state->error.info.exception); - - state->error.code = UT_ERROR_EXCEPTION; - state->error.info.exception = msg; - - return json_object_get(msg); -} - bool ut_val_is_truish(struct json_object *val) { @@ -24,9 +24,6 @@ #include "ast.h" -__attribute__((format(printf, 3, 0))) struct json_object * -ut_exception(struct ut_state *state, uint32_t op, const char *fmt, ...); - bool ut_cmp(int how, struct json_object *v1, struct json_object *v2); @@ -64,47 +64,29 @@ snprintf_append(char **dptr, size_t *dlen, const char *fmt, ssize_t sz, ...) snprintf_append(dptr, dlen, fmt, -1, ##__VA_ARGS__) static void -format_error_context(char **msg, size_t *msglen, const char *path, const char *expr, size_t off) +format_context_line(char **msg, size_t *msglen, const char *line, size_t off) { - int eoff, eline, padlen; - const char *p, *nl; - int i; - - /* skip lines until error line */ - for (p = nl = expr, eline = 0; *p && p < expr + off; p++) { - if (*p == '\n') { - nl = p + 1; - eline++; - } - } - - eoff = p - nl; - - if (path) - sprintf_append(msg, msglen, "In %s, ", path); - else - sprintf_append(msg, msglen, "In "); - - sprintf_append(msg, msglen, "line %u, byte %d:\n\n `", eline + 1, eoff); + const char *p; + int padlen, i; - for (p = nl, padlen = 0; *p != '\n' && *p != '\0'; p++) { + for (p = line, padlen = 0; *p != '\n' && *p != '\0'; p++) { switch (*p) { case '\t': sprintf_append(msg, msglen, " "); - if (p < nl + eoff) + if (p < line + off) padlen += 4; break; case '\r': case '\v': sprintf_append(msg, msglen, " "); - if (p < nl + eoff) + if (p < line + off) padlen++; break; default: sprintf_append(msg, msglen, "%c", *p); - if (p < nl + eoff) + if (p < line + off) padlen++; } } @@ -127,14 +109,72 @@ format_error_context(char **msg, size_t *msglen, const char *path, const char *e } } +static void +format_error_context(char **msg, size_t *msglen, const char *path, const char *expr, size_t off) +{ + const char *p, *nl; + size_t len, rlen; + bool truncated; + char buf[256]; + int eline; + FILE *f; + + if (path) { + f = fopen(path, "rb"); + + if (f) { + truncated = false; + eline = 1; + rlen = 0; + + while (fgets(buf, sizeof(buf), f)) { + len = strlen(buf); + rlen += len; + + if (rlen > off) { + sprintf_append(msg, msglen, "In %s, line %u, byte %d:\n\n `%s", + path, eline, len - (rlen - off) + (truncated ? sizeof(buf) : 1), + truncated ? "..." : ""); + + format_context_line(msg, msglen, buf, + len - (rlen - off) + (truncated ? 3 : 0)); + + break; + } + + truncated = (len > 0 && buf[len-1] != '\n'); + eline += !truncated; + } + } + else { + sprintf_append(msg, msglen, "In %s, byte offset %zu [Unable to read source context]\n", + path, off); + } + + fclose(f); + } + else if (expr) { + /* skip lines until error line */ + for (p = nl = expr, eline = 1; *p && p < expr + off; p++) { + if (*p == '\n') { + nl = p + 1; + eline++; + } + } + + sprintf_append(msg, msglen, "In line %u, byte %d:\n\n `", eline, p - nl); + format_context_line(msg, msglen, nl, p - nl); + } +} + char * ut_format_error(struct ut_state *state, const char *expr) { + char *msg = NULL, *filename = state->filename; size_t off = state ? state->off : 0; struct ut_op *tag; bool first = true; size_t msglen = 0; - char *msg = NULL; int i, max_i; switch (state ? state->error.code : UT_ERROR_OUT_OF_MEMORY) { @@ -207,14 +247,18 @@ ut_format_error(struct ut_state *state, const char *expr) case UT_ERROR_EXCEPTION: tag = json_object_get_userdata(state->error.info.exception); - off = (tag && tag->tree.operand[0]) ? ut_get_op(state, tag->tree.operand[0])->off : 0; + + if (tag && tag->type == T_EXCEPTION) { + off = tag->off; + filename = tag->tag.data; + } sprintf_append(&msg, &msglen, "%s\n", json_object_get_string(state->error.info.exception)); break; } if (off) - format_error_context(&msg, &msglen, state->filename, expr, off); + format_error_context(&msg, &msglen, filename, expr, off); return msg; } |