FMS  2024.03
Flexible Modeling System
yaml_parser_binding.c
1 /***********************************************************************
2  * GNU Lesser General Public License
3  *
4  * This file is part of the GFDL Flexible Modeling System (FMS).
5  *
6  * FMS is free software: you can redistribute it and/or modify it under
7  * the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or (at
9  * your option) any later version.
10  *
11  * FMS is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14  * for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FMS. If not, see <http://www.gnu.org/licenses/>.
18  **********************************************************************/
19 
20 #ifdef use_yaml
21 
22 #include <stdio.h>
23 #include <yaml.h>
24 #include <stdbool.h>
25 
26 /* Type to store info about key */
27 typedef struct {
28  int key_number; /* Id of this key */
29  char key[255]; /* Name of the key */
30  char value[255]; /* Value of the key */
31  char parent_name[255]; /* Name of the block the key belongs to */
32  int parent_key; /* Id of the block the key belongs to */
33 }key_value_pairs;
34 
35 /* Type to store all of the keys */
36 typedef struct {
37  int nkeys;
38  key_value_pairs *keys;
39 }yaml_file;
40 
41 /* Type to store all the yaml files that are opened */
42 typedef struct {
43  yaml_file *files;
44 }file_type;
45 
46 file_type my_files; /* Array of opened yaml files */
47 int nfiles = 0; /* Number of files in the yaml file */
48 
49 /* @brief Private c function that gets the number of key-value pairs in a block
50  @return Number of key-value pairs in this block */
51 int get_nkeys_binding(int *file_id, int *block_id)
52 {
53  int nkeys = 0; /* Number of key-value pairs */
54  int i; /* For loops */
55  int j = *file_id; /* To minimize the typing :) */
56 
57  for ( i = 1; i <= my_files.files[j].nkeys; i++ )
58  {
59  if(my_files.files[j].keys[i].parent_key == *block_id && !strcmp(my_files.files[j].keys[i].parent_name, "") ) nkeys = nkeys + 1;
60  }
61 
62  return nkeys;
63 
64 }
65 
66 /* @brief Private c function that gets the ids of the key-value pairs in a block */
67 void get_key_ids_binding(int *file_id, int *block_id, int *key_ids)
68 {
69  int i; /* For loops */
70  int key_count = -1; /* Number of key-value pairs */
71  int j = *file_id; /* To minimize the typing :) */
72 
73  for ( i = 1; i <= my_files.files[j].nkeys; i++ )
74  {
75  if(my_files.files[j].keys[i].parent_key == *block_id && !strcmp(my_files.files[j].keys[i].parent_name, "") ){
76  key_count = key_count + 1;
77  key_ids[key_count] = i;
78  }
79  }
80 
81  return;
82 }
83 
84 /* @brief Private c function that get the key from a key_id in a yaml file
85  @return Name of the key obtained */
86 char *get_key(int *file_id, int *key_id)
87 {
88  int j = *file_id; /* To minimize the typing :) */
89  return my_files.files[j].keys[*key_id].key;
90 }
91 
92 /* @brief Private c function that get the value from a key_id in a yaml file
93  @return String containing the value obtained */
94 char *get_value(int *file_id, int *key_id)
95 {
96  int j = *file_id; /* To minimize the typing :) */
97  return my_files.files[j].keys[*key_id].value;
98 }
99 
100 /* @brief Private c functions get gets the block name from a block id
101  @return String containing the value obtained */
102 char *get_block(int *file_id, int *block_id)
103 {
104  int j = *file_id; /* To minimize the typing :) */
105  return my_files.files[j].keys[*block_id].parent_name;
106 }
107 
108 /* @brief Private c function that determines they value of a key in yaml_file
109  @return c pointer with the value obtained */
110 char *get_value_from_key_wrap(int *file_id, int *block_id, char *key_name, int *sucess) /*, char *key_name) */
111 {
112  int i; /* For loops */
113  int j = *file_id; /* To minimize the typing :) */
114 
115  *sucess = 0; /* Flag indicating if the search was sucessful */
116 
117  for ( i = 1; i <= my_files.files[j].nkeys; i++ )
118  {
119  if (my_files.files[j].keys[i].parent_key == *block_id)
120  {
121  if( strcmp(my_files.files[j].keys[i].key, key_name) == 0)
122  {
123  *sucess = 1;
124  break;
125  }
126  }
127  }
128  if (*sucess == 1) {return my_files.files[j].keys[i].value;} else {return "";}
129 }
130 
131 /* @brief Private c function that determines the number of blocks with block_name in the yaml file
132  @return Number of blocks with block_name */
133 int get_num_blocks_all(int *file_id, char *block_name)
134 {
135  int nblocks = 0; /* Number of blocks */
136  int i; /* For loops */
137  int j = *file_id; /* To minimize the typing :) */
138 
139  for ( i = 1; i <= my_files.files[j].nkeys; i++ )
140  {
141  if(strcmp(my_files.files[j].keys[i].parent_name, block_name) == 0) nblocks = nblocks + 1;
142  }
143 
144  return nblocks;
145 }
146 
147 /* @brief Private c function that determines the number of unique blocks (i.e diag_files, varlist, etc)
148  @return The number of unique blocks */
149 int get_num_unique_blocks_bind(int *file_id, int *parent_block_id)
150 {
151  int nblocks = 0; /* Number of blocks */
152  int i; /* For loops */
153  int j = *file_id; /* To minimize the typing :) */
154  char block_names[my_files.files[j].nkeys][255]; /* Array that stores the names of the unique blocks*/
155  bool found; /* True if the block name was already found (i.e it not unqiue)*/
156  int k; /* For loops */
157 
158  for ( i = 1; i <= my_files.files[j].nkeys; i++ )
159  {
160  if (my_files.files[j].keys[i].parent_key == *parent_block_id )
161  {
162  if (strcmp(my_files.files[j].keys[i].parent_name, "") == 0){
163  continue;
164  }
165  found = false;
166  for (k = 1; k <= nblocks; k++)
167  {
168  if (strcmp(block_names[k], my_files.files[j].keys[i].parent_name) == 0)
169  {
170  found = true;
171  break;
172  }
173  }
174 
175  if (found) continue;
176 
177  nblocks = nblocks + 1;
178  strcpy(block_names[nblocks], my_files.files[j].keys[i].parent_name);
179  // printf("Block names: %s \n", block_names[nblocks]);
180  }
181  }
182  return nblocks;
183 }
184 
185 /* @brief Private c function that determines the ids of the unique blocks (i.e diag_files, varlist, etc)
186  @return The ids of the unique blocks */
187 void get_unique_block_ids_bind(int *file_id, int *block_ids, int *parent_block_id)
188 {
189  int nblocks = 0; /* Number of blocks */
190  int i; /* For loops */
191  int j = *file_id; /* To minimize the typing :) */
192  char block_names[my_files.files[j].nkeys][255]; /* Array that stores the names of the unique blocks*/
193  bool found; /* True if the block name was already found (i.e it not unqiue)*/
194  int k; /* For loops */
195 
196  for ( i = 1; i <= my_files.files[j].nkeys; i++ )
197  {
198  if (my_files.files[j].keys[i].parent_key == *parent_block_id )
199  {
200  if (strcmp(my_files.files[j].keys[i].parent_name, "") == 0){
201  continue;
202  }
203  found = false;
204  for (k = 1; k <= nblocks; k++)
205  {
206  if (strcmp(block_names[k], my_files.files[j].keys[i].parent_name) == 0)
207  {
208  found = true;
209  break;
210  }
211  }
212 
213  if (found) continue;
214 
215  nblocks = nblocks + 1;
216  block_ids[nblocks - 1] = my_files.files[j].keys[i].key_number;
217  strcpy(block_names[nblocks], my_files.files[j].keys[i].parent_name);
218  //printf("Block names: %s \n", block_names[nblocks]);
219  }
220  }
221  return;
222 }
223 /* @brief Private c function that determines the number of blocks with block_name that belong to
224  a parent block with parent_block_id in the yaml file
225  @return Number of blocks with block_name */
226 int get_num_blocks_child(int *file_id, char *block_name, int *parent_block_id)
227 {
228  int nblocks = 0; /* Number of blocks */
229  int i; /* For loops */
230  int j = *file_id; /* To minimize the typing :) */
231 
232  for ( i = 1; i <= my_files.files[j].nkeys; i++ )
233  {
234  if(strcmp(my_files.files[j].keys[i].parent_name, block_name) == 0 && my_files.files[j].keys[i].parent_key == *parent_block_id) nblocks = nblocks + 1;
235  }
236 
237  return nblocks;
238 }
239 
240 
241 /* @brief Private c function that gets the the ids of the blocks with block_name in the yaml file */
242 void get_block_ids_all(int *file_id, char *block_name, int *block_ids)
243 {
244  int i; /* For loops */
245  int nblocks = -1; /* Number of blocks */
246  int j = *file_id; /* To minimize the typing :) */
247 
248  for ( i = 1; i <= my_files.files[j].nkeys; i++ )
249  {
250  if(strcmp(my_files.files[j].keys[i].parent_name, block_name) == 0) {
251  nblocks = nblocks + 1;
252  block_ids[nblocks] = my_files.files[j].keys[i].key_number;
253  }
254  }
255  return;
256 }
257 
258 /* @brief Private c function that gets the the ids of the blocks with block_name and that
259  belong to a parent block id in the yaml file */
260 void get_block_ids_child(int *file_id, char *block_name, int *block_ids, int *parent_key_id )
261 {
262  int i; /* For loops */
263  int nblocks = -1; /* Number of blocks */
264  int j = *file_id; /* To minimize the typing :) */
265 
266  for ( i = 1; i <= my_files.files[j].nkeys; i++ )
267  {
268  if(strcmp(my_files.files[j].keys[i].parent_name, block_name) == 0 && my_files.files[j].keys[i].parent_key == *parent_key_id) {
269  nblocks = nblocks + 1;
270  block_ids[nblocks] = my_files.files[j].keys[i].key_number;
271  }
272  }
273  return;
274 }
275 
276 /* @brief Private c function to determine if a block_id is valid */
277 bool is_valid_block_id(int *file_id, int *block_id)
278 {
279  /* If the block id it not in the allowed range is not a valid block id */
280  if (*block_id <= -1 || *block_id > my_files.files[*file_id].nkeys) {return false;}
281 
282  /* If the block id has an empty parent name then it is not a valid block id */
283  if (*block_id != 0 && strcmp(my_files.files[*file_id].keys[*block_id].parent_name, "") == 0) {return false;}
284  return true;
285 }
286 
287 /* @brief Private c function to determine if a key_id is valid */
288 bool is_valid_key_id(int *file_id, int *key_id)
289 {
290  if (*key_id > -1 && *key_id <= my_files.files[*file_id].nkeys) {return true;}
291  else { return false;}
292 }
293 
294 /* @brief Private c function to determine if a file_id is valid */
295 bool is_valid_file_id(int *file_id)
296 {
297  if (*file_id > -1 && *file_id < nfiles) {return true;}
298  else { return false;}
299 }
300 
301 /* @brief Private c function that opens and parses a yaml file and saves it in a struct
302  @return Flag indicating if the read was sucessful */
303 int open_and_parse_file_wrap(char *filename, int *file_id)
304 {
305  yaml_parser_t parser;
306  yaml_token_t token;
307  FILE *file;
308 
309  bool is_key = false; /* Flag indicating if the current token in a key */
310  char key_value[255]; /* Value of a key */
311  int layer = 0; /* Current layer (block level) */
312  int key_count=0; /* Current number of keys */
313  int parent[10]; /* Ids of blocks */
314  int current_parent; /* Id of the current block */
315  char layer_name[10][255]; /* Array of block names */
316  char current_layername[255]; /* Name of the current block */
317  int i; /* To minimize the typing :) */
318  int j; /* To minimize the typing :) */
319 
320  if (nfiles == 0 )
321  {
322  my_files.files = (yaml_file*)calloc(1, sizeof(yaml_file));
323  } else
324  {
325  my_files.files = realloc(my_files.files, (nfiles+1)*sizeof(yaml_file));
326  }
327 
328  j = nfiles;
329  *file_id =j;
330 
331 /* printf("Opening file: %s.\nThere are %i files opened.\n", filename, j); */
332  file = fopen(filename, "r");
333  if (file == NULL) return -1;
334 
335  if(!yaml_parser_initialize(&parser)) return -2;
336 
337  my_files.files[j].keys = (key_value_pairs*)calloc(1, sizeof(key_value_pairs));
338 
339  parent[0]=0;
340  strcpy(layer_name[0], "TOP");
341  /* Set input file */
342  yaml_parser_set_input_file(&parser, file);
343  do {
344  if (!yaml_parser_scan(&parser, &token)) {
345  return -3;
346  }
347  switch(token.type)
348  {
349  case YAML_KEY_TOKEN:
350  {
351  is_key = true;
352  break;
353  }
354  case YAML_VALUE_TOKEN:
355  {
356  is_key = false;
357  break;
358  }
359  case YAML_BLOCK_ENTRY_TOKEN:
360  {
361  layer = layer + 1;
362 
363  if (strcmp(key_value, ""))
364  {
365  strcpy(layer_name[layer], key_value);
366  }
367  key_count = key_count + 1;
368  i = key_count;
369  my_files.files[j].keys = realloc(my_files.files[j].keys, (i+1)*sizeof(key_value_pairs));
370  my_files.files[j].keys[i].key_number=i;
371  my_files.files[j].keys[i].parent_key = parent[layer-1];
372  strcpy(my_files.files[j].keys[i].parent_name, layer_name[layer]);
373  strcpy(my_files.files[j].keys[i].key, "");
374  strcpy(my_files.files[j].keys[i].value, "");
375  parent[layer]=key_count;
376  /*printf("KEY:%i LAYER:%i NAME:%s for %s=%i\n", key_count, layer, layer_name[layer], layer_name[layer-1], parent[layer-1]); */
377 
378  break;
379  }
380  case YAML_BLOCK_END_TOKEN:
381  {
382  layer = layer - 1;
383  break;
384  }
385  case YAML_SCALAR_TOKEN:
386  {
387  if ( ! is_key)
388  {
389  current_parent = parent[layer];
390  strcpy(current_layername, "");
391  key_count = key_count + 1;
392  i = key_count;
393  my_files.files[j].keys = realloc(my_files.files[j].keys, (i+1)*sizeof(key_value_pairs));
394  my_files.files[j].keys[i].key_number=i;
395  my_files.files[j].keys[i].parent_key = current_parent;
396  strcpy(my_files.files[j].keys[i].parent_name, current_layername);
397  strcpy(my_files.files[j].keys[i].key, key_value);
398  strcpy(my_files.files[j].keys[i].value, token.data.scalar.value);
399  my_files.files[j].nkeys = key_count;
400  /* printf("----> LAYER:%i LAYER_NAME=%s PARENT:%i, KEYCOUNT:%i KEY: %s VALUE: %s \n", layer, current_layername, current_parent, key_count, key_value, token.data.scalar.value); */
401  strcpy(key_value,"");
402  }
403  else
404  {strcpy(key_value,token.data.scalar.value);}
405  }
406  break;
407  }
408  if(token.type != YAML_STREAM_END_TOKEN)
409  yaml_token_delete(&token);
410  } while(token.type != YAML_STREAM_END_TOKEN);
411  yaml_token_delete(&token);
412  yaml_parser_delete(&parser);
413 
414  /*
415  for ( i = 1; i <= my_files.files[j].nkeys; i++ ) {
416  printf("Key_number:%i Parent_key:%i Parent_name:%s Key:%s Value:%s \n", my_files.files[j].keys[i].key_number, my_files.files[j].keys[i].parent_key, my_files.files[j].keys[i].parent_name, my_files.files[j].keys[i].key, my_files.files[j].keys[i].value);
417  }
418  printf("/\n");
419  */
420 
421  nfiles = nfiles + 1;
422 /* printf("closing file: %s\n", filename); */
423  fclose(file);
424 
425  return 1;
426 }
427 
428 #endif