#ifndef YAML_H #define YAML_H #ifdef __cplusplus extern "C" { #endif #include /* --- Public Types --- */ // Node types. typedef enum { YAML_UNDEF, // Undefined YAML_STR, // Scalar string YAML_SEQ, // Sequence (list) YAML_MAP // Mapping (dictionary) } YAMLType; // Opaque types. typedef struct YAMLNode YAMLNode; typedef struct YAMLDoc YAMLDoc; typedef struct YAMLEmitter YAMLEmitter; /* --- Document Loading and Parsing --- */ // Load a YAML document from a file. For demonstration, the file content is treated as a scalar. YAMLDoc* yaml_load_file(const char *filename); // Load a YAML document from a string. (Currently, the entire string is parsed as a scalar.) YAMLDoc* yaml_load_str(const char *yaml_string); // Destroy the document and free all associated memory. void yaml_doc_destroy(YAMLDoc *doc); // Get the root node of the document. const YAMLNode* yaml_doc_root(const YAMLDoc *doc); /* --- Node Querying and Access --- */ // Get the type of a YAML node. YAMLType yaml_node_type(const YAMLNode *node); // Retrieve the scalar value of a node (if it is a scalar), or NULL. const char* yaml_as_str(const YAMLNode *node); // Convert a scalar node to int. Returns 0 if not a scalar. int yaml_as_int(const YAMLNode *node); // Convert a scalar node to double. Returns 0.0 if not a scalar. double yaml_as_double(const YAMLNode *node); // For mapping nodes: get a child node by key (returns NULL if not found). const YAMLNode* yaml_map_get(const YAMLNode *node, const char *key); // For sequence nodes: return the number of child nodes. size_t yaml_seq_size(const YAMLNode *node); // For sequence nodes: retrieve a child node by index (returns NULL if out of range). const YAMLNode* yaml_seq_index(const YAMLNode *node, size_t index); /* --- Node Creation and Modification --- */ // Create a new scalar node with the given string value. YAMLNode* yaml_new_str(const char *value); // Create a new, empty sequence node. YAMLNode* yaml_new_seq(void); // Create a new, empty mapping node. YAMLNode* yaml_new_map(void); // Append a child node to a sequence node. int yaml_seq_append(YAMLNode *node, YAMLNode *child); // Set a key/value pair in a mapping node (the key is copied). int yaml_map_set(YAMLNode *node, const char *key, YAMLNode *child); // Recursively free a YAML node and its children. void yaml_node_destroy(YAMLNode *node); /* --- YAML Emitting --- */ // Create a new YAML emitter. YAMLEmitter* yaml_emitter_create(void); // Emit the YAML document into the emitter's buffer. int yaml_emit(YAMLEmitter *emitter, const YAMLDoc *doc); // Write the emitter's content to a file (returns 0 on success). int yaml_emit_to_file(YAMLEmitter *emitter, const char *filename); // Get a string copy of the emitter's buffer (caller must free it). char* yaml_emit_to_str(YAMLEmitter *emitter); // Destroy the emitter and free its memory. void yaml_emitter_destroy(YAMLEmitter *emitter); #ifdef __cplusplus } #endif /* --- Implementation --- */ /* To compile the implementation, define YAML_IMPLEMENTATION in one source file before including "yaml.h". */ #ifdef YAML_IMPLEMENTATION #include #include #include /* --- Internal Structures --- */ struct YAMLDoc { YAMLNode *root; }; struct YAMLNode { YAMLType type; // For scalar nodes: char *str; // For sequence nodes: YAMLNode **seq; size_t seq_size; // For mapping nodes: char **keys; YAMLNode **vals; size_t map_size; }; struct YAMLEmitter { char *buffer; size_t size; size_t capacity; }; #define YAMLEMIT_INITIAL_CAPACITY 256 /* --- Document Loading and Parsing --- */ // For demonstration purposes, our "parser" treats the entire input as a scalar node. // In a full implementation, you would integrate a complete YAML parser. YAMLDoc* yaml_load_str(const char *yaml_string) { if (!yaml_string) return NULL; YAMLDoc *doc = (YAMLDoc*)malloc(sizeof(YAMLDoc)); if (!doc) return NULL; doc->root = (YAMLNode*)malloc(sizeof(YAMLNode)); if (!doc->root) { free(doc); return NULL; } doc->root->type = YAML_STR; doc->root->str = strdup(yaml_string); doc->root->seq = NULL; doc->root->seq_size = 0; doc->root->keys = NULL; doc->root->vals = NULL; doc->root->map_size = 0; return doc; } YAMLDoc* yaml_load_file(const char *filename) { FILE *file = fopen(filename, "rb"); if (!file) return NULL; fseek(file, 0, SEEK_END); long length = ftell(file); rewind(file); char *content = (char*)malloc(length + 1); if (!content) { fclose(file); return NULL; } fread(content, 1, length, file); content[length] = '\0'; fclose(file); YAMLDoc *doc = yaml_load_str(content); free(content); return doc; } void yaml_doc_destroy(YAMLDoc *doc) { if (!doc) return; if (doc->root) yaml_node_destroy(doc->root); free(doc); } const YAMLNode* yaml_doc_root(const YAMLDoc *doc) { if (!doc) return NULL; return doc->root; } /* --- Node Querying and Access --- */ YAMLType yaml_node_type(const YAMLNode *node) { if (!node) return YAML_UNDEF; return node->type; } const char* yaml_as_str(const YAMLNode *node) { if (!node || node->type != YAML_STR) return NULL; return node->str; } int yaml_as_int(const YAMLNode *node) { if (!node || node->type != YAML_STR) return 0; return atoi(node->str); } double yaml_as_double(const YAMLNode *node) { if (!node || node->type != YAML_STR) return 0.0; return atof(node->str); } const YAMLNode* yaml_map_get(const YAMLNode *node, const char *key) { if (!node || node->type != YAML_MAP) return NULL; for (size_t i = 0; i < node->map_size; i++) { if (strcmp(node->keys[i], key) == 0) return node->vals[i]; } return NULL; } size_t yaml_seq_size(const YAMLNode *node) { if (!node || node->type != YAML_SEQ) return 0; return node->seq_size; } const YAMLNode* yaml_seq_index(const YAMLNode *node, size_t index) { if (!node || node->type != YAML_SEQ || index >= node->seq_size) return NULL; return node->seq[index]; } /* --- Node Creation and Modification --- */ YAMLNode* yaml_new_str(const char *value) { YAMLNode *node = (YAMLNode*)malloc(sizeof(YAMLNode)); if (!node) return NULL; node->type = YAML_STR; node->str = strdup(value ? value : ""); node->seq = NULL; node->seq_size = 0; node->keys = NULL; node->vals = NULL; node->map_size = 0; return node; } YAMLNode* yaml_new_seq(void) { YAMLNode *node = (YAMLNode*)malloc(sizeof(YAMLNode)); if (!node) return NULL; node->type = YAML_SEQ; node->str = NULL; node->seq = NULL; node->seq_size = 0; node->keys = NULL; node->vals = NULL; node->map_size = 0; return node; } YAMLNode* yaml_new_map(void) { YAMLNode *node = (YAMLNode*)malloc(sizeof(YAMLNode)); if (!node) return NULL; node->type = YAML_MAP; node->str = NULL; node->seq = NULL; node->seq_size = 0; node->keys = NULL; node->vals = NULL; node->map_size = 0; return node; } int yaml_seq_append(YAMLNode *node, YAMLNode *child) { if (!node || node->type != YAML_SEQ) return -1; YAMLNode **new_seq = (YAMLNode**)realloc(node->seq, sizeof(YAMLNode*) * (node->seq_size + 1)); if (!new_seq) return -1; node->seq = new_seq; node->seq[node->seq_size] = child; node->seq_size++; return 0; } int yaml_map_set(YAMLNode *node, const char *key, YAMLNode *child) { if (!node || node->type != YAML_MAP) return -1; // Append new key/value pair char **new_keys = (char**)realloc(node->keys, sizeof(char*) * (node->map_size + 1)); if (!new_keys) return -1; node->keys = new_keys; node->keys[node->map_size] = strdup(key); YAMLNode **new_vals = (YAMLNode**)realloc(node->vals, sizeof(YAMLNode*) * (node->map_size + 1)); if (!new_vals) return -1; node->vals = new_vals; node->vals[node->map_size] = child; node->map_size++; return 0; } void yaml_node_destroy(YAMLNode *node) { if (!node) return; if (node->type == YAML_STR) { free(node->str); } else if (node->type == YAML_SEQ) { for (size_t i = 0; i < node->seq_size; i++) { yaml_node_destroy(node->seq[i]); } free(node->seq); } else if (node->type == YAML_MAP) { for (size_t i = 0; i < node->map_size; i++) { free(node->keys[i]); yaml_node_destroy(node->vals[i]); } free(node->keys); free(node->vals); } free(node); } /* --- YAML Emitting --- */ YAMLEmitter* yaml_emitter_create(void) { YAMLEmitter *emitter = (YAMLEmitter*)malloc(sizeof(YAMLEmitter)); if (!emitter) return NULL; emitter->buffer = (char*)malloc(YAMLEMIT_INITIAL_CAPACITY); if (!emitter->buffer) { free(emitter); return NULL; } emitter->buffer[0] = '\0'; emitter->size = 0; emitter->capacity = YAMLEMIT_INITIAL_CAPACITY; return emitter; } // Internal function: append string to emitter's buffer. static int yaml_emitter_append(YAMLEmitter *emitter, const char *str) { if (!emitter || !str) return -1; size_t len = strlen(str); if (emitter->size + len + 1 > emitter->capacity) { size_t new_cap = emitter->capacity * 2; while (emitter->size + len + 1 > new_cap) new_cap *= 2; char *new_buf = (char*)realloc(emitter->buffer, new_cap); if (!new_buf) return -1; emitter->buffer = new_buf; emitter->capacity = new_cap; } memcpy(emitter->buffer + emitter->size, str, len); emitter->size += len; emitter->buffer[emitter->size] = '\0'; return 0; } // Recursive function to emit a YAML node with proper indentation. static int yaml_emit_node(YAMLEmitter *emitter, const YAMLNode *node, int indent) { char indent_str[256]; if (indent < (int)sizeof(indent_str)) { memset(indent_str, ' ', indent); indent_str[indent] = '\0'; } else { indent_str[0] = '\0'; } char line_buf[1024]; int ret; if (!node) return -1; switch (node->type) { case YAML_STR: // Use block scalar style if the string contains newline(s). if (strchr(node->str, '\n')) { ret = yaml_emitter_append(emitter, "|-\n"); if (ret) return ret; char *copy = strdup(node->str); char *line = strtok(copy, "\n"); while (line) { snprintf(line_buf, sizeof(line_buf), "%s%s\n", indent_str, line); ret = yaml_emitter_append(emitter, line_buf); if (ret) { free(copy); return ret; } line = strtok(NULL, "\n"); } free(copy); } else { snprintf(line_buf, sizeof(line_buf), "%s%s\n", indent_str, node->str); ret = yaml_emitter_append(emitter, line_buf); if (ret) return ret; } break; case YAML_SEQ: for (size_t i = 0; i < node->seq_size; i++) { snprintf(line_buf, sizeof(line_buf), "%s- ", indent_str); ret = yaml_emitter_append(emitter, line_buf); if (ret) return ret; if (node->seq[i]->type == YAML_STR) { ret = yaml_emitter_append(emitter, node->seq[i]->str); ret = yaml_emitter_append(emitter, "\n"); if (ret) return ret; } else { ret = yaml_emitter_append(emitter, "\n"); if (ret) return ret; ret = yaml_emit_node(emitter, node->seq[i], indent + 2); if (ret) return ret; } } break; case YAML_MAP: for (size_t i = 0; i < node->map_size; i++) { snprintf(line_buf, sizeof(line_buf), "%s%s: ", indent_str, node->keys[i]); ret = yaml_emitter_append(emitter, line_buf); if (ret) return ret; if (node->vals[i]->type == YAML_STR) { ret = yaml_emitter_append(emitter, node->vals[i]->str); ret = yaml_emitter_append(emitter, "\n"); if (ret) return ret; } else { ret = yaml_emitter_append(emitter, "\n"); if (ret) return ret; ret = yaml_emit_node(emitter, node->vals[i], indent + 2); if (ret) return ret; } } break; default: break; } return 0; } int yaml_emit(YAMLEmitter *emitter, const YAMLDoc *doc) { if (!emitter || !doc || !doc->root) return -1; return yaml_emit_node(emitter, doc->root, 0); } int yaml_emit_to_file(YAMLEmitter *emitter, const char *filename) { if (!emitter || !filename) return -1; FILE *file = fopen(filename, "w"); if (!file) return -1; fwrite(emitter->buffer, 1, emitter->size, file); fclose(file); return 0; } char* yaml_emit_to_str(YAMLEmitter *emitter) { if (!emitter) return NULL; return strdup(emitter->buffer); } void yaml_emitter_destroy(YAMLEmitter *emitter) { if (!emitter) return; free(emitter->buffer); free(emitter); } #endif // YAML_IMPLEMENTATION #endif // YAML_H