Merge branch 'inference-bug'
# Conflicts: # lyngio/src/commonMain/kotlin/net/sergeych/lyng/io/http/LyngHttpModule.kt
This commit is contained in:
commit
9bee0aed5b
@ -1,5 +1,7 @@
|
|||||||
# Lyng Language Reference for AI Agents (Current Compiler State)
|
# Lyng Language Reference for AI Agents (Current Compiler State)
|
||||||
|
|
||||||
|
[//]: # (excludeFromIndex)
|
||||||
|
|
||||||
Purpose: dense, implementation-first reference for generating valid Lyng code.
|
Purpose: dense, implementation-first reference for generating valid Lyng code.
|
||||||
|
|
||||||
Primary sources used: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/{Parser,Token,Compiler,Script,TypeDecl}.kt`, `lynglib/stdlib/lyng/root.lyng`, tests in `lynglib/src/commonTest` and `lynglib/src/jvmTest`.
|
Primary sources used: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/{Parser,Token,Compiler,Script,TypeDecl}.kt`, `lynglib/stdlib/lyng/root.lyng`, tests in `lynglib/src/commonTest` and `lynglib/src/jvmTest`.
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
# AI notes: avoid Kotlin/Wasm invalid IR with suspend lambdas
|
# AI notes: avoid Kotlin/Wasm invalid IR with suspend lambdas
|
||||||
|
|
||||||
|
[//]: # (excludeFromIndex)
|
||||||
|
|
||||||
## Do
|
## Do
|
||||||
- Prefer explicit `object : Statement()` with `override suspend fun execute(...)` when building compiler statements.
|
- Prefer explicit `object : Statement()` with `override suspend fun execute(...)` when building compiler statements.
|
||||||
- Keep `Statement` objects non-lambda, especially in compiler hot paths like parsing/var declarations.
|
- Keep `Statement` objects non-lambda, especially in compiler hot paths like parsing/var declarations.
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
# Lyng Stdlib Reference for AI Agents (Compact)
|
# Lyng Stdlib Reference for AI Agents (Compact)
|
||||||
|
|
||||||
|
[//]: # (excludeFromIndex)
|
||||||
|
|
||||||
Purpose: fast overview of what is available by default and what must be imported.
|
Purpose: fast overview of what is available by default and what must be imported.
|
||||||
|
|
||||||
Sources: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/Script.kt`, `lynglib/stdlib/lyng/root.lyng`, `lynglib/src/commonMain/kotlin/net/sergeych/lyng/stdlib_included/observable_lyng.kt`.
|
Sources: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/Script.kt`, `lynglib/stdlib/lyng/root.lyng`, `lynglib/src/commonMain/kotlin/net/sergeych/lyng/stdlib_included/observable_lyng.kt`.
|
||||||
|
|||||||
11
docs/downloads.md
Normal file
11
docs/downloads.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Some resources to download
|
||||||
|
|
||||||
|
## Lync CLI tool
|
||||||
|
|
||||||
|
- [lyng-linuxX64.zip](/distributables/lyng-linuxX64.zip) CLI tool for linuxX64: nodependencies, small monolith executable binary.
|
||||||
|
|
||||||
|
## IDE plugins
|
||||||
|
|
||||||
|
- [lyng-textmate.zip](../../lyng/distributables/lyng-textmate.zip) Texmate-compatible bundle with syntax coloring (could be outdated)
|
||||||
|
|
||||||
|
- [lyng-idea-0.0.5-SNAPSHOT.zip](/distributables/lyng-idea-0.0.5-SNAPSHOT.zip) - plugin for IntelliJ-compatible IDE
|
||||||
@ -1,5 +1,7 @@
|
|||||||
# Embedding Lyng in your Kotlin project
|
# Embedding Lyng in your Kotlin project
|
||||||
|
|
||||||
|
[//]: # (topMenu)
|
||||||
|
|
||||||
Lyng is a tiny, embeddable, Kotlin‑first scripting language. This page shows, step by step, how to:
|
Lyng is a tiny, embeddable, Kotlin‑first scripting language. This page shows, step by step, how to:
|
||||||
|
|
||||||
- add Lyng to your build
|
- add Lyng to your build
|
||||||
|
|||||||
@ -31,11 +31,12 @@ class FsIntegrationJvmTest {
|
|||||||
val dir = createTempDirectory("lyng_cli_fs_test_")
|
val dir = createTempDirectory("lyng_cli_fs_test_")
|
||||||
try {
|
try {
|
||||||
val file = dir.resolve("hello.txt")
|
val file = dir.resolve("hello.txt")
|
||||||
|
val filePath = file.toString().replace("\\", "\\\\")
|
||||||
// Drive the operation via Lyng code to validate bindings end-to-end
|
// Drive the operation via Lyng code to validate bindings end-to-end
|
||||||
scope.eval(
|
scope.eval(
|
||||||
"""
|
"""
|
||||||
import lyng.io.fs
|
import lyng.io.fs
|
||||||
val p = Path("${'$'}{file}")
|
val p = Path("${filePath}")
|
||||||
p.writeUtf8("hello from cli test")
|
p.writeUtf8("hello from cli test")
|
||||||
assertEquals(true, p.exists())
|
assertEquals(true, p.exists())
|
||||||
assertEquals("hello from cli test", p.readUtf8())
|
assertEquals("hello from cli test", p.readUtf8())
|
||||||
|
|||||||
@ -332,7 +332,9 @@ private class ObjHttpResponse(
|
|||||||
fun from(response: LyngHttpResponse): ObjHttpResponse {
|
fun from(response: LyngHttpResponse): ObjHttpResponse {
|
||||||
val single = linkedMapOf<String, String>()
|
val single = linkedMapOf<String, String>()
|
||||||
response.headers.forEach { (name, values) ->
|
response.headers.forEach { (name, values) ->
|
||||||
if (values.isNotEmpty() && name !in single) single[name] = values.first()
|
if (values.isNotEmpty() && !single.containsKey(name)) {
|
||||||
|
single[name] = values.first()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ObjHttpResponse(
|
return ObjHttpResponse(
|
||||||
status = response.status.toLong(),
|
status = response.status.toLong(),
|
||||||
|
|||||||
@ -4532,6 +4532,15 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun inferForLoopElementType(source: Statement, constRange: ConstIntRange?): TypeDecl? {
|
||||||
|
if (constRange != null) return TypeDecl.Simple("Int", false)
|
||||||
|
val sourceType = inferTypeDeclFromInitializer(source) ?: return null
|
||||||
|
return when {
|
||||||
|
isRangeType(sourceType) -> TypeDecl.Simple("Int", false)
|
||||||
|
else -> inferCollectionElementType(expandTypeAliases(sourceType, source.pos))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun typeDeclSubtypeOf(arg: TypeDecl, param: TypeDecl): Boolean {
|
private fun typeDeclSubtypeOf(arg: TypeDecl, param: TypeDecl): Boolean {
|
||||||
if (param == TypeDecl.TypeAny || param == TypeDecl.TypeNullableAny) return true
|
if (param == TypeDecl.TypeAny || param == TypeDecl.TypeNullableAny) return true
|
||||||
val (argBase, argNullable) = stripNullable(arg)
|
val (argBase, argNullable) = stripNullable(arg)
|
||||||
@ -4934,12 +4943,33 @@ class Compiler(
|
|||||||
|
|
||||||
private fun inferCallReturnClass(ref: CallRef): ObjClass? {
|
private fun inferCallReturnClass(ref: CallRef): ObjClass? {
|
||||||
return when (val target = ref.target) {
|
return when (val target = ref.target) {
|
||||||
is LocalSlotRef -> callableReturnTypeByScopeId[target.scopeId]?.get(target.slot)
|
is LocalSlotRef -> when (target.name) {
|
||||||
?: resolveClassByName(target.name)
|
"lazy" -> resolveClassByName("lazy")
|
||||||
is LocalVarRef -> callableReturnTypeByName[target.name]
|
"iterator" -> ObjIterator
|
||||||
?: resolveClassByName(target.name)
|
"flow" -> ObjFlow.type
|
||||||
is FastLocalVarRef -> callableReturnTypeByName[target.name]
|
"launch" -> ObjDeferred.type
|
||||||
?: resolveClassByName(target.name)
|
"dynamic" -> ObjDynamic.type
|
||||||
|
else -> callableReturnTypeByScopeId[target.scopeId]?.get(target.slot)
|
||||||
|
?: resolveClassByName(target.name)
|
||||||
|
}
|
||||||
|
is LocalVarRef -> when (target.name) {
|
||||||
|
"lazy" -> resolveClassByName("lazy")
|
||||||
|
"iterator" -> ObjIterator
|
||||||
|
"flow" -> ObjFlow.type
|
||||||
|
"launch" -> ObjDeferred.type
|
||||||
|
"dynamic" -> ObjDynamic.type
|
||||||
|
else -> callableReturnTypeByName[target.name]
|
||||||
|
?: resolveClassByName(target.name)
|
||||||
|
}
|
||||||
|
is FastLocalVarRef -> when (target.name) {
|
||||||
|
"lazy" -> resolveClassByName("lazy")
|
||||||
|
"iterator" -> ObjIterator
|
||||||
|
"flow" -> ObjFlow.type
|
||||||
|
"launch" -> ObjDeferred.type
|
||||||
|
"dynamic" -> ObjDynamic.type
|
||||||
|
else -> callableReturnTypeByName[target.name]
|
||||||
|
?: resolveClassByName(target.name)
|
||||||
|
}
|
||||||
is ConstRef -> when (val value = target.constValue) {
|
is ConstRef -> when (val value = target.constValue) {
|
||||||
is ObjClass -> value
|
is ObjClass -> value
|
||||||
is ObjString -> ObjString.type
|
is ObjString -> ObjString.type
|
||||||
@ -7490,6 +7520,20 @@ class Compiler(
|
|||||||
val loopSlotPlan = SlotPlan(mutableMapOf(), 0, nextScopeId++)
|
val loopSlotPlan = SlotPlan(mutableMapOf(), 0, nextScopeId++)
|
||||||
slotPlanStack.add(loopSlotPlan)
|
slotPlanStack.add(loopSlotPlan)
|
||||||
declareSlotName(tVar.value, isMutable = true, isDelegated = false)
|
declareSlotName(tVar.value, isMutable = true, isDelegated = false)
|
||||||
|
val loopSlotIndex = loopSlotPlan.slots[tVar.value]?.index
|
||||||
|
val loopVarTypeDecl = inferForLoopElementType(source, constRange)
|
||||||
|
val hadLoopNameType = nameTypeDecl.containsKey(tVar.value)
|
||||||
|
val prevLoopNameType = nameTypeDecl[tVar.value]
|
||||||
|
val hadLoopNameClass = nameObjClass.containsKey(tVar.value)
|
||||||
|
val prevLoopNameClass = nameObjClass[tVar.value]
|
||||||
|
if (loopSlotIndex != null && loopVarTypeDecl != null) {
|
||||||
|
slotTypeDeclByScopeId.getOrPut(loopSlotPlan.id) { mutableMapOf() }[loopSlotIndex] = loopVarTypeDecl
|
||||||
|
nameTypeDecl[tVar.value] = loopVarTypeDecl
|
||||||
|
resolveTypeDeclObjClass(loopVarTypeDecl)?.let { loopVarClass ->
|
||||||
|
slotTypeByScopeId.getOrPut(loopSlotPlan.id) { mutableMapOf() }[loopSlotIndex] = loopVarClass
|
||||||
|
nameObjClass[tVar.value] = loopVarClass
|
||||||
|
}
|
||||||
|
}
|
||||||
val (canBreak, body, elseStatement) = try {
|
val (canBreak, body, elseStatement) = try {
|
||||||
resolutionSink?.enterScope(ScopeKind.BLOCK, tVar.pos, null)
|
resolutionSink?.enterScope(ScopeKind.BLOCK, tVar.pos, null)
|
||||||
resolutionSink?.declareSymbol(tVar.value, SymbolKind.LOCAL, isMutable = true, pos = tVar.pos)
|
resolutionSink?.declareSymbol(tVar.value, SymbolKind.LOCAL, isMutable = true, pos = tVar.pos)
|
||||||
@ -7509,6 +7553,16 @@ class Compiler(
|
|||||||
Triple(loopParsed.first, loopParsed.second, elseStmt)
|
Triple(loopParsed.first, loopParsed.second, elseStmt)
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
if (hadLoopNameType) {
|
||||||
|
nameTypeDecl[tVar.value] = prevLoopNameType!!
|
||||||
|
} else {
|
||||||
|
nameTypeDecl.remove(tVar.value)
|
||||||
|
}
|
||||||
|
if (hadLoopNameClass) {
|
||||||
|
nameObjClass[tVar.value] = prevLoopNameClass!!
|
||||||
|
} else {
|
||||||
|
nameObjClass.remove(tVar.value)
|
||||||
|
}
|
||||||
resolutionSink?.exitScope(cc.currentPos())
|
resolutionSink?.exitScope(cc.currentPos())
|
||||||
slotPlanStack.removeLast()
|
slotPlanStack.removeLast()
|
||||||
}
|
}
|
||||||
@ -9164,7 +9218,6 @@ class Compiler(
|
|||||||
varTypeDecl = inferred
|
varTypeDecl = inferred
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDelegate && initialExpression != null) {
|
if (isDelegate && initialExpression != null) {
|
||||||
ensureDelegateType(initialExpression)
|
ensureDelegateType(initialExpression)
|
||||||
val lazyClass = resolveClassByName("lazy")
|
val lazyClass = resolveClassByName("lazy")
|
||||||
|
|||||||
@ -209,4 +209,26 @@ class TestCoroutines {
|
|||||||
// }.toList())
|
// }.toList())
|
||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testInferenceList() = runTest {
|
||||||
|
eval("""
|
||||||
|
import lyng.time
|
||||||
|
|
||||||
|
val d1 = launch {
|
||||||
|
delay(1000.milliseconds)
|
||||||
|
"Task A finished"
|
||||||
|
}
|
||||||
|
val d2 = launch {
|
||||||
|
delay(500.milliseconds)
|
||||||
|
"Task B finished"
|
||||||
|
}
|
||||||
|
val foo = [d1, d2]
|
||||||
|
for (d in foo) {
|
||||||
|
d.await()
|
||||||
|
println(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2050,9 +2050,9 @@ class ScriptTest {
|
|||||||
fun nationalCharsTest() = runTest {
|
fun nationalCharsTest() = runTest {
|
||||||
eval(
|
eval(
|
||||||
"""
|
"""
|
||||||
fun сумма_ряда(x, погрешность=0.0001, f) {
|
fun сумма_ряда(x, погрешность=0.001, f) {
|
||||||
var сумма = 0
|
var сумма = 0
|
||||||
for( n in 1..100000) {
|
for( n in 1..5000) {
|
||||||
val следующая_сумма = сумма + f(x, n)
|
val следующая_сумма = сумма + f(x, n)
|
||||||
if( n > 1 && abs(следующая_сумма - сумма) < погрешность )
|
if( n > 1 && abs(следующая_сумма - сумма) < погрешность )
|
||||||
break следующая_сумма
|
break следующая_сумма
|
||||||
|
|||||||
@ -97,15 +97,15 @@ class ComplexModuleTest {
|
|||||||
assert( 5 + 1.d.i is Complex )
|
assert( 5 + 1.d.i is Complex )
|
||||||
assert( 5.d + 1.i is Complex )
|
assert( 5.d + 1.i is Complex )
|
||||||
assert( 5.d + 2.d.i is Complex )
|
assert( 5.d + 2.d.i is Complex )
|
||||||
assertEquals("0.0+1.0i", 1.d.i.toString())
|
assert(1.d.i.toString() in ["0.0+1.0i", "0+1i"])
|
||||||
assertEquals("1.0+0.0i", 1.d.re.toString())
|
assert(1.d.re.toString() in ["1.0+0.0i", "1+0i"])
|
||||||
|
|
||||||
var c = 1 + 2.i
|
var c = 1 + 2.i
|
||||||
assert(c is Complex)
|
assert(c is Complex)
|
||||||
assertEquals("1.0+2.0i", c.toString())
|
assert(c.toString() in ["1.0+2.0i", "1+2i"])
|
||||||
|
|
||||||
c = 1.d + 2.i
|
c = 1.d + 2.i
|
||||||
assertEquals("1.0+2.0i", c.toString())
|
assert(c.toString() in ["1.0+2.0i", "1+2i"])
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -413,6 +413,9 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#/docs/downloads.md" data-route="downloads">Downloads</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="#/tryling" data-route="tryling">Try in browser</a>
|
<a class="nav-link" href="#/tryling" data-route="tryling">Try in browser</a>
|
||||||
</li>
|
</li>
|
||||||
@ -492,6 +495,8 @@
|
|||||||
var activeLink = null;
|
var activeLink = null;
|
||||||
if (!hash || hash === '#' || hash === '#/') {
|
if (!hash || hash === '#' || hash === '#/') {
|
||||||
activeLink = document.querySelector('#topbarNav .nav-link[data-route="home"]');
|
activeLink = document.querySelector('#topbarNav .nav-link[data-route="home"]');
|
||||||
|
} else if (hash.startsWith('#/docs/downloads.md')) {
|
||||||
|
activeLink = document.querySelector('#topbarNav .nav-link[data-route="downloads"]');
|
||||||
} else if (hash.startsWith('#/docs/') || hash.startsWith('#/authors')) {
|
} else if (hash.startsWith('#/docs/') || hash.startsWith('#/authors')) {
|
||||||
// Mark Docs menu root as active
|
// Mark Docs menu root as active
|
||||||
activeLink = document.querySelector('#topbarNav .nav-link.dropdown-toggle');
|
activeLink = document.querySelector('#topbarNav .nav-link.dropdown-toggle');
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user