Add documentation infrastructure for RTL symbols

Introduces `RtlDocEntry` and `RtlRegistration` structs to store and
manage documentation for native functions and constants. The
`Environment` struct is updated to include a registry for these
documentation entries.

The `register_native_fn` and `register_constant` methods now return an
`RtlRegistration` builder, allowing for optional chaining of `.doc()`,
`.description()`, and `.examples()` methods before the registration is
finalized when the builder is dropped.

Macros `rtl_fn!` and `rtl_const!` are introduced to simplify the
registration process with documentation.

The MCP server is extended with `get_rtl_docs` and `get_symbol_doc`
tools to expose this documentation externally.

Specific RTL functions and constants (e.g., `+`, `-`, `*`, `/`, `NaN`,
`true`, `false`, `date`, `now`, math functions, `series`, `push`, `len`,
`create-random-ohlc`, `create-ticker`, `pipe`) have been updated to
include their respective documentation using the new infrastructure.

The `Signature` and `StaticType` types have gained `to_doc_string`
methods for generating human-readable documentation strings.
This commit is contained in:
2026-03-25 15:02:19 +01:00
parent 446bdcd42d
commit b436e09840
9 changed files with 400 additions and 69 deletions
+36 -36
View File
@@ -6,8 +6,10 @@ use std::rc::Rc;
pub fn register(env: &Environment) {
// Constants
env.register_constant("PI", StaticType::Float, Value::Float(consts::PI));
env.register_constant("E", StaticType::Float, Value::Float(consts::E));
env.register_constant("PI", StaticType::Float, Value::Float(consts::PI))
.doc("Mathematical constant π ≈ 3.14159265.");
env.register_constant("E", StaticType::Float, Value::Float(consts::E))
.doc("Mathematical constant e ≈ 2.71828182 (Euler's number).");
// Helper to get f64 from Value (Int or Float)
fn to_f64(v: &Value) -> Option<f64> {
@@ -19,7 +21,7 @@ pub fn register(env: &Environment) {
}
// Unary functions (float -> float)
let register_unary = |name: &'static str, f: fn(f64) -> f64| {
let register_unary = |name: &'static str, f: fn(f64) -> f64, doc: &'static str| {
let ty = StaticType::Function(Box::new(Signature {
params: StaticType::Tuple(vec![StaticType::Float]),
ret: StaticType::Float,
@@ -30,11 +32,11 @@ pub fn register(env: &Environment) {
} else {
Value::Void
}
});
}).doc(doc);
};
// Unary functions (float -> int)
let register_to_int = |name: &'static str, f: fn(f64) -> f64| {
let register_to_int = |name: &'static str, f: fn(f64) -> f64, doc: &'static str| {
let ty = StaticType::Function(Box::new(Signature {
params: StaticType::Tuple(vec![StaticType::Float]),
ret: StaticType::Int,
@@ -45,29 +47,29 @@ pub fn register(env: &Environment) {
} else {
Value::Void
}
});
}).doc(doc);
};
register_unary("sqrt", f64::sqrt);
register_unary("sin", f64::sin);
register_unary("cos", f64::cos);
register_unary("tan", f64::tan);
register_unary("asin", f64::asin);
register_unary("acos", f64::acos);
register_unary("atan", f64::atan);
register_unary("exp", f64::exp);
register_unary("ln", f64::ln);
register_unary("log10", f64::log10);
register_unary("fract", f64::fract);
register_unary("sqrt", f64::sqrt, "Returns the square root of x.");
register_unary("sin", f64::sin, "Returns the sine of x (radians).");
register_unary("cos", f64::cos, "Returns the cosine of x (radians).");
register_unary("tan", f64::tan, "Returns the tangent of x (radians).");
register_unary("asin", f64::asin, "Returns the arcsine of x in radians.");
register_unary("acos", f64::acos, "Returns the arccosine of x in radians.");
register_unary("atan", f64::atan, "Returns the arctangent of x in radians.");
register_unary("exp", f64::exp, "Returns e raised to the power of x.");
register_unary("ln", f64::ln, "Returns the natural logarithm of x.");
register_unary("log10",f64::log10,"Returns the base-10 logarithm of x.");
register_unary("fract",f64::fract,"Returns the fractional part of x.");
// Rounding functions returning Int
register_to_int("round", f64::round);
register_to_int("floor", f64::floor);
register_to_int("ceil", f64::ceil);
register_to_int("trunc", f64::trunc);
register_to_int("round", f64::round, "Rounds x to the nearest integer.");
register_to_int("floor", f64::floor, "Rounds x down to the largest integer ≤ x.");
register_to_int("ceil", f64::ceil, "Rounds x up to the smallest integer ≥ x.");
register_to_int("trunc", f64::trunc, "Truncates x towards zero.");
// Binary functions (float, float -> float)
let register_binary = |name: &'static str, f: fn(f64, f64) -> f64| {
let register_binary = |name: &'static str, f: fn(f64, f64) -> f64, doc: &'static str| {
let ty = StaticType::Function(Box::new(Signature {
params: StaticType::Tuple(vec![StaticType::Float, StaticType::Float]),
ret: StaticType::Float,
@@ -78,12 +80,12 @@ pub fn register(env: &Environment) {
} else {
Value::Void
}
});
}).doc(doc);
};
register_binary("atan2", f64::atan2);
register_binary("pow", f64::powf);
register_binary("log", f64::log);
register_binary("atan2", f64::atan2, "Returns the arctangent of y/x, using signs to determine the quadrant.");
register_binary("pow", f64::powf, "Returns x raised to the power y.");
register_binary("log", f64::log, "Returns the logarithm of x in the given base: (log base x).");
// Specialized abs to handle Int -> Int, Float -> Float
let abs_ty = StaticType::Function(Box::new(Signature {
@@ -99,7 +101,8 @@ pub fn register(env: &Environment) {
Value::Float(f) => Value::Float(f.abs()),
_ => Value::Void,
}
});
}).doc("Returns the absolute value of a number. Works for both int and float.")
.examples(&["(abs -5)", "(abs -3.14)"]);
// Variadic min / max
let variadic_ty = StaticType::Function(Box::new(Signature {
@@ -120,7 +123,7 @@ pub fn register(env: &Environment) {
}
}
best
});
}).doc("Returns the minimum of its arguments. Variadic: (min 3 1 2) => 1.");
env.register_native_fn("max", variadic_ty, Purity::Pure, |args| {
if args.is_empty() {
@@ -135,7 +138,7 @@ pub fn register(env: &Environment) {
}
}
best
});
}).doc("Returns the maximum of its arguments. Variadic: (max 3 1 2) => 3.");
// Random generator factory (Closure-based)
let generator_sig = Signature {
@@ -154,11 +157,7 @@ pub fn register(env: &Environment) {
},
]);
env.register_native_fn(
"make-random",
make_random_ty,
Purity::SideEffectFree,
|args| {
env.register_native_fn("make-random", make_random_ty, Purity::SideEffectFree, |args| {
let rng = if args.is_empty() {
fastrand::Rng::new()
} else if let Value::Int(s) = args[0] {
@@ -173,6 +172,7 @@ pub fn register(env: &Environment) {
func: Rc::new(move |_args| Value::Float(prng.borrow_mut().f64())),
purity: Purity::Impure,
}))
},
);
}).doc("Creates a random number generator function returning floats in [0, 1).")
.description("Optionally seeded: (make-random seed) for reproducible sequences.")
.examples(&["(def rng (make-random 42))", "(rng)"]);
}