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.
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.
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:
import "std:lua"
log lua.eval("1 + 2").unwrap()
log lua.eval("local x = 2 return x * 3").unwrap()
log lua.version()Lua tables convert to OSL values: a table with keys 1 to n becomes an array, any other table an object.
import "std:lua"
log lua.eval("{1, 2, 3}").unwrap()
log lua.eval("{name = 'Ada', n = 3}").unwrap()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:
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().kindThe 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:
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()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:
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()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.
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()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:
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()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:
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()Lua code runs with lua.eval. What can it do?
Call a Lua function
Define a Lua function square and call it with 7 from OSL.
49
API reference#
29 members, generated from the std:lua source of OSL 9.3.0.
Functions
lua.create() *lua.StateDeprecated. Deprecated: use lua.new(options) instead.
create starts a Lua interpreter.
lua.doString(any code) *lua.StateDeprecated. 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) anyDeprecated. 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.Statenew 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.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) objectDeprecated. 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) resultDeprecated. 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() stringversion 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) anyDeprecated. 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) booleanDeprecated. 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) booleanDeprecated. 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) booleanDeprecated. 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) anyget returns the value of a global, converted to OSL, or null when it is nil. Throws StateError after close.
state.getError() stringDeprecated. 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) anyDeprecated. Deprecated: use state.get(name) instead.
getGlobal returns a global's value, or null.
state.loadFile(any path) booleanDeprecated. 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) booleanDeprecated. 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.