From 6ef94fff655d234429b1b4f72dbb9480fd745dd6 Mon Sep 17 00:00:00 2001 From: sergeych Date: Mon, 2 Jun 2025 07:39:31 +0400 Subject: [PATCH] +list + / += Iterable +CLI app started --- docs/Iterable.md | 8 ++- docs/Iterator.md | 29 ++++++++ docs/List.md | 26 +++++++ docs/Range.md | 1 - docs/tutorial.md | 51 ++++++++++++- .../kotlin/net/sergeych/ling/Obj.kt | 6 ++ .../kotlin/net/sergeych/ling/ObjList.kt | 33 ++++++--- .../kotlin/net/sergeych/ling/ObjRange.kt | 72 ++----------------- .../net/sergeych/ling/ObjRangeIterator.kt | 49 +++++++++++++ lyng/build.gradle.kts | 32 +++++++++ lyng/src/commonMain/kotlin/Common.kt | 7 ++ lyng/src/jvmMain/kotlin/Main.kt | 18 +++++ lyng/src/linuxX64Main/kotlin/Main.kt | 18 +++++ settings.gradle.kts | 3 +- 14 files changed, 271 insertions(+), 82 deletions(-) create mode 100644 docs/Iterator.md create mode 100644 library/src/commonMain/kotlin/net/sergeych/ling/ObjRangeIterator.kt create mode 100644 lyng/build.gradle.kts create mode 100644 lyng/src/commonMain/kotlin/Common.kt create mode 100644 lyng/src/jvmMain/kotlin/Main.kt create mode 100644 lyng/src/linuxX64Main/kotlin/Main.kt diff --git a/docs/Iterable.md b/docs/Iterable.md index 8b6c0e2..b055745 100644 --- a/docs/Iterable.md +++ b/docs/Iterable.md @@ -15,7 +15,13 @@ Just remember at this stage typed declarations are not yet supported. Having `Iterable` in base classes allows to use it in for loop. Also, each `Iterable` has some utility functions available: -## toList() +## Abstract methods + + fun iterator(): Iterator + +## Instance methods + +### toList() Creates a list by iterating to the end. So, the Iterator should be finite to be used with it. diff --git a/docs/Iterator.md b/docs/Iterator.md new file mode 100644 index 0000000..05a28ee --- /dev/null +++ b/docs/Iterator.md @@ -0,0 +1,29 @@ +# Iterator interface + +Iterators are representing the [Iterable] entity, to access its contents +sequentially. + +To implement the iterator you need to implement only two abstract methods: + +## Abstract methods + +### hasNext(): Bool + +Should return `true` if call to `next()` will return valid next element. + +### next(): Obj + +Should return next object in the iterated entity. If there is no next method, +must throw `ObjIterationFinishedError`. + +## Usage + +Iterators are returned when implementing [Iterable] interface. + +## Implemented for classes: + +- [List], [Range] + +[List]: List.md +[Range]: Range.md +[Iterable]: Iterable.md \ No newline at end of file diff --git a/docs/List.md b/docs/List.md index 7482c4b..abf6576 100644 --- a/docs/List.md +++ b/docs/List.md @@ -30,9 +30,35 @@ __Important__ negative indexes works wherever indexes are used, e.g. in insertio ## Concatenation +You can concatenate lists or iterable objects: + assert( [4,5] + [1,2] == [4,5,1,2]) + assert( [4,5] + (1..3) == [4, 5, 1, 2, 3]) >>> void +## Appending + +To append to lists, use `+=` with elements, lists and any [Iterable] instances, but beware it will concatenate [Iterable] objects instead of appending them. To append [Iterable] instance itself, use `list.add`: + + var list = [1, 2] + val other = [3, 4] + + // appending lists is clear: + list += other + assert( list == [1, 2, 3, 4] ) + + // but appending other Iterables could be confusing: + list += (10..12) + assert( list == [1, 2, 3, 4, 10, 11, 12]) + + // now adding list as sublist: + list.add(other) + assert( list == [1, 2, 3, 4, 10, 11, 12, [3,4]]) + + >>> void + + + ## Comparisons assert( [1, 2] != [1, 3]) diff --git a/docs/Range.md b/docs/Range.md index f18fe09..2d1a610 100644 --- a/docs/Range.md +++ b/docs/Range.md @@ -77,7 +77,6 @@ You can use Char as both ends of the closed range: val r = 'a' .. 'c' assert( 'b' in r) assert( 'e' !in r) - assert( 'c' == r[2] ) for( ch in r ) println(ch) >>> a diff --git a/docs/tutorial.md b/docs/tutorial.md index 50efc05..b72b37d 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -278,9 +278,13 @@ Ling has built-in mutable array class `List` with simple literals: [1, "two", 3.33].size >>> 3 +[List] is an implementation of the type `Array`, and through it `Collection` and [Iterable]. + Lists can contain any type of objects, lists too: val list = [1, [2, 3], 4] + assert( list is List ) // concrete implementatino + assert( list is Array ) // general interface assert(list.size == 3) // second element is a list too: assert(list[1].size == 2) @@ -307,7 +311,7 @@ Of course, you can set any list element: a >>> [1, 200, 3] -Lists are comparable, as long as their respective elements are: +Lists are comparable, and it works well as long as their respective elements are: assert( [1,2,3] == [1,2,3]) @@ -322,11 +326,48 @@ Lists are comparable, as long as their respective elements are: assert( [1,2,3] < [1,3] ) >>> void -All comparison operators with list are working ok. Also, you can concatenate lists: +The simplest way to concatenate lists is using `+` and `+=`: + // + works to concatenate iterables: assert( [5, 4] + ["foo", 2] == [5, 4, "foo", 2]) + var list = [1, 2] + + // append allow adding iterables: all elements of it: + list += [2, 1] + // or you can append a single element: + list += "end" + assert( list == [1, 2, 2, 1, "end"]) >>> void +***Important note***: the pitfall of using `+=` is that you can't append in [Iterable] instance as an object: it will always add all its contents. Use `list.add` to add a single iterable instance: + + var list = [1, 2] + val other = [3, 4] + + // appending lists is clear: + list += other + assert( list == [1, 2, 3, 4] ) + + // but appending other Iterables could be confusing: + list += (10..12) + assert( list == [1, 2, 3, 4, 10, 11, 12]) + >>> void + +Use `list.add` to avoid confusion: + + var list = [1, 2] + val other = [3, 4] + + // appending lists is clear: + list.add(other) + assert( list == [1, 2, [3, 4]] ) + + // but appending other Iterables could be confusing: + list.add(10..12) + assert( list == [1, 2, [3, 4], (10..12)]) + >>> void + + To add elements to the list: val x = [1,2] @@ -696,6 +737,10 @@ See [math functions](math.md). Other general purpose functions are: | Real, Int, List, String, List, Bool | Class types for real numbers | | π | See [math](math.md) | - +[List]: List.md +[Iterable]: Iterable.md +[Iterator]: Iterator.md +[Real]: Real.md +[Range]: Range.md diff --git a/library/src/commonMain/kotlin/net/sergeych/ling/Obj.kt b/library/src/commonMain/kotlin/net/sergeych/ling/Obj.kt index c5c429f..fe7cb97 100644 --- a/library/src/commonMain/kotlin/net/sergeych/ling/Obj.kt +++ b/library/src/commonMain/kotlin/net/sergeych/ling/Obj.kt @@ -44,6 +44,12 @@ open class Obj { suspend fun invokeInstanceMethod(context: Context, name: String, vararg args: Obj): Obj = invokeInstanceMethod(context, name, Arguments(args.map { Arguments.Info(it, context.pos) })) + inline suspend fun callMethod( + context: Context, + name: String, + args: Arguments = Arguments.EMPTY + ): T = invokeInstanceMethod(context, name, args) as T + suspend fun invokeInstanceMethod( context: Context, name: String, diff --git a/library/src/commonMain/kotlin/net/sergeych/ling/ObjList.kt b/library/src/commonMain/kotlin/net/sergeych/ling/ObjList.kt index 9ca8a25..1e82d62 100644 --- a/library/src/commonMain/kotlin/net/sergeych/ling/ObjList.kt +++ b/library/src/commonMain/kotlin/net/sergeych/ling/ObjList.kt @@ -3,8 +3,8 @@ package net.sergeych.ling class ObjList(val list: MutableList) : Obj() { init { - for( p in objClass.parents) - parentInstances.add( p.defaultInstance()) + for (p in objClass.parents) + parentInstances.add(p.defaultInstance()) } override fun toString(): String = "[${ @@ -46,21 +46,32 @@ class ObjList(val list: MutableList) : Obj() { } } - override suspend fun plus(context: Context, other: Obj): Obj { - (other as? ObjList) ?: context.raiseError("'+': can't concatenate $this with $other") - return ObjList((list + other.list).toMutableList()) - } + override suspend fun plus(context: Context, other: Obj): Obj = + when { + other is ObjList -> + ObjList((list + other.list).toMutableList()) + + other.isInstanceOf(ObjIterable) -> { + val l = other.callMethod(context, "toList") + ObjList((list + l.list).toMutableList()) + } + + else -> + context.raiseError("'+': can't concatenate $this with $other") + } + override suspend fun plusAssign(context: Context, other: Obj): Obj { // optimization - if( other is ObjList) { + if (other is ObjList) { list += other.list return this } - if( other.isInstanceOf(ObjIterable)) { - TODO("plusassign for iterable is not yet implemented") - } - list += other + if (other.isInstanceOf(ObjIterable)) { + val otherList = other.invokeInstanceMethod(context, "toList") as ObjList + list += otherList.list + } else + list += other return this } diff --git a/library/src/commonMain/kotlin/net/sergeych/ling/ObjRange.kt b/library/src/commonMain/kotlin/net/sergeych/ling/ObjRange.kt index 4e18cba..2fb51d2 100644 --- a/library/src/commonMain/kotlin/net/sergeych/ling/ObjRange.kt +++ b/library/src/commonMain/kotlin/net/sergeych/ling/ObjRange.kt @@ -56,24 +56,6 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob return true } - override suspend fun getAt(context: Context, index: Int): Obj { - if (!isIntRange && !isCharRange) { - return when (index) { - 0 -> start ?: ObjNull - 1 -> end ?: ObjNull - else -> context.raiseIndexOutOfBounds("index out of range: $index for max of 2 for non-int ranges") - } - } - // int range, should be finite - val r0 = start?.toInt() ?: context.raiseArgumentError("start is not integer") - var r1 = end?.toInt() ?: context.raiseArgumentError("end is not integer") - if (isEndInclusive) r1++ - val i = index + r0 - if (i >= r1) context.raiseIndexOutOfBounds("index $index is not in range (${r1 - r0})") - return if (isIntRange) ObjInt(i.toLong()) else ObjChar(i.toChar()) - } - - val isIntRange: Boolean by lazy { start is ObjInt && end is ObjInt } @@ -82,6 +64,13 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob start is ObjChar && end is ObjChar } + override suspend fun compareTo(context: Context, other: Obj): Int { + return (other as? ObjRange)?.let { + if( start == other.start && end == other.end ) 0 else -1 + } + ?: -1 + } + companion object { val type = ObjClass("Range", ObjIterable).apply { addFn("start") { @@ -110,50 +99,3 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob } } -class ObjRangeIterator(val self: ObjRange) : Obj() { - - private var nextIndex = 0 - private var lastIndex = 0 - private var isCharRange: Boolean = false - - override val objClass: ObjClass = type - - fun Context.init() { - if (self.start == null || self.end == null) - raiseError("next is only available for finite ranges") - isCharRange = self.isCharRange - lastIndex = if (self.isIntRange || self.isCharRange) { - if (self.isEndInclusive) - self.end.toInt() - self.start.toInt() + 1 - else - self.end.toInt() - self.start.toInt() - } else { - raiseError("not implemented iterator for range of $this") - } - } - - fun hasNext(): Boolean = nextIndex < lastIndex - - fun next(context: Context): Obj = - if (nextIndex < lastIndex) { - val x = if (self.isEndInclusive) - self.start!!.toLong() + nextIndex++ - else - self.start!!.toLong() + nextIndex++ - if( isCharRange ) ObjChar(x.toInt().toChar()) else ObjInt(x) - } - else { - context.raiseError(ObjIterationFinishedError(context)) - } - - companion object { - val type = ObjClass("RangeIterator", ObjIterable).apply { - addFn("hasNext") { - thisAs().hasNext().toObj() - } - addFn("next") { - thisAs().next(this) - } - } - } -} \ No newline at end of file diff --git a/library/src/commonMain/kotlin/net/sergeych/ling/ObjRangeIterator.kt b/library/src/commonMain/kotlin/net/sergeych/ling/ObjRangeIterator.kt new file mode 100644 index 0000000..592545e --- /dev/null +++ b/library/src/commonMain/kotlin/net/sergeych/ling/ObjRangeIterator.kt @@ -0,0 +1,49 @@ +package net.sergeych.ling + +class ObjRangeIterator(val self: ObjRange) : Obj() { + + private var nextIndex = 0 + private var lastIndex = 0 + private var isCharRange: Boolean = false + + override val objClass: ObjClass = type + + fun Context.init() { + if (self.start == null || self.end == null) + raiseError("next is only available for finite ranges") + isCharRange = self.isCharRange + lastIndex = if (self.isIntRange || self.isCharRange) { + if (self.isEndInclusive) + self.end.toInt() - self.start.toInt() + 1 + else + self.end.toInt() - self.start.toInt() + } else { + raiseError("not implemented iterator for range of $this") + } + } + + fun hasNext(): Boolean = nextIndex < lastIndex + + fun next(context: Context): Obj = + if (nextIndex < lastIndex) { + val x = if (self.isEndInclusive) + self.start!!.toLong() + nextIndex++ + else + self.start!!.toLong() + nextIndex++ + if( isCharRange ) ObjChar(x.toInt().toChar()) else ObjInt(x) + } + else { + context.raiseError(ObjIterationFinishedError(context)) + } + + companion object { + val type = ObjClass("RangeIterator", ObjIterable).apply { + addFn("hasNext") { + thisAs().hasNext().toObj() + } + addFn("next") { + thisAs().next(this) + } + } + } +} \ No newline at end of file diff --git a/lyng/build.gradle.kts b/lyng/build.gradle.kts new file mode 100644 index 0000000..51b490b --- /dev/null +++ b/lyng/build.gradle.kts @@ -0,0 +1,32 @@ +plugins { + kotlin("multiplatform") version "2.1.21" +} + +group = "net.sergeych" +version = "unspecified" + +repositories { + mavenCentral() +} + +kotlin { + jvm() + linuxX64 { + binaries { + executable() + } + } + sourceSets { + val commonMain by getting { + } + val commonTest by getting { + dependencies { + implementation(kotlin("test-common")) + implementation(kotlin("test-annotations-common")) + } + } + val linuxX64Main by getting { + + } + } +} \ No newline at end of file diff --git a/lyng/src/commonMain/kotlin/Common.kt b/lyng/src/commonMain/kotlin/Common.kt new file mode 100644 index 0000000..ee53833 --- /dev/null +++ b/lyng/src/commonMain/kotlin/Common.kt @@ -0,0 +1,7 @@ +package net.sergeych + +// common code + +fun commonCode() { + println("Common code") +} \ No newline at end of file diff --git a/lyng/src/jvmMain/kotlin/Main.kt b/lyng/src/jvmMain/kotlin/Main.kt new file mode 100644 index 0000000..b56eafa --- /dev/null +++ b/lyng/src/jvmMain/kotlin/Main.kt @@ -0,0 +1,18 @@ +import net.sergeych.commonCode + +//TIP To Run code, press or +// click the icon in the gutter. +fun main() { + val name = "Kotlin" + //TIP Press with your caret at the highlighted text + // to see how GIGA IDE suggests fixing it. + println("Hello, native " + name + "!") + + commonCode() + + for (i in 1..5) { + //TIP Press to start debugging your code. We have set one breakpoint + // for you, but you can always add more by pressing . + println("i = $i") + } +} \ No newline at end of file diff --git a/lyng/src/linuxX64Main/kotlin/Main.kt b/lyng/src/linuxX64Main/kotlin/Main.kt new file mode 100644 index 0000000..b56eafa --- /dev/null +++ b/lyng/src/linuxX64Main/kotlin/Main.kt @@ -0,0 +1,18 @@ +import net.sergeych.commonCode + +//TIP To Run code, press or +// click the icon in the gutter. +fun main() { + val name = "Kotlin" + //TIP Press with your caret at the highlighted text + // to see how GIGA IDE suggests fixing it. + println("Hello, native " + name + "!") + + commonCode() + + for (i in 1..5) { + //TIP Press to start debugging your code. We have set one breakpoint + // for you, but you can always add more by pressing . + println("i = $i") + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 1a417c4..880891d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,5 +17,6 @@ dependencyResolutionManagement { } } -rootProject.name = "ling" +rootProject.name = "lyng" include(":library") +include(":lyng")