extension methids

This commit is contained in:
sergeych committed 2025-06-20 03:43:58 +04:00
1 parent a8067d0a6b
commit eee6d75587
9 files changed
+104 -12

No files matched your search

@@ -1432,11 +1432,22 @@ class Compiler(
): Statement {
var t = cc.next()
val start = t.pos
val name = if (t.type != Token.Type.ID)
throw ScriptError(t.pos, "Expected identifier after 'fn'")
var extTypeName: String? = null
var name = if (t.type != Token.Type.ID)
throw ScriptError(t.pos, "Expected identifier after 'fun'")
else t.value
t = cc.next()
// Is extension?
if( t.type == Token.Type.DOT) {
extTypeName = name
t = cc.next()
if( t.type != Token.Type.ID)
throw ScriptError(t.pos, "illegal extension format: expected function name")
name = t.value
t = cc.next()
}
if (t.type != Token.Type.LPAREN)
throw ScriptError(t.pos, "Bad function definition: expected '(' after 'fn ${name}'")
@@ -1465,13 +1476,25 @@ class Compiler(
// load params from caller context
argsDeclaration.assignToContext(context, callerContext.args, defaultAccessType = AccessType.Val)
if( extTypeName != null ) {
context.thisObj = callerContext.thisObj
}
fnStatements.execute(context)
}
return statement(start) { context ->
// we added fn in the context. now we must save closure
// for the function
closure = context
context.addItem(name, false, fnBody, visibility)
extTypeName?.let { typeName ->
// class extension method
val type = context.get(typeName)?.value ?: context.raiseSymbolNotFound("class $typeName not found")
if( type !is ObjClass ) context.raiseClassCastError("$typeName is not the class instance")
type.addFn( name, isOpen = true) {
fnBody.execute(this)
}
}
// regular function/method
?: context.addItem(name, false, fnBody, visibility)
// as the function can be called from anywhere, we have
// saved the proper context in the closure
fnBody
@@ -72,6 +72,7 @@ open class Context(
else {
objects[name]
?: parent?.get(name)
?: thisObj.objClass.getInstanceMemberOrNull(name)
}
fun copy(pos: Pos, args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Context =
@@ -64,6 +64,9 @@ data class ObjReal(val value: Double) : Obj(), Numeric {
(it.thisObj as ObjReal).value.roundToLong().toObj()
},
)
addFn("toInt") {
ObjInt(thisAs<ObjReal>().value.toLong())
}
}
}
}
@@ -112,6 +112,7 @@ data class ObjString(val value: String) : Obj() {
thisAs<ObjString>().value.uppercase().let(::ObjString)
}
addFn("size") { ObjInt(thisAs<ObjString>().value.length.toLong()) }
addFn("toReal") { ObjReal(thisAs<ObjString>().value.toDouble())}
}
}
}
@@ -2284,4 +2284,31 @@ class ScriptTest {
""".trimIndent())
}
@Test
fun testExtend() = runTest() {
eval("""
fun Int.isEven() {
this % 2 == 0
}
fun Object.isInteger() {
when(this) {
is Int -> true
is Real -> toInt() == this
is String -> toReal().isInteger()
else -> false
}
}
assert( 4.isEven() )
assert( !5.isEven() )
assert( 12.isInteger() == true )
assert( 12.1.isInteger() == false )
assert( "5".isInteger() )
assert( ! "5.2".isInteger() )
""".trimIndent())
}
}