#include "scanner.h" #include "token.h" #include "exceptions.h" #include "exp.h" #include #include namespace YAML { Scanner::Scanner(std::istream& in) : INPUT(in), m_startedStream(false), m_endedStream(false), m_simpleKeyAllowed(false), m_canBeJSONFlow(false) { } Scanner::~Scanner() { for(unsigned i=0;i 0) INPUT.ResetColumn(); PopAllIndents(); PopAllSimpleKeys(); m_simpleKeyAllowed = false; m_endedStream = true; } Token *Scanner::PushToken(Token::TYPE type) { m_tokens.push(Token(type, INPUT.mark())); return &m_tokens.back(); } Token::TYPE Scanner::GetStartTokenFor(IndentMarker::INDENT_TYPE type) const { switch(type) { case IndentMarker::SEQ: return Token::BLOCK_SEQ_START; case IndentMarker::MAP: return Token::BLOCK_MAP_START; case IndentMarker::NONE: assert(false); break; } assert(false); } // PushIndentTo // . Pushes an indentation onto the stack, and enqueues the // proper token (sequence start or mapping start). // . Returns the indent marker it generates (if any). Scanner::IndentMarker *Scanner::PushIndentTo(int column, IndentMarker::INDENT_TYPE type) { // are we in flow? if(InFlowContext()) return 0; std::auto_ptr pIndent(new IndentMarker(column, type)); IndentMarker& indent = *pIndent; const IndentMarker& lastIndent = *m_indents.top(); // is this actually an indentation? if(indent.column < lastIndent.column) return 0; if(indent.column == lastIndent.column && !(indent.type == IndentMarker::SEQ && lastIndent.type == IndentMarker::MAP)) return 0; // push a start token indent.pStartToken = PushToken(GetStartTokenFor(type)); // and then the indent m_indents.push(&indent); m_indentRefs.push_back(pIndent.release()); return m_indentRefs.back(); } // PopIndentToHere // . Pops indentations off the stack until we reach the current indentation level, // and enqueues the proper token each time. // . Then pops all invalid indentations off. void Scanner::PopIndentToHere() { // are we in flow? if(InFlowContext()) return; // now pop away while(!m_indents.empty()) { const IndentMarker& indent = *m_indents.top(); if(indent.column < INPUT.column()) break; if(indent.column == INPUT.column() && !(indent.type == IndentMarker::SEQ && !Exp::BlockEntry().Matches(INPUT))) break; PopIndent(); } while(!m_indents.empty() && m_indents.top()->status == IndentMarker::INVALID) PopIndent(); } // PopAllIndents // . Pops all indentations (except for the base empty one) off the stack, // and enqueues the proper token each time. void Scanner::PopAllIndents() { // are we in flow? if(InFlowContext()) return; // now pop away while(!m_indents.empty()) { const IndentMarker& indent = *m_indents.top(); if(indent.type == IndentMarker::NONE) break; PopIndent(); } } // PopIndent // . Pops a single indent, pushing the proper token void Scanner::PopIndent() { const IndentMarker& indent = *m_indents.top(); m_indents.pop(); if(indent.status != IndentMarker::VALID) { InvalidateSimpleKey(); return; } if(indent.type == IndentMarker::SEQ) m_tokens.push(Token(Token::BLOCK_SEQ_END, INPUT.mark())); else if(indent.type == IndentMarker::MAP) m_tokens.push(Token(Token::BLOCK_MAP_END, INPUT.mark())); } // GetTopIndent int Scanner::GetTopIndent() const { if(m_indents.empty()) return 0; 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). // . Does not parse any more tokens. void Scanner::ThrowParserException(const std::string& msg) const { Mark mark = Mark::null(); if(!m_tokens.empty()) { const Token& token = m_tokens.front(); mark = token.mark; } throw ParserException(mark, msg); } void Scanner::ClearAnchors() { m_anchors.clear(); } }