use std::pin::pin; use futures::{FutureExt, StreamExt, stream}; use hashbrown::HashMap; use itertools::Itertools; use orchid_base::error::{OrcRes, Reporter}; use orchid_base::name::Sym; use orchid_base::parse::{ Comment, ParseCtx, Parsed, Snippet, expect_tok, token_errv, try_pop_no_fluff, }; use orchid_base::sym; use orchid_base::tree::Paren; use orchid_extension::gen_expr::{atom, call, sym_ref}; use orchid_extension::parser::{ConstCtx, PSnippet, PTok, PTokTree, ParsCtx, ParsedLine, Parser}; use crate::macros::mactree::{MacTok, MacTree, glossary_v, map_mactree_v}; #[derive(Default)] pub struct LetLine; impl Parser for LetLine { const LINE_HEAD: &'static str = "let"; async fn parse<'a>( ctx: ParsCtx<'a>, exported: bool, comments: Vec, line: PSnippet<'a>, ) -> OrcRes> { let sr = line.sr(); let Parsed { output: name_tok, tail } = try_pop_no_fluff(&ctx, line).await?; let Some(name) = name_tok.as_name() else { let err = token_errv(&ctx, name_tok, "Constant must have a name", |t| { format!("Expected a name but found {t}") }); return Err(err.await); }; let Parsed { tail, .. } = expect_tok(&ctx, tail, ctx.i().i("=").await).await?; let aliased = parse_tokv(tail, &ctx).await; Ok(vec![ParsedLine::cnst(&line.sr(), &comments, exported, name, async move |ctx| { let rep = Reporter::new(); let dealiased = dealias_mac_v(aliased, &ctx, &rep).await; let macro_input = MacTok::S(Paren::Round, dealiased).at(sr.pos()); if let Some(e) = rep.errv() { return Err(e); } Ok(call([ sym_ref(sym!(macros::lower; ctx.i()).await), call([sym_ref(sym!(macros::resolve; ctx.i()).await), atom(macro_input)]), ])) })]) } } pub async fn dealias_mac_v(aliased: Vec, ctx: &ConstCtx, rep: &Reporter) -> Vec { let keys = glossary_v(&aliased).collect_vec(); let mut names: HashMap<_, _> = HashMap::new(); let mut stream = pin!(ctx.names(&keys).zip(stream::iter(&keys))); while let Some((canonical, local)) = stream.next().await { match canonical { Err(e) => rep.report(e), Ok(name) => { names.insert(local.clone(), name); }, } } map_mactree_v(&aliased, &mut false, &mut |tree| match &*tree.tok { MacTok::Name(n) => names.get(n).map(|new_n| MacTok::Name(new_n.clone()).at(tree.pos())), _ => None, }) } pub async fn parse_tokv(line: PSnippet<'_>, ctx: &impl ParseCtx) -> Vec { if let Some((idx, arg)) = line.iter().enumerate().find_map(|(i, x)| Some((i, x.as_lambda()?))) { let (head, lambda) = line.split_at(idx as u32); let (_, body) = lambda.pop_front().unwrap(); let body = parse_tokv(body, ctx).boxed_local().await; let mut all = parse_tokv_no_lambdas(&head, ctx).await; match parse_tok(arg, ctx).await { Some(arg) => all.push(MacTok::Lambda(arg, body).at(lambda.sr().pos())), None => ctx.rep().report( token_errv(ctx, arg, "Lambda argument fluff", |arg| { format!("Lambda arguments must be a valid token, found meaningless fragment {arg}") }) .await, ), }; all } else { parse_tokv_no_lambdas(&line, ctx).await } } async fn parse_tokv_no_lambdas(line: &[PTokTree], ctx: &impl ParseCtx) -> Vec { stream::iter(line).filter_map(|tt| parse_tok(tt, ctx)).collect().await } pub async fn parse_tok(tree: &PTokTree, ctx: &impl ParseCtx) -> Option { let tok = match &tree.tok { PTok::Bottom(errv) => MacTok::Bottom(errv.clone()), PTok::BR | PTok::Comment(_) => return None, PTok::Name(n) => MacTok::Name(Sym::new([n.clone()], ctx.i()).await.unwrap()), PTok::NS(..) => match tree.as_multiname() { Ok(mn) => MacTok::Name(mn.to_sym(ctx.i()).await), Err(nested) => { ctx.rep().report( token_errv(ctx, tree, ":: can only be followed by a name in an expression", |tok| { format!("Expected name, found {tok}") }) .await, ); return parse_tok(nested, ctx).boxed_local().await; }, }, PTok::Handle(expr) => MacTok::Value(expr.clone()), PTok::NewExpr(never) => match *never {}, PTok::LambdaHead(_) => panic!("Lambda-head handled in the sequence parser"), PTok::S(p, body) => MacTok::S(*p, parse_tokv(Snippet::new(tree, body), ctx).boxed_local().await), }; Some(tok.at(tree.sr().pos())) }