1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Andres Noetzli, Morgan Deters |
4 |
|
* |
5 |
|
* This file is part of the cvc5 project. |
6 |
|
* |
7 |
|
* Copyright (c) 2009-2021 by the authors listed in the file AUTHORS |
8 |
|
* in the top-level source directory and their institutional affiliations. |
9 |
|
* All rights reserved. See the file COPYING in the top-level source |
10 |
|
* directory for licensing information. |
11 |
|
* **************************************************************************** |
12 |
|
* |
13 |
|
* Definitions of SMT2 constants. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "cvc5parser_private.h" |
17 |
|
|
18 |
|
#ifndef CVC5__PARSER__SMT2_H |
19 |
|
#define CVC5__PARSER__SMT2_H |
20 |
|
|
21 |
|
#include <sstream> |
22 |
|
#include <stack> |
23 |
|
#include <string> |
24 |
|
#include <unordered_map> |
25 |
|
#include <utility> |
26 |
|
|
27 |
|
#include "api/cpp/cvc5.h" |
28 |
|
#include "parser/parse_op.h" |
29 |
|
#include "parser/parser.h" |
30 |
|
#include "theory/logic_info.h" |
31 |
|
|
32 |
|
namespace cvc5 { |
33 |
|
|
34 |
|
class Command; |
35 |
|
|
36 |
|
namespace api { |
37 |
|
class Solver; |
38 |
|
} |
39 |
|
|
40 |
|
namespace parser { |
41 |
|
|
42 |
|
class Smt2 : public Parser |
43 |
|
{ |
44 |
|
friend class ParserBuilder; |
45 |
|
|
46 |
|
private: |
47 |
|
/** Has the logic been set (either by forcing it or a set-logic command)? */ |
48 |
|
bool d_logicSet; |
49 |
|
/** Have we seen a set-logic command yet? */ |
50 |
|
bool d_seenSetLogic; |
51 |
|
|
52 |
|
LogicInfo d_logic; |
53 |
|
std::unordered_map<std::string, api::Kind> operatorKindMap; |
54 |
|
/** |
55 |
|
* Maps indexed symbols to the kind of the operator (e.g. "extract" to |
56 |
|
* BITVECTOR_EXTRACT). |
57 |
|
*/ |
58 |
|
std::unordered_map<std::string, api::Kind> d_indexedOpKindMap; |
59 |
|
std::pair<api::Term, std::string> d_lastNamedTerm; |
60 |
|
/** |
61 |
|
* A list of sygus grammar objects. We keep track of them here to ensure that |
62 |
|
* they don't get deleted before the commands using them get invoked. |
63 |
|
*/ |
64 |
|
std::vector<std::unique_ptr<api::Grammar>> d_allocGrammars; |
65 |
|
|
66 |
|
protected: |
67 |
|
Smt2(api::Solver* solver, |
68 |
|
SymbolManager* sm, |
69 |
|
bool strictMode = false, |
70 |
|
bool parseOnly = false); |
71 |
|
|
72 |
|
public: |
73 |
|
~Smt2(); |
74 |
|
|
75 |
|
/** |
76 |
|
* Add core theory symbols to the parser state. |
77 |
|
*/ |
78 |
|
void addCoreSymbols(); |
79 |
|
|
80 |
|
void addOperator(api::Kind k, const std::string& name); |
81 |
|
|
82 |
|
/** |
83 |
|
* Registers an indexed function symbol. |
84 |
|
* |
85 |
|
* @param tKind The kind of the term that uses the operator kind (e.g. |
86 |
|
* BITVECTOR_EXTRACT). NOTE: this is an internal kind for now |
87 |
|
* because that is what we use to create expressions. Eventually |
88 |
|
* it will be an api::Kind. |
89 |
|
* @param opKind The kind of the operator term (e.g. BITVECTOR_EXTRACT) |
90 |
|
* @param name The name of the symbol (e.g. "extract") |
91 |
|
*/ |
92 |
|
void addIndexedOperator(api::Kind tKind, |
93 |
|
api::Kind opKind, |
94 |
|
const std::string& name); |
95 |
|
|
96 |
|
api::Kind getOperatorKind(const std::string& name) const; |
97 |
|
|
98 |
|
bool isOperatorEnabled(const std::string& name) const; |
99 |
|
|
100 |
|
bool isTheoryEnabled(theory::TheoryId theory) const; |
101 |
|
|
102 |
|
/** |
103 |
|
* Checks if higher-order support is enabled. |
104 |
|
* |
105 |
|
* @return true if higher-order support is enabled, false otherwise |
106 |
|
*/ |
107 |
|
bool isHoEnabled() const; |
108 |
|
|
109 |
|
bool logicIsSet() override; |
110 |
|
|
111 |
|
/** |
112 |
|
* Creates an indexed constant, e.g. (_ +oo 8 24) (positive infinity |
113 |
|
* as a 32-bit floating-point constant). |
114 |
|
* |
115 |
|
* @param name The name of the constant (e.g. "+oo") |
116 |
|
* @param numerals The parameters for the constant (e.g. [8, 24]) |
117 |
|
* @return The term corresponding to the constant or a parse error if name is |
118 |
|
* not valid. |
119 |
|
*/ |
120 |
|
api::Term mkIndexedConstant(const std::string& name, |
121 |
|
const std::vector<uint64_t>& numerals); |
122 |
|
|
123 |
|
/** |
124 |
|
* Creates an indexed operator term, e.g. (_ extract 5 0). |
125 |
|
* |
126 |
|
* @param name The name of the operator (e.g. "extract") |
127 |
|
* @param numerals The parameters for the operator (e.g. [5, 0]) |
128 |
|
* @return The operator term corresponding to the indexed operator or a parse |
129 |
|
* error if the name is not valid. |
130 |
|
*/ |
131 |
|
api::Op mkIndexedOp(const std::string& name, |
132 |
|
const std::vector<uint64_t>& numerals); |
133 |
|
|
134 |
|
/** |
135 |
|
* Returns the expression that name should be interpreted as. |
136 |
|
*/ |
137 |
|
api::Term getExpressionForNameAndType(const std::string& name, |
138 |
|
api::Sort t) override; |
139 |
|
|
140 |
|
/** |
141 |
|
* If we are in a version < 2.6, this updates name to the tester name of cons, |
142 |
|
* e.g. "is-cons". |
143 |
|
*/ |
144 |
|
bool getTesterName(api::Term cons, std::string& name) override; |
145 |
|
|
146 |
|
/** Make function defined by a define-fun(s)-rec command. |
147 |
|
* |
148 |
|
* fname : the name of the function. |
149 |
|
* sortedVarNames : the list of variable arguments for the function. |
150 |
|
* t : the range type of the function we are defining. |
151 |
|
* |
152 |
|
* This function will create a bind a new function term to name fname. |
153 |
|
* The type of this function is |
154 |
|
* Parser::mkFlatFunctionType(sorts,t,flattenVars), |
155 |
|
* where sorts are the types in the second components of sortedVarNames. |
156 |
|
* As descibed in Parser::mkFlatFunctionType, new bound variables may be |
157 |
|
* added to flattenVars in this function if the function is given a function |
158 |
|
* range type. |
159 |
|
*/ |
160 |
|
api::Term bindDefineFunRec( |
161 |
|
const std::string& fname, |
162 |
|
const std::vector<std::pair<std::string, api::Sort>>& sortedVarNames, |
163 |
|
api::Sort t, |
164 |
|
std::vector<api::Term>& flattenVars); |
165 |
|
|
166 |
|
/** Push scope for define-fun-rec |
167 |
|
* |
168 |
|
* This calls Parser::pushScope() and sets up |
169 |
|
* initial information for reading a body of a function definition |
170 |
|
* in the define-fun-rec and define-funs-rec command. |
171 |
|
* The input parameters func/flattenVars are the result |
172 |
|
* of a call to mkDefineRec above. |
173 |
|
* |
174 |
|
* func : the function whose body we are defining. |
175 |
|
* sortedVarNames : the list of variable arguments for the function. |
176 |
|
* flattenVars : the implicit variables introduced when defining func. |
177 |
|
* |
178 |
|
* This function: |
179 |
|
* (1) Calls Parser::pushScope(). |
180 |
|
* (2) Computes the bound variable list for the quantified formula |
181 |
|
* that defined this definition and stores it in bvs. |
182 |
|
*/ |
183 |
|
void pushDefineFunRecScope( |
184 |
|
const std::vector<std::pair<std::string, api::Sort>>& sortedVarNames, |
185 |
|
api::Term func, |
186 |
|
const std::vector<api::Term>& flattenVars, |
187 |
|
std::vector<api::Term>& bvs); |
188 |
|
|
189 |
|
void reset() override; |
190 |
|
|
191 |
|
/** |
192 |
|
* Creates a command that adds an invariant constraint. |
193 |
|
* |
194 |
|
* @param names Name of four symbols corresponding to the |
195 |
|
* function-to-synthesize, precondition, postcondition, |
196 |
|
* transition relation. |
197 |
|
* @return The command that adds an invariant constraint |
198 |
|
*/ |
199 |
|
std::unique_ptr<Command> invConstraint(const std::vector<std::string>& names); |
200 |
|
|
201 |
|
/** |
202 |
|
* Sets the logic for the current benchmark. Declares any logic and |
203 |
|
* theory symbols. |
204 |
|
* |
205 |
|
* @param name the name of the logic (e.g., QF_UF, AUFLIA) |
206 |
|
* @param fromCommand should be set to true if the request originates from a |
207 |
|
* set-logic command and false otherwise |
208 |
|
* @return the command corresponding to setting the logic (if fromCommand |
209 |
|
* is true), and nullptr otherwise. |
210 |
|
*/ |
211 |
|
Command* setLogic(std::string name, bool fromCommand = true); |
212 |
|
|
213 |
|
/** |
214 |
|
* Get the logic. |
215 |
|
*/ |
216 |
|
const LogicInfo& getLogic() const { return d_logic; } |
217 |
|
|
218 |
|
/** |
219 |
|
* Create a Sygus grammar. |
220 |
|
* @param boundVars the parameters to corresponding synth-fun/synth-inv |
221 |
|
* @param ntSymbols the pre-declaration of the non-terminal symbols |
222 |
|
* @return a pointer to the grammar |
223 |
|
*/ |
224 |
|
api::Grammar* mkGrammar(const std::vector<api::Term>& boundVars, |
225 |
|
const std::vector<api::Term>& ntSymbols); |
226 |
|
|
227 |
|
/** |
228 |
|
* Are we using smtlib 2.6 or above? If exact=true, then this method returns |
229 |
|
* false if the input language is not exactly SMT-LIB 2.6. |
230 |
|
*/ |
231 |
544697 |
bool v2_6(bool exact = false) const |
232 |
|
{ |
233 |
544697 |
return d_solver->getOption("input-language") == "LANG_SMTLIB_V2_6"; |
234 |
|
} |
235 |
|
/** Are we using a sygus language? */ |
236 |
|
bool sygus() const; |
237 |
|
|
238 |
|
/** |
239 |
|
* Returns true if the language that we are parsing (SMT-LIB version >=2.5 |
240 |
|
* and SyGuS) treats duplicate double quotes ("") as an escape sequence |
241 |
|
* denoting a single double quote ("). |
242 |
|
*/ |
243 |
531092 |
bool escapeDupDblQuote() const { return v2_6() || sygus(); } |
244 |
|
|
245 |
|
void checkThatLogicIsSet(); |
246 |
|
|
247 |
|
/** |
248 |
|
* Checks whether the current logic allows free sorts. If the logic does not |
249 |
|
* support free sorts, the function triggers a parse error. |
250 |
|
*/ |
251 |
|
void checkLogicAllowsFreeSorts(); |
252 |
|
|
253 |
|
/** |
254 |
|
* Checks whether the current logic allows functions of non-zero arity. If |
255 |
|
* the logic does not support such functions, the function triggers a parse |
256 |
|
* error. |
257 |
|
*/ |
258 |
|
void checkLogicAllowsFunctions(); |
259 |
|
|
260 |
175809 |
void checkUserSymbol(const std::string& name) |
261 |
|
{ |
262 |
175809 |
if (name.length() > 0 && (name[0] == '.' || name[0] == '@')) |
263 |
|
{ |
264 |
|
std::stringstream ss; |
265 |
|
ss << "cannot declare or define symbol `" << name |
266 |
|
<< "'; symbols starting with . and @ are reserved in SMT-LIB"; |
267 |
|
parseError(ss.str()); |
268 |
|
} |
269 |
175809 |
else if (isOperatorEnabled(name)) |
270 |
|
{ |
271 |
4 |
std::stringstream ss; |
272 |
2 |
ss << "Symbol `" << name << "' is shadowing a theory function symbol"; |
273 |
4 |
parseError(ss.str()); |
274 |
|
} |
275 |
175807 |
} |
276 |
|
|
277 |
|
void includeFile(const std::string& filename); |
278 |
|
|
279 |
4672 |
void setLastNamedTerm(api::Term e, std::string name) |
280 |
|
{ |
281 |
4672 |
d_lastNamedTerm = std::make_pair(e, name); |
282 |
4672 |
} |
283 |
|
|
284 |
70306 |
void clearLastNamedTerm() |
285 |
|
{ |
286 |
70306 |
d_lastNamedTerm = std::make_pair(api::Term(), ""); |
287 |
70306 |
} |
288 |
|
|
289 |
74941 |
std::pair<api::Term, std::string> lastNamedTerm() { return d_lastNamedTerm; } |
290 |
|
|
291 |
|
/** Does name denote an abstract value? (of the form '@n' for numeral n). */ |
292 |
|
bool isAbstractValue(const std::string& name); |
293 |
|
|
294 |
|
/** Make abstract value |
295 |
|
* |
296 |
|
* Abstract values are used for processing get-value calls. The argument |
297 |
|
* name should be such that isAbstractValue(name) is true. |
298 |
|
*/ |
299 |
|
api::Term mkAbstractValue(const std::string& name); |
300 |
|
|
301 |
|
/** |
302 |
|
* Smt2 parser provides its own checkDeclaration, which does the |
303 |
|
* same as the base, but with some more helpful errors. |
304 |
|
*/ |
305 |
7647295 |
void checkDeclaration(const std::string& name, |
306 |
|
DeclarationCheck check, |
307 |
|
SymbolType type = SYM_VARIABLE, |
308 |
|
std::string notes = "") |
309 |
|
{ |
310 |
|
// if the symbol is something like "-1", we'll give the user a helpful |
311 |
|
// syntax hint. (-1 is a valid identifier in SMT-LIB, NOT unary minus.) |
312 |
22262604 |
if (name.length() > 1 && name[0] == '-' |
313 |
7647296 |
&& name.find_first_not_of("0123456789", 1) == std::string::npos) |
314 |
|
{ |
315 |
|
std::stringstream ss; |
316 |
|
ss << notes << "You may have intended to apply unary minus: `(- " |
317 |
|
<< name.substr(1) << ")'\n"; |
318 |
|
this->Parser::checkDeclaration(name, check, type, ss.str()); |
319 |
|
return; |
320 |
|
} |
321 |
7647319 |
this->Parser::checkDeclaration(name, check, type, notes); |
322 |
|
} |
323 |
|
/** |
324 |
|
* Notify that expression expr was given name std::string via a :named |
325 |
|
* attribute. |
326 |
|
*/ |
327 |
|
void notifyNamedExpression(api::Term& expr, std::string name); |
328 |
|
|
329 |
|
// Throw a ParserException with msg appended with the current logic. |
330 |
|
inline void parseErrorLogic(const std::string& msg) |
331 |
|
{ |
332 |
|
const std::string withLogic = msg + getLogic().getLogicString(); |
333 |
|
parseError(withLogic); |
334 |
|
} |
335 |
|
|
336 |
|
//------------------------- processing parse operators |
337 |
|
/** |
338 |
|
* Given a parse operator p, apply a type ascription to it. This method is run |
339 |
|
* when we encounter "(as t type)" and information regarding t has been stored |
340 |
|
* in p. |
341 |
|
* |
342 |
|
* This updates p to take into account the ascription. This may include: |
343 |
|
* - Converting an (pre-ascribed) array constant specification "const" to |
344 |
|
* an ascribed array constant specification (as const type) where type is |
345 |
|
* (Array T1 T2) for some T1, T2. |
346 |
|
* - Converting a (nullary or non-nullary) parametric datatype constructor to |
347 |
|
* the specialized constructor for the given type. |
348 |
|
* - Converting an empty set, universe set, or separation nil reference to |
349 |
|
* the respective term of the given type. |
350 |
|
* - If p's expression field is set, then we leave p unchanged, check if |
351 |
|
* that expression has the given type and throw a parse error otherwise. |
352 |
|
*/ |
353 |
|
void parseOpApplyTypeAscription(ParseOp& p, api::Sort type); |
354 |
|
/** |
355 |
|
* This converts a ParseOp to expression, assuming it is a standalone term. |
356 |
|
* |
357 |
|
* In particular: |
358 |
|
* - If p's expression field is set, then that expression is returned. |
359 |
|
* - If p's name field is set, then we look up that name in the symbol table |
360 |
|
* of this class. |
361 |
|
* In other cases, a parse error is thrown. |
362 |
|
*/ |
363 |
|
api::Term parseOpToExpr(ParseOp& p); |
364 |
|
/** |
365 |
|
* Apply parse operator to list of arguments, and return the resulting |
366 |
|
* expression. |
367 |
|
* |
368 |
|
* This method involves two phases. |
369 |
|
* (1) Processing the operator represented by p, |
370 |
|
* (2) Applying that operator to the set of arguments. |
371 |
|
* |
372 |
|
* For (1), this involves determining the kind of the overall expression. We |
373 |
|
* may be in one the following cases: |
374 |
|
* - If p's expression field is set, we may choose to prepend it to args, or |
375 |
|
* otherwise determine the appropriate kind of the overall expression based on |
376 |
|
* this expression. |
377 |
|
* - If p's name field is set, then we get the appropriate symbol for that |
378 |
|
* name, which may involve disambiguating that name if it is overloaded based |
379 |
|
* on the types of args. We then determine the overall kind of the return |
380 |
|
* expression based on that symbol. |
381 |
|
* - p's kind field may be already set. |
382 |
|
* |
383 |
|
* For (2), we construct the overall expression, which may involve the |
384 |
|
* following: |
385 |
|
* - If p is an array constant specification (as const (Array T1 T2)), then |
386 |
|
* we return the appropriate array constant based on args[0]. |
387 |
|
* - If p represents a tuple selector, then we infer the appropriate tuple |
388 |
|
* selector expression based on the type of args[0]. |
389 |
|
* - If the overall kind of the expression is chainable, we may convert it |
390 |
|
* to a left- or right-associative chain. |
391 |
|
* - If the overall kind is MINUS and args has size 1, then we return an |
392 |
|
* application of UMINUS. |
393 |
|
* - If the overall expression is a partial application, then we process this |
394 |
|
* as a chain of HO_APPLY terms. |
395 |
|
*/ |
396 |
|
api::Term applyParseOp(ParseOp& p, std::vector<api::Term>& args); |
397 |
|
//------------------------- end processing parse operators |
398 |
|
private: |
399 |
|
|
400 |
|
void addArithmeticOperators(); |
401 |
|
|
402 |
|
void addTranscendentalOperators(); |
403 |
|
|
404 |
|
void addQuantifiersOperators(); |
405 |
|
|
406 |
|
void addBitvectorOperators(); |
407 |
|
|
408 |
|
void addDatatypesOperators(); |
409 |
|
|
410 |
|
void addStringOperators(); |
411 |
|
|
412 |
|
void addFloatingPointOperators(); |
413 |
|
|
414 |
|
void addSepOperators(); |
415 |
|
|
416 |
|
/** |
417 |
|
* Utility function to create a conjunction of expressions. |
418 |
|
* |
419 |
|
* @param es Expressions in the conjunction |
420 |
|
* @return True if `es` is empty, `e` if `es` consists of a single element |
421 |
|
* `e`, the conjunction of expressions otherwise. |
422 |
|
*/ |
423 |
|
api::Term mkAnd(const std::vector<api::Term>& es); |
424 |
|
}; /* class Smt2 */ |
425 |
|
|
426 |
|
} // namespace parser |
427 |
|
} // namespace cvc5 |
428 |
|
|
429 |
|
#endif /* CVC5__PARSER__SMT2_H */ |