Skip to main content

logicaffeine_kernel/interface/
repl.rs

1//! REPL for the Vernacular interface.
2//!
3//! Orchestrates command parsing and kernel execution.
4
5use super::command::Command;
6use super::command_parser::parse_command;
7use super::error::InterfaceError;
8use crate::prelude::StandardLibrary;
9use crate::{infer_type, normalize, Context, Term};
10
11/// The Vernacular REPL.
12///
13/// Maintains a kernel context and executes commands against it.
14pub struct Repl {
15    ctx: Context,
16}
17
18impl Repl {
19    /// Create a new REPL with the standard library loaded.
20    pub fn new() -> Self {
21        let mut ctx = Context::new();
22        StandardLibrary::register(&mut ctx);
23        Self { ctx }
24    }
25
26    /// Execute a command string.
27    ///
28    /// Returns the output string (for Check/Eval) or empty string (for Definition/Inductive).
29    pub fn execute(&mut self, input: &str) -> Result<String, InterfaceError> {
30        let cmd = parse_command(input)?;
31
32        match cmd {
33            Command::Definition { name, ty, body, is_hint } => {
34                // Type check the body
35                let inferred_ty = infer_type(&self.ctx, &body)?;
36
37                // Use provided type or inferred type
38                let ty = ty.unwrap_or(inferred_ty);
39
40                // Add definition to context
41                self.ctx.add_definition(name.clone(), ty, body);
42
43                // Register as hint if marked
44                if is_hint {
45                    self.ctx.add_hint(&name);
46                }
47
48                Ok(String::new()) // Silent success
49            }
50
51            Command::Check(term) => {
52                // Infer the type
53                let ty = infer_type(&self.ctx, &term)?;
54                Ok(format!("{} : {}", term, ty))
55            }
56
57            Command::Eval(term) => {
58                // Type check first (ensures well-typed)
59                let _ = infer_type(&self.ctx, &term)?;
60
61                // Normalize
62                let result = normalize(&self.ctx, &term);
63                Ok(format!("{}", result))
64            }
65
66            Command::Inductive {
67                name,
68                params,
69                sort,
70                constructors,
71            } => {
72                // Build polymorphic sort: Π(p1:T1). Π(p2:T2). ... Type
73                let poly_sort = build_polymorphic_sort(&params, sort);
74
75                // Register the inductive type with its polymorphic sort
76                self.ctx.add_inductive(&name, poly_sort);
77
78                // Register constructors with prepended parameters
79                for (ctor_name, ctor_ty) in constructors {
80                    // Prepend params to constructor type:
81                    // If ctor_ty = A -> List A -> List A
82                    // And params = [(A, Type)]
83                    // Result = Π(A:Type). A -> List A -> List A
84                    let poly_ctor_ty = build_polymorphic_constructor(&params, ctor_ty);
85                    self.ctx.add_constructor(&ctor_name, &name, poly_ctor_ty);
86                }
87
88                Ok(String::new()) // Silent success
89            }
90        }
91    }
92
93    /// Get a reference to the underlying context.
94    pub fn context(&self) -> &Context {
95        &self.ctx
96    }
97
98    /// Get a mutable reference to the underlying context.
99    pub fn context_mut(&mut self) -> &mut Context {
100        &mut self.ctx
101    }
102}
103
104impl Default for Repl {
105    fn default() -> Self {
106        Self::new()
107    }
108}
109
110/// Build a polymorphic sort from type parameters.
111///
112/// For params = [(A, Type), (B, Type)] and base_sort = Type,
113/// produces: Π(A:Type). Π(B:Type). Type
114fn build_polymorphic_sort(params: &[(String, Term)], base_sort: Term) -> Term {
115    // Fold right to build nested Pi types
116    params.iter().rev().fold(base_sort, |body, (name, ty)| {
117        Term::Pi {
118            param: name.clone(),
119            param_type: Box::new(ty.clone()),
120            body_type: Box::new(body),
121        }
122    })
123}
124
125/// Build a polymorphic constructor type by prepending parameters.
126///
127/// For params = [(A, Type)] and ctor_ty = A -> List A -> List A,
128/// produces: Π(A:Type). A -> List A -> List A
129///
130/// The kernel uses named variables (Var(String)), so we convert
131/// Global("A") to Var("A") for parameters.
132fn build_polymorphic_constructor(params: &[(String, Term)], ctor_ty: Term) -> Term {
133    if params.is_empty() {
134        return ctor_ty;
135    }
136
137    // Convert Global(param_name) to Var(param_name) for all parameters
138    let param_names: Vec<&str> = params.iter().map(|(n, _)| n.as_str()).collect();
139    let body = substitute_globals_with_vars(&ctor_ty, &param_names);
140
141    // Wrap with Pi bindings (fold right to build nested Pi)
142    params.iter().rev().fold(body, |body, (name, ty)| {
143        Term::Pi {
144            param: name.clone(),
145            param_type: Box::new(ty.clone()),
146            body_type: Box::new(body),
147        }
148    })
149}
150
151/// Convert Global(name) to Var(name) for names in the param list.
152/// This makes parameter references in constructor types into bound variables.
153fn substitute_globals_with_vars(term: &Term, param_names: &[&str]) -> Term {
154    match term {
155        Term::Global(n) if param_names.contains(&n.as_str()) => Term::Var(n.clone()),
156        Term::Global(n) => Term::Global(n.clone()),
157        Term::Var(n) => Term::Var(n.clone()),
158        Term::Sort(u) => Term::Sort(u.clone()),
159        Term::Lit(l) => Term::Lit(l.clone()),
160        Term::App(f, a) => Term::App(
161            Box::new(substitute_globals_with_vars(f, param_names)),
162            Box::new(substitute_globals_with_vars(a, param_names)),
163        ),
164        Term::Lambda { param, param_type, body } => Term::Lambda {
165            param: param.clone(),
166            param_type: Box::new(substitute_globals_with_vars(param_type, param_names)),
167            body: Box::new(substitute_globals_with_vars(body, param_names)),
168        },
169        Term::Pi { param, param_type, body_type } => Term::Pi {
170            param: param.clone(),
171            param_type: Box::new(substitute_globals_with_vars(param_type, param_names)),
172            body_type: Box::new(substitute_globals_with_vars(body_type, param_names)),
173        },
174        Term::Fix { name, body } => Term::Fix {
175            name: name.clone(),
176            body: Box::new(substitute_globals_with_vars(body, param_names)),
177        },
178        Term::Match { discriminant, motive, cases } => Term::Match {
179            discriminant: Box::new(substitute_globals_with_vars(discriminant, param_names)),
180            motive: Box::new(substitute_globals_with_vars(motive, param_names)),
181            cases: cases
182                .iter()
183                .map(|c| substitute_globals_with_vars(c, param_names))
184                .collect(),
185        },
186        Term::Hole => Term::Hole, // Holes are unchanged
187    }
188}