source: pyyaml/trunk/lib/yaml/emitter.py @ 131

Revision 131, 14.7 KB checked in by xi, 8 years ago (diff)

Working on emitter: implement the state machine.

Line 
1
2# Emitter expects events obeying the following grammar:
3# stream ::= STREAM-START document* STREAM-END
4# document ::= DOCUMENT-START node DOCUMENT-END
5# node ::= SCALAR | sequence | mapping
6# sequence ::= SEQUENCE-START node* SEQUENCE-END
7# mapping ::= MAPPING-START (node node)* MAPPING-END
8
9__all__ = ['Emitter', 'EmitterError']
10
11from error import YAMLError
12from events import *
13
14class EmitterError(YAMLError):
15    pass
16
17class Emitter:
18
19    DEFAULT_TAG_PREFIXES = {
20        u'!' : u'!',
21        u'tag:yaml.org,2002:' : u'!!',
22    }
23
24    def __init__(self, writer):
25
26        # The writer should have the methods `write` and possibly `flush`.
27        self.writer = writer
28
29        # Encoding is provided by STREAM-START.
30        self.encoding = None
31
32        # Emitter is a state machine with a stack of states to handle nested
33        # structures.
34        self.states = []
35        self.state = self.expect_stream_start
36
37        # Current event and the event queue.
38        self.events = []
39        self.event = None
40
41        # The current indentation level and the stack of previous indents.
42        self.indents = []
43        self.indent = None
44
45        # Flow level.
46        self.flow_level = 0
47
48        # Contexts.
49        self.root_context = False
50        self.sequence_context = False
51        self.mapping_context = False
52        self.simple_key_context = False
53
54        # Characteristics of the last emitted character:
55        #  - current position.
56        #  - is it a line break?
57        #  - is it a whitespace?
58        #  - is it an indention character
59        #    (indentation space, '-', '?', or ':')?
60        self.line = 0
61        self.column = 0
62        self.whitespace = True
63        self.indention = True
64
65        # Formatting details.
66        self.canonical = False
67        self.best_line_break = u'\n'
68        self.best_indent = 2
69        self.best_width = 80
70        self.tag_prefixes = None
71
72        # Scalar analysis.
73        self.analysis = None
74
75    def emit(self, event):
76        if self.events:
77            self.events.append(event)
78            event = self.events.pop(0)
79        self.event = event
80        if self.need_more_events():
81            self.event.insert(0, event)
82            return
83        self.state()
84        self.event = None
85
86    # In some cases, we wait for a few next events before emitting.
87
88    def need_more_events(self):
89        if isinstance(self.event, DocumentStartEvent):
90            return self.need_events(1)
91        elif isinstance(self.event, SequenceStartEvent):
92            return self.need_events(2)
93        elif isinstance(self.event, MappingStartEvent):
94            return self.need_events(3)
95        else:
96            return False
97
98    def need_events(self, count):
99        level = 0
100        for event in self.events:
101            if isinstance(event, (DocumentStart, CollectionStart)):
102                level += 1
103            elif isinstance(event, (DocumentEnd, CollectionEnd)):
104                level -= 1
105            elif isinstance(event, StreamEnd):
106                level = -1
107            if level < 0:
108                return False
109        return (len(self.events) < count)
110
111    def increase_indent(self, flow=False, indentless=False):
112        self.indents.append(self.indent)
113        if self.indent is None:
114            if flow:
115                self.indent = self.best_indent
116            else:
117                self.indent = 0
118        elif not indentless:
119            self.indent += self.best_indent
120
121    # States.
122
123    # Stream handlers.
124
125    def expect_stream_start(self):
126        if isinstance(self.event, StreamStartEvent):
127            self.encoding = event.encoding
128            self.canonical = event.canonical
129            if self.event.indent and self.event.indent > 1:
130                self.best_indent = self.event.indent
131            if self.event.width and self.event.width > self.best_indent:
132                self.best_width = self.event.width
133            if self.event.line_break in [u'\r', u'\n', u'\r\n']:
134                self.best_line_break = self.event.line_break
135            self.write_stream_start()
136            self.state = self.expect_first_document_start
137        else:
138            raise EmitterError("expected StreamStartEvent, but got %s"
139                    % self.event)
140
141    def expect_nothing(self):
142        raise EmitterError("expected nothing, but got %s" % self.event)
143
144    # Document handlers.
145
146    def expect_first_document_start(self):
147        return self.expect_document_start(first=True)
148
149    def expect_document_start(self, first=False):
150        if isinstance(self.event, DocumentStartEvent):
151            if self.event.version:
152                self.write_version_directive(self.event.version)
153            self.tag_prefixes = self.DEFAULT_TAG_PREFIXES.copy()
154            if self.event.tags:
155                for handle in self.event.tags:
156                    prefix = self.event.tags[handle]
157                    self.tag_prefixes[prefix] = handle
158                    self.write_tag_directive(handle, prefix)
159            implicit = (first and self.event.implicit and not self.canonical
160                    and not self.event.version and not self.event.tags
161                    and not self.check_next_empty_scalar())
162            if not implicit:
163                self.write_indent()
164                self.write_indicator(u'---', True)
165                if self.canonical:
166                    self.write_indent()
167            self.state = self.expect_document_root
168        elif isinstance(self.event, StreamEndEvent):
169            self.write_stream_end()
170            self.state = self.expect_nothing
171        else:
172            raise EmitterError("expected DocumentStartEvent, but got %s"
173                    % self.event)
174
175    def expect_document_end(self):
176        if isinstance(self.event, DocumentEndEvent):
177            self.write_indent()
178            if not event.implicit:
179                self.write_indicator(u'...', True)
180                self.write_indent()
181            self.state = self.expect_document_start
182        else:
183            raise EmitterError("expected DocumentEndEvent, but got %s"
184                    % self.event)
185
186    def expect_document_root(self):
187        self.expect_node(root=True)
188
189    # Node handlers.
190
191    def expect_node(self, root=False, sequence=False, mapping=False,
192            simple_key=False):
193        self.root_context = root
194        self.sequence_context = sequence
195        self.mapping_context = mapping
196        self.simple_key_context = simple_key
197        if isinstance(self.event, AliasEvent):
198            self.expect_alias()
199        elif isinstance(event, (ScalarEvent, CollectionEvent)):
200            self.process_anchor()
201            self.process_tag()
202            if isinstance(self.event, ScalarEvent):
203                self.expect_scalar()
204            elif isinstance(self.event, SequenceEvent):
205                if self.flow_level or self.canonical or self.event.flow_style   \
206                        or self.check_empty_sequence():
207                    self.expect_flow_sequence()
208                else:
209                    self.expect_block_sequence()
210            elif isinstance(self.event, MappingEvent):
211                if self.flow_level or self.canonical or self.event.flow_style   \
212                        or self.check_empty_mapping():
213                    self.expect_flow_mapping()
214                else:
215                    self.expect_block_mapping()
216        else:
217            raise EmitterError("expected NodeEvent, but got %s" % self.event)
218
219    def expect_alias(self):
220        self.write_anchor(u'*', self.event.anchor)
221        self.state = self.states.pop()
222
223    def expect_scalar(self):
224        self.increase_indent(flow=True)
225        self.process_scalar()
226        self.indent = self.indents.pop()
227        self.state = self.states.pop()
228
229    # Flow sequence handlers.
230
231    def expect_flow_sequence(self):
232        self.write_indicator(u'[', True, whitespace=True)
233        self.flow_level += 1
234        self.increase_indent(flow=True)
235        self.state = self.expect_first_flow_sequence_item
236
237    def expect_first_flow_sequence_item(self):
238        if isinstance(self.event, SequenceEndEvent):
239            self.indent = self.indents.pop()
240            self.flow_level -= 1
241            self.write_indicator(u']', False)
242            self.state = self.states.pop()
243        else:
244            if self.canonical or self.column > self.best_width:
245                self.write_indent()
246            self.states.append(self.expect_flow_sequence_item)
247            self.expect_node(sequence=True)
248
249    def expect_flow_sequence_item(self):
250        if isinstance(self.event, SequenceEndEvent):
251            self.indent = self.indents.pop()
252            self.flow_level -= 1
253            if self.canonical:
254                self.write_indicator(u',', False)
255                self.write_indent()
256            self.write_indicator(u']', False)
257            self.state = self.states.pop()
258        else:
259            self.write_indicator(u',', False)
260            if self.canonical or self.column > self.best_width:
261                self.write_indent()
262            self.states.append(self.expect_flow_sequence_item)
263            self.expect_node(sequence=True)
264
265    # Flow mapping handlers.
266
267    def expect_flow_mapping(self):
268        self.write_indicator(u'{', True, whitespace=True)
269        self.flow_level += 1
270        self.increase_indent(flow=True)
271        self.state = self.expect_first_flow_mapping_key
272
273    def expect_first_flow_mapping_key(self):
274        if isinstance(self.event, MappingEndEvent):
275            self.indent = self.indents.pop()
276            self.flow_level -= 1
277            self.write_indicator(u'}', False)
278            self.state = self.states.pop()
279        else:
280            if self.canonical or self.column > self.best_width:
281                self.write_indent()
282            if not self.canonical and self.check_simple_key():
283                self.states.append(self.expect_flow_mapping_simple_value)
284                self.expect_node(mapping=True, simple_key=True)
285            else:
286                self.write_indicator(u'?', True)
287                self.states.append(self.expect_flow_mapping_value)
288                self.expect_node(mapping=True)
289
290    def expect_flow_mapping_key(self):
291        if isinstance(self.event, MappingEndEvent):
292            self.indent = self.indents.pop()
293            self.flow_level -= 1
294            if self.canonical:
295                self.write_indicator(u',', False)
296                self.write_indent()
297            self.write_indicator(u'}', False)
298            self.state = self.states.pop()
299        else:
300            self.write_indicator(u',', False)
301            if self.canonical or self.column > self.best_width:
302                self.write_indent()
303            if not self.canonical and self.check_simple_key():
304                self.states.append(self.expect_flow_mapping_simple_value)
305                self.expect_node(mapping=True, simple_key=True)
306            else:
307                self.write_indicator(u'?', True)
308                self.states.append(self.expect_flow_mapping_value)
309                self.expect_node(mapping=True)
310
311    def expect_flow_mapping_simple_value(self):
312        self.write_indicator(u':', False)
313        self.states.append(self.expect_flow_mapping_key)
314        self.expect_node(mapping=True)
315
316    def expect_flow_mapping_value(self):
317        if self.canonical or self.column > self.best_width:
318            self.write_indent()
319        self.write_indicator(u':', True)
320        self.states.append(self.expect_flow_mapping_key)
321        self.expect_node(mapping=True)
322
323    # Block sequence handlers.
324
325    def expect_block_sequence(self):
326        indentless = (self.mapping_context and not self.indention)
327        self.increase_indent(flow=False, indentless=indentless)
328        self.state = self.expect_first_block_sequence_item
329
330    def expect_first_block_sequence_item(self):
331        return self.expect_block_sequence_item(first=True)
332
333    def expect_block_sequence_item(self, first=False):
334        if not first and isinstance(self.event, SequenceEndEvent):
335            self.indent = self.indents.pop()
336            self.state = self.states.pop()
337        else:
338            self.write_indent()
339            self.write_indicator(u'-', True, indention=True)
340            self.states.append(self.expect_block_sequence_item)
341            self.expect_node(sequence=True)
342
343    # Block mapping handlers.
344
345    def expect_block_mapping(self):
346        self.increase_indent(flow=False)
347        self.state = self.expect_first_block_mapping_key
348
349    def expect_first_block_mapping_key(self):
350        return self.expect_block_mapping_key(first=True)
351
352    def expect_block_mapping_key(self, first=False):
353        if not first and isinstance(self.event, SequenceEndEvent):
354            self.indent = self.indents.pop()
355            self.state = self.states.pop()
356        else:
357            self.write_indent()
358            if self.check_simple_key():
359                self.states.append(self.expect_block_mapping_simple_value)
360                self.expect_node(mapping=True, simple_key=True)
361            else:
362                self.write_indicator(u'?', True, indention=True)
363                self.states.append(self.expect_block_mapping_value)
364                self.expect_node(mapping=True)
365
366    def expect_block_mapping_simple_value(self):
367        self.write_indicator(u':', False)
368        self.states.append(self.expect_block_mapping_key)
369        self.expect_node(mapping=True)
370
371    def expect_block_mapping_value(self):
372        self.write_indent()
373        self.write_indicator(u':', True, indention=True)
374        self.states.append(self.expect_block_mapping_key)
375        self.expect_node(mapping=True)
376
377    # Writers.
378
379    def write_stream_start(self):
380        # Write BOM if needed.
381        if self.encoding and self.encoding.startswith('utf-16'):
382            self.writer.write(u'\xFF\xFE'.encode(self.encoding))
383
384    def write_stream_end(self):
385        if hasattr(self.writer, 'flush'):
386            self.writer.flush()
387
388    def write_indicator(self, indicator, need_whitespace,
389            whitespace=False, indention=False):
390        if self.whitespace:
391            data = indicator
392        else:
393            data = u' '+indicator
394        self.writespace = whitespace
395        self.indention = self.indention and indention
396        self.column += len(data)
397        if self.encoding:
398            data = data.encode(self.encoding)
399        self.writer.write(data)
400
401    def write_indent(self):
402        indent = self.indent or 0
403        if not self.indention or self.column > indent:
404            self.write_line_break()
405        if self.column < indent:
406            data = u' '*(indent-self.column)
407            self.column = indent
408            if self.encoding:
409                data = data.encode(self.encoding)
410            self.writer.write(data)
411
412    def write_line_break(self):
413        data = self.best_line_break
414        self.whitespace = True
415        self.indention = True
416        self.line += 1
417        self.column = 0
418        if self.encoding:
419            data = data.encode(self.encoding)
420        self.writer.write(data)
421
Note: See TracBrowser for help on using the repository browser.