Files
orchid/orchid-host/src/parsed.rs

217 lines
6.4 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: 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<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 [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<ParsedModule> for ParsedMemberKind {
fn from(value: ParsedModule) -> Self { Self::Mod(value) }
}
#[derive(Debug, Default)]
pub struct ParsedModule {
pub exports: Vec<IStr>,
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: 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<IStr> {
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<IStr>>,
}
impl ConstPath {
#[must_use]
pub fn to_const(steps: Tok<Vec<IStr>>) -> 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
}