UNPKG

luma-lang

Version:

The Embeddable Luma Language Compiler and Runtime

206 lines (150 loc) 7.06 kB
<p align="center"> <a href="https://github.com/haroldiedema/luma-language/actions/workflows/test.yml"><img src="https://github.com/haroldiedema/luma-language/actions/workflows/test.yml/badge.svg" alt="Tests" /></a> <a href="https://bundlephobia.com/package/luma-lang"><img src="https://img.shields.io/bundlephobia/minzip/luma-lang" alt="Bundle Size" /></a> </p> <p align="center"> <a href="https://www.npmjs.com/package/luma-lang"><img src="https://img.shields.io/npm/v/luma-lang?color=red" alt="NPM Version" /></a> <a href="https://esm.sh/luma-language"><img src="https://img.shields.io/badge/esm.sh-luma-lang-f39c12" alt="esm.sh" /></a> <a href="https://unpkg.com/browse/luma-lang/"><img src="https://img.shields.io/badge/unpkg-luma-lang-3498db" alt="unpkg" /></a> </p> # Introduction to Luma **Welcome to Luma!** Luma is a lightweight, high-performance scripting language designed to be embedded within any **JavaScript** or **TypeScript** application. It works seamlessly on both the server and client-side and in (Web)Workers, making it an ideal solution for game logic, modding systems, and rule engines. ### Core Philosophy * **Embeddable:** Designed to live inside your host application, not replace it. * **Secure:** Runs in a strictly isolated sandbox. No access to the host's `window`, `process`, or global prototypes unless explicitly granted. * **Familiar:** Syntax inspired by Python (indentation-based) and JavaScript (dynamic typing). * **Resumable:** Scripts can be paused (`wait`), saved to disk, and resumed later. * **Budgeted:** Prevent infinite loops from freezing your app with tick-based execution limits. ## Intuitive Syntax Luma’s syntax utilizes an **indentation-based structure** to reduce visual clutter. It supports modern features like string interpolation, array comprehensions, and classes. ```luma // A simple Luma script class Greeter(name): name = name fn greet(times): // String interpolation print("Hello, {this.name}!") // Python-style array comprehension return [i * 10 for i in 0..times] greeter = new Greeter("World") // Call methods result = greeter.greet(3) ``` ## Security & Sandboxing One of Luma's strongest features is its security model. Luma scripts run in a virtualized environment that is completely isolated from the host. * **No Global Leakage:** Scripts cannot pollute the host's global scope. * **Prototype Protection:** Access to `__proto__` and `constructor` is blocked at the VM level, preventing common sandbox escapes. * **Controlled Interop:** The script can only access functions and classes (fine-tuned to individual properties and methods) you explicitly expose. ## Compilation & Binary Serialization Scripts in Luma are compiled into bytecode before execution. This model ensures the scripts run efficiently and allows for binary caching. ### Basic Compilation To run a script, you compile source code into a `Program` object. ```ts import { Compiler } from 'luma-lang'; // Compile a Luma script into a Program const program = Compiler.compile(`print("Hello, Luma!")`); ``` ### Binary Export (Pre-compilation) You can pre-compile Luma scripts to binary formats (`Uint8Array`) using the `Writer` and `Reader` APIs. This allows you to ship compiled assets and skip parsing at runtime. ```ts import { Compiler, Reader, Writer } from 'luma-lang'; // 1. Serialize the Program to binary const binary = Writer.write(program); // 2. Deserialize from binary later const loadedProgram = Reader.read(binary); ``` > **Performance Tip:** > > Although completely optional, pre-compiling scripts to binary format can > significantly reduce load times, especially for large scripts or when loading > multiple scripts at once. This becomes critical if you load scripts during a > game loop. ## Tick-based Execution & Time Travel Luma uses a **tick-based Virtual Machine**. This allows for tight integration with host applications (like game loops) and enables the `wait` keyword directly in your scripts. ```ts const vm = new VirtualMachine(program, { budget: 100, // Optional: Limit instructions per tick to prevent freezing }); // In your application loop: function gameLoop() { // Advances the VM by a frame (deltaTime in milliseconds) vm.run(deltaTime); requestAnimationFrame(gameLoop); } ``` In your Luma script, you can pause execution without blocking the host: ```luma print("Start") wait(1000) // Pauses this script for 1 second, host keeps running! print("End") ``` ## Native Async Interoperability Luma supports asynchronous host functions out of the box. If you expose a host function that returns a `Promise` (such as a database query or a `fetch` request), Luma will pause the script execution until that Promise resolves. This creates an "automatic await" behavior, allowing you to write synchronous-looking code in Luma that handles asynchronous tasks. ```ts // Expose an async function to Luma const vm = new VirtualMachine(program, { functions: { // The VM detects that this returns a Promise async fetchData(url: string): Promise<string> { const response = await fetch(url); return response.text(); } } }); ``` In your Luma script, you call this function normally. The script halts at the function call: ```luma // The script pauses here automatically data = fetchData("https://example.com/data") // This line runs only after the Promise resolves AND the host calls vm.run() print("Fetched Data: " + data) ``` > [!WARNING] > **The VM is Passive** > > When the script invokes an async function, the VM pauses and `vm.run()` returns immediately. > The VM **does not** automatically resume itself when the Promise resolves. You must continue to call `vm.run()` in your host application's update loop (e.g., every frame). > * If the Promise is still pending, `vm.run()` does nothing (returns immediately). > * Once the Promise resolves, the *next* call to `vm.run()` will resume the script where it left off. ## State Persistence Luma allows you to snapshot the entire state of the Virtual Machine. This is critical for features like ** Save / Load ** in games or session resumption in interactive apps. ```ts // Save the full state (variables, stack, instruction pointer) const serializedState = vm.save(); // Restore the state later - the script continues exactly where it left off vm.load(serializedState); ``` > [!WARNING] > **Versioning Warning:** Saved states are tightly coupled to the structure of > the compiled `Program`. If you recompile the source code, the VM may not be > able to load a state saved from a previous version. --- ## Contributing To set up the development environment, clone the repository and install dependencies: ```bash npm install ``` Build the project using `npm run build` or `npm run watch` for continuous builds. Once built, you have two options to run the tests: - Run the entire suite once: `npm run test` - Run tests in watch mode: `npm run watch:test` ---