|
13 | 13 |
|
14 | 14 | #include <solvers/refinement/string_refinement_invariant.h>
|
15 | 15 | #include <solvers/refinement/string_constraint_generator.h>
|
| 16 | +#include "expr_cast.h" |
16 | 17 |
|
17 | 18 | /// add axioms to say that the returned string expression `res` has length `k`
|
18 | 19 | /// and characters at position `i` in `res` are equal to the character at
|
@@ -359,38 +360,70 @@ string_exprt string_constraint_generatort::add_axioms_for_char_set(
|
359 | 360 | return res;
|
360 | 361 | }
|
361 | 362 |
|
362 |
| -/// add axioms corresponding to the String.replace java function |
363 |
| -/// \par parameters: function application with three arguments, the first is a |
364 |
| -/// string, |
365 |
| -/// the second and the third are characters |
| 363 | +/// Convert two expressions to pair of chars |
| 364 | +/// If both expressions are characters, return pair of them |
| 365 | +/// If both expressions are 1-length strings, return first character of each |
| 366 | +/// Otherwise return empty optional |
| 367 | +/// \param expr1 First expression |
| 368 | +/// \param expr2 Second expression |
| 369 | +/// \return Optional pair of two expressions |
| 370 | +static optionalt<std::pair<exprt, exprt>> to_char_pair( |
| 371 | + exprt expr1, exprt expr2) |
| 372 | +{ |
| 373 | + if((expr1.type().id()==ID_unsignedbv |
| 374 | + || expr1.type().id()==ID_char) |
| 375 | + && (expr2.type().id()==ID_char |
| 376 | + || expr2.type().id()==ID_unsignedbv)) |
| 377 | + return std::make_pair(expr1, expr2); |
| 378 | + const auto expr1_str=to_string_expr(expr1); |
| 379 | + const auto expr2_str=to_string_expr(expr2); |
| 380 | + const auto expr1_length=expr_cast<size_t>(expr1_str.length()); |
| 381 | + const auto expr2_length=expr_cast<size_t>(expr2_str.length()); |
| 382 | + if(expr1_length && expr2_length && *expr1_length==1 && *expr2_length==1) |
| 383 | + return std::make_pair(exprt(expr1_str[0]), exprt(expr2_str[0])); |
| 384 | + return { }; |
| 385 | +} |
| 386 | + |
| 387 | +/// Add axioms corresponding to the String.replace java function |
| 388 | +/// Only supports String.replace(char, char) and |
| 389 | +/// String.replace(String, String) for single-character strings |
| 390 | +/// Returns original string in every other case (that behaviour is to |
| 391 | +/// be fixed in the future) |
| 392 | +/// \param f function application with three arguments, the first is a |
| 393 | +/// string, the second and the third are either pair of characters or |
| 394 | +/// a pair of strings |
366 | 395 | /// \return a new string expression
|
367 | 396 | string_exprt string_constraint_generatort::add_axioms_for_replace(
|
368 | 397 | const function_application_exprt &f)
|
369 | 398 | {
|
370 | 399 | string_exprt str=get_string_expr(args(f, 3)[0]);
|
371 | 400 | const refined_string_typet &ref_type=to_refined_string_type(str.type());
|
372 |
| - const exprt &old_char=args(f, 3)[1]; |
373 |
| - const exprt &new_char=args(f, 3)[2]; |
374 |
| - string_exprt res=fresh_string(ref_type); |
375 |
| - |
376 |
| - // We add axioms: |
377 |
| - // a1 : |res| = |str| |
378 |
| - // a2 : forall qvar, 0<=qvar<|res|, |
379 |
| - // str[qvar]=oldChar => res[qvar]=newChar |
380 |
| - // !str[qvar]=oldChar => res[qvar]=str[qvar] |
381 |
| - |
382 |
| - m_axioms.push_back(res.axiom_for_has_same_length_as(str)); |
383 |
| - |
384 |
| - symbol_exprt qvar=fresh_univ_index("QA_replace", ref_type.get_index_type()); |
385 |
| - implies_exprt case1( |
386 |
| - equal_exprt(str[qvar], old_char), |
387 |
| - equal_exprt(res[qvar], new_char)); |
388 |
| - implies_exprt case2( |
389 |
| - not_exprt(equal_exprt(str[qvar], old_char)), |
390 |
| - equal_exprt(res[qvar], str[qvar])); |
391 |
| - string_constraintt a2(qvar, res.length(), and_exprt(case1, case2)); |
392 |
| - m_axioms.push_back(a2); |
393 |
| - return res; |
| 401 | + if(const auto maybe_chars=to_char_pair(args(f, 3)[1], args(f, 3)[2])) |
| 402 | + { |
| 403 | + const auto old_char=maybe_chars->first; |
| 404 | + const auto new_char=maybe_chars->second; |
| 405 | + string_exprt res=fresh_string(ref_type); |
| 406 | + |
| 407 | + // We add axioms: |
| 408 | + // a1 : |res| = |str| |
| 409 | + // a2 : forall qvar, 0<=qvar<|res|, |
| 410 | + // str[qvar]=oldChar => res[qvar]=newChar |
| 411 | + // !str[qvar]=oldChar => res[qvar]=str[qvar] |
| 412 | + |
| 413 | + m_axioms.push_back(res.axiom_for_has_same_length_as(str)); |
| 414 | + |
| 415 | + symbol_exprt qvar=fresh_univ_index("QA_replace", ref_type.get_index_type()); |
| 416 | + implies_exprt case1( |
| 417 | + equal_exprt(str[qvar], old_char), |
| 418 | + equal_exprt(res[qvar], new_char)); |
| 419 | + implies_exprt case2( |
| 420 | + not_exprt(equal_exprt(str[qvar], old_char)), |
| 421 | + equal_exprt(res[qvar], str[qvar])); |
| 422 | + string_constraintt a2(qvar, res.length(), and_exprt(case1, case2)); |
| 423 | + m_axioms.push_back(a2); |
| 424 | + return res; |
| 425 | + } |
| 426 | + return str; |
394 | 427 | }
|
395 | 428 |
|
396 | 429 | /// add axioms corresponding to the StringBuilder.deleteCharAt java function
|
|
0 commit comments