10 #include <ecoli_node.h>
11 #include <ecoli_config.h>
13 /* associate a yaml node to a ecoli node */
15 const yaml_node_t *ynode;
16 struct ec_node *enode;
19 /* store the ecoli node tree and the associations yaml_node <-> ec_node */
26 static struct ec_node *
27 parse_ec_node(struct enode_tree *tree,
28 const yaml_document_t *document, const yaml_node_t *ynode);
30 static struct ec_config *
31 parse_ec_config_list(struct enode_tree *tree,
32 const struct ec_config_schema *schema,
33 const yaml_document_t *document, const yaml_node_t *ynode);
35 static struct ec_config *
36 parse_ec_config_dict(struct enode_tree *tree,
37 const struct ec_config_schema *schema,
38 const yaml_document_t *document, const yaml_node_t *ynode);
40 /* XXX to utils.c ? */
42 parse_llint(const char *str, int64_t *val)
45 int save_errno = errno;
48 *val = strtoll(str, &endptr, 0);
50 if ((errno == ERANGE && (*val == LLONG_MAX || *val == LLONG_MIN)) ||
51 (errno != 0 && *val == 0))
64 parse_ullint(const char *str, uint64_t *val)
67 int save_errno = errno;
69 /* since a negative input is silently converted to a positive
70 * one by strtoull(), first check that it is positive */
75 *val = strtoull(str, &endptr, 0);
77 if ((errno == ERANGE && *val == ULLONG_MAX) ||
78 (errno != 0 && *val == 0))
89 parse_bool(const char *str, bool *val)
91 if (!strcasecmp(str, "true")) {
94 } else if (!strcasecmp(str, "false")) {
103 add_in_table(struct enode_tree *tree, const yaml_node_t *ynode,
104 struct ec_node *enode)
106 struct pair *table = NULL;
108 table = realloc(tree->table, (tree->table_len + 1) * sizeof(*table));
112 ec_node_clone(enode);
113 table[tree->table_len].ynode = ynode;
114 table[tree->table_len].enode = enode;
122 free_tree(struct enode_tree *tree)
126 if (tree->root != NULL)
127 ec_node_free(tree->root);
128 for (i = 0; i < tree->table_len; i++)
129 ec_node_free(tree->table[i].enode);
133 static struct ec_config *
134 parse_ec_config(struct enode_tree *tree,
135 const struct ec_config_schema *schema_elt,
136 const yaml_document_t *document, const yaml_node_t *ynode)
138 const struct ec_config_schema *subschema;
139 struct ec_config *config = NULL;
140 struct ec_node *enode = NULL;
141 enum ec_config_type type;
142 const char *value_str;
147 type = ec_config_schema_type(schema_elt);
150 case EC_CONFIG_TYPE_BOOL:
151 if (ynode->type != YAML_SCALAR_NODE) {
152 fprintf(stderr, "Boolean should be scalar\n");
155 value_str = (const char *)ynode->data.scalar.value;
156 if (parse_bool(value_str, &boolean) < 0) {
157 fprintf(stderr, "Failed to parse boolean\n");
160 config = ec_config_bool(boolean);
161 if (config == NULL) {
162 fprintf(stderr, "Failed to create config\n");
166 case EC_CONFIG_TYPE_INT64:
167 if (ynode->type != YAML_SCALAR_NODE) {
168 fprintf(stderr, "Int64 should be scalar\n");
171 value_str = (const char *)ynode->data.scalar.value;
172 if (parse_llint(value_str, &i64) < 0) {
173 fprintf(stderr, "Failed to parse i64\n");
176 config = ec_config_i64(i64);
177 if (config == NULL) {
178 fprintf(stderr, "Failed to create config\n");
182 case EC_CONFIG_TYPE_UINT64:
183 if (ynode->type != YAML_SCALAR_NODE) {
184 fprintf(stderr, "Uint64 should be scalar\n");
187 value_str = (const char *)ynode->data.scalar.value;
188 if (parse_ullint(value_str, &u64) < 0) {
189 fprintf(stderr, "Failed to parse u64\n");
192 config = ec_config_u64(u64);
193 if (config == NULL) {
194 fprintf(stderr, "Failed to create config\n");
198 case EC_CONFIG_TYPE_STRING:
199 if (ynode->type != YAML_SCALAR_NODE) {
200 fprintf(stderr, "String should be scalar\n");
203 value_str = (const char *)ynode->data.scalar.value;
204 config = ec_config_string(value_str);
205 if (config == NULL) {
206 fprintf(stderr, "Failed to create config\n");
210 case EC_CONFIG_TYPE_NODE:
211 enode = parse_ec_node(tree, document, ynode);
214 config = ec_config_node(enode);
215 if (config == NULL) {
216 fprintf(stderr, "Failed to create config\n");
220 case EC_CONFIG_TYPE_LIST:
221 subschema = ec_config_schema_sub(schema_elt);
222 if (subschema == NULL) {
223 fprintf(stderr, "List has no subschema\n");
226 config = parse_ec_config_list(tree, subschema, document, ynode);
230 case EC_CONFIG_TYPE_DICT:
231 subschema = ec_config_schema_sub(schema_elt);
232 if (subschema == NULL) {
233 fprintf(stderr, "Dict has no subschema\n");
236 config = parse_ec_config_dict(tree, subschema, document, ynode);
241 fprintf(stderr, "Invalid config type %d\n", type);
249 ec_config_free(config);
253 static struct ec_config *
254 parse_ec_config_list(struct enode_tree *tree,
255 const struct ec_config_schema *schema,
256 const yaml_document_t *document, const yaml_node_t *ynode)
258 struct ec_config *config = NULL, *subconfig = NULL;
259 const yaml_node_item_t *item;
260 const yaml_node_t *value;
266 if (ynode->type != YAML_SEQUENCE_NODE) {
267 fprintf(stderr, "Ecoli list config should be a yaml sequence\n");
271 config = ec_config_list();
272 if (config == NULL) {
273 fprintf(stderr, "Failed to allocate config\n");
277 for (item = ynode->data.sequence.items.start;
278 item < ynode->data.sequence.items.top; item++) {
279 value = document->nodes.start + (*item) - 1; // XXX -1 ?
280 subconfig = parse_ec_config(tree, schema, document, value);
281 if (subconfig == NULL)
283 if (ec_config_list_add(config, subconfig) < 0) {
284 fprintf(stderr, "Failed to add list entry\n");
292 ec_config_free(config);
296 static struct ec_config *
297 parse_ec_config_dict(struct enode_tree *tree,
298 const struct ec_config_schema *schema,
299 const yaml_document_t *document, const yaml_node_t *ynode)
301 const struct ec_config_schema *schema_elt;
302 struct ec_config *config = NULL, *subconfig = NULL;
303 const yaml_node_t *key, *value;
304 const yaml_node_pair_t *pair;
307 if (ynode->type != YAML_MAPPING_NODE) {
308 fprintf(stderr, "Ecoli config should be a yaml mapping node\n");
312 config = ec_config_dict();
313 if (config == NULL) {
314 fprintf(stderr, "Failed to allocate config\n");
318 for (pair = ynode->data.mapping.pairs.start;
319 pair < ynode->data.mapping.pairs.top; pair++) {
320 key = document->nodes.start + pair->key - 1; // XXX -1 ?
321 value = document->nodes.start + pair->value - 1;
322 key_str = (const char *)key->data.scalar.value;
324 if (ec_config_key_is_reserved(key_str))
326 schema_elt = ec_config_schema_lookup(schema, key_str);
327 if (schema_elt == NULL) {
328 fprintf(stderr, "No such config %s\n", key_str);
331 subconfig = parse_ec_config(tree, schema_elt, document, value);
332 if (subconfig == NULL)
334 if (ec_config_dict_set(config, key_str, subconfig) < 0) {
335 fprintf(stderr, "Failed to set dict entry\n");
343 ec_config_free(config);
347 static struct ec_node *
348 parse_ec_node(struct enode_tree *tree,
349 const yaml_document_t *document, const yaml_node_t *ynode)
351 const struct ec_config_schema *schema;
352 const struct ec_node_type *type = NULL;
353 const char *id = NULL, *help = NULL;
354 struct ec_config *config = NULL;
355 const yaml_node_t *attrs = NULL;
356 const yaml_node_t *key, *value;
357 const yaml_node_pair_t *pair;
358 const char *key_str, *value_str;
359 struct ec_node *enode = NULL;
361 if (ynode->type != YAML_MAPPING_NODE) {
362 fprintf(stderr, "Ecoli node should be a yaml mapping node\n");
366 for (pair = ynode->data.mapping.pairs.start;
367 pair < ynode->data.mapping.pairs.top; pair++) {
368 key = document->nodes.start + pair->key - 1; // XXX -1 ?
369 value = document->nodes.start + pair->value - 1;
370 key_str = (const char *)key->data.scalar.value;
371 value_str = (const char *)value->data.scalar.value;
373 if (!strcmp(key_str, "type")) {
375 fprintf(stderr, "Duplicate type\n");
378 if (value->type != YAML_SCALAR_NODE) {
379 fprintf(stderr, "Type must be a string\n");
382 type = ec_node_type_lookup(value_str);
384 fprintf(stderr, "Cannot find type %s\n",
388 } else if (!strcmp(key_str, "attrs")) {
390 fprintf(stderr, "Duplicate attrs\n");
393 if (value->type != YAML_MAPPING_NODE) {
394 fprintf(stderr, "Attrs must be a maping\n");
398 } else if (!strcmp(key_str, "id")) {
400 fprintf(stderr, "Duplicate id\n");
403 if (value->type != YAML_SCALAR_NODE) {
404 fprintf(stderr, "Id must be a scalar\n");
408 } else if (!strcmp(key_str, "help")) {
410 fprintf(stderr, "Duplicate help\n");
413 if (value->type != YAML_SCALAR_NODE) {
414 fprintf(stderr, "Help must be a scalar\n");
421 /* create the ecoli node */
424 enode = ec_node_from_type(type, id);
426 fprintf(stderr, "Cannot create ecoli node\n");
429 if (add_in_table(tree, ynode, enode) < 0) {
430 fprintf(stderr, "Cannot add node in table\n");
433 if (tree->root == NULL) {
434 ec_node_clone(enode);
438 /* create its config */
439 schema = ec_node_type_schema(type);
440 if (schema == NULL) {
441 fprintf(stderr, "No configuration schema for type %s\n",
442 ec_node_type_name(type));
446 config = parse_ec_config_dict(tree, schema, document, ynode);
450 if (ec_node_set_config(enode, config) < 0) {
451 fprintf(stderr, "Failed to set config\n");
455 /* add attributes (all as string) */
462 ec_config_free(config);
467 parse_document(struct enode_tree *tree, const yaml_document_t *document)
471 node = document->nodes.start;
472 if (parse_ec_node(tree, document, node) == NULL)
478 static int parse_file(struct enode_tree *tree, const char *filename)
481 yaml_parser_t parser;
482 yaml_document_t document;
484 file = fopen(filename, "rb");
486 fprintf(stderr, "Failed to open file %s\n", filename);
490 if (yaml_parser_initialize(&parser) == 0) {
491 fprintf(stderr, "Failed to initialize yaml parser\n");
495 yaml_parser_set_input_file(&parser, file);
497 if (yaml_parser_load(&parser, &document) == 0) {
498 fprintf(stderr, "Failed to load yaml document\n");
502 if (yaml_document_get_root_node(&document) == NULL) {
503 fprintf(stderr, "Incomplete document\n"); //XXX check err
507 if (parse_document(tree, &document) < 0) {
508 fprintf(stderr, "Failed to parse document\n");
512 yaml_document_delete(&document);
513 yaml_parser_delete(&parser);
519 yaml_document_delete(&document);
521 yaml_parser_delete(&parser);
529 main(int argc, char *argv[])
531 struct enode_tree tree;
533 memset(&tree, 0, sizeof(tree));
536 fprintf(stderr, "Invalid args\n");
539 if (parse_file(&tree, argv[1]) < 0) {
540 fprintf(stderr, "Failed to parse file\n");
543 printf("root=%p len=%zd\n", tree.root, tree.table_len);
544 ec_node_dump(stdout, tree.root);