121 lines
4.1 KiB
Rust
121 lines
4.1 KiB
Rust
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<Comment>,
|
|
line: PSnippet<'a>,
|
|
) -> OrcRes<Vec<ParsedLine>> {
|
|
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<MacTree>, ctx: &ConstCtx, rep: &Reporter) -> Vec<MacTree> {
|
|
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<MacTree> {
|
|
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<MacTree> {
|
|
stream::iter(line).filter_map(|tt| parse_tok(tt, ctx)).collect().await
|
|
}
|
|
|
|
pub async fn parse_tok(tree: &PTokTree, ctx: &impl ParseCtx) -> Option<MacTree> {
|
|
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()))
|
|
}
|