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,
 | 
			
		||||
) : Obj() {
 | 
			
		||||
 | 
			
		||||
    val classNameObj by lazy { ObjString(className) }
 | 
			
		||||
 | 
			
		||||
    var constructorMeta: ArgsDeclaration? = 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()
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,9 @@ package net.sergeych.lyng.obj
 | 
			
		||||
 | 
			
		||||
import net.sergeych.lyng.Scope
 | 
			
		||||
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() {
 | 
			
		||||
 | 
			
		||||
@ -12,6 +15,14 @@ class ObjMapEntry(val key: Obj, val value: Obj) : Obj() {
 | 
			
		||||
        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()) {
 | 
			
		||||
        0 -> key
 | 
			
		||||
        1 -> value
 | 
			
		||||
@ -24,11 +35,23 @@ class ObjMapEntry(val key: Obj, val value: Obj) : Obj() {
 | 
			
		||||
 | 
			
		||||
    override val objClass = type
 | 
			
		||||
 | 
			
		||||
    override suspend fun serialize(scope: Scope, encoder: LynonEncoder, lynonType: LynonType?) {
 | 
			
		||||
        encoder.encodeAny(scope,key)
 | 
			
		||||
        encoder.encodeAny(scope,value)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        val type = object : ObjClass("MapEntry", ObjArray) {
 | 
			
		||||
            override suspend fun callOn(scope: Scope): Obj {
 | 
			
		||||
                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 {
 | 
			
		||||
            addFn("key") { thisAs<ObjMapEntry>().key }
 | 
			
		||||
            addFn("value") { thisAs<ObjMapEntry>().value }
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@ package net.sergeych.lynon
 | 
			
		||||
import net.sergeych.lyng.Scope
 | 
			
		||||
import net.sergeych.lyng.obj.Obj
 | 
			
		||||
import net.sergeych.lyng.obj.ObjClass
 | 
			
		||||
import net.sergeych.lyng.obj.ObjString
 | 
			
		||||
 | 
			
		||||
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>()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
            // unpack and cache
 | 
			
		||||
            f().also {
 | 
			
		||||
@ -38,32 +39,47 @@ open class LynonDecoder(val bin: BitInput, val settings: LynonSettings = LynonSe
 | 
			
		||||
 | 
			
		||||
    suspend fun decodeAny(scope: Scope): Obj = decodeCached {
 | 
			
		||||
        val type = LynonType.entries[bin.getBits(4).toInt()]
 | 
			
		||||
        type.objClass.deserialize(scope, this, type)
 | 
			
		||||
        if (type != LynonType.Other) {
 | 
			
		||||
            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> {
 | 
			
		||||
        return if( bin.getBit() == 1) {
 | 
			
		||||
        return if (bin.getBit() == 1) {
 | 
			
		||||
            // homogenous
 | 
			
		||||
            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()
 | 
			
		||||
            println("detected homogenous list type $type, $size items")
 | 
			
		||||
            val list = mutableListOf<Obj>()
 | 
			
		||||
            val objClass = type.objClass
 | 
			
		||||
            for( i in 0 ..< size) {
 | 
			
		||||
            for (i in 0..<size) {
 | 
			
		||||
                list += decodeObject(scope, objClass, type).also {
 | 
			
		||||
                        println("decoded: $it")
 | 
			
		||||
                    println("decoded: $it")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            list
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
        } else {
 | 
			
		||||
            val size = unpackUnsigned().toInt()
 | 
			
		||||
            (0..<size).map { decodeAny(scope) }.toMutableList()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun decodeObject(scope: Scope, type: ObjClass,overrideType: LynonType?=null): Obj {
 | 
			
		||||
        return decodeCached {  type.deserialize(scope, this, overrideType) }
 | 
			
		||||
    suspend fun decodeObject(scope: Scope, type: ObjClass, overrideType: LynonType? = null): Obj {
 | 
			
		||||
        return decodeCached { type.deserialize(scope, this, overrideType) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun unpackBinaryData(): ByteArray = bin.decompress()
 | 
			
		||||
 | 
			
		||||
@ -85,13 +85,16 @@ open class LynonEncoder(val bout: BitOutput, val settings: LynonSettings = Lynon
 | 
			
		||||
     */
 | 
			
		||||
    suspend fun encodeAny(scope: Scope, obj: Obj) {
 | 
			
		||||
        encodeCached(obj) {
 | 
			
		||||
            val type = putTypeRecord(obj, obj.lynonType())
 | 
			
		||||
            val type = putTypeRecord(scope, obj, obj.lynonType())
 | 
			
		||||
            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)
 | 
			
		||||
        if( type == LynonType.Other) {
 | 
			
		||||
            encodeObject(scope, obj.objClass.classNameObj)
 | 
			
		||||
        }
 | 
			
		||||
        return type
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -120,7 +123,7 @@ open class LynonEncoder(val bout: BitOutput, val settings: LynonSettings = Lynon
 | 
			
		||||
            }
 | 
			
		||||
        if (isHomogeneous) {
 | 
			
		||||
            putBit(1)
 | 
			
		||||
            putTypeRecord(list[0], type)
 | 
			
		||||
            putTypeRecord(scope, list[0], type)
 | 
			
		||||
            encodeUnsigned(list.size.toULong())
 | 
			
		||||
            for (i in list) encodeObject(scope, i, type)
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,7 @@ import java.nio.file.Path
 | 
			
		||||
import kotlin.test.Test
 | 
			
		||||
import kotlin.test.assertContentEquals
 | 
			
		||||
import kotlin.test.assertEquals
 | 
			
		||||
import kotlin.test.assertTrue
 | 
			
		||||
 | 
			
		||||
class LynonTests {
 | 
			
		||||
 | 
			
		||||
@ -56,6 +57,7 @@ class LynonTests {
 | 
			
		||||
        assertEquals(1, bin.getBit())
 | 
			
		||||
        assertEquals(null, bin.getBitOrNull())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testBitOutputMedium() {
 | 
			
		||||
        val bout = MemoryBitOutput()
 | 
			
		||||
@ -63,8 +65,8 @@ class LynonTests {
 | 
			
		||||
        bout.putBit(1)
 | 
			
		||||
        bout.putBit(0)
 | 
			
		||||
        bout.putBit(1)
 | 
			
		||||
        bout.putBits( 0, 7)
 | 
			
		||||
        bout.putBits( 3, 2)
 | 
			
		||||
        bout.putBits(0, 7)
 | 
			
		||||
        bout.putBits(3, 2)
 | 
			
		||||
        val x = bout.toBitArray()
 | 
			
		||||
        assertEquals(1, x[0])
 | 
			
		||||
        assertEquals(1, x[1])
 | 
			
		||||
@ -243,6 +245,7 @@ class LynonTests {
 | 
			
		||||
        assertEquals(ObjReal(Double.MIN_VALUE), decoder.decodeObject(scope, ObjReal.type))
 | 
			
		||||
        assertEquals(ObjReal(Double.MAX_VALUE), decoder.decodeObject(scope, ObjReal.type))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testUnpackInt() = runTest {
 | 
			
		||||
        val scope = Scope()
 | 
			
		||||
@ -284,15 +287,17 @@ class LynonTests {
 | 
			
		||||
    val original = Files.readString(Path.of("../sample_texts/dikkens_hard_times.txt"))
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testEncodeNullsAndInts() = runTest{
 | 
			
		||||
        testScope().eval("""
 | 
			
		||||
    fun testEncodeNullsAndInts() = runTest {
 | 
			
		||||
        testScope().eval(
 | 
			
		||||
            """
 | 
			
		||||
            testEncode(null)
 | 
			
		||||
            testEncode(0)
 | 
			
		||||
        """.trimIndent())
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testBufferEncoderInterop() = runTest{
 | 
			
		||||
    fun testBufferEncoderInterop() = runTest {
 | 
			
		||||
        val bout = MemoryBitOutput()
 | 
			
		||||
        bout.putBits(0, 1)
 | 
			
		||||
        bout.putBits(1, 4)
 | 
			
		||||
@ -302,7 +307,9 @@ class LynonTests {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun testScope() =
 | 
			
		||||
        Scope().apply { eval("""
 | 
			
		||||
        Scope().apply {
 | 
			
		||||
            eval(
 | 
			
		||||
                """
 | 
			
		||||
            import lyng.serialization
 | 
			
		||||
            fun testEncode(value) {
 | 
			
		||||
                val encoded = Lynon.encode(value)
 | 
			
		||||
@ -310,21 +317,25 @@ class LynonTests {
 | 
			
		||||
                println("Encoded size %d: %s"(encoded.size, value))
 | 
			
		||||
                assertEquals( value, Lynon.decode(encoded) )
 | 
			
		||||
            }
 | 
			
		||||
            """.trimIndent())
 | 
			
		||||
            """.trimIndent()
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testUnaryMinus() = runTest{
 | 
			
		||||
        eval("""
 | 
			
		||||
    fun testUnaryMinus() = runTest {
 | 
			
		||||
        eval(
 | 
			
		||||
            """
 | 
			
		||||
            assertEquals( -1 * π, 0 - π )
 | 
			
		||||
            assertEquals( -1 * π, -π )
 | 
			
		||||
            """.trimIndent())
 | 
			
		||||
            """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testSimpleTypes() = runTest{
 | 
			
		||||
        testScope().eval("""
 | 
			
		||||
    fun testSimpleTypes() = runTest {
 | 
			
		||||
        testScope().eval(
 | 
			
		||||
            """
 | 
			
		||||
            testEncode(null)
 | 
			
		||||
            testEncode(0)
 | 
			
		||||
            testEncode(47)
 | 
			
		||||
@ -342,7 +353,8 @@ class LynonTests {
 | 
			
		||||
            testEncode("Hello, world".encodeUtf8())
 | 
			
		||||
            testEncode("Hello, world")
 | 
			
		||||
            
 | 
			
		||||
        """.trimIndent())
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
@ -383,7 +395,7 @@ class LynonTests {
 | 
			
		||||
        assertEquals("011101", a0.toString())
 | 
			
		||||
        val bin = MemoryBitInput(MemoryBitOutput().apply { putBits(a0) })
 | 
			
		||||
        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)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -399,8 +411,8 @@ class LynonTests {
 | 
			
		||||
        println("Huffman  : ${huff.size}")
 | 
			
		||||
        val lzwhuff = Huffman.compress(lzw, Huffman.byteAlphabet).bytes
 | 
			
		||||
        println("LZW+HUFF : ${lzwhuff.size}")
 | 
			
		||||
        val compressed = Huffman.compress(x,Huffman.byteAlphabet)
 | 
			
		||||
        val decompressed = Huffman.decompress(compressed.toBitInput(),Huffman.byteAlphabet)
 | 
			
		||||
        val compressed = Huffman.compress(x, Huffman.byteAlphabet)
 | 
			
		||||
        val decompressed = Huffman.decompress(compressed.toBitInput(), Huffman.byteAlphabet)
 | 
			
		||||
        assertContentEquals(x, decompressed)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -420,7 +432,7 @@ class LynonTests {
 | 
			
		||||
 | 
			
		||||
            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] }}")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -428,19 +440,19 @@ class LynonTests {
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testBitListSmall() {
 | 
			
		||||
        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)
 | 
			
		||||
        assertEquals(1, t[0])
 | 
			
		||||
        assertEquals(1, t[1])
 | 
			
		||||
        assertEquals(0, t[2])
 | 
			
		||||
        assertEquals("1101000111101",t.toString())
 | 
			
		||||
        assertEquals("1101000111101", t.toString())
 | 
			
		||||
        t[0] = 0
 | 
			
		||||
        t[1] = 0
 | 
			
		||||
        t[2] = 1
 | 
			
		||||
        assertEquals("0011000111101",t.toString())
 | 
			
		||||
        assertEquals("0011000111101", t.toString())
 | 
			
		||||
        t[12] = 0
 | 
			
		||||
        t[11] = 1
 | 
			
		||||
        assertEquals("0011000111110",t.toString())
 | 
			
		||||
        assertEquals("0011000111110", t.toString())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
@ -449,10 +461,10 @@ class LynonTests {
 | 
			
		||||
        val bout = MemoryBitOutput()
 | 
			
		||||
        assertEquals("1101", bitListOf(1, 1, 0, 1).toString())
 | 
			
		||||
        bout.putBits(bitListOf(1, 1, 0, 1))
 | 
			
		||||
        bout.putBits(bitListOf( 0, 0))
 | 
			
		||||
        bout.putBits(bitListOf( 0, 1, 1, 1, 1, 0, 1))
 | 
			
		||||
        bout.putBits(bitListOf(0, 0))
 | 
			
		||||
        bout.putBits(bitListOf(0, 1, 1, 1, 1, 0, 1))
 | 
			
		||||
        val x = bout.toBitArray()
 | 
			
		||||
        assertEquals("1101000111101",x.toString())
 | 
			
		||||
        assertEquals("1101000111101", x.toString())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -486,17 +498,43 @@ class LynonTests {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testIntList() = runTest {
 | 
			
		||||
        testScope().eval("""
 | 
			
		||||
//            testEncode([1,2,3])
 | 
			
		||||
//            testEncode([-1,-2,-3])
 | 
			
		||||
//            testEncode([1,-2,-3])
 | 
			
		||||
//            testEncode([0,1])
 | 
			
		||||
//            testEncode([0,0,0])
 | 
			
		||||
//       testEncode(["the", "the", "wall", "the", "wall", "wall"])
 | 
			
		||||
       testEncode([1,2,3, "the", "wall", "wall"])
 | 
			
		||||
            
 | 
			
		||||
        """.trimIndent())
 | 
			
		||||
        testScope().eval(
 | 
			
		||||
            """
 | 
			
		||||
            testEncode([1,2,3])
 | 
			
		||||
            testEncode([-1,-2,-3])
 | 
			
		||||
            testEncode([1,-2,-3])
 | 
			
		||||
            testEncode([0,1])
 | 
			
		||||
            testEncode([0,0,0])
 | 
			
		||||
            testEncode(["the", "the", "wall", "the", "wall", "wall"])
 | 
			
		||||
            testEncode([1,2,3, "the", "wall", "wall"])
 | 
			
		||||
            testEncode([false, false, false, true,true])
 | 
			
		||||
        """.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