fix: json eval
This commit is contained in:
@@ -267,19 +267,24 @@ Json *json_parse(Arena *arena, const char **s) {
|
|||||||
return json_parse_value__(s, arena);
|
return json_parse_value__(s, arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Minimal JSON printer.
|
char *json_to_string(Arena *arena, Json *item) {
|
||||||
For simplicity, a fixed-size buffer is used.
|
return json_to_string_with_opts(arena, item, 0);
|
||||||
In production you’d dynamically size or use the arena. */
|
}
|
||||||
char *json_print(Arena *arena, Json *item) {
|
|
||||||
|
/* Minimal JSON printer with raw output option.
|
||||||
|
When raw is non-zero and the item is a JSON_STRING, it is printed without quotes.
|
||||||
|
*/
|
||||||
|
char *json_to_string_with_opts(Arena *arena, Json *item, int raw) {
|
||||||
char *out = arena_alloc(arena, 1024);
|
char *out = arena_alloc(arena, 1024);
|
||||||
if (!out) return NULL;
|
if (!out)
|
||||||
|
return NULL;
|
||||||
char *ptr = out;
|
char *ptr = out;
|
||||||
if (item->type == JSON_OBJECT) {
|
if (item->type == JSON_OBJECT) {
|
||||||
ptr += sprintf(ptr, "{");
|
ptr += sprintf(ptr, "{");
|
||||||
Json *child = item->child;
|
Json *child = item->child;
|
||||||
while (child) {
|
while (child) {
|
||||||
ptr += sprintf(ptr, "\"%s\":", child->key ? child->key : "");
|
ptr += sprintf(ptr, "\"%s\":", child->key ? child->key : "");
|
||||||
char *child_str = json_print(arena, child);
|
char *child_str = json_to_string_with_opts(arena, child, raw);
|
||||||
ptr += sprintf(ptr, "%s", child_str);
|
ptr += sprintf(ptr, "%s", child_str);
|
||||||
if (child->next)
|
if (child->next)
|
||||||
ptr += sprintf(ptr, ",");
|
ptr += sprintf(ptr, ",");
|
||||||
@@ -290,7 +295,7 @@ char *json_print(Arena *arena, Json *item) {
|
|||||||
ptr += sprintf(ptr, "[");
|
ptr += sprintf(ptr, "[");
|
||||||
Json *child = item->child;
|
Json *child = item->child;
|
||||||
while (child) {
|
while (child) {
|
||||||
char *child_str = json_print(arena, child);
|
char *child_str = json_to_string_with_opts(arena, child, raw);
|
||||||
ptr += sprintf(ptr, "%s", child_str);
|
ptr += sprintf(ptr, "%s", child_str);
|
||||||
if (child->next)
|
if (child->next)
|
||||||
ptr += sprintf(ptr, ",");
|
ptr += sprintf(ptr, ",");
|
||||||
@@ -298,13 +303,16 @@ char *json_print(Arena *arena, Json *item) {
|
|||||||
}
|
}
|
||||||
sprintf(ptr, "]");
|
sprintf(ptr, "]");
|
||||||
} else if (item->type == JSON_STRING) {
|
} else if (item->type == JSON_STRING) {
|
||||||
sprintf(out, "\"%s\"", item->string);
|
if (raw)
|
||||||
|
sprintf(ptr, "%s", item->string);
|
||||||
|
else
|
||||||
|
sprintf(ptr, "\"%s\"", item->string);
|
||||||
} else if (item->type == JSON_NUMBER) {
|
} else if (item->type == JSON_NUMBER) {
|
||||||
sprintf(out, "%g", item->number);
|
sprintf(ptr, "%g", item->number);
|
||||||
} else if (item->type == JSON_BOOL) {
|
} else if (item->type == JSON_BOOL) {
|
||||||
sprintf(out, item->boolean ? "true" : "false");
|
sprintf(ptr, item->boolean ? "true" : "false");
|
||||||
} else if (item->type == JSON_NULL) {
|
} else if (item->type == JSON_NULL) {
|
||||||
sprintf(out, "null");
|
sprintf(ptr, "null");
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -226,10 +226,9 @@ typedef struct Json {
|
|||||||
|
|
||||||
Json *json_parse(Arena *arena, const char **s);
|
Json *json_parse(Arena *arena, const char **s);
|
||||||
|
|
||||||
/* Minimal JSON printer.
|
char *json_to_string(Arena *arena, Json *item);
|
||||||
For simplicity, a fixed-size buffer is used.
|
|
||||||
In production you’d dynamically size or use the arena. */
|
char *json_to_string_with_opts(Arena *arena, Json *item, int raw);
|
||||||
char *json_print(Arena *arena, Json *item);
|
|
||||||
|
|
||||||
/* Retrieve an object item by key (case-sensitive) */
|
/* Retrieve an object item by key (case-sensitive) */
|
||||||
Json *json_get_object_item(Json *object, const char *key);
|
Json *json_get_object_item(Json *object, const char *key);
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ static void test_print_json_object(void) {
|
|||||||
Arena arena = arena_init(ARENA_SIZE);
|
Arena arena = arena_init(ARENA_SIZE);
|
||||||
const char *json = "{\"key\":\"value\", \"num\":3.14}";
|
const char *json = "{\"key\":\"value\", \"num\":3.14}";
|
||||||
Json *root = json_parse(&arena, &json);
|
Json *root = json_parse(&arena, &json);
|
||||||
char *printed = json_print(&arena, root);
|
char *printed = json_to_string(&arena, root);
|
||||||
assert(strstr(printed, "\"key\":") != NULL);
|
assert(strstr(printed, "\"key\":") != NULL);
|
||||||
assert(strstr(printed, "\"value\"") != NULL);
|
assert(strstr(printed, "\"value\"") != NULL);
|
||||||
assert(strstr(printed, "\"num\":") != NULL);
|
assert(strstr(printed, "\"num\":") != NULL);
|
||||||
@@ -72,7 +72,7 @@ static void test_print_json_number(void) {
|
|||||||
Arena arena = arena_init(ARENA_SIZE);
|
Arena arena = arena_init(ARENA_SIZE);
|
||||||
const char *json = "123.456";
|
const char *json = "123.456";
|
||||||
Json *root = json_parse(&arena, &json);
|
Json *root = json_parse(&arena, &json);
|
||||||
char *printed = json_print(&arena, root);
|
char *printed = json_to_string(&arena, root);
|
||||||
double val = atof(printed);
|
double val = atof(printed);
|
||||||
assert(val == 123.456);
|
assert(val == 123.456);
|
||||||
arena_free(&arena);
|
arena_free(&arena);
|
||||||
@@ -83,7 +83,7 @@ static void test_print_json_string(void) {
|
|||||||
Arena arena = arena_init(ARENA_SIZE);
|
Arena arena = arena_init(ARENA_SIZE);
|
||||||
const char *json = "\"test string\"";
|
const char *json = "\"test string\"";
|
||||||
Json *root = json_parse(&arena, &json);
|
Json *root = json_parse(&arena, &json);
|
||||||
char *printed = json_print(&arena, root);
|
char *printed = json_to_string(&arena, root);
|
||||||
assert(strcmp(printed, "\"test string\"") == 0);
|
assert(strcmp(printed, "\"test string\"") == 0);
|
||||||
arena_free(&arena);
|
arena_free(&arena);
|
||||||
}
|
}
|
||||||
@@ -113,11 +113,11 @@ static void test_arena_reset_reuse(void) {
|
|||||||
Arena arena = arena_init(ARENA_SIZE);
|
Arena arena = arena_init(ARENA_SIZE);
|
||||||
const char *json1 = "{\"key\":\"value\"}";
|
const char *json1 = "{\"key\":\"value\"}";
|
||||||
Json *root1 = json_parse(&arena, &json1);
|
Json *root1 = json_parse(&arena, &json1);
|
||||||
char *printed1 = json_print(&arena, root1);
|
char *printed1 = json_to_string(&arena, root1);
|
||||||
arena_reset(&arena);
|
arena_reset(&arena);
|
||||||
const char *json2 = "\"another test\"";
|
const char *json2 = "\"another test\"";
|
||||||
Json *root2 = json_parse(&arena, &json2);
|
Json *root2 = json_parse(&arena, &json2);
|
||||||
char *printed2 = json_print(&arena, root2);
|
char *printed2 = json_to_string(&arena, root2);
|
||||||
assert(strcmp(printed2, "\"another test\"") == 0);
|
assert(strcmp(printed2, "\"another test\"") == 0);
|
||||||
arena_free(&arena);
|
arena_free(&arena);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,26 +2,28 @@
|
|||||||
|
|
||||||
char *eval(Arena *arena, const Json * const context, const char * const key) {
|
char *eval(Arena *arena, const Json * const context, const char * const key) {
|
||||||
if (!context || !key) return NULL;
|
if (!context || !key) return NULL;
|
||||||
|
|
||||||
char *key_copy = arena_strdup(arena, key);
|
char *key_copy = arena_strdup(arena, key);
|
||||||
char *token, *rest = key_copy;
|
|
||||||
Json *res = context;
|
Json *res = context;
|
||||||
while ((token = strtok_r(rest, ".", &rest))) {
|
char *start = key_copy;
|
||||||
raise_debug("context: %s; token: %s", json_print(arena, res), key);
|
char *dot;
|
||||||
res = json_get_object_item(res, &token);
|
|
||||||
|
// Instead of using strtok_r, manually split the string using strchr.
|
||||||
|
while ((dot = strchr(start, '.')) != NULL) {
|
||||||
|
*dot = '\0';
|
||||||
|
raise_debug("res: %s, token: %s, key: %s", json_to_string(arena, res), start, key);
|
||||||
|
res = json_get_object_item(res, start);
|
||||||
if (!res)
|
if (!res)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
start = dot + 1;
|
||||||
}
|
}
|
||||||
if (res == JSON_STRING && res->string)
|
|
||||||
return arena_strdup(arena, res->string);
|
raise_debug("res: %s, token: %s, key: %s", json_to_string(arena, res), start, key);
|
||||||
else if (res == JSON_NUMBER) {
|
res = json_get_object_item(res, start);
|
||||||
char buf[64];
|
if (!res)
|
||||||
snprintf(buf, sizeof(buf), "%g", res->number);
|
return NULL;
|
||||||
return arena_strdup(arena, buf);
|
|
||||||
}
|
return json_to_string_with_opts(arena, res, 1);
|
||||||
char *temp = json_print(arena, res);
|
|
||||||
char *result = arena_strdup(arena, temp);
|
|
||||||
free(temp);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Modified: text is passed by reference so we can update it and free old allocations */
|
/* Modified: text is passed by reference so we can update it and free old allocations */
|
||||||
|
|||||||
Reference in New Issue
Block a user