source: libyaml/trunk/src/dumper.c @ 238

Revision 238, 9.8 KB checked in by xi, 7 years ago (diff)

Add functions for constructing, parsing and emitting YAML documents.

Line 
1
2#include "yaml_private.h"
3
4/*
5 * API functions.
6 */
7
8YAML_DECLARE(int)
9yaml_emitter_open(yaml_emitter_t *emitter);
10
11YAML_DECLARE(int)
12yaml_emitter_close(yaml_emitter_t *emitter);
13
14YAML_DECLARE(int)
15yaml_emitter_dump(yaml_emitter_t *emitter, yaml_document_t *document);
16
17/*
18 * Clean up functions.
19 */
20
21static void
22yaml_emitter_delete_document_and_anchors(yaml_emitter_t *emitter);
23
24/*
25 * Anchor functions.
26 */
27
28static void
29yaml_emitter_anchor_node(yaml_emitter_t *emitter, int index);
30
31static yaml_char_t *
32yaml_emitter_generate_anchor(yaml_emitter_t *emitter, int anchor_id);
33
34
35/*
36 * Serialize functions.
37 */
38
39static int
40yaml_emitter_dump_node(yaml_emitter_t *emitter, int index);
41
42static int
43yaml_emitter_dump_alias(yaml_emitter_t *emitter, yaml_char_t *anchor);
44
45static int
46yaml_emitter_dump_scalar(yaml_emitter_t *emitter, yaml_node_t *node,
47        yaml_char_t *anchor);
48
49static int
50yaml_emitter_dump_sequence(yaml_emitter_t *emitter, yaml_node_t *node,
51        yaml_char_t *anchor);
52
53static int
54yaml_emitter_dump_mapping(yaml_emitter_t *emitter, yaml_node_t *node,
55        yaml_char_t *anchor);
56
57/*
58 * Issue a STREAM-START event.
59 */
60
61YAML_DECLARE(int)
62yaml_emitter_open(yaml_emitter_t *emitter)
63{
64    yaml_event_t event;
65    yaml_mark_t mark = { 0, 0, 0 };
66
67    assert(emitter);            /* Non-NULL emitter object is required. */
68    assert(!emitter->opened);   /* Emitter should not be opened yet. */
69
70    STREAM_START_EVENT_INIT(event, YAML_ANY_ENCODING, mark, mark);
71
72    if (!yaml_emitter_emit(emitter, &event)) {
73        return 0;
74    }
75
76    emitter->opened = 1;
77
78    return 1;
79}
80
81/*
82 * Issue a STREAM-END event.
83 */
84
85YAML_DECLARE(int)
86yaml_emitter_close(yaml_emitter_t *emitter)
87{
88    yaml_event_t event;
89    yaml_mark_t mark = { 0, 0, 0 };
90
91    assert(emitter);            /* Non-NULL emitter object is required. */
92    assert(emitter->opened);    /* Emitter should be opened. */
93
94    if (emitter->closed) return 1;
95
96    STREAM_END_EVENT_INIT(event, mark, mark);
97
98    if (!yaml_emitter_emit(emitter, &event)) {
99        return 0;
100    }
101
102    emitter->closed = 1;
103
104    return 1;
105}
106
107/*
108 * Dump a YAML document.
109 */
110
111YAML_DECLARE(int)
112yaml_emitter_dump(yaml_emitter_t *emitter, yaml_document_t *document)
113{
114    yaml_event_t event;
115    yaml_mark_t mark = { 0, 0, 0 };
116
117    assert(emitter);            /* Non-NULL emitter object is required. */
118    assert(document);           /* Non-NULL emitter object is expected. */
119
120    emitter->document = document;
121
122    if (!emitter->opened) {
123        if (!yaml_emitter_open(emitter)) goto error;
124    }
125
126    if (STACK_EMPTY(emitter, document->nodes)) {
127        if (!yaml_emitter_close(emitter)) goto error;
128        yaml_emitter_delete_document_and_anchors(emitter);
129        return 1;
130    }
131
132    assert(emitter->opened);    /* Emitter should be opened. */
133
134    emitter->anchors = yaml_malloc(sizeof(*(emitter->anchors))
135            * (document->nodes.top - document->nodes.start));
136    if (!emitter->anchors) goto error;
137    memset(emitter->anchors, 0, sizeof(*(emitter->anchors))
138            * (document->nodes.top - document->nodes.start));
139
140    DOCUMENT_START_EVENT_INIT(event, document->version_directive,
141            document->tag_directives.start, document->tag_directives.end,
142            document->start_implicit, mark, mark);
143    if (!yaml_emitter_emit(emitter, &event)) goto error;
144
145    yaml_emitter_anchor_node(emitter, 1);
146    if (!yaml_emitter_dump_node(emitter, 1)) goto error;
147
148    DOCUMENT_END_EVENT_INIT(event, document->end_implicit, mark, mark);
149    if (!yaml_emitter_emit(emitter, &event)) goto error;
150
151    yaml_emitter_delete_document_and_anchors(emitter);
152
153    return 1;
154
155error:
156
157    yaml_emitter_delete_document_and_anchors(emitter);
158
159    return 0;
160}
161
162/*
163 * Clean up the emitter object after a document is dumped.
164 */
165
166static void
167yaml_emitter_delete_document_and_anchors(yaml_emitter_t *emitter)
168{
169    int index;
170
171    if (!emitter->anchors) {
172        yaml_document_delete(emitter->document);
173        emitter->document = NULL;
174        return;
175    }
176
177    for (index = 0; emitter->document->nodes.start + index
178            < emitter->document->nodes.top; index ++) {
179        yaml_node_t node = emitter->document->nodes.start[index];
180        if (!emitter->anchors[index].serialized) {
181            yaml_free(node.tag);
182            if (node.type == YAML_SCALAR_NODE) {
183                yaml_free(node.data.scalar.value);
184            }
185        }
186        if (node.type == YAML_SEQUENCE_NODE) {
187            STACK_DEL(emitter, node.data.sequence.items);
188        }
189        if (node.type == YAML_MAPPING_NODE) {
190            STACK_DEL(emitter, node.data.mapping.pairs);
191        }
192    }
193
194    STACK_DEL(emitter, emitter->document->nodes);
195    yaml_free(emitter->anchors);
196
197    emitter->anchors = NULL;
198    emitter->last_anchor_id = 0;
199    emitter->document = NULL;
200}
201
202/*
203 * Check the references of a node and assign the anchor id if needed.
204 */
205
206static void
207yaml_emitter_anchor_node(yaml_emitter_t *emitter, int index)
208{
209    yaml_node_t *node = emitter->document->nodes.start + index - 1;
210    yaml_node_item_t *item;
211    yaml_node_pair_t *pair;
212
213    emitter->anchors[index-1].references ++;
214
215    if (emitter->anchors[index-1].references == 1) {
216        switch (node->type) {
217            case YAML_SEQUENCE_NODE:
218                for (item = node->data.sequence.items.start;
219                        item < node->data.sequence.items.top; item ++) {
220                    yaml_emitter_anchor_node(emitter, *item);
221                }
222                break;
223            case YAML_MAPPING_NODE:
224                for (pair = node->data.mapping.pairs.start;
225                        pair < node->data.mapping.pairs.top; pair ++) {
226                    yaml_emitter_anchor_node(emitter, pair->key);
227                    yaml_emitter_anchor_node(emitter, pair->value);
228                }
229                break;
230            default:
231                break;
232        }
233    }
234
235    else if (emitter->anchors[index-1].references == 2) {
236        emitter->anchors[index-1].anchor = (++ emitter->last_anchor_id);
237    }
238}
239
240/*
241 * Generate a textual representation for an anchor.
242 */
243
244#define ANCHOR_TEMPLATE         "id%03d"
245#define ANCHOR_TEMPLATE_LENGTH  16
246
247static yaml_char_t *
248yaml_emitter_generate_anchor(yaml_emitter_t *emitter, int anchor_id)
249{
250    yaml_char_t *anchor = yaml_malloc(ANCHOR_TEMPLATE_LENGTH);
251
252    if (!anchor) return NULL;
253
254    sprintf((char *)anchor, ANCHOR_TEMPLATE, anchor_id);
255
256    return anchor;
257}
258
259/*
260 * Serialize a node.
261 */
262
263static int
264yaml_emitter_dump_node(yaml_emitter_t *emitter, int index)
265{
266    yaml_node_t *node = emitter->document->nodes.start + index - 1;
267    int anchor_id = emitter->anchors[index-1].anchor;
268    yaml_char_t *anchor = NULL;
269
270    if (anchor_id) {
271        anchor = yaml_emitter_generate_anchor(emitter, anchor_id);
272        if (!anchor) return 0;
273    }
274
275    if (emitter->anchors[index-1].serialized) {
276        return yaml_emitter_dump_alias(emitter, anchor);
277    }
278
279    emitter->anchors[index-1].serialized = 1;
280
281    switch (node->type) {
282        case YAML_SCALAR_NODE:
283            return yaml_emitter_dump_scalar(emitter, node, anchor);
284        case YAML_SEQUENCE_NODE:
285            return yaml_emitter_dump_sequence(emitter, node, anchor);
286        case YAML_MAPPING_NODE:
287            return yaml_emitter_dump_mapping(emitter, node, anchor);
288        default:
289            assert(0);      /* Could not happen. */
290            break;
291    }
292
293    return 0;       /* Could not happen. */
294}
295
296/*
297 * Serialize an alias.
298 */
299
300static int
301yaml_emitter_dump_alias(yaml_emitter_t *emitter, yaml_char_t *anchor)
302{
303    yaml_event_t event;
304    yaml_mark_t mark  = { 0, 0, 0 };
305
306    ALIAS_EVENT_INIT(event, anchor, mark, mark);
307
308    return yaml_emitter_emit(emitter, &event);
309}
310
311/*
312 * Serialize a scalar.
313 */
314
315static int
316yaml_emitter_dump_scalar(yaml_emitter_t *emitter, yaml_node_t *node,
317        yaml_char_t *anchor)
318{
319    yaml_event_t event;
320    yaml_mark_t mark  = { 0, 0, 0 };
321
322    int plain_implicit = (strcmp((char *)node->tag,
323                YAML_DEFAULT_SCALAR_TAG) == 0);
324    int quoted_implicit = (strcmp((char *)node->tag,
325                YAML_DEFAULT_SCALAR_TAG) == 0);
326
327    SCALAR_EVENT_INIT(event, anchor, node->tag, node->data.scalar.value,
328            node->data.scalar.length, plain_implicit, quoted_implicit,
329            node->data.scalar.style, mark, mark);
330
331    return yaml_emitter_emit(emitter, &event);
332}
333
334/*
335 * Serialize a sequence.
336 */
337
338static int
339yaml_emitter_dump_sequence(yaml_emitter_t *emitter, yaml_node_t *node,
340        yaml_char_t *anchor)
341{
342    yaml_event_t event;
343    yaml_mark_t mark  = { 0, 0, 0 };
344
345    int implicit = (strcmp((char *)node->tag, YAML_DEFAULT_SEQUENCE_TAG) == 0);
346
347    yaml_node_item_t *item;
348
349    SEQUENCE_START_EVENT_INIT(event, anchor, node->tag, implicit,
350            node->data.sequence.style, mark, mark);
351    if (!yaml_emitter_emit(emitter, &event)) return 0;
352
353    for (item = node->data.sequence.items.start;
354            item < node->data.sequence.items.top; item ++) {
355        if (!yaml_emitter_dump_node(emitter, *item)) return 0;
356    }
357
358    SEQUENCE_END_EVENT_INIT(event, mark, mark);
359    if (!yaml_emitter_emit(emitter, &event)) return 0;
360
361    return 1;
362}
363
364/*
365 * Serialize a mapping.
366 */
367
368static int
369yaml_emitter_dump_mapping(yaml_emitter_t *emitter, yaml_node_t *node,
370        yaml_char_t *anchor)
371{
372    yaml_event_t event;
373    yaml_mark_t mark  = { 0, 0, 0 };
374
375    int implicit = (strcmp((char *)node->tag, YAML_DEFAULT_MAPPING_TAG) == 0);
376
377    yaml_node_pair_t *pair;
378
379    MAPPING_START_EVENT_INIT(event, anchor, node->tag, implicit,
380            node->data.mapping.style, mark, mark);
381    if (!yaml_emitter_emit(emitter, &event)) return 0;
382
383    for (pair = node->data.mapping.pairs.start;
384            pair < node->data.mapping.pairs.top; pair ++) {
385        if (!yaml_emitter_dump_node(emitter, pair->key)) return 0;
386        if (!yaml_emitter_dump_node(emitter, pair->value)) return 0;
387    }
388
389    MAPPING_END_EVENT_INIT(event, mark, mark);
390    if (!yaml_emitter_emit(emitter, &event)) return 0;
391
392    return 1;
393}
394
Note: See TracBrowser for help on using the repository browser.