- fixed bug in compiler (rare)

- added lyng.io.fs (multiplatform)
- CLI tools now have access to the filesystem
This commit is contained in:
sergeych committed 2025-11-29 00:51:01 +01:00
1 parent 41746f22e5
commit 438e48959e
28 files changed
+2137 -6

No files matched your search

@@ -494,9 +494,25 @@ class Compiler(
when (t.value) {
in stopKeywords -> {
if (operand != null) throw ScriptError(t.pos, "unexpected keyword")
cc.previous()
val s = parseStatement() ?: throw ScriptError(t.pos, "Expecting valid statement")
operand = StatementRef(s)
// Allow certain statement-like constructs to act as expressions
// when they appear in expression position (e.g., `if (...) ... else ...`).
// Other keywords should be handled by the outer statement parser.
when (t.value) {
"if" -> {
val s = parseIfStatement()
operand = StatementRef(s)
}
"when" -> {
val s = parseWhenStatement()
operand = StatementRef(s)
}
else -> {
// Do not consume the keyword as part of a term; backtrack
// and return null so outer parser handles it.
cc.previous()
return null
}
}
}
"else", "break", "continue" -> {
@@ -2922,7 +2938,11 @@ class Compiler(
// assigner.generate(context.pos, left, right)
// }
val lastLevel = lastPriority + 1
// Compute levels from the actual operator table rather than relying on
// the mutable construction counter. This prevents accidental inflation
// of precedence depth that could lead to deep recursive descent and
// StackOverflowError during parsing.
val lastLevel = (allOps.maxOf { it.priority }) + 1
val byLevel: List<Map<Token.Type, Operator>> = (0..<lastLevel).map { l ->
allOps.filter { it.priority == l }.associateBy { it.tokenType }
@@ -14,8 +14,8 @@
* limitations under the License.
*
*/
package net.sergeych.lyng.stdlib_included
internal val rootLyng = """
package lyng.stdlib
@@ -123,6 +123,26 @@ fun Iterable.sumOf(f) {
else null
}
fun Iterable.minOf( lambda ) {
val i = iterator()
var minimum = lambda( i.next() )
while( i.hasNext() ) {
val x = lambda(i.next())
if( x < minimum ) minimum = x
}
minimum
}
fun Iterable.maxOf( lambda ) {
val i = iterator()
var maximum = lambda( i.next() )
while( i.hasNext() ) {
val x = lambda(i.next())
if( x > maximum ) maximum = x
}
maximum
}
fun Iterable.sorted() {
sortedWith { a, b -> a <=> b }
}
@@ -3667,6 +3667,48 @@ class ScriptTest {
}
@Test
fun testIterableMinMax() = runTest {
eval("""
import lyng.stdlib
assertEquals( -100, (1..100).toList().minOf { -it } )
assertEquals( -1, (1..100).toList().maxOf { -it } )
""".trimIndent()
)
}
@Test
fun testParserOverflow() = runTest {
try {
eval(
"""
fun Iterable.minByWithIndex( lambda ) {
val i = iterator()
var index = 0
if( !i.hasNext() ) return null
var value = i.next()
var n = 1
while( i.hasNext() ) {
val x = lambda(i.next())
if( x < value ) {
index = n
value = x
}
n++
}
index => value
}
""".trimIndent()
)
// If it compiles fine, great. The test passes implicitly.
} catch (e: ScriptError) {
// The important part: no StackOverflowError anymore, but a meaningful ScriptError
// is thrown. Accept any ScriptError as a valid outcome.
println(e.message)
}
}
// @Test
// fun namedArgsProposal() = runTest {
// eval("""