summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2020-10-13 19:01:02 +0200
committerJo-Philipp Wich <jo@mein.io>2020-10-14 12:23:08 +0200
commit07c147a1803270aba871c9024e021fd913cb8e2a (patch)
treed05fcecf8afc6e82b6c659b272d23029a6cc9249
parent05cc0ee7a7c7dc442e22dfdc22c0574ac6b3e71b (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.c75
-rw-r--r--ast.h42
-rw-r--r--eval.c74
-rw-r--r--eval.h2
-rw-r--r--lexer.c71
-rw-r--r--lib.c303
-rw-r--r--lib.h4
-rw-r--r--main.c73
-rw-r--r--parser.y19
-rw-r--r--tests/00_syntax/01_unterminated_comment2
-rw-r--r--tests/00_syntax/05_block_nesting2
-rw-r--r--tests/02_runtime/04_switch_case6
12 files changed, 260 insertions, 413 deletions
diff --git a/ast.c b/ast.c
index f574de2..72fa6e9 100644
--- a/ast.c
+++ b/ast.c
@@ -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
diff --git a/ast.h b/ast.h
index 9e44e83..5c4d2ab 100644
--- a/ast.h
+++ b/ast.h
@@ -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);
diff --git a/eval.c b/eval.c
index f8b4baf..f6685d5 100644
--- a/eval.c
+++ b/eval.c
@@ -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;
}
diff --git a/eval.h b/eval.h
index bc59cea..7cfd0e0 100644
--- a/eval.h
+++ b/eval.h
@@ -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
diff --git a/lexer.c b/lexer.c
index d94a01d..eb513b8 100644
--- a/lexer.c
+++ b/lexer.c
@@ -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)
diff --git a/lib.c b/lib.c
index d6a6f7a..dbbb028 100644
--- a/lib.c
+++ b/lib.c
@@ -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);
diff --git a/lib.h b/lib.h
index 084714d..d391a8d 100644
--- a/lib.h
+++ b/lib.h
@@ -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_ */
diff --git a/main.c b/main.c
index b19e622..2abe485 100644
--- a/main.c
+++ b/main.c
@@ -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;
diff --git a/parser.y b/parser.y
index f72e812..481a6ea 100644
--- a/parser.y
+++ b/parser.y
@@ -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 -----^