Skip to content

Commit

Permalink
feat: Collapse Runtime into Luau (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
doomspork authored Jan 9, 2024
1 parent 470db44 commit df2a0d4
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 165 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ luau-*.tar

# Temporary files, for example, from tests.
/tmp/

.DS_Store
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Lūʻau

Welcome to the Lua Lūʻau!
Welcome to the Lua Lūʻau!

## What's in a name?

Expand Down
59 changes: 54 additions & 5 deletions lib/luau.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,64 @@ defmodule Luau do
@moduledoc """
Let's get this party started!
"""
@type t :: %__MODULE__{
id: nil | String.t(),
lua_files: [String.t()],
modules: [Luau.Library.t()],
state: tuple(),
variables: %{[String.t()] => any()}
}

alias Luau.Runtime
alias Luerl.New, as: Luerl

@spec init() :: Runtime.t()
defstruct id: nil, modules: [], lua_files: [], state: nil, variables: %{}

@type result :: {:ok, t()}
@type error :: {:error, atom() | String.t()}

@spec init() :: Luau.t()
def init do
Runtime.init()
%Luau{id: Nanoid.generate(), state: Luerl.init()}
end

@spec set_variable(Luau.t(), [String.t()] | String.t(), any()) :: result | error
def set_variable(luau, key, value) do
key = List.wrap(key)

case Luerl.set_table_keys_dec(luau.state, key, value) do
{:ok, _result, new_state} ->
{:ok, %{luau | state: new_state, variables: Map.put(luau.variables, key, value)}}

{:lua_error, reason, _state} ->
{:error, reason}
end
end

def run(runtime, lua) do
Runtime.run(runtime, lua)
@spec load_module!(Luau.t(), Luau.Library.t()) :: Luau.t()
def load_module!(luau, module) do
new_state = Luerl.load_module_dec(luau.state, [module.scope()], module)
%{luau | state: new_state, modules: [module | luau.modules]}
end

@spec load_lua!(Luau.t(), String.t()) :: Luau.t()
def load_lua!(luau, path) do
case Luerl.dofile(luau.state, String.to_charlist(path)) do
{:ok, _result, new_state} ->
%{luau | state: new_state, lua_files: [path | luau.lua_files]}

:error ->
raise "Could not load Lua file #{path}, file not found"
end
end

@spec run(Luau.t(), String.t()) :: {:ok, any(), Luau.t()} | error
def run(luau, lua) do
case Luerl.do(luau.state, lua) do
{:ok, result, new_state} ->
{:ok, result, %{luau | state: new_state}}

{:error, reason, _state} ->
{:error, reason}
end
end
end
62 changes: 0 additions & 62 deletions lib/runtime.ex

This file was deleted.

Empty file removed test/library_test.exs
Empty file.
104 changes: 91 additions & 13 deletions test/luau_test.exs
Original file line number Diff line number Diff line change
@@ -1,27 +1,105 @@
defmodule LuauTest do
use ExUnit.Case

defmodule TestLibrary do
@moduledoc """
A test library for Luau.
"""
use Luau.Library, scope: "test"
defmodule AdderLibrary do
use Luau.Library, scope: "Adder"

deflua hello(name) do
"Hello, #{name}!"
deflua add(a, b) do
a + b
end
end

defmodule EvenLibrary do
use Luau.Library, scope: "Even"

deflua is_even(value) do
rem(value, 2) == 0
end
end

describe "init/0" do
test "returns a runtime" do
assert %Luau.Runtime{} = Luau.init()
test "returns a luau containing the Lua state and unique id" do
assert %Luau{id: id, state: state} = Luau.init()
assert is_binary(id)
refute is_nil(state)
end
end

describe "set_variable/3" do
setup do
{:ok, luau: Luau.init()}
end

test "sets a variable", %{luau: luau} do
name = ["name"]
value = "Robert"
assert {:ok, %Luau{variables: %{^name => ^value}}} = Luau.set_variable(luau, name, value)
end

test "wraps variable path as necessary", %{luau: luau} do
name = "name"
value = "Robert"
assert {:ok, %Luau{variables: %{[^name] => ^value}}} = Luau.set_variable(luau, name, value)
end
end

describe "load_module!/2" do
setup do
{:ok, luau: Luau.init()}
end

test "load a Luau.Library to the lua luau", %{luau: luau} do
assert %Luau{modules: [EvenLibrary, AdderLibrary]} =
loaded_luau = luau |> Luau.load_module!(AdderLibrary) |> Luau.load_module!(EvenLibrary)

script = """
local a = Adder.add(3,3)
local b = Even.is_even(a)
return b
"""

assert {:ok, [true], _luau} = Luau.run(loaded_luau, script)
end
end

describe "execute/2" do
test "evaluates Lua" do
# runtime = Luau.initialize(libraries: [TestLibrary], variables: %{name: "Robert"})
# assert {"Hello, Robert!", %Luau.Runtime{}} = Luau.execute(runtime, ~s/test.hello(name)/)
describe "load_lua!/2" do
setup do
{:ok, luau: Luau.init()}
end

test "raises an error for missing files", %{luau: luau} do
assert_raise RuntimeError, fn ->
Luau.load_lua!(luau, "missing.lua")
end
end

test "loads simple lua script into luau", %{luau: luau} do
path = Path.join([__DIR__, "support", "hello.lua"])
assert %Luau{lua_files: [^path]} = loaded_luau = Luau.load_lua!(luau, path)

script = """
return hello("Robert")
"""

assert {:ok, ["Hello Robert!"], _luau} = Luau.run(loaded_luau, script)
end

test "loads lua module into luau", %{luau: luau} do
path = Path.join([__DIR__, "support", "enum.lua"])

assert {:ok, %Luau{lua_files: [^path]} = loaded_luau} =
luau
|> Luau.load_lua!(path)
|> Luau.set_variable("numbers", [1, 2, 3, 4])

script = """
local all = Enum.all(numbers, function(n) return n % 2 == 0 end)
local any = Enum.any(numbers, function(n) return n % 2 == 0 end)
return all, any
"""

assert {:ok, [false, true], _luau} = Luau.run(loaded_luau, script)
end
end
end
84 changes: 0 additions & 84 deletions test/runtime_test.exs

This file was deleted.

10 changes: 10 additions & 0 deletions test/support/enum.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,14 @@ function E.any(values, callback)
return false
end

function E.all(values, callback)
for i, v in ipairs(values) do
if not callback(v) then
return false
end
end

return true
end

Enum = E

0 comments on commit df2a0d4

Please sign in to comment.