diff --git a/builtin.c b/builtin.c index e90dd2cf..3c3ac5bd 100644 --- a/builtin.c +++ b/builtin.c @@ -677,9 +677,9 @@ static const char* const jq_builtins[] = { }; -int builtins_bind_one(block* bb, const char* code) { +static int builtins_bind_one(jq_state *jq, block* bb, const char* code) { struct locfile src; - locfile_init(&src, code, strlen(code)); + locfile_init(&src, jq, code, strlen(code)); block funcs; int nerrors = jq_parse_library(&src, &funcs); if (nerrors == 0) { @@ -689,14 +689,14 @@ int builtins_bind_one(block* bb, const char* code) { return nerrors; } -int slurp_lib(block* bb) { +static int slurp_lib(jq_state *jq, block* bb) { int nerrors = 0; char* home = getenv("HOME"); if (home) { // silently ignore no $HOME jv filename = jv_string_append_str(jv_string(home), "/.jq"); jv data = jv_load_file(jv_string_value(filename), 1); if (jv_is_valid(data)) { - nerrors = builtins_bind_one(bb, jv_string_value(data) ); + nerrors = builtins_bind_one(jq, bb, jv_string_value(data) ); } jv_free(filename); jv_free(data); @@ -704,14 +704,14 @@ int slurp_lib(block* bb) { return nerrors; } -int builtins_bind(block* bb) { - int nerrors = slurp_lib(bb); +int builtins_bind(jq_state *jq, block* bb) { + int nerrors = slurp_lib(jq, bb); if (nerrors) { block_free(*bb); return nerrors; } for (int i=(int)(sizeof(jq_builtins)/sizeof(jq_builtins[0]))-1; i>=0; i--) { - nerrors = builtins_bind_one(bb, jq_builtins[i]); + nerrors = builtins_bind_one(jq, bb, jq_builtins[i]); assert(!nerrors); } *bb = bind_bytecoded_builtins(*bb); diff --git a/builtin.h b/builtin.h index 1aab133f..bca9bae1 100644 --- a/builtin.h +++ b/builtin.h @@ -1,9 +1,10 @@ #ifndef BUILTIN_H #define BUILTIN_H +#include "jq.h" #include "bytecode.h" #include "compile.h" -int builtins_bind(block*); +int builtins_bind(jq_state *, block*); #endif diff --git a/execute.c b/execute.c index 4b05d793..4ed20d8d 100644 --- a/execute.c +++ b/execute.c @@ -1,4 +1,6 @@ #include +#include +#include #include #include #include @@ -19,6 +21,9 @@ struct jq_state { void *nomem_handler_data; struct bytecode* bc; + jq_err_cb err_cb; + void *err_cb_data; + struct stack stk; stack_ptr curr_frame; stack_ptr stk_top; @@ -238,15 +243,22 @@ static void jq_reset(jq_state *jq) { jq->subexp_nest = 0; } - - - -void print_error(jv value) { +static void print_error(jq_state *jq, jv value) { assert(!jv_is_valid(value)); jv msg = jv_invalid_get_msg(value); - if (jv_get_kind(msg) == JV_KIND_STRING) { - fprintf(stderr, "jq: error: %s\n", jv_string_value(msg)); - } + jv msg2; + if (jv_get_kind(msg) == JV_KIND_STRING) + msg2 = jv_string_fmt("jq: error: %s", jv_string_value(msg)); + else + msg2 = jv_string_fmt("jq: error: "); + jv_free(msg); + + if (jq->err_cb) + jq->err_cb(jq->err_cb_data, msg2); + else if (jv_get_kind(msg2) == JV_KIND_STRING) + fprintf(stderr, "%s\n", jv_string_value(msg2)); + else + fprintf(stderr, "jq: error: %s\n", strerror(ENOMEM)); jv_free(msg); } #define ON_BACKTRACK(op) ((op)+NUM_OPCODES) @@ -368,8 +380,8 @@ jv jq_next(jq_state *jq) { stack_push(jq, jv_object_set(objv, k, v)); stack_push(jq, stktop); } else { - print_error(jv_invalid_with_msg(jv_string_fmt("Cannot use %s as object key", - jv_kind_name(jv_get_kind(k))))); + print_error(jq, jv_invalid_with_msg(jv_string_fmt("Cannot use %s as object key", + jv_kind_name(jv_get_kind(k))))); jv_free(stktop); jv_free(v); jv_free(k); @@ -387,7 +399,7 @@ jv jq_next(jq_state *jq) { jv max = stack_pop(jq); if (jv_get_kind(*var) != JV_KIND_NUMBER || jv_get_kind(max) != JV_KIND_NUMBER) { - print_error(jv_invalid_with_msg(jv_string_fmt("Range bounds must be numeric"))); + print_error(jq, jv_invalid_with_msg(jv_string_fmt("Range bounds must be numeric"))); jv_free(max); goto do_backtrack; } else if (jv_number_value(jv_copy(*var)) >= jv_number_value(jv_copy(max))) { @@ -499,7 +511,7 @@ jv jq_next(jq_state *jq) { if (jv_is_valid(v)) { stack_push(jq, v); } else { - print_error(v); + print_error(jq, v); goto do_backtrack; } break; @@ -552,8 +564,8 @@ jv jq_next(jq_state *jq) { } } else { assert(opcode == EACH); - print_error(jv_invalid_with_msg(jv_string_fmt("Cannot iterate over %s", - jv_kind_name(jv_get_kind(container))))); + print_error(jq, jv_invalid_with_msg(jv_string_fmt("Cannot iterate over %s", + jv_kind_name(jv_get_kind(container))))); keep_going = 0; } @@ -624,7 +636,7 @@ jv jq_next(jq_state *jq) { if (jv_is_valid(top)) { stack_push(jq, top); } else { - print_error(top); + print_error(jq, top); goto do_backtrack; } break; @@ -682,10 +694,23 @@ jq_state *jq_init(void) { jq->fork_top = 0; jq->curr_frame = 0; + jq->err_cb = NULL; + jq->err_cb_data = NULL; + jq->path = jv_null(); return jq; } +void jq_set_error_cb(jq_state *jq, jq_err_cb cb, void *data) { + jq->err_cb = cb; + jq->err_cb_data = data; +} + +void jq_get_error_cb(jq_state *jq, jq_err_cb *cb, void **data) { + *cb = jq->err_cb; + *data = jq->err_cb_data; +} + void jq_set_nomem_handler(jq_state *jq, void (*nomem_handler)(void *), void *data) { jv_nomem_handler(nomem_handler, data); jq->nomem_handler = nomem_handler; @@ -729,7 +754,7 @@ int jq_compile_args(jq_state *jq, const char* str, jv args) { jv_nomem_handler(jq->nomem_handler, jq->nomem_handler_data); assert(jv_get_kind(args) == JV_KIND_ARRAY); struct locfile locations; - locfile_init(&locations, str, strlen(str)); + locfile_init(&locations, jq, str, strlen(str)); block program; jq_reset(jq); if (jq->bc) { @@ -746,13 +771,21 @@ int jq_compile_args(jq_state *jq, const char* str, jv args) { jv_free(name); } jv_free(args); - nerrors = builtins_bind(&program); + nerrors = builtins_bind(jq, &program); if (nerrors == 0) { nerrors = block_compile(program, &locations, &jq->bc); } } if (nerrors) { - fprintf(stderr, "%d compile %s\n", nerrors, nerrors > 1 ? "errors" : "error"); + jv s = jv_string_fmt("%d compile %s", nerrors, + nerrors > 1 ? "errors" : "error"); + if (jq->err_cb != NULL) + jq->err_cb(jq->err_cb_data, s); + else if (!jv_is_valid(s)) + fprintf(stderr, "Error formatting jq compilation errors: %s\n", strerror(errno)); + else + fprintf(stderr, "%s\n", jv_string_value(s)); + jv_free(s); } locfile_free(&locations); return jq->bc != NULL; diff --git a/jq.h b/jq.h index e35845c6..66b79387 100644 --- a/jq.h +++ b/jq.h @@ -1,12 +1,17 @@ #ifndef _JQ_H_ #define _JQ_H_ +#include #include enum {JQ_DEBUG_TRACE = 1}; typedef struct jq_state jq_state; +typedef void (*jq_err_cb)(void *, jv); + jq_state *jq_init(void); +void jq_set_error_cb(jq_state *, jq_err_cb, void *); +void jq_get_error_cb(jq_state *, jq_err_cb *, void **); void jq_set_nomem_handler(jq_state *, void (*)(void *), void *); int jq_compile(jq_state *, const char* str); int jq_compile_args(jq_state *, const char* str, jv args); diff --git a/locfile.c b/locfile.c index f1f17d0f..48cacd6f 100644 --- a/locfile.c +++ b/locfile.c @@ -1,12 +1,16 @@ +#include +#include +#include #include #include -#include -#include +#include +#include "jq.h" #include "jv_alloc.h" #include "locfile.h" -void locfile_init(struct locfile* l, const char* data, int length) { +void locfile_init(struct locfile* l, jq_state *jq, const char* data, int length) { + l->jq = jq; l->data = data; l->length = length; l->nlines = 1; @@ -43,23 +47,57 @@ static int locfile_line_length(struct locfile* l, int line) { } void locfile_locate(struct locfile* l, location loc, const char* fmt, ...) { + jq_err_cb cb; + void *cb_data; va_list fmtargs; va_start(fmtargs, fmt); - vfprintf(stderr, fmt, fmtargs); - va_end(fmtargs); - fprintf(stderr, "\n"); + int startline; + int offset; + + if (loc.start != -1) { + startline = locfile_get_line(l, loc.start); + offset = l->linemap[startline]; + } + + jq_get_error_cb(l->jq, &cb, &cb_data); + + jv m1 = jv_string_vfmt(fmt, fmtargs); + if (!jv_is_valid(m1)) { + jv_free(m1); + goto enomem; + } + jv m2; if (loc.start == -1) { - fprintf(stderr, "\n"); + m2 = jv_string_fmt("%s\n", jv_string_value(m1)); + if (cb) + cb(cb_data, m2); + else + fprintf(stderr, "%s", jv_string_value(m2)); + jv_free(m1); + jv_free(m2); return; } - int startline = locfile_get_line(l, loc.start); - int offset = l->linemap[startline]; - fprintf(stderr, "%.*s\n", locfile_line_length(l, startline), l->data + offset); - fprintf(stderr, "%*s", loc.start - offset, ""); - for (int i = loc.start; - i < loc.end && i < offset + locfile_line_length(l, startline); - i++){ - fprintf(stderr, "^"); + m2 = jv_string_fmt("%s\n%.*s%*s", jv_string_value(m1), + locfile_line_length(l, startline), l->data + offset, + loc.start - offset, ""); + jv_free(m1); + if (!jv_is_valid(m2)) { + jv_free(m2); + goto enomem; } - fprintf(stderr, "\n"); + if (cb) + cb(cb_data, m2); + else + fprintf(stderr, "%s", jv_string_value(m2)); + jv_free(m2); + return; + +enomem: + if (cb != NULL) + cb(cb_data, jv_invalid()); + else if (errno == ENOMEM || errno == 0) + vfprintf(stderr, "Error formatting jq compilation error: %s", strerror(errno ? errno : ENOMEM)); + else + vfprintf(stderr, "Error formatting jq compilation error: %s", strerror(errno)); + return; } diff --git a/locfile.h b/locfile.h index 35907974..4e20df27 100644 --- a/locfile.h +++ b/locfile.h @@ -1,6 +1,8 @@ #ifndef _LOCFILE_H #define _LOCFILE_H +#include "jq.h" + typedef struct { int start, end; } location; @@ -12,9 +14,11 @@ struct locfile { int length; int* linemap; int nlines; + char *error; + jq_state *jq; }; -void locfile_init(struct locfile* l, const char* data, int length); +void locfile_init(struct locfile* l, jq_state *jq, const char* data, int length); void locfile_free(struct locfile* l);