Adaptive Framework  0.9.0
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
afw_yaml_to_value.c
1 // See the 'COPYING' file in the project root for licensing information.
2 /*
3  * AFW yaml conversion functions.
4  *
5  * Copyright (c) 2010-2023 Clemson University
6  *
7  */
8 
15 #include "afw.h"
16 #include "afw_yaml.h"
17 #include <yaml.h>
18 
19 
20 typedef struct afw_yaml_parser_s {
21  yaml_parser_t parser;
22  apr_hash_t *anchors;
23  afw_boolean_t docStarted;
24  const afw_object_t *embedding_object;
25  const afw_utf8_t *property_name;
26  const afw_pool_t *p;
27  const afw_utf8_t *path;
28  afw_boolean_t cede_p;
30 
31 const afw_value_t * afw_yaml_parse_value(
32  afw_yaml_parser_t *parser, afw_xctx_t *xctx);
33 
34 const afw_object_t * afw_yaml_parse_object(
35  afw_yaml_parser_t *parser, afw_xctx_t *xctx);
36 
37 const afw_list_t * afw_yaml_parse_list(
38  afw_yaml_parser_t *parser, afw_xctx_t *xctx);
39 
40 const afw_value_t * afw_yaml_parse_scalar(
41  afw_yaml_parser_t *parser, yaml_token_t *token, afw_xctx_t *xctx);
42 
43 yaml_token_t * afw_yaml_parser_scan(
44  afw_yaml_parser_t *parser, afw_xctx_t *xctx);
45 
46 
47 void afw_yaml_token_cleanup(
48  void *data, void *data2, const afw_pool_t *p, afw_xctx_t *xctx)
49 {
50  yaml_token_t *token = (yaml_token_t *)data;
51 
52  yaml_token_delete(token);
53 }
54 
58 yaml_token_t * afw_yaml_parser_scan(
59  afw_yaml_parser_t *parser, afw_xctx_t *xctx)
60 {
61  int rc;
62  yaml_token_t *token;
63 
64  token = afw_pool_calloc_type(xctx->p, yaml_token_t, xctx);
65 
66  rc = yaml_parser_scan(&parser->parser, token);
67  if (rc == 0) {
68  AFW_THROW_ERROR_RV_FZ(general, yaml_parser, rc, xctx,
69  "Error: %s, near line %d, column %d",
70  parser->parser.problem, parser->parser.problem_mark.line,
71  parser->parser.problem_mark.column);
72  }
73 
74  afw_pool_register_cleanup_before(xctx->p, token, NULL,
75  afw_yaml_token_cleanup, xctx);
76 
77  return token;
78 }
79 
80 const afw_value_t * afw_yaml_parse_scalar(
81  afw_yaml_parser_t *parser, yaml_token_t *token, afw_xctx_t *xctx)
82 {
83  const afw_value_t *value;
84  const afw_utf8_t *str;
85  double number;
86 
87  /* examine the "style" property to help decide how to interpret */
88  switch (token->data.scalar.style) {
89  case YAML_PLAIN_SCALAR_STYLE:
90  case YAML_LITERAL_SCALAR_STYLE:
93  if (sscanf((const char *)token->data.scalar.value, "%lf", &number) > 0) {
94  value = afw_value_create_double(number, parser->p, xctx);
95  } else if (strcmp((const char *)token->data.scalar.value, "true") == 0) {
96  value = afw_value_true;
97  } else if (strcmp((const char *)token->data.scalar.value, "false") == 0) {
98  value = afw_value_false;
99  } else if (strcmp((const char *)token->data.scalar.value, "null") == 0) {
100  value = afw_value_null;
101  } else {
102  /* else must be a string value */
103  str = afw_utf8_create_copy((const afw_utf8_octet_t *)token->data.scalar.value,
104  strlen((const afw_utf8_octet_t *)token->data.scalar.value), parser->p,
105  xctx);
106 
107  value = afw_value_create_string(str, parser->p, xctx);
108  }
109  break;
110 
111  /* treat all of these as string data types */
112  case YAML_ANY_SCALAR_STYLE:
113  case YAML_FOLDED_SCALAR_STYLE:
114  case YAML_SINGLE_QUOTED_SCALAR_STYLE:
115  case YAML_DOUBLE_QUOTED_SCALAR_STYLE:
116  default:
117  str = afw_utf8_create_copy(
118  (const afw_utf8_octet_t *)token->data.scalar.value,
119  strlen((const afw_utf8_octet_t *)token->data.scalar.value),
120  parser->p, xctx);
121 
122  value = afw_value_create_string(str, parser->p, xctx);
123  break;
124  }
125 
126  return value;
127 }
128 
129 
130 const afw_list_t * afw_yaml_parse_list(
131  afw_yaml_parser_t *parser, afw_xctx_t *xctx)
132 {
133  const afw_list_t *list;
134  const afw_value_t *value;
135 
136  list = afw_list_create_generic(parser->p, xctx);
137 
138  do {
139  value = afw_yaml_parse_value(parser, xctx);
140  if (value) {
141  afw_list_add_value(list, value, xctx);
142  }
143  } while (value);
144 
145  /* Return. */
146  return list;
147 }
148 
149 const afw_object_t * afw_yaml_parse_object(
150  afw_yaml_parser_t *parser, afw_xctx_t *xctx)
151 {
152  yaml_token_t *token;
153  const afw_object_t *object;
154  const afw_utf8_t *key = NULL;
155  const afw_value_t *v;
156  int done = 0;AFW_POSSIBLY_UNUSED_VARIABLE const afw_utf8_t *default_path;
158  const afw_object_t *saved_embedding_object;
159  const afw_utf8_t *saved_property_name;
160  const afw_object_t *_meta_;
161 
162  /* Create new memory object.*/
164  parser->embedding_object,
165  parser->property_name,
166  false, parser->cede_p, parser->p, xctx);
167  _meta_ = NULL;
168 
169  /*
170  * Save parser->embedding_object and set to new object.
171  *
172  * Save parser->property_name and set to NULL.
173  *
174  * If entity, default path is supplied by caller, otherwise it's NULL.
175  */
176  saved_embedding_object = parser->embedding_object;
177  parser->embedding_object = object;
178  saved_property_name = parser->property_name;
179  parser->property_name = NULL;
180  default_path = (!saved_embedding_object) ? parser->path : NULL;
181 
182  while (!done) {
183  token = afw_yaml_parser_scan(parser, xctx);
184 
185  if (token->type == YAML_FLOW_ENTRY_TOKEN)
186  continue;
187 
188  if (token->type == YAML_KEY_TOKEN) {
189  v = afw_yaml_parse_value(parser, xctx);
190  if (v && afw_value_is_string(v)) {
191  key = afw_value_as_utf8(v, xctx->p, xctx);
192  parser->property_name = key;
193  }
194  } else if (token->type == YAML_VALUE_TOKEN) {
195  v = afw_yaml_parse_value(parser, xctx);
196  if (v) {
199  /* check if it's a meta object */
200  if (afw_utf8_equal(key, &afw_s__meta_)) {
201  if (!afw_value_is_object(v)) {
202  AFW_THROW_ERROR_Z(general, "_meta_ property must be an object", xctx);
203  }
204  _meta_ = ((const afw_value_object_t*)v)->internal;
205  }
206 
207  /*
208  * If not _meta_ property, set property in new object to this
209  * value if it's not an object. Object will already have been
210  * added by afw_object_create_embedded().
211  */
212  else if (!afw_value_is_object(v)) {
213  afw_object_set_property(object, key, v, xctx);
214  }
215  }
216  } else if (token->type != YAML_BLOCK_END_TOKEN &&
217  token->type != YAML_FLOW_MAPPING_END_TOKEN) {
218  AFW_THROW_ERROR_RV_FZ(general, yaml_token_type, token->type, xctx,
219  "Unexpected token inside map, starting at line %d, column %d",
220  parser->parser.mark.line, parser->parser.mark.column);
221  }
222 
223  done = (token->type == YAML_BLOCK_END_TOKEN ||
224  token->type == YAML_FLOW_MAPPING_END_TOKEN);
225  }
226 
227  /* Set meta. Note: must be called after properties are set. */
228  afw_object_meta_set_meta_object(object, _meta_, xctx);
229 
230  /* Set parser->embedding_object to previous value and return object. */
231  parser->embedding_object = saved_embedding_object;
232  parser->property_name = saved_property_name;
233  return object;
234 }
235 
236 const afw_value_t * afw_yaml_parse_value(
237  afw_yaml_parser_t *parser, afw_xctx_t *xctx)
238 {
239  yaml_token_t *token;
240  const afw_value_t *value = NULL;
241  const afw_object_t *obj;
242  const afw_list_t *list;
243 
244  while (value == NULL) {
245  token = afw_yaml_parser_scan(parser, xctx);
246 
247  switch (token->type) {
248  case YAML_SCALAR_TOKEN:
249  /* return the appropriate scalar value */
250  value = afw_yaml_parse_scalar(parser, token, xctx);
251  break;
252 
253  case YAML_BLOCK_MAPPING_START_TOKEN:
254  case YAML_FLOW_MAPPING_START_TOKEN:
255  /* FLOW mapping denotes {} map, while BLOCK uses spacing */
256  obj = afw_yaml_parse_object(parser, xctx);
257 
258  value = afw_value_create_object(obj, parser->p, xctx);
259  break;
260 
261  case YAML_FLOW_ENTRY_TOKEN:
262  case YAML_BLOCK_ENTRY_TOKEN:
263  continue;
264 
265  case YAML_FLOW_SEQUENCE_START_TOKEN:
266  case YAML_BLOCK_SEQUENCE_START_TOKEN:
267  /* FLOW sequence denotes [] list , while BLOCK sequence denotes - list */
268  list = afw_yaml_parse_list(parser, xctx);
269 
270  value = afw_value_create_list(list, parser->p, xctx);
271  break;
272 
273  case YAML_BLOCK_END_TOKEN:
274  case YAML_FLOW_SEQUENCE_END_TOKEN:
275  /* The end of a sequence or block should return NULL to caller to quit */
276  return NULL;
277  break;
278 
279  case YAML_ANCHOR_TOKEN:
280  /* an anchor defines a value that can be later referenced */
281  value = afw_yaml_parse_value(parser, xctx);
282  apr_hash_set(parser->anchors,
283  apr_pstrdup(afw_pool_get_apr_pool(xctx->p),
284  (const char *)token->data.anchor.value),
285  APR_HASH_KEY_STRING, value);
286 
287  break;
288 
289  case YAML_ALIAS_TOKEN:
290  /* an alias references an anchor */
291  value = apr_hash_get(parser->anchors, token->data.alias.value,
292  APR_HASH_KEY_STRING);
293  if (value == NULL) {
294  AFW_THROW_ERROR_RV_FZ(general, yaml_token_type,
295  token->type, xctx,
296  "Alias references undefined anchor, near line %d, column %d",
297  parser->parser.mark.line, parser->parser.mark.column);
298  }
299  break;
300 
301  case YAML_DOCUMENT_START_TOKEN:
302  if (parser->docStarted) {
303  /* Not supported, can't have more than one document */
304  AFW_THROW_ERROR_RV_Z(general, yaml_token_type, token->type,
305  "Multiple documents are not supported.", xctx);
306  } else
307  parser->docStarted = AFW_TRUE;
308  break;
309 
310  case YAML_TAG_TOKEN:
311  /* A tag tells the application about the data, which could be useful in helping
312  us determine the data type */
313  break;
314 
315  case YAML_VERSION_DIRECTIVE_TOKEN:
316  /* This tells us about the YAML version. FIXME: we may want to restrict features */
317  break;
318 
319  case YAML_TAG_DIRECTIVE_TOKEN:
320  /* This establishes shorthand notation for specifying node tags. Allows for readability */
322  break;
323 
324  default:
325  AFW_THROW_ERROR_RV_FZ(general, yaml_token_type, token->type, xctx,
326  "Unexpected token type, starting at line %d, column %d",
327  parser->parser.mark.line, parser->parser.mark.column);
328  break;
329  }
330  }
331 
332  return value;
333 }
334 
335 /*
336  * Create an adaptive object from yaml.
337  */
339  const afw_memory_t *yaml,
340  const afw_utf8_t *path,
341  const afw_pool_t *p,
342  afw_xctx_t *xctx)
343 {
344  yaml_token_t *token;
345  const afw_value_t *value = NULL;
346  afw_yaml_parser_t parser;
347 
348  memset(&parser, 0, sizeof(afw_yaml_parser_t));
349  parser.embedding_object = NULL;
350  parser.property_name = NULL;
351  parser.p = p;
352  parser.path = path;
353  parser.cede_p = false;
354 
355  /* Initialize parser */
356  if (!yaml_parser_initialize(&parser.parser)) {
357  AFW_THROW_ERROR_Z(general,
358  "Unable to initialize libyaml parser", xctx);
359  }
360 
361  parser.anchors = apr_hash_make(afw_pool_get_apr_pool(xctx->p));
362 
363  yaml_parser_set_input_string(&parser.parser, yaml->ptr, yaml->size);
364 
365  /* We should expect a STREAM_START */
366  token = afw_yaml_parser_scan(&parser, xctx);
367  if (token->type != YAML_STREAM_START_TOKEN) {
368  AFW_THROW_ERROR_RV_Z(general, yaml_token_type, token->type,
369  "Expected start of stream token", xctx);
370  }
371 
372  /* Now parse and return the top-level afw_value_t */
373  value = afw_yaml_parse_value(&parser, xctx);
374 
375  /* We should expect a STREAM_END */
376  token = afw_yaml_parser_scan(&parser, xctx);
377  if (token->type != YAML_STREAM_END_TOKEN) {
378  AFW_THROW_ERROR_RV_Z(general, yaml_token_type, token->type,
379  "Expected end of stream token", xctx);
380  }
381 
382  return value;
383 }
384 
385 /*
386  * Implementation of method raw_to_object of interface afw_content_type.
387  */
389  const afw_memory_t * yaml,
390  const afw_utf8_t * source_location,
391  const afw_utf8_t * adaptor_id,
392  const afw_utf8_t * object_type_id,
393  const afw_utf8_t * object_id,
394  afw_boolean_t cede_p,
395  const afw_pool_t * p,
396  afw_xctx_t * xctx)
397 {
398  afw_yaml_parser_t parser;
399 
400  memset(&parser, 0, sizeof(afw_yaml_parser_t));
401  parser.embedding_object = NULL;
402  parser.property_name = NULL;
403  parser.p = (cede_p) ? p : afw_pool_create(p, xctx);
404  parser.cede_p = afw_object_path_make(
405  adaptor_id, object_type_id, object_id, parser.p, xctx);
406 
407  /* Parse and return object. */
408  return afw_yaml_parse_object(&parser, xctx);
409 }
Adaptive Framework Core API.
Header file for Adaptive Framework YAML.
afw_value_create_double(double internal, const afw_pool_t *p, afw_xctx_t *xctx)
Create function for unmanaged data type double value.
afw_value_create_list(const afw_list_t *internal, const afw_pool_t *p, afw_xctx_t *xctx)
Create function for unmanaged data type list value.
#define afw_value_is_object(A_VALUE)
Macro to determine if value is evaluated object.
afw_value_create_object(const afw_object_t *internal, const afw_pool_t *p, afw_xctx_t *xctx)
Create function for unmanaged data type object value.
afw_value_create_string(const afw_utf8_t *internal, const afw_pool_t *p, afw_xctx_t *xctx)
Create function for unmanaged data type string value.
#define afw_value_is_string(A_VALUE)
Macro to determine if value is evaluated string.
#define AFW_TRUE
Definition: afw_common.h:383
_Bool afw_boolean_t
Definition: afw_common.h:373
char afw_utf8_octet_t
8 bits of utf-8 codepoint.
Definition: afw_common.h:236
#define AFW_POSSIBLY_UNUSED_VARIABLE
Macro to avoid unused variable warning.
Definition: afw_common.h:127
#define AFW_THROW_ERROR_RV_Z(code, rv_source_id, rv, message_z, xctx)
Macro used to set error and rv in xctx and throw it.
Definition: afw_error.h:301
#define AFW_THROW_ERROR_RV_FZ(code, rv_source_id, rv, xctx, format_z,...)
Macro used to set error and rv in xctx and throw it.
Definition: afw_error.h:338
#define AFW_THROW_ERROR_Z(code, message_z, xctx)
Macro used to set error and 0 rv in xctx and throw it.
Definition: afw_error.h:283
#define afw_list_create_generic(p, xctx)
Create an value list in memory.
Definition: afw_list.h:81
afw_list_add_value(const afw_list_t *instance, const afw_value_t *value, afw_xctx_t *xctx)
Call method add_value of interface afw_list_setter.
Definition: afw_list.c:104
afw_object_meta_set_meta_object(const afw_object_t *instance, const afw_object_t *meta, afw_xctx_t *xctx)
Set an object's meta from a meta object.
afw_object_path_make(const afw_utf8_t *adaptor_id, const afw_utf8_t *object_type_id, const afw_utf8_t *object_id, const afw_pool_t *p, afw_xctx_t *xctx)
Construct an object path in a specified pool.
#define AFW_OBJECT_CREATE_ENTITY_OR_EMBEDDED(result, embedding_object, property_name, always_create_unmanaged, cede_p, entity_p, xctx)
Helper macro to create a new entity or embedded object.
Definition: afw_object.h:1015
afw_object_set_property(const afw_object_t *instance, const afw_utf8_t *property_name, const afw_value_t *value, afw_xctx_t *xctx)
Set the value of an object's property.
Definition: afw_object.c:46
#define afw_pool_get_apr_pool(instance)
Call method get_apr_pool of interface afw_pool.
#define afw_pool_register_cleanup_before(instance, data, data2, cleanup, xctx)
Call method register_cleanup_before of interface afw_pool.
#define afw_pool_calloc_type(instance, type, xctx)
Macro to allocate cleared memory to hold type in pool.
Definition: afw_pool.h:167
const afw_pool_t * afw_pool_create(const afw_pool_t *parent, afw_xctx_t *xctx)
Create a new pool.
#define afw_utf8_create_copy(s, len, p, xctx)
Make a utf-8 sting from chars in pool specified.
Definition: afw_utf8.h:369
afw_boolean_t afw_utf8_equal(const afw_utf8_t *s1, const afw_utf8_t *s2)
Check to see if a string equals another string.
afw_value_as_utf8(const afw_value_t *value, const afw_pool_t *p, afw_xctx_t *xctx)
Definition: afw_value.c:456
afw_value_false
Adaptive value false.
Definition: afw_value.h:354
afw_value_null
Adaptive value null.
Definition: afw_value.h:320
afw_value_true
Adaptive value true.
Definition: afw_value.h:348
const afw_value_t * afw_yaml_to_value(const afw_memory_t *yaml, const afw_utf8_t *path, const afw_pool_t *p, afw_xctx_t *xctx)
Convert YAML to an adaptive value.
const afw_object_t * afw_yaml_to_object(const afw_memory_t *yaml, const afw_utf8_t *source_location, const afw_utf8_t *adaptor_id, const afw_utf8_t *object_type_id, const afw_utf8_t *object_id, afw_boolean_t cede_p, const afw_pool_t *p, afw_xctx_t *xctx)
Convert from YAML to adaptive object.
Interface afw_list public struct.
Struct for memory pointer and size.
Definition: afw_common.h:505
Interface afw_object public struct.
Interface afw_pool public struct.
NFC normalized UTF-8 string.
Definition: afw_common.h:545
struct for data type object values.
Interface afw_value public struct.
Interface afw_xctx public struct.