Change error output to be in the hands of the library user instead of the library

This commit is contained in:
rexy712 2020-04-08 16:20:18 -07:00
parent 1b649fb582
commit d7e8c5ccbb
11 changed files with 206 additions and 61 deletions

View File

@ -1,3 +1,4 @@
output
parse
parse_file
*.o

View File

@ -8,7 +8,7 @@ int main(int argc, char** argv){
}
//Read in json argument allowing all RJP extensions (comments, trailing comma)
RJP_value* root = rjp_parse(argv[1], RJP_PARSE_ALL_EXT);
RJP_value* root = rjp_parse(argv[1], RJP_PARSE_ALL_EXT, NULL);
//returns NULL on error
if(!root){

View File

@ -22,7 +22,7 @@ int main(int argc, char** argv){
fclose(fp);
//Read in json argument allowing all RJP extensions (comments, trailing comma)
RJP_value* root = rjp_parse(buffer, RJP_PARSE_ALL_EXT);
RJP_value* root = rjp_parse(buffer, RJP_PARSE_ALL_EXT, NULL);
//returns NULL on error
if(!root){

View File

@ -108,7 +108,11 @@ typedef struct RJP_parse_callback{
int(*read)(char*,int,void*);
void* data;
}RJP_parse_callback;
typedef struct RJP_parse_error{
void* parsestate;
int errcode;
int row, column;
}RJP_parse_error;
/***************** NON OBJECT OPERATIONS *******************/
void* rjp_alloc(RJP_index nbytes);
@ -121,10 +125,14 @@ RJP_index rjp_escape_strlen(const char* str);
RJP_string rjp_escape(const char* src);
/***************** GENERIC OPERATIONS *******************/
RJP_value* rjp_parse(const char* str, int flags);
RJP_value* rjp_parse_cback(int flags, RJP_parse_callback* cbacks);
RJP_value* rjp_simple_parse(const char* str);
RJP_value* rjp_parse(const char* str, int flags, RJP_parse_error* err);
RJP_value* rjp_parse_cback(int flags, RJP_parse_callback* cbacks, RJP_parse_error* err);
char* rjp_to_json(const RJP_value* root, int pretty);
char* rjp_parse_error_to_string(RJP_parse_error* err);
void rjp_delete_parse_error(RJP_parse_error* err);
RJP_value* rjp_new_null(void);
RJP_value* rjp_new_int(RJP_int val);
RJP_value* rjp_new_float(RJP_float val);

View File

@ -24,6 +24,9 @@
#define RJP_LEX_CBACK_BUFFER_SIZE 512
#define RJP_LEX_CBACK_STR_SIZE 64
#define RJP_LEX_TYPE_NORMAL 0
#define RJP_LEX_TYPE_CBACK 1
#define rjp_lex_accept 1
//DFA states. odd numbers are accepting states
typedef enum RJP_lex_category{
@ -79,9 +82,12 @@ typedef struct RJP_lex_state{
RJP_lex_category node; //tracks current dfa state
RJP_index length; //length of current token which parser will utilize
RJP_index offset; //offset in the str buffer that the parser should start from. must be 0 in callback lexer
int type;
}RJP_lex_state;
void irjp_init_lex_cback_state(RJP_lex_state* state);
void irjp_init_lex_state(RJP_lex_state* state);
void irjp_delete_lex_state(RJP_lex_state* state);
RJP_lex_category irjp_lex(RJP_lex_state* state);
RJP_lex_category irjp_lex_cback(RJP_lex_state* state, RJP_parse_callback* cbacks);

View File

@ -30,7 +30,9 @@ namespace rjp{
string to_json(const value& val, int format = RJP_FORMAT_PRETTY);
value parse_json(const rexy::string_base& str, RJP_parse_flag = RJP_PARSE_NONE);
value parse_json(const rexy::string_base& str, RJP_parse_flag, RJP_parse_error&);
value parse_json(const char* str, RJP_parse_flag = RJP_PARSE_NONE);
value parse_json(const char* str, RJP_parse_flag, RJP_parse_error&);
namespace detail{
template<int... Indexes>
struct sequence_tup{};
@ -66,6 +68,7 @@ namespace rjp{
return ph(c, size, typename sequence_gen<sizeof...(Args)>::type{});
}
};
RJP_value* parse_cback(RJP_parse_flag f, RJP_parse_callback* cb, RJP_parse_error& err);
RJP_value* parse_cback(RJP_parse_flag f, RJP_parse_callback* cb);
int irjp_parse_callback(char* dest, int size, void* userdata);
}
@ -77,6 +80,14 @@ namespace rjp{
cb.read = detail::irjp_parse_callback;
return value(detail::parse_cback(f, &cb), true);
}
template<class Func, class... Args>
value parse_json(RJP_parse_flag f, RJP_parse_error& err, Func&& func, Args&&... args){
RJP_parse_callback cb;
detail::invoker_impl<Func,Args...> inv(std::forward<Func>(func), std::forward<Args>(args)...);
cb.data = static_cast<void*>(&inv);
cb.read = detail::irjp_parse_callback;
return value(detail::parse_cback(f, &cb, err), true);
}
}

View File

@ -32,19 +32,28 @@ namespace rjp{
s.reset(rjp_to_json(val.raw(), format));
return s;
}
value parse_json(const rexy::string_base& str, RJP_parse_flag flags, RJP_parse_error& err){
return parse_json(str.get(), flags, err);
}
value parse_json(const rexy::string_base& str, RJP_parse_flag flags){
return value(rjp_parse(str.get(), flags), true);
return parse_json(str.get(), flags);
}
value parse_json(const char* str, RJP_parse_flag flags){
return value(rjp_parse(str, flags), true);
return value(rjp_parse(str, flags, NULL), true);
}
value parse_json(const char* str, RJP_parse_flag flags, RJP_parse_error& err){
return value(rjp_parse(str, flags, &err), true);
}
namespace detail{
int irjp_parse_callback(char* dest, int size, void* userdata){
invoker* inv = static_cast<invoker*>(userdata);
return inv->run(dest, size);
}
RJP_value* parse_cback(RJP_parse_flag f, RJP_parse_callback* cb, RJP_parse_error& err){
return rjp_parse_cback(f, cb, &err);
}
RJP_value* parse_cback(RJP_parse_flag f, RJP_parse_callback* cb){
return rjp_parse_cback(f, cb);
return rjp_parse_cback(f, cb, NULL);
}
template<>

View File

@ -19,6 +19,8 @@ int read_callback(char* c, int size, const char* data, int datalen, int& datapos
int handle_res(const rjp::value& res){
if(res.valid()){
fprintf(stderr, "Accepted\n");
}else{
fprintf(stderr, "Rejected\n");
}
return !res.valid();
}

View File

@ -27,7 +27,18 @@ void irjp_init_lex_cback_state(RJP_lex_state* state){
state->strcap = RJP_LEX_CBACK_STR_SIZE;
state->buff = rjp_alloc(RJP_LEX_CBACK_BUFFER_SIZE);
state->buffcap = RJP_LEX_CBACK_BUFFER_SIZE;
state->type = RJP_LEX_TYPE_CBACK;
}
void irjp_init_lex_state(RJP_lex_state* state){
state->type = RJP_LEX_TYPE_NORMAL;
}
void irjp_delete_lex_state(RJP_lex_state* state){
if(state->type == RJP_LEX_TYPE_CBACK){
rjp_free(state->str);
rjp_free(state->buff);
}
}
static RJP_lex_category irjp_lex_accept(RJP_lex_category val, RJP_lex_state* state){
state->node = rjp_lex_start;
return val;

View File

@ -28,8 +28,19 @@
#define RJP_INITIAL_PARSE_DEPTH 16
#define RJP_PARSE_STATUS_ERR 1
#define RJP_PARSE_STATUS_SUC 2
typedef enum RJP_parse_status{
RJP_PARSE_STATUS_SUC,
RJP_PARSE_STATUS_ERR,
RJP_PARSE_STATUS_MISSING_VALUE,
RJP_PARSE_STATUS_MISSING_COMMA,
RJP_PARSE_STATUS_INVALID,
RJP_PARSE_STATUS_NO_ROOT_VALUE,
RJP_PARSE_STATUS_MISSING_KEY,
RJP_PARSE_STATUS_MISSING_COLON,
RJP_PARSE_STATUS_EXCESS_DATA,
RJP_PARSE_STATUS_MISSING_CLOSE_BRACE,
}RJP_parse_status;
typedef enum RJP_parse_target{
@ -57,8 +68,8 @@ typedef struct RJP_parse_state{
RJP_value* lastadded;
RJP_lex_state lexstate;
int row, column;
const _Bool allow_comments;
const _Bool allow_trail_comma;
_Bool allow_comments;
_Bool allow_trail_comma;
}RJP_parse_state;
static void irjp_init_parse_stack(RJP_parse_stack* s){
@ -175,15 +186,14 @@ static void irjp_init_parse_state(RJP_parse_state* state, const char* str){
}
static void irjp_delete_parse_state(RJP_parse_state* state){
irjp_delete_parse_stack(&state->target_stack);
irjp_delete_lex_state(&state->lexstate);
}
static void irjp_delete_parse_state_no_preserve_root(RJP_parse_state* state){
irjp_delete_parse_state(state);
rjp_free_value(state->root);
state->root = NULL;
}
static inline int irjp_parse_error(RJP_parse_state* state, const char* str){
DIAG_PRINT(stderr, "%s: %d:%d\n", str, state->column, state->row);
return RJP_PARSE_STATUS_ERR;
}
static int irjp_parse_handle_lexcat(RJP_lex_category cat, RJP_parse_state* state){
if(cat == rjp_lex_line_comment || cat == rjp_lex_block_comment)
cat = irjp_convert_comment(state->allow_comments);
@ -196,14 +206,14 @@ static int irjp_parse_handle_lexcat(RJP_lex_category cat, RJP_parse_state* state
return RJP_PARSE_STATUS_SUC;
}
if(cat == rjp_lex_invalid)
return irjp_parse_error(state, "Invalid token");
return RJP_PARSE_STATUS_INVALID;
switch(irjp_parse_stack_current(&state->target_stack)){
case rjp_parse_start:
irjp_parse_stack_set(&state->target_stack, rjp_parse_end);
if(irjp_init_value(state->root, cat, state)){
return irjp_parse_error(state, "Expected value");
return RJP_PARSE_STATUS_NO_ROOT_VALUE;
}
break;
case rjp_parse_first_mem_key:
@ -216,10 +226,10 @@ static int irjp_parse_handle_lexcat(RJP_lex_category cat, RJP_parse_state* state
if(cat == rjp_lex_string){
irjp_parse_stack_set(&state->target_stack, rjp_parse_key_colon);
if(!irjp_add_value_to_object(state, state->lexstate.str+state->lexstate.offset, state->lexstate.length)){
return irjp_parse_error(state, "Expected member key");
return RJP_PARSE_STATUS_MISSING_KEY;
}
}else{
return irjp_parse_error(state, "Expected member key");
return RJP_PARSE_STATUS_MISSING_KEY;
}
}
break;
@ -232,19 +242,19 @@ static int irjp_parse_handle_lexcat(RJP_lex_category cat, RJP_parse_state* state
case rjp_parse_arr_value:
irjp_parse_stack_set(&state->target_stack, rjp_parse_arr_comma);
if(!irjp_add_value_to_array(cat, state))
return irjp_parse_error(state, "Expected value");
return RJP_PARSE_STATUS_MISSING_VALUE;
}
break;
case rjp_parse_key_colon:
if(cat != rjp_lex_colon)
return irjp_parse_error(state, "Expected member key");
return RJP_PARSE_STATUS_MISSING_COLON;
irjp_parse_stack_set(&state->target_stack, rjp_parse_obj_value);
break;
case rjp_parse_obj_value:
irjp_parse_stack_set(&state->target_stack, rjp_parse_obj_comma);
if(irjp_init_value(state->lastadded, cat, state)){
return irjp_parse_error(state, "Expected value");
return RJP_PARSE_STATUS_MISSING_VALUE;
}
break;
case rjp_parse_obj_comma:
@ -254,7 +264,7 @@ static int irjp_parse_handle_lexcat(RJP_lex_category cat, RJP_parse_state* state
irjp_parse_stack_pop(&state->target_stack);
state->curr = state->curr->parent;
}else{
return irjp_parse_error(state, "Expected comma");
return RJP_PARSE_STATUS_MISSING_COMMA;
}
break;
@ -265,13 +275,13 @@ static int irjp_parse_handle_lexcat(RJP_lex_category cat, RJP_parse_state* state
irjp_parse_stack_pop(&state->target_stack);
state->curr = state->curr->parent;
}else{
return irjp_parse_error(state, "Expected comma");
return RJP_PARSE_STATUS_MISSING_COMMA;
}
break;
case rjp_parse_end:
if(state->lexstate.str[state->lexstate.offset] != 0)
return irjp_parse_error(state, "Excess data after end of JSON");
return RJP_PARSE_STATUS_EXCESS_DATA;
};
return RJP_PARSE_STATUS_SUC;
}
@ -279,18 +289,19 @@ static int irjp_parse_handle_lexcat(RJP_lex_category cat, RJP_parse_state* state
//parse loop. it is a successful state though as it just indicates end of input.
static int irjp_handle_final_parse_token(RJP_parse_state* state, RJP_lex_category cat){
if(state->target_stack.position != 0)
return irjp_parse_error(state, "Missing closing brace");
return RJP_PARSE_STATUS_MISSING_CLOSE_BRACE;
if(cat == rjp_lex_end)
return RJP_PARSE_STATUS_SUC;
return irjp_parse_error(state, "Invalid Token");
return RJP_PARSE_STATUS_INVALID;
}
//Basic parse loop
static int irjp_parse(RJP_parse_state* state){
RJP_lex_category cat;
RJP_parse_status status;
for(cat = irjp_lex(&state->lexstate);cat & rjp_lex_accept;cat = irjp_lex(&state->lexstate),state->row += state->lexstate.length){
if(irjp_parse_handle_lexcat(cat, state) != RJP_PARSE_STATUS_SUC)
return RJP_PARSE_STATUS_ERR;
if((status = irjp_parse_handle_lexcat(cat, state)) != RJP_PARSE_STATUS_SUC)
return status;
}
return irjp_handle_final_parse_token(state, cat);
}
@ -298,43 +309,126 @@ static int irjp_parse(RJP_parse_state* state){
//Callback parse loop
static int irjp_parse_cback(RJP_parse_state* state, RJP_parse_callback* cback){
RJP_lex_category cat;
RJP_parse_status status;
for(cat = irjp_lex_cback(&state->lexstate, cback);cat & rjp_lex_accept;cat = irjp_lex_cback(&state->lexstate, cback),state->row += state->lexstate.length){
if(irjp_parse_handle_lexcat(cat, state) != RJP_PARSE_STATUS_SUC)
return RJP_PARSE_STATUS_ERR;
if((status = irjp_parse_handle_lexcat(cat, state)) != RJP_PARSE_STATUS_SUC)
return status;
}
return irjp_handle_final_parse_token(state, cat);
}
RJP_value* rjp_parse(const char* str, int flags){
RJP_parse_state state = {.allow_comments = (flags & RJP_PARSE_ALLOW_COMMENTS),
.allow_trail_comma = (flags & RJP_PARSE_ALLOW_TRAILING_COMMA)
char* rjp_parse_error_to_string(RJP_parse_error* err){
RJP_parse_state* state = (RJP_parse_state*)err->parsestate;
RJP_parse_status status = err->errcode;
char* buffer = NULL;
const char* format = NULL;
switch(status){
case RJP_PARSE_STATUS_MISSING_VALUE:
format = "Expected value before '%.*s'";
buffer = rjp_alloc(snprintf(NULL, 0, format, (int)state->lexstate.length, (state->lexstate.str + state->lexstate.offset)) + 1);
sprintf(buffer, format, (int)state->lexstate.length, (state->lexstate.str + state->lexstate.offset));
break;
case RJP_PARSE_STATUS_MISSING_COMMA:
format = "Expected comma before '%.*s'";
buffer = rjp_alloc(snprintf(NULL, 0, format, (int)state->lexstate.length, (state->lexstate.str + state->lexstate.offset)) + 1);
sprintf(buffer, format, (int)state->lexstate.length, (state->lexstate.str + state->lexstate.offset));
break;
case RJP_PARSE_STATUS_INVALID:
format = "Invalid lex token '%.*s'";
buffer = rjp_alloc(snprintf(NULL, 0, format, (int)state->lexstate.length, (state->lexstate.str + state->lexstate.offset)) + 1);
sprintf(buffer, format, (int)state->lexstate.length, (state->lexstate.str + state->lexstate.offset));
break;
case RJP_PARSE_STATUS_NO_ROOT_VALUE:
format = "Missing root JSON value";
buffer = rjp_alloc(snprintf(NULL, 0, format) + 1);
sprintf(buffer, format);
break;
case RJP_PARSE_STATUS_MISSING_KEY:
format = "Expected key before '%.*s'";
buffer = rjp_alloc(snprintf(NULL, 0, format, (int)state->lexstate.length, (state->lexstate.str + state->lexstate.offset)) + 1);
sprintf(buffer, format, (int)state->lexstate.length, (state->lexstate.str + state->lexstate.offset));
break;
case RJP_PARSE_STATUS_MISSING_COLON:
format = "Expected colon before '%.*s'";
buffer = rjp_alloc(snprintf(NULL, 0, format, (int)state->lexstate.length, (state->lexstate.str + state->lexstate.offset)) + 1);
sprintf(buffer, format, (int)state->lexstate.length, (state->lexstate.str + state->lexstate.offset));
break;
case RJP_PARSE_STATUS_EXCESS_DATA:
format = "Excess data after JSON";
buffer = rjp_alloc(snprintf(NULL, 0, format) + 1);
sprintf(buffer, format);
break;
case RJP_PARSE_STATUS_MISSING_CLOSE_BRACE:
format = "Missing closing brace";
buffer = rjp_alloc(snprintf(NULL, 0, format) + 1);
sprintf(buffer, format);
break;
default:
break;
};
irjp_init_parse_state(&state, str);
int status = irjp_parse(&state);
return buffer;
}
void rjp_delete_parse_error(RJP_parse_error* err){
irjp_delete_parse_state_no_preserve_root((RJP_parse_state*)err->parsestate);
rjp_free(err->parsestate);
}
RJP_value* rjp_simple_parse(const char* str){
return rjp_parse(str, RJP_PARSE_NONE, NULL);
}
RJP_value* rjp_parse(const char* str, int flags, RJP_parse_error* err){
RJP_parse_state* state = rjp_calloc(sizeof(RJP_parse_state), 1);
state->allow_comments = (flags & RJP_PARSE_ALLOW_COMMENTS);
state->allow_trail_comma = (flags & RJP_PARSE_ALLOW_TRAILING_COMMA);
irjp_init_parse_state(state, str);
irjp_init_lex_state(&state->lexstate);
int status = irjp_parse(state);
if(status == RJP_PARSE_STATUS_SUC){
irjp_delete_parse_stack(&state.target_stack);
return state.root;
irjp_delete_parse_state(state);
RJP_value* root = state->root;
rjp_free(state);
return root;
}else{
irjp_delete_parse_state(&state);
if(err){
err->parsestate = state;
err->errcode = status;
err->row = state->column;
err->column = state->row;
}else{
irjp_delete_parse_state_no_preserve_root(state);
rjp_free(state);
}
return NULL;
}
}
//Callback based parse. Runs identical to normal parsing except sets up callback
//lex state and calls callback lex function
RJP_value* rjp_parse_cback(int flags, RJP_parse_callback* cback){
RJP_parse_state state = {.allow_comments = (flags & RJP_PARSE_ALLOW_COMMENTS),
.allow_trail_comma = (flags & RJP_PARSE_ALLOW_TRAILING_COMMA)
};
irjp_init_parse_state(&state, NULL);
irjp_init_lex_cback_state(&state.lexstate);
int status = irjp_parse_cback(&state, cback);
rjp_free(state.lexstate.str);
rjp_free(state.lexstate.buff);
RJP_value* rjp_parse_cback(int flags, RJP_parse_callback* cback, RJP_parse_error* err){
RJP_parse_state* state = rjp_calloc(sizeof(RJP_parse_state), 1);
state->allow_comments = (flags & RJP_PARSE_ALLOW_COMMENTS);
state->allow_trail_comma = (flags & RJP_PARSE_ALLOW_TRAILING_COMMA);
irjp_init_parse_state(state, NULL);
irjp_init_lex_cback_state(&state->lexstate);
int status = irjp_parse_cback(state, cback);
if(status == RJP_PARSE_STATUS_SUC){
irjp_delete_parse_stack(&state.target_stack);
return state.root;
irjp_delete_parse_state(state);
RJP_value* root = state->root;
rjp_free(state);
return root;
}else{
irjp_delete_parse_state(&state);
if(err){
err->parsestate = state;
err->errcode = status;
err->row = state->column;
err->column = state->row;
}else{
irjp_delete_parse_state_no_preserve_root(state);
rjp_free(state);
}
return NULL;
}
}

View File

@ -25,15 +25,16 @@ int read_callback(char* c, int size, void* userdata){
return i;
}
int handle_res(RJP_value* res){
int handle_res(RJP_value* res, RJP_parse_error* err){
if(res){
fprintf(stderr, "Accepted\n");
}
#ifndef RJP_ENABLE_DIAGNOSTICS
else{
fprintf(stderr, "Failed\n");
char* buf = rjp_parse_error_to_string(err);
rjp_delete_parse_error(err);
fprintf(stderr, "%s\n", buf);
rjp_free(buf);
}
#endif
int failed = res == NULL;
rjp_free_value(res);
return failed;
@ -44,14 +45,16 @@ int test_cbacks(const char* str, RJP_parse_flag flags){
cbacks.read = read_callback;
cbacks.data = &cback_data;
RJP_value* res;
res = rjp_parse_cback(flags, &cbacks);
return handle_res(res);
RJP_parse_error err;
res = rjp_parse_cback(flags, &cbacks, &err);
return handle_res(res, &err);
}
int test(const char* str, RJP_parse_flag flags){
RJP_value* res;
res = rjp_parse(str, flags);
return handle_res(res);
RJP_parse_error err;
res = rjp_parse(str, flags, &err);
return handle_res(res, &err);
}
struct parse_pair{