From 4228ca01dc2536a172ecec9c385a296afd719a36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20L=C3=BChne?= Date: Tue, 7 Jun 2016 15:54:01 +0200 Subject: [PATCH] Implemented multi-file input. --- apps/plasp-app/main.cpp | 15 ++++- include/plasp/pddl/Description.h | 2 +- include/plasp/pddl/Domain.h | 3 +- include/plasp/pddl/Problem.h | 2 + include/plasp/utils/Parser.h | 8 ++- include/plasp/utils/ParserException.h | 2 +- include/plasp/utils/ParserWarning.h | 2 +- src/plasp/pddl/Description.cpp | 71 +++++++++++++------- src/plasp/pddl/Domain.cpp | 29 +++++++- src/plasp/pddl/Problem.cpp | 30 ++++++++- src/plasp/pddl/expressions/PrimitiveType.cpp | 13 +--- src/plasp/utils/Logger.cpp | 2 +- src/plasp/utils/Parser.cpp | 26 ++++++- 13 files changed, 154 insertions(+), 51 deletions(-) diff --git a/apps/plasp-app/main.cpp b/apps/plasp-app/main.cpp index dbd298c..c97eb06 100644 --- a/apps/plasp-app/main.cpp +++ b/apps/plasp-app/main.cpp @@ -16,7 +16,7 @@ int main(int argc, char **argv) description.add_options() ("help,h", "Display this help message.") ("version,v", "Display version information.") - ("input,i", po::value(), "Specify the SAS input file.") + ("input,i", po::value>(), "Specify the SAS input file.") ("format,f", po::value()->default_value("SAS"), "Specify the file format (SAS or PDDL)."); po::positional_options_description positionalOptionsDescription; @@ -66,10 +66,19 @@ int main(int argc, char **argv) auto format = variablesMap["format"].as(); std::transform(format.begin(), format.end(), format.begin(), ::tolower); + const auto &inputFiles = variablesMap["input"].as>(); + if (format == "sas") { + if (inputFiles.size() > 0) + { + std::cerr << "Error: Only one input file allowed for SAS translation" << std::endl; + printHelp(); + return EXIT_FAILURE; + } + const auto sasDescription = variablesMap.count("input") - ? plasp::sas::Description::fromFile(variablesMap["input"].as()) + ? plasp::sas::Description::fromFile(inputFiles.front()) : plasp::sas::Description::fromStream(std::cin); const auto sasTranslator = plasp::sas::TranslatorASP(sasDescription); sasTranslator.translate(std::cout); @@ -77,7 +86,7 @@ int main(int argc, char **argv) else if (format == "pddl") { const auto pddlDescription = variablesMap.count("input") - ? plasp::pddl::Description::fromFile(variablesMap["input"].as()) + ? plasp::pddl::Description::fromFiles(inputFiles) : plasp::pddl::Description::fromStream(std::cin); //std::cout << pddlDescription << std::endl; } diff --git a/include/plasp/pddl/Description.h b/include/plasp/pddl/Description.h index d261661..e13be7c 100644 --- a/include/plasp/pddl/Description.h +++ b/include/plasp/pddl/Description.h @@ -22,7 +22,7 @@ class Description { public: static Description fromStream(std::istream &istream); - static Description fromFile(const boost::filesystem::path &path); + static Description fromFiles(const std::vector &paths); public: const Domain &domain() const; diff --git a/include/plasp/pddl/Domain.h b/include/plasp/pddl/Domain.h index 22c4fce..01b2689 100644 --- a/include/plasp/pddl/Domain.h +++ b/include/plasp/pddl/Domain.h @@ -30,9 +30,11 @@ class Domain bool isDeclared() const; + void setName(std::string name); const std::string &name() const; const Requirements &requirements() const; + bool hasRequirement(Requirement::Type requirementType) const; expressions::PrimitiveTypes &types(); const expressions::PrimitiveTypes &types() const; @@ -52,7 +54,6 @@ class Domain void parseSection(); void parseRequirementSection(); - bool hasRequirement(Requirement::Type requirementType) const; void computeDerivedRequirements(); void parseTypeSection(); diff --git a/include/plasp/pddl/Problem.h b/include/plasp/pddl/Problem.h index 2154290..7a4c434 100644 --- a/include/plasp/pddl/Problem.h +++ b/include/plasp/pddl/Problem.h @@ -44,6 +44,8 @@ class Problem bool hasRequirement(Requirement::Type requirementType) const; void computeDerivedRequirements(); + void parseDomainSection(); + void parseObjectSection(); Context &m_context; diff --git a/include/plasp/utils/Parser.h b/include/plasp/utils/Parser.h index 5b1b118..03bc69c 100644 --- a/include/plasp/utils/Parser.h +++ b/include/plasp/utils/Parser.h @@ -21,6 +21,11 @@ class Parser public: explicit Parser(std::istream &istream); + void setFileName(std::string fileName); + const std::string &fileName() const; + + void resetPosition(); + size_t row() const; size_t column() const; @@ -60,6 +65,7 @@ class Parser uint64_t parseIntegerBody(); std::istream &m_istream; + std::string m_fileName; std::istreambuf_iterator m_position; size_t m_row; @@ -67,7 +73,7 @@ class Parser bool m_isCaseSensitive; - bool m_endOfFile; + bool m_atEndOfFile; }; //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/plasp/utils/ParserException.h b/include/plasp/utils/ParserException.h index bb6e0ef..bd15682 100644 --- a/include/plasp/utils/ParserException.h +++ b/include/plasp/utils/ParserException.h @@ -31,7 +31,7 @@ class ParserException: public std::exception } explicit ParserException(const utils::Parser &parser, const std::string &message) - : m_message{std::to_string(parser.row()) + ":" + std::to_string(parser.column()) + "\t" + message} + : m_message{parser.fileName() + ":" + std::to_string(parser.row()) + ":" + std::to_string(parser.column()) + " " + message} { } diff --git a/include/plasp/utils/ParserWarning.h b/include/plasp/utils/ParserWarning.h index 060ef9a..cc0842c 100644 --- a/include/plasp/utils/ParserWarning.h +++ b/include/plasp/utils/ParserWarning.h @@ -31,7 +31,7 @@ class ParserWarning: public std::exception } explicit ParserWarning(const utils::Parser &parser, const std::string &message) - : m_message{std::to_string(parser.row()) + ":" + std::to_string(parser.column()) + "\t" + message} + : m_message{parser.fileName() + ":" + std::to_string(parser.row()) + ":" + std::to_string(parser.column()) + " " + message} { } diff --git a/src/plasp/pddl/Description.cpp b/src/plasp/pddl/Description.cpp index 72d3e62..e14ce9b 100644 --- a/src/plasp/pddl/Description.cpp +++ b/src/plasp/pddl/Description.cpp @@ -20,7 +20,9 @@ namespace pddl Description::Description(std::istream &istream) : m_parser(istream), - m_context(m_parser) + m_context(m_parser), + m_domain{std::make_unique(Domain(m_context))}, + m_problem{std::make_unique(Problem(m_context, *m_domain))} { m_parser.setCaseSensitive(false); } @@ -31,19 +33,9 @@ Description Description::fromStream(std::istream &istream) { Description description(istream); - description.m_domain = std::make_unique(Domain(description.m_context)); - description.m_problem = std::make_unique(Problem(description.m_context, *description.m_domain)); - - while (true) - { - description.m_context.parser.skipWhiteSpace(); - - if (description.m_context.parser.atEndOfFile()) - break; - - description.parseContent(); - } + description.m_parser.setFileName("std::cin"); + description.parseContent(); description.checkConsistency(); return description; @@ -51,14 +43,36 @@ Description Description::fromStream(std::istream &istream) //////////////////////////////////////////////////////////////////////////////////////////////////// -Description Description::fromFile(const boost::filesystem::path &path) +Description Description::fromFiles(const std::vector &paths) { - if (!boost::filesystem::is_regular_file(path)) - throw std::runtime_error("File does not exist: \"" + path.string() + "\""); + BOOST_ASSERT(!paths.empty()); - std::ifstream fileStream(path.string(), std::ios::in); + std::for_each(paths.cbegin(), paths.cend(), + [&](const auto &path) + { + if (!boost::filesystem::is_regular_file(path)) + throw std::runtime_error("File does not exist: \"" + path + "\""); + }); - return Description::fromStream(fileStream); + std::ifstream fileStream; + Description description(fileStream); + + std::for_each(paths.cbegin(), paths.cend(), + [&](const auto &path) + { + fileStream.close(); + fileStream.clear(); + fileStream.open(path, std::ios::in); + + description.m_parser.setFileName(path); + description.m_parser.resetPosition(); + + description.parseContent(); + }); + + description.checkConsistency(); + + return description; } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -74,12 +88,18 @@ const Domain &Description::domain() const void Description::parseContent() { - std::cout << "Parsing file content" << std::endl; + while (true) + { + m_context.parser.skipWhiteSpace(); - m_context.parser.expect("("); - m_context.parser.expect("define"); - parseSection(); - m_context.parser.expect(")"); + if (m_context.parser.atEndOfFile()) + return; + + m_context.parser.expect("("); + m_context.parser.expect("define"); + parseSection(); + m_context.parser.expect(")"); + } } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -91,8 +111,6 @@ void Description::parseSection() const auto sectionIdentifier = m_context.parser.parse(); - std::cout << "Parsing section " << sectionIdentifier << std::endl; - if (sectionIdentifier == "domain") { BOOST_ASSERT(m_domain); @@ -119,6 +137,9 @@ void Description::parseSection() void Description::checkConsistency() { + if (!m_domain->isDeclared()) + throw ConsistencyException("No PDDL domain specified"); + m_domain->checkConsistency(); m_problem->checkConsistency(); } diff --git a/src/plasp/pddl/Domain.cpp b/src/plasp/pddl/Domain.cpp index 2d2d76b..b8c06a0 100644 --- a/src/plasp/pddl/Domain.cpp +++ b/src/plasp/pddl/Domain.cpp @@ -32,7 +32,13 @@ Domain::Domain(Context &context) void Domain::readPDDL() { - m_name = m_context.parser.parseIdentifier(isIdentifier); + const auto domainName = m_context.parser.parseIdentifier(isIdentifier); + + if (m_name.empty()) + m_name = domainName; + // If the domain has previously been referenced, check that the name matches + else if (m_name != domainName) + throw utils::ParserException(m_context.parser, "Domains do not match (\"" + domainName + "\" and \"" + m_name + "\")"); std::cout << "Parsing domain " << m_name << std::endl; @@ -62,6 +68,13 @@ bool Domain::isDeclared() const //////////////////////////////////////////////////////////////////////////////////////////////////// +void Domain::setName(std::string name) +{ + m_name = name; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + const std::string &Domain::name() const { return m_name; @@ -334,11 +347,22 @@ void Domain::checkConsistency() // Verify that typing requirement is correctly declared if used if (!m_primitiveTypes.empty() && !hasRequirement(Requirement::Type::Typing)) { - throw ConsistencyException("Domain contains typing information but does not declare typing requirement"); + m_context.logger.parserWarning(m_context.parser, "Domain contains typing information but does not declare typing requirement"); m_requirements.push_back(Requirement(Requirement::Type::Typing)); } + // Verify that all variables and constants have types + if (hasRequirement(Requirement::Type::Typing)) + { + std::for_each(m_constants.cbegin(), m_constants.cend(), + [&](const auto &constant) + { + if (constant->type() == nullptr) + throw ConsistencyException("Constant \"" + constant->name() + "\" has no type"); + }); + } + // Verify that all used types have been declared std::for_each(m_primitiveTypes.cbegin(), m_primitiveTypes.cend(), [&](const auto &type) @@ -366,7 +390,6 @@ void Domain::checkConsistency() throw ConsistencyException("Predicate \"" + predicate->name() + "\" used but never declared"); }); - // Verify that all variables have types // Verify that constants are unique // Verify that all primitive types are unique // Check for case-sensitivity issues diff --git a/src/plasp/pddl/Problem.cpp b/src/plasp/pddl/Problem.cpp index 3f3738b..5a154bc 100644 --- a/src/plasp/pddl/Problem.cpp +++ b/src/plasp/pddl/Problem.cpp @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -134,7 +135,7 @@ void Problem::parseSection() // TODO: check order of the sections if (sectionIdentifier == "domain") - skipSection(); + parseDomainSection(); else if (sectionIdentifier == "requirements") parseRequirementSection(); else if (sectionIdentifier == "objects") @@ -155,6 +156,23 @@ void Problem::parseSection() //////////////////////////////////////////////////////////////////////////////////////////////////// +void Problem::parseDomainSection() +{ + m_context.parser.skipWhiteSpace(); + + const auto domainName = m_context.parser.parseIdentifier(isIdentifier); + + if (m_domain.isDeclared() && m_domain.name() != domainName) + throw utils::ParserException(m_context.parser, "Domains do not match (\"" + m_domain.name() + "\" and \"" + domainName + "\")"); + + if (!m_domain.isDeclared()) + m_domain.setName(domainName); + + m_context.parser.expect(")"); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + void Problem::parseRequirementSection() { m_context.parser.skipWhiteSpace(); @@ -238,7 +256,7 @@ void Problem::parseObjectSection() // Store constants while (m_context.parser.currentCharacter() != ')') { - //expressions::Constant::parseTypedDeclaration(m_context); + expressions::Constant::parseTypedDeclaration(m_context, *this); m_context.parser.skipWhiteSpace(); } @@ -250,6 +268,14 @@ void Problem::parseObjectSection() void Problem::checkConsistency() { + // Verify that all objects have types + if (hasRequirement(Requirement::Type::Typing) || m_domain.hasRequirement(Requirement::Type::Typing)) + std::for_each(m_objects.cbegin(), m_objects.cend(), + [&](const auto &constant) + { + if (constant->type() == nullptr) + throw ConsistencyException("Object \"" + constant->name() + "\" has no type"); + }); } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/plasp/pddl/expressions/PrimitiveType.cpp b/src/plasp/pddl/expressions/PrimitiveType.cpp index cb708c8..6ed8f59 100644 --- a/src/plasp/pddl/expressions/PrimitiveType.cpp +++ b/src/plasp/pddl/expressions/PrimitiveType.cpp @@ -61,11 +61,13 @@ void PrimitiveType::parseDeclaration(Context &context, Domain &domain) auto *type = match->get(); type->setDirty(); + type->setDeclared(); return; } types.emplace_back(std::make_unique(typeName)); + types.back()->setDeclared(); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -77,11 +79,6 @@ void PrimitiveType::parseTypedDeclaration(Context &context, Domain &domain) // Parse and store type parseDeclaration(context, domain); - auto *type = types.back().get(); - - // Flag type as correctly declared in the types section - type->setDeclared(); - context.parser.skipWhiteSpace(); // Check for type inheritance @@ -129,12 +126,8 @@ PrimitiveType *PrimitiveType::parseAndFindOrCreate(Context &context, Domain &dom if (match == types.cend()) { - // Primitive type "object" is implicitly declared - if (typeName != "object") - context.logger.parserWarning(context.parser, "Primitive type \"" + typeName + "\" used but never declared"); - + // If necessary, insert new primitive type but don't declare it types.emplace_back(std::make_unique(typeName)); - types.back()->setDeclared(); return types.back().get(); } diff --git a/src/plasp/utils/Logger.cpp b/src/plasp/utils/Logger.cpp index fa3d0aa..f66d29b 100644 --- a/src/plasp/utils/Logger.cpp +++ b/src/plasp/utils/Logger.cpp @@ -32,7 +32,7 @@ void Logger::parserWarning(const Parser &parser, const std::string &text) if (m_isPedantic) throw ParserWarning(parser, text); - std::cerr << "Warning: " << parser.row() << ":" << parser.column() << "\t" << text << std::endl; + std::cerr << "Warning: " << parser.fileName() << ":" << parser.row() << ":" << parser.column() << " " << text << std::endl; } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/plasp/utils/Parser.cpp b/src/plasp/utils/Parser.cpp index dddaec4..ac15593 100644 --- a/src/plasp/utils/Parser.cpp +++ b/src/plasp/utils/Parser.cpp @@ -27,14 +27,36 @@ Parser::Parser(std::istream &istream) m_row{1}, m_column{1}, m_isCaseSensitive{true}, - m_endOfFile{false} + m_atEndOfFile{false} { std::setlocale(LC_NUMERIC, "C"); // Don’t skip whitespace istream.exceptions(std::istream::badbit); +} - checkStream(); +//////////////////////////////////////////////////////////////////////////////////////////////////// + +void Parser::setFileName(std::string fileName) +{ + m_fileName = fileName; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +const std::string &Parser::fileName() const +{ + return m_fileName; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +void Parser::resetPosition() +{ + m_row = 1; + m_column = 1; + m_atEndOfFile = false; + m_position = std::istreambuf_iterator(m_istream); } ////////////////////////////////////////////////////////////////////////////////////////////////////