diff --git a/bin/local_release b/bin/local_release new file mode 100755 index 0000000..ecd434a --- /dev/null +++ b/bin/local_release @@ -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 \ No newline at end of file diff --git a/docs/samples/happy_numbers.lyng b/docs/samples/happy_numbers.lyng index 3686142..1c8cedb 100644 --- a/docs/samples/happy_numbers.lyng +++ b/docs/samples/happy_numbers.lyng @@ -16,4 +16,6 @@ fun naiveCountHappyNumbers() { count } -assert( naiveCountHappyNumbers() == 55252 ) +val found = naiveCountHappyNumbers() +println("Found happy numbers: "+found) +assert( found == 55252 ) diff --git a/docs/samples/helloworld.lyng b/docs/samples/helloworld.lyng new file mode 100644 index 0000000..da70ac9 --- /dev/null +++ b/docs/samples/helloworld.lyng @@ -0,0 +1,2 @@ + +println("Hello, world!"); diff --git a/gradle.properties b/gradle.properties index 5e4d56d..0071093 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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 \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0796b68..ac2ac7b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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" } diff --git a/library/build.gradle.kts b/library/build.gradle.kts index 250c429..a9506e7 100644 --- a/library/build.gradle.kts +++ b/library/build.gradle.kts @@ -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().configureEach { + dependsOn(generateBuildConfig) +} diff --git a/library/src/commonMain/kotlin/net/sergeych/lyng/Script.kt b/library/src/commonMain/kotlin/net/sergeych/lyng/Script.kt index f48e4b8..e6be10a 100644 --- a/library/src/commonMain/kotlin/net/sergeych/lyng/Script.kt +++ b/library/src/commonMain/kotlin/net/sergeych/lyng/Script.kt @@ -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) diff --git a/library/src/commonMain/kotlin/net/sergeych/lyng/ScriptError.kt b/library/src/commonMain/kotlin/net/sergeych/lyng/ScriptError.kt index 97ee0bf..68aab66 100644 --- a/library/src/commonMain/kotlin/net/sergeych/lyng/ScriptError.kt +++ b/library/src/commonMain/kotlin/net/sergeych/lyng/ScriptError.kt @@ -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(), diff --git a/library/src/commonMain/kotlin/net/sergeych/lyng/globals.kt b/library/src/commonMain/kotlin/net/sergeych/lyng/globals.kt new file mode 100644 index 0000000..0623503 --- /dev/null +++ b/library/src/commonMain/kotlin/net/sergeych/lyng/globals.kt @@ -0,0 +1,5 @@ +package net.sergeych.lyng + +import net.sergeych.lyng.buildconfig.BuildConfig + +val LyngVersion = BuildConfig.VERSION \ No newline at end of file diff --git a/library/src/commonTest/kotlin/ScriptTest.kt b/library/src/commonTest/kotlin/ScriptTest.kt index c46a4ea..8e76856 100644 --- a/library/src/commonTest/kotlin/ScriptTest.kt +++ b/library/src/commonTest/kotlin/ScriptTest.kt @@ -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) { diff --git a/lyng/build.gradle.kts b/lyng/build.gradle.kts index 51b490b..1779948 100644 --- a/lyng/build.gradle.kts +++ b/lyng/build.gradle.kts @@ -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 { diff --git a/lyng/src/commonMain/kotlin/Common.kt b/lyng/src/commonMain/kotlin/Common.kt index ee53833..a1b7b9e 100644 --- a/lyng/src/commonMain/kotlin/Common.kt +++ b/lyng/src/commonMain/kotlin/Common.kt @@ -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") -} \ No newline at end of file +expect fun exit(code: Int) + +val baseContext = Context().apply { + addFn("exit") { + exit(requireOnlyArg().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 , 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() + 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") + } +} diff --git a/lyng/src/jvmMain/kotlin/Common.jvm.kt b/lyng/src/jvmMain/kotlin/Common.jvm.kt new file mode 100644 index 0000000..c6b76db --- /dev/null +++ b/lyng/src/jvmMain/kotlin/Common.jvm.kt @@ -0,0 +1,7 @@ +package net.sergeych + +import kotlin.system.exitProcess + +actual fun exit(code: Int) { + exitProcess(code) +} \ No newline at end of file diff --git a/lyng/src/jvmMain/kotlin/Main.kt b/lyng/src/jvmMain/kotlin/Main.kt deleted file mode 100644 index b56eafa..0000000 --- a/lyng/src/jvmMain/kotlin/Main.kt +++ /dev/null @@ -1,18 +0,0 @@ -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/jvmMain/kotlin/net/sergeych/lyng_cli/Main.kt b/lyng/src/jvmMain/kotlin/net/sergeych/lyng_cli/Main.kt new file mode 100644 index 0000000..933c422 --- /dev/null +++ b/lyng/src/jvmMain/kotlin/net/sergeych/lyng_cli/Main.kt @@ -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) { + LyngCLI({ runBlocking { it() } }).main(args) +} \ No newline at end of file diff --git a/lyng/src/linuxX64Main/kotlin/Main.kt b/lyng/src/linuxX64Main/kotlin/Main.kt index b56eafa..86c5caa 100644 --- a/lyng/src/linuxX64Main/kotlin/Main.kt +++ b/lyng/src/linuxX64Main/kotlin/Main.kt @@ -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 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") - } +fun main(args: Array) { + LyngCLI( { runBlocking { it() } }).main(args) } \ No newline at end of file diff --git a/lyng/src/nativeMain/kotlin/Common.native.kt b/lyng/src/nativeMain/kotlin/Common.native.kt new file mode 100644 index 0000000..c6b76db --- /dev/null +++ b/lyng/src/nativeMain/kotlin/Common.native.kt @@ -0,0 +1,7 @@ +package net.sergeych + +import kotlin.system.exitProcess + +actual fun exit(code: Int) { + exitProcess(code) +} \ No newline at end of file