solparser_entity.c (15194B)
1 /* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) 2 * Copyright (C) 2016-2018 CNRS 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 16 17 #include "solparser_c.h" 18 19 /******************************************************************************* 20 * Helper functions 21 ******************************************************************************/ 22 static res_T 23 entity_register_name 24 (struct solparser* parser, 25 const yaml_node_t* entity, 26 struct htable_str2sols* htable, 27 const size_t isolent) 28 { 29 struct solparser_entity* solent; 30 size_t* pisolent; 31 res_T res = RES_OK; 32 ASSERT(parser && htable); 33 ASSERT(isolent < darray_entity_size_get(&parser->entities)); 34 35 solent = darray_entity_data_get(&parser->entities) + isolent; 36 37 pisolent = htable_str2sols_find(htable, &solent->name); 38 if(pisolent) { 39 log_err(parser, entity, 40 "an entity with the name `%s' is already defined in the current context.\n", 41 str_cget(&solent->name)); 42 return RES_BAD_ARG; 43 } 44 45 res = htable_str2sols_set(htable, &solent->name, &isolent); 46 if(res != RES_OK) { 47 log_err(parser, entity, "could not register the entity.\n"); 48 return res; 49 } 50 return RES_OK; 51 } 52 53 static res_T 54 anchor_register_name 55 (struct solparser* parser, 56 const yaml_node_t* anchor, 57 struct htable_str2sols* htable, 58 const size_t isolanchor) 59 { 60 struct solparser_anchor* solanchor; 61 size_t* pisolanchor; 62 res_T res = RES_OK; 63 ASSERT(parser && htable); 64 ASSERT(isolanchor < darray_anchor_size_get(&parser->anchors)); 65 66 solanchor = darray_anchor_data_get(&parser->anchors) + isolanchor; 67 68 pisolanchor = htable_str2sols_find(htable, &solanchor->name); 69 if(pisolanchor) { 70 log_err(parser, anchor, 71 "an anchor with the name `%s' is already defined in the cunrrent context.\n", 72 str_cget(&solanchor->name)); 73 return RES_BAD_ARG; 74 } 75 76 res = htable_str2sols_set(htable, &solanchor->name, &isolanchor); 77 if(res != RES_OK) { 78 log_err(parser, anchor, "could not register the anchor.\n"); 79 return res; 80 } 81 return RES_OK; 82 } 83 84 static res_T 85 parse_identifier_string 86 (struct solparser* parser, 87 yaml_node_t* name, 88 struct str* str) 89 { 90 res_T res = RES_OK; 91 ASSERT(parser && name && str); 92 93 res = parse_string(parser, name, str); 94 if(res != RES_OK) goto error; 95 96 if(strchr(str_cget(str), '.')) { 97 log_err(parser, name, "invalid character `.' in the name `%s'.\n", 98 str_cget(str)); 99 res = RES_BAD_ARG; 100 goto error; 101 } 102 if(strchr(str_cget(str), ' ') || strchr(str_cget(str), '\t')) { 103 log_err(parser, name, "invalid space or tabulation in the name `%s'.\n", 104 str_cget(str)); 105 res = RES_BAD_ARG; 106 goto error; 107 } 108 109 exit: 110 return res; 111 error: 112 goto exit; 113 } 114 115 static res_T 116 parse_anchor 117 (struct solparser* parser, 118 yaml_document_t* doc, 119 const yaml_node_t* anchor, 120 struct htable_str2sols* htable, 121 struct solparser_anchor_id* out_isolanchor) 122 { 123 enum { NAME, POSITION }; 124 struct solparser_anchor* solanchor = NULL; 125 size_t isolanchor = SIZE_MAX; 126 intptr_t i, n; 127 int mask = 0; /* Register the parsed attributes */ 128 res_T res = RES_OK; 129 ASSERT(parser && anchor && out_isolanchor); 130 131 if(anchor->type != YAML_MAPPING_NODE) { 132 log_err(parser, anchor, "expect an anchor definition.\n"); 133 res = RES_BAD_ARG; 134 goto error; 135 } 136 137 /* Allocate the anchor */ 138 isolanchor = darray_anchor_size_get(&parser->anchors); 139 res = darray_anchor_resize(&parser->anchors, isolanchor + 1); 140 if(res != RES_OK) { 141 log_err(parser, anchor, "could not allocate the anchor.\n"); 142 goto error; 143 } 144 solanchor = darray_anchor_data_get(&parser->anchors) + isolanchor; 145 146 n = anchor->data.mapping.pairs.top - anchor->data.mapping.pairs.start; 147 FOR_EACH(i, 0, n) { 148 yaml_node_t* key; 149 yaml_node_t* val; 150 151 key = yaml_document_get_node(doc, anchor->data.mapping.pairs.start[i].key); 152 val = yaml_document_get_node(doc, anchor->data.mapping.pairs.start[i].value); 153 if(key->type != YAML_SCALAR_NODE) { 154 log_err(parser, key, "expect an anchor attribute.\n"); 155 res = RES_BAD_ARG; 156 goto error; 157 } 158 159 #define SETUP_MASK(Flag, Name) { \ 160 if(mask & BIT(Flag)) { \ 161 log_err(parser, key, "the anchor "Name" is already defined.\n"); \ 162 res = RES_BAD_ARG; \ 163 goto error; \ 164 } \ 165 mask |= BIT(Flag); \ 166 } (void)0 167 if(!strcmp((char*)key->data.scalar.value, "name")) { 168 SETUP_MASK(NAME, "name"); 169 res = parse_identifier_string(parser, val, &solanchor->name); 170 } else if(!strcmp((char*)key->data.scalar.value, "position")) { 171 SETUP_MASK(POSITION, "position description"); 172 res = parse_real3(parser, doc, val, -DBL_MAX, DBL_MAX, solanchor->position); 173 } else if(!strcmp((char*) key->data.scalar.value, "hyperboloid_image_focals")) { 174 struct solparser_hyperboloid_focals focals; 175 SETUP_MASK(POSITION, "position description"); 176 res = parse_focals_description(parser, doc, val, &focals); 177 if(res != RES_OK) goto error; 178 d3(solanchor->position, 0, 0, focals.image); 179 } else { 180 log_err(parser, key, "unknown anchor parameter `%s'.\n", 181 key->data.scalar.value); 182 res = RES_BAD_ARG; 183 goto error; 184 } 185 if(res != RES_OK) { 186 log_node(parser, key); 187 goto error; 188 } 189 #undef SETUP_MASK 190 } 191 192 #define CHECK_PARAM(Flag, Name) \ 193 if(!(mask & BIT(Flag))) { \ 194 log_err(parser, anchor, "the anchor "Name" is missing.\n"); \ 195 res = RES_BAD_ARG; \ 196 goto error; \ 197 } (void)0 198 CHECK_PARAM(NAME, "name"); 199 CHECK_PARAM(POSITION, "position description"); 200 #undef CHECK_PARAM 201 202 res = anchor_register_name(parser, anchor, htable, isolanchor); 203 if(res != RES_OK) goto error; 204 205 exit: 206 out_isolanchor->i = isolanchor; 207 return res; 208 error: 209 if(solanchor) { 210 darray_anchor_pop_back(&parser->anchors); 211 isolanchor = SIZE_MAX; 212 } 213 goto exit; 214 } 215 216 static res_T 217 parse_anchors 218 (struct solparser* parser, 219 yaml_document_t* doc, 220 const yaml_node_t* anchors, 221 struct htable_str2sols* htable, 222 struct darray_anchor_id* solanchors) 223 { 224 intptr_t i, n; 225 res_T res = RES_OK; 226 ASSERT(parser && anchors); 227 228 if(anchors->type != YAML_SEQUENCE_NODE) { 229 log_err(parser, anchors, "expect a list of anchors.\n"); 230 res = RES_BAD_ARG; 231 goto error; 232 } 233 234 n = anchors->data.sequence.items.top - anchors->data.sequence.items.start; 235 res = darray_anchor_id_resize(solanchors, (size_t)n); 236 if(res != RES_OK) { 237 log_err(parser, anchors, "could not allocate the anchors list.\n"); 238 goto error; 239 } 240 241 FOR_EACH(i, 0, n) { 242 struct solparser_anchor_id* anchor_id; 243 yaml_node_t* anchor; 244 245 anchor_id = darray_anchor_id_data_get(solanchors)+i; 246 anchor = yaml_document_get_node(doc, anchors->data.sequence.items.start[i]); 247 res = parse_anchor(parser, doc, anchor, htable, anchor_id); 248 if(res != RES_OK) goto error; 249 } 250 exit: 251 return res; 252 error: 253 goto exit; 254 } 255 256 static res_T 257 parse_children 258 (struct solparser* parser, 259 yaml_document_t* doc, 260 const yaml_node_t* children, 261 struct htable_str2sols* htable, 262 struct darray_child_id* entities) 263 { 264 intptr_t i, n; 265 res_T res = RES_OK; 266 ASSERT(parser && children && htable && entities); 267 268 if(children->type != YAML_SEQUENCE_NODE) { 269 log_err(parser, children, "expect a list of entities.\n"); 270 res = RES_BAD_ARG; 271 goto error; 272 } 273 274 n = children->data.sequence.items.top - children->data.sequence.items.start; 275 res = darray_child_id_resize(entities, (size_t)n); 276 if(res != RES_OK) { 277 log_err(parser, children, "could not allocate the children list.\n"); 278 goto error; 279 } 280 281 FOR_EACH(i, 0, n) { 282 struct solparser_entity_id* entity_id = darray_child_id_data_get(entities) + i; 283 yaml_node_t* child; 284 285 child = yaml_document_get_node(doc, children->data.sequence.items.start[i]); 286 res = parse_entity(parser, doc, child, htable, entity_id); 287 if(res != RES_OK) goto error; 288 } 289 290 exit: 291 return res; 292 error: 293 darray_child_id_clear(entities); 294 goto exit; 295 } 296 297 298 /******************************************************************************* 299 * Local function 300 ******************************************************************************/ 301 res_T 302 parse_entity 303 (struct solparser* parser, 304 yaml_document_t* doc, 305 yaml_node_t* entity, 306 struct htable_str2sols* htable, 307 struct solparser_entity_id* out_isolent) 308 { 309 enum { ANCHORS, CHILDREN, DATA, NAME, TRANSFORM, PRIMARY }; 310 struct solparser_entity solent; 311 struct solparser_entity* psolent; 312 size_t isolent = SIZE_MAX; 313 intptr_t i, n; 314 int mask = 0; /* Register the parsed attributes */ 315 res_T res = RES_OK; 316 ASSERT(doc && entity && htable && out_isolent); 317 318 solparser_entity_init(parser->allocator, &solent); 319 320 if(entity->type != YAML_MAPPING_NODE) { 321 log_err(parser, entity, "expect an entity definition.\n"); 322 res = RES_BAD_ARG; 323 goto error; 324 } 325 326 /* Allocate the entity but *DO NOT* retrieve a pointer onto it since the 327 * allocation of its children may update its memory location. Use the "on 328 * stack" entity `solent' instead. */ 329 isolent = darray_entity_size_get(&parser->entities); 330 res = darray_entity_resize(&parser->entities, isolent + 1); 331 if(res != RES_OK) { 332 log_err(parser, entity, "could not allocate the entity.\n"); 333 goto error; 334 } 335 336 n = entity->data.mapping.pairs.top - entity->data.mapping.pairs.start; 337 FOR_EACH(i, 0, n) { 338 yaml_node_t* key; 339 yaml_node_t* val; 340 341 key = yaml_document_get_node(doc, entity->data.mapping.pairs.start[i].key); 342 val = yaml_document_get_node(doc, entity->data.mapping.pairs.start[i].value); 343 if(key->type != YAML_SCALAR_NODE) { 344 log_err(parser, key, "expect an entity attribute.\n"); 345 res = RES_BAD_ARG; 346 goto error; 347 } 348 349 #define SETUP_MASK(Flag, Name) { \ 350 if(mask & BIT(Flag)) { \ 351 log_err(parser, key, \ 352 "the entity "Name" is already defined.\n"); \ 353 res = RES_BAD_ARG; \ 354 goto error; \ 355 } \ 356 mask |= BIT(Flag); \ 357 } (void)0 358 if(!strcmp((char*)key->data.scalar.value, "anchors")) { 359 SETUP_MASK(ANCHORS, "anchors"); 360 res = parse_anchors 361 (parser, doc, val, &solent.str2anchors, &solent.anchors); 362 } else if(!strcmp((char*)key->data.scalar.value, "children")) { 363 SETUP_MASK(CHILDREN, "children"); 364 res = parse_children 365 (parser, doc, val, &solent.str2children, &solent.children); 366 } else if(!strcmp((char*)key->data.scalar.value, "geometry")) { 367 SETUP_MASK(DATA, "data"); 368 solent.type = SOLPARSER_ENTITY_GEOMETRY; 369 res = parse_geometry(parser, doc, val, &solent.data.geometry); 370 } else if(!strcmp((char*)key->data.scalar.value, "name")) { 371 SETUP_MASK(NAME, "name"); 372 res = parse_identifier_string(parser, val, &solent.name); 373 if(!strcmp(str_get(&solent.name), "self")) { 374 /* Self is a reserved keyword */ 375 log_err(parser, key, "Reserved keywords cannot be used as names: %s.\n", 376 str_get(&solent.name)); 377 res = RES_BAD_ARG; 378 goto error; 379 } 380 } else if(!strcmp((char*)key->data.scalar.value, "x_pivot")) { 381 SETUP_MASK(DATA, "data"); 382 solent.type = SOLPARSER_ENTITY_X_PIVOT; 383 res = parse_x_pivot(parser, doc, val, &solent.data.x_pivot); 384 } else if(!strcmp((char*) key->data.scalar.value, "zx_pivot")) { 385 SETUP_MASK(DATA, "data"); 386 solent.type = SOLPARSER_ENTITY_ZX_PIVOT; 387 res = parse_zx_pivot(parser, doc, val, &solent.data.zx_pivot); 388 } else if(!strcmp((char*)key->data.scalar.value, "transform")) { 389 SETUP_MASK(TRANSFORM, "transform"); 390 res = parse_transform 391 (parser, doc, val, solent.translation, solent.rotation); 392 } else if(!strcmp((char*) key->data.scalar.value, "primary")) { 393 long tmp; 394 SETUP_MASK(PRIMARY, "primary"); 395 /* FIXME: add NONE/ FRONT / BACK / FRONT_AND_BACK qualifier 396 * to avoid a misunderstanding about shadows results */ 397 res = parse_integer(parser, val, 0, 1, &tmp); 398 solent.primary = (int)tmp; 399 } else { 400 log_err(parser, key, "unknown entity parameter `%s'.\n", 401 key->data.scalar.value); 402 res = RES_BAD_ARG; 403 goto error; 404 } 405 if(res != RES_OK) { 406 log_node(parser, key); 407 goto error; 408 } 409 #undef SETUP_MASK 410 } 411 412 if(!(mask & BIT(DATA))) { 413 solent.type = SOLPARSER_ENTITY_EMPTY; 414 } 415 416 #define CHECK_PARAM(Flag, Name) \ 417 if(!(mask & BIT(Flag))) { \ 418 log_err(parser, entity, "the entity "Name" parameter is missing.\n"); \ 419 res = RES_BAD_ARG; \ 420 goto error; \ 421 } (void)0 422 CHECK_PARAM(NAME, "name"); 423 if(solent.type == SOLPARSER_ENTITY_GEOMETRY) { 424 CHECK_PARAM(PRIMARY, "primary"); 425 } else if(mask & BIT(PRIMARY)) { 426 log_err(parser, entity, 427 "the entity primary parameter is invalid in this context.\n"); 428 res = RES_BAD_ARG; 429 goto error; 430 } 431 #undef CHECK_PARAM 432 433 psolent = darray_entity_data_get(&parser->entities) + isolent; 434 res = solparser_entity_copy_and_clear(psolent, &solent); 435 if(res != RES_OK) { 436 log_err(parser, entity, 437 "could not copy the loaded entity into the parser data structures.\n"); 438 goto error; 439 } 440 res = entity_register_name(parser, entity, htable, isolent); 441 if(res != RES_OK) goto error; 442 443 exit: 444 solparser_entity_release(&solent); 445 out_isolent->i = isolent; 446 return res; 447 error: 448 if(isolent != SIZE_MAX) { 449 htable_str2sols_erase(htable, &solent.name); 450 darray_entity_pop_back(&parser->entities); 451 isolent = SIZE_MAX; 452 } 453 goto exit; 454 } 455 456