json: add rudimentary JSON parser library
This commit is contained in:
parent
2cddc37a67
commit
bd92f00a40
|
@ -0,0 +1,109 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <toaru/json.h>
|
||||||
|
#include <toaru/hashmap.h>
|
||||||
|
#include <toaru/list.h>
|
||||||
|
|
||||||
|
typedef struct JSON_Value Value;
|
||||||
|
|
||||||
|
int main(int argc, char * argv[]) {
|
||||||
|
|
||||||
|
{
|
||||||
|
Value * result = json_parse("\"foo bar baz\"");
|
||||||
|
assert(result && result->type == JSON_TYPE_STRING);
|
||||||
|
assert(strcmp(result->string, "foo bar baz") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Value * result = json_parse("\"foo \\nbar baz\"");
|
||||||
|
assert(result && result->type == JSON_TYPE_STRING);
|
||||||
|
assert(strcmp(result->string, "foo \nbar baz") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Value * result = json_parse("-123");
|
||||||
|
assert(result && result->type == JSON_TYPE_NUMBER);
|
||||||
|
assert(fabs(result->number - (-123.0) < 0.0001));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Value * result = json_parse("2e3");
|
||||||
|
assert(result && result->type == JSON_TYPE_NUMBER);
|
||||||
|
assert(fabs(result->number - (2000.0) < 0.0001));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Value * result = json_parse("0.124");
|
||||||
|
assert(result && result->type == JSON_TYPE_NUMBER);
|
||||||
|
assert(fabs(result->number - (0.124) < 0.0001));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Value * result = json_parse("[ 1, 2, 3 ]");
|
||||||
|
assert(result && result->type == JSON_TYPE_ARRAY);
|
||||||
|
assert(result->array->length == 3);
|
||||||
|
|
||||||
|
assert(fabs(((Value *)list_dequeue(result->array)->value)->number - 1.0) < 0.0001);
|
||||||
|
assert(fabs(((Value *)list_dequeue(result->array)->value)->number - 2.0) < 0.0001);
|
||||||
|
assert(fabs(((Value *)list_dequeue(result->array)->value)->number - 3.0) < 0.0001);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Value * result = json_parse("true");
|
||||||
|
assert(result && result->type == JSON_TYPE_BOOL);
|
||||||
|
assert(result->boolean == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Value * result = json_parse("false");
|
||||||
|
assert(result && result->type == JSON_TYPE_BOOL);
|
||||||
|
assert(result->boolean == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Value * result = json_parse("null");
|
||||||
|
assert(result && result->type == JSON_TYPE_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Value * result = json_parse("torbs");
|
||||||
|
assert(!result);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Value * result = json_parse("{\"foo\": \"bar\", \"bix\": 123}");
|
||||||
|
assert(result && result->type == JSON_TYPE_OBJECT);
|
||||||
|
|
||||||
|
hashmap_t * hash = result->object;
|
||||||
|
assert(hashmap_get(hash, "foo"));
|
||||||
|
assert(((Value *)hashmap_get(hash, "foo"))->type == JSON_TYPE_STRING);
|
||||||
|
assert(strcmp(((Value *)hashmap_get(hash, "foo"))->string, "bar") == 0);
|
||||||
|
assert(((Value *)hashmap_get(hash, "bix"))->type == JSON_TYPE_NUMBER);
|
||||||
|
assert(fabs(((Value *)hashmap_get(hash, "bix"))->number - 123.0) < 0.00001);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
FILE * f = fopen("/opt/demo.json","r");
|
||||||
|
|
||||||
|
char str[1024];
|
||||||
|
fgets(str, 1024, f);
|
||||||
|
|
||||||
|
Value * result = json_parse(str);
|
||||||
|
assert(result && result->type == JSON_TYPE_OBJECT);
|
||||||
|
|
||||||
|
Value * _main = JSON_KEY(result,"main");
|
||||||
|
Value * conditions = (JSON_KEY(result,"weather") && JSON_KEY(result,"weather")->array->length > 0) ?
|
||||||
|
JSON_IND(JSON_KEY(result,"weather"),0) : NULL;
|
||||||
|
|
||||||
|
fprintf(stdout, "temp=%lf\n", JSON_KEY(_main,"temp")->number);
|
||||||
|
fprintf(stdout, "temp_r=%d\n", (int)JSON_KEY(_main,"temp")->number);
|
||||||
|
fprintf(stdout, "conditions=%s\n", conditions ? JSON_KEY(conditions,"main")->string : "");
|
||||||
|
fprintf(stdout, "icon=%s\n", conditions ? JSON_KEY(conditions,"icon")->string : "");
|
||||||
|
fprintf(stderr, "humidity=%d\n", (int)JSON_KEY(_main,"humidity")->number);
|
||||||
|
fprintf(stderr, "clouds=%d\n", (int)JSON_KEY(JSON_KEY(result,"clouds"),"all") ? JSON_KEY(JSON_KEY(result,"clouds"),"all")->number : 0);
|
||||||
|
fprintf(stderr, "city=%s\n", "Tokyo");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <_cheader.h>
|
||||||
|
#include <toaru/hashmap.h>
|
||||||
|
#include <toaru/list.h>
|
||||||
|
|
||||||
|
_Begin_C_Header
|
||||||
|
|
||||||
|
#define JSON_TYPE_OBJECT 0
|
||||||
|
#define JSON_TYPE_ARRAY 1
|
||||||
|
#define JSON_TYPE_STRING 2
|
||||||
|
#define JSON_TYPE_NUMBER 3
|
||||||
|
#define JSON_TYPE_BOOL 4
|
||||||
|
#define JSON_TYPE_NULL 5
|
||||||
|
|
||||||
|
struct JSON_Value {
|
||||||
|
int type;
|
||||||
|
union {
|
||||||
|
char * string;
|
||||||
|
double number;
|
||||||
|
list_t * array;
|
||||||
|
hashmap_t * object;
|
||||||
|
int boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#define JSON_KEY(v,k) ((struct JSON_Value *)(hashmap_get(v->object,k)))
|
||||||
|
#define JSON_IND(v,i) ((struct JSON_Value *)(list_index(v->array,i)))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* json_free
|
||||||
|
*
|
||||||
|
* Free a struct JSON_Value, and its contents recursively if it's an array,
|
||||||
|
* object, string, etc.
|
||||||
|
*/
|
||||||
|
extern void json_free(struct JSON_Value *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* json_parse
|
||||||
|
*
|
||||||
|
* Parse a string into a JSON_Value
|
||||||
|
*/
|
||||||
|
extern struct JSON_Value * json_parse(const char *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* json_parse_file
|
||||||
|
*
|
||||||
|
* Open a file path and parse its contents as JSON
|
||||||
|
* (Convenience function)
|
||||||
|
*/
|
||||||
|
extern struct JSON_Value * json_parse_file(const char * filename);
|
||||||
|
|
||||||
|
_End_C_Header
|
|
@ -0,0 +1,432 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <toaru/hashmap.h>
|
||||||
|
#include <toaru/list.h>
|
||||||
|
#include <toaru/json.h>
|
||||||
|
|
||||||
|
typedef struct JSON_Value Value;
|
||||||
|
|
||||||
|
/* Internal usage */
|
||||||
|
struct JSON_Context {
|
||||||
|
const char * string;
|
||||||
|
int c;
|
||||||
|
const char * error;
|
||||||
|
};
|
||||||
|
|
||||||
|
void json_free(Value * v) {
|
||||||
|
if (v->type == JSON_TYPE_STRING) {
|
||||||
|
free(v->string);
|
||||||
|
}
|
||||||
|
if (v->type == JSON_TYPE_OBJECT) {
|
||||||
|
hashmap_free(v->object);
|
||||||
|
free(v->object);
|
||||||
|
}
|
||||||
|
if (v->type == JSON_TYPE_ARRAY) {
|
||||||
|
foreach(node, v->array) {
|
||||||
|
json_free(node->value);
|
||||||
|
}
|
||||||
|
list_free(v->array);
|
||||||
|
free(v->array);
|
||||||
|
}
|
||||||
|
free(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value * value(struct JSON_Context * ctx);
|
||||||
|
|
||||||
|
static int peek(struct JSON_Context * ctx) {
|
||||||
|
return ctx->string[ctx->c];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void advance(struct JSON_Context * ctx) {
|
||||||
|
ctx->c++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void whitespace(struct JSON_Context * ctx) {
|
||||||
|
while (1) {
|
||||||
|
int ch = peek(ctx);
|
||||||
|
if (ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t') {
|
||||||
|
advance(ctx);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value * string(struct JSON_Context * ctx) {
|
||||||
|
if (peek(ctx) != '"') return NULL;
|
||||||
|
advance(ctx);
|
||||||
|
|
||||||
|
int size = 4;
|
||||||
|
char * tmp = malloc(4);
|
||||||
|
tmp[0] = 0;
|
||||||
|
int used = 0;
|
||||||
|
|
||||||
|
#define add(c) do { \
|
||||||
|
if (used + 1 == size) { \
|
||||||
|
size *= 2; \
|
||||||
|
tmp = realloc(tmp, size); \
|
||||||
|
} \
|
||||||
|
tmp[used] = c; \
|
||||||
|
tmp[used+1] = 0; \
|
||||||
|
used++; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
int ch = peek(ctx);
|
||||||
|
if (ch == 0) goto string_error;
|
||||||
|
if (ch == '"') {
|
||||||
|
break;
|
||||||
|
} else if (ch == '\\') {
|
||||||
|
advance(ctx);
|
||||||
|
ch = peek(ctx);
|
||||||
|
if (ch == '"') add('"');
|
||||||
|
else if (ch == '\\') add('\\');
|
||||||
|
else if (ch == '/') add('/');
|
||||||
|
else if (ch == 'b') add('\b');
|
||||||
|
else if (ch == 'f') add('\f');
|
||||||
|
else if (ch == 'n') add('\n');
|
||||||
|
else if (ch == 'r') add('\r');
|
||||||
|
else if (ch == 't') add('\t');
|
||||||
|
else if (ch == 'u') {
|
||||||
|
/* Parse hex */
|
||||||
|
advance(ctx);
|
||||||
|
char hex_digits[5];
|
||||||
|
if (!isxdigit(peek(ctx))) goto string_error;
|
||||||
|
hex_digits[0] = peek(ctx); advance(ctx);
|
||||||
|
if (!isxdigit(peek(ctx))) goto string_error;
|
||||||
|
hex_digits[1] = peek(ctx); advance(ctx);
|
||||||
|
if (!isxdigit(peek(ctx))) goto string_error;
|
||||||
|
hex_digits[2] = peek(ctx); advance(ctx);
|
||||||
|
if (!isxdigit(peek(ctx))) goto string_error;
|
||||||
|
hex_digits[3] = peek(ctx); /* will be advanced later */
|
||||||
|
hex_digits[4] = 0;
|
||||||
|
|
||||||
|
uint32_t val = strtoul(hex_digits, NULL, 16);
|
||||||
|
if (val < 0x0080) {
|
||||||
|
add(val);
|
||||||
|
} else if (val < 0x0800) {
|
||||||
|
add(0xC0 | (val >> 6));
|
||||||
|
add(0x80 | (val & 0x3F));
|
||||||
|
} else {
|
||||||
|
add(0xE0 | (val >> 12));
|
||||||
|
add(0x80 | ((val >> 6) & 0x3F));
|
||||||
|
add(0x80 | (val & 0x3F));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
goto string_error;
|
||||||
|
}
|
||||||
|
advance(ctx);
|
||||||
|
} else {
|
||||||
|
add(ch);
|
||||||
|
advance(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peek(ctx) != '"') {
|
||||||
|
ctx->error = "Unexpected EOF?";
|
||||||
|
goto string_error;
|
||||||
|
}
|
||||||
|
advance(ctx);
|
||||||
|
|
||||||
|
Value * out = malloc(sizeof(Value));
|
||||||
|
out->type = JSON_TYPE_STRING;
|
||||||
|
out->string = strdup(tmp);
|
||||||
|
free(tmp);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
|
||||||
|
string_error:
|
||||||
|
free(tmp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value * object(struct JSON_Context * ctx) {
|
||||||
|
if (peek(ctx) != '{') {
|
||||||
|
ctx->error = "Expected { (internal error)";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
advance(ctx);
|
||||||
|
|
||||||
|
Value * out;
|
||||||
|
hashmap_t * output = hashmap_create(10);
|
||||||
|
output->hash_val_free = (void (*)(void *))json_free;
|
||||||
|
whitespace(ctx);
|
||||||
|
|
||||||
|
if (peek(ctx) == '}') {
|
||||||
|
advance(ctx);
|
||||||
|
goto _object_done;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
whitespace(ctx);
|
||||||
|
Value * s = string(ctx);
|
||||||
|
|
||||||
|
if (!s) {
|
||||||
|
ctx->error = "Expected string";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
whitespace(ctx);
|
||||||
|
|
||||||
|
if (peek(ctx) != ':') {
|
||||||
|
ctx->error = "Expected :";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
advance(ctx);
|
||||||
|
|
||||||
|
Value * v = value(ctx);
|
||||||
|
|
||||||
|
hashmap_set(output, s->string, v);
|
||||||
|
json_free(s);
|
||||||
|
|
||||||
|
if (peek(ctx) == '}') {
|
||||||
|
advance(ctx);
|
||||||
|
goto _object_done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peek(ctx) != ',') {
|
||||||
|
ctx->error = "Expected , or {";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
advance(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
hashmap_free(output);
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
_object_done:
|
||||||
|
out = malloc(sizeof(Value));
|
||||||
|
out->type = JSON_TYPE_OBJECT;
|
||||||
|
out->object = output;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value * boolean(struct JSON_Context * ctx) {
|
||||||
|
int value = -1;
|
||||||
|
if (peek(ctx) == 't') {
|
||||||
|
advance(ctx);
|
||||||
|
if (peek(ctx) != 'r') { ctx->error = "Invalid literal while parsing bool"; return NULL; }
|
||||||
|
advance(ctx);
|
||||||
|
if (peek(ctx) != 'u') { ctx->error = "Invalid literal while parsing bool"; return NULL; }
|
||||||
|
advance(ctx);
|
||||||
|
if (peek(ctx) != 'e') { ctx->error = "Invalid literal while parsing bool"; return NULL; }
|
||||||
|
advance(ctx);
|
||||||
|
value = 1;
|
||||||
|
} else if (peek(ctx) == 'f') {
|
||||||
|
advance(ctx);
|
||||||
|
if (peek(ctx) != 'a') { ctx->error = "Invalid literal while parsing bool"; return NULL; }
|
||||||
|
advance(ctx);
|
||||||
|
if (peek(ctx) != 'l') { ctx->error = "Invalid literal while parsing bool"; return NULL; }
|
||||||
|
advance(ctx);
|
||||||
|
if (peek(ctx) != 's') { ctx->error = "Invalid literal while parsing bool"; return NULL; }
|
||||||
|
advance(ctx);
|
||||||
|
if (peek(ctx) != 'e') { ctx->error = "Invalid literal while parsing bool"; return NULL; }
|
||||||
|
advance(ctx);
|
||||||
|
value = 0;
|
||||||
|
} else { ctx->error = "Invalid literal while parsing bool"; return NULL; }
|
||||||
|
|
||||||
|
Value * out = malloc(sizeof(Value));
|
||||||
|
out->type = JSON_TYPE_BOOL;
|
||||||
|
out->boolean = value;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value * null(struct JSON_Context * ctx) {
|
||||||
|
if (peek(ctx) != 'n') { ctx->error = "Invalid literal while parsing null"; return NULL; }
|
||||||
|
advance(ctx);
|
||||||
|
if (peek(ctx) != 'u') { ctx->error = "Invalid literal while parsing null"; return NULL; }
|
||||||
|
advance(ctx);
|
||||||
|
if (peek(ctx) != 'l') { ctx->error = "Invalid literal while parsing null"; return NULL; }
|
||||||
|
advance(ctx);
|
||||||
|
if (peek(ctx) != 'l') { ctx->error = "Invalid literal while parsing null"; return NULL; }
|
||||||
|
advance(ctx);
|
||||||
|
|
||||||
|
Value * out = malloc(sizeof(Value));
|
||||||
|
out->type = JSON_TYPE_NULL;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value * number(struct JSON_Context * ctx) {
|
||||||
|
|
||||||
|
double value = 0;
|
||||||
|
int sign = 1;
|
||||||
|
if (peek(ctx) == '-') {
|
||||||
|
/* Negative */
|
||||||
|
sign = -1;
|
||||||
|
advance(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peek(ctx) == '0') {
|
||||||
|
advance(ctx);
|
||||||
|
} else if (isdigit(peek(ctx))) {
|
||||||
|
/* Read any digit */
|
||||||
|
value = peek(ctx) - '0';
|
||||||
|
advance(ctx);
|
||||||
|
while (isdigit(peek(ctx))) {
|
||||||
|
value *= 10;
|
||||||
|
value += peek(ctx) - '0';
|
||||||
|
advance(ctx);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx->error = "Expected digit";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peek(ctx) == '.') {
|
||||||
|
/* Read fractional part */
|
||||||
|
advance(ctx);
|
||||||
|
|
||||||
|
double multiplier = 0.1;
|
||||||
|
/* read at least one digit */
|
||||||
|
if (!isdigit(peek(ctx))) {
|
||||||
|
ctx->error = "Expected digit";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
while (isdigit(peek(ctx))) {
|
||||||
|
value += multiplier * (peek(ctx) - '0');
|
||||||
|
multiplier *= 0.1;
|
||||||
|
advance(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peek(ctx) == 'e' || peek(ctx) == 'E') {
|
||||||
|
/* Read exponent */
|
||||||
|
int exp_sign = 1;
|
||||||
|
advance(ctx);
|
||||||
|
if (peek(ctx) == '+') advance(ctx);
|
||||||
|
else if (peek(ctx) == '-') {
|
||||||
|
exp_sign = -1;
|
||||||
|
advance(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read digits */
|
||||||
|
if (!isdigit(peek(ctx))) {
|
||||||
|
ctx->error = "Expected digit";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
double exp = peek(ctx) - '0';
|
||||||
|
advance(ctx);
|
||||||
|
while (isdigit(peek(ctx))) {
|
||||||
|
exp *= 10;
|
||||||
|
exp += peek(ctx) - '0';
|
||||||
|
advance(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
value = value * pow(10.0,exp * exp_sign);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value * out = malloc(sizeof(Value));
|
||||||
|
out->type = JSON_TYPE_NUMBER;
|
||||||
|
out->number = value * sign;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value * array(struct JSON_Context * ctx) {
|
||||||
|
if (peek(ctx) != '[') return NULL;
|
||||||
|
advance(ctx);
|
||||||
|
|
||||||
|
whitespace(ctx);
|
||||||
|
|
||||||
|
list_t * output = list_create();
|
||||||
|
Value * out;
|
||||||
|
|
||||||
|
if (peek(ctx) == ']') {
|
||||||
|
advance(ctx);
|
||||||
|
goto _array_done;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
Value * next = value(ctx);
|
||||||
|
|
||||||
|
if (!next) break;
|
||||||
|
|
||||||
|
list_insert(output, next);
|
||||||
|
|
||||||
|
if (peek(ctx) == ']') {
|
||||||
|
advance(ctx);
|
||||||
|
goto _array_done;
|
||||||
|
}
|
||||||
|
if (peek(ctx) != ',') {
|
||||||
|
ctx->error = "Expected ,";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
advance(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* uh oh */
|
||||||
|
foreach(node, output) {
|
||||||
|
json_free(node->value);
|
||||||
|
}
|
||||||
|
list_free(output);
|
||||||
|
free(output);
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
_array_done:
|
||||||
|
out = malloc(sizeof(Value));
|
||||||
|
out->type = JSON_TYPE_ARRAY;
|
||||||
|
out->array = output;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define WHITE(c) { \
|
||||||
|
Value * out = c; \
|
||||||
|
whitespace(ctx); \
|
||||||
|
return out; \
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value * value(struct JSON_Context * ctx) {
|
||||||
|
whitespace(ctx);
|
||||||
|
if (peek(ctx) == '"') WHITE(string(ctx))
|
||||||
|
else if (peek(ctx) == '{') WHITE(object(ctx))
|
||||||
|
else if (peek(ctx) == '[') WHITE(array(ctx))
|
||||||
|
else if (peek(ctx) == '-' || isdigit(peek(ctx))) WHITE(number(ctx))
|
||||||
|
else if (peek(ctx) == 't') WHITE(boolean(ctx))
|
||||||
|
else if (peek(ctx) == 'f') WHITE(boolean(ctx))
|
||||||
|
else if (peek(ctx) == 'n') WHITE(null(ctx))
|
||||||
|
ctx->error = "Unexpected value";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value * json_parse(const char * str) {
|
||||||
|
struct JSON_Context ctx;
|
||||||
|
ctx.string = str;
|
||||||
|
ctx.c = 0;
|
||||||
|
ctx.error = NULL;
|
||||||
|
Value * out = value(&ctx);
|
||||||
|
#if 0
|
||||||
|
if (!out) {
|
||||||
|
fprintf(stderr, "JSON parse error at %d (%c)\n", ctx.c, ctx.string[ctx.c]);
|
||||||
|
fprintf(stderr, "%s\n", ctx.error);
|
||||||
|
fprintf(stderr, "%s\n", ctx.string);
|
||||||
|
for (int i = 0; i < ctx.c; ++i) { fprintf(stderr, " "); }
|
||||||
|
fprintf(stderr, "^\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value * json_parse_file(const char * filename) {
|
||||||
|
FILE * f = fopen(filename, "r");
|
||||||
|
|
||||||
|
if (!f) return NULL;
|
||||||
|
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
size_t size = ftell(f);
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
|
||||||
|
char * tmp = malloc(size + 1);
|
||||||
|
fread(tmp, size, 1, f);
|
||||||
|
tmp[size] = 0;
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
Value * out = json_parse(tmp);
|
||||||
|
free(tmp);
|
||||||
|
return out;
|
||||||
|
}
|
|
@ -24,7 +24,8 @@ class Classifier(object):
|
||||||
'<toaru/rline.h>': (None, '-ltoaru_rline', ['<toaru/kbd.h>']),
|
'<toaru/rline.h>': (None, '-ltoaru_rline', ['<toaru/kbd.h>']),
|
||||||
'<toaru/rline_exp.h>': (None, '-ltoaru_rline_exp', ['<toaru/rline.h>']),
|
'<toaru/rline_exp.h>': (None, '-ltoaru_rline_exp', ['<toaru/rline.h>']),
|
||||||
'<toaru/confreader.h>': (None, '-ltoaru_confreader', ['<toaru/hashmap.h>']),
|
'<toaru/confreader.h>': (None, '-ltoaru_confreader', ['<toaru/hashmap.h>']),
|
||||||
'<toaru/markup.h>': (None, '-ltoaru_markup', ['<toaru/hashmap.h>']),
|
'<toaru/markup.h>': (None, '-ltoaru_markup', ['<toaru/hashmap.h>']),
|
||||||
|
'<toaru/json.h>': (None, '-ltoaru_json', ['<toaru/hashmap.h>']),
|
||||||
'<toaru/yutani.h>': (None, '-ltoaru_yutani', ['<toaru/kbd.h>', '<toaru/list.h>', '<toaru/pex.h>', '<toaru/graphics.h>', '<toaru/hashmap.h>']),
|
'<toaru/yutani.h>': (None, '-ltoaru_yutani', ['<toaru/kbd.h>', '<toaru/list.h>', '<toaru/pex.h>', '<toaru/graphics.h>', '<toaru/hashmap.h>']),
|
||||||
'<toaru/decorations.h>': (None, '-ltoaru_decorations', ['<toaru/menu.h>', '<toaru/sdf.h>', '<toaru/graphics.h>', '<toaru/yutani.h>']),
|
'<toaru/decorations.h>': (None, '-ltoaru_decorations', ['<toaru/menu.h>', '<toaru/sdf.h>', '<toaru/graphics.h>', '<toaru/yutani.h>']),
|
||||||
'<toaru/termemu.h>': (None, '-ltoaru_termemu', ['<toaru/graphics.h>']),
|
'<toaru/termemu.h>': (None, '-ltoaru_termemu', ['<toaru/graphics.h>']),
|
||||||
|
|
Loading…
Reference in New Issue