Skip to content

Commit 5918c27

Browse files
committed
Add template for node construction with validation propagation
For each non-leaf node in the `smt_responset` tree, the same logic is required where the child nodes are validated and we construct the non-leaf node if there are no errors, or collect and propagate the errors upwards if there are errors validating the child nodes. The template in this commit abstracts this logic out, so that it doesn't need to be duplicated for each type of node we construct.
1 parent 01e0f08 commit 5918c27

File tree

3 files changed

+134
-0
lines changed

3 files changed

+134
-0
lines changed

src/solvers/smt2_incremental/smt_response_validation.cpp

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ response_or_errort<smtt>::response_or_errort(std::string message)
1313
{
1414
}
1515

16+
template <class smtt>
17+
response_or_errort<smtt>::response_or_errort(std::vector<std::string> messages)
18+
: messages{std::move(messages)}
19+
{
20+
}
21+
1622
template <class smtt>
1723
const smtt *response_or_errort<smtt>::get_if_valid() const
1824
{
@@ -35,6 +41,114 @@ const std::vector<std::string> *response_or_errort<smtt>::get_if_error() const
3541

3642
template class response_or_errort<smt_responset>;
3743

44+
// Implementation detail of `collect_messages` below.
45+
template <typename argumentt, typename... argumentst>
46+
void collect_messages_impl(
47+
std::vector<std::string> &collected_messages,
48+
argumentt &&argument)
49+
{
50+
if(const auto messages = argument.get_if_error())
51+
{
52+
collected_messages.insert(
53+
collected_messages.end(), messages->cbegin(), messages->end());
54+
}
55+
}
56+
57+
// Implementation detail of `collect_messages` below.
58+
template <typename argumentt, typename... argumentst>
59+
void collect_messages_impl(
60+
std::vector<std::string> &collected_messages,
61+
argumentt &&argument,
62+
argumentst &&... arguments)
63+
{
64+
collect_messages_impl(collected_messages, argument);
65+
collect_messages_impl(collected_messages, arguments...);
66+
}
67+
68+
/// Builds a collection of messages composed all messages in the
69+
/// `response_or_errort` typed arguments in \p arguments. This is a templated
70+
/// function in order to handle `response_or_errort` instances which are
71+
/// specialised differently.
72+
template <typename... argumentst>
73+
std::vector<std::string> collect_messages(argumentst &&... arguments)
74+
{
75+
std::vector<std::string> collected_messages;
76+
collect_messages_impl(collected_messages, arguments...);
77+
return collected_messages;
78+
}
79+
80+
/// \brief Given a class to construct and a set of arguments to its constructor
81+
/// which may include errors, either return the collected errors if there are
82+
/// any or construct the class otherwise.
83+
/// \tparam smt_to_constructt
84+
/// The class to construct.
85+
/// \tparam smt_baset
86+
/// If the class to construct should be upcast to a base class before being
87+
/// stored in the `response_or_errort`, then the base class should be supplied
88+
/// in this parameter. If no upcast is required, then this should be left
89+
/// empty.
90+
/// \tparam argumentst
91+
/// The pack of argument types matching the constructor of
92+
/// `smt_to_constructt`. These must each be packed into an instance of
93+
/// `response_or_errort`.
94+
template <
95+
typename smt_to_constructt,
96+
typename smt_baset = smt_to_constructt,
97+
typename... argumentst>
98+
response_or_errort<smt_baset> validation_propagating(argumentst &&... arguments)
99+
{
100+
const auto collected_messages = collect_messages(arguments...);
101+
if(!collected_messages.empty())
102+
return response_or_errort<smt_baset>(collected_messages);
103+
else
104+
{
105+
return response_or_errort<smt_baset>{
106+
smt_to_constructt{(*arguments.get_if_valid())...}};
107+
}
108+
}
109+
110+
static response_or_errort<irep_idt>
111+
validate_string_literal(const irept &parse_tree)
112+
{
113+
if(!parse_tree.get_sub().empty())
114+
{
115+
return response_or_errort<irep_idt>(
116+
"Expected string literal, found \"" + parse_tree.pretty(0, 0) + "\".");
117+
}
118+
return response_or_errort<irep_idt>{parse_tree.id()};
119+
}
120+
121+
/// \returns: A response or error in the case where the parse tree appears to be
122+
/// a get-value command. Returns empty otherwise.
123+
/// \note: Because this kind of response does not start with an identifying
124+
/// keyword, it will be considered that the response is intended to be a
125+
/// get-value response if it is composed of a collection of one or more pairs.
126+
static optionalt<response_or_errort<smt_responset>>
127+
valid_smt_error_response(const irept &parse_tree)
128+
{
129+
// Check if the parse tree looks to be an error response.
130+
if(!parse_tree.id().empty())
131+
return {};
132+
if(parse_tree.get_sub().empty())
133+
return {};
134+
if(parse_tree.get_sub().at(0).id() != "error")
135+
return {};
136+
// Parse tree is now considered to be an error response and anything
137+
// unexpected in the parse tree is now considered to be an invalid response.
138+
if(parse_tree.get_sub().size() == 1)
139+
{
140+
return {response_or_errort<smt_responset>{
141+
"Error response is missing the error message."}};
142+
}
143+
if(parse_tree.get_sub().size() > 2)
144+
{
145+
return {response_or_errort<smt_responset>{
146+
"Error response has multiple error messages."}};
147+
}
148+
return validation_propagating<smt_error_responset, smt_responset>(
149+
validate_string_literal(parse_tree.get_sub()[1]));
150+
}
151+
38152
response_or_errort<smt_responset> validate_smt_response(const irept &parse_tree)
39153
{
40154
if(parse_tree.id() == "sat")
@@ -46,6 +160,8 @@ response_or_errort<smt_responset> validate_smt_response(const irept &parse_tree)
46160
if(parse_tree.id() == "unknown")
47161
return response_or_errort<smt_responset>{
48162
smt_check_sat_responset{smt_unknown_responset{}}};
163+
if(const auto error_response = valid_smt_error_response(parse_tree))
164+
return *error_response;
49165
if(parse_tree.id() == "success")
50166
return response_or_errort<smt_responset>{smt_success_responset{}};
51167
if(parse_tree.id() == "unsupported")

src/solvers/smt2_incremental/smt_response_validation.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class response_or_errort final
1919
public:
2020
explicit response_or_errort(smtt smt);
2121
explicit response_or_errort(std::string message);
22+
explicit response_or_errort(std::vector<std::string> messages);
2223

2324
/// \brief Gets the smt response if the response is valid, or returns nullptr
2425
/// otherwise.

unit/solvers/smt2_incremental/smt_response_validation.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,20 @@ TEST_CASE(
7474
std::vector<std::string>{"Invalid SMT response \"\""});
7575
}
7676
}
77+
78+
TEST_CASE("Validation of SMT error response", "[core][smt2_incremental]")
79+
{
80+
CHECK(
81+
*validate_smt_response(
82+
*smt2irep("(error \"Test error message.\")").parsed_output)
83+
.get_if_valid() == smt_error_responset{"Test error message."});
84+
CHECK(
85+
*validate_smt_response(*smt2irep("(error)").parsed_output).get_if_error() ==
86+
std::vector<std::string>{"Error response is missing the error message."});
87+
CHECK(
88+
*validate_smt_response(
89+
*smt2irep("(error \"Test error message1.\" \"Test error message2.\")")
90+
.parsed_output)
91+
.get_if_error() ==
92+
std::vector<std::string>{"Error response has multiple error messages."});
93+
}

0 commit comments

Comments
 (0)