- fixed bug in compiler (rare)
- added lyng.io.fs (multiplatform) - CLI tools now have access to the filesystem
This commit is contained in:
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("""
|
||||
|
||||
Reference in new issue
Block a user