diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjInt.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjInt.kt index 0671102..d4cb6ba 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjInt.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjInt.kt @@ -55,7 +55,11 @@ class ObjInt(val value: Long, override val isConst: Boolean = false) : Obj(), Nu override suspend fun compareTo(scope: Scope, other: Obj): Int { if (other !is Numeric) return -2 - return value.compareTo(other.doubleValue) + return if (other is ObjInt) { + value.compareTo(other.value) + } else { + doubleValue.compareTo(other.doubleValue) + } } override fun toString(): String = value.toString() @@ -192,4 +196,4 @@ class ObjInt(val value: Long, override val isConst: Boolean = false) : Obj(), Nu } fun Int.toObj() = ObjInt.of(this.toLong()) -fun Long.toObj() = ObjInt.of(this) \ No newline at end of file +fun Long.toObj() = ObjInt.of(this) diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRange.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRange.kt index 0c631d0..dc61c66 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRange.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRange.kt @@ -96,6 +96,30 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob if (other is ObjRange) return containsRange(scope, other) + if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) { + if (start is ObjInt && end is ObjInt && other is ObjInt) { + val s = start.value + val e = end.value + val v = other.value + if (v < s) return false + return if (isEndInclusive) v <= e else v < e + } + if (start is ObjChar && end is ObjChar && other is ObjChar) { + val s = start.value + val e = end.value + val v = other.value + if (v < s) return false + return if (isEndInclusive) v <= e else v < e + } + if (start is ObjString && end is ObjString && other is ObjString) { + val s = start.value + val e = end.value + val v = other.value + if (v < s) return false + return if (isEndInclusive) v <= e else v < e + } + } + if (start == null && end == null) return true if (start != null) { if (start.compareTo(scope, other) > 0) return false @@ -241,4 +265,3 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob } } } - diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt index 2dd0ae6..76e56d4 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt @@ -152,6 +152,90 @@ class BinaryOpRef(private val op: BinOp, private val left: ObjRef, private val r // Primitive fast paths for common cases (guarded by PerfFlags.PRIMITIVE_FASTOPS) if (PerfFlags.PRIMITIVE_FASTOPS) { + // Fast range equality: avoid compareTo/equals for ObjRange when possible + if ((op == BinOp.EQ || op == BinOp.NEQ) && a is ObjRange && b is ObjRange) { + val eq = (a.start == b.start && a.end == b.end) + if (PerfFlags.PIC_DEBUG_COUNTERS) PerfStats.primitiveFastOpsHit++ + return if (op == BinOp.EQ) { + if (eq) ObjTrue else ObjFalse + } else { + if (eq) ObjFalse else ObjTrue + } + } + // Fast membership for common containers + if (op == BinOp.IN || op == BinOp.NOTIN) { + val inResult: Boolean? = when (b) { + is ObjList -> { + if (a is ObjInt) { + var i = 0 + val sz = b.list.size + var found = false + while (i < sz) { + val v = b.list[i] + if (v is ObjInt && v.value == a.value) { + found = true + break + } + i++ + } + found + } else { + b.list.contains(a) + } + } + is ObjSet -> b.set.contains(a) + is ObjMap -> b.map.containsKey(a) + is ObjRange -> { + when (a) { + is ObjInt -> { + val s = b.start as? ObjInt + val e = b.end as? ObjInt + val v = a.value + if (s == null && e == null) null + else { + if (s != null && v < s.value) false + else if (e != null) if (b.isEndInclusive) v <= e.value else v < e.value else true + } + } + is ObjChar -> { + val s = b.start as? ObjChar + val e = b.end as? ObjChar + val v = a.value + if (s == null && e == null) null + else { + if (s != null && v < s.value) false + else if (e != null) if (b.isEndInclusive) v <= e.value else v < e.value else true + } + } + is ObjString -> { + val s = b.start as? ObjString + val e = b.end as? ObjString + val v = a.value + if (s == null && e == null) null + else { + if (s != null && v < s.value) false + else if (e != null) if (b.isEndInclusive) v <= e.value else v < e.value else true + } + } + else -> null + } + } + is ObjString -> when (a) { + is ObjString -> b.value.contains(a.value) + is ObjChar -> b.value.contains(a.value) + else -> null + } + else -> null + } + if (inResult != null) { + if (PerfFlags.PIC_DEBUG_COUNTERS) PerfStats.primitiveFastOpsHit++ + return if (op == BinOp.IN) { + if (inResult) ObjTrue else ObjFalse + } else { + if (inResult) ObjFalse else ObjTrue + } + } + } // Fast boolean ops when both operands are ObjBool if (a is ObjBool && b is ObjBool) { val r: Obj? = when (op) { @@ -604,7 +688,37 @@ class AssignOpRef( else -> null } if (inPlace != null) return inPlace.asReadonly - val result: Obj = when (op) { + val fast: Obj? = if (PerfFlags.PRIMITIVE_FASTOPS) { + when { + x is ObjInt && y is ObjInt -> { + val xv = x.value + val yv = y.value + when (op) { + BinOp.PLUS -> ObjInt.of(xv + yv) + BinOp.MINUS -> ObjInt.of(xv - yv) + BinOp.STAR -> ObjInt.of(xv * yv) + BinOp.SLASH -> if (yv != 0L) ObjInt.of(xv / yv) else null + BinOp.PERCENT -> if (yv != 0L) ObjInt.of(xv % yv) else null + else -> null + } + } + (x is ObjInt || x is ObjReal) && (y is ObjInt || y is ObjReal) -> { + val xv = if (x is ObjInt) x.doubleValue else (x as ObjReal).value + val yv = if (y is ObjInt) y.doubleValue else (y as ObjReal).value + when (op) { + BinOp.PLUS -> ObjReal.of(xv + yv) + BinOp.MINUS -> ObjReal.of(xv - yv) + BinOp.STAR -> ObjReal.of(xv * yv) + BinOp.SLASH -> ObjReal.of(xv / yv) + BinOp.PERCENT -> ObjReal.of(xv % yv) + else -> null + } + } + x is ObjString && op == BinOp.PLUS -> ObjString(x.value + y.toString()) + else -> null + } + } else null + val result: Obj = fast ?: when (op) { BinOp.PLUS -> x.plus(scope, y) BinOp.MINUS -> x.minus(scope, y) BinOp.STAR -> x.mul(scope, y) @@ -632,7 +746,15 @@ class IncDecRef( // We now treat numbers as immutable and always perform write-back via setAt. // This avoids issues where literals are shared and mutated in-place. // For post-inc: return ORIGINAL value; for pre-inc: return NEW value. - val result = if (isIncrement) v.plus(scope, one) else v.minus(scope, one) + val result = if (PerfFlags.PRIMITIVE_FASTOPS) { + when (v) { + is ObjInt -> if (isIncrement) ObjInt.of(v.value + 1L) else ObjInt.of(v.value - 1L) + is ObjReal -> if (isIncrement) ObjReal.of(v.value + 1.0) else ObjReal.of(v.value - 1.0) + else -> if (isIncrement) v.plus(scope, one) else v.minus(scope, one) + } + } else { + if (isIncrement) v.plus(scope, one) else v.minus(scope, one) + } target.setAt(atPos, scope, result) return (if (isPost) v else result).asReadonly } @@ -1246,8 +1368,8 @@ class IndexRef( val i = idx.toInt() return ObjChar(base.value[i]).asMutable } - // Map[String] fast path (common case); return ObjNull if absent - if (base is ObjMap && idx is ObjString) { + // Map[String/Int/Char] fast path (common cases); return ObjNull if absent + if (base is ObjMap && (idx is ObjString || idx is ObjInt || idx is ObjChar)) { val v = base.map[idx] ?: ObjNull return v.asMutable } @@ -1321,8 +1443,8 @@ class IndexRef( val i = idx.toInt() return ObjChar(base.value[i]) } - // Map[String] fast path - if (base is ObjMap && idx is ObjString) { + // Map[String/Int/Char] fast path + if (base is ObjMap && (idx is ObjString || idx is ObjInt || idx is ObjChar)) { return base.map[idx] ?: ObjNull } if (PerfFlags.INDEX_PIC) { @@ -1393,7 +1515,7 @@ class IndexRef( return } // Direct write fast path for ObjMap + ObjString - if (base is ObjMap && idx is ObjString) { + if (base is ObjMap && (idx is ObjString || idx is ObjInt || idx is ObjChar)) { base.map[idx] = newValue return }