Skip to content

Instantly share code, notes, and snippets.

@ShawSumma
Created June 24, 2024 16:38
Show Gist options
  • Save ShawSumma/07543e222dba8c501250726a1d1edb0c to your computer and use it in GitHub Desktop.
Save ShawSumma/07543e222dba8c501250726a1d1edb0c to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdlib.h>
#include <ctype.h>
struct json_array_t;
typedef struct json_array_t json_array_t;
struct json_object_t;
typedef struct json_object_t json_object_t;
struct json_value_t;
typedef struct json_value_t json_value_t;
struct json_array_t {
size_t alloc;
size_t len;
json_value_t *data;
};
struct json_object_t {
size_t alloc;
size_t len;
json_value_t *keys;
json_value_t *values;
};
struct json_value_t {
union {
bool boolean;
double number;
const char *string;
json_array_t *array;
json_object_t *object;
};
enum {
JSON_INVALID,
JSON_NULL,
JSON_BOOLEAN,
JSON_NUMBER,
JSON_STRING,
JSON_ARRAY,
JSON_OBJECT,
} type;
};
void file_trim(FILE *file) {
while (true) {
char c = fgetc(file);
if (!isspace(c)) {
ungetc(c, file);
break;
}
}
}
json_value_t read_json(FILE *file) {
file_trim(file);
char c = fgetc(file);
switch (c) {
case 'f': {
if (fgetc(file) == 'a' && fgetc(file) == 'l' && fgetc(file) == 's' && fgetc(file) == 'e') {
return (json_value_t) {
.type = JSON_BOOLEAN,
.boolean = false,
};
}
}
case 't': {
if (fgetc(file) == 'r' && fgetc(file) == 'u' && fgetc(file) == 'e') {
return (json_value_t) {
.type = JSON_BOOLEAN,
.boolean = true,
};
}
}
case '"': {
size_t alloc = 0;
size_t len = 0;
char *string = NULL;
while (true) {
char c = fgetc(file);
if (c == '"') {
break;
}
if (len + 1 >= alloc) {
alloc = (len + 1) * 2;
string = realloc(string, sizeof(char) * alloc);
}
string[len++] = c;
}
return (json_value_t) {
.type = JSON_STRING,
.string = string,
};
}
case '[': {
ungetc(c, file);
json_array_t *array = malloc(sizeof(json_array_t));
*array = (json_array_t) {0};
while (true) {
file_trim(file);
char last = fgetc(file);
if (last == ']') {
break;
}
json_value_t entry = read_json(file);
if (array->len + 1 >= array->alloc) {
array->alloc = (array->len + 1) * 2;
array->data = realloc(array->data, sizeof(json_value_t) * array->alloc);
}
array->data[array->len++] = entry;
}
return (json_value_t) {
.type = JSON_ARRAY,
.array = array,
};
}
default: {
ungetc(c, file);
if ('0' <= c && c <= '9') {
double number;
fscanf(file, "%lf", &number);
return (json_value_t) {
.type = JSON_NUMBER,
.number = number,
};
}
break;
}
}
return (json_value_t) {
.type = JSON_INVALID,
};
}
void write_json(FILE *out, json_value_t value) {
switch (value.type) {
case JSON_INVALID: {
fprintf(out, "ERROR\n");
break;
}
case JSON_NULL: {
fprintf(out, "null");
break;
}
case JSON_BOOLEAN: {
if (value.boolean) {
fprintf(out, "true");
} else {
fprintf(out, "false");
}
break;
}
case JSON_NUMBER: {
fprintf(out, "%.17g", value.number);
break;
}
case JSON_STRING: {
fprintf(out, "\"%s\"", value.string);
break;
}
case JSON_ARRAY: {
fprintf(out, "[");
for (size_t i = 0; i < value.array->len; i++) {
if (i != 0) {
fprintf(out, ",");
}
write_json(out, value.array->data[i]);
}
fprintf(out, "]");
break;
}
case JSON_OBJECT: {
fprintf(out, "OBJECT\n");
break;
}
}
}
int main() {
FILE *in_file = fopen("in.json", "r");
json_value_t value = read_json(in_file);
fclose(in_file);
FILE *out_file = fopen("out.json", "w");
write_json(out_file, value);
fclose(out_file);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment