82 lines
2.7 KiB
Rust
82 lines
2.7 KiB
Rust
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::<VecDeque<_>>()
|
|
.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
|
|
}
|