diff options
-rw-r--r-- | ast.c | 97 | ||||
-rw-r--r-- | ast.h | 38 | ||||
-rw-r--r-- | eval.c | 96 | ||||
-rw-r--r-- | lexer.c | 65 | ||||
-rw-r--r-- | lib.c | 162 | ||||
-rw-r--r-- | lib.h | 2 | ||||
-rw-r--r-- | main.c | 174 | ||||
-rw-r--r-- | parser.y | 2 | ||||
-rw-r--r-- | tests/02_runtime/04_switch_case | 3 |
9 files changed, 396 insertions, 243 deletions
@@ -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; } @@ -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(); @@ -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; } @@ -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; @@ -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) @@ -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_ */ @@ -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; } @@ -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 -----^ |