defmodule Zig.Manifest do @moduledoc false alias Zig.Command # TODO: spec this better @type t :: [{pos_integer(), {Path.t(), pos_integer()}}] defmacro resolver(manifest, file, def_or_defp) do quote do @__zig_manifest unquote(manifest) # note that this resolver has to be rebuilt here, so that zigler can be compile-time only. unquote(def_or_defp)(__resolve(%{file_name: file, line: line}), do: __resolve(file, line)) defp __resolve(unquote(file), line), do: __resolve(@__zig_manifest, [], line) defp __resolve(file, line) when is_binary(file), do: {file, line} defp __resolve([{anchor_line, _} = head | rest], stack, line) when anchor_line < line do __resolve(rest, [head | stack], line) end defp __resolve([{anchor_line, {file, _}} | _], stack, line) when anchor_line >= line do case List.first(stack) do {anchor, {file, relative_line}} -> {file, line - anchor + relative_line} _ -> {unquote(file), line} end end defp __resolve([], stack, line) do case List.first(stack) do {anchor, {file, relative_line}} -> {file, line - anchor + relative_line} _ -> {unquote(file), line} end end end end def create(module, code) do manifest = code |> Command.split_on_newline() |> Enum.with_index(1) |> Enum.flat_map(fn {"// ref " <> rest, anchor} -> case String.split(rest, ":") do ["nofile", ""] -> [{anchor, {"nofile", 0}}] list -> # livebooks can have colons in the path. line = Enum.at(list, -1) file = list |> Enum.slice(0..-2//1) |> Enum.join(":") [{anchor, {file, String.to_integer(line)}}] end _ -> [] end) manifest_module = case module do %{language: Elixir} -> Module.concat(module.module, Manifest) %{language: :erlang} -> String.to_atom("#{module.module}_manifest") end [{^manifest_module, _bin}] = Code.compile_quoted( quote do defmodule unquote(manifest_module) do @moduledoc false require Zig.Manifest Zig.Manifest.resolver(unquote(manifest), unquote(module.zig_code_path), :def) end end ) %{module | manifest: manifest, manifest_module: manifest_module} end def unload(module) do :code.soft_purge(module.manifest_module) || raise "manifest purging error" module end end