Iter 13d: rustdoc polish for ailang-core + new docwriter agent
Adds ailang-docwriter to /agents/ — a recurring role for keeping crate-, module-, and pub-item-level rustdoc accurate. First mission: ailang-core. Crate root, every module root, every pub item documented; intra-doc links throughout; Iter-13a additions (TypeDef.vars, Type::Con.args) get an explicit backwards-compat note. Two stale broken-link warnings in ailang-check fixed in passing. cargo doc --no-deps now warning-free across the workspace; promoted to verification invariant 6 in DESIGN.md.
This commit is contained in:
+163
-15
@@ -1,32 +1,78 @@
|
||||
//! AST nodes. Serde layout matches the JSON schema in `docs/DESIGN.md`.
|
||||
//! AST nodes for the AILang language.
|
||||
//!
|
||||
//! Every type in this module is the in-memory mirror of a node in the
|
||||
//! AILang JSON schema documented in `docs/DESIGN.md`. The serde
|
||||
//! attributes carry the schema: field renames (`as`, `type`, `fn`,
|
||||
//! `paramTypes`, `retType`), enum tags (`kind`, `t`, `k`, `p`), and
|
||||
//! `skip_serializing_if` predicates that keep the canonical-JSON
|
||||
//! representation backwards compatible across schema extensions.
|
||||
//!
|
||||
//! The entry type is [`Module`]. The two helpers [`def_name`] and
|
||||
//! [`def_kind`] are intended for tools (`ail diff`, `ail manifest`) that
|
||||
//! consume a [`Def`] without going through method calls.
|
||||
//!
|
||||
//! This module does **not** typecheck, evaluate, or hash anything — see
|
||||
//! [`crate::canonical`] for canonical bytes, [`crate::hash`] for content
|
||||
//! hashes, and the `ailang-check` crate for typechecking.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A complete AILang translation unit.
|
||||
///
|
||||
/// Carries the schema tag (always [`crate::SCHEMA`] for this version),
|
||||
/// a module name, the list of imports, and the list of top-level
|
||||
/// definitions. A module is the smallest unit that can be loaded,
|
||||
/// hashed, and emitted as LLVM IR.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Module {
|
||||
/// Schema identifier; must equal [`crate::SCHEMA`] at load time.
|
||||
pub schema: String,
|
||||
/// Module name. By convention matches the file stem
|
||||
/// (`<name>.ail.json`); the [`crate::workspace`] loader enforces
|
||||
/// this on import.
|
||||
pub name: String,
|
||||
/// Imports of other modules. Resolved by the workspace loader as
|
||||
/// `<root_dir>/<module>.ail.json`.
|
||||
#[serde(default)]
|
||||
pub imports: Vec<Import>,
|
||||
/// Top-level definitions in declaration order.
|
||||
pub defs: Vec<Def>,
|
||||
}
|
||||
|
||||
/// An import statement.
|
||||
///
|
||||
/// `module` is the bare module name (no path, no extension). `alias`
|
||||
/// renames it for use in the body; absent means the module is referred
|
||||
/// to under its own name.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Import {
|
||||
/// Imported module name, resolved relative to the entry file's
|
||||
/// directory.
|
||||
pub module: String,
|
||||
/// Optional rename (`import foo as bar`). Serialized as `as` in
|
||||
/// JSON; omitted when absent.
|
||||
#[serde(rename = "as", default, skip_serializing_if = "Option::is_none")]
|
||||
pub alias: Option<String>,
|
||||
}
|
||||
|
||||
/// A top-level definition — the unit that gets a content hash.
|
||||
///
|
||||
/// Discriminated by the JSON `kind` tag: `"fn"`, `"const"`, or
|
||||
/// `"type"`. Use [`def_name`] / [`def_kind`] (or the [`Def::name`]
|
||||
/// method) to inspect generically without matching every variant.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "kind", rename_all = "lowercase")]
|
||||
pub enum Def {
|
||||
/// Function definition; see [`FnDef`].
|
||||
Fn(FnDef),
|
||||
/// Constant definition; see [`ConstDef`].
|
||||
Const(ConstDef),
|
||||
/// Type (ADT) definition; see [`TypeDef`].
|
||||
Type(TypeDef),
|
||||
}
|
||||
|
||||
impl Def {
|
||||
/// Source-level name of the definition, regardless of kind.
|
||||
pub fn name(&self) -> &str {
|
||||
match self {
|
||||
Def::Fn(f) => &f.name,
|
||||
@@ -36,14 +82,20 @@ impl Def {
|
||||
}
|
||||
}
|
||||
|
||||
/// External helper: name of a definition (for tools like `ail diff`
|
||||
/// that consume the def node decoupled from the method call).
|
||||
/// External helper: name of a definition.
|
||||
///
|
||||
/// Equivalent to [`Def::name`], exposed as a free function so tools
|
||||
/// like `ail diff` that hold a [`Def`] node can read its name without
|
||||
/// importing the inherent-method namespace.
|
||||
pub fn def_name(def: &Def) -> &str {
|
||||
def.name()
|
||||
}
|
||||
|
||||
/// External helper: discriminator tag of a definition (`fn`, `const`, `type`).
|
||||
/// Identical to the `kind` field in the JSON representation.
|
||||
/// External helper: discriminator tag of a definition (`"fn"`,
|
||||
/// `"const"`, `"type"`).
|
||||
///
|
||||
/// Identical to the `kind` field in the JSON representation. Useful
|
||||
/// for tools that group or filter definitions by kind.
|
||||
pub fn def_kind(def: &Def) -> &'static str {
|
||||
match def {
|
||||
Def::Fn(_) => "fn",
|
||||
@@ -52,75 +104,129 @@ pub fn def_kind(def: &Def) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
/// An algebraic data type definition.
|
||||
///
|
||||
/// A `TypeDef` introduces a named type with one or more constructors.
|
||||
/// Monomorphic types (`vars` empty) and parameterised types (`vars`
|
||||
/// non-empty) share the same node — see the field docs for the
|
||||
/// schema-compatibility note.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TypeDef {
|
||||
/// Type name (capitalised by convention).
|
||||
pub name: String,
|
||||
/// Type parameters (Iter 13a). A monomorphic ADT has `vars` empty
|
||||
/// and is serialized identically to the pre-13a schema (the field is
|
||||
/// skipped when empty), preserving the canonical-JSON hash of every
|
||||
/// existing module on disk.
|
||||
/// Type parameters (Iter 13a parameterised-ADT support). A
|
||||
/// monomorphic ADT has `vars` empty and is serialized identically
|
||||
/// to the pre-13a schema (the field is **omitted** when empty),
|
||||
/// preserving the canonical-JSON hash of every existing module on
|
||||
/// disk. See the regression test
|
||||
/// `iter13a_schema_extension_preserves_pre_13a_hashes` in
|
||||
/// [`crate::hash`].
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub vars: Vec<String>,
|
||||
/// Constructors in declaration order. Pattern-match arms in
|
||||
/// `ailang-check` are validated against this list.
|
||||
pub ctors: Vec<Ctor>,
|
||||
/// Optional source-level documentation string.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub doc: Option<String>,
|
||||
}
|
||||
|
||||
/// A single constructor of a [`TypeDef`].
|
||||
///
|
||||
/// `fields` is the constructor's positional argument list as types;
|
||||
/// nullary constructors have an empty list.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Ctor {
|
||||
/// Constructor name (capitalised by convention; unique within its
|
||||
/// `TypeDef`).
|
||||
pub name: String,
|
||||
/// Positional field types. Empty for nullary constructors.
|
||||
#[serde(default)]
|
||||
pub fields: Vec<Type>,
|
||||
}
|
||||
|
||||
/// A function definition.
|
||||
///
|
||||
/// `ty` is the full function type (including effect set and any
|
||||
/// `Forall` quantifier for top-level polymorphism). `params` are the
|
||||
/// names bound in `body`, in order.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct FnDef {
|
||||
/// Function name.
|
||||
pub name: String,
|
||||
/// Declared function type. Top-level polymorphism is opt-in via
|
||||
/// [`Type::Forall`].
|
||||
#[serde(rename = "type")]
|
||||
pub ty: Type,
|
||||
/// Parameter names, in the order they appear in `ty.params`.
|
||||
pub params: Vec<String>,
|
||||
/// Function body.
|
||||
pub body: Term,
|
||||
/// Optional source-level documentation string.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub doc: Option<String>,
|
||||
}
|
||||
|
||||
/// A constant (top-level binding to a value).
|
||||
///
|
||||
/// Differs from a zero-arg [`FnDef`] in that it is evaluated once and
|
||||
/// has no parameter list; the codegen emits it as a global.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ConstDef {
|
||||
/// Constant name.
|
||||
pub name: String,
|
||||
/// Declared type of the value.
|
||||
#[serde(rename = "type")]
|
||||
pub ty: Type,
|
||||
/// The value expression. Must be pure (no effects).
|
||||
pub value: Term,
|
||||
/// Optional source-level documentation string.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub doc: Option<String>,
|
||||
}
|
||||
|
||||
/// An expression node ("Term" = computation that produces a value).
|
||||
///
|
||||
/// The JSON discriminator is the `t` field. Each variant's doc
|
||||
/// describes its concrete-syntax intent; the typing rules live in
|
||||
/// `ailang-check`.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "t", rename_all = "lowercase")]
|
||||
pub enum Term {
|
||||
/// Literal value (see [`Literal`]).
|
||||
Lit { lit: Literal },
|
||||
/// Variable reference (parameter, local, top-level def, or
|
||||
/// imported alias).
|
||||
Var { name: String },
|
||||
/// Function application. `callee` is evaluated to a function value;
|
||||
/// `args` are evaluated left-to-right.
|
||||
App {
|
||||
#[serde(rename = "fn")]
|
||||
callee: Box<Term>,
|
||||
args: Vec<Term>,
|
||||
},
|
||||
/// Let-binding: `value` is evaluated and bound to `name` in `body`.
|
||||
Let {
|
||||
name: String,
|
||||
value: Box<Term>,
|
||||
body: Box<Term>,
|
||||
},
|
||||
/// If-expression. Both branches must have the same type.
|
||||
If {
|
||||
cond: Box<Term>,
|
||||
then: Box<Term>,
|
||||
#[serde(rename = "else")]
|
||||
else_: Box<Term>,
|
||||
},
|
||||
/// Effect operation invocation (e.g. `do print "hi"`). The `op` is
|
||||
/// resolved against the effect-handler table at link time.
|
||||
Do {
|
||||
op: String,
|
||||
args: Vec<Term>,
|
||||
},
|
||||
/// Constructor application. `type_name` binds the ADT, `ctor` the variant.
|
||||
/// Example: `Some(42)` -> `{ "t": "ctor", "type": "Option", "ctor": "Some",
|
||||
/// Constructor application. `type_name` binds the ADT, `ctor` the
|
||||
/// variant. Example: `Some(42)` ->
|
||||
/// `{ "t": "ctor", "type": "Option", "ctor": "Some",
|
||||
/// "args": [{"t":"lit","lit":{"kind":"int","value":42}}] }`.
|
||||
Ctor {
|
||||
#[serde(rename = "type")]
|
||||
@@ -129,7 +235,8 @@ pub enum Term {
|
||||
#[serde(default)]
|
||||
args: Vec<Term>,
|
||||
},
|
||||
/// Pattern matching over a value.
|
||||
/// Pattern matching over a value. Arms are tried top-to-bottom;
|
||||
/// exhaustiveness is checked by `ailang-check`.
|
||||
Match {
|
||||
scrutinee: Box<Term>,
|
||||
arms: Vec<Arm>,
|
||||
@@ -158,12 +265,22 @@ pub enum Term {
|
||||
},
|
||||
}
|
||||
|
||||
/// One arm of a [`Term::Match`].
|
||||
///
|
||||
/// `pat` is the pattern; `body` is evaluated when the pattern matches,
|
||||
/// with any pattern-bound variables in scope.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Arm {
|
||||
/// Pattern to match the scrutinee against.
|
||||
pub pat: Pattern,
|
||||
/// Body to evaluate on a successful match.
|
||||
pub body: Term,
|
||||
}
|
||||
|
||||
/// A match pattern.
|
||||
///
|
||||
/// The JSON discriminator is the `p` field. Patterns are linear: each
|
||||
/// pattern variable may appear at most once.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "p", rename_all = "lowercase")]
|
||||
pub enum Pattern {
|
||||
@@ -181,36 +298,63 @@ pub enum Pattern {
|
||||
},
|
||||
}
|
||||
|
||||
/// A literal value.
|
||||
///
|
||||
/// The JSON discriminator is the `kind` field. `Unit` carries no
|
||||
/// payload and serializes as `{"kind":"unit"}`.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "kind", rename_all = "lowercase")]
|
||||
pub enum Literal {
|
||||
/// Signed 64-bit integer.
|
||||
Int { value: i64 },
|
||||
/// Boolean.
|
||||
Bool { value: bool },
|
||||
/// UTF-8 string.
|
||||
Str { value: String },
|
||||
/// The unit value `()`.
|
||||
Unit,
|
||||
}
|
||||
|
||||
/// A type expression.
|
||||
///
|
||||
/// The JSON discriminator is the `k` field. The four variants are
|
||||
/// type constructor application ([`Type::Con`]), function type
|
||||
/// ([`Type::Fn`]), type variable ([`Type::Var`]), and universal
|
||||
/// quantifier ([`Type::Forall`] — only valid at the top level of an
|
||||
/// [`FnDef`] / [`ConstDef`] type).
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "k", rename_all = "lowercase")]
|
||||
pub enum Type {
|
||||
/// Type-constructor application: `Int`, `Bool`, user ADT names,
|
||||
/// and parameterised forms like `List<Int>`.
|
||||
Con {
|
||||
name: String,
|
||||
/// Type arguments (Iter 13a). For pre-13a uses (`Int`, `Bool`,
|
||||
/// non-parameterised user ADTs) this stays empty and is skipped
|
||||
/// during serialization, so the canonical-JSON hash of every
|
||||
/// pre-existing module remains bit-identical.
|
||||
/// non-parameterised user ADTs) this stays empty and is
|
||||
/// **omitted** during serialization, so the canonical-JSON
|
||||
/// hash of every pre-existing module remains bit-identical.
|
||||
/// See the regression test in [`crate::hash`].
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
args: Vec<Type>,
|
||||
},
|
||||
/// Function type with optional effect annotation. `effects` is a
|
||||
/// set; equality compares it modulo order (see the [`PartialEq`]
|
||||
/// impl below).
|
||||
Fn {
|
||||
params: Vec<Type>,
|
||||
ret: Box<Type>,
|
||||
#[serde(default)]
|
||||
effects: Vec<String>,
|
||||
},
|
||||
/// Type variable. During checking, names with the prefix `$m`
|
||||
/// denote checker-internal metavariables; source-level names
|
||||
/// cannot collide because identifiers may not start with `$`.
|
||||
Var {
|
||||
name: String,
|
||||
},
|
||||
/// Universal quantifier (top-level polymorphism only). `body`
|
||||
/// is the quantified type; `vars` are the bound type-variable
|
||||
/// names, instantiated fresh at each use site.
|
||||
Forall {
|
||||
vars: Vec<String>,
|
||||
body: Box<Type>,
|
||||
@@ -218,15 +362,19 @@ pub enum Type {
|
||||
}
|
||||
|
||||
impl Type {
|
||||
/// Convenience constructor for `Int` (no args).
|
||||
pub fn int() -> Type {
|
||||
Type::Con { name: "Int".into(), args: vec![] }
|
||||
}
|
||||
/// Convenience constructor for `Bool` (no args).
|
||||
pub fn bool_() -> Type {
|
||||
Type::Con { name: "Bool".into(), args: vec![] }
|
||||
}
|
||||
/// Convenience constructor for `Unit` (no args).
|
||||
pub fn unit() -> Type {
|
||||
Type::Con { name: "Unit".into(), args: vec![] }
|
||||
}
|
||||
/// Convenience constructor for `Str` (no args).
|
||||
pub fn str_() -> Type {
|
||||
Type::Con { name: "Str".into(), args: vec![] }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user