From e27a3c886ebe97dda5b6f961b04949bb3003d4a6 Mon Sep 17 00:00:00 2001 From: Kirill Simonov Date: Sun, 7 Jan 2007 20:11:16 +0000 Subject: [PATCH] Add functions for constructing, parsing and emitting YAML documents. --- configure.ac | 2 +- include/yaml.h | 557 +++++++++++++-------- src/Makefile.am | 2 +- src/api.c | 309 +++++++++--- src/dumper.c | 394 +++++++++++++++ src/emitter.c | 26 +- src/loader.c | 429 ++++++++++++++++ src/yaml_private.h | 30 +- tests/Makefile.am | 4 +- tests/example-deconstructor-alt.c | 800 ++++++++++++++++++++++++++++++ tests/example-deconstructor.c | 116 ++--- tests/example-reformatter-alt.c | 217 ++++++++ tests/run-dumper.c | 305 ++++++++++++ tests/run-emitter.c | 2 +- tests/run-loader.c | 59 +++ 15 files changed, 2892 insertions(+), 360 deletions(-) create mode 100644 src/dumper.c create mode 100644 src/loader.c create mode 100644 tests/example-deconstructor-alt.c create mode 100644 tests/example-reformatter-alt.c create mode 100644 tests/run-dumper.c create mode 100644 tests/run-loader.c diff --git a/configure.ac b/configure.ac index 95ec383..2aba399 100644 --- a/configure.ac +++ b/configure.ac @@ -18,7 +18,7 @@ m4_define([YAML_BUGS], [http://pyyaml.org/newticket?component=libyaml]) # else: # YAML_AGE = 0 m4_define([YAML_RELEASE], 0) -m4_define([YAML_CURRENT], 0) +m4_define([YAML_CURRENT], 1) m4_define([YAML_REVISION], 0) m4_define([YAML_AGE], 0) diff --git a/include/yaml.h b/include/yaml.h index afb62f0..515bf7c 100644 --- a/include/yaml.h +++ b/include/yaml.h @@ -59,9 +59,9 @@ yaml_get_version_string(void); /** * Get the library version numbers. * - * @param[out] major Major version number. - * @param[out] minor Minor version number. - * @param[out] patch Patch version number. + * @param[out] major Major version number. + * @param[out] minor Minor version number. + * @param[out] patch Patch version number. */ YAML_DECLARE(void) @@ -78,7 +78,7 @@ yaml_get_version(int *major, int *minor, int *patch); typedef unsigned char yaml_char_t; /** The version directive data. */ -typedef struct { +typedef struct yaml_version_directive_s { /** The major version number. */ int major; /** The minor version number. */ @@ -86,7 +86,7 @@ typedef struct { } yaml_version_directive_t; /** The tag directive data. */ -typedef struct { +typedef struct yaml_tag_directive_s { /** The tag handle. */ yaml_char_t *handle; /** The tag prefix. */ @@ -94,7 +94,7 @@ typedef struct { } yaml_tag_directive_t; /** The stream encoding. */ -typedef enum { +typedef enum yaml_encoding_e { YAML_ANY_ENCODING, YAML_UTF8_ENCODING, YAML_UTF16LE_ENCODING, @@ -103,7 +103,7 @@ typedef enum { /** Line break types. */ -typedef enum { +typedef enum yaml_break_e { YAML_ANY_BREAK, YAML_CR_BREAK, YAML_LN_BREAK, @@ -111,7 +111,7 @@ typedef enum { } yaml_break_t; /** Many bad things could happen with the parser and emitter. */ -typedef enum { +typedef enum yaml_error_type_e { YAML_NO_ERROR, YAML_MEMORY_ERROR, @@ -119,13 +119,14 @@ typedef enum { YAML_READER_ERROR, YAML_SCANNER_ERROR, YAML_PARSER_ERROR, + YAML_COMPOSER_ERROR, YAML_WRITER_ERROR, YAML_EMITTER_ERROR } yaml_error_type_t; /** The pointer position. */ -typedef struct { +typedef struct yaml_mark_s { /** The position index. */ size_t index; @@ -144,7 +145,7 @@ typedef struct { */ /** Scalar styles. */ -typedef enum { +typedef enum yaml_scalar_style_e { YAML_ANY_SCALAR_STYLE, YAML_PLAIN_SCALAR_STYLE, @@ -157,7 +158,7 @@ typedef enum { } yaml_scalar_style_t; /** Sequence styles. */ -typedef enum { +typedef enum yaml_sequence_style_e { YAML_ANY_SEQUENCE_STYLE, YAML_BLOCK_SEQUENCE_STYLE, @@ -165,7 +166,7 @@ typedef enum { } yaml_sequence_style_t; /** Mapping styles. */ -typedef enum { +typedef enum yaml_mapping_style_e { YAML_ANY_MAPPING_STYLE, YAML_BLOCK_MAPPING_STYLE, @@ -181,7 +182,7 @@ typedef enum { */ /** Token types. */ -typedef enum { +typedef enum yaml_token_type_e { YAML_NO_TOKEN, YAML_STREAM_START_TOKEN, @@ -213,7 +214,7 @@ typedef enum { } yaml_token_type_t; /** The token structure. */ -typedef struct { +typedef struct yaml_token_s { /** The token type. */ yaml_token_type_t type; @@ -299,7 +300,7 @@ yaml_token_delete(yaml_token_t *token); */ /** Event types. */ -typedef enum { +typedef enum yaml_event_type_e { YAML_NO_EVENT, YAML_STREAM_START_EVENT, @@ -319,7 +320,7 @@ typedef enum { } yaml_event_type_t; /** The event structure. */ -typedef struct { +typedef struct yaml_event_s { /** The event type. */ yaml_event_type_t type; @@ -416,8 +417,8 @@ typedef struct { /** * Create the STREAM-START event. * - * @param[out] event An empty event object. - * @param[in] encoding The stream encoding. + * @param[out] event An empty event object. + * @param[in] encoding The stream encoding. * * @returns @c 1 if the function succeeded, @c 0 on error. */ @@ -429,7 +430,7 @@ yaml_stream_start_event_initialize(yaml_event_t *event, /** * Create the STREAM-END event. * - * @param[out] event An empty event object. + * @param[out] event An empty event object. * * @returns @c 1 if the function succeeded, @c 0 on error. */ @@ -443,11 +444,15 @@ yaml_stream_end_event_initialize(yaml_event_t *event); * The @a implicit argument is considered as a stylistic parameter and may be * ignored by the emitter. * - * @param[out] event An empty event object. - * @param[in] version_directive The %YAML directive value or @c NULL. - * @param[in] tag_directives_start The beginning of the %TAG directives list. - * @param[in] tag_directives_end The end of the %TAG directives list. - * @param[in] implicit If the document start indicator is implicit. + * @param[out] event An empty event object. + * @param[in] version_directive The %YAML directive value or + * @c NULL. + * @param[in] tag_directives_start The beginning of the %TAG + * directives list. + * @param[in] tag_directives_end The end of the %TAG directives + * list. + * @param[in] implicit If the document start indicator is + * implicit. * * @returns @c 1 if the function succeeded, @c 0 on error. */ @@ -465,8 +470,8 @@ yaml_document_start_event_initialize(yaml_event_t *event, * The @a implicit argument is considered as a stylistic parameter and may be * ignored by the emitter. * - * @param[out] event An empty event object. - * @param[in] implicit If the document end indicator is implicit. + * @param[out] event An empty event object. + * @param[in] implicit If the document end indicator is implicit. * * @returns @c 1 if the function succeeded, @c 0 on error. */ @@ -477,8 +482,8 @@ yaml_document_end_event_initialize(yaml_event_t *event, int implicit); /** * Create an ALIAS event. * - * @param[out] event An empty event object. - * @param[in] anchor The anchor value. + * @param[out] event An empty event object. + * @param[in] anchor The anchor value. * * @returns @c 1 if the function succeeded, @c 0 on error. */ @@ -494,14 +499,16 @@ yaml_alias_event_initialize(yaml_event_t *event, yaml_char_t *anchor); * Either the @a tag attribute or one of the @a plain_implicit and * @a quoted_implicit flags must be set. * - * @param[out] event An empty event object. - * @param[in] anchor The scalar anchor or @c NULL. - * @param[in] tag The scalar tag or @c NULL. - * @param[in] value The scalar value. - * @param[in] length The length of the scalar value. - * @param[in] plain_implicit If the tag may be omitted for the plain style. - * @param[in] quoted_implicit If the tag may be omitted for any non-plain style. - * @param[in] style The scalar style. + * @param[out] event An empty event object. + * @param[in] anchor The scalar anchor or @c NULL. + * @param[in] tag The scalar tag or @c NULL. + * @param[in] value The scalar value. + * @param[in] length The length of the scalar value. + * @param[in] plain_implicit If the tag may be omitted for the plain + * style. + * @param[in] quoted_implicit If the tag may be omitted for any + * non-plain style. + * @param[in] style The scalar style. * * @returns @c 1 if the function succeeded, @c 0 on error. */ @@ -520,11 +527,11 @@ yaml_scalar_event_initialize(yaml_event_t *event, * * Either the @a tag attribute or the @a implicit flag must be set. * - * @param[out] event An empty event object. - * @param[in] anchor The sequence anchor or @c NULL. - * @param[in] tag The sequence tag or @c NULL. - * @param[in] implicit If the tag may be omitted. - * @param[in] style The sequence style. + * @param[out] event An empty event object. + * @param[in] anchor The sequence anchor or @c NULL. + * @param[in] tag The sequence tag or @c NULL. + * @param[in] implicit If the tag may be omitted. + * @param[in] style The sequence style. * * @returns @c 1 if the function succeeded, @c 0 on error. */ @@ -537,7 +544,7 @@ yaml_sequence_start_event_initialize(yaml_event_t *event, /** * Create a SEQUENCE-END event. * - * @param[out] event An empty event object. + * @param[out] event An empty event object. * * @returns @c 1 if the function succeeded, @c 0 on error. */ @@ -552,11 +559,11 @@ yaml_sequence_end_event_initialize(yaml_event_t *event); * * Either the @a tag attribute or the @a implicit flag must be set. * - * @param[out] event An empty event object. - * @param[in] anchor The mapping anchor or @c NULL. - * @param[in] tag The mapping tag or @c NULL. - * @param[in] implicit If the tag may be omitted. - * @param[in] style The mapping style. + * @param[out] event An empty event object. + * @param[in] anchor The mapping anchor or @c NULL. + * @param[in] tag The mapping tag or @c NULL. + * @param[in] implicit If the tag may be omitted. + * @param[in] style The mapping style. * * @returns @c 1 if the function succeeded, @c 0 on error. */ @@ -569,7 +576,7 @@ yaml_mapping_start_event_initialize(yaml_event_t *event, /** * Create a MAPPING-END event. * - * @param[out] event An empty event object. + * @param[out] event An empty event object. * * @returns @c 1 if the function succeeded, @c 0 on error. */ @@ -580,7 +587,7 @@ yaml_mapping_end_event_initialize(yaml_event_t *event); /** * Free any memory allocated for an event object. * - * @param[out] event An event object. + * @param[in,out] event An event object. */ YAML_DECLARE(void) @@ -603,10 +610,10 @@ yaml_event_delete(yaml_event_t *event); #define YAML_DEFAULT_SCALAR_TAG YAML_STR_TAG #define YAML_DEFAULT_SEQUENCE_TAG YAML_SEQ_TAG -#define YAML_DEFAULT_MAPPING_STYLE YAML_MAP_TAG +#define YAML_DEFAULT_MAPPING_TAG YAML_MAP_TAG /** Node types. */ -typedef enum { +typedef enum yaml_node_type_e { YAML_NO_NODE, YAML_SCALAR_NODE, @@ -614,31 +621,34 @@ typedef enum { YAML_MAPPING_NODE } yaml_node_type_t; -#if 0 +/** The forward definition of a document node structure. */ +typedef struct yaml_node_s yaml_node_t; -typedef struct _yaml_node_t yaml_node_item_t; +/** An element of a sequence node. */ +typedef int yaml_node_item_t; -typedef struct { - yaml_node_item_t key; - yaml_node_item_t value; +/** An element of a mapping node. */ +typedef struct yaml_node_pair_s { + /** The key of the element. */ + int key; + /** The value of the element. */ + int value; } yaml_node_pair_t; /** The node structure. */ -typedef struct _yaml_node_t { +struct yaml_node_s { /** The node type. */ yaml_node_type_t type; - /* The reference counter. */ - int references; + /** The node tag. */ + yaml_char_t *tag; /** The node data. */ union { /** The scalar parameters (for @c YAML_SCALAR_NODE). */ struct { - /** The tag. */ - yaml_char_t *tag; /** The scalar value. */ yaml_char_t *value; /** The length of the scalar value. */ @@ -649,16 +659,14 @@ typedef struct _yaml_node_t { /** The sequence parameters (for @c YAML_SEQUENCE_NODE). */ struct { - /** The tag. */ - yaml_char_t *tag; /** The stack of sequence items. */ struct { /** The beginning of the stack. */ - struct yaml_node_item_t *start; + yaml_node_item_t *start; /** The end of the stack. */ - struct yaml_node_item_t *end; + yaml_node_item_t *end; /** The top of the stack. */ - struct yaml_node_item_t *top; + yaml_node_item_t *top; } items; /** The sequence style. */ yaml_sequence_style_t style; @@ -666,16 +674,14 @@ typedef struct _yaml_node_t { /** The mapping parameters (for @c YAML_MAPPING_NODE). */ struct { - /** The tag. */ - yaml_char_t *tag; - /** The stack of mapping pairs. */ + /** The stack of mapping pairs (key, value). */ struct { /** The beginning of the stack. */ - struct yaml_node_pair_t *start; + yaml_node_pair_t *start; /** The end of the stack. */ - struct yaml_node_pair_t *end; + yaml_node_pair_t *end; /** The top of the stack. */ - struct yaml_node_pair_t *top; + yaml_node_pair_t *top; } pairs; /** The mapping style. */ yaml_mapping_style_t style; @@ -688,201 +694,191 @@ typedef struct _yaml_node_t { /** The end of the node. */ yaml_mark_t end_mark; -} yaml_node_t; +}; -/** - * Create a SCALAR node. - * - * The @a style argument may be ignored by the emitter. - * - * @param[out] node An empty node object. - * @param[in] tag The scalar tag. - * @param[in] value The scalar value. - * @param[in] length The length of the scalar value. - * @param[in] style The scalar style. - * - * @returns @c 1 if the function succeeded, @c 0 on error. - */ +/** The document structure. */ +typedef struct yaml_document_s { -YAML_DECLARE(int) -yaml_scalar_node_initialize(yaml_node_t *node, - yaml_char_t *tag, yaml_char_t *value, int length, - yaml_scalar_style_t style); + /** The document nodes. */ + struct { + /** The beginning of the stack. */ + yaml_node_t *start; + /** The end of the stack. */ + yaml_node_t *end; + /** The top of the stack. */ + yaml_node_t *top; + } nodes; + + /** The version directive. */ + yaml_version_directive_t *version_directive; + + /** The list of tag directives. */ + struct { + /** The beginning of the tag directives list. */ + yaml_tag_directive_t *start; + /** The end of the tag directives list. */ + yaml_tag_directive_t *end; + } tag_directives; + + /** Is the document start indicator implicit? */ + int start_implicit; + /** Is the document end indicator implicit? */ + int end_implicit; + + /** The beginning of the document. */ + yaml_mark_t start_mark; + /** The end of the document. */ + yaml_mark_t end_mark; + +} yaml_document_t; /** - * Create a SEQUENCE node. - * - * The @a style argument may be ignored by the emitter. + * Create a YAML document. * - * @param[out] node An empty node object. - * @param[in] tag The sequence tag. - * @param[in] style The sequence style. + * @param[out] document An empty document object. + * @param[in] version_directive The %YAML directive value or + * @c NULL. + * @param[in] tag_directives_start The beginning of the %TAG + * directives list. + * @param[in] tag_directives_end The end of the %TAG directives + * list. + * @param[in] start_implicit If the document start indicator is + * implicit. + * @param[in] end_implicit If the document end indicator is + * implicit. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) -yaml_sequence_node_initialize(yaml_node_t *node, - yaml_char_t *tag, yaml_sequence_style_t style); +yaml_document_initialize(yaml_document_t *document, + yaml_version_directive_t *version_directive, + yaml_tag_directive_t *tag_directives_start, + yaml_tag_directive_t *tag_directives_end, + int start_implicit, int end_implicit); /** - * Add an item to a SEQUENCE node + * Delete a YAML document and all its nodes. * - * @param[out] node A sequence node. - * @param[in] item An item node. -* - * @returns @c 1 if the function succeeded, @c 0 on error. + * @param[in,out] document A document object. */ -YAML_DECLARE(int) -yaml_sequence_node_add_item(yaml_node_t *node, yaml_node_t *item) +YAML_DECLARE(void) +yaml_document_delete(yaml_document_t *document); /** - * Create a SCALAR node and add it to a SEQUENCE node. + * Get a node of a YAML document. * - * @param[out] node A sequence node. - * @param[in] tag The scalar tag. - * @param[in] value The scalar value. - * @param[in] length The length of the scalar value. - * @param[in] style The scalar style. + * The pointer returned by this function is valid until any of the functions + * modifying the documents are called. * - * @returns @c 1 if the function succeeded, @c 0 on error. + * @param[in] document A document object. + * @param[in] node The node id. + * + * @returns the node objct or @c NULL if @c node_id is out of range. */ -YAML_DECLARE(int) -yaml_sequence_node_add_scalar_item(yaml_node_t *node, - yaml_char_t *tag, yaml_char_t *value, int length, - yaml_scalar_style_t style); +YAML_DECLARE(yaml_node_t *) +yaml_document_get_node(yaml_document_t *document, int node_id); /** - * Get the number of subnodes of a SEQUENCE node. + * Get the root of a YAML document node. * - * @param[in] node A sequence node. + * The root object is the first object added to the document. * - * @returns the number of subnodes. - */ - -YAML_DECLARE(size_t) -yaml_sequence_node_get_length(yaml_node_t *node); - -/** - * Get a subnode of a SEQUENCE node. + * The pointer returned by this function is valid until any of the functions + * modifying the documents are called. + * + * An empty document produced by the parser signifies the end of a YAML + * stream. + * + * @param[in] document A document object. * - * @param[in] node A sequence node. - * @param[in] index The index of a subnode. - * @param[out] item A subnode. + * @returns the node object or @c NULL if the document is empty. */ -YAML_DECLARE(void) -yaml_sequence_node_get_item(yaml_node_t *node, size_t index, - yaml_node_t *item); +YAML_DECLARE(yaml_node_t *) +yaml_document_get_root_node(yaml_document_t *document); /** - * Create a MAPPING node. + * Create a SCALAR node and attach it to the document. * * The @a style argument may be ignored by the emitter. * - * @param[out] node An empty node object. - * @param[in] tag The mapping tag. - * @param[in] style The mapping style. + * @param[in,out] document A document object. + * @param[in] tag The scalar tag. + * @param[in] value The scalar value. + * @param[in] length The length of the scalar value. + * @param[in] style The scalar style. * - * @returns @c 1 if the function succeeded, @c 0 on error. + * @returns the node id or @c 0 on error. */ YAML_DECLARE(int) -yaml_mapping_node_initialize(yaml_node_t *node, - yaml_char_t *tag, yaml_mapping_style_t style); +yaml_document_add_scalar(yaml_document_t *document, + yaml_char_t *tag, yaml_char_t *value, int length, + yaml_scalar_style_t style); /** - * Add a key/value pair of nodes to a MAPPING node. + * Create a SEQUENCE node and attach it to the document. * - * @param[out] node A mapping node. - * @param[in] key A key node. - * @param[in] value A value node. + * The @a style argument may be ignored by the emitter. * - * @returns @c 1 if the function succeeded, @c 0 on error. + * @param[in,out] document A document object. + * @param[in] tag The sequence tag. + * @param[in] style The sequence style. + * + * @returns the node id or @c 0 on error. */ YAML_DECLARE(int) -yaml_mapping_node_add_pair(yaml_node_t *node, - yaml_node_t *key, yaml_node_t *value) +yaml_document_add_sequence(yaml_document_t *document, + yaml_char_t *tag, yaml_sequence_style_t style); /** - * Create a scalar key and add the key/value pair to a MAPPING node. + * Create a MAPPING node and attach it to the document. * - * @param[out] node A mapping node. - * @param[in] key_tag The key node tag. - * @param[in] key_value The key node value. - * @param[in] key_length The length of the key node value. - * @param[in] key_style The key node style. - * @param[in] value A value node. + * The @a style argument may be ignored by the emitter. * - * @returns @c 1 if the function succeeded, @c 0 on error. + * @param[in,out] document A document object. + * @param[in] tag The sequence tag. + * @param[in] style The sequence style. + * + * @returns the node id or @c 0 on error. */ YAML_DECLARE(int) -yaml_sequence_node_add_scalar_key_pair(yaml_node_t *node, - yaml_char_t *key_tag, yaml_char_t *key_value, int key_length, - yaml_scalar_style_t key_style, - yaml_node_t *value); +yaml_document_add_mapping(yaml_document_t *document, + yaml_char_t *tag, yaml_mapping_style_t style); /** - * Create a scalar key/value nodes and add the pair to a MAPPING node. - * - * @param[out] node A mapping node. - * @param[in] key_tag The key node tag. - * @param[in] key_value The key node value. - * @param[in] key_length The length of the key node value. - * @param[in] key_style The key node style. - * @param[in] value_tag The value node tag. - * @param[in] value_value The value node value. - * @param[in] value_length The length of the value node value. - * @param[in] value_style The value node style. + * Add an item to a SEQUENCE node. * + * @param[in,out] document A document object. + * @param[in] sequence The sequence node id. + * @param[in] item The item node id. +* * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) -yaml_sequence_node_add_scalar_pair(yaml_node_t *node, - yaml_char_t *key_tag, yaml_char_t *key_value, int key_length, - yaml_scalar_style_t key_style, - yaml_char_t *value_tag, yaml_char_t *value_value, int value_length, - yaml_scalar_style_t value_style); - -/** - * Get the number of subnode pairs of a MAPPING node. - * - * @param[in] node A mapping node. - * - * @returns the number of pairs. - */ - -YAML_DECLARE(size_t) -yaml_mapping_node_get_length(yaml_node_t *node); +yaml_document_append_sequence_item(yaml_document_t *document, + int sequence, int item); /** - * Get a subnode of a SEQUENCE node. + * Add a pair of a key and a value to a MAPPING node. * - * @param[in] node A sequence node. - * @param[in] index The index of a subnode. - * @param[out] key The key subnode. - * @param[out] value The value subnode. - */ - -YAML_DECLARE(void) -yaml_mapping_node_get_pair(yaml_node_t *node, size_t index, - yaml_node_t *key, yaml_node_t *value); - -/** - * Delete a node and its subnodes. - * - * @param[out] node A node object. + * @param[in,out] document A document object. + * @param[in] mapping The mapping node id. + * @param[in] key The key node id. + * @param[in] value The value node id. +* + * @returns @c 1 if the function succeeded, @c 0 on error. */ -YAML_DECLARE(void) -yaml_node_delete(yaml_node_t *node); - -#endif +YAML_DECLARE(int) +yaml_document_append_mapping_pair(yaml_document_t *document, + int mapping, int key, int value); /** @} */ @@ -916,7 +912,7 @@ typedef int yaml_read_handler_t(void *data, unsigned char *buffer, size_t size, * This structure holds information about a potential simple key. */ -typedef struct { +typedef struct yaml_simple_key_s { /** Is a simple key possible? */ int possible; @@ -933,7 +929,7 @@ typedef struct { /** * The states of the parser. */ -typedef enum { +typedef enum yaml_parser_state_e { YAML_PARSE_STREAM_START_STATE, YAML_PARSE_IMPLICIT_DOCUMENT_START_STATE, YAML_PARSE_DOCUMENT_START_STATE, @@ -960,6 +956,19 @@ typedef enum { YAML_PARSE_END_STATE } yaml_parser_state_t; +/** + * This structure holds aliases data. + */ + +typedef struct yaml_alias_data_s { + /** The anchor. */ + yaml_char_t *anchor; + /** The node id. */ + int index; + /** The anchor mark. */ + yaml_mark_t mark; +} yaml_alias_data_t; + /** * The parser structure. * @@ -967,7 +976,7 @@ typedef enum { * family of functions. */ -typedef struct { +typedef struct yaml_parser_s { /** * @name Error handling @@ -1167,6 +1176,28 @@ typedef struct { * @} */ + /** + * @name Dumper stuff + * @{ + */ + + /** The alias data. */ + struct { + /** The beginning of the list. */ + yaml_alias_data_t *start; + /** The end of the list. */ + yaml_alias_data_t *end; + /** The top of the list. */ + yaml_alias_data_t *top; + } aliases; + + /** The currently parsed document. */ + yaml_document_t *document; + + /** + * @} + */ + } yaml_parser_t; /** @@ -1175,7 +1206,7 @@ typedef struct { * This function creates a new parser object. An application is responsible * for destroying the object using the yaml_parser_delete() function. * - * @param[out] parser An empty parser object. + * @param[out] parser An empty parser object. * * @returns @c 1 if the function succeeded, @c 0 on error. */ @@ -1256,7 +1287,8 @@ yaml_parser_set_encoding(yaml_parser_t *parser, yaml_encoding_t encoding); * produced token object using the @c yaml_token_delete function. * * An application must not alternate the calls of yaml_parser_scan() with the - * calls of yaml_parser_parse(). Doing this will break the parser. + * calls of yaml_parser_parse() or yaml_parser_load(). Doing this will break + * the parser. * * @param[in,out] parser A parser object. * @param[out] token An empty token object. @@ -1278,8 +1310,9 @@ yaml_parser_scan(yaml_parser_t *parser, yaml_token_t *token); * An application is responsible for freeing any buffers associated with the * produced event object using the yaml_event_delete() function. * - * An application must not alternate the calls of yaml_parser_scan() with the - * calls of yaml_parser_parse(). Doing this will break the parser. + * An application must not alternate the calls of yaml_parser_parse() with the + * calls of yaml_parser_scan() or yaml_parser_load(). Doing this will break the + * parser. * * @param[in,out] parser A parser object. * @param[out] event An empty event object. @@ -1290,6 +1323,31 @@ yaml_parser_scan(yaml_parser_t *parser, yaml_token_t *token); YAML_DECLARE(int) yaml_parser_parse(yaml_parser_t *parser, yaml_event_t *event); +/** + * Parse the input stream and produce the next YAML document. + * + * Call this function subsequently to produce a sequence of documents + * constituting the input stream. + * + * If the produced document has no root node, it means that the document + * end has been reached. + * + * An application is responsible for freeing any data associated with the + * produced document object using the yaml_document_delete() function. + * + * An application must not alternate the calls of yaml_parser_load() with the + * calls of yaml_parser_scan() or yaml_parser_parse(). Doing this will break + * the parser. + * + * @param[in,out] parser A parser object. + * @param[out] document An empty document object. + * + * @return @c 1 if the function succeeded, @c 0 on error. + */ + +YAML_DECLARE(int) +yaml_parser_load(yaml_parser_t *parser, yaml_document_t *document); + /** @} */ /** @@ -1316,7 +1374,7 @@ yaml_parser_parse(yaml_parser_t *parser, yaml_event_t *event); typedef int yaml_write_handler_t(void *data, unsigned char *buffer, size_t size); /** The emitter states. */ -typedef enum { +typedef enum yaml_emitter_state_e { YAML_EMIT_STREAM_START_STATE, YAML_EMIT_FIRST_DOCUMENT_START_STATE, YAML_EMIT_DOCUMENT_START_STATE, @@ -1344,7 +1402,7 @@ typedef enum { * family of functions. */ -typedef struct { +typedef struct yaml_emitter_s { /** * @name Error handling @@ -1549,6 +1607,36 @@ typedef struct { * @} */ + /** + * @name Dumper stuff + * @{ + */ + + /** If the stream was already opened? */ + int opened; + /** If the stream was already closed? */ + int closed; + + /** The information associated with the document nodes. */ + struct { + /** The number of references. */ + int references; + /** The anchor id. */ + int anchor; + /** If the node has been emitted? */ + int serialized; + } *anchors; + + /** The last assigned anchor id. */ + int last_anchor_id; + + /** The currently emitted document. */ + yaml_document_t *document; + + /** + * @} + */ + } yaml_emitter_t; /** @@ -1697,6 +1785,49 @@ yaml_emitter_set_break(yaml_emitter_t *emitter, yaml_break_t line_break); YAML_DECLARE(int) yaml_emitter_emit(yaml_emitter_t *emitter, yaml_event_t *event); +/* + * Start a YAML stream. + * + * This function should be used before yaml_emitter_dump() is called. + * + * @param[in,out] emitter An emitter object. + * + * @returns @c 1 if the function succeeded, @c 0 on error. + */ + +YAML_DECLARE(int) +yaml_emitter_open(yaml_emitter_t *emitter); + +/* + * Finish a YAML stream. + * + * This function should be used after yaml_emitter_dump() is called. + * + * @param[in,out] emitter An emitter object. + * + * @returns @c 1 if the function succeeded, @c 0 on error. + */ + +YAML_DECLARE(int) +yaml_emitter_close(yaml_emitter_t *emitter); + +/* + * Emit a YAML document. + * + * The documen object may be generated using the yaml_parser_load() function + * or the yaml_document_initialize() function. The emitter takes the + * responsibility for the document object and destoys its content after + * it is emitted. The document object is destroyedeven if the function fails. + * + * @param[in,out] emitter An emitter object. + * @param[in,out] document A document object. + * + * @returns @c 1 if the function succeeded, @c 0 on error. + */ + +YAML_DECLARE(int) +yaml_emitter_dump(yaml_emitter_t *emitter, yaml_document_t *document); + /** * Flush the accumulated characters to the output. * diff --git a/src/Makefile.am b/src/Makefile.am index f9cb7a2..724a1b2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ AM_CPPFLAGS = -I$(top_srcdir)/include lib_LTLIBRARIES = libyaml.la -libyaml_la_SOURCES = yaml_private.h api.c reader.c scanner.c parser.c writer.c emitter.c +libyaml_la_SOURCES = yaml_private.h api.c reader.c scanner.c parser.c loader.c writer.c emitter.c dumper.c libyaml_la_LDFLAGS = -release $(YAML_LT_RELEASE) -version-info $(YAML_LT_CURRENT):$(YAML_LT_REVISION):$(YAML_LT_AGE) diff --git a/src/api.c b/src/api.c index a2d5543..a1fdf6b 100644 --- a/src/api.c +++ b/src/api.c @@ -399,6 +399,7 @@ yaml_emitter_delete(yaml_emitter_t *emitter) yaml_free(tag_directive.prefix); } STACK_DEL(emitter, emitter->tag_directives); + yaml_free(emitter->anchors); memset(emitter, 0, sizeof(yaml_emitter_t)); } @@ -1019,23 +1020,187 @@ yaml_event_delete(yaml_event_t *event) memset(event, 0, sizeof(yaml_event_t)); } -#if 0 +/* + * Create a document object. + */ + +YAML_DECLARE(int) +yaml_document_initialize(yaml_document_t *document, + yaml_version_directive_t *version_directive, + yaml_tag_directive_t *tag_directives_start, + yaml_tag_directive_t *tag_directives_end, + int start_implicit, int end_implicit) +{ + struct { + yaml_error_type_t error; + } context; + struct { + yaml_node_t *start; + yaml_node_t *end; + yaml_node_t *top; + } nodes = { NULL, NULL, NULL }; + yaml_version_directive_t *version_directive_copy = NULL; + struct { + yaml_tag_directive_t *start; + yaml_tag_directive_t *end; + yaml_tag_directive_t *top; + } tag_directives_copy = { NULL, NULL, NULL }; + yaml_tag_directive_t value = { NULL, NULL }; + yaml_mark_t mark = { 0, 0, 0 }; + + assert(document); /* Non-NULL document object is expected. */ + assert((tag_directives_start && tag_directives_end) || + (tag_directives_start == tag_directives_end)); + /* Valid tag directives are expected. */ + + if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error; + + if (version_directive) { + version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t)); + if (!version_directive_copy) goto error; + version_directive_copy->major = version_directive->major; + version_directive_copy->minor = version_directive->minor; + } + + if (tag_directives_start != tag_directives_end) { + yaml_tag_directive_t *tag_directive; + if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE)) + goto error; + for (tag_directive = tag_directives_start; + tag_directive != tag_directives_end; tag_directive ++) { + assert(tag_directive->handle); + assert(tag_directive->prefix); + if (!yaml_check_utf8(tag_directive->handle, + strlen((char *)tag_directive->handle))) + goto error; + if (!yaml_check_utf8(tag_directive->prefix, + strlen((char *)tag_directive->prefix))) + goto error; + value.handle = yaml_strdup(tag_directive->handle); + value.prefix = yaml_strdup(tag_directive->prefix); + if (!value.handle || !value.prefix) goto error; + if (!PUSH(&context, tag_directives_copy, value)) + goto error; + value.handle = NULL; + value.prefix = NULL; + } + } + + DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy, + tag_directives_copy.start, tag_directives_copy.top, + start_implicit, end_implicit, mark, mark); + + return 1; + +error: + STACK_DEL(&context, nodes); + yaml_free(version_directive_copy); + while (!STACK_EMPTY(&context, tag_directives_copy)) { + yaml_tag_directive_t value = POP(&context, tag_directives_copy); + yaml_free(value.handle); + yaml_free(value.prefix); + } + STACK_DEL(&context, tag_directives_copy); + yaml_free(value.handle); + yaml_free(value.prefix); + + return 0; +} + +/* + * Destroy a document object. + */ + +YAML_DECLARE(void) +yaml_document_delete(yaml_document_t *document) +{ + struct { + yaml_error_type_t error; + } context; + yaml_tag_directive_t *tag_directive; + + assert(document); /* Non-NULL document object is expected. */ + + while (!STACK_EMPTY(&context, document->nodes)) { + yaml_node_t node = POP(&context, document->nodes); + yaml_free(node.tag); + switch (node.type) { + case YAML_SCALAR_NODE: + yaml_free(node.data.scalar.value); + break; + case YAML_SEQUENCE_NODE: + STACK_DEL(&context, node.data.sequence.items); + break; + case YAML_MAPPING_NODE: + STACK_DEL(&context, node.data.mapping.pairs); + break; + default: + assert(0); /* Should not happen. */ + } + } + STACK_DEL(&context, document->nodes); + + yaml_free(document->version_directive); + for (tag_directive = document->tag_directives.start; + tag_directive != document->tag_directives.end; + tag_directive++) { + yaml_free(tag_directive->handle); + yaml_free(tag_directive->prefix); + } + yaml_free(document->tag_directives.start); + + memset(document, 0, sizeof(yaml_document_t)); +} + +/** + * Get a document node. + */ + +YAML_DECLARE(yaml_node_t *) +yaml_document_get_node(yaml_document_t *document, int node) +{ + assert(document); /* Non-NULL document object is expected. */ + + if (node > 0 && document->nodes.start + node <= document->nodes.top) { + return document->nodes.start + node - 1; + } + return NULL; +} + +/** + * Get the root object. + */ + +YAML_DECLARE(yaml_node_t *) +yaml_document_get_root_node(yaml_document_t *document) +{ + assert(document); /* Non-NULL document object is expected. */ + + if (document->nodes.top != document->nodes.start) { + return document->nodes.start; + } + return NULL; +} /* - * Create a SCALAR node. + * Add a scalar node to a document. */ YAML_DECLARE(int) -yaml_scalar_node_initialize(yaml_node_t *node, +yaml_document_add_scalar(yaml_document_t *document, yaml_char_t *tag, yaml_char_t *value, int length, yaml_scalar_style_t style) { + struct { + yaml_error_type_t error; + } context; yaml_mark_t mark = { 0, 0, 0 }; yaml_char_t *tag_copy = NULL; yaml_char_t *value_copy = NULL; + yaml_node_t node; - assert(node); /* Non-NULL node object is expected. */ - assert(value); /* Non-NULL anchor is expected. */ + assert(document); /* Non-NULL document object is expected. */ + assert(value); /* Non-NULL value is expected. */ if (!tag) { tag = YAML_DEFAULT_SCALAR_TAG; @@ -1055,9 +1220,10 @@ yaml_scalar_node_initialize(yaml_node_t *node, memcpy(value_copy, value, length); value_copy[length] = '\0'; - SCALAR_NODE_INIT(*node, tag_copy, value_copy, length, style, mark, mark); + SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark); + if (!PUSH(&context, document->nodes, node)) goto error; - return 1; + return document->nodes.top - document->nodes.start; error: yaml_free(tag_copy); @@ -1067,11 +1233,11 @@ error: } /* - * Create a SEQUENCE node. + * Add a sequence node to a document. */ YAML_DECLARE(int) -yaml_sequence_node_initialize(yaml_node_t *node, +yaml_document_add_sequence(yaml_document_t *document, yaml_char_t *tag, yaml_sequence_style_t style) { struct { @@ -1084,39 +1250,39 @@ yaml_sequence_node_initialize(yaml_node_t *node, yaml_node_item_t *end; yaml_node_item_t *top; } items = { NULL, NULL, NULL }; + yaml_node_t node; - assert(node); /* Non-NULL node object is expected. */ + assert(document); /* Non-NULL document object is expected. */ if (!tag) { tag = YAML_DEFAULT_SEQUENCE_TAG; } - if (tag) { - if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error; - tag_copy = yaml_strdup(tag); - if (!tag_copy) goto error; - } + if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error; + tag_copy = yaml_strdup(tag); + if (!tag_copy) goto error; - if (!STACK_INIT(context, items, INITIAL_STACK_SIZE)) goto error; + if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error; - SEQUENCE_NODE_INIT(*node, tag_copy, items.start, item.end, style, - mark, mark); + SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end, + style, mark, mark); + if (!PUSH(&context, document->nodes, node)) goto error; - return 1; + return document->nodes.top - document->nodes.start; error: + STACK_DEL(&context, items); yaml_free(tag_copy); - STACK_DEL(context, items); return 0; } /* - * Create a MAPPING node. + * Add a mapping node to a document. */ YAML_DECLARE(int) -yaml_mapping_node_initialize(yaml_node_t *node, +yaml_document_add_mapping(yaml_document_t *document, yaml_char_t *tag, yaml_mapping_style_t style) { struct { @@ -1129,74 +1295,89 @@ yaml_mapping_node_initialize(yaml_node_t *node, yaml_node_pair_t *end; yaml_node_pair_t *top; } pairs = { NULL, NULL, NULL }; + yaml_node_t node; - assert(node); /* Non-NULL node object is expected. */ + assert(document); /* Non-NULL document object is expected. */ if (!tag) { tag = YAML_DEFAULT_MAPPING_TAG; } - if (tag) { - if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error; - tag_copy = yaml_strdup(tag); - if (!tag_copy) goto error; - } + if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error; + tag_copy = yaml_strdup(tag); + if (!tag_copy) goto error; - if (!STACK_INIT(context, pairs, INITIAL_STACK_SIZE)) goto error; + if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error; - MAPPING_NODE_INIT(*node, tag_copy, pairs.start, pairs.end, style, - mark, mark); + MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end, + style, mark, mark); + if (!PUSH(&context, document->nodes, node)) goto error; - return 1; + return document->nodes.top - document->nodes.start; error: + STACK_DEL(&context, pairs); yaml_free(tag_copy); - STACK_DEL(context, pairs); return 0; } /* - * Delete a node and its subnodes. + * Append an item to a sequence node. */ -YAML_DECLARE(void) -yaml_node_delete(yaml_node_t *node) +YAML_DECLARE(int) +yaml_document_append_sequence_item(yaml_document_t *document, + int sequence, int item) { struct { yaml_error_type_t error; } context; - struct { - yaml_node_item_t *start; - yaml_node_item_t *end; - yaml_node_item_t *head; - yaml_node_item_t *tail; - } queue = { NULL, NULL, NULL, NULL }; - assert(node); /* Non-NULL node object is expected. */ + assert(document); /* Non-NULL document is required. */ + assert(sequence > 0 + && document->nodes.start + sequence <= document->nodes.top); + /* Valid sequence id is required. */ + assert(document->nodes.start[sequence-1].type == YAML_SEQUENCE_NODE); + /* A sequence node is required. */ + assert(item > 0 && document->nodes.start + item <= document->nodes.top); + /* Valid item id is required. */ + + if (!PUSH(&context, + document->nodes.start[sequence-1].data.sequence.items, item)) + return 0; - if (node->type == YAML_SCALAR_NODE) { - yaml_free(node->data.scalar.tag); - yaml_free(node->data.scalar.value); - memset(node, 0, sizeof(yaml_node_t)); - return; - } + return 1; +} - if (!QUEUE_INIT(context, queue, INITIAL_QUEUE_SIZE)) goto error; - if (!ENQUEUE(context, queue, node)) goto error; +/* + * Append a pair of a key and a value to a mapping node. + */ - while (!QUEUE_EMPTY(context, queue)) { - yaml_node_t node = DEQUEUE(context, queue); - if (node.type == YAML_SCALAR_NODE) { - if (!node->reference) - } - if (node->type == YAML_SEQUENCE_NODE) { - while (!STACK_EMPTY(context, node->data.sequence.items)) { - yaml_node_t *item = - } - } - } -} +YAML_DECLARE(int) +yaml_document_append_mapping_pair(yaml_document_t *document, + int mapping, int key, int value) +{ + struct { + yaml_error_type_t error; + } context; + yaml_node_pair_t pair = { key, value }; + + assert(document); /* Non-NULL document is required. */ + assert(mapping > 0 + && document->nodes.start + mapping <= document->nodes.top); + /* Valid mapping id is required. */ + assert(document->nodes.start[mapping-1].type == YAML_MAPPING_NODE); + /* A mapping node is required. */ + assert(key > 0 && document->nodes.start + key <= document->nodes.top); + /* Valid key id is required. */ + assert(value > 0 && document->nodes.start + value <= document->nodes.top); + /* Valid value id is required. */ + + if (!PUSH(&context, + document->nodes.start[mapping-1].data.mapping.pairs, pair)) + return 0; -#endif + return 1; +} diff --git a/src/dumper.c b/src/dumper.c new file mode 100644 index 0000000..203c6a7 --- /dev/null +++ b/src/dumper.c @@ -0,0 +1,394 @@ + +#include "yaml_private.h" + +/* + * API functions. + */ + +YAML_DECLARE(int) +yaml_emitter_open(yaml_emitter_t *emitter); + +YAML_DECLARE(int) +yaml_emitter_close(yaml_emitter_t *emitter); + +YAML_DECLARE(int) +yaml_emitter_dump(yaml_emitter_t *emitter, yaml_document_t *document); + +/* + * Clean up functions. + */ + +static void +yaml_emitter_delete_document_and_anchors(yaml_emitter_t *emitter); + +/* + * Anchor functions. + */ + +static void +yaml_emitter_anchor_node(yaml_emitter_t *emitter, int index); + +static yaml_char_t * +yaml_emitter_generate_anchor(yaml_emitter_t *emitter, int anchor_id); + + +/* + * Serialize functions. + */ + +static int +yaml_emitter_dump_node(yaml_emitter_t *emitter, int index); + +static int +yaml_emitter_dump_alias(yaml_emitter_t *emitter, yaml_char_t *anchor); + +static int +yaml_emitter_dump_scalar(yaml_emitter_t *emitter, yaml_node_t *node, + yaml_char_t *anchor); + +static int +yaml_emitter_dump_sequence(yaml_emitter_t *emitter, yaml_node_t *node, + yaml_char_t *anchor); + +static int +yaml_emitter_dump_mapping(yaml_emitter_t *emitter, yaml_node_t *node, + yaml_char_t *anchor); + +/* + * Issue a STREAM-START event. + */ + +YAML_DECLARE(int) +yaml_emitter_open(yaml_emitter_t *emitter) +{ + yaml_event_t event; + yaml_mark_t mark = { 0, 0, 0 }; + + assert(emitter); /* Non-NULL emitter object is required. */ + assert(!emitter->opened); /* Emitter should not be opened yet. */ + + STREAM_START_EVENT_INIT(event, YAML_ANY_ENCODING, mark, mark); + + if (!yaml_emitter_emit(emitter, &event)) { + return 0; + } + + emitter->opened = 1; + + return 1; +} + +/* + * Issue a STREAM-END event. + */ + +YAML_DECLARE(int) +yaml_emitter_close(yaml_emitter_t *emitter) +{ + yaml_event_t event; + yaml_mark_t mark = { 0, 0, 0 }; + + assert(emitter); /* Non-NULL emitter object is required. */ + assert(emitter->opened); /* Emitter should be opened. */ + + if (emitter->closed) return 1; + + STREAM_END_EVENT_INIT(event, mark, mark); + + if (!yaml_emitter_emit(emitter, &event)) { + return 0; + } + + emitter->closed = 1; + + return 1; +} + +/* + * Dump a YAML document. + */ + +YAML_DECLARE(int) +yaml_emitter_dump(yaml_emitter_t *emitter, yaml_document_t *document) +{ + yaml_event_t event; + yaml_mark_t mark = { 0, 0, 0 }; + + assert(emitter); /* Non-NULL emitter object is required. */ + assert(document); /* Non-NULL emitter object is expected. */ + + emitter->document = document; + + if (!emitter->opened) { + if (!yaml_emitter_open(emitter)) goto error; + } + + if (STACK_EMPTY(emitter, document->nodes)) { + if (!yaml_emitter_close(emitter)) goto error; + yaml_emitter_delete_document_and_anchors(emitter); + return 1; + } + + assert(emitter->opened); /* Emitter should be opened. */ + + emitter->anchors = yaml_malloc(sizeof(*(emitter->anchors)) + * (document->nodes.top - document->nodes.start)); + if (!emitter->anchors) goto error; + memset(emitter->anchors, 0, sizeof(*(emitter->anchors)) + * (document->nodes.top - document->nodes.start)); + + DOCUMENT_START_EVENT_INIT(event, document->version_directive, + document->tag_directives.start, document->tag_directives.end, + document->start_implicit, mark, mark); + if (!yaml_emitter_emit(emitter, &event)) goto error; + + yaml_emitter_anchor_node(emitter, 1); + if (!yaml_emitter_dump_node(emitter, 1)) goto error; + + DOCUMENT_END_EVENT_INIT(event, document->end_implicit, mark, mark); + if (!yaml_emitter_emit(emitter, &event)) goto error; + + yaml_emitter_delete_document_and_anchors(emitter); + + return 1; + +error: + + yaml_emitter_delete_document_and_anchors(emitter); + + return 0; +} + +/* + * Clean up the emitter object after a document is dumped. + */ + +static void +yaml_emitter_delete_document_and_anchors(yaml_emitter_t *emitter) +{ + int index; + + if (!emitter->anchors) { + yaml_document_delete(emitter->document); + emitter->document = NULL; + return; + } + + for (index = 0; emitter->document->nodes.start + index + < emitter->document->nodes.top; index ++) { + yaml_node_t node = emitter->document->nodes.start[index]; + if (!emitter->anchors[index].serialized) { + yaml_free(node.tag); + if (node.type == YAML_SCALAR_NODE) { + yaml_free(node.data.scalar.value); + } + } + if (node.type == YAML_SEQUENCE_NODE) { + STACK_DEL(emitter, node.data.sequence.items); + } + if (node.type == YAML_MAPPING_NODE) { + STACK_DEL(emitter, node.data.mapping.pairs); + } + } + + STACK_DEL(emitter, emitter->document->nodes); + yaml_free(emitter->anchors); + + emitter->anchors = NULL; + emitter->last_anchor_id = 0; + emitter->document = NULL; +} + +/* + * Check the references of a node and assign the anchor id if needed. + */ + +static void +yaml_emitter_anchor_node(yaml_emitter_t *emitter, int index) +{ + yaml_node_t *node = emitter->document->nodes.start + index - 1; + yaml_node_item_t *item; + yaml_node_pair_t *pair; + + emitter->anchors[index-1].references ++; + + if (emitter->anchors[index-1].references == 1) { + switch (node->type) { + case YAML_SEQUENCE_NODE: + for (item = node->data.sequence.items.start; + item < node->data.sequence.items.top; item ++) { + yaml_emitter_anchor_node(emitter, *item); + } + break; + case YAML_MAPPING_NODE: + for (pair = node->data.mapping.pairs.start; + pair < node->data.mapping.pairs.top; pair ++) { + yaml_emitter_anchor_node(emitter, pair->key); + yaml_emitter_anchor_node(emitter, pair->value); + } + break; + default: + break; + } + } + + else if (emitter->anchors[index-1].references == 2) { + emitter->anchors[index-1].anchor = (++ emitter->last_anchor_id); + } +} + +/* + * Generate a textual representation for an anchor. + */ + +#define ANCHOR_TEMPLATE "id%03d" +#define ANCHOR_TEMPLATE_LENGTH 16 + +static yaml_char_t * +yaml_emitter_generate_anchor(yaml_emitter_t *emitter, int anchor_id) +{ + yaml_char_t *anchor = yaml_malloc(ANCHOR_TEMPLATE_LENGTH); + + if (!anchor) return NULL; + + sprintf((char *)anchor, ANCHOR_TEMPLATE, anchor_id); + + return anchor; +} + +/* + * Serialize a node. + */ + +static int +yaml_emitter_dump_node(yaml_emitter_t *emitter, int index) +{ + yaml_node_t *node = emitter->document->nodes.start + index - 1; + int anchor_id = emitter->anchors[index-1].anchor; + yaml_char_t *anchor = NULL; + + if (anchor_id) { + anchor = yaml_emitter_generate_anchor(emitter, anchor_id); + if (!anchor) return 0; + } + + if (emitter->anchors[index-1].serialized) { + return yaml_emitter_dump_alias(emitter, anchor); + } + + emitter->anchors[index-1].serialized = 1; + + switch (node->type) { + case YAML_SCALAR_NODE: + return yaml_emitter_dump_scalar(emitter, node, anchor); + case YAML_SEQUENCE_NODE: + return yaml_emitter_dump_sequence(emitter, node, anchor); + case YAML_MAPPING_NODE: + return yaml_emitter_dump_mapping(emitter, node, anchor); + default: + assert(0); /* Could not happen. */ + break; + } + + return 0; /* Could not happen. */ +} + +/* + * Serialize an alias. + */ + +static int +yaml_emitter_dump_alias(yaml_emitter_t *emitter, yaml_char_t *anchor) +{ + yaml_event_t event; + yaml_mark_t mark = { 0, 0, 0 }; + + ALIAS_EVENT_INIT(event, anchor, mark, mark); + + return yaml_emitter_emit(emitter, &event); +} + +/* + * Serialize a scalar. + */ + +static int +yaml_emitter_dump_scalar(yaml_emitter_t *emitter, yaml_node_t *node, + yaml_char_t *anchor) +{ + yaml_event_t event; + yaml_mark_t mark = { 0, 0, 0 }; + + int plain_implicit = (strcmp((char *)node->tag, + YAML_DEFAULT_SCALAR_TAG) == 0); + int quoted_implicit = (strcmp((char *)node->tag, + YAML_DEFAULT_SCALAR_TAG) == 0); + + SCALAR_EVENT_INIT(event, anchor, node->tag, node->data.scalar.value, + node->data.scalar.length, plain_implicit, quoted_implicit, + node->data.scalar.style, mark, mark); + + return yaml_emitter_emit(emitter, &event); +} + +/* + * Serialize a sequence. + */ + +static int +yaml_emitter_dump_sequence(yaml_emitter_t *emitter, yaml_node_t *node, + yaml_char_t *anchor) +{ + yaml_event_t event; + yaml_mark_t mark = { 0, 0, 0 }; + + int implicit = (strcmp((char *)node->tag, YAML_DEFAULT_SEQUENCE_TAG) == 0); + + yaml_node_item_t *item; + + SEQUENCE_START_EVENT_INIT(event, anchor, node->tag, implicit, + node->data.sequence.style, mark, mark); + if (!yaml_emitter_emit(emitter, &event)) return 0; + + for (item = node->data.sequence.items.start; + item < node->data.sequence.items.top; item ++) { + if (!yaml_emitter_dump_node(emitter, *item)) return 0; + } + + SEQUENCE_END_EVENT_INIT(event, mark, mark); + if (!yaml_emitter_emit(emitter, &event)) return 0; + + return 1; +} + +/* + * Serialize a mapping. + */ + +static int +yaml_emitter_dump_mapping(yaml_emitter_t *emitter, yaml_node_t *node, + yaml_char_t *anchor) +{ + yaml_event_t event; + yaml_mark_t mark = { 0, 0, 0 }; + + int implicit = (strcmp((char *)node->tag, YAML_DEFAULT_MAPPING_TAG) == 0); + + yaml_node_pair_t *pair; + + MAPPING_START_EVENT_INIT(event, anchor, node->tag, implicit, + node->data.mapping.style, mark, mark); + if (!yaml_emitter_emit(emitter, &event)) return 0; + + for (pair = node->data.mapping.pairs.start; + pair < node->data.mapping.pairs.top; pair ++) { + if (!yaml_emitter_dump_node(emitter, pair->key)) return 0; + if (!yaml_emitter_dump_node(emitter, pair->value)) return 0; + } + + MAPPING_END_EVENT_INIT(event, mark, mark); + if (!yaml_emitter_emit(emitter, &event)) return 0; + + return 1; +} + diff --git a/src/emitter.c b/src/emitter.c index 28eadcc..0affaab 100644 --- a/src/emitter.c +++ b/src/emitter.c @@ -1160,6 +1160,13 @@ static int yaml_emitter_select_scalar_style(yaml_emitter_t *emitter, yaml_event_t *event) { yaml_scalar_style_t style = event->data.scalar.style; + int no_tag = (!emitter->tag_data.handle && !emitter->tag_data.suffix); + + if (no_tag && !event->data.scalar.plain_implicit + && !event->data.scalar.quoted_implicit) { + return yaml_emitter_set_emitter_error(emitter, + "neither tag nor implicit flags are specified"); + } if (style == YAML_ANY_SCALAR_STYLE) style = YAML_PLAIN_SCALAR_STYLE; @@ -1178,8 +1185,7 @@ yaml_emitter_select_scalar_style(yaml_emitter_t *emitter, yaml_event_t *event) if (!emitter->scalar_data.length && (emitter->flow_level || emitter->simple_key_context)) style = YAML_SINGLE_QUOTED_SCALAR_STYLE; - if (!event->data.scalar.plain_implicit - && !emitter->tag_data.handle && !emitter->tag_data.suffix) + if (no_tag && !event->data.scalar.plain_implicit) style = YAML_SINGLE_QUOTED_SCALAR_STYLE; } @@ -1196,19 +1202,11 @@ yaml_emitter_select_scalar_style(yaml_emitter_t *emitter, yaml_event_t *event) style = YAML_DOUBLE_QUOTED_SCALAR_STYLE; } - if (!emitter->tag_data.handle && !emitter->tag_data.suffix) + if (no_tag && !event->data.scalar.quoted_implicit + && style != YAML_PLAIN_SCALAR_STYLE) { - if (!event->data.scalar.plain_implicit - && !event->data.scalar.quoted_implicit) { - return yaml_emitter_set_emitter_error(emitter, - "neither tag nor implicit flags are specified"); - } - - if (event->data.scalar.plain_implicit - && style != YAML_PLAIN_SCALAR_STYLE) { - emitter->tag_data.handle = (yaml_char_t *)"!"; - emitter->tag_data.handle_length = 1; - } + emitter->tag_data.handle = (yaml_char_t *)"!"; + emitter->tag_data.handle_length = 1; } emitter->scalar_data.style = style; diff --git a/src/loader.c b/src/loader.c new file mode 100644 index 0000000..7ee0dbd --- /dev/null +++ b/src/loader.c @@ -0,0 +1,429 @@ + +#include "yaml_private.h" + +/* + * API functions. + */ + +YAML_DECLARE(int) +yaml_parser_load(yaml_parser_t *parser, yaml_document_t *document); + +/* + * Error handling. + */ + +static int +yaml_parser_set_parser_error(yaml_parser_t *parser, + const char *problem, yaml_mark_t problem_mark); + +static int +yaml_parser_set_parser_error_context(yaml_parser_t *parser, + const char *context, yaml_mark_t context_mark, + const char *problem, yaml_mark_t problem_mark); + + +/* + * Alias handling. + */ + +static int +yaml_parser_register_anchor(yaml_parser_t *parser, + int index, yaml_char_t *anchor); + +/* + * Clean up functions. + */ + +static void +yaml_parser_delete_aliases(yaml_parser_t *parser); + +/* + * Composer functions. + */ + +static int +yaml_parser_load_document(yaml_parser_t *parser, yaml_event_t *first_event); + +static int +yaml_parser_load_node(yaml_parser_t *parser, yaml_event_t *first_event); + +static int +yaml_parser_load_alias(yaml_parser_t *parser, yaml_event_t *first_event); + +static int +yaml_parser_load_scalar(yaml_parser_t *parser, yaml_event_t *first_event); + +static int +yaml_parser_load_sequence(yaml_parser_t *parser, yaml_event_t *first_event); + +static int +yaml_parser_load_mapping(yaml_parser_t *parser, yaml_event_t *first_event); + +/* + * Load the next document of the stream. + */ + +YAML_DECLARE(int) +yaml_parser_load(yaml_parser_t *parser, yaml_document_t *document) +{ + yaml_event_t event; + + assert(parser); /* Non-NULL parser object is expected. */ + assert(document); /* Non-NULL document object is expected. */ + + memset(document, 0, sizeof(yaml_document_t)); + if (!STACK_INIT(parser, document->nodes, INITIAL_STACK_SIZE)) + goto error; + + if (!parser->stream_start_produced) { + if (!yaml_parser_parse(parser, &event)) goto error; + assert(event.type == YAML_STREAM_START_EVENT); + /* STREAM-START is expected. */ + } + + if (parser->stream_end_produced) { + return 1; + } + + if (!yaml_parser_parse(parser, &event)) goto error; + if (event.type == YAML_STREAM_END_EVENT) { + return 1; + } + + if (!STACK_INIT(parser, parser->aliases, INITIAL_STACK_SIZE)) + goto error; + + parser->document = document; + + if (!yaml_parser_load_document(parser, &event)) goto error; + + yaml_parser_delete_aliases(parser); + parser->document = NULL; + + return 1; + +error: + + yaml_parser_delete_aliases(parser); + yaml_document_delete(document); + parser->document = NULL; + + return 0; +} + +/* + * Set composer error. + */ + +static int +yaml_parser_set_composer_error(yaml_parser_t *parser, + const char *problem, yaml_mark_t problem_mark) +{ + parser->error = YAML_COMPOSER_ERROR; + parser->problem = problem; + parser->problem_mark = problem_mark; + + return 0; +} + +/* + * Set composer error with context. + */ + +static int +yaml_parser_set_composer_error_context(yaml_parser_t *parser, + const char *context, yaml_mark_t context_mark, + const char *problem, yaml_mark_t problem_mark) +{ + parser->error = YAML_COMPOSER_ERROR; + parser->context = context; + parser->context_mark = context_mark; + parser->problem = problem; + parser->problem_mark = problem_mark; + + return 0; +} + +/* + * Delete the stack of aliases. + */ + +static void +yaml_parser_delete_aliases(yaml_parser_t *parser) +{ + while (!STACK_EMPTY(parser, parser->aliases)) { + yaml_free(POP(parser, parser->aliases).anchor); + } + STACK_DEL(parser, parser->aliases); +} + +/* + * Compose a document object. + */ + +static int +yaml_parser_load_document(yaml_parser_t *parser, yaml_event_t *first_event) +{ + yaml_event_t event; + + assert(first_event->type == YAML_DOCUMENT_START_EVENT); + /* DOCUMENT-START is expected. */ + + parser->document->version_directive + = first_event->data.document_start.version_directive; + parser->document->tag_directives.start + = first_event->data.document_start.tag_directives.start; + parser->document->tag_directives.end + = first_event->data.document_start.tag_directives.end; + parser->document->start_implicit + = first_event->data.document_start.implicit; + parser->document->start_mark = first_event->start_mark; + + if (!yaml_parser_parse(parser, &event)) return 0; + + if (!yaml_parser_load_node(parser, &event)) return 0; + + if (!yaml_parser_parse(parser, &event)) return 0; + assert(event.type == YAML_DOCUMENT_END_EVENT); + /* DOCUMENT-END is expected. */ + + parser->document->end_implicit = event.data.document_end.implicit; + parser->document->end_mark = event.end_mark; + + return 1; +} + +/* + * Compose a node. + */ + +static int +yaml_parser_load_node(yaml_parser_t *parser, yaml_event_t *first_event) +{ + switch (first_event->type) { + case YAML_ALIAS_EVENT: + return yaml_parser_load_alias(parser, first_event); + case YAML_SCALAR_EVENT: + return yaml_parser_load_scalar(parser, first_event); + case YAML_SEQUENCE_START_EVENT: + return yaml_parser_load_sequence(parser, first_event); + case YAML_MAPPING_START_EVENT: + return yaml_parser_load_mapping(parser, first_event); + default: + assert(0); /* Could not happen. */ + return 0; + } + + return 0; +} + +/* + * Add an anchor. + */ + +static int +yaml_parser_register_anchor(yaml_parser_t *parser, + int index, yaml_char_t *anchor) +{ + yaml_alias_data_t data = { anchor, index, + parser->document->nodes.start[index-1].start_mark }; + yaml_alias_data_t *alias_data; + + if (!anchor) return 1; + + for (alias_data = parser->aliases.start; + alias_data != parser->aliases.top; alias_data ++) { + if (strcmp((char *)alias_data->anchor, (char *)anchor) == 0) { + yaml_free(anchor); + return yaml_parser_set_composer_error_context(parser, + "found duplicate anchor; first occurence", + alias_data->mark, "second occurence", data.mark); + } + } + + if (!PUSH(parser, parser->aliases, data)) { + yaml_free(anchor); + return 0; + } + + return 1; +} + +/* + * Compose a node corresponding to an alias. + */ + +static int +yaml_parser_load_alias(yaml_parser_t *parser, yaml_event_t *first_event) +{ + yaml_char_t *anchor = first_event->data.alias.anchor; + yaml_alias_data_t *alias_data; + + for (alias_data = parser->aliases.start; + alias_data != parser->aliases.top; alias_data ++) { + if (strcmp((char *)alias_data->anchor, (char *)anchor) == 0) { + yaml_free(anchor); + return alias_data->index; + } + } + + yaml_free(anchor); + return yaml_parser_set_composer_error(parser, "found undefined alias", + first_event->start_mark); +} + +/* + * Compose a scalar node. + */ + +static int +yaml_parser_load_scalar(yaml_parser_t *parser, yaml_event_t *first_event) +{ + yaml_node_t node; + int index; + yaml_char_t *tag = first_event->data.scalar.tag; + + if (!tag || strcmp((char *)tag, "!") == 0) { + yaml_free(tag); + tag = yaml_strdup(YAML_DEFAULT_SCALAR_TAG); + if (!tag) goto error; + } + + SCALAR_NODE_INIT(node, tag, first_event->data.scalar.value, + first_event->data.scalar.length, first_event->data.scalar.style, + first_event->start_mark, first_event->end_mark); + + if (!PUSH(parser, parser->document->nodes, node)) goto error; + + index = parser->document->nodes.top - parser->document->nodes.start; + + if (!yaml_parser_register_anchor(parser, index, + first_event->data.scalar.anchor)) return 0; + + return index; + +error: + yaml_free(tag); + yaml_free(first_event->data.scalar.anchor); + yaml_free(first_event->data.scalar.value); + return 0; +} + +/* + * Compose a sequence node. + */ + +static int +yaml_parser_load_sequence(yaml_parser_t *parser, yaml_event_t *first_event) +{ + yaml_event_t event; + yaml_node_t node; + struct { + yaml_node_item_t *start; + yaml_node_item_t *end; + yaml_node_item_t *top; + } items = { NULL, NULL, NULL }; + int index, item_index; + yaml_char_t *tag = first_event->data.sequence_start.tag; + + if (!tag || strcmp((char *)tag, "!") == 0) { + yaml_free(tag); + tag = yaml_strdup(YAML_DEFAULT_SEQUENCE_TAG); + if (!tag) goto error; + } + + if (!STACK_INIT(parser, items, INITIAL_STACK_SIZE)) goto error; + + SEQUENCE_NODE_INIT(node, tag, items.start, items.end, + first_event->data.sequence_start.style, + first_event->start_mark, first_event->end_mark); + + if (!PUSH(parser, parser->document->nodes, node)) goto error; + + index = parser->document->nodes.top - parser->document->nodes.start; + + if (!yaml_parser_register_anchor(parser, index, + first_event->data.sequence_start.anchor)) return 0; + + if (!yaml_parser_parse(parser, &event)) return 0; + + while (event.type != YAML_SEQUENCE_END_EVENT) { + item_index = yaml_parser_load_node(parser, &event); + if (!item_index) return 0; + if (!PUSH(parser, + parser->document->nodes.start[index-1].data.sequence.items, + item_index)) return 0; + if (!yaml_parser_parse(parser, &event)) return 0; + } + + parser->document->nodes.start[index-1].end_mark = event.end_mark; + + return index; + +error: + yaml_free(tag); + yaml_free(first_event->data.sequence_start.anchor); + return 0; +} + +/* + * Compose a mapping node. + */ + +static int +yaml_parser_load_mapping(yaml_parser_t *parser, yaml_event_t *first_event) +{ + yaml_event_t event; + yaml_node_t node; + struct { + yaml_node_pair_t *start; + yaml_node_pair_t *end; + yaml_node_pair_t *top; + } pairs = { NULL, NULL, NULL }; + int index; + yaml_node_pair_t pair; + yaml_char_t *tag = first_event->data.mapping_start.tag; + + if (!tag || strcmp((char *)tag, "!") == 0) { + yaml_free(tag); + tag = yaml_strdup(YAML_DEFAULT_MAPPING_TAG); + if (!tag) goto error; + } + + if (!STACK_INIT(parser, pairs, INITIAL_STACK_SIZE)) goto error; + + MAPPING_NODE_INIT(node, tag, pairs.start, pairs.end, + first_event->data.mapping_start.style, + first_event->start_mark, first_event->end_mark); + + if (!PUSH(parser, parser->document->nodes, node)) goto error; + + index = parser->document->nodes.top - parser->document->nodes.start; + + if (!yaml_parser_register_anchor(parser, index, + first_event->data.mapping_start.anchor)) return 0; + + if (!yaml_parser_parse(parser, &event)) return 0; + + while (event.type != YAML_MAPPING_END_EVENT) { + pair.key = yaml_parser_load_node(parser, &event); + if (!pair.key) return 0; + if (!yaml_parser_parse(parser, &event)) return 0; + pair.value = yaml_parser_load_node(parser, &event); + if (!pair.value) return 0; + if (!PUSH(parser, + parser->document->nodes.start[index-1].data.mapping.pairs, + pair)) return 0; + if (!yaml_parser_parse(parser, &event)) return 0; + } + + parser->document->nodes.start[index-1].end_mark = event.end_mark; + + return index; + +error: + yaml_free(tag); + yaml_free(first_event->data.mapping_start.anchor); + return 0; +} + diff --git a/src/yaml_private.h b/src/yaml_private.h index 3378735..10c4219 100644 --- a/src/yaml_private.h +++ b/src/yaml_private.h @@ -579,28 +579,45 @@ yaml_queue_extend(void **start, void **head, void **tail, void **end); #define MAPPING_END_EVENT_INIT(event,start_mark,end_mark) \ (EVENT_INIT((event),YAML_MAPPING_END_EVENT,(start_mark),(end_mark))) +/* + * Document initializer. + */ + +#define DOCUMENT_INIT(document,document_nodes_start,document_nodes_end, \ + document_version_directive,document_tag_directives_start, \ + document_tag_directives_end,document_start_implicit, \ + document_end_implicit,start_mark,end_mark) \ + (memset(&(document), 0, sizeof(yaml_document_t)), \ + (document).nodes.start = (document_nodes_start), \ + (document).nodes.end = (document_nodes_end), \ + (document).nodes.top = (document_nodes_start), \ + (document).version_directive = (document_version_directive), \ + (document).tag_directives.start = (document_tag_directives_start), \ + (document).tag_directives.end = (document_tag_directives_end), \ + (document).start_implicit = (document_start_implicit), \ + (document).end_implicit = (document_end_implicit)) + /* * Node initializers. */ -#define NODE_INIT(node,node_type,node_start_mark,node_end_mark) \ +#define NODE_INIT(node,node_type,node_tag,node_start_mark,node_end_mark) \ (memset(&(node), 0, sizeof(yaml_node_t)), \ (node).type = (node_type), \ + (node).tag = (node_tag), \ (node).start_mark = (node_start_mark), \ (node).end_mark = (node_end_mark)) #define SCALAR_NODE_INIT(node,node_tag,node_value,node_length, \ node_style,start_mark,end_mark) \ - (EVENT_INIT((node),YAML_SCALAR_NODE,(start_mark),(end_mark)), \ - (node).data.scalar.tag = (node_tag), \ + (NODE_INIT((node),YAML_SCALAR_NODE,(node_tag),(start_mark),(end_mark)), \ (node).data.scalar.value = (node_value), \ (node).data.scalar.length = (node_length), \ (node).data.scalar.style = (node_style)) #define SEQUENCE_NODE_INIT(node,node_tag,node_items_start,node_items_end, \ node_style,start_mark,end_mark) \ - (NODE_INIT((node),YAML_SEQUENCE_NODE,(start_mark),(end_mark)), \ - (node).data.sequence.tag = (node_tag), \ + (NODE_INIT((node),YAML_SEQUENCE_NODE,(node_tag),(start_mark),(end_mark)), \ (node).data.sequence.items.start = (node_items_start), \ (node).data.sequence.items.end = (node_items_end), \ (node).data.sequence.items.top = (node_items_start), \ @@ -608,8 +625,7 @@ yaml_queue_extend(void **start, void **head, void **tail, void **end); #define MAPPING_NODE_INIT(node,node_tag,node_pairs_start,node_pairs_end, \ node_style,start_mark,end_mark) \ - (NODE_INIT((node),YAML_MAPPING_NODE,(start_mark),(end_mark)), \ - (node).data.mapping.tag = (node_tag), \ + (NODE_INIT((node),YAML_MAPPING_NODE,(node_tag),(start_mark),(end_mark)), \ (node).data.mapping.pairs.start = (node_pairs_start), \ (node).data.mapping.pairs.end = (node_pairs_end), \ (node).data.mapping.pairs.top = (node_pairs_start), \ diff --git a/tests/Makefile.am b/tests/Makefile.am index bfc01d7..72e84d2 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -3,4 +3,6 @@ AM_CPPFLAGS = -I$(top_srcdir)/include LDADD = $(top_builddir)/src/libyaml.la TESTS = test-version test-reader check_PROGRAMS = test-version test-reader -noinst_PROGRAMS = run-scanner run-parser run-emitter example-reformatter example-deconstructor +noinst_PROGRAMS = run-scanner run-parser run-loader run-emitter run-dumper \ + example-reformatter example-reformatter-alt \ + example-deconstructor example-deconstructor-alt diff --git a/tests/example-deconstructor-alt.c b/tests/example-deconstructor-alt.c new file mode 100644 index 0000000..7da194a --- /dev/null +++ b/tests/example-deconstructor-alt.c @@ -0,0 +1,800 @@ + +#include + +#include +#include + +int +main(int argc, char *argv[]) +{ + int help = 0; + int canonical = 0; + int unicode = 0; + int k; + int done = 0; + + yaml_parser_t parser; + yaml_emitter_t emitter; + yaml_event_t input_event; + yaml_document_t output_document; + + int root; + + /* Clear the objects. */ + + memset(&parser, 0, sizeof(parser)); + memset(&emitter, 0, sizeof(emitter)); + memset(&input_event, 0, sizeof(input_event)); + memset(&output_document, 0, sizeof(output_document)); + + /* Analyze command line options. */ + + for (k = 1; k < argc; k ++) + { + if (strcmp(argv[k], "-h") == 0 + || strcmp(argv[k], "--help") == 0) { + help = 1; + } + + else if (strcmp(argv[k], "-c") == 0 + || strcmp(argv[k], "--canonical") == 0) { + canonical = 1; + } + + else if (strcmp(argv[k], "-u") == 0 + || strcmp(argv[k], "--unicode") == 0) { + unicode = 1; + } + + else { + fprintf(stderr, "Unrecognized option: %s\n" + "Try `%s --help` for more information.\n", + argv[k], argv[0]); + return 1; + } + } + + /* Display the help string. */ + + if (help) + { + printf("%s . */ + + if (input_event.data.stream_start.encoding) + { + yaml_encoding_t encoding + = input_event.data.stream_start.encoding; + + key = yaml_document_add_scalar(&output_document, NULL, + "encoding", -1, YAML_PLAIN_SCALAR_STYLE); + if (!key) goto document_error; + value = yaml_document_add_scalar(&output_document, NULL, + (encoding == YAML_UTF8_ENCODING ? "utf-8" : + encoding == YAML_UTF16LE_ENCODING ? "utf-16-le" : + encoding == YAML_UTF16BE_ENCODING ? "utf-16-be" : + "unknown"), -1, YAML_PLAIN_SCALAR_STYLE); + if (!value) goto document_error; + if (!yaml_document_append_mapping_pair(&output_document, + properties, key, value)) goto document_error; + } + + break; + + case YAML_STREAM_END_EVENT: + + /* Add 'type': 'STREAM-END'. */ + + key = yaml_document_add_scalar(&output_document, NULL, + "type", -1, YAML_PLAIN_SCALAR_STYLE); + if (!key) goto document_error; + value = yaml_document_add_scalar(&output_document, NULL, + "STREAM-END", -1, YAML_PLAIN_SCALAR_STYLE); + if (!value) goto document_error; + if (!yaml_document_append_mapping_pair(&output_document, + properties, key, value)) goto document_error; + + break; + + case YAML_DOCUMENT_START_EVENT: + + /* Add 'type': 'DOCUMENT-START'. */ + + key = yaml_document_add_scalar(&output_document, NULL, + "type", -1, YAML_PLAIN_SCALAR_STYLE); + if (!key) goto document_error; + value = yaml_document_add_scalar(&output_document, NULL, + "DOCUMENT-START", -1, YAML_PLAIN_SCALAR_STYLE); + if (!value) goto document_error; + if (!yaml_document_append_mapping_pair(&output_document, + properties, key, value)) goto document_error; + + /* Display the output_document version numbers. */ + + if (input_event.data.document_start.version_directive) + { + yaml_version_directive_t *version + = input_event.data.document_start.version_directive; + char number[64]; + + /* Add 'version': {}. */ + + key = yaml_document_add_scalar(&output_document, NULL, + "version", -1, YAML_PLAIN_SCALAR_STYLE); + if (!key) goto document_error; + map = yaml_document_add_mapping(&output_document, NULL, + YAML_FLOW_MAPPING_STYLE); + if (!map) goto document_error; + if (!yaml_document_append_mapping_pair(&output_document, + properties, key, map)) goto document_error; + + /* Add 'major': . */ + + key = yaml_document_add_scalar(&output_document, NULL, + "major", -1, YAML_PLAIN_SCALAR_STYLE); + if (!key) goto document_error; + sprintf(number, "%d", version->major); + value = yaml_document_add_scalar(&output_document, YAML_INT_TAG, + number, -1, YAML_PLAIN_SCALAR_STYLE); + if (!value) goto document_error; + if (!yaml_document_append_mapping_pair(&output_document, + map, key, value)) goto document_error; + + /* Add 'minor': . */ + + key = yaml_document_add_scalar(&output_document, NULL, + "minor", -1, YAML_PLAIN_SCALAR_STYLE); + if (!key) goto document_error; + sprintf(number, "%d", version->minor); + value = yaml_document_add_scalar(&output_document, YAML_INT_TAG, + number, -1, YAML_PLAIN_SCALAR_STYLE); + if (!value) goto document_error; + if (!yaml_document_append_mapping_pair(&output_document, + map, key, value)) goto document_error; + } + + /* Display the output_document tag directives. */ + + if (input_event.data.document_start.tag_directives.start + != input_event.data.document_start.tag_directives.end) + { + yaml_tag_directive_t *tag; + + /* Add 'tags': []. */ + + key = yaml_document_add_scalar(&output_document, NULL, + "tags", -1, YAML_PLAIN_SCALAR_STYLE); + if (!key) goto document_error; + seq = yaml_document_add_sequence(&output_document, NULL, + YAML_BLOCK_SEQUENCE_STYLE); + if (!seq) goto document_error; + if (!yaml_document_append_mapping_pair(&output_document, + properties, key, seq)) goto document_error; + + for (tag = input_event.data.document_start.tag_directives.start; + tag != input_event.data.document_start.tag_directives.end; + tag ++) + { + /* Add {}. */ + + map = yaml_document_add_mapping(&output_document, NULL, + YAML_FLOW_MAPPING_STYLE); + if (!map) goto document_error; + if (!yaml_document_append_sequence_item(&output_document, + seq, map)) goto document_error; + + /* Add 'handle': . */ + + key = yaml_document_add_scalar(&output_document, NULL, + "handle", -1, YAML_PLAIN_SCALAR_STYLE); + if (!key) goto document_error; + value = yaml_document_add_scalar(&output_document, NULL, + tag->handle, -1, YAML_DOUBLE_QUOTED_SCALAR_STYLE); + if (!value) goto document_error; + if (!yaml_document_append_mapping_pair(&output_document, + map, key, value)) goto document_error; + + /* Add 'prefix': . */ + + key = yaml_document_add_scalar(&output_document, NULL, + "prefix", -1, YAML_PLAIN_SCALAR_STYLE); + if (!key) goto document_error; + value = yaml_document_add_scalar(&output_document, NULL, + tag->prefix, -1, YAML_DOUBLE_QUOTED_SCALAR_STYLE); + if (!value) goto document_error; + if (!yaml_document_append_mapping_pair(&output_document, + map, key, value)) goto document_error; + } + } + + /* Add 'implicit': . */ + + key = yaml_document_add_scalar(&output_document, NULL, + "implicit", -1, YAML_PLAIN_SCALAR_STYLE); + if (!key) goto document_error; + value = yaml_document_add_scalar(&output_document, YAML_BOOL_TAG, + (input_event.data.document_start.implicit ? + "true" : "false"), -1, YAML_PLAIN_SCALAR_STYLE); + if (!value) goto document_error; + if (!yaml_document_append_mapping_pair(&output_document, + properties, key, value)) goto document_error; + + break; + + case YAML_DOCUMENT_END_EVENT: + + /* Add 'type': 'DOCUMENT-END'. */ + + key = yaml_document_add_scalar(&output_document, NULL, + "type", -1, YAML_PLAIN_SCALAR_STYLE); + if (!key) goto document_error; + value = yaml_document_add_scalar(&output_document, NULL, + "DOCUMENT-END", -1, YAML_PLAIN_SCALAR_STYLE); + if (!value) goto document_error; + if (!yaml_document_append_mapping_pair(&output_document, + properties, key, value)) goto document_error; + + /* Add 'implicit': . */ + + key = yaml_document_add_scalar(&output_document, NULL, + "implicit", -1, YAML_PLAIN_SCALAR_STYLE); + if (!key) goto document_error; + value = yaml_document_add_scalar(&output_document, YAML_BOOL_TAG, + (input_event.data.document_end.implicit ? + "true" : "false"), -1, YAML_PLAIN_SCALAR_STYLE); + if (!value) goto document_error; + if (!yaml_document_append_mapping_pair(&output_document, + properties, key, value)) goto document_error; + + break; + + case YAML_ALIAS_EVENT: + + /* Add 'type': 'ALIAS'. */ + + key = yaml_document_add_scalar(&output_document, NULL, + "type", -1, YAML_PLAIN_SCALAR_STYLE); + if (!key) goto document_error; + value = yaml_document_add_scalar(&output_document, NULL, + "ALIAS", -1, YAML_PLAIN_SCALAR_STYLE); + if (!value) goto document_error; + if (!yaml_document_append_mapping_pair(&output_document, + properties, key, value)) goto document_error; + + /* Add 'anchor': . */ + + key = yaml_document_add_scalar(&output_document, NULL, + "anchor", -1, YAML_PLAIN_SCALAR_STYLE); + if (!key) goto document_error; + value = yaml_document_add_scalar(&output_document, NULL, + input_event.data.alias.anchor, -1, + YAML_DOUBLE_QUOTED_SCALAR_STYLE); + if (!value) goto document_error; + if (!yaml_document_append_mapping_pair(&output_document, + properties, key, value)) goto document_error; + + break; + + case YAML_SCALAR_EVENT: + + /* Add 'type': 'SCALAR'. */ + + key = yaml_document_add_scalar(&output_document, NULL, + "type", -1, YAML_PLAIN_SCALAR_STYLE); + if (!key) goto document_error; + value = yaml_document_add_scalar(&output_document, NULL, + "SCALAR", -1, YAML_PLAIN_SCALAR_STYLE); + if (!value) goto document_error; + if (!yaml_document_append_mapping_pair(&output_document, + properties, key, value)) goto document_error; + + /* Add 'anchor': . */ + + if (input_event.data.scalar.anchor) + { + key = yaml_document_add_scalar(&output_document, NULL, + "anchor", -1, YAML_PLAIN_SCALAR_STYLE); + if (!key) goto document_error; + value = yaml_document_add_scalar(&output_document, NULL, + input_event.data.scalar.anchor, -1, + YAML_DOUBLE_QUOTED_SCALAR_STYLE); + if (!value) goto document_error; + if (!yaml_document_append_mapping_pair(&output_document, + properties, key, value)) goto document_error; + } + + /* Add 'tag': . */ + + if (input_event.data.scalar.tag) + { + key = yaml_document_add_scalar(&output_document, NULL, + "tag", -1, YAML_PLAIN_SCALAR_STYLE); + if (!key) goto document_error; + value = yaml_document_add_scalar(&output_document, NULL, + input_event.data.scalar.tag, -1, + YAML_DOUBLE_QUOTED_SCALAR_STYLE); + if (!value) goto document_error; + if (!yaml_document_append_mapping_pair(&output_document, + properties, key, value)) goto document_error; + } + + /* Add 'value': . */ + + key = yaml_document_add_scalar(&output_document, NULL, + "value", -1, YAML_PLAIN_SCALAR_STYLE); + if (!key) goto document_error; + value = yaml_document_add_scalar(&output_document, NULL, + input_event.data.scalar.value, + input_event.data.scalar.length, + YAML_DOUBLE_QUOTED_SCALAR_STYLE); + if (!value) goto document_error; + if (!yaml_document_append_mapping_pair(&output_document, + properties, key, value)) goto document_error; + + /* Display if the scalar tag is implicit. */ + + /* Add 'implicit': {} */ + + key = yaml_document_add_scalar(&output_document, NULL, + "version", -1, YAML_PLAIN_SCALAR_STYLE); + if (!key) goto document_error; + map = yaml_document_add_mapping(&output_document, NULL, + YAML_FLOW_MAPPING_STYLE); + if (!map) goto document_error; + if (!yaml_document_append_mapping_pair(&output_document, + properties, key, map)) goto document_error; + + /* Add 'plain': . */ + + key = yaml_document_add_scalar(&output_document, NULL, + "plain", -1, YAML_PLAIN_SCALAR_STYLE); + if (!key) goto document_error; + value = yaml_document_add_scalar(&output_document, YAML_BOOL_TAG, + (input_event.data.scalar.plain_implicit ? + "true" : "false"), -1, YAML_PLAIN_SCALAR_STYLE); + if (!value) goto document_error; + if (!yaml_document_append_mapping_pair(&output_document, + map, key, value)) goto document_error; + + /* Add 'quoted': . */ + + key = yaml_document_add_scalar(&output_document, NULL, + "quoted", -1, YAML_PLAIN_SCALAR_STYLE); + if (!key) goto document_error; + value = yaml_document_add_scalar(&output_document, YAML_BOOL_TAG, + (input_event.data.scalar.quoted_implicit ? + "true" : "false"), -1, YAML_PLAIN_SCALAR_STYLE); + if (!value) goto document_error; + if (!yaml_document_append_mapping_pair(&output_document, + map, key, value)) goto document_error; + + /* Display the style information. */ + + if (input_event.data.scalar.style) + { + yaml_scalar_style_t style = input_event.data.scalar.style; + + /* Add 'style':