added static List.fill(size) { ... }

This commit is contained in:
Sergey Chernov 2026-04-03 21:15:41 +03:00
parent f1003f5b95
commit a7ab0d3905
9 changed files with 64 additions and 9 deletions

View File

@ -45,6 +45,16 @@ You can concatenate lists or iterable objects:
assert( [4,5] + (1..3) == [4, 5, 1, 2, 3]) assert( [4,5] + (1..3) == [4, 5, 1, 2, 3])
>>> void >>> 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 ## Appending
To append to lists, use `+=` with elements, lists and any [Iterable] instances, but beware it will 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 | | `[index]` | get or set element at index | Int |
| `[Range]` | get slice of the array (copy) | Range | | `[Range]` | get slice of the array (copy) | Range |
| `+=` | append element(s) (2) | List or Obj | | `+=` | append element(s) (2) | List or Obj |
| `List.fill(size, block)` | build a new list from indices `0..<size` | Int, Callable |
| `sort()` | in-place sort, natural order | void | | `sort()` | in-place sort, natural order | void |
| `sortBy(predicate)` | in-place sort bu `predicate` call result (3) | void | | `sortBy(predicate)` | in-place sort bu `predicate` call result (3) | void |
| `sortWith(comparator)` | in-place sort using `comarator` function (4) | void | | `sortWith(comparator)` | in-place sort using `comarator` function (4) | void |

View File

@ -1020,6 +1020,14 @@ For example, we want to create an extension method that would test if a value ca
assert( ! "5.2".isInteger() ) assert( ! "5.2".isInteger() )
>>> void >>> void
Extension methods normally act like instance members. If declared as `static`, they are called on the type object itself:
```lyng
static fun List<T>.fill(size: Int, block: (Int)->T): List<T> { ... }
val tens = List.fill(5) { it * 10 }
```
## Extension properties ## 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. Just like methods, you can extend existing classes with properties. These can be defined using simple initialization (for `val` only) or with custom accessors.

View File

@ -116,6 +116,7 @@ Primary sources used: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/{Parser,T
- shorthand: `fun f(x) = expr`. - shorthand: `fun f(x) = expr`.
- generics: `fun f<T>(x: T): T`. - generics: `fun f<T>(x: T): T`.
- extension functions: `fun Type.name(...) { ... }`. - extension functions: `fun Type.name(...) { ... }`.
- static extension functions are callable on the type object: `static fun List<T>.fill(...)` -> `List.fill(...)`.
- delegated callable: `fun f(...) by delegate`. - delegated callable: `fun f(...) by delegate`.
- Type aliases: - Type aliases:
- `type Name = TypeExpr` - `type Name = TypeExpr`

View File

@ -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`. - Iteration/filtering: `forEach`, `filter`, `filterFlow`, `filterNotNull`, `filterFlowNotNull`, `drop`, `dropLast`, `takeLast`.
- Search/predicates: `findFirst`, `findFirstOrNull`, `any`, `all`, `count`, `first`, `last`. - Search/predicates: `findFirst`, `findFirstOrNull`, `any`, `all`, `count`, `first`, `last`.
- Mapping/aggregation: `map`, `flatMap`, `flatten`, `sum`, `sumOf`, `minOf`, `maxOf`. - 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<T>` by evaluating the block once per index from `0` to `size - 1`.
- String helper: `joinToString`, `String.re`. - String helper: `joinToString`, `String.re`.
### 4.3 Delegation helpers ### 4.3 Delegation helpers

View File

@ -811,6 +811,12 @@ Lyng has built-in mutable array class `List` with simple literals:
many collection based methods are implemented there. many collection based methods are implemented there.
For immutable list values, use `list.toImmutable()` and [ImmutableList]. 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: Lists can contain any type of objects, lists too:
val list = [1, [2, 3], 4] val list = [1, [2, 3], 4]

View File

@ -4359,6 +4359,7 @@ class Compiler(
is ListLiteralRef -> inferListLiteralTypeDecl(ref) is ListLiteralRef -> inferListLiteralTypeDecl(ref)
is MapLiteralRef -> inferMapLiteralTypeDecl(ref) is MapLiteralRef -> inferMapLiteralTypeDecl(ref)
is ConstRef -> inferTypeDeclFromConst(ref.constValue) is ConstRef -> inferTypeDeclFromConst(ref.constValue)
is RangeRef -> TypeDecl.Simple("Range", false)
is CallRef -> { is CallRef -> {
val targetDecl = resolveReceiverTypeDecl(ref.target) ?: seedTypeDeclFromRef(ref.target) val targetDecl = resolveReceiverTypeDecl(ref.target) ?: seedTypeDeclFromRef(ref.target)
val targetName = when (val target = ref.target) { val targetName = when (val target = ref.target) {
@ -4394,6 +4395,7 @@ class Compiler(
is ObjString -> TypeDecl.Simple("String", false) is ObjString -> TypeDecl.Simple("String", false)
is ObjBool -> TypeDecl.Simple("Bool", false) is ObjBool -> TypeDecl.Simple("Bool", false)
is ObjChar -> TypeDecl.Simple("Char", false) is ObjChar -> TypeDecl.Simple("Char", false)
is ObjRange -> TypeDecl.Simple("Range", false)
is ObjNull -> TypeDecl.TypeNullableAny is ObjNull -> TypeDecl.TypeNullableAny
is ObjList -> TypeDecl.Generic("List", listOf(TypeDecl.TypeAny), false) is ObjList -> TypeDecl.Generic("List", listOf(TypeDecl.TypeAny), false)
is ObjMap -> TypeDecl.Generic("Map", listOf(TypeDecl.TypeAny, TypeDecl.TypeAny), false) is ObjMap -> TypeDecl.Generic("Map", listOf(TypeDecl.TypeAny, TypeDecl.TypeAny), false)
@ -8714,7 +8716,7 @@ class Compiler(
startPos = start startPos = start
) )
val declaredFn = FunctionDeclStatement(spec) val declaredFn = FunctionDeclStatement(spec)
if (isStatic) { if (isStatic && parentIsClassBody) {
currentInitScope += declaredFn currentInitScope += declaredFn
NopStatement NopStatement
} else } else

View File

@ -169,18 +169,29 @@ internal suspend fun executeFunctionDecl(
spec.extTypeName?.let { typeName -> spec.extTypeName?.let { typeName ->
val type = scope[typeName]?.value ?: scope.raiseSymbolNotFound("class $typeName not found") val type = scope[typeName]?.value ?: scope.raiseSymbolNotFound("class $typeName not found")
if (type !is ObjClass) scope.raiseClassCastError("$typeName is not the class instance") if (type !is ObjClass) scope.raiseClassCastError("$typeName is not the class instance")
scope.addExtension( if (spec.isStatic) {
type, type.createClassField(
spec.name, spec.name,
ObjRecord(
compiledFnBody, compiledFnBody,
isMutable = false, isMutable = false,
visibility = spec.visibility, visibility = spec.visibility,
declaringClass = null, pos = spec.startPos,
type = ObjRecord.Type.Fun, 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 wrapperName = spec.extensionWrapperName ?: extensionCallableName(typeName, spec.name)
val wrapper = ObjExtensionMethodCallable(spec.name, compiledFnBody) val wrapper = ObjExtensionMethodCallable(spec.name, compiledFnBody)
scope.addItem( scope.addItem(

View File

@ -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 @Test
fun binarySearchTest() = runTest { fun binarySearchTest() = runTest {
eval( eval(

View File

@ -422,6 +422,13 @@ fun List<T>.sort(): void {
sortWith { a, b -> a <=> b } sortWith { a, b -> a <=> b }
} }
/* Build a new list of `size` elements by calling `block(index)` for each index. */
static fun List<T>.fill(size: Int, block: (Int)->T): List<T> {
val result = List<T>()
for( i in 0..<size ) result += block(i)
result
}
/* Print this exception and its stack trace to standard output. */ /* Print this exception and its stack trace to standard output. */
fun Exception.printStackTrace(): void { fun Exception.printStackTrace(): void {
println(this) println(this)