SMTStabilizer API
Public API documentation for SMTStabilizer
Loading...
Searching...
No Matches
base_parser.cpp
Go to the documentation of this file.
1/* -*- Source -*-
2 *
3 * An SMT/OMT Parser (Base part)
4 *
5 * Author: Fuqi Jia <jiafq@ios.ac.cn>
6 *
7 * Copyright (C) 2025 Fuqi Jia
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 * DEALINGS IN THE SOFTWARE.
26 */
27// Modified by Xiang Zhang, 2026
28// Additional changes licensed under the MIT License
29#include <algorithm>
30#include <cstdint>
31#include <memory>
32#include <queue>
33#include <stack>
34#include <stdexcept>
35#include <string>
36#include <unordered_map>
37#include <unordered_set>
38#include <utility>
39#include <vector>
40
41#include "parser.h"
42#include "parser/dag.h"
43#include "parser/kind.h"
44#include "parser/util.h"
45
46namespace stabilizer::parser {
47
49 buffer = nullptr;
50 bufptr = nullptr;
51 buflen = 0;
52 line_number = 0;
57 parsing_file = false;
58 in_quantifier_scope = false;
60 placeholder_var_sort = nullptr;
62 node_manager = std::make_unique<NodeManager>();
63 sort_manager = std::make_unique<SortManager>();
64 options = std::make_shared<GlobalOptions>();
65
66 // reverse
67 let_key_map.reserve(1024);
68 preserving_let_key_map.reserve(1024);
69 fun_key_map.reserve(1024);
70 fun_var_map.reserve(1024);
71 sort_key_map.reserve(1024);
72 quant_var_map.reserve(1024);
73 static_functions.reserve(1024);
74 var_names.reserve(1024);
75 temp_var_names.reserve(1024);
76 function_names.reserve(1024);
77}
78
79bool Parser::parse(const std::string &filename) {
80 return parseSmtlib2File(filename);
81}
82
83Parser::Parser(const std::string &filename) {
84 buffer = nullptr;
85 bufptr = nullptr;
86 buflen = 0;
87 line_number = 0;
92 parsing_file = true;
93 in_quantifier_scope = false;
95 placeholder_var_sort = nullptr;
97 node_manager = std::make_unique<NodeManager>();
98 sort_manager = std::make_unique<SortManager>();
99 options = std::make_shared<GlobalOptions>();
100
101 // reverse
102 let_key_map.reserve(1024);
103 preserving_let_key_map.reserve(1024);
104 fun_key_map.reserve(1024);
105 fun_var_map.reserve(1024);
106 sort_key_map.reserve(1024);
107 quant_var_map.reserve(1024);
108 static_functions.reserve(1024);
109 var_names.reserve(1024);
110 temp_var_names.reserve(1024);
111 function_names.reserve(1024);
112
113 parseSmtlib2File(filename);
114}
115
117
119
120std::shared_ptr<DAGNode> Parser::getResult() { return result_node; }
121
124 return result_type;
125 }
126
127 // simple check
128 bool all_true = true;
129 for (auto &assertion : assertions) {
130 if (assertion->isErr()) {
132 return result_type;
133 }
134 else if (assertion->isFalse()) {
135 all_true = false;
137 }
138 else if (assertion->isTrue()) {
139 continue;
140 }
141 else {
142 // unknown assertion
144 return result_type;
145 }
146 }
147 if (all_true) {
149 }
150 return result_type;
151}
152
153// std::shared_ptr<Model> Parser::getModel() {
154// return result_model;
155// }
156
158 // return node_manager->size();
159 // BFS to count the number of nodes
160 // only count the nodes in assertions, assumptions, soft_assertions,
161 // soft_weights, objectives
162 std::unordered_set<std::shared_ptr<DAGNode>> visited;
163 std::queue<std::shared_ptr<DAGNode>> q;
164 for (size_t i = 0; i < assertions.size(); i++) {
165 auto node = assertions[i];
166 q.push(node);
167 visited.insert(node);
168 }
169 for (size_t i = 0; i < assumptions.size(); i++) {
170 for (size_t j = 0; j < assumptions[i].size(); j++) {
171 auto node = assumptions[i][j];
172 q.push(node);
173 visited.insert(node);
174 }
175 }
176 for (size_t i = 0; i < soft_assertions.size(); i++) {
177 auto node = soft_assertions[i];
178 q.push(node);
179 visited.insert(node);
180 }
181 for (size_t i = 0; i < soft_weights.size(); i++) {
182 auto node = soft_weights[i];
183 q.push(node);
184 visited.insert(node);
185 }
186 while (!q.empty()) {
187 auto node = q.front();
188 q.pop();
189 for (size_t i = 0; i < node->getChildrenSize(); i++) {
190 auto child = node->getChild(i);
191 if (visited.find(child) == visited.end()) {
192 visited.insert(child);
193 q.push(child);
194 }
195 }
196 }
197 return visited.size();
198}
199
200// to solver
201std::vector<std::shared_ptr<DAGNode>> Parser::getAssertions() const {
202 return assertions;
203}
204std::unordered_map<std::string, std::unordered_set<size_t>>
208std::vector<std::vector<std::shared_ptr<DAGNode>>>
210 return assumptions;
211}
212std::vector<std::shared_ptr<DAGNode>> Parser::getSoftAssertions() const {
213 return soft_assertions;
214}
215std::vector<std::shared_ptr<DAGNode>> Parser::getSoftWeights() const {
216 return soft_weights;
217}
218std::unordered_map<std::string, std::unordered_set<size_t>>
222
223void Parser::setOption(const std::string &key, const std::string &value) {
224 options->setOption(key, value);
225}
226void Parser::setOption(const std::string &key, const int &value) {
227 options->setOption(key, std::to_string(value));
228}
229void Parser::setOption(const std::string &key, const double &value) {
230 options->setOption(key, std::to_string(value));
231}
232void Parser::setOption(const std::string &key, const bool &value) {
233 options->setOption(key, value ? "true" : "false");
234}
235std::shared_ptr<GlobalOptions> Parser::getOptions() const { return options; }
236std::vector<std::shared_ptr<DAGNode>> Parser::getVariables() const {
237 std::vector<std::shared_ptr<DAGNode>> vars;
238 for (auto &var : var_names) {
239 // std::cout << var.first << ' ' << var.second << std::endl;
240 // if (node_manager->getNode(var.second)->getUseCount() > 1) {
241 // std::cout << (node_manager->getNode(var.second)->getUseCount()) <<
242 // std::endl;
243 node_manager->getNode(var.second)->setName(var.first);
244 vars.emplace_back(node_manager->getNode(var.second));
245 // }
246 }
247 for (auto &var : temp_var_names) {
248 // if (node_manager->getNode(var.second)->getUseCount() > 1)
249 node_manager->getNode(var.second)->setName(var.first);
250 vars.emplace_back(node_manager->getNode(var.second));
251 }
252 return vars;
253}
254std::vector<std::shared_ptr<DAGNode>> Parser::getDeclaredVariables() const {
255 std::vector<std::shared_ptr<DAGNode>> vars;
256 for (auto &var : var_names) {
257 vars.emplace_back(node_manager->getNode(var.second));
258 }
259 return vars;
260}
261std::shared_ptr<DAGNode> Parser::getVariable(const std::string &var_name) {
262 if (var_names.find(var_name) != var_names.end()) {
263 return node_manager->getNode(var_names.at(var_name));
264 }
265 else if (temp_var_names.find(var_name) != temp_var_names.end()) {
266 return node_manager->getNode(temp_var_names.at(var_name));
267 }
269}
270std::vector<std::shared_ptr<DAGNode>> Parser::getFunctions() const {
271 std::vector<std::shared_ptr<DAGNode>> funs;
272 for (auto fun : function_names) {
273 // if (fun_key_map.at(fun)->getUseCount() > 1)
274 funs.emplace_back(fun_key_map.at(fun));
275 }
276 return funs;
277}
278void Parser::setEvaluatePrecision(mpfr_prec_t precision) {
279 options->setEvaluatePrecision(precision);
280}
281mpfr_prec_t Parser::getEvaluatePrecision() const {
282 return options->getEvaluatePrecision();
283}
284void Parser::setEvaluateUseFloating(bool use_floating) {
285 options->setEvaluateUseFloating(use_floating);
286}
288 return options->getEvaluateUseFloating();
289}
290Real Parser::toReal(std::shared_ptr<DAGNode> expr) {
291 ensureNumberValue(expr);
292 condAssert(expr->isCReal() || expr->isCInt(),
293 "Cannot convert non-constant expression to real");
294 if (expr->isPi())
296 if (expr->isE())
298 return expr->getValue()->getNumberValue().toReal(getEvaluatePrecision());
299}
300Integer Parser::toInt(std::shared_ptr<DAGNode> expr) {
301 ensureNumberValue(expr);
302 condAssert(expr->isCInt(),
303 "Cannot convert non-integer expression to integer");
304 return expr->getValue()->getNumberValue().toInteger();
305}
306bool Parser::isZero(std::shared_ptr<DAGNode> expr) {
307 if (expr->isCReal())
308 return toReal(expr) == 0.0;
309 if (expr->isCInt())
310 return toInt(expr) == 0;
311 if (expr->isCBV()) {
312 std::string value = expr->toString();
313 return std::all_of(value.begin(), value.end(), [](char c) { return c != '1'; });
314 }
315 return false;
316}
317bool Parser::isOne(std::shared_ptr<DAGNode> expr) {
318 if (expr->isCReal())
319 return toReal(expr) == 1.0;
320 if (expr->isCInt())
321 return toInt(expr) == 1;
322 if (expr->isCBV()) {
323 std::string value = expr->toString();
324 return std::find(value.begin(), value.end(), '1') == value.end() - 1;
325 }
326 return false;
327}
328bool Parser::isOnes(std::shared_ptr<DAGNode> expr) {
329 if (expr->isCBV()) {
330 std::string value = expr->toString();
331 return std::all_of(value.begin() + 2, value.end(), [](char c) { return c == '1'; });
332 }
333 return false;
334}
335
336void Parser::ensureNumberValue(std::shared_ptr<DAGNode> expr) {
337 if (!expr || !expr->isConst())
338 return;
339 if (expr->getValue() != nullptr)
340 return;
341
342 std::string s = expr->toString();
343 try {
344 if (TypeChecker::isInt(s)) {
345 Integer i(s);
346 expr->setValue(i);
347 }
348 else if (TypeChecker::isReal(s)) {
349 // dynamic precision
350 size_t digits = 0;
351 for (char c : s) {
352 if (std::isdigit(c))
353 digits++;
354 }
355 mpfr_prec_t prec = digits * 4 + 16;
356 Real r(s, prec);
357 expr->setValue(r);
358 }
359 }
360 catch (...) {
361 // raise error
362 err_all(expr, "Cannot convert non-number expression to number", line_number);
363 }
364}
365
366// parse smt-lib2 file
367std::string Parser::getSymbol() {
368 char *beg = bufptr;
369 bool in_scientific_notation = false;
370 bool has_open_bracket = false;
371 int bracket_level = 0;
372
373 // first char was already scanned
374 bufptr++;
375
376 static auto wrapper = [](const std::string &s) {
377 if (s.size() > 1 && s.front() == '|' && s.back() == '|') {
378 std::string t = s.substr(1, s.size() - 2);
383 return s.substr(1, s.size() - 2);
384 }
385 return s;
386 };
387
388 // while not eof
389 while (*bufptr != 0) {
390 switch (scan_mode) {
392 // check if in scientific notation mode
393 if (!in_scientific_notation) {
394 // check if current symbol is the start of scientific notation
395 std::string current(beg, bufptr - beg);
396 size_t e_pos = current.find_first_of("Ee");
397 if (e_pos != std::string::npos && e_pos > 0 &&
398 e_pos == current.size() - 1) {
399 // check if the part before E is a valid real number
400 std::string mantissa = current.substr(0, e_pos);
401 if (TypeChecker::isReal(mantissa)) {
402 // confirm the start of scientific notation
403 in_scientific_notation = true;
404 }
405 }
406 }
407
408 // if in scientific notation mode
409 if (in_scientific_notation) {
410 // handle left parenthesis
411 if (*bufptr == '(') {
412 has_open_bracket = true;
413 bracket_level++;
414 bufptr++;
415 continue;
416 }
417 // handle right parenthesis
418 else if (*bufptr == ')' && has_open_bracket) {
419 bracket_level--;
420 if (bracket_level == 0) {
421 // right parenthesis matched, end scientific notation
422 bufptr++;
423 std::string tmp_s(beg, bufptr - beg);
425 return wrapper(tmp_s);
426 }
427 bufptr++;
428 continue;
429 }
430 // handle space, allow space in scientific notation mode
431 else if (isblank(*bufptr)) {
432 bufptr++;
433 continue;
434 }
435 // if encounter newline or other special characters, end scientific
436 // notation mode
437 else if (*bufptr == '\n' || *bufptr == '\r' || *bufptr == '\v' ||
438 *bufptr == '\f' || *bufptr == ';' || *bufptr == '|' ||
439 *bufptr == '"') {
440 in_scientific_notation = false;
441 // return the parsed part
442 std::string tmp_s(beg, bufptr - beg);
443 return wrapper(tmp_s);
444 }
445 }
446 // normal symbol parsing
447 else {
448 if (isblank(*bufptr)) {
449 // out of symbol mode by ' ' and \t
450 std::string tmp_s(beg, bufptr - beg);
451 // skip space
452 bufptr++;
454 return wrapper(tmp_s);
455 }
456 else if (*bufptr == '\n' || *bufptr == '\r' || *bufptr == '\v' ||
457 *bufptr == '\f') {
458 line_number++;
459 // out of symbol mode by '\n', '\r', '\v' and '\f'
460 std::string tmp_s(beg, bufptr - beg);
461 // skip space
462 bufptr++;
464 return wrapper(tmp_s);
465 }
466 else if (*bufptr == ';' || *bufptr == '|' || *bufptr == '"' ||
467 *bufptr == '(' || *bufptr == ')') {
468 // out of symbol mode by ';', '|', '"', '(' and ')'
469 std::string tmp_s(beg, bufptr - beg);
470 return wrapper(tmp_s);
471 }
472 }
473 break;
474
476 if (*bufptr == '\n' || *bufptr == '\r' || *bufptr == '\v' ||
477 *bufptr == '\f') {
478 line_number++;
479 }
480 else if (*bufptr == '|') {
481 // out of complicated symbol mode
482 bufptr++;
483 std::string tmp_s(beg, bufptr - beg);
484 // skip space
486 return wrapper(tmp_s);
487 }
488 break;
489
491 if (*bufptr == '\n' || *bufptr == '\r' || *bufptr == '\v' ||
492 *bufptr == '\f') {
493 line_number++;
494 }
495 else if (*bufptr == '"') {
496 // process the nested quotes - check if it is an escape quote
497 if (bufptr + 1 < buffer + buflen && *(bufptr + 1) == '"') {
498 // two consecutive quotes are escape quotes, skip the second quote
499 bufptr++;
500 }
501 else {
502 // end of string
503 bufptr++;
504 std::string tmp_s(beg, bufptr - beg);
505 // skip space
507 return wrapper(tmp_s);
508 }
509 }
510 break;
511
512 default:
513 condAssert(false, "Invalid scan mode");
514 }
515
516 // go next char
517 bufptr++;
518 }
519
520 if (parsing_file) {
522 }
523 else {
524 std::string tmp_s(beg, bufptr - beg);
525 // skip space
527 return wrapper(tmp_s);
528 }
529
530 return "";
531}
532
534 // init scan mode
536
537 // while not eof
538 while (*bufptr != 0) {
539 if (*bufptr == '\n' || *bufptr == '\r' || *bufptr == '\v' ||
540 *bufptr == '\f') {
541 line_number++;
542
543 // out of comment mode
546 }
547 else if (scan_mode == SCAN_MODE::SM_COMMON && !isblank(*bufptr)) {
548 switch (*bufptr) {
549 case ';':
550 // encounter comment
552 break;
553 case '|':
554 // encounter next complicated symbol
556 return;
557 case '"':
558 // encounter next std::string
560 return;
561 default:
562 // encounter next symbol
564 return;
565 }
566 }
567
568 // go next char
569 bufptr++;
570 }
571}
572
574 if (*bufptr == '(') {
575 bufptr++;
577 }
578 else {
580 }
581}
582
584 if (*bufptr == ')') {
585 bufptr++;
587 }
588 else {
590 }
591}
592
594 // skip to next right parenthesis with same depth
596 size_t level = 0;
597
598 while (*bufptr != 0) {
599 if (*bufptr == '\n' || *bufptr == '\r' || *bufptr == '\v' ||
600 *bufptr == '\f') {
601 // new line
602 line_number++;
605 }
606 else if (scan_mode == SCAN_MODE::SM_COMMON) {
607 if (*bufptr == '(')
608 level++;
609 else if (*bufptr == ')') {
610 if (level == 0)
611 return;
612 else
613 level--;
614 }
615 else if (*bufptr == ';')
617 else if (*bufptr == '|')
619 else if (*bufptr == '"')
621 }
622 else if (scan_mode == SCAN_MODE::SM_COMP_SYM && *bufptr == '|')
624 else if (scan_mode == SCAN_MODE::SM_STRING && *bufptr == '"')
626
627 // go to next char
628 bufptr++;
629 }
630}
631
632// parse smt-lib2 file
633bool Parser::parseSmtlib2File(const std::string filename) {
634 /*
635 load file or stdin
636 */
637 if (!filename.empty()) {
638 std::ifstream fin(filename, std::ifstream::binary);
639 if (!fin) {
640 err_open_file(filename);
641 }
642 fin.seekg(0, std::ios::end);
643 buflen = (long)fin.tellg();
644 fin.seekg(0, std::ios::beg);
645 buffer = new char[buflen + 1];
646 fin.read(buffer, buflen);
647 buffer[buflen] = 0;
648 fin.close();
649 }
650 else {
651 // read from stdin until EOF
652 std::string content;
653 std::istreambuf_iterator<char> it(std::cin);
654 std::istreambuf_iterator<char> end;
655 content.assign(it, end);
656 buflen = (long)content.size();
657 buffer = new char[buflen + 1];
658 if (buflen > 0)
659 std::memcpy(buffer, content.data(), buflen);
660 buffer[buflen] = 0;
661 }
662
663 /*
664 parse command
665 */
666 parsing_file = true;
667 bufptr = buffer;
668 if (buflen > 0)
669 line_number = 1;
670
671 // skip to first symbol;
673
674 while (*bufptr) {
675 parseLpar();
677 break;
678 parseRpar();
679 }
680 bufptr = nullptr;
681 delete[] buffer;
682 buffer = nullptr;
683 return true;
684}
685
686char *safe_strdup(const std::string &str) {
687 if (str.empty())
688 return nullptr;
689
690 char *new_str = new (std::nothrow) char[str.length() + 1];
691 if (!new_str)
692 return nullptr;
693
694 std::memcpy(new_str, str.c_str(), str.length() + 1);
695 return new_str;
696}
697
698bool Parser::parseStr(const std::string &constraint) {
699 buffer = safe_strdup(constraint);
700 buflen = constraint.length();
701 bufptr = buffer;
702 if (buflen > 0)
703 line_number = 1;
705 while (*bufptr) {
706 parseLpar();
708 break;
709 parseRpar();
710 }
711 bufptr = nullptr;
712 delete[] buffer;
713 buffer = nullptr;
714 return true;
715}
716
717bool Parser::assert(const std::string &constraint) {
718 parsing_file = false;
719 std::shared_ptr<DAGNode> expr = mkExpr(constraint);
720 assertions.emplace_back(expr);
721 return true;
722}
723
724bool Parser::assert(std::shared_ptr<DAGNode> node) {
725 assertions.emplace_back(node);
726 return true;
727}
728
729std::shared_ptr<DAGNode> Parser::mkExpr(const std::string &expression) {
730 parsing_file = false;
731 if (expression.empty()) {
733 }
734
735 buffer = safe_strdup(expression);
736 if (!buffer) {
738 }
739
740 buflen = expression.length();
741 bufptr = buffer;
742 if (buflen > 0)
743 line_number = 1;
745 std::shared_ptr<DAGNode> expr = parseExpr();
746
747 bufptr = nullptr;
748 delete[] buffer;
749 buffer = nullptr;
750 return expr;
751}
752
754 size_t key_ln = line_number;
755 // std::cout << "line_number = " << key_ln << std::endl;
756 std::string key = getSymbol();
757 // std::cout << "key = " << key << std::endl;
758 if (key == ":id") {
759 return KEYWORD::KW_ID;
760 }
761 else if (key == ":weight") {
762 return KEYWORD::KW_WEIGHT;
763 }
764 else if (key == ":comp") {
765 return KEYWORD::KW_COMP;
766 }
767 else if (key == ":epsilon") {
768 return KEYWORD::KW_EPSILON;
769 }
770 else if (key == ":M") {
771 return KEYWORD::KW_M;
772 }
773 else if (key == ":named") {
774 return KEYWORD::KW_NAMED;
775 }
776 else if (key == ":pattern") {
777 return KEYWORD::KW_PATTERN;
778 }
779 else if (key == ":no-pattern") {
781 }
782 else if (key == ":qid") {
783 return KEYWORD::KW_QID;
784 }
785 else if (key == ":skolemid") {
787 }
788 else if (key == ":lblpos") {
789 return KEYWORD::KW_LBLPOS;
790 }
791 else if (key == ":lblneg") {
792 return KEYWORD::KW_LBLNEG;
793 }
794 else {
795 err_unkwn_sym(key, key_ln);
796 }
797 return KEYWORD::KW_NULL;
798}
799
801 size_t command_ln = line_number;
802 std::string command = getSymbol();
803
804 // (assert <expr>) or (assert <expr> [:id <symbol>])
805 if (command == "assert") {
806 std::string grp_id = "";
807 std::string named_name = "";
808
810 if (key == KEYWORD::KW_ID) {
811 // (assert [:id <symbol>] <expr>)
812 grp_id = getSymbol();
813 }
814 std::shared_ptr<DAGNode> assert_expr = parseExpr();
815 size_t index = assertions.size();
816 assertions.emplace_back(assert_expr);
817 //
818 if (grp_id == "") {
820 if (key_ == KEYWORD::KW_ID) {
821 // (assert <expr> [:id <symbol>])
822 grp_id = getSymbol();
823 }
824 }
825 if (named_name == "") {
827 if (key_ == KEYWORD::KW_NAMED) {
828 // (assert <expr> (! expr :named <symbol>))
829 named_name = getSymbol();
830 }
831 }
832 // if grp_id is not empty, insert to assertion_groups
833 if (grp_id != "") {
834 if (assertion_groups.find(grp_id) == assertion_groups.end()) {
835 assertion_groups.insert(
836 std::pair<std::string, std::unordered_set<size_t>>(grp_id,
837 {index}));
838 }
839 else {
840 assertion_groups[grp_id].insert(index);
841 }
842 }
843 // if named_name is not empty, insert to named_assertions
844 if (named_name != "") {
845 named_assertions[named_name] = assert_expr;
846 }
847 skipToRpar();
848 return CMD_TYPE::CT_ASSERT;
849 }
850
851 // (check-sat)
852 if (command == "check-sat") {
853 options->check_sat = true;
854 skipToRpar();
856 }
857
858 // (check-sat-assuming (a1, ..., an)), maybe for future incremental mode.
859 if (command == "check-sat-assuming") {
860 // parse (a1, ..., an)
861 parseLpar();
862 std::vector<std::shared_ptr<DAGNode>> cur_assumptions;
863 while (*bufptr != ')') {
864 std::shared_ptr<DAGNode> assump = parseExpr();
865 cur_assumptions.emplace_back(assump);
866 }
867 assumptions.emplace_back(cur_assumptions);
868 skipToRpar();
870 }
871
872 // (declare-const <symbol> <sort>)
873 if (command == "declare-const") {
874 // get name
875 size_t name_ln = line_number;
876 std::string name = getSymbol();
877
878 // get returned type
879 std::shared_ptr<DAGNode> res = nullptr;
880 std::shared_ptr<Sort> sort = parseSort();
881 res = mkVar(sort, name);
882
883 // multiple declarations
884 if (res->isErr())
885 err_all(res, name, name_ln);
886 skipToRpar();
887
889 }
890
891 // (declare-fun <symbol> (<sort>*) <sort>)
892 if (command == "declare-fun") {
893 // get name
894 size_t name_ln = line_number;
895 std::string name = getSymbol();
896
897 // (declare-fun <symbol> (<sort>*) <sort>)
898 // ^
899 parseLpar();
900 // (declare-fun <symbol> (<sort>*) <sort>)
901 // ^
902 std::shared_ptr<DAGNode> res = nullptr;
903 if (*bufptr == ')') {
904 // (declare-fun <symbol> () <sort>)
905 parseRpar();
906 std::shared_ptr<Sort> out_sort = parseSort();
907 res = mkVar(out_sort, name);
908 }
909 else {
910 // (declare-fun <symbol> (<sort>+) <sort>)
911 std::vector<std::shared_ptr<Sort>> params;
912 while (*bufptr != ')') {
913 params.emplace_back(parseSort());
914 }
915 parseRpar();
916 std::shared_ptr<Sort> out_sort = parseSort();
917 res = mkFuncDec(name, params, out_sort);
918 if (!res->isErr()) {
919 function_names.emplace_back(name);
920 }
921 }
922
923 // multiple declarations
924 if (res->isErr())
925 err_all(res, name, name_ln);
926 skipToRpar();
927
929 }
930
931 // (declare-sort <symbol> <numeral>)
932 if (command == "declare-sort") {
933 // get name
934 std::string name = getSymbol();
935
936 // get numeral
937 std::string numeral = getSymbol();
938 size_t num = std::stoi(numeral);
939
940 // make sort
941 std::shared_ptr<Sort> sort = mkSortDec(name, num);
942 sort_key_map.insert(
943 std::pair<std::string, std::shared_ptr<Sort>>(name, sort));
944 skipToRpar();
945
947 }
948
949 // (declare-datatype <D> <n> (<ctors>)) single datatype (SMT-LIB extension)
950 if (command == "declare-datatype") {
951 // Parse type name and optional arity; tolerate omitted arity
952 std::string type_name = getSymbol();
953 size_t arity = 0;
955 if (*bufptr != '(') {
956 std::string num = getSymbol();
957 try {
958 arity = std::stoul(num);
959 }
960 catch (...) {
961 arity = 0;
962 }
963 }
964 // Register the sort symbol so it can be referenced before
965 // constructors/selectors
966 std::shared_ptr<Sort> dt_sort_sym =
967 sort_manager->createSortDec(type_name, arity);
968 sort_key_map.insert({type_name, dt_sort_sym});
969 datatype_sort_names.insert(type_name);
970
971 // Parse constructors list
972 std::vector<DTTypeDecl> block;
973 DTTypeDecl type_decl;
974 type_decl.name = type_name;
975 type_decl.arity = arity;
976 parseLpar();
977 while (*bufptr != ')') {
978 // A constructor can be either a bare symbol or a parenthesized form (ctor
979 // (sel sort) ...)
980 DTConstructorDecl ctor_decl;
981 std::vector<std::shared_ptr<Sort>> sel_sorts;
982 if (*bufptr == '(') {
983 // (ctor (sel1 S1) ... (selk Sk))
984 parseLpar();
985 std::string ctor_name = getSymbol();
986 ctor_decl.name = ctor_name;
987 while (*bufptr != ')') {
988 // (sel S)
989 parseLpar();
990 std::string sel_name = getSymbol();
991 std::shared_ptr<Sort> sel_sort = parseSort();
992 parseRpar();
993 // declare selector: (type) -> sel_sort
994 mkFuncDec(sel_name, {dt_sort_sym}, sel_sort);
995 datatype_function_names.insert(sel_name);
996 sel_sorts.push_back(sel_sort);
997 ctor_decl.selectors.push_back(DTSelectorDecl{sel_name, sel_sort});
998 }
999 parseRpar(); // end ctor
1000 // declare constructor: (sel_sorts...) -> type
1001 mkFuncDec(ctor_decl.name, sel_sorts, dt_sort_sym);
1002 datatype_function_names.insert(ctor_decl.name);
1003 }
1004 else {
1005 // bare constructor symbol (enumeration-like): A
1006 std::string ctor_name = getSymbol();
1007 ctor_decl.name = ctor_name;
1008 // no selectors, nullary constructor: () -> type
1009 mkFuncDec(ctor_decl.name, /*params*/ {}, dt_sort_sym);
1010 datatype_function_names.insert(ctor_decl.name);
1011 }
1012 type_decl.ctors.push_back(ctor_decl);
1013 }
1014 parseRpar(); // end constructors list
1015
1016 block.push_back(type_decl);
1017 datatype_blocks.push_back(block);
1018
1019 // consume closing ) of declare-datatype
1020 skipToRpar();
1022 }
1023
1024 // (declare-datatypes ((D1 n1) ... (Dk nk)) ( (ctors1) ... (ctorsk)))
1025 if (command == "declare-datatypes") {
1026 // Parse types header list
1027 parseLpar();
1028 std::vector<std::pair<std::string, size_t>> type_headers;
1029 std::vector<DTTypeDecl> block;
1030 while (*bufptr != ')') {
1031 parseLpar();
1032 std::string tname = getSymbol();
1033 std::string num = getSymbol();
1034 size_t arity = 0;
1035 try {
1036 arity = std::stoul(num);
1037 }
1038 catch (...) {
1039 arity = 0;
1040 }
1041 parseRpar();
1042 type_headers.emplace_back(tname, arity);
1043 // Pre-register sorts for mutual references
1044 std::shared_ptr<Sort> dt_sort_sym =
1045 sort_manager->createSortDec(tname, arity);
1046 sort_key_map.insert({tname, dt_sort_sym});
1047 datatype_sort_names.insert(tname);
1048 DTTypeDecl td;
1049 td.name = tname;
1050 td.arity = arity;
1051 block.push_back(td);
1052 }
1053 parseRpar();
1054
1055 // Parse constructors blocks for each type in order
1056 parseLpar();
1057 for (size_t i = 0; i < type_headers.size(); ++i) {
1058 parseLpar(); // begin constructors for type i
1059 const auto &[tname, arity] = type_headers[i];
1060 std::shared_ptr<Sort> dt_sort_sym = sort_key_map[tname];
1061 // Read each constructor for this type
1062 while (*bufptr != ')') {
1063 parseLpar();
1064 std::string ctor_name = getSymbol();
1065 std::vector<std::shared_ptr<Sort>> sel_sorts;
1066 DTConstructorDecl ctor_decl;
1067 ctor_decl.name = ctor_name;
1068 // std::cout << "!!!" << ctor_name << std::endl;
1069 // Parse optional selectors
1070 while (*bufptr != ')') {
1071 parseLpar();
1072 std::string sel_name = getSymbol();
1073 std::shared_ptr<Sort> sel_sort = parseSort();
1074 // std::cout << sel_sort->kind << std::endl;
1075 parseRpar();
1076 // Declare selector sel: (tname) -> sel_sort
1077 // std::cout << sel_name << std::endl;
1078
1079 mkFuncDec(sel_name, {dt_sort_sym}, sel_sort);
1080 datatype_function_names.insert(sel_name);
1081 sel_sorts.push_back(sel_sort);
1082 ctor_decl.selectors.push_back(DTSelectorDecl{sel_name, sel_sort});
1083 }
1084 parseRpar(); // end ctor
1085 // Declare constructor: (sel_sorts...) -> tname
1086 mkFuncDec(ctor_name, sel_sorts, dt_sort_sym);
1087 datatype_function_names.insert(ctor_name);
1088 block[i].ctors.push_back(ctor_decl);
1089 }
1090 parseRpar(); // end constructors for type i
1091 }
1092 parseRpar(); // end constructors blocks list
1093 datatype_blocks.push_back(block);
1094 skipToRpar();
1095 return CMD_TYPE::CT_DECLARE_SORT; // reuse existing category
1096 }
1097
1098 // (define-const <symbol> <sort> <expr>)
1099 if (command == "define-const") {
1100 // get name
1101 size_t name_ln = line_number;
1102 std::string name = getSymbol();
1103
1104 if (fun_key_map.find(name) != fun_key_map.end()) {
1105 std::shared_ptr<DAGNode> check_fun = fun_key_map[name];
1106 if (check_fun->getKind() == NODE_KIND::NT_FUNC_DEF) {
1107 err_mul_def(name, name_ln);
1108 }
1110 }
1111 // keep the function name with the same order
1112 function_names.emplace_back(name);
1113
1114 // get returned type and body
1115 std::shared_ptr<Sort> out_sort = parseSort();
1116 std::shared_ptr<DAGNode> func_body = parseExpr();
1117 std::vector<std::shared_ptr<DAGNode>> params; // empty params for constant
1118 std::shared_ptr<DAGNode> res = mkFuncDef(name, params, out_sort, func_body);
1119 skipToRpar();
1120
1122 }
1123
1124 //(define-fun <symbol> ((<symbol> <sort>)*) <sort> <expr>)
1125 if (command == "define-fun") {
1126 // get name
1127 size_t name_ln = line_number;
1128 std::string name = getSymbol();
1129 // std::cout << name << std::endl;
1130 if (fun_key_map.find(name) != fun_key_map.end()) {
1131 std::shared_ptr<DAGNode> check_fun = fun_key_map[name];
1132 if (check_fun->getKind() == NODE_KIND::NT_FUNC_DEF) {
1133 err_mul_def(name, name_ln);
1134 }
1136 }
1137 // keep the function name with the same order
1138 function_names.emplace_back(name);
1139
1140 // parse ((x Int))
1141 // ^
1142 parseLpar();
1143 std::vector<std::shared_ptr<DAGNode>> params;
1144 std::vector<std::string> key_list;
1145 while (*bufptr != ')') { // there are still (x Int) left.
1146 // (x Int)
1147 // ^
1148 parseLpar();
1149 std::string pname = getSymbol();
1150 std::shared_ptr<Sort> ptype = parseSort();
1151 key_list.emplace_back(pname);
1152 std::shared_ptr<DAGNode> expr = nullptr;
1153 expr = mkFunParamVar(ptype, name + pname);
1154 // multiple declarations
1155 if (fun_var_map.find(pname) != fun_var_map.end()) {
1156 err_mul_decl(pname, line_number);
1157 }
1158 else {
1159 fun_var_map.insert(
1160 std::pair<std::string, std::shared_ptr<DAGNode>>(pname, expr));
1161 params.emplace_back(expr);
1162 }
1163 // (x Int)
1164 // ^
1165 parseRpar();
1166 }
1167
1168 //(define-fun <symbol> ((<symbol> <sort>)*) <sort> <expr>)
1169 // ^
1170 parseRpar();
1171
1172 // get returned type
1173 std::shared_ptr<Sort> out_sort = parseSort();
1174 std::shared_ptr<DAGNode> func_body = parseExpr();
1175 // for (const auto& param : params) {
1176 // std::cout << param->toString() << ' ';
1177 // }
1178 // std::cout << std::endl;
1179 std::shared_ptr<DAGNode> res = mkFuncDef(name, params, out_sort, func_body);
1180 // fun_key_map.insert(std::pair<std::string, std::shared_ptr<DAGNode>>(name,
1181 // res)); std::cout << res->getName() << std::endl;
1182 skipToRpar();
1183
1184 // remove key bindings: for let uses local variables.
1185 while (key_list.size() > 0) {
1186 fun_var_map.erase(key_list.back());
1187 key_list.pop_back();
1188 }
1189
1191 }
1192
1193 // (define-fun-rec <symbol> ((<symbol> <sort>)*) <sort> <expr>)
1194 // recursive function definition
1195 if (command == "define-fun-rec") {
1196 // get name
1197 size_t name_ln = line_number;
1198 std::string name = getSymbol();
1199
1200 if (fun_key_map.find(name) != fun_key_map.end()) {
1201 std::shared_ptr<DAGNode> check_fun = fun_key_map[name];
1202 if (check_fun->getKind() == NODE_KIND::NT_FUNC_DEF) {
1203 err_mul_def(name, name_ln);
1204 }
1206 }
1207 // keep the function name with the same order
1208 function_names.emplace_back(name);
1209
1210 // parse ((x Int))
1211 // ^
1212 parseLpar();
1213 std::vector<std::shared_ptr<DAGNode>> params;
1214 std::vector<std::string> key_list;
1215 std::vector<std::shared_ptr<Sort>> param_sorts;
1216 while (*bufptr != ')') { // there are still (x Int) left.
1217 // (x Int)
1218 // ^
1219 parseLpar();
1220 std::string pname = getSymbol();
1221 std::shared_ptr<Sort> ptype = parseSort();
1222 key_list.emplace_back(pname);
1223 param_sorts.emplace_back(ptype);
1224 std::shared_ptr<DAGNode> expr = nullptr;
1225 expr = mkFunParamVar(ptype, pname);
1226 // multiple declarations
1227 if (fun_var_map.find(pname) != fun_var_map.end()) {
1228 err_mul_decl(pname, line_number);
1229 }
1230 else {
1231 fun_var_map.insert(
1232 std::pair<std::string, std::shared_ptr<DAGNode>>(pname, expr));
1233 params.emplace_back(expr);
1234 }
1235 // (x Int)
1236 // ^
1237 parseRpar();
1238 }
1239
1240 //(define-fun-rec <symbol> ((<symbol> <sort>)*) <sort> <expr>)
1241 // ^
1242 parseRpar();
1243
1244 // get returned type
1245 std::shared_ptr<Sort> out_sort = parseSort();
1246
1247 // For recursive functions, we need to create a function declaration first
1248 // so it can be referenced in its own body
1249 std::shared_ptr<DAGNode> func_dec = mkFuncDec(name, param_sorts, out_sort);
1250
1251 // Now parse the function body (which can reference the function itself)
1252 std::shared_ptr<DAGNode> func_body = parseExpr();
1253 std::shared_ptr<DAGNode> res = mkFuncRec(name, params, out_sort, func_body);
1254 skipToRpar();
1255
1256 // remove key bindings: for let uses local variables.
1257 while (key_list.size() > 0) {
1258 fun_var_map.erase(key_list.back());
1259 key_list.pop_back();
1260 }
1261
1263 }
1264
1265 if (command == "define-funs-rec") {
1266 // (define-funs-rec ((name1 ((param1 type1)...) ret_type1)...) (body1
1267 // body2...))
1268
1269 // Parse function declarations first
1270 parseLpar(); // for function declarations list
1271 std::vector<std::string> func_names;
1272 std::vector<std::vector<std::shared_ptr<DAGNode>>> all_params;
1273 std::vector<std::vector<std::string>> all_key_lists;
1274 std::vector<std::vector<std::shared_ptr<Sort>>> all_param_sorts;
1275 std::vector<std::shared_ptr<Sort>> return_sorts;
1276
1277 while (*bufptr != ')') {
1278 // Parse each function declaration: (name ((param1 type1)...) ret_type)
1279 parseLpar();
1280 std::string name = getSymbol();
1281
1282 if (fun_key_map.find(name) != fun_key_map.end()) {
1283 std::shared_ptr<DAGNode> check_fun = fun_key_map[name];
1284 if (check_fun->getKind() == NODE_KIND::NT_FUNC_DEF) {
1285 err_mul_def(name, line_number);
1286 }
1287 skipToRpar();
1288 continue;
1289 }
1290
1291 func_names.emplace_back(name);
1292 function_names.emplace_back(name);
1293
1294 // Parse parameters: ((param1 type1)...)
1295 parseLpar();
1296 std::vector<std::shared_ptr<DAGNode>> params;
1297 std::vector<std::string> key_list;
1298 std::vector<std::shared_ptr<Sort>> param_sorts;
1299
1300 while (*bufptr != ')') {
1301 parseLpar();
1302 std::string pname = getSymbol();
1303 std::shared_ptr<Sort> ptype = parseSort();
1304 key_list.emplace_back(pname);
1305 param_sorts.emplace_back(ptype);
1306 std::shared_ptr<DAGNode> expr = mkFunParamVar(ptype, pname);
1307 params.emplace_back(expr);
1308 parseRpar();
1309 }
1310 parseRpar(); // end of parameters
1311
1312 // Parse return type
1313 std::shared_ptr<Sort> out_sort = parseSort();
1314 return_sorts.emplace_back(out_sort);
1315
1316 all_params.emplace_back(params);
1317 all_key_lists.emplace_back(key_list);
1318 all_param_sorts.emplace_back(param_sorts);
1319
1320 parseRpar(); // end of function declaration
1321 }
1322 parseRpar(); // end of function declarations list
1323
1324 // Create function declarations for all functions first
1325 // so they can be referenced in each other's bodies
1326 for (size_t i = 0; i < func_names.size(); i++) {
1327 mkFuncDec(func_names[i], all_param_sorts[i], return_sorts[i]);
1328 }
1329
1330 // Parse function bodies
1331 parseLpar(); // for function bodies list
1332 for (size_t i = 0; i < func_names.size(); i++) {
1333 // Add parameter bindings for this function
1334 for (size_t j = 0; j < all_key_lists[i].size(); j++) {
1335 fun_var_map.insert(std::pair<std::string, std::shared_ptr<DAGNode>>(
1336 all_key_lists[i][j], all_params[i][j]));
1337 }
1338
1339 // Parse function body
1340 std::shared_ptr<DAGNode> func_body = parseExpr();
1341 std::shared_ptr<DAGNode> res =
1342 mkFuncRec(func_names[i], all_params[i], return_sorts[i], func_body);
1343
1344 // Remove parameter bindings for this function
1345 for (const auto &key : all_key_lists[i]) {
1346 fun_var_map.erase(key);
1347 }
1348 }
1349 parseRpar(); // end of function bodies list
1350
1351 skipToRpar();
1353 }
1354
1355 // (define-sort <symbol> (<symbol>*) <sort>)
1356 // <symbol>* is a list of symbols that represent template parameters.
1357 // for example, (define-sort List (T) (List T))
1358 // T is a template parameter.
1359 // then (define-sort IntList () (List Int)) is a valid command.
1360 if (command == "define-sort") {
1361 // get name
1362 std::string name = getSymbol();
1363
1364 // get params (symbols)
1365 std::vector<std::string> param_names;
1366 parseLpar();
1367 while (*bufptr != ')') {
1368 param_names.push_back(getSymbol());
1369 }
1370 parseRpar();
1371
1372 // convert param names to Sort parameters
1373 std::vector<std::shared_ptr<Sort>> params;
1374 for (const auto &name : param_names) {
1375 params.push_back(sort_manager->createSort(name));
1376 }
1377
1378 // get out sort
1379 std::shared_ptr<Sort> out_sort = parseSort();
1380 if (params.size() == 0) {
1381 // it means an alias of the out sort
1382 sort_key_map.insert(
1383 std::pair<std::string, std::shared_ptr<Sort>>(name, out_sort));
1384 }
1385 else {
1386 std::shared_ptr<Sort> sort = mkSortDef(name, params, out_sort);
1387 sort_key_map.insert(
1388 std::pair<std::string, std::shared_ptr<Sort>>(name, sort));
1389 }
1390 skipToRpar();
1392 }
1393
1394 if (command == "echo") {
1395 // ignore
1396 warn_cmd_nsup(command, command_ln);
1397 skipToRpar();
1398 return CMD_TYPE::CT_ECHO;
1399 }
1400
1401 // (exit)
1402 if (command == "exit") {
1403 skipToRpar();
1404 return CMD_TYPE::CT_EXIT;
1405 }
1406
1407 // (get-assertions)
1408 // but used in interactive mode, so ignore it.
1409 if (command == "get-assertions") {
1410 // ignore
1411 warn_cmd_nsup(command, command_ln);
1412 skipToRpar();
1414 }
1415
1416 if (command == "get-assignment") {
1417 // ignore
1418 warn_cmd_nsup(command, command_ln);
1419 skipToRpar();
1421 }
1422
1423 if (command == "get-info") {
1424 // ignore
1425 warn_cmd_nsup(command, command_ln);
1426 skipToRpar();
1427 return CMD_TYPE::CT_GET_INFO;
1428 }
1429
1430 if (command == "get-option") {
1431 // ignore
1432 warn_cmd_nsup(command, command_ln);
1433 skipToRpar();
1435 }
1436
1437 if (command == "get-model") {
1438 // ignore
1439 options->get_model = true;
1440 skipToRpar();
1442 }
1443
1444 if (command == "get-option") {
1445 // ignore
1446 warn_cmd_nsup(command, command_ln);
1447 skipToRpar();
1449 }
1450
1451 if (command == "get-proof") {
1452 // ignore
1453 warn_cmd_nsup(command, command_ln);
1454 skipToRpar();
1456 }
1457
1458 if (command == "get-unsat-assumptions") {
1459 // ignore
1460 warn_cmd_nsup(command, command_ln);
1461 skipToRpar();
1463 }
1464
1465 if (command == "get-unsat-core") {
1466 // ignore
1467 warn_cmd_nsup(command, command_ln);
1468 skipToRpar();
1470 }
1471
1472 if (command == "get-value") {
1473 // ignore
1474 warn_cmd_nsup(command, command_ln);
1475 skipToRpar();
1477 }
1478
1479 if (command == "pop") {
1480 // ignore
1481 warn_cmd_nsup(command, command_ln);
1482 skipToRpar();
1483 return CMD_TYPE::CT_POP;
1484 }
1485
1486 if (command == "push") {
1487 // ignore
1488 warn_cmd_nsup(command, command_ln);
1489 skipToRpar();
1490 return CMD_TYPE::CT_PUSH;
1491 }
1492
1493 if (command == "reset") {
1494 // ignore
1495 warn_cmd_nsup(command, command_ln);
1496 skipToRpar();
1497 return CMD_TYPE::CT_RESET;
1498 }
1499
1500 if (command == "reset-assertions") {
1501 // ignore
1502 warn_cmd_nsup(command, command_ln);
1503 skipToRpar();
1505 }
1506
1507 //<attribute ::= <keyword> | <keyword> <attribute_value>
1508 //(set-info <attribute>)
1509 if (command == "set-info") {
1510 skipToRpar();
1511 return CMD_TYPE::CT_SET_INFO;
1512 }
1513
1514 //(set-logic <symbol>)
1515 if (command == "set-logic") {
1516 size_t type_ln = line_number;
1517 std::string type = getSymbol();
1518 bool is_valid = options->setLogic(type);
1519 if (!is_valid) {
1520 err_unkwn_sym(type, type_ln);
1521 }
1522
1524 }
1525
1526 //<option ::= <attribute>
1527 //(set-option <option>)
1528 if (command == "set-option") {
1529 // skipToRpar();
1531 size_t level = 0;
1532 std::string result = "(" + command + " ";
1533 result += *bufptr;
1534 // std::cout << "(" << command << " " << *bufptr;
1535 while (*bufptr != 0) {
1536 if (*bufptr == '\n' || *bufptr == '\r' || *bufptr == '\v' ||
1537 *bufptr == '\f') {
1538 // new line
1539 line_number++;
1542 }
1543 else if (scan_mode == SCAN_MODE::SM_COMMON) {
1544 if (*bufptr == '(')
1545 level++;
1546 else if (*bufptr == ')') {
1547 if (level == 0)
1548 break;
1549 else
1550 level--;
1551 }
1552 else if (*bufptr == ';')
1554 else if (*bufptr == '|')
1556 else if (*bufptr == '"')
1558 }
1559 else if (scan_mode == SCAN_MODE::SM_COMP_SYM && *bufptr == '|')
1561 else if (scan_mode == SCAN_MODE::SM_STRING && *bufptr == '"')
1563
1564 // go to next char
1565 bufptr++;
1566 // std::cout << *bufptr;
1567 result += *bufptr;
1568 }
1569 // std::cout << std::endl;
1570 parse_options.emplace_back(result);
1572 }
1573
1574 // quantifier
1575 // (quantifier ((<symbol> <sort>)+) <expr>)
1576 if (command == "exists") {
1577 in_quantifier_scope = true;
1579 parseQuant("exists");
1581 if (quant_nesting_depth == 0) {
1582 in_quantifier_scope = false;
1583 }
1584 skipToRpar();
1585 return CMD_TYPE::CT_EXISTS;
1586 }
1587 if (command == "forall") {
1588 in_quantifier_scope = true;
1590 parseQuant("forall");
1592 if (quant_nesting_depth == 0) {
1593 in_quantifier_scope = false;
1594 }
1595 skipToRpar();
1596 return CMD_TYPE::CT_FORALL;
1597 }
1598
1599 err_unkwn_sym(command, command_ln);
1600
1601 return CMD_TYPE::CT_UNKNOWN;
1602}
1603
1604// sort ::= <identifier> | (<identifier> <sort>+)
1605std::shared_ptr<Sort> Parser::parseSort() {
1606 if (*bufptr == ')') {
1607 // all ready to return
1609 }
1610 // cache basic sorts
1611 static const std::unordered_map<std::string, std::shared_ptr<Sort>>
1612 BASIC_SORTS = {{"Bool", SortManager::BOOL_SORT},
1613 {"Int", SortManager::INT_SORT},
1614 {"Real", SortManager::REAL_SORT},
1615 {"RoundingMode", SortManager::ROUNDING_MODE_SORT},
1616 {"String", SortManager::STR_SORT},
1617 {"Float16", SortManager::FLOAT16_SORT},
1618 {"Float32", SortManager::FLOAT32_SORT},
1619 {"Float64", SortManager::FLOAT64_SORT},
1620 {"RegLan", SortManager::REG_SORT},
1621 {"Reg", SortManager::REG_SORT},
1622 {"RegEx", SortManager::REG_SORT}};
1623
1624 if (*bufptr != '(') {
1625 // <identifier>
1626 size_t expr_ln = line_number;
1627 std::string s = getSymbol();
1628
1629 // first check the basic type cache
1630 auto basic_it = BASIC_SORTS.find(s);
1631 if (basic_it != BASIC_SORTS.end()) {
1632 return basic_it->second;
1633 }
1634 // then check the user-defined type
1635 else if (sort_key_map.find(s) != sort_key_map.end()) {
1636 return sort_key_map[s];
1637 }
1638 else
1639 err_unkwn_sym(s, expr_ln);
1640 }
1641 // (<identifier> <sort>+)
1642 // (_ <identifier> <param>+)
1643 parseLpar();
1644 size_t expr_ln = line_number;
1645 std::string s = getSymbol();
1646
1647 // parse identifier and get params
1648 std::shared_ptr<Sort> sort = SortManager::NULL_SORT;
1649 if (s == "Array") {
1650 // (Array S T)
1651 // S: sort of index
1652 // T: sort of value
1653 std::shared_ptr<Sort> sortS = parseSort();
1654 std::shared_ptr<Sort> sortT = parseSort();
1655 std::string sort_key_name =
1656 "ARRAY_" + sortS->toString() + "_" + sortT->toString();
1657 if (sort_key_map.find(sort_key_name) != sort_key_map.end()) {
1658 sort = sort_key_map[sort_key_name];
1659 }
1660 else {
1661 sort = sort_manager->createArraySort(sortS, sortT);
1662 sort_key_map.insert(
1663 std::pair<std::string, std::shared_ptr<Sort>>(sort_key_name, sort));
1664 }
1665 }
1666 else if (s == "Datatype") {
1667 }
1668 else if (s == "Set") {
1669 }
1670 else if (s == "Relation") {
1671 }
1672 else if (s == "Bag") {
1673 }
1674 else if (s == "Sequence") {
1675 }
1676 else if (s == "RegEx") {
1677 // (RegEx <alphabet-sort>)
1678 std::shared_ptr<Sort> alphabet_sort = parseSort();
1679 if (!alphabet_sort->isStr()) {
1680 err_all(ERROR_TYPE::ERR_TYPE_MIS, "RegEx sort expects String alphabet", expr_ln);
1681 }
1682 sort = SortManager::REG_SORT;
1683 }
1684 else if (s == "UF") {
1685 // // (UF S T)
1686 // // S: sort of parameters
1687 // // T: sort of return value
1688 // SortS = parseSort();
1689 // SortT = parseSort();
1690 // return std::make_shared<Sort>(SORT_KIND::SK_UF, "UF", 2, {sortS, sortT});
1691 }
1692 else if (s == "_") {
1693 // (_ <identifier> <param>+)
1694 std::string id = getSymbol();
1695
1696 if (id == "BitVec") {
1697 // (_ BitVec n)
1698 // n: bit-width
1699 std::string n = getSymbol();
1700 std::string sort_key_name = "BV_" + n;
1701 if (sort_key_map.find(sort_key_name) != sort_key_map.end()) {
1702 sort = sort_key_map[sort_key_name];
1703 }
1704 else {
1705 sort = sort_manager->createBVSort(std::stoi(n));
1706 sort_key_map.insert(
1707 std::pair<std::string, std::shared_ptr<Sort>>(sort_key_name, sort));
1708 }
1709 }
1710 else if (id == "FloatingPoint") {
1711 // (_ FloatingPoint e s)
1712 // e: exponent width
1713 // s: significand width
1714 std::string e = getSymbol();
1715 std::string s = getSymbol();
1716 std::string sort_key_name = "FP_" + e + "_" + s;
1717 if (sort_key_map.find(sort_key_name) != sort_key_map.end()) {
1718 sort = sort_key_map[sort_key_name];
1719 }
1720 else {
1721 sort = sort_manager->createFPSort(std::stoi(e), std::stoi(s));
1722 sort_key_map.insert(
1723 std::pair<std::string, std::shared_ptr<Sort>>(sort_key_name, sort));
1724 }
1725 }
1726 else
1727 err_unkwn_sym(s, expr_ln);
1728 }
1729 else if (s == "Tuple") {
1730 // (Tuple S1 S2 ...)
1731 std::vector<std::shared_ptr<Sort>> fields;
1732 // consume field sorts until we reach ')', the outer parseRpar will close it
1734 while (*bufptr != ')') {
1735 std::shared_ptr<Sort> field = parseSort();
1736 fields.push_back(field);
1738 }
1739 sort = sort_manager->createTupleSort(fields);
1740 }
1741 else {
1742 // first check the basic type cache
1743 auto basic_it = BASIC_SORTS.find(s);
1744 if (basic_it != BASIC_SORTS.end()) {
1745 sort = basic_it->second;
1746 }
1747 // then check the user-defined type
1748 else if (sort_key_map.find(s) != sort_key_map.end()) {
1749 sort = sort_key_map[s];
1750 }
1751 else
1752 err_unkwn_sym(s, expr_ln);
1753
1754 if (sort->arity > 0) {
1755 for (size_t i = 0; i < sort->arity; i++) {
1756 std::shared_ptr<Sort> sort_child = parseSort();
1757 if (sort_child->arity != sort_child->children.size())
1758 sort->children.push_back(sort_child);
1759 }
1760 }
1761 }
1762 // err_unkwn_sym(s, expr_ln);
1763 parseRpar();
1764
1765 return sort;
1766}
1767
1768std::vector<std::shared_ptr<DAGNode>> Parser::parseParams() {
1769 std::vector<std::shared_ptr<DAGNode>> params;
1770
1771 while (*bufptr != ')') {
1772 std::shared_ptr<DAGNode> expr = parseExpr();
1773 params.emplace_back(expr);
1774 }
1775
1776 return params;
1777}
1778
1779// struct for let context
1781 std::vector<std::shared_ptr<DAGNode>>
1782 params; // let bind vars for current level
1783 std::vector<std::string> key_list;
1784 std::shared_ptr<DAGNode> result; // Store the result directly
1785 std::shared_ptr<DAGNode> bind_var_list; // LET_BIND_VAR_LIST for current level
1788
1789 LetContext(int level = 0)
1790 : result(nullptr), bind_var_list(nullptr), nesting_level(level), is_complete(false) {}
1791};
1792
1793// parse let expression preserving the let-binding
1794// (let (<keybinding>+) expr), return expr
1795// In this function, the let-binding is preserved, and the let-binding is not
1796// expanded So the bind_var cannot be the same in different let-binding For
1797// example, (let ((x 1) (x 2)) x) is not allowed Use let-chain to parse the let
1798// expression let-chain: [LET_BIND_VAR_LIST, LET_BIND_VAR_LIST, ..., Body]
1799// LET_BIND_VAR_LIST: [(<symbol> expr)]
1800// Body: expr
1801std::shared_ptr<DAGNode> Parser::parsePreservingLet() {
1802 // This function uses an iterative approach instead of recursion to handle
1803 // nested let expressions and constructs let-chain to avoid deep nesting
1804 // issues
1805
1806 // Create a stack to store parsing states and contexts
1807 std::vector<LetContext> stateStack;
1808
1809 // Collect all bind_var_lists for final let-chain construction
1810 std::vector<std::shared_ptr<DAGNode>> all_bind_var_lists;
1811
1812 // Push initial state onto the stack
1813 stateStack.emplace_back(LetContext(0));
1814
1815 // Enter the initial "("
1816 parseLpar();
1817
1818 std::string preserving_let_bind_var_suffix =
1820
1821 // Main loop to handle all nested let expressions
1822 while (!stateStack.empty()) {
1823 auto &currentState = stateStack.back();
1824 auto &params = currentState.params;
1825 auto &key_list = currentState.key_list;
1826
1827 if (!currentState.is_complete) {
1828 // Parse the current let bindings
1829 while (*bufptr != ')') {
1830 // Process binding expression (<symbol> expr)
1831 parseLpar();
1832
1833 size_t name_ln = line_number;
1834 std::string name = getSymbol();
1835 std::string prefixed_name = name + preserving_let_bind_var_suffix;
1836
1837 // Check for duplicate key bindings
1838 if (preserving_let_key_map.find(prefixed_name) !=
1839 preserving_let_key_map.end()) {
1840 // Clean up all variable bindings in the state stack
1841 for (auto &state : stateStack) {
1842 for (const auto &key : state.key_list) {
1843 preserving_let_key_map.erase(key);
1844 }
1845 }
1846 err_sym_mis("Duplicate variable binding: " + name, name_ln);
1847 }
1848
1849 // Parse the expression value (this won't trigger recursive let parsing)
1850 std::shared_ptr<DAGNode> expr = parseExpr();
1851
1852 if (expr->isErr()) {
1853 // Clean up all variable bindings in the state stack
1854 for (auto &state : stateStack) {
1855 for (const auto &key : state.key_list) {
1856 preserving_let_key_map.erase(key);
1857 }
1858 }
1859 err_all(expr, name, name_ln);
1860 }
1861
1862 // make let-binding variable
1863 std::shared_ptr<DAGNode> let_var = mkLetBindVar(prefixed_name, expr);
1864 // Add the binding inside the mkLetBindVar
1865 // Add to params in the correct order - bindings first, body later
1866 params.emplace_back(let_var);
1867 key_list.emplace_back(prefixed_name);
1868
1869 parseRpar();
1870 }
1871
1872 // Create LET_BIND_VAR_LIST for current level and add to collection
1873 currentState.bind_var_list = mkLetBindVarList(params);
1874 all_bind_var_lists.emplace_back(currentState.bind_var_list);
1875
1876 // Finished parsing all bindings for the current let, handle the closing
1877 // parenthesis
1878 parseRpar();
1879 }
1880
1881 // Process the body of the let expression
1882 if (*bufptr == '(' && peekSymbol() == "let") {
1883 // If the body is another let expression, we don't recursively call
1884 // parseLet Instead, push it as a new state onto the stack
1885 parseLpar(); // Consume '('
1886 std::string let_key = getSymbol(); // Consume "let"
1887 condAssert(let_key == "let", "Invalid keyword for let");
1888 parseLpar(); // Consume the second let expression's starting '('
1889
1890 stateStack.emplace_back(LetContext(currentState.nesting_level + 1));
1891 }
1892 else {
1893 if (*bufptr != ')') {
1894 // Parse the let body and store as result
1895 currentState.result = parseExpr();
1896 }
1897
1898 // State processing complete, pop from stack
1899 auto completedState = currentState;
1900 stateStack.pop_back();
1901
1902 // If stack is empty, construct final let-chain and return
1903 if (stateStack.empty()) {
1904 // Create let-chain with all collected bind_var_lists + final body
1905 std::shared_ptr<DAGNode> result =
1906 mkLetChain(all_bind_var_lists, completedState.result);
1907 return result;
1908 }
1909 else {
1910 // Consume the closing parenthesis if needed
1911 parseRpar();
1912
1913 // Pass the result to parent level (don't create let-chain yet)
1914 stateStack.back().result = completedState.result;
1915 stateStack.back().is_complete = true;
1916 }
1917 }
1918 }
1919
1920 // Should not reach here, but added for safety
1922}
1923/*
1924keybinding ::= (<symbol> expr)
1925(let (<keybinding>+) expr), return expr
1926*/
1927std::shared_ptr<DAGNode> Parser::parseLet() {
1928 // This function uses an iterative approach instead of recursion to handle
1929 // nested let expressions
1930
1931 // Create a stack to store parsing states and contexts
1932 std::vector<LetContext> stateStack;
1933
1934 // Push initial state onto the stack
1935 stateStack.emplace_back(LetContext(0));
1936
1937 // Enter the initial "("
1938 parseLpar();
1939
1940 // Main loop to handle all nested let expressions
1941 while (!stateStack.empty()) {
1942 auto &currentState = stateStack.back();
1943 auto &params = currentState.params;
1944 auto &key_list = currentState.key_list;
1945
1946 if (!currentState.is_complete) {
1947 // Parse the current let bindings
1948 while (*bufptr != ')') {
1949 // Process binding expression (<symbol> expr)
1950 parseLpar();
1951
1952 size_t name_ln = line_number;
1953 std::string name = getSymbol();
1954
1955 // Check for duplicate key bindings
1956 if (let_key_map.find(name) != let_key_map.end()) {
1957 // Clean up all variable bindings in the state stack
1958 for (auto &state : stateStack) {
1959 for (const auto &key : state.key_list) {
1960 let_key_map.erase(key);
1961 }
1962 }
1963 err_sym_mis("Duplicate variable binding: " + name, name_ln);
1964 }
1965
1966 // Parse the expression value (this won't trigger recursive let parsing)
1967 std::shared_ptr<DAGNode> expr = parseExpr();
1968
1969 if (expr->isErr()) {
1970 // Clean up all variable bindings in the state stack
1971 for (auto &state : stateStack) {
1972 for (const auto &key : state.key_list) {
1973 let_key_map.erase(key);
1974 }
1975 }
1976 err_all(expr, name, name_ln);
1977 }
1978
1979 // Add the binding
1980 let_key_map.insert(
1981 std::pair<std::string, std::shared_ptr<DAGNode>>(name, expr));
1982 params.emplace_back(expr);
1983 key_list.emplace_back(name);
1984
1985 parseRpar();
1986 }
1987
1988 // Finished parsing all bindings for the current let, handle the closing
1989 // parenthesis
1990 parseRpar();
1991 }
1992
1993 // Process the body of the let expression
1994 if (*bufptr == '(' && peekSymbol() == "let") {
1995 // If the body is another let expression, we don't recursively call
1996 // parseLet Instead, push it as a new state onto the stack
1997 parseLpar(); // Consume '('
1998 std::string let_key = getSymbol(); // Consume "let"
1999 condAssert(let_key == "let", "Invalid keyword for let");
2000 parseLpar(); // Consume the second let expression's starting '('
2001
2002 stateStack.emplace_back(LetContext(currentState.nesting_level + 1));
2003 }
2004 else {
2005 if (*bufptr != ')') {
2006 currentState.result = parseExpr();
2007 }
2008
2009 // Remove all variable bindings for the current state
2010 for (const auto &key : key_list) {
2011 let_key_map.erase(key);
2012 }
2013
2014 // State processing complete, pop from stack
2015 stateStack.pop_back();
2016
2017 // If stack is empty, return the result directly
2018 if (stateStack.empty()) {
2019 return currentState.result;
2020 }
2021 else {
2022 // Consume the closing parenthesis
2023 parseRpar();
2024 // Store the result in the parent context
2025 stateStack.back().result = currentState.result;
2026 stateStack.back().is_complete = true;
2027 }
2028 }
2029 }
2030
2031 // Should not reach here, but added for safety
2033}
2034
2035// Helper function to preview the next symbol without consuming input
2036std::string Parser::peekSymbol() {
2037 char *save_bufptr = bufptr;
2038 SCAN_MODE save_mode = scan_mode;
2039 size_t save_line = line_number;
2040
2041 std::string symbol;
2042 if (*bufptr == '(') {
2043 bufptr++;
2045 symbol = getSymbol();
2046 }
2047 else {
2048 symbol = getSymbol();
2049 }
2050
2051 // Restore state
2052 bufptr = save_bufptr;
2053 scan_mode = save_mode;
2054 line_number = save_line;
2055
2056 return symbol;
2057}
2058
2059std::shared_ptr<DAGNode>
2060Parser::applyFun(std::shared_ptr<DAGNode> fun,
2061 const std::vector<std::shared_ptr<DAGNode>> &params) {
2062 // check the number of params
2063 if (fun->getFuncParamsSize() != params.size()) {
2065 }
2066
2067 // For declare-fun (uninterpreted functions), create an application node
2068 if (fun->getFuncBody()->isNull()) {
2069 // If the function is a datatype constructor/selector, use DT_FUN_APPLY
2070 if (datatype_function_names.find(fun->getName()) !=
2072 return mkApplyDTFun(fun->getSort(), fun->getName(), params);
2073 }
2074 // Otherwise treat as UF application
2075 return node_manager->createNode(fun->getSort(), NODE_KIND::NT_UF_APPLY, fun->getName(), params);
2076 }
2077
2078 // For recursive functions (define-fun-rec), behavior depends on
2079 // expand_recursive_functions option If expand_recursive_functions is true,
2080 // expand it like define-fun If false (default), create a recursive function
2081 // application node to avoid infinite recursion
2082 if (fun->isFuncRec()) {
2083 if (!options->getExpandRecursiveFunctions()) {
2084 // Don't expand recursive functions (default behavior)
2085 return mkApplyRecFunc(fun, params);
2086 }
2087 // Otherwise, fall through to expand it like define-fun
2088 }
2089 else if (fun->isFuncDec()) {
2090 // a only declared function, i.e., uninterpreted function
2091 return mkApplyUF(fun->getSort(), fun->getName(), params);
2092 }
2093
2094 // For regular functions (define-fun), check expand_functions option
2095 if (fun->isFuncDef()) {
2096 if (!options->getExpandFunctions()) {
2097 // Don't expand functions, create a function application node
2098 return mkApplyFunc(fun, params);
2099 }
2100 // Otherwise, fall through to expand the function
2101 }
2102 // std::cout << fun->getName() << std::endl;
2103 if (fun->getFuncBody()->isErr()) {
2104 return fun->getFuncBody();
2105 }
2106
2107 // Expand the function: replace parameters with actual arguments
2108 // variable map for local variables
2109 std::unordered_map<std::string, std::shared_ptr<DAGNode>> new_params_map;
2110 std::vector<std::shared_ptr<DAGNode>> func_params = fun->getFuncParams();
2111 for (size_t i = 0; i < func_params.size(); i++) {
2112 if (params[i]->isErr()) {
2113 return params[i];
2114 }
2115 new_params_map.insert(std::pair<std::string, std::shared_ptr<DAGNode>>(
2116 func_params[i]->getName(), params[i]));
2117 }
2118
2119 // function content
2120 std::shared_ptr<DAGNode> formula = fun->getFuncBody();
2121 // std::cout << "Expanding function: " << fun->getName() << " with " <<
2122 // params.size() << " params." << std::endl; Iterative implementation to
2123 // replace recursive applyFunPostOrder
2124 return applyFunPostOrder(formula, new_params_map);
2125}
2126
2127// Iterative version of post-order traversal function application
2128std::shared_ptr<DAGNode> Parser::applyFunPostOrder(
2129 std::shared_ptr<DAGNode> node,
2130 std::unordered_map<std::string, std::shared_ptr<DAGNode>> &params) {
2131 // for (const auto& p : params) {
2132 // std::cout << "Param: " << p.first << std::endl;
2133 // }
2134 if (!node)
2135 return nullptr;
2136
2137 // Stack to track nodes to process
2138 std::stack<std::pair<std::shared_ptr<DAGNode>, bool>> todo;
2139
2140 // Map to store processed results for each node
2141 std::unordered_map<std::shared_ptr<DAGNode>, std::shared_ptr<DAGNode>>
2142 results;
2143
2144 // Push initial node to stack
2145 todo.push(std::make_pair(node, false));
2146
2147 while (!todo.empty()) {
2148 std::shared_ptr<DAGNode> current = todo.top().first;
2149 bool processed = todo.top().second;
2150 todo.pop();
2151
2152 if (processed) {
2153 // Node is being revisited after processing its children
2154 std::vector<std::shared_ptr<DAGNode>> childResults;
2155
2156 // Collect results from all children
2157 for (size_t i = 0; i < current->getChildrenSize(); i++) {
2158 childResults.emplace_back(results[current->getChild(i)]);
2159 }
2160
2161 // Create a new node with processed children
2162 std::shared_ptr<DAGNode> result;
2163 if (current->isUFApplication() || current->isDtFunApplication()) {
2164 // Preserve kind/name when recreating node
2165 if (current->isDtFunApplication())
2166 result = mkApplyDTFun(current->getSort(), current->getName(), childResults);
2167 else
2168 result =
2169 mkApplyUF(current->getSort(), current->getName(), childResults);
2170 }
2171 else if (current->isFuncRecApplication() &&
2172 !options->getExpandRecursiveFunctions()) {
2173 // NT_FUNC_REC_APPLY: Recursive function call when not expanding
2174 // Must preserve function name when recreating node
2175 result = mkApplyRecFunc(current, childResults);
2176 }
2177 else if (current->isFuncApplication() ||
2178 (current->isFuncRecApplication() &&
2179 options->getExpandRecursiveFunctions())) {
2180 // NT_FUNC_APPLY or NT_FUNC_REC_APPLY (when expanding)
2181 // Parameters have been processed, now expand the function
2182 std::vector<std::shared_ptr<DAGNode>> funcParams;
2183 for (size_t i = 1; i < childResults.size(); i++) {
2184 funcParams.emplace_back(childResults[i]);
2185 }
2186 result = applyFun(current->getFuncBody(), funcParams);
2187 }
2188 else {
2189 // For all other cases: regular operators
2190 // std::cout << kindToString(current->getKind()) << std::endl;
2191 // std::cout << current->getName() << std::endl;
2192 if (!childResults.empty())
2193 result = mkOper(current->getSort(), current->getKind(), childResults);
2194 else
2195 result = current;
2196 // result->setName(current->getName());
2197 }
2198 results[current] = result;
2199 }
2200 else {
2201 // First visit to this node
2202 if (current->isFuncParam()) {
2203 // Function parameter - replace with actual parameter
2204 auto it = params.find(current->getName());
2205 if (it != params.end()) {
2206 results[current] = it->second;
2207 }
2208 else {
2209 // Parameter not found, this should not happen if applyFun is called
2210 // correctly
2211 results[current] = mkErr(ERROR_TYPE::ERR_FUN_LOCAL_VAR);
2212 }
2213 }
2214 else if (current->isConst() || current->isVar()) {
2215 // Constants remain unchanged
2216 results[current] = current;
2217 }
2218 else {
2219 // All other cases: operators, function applications, UF applications,
2220 // etc. Mark the node for revisit after processing children
2221 todo.push(std::make_pair(current, true));
2222
2223 // For function applications that will be expanded, skip the first child
2224 // (function definition itself) For all other nodes, process all
2225 // children
2226 bool isFuncAppToExpand = current->isFuncApplication() ||
2227 (current->isFuncRecApplication() &&
2228 options->getExpandRecursiveFunctions());
2229 int startIdx = isFuncAppToExpand ? 1 : 0;
2230
2231 // Push all children onto the stack in reverse order
2232 for (int i = current->getChildrenSize() - 1; i >= startIdx; i--) {
2233 todo.push(std::make_pair(current->getChild(i), false));
2234 }
2235 }
2236 }
2237 }
2238 // std::cout << results[node]->getChild(0)->getName() << ' ' <<
2239 // results[node]->getChild(1)->getName() << std::endl;
2240 return results[node];
2241}
2242
2243std::shared_ptr<DAGNode>
2244Parser::mkApplyFunc(std::shared_ptr<DAGNode> fun,
2245 const std::vector<std::shared_ptr<DAGNode>> &params) {
2246 std::shared_ptr<DAGNode> res = std::shared_ptr<DAGNode>(
2247 new DAGNode(fun->getSort(), NODE_KIND::NT_FUNC_APPLY, fun->getName()));
2248 res->updateApplyFunc(fun->getSort(), fun, params);
2249 static_functions.emplace_back(res);
2250 return res;
2251}
2252
2253std::shared_ptr<DAGNode>
2254Parser::mkApplyRecFunc(std::shared_ptr<DAGNode> fun,
2255 const std::vector<std::shared_ptr<DAGNode>> &params) {
2256 // Create a recursive function application node (similar to mkApplyFunc)
2257 // Store function definition in children[0] and params in children[1..]
2258 std::shared_ptr<DAGNode> res = std::shared_ptr<DAGNode>(new DAGNode(
2259 fun->getSort(), NODE_KIND::NT_FUNC_REC_APPLY, fun->getName()));
2260 res->updateApplyFunc(fun->getSort(), fun, params, true);
2261 static_functions.emplace_back(res);
2262 return res;
2263}
2264
2265std::shared_ptr<DAGNode>
2266Parser::mkPattern(const std::shared_ptr<Sort> &sort, const std::string &name, const std::vector<std::shared_ptr<DAGNode>> &params) {
2267 return node_manager->createNode(sort, NODE_KIND::NT_PATTERN, name, params);
2268}
2269
2270std::shared_ptr<DAGNode> Parser::mkNoPattern(const std::shared_ptr<Sort> &sort,
2271 const std::string &name,
2272 std::shared_ptr<DAGNode> param) {
2273 return node_manager->createNode(sort, NODE_KIND::NT_NO_PATTERN, name, {param});
2274}
2275
2276std::shared_ptr<DAGNode> Parser::mkWeight(const std::shared_ptr<Sort> &sort,
2277 const std::string &name,
2278 std::shared_ptr<DAGNode> param) {
2279 return node_manager->createNode(sort, NODE_KIND::NT_WEIGHT, name, {param});
2280}
2281
2282std::shared_ptr<DAGNode>
2283Parser::mkAttribute(const std::shared_ptr<Sort> &sort, const std::string &name, const std::vector<std::shared_ptr<DAGNode>> &params) {
2284 return node_manager->createNode(sort, NODE_KIND::NT_ATTRIBUTE, name, params);
2285}
2286std::shared_ptr<DAGNode>
2287Parser::mkApplyUF(const std::shared_ptr<Sort> &sort, const std::string &name, const std::vector<std::shared_ptr<DAGNode>> &params) {
2288 return node_manager->createNode(sort, NODE_KIND::NT_UF_APPLY, name, params);
2289}
2290
2291std::shared_ptr<DAGNode>
2292Parser::mkApplyDTFun(const std::shared_ptr<Sort> &sort, const std::string &name, const std::vector<std::shared_ptr<DAGNode>> &params) {
2293 return node_manager->createNode(sort, NODE_KIND::NT_UF_APPLY, name, params);
2294}
2295
2296// QUANTIFIERS
2297// (quantifier ((<identifier> <sort>)+) <expr>)
2298std::shared_ptr<DAGNode> Parser::mkQuantVar(const std::string &name,
2299 std::shared_ptr<Sort> sort) {
2300 // if (quant_var_map.find(name) != quant_var_map.end()) {
2301 // return quant_var_map[name];
2302 // }
2303 // else {
2304 std::shared_ptr<DAGNode> var =
2305 node_manager->createNode(sort, NODE_KIND::NT_QUANT_VAR, "QVAR_" + std::to_string(num_quant_vars++));
2306 auto [itr, success] = quant_var_map.emplace(name, std::vector{var});
2307 if (!success)
2308 itr->second.emplace_back(var);
2309 return var;
2310 // }
2311}
2312std::shared_ptr<DAGNode> Parser::parseQuant(const std::string &type) {
2313 // (quantifier ((<identifier> <sort>)+) <expr>)
2314 // ^
2315
2316 parseLpar();
2317 std::vector<std::shared_ptr<DAGNode>> params;
2318 std::vector<std::string> quant_var_names;
2319 while (*bufptr != ')') {
2320 // (quantifier ((<identifier> <sort>)+) <expr>)
2321 // ^
2322 parseLpar();
2323 std::string var_name = getSymbol();
2324 std::shared_ptr<Sort> var_sort = parseSort();
2325 std::shared_ptr<DAGNode> var = mkQuantVar(var_name, var_sort);
2326 params.emplace_back(var);
2327 quant_var_names.emplace_back(var_name);
2328 parseRpar();
2329 }
2330 // (quantifier ((<identifier> <sort>)+) <expr>)
2331 // ^
2332 parseRpar();
2333 std::shared_ptr<DAGNode> body = parseExpr();
2334
2335 params.insert(params.begin(), body);
2336 std::shared_ptr<DAGNode> res = NodeManager::NULL_NODE;
2337 if (type == "forall") {
2338 res = mkForall(params);
2339 }
2340 else if (type == "exists") {
2341 res = mkExists(params);
2342 }
2343 else {
2344 condAssert(false, "Invalid quantifier");
2345 }
2346 for (auto &name : quant_var_names) {
2347 // quant_var_map.erase(name);
2348 auto itr = quant_var_map.find(name);
2349 if (itr->second.size() == 1)
2350 quant_var_map.erase(itr);
2351 else
2352 itr->second.pop_back();
2353 }
2354 return res;
2355}
2356
2357std::shared_ptr<DAGNode>
2358Parser::mkForall(const std::vector<std::shared_ptr<DAGNode>> &params) {
2360}
2361std::shared_ptr<DAGNode>
2362Parser::mkExists(const std::vector<std::shared_ptr<DAGNode>> &params) {
2364}
2365
2366std::shared_ptr<DAGNode> Parser::substitute(
2367 std::shared_ptr<DAGNode> expr,
2368 std::unordered_map<std::string, std::shared_ptr<DAGNode>> &params) {
2369 std::unordered_map<std::shared_ptr<DAGNode>, std::shared_ptr<DAGNode>>
2370 visited;
2371 return substitute(expr, params, visited);
2372}
2373// visited is used to avoid infinite loop
2374std::shared_ptr<DAGNode> Parser::substitute(
2375 std::shared_ptr<DAGNode> expr,
2376 std::unordered_map<std::string, std::shared_ptr<DAGNode>> &params,
2377 std::unordered_map<std::shared_ptr<DAGNode>, std::shared_ptr<DAGNode>>
2378 &visited) {
2379 /*
2380 Convert the previously recursive implementation into an iterative,
2381 stack-based post-order traversal to avoid potential stack-overflow on very
2382 deep/large DAGs. The algorithm mirrors the logic of applyFunPostOrder used
2383 elsewhere in this file.
2384 */
2385
2386 // Quick hit: if we already substituted this node, return the cached result.
2387 if (visited.find(expr) != visited.end()) {
2388 return visited[expr];
2389 }
2390
2391 // (node, processed?) processed==false => first time we see the node
2392 // processed==true => all children have been handled
2393 std::stack<std::pair<std::shared_ptr<DAGNode>, bool>> todo;
2394 todo.push(std::make_pair(expr, false));
2395
2396 while (!todo.empty()) {
2397 auto curPair = todo.top();
2398 todo.pop();
2399 std::shared_ptr<DAGNode> current = curPair.first;
2400 bool processed = curPair.second;
2401
2402 // If we already computed a substitute for this node elsewhere, skip.
2403 if (visited.find(current) != visited.end()) {
2404 continue;
2405 }
2406
2407 if (processed) {
2408 /*
2409 All children have been processed – build the new node using the
2410 (possibly substituted) child results that are now stored in
2411 `visited`.
2412 */
2413 std::vector<std::shared_ptr<DAGNode>> newChildren;
2414 newChildren.reserve(current->getChildrenSize());
2415 for (size_t i = 0; i < current->getChildrenSize(); ++i) {
2416 newChildren.emplace_back(visited[current->getChild(i)]);
2417 }
2418
2419 // std::shared_ptr<DAGNode> newNode = mkOper(current->getSort(),
2420 // current->getKind(), newChildren);
2421 std::shared_ptr<DAGNode> newNode;
2422 // For nodes with meaningful names (UF applications, function
2423 // applications, etc.), preserve the original name instead of using
2424 // kindToString
2425 if (current->isUFApplication() || current->isFuncApplication() ||
2426 current->isFuncRecApplication() || current->isArray()) {
2427 // Create node with original name preserved
2428 newNode =
2429 node_manager->createNode(current->getSort(), current->getKind(), current->getName(), newChildren);
2430 }
2431 else {
2432 // Use standard mkOper for other node types
2433 newNode = mkOper(current->getSort(), current->getKind(), newChildren);
2434 }
2435 visited[current] = newNode;
2436 }
2437 else {
2438 /* First visit */
2439 if (current->isVar()) {
2440 // Variable: replace if it appears in the substitution map
2441 auto it = params.find(current->getName());
2442 visited[current] = (it != params.end()) ? it->second : current;
2443 }
2444 else if (current->isConst() || current->isFuncParam()) {
2445 // Constants and function-parameters stay unchanged
2446 visited[current] = current;
2447 }
2448 else {
2449 // Non-leaf operator node – schedule a second visit after children
2450 todo.push(std::make_pair(current, true));
2451 // Push children (reverse order keeps original left-to-right after pop)
2452 for (int i = static_cast<int>(current->getChildrenSize()) - 1; i >= 0;
2453 --i) {
2454 auto child = current->getChild(i);
2455 if (visited.find(child) == visited.end()) {
2456 todo.push(std::make_pair(child, false));
2457 }
2458 }
2459 }
2460 }
2461 }
2462
2463 return visited[expr];
2464}
2465
2466// aux functions
2467NODE_KIND Parser::getAddOp(std::shared_ptr<Sort> sort) {
2468 if (sort->isInt() || sort->isReal() || sort->isIntOrReal()) {
2469 return NODE_KIND::NT_ADD;
2470 }
2471 else if (sort->isBv()) {
2472 return NODE_KIND::NT_BV_ADD;
2473 }
2474 else if (sort->isFp()) {
2475 return NODE_KIND::NT_FP_ADD;
2476 }
2477 else {
2478 return NODE_KIND::NT_ERROR;
2479 }
2480}
2484std::shared_ptr<DAGNode> Parser::getZero(std::shared_ptr<Sort> sort) {
2485 if (sort->isInt() || sort->isIntOrReal()) {
2486 return mkConstInt(0);
2487 }
2488 else if (sort->isReal()) {
2489 return mkConstReal(0.0);
2490 }
2491 else if (sort->isBv()) {
2492 return mkConstBv("0", sort->getBitWidth());
2493 }
2494 else if (sort->isFp()) {
2495 return mkConstFp("0.0", sort->getExponentWidth(), sort->getSignificandWidth());
2496 }
2497 else if (sort->isStr()) {
2498 return mkConstStr("");
2499 }
2500 else {
2502 }
2503}
2504
2505bool Parser::isDeclaredVariable(const std::string &var_name) const {
2506 return var_names.find(var_name) != var_names.end();
2507}
2508bool Parser::isDeclaredFunction(const std::string &func_name) const {
2509 return fun_key_map.find(func_name) != fun_key_map.end();
2510}
2511
2512// error operations
2513std::shared_ptr<DAGNode> Parser::mkErr(const ERROR_TYPE t) {
2514 return node_manager->createNode(NODE_KIND::NT_ERROR);
2515}
2516void Parser::err_all(const ERROR_TYPE e, const std::string s, const size_t ln) const {
2517 switch (e) {
2519 err_unexp_eof();
2520 break;
2522 err_sym_mis(s, ln);
2523 break;
2525 err_unkwn_sym(s, ln);
2526 break;
2528 err_param_mis(s, ln);
2529 break;
2531 err_param_nbool(s, ln);
2532 break;
2534 err_param_nnum(s, ln);
2535 break;
2537 err_param_nsame(s, ln);
2538 break;
2540 err_logic(s, ln);
2541 break;
2543 err_mul_decl(s, ln);
2544 break;
2546 err_mul_def(s, ln);
2547 break;
2549 err_zero_divisor(ln);
2550 break;
2552 err_param_nsame(s, ln);
2553 break;
2555 err_arity_mis(s, ln);
2556 break;
2558 err_type_mis(s, ln);
2559 break;
2561 err_neg_param(ln);
2562 break;
2563 default:
2564 throw std::runtime_error("Unknown parser error");
2565 }
2566}
2567
2568void Parser::err_all(const std::shared_ptr<DAGNode> e, const std::string s, const size_t ln) const {
2569 err_all((ERROR_TYPE)e->getKind(), s, ln);
2570}
2571
2572// unexpected end of file
2574 throw std::runtime_error("error: Unexpected end of file found.");
2575}
2576
2577// symbol missing
2578void Parser::err_sym_mis(const std::string mis, const size_t ln) const {
2579 throw std::runtime_error("error: \"" + mis + "\" missing in line " + std::to_string(ln) + ".");
2580}
2581
2582void Parser::err_sym_mis(const std::string mis, const std::string nm, const size_t ln) const {
2583 throw std::runtime_error("error: \"" + mis + "\" missing before \"" + nm + "\" in line " + std::to_string(ln) + ".");
2584}
2585
2586// unknown symbol
2587void Parser::err_unkwn_sym(const std::string nm, const size_t ln) const {
2588 if (nm == "")
2589 err_unexp_eof();
2590 throw std::runtime_error("error: Unknown or unexptected symbol \"" + nm + "\" in line " + std::to_string(ln) + ".");
2591}
2592
2593// wrong number of parameters
2594void Parser::err_param_mis(const std::string nm, const size_t ln) const {
2595 throw std::runtime_error("error: Wrong number of parameters of \"" + nm + "\" in line " + std::to_string(ln) + ".");
2596}
2597
2598// paramerter type error
2599void Parser::err_param_nbool(const std::string nm, const size_t ln) const {
2600 throw std::runtime_error("error: Invalid command \"" + nm + "\" in line " + std::to_string(ln) + ", paramerter is not a boolean.");
2601}
2602
2603void Parser::err_param_nnum(const std::string nm, const size_t ln) const {
2604 throw std::runtime_error("error: Invalid command \"" + nm + "\" in line " + std::to_string(ln) + ", paramerter is not an integer or a real.");
2605}
2606
2607// paramerters are not in same type
2608void Parser::err_param_nsame(const std::string nm, const size_t ln) const {
2609 throw std::runtime_error("error: Invalid command \"" + nm + "\" in line " + std::to_string(ln) + ", paramerters are not in same type.");
2610}
2611
2612// logic doesnt support
2613void Parser::err_logic(const std::string nm, const size_t ln) const {
2614 throw std::runtime_error("error: Logic does not support \"" + nm + "\" in line " + std::to_string(ln) + ".");
2615}
2616
2617// multiple declaration
2618void Parser::err_mul_decl(const std::string nm, const size_t ln) const {
2619 throw std::runtime_error("error: Multiple declarations of \"" + nm + "\" in line " + std::to_string(ln) + ".");
2620}
2621
2622// multiple definition
2623void Parser::err_mul_def(const std::string nm, const size_t ln) const {
2624 throw std::runtime_error("error: Multiple definitions or keybindings of \"" + nm + "\" in line " + std::to_string(ln) + ".");
2625}
2626
2627// divisor is zero
2628void Parser::err_zero_divisor(const size_t ln) const {
2629 throw std::runtime_error("error: Divisor is zero in line " + std::to_string(ln) + ".");
2630}
2631
2632// arity mismatch
2633void Parser::err_arity_mis(const std::string nm, const size_t ln) const {
2634 throw std::runtime_error("error: Arity mismatch of command \"" + nm + "\" in line " + std::to_string(ln) + ".");
2635}
2636
2637// kind mismatch
2638void Parser::err_type_mis(const std::string nm, const size_t ln) const {
2639 throw std::runtime_error("error: Kind mismatch of command \"" + nm + "\" in line " + std::to_string(ln) + ".");
2640}
2641
2642void Parser::err_neg_param(const size_t ln) const {
2643 throw std::runtime_error("error: Negative parameter in line " + std::to_string(ln) + ".");
2644}
2645
2646// keyword error
2647void Parser::err_keyword(const std::string nm, const size_t ln) const {
2648 throw std::runtime_error("error: keyword mismatch of command \"" + nm + "\" in line " + std::to_string(ln) + ".");
2649}
2650
2651/*
2652global errors
2653*/
2654// cannot open file
2655void Parser::err_open_file(const std::string filename) const {
2656 throw std::runtime_error("error: Cannot open file \"" + filename + "\".");
2657}
2658
2659std::shared_ptr<DAGNode> Parser::rename(std::shared_ptr<DAGNode> expr,
2660 const std::string &new_name) {
2661 condAssert(expr->isVar(), "Only variable can be renamed");
2662 std::string old_name = expr->getName();
2663 if (expr->isTempVar()) {
2664 size_t old_index = temp_var_names[old_name];
2665 temp_var_names[new_name] = old_index;
2666 }
2667 else {
2668 size_t old_index = var_names[old_name];
2669 var_names[new_name] = old_index;
2670 }
2671 expr->rename(new_name);
2672
2673 return expr;
2674}
2675
2676std::string Parser::toString(std::shared_ptr<DAGNode> expr) {
2677 return dumpSMTLIB2(expr);
2678}
2679
2680std::string Parser::toString(std::shared_ptr<Sort> sort) {
2681 return sort->toString();
2682}
2683
2684std::string Parser::toString(const NODE_KIND &kind) {
2685 return kindToString(kind);
2686}
2687
2688std::string Parser::optionToString() { return options->toString(); }
2689
2690std::shared_ptr<DAGNode>
2691Parser::rebuildLetBindings(const std::shared_ptr<DAGNode> &root) {
2693
2694 std::unordered_map<std::shared_ptr<DAGNode>, std::shared_ptr<DAGNode>>
2695 subst_map;
2696 std::vector<std::vector<std::shared_ptr<DAGNode>>> let_binds(1);
2697 std::unordered_map<std::shared_ptr<DAGNode>, size_t> refs;
2698
2699 auto is_quanted = [](const std::shared_ptr<DAGNode> &node) {
2700 return node->getKind() == NODE_KIND::NT_FORALL ||
2701 node->getKind() == NODE_KIND::NT_EXISTS;
2702 };
2703 auto is_binder = [](const std::shared_ptr<DAGNode> &node) {
2704 return node->isFuncDef() || node->isFuncRec() || node->isFuncDec() ||
2705 node->isLetBindVar() || node->isAttributeParam();
2706 };
2707 auto is_suitable_for_let = [&](const std::shared_ptr<DAGNode> &node) {
2708 return !is_binder(node) && !node->isVar() && !node->isVUF() &&
2709 !node->isFuncParam() && !node->isCInt() && !node->isAttribute() &&
2710 refs.at(node) > 1;
2711 };
2712
2713 std::vector<std::shared_ptr<DAGNode>> quanted;
2714 std::unordered_map<std::shared_ptr<DAGNode>, size_t> let_level;
2715 constexpr size_t MAX_LET_LEVEL = SIZE_MAX;
2716 constexpr size_t ROOT_LET_LEVEL = 0;
2717 // First top-down traversal
2718 std::vector<std::shared_ptr<DAGNode>> visit(1, root);
2719 std::vector<std::shared_ptr<DAGNode>> nodes;
2720 while (!visit.empty()) {
2721 auto cur = visit.back();
2722 auto [itr, success] = subst_map.emplace(cur, nullptr);
2723 if (success) {
2724 if (!is_binder(cur)) {
2725 for (const auto &child : cur->getChildren()) {
2726 visit.emplace_back(child);
2727 }
2728 }
2729 continue;
2730 }
2731 else if (itr->second == nullptr) {
2732 itr->second = std::make_shared<DAGNode>(*cur);
2733 refs[itr->second] = 0;
2734 let_level[itr->second] = ROOT_LET_LEVEL;
2735 if (!is_binder(itr->second)) {
2736 auto children = itr->second->getChildren();
2737 for (auto &child : children) {
2738 child = subst_map.at(child);
2739 ++refs.at(child);
2740 }
2741 itr->second->replace_children(children);
2742
2743 if (is_quanted(itr->second)) {
2744 quanted.emplace_back(itr->second);
2745 let_level.at(itr->second) = MAX_LET_LEVEL;
2746 }
2747 }
2748 nodes.emplace_back(itr->second);
2749 }
2750 visit.pop_back();
2751 }
2752 subst_map.clear();
2753 size_t lets = 0;
2754 std::reverse(quanted.begin(), quanted.end());
2755 let_binds.resize(quanted.size() + 1);
2756 for (size_t i = 0, isz = quanted.size(); i < isz; ++i) {
2757 const auto &quant = quanted.at(i);
2758 for (size_t j = 1, jsz = quant->getChildrenSize(); j < jsz; ++j) {
2759 auto child = quant->getChild(j);
2760 let_level.at(child) = i + 1;
2761 }
2762 }
2763 // std::cout << let_binds.size() << ' ' << quanted.size() << std::endl;
2764 for (const auto &node : nodes) {
2765 auto children = node->getChildren();
2766 if (!is_binder(node))
2767 for (auto &child : children) {
2768 let_level.at(node) = std::max(let_level.at(node), let_level.at(child));
2769 child = subst_map.at(child);
2770 }
2771 node->replace_children(children);
2772 // std::cout << let_level.at(node) << std::endl;
2773
2774 if (is_suitable_for_let(node) && let_level.at(node) < MAX_LET_LEVEL) {
2775 // let_binds[let_level.at(node)].emplace_back(node);
2776 auto let_var = mkLetBindVar("L" + std::to_string(lets++), node);
2777 let_binds.at(let_level.at(node)).emplace_back(let_var);
2778 subst_map[node] = let_var;
2779 }
2780 else {
2781 subst_map[node] = node;
2782 }
2783 }
2784
2785 for (size_t i = 0, isz = quanted.size(); i < isz; ++i) {
2786 if (let_binds.at(i + 1).empty())
2787 continue;
2788 auto children = quanted.at(i)->getChildren();
2789 std::vector<std::shared_ptr<DAGNode>> var_list;
2790 for (const auto &bind_var : let_binds.at(i + 1)) {
2791 var_list.emplace_back(mkLetBindVarList({bind_var}));
2792 }
2793 children.front() = mkLetChain(var_list, children.front());
2794 if (quanted.at(i)->getChild(0)->isAttribute()) {
2795 children = quanted.at(i)->getChild(0)->getChildren();
2796 children.front() = mkLetChain(var_list, children.front());
2797 quanted.at(i)->getChild(0)->replace_children(children);
2798 }
2799 else
2800 quanted.at(i)->replace_children(children);
2801 }
2802
2803 subst_map.clear();
2804 visit.emplace_back(nodes.back());
2805 while (!visit.empty()) {
2806 auto cur = visit.back();
2807 auto [itr, success] = subst_map.emplace(cur, nullptr);
2808 if (success) {
2809 if (!is_binder(cur) && !is_quanted(cur)) {
2810 for (const auto &child : cur->getChildren()) {
2811 visit.emplace_back(child);
2812 }
2813 }
2814 continue;
2815 }
2816 else if (itr->second == nullptr) {
2817 // std::cout << cur->getName() << ' ' << (refs.find(cur) != refs.end()) <<
2818 // std::endl;
2819
2820 itr->second = cur;
2821 auto children = itr->second->getChildren();
2822 if (!is_binder(cur) && !is_quanted(cur))
2823 for (auto &child : children) {
2824 child = subst_map.at(child);
2825 }
2826
2827 itr->second->replace_children(children);
2828 // std::cout << cur->getName() << ' ' << (refs.find(cur) != refs.end()) <<
2829 // std::endl;
2830 if (refs.find(cur) != refs.end() && is_suitable_for_let(cur)) {
2831 if (lets == 2)
2832 std::cout << "!!!" << std::endl;
2833 auto bind_var = mkLetBindVar("L" + std::to_string(lets++), cur);
2834 let_binds.at(0).emplace_back(bind_var);
2835 itr->second = bind_var;
2836 }
2837 }
2838 visit.pop_back();
2839 }
2840
2841 if (!let_binds.at(0).empty()) {
2842 std::vector<std::shared_ptr<DAGNode>> var_list;
2843 for (const auto &bind_var : let_binds.at(0)) {
2844 var_list.emplace_back(mkLetBindVarList({bind_var}));
2845 }
2846 return mkLetChain(var_list, subst_map.at(nodes.back()));
2847 }
2848 return subst_map.at(nodes.back());
2849}
2850
2851std::string Parser::dumpSMT2() {
2852 std::stringstream ss;
2853
2854 // [set-option]
2855 for (const auto &s : parse_options)
2856 ss << s << std::endl;
2857 ss << "(set-logic " << options->getLogic() << ")" << std::endl;
2858
2859 size_t sort_count = 0;
2860 std::vector<std::pair<std::string, std::shared_ptr<Sort>>> sorts;
2861 for (const auto &[sort_name, sort_ptr] : sort_key_map) {
2862 sorts.emplace_back(sort_name, sort_ptr);
2863 }
2864 std::sort(sorts.begin(), sorts.end(), [](const auto &a, const auto &b) { return a.first < b.first; });
2865 for (const auto &[sort_name, sort_ptr] : sorts) {
2866 if (sort_ptr->isDec()) {
2867 // Skip sorts that are declared via datatypes
2868 // if (datatype_sort_names.find(sort_name) != datatype_sort_names.end()) {
2869 // continue;
2870 // }
2871 if (sort_name.starts_with("DT"))
2872 continue;
2873 // std::string name = "SORT" + std::to_string(sort_count++);
2874 // sort_ptr->setName(name);
2875 ss << "(declare-sort " << sort_name << " " << sort_ptr->arity << ")"
2876 << std::endl;
2877 }
2878 }
2879
2880 // Emit datatype declarations first if any
2881 if (!datatype_blocks.empty()) {
2882 for (const auto &block : datatype_blocks) {
2883 // Headers
2884 ss << "(declare-datatypes (";
2885 for (size_t i = 0; i < block.size(); ++i) {
2886 const auto &td = block[i];
2887 ss << "(" << td.name << " " << td.arity << ")";
2888 if (i + 1 < block.size())
2889 ss << " ";
2890 }
2891 ss << ") (";
2892 // Bodies
2893 for (size_t i = 0; i < block.size(); ++i) {
2894 const auto &td = block[i];
2895 ss << "(";
2896 for (size_t j = 0; j < td.ctors.size(); ++j) {
2897 const auto &cd = td.ctors[j];
2898 ss << "(" << cd.name;
2899 for (const auto &sel : cd.selectors) {
2900 ss << " (" << sel.name << " " << sel.sort->toString() << ")";
2901 }
2902 ss << ")";
2903 if (j + 1 < td.ctors.size())
2904 ss << " ";
2905 }
2906 ss << ")";
2907 if (i + 1 < block.size())
2908 ss << " ";
2909 }
2910 ss << "))" << std::endl;
2911 }
2912 }
2913 // variables
2914 //[TODO] no need to sort var
2915 std::vector<std::shared_ptr<DAGNode>> vars = getVariables();
2916 std::sort(
2917 vars.begin(), vars.end(), [](const std::shared_ptr<DAGNode> &a, const std::shared_ptr<DAGNode> &b) {
2918 return a->getName() < b->getName();
2919 });
2920 for (auto &var : vars) {
2921 ss << "(declare-fun " << var->getName() << " () "
2922 << var->getSort()->toString() << ")" << std::endl;
2923 }
2924
2925 // // Build a skip set for functions generated by datatype declarations
2926 // (ctors/selectors) std::unordered_set<std::string> dt_fun_names; for (const
2927 // auto& block : datatype_blocks) {
2928 // for (const auto& td : block) {
2929 // for (const auto& cd : td.ctors) {
2930 // dt_fun_names.insert(cd.name);
2931 // for (const auto& sel : cd.selectors)
2932 // dt_fun_names.insert(sel.name);
2933 // }
2934 // }
2935 // }
2936
2937 std::vector<std::shared_ptr<DAGNode>> functions = getFunctions();
2938 for (auto &func : functions) {
2939 if (func->isFuncDec()) {
2940 // Skip constructor/selector declarations covered by declare-datatypes
2941 // if (dt_fun_names.find(func->getName()) != dt_fun_names.end()) {
2942 // continue;
2943 // }
2944 // NT_FUNC_DEC: Uninterpreted function declaration (declare-fun)
2945 ss << dumpFuncDec(rebuildLetBindings(func)) << std::endl;
2946 }
2947 else if (func->isFuncRec()) {
2948 // NT_FUNC_REC: Recursive function definition (define-fun-rec)
2949 ss << dumpFuncRec(rebuildLetBindings(func)) << std::endl;
2950 }
2951 else {
2952 // std::cout << func->getName() << std::endl;
2953 // NT_FUNC_DEF: Regular function definition (define-fun)
2954 ss << "(define-fun " << func->getName() << " (";
2955 // For NT_FUNC_DEF, children[0] is body, children[1..n] are parameters
2956 for (size_t i = 1; i < func->getChildrenSize(); i++) {
2957 if (i == 1)
2958 ss << "(" << func->getChild(i)->getName() << " "
2959 << func->getChild(i)->getSort()->toString() << ")";
2960 else
2961 ss << " (" << func->getChild(i)->getName() << " "
2962 << func->getChild(i)->getSort()->toString() << ")";
2963 }
2964 // Use node's declared sort, not body's inferred sort (which may be
2965 // widened to IntOrReal)
2966 ss << ") " << func->getSort()->toString() << " ";
2967 ss << dumpSMTLIB2(rebuildLetBindings(func->getChild(0))) << ")"
2968 << std::endl;
2969 }
2970 }
2971 // constraints
2972 for (auto &constraint : assertions) {
2973 auto formula = rebuildLetBindings(constraint);
2974 ss << "(assert " << dumpSMTLIB2(formula) << ")" << std::endl;
2975 }
2976 ss << "(check-sat)" << std::endl;
2977 ss << "(exit)" << std::endl;
2978 return ss.str();
2979}
2980
2981std::string Parser::dumpSMT2(const std::string &filename) {
2982 std::ofstream file(filename);
2983 file << dumpSMT2();
2984 file.close();
2985 return filename;
2986}
2987
2988size_t Parser::removeFuns(const std::vector<std::string> &funcNames) {
2989 size_t removedCount = 0;
2990
2991 for (const auto &funcName : funcNames) {
2992 // Check if function exists in fun_key_map
2993 auto funIt = fun_key_map.find(funcName);
2994 if (funIt != fun_key_map.end()) {
2995 // Remove from fun_key_map
2996 fun_key_map.erase(funIt);
2997 removedCount++;
2998
2999 // Remove from function_names vector
3000 auto nameIt =
3001 std::find(function_names.begin(), function_names.end(), funcName);
3002 if (nameIt != function_names.end()) {
3003 function_names.erase(nameIt);
3004 }
3005 }
3006 }
3007
3008 return removedCount;
3009}
3010
3011/*
3012warnings
3013*/
3014// command not support
3015void Parser::warn_cmd_nsup(const std::string nm, const size_t ln) const {
3016 // std::cout << "warning: \"" << nm << "\" command is safely ignored in line "
3017 // << ln << "." << std::endl;
3018}
3019
3020ParserPtr newParser() { return std::make_shared<Parser>(); }
3021
3022ParserPtr newParser(const std::string &filename) {
3023 return std::make_shared<Parser>(filename);
3024}
3025
3026} // namespace stabilizer::parser
#define condAssert(cond, msg)
Definition asserting.h:35
std::string toString(int base=10) const
Definition number.cpp:1026
static HighPrecisionReal pi(mpfr_prec_t precision=128)
Definition number.cpp:42
static HighPrecisionReal e(mpfr_prec_t precision=128)
Definition number.cpp:48
static const std::shared_ptr< DAGNode > NULL_NODE
Definition dag.h:1267
void err_all(const ERROR_TYPE e, const std::string s="", const size_t ln=0) const
std::shared_ptr< GlobalOptions > options
Definition parser.h:242
void err_neg_param(const size_t ln) const
void err_param_mis(const std::string nm, const size_t ln) const
std::vector< std::vector< DTTypeDecl > > datatype_blocks
Definition parser.h:222
std::vector< std::shared_ptr< DAGNode > > assertions
Definition parser.h:265
std::unordered_map< std::string, std::vector< std::shared_ptr< DAGNode > > > quant_var_map
Definition parser.h:214
void err_open_file(const std::string) const
void err_param_nnum(const std::string nm, const size_t ln) const
std::unordered_map< std::string, std::shared_ptr< DAGNode > > fun_var_map
Definition parser.h:211
std::shared_ptr< Sort > mkSortDef(const std::string &name, const std::vector< std::shared_ptr< Sort > > &params, std::shared_ptr< Sort > out_sort)
Create a sort definition.
bool parseSmtlib2File(const std::string filename)
Parse an SMT-LIB2 file.
std::unordered_map< std::string, size_t > temp_var_names
Definition parser.h:234
std::vector< std::shared_ptr< DAGNode > > static_functions
Definition parser.h:218
bool isOne(std::shared_ptr< DAGNode > expr)
Check if an expression is one.
std::shared_ptr< DAGNode > applyFun(std::shared_ptr< DAGNode > fun, const std::vector< std::shared_ptr< DAGNode > > &params)
Apply a function to a list of parameters.
void setOption(const std::string &key, const std::string &value)
Set global options.
std::shared_ptr< DAGNode > mkQuantVar(const std::string &name, std::shared_ptr< Sort > sort)
Create a quantifier variable node.
std::unordered_set< std::string > datatype_function_names
Definition parser.h:227
std::shared_ptr< DAGNode > substitute(std::shared_ptr< DAGNode > expr, std::unordered_map< std::string, std::shared_ptr< DAGNode > > &params)
Substitute variables in an expression.
std::vector< std::string > function_names
Definition parser.h:240
void err_zero_divisor(const size_t ln) const
std::vector< std::vector< std::shared_ptr< DAGNode > > > assumptions
Definition parser.h:273
std::vector< std::shared_ptr< DAGNode > > getAssertions() const
Get all assertions.
std::shared_ptr< DAGNode > mkExists(const std::vector< std::shared_ptr< DAGNode > > &params)
Create an exists node.
void setEvaluatePrecision(mpfr_prec_t precision)
Set the precision for evaluation.
std::unordered_map< std::string, std::unordered_set< size_t > > getGroupedSoftAssertions() const
Get grouped soft assertions.
void err_param_nsame(const std::string nm, const size_t ln) const
std::shared_ptr< DAGNode > mkForall(const std::vector< std::shared_ptr< DAGNode > > &params)
Create a forall node.
std::string optionToString()
Print the options.
std::shared_ptr< Sort > placeholder_var_sort
Definition parser.h:195
bool parse(const std::string &filename)
Parse SMT-LIB2 file.
Integer toInt(std::shared_ptr< DAGNode > expr)
Convert an expression to an integer.
std::unordered_map< std::string, std::shared_ptr< DAGNode > > preserving_let_key_map
Definition parser.h:208
std::shared_ptr< DAGNode > parseLet()
void err_keyword(const std::string nm, const size_t ln) const
std::shared_ptr< DAGNode > parsePreservingLet()
std::shared_ptr< DAGNode > getZero(std::shared_ptr< Sort > sort)
Get the zero for a sort.
std::string toString(std::shared_ptr< DAGNode > expr)
Print an expression.
std::vector< std::vector< std::shared_ptr< DAGNode > > > getAssumptions() const
Get assumptions.
std::vector< std::shared_ptr< DAGNode > > getFunctions() const
Get functions.
bool getEvaluateUseFloating() const
Get the use floating for evaluation.
std::shared_ptr< GlobalOptions > getOptions() const
Get global options.
std::shared_ptr< DAGNode > mkWeight(const std::shared_ptr< Sort > &sort, const std::string &name, std::shared_ptr< DAGNode > weight)
std::unordered_map< std::string, size_t > var_names
Definition parser.h:231
std::shared_ptr< DAGNode > mkConstBv(const std::string &v, const size_t &width)
Create a bit-vector constant.
std::unordered_map< std::string, std::shared_ptr< Sort > > sort_key_map
Definition parser.h:212
std::shared_ptr< DAGNode > mkApplyDTFun(const std::shared_ptr< Sort > &sort, const std::string &name, const std::vector< std::shared_ptr< DAGNode > > &params)
NODE_KIND getNegatedKind(NODE_KIND kind)
Get the opposite kind of a node kind.
std::vector< std::shared_ptr< DAGNode > > getSoftWeights() const
Get soft assertion weights.
std::shared_ptr< DAGNode > mkConstReal(const std::string &v)
Create a real constant from string.
std::shared_ptr< Sort > mkSortDec(const std::string &name, const size_t &arity)
Create a sort declaration.
bool parseStr(const std::string &constraint)
Parse a constraint.
RESULT_TYPE getResultType()
Get result type.
std::shared_ptr< DAGNode > mkFuncDec(const std::string &name, const std::vector< std::shared_ptr< Sort > > &params, std::shared_ptr< Sort > out_sort)
Create a function declaration.
void ensureNumberValue(std::shared_ptr< DAGNode > expr)
ensure the expression is a number
std::shared_ptr< DAGNode > mkApplyRecFunc(std::shared_ptr< DAGNode > fun, const std::vector< std::shared_ptr< DAGNode > > &params)
Create a recursive function application.
bool assert(const std::string &constraint)
Assert a constraint.
std::shared_ptr< DAGNode > mkErr(const ERROR_TYPE t)
void err_mul_def(const std::string nm, const size_t ln) const
RESULT_TYPE result_type
Definition parser.h:259
std::shared_ptr< DAGNode > mkLetBindVar(const std::string &name, const std::shared_ptr< DAGNode > &expr)
std::shared_ptr< DAGNode > mkApplyFunc(std::shared_ptr< DAGNode > fun, const std::vector< std::shared_ptr< DAGNode > > &params)
Create a function application node.
std::shared_ptr< DAGNode > applyFunPostOrder(std::shared_ptr< DAGNode > node, std::unordered_map< std::string, std::shared_ptr< DAGNode > > &params)
std::shared_ptr< DAGNode > mkLetBindVarList(const std::vector< std::shared_ptr< DAGNode > > &bind_vars)
std::vector< std::shared_ptr< DAGNode > > parseParams()
std::shared_ptr< DAGNode > mkConstInt(const std::string &v)
Create an integer constant from string.
void err_mul_decl(const std::string nm, const size_t ln) const
std::shared_ptr< DAGNode > mkOper(const std::shared_ptr< Sort > &sort, const NODE_KIND &t, std::shared_ptr< DAGNode > p)
Create an operation.
bool isZero(std::shared_ptr< DAGNode > expr)
Check if an expression is zero.
std::shared_ptr< DAGNode > mkPattern(const std::shared_ptr< Sort > &sort, const std::string &name, const std::vector< std::shared_ptr< DAGNode > > &params)
std::shared_ptr< DAGNode > mkLetChain(const std::vector< std::shared_ptr< DAGNode > > &bind_var_lists, const std::shared_ptr< DAGNode > &body)
size_t removeFuns(const std::vector< std::string > &funcNames)
Remove functions by name.
void err_logic(const std::string nm, const size_t ln) const
std::vector< std::string > parse_options
Definition parser.h:198
std::shared_ptr< DAGNode > mkFuncDef(const std::string &name, const std::vector< std::shared_ptr< DAGNode > > &params, std::shared_ptr< Sort > out_sort, std::shared_ptr< DAGNode > body)
Create a function definition.
unsigned long buflen
Definition parser.h:185
Parser()
Default constructor.
std::vector< std::shared_ptr< DAGNode > > soft_weights
Definition parser.h:275
std::shared_ptr< DAGNode > getVariable(const std::string &var_name)
Get variable.
std::vector< std::shared_ptr< DAGNode > > getVariables() const
Get variables.
std::shared_ptr< DAGNode > parseQuant(const std::string &type)
void err_unkwn_sym(const std::string nm, const size_t ln) const
void setEvaluateUseFloating(bool use_floating)
Set the use floating for evaluation.
Real toReal(std::shared_ptr< DAGNode > expr)
Convert an expression to a real.
void warn_cmd_nsup(const std::string nm, const size_t ln) const
std::unordered_map< std::string, std::shared_ptr< DAGNode > > named_assertions
Definition parser.h:268
std::unordered_map< std::string, std::shared_ptr< DAGNode > > let_key_map
Definition parser.h:206
std::unordered_map< std::string, std::unordered_set< size_t > > soft_assertion_groups
Definition parser.h:277
size_t getNodeCount()
Get node count.
std::string dumpSMT2()
Dump the SMT2 representation of the parsed expressions.
std::shared_ptr< DAGNode > mkNoPattern(const std::shared_ptr< Sort > &sort, const std::string &name, std::shared_ptr< DAGNode > param)
std::shared_ptr< DAGNode > mkApplyUF(const std::shared_ptr< Sort > &sort, const std::string &name, const std::vector< std::shared_ptr< DAGNode > > &params)
Apply an uninterpreted function.
std::shared_ptr< Sort > parseSort()
void err_type_mis(const std::string nm, const size_t ln) const
void err_arity_mis(const std::string nm, const size_t ln) const
mpfr_prec_t getEvaluatePrecision() const
Get the precision for evaluation.
void err_sym_mis(const std::string mis, const size_t ln) const
std::shared_ptr< DAGNode > rebuildLetBindings(const std::shared_ptr< DAGNode > &root)
bool isDeclaredVariable(const std::string &var_name) const
Check if a variable is declared.
std::shared_ptr< DAGNode > getResult()
Get result.
std::shared_ptr< DAGNode > mkFunParamVar(std::shared_ptr< Sort > sort, const std::string &name)
Create a function parameter variable.
void err_param_nbool(const std::string nm, const size_t ln) const
std::vector< std::shared_ptr< DAGNode > > getSoftAssertions() const
Get soft assertions.
std::shared_ptr< DAGNode > mkVar(const std::shared_ptr< Sort > &sort, const std::string &name)
Create a variable.
std::shared_ptr< DAGNode > mkExpr(const std::string &expression)
Create a DAG node from a string expression.
std::shared_ptr< DAGNode > parseExpr()
std::shared_ptr< DAGNode > mkAttribute(const std::shared_ptr< Sort > &sort, const std::string &name, const std::vector< std::shared_ptr< DAGNode > > &params)
std::unordered_set< std::string > datatype_sort_names
Definition parser.h:224
std::vector< std::shared_ptr< DAGNode > > soft_assertions
Definition parser.h:274
std::unique_ptr< NodeManager > node_manager
Definition parser.h:201
std::vector< std::shared_ptr< DAGNode > > getDeclaredVariables() const
Get declared variables.
std::unordered_map< std::string, std::unordered_set< size_t > > getGroupedAssertions() const
Get grouped assertions.
std::shared_ptr< DAGNode > mkConstStr(const std::string &v)
Create a string constant.
std::unique_ptr< SortManager > sort_manager
Definition parser.h:203
std::shared_ptr< DAGNode > mkConstFp(const std::string &v, const size_t &e, const size_t &s)
Create a floating-point constant.
RESULT_TYPE checkSat()
Check satisfiability.
std::shared_ptr< DAGNode > rename(std::shared_ptr< DAGNode > expr, const std::string &new_name)
Rename a node.
NODE_KIND getAddOp(std::shared_ptr< Sort > sort)
Get the add operator for a sort.
std::shared_ptr< DAGNode > result_node
Definition parser.h:260
std::unordered_map< std::string, std::shared_ptr< DAGNode > > fun_key_map
Definition parser.h:209
bool isOnes(std::shared_ptr< DAGNode > expr)
std::unordered_map< std::string, std::unordered_set< size_t > > assertion_groups
Definition parser.h:266
bool isDeclaredFunction(const std::string &func_name) const
Check if a function is declared.
std::shared_ptr< DAGNode > mkFuncRec(const std::string &name, const std::vector< std::shared_ptr< DAGNode > > &params, std::shared_ptr< Sort > out_sort, std::shared_ptr< DAGNode > body)
Create a recursive function definition.
static const std::shared_ptr< Sort > ROUNDING_MODE_SORT
Definition sort.h:419
static const std::shared_ptr< Sort > STR_SORT
Definition sort.h:398
static const std::shared_ptr< Sort > INT_SORT
Definition sort.h:390
static const std::shared_ptr< Sort > FLOAT32_SORT
Definition sort.h:413
static const std::shared_ptr< Sort > FLOAT16_SORT
Definition sort.h:416
static const std::shared_ptr< Sort > FLOAT64_SORT
Definition sort.h:410
static const std::shared_ptr< Sort > REG_SORT
Definition sort.h:400
static const std::shared_ptr< Sort > BOOL_SORT
Definition sort.h:388
static const std::shared_ptr< Sort > REAL_SORT
Definition sort.h:392
static const std::shared_ptr< Sort > NULL_SORT
Definition sort.h:384
static bool isReal(const std::string &str)
Definition util.cpp:58
static bool isScientificNotation(const std::string &str)
Definition util.cpp:75
static bool isFP(const std::string &str)
Definition util.cpp:248
static bool isString(const std::string &str)
Definition util.cpp:259
static bool isInt(const std::string &str)
Definition util.cpp:47
static bool isBV(const std::string &str)
Definition util.cpp:219
static bool isNumber(const std::string &str)
Definition util.cpp:43
std::string kindToString(const NODE_KIND &nk)
Definition kind.cpp:35
const std::string PRESERVING_LET_BIND_VAR_SUFFIX
Definition kind.h:370
std::string dumpSMTLIB2(const std::shared_ptr< DAGNode > &root)
Definition dag.cpp:1003
std::string dumpFuncDec(const std::shared_ptr< DAGNode > &node)
Definition dag.cpp:1043
std::string dumpFuncRec(const std::shared_ptr< DAGNode > &node)
Definition dag.cpp:1026
char * safe_strdup(const std::string &str)
NODE_KIND getNegatedKind(const NODE_KIND &nk)
Definition kind.cpp:577
std::shared_ptr< Parser > ParserPtr
Definition parser.h:3815
std::vector< std::shared_ptr< DAGNode > > params
std::shared_ptr< DAGNode > bind_var_list
std::vector< std::string > key_list
std::shared_ptr< DAGNode > result
std::vector< DTSelectorDecl > selectors
Definition parser.h:172
std::vector< DTConstructorDecl > ctors
Definition parser.h:177