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:
+138
-3
@@ -75,6 +75,62 @@ impl CompilationResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// Documentation entry for a single RTL symbol (function or constant).
|
||||
/// Populated via the builder returned by `register_native_fn` / `register_constant`.
|
||||
pub struct RtlDocEntry {
|
||||
pub name: String,
|
||||
/// Short one-line description (required).
|
||||
pub one_liner: &'static str,
|
||||
/// Optional longer explanation.
|
||||
pub description: Option<&'static str>,
|
||||
/// Optional Myc code examples.
|
||||
pub examples: Option<&'static [&'static str]>,
|
||||
}
|
||||
|
||||
/// Builder returned by `register_native_fn` and `register_constant`.
|
||||
/// Call `.doc(...)` to attach documentation; optional `.description(...)` and `.examples(...)`
|
||||
/// can be chained. The entry is written to the registry when the builder is dropped.
|
||||
pub struct RtlRegistration {
|
||||
name: String,
|
||||
rtl_docs: Rc<RefCell<Vec<RtlDocEntry>>>,
|
||||
one_liner: Option<&'static str>,
|
||||
description: Option<&'static str>,
|
||||
examples: Option<&'static [&'static str]>,
|
||||
}
|
||||
|
||||
impl RtlRegistration {
|
||||
/// Attach a required one-line description.
|
||||
pub fn doc(mut self, one_liner: &'static str) -> Self {
|
||||
self.one_liner = Some(one_liner);
|
||||
self
|
||||
}
|
||||
|
||||
/// Attach an optional longer description (must call `.doc()` first).
|
||||
pub fn description(mut self, desc: &'static str) -> Self {
|
||||
self.description = Some(desc);
|
||||
self
|
||||
}
|
||||
|
||||
/// Attach optional usage examples as Myc code strings.
|
||||
pub fn examples(mut self, ex: &'static [&'static str]) -> Self {
|
||||
self.examples = Some(ex);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RtlRegistration {
|
||||
fn drop(&mut self) {
|
||||
if let Some(one_liner) = self.one_liner {
|
||||
self.rtl_docs.borrow_mut().push(RtlDocEntry {
|
||||
name: self.name.clone(),
|
||||
one_liner,
|
||||
description: self.description,
|
||||
examples: self.examples,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Environment {
|
||||
pub root_types: Rc<RefCell<Vec<StaticType>>>,
|
||||
pub root_purity: Rc<RefCell<Vec<Purity>>>,
|
||||
@@ -91,6 +147,7 @@ pub struct Environment {
|
||||
pub pipeline_generators: Rc<RefCell<Vec<PipelineGenerator>>>,
|
||||
pub search_paths: Rc<RefCell<Vec<PathBuf>>>,
|
||||
pub loaded_modules: Rc<RefCell<HashSet<PathBuf>>>,
|
||||
pub rtl_docs: Rc<RefCell<Vec<RtlDocEntry>>>,
|
||||
}
|
||||
|
||||
struct EnvFunctionRegistry {
|
||||
@@ -177,6 +234,7 @@ impl Environment {
|
||||
pipeline_generators: Rc::new(RefCell::new(Vec::new())),
|
||||
search_paths: Rc::new(RefCell::new(Vec::new())),
|
||||
loaded_modules: Rc::new(RefCell::new(HashSet::new())),
|
||||
rtl_docs: Rc::new(RefCell::new(Vec::new())),
|
||||
};
|
||||
rtl::register(&env);
|
||||
|
||||
@@ -560,7 +618,7 @@ impl Environment {
|
||||
ty: StaticType,
|
||||
purity_level: Purity,
|
||||
func: impl Fn(&[Value]) -> Value + 'static,
|
||||
) {
|
||||
) -> RtlRegistration {
|
||||
self.register_native(
|
||||
name,
|
||||
ty,
|
||||
@@ -569,9 +627,16 @@ impl Environment {
|
||||
purity: purity_level,
|
||||
}),
|
||||
);
|
||||
RtlRegistration {
|
||||
name: name.to_string(),
|
||||
rtl_docs: Rc::clone(&self.rtl_docs),
|
||||
one_liner: None,
|
||||
description: None,
|
||||
examples: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_constant(&self, name: &str, ty: StaticType, val: Value) {
|
||||
pub fn register_constant(&self, name: &str, ty: StaticType, val: Value) -> RtlRegistration {
|
||||
let mut types = self.root_types.borrow_mut();
|
||||
let mut values = self.root_values.borrow_mut();
|
||||
let mut purity = self.root_purity.borrow_mut();
|
||||
@@ -581,7 +646,7 @@ impl Environment {
|
||||
// populate root_scopes[0]
|
||||
let mut root_scopes = self.root_scopes.borrow_mut();
|
||||
let mut slot_count = self.root_slot_count.borrow_mut();
|
||||
|
||||
|
||||
let global_idx = GlobalIdx(*slot_count);
|
||||
root_scopes[0].locals.insert(
|
||||
Symbol::from(name),
|
||||
@@ -597,6 +662,14 @@ impl Environment {
|
||||
types.push(ty);
|
||||
purity.push(Purity::Pure);
|
||||
values.push(val);
|
||||
|
||||
RtlRegistration {
|
||||
name: name.to_string(),
|
||||
rtl_docs: Rc::clone(&self.rtl_docs),
|
||||
one_liner: None,
|
||||
description: None,
|
||||
examples: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -613,6 +686,68 @@ impl Environment {
|
||||
names
|
||||
}
|
||||
|
||||
/// Returns formatted documentation for all RTL symbols that have been annotated
|
||||
/// via the `.doc()` builder. Each entry includes the name, type signature
|
||||
/// (derived from `StaticType`), one-liner, optional description, and examples.
|
||||
pub fn list_rtl_docs(&self) -> Vec<String> {
|
||||
use crate::ast::nodes::Symbol;
|
||||
let docs = self.rtl_docs.borrow();
|
||||
let root_scopes = self.root_scopes.borrow();
|
||||
let scope = &root_scopes[0];
|
||||
|
||||
let mut entries: Vec<String> = docs.iter().map(|entry| {
|
||||
// Derive the signature from the registered StaticType.
|
||||
let sig = scope
|
||||
.locals
|
||||
.get(&Symbol::from(entry.name.as_str()))
|
||||
.map(|info| info._ty.to_doc_string())
|
||||
.unwrap_or_else(|| "unknown".to_string());
|
||||
|
||||
let mut out = format!("{} : {}\n {}", entry.name, sig, entry.one_liner);
|
||||
|
||||
if let Some(desc) = entry.description {
|
||||
out.push_str(&format!("\n {}", desc));
|
||||
}
|
||||
if let Some(examples) = entry.examples {
|
||||
out.push_str("\n Examples:");
|
||||
for ex in examples.iter() {
|
||||
out.push_str(&format!("\n {}", ex));
|
||||
}
|
||||
}
|
||||
out
|
||||
}).collect();
|
||||
|
||||
entries.sort_by_key(|a| a.to_lowercase());
|
||||
entries
|
||||
}
|
||||
|
||||
/// Returns formatted documentation for a single RTL symbol by name,
|
||||
/// or `None` if the symbol has no doc entry.
|
||||
pub fn get_rtl_doc(&self, name: &str) -> Option<String> {
|
||||
use crate::ast::nodes::Symbol;
|
||||
let docs = self.rtl_docs.borrow();
|
||||
let entry = docs.iter().find(|e| e.name == name)?;
|
||||
|
||||
let root_scopes = self.root_scopes.borrow();
|
||||
let sig = root_scopes[0]
|
||||
.locals
|
||||
.get(&Symbol::from(name))
|
||||
.map(|info| info._ty.to_doc_string())
|
||||
.unwrap_or_else(|| "unknown".to_string());
|
||||
|
||||
let mut out = format!("{} : {}\n {}", entry.name, sig, entry.one_liner);
|
||||
if let Some(desc) = entry.description {
|
||||
out.push_str(&format!("\n {}", desc));
|
||||
}
|
||||
if let Some(examples) = entry.examples {
|
||||
out.push_str("\n Examples:");
|
||||
for ex in examples.iter() {
|
||||
out.push_str(&format!("\n {}", ex));
|
||||
}
|
||||
}
|
||||
Some(out)
|
||||
}
|
||||
|
||||
pub fn dump_ast(&self, source: &str) -> Result<String, String> {
|
||||
self.preload_dependencies(source, None)?;
|
||||
let compiled = self.compile(source).into_result()?;
|
||||
|
||||
Reference in New Issue
Block a user