diff --git a/include/plasp/sas/AssignedVariable.h b/include/plasp/sas/AssignedVariable.h index 1e72fb6..92c9523 100644 --- a/include/plasp/sas/AssignedVariable.h +++ b/include/plasp/sas/AssignedVariable.h @@ -6,6 +6,7 @@ #include #include +#include namespace plasp { @@ -26,8 +27,8 @@ using AssignedVariables = std::vector; class AssignedVariable { public: - static AssignedVariable fromSAS(std::istream &istream, const Variables &variables); - static AssignedVariable fromSAS(std::istream &istream, const Variable &variable); + static AssignedVariable fromSAS(utils::Parser &parser, const Variables &variables); + static AssignedVariable fromSAS(utils::Parser &parser, const Variable &variable); public: explicit AssignedVariable(const Variable &variable, const Value &value); diff --git a/include/plasp/sas/AxiomRule.h b/include/plasp/sas/AxiomRule.h index 0455527..b59d1a1 100644 --- a/include/plasp/sas/AxiomRule.h +++ b/include/plasp/sas/AxiomRule.h @@ -5,6 +5,7 @@ #include #include +#include namespace plasp { @@ -28,7 +29,7 @@ class AxiomRule using Condition = AssignedVariable; using Conditions = AssignedVariables; - static AxiomRule fromSAS(std::istream &istream, const Variables &variables); + static AxiomRule fromSAS(utils::Parser &parser, const Variables &variables); public: const Conditions &conditions() const; diff --git a/include/plasp/sas/Description.h b/include/plasp/sas/Description.h index f4d64ff..ae310e3 100644 --- a/include/plasp/sas/Description.h +++ b/include/plasp/sas/Description.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace plasp { @@ -46,14 +47,14 @@ class Description private: Description(); - void parseVersionSection(std::istream &istream) const; - void parseMetricSection(std::istream &istream); - void parseVariablesSection(std::istream &istream); - void parseMutexSection(std::istream &istream); - void parseInitialStateSection(std::istream &istream); - void parseGoalSection(std::istream &istream); - void parseOperatorSection(std::istream &istream); - void parseAxiomSection(std::istream &istream); + void parseVersionSection(utils::Parser &parser) const; + void parseMetricSection(utils::Parser &parser); + void parseVariablesSection(utils::Parser &parser); + void parseMutexSection(utils::Parser &parser); + void parseInitialStateSection(utils::Parser &parser); + void parseGoalSection(utils::Parser &parser); + void parseOperatorSection(utils::Parser &parser); + void parseAxiomSection(utils::Parser &parser); bool m_usesActionCosts; diff --git a/include/plasp/sas/Effect.h b/include/plasp/sas/Effect.h index 20619c7..947bf09 100644 --- a/include/plasp/sas/Effect.h +++ b/include/plasp/sas/Effect.h @@ -5,6 +5,7 @@ #include #include +#include namespace plasp { @@ -28,7 +29,7 @@ class Effect using Condition = AssignedVariable; using Conditions = AssignedVariables; - static Effect fromSAS(std::istream &istream, const Variables &variables, Conditions &preconditions); + static Effect fromSAS(utils::Parser &parser, const Variables &variables, Conditions &preconditions); public: const Conditions &conditions() const; diff --git a/include/plasp/sas/Goal.h b/include/plasp/sas/Goal.h index c39f462..ad0ee82 100644 --- a/include/plasp/sas/Goal.h +++ b/include/plasp/sas/Goal.h @@ -2,6 +2,7 @@ #define __PLASP__SAS__GOAL_H #include +#include namespace plasp { @@ -20,7 +21,7 @@ class Goal using Fact = AssignedVariable; using Facts = AssignedVariables; - static Goal fromSAS(std::istream &istream, const Variables &variables); + static Goal fromSAS(utils::Parser &parser, const Variables &variables); public: const Facts &facts() const; diff --git a/include/plasp/sas/InitialState.h b/include/plasp/sas/InitialState.h index 0149a56..59c0ae8 100644 --- a/include/plasp/sas/InitialState.h +++ b/include/plasp/sas/InitialState.h @@ -2,6 +2,7 @@ #define __PLASP__SAS__INITIAL_STATE_H #include +#include namespace plasp { @@ -20,7 +21,7 @@ class InitialState using Fact = AssignedVariable; using Facts = AssignedVariables; - static InitialState fromSAS(std::istream &istream, const Variables &variables); + static InitialState fromSAS(utils::Parser &parser, const Variables &variables); public: const Facts &facts() const; diff --git a/include/plasp/sas/MutexGroup.h b/include/plasp/sas/MutexGroup.h index 73cf6d6..0965772 100644 --- a/include/plasp/sas/MutexGroup.h +++ b/include/plasp/sas/MutexGroup.h @@ -4,6 +4,7 @@ #include #include +#include namespace plasp { @@ -27,7 +28,7 @@ class MutexGroup using Fact = AssignedVariable; using Facts = AssignedVariables; - static MutexGroup fromSAS(std::istream &istream, const Variables &variables); + static MutexGroup fromSAS(utils::Parser &parser, const Variables &variables); public: const Facts &facts() const; diff --git a/include/plasp/sas/Operator.h b/include/plasp/sas/Operator.h index 9edeb6b..2913df4 100644 --- a/include/plasp/sas/Operator.h +++ b/include/plasp/sas/Operator.h @@ -8,6 +8,7 @@ #include #include #include +#include namespace plasp { @@ -31,7 +32,7 @@ class Operator using Condition = AssignedVariable; using Conditions = AssignedVariables; - static Operator fromSAS(std::istream &istream, const Variables &variables); + static Operator fromSAS(utils::Parser &parser, const Variables &variables); public: void printPredicateAsASP(std::ostream &ostream) const; diff --git a/include/plasp/sas/Predicate.h b/include/plasp/sas/Predicate.h index 1531fbf..9052a46 100644 --- a/include/plasp/sas/Predicate.h +++ b/include/plasp/sas/Predicate.h @@ -5,6 +5,8 @@ #include #include +#include + namespace plasp { namespace sas @@ -19,7 +21,7 @@ namespace sas class Predicate { public: - static Predicate fromSAS(std::istream &istream); + static Predicate fromSAS(utils::Parser &parser); using Arguments = std::vector; diff --git a/include/plasp/sas/Value.h b/include/plasp/sas/Value.h index ca0c648..6cdddfb 100644 --- a/include/plasp/sas/Value.h +++ b/include/plasp/sas/Value.h @@ -5,6 +5,8 @@ #include #include +#include + namespace plasp { namespace sas @@ -36,8 +38,8 @@ struct Value static const Value Any; static const Value None; - static Value fromSAS(std::istream &istream); - static const Value &referenceFromSAS(std::istream &istream, const Variable &variable); + static Value fromSAS(utils::Parser &parser); + static const Value &referenceFromSAS(utils::Parser &parser, const Variable &variable); public: Value negated() const; diff --git a/include/plasp/sas/Variable.h b/include/plasp/sas/Variable.h index ce310a9..0fcc83c 100644 --- a/include/plasp/sas/Variable.h +++ b/include/plasp/sas/Variable.h @@ -6,6 +6,7 @@ #include #include +#include namespace plasp { @@ -26,8 +27,8 @@ using Variables = std::vector; class Variable { public: - static Variable fromSAS(std::istream &istream); - static const Variable &referenceFromSAS(std::istream &istream, const Variables &variables); + static Variable fromSAS(utils::Parser &parser); + static const Variable &referenceFromSAS(utils::Parser &parser, const Variables &variables); public: void printNameAsASPPredicate(std::ostream &ostream) const; diff --git a/include/plasp/sas/VariableTransition.h b/include/plasp/sas/VariableTransition.h index aae2a8f..b38659f 100644 --- a/include/plasp/sas/VariableTransition.h +++ b/include/plasp/sas/VariableTransition.h @@ -5,6 +5,7 @@ #include #include +#include namespace plasp { @@ -25,7 +26,7 @@ using VariableTransitions = std::vector; class VariableTransition { public: - static VariableTransition fromSAS(std::istream &istream, const Variables &variables); + static VariableTransition fromSAS(utils::Parser &parser, const Variables &variables); public: const Variable &variable() const; diff --git a/include/plasp/utils/IO.h b/include/plasp/utils/IO.h new file mode 100644 index 0000000..d2d022e --- /dev/null +++ b/include/plasp/utils/IO.h @@ -0,0 +1,46 @@ +#ifndef __PLASP__UTILS__IO_H +#define __PLASP__UTILS__IO_H + +#include + +namespace plasp +{ +namespace utils +{ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// IO +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +inline std::string escapeASP(const std::string &string) +{ + auto escaped = string; + + boost::replace_all(escaped, "_", "__"); + boost::replace_all(escaped, "-", "_h"); + boost::replace_all(escaped, "@", "_a"); + + return escaped; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +inline std::string unescapeASP(const std::string &string) +{ + auto unescaped = string; + + boost::replace_all(unescaped, "_a", "@"); + boost::replace_all(unescaped, "_h", "-"); + boost::replace_all(unescaped, "__", "_"); + + return unescaped; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +} +} + +#endif diff --git a/include/plasp/utils/Parser.h b/include/plasp/utils/Parser.h new file mode 100644 index 0000000..06b9865 --- /dev/null +++ b/include/plasp/utils/Parser.h @@ -0,0 +1,62 @@ +#ifndef __PLASP__UTILS__PARSER_H +#define __PLASP__UTILS__PARSER_H + +#include +#include + +namespace plasp +{ +namespace utils +{ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Parser +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +class Parser +{ + public: + Parser(std::istream &istream); + + size_t row() const; + size_t column() const; + + template + T parse(); + + template + void expect(const T &expectedValue); + + void skipWhiteSpace(); + void skipLine(); + + std::string getLine(); + + private: + static const std::istream_iterator EndOfFile; + + private: + void checkStream() const; + void advance(); + + bool advanceIf(unsigned char expectedCharacter); + + uint64_t parseIntegerBody(); + + std::istream &m_istream; + std::istream_iterator m_position; + + size_t m_row; + size_t m_column; + + bool m_endOfFile; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +} +} + +#endif diff --git a/include/plasp/utils/ParserException.h b/include/plasp/utils/ParserException.h index e5de7d2..5a48593 100644 --- a/include/plasp/utils/ParserException.h +++ b/include/plasp/utils/ParserException.h @@ -18,17 +18,18 @@ namespace utils class ParserException: public std::exception { public: - explicit ParserException() + explicit ParserException(size_t row, size_t column) + : ParserException(row, column, "Unspecified parser error") { } - explicit ParserException(const char *message) - : m_message(message) + explicit ParserException(size_t row, size_t column, const char *message) + : ParserException(row, column, static_cast(message)) { } - explicit ParserException(const std::string &message) - : m_message(message) + explicit ParserException(size_t row, size_t column, const std::string &message) + : m_message{std::to_string(row) + ":" + std::to_string(column) + "\t" + message} { } @@ -39,7 +40,7 @@ class ParserException: public std::exception const char *what() const throw() { if (m_message.empty()) - return "Unspecified error while parsing SAS description file"; + return "Unspecified parser error"; return m_message.c_str(); } diff --git a/include/plasp/utils/Parsing.h b/include/plasp/utils/Parsing.h deleted file mode 100644 index b90536a..0000000 --- a/include/plasp/utils/Parsing.h +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef __PLASP__UTILS__PARSING_H -#define __PLASP__UTILS__PARSING_H - -#include -#include -#include -#include -#include -#include - -#include - -#include - -namespace plasp -{ -namespace utils -{ - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Parsing -// -//////////////////////////////////////////////////////////////////////////////////////////////////// - -template -T parse(std::istream &istream) -{ - T value; - - try - { - istream >> value; - } - catch (const std::exception &e) - { - throw ParserException(std::string("Could not parse value of type ") + typeid(T).name() + " (" + e.what() + ")"); - } - - return value; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -template -void parseExpected(std::istream &istream, const T &expectedValue) -{ - const auto value = parse(istream); - - if (value == expectedValue) - return; - - std::stringstream errorStream; - - errorStream << "Invalid format, expected " << expectedValue << ", got " + value; - - throw utils::ParserException(errorStream.str()); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -inline std::string escapeASP(const std::string &string) -{ - auto escaped = string; - - boost::replace_all(escaped, "_", "__"); - boost::replace_all(escaped, "-", "_h"); - boost::replace_all(escaped, "@", "_a"); - - return escaped; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -inline std::string unescapeASP(const std::string &string) -{ - auto unescaped = string; - - boost::replace_all(unescaped, "_a", "@"); - boost::replace_all(unescaped, "_h", "-"); - boost::replace_all(unescaped, "__", "_"); - - return unescaped; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -} -} - -#endif diff --git a/src/plasp/sas/AssignedVariable.cpp b/src/plasp/sas/AssignedVariable.cpp index 3fa9806..801b00c 100644 --- a/src/plasp/sas/AssignedVariable.cpp +++ b/src/plasp/sas/AssignedVariable.cpp @@ -4,8 +4,6 @@ #include -#include - namespace plasp { namespace sas @@ -33,24 +31,24 @@ AssignedVariable::AssignedVariable(const Variable &variable, const Value &value) //////////////////////////////////////////////////////////////////////////////////////////////////// -AssignedVariable AssignedVariable::fromSAS(std::istream &istream, const Variables &variables) +AssignedVariable AssignedVariable::fromSAS(utils::Parser &parser, const Variables &variables) { AssignedVariable assignedVariable; - assignedVariable.m_variable = &Variable::referenceFromSAS(istream, variables); - assignedVariable.m_value = &Value::referenceFromSAS(istream, *assignedVariable.m_variable); + assignedVariable.m_variable = &Variable::referenceFromSAS(parser, variables); + assignedVariable.m_value = &Value::referenceFromSAS(parser, *assignedVariable.m_variable); return assignedVariable; } //////////////////////////////////////////////////////////////////////////////////////////////////// -AssignedVariable AssignedVariable::fromSAS(std::istream &istream, const Variable &variable) +AssignedVariable AssignedVariable::fromSAS(utils::Parser &parser, const Variable &variable) { AssignedVariable assignedVariable; assignedVariable.m_variable = &variable; - assignedVariable.m_value = &Value::referenceFromSAS(istream, *assignedVariable.m_variable); + assignedVariable.m_value = &Value::referenceFromSAS(parser, *assignedVariable.m_variable); return assignedVariable; } diff --git a/src/plasp/sas/AxiomRule.cpp b/src/plasp/sas/AxiomRule.cpp index 221da55..d18c2eb 100644 --- a/src/plasp/sas/AxiomRule.cpp +++ b/src/plasp/sas/AxiomRule.cpp @@ -3,7 +3,6 @@ #include #include -#include namespace plasp { @@ -24,24 +23,24 @@ AxiomRule::AxiomRule(AxiomRule::Conditions conditions, AxiomRule::Condition post //////////////////////////////////////////////////////////////////////////////////////////////////// -AxiomRule AxiomRule::fromSAS(std::istream &istream, const Variables &variables) +AxiomRule AxiomRule::fromSAS(utils::Parser &parser, const Variables &variables) { - utils::parseExpected(istream, "begin_rule"); + parser.expect("begin_rule"); - const auto numberOfConditions = utils::parse(istream); + const auto numberOfConditions = parser.parse(); Conditions conditions; conditions.reserve(numberOfConditions); for (size_t j = 0; j < numberOfConditions; j++) - conditions.emplace_back(Condition::fromSAS(istream, variables)); + conditions.emplace_back(Condition::fromSAS(parser, variables)); - const auto variableTransition = VariableTransition::fromSAS(istream, variables); + const auto variableTransition = VariableTransition::fromSAS(parser, variables); if (&variableTransition.valueBefore() != &Value::Any) conditions.emplace_back(Condition(variableTransition.variable(), variableTransition.valueBefore())); - utils::parseExpected(istream, "end_rule"); + parser.expect("end_rule"); const Condition postcondition(variableTransition.variable(), variableTransition.valueAfter()); const AxiomRule axiomRule(std::move(conditions), std::move(postcondition)); diff --git a/src/plasp/sas/Description.cpp b/src/plasp/sas/Description.cpp index cac6ca8..ffb524f 100644 --- a/src/plasp/sas/Description.cpp +++ b/src/plasp/sas/Description.cpp @@ -8,7 +8,6 @@ #include #include -#include #include namespace plasp @@ -33,18 +32,16 @@ Description Description::fromStream(std::istream &istream) { Description description; - std::setlocale(LC_NUMERIC, "C"); + utils::Parser parser(istream); - istream.exceptions(std::ifstream::failbit | std::ifstream::badbit); - - description.parseVersionSection(istream); - description.parseMetricSection(istream); - description.parseVariablesSection(istream); - description.parseMutexSection(istream); - description.parseInitialStateSection(istream); - description.parseGoalSection(istream); - description.parseOperatorSection(istream); - description.parseAxiomSection(istream); + description.parseVersionSection(parser); + description.parseMetricSection(parser); + description.parseVariablesSection(parser); + description.parseMutexSection(parser); + description.parseInitialStateSection(parser); + description.parseGoalSection(parser); + description.parseOperatorSection(parser); + description.parseAxiomSection(parser); return description; } @@ -149,85 +146,85 @@ bool Description::usesConditionalEffects() const //////////////////////////////////////////////////////////////////////////////////////////////////// -void Description::parseVersionSection(std::istream &istream) const +void Description::parseVersionSection(utils::Parser &parser) const { - utils::parseExpected(istream, "begin_version"); + parser.expect("begin_version"); - const auto formatVersion = utils::parse(istream); + const auto formatVersion = parser.parse(); if (formatVersion != 3) - throw utils::ParserException("Unsupported SAS format version (" + std::to_string(formatVersion) + ")"); + throw utils::ParserException(parser.row(), parser.column(), "Unsupported SAS format version (" + std::to_string(formatVersion) + ")"); - utils::parseExpected(istream, "end_version"); + parser.expect("end_version"); } //////////////////////////////////////////////////////////////////////////////////////////////////// -void Description::parseMetricSection(std::istream &istream) +void Description::parseMetricSection(utils::Parser &parser) { - utils::parseExpected(istream, "begin_metric"); + parser.expect("begin_metric"); - m_usesActionCosts = utils::parse(istream); + m_usesActionCosts = parser.parse(); - utils::parseExpected(istream, "end_metric"); + parser.expect("end_metric"); } //////////////////////////////////////////////////////////////////////////////////////////////////// -void Description::parseVariablesSection(std::istream &istream) +void Description::parseVariablesSection(utils::Parser &parser) { - const auto numberOfVariables = utils::parse(istream); + const auto numberOfVariables = parser.parse(); m_variables.reserve(numberOfVariables); for (size_t i = 0; i < numberOfVariables; i++) - m_variables.emplace_back(Variable::fromSAS(istream)); + m_variables.emplace_back(Variable::fromSAS(parser)); } //////////////////////////////////////////////////////////////////////////////////////////////////// -void Description::parseMutexSection(std::istream &istream) +void Description::parseMutexSection(utils::Parser &parser) { - const auto numberOfMutexGroups = utils::parse(istream); + const auto numberOfMutexGroups = parser.parse(); m_mutexGroups.reserve(numberOfMutexGroups); for (size_t i = 0; i < numberOfMutexGroups; i++) - m_mutexGroups.emplace_back(MutexGroup::fromSAS(istream, m_variables)); + m_mutexGroups.emplace_back(MutexGroup::fromSAS(parser, m_variables)); } //////////////////////////////////////////////////////////////////////////////////////////////////// -void Description::parseInitialStateSection(std::istream &istream) +void Description::parseInitialStateSection(utils::Parser &parser) { - m_initialState = std::make_unique(InitialState::fromSAS(istream, m_variables)); + m_initialState = std::make_unique(InitialState::fromSAS(parser, m_variables)); } //////////////////////////////////////////////////////////////////////////////////////////////////// -void Description::parseGoalSection(std::istream &istream) +void Description::parseGoalSection(utils::Parser &parser) { - m_goal = std::make_unique(Goal::fromSAS(istream, m_variables)); + m_goal = std::make_unique(Goal::fromSAS(parser, m_variables)); } //////////////////////////////////////////////////////////////////////////////////////////////////// -void Description::parseOperatorSection(std::istream &istream) +void Description::parseOperatorSection(utils::Parser &parser) { - const auto numberOfOperators = utils::parse(istream); + const auto numberOfOperators = parser.parse(); m_operators.reserve(numberOfOperators); for (size_t i = 0; i < numberOfOperators; i++) - m_operators.emplace_back(Operator::fromSAS(istream, m_variables)); + m_operators.emplace_back(Operator::fromSAS(parser, m_variables)); } //////////////////////////////////////////////////////////////////////////////////////////////////// -void Description::parseAxiomSection(std::istream &istream) +void Description::parseAxiomSection(utils::Parser &parser) { - const auto numberOfAxiomRules = utils::parse(istream); + const auto numberOfAxiomRules = parser.parse(); m_axiomRules.reserve(numberOfAxiomRules); for (size_t i = 0; i < numberOfAxiomRules; i++) - m_axiomRules.emplace_back(AxiomRule::fromSAS(istream, m_variables)); + m_axiomRules.emplace_back(AxiomRule::fromSAS(parser, m_variables)); } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/plasp/sas/Effect.cpp b/src/plasp/sas/Effect.cpp index 0876e67..9f39e1f 100644 --- a/src/plasp/sas/Effect.cpp +++ b/src/plasp/sas/Effect.cpp @@ -3,7 +3,6 @@ #include #include -#include namespace plasp { @@ -24,17 +23,17 @@ Effect::Effect(Conditions conditions, Condition postcondition) //////////////////////////////////////////////////////////////////////////////////////////////////// -Effect Effect::fromSAS(std::istream &istream, const Variables &variables, Conditions &preconditions) +Effect Effect::fromSAS(utils::Parser &parser, const Variables &variables, Conditions &preconditions) { Effect::Conditions conditions; - const auto numberOfEffectConditions = utils::parse(istream); + const auto numberOfEffectConditions = parser.parse(); conditions.reserve(numberOfEffectConditions); for (size_t k = 0; k < numberOfEffectConditions; k++) - conditions.emplace_back(Condition::fromSAS(istream, variables)); + conditions.emplace_back(Condition::fromSAS(parser, variables)); - const auto variableTransition = VariableTransition::fromSAS(istream, variables); + const auto variableTransition = VariableTransition::fromSAS(parser, variables); if (&variableTransition.valueBefore() != &Value::Any) preconditions.emplace_back(Condition(variableTransition.variable(), variableTransition.valueBefore())); diff --git a/src/plasp/sas/Goal.cpp b/src/plasp/sas/Goal.cpp index 5292fca..69ddec1 100644 --- a/src/plasp/sas/Goal.cpp +++ b/src/plasp/sas/Goal.cpp @@ -2,8 +2,6 @@ #include -#include - namespace plasp { namespace sas @@ -15,19 +13,19 @@ namespace sas // //////////////////////////////////////////////////////////////////////////////////////////////////// -Goal Goal::fromSAS(std::istream &istream, const Variables &variables) +Goal Goal::fromSAS(utils::Parser &parser, const Variables &variables) { Goal goal; - utils::parseExpected(istream, "begin_goal"); + parser.expect("begin_goal"); - const auto numberOfGoalFacts = utils::parse(istream); + const auto numberOfGoalFacts = parser.parse(); goal.m_facts.reserve(numberOfGoalFacts); for (size_t i = 0; i < numberOfGoalFacts; i++) - goal.m_facts.emplace_back(Fact::fromSAS(istream, variables)); + goal.m_facts.emplace_back(Fact::fromSAS(parser, variables)); - utils::parseExpected(istream, "end_goal"); + parser.expect("end_goal"); return goal; } diff --git a/src/plasp/sas/InitialState.cpp b/src/plasp/sas/InitialState.cpp index d188b84..01a5383 100644 --- a/src/plasp/sas/InitialState.cpp +++ b/src/plasp/sas/InitialState.cpp @@ -2,8 +2,6 @@ #include -#include - namespace plasp { namespace sas @@ -15,18 +13,18 @@ namespace sas // //////////////////////////////////////////////////////////////////////////////////////////////////// -InitialState InitialState::fromSAS(std::istream &istream, const Variables &variables) +InitialState InitialState::fromSAS(utils::Parser &parser, const Variables &variables) { InitialState initialState; - utils::parseExpected(istream, "begin_state"); + parser.expect("begin_state"); initialState.m_facts.reserve(variables.size()); for (size_t i = 0; i < variables.size(); i++) - initialState.m_facts.emplace_back(Fact::fromSAS(istream, variables[i])); + initialState.m_facts.emplace_back(Fact::fromSAS(parser, variables[i])); - utils::parseExpected(istream, "end_state"); + parser.expect("end_state"); return initialState; } diff --git a/src/plasp/sas/MutexGroup.cpp b/src/plasp/sas/MutexGroup.cpp index 3688e5a..8709f84 100644 --- a/src/plasp/sas/MutexGroup.cpp +++ b/src/plasp/sas/MutexGroup.cpp @@ -2,7 +2,7 @@ #include -#include +#include namespace plasp { @@ -15,24 +15,24 @@ namespace sas // //////////////////////////////////////////////////////////////////////////////////////////////////// -MutexGroup MutexGroup::fromSAS(std::istream &istream, const Variables &variables) +MutexGroup MutexGroup::fromSAS(utils::Parser &parser, const Variables &variables) { MutexGroup mutexGroup; - utils::parseExpected(istream, "begin_mutex_group"); + parser.expect("begin_mutex_group"); - const auto numberOfFacts = utils::parse(istream); + const auto numberOfFacts = parser.parse(); mutexGroup.m_facts.reserve(numberOfFacts); for (size_t j = 0; j < numberOfFacts; j++) { - mutexGroup.m_facts.emplace_back(Fact::fromSAS(istream, variables)); + mutexGroup.m_facts.emplace_back(Fact::fromSAS(parser, variables)); if (mutexGroup.m_facts[j].value() == Value::None) - throw utils::ParserException("Mutex groups must not contain values"); + throw utils::ParserException(parser.row(), parser.column(), "Mutex groups must not contain values"); } - utils::parseExpected(istream, "end_mutex_group"); + parser.expect("end_mutex_group"); return mutexGroup; } diff --git a/src/plasp/sas/Operator.cpp b/src/plasp/sas/Operator.cpp index 2a34541..cf69f73 100644 --- a/src/plasp/sas/Operator.cpp +++ b/src/plasp/sas/Operator.cpp @@ -4,7 +4,6 @@ #include #include -#include namespace plasp { @@ -17,29 +16,29 @@ namespace sas // //////////////////////////////////////////////////////////////////////////////////////////////////// -Operator Operator::fromSAS(std::istream &istream, const Variables &variables) +Operator Operator::fromSAS(utils::Parser &parser, const Variables &variables) { Operator operator_; - utils::parseExpected(istream, "begin_operator"); + parser.expect("begin_operator"); - operator_.m_predicate = Predicate::fromSAS(istream); + operator_.m_predicate = Predicate::fromSAS(parser); - const auto numberOfPrevailConditions = utils::parse(istream); + const auto numberOfPrevailConditions = parser.parse(); operator_.m_preconditions.reserve(numberOfPrevailConditions); for (size_t j = 0; j < numberOfPrevailConditions; j++) - operator_.m_preconditions.emplace_back(Condition::fromSAS(istream, variables)); + operator_.m_preconditions.emplace_back(Condition::fromSAS(parser, variables)); - const auto numberOfEffects = utils::parse(istream); + const auto numberOfEffects = parser.parse(); operator_.m_effects.reserve(numberOfEffects); for (size_t j = 0; j < numberOfEffects; j++) - operator_.m_effects.emplace_back(Effect::fromSAS(istream, variables, operator_.m_preconditions)); + operator_.m_effects.emplace_back(Effect::fromSAS(parser, variables, operator_.m_preconditions)); - operator_.m_costs = utils::parse(istream); + operator_.m_costs = parser.parse(); - utils::parseExpected(istream, "end_operator"); + parser.expect("end_operator"); return operator_; } diff --git a/src/plasp/sas/Predicate.cpp b/src/plasp/sas/Predicate.cpp index 7536a89..4f1b541 100644 --- a/src/plasp/sas/Predicate.cpp +++ b/src/plasp/sas/Predicate.cpp @@ -4,7 +4,8 @@ #include #include -#include +#include +#include namespace plasp { @@ -17,21 +18,19 @@ namespace sas // //////////////////////////////////////////////////////////////////////////////////////////////////// -Predicate Predicate::fromSAS(std::istream &istream) +Predicate Predicate::fromSAS(utils::Parser &parser) { Predicate predicate; try { - istream.ignore(std::numeric_limits::max(), '\n'); + parser.skipLine(); // TODO: Inefficient, reimplement in one pass - std::string line; - std::getline(istream, line); + const std::string line = parser.getLine(); std::stringstream lineStream(line); - - predicate.m_name = utils::parse(lineStream); + lineStream >> predicate.m_name; while (lineStream.peek() == ' ') lineStream.ignore(1); @@ -41,7 +40,7 @@ Predicate Predicate::fromSAS(std::istream &istream) } catch (const std::exception &e) { - throw utils::ParserException("Could not parse operator predicate"); + throw utils::ParserException(parser.row(), parser.column(), "Could not parse operator predicate"); } return predicate; diff --git a/src/plasp/sas/TranslatorASP.cpp b/src/plasp/sas/TranslatorASP.cpp index e8f7b6f..8300829 100644 --- a/src/plasp/sas/TranslatorASP.cpp +++ b/src/plasp/sas/TranslatorASP.cpp @@ -1,7 +1,6 @@ #include #include -#include namespace plasp { diff --git a/src/plasp/sas/Value.cpp b/src/plasp/sas/Value.cpp index 0219025..2c13e34 100644 --- a/src/plasp/sas/Value.cpp +++ b/src/plasp/sas/Value.cpp @@ -3,7 +3,8 @@ #include #include -#include +#include +#include namespace plasp { @@ -53,14 +54,14 @@ Value Value::negated() const //////////////////////////////////////////////////////////////////////////////////////////////////// -Value Value::fromSAS(std::istream &istream) +Value Value::fromSAS(utils::Parser &parser) { - const auto sasSign = utils::parse(istream); + const auto sasSign = parser.parse(); if (sasSign == "(istream, "of"); - utils::parseExpected(istream, "those>"); + parser.expect("of"); + parser.expect("those>"); // TODO: do not return a copy of Value::None return Value::None; @@ -73,12 +74,12 @@ Value Value::fromSAS(std::istream &istream) else if (sasSign == "NegatedAtom") value.m_sign = Value::Sign::Negative; else - throw utils::ParserException("Invalid value sign \"" + sasSign + "\""); + throw utils::ParserException(parser.row(), parser.column(), "Invalid value sign \"" + sasSign + "\""); try { - istream.ignore(1); - std::getline(istream, value.m_name); + parser.skipWhiteSpace(); + value.m_name = parser.getLine(); // Remove trailing () if (value.m_name.find("()") != std::string::npos) @@ -89,7 +90,7 @@ Value Value::fromSAS(std::istream &istream) } catch (const std::exception &e) { - throw utils::ParserException(std::string("Could not parse variable value (") + e.what() + ")"); + throw utils::ParserException(parser.row(), parser.column(), std::string("Could not parse variable value (") + e.what() + ")"); } return value; @@ -97,15 +98,15 @@ Value Value::fromSAS(std::istream &istream) //////////////////////////////////////////////////////////////////////////////////////////////////// -const Value &Value::referenceFromSAS(std::istream &istream, const Variable &variable) +const Value &Value::referenceFromSAS(utils::Parser &parser, const Variable &variable) { - const auto valueID = utils::parse(istream); + const auto valueID = parser.parse(); if (valueID == -1) return Value::Any; if (valueID < 0 || static_cast(valueID) >= variable.values().size()) - throw utils::ParserException("Value index out of range (variable " + variable.name() + ", index " + std::to_string(valueID) + ")"); + throw utils::ParserException(parser.row(), parser.column(), "Value index out of range (variable " + variable.name() + ", index " + std::to_string(valueID) + ")"); return variable.values()[valueID]; } diff --git a/src/plasp/sas/Variable.cpp b/src/plasp/sas/Variable.cpp index 067d6c7..104f3f3 100644 --- a/src/plasp/sas/Variable.cpp +++ b/src/plasp/sas/Variable.cpp @@ -2,7 +2,8 @@ #include -#include +#include +#include namespace plasp { @@ -22,28 +23,28 @@ Variable::Variable() //////////////////////////////////////////////////////////////////////////////////////////////////// -Variable Variable::fromSAS(std::istream &istream) +Variable Variable::fromSAS(utils::Parser &parser) { Variable variable; - utils::parseExpected(istream, "begin_variable"); + parser.expect("begin_variable"); - variable.m_name = utils::parse(istream); - variable.m_axiomLayer = utils::parse(istream); + variable.m_name = parser.parse(); + variable.m_axiomLayer = parser.parse(); - const auto numberOfValues = utils::parse(istream); + const auto numberOfValues = parser.parse(); variable.m_values.reserve(numberOfValues); for (size_t j = 0; j < numberOfValues; j++) { - variable.m_values.emplace_back(Value::fromSAS(istream)); + variable.m_values.emplace_back(Value::fromSAS(parser)); // values are only allowed at the end if (j < numberOfValues - 1 && variable.m_values[j] == Value::None) - throw utils::ParserException(" value must be the last value of a variable"); + throw utils::ParserException(parser.row(), parser.column(), " value must be the last value of a variable"); } - utils::parseExpected(istream, "end_variable"); + parser.expect("end_variable"); return variable; } @@ -57,12 +58,12 @@ void Variable::printNameAsASPPredicate(std::ostream &ostream) const //////////////////////////////////////////////////////////////////////////////////////////////////// -const Variable &Variable::referenceFromSAS(std::istream &istream, const Variables &variables) +const Variable &Variable::referenceFromSAS(utils::Parser &parser, const Variables &variables) { - const auto variableID = utils::parse(istream); + const auto variableID = parser.parse(); if (variableID >= variables.size()) - throw utils::ParserException("Variable index out of range (index " + std::to_string(variableID) + ")"); + throw utils::ParserException(parser.row(), parser.column(), "Variable index out of range (index " + std::to_string(variableID) + ")"); return variables[variableID]; } diff --git a/src/plasp/sas/VariableTransition.cpp b/src/plasp/sas/VariableTransition.cpp index 0ce7c66..143c753 100644 --- a/src/plasp/sas/VariableTransition.cpp +++ b/src/plasp/sas/VariableTransition.cpp @@ -4,8 +4,6 @@ #include -#include - namespace plasp { namespace sas @@ -26,13 +24,13 @@ VariableTransition::VariableTransition() //////////////////////////////////////////////////////////////////////////////////////////////////// -VariableTransition VariableTransition::fromSAS(std::istream &istream, const Variables &variables) +VariableTransition VariableTransition::fromSAS(utils::Parser &parser, const Variables &variables) { VariableTransition variableTransition; - variableTransition.m_variable = &Variable::referenceFromSAS(istream, variables); - variableTransition.m_valueBefore = &Value::referenceFromSAS(istream, *variableTransition.m_variable); - variableTransition.m_valueAfter = &Value::referenceFromSAS(istream, *variableTransition.m_variable); + variableTransition.m_variable = &Variable::referenceFromSAS(parser, variables); + variableTransition.m_valueBefore = &Value::referenceFromSAS(parser, *variableTransition.m_variable); + variableTransition.m_valueAfter = &Value::referenceFromSAS(parser, *variableTransition.m_variable); return variableTransition; } diff --git a/src/plasp/utils/Parser.cpp b/src/plasp/utils/Parser.cpp new file mode 100644 index 0000000..13be87a --- /dev/null +++ b/src/plasp/utils/Parser.cpp @@ -0,0 +1,392 @@ +#include + +#include + +#include + +#include + +namespace plasp +{ +namespace utils +{ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Parser +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +const std::istream_iterator Parser::EndOfFile = std::istream_iterator(); + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +Parser::Parser(std::istream &istream) +: m_istream(istream), + m_position(m_istream), + m_row{1}, + m_column{1}, + m_endOfFile{false} +{ + std::setlocale(LC_NUMERIC, "C"); + + istream.exceptions(std::istream::badbit); + + // Don’t skip whitespace + istream >> std::noskipws; + + checkStream(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +size_t Parser::row() const +{ + return m_row; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +size_t Parser::column() const +{ + return m_column; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +void Parser::checkStream() const +{ + if (m_position == EndOfFile) + throw ParserException(m_row, m_column, "Reading past end of file"); + + if (m_istream.fail()) + throw ParserException(m_row, m_column); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +void Parser::advance() +{ + checkStream(); + + const auto character = *m_position; + + if (character == '\n') + { + m_row++; + m_column = 1; + } + else if (std::isblank(character) || std::isprint(character)) + m_column++; + + m_position++; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Parser::advanceIf(unsigned char expectedCharacter) +{ + checkStream(); + + if (*m_position != expectedCharacter) + return false; + + advance(); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +void Parser::skipWhiteSpace() +{ + checkStream(); + + while (std::isspace(*m_position)) + advance(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +void Parser::skipLine() +{ + checkStream(); + + while (*m_position != '\n') + advance(); + + advance(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +std::string Parser::getLine() +{ + checkStream(); + + std::string value; + + while (true) + { + const auto character = *m_position; + + advance(); + + if (character == '\n') + break; + else if (character == '\r') + continue; + + value.push_back(character); + } + + return value; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +template<> +std::string Parser::parse() +{ + skipWhiteSpace(); + + std::string value; + + while (true) + { + const auto character = *m_position; + + if (std::isspace(character)) + break; + + value.push_back(character); + advance(); + } + + return value; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +template<> +void Parser::expect(const std::string &expectedValue) +{ + BOOST_ASSERT(!std::isspace(expectedValue[0])); + + skipWhiteSpace(); + + std::for_each(expectedValue.cbegin(), expectedValue.cend(), + [&](const auto &expectedCharacter) + { + const auto character = *m_position; + + if (character != expectedCharacter) + throw ParserException(m_row, m_column, "Unexpected string, expected " + expectedValue); + + this->advance(); + }); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +uint64_t Parser::parseIntegerBody() +{ + checkStream(); + + if (!std::isdigit(*m_position)) + throw ParserException(m_row, m_column, "Could not parse integer value"); + + uint64_t value = 0; + + while (m_position != std::istream_iterator()) + { + const auto character = *m_position; + + if (!std::isdigit(character)) + break; + + value *= 10; + value += character - '0'; + + advance(); + } + + return value; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +template<> +int64_t Parser::parse() +{ + skipWhiteSpace(); + + bool positive = advanceIf('+') || !advanceIf('-'); + + const auto value = parseIntegerBody(); + + return (positive ? value : -value); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +template<> +uint64_t Parser::parse() +{ + skipWhiteSpace(); + + if (*m_position == '-') + throw ParserException(m_row, m_column, "Expected unsigned integer, got signed one"); + + return parseIntegerBody(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +template<> +void Parser::expect(const int64_t &expectedValue) +{ + const auto value = parse(); + + if (value != expectedValue) + throw ParserException(m_row, m_column, "Unexpected value " + std::to_string(value) + ", expected " + std::to_string(expectedValue)); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +template<> +void Parser::expect(const uint64_t &expectedValue) +{ + const auto value = parse(); + + if (value != expectedValue) + throw ParserException(m_row, m_column, "Unexpected value " + std::to_string(value) + ", expected " + std::to_string(expectedValue)); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +template<> +int32_t Parser::parse() +{ + return static_cast(parse()); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +template<> +uint32_t Parser::parse() +{ + return static_cast(parse()); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +template<> +void Parser::expect(const int32_t &expectedValue) +{ + expect(static_cast(expectedValue)); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +template<> +void Parser::expect(const uint32_t &expectedValue) +{ + expect(static_cast(expectedValue)); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +template<> +int16_t Parser::parse() +{ + return static_cast(parse()); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +template<> +uint16_t Parser::parse() +{ + return static_cast(parse()); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +template<> +void Parser::expect(const int16_t &expectedValue) +{ + expect(static_cast(expectedValue)); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +template<> +void Parser::expect(const uint16_t &expectedValue) +{ + expect(static_cast(expectedValue)); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +template<> +int8_t Parser::parse() +{ + return static_cast(parse()); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +template<> +uint8_t Parser::parse() +{ + return static_cast(parse()); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +template<> +void Parser::expect(const int8_t &expectedValue) +{ + expect(static_cast(expectedValue)); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +template<> +void Parser::expect(const uint8_t &expectedValue) +{ + expect(static_cast(expectedValue)); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +template<> +bool Parser::parse() +{ + skipWhiteSpace(); + + if (advanceIf('0')) + return false; + + if (advanceIf('1')) + return true; + + throw ParserException(m_row, m_column, "Could not parse Boolean value"); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +template<> +void Parser::expect(const bool &expectedValue) +{ + const auto value = parse(); + + if (value != expectedValue) + throw ParserException(m_row, m_column, "Unexpected value " + std::to_string(value) + ", expected " + std::to_string(expectedValue)); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +} +} diff --git a/tests/TestUtils.cpp b/tests/TestUtils.cpp index 2e0e867..f8dbe0f 100644 --- a/tests/TestUtils.cpp +++ b/tests/TestUtils.cpp @@ -1,20 +1,84 @@ #include -#include +#include +#include +#include //////////////////////////////////////////////////////////////////////////////////////////////////// TEST(UtilsTests, ParseSimple) { - std::stringstream stream("identifier 5 \n-51\t expected unexpected 100 -100"); + std::stringstream s("identifier 5 \n-51\t 0 1 expected unexpected"); + plasp::utils::Parser p(s); - ASSERT_EQ(plasp::utils::parse(stream), "identifier"); - ASSERT_EQ(plasp::utils::parse(stream), 5u); - ASSERT_EQ(plasp::utils::parse(stream), -51); - ASSERT_NO_THROW(plasp::utils::parseExpected(stream, "expected")); - ASSERT_THROW(plasp::utils::parseExpected(stream, "expected"), plasp::utils::ParserException); - ASSERT_NO_THROW(plasp::utils::parseExpected(stream, 100)); - ASSERT_THROW(plasp::utils::parseExpected(stream, 100), plasp::utils::ParserException); + ASSERT_EQ(p.parse(), "identifier"); + ASSERT_EQ(p.parse(), 5u); + ASSERT_EQ(p.parse(), -51); + ASSERT_EQ(p.parse(), false); + ASSERT_EQ(p.parse(), true); + + ASSERT_NO_THROW(p.expect("expected")); + ASSERT_THROW(p.expect("expected"), plasp::utils::ParserException); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +TEST(UtilsTests, ParseUnsignedNumbers) +{ + std::stringstream s("100 200 -300 -400"); + plasp::utils::Parser p(s); + + ASSERT_EQ(p.parse(), 100); + ASSERT_EQ(p.parse(), 200u); + ASSERT_EQ(p.parse(), -300); + ASSERT_THROW(p.parse(), plasp::utils::ParserException); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +TEST(UtilsTests, ParseEndOfFile) +{ + std::stringstream s1("test"); + plasp::utils::Parser p1(s1); + + ASSERT_NO_THROW(p1.expect("test")); + ASSERT_THROW(p1.parse(), plasp::utils::ParserException); + + std::stringstream s2("test1 test2 test3"); + plasp::utils::Parser p2(s2); + + ASSERT_NO_THROW(p2.expect("test1")); + ASSERT_NO_THROW(p2.expect("test2")); + ASSERT_NO_THROW(p2.expect("test3")); + ASSERT_THROW(p2.parse(), plasp::utils::ParserException); + + std::stringstream s3("-127"); + plasp::utils::Parser p3(s3); + + p3.expect(-127); + ASSERT_THROW(p3.parse(), plasp::utils::ParserException); + + std::stringstream s4("128 -1023 -4095"); + plasp::utils::Parser p4(s4); + + ASSERT_NO_THROW(p4.expect(128)); + ASSERT_NO_THROW(p4.expect(-1023)); + ASSERT_NO_THROW(p4.expect(-4095)); + ASSERT_THROW(p4.parse(), plasp::utils::ParserException); + + std::stringstream s5("0"); + plasp::utils::Parser p5(s5); + + p5.expect(false); + ASSERT_THROW(p5.parse(), plasp::utils::ParserException); + + std::stringstream s6("0 1 0"); + plasp::utils::Parser p6(s6); + + ASSERT_NO_THROW(p6.expect(false)); + ASSERT_NO_THROW(p6.expect(true)); + ASSERT_NO_THROW(p6.expect(false)); + ASSERT_THROW(p6.parse(), plasp::utils::ParserException); } ////////////////////////////////////////////////////////////////////////////////////////////////////