Fawkes API  Fawkes Development Version
pddl_semantics.cpp
1 /***************************************************************************
2  * pddl_semantics.cpp semantic checks during parsing
3  *
4  * Created: Thursday 15 October 2020
5  * Copyright 2020 Tarik Viehmann
6  ****************************************************************************/
7 
8 /* This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Library General Public License for more details.
17  *
18  * Read the full text in the LICENSE.GPL file in the doc directory.
19  */
20 
21 #include "pddl_semantics.h"
22 
23 #include "pddl_exception.h"
24 
25 #include <algorithm>
26 
27 namespace pddl_parser {
28 
29 namespace semantics_utils {
30 bool
31 typing_required(const Domain &d)
32 {
33  auto typing_reqs = {"typing", "adl", "ucpop"};
34  for (const auto &type_req : typing_reqs) {
35  if (d.requirements.end() != std::find(d.requirements.begin(), d.requirements.end(), type_req)) {
36  return true;
37  }
38  }
39  return false;
40 }
41 
42 void
43 check_type_vs_requirement(const iterator_type &where, bool typing_required, const std::string &type)
44 {
45  if ((type == "") && typing_required) {
46  throw PddlTypeException(std::string("Missing type."), where);
47  }
48  if ((type != "") && !typing_required) {
49  throw PddlTypeException(std::string("Requirement typing disabled, unexpected type found."),
50  where);
51  }
52 }
53 
54 } // end namespace semantics_utils
55 
56 pair_type
57 TypeSemantics::operator()(const iterator_type &where,
58  const pair_type & parsed,
59  const Domain & domain) const
60 {
61  if (!semantics_utils::typing_required(domain)) {
62  throw PddlTypeException(std::string("Requirement typing disabled, unexpected type found."),
63  where);
64  }
65  return parsed;
66 }
67 
68 pair_type
69 ParamTransformer::operator()(const iterator_type & where,
70  const pair_strings_type &parsed,
71  string_pairs_type & target) const
72 {
73  if (parsed.second.empty()) {
74  std::transform(parsed.first.begin(),
75  parsed.first.end(),
76  std::back_inserter(target),
77  [](const std::string &s) { return std::make_pair(s, ""); });
78  } else {
79  for (const auto &variant_type : parsed.second) {
80  std::transform(parsed.first.begin(),
81  parsed.first.end(),
82  std::back_inserter(target),
83  [variant_type](const std::string &s) {
84  return std::make_pair(s, variant_type);
85  });
86  }
87  }
88  pair_type res = target.back();
89  target.pop_back();
90  return res;
91 }
92 
93 pair_multi_const
94 ConstantSemantics::operator()(const iterator_type & where,
95  const pair_multi_const & parsed,
96  const Domain & domain,
97  std::vector<std::string> &warnings) const
98 {
99  // typing test:
100  bool typing_enabled = semantics_utils::typing_required(domain);
101  if (typing_enabled) {
102  auto search =
103  std::find_if(domain.types.begin(), domain.types.end(), [parsed](const pair_type &p) {
104  return p.first == parsed.second || p.second == parsed.second;
105  });
106  if (search == domain.types.end()) {
107  throw PddlTypeException(std::string("Unknown type: ") + parsed.second, where);
108  }
109  }
110  semantics_utils::check_type_vs_requirement(where, typing_enabled, parsed.second);
111  for (const auto &constant : parsed.first) {
112  for (const auto &dom_constants : domain.constants) {
113  std::for_each(dom_constants.first.begin(),
114  dom_constants.first.end(),
115  [&parsed, &constant, &dom_constants, &warnings](const auto &c) mutable {
116  if (c == constant && parsed.second != dom_constants.second) {
117  warnings.push_back(std::string("Ambiguous type: ") + constant + " type "
118  + parsed.second + " and " + dom_constants.second);
119  }
120  });
121  }
122  }
123  return parsed;
124 }
125 
126 Action
127 ActionSemantics::operator()(const iterator_type &where,
128  const Action & parsed,
129  const Domain & domain) const
130 {
131  bool typing_enabled = semantics_utils::typing_required(domain);
132  for (const auto &action_param : parsed.action_params) {
133  if (typing_enabled) {
134  auto search =
135  std::find_if(domain.types.begin(), domain.types.end(), [action_param](const pair_type &p) {
136  return p.first == action_param.second || p.second == action_param.second;
137  });
138  if (search == domain.types.end()) {
139  throw PddlTypeException(std::string("Unknown type: ") + action_param.first + " - "
140  + action_param.second,
141  where);
142  }
143  }
144  semantics_utils::check_type_vs_requirement(where, typing_enabled, action_param.second);
145  }
146  // predicate signature test:
147  string_pairs_type bound_vars;
148  check_action_condition(where, parsed.precondition, domain, parsed, bound_vars);
149  check_action_condition(where, parsed.effect, domain, parsed, bound_vars);
150 
151  return parsed;
152 }
153 
154 bool
155 ActionSemantics::check_type(const iterator_type &where,
156  const std::string & got,
157  const std::string & expected,
158  const Domain & domain)
159 {
160  if (got != expected) {
161  auto generalized_it = std::find_if(domain.types.begin(),
162  domain.types.end(),
163  [got](const pair_type &p) { return p.first == got; });
164  if (generalized_it == domain.types.end()) {
165  return false;
166  } else {
167  return check_type(where, generalized_it->second, expected, domain);
168  }
169  } else {
170  return true;
171  }
172 }
173 
174 void
175 ActionSemantics::check_action_condition(const iterator_type &where,
176  const Expression & expr,
177  const Domain & domain,
178  const Action & curr_action,
179  string_pairs_type & bound_vars)
180 {
181  auto curr_obj_type = boost::apply_visitor(ExpressionTypeVisitor(), expr.expression);
182  // this function checks conditions, if the expression is an atom, then the action has an invalid structure
183  if (curr_obj_type == std::type_index(typeid(Atom))) {
184  throw PddlExpressionException(std::string("Unexpected Atom in expression: ")
185  + boost::get<Atom>(expr.expression),
186  where);
187  }
188  if (curr_obj_type == std::type_index(typeid(QuantifiedFormula))) {
189  QuantifiedFormula f = boost::get<QuantifiedFormula>(expr.expression);
190  bound_vars.insert(bound_vars.end(), f.args.begin(), f.args.end());
191  check_action_condition(where, f.sub_expr, domain, curr_action, bound_vars);
192  }
193 
194  if (curr_obj_type == std::type_index(typeid(Predicate))) {
195  return check_action_predicate(
196  where, boost::get<Predicate>(expr.expression), expr.type, domain, curr_action, bound_vars);
197  }
198 }
199 
200 void
201 ActionSemantics::check_action_predicate(const iterator_type & where,
202  const Predicate & pred,
203  const ExpressionType &type,
204  const Domain & domain,
205  const Action & curr_action,
206  string_pairs_type & bound_vars)
207 {
208  bool typing_enabled = semantics_utils::typing_required(domain);
209  switch (type) {
210  case ExpressionType::BOOL: {
211  for (const auto &sub_expr : pred.arguments) {
212  // recursively check sub expressions of booelean expressions, they all are predicate expressions
213  check_action_condition(where, sub_expr, domain, curr_action, bound_vars);
214  }
215  break;
216  }
217  case ExpressionType::PREDICATE: {
218  auto defined_pred =
219  // check if the predicate name is defined in the domain ...
220  std::find_if(domain.predicates.begin(),
221  domain.predicates.end(),
222  [pred](const predicate_type &p) { return pred.function == p.first; });
223  if (defined_pred == domain.predicates.end()) {
224  // ... if it is not, then this predicate is invalid
225  throw PddlPredicateException(std::string("Unknown predicate: ") + pred.function, where);
226  } else {
227  // If the predicate is defined, the signature has to match
228  if (defined_pred->second.size() != pred.arguments.size()) {
229  throw PddlPredicateException(std::string("Predicate argument length missmatch, expected ")
230  + std::to_string(defined_pred->second.size()) + " but got "
231  + std::to_string(pred.arguments.size()),
232  where);
233  } else {
234  // and all arguments must be atomic expressions
235  for (size_t i = 0; i < pred.arguments.size(); i++) {
236  if (boost::apply_visitor(ExpressionTypeVisitor(), pred.arguments[i].expression)
237  != std::type_index(typeid(Atom))) {
238  throw PddlPredicateException(std::string("Unexpected nested predicate."), where);
239  } else {
240  Atom curr_arg = boost::get<Atom>(pred.arguments[i].expression);
241  bool is_type_error = false;
242  std::string arg_type = "";
243  if (curr_arg.front() != '?') {
244  // constants need to be known
245  bool constant_found = false;
246  auto constant_match = std::find_if(
247  domain.constants.begin(),
248  domain.constants.end(),
249  [&curr_arg, &domain, &defined_pred, &i, &where, &constant_found, &arg_type](
250  const pair_multi_const &c) mutable {
251  if (c.first.end() != std::find(c.first.begin(), c.first.end(), curr_arg)) {
252  constant_found = true;
253  arg_type += " " + c.second;
254  return check_type(where, c.second, defined_pred->second[i].second, domain);
255  } else {
256  return false;
257  }
258  });
259  if (constant_match == domain.constants.end()) {
260  is_type_error = true;
261  if (!constant_found) {
262  throw PddlConstantException(std::string("Unknown constant ") + curr_arg, where);
263  }
264  }
265  } else {
266  auto bound_vars_match =
267  std::find_if(bound_vars.begin(), bound_vars.end(), [curr_arg](const pair_type &c) {
268  return c.first == curr_arg.substr(1, std::string::npos);
269  });
270  if (bound_vars_match == bound_vars.end()) {
271  auto parameter_match =
272  std::find_if(curr_action.action_params.begin(),
273  curr_action.action_params.end(),
274  [curr_arg](const pair_type &c) {
275  return c.first == curr_arg.substr(1, std::string::npos);
276  });
277  if (parameter_match == curr_action.action_params.end()) {
278  throw PddlParameterException(std::string("Unknown Parameter ") + curr_arg, where);
279  } else {
280  arg_type = parameter_match->second;
281  }
282  } else {
283  arg_type = bound_vars_match->second;
284  }
285  is_type_error = !check_type(where, arg_type, defined_pred->second[i].second, domain);
286  }
287  // and if typing is required, then the types should match the signature
288  if (typing_enabled && is_type_error) {
289  throw PddlTypeException(std::string("Type missmatch: Argument ") + std::to_string(i)
290  + " of " + defined_pred->first + " expects "
291  + defined_pred->second[i].second + " but got " + arg_type,
292  where);
293  }
294  }
295  }
296  }
297  }
298  break;
299  }
300  default: break;
301  }
302 }
303 } // namespace pddl_parser
Exception thrown by the parser if a declared constant does not match a defined one.
Exception thrown by the parser if an expression is invalid.
Exception thrown by the parser if a parameter mismatch is encountered.
Exception thrown by the parser if a declared relation does not match the defined predicate.
Exception thrown by the parser if declared type does not match the defined one.
This class tries to translate the found plan to interpreteable data for the rest of the program.
A structured representation of a PDDL action.
Definition: pddl_ast.h:133
string_pairs_type action_params
A typed list of action parameters.
Definition: pddl_ast.h:137
Expression effect
The effect of an action.
Definition: pddl_ast.h:143
Expression precondition
The precondition of an action.
Definition: pddl_ast.h:141
pair_multi_const operator()(const iterator_type &where, const pair_multi_const &parsed, const Domain &domain, std::vector< std::string > &warnings) const
Check whether the given type for a set of constants is defined and registers warnings if constants ar...
A structured representation of a PDDL domain.
Definition: pddl_ast.h:157
pairs_multi_consts constants
A typed list of constants defined in the domain.
Definition: pddl_ast.h:165
pairs_type types
A list of types with their super types.
Definition: pddl_ast.h:163
std::vector< predicate_type > predicates
A list of predicate names in the domain, including the types of their arguments.
Definition: pddl_ast.h:169
Retrieve the type index of an expression_t expression to determine the underlying type of the variant...
A PDDL Expression.
Definition: pddl_ast.h:78
expression_t expression
The expression formula.
Definition: pddl_ast.h:82
ExpressionType type
The type of the expression, determined at parsing time.
Definition: pddl_ast.h:80
pair_type operator()(const iterator_type &where, const pair_strings_type &parsed, string_pairs_type &target) const
Transform a pair of string vectors to pairs of strings.
A PDDL formula (either part of a precondition or an effect(.
Definition: pddl_ast.h:107
std::vector< Expression > arguments
The arguments of the predicate or the subformulae of the compound formula.
Definition: pddl_ast.h:115
Atom function
The name of the predicate for atomic formulae, 'and' for a conjunction, 'or' for a disjunction,...
Definition: pddl_ast.h:111
A PDDL quantified formula.
Definition: pddl_ast.h:89
Expression sub_expr
Sub-expression that is quantified over.
Definition: pddl_ast.h:97
string_pairs_type args
args that are bound by the quantifier
Definition: pddl_ast.h:94
pair_type operator()(const iterator_type &where, const pair_type &parsed, const Domain &domain) const
Throw an exception if the parsed type is a sub-type but the domain does not have the requirement :typ...