Added support for defining macros in Rust within the macro system
Also fixed a lot of bugs
This commit is contained in:
@@ -7,3 +7,4 @@ pub use std::string::str_atom::OrcString;
|
||||
|
||||
pub use macros::macro_system::MacroSystem;
|
||||
pub use macros::mactree::{MacTok, MacTree};
|
||||
use orchid_api as api;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use never::Never;
|
||||
use orchid_extension::atom::{Atomic, TypAtom};
|
||||
use orchid_base::format::fmt;
|
||||
use orchid_extension::atom::{Atomic, TAtom};
|
||||
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
|
||||
use orchid_extension::conv::{ToExpr, TryFromExpr};
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::coroutine_exec::exec;
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::gen_expr::GExpr;
|
||||
|
||||
@@ -24,26 +26,33 @@ impl OwnedAtom for InstantiateTplCall {
|
||||
type Refs = Never;
|
||||
// Technically must be supported but shouldn't actually ever be called
|
||||
async fn call_ref(&self, arg: Expr) -> GExpr {
|
||||
eprintln!(
|
||||
"Copying partially applied instantiate_tpl call. This is an internal value.\
|
||||
\nIt should be fully consumed within generated code."
|
||||
);
|
||||
if !self.argv.is_empty() {
|
||||
eprintln!(
|
||||
"Copying partially applied instantiate_tpl call. This is an internal value.\
|
||||
\nIt should be fully consumed within generated code."
|
||||
);
|
||||
}
|
||||
self.clone().call(arg).await
|
||||
}
|
||||
async fn call(mut self, arg: Expr) -> GExpr {
|
||||
match TypAtom::<MacTree>::try_from_expr(arg).await {
|
||||
Err(e) => return Err::<Never, _>(e).to_expr().await,
|
||||
Ok(t) => self.argv.push(own(t).await),
|
||||
};
|
||||
if self.argv.len() < self.argc {
|
||||
return self.to_expr().await;
|
||||
}
|
||||
let mut args = self.argv.into_iter();
|
||||
let ret = map_mactree(&self.tpl, &mut false, &mut |mt| match mt.tok() {
|
||||
MacTok::Slot => Some(args.next().expect("Not enough arguments to fill all slots")),
|
||||
_ => None,
|
||||
});
|
||||
assert!(args.next().is_none(), "Too many arguments for all slots");
|
||||
ret.to_expr().await
|
||||
exec("macros::instantiate_tpl", async move |mut h| {
|
||||
match h.exec::<TAtom<MacTree>>(arg.clone()).await {
|
||||
Err(_) => panic!("Expected a macro param, found {}", fmt(&arg, arg.ctx().i()).await),
|
||||
Ok(t) => self.argv.push(own(t).await),
|
||||
};
|
||||
if self.argv.len() < self.argc {
|
||||
return self.to_expr().await;
|
||||
}
|
||||
let mut args = self.argv.into_iter();
|
||||
let ret = map_mactree(&self.tpl, &mut false, &mut |mt| match mt.tok() {
|
||||
MacTok::Slot => Some(args.next().expect("Not enough arguments to fill all slots")),
|
||||
_ => None,
|
||||
});
|
||||
assert!(args.next().is_none(), "Too many arguments for all slots");
|
||||
ret.to_expr().await
|
||||
})
|
||||
.await
|
||||
.to_expr()
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,10 +10,13 @@ use orchid_base::parse::{
|
||||
};
|
||||
use orchid_base::sym;
|
||||
use orchid_base::tree::Paren;
|
||||
use orchid_extension::atom::TAtom;
|
||||
use orchid_extension::conv::TryFromExpr;
|
||||
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};
|
||||
use crate::macros::ph_lexer::PhAtom;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LetLine;
|
||||
@@ -42,10 +45,10 @@ impl Parser for LetLine {
|
||||
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)]),
|
||||
]))
|
||||
Ok(call(sym_ref(sym!(macros::lower; ctx.i()).await), [call(
|
||||
sym_ref(sym!(macros::resolve; ctx.i()).await),
|
||||
[atom(macro_input)],
|
||||
)]))
|
||||
})])
|
||||
}
|
||||
}
|
||||
@@ -110,7 +113,10 @@ pub async fn parse_tok(tree: &PTokTree, ctx: &impl ParseCtx) -> Option<MacTree>
|
||||
return parse_tok(nested, ctx).boxed_local().await;
|
||||
},
|
||||
},
|
||||
PTok::Handle(expr) => MacTok::Value(expr.clone()),
|
||||
PTok::Handle(expr) => match TAtom::<PhAtom>::try_from_expr(expr.clone()).await {
|
||||
Err(_) => MacTok::Value(expr.clone()),
|
||||
Ok(ta) => MacTok::Ph(ta.value.to_full(ta.ctx()).await),
|
||||
},
|
||||
PTok::NewExpr(never) => match *never {},
|
||||
PTok::LambdaHead(_) => panic!("Lambda-head handled in the sequence parser"),
|
||||
PTok::S(p, body) =>
|
||||
|
||||
@@ -1,58 +1,47 @@
|
||||
use hashbrown::HashMap;
|
||||
use itertools::Itertools;
|
||||
use itertools::{Itertools, chain};
|
||||
use orchid_base::error::Reporter;
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::TypAtom;
|
||||
use orchid_base::{clone, sym};
|
||||
use orchid_extension::atom::TAtom;
|
||||
use orchid_extension::atom_owned::own;
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::coroutine_exec::exec;
|
||||
use orchid_extension::gen_expr::{atom, call, sym_ref};
|
||||
use orchid_extension::func_atom::Lambda;
|
||||
use orchid_extension::gen_expr::{call, sym_ref};
|
||||
use orchid_extension::reflection::{ReflMemKind, refl};
|
||||
use orchid_extension::tree::{GenMember, comments, fun, prefix};
|
||||
use orchid_extension::tree::{GenMember, MemKind, fun, lazy, prefix};
|
||||
use substack::Substack;
|
||||
|
||||
use crate::Int;
|
||||
use crate::macros::instantiate_tpl::InstantiateTplCall;
|
||||
use crate::macros::macro_line::{Macro, Matcher};
|
||||
use crate::macros::mactree::{LowerCtx, MacTree};
|
||||
use crate::macros::recur_state::RecurState;
|
||||
use crate::MacTok;
|
||||
use crate::macros::macro_value::{Macro, Matcher};
|
||||
use crate::macros::mactree::{LowerCtx, MacTree, Ph};
|
||||
use crate::macros::resolve::{ResolveCtx, resolve};
|
||||
use crate::macros::utils::{mactree, mactreev, mk_macro};
|
||||
|
||||
pub fn gen_macro_lib() -> Vec<GenMember> {
|
||||
prefix("macros", [
|
||||
comments(
|
||||
["This is an internal function, you can't obtain a value of its argument type.", "hidden"],
|
||||
fun(true, "instantiate_tpl", |tpl: TypAtom<MacTree>, right: Int| async move {
|
||||
InstantiateTplCall {
|
||||
tpl: own(tpl).await,
|
||||
argc: right.0.try_into().unwrap(),
|
||||
argv: Vec::new(),
|
||||
}
|
||||
}),
|
||||
),
|
||||
fun(true, "resolve", |tpl: TypAtom<MacTree>| async move {
|
||||
call([
|
||||
sym_ref(sym!(macros::resolve_recur; tpl.untyped.ctx().i()).await),
|
||||
atom(RecurState::Bottom),
|
||||
tpl.untyped.ex().to_expr().await,
|
||||
])
|
||||
}),
|
||||
fun(true, "lower", |tpl: TypAtom<MacTree>| async move {
|
||||
fun(true, "lower", |tpl: TAtom<MacTree>| async move {
|
||||
let ctx = LowerCtx { sys: tpl.untyped.ctx().clone(), rep: &Reporter::new() };
|
||||
let res = own(tpl).await.lower(ctx, Substack::Bottom).await;
|
||||
if let Some(e) = Reporter::new().errv() { Err(e) } else { Ok(res) }
|
||||
}),
|
||||
fun(true, "resolve_recur", |state: TypAtom<RecurState>, tpl: TypAtom<MacTree>| async move {
|
||||
exec("macros::resolve_recur", async move |mut h| {
|
||||
fun(true, "recur", async |tpl: TAtom<MacTree>| {
|
||||
call(sym_ref(sym!(macros::lower; tpl.i()).await), [call(
|
||||
sym_ref(sym!(macros::resolve; tpl.i()).await),
|
||||
[tpl.to_expr().await],
|
||||
)])
|
||||
}),
|
||||
fun(true, "resolve", |tpl: TAtom<MacTree>| async move {
|
||||
exec("macros::resolve", async move |mut h| {
|
||||
let ctx = tpl.ctx().clone();
|
||||
let root = refl(&ctx);
|
||||
let tpl = own(tpl.clone()).await;
|
||||
let mut macros = HashMap::new();
|
||||
for n in tpl.glossary() {
|
||||
if let Ok(ReflMemKind::Const) = root.get_by_path(n).await.map(|m| m.kind()) {
|
||||
let Ok(mac) = h.exec::<TypAtom<Macro>>(sym_ref(n.clone())).await else { continue };
|
||||
let Ok(mac) = h.exec::<TAtom<Macro>>(sym_ref(n.clone())).await else { continue };
|
||||
let mac = own(mac).await;
|
||||
macros.entry(mac.0.own_kws[0].clone()).or_insert(mac);
|
||||
macros.entry(mac.canonical_name(&ctx).await).or_insert(mac);
|
||||
}
|
||||
}
|
||||
let mut named = HashMap::new();
|
||||
@@ -69,7 +58,7 @@ pub fn gen_macro_lib() -> Vec<GenMember> {
|
||||
}
|
||||
}
|
||||
let priod = priod.into_iter().sorted_unstable_by_key(|(p, _)| *p).map(|(_, r)| r).collect();
|
||||
let mut rctx = ResolveCtx { h, recur: own(state).await, ctx: ctx.clone(), named, priod };
|
||||
let mut rctx = ResolveCtx { h, ctx: ctx.clone(), named, priod };
|
||||
let resolve_res = resolve(&mut rctx, &tpl).await;
|
||||
std::mem::drop(rctx);
|
||||
match resolve_res {
|
||||
@@ -79,5 +68,30 @@ pub fn gen_macro_lib() -> Vec<GenMember> {
|
||||
})
|
||||
.await
|
||||
}),
|
||||
// TODO test whether any of this worked
|
||||
lazy(true, "common", async |_, ctx| {
|
||||
let add_macro = {
|
||||
clone!(ctx);
|
||||
mk_macro(Some(1), ["+"], [(
|
||||
mactreev!(ctx.i(); "...$" lhs 0 macros::common::+ "...$" rhs 1),
|
||||
Lambda::new("std::number::add", async move |lhs: TAtom<MacTree>, rhs: TAtom<MacTree>| {
|
||||
mactree!(ctx.i(); std::number::add
|
||||
(macros::recur "'" lhs.ex();)
|
||||
(macros::recur "'" rhs.ex();)
|
||||
)
|
||||
}),
|
||||
)])
|
||||
};
|
||||
let mul_macro = mk_macro(Some(2), ["*"], [(
|
||||
mactreev!(ctx.i(); "...$" lhs 0 macros::common::* "...$" rhs 1),
|
||||
Lambda::new("std::number::mul", async |lhs: TAtom<MacTree>, rhs: TAtom<MacTree>| {
|
||||
mactree!(lhs.ctx().i(); std::number::mul
|
||||
(macros::recur "'" lhs.ex();)
|
||||
(macros::recur "'" rhs.ex();)
|
||||
)
|
||||
}),
|
||||
)]);
|
||||
MemKind::Mod { members: chain!(add_macro, mul_macro).collect_vec() }
|
||||
}),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -1,31 +1,24 @@
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use async_once_cell::OnceCell;
|
||||
use futures::{StreamExt, stream};
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use itertools::Itertools;
|
||||
use never::Never;
|
||||
use orchid_base::error::{OrcRes, Reporter, mk_errv};
|
||||
use orchid_base::interner::Tok;
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::parse::{
|
||||
Comment, ParseCtx, Parsed, Snippet, expect_end, expect_tok, line_items, token_errv,
|
||||
try_pop_no_fluff,
|
||||
};
|
||||
use orchid_base::tree::{Paren, Token};
|
||||
use orchid_base::{clone, sym};
|
||||
use orchid_extension::atom::{Atomic, TypAtom};
|
||||
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant};
|
||||
use orchid_extension::atom::TAtom;
|
||||
use orchid_extension::conv::{ToExpr, TryFromExpr};
|
||||
use orchid_extension::gen_expr::{atom, call, sym_ref};
|
||||
use orchid_extension::parser::{PSnippet, ParsCtx, ParsedLine, Parser};
|
||||
|
||||
use crate::macros::let_line::{dealias_mac_v, parse_tokv};
|
||||
use crate::macros::macro_value::{Macro, MacroData, Matcher, Rule};
|
||||
use crate::macros::mactree::{glossary_v, map_mactree_v};
|
||||
use crate::macros::recur_state::{RecurState, RulePath};
|
||||
use crate::macros::rule::matcher::{NamedMatcher, PriodMatcher};
|
||||
use crate::{Int, MacTok};
|
||||
|
||||
@@ -48,42 +41,46 @@ impl Parser for MacroLine {
|
||||
));
|
||||
}
|
||||
let module = ctx.module();
|
||||
let Parsed { output, tail } = try_pop_no_fluff(&ctx, line).await?;
|
||||
let Parsed { output: prio_or_body, tail } = try_pop_no_fluff(&ctx, line).await?;
|
||||
let bad_first_item_err = || {
|
||||
token_errv(&ctx, output, "Expected priority or block", |s| {
|
||||
token_errv(&ctx, prio_or_body, "Expected priority or block", |s| {
|
||||
format!("Expected a priority number or a () block, found {s}")
|
||||
})
|
||||
};
|
||||
let (prio, body) = match &output.tok {
|
||||
Token::S(Paren::Round, body) => (None, body),
|
||||
Token::Handle(expr) => match TypAtom::<Int>::try_from_expr(expr.clone()).await {
|
||||
let (prio, body) = match &prio_or_body.tok {
|
||||
Token::S(Paren::Round, body) => {
|
||||
expect_end(&ctx, tail).await?;
|
||||
(None, body)
|
||||
},
|
||||
Token::Handle(expr) => match TAtom::<Int>::try_from_expr(expr.clone()).await {
|
||||
Err(e) => {
|
||||
return Err(e + bad_first_item_err().await);
|
||||
},
|
||||
Ok(prio) => {
|
||||
let Token::S(Paren::Round, block) = &output.tok else {
|
||||
let Parsed { output: body, tail } = try_pop_no_fluff(&ctx, tail).await?;
|
||||
let Token::S(Paren::Round, block) = &body.tok else {
|
||||
return Err(
|
||||
token_errv(&ctx, output, "Expected () block", |s| {
|
||||
token_errv(&ctx, prio_or_body, "Expected () block", |s| {
|
||||
format!("Expected a () block, found {s}")
|
||||
})
|
||||
.await,
|
||||
);
|
||||
};
|
||||
expect_end(&ctx, tail).await?;
|
||||
(Some(prio), block)
|
||||
},
|
||||
},
|
||||
_ => return Err(bad_first_item_err().await),
|
||||
};
|
||||
expect_end(&ctx, tail).await?;
|
||||
let lines = line_items(&ctx, Snippet::new(output, body)).await;
|
||||
let lines = line_items(&ctx, Snippet::new(prio_or_body, body)).await;
|
||||
let Some((kw_line, rule_lines)) = lines.split_first() else { return Ok(Vec::new()) };
|
||||
let mut keywords = HashMap::new();
|
||||
let mut keywords = Vec::new();
|
||||
let Parsed { tail: kw_tail, .. } =
|
||||
expect_tok(&ctx, kw_line.tail, ctx.i().i("keywords").await).await?;
|
||||
for kw_tok in kw_tail.iter().filter(|kw| !kw.is_fluff()) {
|
||||
match kw_tok.as_name() {
|
||||
Some(kw) => {
|
||||
keywords.insert(kw, kw_tok.sr());
|
||||
keywords.push((kw, kw_tok.sr()));
|
||||
},
|
||||
None => ctx.rep().report(
|
||||
token_errv(&ctx, kw_tok, "invalid macro keywords list", |tok| {
|
||||
@@ -93,7 +90,7 @@ impl Parser for MacroLine {
|
||||
),
|
||||
}
|
||||
}
|
||||
let Some(macro_name) = keywords.keys().next().cloned() else {
|
||||
let Some((macro_name, _)) = keywords.first().cloned() else {
|
||||
return Err(mk_errv(
|
||||
ctx.i().i("macro with no keywords").await,
|
||||
"Macros must define at least one macro of their own.",
|
||||
@@ -103,9 +100,8 @@ impl Parser for MacroLine {
|
||||
let mut rules = Vec::new();
|
||||
let mut lines = Vec::new();
|
||||
for (idx, line) in rule_lines.iter().enumerate().map(|(n, v)| (n as u32, v)) {
|
||||
let path = RulePath { module: module.clone(), main_kw: macro_name.clone(), rule: idx };
|
||||
let sr = line.tail.sr();
|
||||
let name = ctx.i().i(&path.name()).await;
|
||||
let name = ctx.i().i(&format!("rule::{}::{}", macro_name, idx)).await;
|
||||
let Parsed { tail, .. } = expect_tok(&ctx, line.tail, ctx.i().i("rule").await).await?;
|
||||
let arrow_token = ctx.i().i("=>").await;
|
||||
let Some((pattern, body)) = tail.split_once(|tok| tok.is_kw(arrow_token.clone())) else {
|
||||
@@ -132,7 +128,7 @@ impl Parser for MacroLine {
|
||||
]
|
||||
}
|
||||
let body_sr = body.sr();
|
||||
rules.push((name.clone(), placeholders, rules.len() as u32, sr.pos(), pattern));
|
||||
rules.push((name.clone(), placeholders, pattern));
|
||||
lines.push(ParsedLine::cnst(&sr, &line.output, true, name, async move |ctx| {
|
||||
let rep = Reporter::new();
|
||||
let body = dealias_mac_v(body_mactree, &ctx, &rep).await;
|
||||
@@ -140,25 +136,23 @@ impl Parser for MacroLine {
|
||||
if let Some(e) = rep.errv() {
|
||||
return Err(e);
|
||||
}
|
||||
Ok(call([
|
||||
sym_ref(sym!(macros::resolve_recur; ctx.i()).await),
|
||||
atom(RecurState::base(path)),
|
||||
macro_input.to_expr().await,
|
||||
]))
|
||||
Ok(call(sym_ref(sym!(macros::lower; ctx.i()).await), [call(
|
||||
sym_ref(sym!(macros::resolve; ctx.i()).await),
|
||||
[macro_input.to_expr().await],
|
||||
)]))
|
||||
}))
|
||||
}
|
||||
let mac_cell = Rc::new(OnceCell::new());
|
||||
let keywords = Rc::new(keywords);
|
||||
let rules = Rc::new(RefCell::new(Some(rules)));
|
||||
for (kw, sr) in &*keywords {
|
||||
clone!(mac_cell, keywords, rules, module, prio);
|
||||
clone!(mac_cell, rules, module, prio);
|
||||
lines.push(ParsedLine::cnst(&sr.clone(), &comments, true, kw.clone(), async move |cctx| {
|
||||
let mac = mac_cell
|
||||
.get_or_init(async {
|
||||
let rep = Reporter::new();
|
||||
let rules = rules.borrow_mut().take().expect("once cell initializer runs");
|
||||
let rules = stream::iter(rules)
|
||||
.then(|(body_name, placeholders, index, pos, pattern_macv)| {
|
||||
.then(|(body_name, placeholders, pattern_macv)| {
|
||||
let cctx = &cctx;
|
||||
let rep = &rep;
|
||||
let prio = &prio;
|
||||
@@ -171,8 +165,7 @@ impl Parser for MacroLine {
|
||||
};
|
||||
let placeholders = placeholders.into_iter().map(|(ph, _)| ph.name).collect_vec();
|
||||
match pattern_res {
|
||||
Ok(pattern) =>
|
||||
Some(Rule { index, pos, body_name, pattern, glossary, placeholders }),
|
||||
Ok(pattern) => Some(Rule { body_name, pattern, glossary, placeholders }),
|
||||
Err(e) => {
|
||||
rep.report(e);
|
||||
None
|
||||
@@ -183,8 +176,7 @@ impl Parser for MacroLine {
|
||||
.flat_map(stream::iter)
|
||||
.collect::<Vec<_>>()
|
||||
.await;
|
||||
let own_kws = keywords.keys().cloned().collect_vec();
|
||||
Macro(Rc::new(MacroData { module, prio: prio.map(|i| i.0 as u64), rules, own_kws }))
|
||||
Macro(Rc::new(MacroData { module, prio: prio.map(|i| i.0 as u64), rules }))
|
||||
})
|
||||
.await;
|
||||
atom(mac.clone())
|
||||
@@ -193,36 +185,3 @@ impl Parser for MacroLine {
|
||||
Ok(lines)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MacroData {
|
||||
pub module: Sym,
|
||||
pub prio: Option<u64>,
|
||||
pub rules: Vec<Rule>,
|
||||
pub own_kws: Vec<Tok<String>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Macro(pub Rc<MacroData>);
|
||||
#[derive(Debug)]
|
||||
pub struct Rule {
|
||||
pub index: u32,
|
||||
pub pos: Pos,
|
||||
pub pattern: Matcher,
|
||||
pub glossary: HashSet<Sym>,
|
||||
pub placeholders: Vec<Tok<String>>,
|
||||
pub body_name: Tok<String>,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub enum Matcher {
|
||||
Named(NamedMatcher),
|
||||
Priod(PriodMatcher),
|
||||
}
|
||||
impl Atomic for Macro {
|
||||
type Data = ();
|
||||
type Variant = OwnedVariant;
|
||||
}
|
||||
impl OwnedAtom for Macro {
|
||||
type Refs = Never;
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use never::Never;
|
||||
use orchid_base::interner::Interner;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::reqnot::Receipt;
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::{AtomDynfo, AtomicFeatures};
|
||||
use orchid_extension::entrypoint::ExtReq;
|
||||
use orchid_extension::lexer::LexerObj;
|
||||
@@ -14,9 +14,11 @@ use orchid_extension::tree::GenMember;
|
||||
use crate::macros::instantiate_tpl::InstantiateTplCall;
|
||||
use crate::macros::let_line::LetLine;
|
||||
use crate::macros::macro_lib::gen_macro_lib;
|
||||
use crate::macros::macro_line::{Macro, MacroLine};
|
||||
use crate::macros::macro_line::MacroLine;
|
||||
use crate::macros::macro_value::Macro;
|
||||
use crate::macros::mactree_lexer::MacTreeLexer;
|
||||
use crate::macros::recur_state::RecurState;
|
||||
use crate::macros::ph_lexer::{PhAtom, PhLexer};
|
||||
use crate::macros::requests::MacroReq;
|
||||
use crate::{MacTree, StdSystem};
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -30,20 +32,26 @@ impl SystemCtor for MacroSystem {
|
||||
}
|
||||
impl SystemCard for MacroSystem {
|
||||
type Ctor = Self;
|
||||
type Req = Never;
|
||||
type Req = MacroReq;
|
||||
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn AtomDynfo>>> {
|
||||
[
|
||||
Some(InstantiateTplCall::dynfo()),
|
||||
Some(MacTree::dynfo()),
|
||||
Some(RecurState::dynfo()),
|
||||
Some(Macro::dynfo()),
|
||||
Some(PhAtom::dynfo()),
|
||||
]
|
||||
}
|
||||
}
|
||||
impl System for MacroSystem {
|
||||
async fn request(_: ExtReq<'_>, req: Self::Req) -> Receipt<'_> { match req {} }
|
||||
async fn prelude(_: &Interner) -> Vec<Sym> { vec![] }
|
||||
fn lexers() -> Vec<LexerObj> { vec![&MacTreeLexer] }
|
||||
async fn request(_: ExtReq<'_>, req: Self::Req) -> Receipt<'_> { todo!("Handle {req:?}") }
|
||||
async fn prelude(i: &Interner) -> Vec<Sym> {
|
||||
vec![
|
||||
sym!(macros::resolve; i).await,
|
||||
sym!(macros::common::+; i).await,
|
||||
sym!(macros::common::*; i).await,
|
||||
]
|
||||
}
|
||||
fn lexers() -> Vec<LexerObj> { vec![&MacTreeLexer, &PhLexer] }
|
||||
fn parsers() -> Vec<ParserObj> { vec![&LetLine, &MacroLine] }
|
||||
fn env() -> Vec<GenMember> { gen_macro_lib() }
|
||||
}
|
||||
|
||||
48
orchid-std/src/macros/macro_value.rs
Normal file
48
orchid-std/src/macros/macro_value.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use std::borrow::Cow;
|
||||
use std::rc::Rc;
|
||||
|
||||
use hashbrown::HashSet;
|
||||
use never::Never;
|
||||
use orchid_base::interner::Tok;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_extension::atom::Atomic;
|
||||
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant};
|
||||
use orchid_extension::system::SysCtx;
|
||||
|
||||
use crate::macros::rule::matcher::{NamedMatcher, PriodMatcher};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MacroData {
|
||||
pub module: Sym,
|
||||
pub prio: Option<u64>,
|
||||
pub rules: Vec<Rule>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Macro(pub Rc<MacroData>);
|
||||
impl Macro {
|
||||
pub async fn canonical_name(&self, ctx: &SysCtx) -> Sym {
|
||||
self.0.module.suffix([self.0.rules[0].body_name.clone()], ctx.i()).await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Rule {
|
||||
pub pattern: Matcher,
|
||||
pub glossary: HashSet<Sym>,
|
||||
pub placeholders: Vec<Tok<String>>,
|
||||
pub body_name: Tok<String>,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub enum Matcher {
|
||||
Named(NamedMatcher),
|
||||
Priod(PriodMatcher),
|
||||
}
|
||||
impl Atomic for Macro {
|
||||
type Data = ();
|
||||
type Variant = OwnedVariant;
|
||||
}
|
||||
impl OwnedAtom for Macro {
|
||||
type Refs = Never;
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use futures::FutureExt;
|
||||
use futures::future::join_all;
|
||||
use hashbrown::HashSet;
|
||||
use itertools::Itertools;
|
||||
use orchid_api_derive::Coding;
|
||||
use orchid_base::error::{OrcErrv, Reporter, mk_errv};
|
||||
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants, fmt};
|
||||
use orchid_base::interner::Tok;
|
||||
@@ -42,38 +43,56 @@ impl MacTree {
|
||||
MacTok::Bottom(e) => bot(e.clone()),
|
||||
MacTok::Lambda(arg, body) => {
|
||||
let MacTok::Name(name) = &*arg.tok else {
|
||||
let err = mk_errv(
|
||||
return bot(mk_errv(
|
||||
ctx.sys.i().i("Syntax error after macros").await,
|
||||
"This token ends up as a binding, consider replacing it with a name",
|
||||
[arg.pos()],
|
||||
);
|
||||
ctx.rep.report(err.clone());
|
||||
return bot(err);
|
||||
));
|
||||
};
|
||||
lambda(args.len() as u64, lower_v(body, ctx, args.push(name.clone())).await)
|
||||
let arg_pos = args.len() as u64;
|
||||
let args = args.push(name.clone());
|
||||
let body = match &body[..] {
|
||||
[] => bot(mk_errv(
|
||||
ctx.sys.i().i("Empty lambda body").await,
|
||||
"Lambdas must evaluate to an expression",
|
||||
[self.pos()],
|
||||
)),
|
||||
[f, argv @ ..] => call(
|
||||
f.lower(ctx.clone(), args.clone()).boxed_local().await,
|
||||
lower_v(argv, ctx, args).await,
|
||||
),
|
||||
};
|
||||
lambda(arg_pos, body)
|
||||
},
|
||||
MacTok::Name(name) => match args.iter().enumerate().find(|(_, n)| *n == name) {
|
||||
None => sym_ref(name.clone()),
|
||||
Some((i, _)) => arg((args.len() - i) as u64),
|
||||
Some((i, _)) => arg((args.len() - i - 1) as u64),
|
||||
},
|
||||
MacTok::Ph(ph) => {
|
||||
let err = mk_errv(
|
||||
return bot(mk_errv(
|
||||
ctx.sys.i().i("Placeholder in value").await,
|
||||
format!("Placeholder {ph} is only supported in macro patterns"),
|
||||
[self.pos()],
|
||||
);
|
||||
ctx.rep.report(err.clone());
|
||||
return bot(err);
|
||||
));
|
||||
},
|
||||
MacTok::S(Paren::Round, body) => match &body[..] {
|
||||
[fun, argv @ ..] => call(
|
||||
fun.lower(ctx.clone(), args.clone()).boxed_local().await,
|
||||
lower_v(argv, ctx, args).await,
|
||||
),
|
||||
[] =>
|
||||
return bot(mk_errv(
|
||||
ctx.sys.i().i("Empty ()").await,
|
||||
"Empty () is not a meaningful expression",
|
||||
[self.pos()],
|
||||
)),
|
||||
},
|
||||
MacTok::S(Paren::Round, body) => call(lower_v(body, ctx, args).await),
|
||||
MacTok::S(..) => {
|
||||
let err = mk_errv(
|
||||
return bot(mk_errv(
|
||||
ctx.sys.i().i("[] or {} after macros").await,
|
||||
format!("{} didn't match any macro", fmt(self, ctx.sys.i()).await),
|
||||
[self.pos()],
|
||||
);
|
||||
ctx.rep.report(err.clone());
|
||||
return bot(err);
|
||||
));
|
||||
},
|
||||
MacTok::Slot => panic!("Uninstantiated template should never be exposed"),
|
||||
MacTok::Value(v) => v.clone().to_expr().await,
|
||||
@@ -90,7 +109,8 @@ impl OwnedAtom for MacTree {
|
||||
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||
async fn print_atom<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||
self.tok.print(c).await
|
||||
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("'{0}")))
|
||||
.units([self.tok.print(c).await])
|
||||
}
|
||||
}
|
||||
impl Format for MacTree {
|
||||
@@ -134,22 +154,18 @@ impl Format for MacTok {
|
||||
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||
match self {
|
||||
Self::Value(v) => v.print(c).await,
|
||||
Self::Lambda(arg, b) => FmtUnit::new(
|
||||
tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
||||
.unbounded("\\{0b}.{1l}")
|
||||
.bounded("(\\{0b}.{1b})"))),
|
||||
[arg.print(c).boxed_local().await, mtreev_fmt(b, c).await],
|
||||
),
|
||||
Self::Lambda(arg, b) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
||||
.unbounded("\\{0} {1l}")
|
||||
.bounded("(\\{0} {1b})")))
|
||||
.units([arg.print(c).boxed_local().await, mtreev_fmt(b, c).await]),
|
||||
Self::Name(n) => format!("{n}").into(),
|
||||
Self::Ph(ph) => format!("{ph}").into(),
|
||||
Self::S(p, body) => FmtUnit::new(
|
||||
match *p {
|
||||
Paren::Round => Rc::new(Variants::default().bounded("({0b})")),
|
||||
Paren::Curly => Rc::new(Variants::default().bounded("{{0b}}")),
|
||||
Paren::Square => Rc::new(Variants::default().bounded("[{0b}]")),
|
||||
},
|
||||
[mtreev_fmt(body, c).await],
|
||||
),
|
||||
Self::S(p, body) => match *p {
|
||||
Paren::Round => tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("({0b})"))),
|
||||
Paren::Curly => tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{{0b}}"))),
|
||||
Paren::Square => tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("[{0b}]"))),
|
||||
}
|
||||
.units([mtreev_fmt(body, c).await]),
|
||||
Self::Slot => "$SLOT".into(),
|
||||
Self::Bottom(err) if err.len() == 1 => format!("Bottom({}) ", err.one().unwrap()).into(),
|
||||
Self::Bottom(err) => format!("Botttom(\n{}) ", indent(&err.to_string())).into(),
|
||||
@@ -181,7 +197,7 @@ impl Display for Ph {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Coding)]
|
||||
pub enum PhKind {
|
||||
Scalar,
|
||||
Vector { at_least_one: bool, priority: u8 },
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
use futures::FutureExt;
|
||||
use itertools::chain;
|
||||
use orchid_base::error::{OrcRes, mk_errv};
|
||||
use orchid_base::parse::ParseCtx;
|
||||
use orchid_base::sym;
|
||||
use orchid_base::tokens::PARENS;
|
||||
use orchid_base::tree::Paren;
|
||||
use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable};
|
||||
use orchid_extension::parser::p_tree2gen;
|
||||
use orchid_extension::tree::{GenTok, GenTokTree, ref_tok, x_tok};
|
||||
use orchid_extension::tree::{GenTok, GenTokTree, x_tok};
|
||||
|
||||
use crate::macros::instantiate_tpl::InstantiateTplCall;
|
||||
use crate::macros::let_line::parse_tok;
|
||||
use crate::macros::mactree::{MacTok, MacTree};
|
||||
|
||||
@@ -29,12 +30,9 @@ impl Lexer for MacTreeLexer {
|
||||
let tok = match &args[..] {
|
||||
[] => x_tok(mactree).await,
|
||||
_ => {
|
||||
let call = ([
|
||||
ref_tok(sym!(macros::instantiate_tpl; ctx.i()).await).await.at(range.clone()),
|
||||
x_tok(mactree).await.at(range.clone()),
|
||||
]
|
||||
.into_iter())
|
||||
.chain(args.into_iter());
|
||||
let instantiate_tpl_call =
|
||||
InstantiateTplCall { argc: args.len(), argv: vec![], tpl: mactree };
|
||||
let call = chain!([x_tok(instantiate_tpl_call).await.at(range.clone())], args);
|
||||
GenTok::S(Paren::Round, call.collect())
|
||||
},
|
||||
};
|
||||
|
||||
@@ -3,10 +3,13 @@ mod let_line;
|
||||
mod macro_lib;
|
||||
mod macro_line;
|
||||
pub mod macro_system;
|
||||
mod macro_value;
|
||||
pub mod mactree;
|
||||
mod mactree_lexer;
|
||||
pub mod recur_state;
|
||||
mod ph_lexer;
|
||||
mod requests;
|
||||
mod resolve;
|
||||
mod rule;
|
||||
mod utils;
|
||||
|
||||
use mactree::{MacTok, MacTree};
|
||||
|
||||
79
orchid-std/src/macros/ph_lexer.rs
Normal file
79
orchid-std/src/macros/ph_lexer.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
use orchid_api_derive::Coding;
|
||||
use orchid_base::error::{OrcRes, mk_errv};
|
||||
use orchid_base::format::FmtUnit;
|
||||
use orchid_base::parse::{name_char, name_start};
|
||||
use orchid_extension::atom::Atomic;
|
||||
use orchid_extension::atom_thin::{ThinAtom, ThinVariant};
|
||||
use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable};
|
||||
use orchid_extension::system::SysCtx;
|
||||
use orchid_extension::tree::{GenTokTree, x_tok};
|
||||
|
||||
use crate::macros::mactree::{Ph, PhKind};
|
||||
|
||||
#[derive(Clone, Coding)]
|
||||
pub struct PhAtom(orchid_api::TStr, PhKind);
|
||||
impl PhAtom {
|
||||
pub async fn to_full(&self, ctx: &SysCtx) -> Ph {
|
||||
Ph { kind: self.1, name: ctx.i().ex(self.0).await }
|
||||
}
|
||||
}
|
||||
impl Atomic for PhAtom {
|
||||
type Data = Self;
|
||||
type Variant = ThinVariant;
|
||||
}
|
||||
impl ThinAtom for PhAtom {
|
||||
async fn print(&self, ctx: SysCtx) -> FmtUnit {
|
||||
Ph { name: ctx.i().ex(self.0).await, kind: self.1 }.to_string().into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PhLexer;
|
||||
impl Lexer for PhLexer {
|
||||
const CHAR_FILTER: &'static [std::ops::RangeInclusive<char>] = &['$'..='$', '.'..='.'];
|
||||
async fn lex<'a>(line: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree)> {
|
||||
let (tail, name, phkind) = if let Some(tail) = line.strip_prefix("$")
|
||||
&& tail.starts_with(name_start)
|
||||
{
|
||||
let name = tail.split_once(|c| !name_char(c)).map_or("", |(h, _)| h);
|
||||
let tail = tail.split_at(name.len()).1;
|
||||
(tail, name, PhKind::Scalar)
|
||||
} else {
|
||||
async fn name_and_prio<'a>(
|
||||
tail: &'a str,
|
||||
ctx: &'a LexContext<'a>,
|
||||
) -> OrcRes<(&'a str, u8, &'a str)> {
|
||||
let name = tail.split_once(|c| !name_char(c)).map_or("", |(h, _)| h);
|
||||
let tail = tail.split_at(name.len()).1;
|
||||
let (prio, tail) = match tail.strip_prefix(":") {
|
||||
None => (0, tail),
|
||||
Some(tail) => {
|
||||
let prio = tail.split_once(|c: char| c.is_ascii_digit()).map_or("", |(h, _)| h);
|
||||
let tail = tail.split_at(prio.len()).1;
|
||||
if let Ok(prio_num) = prio.parse::<u8>() {
|
||||
(prio_num, tail)
|
||||
} else {
|
||||
return Err(mk_errv(
|
||||
ctx.ctx.i().i("Invalid priority, must be 0-255").await,
|
||||
format!("{prio} is not a valid placeholder priority"),
|
||||
[ctx.pos_lt(prio.len(), tail)],
|
||||
));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok((name, prio, tail))
|
||||
}
|
||||
if let Some(tail) = line.strip_prefix("..$") {
|
||||
let (name, priority, tail) = name_and_prio(tail, ctx).await?;
|
||||
(tail, name, PhKind::Vector { at_least_one: false, priority })
|
||||
} else if let Some(tail) = line.strip_prefix("...$") {
|
||||
let (name, priority, tail) = name_and_prio(tail, ctx).await?;
|
||||
(tail, name, PhKind::Vector { at_least_one: true, priority })
|
||||
} else {
|
||||
return Err(err_not_applicable(ctx.ctx.i()).await);
|
||||
}
|
||||
};
|
||||
let ph_atom = PhAtom(ctx.ctx.i().i::<String>(name).await.to_api(), phkind);
|
||||
Ok((tail, x_tok(ph_atom).await.at(ctx.pos_tt(line, tail))))
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
|
||||
use never::Never;
|
||||
use orchid_base::format::{FmtCtx, FmtUnit};
|
||||
use orchid_base::interner::Tok;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_extension::atom::Atomic;
|
||||
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct RulePath {
|
||||
pub module: Sym,
|
||||
pub main_kw: Tok<String>,
|
||||
pub rule: u32,
|
||||
}
|
||||
impl RulePath {
|
||||
pub fn name(&self) -> String { format!("rule::{}::{}", self.main_kw, self.rule) }
|
||||
}
|
||||
impl fmt::Display for RulePath {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Rule {}::({})::{}", self.module, self.main_kw, self.rule)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum RecurState {
|
||||
Bottom,
|
||||
Recursive { path: RulePath, prev: Rc<RecurState> },
|
||||
}
|
||||
impl RecurState {
|
||||
pub fn base(path: RulePath) -> Self {
|
||||
RecurState::Recursive { path, prev: Rc::new(RecurState::Bottom) }
|
||||
}
|
||||
pub fn push(&self, new: RulePath) -> Option<Self> {
|
||||
let mut cur = self;
|
||||
while let Self::Recursive { path, prev } = cur {
|
||||
if &new == path {
|
||||
return None;
|
||||
}
|
||||
cur = prev;
|
||||
}
|
||||
Some(Self::Recursive { path: new, prev: Rc::new(self.clone()) })
|
||||
}
|
||||
}
|
||||
impl Atomic for RecurState {
|
||||
type Data = Option<()>;
|
||||
type Variant = OwnedVariant;
|
||||
}
|
||||
impl OwnedAtom for RecurState {
|
||||
type Refs = Never;
|
||||
|
||||
async fn val(&self) -> Cow<'_, Self::Data> {
|
||||
Cow::Owned(match self {
|
||||
Self::Bottom => None,
|
||||
Self::Recursive { .. } => Some(()),
|
||||
})
|
||||
}
|
||||
async fn print_atom<'a>(&'a self, _: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||
self.to_string().into()
|
||||
}
|
||||
}
|
||||
impl fmt::Display for RecurState {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Bottom => write!(f, "RecurState::Bottom"),
|
||||
Self::Recursive { path, prev } => write!(f, "{path}\n{prev}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
67
orchid-std/src/macros/requests.rs
Normal file
67
orchid-std/src/macros/requests.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use orchid_api_derive::{Coding, Hierarchy};
|
||||
use orchid_api_traits::Request;
|
||||
|
||||
use crate::api;
|
||||
use crate::macros::mactree::PhKind;
|
||||
|
||||
/* TODO:
|
||||
Create handlers and wrappers for these, probably expose MacTree to other crates.
|
||||
Define new extension binary to test the request functionality.
|
||||
*/
|
||||
|
||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||
#[extendable]
|
||||
pub enum MacroReq {
|
||||
CreateMacro(CreateMacro),
|
||||
CreateQuote(CreateQuote),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||
#[extends(MacroReq)]
|
||||
pub struct CreateMacro {
|
||||
pub module: api::TStrv,
|
||||
pub prio: Option<u64>,
|
||||
pub rules: Vec<CreateRule>,
|
||||
}
|
||||
impl Request for CreateMacro {
|
||||
type Response = api::ExprTicket;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Coding)]
|
||||
pub struct CreateRule {
|
||||
pub pattern: Vec<api::TokenTree>,
|
||||
pub body_name: api::TStr,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||
#[extends(MacroReq)]
|
||||
pub struct CreateQuote(MsgMacTree);
|
||||
impl Request for CreateQuote {
|
||||
type Response = api::ExprTicket;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Coding)]
|
||||
pub struct MsgMacTree {
|
||||
pub tok: MsgMacTok,
|
||||
pub location: api::Location,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Coding)]
|
||||
pub enum MsgMacTok {
|
||||
S(api::Paren, Vec<MsgMacTree>),
|
||||
Name(api::TStrv),
|
||||
/// Only permitted in arguments to `instantiate_tpl`
|
||||
Slot,
|
||||
Value(api::ExprTicket),
|
||||
Lambda(Box<MsgMacTree>, Vec<MsgMacTree>),
|
||||
/// Only permitted in "pattern" values produced by macro blocks, which are
|
||||
/// never accessed as variables by usercode
|
||||
Ph(MsgPh),
|
||||
Bottom(Vec<api::OrcError>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Coding)]
|
||||
pub struct MsgPh {
|
||||
kind: PhKind,
|
||||
name: api::TStr,
|
||||
}
|
||||
@@ -1,25 +1,22 @@
|
||||
use futures::FutureExt;
|
||||
use hashbrown::HashMap;
|
||||
use itertools::Itertools;
|
||||
use orchid_base::error::mk_errv;
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::sym;
|
||||
use orchid_base::tree::Paren;
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::coroutine_exec::ExecHandle;
|
||||
use orchid_extension::gen_expr::{GExpr, bot, call, sym_ref};
|
||||
use orchid_extension::gen_expr::{GExpr, call, sym_ref};
|
||||
use orchid_extension::system::SysCtx;
|
||||
|
||||
use crate::macros::macro_line::{Macro, Rule};
|
||||
use crate::macros::recur_state::{RecurState, RulePath};
|
||||
use crate::macros::macro_value::{Macro, Rule};
|
||||
use crate::macros::rule::matcher::{NamedMatcher, PriodMatcher};
|
||||
use crate::macros::rule::state::{MatchState, StateEntry};
|
||||
use crate::{MacTok, MacTree};
|
||||
|
||||
pub struct ResolveCtx<'a> {
|
||||
pub ctx: SysCtx,
|
||||
pub recur: RecurState,
|
||||
pub h: ExecHandle<'a>,
|
||||
pub named: HashMap<Sym, Vec<(&'a NamedMatcher, &'a Macro, &'a Rule)>>,
|
||||
pub priod: Vec<(&'a PriodMatcher, &'a Macro, &'a Rule)>,
|
||||
@@ -52,7 +49,7 @@ pub async fn resolve_seq(ctx: &mut ResolveCtx<'_>, val: &[MacTree]) -> Option<Ve
|
||||
any_changed = true;
|
||||
let (mac, rule, (state, tail)) = matches.into_iter().exactly_one().unwrap();
|
||||
let end = val.len() - tail.len();
|
||||
let body_call = mk_body_call(mac, rule, &state, &ctx.ctx, ctx.recur.clone()).await;
|
||||
let body_call = mk_body_call(mac, rule, &state, &ctx.ctx).await;
|
||||
std::mem::drop(state);
|
||||
val.splice(i..end, [MacTok::Value(ctx.h.register(body_call).await).at(Pos::None)]);
|
||||
i = end;
|
||||
@@ -66,10 +63,8 @@ pub async fn resolve_seq(ctx: &mut ResolveCtx<'_>, val: &[MacTree]) -> Option<Ve
|
||||
for (matcher, mac, rule) in &ctx.priod {
|
||||
let Some(state) = matcher.apply(&val, |_| false) else { continue };
|
||||
return Some(vec![
|
||||
MacTok::Value(
|
||||
ctx.h.register(mk_body_call(mac, rule, &state, &ctx.ctx, ctx.recur.clone()).await).await,
|
||||
)
|
||||
.at(Pos::None),
|
||||
MacTok::Value(ctx.h.register(mk_body_call(mac, rule, &state, &ctx.ctx).await).await)
|
||||
.at(Pos::None),
|
||||
]);
|
||||
}
|
||||
for expr in val.iter_mut() {
|
||||
@@ -81,30 +76,16 @@ pub async fn resolve_seq(ctx: &mut ResolveCtx<'_>, val: &[MacTree]) -> Option<Ve
|
||||
if any_changed { Some(val) } else { None }
|
||||
}
|
||||
|
||||
async fn mk_body_call(
|
||||
mac: &Macro,
|
||||
rule: &Rule,
|
||||
state: &MatchState<'_>,
|
||||
ctx: &SysCtx,
|
||||
recur: RecurState,
|
||||
) -> GExpr {
|
||||
let rule_path =
|
||||
RulePath { module: mac.0.module.clone(), main_kw: mac.0.own_kws[0].clone(), rule: rule.index };
|
||||
let Some(new_recur) = recur.push(rule_path.clone()) else {
|
||||
return bot(mk_errv(
|
||||
ctx.i().i("Circular macro dependency").await,
|
||||
format!("The definition of {rule_path} is circular"),
|
||||
[rule.pos.clone()],
|
||||
));
|
||||
};
|
||||
let mut call_args = vec![sym_ref(mac.0.module.suffix([rule.body_name.clone()], ctx.i()).await)];
|
||||
async fn mk_body_call(mac: &Macro, rule: &Rule, state: &MatchState<'_>, ctx: &SysCtx) -> GExpr {
|
||||
let mut call_args = vec![];
|
||||
for name in rule.placeholders.iter() {
|
||||
call_args.push(match state.get(name).expect("Missing state entry for placeholder") {
|
||||
StateEntry::Scalar(scal) => (**scal).clone().to_expr().await,
|
||||
StateEntry::Vec(vec) => MacTok::S(Paren::Round, vec.to_vec()).at(Pos::None).to_expr().await,
|
||||
});
|
||||
}
|
||||
call_args
|
||||
.push(call([sym_ref(sym!(macros::resolve_recur; ctx.i()).await), new_recur.to_expr().await]));
|
||||
call(call_args)
|
||||
call(sym_ref(sym!(macros::lower; ctx.i()).await), [call(
|
||||
sym_ref(mac.0.module.suffix([rule.body_name.clone()], ctx.i()).await),
|
||||
call_args,
|
||||
)])
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ pub fn vec_match<'a>(
|
||||
}
|
||||
None
|
||||
},
|
||||
// XXX predict heap space usage and allocation count
|
||||
VecMatcher::Middle { left, left_sep, mid, right_sep, right, key_order } => {
|
||||
if seq.len() < left_sep.len() + right_sep.len() {
|
||||
return None;
|
||||
|
||||
166
orchid-std/src/macros/utils.rs
Normal file
166
orchid-std/src/macros/utils.rs
Normal file
@@ -0,0 +1,166 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use async_fn_stream::stream;
|
||||
use futures::StreamExt;
|
||||
use itertools::{Itertools, chain};
|
||||
use orchid_base::name::{NameLike, Sym, VPath};
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::gen_expr::sym_ref;
|
||||
use orchid_extension::tree::{GenMember, MemKind, cnst, lazy};
|
||||
|
||||
use crate::macros::macro_value::{Macro, MacroData, Matcher, Rule};
|
||||
use crate::macros::mactree::map_mactree_v;
|
||||
use crate::macros::rule::matcher::{NamedMatcher, PriodMatcher};
|
||||
use crate::{MacTok, MacTree};
|
||||
|
||||
pub(crate) fn mk_macro<B: ToExpr + Clone + 'static>(
|
||||
prio: Option<u64>,
|
||||
own_kws: impl IntoIterator<Item = &'static str>,
|
||||
rules: impl IntoIterator<Item = (Vec<MacTree>, B)>,
|
||||
) -> Vec<GenMember> {
|
||||
let own_kws = own_kws.into_iter().collect_vec();
|
||||
let name = own_kws[0];
|
||||
let (patterns, bodies) = rules.into_iter().unzip::<_, _, Vec<Vec<MacTree>>, Vec<B>>();
|
||||
let main_const = lazy(true, name, async move |path, ctx| {
|
||||
let module = (Sym::new(path.split_last_seg().1.iter().cloned(), ctx.i()).await)
|
||||
.expect("Default macro in global root");
|
||||
MemKind::Const(
|
||||
Macro(Rc::new(MacroData {
|
||||
module,
|
||||
prio,
|
||||
rules: stream(async |mut h| {
|
||||
for (counter, pat) in patterns.into_iter().enumerate() {
|
||||
let mut placeholders = Vec::new();
|
||||
map_mactree_v(&pat, &mut false, &mut |tt| {
|
||||
if let MacTok::Ph(ph) = &*tt.tok {
|
||||
placeholders.push(ph.name.clone())
|
||||
}
|
||||
None
|
||||
});
|
||||
let pattern = match prio {
|
||||
Some(_) => Matcher::Priod(PriodMatcher::new(&pat, ctx.i()).await.unwrap()),
|
||||
None => Matcher::Named(NamedMatcher::new(&pat, ctx.i()).await.unwrap()),
|
||||
};
|
||||
h.emit(Rule {
|
||||
glossary: pat.iter().flat_map(|t| t.glossary()).cloned().collect(),
|
||||
pattern,
|
||||
placeholders,
|
||||
body_name: ctx.i().i(&format!("({name})::{counter}")).await,
|
||||
})
|
||||
.await;
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
.await,
|
||||
}))
|
||||
.to_expr()
|
||||
.await,
|
||||
)
|
||||
});
|
||||
let kw_consts = own_kws[1..].iter().flat_map(|kw| {
|
||||
lazy(true, kw, async |path, ctx| {
|
||||
let name = VPath::new(path.split_last_seg().1.iter().cloned())
|
||||
.name_with_suffix(ctx.i().i(*kw).await)
|
||||
.to_sym(ctx.i())
|
||||
.await;
|
||||
MemKind::Const(sym_ref(name))
|
||||
})
|
||||
});
|
||||
let body_consts = (bodies.into_iter().enumerate())
|
||||
.flat_map(|(counter, body)| cnst(false, &format!("({name})::{counter}"), body));
|
||||
chain!(main_const, kw_consts, body_consts).collect()
|
||||
}
|
||||
|
||||
macro_rules! mactree {
|
||||
($i:expr; $($body:tt)*) => {
|
||||
$crate::macros::utils::mactreev!($i; ($($body)*)).remove(0)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! mactreev {
|
||||
(@RECUR $i:expr; $ret:ident) => {};
|
||||
(@RECUR $i:expr; $ret:ident "..$" $name:ident $prio:literal $($tail:tt)*) => {
|
||||
ret.push(MacTok::Ph(Ph{
|
||||
name: i.i(stringify!($name)).await,
|
||||
kind: PhKind::Vector{ at_least_one: false, priority: $prio }
|
||||
}).at(Pos::Inherit));
|
||||
mactreev!(@RECUR $i; $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $i:expr; $ret:ident "...$" $name:ident $prio:literal $($tail:tt)*) => {
|
||||
$ret.push(MacTok::Ph(Ph{
|
||||
name: $i.i(stringify!($name)).await,
|
||||
kind: $crate::macros::mactree::PhKind::Vector{ at_least_one: true, priority: $prio }
|
||||
}).at(orchid_base::location::Pos::Inherit));
|
||||
mactreev!(@RECUR $i; $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $i:expr; $ret:ident "$" $name:ident $($tail:tt)*) => {
|
||||
$ret.push(MacTok::Ph(Ph{
|
||||
name: $i.i(stringify!(name)).await,
|
||||
kind: $crate::macros::mactree::PhKind::Scalar
|
||||
}).at(orchid_base::location::Pos::Inherit));
|
||||
mactreev!(@RECUR $i; $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $i:expr; $ret:ident "'" $arg:expr ; $($tail:tt)*) => {
|
||||
$ret.push(MacTok::Value($arg).at(orchid_base::location::Pos::Inherit));
|
||||
mactreev!(@RECUR $i; $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $i:expr; $ret:ident "" $arg:expr ; $($tail:tt)*) => {
|
||||
$ret.push($arg);
|
||||
mactreev!(@RECUR $i; $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $i:expr; $ret:ident "l_" $arg:expr ; ($($body:tt)*) $($tail:tt)*) => {
|
||||
$ret.push(MacTok::Lambda(
|
||||
MacTok::Value($arg).at(orchid_base::location::Pos::Inherit),
|
||||
mactreev!(i; $($body)*)
|
||||
).at(Pos::Inherit));
|
||||
mactreev!(@RECUR $i; $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $i:expr; $ret:ident "l" $argh:tt $(:: $arg:tt)* ($($body:tt)*) $($tail:tt)*) => {
|
||||
$ret.push(MacTok::Lambda(
|
||||
MacTok::Name(sym!($argh $(:: $arg)*; $i).await).at(orchid_base::location::Pos::Inherit),
|
||||
mactreev!(i; $($body)*)
|
||||
).at(orchid_base::location::Pos::Inherit));
|
||||
mactreev!(@RECUR $i; $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $i:expr; $ret:ident ( $($body:tt)* ) $($tail:tt)*) => {
|
||||
$ret.push(
|
||||
MacTok::S(orchid_base::tree::Paren::Round, mactreev!($i; $($body)*))
|
||||
.at(orchid_base::location::Pos::Inherit)
|
||||
);
|
||||
mactreev!(@RECUR $i; $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $i:expr; $ret:ident [ $($body:tt)* ] $($tail:tt)*) => {
|
||||
$ret.push(
|
||||
MacTok::S(orchid_base::tree::Paren::Square, mactreev!($i; $($body)*))
|
||||
.at(orchid_base::location::Pos::Inherit)
|
||||
);
|
||||
mactreev!(@RECUR $i; $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $i:expr; $ret:ident { $($body:tt)* } $($tail:tt)*) => {
|
||||
$ret.push(
|
||||
MacTok::S(orchid_base::tree::Paren::Curly, mactreev!($i; $($body)*))
|
||||
.at(orchid_base::location::Pos::Inherit)
|
||||
);
|
||||
mactreev!(@RECUR $i; $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $i:expr; $ret:ident $nhead:tt $($tail:tt)*) => {
|
||||
mactreev!(@NAME_MUNCHER $i; $ret ($nhead) $($tail)*)
|
||||
};
|
||||
(@NAME_MUNCHER $i:expr; $ret:ident ($($munched:tt)*) :: $name:tt $($tail:tt)*) => {
|
||||
mactreev!(@NAME_MUNCHER $i; $ret ($($munched)* :: $name) $($tail)*)
|
||||
};
|
||||
(@NAME_MUNCHER $i:expr; $ret:ident ($($munched:tt)*) $($tail:tt)*) => {
|
||||
let sym = orchid_base::sym!($($munched)* ; $i).await;
|
||||
$ret.push(MacTok::Name(sym).at(orchid_base::location::Pos::Inherit));
|
||||
mactreev!(@RECUR $i; $ret $($tail)*);
|
||||
};
|
||||
($i:expr; ) => { Vec::new() };
|
||||
($i:expr; $($tail:tt)*) => {
|
||||
{
|
||||
let mut ret = Vec::<MacTree>::new();
|
||||
mactreev!(@RECUR $i; ret $($tail)*);
|
||||
ret
|
||||
}
|
||||
};
|
||||
}
|
||||
pub(crate) use {mactree, mactreev};
|
||||
@@ -2,7 +2,7 @@ use orchid_api_derive::Coding;
|
||||
use orchid_base::error::OrcRes;
|
||||
use orchid_base::format::FmtUnit;
|
||||
use orchid_base::number::Numeric;
|
||||
use orchid_extension::atom::{AtomFactory, Atomic, AtomicFeatures, ToAtom, TypAtom};
|
||||
use orchid_extension::atom::{AtomFactory, Atomic, AtomicFeatures, ToAtom, TAtom};
|
||||
use orchid_extension::atom_thin::{ThinAtom, ThinVariant};
|
||||
use orchid_extension::conv::TryFromExpr;
|
||||
use orchid_extension::expr::Expr;
|
||||
@@ -21,7 +21,7 @@ impl ThinAtom for Int {
|
||||
}
|
||||
impl TryFromExpr for Int {
|
||||
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
|
||||
TypAtom::<Int>::try_from_expr(expr).await.map(|t| t.value)
|
||||
TAtom::<Int>::try_from_expr(expr).await.map(|t| t.value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ impl TryFromExpr for Num {
|
||||
Ok(t) => return Ok(Num(Numeric::Int(t.0))),
|
||||
Err(e) => e,
|
||||
};
|
||||
match TypAtom::<Float>::try_from_expr(expr).await {
|
||||
match TAtom::<Float>::try_from_expr(expr).await {
|
||||
Ok(t) => Ok(Num(Numeric::Float(t.0))),
|
||||
Err(e2) => Err(e + e2),
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use orchid_api_traits::{Encode, Request};
|
||||
use orchid_base::error::{OrcRes, mk_errv};
|
||||
use orchid_base::format::{FmtCtx, FmtUnit};
|
||||
use orchid_base::interner::Tok;
|
||||
use orchid_extension::atom::{AtomMethod, Atomic, MethodSetBuilder, Supports, TypAtom};
|
||||
use orchid_extension::atom::{AtomMethod, Atomic, MethodSetBuilder, Supports, TAtom};
|
||||
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
|
||||
use orchid_extension::conv::TryFromExpr;
|
||||
use orchid_extension::expr::Expr;
|
||||
@@ -89,8 +89,8 @@ pub struct OrcString {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum OrcStringKind {
|
||||
Val(TypAtom<StrAtom>),
|
||||
Int(TypAtom<IntStrAtom>),
|
||||
Val(TAtom<StrAtom>),
|
||||
Int(TAtom<IntStrAtom>),
|
||||
}
|
||||
impl OrcString {
|
||||
pub async fn get_string(&self) -> Rc<String> {
|
||||
@@ -103,11 +103,11 @@ impl OrcString {
|
||||
|
||||
impl TryFromExpr for OrcString {
|
||||
async fn try_from_expr(expr: Expr) -> OrcRes<OrcString> {
|
||||
if let Ok(v) = TypAtom::<StrAtom>::try_from_expr(expr.clone()).await {
|
||||
if let Ok(v) = TAtom::<StrAtom>::try_from_expr(expr.clone()).await {
|
||||
return Ok(OrcString { ctx: expr.ctx(), kind: OrcStringKind::Val(v) });
|
||||
}
|
||||
let ctx = expr.ctx();
|
||||
match TypAtom::<IntStrAtom>::try_from_expr(expr).await {
|
||||
match TAtom::<IntStrAtom>::try_from_expr(expr).await {
|
||||
Ok(t) => Ok(OrcString { ctx: t.untyped.ctx().clone(), kind: OrcStringKind::Int(t) }),
|
||||
Err(e) => Err(mk_errv(ctx.i().i("A string was expected").await, "", e.pos_iter())),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user