Index: /branches/pyyaml3000/lib/yaml/composer.py
===================================================================
--- /branches/pyyaml3000/lib/yaml/composer.py	(revision 53)
+++ /branches/pyyaml3000/lib/yaml/composer.py	(revision 53)
@@ -0,0 +1,92 @@
+
+from error import MarkedYAMLError
+from events import *
+from nodes import *
+
+class ComposerError(MarkedYAMLError):
+    pass
+
+class Composer:
+
+    def __init__(self, parser):
+        self.parser = parser
+        self.all_anchors = {}
+        self.complete_anchors = {}
+
+    def check(self):
+        # If there are more documents available?
+        return not self.parser.check(StreamEndEvent)
+
+    def get(self):
+        # Get the root node of the next document.
+        if not self.parser.check(StreamEndEvent):
+            return self.compose_document()
+
+    def __iter__(self):
+        # Iterator protocol.
+        while not self.parser.check(StreamEndEvent):
+            yield self.compose_document()
+
+    def compose_document(self):
+        node = self.compose_node()
+        self.all_anchors = {}
+        self.complete_anchors = {}
+        return node
+
+    def compose_node(self):
+        if self.parser.check(AliasEvent):
+            event = self.parser.get()
+            anchor = event.anchor
+            if anchor not in self.all_anchors:
+                raise ComposerError(None, None, "found undefined alias %r"
+                        % anchor.encode('utf-8'), event.start_marker)
+            if anchor not in self.complete_anchors:
+                collection_event = self.all_anchors[anchor]
+                raise ComposerError("while composing a collection",
+                        collection_event.start_marker,
+                        "found recursive anchor %r" % anchor.encode('utf-8'),
+                        event.start_marker)
+            return self.complete_anchors[anchor]
+        event = self.parser.peek()
+        anchor = event.anchor
+        if anchor is not None:
+            if anchor in self.all_anchors:
+                raise ComposerError("found duplicate anchor %r; first occurence"
+                        % anchor.encode('utf-8'), self.all_anchors[anchor].start_marker,
+                        "second occurence", event.start_marker)
+            self.all_anchors[anchor] = event
+        if self.parser.check(ScalarEvent):
+            node = self.compose_scalar_node()
+        elif self.parser.check(SequenceEvent):
+            node = self.compose_sequence_node()
+        elif self.parser.check(MappingEvent):
+            node = self.compose_mapping_node()
+        if anchor is not None:
+            self.complete_anchors[anchor] = node
+        return node
+
+    def compose_scalar_node(self):
+        event = self.parser.get()
+        return ScalarNode(event.tag, event.value,
+                event.start_marker, event.end_marker)
+
+    def compose_sequence_node(self):
+        start_event = self.parser.get()
+        value = []
+        while not self.parser.check(CollectionEndEvent):
+            value.append(self.compose_node())
+        end_event = self.parser.get()
+        return SequenceNode(start_event.tag, value,
+                start_event.start_marker, end_event.end_marker)
+
+    def compose_mapping_node(self):
+        start_event = self.parser.get()
+        value = []
+        while not self.parser.check(CollectionEndEvent):
+            item_key = self.compose_node()
+            item_value = self.compose_node()
+            value.append((item_key, item_value))
+        end_event = self.parser.get()
+        return MappingNode(start_event.tag, value,
+                start_event.start_marker, end_event.end_marker)
+
Index: /branches/pyyaml3000/lib/yaml/nodes.py
===================================================================
--- /branches/pyyaml3000/lib/yaml/nodes.py	(revision 53)
+++ /branches/pyyaml3000/lib/yaml/nodes.py	(revision 53)
@@ -0,0 +1,35 @@
+
+class Node:
+    def __init__(self, tag, value, start_marker, end_marker):
+        self.tag = tag
+        self.value = value
+        self.start_marker = start_marker
+        self.end_marker = end_marker
+    def __repr__(self):
+        value = self.value
+        if isinstance(value, list):
+            if len(value) == 0:
+                value = '<empty>'
+            elif len(value) == 1:
+                value = '<1 item>'
+            else:
+                value = '<%d items>' % len(value)
+        else:
+            if len(value) > 75:
+                value = repr(value[:70]+u' ... ')
+            else:
+                value = repr(value)
+        return '%s(tag=%r, value=%s)' % (self.__class__.__name__, self.tag, value)
+
+class ScalarNode(Node):
+    pass
+
+class CollectionNode(Node):
+    pass
+
+class SequenceNode(CollectionNode):
+    pass
+
+class MappingNode(CollectionNode):
+    pass
+
Index: /branches/pyyaml3000/lib/yaml/error.py
===================================================================
--- /branches/pyyaml3000/lib/yaml/error.py	(revision 52)
+++ /branches/pyyaml3000/lib/yaml/error.py	(revision 53)
@@ -64,14 +64,14 @@
         if self.context is not None:
             lines.append(self.context)
-            if self.context_marker is not None  \
-                and (self.problem is None or self.problem_marker is None
-                        or self.context_marker.name != self.problem_marker.name
-                        or self.context_marker.line != self.problem_marker.line
-                        or self.context_marker.column != self.problem_marker.column):
-                lines.append(str(self.context_marker))
+        if self.context_marker is not None  \
+            and (self.problem is None or self.problem_marker is None
+                    or self.context_marker.name != self.problem_marker.name
+                    or self.context_marker.line != self.problem_marker.line
+                    or self.context_marker.column != self.problem_marker.column):
+            lines.append(str(self.context_marker))
         if self.problem is not None:
             lines.append(self.problem)
-            if self.problem_marker is not None:
-                lines.append(str(self.problem_marker))
+        if self.problem_marker is not None:
+            lines.append(str(self.problem_marker))
         return '\n'.join(lines)
 
Index: /branches/pyyaml3000/lib/yaml/parser.py
===================================================================
--- /branches/pyyaml3000/lib/yaml/parser.py	(revision 52)
+++ /branches/pyyaml3000/lib/yaml/parser.py	(revision 53)
@@ -99,4 +99,13 @@
                     return True
         return False
+
+    def peek(self):
+        # Get the next event.
+        if self.current_event is None:
+            try:
+                self.current_event = self.event_generator.next()
+            except StopIteration:
+                pass
+        return self.current_event
 
     def get(self):
Index: /branches/pyyaml3000/lib/yaml/__init__.py
===================================================================
--- /branches/pyyaml3000/lib/yaml/__init__.py	(revision 51)
+++ /branches/pyyaml3000/lib/yaml/__init__.py	(revision 53)
@@ -1,6 +1,9 @@
 
+from error import YAMLError
 from reader import Reader
 from scanner import Scanner
 from parser import Parser
+from composer import Composer
+from resolver import Resolver
 
 from tokens import *
@@ -18,2 +21,31 @@
     return iter(parser)
 
+def compose(data, Reader=Reader, Scanner=Scanner, Parser=Parser,
+        Composer=Composer):
+    reader = Reader(data)
+    scanner = Scanner(reader)
+    parser = Parser(scanner)
+    composer = Composer(parser)
+    return iter(composer)
+
+def compose_document(*args, **kwds):
+    try:
+        return compose(*args, **kwds).next()
+    except StopIteration:
+        return None
+
+def resolve(data, Reader=Reader, Scanner=Scanner, Parser=Parser,
+        Composer=Composer, Resolver=Resolver):
+    reader = Reader(data)
+    scanner = Scanner(reader)
+    parser = Parser(scanner)
+    composer = Composer(parser)
+    resolver = Resolver(composer)
+    return iter(resolver)
+
+def resolve_document(*args, **kwds):
+    try:
+        return resolve(*args, **kwds).next()
+    except StopIteration:
+        return None
+
Index: /branches/pyyaml3000/lib/yaml/resolver.py
===================================================================
--- /branches/pyyaml3000/lib/yaml/resolver.py	(revision 53)
+++ /branches/pyyaml3000/lib/yaml/resolver.py	(revision 53)
@@ -0,0 +1,68 @@
+
+from nodes import *
+
+class BaseResolver:
+
+    DEFAULT_SCALAR_TAG = u'tag:yaml.org,2002:str'
+    DEFAULT_SEQUENCE_TAG = u'tag:yaml.org,2002:seq'
+    DEFAULT_MAPPING_TAG = u'tag:yaml.org,2002:map'
+
+    def __init__(self, composer):
+        self.composer = composer
+        self.resolved_nodes = {}
+
+    def check(self):
+        # If there are more documents available?
+        return self.composer.check()
+
+    def get(self):
+        # Resolve and return the root node of the next document.
+        if self.composer.check():
+            return self.resolve_document(self.composer.get())
+
+    def __iter__(self):
+        # Iterator protocol.
+        while self.composer.check():
+            yield self.resolve_document(self.composer.get())
+
+    def resolve_document(self, node):
+        self.resolve_node([], node)
+        return node
+        self.resolved_nodes = {}
+
+    def resolve_node(self, path, node):
+        if node in self.resolved_nodes:
+            return
+        self.resolved_nodes[node] = None
+        if isinstance(node, ScalarNode):
+            self.resolve_scalar(path, node)
+        elif isinstance(node, SequenceNode):
+            self.resolve_sequence(path, node)
+            for index in len(node.value):
+                self.resolve_node(path+[node, index], node.value[index])
+        elif isinstance(node, MappingNode):
+            self.resolve_mapping(path, node)
+            for key, value in node.value:
+                self.resolve_node(path+[node, None], key)
+                self.resolve_node(path+[node, key], value)
+
+    def resolve_scalar(self, node):
+        if node.tag is None:
+            node.tag = self.detect_scalar(node.value)
+        if node.tag is None or node.tag == u'!':
+            node.tag = self.DEFAULT_SCALAR_TAG
+
+    def resolve_sequence(self, node):
+        if node.tag is None or node.tag == u'!':
+            node.tag = self.DEFAULT_SEQUENCE_TAG
+
+    def resolve_mapping(self, node):
+        if node.tag is None or node.tag == u'!':
+            node.tag = self.DEFAULT_MAPPING_TAG
+
+    def detect_scalar(self, value):
+        return None
+
+class Resolver(BaseResolver):
+    pass
+
Index: /branches/pyyaml3000/lib/yaml/events.py
===================================================================
--- /branches/pyyaml3000/lib/yaml/events.py	(revision 51)
+++ /branches/pyyaml3000/lib/yaml/events.py	(revision 53)
@@ -13,15 +13,11 @@
 
 class NodeEvent(Event):
-    def __init__(self, anchor, tag, start_marker, end_marker):
+    def __init__(self, anchor, start_marker, end_marker):
         self.anchor = anchor
-        self.tag = tag
         self.start_marker = start_marker
         self.end_marker = end_marker
 
 class AliasEvent(NodeEvent):
-    def __init__(self, name, start_marker, end_marker):
-        self.name = name
-        self.start_marker = start_marker
-        self.end_marker = end_marker
+    pass
 
 class ScalarEvent(NodeEvent):
@@ -34,5 +30,9 @@
 
 class CollectionEvent(NodeEvent):
-    pass
+    def __init__(self, anchor, tag, start_marker, end_marker):
+        self.anchor = anchor
+        self.tag = tag
+        self.start_marker = start_marker
+        self.end_marker = end_marker
 
 class SequenceEvent(CollectionEvent):
Index: /branches/pyyaml3000/tests/test_errors.py
===================================================================
--- /branches/pyyaml3000/tests/test_errors.py	(revision 52)
+++ /branches/pyyaml3000/tests/test_errors.py	(revision 53)
@@ -6,4 +6,6 @@
 from yaml.scanner import *
 from yaml.parser import *
+from yaml.composer import *
+from yaml.resolver import *
 
 class TestErrors(test_appliance.TestAppliance):
@@ -22,8 +24,11 @@
             scanner = Scanner(reader)
             parser = Parser(scanner)
-            return list(parser)
+            composer = Composer(parser)
+            resolver = Resolver(composer)
+            return list(composer)
         except YAMLError, exc:
         #except ScannerError, exc:
         #except ParserError, exc:
+        #except ComposerError, exc:
             #print '.'*70
             #print "%s:" % exc.__class__.__name__, exc
@@ -35,8 +40,11 @@
             scanner = Scanner(reader)
             parser = Parser(scanner)
-            return list(parser)
+            composer = Composer(parser)
+            resolver = Resolver(composer)
+            return list(composer)
         except YAMLError, exc:
         #except ScannerError, exc:
         #except ParserError, exc:
+        #except ComposerError, exc:
             #print '.'*70
             #print "%s:" % filename
Index: /branches/pyyaml3000/tests/test_structure.py
===================================================================
--- /branches/pyyaml3000/tests/test_structure.py	(revision 51)
+++ /branches/pyyaml3000/tests/test_structure.py	(revision 53)
@@ -124,5 +124,5 @@
             self.failUnlessEqual(event1.__class__, event2.__class__)
             if isinstance(event1, AliasEvent):
-                self.failUnlessEqual(event1.name, event2.name)
+                self.failUnlessEqual(event1.anchor, event2.anchor)
             elif isinstance(event1, ScalarEvent):
                 self.failUnlessEqual(event1.anchor, event2.anchor)
Index: /branches/pyyaml3000/tests/data/recursive-anchor.error-message
===================================================================
--- /branches/pyyaml3000/tests/data/recursive-anchor.error-message	(revision 53)
+++ /branches/pyyaml3000/tests/data/recursive-anchor.error-message	(revision 53)
@@ -0,0 +1,4 @@
+- &foo [1
+    2,
+    3,
+    *foo]
Index: /branches/pyyaml3000/tests/data/undefined-anchor.error-message
===================================================================
--- /branches/pyyaml3000/tests/data/undefined-anchor.error-message	(revision 53)
+++ /branches/pyyaml3000/tests/data/undefined-anchor.error-message	(revision 53)
@@ -0,0 +1,3 @@
+- foo
+- &bar baz
+- *bat
Index: /branches/pyyaml3000/tests/data/duplicate-anchor-2.error-message
===================================================================
--- /branches/pyyaml3000/tests/data/duplicate-anchor-2.error-message	(revision 53)
+++ /branches/pyyaml3000/tests/data/duplicate-anchor-2.error-message	(revision 53)
@@ -0,0 +1,1 @@
+&foo [1, 2, 3, &foo 4]
Index: /branches/pyyaml3000/tests/data/duplicate-anchor-1.error-message
===================================================================
--- /branches/pyyaml3000/tests/data/duplicate-anchor-1.error-message	(revision 53)
+++ /branches/pyyaml3000/tests/data/duplicate-anchor-1.error-message	(revision 53)
@@ -0,0 +1,3 @@
+- &foo bar
+- &bar bar
+- &foo bar
