fix
This commit is contained in:
parent
902125750b
commit
dde8c3ee42
18
Makefile
18
Makefile
@ -1,26 +1,36 @@
|
|||||||
|
|
||||||
MAKEFLAGS += -j16
|
MAKEFLAGS += -j16
|
||||||
|
|
||||||
CFLAGS=-std=c17 -pedantic-errors -Wall -Wextra -Wconversion -g
|
CFLAGS=-std=c17 -pedantic-errors -Wall -Wextra -Wconversion
|
||||||
LDFLAGS=
|
LDFLAGS=
|
||||||
|
|
||||||
# CFLAGS+=-fsanitize=address
|
# CFLAGS+=-fsanitize=address
|
||||||
|
|
||||||
|
RELEASE=0
|
||||||
|
|
||||||
|
ifeq ($(RELEASE),1)
|
||||||
|
CFLAGS += -O3
|
||||||
|
else
|
||||||
|
CFLAGS += -g
|
||||||
|
endif
|
||||||
|
|
||||||
build_dir = build
|
build_dir = build
|
||||||
obj_dir = $(build_dir)/obj
|
obj_dir = $(build_dir)/obj
|
||||||
|
|
||||||
sources = \
|
sources = \
|
||||||
src/main.c \
|
src/main.c \
|
||||||
src/json_parse.c \
|
src/collections.c \
|
||||||
src/json_value.c \
|
src/json_value.c \
|
||||||
src/collections.c
|
src/json_parse.c \
|
||||||
|
src/json_query.c \
|
||||||
|
src/json_stringify.c
|
||||||
|
|
||||||
target=$(build_dir)/jq
|
target=$(build_dir)/jq
|
||||||
|
|
||||||
all: $(target)
|
all: $(target)
|
||||||
|
|
||||||
debug: $(target)
|
debug: $(target)
|
||||||
gdb tui -ex 'b main.c:254' -ex 'b collections.c:161' -ex 'r' --args build/jq '.' data.json
|
gdb -ex 'r' --args build/jq '.' data.json
|
||||||
|
|
||||||
$(target): $(sources:%.c=$(obj_dir)/%.o)
|
$(target): $(sources:%.c=$(obj_dir)/%.o)
|
||||||
gcc -o $@ $(CFLAGS) $(LDFLAGS) $^
|
gcc -o $@ $(CFLAGS) $(LDFLAGS) $^
|
||||||
|
|||||||
@ -58,6 +58,8 @@ void *array_insert_at(void **data,
|
|||||||
return src_ptr;
|
return src_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define small_count_max 3
|
||||||
|
|
||||||
void smallarray_construct(struct smallarray *a)
|
void smallarray_construct(struct smallarray *a)
|
||||||
{
|
{
|
||||||
*a = (struct smallarray) { 0 };
|
*a = (struct smallarray) { 0 };
|
||||||
@ -77,11 +79,12 @@ static bool sa_is_big(struct smallarray const *a)
|
|||||||
|
|
||||||
static void sa_push_big(struct smallarray *a, void *value)
|
static void sa_push_big(struct smallarray *a, void *value)
|
||||||
{
|
{
|
||||||
|
if (!sa_is_big(a)) {
|
||||||
size_t capacity = 8;
|
size_t capacity = 8;
|
||||||
void **ptr = malloc(capacity * sizeof(void *));
|
void **ptr = malloc(capacity * sizeof(void *));
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
|
|
||||||
for (size_t i = 0; i < 3; ++i) {
|
for (size_t i = 0; i < small_count_max; ++i) {
|
||||||
if ((a->smalldata[i] & 1) == 0)
|
if ((a->smalldata[i] & 1) == 0)
|
||||||
break;
|
break;
|
||||||
ptr[i] = (void *)(a->smalldata[i] & ~1ull);
|
ptr[i] = (void *)(a->smalldata[i] & ~1ull);
|
||||||
@ -91,13 +94,15 @@ static void sa_push_big(struct smallarray *a, void *value)
|
|||||||
a->capacity = capacity;
|
a->capacity = capacity;
|
||||||
a->data = ptr;
|
a->data = ptr;
|
||||||
a->count = count;
|
a->count = count;
|
||||||
|
}
|
||||||
|
|
||||||
array_push(
|
array_push(
|
||||||
(void **)&a->data, &a->capacity, &a->count, &value, sizeof(void *));
|
(void **)&a->data, &a->capacity, &a->count, &value, sizeof(void *));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sa_push_small(struct smallarray *a, void *value)
|
static void sa_push_small(struct smallarray *a, void *value)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < 3; ++i) {
|
for (size_t i = 0; i < small_count_max; ++i) {
|
||||||
if (a->smalldata[i] & 1)
|
if (a->smalldata[i] & 1)
|
||||||
continue;
|
continue;
|
||||||
a->smalldata[i] = (size_t)value | 1;
|
a->smalldata[i] = (size_t)value | 1;
|
||||||
@ -109,7 +114,7 @@ void smallarray_push(struct smallarray *a, void *value)
|
|||||||
{
|
{
|
||||||
assert((size_t)value % 2 == 0 && "pointer must be 2 bytes aligned");
|
assert((size_t)value % 2 == 0 && "pointer must be 2 bytes aligned");
|
||||||
|
|
||||||
if (sa_is_big(a)) {
|
if (sa_is_big(a) || smallarray_count(a) >= small_count_max) {
|
||||||
sa_push_big(a, value);
|
sa_push_big(a, value);
|
||||||
} else {
|
} else {
|
||||||
sa_push_small(a, value);
|
sa_push_small(a, value);
|
||||||
@ -122,7 +127,7 @@ size_t smallarray_count(struct smallarray const *a)
|
|||||||
return a->count;
|
return a->count;
|
||||||
} else {
|
} else {
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
for (size_t i = 0; i < 3; ++i) {
|
for (size_t i = 0; i < small_count_max; ++i) {
|
||||||
if ((a->smalldata[i] & 1) == 0)
|
if ((a->smalldata[i] & 1) == 0)
|
||||||
break;
|
break;
|
||||||
count += 1;
|
count += 1;
|
||||||
@ -136,12 +141,17 @@ void *smallarray_get(struct smallarray *a, size_t idx)
|
|||||||
if (sa_is_big(a)) {
|
if (sa_is_big(a)) {
|
||||||
return a->data[idx];
|
return a->data[idx];
|
||||||
} else {
|
} else {
|
||||||
if (idx >= 3 || (a->smalldata[idx] & 1) == 0)
|
if (idx >= small_count_max || (a->smalldata[idx] & 1) == 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
return (void *)(a->smalldata[idx] & ~1ull);
|
return (void *)(a->smalldata[idx] & ~1ull);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void const *smallarray_get_const(struct smallarray const *a, size_t idx)
|
||||||
|
{
|
||||||
|
return smallarray_get((struct smallarray *)a, idx);
|
||||||
|
}
|
||||||
|
|
||||||
static uint64_t hash_key(char const *data)
|
static uint64_t hash_key(char const *data)
|
||||||
{
|
{
|
||||||
// djb2
|
// djb2
|
||||||
@ -172,14 +182,15 @@ static uint64_t hash_key_sized(char const *data, size_t size)
|
|||||||
|
|
||||||
void hashmap_construct(struct hashmap *t)
|
void hashmap_construct(struct hashmap *t)
|
||||||
{
|
{
|
||||||
*t = (struct hashmap) { NULL, 0, 0 };
|
*t = (struct hashmap) { NULL, 0, 0, NULL, 0, 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
void hashmap_destroy(struct hashmap *t)
|
void hashmap_destroy(struct hashmap *t)
|
||||||
{
|
{
|
||||||
if (t->buckets) {
|
if (t->buckets)
|
||||||
free(t->buckets);
|
free(t->buckets);
|
||||||
}
|
if (t->keys)
|
||||||
|
free(t->keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct hash_entry *find_entry(struct hashmap *m, uint64_t hash)
|
static struct hash_entry *find_entry(struct hashmap *m, uint64_t hash)
|
||||||
@ -256,7 +267,31 @@ static struct hash_entry *make_entry(struct hashmap *m, uint64_t hash)
|
|||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hashmap_set_internal(struct hashmap *m, uint64_t hash, void *value)
|
static void insert_key(
|
||||||
|
struct hashmap *m, char const *key, size_t key_size, uint64_t hash)
|
||||||
|
{
|
||||||
|
key_size = key_size != 0 ? key_size : strlen(key);
|
||||||
|
char *key_value = malloc(key_size + 1);
|
||||||
|
strncpy(key_value, key, key_size);
|
||||||
|
key_value[key_size] = '\0';
|
||||||
|
|
||||||
|
struct hash_key_entry key_entry = {
|
||||||
|
.hash = hash,
|
||||||
|
.value = key_value,
|
||||||
|
};
|
||||||
|
|
||||||
|
array_push((void **)&m->keys,
|
||||||
|
&m->keys_capacity,
|
||||||
|
&m->keys_count,
|
||||||
|
&key_entry,
|
||||||
|
sizeof(key_entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hashmap_set_internal(struct hashmap *m,
|
||||||
|
char const *key,
|
||||||
|
size_t key_size,
|
||||||
|
uint64_t hash,
|
||||||
|
void *value)
|
||||||
{
|
{
|
||||||
if (m->buckets_count == 0) {
|
if (m->buckets_count == 0) {
|
||||||
struct hash_bucket *bucket = insert_bucket_at(m, 0);
|
struct hash_bucket *bucket = insert_bucket_at(m, 0);
|
||||||
@ -264,6 +299,8 @@ static void hashmap_set_internal(struct hashmap *m, uint64_t hash, void *value)
|
|||||||
bucket->count += 1;
|
bucket->count += 1;
|
||||||
bucket->first_hash = hash;
|
bucket->first_hash = hash;
|
||||||
bucket->last_hash = hash;
|
bucket->last_hash = hash;
|
||||||
|
|
||||||
|
insert_key(m, key, key_size, hash);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,6 +308,8 @@ static void hashmap_set_internal(struct hashmap *m, uint64_t hash, void *value)
|
|||||||
if (!entry) {
|
if (!entry) {
|
||||||
entry = make_entry(m, hash);
|
entry = make_entry(m, hash);
|
||||||
entry->hash = hash;
|
entry->hash = hash;
|
||||||
|
|
||||||
|
insert_key(m, key, key_size, hash);
|
||||||
}
|
}
|
||||||
entry->value = value;
|
entry->value = value;
|
||||||
return;
|
return;
|
||||||
@ -279,14 +318,14 @@ static void hashmap_set_internal(struct hashmap *m, uint64_t hash, void *value)
|
|||||||
void hashmap_set(struct hashmap *m, char const *key, void *value)
|
void hashmap_set(struct hashmap *m, char const *key, void *value)
|
||||||
{
|
{
|
||||||
uint64_t hash = hash_key(key);
|
uint64_t hash = hash_key(key);
|
||||||
hashmap_set_internal(m, hash, value);
|
hashmap_set_internal(m, key, 0, hash, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hashmap_set_sized(
|
void hashmap_set_sized(
|
||||||
struct hashmap *m, char const *key, size_t key_size, void *value)
|
struct hashmap *m, char const *key, size_t key_size, void *value)
|
||||||
{
|
{
|
||||||
uint64_t hash = hash_key_sized(key, key_size);
|
uint64_t hash = hash_key_sized(key, key_size);
|
||||||
hashmap_set_internal(m, hash, value);
|
hashmap_set_internal(m, key, key_size, hash, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hashmap_has(struct hashmap *m, char const *key)
|
bool hashmap_has(struct hashmap *m, char const *key)
|
||||||
@ -311,6 +350,23 @@ void *hashmap_get_sized(struct hashmap *m, char const *key, size_t key_size)
|
|||||||
return hashmap_get_internal(m, hash);
|
return hashmap_get_internal(m, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void *hashmap_get_hash(struct hashmap *m, size_t hash)
|
||||||
|
{
|
||||||
|
return hashmap_get_internal(m, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
void const *hashmap_get_hash_const(struct hashmap const *m, size_t hash)
|
||||||
|
{
|
||||||
|
return hashmap_get_internal((struct hashmap *)m, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashmap_keys(
|
||||||
|
struct hashmap const *m, struct hash_key_entry const **keys, size_t *count)
|
||||||
|
{
|
||||||
|
*keys = m->keys;
|
||||||
|
*count = m->keys_count;
|
||||||
|
}
|
||||||
|
|
||||||
struct blockalloc_block {
|
struct blockalloc_block {
|
||||||
unsigned char *data;
|
unsigned char *data;
|
||||||
size_t size;
|
size_t size;
|
||||||
|
|||||||
@ -34,6 +34,7 @@ void smallarray_destroy(struct smallarray *a);
|
|||||||
void smallarray_push(struct smallarray *a, void *value);
|
void smallarray_push(struct smallarray *a, void *value);
|
||||||
size_t smallarray_count(struct smallarray const *a);
|
size_t smallarray_count(struct smallarray const *a);
|
||||||
void *smallarray_get(struct smallarray *a, size_t idx);
|
void *smallarray_get(struct smallarray *a, size_t idx);
|
||||||
|
void const *smallarray_get_const(struct smallarray const *a, size_t idx);
|
||||||
|
|
||||||
struct hash_entry {
|
struct hash_entry {
|
||||||
uint64_t hash;
|
uint64_t hash;
|
||||||
@ -49,10 +50,18 @@ struct hash_bucket {
|
|||||||
uint64_t last_hash;
|
uint64_t last_hash;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct hash_key_entry {
|
||||||
|
uint64_t hash;
|
||||||
|
char *value;
|
||||||
|
};
|
||||||
|
|
||||||
struct hashmap {
|
struct hashmap {
|
||||||
struct hash_bucket *buckets;
|
struct hash_bucket *buckets;
|
||||||
size_t buckets_capacity;
|
size_t buckets_capacity;
|
||||||
size_t buckets_count;
|
size_t buckets_count;
|
||||||
|
struct hash_key_entry *keys;
|
||||||
|
size_t keys_capacity;
|
||||||
|
size_t keys_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
void hashmap_construct(struct hashmap *m);
|
void hashmap_construct(struct hashmap *m);
|
||||||
@ -63,6 +72,10 @@ void hashmap_set_sized(
|
|||||||
bool hashmap_has(struct hashmap *m, char const *key);
|
bool hashmap_has(struct hashmap *m, char const *key);
|
||||||
void *hashmap_get(struct hashmap *m, char const *key);
|
void *hashmap_get(struct hashmap *m, char const *key);
|
||||||
void *hashmap_get_sized(struct hashmap *m, char const *key, size_t key_size);
|
void *hashmap_get_sized(struct hashmap *m, char const *key, size_t key_size);
|
||||||
|
void *hashmap_get_hash(struct hashmap *m, size_t hash);
|
||||||
|
void const *hashmap_get_hash_const(struct hashmap const *m, size_t hash);
|
||||||
|
void hashmap_keys(
|
||||||
|
struct hashmap const *m, struct hash_key_entry const **keys, size_t* count);
|
||||||
|
|
||||||
#define blockalloc_default_block 4096
|
#define blockalloc_default_block 4096
|
||||||
|
|
||||||
|
|||||||
34
src/json.h
34
src/json.h
@ -1,6 +1,7 @@
|
|||||||
#ifndef JSON_H
|
#ifndef JSON_H
|
||||||
#define JSON_H
|
#define JSON_H
|
||||||
|
|
||||||
|
#include "collections.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@ -23,29 +24,50 @@ void json_free(struct json_value *value);
|
|||||||
|
|
||||||
bool json_is(struct json_value const *value, enum json_type type);
|
bool json_is(struct json_value const *value, enum json_type type);
|
||||||
|
|
||||||
|
enum json_type json_get_type(struct json_value const *value);
|
||||||
bool json_get_bool(struct json_value const *value);
|
bool json_get_bool(struct json_value const *value);
|
||||||
int64_t json_get_int(struct json_value const *value);
|
int64_t json_get_int(struct json_value const *value);
|
||||||
double json_get_float(struct json_value const *value);
|
double json_get_float(struct json_value const *value);
|
||||||
char *json_get_string(struct json_value *value);
|
char const *json_get_string(struct json_value const *value);
|
||||||
|
|
||||||
void json_set_bool(struct json_value *value, bool val);
|
void json_set_null(struct json_value **value);
|
||||||
void json_set_int(struct json_value *value, int64_t val);
|
void json_set_bool(struct json_value **value, bool val);
|
||||||
void json_set_float(struct json_value *value, double val);
|
void json_set_int(struct json_value **value, int64_t val);
|
||||||
void json_set_string(struct json_value *value, char *val);
|
void json_set_float(struct json_value **value, double val);
|
||||||
|
void json_set_string(struct json_value *value, char *vaAl);
|
||||||
|
|
||||||
|
struct json_value *json_new_int(int64_t val);
|
||||||
|
struct json_value *json_new_float(double val);
|
||||||
|
|
||||||
|
size_t json_array_count(struct json_value const *array);
|
||||||
struct json_value *json_idx(struct json_value *array, size_t idx);
|
struct json_value *json_idx(struct json_value *array, size_t idx);
|
||||||
|
struct json_value const *json_idx_const(struct json_value const *array, size_t idx);
|
||||||
void json_push(struct json_value *array, struct json_value *value);
|
void json_push(struct json_value *array, struct json_value *value);
|
||||||
|
|
||||||
struct json_value *json_key(struct json_value *object, char const *key);
|
struct json_value *json_key(struct json_value *object, char const *key);
|
||||||
struct json_value *json_key_sized(
|
struct json_value *json_key_sized(
|
||||||
struct json_value *object, char const *key, size_t key_size);
|
struct json_value *object, char const *key, size_t key_size);
|
||||||
|
struct json_value const *json_key_hash_const(
|
||||||
|
struct json_value const *object, size_t hash);
|
||||||
void json_set(
|
void json_set(
|
||||||
struct json_value *object, char const *key, struct json_value *value);
|
struct json_value *object, char const *key, struct json_value *value);
|
||||||
void json_set_sized(struct json_value *object,
|
void json_set_sized(struct json_value *object,
|
||||||
char const *key,
|
char const *key,
|
||||||
size_t key_size,
|
size_t key_size,
|
||||||
struct json_value *value);
|
struct json_value *value);
|
||||||
|
void json_object_keys(
|
||||||
|
struct json_value const *object, struct hash_key_entry const **keys, size_t *count);
|
||||||
|
|
||||||
struct json_value *json_parse(char const *text, size_t text_size);
|
struct json_value *json_parse(
|
||||||
|
char const *text, size_t text_size, struct blockalloc* alloc);
|
||||||
|
|
||||||
|
struct json_value *json_query(struct json_value *val, char const *query);
|
||||||
|
|
||||||
|
typedef int JsonWriteCb(void* self, const char* data, size_t size);
|
||||||
|
|
||||||
|
int json_stringify(
|
||||||
|
struct json_value const *node,
|
||||||
|
JsonWriteCb write,
|
||||||
|
void* self);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -110,10 +110,13 @@ static struct tok t_make_ident_tok(
|
|||||||
struct tokenizer *t, struct loc loc, size_t *i)
|
struct tokenizer *t, struct loc loc, size_t *i)
|
||||||
{
|
{
|
||||||
char const *kws[] = { "null", "false", "true" };
|
char const *kws[] = { "null", "false", "true" };
|
||||||
|
size_t const lens[] = { 4, 5, 4 };
|
||||||
enum tokty tys[] = { tt_null, tt_false, tt_true };
|
enum tokty tys[] = { tt_null, tt_false, tt_true };
|
||||||
for (size_t kw_i = 0; kw_i < sizeof(kws) / sizeof(kws[0]); ++kw_i) {
|
for (size_t j = 0; j < sizeof(kws) / sizeof(kws[0]); ++j) {
|
||||||
if (strncmp(kws[kw_i], &t->text[loc.idx], *i - loc.idx)) {
|
size_t len = *i - loc.idx;
|
||||||
return t_tok(t, tys[kw_i], loc);
|
if (lens[j] == len && strncmp(kws[j], &t->text[loc.idx], len) == 0) {
|
||||||
|
|
||||||
|
return t_tok(t, tys[j], loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
report(loc, "invalid identifier", t->text, t->len);
|
report(loc, "invalid identifier", t->text, t->len);
|
||||||
@ -201,7 +204,7 @@ static struct tok tokenizer_next(struct tokenizer *t)
|
|||||||
struct parser {
|
struct parser {
|
||||||
struct tokenizer tokenizer;
|
struct tokenizer tokenizer;
|
||||||
struct tok tok;
|
struct tok tok;
|
||||||
struct blockalloc allocator;
|
struct blockalloc* alloc;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void p_step(struct parser *p)
|
static void p_step(struct parser *p)
|
||||||
@ -210,14 +213,13 @@ static void p_step(struct parser *p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void parser_construct(
|
static void parser_construct(
|
||||||
struct parser *p, char const *text, size_t text_len)
|
struct parser *p, char const *text, size_t text_len, struct blockalloc* alloc)
|
||||||
{
|
{
|
||||||
*p = (struct parser) {
|
*p = (struct parser) {
|
||||||
.tokenizer = (struct tokenizer) { text, text_len, 0, 1, 1, false },
|
.tokenizer = (struct tokenizer) { text, text_len, 0, 1, 1, false },
|
||||||
.tok = (struct tok) { 0 },
|
.tok = (struct tok) { 0 },
|
||||||
.allocator = (struct blockalloc) { 0 },
|
.alloc = alloc,
|
||||||
};
|
};
|
||||||
blockalloc_construct(&p->allocator);
|
|
||||||
p_step(p);
|
p_step(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,18 +302,16 @@ static struct json_value *parser_parse(struct parser *p)
|
|||||||
return val;
|
return val;
|
||||||
} else if (*ty == tt_int) {
|
} else if (*ty == tt_int) {
|
||||||
int64_t value = strtol(p->tok.ptr, NULL, 10);
|
int64_t value = strtol(p->tok.ptr, NULL, 10);
|
||||||
struct json_value *val = json_new(json_int);
|
struct json_value *val = json_new_int(value);
|
||||||
json_set_int(val, value);
|
|
||||||
p_step(p);
|
p_step(p);
|
||||||
return val;
|
return val;
|
||||||
} else if (*ty == tt_float) {
|
} else if (*ty == tt_float) {
|
||||||
double value = strtod(p->tok.ptr, NULL);
|
double value = strtod(p->tok.ptr, NULL);
|
||||||
struct json_value *val = json_new(json_float);
|
struct json_value *val = json_new_float(value);
|
||||||
json_set_float(val, value);
|
|
||||||
p_step(p);
|
p_step(p);
|
||||||
return val;
|
return val;
|
||||||
} else if (*ty == tt_string) {
|
} else if (*ty == tt_string) {
|
||||||
char *value = blockalloc_alloc(&p->allocator, p->tok.len - 2 + 1, 2);
|
char *value = blockalloc_alloc(p->alloc, p->tok.len - 2 + 1, 2);
|
||||||
strncpy(value, p->tok.ptr + 1, p->tok.len - 2);
|
strncpy(value, p->tok.ptr + 1, p->tok.len - 2);
|
||||||
value[p->tok.len - 2] = '\0';
|
value[p->tok.len - 2] = '\0';
|
||||||
struct json_value *val = json_new(json_string);
|
struct json_value *val = json_new(json_string);
|
||||||
@ -323,17 +323,27 @@ static struct json_value *parser_parse(struct parser *p)
|
|||||||
} else if (*ty == '{') {
|
} else if (*ty == '{') {
|
||||||
return parser_parse_object(p, ty);
|
return parser_parse_object(p, ty);
|
||||||
} else {
|
} else {
|
||||||
|
if (!p->tokenizer.failed) {
|
||||||
p_report(p, loc, "expected expression");
|
p_report(p, loc, "expected expression");
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct json_value *json_parse(char const *text, size_t text_size)
|
struct json_value *json_parse(
|
||||||
|
char const *text, size_t text_size, struct blockalloc* alloc)
|
||||||
{
|
{
|
||||||
text_size = text_size > 0 ? text_size : strlen(text);
|
text_size = text_size > 0 ? text_size : strlen(text);
|
||||||
|
|
||||||
struct parser p;
|
struct parser p;
|
||||||
parser_construct(&p, text, text_size);
|
parser_construct(&p, text, text_size, alloc);
|
||||||
|
|
||||||
return parser_parse(&p);
|
struct json_value *result = parser_parse(&p);
|
||||||
|
|
||||||
|
if (result && p.tokenizer.failed) {
|
||||||
|
json_free(result);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
252
src/json_query.c
Normal file
252
src/json_query.c
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
#include "json.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static void report(
|
||||||
|
size_t idx, char const *message, char const *text, size_t text_len)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "error: %s\n", message);
|
||||||
|
if (!text)
|
||||||
|
return;
|
||||||
|
static char const *spaces = " ";
|
||||||
|
printf("|%.*s\n"
|
||||||
|
"|%.*s^\n",
|
||||||
|
(int)text_len,
|
||||||
|
text,
|
||||||
|
(int)idx,
|
||||||
|
spaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum tokty {
|
||||||
|
tt_eof,
|
||||||
|
tt_string,
|
||||||
|
tt_ident,
|
||||||
|
tt_int = '0',
|
||||||
|
tt_dot = '.',
|
||||||
|
tt_lbracket = '[',
|
||||||
|
tt_rbracket = ']',
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tok {
|
||||||
|
enum tokty ty;
|
||||||
|
char const *ptr;
|
||||||
|
size_t idx;
|
||||||
|
size_t len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tokenizer {
|
||||||
|
char const *text;
|
||||||
|
size_t len;
|
||||||
|
size_t idx;
|
||||||
|
bool failed;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void t_step(struct tokenizer *t)
|
||||||
|
{
|
||||||
|
if (t->idx >= t->len)
|
||||||
|
return;
|
||||||
|
t->idx += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct tok t_tok(struct tokenizer *t, enum tokty ty, size_t idx)
|
||||||
|
{
|
||||||
|
return (struct tok) { ty, &t->text[idx], idx, t->idx - idx };
|
||||||
|
}
|
||||||
|
static struct tok tokenizer_next(struct tokenizer *t);
|
||||||
|
|
||||||
|
static struct tok t_make_number_tok(
|
||||||
|
struct tokenizer *t, size_t idx, size_t *i)
|
||||||
|
{
|
||||||
|
while (*i < t->len && t->text[*i] >= '0' && t->text[*i] <= '9') {
|
||||||
|
t_step(t);
|
||||||
|
}
|
||||||
|
return t_tok(t, tt_int, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct tok t_make_string_tok(
|
||||||
|
struct tokenizer *t, size_t idx, size_t *i)
|
||||||
|
{
|
||||||
|
t_step(t);
|
||||||
|
while (*i < t->len && t->text[*i] != '\"') {
|
||||||
|
if (t->text[*i] == '\\') {
|
||||||
|
t_step(t);
|
||||||
|
if (*i >= t->len)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
t_step(t);
|
||||||
|
}
|
||||||
|
if (*i >= t->len && t->text[*i] != '\"') {
|
||||||
|
report(idx, "malformed string", t->text, t->len);
|
||||||
|
t->failed = true;
|
||||||
|
return tokenizer_next(t);
|
||||||
|
}
|
||||||
|
t_step(t);
|
||||||
|
return t_tok(t, tt_string, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct tok tokenizer_next(struct tokenizer *t)
|
||||||
|
{
|
||||||
|
size_t idx = t->idx;
|
||||||
|
size_t *i = &t->idx;
|
||||||
|
if (*i >= t->len) {
|
||||||
|
return t_tok(t, tt_eof, idx);
|
||||||
|
}
|
||||||
|
bool matched = false;
|
||||||
|
while (*i < t->len && strchr(" \t\r\n", t->text[*i]) != NULL) {
|
||||||
|
matched = true;
|
||||||
|
t_step(t);
|
||||||
|
}
|
||||||
|
if (matched) {
|
||||||
|
return tokenizer_next(t);
|
||||||
|
}
|
||||||
|
if (strchr(".[]0", t->text[*i]) != NULL) {
|
||||||
|
enum tokty ty = (enum tokty)t->text[*i];
|
||||||
|
t_step(t);
|
||||||
|
return t_tok(t, ty, idx);
|
||||||
|
}
|
||||||
|
while (*i < t->len && t->text[*i] >= 'a' && t->text[*i] <= 'z') {
|
||||||
|
matched = true;
|
||||||
|
t_step(t);
|
||||||
|
}
|
||||||
|
if (matched) {
|
||||||
|
return t_tok(t, tt_ident, idx);
|
||||||
|
}
|
||||||
|
if (t->text[*i] >= '1' && t->text[*i] <= '9') {
|
||||||
|
return t_make_number_tok(t, idx, i);
|
||||||
|
}
|
||||||
|
if (t->text[*i] == '\"') {
|
||||||
|
return t_make_string_tok(t, idx, i);
|
||||||
|
}
|
||||||
|
report(idx, "illegal character", t->text, t->len);
|
||||||
|
t->failed = true;
|
||||||
|
t_step(t);
|
||||||
|
return tokenizer_next(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum path_seg_ty {
|
||||||
|
pt_error,
|
||||||
|
pt_eof,
|
||||||
|
pt_ident_key,
|
||||||
|
pt_string_key,
|
||||||
|
pt_idx,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct path_seg {
|
||||||
|
enum path_seg_ty ty;
|
||||||
|
struct tok tok;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct parser {
|
||||||
|
struct tokenizer tokenizer;
|
||||||
|
struct tok tok;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void p_step(struct parser *p)
|
||||||
|
{
|
||||||
|
p->tok = tokenizer_next(&p->tokenizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void p_report(struct parser *p, size_t idx, char const *message)
|
||||||
|
{
|
||||||
|
report(idx, message, p->tokenizer.text, p->tokenizer.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct path_seg parse_next(struct parser *p)
|
||||||
|
{
|
||||||
|
size_t idx = p->tok.idx;
|
||||||
|
enum tokty *ty = &p->tok.ty;
|
||||||
|
|
||||||
|
if (*ty == tt_eof) {
|
||||||
|
return (struct path_seg) { .ty = pt_eof, .tok = p->tok };
|
||||||
|
} else if (*ty == '.') {
|
||||||
|
p_step(p);
|
||||||
|
struct tok tok = p->tok;
|
||||||
|
if (*ty == tt_eof || *ty != tt_ident) {
|
||||||
|
p_report(p, p->tok.idx, "expected identifier");
|
||||||
|
return (struct path_seg) { .ty = pt_error, .tok = p->tok };
|
||||||
|
}
|
||||||
|
p_step(p);
|
||||||
|
return (struct path_seg) { .ty = pt_ident_key, .tok = tok };
|
||||||
|
} else if (*ty == '[') {
|
||||||
|
p_step(p);
|
||||||
|
struct tok tok = p->tok;
|
||||||
|
enum path_seg_ty seg_ty;
|
||||||
|
if (tok.ty == tt_int) {
|
||||||
|
seg_ty = pt_idx;
|
||||||
|
} else if (tok.ty == tt_string) {
|
||||||
|
seg_ty = pt_string_key;
|
||||||
|
} else {
|
||||||
|
p_report(p, p->tok.idx, "expected string or integer");
|
||||||
|
return (struct path_seg) { .ty = pt_error, .tok = p->tok };
|
||||||
|
}
|
||||||
|
p_step(p);
|
||||||
|
if (*ty == tt_eof || *ty != ']') {
|
||||||
|
p_report(p, p->tok.idx, "expected ']'");
|
||||||
|
return (struct path_seg) { .ty = pt_error, .tok = p->tok };
|
||||||
|
}
|
||||||
|
p_step(p);
|
||||||
|
return (struct path_seg) { .ty = seg_ty, .tok = tok };
|
||||||
|
} else {
|
||||||
|
if (!p->tokenizer.failed) {
|
||||||
|
p_report(p, idx, "expected expression");
|
||||||
|
}
|
||||||
|
return (struct path_seg) { .ty = pt_error, .tok = p->tok };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct json_value *resolve_next_path_seg(
|
||||||
|
struct parser *p, struct json_value* node)
|
||||||
|
{
|
||||||
|
struct path_seg seg = parse_next(p);
|
||||||
|
switch (seg.ty){
|
||||||
|
case pt_error:
|
||||||
|
return NULL;
|
||||||
|
case pt_eof:
|
||||||
|
return node;
|
||||||
|
case pt_ident_key: {
|
||||||
|
if (!json_is(node, json_object))
|
||||||
|
return NULL;
|
||||||
|
struct json_value *child =
|
||||||
|
json_key_sized(node, seg.tok.ptr, seg.tok.len);
|
||||||
|
return resolve_next_path_seg(p, child);
|
||||||
|
}
|
||||||
|
case pt_string_key: {
|
||||||
|
if (!json_is(node, json_object))
|
||||||
|
return NULL;
|
||||||
|
struct json_value *child =
|
||||||
|
json_key_sized(node, seg.tok.ptr + 1, seg.tok.len - 2);
|
||||||
|
return resolve_next_path_seg(p, child);
|
||||||
|
}
|
||||||
|
case pt_idx: {
|
||||||
|
if (!json_is(node, json_array))
|
||||||
|
return NULL;
|
||||||
|
struct json_value *child =
|
||||||
|
json_idx(node, strtoull(seg.tok.ptr, NULL, 10));
|
||||||
|
return resolve_next_path_seg(p, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct json_value *json_query(struct json_value *val, char const *query)
|
||||||
|
{
|
||||||
|
size_t query_len = strlen(query);
|
||||||
|
|
||||||
|
struct parser p = {
|
||||||
|
.tokenizer = (struct tokenizer) {
|
||||||
|
.text = query,
|
||||||
|
.len = query_len,
|
||||||
|
.idx = 0,
|
||||||
|
.failed = false,
|
||||||
|
},
|
||||||
|
.tok = (struct tok){0},
|
||||||
|
};
|
||||||
|
p_step(&p);
|
||||||
|
|
||||||
|
return resolve_next_path_seg(&p, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
100
src/json_stringify.c
Normal file
100
src/json_stringify.c
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
#include "collections.h"
|
||||||
|
#include "json.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define TRY(EXPR) do {int result = (EXPR); if (result != 0) { return result; }} while(0)
|
||||||
|
|
||||||
|
static int write_indent(JsonWriteCb write, void *self, int depth)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < depth; ++i) {
|
||||||
|
TRY(write(self, " ", 4));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stringify_pretty_rec(
|
||||||
|
struct json_value const *node, JsonWriteCb write, void *self, int depth)
|
||||||
|
{
|
||||||
|
static const char* kws[] = {
|
||||||
|
[json_null] = "null",
|
||||||
|
[json_false] = "false",
|
||||||
|
[json_true] = "true",
|
||||||
|
};
|
||||||
|
enum json_type ty = json_get_type(node);
|
||||||
|
switch (ty) {
|
||||||
|
case json_null:
|
||||||
|
case json_false:
|
||||||
|
case json_true:
|
||||||
|
TRY(write(self, kws[ty], strlen(kws[ty])));
|
||||||
|
break;
|
||||||
|
case json_int: {
|
||||||
|
char buf[25] = "";
|
||||||
|
int len = snprintf(buf, sizeof(buf) - 1, "%ld", json_get_int(node));
|
||||||
|
TRY(write(self, buf, (size_t)len));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case json_float: {
|
||||||
|
char buf[32] = "";
|
||||||
|
int len = snprintf(buf, sizeof(buf) - 1, "%f", json_get_float(node));
|
||||||
|
TRY(write(self, buf, (size_t)len));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case json_string: {
|
||||||
|
const char* str = json_get_string(node);
|
||||||
|
TRY(write(self, "\"", 1));
|
||||||
|
TRY(write(self, str, strlen(str)));
|
||||||
|
TRY(write(self, "\"", 1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case json_array: {
|
||||||
|
size_t count = json_array_count(node);
|
||||||
|
TRY(write(self, "[", 1));
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
struct json_value const *child = json_idx_const(node, i);
|
||||||
|
|
||||||
|
if (i != 0) {
|
||||||
|
TRY(write(self, ",", 1));
|
||||||
|
}
|
||||||
|
TRY(write(self, "\n", 1));
|
||||||
|
TRY(write_indent(write, self, depth + 1));
|
||||||
|
TRY(stringify_pretty_rec(child, write, self, depth + 1));
|
||||||
|
}
|
||||||
|
TRY(write(self, "\n", 1));
|
||||||
|
TRY(write_indent(write, self, depth));
|
||||||
|
TRY(write(self, "]", 1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case json_object: {
|
||||||
|
struct hash_key_entry const *keys;
|
||||||
|
size_t count;
|
||||||
|
json_object_keys(node, &keys, &count);
|
||||||
|
TRY(write(self, "{", 1));
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
struct json_value const *child =
|
||||||
|
json_key_hash_const(node, keys[i].hash);
|
||||||
|
|
||||||
|
if (i != 0) {
|
||||||
|
TRY(write(self, ",", 1));
|
||||||
|
}
|
||||||
|
TRY(write(self, "\n", 1));
|
||||||
|
TRY(write_indent(write, self, depth + 1));
|
||||||
|
TRY(write(self, "\"", 1));
|
||||||
|
TRY(write(self, keys[i].value, strlen(keys[i].value)));
|
||||||
|
TRY(write(self, "\": ", 3));
|
||||||
|
TRY(stringify_pretty_rec(child, write, self, depth + 1));
|
||||||
|
}
|
||||||
|
TRY(write(self, "\n", 1));
|
||||||
|
TRY(write_indent(write, self, depth));
|
||||||
|
TRY(write(self, "}", 1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int json_stringify(
|
||||||
|
struct json_value const *node, JsonWriteCb write, void *self)
|
||||||
|
{
|
||||||
|
return stringify_pretty_rec(node, write, self, 0);
|
||||||
|
}
|
||||||
195
src/json_value.c
195
src/json_value.c
@ -19,7 +19,43 @@ struct json_value {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct json_value *json_new(enum json_type ty)
|
#define shallow_int_min (-144115188075855872ll)
|
||||||
|
#define shallow_int_max 144115188075855871ll
|
||||||
|
|
||||||
|
static bool is_shallow(struct json_value const *ptr)
|
||||||
|
{
|
||||||
|
return ((size_t)ptr >> 1 & 1) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum json_type shallow_ty(struct json_value const *ptr)
|
||||||
|
{
|
||||||
|
return (enum json_type)((size_t)ptr >> 2 & 0xf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_shallow_ty(struct json_value **ptr, enum json_type ty)
|
||||||
|
{
|
||||||
|
*(size_t *)ptr |= (ty & 0xf) << 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t shallow_val(struct json_value const *ptr)
|
||||||
|
{
|
||||||
|
return (size_t)ptr >> 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_shallow_val(struct json_value **ptr, size_t val)
|
||||||
|
{
|
||||||
|
*(size_t *)ptr |= val << 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct json_value *new_shallow(enum json_type ty)
|
||||||
|
{
|
||||||
|
struct json_value *shallow_val = 0;
|
||||||
|
*(size_t *)&shallow_val |= 0x2;
|
||||||
|
set_shallow_ty(&shallow_val, ty);
|
||||||
|
return shallow_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct json_value *new_deep(enum json_type ty)
|
||||||
{
|
{
|
||||||
struct json_value *v = malloc(sizeof(struct json_value));
|
struct json_value *v = malloc(sizeof(struct json_value));
|
||||||
*v = (struct json_value) { .ty = ty };
|
*v = (struct json_value) { .ty = ty };
|
||||||
@ -41,8 +77,28 @@ struct json_value *json_new(enum json_type ty)
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct json_value *json_new(enum json_type ty)
|
||||||
|
{
|
||||||
|
switch (ty) {
|
||||||
|
case json_null:
|
||||||
|
case json_false:
|
||||||
|
case json_true:
|
||||||
|
case json_int:
|
||||||
|
case json_float:
|
||||||
|
return new_shallow(ty);
|
||||||
|
case json_string:
|
||||||
|
case json_array:
|
||||||
|
case json_object:
|
||||||
|
return new_deep(ty);
|
||||||
|
}
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
void json_free(struct json_value *v)
|
void json_free(struct json_value *v)
|
||||||
{
|
{
|
||||||
|
if (is_shallow(v))
|
||||||
|
return;
|
||||||
|
|
||||||
switch (v->ty) {
|
switch (v->ty) {
|
||||||
case json_null:
|
case json_null:
|
||||||
case json_false:
|
case json_false:
|
||||||
@ -66,57 +122,151 @@ void json_free(struct json_value *v)
|
|||||||
|
|
||||||
bool json_is(struct json_value const *value, enum json_type type)
|
bool json_is(struct json_value const *value, enum json_type type)
|
||||||
{
|
{
|
||||||
|
if (is_shallow(value))
|
||||||
|
return shallow_ty(value) == type;
|
||||||
|
|
||||||
return value->ty == type;
|
return value->ty == type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum json_type json_get_type(struct json_value const *value)
|
||||||
|
{
|
||||||
|
if (is_shallow(value))
|
||||||
|
return shallow_ty(value);
|
||||||
|
|
||||||
|
return value->ty;
|
||||||
|
}
|
||||||
|
|
||||||
bool json_get_bool(struct json_value const *value)
|
bool json_get_bool(struct json_value const *value)
|
||||||
{
|
{
|
||||||
assert(value->ty == json_true || value->ty == json_false);
|
enum json_type ty;
|
||||||
return value->ty == json_true;
|
|
||||||
|
if (is_shallow(value)) {
|
||||||
|
ty = shallow_ty(value);
|
||||||
|
} else {
|
||||||
|
ty = value->ty;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(ty == json_true || ty == json_false);
|
||||||
|
return ty == json_true;
|
||||||
}
|
}
|
||||||
int64_t json_get_int(struct json_value const *value)
|
int64_t json_get_int(struct json_value const *value)
|
||||||
{
|
{
|
||||||
|
if (is_shallow(value)) {
|
||||||
|
assert(shallow_ty(value) == json_int);
|
||||||
|
return (int64_t)shallow_val(value);
|
||||||
|
}
|
||||||
|
|
||||||
assert(value->ty == json_int);
|
assert(value->ty == json_int);
|
||||||
return value->int_val;
|
return value->int_val;
|
||||||
}
|
}
|
||||||
double json_get_float(struct json_value const *value)
|
double json_get_float(struct json_value const *value)
|
||||||
{
|
{
|
||||||
|
if (is_shallow(value)) {
|
||||||
|
assert(shallow_ty(value) == json_float);
|
||||||
|
size_t raw_val = shallow_val(value) << 6;
|
||||||
|
return *(double *)&raw_val;
|
||||||
|
}
|
||||||
|
|
||||||
assert(value->ty == json_float);
|
assert(value->ty == json_float);
|
||||||
return value->float_val;
|
return value->float_val;
|
||||||
}
|
}
|
||||||
char *json_get_string(struct json_value *value)
|
char const *json_get_string(struct json_value const *value)
|
||||||
{
|
{
|
||||||
assert(value->ty == json_string);
|
assert(value->ty == json_string);
|
||||||
return value->string_val;
|
return value->string_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
void json_set_bool(struct json_value *value, bool val)
|
void json_set_null(struct json_value **value)
|
||||||
{
|
{
|
||||||
assert(value->ty == json_true || value->ty == json_false);
|
if (is_shallow(*value)) {
|
||||||
value->ty = val ? json_true : json_false;
|
set_shallow_ty(value, json_null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*value)->ty = json_null;
|
||||||
}
|
}
|
||||||
void json_set_int(struct json_value *value, int64_t val)
|
|
||||||
|
void json_set_bool(struct json_value **value, bool val)
|
||||||
{
|
{
|
||||||
assert(value->ty == json_int);
|
if (is_shallow(*value)) {
|
||||||
value->int_val = val;
|
set_shallow_ty(value, val ? json_true : json_false);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*value)->ty = val ? json_true : json_false;
|
||||||
}
|
}
|
||||||
void json_set_float(struct json_value *value, double val)
|
void json_set_int(struct json_value **value, int64_t val)
|
||||||
{
|
{
|
||||||
assert(value->ty == json_float);
|
if (is_shallow(*value)) {
|
||||||
value->float_val = val;
|
if (val >= shallow_int_min && val <= shallow_int_max) {
|
||||||
|
set_shallow_ty(value, json_int);
|
||||||
|
set_shallow_val(value, (size_t)val);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
*value = new_deep(json_int);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(*value)->ty = json_int;
|
||||||
|
(*value)->int_val = val;
|
||||||
|
}
|
||||||
|
void json_set_float(struct json_value **value, double val)
|
||||||
|
{
|
||||||
|
if (is_shallow(*value)) {
|
||||||
|
size_t raw_val = *(size_t *)&val;
|
||||||
|
if ((raw_val & 0x7f) == 0) {
|
||||||
|
set_shallow_ty(value, json_float);
|
||||||
|
set_shallow_val(value, raw_val >> 6);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
*value = new_deep(json_float);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(*value)->ty = json_float;
|
||||||
|
(*value)->float_val = val;
|
||||||
}
|
}
|
||||||
void json_set_string(struct json_value *value, char *val)
|
void json_set_string(struct json_value *value, char *val)
|
||||||
{
|
{
|
||||||
assert(value->ty == json_string);
|
value->ty = json_string;
|
||||||
value->string_val = val;
|
value->string_val = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct json_value *json_new_int(int64_t val)
|
||||||
|
{
|
||||||
|
struct json_value *node = json_new(json_int);
|
||||||
|
json_set_int(&node, val);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct json_value *json_new_float(double val)
|
||||||
|
{
|
||||||
|
struct json_value *node = json_new(json_float);
|
||||||
|
json_set_float(&node, val);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t json_array_count(struct json_value const *array)
|
||||||
|
{
|
||||||
|
assert(array->ty == json_array);
|
||||||
|
return smallarray_count(&array->array_vals);
|
||||||
|
}
|
||||||
|
|
||||||
struct json_value *json_idx(struct json_value *array, size_t idx)
|
struct json_value *json_idx(struct json_value *array, size_t idx)
|
||||||
{
|
{
|
||||||
assert(array->ty == json_array);
|
assert(array->ty == json_array);
|
||||||
|
if (idx >= smallarray_count(&array->array_vals))
|
||||||
|
return NULL;
|
||||||
return smallarray_get(&array->array_vals, idx);
|
return smallarray_get(&array->array_vals, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct json_value const *json_idx_const(struct json_value const *array, size_t idx)
|
||||||
|
{
|
||||||
|
assert(array->ty == json_array);
|
||||||
|
if (idx >= smallarray_count(&array->array_vals))
|
||||||
|
return NULL;
|
||||||
|
return smallarray_get_const(&array->array_vals, idx);
|
||||||
|
}
|
||||||
|
|
||||||
void json_push(struct json_value *array, struct json_value *value)
|
void json_push(struct json_value *array, struct json_value *value)
|
||||||
{
|
{
|
||||||
assert(array->ty == json_array);
|
assert(array->ty == json_array);
|
||||||
@ -136,6 +286,14 @@ struct json_value *json_key_sized(
|
|||||||
return hashmap_get_sized(&object->object_fields, key, key_size);
|
return hashmap_get_sized(&object->object_fields, key, key_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct json_value const *json_key_hash_const(
|
||||||
|
struct json_value const *object, size_t hash)
|
||||||
|
{
|
||||||
|
assert(object->ty == json_object);
|
||||||
|
return hashmap_get_hash_const(&object->object_fields, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void json_set(
|
void json_set(
|
||||||
struct json_value *object, char const *key, struct json_value *value)
|
struct json_value *object, char const *key, struct json_value *value)
|
||||||
{
|
{
|
||||||
@ -151,3 +309,12 @@ void json_set_sized(struct json_value *object,
|
|||||||
assert(object->ty == json_object);
|
assert(object->ty == json_object);
|
||||||
hashmap_set_sized(&object->object_fields, key, key_size, value);
|
hashmap_set_sized(&object->object_fields, key, key_size, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void json_object_keys(
|
||||||
|
struct json_value const *object, struct hash_key_entry const **keys, size_t *count)
|
||||||
|
{
|
||||||
|
assert(object->ty == json_object);
|
||||||
|
*keys = object->object_fields.keys;
|
||||||
|
*count = object->object_fields.keys_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
34
src/main.c
34
src/main.c
@ -9,13 +9,20 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
static int stringify_cb(void *self, char const *data, unsigned long size)
|
||||||
|
{
|
||||||
|
(void)self;
|
||||||
|
fwrite(data, size, 1, stdout);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
if (argc < 3) {
|
if (argc < 3) {
|
||||||
fprintf(stderr, "error: incorrect arguments\n");
|
fprintf(stderr, "error: incorrect arguments\n");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
char const *pattern = argv[1];
|
char const *query = argv[1];
|
||||||
char const *filename = argv[2];
|
char const *filename = argv[2];
|
||||||
|
|
||||||
FILE *file = fopen(filename, "r");
|
FILE *file = fopen(filename, "r");
|
||||||
@ -48,16 +55,23 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
|
||||||
struct json_value *val = json_parse(text, file_size);
|
struct blockalloc alloc = { 0 };
|
||||||
|
blockalloc_construct(&alloc);
|
||||||
|
|
||||||
|
struct json_value *parsed = json_parse(text, file_size, &alloc);
|
||||||
|
if (!parsed) {
|
||||||
|
fprintf(stderr, "something went wrong\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
free(text);
|
free(text);
|
||||||
|
|
||||||
typedef struct json_value V;
|
struct json_value *result = json_query(parsed, query);
|
||||||
|
if (!result) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
json_stringify(result, stringify_cb, NULL);
|
||||||
|
fputc('\n', stdout);
|
||||||
|
|
||||||
V *a = json_key(val, "foo");
|
json_free(parsed);
|
||||||
V *b = json_idx(a, 0);
|
blockalloc_destroy(&alloc);
|
||||||
V *c = json_idx(a, 1);
|
|
||||||
printf(".foo[0] = %ld\n", json_get_int(b));
|
|
||||||
printf(".foo[1] = %s\n", json_get_string(c));
|
|
||||||
|
|
||||||
json_free(val);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user