solstice

Compute collected power and efficiencies of a solar plant
git clone git://git.meso-star.com/solstice.git
Log | Files | Refs | README | LICENSE

solparser.c (34899B)


      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 #define _POSIX_C_SOURCE 200112L /* nextafter support */
     18 
     19 #include "solparser_c.h"
     20 
     21 #include <rsys/cstr.h>
     22 
     23 #include <stdarg.h>
     24 #include <stdio.h>
     25 #include <string.h>
     26 #include <yaml.h>
     27 
     28 /*******************************************************************************
     29  * Helper functions
     30  ******************************************************************************/
     31 static res_T
     32 flush_deferred_target_aliases
     33   (struct solparser* parser,
     34    const yaml_node_t* node,
     35    const struct solparser_entity_id entity_id)
     36 {
     37   const struct solparser_entity* entity;
     38   struct darray_char alias;
     39   size_t i, n;
     40   size_t prefix_len;
     41   res_T res = RES_OK;
     42   ASSERT(parser);
     43 
     44   darray_char_init(parser->allocator, &alias);
     45 
     46   if(!darray_tgtalias_size_get(&parser->tgtaliases))
     47     goto exit; /* No deferred target alias */
     48 
     49   /* Retrieve the entity referenced by the 'self' keyword */
     50   entity = solparser_get_entity(parser, entity_id);
     51 
     52   /* Copy the entity name */
     53   prefix_len = strlen(str_cget(&entity->name));
     54   res = darray_char_resize(&alias, prefix_len);
     55   if(res != RES_OK) {
     56     log_err(parser, node,
     57       "could not reserve the prefix of the targeted alias name.\n");
     58     goto error;
     59   }
     60   strncpy(darray_char_data_get(&alias), str_cget(&entity->name), prefix_len);
     61 
     62   n = darray_tgtalias_size_get(&parser->tgtaliases);
     63   FOR_EACH(i, 0, n) {
     64     const struct solparser_anchor* anchor;
     65     struct solparser_x_pivot* x_pivot;
     66     const struct target_alias* tgt;
     67     size_t ianchor;
     68     size_t len;
     69 
     70     tgt = darray_tgtalias_cdata_get(&parser->tgtaliases) + i;
     71     ASSERT(!strncmp((char*)tgt->alias->data.scalar.value, "self.", 5));
     72 
     73     /* Copy the anchor alias */
     74     len = strlen((char*)tgt->alias->data.scalar.value)
     75       - 4/*strlen(self)*/ + 1/*NULL char*/;
     76     res = darray_char_resize(&alias, prefix_len + len);
     77     if(res != RES_OK) {
     78       log_err(parser, node,
     79         "could not reserve the suffix of the targeted alias name.\n");
     80       goto error;
     81     }
     82     strcpy(darray_char_data_get(&alias) + prefix_len,
     83       (char*)tgt->alias->data.scalar.value+4);
     84 
     85     /* Retrieve the anchor */
     86     anchor = solparser_find_anchor(parser, darray_char_cdata_get(&alias));
     87     if(!anchor) {
     88       log_err(parser, tgt->alias, "undefined anchor `%s'.\n",
     89         tgt->alias->data.scalar.value);
     90       res = RES_BAD_ARG;
     91       goto error;
     92     }
     93 
     94     /* Define the targeted anchor of the pivot */
     95     x_pivot = darray_x_pivot_data_get(&parser->x_pivots) + tgt->pivot.i;
     96     ASSERT(x_pivot->target.type == SOLPARSER_TARGET_ANCHOR);
     97     ianchor = (size_t)(anchor - darray_anchor_cdata_get(&parser->anchors));
     98     ASSERT(ianchor < darray_anchor_size_get(&parser->anchors));
     99     x_pivot->target.data.anchor.i = ianchor;
    100   }
    101 
    102   darray_tgtalias_clear(&parser->tgtaliases);
    103 
    104 exit:
    105   darray_char_release(&alias);
    106   return res;
    107 error:
    108   goto exit;
    109 }
    110 
    111 static res_T
    112 parse_item
    113   (struct solparser* parser,
    114    yaml_document_t* doc,
    115    const yaml_node_t* item)
    116 {
    117   /* Temporary dummy variables */
    118   struct solparser_entity_id entity;
    119   struct solparser_geometry_id geometry;
    120   struct solparser_material_double_sided_id mtl2;
    121   struct solparser_medium_id medium;
    122   struct solparser_sun* sun;
    123   struct solparser_atmosphere* atmosphere;
    124 
    125   yaml_node_t* key;
    126   yaml_node_t* val;
    127   intptr_t n;
    128   res_T res = RES_OK;
    129   ASSERT(doc && item);
    130 
    131   if(item->type != YAML_MAPPING_NODE) {
    132     log_err(parser, item, "expect an item definition.\n");
    133     res = RES_BAD_ARG;
    134     goto error;
    135   }
    136 
    137   n = item->data.mapping.pairs.top - item->data.mapping.pairs.start;
    138   if(n != 1) {
    139     log_err(parser, item,
    140       "expect only one \"key:value\" pair while %li are provided.\n", n);
    141     res = RES_BAD_ARG;
    142     goto error;
    143   }
    144 
    145   key = yaml_document_get_node(doc, item->data.mapping.pairs.start[0].key);
    146   val = yaml_document_get_node(doc, item->data.mapping.pairs.start[0].value);
    147   if(key->type != YAML_SCALAR_NODE) {
    148     log_err(parser, key, "expecting an item name.\n");
    149     res = RES_BAD_ARG;
    150     goto error;
    151   }
    152 
    153   /* The parsing of the templates/spectra is deferred to their explicit use */
    154   if(!strcmp((char*)key->data.scalar.value, "material")) {
    155     res = parse_material(parser, doc, val, &mtl2);
    156   } else if(!strcmp((char*)key->data.scalar.value, "medium")) {
    157     res = parse_medium(parser, doc, val, &medium);
    158   } else if(!strcmp((char*)key->data.scalar.value, "entity")) {
    159     res = parse_entity(parser, doc, val, &parser->str2entities, &entity);
    160     if(res == RES_OK) {
    161       res = flush_deferred_target_aliases(parser, item, entity);
    162     }
    163   } else if(!strcmp((char*)key->data.scalar.value, "template")) { /* Deferred */
    164   } else if(!strcmp((char*)key->data.scalar.value, "geometry")) {
    165     res = parse_geometry(parser, doc, val, &geometry);
    166   } else if(!strcmp((char*)key->data.scalar.value, "sun")) {
    167     res = parse_sun(parser, doc, val, &sun);
    168   } else if (!strcmp((char*)key->data.scalar.value, "atmosphere")) {
    169     res = parse_atmosphere(parser, doc, val, &atmosphere);
    170   } else if(!strcmp((char*)key->data.scalar.value, "spectrum")) { /* Deferred */
    171   } else {
    172     log_err(parser, key, "unknown item `%s'.\n", key->data.scalar.value);
    173     res = RES_BAD_ARG;
    174     goto error;
    175   }
    176   if(res != RES_OK) {
    177     log_node(parser, key);
    178     goto error;
    179   }
    180 
    181 exit:
    182   return res;
    183 error:
    184   goto exit;
    185 }
    186 
    187 /* Clean up loaded data */
    188 static INLINE void
    189 parser_clear(struct solparser* parser)
    190 {
    191   ASSERT(parser);
    192 
    193   /* Materials */
    194   htable_yaml2sols_clear(&parser->yaml2mtls);
    195   darray_image_clear(&parser->images);
    196   darray_material_clear(&parser->mtls);
    197   darray_material2_clear(&parser->mtls2);
    198   darray_dielectric_clear(&parser->dielectrics);
    199   darray_matte_clear(&parser->mattes);
    200   darray_mirror_clear(&parser->mirrors);
    201   darray_thin_dielectric_clear(&parser->thin_dielectrics);
    202 
    203   /* Mediums */
    204   htable_yaml2sols_clear(&parser->yaml2mediums);
    205   darray_medium_clear(&parser->mediums);
    206 
    207   /* Deferred targeted anchors */
    208   darray_tgtalias_clear(&parser->tgtaliases);
    209 
    210   /* Shapes */
    211   darray_shape_clear(&parser->shapes);
    212   darray_cuboid_clear(&parser->cuboids);
    213   darray_cylinder_clear(&parser->cylinders);
    214   darray_impgeom_clear(&parser->objs);
    215   darray_paraboloid_clear(&parser->parabols);
    216   darray_paraboloid_clear(&parser->parabolic_cylinders);
    217   darray_hyperboloid_clear(&parser->hyperbols);
    218   darray_hemisphere_clear(&parser->hemispheres);
    219   darray_plane_clear(&parser->planes);
    220   darray_sphere_clear(&parser->spheres);
    221   darray_impgeom_clear(&parser->stls);
    222 
    223   /* Geometries */
    224   htable_yaml2sols_clear(&parser->yaml2geoms);
    225   darray_object_clear(&parser->objects);
    226   darray_geometry_clear(&parser->geometries);
    227 
    228   /* Sun */
    229   solparser_sun_clear(&parser->sun);
    230   parser->sun_key = 0;
    231 
    232   /* Atmosphere */
    233   solparser_atmosphere_clear(&parser->atmosphere);
    234   parser->atmosphere_key = 0;
    235 
    236   /* Entities */
    237   htable_str2sols_clear(&parser->str2entities);
    238   darray_entity_clear(&parser->entities);
    239 
    240   /* Miscellaneous */
    241   darray_anchor_clear(&parser->anchors);
    242   darray_x_pivot_clear(&parser->x_pivots);
    243   darray_zx_pivot_clear(&parser->zx_pivots);
    244   darray_spectrum_clear(&parser->spectra);
    245 }
    246 
    247 static void
    248 parser_release(ref_T* ref)
    249 {
    250   struct solparser* parser;
    251   ASSERT(ref);
    252 
    253   parser = CONTAINER_OF(ref, struct solparser, ref);
    254   if(parser->parser_is_init) yaml_parser_delete(&parser->parser);
    255   str_release(&parser->stream_name);
    256 
    257   /* Materials */
    258   htable_yaml2sols_release(&parser->yaml2mtls);
    259   darray_image_release(&parser->images);
    260   darray_material_release(&parser->mtls);
    261   darray_material2_release(&parser->mtls2);
    262   darray_dielectric_release(&parser->dielectrics);
    263   darray_matte_release(&parser->mattes);
    264   darray_mirror_release(&parser->mirrors);
    265   darray_thin_dielectric_release(&parser->thin_dielectrics);
    266 
    267   /* Mediums */
    268   htable_yaml2sols_release(&parser->yaml2mediums);
    269   darray_medium_release(&parser->mediums);
    270 
    271   /* Deferred targeted anchors */
    272   darray_tgtalias_release(&parser->tgtaliases);
    273 
    274   /* Shapes */
    275   darray_shape_release(&parser->shapes);
    276   darray_cuboid_release(&parser->cuboids);
    277   darray_cylinder_release(&parser->cylinders);
    278   darray_impgeom_release(&parser->objs);
    279   darray_paraboloid_release(&parser->parabols);
    280   darray_paraboloid_release(&parser->parabolic_cylinders);
    281   darray_hyperboloid_release(&parser->hyperbols);
    282   darray_hemisphere_release(&parser->hemispheres);
    283   darray_plane_release(&parser->planes);
    284   darray_sphere_release(&parser->spheres);
    285   darray_impgeom_release(&parser->stls);
    286 
    287   /* Geometries */
    288   htable_yaml2sols_release(&parser->yaml2geoms);
    289   darray_object_release(&parser->objects);
    290   darray_geometry_release(&parser->geometries);
    291 
    292   /* Sun */
    293   solparser_sun_release(&parser->sun);
    294 
    295   /* Atmosphere */
    296   solparser_atmosphere_release(&parser->atmosphere);
    297 
    298   /* Entities */
    299   htable_str2sols_release(&parser->str2entities);
    300   darray_entity_release(&parser->entities);
    301 
    302   /* Miscellaneous */
    303   darray_anchor_release(&parser->anchors);
    304   darray_x_pivot_release(&parser->x_pivots);
    305   darray_zx_pivot_release(&parser->zx_pivots);
    306   darray_spectrum_release(&parser->spectra);
    307 
    308   MEM_RM(parser->allocator, parser);
    309 }
    310 
    311 /*******************************************************************************
    312  * Local functions
    313  ******************************************************************************/
    314 void
    315 log_err
    316   (const struct solparser* parser,
    317    const yaml_node_t* node,
    318    const char* fmt,
    319    ...)
    320 {
    321   va_list vargs_list;
    322   ASSERT(parser && node && fmt);
    323 
    324   fprintf(stderr, "%s:%lu:%lu: ",
    325     str_cget(&parser->stream_name),
    326     (unsigned long)node->start_mark.line+1,
    327     (unsigned long)node->start_mark.column+1);
    328   va_start(vargs_list, fmt);
    329   vfprintf(stderr, fmt, vargs_list);
    330   va_end(vargs_list);
    331 }
    332 
    333 void
    334 log_node(const struct solparser* parser, const yaml_node_t* node)
    335 {
    336   fprintf(stderr, "\tby %s:%lu:%lu\n",
    337     str_cget(&parser->stream_name),
    338     (unsigned long)node->start_mark.line+1,
    339     (unsigned long)node->start_mark.column+1);
    340 }
    341 
    342 res_T
    343 parse_real
    344   (struct solparser* parser,
    345    const yaml_node_t* real,
    346    const double lower_bound,
    347    const double upper_bound,
    348    double* dst)
    349 {
    350   res_T res = RES_OK;
    351   ASSERT(real && dst && lower_bound < upper_bound);
    352 
    353   if(real->type != YAML_SCALAR_NODE
    354   || !strlen((char*)real->data.scalar.value)) {
    355     log_err(parser, real, "expect a floating point number.\n");
    356     res = RES_BAD_ARG;
    357     goto error;
    358   }
    359 
    360   res = cstr_to_double((char*)real->data.scalar.value, dst);
    361   if(res != RES_OK) {
    362     log_err(parser, real, "invalid floating point number `%s'.\n",
    363       real->data.scalar.value);
    364     res = RES_BAD_ARG;
    365     goto error;
    366   }
    367 
    368   if(*dst < lower_bound || *dst > upper_bound) {
    369     double l = nextafter(lower_bound, -DBL_MAX);
    370     double u = nextafter(upper_bound, DBL_MAX);
    371     int l_excluded = (l == (double) (int) l);
    372     int u_excluded = (u == (double) (int) u);
    373     if(l_excluded && u_excluded) {
    374       log_err(parser, real, "%g is not in ]%g, %g[.\n",
    375         *dst, l, u);
    376     } else if(l_excluded) {
    377       log_err(parser, real, "%g is not in ]%g, %g].\n",
    378         *dst, l, upper_bound);
    379     } else if(u_excluded) {
    380       log_err(parser, real, "%g is not in [%g, %g[.\n",
    381         *dst, lower_bound, u);
    382     } else {
    383       log_err(parser, real, "%g is not in [%g, %g].\n",
    384         *dst, lower_bound, upper_bound);
    385     }
    386     res = RES_BAD_ARG;
    387     goto error;
    388   }
    389 
    390 exit:
    391   return res;
    392 error:
    393   goto exit;
    394 }
    395 
    396 res_T
    397 parse_realX
    398   (struct solparser* parser,
    399    yaml_document_t* doc,
    400    const yaml_node_t* realX,
    401    const double lower_bound,
    402    const double upper_bound,
    403    const size_t dim,
    404    double dst[])
    405 {
    406   intptr_t i, n;
    407   res_T res = RES_OK;
    408   ASSERT(doc && realX && dst && dim > 0);
    409 
    410   if(realX->type != YAML_SEQUENCE_NODE) {
    411     log_err(parser, realX, "expect a sequence of %lu reals.\n", 
    412       (unsigned long)dim);
    413     res = RES_BAD_ARG;
    414     goto error;
    415   }
    416 
    417   n = realX->data.sequence.items.top - realX->data.sequence.items.start;
    418   if((size_t)n != dim) {
    419     log_err(parser, realX, "expect %lu reals while `%li' %s submitted.\n",
    420       (unsigned long)dim, n, n > 1 ? "are" : "is");
    421     res = RES_BAD_ARG;
    422     goto error;
    423   }
    424 
    425   FOR_EACH(i, 0, n) {
    426     yaml_node_t* real;
    427     real = yaml_document_get_node(doc, realX->data.sequence.items.start[i]);
    428     res = parse_real(parser, real, lower_bound, upper_bound, dst + i);
    429     if(res != RES_OK) goto error;
    430   }
    431 
    432 exit:
    433   return res;
    434 error:
    435   goto exit;
    436 }
    437 
    438 res_T
    439 parse_integer
    440   (struct solparser* parser,
    441    yaml_node_t* integer,
    442    const long lower_bound,
    443    const long upper_bound,
    444    long* dst)
    445 {
    446   res_T res = RES_OK;
    447   ASSERT(integer && dst && lower_bound < upper_bound);
    448 
    449   if(integer->type != YAML_SCALAR_NODE
    450   || !strlen((char*)integer->data.scalar.value)) {
    451     log_err(parser, integer, "expect an integer.\n");
    452     res = RES_BAD_ARG;
    453     goto error;
    454   }
    455 
    456   res = cstr_to_long((char*)integer->data.scalar.value, dst);
    457   if(res != RES_OK) {
    458     log_err(parser, integer, "invalid integer `%s'.\n",
    459       integer->data.scalar.value);
    460     res = RES_BAD_ARG;
    461     goto error;
    462   }
    463 
    464   if(*dst < lower_bound || *dst > upper_bound) {
    465     log_err(parser, integer, "%li is not in [%li, %li].\n",
    466       *dst, lower_bound, upper_bound);
    467     res = RES_BAD_ARG;
    468     goto error;
    469   }
    470 exit:
    471   return res;
    472 error:
    473   goto exit;
    474 }
    475 
    476 res_T
    477 parse_string
    478   (struct solparser* parser,
    479    yaml_node_t* string,
    480    struct str* str)
    481 {
    482   res_T res = RES_OK;
    483   ASSERT(string && str);
    484 
    485   if(string->type != YAML_SCALAR_NODE
    486   || !strlen((char*)string->data.scalar.value)) {
    487     log_err(parser, string, "expect a character string.\n");
    488     res = RES_BAD_ARG;
    489     goto error;
    490   }
    491   res = str_set(str, (char*)string->data.scalar.value);
    492   if(res !=  RES_OK) {
    493     log_err(parser, string, "could not register the string `%s'.\n",
    494       string->data.scalar.value);
    495     goto error;
    496   }
    497 
    498 exit:
    499   return res;
    500 error:
    501   goto exit;
    502 }
    503 
    504 res_T
    505 parse_transform
    506   (struct solparser* parser,
    507    yaml_document_t* doc,
    508    const yaml_node_t* transform,
    509    double translation[3],
    510    double rotation[3])
    511 {
    512   enum { ROTATION, TRANSLATION };
    513   intptr_t i, n;
    514   int mask = 0; /* Register the parsed attributes */
    515   res_T res = RES_OK;
    516   ASSERT(doc && translation && rotation && transform);
    517 
    518   d3_splat(translation, 0);
    519   d3_splat(rotation, 0);
    520 
    521   if(transform->type != YAML_MAPPING_NODE) {
    522     log_err(parser, transform, "expect a mapping of transform parameters.\n");
    523     res = RES_BAD_ARG;
    524     goto error;
    525   }
    526 
    527   n = transform->data.mapping.pairs.top - transform->data.mapping.pairs.start;
    528   FOR_EACH(i, 0, n) {
    529     yaml_node_t* key;
    530     yaml_node_t* val;
    531 
    532     key = yaml_document_get_node(doc, transform->data.mapping.pairs.start[i].key);
    533     val = yaml_document_get_node(doc, transform->data.mapping.pairs.start[i].value);
    534     if(key->type != YAML_SCALAR_NODE) {
    535       log_err(parser, key, "expect transform parameters.\n");
    536       res = RES_BAD_ARG;
    537       goto error;
    538     }
    539 
    540     #define SETUP_MASK(Flag, Name) {                                           \
    541       if(mask & BIT(Flag)) {                                                   \
    542         log_err(parser, key, "the transform `"Name"' is already defined.\n");  \
    543         res = RES_BAD_ARG;                                                     \
    544         goto error;                                                            \
    545       }                                                                        \
    546       mask |= BIT(Flag);                                                       \
    547     } (void)0
    548     if(!strcmp((char*)key->data.scalar.value, "translation")) {
    549       SETUP_MASK(TRANSLATION, "translation");
    550       res = parse_real3(parser, doc, val, -DBL_MAX, DBL_MAX, translation);
    551     } else if(!strcmp((char*)key->data.scalar.value, "rotation")) {
    552       SETUP_MASK(ROTATION, "rotation");
    553       res = parse_real3(parser, doc, val, -DBL_MAX, DBL_MAX, rotation);
    554     } else {
    555       log_err(parser, key, "unknown transform parameter `%s'.\n",
    556         key->data.scalar.value);
    557       res = RES_BAD_ARG;
    558       goto error;
    559     }
    560     if(res != RES_OK) {
    561       log_node(parser, key);
    562       goto error;
    563     }
    564     #undef SETUP_MASK
    565   }
    566 exit:
    567   return res;
    568 error:
    569   goto exit;
    570 }
    571 
    572 /*******************************************************************************
    573  * Exported functions
    574  ******************************************************************************/
    575 res_T
    576 solparser_create
    577   (struct mem_allocator* allocator, struct solparser** out_parser)
    578 {
    579   struct solparser* parser = NULL;
    580   struct mem_allocator* mem_allocator;
    581   res_T res = RES_OK;
    582   ASSERT(out_parser);
    583 
    584   mem_allocator = allocator ? allocator : &mem_default_allocator;
    585   parser = MEM_CALLOC(mem_allocator, 1, sizeof(struct solparser));
    586   if(!parser) {
    587     fprintf(stderr, "Could not allocate the Solstice parser.\n");
    588     res = RES_MEM_ERR;
    589     goto error;
    590   }
    591   parser->allocator = mem_allocator;
    592   ref_init(&parser->ref);
    593   str_init(mem_allocator, &parser->stream_name);
    594 
    595   /* Materials */
    596   htable_yaml2sols_init(mem_allocator, &parser->yaml2mtls);
    597   darray_image_init(mem_allocator, &parser->images);
    598   darray_material_init(mem_allocator, &parser->mtls);
    599   darray_material2_init(mem_allocator, &parser->mtls2);
    600   darray_dielectric_init(mem_allocator, &parser->dielectrics);
    601   darray_matte_init(mem_allocator, &parser->mattes);
    602   darray_mirror_init(mem_allocator, &parser->mirrors);
    603   darray_thin_dielectric_init(mem_allocator, &parser->thin_dielectrics);
    604 
    605   /* Mediums */
    606   htable_yaml2sols_init(mem_allocator, &parser->yaml2mediums);
    607   darray_medium_init(mem_allocator, &parser->mediums);
    608 
    609   /* Deferred targeted anchors */
    610   darray_tgtalias_init(mem_allocator, &parser->tgtaliases);
    611 
    612   /* Shapes */
    613   darray_shape_init(mem_allocator, &parser->shapes);
    614   darray_cuboid_init(mem_allocator, &parser->cuboids);
    615   darray_cylinder_init(mem_allocator, &parser->cylinders);
    616   darray_impgeom_init(mem_allocator, &parser->objs);
    617   darray_paraboloid_init(mem_allocator, &parser->parabols);
    618   darray_paraboloid_init(mem_allocator, &parser->parabolic_cylinders);
    619   darray_hyperboloid_init(mem_allocator, &parser->hyperbols);
    620   darray_hemisphere_init(mem_allocator, &parser->hemispheres);
    621   darray_plane_init(mem_allocator, &parser->planes);
    622   darray_sphere_init(mem_allocator, &parser->spheres);
    623   darray_impgeom_init(mem_allocator, &parser->stls);
    624 
    625   /* Geometries */
    626   htable_yaml2sols_init(mem_allocator, &parser->yaml2geoms);
    627   darray_object_init(mem_allocator, &parser->objects);
    628   darray_geometry_init(mem_allocator, &parser->geometries);
    629 
    630   /* Sun */
    631   solparser_sun_init(mem_allocator, &parser->sun);
    632 
    633   /* Atmosphere */
    634   solparser_atmosphere_init(mem_allocator, &parser->atmosphere);
    635 
    636   /* Entities */
    637   htable_str2sols_init(mem_allocator, &parser->str2entities);
    638   darray_entity_init(mem_allocator, &parser->entities);
    639 
    640   /* Miscellaneous */
    641   darray_anchor_init(mem_allocator, &parser->anchors);
    642   darray_x_pivot_init(mem_allocator, &parser->x_pivots);
    643   darray_zx_pivot_init(mem_allocator, &parser->zx_pivots);
    644   darray_spectrum_init(mem_allocator, &parser->spectra);
    645 
    646 exit:
    647   *out_parser = parser;
    648   return res;
    649 error:
    650   if(parser) {
    651     solparser_ref_put(parser);
    652     parser = NULL;
    653   }
    654   goto exit;
    655 }
    656 
    657 void
    658 solparser_ref_get(struct solparser* parser)
    659 {
    660   ASSERT(parser);
    661   ref_get(&parser->ref);
    662 }
    663 
    664 void
    665 solparser_ref_put(struct solparser* parser)
    666 {
    667   ASSERT(parser);
    668   ref_put(&parser->ref, parser_release);
    669 }
    670 
    671 res_T
    672 solparser_setup
    673   (struct solparser* parser,
    674    const char* stream_name,
    675    FILE* stream)
    676 {
    677   res_T res = RES_OK;
    678   ASSERT(parser && stream);
    679 
    680   if(parser->parser_is_init) {
    681     yaml_parser_delete(&parser->parser);
    682     parser->parser_is_init = 0;
    683   }
    684   res = str_set(&parser->stream_name, stream_name ? stream_name : "<stream>");
    685   if(res != RES_OK) {
    686     fprintf(stderr, "Could not register the filename.\n");
    687     goto error;
    688   }
    689   if(!yaml_parser_initialize(&parser->parser)) {
    690     fprintf(stderr, "Could not initialise the YAML parser.\n");
    691     res = RES_UNKNOWN_ERR;
    692     goto error;
    693   }
    694   parser->parser_is_init = 1;
    695   yaml_parser_set_input_file(&parser->parser, stream);
    696 
    697 exit:
    698   return res;
    699 error:
    700   str_clear(&parser->stream_name);
    701   if(parser->parser_is_init) {
    702     yaml_parser_delete(&parser->parser);
    703     parser->parser_is_init = 0;
    704   }
    705   goto exit;
    706 }
    707 
    708 res_T
    709 solparser_load(struct solparser* parser)
    710 {
    711   yaml_document_t doc;
    712   yaml_node_t* root;
    713   const char* filename;
    714   intptr_t i, n;
    715   int doc_is_init = 0;
    716   res_T res = RES_OK;
    717   ASSERT(parser);
    718 
    719   filename = str_cget(&parser->stream_name);
    720 
    721   parser_clear(parser); /* Clean up previously loaded data */
    722 
    723   if(!parser->parser_is_init) {
    724     res = RES_BAD_OP;
    725     goto error;
    726   }
    727 
    728   if(!yaml_parser_load(&parser->parser, &doc)) {
    729     fprintf(stderr, "%s:%lu:%lu: %s.\n",
    730       filename,
    731       (unsigned long)parser->parser.problem_mark.line+1,
    732       (unsigned long)parser->parser.problem_mark.column+1,
    733       parser->parser.problem);
    734     yaml_parser_delete(&parser->parser);
    735     parser->parser_is_init = 0;
    736     res = RES_BAD_OP;
    737     goto error;
    738   }
    739   doc_is_init = 1;
    740 
    741   root = yaml_document_get_root_node(&doc);
    742   if(!root) {
    743     yaml_parser_delete(&parser->parser);
    744     parser->parser_is_init = 0;
    745     res = RES_BAD_OP;
    746     goto error;
    747   }
    748 
    749   if(root->type != YAML_SEQUENCE_NODE) {
    750     log_err(parser, root, "expect a list of items.\n");
    751     res = RES_BAD_ARG;
    752     goto error;
    753   }
    754 
    755   n = root->data.sequence.items.top - root->data.sequence.items.start;
    756   FOR_EACH(i, 0, n) {
    757     yaml_node_t* item;
    758 
    759     item = yaml_document_get_node(&doc, root->data.sequence.items.start[i]);
    760     res = parse_item(parser, &doc, item);
    761     if(res != RES_OK) goto error;
    762   }
    763 
    764   if(!parser->sun_key) {
    765     log_err(parser, root, "no sun definition in the document.\n");
    766     res = RES_BAD_ARG;
    767     goto error;
    768   }
    769 
    770 exit:
    771   if(doc_is_init) yaml_document_delete(&doc);
    772   return res;
    773 error:
    774   parser_clear(parser);
    775   goto exit;
    776 }
    777 
    778 const struct solparser_anchor*
    779 solparser_find_anchor
    780   (struct solparser* parser, const char* name)
    781 {
    782   struct str str;
    783   struct str str_tk;
    784   struct htable_str2sols* htable = NULL;
    785   struct solparser_entity* entity = NULL;
    786   struct solparser_anchor* anchor = NULL;
    787   char* cstr;
    788   char* tk;
    789   char* tk_anchor;
    790   res_T res = RES_OK;
    791   ASSERT(parser && name);
    792 
    793   str_init(parser->allocator, &str);
    794   str_init(parser->allocator, &str_tk);
    795 
    796   res = str_set(&str, name);
    797   if(res != RES_OK) {
    798     fprintf(stderr, "%s: could not copy the input string.\n", FUNC_NAME);
    799     goto error;
    800   }
    801   res = str_reserve(&str_tk, str_len(&str));
    802   if(res != RES_OK) {
    803     fprintf(stderr, "%s: could not allocate the temporary token string.\n",
    804       FUNC_NAME);
    805     goto error;
    806   }
    807 
    808   cstr = str_get(&str);
    809   tk_anchor = strrchr(cstr, '.');
    810   if(!tk_anchor) goto exit;
    811   *tk_anchor='\0';
    812   ++tk_anchor;
    813 
    814   tk = strtok(cstr, ".");
    815   htable = &parser->str2entities;
    816   while(tk) {
    817     size_t* pientity;
    818     str_set(&str_tk, tk);
    819     pientity = htable_str2sols_find(htable, &str_tk);
    820     if(!pientity) {
    821       tk = NULL;
    822       entity = NULL;
    823     } else {
    824       tk = strtok(NULL, ".");
    825       entity = darray_entity_data_get(&parser->entities) + *pientity;
    826       htable = &entity->str2children;
    827     }
    828   }
    829 
    830   if(entity) {
    831     size_t* pianchor;
    832     str_set(&str_tk, tk_anchor);
    833     pianchor = htable_str2sols_find(&entity->str2anchors, &str_tk);
    834     if(pianchor) {
    835       anchor = darray_anchor_data_get(&parser->anchors) + *pianchor;
    836     }
    837   }
    838 
    839 exit:
    840   str_release(&str);
    841   str_release(&str_tk);
    842   return anchor;
    843 error:
    844   anchor = NULL;
    845   goto exit;
    846 }
    847 
    848 const struct solparser_entity*
    849 solparser_find_entity
    850   (struct solparser* parser, const char* name)
    851 {
    852   struct htable_str2sols* htable = NULL;
    853   struct solparser_entity* entity = NULL;
    854   struct str str;
    855   struct str str_tk;
    856   char* cstr;
    857   char* tk;
    858   res_T res = RES_OK;
    859   ASSERT(parser && name);
    860 
    861   str_init(parser->allocator, &str);
    862   str_init(parser->allocator, &str_tk);
    863 
    864   res = str_set(&str, name);
    865   if(res != RES_OK) {
    866     fprintf(stderr, "%s: could not copy the input string.\n", FUNC_NAME);
    867     goto error;
    868   }
    869   res = str_reserve(&str_tk, str_len(&str));
    870   if(res != RES_OK) {
    871     fprintf(stderr, "%s: could not allocate the temporary token sting.\n",
    872       FUNC_NAME);
    873     goto error;
    874   }
    875 
    876   cstr = str_get(&str);
    877   tk = strtok(cstr, ".");
    878   htable = &parser->str2entities;
    879   while(tk) {
    880     size_t* pientity;
    881     str_set(&str_tk, tk);
    882     pientity = htable_str2sols_find(htable, &str_tk);
    883     if(!pientity) {
    884       tk = NULL;
    885       entity = NULL;
    886     } else {
    887       tk = strtok(NULL, ".");
    888       entity = darray_entity_data_get(&parser->entities) + *pientity;
    889       htable = &entity->str2children;
    890     }
    891   }
    892 
    893 exit:
    894   str_release(&str);
    895   str_release(&str_tk);
    896   return entity;
    897 error:
    898   entity = NULL;
    899   goto exit;
    900 }
    901 
    902 const struct solparser_anchor*
    903 solparser_get_anchor
    904   (const struct solparser* parser,
    905    const struct solparser_anchor_id anchor)
    906 {
    907   ASSERT(parser && anchor.i < darray_anchor_size_get(&parser->anchors));
    908   return darray_anchor_cdata_get(&parser->anchors) + anchor.i;
    909 }
    910 
    911 const struct solparser_entity*
    912 solparser_get_entity
    913   (const struct solparser* parser,
    914    const struct solparser_entity_id entity)
    915 {
    916   ASSERT(parser && entity.i < darray_entity_size_get(&parser->entities));
    917   return darray_entity_cdata_get(&parser->entities) + entity.i;
    918 }
    919 
    920 const struct solparser_image*
    921 solparser_get_image
    922   (const struct solparser* parser,
    923    const struct solparser_image_id image)
    924 {
    925   ASSERT(parser && image.i < darray_image_size_get(&parser->images));
    926   return darray_image_cdata_get(&parser->images) + image.i;
    927 }
    928 
    929 const struct solparser_geometry*
    930 solparser_get_geometry
    931   (const struct solparser* parser,
    932    const struct solparser_geometry_id geom)
    933 {
    934   ASSERT(parser && geom.i < darray_geometry_size_get(&parser->geometries));
    935   return darray_geometry_cdata_get(&parser->geometries) + geom.i;
    936 }
    937 
    938 const struct solparser_medium*
    939 solparser_get_medium
    940   (const struct solparser* parser,
    941    const struct solparser_medium_id medium)
    942 {
    943   ASSERT(parser && medium.i < darray_medium_size_get(&parser->mediums));
    944   return darray_medium_cdata_get(&parser->mediums) + medium.i;
    945 }
    946 
    947 const struct solparser_material*
    948 solparser_get_material
    949   (const struct solparser* parser,
    950    const struct solparser_material_id mtl)
    951 {
    952   ASSERT(parser && mtl.i < darray_material_size_get(&parser->mtls));
    953   return darray_material_cdata_get(&parser->mtls) + mtl.i;
    954 }
    955 
    956 const struct solparser_material_double_sided*
    957 solparser_get_material_double_sided
    958   (const struct solparser* parser,
    959    const struct solparser_material_double_sided_id mtl2)
    960 {
    961   ASSERT(parser && mtl2.i < darray_material2_size_get(&parser->mtls2));
    962   return darray_material2_cdata_get(&parser->mtls2) + mtl2.i;
    963 }
    964 
    965 const struct solparser_material_dielectric*
    966 solparser_get_material_dielectric
    967   (const struct solparser* parser,
    968    const struct solparser_material_dielectric_id dielectric)
    969 {
    970   ASSERT(parser);
    971   ASSERT(dielectric.i < darray_dielectric_size_get(&parser->dielectrics));
    972   return darray_dielectric_cdata_get(&parser->dielectrics) + dielectric.i;
    973 }
    974 
    975 const struct solparser_material_matte*
    976 solparser_get_material_matte
    977   (const struct solparser* parser,
    978    const struct solparser_material_matte_id matte)
    979 {
    980   ASSERT(parser && matte.i < darray_matte_size_get(&parser->mattes));
    981   return darray_matte_cdata_get(&parser->mattes) + matte.i;
    982 }
    983 
    984 const struct solparser_material_mirror*
    985 solparser_get_material_mirror
    986   (const struct solparser* parser,
    987    const struct solparser_material_mirror_id mirror)
    988 {
    989   ASSERT(parser && mirror.i < darray_mirror_size_get(&parser->mirrors));
    990   return darray_mirror_cdata_get(&parser->mirrors) + mirror.i;
    991 }
    992 
    993 const struct solparser_material_thin_dielectric*
    994 solparser_get_material_thin_dielectric
    995   (const struct solparser* parser,
    996    const struct solparser_material_thin_dielectric_id thin)
    997 {
    998   ASSERT(parser);
    999   ASSERT(thin.i < darray_thin_dielectric_size_get(&parser->thin_dielectrics));
   1000   return darray_thin_dielectric_cdata_get(&parser->thin_dielectrics) + thin.i;
   1001 }
   1002 
   1003 const struct solparser_object*
   1004 solparser_get_object
   1005   (const struct solparser* parser,
   1006    const struct solparser_object_id obj)
   1007 {
   1008   ASSERT(parser && obj.i < darray_object_size_get(&parser->objects));
   1009   return darray_object_cdata_get(&parser->objects) + obj.i;
   1010 }
   1011 
   1012 const struct solparser_x_pivot*
   1013 solparser_get_x_pivot
   1014   (const struct solparser* parser,
   1015    const struct solparser_pivot_id x_pivot)
   1016 {
   1017   ASSERT(parser && x_pivot.i < darray_x_pivot_size_get(&parser->x_pivots));
   1018   return darray_x_pivot_cdata_get(&parser->x_pivots) + x_pivot.i;
   1019 }
   1020 
   1021 const struct solparser_zx_pivot*
   1022 solparser_get_zx_pivot
   1023   (const struct solparser* parser,
   1024    const struct solparser_pivot_id zx_pivot)
   1025 {
   1026   ASSERT(parser && zx_pivot.i < darray_zx_pivot_size_get(&parser->zx_pivots));
   1027   return darray_zx_pivot_cdata_get(&parser->zx_pivots) + zx_pivot.i;
   1028 }
   1029 
   1030 const struct solparser_shape*
   1031 solparser_get_shape
   1032   (const struct solparser* parser,
   1033    const struct solparser_shape_id shape)
   1034 {
   1035   ASSERT(parser && shape.i < darray_shape_size_get(&parser->shapes));
   1036   return darray_shape_cdata_get(&parser->shapes) + shape.i;
   1037 }
   1038 
   1039 const struct solparser_shape_cuboid*
   1040 solparser_get_shape_cuboid
   1041   (const struct solparser* parser,
   1042    const struct solparser_shape_cuboid_id cuboid)
   1043 {
   1044   ASSERT(parser && cuboid.i < darray_cuboid_size_get(&parser->cuboids));
   1045   return darray_cuboid_cdata_get(&parser->cuboids) + cuboid.i;
   1046 }
   1047 
   1048 const struct solparser_shape_cylinder*
   1049 solparser_get_shape_cylinder
   1050   (const struct solparser* parser,
   1051    const struct solparser_shape_cylinder_id cylinder)
   1052 {
   1053   ASSERT(parser && cylinder.i < darray_cylinder_size_get(&parser->cylinders));
   1054   return darray_cylinder_cdata_get(&parser->cylinders) + cylinder.i;
   1055 }
   1056 
   1057 const struct solparser_shape_imported_geometry*
   1058 solparser_get_shape_obj
   1059   (const struct solparser* parser,
   1060    const struct solparser_shape_imported_geometry_id impgeom)
   1061 {
   1062   ASSERT(parser && impgeom.i < darray_impgeom_size_get(&parser->objs));
   1063   return darray_impgeom_cdata_get(&parser->objs) + impgeom.i;
   1064 }
   1065 
   1066 const struct solparser_shape_paraboloid*
   1067 solparser_get_shape_parabol
   1068   (const struct solparser* parser,
   1069    const struct solparser_shape_paraboloid_id paraboloid)
   1070 {
   1071   ASSERT(parser && paraboloid.i < darray_paraboloid_size_get(&parser->parabols));
   1072   return darray_paraboloid_cdata_get(&parser->parabols) + paraboloid.i;
   1073 }
   1074 
   1075 const struct solparser_shape_paraboloid*
   1076 solparser_get_shape_parabolic_cylinder
   1077   (const struct solparser* parser,
   1078    const struct solparser_shape_paraboloid_id paraboloid)
   1079 {
   1080   ASSERT(parser);
   1081   ASSERT(paraboloid.i<darray_paraboloid_size_get(&parser->parabolic_cylinders));
   1082   return darray_paraboloid_cdata_get(&parser->parabolic_cylinders)+paraboloid.i;
   1083 }
   1084 
   1085 const struct solparser_shape_hyperboloid*
   1086 solparser_get_shape_hyperbol
   1087   (const struct solparser* parser,
   1088    const struct solparser_shape_hyperboloid_id hyperboloid)
   1089 {
   1090   ASSERT(parser && hyperboloid.i < darray_hyperboloid_size_get(&parser->hyperbols));
   1091   return darray_hyperboloid_cdata_get(&parser->hyperbols) + hyperboloid.i;
   1092 }
   1093 
   1094 const struct solparser_shape_hemisphere*
   1095 solparser_get_shape_hemisphere
   1096   (const struct solparser* parser,
   1097    const struct solparser_shape_hemisphere_id hemisphere)
   1098 {
   1099   ASSERT(parser && hemisphere.i < darray_hemisphere_size_get(&parser->hemispheres));
   1100   return darray_hemisphere_cdata_get(&parser->hemispheres) + hemisphere.i;
   1101 }
   1102 
   1103 const struct solparser_shape_plane*
   1104 solparser_get_shape_plane
   1105   (const struct solparser* parser,
   1106    const struct solparser_shape_plane_id plane)
   1107 {
   1108   ASSERT(parser && plane.i < darray_plane_size_get(&parser->planes));
   1109   return darray_plane_cdata_get(&parser->planes) + plane.i;
   1110 }
   1111 
   1112 const struct solparser_shape_sphere*
   1113 solparser_get_shape_sphere
   1114   (const struct solparser* parser,
   1115    const struct solparser_shape_sphere_id sphere)
   1116 {
   1117   ASSERT(parser && sphere.i < darray_sphere_size_get(&parser->spheres));
   1118   return darray_sphere_cdata_get(&parser->spheres) + sphere.i;
   1119 }
   1120 
   1121 const struct solparser_shape_imported_geometry*
   1122 solparser_get_shape_stl
   1123   (const struct solparser* parser,
   1124    const struct solparser_shape_imported_geometry_id impgeom)
   1125 {
   1126   ASSERT(parser && impgeom.i < darray_impgeom_size_get(&parser->stls));
   1127   return darray_impgeom_cdata_get(&parser->stls) + impgeom.i;
   1128 }
   1129 
   1130 const struct solparser_spectrum*
   1131 solparser_get_spectrum
   1132   (const struct solparser* parser,
   1133    const struct solparser_spectrum_id spectrum)
   1134 {
   1135   ASSERT(parser && spectrum.i < darray_spectrum_size_get(&parser->spectra));
   1136   return darray_spectrum_cdata_get(&parser->spectra) + spectrum.i;
   1137 }
   1138 
   1139 int
   1140 solparser_has_spectrum(const struct solparser* parser)
   1141 {
   1142   ASSERT(parser);
   1143   return darray_spectrum_size_get(&parser->spectra) != 0;
   1144 }
   1145 
   1146 const struct solparser_sun*
   1147 solparser_get_sun(const struct solparser* parser)
   1148 {
   1149   ASSERT(parser && parser->sun_key);
   1150   return &parser->sun;
   1151 }
   1152 
   1153 const struct solparser_atmosphere*
   1154 solparser_get_atmosphere(const struct solparser* parser)
   1155 {
   1156   ASSERT(parser);
   1157   if(parser->atmosphere_key) return &parser->atmosphere;
   1158   else return NULL;
   1159 }
   1160 
   1161 void
   1162 solparser_entity_iterator_begin
   1163   (struct solparser* parser,
   1164    struct solparser_entity_iterator* it)
   1165 {
   1166   ASSERT(parser && it);
   1167   htable_str2sols_begin(&parser->str2entities, &it->it__);
   1168 }
   1169 
   1170 void
   1171 solparser_entity_iterator_end
   1172   (struct solparser* parser,
   1173    struct solparser_entity_iterator* it)
   1174 {
   1175   ASSERT(parser && it);
   1176   htable_str2sols_end(&parser->str2entities, &it->it__);
   1177 }
   1178 
   1179 void
   1180 solparser_material_iterator_begin
   1181   (struct solparser* parser, struct solparser_material_iterator* it)
   1182 {
   1183   ASSERT(parser && it);
   1184   it->mtls__ = darray_material_cdata_get(&parser->mtls);
   1185   it->imtl__ = 0;
   1186 }
   1187 
   1188 void
   1189 solparser_material_iterator_end
   1190   (struct solparser* parser, struct solparser_material_iterator* it)
   1191 {
   1192   ASSERT(parser && it);
   1193   it->mtls__ = darray_material_cdata_get(&parser->mtls);
   1194   it->imtl__ = darray_material_size_get(&parser->mtls);
   1195 }
   1196 
   1197 void
   1198 solparser_geometry_iterator_begin
   1199   (struct solparser* parser, struct solparser_geometry_iterator* it)
   1200 {
   1201   ASSERT(parser && it);
   1202   it->geoms__ = darray_geometry_cdata_get(&parser->geometries);
   1203   it->igeom__ = 0;
   1204 }
   1205 
   1206 void
   1207 solparser_geometry_iterator_end
   1208   (struct solparser* parser, struct solparser_geometry_iterator* it)
   1209 {
   1210   ASSERT(parser && it);
   1211   it->geoms__ = darray_geometry_cdata_get(&parser->geometries);
   1212   it->igeom__ = darray_geometry_size_get(&parser->geometries);
   1213 }
   1214