commit c32ed3872c6fbd7dae40ebe6dfc7756820efbf3c
parent d3de4260d454d80a49655b45896e2da8e9f24103
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Wed, 9 Nov 2016 15:18:23 +0100
Refactor the loader API
Expose an explicit parser object and use it to parse the multiple
YAML documents contained in a file.
Diffstat:
6 files changed, 2284 insertions(+), 2142 deletions(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -51,8 +51,8 @@ set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})
set(SOLSTICE_FILES_SRC
solstice.c
- solstice_facility.c)
-set(SOLSTICE_FILES_INC solstice_facility.h)
+ solstice_parser.c)
+set(SOLSTICE_FILES_INC solstice_parser.h)
set(SOLSTICE_FILES_DOC COPYING README.md)
# Prepend each file in the `SOLSTICE_FILES_<SRC|INC>' list by `SOLSTICE_SOURCE_DIR'
@@ -74,6 +74,15 @@ rcmake_copy_runtime_libraries(solstice)
################################################################################
# Tests
################################################################################
+if(NOT NO_TEST)
+ function(new_test _name)
+ set(_files "")
+ foreach(_file ${ARGN})
+ list(APPEND _files ${SOLSTICE_SOURCE_DIR}/${_file})
+ endforeach()
+ add_executable(${_name} ${SOLSTICE_SOURCE_DIR}/${_name}.c ${_files})
+ endfunction()
+endif()
################################################################################
# Define output & install directories
diff --git a/src/solstice.c b/src/solstice.c
@@ -13,12 +13,14 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
-#include "solstice_facility.h"
+#include "solstice_parser.h"
#include <rsys/rsys.h>
int
main(int argc, char** argv)
{
+ FILE* file = NULL;
+ struct solstice_parser* parser = NULL;
res_T res;
int err;
int i;
@@ -29,16 +31,38 @@ main(int argc, char** argv)
goto error;
}
+ res = solstice_parser_create(NULL, &parser);
+ if(res != RES_OK) goto error;
+
FOR_EACH(i, 1, argc) {
- res = solstice_facility_load(argv[i]);
- if(res != RES_OK) {
- err = 1;
+ int is_empty = 1;
+
+ file = fopen(argv[i], "rb");
+ if(!file) {
+ fprintf(stderr, "Could not open the file `%s'.\n", argv[i]);
goto error;
}
+
+ res = solstice_parser_setup(parser, argv[i], file);
+ if(res != RES_OK) break;
+
+ do {
+ res = solstice_parser_load(parser);
+ if(res != RES_BAD_OP) is_empty = 0;
+ } while(res != RES_BAD_OP);
+
+ if(is_empty) {
+ fprintf(stderr, "The `%s' file seems empty.\n", argv[i]);
+ }
+ fclose(file);
+ file = NULL;
}
exit:
+ if(parser) solstice_parser_ref_put(parser);
+ if(file) fclose(file);
return err;
error:
+ err = -1;
goto exit;
}
diff --git a/src/solstice_facility.c b/src/solstice_facility.c
@@ -1,2110 +0,0 @@
-/* Copyright (C) CNRS 2016
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#define _POSIX_C_SOURCE 200112L
-
-#include "solstice_facility.h"
-
-#include <rsys/cstr.h>
-#include <rsys/double3.h>
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-#include <yaml.h>
-
-enum paraboloid_type {
- PARABOL,
- PARABOLIC_CYLINDER
-};
-
-enum geometry_fileformat {
- GEOMETRY_OBJ,
- GEOMETRY_STL
-};
-
-static res_T
-parse_node
- (const char* filename,
- yaml_document_t* doc,
- const yaml_node_t* children);
-
-static res_T
-parse_object
- (const char* filename,
- yaml_document_t* doc,
- const yaml_node_t* object);
-
-static res_T
-parse_pivot
- (const char* filename,
- yaml_document_t* doc,
- const yaml_node_t* pivot);
-
-static res_T
-parse_sun
- (const char* filename,
- yaml_document_t* doc,
- const yaml_node_t* sun);
-
-/*******************************************************************************
- * Helper functions
- ******************************************************************************/
-static INLINE void
-log_err
- (const char* filename,
- const yaml_node_t* node,
- const char* fmt,
- ...)
-{
- va_list vargs_list;
- ASSERT(filename && node && fmt);
-
- fprintf(stderr, "%s:%lu:%lu: ",
- filename,
- (unsigned long)node->start_mark.line+1,
- (unsigned long)node->start_mark.column+1);
- va_start(vargs_list, fmt);
- vfprintf(stderr, fmt, vargs_list);
- va_end(vargs_list);
-}
-
-static INLINE const char*
-paraboloid_type_name(const enum paraboloid_type type)
-{
- switch(type) {
- case PARABOL: return "parabol"; break;
- case PARABOLIC_CYLINDER: return "parabolic cylinder"; break;
- default: FATAL("Unreachable code.\n"); break;
- }
-}
-
-static INLINE const char*
-geometry_fileformat_name(const enum geometry_fileformat fileformat)
-{
- switch(fileformat) {
- case GEOMETRY_OBJ: return "obj"; break;
- case GEOMETRY_STL: return "stl"; break;
- default: FATAL("Unreachable code.\n"); break;
- }
-}
-
-/*******************************************************************************
- * Miscellaneous parsing functions
- ******************************************************************************/
-static res_T
-parse_real
- (const char* filename,
- const yaml_node_t* real,
- const double lower_bound,
- const double upper_bound,
- double* dst)
-{
- res_T res = RES_OK;
- ASSERT(real && dst && lower_bound < upper_bound);
-
- if(real->type != YAML_SCALAR_NODE) {
- log_err(filename, real, "expect a floating point number.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- res = cstr_to_double((char*)real->data.scalar.value, dst);
- if(res != RES_OK) {
- log_err(filename, real, "invalid floating point number `%s'.\n",
- real->data.scalar.value);
- res = RES_BAD_ARG;
- goto error;
- }
-
- if(*dst < lower_bound || *dst > upper_bound) {
- log_err(filename, real, "%g is not in [%g, %g].\n",
- *dst, lower_bound, upper_bound);
- res = RES_BAD_ARG;
- goto error;
- }
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-static res_T
-parse_real3
- (const char* filename,
- yaml_document_t* doc,
- const yaml_node_t* real3,
- const double lower_bound,
- const double upper_bound,
- double dst[3])
-{
- intptr_t i, n;
- res_T res = RES_OK;
- ASSERT(doc && real3 && dst);
-
- if(real3->type != YAML_SEQUENCE_NODE) {
- log_err(filename, real3, "expect a sequence of 3 reals.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = real3->data.sequence.items.top - real3->data.sequence.items.start;
- if(n != 3) {
- log_err(filename, real3, "expect 3 reals while `%li' %s submitted.\n",
- n, n > 1 ? "are" : "is");
- res = RES_BAD_ARG;
- goto error;
- }
-
- FOR_EACH(i, 0, n) {
- yaml_node_t* real;
- real = yaml_document_get_node(doc, real3->data.sequence.items.start[i]);
- res = parse_real(filename, real, lower_bound, upper_bound, dst + i);
- if(res != RES_OK) goto error;
- }
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-static res_T
-parse_integer
- (const char* filename,
- yaml_node_t* integer,
- const long lower_bound,
- const long upper_bound,
- long* dst)
-{
- res_T res = RES_OK;
- ASSERT(integer && dst && lower_bound < upper_bound);
-
- if(integer->type != YAML_SCALAR_NODE) {
- log_err(filename, integer, "expect an integer.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- res = cstr_to_long((char*)integer->data.scalar.value, dst);
- if(res != RES_OK) {
- log_err(filename, integer, "invalid integer `%s'.\n",
- integer->data.scalar.value);
- res = RES_BAD_ARG;
- goto error;
- }
-
- if(*dst < lower_bound || *dst > upper_bound) {
- log_err(filename, integer, "%li is not in [%li, %li].\n",
- *dst, lower_bound, upper_bound);
- res = RES_BAD_ARG;
- goto error;
- }
-exit:
- return res;
-error:
- goto exit;
-}
-
-static res_T
-parse_string(const char* filename, yaml_node_t* string)
-{
- res_T res = RES_OK;
- ASSERT(string);
-
- if(string->type != YAML_SCALAR_NODE) {
- log_err(filename, string, "expect a character string.\n");
- res = RES_BAD_ARG;
- goto error;
- }
- /* TODO create a string from the value of the YAML scalar node */
-exit:
- return res;
-error:
- goto exit;
-}
-
-static res_T
-parse_transform
- (const char* filename,
- yaml_document_t* doc,
- const yaml_node_t* transform,
- double translation[3],
- double rotation[3])
-{
- enum { ROTATION, TRANSLATION };
- intptr_t i, n;
- int mask = 0; /* Register the parsed attributes */
- res_T res = RES_OK;
- ASSERT(doc && translation && rotation && transform);
-
- d3_splat(translation, 0);
- d3_splat(rotation, 0);
-
- if(transform->type != YAML_MAPPING_NODE) {
- log_err(filename, transform, "expect a mapping of transform parameters.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = transform->data.mapping.pairs.top - transform->data.mapping.pairs.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* key;
- yaml_node_t* val;
-
- key = yaml_document_get_node(doc, transform->data.mapping.pairs.start[i].key);
- val = yaml_document_get_node(doc, transform->data.mapping.pairs.start[i].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expect transform parameters.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- #define SETUP_MASK(Flag, Name) { \
- if(mask & BIT(Flag)) { \
- log_err(filename, key, "the transform `"Name"' is already defined.\n");\
- res = RES_BAD_ARG; \
- goto error; \
- } \
- mask |= BIT(Flag); \
- } (void)0
- if(!strcmp((char*)key->data.scalar.value, "translation")) {
- SETUP_MASK(TRANSLATION, "translation");
- res = parse_real3(filename, doc, val, -DBL_MAX, DBL_MAX, translation);
- } else if(!strcmp((char*)key->data.scalar.value, "rotation")) {
- SETUP_MASK(ROTATION, "rotation");
- res = parse_real3(filename, doc, val, -DBL_MAX, DBL_MAX, rotation);
- } else {
- log_err(filename, key, "unknown transform parameter `%s'.\n",
- key->data.scalar.value);
- res = RES_BAD_ARG;
- }
- if(res != RES_OK) goto error;
- #undef SETUP_MASK
- }
-exit:
- return res;
-error:
- goto exit;
-}
-
-static res_T
-parse_spectrum_data
- (const char* filename, yaml_document_t* doc,
- const double lower_bound,
- const double upper_bound,
- const yaml_node_t* sdata)
-{
- enum { DATA, WAVELENGTH };
- double wavelength;
- double data;
- intptr_t i, n;
- int mask = 0; /* Register the parsed attributes */
- res_T res = RES_OK;
- ASSERT(doc && data && lower_bound < upper_bound);
-
- if(sdata->type != YAML_MAPPING_NODE) {
- log_err(filename, sdata, "expect the definition of a spectrum data.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = sdata->data.mapping.pairs.top - sdata->data.mapping.pairs.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* key;
- yaml_node_t* val;
-
- key = yaml_document_get_node(doc, sdata->data.mapping.pairs.start[i].key);
- val = yaml_document_get_node(doc, sdata->data.mapping.pairs.start[i].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expect a spectrum data parameter.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- #define SETUP_MASK(Flag, Name) { \
- if(mask & BIT(Flag)) { \
- log_err(filename, key, \
- "the `"Name"' of the spectrum data is already defined.\n"); \
- res = RES_BAD_ARG; \
- goto error; \
- } \
- mask |= BIT(Flag); \
- } (void)0
- if(!strcmp((char*)key->data.scalar.value, "data")) {
- SETUP_MASK(DATA, "data");
- res = parse_real(filename, val, lower_bound, upper_bound, &data);
- } else if(!strcmp((char*)key->data.scalar.value, "wavelength")) {
- SETUP_MASK(WAVELENGTH, "wavelength");
- res = parse_real(filename, val, 0, DBL_MAX, &wavelength);
- } else {
- log_err(filename, key, "unknown spectrum data parameter `%s'.\n",
- key->data.scalar.value);
- res = RES_BAD_ARG;
- }
- if(res != RES_OK) goto error;
- #undef SETUP_MASK
- }
-
- #define CHECK_PARAM(Flag, Name) \
- if(!(mask & BIT(Flag))) { \
- log_err(filename, sdata,"the "Name" of the spectrum data is missing.\n");\
- res = RES_BAD_ARG; \
- goto error; \
- } (void)0
- CHECK_PARAM(DATA, "data");
- CHECK_PARAM(WAVELENGTH, "wavelength");
- #undef CHECK_PARAM
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-static res_T
-parse_spectrum
- (const char* filename,
- yaml_document_t* doc,
- const double lower_bound,
- const double upper_bound,
- const yaml_node_t* spectrum)
-{
- intptr_t i, n;
- res_T res = RES_OK;
- ASSERT(doc && spectrum && lower_bound < upper_bound);
-
- if(spectrum->type != YAML_SEQUENCE_NODE) {
- log_err(filename, spectrum, "expect a list of spectrum data.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = spectrum->data.sequence.items.top - spectrum->data.sequence.items.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* sdata;
-
- sdata = yaml_document_get_node(doc, spectrum->data.sequence.items.start[i]);
- res = parse_spectrum_data(filename, doc, lower_bound, upper_bound, sdata);
- if(res != RES_OK) goto error;
-
- /* TODO register the spectrum data */
- }
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-/*******************************************************************************
- * Instance
- ******************************************************************************/
-static res_T
-parse_instance
- (const char* filename, yaml_document_t* doc, const yaml_node_t* inst)
-{
- enum { GEOMETRY, TRANSFORM };
- double translation[3] = {0, 0, 0};
- double rotation[3] = {0, 0, 0};
- intptr_t i, n;
- int mask = 0; /* Register the parsed attributes */
- res_T res = RES_OK;
- ASSERT(doc && inst);
-
- if(inst->type != YAML_MAPPING_NODE) {
- log_err(filename, inst, "expect an instance definition.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = inst->data.mapping.pairs.top - inst->data.mapping.pairs.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* key;
- yaml_node_t* val;
-
- key = yaml_document_get_node(doc, inst->data.mapping.pairs.start[i].key);
- val = yaml_document_get_node(doc, inst->data.mapping.pairs.start[i].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expect instance parameters.\n");
- res = RES_BAD_ARG;
- goto error;
- }
- #define SETUP_MASK(Flag, Name) { \
- if(mask & BIT(Flag)) { \
- log_err(filename, key, "the instance "Name" is already defined.\n"); \
- res = RES_BAD_ARG; \
- goto error; \
- } \
- mask |= BIT(Flag); \
- } (void)0
- if(!strcmp((char*)key->data.scalar.value, "node")) {
- SETUP_MASK(GEOMETRY, "geometry");
- res = parse_node(filename, doc, val);
- } else if(!strcmp((char*)key->data.scalar.value, "object")) {
- SETUP_MASK(GEOMETRY, "geometry");
- res = parse_object(filename, doc, val);
- } else if(!strcmp((char*)key->data.scalar.value, "transform")) {
- SETUP_MASK(TRANSFORM, "transform");
- res = parse_transform(filename, doc, val, translation, rotation);
- } else {
- log_err(filename, key, "unknown instance parameter `%s'.\n",
- key->data.scalar.value);
- res = RES_BAD_ARG;
- }
- if(res != RES_OK) goto error;
- #undef SETUP_MASK
- }
-
- #define CHECK_PARAM(Flag, Name) \
- if(!(mask & BIT(Flag))) { \
- log_err(filename, inst, "the instance "Name" is missing.\n"); \
- res = RES_BAD_ARG; \
- goto error; \
- } (void)0
- CHECK_PARAM(GEOMETRY, "geometry");
- #undef CHECK_PARAM
-
- /* TODO register the instance */
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-/*******************************************************************************
- * Material
- ******************************************************************************/
-static res_T
-parse_material_matte
- (const char* filename, yaml_document_t* doc, const yaml_node_t* matte)
-{
- enum { REFLECTIVITY };
- double reflectivity;
- intptr_t i, n;
- int mask = 0; /* Register the parsed attributes */
- res_T res = RES_OK;
- ASSERT(doc && matte);
-
- if(matte->type != YAML_MAPPING_NODE) {
- log_err(filename, matte, "expect a mapping of matte material parameters.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = matte->data.mapping.pairs.top - matte->data.mapping.pairs.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* key;
- yaml_node_t* val;
-
- key = yaml_document_get_node(doc, matte->data.mapping.pairs.start[0].key);
- val = yaml_document_get_node(doc, matte->data.mapping.pairs.start[0].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expect a matte material parameter.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- if(!strcmp((char*)key->data.scalar.value, "reflectivity")) {
- if(mask & BIT(REFLECTIVITY)) {
- log_err(filename, key, "the matte reflectivity is already defined.\n");
- res = RES_BAD_ARG;
- goto error;
- }
- mask |= BIT(REFLECTIVITY);
- res = parse_real(filename, val, 0, 1, &reflectivity);
- } else {
- log_err(filename, key, "unknown matte parameter `%s'.\n",
- key->data.scalar.value);
- res = RES_BAD_ARG;
- }
- if(res != RES_OK) goto error;
- }
-
- if(!(mask & BIT(REFLECTIVITY))) {
- log_err(filename, matte, "the matte reflectivity is missing.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- /* TODO create the matte material */
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-static res_T
-parse_material_mirror
- (const char* filename, yaml_document_t* doc, const yaml_node_t* mirror)
-{
- enum { REFLECTIVITY, ROUGHNESS };
- double reflectivity;
- double roughness;
- int mask = 0; /* Register the parsed attributes */
- intptr_t i, n;
- res_T res = RES_OK;
- ASSERT(doc && mirror);
-
- if(mirror->type != YAML_MAPPING_NODE) {
- log_err(filename, mirror,
- "expect a mapping of mirror material attributes .\n");
- res = RES_BAD_ARG;
- goto error;
- }
- n = mirror->data.mapping.pairs.top - mirror->data.mapping.pairs.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* key;
- yaml_node_t* val;
-
- key = yaml_document_get_node(doc, mirror->data.mapping.pairs.start[i].key);
- val = yaml_document_get_node(doc, mirror->data.mapping.pairs.start[i].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expect a mirror material parameter.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- #define SETUP_MASK(Flag, Name) { \
- if(mask & BIT(Flag)) { \
- log_err(filename, key, \
- "the "Name" of the mirror material is already defined.\n"); \
- res = RES_BAD_ARG; \
- goto error; \
- } \
- mask |= BIT(Flag); \
- } (void)0
- if(!strcmp((char*)key->data.scalar.value, "reflectivity")) {
- SETUP_MASK(REFLECTIVITY, "reflectivity");
- res = parse_real(filename, val, 0, 1, &reflectivity);
- } else if(!strcmp((char*)key->data.scalar.value, "roughness")) {
- SETUP_MASK(ROUGHNESS, "roughness");
- res = parse_real(filename, val, 0, 1, &roughness);
- } else {
- log_err(filename, key, "unknown mirror attribute `%s'.\n",
- key->data.scalar.value);
- res = RES_BAD_ARG;
- }
- if(res != RES_OK) goto error;
- #undef SETUP_MASK
- }
-
- #define CHECK_PARAM(Flag, Name) \
- if(!(mask & BIT(Flag))) { \
- log_err(filename, mirror, "the mirror "Name" is missing.\n"); \
- res = RES_BAD_ARG; \
- goto error; \
- } (void)0
- CHECK_PARAM(REFLECTIVITY, "reflectivity");
- CHECK_PARAM(ROUGHNESS, "roughness");
- #undef CHECK_PARAM
-
- /* TODO create the mirror material */
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-static res_T
-parse_material_descriptor
- (const char* filename, yaml_document_t* doc, const yaml_node_t* desc)
-{
- enum { DESCRIPTOR };
- intptr_t i, n;
- int mask = 0; /* Register the parsed attributes */
- res_T res = RES_OK;
- ASSERT(doc && desc);
-
- /* TODO If the descriptor is an alias of an already created material skip the
- * parsing and return the aliased material descriptor */
-
- if(desc->type != YAML_MAPPING_NODE) {
- log_err(filename, desc, "expect a material descriptor.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = desc->data.mapping.pairs.top - desc->data.mapping.pairs.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* key;
- yaml_node_t* val;
-
- key = yaml_document_get_node(doc, desc->data.mapping.pairs.start[i].key);
- val = yaml_document_get_node(doc, desc->data.mapping.pairs.start[i].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expect a material name.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- #define SETUP_MASK(Flag, Name) { \
- if(mask & BIT(Flag)) { \
- log_err(filename, key, "the material "Name" is already defined.\n"); \
- res = RES_BAD_ARG; \
- goto error; \
- } \
- mask |= BIT(Flag); \
- } (void)0
- if(!strcmp((char*)key->data.scalar.value, "matte")) {
- SETUP_MASK(DESCRIPTOR, "descriptor");
- res = parse_material_matte(filename, doc, val);
- } else if(!strcmp((char*)key->data.scalar.value, "mirror")) {
- SETUP_MASK(DESCRIPTOR, "descriptor");
- res = parse_material_mirror(filename, doc, val);
- } else {
- log_err(filename, key, "unknown material descriptor `%s'.\n",
- key->data.scalar.value);
- res = RES_BAD_ARG;
- goto error;
- }
- if(res != RES_OK) goto error;
- #undef SETUP_MASK
- }
-
- if(!(mask & BIT(DESCRIPTOR))) {
- log_err(filename, desc, "the material descriptor is missing.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-static res_T
-parse_material
- (const char* filename, yaml_document_t* doc, const yaml_node_t* mtl)
-{
- enum { FRONT, BACK };
- intptr_t i, n;
- int mask = 0; /* Register the parsed attributes */
- res_T res = RES_OK;
- ASSERT(doc && mtl);
-
- if(mtl->type != YAML_MAPPING_NODE) {
- log_err(filename, mtl, "expect a material definition.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = mtl->data.mapping.pairs.top - mtl->data.mapping.pairs.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* key;
- yaml_node_t* val;
-
- key = yaml_document_get_node(doc, mtl->data.mapping.pairs.start[i].key);
- val = yaml_document_get_node(doc, mtl->data.mapping.pairs.start[i].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key,
- "expect a material descriptor or a double sided material.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- #define SETUP_MASK(Flag, Name) { \
- if(mask & BIT(Flag)) { \
- log_err(filename, key, \
- "the "Name" material descriptor is already defined.\n"); \
- res = RES_BAD_ARG; \
- goto error; \
- } \
- mask |= BIT(Flag); \
- } (void)0
- if(!strcmp((char*)key->data.scalar.value, "front")) {
- SETUP_MASK(FRONT, "front");
- res = parse_material_descriptor(filename, doc, val);
- } else if(!strcmp((char*)key->data.scalar.value, "back")) {
- SETUP_MASK(BACK, "back");
- res = parse_material_descriptor(filename, doc, val);
- } else {
- SETUP_MASK(FRONT, "front");
- SETUP_MASK(BACK, "back");
- res = parse_material_descriptor(filename, doc, mtl);
- }
- if(res != RES_OK) goto error;
- #undef SETUP_MASK
- }
-
- #define CHECK_PARAM(Flag, Name) \
- if(!(mask & BIT(Flag))) { \
- log_err(filename, mtl, "the "Name" material descriptor is missing.\n"); \
- res = RES_BAD_ARG; \
- goto error; \
- } (void)0
- CHECK_PARAM(FRONT, "front");
- CHECK_PARAM(BACK, "back");
- #undef CHECK_PARAM
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-/*******************************************************************************
- * Clipping polygon
- ******************************************************************************/
-static res_T
-parse_clip_op(const char* filename, const yaml_node_t* op)
-{
- res_T res = RES_OK;
- ASSERT(op);
-
- if(op->type != YAML_SCALAR_NODE) {
- log_err(filename, op, "expect a clipping operation.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- if(!strcmp((char*)op->data.scalar.value, "AND")) { /* TODO setup op */
- } else if(!strcmp((char*)op->data.scalar.value, "SUB")) { /* TODO setup op */
- } else {
- log_err(filename, op, "unknown clipping operation `%s'.\n",
- op->data.scalar.value);
- res = RES_BAD_ARG;
- goto error;
- }
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-static res_T
-parse_vertices
- (const char* filename, yaml_document_t* doc, const yaml_node_t* vertices)
-{
- intptr_t i, n;
- res_T res = RES_OK;
- ASSERT(doc && vertices);
-
- if(vertices->type != YAML_SEQUENCE_NODE) {
- log_err(filename, vertices, "expect a list of vertices.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = vertices->data.sequence.items.top - vertices->data.sequence.items.start;
- if(n < 3) {
- log_err(filename, vertices, "expect at least 3 vertices.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- FOR_EACH(i, 0, n) {
- yaml_node_t* vertex;
- double coords[3];
-
- vertex = yaml_document_get_node(doc, vertices->data.sequence.items.start[i]);
- res = parse_real3(filename, doc, vertex, -DBL_MAX, DBL_MAX, coords);
- if(res != RES_OK) goto error;
-
- /* TODO register the vertex */
- }
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-static res_T
-parse_polyclip
- (const char* filename, yaml_document_t* doc, const yaml_node_t* polyclip)
-{
- enum { OPERATION, VERTICES };
- intptr_t i, n;
- int mask = 0; /* Register the parsed attributes */
- res_T res = RES_OK;
- ASSERT(doc && polyclip);
-
- if(polyclip->type != YAML_MAPPING_NODE) {
- log_err(filename, polyclip,
- "expect a mapping of clipping polygon parameters.\n");
- res = RES_OK;
- goto error;
- }
-
- n = polyclip->data.mapping.pairs.top - polyclip->data.mapping.pairs.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* key;
- yaml_node_t* val;
-
- key = yaml_document_get_node(doc, polyclip->data.mapping.pairs.start[i].key);
- val = yaml_document_get_node(doc, polyclip->data.mapping.pairs.start[i].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expect a clipping polygon parameter.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- #define SETUP_MASK(Flag, Name) { \
- if(mask & BIT(Flag)) { \
- log_err(filename, key, \
- "the clipping polygon parameter `"Name"' is already defined.\n"); \
- res = RES_BAD_ARG; \
- goto error; \
- } \
- mask |= BIT(Flag); \
- } (void)0
- if(!strcmp((char*)key->data.scalar.value, "operation")) {
- SETUP_MASK(OPERATION, "operation");
- res = parse_clip_op(filename, val);
- } else if(!strcmp((char*)key->data.scalar.value, "vertices")) {
- SETUP_MASK(VERTICES, "vertices");
- res = parse_vertices(filename, doc, val);
- } else {
- log_err(filename, key, "unknown clipping polygon parameter `%s'.\n",
- key->data.scalar.value);
- res = RES_BAD_ARG;
- }
- if(res != RES_OK) goto error;
- #undef SETUP_MASK
- }
-
- #define CHECK_PARAM(Flag, Name) \
- if(!(mask & BIT(Flag))) { \
- log_err(filename, polyclip, \
- "the clipping polygon parameter `"Name"' is missing"); \
- res = RES_BAD_ARG; \
- goto error; \
- } (void)0
- CHECK_PARAM(OPERATION, "operation");
- CHECK_PARAM(VERTICES, "vertices");
- #undef CHECK_PARAM
-
- /* TODO register the polyclip */
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-static res_T
-parse_clip(const char* filename, yaml_document_t* doc, const yaml_node_t* clip)
-{
- intptr_t i, n;
- res_T res = RES_OK;
- ASSERT(doc && clip);
-
- if(clip->type != YAML_SEQUENCE_NODE) {
- log_err(filename, clip, "expect a list of clipping polygons.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = clip->data.sequence.items.top - clip->data.sequence.items.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* polyclip;
-
- polyclip = yaml_document_get_node(doc, clip->data.sequence.items.start[i]);
- res = parse_polyclip(filename, doc, polyclip);
- if(res != RES_OK) goto error;
- }
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-/*******************************************************************************
- * Shapes
- ******************************************************************************/
-static res_T
-parse_cuboid
- (const char* filename, yaml_document_t* doc, const yaml_node_t* cuboid)
-{
- enum { SIZE };
- double size[3];
- intptr_t i, n;
- int mask = 0; /* Register the parsed attributes */
- res_T res = RES_OK;
- ASSERT(doc && cuboid);
-
- if(cuboid->type != YAML_MAPPING_NODE) {
- log_err(filename, cuboid, "expect a mapping of cuboid parameters.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = cuboid->data.mapping.pairs.top - cuboid->data.mapping.pairs.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* key;
- yaml_node_t* val;
-
- key = yaml_document_get_node(doc, cuboid->data.mapping.pairs.start[i].key);
- val = yaml_document_get_node(doc, cuboid->data.mapping.pairs.start[i].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expect cuboid parameters.\n");
- res = RES_BAD_ARG;
- goto error;
- }
- if(!strcmp((char*)key->data.scalar.value, "size")) {
- if(mask & BIT(SIZE)) {
- log_err(filename, key, "the cuboid size is already defined.\n");
- res = RES_BAD_ARG;
- goto error;
- }
- mask |= BIT(SIZE);
- res = parse_real3(filename, doc, val, 0, DBL_MAX, size);
- } else {
- log_err(filename, key, "unknown cuboid parameter `%s'.\n",
- key->data.scalar.value);
- res = RES_BAD_ARG;
- }
- if(res != RES_OK) goto error;
- }
-
- if(!(mask & BIT(SIZE))) {
- log_err(filename, cuboid, "the size of the cuboid is missing.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- /* TODO register the cuboid */
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-static res_T
-parse_cylinder
- (const char* filename, yaml_document_t* doc, const yaml_node_t* cylinder)
-{
- enum { HEIGHT, RADIUS, SLICES };
- double radius;
- double height;
- long nslices = 16;
- intptr_t i, n;
- int mask = 0; /* Register the parsed attributes */
- res_T res = RES_OK;
- ASSERT(doc && cylinder);
-
- if(cylinder->type != YAML_MAPPING_NODE) {
- log_err(filename, cylinder, "expect a mapping of cylinder parameters.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = cylinder->data.mapping.pairs.top - cylinder->data.mapping.pairs.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* key;
- yaml_node_t* val;
-
- key = yaml_document_get_node(doc, cylinder->data.mapping.pairs.start[i].key);
- val = yaml_document_get_node(doc, cylinder->data.mapping.pairs.start[i].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expect cylinder parameters.\n");
- res = RES_BAD_ARG;
- goto error;
- }
- #define SETUP_MASK(Flag, Name) { \
- if(mask & BIT(Flag)) { \
- log_err(filename, key, \
- "the cylinder parameter `"Name"' is already defined.\n"); \
- res = RES_BAD_ARG; \
- goto error; \
- } \
- mask |= BIT(Flag); \
- } (void)0
- if(!strcmp((char*)key->data.scalar.value, "height")) {
- SETUP_MASK(HEIGHT, "height");
- res = parse_real(filename, val, 0, DBL_MAX, &height);
- } else if(!strcmp((char*)key->data.scalar.value, "radius")) {
- SETUP_MASK(RADIUS, "radius");
- res = parse_real(filename, val, 0, DBL_MAX, &radius);
- } else if(!strcmp((char*)key->data.scalar.value, "slices")) {
- SETUP_MASK(SLICES, "slices");
- res = parse_integer(filename, val, 4, 4096, &nslices);
- } else {
- log_err(filename, key, "unknown cylinder parameter `%s'.\n",
- key->data.scalar.value);
- res = RES_BAD_ARG;
- }
- if(res != RES_OK) goto error;
- #undef SETUP_MASK
- }
-
- #define CHECK_PARAM(Flag, Name) \
- if(!(mask & BIT(Flag))) { \
- log_err(filename, cylinder, \
- "the cylinder parameter `"Name"' is missing.\n"); \
- res = RES_BAD_ARG; \
- goto error; \
- } (void)0
- CHECK_PARAM(HEIGHT, "height");
- CHECK_PARAM(RADIUS, "radius");
- #undef CHECK_PARAM
-
- /* TODO register the cylinder */
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-static res_T
-parse_imported_geometry
- (const char* filename,
- yaml_document_t* doc,
- const yaml_node_t* geom,
- const enum geometry_fileformat fileformat)
-{
- enum { PATH };
- intptr_t i, n;
- int mask = 0; /* Register the parsed attributes */
- const char* name = geometry_fileformat_name(fileformat);
- res_T res = RES_OK;
- ASSERT(doc && geom);
-
- if(geom->type != YAML_MAPPING_NODE) {
- log_err(filename, geom, "expect a mapping of %s parameters.\n", name);
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = geom->data.mapping.pairs.top - geom->data.mapping.pairs.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* key;
- yaml_node_t* val;
-
- key = yaml_document_get_node(doc, geom->data.mapping.pairs.start[i].key);
- val = yaml_document_get_node(doc, geom->data.mapping.pairs.start[i].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expect %s parameters.\n", name);
- res = RES_BAD_ARG;
- goto error;
- }
- if(!strcmp((char*)key->data.scalar.value, "path")) {
- if(mask & BIT(PATH)) {
- log_err(filename, key, "the %s path is already defined.\n", name);
- res = RES_BAD_ARG;
- goto error;
- }
- mask |= BIT(PATH);
- res = parse_string(filename, val);
- } else {
- log_err(filename, key, "unknown %s parameter `%s'.\n",
- name, key->data.scalar.value);
- res = RES_BAD_ARG;
- }
- if(res != RES_OK) goto error;
- }
-
- if(!(mask & BIT(PATH))) {
- log_err(filename, geom, "the path of the %s geometry is missing.\n", name);
- res = RES_BAD_ARG;
- goto error;
- }
-
- /* TODO Register the imported geometry */
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-static res_T
-parse_paraboloid
- (const char* filename,
- yaml_document_t* doc,
- const yaml_node_t* paraboloid,
- const enum paraboloid_type type)
-{
- enum { CLIP, FOCAL };
- double focal;
- intptr_t i, n;
- int mask = 0; /* Register the parsed attributes */
- const char* name = paraboloid_type_name(type);
- res_T res = RES_OK;
- ASSERT(doc && paraboloid);
-
- if(paraboloid->type != YAML_MAPPING_NODE) {
- log_err(filename, paraboloid, "expect a mapping of %s parameters.\n", name);
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = paraboloid->data.mapping.pairs.top - paraboloid->data.mapping.pairs.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* key;
- yaml_node_t* val;
-
- key = yaml_document_get_node(doc, paraboloid->data.mapping.pairs.start[i].key);
- val = yaml_document_get_node(doc, paraboloid->data.mapping.pairs.start[i].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expect %s parameters.\n", name);
- res = RES_BAD_ARG;
- goto error;
- }
- #define SETUP_MASK(Flag, Name) { \
- if(mask & BIT(Flag)) { \
- log_err(filename, key, \
- "the %s parameter `"Name"' is already defined.\n", name); \
- res = RES_BAD_ARG; \
- goto error; \
- } \
- mask |= BIT(Flag); \
- } (void)0
- if(!strcmp((char*)key->data.scalar.value, "clip")) {
- SETUP_MASK(CLIP, "clip");
- res = parse_clip(filename, doc, val);
- } else if(!strcmp((char*)key->data.scalar.value, "focal")) {
- SETUP_MASK(FOCAL, "focal");
- res = parse_real(filename, val, nextafter(0, 1), DBL_MAX, &focal);
- } else {
- log_err(filename, key, "unknown %s parameter `%s'.\n",
- name, key->data.scalar.value);
- res = RES_BAD_ARG;
- }
- if(res != RES_OK) goto error;
- #undef SETUP_MASK
- }
- #define CHECK_PARAM(Flag, Name) \
- if(!(mask & BIT(Flag))) { \
- log_err(filename, paraboloid, \
- "the %s parameter `"Name"' is missing.\n", name); \
- res = RES_BAD_ARG; \
- goto error; \
- } (void)0
- CHECK_PARAM(CLIP, "clip");
- CHECK_PARAM(FOCAL, "focal");
- #undef CHECK_PARAM
-
- /* TODO register the paraboloid */
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-static res_T
-parse_plane
- (const char* filename, yaml_document_t* doc, const yaml_node_t* plane)
-{
- enum { CLIP };
- intptr_t i, n;
- int mask = 0; /* Register the parsed attributes */
- res_T res = RES_OK;
- ASSERT(doc && plane);
-
- if(plane->type != YAML_MAPPING_NODE) {
- log_err(filename, plane, "expect a mapping of plane parameters.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = plane->data.mapping.pairs.top - plane->data.mapping.pairs.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* key;
- yaml_node_t* val;
-
- key = yaml_document_get_node(doc, plane->data.mapping.pairs.start[i].key);
- val = yaml_document_get_node(doc, plane->data.mapping.pairs.start[i].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expect plane parameters.\n");
- res = RES_BAD_ARG;
- goto error;
- }
- if(!strcmp((char*)key->data.scalar.value, "clip")) {
- if(mask & BIT(CLIP)) {
- log_err(filename, key, "the plane clipping is already defined.\n");
- res = RES_BAD_ARG;
- goto error;
- }
- mask |= BIT(CLIP);
- res = parse_clip(filename, doc, val);
- } else {
- log_err(filename, key, "unknown plane parameter `%s'.\n",
- key->data.scalar.value);
- res = RES_BAD_ARG;
- }
- if(res != RES_OK) goto error;
- }
- if(!(mask & BIT(CLIP))) {
- log_err(filename, plane, "the plane parameter `clip' is missing.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- /* TODO register the plane */
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-static res_T
-parse_sphere
- (const char* filename, yaml_document_t* doc, const yaml_node_t* sphere)
-{
- enum { RADIUS, SLICES };
- double radius;
- long nslices;
- intptr_t i, n;
- int mask = 0; /* Register the parsed attributes */
- res_T res = RES_OK;
- ASSERT(doc && sphere);
-
- if(sphere->type != YAML_MAPPING_NODE) {
- log_err(filename, sphere, "expect a mapping of sphere parameters.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = sphere->data.mapping.pairs.top - sphere->data.mapping.pairs.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* key;
- yaml_node_t* val;
-
- key = yaml_document_get_node(doc, sphere->data.mapping.pairs.start[i].key);
- val = yaml_document_get_node(doc, sphere->data.mapping.pairs.start[i].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expect sphere parameters.\n");
- res = RES_BAD_ARG;
- goto error;
- }
- #define SETUP_MASK(Flag, Name) { \
- if(mask & BIT(Flag)) { \
- log_err(filename, key, \
- "the sphere parameter `"Name"' is already defined.\n"); \
- res = RES_BAD_ARG; \
- goto error; \
- } \
- mask |= BIT(Flag); \
- } (void)0
- if(!strcmp((char*)key->data.scalar.value, "radius")) {
- SETUP_MASK(RADIUS, "radius");
- res = parse_real(filename, val, 0, DBL_MAX, &radius);
- } else if(!strcmp((char*)key->data.scalar.value, "slices")) {
- SETUP_MASK(SLICES, "slices");
- res = parse_integer(filename, val, 4, 4096, &nslices);
- } else {
- log_err(filename, key, "unknown sphere parameter `%s'.\n",
- key->data.scalar.value);
- res = RES_BAD_ARG;
- }
- if(res != RES_OK) goto error;
- #undef SETUP_MASK
- }
-
- if(!(mask & BIT(RADIUS))) {
- log_err(filename, sphere, "the sphere radius is missing.\n");
- res = RES_BAD_ARG;
- goto error;
- }
- /* TODO register the sphere */
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-/*******************************************************************************
- * Object
- ******************************************************************************/
-res_T
-parse_object
- (const char* filename, yaml_document_t* doc, const yaml_node_t* object)
-{
- enum { MATERIAL, SHAPE, TRANSFORM };
- double translation[3] = {0, 0, 0};
- double rotation[3] = {0, 0, 0};
- intptr_t i, n;
- int mask = 0; /* Register the parsed attributes */
- res_T res = RES_OK;
- ASSERT(doc && object);
-
- /* TODO If the object is an alias of an already created object skip the
- * parsing and return the aliased object */
-
- if(object->type != YAML_MAPPING_NODE) {
- log_err(filename, object, "expect an object definition.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = object->data.mapping.pairs.top - object->data.mapping.pairs.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* key;
- yaml_node_t* val;
-
- key = yaml_document_get_node(doc, object->data.mapping.pairs.start[i].key);
- val = yaml_document_get_node(doc, object->data.mapping.pairs.start[i].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expect an object parameter.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- #define SETUP_MASK(Flag, Name) { \
- if(mask & BIT(Flag)) { \
- log_err(filename, key, \
- "the object "Name" is already defined.\n"); \
- res = RES_BAD_ARG; \
- goto error; \
- } \
- mask |= BIT(Flag); \
- } (void)0
- if(!strcmp((char*)key->data.scalar.value, "material")) {
- SETUP_MASK(MATERIAL, "material");
- res = parse_material(filename, doc, val);
- } else if(!strcmp((char*)key->data.scalar.value, "cuboid")) {
- SETUP_MASK(SHAPE, "shape");
- res = parse_cuboid(filename, doc, val);
- } else if(!strcmp((char*)key->data.scalar.value, "cylinder")) {
- SETUP_MASK(SHAPE, "shape");
- res = parse_cylinder(filename, doc, val);
- } else if(!strcmp((char*)key->data.scalar.value, "obj")) {
- SETUP_MASK(SHAPE, "shape");
- res = parse_imported_geometry(filename, doc, val, GEOMETRY_OBJ);
- } else if(!strcmp((char*)key->data.scalar.value, "parabol")) {
- SETUP_MASK(SHAPE, "shape");
- res = parse_paraboloid(filename, doc, val, PARABOL);
- } else if(!strcmp((char*)key->data.scalar.value, "parabolic-cylinder")) {
- SETUP_MASK(SHAPE, "shape");
- res = parse_paraboloid(filename, doc, val, PARABOLIC_CYLINDER);
- } else if(!strcmp((char*)key->data.scalar.value, "plane")) {
- SETUP_MASK(SHAPE, "shape");
- res = parse_plane(filename, doc, val);
- } else if(!strcmp((char*)key->data.scalar.value, "sphere")) {
- SETUP_MASK(SHAPE, "shape");
- res = parse_sphere(filename, doc, val);
- } else if(!strcmp((char*)key->data.scalar.value, "stl")) {
- SETUP_MASK(SHAPE, "shape");
- res = parse_imported_geometry(filename, doc, val, GEOMETRY_STL);
- } else if(!strcmp((char*)key->data.scalar.value, "transform")) {
- SETUP_MASK(TRANSFORM, "transform");
- res = parse_transform(filename, doc, val, translation, rotation);
- } else {
- log_err(filename, key, "unknown object parameter `%s'.\n",
- key->data.scalar.value);
- res = RES_BAD_ARG;
- }
- if(res != RES_OK) goto error;
- #undef SETUP_MASK
- }
-
- #define CHECK_PARAM(Flag, Name) \
- if(!(mask & BIT(Flag))) { \
- log_err(filename, object, "the object "Name" is missing.\n"); \
- res = RES_BAD_ARG; \
- goto error; \
- } (void)0
- CHECK_PARAM(MATERIAL, "material");
- CHECK_PARAM(SHAPE, "shape");
- #undef CHECK_PARAM
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-/*******************************************************************************
- * Node
- ******************************************************************************/
-static res_T
-parse_children
- (const char* filename, yaml_document_t* doc, const yaml_node_t* children)
-{
- intptr_t i, n;
- res_T res = RES_OK;
- ASSERT(doc && children);
-
- if(children->type != YAML_SEQUENCE_NODE) {
- log_err(filename, children, "expect a list of nodes.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = children->data.sequence.items.top - children->data.sequence.items.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* child;
- yaml_node_t* key;
- yaml_node_t* val;
- intptr_t nb;
-
- child = yaml_document_get_node(doc, children->data.sequence.items.start[i]);
- if(child->type != YAML_MAPPING_NODE) {
- log_err(filename, child, "expect a node definition.\n");
- res = RES_BAD_ARG;
- goto error;
- }
- nb = child->data.mapping.pairs.top - child->data.mapping.pairs.start;
- if(nb != 1) {
- log_err(filename, child,
- "expect only one \"key:value\" pair while %li are provided.\n", nb);
- res = RES_BAD_ARG;
- goto error;
- }
-
- key = yaml_document_get_node(doc, child->data.mapping.pairs.start[0].key);
- val = yaml_document_get_node(doc, child->data.mapping.pairs.start[0].value);
- if(!strcmp((char*)key->data.scalar.value, "node")) {
- res = parse_node(filename, doc, val);
- } else {
- log_err(filename, key, "unexpected directive `%s'. Expect a node.\n",
- key->data.scalar.value);
- res = RES_BAD_ARG;
- }
- if(res != RES_OK) goto error;
-
- /* TODO register the child node */
- }
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-static res_T
-parse_entities
- (const char* filename, yaml_document_t* doc, const yaml_node_t* entities)
-{
- intptr_t i, n;
- res_T res = RES_OK;
- ASSERT(doc && entities);
-
- if(entities->type != YAML_SEQUENCE_NODE) {
- log_err(filename, entities, "expect a list of entities.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = entities->data.sequence.items.top - entities->data.sequence.items.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* entity;
- yaml_node_t* key;
- yaml_node_t* val;
- intptr_t nb;
-
- entity = yaml_document_get_node(doc, entities->data.sequence.items.start[i]);
-
- if(entity->type != YAML_MAPPING_NODE) {
- log_err(filename, entity, "expect an entity definition.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- nb = entity->data.mapping.pairs.top - entity->data.mapping.pairs.start;
- if(nb != 1) {
- log_err(filename, entity,
- "expect only one \"key:value\" pair while %li are provided.\n", nb);
- res = RES_BAD_ARG;
- goto error;
- }
-
- key = yaml_document_get_node(doc, entity->data.mapping.pairs.start[0].key);
- val = yaml_document_get_node(doc, entity->data.mapping.pairs.start[0].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expect an entity name.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- if(!strcmp((char*)key->data.scalar.value, "object")) {
- res = parse_object(filename, doc, val);
- } else if(!strcmp((char*)key->data.scalar.value, "pivot")) {
- res = parse_pivot(filename, doc, val);
- } else {
- log_err(filename, key, "unknown entity `%s'.\n", key->data.scalar.value);
- res = RES_BAD_ARG;
- }
- if(res != RES_OK) goto error;
-
- /* TODO register the entity */
- }
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-res_T
-parse_node(const char* filename, yaml_document_t* doc, const yaml_node_t* node)
-{
- enum { CHILDREN, ENTITIES, TRANSFORM };
- double translation[3] = {0, 0, 0};
- double rotation[3] = {0, 0, 0};
- intptr_t i, n;
- int mask = 0; /* Register the parsed attributes */
- res_T res = RES_OK;
- ASSERT(doc && node);
-
- if(node->type != YAML_MAPPING_NODE) {
- log_err(filename, node, "expect a node definition.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = node->data.mapping.pairs.top - node->data.mapping.pairs.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* key;
- yaml_node_t* val;
-
- key = yaml_document_get_node(doc, node->data.mapping.pairs.start[i].key);
- val = yaml_document_get_node(doc, node->data.mapping.pairs.start[i].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expect a node attribute.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- #define SETUP_MASK(Flag, Name) { \
- if(mask & BIT(Flag)) { \
- log_err(filename, key, \
- "the node attribute `"Name"' is already defined.\n"); \
- res = RES_BAD_ARG; \
- goto error; \
- } \
- mask |= BIT(Flag); \
- } (void)0
- if(!strcmp((char*)key->data.scalar.value, "children")) {
- SETUP_MASK(CHILDREN, "children");
- res = parse_children(filename, doc, val);
- } else if(!strcmp((char*)key->data.scalar.value, "entities")) {
- SETUP_MASK(ENTITIES, "entities");
- res = parse_entities(filename, doc, val);
- } else if(!strcmp((char*)key->data.scalar.value, "transform")) {
- SETUP_MASK(TRANSFORM, "transform");
- res = parse_transform(filename, doc, val, translation, rotation);
- } else {
- log_err(filename, key, "unknown node parameter `%s'.\n",
- key->data.scalar.value);
- res = RES_BAD_ARG;
- goto error;
- }
- if(res != RES_OK) goto error;
- #undef SETUP_MASK
- }
-
- #define CHECK_PARAM(Flag, Name) \
- if(!(mask & BIT(Flag))) { \
- log_err(filename, node, "the node `"Name"' parameter is missing.\n"); \
- res = RES_BAD_ARG; \
- goto error; \
- } (void)0
- CHECK_PARAM(ENTITIES, "entities");
- #undef CHECK_PARAM
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-/*******************************************************************************
- * Pivot
- ******************************************************************************/
-static res_T
-parse_target
- (const char* filename, yaml_document_t* doc, const yaml_node_t* target)
-{
- enum { POLICY };
- double direction[3];
- double position[3];
- intptr_t i, n;
- int mask = 0; /* Register the parsed attributes */
- res_T res = RES_OK;
- ASSERT(doc && target);
-
- if(target->type != YAML_MAPPING_NODE) {
- log_err(filename, target, "expect a target definition.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = target->data.mapping.pairs.top - target->data.mapping.pairs.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* key;
- yaml_node_t* val;
-
- key = yaml_document_get_node(doc, target->data.mapping.pairs.start[i].key);
- val = yaml_document_get_node(doc, target->data.mapping.pairs.start[i].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expect a target parameter.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- #define SETUP_MASK(Flag, Name) { \
- if(mask & BIT(Flag)) { \
- log_err(filename, key, "the target "Name" is already defined.\n"); \
- res = RES_BAD_ARG; \
- goto error; \
- } \
- mask |= BIT(Flag); \
- } (void)0
- if(!strcmp((char*)key->data.scalar.value, "direction")) {
- SETUP_MASK(POLICY, "policy");
- res = parse_real3(filename, doc, val, -DBL_MAX, DBL_MAX, direction);
- } else if(!strcmp((char*)key->data.scalar.value, "position")) {
- SETUP_MASK(POLICY, "policy");
- res = parse_real3(filename, doc, val, -DBL_MAX, DBL_MAX, position);
- } else if(!strcmp((char*)key->data.scalar.value, "sun")) {
- SETUP_MASK(POLICY, "policy");
- res = parse_sun(filename, doc, val);
- } else {
- log_err(filename, key, "unknown target parameter `%s'.\n",
- key->data.scalar.value);
- res = RES_BAD_ARG;
- goto error;
- }
- if(res != RES_OK) goto error;
- #undef SETUP_MASK
- }
-
- if(!(mask & BIT(POLICY))) {
- log_err(filename, target, "the target policy is missing.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-static res_T
-parse_pivot
- (const char* filename, yaml_document_t* doc, const yaml_node_t* pivot)
-{
- enum { NORMAL, POINT, TARGET, TRANSFORM };
- double point[3];
- double normal[3];
- double translation[3] = {0, 0, 0};
- double rotation[3] = {0, 0, 0};
- int mask = 0; /* Register the parsed attributes */
- intptr_t i, n;
- res_T res = RES_OK;
- ASSERT(doc && pivot);
-
- if(pivot->type != YAML_MAPPING_NODE) {
- log_err(filename, pivot, "expect a pivot definition.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = pivot->data.mapping.pairs.top - pivot->data.mapping.pairs.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* key;
- yaml_node_t* val;
-
- key = yaml_document_get_node(doc, pivot->data.mapping.pairs.start[i].key);
- val = yaml_document_get_node(doc, pivot->data.mapping.pairs.start[i].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expect pivot parameters.\n");
- res = RES_BAD_ARG;
- goto error;
- }
- #define SETUP_MASK(Flag, Name) { \
- if(mask & BIT(Flag)) { \
- log_err(filename, key, \
- "the pivot parameter `"Name"' is already defined.\n"); \
- res = RES_BAD_ARG; \
- goto error; \
- } \
- mask |= BIT(Flag); \
- } (void)0
- if(!strcmp((char*)key->data.scalar.value, "point")) {
- SETUP_MASK(POINT, "point");
- res = parse_real3(filename, doc, val, -DBL_MAX, DBL_MAX, point);
- } else if(!strcmp((char*)key->data.scalar.value, "normal")) {
- SETUP_MASK(NORMAL, "normal");
- res = parse_real3(filename, doc, val, -DBL_MAX, DBL_MAX, normal);
- } else if(!strcmp((char*)key->data.scalar.value, "target")) {
- SETUP_MASK(TARGET, "target");
- res = parse_target(filename, doc, val);
- } else if(!strcmp((char*)key->data.scalar.value, "transform")) {
- SETUP_MASK(TRANSFORM, "transform");
- res = parse_transform(filename, doc, val, translation, rotation);
- } else {
- log_err(filename, key, "unknown pivot parameter `%s'.\n",
- key->data.scalar.value);
- res = RES_BAD_ARG;
- }
- if(res != RES_OK) goto error;
- #undef SETUP_MASK
- }
-
- #define CHECK_PARAM(Flag, Name) \
- if(!(mask & BIT(Flag))) { \
- log_err(filename, pivot, "the pivot parameter `"Name"' is missing.\n"); \
- res = RES_BAD_ARG; \
- goto error; \
- } (void)0
- CHECK_PARAM(POINT, "point");
- CHECK_PARAM(NORMAL, "normal");
- #undef CHECK_PARAM
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-/*******************************************************************************
- * Sun
- ******************************************************************************/
-static res_T
-parse_buie(const char* filename, yaml_document_t* doc, const yaml_node_t* buie)
-{
- enum { CSR };
- intptr_t i, n;
- double csr;
- int mask = 0; /* Register the parsed attributes */
- res_T res = RES_OK;
- ASSERT(doc && buie);
-
- if(buie->type != YAML_MAPPING_NODE) {
- log_err(filename, buie,
- "expect a buie definition of the sun radial angular distribution.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = buie->data.mapping.pairs.top - buie->data.mapping.pairs.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* key;
- yaml_node_t* val;
-
- key = yaml_document_get_node(doc, buie->data.mapping.pairs.start[i].key);
- val = yaml_document_get_node(doc, buie->data.mapping.pairs.start[i].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expect a buie parameter.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- if(!strcmp((char*)key->data.scalar.value, "csr")) {
- if(mask & BIT(CSR)) {
- log_err(filename, key, "the buie `csr' is already defined.\n");
- res = RES_BAD_ARG;
- goto error;
- }
- mask |= BIT(CSR);
- res = parse_real(filename, val, nextafter(0, 1), nextafter(1, 0), &csr);
- } else {
- log_err(filename, key, "unknown buie parameter `%s'.\n",
- key->data.scalar.value);
- res = RES_BAD_ARG;
- }
- if(res != RES_OK) goto error;
- }
-
- if(!(mask & BIT(CSR))) {
- log_err(filename, buie, "the buie csr parameter is missing.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-static res_T
-parse_pillbox
- (const char* filename, yaml_document_t* doc, const yaml_node_t* pillbox)
-{
- enum { APERTURE };
- intptr_t i, n;
- double aperture;
- int mask = 0; /* Register the parsed attributes */
- res_T res = RES_OK;
- ASSERT(doc && pillbox);
-
- if(pillbox->type != YAML_MAPPING_NODE) {
- log_err(filename, pillbox,
- "expect a pillbox definition of the sun radial angular distribution.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = pillbox->data.mapping.pairs.top - pillbox->data.mapping.pairs.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* key = key;
- yaml_node_t* val = val;
-
- key = yaml_document_get_node(doc, pillbox->data.mapping.pairs.start[i].key);
- val = yaml_document_get_node(doc, pillbox->data.mapping.pairs.start[i].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expect a pillbox parameter.\n");
- res = RES_BAD_ARG;
- goto error;
- }
- if(!strcmp((char*)key->data.scalar.value, "aperture")) {
- if(mask & BIT(APERTURE)) {
- log_err(filename, key, "the pillbox `aperture' is already defined.\n");
- res = RES_BAD_ARG;
- goto error;
- }
- mask |= BIT(APERTURE);
- res = parse_real(filename, val, nextafter(0, 1), PI/2.0, &aperture);
- } else {
- log_err(filename, pillbox, "unknown pillbox parameter `%s'.\n",
- key->data.scalar.value);
- res = RES_BAD_ARG;
- }
- if(res != RES_OK) goto error;
- }
-
- if(!(mask & BIT(APERTURE))) {
- log_err(filename, pillbox, "the pillbox aperture parameter is missing.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-res_T
-parse_sun(const char* filename, yaml_document_t* doc, const yaml_node_t* sun)
-{
- enum { DNI, RADIAL_ANGULAR_DISTRIB, SPECTRUM };
- double dni;
- intptr_t i, n;
- int mask = 0; /* Register the parsed attributes */
- res_T res = RES_OK;
- ASSERT(doc && sun);
-
- if(sun->type != YAML_MAPPING_NODE) {
- log_err(filename, sun, "expect a sun definition.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = sun->data.mapping.pairs.top - sun->data.mapping.pairs.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* key;
- yaml_node_t* val;
-
- key = yaml_document_get_node(doc, sun->data.mapping.pairs.start[i].key);
- val = yaml_document_get_node(doc, sun->data.mapping.pairs.start[i].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expect sun parameters.\n");
- res = RES_BAD_ARG;
- goto error;
- }
- #define SETUP_MASK(Flag, Name) { \
- if(mask & BIT(Flag)) { \
- log_err(filename, key, "the sun "Name" is already defined.\n"); \
- res = RES_BAD_ARG; \
- goto error; \
- } \
- mask |= BIT(Flag); \
- } (void)0
- if(!strcmp((char*)key->data.scalar.value, "dni")) {
- SETUP_MASK(DNI, "dni");
- res = parse_real(filename, val, nextafter(0, 1), DBL_MAX, &dni);
- } else if(!strcmp((char*)key->data.scalar.value, "buie")) {
- SETUP_MASK(RADIAL_ANGULAR_DISTRIB, "radial angular distribution");
- res = parse_buie(filename, doc, val);
- } else if(!strcmp((char*)key->data.scalar.value, "pillbox")) {
- SETUP_MASK(RADIAL_ANGULAR_DISTRIB, "radial angular distribution");
- res = parse_pillbox(filename, doc, val);
- } else if(!strcmp((char*)key->data.scalar.value, "spectrum")) {
- SETUP_MASK(SPECTRUM, "spectrum");
- res = parse_spectrum(filename, doc, 0, DBL_MAX, val);
- } else {
- log_err(filename, key, "unknown sun parameter `%s'.\n",
- key->data.scalar.value);
- res = RES_BAD_ARG;
- }
- if(res != RES_OK) goto error;
- #undef SETUP_MASK
- }
-
- #define CHECK_PARAM(Flag, Name) \
- if(!(mask & BIT(Flag))) { \
- log_err(filename, sun, "the sun "Name" is missing.\n"); \
- res = RES_BAD_ARG; \
- goto error; \
- } (void)0
- CHECK_PARAM(DNI, "dni");
- CHECK_PARAM(SPECTRUM, "spectrum");
- #undef CHECK_PARAM
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-/*******************************************************************************
- * Item
- ******************************************************************************/
-static res_T
-parse_item
- (const char* filename, yaml_document_t* doc, const yaml_node_t* item)
-{
- yaml_node_t* key;
- yaml_node_t* val;
- intptr_t n;
- res_T res = RES_OK;
- ASSERT(doc && item);
-
- if(item->type != YAML_MAPPING_NODE) {
- log_err(filename, item, "expect an item definition.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = item->data.mapping.pairs.top - item->data.mapping.pairs.start;
- if(n != 1) {
- log_err(filename, item,
- "expect only one \"key:value\" pair while %li are provided.\n", n);
- res = RES_BAD_ARG;
- goto error;
- }
-
- key = yaml_document_get_node(doc, item->data.mapping.pairs.start[0].key);
- val = yaml_document_get_node(doc, item->data.mapping.pairs.start[0].value);
- if(key->type != YAML_SCALAR_NODE) {
- log_err(filename, key, "expecting an item name.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- if(!strcmp((char*)key->data.scalar.value, "instance")) {
- res = parse_instance(filename, doc, val);
- } else if(!strcmp((char*)key->data.scalar.value, "material")) {
- res = parse_material(filename, doc, val);
- } else if(!strcmp((char*)key->data.scalar.value, "node")) {
- res = parse_node(filename, doc, val);
- } else if(!strcmp((char*)key->data.scalar.value, "object")) {
- res = parse_object(filename, doc, val);
- } else if(!strcmp((char*)key->data.scalar.value, "pivot")) {
- res = parse_pivot(filename, doc, val);
- } else if(!strcmp((char*)key->data.scalar.value, "sun")) {
- res = parse_sun(filename, doc, val);
- } else {
- log_err(filename, key, "unknown item `%s'.\n", key->data.scalar.value);
- res = RES_BAD_ARG;
- }
- if(res != RES_OK) goto error;
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-/*******************************************************************************
- * Local functions
- ******************************************************************************/
-res_T
-solstice_facility_load(const char* filename)
-{
- yaml_parser_t parser;
- yaml_document_t doc;
- yaml_node_t* root;
- FILE* file = NULL;
- intptr_t i, n;
- int doc_is_init = 0;
- int is_empty = 1;
- res_T res = RES_OK;
-
- if(!yaml_parser_initialize(&parser)) {
- fprintf(stderr, "Could not initialise the YAML parser.\n");
- return RES_UNKNOWN_ERR;
- }
-
- file = fopen(filename, "rb");
- if(!file) {
- fprintf(stderr, "Could not open the YAML file `%s'.\n", filename);
- res = RES_IO_ERR;
- goto error;
- }
-
- yaml_parser_set_input_file(&parser, file);
-
- for(;;) {
- if(!yaml_parser_load(&parser, &doc)) {
- fprintf(stderr, "%s:%lu:%lu: %s.\n",
- filename,
- (unsigned long)parser.problem_mark.line+1,
- (unsigned long)parser.problem_mark.column+1,
- parser.problem);
- res = RES_IO_ERR;
- goto error;
- }
- doc_is_init = 1;
-
- root = yaml_document_get_root_node(&doc);
- if(!root) {
- if(!is_empty) {
- break;
- } else {
- fprintf(stderr, "The file `%s' seems empty.\n", filename);
- res = RES_BAD_ARG;
- goto error;
- }
- }
-
- is_empty = 0;
- if(root->type != YAML_SEQUENCE_NODE) {
- log_err(filename, root, "expect a list of items.\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- n = root->data.sequence.items.top - root->data.sequence.items.start;
- FOR_EACH(i, 0, n) {
- yaml_node_t* item;
-
- item = yaml_document_get_node(&doc, root->data.sequence.items.start[i]);
- res = parse_item(filename, &doc, item);
- if(res != RES_OK) goto error;
- }
-
- yaml_document_delete(&doc);
- doc_is_init = 0;
- }
-
-exit:
- yaml_parser_delete(&parser);
- if(doc_is_init) yaml_document_delete(&doc);
- if(file) fclose(file);
- return res;
-error:
- goto exit;
-}
-
diff --git a/src/solstice_facility.h b/src/solstice_facility.h
@@ -1,26 +0,0 @@
-/* Copyright (C) CNRS 2016
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#ifndef SOLSTICE_FACILITY_H
-#define SOLSTICE_FACILITY_H
-
-#include <rsys/rsys.h>
-
-extern LOCAL_SYM res_T
-solstice_facility_load
- (const char* filename);
-
-#endif /* SOLSTICE_FACILITY_H */
-
diff --git a/src/solstice_parser.c b/src/solstice_parser.c
@@ -0,0 +1,2196 @@
+/* Copyright (C) CNRS 2016
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#define _POSIX_C_SOURCE 200112L /* nextafter support */
+
+#include "solstice_parser.h"
+
+#include <rsys/cstr.h>
+#include <rsys/double3.h>
+#include <rsys/ref_count.h>
+#include <rsys/str.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <yaml.h>
+
+struct solstice_parser {
+ yaml_parser_t parser;
+ struct str stream_name;
+ int parser_is_init;
+
+ ref_T ref;
+ struct mem_allocator* allocator;
+};
+
+enum paraboloid_type {
+ PARABOL,
+ PARABOLIC_CYLINDER
+};
+
+enum geometry_fileformat {
+ GEOMETRY_OBJ,
+ GEOMETRY_STL
+};
+
+static res_T
+parse_node
+ (const char* filename,
+ yaml_document_t* doc,
+ const yaml_node_t* children);
+
+static res_T
+parse_object
+ (const char* filename,
+ yaml_document_t* doc,
+ const yaml_node_t* object);
+
+static res_T
+parse_pivot
+ (const char* filename,
+ yaml_document_t* doc,
+ const yaml_node_t* pivot);
+
+static res_T
+parse_sun
+ (const char* filename,
+ yaml_document_t* doc,
+ const yaml_node_t* sun);
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+static INLINE void
+log_err
+ (const char* filename,
+ const yaml_node_t* node,
+ const char* fmt,
+ ...)
+{
+ va_list vargs_list;
+ ASSERT(filename && node && fmt);
+
+ fprintf(stderr, "%s:%lu:%lu: ",
+ filename,
+ (unsigned long)node->start_mark.line+1,
+ (unsigned long)node->start_mark.column+1);
+ va_start(vargs_list, fmt);
+ vfprintf(stderr, fmt, vargs_list);
+ va_end(vargs_list);
+}
+
+static INLINE const char*
+paraboloid_type_name(const enum paraboloid_type type)
+{
+ switch(type) {
+ case PARABOL: return "parabol"; break;
+ case PARABOLIC_CYLINDER: return "parabolic cylinder"; break;
+ default: FATAL("Unreachable code.\n"); break;
+ }
+}
+
+static INLINE const char*
+geometry_fileformat_name(const enum geometry_fileformat fileformat)
+{
+ switch(fileformat) {
+ case GEOMETRY_OBJ: return "obj"; break;
+ case GEOMETRY_STL: return "stl"; break;
+ default: FATAL("Unreachable code.\n"); break;
+ }
+}
+
+static void
+parser_release(ref_T* ref)
+{
+ struct solstice_parser* parser;
+ ASSERT(ref);
+
+ parser = CONTAINER_OF(ref, struct solstice_parser, ref);
+ if(parser->parser_is_init) yaml_parser_delete(&parser->parser);
+ str_release(&parser->stream_name);
+ MEM_RM(parser->allocator, parser);
+}
+
+/*******************************************************************************
+ * Miscellaneous parsing functions
+ ******************************************************************************/
+static res_T
+parse_real
+ (const char* filename,
+ const yaml_node_t* real,
+ const double lower_bound,
+ const double upper_bound,
+ double* dst)
+{
+ res_T res = RES_OK;
+ ASSERT(real && dst && lower_bound < upper_bound);
+
+ if(real->type != YAML_SCALAR_NODE) {
+ log_err(filename, real, "expect a floating point number.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ res = cstr_to_double((char*)real->data.scalar.value, dst);
+ if(res != RES_OK) {
+ log_err(filename, real, "invalid floating point number `%s'.\n",
+ real->data.scalar.value);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if(*dst < lower_bound || *dst > upper_bound) {
+ log_err(filename, real, "%g is not in [%g, %g].\n",
+ *dst, lower_bound, upper_bound);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+parse_real3
+ (const char* filename,
+ yaml_document_t* doc,
+ const yaml_node_t* real3,
+ const double lower_bound,
+ const double upper_bound,
+ double dst[3])
+{
+ intptr_t i, n;
+ res_T res = RES_OK;
+ ASSERT(doc && real3 && dst);
+
+ if(real3->type != YAML_SEQUENCE_NODE) {
+ log_err(filename, real3, "expect a sequence of 3 reals.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = real3->data.sequence.items.top - real3->data.sequence.items.start;
+ if(n != 3) {
+ log_err(filename, real3, "expect 3 reals while `%li' %s submitted.\n",
+ n, n > 1 ? "are" : "is");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* real;
+ real = yaml_document_get_node(doc, real3->data.sequence.items.start[i]);
+ res = parse_real(filename, real, lower_bound, upper_bound, dst + i);
+ if(res != RES_OK) goto error;
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+parse_integer
+ (const char* filename,
+ yaml_node_t* integer,
+ const long lower_bound,
+ const long upper_bound,
+ long* dst)
+{
+ res_T res = RES_OK;
+ ASSERT(integer && dst && lower_bound < upper_bound);
+
+ if(integer->type != YAML_SCALAR_NODE) {
+ log_err(filename, integer, "expect an integer.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ res = cstr_to_long((char*)integer->data.scalar.value, dst);
+ if(res != RES_OK) {
+ log_err(filename, integer, "invalid integer `%s'.\n",
+ integer->data.scalar.value);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if(*dst < lower_bound || *dst > upper_bound) {
+ log_err(filename, integer, "%li is not in [%li, %li].\n",
+ *dst, lower_bound, upper_bound);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+parse_string(const char* filename, yaml_node_t* string)
+{
+ res_T res = RES_OK;
+ ASSERT(string);
+
+ if(string->type != YAML_SCALAR_NODE) {
+ log_err(filename, string, "expect a character string.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ /* TODO create a string from the value of the YAML scalar node */
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+parse_transform
+ (const char* filename,
+ yaml_document_t* doc,
+ const yaml_node_t* transform,
+ double translation[3],
+ double rotation[3])
+{
+ enum { ROTATION, TRANSLATION };
+ intptr_t i, n;
+ int mask = 0; /* Register the parsed attributes */
+ res_T res = RES_OK;
+ ASSERT(doc && translation && rotation && transform);
+
+ d3_splat(translation, 0);
+ d3_splat(rotation, 0);
+
+ if(transform->type != YAML_MAPPING_NODE) {
+ log_err(filename, transform, "expect a mapping of transform parameters.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = transform->data.mapping.pairs.top - transform->data.mapping.pairs.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* key;
+ yaml_node_t* val;
+
+ key = yaml_document_get_node(doc, transform->data.mapping.pairs.start[i].key);
+ val = yaml_document_get_node(doc, transform->data.mapping.pairs.start[i].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expect transform parameters.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ #define SETUP_MASK(Flag, Name) { \
+ if(mask & BIT(Flag)) { \
+ log_err(filename, key, "the transform `"Name"' is already defined.\n");\
+ res = RES_BAD_ARG; \
+ goto error; \
+ } \
+ mask |= BIT(Flag); \
+ } (void)0
+ if(!strcmp((char*)key->data.scalar.value, "translation")) {
+ SETUP_MASK(TRANSLATION, "translation");
+ res = parse_real3(filename, doc, val, -DBL_MAX, DBL_MAX, translation);
+ } else if(!strcmp((char*)key->data.scalar.value, "rotation")) {
+ SETUP_MASK(ROTATION, "rotation");
+ res = parse_real3(filename, doc, val, -DBL_MAX, DBL_MAX, rotation);
+ } else {
+ log_err(filename, key, "unknown transform parameter `%s'.\n",
+ key->data.scalar.value);
+ res = RES_BAD_ARG;
+ }
+ if(res != RES_OK) goto error;
+ #undef SETUP_MASK
+ }
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+parse_spectrum_data
+ (const char* filename, yaml_document_t* doc,
+ const double lower_bound,
+ const double upper_bound,
+ const yaml_node_t* sdata)
+{
+ enum { DATA, WAVELENGTH };
+ double wavelength;
+ double data;
+ intptr_t i, n;
+ int mask = 0; /* Register the parsed attributes */
+ res_T res = RES_OK;
+ ASSERT(doc && data && lower_bound < upper_bound);
+
+ if(sdata->type != YAML_MAPPING_NODE) {
+ log_err(filename, sdata, "expect the definition of a spectrum data.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = sdata->data.mapping.pairs.top - sdata->data.mapping.pairs.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* key;
+ yaml_node_t* val;
+
+ key = yaml_document_get_node(doc, sdata->data.mapping.pairs.start[i].key);
+ val = yaml_document_get_node(doc, sdata->data.mapping.pairs.start[i].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expect a spectrum data parameter.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ #define SETUP_MASK(Flag, Name) { \
+ if(mask & BIT(Flag)) { \
+ log_err(filename, key, \
+ "the `"Name"' of the spectrum data is already defined.\n"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } \
+ mask |= BIT(Flag); \
+ } (void)0
+ if(!strcmp((char*)key->data.scalar.value, "data")) {
+ SETUP_MASK(DATA, "data");
+ res = parse_real(filename, val, lower_bound, upper_bound, &data);
+ } else if(!strcmp((char*)key->data.scalar.value, "wavelength")) {
+ SETUP_MASK(WAVELENGTH, "wavelength");
+ res = parse_real(filename, val, 0, DBL_MAX, &wavelength);
+ } else {
+ log_err(filename, key, "unknown spectrum data parameter `%s'.\n",
+ key->data.scalar.value);
+ res = RES_BAD_ARG;
+ }
+ if(res != RES_OK) goto error;
+ #undef SETUP_MASK
+ }
+
+ #define CHECK_PARAM(Flag, Name) \
+ if(!(mask & BIT(Flag))) { \
+ log_err(filename, sdata,"the "Name" of the spectrum data is missing.\n");\
+ res = RES_BAD_ARG; \
+ goto error; \
+ } (void)0
+ CHECK_PARAM(DATA, "data");
+ CHECK_PARAM(WAVELENGTH, "wavelength");
+ #undef CHECK_PARAM
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+parse_spectrum
+ (const char* filename,
+ yaml_document_t* doc,
+ const double lower_bound,
+ const double upper_bound,
+ const yaml_node_t* spectrum)
+{
+ intptr_t i, n;
+ res_T res = RES_OK;
+ ASSERT(doc && spectrum && lower_bound < upper_bound);
+
+ if(spectrum->type != YAML_SEQUENCE_NODE) {
+ log_err(filename, spectrum, "expect a list of spectrum data.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = spectrum->data.sequence.items.top - spectrum->data.sequence.items.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* sdata;
+
+ sdata = yaml_document_get_node(doc, spectrum->data.sequence.items.start[i]);
+ res = parse_spectrum_data(filename, doc, lower_bound, upper_bound, sdata);
+ if(res != RES_OK) goto error;
+
+ /* TODO register the spectrum data */
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+/*******************************************************************************
+ * Instance
+ ******************************************************************************/
+static res_T
+parse_instance
+ (const char* filename, yaml_document_t* doc, const yaml_node_t* inst)
+{
+ enum { GEOMETRY, TRANSFORM };
+ double translation[3] = {0, 0, 0};
+ double rotation[3] = {0, 0, 0};
+ intptr_t i, n;
+ int mask = 0; /* Register the parsed attributes */
+ res_T res = RES_OK;
+ ASSERT(doc && inst);
+
+ if(inst->type != YAML_MAPPING_NODE) {
+ log_err(filename, inst, "expect an instance definition.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = inst->data.mapping.pairs.top - inst->data.mapping.pairs.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* key;
+ yaml_node_t* val;
+
+ key = yaml_document_get_node(doc, inst->data.mapping.pairs.start[i].key);
+ val = yaml_document_get_node(doc, inst->data.mapping.pairs.start[i].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expect instance parameters.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ #define SETUP_MASK(Flag, Name) { \
+ if(mask & BIT(Flag)) { \
+ log_err(filename, key, "the instance "Name" is already defined.\n"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } \
+ mask |= BIT(Flag); \
+ } (void)0
+ if(!strcmp((char*)key->data.scalar.value, "node")) {
+ SETUP_MASK(GEOMETRY, "geometry");
+ res = parse_node(filename, doc, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "object")) {
+ SETUP_MASK(GEOMETRY, "geometry");
+ res = parse_object(filename, doc, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "transform")) {
+ SETUP_MASK(TRANSFORM, "transform");
+ res = parse_transform(filename, doc, val, translation, rotation);
+ } else {
+ log_err(filename, key, "unknown instance parameter `%s'.\n",
+ key->data.scalar.value);
+ res = RES_BAD_ARG;
+ }
+ if(res != RES_OK) goto error;
+ #undef SETUP_MASK
+ }
+
+ #define CHECK_PARAM(Flag, Name) \
+ if(!(mask & BIT(Flag))) { \
+ log_err(filename, inst, "the instance "Name" is missing.\n"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } (void)0
+ CHECK_PARAM(GEOMETRY, "geometry");
+ #undef CHECK_PARAM
+
+ /* TODO register the instance */
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+/*******************************************************************************
+ * Material
+ ******************************************************************************/
+static res_T
+parse_material_matte
+ (const char* filename, yaml_document_t* doc, const yaml_node_t* matte)
+{
+ enum { REFLECTIVITY };
+ double reflectivity;
+ intptr_t i, n;
+ int mask = 0; /* Register the parsed attributes */
+ res_T res = RES_OK;
+ ASSERT(doc && matte);
+
+ if(matte->type != YAML_MAPPING_NODE) {
+ log_err(filename, matte, "expect a mapping of matte material parameters.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = matte->data.mapping.pairs.top - matte->data.mapping.pairs.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* key;
+ yaml_node_t* val;
+
+ key = yaml_document_get_node(doc, matte->data.mapping.pairs.start[0].key);
+ val = yaml_document_get_node(doc, matte->data.mapping.pairs.start[0].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expect a matte material parameter.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if(!strcmp((char*)key->data.scalar.value, "reflectivity")) {
+ if(mask & BIT(REFLECTIVITY)) {
+ log_err(filename, key, "the matte reflectivity is already defined.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ mask |= BIT(REFLECTIVITY);
+ res = parse_real(filename, val, 0, 1, &reflectivity);
+ } else {
+ log_err(filename, key, "unknown matte parameter `%s'.\n",
+ key->data.scalar.value);
+ res = RES_BAD_ARG;
+ }
+ if(res != RES_OK) goto error;
+ }
+
+ if(!(mask & BIT(REFLECTIVITY))) {
+ log_err(filename, matte, "the matte reflectivity is missing.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ /* TODO create the matte material */
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+parse_material_mirror
+ (const char* filename, yaml_document_t* doc, const yaml_node_t* mirror)
+{
+ enum { REFLECTIVITY, ROUGHNESS };
+ double reflectivity;
+ double roughness;
+ int mask = 0; /* Register the parsed attributes */
+ intptr_t i, n;
+ res_T res = RES_OK;
+ ASSERT(doc && mirror);
+
+ if(mirror->type != YAML_MAPPING_NODE) {
+ log_err(filename, mirror,
+ "expect a mapping of mirror material attributes .\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ n = mirror->data.mapping.pairs.top - mirror->data.mapping.pairs.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* key;
+ yaml_node_t* val;
+
+ key = yaml_document_get_node(doc, mirror->data.mapping.pairs.start[i].key);
+ val = yaml_document_get_node(doc, mirror->data.mapping.pairs.start[i].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expect a mirror material parameter.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ #define SETUP_MASK(Flag, Name) { \
+ if(mask & BIT(Flag)) { \
+ log_err(filename, key, \
+ "the "Name" of the mirror material is already defined.\n"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } \
+ mask |= BIT(Flag); \
+ } (void)0
+ if(!strcmp((char*)key->data.scalar.value, "reflectivity")) {
+ SETUP_MASK(REFLECTIVITY, "reflectivity");
+ res = parse_real(filename, val, 0, 1, &reflectivity);
+ } else if(!strcmp((char*)key->data.scalar.value, "roughness")) {
+ SETUP_MASK(ROUGHNESS, "roughness");
+ res = parse_real(filename, val, 0, 1, &roughness);
+ } else {
+ log_err(filename, key, "unknown mirror attribute `%s'.\n",
+ key->data.scalar.value);
+ res = RES_BAD_ARG;
+ }
+ if(res != RES_OK) goto error;
+ #undef SETUP_MASK
+ }
+
+ #define CHECK_PARAM(Flag, Name) \
+ if(!(mask & BIT(Flag))) { \
+ log_err(filename, mirror, "the mirror "Name" is missing.\n"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } (void)0
+ CHECK_PARAM(REFLECTIVITY, "reflectivity");
+ CHECK_PARAM(ROUGHNESS, "roughness");
+ #undef CHECK_PARAM
+
+ /* TODO create the mirror material */
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+parse_material_descriptor
+ (const char* filename, yaml_document_t* doc, const yaml_node_t* desc)
+{
+ enum { DESCRIPTOR };
+ intptr_t i, n;
+ int mask = 0; /* Register the parsed attributes */
+ res_T res = RES_OK;
+ ASSERT(doc && desc);
+
+ /* TODO If the descriptor is an alias of an already created material skip the
+ * parsing and return the aliased material descriptor */
+
+ if(desc->type != YAML_MAPPING_NODE) {
+ log_err(filename, desc, "expect a material descriptor.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = desc->data.mapping.pairs.top - desc->data.mapping.pairs.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* key;
+ yaml_node_t* val;
+
+ key = yaml_document_get_node(doc, desc->data.mapping.pairs.start[i].key);
+ val = yaml_document_get_node(doc, desc->data.mapping.pairs.start[i].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expect a material name.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ #define SETUP_MASK(Flag, Name) { \
+ if(mask & BIT(Flag)) { \
+ log_err(filename, key, "the material "Name" is already defined.\n"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } \
+ mask |= BIT(Flag); \
+ } (void)0
+ if(!strcmp((char*)key->data.scalar.value, "matte")) {
+ SETUP_MASK(DESCRIPTOR, "descriptor");
+ res = parse_material_matte(filename, doc, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "mirror")) {
+ SETUP_MASK(DESCRIPTOR, "descriptor");
+ res = parse_material_mirror(filename, doc, val);
+ } else {
+ log_err(filename, key, "unknown material descriptor `%s'.\n",
+ key->data.scalar.value);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ if(res != RES_OK) goto error;
+ #undef SETUP_MASK
+ }
+
+ if(!(mask & BIT(DESCRIPTOR))) {
+ log_err(filename, desc, "the material descriptor is missing.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+parse_material
+ (const char* filename, yaml_document_t* doc, const yaml_node_t* mtl)
+{
+ enum { FRONT, BACK };
+ intptr_t i, n;
+ int mask = 0; /* Register the parsed attributes */
+ res_T res = RES_OK;
+ ASSERT(doc && mtl);
+
+ if(mtl->type != YAML_MAPPING_NODE) {
+ log_err(filename, mtl, "expect a material definition.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = mtl->data.mapping.pairs.top - mtl->data.mapping.pairs.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* key;
+ yaml_node_t* val;
+
+ key = yaml_document_get_node(doc, mtl->data.mapping.pairs.start[i].key);
+ val = yaml_document_get_node(doc, mtl->data.mapping.pairs.start[i].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key,
+ "expect a material descriptor or a double sided material.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ #define SETUP_MASK(Flag, Name) { \
+ if(mask & BIT(Flag)) { \
+ log_err(filename, key, \
+ "the "Name" material descriptor is already defined.\n"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } \
+ mask |= BIT(Flag); \
+ } (void)0
+ if(!strcmp((char*)key->data.scalar.value, "front")) {
+ SETUP_MASK(FRONT, "front");
+ res = parse_material_descriptor(filename, doc, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "back")) {
+ SETUP_MASK(BACK, "back");
+ res = parse_material_descriptor(filename, doc, val);
+ } else {
+ SETUP_MASK(FRONT, "front");
+ SETUP_MASK(BACK, "back");
+ res = parse_material_descriptor(filename, doc, mtl);
+ }
+ if(res != RES_OK) goto error;
+ #undef SETUP_MASK
+ }
+
+ #define CHECK_PARAM(Flag, Name) \
+ if(!(mask & BIT(Flag))) { \
+ log_err(filename, mtl, "the "Name" material descriptor is missing.\n"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } (void)0
+ CHECK_PARAM(FRONT, "front");
+ CHECK_PARAM(BACK, "back");
+ #undef CHECK_PARAM
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+/*******************************************************************************
+ * Clipping polygon
+ ******************************************************************************/
+static res_T
+parse_clip_op(const char* filename, const yaml_node_t* op)
+{
+ res_T res = RES_OK;
+ ASSERT(op);
+
+ if(op->type != YAML_SCALAR_NODE) {
+ log_err(filename, op, "expect a clipping operation.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if(!strcmp((char*)op->data.scalar.value, "AND")) { /* TODO setup op */
+ } else if(!strcmp((char*)op->data.scalar.value, "SUB")) { /* TODO setup op */
+ } else {
+ log_err(filename, op, "unknown clipping operation `%s'.\n",
+ op->data.scalar.value);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+parse_vertices
+ (const char* filename, yaml_document_t* doc, const yaml_node_t* vertices)
+{
+ intptr_t i, n;
+ res_T res = RES_OK;
+ ASSERT(doc && vertices);
+
+ if(vertices->type != YAML_SEQUENCE_NODE) {
+ log_err(filename, vertices, "expect a list of vertices.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = vertices->data.sequence.items.top - vertices->data.sequence.items.start;
+ if(n < 3) {
+ log_err(filename, vertices, "expect at least 3 vertices.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* vertex;
+ double coords[3];
+
+ vertex = yaml_document_get_node(doc, vertices->data.sequence.items.start[i]);
+ res = parse_real3(filename, doc, vertex, -DBL_MAX, DBL_MAX, coords);
+ if(res != RES_OK) goto error;
+
+ /* TODO register the vertex */
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+parse_polyclip
+ (const char* filename, yaml_document_t* doc, const yaml_node_t* polyclip)
+{
+ enum { OPERATION, VERTICES };
+ intptr_t i, n;
+ int mask = 0; /* Register the parsed attributes */
+ res_T res = RES_OK;
+ ASSERT(doc && polyclip);
+
+ if(polyclip->type != YAML_MAPPING_NODE) {
+ log_err(filename, polyclip,
+ "expect a mapping of clipping polygon parameters.\n");
+ res = RES_OK;
+ goto error;
+ }
+
+ n = polyclip->data.mapping.pairs.top - polyclip->data.mapping.pairs.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* key;
+ yaml_node_t* val;
+
+ key = yaml_document_get_node(doc, polyclip->data.mapping.pairs.start[i].key);
+ val = yaml_document_get_node(doc, polyclip->data.mapping.pairs.start[i].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expect a clipping polygon parameter.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ #define SETUP_MASK(Flag, Name) { \
+ if(mask & BIT(Flag)) { \
+ log_err(filename, key, \
+ "the clipping polygon parameter `"Name"' is already defined.\n"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } \
+ mask |= BIT(Flag); \
+ } (void)0
+ if(!strcmp((char*)key->data.scalar.value, "operation")) {
+ SETUP_MASK(OPERATION, "operation");
+ res = parse_clip_op(filename, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "vertices")) {
+ SETUP_MASK(VERTICES, "vertices");
+ res = parse_vertices(filename, doc, val);
+ } else {
+ log_err(filename, key, "unknown clipping polygon parameter `%s'.\n",
+ key->data.scalar.value);
+ res = RES_BAD_ARG;
+ }
+ if(res != RES_OK) goto error;
+ #undef SETUP_MASK
+ }
+
+ #define CHECK_PARAM(Flag, Name) \
+ if(!(mask & BIT(Flag))) { \
+ log_err(filename, polyclip, \
+ "the clipping polygon parameter `"Name"' is missing"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } (void)0
+ CHECK_PARAM(OPERATION, "operation");
+ CHECK_PARAM(VERTICES, "vertices");
+ #undef CHECK_PARAM
+
+ /* TODO register the polyclip */
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+parse_clip(const char* filename, yaml_document_t* doc, const yaml_node_t* clip)
+{
+ intptr_t i, n;
+ res_T res = RES_OK;
+ ASSERT(doc && clip);
+
+ if(clip->type != YAML_SEQUENCE_NODE) {
+ log_err(filename, clip, "expect a list of clipping polygons.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = clip->data.sequence.items.top - clip->data.sequence.items.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* polyclip;
+
+ polyclip = yaml_document_get_node(doc, clip->data.sequence.items.start[i]);
+ res = parse_polyclip(filename, doc, polyclip);
+ if(res != RES_OK) goto error;
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+/*******************************************************************************
+ * Shapes
+ ******************************************************************************/
+static res_T
+parse_cuboid
+ (const char* filename, yaml_document_t* doc, const yaml_node_t* cuboid)
+{
+ enum { SIZE };
+ double size[3];
+ intptr_t i, n;
+ int mask = 0; /* Register the parsed attributes */
+ res_T res = RES_OK;
+ ASSERT(doc && cuboid);
+
+ if(cuboid->type != YAML_MAPPING_NODE) {
+ log_err(filename, cuboid, "expect a mapping of cuboid parameters.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = cuboid->data.mapping.pairs.top - cuboid->data.mapping.pairs.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* key;
+ yaml_node_t* val;
+
+ key = yaml_document_get_node(doc, cuboid->data.mapping.pairs.start[i].key);
+ val = yaml_document_get_node(doc, cuboid->data.mapping.pairs.start[i].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expect cuboid parameters.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ if(!strcmp((char*)key->data.scalar.value, "size")) {
+ if(mask & BIT(SIZE)) {
+ log_err(filename, key, "the cuboid size is already defined.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ mask |= BIT(SIZE);
+ res = parse_real3(filename, doc, val, 0, DBL_MAX, size);
+ } else {
+ log_err(filename, key, "unknown cuboid parameter `%s'.\n",
+ key->data.scalar.value);
+ res = RES_BAD_ARG;
+ }
+ if(res != RES_OK) goto error;
+ }
+
+ if(!(mask & BIT(SIZE))) {
+ log_err(filename, cuboid, "the size of the cuboid is missing.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ /* TODO register the cuboid */
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+parse_cylinder
+ (const char* filename, yaml_document_t* doc, const yaml_node_t* cylinder)
+{
+ enum { HEIGHT, RADIUS, SLICES };
+ double radius;
+ double height;
+ long nslices = 16;
+ intptr_t i, n;
+ int mask = 0; /* Register the parsed attributes */
+ res_T res = RES_OK;
+ ASSERT(doc && cylinder);
+
+ if(cylinder->type != YAML_MAPPING_NODE) {
+ log_err(filename, cylinder, "expect a mapping of cylinder parameters.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = cylinder->data.mapping.pairs.top - cylinder->data.mapping.pairs.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* key;
+ yaml_node_t* val;
+
+ key = yaml_document_get_node(doc, cylinder->data.mapping.pairs.start[i].key);
+ val = yaml_document_get_node(doc, cylinder->data.mapping.pairs.start[i].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expect cylinder parameters.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ #define SETUP_MASK(Flag, Name) { \
+ if(mask & BIT(Flag)) { \
+ log_err(filename, key, \
+ "the cylinder parameter `"Name"' is already defined.\n"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } \
+ mask |= BIT(Flag); \
+ } (void)0
+ if(!strcmp((char*)key->data.scalar.value, "height")) {
+ SETUP_MASK(HEIGHT, "height");
+ res = parse_real(filename, val, 0, DBL_MAX, &height);
+ } else if(!strcmp((char*)key->data.scalar.value, "radius")) {
+ SETUP_MASK(RADIUS, "radius");
+ res = parse_real(filename, val, 0, DBL_MAX, &radius);
+ } else if(!strcmp((char*)key->data.scalar.value, "slices")) {
+ SETUP_MASK(SLICES, "slices");
+ res = parse_integer(filename, val, 4, 4096, &nslices);
+ } else {
+ log_err(filename, key, "unknown cylinder parameter `%s'.\n",
+ key->data.scalar.value);
+ res = RES_BAD_ARG;
+ }
+ if(res != RES_OK) goto error;
+ #undef SETUP_MASK
+ }
+
+ #define CHECK_PARAM(Flag, Name) \
+ if(!(mask & BIT(Flag))) { \
+ log_err(filename, cylinder, \
+ "the cylinder parameter `"Name"' is missing.\n"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } (void)0
+ CHECK_PARAM(HEIGHT, "height");
+ CHECK_PARAM(RADIUS, "radius");
+ #undef CHECK_PARAM
+
+ /* TODO register the cylinder */
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+parse_imported_geometry
+ (const char* filename,
+ yaml_document_t* doc,
+ const yaml_node_t* geom,
+ const enum geometry_fileformat fileformat)
+{
+ enum { PATH };
+ intptr_t i, n;
+ int mask = 0; /* Register the parsed attributes */
+ const char* name = geometry_fileformat_name(fileformat);
+ res_T res = RES_OK;
+ ASSERT(doc && geom);
+
+ if(geom->type != YAML_MAPPING_NODE) {
+ log_err(filename, geom, "expect a mapping of %s parameters.\n", name);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = geom->data.mapping.pairs.top - geom->data.mapping.pairs.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* key;
+ yaml_node_t* val;
+
+ key = yaml_document_get_node(doc, geom->data.mapping.pairs.start[i].key);
+ val = yaml_document_get_node(doc, geom->data.mapping.pairs.start[i].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expect %s parameters.\n", name);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ if(!strcmp((char*)key->data.scalar.value, "path")) {
+ if(mask & BIT(PATH)) {
+ log_err(filename, key, "the %s path is already defined.\n", name);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ mask |= BIT(PATH);
+ res = parse_string(filename, val);
+ } else {
+ log_err(filename, key, "unknown %s parameter `%s'.\n",
+ name, key->data.scalar.value);
+ res = RES_BAD_ARG;
+ }
+ if(res != RES_OK) goto error;
+ }
+
+ if(!(mask & BIT(PATH))) {
+ log_err(filename, geom, "the path of the %s geometry is missing.\n", name);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ /* TODO Register the imported geometry */
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+parse_paraboloid
+ (const char* filename,
+ yaml_document_t* doc,
+ const yaml_node_t* paraboloid,
+ const enum paraboloid_type type)
+{
+ enum { CLIP, FOCAL };
+ double focal;
+ intptr_t i, n;
+ int mask = 0; /* Register the parsed attributes */
+ const char* name = paraboloid_type_name(type);
+ res_T res = RES_OK;
+ ASSERT(doc && paraboloid);
+
+ if(paraboloid->type != YAML_MAPPING_NODE) {
+ log_err(filename, paraboloid, "expect a mapping of %s parameters.\n", name);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = paraboloid->data.mapping.pairs.top - paraboloid->data.mapping.pairs.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* key;
+ yaml_node_t* val;
+
+ key = yaml_document_get_node(doc, paraboloid->data.mapping.pairs.start[i].key);
+ val = yaml_document_get_node(doc, paraboloid->data.mapping.pairs.start[i].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expect %s parameters.\n", name);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ #define SETUP_MASK(Flag, Name) { \
+ if(mask & BIT(Flag)) { \
+ log_err(filename, key, \
+ "the %s parameter `"Name"' is already defined.\n", name); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } \
+ mask |= BIT(Flag); \
+ } (void)0
+ if(!strcmp((char*)key->data.scalar.value, "clip")) {
+ SETUP_MASK(CLIP, "clip");
+ res = parse_clip(filename, doc, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "focal")) {
+ SETUP_MASK(FOCAL, "focal");
+ res = parse_real(filename, val, nextafter(0, 1), DBL_MAX, &focal);
+ } else {
+ log_err(filename, key, "unknown %s parameter `%s'.\n",
+ name, key->data.scalar.value);
+ res = RES_BAD_ARG;
+ }
+ if(res != RES_OK) goto error;
+ #undef SETUP_MASK
+ }
+ #define CHECK_PARAM(Flag, Name) \
+ if(!(mask & BIT(Flag))) { \
+ log_err(filename, paraboloid, \
+ "the %s parameter `"Name"' is missing.\n", name); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } (void)0
+ CHECK_PARAM(CLIP, "clip");
+ CHECK_PARAM(FOCAL, "focal");
+ #undef CHECK_PARAM
+
+ /* TODO register the paraboloid */
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+parse_plane
+ (const char* filename, yaml_document_t* doc, const yaml_node_t* plane)
+{
+ enum { CLIP };
+ intptr_t i, n;
+ int mask = 0; /* Register the parsed attributes */
+ res_T res = RES_OK;
+ ASSERT(doc && plane);
+
+ if(plane->type != YAML_MAPPING_NODE) {
+ log_err(filename, plane, "expect a mapping of plane parameters.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = plane->data.mapping.pairs.top - plane->data.mapping.pairs.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* key;
+ yaml_node_t* val;
+
+ key = yaml_document_get_node(doc, plane->data.mapping.pairs.start[i].key);
+ val = yaml_document_get_node(doc, plane->data.mapping.pairs.start[i].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expect plane parameters.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ if(!strcmp((char*)key->data.scalar.value, "clip")) {
+ if(mask & BIT(CLIP)) {
+ log_err(filename, key, "the plane clipping is already defined.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ mask |= BIT(CLIP);
+ res = parse_clip(filename, doc, val);
+ } else {
+ log_err(filename, key, "unknown plane parameter `%s'.\n",
+ key->data.scalar.value);
+ res = RES_BAD_ARG;
+ }
+ if(res != RES_OK) goto error;
+ }
+ if(!(mask & BIT(CLIP))) {
+ log_err(filename, plane, "the plane parameter `clip' is missing.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ /* TODO register the plane */
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+parse_sphere
+ (const char* filename, yaml_document_t* doc, const yaml_node_t* sphere)
+{
+ enum { RADIUS, SLICES };
+ double radius;
+ long nslices;
+ intptr_t i, n;
+ int mask = 0; /* Register the parsed attributes */
+ res_T res = RES_OK;
+ ASSERT(doc && sphere);
+
+ if(sphere->type != YAML_MAPPING_NODE) {
+ log_err(filename, sphere, "expect a mapping of sphere parameters.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = sphere->data.mapping.pairs.top - sphere->data.mapping.pairs.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* key;
+ yaml_node_t* val;
+
+ key = yaml_document_get_node(doc, sphere->data.mapping.pairs.start[i].key);
+ val = yaml_document_get_node(doc, sphere->data.mapping.pairs.start[i].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expect sphere parameters.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ #define SETUP_MASK(Flag, Name) { \
+ if(mask & BIT(Flag)) { \
+ log_err(filename, key, \
+ "the sphere parameter `"Name"' is already defined.\n"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } \
+ mask |= BIT(Flag); \
+ } (void)0
+ if(!strcmp((char*)key->data.scalar.value, "radius")) {
+ SETUP_MASK(RADIUS, "radius");
+ res = parse_real(filename, val, 0, DBL_MAX, &radius);
+ } else if(!strcmp((char*)key->data.scalar.value, "slices")) {
+ SETUP_MASK(SLICES, "slices");
+ res = parse_integer(filename, val, 4, 4096, &nslices);
+ } else {
+ log_err(filename, key, "unknown sphere parameter `%s'.\n",
+ key->data.scalar.value);
+ res = RES_BAD_ARG;
+ }
+ if(res != RES_OK) goto error;
+ #undef SETUP_MASK
+ }
+
+ if(!(mask & BIT(RADIUS))) {
+ log_err(filename, sphere, "the sphere radius is missing.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ /* TODO register the sphere */
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+/*******************************************************************************
+ * Object
+ ******************************************************************************/
+res_T
+parse_object
+ (const char* filename, yaml_document_t* doc, const yaml_node_t* object)
+{
+ enum { MATERIAL, SHAPE, TRANSFORM };
+ double translation[3] = {0, 0, 0};
+ double rotation[3] = {0, 0, 0};
+ intptr_t i, n;
+ int mask = 0; /* Register the parsed attributes */
+ res_T res = RES_OK;
+ ASSERT(doc && object);
+
+ /* TODO If the object is an alias of an already created object skip the
+ * parsing and return the aliased object */
+
+ if(object->type != YAML_MAPPING_NODE) {
+ log_err(filename, object, "expect an object definition.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = object->data.mapping.pairs.top - object->data.mapping.pairs.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* key;
+ yaml_node_t* val;
+
+ key = yaml_document_get_node(doc, object->data.mapping.pairs.start[i].key);
+ val = yaml_document_get_node(doc, object->data.mapping.pairs.start[i].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expect an object parameter.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ #define SETUP_MASK(Flag, Name) { \
+ if(mask & BIT(Flag)) { \
+ log_err(filename, key, \
+ "the object "Name" is already defined.\n"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } \
+ mask |= BIT(Flag); \
+ } (void)0
+ if(!strcmp((char*)key->data.scalar.value, "material")) {
+ SETUP_MASK(MATERIAL, "material");
+ res = parse_material(filename, doc, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "cuboid")) {
+ SETUP_MASK(SHAPE, "shape");
+ res = parse_cuboid(filename, doc, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "cylinder")) {
+ SETUP_MASK(SHAPE, "shape");
+ res = parse_cylinder(filename, doc, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "obj")) {
+ SETUP_MASK(SHAPE, "shape");
+ res = parse_imported_geometry(filename, doc, val, GEOMETRY_OBJ);
+ } else if(!strcmp((char*)key->data.scalar.value, "parabol")) {
+ SETUP_MASK(SHAPE, "shape");
+ res = parse_paraboloid(filename, doc, val, PARABOL);
+ } else if(!strcmp((char*)key->data.scalar.value, "parabolic-cylinder")) {
+ SETUP_MASK(SHAPE, "shape");
+ res = parse_paraboloid(filename, doc, val, PARABOLIC_CYLINDER);
+ } else if(!strcmp((char*)key->data.scalar.value, "plane")) {
+ SETUP_MASK(SHAPE, "shape");
+ res = parse_plane(filename, doc, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "sphere")) {
+ SETUP_MASK(SHAPE, "shape");
+ res = parse_sphere(filename, doc, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "stl")) {
+ SETUP_MASK(SHAPE, "shape");
+ res = parse_imported_geometry(filename, doc, val, GEOMETRY_STL);
+ } else if(!strcmp((char*)key->data.scalar.value, "transform")) {
+ SETUP_MASK(TRANSFORM, "transform");
+ res = parse_transform(filename, doc, val, translation, rotation);
+ } else {
+ log_err(filename, key, "unknown object parameter `%s'.\n",
+ key->data.scalar.value);
+ res = RES_BAD_ARG;
+ }
+ if(res != RES_OK) goto error;
+ #undef SETUP_MASK
+ }
+
+ #define CHECK_PARAM(Flag, Name) \
+ if(!(mask & BIT(Flag))) { \
+ log_err(filename, object, "the object "Name" is missing.\n"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } (void)0
+ CHECK_PARAM(MATERIAL, "material");
+ CHECK_PARAM(SHAPE, "shape");
+ #undef CHECK_PARAM
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+/*******************************************************************************
+ * Node
+ ******************************************************************************/
+static res_T
+parse_children
+ (const char* filename, yaml_document_t* doc, const yaml_node_t* children)
+{
+ intptr_t i, n;
+ res_T res = RES_OK;
+ ASSERT(doc && children);
+
+ if(children->type != YAML_SEQUENCE_NODE) {
+ log_err(filename, children, "expect a list of nodes.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = children->data.sequence.items.top - children->data.sequence.items.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* child;
+ yaml_node_t* key;
+ yaml_node_t* val;
+ intptr_t nb;
+
+ child = yaml_document_get_node(doc, children->data.sequence.items.start[i]);
+ if(child->type != YAML_MAPPING_NODE) {
+ log_err(filename, child, "expect a node definition.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ nb = child->data.mapping.pairs.top - child->data.mapping.pairs.start;
+ if(nb != 1) {
+ log_err(filename, child,
+ "expect only one \"key:value\" pair while %li are provided.\n", nb);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ key = yaml_document_get_node(doc, child->data.mapping.pairs.start[0].key);
+ val = yaml_document_get_node(doc, child->data.mapping.pairs.start[0].value);
+ if(!strcmp((char*)key->data.scalar.value, "node")) {
+ res = parse_node(filename, doc, val);
+ } else {
+ log_err(filename, key, "unexpected directive `%s'. Expect a node.\n",
+ key->data.scalar.value);
+ res = RES_BAD_ARG;
+ }
+ if(res != RES_OK) goto error;
+
+ /* TODO register the child node */
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+parse_entities
+ (const char* filename, yaml_document_t* doc, const yaml_node_t* entities)
+{
+ intptr_t i, n;
+ res_T res = RES_OK;
+ ASSERT(doc && entities);
+
+ if(entities->type != YAML_SEQUENCE_NODE) {
+ log_err(filename, entities, "expect a list of entities.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = entities->data.sequence.items.top - entities->data.sequence.items.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* entity;
+ yaml_node_t* key;
+ yaml_node_t* val;
+ intptr_t nb;
+
+ entity = yaml_document_get_node(doc, entities->data.sequence.items.start[i]);
+
+ if(entity->type != YAML_MAPPING_NODE) {
+ log_err(filename, entity, "expect an entity definition.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ nb = entity->data.mapping.pairs.top - entity->data.mapping.pairs.start;
+ if(nb != 1) {
+ log_err(filename, entity,
+ "expect only one \"key:value\" pair while %li are provided.\n", nb);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ key = yaml_document_get_node(doc, entity->data.mapping.pairs.start[0].key);
+ val = yaml_document_get_node(doc, entity->data.mapping.pairs.start[0].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expect an entity name.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if(!strcmp((char*)key->data.scalar.value, "object")) {
+ res = parse_object(filename, doc, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "pivot")) {
+ res = parse_pivot(filename, doc, val);
+ } else {
+ log_err(filename, key, "unknown entity `%s'.\n", key->data.scalar.value);
+ res = RES_BAD_ARG;
+ }
+ if(res != RES_OK) goto error;
+
+ /* TODO register the entity */
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+parse_node(const char* filename, yaml_document_t* doc, const yaml_node_t* node)
+{
+ enum { CHILDREN, ENTITIES, TRANSFORM };
+ double translation[3] = {0, 0, 0};
+ double rotation[3] = {0, 0, 0};
+ intptr_t i, n;
+ int mask = 0; /* Register the parsed attributes */
+ res_T res = RES_OK;
+ ASSERT(doc && node);
+
+ if(node->type != YAML_MAPPING_NODE) {
+ log_err(filename, node, "expect a node definition.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = node->data.mapping.pairs.top - node->data.mapping.pairs.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* key;
+ yaml_node_t* val;
+
+ key = yaml_document_get_node(doc, node->data.mapping.pairs.start[i].key);
+ val = yaml_document_get_node(doc, node->data.mapping.pairs.start[i].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expect a node attribute.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ #define SETUP_MASK(Flag, Name) { \
+ if(mask & BIT(Flag)) { \
+ log_err(filename, key, \
+ "the node attribute `"Name"' is already defined.\n"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } \
+ mask |= BIT(Flag); \
+ } (void)0
+ if(!strcmp((char*)key->data.scalar.value, "children")) {
+ SETUP_MASK(CHILDREN, "children");
+ res = parse_children(filename, doc, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "entities")) {
+ SETUP_MASK(ENTITIES, "entities");
+ res = parse_entities(filename, doc, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "transform")) {
+ SETUP_MASK(TRANSFORM, "transform");
+ res = parse_transform(filename, doc, val, translation, rotation);
+ } else {
+ log_err(filename, key, "unknown node parameter `%s'.\n",
+ key->data.scalar.value);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ if(res != RES_OK) goto error;
+ #undef SETUP_MASK
+ }
+
+ #define CHECK_PARAM(Flag, Name) \
+ if(!(mask & BIT(Flag))) { \
+ log_err(filename, node, "the node `"Name"' parameter is missing.\n"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } (void)0
+ CHECK_PARAM(ENTITIES, "entities");
+ #undef CHECK_PARAM
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+/*******************************************************************************
+ * Pivot
+ ******************************************************************************/
+static res_T
+parse_target
+ (const char* filename, yaml_document_t* doc, const yaml_node_t* target)
+{
+ enum { POLICY };
+ double direction[3];
+ double position[3];
+ intptr_t i, n;
+ int mask = 0; /* Register the parsed attributes */
+ res_T res = RES_OK;
+ ASSERT(doc && target);
+
+ if(target->type != YAML_MAPPING_NODE) {
+ log_err(filename, target, "expect a target definition.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = target->data.mapping.pairs.top - target->data.mapping.pairs.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* key;
+ yaml_node_t* val;
+
+ key = yaml_document_get_node(doc, target->data.mapping.pairs.start[i].key);
+ val = yaml_document_get_node(doc, target->data.mapping.pairs.start[i].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expect a target parameter.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ #define SETUP_MASK(Flag, Name) { \
+ if(mask & BIT(Flag)) { \
+ log_err(filename, key, "the target "Name" is already defined.\n"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } \
+ mask |= BIT(Flag); \
+ } (void)0
+ if(!strcmp((char*)key->data.scalar.value, "direction")) {
+ SETUP_MASK(POLICY, "policy");
+ res = parse_real3(filename, doc, val, -DBL_MAX, DBL_MAX, direction);
+ } else if(!strcmp((char*)key->data.scalar.value, "position")) {
+ SETUP_MASK(POLICY, "policy");
+ res = parse_real3(filename, doc, val, -DBL_MAX, DBL_MAX, position);
+ } else if(!strcmp((char*)key->data.scalar.value, "sun")) {
+ SETUP_MASK(POLICY, "policy");
+ res = parse_sun(filename, doc, val);
+ } else {
+ log_err(filename, key, "unknown target parameter `%s'.\n",
+ key->data.scalar.value);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ if(res != RES_OK) goto error;
+ #undef SETUP_MASK
+ }
+
+ if(!(mask & BIT(POLICY))) {
+ log_err(filename, target, "the target policy is missing.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+parse_pivot
+ (const char* filename, yaml_document_t* doc, const yaml_node_t* pivot)
+{
+ enum { NORMAL, POINT, TARGET, TRANSFORM };
+ double point[3];
+ double normal[3];
+ double translation[3] = {0, 0, 0};
+ double rotation[3] = {0, 0, 0};
+ int mask = 0; /* Register the parsed attributes */
+ intptr_t i, n;
+ res_T res = RES_OK;
+ ASSERT(doc && pivot);
+
+ if(pivot->type != YAML_MAPPING_NODE) {
+ log_err(filename, pivot, "expect a pivot definition.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = pivot->data.mapping.pairs.top - pivot->data.mapping.pairs.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* key;
+ yaml_node_t* val;
+
+ key = yaml_document_get_node(doc, pivot->data.mapping.pairs.start[i].key);
+ val = yaml_document_get_node(doc, pivot->data.mapping.pairs.start[i].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expect pivot parameters.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ #define SETUP_MASK(Flag, Name) { \
+ if(mask & BIT(Flag)) { \
+ log_err(filename, key, \
+ "the pivot parameter `"Name"' is already defined.\n"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } \
+ mask |= BIT(Flag); \
+ } (void)0
+ if(!strcmp((char*)key->data.scalar.value, "point")) {
+ SETUP_MASK(POINT, "point");
+ res = parse_real3(filename, doc, val, -DBL_MAX, DBL_MAX, point);
+ } else if(!strcmp((char*)key->data.scalar.value, "normal")) {
+ SETUP_MASK(NORMAL, "normal");
+ res = parse_real3(filename, doc, val, -DBL_MAX, DBL_MAX, normal);
+ } else if(!strcmp((char*)key->data.scalar.value, "target")) {
+ SETUP_MASK(TARGET, "target");
+ res = parse_target(filename, doc, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "transform")) {
+ SETUP_MASK(TRANSFORM, "transform");
+ res = parse_transform(filename, doc, val, translation, rotation);
+ } else {
+ log_err(filename, key, "unknown pivot parameter `%s'.\n",
+ key->data.scalar.value);
+ res = RES_BAD_ARG;
+ }
+ if(res != RES_OK) goto error;
+ #undef SETUP_MASK
+ }
+
+ #define CHECK_PARAM(Flag, Name) \
+ if(!(mask & BIT(Flag))) { \
+ log_err(filename, pivot, "the pivot parameter `"Name"' is missing.\n"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } (void)0
+ CHECK_PARAM(POINT, "point");
+ CHECK_PARAM(NORMAL, "normal");
+ #undef CHECK_PARAM
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+/*******************************************************************************
+ * Sun
+ ******************************************************************************/
+static res_T
+parse_buie(const char* filename, yaml_document_t* doc, const yaml_node_t* buie)
+{
+ enum { CSR };
+ intptr_t i, n;
+ double csr;
+ int mask = 0; /* Register the parsed attributes */
+ res_T res = RES_OK;
+ ASSERT(doc && buie);
+
+ if(buie->type != YAML_MAPPING_NODE) {
+ log_err(filename, buie,
+ "expect a buie definition of the sun radial angular distribution.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = buie->data.mapping.pairs.top - buie->data.mapping.pairs.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* key;
+ yaml_node_t* val;
+
+ key = yaml_document_get_node(doc, buie->data.mapping.pairs.start[i].key);
+ val = yaml_document_get_node(doc, buie->data.mapping.pairs.start[i].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expect a buie parameter.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if(!strcmp((char*)key->data.scalar.value, "csr")) {
+ if(mask & BIT(CSR)) {
+ log_err(filename, key, "the buie `csr' is already defined.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ mask |= BIT(CSR);
+ res = parse_real(filename, val, nextafter(0, 1), nextafter(1, 0), &csr);
+ } else {
+ log_err(filename, key, "unknown buie parameter `%s'.\n",
+ key->data.scalar.value);
+ res = RES_BAD_ARG;
+ }
+ if(res != RES_OK) goto error;
+ }
+
+ if(!(mask & BIT(CSR))) {
+ log_err(filename, buie, "the buie csr parameter is missing.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+parse_pillbox
+ (const char* filename, yaml_document_t* doc, const yaml_node_t* pillbox)
+{
+ enum { APERTURE };
+ intptr_t i, n;
+ double aperture;
+ int mask = 0; /* Register the parsed attributes */
+ res_T res = RES_OK;
+ ASSERT(doc && pillbox);
+
+ if(pillbox->type != YAML_MAPPING_NODE) {
+ log_err(filename, pillbox,
+ "expect a pillbox definition of the sun radial angular distribution.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = pillbox->data.mapping.pairs.top - pillbox->data.mapping.pairs.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* key = key;
+ yaml_node_t* val = val;
+
+ key = yaml_document_get_node(doc, pillbox->data.mapping.pairs.start[i].key);
+ val = yaml_document_get_node(doc, pillbox->data.mapping.pairs.start[i].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expect a pillbox parameter.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ if(!strcmp((char*)key->data.scalar.value, "aperture")) {
+ if(mask & BIT(APERTURE)) {
+ log_err(filename, key, "the pillbox `aperture' is already defined.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ mask |= BIT(APERTURE);
+ res = parse_real(filename, val, nextafter(0, 1), PI/2.0, &aperture);
+ } else {
+ log_err(filename, pillbox, "unknown pillbox parameter `%s'.\n",
+ key->data.scalar.value);
+ res = RES_BAD_ARG;
+ }
+ if(res != RES_OK) goto error;
+ }
+
+ if(!(mask & BIT(APERTURE))) {
+ log_err(filename, pillbox, "the pillbox aperture parameter is missing.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+parse_sun(const char* filename, yaml_document_t* doc, const yaml_node_t* sun)
+{
+ enum { DNI, RADIAL_ANGULAR_DISTRIB, SPECTRUM };
+ double dni;
+ intptr_t i, n;
+ int mask = 0; /* Register the parsed attributes */
+ res_T res = RES_OK;
+ ASSERT(doc && sun);
+
+ if(sun->type != YAML_MAPPING_NODE) {
+ log_err(filename, sun, "expect a sun definition.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = sun->data.mapping.pairs.top - sun->data.mapping.pairs.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* key;
+ yaml_node_t* val;
+
+ key = yaml_document_get_node(doc, sun->data.mapping.pairs.start[i].key);
+ val = yaml_document_get_node(doc, sun->data.mapping.pairs.start[i].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expect sun parameters.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ #define SETUP_MASK(Flag, Name) { \
+ if(mask & BIT(Flag)) { \
+ log_err(filename, key, "the sun "Name" is already defined.\n"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } \
+ mask |= BIT(Flag); \
+ } (void)0
+ if(!strcmp((char*)key->data.scalar.value, "dni")) {
+ SETUP_MASK(DNI, "dni");
+ res = parse_real(filename, val, nextafter(0, 1), DBL_MAX, &dni);
+ } else if(!strcmp((char*)key->data.scalar.value, "buie")) {
+ SETUP_MASK(RADIAL_ANGULAR_DISTRIB, "radial angular distribution");
+ res = parse_buie(filename, doc, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "pillbox")) {
+ SETUP_MASK(RADIAL_ANGULAR_DISTRIB, "radial angular distribution");
+ res = parse_pillbox(filename, doc, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "spectrum")) {
+ SETUP_MASK(SPECTRUM, "spectrum");
+ res = parse_spectrum(filename, doc, 0, DBL_MAX, val);
+ } else {
+ log_err(filename, key, "unknown sun parameter `%s'.\n",
+ key->data.scalar.value);
+ res = RES_BAD_ARG;
+ }
+ if(res != RES_OK) goto error;
+ #undef SETUP_MASK
+ }
+
+ #define CHECK_PARAM(Flag, Name) \
+ if(!(mask & BIT(Flag))) { \
+ log_err(filename, sun, "the sun "Name" is missing.\n"); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } (void)0
+ CHECK_PARAM(DNI, "dni");
+ CHECK_PARAM(SPECTRUM, "spectrum");
+ #undef CHECK_PARAM
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+/*******************************************************************************
+ * Item
+ ******************************************************************************/
+static res_T
+parse_item
+ (const char* filename, yaml_document_t* doc, const yaml_node_t* item)
+{
+ yaml_node_t* key;
+ yaml_node_t* val;
+ intptr_t n;
+ res_T res = RES_OK;
+ ASSERT(doc && item);
+
+ if(item->type != YAML_MAPPING_NODE) {
+ log_err(filename, item, "expect an item definition.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = item->data.mapping.pairs.top - item->data.mapping.pairs.start;
+ if(n != 1) {
+ log_err(filename, item,
+ "expect only one \"key:value\" pair while %li are provided.\n", n);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ key = yaml_document_get_node(doc, item->data.mapping.pairs.start[0].key);
+ val = yaml_document_get_node(doc, item->data.mapping.pairs.start[0].value);
+ if(key->type != YAML_SCALAR_NODE) {
+ log_err(filename, key, "expecting an item name.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if(!strcmp((char*)key->data.scalar.value, "instance")) {
+ res = parse_instance(filename, doc, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "material")) {
+ res = parse_material(filename, doc, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "node")) {
+ res = parse_node(filename, doc, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "object")) {
+ res = parse_object(filename, doc, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "pivot")) {
+ res = parse_pivot(filename, doc, val);
+ } else if(!strcmp((char*)key->data.scalar.value, "sun")) {
+ res = parse_sun(filename, doc, val);
+ } else {
+ log_err(filename, key, "unknown item `%s'.\n", key->data.scalar.value);
+ res = RES_BAD_ARG;
+ }
+ if(res != RES_OK) goto error;
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+/*******************************************************************************
+ * Local functions
+ ******************************************************************************/
+res_T
+solstice_parser_create
+ (struct mem_allocator* allocator, struct solstice_parser** out_parser)
+{
+ struct solstice_parser* parser = NULL;
+ struct mem_allocator* mem_allocator;
+ res_T res = RES_OK;
+ ASSERT(out_parser);
+
+ mem_allocator = allocator ? allocator : &mem_default_allocator;
+ parser = MEM_CALLOC(mem_allocator, 1, sizeof(struct solstice_parser));
+ if(!parser) {
+ fprintf(stderr, "Could not allocat the Solstice parser.\n");
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ parser->allocator = mem_allocator;
+ ref_init(&parser->ref);
+ str_init(mem_allocator, &parser->stream_name);
+
+exit:
+ *out_parser = parser;
+ return res;
+error:
+ if(parser) {
+ solstice_parser_ref_put(parser);
+ parser = NULL;
+ }
+ goto exit;
+}
+
+void
+solstice_parser_ref_get(struct solstice_parser* parser)
+{
+ ASSERT(parser);
+ ref_get(&parser->ref);
+}
+
+void
+solstice_parser_ref_put(struct solstice_parser* parser)
+{
+ ASSERT(parser);
+ ref_put(&parser->ref, parser_release);
+}
+
+res_T
+solstice_parser_setup
+ (struct solstice_parser* parser,
+ const char* stream_name,
+ FILE* stream)
+{
+ res_T res = RES_OK;
+ ASSERT(parser && stream );
+
+ if(parser->parser_is_init) {
+ yaml_parser_delete(&parser->parser);
+ parser->parser_is_init = 0;
+ }
+ res = str_set(&parser->stream_name, stream_name ? stream_name : "<stream>");
+ if(res != RES_OK) {
+ fprintf(stderr, "Could not register the filename.\n");
+ goto error;
+ }
+ if(!yaml_parser_initialize(&parser->parser)) {
+ fprintf(stderr, "Could not initialise the YAML parser.\n");
+ res = RES_UNKNOWN_ERR;
+ goto error;
+ }
+ parser->parser_is_init = 1;
+ yaml_parser_set_input_file(&parser->parser, stream);
+
+exit:
+ return res;
+error:
+ str_clear(&parser->stream_name);
+ if(parser->parser_is_init) {
+ yaml_parser_delete(&parser->parser);
+ parser->parser_is_init = 0;
+ }
+ goto exit;
+}
+
+res_T
+solstice_parser_load(struct solstice_parser* parser)
+{
+ yaml_document_t doc;
+ yaml_node_t* root;
+ const char* filename;
+ intptr_t i, n;
+ int doc_is_init = 0;
+ res_T res = RES_OK;
+ ASSERT(parser);
+
+ filename = str_cget(&parser->stream_name);
+
+ if(!parser->parser_is_init) {
+ res = RES_BAD_OP;
+ goto error;
+ }
+
+ if(!yaml_parser_load(&parser->parser, &doc)) {
+ fprintf(stderr, "%s:%lu:%lu: %s.\n",
+ filename,
+ (unsigned long)parser->parser.problem_mark.line+1,
+ (unsigned long)parser->parser.problem_mark.column+1,
+ parser->parser.problem);
+ res = RES_IO_ERR;
+ goto error;
+ }
+ doc_is_init = 1;
+
+ root = yaml_document_get_root_node(&doc);
+ if(!root) {
+ yaml_parser_delete(&parser->parser);
+ parser->parser_is_init = 0;
+ res = RES_BAD_OP;
+ goto error;
+ }
+
+ if(root->type != YAML_SEQUENCE_NODE) {
+ log_err(filename, root, "expect a list of items.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ n = root->data.sequence.items.top - root->data.sequence.items.start;
+ FOR_EACH(i, 0, n) {
+ yaml_node_t* item;
+
+ item = yaml_document_get_node(&doc, root->data.sequence.items.start[i]);
+ res = parse_item(filename, &doc, item);
+ if(res != RES_OK) goto error;
+ }
+
+exit:
+ if(doc_is_init) yaml_document_delete(&doc);
+ return res;
+error:
+ goto exit;
+}
+
diff --git a/src/solstice_parser.h b/src/solstice_parser.h
@@ -0,0 +1,49 @@
+/* Copyright (C) CNRS 2016
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef SOLSTICE_PARSER_H
+#define SOLSTICE_PARSER_H
+
+#include <rsys/rsys.h>
+
+struct mem_allocator;
+struct solstice_parser;
+
+extern LOCAL_SYM res_T
+solstice_parser_create
+ (struct mem_allocator* allocator, /* May be NULL <=> use default allocator */
+ struct solstice_parser** parser);
+
+extern LOCAL_SYM void
+solstice_parser_ref_get
+ (struct solstice_parser* parser);
+
+extern LOCAL_SYM void
+solstice_parser_ref_put
+ (struct solstice_parser* parser);
+
+extern LOCAL_SYM res_T
+solstice_parser_setup
+ (struct solstice_parser* parser,
+ const char* stream_name, /* May be NULL */
+ FILE* stream);
+
+/* Return RES_BAD_OP if there is no more YAML document to parse */
+extern LOCAL_SYM res_T
+solstice_parser_load
+ (struct solstice_parser* parser);
+
+#endif /* SOLSTICE_PARSER_H */
+