From 55c8486ee3313df6ca479143bb962c2138398fc3 Mon Sep 17 00:00:00 2001 From: rtweeks21 Date: Sat, 11 Sep 2010 22:11:02 +0000 Subject: [PATCH] Reintegrated the event-api branch (second iteration) into the rtweeks21-staging branch. --- include/aliasmanager.h | 38 +++ include/anchor.h | 14 + include/emitfromevents.h | 43 +++ include/eventhandler.h | 34 +++ include/graphbuilder.h | 117 ++++++++ include/node.h | 26 +- include/nodeproperties.h | 14 + include/parser.h | 29 +- src/aliascontent.cpp | 22 +- src/aliascontent.h | 6 +- src/aliasmanager.cpp | 45 +++ src/collectionstack.h | 33 +++ src/content.cpp | 21 ++ src/content.h | 27 +- src/{parserstate.cpp => directives.cpp} | 10 +- src/directives.h | 27 ++ src/emitfromevents.cpp | 106 +++++++ src/graphbuilderadapter.cpp | 96 ++++++ src/graphbuilderadapter.h | 67 +++++ src/map.cpp | 165 +---------- src/map.h | 15 +- src/node.cpp | 222 ++++++-------- src/nodebuilder.cpp | 142 +++++++++ src/nodebuilder.h | 59 ++++ src/parser.cpp | 91 +++--- src/parserstate.h | 37 --- src/ptr_stack.h | 37 +++ src/scalar.cpp | 26 +- src/scalar.h | 7 +- src/scanner.cpp | 36 +-- src/scanner.h | 6 - src/sequence.cpp | 102 +------ src/sequence.h | 10 +- src/singledocparser.cpp | 369 ++++++++++++++++++++++++ src/singledocparser.h | 63 ++++ src/tag.cpp | 10 +- src/tag.h | 4 +- util/parse.cpp | 16 + 38 files changed, 1575 insertions(+), 617 deletions(-) create mode 100644 include/aliasmanager.h create mode 100644 include/anchor.h create mode 100644 include/emitfromevents.h create mode 100644 include/eventhandler.h create mode 100644 include/graphbuilder.h create mode 100644 include/nodeproperties.h create mode 100644 src/aliasmanager.cpp create mode 100644 src/collectionstack.h create mode 100644 src/content.cpp rename src/{parserstate.cpp => directives.cpp} (69%) create mode 100644 src/directives.h create mode 100644 src/emitfromevents.cpp create mode 100644 src/graphbuilderadapter.cpp create mode 100644 src/graphbuilderadapter.h create mode 100644 src/nodebuilder.cpp create mode 100644 src/nodebuilder.h delete mode 100644 src/parserstate.h create mode 100644 src/ptr_stack.h create mode 100644 src/singledocparser.cpp create mode 100644 src/singledocparser.h diff --git a/include/aliasmanager.h b/include/aliasmanager.h new file mode 100644 index 0000000..caa6648 --- /dev/null +++ b/include/aliasmanager.h @@ -0,0 +1,38 @@ +#pragma once + +#ifndef ALIASMANAGER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define ALIASMANAGER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#include "anchor.h" +#include + +namespace YAML +{ + class Node; + + class AliasManager + { + public: + AliasManager(); + + void RegisterReference(const Node& node); + + const Node *LookupReference(const Node& node) const; + anchor_t LookupAnchor(const Node& node) const; + + private: + const Node *_LookupReference(const Node& oldIdentity) const; + anchor_t _CreateNewAnchor(); + + private: + typedef std::map NodeByNode; + NodeByNode m_newIdentityByOldIdentity; + + typedef std::map AnchorByIdentity; + AnchorByIdentity m_anchorByIdentity; + + anchor_t m_curAnchor; + }; +} + +#endif // ALIASMANAGER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/anchor.h b/include/anchor.h new file mode 100644 index 0000000..c7b283a --- /dev/null +++ b/include/anchor.h @@ -0,0 +1,14 @@ +#pragma once + +#ifndef ANCHOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define ANCHOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#include + +namespace YAML +{ + typedef std::size_t anchor_t; + const anchor_t NullAnchor = 0; +} + +#endif // ANCHOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/emitfromevents.h b/include/emitfromevents.h new file mode 100644 index 0000000..6c9b241 --- /dev/null +++ b/include/emitfromevents.h @@ -0,0 +1,43 @@ +#pragma once + +#ifndef EMITFROMEVENTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define EMITFROMEVENTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#include "eventhandler.h" +#include + +namespace YAML +{ + class Emitter; + + class EmitFromEvents: public EventHandler + { + public: + EmitFromEvents(Emitter& emitter); + + virtual void OnDocumentStart(const Mark& mark); + virtual void OnDocumentEnd(); + + virtual void OnNull(const std::string& tag, anchor_t anchor); + virtual void OnAlias(const Mark& mark, anchor_t anchor); + virtual void OnScalar(const Mark& mark, const std::string& tag, anchor_t anchor, const std::string& value); + + virtual void OnSequenceStart(const Mark& mark, const std::string& tag, anchor_t anchor); + virtual void OnSequenceEnd(); + + virtual void OnMapStart(const Mark& mark, const std::string& tag, anchor_t anchor); + virtual void OnMapEnd(); + + private: + void BeginNode(); + void EmitProps(const std::string& tag, anchor_t anchor); + + private: + Emitter& m_emitter; + + struct State { enum value { WaitingForSequenceEntry, WaitingForKey, WaitingForValue }; }; + std::stack m_stateStack; + }; +} + +#endif // EMITFROMEVENTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/eventhandler.h b/include/eventhandler.h new file mode 100644 index 0000000..f76b946 --- /dev/null +++ b/include/eventhandler.h @@ -0,0 +1,34 @@ +#pragma once + +#ifndef EVENTHANDLER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define EVENTHANDLER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#include "anchor.h" +#include + +namespace YAML +{ + struct Mark; + + class EventHandler + { + public: + virtual ~EventHandler() {} + + virtual void OnDocumentStart(const Mark& mark) = 0; + virtual void OnDocumentEnd() = 0; + + virtual void OnNull(const std::string& tag, anchor_t anchor) = 0; + virtual void OnAlias(const Mark& mark, anchor_t anchor) = 0; + virtual void OnScalar(const Mark& mark, const std::string& tag, anchor_t anchor, const std::string& value) = 0; + + virtual void OnSequenceStart(const Mark& mark, const std::string& tag, anchor_t anchor) = 0; + virtual void OnSequenceEnd() = 0; + + virtual void OnMapStart(const Mark& mark, const std::string& tag, anchor_t anchor) = 0; + virtual void OnMapEnd() = 0; + }; +} + +#endif // EVENTHANDLER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + diff --git a/include/graphbuilder.h b/include/graphbuilder.h new file mode 100644 index 0000000..79e8a8a --- /dev/null +++ b/include/graphbuilder.h @@ -0,0 +1,117 @@ +#ifndef GRAPHBUILDER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define GRAPHBUILDER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#include + +namespace YAML +{ + struct Mark; + + // GraphBuilderInterface + // . Abstraction of node creation + // . pParentNode is always NULL or the return value of one of the NewXXX() + // functions. + class GraphBuilderInterface + { + public: + // Create and return a new node with a null value. + virtual void *NewNull(const std::string& tag, void *pParentNode) = 0; + + // Create and return a new node with the given tag and value. + virtual void *NewScalar(const Mark& mark, const std::string& tag, void *pParentNode, const std::string& value) = 0; + + // Create and return a new sequence node + virtual void *NewSequence(const Mark& mark, const std::string& tag, void *pParentNode) = 0; + // Add pNode to pSequence. pNode was created with one of the NewXxx() + // functions and pSequence with NewSequence(). + virtual void AppendToSequence(void *pSequence, void *pNode) = 0; + // Note that no moew entries will be added to pSequence + virtual void SequenceComplete(void *pSequence) {(void)pSequence;} + + // Create and return a new map node + virtual void *NewMap(const Mark& mark, const std::string& tag, void *pParentNode) = 0; + // Add the pKeyNode => pValueNode mapping to pMap. pKeyNode and pValueNode + // were created with one of the NewXxx() methods and pMap with NewMap(). + virtual void AssignInMap(void *pMap, void *pKeyNode, void *pValueNode) = 0; + // Note that no more assignments will be made in pMap + virtual void MapComplete(void *pMap) {(void)pMap;} + + // Return the node that should be used in place of an alias referencing + // pNode (pNode by default) + virtual void *AnchorReference(const Mark& mark, void *pNode) {(void)mark; return pNode;} + }; + + // Typesafe wrapper for GraphBuilderInterface. Assumes that Impl defines + // Node, Sequence, and Map types. Sequence and Map must derive from Node + // (unless Node is defined as void). Impl must also implement function with + // all of the same names as the virtual functions in GraphBuilderInterface + // -- including the ones with default implementations -- but with the + // prototypes changed to accept an explicit Node*, Sequence*, or Map* where + // appropriate. + template + class GraphBuilder : public GraphBuilderInterface + { + public: + typedef typename Impl::Node Node; + typedef typename Impl::Sequence Sequence; + typedef typename Impl::Map Map; + + GraphBuilder(Impl& impl) : m_impl(impl) + { + Map* pMap = NULL; + Sequence* pSeq = NULL; + Node* pNode = NULL; + + // Type consistency checks + pNode = pMap; + pNode = pSeq; + } + + GraphBuilderInterface& AsBuilderInterface() {return *this;} + + virtual void *NewNull(const std::string& tag, void* pParentNode) { + return CheckType(m_impl.NewNull(tag, AsNode(pParentNode))); + } + + virtual void *NewScalar(const Mark& mark, const std::string& tag, void *pParentNode, const std::string& value) { + return CheckType(m_impl.NewScalar(mark, tag, AsNode(pParentNode), value)); + } + + virtual void *NewSequence(const Mark& mark, const std::string& tag, void *pParentNode) { + return CheckType(m_impl.NewSequence(mark, tag, AsNode(pParentNode))); + } + virtual void AppendToSequence(void *pSequence, void *pNode) { + m_impl.AppendToSequence(AsSequence(pSequence), AsNode(pNode)); + } + virtual void SequenceComplete(void *pSequence) { + m_impl.SequenceComplete(AsSequence(pSequence)); + } + + virtual void *NewMap(const Mark& mark, const std::string& tag, void *pParentNode) { + return CheckType(m_impl.NewMap(mark, tag, AsNode(pParentNode))); + } + virtual void AssignInMap(void *pMap, void *pKeyNode, void *pValueNode) { + m_impl.AssignInMap(AsMap(pMap), AsNode(pKeyNode), AsNode(pValueNode)); + } + virtual void MapComplete(void *pMap) { + m_impl.MapComplete(AsMap(pMap)); + } + + virtual void *AnchorReference(const Mark& mark, void *pNode) { + return CheckType(m_impl.AnchorReference(mark, AsNode(pNode))); + } + + private: + Impl& m_impl; + + // Static check for pointer to T + template + static T* CheckType(U* p) {return p;} + + static Node *AsNode(void *pNode) {return static_cast(pNode);} + static Sequence *AsSequence(void *pSeq) {return static_cast(pSeq);} + static Map *AsMap(void *pMap) {return static_cast(pMap);} + }; +} + +#endif // GRAPHBUILDER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/node.h b/include/node.h index d3cf791..2250f8e 100644 --- a/include/node.h +++ b/include/node.h @@ -17,10 +17,12 @@ namespace YAML { + class AliasManager; class Content; class Scanner; class Emitter; - struct ParserState; + class EventHandler; + struct NodeProperties; enum CONTENT_TYPE { CT_NONE, CT_SCALAR, CT_SEQUENCE, CT_MAP }; @@ -32,7 +34,16 @@ namespace YAML void Clear(); std::auto_ptr Clone() const; - void Parse(Scanner *pScanner, ParserState& state); + void EmitEvents(EventHandler& eventHandler) const; + void EmitEvents(AliasManager& am, EventHandler& eventHandler) const; + + void Init(CONTENT_TYPE type, const Mark& mark, const std::string& tag); + void InitNull(const std::string& tag); + void InitAlias(const Mark& mark, const Node& identity); + + void SetData(const std::string& data); + void Append(std::auto_ptr pNode); + void Insert(std::auto_ptr pKey, std::auto_ptr pValue); CONTENT_TYPE GetType() const; @@ -97,19 +108,10 @@ namespace YAML template const Node *FindValueForKey(const T& key) const; - - // helper for cloning - Node(const Mark& mark, const std::string& anchor, const std::string& tag, const Content *pContent); - - // helpers for parsing - void ParseHeader(Scanner *pScanner, ParserState& state); - void ParseTag(Scanner *pScanner, ParserState& state); - void ParseAnchor(Scanner *pScanner, ParserState& state); - void ParseAlias(Scanner *pScanner, ParserState& state); private: Mark m_mark; - std::string m_anchor, m_tag; + std::string m_tag; Content *m_pContent; bool m_alias; const Node *m_pIdentity; diff --git a/include/nodeproperties.h b/include/nodeproperties.h new file mode 100644 index 0000000..ae435d5 --- /dev/null +++ b/include/nodeproperties.h @@ -0,0 +1,14 @@ +#pragma once + +#ifndef NODEPROPERTIES_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define NODEPROPERTIES_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +namespace YAML +{ + struct NodeProperties { + std::string tag; + std::string anchor; + }; +} + +#endif // NODEPROPERTIES_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/parser.h b/include/parser.h index fec63ea..160c40c 100644 --- a/include/parser.h +++ b/include/parser.h @@ -4,19 +4,20 @@ #define PARSER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 -#include "node.h" +#include "graphbuilder.h" #include "noncopyable.h" #include -#include -#include -#include #include namespace YAML { - class Scanner; - struct ParserState; + struct Directives; + struct Mark; struct Token; + class EventHandler; + class GraphBuilderInterface; + class Node; + class Scanner; class Parser: private noncopyable { @@ -28,6 +29,18 @@ namespace YAML operator bool() const; void Load(std::istream& in); + bool HandleNextDocument(EventHandler& eventHandler); + + void *BuildNextDocumentGraph(GraphBuilderInterface& graphBuilder); + template + typename Builder::Node *BuildNextDocumentGraph(Builder& graphBuilder) + { + GraphBuilder wrapper(graphBuilder); // Must be lvalue to make C++ happy + return static_cast( + BuildNextDocumentGraph(wrapper.AsBuilderInterface()) + ); + } + bool GetNextDocument(Node& document); void PrintTokens(std::ostream& out); @@ -36,10 +49,10 @@ namespace YAML void HandleDirective(const Token& token); void HandleYamlDirective(const Token& token); void HandleTagDirective(const Token& token); - + private: std::auto_ptr m_pScanner; - std::auto_ptr m_pState; + std::auto_ptr m_pDirectives; }; } diff --git a/src/aliascontent.cpp b/src/aliascontent.cpp index 111938b..e59d5ad 100644 --- a/src/aliascontent.cpp +++ b/src/aliascontent.cpp @@ -2,25 +2,10 @@ namespace YAML { - AliasContent::AliasContent(Content* pNodeContent) - : m_pRef(pNodeContent) + AliasContent::AliasContent(Content* pNodeContent): m_pRef(pNodeContent) { } - Content *AliasContent::Clone() const - { - return 0; // TODO: how to clone an alias? - } - - void AliasContent::Parse(Scanner * /*pScanner*/, ParserState& /*state*/) - { - } - - void AliasContent::Write(Emitter&) const - { - // no content (just an alias) - } - bool AliasContent::GetBegin(std::vector ::const_iterator& i) const { return m_pRef->GetBegin(i); @@ -71,6 +56,11 @@ namespace YAML return m_pRef->GetScalar(scalar); } + void AliasContent::EmitEvents(AliasManager& am, EventHandler& eventHandler, const Mark& mark, const std::string& tag, anchor_t anchor) const + { + m_pRef->EmitEvents(am, eventHandler, mark, tag, anchor); + } + int AliasContent::Compare(Content *pContent) { return m_pRef->Compare(pContent); diff --git a/src/aliascontent.h b/src/aliascontent.h index a69c28b..d5893b4 100644 --- a/src/aliascontent.h +++ b/src/aliascontent.h @@ -12,11 +12,6 @@ namespace YAML { public: AliasContent(Content *pNodeContent); - - virtual Content *Clone() const; - - virtual void Parse(Scanner* pScanner, ParserState& state); - virtual void Write(Emitter&) const; virtual bool GetBegin(std::vector ::const_iterator&) const; virtual bool GetBegin(std::map ::const_iterator&) const; @@ -29,6 +24,7 @@ namespace YAML virtual bool IsSequence() const; virtual bool GetScalar(std::string& s) const; + virtual void EmitEvents(AliasManager& am, EventHandler& eventHandler, const Mark& mark, const std::string& tag, anchor_t anchor) const; virtual int Compare(Content *); virtual int Compare(Scalar *); diff --git a/src/aliasmanager.cpp b/src/aliasmanager.cpp new file mode 100644 index 0000000..61443a9 --- /dev/null +++ b/src/aliasmanager.cpp @@ -0,0 +1,45 @@ +#include "aliasmanager.h" +#include "node.h" +#include +#include + +namespace YAML +{ + AliasManager::AliasManager(): m_curAnchor(0) + { + } + + void AliasManager::RegisterReference(const Node& node) + { + const Node *pIdentity = node.Identity(); + m_newIdentityByOldIdentity.insert(std::make_pair(pIdentity, &node)); + m_anchorByIdentity.insert(std::make_pair(&node, _CreateNewAnchor())); + } + + const Node *AliasManager::LookupReference(const Node& node) const + { + const Node *pIdentity = node.Identity(); + return _LookupReference(*pIdentity); + } + + anchor_t AliasManager::LookupAnchor(const Node& node) const + { + AnchorByIdentity::const_iterator it = m_anchorByIdentity.find(&node); + if(it == m_anchorByIdentity.end()) + assert(false); // TODO: throw + return it->second; + } + + const Node *AliasManager::_LookupReference(const Node& oldIdentity) const + { + NodeByNode::const_iterator it = m_newIdentityByOldIdentity.find(&oldIdentity); + if(it == m_newIdentityByOldIdentity.end()) + return 0; + return it->second; + } + + anchor_t AliasManager::_CreateNewAnchor() + { + return ++m_curAnchor; + } +} diff --git a/src/collectionstack.h b/src/collectionstack.h new file mode 100644 index 0000000..fd3b885 --- /dev/null +++ b/src/collectionstack.h @@ -0,0 +1,33 @@ +#pragma once + +#ifndef COLLECTIONSTACK_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define COLLECTIONSTACK_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include +#include + +namespace YAML +{ + struct CollectionType { + enum value { None, BlockMap, BlockSeq, FlowMap, FlowSeq, CompactMap }; + }; + + class CollectionStack + { + public: + CollectionType::value GetCurCollectionType() const { + if(collectionStack.empty()) + return CollectionType::None; + return collectionStack.top(); + } + + void PushCollectionType(CollectionType::value type) { collectionStack.push(type); } + void PopCollectionType(CollectionType::value type) { assert(type == GetCurCollectionType()); collectionStack.pop(); } + + private: + std::stack collectionStack; + }; +} + +#endif // COLLECTIONSTACK_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/content.cpp b/src/content.cpp new file mode 100644 index 0000000..208e988 --- /dev/null +++ b/src/content.cpp @@ -0,0 +1,21 @@ +#include "content.h" +#include "node.h" +#include + +namespace YAML +{ + void Content::SetData(const std::string&) + { + assert(false); // TODO: throw + } + + void Content::Append(std::auto_ptr) + { + assert(false); // TODO: throw + } + + void Content::Insert(std::auto_ptr, std::auto_ptr) + { + assert(false); // TODO: throw + } +} diff --git a/src/content.h b/src/content.h index d6e0e8c..2c8cc8a 100644 --- a/src/content.h +++ b/src/content.h @@ -4,32 +4,30 @@ #define CONTENT_H_62B23520_7C8E_11DE_8A39_0800200C9A66 -#include -#include -#include "parserstate.h" +#include "anchor.h" #include "exceptions.h" #include "ltnode.h" +#include +#include +#include namespace YAML { - class Scanner; - class Parser; + struct Mark; + struct NodeProperties; + class AliasManager; + class EventHandler; + class Map; class Node; class Scalar; + class Scanner; class Sequence; - class Map; - class Emitter; class Content { public: Content() {} virtual ~Content() {} - - virtual Content *Clone() const = 0; - - virtual void Parse(Scanner *pScanner, ParserState& state) = 0; - virtual void Write(Emitter& out) const = 0; virtual bool GetBegin(std::vector ::const_iterator&) const { return false; } virtual bool GetBegin(std::map ::const_iterator&) const { return false; } @@ -40,6 +38,11 @@ namespace YAML virtual bool IsScalar() const { return false; } virtual bool IsMap() const { return false; } virtual bool IsSequence() const { return false; } + + virtual void SetData(const std::string& data); + virtual void Append(std::auto_ptr pNode); + virtual void Insert(std::auto_ptr pKey, std::auto_ptr pValue); + virtual void EmitEvents(AliasManager& am, EventHandler& eventHandler, const Mark& mark, const std::string& tag, anchor_t anchor) const = 0; // extraction virtual bool GetScalar(std::string&) const { return false; } diff --git a/src/parserstate.cpp b/src/directives.cpp similarity index 69% rename from src/parserstate.cpp rename to src/directives.cpp index ebd6609..faf1483 100644 --- a/src/parserstate.cpp +++ b/src/directives.cpp @@ -1,16 +1,16 @@ -#include "parserstate.h" +#include "directives.h" namespace YAML { - ParserState::ParserState() + Directives::Directives() { // version version.isDefault = true; version.major = 1; version.minor = 2; } - - const std::string ParserState::TranslateTagHandle(const std::string& handle) const + + const std::string Directives::TranslateTagHandle(const std::string& handle) const { std::map ::const_iterator it = tags.find(handle); if(it == tags.end()) { @@ -18,7 +18,7 @@ namespace YAML return "tag:yaml.org,2002:"; return handle; } - + return it->second; } } diff --git a/src/directives.h b/src/directives.h new file mode 100644 index 0000000..e063faa --- /dev/null +++ b/src/directives.h @@ -0,0 +1,27 @@ +#pragma once + +#ifndef DIRECTIVES_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define DIRECTIVES_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include +#include + +namespace YAML +{ + struct Version { + bool isDefault; + int major, minor; + }; + + struct Directives { + Directives(); + + const std::string TranslateTagHandle(const std::string& handle) const; + + Version version; + std::map tags; + }; +} + +#endif // DIRECTIVES_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/emitfromevents.cpp b/src/emitfromevents.cpp new file mode 100644 index 0000000..7cbf42c --- /dev/null +++ b/src/emitfromevents.cpp @@ -0,0 +1,106 @@ +#include "emitfromevents.h" +#include "emitter.h" +#include "null.h" +#include +#include + +namespace { + std::string ToString(YAML::anchor_t anchor) { + std::stringstream stream; + stream << anchor; + return stream.str(); + } +} + +namespace YAML +{ + EmitFromEvents::EmitFromEvents(Emitter& emitter): m_emitter(emitter) + { + } + + void EmitFromEvents::OnDocumentStart(const Mark&) + { + } + + void EmitFromEvents::OnDocumentEnd() + { + } + + void EmitFromEvents::OnNull(const std::string& tag, anchor_t anchor) + { + BeginNode(); + EmitProps(tag, anchor); + if(tag.empty()) + m_emitter << Null; + } + + void EmitFromEvents::OnAlias(const Mark&, anchor_t anchor) + { + BeginNode(); + m_emitter << Alias(ToString(anchor)); + } + + void EmitFromEvents::OnScalar(const Mark&, const std::string& tag, anchor_t anchor, const std::string& value) + { + BeginNode(); + EmitProps(tag, anchor); + m_emitter << value; + } + + void EmitFromEvents::OnSequenceStart(const Mark&, const std::string& tag, anchor_t anchor) + { + BeginNode(); + EmitProps(tag, anchor); + m_emitter << BeginSeq; + m_stateStack.push(State::WaitingForSequenceEntry); + } + + void EmitFromEvents::OnSequenceEnd() + { + m_emitter << EndSeq; + assert(m_stateStack.top() == State::WaitingForSequenceEntry); + m_stateStack.pop(); + } + + void EmitFromEvents::OnMapStart(const Mark&, const std::string& tag, anchor_t anchor) + { + BeginNode(); + EmitProps(tag, anchor); + m_emitter << BeginMap; + m_stateStack.push(State::WaitingForKey); + } + + void EmitFromEvents::OnMapEnd() + { + m_emitter << EndMap; + assert(m_stateStack.top() == State::WaitingForKey); + m_stateStack.pop(); + } + + void EmitFromEvents::BeginNode() + { + if(m_stateStack.empty()) + return; + + switch(m_stateStack.top()) { + case State::WaitingForKey: + m_emitter << Key; + m_stateStack.top() = State::WaitingForValue; + break; + case State::WaitingForValue: + m_emitter << Value; + m_stateStack.top() = State::WaitingForKey; + break; + default: + break; + } + } + + void EmitFromEvents::EmitProps(const std::string& tag, anchor_t anchor) + { + if(!tag.empty()) + m_emitter << VerbatimTag(tag); + if(anchor) + m_emitter << Anchor(ToString(anchor)); + } +} diff --git a/src/graphbuilderadapter.cpp b/src/graphbuilderadapter.cpp new file mode 100644 index 0000000..74b7e22 --- /dev/null +++ b/src/graphbuilderadapter.cpp @@ -0,0 +1,96 @@ +#include "graphbuilderadapter.h" + +namespace YAML +{ + int GraphBuilderAdapter::ContainerFrame::sequenceMarker; + + void GraphBuilderAdapter::OnNull(const std::string& tag, anchor_t anchor) + { + void *pParent = GetCurrentParent(); + void *pNode = m_builder.NewNull(tag, pParent); + RegisterAnchor(anchor, pNode); + + DispositionNode(pNode); + } + + void GraphBuilderAdapter::OnAlias(const Mark& mark, anchor_t anchor) + { + void *pReffedNode = m_anchors[anchor]; + DispositionNode(m_builder.AnchorReference(mark, pReffedNode)); + } + + void GraphBuilderAdapter::OnScalar(const Mark& mark, const std::string& tag, anchor_t anchor, const std::string& value) + { + void *pParent = GetCurrentParent(); + void *pNode = m_builder.NewScalar(mark, tag, pParent, value); + RegisterAnchor(anchor, pNode); + + DispositionNode(pNode); + } + + void GraphBuilderAdapter::OnSequenceStart(const Mark& mark, const std::string& tag, anchor_t anchor) + { + void *pNode = m_builder.NewSequence(mark, tag, GetCurrentParent()); + m_containers.push(ContainerFrame(pNode)); + RegisterAnchor(anchor, pNode); + } + + void GraphBuilderAdapter::OnSequenceEnd() + { + void *pSequence = m_containers.top().pContainer; + m_containers.pop(); + + DispositionNode(pSequence); + } + + void GraphBuilderAdapter::OnMapStart(const Mark& mark, const std::string& tag, anchor_t anchor) + { + void *pNode = m_builder.NewMap(mark, tag, GetCurrentParent()); + m_containers.push(ContainerFrame(pNode, m_pKeyNode)); + m_pKeyNode = NULL; + RegisterAnchor(anchor, pNode); + } + + void GraphBuilderAdapter::OnMapEnd() + { + void *pMap = m_containers.top().pContainer; + m_pKeyNode = m_containers.top().pPrevKeyNode; + m_containers.pop(); + DispositionNode(pMap); + } + + void *GraphBuilderAdapter::GetCurrentParent() const + { + if (m_containers.empty()) { + return NULL; + } + return m_containers.top().pContainer; + } + + void GraphBuilderAdapter::RegisterAnchor(anchor_t anchor, void *pNode) + { + if (anchor) { + m_anchors[anchor] = pNode; + } + } + + void GraphBuilderAdapter::DispositionNode(void *pNode) + { + if (m_containers.empty()) { + m_pRootNode = pNode; + return; + } + + void *pContainer = m_containers.top().pContainer; + if (m_containers.top().isMap()) { + if (m_pKeyNode) { + m_builder.AssignInMap(pContainer, m_pKeyNode, pNode); + m_pKeyNode = NULL; + } else { + m_pKeyNode = pNode; + } + } else { + m_builder.AppendToSequence(pContainer, pNode); + } + } +} diff --git a/src/graphbuilderadapter.h b/src/graphbuilderadapter.h new file mode 100644 index 0000000..5b4aa3a --- /dev/null +++ b/src/graphbuilderadapter.h @@ -0,0 +1,67 @@ +#ifndef GRAPHBUILDERADAPTER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define GRAPHBUILDERADAPTER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#include +#include +#include "eventhandler.h" +#include "graphbuilder.h" + +namespace YAML +{ + class GraphBuilderAdapter : public EventHandler + { + public: + GraphBuilderAdapter(GraphBuilderInterface& builder) + : m_builder(builder), m_pRootNode(NULL), m_pKeyNode(NULL) + { + } + + virtual void OnDocumentStart(const Mark& mark) {(void)mark;} + virtual void OnDocumentEnd() {} + + virtual void OnNull(const std::string& tag, anchor_t anchor); + virtual void OnAlias(const Mark& mark, anchor_t anchor); + virtual void OnScalar(const Mark& mark, const std::string& tag, anchor_t anchor, const std::string& value); + + virtual void OnSequenceStart(const Mark& mark, const std::string& tag, anchor_t anchor); + virtual void OnSequenceEnd(); + + virtual void OnMapStart(const Mark& mark, const std::string& tag, anchor_t anchor); + virtual void OnMapEnd(); + + void *RootNode() const {return m_pRootNode;} + + private: + struct ContainerFrame + { + ContainerFrame(void *pSequence) + : pContainer(pSequence), pPrevKeyNode(&sequenceMarker) + {} + ContainerFrame(void *pMap, void* pPrevKeyNode) + : pContainer(pMap), pPrevKeyNode(pPrevKeyNode) + {} + + void *pContainer; + void *pPrevKeyNode; + + bool isMap() const {return pPrevKeyNode != &sequenceMarker;} + + private: + static int sequenceMarker; + }; + typedef std::stack ContainerStack; + typedef std::map AnchorMap; + + GraphBuilderInterface& m_builder; + ContainerStack m_containers; + AnchorMap m_anchors; + void *m_pRootNode; + void *m_pKeyNode; + + void *GetCurrentParent() const; + void RegisterAnchor(anchor_t anchor, void *pNode); + void DispositionNode(void *pNode); + }; +} + +#endif // GRAPHBUILDERADAPTER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/map.cpp b/src/map.cpp index 2db38b9..d9e799c 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1,24 +1,13 @@ #include "map.h" #include "node.h" -#include "scanner.h" -#include "token.h" +#include "eventhandler.h" #include "exceptions.h" -#include "emitter.h" namespace YAML { Map::Map() { } - - Map::Map(const node_map& data) - { - for(node_map::const_iterator it=data.begin();it!=data.end();++it) { - std::auto_ptr pKey = it->first->Clone(); - std::auto_ptr pValue = it->second->Clone(); - AddEntry(pKey, pValue); - } - } Map::~Map() { @@ -34,11 +23,6 @@ namespace YAML m_data.clear(); } - Content *Map::Clone() const - { - return new Map(m_data); - } - bool Map::GetBegin(std::map ::const_iterator& it) const { it = m_data.begin(); @@ -56,140 +40,7 @@ namespace YAML return m_data.size(); } - void Map::Parse(Scanner *pScanner, ParserState& state) - { - Clear(); - - // split based on start token - switch(pScanner->peek().type) { - case Token::BLOCK_MAP_START: ParseBlock(pScanner, state); break; - case Token::FLOW_MAP_START: ParseFlow(pScanner, state); break; - case Token::KEY: ParseCompact(pScanner, state); break; - case Token::VALUE: ParseCompactWithNoKey(pScanner, state); break; - default: break; - } - } - - void Map::ParseBlock(Scanner *pScanner, ParserState& state) - { - // eat start token - pScanner->pop(); - state.PushCollectionType(ParserState::BLOCK_MAP); - - while(1) { - if(pScanner->empty()) - throw ParserException(Mark::null(), ErrorMsg::END_OF_MAP); - - Token token = pScanner->peek(); - if(token.type != Token::KEY && token.type != Token::VALUE && token.type != Token::BLOCK_MAP_END) - throw ParserException(token.mark, ErrorMsg::END_OF_MAP); - - if(token.type == Token::BLOCK_MAP_END) { - pScanner->pop(); - break; - } - - std::auto_ptr pKey(new Node), pValue(new Node); - - // grab key (if non-null) - if(token.type == Token::KEY) { - pScanner->pop(); - pKey->Parse(pScanner, state); - } - - // now grab value (optional) - if(!pScanner->empty() && pScanner->peek().type == Token::VALUE) { - pScanner->pop(); - pValue->Parse(pScanner, state); - } - - AddEntry(pKey, pValue); - } - - state.PopCollectionType(ParserState::BLOCK_MAP); - } - - void Map::ParseFlow(Scanner *pScanner, ParserState& state) - { - // eat start token - pScanner->pop(); - state.PushCollectionType(ParserState::FLOW_MAP); - - while(1) { - if(pScanner->empty()) - throw ParserException(Mark::null(), ErrorMsg::END_OF_MAP_FLOW); - - Token& token = pScanner->peek(); - // first check for end - if(token.type == Token::FLOW_MAP_END) { - pScanner->pop(); - break; - } - - std::auto_ptr pKey(new Node), pValue(new Node); - - // grab key (if non-null) - if(token.type == Token::KEY) { - pScanner->pop(); - pKey->Parse(pScanner, state); - } - - // now grab value (optional) - if(!pScanner->empty() && pScanner->peek().type == Token::VALUE) { - pScanner->pop(); - pValue->Parse(pScanner, state); - } - - // now eat the separator (or could be a map end, which we ignore - but if it's neither, then it's a bad node) - Token& nextToken = pScanner->peek(); - if(nextToken.type == Token::FLOW_ENTRY) - pScanner->pop(); - else if(nextToken.type != Token::FLOW_MAP_END) - throw ParserException(nextToken.mark, ErrorMsg::END_OF_MAP_FLOW); - - AddEntry(pKey, pValue); - } - - state.PopCollectionType(ParserState::FLOW_MAP); - } - - // ParseCompact - // . Single "key: value" pair in a flow sequence - void Map::ParseCompact(Scanner *pScanner, ParserState& state) - { - state.PushCollectionType(ParserState::COMPACT_MAP); - std::auto_ptr pKey(new Node), pValue(new Node); - - // grab key - pScanner->pop(); - pKey->Parse(pScanner, state); - - // now grab value (optional) - if(!pScanner->empty() && pScanner->peek().type == Token::VALUE) { - pScanner->pop(); - pValue->Parse(pScanner, state); - } - - AddEntry(pKey, pValue); - state.PopCollectionType(ParserState::COMPACT_MAP); - } - - // ParseCompactWithNoKey - // . Single ": value" pair in a flow sequence - void Map::ParseCompactWithNoKey(Scanner *pScanner, ParserState& state) - { - state.PushCollectionType(ParserState::COMPACT_MAP); - std::auto_ptr pKey(new Node), pValue(new Node); - - // grab value - pScanner->pop(); - pValue->Parse(pScanner, state); - - AddEntry(pKey, pValue); - state.PopCollectionType(ParserState::COMPACT_MAP); - } - - void Map::AddEntry(std::auto_ptr pKey, std::auto_ptr pValue) + void Map::Insert(std::auto_ptr pKey, std::auto_ptr pValue) { node_map::const_iterator it = m_data.find(pKey.get()); if(it != m_data.end()) @@ -198,12 +49,14 @@ namespace YAML m_data[pKey.release()] = pValue.release(); } - void Map::Write(Emitter& out) const + void Map::EmitEvents(AliasManager& am, EventHandler& eventHandler, const Mark& mark, const std::string& tag, anchor_t anchor) const { - out << BeginMap; - for(node_map::const_iterator it=m_data.begin();it!=m_data.end();++it) - out << Key << *it->first << Value << *it->second; - out << EndMap; + eventHandler.OnMapStart(mark, tag, anchor); + for(node_map::const_iterator it=m_data.begin();it!=m_data.end();++it) { + it->first->EmitEvents(am, eventHandler); + it->second->EmitEvents(am, eventHandler); + } + eventHandler.OnMapEnd(); } int Map::Compare(Content *pContent) diff --git a/src/map.h b/src/map.h index 6887880..8eb44ee 100644 --- a/src/map.h +++ b/src/map.h @@ -19,17 +19,16 @@ namespace YAML public: Map(); - Map(const node_map& data); virtual ~Map(); void Clear(); - virtual Content *Clone() const; virtual bool GetBegin(std::map ::const_iterator& it) const; virtual bool GetEnd(std::map ::const_iterator& it) const; virtual std::size_t GetSize() const; - virtual void Parse(Scanner *pScanner, ParserState& state); - virtual void Write(Emitter& out) const; + + virtual void Insert(std::auto_ptr pKey, std::auto_ptr pValue); + virtual void EmitEvents(AliasManager& am, EventHandler& eventHandler, const Mark& mark, const std::string& tag, anchor_t anchor) const; virtual bool IsMap() const { return true; } @@ -39,14 +38,6 @@ namespace YAML virtual int Compare(Sequence *) { return 1; } virtual int Compare(Map *pMap); - private: - void ParseBlock(Scanner *pScanner, ParserState& state); - void ParseFlow(Scanner *pScanner, ParserState& state); - void ParseCompact(Scanner *pScanner, ParserState& state); - void ParseCompactWithNoKey(Scanner *pScanner, ParserState& state); - - void AddEntry(std::auto_ptr pKey, std::auto_ptr pValue); - private: node_map m_data; }; diff --git a/src/node.cpp b/src/node.cpp index 24d3635..757b798 100644 --- a/src/node.cpp +++ b/src/node.cpp @@ -1,15 +1,20 @@ #include "node.h" -#include "token.h" -#include "scanner.h" -#include "content.h" -#include "parser.h" -#include "scalar.h" -#include "sequence.h" -#include "map.h" #include "aliascontent.h" -#include "iterpriv.h" +#include "aliasmanager.h" +#include "content.h" +#include "emitfromevents.h" #include "emitter.h" +#include "eventhandler.h" +#include "iterpriv.h" +#include "map.h" +#include "nodebuilder.h" +#include "nodeproperties.h" +#include "scalar.h" +#include "scanner.h" +#include "sequence.h" #include "tag.h" +#include "token.h" +#include #include namespace YAML @@ -20,17 +25,10 @@ namespace YAML return *pNode1 < *pNode2; } - Node::Node(): m_pContent(0), m_alias(false), m_pIdentity(this), m_referenced(true) + Node::Node(): m_pContent(0), m_alias(false), m_pIdentity(this), m_referenced(false) { } - Node::Node(const Mark& mark, const std::string& anchor, const std::string& tag, const Content *pContent) - : m_mark(mark), m_anchor(anchor), m_tag(tag), m_pContent(0), m_alias(false), m_pIdentity(this), m_referenced(false) - { - if(pContent) - m_pContent = pContent->Clone(); - } - Node::~Node() { Clear(); @@ -42,138 +40,105 @@ namespace YAML m_pContent = 0; m_alias = false; m_referenced = false; - m_anchor.clear(); m_tag.clear(); } std::auto_ptr Node::Clone() const { - if(m_alias) - throw std::runtime_error("yaml-cpp: Can't clone alias"); // TODO: what to do about aliases? - - return std::auto_ptr (new Node(m_mark, m_anchor, m_tag, m_pContent)); + std::auto_ptr pNode(new Node); + NodeBuilder nodeBuilder(*pNode); + EmitEvents(nodeBuilder); + return pNode; } - void Node::Parse(Scanner *pScanner, ParserState& state) + void Node::EmitEvents(EventHandler& eventHandler) const + { + eventHandler.OnDocumentStart(m_mark); + AliasManager am; + EmitEvents(am, eventHandler); + eventHandler.OnDocumentEnd(); + } + + void Node::EmitEvents(AliasManager& am, EventHandler& eventHandler) const + { + anchor_t anchor = NullAnchor; + if(m_referenced || m_alias) { + if(const Node *pOther = am.LookupReference(*this)) { + eventHandler.OnAlias(m_mark, am.LookupAnchor(*pOther)); + return; + } + + am.RegisterReference(*this); + anchor = am.LookupAnchor(*this); + } + + if(m_pContent) + m_pContent->EmitEvents(am, eventHandler, m_mark, GetTag(), anchor); + else + eventHandler.OnNull(GetTag(), anchor); + } + + void Node::Init(CONTENT_TYPE type, const Mark& mark, const std::string& tag) { Clear(); + m_mark = mark; + m_tag = tag; + m_alias = false; + m_pIdentity = this; + m_referenced = false; - // an empty node *is* a possibility - if(pScanner->empty()) - return; - - // save location - m_mark = pScanner->peek().mark; - - // special case: a value node by itself must be a map, with no header - if(pScanner->peek().type == Token::VALUE) { - m_pContent = new Map; - m_pContent->Parse(pScanner, state); - return; - } - - ParseHeader(pScanner, state); - - // is this an alias? if so, its contents are an alias to - // a previously defined anchor - if(m_alias) { - // the scanner throws an exception if it doesn't know this anchor name - const Node *pReferencedNode = pScanner->Retrieve(m_anchor); - m_pIdentity = pReferencedNode; - - // mark the referenced node for the sake of the client code - pReferencedNode->m_referenced = true; - - // use of an Alias object keeps the referenced content from - // being deleted twice - Content *pAliasedContent = pReferencedNode->m_pContent; - if(pAliasedContent) - m_pContent = new AliasContent(pAliasedContent); - - return; - } - - // now split based on what kind of node we should be - switch(pScanner->peek().type) { - case Token::SCALAR: + switch(type) { + case CT_SCALAR: m_pContent = new Scalar; break; - case Token::FLOW_SEQ_START: - case Token::BLOCK_SEQ_START: + case CT_SEQUENCE: m_pContent = new Sequence; break; - case Token::FLOW_MAP_START: - case Token::BLOCK_MAP_START: + case CT_MAP: m_pContent = new Map; break; - case Token::KEY: - // compact maps can only go in a flow sequence - if(state.GetCurCollectionType() == ParserState::FLOW_SEQ) - m_pContent = new Map; - break; default: + m_pContent = 0; break; } - - // Have to save anchor before parsing to allow for aliases as - // contained node (recursive structure) - if(!m_anchor.empty()) - pScanner->Save(m_anchor, this); - - if(m_pContent) - m_pContent->Parse(pScanner, state); } - // ParseHeader - // . Grabs any tag, alias, or anchor tokens and deals with them. - void Node::ParseHeader(Scanner *pScanner, ParserState& state) + void Node::InitNull(const std::string& tag) { - while(1) { - if(pScanner->empty()) - return; - - switch(pScanner->peek().type) { - case Token::TAG: ParseTag(pScanner, state); break; - case Token::ANCHOR: ParseAnchor(pScanner, state); break; - case Token::ALIAS: ParseAlias(pScanner, state); break; - default: return; - } - } + Clear(); + m_tag = tag; + m_alias = false; + m_pIdentity = this; + m_referenced = false; } - void Node::ParseTag(Scanner *pScanner, ParserState& state) + void Node::InitAlias(const Mark& mark, const Node& identity) { - Token& token = pScanner->peek(); - if(m_tag != "") - throw ParserException(token.mark, ErrorMsg::MULTIPLE_TAGS); + Clear(); + m_mark = mark; + m_alias = true; + m_pIdentity = &identity; + if(identity.m_pContent) + m_pContent = new AliasContent(identity.m_pContent); + identity.m_referenced = true; + } - Tag tag(token); - m_tag = tag.Translate(state); - pScanner->pop(); + void Node::SetData(const std::string& data) + { + assert(m_pContent); // TODO: throw + m_pContent->SetData(data); + } + + void Node::Append(std::auto_ptr pNode) + { + assert(m_pContent); // TODO: throw + m_pContent->Append(pNode); } - void Node::ParseAnchor(Scanner *pScanner, ParserState& /*state*/) + void Node::Insert(std::auto_ptr pKey, std::auto_ptr pValue) { - Token& token = pScanner->peek(); - if(m_anchor != "") - throw ParserException(token.mark, ErrorMsg::MULTIPLE_ANCHORS); - - m_anchor = token.value; - m_alias = false; - pScanner->pop(); - } - - void Node::ParseAlias(Scanner *pScanner, ParserState& /*state*/) - { - Token& token = pScanner->peek(); - if(m_anchor != "") - throw ParserException(token.mark, ErrorMsg::MULTIPLE_ALIASES); - if(m_tag != "") - throw ParserException(token.mark, ErrorMsg::ALIAS_CONTENT); - - m_anchor = token.value; - m_alias = true; - pScanner->pop(); + assert(m_pContent); // TODO: throw + m_pContent->Insert(pKey, pValue); } CONTENT_TYPE Node::GetType() const @@ -261,23 +226,8 @@ namespace YAML Emitter& operator << (Emitter& out, const Node& node) { - // write anchor/alias - if(node.m_anchor != "") { - if(node.m_alias) - out << Alias(node.m_anchor); - else - out << Anchor(node.m_anchor); - } - - if(node.m_tag != "") - out << VerbatimTag(node.m_tag); - - // write content - if(node.m_pContent) - node.m_pContent->Write(out); - else if(!node.m_alias) - out << Null; - + EmitFromEvents emitFromEvents(out); + node.EmitEvents(emitFromEvents); return out; } diff --git a/src/nodebuilder.cpp b/src/nodebuilder.cpp new file mode 100644 index 0000000..7da7d16 --- /dev/null +++ b/src/nodebuilder.cpp @@ -0,0 +1,142 @@ +#include "nodebuilder.h" +#include "mark.h" +#include "node.h" +#include "nodeproperties.h" +#include + +namespace YAML +{ + NodeBuilder::NodeBuilder(Node& root): m_root(root), m_initializedRoot(false), m_finished(false) + { + m_root.Clear(); + m_anchors.push_back(0); // since the anchors start at 1 + } + + NodeBuilder::~NodeBuilder() + { + } + + void NodeBuilder::OnDocumentStart(const Mark&) + { + } + + void NodeBuilder::OnDocumentEnd() + { + assert(m_finished); + } + + void NodeBuilder::OnNull(const std::string& tag, anchor_t anchor) + { + Node& node = Push(anchor); + node.InitNull(tag); + Pop(); + } + + void NodeBuilder::OnAlias(const Mark& mark, anchor_t anchor) + { + Node& node = Push(); + node.InitAlias(mark, *m_anchors[anchor]); + Pop(); + } + + void NodeBuilder::OnScalar(const Mark& mark, const std::string& tag, anchor_t anchor, const std::string& value) + { + Node& node = Push(anchor); + node.Init(CT_SCALAR, mark, tag); + node.SetData(value); + Pop(); + } + + void NodeBuilder::OnSequenceStart(const Mark& mark, const std::string& tag, anchor_t anchor) + { + Node& node = Push(anchor); + node.Init(CT_SEQUENCE, mark, tag); + } + + void NodeBuilder::OnSequenceEnd() + { + Pop(); + } + + void NodeBuilder::OnMapStart(const Mark& mark, const std::string& tag, anchor_t anchor) + { + Node& node = Push(anchor); + node.Init(CT_MAP, mark, tag); + m_didPushKey.push(false); + } + + void NodeBuilder::OnMapEnd() + { + m_didPushKey.pop(); + Pop(); + } + + Node& NodeBuilder::Push(anchor_t anchor) + { + Node& node = Push(); + RegisterAnchor(anchor, node); + return node; + } + + Node& NodeBuilder::Push() + { + if(!m_initializedRoot) { + m_initializedRoot = true; + return m_root; + } + + std::auto_ptr pNode(new Node); + m_stack.push(pNode); + return m_stack.top(); + } + + Node& NodeBuilder::Top() + { + return m_stack.empty() ? m_root : m_stack.top(); + } + + void NodeBuilder::Pop() + { + assert(!m_finished); + if(m_stack.empty()) { + m_finished = true; + return; + } + + std::auto_ptr pNode = m_stack.pop(); + Insert(pNode); + } + + void NodeBuilder::Insert(std::auto_ptr pNode) + { + Node& node = Top(); + switch(node.GetType()) { + case CT_SEQUENCE: + node.Append(pNode); + break; + case CT_MAP: + assert(!m_didPushKey.empty()); + if(m_didPushKey.top()) { + assert(!m_pendingKeys.empty()); + + std::auto_ptr pKey = m_pendingKeys.pop(); + node.Insert(pKey, pNode); + m_didPushKey.top() = false; + } else { + m_pendingKeys.push(pNode); + m_didPushKey.top() = true; + } + break; + default: + assert(false); + } + } + + void NodeBuilder::RegisterAnchor(anchor_t anchor, const Node& node) + { + if(anchor) { + assert(anchor == m_anchors.size()); + m_anchors.push_back(&node); + } + } +} diff --git a/src/nodebuilder.h b/src/nodebuilder.h new file mode 100644 index 0000000..004aaad --- /dev/null +++ b/src/nodebuilder.h @@ -0,0 +1,59 @@ +#pragma once + +#ifndef NODEBUILDER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define NODEBUILDER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#include "eventhandler.h" +#include "ptr_stack.h" +#include +#include +#include + +namespace YAML +{ + class Node; + + class NodeBuilder: public EventHandler + { + public: + explicit NodeBuilder(Node& root); + virtual ~NodeBuilder(); + + virtual void OnDocumentStart(const Mark& mark); + virtual void OnDocumentEnd(); + + virtual void OnNull(const std::string& tag, anchor_t anchor); + virtual void OnAlias(const Mark& mark, anchor_t anchor); + virtual void OnScalar(const Mark& mark, const std::string& tag, anchor_t anchor, const std::string& value); + + virtual void OnSequenceStart(const Mark& mark, const std::string& tag, anchor_t anchor); + virtual void OnSequenceEnd(); + + virtual void OnMapStart(const Mark& mark, const std::string& tag, anchor_t anchor); + virtual void OnMapEnd(); + + private: + Node& Push(anchor_t anchor); + Node& Push(); + Node& Top(); + void Pop(); + + void Insert(std::auto_ptr pNode); + void RegisterAnchor(anchor_t anchor, const Node& node); + + private: + Node& m_root; + bool m_initializedRoot; + bool m_finished; + + ptr_stack m_stack; + ptr_stack m_pendingKeys; + std::stack m_didPushKey; + + typedef std::vector Anchors; + Anchors m_anchors; + }; +} + +#endif // NODEBUILDER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + diff --git a/src/parser.cpp b/src/parser.cpp index e9fd0a1..f557b9b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,8 +1,14 @@ #include "parser.h" -#include "scanner.h" -#include "token.h" +#include "directives.h" +#include "eventhandler.h" #include "exceptions.h" -#include "parserstate.h" +#include "graphbuilderadapter.h" +#include "node.h" +#include "nodebuilder.h" +#include "scanner.h" +#include "singledocparser.h" +#include "tag.h" +#include "token.h" #include #include @@ -29,7 +35,38 @@ namespace YAML void Parser::Load(std::istream& in) { m_pScanner.reset(new Scanner(in)); - m_pState.reset(new ParserState); + m_pDirectives.reset(new Directives); + } + + // HandleNextDocument + // . Handles the next document + // . Throws a ParserException on error. + // . Returns false if there are no more documents + bool Parser::HandleNextDocument(EventHandler& eventHandler) + { + if(!m_pScanner.get()) + return false; + + ParseDirectives(); + if(m_pScanner->empty()) + return false; + + SingleDocParser sdp(*m_pScanner, *m_pDirectives); + sdp.HandleDocument(eventHandler); + return true; + } + + // BuildNextDocumentGraph + // . Builds a graph of the next document + // . Returns NULL if there are no more documents + void *Parser::BuildNextDocumentGraph(GraphBuilderInterface& graphBuilder) + { + GraphBuilderAdapter eventHandler(graphBuilder); + if (HandleNextDocument(eventHandler)) { + return eventHandler.RootNode(); + } else { + return NULL; + } } // GetNextDocument @@ -37,34 +74,8 @@ namespace YAML // . Throws a ParserException on error. bool Parser::GetNextDocument(Node& document) { - if(!m_pScanner.get()) - return false; - - // clear node - document.Clear(); - - // first read directives - ParseDirectives(); - - // we better have some tokens in the queue - if(m_pScanner->empty()) - return false; - - // first eat doc start (optional) - if(m_pScanner->peek().type == Token::DOC_START) - m_pScanner->pop(); - - // now parse our root node - document.Parse(m_pScanner.get(), *m_pState); - - // and finally eat any doc ends we see - while(!m_pScanner->empty() && m_pScanner->peek().type == Token::DOC_END) - m_pScanner->pop(); - - // clear anchors from the scanner, which are no longer relevant - m_pScanner->ClearAnchors(); - - return true; + NodeBuilder builder(document); + return HandleNextDocument(builder); } // ParseDirectives @@ -84,7 +95,7 @@ namespace YAML // we keep the directives from the last document if none are specified; // but if any directives are specific, then we reset them if(!readDirective) - m_pState.reset(new ParserState); + m_pDirectives.reset(new Directives); readDirective = true; HandleDirective(token); @@ -107,20 +118,20 @@ namespace YAML if(token.params.size() != 1) throw ParserException(token.mark, ErrorMsg::YAML_DIRECTIVE_ARGS); - if(!m_pState->version.isDefault) + if(!m_pDirectives->version.isDefault) throw ParserException(token.mark, ErrorMsg::REPEATED_YAML_DIRECTIVE); std::stringstream str(token.params[0]); - str >> m_pState->version.major; + str >> m_pDirectives->version.major; str.get(); - str >> m_pState->version.minor; + str >> m_pDirectives->version.minor; if(!str || str.peek() != EOF) throw ParserException(token.mark, ErrorMsg::YAML_VERSION + token.params[0]); - if(m_pState->version.major > 1) + if(m_pDirectives->version.major > 1) throw ParserException(token.mark, ErrorMsg::YAML_MAJOR_VERSION); - m_pState->version.isDefault = false; + m_pDirectives->version.isDefault = false; // TODO: warning on major == 1, minor > 2? } @@ -133,10 +144,10 @@ namespace YAML const std::string& handle = token.params[0]; const std::string& prefix = token.params[1]; - if(m_pState->tags.find(handle) != m_pState->tags.end()) + if(m_pDirectives->tags.find(handle) != m_pDirectives->tags.end()) throw ParserException(token.mark, ErrorMsg::REPEATED_TAG_DIRECTIVE); - m_pState->tags[handle] = prefix; + m_pDirectives->tags[handle] = prefix; } void Parser::PrintTokens(std::ostream& out) diff --git a/src/parserstate.h b/src/parserstate.h deleted file mode 100644 index 0b849c3..0000000 --- a/src/parserstate.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#ifndef PARSERSTATE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 -#define PARSERSTATE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 - - -#include -#include -#include -#include - -namespace YAML -{ - struct Version { - bool isDefault; - int major, minor; - }; - - struct ParserState - { - enum COLLECTION_TYPE { NONE, BLOCK_MAP, BLOCK_SEQ, FLOW_MAP, FLOW_SEQ, COMPACT_MAP }; - - ParserState(); - - const std::string TranslateTagHandle(const std::string& handle) const; - COLLECTION_TYPE GetCurCollectionType() const { if(collectionStack.empty()) return NONE; return collectionStack.top(); } - - void PushCollectionType(COLLECTION_TYPE type) { collectionStack.push(type); } - void PopCollectionType(COLLECTION_TYPE type) { assert(type == GetCurCollectionType()); collectionStack.pop(); } - - Version version; - std::map tags; - std::stack collectionStack; - }; -} - -#endif // PARSERSTATE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/ptr_stack.h b/src/ptr_stack.h new file mode 100644 index 0000000..cfe6fac --- /dev/null +++ b/src/ptr_stack.h @@ -0,0 +1,37 @@ +#pragma once + +#ifndef PTR_STACK_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define PTR_STACK_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#include +#include + +template +class ptr_stack +{ +public: + ptr_stack() {} + ~ptr_stack() { clear(); } + + void clear() { + for(unsigned i=0;i t) { m_data.push_back(t.release()); } + std::auto_ptr pop() { + std::auto_ptr t(m_data.back()); + m_data.pop_back(); + return t; + } + T& top() { return *m_data.back(); } + +private: + std::vector m_data; +}; + +#endif // PTR_STACK_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/scalar.cpp b/src/scalar.cpp index 415bace..9383891 100644 --- a/src/scalar.cpp +++ b/src/scalar.cpp @@ -1,9 +1,5 @@ #include "scalar.h" -#include "scanner.h" -#include "token.h" -#include "exceptions.h" -#include "node.h" -#include "emitter.h" +#include "eventhandler.h" namespace YAML { @@ -11,29 +7,13 @@ namespace YAML { } - Scalar::Scalar(const std::string& data): m_data(data) - { - } - Scalar::~Scalar() { } - Content *Scalar::Clone() const + void Scalar::EmitEvents(AliasManager&, EventHandler& eventHandler, const Mark& mark, const std::string& tag, anchor_t anchor) const { - return new Scalar(m_data); - } - - void Scalar::Parse(Scanner *pScanner, ParserState& /*state*/) - { - Token& token = pScanner->peek(); - m_data = token.value; - pScanner->pop(); - } - - void Scalar::Write(Emitter& out) const - { - out << m_data; + eventHandler.OnScalar(mark, tag, anchor, m_data); } int Scalar::Compare(Content *pContent) diff --git a/src/scalar.h b/src/scalar.h index 1baecaa..5843470 100644 --- a/src/scalar.h +++ b/src/scalar.h @@ -13,15 +13,12 @@ namespace YAML { public: Scalar(); - Scalar(const std::string& data); virtual ~Scalar(); - virtual Content *Clone() const; - - virtual void Parse(Scanner *pScanner, ParserState& state); - virtual void Write(Emitter& out) const; + virtual void SetData(const std::string& data) { m_data = data; } virtual bool IsScalar() const { return true; } + virtual void EmitEvents(AliasManager& am, EventHandler& eventHandler, const Mark& mark, const std::string& tag, anchor_t anchor) const; // extraction virtual bool GetScalar(std::string& scalar) const { diff --git a/src/scanner.cpp b/src/scanner.cpp index 33052c2..2e5fef7 100644 --- a/src/scanner.cpp +++ b/src/scanner.cpp @@ -32,13 +32,8 @@ namespace YAML void Scanner::pop() { EnsureTokensInQueue(); - if(!m_tokens.empty()) { - // Saved anchors shouldn't survive popping the document end marker - if (m_tokens.front().type == Token::DOC_END) { - ClearAnchors(); - } + if(!m_tokens.empty()) m_tokens.pop(); - } } // peek @@ -245,7 +240,6 @@ namespace YAML IndentMarker *pIndent = new IndentMarker(-1, IndentMarker::NONE); m_indentRefs.push_back(pIndent); m_indents.push(pIndent); - m_anchors.clear(); } // EndStream @@ -378,29 +372,6 @@ namespace YAML return m_indents.top()->column; } - // Save - // . Saves a pointer to the Node object referenced by a particular anchor - // name. - void Scanner::Save(const std::string& anchor, Node* value) - { - m_anchors[anchor] = value; - } - - // Retrieve - // . Retrieves a pointer previously saved for an anchor name. - // . Throws an exception if the anchor has not been defined. - const Node *Scanner::Retrieve(const std::string& anchor) const - { - typedef std::map map; - - map::const_iterator itNode = m_anchors.find(anchor); - - if(m_anchors.end() == itNode) - ThrowParserException(ErrorMsg::UNKNOWN_ANCHOR); - - return itNode->second; - } - // ThrowParserException // . Throws a ParserException with the current token location // (if available). @@ -414,10 +385,5 @@ namespace YAML } throw ParserException(mark, msg); } - - void Scanner::ClearAnchors() - { - m_anchors.clear(); - } } diff --git a/src/scanner.h b/src/scanner.h index 28ce96d..17908cc 100644 --- a/src/scanner.h +++ b/src/scanner.h @@ -29,11 +29,6 @@ namespace YAML void pop(); Token& peek(); - // anchor management - void Save(const std::string& anchor, Node* value); - const Node *Retrieve(const std::string& anchor) const; - void ClearAnchors(); - private: struct IndentMarker { enum INDENT_TYPE { MAP, SEQ, NONE }; @@ -127,7 +122,6 @@ namespace YAML std::stack m_indents; std::vector m_indentRefs; // for "garbage collection" std::stack m_flows; - std::map m_anchors; }; } diff --git a/src/sequence.cpp b/src/sequence.cpp index 0f96e27..60a0f14 100644 --- a/src/sequence.cpp +++ b/src/sequence.cpp @@ -1,8 +1,6 @@ #include "sequence.h" +#include "eventhandler.h" #include "node.h" -#include "scanner.h" -#include "token.h" -#include "emitter.h" #include namespace YAML @@ -12,12 +10,6 @@ namespace YAML } - Sequence::Sequence(const std::vector& data) - { - for(std::size_t i=0;iClone().release()); - } - Sequence::~Sequence() { Clear(); @@ -30,11 +22,6 @@ namespace YAML m_data.clear(); } - Content *Sequence::Clone() const - { - return new Sequence(m_data); - } - bool Sequence::GetBegin(std::vector ::const_iterator& it) const { it = m_data.begin(); @@ -59,90 +46,17 @@ namespace YAML return m_data.size(); } - void Sequence::Parse(Scanner *pScanner, ParserState& state) + void Sequence::Append(std::auto_ptr pNode) { - Clear(); - - // split based on start token - switch(pScanner->peek().type) { - case Token::BLOCK_SEQ_START: ParseBlock(pScanner, state); break; - case Token::FLOW_SEQ_START: ParseFlow(pScanner, state); break; - default: break; - } + m_data.push_back(pNode.release()); } - - void Sequence::ParseBlock(Scanner *pScanner, ParserState& state) + + void Sequence::EmitEvents(AliasManager& am, EventHandler& eventHandler, const Mark& mark, const std::string& tag, anchor_t anchor) const { - // eat start token - pScanner->pop(); - state.PushCollectionType(ParserState::BLOCK_SEQ); - - while(1) { - if(pScanner->empty()) - throw ParserException(Mark::null(), ErrorMsg::END_OF_SEQ); - - Token token = pScanner->peek(); - if(token.type != Token::BLOCK_ENTRY && token.type != Token::BLOCK_SEQ_END) - throw ParserException(token.mark, ErrorMsg::END_OF_SEQ); - - pScanner->pop(); - if(token.type == Token::BLOCK_SEQ_END) - break; - - Node *pNode = new Node; - m_data.push_back(pNode); - - // check for null - if(!pScanner->empty()) { - const Token& token = pScanner->peek(); - if(token.type == Token::BLOCK_ENTRY || token.type == Token::BLOCK_SEQ_END) - continue; - } - - pNode->Parse(pScanner, state); - } - - state.PopCollectionType(ParserState::BLOCK_SEQ); - } - - void Sequence::ParseFlow(Scanner *pScanner, ParserState& state) - { - // eat start token - pScanner->pop(); - state.PushCollectionType(ParserState::FLOW_SEQ); - - while(1) { - if(pScanner->empty()) - throw ParserException(Mark::null(), ErrorMsg::END_OF_SEQ_FLOW); - - // first check for end - if(pScanner->peek().type == Token::FLOW_SEQ_END) { - pScanner->pop(); - break; - } - - // then read the node - Node *pNode = new Node; - m_data.push_back(pNode); - pNode->Parse(pScanner, state); - - // now eat the separator (or could be a sequence end, which we ignore - but if it's neither, then it's a bad node) - Token& token = pScanner->peek(); - if(token.type == Token::FLOW_ENTRY) - pScanner->pop(); - else if(token.type != Token::FLOW_SEQ_END) - throw ParserException(token.mark, ErrorMsg::END_OF_SEQ_FLOW); - } - - state.PopCollectionType(ParserState::FLOW_SEQ); - } - - void Sequence::Write(Emitter& out) const - { - out << BeginSeq; + eventHandler.OnSequenceStart(mark, tag, anchor); for(std::size_t i=0;iEmitEvents(am, eventHandler); + eventHandler.OnSequenceEnd(); } int Sequence::Compare(Content *pContent) diff --git a/src/sequence.h b/src/sequence.h index 49bdc83..342c5cb 100644 --- a/src/sequence.h +++ b/src/sequence.h @@ -15,19 +15,17 @@ namespace YAML { public: Sequence(); - Sequence(const std::vector& data); virtual ~Sequence(); void Clear(); - virtual Content *Clone() const; virtual bool GetBegin(std::vector ::const_iterator& it) const; virtual bool GetEnd(std::vector ::const_iterator& it) const; virtual Node *GetNode(std::size_t i) const; virtual std::size_t GetSize() const; - virtual void Parse(Scanner *pScanner, ParserState& state); - virtual void Write(Emitter& out) const; + virtual void Append(std::auto_ptr pNode); + virtual void EmitEvents(AliasManager& am, EventHandler& eventHandler, const Mark& mark, const std::string& tag, anchor_t anchor) const; virtual bool IsSequence() const { return true; } @@ -37,10 +35,6 @@ namespace YAML virtual int Compare(Sequence *pSeq); virtual int Compare(Map *) { return -1; } - private: - void ParseBlock(Scanner *pScanner, ParserState& state); - void ParseFlow(Scanner *pScanner, ParserState& state); - protected: std::vector m_data; }; diff --git a/src/singledocparser.cpp b/src/singledocparser.cpp new file mode 100644 index 0000000..964212e --- /dev/null +++ b/src/singledocparser.cpp @@ -0,0 +1,369 @@ +#include "singledocparser.h" +#include "collectionstack.h" +#include "directives.h" +#include "eventhandler.h" +#include "exceptions.h" +#include "scanner.h" +#include "tag.h" +#include "token.h" +#include +#include + +namespace YAML +{ + SingleDocParser::SingleDocParser(Scanner& scanner, const Directives& directives): m_scanner(scanner), m_directives(directives), m_pCollectionStack(new CollectionStack), m_curAnchor(0) + { + } + + SingleDocParser::~SingleDocParser() + { + } + + // HandleDocument + // . Handles the next document + // . Throws a ParserException on error. + void SingleDocParser::HandleDocument(EventHandler& eventHandler) + { + assert(!m_scanner.empty()); // guaranteed that there are tokens + assert(!m_curAnchor); + + eventHandler.OnDocumentStart(m_scanner.peek().mark); + + // eat doc start + if(m_scanner.peek().type == Token::DOC_START) + m_scanner.pop(); + + // recurse! + HandleNode(eventHandler); + + eventHandler.OnDocumentEnd(); + + // and finally eat any doc ends we see + while(!m_scanner.empty() && m_scanner.peek().type == Token::DOC_END) + m_scanner.pop(); + } + + void SingleDocParser::HandleNode(EventHandler& eventHandler) + { + // an empty node *is* a possibility + if(m_scanner.empty()) { + eventHandler.OnNull("", NullAnchor); + return; + } + + // save location + Mark mark = m_scanner.peek().mark; + + // special case: a value node by itself must be a map, with no header + if(m_scanner.peek().type == Token::VALUE) { + eventHandler.OnMapStart(mark, "", NullAnchor); + HandleMap(eventHandler); + eventHandler.OnMapEnd(); + return; + } + + // special case: an alias node + if(m_scanner.peek().type == Token::ALIAS) { + eventHandler.OnAlias(mark, LookupAnchor(mark, m_scanner.peek().value)); + m_scanner.pop(); + return; + } + + std::string tag; + anchor_t anchor; + ParseProperties(tag, anchor); + + // now split based on what kind of node we should be + switch(m_scanner.peek().type) { + case Token::SCALAR: + eventHandler.OnScalar(mark, tag, anchor, m_scanner.peek().value); + m_scanner.pop(); + return; + case Token::FLOW_SEQ_START: + case Token::BLOCK_SEQ_START: + eventHandler.OnSequenceStart(mark, tag, anchor); + HandleSequence(eventHandler); + eventHandler.OnSequenceEnd(); + return; + case Token::FLOW_MAP_START: + case Token::BLOCK_MAP_START: + eventHandler.OnMapStart(mark, tag, anchor); + HandleMap(eventHandler); + eventHandler.OnMapEnd(); + return; + case Token::KEY: + // compact maps can only go in a flow sequence + if(m_pCollectionStack->GetCurCollectionType() == CollectionType::FlowSeq) { + eventHandler.OnMapStart(mark, tag, anchor); + HandleMap(eventHandler); + eventHandler.OnMapEnd(); + return; + } + break; + default: + break; + } + + eventHandler.OnNull(tag, anchor); + } + + void SingleDocParser::HandleSequence(EventHandler& eventHandler) + { + // split based on start token + switch(m_scanner.peek().type) { + case Token::BLOCK_SEQ_START: HandleBlockSequence(eventHandler); break; + case Token::FLOW_SEQ_START: HandleFlowSequence(eventHandler); break; + default: break; + } + } + + void SingleDocParser::HandleBlockSequence(EventHandler& eventHandler) + { + // eat start token + m_scanner.pop(); + m_pCollectionStack->PushCollectionType(CollectionType::BlockSeq); + + while(1) { + if(m_scanner.empty()) + throw ParserException(Mark::null(), ErrorMsg::END_OF_SEQ); + + Token token = m_scanner.peek(); + if(token.type != Token::BLOCK_ENTRY && token.type != Token::BLOCK_SEQ_END) + throw ParserException(token.mark, ErrorMsg::END_OF_SEQ); + + m_scanner.pop(); + if(token.type == Token::BLOCK_SEQ_END) + break; + + // check for null + if(!m_scanner.empty()) { + const Token& token = m_scanner.peek(); + if(token.type == Token::BLOCK_ENTRY || token.type == Token::BLOCK_SEQ_END) { + eventHandler.OnNull("", NullAnchor); + continue; + } + } + + HandleNode(eventHandler); + } + + m_pCollectionStack->PopCollectionType(CollectionType::BlockSeq); + } + + void SingleDocParser::HandleFlowSequence(EventHandler& eventHandler) + { + // eat start token + m_scanner.pop(); + m_pCollectionStack->PushCollectionType(CollectionType::FlowSeq); + + while(1) { + if(m_scanner.empty()) + throw ParserException(Mark::null(), ErrorMsg::END_OF_SEQ_FLOW); + + // first check for end + if(m_scanner.peek().type == Token::FLOW_SEQ_END) { + m_scanner.pop(); + break; + } + + // then read the node + HandleNode(eventHandler); + + // now eat the separator (or could be a sequence end, which we ignore - but if it's neither, then it's a bad node) + Token& token = m_scanner.peek(); + if(token.type == Token::FLOW_ENTRY) + m_scanner.pop(); + else if(token.type != Token::FLOW_SEQ_END) + throw ParserException(token.mark, ErrorMsg::END_OF_SEQ_FLOW); + } + + m_pCollectionStack->PopCollectionType(CollectionType::FlowSeq); + } + + void SingleDocParser::HandleMap(EventHandler& eventHandler) + { + // split based on start token + switch(m_scanner.peek().type) { + case Token::BLOCK_MAP_START: HandleBlockMap(eventHandler); break; + case Token::FLOW_MAP_START: HandleFlowMap(eventHandler); break; + case Token::KEY: HandleCompactMap(eventHandler); break; + case Token::VALUE: HandleCompactMapWithNoKey(eventHandler); break; + default: break; + } + } + + void SingleDocParser::HandleBlockMap(EventHandler& eventHandler) + { + // eat start token + m_scanner.pop(); + m_pCollectionStack->PushCollectionType(CollectionType::BlockMap); + + while(1) { + if(m_scanner.empty()) + throw ParserException(Mark::null(), ErrorMsg::END_OF_MAP); + + Token token = m_scanner.peek(); + if(token.type != Token::KEY && token.type != Token::VALUE && token.type != Token::BLOCK_MAP_END) + throw ParserException(token.mark, ErrorMsg::END_OF_MAP); + + if(token.type == Token::BLOCK_MAP_END) { + m_scanner.pop(); + break; + } + + // grab key (if non-null) + if(token.type == Token::KEY) { + m_scanner.pop(); + HandleNode(eventHandler); + } else { + eventHandler.OnNull("", NullAnchor); + } + + // now grab value (optional) + if(!m_scanner.empty() && m_scanner.peek().type == Token::VALUE) { + m_scanner.pop(); + HandleNode(eventHandler); + } else { + eventHandler.OnNull("", NullAnchor); + } + } + + m_pCollectionStack->PopCollectionType(CollectionType::BlockMap); + } + + void SingleDocParser::HandleFlowMap(EventHandler& eventHandler) + { + // eat start token + m_scanner.pop(); + m_pCollectionStack->PushCollectionType(CollectionType::FlowMap); + + while(1) { + if(m_scanner.empty()) + throw ParserException(Mark::null(), ErrorMsg::END_OF_MAP_FLOW); + + Token& token = m_scanner.peek(); + // first check for end + if(token.type == Token::FLOW_MAP_END) { + m_scanner.pop(); + break; + } + + // grab key (if non-null) + if(token.type == Token::KEY) { + m_scanner.pop(); + HandleNode(eventHandler); + } else { + eventHandler.OnNull("", NullAnchor); + } + + // now grab value (optional) + if(!m_scanner.empty() && m_scanner.peek().type == Token::VALUE) { + m_scanner.pop(); + HandleNode(eventHandler); + } else { + eventHandler.OnNull("", NullAnchor); + } + + // now eat the separator (or could be a map end, which we ignore - but if it's neither, then it's a bad node) + Token& nextToken = m_scanner.peek(); + if(nextToken.type == Token::FLOW_ENTRY) + m_scanner.pop(); + else if(nextToken.type != Token::FLOW_MAP_END) + throw ParserException(nextToken.mark, ErrorMsg::END_OF_MAP_FLOW); + } + + m_pCollectionStack->PopCollectionType(CollectionType::FlowMap); + } + + // . Single "key: value" pair in a flow sequence + void SingleDocParser::HandleCompactMap(EventHandler& eventHandler) + { + m_pCollectionStack->PushCollectionType(CollectionType::CompactMap); + + // grab key + m_scanner.pop(); + HandleNode(eventHandler); + + // now grab value (optional) + if(!m_scanner.empty() && m_scanner.peek().type == Token::VALUE) { + m_scanner.pop(); + HandleNode(eventHandler); + } else { + eventHandler.OnNull("", NullAnchor); + } + + m_pCollectionStack->PopCollectionType(CollectionType::CompactMap); + } + + // . Single ": value" pair in a flow sequence + void SingleDocParser::HandleCompactMapWithNoKey(EventHandler& eventHandler) + { + m_pCollectionStack->PushCollectionType(CollectionType::CompactMap); + + // null key + eventHandler.OnNull("", NullAnchor); + + // grab value + m_scanner.pop(); + HandleNode(eventHandler); + + m_pCollectionStack->PopCollectionType(CollectionType::CompactMap); + } + + // ParseProperties + // . Grabs any tag or anchor tokens and deals with them. + void SingleDocParser::ParseProperties(std::string& tag, anchor_t& anchor) + { + tag.clear(); + anchor = NullAnchor; + + while(1) { + if(m_scanner.empty()) + return; + + switch(m_scanner.peek().type) { + case Token::TAG: ParseTag(tag); break; + case Token::ANCHOR: ParseAnchor(anchor); break; + default: return; + } + } + } + + void SingleDocParser::ParseTag(std::string& tag) + { + Token& token = m_scanner.peek(); + if(!tag.empty()) + throw ParserException(token.mark, ErrorMsg::MULTIPLE_TAGS); + + Tag tagInfo(token); + tag = tagInfo.Translate(m_directives); + m_scanner.pop(); + } + + void SingleDocParser::ParseAnchor(anchor_t& anchor) + { + Token& token = m_scanner.peek(); + if(anchor) + throw ParserException(token.mark, ErrorMsg::MULTIPLE_ANCHORS); + + anchor = RegisterAnchor(token.value); + m_scanner.pop(); + } + + anchor_t SingleDocParser::RegisterAnchor(const std::string& name) + { + if(name.empty()) + return NullAnchor; + + return m_anchors[name] = ++m_curAnchor; + } + + anchor_t SingleDocParser::LookupAnchor(const Mark& mark, const std::string& name) const + { + Anchors::const_iterator it = m_anchors.find(name); + if(it == m_anchors.end()) + throw ParserException(mark, ErrorMsg::UNKNOWN_ANCHOR); + + return it->second; + } +} diff --git a/src/singledocparser.h b/src/singledocparser.h new file mode 100644 index 0000000..19e670e --- /dev/null +++ b/src/singledocparser.h @@ -0,0 +1,63 @@ +#pragma once + +#ifndef SINGLEDOCPARSER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define SINGLEDOCPARSER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include "anchor.h" +#include "noncopyable.h" +#include +#include +#include + +namespace YAML +{ + struct Directives; + struct Mark; + struct Token; + class CollectionStack; + class EventHandler; + class Node; + class Scanner; + + class SingleDocParser: private noncopyable + { + public: + SingleDocParser(Scanner& scanner, const Directives& directives); + ~SingleDocParser(); + + void HandleDocument(EventHandler& eventHandler); + + private: + void HandleNode(EventHandler& eventHandler); + + void HandleSequence(EventHandler& eventHandler); + void HandleBlockSequence(EventHandler& eventHandler); + void HandleFlowSequence(EventHandler& eventHandler); + + void HandleMap(EventHandler& eventHandler); + void HandleBlockMap(EventHandler& eventHandler); + void HandleFlowMap(EventHandler& eventHandler); + void HandleCompactMap(EventHandler& eventHandler); + void HandleCompactMapWithNoKey(EventHandler& eventHandler); + + void ParseProperties(std::string& tag, anchor_t& anchor); + void ParseTag(std::string& tag); + void ParseAnchor(anchor_t& anchor); + + anchor_t RegisterAnchor(const std::string& name); + anchor_t LookupAnchor(const Mark& mark, const std::string& name) const; + + private: + Scanner& m_scanner; + const Directives& m_directives; + std::auto_ptr m_pCollectionStack; + + typedef std::map Anchors; + Anchors m_anchors; + + anchor_t m_curAnchor; + }; +} + +#endif // SINGLEDOCPARSER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/tag.cpp b/src/tag.cpp index 694a4f3..30d68e1 100644 --- a/src/tag.cpp +++ b/src/tag.cpp @@ -1,6 +1,6 @@ #include "tag.h" +#include "directives.h" #include "token.h" -#include "parserstate.h" #include namespace YAML @@ -28,17 +28,17 @@ namespace YAML } } - const std::string Tag::Translate(const ParserState& state) + const std::string Tag::Translate(const Directives& directives) { switch(type) { case VERBATIM: return value; case PRIMARY_HANDLE: - return state.TranslateTagHandle("!") + value; + return directives.TranslateTagHandle("!") + value; case SECONDARY_HANDLE: - return state.TranslateTagHandle("!!") + value; + return directives.TranslateTagHandle("!!") + value; case NAMED_HANDLE: - return state.TranslateTagHandle("!" + handle + "!") + value; + return directives.TranslateTagHandle("!" + handle + "!") + value; case NON_SPECIFIC: // TODO: return "!"; diff --git a/src/tag.h b/src/tag.h index b448994..68aa296 100644 --- a/src/tag.h +++ b/src/tag.h @@ -8,7 +8,7 @@ namespace YAML { struct Token; - struct ParserState; + struct Directives; struct Tag { enum TYPE { @@ -16,7 +16,7 @@ namespace YAML }; Tag(const Token& token); - const std::string Translate(const ParserState& state); + const std::string Translate(const Directives& directives); TYPE type; std::string handle, value; diff --git a/util/parse.cpp b/util/parse.cpp index 8a92576..c7eb4bc 100644 --- a/util/parse.cpp +++ b/util/parse.cpp @@ -1,9 +1,25 @@ #include "yaml.h" #include #include +#include + +struct Params { + bool hasFile; + std::string fileName; +}; + +Params ParseArgs(int argc, char **argv) { + Params p; + + std::vector args(argv + 1, argv + argc); + + return p; +} int main(int argc, char **argv) { + Params p = ParseArgs(argc, argv); + std::ifstream fin; if(argc > 1) fin.open(argv[1]);