use std::fmt::{self, Debug}; use std::rc::Rc; use futures::FutureExt; use futures::future::{LocalBoxFuture, join_all}; use hashbrown::HashSet; use itertools::Itertools; use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants}; use orchid_base::interner::Tok; use orchid_base::location::SrcRange; use orchid_base::parse::{Comment, Import}; use orchid_base::tl_cache; use orchid_base::tree::{TokTree, Token, recur}; use crate::api; use crate::dealias::{ChildErrorKind, ChildResult, Tree}; use crate::expr::{Expr, ExprWillPanic}; use crate::expr_store::ExprStore; use crate::system::System; pub type ParsTokTree = TokTree; pub type ParsTok = Token; #[derive(Debug)] pub struct Item { pub sr: SrcRange, pub comments: Vec, pub kind: ItemKind, } impl Item { pub fn new(sr: SrcRange, kind: impl Into) -> Self { Self { sr, comments: Vec::new(), kind: kind.into() } } } #[derive(Debug)] pub enum ItemKind { Member(ParsedMember), Import(Import), } impl ItemKind { #[must_use] pub fn at(self, sr: SrcRange) -> Item { Item { comments: vec![], sr, kind: self } } } impl From for ItemKind { fn from(value: ParsedMember) -> Self { Self::Member(value) } } impl From for ItemKind { fn from(value: Import) -> Self { Self::Import(value) } } impl Format for Item { async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit { let comment_text = self.comments.iter().join("\n"); let item_text = match &self.kind { ItemKind::Import(i) => format!("import {i}").into(), ItemKind::Member(mem) => match &mem.kind { ParsedMemberKind::Const(_, sys) => tl_cache!(Rc: Rc::new(Variants::default().bounded("const {0} via {1}"))) .units([mem.name.rc().into(), sys.print(c).await]), ParsedMemberKind::Mod(module) => tl_cache!(Rc: Rc::new(Variants::default().bounded("module {0} {{\n\t{1}\n}}"))) .units([mem.name.rc().into(), module.print(c).boxed_local().await]), }, }; tl_cache!(Rc: Rc::new(Variants::default().bounded("{0}\n{1}"))) .units([comment_text.into(), item_text]) } } pub struct ParsedMember { pub name: IStr, pub exported: bool, pub kind: ParsedMemberKind, } impl ParsedMember { #[must_use] pub fn name(&self) -> IStr { self.name.clone() } pub fn new(exported: bool, name: IStr, kind: impl Into) -> Self { Self { exported, name, kind: kind.into() } } } impl Debug for ParsedMember { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Member") .field("name", &self.name) .field("kind", &self.kind) .finish_non_exhaustive() } } pub(crate) type ParsedExprCallback = Rc Fn(&'a [IStr]) -> LocalBoxFuture<'a, Expr>>; pub struct ParsedExpr { pub(crate) debug: String, pub(crate) callback: ParsedExprCallback, } impl ParsedExpr { pub async fn run(self, imported_names: &[IStr]) -> Expr { (self.callback)(imported_names).await } } impl fmt::Debug for ParsedExpr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.debug) } } #[derive(Debug)] pub enum ParsedMemberKind { Const(api::ParsedConstId, System), Mod(ParsedModule), } impl From for ParsedMemberKind { fn from(value: ParsedModule) -> Self { Self::Mod(value) } } #[derive(Debug, Default)] pub struct ParsedModule { pub exports: Vec, pub items: Vec, pub use_prelude: bool, } impl ParsedModule { #[must_use] pub fn new(use_prelude: bool, items: impl IntoIterator) -> Self { let items = items.into_iter().collect_vec(); let exports = (items.iter()) .filter_map(|i| if let ItemKind::Member(m) = &i.kind { Some(m) } else { None }) .filter(|m| m.exported) .map(|m| m.name.clone()) .collect_vec(); Self { exports, items, use_prelude } } pub fn merge(&mut self, other: ParsedModule) { let mut swap = ParsedModule::default(); std::mem::swap(self, &mut swap); assert_eq!(self.use_prelude, other.use_prelude, "merging modules that disagree on prelude"); *self = ParsedModule::new(self.use_prelude, swap.items.into_iter().chain(other.items)) } #[must_use] pub fn get_imports(&self) -> impl IntoIterator { (self.items.iter()) .filter_map(|it| if let ItemKind::Import(i) = &it.kind { Some(i) } else { None }) } pub fn default_item(self, name: IStr, sr: SrcRange) -> Item { let mem = ParsedMember { exported: true, name, kind: ParsedMemberKind::Mod(self) }; Item { comments: vec![], sr, kind: ItemKind::Member(mem) } } } impl Tree for ParsedModule { type Ctx<'a> = (); async fn child( &self, key: IStr, public_only: bool, (): &mut Self::Ctx<'_>, ) -> ChildResult<'_, Self> { if public_only && !self.exports.contains(&key) { return ChildResult::Err(ChildErrorKind::Private); } if let Some(member) = (self.items.iter()) .filter_map(|it| if let ItemKind::Member(m) = &it.kind { Some(m) } else { None }) .find(|m| m.name == key) { match &member.kind { ParsedMemberKind::Const(..) => return ChildResult::Err(ChildErrorKind::Constant), ParsedMemberKind::Mod(m) => return ChildResult::Ok(m), } } ChildResult::Err(ChildErrorKind::Missing) } fn children(&self, public_only: bool) -> HashSet { let mut public: HashSet<_> = self.exports.iter().cloned().collect(); if !public_only { public.extend( (self.items.iter()) .filter_map( |it| if let ItemKind::Member(mem) = &it.kind { Some(&mem.name) } else { None }, ) .cloned(), ) } public } } impl Format for ParsedModule { async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit { let head_str = format!("export ::({})\n", self.exports.iter().join(", ")); Variants::sequence(self.items.len() + 1, "\n", None).units( [head_str.into()].into_iter().chain(join_all(self.items.iter().map(|i| i.print(c))).await), ) } } /// Selects a code element /// /// Either the steps point to a constant and rule_loc is None, or the steps /// point to a module and rule_loc selects a macro rule within that module #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct ConstPath { steps: Tok>, } impl ConstPath { #[must_use] pub fn to_const(steps: Tok>) -> Self { Self { steps } } } pub async fn tt_to_api(exprs: &mut ExprStore, subtree: ParsTokTree) -> api::TokenTree { let without_new_expr = recur(subtree, &|tt, r| { if let ParsTok::NewExpr(expr) = tt.tok { return ParsTok::Handle(expr).at(tt.sr); } r(tt) }); without_new_expr.into_api(exprs, &mut ExprWillPanic).await }