axon-frontend 3.8.0

AXON compiler frontend - lexer, parser, AST, epistemic type system, type checker, IR generator. Zero runtime dependencies. Ships the two judgments the runtime shares verbatim: `stability` (the `mandate` gain band D < |Kp+Ki+Kd| < 1/L, both endpoints exclusive, verified at compile time against declared bounds) and `substrate` (the `fabric` provider/region/jurisdiction catalog behind axon-E041 region mismatch and axon-E042 compliance-jurisdiction). It also accepts the step-body statement positions the language reference has always published: mandate/shield/ots/lambda applications scoped to the step they govern, the PIX verbs with a braceless field list, and `hibernate until <event>`. axon-T957 RegulatedBoundaryCoverage and axon-T1215 channel κ-coverage guard every regulated boundary. See https://www.ricardovelit.com/axon-docs
Documentation
//! v4.8.0 — reading the surfaces this project publishes, once.
//!
//! Two artifacts teach AXON to somebody: `docs/`, which a person reads, and
//! `src/axon-emcp/knowledge/`, which an AI coding agent reads over MCP. Both are
//! markdown with fenced blocks, both are build inputs, and both have laws over
//! them — that every ` ```axon ` block compiles, that the diagnostics they name
//! exist, that the lawful bases they teach are in the catalogue.
//!
//! Those laws live in two different crates' test suites, and until this module
//! they each carried their own copy of "what is a fence" and "does this block
//! declare something". Two copies of a predicate is two things that drift, which
//! is the defect this whole cycle has been closing everywhere else; it would be
//! odd to keep one here.
//!
//! It cost something already. [`declares`] began as "a line starting with a
//! declaration keyword", which matches **English**: a doctrine page carries a
//! fenced error message reading *"…the projected type expects `receive
//! Decision`…"*, and a law that reads that as a declaration fails for a reason
//! with nothing to do with the corpus. The Rust gate was tightened; the Python
//! used to scope the work was not, and reported the same false positive a second
//! time from the other side. One definition, in the crate both gates already
//! depend on.

/// One fenced block: its language tag, its body, and the 1-based line it opens on.
#[derive(Debug, Clone)]
pub struct Fence {
    /// The word after the backticks — `axon`, `text`, or empty.
    pub lang: String,
    /// The block's contents, newline-terminated per line.
    pub body: String,
    /// 1-based line of the opening ``` in the source file.
    pub line: usize,
}

/// Every fenced block in `markdown`, in source order.
pub fn fences(markdown: &str) -> Vec<Fence> {
    let mut out = Vec::new();
    let mut lines = markdown.lines().enumerate();
    while let Some((i, line)) = lines.next() {
        let t = line.trim_end();
        if !t.starts_with("```") {
            continue;
        }
        let lang = t.trim_start_matches('`').trim().to_string();
        let mut body = String::new();
        for (_, l) in lines.by_ref() {
            if l.trim_end().starts_with("```") {
                break;
            }
            body.push_str(l);
            body.push('\n');
        }
        out.push(Fence { lang, body, line: i + 1 });
    }
    out
}

/// The top-level declaration keywords a published block can open with.
pub const DECLARATION_HEADS: &[&str] = &[
    "type", "axonstore", "axonendpoint", "axpoint", "flow", "shield", "socket",
    "tool", "fabric", "resource", "manifest", "mandate", "persona", "context",
    "daemon", "corpus", "agent", "session", "channel", "effect", "observable",
    "scope", "memory", "anchor",
];

/// Whether `body` opens an AXON top-level declaration.
///
/// A keyword list rather than "does it parse", deliberately: a block that fails
/// to parse is the thing being looked for, so parsing cannot be the filter.
///
/// The shape is `<head> <Name>` followed by `{`, `(`, or the end of the line —
/// **not** merely a line beginning with the word. Without that second half this
/// matches ordinary English, and the false positive lands on exactly the pages
/// that discuss the language.
pub fn declares(body: &str) -> bool {
    body.lines().any(|l| {
        let l = l.trim_start();
        DECLARATION_HEADS.iter().any(|h| {
            let Some(rest) = l.strip_prefix(h) else { return false };
            let trimmed = rest.trim_start();
            if trimmed.len() == rest.len() {
                return false; // no separator: `typescript`, not `type X`
            }
            let Some(first) = trimmed.chars().next() else { return false };
            if !(first.is_ascii_alphabetic() || first == '_' || first == '<') {
                return false;
            }
            let tail: String = trimmed
                .chars()
                .skip_while(|c| c.is_alphanumeric() || *c == '_' || *c == '<' || *c == '>')
                .collect();
            let tail = tail.trim_start();
            tail.starts_with('{') || tail.starts_with('(') || tail.is_empty()
        })
    })
}

/// Rewrite `#` comments as `//`, leaving `#` inside string literals alone.
///
/// AXON comments with `//`. A `#` is an unexpected character and the block does
/// not even LEX — which means a law that checks a body without normalising sees
/// a lexer error and can say nothing about whether the block was a program, a
/// fragment or a sketch. Both published surfaces carried `#` in dozens of
/// blocks, so this is not an edge case; it is the first thing to do to a body
/// before asking the compiler anything about it.
pub fn normalise_comments(body: &str) -> String {
    let mut out = String::with_capacity(body.len());
    for line in body.lines() {
        match hash_outside_a_string(line) {
            Some(j) => {
                out.push_str(&line[..j]);
                out.push_str("//");
                out.push_str(&line[j + 1..]);
            }
            None => out.push_str(line),
        }
        out.push('\n');
    }
    out
}

/// The byte offset of the first `#` on `line` that is not inside a string
/// literal, if any.
///
/// The string check is the whole of it, and it is not pedantry: `route:
/// "/v1/#anchor"` carries a `#` that is DATA, and a scan that could not tell
/// the two apart would either miss the comments or refuse the data.
///
/// Shared so that the rewriter above and the law that forbids its input cannot
/// disagree about what a `#` comment is. Two copies of a predicate is two
/// things that drift.
pub fn hash_outside_a_string(line: &str) -> Option<usize> {
    let mut in_string = false;
    for (j, ch) in line.char_indices() {
        match ch {
            '"' => in_string = !in_string,
            '#' if !in_string => return Some(j),
            _ => {}
        }
    }
    None
}

/// Why a block that declares something is NOT a program.
///
/// A published block is either a program — and then it compiles — or an excerpt,
/// and then it says which kind. The kinds are closed on purpose: a body that is
/// none of them is a defect, and a defect belongs under the law where it fails
/// loudly rather than in a list of things allowed to fail.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExcerptKind {
    /// `<Name>`, `<a|b>` — a shape, not a program. `<Name>` never compiles and
    /// is not supposed to.
    Sketch,
    /// `{{var}}` — a prompt scaffold, filled in at serve time.
    Template,
    /// `…` — the author cut the middle out.
    Elision,
    /// It names what a neighbouring block declares.
    Fragment,
    /// It is shown in order to be REFUSED — the page teaches the diagnostic.
    CounterExample,
    /// It is more than one FILE, shown inline under `// ── File: … ` banners.
    /// No single-file compile can accept it, and that is a property of the
    /// block rather than a defect in it.
    ///
    /// One page uses this today. The kind is about what the block IS, not about
    /// how many pages do it — forcing a two-file illustration into `Fragment`
    /// would put a false label in the register that exists so labels are true.
    MultiFile,
}

impl ExcerptKind {
    /// The slug written in a pin file.
    pub fn slug(self) -> &'static str {
        match self {
            ExcerptKind::Sketch => "sketch",
            ExcerptKind::Template => "template",
            ExcerptKind::Elision => "elision",
            ExcerptKind::Fragment => "fragment",
            ExcerptKind::CounterExample => "counter_example",
            ExcerptKind::MultiFile => "multi_file",
        }
    }
}

/// Classify a non-compiling block, or `None` when it is simply broken.
///
/// `diagnostic` is the compiler's output for the block — the only way to tell a
/// fragment (it names something declared next door) from a defect.
pub fn excerpt_kind(body: &str, diagnostic: &str) -> Option<ExcerptKind> {
    if body.contains("── File:") {
        return Some(ExcerptKind::MultiFile);
    }
    if body.contains("{{") {
        return Some(ExcerptKind::Template);
    }
    // A placeholder or an alternation is a grammar sketch. Checked before the
    // rest because a sketch fails in whatever way its placeholders happen to,
    // and the failure says nothing about the page.
    if has_placeholder(body) {
        return Some(ExcerptKind::Sketch);
    }
    if body.contains('') || body.contains("...") {
        return Some(ExcerptKind::Elision);
    }
    let low = diagnostic.to_ascii_lowercase();
    // A page may show a value being REFUSED — that teaching is the whole point
    // of a closed vocabulary, and a law forbidding it would forbid documenting
    // the guarantee. The block must be one the compiler names, not merely one
    // that fails.
    if low.contains("axon-t1214") || low.contains("is not a regulatory class") {
        return Some(ExcerptKind::CounterExample);
    }
    // "not found" carries the module case: an `import` example names a module
    // that lives on disk beside the page rather than inside the block.
    if ["undefined", "not declared", "does not resolve", "unknown", "not found"]
        .iter()
        .any(|w| low.contains(w))
    {
        return Some(ExcerptKind::Fragment);
    }
    None
}

/// `<Name>` / `<a|b>` — a placeholder, not a type parameter.
///
/// `Stream<Token>` and `FlowEnvelope<T>` are real types and must not read as
/// sketches, so the contents have to look like prose or an alternation: a space,
/// a pipe, or a lowercase-leading word that is not a known type parameter.
fn has_placeholder(body: &str) -> bool {
    let bytes: Vec<char> = body.chars().collect();
    let mut i = 0;
    while i < bytes.len() {
        if bytes[i] != '<' {
            i += 1;
            continue;
        }
        let Some(close) = bytes[i + 1..].iter().position(|c| *c == '>') else { break };
        let inner: String = bytes[i + 1..i + 1 + close].iter().collect();
        i += close + 2;
        if inner.is_empty() {
            continue;
        }
        let looks_like_a_type = inner
            .chars()
            .next()
            .is_some_and(|c| c.is_ascii_uppercase())
            && inner.chars().all(|c| c.is_alphanumeric() || c == '_');
        if !looks_like_a_type {
            return true;
        }
    }
    // `method: <GET|POST>` style alternation outside angle brackets.
    body.lines().any(|l| {
        let l = l.trim();
        l.contains('|') && !l.starts_with("//") && !l.starts_with('|')
    })
}