working CLI tool, versioning support, etc

This commit is contained in:
Sergey Chernov 2025-06-03 16:32:36 +04:00
parent 7b76b7d80f
commit 953e0e696b
17 changed files with 234 additions and 46 deletions

10
bin/local_release Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash
set -e
file=./lyng/build/bin/linuxX64/releaseExecutable/lyng.kexe
./gradlew :lyng:linkReleaseExecutableLinuxX64
strip $file
upx $file
cp $file ~/bin/lyng

View File

@ -16,4 +16,6 @@ fun naiveCountHappyNumbers() {
count
}
assert( naiveCountHappyNumbers() == 55252 )
val found = naiveCountHappyNumbers()
println("Found happy numbers: "+found)
assert( found == 55252 )

View File

@ -0,0 +1,2 @@
println("Hello, world!");

View File

@ -4,9 +4,11 @@ org.gradle.caching=true
org.gradle.configuration-cache=true
#Kotlin
kotlin.code.style=official
kotlin.js.compiler=ir
#MPP
kotlin.mpp.enableCInteropCommonization=true
#Android
android.useAndroidX=true
android.nonTransitiveRClass=true
# other
kotlin.native.cacheKind.linuxX64=none

View File

@ -1,18 +1,24 @@
[versions]
agp = "8.5.2"
clikt = "5.0.3"
kotlin = "2.1.21"
android-minSdk = "24"
android-compileSdk = "34"
kotlinx-coroutines = "1.10.1"
mp_bintools = "0.1.12"
firebaseCrashlyticsBuildtools = "3.0.3"
okioVersion = "3.10.2"
[libraries]
clikt = { module = "com.github.ajalt.clikt:clikt", version.ref = "clikt" }
clikt-markdown = { module = "com.github.ajalt.clikt:clikt-markdown", version.ref = "clikt" }
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" }
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
mp_bintools = { module = "net.sergeych:mp_bintools", version.ref = "mp_bintools" }
firebase-crashlytics-buildtools = { group = "com.google.firebase", name = "firebase-crashlytics-buildtools", version.ref = "firebaseCrashlyticsBuildtools" }
okio = { module = "com.squareup.okio:okio", version.ref = "okioVersion" }
okio-fakefilesystem = { module = "com.squareup.okio:okio-fakefilesystem", version.ref = "okioVersion" }
[plugins]
androidLibrary = { id = "com.android.library", version.ref = "agp" }

View File

@ -11,7 +11,7 @@ plugins {
}
group = "net.sergeych"
version = "0.0.1-SNAPSHOT"
version = "0.0.5-SNAPSHOT"
kotlin {
jvm()
@ -26,7 +26,7 @@ kotlin {
// iosArm64()
// iosSimulatorArm64()
linuxX64()
js(IR) {
js {
browser()
nodejs()
}
@ -44,12 +44,13 @@ kotlin {
}
val commonMain by getting {
kotlin.srcDir("$buildDir/generated/buildConfig/commonMain/kotlin")
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1")
//put your multiplatform dependencies here
implementation(libs.kotlinx.coroutines.core)
implementation(libs.mp.bintools)
implementation("net.sergeych:mp_stools:1.5.2")
api(libs.kotlinx.coroutines.core)
api(libs.mp.bintools)
api("net.sergeych:mp_stools:1.5.2")
}
}
val commonTest by getting {
@ -109,3 +110,40 @@ mavenPublishing {
}
}
}
val projectVersion by project.extra(provider {
// Compute value lazily
(version as String)
})
val generateBuildConfig by tasks.registering {
// Declare outputs safely
val outputDir = layout.buildDirectory.dir("generated/buildConfig/commonMain/kotlin")
outputs.dir(outputDir)
val version = projectVersion.get()
// Inputs: Version is tracked as an input
inputs.property("version", version)
doLast {
val packageName = "net.sergeych.lyng.buildconfig"
val packagePath = packageName.replace('.', '/')
val buildConfigFile = outputDir.get().file("$packagePath/BuildConfig.kt").asFile
buildConfigFile.parentFile?.mkdirs()
buildConfigFile.writeText(
"""
|package $packageName
|
|object BuildConfig {
| const val VERSION = "$version"
|}
""".trimMargin()
)
}
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
dependsOn(generateBuildConfig)
}

View File

@ -21,7 +21,6 @@ class Script(
companion object {
val defaultContext: Context = Context().apply {
addFn("println") {
print("yn: ")
for ((i, a) in args.withIndex()) {
if (i > 0) print(' ' + a.asStr.value)
else print(a.asStr.value)

View File

@ -5,6 +5,7 @@ package net.sergeych.lyng
open class ScriptError(val pos: Pos, val errorMessage: String,cause: Throwable?=null) : Exception(
"""
$pos: Error: $errorMessage
${pos.currentLine}
${"-".repeat(pos.column)}^
""".trimIndent(),

View File

@ -0,0 +1,5 @@
package net.sergeych.lyng
import net.sergeych.lyng.buildconfig.BuildConfig
val LyngVersion = BuildConfig.VERSION

View File

@ -6,6 +6,12 @@ import kotlin.test.*
class ScriptTest {
@Test
fun testVersion() {
println("--------------------------------------------")
println("version = ${LyngVersion}")
}
@Test
fun parseNewlines() {
fun check(expected: String, type: Token.Type, row: Int, col: Int, src: String, offset: Int = 0) {

View File

@ -7,10 +7,20 @@ version = "unspecified"
repositories {
mavenCentral()
maven("https://maven.universablockchain.com/")
maven("https://gitea.sergeych.net/api/packages/SergeychWorks/maven")
mavenLocal()
maven("https://gitea.sergeych.net/api/packages/SergeychWorks/maven")
}
kotlin {
jvm()
jvm {
binaries {
executable {
mainClass.set("net.sergeych.lyng_cli.MainKt")
}
}
}
linuxX64 {
binaries {
executable()
@ -18,11 +28,21 @@ kotlin {
}
sourceSets {
val commonMain by getting {
dependencies {
implementation(project(":library"))
implementation(libs.okio)
implementation(libs.clikt)
// optional support for rendering markdown in help messages
// implementation(libs.clikt.markdown)
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
implementation(libs.okio.fakefilesystem)
}
}
val linuxX64Main by getting {

View File

@ -1,7 +1,110 @@
package net.sergeych
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.arguments.multiple
import com.github.ajalt.clikt.parameters.arguments.optional
import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.option
import net.sergeych.lyng.*
import okio.FileSystem
import okio.Path.Companion.toPath
import okio.SYSTEM
import okio.buffer
import okio.use
// common code
fun commonCode() {
println("Common code")
}
expect fun exit(code: Int)
val baseContext = Context().apply {
addFn("exit") {
exit(requireOnlyArg<ObjInt>().toInt())
ObjVoid
}
}
class LyngCLI(val launcher: (suspend () -> Unit) -> Unit) : CliktCommand() {
override val printHelpOnEmptyArgs = true
val version by option("-v", "--version", help = "Print version and exit").flag()
val script by argument(help = "one or more scripts to execute").optional()
val execute: String? by option(
"-x", "--execute", help = """
execute string <text>, the rest of command line is passed to Lyng as ARGV
""".trimIndent()
)
val args by argument(help = "arguments for script").multiple()
override fun help(context: com.github.ajalt.clikt.core.Context): String =
"""
The Lyng script language interpreter, language version is $LyngVersion.
Please refer form more information to the project site:
https://gitea.sergeych.net/SergeychWorks/lyng
""".trimIndent()
override fun run() {
when {
version -> {
println("Lyng language version ${LyngVersion}")
}
execute != null -> {
val objargs = mutableListOf<String>()
script?.let { objargs += it }
objargs += args
baseContext.addConst(
"ARGV", ObjList(
objargs.map { ObjString(it) }.toMutableList()
)
)
launcher {
// there is no script name, it is a first argument instead:
processErrors {
baseContext.eval(execute!!)
}
}
}
else -> {
if (script == null) {
println(
"""
Error: no script specified.
""".trimIndent()
)
echoFormattedHelp()
} else {
baseContext.addConst("ARGV", ObjList(args.map { ObjString(it) }.toMutableList()))
launcher { executeFile(script!!) }
}
}
}
}
}
suspend fun executeFile(fileName: String) {
val text = FileSystem.SYSTEM.source(fileName.toPath()).use { fileSource ->
fileSource.buffer().use { bs ->
bs.readUtf8()
}
}
processErrors {
Compiler().compile(Source(fileName, text)).execute(baseContext)
}
}
suspend fun processErrors(block: suspend () -> Unit) {
try {
block()
}
catch (e: ScriptError) {
println("\nError executing the script:\n$e\n")
}
}

View File

@ -0,0 +1,7 @@
package net.sergeych
import kotlin.system.exitProcess
actual fun exit(code: Int) {
exitProcess(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

@ -0,0 +1,9 @@
package net.sergeych.lyng_cli
import com.github.ajalt.clikt.core.main
import kotlinx.coroutines.runBlocking
import net.sergeych.LyngCLI
fun main(args: Array<String>) {
LyngCLI({ runBlocking { it() } }).main(args)
}

View File

@ -1,18 +1,7 @@
import net.sergeych.commonCode
import com.github.ajalt.clikt.core.main
import kotlinx.coroutines.runBlocking
import net.sergeych.LyngCLI
//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")
}
fun main(args: Array<String>) {
LyngCLI( { runBlocking { it() } }).main(args)
}

View File

@ -0,0 +1,7 @@
package net.sergeych
import kotlin.system.exitProcess
actual fun exit(code: Int) {
exitProcess(code)
}