improved vairable tracking, fixed plugin to wirk with 1.0.10, fixed lambda comparison
This commit is contained in:
parent
1e18a162c4
commit
0732202c80
1
.gitignore
vendored
1
.gitignore
vendored
@ -18,3 +18,4 @@ xcuserdata
|
|||||||
/kotlin-js-store/wasm/yarn.lock
|
/kotlin-js-store/wasm/yarn.lock
|
||||||
/distributables
|
/distributables
|
||||||
/.output.txt
|
/.output.txt
|
||||||
|
/build.log
|
||||||
|
|||||||
@ -81,8 +81,14 @@ statements discussed later, there could be default values, ellipsis, etc.
|
|||||||
class Point(x=0,y=0)
|
class Point(x=0,y=0)
|
||||||
val p = Point()
|
val p = Point()
|
||||||
assert( p.x == 0 && p.y == 0 )
|
assert( p.x == 0 && p.y == 0 )
|
||||||
|
|
||||||
|
// Named arguments in constructor calls use colon syntax:
|
||||||
|
val p2 = Point(y: 10, x: 5)
|
||||||
|
assert( p2.x == 5 && p2.y == 10 )
|
||||||
>>> void
|
>>> void
|
||||||
|
|
||||||
|
Note that unlike **Kotlin**, which uses `=` for named arguments, Lyng uses `:` to avoid ambiguity with assignment expressions.
|
||||||
|
|
||||||
## Methods
|
## Methods
|
||||||
|
|
||||||
Functions defined inside a class body are methods, and unless declared
|
Functions defined inside a class body are methods, and unless declared
|
||||||
|
|||||||
@ -123,7 +123,7 @@ Rules:
|
|||||||
- A named argument cannot reassign a parameter already set positionally.
|
- A named argument cannot reassign a parameter already set positionally.
|
||||||
- If the last parameter has already been assigned by a named argument (or named splat), a trailing block is not allowed and results in an error.
|
- If the last parameter has already been assigned by a named argument (or named splat), a trailing block is not allowed and results in an error.
|
||||||
|
|
||||||
Why `:` and not `=` at call sites? In Lyng, `=` is an expression (assignment), so we use `:` to avoid ambiguity. Declarations continue to use `:` for types, while call sites use `as` / `as?` for type operations.
|
Why `:` and not `=` at call sites? In Lyng, `=` is an expression (assignment), so we use `:` to avoid ambiguity. This is a key difference from **Kotlin**, which uses `=` for named arguments. Declarations in Lyng continue to use `:` for types, while call sites use `as` / `as?` for type operations.
|
||||||
|
|
||||||
## Named splats (map splats)
|
## Named splats (map splats)
|
||||||
|
|
||||||
|
|||||||
@ -439,6 +439,19 @@ It is possible to define also vararg using ellipsis:
|
|||||||
|
|
||||||
See the [arguments reference](declaring_arguments.md) for more details.
|
See the [arguments reference](declaring_arguments.md) for more details.
|
||||||
|
|
||||||
|
## Named arguments
|
||||||
|
|
||||||
|
When calling functions, you can use named arguments with the colon syntax `name: value`. This is particularly useful when you have many parameters with default values.
|
||||||
|
|
||||||
|
```lyng
|
||||||
|
fun test(a="foo", b="bar", c="bazz") { [a, b, c] }
|
||||||
|
|
||||||
|
assertEquals(["foo", "b", "bazz"], test(b: "b"))
|
||||||
|
assertEquals(["a", "bar", "c"], test("a", c: "c"))
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note for Kotlin users:** Lyng uses `:` instead of `=` for named arguments at call sites. This is because in Lyng, `=` is an expression that returns the assigned value, and using it in an argument list would create ambiguity.
|
||||||
|
|
||||||
## Closures
|
## Closures
|
||||||
|
|
||||||
Each __block has an isolated context that can be accessed from closures__. For example:
|
Each __block has an isolated context that can be accessed from closures__. For example:
|
||||||
|
|||||||
@ -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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Lightweight BASIC completion for Lyng, MVP version.
|
* Lightweight BASIC completion for Lyng, MVP version.
|
||||||
* Uses MiniAst (best-effort) + BuiltinDocRegistry to suggest symbols.
|
* Uses MiniAst (best-effort) + BuiltinDocRegistry to suggest symbols.
|
||||||
@ -192,6 +209,7 @@ class LyngCompletionContributor : CompletionContributor() {
|
|||||||
emit(builder)
|
emit(builder)
|
||||||
existing.add(name)
|
existing.add(name)
|
||||||
}
|
}
|
||||||
|
is MiniInitDecl -> {}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Fallback: emit simple method name without detailed types
|
// Fallback: emit simple method name without detailed types
|
||||||
@ -329,6 +347,7 @@ class LyngCompletionContributor : CompletionContributor() {
|
|||||||
when (m) {
|
when (m) {
|
||||||
is MiniMemberFunDecl -> if (!m.isStatic) continue
|
is MiniMemberFunDecl -> if (!m.isStatic) continue
|
||||||
is MiniMemberValDecl -> if (!m.isStatic) continue
|
is MiniMemberValDecl -> if (!m.isStatic) continue
|
||||||
|
is MiniInitDecl -> continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val list = target.getOrPut(m.name) { mutableListOf() }
|
val list = target.getOrPut(m.name) { mutableListOf() }
|
||||||
@ -404,6 +423,7 @@ class LyngCompletionContributor : CompletionContributor() {
|
|||||||
.withTypeText(typeOf((chosen as MiniMemberValDecl).type), true)
|
.withTypeText(typeOf((chosen as MiniMemberValDecl).type), true)
|
||||||
emit(builder)
|
emit(builder)
|
||||||
}
|
}
|
||||||
|
is MiniInitDecl -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -454,6 +474,7 @@ class LyngCompletionContributor : CompletionContributor() {
|
|||||||
emit(builder)
|
emit(builder)
|
||||||
already.add(name)
|
already.add(name)
|
||||||
}
|
}
|
||||||
|
is MiniInitDecl -> {}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Synthetic fallback: method without detailed params/types to improve UX in absence of docs
|
// Synthetic fallback: method without detailed params/types to improve UX in absence of docs
|
||||||
@ -504,6 +525,7 @@ class LyngCompletionContributor : CompletionContributor() {
|
|||||||
already.add(name)
|
already.add(name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
is MiniInitDecl -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Fallback: emit without detailed types if we couldn't resolve
|
// Fallback: emit without detailed types if we couldn't resolve
|
||||||
@ -613,6 +635,7 @@ class LyngCompletionContributor : CompletionContributor() {
|
|||||||
val rt = when (m) {
|
val rt = when (m) {
|
||||||
is MiniMemberFunDecl -> m.returnType
|
is MiniMemberFunDecl -> m.returnType
|
||||||
is MiniMemberValDecl -> m.type
|
is MiniMemberValDecl -> m.type
|
||||||
|
is MiniInitDecl -> null
|
||||||
}
|
}
|
||||||
simpleClassNameOf(rt)
|
simpleClassNameOf(rt)
|
||||||
}
|
}
|
||||||
@ -715,6 +738,24 @@ class LyngCompletionContributor : CompletionContributor() {
|
|||||||
if (hasDigits) {
|
if (hasDigits) {
|
||||||
return if (isHex) "Int" else if (hasDot || hasExp) "Real" else "Int"
|
return if (isHex) "Int" else if (hasDot || hasExp) "Real" else "Int"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3) this@Type or as Type
|
||||||
|
val identRange = TextCtx.wordRangeAt(text, i + 1)
|
||||||
|
if (identRange != null) {
|
||||||
|
val ident = text.substring(identRange.startOffset, identRange.endOffset)
|
||||||
|
// if it's "as Type", we want Type
|
||||||
|
var k2 = TextCtx.prevNonWs(text, identRange.startOffset - 1)
|
||||||
|
if (k2 >= 1 && text[k2] == 's' && text[k2 - 1] == 'a' && (k2 - 1 == 0 || !text[k2 - 2].isLetterOrDigit())) {
|
||||||
|
return ident
|
||||||
|
}
|
||||||
|
// if it's "this@Type", we want Type
|
||||||
|
if (k2 >= 0 && text[k2] == '@') {
|
||||||
|
val k3 = TextCtx.prevNonWs(text, k2 - 1)
|
||||||
|
if (k3 >= 3 && text.substring(k3 - 3, k3 + 1) == "this") {
|
||||||
|
return ident
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -761,6 +802,7 @@ class LyngCompletionContributor : CompletionContributor() {
|
|||||||
val returnType = when (member) {
|
val returnType = when (member) {
|
||||||
is MiniMemberFunDecl -> member.returnType
|
is MiniMemberFunDecl -> member.returnType
|
||||||
is MiniMemberValDecl -> member.type
|
is MiniMemberValDecl -> member.type
|
||||||
|
is MiniInitDecl -> null
|
||||||
}
|
}
|
||||||
return simpleClassNameOf(returnType)
|
return simpleClassNameOf(returnType)
|
||||||
}
|
}
|
||||||
@ -840,6 +882,7 @@ class LyngCompletionContributor : CompletionContributor() {
|
|||||||
val returnType = when (member) {
|
val returnType = when (member) {
|
||||||
is MiniMemberFunDecl -> member.returnType
|
is MiniMemberFunDecl -> member.returnType
|
||||||
is MiniMemberValDecl -> member.type
|
is MiniMemberValDecl -> member.type
|
||||||
|
is MiniInitDecl -> null
|
||||||
}
|
}
|
||||||
return simpleClassNameOf(returnType)
|
return simpleClassNameOf(returnType)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -154,7 +154,27 @@ class LyngDocumentationProvider : AbstractDocumentationProvider() {
|
|||||||
} else null
|
} else null
|
||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
else -> DocLookupUtils.guessClassFromCallBefore(text, dotPos, importedModules)
|
else -> {
|
||||||
|
val guessed = DocLookupUtils.guessClassFromCallBefore(text, dotPos, importedModules)
|
||||||
|
if (guessed != null) guessed
|
||||||
|
else {
|
||||||
|
// handle this@Type or as Type
|
||||||
|
val i2 = TextCtx.prevNonWs(text, dotPos - 1)
|
||||||
|
if (i2 >= 0) {
|
||||||
|
val identRange = TextCtx.wordRangeAt(text, i2 + 1)
|
||||||
|
if (identRange != null) {
|
||||||
|
val id = text.substring(identRange.startOffset, identRange.endOffset)
|
||||||
|
val k = TextCtx.prevNonWs(text, identRange.startOffset - 1)
|
||||||
|
if (k >= 1 && text[k] == 's' && text[k-1] == 'a' && (k-1 == 0 || !text[k-2].isLetterOrDigit())) {
|
||||||
|
id
|
||||||
|
} else if (k >= 0 && text[k] == '@') {
|
||||||
|
val k2 = TextCtx.prevNonWs(text, k - 1)
|
||||||
|
if (k2 >= 3 && text.substring(k2 - 3, k2 + 1) == "this") id else null
|
||||||
|
} else null
|
||||||
|
} else null
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (DEBUG_LOG) log.info("[LYNG_DEBUG] QuickDoc: memberCtx dotPos=${dotPos} chBeforeDot='${if (dotPos>0) text[dotPos-1] else ' '}' classGuess=${className} imports=${importedModules}")
|
if (DEBUG_LOG) log.info("[LYNG_DEBUG] QuickDoc: memberCtx dotPos=${dotPos} chBeforeDot='${if (dotPos>0) text[dotPos-1] else ' '}' classGuess=${className} imports=${importedModules}")
|
||||||
if (className != null) {
|
if (className != null) {
|
||||||
@ -163,6 +183,7 @@ class LyngDocumentationProvider : AbstractDocumentationProvider() {
|
|||||||
return when (member) {
|
return when (member) {
|
||||||
is MiniMemberFunDecl -> renderMemberFunDoc(owner, member)
|
is MiniMemberFunDecl -> renderMemberFunDoc(owner, member)
|
||||||
is MiniMemberValDecl -> renderMemberValDoc(owner, member)
|
is MiniMemberValDecl -> renderMemberValDoc(owner, member)
|
||||||
|
is MiniInitDecl -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.info("[LYNG_DEBUG] QuickDoc: resolve failed for ${className}.${ident}")
|
log.info("[LYNG_DEBUG] QuickDoc: resolve failed for ${className}.${ident}")
|
||||||
@ -224,6 +245,7 @@ class LyngDocumentationProvider : AbstractDocumentationProvider() {
|
|||||||
return when (member) {
|
return when (member) {
|
||||||
is MiniMemberFunDecl -> renderMemberFunDoc(owner, member)
|
is MiniMemberFunDecl -> renderMemberFunDoc(owner, member)
|
||||||
is MiniMemberValDecl -> renderMemberValDoc(owner, member)
|
is MiniMemberValDecl -> renderMemberValDoc(owner, member)
|
||||||
|
is MiniInitDecl -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -242,6 +264,7 @@ class LyngDocumentationProvider : AbstractDocumentationProvider() {
|
|||||||
return when (member) {
|
return when (member) {
|
||||||
is MiniMemberFunDecl -> renderMemberFunDoc(owner, member)
|
is MiniMemberFunDecl -> renderMemberFunDoc(owner, member)
|
||||||
is MiniMemberValDecl -> renderMemberValDoc(owner, member)
|
is MiniMemberValDecl -> renderMemberValDoc(owner, member)
|
||||||
|
is MiniInitDecl -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -254,6 +277,7 @@ class LyngDocumentationProvider : AbstractDocumentationProvider() {
|
|||||||
return when (member) {
|
return when (member) {
|
||||||
is MiniMemberFunDecl -> renderMemberFunDoc(owner, member)
|
is MiniMemberFunDecl -> renderMemberFunDoc(owner, member)
|
||||||
is MiniMemberValDecl -> renderMemberValDoc(owner, member)
|
is MiniMemberValDecl -> renderMemberValDoc(owner, member)
|
||||||
|
is MiniInitDecl -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -268,6 +292,7 @@ class LyngDocumentationProvider : AbstractDocumentationProvider() {
|
|||||||
return when (m) {
|
return when (m) {
|
||||||
is MiniMemberFunDecl -> renderMemberFunDoc("String", m)
|
is MiniMemberFunDecl -> renderMemberFunDoc("String", m)
|
||||||
is MiniMemberValDecl -> renderMemberValDoc("String", m)
|
is MiniMemberValDecl -> renderMemberValDoc("String", m)
|
||||||
|
is MiniInitDecl -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -277,6 +302,7 @@ class LyngDocumentationProvider : AbstractDocumentationProvider() {
|
|||||||
return when (member) {
|
return when (member) {
|
||||||
is MiniMemberFunDecl -> renderMemberFunDoc(owner, member)
|
is MiniMemberFunDecl -> renderMemberFunDoc(owner, member)
|
||||||
is MiniMemberValDecl -> renderMemberValDoc(owner, member)
|
is MiniMemberValDecl -> renderMemberValDoc(owner, member)
|
||||||
|
is MiniInitDecl -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2569,7 +2569,21 @@ class Compiler(
|
|||||||
val pattern = ListLiteralRef(entries)
|
val pattern = ListLiteralRef(entries)
|
||||||
|
|
||||||
// Register all names in the pattern
|
// 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()
|
val eqToken = cc.next()
|
||||||
if (eqToken.type != Token.Type.ASSIGN)
|
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.
|
* Pure-Kotlin, PSI-free completion engine used for isolated tests and non-IDE harnesses.
|
||||||
* Mirrors the IntelliJ MVP logic: MiniAst + BuiltinDocRegistry + lenient imports.
|
* Mirrors the IntelliJ MVP logic: MiniAst + BuiltinDocRegistry + lenient imports.
|
||||||
@ -270,6 +287,24 @@ object CompletionEngineLight {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
if (hasDigits) return if (hasDot || hasExp) "Real" else "Int"
|
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
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@ -180,6 +180,10 @@ class ObjInt(var value: Long, override val isConst: Boolean = false) : Obj(), Nu
|
|||||||
LynonType.IntSigned -> ObjInt(decoder.unpackSigned())
|
LynonType.IntSigned -> ObjInt(decoder.unpackSigned())
|
||||||
else -> scope.raiseIllegalState("illegal type code for Int: $lynonType")
|
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.
|
* Used for declaring local variables in destructuring.
|
||||||
*/
|
*/
|
||||||
fun forEachVariable(block: (String) -> Unit) {}
|
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. */
|
/** 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) {
|
override fun forEachVariable(block: (String) -> Unit) {
|
||||||
block(name)
|
block(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun forEachVariableWithPos(block: (String, Pos) -> Unit) {
|
||||||
|
block(name, atPos)
|
||||||
|
}
|
||||||
// Per-frame slot cache to avoid repeated name lookups
|
// Per-frame slot cache to avoid repeated name lookups
|
||||||
private var cachedFrameId: Long = 0L
|
private var cachedFrameId: Long = 0L
|
||||||
private var cachedSlot: Int = -1
|
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 {
|
override suspend fun get(scope: Scope): ObjRecord {
|
||||||
// Heuristic capacity hint: count element entries; spreads handled opportunistically
|
// Heuristic capacity hint: count element entries; spreads handled opportunistically
|
||||||
val elemCount = entries.count { it is ListEntry.Element }
|
val elemCount = entries.count { it is ListEntry.Element }
|
||||||
|
|||||||
@ -47,7 +47,8 @@ abstract class Statement(
|
|||||||
|
|
||||||
override suspend fun compareTo(scope: Scope, other: Obj): Int {
|
override suspend fun compareTo(scope: Scope, other: Obj): Int {
|
||||||
if( other == ObjNull || other == ObjVoid ) return 1
|
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 {
|
override suspend fun callOn(scope: Scope): Obj {
|
||||||
|
|||||||
@ -16,13 +16,18 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import net.sergeych.lyng.Script
|
||||||
import net.sergeych.lyng.eval
|
import net.sergeych.lyng.eval
|
||||||
|
import net.sergeych.lyng.obj.ObjInstance
|
||||||
|
import net.sergeych.lyng.obj.ObjList
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class OOTest {
|
class OOTest {
|
||||||
@Test
|
@Test
|
||||||
fun testClassProps() = runTest {
|
fun testClassProps() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
import lyng.time
|
import lyng.time
|
||||||
|
|
||||||
class Point(x,y) {
|
class Point(x,y) {
|
||||||
@ -35,11 +40,14 @@ class OOTest {
|
|||||||
assertEquals(Point(0,0), Point.origin)
|
assertEquals(Point(0,0), Point.origin)
|
||||||
assertEquals(Point(1,2), Point.center)
|
assertEquals(Point(1,2), Point.center)
|
||||||
|
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testClassMethods() = runTest {
|
fun testClassMethods() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
import lyng.time
|
import lyng.time
|
||||||
|
|
||||||
class Point(x,y) {
|
class Point(x,y) {
|
||||||
@ -58,12 +66,14 @@ class OOTest {
|
|||||||
assertEquals(null, Point.getData() )
|
assertEquals(null, Point.getData() )
|
||||||
Point.setData("foo")
|
Point.setData("foo")
|
||||||
assertEquals( "foo!", Point.getData() )
|
assertEquals( "foo!", Point.getData() )
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testDynamicGet() = runTest {
|
fun testDynamicGet() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
val accessor = dynamic {
|
val accessor = dynamic {
|
||||||
get { name ->
|
get { name ->
|
||||||
if( name == "foo" ) "bar" else null
|
if( name == "foo" ) "bar" else null
|
||||||
@ -74,12 +84,14 @@ class OOTest {
|
|||||||
assertEquals("bar", accessor.foo)
|
assertEquals("bar", accessor.foo)
|
||||||
assertEquals(null, accessor.bar)
|
assertEquals(null, accessor.bar)
|
||||||
|
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testDelegateSet() = runTest {
|
fun testDelegateSet() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
var setValueForBar = null
|
var setValueForBar = null
|
||||||
val accessor = dynamic {
|
val accessor = dynamic {
|
||||||
get { name ->
|
get { name ->
|
||||||
@ -104,12 +116,14 @@ class OOTest {
|
|||||||
assertThrows {
|
assertThrows {
|
||||||
accessor.bad = "!23"
|
accessor.bad = "!23"
|
||||||
}
|
}
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testDynamicIndexAccess() = runTest {
|
fun testDynamicIndexAccess() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
val store = Map()
|
val store = Map()
|
||||||
val accessor = dynamic {
|
val accessor = dynamic {
|
||||||
get { name ->
|
get { name ->
|
||||||
@ -124,23 +138,27 @@ class OOTest {
|
|||||||
accessor["foo"] = "bar"
|
accessor["foo"] = "bar"
|
||||||
assertEquals("bar", accessor["foo"])
|
assertEquals("bar", accessor["foo"])
|
||||||
assertEquals("bar", accessor.foo)
|
assertEquals("bar", accessor.foo)
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testMultilineConstructor() = runTest {
|
fun testMultilineConstructor() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
class Point(
|
class Point(
|
||||||
x,
|
x,
|
||||||
y
|
y
|
||||||
)
|
)
|
||||||
assertEquals(Point(1,2), Point(1,2) )
|
assertEquals(Point(1,2), Point(1,2) )
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testDynamicClass() = runTest {
|
fun testDynamicClass() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
|
|
||||||
fun getContract(contractName) {
|
fun getContract(contractName) {
|
||||||
dynamic {
|
dynamic {
|
||||||
@ -150,14 +168,16 @@ class OOTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
getContract("foo").bar
|
getContract("foo").bar
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testDynamicClassReturn2() = runTest {
|
fun testDynamicClassReturn2() = runTest {
|
||||||
// todo: should work without extra parenthesis
|
// todo: should work without extra parenthesis
|
||||||
// see below
|
// see below
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
|
|
||||||
fun getContract(contractName) {
|
fun getContract(contractName) {
|
||||||
println("1")
|
println("1")
|
||||||
@ -184,12 +204,14 @@ class OOTest {
|
|||||||
assertEquals(6, x(1,2,3))
|
assertEquals(6, x(1,2,3))
|
||||||
// v HERE v
|
// v HERE v
|
||||||
assertEquals(15, cc.foo.bar(10,2,3))
|
assertEquals(15, cc.foo.bar(10,2,3))
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testClassInitialization() = runTest {
|
fun testClassInitialization() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
var countInstances = 0
|
var countInstances = 0
|
||||||
class Point(val x: Int, val y: Int) {
|
class Point(val x: Int, val y: Int) {
|
||||||
println("Class initializer is called 1")
|
println("Class initializer is called 1")
|
||||||
@ -210,12 +232,14 @@ class OOTest {
|
|||||||
assertEquals(1, countInstances)
|
assertEquals(1, countInstances)
|
||||||
assertEquals(p, Point(1,2) )
|
assertEquals(p, Point(1,2) )
|
||||||
assertEquals(2, countInstances)
|
assertEquals(2, countInstances)
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testMIInitialization() = runTest {
|
fun testMIInitialization() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
var order = []
|
var order = []
|
||||||
class A {
|
class A {
|
||||||
init { order.add("A") }
|
init { order.add("A") }
|
||||||
@ -231,12 +255,14 @@ class OOTest {
|
|||||||
}
|
}
|
||||||
D()
|
D()
|
||||||
assertEquals(["A", "B", "C", "D"], order)
|
assertEquals(["A", "B", "C", "D"], order)
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testMIDiamondInitialization() = runTest {
|
fun testMIDiamondInitialization() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
var order = []
|
var order = []
|
||||||
class A {
|
class A {
|
||||||
init { order.add("A") }
|
init { order.add("A") }
|
||||||
@ -252,12 +278,14 @@ class OOTest {
|
|||||||
}
|
}
|
||||||
D()
|
D()
|
||||||
assertEquals(["A", "B", "C", "D"], order)
|
assertEquals(["A", "B", "C", "D"], order)
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testInitBlockInDeserialization() = runTest {
|
fun testInitBlockInDeserialization() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
import lyng.serialization
|
import lyng.serialization
|
||||||
var count = 0
|
var count = 0
|
||||||
class A {
|
class A {
|
||||||
@ -267,6 +295,52 @@ class OOTest {
|
|||||||
val coded = Lynon.encode(a1)
|
val coded = Lynon.encode(a1)
|
||||||
val a2 = Lynon.decode(coded)
|
val a2 = Lynon.decode(coded)
|
||||||
assertEquals(2, count)
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user