Add a skeleton of the emitter.
diff --git a/lib/yaml/__init__.py b/lib/yaml/__init__.py
index 55e3f38..bf07a0e 100644
--- a/lib/yaml/__init__.py
+++ b/lib/yaml/__init__.py
@@ -7,6 +7,8 @@
from resolver import *
from constructor import *
+from emitter import *
+
from tokens import *
from events import *
from nodes import *
diff --git a/lib/yaml/emitter.py b/lib/yaml/emitter.py
new file mode 100644
index 0000000..4f33cb8
--- /dev/null
+++ b/lib/yaml/emitter.py
@@ -0,0 +1,180 @@
+
+# Events should obey the following grammar:
+# stream ::= STREAM-START document* STREAM-END
+# document ::= DOCUMENT-START node DOCUMENT-END
+# node ::= SCALAR | sequence | mapping
+# sequence ::= SEQUENCE-START node* SEQUENCE-END
+# mapping ::= MAPPING-START (node node)* MAPPING-END
+
+__all__ = ['Emitter', 'EmitterError']
+
+from error import YAMLError
+from events import *
+
+class EmitterError(YAMLError):
+ pass
+
+class Emitter:
+
+ def __init__(self, writer):
+ self.writer = writer
+ self.states = []
+ self.state = self.expect_stream_start
+ self.levels = []
+ self.level = 0
+ self.soft_space = False
+
+ def emit(self, event):
+ self.state(event)
+
+ def expect_stream_start(self, event):
+ if isinstance(event, StreamStartEvent):
+ self.state = self.expect_document_start
+ else:
+ raise EmitterError("expected StreamStartEvent, but got %s" % event.__class__.__name__)
+
+ def expect_document_start(self, event):
+ if isinstance(event, DocumentStartEvent):
+ self.write_document_start()
+ self.states.append(self.expect_document_end)
+ self.state = self.expect_root_node
+ elif isinstance(event, StreamEndEvent):
+ self.writer.flush()
+ self.state = self.expect_nothing
+ else:
+ raise EmitterError("expected DocumentStartEvent, but got %s" % event.__class__.__name__)
+
+ def expect_document_end(self, event):
+ if isinstance(event, DocumentEndEvent):
+ self.write_document_end()
+ self.state = self.expect_document_start
+ else:
+ raiseEmitterError("expected DocumentEndEvent, but got %s" % event.__class__.__name__)
+
+ def expect_root_node(self, event):
+ self.expect_node(event)
+
+ def expect_node(self, event):
+ if isinstance(event, AliasEvent):
+ self.write_anchor("*", event.anchor)
+ self.state = self.states.pop()
+ elif isinstance(event, NodeEvent):
+ if event.anchor:
+ self.write_anchor("&", event.anchor)
+ if event.tag:
+ self.write_tag(event.tag)
+ if isinstance(event, ScalarEvent):
+ self.write_scalar(event.value)
+ self.state = self.states.pop()
+ elif isinstance(event, SequenceEvent):
+ self.write_collection_start("[")
+ self.level += 1
+ self.state = self.expect_first_sequence_item
+ elif isinstance(event, MappingEvent):
+ self.write_collection_start("{")
+ self.level += 1
+ self.state = self.expect_first_mapping_key
+ else:
+ raise EmitterError("Expected NodeEvent, but got %s" % event.__class__.__name__)
+
+ def expect_first_sequence_item(self, event):
+ if isinstance(event, CollectionEndEvent):
+ self.write_collection_end("]")
+ self.state = self.states.pop()
+ else:
+ self.write_indent()
+ self.states.append(self.expect_sequence_item)
+ self.expect_node(event)
+
+ def expect_sequence_item(self, event):
+ if isinstance(event, CollectionEndEvent):
+ self.level -= 1
+ self.write_indent()
+ self.write_collection_end("]")
+ self.state = self.states.pop()
+ else:
+ self.write_indicator(",")
+ self.write_indent()
+ self.states.append(self.expect_sequence_item)
+ self.expect_node(event)
+
+ def expect_first_mapping_key(self, event):
+ if isinstance(event, CollectionEndEvent):
+ self.write_collection_end("}")
+ self.state = self.states.pop()
+ else:
+ self.write_indent()
+ self.write_indicator("?")
+ self.states.append(self.expect_mapping_value)
+ self.expect_node(event)
+
+ def expect_mapping_key(self, event):
+ if isinstance(event, CollectionEndEvent):
+ self.level -= 1
+ self.write_indent()
+ self.write_collection_end("}")
+ self.state = self.states.pop()
+ else:
+ self.write_indicator(",")
+ self.write_indent()
+ self.write_indicator("?")
+ self.states.append(self.expect_mapping_value)
+ self.expect_node(event)
+
+ def expect_mapping_value(self, event):
+ self.write_indent()
+ self.write_indicator(":")
+ self.states.append(self.expect_mapping_key)
+ self.expect_node(event)
+
+ def expect_nothing(self, event):
+ raise EmitterError("expected nothing, but got %s" % event.__class__.__name__)
+
+ def write_document_start(self):
+ self.writer.write("%YAML 1.1\n")
+ self.writer.write("---")
+ self.soft_space = True
+
+ def write_document_end(self):
+ self.writer.write("\n...\n")
+ self.soft_space = False
+
+ def write_collection_start(self, indicator):
+ if self.soft_space:
+ self.writer.write(" ")
+ self.writer.write(indicator)
+ self.soft_space = False
+
+ def write_collection_end(self, indicator):
+ self.writer.write(indicator)
+ self.soft_space = True
+
+ def write_anchor(self, indicator, name):
+ if self.soft_space:
+ self.writer.write(" ")
+ self.writer.write("%s%s" % (indicator, name))
+ self.soft_space = True
+
+ def write_tag(self, tag):
+ if self.soft_space:
+ self.writer.write(" ")
+ if tag.startswith("tag:yaml.org,2002:"):
+ self.writer.write("!!%s" % tag[len("tag.yaml.org,2002:"):])
+ else:
+ self.writer.write("!<%s>" % tag)
+ self.soft_space = True
+
+ def write_scalar(self, value):
+ if self.soft_space:
+ self.writer.write(" ")
+ self.writer.write("\"%s\"" % value.encode('utf-8'))
+ self.soft_space = True
+
+ def write_indicator(self, indicator):
+ self.writer.write(indicator)
+ self.soft_space = True
+
+ def write_indent(self):
+ self.writer.write("\n"+" "*(self.level*4))
+ self.soft_space = False
+
diff --git a/lib/yaml/events.py b/lib/yaml/events.py
index 8837633..97bccb3 100644
--- a/lib/yaml/events.py
+++ b/lib/yaml/events.py
@@ -1,18 +1,17 @@
class Event:
- def __init__(self, start_mark, end_mark):
+ def __init__(self, start_mark=None, end_mark=None):
self.start_mark = start_mark
self.end_mark = end_mark
def __repr__(self):
- attributes = [key for key in self.__dict__
- if not key.endswith('_mark')]
- attributes.sort()
+ attributes = [key for key in ['anchor', 'tag', 'value']
+ if hasattr(self, key)]
arguments = ', '.join(['%s=%r' % (key, getattr(self, key))
for key in attributes])
return '%s(%s)' % (self.__class__.__name__, arguments)
class NodeEvent(Event):
- def __init__(self, anchor, start_mark, end_mark):
+ def __init__(self, anchor, start_mark=None, end_mark=None):
self.anchor = anchor
self.start_mark = start_mark
self.end_mark = end_mark
@@ -21,7 +20,7 @@
pass
class ScalarEvent(NodeEvent):
- def __init__(self, anchor, tag, value, start_mark, end_mark):
+ def __init__(self, anchor, tag, value, start_mark=None, end_mark=None):
self.anchor = anchor
self.tag = tag
self.value = value
@@ -29,7 +28,7 @@
self.end_mark = end_mark
class CollectionEvent(NodeEvent):
- def __init__(self, anchor, tag, start_mark, end_mark):
+ def __init__(self, anchor, tag, start_mark=None, end_mark=None):
self.anchor = anchor
self.tag = tag
self.start_mark = start_mark
diff --git a/lib/yaml/tokens.py b/lib/yaml/tokens.py
index 59b36af..93c3005 100644
--- a/lib/yaml/tokens.py
+++ b/lib/yaml/tokens.py
@@ -1,6 +1,6 @@
class Token:
- def __init__(self, start_mark, end_mark):
+ def __init__(self, start_mark=None, end_mark=None):
self.start_mark = start_mark
self.end_mark = end_mark
def __repr__(self):
@@ -16,7 +16,7 @@
class DirectiveToken(Token):
id = '<directive>'
- def __init__(self, name, value, start_mark, end_mark):
+ def __init__(self, name, value, start_mark=None, end_mark=None):
self.name = name
self.value = value
self.start_mark = start_mark
@@ -69,28 +69,28 @@
class AliasToken(Token):
id = '<alias>'
- def __init__(self, value, start_mark, end_mark):
+ def __init__(self, value, start_mark=None, end_mark=None):
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
class AnchorToken(Token):
id = '<anchor>'
- def __init__(self, value, start_mark, end_mark):
+ def __init__(self, value, start_mark=None, end_mark=None):
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
class TagToken(Token):
id = '<tag>'
- def __init__(self, value, start_mark, end_mark):
+ def __init__(self, value, start_mark=None, end_mark=None):
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
class ScalarToken(Token):
id = '<scalar>'
- def __init__(self, value, plain, start_mark, end_mark):
+ def __init__(self, value, plain, start_mark=None, end_mark=None):
self.value = value
self.plain = plain
self.start_mark = start_mark
diff --git a/tests/test_emitter.py b/tests/test_emitter.py
new file mode 100644
index 0000000..1ff0ecc
--- /dev/null
+++ b/tests/test_emitter.py
@@ -0,0 +1,19 @@
+
+import test_appliance, sys
+
+from yaml import *
+
+class TestEmitterOnCanonical(test_appliance.TestAppliance):
+
+ def _testEmitterOnCanonical(self, test_name, canonical_filename):
+ events = list(iter(Parser(Scanner(Reader(file(canonical_filename, 'rb'))))))
+ writer = sys.stdout
+ emitter = Emitter(writer)
+ print "-"*30
+ print "ORIGINAL DATA:"
+ print file(canonical_filename, 'rb').read()
+ for event in events:
+ emitter.emit(event)
+
+TestEmitterOnCanonical.add_tests('testEmitterOnCanonical', '.canonical')
+
diff --git a/tests/test_yaml.py b/tests/test_yaml.py
index bb5a9f1..99e8b72 100644
--- a/tests/test_yaml.py
+++ b/tests/test_yaml.py
@@ -9,6 +9,7 @@
from test_errors import *
from test_detector import *
from test_constructor import *
+from test_emitter import *
from test_syck import *
def main(module='__main__'):