# Logos v0.2.0 - Table of Contents

> A Clojure-flavored Lisp, embeddable in Elixir/BEAM applications -- parsed and executed via Ichor, with real tail-call optimization, namespaces/Vars, hygienic syntax-quote macros, and BEAM-native concurrency (spawn/send/receive, atoms).

## Pages

- [Logos](readme.md)
- [Tutorial: embedding Logos in an Elixir application](tutorial-1.md)
- [Examples: embedding Logos](examples.md)
- [Cheatsheet: embedding Logos](cheatsheet.md)
- [Changelog](changelog.md)
- [Contributing to Logos](contributing.md)
- [License](license.md)

- Language
  - [Tutorial: the Logos language](tutorial-2.md)
  - [Logos language reference](logos-language-reference.md)
  - [Logos language examples](logos_examples.md)
  - [Logos language cheatsheet](logos_cheatsheet.md)

- Stdlib Reference
  - [Logos stdlib &amp; special forms reference: overview](logos-stdlib-overview.md)
  - [Logos special forms reference](logos-special-forms.md)
  - [Logos primitives reference](logos-primitives.md)
  - [logos.core reference](logos-core-reference.md)
  - [logos.seq reference](logos-seq-reference.md)
  - [logos.concurrency reference](logos-concurrency-reference.md)
  - [logos.multimethod reference](logos-multimethod-reference.md)
  - [logos.set reference](logos-set-reference.md)
  - [logos.walk reference](logos-walk-reference.md)
  - [logos.string reference](logos-string-reference.md)
  - [logos.test reference](logos-test-reference.md)

## Modules

- Core
  - [Logos](Logos.md): Top-level end-to-end API: `text --[Reader]--> Logos.Form.t()
--[Macroexpand]--> Logos.Form.t() --[Eval]--> value`.
  - [Logos.Env](Logos.Env.md): Lexical scope, and *only* lexical scope. This is a plain immutable
chained-frame environment: a frame is a `bindings` map plus a `parent`
frame (or `nil` at the root). `let`-style lexical binding and `fn`
closures are the only things this module is responsible for --
top-level `def` visibility is an entirely separate concern, handled by
`Logos.Namespace`/`Logos.Var` against a `Logos.Runtime` (see
`Logos.Eval`'s moduledoc for how symbol resolution combines the two).
  - [Logos.EvalError](Logos.EvalError.md): The Elixir exception carrying a primitive-level evaluation failure --
division by zero, an unbound symbol, a wrong-arity call, and every
other case `Logos.Eval`/`Logos.Primitives` used to signal via a plain
`{:error, reason}` return before this module existed. `reason` is
exactly what that tuple's second element always was -- this is a
mechanical change in *how* a failure propagates (raise instead of
return), not in what failures look like.
  - [Logos.Form](Logos.Form.md): The reader/macro-level data type: what `Logos.Reader.read/1` and
`Logos.Macroexpand` produce and consume.
  - [Logos.MacroError](Logos.MacroError.md): Raised when applying a macro (during `Logos.Macroexpand.expand/3`) fails -- e.g. the macro function itself errors, or is called with a mismatched arity. Macroexpansion has no `{:error, _}`-tuple convention of its own (see `Logos.Macroexpand`'s moduledoc), so failures here are surfaced as a real exception, the same way a reader syntax error would be.
  - [Logos.Thrown](Logos.Thrown.md): The Elixir exception carrying a Logos-level `(throw :tag value)`.
`Logos.Eval`'s `try` special form catches this specifically (via a real
Elixir `rescue`), matches `tag` against each `catch` clause's keyword
with `=` -- Logos catches by keyword tag alone, with no exception-class
hierarchy the way Elixir's own `rescue` has -- and binds `value` to the
catch clause's bound name.

- Reader
  - [Logos.DataReaders](Logos.DataReaders.md): The two default tagged-literal readers (`#inst`, `#uuid`), pre-
registered into every fresh `Logos.Runtime` -- see that module's
`new/1` and `lib/logos/reader/actions.ex`'s `handle_rule(:tagged_literal,
...)`, which is what actually calls these at read time.
  - [Logos.Reader](Logos.Reader.md): The public reader: turns Logos source text into plain `Logos.Form.t()`
data, via `priv/grammar/logos.aether` (an Aether grammar) and
`Logos.Reader.Actions` (pure reification, see that module's docs).
  - [Logos.Reader.Actions](Logos.Reader.Actions.md): `Ichor.Actions` implementation for `priv/grammar/logos.aether`.

- Macroexpand &amp; Eval
  - [Logos.Eval](Logos.Eval.md): The tree-walking evaluator: `eval(form, env, runtime)`. Implements
exactly six special forms -- `quote`, `cond`, `do`, `def`, `fn`, `try`
-- every other list-headed form is a function-position call (evaluate
head, evaluate args, apply).
  - [Logos.Macroexpand](Logos.Macroexpand.md): The macroexpansion pass: `Logos data --[Logos.Macroexpand]--> Logos data`,
entirely separate from -- and running entirely before -- `Logos.Eval`.

- Runtime, Namespaces &amp; Vars
  - [Logos.Namespace](Logos.Namespace.md): Functions operating against a `Logos.Runtime`'s ETS table -- there is no
free-floating `%Logos.Namespace{}` struct someone could get out of sync
with the registry; every operation here reads/writes `runtime`'s table
directly. See `Logos.Runtime`'s moduledoc for the exact row shapes.
  - [Logos.Primitives](Logos.Primitives.md): Layer 1: the Elixir-implemented primitive functions every Logos program
is ultimately built on -- arithmetic, comparison, collections,
namespace/Var management, macros, concurrency, and dev-tooling
introspection. (String operations are not primitives here; they reach
Logos through `import`ing allowlisted Elixir functions -- see
`Logos.Interop.Allowlist`.) Every primitive takes `(args, runtime)`
uniformly, where `args` is the already-evaluated argument list and
`runtime` is the calling `Logos.Runtime`.
  - [Logos.Runtime](Logos.Runtime.md): `Logos.Runtime` -- the per-embedding-instance, `:public` ETS-backed
namespace registry. **Never** a global singleton: every embedder of
Logos calls `Logos.Runtime.new/1` (or the `Logos.new_runtime/0`
convenience) to get its own isolated table, so multiple independent
Logos instances can coexist in the same VM without seeing each other's
namespaces or vars. An earlier version of this codebase instead kept a
single bare, module-level, `:named_table` ETS table shared by the whole
VM; this module replaced that global-singleton design.
  - [Logos.Runtime.Registry](Logos.Runtime.Registry.md): A named, VM-wide lookup table from an arbitrary `String.t()` name to a
`%Logos.Runtime{}`. Its main consumer is `mix logos.remsh`: a host app
that starts distributed (`--sname`/`--name` + cookie) registers its
`Runtime`(s) here under a name, and a `logos.remsh` session attached to
that node looks up "the Runtime the host app wants exposed" by name
rather than needing a direct reference passed around from process to
process.
  - [Logos.Stdlib](Logos.Stdlib.md): Loads the Layer-2 standard library -- macros and functions written in
Logos itself, layered on top of the Elixir-implemented Layer-1
primitives -- into a fresh `Logos.Runtime`. Source lives as real files
under `priv/stdlib/` (not embedded Elixir string constants), one per
namespace
  - [Logos.Var](Logos.Var.md): A Var's *identity*: `%{ns, name}`. Conceptually a Var is `%{ns, name,
value, meta}` -- mutable, interned in a `Logos.Namespace` -- but only
the `ns`/`name` pair lives on this struct; see below for where
`value`/`meta` actually live.

- Concurrency
  - [Logos.Process](Logos.Process.md): The BEAM-native concurrency primitive family: `spawn`, `spawn-link`,
`spawn-monitor`, `link`, `unlink`, `monitor`, `demonitor`,
`trap-exits!`, `exit`, `self`, `send`, `receive-match!`. Every function
here is plain Elixir, called from `Logos.Primitives`' `(args, runtime)`
dispatch against already-evaluated argument values -- none of them need
macro-hood. (The one related piece that *does* need macro-hood is the
Lisp-level `receive` macro in `concurrency.logos`, since its clauses must
stay unevaluated until a match is found.)
  - [Logos.ProcessCrash](Logos.ProcessCrash.md): The Elixir exception a Logos-level process (a `Logos.Fn` thunk running
under `spawn`/`spawn-link`/`spawn-monitor`) raises when its body's
evaluation raises `Logos.EvalError` (`Logos.Eval`'s own primitive-level
failure signal -- see that module's moduledoc). Deliberately re-raised
as a *real* BEAM crash (rather than silently swallowed) so that
`link`/`monitor` on the spawning side observe a genuine `:EXIT`/`:DOWN`
signal carrying `reason`, exactly like any other process crash: a
Logos function erroring is treated as "this process failed," the same
as an Elixir process raising.

- Value Types
  - [Logos.Atom](Logos.Atom.md): A Logos atom (mutable-reference-via-process): `%Logos.Atom{pid: pid()}`.
This is unrelated to Elixir's `:atom` type (an interned symbolic
constant) -- a Logos atom is a mutable reference cell, analogous to
Clojure's `atom`.
  - [Logos.Char](Logos.Char.md): A Logos character literal: `\a`, `\newline`, `\space`, `\tab`,
`\u0041`.
  - [Logos.Decimal](Logos.Decimal.md): A Logos decimal literal: `10.99M`, `3M`, `-2.5M` -- Clojure-style
arbitrary-precision, exact-*scale* decimal arithmetic. Unlike `float`
(IEEE 754 binary floating point, can't represent `0.1` exactly) and
unlike `Logos.Ratio` (always reduces to lowest terms, so `10.10`
loses its original "two decimal places" the moment it becomes a
ratio), a decimal preserves the scale it was written with: `10.10M`
prints back as `"10.10M"`, not `"10.1M"`.
  - [Logos.Fn](Logos.Fn.md): A Logos closure: `%Logos.Fn{params, variadic?, body, env, clauses}`.
`env` is the lexically-captured defining `Logos.Env` -- capturing it is
the whole point of `fn`: it lets the closure see the bindings visible
at its definition site even after that scope has otherwise returned,
which is what makes it a closure rather than a plain function pointer.
  - [Logos.Keyword](Logos.Keyword.md): A Logos keyword: `:a`, `:my-ns/x`. Self-evaluating -- unlike a symbol, a
keyword never resolves against an environment.
  - [Logos.Keyword.Registry](Logos.Keyword.Registry.md): Interns `%Logos.Keyword{}` structs so that reading the same keyword text
twice (`:a` ... `:a`) always yields the exact same struct value.
  - [Logos.Pid](Logos.Pid.md): A Logos process handle: `%Logos.Pid{pid: pid()}`. Print-only: a pid can
be displayed but never appears in source text, since there is no
reader syntax that produces one.
  - [Logos.Ratio](Logos.Ratio.md): A Logos ratio literal: `1/3`, `-22/7`.
  - [Logos.Record](Logos.Record.md): A Logos record: `%Logos.Record{type: kw, fields: %{kw => value}}` --
built by `priv/stdlib/core.logos`'s `defrecord` macro via the `record`
primitive (`Logos.Primitives`), no other construction path. `type` is
always a namespace-qualified `Logos.Keyword` (e.g. `:user/point`),
fixed at `defrecord`-expansion time to the namespace `defrecord` was
invoked from -- so two different namespaces' same-named record types
never collide, the direct analogue of Clojure records being tied to
their defining namespace at compile time. `fields` is keyed by
`Logos.Keyword`, matching how idiomatic Logos maps already use keyword
keys, so record field access (`(:x point)`, `(get point :x)`) reads
exactly like map field access -- see `Logos.Eval.apply_fn/3`'s
keyword-as-function clauses and `Logos.Primitives`' `get`/`assoc`.
  - [Logos.SortedMap](Logos.SortedMap.md): A Logos sorted map: `(sorted-map ...)` / `(sorted-map-by cmp ...)`.
  - [Logos.SortedSet](Logos.SortedSet.md): A Logos sorted set: `(sorted-set ...)` / `(sorted-set-by cmp ...)`.
  - [Logos.Symbol](Logos.Symbol.md): A Logos symbol: `x`, `my-fn`, `ns/x`, `+`, `/`.
  - [Logos.Transient](Logos.Transient.md): A Logos transient: `(transient coll)`. Wraps a single-row, `:private`
ETS table (`{:value, current}`), not a spawned process like
`Logos.Atom` -- a deliberate, different choice from atoms, chosen
because a transient's whole purpose is the *opposite* of an atom's:
atoms are meant to be a shared, thread-safe reference cell (hence a
process + message-passing, so BEAM's one-message-at-a-time guarantee
gives atomicity for free); a transient is explicitly *not* meant to be
shared -- real Clojure transients are single-thread-use only, and using
one from a different thread (or after `persistent!`) is a documented
error. A `:private` ETS table gets both of those for free, as genuine
BEAM-enforced guarantees rather than a documented-only discipline:
only the owning process may `:ets.lookup`/`:ets.insert` into it (any
other process gets `ArgumentError`), and `persistent!/1` deletes the
table outright, so any further op against it raises the exact same
`ArgumentError` -- one mechanism naturally covers both of Clojure's
transient safety rules, not two separate checks. This also means a
transient op is genuine O(1) in-place mutation, no message round-trip --
actually delivering the performance transients exist for in real
Clojure, unlike a process-based design would have.
  - [Logos.Vector](Logos.Vector.md): A Logos persistent vector: `[1 2 3]`.

- Interop
  - [Logos.Interop.Allowlist](Logos.Interop.Allowlist.md): The host-curated allowlist `import` is restricted to: Logos's `import`
only ever pulls from this module's `@entries` map, never opens up
arbitrary `Module.function` access to the underlying Elixir/Erlang
runtime. This is what keeps `import` a sandboxed capability rather than
a full escape hatch out of the Logos data model.

- Dev Tooling
  - [Logos.Format](Logos.Format.md): The Logos code formatter (`mix logos.format`). Style rules: 2-space
indent; one space between siblings; no space before a closing paren;
`defn`/`fn`/`let`/`ns`/`cond`/`try` bodies indent 2 spaces from the
opening form (not aligned to the first argument). This is deliberately
the simplest consistent rule, not a full `cljfmt`-parity style guide.
  - [Logos.Printer](Logos.Printer.md): `Logos.Printer.print/1` -- the textual printer. Every reader-producible
type (numbers, strings, symbols, keywords, collections) must round-trip:
`print/1` followed by `Logos.Reader.read/1` must yield an `==`-equal
value back. Runtime-only types (`Pid`, `Atom`, `Fn`) are exempt from
that requirement -- they print informatively but have no reader syntax
to read back into.
  - [Logos.Repl](Logos.Repl.md): The local REPL (`mix logos.repl`): a fresh Runtime with `logos.core`
and project files preloaded, a stdin read-eval-print loop, and a
Clojure-style prompt (`user=>`). History vars `*1`/`*2`/`*3`/`*e` are
ordinary def'd Vars. Each entered form is evaluated in its own
spawned+monitored process, which gives real crash isolation and (in
principle) a way to cancel just that form -- see the Ctrl+C section
below for exactly what's implemented today.
  - [Logos.StdlibDocs](Logos.StdlibDocs.md): Builds the Markdown source for `guides/language/stdlib/*.md` -- one
generated, per-symbol lookup document per file-backed stdlib namespace
(`Logos.Stdlib.namespaces/0`), one for the six special forms
`Logos.Eval` wires in directly, one for every Layer-1 primitive
(`Logos.Primitives.docs/0`, see `primitives_document/0`), and a
table-of-contents `OVERVIEW.md` linking to every one of them (see
`overview_document/0`) -- ALL of these, `OVERVIEW.md` included, are
generated and written by `mix logos.gen_docs`; none is hand-edited
(`OVERVIEW.md`'s own body is a static template, unlike the others'
`:doc`-metadata-driven content, but it still goes through the exact
same generate-and-write path, so hand-editing it would just get
silently clobbered on the next regeneration like any other file here).
Each namespace/primitives document covers every public Var, pulled live
from its own `:doc` metadata (`Logos.Primitives.install!/1` gives every
primitive a real one too, sourced from the same `Logos.Primitives.docs/0`
data -- no more generic `"Layer-1 primitive"` placeholder), together
with a runnable example: `@examples`/`@special_form_examples`/
`@primitive_examples` hold one Logos source snippet per documented
item, actually evaluated (fresh `Logos.new_runtime/0` inside its own
isolated process, starting from namespace `"user"` -- see
`evaluate_example/2`'s own comment) at generation time and rendered
alongside its real, freshly-computed printed result -- never a
hand-typed "expected output" that could quietly drift from the code.
Every stdlib-namespace entry (special forms/primitives are Elixir-
implemented, so this doesn't apply to those two pages) also gets its
own defining form's exact source, reconstructed from
`Logos.Reader.tokenize/1`'s position-preserving token stream rather
than re-parsed by hand -- see `extract_source_snippets/1`'s own
comment.

## Mix Tasks

- Mix Tasks
  - [mix logos.format](Mix.Tasks.Logos.Format.md): Formats Logos source files, or checks that they're already formatted.
Built on `Logos.Format`, which formats from Ichor's raw token stream
(position- and text-preserving, so comments survive) plus a simple
paren-nesting-depth indentation rule -- `--check-formatted` mirrors
`mix format --check-formatted`.
  - [mix logos.gen_docs](Mix.Tasks.Logos.GenDocs.md): Regenerates `guides/language/stdlib/*.md` -- an overview page, one
special-forms page, and one page per stdlib namespace, all built from
`Logos.StdlibDocs.documents/0` (every entry paired with a real,
freshly-evaluated example). Those files are checked in and generated;
never hand-edit them (see each file's own header comment and
`Logos.StdlibDocs`'s moduledoc).
  - [mix logos.remsh](Mix.Tasks.Logos.Remsh.md): Attaches an interactive REPL to a running, distributed Logos node,
mirroring `iex --remsh`. The target host application must already be
running as a distributed node (started with `--sname`/`--name` and a
shared cookie) and must have registered its `Logos.Runtime`(s) by name
via `Logos.Runtime.Registry`. Each form entered here is shipped via
`:rpc.call` to the target node and evaluated there -- this session
never reads the target's ETS tables directly.
  - [mix logos.repl](Mix.Tasks.Logos.Repl.md): Starts a local Logos REPL: a fresh Runtime with `logos.core` and any
project `.logos` files preloaded, then a stdin read-eval-print loop
with a Clojure-style prompt (`user=>`).
  - [mix logos.run](Mix.Tasks.Logos.Run.md): Runs a Logos script file or an inline expression: builds a fresh
Runtime, evaluates the source as a top-level form sequence, and exits
with a status reflecting success or failure.

