diff --git a/CHANGELOG.md b/CHANGELOG.md index 33d06dd..0b56322 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,31 @@ -## Changelog +## 1.5.0-SNAPSHOT -### Unreleased +### Language Features +- Added `return` statement with local and non-local exit support (`return@label`). +- Support for `abstract` classes, methods, and variables. +- Introduced `interface` as a synonym for `abstract class`. +- Multiple Inheritance (MI) completed and enabled by default (C3 MRO). +- Class properties with custom accessors (`get`, `set`). +- Restricted setter visibility (`private set`, `protected set`). +- Late-initialized `val` fields in classes with `Unset` protection. +- Named arguments (`name: value`) and named splats (`...Map`). +- Assign-if-null operator `?=`. +- Refined `protected` visibility rules and `closed` modifier. +- Transient attribute `@Transient` for serialization and equality. +- Unified Delegation model for `val`, `var`, and `fun`. +- Singleton objects (`object`) and object expressions. + +### Standard Library +- Added `with(self, block)` for scoped execution. +- Added `clamp()` function and extension. +- Improved `Exception` and `StackTraceEntry` reporting. + +### Tooling and IDE +- **CLI**: Added `fmt` as a first-class subcommand for code formatting. +- **IDEA Plugin**: Lightweight autocompletion (experimental), improved docs, and Grazie integration. +- **Highlighters**: Updated TextMate bundle and website highlighters for new syntax. + +### Detailed Changes: - Language: Refined `protected` visibility rules - Ancestor classes can now access `protected` members of their descendants, provided the ancestor also defines or inherits a member with the same name (indicating an override of a member known to the ancestor). @@ -109,16 +134,6 @@ - Documentation updated (docs/OOP.md and tutorial quick-start) to reflect MI with active C3 MRO. -Notes: -- Existing single-inheritance code continues to work; resolution reduces to the single base. -- If code previously relied on non-deterministic parent set iteration, C3 MRO provides a predictable order; disambiguate explicitly if needed using `this@Type`/casts. - -# Changelog - -All notable changes to this project will be documented in this file. - -## Unreleased - - CLI: Added `fmt` as a first-class Clikt subcommand. - Default behavior: formats files to stdout (no in-place edits by default). - Options: diff --git a/LYNG_AI_SPEC.md b/LYNG_AI_SPEC.md index 8137744..947e77c 100644 --- a/LYNG_AI_SPEC.md +++ b/LYNG_AI_SPEC.md @@ -1,4 +1,4 @@ -# Lyng Language AI Specification (V1.3) +# Lyng Language AI Specification (V1.5.0-SNAPSHOT) High-density specification for LLMs. Reference this for all Lyng code generation. diff --git a/README.md b/README.md index 6fd8584..02c5671 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ assertEquals(A.One, A.E.One) - [Language home](https://lynglang.com) - [introduction and tutorial](docs/tutorial.md) - start here please -- [What's New in 1.3](docs/whats_new_1_3.md) +- [What's New in 1.5](docs/whats_new_1_5.md) - [Testing and Assertions](docs/Testing.md) - [Filesystem and Processes (lyngio)](docs/lyngio.md) - [Return Statement](docs/return_statement.md) @@ -64,7 +64,7 @@ assertEquals(A.One, A.E.One) ```kotlin // update to current please: -val lyngVersion = "0.6.1-SNAPSHOT" +val lyngVersion = "1.5.0-SNAPSHOT" repositories { // ... @@ -177,7 +177,7 @@ Designed to add scripting to kotlin multiplatform application in easy and effici # Language Roadmap -We are now at **v1.0**: basic optimization performed, battery included: standard library is 90% here, initial +We are now at **v1.5.0-SNAPSHOT** (stable development cycle): basic optimization performed, battery included: standard library is 90% here, initial support in HTML, popular editors, and IDEA; tools to syntax highlight and format code are ready. It was released closed to schedule. Ready features: @@ -217,7 +217,7 @@ Ready features: All of this is documented in the [language site](https://lynglang.com) and locally [docs/language.md](docs/tutorial.md). the current nightly builds published on the site and in the private maven repository. -## plan: towards v1.5 Enhancing +## plan: towards v2.0 Next Generation - [x] site with integrated interpreter to give a try - [x] kotlin part public API good docs, integration focused diff --git a/docs/whats_new.md b/docs/whats_new.md index 75aa99c..17d3f81 100644 --- a/docs/whats_new.md +++ b/docs/whats_new.md @@ -1,7 +1,7 @@ # What's New in Lyng This document highlights the latest additions and improvements to the Lyng language and its ecosystem. -For a programmer-focused migration summary, see `docs/whats_new_1_3.md`. +For a programmer-focused migration summary, see `docs/whats_new_1_5.md`. ## Language Features @@ -102,7 +102,7 @@ Singleton objects are declared using the `object` keyword. They provide a conven ```lyng object Config { - val version = "1.2.3" + val version = "1.5.0-SNAPSHOT" fun show() = println("Config version: " + version) } diff --git a/docs/whats_new_1_5.md b/docs/whats_new_1_5.md new file mode 100644 index 0000000..ae9431a --- /dev/null +++ b/docs/whats_new_1_5.md @@ -0,0 +1,93 @@ +# What's New in Lyng 1.5 (vs 1.3.* / master) + +This document summarizes the significant changes and new features introduced in the 1.5.0-SNAPSHOT development cycle. + +## Highlights + +- **The `return` Statement**: Added support for local and non-local returns using labels. +- **Abstract Classes and Interfaces**: Full support for `abstract` members and the `interface` keyword. +- **Class Properties with Accessors**: Define `val` and `var` properties with custom `get()` and `set()`. +- **Restricted Setter Visibility**: Use `private set` and `protected set` on fields and properties. +- **Late-initialized `val`**: Support for `val` fields that are initialized in `init` blocks or class bodies. +- **Named Arguments and Splats**: Improved call-site readability with `name: value` and map-based splats. +- **Refined Visibility**: Improved `protected` access and `closed` modifier for better encapsulation. + +## Language Features + +### The `return` Statement +You can now exit from the innermost enclosing callable (function or lambda) using `return`. Lyng also supports non-local returns to outer scopes using labels. + +```lyng +fun findFirst(list: Iterable, predicate: (T)->Bool): T? { + list.forEach { + if (predicate(it)) return@findFirst it + } + null +} +``` + +### Abstract Classes and Interfaces +Lyng now supports the `abstract` modifier for classes and their members. `interface` is introduced as a synonym for `abstract class`, allowing for rich multi-inheritance patterns. + +```lyng +interface Shape { + abstract val area: Real + fun describe() = "Area: %g"(area) +} + +class Circle(val radius: Real) : Shape { + override val area get = Math.PI * radius * radius +} +``` + +### Class Properties with Accessors +Properties can now have custom getters and setters. They do not have automatic backing fields, making them perfect for computed values or delegation. + +```lyng +class Rectangle(var width: Real, var height: Real) { + val area: Real get() = width * height + + var squareSize: Real + get() = area + set(v) { + width = sqrt(v) + height = width + } +} +``` + +### Named Arguments and Named Splats +Improve call-site clarity by specifying argument names. You can also expand a Map into named arguments using the splat operator. + +```lyng +fun configure(timeout: Int, retry: Int = 3) { ... } + +configure(timeout: 5000, retry: 5) +val options = { timeout: 1000, retry: 1 } +configure(...options) +``` + +### Modern Operators +The `?=` operator allows for concise "assign if null" logic. + +```lyng +var cache: Map? = null +cache ?= { "status": "ok" } // Only assigns if cache is null +``` + +## Tooling and IDE + +- **IDEA Plugin**: Significant improvements to autocompletion, documentation tooltips, and natural language support (Grazie integration). +- **CLI**: The `lyng fmt` command is now a first-class tool for formatting code with various options like `--check` and `--in-place`. +- **Performance**: Ongoing optimizations in the bytecode VM and compiler for faster execution and smaller footprint. + +## Migration Guide (from 1.3.*) + +1. **Check Visibility**: Refined `protected` and `private` rules may catch previously undetected invalid accesses. +2. **Override Keyword**: Ensure all members that override ancestor declarations are marked with the `override` keyword (now mandatory). +3. **Return in Shorthand**: Remember that `return` is forbidden in `=` shorthand functions; use block syntax if you need early exit. + +## References +- `docs/OOP.md` +- `docs/return_statement.md` +- `docs/tutorial.md` diff --git a/lynglib/build.gradle.kts b/lynglib/build.gradle.kts index 9203573..daf1234 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.4.0-SNAPSHOT" +version = "1.5.0-SNAPSHOT" // Removed legacy buildscript classpath declarations; plugins are applied via the plugins DSL below diff --git a/site/src/jsMain/kotlin/HomePage.kt b/site/src/jsMain/kotlin/HomePage.kt index f67bf03..e67fcf4 100644 --- a/site/src/jsMain/kotlin/HomePage.kt +++ b/site/src/jsMain/kotlin/HomePage.kt @@ -29,8 +29,8 @@ fun HomePage() { listOf( """ // Everything is an expression - val x = 10 - val status = if (x > 0) "Positive" else "Zero or Negative" + val x: Int = 10 + val status: String = if (x > 0) "Positive" else "Zero or Negative" // Even loops return values! val result = for (i in 1..5) { @@ -40,8 +40,8 @@ fun HomePage() { println("Result: " + result) """.trimIndent(), """ - // Functional power with ranges and collections - val squares = (1..10) + // Functional power with generics and collections + val squares: List = (1..10) .filter { it % 2 == 0 } .map { it * it } @@ -49,20 +49,43 @@ fun HomePage() { // Output: [4, 16, 36, 64, 100] """.trimIndent(), """ + // Generics and type aliases + typealias Num = Int | Real + + class Box(val value: T) { + fun get(): T = value + } + + val intBox = Box(42) + val realBox = Box(3.14) + println("Boxes: " + intBox.get() + ", " + realBox.get()) + """.trimIndent(), + """ + // Strict compile-time types and symbol resolution + fun greet(name: String, count: Int) { + for (i in 1..count) { + println("Hello, " + name + "!") + } + } + + greet("Lyng", 3) + // greet(10, "error") // This would be a compile-time error! + """.trimIndent(), + """ // Flexible map literals and shorthands val id = 101 val name = "Lyng" val base = { id:, name: } // Shorthand for id: id, name: name - val full = { ...base, version: "1.0", status: "active" } + val full = { ...base, version: "1.5.0-SNAPSHOT", status: "active" } println(full) """.trimIndent(), """ // Modern null safety - var config = null + var config: Map? = null config ?= { timeout: 30 } // Assign only if null - val timeout = config?.timeout ?: 60 + val timeout = config?["timeout"] ?: 60 println("Timeout is: " + timeout) """.trimIndent(), """ @@ -76,10 +99,10 @@ fun HomePage() { """ // Diamond-safe Multiple Inheritance (C3 MRO) interface Logger { - fun log(m) = println("[LOG] " + m) + fun log(m: String) = println("[LOG] " + m) } interface Auth { - fun login(u) = println("Login: " + u) + fun login(u: String) = println("Login: " + u) } class App() : Logger, Auth { @@ -92,30 +115,30 @@ fun HomePage() { """.trimIndent(), """ // Extension functions and properties - fun String.shout() = this.toUpperCase() + "!!!" + fun String.shout(): String = this.uppercase() + "!!!" println("hello".shout()) - val List.second get = this[1] + val List.second: T get() = this[1] println([10, 20, 30].second) """.trimIndent(), """ // Non-local returns from closures - fun findFirst(list, predicate) { + fun findFirst(list: Iterable, predicate: (T)->Bool): T? { list.forEach { if (predicate(it)) return@findFirst it } null } - val found = findFirst([1, 5, 8, 12]) { it > 10 } + val found: Int? = findFirst([1, 5, 8, 12]) { it > 10 } println("Found: " + found) """.trimIndent(), """ // Easy operator overloading - class Vector(val x, val y) { - override fun plus(other) = Vector(x + other.x, y + other.y) - override fun toString() = "Vector(%g, %g)"(x, y) + class Vector(val x: Real, val y: Real) { + override fun plus(other: Vector): Vector = Vector(x + other.x, y + other.y) + override fun toString(): String = "Vector(%g, %g)"(x, y) } val v1 = Vector(1, 2) @@ -125,7 +148,7 @@ fun HomePage() { """ // Property delegation to Map class User() { - var name by Map() + var name: String by Map() } val u = User() @@ -165,17 +188,18 @@ fun HomePage() { Div({ classes("text-center") }) { H1({ classes("display-5", "fw-bold", "mb-3") }) { Text("Welcome to Lyng") } P({ classes("lead", "text-muted", "mb-4") }) { - Text("A lightweight, expressive scripting language designed for clarity, composability, and fun. ") + Text("A lightweight, expressive scripting language with strict static typing and functional power. ") Br() Text("Run it anywhere Kotlin runs — share logic across JS, JVM, and more.") } Div({ classes("d-flex", "justify-content-center", "gap-2", "flex-wrap", "mb-4") }) { // Benefits pills listOf( - "Clean, familiar syntax", + "Strict static typing", + "Generics & Type Aliases", + "Implicit coroutines", "both FP and OOP", - "Batteries-included standard library", - "Embeddable and testable" + "Batteries-included standard library" ).forEach { b -> Span({ classes("badge", "text-bg-secondary", "rounded-pill") }) { Text(b) } } @@ -233,7 +257,7 @@ fun HomePage() { // Short features list Div({ classes("row", "g-4", "mt-1") }) { listOf( - Triple("Fast to learn", "Familiar constructs and readable patterns — be productive in minutes.", "lightning"), + Triple("Safe and Robust", "Strict compile-time resolution, generics, and null safety catch errors early.", "shield-check"), Triple("Portable", "Runs wherever Kotlin runs: reuse logic across platforms.", "globe2"), Triple("Pragmatic", "A standard library that solves real problems without ceremony.", "gear-fill") ).forEach { (title, text, icon) -> diff --git a/site/src/jsMain/kotlin/TryLyngPage.kt b/site/src/jsMain/kotlin/TryLyngPage.kt index c3b47e0..0912139 100644 --- a/site/src/jsMain/kotlin/TryLyngPage.kt +++ b/site/src/jsMain/kotlin/TryLyngPage.kt @@ -22,6 +22,7 @@ import net.sergeych.lyng.Script import net.sergeych.lyng.ScriptError import net.sergeych.lyng.highlight.TextRange import net.sergeych.lyng.miniast.CompletionItem +import net.sergeych.lyng.requireScope import net.sergeych.lyng.tools.LyngDiagnostic import net.sergeych.lyng.tools.LyngDiagnosticSeverity import net.sergeych.lyng.tools.LyngSymbolInfo @@ -48,11 +49,18 @@ fun TryLyngPage(route: String) { var code by remember(initialCode) { mutableStateOf( initialCode ?: """ - // Welcome to Lyng! Edit and run. - // Try changing the data and press Ctrl+Enter or click Run. - - val data = 1..5 // or [1, 2, 3, 4, 5] - data.filter { it % 2 == 0 }.map { it * it } + // Welcome to Lyng! Modern scripting with strict types and generics. + + typealias Numeric = Int | Real + + fun process(items: List): List { + items.filter { it > 0 }.map { it * it } + } + + val data: List = [-2, -1, 0, 1, 2] + println("Processed: " + process(data)) + + // Try changing data or adding Real numbers! """.trimIndent() ) } @@ -94,13 +102,13 @@ fun TryLyngPage(route: String) { s.addVoidFn("print") { for ((i, a) in this.args.withIndex()) { if (i > 0) printed.append(' ') - printed.append(a.toString(this).value) + printed.append(a.toString(this.requireScope()).value) } } s.addVoidFn("println") { for ((i, a) in this.args.withIndex()) { if (i > 0) printed.append(' ') - printed.append(a.toString(this).value) + printed.append(a.toString(this.requireScope()).value) } printed.append('\n') } @@ -154,8 +162,8 @@ fun TryLyngPage(route: String) { fun resetCode() { code = initialCode ?: """ - // Welcome to Lyng! Edit and run. - [1,2,3].map { it * 10 } + // Welcome to Lyng! Modern scripting with strict types and generics. + [1, 2, 3].map { it * 10 } """.trimIndent() output = null error = null diff --git a/site/src/jsMain/resources/index.html b/site/src/jsMain/resources/index.html index 14931bf..43c151b 100644 --- a/site/src/jsMain/resources/index.html +++ b/site/src/jsMain/resources/index.html @@ -329,7 +329,7 @@
- v1.2.0! + v1.5.0-SNAPSHOT!