diff options
author | Jo-Philipp Wich <jo@mein.io> | 2020-10-13 19:01:02 +0200 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2020-10-14 12:23:08 +0200 |
commit | 07c147a1803270aba871c9024e021fd913cb8e2a (patch) | |
tree | d05fcecf8afc6e82b6c659b272d23029a6cc9249 | |
parent | 05cc0ee7a7c7dc442e22dfdc22c0574ac6b3e71b (diff) |
treewide: unify error handling
Get rid of the distinction between lexer/parser errors and runtime
exceptions, use exceptions everywhere instead.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
-rw-r--r-- | ast.c | 75 | ||||
-rw-r--r-- | ast.h | 42 | ||||
-rw-r--r-- | eval.c | 74 | ||||
-rw-r--r-- | eval.h | 2 | ||||
-rw-r--r-- | lexer.c | 71 | ||||
-rw-r--r-- | lib.c | 303 | ||||
-rw-r--r-- | lib.h | 4 | ||||
-rw-r--r-- | main.c | 73 | ||||
-rw-r--r-- | parser.y | 19 | ||||
-rw-r--r-- | tests/00_syntax/01_unterminated_comment | 2 | ||||
-rw-r--r-- | tests/00_syntax/05_block_nesting | 2 | ||||
-rw-r--r-- | tests/02_runtime/04_switch_case | 6 |
12 files changed, 260 insertions, 413 deletions
@@ -391,7 +391,7 @@ add_stacktrace(struct json_object *a, struct ut_source *source, const char *func if (rlen > off) { json_object_object_add(o, "line", xjs_new_int64(line)); - json_object_object_add(o, "byte", xjs_new_int64(len - (rlen - off) + (truncated ? sizeof(buf) : 0))); + json_object_object_add(o, "byte", xjs_new_int64(len - (rlen - off) + (truncated ? sizeof(buf) : 0) + 1)); break; } @@ -406,51 +406,42 @@ __attribute__((format(printf, 3, 4))) struct json_object * ut_new_exception(struct ut_state *s, uint32_t off, const char *fmt, ...) { struct ut_callstack *callstack; - struct ut_op *op, *failing_op; struct json_object *a; + struct ut_op *op; va_list ap; - ssize_t sz; char *p; + int len; - sz = ALIGN(sizeof(*op)); - - if (s->source && s->source->filename) - sz += ALIGN(strlen(s->source->filename) + 1); - - failing_op = ut_get_op(s, off); - - op = xalloc(sz); + op = xalloc(sizeof(*op)); op->type = T_EXCEPTION; - op->off = failing_op ? failing_op->off : 0; - - if (s->source && s->source->filename) { - p = (char *)op + ALIGN(sizeof(*op)); - op->tag.data = strcpy(p, s->source->filename); - } - - va_start(ap, fmt); - sz = xvasprintf(&p, fmt, ap); - va_end(ap); - op->val = xjs_new_object(); + op->off = off; + op->tag.data = s->source; a = xjs_new_array(); - add_stacktrace(a, s->function->source, s->function->name, failing_op ? failing_op->off : 0); + add_stacktrace(a, + s->function ? s->function->source : s->source, + s->function ? s->function->name : NULL, + off); - for (callstack = s->callstack ? s->callstack->next : NULL; callstack && callstack->next; callstack = callstack->next) - add_stacktrace(a, callstack->source, callstack->funcname, callstack->off); + for (callstack = s->callstack ? s->callstack->next : NULL; callstack; callstack = callstack->next) + if (callstack->off) + add_stacktrace(a, callstack->source, callstack->funcname, callstack->off); json_object_object_add(op->val, "stacktrace", a); - json_object_object_add(op->val, "message", xjs_new_string_len(p, sz)); + va_start(ap, fmt); + len = xvasprintf(&p, fmt, ap); + va_end(ap); + + json_object_object_add(op->val, "message", xjs_new_string_len(p, len)); free(p); - if (s->error.code == UT_ERROR_EXCEPTION) - json_object_put(s->error.info.exception); + if (s->exception) + json_object_put(s->exception); - s->error.code = UT_ERROR_EXCEPTION; - s->error.info.exception = op->val; + s->exception = op->val; json_object_set_serializer(op->val, exception_to_string, op, exception_free); @@ -518,12 +509,8 @@ ut_parent_scope(struct ut_scope *scope) static void ut_reset(struct ut_state *s) { - if (s->error.code == UT_ERROR_EXCEPTION) - json_object_put(s->error.info.exception); - else if (s->error.code == UT_ERROR_INVALID_REGEXP) - free(s->error.info.regexp_error); - - memset(&s->error, 0, sizeof(s->error)); + json_object_put(s->exception); + s->exception = NULL; free(s->lex.lookbehind); free(s->lex.buf); @@ -577,34 +564,28 @@ ut_free(struct ut_state *s) free(s); } -enum ut_error_type +struct json_object * ut_parse(struct ut_state *s, FILE *fp) { struct ut_op *op; void *pParser; uint32_t off; - if (!s) - return UT_ERROR_OUT_OF_MEMORY; - ut_reset(s); - pParser = ParseAlloc(malloc); - - if (!pParser) - return UT_ERROR_OUT_OF_MEMORY; + pParser = ParseAlloc(xalloc); while (s->lex.state != UT_LEX_EOF) { off = ut_get_token(s, fp); op = ut_get_op(s, off); - if (s->error.code) + if (s->exception) goto out; if (op) Parse(pParser, op->type, off, s); - if (s->error.code) + if (s->exception) goto out; } @@ -613,7 +594,7 @@ ut_parse(struct ut_state *s, FILE *fp) out: ParseFree(pParser, free); - return s->error.code; + return s->exception; } bool @@ -33,21 +33,6 @@ #define JSON_C_TO_STRING_STRICT (1<<31) -enum ut_error_type { - UT_ERROR_NO_ERROR, - UT_ERROR_OUT_OF_MEMORY, - UT_ERROR_UNTERMINATED_COMMENT, - UT_ERROR_UNTERMINATED_STRING, - UT_ERROR_UNTERMINATED_BLOCK, - UT_ERROR_UNEXPECTED_TOKEN, - UT_ERROR_UNEXPECTED_CHAR, - UT_ERROR_OVERLONG_STRING, - UT_ERROR_INVALID_ESCAPE, - UT_ERROR_NESTED_BLOCKS, - UT_ERROR_INVALID_REGEXP, - UT_ERROR_EXCEPTION -}; - enum ut_lex_state { UT_LEX_IDENTIFY_BLOCK, UT_LEX_BLOCK_COMMENT_START, @@ -123,7 +108,6 @@ struct ut_state { uint8_t trim_blocks:1; uint8_t lstrip_blocks:1; uint8_t strict_declarations:1; - uint8_t skip_shebang:1; struct { enum ut_lex_state state; uint8_t eof:1; @@ -144,15 +128,7 @@ struct ut_state { int lead_surrogate; size_t lastoff; } lex; - struct { - enum ut_error_type code; - union { - struct json_object *exception; - uint64_t tokens[2]; - char *regexp_error; - } info; - } error; - struct json_object *ctx, *rval; + struct json_object *ctx, *rval, *exception; struct ut_scope *scopelist, *scope; struct ut_source *sources, *source; struct ut_callstack *callstack; @@ -179,22 +155,10 @@ static inline bool ut_is_type(struct json_object *val, int type) { }; -#define UT_ET_DIV (sizeof(s->error.info.tokens[0]) * 8) -#define UT_ET_TYPE typeof(s->error.info.tokens[0]) - -static inline void ut_set_error_token(struct ut_state *s, int tokennr) { - s->error.info.tokens[tokennr / UT_ET_DIV] |= ((UT_ET_TYPE)1 << (tokennr % UT_ET_DIV)); -} - -static inline bool ut_is_error_token(struct ut_state *s, int tokennr) { - return (s->error.info.tokens[tokennr / UT_ET_DIV] & ((UT_ET_TYPE)1 << (tokennr % UT_ET_DIV))); -} - - uint32_t ut_new_op(struct ut_state *s, int type, struct json_object *val, ...); uint32_t ut_wrap_op(struct ut_state *s, uint32_t parent, ...); uint32_t ut_append_op(struct ut_state *s, uint32_t a, uint32_t b); -enum ut_error_type ut_parse(struct ut_state *s, FILE *fp); +struct json_object *ut_parse(struct ut_state *s, FILE *fp); void ut_free(struct ut_state *s); struct json_object *ut_new_func(struct ut_state *s, struct ut_op *decl, struct ut_scope *scope); @@ -206,8 +170,6 @@ struct json_object *ut_new_regexp(const char *source, bool icase, bool newline, __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); @@ -252,9 +252,9 @@ ut_getref(struct ut_state *state, uint32_t off, struct json_object **key) if (!next) { if (state->strict_declarations) { - return ut_exception(state, off, - "Reference error: access to undeclared variable %s", - json_object_get_string(op->val)); + return ut_new_exception(state, op->off, + "Reference error: access to undeclared variable %s", + json_object_get_string(op->val)); } break; @@ -279,8 +279,7 @@ ut_getref(struct ut_state *state, uint32_t off, struct json_object **key) static struct json_object * ut_getref_required(struct ut_state *state, uint32_t off, struct json_object **key) { - struct ut_op *op = ut_get_op(state, off); - uint32_t off1 = op ? op->tree.operand[0] : 0; + struct ut_op *op1 = ut_get_child(state, off, 0); struct json_object *scope, *skey, *rv; char *lhs, *p = NULL; @@ -289,7 +288,7 @@ ut_getref_required(struct ut_state *state, uint32_t off, struct json_object **ke if (!json_object_is_type(scope, json_type_array) && !json_object_is_type(scope, json_type_object)) { if (!ut_is_type(scope, T_EXCEPTION)) { - lhs = off1 ? ut_ref_to_str(state, off1) : NULL; + lhs = op1 ? ut_ref_to_str(state, ut_get_off(state, op1)) : NULL; if (lhs) { p = alloca_sprintf("Type error: the result of `%s` is %s", lhs, @@ -298,7 +297,7 @@ ut_getref_required(struct ut_state *state, uint32_t off, struct json_object **ke } json_object_put(scope); - rv = ut_exception(state, off1, p ? p : "Type error: left-hand side is not an array or object"); + rv = ut_new_exception(state, op1->off, p ? p : "Type error: left-hand side is not an array or object"); } else { rv = scope; @@ -494,14 +493,14 @@ ut_execute_for(struct ut_state *state, uint32_t off) } if (init->type != T_IN) - return ut_exception(state, ut_get_off(state, init), - "Syntax error: missing ';' after for loop initializer"); + return ut_new_exception(state, init->off, + "Syntax error: missing ';' after for loop initializer"); ivar = ut_get_op(state, init->tree.operand[0]); if (!ivar || ivar->type != T_LABEL) - return ut_exception(state, ut_get_off(state, init), - "Syntax error: invalid for-in left-hand side"); + return ut_new_exception(state, init->off, + "Syntax error: invalid for-in left-hand side"); val = ut_execute_op(state, init->tree.operand[1]); @@ -1027,9 +1026,9 @@ ut_invoke(struct ut_state *state, uint32_t off, struct json_object *this, case T_BREAK: case T_CONTINUE: json_object_put(rv); - rv = ut_exception(state, ut_get_off(state, tag), - "Syntax error: %s statement must be inside loop", - ut_get_tokenname(tag->type)); + rv = ut_new_exception(state, ut_get_off(state, tag), + "Syntax error: %s statement must be inside loop", + ut_get_tokenname(tag->type)); break; case T_RETURN: @@ -1062,6 +1061,7 @@ static struct json_object * ut_execute_call(struct ut_state *state, uint32_t off) { struct ut_op *decl, *op = ut_get_op(state, off); + struct ut_op *op1 = ut_get_child(state, off, 0); struct json_object *v[2], *rv; char *lhs, *p = NULL; @@ -1070,14 +1070,14 @@ ut_execute_call(struct ut_state *state, uint32_t off) decl = json_object_get_userdata(v[0]); if (!decl || (decl->type != T_FUNC && decl->type != T_CFUNC)) { - lhs = ut_ref_to_str(state, op->tree.operand[0]); + lhs = ut_ref_to_str(state, ut_get_off(state, op1)); if (lhs) { p = alloca_sprintf("Type error: %s is not a function", lhs); free(lhs); } - rv = ut_exception(state, op->tree.operand[0], p ? p : "Type error: left-hand side expression is not a function"); + rv = ut_new_exception(state, op1->off, p ? p : "Type error: left-hand side expression is not a function"); } else { if (v[1] == NULL) @@ -1395,7 +1395,8 @@ ut_execute_try_catch(struct ut_state *state, uint32_t off) json_object_put(ut_setval(state->scope->scope, ut_get_child(state, off, 1)->val, xjs_new_string(json_object_get_string(rv)))); - memset(&state->error, 0, sizeof(state->error)); + json_object_put(state->exception); + state->exception = NULL; json_object_put(rv); rv = ut_execute_op_sequence(state, op->tree.operand[2]); @@ -1433,8 +1434,8 @@ ut_execute_switch_case(struct ut_state *state, uint32_t off) if (Default) { json_object_put(v[0]); - return ut_exception(state, ut_get_off(state, Case), - "Syntax error: more than one switch default case"); + return ut_new_exception(state, Case->off, + "Syntax error: more than one switch default case"); } Default = Case; @@ -1501,9 +1502,9 @@ ut_execute_label(struct ut_state *state, uint32_t off) state->ctx = NULL; if (state->strict_declarations && scope == NULL) { - return ut_exception(state, off, - "Reference error: %s is not defined", - json_object_get_string(op->val)); + return ut_new_exception(state, op->off, + "Reference error: %s is not defined", + json_object_get_string(op->val)); } val = ut_getval(scope, key); @@ -1605,7 +1606,7 @@ ut_execute_op(struct ut_state *state, uint32_t off) struct ut_op *op = ut_get_op(state, off); if (!fns[op->type]) - return ut_exception(state, off, "Runtime error: Unrecognized opcode %d", op->type); + return ut_new_exception(state, op->off, "Runtime error: Unrecognized opcode %d", op->type); return fns[op->type](state, off); } @@ -1675,24 +1676,15 @@ ut_register_variable(struct json_object *scope, const char *key, struct json_obj free(name); } -enum ut_error_type +struct json_object * ut_run(struct ut_state *state, struct json_object *env, struct json_object *modules) { - struct ut_op *op = ut_get_op(state, state->main); - struct json_object *entry, *args, *rv; + struct json_object *args, *rv; size_t i; - if (!op || op->type != T_FUNC) { - ut_exception(state, state->main, "Runtime error: Invalid root operation in AST"); - - return UT_ERROR_EXCEPTION; - } - state->scope = ut_new_scope(state, NULL); state->ctx = NULL; - entry = ut_execute_function(state, state->main); - if (env) { json_object_object_foreach(env, key, val) ut_register_variable(state->scope->scope, key, json_object_get(val)); @@ -1701,9 +1693,9 @@ ut_run(struct ut_state *state, struct json_object *env, struct json_object *modu ut_globals_init(state, state->scope->scope); ut_lib_init(state, state->scope->scope); - args = xjs_new_array(); - if (modules) { + args = xjs_new_array(); + for (i = 0; i < json_object_array_length(modules); i++) { json_object_array_put_idx(args, 0, json_object_get(json_object_array_get_idx(modules, i))); @@ -1719,17 +1711,13 @@ ut_run(struct ut_state *state, struct json_object *env, struct json_object *modu rv); } - json_object_array_del_idx(args, 0, 1); + json_object_put(args); } - rv = ut_invoke(state, state->main, NULL, entry, args); + rv = ut_execute_source(state, state->source, state->scope); out: ut_release_scope(state->scope); - json_object_put(entry); - json_object_put(args); - json_object_put(rv); - - return state->error.code; + return rv; } @@ -36,7 +36,7 @@ ut_cast_number(struct json_object *v, int64_t *n, double *d); struct json_object * ut_invoke(struct ut_state *, uint32_t, struct json_object *, struct json_object *, struct json_object *); -enum ut_error_type +struct json_object * ut_run(struct ut_state *state, struct json_object *env, struct json_object *modules); #endif @@ -319,16 +319,6 @@ static void buf_consume(struct ut_state *s, ssize_t len) { s->source->off += len; } -/* - * Parses a comment from the given buffer. - * - * Returns a negative value on error, otherwise the amount of consumed - * characters from the given buffer. - * - * Error values: - * -UT_ERROR_UNTERMINATED_COMMENT Unterminated string - */ - static uint32_t parse_comment(struct ut_state *s) { @@ -337,7 +327,7 @@ parse_comment(struct ut_state *s) size_t elen; if (!buf_remaining(s)) { - s->error.code = UT_ERROR_UNTERMINATED_COMMENT; + ut_new_exception(s, s->lex.lastoff, "Syntax error: Unterminated comment"); return 0; } @@ -375,18 +365,6 @@ static void append_utf8(struct ut_state *s, int code) { lookbehind_append(s, ustr, up - ustr); } -/* - * Parses a string literal from the given buffer. - * - * Returns a negative value on error, otherwise the amount of consumed - * characters from the given buffer. - * - * Error values: - * -UT_ERROR_UNTERMINATED_STRING Unterminated string - * -UT_ERROR_INVALID_ESCAPE Invalid escape sequence - * -UT_ERROR_OVERLONG_STRING String literal too long - */ - static uint32_t parse_string(struct ut_state *s) { @@ -397,8 +375,7 @@ parse_string(struct ut_state *s) int code; if (!buf_remaining(s)) { - s->error.code = UT_ERROR_UNTERMINATED_STRING; - s->source->off = s->lex.lastoff; + ut_new_exception(s, s->lex.lastoff, "Syntax error: Unterminated string"); return 0; } @@ -465,8 +442,7 @@ parse_string(struct ut_state *s) case 'u': if (s->lex.esclen < 5) { if (!isxdigit(*ptr)) { - s->source->off += s->lex.esclen + 1; - s->error.code = UT_ERROR_INVALID_ESCAPE; + ut_new_exception(s, s->source->off + s->lex.esclen + 1, "Syntax error: Invalid escape sequence"); return 0; } @@ -521,8 +497,8 @@ parse_string(struct ut_state *s) case 'x': if (s->lex.esclen < 3) { if (!isxdigit(*ptr)) { - s->source->off += s->lex.esclen + 1; - s->error.code = UT_ERROR_INVALID_ESCAPE; + ut_new_exception(s, s->source->off + s->lex.esclen + 1, "Syntax error: Invalid escape sequence"); + return 0; } @@ -575,8 +551,7 @@ parse_string(struct ut_state *s) dec(s->lex.esc[3]); if (code > 255) { - s->source->off += s->lex.esclen + 1; - s->error.code = UT_ERROR_INVALID_ESCAPE; + ut_new_exception(s, s->source->off + s->lex.esclen + 1, "Syntax error: Invalid escape sequence"); return 0; } @@ -709,9 +684,8 @@ parse_regexp(struct ut_state *s) op->val = pattern; if (!pattern) { - s->error.info.regexp_error = err; - s->error.code = UT_ERROR_INVALID_REGEXP; - s->source->off = s->lex.lastoff; + ut_new_exception(s, op->off, "Syntax error: %s", err); + free(err); return 0; } @@ -821,21 +795,19 @@ parse_number(struct ut_state *s) if (*e == '.' || *e == 'e' || *e == 'E') { d = strtod(s->lex.lookbehind, &e); - if (e > s->lex.lookbehind && *e == 0) { + if (e > s->lex.lookbehind && *e == 0) rv = emit_op(s, s->source->off - (e - s->lex.lookbehind), T_DOUBLE, ut_new_double(d)); - } - else { - s->error.code = UT_ERROR_INVALID_ESCAPE; - s->source->off -= s->lex.lookbehindlen - (e - s->lex.lookbehind) - 1; - } + else + ut_new_exception(s, s->source->off - (s->lex.lookbehindlen - (e - s->lex.lookbehind) - 1), + "Syntax error: Invalid number literal"); } else if (*e == 0) { rv = emit_op(s, s->source->off - (e - s->lex.lookbehind), T_NUMBER, xjs_new_int64(n)); ut_get_op(s, rv)->is_overflow = (errno == ERANGE); } else { - s->error.code = UT_ERROR_INVALID_ESCAPE; - s->source->off -= s->lex.lookbehindlen - (e - s->lex.lookbehind) - 1; + ut_new_exception(s, s->source->off - (s->lex.lookbehindlen - (e - s->lex.lookbehind) - 1), + "Syntax error: Invalid number literal"); } lookbehind_reset(s); @@ -1018,10 +990,8 @@ lex_step(struct ut_state *s, FILE *fp) } /* we're at eof */ - if (s->lex.eof) { - s->source->off = s->lex.lastoff; - s->error.code = UT_ERROR_UNTERMINATED_BLOCK; - } + if (s->lex.eof) + ut_new_exception(s, s->lex.lastoff, "Syntax error: Unterminated template block"); break; @@ -1060,8 +1030,7 @@ lex_step(struct ut_state *s, FILE *fp) (tok->type == T_LSTM || tok->type == T_RSTM || tok->type == T_LEXP)) || (s->lex.within_statement_block && (tok->type == T_LEXP || tok->type == T_REXP || tok->type == T_LSTM))) { - s->error.code = UT_ERROR_NESTED_BLOCKS; - s->source->off -= tok->plen; + ut_new_exception(s, s->source->off - tok->plen, "Syntax error: Template blocks may not be nested"); return 0; } @@ -1106,7 +1075,7 @@ lex_step(struct ut_state *s, FILE *fp) /* no token matched and we do have remaining data, junk */ if (buf_remaining(s)) { - s->error.code = UT_ERROR_UNEXPECTED_CHAR; + ut_new_exception(s, s->source->off, "Syntax error: Unexpected character"); return 0; } @@ -1119,7 +1088,7 @@ lex_step(struct ut_state *s, FILE *fp) } /* premature EOF */ - s->error.code = UT_ERROR_UNTERMINATED_BLOCK; + ut_new_exception(s, s->source->off, "Syntax error: Unterminated template block"); break; @@ -1157,7 +1126,7 @@ ut_get_token(struct ut_state *s, FILE *fp) while (s->lex.state != UT_LEX_EOF) { rv = lex_step(s, fp); - if (rv == 0 && s->error.code) + if (rv == 0 && s->exception) break; if (rv > 0) @@ -110,74 +110,62 @@ format_context_line(char **msg, size_t *msglen, const char *line, size_t off) } static void -format_error_context(char **msg, size_t *msglen, const char *path, FILE *fp, struct json_object *stacktrace, size_t off) +format_error_context(char **msg, size_t *msglen, struct ut_source *src, struct json_object *stacktrace, size_t off) { struct json_object *e, *fn, *file; size_t len, rlen, idx; + const char *path; bool truncated; char buf[256]; int eline; - if (stacktrace) { - for (idx = 0; idx < json_object_array_length(stacktrace); idx++) { - e = json_object_array_get_idx(stacktrace, idx); - fn = json_object_object_get(e, "function"); - file = json_object_object_get(e, "filename"); - - if (idx == 0) { - path = (file && strcmp(json_object_get_string(file), "[stdin]")) - ? json_object_get_string(file) : NULL; - - if (path && fn) - sprintf_append(msg, msglen, "In %s(), file %s, ", - json_object_get_string(fn), path); - else if (fn) - sprintf_append(msg, msglen, "In %s(), ", - json_object_get_string(fn)); - else if (path) - sprintf_append(msg, msglen, "In %s, ", path); - else - sprintf_append(msg, msglen, "In "); + for (idx = 0; idx < json_object_array_length(stacktrace); idx++) { + e = json_object_array_get_idx(stacktrace, idx); + fn = json_object_object_get(e, "function"); + file = json_object_object_get(e, "filename"); + + if (idx == 0) { + path = (file && strcmp(json_object_get_string(file), "[stdin]")) + ? json_object_get_string(file) : NULL; + + if (path && fn) + sprintf_append(msg, msglen, "In %s(), file %s, ", + json_object_get_string(fn), path); + else if (fn) + sprintf_append(msg, msglen, "In %s(), ", + json_object_get_string(fn)); + else if (path) + sprintf_append(msg, msglen, "In %s, ", path); + else + sprintf_append(msg, msglen, "In "); - sprintf_append(msg, msglen, "line %" PRId64 ", byte %" PRId64 ":\n", - json_object_get_int64(json_object_object_get(e, "line")), - json_object_get_int64(json_object_object_get(e, "byte"))); - } - else { - sprintf_append(msg, msglen, " at %s%s (%s:%" PRId64 ":%" PRId64 ")\n", - fn ? "function " : "main function", - fn ? json_object_get_string(fn) : "", - json_object_get_string(file), - json_object_get_int64(json_object_object_get(e, "line")), - json_object_get_int64(json_object_object_get(e, "byte"))); - } + sprintf_append(msg, msglen, "line %" PRId64 ", byte %" PRId64 ":\n", + json_object_get_int64(json_object_object_get(e, "line")), + json_object_get_int64(json_object_object_get(e, "byte"))); + } + else { + sprintf_append(msg, msglen, " at %s%s (%s:%" PRId64 ":%" PRId64 ")\n", + fn ? "function " : "main function", + fn ? json_object_get_string(fn) : "", + json_object_get_string(file), + json_object_get_int64(json_object_object_get(e, "line")), + json_object_get_int64(json_object_object_get(e, "byte"))); } } - fseek(fp, 0, SEEK_SET); + fseek(src->fp, 0, SEEK_SET); truncated = false; eline = 1; rlen = 0; - while (fgets(buf, sizeof(buf), fp)) { + while (fgets(buf, sizeof(buf), src->fp)) { len = strlen(buf); rlen += len; if (rlen > off) { - if (!stacktrace) { - if (path && strcmp(path, "[stdin]")) - sprintf_append(msg, msglen, "In %s, ", path); - else - sprintf_append(msg, msglen, "In "); - - sprintf_append(msg, msglen, "line %d, byte %zu:\n", - eline, len - (rlen - off) + (truncated ? sizeof(buf) : 0)); - } - sprintf_append(msg, msglen, "\n `%s", truncated ? "..." : ""); format_context_line(msg, msglen, buf, len - (rlen - off) + (truncated ? 3 : 0)); - break; } @@ -186,103 +174,55 @@ format_error_context(char **msg, size_t *msglen, const char *path, FILE *fp, str } } -char * -ut_format_error(struct ut_state *state, FILE *fp) +struct json_object * +ut_parse_error(struct ut_state *s, uint32_t off, uint64_t *tokens, int max_token) { - char *msg = NULL, *filename = state->source->filename; - struct json_object *stacktrace = NULL; - size_t off = state ? state->source->off : 0; - struct ut_op *tag; - bool first = true; + struct ut_op *op = ut_get_op(s, off); + struct json_object *rv; size_t msglen = 0; - int i, max_i; - - switch (state ? state->error.code : UT_ERROR_OUT_OF_MEMORY) { - case UT_ERROR_NO_ERROR: - return NULL; - - case UT_ERROR_OUT_OF_MEMORY: - sprintf_append(&msg, &msglen, "Runtime error: Out of memory\n"); - break; - - case UT_ERROR_UNTERMINATED_COMMENT: - sprintf_append(&msg, &msglen, "Syntax error: Unterminated comment\n"); - break; - - case UT_ERROR_UNTERMINATED_STRING: - sprintf_append(&msg, &msglen, "Syntax error: Unterminated string\n"); - break; - - case UT_ERROR_UNTERMINATED_BLOCK: - sprintf_append(&msg, &msglen, "Syntax error: Unterminated template block\n"); - break; - - case UT_ERROR_UNEXPECTED_CHAR: - sprintf_append(&msg, &msglen, "Syntax error: Unexpected character\n"); - break; - - case UT_ERROR_OVERLONG_STRING: - sprintf_append(&msg, &msglen, "Syntax error: String or label literal too long\n"); - break; - - case UT_ERROR_INVALID_ESCAPE: - sprintf_append(&msg, &msglen, "Syntax error: Invalid escape sequence\n"); - break; - - case UT_ERROR_NESTED_BLOCKS: - sprintf_append(&msg, &msglen, "Syntax error: Template blocks may not be nested\n"); - break; - - case UT_ERROR_UNEXPECTED_TOKEN: - sprintf_append(&msg, &msglen, "Syntax error: Unexpected token\n"); - - for (i = 0, max_i = 0; i < sizeof(state->error.info.tokens) * 8; i++) - if (ut_is_error_token(state, i)) - max_i = i; + bool first = true; + char *msg = NULL; + int i; - for (i = 0; i < sizeof(state->error.info.tokens) * 8; i++) { - if (ut_is_error_token(state, i)) { - if (first) { - sprintf_append(&msg, &msglen, "Expecting %s", ut_get_tokenname(i)); - first = false; - } - else if (i < max_i) { - sprintf_append(&msg, &msglen, ", %s", ut_get_tokenname(i)); - } - else { - sprintf_append(&msg, &msglen, " or %s", ut_get_tokenname(i)); - } + for (i = 0; i <= max_token; i++) { + if (tokens[i / 64] & ((uint64_t)1 << (i % 64))) { + if (first) { + sprintf_append(&msg, &msglen, "Expecting %s", ut_get_tokenname(i)); + first = false; + } + else if (i < max_token) { + sprintf_append(&msg, &msglen, ", %s", ut_get_tokenname(i)); + } + else { + sprintf_append(&msg, &msglen, " or %s", ut_get_tokenname(i)); } } + } - sprintf_append(&msg, &msglen, "\n"); - break; - - case UT_ERROR_INVALID_REGEXP: - if (state->error.info.regexp_error) - sprintf_append(&msg, &msglen, "Syntax error: %s\n", state->error.info.regexp_error); - else - sprintf_append(&msg, &msglen, "Runtime error: Out of memory while compiling regexp\n"); - break; - - case UT_ERROR_EXCEPTION: - tag = json_object_get_userdata(state->error.info.exception); + rv = ut_new_exception(s, op->off, "Syntax error: Unexpected token\n%s", msg); + free(msg); - if (tag && tag->type == T_EXCEPTION) { - off = tag->off; - filename = tag->tag.data; - } + return rv; +} - stacktrace = json_object_object_get(state->error.info.exception, "stacktrace"); +char * +ut_format_error(struct ut_state *state, FILE *fp) +{ + struct ut_source *src; + struct ut_op *tag; + size_t msglen = 0; + char *msg = NULL; - sprintf_append(&msg, &msglen, "%s\n", - json_object_get_string(json_object_object_get(state->error.info.exception, "message"))); + tag = json_object_get_userdata(state->exception); + src = tag->tag.data; - break; - } + sprintf_append(&msg, &msglen, "%s\n", + json_object_get_string(json_object_object_get(state->exception, "message"))); - if (off) - format_error_context(&msg, &msglen, filename, fp, stacktrace, off); + if (tag->off) + format_error_context(&msg, &msglen, src, + json_object_object_get(state->exception, "stacktrace"), + tag->off); return msg; } @@ -617,8 +557,9 @@ static struct json_object * ut_die(struct ut_state *s, uint32_t off, struct json_object *args) { const char *msg = json_object_get_string(json_object_array_get_idx(args, 0)); + struct ut_op *op = ut_get_op(s, off); - return ut_exception(s, off, "%s", msg ? msg : "Died"); + return ut_new_exception(s, op->off, "%s", msg ? msg : "Died"); } static struct json_object * @@ -1538,6 +1479,7 @@ static struct json_object * ut_require_so(struct ut_state *s, uint32_t off, const char *path) { void (*init)(const struct ut_ops *, struct ut_state *, struct json_object *); + struct ut_op *op = ut_get_op(s, off); struct json_object *scope; struct stat st; void *dlh; @@ -1549,12 +1491,12 @@ ut_require_so(struct ut_state *s, uint32_t off, const char *path) dlh = dlopen(path, RTLD_LAZY|RTLD_LOCAL); if (!dlh) - return ut_exception(s, off, "Unable to dlopen file %s: %s", path, dlerror()); + return ut_new_exception(s, op->off, "Unable to dlopen file %s: %s", path, dlerror()); init = dlsym(dlh, "ut_module_init"); if (!init) - return ut_exception(s, off, "Module %s provides no 'ut_module_init' function", path); + return ut_new_exception(s, op->off, "Module %s provides no 'ut_module_init' function", path); scope = xjs_new_object(); @@ -1563,57 +1505,55 @@ ut_require_so(struct ut_state *s, uint32_t off, const char *path) return scope; } -static struct json_object * -ut_require_utpl(struct ut_state *s, uint32_t off, const char *path, struct ut_scope *scope) +struct json_object * +ut_execute_source(struct ut_state *s, struct ut_source *src, struct ut_scope *scope) { - struct json_object *ex, *entry, *rv; - struct ut_source *src; - struct stat st; - char *msg; + struct json_object *entry, *rv; + struct ut_source *prev_src; - if (stat(path, &st)) - return NULL; + prev_src = s->source; + s->source = src; - src = xalloc(sizeof(*src)); - src->fp = fopen(path, "rb"); + rv = ut_parse(s, src->fp); - if (!src->fp) { - free(src); + if (!ut_is_type(rv, T_EXCEPTION)) { + entry = ut_new_func(s, ut_get_op(s, s->main), scope ? scope : s->scope); - return ut_exception(s, off, "Unable to open file %s: %s", path, strerror(errno)); - } + json_object_put(rv); + rv = ut_invoke(s, s->main, NULL, entry, NULL); - src->next = s->sources; - s->sources = src; - s->source = src; + json_object_put(entry); + } - if (ut_parse(s, src->fp)) { - msg = ut_format_error(s, src->fp); - ex = ut_exception(s, off, "Module loading failed: %s", msg); + s->source = prev_src; - fclose(src->fp); - free(src); - free(msg); + return rv; +} - return ex; - } +static struct json_object * +ut_require_utpl(struct ut_state *s, uint32_t off, const char *path, struct ut_scope *scope) +{ + struct ut_op *op = ut_get_op(s, off); + struct ut_source *src; + struct stat st; + FILE *fp; - entry = ut_new_func(s, ut_get_op(s, s->main), scope ? scope : s->scope); + if (stat(path, &st)) + return NULL; - rv = ut_invoke(s, off, NULL, entry, NULL); + fp = fopen(path, "rb"); - if (ut_is_type(rv, T_EXCEPTION)) { - msg = ut_format_error(s, src->fp); - json_object_put(rv); - rv = ut_exception(s, off, "%s", msg); - free(msg); - } + if (!fp) + return ut_new_exception(s, op->off, "Unable to open file %s: %s", path, strerror(errno)); - json_object_put(entry); + src = xalloc(sizeof(*src)); + src->fp = fp; + src->filename = path ? xstrdup(path) : NULL; + src->next = s->sources; - s->source = src->next; + s->sources = src; - return rv; + return ut_execute_source(s, src, scope); } static struct json_object * @@ -1662,6 +1602,7 @@ ut_require(struct ut_state *s, uint32_t off, struct json_object *args) { struct json_object *val = json_object_array_get_idx(args, 0); struct json_object *search, *se, *res; + struct ut_op *op = ut_get_op(s, off); struct ut_scope *sc, *scparent; size_t arridx, arrlen; const char *name; @@ -1683,7 +1624,7 @@ ut_require(struct ut_state *s, uint32_t off, struct json_object *args) search = sc ? json_object_object_get(sc->scope, "REQUIRE_SEARCH_PATH") : NULL; if (!json_object_is_type(search, json_type_array)) - return ut_exception(s, off, "Global require search path not set"); + return ut_new_exception(s, op->off, "Global require search path not set"); for (arridx = 0, arrlen = json_object_array_length(search); arridx < arrlen; arridx++) { se = json_object_array_get_idx(search, arridx); @@ -1697,7 +1638,7 @@ ut_require(struct ut_state *s, uint32_t off, struct json_object *args) return res; } - return ut_exception(s, off, "No module named '%s' could be found", name); + return ut_new_exception(s, op->off, "No module named '%s' could be found", name); } static struct json_object * @@ -2036,13 +1977,14 @@ static struct json_object * ut_json(struct ut_state *s, uint32_t off, struct json_object *args) { struct json_object *rv, *src = json_object_array_get_idx(args, 0); + struct ut_op *op = ut_get_op(s, off); struct json_tokener *tok = NULL; enum json_tokener_error err; const char *str; size_t len; if (!json_object_is_type(src, json_type_string)) - return ut_exception(s, off, "Passed value is not a string"); + return ut_new_exception(s, op->off, "Passed value is not a string"); tok = xjs_new_tokener(); str = json_object_get_string(src); @@ -2053,16 +1995,16 @@ ut_json(struct ut_state *s, uint32_t off, struct json_object *args) if (err == json_tokener_continue) { json_object_put(rv); - rv = ut_exception(s, off, "Unexpected end of string in JSON data"); + rv = ut_new_exception(s, op->off, "Unexpected end of string in JSON data"); } else if (err != json_tokener_success) { json_object_put(rv); - rv = ut_exception(s, off, "Failed to parse JSON string: %s", + rv = ut_new_exception(s, op->off, "Failed to parse JSON string: %s", json_tokener_error_desc(err)); } else if (json_tokener_get_parse_end(tok) < len) { json_object_put(rv); - rv = ut_exception(s, off, "Trailing garbage after JSON data"); + rv = ut_new_exception(s, op->off, "Trailing garbage after JSON data"); } json_tokener_free(tok); @@ -2108,19 +2050,20 @@ ut_include(struct ut_state *s, uint32_t off, struct json_object *args) { struct json_object *rv, *path = json_object_array_get_idx(args, 0); struct json_object *scope = json_object_array_get_idx(args, 1); + struct ut_op *op = ut_get_op(s, off); struct ut_scope *sc; char *p; if (!json_object_is_type(path, json_type_string)) - return ut_exception(s, off, "Passed filename is not a string"); + return ut_new_exception(s, op->off, "Passed filename is not a string"); if (scope && !json_object_is_type(scope, json_type_object)) - return ut_exception(s, off, "Passed scope value is not an object"); + return ut_new_exception(s, op->off, "Passed scope value is not an object"); p = include_path(s->source->filename, json_object_get_string(path)); if (!p) - return ut_exception(s, off, "Include file not found"); + return ut_new_exception(s, op->off, "Include file not found"); if (scope) { sc = ut_new_scope(s, NULL); @@ -28,6 +28,10 @@ typedef struct json_object *(ut_c_fn)(struct ut_state *, uint32_t, struct json_o void ut_lib_init(struct ut_state *state, struct json_object *scope); +struct json_object *ut_execute_source(struct ut_state *s, struct ut_source *src, struct ut_scope *scope); + +struct json_object *ut_parse_error(struct ut_state *s, uint32_t off, uint64_t *tokens, int max_token); + char *ut_format_error(struct ut_state *state, FILE *fp); #endif /* __LIB_H_ */ @@ -141,59 +141,56 @@ static void dump(struct ut_state *s, uint32_t off, int level) { } #endif /* NDEBUG */ -static enum ut_error_type -parse(struct ut_state *state, FILE *fp, bool dumponly, - struct json_object *env, struct json_object *modules) +static int +parse(struct ut_state *state, struct ut_source *src, bool dumponly, + bool skip_shebang, struct json_object *env, struct json_object *modules) { - enum ut_error_type err; + struct json_object *rv; char c, c2, *msg; + int rc = 0; - if (state->skip_shebang) { - c = fgetc(fp); - c2 = fgetc(fp); + if (skip_shebang) { + c = fgetc(src->fp); + c2 = fgetc(src->fp); if (c == '#' && c2 == '!') { - while ((c = fgetc(fp)) != EOF) { - state->source->off++; + while ((c = fgetc(src->fp)) != EOF) { + src->off++; if (c == '\n') break; } } else { - ungetc(c, fp); - ungetc(c2, fp); + ungetc(c, src->fp); + ungetc(c2, src->fp); } - - state->skip_shebang = false; } - err = ut_parse(state, fp); - - if (!err) { - if (dumponly) { + if (dumponly) { #ifdef NDEBUG - fprintf(stderr, "Debug support not compiled in\n"); - err = UT_ERROR_EXCEPTION; + rv = ut_new_exception(state, 0, "Debug support not compiled in"); #else /* NDEBUG */ + rv = ut_parse(state, src->fp); + + if (!ut_is_type(rv, T_EXCEPTION)) dump(state, state->main, 0); #endif /* NDEBUG */ - } - else { - err = ut_run(state, env, modules); - } + } + else { + rv = ut_run(state, env, modules); } - if (err) { - msg = ut_format_error(state, fp); - + if (ut_is_type(rv, T_EXCEPTION)) { + msg = ut_format_error(state, src->fp); fprintf(stderr, "%s\n\n", msg); free(msg); + rc = 1; } - ut_free(state); + json_object_put(rv); - return err; + return rc; } static FILE * @@ -261,9 +258,10 @@ int main(int argc, char **argv) { struct json_object *env = NULL, *modules = NULL, *o; + struct ut_state *state = NULL; struct ut_source source = {}; - struct ut_state *state; bool dumponly = false; + bool shebang = false; FILE *envfile = NULL; char *stdin = NULL; int opt, rv = 0; @@ -300,7 +298,7 @@ main(int argc, char **argv) if (!source.fp) { fprintf(stderr, "Failed to open %s: %s\n", optarg, strerror(errno)); - rv = UT_ERROR_EXCEPTION; + rv = 1; goto out; } @@ -343,7 +341,7 @@ main(int argc, char **argv) if (!envfile) { fprintf(stderr, "Failed to open %s: %s\n", optarg, strerror(errno)); - rv = UT_ERROR_EXCEPTION; + rv = 1; goto out; } } @@ -354,8 +352,7 @@ main(int argc, char **argv) if (!o) { fprintf(stderr, "Option -%c must point to a valid JSON object\n", opt); - - rv = UT_ERROR_EXCEPTION; + rv = 1; goto out; } @@ -380,18 +377,19 @@ main(int argc, char **argv) if (!source.fp && argv[optind] != NULL) { source.fp = fopen(argv[optind], "rb"); source.filename = xstrdup(argv[optind]); - state->skip_shebang = 1; if (!source.fp) { fprintf(stderr, "Failed to open %s: %s\n", argv[optind], strerror(errno)); - rv = UT_ERROR_EXCEPTION; + rv = 1; goto out; } + + shebang = true; } if (!source.fp) { fprintf(stderr, "One of -i or -s is required\n"); - rv = UT_ERROR_EXCEPTION; + rv = 1; goto out; } @@ -399,12 +397,13 @@ main(int argc, char **argv) state->sources = state->source; *state->source = source; - rv = parse(state, source.fp, dumponly, env, modules); + rv = parse(state, state->source, dumponly, shebang, env, modules); out: json_object_put(modules); json_object_put(env); + ut_free(state); free(stdin); return rv; @@ -51,6 +51,7 @@ #include <string.h> #include "ast.h" +#include "lib.h" #include "lexer.h" #include "parser.h" @@ -80,17 +81,17 @@ ut_no_empty_obj(struct ut_state *s, uint32_t off) } %syntax_error { - struct ut_op *op = TOKEN ? ut_get_op(s, TOKEN) : NULL; - int i; + uint64_t tokens[(__T_MAX + 63) & -64]; + int i, max_token = 0; - s->error.code = UT_ERROR_UNEXPECTED_TOKEN; + for (i = 0; i < __T_MAX; i++) { + if (yy_find_shift_action(yypParser, (YYCODETYPE)i) < YYNSTATE + YYNRULE) { + tokens[i / 64] |= ((uint64_t)1 << (i % 64)); + max_token = i; + } + } - if (op) - s->source->off = op->off; - - for (i = 0; i < __T_MAX; i++) - if (yy_find_shift_action(yypParser, (YYCODETYPE)i) < YYNSTATE + YYNRULE) - ut_set_error_token(s, i); + ut_parse_error(s, TOKEN, tokens, max_token); } diff --git a/tests/00_syntax/01_unterminated_comment b/tests/00_syntax/01_unterminated_comment index 0a28af1..1d3669c 100644 --- a/tests/00_syntax/01_unterminated_comment +++ b/tests/00_syntax/01_unterminated_comment @@ -2,7 +2,7 @@ Unterminated comment -- Expect stderr -- Syntax error: Unterminated template block -In line 1, byte 8: +In line 1, byte 9: `This is {# an unclosed comment` ^-- Near here diff --git a/tests/00_syntax/05_block_nesting b/tests/00_syntax/05_block_nesting index 728c00c..7b74adb 100644 --- a/tests/00_syntax/05_block_nesting +++ b/tests/00_syntax/05_block_nesting @@ -2,7 +2,7 @@ Nesting blocks into non-comment blocks should fail. -- Expect stderr -- Syntax error: Template blocks may not be nested -In line 2, byte 60: +In line 2, byte 61: `We may not nest statement blocks into expression blocks: {{ {% print(1 + 2) %} }}.` Near here --------------------------------------------------^ diff --git a/tests/02_runtime/04_switch_case b/tests/02_runtime/04_switch_case index d83667f..2102ee5 100644 --- a/tests/02_runtime/04_switch_case +++ b/tests/02_runtime/04_switch_case @@ -89,7 +89,7 @@ default -- Expect stderr -- Syntax error: more than one switch default case -In line 6, byte 2: +In line 6, byte 3: ` default:` ^-- Near here @@ -242,8 +242,8 @@ one -- Expect stderr -- Died -In test(), line 6, byte 6: - at main function ([stdin]:17:11) +In test(), line 6, byte 7: + at main function ([stdin]:17:12) ` die();` Near here -----^ |