| 251 | } |
| 252 | |
| 253 | cbm_yaml_node_t *cbm_yaml_parse(const char *text, int len) { |
| 254 | if (!text || len <= 0) { |
| 255 | return node_new(YAML_MAP); |
| 256 | } |
| 257 | |
| 258 | cbm_yaml_node_t *root = node_new(YAML_MAP); |
| 259 | if (!root) { |
| 260 | return NULL; |
| 261 | } |
| 262 | |
| 263 | /* Stack for tracking parent context */ |
| 264 | stack_entry_t stack[CBM_SZ_32]; |
| 265 | int stack_depth = 0; |
| 266 | stack[0] = (stack_entry_t){.node = root, .indent = YAML_ROOT_INDENT}; |
| 267 | stack_depth = SKIP_ONE; |
| 268 | |
| 269 | const char *p = text; |
| 270 | const char *end = text + len; |
| 271 | |
| 272 | while (p < end) { |
| 273 | /* Find end of line */ |
| 274 | const char *eol = memchr(p, '\n', (size_t)(end - p)); |
| 275 | if (!eol) { |
| 276 | eol = end; |
| 277 | } |
| 278 | int line_len = (int)(eol - p); |
| 279 | |
| 280 | /* Skip empty lines and comments */ |
| 281 | int indent = leading_spaces(p); |
| 282 | const char *content = p + indent; |
| 283 | int content_len = line_len - indent; |
| 284 | |
| 285 | /* Strip \r */ |
| 286 | if (content_len > 0 && content[content_len - SKIP_ONE] == '\r') { |
| 287 | content_len--; |
| 288 | } |
| 289 | |
| 290 | if (content_len == 0 || content[0] == '#') { |
| 291 | p = (eol < end) ? eol + SKIP_ONE : end; |
| 292 | continue; |
| 293 | } |
| 294 | |
| 295 | /* Pop stack to find parent at correct indentation */ |
| 296 | while (stack_depth > SKIP_ONE && stack[stack_depth - SKIP_ONE].indent >= indent) { |
| 297 | stack_depth--; |
| 298 | } |
| 299 | cbm_yaml_node_t *parent = stack[stack_depth - SKIP_ONE].node; |
| 300 | |
| 301 | if (content[0] == '-' && content_len >= PAIR_LEN && content[SKIP_ONE] == ' ') { |
| 302 | parse_list_item(content, content_len, parent); |
| 303 | } else { |
| 304 | parse_key_line(content, content_len, indent, parent, stack, &stack_depth, eol, end); |
| 305 | } |
| 306 | |
| 307 | p = (eol < end) ? eol + SKIP_ONE : end; |
| 308 | } |
| 309 | |
| 310 | return root; |
no test coverage detected