Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 44 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<p align="center">
<img src="docs/media/logo.png" alt="BlueLuaK" width="760">
<img src="docs/media/logo.png" alt="Basalt Luak" width="760">
</p>

<p align="center">
<strong>A Kotlin Multiplatform implementation of an embeddable Lua 5.2 runtime.</strong>
<strong>A Kotlin Multiplatform implementation of an embeddable Lua 5.5.1 runtime.</strong>
</p>

<p align="center">
Expand All @@ -12,65 +12,65 @@
<img alt="Kotlin" src="https://img.shields.io/badge/Kotlin-2.4.10-7F52FF?logo=kotlin&logoColor=white">
<img alt="Gradle" src="https://img.shields.io/badge/Gradle-9.6.1-02303A?logo=gradle&logoColor=white">
<img alt="JVM" src="https://img.shields.io/badge/JVM-17+-ED8B00?logo=openjdk&logoColor=white">
<img alt="Lua" src="https://img.shields.io/badge/Lua-5.2-000080?logo=lua&logoColor=white">
<img alt="Lua" src="https://img.shields.io/badge/Lua-5.5.1-000080?logo=lua&logoColor=white">
<img alt="License" src="https://img.shields.io/badge/license-MIT-green">
</p>

> **Rebranding in progress.** BlueLuaK is becoming **Basalt Luak** (or simply Luak). The name is moving ahead of everything else: releases still publish to Blueva's Maven repository, the coordinates are still `net.blueva:blueluak-*`, and the packages are still `net.blueva.luak`. The install instructions below are the ones that work today.

## Overview

BlueLuaK is a Kotlin-first fork of [LuaJ 3.0.2](https://github.com/luaj/luaj), rebuilt as a **Kotlin Multiplatform** library. Its shared module currently targets:
Basalt Luak (or simply Luak) is a Kotlin-first implementation of an embeddable Lua runtime, built as a **Kotlin Multiplatform** library. Its shared module currently targets:

- **JVM 17+**
- **JavaScript IR**, tested on Node.js
- **WebAssembly**, tested on Node.js
- **Kotlin/Native** for Linux x64, Windows x64, macOS x64, and macOS ARM64

The Lua runtime, value model, bytecode compiler, AST, standard libraries, and ANTLR Kotlin parser live in `commonMain`. JVM-specific integration is isolated from the shared runtime.
The Lua runtime, value model, bytecode compiler, and standard libraries live in `commonMain`. JVM-specific integration is isolated from the shared runtime.

BlueLuaK currently implements Lua 5.2 and provides:
Luak currently implements **Lua 5.5.1** and provides:

- An embeddable Lua VM written entirely in Kotlin.
- Lua source parsing through ANTLR Kotlin, without JavaCC or generated Java.
- Lua bytecode compilation and execution across the configured KMP targets.
- Tables, metatables, functions, coroutines, and Lua 5.2 standard libraries.
- Tables, metatables, functions, coroutines, and Lua 5.5.1 standard libraries.
- `LuaPlatform.standardGlobals()`, one entry point that builds a fully loaded `Globals` on every target.
- A shared `io` library (`io.open`, `io.lines`, `io.tmpfile`, file handles, `os.remove`/`rename`/`tmpname`) on every target, not just the JVM.
- Shared tests for the runtime, compiler, and parser across KMP targets.
- Shared tests for the runtime, compiler, and libraries across KMP targets.
- JVM integrations for processes, Java reflection, script engines, and `luajava`.

BlueLuaK is no longer source-compatible with LuaJ: modules, packages, platform classes, and APIs use BlueLuaK naming under `net.blueva.luak`.
You can find more information on our website: [luaklang.org](https://luaklang.org)

## Multiplatform Architecture

| Source set or module | Purpose |
|---|---|
| [`blueluak-core/src/commonMain/kotlin/`](blueluak-core/src/commonMain/kotlin/) | Shared Lua runtime, compiler, AST, parser, and libraries |
| [`blueluak-core/src/jvmMain/kotlin/`](blueluak-core/src/jvmMain/kotlin/) | JVM implementations of platform abstractions |
| [`blueluak-core/src/nonJvmMain/kotlin/`](blueluak-core/src/nonJvmMain/kotlin/) | Portable implementations shared by JavaScript and Wasm |
| [`blueluak-core/src/jsHostMain/kotlin/`](blueluak-core/src/jsHostMain/kotlin/) | JavaScript-host implementations (`node:fs`, `process`) for the JS and Wasm-JS targets |
| [`blueluak-core/src/wasmWasiMain/kotlin/`](blueluak-core/src/wasmWasiMain/kotlin/) | WASI implementations over raw `wasi_snapshot_preview1` syscalls |
| [`blueluak-core/src/nativeMain/kotlin/`](blueluak-core/src/nativeMain/kotlin/) | Kotlin/Native implementations of platform abstractions |
| [`blueluak-core/src/nativePosixMain/kotlin/`](blueluak-core/src/nativePosixMain/kotlin/) | 64-bit file offsets for Linux and macOS |
| [`blueluak-core/src/nativeWindowsMain/kotlin/`](blueluak-core/src/nativeWindowsMain/kotlin/) | 64-bit file offsets for Windows |
| [`blueluak-core/src/commonTest/kotlin/`](blueluak-core/src/commonTest/kotlin/) | Tests shared by all core targets |
| [`blueluak-jvm/src/main/kotlin/`](blueluak-jvm/src/main/kotlin/) | JVM-only integrations and command-line tooling |
| [`grammar/`](grammar/) | ANTLR Kotlin lexer and parser grammars for Lua 5.2 |
| [`luak-core/src/commonMain/kotlin/`](luak-core/src/commonMain/kotlin/) | Shared Lua runtime, compiler, and libraries |
| [`luak-core/src/jvmMain/kotlin/`](luak-core/src/jvmMain/kotlin/) | JVM implementations of platform abstractions |
| [`luak-core/src/nonJvmMain/kotlin/`](luak-core/src/nonJvmMain/kotlin/) | Portable implementations shared by JavaScript and Wasm |
| [`luak-core/src/jsHostMain/kotlin/`](luak-core/src/jsHostMain/kotlin/) | JavaScript-host implementations (`node:fs`, `process`) for the JS and Wasm-JS targets |
| [`luak-core/src/wasmWasiMain/kotlin/`](luak-core/src/wasmWasiMain/kotlin/) | WASI implementations over raw `wasi_snapshot_preview1` syscalls |
| [`luak-core/src/nativeMain/kotlin/`](luak-core/src/nativeMain/kotlin/) | Kotlin/Native implementations of platform abstractions |
| [`luak-core/src/nativePosixMain/kotlin/`](luak-core/src/nativePosixMain/kotlin/) | 64-bit file offsets for Linux and macOS |
| [`luak-core/src/nativeWindowsMain/kotlin/`](luak-core/src/nativeWindowsMain/kotlin/) | 64-bit file offsets for Windows |
| [`luak-core/src/commonTest/kotlin/`](luak-core/src/commonTest/kotlin/) | Tests shared by all core targets |
| [`luak-jvm/src/main/kotlin/`](luak-jvm/src/main/kotlin/) | JVM-only integrations and command-line tooling |
| [`examples/`](examples/) | Kotlin and Lua usage examples |

Gradle modules:

| Module | Targets | Purpose |
|---|---|---|
| `blueluak-core` | JVM, JavaScript IR, Wasm, Kotlin/Native | Multiplatform Lua runtime, compiler, and parser |
| `blueluak-jvm` | JVM | JVM platform adapters, `luajava`, scripting, CLI, and JIT support |
| `luak-core` | JVM, JavaScript IR, Wasm, Kotlin/Native | Multiplatform Lua runtime, compiler, and libraries |
| `luak-jvm` | JVM | JVM platform adapters, `luajava`, scripting, CLI, and JIT support |

Platform-dependent functionality is exposed through `expect`/`actual` implementations. Code intended to run on every target belongs in `commonMain`; Java and JVM APIs remain confined to JVM source sets and `blueluak-jvm`. No type in the public `commonMain` API is platform-specific.
Platform-dependent functionality is exposed through `expect`/`actual` implementations. Code intended to run on every target belongs in `commonMain`; Java and JVM APIs remain confined to JVM source sets and `luak-jvm`. No type in the public `commonMain` API is platform-specific.

The host surface every shared library is built on is deliberately small: console streams, resource lookup, a random-access file handle, delete/rename/temp-name, environment variables, exit, GC, and weak references. Everything else (the value model, the compiler, the parser, and all nine standard libraries) is shared code.
The host surface every shared library is built on is deliberately small: console streams, resource lookup, a random-access file handle, delete/rename/temp-name, environment variables, exit, GC, and weak references. Everything else (the value model, the compiler, and all nine standard libraries) is shared code.

## Installation

Releases publish to [repo.blueva.net](https://repo.blueva.net/releases), a public Maven repository, so no authentication is needed to depend on BlueLuaK.
Releases publish to [repo.blueva.net](https://repo.blueva.net/releases), a public Maven repository, so no authentication is needed to depend on Luak.

### JVM projects

Expand All @@ -79,7 +79,7 @@ Two artifacts are available. Pick one:
| Artifact | Contains | Use it when |
|---|---|---|
| `blueluak-jvm` | The multiplatform core (as a compile dependency) plus `JvmPlatform.standardGlobals()`, `luajava`, `io.popen`/`os.execute`, the `luajc` JIT compiler, CLI tooling, and `javax.script` integration | You want a ready-to-use Lua runtime, the common case |
| `blueluak-core-jvm` | Just the shared runtime, compiler, AST, parser, and standard libraries on the JVM target, including `LuaPlatform.standardGlobals()`, but without `luajava`, `io.popen`, `os.execute`, or the JIT | You don't need the JVM-only integrations, or want the smallest possible footprint |
| `blueluak-core-jvm` | Just the shared runtime, compiler, and standard libraries on the JVM target, including `LuaPlatform.standardGlobals()`, but without `luajava`, `io.popen`, `os.execute`, or the JIT | You don't need the JVM-only integrations, or want the smallest possible footprint |

`blueluak-jvm` pulls in `blueluak-core-jvm` transitively, so depending on it alone is enough for most projects.

Expand Down Expand Up @@ -127,13 +127,13 @@ globals.load("print('hello, world')")!!.call()

`LuaPlatform.debugGlobals()` adds the `debug` library. Loading the individual classes in `net.blueva.luak.lib` (`BaseLib`, `PackageLib`, `StringLib`, `TableLib`, `MathLib`, `CoroutineLib`, `OsLib`, `IoLib`, `Bit32Lib`) by hand remains available when you want a smaller footprint.

Add the `repo.blueva.net/releases` repository shown above at the project level, then depend on the shared `net.blueva:blueluak-core:26.5
Add the `repo.blueva.net/releases` repository shown above at the project level, then depend on the shared `net.blueva:blueluak-core:26.5` module.

| Target | Gradle target function | Source set | Tested on |
|---|---|---|---|
| JavaScript IR | `js { nodejs() }` | `jsMain` | Node.js |
| WebAssembly | `wasmJs { nodejs() }` | `wasmJsMain` | Node.js |
| WebAssembly (WASI) | `wasmWasi { nodejs() }` | `wasmWasiMain` | Node.js's experimental `node:wasi` (raw `wasi_snapshot_preview1` syscalls, no host-specific APIs, so wasmtime/wasmer should work too, though only Node has been verified so far) |
| WebAssembly (WASI) | `wasmWasi { nodejs() }` | `wasmWasiMain` | Node.js's experimental `node:wasi` |
| Kotlin/Native | `linuxX64()`, `mingwX64()`, `macosX64()`, `macosArm64()` | `linuxX64Main`, `mingwX64Main`, `macosX64Main`, `macosArm64Main` | Matching GitHub Actions runners in CI |

```kotlin
Expand All @@ -151,8 +151,6 @@ kotlin {
sourceSets {
commonMain {
dependencies {
// Resolves to blueluak-core-js, -wasm-js, -wasm-wasi, -linuxx64,
// -macosarm64, etc. automatically for each target above.
implementation("net.blueva:blueluak-core:26.5")
}
}
Expand All @@ -171,37 +169,37 @@ Build every target and module from a clean checkout:
Build only the multiplatform core:

```bash
./gradlew :blueluak-core:build
./gradlew :luak-core:build
```

Compile an individual target:

```bash
./gradlew :blueluak-core:compileKotlinJvm
./gradlew :blueluak-core:compileKotlinJs
./gradlew :blueluak-core:compileKotlinWasmJs
./gradlew :blueluak-core:compileKotlinMacosArm64
./gradlew :luak-core:compileKotlinJvm
./gradlew :luak-core:compileKotlinJs
./gradlew :luak-core:compileKotlinWasmJs
./gradlew :luak-core:compileKotlinMacosArm64
```

## Testing

Run every test suite available on the current host:

```bash
./gradlew :blueluak-core:allTests
./gradlew :luak-core:allTests
```

Run an individual target suite:

```bash
./gradlew :blueluak-core:jvmTest
./gradlew :blueluak-core:jsNodeTest
./gradlew :blueluak-core:wasmJsNodeTest
./gradlew :blueluak-core:wasmWasiNodeTest
./gradlew :blueluak-core:macosArm64Test
./gradlew :luak-core:jvmTest
./gradlew :luak-core:jsNodeTest
./gradlew :luak-core:wasmJsNodeTest
./gradlew :luak-core:wasmWasiNodeTest
./gradlew :luak-core:macosArm64Test
```

Native tests can only run on their matching host. Cross-platform Native compilation remains available from supported hosts. The full build also runs the inherited JVM regression suite; it is green with no `ignoreFailures` exemptions, so any real regression fails the build.
Native tests can only run on their matching host. Cross-platform Native compilation remains available from supported hosts.

## Requirements

Expand All @@ -217,7 +215,7 @@ Use the included wrapper rather than a system Gradle installation.

## Platform Support and Limitations

The shared runtime, compiler, parser, and standard libraries behave identically on every target. What differs is what the *host* can provide, and BlueLuaK reports those gaps the way Lua does, returning `nil` plus a message or raising an ordinary Lua error, rather than omitting functions:
The shared runtime, compiler, and standard libraries behave identically on every target. What differs is what the *host* can provide, and Luak reports those gaps the way Lua does, returning `nil` plus a message or raising an ordinary Lua error, rather than omitting functions:

| Capability | JVM | Kotlin/Native | JavaScript / Wasm-JS | Wasm-WASI |
|---|---|---|---|---|
Expand All @@ -229,16 +227,8 @@ The shared runtime, compiler, parser, and standard libraries behave identically
| Weak tables (`__mode`) | Yes | Yes | No (no weak references in the host) | No |
| `os.date` / `os.time` | UTC | UTC | UTC | UTC |

Where a host grants no filesystem at all, `io.open` returns `nil` and a message and the rest of the library keeps working. `io.popen` behaves the same way outside `blueluak-jvm`.

Versions follow a `year.build` scheme: `26.5` is the fifth release of 2026, and the build number restarts when the year changes. A release is cut automatically for every push to `master` that does not carry `[skip ci]`. The public API is still being refined, so **binary compatibility between releases is not guaranteed**. BlueLuaK is not source- or binary-compatible with LuaJ, and reintroducing `org.luaj.vm2` naming is out of scope.

## Roadmap

Current priorities are:

1. Modernize the Lua implementation beyond 5.2.
Where a host grants no filesystem at all, `io.open` returns `nil` and a message and the rest of the library keeps working. `io.popen` behaves the same way outside `luak-jvm`.

## License

BlueLuaK is distributed under the [MIT License](LICENSE).
Basalt Luak is distributed under the [MIT License](LICENSE).
23 changes: 2 additions & 21 deletions blueluak-core/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import com.strumenta.antlrkotlin.gradle.AntlrKotlinTask
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import java.time.Duration

plugins {
kotlin("multiplatform")
id("com.strumenta.antlr-kotlin")
`maven-publish`
}

val generatedBuildInfo = layout.buildDirectory.dir("generated-src/build-info/commonMain/kotlin")
val generatedGrammar = layout.buildDirectory.dir("generated-src/antlr/commonMain/kotlin")

val generateBuildInfo = tasks.register("generateBuildInfo") {
group = "build"
Expand All @@ -33,18 +30,6 @@ val generateBuildInfo = tasks.register("generateBuildInfo") {
}
}

val generateKotlinGrammarSource = tasks.register<AntlrKotlinTask>("generateKotlinGrammarSource") {
source = fileTree(rootProject.layout.projectDirectory.dir("grammar")) {
include("LuaLexer.g4", "LuaParser.g4")
}
packageName = "net.blueva.luak.parser.antlr"
arguments = listOf("-visitor", "-no-listener")
outputDirectory = generatedGrammar
.map { it.dir("net/blueva/luak/parser/antlr") }
.get()
.asFile
}

@OptIn(ExperimentalWasmDsl::class)
kotlin {
jvm()
Expand Down Expand Up @@ -113,10 +98,6 @@ kotlin {
}
commonMain {
kotlin.srcDir(generatedBuildInfo)
kotlin.srcDir(generatedGrammar)
dependencies {
implementation("com.strumenta:antlr-kotlin-runtime:1.0.13")
}
}
commonTest {
dependencies {
Expand All @@ -127,7 +108,7 @@ kotlin {
}

tasks.matching { it.name.startsWith("compile") && it.name.contains("Kotlin") }.configureEach {
dependsOn(generateBuildInfo, generateKotlinGrammarSource)
dependsOn(generateBuildInfo)
}

// Bounds every test task so an unresumed coroutine continuation fails
Expand All @@ -138,7 +119,7 @@ tasks.matching { it.name.endsWith("Test") }.configureEach {
}

tasks.matching { it.name.endsWith("SourcesJar", ignoreCase = true) }.configureEach {
dependsOn(generateBuildInfo, generateKotlinGrammarSource)
dependsOn(generateBuildInfo)
}

publishing {
Expand Down
Loading
Loading