Add parse functionality
This commit is contained in:
parent
34171aa795
commit
f899b54adc
273
src/load.c
Normal file
273
src/load.c
Normal file
@ -0,0 +1,273 @@
|
||||
/* This software is licensed by the MIT License, see LICENSE file */
|
||||
/* Copyright © 2022 Gregory Lirent */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include "buffer.h"
|
||||
|
||||
#include "../modules/libcdsb/include/list.h"
|
||||
#include "../modules/libcdsb/include/map.h"
|
||||
#include "../modules/libcdsb/include/string.h"
|
||||
|
||||
typedef struct {
|
||||
FILE* stream;
|
||||
char cur;
|
||||
} reader_t;
|
||||
|
||||
#define next libcjsonp_builtin_read_next
|
||||
#define next_sign libcjsonp_builtin_read_next_sign
|
||||
|
||||
static bool libcjsonp_builtin_parse(value_t* x, reader_t* s);
|
||||
|
||||
static inline char libcjsonp_builtin_read_next(reader_t *x) {
|
||||
int cur = fgetc(x->stream);
|
||||
|
||||
return x->cur = (cur == EOF) ? 0 : cur;
|
||||
}
|
||||
|
||||
static inline char libcjsonp_builtin_read_next_sign(reader_t *x) {
|
||||
for (int cur;;) {
|
||||
switch (cur = fgetc(x->stream)) {
|
||||
default: return x->cur = cur;
|
||||
case EOF: return x->cur = 0;
|
||||
|
||||
case '\n': case ' ': case '\r':
|
||||
case '\t': case '\v': break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool libcjsonp_builtin_parse_number(value_t* x, reader_t* s) {
|
||||
char b[64], *p = b;
|
||||
bool is_float = false;
|
||||
|
||||
for (;;) {
|
||||
switch (*p++ = s->cur) {
|
||||
case '.': case 'E': case 'e': if (p > b+1) {
|
||||
is_float = true;
|
||||
break;
|
||||
} case 0: return false;
|
||||
|
||||
case '\n': case ' ': case '\r':
|
||||
case '\t': case '\v': next_sign(s);
|
||||
default: p[-1] = 0; goto break_;
|
||||
|
||||
case '-': case '+': case '0': case '1':
|
||||
case '2': case '3': case '4': case '5':
|
||||
case '6': case '7': case '8': case '9': break;
|
||||
}
|
||||
next(s);
|
||||
}
|
||||
|
||||
break_:
|
||||
|
||||
return (bool)libcjsonp_builtin_fetch_number(x, b, is_float);
|
||||
}
|
||||
|
||||
static bool libcjsonp_builtin_parse_string(vtype_string* x, reader_t* s) {
|
||||
|
||||
char *mem, *ptr;
|
||||
size_t nmemb, size;
|
||||
vtype_uint8 esc;
|
||||
|
||||
mem = 0;
|
||||
nmemb = 0;
|
||||
size = 0;
|
||||
esc = 0;
|
||||
|
||||
while (next(s)) {
|
||||
if (s->cur == '"' && !esc) {
|
||||
if (!nmemb) {
|
||||
mem = realloc(mem, size + 1);
|
||||
mem[size] = 0;
|
||||
} else *ptr = 0;
|
||||
|
||||
x->buffer = mem;
|
||||
libcjsonp_string_unescape(x);
|
||||
|
||||
return true;
|
||||
} else if (s->cur == '\\') {
|
||||
esc ^= 1;
|
||||
} else esc = 0;
|
||||
|
||||
if (nmemb--) {
|
||||
*ptr++ = s->cur;
|
||||
} else {
|
||||
mem = realloc(mem, size += nmemb = buffer_block);
|
||||
ptr = mem + (size - buffer_block);
|
||||
}
|
||||
}
|
||||
|
||||
libcdsb_free(mem);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool libcjsonp_builtin_parse_map(vtype_map* x, reader_t* s) {
|
||||
vtype_string name;
|
||||
value_t value;
|
||||
|
||||
map_init(x, VTYPE_STRING);
|
||||
|
||||
if (next_sign(s) == '}')
|
||||
return true;
|
||||
|
||||
for (;;) {
|
||||
if (!libcjsonp_builtin_parse_string(&name, s))
|
||||
return false;
|
||||
|
||||
if (next_sign(s) != ':') {
|
||||
goto bad_;
|
||||
} else next_sign(s);
|
||||
|
||||
if (!libcjsonp_builtin_parse(&value, s))
|
||||
goto bad_;
|
||||
|
||||
switch (s->cur) {
|
||||
case ',':
|
||||
libcdsb_map_inject(x, &name, VTYPE_STRING, value.value, value.type);
|
||||
next_sign(s);
|
||||
break;
|
||||
case '}':
|
||||
return true;
|
||||
default:
|
||||
if (value.type == VTYPE_MAP) map_free ((void*)value.value);
|
||||
else if (value.type == VTYPE_LIST) list_free ((void*)value.value);
|
||||
else if (value.type == VTYPE_STRING) string_free((void*)value.value);
|
||||
bad_:
|
||||
string_free(&name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool libcjsonp_builtin_parse_list(vtype_list* x, reader_t* s) {
|
||||
value_t value;
|
||||
|
||||
list_init(x);
|
||||
|
||||
if (next_sign(s) == ']')
|
||||
return true;
|
||||
|
||||
for (;;) {
|
||||
if (!libcjsonp_builtin_parse(&value, s))
|
||||
return false;
|
||||
|
||||
switch (s->cur) {
|
||||
case ',':
|
||||
libcdsb_list_attach(x, -1, value.value, value.type, 1);
|
||||
next_sign(s);
|
||||
break;
|
||||
case ']':
|
||||
return true;
|
||||
default:
|
||||
if (value.type == VTYPE_MAP) map_free ((void*)value.value);
|
||||
else if (value.type == VTYPE_LIST) list_free ((void*)value.value);
|
||||
else if (value.type == VTYPE_STRING) string_free((void*)value.value);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool libcjsonp_builtin_parse(value_t* x, reader_t* s) {
|
||||
bool ret = true;
|
||||
|
||||
switch (s->cur) {
|
||||
case '{': if (!(ret = libcjsonp_builtin_parse_map((void*)x->value, s))) {
|
||||
map_free((void*)x->value);
|
||||
} else x->type = VTYPE_MAP;
|
||||
break;
|
||||
|
||||
case '[': if (!(ret = libcjsonp_builtin_parse_list((void*)x->value, s))) {
|
||||
list_free((void*)x->value);
|
||||
} else x->type = VTYPE_LIST;
|
||||
break;
|
||||
|
||||
case '"': if (!(ret = libcjsonp_builtin_parse_string((void*)x->value, s))) {
|
||||
string_free((void*)x->value);
|
||||
} else x->type = VTYPE_STRING;
|
||||
break;
|
||||
|
||||
case 't': if (next(s) == 'r' && next(s) == 'u' && next(s) == 'e') {
|
||||
x->value->b = true;
|
||||
x->type = VTYPE_BOOLEAN;
|
||||
} else ret = false;
|
||||
break;
|
||||
|
||||
case 'f': if (next(s) == 'a' && next(s) == 'l' && next(s) == 's' && next(s) == 'e') {
|
||||
x->value->b = false;
|
||||
x->type = VTYPE_BOOLEAN;
|
||||
} else ret = false;
|
||||
break;
|
||||
|
||||
case 'n': if (next(s) == 'u' && next(s) == 'l' && next(s) == 'l') {
|
||||
x->value->ptr = 0;
|
||||
x->type = VTYPE_POINTER;
|
||||
} else ret = false;
|
||||
break;
|
||||
|
||||
case '-': case '0': case '1': case '2': case '3':
|
||||
case '4': case '5': case '6': case '7': case '8':
|
||||
case '9': return libcjsonp_builtin_parse_number(x, s);
|
||||
}
|
||||
|
||||
if (ret) next_sign(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*#####################################################################################################################*/
|
||||
|
||||
bool json_load(json_t* x, FILE* s) {
|
||||
|
||||
reader_t reader;
|
||||
value_t value;
|
||||
|
||||
reader.stream = s;
|
||||
next_sign(&reader);
|
||||
|
||||
if (libcjsonp_builtin_parse(&value, &reader)) {
|
||||
switch (x->type = value.type) {
|
||||
default:
|
||||
#ifndef NDEBUG
|
||||
abort();
|
||||
#endif
|
||||
|
||||
case VTYPE_BOOLEAN:
|
||||
case VTYPE_INT8:
|
||||
case VTYPE_UINT8: x->data = libcdsb_memndup(value.value, sizeof(vtype_uint8));
|
||||
break;
|
||||
|
||||
case VTYPE_INT16:
|
||||
case VTYPE_UINT16: x->data = libcdsb_memndup(value.value, sizeof(vtype_uint16));
|
||||
break;
|
||||
|
||||
case VTYPE_INT32:
|
||||
case VTYPE_UINT32:
|
||||
x86_: x->data = libcdsb_memndup(value.value, sizeof(vtype_uint32));
|
||||
break;
|
||||
|
||||
case VTYPE_POINTER: if (sizeof(void*) == sizeof(vtype_uint32)) goto x86_;
|
||||
case VTYPE_INT64:
|
||||
case VTYPE_UINT64: x->data = libcdsb_memndup(value.value, sizeof(vtype_uint64));
|
||||
break;
|
||||
|
||||
case VTYPE_LDOUBLE: x->data = libcdsb_memndup(value.value, sizeof(vtype_ldouble));
|
||||
break;
|
||||
|
||||
case VTYPE_MAP: x->data = libcdsb_memndup(value.value, sizeof(vtype_map));
|
||||
break;
|
||||
|
||||
case VTYPE_LIST: x->data = libcdsb_memndup(value.value, sizeof(vtype_list));
|
||||
break;
|
||||
|
||||
case VTYPE_STRING: x->data = libcdsb_memndup(value.value, sizeof(vtype_string));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
memset(x, 0, sizeof(*x));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
312
src/parse.c
Normal file
312
src/parse.c
Normal file
@ -0,0 +1,312 @@
|
||||
/* This software is licensed by the MIT License, see LICENSE file */
|
||||
/* Copyright © 2022 Gregory Lirent */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include "include.h"
|
||||
|
||||
#include "../modules/libcdsb/include/list.h"
|
||||
#include "../modules/libcdsb/include/map.h"
|
||||
#include "../modules/libcdsb/include/string.h"
|
||||
|
||||
#define skip_spaces libcjsonp_builtin_skip_spaces
|
||||
|
||||
static const char* libcjsonp_builtin_parse(value_t* x, const char* s);
|
||||
|
||||
static char* libcjsonp_builtin_skip_spaces(const char* s) {
|
||||
static size_t m[32/(sizeof(size_t))] = {0};
|
||||
|
||||
vtype_uint8* l;
|
||||
|
||||
if (sizeof(size_t) == 8) {
|
||||
m[0] = 0x0000000100002e00UL;
|
||||
} else {
|
||||
m[0] = 0x00002e00UL;
|
||||
m[1] = 0x00000001UL;
|
||||
}
|
||||
|
||||
if (!s)
|
||||
return (char*)s;
|
||||
|
||||
l = (void*)s;
|
||||
|
||||
while (m[*l/(8*sizeof(size_t))]&((size_t)1<<(*l%(8*sizeof(size_t)))))
|
||||
++l;
|
||||
|
||||
return (void*)l;
|
||||
}
|
||||
|
||||
static const char* libcjsonp_builtin_parse_number(value_t* x, const char* s) {
|
||||
char *p = (void*)s;
|
||||
bool is_float = false;
|
||||
|
||||
for (bool end = false;!end;) {
|
||||
switch (*p++) {
|
||||
case '.': case 'E': case 'e': if (p > s+1) {
|
||||
is_float = true;
|
||||
break;
|
||||
} case 0: return 0;
|
||||
|
||||
default: end = true;
|
||||
case '-': case '+': case '0': case '1':
|
||||
case '2': case '3': case '4': case '5':
|
||||
case '6': case '7': case '8': case '9': break;
|
||||
}
|
||||
}
|
||||
|
||||
return libcjsonp_builtin_fetch_number(x, s, is_float);
|
||||
}
|
||||
|
||||
static const char* libcjsonp_builtin_parse_string(vtype_string* x, const char* s) {
|
||||
const char *e;
|
||||
|
||||
if (*(s++) == '"') {
|
||||
e = s;
|
||||
|
||||
do {
|
||||
if (*e == '"' && e[-1] != '\\') {
|
||||
x->buffer = libcdsb_strndup(s, e-s);
|
||||
libcjsonp_string_unescape(x);
|
||||
|
||||
return e + 1;
|
||||
}
|
||||
} while (*++e);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char* libcjsonp_builtin_parse_map(vtype_map* x, const char* s) {
|
||||
vtype_string name;
|
||||
value_t value;
|
||||
|
||||
map_init(x, VTYPE_STRING);
|
||||
|
||||
if (*(s++) != '{') return 0;
|
||||
if (*(s = skip_spaces(s)) == '}') return s+1;
|
||||
|
||||
do {
|
||||
if ((s = libcjsonp_builtin_parse_string(&name, s))) {
|
||||
s = skip_spaces(s);
|
||||
} else return 0;
|
||||
|
||||
if (*s++ != ':') goto bad_;
|
||||
|
||||
if ((s = libcjsonp_builtin_parse(&value, s))) {
|
||||
s = skip_spaces(s);
|
||||
} else goto bad_;
|
||||
|
||||
if (*s == ',' || *s == '}') {
|
||||
libcdsb_map_inject(x, &name, VTYPE_STRING, value.value, value.type);
|
||||
|
||||
if (*s++ == '}')
|
||||
return s;
|
||||
|
||||
s = skip_spaces(s);
|
||||
} else {
|
||||
if (value.type == VTYPE_MAP) map_free ((void*)value.value);
|
||||
else if (value.type == VTYPE_LIST) list_free ((void*)value.value);
|
||||
else if (value.type == VTYPE_STRING) string_free((void*)value.value);
|
||||
bad_:
|
||||
string_free(&name);
|
||||
return 0;
|
||||
}
|
||||
} while (*s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char* libcjsonp_builtin_parse_list(vtype_list* x, const char* s) {
|
||||
value_t value;
|
||||
|
||||
list_init(x);
|
||||
|
||||
if (*(s++) != '[') return 0;
|
||||
if (*(s = skip_spaces(s)) == ']') return s+1;
|
||||
|
||||
do {
|
||||
if ((s = libcjsonp_builtin_parse(&value, s))) {
|
||||
s = skip_spaces(s);
|
||||
} else return 0;
|
||||
|
||||
if (*s == ',' || *s == ']') {
|
||||
libcdsb_list_attach(x, -1, value.value, value.type, 1);
|
||||
|
||||
if (*s++ == ']')
|
||||
return s;
|
||||
} else {
|
||||
if (value.type == VTYPE_MAP) map_free ((void*)value.value);
|
||||
else if (value.type == VTYPE_LIST) list_free ((void*)value.value);
|
||||
else if (value.type == VTYPE_STRING) string_free((void*)value.value);
|
||||
return 0;
|
||||
}
|
||||
} while (*s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char* libcjsonp_builtin_parse(value_t* x, const char* s) {
|
||||
|
||||
const char* e;
|
||||
|
||||
switch (*(s = skip_spaces(s))) {
|
||||
case '{': if (!(e = libcjsonp_builtin_parse_map((void*)x->value, s))) {
|
||||
map_free((void*)x->value);
|
||||
} else x->type = VTYPE_MAP;
|
||||
break;
|
||||
|
||||
case '[': if (!(e = libcjsonp_builtin_parse_list((void*)x->value, s))) {
|
||||
list_free((void*)x->value);
|
||||
} else x->type = VTYPE_LIST;
|
||||
break;
|
||||
|
||||
case '"': if (!(e = libcjsonp_builtin_parse_string((void*)x->value, s))) {
|
||||
string_free((void*)x->value);
|
||||
} else x->type = VTYPE_STRING;
|
||||
break;
|
||||
|
||||
case 't': if (s[1] == 'r' && s[2] == 'u' && s[3] == 'e') {
|
||||
x->value->b = true;
|
||||
x->type = VTYPE_BOOLEAN;
|
||||
e = s + 4;
|
||||
break;
|
||||
} else return 0;
|
||||
|
||||
case 'f': if (s[1] == 'a' && s[2] == 'l' && s[3] == 's' && s[4] == 'e') {
|
||||
x->value->b = false;
|
||||
x->type = VTYPE_BOOLEAN;
|
||||
e = s + 5;
|
||||
break;
|
||||
} else return 0;
|
||||
|
||||
case 'n': if (s[1] == 'u' && s[2] == 'l' && s[3] == 'l') {
|
||||
x->value->ptr = 0;
|
||||
x->type = VTYPE_POINTER;
|
||||
e = s + 4;
|
||||
break;
|
||||
} else return 0;
|
||||
|
||||
case '-': case '0': case '1': case '2': case '3':
|
||||
case '4': case '5': case '6': case '7': case '8':
|
||||
case '9': return libcjsonp_builtin_parse_number(x, s);
|
||||
|
||||
default: return 0;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
/*#####################################################################################################################*/
|
||||
|
||||
const char* libcjsonp_builtin_fetch_number(value_t* x, const char *s, bool is_float) {
|
||||
char* e;
|
||||
|
||||
if (!is_float) {
|
||||
if (*s != '-') {
|
||||
x->value->u64 = strtoull(s, &e, 10);
|
||||
|
||||
if (x->value->u64 == ULLONG_MAX && errno == ERANGE) {
|
||||
errno = 0;
|
||||
goto float_;
|
||||
}
|
||||
|
||||
if (x->value->u64 <= UCHAR_MAX) {
|
||||
x->value->u8 = x->value->u64;
|
||||
x->type = VTYPE_UINT8;
|
||||
} else if (x->value->u64 <= USHRT_MAX) {
|
||||
x->value->u16 = x->value->u64;
|
||||
x->type = VTYPE_UINT16;
|
||||
} else if (x->value->u64 <= UINT_MAX) {
|
||||
x->value->u32 = x->value->u64;
|
||||
x->type = VTYPE_UINT32;
|
||||
} else x->type = VTYPE_UINT64;
|
||||
} else {
|
||||
x->value->s64 = strtoll(s, &e, 10);
|
||||
|
||||
if (x->value->s64 == LLONG_MIN && errno == ERANGE) {
|
||||
errno = 0;
|
||||
goto float_;
|
||||
}
|
||||
|
||||
if (x->value->s64 >= SCHAR_MIN) {
|
||||
x->value->s8 = x->value->s64;
|
||||
x->type = VTYPE_INT8;
|
||||
} else if (x->value->s64 >= SHRT_MIN) {
|
||||
x->value->s16 = x->value->s64;
|
||||
x->type = VTYPE_INT16;
|
||||
} else if (x->value->s64 >= INT_MIN) {
|
||||
x->value->s32 = x->value->s64;
|
||||
x->type = VTYPE_INT32;
|
||||
} else x->type = VTYPE_INT64;
|
||||
}
|
||||
|
||||
} else { float_:
|
||||
x->value->ldbl = strtold(s, &e);
|
||||
|
||||
if ((x->value->ldbl == HUGE_VALL || x->value->ldbl == -HUGE_VALL) && errno == ERANGE) {
|
||||
e = (void*)(uintptr_t)(errno = 0);
|
||||
} else x->type = VTYPE_LDOUBLE;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
bool libcjsonp_json_parse(json_t* x, const char* s, size_t n) {
|
||||
|
||||
char* buffer;
|
||||
value_t value;
|
||||
|
||||
if (n) {
|
||||
s = buffer = libcdsb_strndup(s, n);
|
||||
} else buffer = 0;
|
||||
|
||||
s = libcjsonp_builtin_parse(&value, s);
|
||||
libcdsb_free(buffer);
|
||||
|
||||
if (s) {
|
||||
switch (x->type = value.type) {
|
||||
default:
|
||||
#ifndef NDEBUG
|
||||
abort();
|
||||
#endif
|
||||
|
||||
case VTYPE_BOOLEAN:
|
||||
case VTYPE_INT8:
|
||||
case VTYPE_UINT8: x->data = libcdsb_memndup(value.value, sizeof(vtype_uint8));
|
||||
break;
|
||||
|
||||
case VTYPE_INT16:
|
||||
case VTYPE_UINT16: x->data = libcdsb_memndup(value.value, sizeof(vtype_uint16));
|
||||
break;
|
||||
|
||||
case VTYPE_INT32:
|
||||
case VTYPE_UINT32:
|
||||
x86_: x->data = libcdsb_memndup(value.value, sizeof(vtype_uint32));
|
||||
break;
|
||||
|
||||
case VTYPE_POINTER: if (sizeof(void*) == sizeof(vtype_uint32)) goto x86_;
|
||||
case VTYPE_INT64:
|
||||
case VTYPE_UINT64: x->data = libcdsb_memndup(value.value, sizeof(vtype_uint64));
|
||||
break;
|
||||
|
||||
case VTYPE_LDOUBLE: x->data = libcdsb_memndup(value.value, sizeof(vtype_ldouble));
|
||||
break;
|
||||
|
||||
case VTYPE_MAP: x->data = libcdsb_memndup(value.value, sizeof(vtype_map));
|
||||
break;
|
||||
|
||||
case VTYPE_LIST: x->data = libcdsb_memndup(value.value, sizeof(vtype_list));
|
||||
break;
|
||||
|
||||
case VTYPE_STRING: x->data = libcdsb_memndup(value.value, sizeof(vtype_string));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
memset(x, 0, sizeof(*x));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
Loading…
Reference in New Issue
Block a user