const PREAMBLE: &str = include_str!("../agent-preamble.md");
const DIAGNOSTICS: &str = include_str!("../diagnostics.md");
pub fn render() -> String {
let mut out = String::with_capacity(32 * 1024);
out.push_str("# Aura ");
out.push_str(env!("CARGO_PKG_VERSION"));
out.push_str(" — language reference for coding assistants\n\n");
out.push_str(
"Generated by `aura docs --agent` from the compiler's own definitions, so it \
describes the binary that produced it. Regenerate rather than edit.\n\n",
);
out.push_str(PREAMBLE.trim_end());
out.push_str("\n\n## Keywords\n\nAll ");
out.push_str(&crate::lexer::token::KEYWORDS.len().to_string());
out.push_str(" reserved words:\n\n");
out.push_str(&crate::lexer::token::KEYWORDS.join(", "));
out.push_str(
".\n\nPlus `text`, which is *not* reserved: it opens a block string only \
where a value is expected, and is an ordinary identifier everywhere \
else.\n",
);
out.push_str("\n## Standard library\n\n");
out.push_str(
"Methods are called on a value (`\"a\".upper()`, `xs.map(f)`); builtins are \
global functions.\n\n",
);
out.push_str(&stdlib_section());
out.push_str("\n## Diagnostics\n\n");
out.push_str(
"Every code is stable. When one is reported, the fix is in this table rather \
than in guesswork.\n\n",
);
out.push_str(strip_front_matter(DIAGNOSTICS).trim_end());
out.push('\n');
out
}
fn stdlib_section() -> String {
let Ok(json) = eval_manifest(crate::STDLIB_MANIFEST) else {
return "_The stdlib manifest failed to evaluate; see crates/aura-lang/stdlib.aura._\n"
.to_string();
};
let Some(groups) = json.as_object() else {
return String::new();
};
let mut out = String::new();
for (receiver, methods) in groups {
let Some(methods) = methods.as_object() else {
continue;
};
out.push_str("### ");
out.push_str(if receiver == "builtins" {
"Builtins"
} else {
receiver
});
out.push_str("\n\n| Signature | |\n| --- | --- |\n");
for (name, spec) in methods {
let params = spec
.get("params")
.and_then(|p| p.as_array())
.map(|xs| {
xs.iter()
.filter_map(|v| v.as_str())
.collect::<Vec<_>>()
.join(", ")
})
.unwrap_or_default();
let returns = spec.get("returns").and_then(|v| v.as_str()).unwrap_or("?");
let doc = spec.get("doc").and_then(|v| v.as_str()).unwrap_or("");
out.push_str(&format!("| `{name}({params}) -> {returns}` | {doc} |\n"));
}
out.push('\n');
}
out
}
fn eval_manifest(src: &str) -> Result<serde_json::Value, ()> {
let tokens = crate::lexer::Lexer::new(src, 0)
.tokenize()
.map_err(|_| ())?;
let module = crate::parser::Parser::new(tokens)
.parse_module()
.map_err(|_| ())?;
let mut interp = crate::eval::Interpreter::new(crate::eval::Options {
strict: false,
dry_run: false,
});
let value = interp.eval_module(&module).map_err(|_| ())?;
crate::serialize::to_json(&value).map_err(|_| ())
}
fn strip_front_matter(md: &str) -> String {
let body = md
.split_once("\n## ")
.map_or(md, |(_, rest)| rest)
.to_string();
let mut out = String::from("### ");
out.push_str(&body.replace("\n## ", "\n### "));
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_reference_carries_every_section_it_claims() {
let doc = render();
for needle in [
"## Keywords",
"## Standard library",
"## Diagnostics",
"### Builtins",
"`:` exports, `=` does not",
] {
assert!(doc.contains(needle), "missing from the reference: {needle}");
}
}
#[test]
fn the_stdlib_section_is_populated_from_the_manifest() {
let doc = render();
assert!(
!doc.contains("failed to evaluate"),
"the stdlib manifest did not evaluate"
);
for needle in ["`upper() -> String`", "`map(fn) -> List`", "### String"] {
assert!(
doc.contains(needle),
"expected {needle} in the stdlib tables"
);
}
}
#[test]
fn every_reserved_word_is_listed() {
let doc = render();
for kw in crate::lexer::token::KEYWORDS {
assert!(doc.contains(kw), "keyword absent from the reference: {kw}");
}
}
}