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: 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(3.14::class == 1::class)
println(π::class) println(π::class)
>>> true >>> true

View File

@ -89,14 +89,14 @@ class CompilerContext(val tokens: List<Token>) : ListIterator<Token> by tokens.l
} else true } else true
} }
fun ifNextIs(typeId: Token.Type, f: (Token) -> Unit): IfScope { fun ifNextIs(typeId: Token.Type, f: (Token) -> Unit): Boolean {
val t = next() val t = next()
return if (t.type == typeId) { return if (t.type == typeId) {
f(t) f(t)
IfScope(true) true
} else { } else {
previous() previous()
IfScope(false) false
} }
} }
@ -277,22 +277,28 @@ class Compiler {
Token.Type.DOT -> { Token.Type.DOT -> {
operand?.let { left -> operand?.let { left ->
// dotcall: calling method on the operand, if next is ID, "(" // 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) { cc.ifNextIs(Token.Type.LPAREN) {
// instance method call // instance method call
val args = parseArgs(cc) val args = parseArgs(cc)
isCall = true
operand = Accessor { context -> operand = Accessor { context ->
context.pos = methodToken.pos context.pos = next.pos
val v = left.getter(context) val v = left.getter(context)
v.callInstanceMethod( v.callInstanceMethod(
context, context,
methodToken.value, next.value,
args.toArguments() 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") } ?: throw ScriptError(t.pos, "Expecting expression before dot")
} }
@ -489,7 +495,10 @@ class Compiler {
else -> { else -> {
Accessor({ Accessor({
it.pos = t.pos 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, newValue ->
ctx.get(t.value)?.let { stored -> ctx.get(t.value)?.let { stored ->
ctx.pos = t.pos ctx.pos = t.pos

View File

@ -37,14 +37,13 @@ class Context(
objects.put(name, StoredObj(value, isMutable)) objects.put(name, StoredObj(value, isMutable))
} }
fun getOrCreateNamespace(name: String) = fun getOrCreateNamespace(name: String): ObjNamespace =
(objects.getOrPut(name) { (objects.getOrPut(name) {
StoredObj( StoredObj(
ObjNamespace(name, copy(pos)), ObjNamespace(name),
isMutable = false isMutable = false
) )
}.value as ObjNamespace) }.value as ObjNamespace)
.context
inline fun <reified T> addFn(vararg names: String, crossinline fn: suspend Context.() -> T) { inline fun <reified T> addFn(vararg names: String, crossinline fn: suspend Context.() -> T) {
val newFn = object : Statement() { val newFn = object : Statement() {

View File

@ -26,9 +26,12 @@ sealed class Obj {
private val monitor = Mutex() private val monitor = Mutex()
// members: fields most often // 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>() private val parentInstances = listOf<Obj>()
/** /**
* 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.
*/ */
@ -38,15 +41,18 @@ sealed class Obj {
return null return null
} }
fun getInstanceMember(atPos: Pos, name: String): Obj = getInstanceMemberOrNull(name) fun getInstanceMember(atPos: Pos, name: String): Obj =
getInstanceMemberOrNull(name)
?: throw ScriptError(atPos, "symbol doesn't exist: $name") ?: throw ScriptError(atPos, "symbol doesn't exist: $name")
suspend fun callInstanceMethod(context: Context, name: String,args: Arguments): Obj { suspend fun callInstanceMethod(context: Context, name: String, args: Arguments): Obj =
// instance _methods_ are our ObjClass instance: // instance _methods_ are our ObjClass instance:
// note that getInstanceMember traverses the hierarchy // note that getInstanceMember traverses the hierarchy
return objClass.getInstanceMember(context.pos,name).invoke(context, this, args) // instance _methods_ are our ObjClass instance:
} // note that getInstanceMember traverses the hierarchy
// instance _methods_ are our ObjClass instance:
// note that getInstanceMember traverses the hierarchy
objClass.getInstanceMember(context.pos, name).invoke(context, this, args)
// methods that to override // methods that to override
@ -99,14 +105,22 @@ sealed class Obj {
suspend fun <T> sync(block: () -> T): T = monitor.withLock { block() } suspend fun <T> sync(block: () -> T): T = monitor.withLock { block() }
open suspend fun readField(context: Context, name: String): Obj { suspend fun readField(context: Context, name: String): Obj = getInstanceMember(context.pos, name)
context.raiseNotImplemented()
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) { fun createField(name: String, initialValue: Obj, isMutable: Boolean = false, pos: Pos = Pos.builtIn) {
context.raiseNotImplemented() 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 { open suspend fun callOn(context: Context): Obj {
context.raiseNotImplemented() context.raiseNotImplemented()
} }
@ -202,7 +216,6 @@ fun Obj.toBool(): Boolean =
(this as? ObjBool)?.value ?: throw IllegalArgumentException("cannot convert to boolean $this") (this as? ObjBool)?.value ?: throw IllegalArgumentException("cannot convert to boolean $this")
data class ObjReal(val value: Double) : Obj(), Numeric { data class ObjReal(val value: Double) : Obj(), Numeric {
override val asStr by lazy { ObjString(value.toString()) } override val asStr by lazy { ObjString(value.toString()) }
override val longValue: Long by lazy { floor(value).toLong() } override val longValue: Long by lazy { floor(value).toLong() }
@ -221,11 +234,11 @@ data class ObjReal(val value: Double) : Obj(), Numeric {
companion object { companion object {
val type: ObjClass = ObjClass("Real").apply { val type: ObjClass = ObjClass("Real").apply {
members["roundToInt"] = WithAccess( createField(
"roundToInt",
statement(Pos.builtIn) { statement(Pos.builtIn) {
(it.thisObj as ObjReal).value.roundToLong().toObj() (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() 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 { override fun toString(): String {
return "namespace ${name}" 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() { 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") class ObjNullPointerError(context: Context) : ObjError(context, "object is null")

View File

@ -50,8 +50,8 @@ class Script(
val z = pi.objClass val z = pi.objClass
println("PI class $z") println("PI class $z")
addConst(pi, "π") addConst(pi, "π")
getOrCreateNamespace("Math").also { ns -> getOrCreateNamespace("Math").apply {
ns.addConst(pi, "PI") addConst( "PI", pi)
} }
} }
} }