#include "hsv.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
static char *strdup_range(const char *start, const char *end) {
size_t len = end - start;
char *s = malloc(len + 1);
if (s) {
memcpy(s, start, len);
s[len] = '\0';
}
return s;
}
static hsv_value_t *create_string_value(const char *s) {
hsv_value_t *v = malloc(sizeof(hsv_value_t));
if (v) {
v->type = HSV_TYPE_STRING;
v->data.string = strdup(s);
}
return v;
}
static hsv_value_t *create_object_value(void) {
hsv_value_t *v = malloc(sizeof(hsv_value_t));
if (v) {
v->type = HSV_TYPE_OBJECT;
v->data.object.pairs = NULL;
v->data.object.count = 0;
}
return v;
}
static hsv_value_t *create_array_value(void) {
hsv_value_t *v = malloc(sizeof(hsv_value_t));
if (v) {
v->type = HSV_TYPE_ARRAY;
v->data.array.items = NULL;
v->data.array.count = 0;
}
return v;
}
static void object_add_pair(hsv_value_t *obj, const char *key, hsv_value_t *value) {
if (obj->type != HSV_TYPE_OBJECT) return;
size_t new_count = obj->data.object.count + 1;
hsv_pair_t *new_pairs = realloc(obj->data.object.pairs, new_count * sizeof(hsv_pair_t));
if (new_pairs) {
obj->data.object.pairs = new_pairs;
obj->data.object.pairs[obj->data.object.count].key = strdup(key);
obj->data.object.pairs[obj->data.object.count].value = value;
obj->data.object.count = new_count;
}
}
static void array_add_item(hsv_value_t *arr, hsv_value_t *item) {
if (arr->type != HSV_TYPE_ARRAY) return;
size_t new_count = arr->data.array.count + 1;
hsv_value_t **new_items = realloc(arr->data.array.items, new_count * sizeof(hsv_value_t *));
if (new_items) {
arr->data.array.items = new_items;
arr->data.array.items[arr->data.array.count] = item;
arr->data.array.count = new_count;
}
}
typedef struct {
char **parts;
size_t count;
} split_result_t;
static split_result_t split_respecting_nesting(const char *text, char sep) {
split_result_t result = {NULL, 0};
const char *start = text;
const char *p = text;
int depth = 0;
while (*p) {
if (*p == HSV_SO) {
depth++;
} else if (*p == HSV_SI) {
depth--;
} else if (*p == sep && depth == 0) {
result.parts = realloc(result.parts, (result.count + 1) * sizeof(char *));
result.parts[result.count] = strdup_range(start, p);
result.count++;
start = p + 1;
}
p++;
}
result.parts = realloc(result.parts, (result.count + 1) * sizeof(char *));
result.parts[result.count] = strdup(start);
result.count++;
return result;
}
static void free_split_result(split_result_t *r) {
for (size_t i = 0; i < r->count; i++) {
free(r->parts[i]);
}
free(r->parts);
r->parts = NULL;
r->count = 0;
}
static hsv_value_t *parse_value(const char *text);
static hsv_value_t *parse_object(const char *text);
static hsv_value_t *parse_value(const char *text) {
size_t len = strlen(text);
if (len >= 2 && text[0] == HSV_SO && text[len - 1] == HSV_SI) {
char *inner = strdup_range(text + 1, text + len - 1);
hsv_value_t *obj = parse_object(inner);
free(inner);
return obj;
}
if (strchr(text, HSV_GS)) {
hsv_value_t *arr = create_array_value();
split_result_t parts = split_respecting_nesting(text, HSV_GS);
for (size_t i = 0; i < parts.count; i++) {
array_add_item(arr, parse_value(parts.parts[i]));
}
free_split_result(&parts);
return arr;
}
return create_string_value(text);
}
static hsv_value_t *parse_object(const char *text) {
hsv_value_t *obj = create_object_value();
split_result_t props = split_respecting_nesting(text, HSV_RS);
for (size_t i = 0; i < props.count; i++) {
split_result_t kv = split_respecting_nesting(props.parts[i], HSV_US);
if (kv.count >= 2) {
size_t value_len = 0;
for (size_t j = 1; j < kv.count; j++) {
value_len += strlen(kv.parts[j]) + 1;
}
char *value = malloc(value_len);
value[0] = '\0';
for (size_t j = 1; j < kv.count; j++) {
if (j > 1) {
size_t len = strlen(value);
value[len] = HSV_US;
value[len + 1] = '\0';
}
strcat(value, kv.parts[j]);
}
object_add_pair(obj, kv.parts[0], parse_value(value));
free(value);
}
free_split_result(&kv);
}
free_split_result(&props);
return obj;
}
hsv_document_t *hsv_parse(const char *text) {
hsv_document_t *doc = malloc(sizeof(hsv_document_t));
if (!doc) return NULL;
doc->header = NULL;
doc->records = NULL;
doc->record_count = 0;
const char *p = text;
while (*p) {
if (*p == HSV_SOH) {
const char *stx = strchr(p + 1, HSV_STX);
if (!stx) {
p++;
continue;
}
char *header_content = strdup_range(p + 1, stx);
doc->header = parse_object(header_content);
free(header_content);
const char *etx = strchr(stx + 1, HSV_ETX);
if (!etx) {
p = stx + 1;
continue;
}
char *data_content = strdup_range(stx + 1, etx);
split_result_t records = split_respecting_nesting(data_content, HSV_FS);
for (size_t i = 0; i < records.count; i++) {
hsv_value_t *obj = parse_object(records.parts[i]);
if (obj->data.object.count > 0) {
doc->records = realloc(doc->records, (doc->record_count + 1) * sizeof(hsv_value_t *));
doc->records[doc->record_count] = obj;
doc->record_count++;
} else {
hsv_free_value(obj);
}
}
free_split_result(&records);
free(data_content);
p = etx + 1;
} else if (*p == HSV_STX) {
const char *etx = strchr(p + 1, HSV_ETX);
if (!etx) {
p++;
continue;
}
char *data_content = strdup_range(p + 1, etx);
split_result_t records = split_respecting_nesting(data_content, HSV_FS);
for (size_t i = 0; i < records.count; i++) {
hsv_value_t *obj = parse_object(records.parts[i]);
if (obj->data.object.count > 0) {
doc->records = realloc(doc->records, (doc->record_count + 1) * sizeof(hsv_value_t *));
doc->records[doc->record_count] = obj;
doc->record_count++;
} else {
hsv_free_value(obj);
}
}
free_split_result(&records);
free(data_content);
p = etx + 1;
} else {
p++;
}
}
return doc;
}
void hsv_free_value(hsv_value_t *value) {
if (!value) return;
switch (value->type) {
case HSV_TYPE_STRING:
free(value->data.string);
break;
case HSV_TYPE_ARRAY:
for (size_t i = 0; i < value->data.array.count; i++) {
hsv_free_value(value->data.array.items[i]);
}
free(value->data.array.items);
break;
case HSV_TYPE_OBJECT:
for (size_t i = 0; i < value->data.object.count; i++) {
free(value->data.object.pairs[i].key);
hsv_free_value(value->data.object.pairs[i].value);
}
free(value->data.object.pairs);
break;
}
free(value);
}
void hsv_free_document(hsv_document_t *doc) {
if (!doc) return;
hsv_free_value(doc->header);
for (size_t i = 0; i < doc->record_count; i++) {
hsv_free_value(doc->records[i]);
}
free(doc->records);
free(doc);
}
const char *hsv_get_string(hsv_value_t *obj, const char *key) {
hsv_value_t *v = hsv_get(obj, key);
if (v && v->type == HSV_TYPE_STRING) {
return v->data.string;
}
return NULL;
}
hsv_value_t *hsv_get(hsv_value_t *obj, const char *key) {
if (!obj || obj->type != HSV_TYPE_OBJECT) return NULL;
for (size_t i = 0; i < obj->data.object.count; i++) {
if (strcmp(obj->data.object.pairs[i].key, key) == 0) {
return obj->data.object.pairs[i].value;
}
}
return NULL;
}