Skip to main content

xpile_frontend/
lib.rs

1//! Frontend trait.
2//!
3//! Every source language in xpile (Python, C, Ruchy, ...) provides
4//! one type implementing [`Frontend`]. The trait is intentionally
5//! narrow: parse a source file and lower it to canonical meta-HIR.
6//! Everything else — agent loop, oracle, codegen, MCP — is shared.
7
8use std::path::Path;
9use xpile_meta_hir::Module;
10
11#[derive(Debug, thiserror::Error)]
12pub enum FrontendError {
13    #[error("parse error: {0}")]
14    Parse(String),
15    #[error("lowering error: {0}")]
16    Lower(String),
17    #[error("io error: {0}")]
18    Io(#[from] std::io::Error),
19}
20
21pub trait Frontend: Send + Sync {
22    /// Human-readable language name, e.g. "python", "c", "ruchy".
23    fn name(&self) -> &'static str;
24
25    /// File extensions handled by this frontend, without leading dot.
26    fn extensions(&self) -> &[&'static str];
27
28    /// True when this frontend should claim `path`. PMAT-038
29    /// (XPILE-BASHRS-MERGER-001 follow-up): default impl preserves
30    /// the pre-existing extension-only routing — everything that
31    /// matched via `extensions()` keeps matching here. Frontends with
32    /// extensionless-filename idioms (`bashrs-frontend` for
33    /// `Makefile` / `Dockerfile`) override this method to extend the
34    /// match. Centralising routing here means dispatch sites can call
35    /// one method instead of duplicating the extension-lookup logic.
36    fn matches_path(&self, path: &Path) -> bool {
37        path.extension()
38            .and_then(|ext| ext.to_str())
39            .map(|ext_str| self.extensions().contains(&ext_str))
40            .unwrap_or(false)
41    }
42
43    /// Parse source and lower to meta-HIR.
44    fn parse_and_lower(&self, path: &Path, source: &str) -> Result<Module, FrontendError>;
45}