/* Ousía Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include "ParserScope.hpp" #include "ParserStack.hpp" namespace ousia { /* A default handler */ /** * The DefaultHandler class is used in case no element handler is specified in * the ParserState descriptor. */ class DefaultHandler : public Handler { public: using Handler::Handler; void start(Variant::mapType &args) override {} void end() override {} static Handler *create(const HandlerData &handlerData) { return new DefaultHandler{handlerData}; } }; /* Class Handler */ void Handler::data(const std::string &data, int field) { for (auto &c : data) { if (!Utils::isWhitespace(c)) { logger().error("Expected command but found character data."); return; } } } /* Class ParserStack */ /** * Returns an Exception that should be thrown when a currently invalid command * is thrown. */ static LoggableException InvalidCommand(const std::string &name, const std::set &expected) { if (expected.empty()) { return LoggableException{ std::string{"No nested elements allowed, but got \""} + name + std::string{"\""}}; } else { return LoggableException{ std::string{"Expected "} + (expected.size() == 1 ? std::string{"\""} : std::string{"one of \""}) + Utils::join(expected, "\", \"") + std::string{"\", but got \""} + name + std::string{"\""}}; } } ParserStack::ParserStack( ParserContext &ctx, const std::multimap &states) : ctx(ctx), states(states) { } bool ParserStack::deduceState() { // Assemble all states std::vector states; for (const auto &e : this->states) { states.push_back(e.second); } // Fetch the type signature of the scope and derive all possible states, // abort if no unique parser state was found std::vector possibleStates = ParserStateDeductor(ctx.getScope().getStackTypeSignature(), states) .deduce(); if (possibleStates.size() != 1) { ctx.getLogger().error( "Error while including file: Cannot deduce parser state."); return false; } // Switch to this state by creating a dummy handler const ParserState *state = possibleStates[0]; Handler *handler = DefaultHandler::create({ctx, "", *state, *state, SourceLocation{}}); stack.emplace(handler); return true; } std::set ParserStack::expectedCommands(const ParserState &state) { std::set res; for (const auto &v : states) { if (v.second->parents.count(&state)) { res.insert(v.first); } } return res; } const ParserState &ParserStack::currentState() { return stack.empty() ? ParserStates::None : stack.top()->state(); } std::string ParserStack::currentCommandName() { return stack.empty() ? std::string{} : stack.top()->name(); } void ParserStack::start(std::string name, Variant::mapType &args, const SourceLocation &location) { // Fetch the current handler and the current state ParserState const *currentState = &(this->currentState()); // Fetch the correct Handler descriptor for this ParserState const *targetState = nullptr; HandlerConstructor ctor = nullptr; auto range = states.equal_range(name); for (auto it = range.first; it != range.second; it++) { const ParserStateSet &parents = it->second->parents; if (parents.count(currentState) || parents.count(&ParserStates::All)) { targetState = it->second; ctor = targetState->elementHandler ? targetState->elementHandler : DefaultHandler::create; break; } } // Try to use the child handler if one was given if (!targetState && currentState->childHandler) { targetState = currentState; ctor = targetState->childHandler; } // No descriptor found, throw an exception. if (!targetState || !ctor) { throw InvalidCommand(name, expectedCommands(*currentState)); } // Canonicalize the arguments, allow additional arguments targetState->arguments.validateMap(args, ctx.getLogger(), true); // Instantiate the handler and call its start function Handler *handler = ctor({ctx, name, *targetState, *currentState, location}); handler->start(args); stack.emplace(handler); } void ParserStack::start(std::string name, const Variant::mapType &args, const SourceLocation &location) { Variant::mapType argsCopy(args); start(name, argsCopy); } void ParserStack::end() { // Check whether the current command could be ended if (stack.empty()) { throw LoggableException{"No command to end."}; } // Remove the current HandlerInstance from the stack std::shared_ptr inst{stack.top()}; stack.pop(); // Call the end function of the last Handler inst->end(); } void ParserStack::data(const std::string &data, int field) { // Check whether there is any command the data can be sent to if (stack.empty()) { throw LoggableException{"No command to receive data."}; } // Pass the data to the current Handler instance stack.top()->data(data, field); } }