lyng/lynglib/stdlib/lyng/root.lyng

342 lines
8.3 KiB
Plaintext

package lyng.stdlib
/*
Wrap a builder into a zero-argument thunk that computes once and caches the result.
The first call invokes builder() and stores the value; subsequent calls return the cached value.
*/
fun cached(builder) {
var calculated = false
var value = null
{
if( !calculated ) {
value = builder()
calculated = true
}
value
}
}
/* Filter elements of this iterable using the provided predicate and provide a flow
of results. Coudl be used to map infinte flows, etc.
*/
fun Iterable.filterFlow(predicate): Flow {
val list = this
flow {
for( item in list ) {
if( predicate(item) ) {
emit(item)
}
}
}
}
/*
Filter this iterable and return List of elements
*/
fun Iterable.filter(predicate) {
val result = []
for( item in this ) if( predicate(item) ) result.add(item)
result
}
/*
Count all items in this iterable for which predicate return true
*/
fun Iterable.count(predicate): Int {
var hits = 0
this.forEach {
if( predicate(it) ) hits++
}
hits
}
/*
filter out all null elements from this collection (Iterable); flow of
non-null elements is returned
*/
fun Iterable.filterFlowNotNull(): Flow {
filterFlow { it != null }
}
/* Filter non-null elements and collect them into a List
*/
fun Iterable.filterNotNull(): List {
filter { it != null }
}
/* Skip the first N elements of this iterable. */
fun Iterable.drop(n) {
var cnt = 0
filter { cnt++ >= n }
}
/* Return the first element or throw if the iterable is empty. */
val Iterable.first get() {
val i = iterator()
if( !i.hasNext() ) throw NoSuchElementException()
i.next().also { i.cancelIteration() }
}
/*
Return the first element that matches the predicate or throws
NuSuchElementException
*/
fun Iterable.findFirst(predicate) {
for( x in this ) {
if( predicate(x) )
break x
}
else throw NoSuchElementException()
}
/*
return the first element matching the predicate or null
*/
fun Iterable.findFirstOrNull(predicate) {
for( x in this ) {
if( predicate(x) )
break x
}
else null
}
/* Return the last element or throw if the iterable is empty. */
val Iterable.last get() {
var found = false
var element = null
for( i in this ) {
element = i
found = true
}
if( !found ) throw NoSuchElementException()
element
}
/* Emit all but the last N elements of this iterable. */
fun Iterable.dropLast(n) {
val list = this
val buffer = RingBuffer(n)
flow {
for( item in list ) {
if( buffer.size == n )
emit( buffer.first() )
buffer += item
}
}
}
/* Return the last N elements of this iterable as a buffer/list. */
fun Iterable.takeLast(n) {
val buffer = RingBuffer(n)
for( item in this ) buffer += item
buffer
}
/* Join elements into a string with a separator (separator parameter) and optional transformer. */
fun Iterable.joinToString(separator=" ", transformer=null) {
var result = null
for( part in this ) {
val transformed = transformer?(part)?.toString() ?: part.toString()
if( result == null ) result = transformed
else result += separator + transformed
}
result ?: ""
}
/* Return true if any element matches the predicate. */
fun Iterable.any(predicate): Bool {
for( i in this ) {
if( predicate(i) )
break true
} else false
}
/* Return true if all elements match the predicate. */
fun Iterable.all(predicate): Bool {
!any { !predicate(it) }
}
/* Sum all elements; returns null for empty collections. */
fun Iterable.sum() {
val i = iterator()
if( i.hasNext() ) {
var result = i.next()
while( i.hasNext() ) result += i.next()
result
}
else null
}
/* Sum mapped values of elements; returns null for empty collections. */
fun Iterable.sumOf(f) {
val i = iterator()
if( i.hasNext() ) {
var result = f(i.next())
while( i.hasNext() ) result += f(i.next())
result
}
else null
}
/* Minimum value of the given function applied to elements of the collection. */
fun Iterable.minOf( lambda ) {
val i = iterator()
var minimum = lambda( i.next() )
while( i.hasNext() ) {
val x = lambda(i.next())
if( x < minimum ) minimum = x
}
minimum
}
/* Maximum value of the given function applied to elements of the collection. */
fun Iterable.maxOf( lambda ) {
val i = iterator()
var maximum = lambda( i.next() )
while( i.hasNext() ) {
val x = lambda(i.next())
if( x > maximum ) maximum = x
}
maximum
}
/* Return elements sorted by natural order. */
fun Iterable.sorted() {
sortedWith { a, b -> a <=> b }
}
/* Return elements sorted by the key selector. */
fun Iterable.sortedBy(predicate) {
sortedWith { a, b -> predicate(a) <=> predicate(b) }
}
/* Return a shuffled copy of the iterable as a list. */
fun Iterable.shuffled() {
toList().apply { shuffle() }
}
/*
Returns a single list of all elements from all collections in the given collection.
@return List
*/
fun Iterable.flatten() {
val result = []
forEach { i ->
i.forEach { result.add(it) }
}
result
}
/*
Returns a single list of all elements yielded from results of transform function being
invoked on each element of original collection.
*/
fun Iterable.flatMap(transform): List {
map(transform).flatten()
}
/* Return string representation like [a,b,c]. */
fun List.toString() {
"[" + joinToString(",") + "]"
}
/* Sort list in-place by key selector. */
fun List.sortBy(predicate) {
sortWith { a, b -> predicate(a) <=> predicate(b) }
}
/* Sort list in-place by natural order. */
fun List.sort() {
sortWith { a, b -> a <=> b }
}
/* Represents a single stack trace element. */
class StackTraceEntry(
val sourceName: String,
val line: Int,
val column: Int,
val sourceString: String
) {
/* Formatted representation: source:line:column: text. */
fun toString() {
"%s:%d:%d: %s"(sourceName, line, column, sourceString.trim())
}
}
/* Print this exception and its stack trace to standard output. */
fun Exception.printStackTrace() {
println(this)
var lastEntry = null
for( entry in stackTrace() ) {
if( lastEntry == null || lastEntry !is StackTraceEntry || lastEntry.line != entry.line )
println("\tat "+entry.toString())
lastEntry = entry
}
}
/* Compile this string into a regular expression. */
val String.re get() = Regex(this)
fun TODO(message=null) = throw NotImplementedException(message ?: "not implemented")
/*
Provides different access types for delegates.
Used in the 'bind' hook to validate delegate usage.
*/
enum DelegateAccess {
Val,
Var,
Callable
}
/*
Base interface for all delegates.
Implementing this interface is optional as Lyng uses dynamic dispatch,
but it is recommended for documentation and clarity.
*/
interface Delegate {
/* Called when a delegated 'val' or 'var' is read. */
fun getValue(thisRef, name) = TODO("delegate getter is not implemented")
/* Called when a delegated 'var' is written. */
fun setValue(thisRef, name, newValue) = TODO("delegate setter is not implemented")
/* Called when a delegated function is invoked. */
fun invoke(thisRef, name, args...) = TODO("delegate invoke is not implemented")
/*
Called once during initialization to configure or validate the delegate.
Should return the delegate object to be used (usually 'this').
*/
fun bind(name, access, thisRef) = this
}
/*
Executes the block with `this` set to self and
returns what the block returns.
*/
fun with(self, block) {
var result = Unset
self.apply { result = block() }
result
}
/*
Standard implementation of a lazy-initialized property delegate.
The provided creator lambda is called once on the first access to compute the value.
Can only be used with 'val' properties.
*/
class lazy(creatorParam) : Delegate {
private val creator = creatorParam
private var value = Unset
override fun bind(name, access, thisRef) {
if (access != DelegateAccess.Val) throw "lazy delegate can only be used with 'val'"
this
}
override fun getValue(thisRef, name) {
if (value == Unset) value = with(thisRef,creator)
value
}
}