fix: lambda now have correct closure
This commit is contained in:
		
							parent
							
								
									306a7f26ef
								
							
						
					
					
						commit
						c0cf190452
					
				
							
								
								
									
										15
									
								
								docs/OOP.md
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								docs/OOP.md
									
									
									
									
									
								
							@ -70,3 +70,18 @@ Regular methods are called on instances as usual `instance.method()`. The method
 | 
			
		||||
1. this instance methods;
 | 
			
		||||
2. parents method: no guarantee but we enumerate parents in order of appearance;
 | 
			
		||||
3. possible extension methods (scoped)
 | 
			
		||||
 | 
			
		||||
# Defining a new class
 | 
			
		||||
 | 
			
		||||
The class is a some data record with named fields and fixed order, in fact. To define a class,
 | 
			
		||||
just Provide a name and a record like this:
 | 
			
		||||
 | 
			
		||||
    class Vec2(x,y)
 | 
			
		||||
 | 
			
		||||
This way, you have created a _constructor_, so calling `Vec2( 10, 20 )` would create an _instane_ of `Vec2` class:
 | 
			
		||||
 | 
			
		||||
    class Vec2(x,y)
 | 
			
		||||
    Vec2(10,20)
 | 
			
		||||
    >> eee
 | 
			
		||||
 | 
			
		||||
TBD
 | 
			
		||||
@ -47,7 +47,7 @@ One interesting way of using closure isolation is to keep state of the functions
 | 
			
		||||
    >>> 0
 | 
			
		||||
    >>> 1
 | 
			
		||||
    >>> 2
 | 
			
		||||
    >>> void
 | 
			
		||||
    >> void
 | 
			
		||||
 | 
			
		||||
Inner `counter` is not accessible from outside, no way; still it is kept 
 | 
			
		||||
between calls in the closure, as inner function `doit`, returned from the
 | 
			
		||||
@ -75,3 +75,15 @@ The example above could be rewritten using inner lambda, too:
 | 
			
		||||
    >>> 1
 | 
			
		||||
    >>> 2
 | 
			
		||||
    >>> void
 | 
			
		||||
 | 
			
		||||
Lambda functions remember their scopes, so it will work the same as previous:
 | 
			
		||||
 | 
			
		||||
    var counter = 200
 | 
			
		||||
    fun createLambda() {
 | 
			
		||||
        var counter = 0
 | 
			
		||||
        { counter += 1 }
 | 
			
		||||
    }
 | 
			
		||||
    val c = createLambda()
 | 
			
		||||
    println(c)
 | 
			
		||||
    >> 1
 | 
			
		||||
    >> void
 | 
			
		||||
							
								
								
									
										24
									
								
								docs/class_reference.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								docs/class_reference.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
# Classes
 | 
			
		||||
 | 
			
		||||
## Declaring
 | 
			
		||||
 | 
			
		||||
    class Foo1
 | 
			
		||||
    class Foo2() // same, empty constructor
 | 
			
		||||
    class Foo3() { // full
 | 
			
		||||
    }
 | 
			
		||||
    class Foo4 { // Only body
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
class_declaration = ["abstract",] "class" [, constructor] [, body]
 | 
			
		||||
constructor = "(", [field [, field]] ")
 | 
			
		||||
field = [visibility ,] [access ,] name [, typedecl]
 | 
			
		||||
body = [visibility] ("var", vardecl) | ("val", vardecl) | ("fun", fundecl)
 | 
			
		||||
visibility = "private" | "protected" | "internal"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Abstract classes
 | 
			
		||||
 | 
			
		||||
Contain one pr more abstract methods which must be implemented; though they
 | 
			
		||||
can have constructors, the instances of the abstract classes could not be
 | 
			
		||||
created independently
 | 
			
		||||
@ -326,9 +326,11 @@ class Compiler(
 | 
			
		||||
            throw ScriptError(startPos, "lambda must have either valid arguments declaration with '->' or no arguments")
 | 
			
		||||
        val pos = cc.currentPos()
 | 
			
		||||
        val body = parseBlock(cc, skipLeadingBrace = true)
 | 
			
		||||
        return Accessor { _ ->
 | 
			
		||||
            statement {
 | 
			
		||||
                val context = this.copy(pos)
 | 
			
		||||
 | 
			
		||||
        var closure: Context? = null
 | 
			
		||||
 | 
			
		||||
        val callStatement = statement {
 | 
			
		||||
            val context = closure!!.copy(pos, args)
 | 
			
		||||
            if (argsDeclaration == null) {
 | 
			
		||||
                // no args: automatic var 'it'
 | 
			
		||||
                val l = args.values
 | 
			
		||||
@ -360,7 +362,11 @@ class Compiler(
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            body.execute(context)
 | 
			
		||||
            }.asReadonly
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Accessor { x ->
 | 
			
		||||
            if( closure == null ) closure = x
 | 
			
		||||
            callStatement.asReadonly
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -402,12 +408,22 @@ class Compiler(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    enum class AccessType {
 | 
			
		||||
        Val, Var, Default
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    enum class Visibility {
 | 
			
		||||
        Default, Public, Private, Protected, Internal
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    data class ArgVar(
 | 
			
		||||
        val name: String,
 | 
			
		||||
        val type: TypeDecl = TypeDecl.Obj,
 | 
			
		||||
        val pos: Pos,
 | 
			
		||||
        val isEllipsis: Boolean,
 | 
			
		||||
        val initialValue: Statement? = null
 | 
			
		||||
        val initialValue: Statement? = null,
 | 
			
		||||
        val accessType: AccessType = AccessType.Default,
 | 
			
		||||
        val visibility: Visibility = Visibility.Default
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class ArgsDeclaration(val args: List<ArgVar>, val endTokenType: Token.Type) {
 | 
			
		||||
@ -421,16 +437,73 @@ class Compiler(
 | 
			
		||||
     * Parse argument declaration, used in lambda (and later in fn too)
 | 
			
		||||
     * @return declaration or null if there is no valid list of arguments
 | 
			
		||||
     */
 | 
			
		||||
    private fun parseArgsDeclaration(cc: CompilerContext): ArgsDeclaration? {
 | 
			
		||||
    private fun parseArgsDeclaration(cc: CompilerContext, isClassDeclaration: Boolean = false): ArgsDeclaration? {
 | 
			
		||||
        val result = mutableListOf<ArgVar>()
 | 
			
		||||
        var endTokenType: Token.Type? = null
 | 
			
		||||
        val startPos = cc.savePos()
 | 
			
		||||
 | 
			
		||||
        while (endTokenType == null) {
 | 
			
		||||
            val t = cc.next()
 | 
			
		||||
            var t = cc.next()
 | 
			
		||||
            when (t.type) {
 | 
			
		||||
                Token.Type.NEWLINE -> {}
 | 
			
		||||
                Token.Type.ID -> {
 | 
			
		||||
                    // visibility
 | 
			
		||||
                    val visibility = when (t.value) {
 | 
			
		||||
                        "private" -> {
 | 
			
		||||
                            if (!isClassDeclaration) {
 | 
			
		||||
                                cc.restorePos(startPos); return null
 | 
			
		||||
                            }
 | 
			
		||||
                            t = cc.next()
 | 
			
		||||
                            Visibility.Private
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        "protected" -> {
 | 
			
		||||
                            if (!isClassDeclaration) {
 | 
			
		||||
                                cc.restorePos(startPos); return null
 | 
			
		||||
                            }
 | 
			
		||||
                            t = cc.next()
 | 
			
		||||
                            Visibility.Protected
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        "internal" -> {
 | 
			
		||||
                            if (!isClassDeclaration) {
 | 
			
		||||
                                cc.restorePos(startPos); return null
 | 
			
		||||
                            }
 | 
			
		||||
                            t = cc.next()
 | 
			
		||||
                            Visibility.Internal
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        "public" -> {
 | 
			
		||||
                            if (!isClassDeclaration) {
 | 
			
		||||
                                cc.restorePos(startPos); return null
 | 
			
		||||
                            }
 | 
			
		||||
                            t = cc.next()
 | 
			
		||||
                            Visibility.Public
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        else -> Visibility.Default
 | 
			
		||||
                    }
 | 
			
		||||
                    // val/var?
 | 
			
		||||
                    val access = when (t.value) {
 | 
			
		||||
                        "val" -> {
 | 
			
		||||
                            if (!isClassDeclaration) {
 | 
			
		||||
                                cc.restorePos(startPos); return null
 | 
			
		||||
                            }
 | 
			
		||||
                            t = cc.next()
 | 
			
		||||
                            AccessType.Val
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        "var" -> {
 | 
			
		||||
                            if (!isClassDeclaration) {
 | 
			
		||||
                                cc.restorePos(startPos); return null
 | 
			
		||||
                            }
 | 
			
		||||
                            t = cc.next()
 | 
			
		||||
                            AccessType.Var
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        else -> AccessType.Default
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    var defaultValue: Statement? = null
 | 
			
		||||
                    cc.ifNextIs(Token.Type.ASSIGN) {
 | 
			
		||||
                        defaultValue = parseExpression(cc)
 | 
			
		||||
@ -438,7 +511,7 @@ class Compiler(
 | 
			
		||||
                    // type information
 | 
			
		||||
                    val typeInfo = parseTypeDeclaration(cc)
 | 
			
		||||
                    val isEllipsis = cc.skipTokenOfType(Token.Type.ELLIPSIS, isOptional = true)
 | 
			
		||||
                    result += ArgVar(t.value, typeInfo, t.pos, isEllipsis, defaultValue)
 | 
			
		||||
                    result += ArgVar(t.value, typeInfo, t.pos, isEllipsis, defaultValue, access, visibility)
 | 
			
		||||
 | 
			
		||||
                    // important: valid argument list continues with ',' and ends with '->' or ')'
 | 
			
		||||
                    // otherwise it is not an argument list:
 | 
			
		||||
@ -630,9 +703,36 @@ class Compiler(
 | 
			
		||||
        "continue" -> parseContinueStatement(id.pos, cc)
 | 
			
		||||
        "fn", "fun" -> parseFunctionDeclaration(cc)
 | 
			
		||||
        "if" -> parseIfStatement(cc)
 | 
			
		||||
        "class" -> parseClassDeclaration(cc, false)
 | 
			
		||||
        "struct" -> parseClassDeclaration(cc, true)
 | 
			
		||||
        else -> null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseClassDeclaration(cc: CompilerContext, isStruct: Boolean): Statement {
 | 
			
		||||
        val nameToken = cc.requireToken(Token.Type.ID)
 | 
			
		||||
        val parsedArgs = parseArgsDeclaration(cc)
 | 
			
		||||
        cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true)
 | 
			
		||||
        val t = cc.next()
 | 
			
		||||
        if (t.type == Token.Type.LBRACE) {
 | 
			
		||||
            // parse body
 | 
			
		||||
        }
 | 
			
		||||
        // create class
 | 
			
		||||
        val className = nameToken.value
 | 
			
		||||
 | 
			
		||||
//        val constructorCode = statement {
 | 
			
		||||
//            val classContext = copy()
 | 
			
		||||
//        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        val newClass = ObjClass(className, parsedArgs?.args ?: emptyList())
 | 
			
		||||
//        statement {
 | 
			
		||||
//            addConst(nameToken.value, )
 | 
			
		||||
//        }
 | 
			
		||||
//        }
 | 
			
		||||
        TODO()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private fun getLabel(cc: CompilerContext, maxDepth: Int = 2): String? {
 | 
			
		||||
        var cnt = 0
 | 
			
		||||
        var found: String? = null
 | 
			
		||||
@ -1185,7 +1285,8 @@ class Compiler(
 | 
			
		||||
            Operator.simple(Token.Type.NOTIS, lastPrty) { c, a, b -> ObjBool(!a.isInstanceOf(b)) },
 | 
			
		||||
            // shuttle <=> 6
 | 
			
		||||
            Operator.simple(Token.Type.SHUTTLE, ++lastPrty) { c, a, b ->
 | 
			
		||||
                ObjInt(a.compareTo(c, b).toLong()) },
 | 
			
		||||
                ObjInt(a.compareTo(c, b).toLong())
 | 
			
		||||
            },
 | 
			
		||||
            // bit shifts 7
 | 
			
		||||
            Operator.simple(Token.Type.PLUS, ++lastPrty) { ctx, a, b -> a.plus(ctx, b) },
 | 
			
		||||
            Operator.simple(Token.Type.MINUS, lastPrty) { ctx, a, b -> a.minus(ctx, b) },
 | 
			
		||||
@ -1213,7 +1314,7 @@ class Compiler(
 | 
			
		||||
        /**
 | 
			
		||||
         * The keywords that stop processing of expression term
 | 
			
		||||
         */
 | 
			
		||||
        val stopKeywords = setOf("break", "continue", "return", "if", "when", "do", "while", "for")
 | 
			
		||||
        val stopKeywords = setOf("break", "continue", "return", "if", "when", "do", "while", "for", "class", "struct")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,11 @@ import net.sergeych.synctools.ProtectedOp
 | 
			
		||||
 | 
			
		||||
//typealias InstanceMethod = (Context, Obj) -> Obj
 | 
			
		||||
 | 
			
		||||
data class WithAccess<T>(var value: T, val isMutable: Boolean)
 | 
			
		||||
data class WithAccess<T>(
 | 
			
		||||
    var value: T,
 | 
			
		||||
    val isMutable: Boolean,
 | 
			
		||||
    val visibility: Compiler.Visibility = Compiler.Visibility.Public
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
data class Accessor(
 | 
			
		||||
    val getter: suspend (Context) -> WithAccess<Obj>,
 | 
			
		||||
 | 
			
		||||
@ -4,8 +4,14 @@ val ObjClassType by lazy { ObjClass("Class") }
 | 
			
		||||
 | 
			
		||||
class ObjClass(
 | 
			
		||||
    val className: String,
 | 
			
		||||
    val constructorArgs: List<Compiler.ArgVar> = emptyList(),
 | 
			
		||||
    vararg val parents: ObjClass,
 | 
			
		||||
) : Obj() {
 | 
			
		||||
    constructor(
 | 
			
		||||
        className: String,
 | 
			
		||||
        vararg parents: ObjClass,
 | 
			
		||||
    ) : this(className, emptyList(), *parents)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    val allParentsSet: Set<ObjClass> = parents.flatMap {
 | 
			
		||||
        listOf(it) + it.allParentsSet
 | 
			
		||||
@ -70,7 +76,8 @@ class ObjClass(
 | 
			
		||||
/**
 | 
			
		||||
 * Abstract class that must provide `iterator` method that returns [ObjIterator] instance.
 | 
			
		||||
 */
 | 
			
		||||
val ObjIterable by lazy { ObjClass("Iterable").apply {
 | 
			
		||||
val ObjIterable by lazy {
 | 
			
		||||
    ObjClass("Iterable").apply {
 | 
			
		||||
 | 
			
		||||
        addFn("toList") {
 | 
			
		||||
            val result = mutableListOf<Obj>()
 | 
			
		||||
@ -87,7 +94,8 @@ val ObjIterable by lazy { ObjClass("Iterable").apply {
 | 
			
		||||
            ObjList(result)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
} }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Collection is an iterator with `size`]
 | 
			
		||||
@ -145,3 +153,7 @@ val ObjArray by lazy {
 | 
			
		||||
        addFn("isample") { "ok".toObj() }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class ObjInstance(override val objClass: ObjClass): Obj() {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1167,6 +1167,7 @@ class ScriptTest {
 | 
			
		||||
        eval(
 | 
			
		||||
            """
 | 
			
		||||
            fun mapValues(iterable, transform) {
 | 
			
		||||
                println("start: ", transform)
 | 
			
		||||
                var result = []
 | 
			
		||||
                for( x in iterable ) result += transform(x)
 | 
			
		||||
            }
 | 
			
		||||
@ -1238,6 +1239,17 @@ class ScriptTest {
 | 
			
		||||
            assert( 5 <=> 7 < 0 )
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun simpleClassDelaration() = runTest {
 | 
			
		||||
        eval( """
 | 
			
		||||
//            class Vec2(x,y)
 | 
			
		||||
//             println(Vec2(1,2)::class)
 | 
			
		||||
            println("---------------------")
 | 
			
		||||
            println(Int::class)
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -8,6 +8,7 @@ import net.sergeych.lyng.ObjVoid
 | 
			
		||||
import java.nio.file.Files
 | 
			
		||||
import java.nio.file.Files.readAllLines
 | 
			
		||||
import java.nio.file.Paths
 | 
			
		||||
import kotlin.io.path.absolutePathString
 | 
			
		||||
import kotlin.io.path.extension
 | 
			
		||||
import kotlin.test.Test
 | 
			
		||||
import kotlin.test.assertEquals
 | 
			
		||||
@ -41,7 +42,8 @@ data class DocTest(
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String {
 | 
			
		||||
        return "DocTest:$fileName:${line + 1}..${line + sourceLines.size}"
 | 
			
		||||
        val absPath = Paths.get(fileName).absolutePathString()
 | 
			
		||||
        return "DocTest: $absPath:${line + 1}"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val detailedString by lazy {
 | 
			
		||||
@ -190,7 +192,7 @@ suspend fun DocTest.test(context: Context = Context()) {
 | 
			
		||||
        if (error != null || expectedOutput != collectedOutput.toString() ||
 | 
			
		||||
            expectedResult != result
 | 
			
		||||
        ) {
 | 
			
		||||
            println("Test failed: ${this.detailedString}")
 | 
			
		||||
            System.err.println("\nfailed: ${this.detailedString}")
 | 
			
		||||
        }
 | 
			
		||||
        error?.let {
 | 
			
		||||
            fail("test failed", it)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user