Skip to content

Commit 58f289e

Browse files
committed
Add string interpolation support in codegen
1 parent 651ecca commit 58f289e

File tree

7 files changed

+82
-5
lines changed

7 files changed

+82
-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>) : CgStatement
831+
826832
// Enum constant
827833

828834
data class CgEnumConstantAccess(

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

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

597597
override fun visit(element: CgLiteral) {
598-
val value = with(element.value) {
598+
val value = typeToStringConstant(element)
599+
print(value)
600+
}
601+
602+
protected fun typeToStringConstant(element: CgLiteral, useRawString: Boolean = false): String =
603+
with(element.value) {
599604
when (this) {
600605
is Byte -> toStringConstant()
601606
is Char -> toStringConstant()
@@ -605,12 +610,12 @@ abstract class CgAbstractRenderer(
605610
is Float -> toStringConstant()
606611
is Double -> toStringConstant()
607612
is Boolean -> toStringConstant()
608-
is String -> toStringConstant()
613+
// String is "\"" + "str" + "\"", RawString is "str"
614+
is String -> if (useRawString) "$this" else toStringConstant()
609615
else -> "$this"
610616
}
611617
}
612-
print(value)
613-
}
618+
614619

615620
// Non-static runnable like this::toString or (new Object())::toString etc
616621
override fun visit(element: CgNonStaticRunnable) {
@@ -924,7 +929,7 @@ abstract class CgAbstractRenderer(
924929
private fun Boolean.toStringConstant() =
925930
if (this) "true" else "false"
926931

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

929934
protected abstract fun String.escapeCharacters(): String
930935

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

Lines changed: 26 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,30 @@ 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+
typeToStringConstant(
333+
cgElement,
334+
useRawString = true
335+
)
336+
) else append("%s")
337+
if (index < element.array.lastIndex) append(" ")
338+
}
339+
}
340+
341+
print(constructedMsg.toStringConstant())
342+
343+
// Comma to separate msg from variables
344+
if (nonLiteralElements.isNotEmpty()) print(", ")
345+
nonLiteralElements.renderSeparated(false)
346+
print(")")
347+
}
348+
323349
override fun toStringConstantImpl(byte: Byte): String = "(byte) $byte"
324350

325351
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) print(typeToStringConstant(cgElement, useRawString = true))
490+
else {
491+
print("$")
492+
when (cgElement) {
493+
// It is not necessary to wrap simple values or variables with curly brackets
494+
is CgValue -> cgElement.accept(this)
495+
else -> {
496+
print("{")
497+
cgElement.accept(this)
498+
print("}")
499+
}
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+
//TODO
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+
//TODO
536+
}
537+
533538
override fun String.escapeCharacters(): String =
534539
StringEscapeUtils
535540
.escapeJava(this)

0 commit comments

Comments
 (0)