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}