trivial implementation of class name = based serialization (LynonType.Other)
This commit is contained in:
parent
d7bd159fcb
commit
2339130241
@ -11,6 +11,8 @@ open class ObjClass(
|
|||||||
vararg parents: ObjClass,
|
vararg parents: ObjClass,
|
||||||
) : Obj() {
|
) : Obj() {
|
||||||
|
|
||||||
|
val classNameObj by lazy { ObjString(className) }
|
||||||
|
|
||||||
var constructorMeta: ArgsDeclaration? = null
|
var constructorMeta: ArgsDeclaration? = null
|
||||||
var instanceConstructor: Statement? = null
|
var instanceConstructor: Statement? = null
|
||||||
|
|
||||||
@ -100,6 +102,7 @@ open class ObjClass(
|
|||||||
}
|
}
|
||||||
|
|
||||||
open suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj = scope.raiseNotImplemented()
|
open suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj = scope.raiseNotImplemented()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,6 +2,9 @@ package net.sergeych.lyng.obj
|
|||||||
|
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
import net.sergeych.lyng.Statement
|
import net.sergeych.lyng.Statement
|
||||||
|
import net.sergeych.lynon.LynonDecoder
|
||||||
|
import net.sergeych.lynon.LynonEncoder
|
||||||
|
import net.sergeych.lynon.LynonType
|
||||||
|
|
||||||
class ObjMapEntry(val key: Obj, val value: Obj) : Obj() {
|
class ObjMapEntry(val key: Obj, val value: Obj) : Obj() {
|
||||||
|
|
||||||
@ -12,6 +15,14 @@ class ObjMapEntry(val key: Obj, val value: Obj) : Obj() {
|
|||||||
return value.compareTo(scope, other.value)
|
return value.compareTo(scope, other.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return key.hashCode() + value.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return other is ObjMapEntry && key == other.key && value == other.value
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun getAt(scope: Scope, index: Obj): Obj = when (index.toInt()) {
|
override suspend fun getAt(scope: Scope, index: Obj): Obj = when (index.toInt()) {
|
||||||
0 -> key
|
0 -> key
|
||||||
1 -> value
|
1 -> value
|
||||||
@ -24,11 +35,23 @@ class ObjMapEntry(val key: Obj, val value: Obj) : Obj() {
|
|||||||
|
|
||||||
override val objClass = type
|
override val objClass = type
|
||||||
|
|
||||||
|
override suspend fun serialize(scope: Scope, encoder: LynonEncoder, lynonType: LynonType?) {
|
||||||
|
encoder.encodeAny(scope,key)
|
||||||
|
encoder.encodeAny(scope,value)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val type = object : ObjClass("MapEntry", ObjArray) {
|
val type = object : ObjClass("MapEntry", ObjArray) {
|
||||||
override suspend fun callOn(scope: Scope): Obj {
|
override suspend fun callOn(scope: Scope): Obj {
|
||||||
return ObjMapEntry(scope.requiredArg<Obj>(0), scope.requiredArg<Obj>(1))
|
return ObjMapEntry(scope.requiredArg<Obj>(0), scope.requiredArg<Obj>(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj {
|
||||||
|
return ObjMapEntry(
|
||||||
|
decoder.decodeAny(scope),
|
||||||
|
decoder.decodeAny(scope)
|
||||||
|
)
|
||||||
|
}
|
||||||
}.apply {
|
}.apply {
|
||||||
addFn("key") { thisAs<ObjMapEntry>().key }
|
addFn("key") { thisAs<ObjMapEntry>().key }
|
||||||
addFn("value") { thisAs<ObjMapEntry>().value }
|
addFn("value") { thisAs<ObjMapEntry>().value }
|
||||||
|
@ -3,6 +3,7 @@ package net.sergeych.lynon
|
|||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
import net.sergeych.lyng.obj.Obj
|
import net.sergeych.lyng.obj.Obj
|
||||||
import net.sergeych.lyng.obj.ObjClass
|
import net.sergeych.lyng.obj.ObjClass
|
||||||
|
import net.sergeych.lyng.obj.ObjString
|
||||||
|
|
||||||
open class LynonDecoder(val bin: BitInput, val settings: LynonSettings = LynonSettings.default) {
|
open class LynonDecoder(val bin: BitInput, val settings: LynonSettings = LynonSettings.default) {
|
||||||
|
|
||||||
@ -17,7 +18,7 @@ open class LynonDecoder(val bin: BitInput, val settings: LynonSettings = LynonSe
|
|||||||
val cache = mutableListOf<Any>()
|
val cache = mutableListOf<Any>()
|
||||||
|
|
||||||
|
|
||||||
inline fun <reified T : Any>decodeCached(f: LynonDecoder.() -> T): T {
|
inline fun <reified T : Any> decodeCached(f: LynonDecoder.() -> T): T {
|
||||||
return if (bin.getBit() == 0) {
|
return if (bin.getBit() == 0) {
|
||||||
// unpack and cache
|
// unpack and cache
|
||||||
f().also {
|
f().also {
|
||||||
@ -38,31 +39,46 @@ open class LynonDecoder(val bin: BitInput, val settings: LynonSettings = LynonSe
|
|||||||
|
|
||||||
suspend fun decodeAny(scope: Scope): Obj = decodeCached {
|
suspend fun decodeAny(scope: Scope): Obj = decodeCached {
|
||||||
val type = LynonType.entries[bin.getBits(4).toInt()]
|
val type = LynonType.entries[bin.getBits(4).toInt()]
|
||||||
|
if (type != LynonType.Other) {
|
||||||
type.objClass.deserialize(scope, this, type)
|
type.objClass.deserialize(scope, this, type)
|
||||||
|
} else {
|
||||||
|
decodeClassObj(scope).deserialize(scope, this, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun decodeClassObj(scope: Scope): ObjClass {
|
||||||
|
val className = decodeObject(scope, ObjString.type, null) as ObjString
|
||||||
|
println("expected class name $className")
|
||||||
|
return scope.get(className.value)?.value?.let {
|
||||||
|
if (it !is ObjClass)
|
||||||
|
scope.raiseClassCastError("Expected obj class but got ${it::class.qualifiedName}")
|
||||||
|
it
|
||||||
|
} ?: scope.raiseSymbolNotFound("can't deserialize: not found type $className")
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun decodeAnyList(scope: Scope): MutableList<Obj> {
|
suspend fun decodeAnyList(scope: Scope): MutableList<Obj> {
|
||||||
return if( bin.getBit() == 1) {
|
return if (bin.getBit() == 1) {
|
||||||
// homogenous
|
// homogenous
|
||||||
val type = LynonType.entries[getBitsAsInt(4)]
|
val type = LynonType.entries[getBitsAsInt(4)]
|
||||||
|
val list = mutableListOf<Obj>()
|
||||||
|
val objClass = if (type == LynonType.Other)
|
||||||
|
decodeClassObj(scope).also { println("detected class obj: $it") }
|
||||||
|
else type.objClass
|
||||||
val size = bin.unpackUnsigned().toInt()
|
val size = bin.unpackUnsigned().toInt()
|
||||||
println("detected homogenous list type $type, $size items")
|
println("detected homogenous list type $type, $size items")
|
||||||
val list = mutableListOf<Obj>()
|
for (i in 0..<size) {
|
||||||
val objClass = type.objClass
|
|
||||||
for( i in 0 ..< size) {
|
|
||||||
list += decodeObject(scope, objClass, type).also {
|
list += decodeObject(scope, objClass, type).also {
|
||||||
println("decoded: $it")
|
println("decoded: $it")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list
|
list
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
val size = unpackUnsigned().toInt()
|
val size = unpackUnsigned().toInt()
|
||||||
(0..<size).map { decodeAny(scope) }.toMutableList()
|
(0..<size).map { decodeAny(scope) }.toMutableList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun decodeObject(scope: Scope, type: ObjClass,overrideType: LynonType?=null): Obj {
|
suspend fun decodeObject(scope: Scope, type: ObjClass, overrideType: LynonType? = null): Obj {
|
||||||
return decodeCached { type.deserialize(scope, this, overrideType) }
|
return decodeCached { type.deserialize(scope, this, overrideType) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,13 +85,16 @@ open class LynonEncoder(val bout: BitOutput, val settings: LynonSettings = Lynon
|
|||||||
*/
|
*/
|
||||||
suspend fun encodeAny(scope: Scope, obj: Obj) {
|
suspend fun encodeAny(scope: Scope, obj: Obj) {
|
||||||
encodeCached(obj) {
|
encodeCached(obj) {
|
||||||
val type = putTypeRecord(obj, obj.lynonType())
|
val type = putTypeRecord(scope, obj, obj.lynonType())
|
||||||
obj.serialize(scope, this, type)
|
obj.serialize(scope, this, type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun putTypeRecord(obj: Obj, type: LynonType): LynonType {
|
private suspend fun putTypeRecord(scope: Scope, obj: Obj, type: LynonType): LynonType {
|
||||||
putType(type)
|
putType(type)
|
||||||
|
if( type == LynonType.Other) {
|
||||||
|
encodeObject(scope, obj.objClass.classNameObj)
|
||||||
|
}
|
||||||
return type
|
return type
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +123,7 @@ open class LynonEncoder(val bout: BitOutput, val settings: LynonSettings = Lynon
|
|||||||
}
|
}
|
||||||
if (isHomogeneous) {
|
if (isHomogeneous) {
|
||||||
putBit(1)
|
putBit(1)
|
||||||
putTypeRecord(list[0], type)
|
putTypeRecord(scope, list[0], type)
|
||||||
encodeUnsigned(list.size.toULong())
|
encodeUnsigned(list.size.toULong())
|
||||||
for (i in list) encodeObject(scope, i, type)
|
for (i in list) encodeObject(scope, i, type)
|
||||||
} else {
|
} else {
|
||||||
|
@ -11,6 +11,7 @@ import java.nio.file.Path
|
|||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertContentEquals
|
import kotlin.test.assertContentEquals
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class LynonTests {
|
class LynonTests {
|
||||||
|
|
||||||
@ -56,6 +57,7 @@ class LynonTests {
|
|||||||
assertEquals(1, bin.getBit())
|
assertEquals(1, bin.getBit())
|
||||||
assertEquals(null, bin.getBitOrNull())
|
assertEquals(null, bin.getBitOrNull())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testBitOutputMedium() {
|
fun testBitOutputMedium() {
|
||||||
val bout = MemoryBitOutput()
|
val bout = MemoryBitOutput()
|
||||||
@ -63,8 +65,8 @@ class LynonTests {
|
|||||||
bout.putBit(1)
|
bout.putBit(1)
|
||||||
bout.putBit(0)
|
bout.putBit(0)
|
||||||
bout.putBit(1)
|
bout.putBit(1)
|
||||||
bout.putBits( 0, 7)
|
bout.putBits(0, 7)
|
||||||
bout.putBits( 3, 2)
|
bout.putBits(3, 2)
|
||||||
val x = bout.toBitArray()
|
val x = bout.toBitArray()
|
||||||
assertEquals(1, x[0])
|
assertEquals(1, x[0])
|
||||||
assertEquals(1, x[1])
|
assertEquals(1, x[1])
|
||||||
@ -243,6 +245,7 @@ class LynonTests {
|
|||||||
assertEquals(ObjReal(Double.MIN_VALUE), decoder.decodeObject(scope, ObjReal.type))
|
assertEquals(ObjReal(Double.MIN_VALUE), decoder.decodeObject(scope, ObjReal.type))
|
||||||
assertEquals(ObjReal(Double.MAX_VALUE), decoder.decodeObject(scope, ObjReal.type))
|
assertEquals(ObjReal(Double.MAX_VALUE), decoder.decodeObject(scope, ObjReal.type))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testUnpackInt() = runTest {
|
fun testUnpackInt() = runTest {
|
||||||
val scope = Scope()
|
val scope = Scope()
|
||||||
@ -284,15 +287,17 @@ class LynonTests {
|
|||||||
val original = Files.readString(Path.of("../sample_texts/dikkens_hard_times.txt"))
|
val original = Files.readString(Path.of("../sample_texts/dikkens_hard_times.txt"))
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testEncodeNullsAndInts() = runTest{
|
fun testEncodeNullsAndInts() = runTest {
|
||||||
testScope().eval("""
|
testScope().eval(
|
||||||
|
"""
|
||||||
testEncode(null)
|
testEncode(null)
|
||||||
testEncode(0)
|
testEncode(0)
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testBufferEncoderInterop() = runTest{
|
fun testBufferEncoderInterop() = runTest {
|
||||||
val bout = MemoryBitOutput()
|
val bout = MemoryBitOutput()
|
||||||
bout.putBits(0, 1)
|
bout.putBits(0, 1)
|
||||||
bout.putBits(1, 4)
|
bout.putBits(1, 4)
|
||||||
@ -302,7 +307,9 @@ class LynonTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun testScope() =
|
suspend fun testScope() =
|
||||||
Scope().apply { eval("""
|
Scope().apply {
|
||||||
|
eval(
|
||||||
|
"""
|
||||||
import lyng.serialization
|
import lyng.serialization
|
||||||
fun testEncode(value) {
|
fun testEncode(value) {
|
||||||
val encoded = Lynon.encode(value)
|
val encoded = Lynon.encode(value)
|
||||||
@ -310,21 +317,25 @@ class LynonTests {
|
|||||||
println("Encoded size %d: %s"(encoded.size, value))
|
println("Encoded size %d: %s"(encoded.size, value))
|
||||||
assertEquals( value, Lynon.decode(encoded) )
|
assertEquals( value, Lynon.decode(encoded) )
|
||||||
}
|
}
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testUnaryMinus() = runTest{
|
fun testUnaryMinus() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
assertEquals( -1 * π, 0 - π )
|
assertEquals( -1 * π, 0 - π )
|
||||||
assertEquals( -1 * π, -π )
|
assertEquals( -1 * π, -π )
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSimpleTypes() = runTest{
|
fun testSimpleTypes() = runTest {
|
||||||
testScope().eval("""
|
testScope().eval(
|
||||||
|
"""
|
||||||
testEncode(null)
|
testEncode(null)
|
||||||
testEncode(0)
|
testEncode(0)
|
||||||
testEncode(47)
|
testEncode(47)
|
||||||
@ -342,7 +353,8 @@ class LynonTests {
|
|||||||
testEncode("Hello, world".encodeUtf8())
|
testEncode("Hello, world".encodeUtf8())
|
||||||
testEncode("Hello, world")
|
testEncode("Hello, world")
|
||||||
|
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -383,7 +395,7 @@ class LynonTests {
|
|||||||
assertEquals("011101", a0.toString())
|
assertEquals("011101", a0.toString())
|
||||||
val bin = MemoryBitInput(MemoryBitOutput().apply { putBits(a0) })
|
val bin = MemoryBitInput(MemoryBitOutput().apply { putBits(a0) })
|
||||||
var result = TinyBits()
|
var result = TinyBits()
|
||||||
for( i in a0.indices) result = result.insertBit(bin.getBit())
|
for (i in a0.indices) result = result.insertBit(bin.getBit())
|
||||||
assertEquals(a0, result)
|
assertEquals(a0, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,8 +411,8 @@ class LynonTests {
|
|||||||
println("Huffman : ${huff.size}")
|
println("Huffman : ${huff.size}")
|
||||||
val lzwhuff = Huffman.compress(lzw, Huffman.byteAlphabet).bytes
|
val lzwhuff = Huffman.compress(lzw, Huffman.byteAlphabet).bytes
|
||||||
println("LZW+HUFF : ${lzwhuff.size}")
|
println("LZW+HUFF : ${lzwhuff.size}")
|
||||||
val compressed = Huffman.compress(x,Huffman.byteAlphabet)
|
val compressed = Huffman.compress(x, Huffman.byteAlphabet)
|
||||||
val decompressed = Huffman.decompress(compressed.toBitInput(),Huffman.byteAlphabet)
|
val decompressed = Huffman.decompress(compressed.toBitInput(), Huffman.byteAlphabet)
|
||||||
assertContentEquals(x, decompressed)
|
assertContentEquals(x, decompressed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,7 +432,7 @@ class LynonTests {
|
|||||||
|
|
||||||
override fun ordinalOf(value: LynonType): Int = value.ordinal
|
override fun ordinalOf(value: LynonType): Int = value.ordinal
|
||||||
}
|
}
|
||||||
for(code in Huffman.generateCanonicalCodes(frequencies, alphabet)) {
|
for (code in Huffman.generateCanonicalCodes(frequencies, alphabet)) {
|
||||||
println("${code?.bits}: ${code?.ordinal?.let { LynonType.entries[it] }}")
|
println("${code?.bits}: ${code?.ordinal?.let { LynonType.entries[it] }}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -428,19 +440,19 @@ class LynonTests {
|
|||||||
@Test
|
@Test
|
||||||
fun testBitListSmall() {
|
fun testBitListSmall() {
|
||||||
var t = TinyBits()
|
var t = TinyBits()
|
||||||
for( i in listOf(1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1) )
|
for (i in listOf(1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1))
|
||||||
t = t.insertBit(i)
|
t = t.insertBit(i)
|
||||||
assertEquals(1, t[0])
|
assertEquals(1, t[0])
|
||||||
assertEquals(1, t[1])
|
assertEquals(1, t[1])
|
||||||
assertEquals(0, t[2])
|
assertEquals(0, t[2])
|
||||||
assertEquals("1101000111101",t.toString())
|
assertEquals("1101000111101", t.toString())
|
||||||
t[0] = 0
|
t[0] = 0
|
||||||
t[1] = 0
|
t[1] = 0
|
||||||
t[2] = 1
|
t[2] = 1
|
||||||
assertEquals("0011000111101",t.toString())
|
assertEquals("0011000111101", t.toString())
|
||||||
t[12] = 0
|
t[12] = 0
|
||||||
t[11] = 1
|
t[11] = 1
|
||||||
assertEquals("0011000111110",t.toString())
|
assertEquals("0011000111110", t.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -449,10 +461,10 @@ class LynonTests {
|
|||||||
val bout = MemoryBitOutput()
|
val bout = MemoryBitOutput()
|
||||||
assertEquals("1101", bitListOf(1, 1, 0, 1).toString())
|
assertEquals("1101", bitListOf(1, 1, 0, 1).toString())
|
||||||
bout.putBits(bitListOf(1, 1, 0, 1))
|
bout.putBits(bitListOf(1, 1, 0, 1))
|
||||||
bout.putBits(bitListOf( 0, 0))
|
bout.putBits(bitListOf(0, 0))
|
||||||
bout.putBits(bitListOf( 0, 1, 1, 1, 1, 0, 1))
|
bout.putBits(bitListOf(0, 1, 1, 1, 1, 0, 1))
|
||||||
val x = bout.toBitArray()
|
val x = bout.toBitArray()
|
||||||
assertEquals("1101000111101",x.toString())
|
assertEquals("1101000111101", x.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -486,17 +498,43 @@ class LynonTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testIntList() = runTest {
|
fun testIntList() = runTest {
|
||||||
testScope().eval("""
|
testScope().eval(
|
||||||
// testEncode([1,2,3])
|
"""
|
||||||
// testEncode([-1,-2,-3])
|
testEncode([1,2,3])
|
||||||
// testEncode([1,-2,-3])
|
testEncode([-1,-2,-3])
|
||||||
// testEncode([0,1])
|
testEncode([1,-2,-3])
|
||||||
// testEncode([0,0,0])
|
testEncode([0,1])
|
||||||
// testEncode(["the", "the", "wall", "the", "wall", "wall"])
|
testEncode([0,0,0])
|
||||||
|
testEncode(["the", "the", "wall", "the", "wall", "wall"])
|
||||||
testEncode([1,2,3, "the", "wall", "wall"])
|
testEncode([1,2,3, "the", "wall", "wall"])
|
||||||
|
testEncode([false, false, false, true,true])
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testNamedObject() = runTest {
|
||||||
|
val s = testScope()
|
||||||
|
val x = s.eval("""5 => 6""")
|
||||||
|
assert(x is ObjMapEntry)
|
||||||
|
val bout = MemoryBitOutput()
|
||||||
|
val e = LynonEncoder(bout)
|
||||||
|
e.encodeAny(s, x)
|
||||||
|
val bin = bout.toBitInput()
|
||||||
|
val d = LynonDecoder(bin)
|
||||||
|
val x2 = d.decodeAny(s)
|
||||||
|
println(x)
|
||||||
|
println(x2)
|
||||||
|
assertTrue { x.compareTo(s, x2) == 0}
|
||||||
|
assertEquals(x, x2)
|
||||||
|
|
||||||
|
s.eval("""
|
||||||
|
testEncode( 1 => "one" )
|
||||||
|
testEncode( [1 => "one", 1 => "one"] )
|
||||||
|
testEncode( [1 => "one", 1 => "one", "foo" => "MapEntry"] )
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user