Add support for recursive nodes to Composer. Constructor does not support recursive...
[pyyaml/python3.git] / lib / yaml / composer.py
blobd256b0542611287fd26aee0aa7110121762c5d2e
2 __all__ = ['Composer', 'ComposerError']
4 from error import MarkedYAMLError
5 from events import *
6 from nodes import *
8 class ComposerError(MarkedYAMLError):
9 pass
11 class Composer:
13 def __init__(self):
14 self.anchors = {}
16 def check_node(self):
17 # If there are more documents available?
18 return not self.check_event(StreamEndEvent)
20 def get_node(self):
21 # Get the root node of the next document.
22 if not self.check_event(StreamEndEvent):
23 return self.compose_document()
25 def __iter__(self):
26 # Iterator protocol.
27 while not self.check_event(StreamEndEvent):
28 yield self.compose_document()
30 def compose_document(self):
32 # Drop the STREAM-START event.
33 if self.check_event(StreamStartEvent):
34 self.get_event()
36 # Drop the DOCUMENT-START event.
37 self.get_event()
39 # Compose the root node.
40 node = self.compose_node(None, None)
42 # Drop the DOCUMENT-END event.
43 self.get_event()
45 self.complete_anchors = {}
46 return node
48 def compose_node(self, parent, index):
49 if self.check_event(AliasEvent):
50 event = self.get_event()
51 anchor = event.anchor
52 if anchor not in self.anchors:
53 raise ComposerError(None, None, "found undefined alias %r"
54 % anchor.encode('utf-8'), event.start_mark)
55 return self.anchors[anchor]
56 event = self.peek_event()
57 anchor = event.anchor
58 if anchor is not None:
59 if anchor in self.anchors:
60 raise ComposerError("found duplicate anchor %r; first occurence"
61 % anchor.encode('utf-8'), self.anchors[anchor].start_mark,
62 "second occurence", event.start_mark)
63 self.descend_resolver(parent, index)
64 if self.check_event(ScalarEvent):
65 node = self.compose_scalar_node(anchor)
66 elif self.check_event(SequenceStartEvent):
67 node = self.compose_sequence_node(anchor)
68 elif self.check_event(MappingStartEvent):
69 node = self.compose_mapping_node(anchor)
70 self.ascend_resolver()
71 return node
73 def compose_scalar_node(self, anchor):
74 event = self.get_event()
75 tag = event.tag
76 if tag is None or tag == u'!':
77 tag = self.resolve(ScalarNode, event.value, event.implicit)
78 node = ScalarNode(tag, event.value,
79 event.start_mark, event.end_mark, style=event.style)
80 if anchor is not None:
81 self.anchors[anchor] = node
82 return node
84 def compose_sequence_node(self, anchor):
85 start_event = self.get_event()
86 tag = start_event.tag
87 if tag is None or tag == u'!':
88 tag = self.resolve(SequenceNode, None, start_event.implicit)
89 node = SequenceNode(tag, [],
90 start_event.start_mark, None,
91 flow_style=start_event.flow_style)
92 if anchor is not None:
93 self.anchors[anchor] = node
94 index = 0
95 while not self.check_event(SequenceEndEvent):
96 node.value.append(self.compose_node(node, index))
97 index += 1
98 end_event = self.get_event()
99 node.end_mark = end_event.end_mark
100 return node
102 def compose_mapping_node(self, anchor):
103 start_event = self.get_event()
104 tag = start_event.tag
105 if tag is None or tag == u'!':
106 tag = self.resolve(MappingNode, None, start_event.implicit)
107 node = MappingNode(tag, {},
108 start_event.start_mark, None,
109 flow_style=start_event.flow_style)
110 if anchor is not None:
111 self.anchors[anchor] = node
112 while not self.check_event(MappingEndEvent):
113 key_event = self.peek_event()
114 item_key = self.compose_node(node, None)
115 if item_key in node.value:
116 raise ComposerError("while composing a mapping", start_event.start_mark,
117 "found duplicate key", key_event.start_mark)
118 item_value = self.compose_node(node, item_key)
119 node.value[item_key] = item_value
120 end_event = self.get_event()
121 node.end_mark = end_event.end_mark
122 return node