diff --git a/src/search/tasks/root_task.cc b/src/search/tasks/root_task.cc index efbbba7f90..abd5506ec5 100644 --- a/src/search/tasks/root_task.cc +++ b/src/search/tasks/root_task.cc @@ -141,29 +141,18 @@ static void check_facts(const ExplicitOperator &action, const vector read_facts(utils::InputFileParser &in) { - int count = in.parse_single_token_line().parse_int("number of conditions"); +static vector read_facts(utils::InputFileParser &in, bool read_from_single_line) { + int count = read_from_single_line ? in.read_int("number of conditions") + : in.read_line_int("number of conditions"); vector conditions; conditions.reserve(count); for (int i = 0; i < count; ++i) { - utils::InputFileLineParser line = in.parse_line(); FactPair condition = FactPair::no_fact; - condition.var = line.read_token().parse_int("condition variable"); - condition.value = line.read_token().parse_int("condition value"); - line.check_last_token(); - conditions.push_back(condition); - } - return conditions; -} - -static vector read_facts(utils::InputFileLineParser &line) { - int count = line.read_token().parse_int("number of conditions"); - vector conditions; - conditions.reserve(count); - for (int i = 0; i < count; ++i) { - FactPair condition = FactPair::no_fact; - condition.var = line.read_token().parse_int("condition variable"); - condition.value = line.read_token().parse_int("condition value"); + condition.var = in.read_int("condition variable"); + condition.value = in.read_int("condition value"); + if (!read_from_single_line) { + in.confirm_end_of_line(); + } conditions.push_back(condition); } return conditions; @@ -171,13 +160,12 @@ static vector read_facts(utils::InputFileLineParser &line) { ExplicitVariable::ExplicitVariable(utils::InputFileParser &in) { in.read_magic_line("begin_variable"); - name = in.read_line().get_line(); - axiom_layer = in.parse_single_token_line().parse_int("variable axiom layer"); - domain_size = in.parse_single_token_line().parse_int("variable domain size"); - // in >> ws; + name = in.read_line("variable name"); + axiom_layer = in.read_line_int("variable axiom layer"); + domain_size = in.read_line_int("variable domain size"); fact_names.resize(domain_size); for (int i = 0; i < domain_size; ++i) - fact_names[i] = in.read_line().get_line(); + fact_names[i] = in.read_line("fact name"); in.read_magic_line("end_variable"); } @@ -188,24 +176,23 @@ ExplicitEffect::ExplicitEffect( } void ExplicitOperator::read_pre_post(utils::InputFileParser &in) { - utils::InputFileLineParser line = in.parse_line(); - vector conditions = read_facts(line); - int var = line.read_token().parse_int("variable affected by effect"); - int value_pre = line.read_token().parse_int("variable value precondition"); - int value_post = line.read_token().parse_int("variable value postcondition"); - line.check_last_token(); + vector conditions = read_facts(in, true); + int var = in.read_int("variable affected by effect"); + int value_pre = in.read_int("variable value precondition"); + int value_post = in.read_int("variable value postcondition"); if (value_pre != -1) { preconditions.emplace_back(var, value_pre); } + in.confirm_end_of_line(); effects.emplace_back(var, value_post, move(conditions)); } void ExplicitOperator::read_axiom(utils::InputFileParser &in) { - vector conditions = read_facts(in); - utils::InputFileLineParser line = in.parse_line(); - int var = line.read_token().parse_int("variable affected by axiom"); - int value_pre = line.read_token().parse_int("variable value precondition"); - int value_post = line.read_token().parse_int("variable value postcondition"); + vector conditions = read_facts(in, false); + int var = in.read_int("variable affected by axiom"); + int value_pre = in.read_int("variable value precondition"); + int value_post = in.read_int("variable value postcondition"); + in.confirm_end_of_line(); if (value_pre != -1) { preconditions.emplace_back(var, value_pre); } @@ -216,14 +203,14 @@ ExplicitOperator::ExplicitOperator(utils::InputFileParser &in, bool is_an_axiom, : is_an_axiom(is_an_axiom) { if (!is_an_axiom) { in.read_magic_line("begin_operator"); - name = in.read_line().get_line(); - preconditions = read_facts(in); - int count = in.parse_single_token_line().parse_int("number of operator effects"); + name = in.read_line("operator name"); + preconditions = read_facts(in, false); + int count = in.read_line_int("number of operator effects"); effects.reserve(count); for (int i = 0; i < count; ++i) { read_pre_post(in); } - int op_cost = in.parse_single_token_line().parse_int("operator cost"); + int op_cost = in.read_line_int("operator cost"); cost = use_metric ? op_cost : 1; in.read_magic_line("end_operator"); } else { @@ -239,7 +226,7 @@ ExplicitOperator::ExplicitOperator(utils::InputFileParser &in, bool is_an_axiom, static void read_and_verify_version(utils::InputFileParser &in) { in.set_context("version section"); in.read_magic_line("begin_version"); - int version = in.parse_line().read_token().parse_int("version number"); + int version = in.read_line_int("version number"); if (version != PRE_FILE_VERSION) { cerr << "Expected translator output file version " << PRE_FILE_VERSION << ", got " << version << "." << endl @@ -252,7 +239,7 @@ static void read_and_verify_version(utils::InputFileParser &in) { static bool read_metric(utils::InputFileParser &in) { in.set_context("metric_section"); in.read_magic_line("begin_metric"); - string use_metric_string = in.read_line().get_line(); + string use_metric_string = in.read_line("use metric"); bool use_metric = false; if (use_metric_string == "1") { use_metric = true; @@ -267,7 +254,7 @@ static bool read_metric(utils::InputFileParser &in) { static vector read_variables(utils::InputFileParser &in) { in.set_context("variable_section"); - int count = in.parse_single_token_line().parse_int("variable count"); + int count = in.read_line_int("variable count"); vector variables; variables.reserve(count); for (int i = 0; i < count; ++i) { @@ -282,7 +269,7 @@ static vector>> read_mutexes(utils::InputFileParser &in, co for (size_t i = 0; i < variables.size(); ++i) inconsistent_facts[i].resize(variables[i].domain_size); - int num_mutex_groups = in.parse_single_token_line().parse_int("number of mutex groups"); + int num_mutex_groups = in.read_line_int("number of mutex groups"); /* NOTE: Mutex groups can overlap, in which case the same mutex @@ -293,14 +280,13 @@ static vector>> read_mutexes(utils::InputFileParser &in, co */ for (int i = 0; i < num_mutex_groups; ++i) { in.read_magic_line("begin_mutex_group"); - int num_facts = in.parse_single_token_line().parse_int("number of facts in mutex group"); + int num_facts = in.read_line_int("number of facts in mutex group"); vector invariant_group; invariant_group.reserve(num_facts); for (int j = 0; j < num_facts; ++j) { - utils::InputFileLineParser line = in.parse_line(); - int var = line.read_token().parse_int("variable number of mutex atom"); - int value = line.read_token().parse_int("value of mutex atom"); - line.check_last_token(); + int var = in.read_int("variable number of mutex atom"); + int value = in.read_int("value of mutex atom"); + in.confirm_end_of_line(); invariant_group.emplace_back(var, value); } in.read_magic_line("end_mutex_group"); @@ -328,7 +314,7 @@ static vector>> read_mutexes(utils::InputFileParser &in, co static vector read_goal(utils::InputFileParser &in) { in.set_context("goal section"); in.read_magic_line("begin_goal"); - vector goals = read_facts(in); + vector goals = read_facts(in, false); in.read_magic_line("end_goal"); if (goals.empty()) { cerr << "Task has no goal condition!" << endl; @@ -341,7 +327,7 @@ static vector read_actions( utils::InputFileParser &in, bool is_axiom, bool use_metric, const vector &variables) { in.set_context(is_axiom ? "axiom section" : "operator section"); - int count = in.parse_single_token_line().parse_int(is_axiom ? "number of axioms" : "number of operators"); + int count = in.read_line_int(is_axiom ? "number of axioms" : "number of operators"); vector actions; actions.reserve(count); for (int i = 0; i < count; ++i) { @@ -359,8 +345,8 @@ RootTask::RootTask(istream &in) { int num_variables = variables.size(); mutexes = read_mutexes(file, variables); - for (int i = 0; i < mutexes.size(); ++i) { - for (int j = 0; j < mutexes[i].size(); ++j) { + for (size_t i = 0; i < mutexes.size(); ++i) { + for (size_t j = 0; j < mutexes[i].size(); ++j) { check_facts(mutexes[i][j], variables); } } @@ -370,7 +356,7 @@ RootTask::RootTask(istream &in) { initial_state_values.resize(num_variables); file.read_magic_line("begin_state"); for (int i = 0; i < num_variables; ++i) { - initial_state_values[i] = file.parse_single_token_line().parse_int("initial state variable value"); + initial_state_values[i] = file.read_line_int("initial state variable value"); } file.read_magic_line("end_state"); @@ -383,6 +369,7 @@ RootTask::RootTask(istream &in) { check_facts(goals, variables); operators = read_actions(file, false, use_metric, variables); axioms = read_actions(file, true, use_metric, variables); + file.confirm_end_of_file(); /* TODO: We should be stricter here and verify that we have reached the end of "in". */ diff --git a/src/search/utils/input_file_parser.cc b/src/search/utils/input_file_parser.cc index 32631b9564..d7d0970584 100644 --- a/src/search/utils/input_file_parser.cc +++ b/src/search/utils/input_file_parser.cc @@ -1,140 +1,139 @@ +#include "input_file_parser.h" #include "system.h" -#include +#include #include +#include #include -#include "input_file_parser.h" using namespace std; using utils::ExitCode; namespace utils { -static vector split_line(const string &line) { - istringstream stream(line); - vector words; - while (!stream.eof()) { - string word; - stream >> word; - words.push_back(word); - } - return words; -} -InputFileParser::InputFileParser(istream &stream) : stream(stream) { - context = ""; - line_number = 0; -} -InputFileParser::~InputFileParser() { +InputFileParser::InputFileParser(istream &stream) +: stream(stream), context(""), only_whitespaces("\\s*") { } -void InputFileParser::set_context(const string &context) { - this->context = context; +InputFileParser::~InputFileParser() { } -InputFileLine InputFileParser::read_line() { - if (stream.eof()) { +string InputFileParser::find_next_line(bool throw_error_on_failure) { + string next_line; + while (!stream.eof()) { + getline(stream, next_line); + ++line_number; + if (!regex_match(next_line, only_whitespaces)) { + return next_line; + } + } + if (throw_error_on_failure) { error("Unexpected end of file."); } - string line; - getline(stream, line); - ++line_number; - return InputFileLine(make_shared(*this), line, line_number); + return ""; } -InputFileLineParser InputFileParser::parse_line() { - shared_ptr line = make_shared(read_line()); - return InputFileLineParser(make_shared(*this), line); +void InputFileParser::initialize_tokens() { + assert(may_start_line); + assert(token_number == 0); + assert(tokens.empty()); + assert(line != ""); + istringstream stream(line); + string word; + while (!stream.eof()) { + stream >> word; + tokens.push_back(word); + } + assert(tokens.size() > 0); } -InputFileToken InputFileParser::parse_single_token_line() { - InputFileLineParser line_parser = parse_line(); - InputFileToken token = line_parser.read_token(); - line_parser.check_last_token(); - return token; +int InputFileParser::parse_int(const string &str, const string &cause) { + try { + string::size_type parsed_length; + int number = stoi(str, &parsed_length); + if (parsed_length == str.size()) { + return number; + } + } catch (exception &e) { + } + error("expected number; cause: " + cause); } -void InputFileParser::read_magic_line(const string &magic) { // TODO: name? - InputFileLine line = read_line(); - if (line.get_line() != magic) { - error("Expected magic line " + magic + ", got " + line.get_line() + "."); - } +void InputFileParser::set_context(const string &context) { + this->context = context; } -void InputFileParser::error(const string &message) const { - cerr << "Error reading input file "; - if (line_number > 0) { - cerr << "line " << line_number; +string InputFileParser::read(const string &message) { + if (may_start_line) { + line = find_next_line(true); + initialize_tokens(); + may_start_line = false; } - cerr << "." << endl; - if (context != "") { - cerr << "Context: " << context << endl; + if (token_number >= tokens.size()) { + error("Unexpected end of line. Message: " + message); } - cerr << message << endl - << "Exiting." << endl; - utils::exit_with(ExitCode::SEARCH_INPUT_ERROR); -} - -InputFileLine::InputFileLine(const shared_ptr file_parser, const string &line, int line_number) -: file_parser(file_parser), line(line) { - this->line_number = line_number; + string token = tokens[token_number]; + ++token_number; + return token; } -InputFileLine::~InputFileLine() { +int InputFileParser::read_int(const string &message) { + string token = read(message); + return parse_int(token, message); } -const string& InputFileLine::get_line() const { +string InputFileParser::read_line(const string &message) { + assert(may_start_line); // We probably forgot a confirm_end_of_line. + line = find_next_line(true); return line; } -InputFileLineParser::InputFileLineParser(const shared_ptrfile_parser, const shared_ptr line) -: file_parser(file_parser), line(line) { - tokens = split_line(line->get_line()); - token_number = 0; -} - -InputFileLineParser::~InputFileLineParser() { +int InputFileParser::read_line_int(const string &message) { + string line = read_line(message); + return parse_int(line, message); } -const vector& InputFileLineParser::get_tokens() const { - return tokens; -} - -InputFileToken InputFileLineParser::read_token() { - if (token_number > tokens.size()) { - file_parser->error("unexpected end of line"); +void InputFileParser::read_magic_line(const string &magic) { + string line = read_line("read magic line"); + if (line != magic) { + error("Expected magic line " + magic + ", got " + line + "."); } - ++token_number; - return InputFileToken(file_parser, line, tokens[token_number - 1], token_number); } -void InputFileLineParser::check_last_token() { - if (token_number > tokens.size()) { - file_parser->error("expected end of line"); +void InputFileParser::confirm_end_of_line() { + if (may_start_line) { + return; + } + if (token_number == tokens.size()) { + may_start_line = true; + token_number = 0; + tokens.clear(); + } else { + error("Expected end of line after token " + to_string(token_number) + + " but line contains " + to_string(tokens.size()) + " tokens."); } } -InputFileToken::InputFileToken(const shared_ptr file_parser, const shared_ptr line, const string &token, int token_number) - : file_parser(file_parser), line(line), token(token) { - this->token_number = token_number; -} - -InputFileToken::~InputFileToken() { -} - -const string& InputFileToken::get_token() const { - return token; +void InputFileParser::confirm_end_of_file() { + assert(may_start_line); + string next_line = find_next_line(false); + if(next_line != "") { + error("Expected end of file, found non-empty line " + next_line); + } } -int InputFileToken::parse_int(const string &cause) const { - try { - string::size_type parsed_length; - int number = stoi(token, &parsed_length); - if (parsed_length == token.size()) { - return number; - } - } catch (exception &e) { +void InputFileParser::error(const string &message) const { + cerr << "Error reading input file "; + if (line_number > 0) { + cerr << "line " << line_number; + } + cerr << "." << endl; + if (context != "") { + cerr << "Context: " << context << endl; } - file_parser->error("expected number; cause: " + cause); + cerr << message << endl + << "Exiting." << endl; + utils::exit_with(ExitCode::SEARCH_INPUT_ERROR); } } diff --git a/src/search/utils/input_file_parser.h b/src/search/utils/input_file_parser.h index d27b1511ec..1ef0399569 100644 --- a/src/search/utils/input_file_parser.h +++ b/src/search/utils/input_file_parser.h @@ -2,6 +2,7 @@ #define UTILS_INPUT_FILE_PARSER_H #include +#include namespace utils { class InputFileLine; @@ -11,51 +12,29 @@ class InputFileToken; class InputFileParser { std::istream &stream; std::string context; - int line_number; + int line_number = 0; + size_t token_number; + std::string line; + std::vector tokens; + bool may_start_line = true; + const std::regex only_whitespaces; public: explicit InputFileParser(std::istream &stream); ~InputFileParser(); + void set_context(const std::string &context); - InputFileLine read_line(); - InputFileLineParser parse_line(); - InputFileToken parse_single_token_line(); + std::string read(const std::string &message); // TODO: templates + int read_int(const std::string &message); // TODO: templates + std::string read_line(const std::string &message); // TODO: templates + int read_line_int(const std::string &message); // TODO: templates void read_magic_line(const std::string &magic); + void confirm_end_of_line(); + void confirm_end_of_file(); void error(const std::string &message) const; -}; - -class InputFileLine { - const std::shared_ptr file_parser; - const std::string line; - int line_number; -public: - InputFileLine(const std::shared_ptr file_parser, const std::string &line, int line_number); - ~InputFileLine(); - const std::string& get_line() const; -}; - -class InputFileLineParser { - const std::shared_ptr file_parser; - const std::shared_ptr line; - std::vector tokens; - int token_number; -public: - InputFileLineParser(const std::shared_ptr file_parser, const std::shared_ptr line); - ~InputFileLineParser(); - const std::vector& get_tokens() const; - InputFileToken read_token(); - void check_last_token(); -}; - -class InputFileToken { - const std::shared_ptr file_parser; - const std::shared_ptr line; - const std::string token; - int token_number; -public: - InputFileToken(const std::shared_ptr file_parser, const std::shared_ptr line, const std::string &token, int token_number); - ~InputFileToken(); - const std::string& get_token() const; - int parse_int(const std::string &cause) const; +private: + std::string find_next_line(bool throw_error_on_failure=true); + void initialize_tokens(); + int parse_int(const std::string &str, const std::string &cause); }; } #endif