Skip to content

Commit 55aec9d

Browse files
Extend index-of for c
into a new c-specific axiom-generating function. Basically, the addition encodes the existence of terminating-zero and the properties this entails.
1 parent cd6b7c9 commit 55aec9d

File tree

4 files changed

+124
-0
lines changed

4 files changed

+124
-0
lines changed

src/solvers/strings/string_constraint_generator.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,12 +193,18 @@ class string_constraint_generatort final
193193
const array_string_exprt &str,
194194
const exprt &c,
195195
const exprt &from_index);
196+
std::pair<exprt, string_constraintst> add_axioms_for_c_index_of(
197+
const array_string_exprt &str,
198+
const exprt &c,
199+
const exprt &from_index);
196200
std::pair<exprt, string_constraintst> add_axioms_for_index_of_string(
197201
const array_string_exprt &haystack,
198202
const array_string_exprt &needle,
199203
const exprt &from_index);
200204
std::pair<exprt, string_constraintst>
201205
add_axioms_for_index_of(const function_application_exprt &f);
206+
std::pair<exprt, string_constraintst>
207+
add_axioms_for_c_index_of(const function_application_exprt &f);
202208
std::pair<exprt, string_constraintst> add_axioms_for_last_index_of_string(
203209
const array_string_exprt &haystack,
204210
const array_string_exprt &needle,

src/solvers/strings/string_constraint_generator_indexof.cpp

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,90 @@ string_constraint_generatort::add_axioms_for_index_of(
8282
return {index, std::move(constraints)};
8383
}
8484

85+
/// Add axioms stating that the returned value is the index within `haystack`
86+
/// (`str`) of the first occurrence of `needle` (`c`) starting the search at
87+
/// `from_index`, or is `-1` if no such character occurs at or after position
88+
/// `from_index`.
89+
/// \todo Make argument names match whose of add_axioms_for_index_of_string
90+
///
91+
/// These axioms are:
92+
/// 1a. \f$-1 \le {\tt index} < {\tt terminating_zero} \f$
93+
/// 1b. \f${\tt terminating_zero} < |{\tt haystack}| \f$
94+
/// 2a. \f$ \lnot contains \Leftrightarrow {\tt index} = -1 \f$
95+
/// 2b. \f$ {\tt haystack}{\tt terminating_zero} = '\0' \f$
96+
/// 3. \f$ contains \Rightarrow {\tt from\_index} \le {\tt index}
97+
/// \land {\tt haystack}[{\tt index}] = {\tt needle} \f$
98+
/// 4. \f$ \forall i \in [{\tt from\_index}, {\tt index}).\ contains
99+
/// \Rightarrow {\tt haystack}[i] \ne {\tt needle} \f$
100+
/// 5. \f$ \forall m, n \in [{\tt from\_index}, {\tt terminating_zero+1})
101+
/// .\ \lnot contains \Rightarrow {\tt haystack}[m] \ne {\tt needle}
102+
/// \f$
103+
/// \param str: an array of characters expression
104+
/// \param c: a character expression
105+
/// \param from_index: an integer expression
106+
/// \return integer expression `index`
107+
std::pair<exprt, string_constraintst>
108+
string_constraint_generatort::add_axioms_for_c_index_of(
109+
const array_string_exprt &str,
110+
const exprt &c,
111+
const exprt &from_index)
112+
{
113+
string_constraintst constraints;
114+
const typet &index_type = str.length_type();
115+
symbol_exprt index = fresh_symbol("index_of", index_type);
116+
symbol_exprt contains = fresh_symbol("contains_in_index_of");
117+
symbol_exprt terminating_zero = fresh_symbol("zero_in_index_of", index_type);
118+
119+
exprt minus1 = from_integer(-1, index_type);
120+
and_exprt a1(
121+
binary_relation_exprt(index, ID_ge, minus1),
122+
binary_relation_exprt(index, ID_le, terminating_zero),
123+
binary_relation_exprt(
124+
terminating_zero, ID_lt, array_pool.get_or_create_length(str)));
125+
constraints.existential.push_back(a1);
126+
127+
equal_exprt a2(not_exprt(contains), equal_exprt(index, minus1));
128+
constraints.existential.push_back(a2);
129+
130+
const exprt lower_bound(zero_if_negative(from_index));
131+
// make sure that terminating zero exists (and is the smallest index after from
132+
// that has a 0 character)
133+
constraints.existential.push_back(
134+
equal_exprt{str[terminating_zero], from_integer(0, c.type())});
135+
symbol_exprt k = fresh_symbol("QA_index_of", index_type);
136+
const string_constraintt a0 = string_constraintt{
137+
k,
138+
lower_bound,
139+
zero_if_negative(terminating_zero),
140+
not_exprt{equal_exprt{str[k], from_integer(0, c.type())}}};
141+
constraints.universal.push_back(a0);
142+
143+
implies_exprt a3(
144+
contains,
145+
and_exprt(
146+
binary_relation_exprt(from_index, ID_le, index),
147+
equal_exprt(str[index], c)));
148+
constraints.existential.push_back(a3);
149+
150+
symbol_exprt n = fresh_symbol("QA_index_of", index_type);
151+
string_constraintt a4{n,
152+
lower_bound,
153+
zero_if_negative(index),
154+
implies_exprt{contains, notequal_exprt{str[n], c}}};
155+
constraints.universal.push_back(a4);
156+
157+
symbol_exprt m = fresh_symbol("QA_index_of", index_type);
158+
string_constraintt a5(
159+
m,
160+
lower_bound,
161+
zero_if_negative(
162+
plus_exprt{terminating_zero, from_integer(1, terminating_zero.type())}),
163+
implies_exprt(not_exprt(contains), not_exprt(equal_exprt(str[m], c))));
164+
constraints.universal.push_back(a5);
165+
166+
return {index, std::move(constraints)};
167+
}
168+
85169
/// Add axioms stating that the returned value `index` is the index within
86170
/// `haystack` of the first occurrence of `needle` starting the search at
87171
/// `from_index`, or `-1` if needle does not occur at or after position
@@ -329,6 +413,37 @@ string_constraint_generatort::add_axioms_for_index_of(
329413
}
330414
}
331415

416+
/// Index of the first occurence of a target inside the string
417+
///
418+
/// If the target is a character:
419+
// NOLINTNEXTLINE
420+
/// \copybrief add_axioms_for_index_of(const array_string_exprt&,const exprt&,const exprt&)
421+
// NOLINTNEXTLINE
422+
/// \link add_axioms_for_index_of(const array_string_exprt&,const exprt&,const exprt&)
423+
/// (More...) \endlink
424+
///
425+
/// If the target is a refined_string:
426+
/// \copybrief add_axioms_for_index_of_string
427+
/// \link add_axioms_for_index_of_string (More...)
428+
/// \endlink
429+
/// \warning slow for string targets
430+
/// \param f: function application with arguments refined_string `haystack`,
431+
/// refined_string or character `needle`, and optional integer `from_index`
432+
/// with default value `0`
433+
/// \return integer expression
434+
std::pair<exprt, string_constraintst>
435+
string_constraint_generatort::add_axioms_for_c_index_of(
436+
const function_application_exprt &f)
437+
{
438+
auto const &str = f.arguments().at(0);
439+
auto const &c = f.arguments().at(1);
440+
auto const str_array = get_string_expr(array_pool, str);
441+
return add_axioms_for_c_index_of(
442+
str_array,
443+
typecast_exprt{c, str_array.content().type().subtype()},
444+
from_integer(0, str_array.length_type()));
445+
}
446+
332447
/// Add axioms stating that the returned value is the index within `haystack`
333448
/// (`str`) of the last occurrence of `needle` (`c`) starting the search
334449
/// backward at `from_index`, or `-1` if no such character occurs at or before

src/solvers/strings/string_constraint_generator_main.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,8 @@ string_constraint_generatort::add_axioms_for_function_application(
237237
return add_axioms_for_contains(expr);
238238
else if(id == ID_cprover_string_index_of_func)
239239
return add_axioms_for_index_of(expr);
240+
else if(id == ID_cprover_string_c_index_of_func)
241+
return add_axioms_for_c_index_of(expr);
240242
else if(id == ID_cprover_string_last_index_of_func)
241243
return add_axioms_for_last_index_of(expr);
242244
else if(id == ID_cprover_string_parse_int_func)

src/util/irep_ids.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,7 @@ IREP_ID_ONE(cprover_string_empty_string_func)
610610
IREP_ID_ONE(cprover_string_endswith_func)
611611
IREP_ID_ONE(cprover_string_format_func)
612612
IREP_ID_ONE(cprover_string_index_of_func)
613+
IREP_ID_ONE(cprover_string_c_index_of_func)
613614
IREP_ID_ONE(cprover_string_insert_func)
614615
IREP_ID_ONE(cprover_string_is_prefix_func)
615616
IREP_ID_ONE(cprover_string_is_suffix_func)

0 commit comments

Comments
 (0)