HAL
types.cpp
Go to the documentation of this file.
2 
3 #include <boost/spirit/home/x3.hpp>
4 #include <mutex>
5 #include <numeric>
6 #include <sstream>
7 
8 namespace hal
9 {
10  namespace SMT
11  {
12  namespace ModelParser
13  {
14  namespace x3 = boost::spirit::x3;
15 
18  {
20  std::map<std::string, std::tuple<u64, u16>> model;
21 
23  std::string current_name;
25  std::string current_size;
27  std::string current_value;
28  };
29 
30  // Parser context to handle
32 
33  // Action to store a translated name in the parser context.
34  const auto NameAction = [](const auto& ctx) { parser_context.current_name = _attr(ctx); };
35  // Action to store a translated size in the parser context.
36  const auto SortAction = [](const auto& ctx) { parser_context.current_size = boost::fusion::at_c<0>(_attr(ctx)); };
37  // Action to store a translated binary value in the parser context.
38  const auto BinaryValueAction = [](const auto& ctx) { parser_context.current_value = "0b" + _attr(ctx); };
39  // Action to store a translated hexadecimal value in the parser context.
40  const auto HexValueAction = [](const auto& ctx) { parser_context.current_value = "0x" + _attr(ctx); };
41 
42  // Action to store a translated signal assignment in the parser context.
43  const auto SignalAssigmentAction = [](const auto& /* ctx */) {
44  const auto value = (parser_context.current_value.at(1) == 'b') ? strtoull(parser_context.current_value.substr(2, parser_context.current_value.length() - 2).c_str(), nullptr, 2)
45  : strtoull(parser_context.current_value.c_str(), nullptr, 0);
46 
47  const auto size = strtoul(parser_context.current_size.c_str(), nullptr, 0);
48 
49  parser_context.model.insert({parser_context.current_name, {value, size}});
50  };
51 
52  // Parser rule to translate a signal assignment name.
53  //
54  // Example: "A"
55  const auto NameRule = (+x3::char_("0-9a-zA-Z_"))[NameAction];
56  // Parser rule to translate a signal assignment sort.
57  //
58  // Example: "(_ BitVec 1)"
59  const auto SortRule = (x3::lit("(_ BitVec ") >> +x3::digit >> x3::lit(")"))[SortAction];
60 
61  // Parser rule to translate a signal assignment binary constant value.
62  //
63  // Example: "#b0"
64  const auto BinaryValueRule = (x3::lit("#b") >> +x3::char_("0-1"))[BinaryValueAction];
65  // Parser rule to translate a signal assignment hexadecimal constant value.
66  //
67  // Example: "#x1337c0de"
68  const auto HexValueRule = (x3::lit("#x") >> +x3::xdigit)[HexValueAction];
69  // General parser rule to translate signal assignment constant values.
71 
72  // Parser rule to translate a given signal assignment into its signal
73  // name, its bit-size and the constant value.
74  //
75  // Example: "(define-fun A () (_ BitVec 1) #b0)"
76  const auto SignalAssignmentRule = (x3::lit("(define-fun") >> NameRule >> x3::lit("()") >> SortRule >> ValueRule >> x3::lit(")"))[SignalAssigmentAction];
77 
78  // Boolector grammar to parse a model for a satisfiable formula.
79  //
80  // Example: "(model (define-fun A () (_ BitVec 1) #b0))"
81  const auto BOOLECTOR_MODEL_GRAMMAR = +(x3::lit("(model") >> +SignalAssignmentRule >> x3::lit(")"));
82 
83  // Z3 grammar to parse a model for a satisfiable formula.
84  //
85  // Example: "((define-fun A () (_ BitVec 1) #b0))"
86  const auto Z3_MODEL_GRAMMAR = (x3::lit("(") >> (+SignalAssignmentRule) >> x3::lit(")")) | +(x3::lit("(model") >> +SignalAssignmentRule >> x3::lit(")"));
87  } // namespace ModelParser
88 
90  {
91  this->solver = _solver;
92  return *this;
93  }
94 
96  {
97  this->call = _call;
98  return *this;
99  }
100 
102  {
103  this->local = true;
104  return *this;
105  }
106 
108  {
109  this->local = false;
110  return *this;
111  }
112 
114  {
115  this->generate_model = true;
116  return *this;
117  }
118 
120  {
121  this->generate_model = false;
122  return *this;
123  }
124 
126  {
127  this->timeout_in_seconds = seconds;
128  return *this;
129  }
130 
131  std::ostream& operator<<(std::ostream& out, const QueryConfig& config)
132  {
133  out << "{";
134  out << "solver : " << enum_to_string(config.solver) << ", ";
135  out << "call: " << enum_to_string(config.call) << ", ";
136  out << "local : " << std::boolalpha << config.local << ", ";
137  out << "generate_model : " << std::boolalpha << config.generate_model << ", ";
138  out << "timeout : " << std::dec << config.timeout_in_seconds << "s";
139  out << "}";
140  return out;
141  }
142 
143  Constraint::Constraint(BooleanFunction&& _constraint) : constraint(std::move(_constraint))
144  {
145  }
146 
147  Constraint::Constraint(BooleanFunction&& _lhs, BooleanFunction&& _rhs) : constraint(std::make_pair(std::move(_lhs), std::move(_rhs)))
148  {
149  }
150 
151  std::ostream& operator<<(std::ostream& out, const Constraint& constraint)
152  {
153  if (constraint.is_assignment())
154  {
155  const auto assignment = constraint.get_assignment().get();
156  out << assignment->first << " = " << assignment->second;
157  }
158  else
159  {
160  out << *constraint.get_function().get();
161  }
162 
163  return out;
164  }
165 
166  std::string Constraint::to_string() const
167  {
168  std::stringstream ss;
169  ss << *this;
170  return ss.str();
171  }
172 
174  {
175  return std::get_if<std::pair<BooleanFunction, BooleanFunction>>(&constraint);
176  }
177 
179  {
180  if (this->is_assignment())
181  {
182  return OK(&std::get<std::pair<BooleanFunction, BooleanFunction>>(constraint));
183  }
184  return ERR("constraint is not an assignment");
185  }
186 
188  {
189  if (!this->is_assignment())
190  {
191  return OK(&std::get<BooleanFunction>(constraint));
192  }
193  return ERR("constraint is not a function");
194  }
195 
196  Model::Model(const std::map<std::string, std::tuple<u64, u16>>& _model) : model(_model)
197  {
198  }
199 
200  bool Model::operator==(const Model& other) const
201  {
202  return this->model == other.model;
203  }
204 
205  bool Model::operator!=(const Model& other) const
206  {
207  return !(*this == other);
208  }
209 
210  std::ostream& operator<<(std::ostream& out, const Model& model)
211  {
212  auto s = std::accumulate(model.model.begin(), model.model.end(), std::string(), [](auto accumulator, auto entry) -> std::string {
213  return accumulator + ", " + std::get<0>(entry) + ":" + std::to_string(std::get<0>(std::get<1>(entry)));
214  });
215 
216  out << "{" << s << "}";
217  return out;
218  }
219 
220  Result<Model> Model::parse(const std::string& s, const SolverType& type)
221  {
222  // TODO:
223  // check how to attach a local parser context variable to the x3 parser
224  // in order to prevent the necessity of a mutex and thus speed-up
225  // parallel processing.
226 
227  // to transform the current parser implementation into a thread-safe
228  // implementation, we use a static mutex variable that is locked-upon
229  // entry in order to prevent any overwrite of the parser_context data
230  static std::mutex mutex;
231 
232  // (1) reset current context in a thread-safe way
233  std::lock_guard<std::mutex> lock(mutex);
235 
236  // (2) parse model using the SMT-LIB grammars for the different solver
237  auto iter = s.begin();
238  auto ok = [&]() -> bool {
239  switch (type)
240  {
241  case SolverType::Z3:
242  return boost::spirit::x3::phrase_parse(iter, s.end(), ModelParser::Z3_MODEL_GRAMMAR, boost::spirit::x3::space);
244  return boost::spirit::x3::phrase_parse(iter, s.end(), ModelParser::BOOLECTOR_MODEL_GRAMMAR, boost::spirit::x3::space);
246  return boost::spirit::x3::phrase_parse(iter, s.end(), ModelParser::Z3_MODEL_GRAMMAR, boost::spirit::x3::space);
247 
248  default:
249  return false;
250  }
251  }();
252 
253  if (ok && (iter == s.end()))
254  {
256  }
257 
258  return ERR("could not parse SMT-Lib model");
259  }
260 
262  {
263  std::vector<BooleanFunction::Node> new_nodes;
264 
265  for (const auto& node : bf.get_nodes())
266  {
267  if (node.is_variable())
268  {
269  const auto var_name = node.variable;
270  if (auto it = model.find(var_name); it != model.end())
271  {
272  const auto constant = BooleanFunction::Const(std::get<0>(it->second), std::get<1>(it->second));
273  new_nodes.insert(new_nodes.end(), constant.get_nodes().begin(), constant.get_nodes().end());
274  }
275  else
276  {
277  new_nodes.push_back(node);
278  }
279  }
280  else
281  {
282  new_nodes.push_back(node);
283  }
284  }
285 
286  auto build_res = BooleanFunction::build(std::move(new_nodes));
287  if (build_res.is_error())
288  {
289  return ERR_APPEND(build_res.get_error(), "failed to evaluate Boolean function for the model: failed to build function after replacing nodes.");
290  }
291 
292  return OK(build_res.get().simplify_local());
293  }
294 
295  SolverResult::SolverResult(SolverResultType _type, std::optional<Model> _model) : type(_type), model(_model)
296  {
297  }
298 
299  SolverResult SolverResult::Sat(const std::optional<Model>& model)
300  {
302  }
303 
305  {
307  }
308 
310  {
312  }
313 
314  bool SolverResult::is(const SolverResultType& _type) const
315  {
316  return this->type == _type;
317  }
318 
319  bool SolverResult::is_sat() const
320  {
321  return this->is(SolverResultType::Sat);
322  }
323 
325  {
326  return this->is(SolverResultType::UnSat);
327  }
328 
330  {
331  return this->is(SolverResultType::Unknown);
332  }
333 
334  std::ostream& operator<<(std::ostream& out, const SolverResult& result)
335  {
336  out << "{";
337  out << "type : " << enum_to_string(result.type) << ", ";
338  out << "model: ";
339  if (result.model.has_value())
340  {
341  out << *result.model;
342  }
343  else
344  {
345  out << "none";
346  }
347  out << "}";
348  return out;
349  }
350  } // namespace SMT
351 
352  template<>
353  std::map<SMT::SolverType, std::string> EnumStrings<SMT::SolverType>::data = {{SMT::SolverType::Z3, "Z3"},
354  {SMT::SolverType::Boolector, "Boolector"},
355  {SMT::SolverType::Bitwuzla, "Bitwuzla"},
356  {SMT::SolverType::Unknown, "Unknown"}};
357 
358  template<>
359  std::map<SMT::SolverCall, std::string> EnumStrings<SMT::SolverCall>::data = {{SMT::SolverCall::Library, "Library"}, {SMT::SolverCall::Binary, "Binary"}};
360 
361  template<>
362  std::map<SMT::SolverResultType, std::string> EnumStrings<SMT::SolverResultType>::data = {{SMT::SolverResultType::Sat, "sat"},
363  {SMT::SolverResultType::UnSat, "unsat"},
364  {SMT::SolverResultType::Unknown, "unknown"}};
365 } // namespace hal
static Result< BooleanFunction > build(std::vector< Node > &&nodes)
const std::vector< BooleanFunction::Node > & get_nodes() const
static BooleanFunction Const(const BooleanFunction::Value &value)
uint64_t u64
Definition: defines.h:42
#define ERR(message)
Definition: result.h:53
#define OK(...)
Definition: result.h:49
#define ERR_APPEND(prev_error, message)
Definition: result.h:57
const auto ValueRule
Definition: types.cpp:70
ParserContext parser_context
Definition: types.cpp:31
const auto HexValueRule
Definition: types.cpp:68
const auto HexValueAction
Definition: types.cpp:40
const auto SortRule
Definition: types.cpp:59
const auto BOOLECTOR_MODEL_GRAMMAR
Definition: types.cpp:81
const auto SignalAssignmentRule
Definition: types.cpp:76
const auto Z3_MODEL_GRAMMAR
Definition: types.cpp:86
const auto SortAction
Definition: types.cpp:36
const auto BinaryValueAction
Definition: types.cpp:38
const auto BinaryValueRule
Definition: types.cpp:64
const auto NameAction
Definition: types.cpp:34
const auto NameRule
Definition: types.cpp:55
const auto SignalAssigmentAction
Definition: types.cpp:43
SolverCall
Definition: types.h:52
SolverType
Definition: types.h:44
SolverResultType
Definition: types.h:217
std::ostream & operator<<(std::ostream &out, const QueryConfig &config)
Definition: types.cpp:131
std::string enum_to_string(T e)
Definition: enums.h:52
PinType type
std::string to_string() const
Definition: types.cpp:166
bool is_assignment() const
Definition: types.cpp:173
std::variant< BooleanFunction, std::pair< BooleanFunction, BooleanFunction > > constraint
A constraint that is either an assignment of two Boolean functions or a single Boolean function,...
Definition: types.h:154
Constraint(BooleanFunction &&constraint)
Definition: types.cpp:143
Result< const BooleanFunction * > get_function() const
Definition: types.cpp:187
Result< const std::pair< BooleanFunction, BooleanFunction > * > get_assignment() const
Definition: types.cpp:178
Model(const std::map< std::string, std::tuple< u64, u16 >> &model={})
Definition: types.cpp:196
static Result< Model > parse(const std::string &model_str, const SolverType &solver)
Definition: types.cpp:220
bool operator!=(const Model &other) const
Definition: types.cpp:205
std::map< std::string, std::tuple< u64, u16 > > model
maps variable identifiers to a (1) value and (2) its bit-size.
Definition: types.h:233
Result< BooleanFunction > evaluate(const BooleanFunction &bf) const
Definition: types.cpp:261
bool operator==(const Model &other) const
Definition: types.cpp:200
'ParserContext' refers to the data structure that stores parser results.
Definition: types.cpp:18
std::string current_name
stores the current name of a variable assignment
Definition: types.cpp:23
std::string current_value
stores the current value of a variable assignment
Definition: types.cpp:27
std::map< std::string, std::tuple< u64, u16 > > model
stores mapping from variables to its (1) value and (2) bit-size
Definition: types.cpp:20
std::string current_size
stores the current size of a variable assignment
Definition: types.cpp:25
bool generate_model
Controls whether the SMT solver should generate a model in case formula is satisfiable.
Definition: types.h:73
QueryConfig & with_remote_solver()
Definition: types.cpp:107
SolverType solver
The SMT solver identifier.
Definition: types.h:67
u64 timeout_in_seconds
The timeout after which the SMT solver is killed in seconds.
Definition: types.h:75
QueryConfig & without_model_generation()
Definition: types.cpp:119
QueryConfig & with_model_generation()
Definition: types.cpp:113
SolverCall call
The calling format for the SMT solver.
Definition: types.h:69
QueryConfig & with_local_solver()
Definition: types.cpp:101
QueryConfig & with_timeout(u64 seconds)
Definition: types.cpp:125
QueryConfig & with_solver(SolverType solver)
Definition: types.cpp:89
QueryConfig & with_call(SolverCall call)
Definition: types.cpp:95
static SolverResult Unknown()
Definition: types.cpp:309
SolverResult()
Default constructor (required for Result<T> initialization)
Definition: types.h:312
std::optional< Model > model
The (optional) model that is only available if type == SMT::ResultType::Sat and model generation is e...
Definition: types.h:305
static SolverResult Sat(const std::optional< Model > &model={})
Definition: types.cpp:299
bool is_sat() const
Definition: types.cpp:319
bool is(const SolverResultType &type) const
Definition: types.cpp:314
bool is_unsat() const
Definition: types.cpp:324
static SolverResult UnSat()
Definition: types.cpp:304
SolverResultType type
Result type of the SMT query.
Definition: types.h:303
bool is_unknown() const
Definition: types.cpp:329