# lua

> Embed Lua 5.1 in an OSL program, from one-off expressions to long-lived interpreters that share data and functions with your code.

This is the Markdown version of https://osl-lang.org/packages/lua. Every page of the OSL docs has one: add .md to its path. https://osl-lang.org/llms.txt lists them all.

```osl
import "std:lua"
```

`std:lua` runs Lua 5.1 code (with gopher-lua, a Lua interpreter written in Go). Use it to give your program a scripting language for rules, configuration, plugins or game logic that people can change without recompiling. You can run a snippet and take its value, or keep an interpreter alive, share data with it and call functions in both directions.

> **Warning**
>
> Lua code gets the whole standard library, `os` and `io` included, so it can read files and run commands. Don't run code you don't trust. [js](/packages/js.md) runs JavaScript in a real sandbox if you need one.

## One-off code

`lua.eval(code)` runs code in a fresh interpreter and returns a result holding its value. The value is that of a single expression, or what a chunk returns:

```osl
import "std:lua"

log lua.eval("1 + 2").unwrap()
log lua.eval("local x = 2 return x * 3").unwrap()
log lua.version()
```

Output:

```text
3
6
Lua 5.1 (gopher-lua)
```

Lua tables convert to OSL values: a table with keys 1 to n becomes an array, any other table an object.

```osl
import "std:lua"

log lua.eval("{1, 2, 3}").unwrap()
log lua.eval("{name = 'Ada', n = 3}").unwrap()
```

Output:

```text
[1,2,3]
{"n":3,"name":"Ada"}
```

`lua.run(code)` runs code for its effects and returns nothing, and `lua.evalFile(path)` runs a file.

## Failures are results

Every call returns a result. The error kinds are `ParseError` for a syntax error, `RuntimeError` for an error the code raises, `TimeoutError` when it runs too long, and `ValueError` for source over 16 MiB:

```osl
import "std:lua"

auto syntax = lua.run("x = ")
log syntax.unwrapErr().kind

auto raised = lua.run("error('boom')")
log raised.unwrapErr().kind
log raised.unwrapErr().message

auto endless = lua.run("while true do end", {timeout: 100})
log endless.unwrapErr().kind
```

Output:

```text
ParseError
RuntimeError
lua.run: <string>:1: boom
TimeoutError
```

The default `timeout` is 5000 milliseconds. Set it per call, or for a whole interpreter in `lua.new`.

## An interpreter that keeps its state

`lua.new()` starts an interpreter whose globals survive between calls. `state.run` defines things, `state.eval` evaluates, `state.get` reads a global, and `state.call(name, args...)` calls a Lua function with each argument as its own value and returns its first result:

```osl
import "std:lua"

*lua.State state = lua.new()
state.run("count = 0 function add(a, b) count = count + 1 return a + b end").unwrap()

log state.call("add", 2, 3).unwrap()
log state.call("add", 10, 5).unwrap()
log state.get("count")

log state.call("missing").unwrapErr().kind
state.close()
```

Output:

```text
5
15
2
NotFoundError
```

Close a state when you're done. Using a closed state throws a `StateError`.

## Sending data in

`state.set(name, value)` sets a global to a copy of an OSL value: arrays become tables with keys 1 to n, and objects become tables with string keys. Changes Lua makes to its copy don't reach your variable:

```osl
import "std:lua"

*lua.State state = lua.new()
state.set("items", [10, 20, 30])
state.set("config", {mode: "fast", limit: 5})

log state.eval("#items").unwrap()
log state.eval("config.mode").unwrap()

state.run("total = 0 for _, n in ipairs(items) do total = total + n end").unwrap()
log state.get("total")
state.close()
```

Output:

```text
3
fast
60
```

Lua arrays are 1-based like OSL's, so `items[1]` is 10 in both.

## Letting Lua call your functions

`state.register(name, fn)` makes an OSL function callable from Lua. Arguments and return values are converted both ways. This is how a script reaches the rest of your program, and the way to limit what it can do: offer only the functions you choose.

```osl
import "std:lua"

def discount(number price, number percent) number -> (price * (100 - percent)) / 100

*lua.State state = lua.new()
state.register("discount", discount)

log state.eval("discount(80, 25)").unwrap()
state.run("function price(base) return discount(base, 10) end").unwrap()
log state.call("price", 200).unwrap()
state.close()
```

Output:

```text
60
180
```

An error that a registered function throws becomes a Lua error. Lua's `pcall` can catch it, and otherwise it fails the run, so the caller sees it as an error result:

```osl
import "std:lua"

*lua.State state = lua.new()
state.register("fail", def() ( throw "nope" ))

auto outcome = state.run("fail()")
log outcome.isErr()

log state.eval("select(2, pcall(fail))").unwrap()
state.close()
```

Output:

```text
true
<string>:1: RuntimeError: nope
```

## A rule engine

Rules written in Lua (kept in a string here; a real program would read them with `fs.readFile`) applied to your data one at a time:

```osl
import "std:lua"

string rules = `
function shipping(order)
  if order.total >= 100 then return 0 end
  if order.country == "NZ" then return 5 end
  return 12
end
`

*lua.State state = lua.new()
state.run(rules).unwrap()

auto orders = [
  {total: 120, country: "NZ"},
  {total: 40, country: "NZ"},
  {total: 40, country: "AU"}
]
for order in orders (
  log state.call("shipping", order).unwrap()
)
state.close()
```

Output:

```text
0
5
12
```

**Quiz.** Lua code runs with `lua.eval`. What can it do?

- [ ] Only compute values.
- [x] Everything Lua's standard library allows, including files and commands.
- [ ] Nothing outside its own variables.

Answer: The interpreter isn't sandboxed. Run only code you trust, or use `std:js`.

### Exercise: Call a Lua function

Define a Lua function `square` and call it with 7 from OSL.

Starting code:

```osl
import "std:lua"

*lua.State state = lua.new()
log state.eval("1 + 1").unwrap()
state.close()
```

It should print:

```text
49
```

Hint 1: `state.run("function square(n) return n * n end")` defines it, and `state.call("square", 7)` calls it.

A solution:

```osl
import "std:lua"

*lua.State state = lua.new()
state.run("function square(n) return n * n end").unwrap()
log state.call("square", 7).unwrap()
state.close()
```

## API reference

## API reference

Every member of `std:lua`, generated from the source of OSL.

### Functions

#### `lua.create() *lua.State`

**Deprecated.** Deprecated: use lua.new(options) instead.

create starts a Lua interpreter.

#### `lua.doString(any code) *lua.State`

**Deprecated.** Deprecated: use lua.new() and state.run(code), which reports failure.

doString starts an interpreter and runs code in it, returning the state
even when the code failed.

#### `lua.eval(string code, object? options) result<any, error>`

eval runs Lua code in a fresh interpreter and returns its value: the
value of code that is a single expression, as in lua.eval("1 + 2"), or
what a chunk returns, as in "local x = 2 return x * 3", or null. Code
that returns several values gives the last one. Tables
with keys 1 to n become arrays and other tables objects. Option and
errors as for lua.run.

#### `lua.evalFile(string path, object? options) result<any, error>`

evalFile runs a Lua file in a fresh interpreter and returns what it
returns, or null. Option: timeout in milliseconds (default 5000). Fails
with NotFoundError or IOError when the file can't be read, and otherwise
like lua.run.

#### `lua.get(any code, any name) any`

**Deprecated.** Deprecated: run the code in a lua.new() state with state.run(code), then read state.get(name).

get runs code and returns the global name afterwards, or null when the
code fails.

#### `lua.new(object? options) *lua.State`

new starts a Lua interpreter that keeps its globals between calls. The
code it runs has the whole Lua standard library, including os and io,
so don't give it untrusted code; std:js runs JavaScript in a sandbox.
Option: timeout, the default limit in milliseconds for each run, eval
and call (5000). Close the State when done with it.

#### `lua.newTable() any`

**Deprecated.** Deprecated: use {} instead.

newTable returns an empty object.

#### `lua.run(string code, object? options) result<unit, error>`

run runs Lua code in a fresh interpreter. Option: timeout in
milliseconds (default 5000). Fails with ParseError for a syntax error,
TimeoutError when the code runs too long, ValueError for source over 16
MiB and RuntimeError when the code raises an error.

#### `lua.runFile(any path) object`

**Deprecated.** Deprecated: use lua.evalFile(path), which fails with a typed error.

runFile runs a Lua file and returns {success, error}.

#### `lua.runTimeout(any code, any timeout) result`

**Deprecated.** Deprecated: use lua.run(code, {timeout: ms}) instead; note that the timeout there is in milliseconds.

runTimeout runs code with a timeout in seconds.

#### `lua.version() string`

version returns the Lua version the interpreter implements.

### `*lua.State`

#### `state.call(string name, any ...args) result<any, error>`

call calls the global Lua function name with args and returns its first
result, or null. Uses the state's timeout. Fails with NotFoundError when
name isn't a function, and otherwise like state.run. Throws StateError
after close.

#### `state.callTimeout(any funcName, any timeout, any ...args) any`

**Deprecated.** Deprecated: use state.call(name, args...) with the timeout set by lua.new({timeout: ms}).

callTimeout calls a global Lua function with a timeout in seconds,
returning null when it fails.

#### `state.close()`

close shuts the interpreter down. Closing twice does nothing; any other
method throws StateError afterwards.

#### `state.doFile(any path) boolean`

**Deprecated.** Deprecated: use state.runFile(path) instead, which fails with a typed error.

doFile runs a Lua file, returning false when it fails or the state is
closed.

#### `state.doString(any code) boolean`

**Deprecated.** Deprecated: use state.run(code) instead, which fails with a typed error.

doString runs code, returning false when it fails or the state is
closed.

#### `state.doStringTimeout(any code, any timeout) boolean`

**Deprecated.** Deprecated: use state.run(code, {timeout: ms}) instead; note that the timeout there is in milliseconds.

doStringTimeout runs code with a timeout in seconds, returning false
when it fails or the state is closed.

#### `state.eval(string code, object? options) result<any, error>`

eval runs Lua code in this state and returns its value, like lua.eval.
Throws StateError after close.

#### `state.evalFile(string path, object? options) result<any, error>`

evalFile runs a Lua file in this state and returns what it returns, or
null. Fails like state.runFile. Throws StateError after close.

#### `state.get(string name) any`

get returns the value of a global, converted to OSL, or null when it is
nil. Throws StateError after close.

#### `state.getError() string`

**Deprecated.** Deprecated: the new methods return their error, as in state.run(code) catch (err) ( ... ).

getError returns the error of the last failed deprecated doString,
doStringTimeout, doFile, callTimeout, loadString or loadFile, "" after
one that succeeded, or "state not initialized" once closed.

#### `state.getGlobal(any name) any`

**Deprecated.** Deprecated: use state.get(name) instead.

getGlobal returns a global's value, or null.

#### `state.loadFile(any path) boolean`

**Deprecated.** Deprecated: use state.runFile(path), which fails with a typed error.

loadFile reports whether a Lua file can be read and compiles, without
running it.

#### `state.loadString(any code) boolean`

**Deprecated.** Deprecated: use state.run(code), which fails with ParseError for a syntax error.

loadString reports whether code compiles, without running it.

#### `state.register(string name, any fn)`

register makes fn callable from Lua as the global function name. Lua
arguments are converted to OSL values and fn's result back to Lua; an
error fn throws becomes a Lua error, which Lua's pcall can catch and
which otherwise fails the run with the error's own kind. Throws
StateError after close.

#### `state.run(string code, object? options) result<unit, error>`

run runs Lua code in this state, so the globals it sets stay for later
calls. Option: timeout in milliseconds, by default the state's. Fails
with ParseError, TimeoutError, ValueError or RuntimeError like
lua.run; an error thrown by a registered OSL function keeps its kind.
Throws StateError after close.

#### `state.runFile(string path, object? options) result<unit, error>`

runFile runs a Lua file in this state. Option: timeout in milliseconds,
by default the state's. Fails with NotFoundError or IOError when the
file can't be read, and otherwise like state.run. Throws StateError
after close.

#### `state.set(string name, any value)`

set sets a global to a copy of value: arrays become tables with keys 1
to n and objects tables with string keys. Throws StateError after
close.

#### `state.setGlobal(any name, any value)`

**Deprecated.** Deprecated: use state.set(name, value) instead.

setGlobal sets a global's value.
