operators overriding

This commit is contained in:
Sergey Chernov 2026-01-07 09:33:10 +01:00
parent fe5dded7af
commit 2c0a6c7b34
24 changed files with 216 additions and 57 deletions

View File

@ -544,6 +544,99 @@ class Critical {
Attempting to override a `closed` member results in a compile-time error. Attempting to override a `closed` member results in a compile-time error.
## Operator Overloading
Lyng allows you to overload standard operators by defining specific named methods in your classes. When an operator expression is evaluated, Lyng delegates the operation to these methods if they are available.
### Binary Operators
To overload a binary operator, define the corresponding method that takes one argument:
| Operator | Method Name |
| :--- | :--- |
| `a + b` | `plus(other)` |
| `a - b` | `minus(other)` |
| `a * b` | `mul(other)` |
| `a / b` | `div(other)` |
| `a % b` | `mod(other)` |
| `a && b` | `logicalAnd(other)` |
| `a \|\| b` | `logicalOr(other)` |
| `a =~ b` | `operatorMatch(other)` |
| `a & b` | `bitAnd(other)` |
| `a \| b` | `bitOr(other)` |
| `a ^ b` | `bitXor(other)` |
| `a << b` | `shl(other)` |
| `a >> b` | `shr(other)` |
Example:
```lyng
class Vector(val x, val y) {
fun plus(other) = Vector(x + other.x, y + other.y)
override fun toString() = "Vector(${x}, ${y})"
}
val v1 = Vector(1, 2)
val v2 = Vector(3, 4)
assertEquals(Vector(4, 6), v1 + v2)
```
### Unary Operators
Unary operators are overloaded by defining methods with no arguments:
| Operator | Method Name |
| :--- | :--- |
| `-a` | `negate()` |
| `!a` | `logicalNot()` |
| `~a` | `bitNot()` |
### Assignment Operators
Assignment operators like `+=` first attempt to call a specific assignment method. If that method is not defined, they fall back to a combination of the binary operator and a regular assignment (e.g., `a = a + b`).
| Operator | Method Name | Fallback |
| :--- | :--- | :--- |
| `a += b` | `plusAssign(other)` | `a = a + b` |
| `a -= b` | `minusAssign(other)` | `a = a - b` |
| `a *= b` | `mulAssign(other)` | `a = a * b` |
| `a /= b` | `divAssign(other)` | `a = a / b` |
| `a %= b` | `modAssign(other)` | `a = a % b` |
Example of in-place mutation:
```lyng
class Counter(var value) {
fun plusAssign(n) {
value = value + n
}
}
val c = Counter(10)
c += 5
assertEquals(15, c.value)
```
### Comparison Operators
Comparison operators use `compareTo` and `equals`.
| Operator | Method Name |
| :--- | :--- |
| `a == b`, `a != b` | `equals(other)` |
| `<`, `>`, `<=`, `>=`, `<=>` | `compareTo(other)` |
- `compareTo` should return:
- `0` if `a == b`
- A negative integer if `a < b`
- A positive integer if `a > b`
- The `<=>` (shuttle) operator returns the result of `compareTo` directly.
- `equals` returns a `Bool`. If `equals` is not explicitly defined, Lyng falls back to `compareTo(other) == 0`.
> **Note**: Methods that are already defined in the base `Obj` class (like `equals`, `toString`, or `contains`) require the `override` keyword when redefined in your class or as an extension. Other operator methods (like `plus` or `negate`) do not require `override` unless they are already present in your class's hierarchy.
### Increment and Decrement
`++` and `--` operators are implemented using `plus(1)` or `minus(1)` combined with an assignment back to the variable. If the variable is a field or local variable, it will be updated with the result of the operation.
Compatibility notes: Compatibility notes:
- Existing single‑inheritance code continues to work unchanged; its resolution order reduces to the single base. - Existing single‑inheritance code continues to work unchanged; its resolution order reduces to the single base.

View File

@ -1351,8 +1351,11 @@ class Compiler(
val isMember = (codeContexts.lastOrNull() is CodeContext.ClassBody) val isMember = (codeContexts.lastOrNull() is CodeContext.ClassBody)
if (!isMember && (isOverride || isClosed)) if (!isMember && isClosed)
throw ScriptError(currentToken.pos, "modifiers override and closed are only allowed for class members") throw ScriptError(currentToken.pos, "modifier closed is only allowed for class members")
if (!isMember && isOverride && currentToken.value != "fun" && currentToken.value != "fn")
throw ScriptError(currentToken.pos, "modifier override outside class is only allowed for extension functions")
if (!isMember && isAbstract && currentToken.value != "class") if (!isMember && isAbstract && currentToken.value != "class")
throw ScriptError(currentToken.pos, "modifier abstract at top level is only allowed for classes") throw ScriptError(currentToken.pos, "modifier abstract at top level is only allowed for classes")

View File

@ -143,11 +143,17 @@ open class Obj {
open suspend fun compareTo(scope: Scope, other: Obj): Int { open suspend fun compareTo(scope: Scope, other: Obj): Int {
if (other === this) return 0 if (other === this) return 0
if (other === ObjNull || other === ObjUnset || other === ObjVoid) return 2 if (other === ObjNull || other === ObjUnset || other === ObjVoid) return 2
scope.raiseNotImplemented() return invokeInstanceMethod(scope, "compareTo", Arguments(other)) {
scope.raiseNotImplemented("compareTo for ${objClass.className}")
}.cast<ObjInt>(scope).toInt()
} }
open suspend fun equals(scope: Scope, other: Obj): Boolean { open suspend fun equals(scope: Scope, other: Obj): Boolean {
if (other === this) return true if (other === this) return true
val m = objClass.getInstanceMemberOrNull("equals") ?: scope.findExtension(objClass, "equals")
if (m != null) {
return invokeInstanceMethod(scope, "equals", Arguments(other)).toBool()
}
return try { return try {
compareTo(scope, other) == 0 compareTo(scope, other) == 0
} catch (e: ExecutionError) { } catch (e: ExecutionError) {
@ -225,46 +231,66 @@ open class Obj {
* Class of the object: definition of member functions (top-level), etc. * Class of the object: definition of member functions (top-level), etc.
* Note that using lazy allows to avoid endless recursion here * Note that using lazy allows to avoid endless recursion here
*/ */
open val objClass: ObjClass = rootObjectType open val objClass: ObjClass get() = rootObjectType
open suspend fun plus(scope: Scope, other: Obj): Obj { open suspend fun plus(scope: Scope, other: Obj): Obj {
scope.raiseNotImplemented() return invokeInstanceMethod(scope, "plus", Arguments(other)) {
scope.raiseNotImplemented("plus for ${objClass.className}")
}
} }
open suspend fun minus(scope: Scope, other: Obj): Obj { open suspend fun minus(scope: Scope, other: Obj): Obj {
scope.raiseNotImplemented() return invokeInstanceMethod(scope, "minus", Arguments(other)) {
scope.raiseNotImplemented("minus for ${objClass.className}")
}
} }
open suspend fun negate(scope: Scope): Obj { open suspend fun negate(scope: Scope): Obj {
scope.raiseNotImplemented() return invokeInstanceMethod(scope, "negate", Arguments.EMPTY) {
scope.raiseNotImplemented("negate for ${objClass.className}")
}
} }
open suspend fun mul(scope: Scope, other: Obj): Obj { open suspend fun mul(scope: Scope, other: Obj): Obj {
scope.raiseNotImplemented() return invokeInstanceMethod(scope, "mul", Arguments(other)) {
scope.raiseNotImplemented("mul for ${objClass.className}")
}
} }
open suspend fun div(scope: Scope, other: Obj): Obj { open suspend fun div(scope: Scope, other: Obj): Obj {
scope.raiseNotImplemented() return invokeInstanceMethod(scope, "div", Arguments(other)) {
scope.raiseNotImplemented("div for ${objClass.className}")
}
} }
open suspend fun mod(scope: Scope, other: Obj): Obj { open suspend fun mod(scope: Scope, other: Obj): Obj {
scope.raiseNotImplemented() return invokeInstanceMethod(scope, "mod", Arguments(other)) {
scope.raiseNotImplemented("mod for ${objClass.className}")
}
} }
open suspend fun logicalNot(scope: Scope): Obj { open suspend fun logicalNot(scope: Scope): Obj {
scope.raiseNotImplemented() return invokeInstanceMethod(scope, "logicalNot", Arguments.EMPTY) {
scope.raiseNotImplemented("logicalNot for ${objClass.className}")
}
} }
open suspend fun logicalAnd(scope: Scope, other: Obj): Obj { open suspend fun logicalAnd(scope: Scope, other: Obj): Obj {
scope.raiseNotImplemented() return invokeInstanceMethod(scope, "logicalAnd", Arguments(other)) {
scope.raiseNotImplemented("logicalAnd for ${objClass.className}")
}
} }
open suspend fun logicalOr(scope: Scope, other: Obj): Obj { open suspend fun logicalOr(scope: Scope, other: Obj): Obj {
scope.raiseNotImplemented() return invokeInstanceMethod(scope, "logicalOr", Arguments(other)) {
scope.raiseNotImplemented("logicalOr for ${objClass.className}")
}
} }
open suspend fun operatorMatch(scope: Scope, other: Obj): Obj { open suspend fun operatorMatch(scope: Scope, other: Obj): Obj {
scope.raiseNotImplemented() return invokeInstanceMethod(scope, "operatorMatch", Arguments(other)) {
scope.raiseNotImplemented("operatorMatch for ${objClass.className}")
}
} }
open suspend fun operatorNotMatch(scope: Scope, other: Obj): Obj { open suspend fun operatorNotMatch(scope: Scope, other: Obj): Obj {
@ -273,27 +299,39 @@ open class Obj {
// Bitwise ops default (override in numeric types that support them) // Bitwise ops default (override in numeric types that support them)
open suspend fun bitAnd(scope: Scope, other: Obj): Obj { open suspend fun bitAnd(scope: Scope, other: Obj): Obj {
scope.raiseNotImplemented() return invokeInstanceMethod(scope, "bitAnd", Arguments(other)) {
scope.raiseNotImplemented("bitAnd for ${objClass.className}")
}
} }
open suspend fun bitOr(scope: Scope, other: Obj): Obj { open suspend fun bitOr(scope: Scope, other: Obj): Obj {
scope.raiseNotImplemented() return invokeInstanceMethod(scope, "bitOr", Arguments(other)) {
scope.raiseNotImplemented("bitOr for ${objClass.className}")
}
} }
open suspend fun bitXor(scope: Scope, other: Obj): Obj { open suspend fun bitXor(scope: Scope, other: Obj): Obj {
scope.raiseNotImplemented() return invokeInstanceMethod(scope, "bitXor", Arguments(other)) {
scope.raiseNotImplemented("bitXor for ${objClass.className}")
}
} }
open suspend fun shl(scope: Scope, other: Obj): Obj { open suspend fun shl(scope: Scope, other: Obj): Obj {
scope.raiseNotImplemented() return invokeInstanceMethod(scope, "shl", Arguments(other)) {
scope.raiseNotImplemented("shl for ${objClass.className}")
}
} }
open suspend fun shr(scope: Scope, other: Obj): Obj { open suspend fun shr(scope: Scope, other: Obj): Obj {
scope.raiseNotImplemented() return invokeInstanceMethod(scope, "shr", Arguments(other)) {
scope.raiseNotImplemented("shr for ${objClass.className}")
}
} }
open suspend fun bitNot(scope: Scope): Obj { open suspend fun bitNot(scope: Scope): Obj {
scope.raiseNotImplemented() return invokeInstanceMethod(scope, "bitNot", Arguments.EMPTY) {
scope.raiseNotImplemented("bitNot for ${objClass.className}")
}
} }
open suspend fun assign(scope: Scope, other: Obj): Obj? = null open suspend fun assign(scope: Scope, other: Obj): Obj? = null
@ -305,15 +343,43 @@ open class Obj {
* if( the operation is not defined, it returns null and the compiler would try * if( the operation is not defined, it returns null and the compiler would try
* to generate it as 'this = this + other', reassigning its variable * to generate it as 'this = this + other', reassigning its variable
*/ */
open suspend fun plusAssign(scope: Scope, other: Obj): Obj? = null open suspend fun plusAssign(scope: Scope, other: Obj): Obj? {
val m = objClass.getInstanceMemberOrNull("plusAssign") ?: scope.findExtension(objClass, "plusAssign")
return if (m != null) {
invokeInstanceMethod(scope, "plusAssign", Arguments(other))
} else null
}
/** /**
* `-=` operations, see [plusAssign] * `-=` operations, see [plusAssign]
*/ */
open suspend fun minusAssign(scope: Scope, other: Obj): Obj? = null open suspend fun minusAssign(scope: Scope, other: Obj): Obj? {
open suspend fun mulAssign(scope: Scope, other: Obj): Obj? = null val m = objClass.getInstanceMemberOrNull("minusAssign") ?: scope.findExtension(objClass, "minusAssign")
open suspend fun divAssign(scope: Scope, other: Obj): Obj? = null return if (m != null) {
open suspend fun modAssign(scope: Scope, other: Obj): Obj? = null invokeInstanceMethod(scope, "minusAssign", Arguments(other))
} else null
}
open suspend fun mulAssign(scope: Scope, other: Obj): Obj? {
val m = objClass.getInstanceMemberOrNull("mulAssign") ?: scope.findExtension(objClass, "mulAssign")
return if (m != null) {
invokeInstanceMethod(scope, "mulAssign", Arguments(other))
} else null
}
open suspend fun divAssign(scope: Scope, other: Obj): Obj? {
val m = objClass.getInstanceMemberOrNull("divAssign") ?: scope.findExtension(objClass, "divAssign")
return if (m != null) {
invokeInstanceMethod(scope, "divAssign", Arguments(other))
} else null
}
open suspend fun modAssign(scope: Scope, other: Obj): Obj? {
val m = objClass.getInstanceMemberOrNull("modAssign") ?: scope.findExtension(objClass, "modAssign")
return if (m != null) {
invokeInstanceMethod(scope, "modAssign", Arguments(other))
} else null
}
open suspend fun getAndIncrement(scope: Scope): Obj { open suspend fun getAndIncrement(scope: Scope): Obj {
scope.raiseNotImplemented() scope.raiseNotImplemented()

View File

@ -23,7 +23,7 @@ import net.sergeych.lynon.BitArray
class ObjBitBuffer(val bitArray: BitArray) : Obj() { class ObjBitBuffer(val bitArray: BitArray) : Obj() {
override val objClass = type override val objClass get() = type
override suspend fun getAt(scope: Scope, index: Obj): Obj { override suspend fun getAt(scope: Scope, index: Obj): Obj {
return bitArray[index.toLong()].toObj() return bitArray[index.toLong()].toObj()

View File

@ -37,7 +37,7 @@ data class ObjBool(val value: Boolean) : Obj() {
override fun toString(): String = value.toString() override fun toString(): String = value.toString()
override val objClass: ObjClass = type override val objClass: ObjClass get() = type
override suspend fun logicalNot(scope: Scope): Obj = ObjBool(!value) override suspend fun logicalNot(scope: Scope): Obj = ObjBool(!value)

View File

@ -34,7 +34,7 @@ import kotlin.math.min
open class ObjBuffer(val byteArray: UByteArray) : Obj() { open class ObjBuffer(val byteArray: UByteArray) : Obj() {
override val objClass: ObjClass = type override val objClass: ObjClass get() = type
val hex by lazy { byteArray.encodeToHex("")} val hex by lazy { byteArray.encodeToHex("")}
val base64 by lazy { byteArray.toByteArray().encodeToBase64Url()} val base64 by lazy { byteArray.toByteArray().encodeToBase64Url()}

View File

@ -23,7 +23,7 @@ import net.sergeych.lyng.miniast.type
class ObjChar(val value: Char): Obj() { class ObjChar(val value: Char): Obj() {
override val objClass: ObjClass = type override val objClass: ObjClass get() = type
override suspend fun compareTo(scope: Scope, other: Obj): Int = override suspend fun compareTo(scope: Scope, other: Obj): Int =
(other as? ObjChar)?.let { value.compareTo(it.value) } ?: -1 (other as? ObjChar)?.let { value.compareTo(it.value) } ?: -1

View File

@ -25,7 +25,7 @@ import net.sergeych.lyng.miniast.type
class ObjCompletableDeferred(val completableDeferred: CompletableDeferred<Obj>): ObjDeferred(completableDeferred) { class ObjCompletableDeferred(val completableDeferred: CompletableDeferred<Obj>): ObjDeferred(completableDeferred) {
override val objClass = type override val objClass get() = type
companion object { companion object {
val type = object: ObjClass("CompletableDeferred", ObjDeferred.type){ val type = object: ObjClass("CompletableDeferred", ObjDeferred.type){

View File

@ -24,7 +24,7 @@ import net.sergeych.lyng.miniast.type
open class ObjDeferred(val deferred: Deferred<Obj>): Obj() { open class ObjDeferred(val deferred: Deferred<Obj>): Obj() {
override val objClass = type override val objClass get() = type
companion object { companion object {
val type = object: ObjClass("Deferred"){ val type = object: ObjClass("Deferred"){

View File

@ -29,7 +29,7 @@ import kotlin.time.Duration.Companion.seconds
import kotlin.time.DurationUnit import kotlin.time.DurationUnit
class ObjDuration(val duration: Duration) : Obj() { class ObjDuration(val duration: Duration) : Obj() {
override val objClass: ObjClass = type override val objClass: ObjClass get() = type
override fun toString(): String { override fun toString(): String {
return duration.toString() return duration.toString()

View File

@ -23,7 +23,7 @@ import net.sergeych.lyng.Scope
import net.sergeych.lyng.Statement import net.sergeych.lyng.Statement
class ObjDynamicContext(val delegate: ObjDynamic) : Obj() { class ObjDynamicContext(val delegate: ObjDynamic) : Obj() {
override val objClass: ObjClass = type override val objClass: ObjClass get() = type
companion object { companion object {
val type = ObjClass("DelegateContext").apply { val type = ObjClass("DelegateContext").apply {
@ -54,7 +54,7 @@ class ObjDynamicContext(val delegate: ObjDynamic) : Obj() {
*/ */
open class ObjDynamic(var readCallback: Statement? = null, var writeCallback: Statement? = null) : Obj() { open class ObjDynamic(var readCallback: Statement? = null, var writeCallback: Statement? = null) : Obj() {
override val objClass: ObjClass = type override val objClass: ObjClass get() = type
// Capture the lexical scope used to build this dynamic so callbacks can see outer locals // Capture the lexical scope used to build this dynamic so callbacks can see outer locals
internal var builderScope: Scope? = null internal var builderScope: Scope? = null

View File

@ -35,7 +35,7 @@ import kotlin.coroutines.cancellation.CancellationException
class ObjFlowBuilder(val output: SendChannel<Obj>) : Obj() { class ObjFlowBuilder(val output: SendChannel<Obj>) : Obj() {
override val objClass = type override val objClass get() = type
companion object { companion object {
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
@ -91,7 +91,7 @@ private fun createLyngFlowInput(scope: Scope, producer: Statement): ReceiveChann
class ObjFlow(val producer: Statement, val scope: Scope) : Obj() { class ObjFlow(val producer: Statement, val scope: Scope) : Obj() {
override val objClass = type override val objClass get() = type
companion object { companion object {
val type = object : ObjClass("Flow", ObjIterable) { val type = object : ObjClass("Flow", ObjIterable) {
@ -119,7 +119,7 @@ class ObjFlow(val producer: Statement, val scope: Scope) : Obj() {
class ObjFlowIterator(val producer: Statement) : Obj() { class ObjFlowIterator(val producer: Statement) : Obj() {
override val objClass: ObjClass = type override val objClass: ObjClass get() = type
private var channel: ReceiveChannel<Obj>? = null private var channel: ReceiveChannel<Obj>? = null

View File

@ -42,9 +42,6 @@ class ObjInstanceClass(val name: String, vararg parents: ObjClass) : ObjClass(na
} }
init { init {
addFn("toString", true) {
thisObj.toString(this, true)
}
} }
} }

View File

@ -29,7 +29,7 @@ import net.sergeych.lyng.Scope
*/ */
class ObjKotlinIterator(val iterator: Iterator<Any?>) : Obj() { class ObjKotlinIterator(val iterator: Iterator<Any?>) : Obj() {
override val objClass = type override val objClass get() = type
companion object { companion object {
val type = ObjClass("KotlinIterator", ObjIterator).apply { val type = ObjClass("KotlinIterator", ObjIterator).apply {
@ -46,7 +46,7 @@ class ObjKotlinIterator(val iterator: Iterator<Any?>) : Obj() {
*/ */
class ObjKotlinObjIterator(val iterator: Iterator<Obj>) : Obj() { class ObjKotlinObjIterator(val iterator: Iterator<Obj>) : Obj() {
override val objClass = type override val objClass get() = type
companion object { companion object {
val type = ObjClass("KotlinIterator", ObjIterator).apply { val type = ObjClass("KotlinIterator", ObjIterator).apply {

View File

@ -56,7 +56,7 @@ class ObjMapEntry(val key: Obj, val value: Obj) : Obj() {
return ObjString("(${key.toString(scope).value} => ${value.toString(scope).value})") return ObjString("(${key.toString(scope).value} => ${value.toString(scope).value})")
} }
override val objClass = type override val objClass get() = type
override suspend fun serialize(scope: Scope, encoder: LynonEncoder, lynonType: LynonType?) { override suspend fun serialize(scope: Scope, encoder: LynonEncoder, lynonType: LynonType?) {
encoder.encodeAny(scope,key) encoder.encodeAny(scope,key)
@ -106,7 +106,7 @@ class ObjMapEntry(val key: Obj, val value: Obj) : Obj() {
class ObjMap(val map: MutableMap<Obj, Obj> = mutableMapOf()) : Obj() { class ObjMap(val map: MutableMap<Obj, Obj> = mutableMapOf()) : Obj() {
override val objClass = type override val objClass get() = type
override suspend fun getAt(scope: Scope, index: Obj): Obj = override suspend fun getAt(scope: Scope, index: Obj): Obj =
map.get(index) ?: ObjNull map.get(index) ?: ObjNull

View File

@ -26,7 +26,7 @@ import net.sergeych.lyng.miniast.addFnDoc
import net.sergeych.lyng.miniast.type import net.sergeych.lyng.miniast.type
class ObjMutex(val mutex: Mutex): Obj() { class ObjMutex(val mutex: Mutex): Obj() {
override val objClass = type override val objClass get() = type
companion object { companion object {
val type = object: ObjClass("Mutex") { val type = object: ObjClass("Mutex") {

View File

@ -27,7 +27,7 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob
val isOpenStart by lazy { start == null || start.isNull } val isOpenStart by lazy { start == null || start.isNull }
val isOpenEnd by lazy { end == null || end.isNull } val isOpenEnd by lazy { end == null || end.isNull }
override val objClass: ObjClass = type override val objClass: ObjClass get() = type
override suspend fun defaultToString(scope: Scope): ObjString { override suspend fun defaultToString(scope: Scope): ObjString {
val result = StringBuilder() val result = StringBuilder()

View File

@ -26,7 +26,7 @@ class ObjRangeIterator(val self: ObjRange) : Obj() {
private var lastIndex = 0 private var lastIndex = 0
private var isCharRange: Boolean = false private var isCharRange: Boolean = false
override val objClass: ObjClass = type override val objClass: ObjClass get() = type
fun Scope.init() { fun Scope.init() {
val s = self.start val s = self.start
@ -84,7 +84,7 @@ class ObjFastIntRangeIterator(private val start: Int, private val endExclusive:
private var cur: Int = start private var cur: Int = start
override val objClass: ObjClass = type override val objClass: ObjClass get() = type
fun hasNext(): Boolean = cur < endExclusive fun hasNext(): Boolean = cur < endExclusive

View File

@ -37,7 +37,7 @@ data class ObjReal(val value: Double) : Obj(), Numeric {
override val toObjInt: ObjInt get() = ObjInt.of(longValue) override val toObjInt: ObjInt get() = ObjInt.of(longValue)
override val toObjReal: ObjReal get() = this override val toObjReal: ObjReal get() = this
override val objClass: ObjClass = type override val objClass: ObjClass get() = type
override fun byValueCopy(): Obj = this override fun byValueCopy(): Obj = this

View File

@ -26,7 +26,7 @@ import net.sergeych.lyng.miniast.addFnDoc
import net.sergeych.lyng.miniast.type import net.sergeych.lyng.miniast.type
class ObjRegex(val regex: Regex) : Obj() { class ObjRegex(val regex: Regex) : Obj() {
override val objClass = type override val objClass get() = type
override suspend fun operatorMatch(scope: Scope, other: Obj): Obj { override suspend fun operatorMatch(scope: Scope, other: Obj): Obj {
return regex.find(other.cast<ObjString>(scope).value)?.let { return regex.find(other.cast<ObjString>(scope).value)?.let {
@ -81,7 +81,7 @@ class ObjRegex(val regex: Regex) : Obj() {
} }
class ObjRegexMatch(val match: MatchResult) : Obj() { class ObjRegexMatch(val match: MatchResult) : Obj() {
override val objClass = type override val objClass get() = type
val objGroups: ObjList by lazy { val objGroups: ObjList by lazy {
// Use groupValues so that index 0 is the whole match and subsequent indices are capturing groups, // Use groupValues so that index 0 is the whole match and subsequent indices are capturing groups,

View File

@ -81,7 +81,7 @@ class RingBuffer<T>(val maxSize: Int) : Iterable<T> {
class ObjRingBuffer(val capacity: Int) : Obj() { class ObjRingBuffer(val capacity: Int) : Obj() {
val buffer = RingBuffer<Obj>(capacity) val buffer = RingBuffer<Obj>(capacity)
override val objClass: ObjClass = type override val objClass: ObjClass get() = type
override suspend fun plusAssign(scope: Scope, other: Obj): Obj { override suspend fun plusAssign(scope: Scope, other: Obj): Obj {
buffer.add(other.byValueCopy()) buffer.add(other.byValueCopy())

View File

@ -28,7 +28,7 @@ import net.sergeych.lynon.LynonType
class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() { class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
override val objClass = type override val objClass get() = type
override suspend fun contains(scope: Scope, other: Obj): Boolean { override suspend fun contains(scope: Scope, other: Obj): Boolean {
return set.contains(other) return set.contains(other)

View File

@ -4386,7 +4386,7 @@ class ScriptTest {
""" """
class A(x,y) class A(x,y)
class B(x,y) { class B(x,y) {
fun toString() { override fun toString() {
"B(%d,%d)"(x,y) "B(%d,%d)"(x,y)
} }
} }
@ -4394,7 +4394,7 @@ class ScriptTest {
assertEquals("B(1,2)", B(1,2).toString()) assertEquals("B(1,2)", B(1,2).toString())
assertEquals("A(x=1,y=2)", A(1,2).toString()) assertEquals("A(x=1,y=2)", A(1,2).toString())
// now tricky part: this _should_ cakk custom toString() // now tricky part: this _should_ call custom toString()
assertEquals(":B(1,2)", ":" + B(1,2).toString()) assertEquals(":B(1,2)", ":" + B(1,2).toString())
// and this must be exactly same: // and this must be exactly same:
assertEquals(":B(1,2)", ":" + B(1,2)) assertEquals(":B(1,2)", ":" + B(1,2))

View File

@ -235,7 +235,7 @@ fun Iterable.flatMap(transform): List {
} }
/* Return string representation like [a,b,c]. */ /* Return string representation like [a,b,c]. */
fun List.toString() { override fun List.toString() {
"[" + joinToString(",") + "]" "[" + joinToString(",") + "]"
} }
@ -257,7 +257,7 @@ class StackTraceEntry(
val sourceString: String val sourceString: String
) { ) {
/* Formatted representation: source:line:column: text. */ /* Formatted representation: source:line:column: text. */
fun toString() { override fun toString() {
"%s:%d:%d: %s"(sourceName, line, column, sourceString.trim()) "%s:%d:%d: %s"(sourceName, line, column, sourceString.trim())
} }
} }