Add callback interface for errors
Printing to stderr is not the right answer for a library.
This commit is contained in:
1 parent
04bc2ef7cf
commit
eb165459aa
6 files changed
+123
-42
No files matched your search
@@ -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;
|
struct locfile src;
|
||||||
locfile_init(&src, code, strlen(code));
|
locfile_init(&src, jq, code, strlen(code));
|
||||||
block funcs;
|
block funcs;
|
||||||
int nerrors = jq_parse_library(&src, &funcs);
|
int nerrors = jq_parse_library(&src, &funcs);
|
||||||
if (nerrors == 0) {
|
if (nerrors == 0) {
|
||||||
@@ -689,14 +689,14 @@ int builtins_bind_one(block* bb, const char* code) {
|
|||||||
return nerrors;
|
return nerrors;
|
||||||
}
|
}
|
||||||
|
|
||||||
int slurp_lib(block* bb) {
|
static int slurp_lib(jq_state *jq, block* bb) {
|
||||||
int nerrors = 0;
|
int nerrors = 0;
|
||||||
char* home = getenv("HOME");
|
char* home = getenv("HOME");
|
||||||
if (home) { // silently ignore no $HOME
|
if (home) { // silently ignore no $HOME
|
||||||
jv filename = jv_string_append_str(jv_string(home), "/.jq");
|
jv filename = jv_string_append_str(jv_string(home), "/.jq");
|
||||||
jv data = jv_load_file(jv_string_value(filename), 1);
|
jv data = jv_load_file(jv_string_value(filename), 1);
|
||||||
if (jv_is_valid(data)) {
|
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(filename);
|
||||||
jv_free(data);
|
jv_free(data);
|
||||||
@@ -704,14 +704,14 @@ int slurp_lib(block* bb) {
|
|||||||
return nerrors;
|
return nerrors;
|
||||||
}
|
}
|
||||||
|
|
||||||
int builtins_bind(block* bb) {
|
int builtins_bind(jq_state *jq, block* bb) {
|
||||||
int nerrors = slurp_lib(bb);
|
int nerrors = slurp_lib(jq, bb);
|
||||||
if (nerrors) {
|
if (nerrors) {
|
||||||
block_free(*bb);
|
block_free(*bb);
|
||||||
return nerrors;
|
return nerrors;
|
||||||
}
|
}
|
||||||
for (int i=(int)(sizeof(jq_builtins)/sizeof(jq_builtins[0]))-1; i>=0; i--) {
|
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);
|
assert(!nerrors);
|
||||||
}
|
}
|
||||||
*bb = bind_bytecoded_builtins(*bb);
|
*bb = bind_bytecoded_builtins(*bb);
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
#ifndef BUILTIN_H
|
#ifndef BUILTIN_H
|
||||||
#define BUILTIN_H
|
#define BUILTIN_H
|
||||||
|
|
||||||
|
#include "jq.h"
|
||||||
#include "bytecode.h"
|
#include "bytecode.h"
|
||||||
#include "compile.h"
|
#include "compile.h"
|
||||||
|
|
||||||
int builtins_bind(block*);
|
int builtins_bind(jq_state *, block*);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -19,6 +21,9 @@ struct jq_state {
|
|||||||
void *nomem_handler_data;
|
void *nomem_handler_data;
|
||||||
struct bytecode* bc;
|
struct bytecode* bc;
|
||||||
|
|
||||||
|
jq_err_cb err_cb;
|
||||||
|
void *err_cb_data;
|
||||||
|
|
||||||
struct stack stk;
|
struct stack stk;
|
||||||
stack_ptr curr_frame;
|
stack_ptr curr_frame;
|
||||||
stack_ptr stk_top;
|
stack_ptr stk_top;
|
||||||
@@ -238,15 +243,22 @@ static void jq_reset(jq_state *jq) {
|
|||||||
jq->subexp_nest = 0;
|
jq->subexp_nest = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void print_error(jq_state *jq, jv value) {
|
||||||
|
|
||||||
|
|
||||||
void print_error(jv value) {
|
|
||||||
assert(!jv_is_valid(value));
|
assert(!jv_is_valid(value));
|
||||||
jv msg = jv_invalid_get_msg(value);
|
jv msg = jv_invalid_get_msg(value);
|
||||||
if (jv_get_kind(msg) == JV_KIND_STRING) {
|
jv msg2;
|
||||||
fprintf(stderr, "jq: error: %s\n", jv_string_value(msg));
|
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: <unknown>");
|
||||||
|
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);
|
jv_free(msg);
|
||||||
}
|
}
|
||||||
#define ON_BACKTRACK(op) ((op)+NUM_OPCODES)
|
#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, jv_object_set(objv, k, v));
|
||||||
stack_push(jq, stktop);
|
stack_push(jq, stktop);
|
||||||
} else {
|
} else {
|
||||||
print_error(jv_invalid_with_msg(jv_string_fmt("Cannot use %s as object key",
|
print_error(jq, jv_invalid_with_msg(jv_string_fmt("Cannot use %s as object key",
|
||||||
jv_kind_name(jv_get_kind(k)))));
|
jv_kind_name(jv_get_kind(k)))));
|
||||||
jv_free(stktop);
|
jv_free(stktop);
|
||||||
jv_free(v);
|
jv_free(v);
|
||||||
jv_free(k);
|
jv_free(k);
|
||||||
@@ -387,7 +399,7 @@ jv jq_next(jq_state *jq) {
|
|||||||
jv max = stack_pop(jq);
|
jv max = stack_pop(jq);
|
||||||
if (jv_get_kind(*var) != JV_KIND_NUMBER ||
|
if (jv_get_kind(*var) != JV_KIND_NUMBER ||
|
||||||
jv_get_kind(max) != 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);
|
jv_free(max);
|
||||||
goto do_backtrack;
|
goto do_backtrack;
|
||||||
} else if (jv_number_value(jv_copy(*var)) >= jv_number_value(jv_copy(max))) {
|
} 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)) {
|
if (jv_is_valid(v)) {
|
||||||
stack_push(jq, v);
|
stack_push(jq, v);
|
||||||
} else {
|
} else {
|
||||||
print_error(v);
|
print_error(jq, v);
|
||||||
goto do_backtrack;
|
goto do_backtrack;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -552,8 +564,8 @@ jv jq_next(jq_state *jq) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assert(opcode == EACH);
|
assert(opcode == EACH);
|
||||||
print_error(jv_invalid_with_msg(jv_string_fmt("Cannot iterate over %s",
|
print_error(jq, jv_invalid_with_msg(jv_string_fmt("Cannot iterate over %s",
|
||||||
jv_kind_name(jv_get_kind(container)))));
|
jv_kind_name(jv_get_kind(container)))));
|
||||||
keep_going = 0;
|
keep_going = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -624,7 +636,7 @@ jv jq_next(jq_state *jq) {
|
|||||||
if (jv_is_valid(top)) {
|
if (jv_is_valid(top)) {
|
||||||
stack_push(jq, top);
|
stack_push(jq, top);
|
||||||
} else {
|
} else {
|
||||||
print_error(top);
|
print_error(jq, top);
|
||||||
goto do_backtrack;
|
goto do_backtrack;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -682,10 +694,23 @@ jq_state *jq_init(void) {
|
|||||||
jq->fork_top = 0;
|
jq->fork_top = 0;
|
||||||
jq->curr_frame = 0;
|
jq->curr_frame = 0;
|
||||||
|
|
||||||
|
jq->err_cb = NULL;
|
||||||
|
jq->err_cb_data = NULL;
|
||||||
|
|
||||||
jq->path = jv_null();
|
jq->path = jv_null();
|
||||||
return jq;
|
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) {
|
void jq_set_nomem_handler(jq_state *jq, void (*nomem_handler)(void *), void *data) {
|
||||||
jv_nomem_handler(nomem_handler, data);
|
jv_nomem_handler(nomem_handler, data);
|
||||||
jq->nomem_handler = nomem_handler;
|
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);
|
jv_nomem_handler(jq->nomem_handler, jq->nomem_handler_data);
|
||||||
assert(jv_get_kind(args) == JV_KIND_ARRAY);
|
assert(jv_get_kind(args) == JV_KIND_ARRAY);
|
||||||
struct locfile locations;
|
struct locfile locations;
|
||||||
locfile_init(&locations, str, strlen(str));
|
locfile_init(&locations, jq, str, strlen(str));
|
||||||
block program;
|
block program;
|
||||||
jq_reset(jq);
|
jq_reset(jq);
|
||||||
if (jq->bc) {
|
if (jq->bc) {
|
||||||
@@ -746,13 +771,21 @@ int jq_compile_args(jq_state *jq, const char* str, jv args) {
|
|||||||
jv_free(name);
|
jv_free(name);
|
||||||
}
|
}
|
||||||
jv_free(args);
|
jv_free(args);
|
||||||
nerrors = builtins_bind(&program);
|
nerrors = builtins_bind(jq, &program);
|
||||||
if (nerrors == 0) {
|
if (nerrors == 0) {
|
||||||
nerrors = block_compile(program, &locations, &jq->bc);
|
nerrors = block_compile(program, &locations, &jq->bc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (nerrors) {
|
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);
|
locfile_free(&locations);
|
||||||
return jq->bc != NULL;
|
return jq->bc != NULL;
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
#ifndef _JQ_H_
|
#ifndef _JQ_H_
|
||||||
#define _JQ_H_
|
#define _JQ_H_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
#include <jv.h>
|
#include <jv.h>
|
||||||
|
|
||||||
enum {JQ_DEBUG_TRACE = 1};
|
enum {JQ_DEBUG_TRACE = 1};
|
||||||
|
|
||||||
typedef struct jq_state jq_state;
|
typedef struct jq_state jq_state;
|
||||||
|
typedef void (*jq_err_cb)(void *, jv);
|
||||||
|
|
||||||
jq_state *jq_init(void);
|
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 *);
|
void jq_set_nomem_handler(jq_state *, void (*)(void *), void *);
|
||||||
int jq_compile(jq_state *, const char* str);
|
int jq_compile(jq_state *, const char* str);
|
||||||
int jq_compile_args(jq_state *, const char* str, jv args);
|
int jq_compile_args(jq_state *, const char* str, jv args);
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdarg.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <assert.h>
|
#include <string.h>
|
||||||
#include <stdarg.h>
|
#include "jq.h"
|
||||||
#include "jv_alloc.h"
|
#include "jv_alloc.h"
|
||||||
#include "locfile.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->data = data;
|
||||||
l->length = length;
|
l->length = length;
|
||||||
l->nlines = 1;
|
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, ...) {
|
void locfile_locate(struct locfile* l, location loc, const char* fmt, ...) {
|
||||||
|
jq_err_cb cb;
|
||||||
|
void *cb_data;
|
||||||
va_list fmtargs;
|
va_list fmtargs;
|
||||||
va_start(fmtargs, fmt);
|
va_start(fmtargs, fmt);
|
||||||
vfprintf(stderr, fmt, fmtargs);
|
int startline;
|
||||||
va_end(fmtargs);
|
int offset;
|
||||||
fprintf(stderr, "\n");
|
|
||||||
|
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) {
|
if (loc.start == -1) {
|
||||||
fprintf(stderr, "<unknown location>\n");
|
m2 = jv_string_fmt("%s\n<unknown location>", 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;
|
return;
|
||||||
}
|
}
|
||||||
int startline = locfile_get_line(l, loc.start);
|
m2 = jv_string_fmt("%s\n%.*s%*s", jv_string_value(m1),
|
||||||
int offset = l->linemap[startline];
|
locfile_line_length(l, startline), l->data + offset,
|
||||||
fprintf(stderr, "%.*s\n", locfile_line_length(l, startline), l->data + offset);
|
loc.start - offset, "");
|
||||||
fprintf(stderr, "%*s", loc.start - offset, "");
|
jv_free(m1);
|
||||||
for (int i = loc.start;
|
if (!jv_is_valid(m2)) {
|
||||||
i < loc.end && i < offset + locfile_line_length(l, startline);
|
jv_free(m2);
|
||||||
i++){
|
goto enomem;
|
||||||
fprintf(stderr, "^");
|
|
||||||
}
|
}
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
#ifndef _LOCFILE_H
|
#ifndef _LOCFILE_H
|
||||||
#define _LOCFILE_H
|
#define _LOCFILE_H
|
||||||
|
|
||||||
|
#include "jq.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int start, end;
|
int start, end;
|
||||||
} location;
|
} location;
|
||||||
@@ -12,9 +14,11 @@ struct locfile {
|
|||||||
int length;
|
int length;
|
||||||
int* linemap;
|
int* linemap;
|
||||||
int nlines;
|
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);
|
void locfile_free(struct locfile* l);
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user