From f885300d1847c7518af7f8a8ce95113027c56649 Mon Sep 17 00:00:00 2001 From: sergeych Date: Wed, 28 May 2025 14:01:44 +0400 Subject: [PATCH] converted namespaces to objects (coming to static objects, singletons) --- docs/OOP.md | 2 +- .../kotlin/net/sergeych/ling/Compiler.kt | 27 +++++--- .../kotlin/net/sergeych/ling/Context.kt | 5 +- .../kotlin/net/sergeych/ling/Obj.kt | 61 +++++++++++-------- .../kotlin/net/sergeych/ling/Script.kt | 4 +- 5 files changed, 58 insertions(+), 41 deletions(-) diff --git a/docs/OOP.md b/docs/OOP.md index d7b8e3b..14ab2e6 100644 --- a/docs/OOP.md +++ b/docs/OOP.md @@ -40,7 +40,7 @@ Class is the object, naturally, with class: Classes can be compared: - println(3.14::class == 2.21::class) + println(1.21::class == Math.PI::class) println(3.14::class == 1::class) println(π::class) >>> true diff --git a/library/src/commonMain/kotlin/net/sergeych/ling/Compiler.kt b/library/src/commonMain/kotlin/net/sergeych/ling/Compiler.kt index cc11ec2..618d62b 100644 --- a/library/src/commonMain/kotlin/net/sergeych/ling/Compiler.kt +++ b/library/src/commonMain/kotlin/net/sergeych/ling/Compiler.kt @@ -89,14 +89,14 @@ class CompilerContext(val tokens: List) : ListIterator by tokens.l } else true } - fun ifNextIs(typeId: Token.Type, f: (Token) -> Unit): IfScope { + fun ifNextIs(typeId: Token.Type, f: (Token) -> Unit): Boolean { val t = next() return if (t.type == typeId) { f(t) - IfScope(true) + true } else { previous() - IfScope(false) + false } } @@ -277,22 +277,28 @@ class Compiler { Token.Type.DOT -> { operand?.let { left -> // dotcall: calling method on the operand, if next is ID, "(" - cc.ifNextIs(Token.Type.ID) { methodToken -> + var isCall = false + val next = cc.next() + if( next.type == Token.Type.ID) { cc.ifNextIs(Token.Type.LPAREN) { // instance method call val args = parseArgs(cc) + isCall = true operand = Accessor { context -> - context.pos = methodToken.pos + context.pos = next.pos val v = left.getter(context) v.callInstanceMethod( context, - methodToken.value, + next.value, args.toArguments() ) } } - }.otherwise { - TODO("implement member access") + } + if (!isCall) { + operand = Accessor { context -> + left.getter(context).readField(context, next.value) + } } } ?: throw ScriptError(t.pos, "Expecting expression before dot") } @@ -489,7 +495,10 @@ class Compiler { else -> { Accessor({ it.pos = t.pos - it.get(t.value)?.value ?: it.raiseError("symbol not defined: '${t.value}'") + it.get(t.value)?.value?.also { + println("got ${t.value} -> $it") + } + ?: it.raiseError("symbol not defined: '${t.value}'") }) { ctx, newValue -> ctx.get(t.value)?.let { stored -> ctx.pos = t.pos diff --git a/library/src/commonMain/kotlin/net/sergeych/ling/Context.kt b/library/src/commonMain/kotlin/net/sergeych/ling/Context.kt index b2245b4..eea92b9 100644 --- a/library/src/commonMain/kotlin/net/sergeych/ling/Context.kt +++ b/library/src/commonMain/kotlin/net/sergeych/ling/Context.kt @@ -37,14 +37,13 @@ class Context( objects.put(name, StoredObj(value, isMutable)) } - fun getOrCreateNamespace(name: String) = + fun getOrCreateNamespace(name: String): ObjNamespace = (objects.getOrPut(name) { StoredObj( - ObjNamespace(name, copy(pos)), + ObjNamespace(name), isMutable = false ) }.value as ObjNamespace) - .context inline fun addFn(vararg names: String, crossinline fn: suspend Context.() -> T) { val newFn = object : Statement() { diff --git a/library/src/commonMain/kotlin/net/sergeych/ling/Obj.kt b/library/src/commonMain/kotlin/net/sergeych/ling/Obj.kt index ab94e8a..a8b76d0 100644 --- a/library/src/commonMain/kotlin/net/sergeych/ling/Obj.kt +++ b/library/src/commonMain/kotlin/net/sergeych/ling/Obj.kt @@ -17,7 +17,7 @@ data class Accessor( ) { constructor(getter: suspend (Context) -> Obj) : this(getter, null) - fun setter(pos: Pos) = setterOrNull ?: throw ScriptError(pos,"can't assign value") + fun setter(pos: Pos) = setterOrNull ?: throw ScriptError(pos, "can't assign value") } sealed class Obj { @@ -26,9 +26,12 @@ sealed class Obj { private val monitor = Mutex() // members: fields most often - internal val members = mutableMapOf>() + private val members = mutableMapOf>() + + // private val memberMutex = Mutex() private val parentInstances = listOf() + /** * Get instance member traversing the hierarchy if needed. Its meaning is different for different objects. */ @@ -38,15 +41,18 @@ sealed class Obj { return null } - fun getInstanceMember(atPos: Pos, name: String): Obj = getInstanceMemberOrNull(name) - ?: throw ScriptError(atPos,"symbol doesn't exist: $name") + fun getInstanceMember(atPos: Pos, name: String): Obj = + getInstanceMemberOrNull(name) + ?: throw ScriptError(atPos, "symbol doesn't exist: $name") - suspend fun callInstanceMethod(context: Context, name: String,args: Arguments): Obj { - // instance _methods_ are our ObjClass instance: + suspend fun callInstanceMethod(context: Context, name: String, args: Arguments): Obj = + // instance _methods_ are our ObjClass instance: + // note that getInstanceMember traverses the hierarchy + // instance _methods_ are our ObjClass instance: + // note that getInstanceMember traverses the hierarchy +// instance _methods_ are our ObjClass instance: // note that getInstanceMember traverses the hierarchy - return objClass.getInstanceMember(context.pos,name).invoke(context, this, args) - } - + objClass.getInstanceMember(context.pos, name).invoke(context, this, args) // methods that to override @@ -99,23 +105,31 @@ sealed class Obj { suspend fun sync(block: () -> T): T = monitor.withLock { block() } - open suspend fun readField(context: Context, name: String): Obj { - context.raiseNotImplemented() + suspend fun readField(context: Context, name: String): Obj = getInstanceMember(context.pos, name) + + suspend fun writeField(context: Context, name: String, newValue: Obj) { + willMutate(context) + members[name]?.let { if (it.isMutable) it.value = newValue } + ?: context.raiseError("Can't reassign member: $name") } - open suspend fun writeField(context: Context,name: String, newValue: Obj) { - context.raiseNotImplemented() + fun createField(name: String, initialValue: Obj, isMutable: Boolean = false, pos: Pos = Pos.builtIn) { + if (name in members || parentInstances.any { name in it.members }) + throw ScriptError(pos, "$name is already defined in $objClass or one of its supertypes") + members[name] = WithAccess(initialValue, isMutable) } + fun addConst(name: String, value: Obj) = createField(name, value, isMutable = false) + open suspend fun callOn(context: Context): Obj { context.raiseNotImplemented() } - suspend fun invoke(context: Context, thisObj: Obj,args: Arguments): Obj = - callOn(context.copy(context.pos,args = args, newThisObj = thisObj)) + suspend fun invoke(context: Context, thisObj: Obj, args: Arguments): Obj = + callOn(context.copy(context.pos, args = args, newThisObj = thisObj)) - suspend fun invoke(context: Context,atPos: Pos, thisObj: Obj,args: Arguments): Obj = - callOn(context.copy(atPos,args = args,newThisObj = thisObj)) + suspend fun invoke(context: Context, atPos: Pos, thisObj: Obj, args: Arguments): Obj = + callOn(context.copy(atPos, args = args, newThisObj = thisObj)) companion object { @@ -202,7 +216,6 @@ fun Obj.toBool(): Boolean = (this as? ObjBool)?.value ?: throw IllegalArgumentException("cannot convert to boolean $this") - data class ObjReal(val value: Double) : Obj(), Numeric { override val asStr by lazy { ObjString(value.toString()) } override val longValue: Long by lazy { floor(value).toLong() } @@ -221,11 +234,11 @@ data class ObjReal(val value: Double) : Obj(), Numeric { companion object { val type: ObjClass = ObjClass("Real").apply { - members["roundToInt"] = WithAccess( + createField( + "roundToInt", statement(Pos.builtIn) { (it.thisObj as ObjReal).value.roundToLong().toObj() }, - false ) } } @@ -275,15 +288,10 @@ data class ObjBool(val value: Boolean) : Obj() { override fun toString(): String = value.toString() } -data class ObjNamespace(val name: String, val context: Context) : Obj() { +data class ObjNamespace(val name: String) : Obj() { override fun toString(): String { return "namespace ${name}" } - - override suspend fun readField(callerContext: Context,name: String): Obj { - return context[name]?.value ?: callerContext.raiseError("not found: $name") - } - } open class ObjError(val context: Context, val message: String) : Obj() { @@ -291,3 +299,4 @@ open class ObjError(val context: Context, val message: String) : Obj() { } class ObjNullPointerError(context: Context) : ObjError(context, "object is null") + diff --git a/library/src/commonMain/kotlin/net/sergeych/ling/Script.kt b/library/src/commonMain/kotlin/net/sergeych/ling/Script.kt index b652e57..659fd45 100644 --- a/library/src/commonMain/kotlin/net/sergeych/ling/Script.kt +++ b/library/src/commonMain/kotlin/net/sergeych/ling/Script.kt @@ -50,8 +50,8 @@ class Script( val z = pi.objClass println("PI class $z") addConst(pi, "π") - getOrCreateNamespace("Math").also { ns -> - ns.addConst(pi, "PI") + getOrCreateNamespace("Math").apply { + addConst( "PI", pi) } } }