smple enums w/serialization
This commit is contained in:
		
							parent
							
								
									84e345b04e
								
							
						
					
					
						commit
						b5e89c7e78
					
				@ -475,7 +475,7 @@ Lyng has built-in mutable array class `List` with simple literals:
 | 
			
		||||
    [1, "two", 3.33].size
 | 
			
		||||
    >>> 3
 | 
			
		||||
 | 
			
		||||
[List] is an implementation of the type `Array`, and through it `Collection` and [Iterable].
 | 
			
		||||
[List] is an implementation of the type `Array`, and through it `Collection` and [Iterable]. Please read [Iterable], many collection based methods are implemented there.
 | 
			
		||||
 | 
			
		||||
Lists can contain any type of objects, lists too:
 | 
			
		||||
 | 
			
		||||
@ -1120,6 +1120,25 @@ These should be imported from [lyng.time](time.md). For example:
 | 
			
		||||
 | 
			
		||||
See [more docs on time manipulation](time.md)
 | 
			
		||||
 | 
			
		||||
# Enums
 | 
			
		||||
 | 
			
		||||
For the moment, only simple enums are implemented. Enum is a list of constants, represented also by their _ordinal_ - [Int] value.
 | 
			
		||||
 | 
			
		||||
    enum Color {
 | 
			
		||||
        RED, GREEN, BLUE
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    assert( Color.RED is Color )
 | 
			
		||||
 | 
			
		||||
    assertEquals( 2, Color.BLUE.ordinal )
 | 
			
		||||
    assertEquals( "BLUE", Color.BLUE.name )
 | 
			
		||||
    
 | 
			
		||||
    assertEquals( [Color.RED,Color.GREEN,Color.BLUE], Color.entries)
 | 
			
		||||
    assertEquals( Color.valueOf("GREEN"), Color.GREEN )
 | 
			
		||||
    >>> void
 | 
			
		||||
 | 
			
		||||
Enums are serialized as ordinals. Please note that due to caching, serialized string arrays could be even more compact than enum arrays, until `Lynon.encodeTyped` will be implemented.
 | 
			
		||||
 | 
			
		||||
# Comments
 | 
			
		||||
 | 
			
		||||
    // single line comment
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
 | 
			
		||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
 | 
			
		||||
 | 
			
		||||
group = "net.sergeych"
 | 
			
		||||
version = "0.8.7-SNAPSHOT"
 | 
			
		||||
version = "0.8.8-SNAPSHOT"
 | 
			
		||||
 | 
			
		||||
buildscript {
 | 
			
		||||
    repositories {
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
package net.sergeych.lyng
 | 
			
		||||
 | 
			
		||||
import ObjEnumClass
 | 
			
		||||
import net.sergeych.lyng.obj.*
 | 
			
		||||
import net.sergeych.lyng.pacman.ImportProvider
 | 
			
		||||
 | 
			
		||||
@ -858,7 +859,8 @@ class Compiler(
 | 
			
		||||
        "break" -> parseBreakStatement(id.pos)
 | 
			
		||||
        "continue" -> parseContinueStatement(id.pos)
 | 
			
		||||
        "if" -> parseIfStatement()
 | 
			
		||||
        "class" -> parseClassDeclaration(false)
 | 
			
		||||
        "class" -> parseClassDeclaration()
 | 
			
		||||
        "enum" -> parseEnumDeclaration()
 | 
			
		||||
        "try" -> parseTryStatement()
 | 
			
		||||
        "throw" -> parseThrowStatement()
 | 
			
		||||
        "when" -> parseWhenStatement()
 | 
			
		||||
@ -1145,7 +1147,40 @@ class Compiler(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private suspend fun parseClassDeclaration(isStruct: Boolean): Statement {
 | 
			
		||||
    private fun parseEnumDeclaration(): Statement {
 | 
			
		||||
        val nameToken = cc.requireToken(Token.Type.ID)
 | 
			
		||||
        // so far only simplest enums:
 | 
			
		||||
        val names = mutableListOf<String>()
 | 
			
		||||
        // skip '{'
 | 
			
		||||
        cc.skipTokenOfType(Token.Type.LBRACE)
 | 
			
		||||
 | 
			
		||||
        do {
 | 
			
		||||
            val t = cc.skipWsTokens()
 | 
			
		||||
            when(t.type) {
 | 
			
		||||
                Token.Type.ID -> {
 | 
			
		||||
                    names += t.value
 | 
			
		||||
                    val t1 = cc.skipWsTokens()
 | 
			
		||||
                    when(t1.type) {
 | 
			
		||||
                        Token.Type.COMMA ->
 | 
			
		||||
                            continue
 | 
			
		||||
                        Token.Type.RBRACE -> break
 | 
			
		||||
                        else -> {
 | 
			
		||||
                            t1.raiseSyntax("unexpected token")
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else -> t.raiseSyntax("expected enum entry name")
 | 
			
		||||
            }
 | 
			
		||||
        } while(true)
 | 
			
		||||
 | 
			
		||||
        return statement {
 | 
			
		||||
            ObjEnumClass.createSimpleEnum(nameToken.value, names).also {
 | 
			
		||||
                addItem(nameToken.value, false, it, recordType = ObjRecord.Type.Enum)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private suspend fun parseClassDeclaration(): Statement {
 | 
			
		||||
        val nameToken = cc.requireToken(Token.Type.ID)
 | 
			
		||||
        val constructorArgsDeclaration =
 | 
			
		||||
            if (cc.skipTokenOfType(Token.Type.LPAREN, isOptional = true))
 | 
			
		||||
@ -1178,8 +1213,8 @@ class Compiler(
 | 
			
		||||
        // create class
 | 
			
		||||
        val className = nameToken.value
 | 
			
		||||
 | 
			
		||||
        @Suppress("UNUSED_VARIABLE") val defaultAccess = if (isStruct) AccessType.Var else AccessType.Initialization
 | 
			
		||||
        @Suppress("UNUSED_VARIABLE") val defaultVisibility = Visibility.Public
 | 
			
		||||
//        @Suppress("UNUSED_VARIABLE") val defaultAccess = if (isStruct) AccessType.Var else AccessType.Initialization
 | 
			
		||||
//        @Suppress("UNUSED_VARIABLE") val defaultVisibility = Visibility.Public
 | 
			
		||||
 | 
			
		||||
        // create instance constructor
 | 
			
		||||
        // create custom objClass with all fields and instance constructor
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,10 @@
 | 
			
		||||
package net.sergeych.lyng
 | 
			
		||||
 | 
			
		||||
data class Token(val value: String, val pos: Pos, val type: Type) {
 | 
			
		||||
    fun raiseSyntax(text: String): Nothing {
 | 
			
		||||
        throw ScriptError(pos, text)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val isComment: Boolean by lazy { type == Type.SINLGE_LINE_COMMENT || type == Type.MULTILINE_COMMENT }
 | 
			
		||||
 | 
			
		||||
    @Suppress("unused")
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,62 @@
 | 
			
		||||
import net.sergeych.lyng.Scope
 | 
			
		||||
import net.sergeych.lyng.obj.*
 | 
			
		||||
import net.sergeych.lynon.LynonDecoder
 | 
			
		||||
import net.sergeych.lynon.LynonEncoder
 | 
			
		||||
import net.sergeych.lynon.LynonType
 | 
			
		||||
 | 
			
		||||
open class ObjEnumEntry(enumClass: ObjEnumClass, val name: ObjString, val ordinal: ObjInt) : Obj() {
 | 
			
		||||
    override val objClass = enumClass
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String {
 | 
			
		||||
        return "$objClass.$name"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun serialize(scope: Scope, encoder: LynonEncoder, lynonType: LynonType?) {
 | 
			
		||||
        encoder.encodeUnsigned(ordinal.value.toULong())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun compareTo(scope: Scope, other: Obj): Int {
 | 
			
		||||
        if( other !is ObjEnumEntry) return -2
 | 
			
		||||
        if( other.objClass != objClass ) return -2
 | 
			
		||||
        return ordinal.compareTo(scope, other.ordinal)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
object EnumBase : ObjClass("Enum") {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class ObjEnumClass(val name: String) : ObjClass(name, EnumBase) {
 | 
			
		||||
    val objEntries = ObjList()
 | 
			
		||||
    val byName by lazy { objEntries.list.associateBy { (it as ObjEnumEntry).name } }
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        addClassConst("entries", objEntries )
 | 
			
		||||
        addClassFn("valueOf") {
 | 
			
		||||
            val name = requireOnlyArg<ObjString>()
 | 
			
		||||
            byName[name] ?: raiseSymbolNotFound("does not exists: enum ${className}.$name")
 | 
			
		||||
        }
 | 
			
		||||
        addFn("name") { thisAs<ObjEnumEntry>().name }
 | 
			
		||||
        addFn("ordinal") { thisAs<ObjEnumEntry>().ordinal }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj {
 | 
			
		||||
        val index = decoder.unpackUnsigned().toInt()
 | 
			
		||||
        return objEntries.list[index]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        fun createSimpleEnum(enumName: String, names: List<String>): ObjEnumClass {
 | 
			
		||||
            val klass = ObjEnumClass(enumName)
 | 
			
		||||
            names.forEachIndexed { index, name ->
 | 
			
		||||
                val entry = ObjEnumEntry(klass, ObjString(name), ObjInt(index.toLong(), isConst = true))
 | 
			
		||||
                klass.objEntries.list += entry
 | 
			
		||||
                klass.addClassConst(name, entry)
 | 
			
		||||
            }
 | 
			
		||||
            return klass
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -19,6 +19,9 @@ data class ObjRecord(
 | 
			
		||||
        @Suppress("unused")
 | 
			
		||||
        Fun,
 | 
			
		||||
        ConstructorField(true, true),
 | 
			
		||||
        @Suppress("unused")
 | 
			
		||||
        Class,
 | 
			
		||||
        Enum,
 | 
			
		||||
        Other
 | 
			
		||||
    }
 | 
			
		||||
    @Suppress("unused")
 | 
			
		||||
 | 
			
		||||
@ -2855,4 +2855,45 @@ class ScriptTest {
 | 
			
		||||
        """.trimIndent())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun enumTest() = runTest {
 | 
			
		||||
        eval(
 | 
			
		||||
            """
 | 
			
		||||
                enum Color {
 | 
			
		||||
                    RED, GREEN, BLUE
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                assert( Color.RED is Color )
 | 
			
		||||
                assertEquals( 2, Color.BLUE.ordinal )
 | 
			
		||||
                assertEquals( "BLUE", Color.BLUE.name )
 | 
			
		||||
 | 
			
		||||
                assertEquals( [Color.RED,Color.GREEN,Color.BLUE], Color.entries)
 | 
			
		||||
 | 
			
		||||
                assertEquals( Color.valueOf("GREEN"), Color.GREEN )
 | 
			
		||||
 | 
			
		||||
                
 | 
			
		||||
                """.trimIndent())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun enumSerializationTest() = runTest {
 | 
			
		||||
        eval("""
 | 
			
		||||
            import lyng.serialization
 | 
			
		||||
            
 | 
			
		||||
            enum Color {
 | 
			
		||||
                RED, GREEN, BLUE
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            val e = Lynon.encode(Color.BLUE)
 | 
			
		||||
            assertEquals( Color.BLUE, Lynon.decode(e) )
 | 
			
		||||
            println(e.toDump())
 | 
			
		||||
            
 | 
			
		||||
            val e1 = Lynon.encode( (1..100).map { Color.GREEN } )
 | 
			
		||||
            println(e1.toDump())
 | 
			
		||||
            println(Lynon.encode( (1..100).map { "RED" } ).toDump() )
 | 
			
		||||
            
 | 
			
		||||
        """.trimIndent())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user