<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`

---
