summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--ast.c97
-rw-r--r--ast.h38
-rw-r--r--eval.c96
-rw-r--r--lexer.c65
-rw-r--r--lib.c162
-rw-r--r--lib.h2
-rw-r--r--main.c174
-rw-r--r--parser.y2
-rw-r--r--tests/02_runtime/04_switch_case3
9 files changed, 396 insertions, 243 deletions
diff --git a/ast.c b/ast.c
index a78518f..f574de2 100644
--- a/ast.c
+++ b/ast.c
@@ -322,7 +322,6 @@ ut_new_func(struct ut_state *s, struct ut_op *decl, struct ut_scope *scope)
struct ut_op *op, *name, *args, *arg;
struct ut_function *fn;
size_t sz;
- char *p;
sz = ALIGN(sizeof(*op)) + ALIGN(sizeof(*fn));
@@ -332,23 +331,13 @@ ut_new_func(struct ut_state *s, struct ut_op *decl, struct ut_scope *scope)
if (name)
sz += ALIGN(json_object_get_string_len(name->val) + 1);
- if (s->filename)
- sz += ALIGN(strlen(s->filename) + 1);
-
op = xalloc(sz);
fn = (void *)op + ALIGN(sizeof(*op));
fn->entry = decl->tree.operand[2];
- p = (char *)fn + ALIGN(sizeof(*fn));
-
- if (name) {
- fn->name = strcpy(p, json_object_get_string(name->val));
- p += ALIGN(json_object_get_string_len(name->val) + 1);
- }
-
- if (s->filename)
- fn->filename = strcpy(p, s->filename);
+ if (name)
+ fn->name = strcpy((char *)fn + ALIGN(sizeof(*fn)), json_object_get_string(name->val));
if (args) {
fn->args = xjs_new_array();
@@ -357,6 +346,7 @@ ut_new_func(struct ut_state *s, struct ut_op *decl, struct ut_scope *scope)
json_object_array_add(fn->args, json_object_get(arg->val));
}
+ fn->source = s->source;
fn->parent_scope = ut_acquire_scope(scope);
op->val = val;
@@ -374,18 +364,58 @@ exception_free(struct json_object *v, void *ud)
free(ud);
}
-__attribute__((format(printf, 3, 0))) struct json_object *
+static int
+exception_to_string(struct json_object *v, struct printbuf *pb, int level, int flags)
+{
+ return sprintbuf(pb, "%s", json_object_get_string(json_object_object_get(v, "message")));
+}
+
+static void
+add_stacktrace(struct json_object *a, struct ut_source *source, const char *funcname, size_t off) {
+ struct json_object *o = xjs_new_object();
+ size_t line = 1, rlen = 0, len;
+ bool truncated = false;
+ char buf[256];
+
+ if (source->filename)
+ json_object_object_add(o, "filename", xjs_new_string(source->filename));
+
+ if (funcname)
+ json_object_object_add(o, "function", xjs_new_string(funcname));
+
+ fseek(source->fp, 0, SEEK_SET);
+
+ while (fgets(buf, sizeof(buf), source->fp)) {
+ len = strlen(buf);
+ rlen += len;
+
+ 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)));
+ break;
+ }
+
+ truncated = (len > 0 && buf[len-1] != '\n');
+ line += !truncated;
+ }
+
+ json_object_array_add(a, o);
+}
+
+__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;
va_list ap;
ssize_t sz;
char *p;
sz = ALIGN(sizeof(*op));
- if (s->filename)
- sz += ALIGN(strlen(s->filename) + 1);
+ if (s->source && s->source->filename)
+ sz += ALIGN(strlen(s->source->filename) + 1);
failing_op = ut_get_op(s, off);
@@ -393,19 +423,28 @@ ut_new_exception(struct ut_state *s, uint32_t off, const char *fmt, ...)
op->type = T_EXCEPTION;
op->off = failing_op ? failing_op->off : 0;
- if (s->filename) {
+ if (s->source && s->source->filename) {
p = (char *)op + ALIGN(sizeof(*op));
- op->tag.data = strcpy(p, s->filename);
+ op->tag.data = strcpy(p, s->source->filename);
}
va_start(ap, fmt);
sz = xvasprintf(&p, fmt, ap);
va_end(ap);
- op->val = xjs_new_string_len(p, sz);
- free(p);
+ op->val = xjs_new_object();
+
+ a = xjs_new_array();
- json_object_set_userdata(op->val, op, exception_free);
+ add_stacktrace(a, s->function->source, s->function->name, failing_op ? failing_op->off : 0);
+
+ for (callstack = s->callstack ? s->callstack->next : NULL; callstack && callstack->next; callstack = callstack->next)
+ 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));
+ free(p);
if (s->error.code == UT_ERROR_EXCEPTION)
json_object_put(s->error.info.exception);
@@ -413,6 +452,8 @@ ut_new_exception(struct ut_state *s, uint32_t off, const char *fmt, ...)
s->error.code = UT_ERROR_EXCEPTION;
s->error.info.exception = op->val;
+ json_object_set_serializer(op->val, exception_to_string, op, exception_free);
+
return json_object_get(op->val);
}
@@ -492,6 +533,7 @@ ut_reset(struct ut_state *s)
void
ut_free(struct ut_state *s)
{
+ struct ut_source *src, *src_next;
struct ut_scope *sc, *sc_next;
size_t n;
@@ -524,15 +566,20 @@ ut_free(struct ut_state *s)
free(sc);
}
+ for (src = s->sources; src; src = src_next) {
+ src_next = src->next;
+ fclose(src->fp);
+ free(src->filename);
+ free(src);
+ }
+
free(ut_ext_types);
- free(s->filename);
free(s);
}
enum ut_error_type
-ut_parse(struct ut_state *s, const char *expr)
+ut_parse(struct ut_state *s, FILE *fp)
{
- FILE *fp = fmemopen((char *)expr, strlen(expr), "r");
struct ut_op *op;
void *pParser;
uint32_t off;
@@ -566,8 +613,6 @@ ut_parse(struct ut_state *s, const char *expr)
out:
ParseFree(pParser, free);
- fclose(fp);
-
return s->error.code;
}
diff --git a/ast.h b/ast.h
index 1d825c7..9e44e83 100644
--- a/ast.h
+++ b/ast.h
@@ -93,13 +93,28 @@ struct ut_scope {
size_t refs;
};
+struct ut_source {
+ struct ut_source *next;
+ char *filename;
+ uint32_t off;
+ FILE *fp;
+};
+
struct ut_function {
- char *name, *filename;
+ char *name;
struct json_object *args;
struct ut_scope *scope, *parent_scope;
+ struct ut_source *source;
uint32_t entry;
};
+struct ut_callstack {
+ struct ut_callstack *next;
+ struct ut_source *source;
+ char *funcname;
+ uint32_t off;
+};
+
struct ut_state {
struct ut_op *pool;
uint32_t poolsize;
@@ -127,7 +142,7 @@ struct ut_state {
char esc[5];
uint8_t esclen;
int lead_surrogate;
- size_t off, lastoff;
+ size_t lastoff;
} lex;
struct {
enum ut_error_type code;
@@ -137,9 +152,11 @@ struct ut_state {
char *regexp_error;
} info;
} error;
- struct ut_scope *scopelist, *scope;
struct json_object *ctx, *rval;
- char *filename;
+ struct ut_scope *scopelist, *scope;
+ struct ut_source *sources, *source;
+ struct ut_callstack *callstack;
+ struct ut_function *function;
};
struct ut_extended_type {
@@ -177,7 +194,7 @@ static inline bool ut_is_error_token(struct ut_state *s, int tokennr) {
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, const char *expr);
+enum ut_error_type 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);
@@ -236,6 +253,17 @@ static inline void *xrealloc(void *ptr, size_t size) {
return ptr;
}
+static inline char *xstrdup(const char *s) {
+ char *ptr = strdup(s);
+
+ if (!ptr) {
+ fprintf(stderr, "Out of memory\n");
+ abort();
+ }
+
+ return ptr;
+}
+
static inline struct json_object *xjs_new_object(void) {
struct json_object *ptr = json_object_new_object();
diff --git a/eval.c b/eval.c
index af38c50..f8b4baf 100644
--- a/eval.c
+++ b/eval.c
@@ -977,71 +977,83 @@ struct json_object *
ut_invoke(struct ut_state *state, uint32_t off, struct json_object *this,
struct json_object *func, struct json_object *argvals)
{
- struct ut_op *tag = json_object_get_userdata(func);
+ struct ut_op *op, *tag = json_object_get_userdata(func);
+ struct ut_callstack callstack = {};
+ struct ut_function *fn, *prev_fn;
struct json_object *rv = NULL;
- struct ut_function *fn;
struct ut_scope *sc;
- char *filename;
size_t arridx;
ut_c_fn *cfn;
if (!tag)
return NULL;
+ op = ut_get_op(state, off);
+
+ callstack.next = state->callstack;
+ callstack.source = state->source;
+ callstack.funcname = state->function ? state->function->name : NULL;
+ callstack.off = op ? op->off : 0;
+ state->callstack = &callstack;
+
/* is native function */
if (tag->type == T_CFUNC) {
cfn = (ut_c_fn *)tag->tag.data;
-
- return cfn ? cfn(state, off, argvals) : NULL;
+ rv = cfn ? cfn(state, off, argvals) : NULL;
}
- fn = tag->tag.data;
- fn->scope = ut_new_scope(state, fn->parent_scope);
- fn->scope->ctx = json_object_get(this ? this : state->ctx);
+ /* is utpl function */
+ else {
+ fn = tag->tag.data;
+ fn->scope = ut_new_scope(state, fn->parent_scope);
+ fn->scope->ctx = json_object_get(this ? this : state->ctx);
- sc = state->scope;
- filename = state->filename;
+ sc = state->scope;
- state->scope = ut_acquire_scope(fn->scope);
- state->filename = fn->filename;
+ state->scope = ut_acquire_scope(fn->scope);
- if (fn->args)
- for (arridx = 0; arridx < json_object_array_length(fn->args); arridx++)
- ut_setval(fn->scope->scope, json_object_array_get_idx(fn->args, arridx),
- argvals ? json_object_array_get_idx(argvals, arridx) : NULL);
+ prev_fn = state->function;
+ state->function = fn;
- rv = ut_execute_op_sequence(state, fn->entry);
- tag = json_object_get_userdata(rv);
+ if (fn->args)
+ for (arridx = 0; arridx < json_object_array_length(fn->args); arridx++)
+ ut_setval(fn->scope->scope, json_object_array_get_idx(fn->args, arridx),
+ argvals ? json_object_array_get_idx(argvals, arridx) : NULL);
- switch (tag ? tag->type : 0) {
- 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));
- break;
+ rv = ut_execute_op_sequence(state, fn->entry);
+ tag = json_object_get_userdata(rv);
- case T_RETURN:
- json_object_put(rv);
- rv = json_object_get(state->rval);
- break;
- }
+ switch (tag ? tag->type : 0) {
+ 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));
+ break;
- /* we left the function, pop the function scope... */
- ut_release_scope(state->scope);
- state->scope = sc;
+ case T_RETURN:
+ json_object_put(rv);
+ rv = json_object_get(state->rval);
+ break;
+ }
- /* ... and remove the "this" context... */
- json_object_put(fn->scope->ctx);
- fn->scope->ctx = NULL;
+ /* we left the function, pop the function scope... */
+ ut_release_scope(state->scope);
+ state->scope = sc;
- /* ... and reset the function scope... */
- ut_release_scope(fn->scope);
- fn->scope = NULL;
+ /* ... and remove the "this" context... */
+ json_object_put(fn->scope->ctx);
+ fn->scope->ctx = NULL;
+
+ /* ... and reset the function scope... */
+ ut_release_scope(fn->scope);
+ fn->scope = NULL;
+
+ state->function = prev_fn;
+ }
- /* ... and the file name context */
- state->filename = filename;
+ state->callstack = callstack.next;
return rv;
}
diff --git a/lexer.c b/lexer.c
index 2ec1ea0..d94a01d 100644
--- a/lexer.c
+++ b/lexer.c
@@ -316,7 +316,7 @@ static inline bool _buf_startswith(struct ut_state *s, const char *str, size_t l
static void buf_consume(struct ut_state *s, ssize_t len) {
s->lex.bufstart += len;
- s->lex.off += len;
+ s->source->off += len;
}
/*
@@ -398,7 +398,7 @@ parse_string(struct ut_state *s)
if (!buf_remaining(s)) {
s->error.code = UT_ERROR_UNTERMINATED_STRING;
- s->lex.off = s->lex.lastoff;
+ s->source->off = s->lex.lastoff;
return 0;
}
@@ -465,7 +465,7 @@ parse_string(struct ut_state *s)
case 'u':
if (s->lex.esclen < 5) {
if (!isxdigit(*ptr)) {
- s->lex.off += s->lex.esclen + 1;
+ s->source->off += s->lex.esclen + 1;
s->error.code = UT_ERROR_INVALID_ESCAPE;
return 0;
@@ -521,7 +521,7 @@ parse_string(struct ut_state *s)
case 'x':
if (s->lex.esclen < 3) {
if (!isxdigit(*ptr)) {
- s->lex.off += s->lex.esclen + 1;
+ s->source->off += s->lex.esclen + 1;
s->error.code = UT_ERROR_INVALID_ESCAPE;
return 0;
}
@@ -575,7 +575,7 @@ parse_string(struct ut_state *s)
dec(s->lex.esc[3]);
if (code > 255) {
- s->lex.off += s->lex.esclen + 1;
+ s->source->off += s->lex.esclen + 1;
s->error.code = UT_ERROR_INVALID_ESCAPE;
return 0;
@@ -655,10 +655,10 @@ parse_regexp(struct ut_state *s)
if (buf_startswith(s, "=")) {
buf_consume(s, 1);
- return emit_op(s, s->lex.off, T_ASDIV, NULL);
+ return emit_op(s, s->source->off, T_ASDIV, NULL);
}
- return emit_op(s, s->lex.off, T_DIV, NULL);
+ return emit_op(s, s->source->off, T_DIV, NULL);
}
s->lex.esc[0] = UT_LEX_PARSE_REGEX_PATTERN;
@@ -711,7 +711,7 @@ parse_regexp(struct ut_state *s)
if (!pattern) {
s->error.info.regexp_error = err;
s->error.code = UT_ERROR_INVALID_REGEXP;
- s->lex.off = s->lex.lastoff;
+ s->source->off = s->lex.lastoff;
return 0;
}
@@ -756,22 +756,22 @@ parse_label(struct ut_state *s)
switch (word->type) {
case T_DOUBLE:
- rv = emit_op(s, s->lex.off - word->plen, word->type, ut_new_double(word->d));
+ rv = emit_op(s, s->source->off - word->plen, word->type, ut_new_double(word->d));
break;
case T_BOOL:
- rv = emit_op(s, s->lex.off - word->plen, word->type, xjs_new_boolean(word->b));
+ rv = emit_op(s, s->source->off - word->plen, word->type, xjs_new_boolean(word->b));
break;
default:
- rv = emit_op(s, s->lex.off - word->plen, word->type, NULL);
+ rv = emit_op(s, s->source->off - word->plen, word->type, NULL);
}
return rv;
}
}
- return lookbehind_to_text(s, s->lex.off - s->lex.lookbehindlen, T_LABEL, NULL);
+ return lookbehind_to_text(s, s->source->off - s->lex.lookbehindlen, T_LABEL, NULL);
}
for (ptr = s->lex.bufstart; ptr < s->lex.bufend && (*ptr == '_' || isalnum(*ptr)); ptr++)
@@ -822,20 +822,20 @@ parse_number(struct ut_state *s)
d = strtod(s->lex.lookbehind, &e);
if (e > s->lex.lookbehind && *e == 0) {
- rv = emit_op(s, s->lex.off - (e - s->lex.lookbehind), T_DOUBLE, ut_new_double(d));
+ 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->lex.off -= s->lex.lookbehindlen - (e - s->lex.lookbehind) - 1;
+ s->source->off -= s->lex.lookbehindlen - (e - s->lex.lookbehind) - 1;
}
}
else if (*e == 0) {
- rv = emit_op(s, s->lex.off - (e - s->lex.lookbehind), T_NUMBER, xjs_new_int64(n));
+ 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->lex.off -= s->lex.lookbehindlen - (e - s->lex.lookbehind) - 1;
+ s->source->off -= s->lex.lookbehindlen - (e - s->lex.lookbehind) - 1;
}
lookbehind_reset(s);
@@ -906,7 +906,7 @@ lex_step(struct ut_state *s, FILE *fp)
if (!strncmp(ptr, "{#", 2)) {
lookbehind_append(s, s->lex.bufstart, ptr - s->lex.bufstart);
buf_consume(s, (ptr + 2) - s->lex.bufstart);
- s->lex.lastoff = s->lex.off - 2;
+ s->lex.lastoff = s->source->off - 2;
s->lex.state = UT_LEX_BLOCK_COMMENT_START;
return 0;
@@ -916,7 +916,7 @@ lex_step(struct ut_state *s, FILE *fp)
else if (!strncmp(ptr, "{{", 2)) {
lookbehind_append(s, s->lex.bufstart, ptr - s->lex.bufstart);
buf_consume(s, (ptr + 2) - s->lex.bufstart);
- s->lex.lastoff = s->lex.off - 2;
+ s->lex.lastoff = s->source->off - 2;
s->lex.state = UT_LEX_BLOCK_EXPRESSION_START;
return 0;
@@ -926,7 +926,7 @@ lex_step(struct ut_state *s, FILE *fp)
else if (!strncmp(ptr, "{%", 2)) {
lookbehind_append(s, s->lex.bufstart, ptr - s->lex.bufstart);
buf_consume(s, (ptr + 2) - s->lex.bufstart);
- s->lex.lastoff = s->lex.off - 2;
+ s->lex.lastoff = s->source->off - 2;
s->lex.state = UT_LEX_BLOCK_STATEMENT_START;
return 0;
@@ -954,7 +954,7 @@ lex_step(struct ut_state *s, FILE *fp)
/* strip whitespace before block */
if (buf_startswith(s, "-")) {
- rv = lookbehind_to_text(s, s->lex.off, T_TEXT, " \n\t\v\f\r");
+ rv = lookbehind_to_text(s, s->source->off, T_TEXT, " \n\t\v\f\r");
buf_consume(s, 1);
}
@@ -962,17 +962,17 @@ lex_step(struct ut_state *s, FILE *fp)
else if (s->lex.state == UT_LEX_BLOCK_STATEMENT_START) {
/* disable lstrip flag */
if (buf_startswith(s, "+")) {
- rv = lookbehind_to_text(s, s->lex.off, T_TEXT, NULL);
+ rv = lookbehind_to_text(s, s->source->off, T_TEXT, NULL);
buf_consume(s, 1);
}
/* global block lstrip */
else if (s->lstrip_blocks) {
- rv = lookbehind_to_text(s, s->lex.off, T_TEXT, " \t\v\f\r");
+ rv = lookbehind_to_text(s, s->source->off, T_TEXT, " \t\v\f\r");
}
}
else {
- rv = lookbehind_to_text(s, s->lex.off, T_TEXT, NULL);
+ rv = lookbehind_to_text(s, s->source->off, T_TEXT, NULL);
}
switch (s->lex.state) {
@@ -1003,14 +1003,14 @@ lex_step(struct ut_state *s, FILE *fp)
s->lex.state = UT_LEX_IDENTIFY_BLOCK;
s->lex.skip_leading_whitespace = 1;
buf_consume(s, 3);
- s->lex.lastoff = s->lex.off;
+ s->lex.lastoff = s->source->off;
break;
}
else if (buf_startswith(s, "#}")) {
s->lex.state = UT_LEX_IDENTIFY_BLOCK;
s->lex.skip_leading_whitespace = 0;
buf_consume(s, 2);
- s->lex.lastoff = s->lex.off;
+ s->lex.lastoff = s->source->off;
break;
}
@@ -1019,7 +1019,7 @@ lex_step(struct ut_state *s, FILE *fp)
/* we're at eof */
if (s->lex.eof) {
- s->lex.off = s->lex.lastoff;
+ s->source->off = s->lex.lastoff;
s->error.code = UT_ERROR_UNTERMINATED_BLOCK;
}
@@ -1030,7 +1030,7 @@ lex_step(struct ut_state *s, FILE *fp)
s->lex.within_expression_block = 1;
s->lex.state = UT_LEX_IDENTIFY_TOKEN;
- return emit_op(s, s->lex.off, T_LEXP, NULL);
+ return emit_op(s, s->source->off, T_LEXP, NULL);
case UT_LEX_IDENTIFY_TOKEN:
@@ -1045,11 +1045,12 @@ lex_step(struct ut_state *s, FILE *fp)
: (c >= tok->pat[0] && c <= tok->pat[1])) {
buf_consume(s, tok->plen);
+ s->lex.lastoff = s->source->off - tok->plen;
+
/* token has a parse method, switch state */
if (tok->parse) {
s->lex.tok = tok;
s->lex.state = UT_LEX_PARSE_TOKEN;
- s->lex.lastoff = s->lex.off - tok->plen;
return 0;
}
@@ -1060,7 +1061,7 @@ lex_step(struct ut_state *s, FILE *fp)
(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->lex.off -= tok->plen;
+ s->source->off -= tok->plen;
return 0;
}
@@ -1075,7 +1076,7 @@ lex_step(struct ut_state *s, FILE *fp)
/* rewind */
buf_consume(s, -tok->plen);
- return emit_op(s, s->lex.off, T_SCOL, NULL);
+ return emit_op(s, s->source->off, T_SCOL, NULL);
}
/* strip whitespace after block */
@@ -1090,12 +1091,12 @@ lex_step(struct ut_state *s, FILE *fp)
s->lex.within_statement_block = false;
s->lex.within_expression_block = false;
s->lex.state = UT_LEX_IDENTIFY_BLOCK;
- s->lex.lastoff = s->lex.off;
+ s->lex.lastoff = s->source->off;
}
/* do not report statement tags to the parser */
if (tok->type != 0 && tok->type != T_LSTM && tok->type != T_RSTM)
- rv = emit_op(s, s->lex.off - tok->plen, tok->type, NULL);
+ rv = emit_op(s, s->source->off - tok->plen, tok->type, NULL);
else
rv = 0;
diff --git a/lib.c b/lib.c
index 5cc5598..d6a6f7a 100644
--- a/lib.c
+++ b/lib.c
@@ -36,7 +36,7 @@
#include <sys/types.h>
-static void
+__attribute__((format(printf, 3, 5))) static void
snprintf_append(char **dptr, size_t *dlen, const char *fmt, ssize_t sz, ...)
{
va_list ap;
@@ -110,68 +110,88 @@ 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, const char *expr, size_t off)
+format_error_context(char **msg, size_t *msglen, const char *path, FILE *fp, struct json_object *stacktrace, size_t off)
{
- const char *p, *nl;
- size_t len, rlen;
+ struct json_object *e, *fn, *file;
+ size_t len, rlen, idx;
bool truncated;
char buf[256];
int eline;
- FILE *f;
- if (path) {
- f = fopen(path, "rb");
+ 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 ");
- if (f) {
- truncated = false;
- eline = 1;
- rlen = 0;
+ 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")));
+ }
+ }
+ }
- while (fgets(buf, sizeof(buf), f)) {
- len = strlen(buf);
- rlen += len;
+ fseek(fp, 0, SEEK_SET);
- 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 ? "..." : "");
+ truncated = false;
+ eline = 1;
+ rlen = 0;
- format_context_line(msg, msglen, buf,
- len - (rlen - off) + (truncated ? 3 : 0));
+ while (fgets(buf, sizeof(buf), fp)) {
+ len = strlen(buf);
+ rlen += len;
- break;
- }
+ if (rlen > off) {
+ if (!stacktrace) {
+ if (path && strcmp(path, "[stdin]"))
+ sprintf_append(msg, msglen, "In %s, ", path);
+ else
+ sprintf_append(msg, msglen, "In ");
- truncated = (len > 0 && buf[len-1] != '\n');
- eline += !truncated;
+ sprintf_append(msg, msglen, "line %d, byte %zu:\n",
+ eline, len - (rlen - off) + (truncated ? sizeof(buf) : 0));
}
- }
- 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, "\n `%s", truncated ? "..." : "");
+ format_context_line(msg, msglen, buf, len - (rlen - off) + (truncated ? 3 : 0));
+
+ break;
}
- sprintf_append(msg, msglen, "In line %u, byte %d:\n\n `", eline, p - nl);
- format_context_line(msg, msglen, nl, p - nl);
+ truncated = (len > 0 && buf[len-1] != '\n');
+ eline += !truncated;
}
}
char *
-ut_format_error(struct ut_state *state, const char *expr)
+ut_format_error(struct ut_state *state, FILE *fp)
{
- char *msg = NULL, *filename = state->filename;
- size_t off = state ? state->lex.off : 0;
+ 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;
size_t msglen = 0;
@@ -253,12 +273,16 @@ ut_format_error(struct ut_state *state, const char *expr)
filename = tag->tag.data;
}
- sprintf_append(&msg, &msglen, "%s\n", json_object_get_string(state->error.info.exception));
+ stacktrace = json_object_object_get(state->error.info.exception, "stacktrace");
+
+ sprintf_append(&msg, &msglen, "%s\n",
+ json_object_get_string(json_object_object_get(state->error.info.exception, "message")));
+
break;
}
if (off)
- format_error_context(&msg, &msglen, filename, expr, off);
+ format_error_context(&msg, &msglen, filename, fp, stacktrace, off);
return msg;
}
@@ -1543,28 +1567,32 @@ static struct json_object *
ut_require_utpl(struct ut_state *s, uint32_t off, const char *path, struct ut_scope *scope)
{
struct json_object *ex, *entry, *rv;
- char *source, *msg;
+ struct ut_source *src;
struct stat st;
- FILE *sfile;
+ char *msg;
if (stat(path, &st))
return NULL;
- sfile = fopen(path, "rb");
+ src = xalloc(sizeof(*src));
+ src->fp = fopen(path, "rb");
- if (!sfile)
- return ut_exception(s, off, "Unable to open file %s: %s", path, strerror(errno));
+ if (!src->fp) {
+ free(src);
- source = xalloc(st.st_size + 1);
+ return ut_exception(s, off, "Unable to open file %s: %s", path, strerror(errno));
+ }
- fread(source, 1, st.st_size, sfile);
- fclose(sfile);
+ src->next = s->sources;
+ s->sources = src;
+ s->source = src;
- if (ut_parse(s, source)) {
- msg = ut_format_error(s, source);
+ if (ut_parse(s, src->fp)) {
+ msg = ut_format_error(s, src->fp);
ex = ut_exception(s, off, "Module loading failed: %s", msg);
- free(source);
+ fclose(src->fp);
+ free(src);
free(msg);
return ex;
@@ -1575,16 +1603,16 @@ ut_require_utpl(struct ut_state *s, uint32_t off, const char *path, struct ut_sc
rv = ut_invoke(s, off, NULL, entry, NULL);
if (ut_is_type(rv, T_EXCEPTION)) {
- msg = ut_format_error(s, source);
+ msg = ut_format_error(s, src->fp);
json_object_put(rv);
rv = ut_exception(s, off, "%s", msg);
free(msg);
}
- free(source);
-
json_object_put(entry);
+ s->source = src->next;
+
return rv;
}
@@ -1592,8 +1620,8 @@ static struct json_object *
ut_require_path(struct ut_state *s, uint32_t off, const char *path_template, const char *name)
{
struct json_object *rv = NULL;
- char *path = NULL, *filename;
const char *p, *q, *last;
+ char *path = NULL;
size_t plen = 0;
p = strchr(path_template, '*');
@@ -1618,16 +1646,11 @@ ut_require_path(struct ut_state *s, uint32_t off, const char *path_template, con
}
}
- filename = s->filename;
- s->filename = path;
-
if (!strcmp(p, ".so"))
rv = ut_require_so(s, off, path);
else if (!strcmp(p, ".utpl"))
rv = ut_require_utpl(s, off, path, NULL);
- s->filename = filename;
-
invalid:
free(path);
@@ -2086,7 +2109,7 @@ 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_scope *sc;
- char *p, *filename;
+ char *p;
if (!json_object_is_type(path, json_type_string))
return ut_exception(s, off, "Passed filename is not a string");
@@ -2094,7 +2117,7 @@ ut_include(struct ut_state *s, uint32_t off, struct json_object *args)
if (scope && !json_object_is_type(scope, json_type_object))
return ut_exception(s, off, "Passed scope value is not an object");
- p = include_path(s->filename, json_object_get_string(path));
+ p = include_path(s->source->filename, json_object_get_string(path));
if (!p)
return ut_exception(s, off, "Include file not found");
@@ -2109,13 +2132,8 @@ ut_include(struct ut_state *s, uint32_t off, struct json_object *args)
sc = s->scope;
}
- filename = s->filename;
- s->filename = p;
-
rv = ut_require_utpl(s, off, p, sc);
- s->filename = filename;
-
free(p);
if (scope)
diff --git a/lib.h b/lib.h
index 9471141..084714d 100644
--- a/lib.h
+++ b/lib.h
@@ -28,6 +28,6 @@ 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);
-char *ut_format_error(struct ut_state *state, const char *expr);
+char *ut_format_error(struct ut_state *state, FILE *fp);
#endif /* __LIB_H_ */
diff --git a/main.c b/main.c
index 0d1f571..b19e622 100644
--- a/main.c
+++ b/main.c
@@ -142,23 +142,33 @@ static void dump(struct ut_state *s, uint32_t off, int level) {
#endif /* NDEBUG */
static enum ut_error_type
-parse(struct ut_state *state, const char *source, bool dumponly,
+parse(struct ut_state *state, FILE *fp, bool dumponly,
struct json_object *env, struct json_object *modules)
{
enum ut_error_type err;
- char *msg;
+ char c, c2, *msg;
if (state->skip_shebang) {
- if (source[0] == '#' && source[1] == '!') {
- while (source[0] != '\0' && source[0] != '\n')
- source++;
+ c = fgetc(fp);
+ c2 = fgetc(fp);
- if (source[0] == '\n')
- source++;
+ if (c == '#' && c2 == '!') {
+ while ((c = fgetc(fp)) != EOF) {
+ state->source->off++;
+
+ if (c == '\n')
+ break;
+ }
}
+ else {
+ ungetc(c, fp);
+ ungetc(c2, fp);
+ }
+
+ state->skip_shebang = false;
}
- err = ut_parse(state, source);
+ err = ut_parse(state, fp);
if (!err) {
if (dumponly) {
@@ -175,7 +185,7 @@ parse(struct ut_state *state, const char *source, bool dumponly,
}
if (err) {
- msg = ut_format_error(state, source);
+ msg = ut_format_error(state, fp);
fprintf(stderr, "%s\n\n", msg);
free(msg);
@@ -186,58 +196,76 @@ parse(struct ut_state *state, const char *source, bool dumponly,
return err;
}
-static bool stdin_used = false;
+static FILE *
+read_stdin(char **ptr)
+{
+ size_t rlen = 0, tlen = 0;
+ char buf[128];
-static char *
-read_file(const char *path) {
- char buf[64], *s = NULL;
- size_t rlen, tlen = 0;
- FILE *fp = NULL;
+ if (*ptr) {
+ fprintf(stderr, "Can read from stdin only once\n");
+ errno = EINVAL;
- if (!strcmp(path, "-")) {
- if (stdin_used) {
- fprintf(stderr, "Can read from stdin only once\n");
- goto out;
- }
-
- fp = stdin;
- stdin_used = true;
+ return NULL;
}
- else {
- fp = fopen(path, "r");
- if (!fp) {
- fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
- goto out;
- }
+ while (true) {
+ rlen = fread(buf, 1, sizeof(buf), stdin);
+
+ if (rlen == 0)
+ break;
+
+ *ptr = xrealloc(*ptr, tlen + rlen);
+ memcpy(*ptr + tlen, buf, rlen);
+ tlen += rlen;
}
- while (1) {
+ return fmemopen(*ptr, tlen, "rb");
+}
+
+static struct json_object *
+parse_envfile(FILE *fp)
+{
+ struct json_object *rv = NULL;
+ enum json_tokener_error err;
+ struct json_tokener *tok;
+ char buf[128];
+ size_t rlen;
+
+ tok = xjs_new_tokener();
+
+ while (true) {
rlen = fread(buf, 1, sizeof(buf), fp);
if (rlen == 0)
break;
- s = xrealloc(s, tlen + rlen + 1);
- memcpy(s + tlen, buf, rlen);
- s[tlen + rlen] = 0;
- tlen += rlen;
+ rv = json_tokener_parse_ex(tok, buf, rlen);
+ err = json_tokener_get_error(tok);
+
+ if (err != json_tokener_continue)
+ break;
}
-out:
- if (fp != NULL && fp != stdin)
- fclose(fp);
+ if (err != json_tokener_success || !json_object_is_type(rv, json_type_object)) {
+ json_object_put(rv);
+ rv = NULL;
+ }
- return s;
+ json_tokener_free(tok);
+
+ return rv;
}
int
main(int argc, char **argv)
{
- char *srcstr = NULL, *srcfile = NULL, *envstr = NULL;
struct json_object *env = NULL, *modules = NULL, *o;
+ struct ut_source source = {};
struct ut_state *state;
bool dumponly = false;
+ FILE *envfile = NULL;
+ char *stdin = NULL;
int opt, rv = 0;
if (argc == 1)
@@ -258,16 +286,24 @@ main(int argc, char **argv)
goto out;
case 'i':
- srcfile = read_file(optarg);
+ if (source.fp)
+ fprintf(stderr, "Options -i and -s are exclusive\n");
- if (!srcfile) {
+ if (!strcmp(optarg, "-")) {
+ source.fp = read_stdin(&stdin);
+ source.filename = xstrdup("[stdin]");
+ }
+ else {
+ source.fp = fopen(optarg, "rb");
+ source.filename = xstrdup(optarg);
+ }
+
+ if (!source.fp) {
+ fprintf(stderr, "Failed to open %s: %s\n", optarg, strerror(errno));
rv = UT_ERROR_EXCEPTION;
goto out;
}
- if (strcmp(optarg, "-"))
- state->filename = strdup(optarg);
-
break;
case 'd':
@@ -283,7 +319,11 @@ main(int argc, char **argv)
break;
case 's':
- srcstr = optarg;
+ if (source.fp)
+ fprintf(stderr, "Options -i and -s are exclusive\n");
+
+ source.fp = fmemopen(optarg, strlen(optarg), "rb");
+ source.filename = xstrdup("[-s argument]");
break;
case 'S':
@@ -291,25 +331,28 @@ main(int argc, char **argv)
break;
case 'e':
- envstr = optarg;
+ envfile = fmemopen(optarg, strlen(optarg), "rb");
/* fallthrough */
case 'E':
- if (!envstr) {
- envstr = read_file(optarg);
-
- if (!envstr) {
+ if (!envfile) {
+ if (!strcmp(optarg, "-"))
+ envfile = read_stdin(&stdin);
+ else
+ envfile = fopen(optarg, "rb");
+
+ if (!envfile) {
+ fprintf(stderr, "Failed to open %s: %s\n", optarg, strerror(errno));
rv = UT_ERROR_EXCEPTION;
goto out;
}
}
- o = json_tokener_parse(envstr);
+ o = parse_envfile(envfile);
- if (envstr != optarg)
- free(envstr);
+ fclose(envfile);
- if (!json_object_is_type(o, json_type_object)) {
+ if (!o) {
fprintf(stderr, "Option -%c must point to a valid JSON object\n", opt);
rv = UT_ERROR_EXCEPTION;
@@ -334,30 +377,35 @@ main(int argc, char **argv)
}
}
- if (!srcstr && !srcfile && argv[optind] != NULL) {
- srcfile = read_file(argv[optind]);
+ if (!source.fp && argv[optind] != NULL) {
+ source.fp = fopen(argv[optind], "rb");
+ source.filename = xstrdup(argv[optind]);
state->skip_shebang = 1;
- state->filename = strdup(argv[optind]);
- if (!srcfile) {
+ if (!source.fp) {
+ fprintf(stderr, "Failed to open %s: %s\n", argv[optind], strerror(errno));
rv = UT_ERROR_EXCEPTION;
goto out;
}
}
- if ((srcstr && srcfile) || (!srcstr && !srcfile)) {
- fprintf(stderr, srcstr ? "Options -i and -s are exclusive\n" : "One of -i or -s is required\n");
-
+ if (!source.fp) {
+ fprintf(stderr, "One of -i or -s is required\n");
rv = UT_ERROR_EXCEPTION;
goto out;
}
- rv = parse(state, srcstr ? srcstr : srcfile, dumponly, env, modules);
+ state->source = xalloc(sizeof(source));
+ state->sources = state->source;
+ *state->source = source;
+
+ rv = parse(state, source.fp, dumponly, env, modules);
out:
json_object_put(modules);
json_object_put(env);
- free(srcfile);
+
+ free(stdin);
return rv;
}
diff --git a/parser.y b/parser.y
index 0ee4f30..f72e812 100644
--- a/parser.y
+++ b/parser.y
@@ -86,7 +86,7 @@ ut_no_empty_obj(struct ut_state *s, uint32_t off)
s->error.code = UT_ERROR_UNEXPECTED_TOKEN;
if (op)
- s->lex.off = op->off;
+ s->source->off = op->off;
for (i = 0; i < __T_MAX; i++)
if (yy_find_shift_action(yypParser, (YYCODETYPE)i) < YYNSTATE + YYNRULE)
diff --git a/tests/02_runtime/04_switch_case b/tests/02_runtime/04_switch_case
index 4a9d3e9..d83667f 100644
--- a/tests/02_runtime/04_switch_case
+++ b/tests/02_runtime/04_switch_case
@@ -242,7 +242,8 @@ one
-- Expect stderr --
Died
-In line 6, byte 6:
+In test(), line 6, byte 6:
+ at main function ([stdin]:17:11)
` die();`
Near here -----^