optimizing refactoring: no more StoredObj.kt, WithAccess now always deals with Obj and is renamed to ObjRecord

This commit is contained in:
Sergey Chernov 2025-06-09 16:34:30 +04:00
parent 33fdb0934a
commit 56e0d2fd63
5 changed files with 48 additions and 44 deletions

View File

@ -132,7 +132,7 @@ class Compiler(
operand = Accessor { context -> operand = Accessor { context ->
context.pos = next.pos context.pos = next.pos
val v = left.getter(context).value val v = left.getter(context).value
WithAccess( ObjRecord(
v.invokeInstanceMethod( v.invokeInstanceMethod(
context, context,
next.value, next.value,
@ -641,7 +641,7 @@ class Compiler(
else -> { else -> {
Accessor({ Accessor({
it.pos = t.pos it.pos = t.pos
it.get(t.value)?.asAccess it.get(t.value)
?: it.raiseError("symbol not defined: '${t.value}'") ?: it.raiseError("symbol not defined: '${t.value}'")
}) { ctx, newValue -> }) { ctx, newValue ->
ctx.get(t.value)?.let { stored -> ctx.get(t.value)?.let { stored ->
@ -886,7 +886,7 @@ class Compiler(
} }
private suspend fun loopIntRange( 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 body: Statement, elseStatement: Statement?, label: String?, catchBreak: Boolean
): Obj { ): Obj {
var result: Obj = ObjVoid var result: Obj = ObjVoid
@ -914,7 +914,7 @@ class Compiler(
} }
private suspend fun loopIterable( private suspend fun loopIterable(
forContext: Context, sourceObj: Obj, loopVar: StoredObj, forContext: Context, sourceObj: Obj, loopVar: ObjRecord,
body: Statement, elseStatement: Statement?, label: String?, body: Statement, elseStatement: Statement?, label: String?,
catchBreak: Boolean catchBreak: Boolean
): Obj { ): Obj {

View File

@ -35,47 +35,47 @@ class Context(
throw ExecutionError(obj) throw ExecutionError(obj)
} }
inline fun <reified T: Obj>requiredArg(index: Int): T { inline fun <reified T : Obj> requiredArg(index: Int): T {
if( args.list.size <= index ) raiseError("Expected at least ${index+1} argument, got ${args.list.size}") if (args.list.size <= index) raiseError("Expected at least ${index + 1} argument, got ${args.list.size}")
return (args.list[index].value as? T) return (args.list[index].value as? T)
?: raiseClassCastError("Expected type ${T::class.simpleName}, got ${args.list[index].value::class.simpleName}") ?: raiseClassCastError("Expected type ${T::class.simpleName}, got ${args.list[index].value::class.simpleName}")
} }
inline fun <reified T: Obj>requireOnlyArg(): T { inline fun <reified T : Obj> requireOnlyArg(): T {
if( args.list.size != 1 ) raiseError("Expected exactly 1 argument, got ${args.list.size}") if (args.list.size != 1) raiseError("Expected exactly 1 argument, got ${args.list.size}")
return requiredArg(0) return requiredArg(0)
} }
@Suppress("unused") @Suppress("unused")
fun requireExactCount(count: Int) { fun requireExactCount(count: Int) {
if( args.list.size != count ) { if (args.list.size != count) {
raiseError("Expected exactly $count arguments, got ${args.list.size}") raiseError("Expected exactly $count arguments, got ${args.list.size}")
} }
} }
inline fun <reified T: Obj>thisAs(): T = (thisObj as? T) inline fun <reified T : Obj> thisAs(): T = (thisObj as? T)
?: raiseClassCastError("Cannot cast ${thisObj.objClass.className} to ${T::class.simpleName}") ?: raiseClassCastError("Cannot cast ${thisObj.objClass.className} to ${T::class.simpleName}")
private val objects = mutableMapOf<String, StoredObj>() private val objects = mutableMapOf<String, ObjRecord>()
operator fun get(name: String): StoredObj? = operator fun get(name: String): ObjRecord? =
objects[name] objects[name]
?: parent?.get(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) 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) Context(this, args, pos, newThisObj ?: thisObj)
fun copy() = Context(this, args, pos, thisObj) fun copy() = Context(this, args, pos, thisObj)
fun addItem(name: String, isMutable: Boolean, value: Obj?): StoredObj { fun addItem(name: String, isMutable: Boolean, value: Obj): ObjRecord {
return StoredObj(value, isMutable).also { objects.put(name, it) } return ObjRecord(value, isMutable).also { objects.put(name, it) }
} }
fun getOrCreateNamespace(name: String): ObjClass { 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 return ns!!.objClass
} }
@ -86,7 +86,7 @@ class Context(
} }
} }
inline fun <reified T: Obj> addFn(vararg names: String, crossinline fn: suspend Context.() -> T) { inline fun <reified T : Obj> addFn(vararg names: String, crossinline fn: suspend Context.() -> T) {
val newFn = object : Statement() { val newFn = object : Statement() {
override val pos: Pos = Pos.builtIn 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 = suspend fun eval(code: String): Obj =
Compiler().compile(code.toSource()).execute(this) Compiler().compile(code.toSource()).execute(this)

View File

@ -6,20 +6,35 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import net.sergeych.synctools.ProtectedOp import net.sergeych.synctools.ProtectedOp
//typealias InstanceMethod = (Context, Obj) -> Obj /**
* Record to store object with access rules, e.g. [isMutable] and access level [visibility].
data class WithAccess<T>( */
var value: T, data class ObjRecord(
var value: Obj,
val isMutable: Boolean, val isMutable: Boolean,
val visibility: Compiler.Visibility = Compiler.Visibility.Public 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( data class Accessor(
val getter: suspend (Context) -> WithAccess<Obj>, val getter: suspend (Context) -> ObjRecord,
val setterOrNull: (suspend (Context, Obj) -> Unit)? val setterOrNull: (suspend (Context, Obj) -> Unit)?
) { ) {
constructor(getter: suspend (Context) -> WithAccess<Obj>) : 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") fun setter(pos: Pos) = setterOrNull ?: throw ScriptError(pos, "can't assign value")
} }
@ -164,13 +179,13 @@ open class Obj {
suspend fun <T> sync(block: () -> T): T = monitor.withLock { block() } suspend fun <T> sync(block: () -> T): T = monitor.withLock { block() }
suspend fun readField(context: Context, name: String): WithAccess<Obj> { suspend fun readField(context: Context, name: String): ObjRecord {
// could be property or class field: // could be property or class field:
val obj = objClass.getInstanceMemberOrNull(name) val obj = objClass.getInstanceMemberOrNull(name)
val value = obj?.value val value = obj?.value
return when (value) { return when (value) {
is Statement -> { 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 // could be writable property naturally
null -> ObjNull.asReadonly null -> ObjNull.asReadonly
@ -221,8 +236,8 @@ open class Obj {
callOn(context.copy(atPos, args = args, newThisObj = thisObj)) callOn(context.copy(atPos, args = args, newThisObj = thisObj))
val asReadonly: WithAccess<Obj> by lazy { WithAccess(this, false) } val asReadonly: ObjRecord by lazy { ObjRecord(this, false) }
val asMutable: WithAccess<Obj> by lazy { WithAccess(this, true) } val asMutable: ObjRecord by lazy { ObjRecord(this, true) }
companion object { companion object {

View File

@ -16,7 +16,7 @@ class ObjClass(
override val objClass: ObjClass by lazy { ObjClassType } override val objClass: ObjClass by lazy { ObjClassType }
// members: fields most often // members: fields most often
private val members = mutableMapOf<String, WithAccess<Obj>>() private val members = mutableMapOf<String, ObjRecord>()
override fun toString(): String = className override fun toString(): String = className
@ -46,7 +46,7 @@ class ObjClass(
) { ) {
if (name in members || allParentsSet.any { name in it.members } == true) 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") 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) { 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. * Get instance member traversing the hierarchy if needed. Its meaning is different for different objects.
*/ */
fun getInstanceMemberOrNull(name: String): WithAccess<Obj>? { fun getInstanceMemberOrNull(name: String): ObjRecord? {
members[name]?.let { return it } members[name]?.let { return it }
allParentsSet.forEach { parent -> parent.getInstanceMemberOrNull(name)?.let { return it } } allParentsSet.forEach { parent -> parent.getInstanceMemberOrNull(name)?.let { return it } }
return null return null
} }
fun getInstanceMember(atPos: Pos, name: String): WithAccess<Obj> = fun getInstanceMember(atPos: Pos, name: String): ObjRecord =
getInstanceMemberOrNull(name) getInstanceMemberOrNull(name)
?: throw ScriptError(atPos, "symbol doesn't exist: $name") ?: throw ScriptError(atPos, "symbol doesn't exist: $name")
} }

View File

@ -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<Obj>? get() = value?.let { WithAccess(it, isMutable) }
}