diff --git a/library/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt b/library/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt index 00165aa..34ac778 100644 --- a/library/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt +++ b/library/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt @@ -132,7 +132,7 @@ class Compiler( operand = Accessor { context -> context.pos = next.pos val v = left.getter(context).value - WithAccess( + ObjRecord( v.invokeInstanceMethod( context, next.value, @@ -641,7 +641,7 @@ class Compiler( else -> { Accessor({ it.pos = t.pos - it.get(t.value)?.asAccess + it.get(t.value) ?: it.raiseError("symbol not defined: '${t.value}'") }) { ctx, newValue -> ctx.get(t.value)?.let { stored -> @@ -886,7 +886,7 @@ class Compiler( } private suspend fun loopIntRange( - forContext: Context, start: Int, end: Int, loopVar: StoredObj, + forContext: Context, start: Int, end: Int, loopVar: ObjRecord, body: Statement, elseStatement: Statement?, label: String?, catchBreak: Boolean ): Obj { var result: Obj = ObjVoid @@ -914,7 +914,7 @@ class Compiler( } private suspend fun loopIterable( - forContext: Context, sourceObj: Obj, loopVar: StoredObj, + forContext: Context, sourceObj: Obj, loopVar: ObjRecord, body: Statement, elseStatement: Statement?, label: String?, catchBreak: Boolean ): Obj { diff --git a/library/src/commonMain/kotlin/net/sergeych/lyng/Context.kt b/library/src/commonMain/kotlin/net/sergeych/lyng/Context.kt index 9d99bea..e0744f8 100644 --- a/library/src/commonMain/kotlin/net/sergeych/lyng/Context.kt +++ b/library/src/commonMain/kotlin/net/sergeych/lyng/Context.kt @@ -35,47 +35,47 @@ class Context( throw ExecutionError(obj) } - inline fun requiredArg(index: Int): T { - if( args.list.size <= index ) raiseError("Expected at least ${index+1} argument, got ${args.list.size}") + inline fun requiredArg(index: Int): T { + if (args.list.size <= index) raiseError("Expected at least ${index + 1} argument, got ${args.list.size}") return (args.list[index].value as? T) ?: raiseClassCastError("Expected type ${T::class.simpleName}, got ${args.list[index].value::class.simpleName}") } - inline fun requireOnlyArg(): T { - if( args.list.size != 1 ) raiseError("Expected exactly 1 argument, got ${args.list.size}") + inline fun requireOnlyArg(): T { + if (args.list.size != 1) raiseError("Expected exactly 1 argument, got ${args.list.size}") return requiredArg(0) } @Suppress("unused") fun requireExactCount(count: Int) { - if( args.list.size != count ) { + if (args.list.size != count) { raiseError("Expected exactly $count arguments, got ${args.list.size}") } } - inline fun thisAs(): T = (thisObj as? T) + inline fun thisAs(): T = (thisObj as? T) ?: raiseClassCastError("Cannot cast ${thisObj.objClass.className} to ${T::class.simpleName}") - private val objects = mutableMapOf() + private val objects = mutableMapOf() - operator fun get(name: String): StoredObj? = + operator fun get(name: String): ObjRecord? = objects[name] ?: parent?.get(name) - fun copy(pos: Pos, args: Arguments = Arguments.EMPTY,newThisObj: Obj? = null): Context = + fun copy(pos: Pos, args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Context = Context(this, args, pos, newThisObj ?: thisObj) - fun copy(args: Arguments = Arguments.EMPTY,newThisObj: Obj? = null): Context = + fun copy(args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Context = Context(this, args, pos, newThisObj ?: thisObj) fun copy() = Context(this, args, pos, thisObj) - fun addItem(name: String, isMutable: Boolean, value: Obj?): StoredObj { - return StoredObj(value, isMutable).also { objects.put(name, it) } + fun addItem(name: String, isMutable: Boolean, value: Obj): ObjRecord { + return ObjRecord(value, isMutable).also { objects.put(name, it) } } fun getOrCreateNamespace(name: String): ObjClass { - val ns = objects.getOrPut(name) { StoredObj(ObjNamespace(name), isMutable = false) }.value + val ns = objects.getOrPut(name) { ObjRecord(ObjNamespace(name), isMutable = false) }.value return ns!!.objClass } @@ -86,7 +86,7 @@ class Context( } } - inline fun addFn(vararg names: String, crossinline fn: suspend Context.() -> T) { + inline fun addFn(vararg names: String, crossinline fn: suspend Context.() -> T) { val newFn = object : Statement() { override val pos: Pos = Pos.builtIn @@ -102,7 +102,7 @@ class Context( } } - fun addConst(name: String,value: Obj) = addItem(name, false, value) + fun addConst(name: String, value: Obj) = addItem(name, false, value) suspend fun eval(code: String): Obj = Compiler().compile(code.toSource()).execute(this) diff --git a/library/src/commonMain/kotlin/net/sergeych/lyng/Obj.kt b/library/src/commonMain/kotlin/net/sergeych/lyng/Obj.kt index 060a15c..b65328f 100644 --- a/library/src/commonMain/kotlin/net/sergeych/lyng/Obj.kt +++ b/library/src/commonMain/kotlin/net/sergeych/lyng/Obj.kt @@ -6,20 +6,35 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import net.sergeych.synctools.ProtectedOp -//typealias InstanceMethod = (Context, Obj) -> Obj - -data class WithAccess( - var value: T, +/** + * Record to store object with access rules, e.g. [isMutable] and access level [visibility]. + */ +data class ObjRecord( + var value: Obj, val isMutable: Boolean, val visibility: Compiler.Visibility = Compiler.Visibility.Public ) +/** + * When we need read-write access to an object in some abstract storage, we need Accessor, + * as in-site assigning is not always sufficient, in general case we need to replace the object + * in the storage. + * + * Note that assigning new value is more complex than just replacing the object, see how assignment + * operator is implemented in [Compiler.allOps]. + */ data class Accessor( - val getter: suspend (Context) -> WithAccess, + val getter: suspend (Context) -> ObjRecord, val setterOrNull: (suspend (Context, Obj) -> Unit)? ) { - constructor(getter: suspend (Context) -> WithAccess) : this(getter, null) + /** + * Simplified constructor for immutable stores. + */ + constructor(getter: suspend (Context) -> ObjRecord) : this(getter, null) + /** + * Get the setter or throw. + */ fun setter(pos: Pos) = setterOrNull ?: throw ScriptError(pos, "can't assign value") } @@ -164,13 +179,13 @@ open class Obj { suspend fun sync(block: () -> T): T = monitor.withLock { block() } - suspend fun readField(context: Context, name: String): WithAccess { + suspend fun readField(context: Context, name: String): ObjRecord { // could be property or class field: val obj = objClass.getInstanceMemberOrNull(name) val value = obj?.value return when (value) { is Statement -> { - WithAccess(value.execute(context.copy(context.pos, newThisObj = this)), obj.isMutable) + ObjRecord(value.execute(context.copy(context.pos, newThisObj = this)), obj.isMutable) } // could be writable property naturally null -> ObjNull.asReadonly @@ -221,8 +236,8 @@ open class Obj { callOn(context.copy(atPos, args = args, newThisObj = thisObj)) - val asReadonly: WithAccess by lazy { WithAccess(this, false) } - val asMutable: WithAccess by lazy { WithAccess(this, true) } + val asReadonly: ObjRecord by lazy { ObjRecord(this, false) } + val asMutable: ObjRecord by lazy { ObjRecord(this, true) } companion object { diff --git a/library/src/commonMain/kotlin/net/sergeych/lyng/ObjClass.kt b/library/src/commonMain/kotlin/net/sergeych/lyng/ObjClass.kt index 3126c4e..11998e3 100644 --- a/library/src/commonMain/kotlin/net/sergeych/lyng/ObjClass.kt +++ b/library/src/commonMain/kotlin/net/sergeych/lyng/ObjClass.kt @@ -16,7 +16,7 @@ class ObjClass( override val objClass: ObjClass by lazy { ObjClassType } // members: fields most often - private val members = mutableMapOf>() + private val members = mutableMapOf() override fun toString(): String = className @@ -46,7 +46,7 @@ class ObjClass( ) { if (name in members || allParentsSet.any { name in it.members } == true) throw ScriptError(pos, "$name is already defined in $objClass or one of its supertypes") - members[name] = WithAccess(initialValue, isMutable) + members[name] = ObjRecord(initialValue, isMutable) } fun addFn(name: String, isOpen: Boolean = false, code: suspend Context.() -> Obj) { @@ -59,13 +59,13 @@ class ObjClass( /** * Get instance member traversing the hierarchy if needed. Its meaning is different for different objects. */ - fun getInstanceMemberOrNull(name: String): WithAccess? { + fun getInstanceMemberOrNull(name: String): ObjRecord? { members[name]?.let { return it } allParentsSet.forEach { parent -> parent.getInstanceMemberOrNull(name)?.let { return it } } return null } - fun getInstanceMember(atPos: Pos, name: String): WithAccess = + fun getInstanceMember(atPos: Pos, name: String): ObjRecord = getInstanceMemberOrNull(name) ?: throw ScriptError(atPos, "symbol doesn't exist: $name") } diff --git a/library/src/commonMain/kotlin/net/sergeych/lyng/StoredObj.kt b/library/src/commonMain/kotlin/net/sergeych/lyng/StoredObj.kt deleted file mode 100644 index 2532704..0000000 --- a/library/src/commonMain/kotlin/net/sergeych/lyng/StoredObj.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.sergeych.lyng - -/** - * Whatever [Obj] stored somewhere - */ -data class StoredObj( - var value: Obj?, - val isMutable: Boolean = false -) { - val asAccess: WithAccess? get() = value?.let { WithAccess(it, isMutable) } -} \ No newline at end of file