use std::collections::VecDeque; use async_fn_stream::stream; use futures::{FutureExt, StreamExt, stream}; use itertools::Itertools; use orchid_base::{OrcErrv, Paren, Sym, fmt, is, mk_errv, report}; use orchid_extension::ToExpr; use orchid_extension::gen_expr::{GExpr, GExprKind, bot, call}; use substack::Substack; use crate::macros::mactree::MacTreeSeq; use crate::{MacTok, MacTree}; fn on_err(e: OrcErrv) -> GExpr { report(e.clone()); bot(e) } pub async fn lower(mt: &MacTree, args: Substack<'_, Sym>) -> GExpr { match mt.tok() { MacTok::Resolved(inner) => lower(inner, args).boxed_local().await, MacTok::Bottom(b) => on_err(b.clone()), MacTok::Slot => on_err(mk_errv( is("Lowering intermediary slotted mactree").await, "Found a Slot during lowering, which should only exist in temporary templates", [mt.pos()], )), MacTok::Ph(ph) => on_err(mk_errv( is("Lowering placeholder").await, format!("Found {ph} during lowering. Placeholders should only exist in macro patterns"), [mt.pos()], )), MacTok::S(Paren::Curly | Paren::Square, _) => on_err(mk_errv( is("Lowering {...} or [...]").await, format!("Cannot lower this syntax, probable incorrect macro call: {}", fmt(mt).await), [mt.pos()], )), MacTok::S(Paren::Round, b) if b.items.is_empty() => on_err(mk_errv( is("Cannot lower empty ()").await, "Attempted to lower empty (). All expressions must have a value.", [mt.pos()], )), MacTok::Lambda(arg, body) => { let MacTok::Name(n) = arg.tok() else { return on_err(mk_errv( is("Lowering lambda with complex param").await, format!("In code after macros, lambda params can only be names, not {}", fmt(arg).await), [arg.pos()], )); }; if body.items.is_empty() { return on_err(mk_errv( is("Lowering lambda with empty body").await, "Lambdas without a body are invalid, all expressions in Orchid must have a value", [mt.pos()], )); } let body = lower_seq(body, args.push(n.clone())).await; GExprKind::Lambda(Box::new(body)).at(mt.pos()) }, MacTok::S(Paren::Round, body) => lower_seq(body, args).await, MacTok::Name(n) => match args.iter().find_position(|b| *b == n) { Some((i, _)) => GExprKind::Arg(i.try_into().unwrap()).at(mt.pos()), None => n.clone().to_gen().await, }, MacTok::Value(expr) => expr.clone().to_gen().await, } } pub async fn lower_seq(mtv: &MacTreeSeq, args: Substack<'_, Sym>) -> GExpr { let mut exprs = stream(async |mut h| { for mt in mtv.items.iter() { h.emit(lower(mt, args.clone()).await).await } }) .collect::>() .boxed_local() .await; let first = exprs.pop_front().expect("We checked first that it isn't empty"); stream::iter(exprs).fold(first, async |f, x| call(f, x).await).await }