From 8300c2a3ab009fbde6871cbe74dc1d0ece222432 Mon Sep 17 00:00:00 2001 From: sergeych Date: Tue, 23 Dec 2025 13:06:46 +0100 Subject: [PATCH] v1.1.0-beta1: mini function declarations and working properties --- docs/OOP.md | 7 ++++++- lynglib/build.gradle.kts | 2 +- .../kotlin/net/sergeych/lyng/Compiler.kt | 15 ++++++++++++++- lynglib/src/commonTest/kotlin/ScriptTest.kt | 16 +++++++++++++++- .../kotlin/net/sergeych/lyngweb/Highlight.kt | 6 +++--- 5 files changed, 39 insertions(+), 7 deletions(-) diff --git a/docs/OOP.md b/docs/OOP.md index 694c2a2..9164fb0 100644 --- a/docs/OOP.md +++ b/docs/OOP.md @@ -74,14 +74,18 @@ assertEquals("Adult", p.ageCategory) ### Laconic Expression Shorthand -For simple accessors, you can use the `=` shorthand for a more elegant and laconic form: +For simple accessors and methods, you can use the `=` shorthand for a more elegant and laconic form: ```kotlin class Circle(val radius: Real) { val area get() = π * radius * radius val circumference get() = 2 * π * radius + + fun diameter() = radius * 2 } +fun median(a, b) = (a + b) / 2 + class Counter { private var _count = 0 var count get() = _count set(v) = _count = v @@ -92,6 +96,7 @@ class Counter { - **`val` properties** must have a `get()` accessor and cannot have a `set()`. - **`var` properties** must have both `get()` and `set()` accessors. +- **Functions and methods** can use the `=` shorthand to return the result of a single expression. - **No Backing Fields**: There is no magic `field` identifier. If you need to store state, you must declare a separate (usually `private`) field. - **Type Inference**: You can omit the type declaration if it can be inferred or if you don't need strict typing. diff --git a/lynglib/build.gradle.kts b/lynglib/build.gradle.kts index 850526b..7f97625 100644 --- a/lynglib/build.gradle.kts +++ b/lynglib/build.gradle.kts @@ -21,7 +21,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl import org.jetbrains.kotlin.gradle.dsl.JvmTarget group = "net.sergeych" -version = "1.0.10-SNAPSHOT" +version = "1.1.0-beta1" // Removed legacy buildscript classpath declarations; plugins are applied via the plugins DSL below diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt index 46dfe92..371c87b 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt @@ -2433,7 +2433,20 @@ class Compiler( val fnStatements = if (isExtern) statement { raiseError("extern function not provided: $name") } else - withLocalNames(paramNames) { parseBlock() } + withLocalNames(paramNames) { + val next = cc.peekNextNonWhitespace() + if (next.type == Token.Type.ASSIGN) { + cc.skipWsTokens() + cc.next() // consume '=' + val expr = parseExpression() ?: throw ScriptError(cc.current().pos, "Expected function body expression") + // Shorthand function returns the expression value + statement(expr.pos) { scope -> + expr.execute(scope) + } + } else { + parseBlock() + } + } // Capture and pop the local declarations count for this function val fnLocalDecls = localDeclCountStack.removeLastOrNull() ?: 0 diff --git a/lynglib/src/commonTest/kotlin/ScriptTest.kt b/lynglib/src/commonTest/kotlin/ScriptTest.kt index 0a66f06..489ac56 100644 --- a/lynglib/src/commonTest/kotlin/ScriptTest.kt +++ b/lynglib/src/commonTest/kotlin/ScriptTest.kt @@ -3697,7 +3697,7 @@ class ScriptTest { } - // @Test + @Test fun testMinimumOptimization() = runTest { for (i in 1..200) { bm { @@ -4518,4 +4518,18 @@ class ScriptTest { """.trimIndent() ) } + + @Test + fun testFunMiniDeclaration() = runTest { + eval(""" + class T(x) { + fun method() = x + 1 + } + fun median(a,b) = (a+b)/2 + + assertEquals(11, T(10).method()) + assertEquals(2, median(1,3)) + """.trimIndent()) + } + } diff --git a/lyngweb/src/jsMain/kotlin/net/sergeych/lyngweb/Highlight.kt b/lyngweb/src/jsMain/kotlin/net/sergeych/lyngweb/Highlight.kt index 333f0ac..3daabd3 100644 --- a/lyngweb/src/jsMain/kotlin/net/sergeych/lyngweb/Highlight.kt +++ b/lyngweb/src/jsMain/kotlin/net/sergeych/lyngweb/Highlight.kt @@ -647,14 +647,14 @@ private fun detectDeclarationAndParamOverrides(text: String): Map i = p continue } - // Generic function call site: ident followed by '(' (after optional spaces) + // Generic function call site: ident followed by '(' or '=' (after optional spaces) readIdent(i)?.let { (name, endIdx) -> val startIdx = i // Avoid keywords; allow member calls too (a.b()) by not checking preceding char if (name !in kw) { var q = skipWs(endIdx) - if (q < n && text[q] == '(') { - // Mark as function identifier at call site + if (q < n && (text[q] == '(' || text[q] == '=')) { + // Mark as function identifier at call site or shorthand declaration result[startIdx to endIdx] = "hl-fn" i = endIdx return@let