+list + / += Iterable
+CLI app started
This commit is contained in:
parent
939e391a20
commit
6ef94fff65
@ -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:
|
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.
|
Creates a list by iterating to the end. So, the Iterator should be finite to be used with it.
|
||||||
|
|
||||||
|
29
docs/Iterator.md
Normal file
29
docs/Iterator.md
Normal file
@ -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
|
26
docs/List.md
26
docs/List.md
@ -30,9 +30,35 @@ __Important__ negative indexes works wherever indexes are used, e.g. in insertio
|
|||||||
|
|
||||||
## Concatenation
|
## Concatenation
|
||||||
|
|
||||||
|
You can concatenate lists or iterable objects:
|
||||||
|
|
||||||
assert( [4,5] + [1,2] == [4,5,1,2])
|
assert( [4,5] + [1,2] == [4,5,1,2])
|
||||||
|
assert( [4,5] + (1..3) == [4, 5, 1, 2, 3])
|
||||||
>>> void
|
>>> 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
|
## Comparisons
|
||||||
|
|
||||||
assert( [1, 2] != [1, 3])
|
assert( [1, 2] != [1, 3])
|
||||||
|
@ -77,7 +77,6 @@ You can use Char as both ends of the closed range:
|
|||||||
val r = 'a' .. 'c'
|
val r = 'a' .. 'c'
|
||||||
assert( 'b' in r)
|
assert( 'b' in r)
|
||||||
assert( 'e' !in r)
|
assert( 'e' !in r)
|
||||||
assert( 'c' == r[2] )
|
|
||||||
for( ch in r )
|
for( ch in r )
|
||||||
println(ch)
|
println(ch)
|
||||||
>>> a
|
>>> a
|
||||||
|
@ -278,9 +278,13 @@ Ling has built-in mutable array class `List` with simple literals:
|
|||||||
[1, "two", 3.33].size
|
[1, "two", 3.33].size
|
||||||
>>> 3
|
>>> 3
|
||||||
|
|
||||||
|
[List] is an implementation of the type `Array`, and through it `Collection` and [Iterable].
|
||||||
|
|
||||||
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]
|
||||||
|
assert( list is List ) // concrete implementatino
|
||||||
|
assert( list is Array ) // general interface
|
||||||
assert(list.size == 3)
|
assert(list.size == 3)
|
||||||
// second element is a list too:
|
// second element is a list too:
|
||||||
assert(list[1].size == 2)
|
assert(list[1].size == 2)
|
||||||
@ -307,7 +311,7 @@ Of course, you can set any list element:
|
|||||||
a
|
a
|
||||||
>>> [1, 200, 3]
|
>>> [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])
|
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] )
|
assert( [1,2,3] < [1,3] )
|
||||||
>>> void
|
>>> 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])
|
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
|
>>> 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:
|
To add elements to the list:
|
||||||
|
|
||||||
val x = [1,2]
|
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 |
|
| Real, Int, List, String, List, Bool | Class types for real numbers |
|
||||||
| π | See [math](math.md) |
|
| π | See [math](math.md) |
|
||||||
|
|
||||||
|
[List]: List.md
|
||||||
|
[Iterable]: Iterable.md
|
||||||
|
[Iterator]: Iterator.md
|
||||||
|
[Real]: Real.md
|
||||||
|
[Range]: Range.md
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,6 +44,12 @@ open class Obj {
|
|||||||
suspend fun invokeInstanceMethod(context: Context, name: String, vararg args: Obj): Obj =
|
suspend fun invokeInstanceMethod(context: Context, name: String, vararg args: Obj): Obj =
|
||||||
invokeInstanceMethod(context, name, Arguments(args.map { Arguments.Info(it, context.pos) }))
|
invokeInstanceMethod(context, name, Arguments(args.map { Arguments.Info(it, context.pos) }))
|
||||||
|
|
||||||
|
inline suspend fun <reified T : Obj> callMethod(
|
||||||
|
context: Context,
|
||||||
|
name: String,
|
||||||
|
args: Arguments = Arguments.EMPTY
|
||||||
|
): T = invokeInstanceMethod(context, name, args) as T
|
||||||
|
|
||||||
suspend fun invokeInstanceMethod(
|
suspend fun invokeInstanceMethod(
|
||||||
context: Context,
|
context: Context,
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -3,8 +3,8 @@ package net.sergeych.ling
|
|||||||
class ObjList(val list: MutableList<Obj>) : Obj() {
|
class ObjList(val list: MutableList<Obj>) : Obj() {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
for( p in objClass.parents)
|
for (p in objClass.parents)
|
||||||
parentInstances.add( p.defaultInstance())
|
parentInstances.add(p.defaultInstance())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String = "[${
|
override fun toString(): String = "[${
|
||||||
@ -46,21 +46,32 @@ class ObjList(val list: MutableList<Obj>) : Obj() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun plus(context: Context, other: Obj): Obj {
|
override suspend fun plus(context: Context, other: Obj): Obj =
|
||||||
(other as? ObjList) ?: context.raiseError("'+': can't concatenate $this with $other")
|
when {
|
||||||
return ObjList((list + other.list).toMutableList())
|
other is ObjList ->
|
||||||
}
|
ObjList((list + other.list).toMutableList())
|
||||||
|
|
||||||
|
other.isInstanceOf(ObjIterable) -> {
|
||||||
|
val l = other.callMethod<ObjList>(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 {
|
override suspend fun plusAssign(context: Context, other: Obj): Obj {
|
||||||
// optimization
|
// optimization
|
||||||
if( other is ObjList) {
|
if (other is ObjList) {
|
||||||
list += other.list
|
list += other.list
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
if( other.isInstanceOf(ObjIterable)) {
|
if (other.isInstanceOf(ObjIterable)) {
|
||||||
TODO("plusassign for iterable is not yet implemented")
|
val otherList = other.invokeInstanceMethod(context, "toList") as ObjList
|
||||||
}
|
list += otherList.list
|
||||||
list += other
|
} else
|
||||||
|
list += other
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,24 +56,6 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob
|
|||||||
return true
|
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 {
|
val isIntRange: Boolean by lazy {
|
||||||
start is ObjInt && end is ObjInt
|
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
|
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 {
|
companion object {
|
||||||
val type = ObjClass("Range", ObjIterable).apply {
|
val type = ObjClass("Range", ObjIterable).apply {
|
||||||
addFn("start") {
|
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<ObjRangeIterator>().hasNext().toObj()
|
|
||||||
}
|
|
||||||
addFn("next") {
|
|
||||||
thisAs<ObjRangeIterator>().next(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<ObjRangeIterator>().hasNext().toObj()
|
||||||
|
}
|
||||||
|
addFn("next") {
|
||||||
|
thisAs<ObjRangeIterator>().next(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
lyng/build.gradle.kts
Normal file
32
lyng/build.gradle.kts
Normal file
@ -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 {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
lyng/src/commonMain/kotlin/Common.kt
Normal file
7
lyng/src/commonMain/kotlin/Common.kt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package net.sergeych
|
||||||
|
|
||||||
|
// common code
|
||||||
|
|
||||||
|
fun commonCode() {
|
||||||
|
println("Common code")
|
||||||
|
}
|
18
lyng/src/jvmMain/kotlin/Main.kt
Normal file
18
lyng/src/jvmMain/kotlin/Main.kt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import net.sergeych.commonCode
|
||||||
|
|
||||||
|
//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
|
||||||
|
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
|
||||||
|
fun main() {
|
||||||
|
val name = "Kotlin"
|
||||||
|
//TIP Press <shortcut actionId="ShowIntentionActions"/> 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 <shortcut actionId="Debug"/> to start debugging your code. We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
|
||||||
|
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.
|
||||||
|
println("i = $i")
|
||||||
|
}
|
||||||
|
}
|
18
lyng/src/linuxX64Main/kotlin/Main.kt
Normal file
18
lyng/src/linuxX64Main/kotlin/Main.kt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import net.sergeych.commonCode
|
||||||
|
|
||||||
|
//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
|
||||||
|
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
|
||||||
|
fun main() {
|
||||||
|
val name = "Kotlin"
|
||||||
|
//TIP Press <shortcut actionId="ShowIntentionActions"/> 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 <shortcut actionId="Debug"/> to start debugging your code. We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
|
||||||
|
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.
|
||||||
|
println("i = $i")
|
||||||
|
}
|
||||||
|
}
|
@ -17,5 +17,6 @@ dependencyResolutionManagement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rootProject.name = "ling"
|
rootProject.name = "lyng"
|
||||||
include(":library")
|
include(":library")
|
||||||
|
include(":lyng")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user