From a7ab0d390547bb1297b3b0cd7588f2f708c8d3c6 Mon Sep 17 00:00:00 2001 From: sergeych Date: Fri, 3 Apr 2026 21:15:41 +0300 Subject: [PATCH] added static List.fill(size) { ... } --- docs/List.md | 11 ++++++++ docs/OOP.md | 8 ++++++ docs/ai_language_reference.md | 1 + docs/ai_stdlib_reference.md | 3 ++- docs/tutorial.md | 6 +++++ .../kotlin/net/sergeych/lyng/Compiler.kt | 4 ++- .../sergeych/lyng/FunctionDeclStatement.kt | 25 +++++++++++++------ lynglib/src/commonTest/kotlin/ScriptTest.kt | 8 ++++++ lynglib/stdlib/lyng/root.lyng | 7 ++++++ 9 files changed, 64 insertions(+), 9 deletions(-) diff --git a/docs/List.md b/docs/List.md index 017797a..d857c26 100644 --- a/docs/List.md +++ b/docs/List.md @@ -45,6 +45,16 @@ You can concatenate lists or iterable objects: assert( [4,5] + (1..3) == [4, 5, 1, 2, 3]) >>> void +## Constructing lists + +Besides literals, you can build a list by size using `List.fill`: + + val squares = List.fill(5) { i -> i * i } + assertEquals([0, 1, 4, 9, 16], squares) + >>> void + +`List.fill(size) { ... }` calls the block once for each index from `0` to `size - 1` and returns a new mutable list. + ## Appending To append to lists, use `+=` with elements, lists and any [Iterable] instances, but beware it will @@ -164,6 +174,7 @@ List could be sorted in place, just like [Collection] provide sorted copies, in | `[index]` | get or set element at index | Int | | `[Range]` | get slice of the array (copy) | Range | | `+=` | append element(s) (2) | List or Obj | +| `List.fill(size, block)` | build a new list from indices `0..>> void +Extension methods normally act like instance members. If declared as `static`, they are called on the type object itself: + +```lyng +static fun List.fill(size: Int, block: (Int)->T): List { ... } + +val tens = List.fill(5) { it * 10 } +``` + ## Extension properties Just like methods, you can extend existing classes with properties. These can be defined using simple initialization (for `val` only) or with custom accessors. diff --git a/docs/ai_language_reference.md b/docs/ai_language_reference.md index dab1ef2..e906cec 100644 --- a/docs/ai_language_reference.md +++ b/docs/ai_language_reference.md @@ -116,6 +116,7 @@ Primary sources used: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/{Parser,T - shorthand: `fun f(x) = expr`. - generics: `fun f(x: T): T`. - extension functions: `fun Type.name(...) { ... }`. + - static extension functions are callable on the type object: `static fun List.fill(...)` -> `List.fill(...)`. - delegated callable: `fun f(...) by delegate`. - Type aliases: - `type Name = TypeExpr` diff --git a/docs/ai_stdlib_reference.md b/docs/ai_stdlib_reference.md index 8f450fd..db45c7b 100644 --- a/docs/ai_stdlib_reference.md +++ b/docs/ai_stdlib_reference.md @@ -46,7 +46,8 @@ Sources: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/Script.kt`, `lynglib/s - Iteration/filtering: `forEach`, `filter`, `filterFlow`, `filterNotNull`, `filterFlowNotNull`, `drop`, `dropLast`, `takeLast`. - Search/predicates: `findFirst`, `findFirstOrNull`, `any`, `all`, `count`, `first`, `last`. - Mapping/aggregation: `map`, `flatMap`, `flatten`, `sum`, `sumOf`, `minOf`, `maxOf`. -- Ordering: `sorted`, `sortedBy`, `shuffled`, `List.sort`, `List.sortBy`. +- Ordering and list building: `sorted`, `sortedBy`, `shuffled`, `List.sort`, `List.sortBy`, `List.fill`. + - `List.fill(size) { index -> ... }` constructs a new `List` by evaluating the block once per index from `0` to `size - 1`. - String helper: `joinToString`, `String.re`. ### 4.3 Delegation helpers diff --git a/docs/tutorial.md b/docs/tutorial.md index 5c922de..e0e7dfd 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -811,6 +811,12 @@ Lyng has built-in mutable array class `List` with simple literals: many collection based methods are implemented there. For immutable list values, use `list.toImmutable()` and [ImmutableList]. +To construct a list programmatically, use the static helper `List.fill`: + + val tens = List.fill(5) { index -> index * 10 } + assertEquals([0, 10, 20, 30, 40], tens) + >>> void + Lists can contain any type of objects, lists too: val list = [1, [2, 3], 4] diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt index f350186..8d93fd0 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt @@ -4359,6 +4359,7 @@ class Compiler( is ListLiteralRef -> inferListLiteralTypeDecl(ref) is MapLiteralRef -> inferMapLiteralTypeDecl(ref) is ConstRef -> inferTypeDeclFromConst(ref.constValue) + is RangeRef -> TypeDecl.Simple("Range", false) is CallRef -> { val targetDecl = resolveReceiverTypeDecl(ref.target) ?: seedTypeDeclFromRef(ref.target) val targetName = when (val target = ref.target) { @@ -4394,6 +4395,7 @@ class Compiler( is ObjString -> TypeDecl.Simple("String", false) is ObjBool -> TypeDecl.Simple("Bool", false) is ObjChar -> TypeDecl.Simple("Char", false) + is ObjRange -> TypeDecl.Simple("Range", false) is ObjNull -> TypeDecl.TypeNullableAny is ObjList -> TypeDecl.Generic("List", listOf(TypeDecl.TypeAny), false) is ObjMap -> TypeDecl.Generic("Map", listOf(TypeDecl.TypeAny, TypeDecl.TypeAny), false) @@ -8714,7 +8716,7 @@ class Compiler( startPos = start ) val declaredFn = FunctionDeclStatement(spec) - if (isStatic) { + if (isStatic && parentIsClassBody) { currentInitScope += declaredFn NopStatement } else diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/FunctionDeclStatement.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/FunctionDeclStatement.kt index 615d936..5a6366e 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/FunctionDeclStatement.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/FunctionDeclStatement.kt @@ -169,18 +169,29 @@ internal suspend fun executeFunctionDecl( spec.extTypeName?.let { typeName -> val type = scope[typeName]?.value ?: scope.raiseSymbolNotFound("class $typeName not found") if (type !is ObjClass) scope.raiseClassCastError("$typeName is not the class instance") - scope.addExtension( - type, - spec.name, - ObjRecord( + if (spec.isStatic) { + type.createClassField( + spec.name, compiledFnBody, isMutable = false, visibility = spec.visibility, - declaringClass = null, + pos = spec.startPos, type = ObjRecord.Type.Fun, - typeDecl = spec.typeDecl ) - ) + } else { + scope.addExtension( + type, + spec.name, + ObjRecord( + compiledFnBody, + isMutable = false, + visibility = spec.visibility, + declaringClass = null, + type = ObjRecord.Type.Fun, + typeDecl = spec.typeDecl + ) + ) + } val wrapperName = spec.extensionWrapperName ?: extensionCallableName(typeName, spec.name) val wrapper = ObjExtensionMethodCallable(spec.name, compiledFnBody) scope.addItem( diff --git a/lynglib/src/commonTest/kotlin/ScriptTest.kt b/lynglib/src/commonTest/kotlin/ScriptTest.kt index 5aeb01a..d29b161 100644 --- a/lynglib/src/commonTest/kotlin/ScriptTest.kt +++ b/lynglib/src/commonTest/kotlin/ScriptTest.kt @@ -4021,6 +4021,14 @@ class ScriptTest { ) } + @Test + fun testListFill() = runTest { + eval(""" + val x = List.fill(5) { it*10 } + assertEquals( [0,10,20,30,40], x) + """.trimIndent()) + } + @Test fun binarySearchTest() = runTest { eval( diff --git a/lynglib/stdlib/lyng/root.lyng b/lynglib/stdlib/lyng/root.lyng index b447087..dff3de8 100644 --- a/lynglib/stdlib/lyng/root.lyng +++ b/lynglib/stdlib/lyng/root.lyng @@ -422,6 +422,13 @@ fun List.sort(): void { sortWith { a, b -> a <=> b } } +/* Build a new list of `size` elements by calling `block(index)` for each index. */ +static fun List.fill(size: Int, block: (Int)->T): List { + val result = List() + for( i in 0..