GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/parser/smt2/smt2.h Lines: 22 33 66.7 %
Date: 2021-05-22 Branches: 27 74 36.5 %

Line Exec Source
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
209
   */
210
  Command* setLogic(std::string name, bool fromCommand = true);
211
212
  /**
213
   * Get the logic.
214
   */
215
2521
  const LogicInfo& getLogic() const { return d_logic; }
216
217
  /**
218
   * Create a Sygus grammar.
219
   * @param boundVars the parameters to corresponding synth-fun/synth-inv
220
   * @param ntSymbols the pre-declaration of the non-terminal symbols
221
   * @return a pointer to the grammar
222
   */
223
  api::Grammar* mkGrammar(const std::vector<api::Term>& boundVars,
224
                          const std::vector<api::Term>& ntSymbols);
225
226
  /**
227
   * Are we using smtlib 2.6 or above? If exact=true, then this method returns
228
   * false if the input language is not exactly SMT-LIB 2.6.
229
   */
230
523317
  bool v2_6(bool exact = false) const
231
  {
232
523317
    return language::isInputLang_smt2_6(getLanguage(), exact);
233
  }
234
  /** Are we using a sygus language? */
235
  bool sygus() const;
236
  /** Are we using the sygus version 2.0 format? */
237
  bool sygus_v2() const;
238
239
  /**
240
   * Returns true if the language that we are parsing (SMT-LIB version >=2.5
241
   * and SyGuS) treats duplicate double quotes ("") as an escape sequence
242
   * denoting a single double quote (").
243
   */
244
510342
  bool escapeDupDblQuote() const { return v2_6() || sygus(); }
245
246
  void checkThatLogicIsSet();
247
248
  /**
249
   * Checks whether the current logic allows free sorts. If the logic does not
250
   * support free sorts, the function triggers a parse error.
251
   */
252
  void checkLogicAllowsFreeSorts();
253
254
  /**
255
   * Checks whether the current logic allows functions of non-zero arity. If
256
   * the logic does not support such functions, the function triggers a parse
257
   * error.
258
   */
259
  void checkLogicAllowsFunctions();
260
261
178996
  void checkUserSymbol(const std::string& name)
262
  {
263
178996
    if (name.length() > 0 && (name[0] == '.' || name[0] == '@'))
264
    {
265
      std::stringstream ss;
266
      ss << "cannot declare or define symbol `" << name
267
         << "'; symbols starting with . and @ are reserved in SMT-LIB";
268
      parseError(ss.str());
269
    }
270
178996
    else if (isOperatorEnabled(name))
271
    {
272
4
      std::stringstream ss;
273
2
      ss << "Symbol `" << name << "' is shadowing a theory function symbol";
274
4
      parseError(ss.str());
275
    }
276
178994
  }
277
278
  void includeFile(const std::string& filename);
279
280
4648
  void setLastNamedTerm(api::Term e, std::string name)
281
  {
282
4648
    d_lastNamedTerm = std::make_pair(e, name);
283
4648
  }
284
285
70195
  void clearLastNamedTerm()
286
  {
287
70195
    d_lastNamedTerm = std::make_pair(api::Term(), "");
288
70195
  }
289
290
74814
  std::pair<api::Term, std::string> lastNamedTerm() { return d_lastNamedTerm; }
291
292
  /** Does name denote an abstract value? (of the form '@n' for numeral n). */
293
  bool isAbstractValue(const std::string& name);
294
295
  /** Make abstract value
296
   *
297
   * Abstract values are used for processing get-value calls. The argument
298
   * name should be such that isAbstractValue(name) is true.
299
   */
300
  api::Term mkAbstractValue(const std::string& name);
301
302
  /**
303
   * Smt2 parser provides its own checkDeclaration, which does the
304
   * same as the base, but with some more helpful errors.
305
   */
306
7787551
  void checkDeclaration(const std::string& name,
307
                        DeclarationCheck check,
308
                        SymbolType type = SYM_VARIABLE,
309
                        std::string notes = "")
310
  {
311
    // if the symbol is something like "-1", we'll give the user a helpful
312
    // syntax hint.  (-1 is a valid identifier in SMT-LIB, NOT unary minus.)
313
22680617
    if (name.length() > 1 && name[0] == '-'
314
7787552
        && name.find_first_not_of("0123456789", 1) == std::string::npos)
315
    {
316
      std::stringstream ss;
317
      ss << notes << "You may have intended to apply unary minus: `(- "
318
         << name.substr(1) << ")'\n";
319
      this->Parser::checkDeclaration(name, check, type, ss.str());
320
      return;
321
    }
322
7787575
    this->Parser::checkDeclaration(name, check, type, notes);
323
  }
324
  /**
325
   * Notify that expression expr was given name std::string via a :named
326
   * attribute.
327
   */
328
  void notifyNamedExpression(api::Term& expr, std::string name);
329
330
  // Throw a ParserException with msg appended with the current logic.
331
  inline void parseErrorLogic(const std::string& msg)
332
  {
333
    const std::string withLogic = msg + getLogic().getLogicString();
334
    parseError(withLogic);
335
  }
336
337
  //------------------------- processing parse operators
338
  /**
339
   * Given a parse operator p, apply a type ascription to it. This method is run
340
   * when we encounter "(as t type)" and information regarding t has been stored
341
   * in p.
342
   *
343
   * This updates p to take into account the ascription. This may include:
344
   * - Converting an (pre-ascribed) array constant specification "const" to
345
   * an ascribed array constant specification (as const type) where type is
346
   * (Array T1 T2) for some T1, T2.
347
   * - Converting a (nullary or non-nullary) parametric datatype constructor to
348
   * the specialized constructor for the given type.
349
   * - Converting an empty set, universe set, or separation nil reference to
350
   * the respective term of the given type.
351
   * - If p's expression field is set, then we leave p unchanged, check if
352
   * that expression has the given type and throw a parse error otherwise.
353
   */
354
  void parseOpApplyTypeAscription(ParseOp& p, api::Sort type);
355
  /**
356
   * This converts a ParseOp to expression, assuming it is a standalone term.
357
   *
358
   * In particular:
359
   * - If p's expression field is set, then that expression is returned.
360
   * - If p's name field is set, then we look up that name in the symbol table
361
   * of this class.
362
   * In other cases, a parse error is thrown.
363
   */
364
  api::Term parseOpToExpr(ParseOp& p);
365
  /**
366
   * Apply parse operator to list of arguments, and return the resulting
367
   * expression.
368
   *
369
   * This method involves two phases.
370
   * (1) Processing the operator represented by p,
371
   * (2) Applying that operator to the set of arguments.
372
   *
373
   * For (1), this involves determining the kind of the overall expression. We
374
   * may be in one the following cases:
375
   * - If p's expression field is set, we may choose to prepend it to args, or
376
   * otherwise determine the appropriate kind of the overall expression based on
377
   * this expression.
378
   * - If p's name field is set, then we get the appropriate symbol for that
379
   * name, which may involve disambiguating that name if it is overloaded based
380
   * on the types of args. We then determine the overall kind of the return
381
   * expression based on that symbol.
382
   * - p's kind field may be already set.
383
   *
384
   * For (2), we construct the overall expression, which may involve the
385
   * following:
386
   * - If p is an array constant specification (as const (Array T1 T2)), then
387
   * we return the appropriate array constant based on args[0].
388
   * - If p represents a tuple selector, then we infer the appropriate tuple
389
   * selector expression based on the type of args[0].
390
   * - If the overall kind of the expression is chainable, we may convert it
391
   * to a left- or right-associative chain.
392
   * - If the overall kind is MINUS and args has size 1, then we return an
393
   * application of UMINUS.
394
   * - If the overall expression is a partial application, then we process this
395
   * as a chain of HO_APPLY terms.
396
   */
397
  api::Term applyParseOp(ParseOp& p, std::vector<api::Term>& args);
398
  //------------------------- end processing parse operators
399
 private:
400
401
  void addArithmeticOperators();
402
403
  void addTranscendentalOperators();
404
405
  void addQuantifiersOperators();
406
407
  void addBitvectorOperators();
408
409
  void addDatatypesOperators();
410
411
  void addStringOperators();
412
413
  void addFloatingPointOperators();
414
415
  void addSepOperators();
416
417
  InputLanguage getLanguage() const;
418
419
  /**
420
   * Utility function to create a conjunction of expressions.
421
   *
422
   * @param es Expressions in the conjunction
423
   * @return True if `es` is empty, `e` if `es` consists of a single element
424
   *         `e`, the conjunction of expressions otherwise.
425
   */
426
  api::Term mkAnd(const std::vector<api::Term>& es);
427
}; /* class Smt2 */
428
429
}  // namespace parser
430
}  // namespace cvc5
431
432
#endif /* CVC5__PARSER__SMT2_H */