diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt index a56c44ccc..f1395625c 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt @@ -32,6 +32,7 @@ import org.jacodb.ets.model.EtsBitOrExpr import org.jacodb.ets.model.EtsBitXorExpr import org.jacodb.ets.model.EtsBlockCfg import org.jacodb.ets.model.EtsBooleanConstant +import org.jacodb.ets.model.EtsBooleanLiteralType import org.jacodb.ets.model.EtsBooleanType import org.jacodb.ets.model.EtsCallExpr import org.jacodb.ets.model.EtsCallStmt @@ -75,7 +76,6 @@ import org.jacodb.ets.model.EtsInstanceOfExpr import org.jacodb.ets.model.EtsIntersectionType import org.jacodb.ets.model.EtsLeftShiftExpr import org.jacodb.ets.model.EtsLexicalEnvType -import org.jacodb.ets.model.EtsLiteralType import org.jacodb.ets.model.EtsLocal import org.jacodb.ets.model.EtsLocalSignature import org.jacodb.ets.model.EtsLtEqExpr @@ -99,6 +99,7 @@ import org.jacodb.ets.model.EtsNullConstant import org.jacodb.ets.model.EtsNullType import org.jacodb.ets.model.EtsNullishCoalescingExpr import org.jacodb.ets.model.EtsNumberConstant +import org.jacodb.ets.model.EtsNumberLiteralType import org.jacodb.ets.model.EtsNumberType import org.jacodb.ets.model.EtsOrExpr import org.jacodb.ets.model.EtsParameterRef @@ -118,6 +119,7 @@ import org.jacodb.ets.model.EtsStmtLocation import org.jacodb.ets.model.EtsStrictEqExpr import org.jacodb.ets.model.EtsStrictNotEqExpr import org.jacodb.ets.model.EtsStringConstant +import org.jacodb.ets.model.EtsStringLiteralType import org.jacodb.ets.model.EtsStringType import org.jacodb.ets.model.EtsSubExpr import org.jacodb.ets.model.EtsThis @@ -544,9 +546,11 @@ fun TypeDto.toEtsType(): EtsType = when (this) { closures = closures.map { it.toEtsLocal() }, ) - is LiteralTypeDto -> EtsLiteralType( - literalTypeName = literal.toString(), - ) + is LiteralTypeDto -> when (val literalValue = literal) { + is PrimitiveLiteralDto.StringLiteral -> EtsStringLiteralType(literalValue.value) + is PrimitiveLiteralDto.NumberLiteral -> EtsNumberLiteralType(literalValue.value) + is PrimitiveLiteralDto.BooleanLiteral -> EtsBooleanLiteralType(literalValue.value) + } NeverTypeDto -> EtsNeverType diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Type.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Type.kt index 26395d0e8..d6dda2ae2 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Type.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Type.kt @@ -41,7 +41,11 @@ interface EtsType : TypeName, CommonType { fun visit(type: EtsUndefinedType): R fun visit(type: EtsVoidType): R fun visit(type: EtsNeverType): R - fun visit(type: EtsLiteralType): R + + // Literal + fun visit(type: EtsStringLiteralType): R + fun visit(type: EtsNumberLiteralType): R + fun visit(type: EtsBooleanLiteralType): R // Ref fun visit(type: EtsClassType): R @@ -74,7 +78,10 @@ interface EtsType : TypeName, CommonType { override fun visit(type: EtsUndefinedType): R = defaultVisit(type) override fun visit(type: EtsVoidType): R = defaultVisit(type) override fun visit(type: EtsNeverType): R = defaultVisit(type) - override fun visit(type: EtsLiteralType): R = defaultVisit(type) + + override fun visit(type: EtsStringLiteralType): R = defaultVisit(type) + override fun visit(type: EtsNumberLiteralType): R = defaultVisit(type) + override fun visit(type: EtsBooleanLiteralType): R = defaultVisit(type) override fun visit(type: EtsClassType): R = defaultVisit(type) override fun visit(type: EtsUnclearRefType): R = defaultVisit(type) @@ -277,11 +284,39 @@ object EtsNeverType : EtsPrimitiveType { } } -data class EtsLiteralType( - val literalTypeName: String, -) : EtsPrimitiveType { +sealed interface EtsLiteralType : EtsPrimitiveType + +data class EtsStringLiteralType( + val value: String, +) : EtsLiteralType { + override val typeName: String + get() = "\"$value\"" + + override fun toString(): String = typeName + + override fun accept(visitor: EtsType.Visitor): R { + return visitor.visit(this) + } +} + +data class EtsNumberLiteralType( + val value: Double, +) : EtsLiteralType { + override val typeName: String + get() = value.toString() + + override fun toString(): String = typeName + + override fun accept(visitor: EtsType.Visitor): R { + return visitor.visit(this) + } +} + +data class EtsBooleanLiteralType( + val value: Boolean, +) : EtsLiteralType { override val typeName: String - get() = literalTypeName + get() = value.toString() override fun toString(): String = typeName diff --git a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt index ee06f7997..7c920d504 100644 --- a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt +++ b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt @@ -428,6 +428,126 @@ class EtsFromJsonTest { assertEquals(PrimitiveLiteralDto.StringLiteral("hello"), typeDto.literal) } + @Test + fun testLoadNumberLiteralTypeFromJson() { + // TS: `let x: 42 = 42;` + val jsonString = """ + { + "_": "LiteralType", + "literal": 42 + } + """.trimIndent() + val typeDto = Json.decodeFromString(jsonString) + logger.info { "typeDto = $typeDto" } + assertIs(typeDto) + assertEquals(PrimitiveLiteralDto.NumberLiteral(42.0), typeDto.literal) + } + + @Test + fun testLoadFloatLiteralTypeFromJson() { + // TS: `let x: 3.14 = 3.14;` + val jsonString = """ + { + "_": "LiteralType", + "literal": 3.14 + } + """.trimIndent() + val typeDto = Json.decodeFromString(jsonString) + logger.info { "typeDto = $typeDto" } + assertIs(typeDto) + assertEquals(PrimitiveLiteralDto.NumberLiteral(3.14), typeDto.literal) + } + + @Test + fun testLoadNegativeNumberLiteralTypeFromJson() { + // TS: `let x: -5 = -5;` + val jsonString = """ + { + "_": "LiteralType", + "literal": -5 + } + """.trimIndent() + val typeDto = Json.decodeFromString(jsonString) + logger.info { "typeDto = $typeDto" } + assertIs(typeDto) + assertEquals(PrimitiveLiteralDto.NumberLiteral(-5.0), typeDto.literal) + } + + @Test + fun testLoadBooleanTrueLiteralTypeFromJson() { + // TS: `let x: true = true;` + val jsonString = """ + { + "_": "LiteralType", + "literal": true + } + """.trimIndent() + val typeDto = Json.decodeFromString(jsonString) + logger.info { "typeDto = $typeDto" } + assertIs(typeDto) + assertEquals(PrimitiveLiteralDto.BooleanLiteral(true), typeDto.literal) + } + + @Test + fun testLoadBooleanFalseLiteralTypeFromJson() { + // TS: `let x: false = false;` + val jsonString = """ + { + "_": "LiteralType", + "literal": false + } + """.trimIndent() + val typeDto = Json.decodeFromString(jsonString) + logger.info { "typeDto = $typeDto" } + assertIs(typeDto) + assertEquals(PrimitiveLiteralDto.BooleanLiteral(false), typeDto.literal) + } + + @Test + fun testLoadEmptyStringLiteralTypeFromJson() { + // TS: `let x: "" = "";` + val jsonString = """ + { + "_": "LiteralType", + "literal": "" + } + """.trimIndent() + val typeDto = Json.decodeFromString(jsonString) + logger.info { "typeDto = $typeDto" } + assertIs(typeDto) + assertEquals(PrimitiveLiteralDto.StringLiteral(""), typeDto.literal) + } + + @Test + fun testLoadStringLiteralWithSpecialCharactersFromJson() { + // TS: `let x: "Hello\nWorld\t!" = "Hello\nWorld\t!";` + val jsonString = """ + { + "_": "LiteralType", + "literal": "Hello\nWorld\t!" + } + """.trimIndent() + val typeDto = Json.decodeFromString(jsonString) + logger.info { "typeDto = $typeDto" } + assertIs(typeDto) + assertEquals(PrimitiveLiteralDto.StringLiteral("Hello\nWorld\t!"), typeDto.literal) + } + + @Test + fun testLoadZeroLiteralTypeFromJson() { + // TS: `let x: 0 = 0;` + val jsonString = """ + { + "_": "LiteralType", + "literal": 0 + } + """.trimIndent() + val typeDto = Json.decodeFromString(jsonString) + logger.info { "typeDto = $typeDto" } + assertIs(typeDto) + assertEquals(PrimitiveLiteralDto.NumberLiteral(0.0), typeDto.literal) + } + @Test fun testLoadRawTypeFromJson() { val jsonString = """