Skip to content

Commit 55a6434

Browse files
committed
Implement basic simplifications for substring expressions
1 parent f5640e3 commit 55a6434

2 files changed

Lines changed: 94 additions & 3 deletions

File tree

ksmt-core/src/main/kotlin/io/ksmt/expr/rewrite/simplify/StringSimplification.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,19 +65,19 @@ fun KContext.simplifyStringContains(
6565
fun KContext.simplifyStringSingletonSub(
6666
arg0: KExpr<KStringSort>,
6767
arg1: KExpr<KIntSort>
68-
): KExpr<KStringSort> = mkStringSingletonSubNoSimplify(arg0, arg1) // Temporarily
68+
): KExpr<KStringSort> = simplifyStringSingletonSubExprBasic(arg0, arg1, ::mkStringSingletonSubNoSimplify)
6969

7070
fun KContext.simplifyStringSub(
7171
arg0: KExpr<KStringSort>,
7272
arg1: KExpr<KIntSort>,
7373
arg2: KExpr<KIntSort>
74-
): KExpr<KStringSort> = mkStringSubNoSimplify(arg0, arg1, arg2) // Temporarily
74+
): KExpr<KStringSort> = simplifyStringSubExprBasic(arg0, arg1, arg2, ::mkStringSubNoSimplify)
7575

7676
fun KContext.simplifyStringIndexOf(
7777
arg0: KExpr<KStringSort>,
7878
arg1: KExpr<KStringSort>,
7979
arg2: KExpr<KIntSort>
80-
): KExpr<KIntSort> = mkStringIndexOfNoSimplify(arg0, arg1, arg2) // Temporarily
80+
): KExpr<KIntSort> = simplifyStringIndexOfExprBasic(arg0, arg1, arg2, ::mkStringIndexOfNoSimplify)
8181

8282
fun KContext.simplifyStringIndexOfRegex(
8383
arg0: KExpr<KStringSort>,

ksmt-core/src/main/kotlin/io/ksmt/expr/rewrite/simplify/StringSimplificationRules.kt

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,90 @@ inline fun KContext.simplifyStringBasicContainsExpr(
170170
cont(arg0, arg1)
171171
}
172172

173+
/*
174+
* Substring expressions simplifications
175+
* */
176+
177+
inline fun KContext.simplifyStringSingletonSubExprBasic(
178+
arg0: KExpr<KStringSort>,
179+
arg1: KExpr<KIntSort>,
180+
cont: (KExpr<KStringSort>, KExpr<KIntSort>) -> KExpr<KStringSort>
181+
): KExpr<KStringSort> {
182+
return if (arg0 is KStringLiteralExpr && arg1 is KIntNumExpr) {
183+
val str = arg0.value
184+
val pos = when (arg1) {
185+
is KInt32NumExpr -> arg1.value.toLong()
186+
is KInt64NumExpr -> arg1.value
187+
is KIntBigNumExpr -> arg1.value.toLong()
188+
else -> return cont(arg0, arg1)
189+
}
190+
val result = if (pos <= Int.MAX_VALUE && pos >= 0 && pos < str.length) {
191+
str[pos.toInt()].toString()
192+
} else {
193+
""
194+
}
195+
mkStringLiteral(result)
196+
} else {
197+
cont(arg0, arg1)
198+
}
199+
}
200+
201+
inline fun KContext.simplifyStringSubExprBasic(
202+
arg0: KExpr<KStringSort>,
203+
arg1: KExpr<KIntSort>,
204+
arg2: KExpr<KIntSort>,
205+
cont: (KExpr<KStringSort>, KExpr<KIntSort>, KExpr<KIntSort>) -> KExpr<KStringSort>
206+
): KExpr<KStringSort> {
207+
return if (arg0 is KStringLiteralExpr && arg1 is KIntNumExpr && arg2 is KIntNumExpr) {
208+
val str = arg0.value
209+
val startPos = arg1.toLongValue()
210+
val length = arg2.toLongValue()
211+
val result = if (startPos in 0..Int.MAX_VALUE && length in 0..Int.MAX_VALUE && startPos < str.length) {
212+
val endPos = minOf(startPos + length, str.length.toLong()).toInt()
213+
str.substring(startPos.toInt(), endPos)
214+
} else {
215+
""
216+
}
217+
mkStringLiteral(result)
218+
} else {
219+
cont(arg0, arg1, arg2)
220+
}
221+
}
222+
223+
inline fun KContext.simplifyStringIndexOfExprBasic(
224+
arg0: KExpr<KStringSort>, // Исходная строка (s)
225+
arg1: KExpr<KStringSort>, // Подстрока для поиска (t)
226+
arg2: KExpr<KIntSort>, // Начальная позиция (i)
227+
cont: (KExpr<KStringSort>, KExpr<KStringSort>, KExpr<KIntSort>) -> KExpr<KIntSort>
228+
): KExpr<KIntSort> = with(arg0.ctx) {
229+
230+
if (arg0 is KStringLiteralExpr && arg1 is KStringLiteralExpr && arg2 is KIntNumExpr) {
231+
val str = arg0.value
232+
val search = arg1.value
233+
val startPosLong = arg2.toLongValue()
234+
val startPos: Int
235+
if (startPosLong <= Int.MAX_VALUE) {
236+
startPos = startPosLong.toInt()
237+
} else {
238+
return cont(arg0, arg1, arg2)
239+
}
240+
241+
val result = if (startPos >= 0 && startPos <= str.length) {
242+
if (search.isEmpty()) {
243+
startPos
244+
} else {
245+
val index = str.indexOf(search, startPos)
246+
if (index >= 0) index else -1
247+
}
248+
} else {
249+
-1
250+
}
251+
return mkIntNum(result)
252+
} else {
253+
return cont(arg0, arg1, arg2)
254+
}
255+
}
256+
173257
/*
174258
* String replace expressions simplifications
175259
* */
@@ -325,3 +409,10 @@ inline fun <K: KSort> tryEvalStringLiteralOperation(
325409
} else {
326410
cont()
327411
}
412+
413+
fun KIntNumExpr.toLongValue(): Long = when (this) {
414+
is KInt32NumExpr -> value.toLong()
415+
is KInt64NumExpr -> value
416+
is KIntBigNumExpr -> value.toLong()
417+
else -> throw IllegalArgumentException("Unsupported KIntNumExpr type: ${this::class}")
418+
}

0 commit comments

Comments
 (0)