Packages

Run WebAssembly from Elixir. Load WASM modules in Rust, Go, C — call them like native functions.

Current section

Files

Jump to
firebird lib mix tasks firebird.target.new.ex
Raw

lib/mix/tasks/firebird.target.new.ex

defmodule Mix.Tasks.Firebird.Target.New do
@moduledoc """
Generate a new WASM-ready Elixir module with `@wasm true` annotations.
## Usage
# Generate a basic WASM module
mix firebird.target.new MyApp.Math
# Generate with specific functions
mix firebird.target.new MyApp.Math --functions add,subtract,multiply
# Generate in a specific directory
mix firebird.target.new MyApp.Math --dir lib/wasm
# Generate with test file
mix firebird.target.new MyApp.Math --with-test
## Generated Module
The generated module will include:
- Module with `@wasm true` annotations
- Example functions showing compilable patterns
- Comments explaining the compilable subset
"""
use Mix.Task
@shortdoc "Generate a new WASM-ready Elixir module"
@switches [
dir: :string,
functions: :string,
with_test: :boolean
]
@aliases [
d: :dir,
f: :functions,
t: :with_test
]
@impl Mix.Task
def run(args) do
{opts, positional, _} = OptionParser.parse(args, switches: @switches, aliases: @aliases)
if Enum.empty?(positional) do
Mix.raise("Usage: mix firebird.target.new ModuleName [options]")
end
module_name = hd(positional)
dir = Keyword.get(opts, :dir, "lib/wasm_modules")
functions = parse_functions(Keyword.get(opts, :functions))
with_test = Keyword.get(opts, :with_test, false)
# Generate module path
filename =
module_name
|> String.split(".")
|> List.last()
|> Macro.underscore()
module_path = Path.join(dir, "#{filename}.ex")
File.mkdir_p!(dir)
# Generate module content
content = generate_module(module_name, functions)
File.write!(module_path, content)
Mix.shell().info("✅ Created #{module_path}")
# Generate test if requested
if with_test do
test_dir = Path.join("test", dir |> String.replace_prefix("lib/", ""))
File.mkdir_p!(test_dir)
test_path = Path.join(test_dir, "#{filename}_test.exs")
test_content = generate_test(module_name, functions)
File.write!(test_path, test_content)
Mix.shell().info("✅ Created #{test_path}")
end
Mix.shell().info("")
Mix.shell().info("Next steps:")
Mix.shell().info(" 1. Edit #{module_path} to add your functions")
Mix.shell().info(" 2. Compile: mix firebird.target --files #{module_path}")
Mix.shell().info(" 3. Verify: mix firebird.target --files #{module_path} --verify")
:ok
end
defp parse_functions(nil), do: nil
defp parse_functions(str), do: String.split(str, ",", trim: true)
defp generate_module(module_name, nil) do
"""
defmodule #{module_name} do
@moduledoc \"\"\"
WASM-compiled module.
All functions marked with `@wasm true` will be compiled to WebAssembly.
## Supported operations
- Integer arithmetic: +, -, *, div, rem
- Comparison: ==, !=, <, >, <=, >=
- Boolean: and, or, not
- Control flow: if/else, case, cond
- Pattern matching on integer literals
- Recursive functions
\"\"\"
@wasm true
def add(a, b), do: a + b
@wasm true
def subtract(a, b), do: a - b
@wasm true
def multiply(a, b), do: a * b
end
"""
end
defp generate_module(module_name, functions) do
func_defs =
Enum.map(functions, fn name ->
name = String.trim(name)
"""
@wasm true
def #{name}(a, b), do: a + b
"""
end)
|> Enum.join("\n")
"""
defmodule #{module_name} do
@moduledoc \"\"\"
WASM-compiled module.
\"\"\"
#{func_defs}end
"""
end
defp generate_test(module_name, nil) do
"""
defmodule #{module_name}Test do
use ExUnit.Case
@moduletag :wasm
setup do
source = File.read!(Path.join("lib/wasm_modules", "#{module_name |> String.split(".") |> List.last() |> Macro.underscore()}.ex"))
{:ok, result} = Firebird.Compiler.compile_source(source)
{:ok, instance} = Firebird.load(result.wasm)
on_exit(fn -> Firebird.stop(instance) end)
{:ok, instance: instance}
end
test "add", %{instance: inst} do
assert {:ok, [3]} = Firebird.call(inst, "add", [1, 2])
end
test "subtract", %{instance: inst} do
assert {:ok, [3]} = Firebird.call(inst, "subtract", [5, 2])
end
test "multiply", %{instance: inst} do
assert {:ok, [6]} = Firebird.call(inst, "multiply", [2, 3])
end
end
"""
end
defp generate_test(module_name, functions) do
test_cases =
Enum.map(functions, fn name ->
name = String.trim(name)
"""
test "#{name}", %{instance: inst} do
assert {:ok, [_result]} = Firebird.call(inst, "#{name}", [1, 2])
end
"""
end)
|> Enum.join("\n")
"""
defmodule #{module_name}Test do
use ExUnit.Case
@moduletag :wasm
setup do
source = File.read!(Path.join("lib/wasm_modules", "#{module_name |> String.split(".") |> List.last() |> Macro.underscore()}.ex"))
{:ok, result} = Firebird.Compiler.compile_source(source)
{:ok, instance} = Firebird.load(result.wasm)
on_exit(fn -> Firebird.stop(instance) end)
{:ok, instance: instance}
end
#{test_cases}end
"""
end
end