Skip to main content

elly_core/
lib.rs

1//! Elly — a small language layered on Muon (see `docs/elly-spec.md`).
2//!
3//! This crate implements a subset including references, application, `&` abstraction,
4//! symbols, and positional lists with projection. It uses the `muon` crate for
5//! syntax, parsing into an [`Expr`] which is then evaluated by a tree-walking
6//! [`eval`](fn@eval)uator.
7//!
8//! ```
9//! let expr = elly_core::parse("(&x x) .foo").unwrap();
10//! assert_eq!(elly_core::eval(&expr).unwrap().to_display(), ".foo");
11//! ```
12
13#![no_std]
14
15extern crate alloc;
16#[cfg(feature = "stats-env")]
17extern crate std;
18
19mod ast;
20mod builtins;
21mod eval;
22mod eval_env;
23mod eval_match;
24mod load;
25mod module;
26mod parse;
27mod resolve;
28mod value;
29
30pub use ast::{Expr, HomeLeaf, ModuleData, Pattern, ProtoRef, Text, TyKind};
31pub use eval::{
32    apply, apply_with, eval, eval_env, eval_main, instantiate, Builtin, Ctx, Env, EnvNode, EnvRef,
33    HostCall, HostFn, ObjectData, Raised, Value,
34};
35#[cfg(feature = "stats-env")]
36pub use eval_env::stats_env;
37pub use load::{load_module, load_module_source, Instances, LoadError, ModuleResolver, Resolved};
38pub use module::{compile_module_framed, compile_module_named, ImportDecl, ImportSpec, ModuleName};
39pub use parse::{is_bindable_name, parse_module, parse_program, ParseError};
40pub use resolve::{resolve, resolve_module_bodies, resolve_open, resolve_prelude};
41
42use alloc::rc::Rc;
43
44// TODO: use thiserror?
45// TODO: errors with spans
46/// A failure at the syntax or parse stage.
47#[derive(Debug, Clone, PartialEq, Eq)]
48pub enum Error {
49    /// The syntax layer (Muon) rejected the input.
50    Syntax(muon::ParseError),
51    /// The input is a valid Muon tree but not a valid Elly program (this subset).
52    Parse(ParseError),
53}
54
55/// Parse Elly source into an [`Expr`] with name resolution.
56/// Free variables cause [`ParseError::UnboundName`].
57pub fn parse(src: &str) -> Result<Expr, Error> {
58    let mut expr = parse_open(src)?;
59    resolve(&mut expr).map_err(Error::Parse)?;
60    Ok(expr)
61}
62
63/// Parse Elly source into an [`Expr`] without resolution (open term).
64/// Used by tooling and the parse golden suite. For evaluation, prefer [`parse`].
65pub fn parse_open(src: &str) -> Result<Expr, Error> {
66    let seq = muon::parse(src).map_err(Error::Syntax)?;
67    parse_program(&seq).map_err(Error::Parse)
68}
69
70/// Compile Elly module source into [`ModuleData`].
71///
72/// This is purely syntactic: bodies are resolved against the module's item set
73/// and imports are mapped to leading frame slots. No bodies are evaluated.
74/// See `docs/done/2026-08-07_elly-modules-v0.md`.
75pub fn compile_module(src: &str) -> Result<Rc<ModuleData>, Error> {
76    compile_module_framed(src, &[])
77}
78
79/// Parse Elly source into an [`Expr`] resolved against a fixed outer frame (prelude).
80/// Unknown names cause [`ParseError::UnboundName`].
81pub fn parse_preluded(src: &str, prelude: &[Text]) -> Result<Expr, Error> {
82    let mut expr = parse_open(src)?;
83    resolve_prelude(&mut expr, prelude).map_err(Error::Parse)?;
84    Ok(expr)
85}