yamltree.c (10049B)
1 /* 2 3 Copyright (C) 2016, David "Davee" Morgan 4 5 Permission is hereby granted, free of charge, to any person obtaining a 6 copy of this software and associated documentation files (the "Software"), 7 to deal in the Software without restriction, including without limitation 8 the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 and/or sell copies of the Software, and to permit persons to whom the 10 Software is furnished to do so, subject to the following conditions: 11 12 The above copyright notice and this permission notice shall be included in 13 all copies or substantial portions of the Software. 14 15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 DEALINGS IN THE SOFTWARE. 22 23 */ 24 25 #include "yamltree.h" 26 #include <yaml.h> 27 #include <stdio.h> 28 #include <string.h> 29 #include <assert.h> 30 31 typedef struct 32 { 33 yaml_parser_t parser; 34 yaml_event_t event; 35 yaml_event_t next_event; 36 yaml_error *error; 37 } parser_context; 38 39 static yaml_node *process_node(parser_context *ctx); 40 41 char *format_error_string(parser_context *ctx) 42 { 43 assert(ctx->parser.error != YAML_NO_ERROR); 44 char *ptr; 45 46 switch (ctx->parser.error) 47 { 48 case YAML_MEMORY_ERROR: 49 asprintf(&ptr, "libyaml: failed to allocate or reallocate a block of memory."); 50 break; 51 52 case YAML_READER_ERROR: 53 if (ctx->parser.problem_value != -1) 54 asprintf(&ptr, "libyaml: reader error: '%s:#%X' at line %d, column %d.", ctx->parser.problem, ctx->parser.problem_value, ctx->parser.problem_mark.line, ctx->parser.problem_mark.column); 55 else 56 asprintf(&ptr, "libyaml: reader error: '%s' at line %d, column %d.", ctx->parser.problem, ctx->parser.problem_mark.line, ctx->parser.problem_mark.column); 57 break; 58 59 case YAML_SCANNER_ERROR: 60 asprintf(&ptr, "libyaml: scanner error: '%s' at line %d, column %d.", ctx->parser.problem, ctx->parser.problem_mark.line, ctx->parser.problem_mark.column); 61 break; 62 63 case YAML_PARSER_ERROR: 64 asprintf(&ptr, "libyaml: parser error: '%s' at line %d, column %d.", ctx->parser.problem, ctx->parser.problem_mark.line, ctx->parser.problem_mark.column); 65 break; 66 67 case YAML_COMPOSER_ERROR: 68 asprintf(&ptr, "libyaml: composer error: '%s' at line %d, column %d.", ctx->parser.problem, ctx->parser.problem_mark.line, ctx->parser.problem_mark.column); 69 break; 70 71 case YAML_WRITER_ERROR: 72 asprintf(&ptr, "libyaml: writer error: '%s' at line %d, column %d.", ctx->parser.problem, ctx->parser.problem_mark.line, ctx->parser.problem_mark.column); 73 break; 74 75 case YAML_EMITTER_ERROR: 76 asprintf(&ptr, "libyaml: emitter error: '%s' at line %d, column %d.", ctx->parser.problem, ctx->parser.problem_mark.line, ctx->parser.problem_mark.column); 77 break; 78 79 default: 80 asprintf(&ptr, "unknown error code (%i) from libyaml. possible memory corruption?", ctx->parser.error); 81 break; 82 } 83 84 assert(ptr); 85 return ptr; 86 } 87 88 static const char *event_to_string(yaml_event_type_t event) 89 { 90 switch (event) 91 { 92 case YAML_NO_EVENT: 93 return "YAML_NO_EVENT"; 94 case YAML_STREAM_START_EVENT: 95 return "YAML_STREAM_START_EVENT"; 96 case YAML_STREAM_END_EVENT: 97 return "YAML_STREAM_END_EVENT"; 98 case YAML_DOCUMENT_START_EVENT: 99 return "YAML_DOCUMENT_START_EVENT"; 100 case YAML_DOCUMENT_END_EVENT: 101 return "YAML_DOCUMENT_END_EVENT"; 102 case YAML_ALIAS_EVENT: 103 return "YAML_ALIAS_EVENT"; 104 case YAML_SCALAR_EVENT: 105 return "YAML_SCALAR_EVENT"; 106 case YAML_SEQUENCE_START_EVENT: 107 return "YAML_SEQUENCE_START_EVENT"; 108 case YAML_SEQUENCE_END_EVENT: 109 return "YAML_SEQUENCE_END_EVENT"; 110 case YAML_MAPPING_START_EVENT: 111 return "YAML_MAPPING_START_EVENT"; 112 case YAML_MAPPING_END_EVENT: 113 return "YAML_MAPPING_END_EVENT"; 114 default: 115 return "UNKNOWN"; 116 } 117 118 assert(0); 119 return "UNKNOWN"; 120 } 121 122 static int is_error_set(parser_context *ctx) 123 { 124 return ctx->error->problem != NULL; 125 } 126 127 static int set_error(parser_context *ctx) 128 { 129 if (!is_error_set(ctx) && ctx->parser.error) 130 { 131 ctx->error->problem = format_error_string(ctx); 132 return 1; 133 } 134 135 return 0; 136 } 137 138 static int process_event(parser_context *ctx) 139 { 140 memcpy(&ctx->event, &ctx->next_event, sizeof(yaml_event_t)); 141 yaml_parser_parse(&ctx->parser, &ctx->next_event); 142 return set_error(ctx) ? (-1) : (0); 143 } 144 145 static yaml_event_type_t peek_next_event(parser_context *ctx) 146 { 147 // just peek the next event 148 return ctx->next_event.type; 149 } 150 151 static yaml_event_type_t next_event(parser_context *ctx) 152 { 153 // process event and return it 154 if (process_event(ctx) < 0) 155 return YAML_NO_EVENT; 156 157 return ctx->event.type; 158 } 159 160 static yaml_node *process_scalar(parser_context *ctx) 161 { 162 yaml_node *scalar = malloc(sizeof(yaml_node)); 163 scalar->type = NODE_SCALAR; 164 scalar->position.line = ctx->event.start_mark.line; 165 scalar->position.column = ctx->event.start_mark.column; 166 scalar->data.scalar.value = strdup(ctx->event.data.scalar.value); 167 scalar->data.scalar.len = ctx->event.data.scalar.length; 168 return scalar; 169 } 170 171 static yaml_node *process_sequence(parser_context *ctx) 172 { 173 yaml_node *sequence = (yaml_node *)malloc(sizeof(yaml_node)); 174 if (!sequence) 175 return NULL; 176 yaml_sequence *seq = &sequence->data.sequence; 177 178 sequence->type = NODE_SEQUENCE; 179 sequence->position.line = ctx->event.start_mark.line; 180 sequence->position.column = ctx->event.start_mark.column; 181 182 // zero out values 183 seq->count = 0; 184 seq->nodes = NULL; 185 186 while (peek_next_event(ctx) != YAML_SEQUENCE_END_EVENT) 187 { 188 yaml_node *node = process_node(ctx); 189 190 if (!node) 191 { 192 free(sequence); 193 return NULL; 194 } 195 196 // extend space 197 seq->nodes = realloc(seq->nodes, (seq->count+1)*sizeof(yaml_node*)); 198 seq->nodes[seq->count++] = node; 199 } 200 201 if (process_event(ctx) < 0) 202 { 203 free(sequence); 204 return NULL; 205 } 206 207 return sequence; 208 } 209 210 static yaml_node *process_mapping(parser_context *ctx) 211 { 212 yaml_node *mapping = (yaml_node *)malloc(sizeof(yaml_node)); 213 if (!mapping) 214 return NULL; 215 yaml_mapping *map = &mapping->data.mapping; 216 217 mapping->type = NODE_MAPPING; 218 mapping->position.line = ctx->event.start_mark.line; 219 mapping->position.column = ctx->event.start_mark.column; 220 221 // zero out values 222 map->count = 0; 223 map->pairs = NULL; 224 225 while (peek_next_event(ctx) != YAML_MAPPING_END_EVENT) 226 { 227 yaml_node_pair *pair = malloc(sizeof(yaml_node_pair)); 228 229 pair->lhs = process_node(ctx); 230 231 if (!pair->lhs) 232 { 233 free(mapping); 234 return NULL; 235 } 236 237 pair->rhs = process_node(ctx); 238 239 if (!pair->rhs) 240 { 241 free(mapping); 242 return NULL; 243 } 244 245 // extend space as needed 246 map->pairs = realloc(map->pairs, (map->count+1)*sizeof(yaml_node_pair*)); 247 map->pairs[map->count++] = pair; 248 } 249 250 if (process_event(ctx) < 0) 251 { 252 free(mapping); 253 return NULL; 254 } 255 256 return mapping; 257 } 258 259 static yaml_node *process_node(parser_context *ctx) 260 { 261 // we expect either: alias, scalar, sequence or mapping 262 switch (next_event(ctx)) 263 { 264 case YAML_ALIAS_EVENT: 265 // TODO: we dont support aliases for now 266 asprintf(&ctx->error->problem, "yamltree: there is no support for aliases implemented."); 267 return NULL; 268 269 case YAML_SCALAR_EVENT: 270 return process_scalar(ctx); 271 272 case YAML_SEQUENCE_START_EVENT: 273 return process_sequence(ctx); 274 275 case YAML_MAPPING_START_EVENT: 276 return process_mapping(ctx); 277 278 default: 279 // probably an error 280 break; 281 } 282 283 return NULL; 284 } 285 286 static yaml_document *process_document(parser_context *ctx) 287 { 288 // look for document start event. 289 if (next_event(ctx) != YAML_DOCUMENT_START_EVENT) 290 { 291 if (!is_error_set(ctx)) 292 { 293 asprintf(&ctx->error->problem, "yamltree: expecting YAML_DOCUMENT_START_EVENT got '%s'.", event_to_string(ctx->event.type)); 294 } 295 296 return NULL; 297 } 298 299 // a document is basically a fancy name for a root node 300 yaml_document *doc = process_node(ctx); 301 302 if (!doc) 303 return NULL; 304 305 // get end of document 306 if (next_event(ctx) != YAML_DOCUMENT_END_EVENT) 307 { 308 if (!is_error_set(ctx)) 309 { 310 asprintf(&ctx->error->problem, "yamltree: expecting YAML_DOCUMENT_END_EVENT got '%s'.", event_to_string(ctx->event.type)); 311 } 312 313 return NULL; 314 } 315 316 return doc; 317 } 318 319 yaml_tree *parse_yaml_stream(FILE *input, yaml_error *error) 320 { 321 parser_context ctx; 322 ctx.error = error; 323 yaml_parser_initialize(&ctx.parser); 324 yaml_parser_set_input_file(&ctx.parser, input); 325 326 if (process_event(&ctx) < 0) 327 goto error; 328 329 if (ctx.next_event.type != YAML_STREAM_START_EVENT) 330 { 331 asprintf(&ctx.error->problem, "yamltree: expecting YAML_STREAM_START_EVENT got '%s'.", event_to_string(ctx.next_event.type)); 332 goto error; 333 } 334 335 yaml_tree *stream = malloc(sizeof(yaml_tree)); 336 337 stream->count = 0; 338 stream->docs = NULL; 339 340 while (next_event(&ctx) != YAML_STREAM_END_EVENT) 341 { 342 // check error 343 if (is_error_set(&ctx)) 344 { 345 goto error; 346 } 347 348 yaml_document *document = process_document(&ctx); 349 350 if (!document) 351 { 352 // TODO: clean up structure 353 goto error; 354 } 355 356 stream->docs = realloc(stream->docs, (stream->count+1)*sizeof(yaml_tree)); 357 stream->docs[stream->count++] = document; 358 } 359 360 yaml_parser_delete(&ctx.parser); 361 return stream; 362 363 error: 364 yaml_parser_delete(&ctx.parser); 365 return NULL; 366 } 367 368 void free_yaml_tree(yaml_tree *tree) 369 { 370 // TODO: implement 371 } 372 373 const char *node_type_str(yaml_node *node) 374 { 375 switch (node->type) 376 { 377 case NODE_MAPPING: 378 return "mapping"; 379 case NODE_SEQUENCE: 380 return "sequence"; 381 case NODE_SCALAR: 382 return "scalar"; 383 default: 384 assert(0); 385 break; 386 } 387 388 assert(0); 389 return NULL; 390 }