converted namespaces to objects (coming to static objects, singletons)

This commit is contained in:
Sergey Chernov 2025-05-28 14:01:44 +04:00
parent d21544ca5d
commit f885300d18
5 changed files with 58 additions and 41 deletions

View File

@ -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

View File

@ -89,14 +89,14 @@ class CompilerContext(val tokens: List<Token>) : ListIterator<Token> 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

View File

@ -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 <reified T> addFn(vararg names: String, crossinline fn: suspend Context.() -> T) {
val newFn = object : Statement() {

View File

@ -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<String, WithAccess<Obj>>()
private val members = mutableMapOf<String, WithAccess<Obj>>()
// private val memberMutex = Mutex()
private val parentInstances = listOf<Obj>()
/**
* 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 <T> 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<Obj> { 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")

View File

@ -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)
}
}
}