# `Schooner.Library.Loader`
[🔗](https://github.com/ausimian/schooner/blob/1.2.0/lib/schooner/library/loader.ex#L1)

Compiles `(define-library ...)` declarations into
`%Schooner.Library{}` registry entries.

## Supported declaration heads

  * `(import spec ...)` — pull bindings from already-registered
    libraries, with any of the r7rs import modifiers: `only`,
    `except`, `prefix` and `rename`.
  * `(begin form ...)` — body forms evaluated in the library's
    private env.
  * `(export name ...)` — names visible to importers. Each entry is
    a bare symbol or `(rename internal external)`. Names not bound
    by the body or imports raise at compile time.
  * `(cond-expand clause ...)` — selects a list of declarations
    based on the feature requirement clauses. Supported requirement
    forms: `feature-id` (a symbol), `(library NAME)`, `(and ...)`,
    `(or ...)`, `(not ...)`, and an `else` clause.
  * `(include "path" ...)` — read each named file as a sequence of
    body forms and splice them into the library's `(begin …)`. Paths
    resolve relative to the file containing the `include` form.
  * `(include-library-declarations "path" ...)` — read each named
    file as a sequence of declarations and process them in place.
    Paths resolve relative to the including file, so nested includes
    thread the directory of the innermost file.

When a base directory is known (from `load_file/2` or the
`:base_dir` option), include paths, relative or absolute, that
resolve outside it are rejected. Nested includes retain this root
even when their resolution directory changes. Without a base
directory, both relative and absolute include paths are an error.

## Diagnostics

Errors raised by the loader quote the file path and `line:column` of
the offending source datum where the reader can supply one. Libraries
loaded from disk record their origin in the `:source` field as
`{:file, path, {line, column}}`. The topological pre-pass over
`include-library-declarations` runs without per-path positions, so a
malformed path datum found during that pass is reported without a
line number; once the pass succeeds, the main walker re-reads the
same files with positions and quotes any subsequent error precisely.

Libraries always record source locations. Script-level errors
raised while expanding or evaluating a library body, or later by a
procedure the library defines, carry the
`Schooner.Location` of the form in the file it was read from: the
library's own file, or the included file. Pass `debug: true` to
`load_file/3` or `load_string/3` to also locate errors raised while
applying procedures inside the library's code (see "Source
locations" in `Schooner`).

# `ctx`

```elixir
@type ctx() :: %{
  optional(:debug) =&gt; boolean(),
  base_dir: binary() | nil,
  root_dir: binary() | nil,
  path: binary() | nil
}
```

Path-resolution context threaded through declaration processing.

# `compile`

```elixir
@spec compile(Schooner.Value.t(), Schooner.Library.registry(), ctx()) ::
  Schooner.Library.t()
```

Compile a single `(define-library ...)` datum against `registry`.
Returns a `%Schooner.Library{}` ready for `Library.register/2`.

`ctx` carries the directory used to resolve relative include paths
and the path threaded into diagnostics. When called with a raw datum
(no positioned tree from the reader), error messages render without
line/column information.

# `load_file`

```elixir
@spec load_file(binary(), Schooner.Library.registry(), keyword()) ::
  Schooner.Library.registry()
```

Read a file containing one or more `(define-library ...)` forms and
register every library it defines into `registry`. Libraries are
compiled in topological order so each library's dependencies are
already present when its body is evaluated. Returns the augmented
registry.

Raises `ArgumentError` on cycles in the local-to-the-file dependency
graph. Cycles that mix file-local libraries with already-registered
libraries cannot occur because the registry is append-only.

# `load_string`

```elixir
@spec load_string(binary(), Schooner.Library.registry(), keyword()) ::
  Schooner.Library.registry()
```

Compile and register every define-library form in `source`.

Options:

  * `:base_dir` — directory used to resolve relative `(include …)` and
    `(include-library-declarations …)` paths and confine all include
    paths. Defaults to `nil`, which rejects both relative and absolute
    includes.
  * `:path` — file path threaded into diagnostics and error
    locations. Defaults to `nil` (anonymous source).
  * `:debug` — when `true`, locate errors raised while applying
    procedures inside the libraries' code. Defaults to `false`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
