From b253eed03234b09d7b1149a9002e58b3d71530a0 Mon Sep 17 00:00:00 2001 From: sergeych Date: Wed, 11 Jun 2025 08:57:12 +0400 Subject: [PATCH] fixed 1e-6 type Real literal --- docs/samples/sum.lyng | 26 +++ .../net/sergeych/lyng/ArgsDeclaration.kt | 8 +- .../kotlin/net/sergeych/lyng/Compiler.kt | 2 +- .../kotlin/net/sergeych/lyng/Parser.kt | 14 +- library/src/commonTest/kotlin/ScriptTest.kt | 209 ++++++++++++------ 5 files changed, 189 insertions(+), 70 deletions(-) create mode 100644 docs/samples/sum.lyng diff --git a/docs/samples/sum.lyng b/docs/samples/sum.lyng new file mode 100644 index 0000000..58dc1ab --- /dev/null +++ b/docs/samples/sum.lyng @@ -0,0 +1,26 @@ +/* + Calculate the limit of Sum( f(n) ) + until it reaches asymptotic limit 0.00001% change + + return null or found limit +*/ +fun findSumLimit(f) { + var sum = 0.0 + for( n in 1..1000000 ) { + val s0 = sum + sum += f(n) + if( abs(sum - s0) / abs(sum) < 1.0e-6 ) { + println("limit reached after "+n+" rounds") + break sum + } + n++ + } + else { + println("limit not reached") + null + } +} + +val limit = findSumLimit { n -> 1.0/n/n } + +println("Result: "+limit) diff --git a/library/src/commonMain/kotlin/net/sergeych/lyng/ArgsDeclaration.kt b/library/src/commonMain/kotlin/net/sergeych/lyng/ArgsDeclaration.kt index 124087f..30ad2a9 100644 --- a/library/src/commonMain/kotlin/net/sergeych/lyng/ArgsDeclaration.kt +++ b/library/src/commonMain/kotlin/net/sergeych/lyng/ArgsDeclaration.kt @@ -11,7 +11,8 @@ data class ArgsDeclaration(val params: List, val endTokenType: Token.Type) val start = params.indexOfFirst { it.defaultValue != null } if (start >= 0) for (j in start + 1 until params.size) - if (params[j].defaultValue == null) throw ScriptError( + // last non-default could be lambda: + if (params[j].defaultValue == null && j != params.size - 1) throw ScriptError( params[j].pos, "required argument can't follow default one" ) @@ -22,7 +23,7 @@ data class ArgsDeclaration(val params: List, val endTokenType: Token.Type) */ suspend fun assignToContext( context: Context, - fromArgs: Arguments = context.args, + _fromArgs: Arguments = context.args, defaultAccessType: AccessType = AccessType.Var, defaultVisibility: Visibility = Visibility.Public ) { @@ -31,6 +32,9 @@ data class ArgsDeclaration(val params: List, val endTokenType: Token.Type) a.visibility ?: defaultVisibility) } + // will be used with last lambda arg fix + val fromArgs = _fromArgs + suspend fun processHead(index: Int): Int { var i = index while (i != params.size) { diff --git a/library/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt b/library/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt index 4fa7f83..10a8d2e 100644 --- a/library/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt +++ b/library/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt @@ -1330,7 +1330,7 @@ class Compiler( /** * The keywords that stop processing of expression term */ - val stopKeywords = setOf("break", "continue", "return", "if", "when", "do", "while", "for", "class", "struct") + val stopKeywords = setOf("do", "break", "continue", "return", "if", "when", "do", "while", "for", "class", "struct") } } diff --git a/library/src/commonMain/kotlin/net/sergeych/lyng/Parser.kt b/library/src/commonMain/kotlin/net/sergeych/lyng/Parser.kt index 05dc2f1..2af5e19 100644 --- a/library/src/commonMain/kotlin/net/sergeych/lyng/Parser.kt +++ b/library/src/commonMain/kotlin/net/sergeych/lyng/Parser.kt @@ -293,7 +293,19 @@ private class Parser(fromPos: Pos) { private fun decodeNumber(p1: String, start: Pos): Token = if (pos.end) Token(p1, start, Token.Type.INT) - else if (currentChar == '.') { + else if( currentChar == 'e' || currentChar == 'E' ) { + pos.advance() + var negative = false + if (currentChar == '+') + pos.advance() + else if (currentChar == '-') { + negative = true + pos.advance() + } + var p3 = loadChars(digits) + if (negative) p3 = "-$p3" + Token("${p1}e$p3", start, Token.Type.REAL) + } else if (currentChar == '.') { // could be decimal pos.advance() if (currentChar in digitsSet) { diff --git a/library/src/commonTest/kotlin/ScriptTest.kt b/library/src/commonTest/kotlin/ScriptTest.kt index 715cc2b..3ac12d6 100644 --- a/library/src/commonTest/kotlin/ScriptTest.kt +++ b/library/src/commonTest/kotlin/ScriptTest.kt @@ -57,6 +57,8 @@ class ScriptTest { check("17.2e22", Token.Type.REAL, 0, 0, "17.2E+22") check("17.2e22", Token.Type.REAL, 0, 0, "17.2E22") check("17.2e-22", Token.Type.REAL, 0, 0, "17.2E-22") + check("17.2e-22", Token.Type.REAL, 0, 0, "17.2E-22") + check("1e-22", Token.Type.REAL, 0, 0, "1E-22") // hex check("1", Token.Type.HEX, 0, 0, "0x1") @@ -490,37 +492,41 @@ class ScriptTest { fun testAssignArgumentsNoEllipsis() = runTest { // equal args, no ellipsis, no defaults, ok val ttEnd = Token.Type.RBRACE - var pa = ArgsDeclaration(listOf( - ArgsDeclaration.Item("a"), - ArgsDeclaration.Item("b"), - ArgsDeclaration.Item("c"), - ), ttEnd) - var c = Context(pos = Pos.builtIn, args = Arguments.from(listOf(1,2,3).map { it.toObj() })) + var pa = ArgsDeclaration( + listOf( + ArgsDeclaration.Item("a"), + ArgsDeclaration.Item("b"), + ArgsDeclaration.Item("c"), + ), ttEnd + ) + var c = Context(pos = Pos.builtIn, args = Arguments.from(listOf(1, 2, 3).map { it.toObj() })) pa.assignToContext(c) - assertEquals( ObjInt(1), c["a"]?.value) - assertEquals( ObjInt(2), c["b"]?.value) - assertEquals( ObjInt(3), c["c"]?.value) + assertEquals(ObjInt(1), c["a"]?.value) + assertEquals(ObjInt(2), c["b"]?.value) + assertEquals(ObjInt(3), c["c"]?.value) // less args: error - c = Context(pos = Pos.builtIn, args = Arguments.from(listOf(1,2).map { it.toObj() })) + c = Context(pos = Pos.builtIn, args = Arguments.from(listOf(1, 2).map { it.toObj() })) assertFailsWith { pa.assignToContext(c) } // less args, no ellipsis, defaults, ok - pa = ArgsDeclaration(listOf( - ArgsDeclaration.Item("a"), - ArgsDeclaration.Item("b"), - ArgsDeclaration.Item("c", defaultValue = statement { ObjInt(100) }), - ), ttEnd) + pa = ArgsDeclaration( + listOf( + ArgsDeclaration.Item("a"), + ArgsDeclaration.Item("b"), + ArgsDeclaration.Item("c", defaultValue = statement { ObjInt(100) }), + ), ttEnd + ) pa.assignToContext(c) - assertEquals( ObjInt(1), c["a"]?.value) - assertEquals( ObjInt(2), c["b"]?.value) - assertEquals( ObjInt(100), c["c"]?.value) + assertEquals(ObjInt(1), c["a"]?.value) + assertEquals(ObjInt(2), c["b"]?.value) + assertEquals(ObjInt(100), c["c"]?.value) // enough args. default value is ignored: c = Context(pos = Pos.builtIn, args = Arguments.from(listOf(10, 2, 5).map { it.toObj() })) pa.assignToContext(c) - assertEquals( ObjInt(10), c["a"]?.value) - assertEquals( ObjInt(2), c["b"]?.value) - assertEquals( ObjInt(5), c["c"]?.value) + assertEquals(ObjInt(10), c["a"]?.value) + assertEquals(ObjInt(2), c["b"]?.value) + assertEquals(ObjInt(5), c["c"]?.value) } @Test @@ -528,16 +534,18 @@ class ScriptTest { // equal args, // less args, no ellipsis, defaults, ok val ttEnd = Token.Type.RBRACE - val pa = ArgsDeclaration(listOf( - ArgsDeclaration.Item("a"), - ArgsDeclaration.Item("b", isEllipsis = true), - ), ttEnd) - var c = Context(args = Arguments.from(listOf(1,2,3).map { it.toObj() })) + val pa = ArgsDeclaration( + listOf( + ArgsDeclaration.Item("a"), + ArgsDeclaration.Item("b", isEllipsis = true), + ), ttEnd + ) + var c = Context(args = Arguments.from(listOf(1, 2, 3).map { it.toObj() })) pa.assignToContext(c) c.eval("assert( a == 1 ); println(b)") c.eval("assert( b == [2,3] )") - c = Context(args = Arguments.from(listOf(1,2).map { it.toObj() })) + c = Context(args = Arguments.from(listOf(1, 2).map { it.toObj() })) pa.assignToContext(c) c.eval("assertEquals( a, 1 ); println(b)") c.eval("assertEquals( b, [2] )") @@ -551,24 +559,26 @@ class ScriptTest { @Test fun testAssignArgumentsStartEllipsis() = runTest { val ttEnd = Token.Type.RBRACE - val pa = ArgsDeclaration(listOf( - ArgsDeclaration.Item("a", isEllipsis = true), - ArgsDeclaration.Item("b"), - ArgsDeclaration.Item("c"), - ), ttEnd) - var c = Context(args = Arguments.from(listOf(0,1,2,3).map { it.toObj() })) + val pa = ArgsDeclaration( + listOf( + ArgsDeclaration.Item("a", isEllipsis = true), + ArgsDeclaration.Item("b"), + ArgsDeclaration.Item("c"), + ), ttEnd + ) + var c = Context(args = Arguments.from(listOf(0, 1, 2, 3).map { it.toObj() })) pa.assignToContext(c) c.eval("assertEquals( a,[0,1] )") c.eval("assertEquals( b, 2 )") c.eval("assertEquals( c, 3 )") - c = Context(args = Arguments.from(listOf(1,2,3).map { it.toObj() })) + c = Context(args = Arguments.from(listOf(1, 2, 3).map { it.toObj() })) pa.assignToContext(c) c.eval("assertEquals( a,[1] )") c.eval("assertEquals( b, 2 )") c.eval("assertEquals( c, 3 )") - c = Context(args = Arguments.from(listOf(2,3).map { it.toObj() })) + c = Context(args = Arguments.from(listOf(2, 3).map { it.toObj() })) pa.assignToContext(c) c.eval("assertEquals( a,[] )") c.eval("assertEquals( b, 2 )") @@ -583,34 +593,36 @@ class ScriptTest { @Test fun testAssignArgumentsmiddleEllipsis() = runTest { val ttEnd = Token.Type.RBRACE - val pa = ArgsDeclaration(listOf( - ArgsDeclaration.Item("i"), - ArgsDeclaration.Item("a", isEllipsis = true), - ArgsDeclaration.Item("b"), - ArgsDeclaration.Item("c"), - ), ttEnd) - var c = Context(args = Arguments.from(listOf(-1,0,1,2,3).map { it.toObj() })) + val pa = ArgsDeclaration( + listOf( + ArgsDeclaration.Item("i"), + ArgsDeclaration.Item("a", isEllipsis = true), + ArgsDeclaration.Item("b"), + ArgsDeclaration.Item("c"), + ), ttEnd + ) + var c = Context(args = Arguments.from(listOf(-1, 0, 1, 2, 3).map { it.toObj() })) pa.assignToContext(c) c.eval("assertEquals( i, -1 )") c.eval("assertEquals( a,[0,1] )") c.eval("assertEquals( b, 2 )") c.eval("assertEquals( c, 3 )") - c = Context(args = Arguments.from(listOf(0, 1,2,3).map { it.toObj() })) + c = Context(args = Arguments.from(listOf(0, 1, 2, 3).map { it.toObj() })) pa.assignToContext(c) c.eval("assertEquals( i, 0 )") c.eval("assertEquals( a,[1] )") c.eval("assertEquals( b, 2 )") c.eval("assertEquals( c, 3 )") - c = Context(args = Arguments.from(listOf(1,2,3).map { it.toObj() })) + c = Context(args = Arguments.from(listOf(1, 2, 3).map { it.toObj() })) pa.assignToContext(c) c.eval("assertEquals( i, 1)") c.eval("assertEquals( a,[] )") c.eval("assertEquals( b, 2 )") c.eval("assertEquals( c, 3 )") - c = Context(args = Arguments.from(listOf(2,3).map { it.toObj() })) + c = Context(args = Arguments.from(listOf(2, 3).map { it.toObj() })) assertFailsWith { pa.assignToContext(c) } @@ -1385,7 +1397,8 @@ class ScriptTest { @Test fun testSimpleStruct() = runTest { val c = Context() - c.eval(""" + c.eval( + """ class Point(x,y) assert( Point::class is Class ) val p = Point(2,3) @@ -1398,13 +1411,15 @@ class ScriptTest { val p2 = Point(p.x+1,p.y+1) p.x = 0 assertEquals( 0, p.x ) - """.trimIndent()) + """.trimIndent() + ) } @Test fun testNonAssignalbeFieldInStruct() = runTest { val c = Context() - c.eval(""" + c.eval( + """ class Point(x,y) val p = Point("2",3) assert(p is Point) @@ -1413,13 +1428,15 @@ class ScriptTest { p.x = 0 assertEquals( 0, p.x ) - """.trimIndent()) + """.trimIndent() + ) } @Test fun testStructBodyVal() = runTest { val c = Context() - c.eval(""" + c.eval( + """ class Point(x,y) { val length = sqrt(x*x+y*y) var foo = "zero" @@ -1433,13 +1450,15 @@ class ScriptTest { assert( p.foo == "bar") // length is a val, is shoud not change assert( p.length == 5 ) - """.trimIndent()) + """.trimIndent() + ) } @Test fun testStructBodyFun() = runTest { val c = Context() - c.eval(""" + c.eval( + """ class Point(x,y) { fun length() { sqrt(x*x+y*y) @@ -1451,23 +1470,27 @@ class ScriptTest { p.y = 10 println(p.length()) assertEquals(sqrt(109), p.length()) - """.trimIndent()) + """.trimIndent() + ) } @Test fun testPrivateConstructorParams() = runTest { val c = Context() - c.eval(""" + c.eval( + """ class Point(private var x,y) val p = Point(1,2) p.y = 101 assertThrows { p.x = 10 } - """) + """ + ) } @Test fun testLBraceMethodCall() = runTest { - eval(""" + eval( + """ class Foo() { fun cond(block) { block() @@ -1475,22 +1498,26 @@ class ScriptTest { } val f = Foo() assertEquals( 1, f.cond { 1 } ) - """.trimIndent()) + """.trimIndent() + ) } @Test fun testLBraceFnCall() = runTest { - eval(""" + eval( + """ fun cond(block) { block() } assertEquals( 1, cond { 1 } ) - """.trimIndent()) + """.trimIndent() + ) } @Test fun testClasstoString() = runTest { - eval(""" + eval( + """ class Point { var x var y @@ -1499,25 +1526,75 @@ class ScriptTest { p.x = 1 p.y = 2 println(p) - """.trimIndent()) + """.trimIndent() + ) } @Test fun testClassDefaultCompare() = runTest { - eval(""" + eval( + """ class Point(x,y) assert( Point(1,2) == Point(1,2) ) assert( Point(1,2) !== Point(1,2) ) assert( Point(1,2) != Point(1,3) ) assert( Point(1,2) < Point(2,2) ) assert( Point(1,2) < Point(1,3) ) - """.trimIndent()) + """.trimIndent() + ) } @Test fun testAccessShortcuts() { - assertTrue( Visibility.Public.isPublic ) - assertFalse( Visibility.Private.isPublic ) - assertFalse( Visibility.Protected.isPublic ) + assertTrue(Visibility.Public.isPublic) + assertFalse(Visibility.Private.isPublic) + assertFalse(Visibility.Protected.isPublic) } + + @Test + fun segfault1Test() = runTest { + eval( + """ + + fun findSumLimit(f) { + var sum = 0.0 + for( n in 1..1000000 ) { + val s0 = sum + sum += f(n) + if( abs(sum - s0) < 0.00001 ) { + println("limit reached after "+n+" rounds") + break sum + } + n++ + } + else { + println("limit not reached") + null + } + } + + val limit = findSumLimit { n -> 1.0/n/n } + + println("Result: "+limit) + """ + ) + } + + @Test + fun testIntExponentRealForm() = runTest { + assertEquals("1.0E-6", eval("1e-6").toString()) + } + +// @Test +// fun testLambdaLastArgAfterDetault() = runTest { +// val c = Context() +// eval(""" +// // this means last is lambda: +// fun f(e=1, f) { +// "e="+e+"f="+f() +// } +// assertEquals("e=1f=xx", f { "xx" }) +// """.trimIndent()) +// +// } } \ No newline at end of file