Compare commits

..

No commits in common. "e13dde68b54ff815ff103ba22b2f01ebe7ef9df7" and "c65f711ee348e9d852e498577cd3ba1ffc50b6bd" have entirely different histories.

42 changed files with 140 additions and 326 deletions

View File

@ -1,4 +1,4 @@
# Lying: scripting lang for kotlin multiplatform
# Ling: scripting lang for kotlin multiplatform
in the form of multiplatform library.

View File

@ -15,13 +15,7 @@ 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:
## Abstract methods
fun iterator(): Iterator
## Instance methods
### toList()
## toList()
Creates a list by iterating to the end. So, the Iterator should be finite to be used with it.

View File

@ -1,29 +0,0 @@
# 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

View File

@ -2,7 +2,7 @@
Mutable list of any objects.
It's class in Lying is `List`:
It's class in Ling is `List`:
[1,2,3]::class
>>> List
@ -30,35 +30,9 @@ __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])

View File

@ -1,4 +1,4 @@
# OO implementation in Lying
# OO implementation in Ling
Basic principles:
@ -15,7 +15,7 @@ Basic principles:
## Instances
Result of executing of any expression or statement in the Lying is the object that
Result of executing of any expression or statement in the Ling is the object that
inherits `Obj`, but is not `Obj`. For example it could be Int, void, null, real, string, bool, etc.
This means whatever expression returns or the variable holds, is the first-class

View File

@ -77,6 +77,7 @@ 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

View File

@ -3,7 +3,7 @@
Class that supports double-precision math. Woks much like double in other languages. We honor no floats, so it is '
Real'.
It's class in Lying is `Real`:
It's class in Ling is `Real`:
(π/2)::class
>>> Real

View File

@ -1,6 +1,6 @@
# Lying tutorial
# Ling tutorial
Lying is a very simple language, where we take only most important and popular features from
Ling is a very simple language, where we take only most important and popular features from
other scripts and languages. In particular, we adopt _principle of minimal confusion_[^1].
In other word, the code usually works as expected when you see it. So, nothing unusual.
@ -8,12 +8,12 @@ __Other documents to read__ maybe after this one:
- [Advanced topics](advanced_topics.md)
- [OOP notes](OOP.md)
- [math in Lying](math.md)
- [math in Ling](math.md)
- Some class references: [List](List.md), [Real](Real.md), [Range](Range.md)
# Expressions
Everything is an expression in Lying. Even an empty block:
Everything is an expression in Ling. Even an empty block:
// empty block
>>> void
@ -221,7 +221,7 @@ Notice how function definition return a value, instance of `Callable`.
You can use both `fn` and `fun`. Note that function declaration _is an expression returning callable_.
There are default parameters in Lying:
There are default parameters in Ling:
fn check(amount, prefix = "answer: ") {
prefix + if( amount > 100 )
@ -273,18 +273,14 @@ If you need to create _unnamed_ function, use alternative syntax (TBD, like { ->
# Lists (aka arrays)
Lying has built-in mutable array class `List` with simple literals:
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)
@ -311,7 +307,7 @@ Of course, you can set any list element:
a
>>> [1, 200, 3]
Lists are comparable, and it works well as long as their respective elements are:
Lists are comparable, as long as their respective elements are:
assert( [1,2,3] == [1,2,3])
@ -326,48 +322,11 @@ Lists are comparable, and it works well as long as their respective elements are
assert( [1,2,3] < [1,3] )
>>> void
The simplest way to concatenate lists is using `+` and `+=`:
All comparison operators with list are working ok. Also, you can concatenate lists:
// + 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]
@ -546,7 +505,7 @@ We can skip the rest of the loop and restart it, as usual, with `continue` opera
>>> 0
Notice that `total` remains 0 as the end of the outerLoop@ is not reachable: `continue` is always called and always make
Lying to skip it.
Ling to skip it.
## else statement
@ -737,10 +696,6 @@ 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

View File

@ -81,7 +81,7 @@ mavenPublishing {
coordinates(group.toString(), "library", version.toString())
pom {
name = "Lying language"
name = "Ling language"
description = "Kotlin-bound scripting loanguage"
inceptionYear = "2025"
// url = "https://sergeych.net"

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
data class ParsedArgument(val value: Statement, val pos: Pos, val isSplat: Boolean = false)

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
//fun buildDoubleFromParts(
// integerPart: Long,

View File

@ -1,7 +1,7 @@
package net.sergeych.lying
package net.sergeych.ling
/**
* The LYING compiler.
* The LING compiler.
*/
class Compiler(
@Suppress("UNUSED_PARAMETER")

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
internal class CompilerContext(val tokens: List<Token>) : ListIterator<Token> by tokens.listIterator() {
val labels = mutableSetOf<String>()

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
class Context(
val parent: Context?,
@ -16,15 +16,10 @@ class Context(
@Suppress("unused")
fun raiseNPE(): Nothing = raiseError(ObjNullPointerError(this))
@Suppress("unused")
fun raiseIndexOutOfBounds(message: String = "Index out of bounds"): Nothing =
raiseError(ObjIndexOutOfBoundsError(this, message))
@Suppress("unused")
fun raiseArgumentError(message: String = "Illegal argument error"): Nothing =
raiseError(ObjIllegalArgumentError(this, message))
fun raiseClassCastError(msg: String): Nothing = raiseError(ObjClassCastError(this, msg))
fun raiseError(message: String): Nothing {
@ -69,10 +64,13 @@ class Context(
return StoredObj(value, isMutable).also { objects.put(name, it) }
}
fun getOrCreateNamespace(name: String): ObjClass {
val ns = objects.getOrPut(name) { StoredObj(ObjNamespace(name), isMutable = false) }.value
return ns!!.objClass
}
fun getOrCreateNamespace(name: String): ObjNamespace =
(objects.getOrPut(name) {
StoredObj(
ObjNamespace(name),
isMutable = false
)
}.value as ObjNamespace)
inline fun addVoidFn(vararg names: String, crossinline fn: suspend Context.() -> Unit) {
addFn<ObjVoid>(*names) {

View File

@ -0,0 +1,9 @@
package net.sergeych.ling
class IfScope(val isTrue: Boolean) {
fun otherwise(f: ()->Unit): Boolean {
if( !isTrue ) f()
return false
}
}

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
sealed class ListEntry {
data class Element(val accessor: Accessor) : ListEntry()

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
class LoopBreakContinueException(
val doContinue: Boolean,

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
@ -44,12 +44,6 @@ 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 <reified T : Obj> callMethod(
context: Context,
name: String,
args: Arguments = Arguments.EMPTY
): T = invokeInstanceMethod(context, name, args) as T
suspend fun invokeInstanceMethod(
context: Context,
name: String,
@ -298,12 +292,8 @@ fun Obj.toBool(): Boolean =
data class ObjNamespace(val name: String) : Obj() {
override val objClass by lazy { ObjClass(name) }
override fun inspect(): String = "Ns[$name]"
override fun toString(): String {
return "package $name"
return "namespace ${name}"
}
}

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
data class ObjBool(val value: Boolean) : Obj() {
override val asStr by lazy { ObjString(value.toString()) }

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
class ObjChar(val value: Char): Obj() {

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
val ObjClassType by lazy { ObjClass("Class") }

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
data class ObjInt(var value: Long) : Obj(), Numeric {
override val asStr get() = ObjString(value.toString())

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
class ObjList(val list: MutableList<Obj>) : Obj() {
@ -46,21 +46,11 @@ class ObjList(val list: MutableList<Obj>) : Obj() {
}
}
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<ObjList>(context, "toList")
ObjList((list + l.list).toMutableList())
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())
}
else ->
context.raiseError("'+': can't concatenate $this with $other")
}
override suspend fun plusAssign(context: Context, other: Obj): Obj {
// optimization
if( other is ObjList) {
@ -68,9 +58,8 @@ class ObjList(val list: MutableList<Obj>) : Obj() {
return this
}
if( other.isInstanceOf(ObjIterable)) {
val otherList = other.invokeInstanceMethod(context, "toList") as ObjList
list += otherList.list
} else
TODO("plusassign for iterable is not yet implemented")
}
list += other
return this
}

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Obj() {
@ -56,6 +56,24 @@ 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
}
@ -64,13 +82,6 @@ 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") {
@ -99,3 +110,50 @@ 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)
}
}
}
}

View File

@ -1,49 +0,0 @@
package net.sergeych.lying
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)
}
}
}
}

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
import kotlin.math.floor
import kotlin.math.roundToLong

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
val digitsSet = ('0'..'9').toSet()
val digits = { d: Char -> d in digitsSet }

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
data class Pos(val source: Source, val line: Int, val column: Int) {
override fun toString(): String {

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
import kotlin.math.*

View File

@ -1,6 +1,6 @@
@file:Suppress("CanBeParameter")
package net.sergeych.lying
package net.sergeych.ling
open class ScriptError(val pos: Pos, val errorMessage: String,cause: Throwable?=null) : Exception(
"""

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
class Source(val fileName: String, text: String) {

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
/**
* Whatever [Obj] stored somewhere

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
data class Token(val value: String, val pos: Pos, val type: Type) {
val isComment: Boolean by lazy { type == Type.SINLGE_LINE_COMMENT || type == Type.MULTILINE_COMMENT }

View File

@ -1,4 +1,4 @@
package net.sergeych.lying
package net.sergeych.ling
fun String.toSource(name: String = "eval"): Source = Source(name, this)

View File

@ -1,7 +1,7 @@
package io.github.kotlin.fibonacci
import kotlinx.coroutines.test.runTest
import net.sergeych.lying.*
import net.sergeych.ling.*
import kotlin.test.*
class ScriptTest {

View File

@ -3,8 +3,8 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.test.runTest
import net.sergeych.lying.Context
import net.sergeych.lying.ObjVoid
import net.sergeych.ling.Context
import net.sergeych.ling.ObjVoid
import java.nio.file.Files.readAllLines
import java.nio.file.Paths
import kotlin.test.Test

View File

@ -1,32 +0,0 @@
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 {
}
}
}

View File

@ -1,7 +0,0 @@
package net.sergeych
// common code
fun commonCode() {
println("Common code")
}

View File

@ -1,18 +0,0 @@
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")
}
}

View File

@ -1,18 +0,0 @@
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")
}
}

View File

@ -17,6 +17,5 @@ dependencyResolutionManagement {
}
}
rootProject.name = "lyng"
rootProject.name = "ling"
include(":library")
include(":lyng")