Skip to content

Commit 904193a

Browse files
sofurihafetamarinvs19
authored andcommitted
Add string interpolation support in codegen #1546 (#1587)
1 parent be81925 commit 904193a

File tree

7 files changed

+77
-5
lines changed

7 files changed

+77
-5
lines changed

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/CgElement.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ interface CgElement {
8787
is CgNotNullAssertion -> visit(element)
8888
is CgVariable -> visit(element)
8989
is CgParameterDeclaration -> visit(element)
90+
is CgFormattedString -> visit(element)
9091
is CgLiteral -> visit(element)
9192
is CgNonStaticRunnable -> visit(element)
9293
is CgStaticRunnable -> visit(element)
@@ -823,6 +824,11 @@ class CgArrayInitializer(val arrayType: ClassId, val elementType: ClassId, val v
823824

824825
class CgSpread(override val type: ClassId, val array: CgExpression) : CgExpression
825826

827+
// Interpolated string
828+
// e.g. String.format() for Java, "${}" for Kotlin
829+
830+
class CgFormattedString(val array: List<CgExpression>) : CgElement
831+
826832
// Enum constant
827833

828834
data class CgEnumConstantAccess(

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/renderer/CgAbstractRenderer.kt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,11 @@ abstract class CgAbstractRenderer(
595595
// Primitive and String literals
596596

597597
override fun visit(element: CgLiteral) {
598-
val value = with(element.value) {
598+
print(element.toStringConstant())
599+
}
600+
601+
protected fun CgLiteral.toStringConstant(asRawString: Boolean = false) =
602+
with(this.value) {
599603
when (this) {
600604
is Byte -> toStringConstant()
601605
is Char -> toStringConstant()
@@ -605,12 +609,11 @@ abstract class CgAbstractRenderer(
605609
is Float -> toStringConstant()
606610
is Double -> toStringConstant()
607611
is Boolean -> toStringConstant()
608-
is String -> toStringConstant()
612+
// String is "\"" + "str" + "\"", RawString is "str"
613+
is String -> if (asRawString) "$this".escapeCharacters() else toStringConstant()
609614
else -> "$this"
610615
}
611616
}
612-
print(value)
613-
}
614617

615618
// Non-static runnable like this::toString or (new Object())::toString etc
616619
override fun visit(element: CgNonStaticRunnable) {
@@ -924,7 +927,7 @@ abstract class CgAbstractRenderer(
924927
private fun Boolean.toStringConstant() =
925928
if (this) "true" else "false"
926929

927-
private fun String.toStringConstant(): String = "\"" + escapeCharacters() + "\""
930+
protected fun String.toStringConstant(): String = "\"" + escapeCharacters() + "\""
928931

929932
protected abstract fun String.escapeCharacters(): String
930933

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/renderer/CgJavaRenderer.kt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import org.utbot.framework.codegen.domain.models.CgSwitchCase
3232
import org.utbot.framework.codegen.domain.models.CgSwitchCaseLabel
3333
import org.utbot.framework.codegen.domain.models.CgClass
3434
import org.utbot.framework.codegen.domain.models.CgClassBody
35+
import org.utbot.framework.codegen.domain.models.CgFormattedString
36+
import org.utbot.framework.codegen.domain.models.CgLiteral
3537
import org.utbot.framework.codegen.domain.models.CgTestMethod
3638
import org.utbot.framework.codegen.domain.models.CgTypeCast
3739
import org.utbot.framework.codegen.domain.models.CgVariable
@@ -320,6 +322,27 @@ internal class CgJavaRenderer(context: CgRendererContext, printer: CgPrinter = C
320322
println("}")
321323
}
322324

325+
override fun visit(element: CgFormattedString) {
326+
val nonLiteralElements = element.array.filterNot { it is CgLiteral }
327+
328+
print("String.format(")
329+
val constructedMsg = buildString {
330+
element.array.forEachIndexed { index, cgElement ->
331+
if (cgElement is CgLiteral) append(
332+
cgElement.toStringConstant(asRawString = true)
333+
) else append("%s")
334+
if (index < element.array.lastIndex) append(" ")
335+
}
336+
}
337+
338+
print(constructedMsg.toStringConstant())
339+
340+
// Comma to separate msg from variables
341+
if (nonLiteralElements.isNotEmpty()) print(", ")
342+
nonLiteralElements.renderSeparated(newLines = false)
343+
print(")")
344+
}
345+
323346
override fun toStringConstantImpl(byte: Byte): String = "(byte) $byte"
324347

325348
override fun toStringConstantImpl(short: Short): String = "(short) $short"

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/renderer/CgKotlinRenderer.kt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,11 @@ import org.utbot.framework.codegen.domain.models.CgSwitchCase
3838
import org.utbot.framework.codegen.domain.models.CgSwitchCaseLabel
3939
import org.utbot.framework.codegen.domain.models.CgClass
4040
import org.utbot.framework.codegen.domain.models.CgClassBody
41+
import org.utbot.framework.codegen.domain.models.CgFormattedString
42+
import org.utbot.framework.codegen.domain.models.CgLiteral
4143
import org.utbot.framework.codegen.domain.models.CgTestMethod
4244
import org.utbot.framework.codegen.domain.models.CgTypeCast
45+
import org.utbot.framework.codegen.domain.models.CgValue
4346
import org.utbot.framework.codegen.domain.models.CgVariable
4447
import org.utbot.framework.codegen.util.nullLiteral
4548
import org.utbot.framework.plugin.api.BuiltinClassId
@@ -480,6 +483,29 @@ internal class CgKotlinRenderer(context: CgRendererContext, printer: CgPrinter =
480483
println("}")
481484
}
482485

486+
override fun visit(element: CgFormattedString) {
487+
print("\"")
488+
element.array.forEachIndexed { index, cgElement ->
489+
if (cgElement is CgLiteral)
490+
print(cgElement.toStringConstant(asRawString = true))
491+
else {
492+
print("$")
493+
when (cgElement) {
494+
// It is not necessary to wrap variables with curly brackets
495+
is CgVariable -> cgElement.accept(this)
496+
else -> {
497+
print("{")
498+
cgElement.accept(this)
499+
print("}")
500+
}
501+
}
502+
}
503+
504+
if (index < element.array.lastIndex) print(" ")
505+
}
506+
print("\"")
507+
}
508+
483509
override fun toStringConstantImpl(byte: Byte): String = buildString {
484510
if (byte < 0) {
485511
append("(")

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/renderer/CgVisitor.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ import org.utbot.framework.codegen.domain.models.CgSwitchCaseLabel
7676
import org.utbot.framework.codegen.domain.models.CgClass
7777
import org.utbot.framework.codegen.domain.models.CgClassBody
7878
import org.utbot.framework.codegen.domain.models.CgDocRegularLineStmt
79+
import org.utbot.framework.codegen.domain.models.CgFormattedString
7980
import org.utbot.framework.codegen.domain.models.CgNestedClassesRegion
8081
import org.utbot.framework.codegen.domain.models.CgTestMethod
8182
import org.utbot.framework.codegen.domain.models.CgTestMethodCluster
@@ -223,6 +224,9 @@ interface CgVisitor<R> {
223224
// Spread operator
224225
fun visit(element: CgSpread): R
225226

227+
// Formatted string
228+
fun visit(element: CgFormattedString): R
229+
226230
// Enum constant
227231
fun visit(element: CgEnumConstantAccess): R
228232

utbot-js/src/main/kotlin/framework/codegen/model/constructor/visitor/CgJsRenderer.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import org.utbot.framework.codegen.domain.models.CgExecutableCall
2121
import org.utbot.framework.codegen.domain.models.CgExpression
2222
import org.utbot.framework.codegen.domain.models.CgFieldAccess
2323
import org.utbot.framework.codegen.domain.models.CgForLoop
24+
import org.utbot.framework.codegen.domain.models.CgFormattedString
2425
import org.utbot.framework.codegen.domain.models.CgGetJavaClass
2526
import org.utbot.framework.codegen.domain.models.CgGetKotlinClass
2627
import org.utbot.framework.codegen.domain.models.CgGetLength
@@ -340,6 +341,10 @@ internal class CgJsRenderer(context: CgRendererContext, printer: CgPrinter = CgP
340341
}
341342
}
342343

344+
override fun visit(element: CgFormattedString) {
345+
throw NotImplementedError("String interpolation is not supported in JavaScript renderer")
346+
}
347+
343348
//TODO MINOR: check
344349
override fun renderForLoopVarControl(element: CgForLoop) {
345350
print("for (")

utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/visitor/CgPythonRenderer.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import org.utbot.framework.codegen.domain.models.CgExecutableCall
2828
import org.utbot.framework.codegen.domain.models.CgExpression
2929
import org.utbot.framework.codegen.domain.models.CgForEachLoop
3030
import org.utbot.framework.codegen.domain.models.CgForLoop
31+
import org.utbot.framework.codegen.domain.models.CgFormattedString
3132
import org.utbot.framework.codegen.domain.models.CgGetJavaClass
3233
import org.utbot.framework.codegen.domain.models.CgGetKotlinClass
3334
import org.utbot.framework.codegen.domain.models.CgGetLength
@@ -530,6 +531,10 @@ internal class CgPythonRenderer(
530531
print(element.value.toString())
531532
}
532533

534+
override fun visit(element: CgFormattedString) {
535+
throw NotImplementedError("String interpolation is not supported in Python renderer")
536+
}
537+
533538
override fun String.escapeCharacters(): String =
534539
StringEscapeUtils
535540
.escapeJava(this)

0 commit comments

Comments
 (0)