CBMC
Loading...
Searching...
No Matches
smt2_solver.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module: SMT2 Solver that uses boolbv and the default SAT solver
4
5Author: Daniel Kroening, kroening@kroening.com
6
7\*******************************************************************/
8
9#include <util/message.h>
10#include <util/namespace.h>
11#include <util/simplify_expr.h>
12#include <util/symbol_table.h>
13
16
17#include "smt2_format.h"
18#include "smt2_parser.h"
19
20#include <fstream> // IWYU pragma: keep
21#include <iostream>
22
24{
25public:
31
32protected:
34
35 void setup_commands();
36 void define_constants();
38
39 std::set<irep_idt> constants_done;
40
41 enum
42 {
45 UNSAT
47};
48
50{
51 for(const auto &id : id_map)
52 {
53 if(id.second.type.id() == ID_mathematical_function)
54 continue;
55
56 if(id.second.definition.is_nil())
57 continue;
58
59 const irep_idt &identifier = id.first;
60
61 // already done?
62 if(constants_done.find(identifier)!=constants_done.end())
63 continue;
64
65 constants_done.insert(identifier);
66
67 exprt def = id.second.definition;
70 equal_exprt(symbol_exprt(identifier, id.second.type), def));
71 }
72}
73
75{
76 for(exprt &op : expr.operands())
78
79 if(expr.id()==ID_function_application)
80 {
82
83 if(app.function().id() == ID_symbol)
84 {
85 // look up the symbol
86 auto identifier = to_symbol_expr(app.function()).identifier();
87 auto f_it = id_map.find(identifier);
88
89 if(f_it != id_map.end())
90 {
91 const auto &f = f_it->second;
92
94 f.type.id() == ID_mathematical_function,
95 "type of function symbol must be mathematical_function_type");
96
97 const auto &domain = to_mathematical_function_type(f.type).domain();
98
100 domain.size() == app.arguments().size(),
101 "number of parameters must match number of arguments");
102
103 // Does it have a definition? It's otherwise uninterpreted.
104 if(!f.definition.is_nil())
105 {
106 exprt body = f.definition;
107
108 if(body.id() == ID_lambda)
109 body = to_lambda_expr(body).application(app.arguments());
110
111 expand_function_applications(body); // rec. call
112 expr = body;
113 }
114 }
115 }
116 }
117}
118
120{
121 {
122 commands["assert"] = [this]() {
123 exprt e = expression();
124 if(e.is_not_nil())
125 {
128 }
129 };
130
131 commands["check-sat"] = [this]() {
132 // add constant definitions as constraints
134
135 switch(solver())
136 {
138 std::cout << "sat\n";
139 status = SAT;
140 break;
141
143 std::cout << "unsat\n";
144 status = UNSAT;
145 break;
146
148 std::cout << "error\n";
150 }
151 };
152
153 commands["check-sat-assuming"] = [this]() {
154 std::vector<exprt> assumptions;
155
156 if(next_token() != smt2_tokenizert::OPEN)
157 throw error("check-sat-assuming expects list as argument");
158
159 while(smt2_tokenizer.peek() != smt2_tokenizert::CLOSE &&
160 smt2_tokenizer.peek() != smt2_tokenizert::END_OF_FILE)
161 {
162 auto e = expression(); // any term
164 assumptions.push_back(solver.handle(e));
165 }
166
167 if(next_token() != smt2_tokenizert::CLOSE)
168 throw error("check-sat-assuming expects ')' at end of list");
169
170 // add constant definitions as constraints
172
173 // add the assumptions
174 solver.push(assumptions);
175
176 switch(solver())
177 {
179 std::cout << "sat\n";
180 status = SAT;
181 break;
182
184 std::cout << "unsat\n";
185 status = UNSAT;
186 break;
187
189 std::cout << "error\n";
191 }
192
193 // remove the assumptions again
194 solver.pop();
195 };
196
197 commands["display"] = [this]() {
198 // this is a command that Z3 appears to implement
199 exprt e = expression();
200 if(e.is_not_nil())
201 std::cout << smt2_format(e) << '\n';
202 };
203
204 commands["get-unsat-assumptions"] = [this]() {
205 throw error("not yet implemented");
206 };
207
208 commands["get-value"] = [this]() {
209 std::vector<exprt> ops;
210
211 if(next_token() != smt2_tokenizert::OPEN)
212 throw error("get-value expects list as argument");
213
214 while(smt2_tokenizer.peek() != smt2_tokenizert::CLOSE &&
215 smt2_tokenizer.peek() != smt2_tokenizert::END_OF_FILE)
216 {
217 ops.push_back(expression()); // any term
218 }
219
220 if(next_token() != smt2_tokenizert::CLOSE)
221 throw error("get-value expects ')' at end of list");
222
223 if(status != SAT)
224 throw error("model is not available");
225
226 std::vector<exprt> values;
227 values.reserve(ops.size());
228
229 for(const auto &op : ops)
230 {
231 if(op.id() != ID_symbol)
232 throw error("get-value expects symbol");
233
234 const auto &identifier = to_symbol_expr(op).identifier();
235
236 const auto id_map_it = id_map.find(identifier);
237
238 if(id_map_it == id_map.end())
239 throw error() << "unexpected symbol '" << identifier << '\'';
240
241 const exprt value = solver.get(op);
242
243 if(value.is_nil())
244 throw error() << "no value for '" << identifier << '\'';
245
246 values.push_back(value);
247 }
248
249 std::cout << '(';
250
251 for(std::size_t op_nr = 0; op_nr < ops.size(); op_nr++)
252 {
253 if(op_nr != 0)
254 std::cout << "\n ";
255
256 std::cout << '(' << smt2_format(ops[op_nr]) << ' '
257 << smt2_format(values[op_nr]) << ')';
258 }
259
260 std::cout << ")\n";
261 };
262
263 commands["echo"] = [this]()
264 {
265 auto str_token = next_token();
266 if(str_token != smt2_tokenizert::STRING_LITERAL)
267 throw error("expected string literal");
268
269 std::cout << smt2_format(constant_exprt(str_token.text, string_typet()))
270 << '\n';
271 };
272
273 commands["get-assignment"] = [this]() {
274 // print satisfying assignment for all named expressions
275
276 if(status != SAT)
277 throw error("model is not available");
278
279 bool first = true;
280
281 std::cout << '(';
282 for(const auto &named_term : named_terms)
283 {
284 const symbol_tablet symbol_table;
285 const namespacet ns(symbol_table);
286 const auto value =
287 simplify_expr(solver.get(named_term.second.term), ns);
288
289 if(value.is_constant())
290 {
291 if(first)
292 first = false;
293 else
294 std::cout << '\n' << ' ';
295
296 std::cout << '(' << smt2_format(named_term.second.name) << ' '
297 << smt2_format(value) << ')';
298 }
299 }
300 std::cout << ')' << '\n';
301 };
302
303 commands["get-model"] = [this]() {
304 // print a model for all identifiers
305
306 if(status != SAT)
307 throw error("model is not available");
308
309 const symbol_tablet symbol_table;
310 const namespacet ns(symbol_table);
311
312 bool first = true;
313
314 std::cout << '(';
315 for(const auto &id : id_map)
316 {
317 const symbol_exprt name(id.first, id.second.type);
318 const auto value = simplify_expr(solver.get(name), ns);
319
320 if(value.is_not_nil())
321 {
322 if(first)
323 first = false;
324 else
325 std::cout << '\n' << ' ';
326
327 std::cout << "(define-fun " << smt2_format(name) << ' ';
328
329 if(id.second.type.id() == ID_mathematical_function)
330 throw error("models for functions unimplemented");
331 else
332 std::cout << "() " << smt2_format(id.second.type);
333
334 std::cout << ' ' << smt2_format(value) << ')';
335 }
336 }
337 std::cout << ')' << '\n';
338 };
339
340 commands["simplify"] = [this]() {
341 // this is a command that Z3 appears to implement
342 exprt e = expression();
343 if(e.is_not_nil())
344 {
345 const symbol_tablet symbol_table;
346 const namespacet ns(symbol_table);
348 std::cout << smt2_format(e_simplified) << '\n';
349 }
350 };
351 }
352
353#if 0
354 // TODO:
355 | ( declare-const hsymboli hsorti )
356 | ( declare-datatype hsymboli hdatatype_deci)
357 | ( declare-datatypes ( hsort_deci n+1 ) ( hdatatype_deci n+1 ) )
358 | ( declare-fun hsymboli ( hsorti ??? ) hsorti )
359 | ( declare-sort hsymboli hnumerali )
360 | ( define-fun hfunction_def i )
362 | ( define-funs-rec ( hfunction_deci n+1 ) ( htermi n+1 ) )
363 | ( define-sort hsymboli ( hsymboli ??? ) hsorti )
364 | ( get-assertions )
365 | ( get-info hinfo_flag i )
366 | ( get-option hkeywordi )
367 | ( get-proof )
368 | ( get-unsat-assumptions )
369 | ( get-unsat-core )
370 | ( pop hnumerali )
371 | ( push hnumerali )
372 | ( reset )
373 | ( reset-assertions )
374 | ( set-info hattributei )
375 | ( set-option hoptioni )
376#endif
377}
378
380{
381public:
382 void print(unsigned level, const std::string &message) override
383 {
384 message_handlert::print(level, message);
385
386 if(level < 4) // errors
387 std::cout << "(error \"" << message << "\")\n";
388 else
389 std::cout << "; " << message << '\n';
390 }
391
392 void print(unsigned, const xmlt &) override
393 {
394 }
395
396 void print(unsigned, const jsont &) override
397 {
398 }
399
400 void flush(unsigned) override
401 {
402 std::cout << std::flush;
403 }
404};
405
406int solver(std::istream &in)
407{
408 symbol_tablet symbol_table;
409 namespacet ns(symbol_table);
410
411 smt2_message_handlert message_handler;
412 messaget message(message_handler);
413
414 // this is our default verbosity
415 message_handler.set_verbosity(messaget::M_STATISTICS);
416
417 satcheckt satcheck{message_handler};
418 boolbvt boolbv{ns, satcheck, message_handler};
419
420 smt2_solvert smt2_solver{in, boolbv};
421 bool error_found = false;
422
423 while(!smt2_solver.exit)
424 {
425 try
426 {
427 smt2_solver.parse();
428 }
429 catch(const smt2_tokenizert::smt2_errort &error)
430 {
431 smt2_solver.skip_to_end_of_list();
432 error_found = true;
433 message.error().source_location.set_line(error.get_line_no());
434 message.error() << error.what() << messaget::eom;
435 }
436 catch(const analysis_exceptiont &error)
437 {
438 smt2_solver.skip_to_end_of_list();
439 error_found = true;
440 message.error() << error.what() << messaget::eom;
441 }
442 }
443
444 if(error_found)
445 return 20;
446 else
447 return 0;
448}
449
450int main(int argc, const char *argv[])
451{
452 if(argc==1)
453 return solver(std::cin);
454
455 if(argc!=2)
456 {
457 std::cerr << "usage: smt2_solver file\n";
458 return 1;
459 }
460
461 std::ifstream in(argv[1]);
462 if(!in)
463 {
464 std::cerr << "failed to open " << argv[1] << '\n';
465 return 1;
466 }
467
468 return solver(in);
469}
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:566
Thrown when an unexpected error occurs during the analysis (e.g., when the SAT solver returns an erro...
A constant literal expression.
Definition std_expr.h:3007
virtual exprt get(const exprt &) const =0
Return expr with variables replaced by values from satisfying assignment if available.
virtual exprt handle(const exprt &)=0
Generate a handle, which is an expression that has the same value as the argument in any model that i...
void set_to_true(const exprt &)
For a Boolean expression expr, add the constraint 'expr'.
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Definition dstring.h:38
Equality.
Definition std_expr.h:1339
Base class for all expressions.
Definition expr.h:57
operandst & operands()
Definition expr.h:95
bool is_not_nil() const
Definition irep.h:372
const irep_idt & id() const
Definition irep.h:388
bool is_nil() const
Definition irep.h:368
Definition json.h:27
void set_verbosity(unsigned _verbosity)
Definition message.h:52
virtual void print(unsigned level, const std::string &message)=0
Definition message.cpp:60
source_locationt source_location
Definition message.h:239
Class that provides messages with a built-in verbosity 'level'.
Definition message.h:154
mstreamt & error() const
Definition message.h:401
@ M_STATISTICS
Definition message.h:170
static eomt eom
Definition message.h:289
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition namespace.h:91
void print(unsigned level, const std::string &message) override
void flush(unsigned) override
void print(unsigned, const xmlt &) override
void print(unsigned, const jsont &) override
named_termst named_terms
Definition smt2_parser.h:74
id_mapt id_map
Definition smt2_parser.h:58
std::unordered_map< std::string, std::function< void()> > commands
smt2_tokenizert::smt2_errort error() const
Definition smt2_parser.h:83
smt2_tokenizert::tokent next_token()
smt2_tokenizert smt2_tokenizer
Definition smt2_parser.h:92
smt2_solvert(std::istream &_in, stack_decision_proceduret &_solver)
void expand_function_applications(exprt &)
stack_decision_proceduret & solver
enum smt2_solvert::@5 status
void setup_commands()
std::set< irep_idt > constants_done
void define_constants()
Exception thrown by the tokenizer (and the parser built on top of it) to report a syntactic error at ...
const tokent & peek()
Return the next token without consuming it.
void set_line(const irep_idt &line)
virtual void pop()=0
Pop whatever is on top of the stack.
virtual void push(const std::vector< exprt > &assumptions)=0
Pushes a new context on the stack that consists of the given (possibly empty vector of) assumptions.
String type.
Definition std_types.h:906
Expression to hold a symbol (variable)
Definition std_expr.h:132
The symbol table.
Definition xml.h:21
int main()
Definition example.cpp:18
const function_application_exprt & to_function_application_expr(const exprt &expr)
Cast an exprt to a function_application_exprt.
const lambda_exprt & to_lambda_expr(const exprt &expr)
Cast an exprt to a lambda_exprt.
const mathematical_function_typet & to_mathematical_function_type(const typet &type)
Cast a typet to a mathematical_function_typet.
exprt simplify_expr(exprt src, const namespacet &ns)
static smt2_format_containert< T > smt2_format(const T &_o)
Definition smt2_format.h:28
int solver(std::istream &in)
#define DATA_INVARIANT(CONDITION, REASON)
This condition should be used to document that assumptions that are made on goto_functions,...
Definition invariant.h:534
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
Definition std_expr.h:221
Author: Diffblue Ltd.