Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[issue1146] Validate SAS file #227

Draft
wants to merge 41 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
729fcbe
[issue 1146] Check that the input contains a number where expected.
tanjaschindler Jul 12, 2024
7659cd6
Add fact check for initial state and mutexes.
grucla Jul 12, 2024
777bef8
[Issue 1146] First version of sas file parser.
tanjaschindler Jul 17, 2024
5c189e7
[Issue 1146] Error handling only on InputFileParser level; get rid of…
tanjaschindler Jul 17, 2024
53c5096
[Issue 1146] Simplify InputFileParser, fix usage in RootTask.
tanjaschindler Jul 18, 2024
62c7e7f
[Issue 1146] Add documentation to parser.
tanjaschindler Jul 19, 2024
0ba73dc
[Issue1146] Check that there is at least one state variable and domai…
grucla Jul 19, 2024
f30e600
[Issue1146] Check that mutex groups have unique facts and at least si…
grucla Jul 19, 2024
9ae051f
Address parts of reviews. Main changes: may_start_line is now a funct…
tanjaschindler Nov 14, 2024
96a85aa
Rename InputFIleParser to TaskParser.
tanjaschindler Nov 15, 2024
5d3c78f
Make find_next_line a void function.
tanjaschindler Nov 18, 2024
2e1e822
Add documentation to TaskParser.
tanjaschindler Nov 18, 2024
dd210fe
Improve error messages.
tanjaschindler Nov 18, 2024
5d2e7e9
Rework duplicate code.
tanjaschindler Nov 25, 2024
cd034bf
Use template to avoid code duplication.
tanjaschindler Nov 25, 2024
4de84a3
small fixes
FlorianPommerening Dec 10, 2024
05efd05
fix includes
FlorianPommerening Dec 10, 2024
6f0e262
use context
FlorianPommerening Dec 10, 2024
d92ea31
Rename task parser to task lexer
FlorianPommerening Dec 10, 2024
09815b0
move task parsing to its own class
FlorianPommerening Dec 13, 2024
96ca8a9
initialize token_number in lexer
FlorianPommerening Dec 13, 2024
be1333b
rename local variable to reduce next diff
FlorianPommerening Dec 13, 2024
bee1bb4
move parsing functionality into parser class
FlorianPommerening Dec 13, 2024
a7cecd6
Split parsing operators and axioms and remove dependency.
FlorianPommerening Dec 13, 2024
f2fa8e4
remove unused includes
FlorianPommerening Dec 13, 2024
a577d0e
move int parsing to parser
FlorianPommerening Dec 14, 2024
c6cdea1
move magic line check out of lexer
FlorianPommerening Dec 14, 2024
d84cb3b
use parser context instead of lexer context for error handling
FlorianPommerening Dec 14, 2024
6ebe2d4
move initial state parsing into a method
FlorianPommerening Dec 14, 2024
c45d8c0
add more trace blocks
FlorianPommerening Dec 14, 2024
ff14625
decorate traceback with line numbers
FlorianPommerening Dec 14, 2024
8ebdfbe
Restructure TaskLexer. It now explicitly enters a line reading mode
FlorianPommerening Dec 15, 2024
358a8a1
Check facts immediately after reading them.
tanjaschindler Dec 17, 2024
3f38b07
Check operator cost.
tanjaschindler Dec 17, 2024
3f17f87
Check that goal variables are unique.
tanjaschindler Dec 17, 2024
6afb7b6
Check that variable affected by axiom is derived, and variables affec…
tanjaschindler Dec 17, 2024
c60a402
Check layering condition and pre-post-value condition for axioms.
tanjaschindler Dec 17, 2024
cba9cd5
Check that derived variables are binary.
tanjaschindler Dec 18, 2024
ed520a1
Separate read_pre_post for operators.
tanjaschindler Dec 18, 2024
83c3318
Small fix.
tanjaschindler Dec 19, 2024
a14e96b
Small fix.
tanjaschindler Dec 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/search/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ create_fast_downward_library(
utils/countdown_timer
utils/exceptions
utils/hash
utils/task_parser
utils/language
utils/logging
utils/markup
Expand Down
222 changes: 129 additions & 93 deletions src/search/tasks/root_task.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "../plugins/plugin.h"
#include "../utils/collections.h"
#include "../utils/task_parser.h"
#include "../utils/timer.h"

#include <algorithm>
Expand All @@ -28,7 +29,7 @@ struct ExplicitVariable {
int axiom_layer;
int axiom_default_value;

explicit ExplicitVariable(istream &in);
explicit ExplicitVariable(utils::TaskParser &task_parser);
};


Expand All @@ -47,8 +48,9 @@ struct ExplicitOperator {
string name;
bool is_an_axiom;

void read_pre_post(istream &in);
ExplicitOperator(istream &in, bool is_an_axiom, bool use_metric);
void read_pre_post(utils::TaskParser &task_parser);
void read_axiom(utils::TaskParser &task_parser);
ExplicitOperator(utils::TaskParser &task_parser, bool is_an_axiom, bool use_metric);
};


Expand Down Expand Up @@ -125,6 +127,12 @@ static void check_facts(const vector<FactPair> &facts, const vector<ExplicitVari
}
}

static void check_facts(const set<FactPair> &facts, const vector<ExplicitVariable> &variables) {
tanjaschindler marked this conversation as resolved.
Show resolved Hide resolved
for (FactPair fact : facts) {
check_fact(fact, variables);
}
}

static void check_facts(const ExplicitOperator &action, const vector<ExplicitVariable> &variables) {
check_facts(action.preconditions, variables);
for (const ExplicitEffect &eff : action.effects) {
Expand All @@ -133,44 +141,35 @@ static void check_facts(const ExplicitOperator &action, const vector<ExplicitVar
}
}

static void check_magic(istream &in, const string &magic) {
string word;
in >> word;
if (word != magic) {
cerr << "Failed to match magic word '" << magic << "'." << endl
<< "Got '" << word << "'." << endl;
if (magic == "begin_version") {
cerr << "Possible cause: you are running the planner "
<< "on a translator output file from " << endl
<< "an older version." << endl;
}
utils::exit_with(ExitCode::SEARCH_INPUT_ERROR);
}
}

static vector<FactPair> read_facts(istream &in) {
int count;
in >> count;
static vector<FactPair> read_facts(utils::TaskParser &task_parser, bool read_from_single_line) {
int count = read_from_single_line ? task_parser.read_int("number of conditions")
: task_parser.read_line_int("number of conditions");
vector<FactPair> conditions;
conditions.reserve(count);
for (int i = 0; i < count; ++i) {
FactPair condition = FactPair::no_fact;
in >> condition.var >> condition.value;
condition.var = task_parser.read_int("condition variable");
condition.value = task_parser.read_int("condition value");
if (!read_from_single_line) {
task_parser.confirm_end_of_line();
}
conditions.push_back(condition);
}
return conditions;
}

ExplicitVariable::ExplicitVariable(istream &in) {
check_magic(in, "begin_variable");
in >> name;
in >> axiom_layer;
in >> domain_size;
in >> ws;
ExplicitVariable::ExplicitVariable(utils::TaskParser &task_parser) {
task_parser.read_magic_line("begin_variable");
name = task_parser.read_line("variable name");
axiom_layer = task_parser.read_line_int("variable axiom layer");
domain_size = task_parser.read_line_int("variable domain size");
if (domain_size < 1) {
task_parser.error("Domain size is less than 1, should be at least 1.");
}
fact_names.resize(domain_size);
for (int i = 0; i < domain_size; ++i)
getline(in, fact_names[i]);
check_magic(in, "end_variable");
fact_names[i] = task_parser.read_line("fact name");
task_parser.read_magic_line("end_variable");
}


Expand All @@ -179,84 +178,104 @@ ExplicitEffect::ExplicitEffect(
: fact(var, value), conditions(move(conditions)) {
}

void ExplicitOperator::read_pre_post(utils::TaskParser &task_parser) {
vector<FactPair> conditions = read_facts(task_parser, true);
int var = task_parser.read_int("variable affected by effect");
int value_pre = task_parser.read_int("variable value precondition");
int value_post = task_parser.read_int("variable value postcondition");
if (value_pre != -1) {
preconditions.emplace_back(var, value_pre);
}
task_parser.confirm_end_of_line();
effects.emplace_back(var, value_post, move(conditions));
}

void ExplicitOperator::read_pre_post(istream &in) {
vector<FactPair> conditions = read_facts(in);
int var, value_pre, value_post;
in >> var >> value_pre >> value_post;
void ExplicitOperator::read_axiom(utils::TaskParser &task_parser) {
vector<FactPair> conditions = read_facts(task_parser, false);
int var = task_parser.read_int("variable affected by axiom");
int value_pre = task_parser.read_int("variable value precondition");
int value_post = task_parser.read_int("variable value postcondition");
task_parser.confirm_end_of_line();
if (value_pre != -1) {
preconditions.emplace_back(var, value_pre);
}
effects.emplace_back(var, value_post, move(conditions));
}

ExplicitOperator::ExplicitOperator(istream &in, bool is_an_axiom, bool use_metric)
ExplicitOperator::ExplicitOperator(utils::TaskParser &task_parser, bool is_an_axiom, bool use_metric)
: is_an_axiom(is_an_axiom) {
if (!is_an_axiom) {
check_magic(in, "begin_operator");
in >> ws;
getline(in, name);
preconditions = read_facts(in);
int count;
in >> count;
task_parser.read_magic_line("begin_operator");
name = task_parser.read_line("operator name");
preconditions = read_facts(task_parser, false);
int count = task_parser.read_line_int("number of operator effects");
effects.reserve(count);
for (int i = 0; i < count; ++i) {
read_pre_post(in);
read_pre_post(task_parser);
}

int op_cost;
in >> op_cost;
int op_cost = task_parser.read_line_int("operator cost");
cost = use_metric ? op_cost : 1;
check_magic(in, "end_operator");
task_parser.read_magic_line("end_operator");
} else {
name = "<axiom>";
cost = 0;
check_magic(in, "begin_rule");
read_pre_post(in);
check_magic(in, "end_rule");
task_parser.read_magic_line("begin_rule");
read_axiom(task_parser);
task_parser.read_magic_line("end_rule");
}
assert(cost >= 0);
}

static void read_and_verify_version(istream &in) {
int version;
check_magic(in, "begin_version");
in >> version;
check_magic(in, "end_version");
static void read_and_verify_version(utils::TaskParser &task_parser) {
task_parser.set_context("version section");
task_parser.read_magic_line("begin_version");
int version = task_parser.read_line_int("version number");
if (version != PRE_FILE_VERSION) {
cerr << "Expected translator output file version " << PRE_FILE_VERSION
<< ", got " << version << "." << endl
<< "Exiting." << endl;
utils::exit_with(ExitCode::SEARCH_INPUT_ERROR);
}
}

static bool read_metric(istream &in) {
bool use_metric;
check_magic(in, "begin_metric");
in >> use_metric;
check_magic(in, "end_metric");
task_parser.read_magic_line("end_version");
}

static bool read_metric(utils::TaskParser &task_parser) {
task_parser.set_context("metric_section");
task_parser.read_magic_line("begin_metric");
string use_metric_string = task_parser.read_line("use metric");
bool use_metric = false;
if (use_metric_string == "1") {
use_metric = true;
} else if (use_metric_string == "0") {
use_metric = false;
} else {
task_parser.error("expected boolean");
}
task_parser.read_magic_line("end_metric");
return use_metric;
}

static vector<ExplicitVariable> read_variables(istream &in) {
int count;
in >> count;
static vector<ExplicitVariable> read_variables(utils::TaskParser &task_parser) {
task_parser.set_context("variable_section");
int count = task_parser.read_line_int("variable count");
if (count < 1) {
task_parser.error("Number of variables is less than 1, should be at least 1.");
}
vector<ExplicitVariable> variables;
variables.reserve(count);
for (int i = 0; i < count; ++i) {
variables.emplace_back(in);
variables.emplace_back(task_parser);
}
return variables;
}

static vector<vector<set<FactPair>>> read_mutexes(istream &in, const vector<ExplicitVariable> &variables) {
static vector<vector<set<FactPair>>> read_mutexes(utils::TaskParser &task_parser, const vector<ExplicitVariable> &variables) {
task_parser.set_context("mutex section");
vector<vector<set<FactPair>>> inconsistent_facts(variables.size());
for (size_t i = 0; i < variables.size(); ++i)
inconsistent_facts[i].resize(variables[i].domain_size);

int num_mutex_groups;
in >> num_mutex_groups;
int num_mutex_groups = task_parser.read_line_int("number of mutex groups");

/*
NOTE: Mutex groups can overlap, in which case the same mutex
Expand All @@ -266,18 +285,24 @@ static vector<vector<set<FactPair>>> read_mutexes(istream &in, const vector<Expl
aware of.
*/
for (int i = 0; i < num_mutex_groups; ++i) {
check_magic(in, "begin_mutex_group");
int num_facts;
in >> num_facts;
task_parser.read_magic_line("begin_mutex_group");
int num_facts = task_parser.read_line_int("number of facts in mutex group");
if (num_facts < 1) {
task_parser.error("Number of facts in mutex group is less than 1, should be at least 1.");
}
vector<FactPair> invariant_group;
invariant_group.reserve(num_facts);
for (int j = 0; j < num_facts; ++j) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we already have a function that reads a list of facts? And doesn't it also need the check that there are no duplicate facts?

int var;
int value;
in >> var >> value;
int var = task_parser.read_int("variable number of mutex atom");
int value = task_parser.read_int("value of mutex atom");
task_parser.confirm_end_of_line();
invariant_group.emplace_back(var, value);
}
check_magic(in, "end_mutex_group");
task_parser.read_magic_line("end_mutex_group");
set<FactPair> invariant_group_set(invariant_group.begin(), invariant_group.end());
if (invariant_group_set.size() != invariant_group.size()) {
task_parser.error("Mutex group may not contain the same fact more than once.");
}
for (const FactPair &fact1 : invariant_group) {
for (const FactPair &fact2 : invariant_group) {
if (fact1.var != fact2.var) {
Expand All @@ -299,10 +324,11 @@ static vector<vector<set<FactPair>>> read_mutexes(istream &in, const vector<Expl
return inconsistent_facts;
}

static vector<FactPair> read_goal(istream &in) {
check_magic(in, "begin_goal");
vector<FactPair> goals = read_facts(in);
check_magic(in, "end_goal");
static vector<FactPair> read_goal(utils::TaskParser &task_parser) {
task_parser.set_context("goal section");
task_parser.read_magic_line("begin_goal");
vector<FactPair> goals = read_facts(task_parser, false);
task_parser.read_magic_line("end_goal");
if (goals.empty()) {
cerr << "Task has no goal condition!" << endl;
utils::exit_with(ExitCode::SEARCH_INPUT_ERROR);
Expand All @@ -311,42 +337,52 @@ static vector<FactPair> read_goal(istream &in) {
}

static vector<ExplicitOperator> read_actions(
istream &in, bool is_axiom, bool use_metric,
utils::TaskParser &task_parser, bool is_axiom, bool use_metric,
const vector<ExplicitVariable> &variables) {
int count;
in >> count;
task_parser.set_context(is_axiom ? "axiom section" : "operator section");
int count = task_parser.read_line_int(is_axiom ? "number of axioms" : "number of operators");
vector<ExplicitOperator> actions;
actions.reserve(count);
for (int i = 0; i < count; ++i) {
actions.emplace_back(in, is_axiom, use_metric);
actions.emplace_back(task_parser, is_axiom, use_metric);
check_facts(actions.back(), variables);
}
return actions;
}

RootTask::RootTask(istream &in) {
read_and_verify_version(in);
bool use_metric = read_metric(in);
variables = read_variables(in);
utils::TaskParser task_parser(in);
read_and_verify_version(task_parser);
bool use_metric = read_metric(task_parser);
variables = read_variables(task_parser);
int num_variables = variables.size();

mutexes = read_mutexes(in, variables);
mutexes = read_mutexes(task_parser, variables);
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);
}
}

// TODO: Maybe we could move this into a separate function as well
tanjaschindler marked this conversation as resolved.
Show resolved Hide resolved
task_parser.set_context("initial state section");
initial_state_values.resize(num_variables);
check_magic(in, "begin_state");
task_parser.read_magic_line("begin_state");
for (int i = 0; i < num_variables; ++i) {
in >> initial_state_values[i];
initial_state_values[i] = task_parser.read_line_int("initial state variable value");
}
check_magic(in, "end_state");
task_parser.read_magic_line("end_state");

for (int i = 0; i < num_variables; ++i) {
check_fact(FactPair(i, initial_state_values[i]), variables);
variables[i].axiom_default_value = initial_state_values[i];
}

goals = read_goal(in);
goals = read_goal(task_parser);
check_facts(goals, variables);
operators = read_actions(in, false, use_metric, variables);
axioms = read_actions(in, true, use_metric, variables);
operators = read_actions(task_parser, false, use_metric, variables);
axioms = read_actions(task_parser, true, use_metric, variables);
task_parser.confirm_end_of_input();
/* TODO: We should be stricter here and verify that we
tanjaschindler marked this conversation as resolved.
Show resolved Hide resolved
have reached the end of "in". */

Expand Down
Loading
Loading