216 lines
6.5 KiB
Rust
216 lines
6.5 KiB
Rust
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<Expr, Expr>;
|
|
pub type ParsTok = Token<Expr, Expr>;
|
|
|
|
#[derive(Debug)]
|
|
pub struct Item {
|
|
pub sr: SrcRange,
|
|
pub comments: Vec<Comment>,
|
|
pub kind: ItemKind,
|
|
}
|
|
impl Item {
|
|
pub fn new(sr: SrcRange, kind: impl Into<ItemKind>) -> 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<ParsedMember> for ItemKind {
|
|
fn from(value: ParsedMember) -> Self { Self::Member(value) }
|
|
}
|
|
impl From<Import> 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<Variants>: Rc::new(Variants::default().bounded("const {0} via {1}")))
|
|
.units([mem.name.rc().into(), sys.print(c).await]),
|
|
ParsedMemberKind::Mod(module) =>
|
|
tl_cache!(Rc<Variants>: 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<Variants>: Rc::new(Variants::default().bounded("{0}\n{1}")))
|
|
.units([comment_text.into(), item_text])
|
|
}
|
|
}
|
|
|
|
pub struct ParsedMember {
|
|
pub name: Tok<String>,
|
|
pub exported: bool,
|
|
pub kind: ParsedMemberKind,
|
|
}
|
|
impl ParsedMember {
|
|
#[must_use]
|
|
pub fn name(&self) -> Tok<String> { self.name.clone() }
|
|
pub fn new(exported: bool, name: Tok<String>, kind: impl Into<ParsedMemberKind>) -> 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<dyn for<'a> Fn(&'a [Tok<String>]) -> LocalBoxFuture<'a, Expr>>;
|
|
|
|
pub struct ParsedExpr {
|
|
pub(crate) debug: String,
|
|
pub(crate) callback: ParsedExprCallback,
|
|
}
|
|
impl ParsedExpr {
|
|
pub async fn run(self, imported_names: &[Tok<String>]) -> 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<ParsedModule> for ParsedMemberKind {
|
|
fn from(value: ParsedModule) -> Self { Self::Mod(value) }
|
|
}
|
|
#[derive(Debug, Default)]
|
|
pub struct ParsedModule {
|
|
pub exports: Vec<Tok<String>>,
|
|
pub items: Vec<Item>,
|
|
pub use_prelude: bool,
|
|
}
|
|
impl ParsedModule {
|
|
#[must_use]
|
|
pub fn new(use_prelude: bool, items: impl IntoIterator<Item = Item>) -> 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<Item = &Import> {
|
|
(self.items.iter())
|
|
.filter_map(|it| if let ItemKind::Import(i) = &it.kind { Some(i) } else { None })
|
|
}
|
|
pub fn default_item(self, name: Tok<String>, 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: Tok<String>,
|
|
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<Tok<String>> {
|
|
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<Vec<Tok<String>>>,
|
|
}
|
|
impl ConstPath {
|
|
#[must_use]
|
|
pub fn to_const(steps: Tok<Vec<Tok<String>>>) -> 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
|
|
}
|