fixed many bugs with variable visibility in mixed scopes
This commit is contained in:
parent
eca746b189
commit
464a6dcb99
14
docs/development/scope_resolution.md
Normal file
14
docs/development/scope_resolution.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
|
||||||
|
Provide:
|
||||||
|
|
||||||
|
|
||||||
|
fun outer(a1)
|
||||||
|
// a1 is caller.a1:arg
|
||||||
|
val a1_local = a1 + 1
|
||||||
|
// we return lambda:
|
||||||
|
{ it ->
|
||||||
|
// a1_local
|
||||||
|
a1_lcoal + it
|
||||||
|
}
|
||||||
|
}
|
@ -21,7 +21,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
|||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
group = "net.sergeych"
|
group = "net.sergeych"
|
||||||
version = "0.8.10-SNAPSHOT"
|
version = "0.8.11-SNAPSHOT"
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -48,13 +48,12 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
|||||||
arguments: Arguments = scope.args,
|
arguments: Arguments = scope.args,
|
||||||
defaultAccessType: AccessType = AccessType.Var,
|
defaultAccessType: AccessType = AccessType.Var,
|
||||||
defaultVisibility: Visibility = Visibility.Public,
|
defaultVisibility: Visibility = Visibility.Public,
|
||||||
defaultRecordType: ObjRecord.Type = ObjRecord.Type.ConstructorField
|
|
||||||
) {
|
) {
|
||||||
fun assign(a: Item, value: Obj) {
|
fun assign(a: Item, value: Obj) {
|
||||||
scope.addItem(a.name, (a.accessType ?: defaultAccessType).isMutable,
|
scope.addItem(a.name, (a.accessType ?: defaultAccessType).isMutable,
|
||||||
value.byValueCopy(),
|
value.byValueCopy(),
|
||||||
a.visibility ?: defaultVisibility,
|
a.visibility ?: defaultVisibility,
|
||||||
recordType = defaultRecordType)
|
recordType = ObjRecord.Type.Argument)
|
||||||
}
|
}
|
||||||
|
|
||||||
// will be used with last lambda arg fix
|
// will be used with last lambda arg fix
|
||||||
|
@ -24,10 +24,33 @@ import net.sergeych.lyng.obj.ObjRecord
|
|||||||
* Inherits [Scope.args] and [Scope.thisObj] from [callScope] and adds lookup for symbols
|
* Inherits [Scope.args] and [Scope.thisObj] from [callScope] and adds lookup for symbols
|
||||||
* from [closureScope] with proper precedence
|
* from [closureScope] with proper precedence
|
||||||
*/
|
*/
|
||||||
class ClosureScope(val callScope: Scope,val closureScope: Scope) : Scope(callScope, callScope.args, thisObj = callScope.thisObj) {
|
class ClosureScope(val callScope: Scope, val closureScope: Scope) :
|
||||||
|
Scope(callScope, callScope.args, thisObj = callScope.thisObj) {
|
||||||
|
|
||||||
override fun get(name: String): ObjRecord? {
|
override fun get(name: String): ObjRecord? {
|
||||||
// closure should be treated below callScope
|
// we take arguments from the callerScope, the rest
|
||||||
return super.get(name) ?: closureScope.get(name)
|
// from the closure.
|
||||||
|
|
||||||
|
// note using super, not callScope, as arguments are assigned by the constructor
|
||||||
|
// and are not assigned yet to vars in callScope self:
|
||||||
|
super.objects[name]?.let {
|
||||||
|
// if( name == "predicate" ) {
|
||||||
|
// println("predicate: ${it.type.isArgument}: ${it.value}")
|
||||||
|
// }
|
||||||
|
if( it.type.isArgument ) return it
|
||||||
|
}
|
||||||
|
return closureScope.get(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ApplyScope(_parent: Scope,val applied: Scope) : Scope(_parent, thisObj = applied.thisObj) {
|
||||||
|
|
||||||
|
override fun get(name: String): ObjRecord? {
|
||||||
|
return applied.get(name) ?: super.get(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun applyClosure(closure: Scope): Scope {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sergeych.lyng
|
||||||
|
|
||||||
|
sealed class CodeContext {
|
||||||
|
class Module(@Suppress("unused") val packageName: String?): CodeContext()
|
||||||
|
class Function(val name: String): CodeContext()
|
||||||
|
class ClassBody(val name: String): CodeContext()
|
||||||
|
}
|
@ -45,6 +45,17 @@ class Compiler(
|
|||||||
|
|
||||||
private fun popInitScope(): MutableList<Statement> = initStack.removeLast()
|
private fun popInitScope(): MutableList<Statement> = initStack.removeLast()
|
||||||
|
|
||||||
|
private val codeContexts = mutableListOf<CodeContext>(CodeContext.Module(null))
|
||||||
|
|
||||||
|
private suspend fun <T>inCodeContext(context: CodeContext,f: suspend ()->T): T {
|
||||||
|
return try {
|
||||||
|
codeContexts.add(context)
|
||||||
|
f()
|
||||||
|
} finally {
|
||||||
|
codeContexts.removeLast()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun parseScript(): Script {
|
private suspend fun parseScript(): Script {
|
||||||
val statements = mutableListOf<Statement>()
|
val statements = mutableListOf<Statement>()
|
||||||
val start = cc.currentPos()
|
val start = cc.currentPos()
|
||||||
@ -504,7 +515,7 @@ class Compiler(
|
|||||||
|
|
||||||
val callStatement = statement {
|
val callStatement = statement {
|
||||||
// and the source closure of the lambda which might have other thisObj.
|
// and the source closure of the lambda which might have other thisObj.
|
||||||
val context = ClosureScope(this, closure!!) //AppliedScope(closure!!, args, this)
|
val context = this.applyClosure(closure!!)
|
||||||
if (argsDeclaration == null) {
|
if (argsDeclaration == null) {
|
||||||
// no args: automatic var 'it'
|
// no args: automatic var 'it'
|
||||||
val l = args.list
|
val l = args.list
|
||||||
@ -516,7 +527,7 @@ class Compiler(
|
|||||||
// more args: it is a list of args
|
// more args: it is a list of args
|
||||||
else -> ObjList(l.toMutableList())
|
else -> ObjList(l.toMutableList())
|
||||||
}
|
}
|
||||||
context.addItem("it", false, itValue)
|
context.addItem("it", false, itValue, recordType = ObjRecord.Type.Argument)
|
||||||
} else {
|
} else {
|
||||||
// assign vars as declared the standard way
|
// assign vars as declared the standard way
|
||||||
argsDeclaration.assignToContext(context, defaultAccessType = AccessType.Val)
|
argsDeclaration.assignToContext(context, defaultAccessType = AccessType.Val)
|
||||||
@ -525,7 +536,7 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Accessor { x ->
|
return Accessor { x ->
|
||||||
if (closure == null) closure = x
|
closure = x
|
||||||
callStatement.asReadonly
|
callStatement.asReadonly
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1202,73 +1213,76 @@ class Compiler(
|
|||||||
|
|
||||||
private suspend fun parseClassDeclaration(): Statement {
|
private suspend fun parseClassDeclaration(): Statement {
|
||||||
val nameToken = cc.requireToken(Token.Type.ID)
|
val nameToken = cc.requireToken(Token.Type.ID)
|
||||||
val constructorArgsDeclaration =
|
return inCodeContext(CodeContext.ClassBody(nameToken.value)) {
|
||||||
if (cc.skipTokenOfType(Token.Type.LPAREN, isOptional = true))
|
val constructorArgsDeclaration =
|
||||||
parseArgsDeclaration(isClassDeclaration = true)
|
if (cc.skipTokenOfType(Token.Type.LPAREN, isOptional = true))
|
||||||
else null
|
parseArgsDeclaration(isClassDeclaration = true)
|
||||||
|
else null
|
||||||
|
|
||||||
if (constructorArgsDeclaration != null && constructorArgsDeclaration.endTokenType != Token.Type.RPAREN)
|
if (constructorArgsDeclaration != null && constructorArgsDeclaration.endTokenType != Token.Type.RPAREN)
|
||||||
throw ScriptError(
|
throw ScriptError(
|
||||||
nameToken.pos,
|
nameToken.pos,
|
||||||
"Bad class declaration: expected ')' at the end of the primary constructor"
|
"Bad class declaration: expected ')' at the end of the primary constructor"
|
||||||
)
|
)
|
||||||
|
|
||||||
cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true)
|
cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true)
|
||||||
val t = cc.next()
|
val t = cc.next()
|
||||||
|
|
||||||
pushInitScope()
|
pushInitScope()
|
||||||
|
|
||||||
val bodyInit: Statement? = if (t.type == Token.Type.LBRACE) {
|
val bodyInit: Statement? = if (t.type == Token.Type.LBRACE) {
|
||||||
// parse body
|
// parse body
|
||||||
parseScript().also {
|
parseScript().also {
|
||||||
cc.skipTokens(Token.Type.RBRACE)
|
cc.skipTokens(Token.Type.RBRACE)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cc.previous()
|
||||||
|
null
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
cc.previous()
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
val initScope = popInitScope()
|
val initScope = popInitScope()
|
||||||
|
|
||||||
// create class
|
// create class
|
||||||
val className = nameToken.value
|
val className = nameToken.value
|
||||||
|
|
||||||
// @Suppress("UNUSED_VARIABLE") val defaultAccess = if (isStruct) AccessType.Var else AccessType.Initialization
|
// @Suppress("UNUSED_VARIABLE") val defaultAccess = if (isStruct) AccessType.Var else AccessType.Initialization
|
||||||
// @Suppress("UNUSED_VARIABLE") val defaultVisibility = Visibility.Public
|
// @Suppress("UNUSED_VARIABLE") val defaultVisibility = Visibility.Public
|
||||||
|
|
||||||
// create instance constructor
|
// create instance constructor
|
||||||
// create custom objClass with all fields and instance constructor
|
// create custom objClass with all fields and instance constructor
|
||||||
|
|
||||||
val constructorCode = statement {
|
val constructorCode = statement {
|
||||||
// constructor code is registered with class instance and is called over
|
// constructor code is registered with class instance and is called over
|
||||||
// new `thisObj` already set by class to ObjInstance.instanceContext
|
// new `thisObj` already set by class to ObjInstance.instanceContext
|
||||||
thisObj as ObjInstance
|
thisObj as ObjInstance
|
||||||
|
|
||||||
// the context now is a "class creation context", we must use its args to initialize
|
// the context now is a "class creation context", we must use its args to initialize
|
||||||
// fields. Note that 'this' is already set by class
|
// fields. Note that 'this' is already set by class
|
||||||
constructorArgsDeclaration?.assignToContext(this)
|
constructorArgsDeclaration?.assignToContext(this)
|
||||||
bodyInit?.execute(this)
|
bodyInit?.execute(this)
|
||||||
|
|
||||||
thisObj
|
thisObj
|
||||||
}
|
}
|
||||||
// inheritance must alter this code:
|
// inheritance must alter this code:
|
||||||
val newClass = ObjInstanceClass(className).apply {
|
val newClass = ObjInstanceClass(className).apply {
|
||||||
instanceConstructor = constructorCode
|
instanceConstructor = constructorCode
|
||||||
constructorMeta = constructorArgsDeclaration
|
constructorMeta = constructorArgsDeclaration
|
||||||
}
|
}
|
||||||
|
|
||||||
return statement {
|
statement {
|
||||||
// the main statement should create custom ObjClass instance with field
|
// the main statement should create custom ObjClass instance with field
|
||||||
// accessors, constructor registration, etc.
|
// accessors, constructor registration, etc.
|
||||||
addItem(className, false, newClass)
|
addItem(className, false, newClass)
|
||||||
if (initScope.isNotEmpty()) {
|
if (initScope.isNotEmpty()) {
|
||||||
val classScope = copy(newThisObj = newClass)
|
val classScope = copy(newThisObj = newClass)
|
||||||
newClass.classScope = classScope
|
newClass.classScope = classScope
|
||||||
for (s in initScope)
|
for (s in initScope)
|
||||||
s.execute(classScope)
|
s.execute(classScope)
|
||||||
|
}
|
||||||
|
newClass
|
||||||
}
|
}
|
||||||
newClass
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1639,8 +1653,7 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun
|
private suspend fun parseFunctionDeclaration(
|
||||||
parseFunctionDeclaration(
|
|
||||||
visibility: Visibility = Visibility.Public,
|
visibility: Visibility = Visibility.Public,
|
||||||
@Suppress("UNUSED_PARAMETER") isOpen: Boolean = false,
|
@Suppress("UNUSED_PARAMETER") isOpen: Boolean = false,
|
||||||
isExtern: Boolean = false,
|
isExtern: Boolean = false,
|
||||||
@ -1654,7 +1667,7 @@ class Compiler(
|
|||||||
else t.value
|
else t.value
|
||||||
|
|
||||||
val annotation = lastAnnotation
|
val annotation = lastAnnotation
|
||||||
|
val parentContext = codeContexts.last()
|
||||||
|
|
||||||
t = cc.next()
|
t = cc.next()
|
||||||
// Is extension?
|
// Is extension?
|
||||||
@ -1679,61 +1692,66 @@ class Compiler(
|
|||||||
|
|
||||||
if (cc.current().type == Token.Type.COLON) parseTypeDeclaration()
|
if (cc.current().type == Token.Type.COLON) parseTypeDeclaration()
|
||||||
|
|
||||||
// Here we should be at open body
|
return inCodeContext(CodeContext.Function(name)) {
|
||||||
val fnStatements = if (isExtern)
|
|
||||||
statement { raiseError("extern function not provided: $name") }
|
|
||||||
else
|
|
||||||
parseBlock()
|
|
||||||
|
|
||||||
var closure: Scope? = null
|
// Here we should be at open body
|
||||||
|
val fnStatements = if (isExtern)
|
||||||
|
statement { raiseError("extern function not provided: $name") }
|
||||||
|
else
|
||||||
|
parseBlock()
|
||||||
|
|
||||||
val fnBody = statement(t.pos) { callerContext ->
|
var closure: Scope? = null
|
||||||
callerContext.pos = start
|
|
||||||
|
|
||||||
// restore closure where the function was defined, and making a copy of it
|
val fnBody = statement(t.pos) { callerContext ->
|
||||||
// for local space (otherwise it will write local stuff to closure!)
|
callerContext.pos = start
|
||||||
val context = closure?.let { ClosureScope(callerContext, it) }
|
|
||||||
?: callerContext.raiseError("bug: closure not set")
|
|
||||||
|
|
||||||
// load params from caller context
|
// restore closure where the function was defined, and making a copy of it
|
||||||
argsDeclaration.assignToContext(context, callerContext.args, defaultAccessType = AccessType.Val)
|
// for local space. If there is no closure, we are in, say, class context where
|
||||||
if (extTypeName != null) {
|
// the closure is in the class initialization and we needn't more:
|
||||||
context.thisObj = callerContext.thisObj
|
val context = closure?.let { ClosureScope(callerContext, it) }
|
||||||
}
|
?: callerContext
|
||||||
fnStatements.execute(context)
|
|
||||||
}
|
|
||||||
val fnCreateStatement = statement(start) { context ->
|
|
||||||
// we added fn in the context. now we must save closure
|
|
||||||
// for the function
|
|
||||||
closure = context
|
|
||||||
|
|
||||||
val annotatedFnBody = annotation?.invoke(context, ObjString(name), fnBody)
|
// load params from caller context
|
||||||
?: fnBody
|
argsDeclaration.assignToContext(context, callerContext.args, defaultAccessType = AccessType.Val)
|
||||||
|
if (extTypeName != null) {
|
||||||
extTypeName?.let { typeName ->
|
context.thisObj = callerContext.thisObj
|
||||||
// class extension method
|
|
||||||
val type = context[typeName]?.value ?: context.raiseSymbolNotFound("class $typeName not found")
|
|
||||||
if (type !is ObjClass) context.raiseClassCastError("$typeName is not the class instance")
|
|
||||||
type.addFn(name, isOpen = true) {
|
|
||||||
// ObjInstance has a fixed instance scope, so we need to build a closure
|
|
||||||
(thisObj as? ObjInstance)?.let { i ->
|
|
||||||
annotatedFnBody.execute(ClosureScope(this, i.instanceScope))
|
|
||||||
}
|
|
||||||
// other classes can create one-time scope for this rare case:
|
|
||||||
?: annotatedFnBody.execute(thisObj.autoInstanceScope(this))
|
|
||||||
}
|
}
|
||||||
|
fnStatements.execute(context)
|
||||||
}
|
}
|
||||||
// regular function/method
|
val fnCreateStatement = statement(start) { context ->
|
||||||
?: context.addItem(name, false, annotatedFnBody, visibility)
|
// we added fn in the context. now we must save closure
|
||||||
// as the function can be called from anywhere, we have
|
// for the function, unless we're in the class scope:
|
||||||
// saved the proper context in the closure
|
if( isStatic || parentContext !is CodeContext.ClassBody)
|
||||||
annotatedFnBody
|
closure = context
|
||||||
|
|
||||||
|
val annotatedFnBody = annotation?.invoke(context, ObjString(name), fnBody)
|
||||||
|
?: fnBody
|
||||||
|
|
||||||
|
extTypeName?.let { typeName ->
|
||||||
|
// class extension method
|
||||||
|
val type = context[typeName]?.value ?: context.raiseSymbolNotFound("class $typeName not found")
|
||||||
|
if (type !is ObjClass) context.raiseClassCastError("$typeName is not the class instance")
|
||||||
|
type.addFn(name, isOpen = true) {
|
||||||
|
// ObjInstance has a fixed instance scope, so we need to build a closure
|
||||||
|
(thisObj as? ObjInstance)?.let { i ->
|
||||||
|
annotatedFnBody.execute(ClosureScope(this, i.instanceScope))
|
||||||
|
}
|
||||||
|
// other classes can create one-time scope for this rare case:
|
||||||
|
?: annotatedFnBody.execute(thisObj.autoInstanceScope(this))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// regular function/method
|
||||||
|
?: context.addItem(name, false, annotatedFnBody, visibility)
|
||||||
|
// as the function can be called from anywhere, we have
|
||||||
|
// saved the proper context in the closure
|
||||||
|
annotatedFnBody
|
||||||
|
}
|
||||||
|
if (isStatic) {
|
||||||
|
currentInitScope += fnCreateStatement
|
||||||
|
NopStatement
|
||||||
|
} else
|
||||||
|
fnCreateStatement
|
||||||
}
|
}
|
||||||
return if (isStatic) {
|
|
||||||
currentInitScope += fnCreateStatement
|
|
||||||
NopStatement
|
|
||||||
} else
|
|
||||||
fnCreateStatement
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun parseBlock(skipLeadingBrace: Boolean = false): Statement {
|
private suspend fun parseBlock(skipLeadingBrace: Boolean = false): Statement {
|
||||||
|
@ -131,9 +131,16 @@ open class Scope(
|
|||||||
open operator fun get(name: String): ObjRecord? =
|
open operator fun get(name: String): ObjRecord? =
|
||||||
if (name == "this") thisObj.asReadonly
|
if (name == "this") thisObj.asReadonly
|
||||||
else {
|
else {
|
||||||
objects[name]
|
(objects[name]
|
||||||
?: parent?.get(name)
|
?: parent?.get(name)
|
||||||
?: thisObj.objClass.getInstanceMemberOrNull(name)
|
?: thisObj.objClass
|
||||||
|
.getInstanceMemberOrNull(name)
|
||||||
|
)
|
||||||
|
// ?.also {
|
||||||
|
// if( name == "predicate") {
|
||||||
|
// println("got predicate $it")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun copy(pos: Pos, args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Scope =
|
fun copy(pos: Pos, args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Scope =
|
||||||
@ -242,6 +249,8 @@ open class Scope(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun applyClosure(closure: Scope): Scope = ClosureScope(this, closure)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
fun new(): Scope =
|
fun new(): Scope =
|
||||||
|
@ -296,9 +296,14 @@ open class Obj {
|
|||||||
args.firstAndOnly().callOn(copy(Arguments(thisObj)))
|
args.firstAndOnly().callOn(copy(Arguments(thisObj)))
|
||||||
}
|
}
|
||||||
addFn("apply") {
|
addFn("apply") {
|
||||||
val newContext = (thisObj as? ObjInstance)?.instanceScope ?: this
|
val body = args.firstAndOnly()
|
||||||
args.firstAndOnly()
|
(thisObj as? ObjInstance)?.let {
|
||||||
.callOn(newContext)
|
println("apply in ${thisObj is ObjInstance}, ${it.instanceScope}")
|
||||||
|
body.callOn(ApplyScope(this, it.instanceScope))
|
||||||
|
} ?: run {
|
||||||
|
println("apply on non-instance $thisObj")
|
||||||
|
body.callOn(this)
|
||||||
|
}
|
||||||
thisObj
|
thisObj
|
||||||
}
|
}
|
||||||
addFn("also") {
|
addFn("also") {
|
||||||
|
@ -17,31 +17,26 @@
|
|||||||
|
|
||||||
package net.sergeych.lyng.obj
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
import net.sergeych.lyng.Arguments
|
//class ObjDelegateContext()
|
||||||
import net.sergeych.lyng.Scope
|
//
|
||||||
import net.sergeych.lyng.Statement
|
//class ObjDelegate(
|
||||||
import net.sergeych.lyng.statement
|
// val getter: Statement,
|
||||||
|
// val setter: Statement = statement { raiseNotImplemented("setter is not implemented") }
|
||||||
class ObjDelegateContext()
|
//): Obj() {
|
||||||
|
//
|
||||||
class ObjDelegate(
|
// override suspend fun assign(scope: Scope, other: Obj): Obj? {
|
||||||
val getter: Statement,
|
// setter.execute(scope.copy(Arguments(other)))
|
||||||
val setter: Statement = statement { raiseNotImplemented("setter is not implemented") }
|
// return other
|
||||||
): Obj() {
|
// }
|
||||||
|
//
|
||||||
override suspend fun assign(scope: Scope, other: Obj): Obj? {
|
// companion object {
|
||||||
setter.execute(scope.copy(Arguments(other)))
|
// val type = object: ObjClass("Delegate") {
|
||||||
return other
|
// override suspend fun callOn(scope: Scope): Obj {
|
||||||
}
|
// scope.raiseError("Delegate should not be constructed directly")
|
||||||
|
// }
|
||||||
companion object {
|
// }.apply {
|
||||||
val type = object: ObjClass("Delegate") {
|
//
|
||||||
override suspend fun callOn(scope: Scope): Obj {
|
// }
|
||||||
scope.raiseError("Delegate should not be constructed directly")
|
// }
|
||||||
}
|
//
|
||||||
}.apply {
|
//}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -40,12 +40,12 @@ class ObjFlowBuilder(val output: SendChannel<Obj>) : Obj() {
|
|||||||
val data = requireOnlyArg<Obj>()
|
val data = requireOnlyArg<Obj>()
|
||||||
try {
|
try {
|
||||||
val channel = thisAs<ObjFlowBuilder>().output
|
val channel = thisAs<ObjFlowBuilder>().output
|
||||||
if( !channel.isClosedForSend )
|
if (!channel.isClosedForSend)
|
||||||
channel.send(data)
|
channel.send(data)
|
||||||
else
|
else
|
||||||
throw ScriptFlowIsNoMoreCollected()
|
throw ScriptFlowIsNoMoreCollected()
|
||||||
} catch (x: Exception) {
|
} catch (x: Exception) {
|
||||||
if( x !is CancellationException )
|
if (x !is CancellationException)
|
||||||
x.printStackTrace()
|
x.printStackTrace()
|
||||||
throw ScriptFlowIsNoMoreCollected()
|
throw ScriptFlowIsNoMoreCollected()
|
||||||
}
|
}
|
||||||
@ -62,12 +62,10 @@ private fun createLyngFlowInput(scope: Scope, producer: Statement): ReceiveChann
|
|||||||
globalLaunch {
|
globalLaunch {
|
||||||
try {
|
try {
|
||||||
producer.execute(builderScope)
|
producer.execute(builderScope)
|
||||||
}
|
} catch (x: ScriptFlowIsNoMoreCollected) {
|
||||||
catch(x: ScriptFlowIsNoMoreCollected) {
|
|
||||||
x.printStackTrace()
|
x.printStackTrace()
|
||||||
// premature flow closing, OK
|
// premature flow closing, OK
|
||||||
}
|
} catch (x: Exception) {
|
||||||
catch(x: Exception) {
|
|
||||||
x.printStackTrace()
|
x.printStackTrace()
|
||||||
}
|
}
|
||||||
channel.close()
|
channel.close()
|
||||||
@ -87,7 +85,11 @@ class ObjFlow(val producer: Statement, val scope: Scope) : Obj() {
|
|||||||
}.apply {
|
}.apply {
|
||||||
addFn("iterator") {
|
addFn("iterator") {
|
||||||
val objFlow = thisAs<ObjFlow>()
|
val objFlow = thisAs<ObjFlow>()
|
||||||
ObjFlowIterator( statement { objFlow.producer.execute(ClosureScope(this,objFlow.scope)) } )
|
ObjFlowIterator(statement {
|
||||||
|
objFlow.producer.execute(
|
||||||
|
ClosureScope(this, objFlow.scope)
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -105,9 +107,10 @@ class ObjFlowIterator(val producer: Statement) : Obj() {
|
|||||||
private var isCancelled = false
|
private var isCancelled = false
|
||||||
|
|
||||||
private fun checkNotCancelled(scope: Scope) {
|
private fun checkNotCancelled(scope: Scope) {
|
||||||
if( isCancelled )
|
if (isCancelled)
|
||||||
scope.raiseIllegalState("iteration is cancelled")
|
scope.raiseIllegalState("iteration is cancelled")
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun hasNext(scope: Scope): ObjBool {
|
suspend fun hasNext(scope: Scope): ObjBool {
|
||||||
checkNotCancelled(scope)
|
checkNotCancelled(scope)
|
||||||
// cold start:
|
// cold start:
|
||||||
|
@ -35,11 +35,15 @@ data class ObjRecord(
|
|||||||
Field(true, true),
|
Field(true, true),
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
Fun,
|
Fun,
|
||||||
|
@Suppress("unused")
|
||||||
ConstructorField(true, true),
|
ConstructorField(true, true),
|
||||||
|
Argument(true, true),
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
Class,
|
Class,
|
||||||
Enum,
|
Enum,
|
||||||
Other
|
Other;
|
||||||
|
|
||||||
|
val isArgument get() = this == Argument
|
||||||
}
|
}
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
fun qualifiedName(name: String): String =
|
fun qualifiedName(name: String): String =
|
||||||
|
@ -35,7 +35,7 @@ fun Iterable.filter(predicate) {
|
|||||||
val list = this
|
val list = this
|
||||||
flow {
|
flow {
|
||||||
for( item in list ) {
|
for( item in list ) {
|
||||||
if( predicate(item) ) {
|
if( predicate(item) ) {ln
|
||||||
emit(item)
|
emit(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,5 +93,23 @@ fun Iterable.joinToString(prefix=" ", transformer=null) {
|
|||||||
result ?: ""
|
result ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Iterable.any(predicate): Bool {
|
||||||
|
for( i in this ) {
|
||||||
|
if( predicate(i) ) {
|
||||||
|
break true
|
||||||
|
// todo: add cancelIteration() in for loop!
|
||||||
|
}
|
||||||
|
} else false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Iterable.all(predicate): Bool {
|
||||||
|
for( i in this ) {
|
||||||
|
if( !predicate(i) ) {
|
||||||
|
break false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else true
|
||||||
|
}
|
||||||
|
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
|
@ -1359,9 +1359,6 @@ class ScriptTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val prefix = ":"
|
val prefix = ":"
|
||||||
val lambda = {
|
|
||||||
prefix + getText() + "!"
|
|
||||||
}
|
|
||||||
|
|
||||||
val text = "invalid"
|
val text = "invalid"
|
||||||
val t1 = T("foo")
|
val t1 = T("foo")
|
||||||
@ -1371,18 +1368,22 @@ class ScriptTest {
|
|||||||
// it must take "text" from class t1:
|
// it must take "text" from class t1:
|
||||||
assertEquals("foo", text)
|
assertEquals("foo", text)
|
||||||
assertEquals( "foo!", getText() )
|
assertEquals( "foo!", getText() )
|
||||||
assertEquals( ":foo!!", lambda() )
|
assertEquals( ":foo!!", {
|
||||||
|
prefix + getText() + "!"
|
||||||
|
}())
|
||||||
}
|
}
|
||||||
t2.apply {
|
t2.apply {
|
||||||
assertEquals("bar", text)
|
assertEquals("bar", text)
|
||||||
assertEquals( "bar!", getText() )
|
assertEquals( "bar!", getText() )
|
||||||
assertEquals( ":bar!!", lambda() )
|
assertEquals( ":bar!!", {
|
||||||
|
prefix + getText() + "!"
|
||||||
|
}())
|
||||||
}
|
}
|
||||||
// worst case: names clash
|
// worst case: names clash
|
||||||
fun badOne() {
|
fun badOne() {
|
||||||
val prefix = "&"
|
val prefix = "&"
|
||||||
t1.apply {
|
t1.apply {
|
||||||
assertEquals( ":foo!!", lambda() )
|
assertEquals( ":foo!!", prefix + getText() + "!" )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
badOne()
|
badOne()
|
||||||
@ -2941,5 +2942,4 @@ class ScriptTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -23,8 +23,9 @@ class StdlibTest {
|
|||||||
@Test
|
@Test
|
||||||
fun testIterableFilter() = runTest {
|
fun testIterableFilter() = runTest {
|
||||||
eval("""
|
eval("""
|
||||||
assertEquals([1,3,5,7], (1..8).filter{ it % 2 == 1 }.toList() )
|
assertEquals([2,4,6,8], (1..8).filter{ println("call2"); it % 2 == 0 }.toList() )
|
||||||
assertEquals([2,4,6,8], (1..8).filter{ it % 2 == 0 }.toList() )
|
println("-------------------")
|
||||||
|
assertEquals([1,3,5,7], (1..8).filter{ println("call1"); it % 2 == 1 }.toList() )
|
||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -46,6 +47,16 @@ class StdlibTest {
|
|||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAnyAndAll() = runTest {
|
||||||
|
eval("""
|
||||||
|
assert( [1,2,3].any { it > 2 } )
|
||||||
|
assert( ![1,2,3].any { it > 4 } )
|
||||||
|
assert( [1,2,3].all { it <= 3 } )
|
||||||
|
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testRingBuffer() = runTest {
|
fun testRingBuffer() = runTest {
|
||||||
eval("""
|
eval("""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user