From 4c5a488f682f01219f6bd2678ae53ccd621f3158 Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Tue, 1 Jul 2008 06:28:10 +0000 Subject: [PATCH] Tags, anchors, and aliases are all parsed now. --- content.h | 4 +- document.cpp | 4 +- document.h | 3 +- main.cpp | 7 ++- map.cpp | 18 +++---- map.h | 6 +-- node.cpp | 27 +++++----- node.h | 11 ++-- parser.cpp | 74 ++++++++++++++++++++++++-- parser.h | 15 +++++- parserstate.cpp | 25 +++++++++ parserstate.h | 20 +++++++ scalar.cpp | 2 +- scalar.h | 2 +- scantoken.cpp | 16 +++--- sequence.cpp | 20 +++---- sequence.h | 8 +-- test.yaml | 126 ++++++++++++++++++++++++++++++++++----------- yaml-reader.vcproj | 8 +++ 19 files changed, 303 insertions(+), 93 deletions(-) create mode 100644 parserstate.cpp create mode 100644 parserstate.h diff --git a/content.h b/content.h index 12a2b86..ce23447 100644 --- a/content.h +++ b/content.h @@ -1,10 +1,12 @@ #pragma once #include +#include "parserstate.h" namespace YAML { class Scanner; + class Parser; class Content { @@ -12,7 +14,7 @@ namespace YAML Content(); virtual ~Content(); - virtual void Parse(Scanner *pScanner) = 0; + virtual void Parse(Scanner *pScanner, const ParserState& state) = 0; virtual void Write(std::ostream& out, int indent) = 0; protected: diff --git a/document.cpp b/document.cpp index fd526e2..c5d1996 100644 --- a/document.cpp +++ b/document.cpp @@ -20,7 +20,7 @@ namespace YAML m_pRoot = 0; } - void Document::Parse(Scanner *pScanner) + void Document::Parse(Scanner *pScanner, const ParserState& state) { Clear(); @@ -34,7 +34,7 @@ namespace YAML // now create our root node and parse it m_pRoot = new Node; - m_pRoot->Parse(pScanner); + m_pRoot->Parse(pScanner, state); // and finally eat any doc ends we see while(pScanner->PeekNextToken() && pScanner->PeekNextToken()->type == TT_DOC_END) diff --git a/document.h b/document.h index e018981..257f570 100644 --- a/document.h +++ b/document.h @@ -1,6 +1,7 @@ #pragma once #include +#include "parserstate.h" namespace YAML { @@ -14,7 +15,7 @@ namespace YAML ~Document(); void Clear(); - void Parse(Scanner *pScanner); + void Parse(Scanner *pScanner, const ParserState& state); friend std::ostream& operator << (std::ostream& out, const Document& doc); diff --git a/main.cpp b/main.cpp index dba870d..22d0b1b 100644 --- a/main.cpp +++ b/main.cpp @@ -8,8 +8,11 @@ int main() YAML::Parser parser(fin); YAML::Document doc; - parser.GetNextDocument(doc); - std::cout << doc; + while(parser) { + std::cout << "---\n"; + parser.GetNextDocument(doc); + std::cout << doc; + } getchar(); return 0; diff --git a/map.cpp b/map.cpp index d3cba8f..e759f37 100644 --- a/map.cpp +++ b/map.cpp @@ -23,7 +23,7 @@ namespace YAML m_data.clear(); } - void Map::Parse(Scanner *pScanner) + void Map::Parse(Scanner *pScanner, const ParserState& state) { Clear(); @@ -31,12 +31,12 @@ namespace YAML Token *pToken = pScanner->PeekNextToken(); switch(pToken->type) { - case TT_BLOCK_MAP_START: ParseBlock(pScanner); break; - case TT_FLOW_MAP_START: ParseFlow(pScanner); break; + case TT_BLOCK_MAP_START: ParseBlock(pScanner, state); break; + case TT_FLOW_MAP_START: ParseFlow(pScanner, state); break; } } - void Map::ParseBlock(Scanner *pScanner) + void Map::ParseBlock(Scanner *pScanner, const ParserState& state) { // eat start token pScanner->EatNextToken(); @@ -58,17 +58,17 @@ namespace YAML m_data[pKey] = pValue; // grab key - pKey->Parse(pScanner); + pKey->Parse(pScanner, state); // now grab value (optional) if(pScanner->PeekNextToken() && pScanner->PeekNextToken()->type == TT_VALUE) { pScanner->PopNextToken(); - pValue->Parse(pScanner); + pValue->Parse(pScanner, state); } } } - void Map::ParseFlow(Scanner *pScanner) + void Map::ParseFlow(Scanner *pScanner, const ParserState& state) { // eat start token pScanner->EatNextToken(); @@ -95,12 +95,12 @@ namespace YAML m_data[pKey] = pValue; // grab key - pKey->Parse(pScanner); + pKey->Parse(pScanner, state); // now grab value (optional) if(pScanner->PeekNextToken() && pScanner->PeekNextToken()->type == TT_VALUE) { pScanner->PopNextToken(); - pValue->Parse(pScanner); + 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) diff --git a/map.h b/map.h index 0da12b3..9f29298 100644 --- a/map.h +++ b/map.h @@ -14,12 +14,12 @@ namespace YAML virtual ~Map(); void Clear(); - virtual void Parse(Scanner *pScanner); + virtual void Parse(Scanner *pScanner, const ParserState& state); virtual void Write(std::ostream& out, int indent); private: - void ParseBlock(Scanner *pScanner); - void ParseFlow(Scanner *pScanner); + void ParseBlock(Scanner *pScanner, const ParserState& state); + void ParseFlow(Scanner *pScanner, const ParserState& state); protected: typedef std::map node_map; diff --git a/node.cpp b/node.cpp index 439bdd6..47e2080 100644 --- a/node.cpp +++ b/node.cpp @@ -25,11 +25,11 @@ namespace YAML m_alias = false; } - void Node::Parse(Scanner *pScanner) + void Node::Parse(Scanner *pScanner, const ParserState& state) { Clear(); - ParseHeader(pScanner); + ParseHeader(pScanner, state); // is this an alias? if so, it can have no content if(m_alias) @@ -43,24 +43,24 @@ namespace YAML switch(pToken->type) { case TT_SCALAR: m_pContent = new Scalar; - m_pContent->Parse(pScanner); + m_pContent->Parse(pScanner, state); break; case TT_FLOW_SEQ_START: case TT_BLOCK_SEQ_START: case TT_BLOCK_ENTRY: m_pContent = new Sequence; - m_pContent->Parse(pScanner); + m_pContent->Parse(pScanner, state); break; case TT_FLOW_MAP_START: case TT_BLOCK_MAP_START: m_pContent = new Map; - m_pContent->Parse(pScanner); + m_pContent->Parse(pScanner, state); } } // ParseHeader // . Grabs any tag, alias, or anchor tokens and deals with them. - void Node::ParseHeader(Scanner *pScanner) + void Node::ParseHeader(Scanner *pScanner, const ParserState& state) { while(1) { Token *pToken = pScanner->PeekNextToken(); @@ -68,26 +68,27 @@ namespace YAML break; switch(pToken->type) { - case TT_TAG: ParseTag(pScanner); break; - case TT_ANCHOR: ParseAnchor(pScanner); break; - case TT_ALIAS: ParseAlias(pScanner); break; + case TT_TAG: ParseTag(pScanner, state); break; + case TT_ANCHOR: ParseAnchor(pScanner, state); break; + case TT_ALIAS: ParseAlias(pScanner, state); break; } } } - void Node::ParseTag(Scanner *pScanner) + void Node::ParseTag(Scanner *pScanner, const ParserState& state) { if(m_tag != "") return; // TODO: throw Token *pToken = pScanner->PeekNextToken(); - m_tag = pToken->value; + m_tag = state.TranslateTag(pToken->value); + for(unsigned i=0;iparams.size();i++) m_tag += pToken->params[i]; pScanner->PopNextToken(); } - void Node::ParseAnchor(Scanner *pScanner) + void Node::ParseAnchor(Scanner *pScanner, const ParserState& state) { if(m_anchor != "") return; // TODO: throw @@ -98,7 +99,7 @@ namespace YAML pScanner->PopNextToken(); } - void Node::ParseAlias(Scanner *pScanner) + void Node::ParseAlias(Scanner *pScanner, const ParserState& state) { if(m_anchor != "") return; // TODO: throw diff --git a/node.h b/node.h index d6e8727..f8cfe5c 100644 --- a/node.h +++ b/node.h @@ -2,6 +2,7 @@ #include #include +#include "parserstate.h" namespace YAML { @@ -19,14 +20,14 @@ namespace YAML ~Node(); void Clear(); - void Parse(Scanner *pScanner); + void Parse(Scanner *pScanner, const ParserState& state); void Write(std::ostream& out, int indent); private: - void ParseHeader(Scanner *pScanner); - void ParseTag(Scanner *pScanner); - void ParseAnchor(Scanner *pScanner); - void ParseAlias(Scanner *pScanner); + void ParseHeader(Scanner *pScanner, const ParserState& state); + void ParseTag(Scanner *pScanner, const ParserState& state); + void ParseAnchor(Scanner *pScanner, const ParserState& state); + void ParseAlias(Scanner *pScanner, const ParserState& state); private: bool m_alias; diff --git a/parser.cpp b/parser.cpp index 8db28ea..053c83e 100644 --- a/parser.cpp +++ b/parser.cpp @@ -1,13 +1,14 @@ #include "parser.h" #include "scanner.h" #include "token.h" -#include +#include namespace YAML { Parser::Parser(std::istream& in): m_pScanner(0) { m_pScanner = new Scanner(in); + m_state.Reset(); } Parser::~Parser() @@ -15,19 +16,82 @@ namespace YAML delete m_pScanner; } - void Parser::GetNextDocument(Document& document) + Parser::operator bool() const { - document.Parse(m_pScanner); + return m_pScanner->PeekNextToken() != 0; } - void Parser::PrintTokens() + void Parser::GetNextDocument(Document& document) + { + // first read directives + ParseDirectives(); + + // then parse the document + document.Parse(m_pScanner, m_state); + } + + void Parser::ParseDirectives() + { + bool readDirective = false; + + while(1) { + Token *pToken = m_pScanner->PeekNextToken(); + if(!pToken || pToken->type != TT_DIRECTIVE) + break; + + // 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_state.Reset(); + + readDirective = true; + HandleDirective(pToken->value, pToken->params); + m_pScanner->PopNextToken(); + } + } + + void Parser::HandleDirective(const std::string& name, const std::vector & params) + { + if(name == "YAML") + HandleYamlDirective(params); + else if(name == "TAG") + HandleTagDirective(params); + } + + // HandleYamlDirective + // . Should be of the form 'major.minor' (like a version number) + void Parser::HandleYamlDirective(const std::vector & params) + { + if(params.empty()) + return; // TODO: throw? (or throw on params.size() > 1?) + + std::stringstream str(params[0]); + str >> m_state.version.major; + str.get(); + str >> m_state.version.minor; + if(!str) + return; // TODO: throw? (or throw if there are any more characters in the stream?) + + // TODO: throw on major > 1? warning on major == 1, minor > 1? + } + + void Parser::HandleTagDirective(const std::vector & params) + { + if(params.size() != 2) + return; // TODO: throw? + + std::string handle = params[0], prefix = params[1]; + m_state.tags[handle] = prefix; + } + + void Parser::PrintTokens(std::ostream& out) { while(1) { Token *pToken = m_pScanner->GetNextToken(); if(!pToken) break; - std::cout << *pToken << std::endl; + out << *pToken << std::endl; } } } diff --git a/parser.h b/parser.h index 95bb663..cd42017 100644 --- a/parser.h +++ b/parser.h @@ -1,7 +1,11 @@ #pragma once #include +#include +#include +#include #include "document.h" +#include "parserstate.h" namespace YAML { @@ -14,10 +18,19 @@ namespace YAML Parser(std::istream& in); ~Parser(); + operator bool() const; + void GetNextDocument(Document& document); - void PrintTokens(); + void PrintTokens(std::ostream& out); + + private: + void ParseDirectives(); + void HandleDirective(const std::string& name, const std::vector & params); + void HandleYamlDirective(const std::vector & params); + void HandleTagDirective(const std::vector & params); private: Scanner *m_pScanner; + ParserState m_state; }; } diff --git a/parserstate.cpp b/parserstate.cpp new file mode 100644 index 0000000..d0b5209 --- /dev/null +++ b/parserstate.cpp @@ -0,0 +1,25 @@ +#include "parserstate.h" + +namespace YAML +{ + void ParserState::Reset() + { + // version + version.major = 1; + version.minor = 1; + + // and tags + tags.clear(); + tags["!"] = "!"; + tags["!!"] = "tag:yaml.org,2002:"; + } + + std::string ParserState::TranslateTag(const std::string& handle) const + { + std::map ::const_iterator it = tags.find(handle); + if(it == tags.end()) + return handle; + + return it->second; + } +} diff --git a/parserstate.h b/parserstate.h new file mode 100644 index 0000000..e98667d --- /dev/null +++ b/parserstate.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +namespace YAML +{ + struct Version { + int major, minor; + }; + + struct ParserState + { + Version version; + std::map tags; + + void Reset(); + std::string TranslateTag(const std::string& handle) const; + }; +} diff --git a/scalar.cpp b/scalar.cpp index 9fe73a6..5176e52 100644 --- a/scalar.cpp +++ b/scalar.cpp @@ -12,7 +12,7 @@ namespace YAML { } - void Scalar::Parse(Scanner *pScanner) + void Scalar::Parse(Scanner *pScanner, const ParserState& state) { Token *pToken = pScanner->GetNextToken(); m_data = pToken->value; diff --git a/scalar.h b/scalar.h index bea56af..90f2583 100644 --- a/scalar.h +++ b/scalar.h @@ -11,7 +11,7 @@ namespace YAML Scalar(); virtual ~Scalar(); - virtual void Parse(Scanner *pScanner); + virtual void Parse(Scanner *pScanner, const ParserState& state); virtual void Write(std::ostream& out, int indent); protected: diff --git a/scantoken.cpp b/scantoken.cpp index fca7ae6..05bae3d 100644 --- a/scantoken.cpp +++ b/scantoken.cpp @@ -189,7 +189,7 @@ namespace YAML void Scanner::ScanAnchorOrAlias() { bool alias; - std::string tag; + std::string name; // insert a potential simple key if(m_simpleKeyAllowed) @@ -202,10 +202,10 @@ namespace YAML // now eat the content while(Exp::AlphaNumeric.Matches(INPUT)) - tag += INPUT.get(); + name += INPUT.get(); // we need to have read SOMETHING! - if(tag.empty()) + if(name.empty()) throw AnchorNotFound(); // and needs to end correctly @@ -214,7 +214,7 @@ namespace YAML // and we're done Token *pToken = new Token(alias ? TT_ALIAS : TT_ANCHOR); - pToken->value = tag; + pToken->value = name; m_tokens.push(pToken); } @@ -229,7 +229,7 @@ namespace YAML m_simpleKeyAllowed = false; // eat the indicator - INPUT.eat(1); + handle += INPUT.get(); // read the handle while(INPUT.peek() != EOF && INPUT.peek() != Keys::Tag && !Exp::BlankOrBreak.Matches(INPUT)) @@ -238,11 +238,15 @@ namespace YAML // is there a suffix? if(INPUT.peek() == Keys::Tag) { // eat the indicator - INPUT.eat(1); + handle += INPUT.get(); // then read it while(INPUT.peek() != EOF && !Exp::BlankOrBreak.Matches(INPUT)) suffix += INPUT.get(); + } else { + // this is a bit weird: we keep just the '!' as the handle and move the rest to the suffix + suffix = handle.substr(1); + handle = "!"; } Token *pToken = new Token(TT_TAG); diff --git a/sequence.cpp b/sequence.cpp index 8332e13..8d0ca13 100644 --- a/sequence.cpp +++ b/sequence.cpp @@ -22,7 +22,7 @@ namespace YAML m_data.clear(); } - void Sequence::Parse(Scanner *pScanner) + void Sequence::Parse(Scanner *pScanner, const ParserState& state) { Clear(); @@ -30,13 +30,13 @@ namespace YAML Token *pToken = pScanner->PeekNextToken(); switch(pToken->type) { - case TT_BLOCK_SEQ_START: ParseBlock(pScanner); break; - case TT_BLOCK_ENTRY: ParseImplicit(pScanner); break; - case TT_FLOW_SEQ_START: ParseFlow(pScanner); break; + case TT_BLOCK_SEQ_START: ParseBlock(pScanner, state); break; + case TT_BLOCK_ENTRY: ParseImplicit(pScanner, state); break; + case TT_FLOW_SEQ_START: ParseFlow(pScanner, state); break; } } - void Sequence::ParseBlock(Scanner *pScanner) + void Sequence::ParseBlock(Scanner *pScanner, const ParserState& state) { // eat start token pScanner->EatNextToken(); @@ -55,11 +55,11 @@ namespace YAML Node *pNode = new Node; m_data.push_back(pNode); - pNode->Parse(pScanner); + pNode->Parse(pScanner, state); } } - void Sequence::ParseImplicit(Scanner *pScanner) + void Sequence::ParseImplicit(Scanner *pScanner, const ParserState& state) { while(1) { Token *pToken = pScanner->PeekNextToken(); @@ -75,11 +75,11 @@ namespace YAML Node *pNode = new Node; m_data.push_back(pNode); - pNode->Parse(pScanner); + pNode->Parse(pScanner, state); } } - void Sequence::ParseFlow(Scanner *pScanner) + void Sequence::ParseFlow(Scanner *pScanner, const ParserState& state) { // eat start token pScanner->EatNextToken(); @@ -98,7 +98,7 @@ namespace YAML // then read the node Node *pNode = new Node; m_data.push_back(pNode); - pNode->Parse(pScanner); + 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) pToken = pScanner->PeekNextToken(); diff --git a/sequence.h b/sequence.h index 42e9fbb..85fcf8a 100644 --- a/sequence.h +++ b/sequence.h @@ -14,13 +14,13 @@ namespace YAML virtual ~Sequence(); void Clear(); - virtual void Parse(Scanner *pScanner); + virtual void Parse(Scanner *pScanner, const ParserState& state); virtual void Write(std::ostream& out, int indent); private: - void ParseBlock(Scanner *pScanner); - void ParseImplicit(Scanner *pScanner); - void ParseFlow(Scanner *pScanner); + void ParseBlock(Scanner *pScanner, const ParserState& state); + void ParseImplicit(Scanner *pScanner, const ParserState& state); + void ParseFlow(Scanner *pScanner, const ParserState& state); protected: std::vector m_data; diff --git a/test.yaml b/test.yaml index 4b98eea..8a9714c 100644 --- a/test.yaml +++ b/test.yaml @@ -1,29 +1,97 @@ ---- ! -invoice: 34843 -date : 2001-01-23 -bill-to: &id001 - given : Chris - family : Dumars - address: - lines: | - 458 Walkman Dr. - Suite #292 - city : Royal Oak - state : MI - postal : 48046 -ship-to: *id001 -product: - - sku : BL394D - quantity : 4 - description : Basketball - price : 450.00 - - sku : BL4438H - quantity : 1 - description : Super Hoop - price : 2392.00 -tax : 251.42 -total: 4443.52 -comments: - Late afternoon is best. - Backup contact is Nancy - Billsmer @ 338-4338. \ No newline at end of file +--- +model: + file: data/models/compound.model + textures: data/materials/compound +rooms: + - name: "Room #1" + pos: [0, 0, 0] + size: [1000, 1000, 500] + height: 500 + stairtype: none + display: [] + pathfinding: + tilesize: 50 + size: [24, 24] + map: | + ----------------------- + -+++++++++++++++++++++- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+--------------------- + -+--------------------- + -+--------------------- + -+--------------------- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+++++++++++++++++++++- + ----------------------- + - name: Doorway + pos: [1000, 400, 0] + size: [50, 200, 500] + height: 500 + stairtype: none + display: [] + pathfinding: + tilesize: 50 + size: [5, 9] + map: | + ----- + -+++- + ----- + ----- + ----- + ----- + ----- + -+++- + ----- + - name: "Room #2" + pos: [1050, 0, 0] + size: [1000, 1000, 500] + height: 500 + stairtype: none + display: [] + pathfinding: + tilesize: 50 + size: [24, 24] + map: | + ----------------------- + -+++++++++++++++++++++- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+-------------------+- + ---------------------+- + ---------------------+- + ---------------------+- + ---------------------+- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+-------------------+- + -+++++++++++++++++++++- + ----------------------- +exits: + - room1: "Room #1" + room2: "Room #2" + dir: e + pos: [400, 600] diff --git a/yaml-reader.vcproj b/yaml-reader.vcproj index 4f8d382..62387b0 100644 --- a/yaml-reader.vcproj +++ b/yaml-reader.vcproj @@ -204,6 +204,10 @@ RelativePath=".\parser.cpp" > + + + +