improved vairable tracking, fixed plugin to wirk with 1.0.10, fixed lambda comparison
This commit is contained in:
1 parent
1e18a162c4
commit
0732202c80
12 files changed
+266
-28
No files matched your search
@@ -2569,7 +2569,21 @@ class Compiler(
|
||||
val pattern = ListLiteralRef(entries)
|
||||
|
||||
// Register all names in the pattern
|
||||
pattern.forEachVariable { name -> declareLocalName(name) }
|
||||
pattern.forEachVariableWithPos { name, namePos ->
|
||||
declareLocalName(name)
|
||||
val declRange = MiniRange(namePos, namePos)
|
||||
val node = MiniValDecl(
|
||||
range = declRange,
|
||||
name = name,
|
||||
mutable = isMutable,
|
||||
type = null,
|
||||
initRange = null,
|
||||
doc = pendingDeclDoc,
|
||||
nameStart = namePos
|
||||
)
|
||||
miniSink?.onValDecl(node)
|
||||
}
|
||||
pendingDeclDoc = null
|
||||
|
||||
val eqToken = cc.next()
|
||||
if (eqToken.type != Token.Type.ASSIGN)
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Pure-Kotlin, PSI-free completion engine used for isolated tests and non-IDE harnesses.
|
||||
* Mirrors the IntelliJ MVP logic: MiniAst + BuiltinDocRegistry + lenient imports.
|
||||
@@ -270,6 +287,24 @@ object CompletionEngineLight {
|
||||
break
|
||||
}
|
||||
if (hasDigits) return if (hasDot || hasExp) "Real" else "Int"
|
||||
|
||||
// 3) this@Type or as Type
|
||||
val identRange = wordRangeAt(text, i + 1)
|
||||
if (identRange != null) {
|
||||
val ident = text.substring(identRange.first, identRange.second)
|
||||
// if it's "as Type", we want Type
|
||||
var k = prevNonWs(text, identRange.first - 1)
|
||||
if (k >= 1 && text[k] == 's' && text[k - 1] == 'a' && (k - 1 == 0 || !text[k - 2].isLetterOrDigit())) {
|
||||
return ident
|
||||
}
|
||||
// if it's "this@Type", we want Type
|
||||
if (k >= 0 && text[k] == '@') {
|
||||
val k2 = prevNonWs(text, k - 1)
|
||||
if (k2 >= 3 && text.substring(k2 - 3, k2 + 1) == "this") {
|
||||
return ident
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -180,6 +180,10 @@ class ObjInt(var value: Long, override val isConst: Boolean = false) : Obj(), Nu
|
||||
LynonType.IntSigned -> ObjInt(decoder.unpackSigned())
|
||||
else -> scope.raiseIllegalState("illegal type code for Int: $lynonType")
|
||||
}
|
||||
}.apply {
|
||||
addFn("toInt") {
|
||||
thisObj
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,14 @@ sealed interface ObjRef {
|
||||
* Used for declaring local variables in destructuring.
|
||||
*/
|
||||
fun forEachVariable(block: (String) -> Unit) {}
|
||||
|
||||
/**
|
||||
* Calls [block] for each variable name that this reference targets for writing,
|
||||
* including its source position if available.
|
||||
*/
|
||||
fun forEachVariableWithPos(block: (String, Pos) -> Unit) {
|
||||
forEachVariable { block(it, Pos.UNKNOWN) }
|
||||
}
|
||||
}
|
||||
|
||||
/** Runtime-computed read-only reference backed by a lambda. */
|
||||
@@ -1225,6 +1233,10 @@ class LocalVarRef(private val name: String, private val atPos: Pos) : ObjRef {
|
||||
override fun forEachVariable(block: (String) -> Unit) {
|
||||
block(name)
|
||||
}
|
||||
|
||||
override fun forEachVariableWithPos(block: (String, Pos) -> Unit) {
|
||||
block(name, atPos)
|
||||
}
|
||||
// Per-frame slot cache to avoid repeated name lookups
|
||||
private var cachedFrameId: Long = 0L
|
||||
private var cachedSlot: Int = -1
|
||||
@@ -1632,6 +1644,15 @@ class ListLiteralRef(private val entries: List<ListEntry>) : ObjRef {
|
||||
}
|
||||
}
|
||||
|
||||
override fun forEachVariableWithPos(block: (String, Pos) -> Unit) {
|
||||
for (e in entries) {
|
||||
when (e) {
|
||||
is ListEntry.Element -> e.ref.forEachVariableWithPos(block)
|
||||
is ListEntry.Spread -> e.ref.forEachVariableWithPos(block)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun get(scope: Scope): ObjRecord {
|
||||
// Heuristic capacity hint: count element entries; spreads handled opportunistically
|
||||
val elemCount = entries.count { it is ListEntry.Element }
|
||||
|
||||
@@ -47,7 +47,8 @@ abstract class Statement(
|
||||
|
||||
override suspend fun compareTo(scope: Scope, other: Obj): Int {
|
||||
if( other == ObjNull || other == ObjVoid ) return 1
|
||||
throw UnsupportedOperationException("not comparable")
|
||||
if( other === this ) return 0
|
||||
return -1
|
||||
}
|
||||
|
||||
override suspend fun callOn(scope: Scope): Obj {
|
||||
|
||||
@@ -16,13 +16,18 @@
|
||||
*/
|
||||
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import net.sergeych.lyng.Script
|
||||
import net.sergeych.lyng.eval
|
||||
import net.sergeych.lyng.obj.ObjInstance
|
||||
import net.sergeych.lyng.obj.ObjList
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class OOTest {
|
||||
@Test
|
||||
fun testClassProps() = runTest {
|
||||
eval("""
|
||||
eval(
|
||||
"""
|
||||
import lyng.time
|
||||
|
||||
class Point(x,y) {
|
||||
@@ -35,11 +40,14 @@ class OOTest {
|
||||
assertEquals(Point(0,0), Point.origin)
|
||||
assertEquals(Point(1,2), Point.center)
|
||||
|
||||
""".trimIndent())
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testClassMethods() = runTest {
|
||||
eval("""
|
||||
eval(
|
||||
"""
|
||||
import lyng.time
|
||||
|
||||
class Point(x,y) {
|
||||
@@ -58,12 +66,14 @@ class OOTest {
|
||||
assertEquals(null, Point.getData() )
|
||||
Point.setData("foo")
|
||||
assertEquals( "foo!", Point.getData() )
|
||||
""".trimIndent())
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDynamicGet() = runTest {
|
||||
eval("""
|
||||
eval(
|
||||
"""
|
||||
val accessor = dynamic {
|
||||
get { name ->
|
||||
if( name == "foo" ) "bar" else null
|
||||
@@ -74,12 +84,14 @@ class OOTest {
|
||||
assertEquals("bar", accessor.foo)
|
||||
assertEquals(null, accessor.bar)
|
||||
|
||||
""".trimIndent())
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDelegateSet() = runTest {
|
||||
eval("""
|
||||
eval(
|
||||
"""
|
||||
var setValueForBar = null
|
||||
val accessor = dynamic {
|
||||
get { name ->
|
||||
@@ -104,12 +116,14 @@ class OOTest {
|
||||
assertThrows {
|
||||
accessor.bad = "!23"
|
||||
}
|
||||
""".trimIndent())
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDynamicIndexAccess() = runTest {
|
||||
eval("""
|
||||
eval(
|
||||
"""
|
||||
val store = Map()
|
||||
val accessor = dynamic {
|
||||
get { name ->
|
||||
@@ -124,23 +138,27 @@ class OOTest {
|
||||
accessor["foo"] = "bar"
|
||||
assertEquals("bar", accessor["foo"])
|
||||
assertEquals("bar", accessor.foo)
|
||||
""".trimIndent())
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMultilineConstructor() = runTest {
|
||||
eval("""
|
||||
eval(
|
||||
"""
|
||||
class Point(
|
||||
x,
|
||||
y
|
||||
)
|
||||
assertEquals(Point(1,2), Point(1,2) )
|
||||
""".trimIndent())
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDynamicClass() = runTest {
|
||||
eval("""
|
||||
eval(
|
||||
"""
|
||||
|
||||
fun getContract(contractName) {
|
||||
dynamic {
|
||||
@@ -150,14 +168,16 @@ class OOTest {
|
||||
}
|
||||
}
|
||||
getContract("foo").bar
|
||||
""")
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDynamicClassReturn2() = runTest {
|
||||
// todo: should work without extra parenthesis
|
||||
// see below
|
||||
eval("""
|
||||
eval(
|
||||
"""
|
||||
|
||||
fun getContract(contractName) {
|
||||
println("1")
|
||||
@@ -184,12 +204,14 @@ class OOTest {
|
||||
assertEquals(6, x(1,2,3))
|
||||
// v HERE v
|
||||
assertEquals(15, cc.foo.bar(10,2,3))
|
||||
""")
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testClassInitialization() = runTest {
|
||||
eval("""
|
||||
eval(
|
||||
"""
|
||||
var countInstances = 0
|
||||
class Point(val x: Int, val y: Int) {
|
||||
println("Class initializer is called 1")
|
||||
@@ -210,12 +232,14 @@ class OOTest {
|
||||
assertEquals(1, countInstances)
|
||||
assertEquals(p, Point(1,2) )
|
||||
assertEquals(2, countInstances)
|
||||
""".trimIndent())
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMIInitialization() = runTest {
|
||||
eval("""
|
||||
eval(
|
||||
"""
|
||||
var order = []
|
||||
class A {
|
||||
init { order.add("A") }
|
||||
@@ -231,12 +255,14 @@ class OOTest {
|
||||
}
|
||||
D()
|
||||
assertEquals(["A", "B", "C", "D"], order)
|
||||
""")
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMIDiamondInitialization() = runTest {
|
||||
eval("""
|
||||
eval(
|
||||
"""
|
||||
var order = []
|
||||
class A {
|
||||
init { order.add("A") }
|
||||
@@ -252,12 +278,14 @@ class OOTest {
|
||||
}
|
||||
D()
|
||||
assertEquals(["A", "B", "C", "D"], order)
|
||||
""")
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInitBlockInDeserialization() = runTest {
|
||||
eval("""
|
||||
eval(
|
||||
"""
|
||||
import lyng.serialization
|
||||
var count = 0
|
||||
class A {
|
||||
@@ -267,6 +295,52 @@ class OOTest {
|
||||
val coded = Lynon.encode(a1)
|
||||
val a2 = Lynon.decode(coded)
|
||||
assertEquals(2, count)
|
||||
""")
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDefaultCompare() = runTest {
|
||||
eval(
|
||||
"""
|
||||
class Point(val x: Int, val y: Int)
|
||||
|
||||
assertEquals(Point(1,2), Point(1,2) )
|
||||
assert( Point(1,2) != Point(2,1) )
|
||||
assert( Point(1,2) == Point(1,2) )
|
||||
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testConstructorCallsWithNamedParams() = runTest {
|
||||
val scope = Script.newScope()
|
||||
val list = scope.eval(
|
||||
"""
|
||||
import lyng.time
|
||||
|
||||
class BarRequest(
|
||||
id,
|
||||
vaultId, userAddress, isDepositRequest, grossWeight, fineness, notes="",
|
||||
createdAt = Instant.now().truncateToSecond(),
|
||||
updatedAt = Instant.now().truncateToSecond()
|
||||
) {
|
||||
// unrelated for comparison
|
||||
static val stateNames = [1, 2, 3]
|
||||
|
||||
val cell = cached { Cell[id] }
|
||||
}
|
||||
assertEquals( 5,5.toInt())
|
||||
val b1 = BarRequest(1, "v1", "u1", true, 1000, 999)
|
||||
val b2 = BarRequest(1, "v1", "u1", true, 1000, 999, createdAt: b1.createdAt, updatedAt: b1.updatedAt)
|
||||
assertEquals(b1, b2)
|
||||
assertEquals( 0, b1 <=> b2)
|
||||
[b1, b2]
|
||||
""".trimIndent()
|
||||
) as ObjList
|
||||
val b1 = list.list[0] as ObjInstance
|
||||
val b2 = list.list[1] as ObjInstance
|
||||
assertEquals(0, b1.compareTo(scope, b2))
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user