summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKirill Simonov <xi@resolvent.net>2007-01-07 20:11:16 +0000
committerKirill Simonov <xi@resolvent.net>2007-01-07 20:11:16 +0000
commite27a3c886ebe97dda5b6f961b04949bb3003d4a6 (patch)
treea99269e56362f968985551445cab0267c16d2aa9 /src
parenta907bf857a165a271a23140919c26cde14223dfc (diff)
downloadlibyaml-git-e27a3c886ebe97dda5b6f961b04949bb3003d4a6.tar.gz
Add functions for constructing, parsing and emitting YAML documents.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/api.c309
-rw-r--r--src/dumper.c394
-rw-r--r--src/emitter.c26
-rw-r--r--src/loader.c429
-rw-r--r--src/yaml_private.h30
6 files changed, 1104 insertions, 86 deletions
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
@@ -580,27 +580,44 @@ yaml_queue_extend(void **start, void **head, void **tail, void **end);
(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), \