forked from Orchid/orchid
New macro system and stdlib additions
This commit is contained in:
@@ -19,7 +19,9 @@ orchid-extension = { version = "0.1.0", path = "../orchid-extension", features =
|
||||
"tokio",
|
||||
] }
|
||||
ordered-float = "5.0.0"
|
||||
pastey = "0.1.1"
|
||||
rust_decimal = "1.38.0"
|
||||
subslice-offset = "0.1.1"
|
||||
substack = "1.1.1"
|
||||
tokio = { version = "1.47.1", features = ["full"] }
|
||||
|
||||
|
||||
@@ -2,8 +2,11 @@ mod macros;
|
||||
mod std;
|
||||
|
||||
pub use std::number::num_atom::{Float, HomoArray, Int, Num};
|
||||
pub use std::option::OrcOpt;
|
||||
pub use std::reflection::sym_atom::{SymAtom, sym_expr};
|
||||
pub use std::std_system::StdSystem;
|
||||
pub use std::string::str_atom::OrcString;
|
||||
pub use std::tuple::{HomoTpl, Tpl, Tuple, UntypedTuple};
|
||||
|
||||
pub use macros::macro_system::MacroSystem;
|
||||
pub use macros::mactree::{MacTok, MacTree};
|
||||
|
||||
@@ -4,12 +4,13 @@ use never::Never;
|
||||
use orchid_base::format::fmt;
|
||||
use orchid_extension::atom::{Atomic, TAtom};
|
||||
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::coroutine_exec::exec;
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::gen_expr::GExpr;
|
||||
|
||||
use crate::macros::mactree::{MacTok, MacTree, map_mactree};
|
||||
use crate::macros::mactree::{MacTok, MacTree};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InstantiateTplCall {
|
||||
@@ -35,24 +36,24 @@ impl OwnedAtom for InstantiateTplCall {
|
||||
self.clone().call(arg).await
|
||||
}
|
||||
async fn call(mut self, arg: Expr) -> GExpr {
|
||||
exec("macros::instantiate_tpl", async move |mut h| {
|
||||
exec(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),
|
||||
Err(_) => panic!("Expected a macro param, found {}", fmt(&arg, &i()).await),
|
||||
Ok(t) => self.argv.push(own(&t).await),
|
||||
};
|
||||
if self.argv.len() < self.argc {
|
||||
return self.to_expr().await;
|
||||
return self.to_gen().await;
|
||||
}
|
||||
let mut args = self.argv.into_iter();
|
||||
let ret = map_mactree(&self.tpl, &mut false, &mut |mt| match mt.tok() {
|
||||
let ret = self.tpl.map(&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
|
||||
ret.to_gen().await
|
||||
})
|
||||
.await
|
||||
.to_expr()
|
||||
.to_gen()
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,12 @@ use orchid_base::parse::{
|
||||
use orchid_base::sym;
|
||||
use orchid_base::tree::Paren;
|
||||
use orchid_extension::atom::TAtom;
|
||||
use orchid_extension::context::i;
|
||||
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::mactree::{MacTok, MacTree, MacTreeSeq};
|
||||
use crate::macros::ph_lexer::PhAtom;
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -40,21 +41,18 @@ impl Parser for LetLine {
|
||||
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());
|
||||
let macro_input =
|
||||
MacTok::S(Paren::Round, dealias_mac_v(&aliased, &ctx, &rep).await).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)],
|
||||
)]))
|
||||
Ok(call(sym_ref(sym!(macros::resolve; i())), [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();
|
||||
pub async fn dealias_mac_v(aliased: &MacTreeSeq, ctx: &ConstCtx, rep: &Reporter) -> MacTreeSeq {
|
||||
let keys = aliased.glossary().iter().cloned().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 {
|
||||
@@ -65,13 +63,13 @@ pub async fn dealias_mac_v(aliased: Vec<MacTree>, ctx: &ConstCtx, rep: &Reporter
|
||||
},
|
||||
}
|
||||
}
|
||||
map_mactree_v(&aliased, &mut false, &mut |tree| match &*tree.tok {
|
||||
aliased.map(&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> {
|
||||
pub async fn parse_tokv(line: PSnippet<'_>, ctx: &impl ParseCtx) -> MacTreeSeq {
|
||||
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();
|
||||
@@ -86,14 +84,14 @@ pub async fn parse_tokv(line: PSnippet<'_>, ctx: &impl ParseCtx) -> Vec<MacTree>
|
||||
.await,
|
||||
),
|
||||
};
|
||||
all
|
||||
MacTreeSeq::new(all)
|
||||
} else {
|
||||
parse_tokv_no_lambdas(&line, ctx).await
|
||||
MacTreeSeq::new(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
|
||||
stream::iter(line).filter_map(|tt| parse_tok(tt, ctx)).collect::<Vec<_>>().await
|
||||
}
|
||||
|
||||
pub async fn parse_tok(tree: &PTokTree, ctx: &impl ParseCtx) -> Option<MacTree> {
|
||||
@@ -115,7 +113,7 @@ pub async fn parse_tok(tree: &PTokTree, ctx: &impl ParseCtx) -> Option<MacTree>
|
||||
},
|
||||
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),
|
||||
Ok(ta) => MacTok::Ph(ta.value.to_full().await),
|
||||
},
|
||||
PTok::NewExpr(never) => match *never {},
|
||||
PTok::LambdaHead(_) => panic!("Lambda-head handled in the sequence parser"),
|
||||
|
||||
@@ -1,97 +1,65 @@
|
||||
use hashbrown::HashMap;
|
||||
use itertools::{Itertools, chain};
|
||||
use orchid_base::error::Reporter;
|
||||
use orchid_base::{clone, sym};
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::TAtom;
|
||||
use orchid_extension::atom_owned::own;
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::coroutine_exec::exec;
|
||||
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, MemKind, fun, lazy, prefix};
|
||||
use substack::Substack;
|
||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||
|
||||
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};
|
||||
use crate::macros::mactree::MacTree;
|
||||
use crate::macros::resolve::resolve;
|
||||
use crate::macros::utils::{build_macro, mactree, mactreev};
|
||||
|
||||
pub fn gen_macro_lib() -> Vec<GenMember> {
|
||||
pub async fn gen_macro_lib() -> Vec<GenMember> {
|
||||
prefix("macros", [
|
||||
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, "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::<TAtom<Macro>>(sym_ref(n.clone())).await else { continue };
|
||||
let mac = own(mac).await;
|
||||
macros.entry(mac.canonical_name(&ctx).await).or_insert(mac);
|
||||
}
|
||||
}
|
||||
let mut named = HashMap::new();
|
||||
let mut priod = Vec::new();
|
||||
for (_, mac) in macros.iter() {
|
||||
for rule in mac.0.rules.iter() {
|
||||
if rule.glossary.is_subset(tpl.glossary()) {
|
||||
match &rule.pattern {
|
||||
Matcher::Named(m) =>
|
||||
named.entry(m.head()).or_insert(Vec::new()).push((m, mac, rule)),
|
||||
Matcher::Priod(p) => priod.push((mac.0.prio, (p, mac, rule))),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let priod = priod.into_iter().sorted_unstable_by_key(|(p, _)| *p).map(|(_, r)| r).collect();
|
||||
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 {
|
||||
Some(out_tree) => out_tree.to_expr().await,
|
||||
None => tpl.to_expr().await,
|
||||
}
|
||||
})
|
||||
.await
|
||||
}),
|
||||
fun(true, "resolve", async |tpl: TAtom<MacTree>| resolve(own(&tpl).await).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() }
|
||||
}),
|
||||
prefix("common", [
|
||||
build_macro(None, ["..", "_"]).finish(),
|
||||
build_macro(Some(1), ["+"])
|
||||
.rule(mactreev!("...$" lhs 0 macros::common::+ "...$" rhs 1), [async |[lhs, rhs]| {
|
||||
call(sym_ref(sym!(std::number::add; i())), [resolve(lhs).await, resolve(rhs).await])
|
||||
}])
|
||||
.finish(),
|
||||
build_macro(Some(2), ["*"])
|
||||
.rule(mactreev!("...$" lhs 0 macros::common::* "...$" rhs 1), [async |[lhs, rhs]| {
|
||||
call(sym_ref(sym!(std::number::mul; i())), [resolve(lhs).await, resolve(rhs).await])
|
||||
}])
|
||||
.finish(),
|
||||
build_macro(None, ["comma_list", ","])
|
||||
.rule(
|
||||
mactreev!(macros::common::comma_list ( "...$" head 0 macros::common::, "...$" tail 1)),
|
||||
[async |[head, tail]| {
|
||||
call(sym_ref(sym!(std::tuple::cat; i())), [
|
||||
call(sym_ref(sym!(std::tuple::one; i())), [head.to_gen().await]),
|
||||
resolve(mactree!(macros::common::comma_list "push" tail ;)).await,
|
||||
])
|
||||
}],
|
||||
)
|
||||
.rule(mactreev!(macros::common::comma_list ( "...$" final_tail 0 )), [async |[tail]| {
|
||||
call(sym_ref(sym!(std::tuple::one; i())), [tail.to_gen().await])
|
||||
}])
|
||||
.rule(mactreev!(macros::common::comma_list()), [async |[]| {
|
||||
sym_ref(sym!(std::tuple::empty; i()))
|
||||
}])
|
||||
.finish(),
|
||||
build_macro(None, ["semi_list", ";"])
|
||||
.rule(
|
||||
mactreev!(macros::common::semi_list ( "...$" head 0 macros::common::; "...$" tail 1)),
|
||||
[async |[head, tail]| {
|
||||
call(sym_ref(sym!(std::tuple::cat; i())), [
|
||||
call(sym_ref(sym!(std::tuple::one; i())), [resolve(head).await]),
|
||||
resolve(mactree!(macros::common::semi_list "push" tail ;)).await,
|
||||
])
|
||||
}],
|
||||
)
|
||||
.rule(mactreev!(macros::common::semi_list ( "...$" final_tail 0 )), [async |[tail]| {
|
||||
call(sym_ref(sym!(std::tuple::one; i())), [resolve(tail).await])
|
||||
}])
|
||||
.rule(mactreev!(macros::common::semi_list()), [async |[]| {
|
||||
sym_ref(sym!(std::tuple::empty; i()))
|
||||
}])
|
||||
.finish(),
|
||||
]),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -12,14 +12,15 @@ use orchid_base::parse::{
|
||||
use orchid_base::tree::{Paren, Token};
|
||||
use orchid_base::{clone, sym};
|
||||
use orchid_extension::atom::TAtom;
|
||||
use orchid_extension::context::i;
|
||||
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::rule::matcher::{NamedMatcher, PriodMatcher};
|
||||
use crate::macros::macro_value::{Macro, MacroData, Rule};
|
||||
use crate::macros::mactree::MacTreeSeq;
|
||||
use crate::macros::rule::matcher::Matcher;
|
||||
use crate::{Int, MacTok};
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -114,7 +115,7 @@ impl Parser for MacroLine {
|
||||
};
|
||||
let pattern = parse_tokv(pattern, &ctx).await;
|
||||
let mut placeholders = Vec::new();
|
||||
map_mactree_v(&pattern, &mut false, &mut |tok| {
|
||||
pattern.map(&mut false, &mut |tok| {
|
||||
if let MacTok::Ph(ph) = tok.tok() {
|
||||
placeholders.push((ph.clone(), tok.pos()))
|
||||
}
|
||||
@@ -123,23 +124,21 @@ impl Parser for MacroLine {
|
||||
let mut body_mactree = parse_tokv(body, &ctx).await;
|
||||
for (ph, ph_pos) in placeholders.iter().rev() {
|
||||
let name = ctx.module().suffix([ph.name.clone()], ctx.i()).await;
|
||||
body_mactree = vec![
|
||||
MacTok::Lambda(MacTok::Name(name).at(ph_pos.clone()), body_mactree).at(ph_pos.clone()),
|
||||
]
|
||||
body_mactree =
|
||||
MacTreeSeq::new([
|
||||
MacTok::Lambda(MacTok::Name(name).at(ph_pos.clone()), body_mactree).at(ph_pos.clone())
|
||||
])
|
||||
}
|
||||
let body_sr = body.sr();
|
||||
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;
|
||||
let body = dealias_mac_v(&body_mactree, &ctx, &rep).await;
|
||||
let macro_input = MacTok::S(Paren::Round, body).at(body_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),
|
||||
[macro_input.to_expr().await],
|
||||
)]))
|
||||
Ok(call(sym_ref(sym!(macros::resolve; i())), [macro_input.to_gen().await]))
|
||||
}))
|
||||
}
|
||||
let mac_cell = Rc::new(OnceCell::new());
|
||||
@@ -152,20 +151,15 @@ impl Parser for MacroLine {
|
||||
let rep = Reporter::new();
|
||||
let rules = rules.borrow_mut().take().expect("once cell initializer runs");
|
||||
let rules = stream::iter(rules)
|
||||
.then(|(body_name, placeholders, pattern_macv)| {
|
||||
.then(|(body_name, placeholders, pattern_rel)| {
|
||||
let cctx = &cctx;
|
||||
let rep = &rep;
|
||||
let prio = &prio;
|
||||
async move {
|
||||
let pattern_abs = dealias_mac_v(pattern_macv, cctx, rep).await;
|
||||
let glossary = glossary_v(&pattern_abs).collect();
|
||||
let pattern_res = match prio {
|
||||
None => NamedMatcher::new(&pattern_abs, cctx.i()).await.map(Matcher::Named),
|
||||
Some(_) => PriodMatcher::new(&pattern_abs, cctx.i()).await.map(Matcher::Priod),
|
||||
};
|
||||
let pattern = dealias_mac_v(&pattern_rel, cctx, rep).await;
|
||||
let pattern_res = Matcher::new(pattern.clone()).await;
|
||||
let placeholders = placeholders.into_iter().map(|(ph, _)| ph.name).collect_vec();
|
||||
match pattern_res {
|
||||
Ok(pattern) => Some(Rule { body_name, pattern, glossary, placeholders }),
|
||||
Ok(matcher) => Some(Rule { body_name, matcher, pattern, placeholders }),
|
||||
Err(e) => {
|
||||
rep.report(e);
|
||||
None
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
use orchid_base::interner::Interner;
|
||||
use never::Never;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::reqnot::Receipt;
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::{AtomDynfo, AtomicFeatures};
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::entrypoint::ExtReq;
|
||||
use orchid_extension::lexer::LexerObj;
|
||||
use orchid_extension::other_system::SystemHandle;
|
||||
use orchid_extension::parser::ParserObj;
|
||||
use orchid_extension::system::{System, SystemCard};
|
||||
use orchid_extension::system_ctor::SystemCtor;
|
||||
use orchid_extension::tree::GenMember;
|
||||
use orchid_extension::tree::{GenMember, merge_trivial};
|
||||
|
||||
use crate::macros::instantiate_tpl::InstantiateTplCall;
|
||||
use crate::macros::let_line::LetLine;
|
||||
@@ -17,8 +18,10 @@ use crate::macros::macro_lib::gen_macro_lib;
|
||||
use crate::macros::macro_line::MacroLine;
|
||||
use crate::macros::macro_value::Macro;
|
||||
use crate::macros::mactree_lexer::MacTreeLexer;
|
||||
use crate::macros::match_macros::gen_match_macro_lib;
|
||||
use crate::macros::ph_lexer::{PhAtom, PhLexer};
|
||||
use crate::macros::requests::MacroReq;
|
||||
use crate::macros::std_macros::gen_std_macro_lib;
|
||||
use crate::macros::utils::MacroBodyArgCollector;
|
||||
use crate::{MacTree, StdSystem};
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -32,26 +35,30 @@ impl SystemCtor for MacroSystem {
|
||||
}
|
||||
impl SystemCard for MacroSystem {
|
||||
type Ctor = Self;
|
||||
type Req = MacroReq;
|
||||
type Req = Never;
|
||||
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn AtomDynfo>>> {
|
||||
[
|
||||
Some(InstantiateTplCall::dynfo()),
|
||||
Some(MacTree::dynfo()),
|
||||
Some(Macro::dynfo()),
|
||||
Some(PhAtom::dynfo()),
|
||||
Some(MacroBodyArgCollector::dynfo()),
|
||||
]
|
||||
}
|
||||
}
|
||||
impl System for MacroSystem {
|
||||
async fn request(_: ExtReq<'_>, req: Self::Req) -> Receipt<'_> { todo!("Handle {req:?}") }
|
||||
async fn prelude(i: &Interner) -> Vec<Sym> {
|
||||
async fn request(_: ExtReq<'_>, req: Never) -> Receipt<'_> { match req {} }
|
||||
async fn prelude() -> Vec<Sym> {
|
||||
vec![
|
||||
sym!(macros::resolve; i).await,
|
||||
sym!(macros::common::+; i).await,
|
||||
sym!(macros::common::*; i).await,
|
||||
sym!(macros::common::+; i()),
|
||||
sym!(macros::common::*; i()),
|
||||
sym!(macros::common::,; i()),
|
||||
sym!(std::tuple::t; i()),
|
||||
]
|
||||
}
|
||||
fn lexers() -> Vec<LexerObj> { vec![&MacTreeLexer, &PhLexer] }
|
||||
fn parsers() -> Vec<ParserObj> { vec![&LetLine, &MacroLine] }
|
||||
fn env() -> Vec<GenMember> { gen_macro_lib() }
|
||||
async fn env() -> Vec<GenMember> {
|
||||
merge_trivial([gen_macro_lib().await, gen_std_macro_lib().await, gen_match_macro_lib().await])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
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 orchid_extension::context::i;
|
||||
|
||||
use crate::macros::rule::matcher::{NamedMatcher, PriodMatcher};
|
||||
use crate::macros::mactree::MacTreeSeq;
|
||||
use crate::macros::rule::matcher::Matcher;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MacroData {
|
||||
@@ -21,23 +21,18 @@ pub struct MacroData {
|
||||
#[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
|
||||
pub async fn canonical_name(&self) -> Sym {
|
||||
self.0.module.suffix([self.0.rules[0].body_name.clone()], &i()).await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Rule {
|
||||
pub pattern: Matcher,
|
||||
pub glossary: HashSet<Sym>,
|
||||
pub pattern: MacTreeSeq,
|
||||
pub matcher: Matcher,
|
||||
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;
|
||||
|
||||
@@ -5,10 +5,9 @@ use std::rc::Rc;
|
||||
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::error::OrcErrv;
|
||||
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
|
||||
use orchid_base::interner::Tok;
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::name::Sym;
|
||||
@@ -16,16 +15,89 @@ use orchid_base::tl_cache;
|
||||
use orchid_base::tree::{Paren, indent};
|
||||
use orchid_extension::atom::Atomic;
|
||||
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant};
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::gen_expr::{GExpr, arg, bot, call, lambda, sym_ref};
|
||||
use orchid_extension::system::SysCtx;
|
||||
use substack::Substack;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LowerCtx<'a> {
|
||||
pub sys: SysCtx,
|
||||
pub rep: &'a Reporter,
|
||||
fn union_rc_sets(seq: impl IntoIterator<Item = Rc<HashSet<Sym>>>) -> Rc<HashSet<Sym>> {
|
||||
let mut acc = Rc::<HashSet<Sym>>::default();
|
||||
for right in seq {
|
||||
if acc.is_empty() {
|
||||
acc = right;
|
||||
continue;
|
||||
}
|
||||
if right.is_empty() {
|
||||
continue;
|
||||
}
|
||||
acc = match (Rc::try_unwrap(acc), Rc::try_unwrap(right)) {
|
||||
(Ok(mut left), Ok(right)) => {
|
||||
left.extend(right);
|
||||
Rc::new(left)
|
||||
},
|
||||
(Ok(mut owned), Err(borrowed)) | (Err(borrowed), Ok(mut owned)) => {
|
||||
owned.extend(borrowed.iter().cloned());
|
||||
Rc::new(owned)
|
||||
},
|
||||
(Err(left), Err(right)) => Rc::new(left.union(&right).cloned().collect()),
|
||||
}
|
||||
}
|
||||
acc
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MacTreeSeq {
|
||||
pub items: Rc<Vec<MacTree>>,
|
||||
pub top_glossary: Rc<HashSet<Sym>>,
|
||||
pub glossary: Rc<HashSet<Sym>>,
|
||||
}
|
||||
impl MacTreeSeq {
|
||||
pub fn new(i: impl IntoIterator<Item = MacTree>) -> Self {
|
||||
let mut items = Vec::new();
|
||||
let mut top_glossary = HashSet::new();
|
||||
let mut glossary = HashSet::new();
|
||||
for item in i {
|
||||
glossary.extend(item.glossary().iter().cloned());
|
||||
if let MacTok::Name(n) = item.tok() {
|
||||
top_glossary.insert(n.clone());
|
||||
}
|
||||
items.push(item);
|
||||
}
|
||||
Self { items: Rc::new(items), top_glossary: Rc::new(top_glossary), glossary: Rc::new(glossary) }
|
||||
}
|
||||
pub fn map<F: FnMut(MacTree) -> Option<MacTree>>(&self, changed: &mut bool, map: &mut F) -> Self {
|
||||
Self::new(self.items.iter().map(|tree| ro(changed, |changed| tree.map(changed, map))))
|
||||
}
|
||||
pub fn glossary(&self) -> &HashSet<Sym> { &self.glossary }
|
||||
pub fn concat(self, other: Self) -> Self {
|
||||
if self.items.is_empty() {
|
||||
return other;
|
||||
} else if other.items.is_empty() {
|
||||
return self;
|
||||
}
|
||||
let items = match (Rc::try_unwrap(self.items), Rc::try_unwrap(other.items)) {
|
||||
(Ok(mut left), Ok(mut right)) => {
|
||||
left.append(&mut right);
|
||||
left
|
||||
},
|
||||
(Ok(mut left), Err(right)) => {
|
||||
left.extend_from_slice(&right[..]);
|
||||
left
|
||||
},
|
||||
(Err(left), Ok(mut right)) => {
|
||||
right.splice(0..0, left.iter().cloned());
|
||||
right
|
||||
},
|
||||
(Err(left), Err(right)) => left.iter().chain(&right[..]).cloned().collect(),
|
||||
};
|
||||
Self {
|
||||
items: Rc::new(items),
|
||||
top_glossary: union_rc_sets([self.top_glossary, other.top_glossary]),
|
||||
glossary: union_rc_sets([self.glossary, other.glossary]),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Format for MacTreeSeq {
|
||||
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||
mtreev_fmt(&self.items[..], c).await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -38,66 +110,21 @@ impl MacTree {
|
||||
pub fn tok(&self) -> &MacTok { &self.tok }
|
||||
pub fn pos(&self) -> Pos { self.pos.clone() }
|
||||
pub fn glossary(&self) -> &HashSet<Sym> { &self.glossary }
|
||||
pub async fn lower(&self, ctx: LowerCtx<'_>, args: Substack<'_, Sym>) -> GExpr {
|
||||
let expr = match self.tok() {
|
||||
MacTok::Bottom(e) => bot(e.clone()),
|
||||
MacTok::Lambda(arg, body) => {
|
||||
let MacTok::Name(name) = &*arg.tok else {
|
||||
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()],
|
||||
));
|
||||
};
|
||||
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)
|
||||
pub fn map<F: FnMut(Self) -> Option<Self>>(&self, changed: &mut bool, map: &mut F) -> Self {
|
||||
let tok = match map(self.clone()) {
|
||||
Some(new_tok) => {
|
||||
*changed = true;
|
||||
return new_tok;
|
||||
},
|
||||
MacTok::Name(name) => match args.iter().enumerate().find(|(_, n)| *n == name) {
|
||||
None => sym_ref(name.clone()),
|
||||
Some((i, _)) => arg((args.len() - i - 1) as u64),
|
||||
None => match &*self.tok {
|
||||
MacTok::Lambda(arg, body) =>
|
||||
MacTok::Lambda(ro(changed, |changed| arg.map(changed, map)), body.map(changed, map)),
|
||||
MacTok::Name(_) | MacTok::Value(_) => return self.clone(),
|
||||
MacTok::Slot | MacTok::Ph(_) | MacTok::Bottom(_) => return self.clone(),
|
||||
MacTok::S(p, body) => MacTok::S(*p, body.map(changed, map)),
|
||||
},
|
||||
MacTok::Ph(ph) => {
|
||||
return bot(mk_errv(
|
||||
ctx.sys.i().i("Placeholder in value").await,
|
||||
format!("Placeholder {ph} is only supported in macro patterns"),
|
||||
[self.pos()],
|
||||
));
|
||||
},
|
||||
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(..) => {
|
||||
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()],
|
||||
));
|
||||
},
|
||||
MacTok::Slot => panic!("Uninstantiated template should never be exposed"),
|
||||
MacTok::Value(v) => v.clone().to_expr().await,
|
||||
};
|
||||
expr.at(self.pos())
|
||||
if *changed { tok.at(self.pos()) } else { self.clone() }
|
||||
}
|
||||
}
|
||||
impl Atomic for MacTree {
|
||||
@@ -119,35 +146,31 @@ impl Format for MacTree {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn lower_v(v: &[MacTree], ctx: LowerCtx<'_>, args: Substack<'_, Sym>) -> Vec<GExpr> {
|
||||
join_all(v.iter().map(|t| t.lower(ctx.clone(), args.clone())).collect::<Vec<_>>()).await
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MacTok {
|
||||
S(Paren, Vec<MacTree>),
|
||||
S(Paren, MacTreeSeq),
|
||||
Name(Sym),
|
||||
/// Only permitted in arguments to `instantiate_tpl`
|
||||
Slot,
|
||||
Value(Expr),
|
||||
Lambda(MacTree, Vec<MacTree>),
|
||||
Lambda(MacTree, MacTreeSeq),
|
||||
/// Only permitted in "pattern" values produced by macro blocks, which are
|
||||
/// never accessed as variables by usercode
|
||||
Ph(Ph),
|
||||
Bottom(OrcErrv),
|
||||
}
|
||||
impl MacTok {
|
||||
pub fn build_glossary(&self) -> HashSet<Sym> {
|
||||
pub fn build_glossary(&self) -> Rc<HashSet<Sym>> {
|
||||
match self {
|
||||
MacTok::Bottom(_) | MacTok::Ph(_) | MacTok::Slot | MacTok::Value(_) => HashSet::new(),
|
||||
MacTok::Name(sym) => HashSet::from([sym.clone()]),
|
||||
MacTok::S(_, body) => body.iter().flat_map(|mt| &*mt.glossary).cloned().collect(),
|
||||
MacTok::Bottom(_) | MacTok::Ph(_) | MacTok::Slot | MacTok::Value(_) => Rc::default(),
|
||||
MacTok::Name(sym) => Rc::new(HashSet::from([sym.clone()])),
|
||||
MacTok::S(_, body) => union_rc_sets(body.items.iter().map(|mt| mt.glossary.clone())),
|
||||
MacTok::Lambda(arg, body) =>
|
||||
body.iter().chain([arg]).flat_map(|mt| &*mt.glossary).cloned().collect(),
|
||||
union_rc_sets(body.items.iter().chain([arg]).map(|mt| mt.glossary.clone())),
|
||||
}
|
||||
}
|
||||
pub fn at(self, pos: impl Into<Pos>) -> MacTree {
|
||||
MacTree { pos: pos.into(), glossary: Rc::new(self.build_glossary()), tok: Rc::new(self) }
|
||||
MacTree { pos: pos.into(), glossary: self.build_glossary(), tok: Rc::new(self) }
|
||||
}
|
||||
}
|
||||
impl Format for MacTok {
|
||||
@@ -157,7 +180,7 @@ impl Format for MacTok {
|
||||
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]),
|
||||
.units([arg.print(c).boxed_local().await, b.print(c).await]),
|
||||
Self::Name(n) => format!("{n}").into(),
|
||||
Self::Ph(ph) => format!("{ph}").into(),
|
||||
Self::S(p, body) => match *p {
|
||||
@@ -165,7 +188,7 @@ impl Format for MacTok {
|
||||
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]),
|
||||
.units([body.print(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(),
|
||||
@@ -177,7 +200,7 @@ pub async fn mtreev_fmt<'b>(
|
||||
v: impl IntoIterator<Item = &'b MacTree>,
|
||||
c: &(impl FmtCtx + ?Sized),
|
||||
) -> FmtUnit {
|
||||
FmtUnit::sequence(" ", None, join_all(v.into_iter().map(|t| t.print(c))).await)
|
||||
FmtUnit::sequence("", " ", "", None, join_all(v.into_iter().map(|t| t.print(c))).await)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
@@ -203,36 +226,6 @@ pub enum PhKind {
|
||||
Vector { at_least_one: bool, priority: u8 },
|
||||
}
|
||||
|
||||
pub fn map_mactree<F: FnMut(MacTree) -> Option<MacTree>>(
|
||||
src: &MacTree,
|
||||
changed: &mut bool,
|
||||
map: &mut F,
|
||||
) -> MacTree {
|
||||
let tok = match map(src.clone()) {
|
||||
Some(new_tok) => {
|
||||
*changed = true;
|
||||
return new_tok;
|
||||
},
|
||||
None => match &*src.tok {
|
||||
MacTok::Lambda(arg, body) => MacTok::Lambda(
|
||||
ro(changed, |changed| map_mactree(arg, changed, map)),
|
||||
map_mactree_v(body, changed, map),
|
||||
),
|
||||
MacTok::Name(_) | MacTok::Value(_) => return src.clone(),
|
||||
MacTok::Slot | MacTok::Ph(_) | MacTok::Bottom(_) => return src.clone(),
|
||||
MacTok::S(p, body) => MacTok::S(*p, map_mactree_v(body, changed, map)),
|
||||
},
|
||||
};
|
||||
if *changed { tok.at(src.pos()) } else { src.clone() }
|
||||
}
|
||||
pub fn map_mactree_v<F: FnMut(MacTree) -> Option<MacTree>>(
|
||||
src: &[MacTree],
|
||||
changed: &mut bool,
|
||||
map: &mut F,
|
||||
) -> Vec<MacTree> {
|
||||
src.iter().map(|tree| ro(changed, |changed| map_mactree(tree, changed, map))).collect_vec()
|
||||
}
|
||||
|
||||
/// reverse "or". Inside, the flag is always false, but raising it will raise
|
||||
/// the outside flag too.
|
||||
fn ro<T>(flag: &mut bool, cb: impl FnOnce(&mut bool) -> T) -> T {
|
||||
@@ -241,7 +234,3 @@ fn ro<T>(flag: &mut bool, cb: impl FnOnce(&mut bool) -> T) -> T {
|
||||
*flag |= new_flag;
|
||||
val
|
||||
}
|
||||
|
||||
pub fn glossary_v(src: &[MacTree]) -> impl Iterator<Item = Sym> {
|
||||
src.iter().flat_map(|mt| mt.glossary()).cloned()
|
||||
}
|
||||
|
||||
@@ -12,21 +12,21 @@ 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};
|
||||
use crate::macros::mactree::{MacTok, MacTree, MacTreeSeq};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MacTreeLexer;
|
||||
impl Lexer for MacTreeLexer {
|
||||
const CHAR_FILTER: &'static [RangeInclusive<char>] = &['\''..='\''];
|
||||
async fn lex<'a>(tail: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree)> {
|
||||
async fn lex<'a>(tail: &'a str, lctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree)> {
|
||||
let Some(tail2) = tail.strip_prefix('\'') else {
|
||||
return Err(err_not_applicable(ctx.i()).await);
|
||||
return Err(err_not_applicable().await);
|
||||
};
|
||||
let tail3 = tail2.trim_start();
|
||||
let mut args = Vec::new();
|
||||
return match mac_tree(tail3, &mut args, ctx).await {
|
||||
return match mac_tree(tail3, &mut args, lctx).await {
|
||||
Ok((tail4, mactree)) => {
|
||||
let range = ctx.pos_tt(tail, tail4);
|
||||
let range = lctx.pos_tt(tail, tail4);
|
||||
let tok = match &args[..] {
|
||||
[] => x_tok(mactree).await,
|
||||
_ => {
|
||||
@@ -38,7 +38,7 @@ impl Lexer for MacTreeLexer {
|
||||
};
|
||||
Ok((tail4, tok.at(range)))
|
||||
},
|
||||
Err(e) => Ok((tail2, GenTok::Bottom(e).at(ctx.pos_lt(1, tail2)))),
|
||||
Err(e) => Ok((tail2, GenTok::Bottom(e).at(lctx.pos_lt(1, tail2)))),
|
||||
};
|
||||
async fn mac_tree<'a>(
|
||||
tail: &'a str,
|
||||
@@ -51,7 +51,8 @@ impl Lexer for MacTreeLexer {
|
||||
return loop {
|
||||
let tail2 = body_tail.trim_start();
|
||||
if let Some(tail3) = tail2.strip_prefix(*rp) {
|
||||
break Ok((tail3, MacTok::S(*paren, items).at(ctx.pos_tt(tail, tail3).pos())));
|
||||
let tok = MacTok::S(*paren, MacTreeSeq::new(items));
|
||||
break Ok((tail3, tok.at(ctx.pos_tt(tail, tail3).pos())));
|
||||
} else if tail2.is_empty() {
|
||||
return Err(mk_errv(
|
||||
ctx.i().i("Unclosed block").await,
|
||||
@@ -83,7 +84,7 @@ impl Lexer for MacTreeLexer {
|
||||
body.push(body_tok);
|
||||
tail3 = tail5;
|
||||
}
|
||||
Ok((tail3, MacTok::Lambda(param, body).at(ctx.pos_tt(tail, tail3).pos())))
|
||||
Ok((tail3, MacTok::Lambda(param, MacTreeSeq::new(body)).at(ctx.pos_tt(tail, tail3).pos())))
|
||||
} else {
|
||||
let (tail2, sub) = ctx.recurse(tail).await?;
|
||||
let parsed = parse_tok(&sub, ctx).await.expect("Unexpected invalid token");
|
||||
|
||||
178
orchid-std/src/macros/match_macros.rs
Normal file
178
orchid-std/src/macros/match_macros.rs
Normal file
@@ -0,0 +1,178 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use async_fn_stream::stream;
|
||||
use futures::future::join_all;
|
||||
use futures::{Stream, StreamExt, stream};
|
||||
use never::Never;
|
||||
use orchid_api::ExprTicket;
|
||||
use orchid_api_derive::Coding;
|
||||
use orchid_base::error::{OrcRes, mk_errv};
|
||||
use orchid_base::format::fmt;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::{Atomic, TAtom};
|
||||
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::coroutine_exec::{ExecHandle, exec};
|
||||
use orchid_extension::expr::{Expr, ExprHandle};
|
||||
use orchid_extension::gen_expr::{GExpr, arg, bot, call, lambda, sym_ref};
|
||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||
|
||||
use crate::macros::resolve::resolve;
|
||||
use crate::macros::utils::{build_macro, mactree, mactreev};
|
||||
use crate::std::reflection::sym_atom::SymAtom;
|
||||
use crate::std::tuple::Tuple;
|
||||
use crate::{HomoTpl, MacTok, MacTree, OrcOpt, Tpl, UntypedTuple, api};
|
||||
|
||||
#[derive(Clone, Coding)]
|
||||
pub struct MatcherData {
|
||||
keys: Vec<api::TStrv>,
|
||||
matcher: ExprTicket,
|
||||
}
|
||||
impl MatcherData {
|
||||
async fn matcher(&self) -> Expr { Expr::from_handle(ExprHandle::from_ticket(self.matcher).await) }
|
||||
pub async fn run_matcher(
|
||||
&self,
|
||||
h: &mut ExecHandle<'_>,
|
||||
val: impl ToExpr,
|
||||
) -> OrcRes<OrcOpt<HomoTpl<Expr>>> {
|
||||
h.exec::<OrcOpt<HomoTpl<Expr>>>(call(self.matcher().await.to_gen().await, [val.to_gen().await]))
|
||||
.await
|
||||
}
|
||||
pub fn keys(&self) -> impl Stream<Item = Sym> {
|
||||
stream(async |mut h| {
|
||||
for tk in &self.keys {
|
||||
h.emit(Sym::from_api(*tk, &i()).await).await
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct MatcherAtom {
|
||||
/// The names that subresults may be bound to
|
||||
pub(super) keys: Vec<Sym>,
|
||||
/// Takes the value-to-be-matched, returns an `option (tuple T1..TN)` of the
|
||||
/// subresults to be bound to the names returned by [Self::keys]
|
||||
pub(super) matcher: Expr,
|
||||
}
|
||||
impl Atomic for MatcherAtom {
|
||||
type Data = MatcherData;
|
||||
type Variant = OwnedVariant;
|
||||
}
|
||||
impl OwnedAtom for MatcherAtom {
|
||||
type Refs = Never;
|
||||
async fn val(&self) -> std::borrow::Cow<'_, Self::Data> {
|
||||
Cow::Owned(MatcherData {
|
||||
keys: self.keys.iter().map(|t| t.to_api()).collect(),
|
||||
matcher: self.matcher.handle().ticket(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn gen_match_macro_lib() -> Vec<GenMember> {
|
||||
prefix("pattern", [
|
||||
fun(
|
||||
true,
|
||||
"match_one",
|
||||
async |mat: TAtom<MatcherAtom>, value: Expr, then: Expr, default: Expr| {
|
||||
exec(async move |mut h| match mat.run_matcher(&mut h, value).await? {
|
||||
OrcOpt(Some(values)) =>
|
||||
Ok(call(then.to_gen().await, join_all(values.0.into_iter().map(|x| x.to_gen())).await)),
|
||||
OrcOpt(None) => Ok(default.to_gen().await),
|
||||
})
|
||||
.await
|
||||
},
|
||||
),
|
||||
fun(true, "matcher", async |names: HomoTpl<TAtom<SymAtom>>, matcher: Expr| MatcherAtom {
|
||||
keys: join_all(names.0.iter().map(async |atm| Sym::from_api(atm.0, &i()).await)).await,
|
||||
matcher,
|
||||
}),
|
||||
build_macro(None, ["match", "match_rule", "_row", "=>"])
|
||||
.rule(mactreev!("pattern::match" { "..$" rules 0 }), [async |[rules]| {
|
||||
exec(async move |mut h| {
|
||||
let rule_lines = h
|
||||
.exec::<TAtom<Tuple>>(call(sym_ref(sym!(macros::resolve; i())), [
|
||||
mactree!(macros::common::semi_list "push" rules.clone();).to_gen().await,
|
||||
]))
|
||||
.await?;
|
||||
let mut rule_atoms = Vec::<(TAtom<MatcherAtom>, Expr)>::new();
|
||||
for line_exprh in rule_lines.iter() {
|
||||
let line_mac = h
|
||||
.exec::<TAtom<MacTree>>(Expr::from_handle(ExprHandle::from_ticket(*line_exprh).await))
|
||||
.await?;
|
||||
let Tpl((matcher, body)) = h
|
||||
.exec(call(sym_ref(sym!(macros::resolve; i())), [
|
||||
mactree!(pattern::_row "push" own(&line_mac).await ;).to_gen().await,
|
||||
]))
|
||||
.await?;
|
||||
rule_atoms.push((matcher, body));
|
||||
}
|
||||
let base_case = lambda(0, [bot(mk_errv(
|
||||
i().i("No branches match").await,
|
||||
"None of the pattern provided matches this value",
|
||||
[rules.pos()],
|
||||
))]);
|
||||
let match_expr = stream::iter(rule_atoms.into_iter().rev())
|
||||
.fold(base_case, async |tail, (mat, body)| {
|
||||
lambda(0, [call(sym_ref(sym!(pattern::match_one; i())), [
|
||||
mat.to_gen().await,
|
||||
arg(0),
|
||||
body.to_gen().await,
|
||||
call(tail, [arg(0)]),
|
||||
])])
|
||||
})
|
||||
.await;
|
||||
Ok(match_expr)
|
||||
})
|
||||
.await
|
||||
}])
|
||||
.rule(mactreev!(pattern::match_rule (( "...$" pattern 0 ))), [async |[pattern]| {
|
||||
resolve(mactree!(pattern::match_rule "push" pattern; )).await
|
||||
}])
|
||||
.rule(mactreev!(pattern::_row ( "...$" pattern 0 pattern::=> "...$" value 1 )), [
|
||||
async |[pattern, mut value]| {
|
||||
exec(async move |mut h| -> OrcRes<Tpl<(TAtom<MatcherAtom>, GExpr)>> {
|
||||
let Ok(pat) = h
|
||||
.exec::<TAtom<MatcherAtom>>(call(sym_ref(sym!(macros::resolve; i())), [
|
||||
mactree!(pattern::match_rule "push" pattern.clone();).to_gen().await,
|
||||
]))
|
||||
.await
|
||||
else {
|
||||
return Err(mk_errv(
|
||||
i().i("Invalid pattern").await,
|
||||
format!("Could not parse {} as a match pattern", fmt(&pattern, &i()).await),
|
||||
[pattern.pos()],
|
||||
));
|
||||
};
|
||||
value = (pat.keys())
|
||||
.fold(value, async |value, name| mactree!("l_" name; ( "push" value ; )))
|
||||
.await;
|
||||
Ok(Tpl((pat, resolve(value).await)))
|
||||
})
|
||||
.await
|
||||
},
|
||||
])
|
||||
.finish(),
|
||||
fun(true, "ref_body", async |val| OrcOpt(Some(UntypedTuple(vec![val])))),
|
||||
build_macro(None, ["ref"])
|
||||
.rule(mactreev!(pattern::match_rule(pattern::ref "$" name)), [async |[name]| {
|
||||
let MacTok::Name(name) = name.tok() else {
|
||||
return Err(mk_errv(
|
||||
i().i("pattern 'ref' requires a name to bind to").await,
|
||||
format!(
|
||||
"'ref' was interpreted as a binding matcher, \
|
||||
but it was followed by {} instead of a name",
|
||||
fmt(&name, &i()).await
|
||||
),
|
||||
[name.pos()],
|
||||
));
|
||||
};
|
||||
Ok(MatcherAtom {
|
||||
keys: vec![name.clone()],
|
||||
matcher: sym_ref(sym!(pattern::ref_body; i())).to_expr().await,
|
||||
})
|
||||
}])
|
||||
.finish(),
|
||||
])
|
||||
}
|
||||
@@ -6,10 +6,11 @@ pub mod macro_system;
|
||||
mod macro_value;
|
||||
pub mod mactree;
|
||||
mod mactree_lexer;
|
||||
pub mod match_macros;
|
||||
mod ph_lexer;
|
||||
mod requests;
|
||||
mod resolve;
|
||||
mod rule;
|
||||
pub mod std_macros;
|
||||
mod utils;
|
||||
|
||||
use mactree::{MacTok, MacTree};
|
||||
|
||||
@@ -4,8 +4,8 @@ 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::context::i;
|
||||
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};
|
||||
@@ -13,17 +13,15 @@ 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 }
|
||||
}
|
||||
pub async fn to_full(&self) -> Ph { Ph { kind: self.1, name: 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()
|
||||
async fn print(&self) -> FmtUnit {
|
||||
Ph { name: i().ex(self.0).await, kind: self.1 }.to_string().into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +52,7 @@ impl Lexer for PhLexer {
|
||||
(prio_num, tail)
|
||||
} else {
|
||||
return Err(mk_errv(
|
||||
ctx.ctx.i().i("Invalid priority, must be 0-255").await,
|
||||
i().i("Invalid priority, must be 0-255").await,
|
||||
format!("{prio} is not a valid placeholder priority"),
|
||||
[ctx.pos_lt(prio.len(), tail)],
|
||||
));
|
||||
@@ -70,10 +68,10 @@ impl Lexer for PhLexer {
|
||||
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);
|
||||
return Err(err_not_applicable().await);
|
||||
}
|
||||
};
|
||||
let ph_atom = PhAtom(ctx.ctx.i().i::<String>(name).await.to_api(), phkind);
|
||||
let ph_atom = PhAtom(i().i::<String>(name).await.to_api(), phkind);
|
||||
Ok((tail, x_tok(ph_atom).await.at(ctx.pos_tt(line, tail))))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
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,91 +1,275 @@
|
||||
use futures::FutureExt;
|
||||
use hashbrown::HashMap;
|
||||
use std::ops::{Add, Range};
|
||||
|
||||
use async_fn_stream::stream;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use itertools::Itertools;
|
||||
use orchid_base::error::mk_errv;
|
||||
use orchid_base::format::fmt;
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::sym;
|
||||
use orchid_base::tree::Paren;
|
||||
use orchid_extension::atom::TAtom;
|
||||
use orchid_extension::atom_owned::own;
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::coroutine_exec::ExecHandle;
|
||||
use orchid_extension::gen_expr::{GExpr, call, sym_ref};
|
||||
use orchid_extension::system::SysCtx;
|
||||
use orchid_extension::coroutine_exec::{ExecHandle, exec};
|
||||
use orchid_extension::gen_expr::{GExpr, bot, call, lambda, sym_ref};
|
||||
use orchid_extension::reflection::{ReflMemKind, refl};
|
||||
use subslice_offset::SubsliceOffset;
|
||||
use substack::Substack;
|
||||
|
||||
use crate::macros::macro_value::{Macro, Rule};
|
||||
use crate::macros::rule::matcher::{NamedMatcher, PriodMatcher};
|
||||
use crate::macros::mactree::MacTreeSeq;
|
||||
use crate::macros::rule::state::{MatchState, StateEntry};
|
||||
use crate::{MacTok, MacTree};
|
||||
|
||||
pub struct ResolveCtx<'a> {
|
||||
pub ctx: SysCtx,
|
||||
pub h: ExecHandle<'a>,
|
||||
pub named: HashMap<Sym, Vec<(&'a NamedMatcher, &'a Macro, &'a Rule)>>,
|
||||
pub priod: Vec<(&'a PriodMatcher, &'a Macro, &'a Rule)>,
|
||||
pub async fn resolve(tpl: MacTree) -> GExpr {
|
||||
exec(async move |mut h| {
|
||||
let root = refl();
|
||||
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::<TAtom<Macro>>(sym_ref(n.clone())).await else { continue };
|
||||
let mac = own(&mac).await;
|
||||
macros.entry(mac.canonical_name().await).or_insert(mac);
|
||||
}
|
||||
}
|
||||
let mut exclusive = Vec::new();
|
||||
let mut prios = Vec::<u64>::new();
|
||||
let mut priod = Vec::<FilteredMacroRecord>::new();
|
||||
for (_, mac) in macros.iter() {
|
||||
let mut record = FilteredMacroRecord { mac, rules: Vec::new() };
|
||||
for (rule_i, rule) in mac.0.rules.iter().enumerate() {
|
||||
if rule.pattern.glossary.is_subset(tpl.glossary()) {
|
||||
record.rules.push(rule_i);
|
||||
}
|
||||
}
|
||||
if !record.rules.is_empty() {
|
||||
match mac.0.prio {
|
||||
None => exclusive.push(record),
|
||||
Some(prio) => {
|
||||
let i = prios.partition_point(|p| *p > prio);
|
||||
prios.insert(i, prio);
|
||||
priod.insert(i, record);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut rctx = ResolveCtx { h, exclusive, priod };
|
||||
resolve_one(&mut rctx, Substack::Bottom, &tpl).await
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn resolve(ctx: &mut ResolveCtx<'_>, value: &MacTree) -> Option<MacTree> {
|
||||
/// Rules belonging to one macro that passed a particular filter
|
||||
pub struct FilteredMacroRecord<'a> {
|
||||
mac: &'a Macro,
|
||||
/// The rules in increasing order of index
|
||||
rules: Vec<usize>,
|
||||
}
|
||||
|
||||
struct ResolveCtx<'a> {
|
||||
pub h: ExecHandle<'a>,
|
||||
/// If these overlap, that's a compile-time error
|
||||
pub exclusive: Vec<FilteredMacroRecord<'a>>,
|
||||
/// If these overlap, the priorities decide the order. In case of a tie, the
|
||||
/// order is unspecified
|
||||
pub priod: Vec<FilteredMacroRecord<'a>>,
|
||||
}
|
||||
|
||||
async fn resolve_one(
|
||||
ctx: &mut ResolveCtx<'_>,
|
||||
arg_stk: Substack<'_, Sym>,
|
||||
value: &MacTree,
|
||||
) -> GExpr {
|
||||
match value.tok() {
|
||||
MacTok::Ph(_) | MacTok::Slot => panic!("Forbidden element in value mactree"),
|
||||
MacTok::Bottom(_) | MacTok::Value(_) | MacTok::Name(_) => None,
|
||||
MacTok::Lambda(arg, body) =>
|
||||
Some(MacTok::Lambda(arg.clone(), resolve_seq(ctx, body).await?).at(value.pos())),
|
||||
MacTok::S(ptyp, body) => Some(MacTok::S(*ptyp, resolve_seq(ctx, body).await?).at(value.pos())),
|
||||
MacTok::Bottom(err) => bot(err.clone()),
|
||||
MacTok::Value(v) => v.clone().to_gen().await,
|
||||
MacTok::Name(n) => sym_ref(n.clone()),
|
||||
MacTok::Lambda(arg, body) => {
|
||||
let MacTok::Name(name) = &*arg.tok else {
|
||||
return bot(mk_errv(
|
||||
i().i("Syntax error after macros").await,
|
||||
"This token ends up as a binding, consider replacing it with a name",
|
||||
[arg.pos()],
|
||||
));
|
||||
};
|
||||
let arg_pos = arg_stk.len() as u64;
|
||||
let arg_stk = arg_stk.push(name.clone());
|
||||
lambda(arg_pos, [resolve_seq(ctx, arg_stk, body.clone(), value.pos()).await])
|
||||
},
|
||||
MacTok::S(Paren::Round, body) => resolve_seq(ctx, arg_stk, body.clone(), value.pos()).await,
|
||||
MacTok::S(..) => bot(mk_errv(
|
||||
i().i("Leftover [] or {} not matched by macro").await,
|
||||
format!("{} was not matched by any macro", fmt(value, &i()).await),
|
||||
[value.pos()],
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn resolve_seq(ctx: &mut ResolveCtx<'_>, val: &[MacTree]) -> Option<Vec<MacTree>> {
|
||||
let mut any_changed = false;
|
||||
let mut i = 0;
|
||||
let mut val = val.to_vec();
|
||||
'all_named: while i < val.len() {
|
||||
'one_named: {
|
||||
let MacTok::Name(key) = val[i].tok() else { break 'one_named };
|
||||
let Some(options) = ctx.named.get(key) else { break 'one_named };
|
||||
let matches = (options.iter())
|
||||
.filter_map(|r| Some((r.1, r.2, r.0.apply(&val[i..], |_| false)?)))
|
||||
.collect_vec();
|
||||
match matches.len() {
|
||||
0 => break 'one_named,
|
||||
1 => {
|
||||
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).await;
|
||||
std::mem::drop(state);
|
||||
val.splice(i..end, [MacTok::Value(ctx.h.register(body_call).await).at(Pos::None)]);
|
||||
i = end;
|
||||
},
|
||||
2.. => todo!("Named macros conflict!"),
|
||||
type XMatches<'a> = Vec<(Range<usize>, &'a Macro, &'a Rule, MatchState<'a>)>;
|
||||
|
||||
/// find the subsection of the slice that satisfies both the lower and upper
|
||||
/// limit.
|
||||
fn subsection<T>(
|
||||
slice: &[T],
|
||||
lower_limit: impl FnMut(&T) -> bool,
|
||||
mut upper_limit: impl FnMut(&T) -> bool,
|
||||
) -> Range<usize> {
|
||||
let start = slice.partition_point(lower_limit);
|
||||
let len = slice[start..].partition_point(|t| !upper_limit(t));
|
||||
start..start + len
|
||||
}
|
||||
|
||||
async fn resolve_seq(
|
||||
ctx: &mut ResolveCtx<'_>,
|
||||
arg_stk: Substack<'_, Sym>,
|
||||
val: MacTreeSeq,
|
||||
fallback_pos: Pos,
|
||||
) -> GExpr {
|
||||
if val.items.is_empty() {
|
||||
return bot(mk_errv(
|
||||
i().i("Empty sequence").await,
|
||||
"() or (\\arg ) left after macro execution. \
|
||||
This is usually caused by an incomplete call to a macro with bad error detection",
|
||||
[fallback_pos],
|
||||
));
|
||||
}
|
||||
// A sorted collection of overlapping but non-nested matches to exclusive
|
||||
// macros
|
||||
let mut x_matches: XMatches = Vec::new();
|
||||
let top_glossary = val.top_glossary.clone();
|
||||
let mut new_val = val.items.to_vec();
|
||||
'x_macros: for x in &ctx.exclusive {
|
||||
let mut rules_iter = x.rules.iter();
|
||||
let ((before, state, after), rule) = 'rules: loop {
|
||||
let Some(ridx) = rules_iter.next() else { continue 'x_macros };
|
||||
let rule = &x.mac.0.rules[*ridx];
|
||||
if rule.pattern.top_glossary.is_subset(&top_glossary)
|
||||
&& let Some(record) = rule.matcher.apply(&val.items[..], &|_| true).await
|
||||
{
|
||||
break 'rules (record, rule);
|
||||
};
|
||||
};
|
||||
let new_r = (before.len()..new_val.len() - after.len(), x.mac, rule, state);
|
||||
// elements that overlap with us
|
||||
let overlap =
|
||||
subsection(&x_matches[..], |r| new_r.0.start < r.0.end, |r| r.0.start < new_r.0.end);
|
||||
let overlapping = &x_matches[overlap.clone()];
|
||||
// elements that fully contain us
|
||||
let geq_range =
|
||||
subsection(overlapping, |r| r.0.start <= new_r.0.start, |r| new_r.0.end <= r.0.end);
|
||||
let geq = &overlapping[geq_range.clone()];
|
||||
// if any of these is equal to us, all of them must be, otherwise the larger
|
||||
// ranges would have overridden the smaller ones
|
||||
if let Some(example) = geq.first() {
|
||||
// if they are equal to us, record the conflict.
|
||||
if example.0 == new_r.0 {
|
||||
let idx = (x_matches.subslice_offset(geq))
|
||||
.expect("this slice is statically derived from x_matches");
|
||||
x_matches.insert(idx, new_r);
|
||||
}
|
||||
continue 'all_named;
|
||||
// either way, we matched so no further rules can run.
|
||||
continue 'x_macros;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
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).await).await)
|
||||
.at(Pos::None),
|
||||
]);
|
||||
}
|
||||
for expr in val.iter_mut() {
|
||||
if let Some(new) = resolve(ctx, expr).boxed_local().await {
|
||||
*expr = new;
|
||||
any_changed = true;
|
||||
// elements we fully contain. Equal ranges have been handled above
|
||||
let lt_range =
|
||||
subsection(overlapping, |r| new_r.0.start <= r.0.start, |r| r.0.end <= new_r.0.end);
|
||||
let lt = &overlapping[lt_range.clone()];
|
||||
if lt.is_empty() {
|
||||
// an empty range
|
||||
let i = x_matches.partition_point(|r| r.0.start < new_r.0.start);
|
||||
x_matches.insert(i, new_r);
|
||||
} else {
|
||||
let lt_start =
|
||||
x_matches.subslice_offset(overlapping).expect("Slice statically derived from x_matches");
|
||||
x_matches.splice(lt_start..lt_start + lt_range.len(), [new_r]);
|
||||
}
|
||||
}
|
||||
if any_changed { Some(val) } else { None }
|
||||
// apply exclusive matches
|
||||
if !x_matches.is_empty() {
|
||||
// ranges of indices into x_matches which setwise conflict with each other.
|
||||
// Pairwise conflict reporting is excess noise, but a single conflict error
|
||||
// doesn't reveal where within the parenthesized block to look, so it's easiest
|
||||
// to group them setwise even if these sets may associate macros which don't
|
||||
// directly conflict.
|
||||
let conflict_sets = (0..x_matches.len()).map(|x| x..x + 1).coalesce(|lran, rran| {
|
||||
// each index was mapped to a range that contains only itself. Now we check if
|
||||
// the last match in the first range overlaps the first match in the second
|
||||
// range, and combine them if this is the case.
|
||||
if x_matches[rran.start].0.start < x_matches[lran.end].0.end {
|
||||
Ok(lran.start..rran.end)
|
||||
} else {
|
||||
Err((lran, rran))
|
||||
}
|
||||
});
|
||||
let mac_conflict_tk = i().i("Macro conflict").await;
|
||||
let error = conflict_sets
|
||||
.filter(|r| 1 < r.len())
|
||||
.map(|set| {
|
||||
mk_errv(
|
||||
mac_conflict_tk.clone(),
|
||||
"Multiple partially overlapping syntax elements detected. \n\
|
||||
Try parenthesizing whichever side is supposed to be the subexpression.",
|
||||
x_matches[set].iter().flat_map(|rec| rec.3.names()).flat_map(|name| name.1).cloned(),
|
||||
)
|
||||
})
|
||||
.reduce(|l, r| l + r);
|
||||
if let Some(error) = error {
|
||||
return bot(error);
|
||||
}
|
||||
// no conflicts, apply all exclusive matches
|
||||
for (range, mac, rule, state) in x_matches.into_iter().rev() {
|
||||
// backwards so that the non-overlapping ranges remain valid
|
||||
let pos = (state.names().flat_map(|r| r.1).cloned().reduce(Pos::add))
|
||||
.expect("All macro rules must contain at least one locally defined name");
|
||||
let subex = ctx.h.register(mk_body_call(mac, rule, &state, pos.clone()).await).await;
|
||||
new_val.splice(range, [MacTok::Value(subex).at(pos)]);
|
||||
}
|
||||
};
|
||||
// Does this glossary refresh actually pay off?
|
||||
let top_glossary = (new_val.iter())
|
||||
.flat_map(|t| if let MacTok::Name(t) = t.tok() { Some(t.clone()) } else { None })
|
||||
.collect::<HashSet<_>>();
|
||||
for FilteredMacroRecord { mac, rules } in &ctx.priod {
|
||||
for ridx in rules {
|
||||
let rule = &mac.0.rules[*ridx];
|
||||
if !rule.pattern.top_glossary.is_subset(&top_glossary) {
|
||||
continue;
|
||||
}
|
||||
let Some((pre, state, suf)) = rule.matcher.apply(&new_val, &|_| true).await else { continue };
|
||||
let range = pre.len()..new_val.len() - suf.len();
|
||||
let pos = (state.names().flat_map(|pair| pair.1).cloned().reduce(Pos::add))
|
||||
.expect("All macro rules must contain at least one locally defined name");
|
||||
let subex = ctx.h.register(mk_body_call(mac, rule, &state, pos.clone()).await).await;
|
||||
std::mem::drop(state);
|
||||
new_val.splice(range, [MacTok::Value(subex).at(pos)]);
|
||||
}
|
||||
}
|
||||
let exprs = stream(async |mut h| {
|
||||
for mt in new_val {
|
||||
h.emit(resolve_one(ctx, arg_stk.clone(), &mt).await).await
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.boxed_local()
|
||||
.await;
|
||||
exprs.into_iter().reduce(|f, x| call(f, [x])).expect(
|
||||
"We checked first that it isn't empty, and named macros get replaced with their results",
|
||||
)
|
||||
}
|
||||
|
||||
async fn mk_body_call(mac: &Macro, rule: &Rule, state: &MatchState<'_>, ctx: &SysCtx) -> GExpr {
|
||||
async fn mk_body_call(mac: &Macro, rule: &Rule, state: &MatchState<'_>, pos: Pos) -> 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,
|
||||
StateEntry::Scalar(scal) => (**scal).clone().to_gen().await,
|
||||
StateEntry::Vec(vec) =>
|
||||
MacTok::S(Paren::Round, MacTreeSeq::new(vec.iter().cloned())).at(Pos::None).to_gen().await,
|
||||
});
|
||||
}
|
||||
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,
|
||||
)])
|
||||
call(sym_ref(mac.0.module.suffix([rule.body_name.clone()], &i()).await), call_args)
|
||||
.at(pos.clone())
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@ use futures::FutureExt;
|
||||
use futures::future::join_all;
|
||||
use itertools::Itertools;
|
||||
use orchid_base::error::{OrcRes, mk_errv};
|
||||
use orchid_base::interner::{Interner, Tok};
|
||||
use orchid_base::interner::Tok;
|
||||
use orchid_base::join_ok;
|
||||
use orchid_base::side::Side;
|
||||
use orchid_extension::context::i;
|
||||
|
||||
use super::shared::{AnyMatcher, ScalMatcher, VecMatcher};
|
||||
use super::vec_attrs::vec_attrs;
|
||||
@@ -31,29 +32,29 @@ fn scal_cnt<'a>(iter: impl Iterator<Item = &'a MacTree>) -> usize {
|
||||
iter.take_while(|expr| vec_attrs(expr).is_none()).count()
|
||||
}
|
||||
|
||||
pub async fn mk_any(pattern: &[MacTree], i: &Interner) -> OrcRes<AnyMatcher> {
|
||||
pub async fn mk_any(pattern: &[MacTree]) -> OrcRes<AnyMatcher> {
|
||||
let left_split = scal_cnt(pattern.iter());
|
||||
if pattern.len() <= left_split {
|
||||
return Ok(AnyMatcher::Scalar(mk_scalv(pattern, i).await?));
|
||||
return Ok(AnyMatcher::Scalar(mk_scalv(pattern).await?));
|
||||
}
|
||||
let (left, not_left) = pattern.split_at(left_split);
|
||||
let right_split = not_left.len() - scal_cnt(pattern.iter().rev());
|
||||
let (mid, right) = not_left.split_at(right_split);
|
||||
join_ok! {
|
||||
left = mk_scalv(left, i).await;
|
||||
mid = mk_vec(mid, i).await;
|
||||
right = mk_scalv(right, i).await;
|
||||
left = mk_scalv(left).await;
|
||||
mid = mk_vec(mid).await;
|
||||
right = mk_scalv(right).await;
|
||||
}
|
||||
Ok(AnyMatcher::Vec { left, mid, right })
|
||||
}
|
||||
|
||||
/// Pattern MUST NOT contain vectorial placeholders
|
||||
async fn mk_scalv(pattern: &[MacTree], i: &Interner) -> OrcRes<Vec<ScalMatcher>> {
|
||||
join_all(pattern.iter().map(|pat| mk_scalar(pat, i))).await.into_iter().collect()
|
||||
async fn mk_scalv(pattern: &[MacTree]) -> OrcRes<Vec<ScalMatcher>> {
|
||||
join_all(pattern.iter().map(mk_scalar)).await.into_iter().collect()
|
||||
}
|
||||
|
||||
/// Pattern MUST start and end with a vectorial placeholder
|
||||
pub async fn mk_vec(pattern: &[MacTree], i: &Interner) -> OrcRes<VecMatcher> {
|
||||
pub async fn mk_vec(pattern: &[MacTree]) -> OrcRes<VecMatcher> {
|
||||
debug_assert!(!pattern.is_empty(), "pattern cannot be empty");
|
||||
debug_assert!(pattern.first().map(vec_attrs).is_some(), "pattern must start with a vectorial");
|
||||
debug_assert!(pattern.last().map(vec_attrs).is_some(), "pattern must end with a vectorial");
|
||||
@@ -68,8 +69,8 @@ pub async fn mk_vec(pattern: &[MacTree], i: &Interner) -> OrcRes<VecMatcher> {
|
||||
(&[], &[]) => Ok(VecMatcher::Placeh { key, nonzero }),
|
||||
(&[], _) => {
|
||||
join_ok! {
|
||||
sep = mk_scalv(r_sep, i).await;
|
||||
right = mk_vec(r_side, i).boxed_local().await;
|
||||
sep = mk_scalv(r_sep).await;
|
||||
right = mk_vec(r_side).boxed_local().await;
|
||||
}
|
||||
Ok(VecMatcher::Scan {
|
||||
direction: Side::Left,
|
||||
@@ -80,8 +81,8 @@ pub async fn mk_vec(pattern: &[MacTree], i: &Interner) -> OrcRes<VecMatcher> {
|
||||
},
|
||||
(_, &[]) => {
|
||||
join_ok! {
|
||||
left = mk_vec(l_side, i).boxed_local().await;
|
||||
sep = mk_scalv(l_sep, i).await;
|
||||
left = mk_vec(l_side).boxed_local().await;
|
||||
sep = mk_scalv(l_sep).await;
|
||||
}
|
||||
Ok(VecMatcher::Scan {
|
||||
direction: Side::Right,
|
||||
@@ -95,10 +96,10 @@ pub async fn mk_vec(pattern: &[MacTree], i: &Interner) -> OrcRes<VecMatcher> {
|
||||
l_side.iter().chain(r_side.iter()).filter_map(vec_attrs).collect::<Vec<_>>();
|
||||
key_order.sort_by_key(|(_, prio, _)| -(*prio as i64));
|
||||
join_ok! {
|
||||
left = mk_vec(l_side, i).boxed_local().await;
|
||||
left_sep = mk_scalv(l_sep, i).await;
|
||||
right_sep = mk_scalv(r_sep, i).await;
|
||||
right = mk_vec(r_side, i).boxed_local().await;
|
||||
left = mk_vec(l_side).boxed_local().await;
|
||||
left_sep = mk_scalv(l_sep).await;
|
||||
right_sep = mk_scalv(r_sep).await;
|
||||
right = mk_vec(r_side).boxed_local().await;
|
||||
}
|
||||
Ok(VecMatcher::Middle {
|
||||
left: Box::new(left),
|
||||
@@ -113,7 +114,7 @@ pub async fn mk_vec(pattern: &[MacTree], i: &Interner) -> OrcRes<VecMatcher> {
|
||||
}
|
||||
|
||||
/// Pattern MUST NOT be a vectorial placeholder
|
||||
async fn mk_scalar(pattern: &MacTree, i: &Interner) -> OrcRes<ScalMatcher> {
|
||||
async fn mk_scalar(pattern: &MacTree) -> OrcRes<ScalMatcher> {
|
||||
Ok(match &*pattern.tok {
|
||||
MacTok::Name(n) => ScalMatcher::Name(n.clone()),
|
||||
MacTok::Ph(Ph { name, kind }) => match kind {
|
||||
@@ -122,10 +123,10 @@ async fn mk_scalar(pattern: &MacTree, i: &Interner) -> OrcRes<ScalMatcher> {
|
||||
},
|
||||
PhKind::Scalar => ScalMatcher::Placeh { key: name.clone() },
|
||||
},
|
||||
MacTok::S(c, body) => ScalMatcher::S(*c, Box::new(mk_any(body, i).boxed_local().await?)),
|
||||
MacTok::S(c, body) => ScalMatcher::S(*c, Box::new(mk_any(&body.items).boxed_local().await?)),
|
||||
MacTok::Lambda(..) =>
|
||||
return Err(mk_errv(
|
||||
i.i("Lambda in matcher").await,
|
||||
i().i("Lambda in matcher").await,
|
||||
"Lambdas can't be matched for, only generated in templates",
|
||||
[pattern.pos()],
|
||||
)),
|
||||
@@ -136,50 +137,52 @@ async fn mk_scalar(pattern: &MacTree, i: &Interner) -> OrcRes<ScalMatcher> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use orchid_base::interner::Interner;
|
||||
use orchid_base::location::SrcRange;
|
||||
use orchid_base::sym;
|
||||
use orchid_base::tokens::Paren;
|
||||
use orchid_extension::context::{i, mock_ctx, with_ctx};
|
||||
use test_executors::spin_on;
|
||||
|
||||
use super::mk_any;
|
||||
use crate::macros::MacTok;
|
||||
use crate::macros::mactree::{Ph, PhKind};
|
||||
use crate::macros::mactree::{MacTreeSeq, Ph, PhKind};
|
||||
|
||||
#[test]
|
||||
fn test_scan() {
|
||||
spin_on(async {
|
||||
let i = Interner::new_master();
|
||||
let ex = |tok: MacTok| async { tok.at(SrcRange::mock(&i).await.pos()) };
|
||||
spin_on(with_ctx(mock_ctx(), async {
|
||||
let ex = |tok: MacTok| async { tok.at(SrcRange::mock(&i()).await.pos()) };
|
||||
let pattern = vec![
|
||||
ex(MacTok::Ph(Ph {
|
||||
kind: PhKind::Vector { priority: 0, at_least_one: false },
|
||||
name: i.i("::prefix").await,
|
||||
name: i().i("::prefix").await,
|
||||
}))
|
||||
.await,
|
||||
ex(MacTok::Name(sym!(prelude::do; i).await)).await,
|
||||
ex(MacTok::S(Paren::Round, vec![
|
||||
ex(MacTok::Ph(Ph {
|
||||
kind: PhKind::Vector { priority: 0, at_least_one: false },
|
||||
name: i.i("expr").await,
|
||||
}))
|
||||
.await,
|
||||
ex(MacTok::Name(sym!(prelude::; ; i).await)).await,
|
||||
ex(MacTok::Ph(Ph {
|
||||
kind: PhKind::Vector { priority: 1, at_least_one: false },
|
||||
name: i.i("rest").await,
|
||||
}))
|
||||
.await,
|
||||
]))
|
||||
ex(MacTok::Name(sym!(prelude::do; i()))).await,
|
||||
ex(MacTok::S(
|
||||
Paren::Round,
|
||||
MacTreeSeq::new([
|
||||
ex(MacTok::Ph(Ph {
|
||||
kind: PhKind::Vector { priority: 0, at_least_one: false },
|
||||
name: i().i("expr").await,
|
||||
}))
|
||||
.await,
|
||||
ex(MacTok::Name(sym!(prelude::; ; i()))).await,
|
||||
ex(MacTok::Ph(Ph {
|
||||
kind: PhKind::Vector { priority: 1, at_least_one: false },
|
||||
name: i().i("rest").await,
|
||||
}))
|
||||
.await,
|
||||
]),
|
||||
))
|
||||
.await,
|
||||
ex(MacTok::Ph(Ph {
|
||||
kind: PhKind::Vector { priority: 0, at_least_one: false },
|
||||
name: i.i("::suffix").await,
|
||||
name: i().i("::suffix").await,
|
||||
}))
|
||||
.await,
|
||||
];
|
||||
let matcher = mk_any(&pattern, &i).await.expect("This matcher isn't broken");
|
||||
let matcher = mk_any(&pattern).await.expect("This matcher isn't broken");
|
||||
println!("{matcher}");
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,87 +1,61 @@
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
|
||||
use itertools::Itertools;
|
||||
use orchid_base::error::OrcRes;
|
||||
use orchid_base::interner::{Interner, Tok};
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_extension::context::i;
|
||||
|
||||
use super::any_match::any_match;
|
||||
use super::build::{mk_any, mk_vec};
|
||||
use super::shared::{AnyMatcher, VecMatcher};
|
||||
use super::build::mk_any;
|
||||
use super::shared::AnyMatcher;
|
||||
use super::state::{MatchState, StateEntry};
|
||||
use super::vec_attrs::vec_attrs;
|
||||
use super::vec_match::vec_match;
|
||||
use crate::macros::mactree::{Ph, PhKind};
|
||||
use crate::macros::mactree::{MacTreeSeq, Ph, PhKind};
|
||||
use crate::macros::{MacTok, MacTree};
|
||||
|
||||
pub struct NamedMatcher {
|
||||
pub struct Matcher {
|
||||
inner: AnyMatcher,
|
||||
head: Sym,
|
||||
after_tok: Tok<String>,
|
||||
}
|
||||
impl NamedMatcher {
|
||||
pub async fn new(pattern: &[MacTree], i: &Interner) -> OrcRes<Self> {
|
||||
let head = match pattern.first().map(|tree| tree.tok()) {
|
||||
Some(MacTok::Name(name)) => name.clone(),
|
||||
_ => panic!("Named matchers must begin with a name"),
|
||||
};
|
||||
let after_tok = i.i("::after").await;
|
||||
let inner = match pattern.last().and_then(vec_attrs).is_some() {
|
||||
true => mk_any(pattern, i).await?,
|
||||
false => {
|
||||
let kind = PhKind::Vector { priority: 0, at_least_one: false };
|
||||
let suffix = [MacTok::Ph(Ph { name: after_tok.clone(), kind }).at(Pos::None)];
|
||||
mk_any(&pattern.iter().cloned().chain(suffix).collect_vec(), i).await?
|
||||
},
|
||||
};
|
||||
Ok(Self { after_tok, inner, head })
|
||||
}
|
||||
pub fn head(&self) -> Sym { self.head.clone() }
|
||||
/// Also returns the tail, if any, which should be matched further
|
||||
/// Note that due to how priod works below, the main usable information from
|
||||
/// the tail is its length
|
||||
pub fn apply<'a>(
|
||||
&self,
|
||||
seq: &'a [MacTree],
|
||||
save_loc: impl Fn(Sym) -> bool,
|
||||
) -> Option<(MatchState<'a>, &'a [MacTree])> {
|
||||
let mut state = any_match(&self.inner, seq, &save_loc)?;
|
||||
match state.remove(self.after_tok.clone()) {
|
||||
Some(StateEntry::Scalar(_)) => panic!("{} can never be a scalar entry!", self.after_tok),
|
||||
Some(StateEntry::Vec(v)) => Some((state, v)),
|
||||
None => Some((state, &[][..])),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl fmt::Display for NamedMatcher {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) }
|
||||
}
|
||||
impl fmt::Debug for NamedMatcher {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "NamedMatcher({self})") }
|
||||
}
|
||||
|
||||
pub struct PriodMatcher(VecMatcher);
|
||||
impl PriodMatcher {
|
||||
pub async fn new(pattern: &[MacTree], i: &Interner) -> OrcRes<Self> {
|
||||
assert!(
|
||||
pattern.first().and_then(vec_attrs).is_some() && pattern.last().and_then(vec_attrs).is_some(),
|
||||
"Prioritized matchers must start and end with a vectorial",
|
||||
);
|
||||
Ok(Self(mk_vec(pattern, i).await?))
|
||||
impl Matcher {
|
||||
pub async fn new(pattern: MacTreeSeq) -> OrcRes<Self> {
|
||||
let mut pattern = Rc::unwrap_or_clone(pattern.items);
|
||||
let kind = PhKind::Vector { at_least_one: false, priority: 0 };
|
||||
let first = pattern.first().expect("Empty pattern is not allowed");
|
||||
if vec_attrs(first).is_none() {
|
||||
let pos = first.pos();
|
||||
pattern.insert(0, MacTok::Ph(Ph { name: i().i("::before").await, kind }).at(pos));
|
||||
}
|
||||
let last = pattern.last().expect("first returned Some above");
|
||||
if vec_attrs(last).is_none() {
|
||||
let pos = last.pos();
|
||||
pattern.insert(0, MacTok::Ph(Ph { name: i().i("::after").await, kind }).at(pos));
|
||||
}
|
||||
Ok(Matcher { inner: mk_any(&pattern).await? })
|
||||
}
|
||||
/// tokens before the offset always match the prefix
|
||||
pub fn apply<'a>(
|
||||
/// Also returns the head and tail, which should be matched by overarching
|
||||
/// matchers attempted later.
|
||||
pub async fn apply<'a>(
|
||||
&self,
|
||||
seq: &'a [MacTree],
|
||||
save_loc: impl Fn(Sym) -> bool,
|
||||
) -> Option<MatchState<'a>> {
|
||||
vec_match(&self.0, seq, &save_loc)
|
||||
save_loc: &dyn Fn(Sym) -> bool,
|
||||
) -> Option<(&'a [MacTree], MatchState<'a>, &'a [MacTree])> {
|
||||
let mut result = any_match(&self.inner, seq, &save_loc)?;
|
||||
async fn remove_frame<'a>(result: &mut MatchState<'a>, key: &str) -> &'a [MacTree] {
|
||||
match result.remove(i().i(key).await) {
|
||||
Some(StateEntry::Scalar(_)) => panic!("{key} is defined in the constructor as a Vec"),
|
||||
Some(StateEntry::Vec(v)) => v,
|
||||
None => &[],
|
||||
}
|
||||
}
|
||||
let before = remove_frame(&mut result, "::before").await;
|
||||
let after = remove_frame(&mut result, "::after").await;
|
||||
Some((before, result, after))
|
||||
}
|
||||
}
|
||||
impl fmt::Display for PriodMatcher {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) }
|
||||
impl fmt::Display for Matcher {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) }
|
||||
}
|
||||
impl fmt::Debug for PriodMatcher {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "PriodMatcher({self})") }
|
||||
impl fmt::Debug for Matcher {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "NamedMatcher({self})") }
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ pub fn scal_match<'a>(
|
||||
(ScalMatcher::Placeh { key }, _) =>
|
||||
Some(MatchState::from_ph(key.clone(), StateEntry::Scalar(expr))),
|
||||
(ScalMatcher::S(c1, b_mat), MacTok::S(c2, body)) if c1 == c2 =>
|
||||
any_match(b_mat, &body[..], save_loc),
|
||||
any_match(b_mat, &body.items, save_loc),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,9 @@ impl<'a> MatchState<'a> {
|
||||
pub fn from_name(name: Sym, location: Pos) -> Self {
|
||||
Self { name_posv: HashMap::from([(name, vec![location])]), placeholders: HashMap::new() }
|
||||
}
|
||||
pub fn names(&self) -> impl Iterator<Item = (Sym, &[Pos])> {
|
||||
self.name_posv.iter().map(|(sym, vec)| (sym.clone(), &vec[..]))
|
||||
}
|
||||
pub fn get(&self, key: &Tok<String>) -> Option<&StateEntry<'a>> { self.placeholders.get(key) }
|
||||
pub fn remove(&mut self, name: Tok<String>) -> Option<StateEntry<'a>> {
|
||||
self.placeholders.remove(&name)
|
||||
|
||||
177
orchid-std/src/macros/std_macros.rs
Normal file
177
orchid-std/src/macros/std_macros.rs
Normal file
@@ -0,0 +1,177 @@
|
||||
use futures::{StreamExt, stream};
|
||||
use orchid_base::error::OrcRes;
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::TAtom;
|
||||
use orchid_extension::atom_owned::own;
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::coroutine_exec::exec;
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::gen_expr::{GExpr, call, sym_ref};
|
||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||
|
||||
use crate::macros::match_macros::MatcherAtom;
|
||||
use crate::macros::resolve::resolve;
|
||||
use crate::macros::utils::{build_macro, mactree, mactreev};
|
||||
use crate::{HomoTpl, MacTree, OrcOpt, Tpl};
|
||||
|
||||
pub async fn gen_std_macro_lib() -> Vec<GenMember> {
|
||||
prefix("std", [
|
||||
prefix("option", [
|
||||
fun(false, "is_some_body", |sub: TAtom<MatcherAtom>, val: OrcOpt<Expr>| {
|
||||
exec(async move |mut h| {
|
||||
let Some(sub_val) = val.0 else { return Ok(OrcOpt(None)) };
|
||||
h.exec::<OrcOpt<Expr>>(call(sub.to_gen().await, [sub_val.to_gen().await])).await
|
||||
})
|
||||
}),
|
||||
fun(false, "is_none_body", async |val: OrcOpt<Expr>| {
|
||||
if val.0.is_none() { OrcOpt(Some(Tpl(()))) } else { OrcOpt(None) }
|
||||
}),
|
||||
build_macro(None, ["of", "empty"])
|
||||
.rule(mactreev!(pattern::match_rule ( std::option::of "...$" sub_pattern 0)), [
|
||||
|[sub]: [_; _]| {
|
||||
exec(async move |mut h| {
|
||||
let sub = h
|
||||
.exec::<TAtom<MatcherAtom>>(
|
||||
resolve(mactree!(pattern::match_rule "push" sub;)).await,
|
||||
)
|
||||
.await?;
|
||||
Ok(MatcherAtom {
|
||||
keys: sub.keys().collect().await,
|
||||
matcher: h
|
||||
.register(call(sym_ref(sym!(std::option::is_some_body; i())), [sub
|
||||
.to_gen()
|
||||
.await]))
|
||||
.await,
|
||||
})
|
||||
})
|
||||
},
|
||||
])
|
||||
.rule(mactreev!(pattern::match_rule(std::option::empty)), [|[]: [_; _]| {
|
||||
exec(async |mut h| {
|
||||
Ok(MatcherAtom {
|
||||
keys: vec![],
|
||||
matcher: h.register(sym_ref(sym!(std::option::is_none_body; i()))).await,
|
||||
})
|
||||
})
|
||||
}])
|
||||
.finish(),
|
||||
]),
|
||||
prefix("tuple", [
|
||||
build_macro(None, ["t"])
|
||||
.rule(mactreev!(std::tuple::t [ "...$" elements 0 ]), [|[elements]: [_; _]| {
|
||||
exec(async move |mut h| {
|
||||
let tup = h
|
||||
.exec::<HomoTpl<TAtom<MacTree>>>(call(sym_ref(sym!(macros::resolve; i())), [
|
||||
mactree!((macros::common::comma_list "push" elements ;)).to_gen().await,
|
||||
]))
|
||||
.await?;
|
||||
let val = stream::iter(&tup.0[..])
|
||||
.fold(sym_ref(sym!(std::tuple::empty; i())), async |head, new| {
|
||||
call(sym_ref(sym!(std::tuple::cat; i())), [
|
||||
head,
|
||||
call(sym_ref(sym!(std::tuple::one; i())), [call(
|
||||
sym_ref(sym!(macros::resolve; i())),
|
||||
[new.clone().to_gen().await],
|
||||
)]),
|
||||
])
|
||||
})
|
||||
.await;
|
||||
Ok(val)
|
||||
})
|
||||
}])
|
||||
.rule(
|
||||
mactreev!(pattern::match_rule(std::tuple::t[ "...$" elements 0 macros::common::..])),
|
||||
[async |[elements]: [_; _]| parse_tpl(elements, Some(mactree!(macros::common::_))).await],
|
||||
)
|
||||
.rule(
|
||||
mactreev!(pattern::match_rule(
|
||||
std::tuple::t[ "...$" elements 1 macros::common::.. "...$" tail 0]
|
||||
)),
|
||||
[async |[elements, tail]: [_; _]| parse_tpl(elements, Some(tail)).await],
|
||||
)
|
||||
.rule(mactreev!(pattern::match_rule(std::tuple::t[ "...$" elements 0])), [
|
||||
|[elements]: [_; _]| parse_tpl(elements, None),
|
||||
])
|
||||
.finish(),
|
||||
fun(false, "matcher_body", tuple_matcher_body),
|
||||
]),
|
||||
])
|
||||
}
|
||||
|
||||
fn parse_tpl(elements: MacTree, tail_matcher: Option<MacTree>) -> impl Future<Output = GExpr> {
|
||||
exec(async move |mut h| -> OrcRes<MatcherAtom> {
|
||||
let tup = h
|
||||
.exec::<HomoTpl<TAtom<MacTree>>>(call(sym_ref(sym!(macros::resolve; i())), [
|
||||
mactree!((macros::common::comma_list "push" elements ;)).to_gen().await,
|
||||
]))
|
||||
.await?;
|
||||
let mut subs = Vec::with_capacity(tup.0.len());
|
||||
for mac_a in &tup.0[..] {
|
||||
let mac = own(mac_a).await;
|
||||
let sub = h
|
||||
.exec::<TAtom<MatcherAtom>>(call(sym_ref(sym!(macros::resolve; i())), [
|
||||
mactree!(pattern::match_rule "push" mac ;).to_gen().await,
|
||||
]))
|
||||
.await?;
|
||||
subs.push(sub);
|
||||
}
|
||||
let tail_matcher = match tail_matcher {
|
||||
Some(mac) => Some(
|
||||
h.exec::<TAtom<MatcherAtom>>(call(sym_ref(sym!(macros::resolve; i())), [
|
||||
mactree!(pattern::match_rule "push" mac ;).to_gen().await,
|
||||
]))
|
||||
.await?,
|
||||
),
|
||||
None => None,
|
||||
};
|
||||
Ok(MatcherAtom {
|
||||
keys: stream::iter(&subs[..])
|
||||
.flat_map(|t| t.keys())
|
||||
.chain(stream::iter(&tail_matcher).flat_map(|mat| mat.keys()))
|
||||
.collect()
|
||||
.await,
|
||||
matcher: call(sym_ref(sym!(std::tuple::matcher_body; i())), [
|
||||
HomoTpl(subs).to_gen().await,
|
||||
OrcOpt(tail_matcher).to_gen().await,
|
||||
])
|
||||
.to_expr()
|
||||
.await,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn tuple_matcher_body(
|
||||
children: HomoTpl<TAtom<MatcherAtom>>,
|
||||
tail: OrcOpt<TAtom<MatcherAtom>>,
|
||||
value: HomoTpl<Expr>,
|
||||
) -> impl Future<Output = GExpr> {
|
||||
exec(async move |mut h| -> OrcRes<OrcOpt<GExpr>> {
|
||||
if value.0.len() < children.0.len() {
|
||||
return Ok(OrcOpt(None));
|
||||
}
|
||||
let mut binds = Vec::new();
|
||||
for (sub_mat, sub_val) in children.0.iter().zip(&value.0) {
|
||||
match sub_mat.run_matcher(&mut h, sub_val.clone()).await? {
|
||||
OrcOpt(None) => return Ok(OrcOpt(None)),
|
||||
OrcOpt(Some(subres)) => binds.extend(subres.0),
|
||||
}
|
||||
}
|
||||
match tail.0 {
|
||||
None if children.0.len() < value.0.len() => return Ok(OrcOpt(None)),
|
||||
None => (),
|
||||
Some(tail_mat) => {
|
||||
let tail_tpl = stream::iter(&value.0[children.0.len()..])
|
||||
.fold(sym_ref(sym!(std::tuple::empty; i())), async |prefix, new| {
|
||||
call(sym_ref(sym!(std::tuple::cat; i())), [prefix, new.clone().to_gen().await])
|
||||
})
|
||||
.await;
|
||||
match tail_mat.run_matcher(&mut h, tail_tpl).await? {
|
||||
OrcOpt(Some(tail_binds)) => binds.extend(tail_binds.0),
|
||||
OrcOpt(None) => return Ok(OrcOpt(None)),
|
||||
}
|
||||
},
|
||||
};
|
||||
todo!()
|
||||
})
|
||||
}
|
||||
@@ -1,166 +1,275 @@
|
||||
use std::borrow::Cow;
|
||||
use std::rc::Rc;
|
||||
|
||||
use async_fn_stream::stream;
|
||||
use futures::StreamExt;
|
||||
use futures::future::LocalBoxFuture;
|
||||
use itertools::{Itertools, chain};
|
||||
use never::Never;
|
||||
use orchid_base::name::{NameLike, Sym, VPath};
|
||||
use orchid_extension::atom::{Atomic, TAtom};
|
||||
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::gen_expr::sym_ref;
|
||||
use orchid_extension::gen_expr::{GExpr, 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::macros::macro_value::{Macro, MacroData, Rule};
|
||||
use crate::macros::mactree::MacTreeSeq;
|
||||
use crate::macros::rule::matcher::Matcher;
|
||||
use crate::{MacTok, MacTree};
|
||||
|
||||
pub(crate) fn mk_macro<B: ToExpr + Clone + 'static>(
|
||||
pub type Args = Vec<MacTree>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MacroBodyArgCollector {
|
||||
argc: usize,
|
||||
args: Args,
|
||||
cb: Rc<dyn Fn(Args) -> LocalBoxFuture<'static, GExpr>>,
|
||||
}
|
||||
impl Atomic for MacroBodyArgCollector {
|
||||
type Data = ();
|
||||
type Variant = OwnedVariant;
|
||||
}
|
||||
impl OwnedAtom for MacroBodyArgCollector {
|
||||
type Refs = Never;
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||
async fn call_ref(&self, arg: orchid_extension::expr::Expr) -> GExpr {
|
||||
eprintln!("This is an intermediary value. It should never be copied");
|
||||
self.clone().call(arg).await
|
||||
}
|
||||
async fn call(mut self, arg: orchid_extension::expr::Expr) -> GExpr {
|
||||
let atom = (TAtom::downcast(arg.handle()).await).unwrap_or_else(|_| {
|
||||
panic!("This is an intermediary value, the argument types are known in advance")
|
||||
});
|
||||
self.args.push(own(&atom).await);
|
||||
if self.argc == self.args.len() {
|
||||
(self.cb)(self.args).await.to_gen().await
|
||||
} else {
|
||||
self.to_gen().await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn body_name(name: &str, counter: usize) -> String { format!("({name})::{counter}") }
|
||||
|
||||
pub(crate) fn build_macro(
|
||||
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()
|
||||
) -> MacroBuilder {
|
||||
MacroBuilder {
|
||||
prio,
|
||||
own_kws: own_kws.into_iter().collect(),
|
||||
patterns: Vec::new(),
|
||||
body_consts: Vec::new(),
|
||||
}
|
||||
}
|
||||
pub(crate) struct MacroBuilder {
|
||||
prio: Option<u64>,
|
||||
own_kws: Vec<&'static str>,
|
||||
patterns: Vec<MacTreeSeq>,
|
||||
body_consts: Vec<GenMember>,
|
||||
}
|
||||
impl MacroBuilder {
|
||||
pub(crate) fn rule<const N: usize, R: ToExpr>(
|
||||
mut self,
|
||||
pat: MacTreeSeq,
|
||||
body: [impl AsyncFn([MacTree; N]) -> R + 'static; 1],
|
||||
) -> Self {
|
||||
let [body] = body;
|
||||
let body = Rc::new(body);
|
||||
let name = &body_name(self.own_kws[0], self.body_consts.len());
|
||||
self.body_consts.extend(match N {
|
||||
0 => lazy(true, name, async move |_| {
|
||||
let argv = [].into_iter().collect_array().expect("N is 0");
|
||||
MemKind::Const(body(argv).await.to_gen().await)
|
||||
}),
|
||||
1.. => cnst(true, name, MacroBodyArgCollector {
|
||||
argc: N,
|
||||
args: Vec::new(),
|
||||
cb: Rc::new(move |argv| {
|
||||
let arr = argv.into_iter().collect_array::<N>().expect("argc should enforce the length");
|
||||
let body = body.clone();
|
||||
Box::pin(async move { body(arr).await.to_gen().await })
|
||||
}),
|
||||
}),
|
||||
});
|
||||
self.patterns.push(pat);
|
||||
self
|
||||
}
|
||||
pub(crate) fn finish(self) -> Vec<GenMember> {
|
||||
let Self { own_kws, prio, patterns, body_consts } = self;
|
||||
let name = own_kws[0];
|
||||
let main_const = lazy(true, name, async move |path| {
|
||||
let module = (Sym::new(path.split_last_seg().1.iter().cloned(), &i()).await)
|
||||
.expect("Default macro in global root");
|
||||
MemKind::Const(
|
||||
Macro(Rc::new(MacroData {
|
||||
module,
|
||||
prio,
|
||||
rules: stream(async |mut h| {
|
||||
for (counter, pattern) in patterns.into_iter().enumerate() {
|
||||
let mut placeholders = Vec::new();
|
||||
pattern.map(&mut false, &mut |tt| {
|
||||
if let MacTok::Ph(ph) = &*tt.tok {
|
||||
placeholders.push(ph.name.clone())
|
||||
}
|
||||
None
|
||||
});
|
||||
h.emit(Rule {
|
||||
matcher: Matcher::new(pattern.clone()).await.unwrap(),
|
||||
pattern,
|
||||
placeholders,
|
||||
body_name: i().i(&format!("({name})::{counter}")).await,
|
||||
})
|
||||
.await;
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
.await,
|
||||
}))
|
||||
.to_gen()
|
||||
.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()
|
||||
)
|
||||
});
|
||||
let kw_consts = own_kws[1..].iter().flat_map(|kw| {
|
||||
lazy(true, kw, async |path| {
|
||||
let main_const_name = VPath::new(path.split_last_seg().1.iter().cloned())
|
||||
.name_with_suffix(i().i(name).await)
|
||||
.to_sym(&i())
|
||||
.await;
|
||||
MemKind::Const(sym_ref(main_const_name))
|
||||
})
|
||||
});
|
||||
chain!(main_const, kw_consts, body_consts).collect()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! mactree {
|
||||
($i:expr; $($body:tt)*) => {
|
||||
$crate::macros::utils::mactreev!($i; ($($body)*)).remove(0)
|
||||
($($body:tt)*) => {
|
||||
$crate::macros::utils::mactreev!(($($body)*)).items[0].clone()
|
||||
};
|
||||
}
|
||||
|
||||
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)*);
|
||||
macro_rules! mactreev_impl {
|
||||
(@RECUR $ret:ident) => {};
|
||||
(@RECUR $ret:ident "..$" $name:ident $prio:literal $($tail:tt)*) => {
|
||||
$ret.push($crate::macros::mactree::MacTok::Ph($crate::macros::mactree::Ph{
|
||||
name: orchid_extension::context::i().i(stringify!($name)).await,
|
||||
kind: $crate::macros::mactree::PhKind::Vector{ at_least_one: false, priority: $prio }
|
||||
}).at(orchid_base::location::Pos::Inherit));
|
||||
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $i:expr; $ret:ident "...$" $name:ident $prio:literal $($tail:tt)*) => {
|
||||
$ret.push(MacTok::Ph(Ph{
|
||||
name: $i.i(stringify!($name)).await,
|
||||
(@RECUR $ret:ident "...$" $name:ident $prio:literal $($tail:tt)*) => {
|
||||
$ret.push($crate::macros::mactree::MacTok::Ph($crate::macros::mactree::Ph{
|
||||
name: orchid_extension::context::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)*);
|
||||
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $i:expr; $ret:ident "$" $name:ident $($tail:tt)*) => {
|
||||
$ret.push(MacTok::Ph(Ph{
|
||||
name: $i.i(stringify!(name)).await,
|
||||
(@RECUR $ret:ident "$" $name:ident $($tail:tt)*) => {
|
||||
$ret.push($crate::macros::mactree::MacTok::Ph($crate::macros::mactree::Ph{
|
||||
name: orchid_extension::context::i().i(stringify!(name)).await,
|
||||
kind: $crate::macros::mactree::PhKind::Scalar
|
||||
}).at(orchid_base::location::Pos::Inherit));
|
||||
mactreev!(@RECUR $i; $ret $($tail)*);
|
||||
$crate::macros::utils::mactreev_impl!(@RECUR $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 $ret:ident "Val" $arg:expr ; $($tail:tt)*) => {
|
||||
$ret.push(
|
||||
$crate::macros::mactree::MacTok::Value($arg)
|
||||
.at(orchid_base::location::Pos::Inherit)
|
||||
);
|
||||
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $i:expr; $ret:ident "" $arg:expr ; $($tail:tt)*) => {
|
||||
(@RECUR $ret:ident "push" $arg:expr ; $($tail:tt)*) => {
|
||||
$ret.push($arg);
|
||||
mactreev!(@RECUR $i; $ret $($tail)*);
|
||||
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $i:expr; $ret:ident "l_" $arg:expr ; ($($body:tt)*) $($tail:tt)*) => {
|
||||
(@RECUR $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)*)
|
||||
MacTok::Name($arg).at(orchid_base::location::Pos::Inherit),
|
||||
$crate::macros::utils::mactreev!($($body)*)
|
||||
).at(orchid_base::location::Pos::Inherit));
|
||||
mactreev!(@RECUR $i; $ret $($tail)*);
|
||||
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $i:expr; $ret:ident ( $($body:tt)* ) $($tail:tt)*) => {
|
||||
(@RECUR $ret:ident "l" $argh:tt $(:: $arg:tt)+ ($($body:tt)*) $($tail:tt)*) => {
|
||||
$ret.push(MacTok::Lambda(
|
||||
MacTok::Name(sym!($argh $(:: $arg)+; orchid_extension::context::i()).await).at(orchid_base::location::Pos::Inherit),
|
||||
$crate::macros::utils::mactreev!($($body)*)
|
||||
).at(orchid_base::location::Pos::Inherit));
|
||||
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $ret:ident $name:literal $($tail:tt)*) => {
|
||||
assert!(
|
||||
$name.contains("::"),
|
||||
"{} was treated as a name, but it doesn't have a namespace prefix",
|
||||
$name
|
||||
);
|
||||
let sym = orchid_base::name::Sym::parse(
|
||||
$name,
|
||||
&orchid_extension::context::i()
|
||||
).await.expect("Empty string in sym literal in Rust");
|
||||
$ret.push(
|
||||
MacTok::S(orchid_base::tree::Paren::Round, mactreev!($i; $($body)*))
|
||||
$crate::macros::mactree::MacTok::Name(sym)
|
||||
.at(orchid_base::location::Pos::Inherit)
|
||||
);
|
||||
mactreev!(@RECUR $i; $ret $($tail)*);
|
||||
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $i:expr; $ret:ident [ $($body:tt)* ] $($tail:tt)*) => {
|
||||
(@RECUR $ret:ident ( $($body:tt)* ) $($tail:tt)*) => {
|
||||
$ret.push(
|
||||
MacTok::S(orchid_base::tree::Paren::Square, mactreev!($i; $($body)*))
|
||||
$crate::macros::mactree::MacTok::S(
|
||||
orchid_base::tree::Paren::Round,
|
||||
$crate::macros::utils::mactreev!($($body)*)
|
||||
)
|
||||
.at(orchid_base::location::Pos::Inherit)
|
||||
);
|
||||
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $ret:ident [ $($body:tt)* ] $($tail:tt)*) => {
|
||||
$ret.push(
|
||||
$crate::macros::mactree::MacTok::S(
|
||||
orchid_base::tree::Paren::Square,
|
||||
$crate::macros::utils::mactreev!($($body)*)
|
||||
)
|
||||
.at(orchid_base::location::Pos::Inherit)
|
||||
);
|
||||
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $ret:ident { $($body:tt)* } $($tail:tt)*) => {
|
||||
$ret.push(
|
||||
$crate::macros::mactree::MacTok::S(
|
||||
orchid_base::tree::Paren::Curly,
|
||||
$crate::macros::utils::mactreev!($($body)*)
|
||||
)
|
||||
.at(orchid_base::location::Pos::Inherit)
|
||||
);
|
||||
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $ret:ident $ns:ident :: $nhead:tt $($tail:tt)*) => {
|
||||
$crate::macros::utils::mactreev_impl!(@NAME_MUNCHER $ret ($ns :: $nhead) $($tail)*)
|
||||
};
|
||||
(@NAME_MUNCHER $ret:ident ($($munched:tt)*) :: $name:tt $($tail:tt)*) => {
|
||||
$crate::macros::utils::mactreev_impl!(@NAME_MUNCHER $ret ($($munched)* :: $name) $($tail)*)
|
||||
};
|
||||
(@NAME_MUNCHER $ret:ident ($($munched:tt)*) $($tail:tt)*) => {
|
||||
let sym = orchid_base::sym!($($munched)* ; orchid_extension::context::i());
|
||||
$ret.push(
|
||||
$crate::macros::mactree::MacTok::Name(sym)
|
||||
.at(orchid_base::location::Pos::Inherit)
|
||||
);
|
||||
mactreev!(@RECUR $i; $ret $($tail)*);
|
||||
$crate::macros::utils::mactreev_impl!(@RECUR $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)*) => {
|
||||
() => { Vec::new() };
|
||||
}
|
||||
macro_rules! mactreev {
|
||||
($($tail:tt)*) => {
|
||||
{
|
||||
let mut ret = Vec::<MacTree>::new();
|
||||
mactreev!(@RECUR $i; ret $($tail)*);
|
||||
ret
|
||||
let mut ret = Vec::<$crate::macros::mactree::MacTree>::new();
|
||||
ret.extend([]); // silence unneeded mut warning
|
||||
$crate::macros::utils::mactreev_impl!(@RECUR ret $($tail)*);
|
||||
$crate::macros::mactree::MacTreeSeq::new(ret)
|
||||
}
|
||||
};
|
||||
}
|
||||
pub(crate) use {mactree, mactreev};
|
||||
|
||||
pub(crate) use {mactree, mactreev, mactreev_impl};
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
pub mod number;
|
||||
pub mod string;
|
||||
|
||||
pub mod option;
|
||||
pub mod protocol;
|
||||
pub mod record;
|
||||
pub mod reflection;
|
||||
pub mod std_system;
|
||||
pub mod string;
|
||||
pub mod tuple;
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
use orchid_api_derive::Coding;
|
||||
use orchid_api_traits::Request;
|
||||
use orchid_base::error::OrcRes;
|
||||
use orchid_base::format::FmtUnit;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::number::Numeric;
|
||||
use orchid_extension::atom::{AtomFactory, Atomic, AtomicFeatures, ToAtom, TAtom};
|
||||
use orchid_extension::atom::{AtomFactory, Atomic, AtomicFeatures, Supports, TAtom, ToAtom};
|
||||
use orchid_extension::atom_thin::{ThinAtom, ThinVariant};
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::conv::TryFromExpr;
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::system::SysCtx;
|
||||
use ordered_float::NotNan;
|
||||
use rust_decimal::prelude::Zero;
|
||||
|
||||
use crate::std::protocol::types::GetTagIdMethod;
|
||||
use crate::std::string::to_string::ToStringMethod;
|
||||
|
||||
#[derive(Clone, Debug, Coding)]
|
||||
pub struct Int(pub i64);
|
||||
impl Atomic for Int {
|
||||
@@ -17,13 +22,23 @@ impl Atomic for Int {
|
||||
type Data = Self;
|
||||
}
|
||||
impl ThinAtom for Int {
|
||||
async fn print(&self, _: SysCtx) -> FmtUnit { self.0.to_string().into() }
|
||||
async fn print(&self) -> FmtUnit { self.0.to_string().into() }
|
||||
}
|
||||
impl TryFromExpr for Int {
|
||||
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
|
||||
TAtom::<Int>::try_from_expr(expr).await.map(|t| t.value)
|
||||
}
|
||||
}
|
||||
impl Supports<GetTagIdMethod> for Int {
|
||||
async fn handle(&self, _: GetTagIdMethod) -> <GetTagIdMethod as Request>::Response {
|
||||
Sym::parse("std::number::Int", &i()).await.unwrap().to_api()
|
||||
}
|
||||
}
|
||||
impl Supports<ToStringMethod> for Int {
|
||||
async fn handle(&self, _: ToStringMethod) -> <ToStringMethod as Request>::Response {
|
||||
self.0.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Coding)]
|
||||
pub struct Float(pub NotNan<f64>);
|
||||
@@ -32,13 +47,18 @@ impl Atomic for Float {
|
||||
type Data = Self;
|
||||
}
|
||||
impl ThinAtom for Float {
|
||||
async fn print(&self, _: SysCtx) -> FmtUnit { self.0.to_string().into() }
|
||||
async fn print(&self) -> FmtUnit { self.0.to_string().into() }
|
||||
}
|
||||
impl TryFromExpr for Float {
|
||||
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
|
||||
Ok(Self(Num::try_from_expr(expr).await?.0.to_f64()))
|
||||
}
|
||||
}
|
||||
impl Supports<ToStringMethod> for Float {
|
||||
async fn handle(&self, _: ToStringMethod) -> <ToStringMethod as Request>::Response {
|
||||
self.0.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Num(pub Numeric);
|
||||
impl TryFromExpr for Num {
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::ops::RangeInclusive;
|
||||
use orchid_base::error::OrcRes;
|
||||
use orchid_base::number::{num_to_errv, parse_num};
|
||||
use orchid_extension::atom::ToAtom;
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::lexer::{LexContext, Lexer};
|
||||
use orchid_extension::tree::{GenTokTree, x_tok};
|
||||
|
||||
@@ -12,13 +13,13 @@ use super::num_atom::Num;
|
||||
pub struct NumLexer;
|
||||
impl Lexer for NumLexer {
|
||||
const CHAR_FILTER: &'static [RangeInclusive<char>] = &['0'..='9'];
|
||||
async fn lex<'a>(all: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree)> {
|
||||
async fn lex<'a>(all: &'a str, lxcx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree)> {
|
||||
let ends_at = all.find(|c: char| !c.is_ascii_hexdigit() && !"xX._pP".contains(c));
|
||||
let (chars, tail) = all.split_at(ends_at.unwrap_or(all.len()));
|
||||
let fac = match parse_num(chars) {
|
||||
Ok(numeric) => Num(numeric).to_atom_factory(),
|
||||
Err(e) => return Err(num_to_errv(e, ctx.pos(all), ctx.src(), ctx.ctx.i()).await),
|
||||
Err(e) => return Err(num_to_errv(e, lxcx.pos(all), lxcx.src(), &i()).await),
|
||||
};
|
||||
Ok((tail, x_tok(fac).await.at(ctx.pos_lt(chars.len(), tail))))
|
||||
Ok((tail, x_tok(fac).await.at(lxcx.pos_lt(chars.len(), tail))))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,28 +6,28 @@ use super::num_atom::{Float, HomoArray, Int, Num};
|
||||
|
||||
pub fn gen_num_lib() -> Vec<GenMember> {
|
||||
prefix("std::number", [
|
||||
fun(true, "add", |a: Num, b: Num| async move {
|
||||
fun(true, "add", async |a: Num, b: Num| {
|
||||
Num(match HomoArray::new([a.0, b.0]) {
|
||||
HomoArray::Int([a, b]) => Numeric::Int(a + b),
|
||||
HomoArray::Float([a, b]) => Numeric::Float(a + b),
|
||||
})
|
||||
}),
|
||||
fun(true, "neg", |a: Num| async move {
|
||||
fun(true, "neg", async |a: Num| {
|
||||
Num(match a.0 {
|
||||
Numeric::Int(i) => Numeric::Int(-i),
|
||||
Numeric::Float(f) => Numeric::Float(-f),
|
||||
})
|
||||
}),
|
||||
fun(true, "mul", |a: Num, b: Num| async move {
|
||||
fun(true, "mul", async |a: Num, b: Num| {
|
||||
Num(match HomoArray::new([a.0, b.0]) {
|
||||
HomoArray::Int([a, b]) => Numeric::Int(a * b),
|
||||
HomoArray::Float([a, b]) => Numeric::Float(a * b),
|
||||
})
|
||||
}),
|
||||
fun(true, "idiv", |a: Int, b: Int| async move { Int(a.0 / b.0) }),
|
||||
fun(true, "imod", |a: Int, b: Int| async move { Int(a.0 % b.0) }),
|
||||
fun(true, "fdiv", |a: Float, b: Float| async move { Float(a.0 / b.0) }),
|
||||
fun(true, "fmod", |a: Float, b: Float| async move {
|
||||
fun(true, "idiv", async |a: Int, b: Int| Int(a.0 / b.0)),
|
||||
fun(true, "imod", async |a: Int, b: Int| Int(a.0 % b.0)),
|
||||
fun(true, "fdiv", async |a: Float, b: Float| Float(a.0 / b.0)),
|
||||
fun(true, "fmod", async |a: Float, b: Float| {
|
||||
Float(a.0 - NotNan::new((a.0 / b.0).trunc()).unwrap() * b.0)
|
||||
}),
|
||||
])
|
||||
|
||||
75
orchid-std/src/std/option.rs
Normal file
75
orchid-std/src/std/option.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
use std::borrow::Cow;
|
||||
use std::pin::Pin;
|
||||
|
||||
use futures::AsyncWrite;
|
||||
use orchid_api_traits::Encode;
|
||||
use orchid_base::error::mk_errv;
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::{Atomic, ForeignAtom, TAtom};
|
||||
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::conv::{ToExpr, TryFromExpr};
|
||||
use orchid_extension::expr::{Expr, ExprHandle};
|
||||
use orchid_extension::gen_expr::{call, sym_ref};
|
||||
use orchid_extension::tree::{GenMember, cnst, fun, prefix};
|
||||
|
||||
use crate::{OrcString, api};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OptAtom(Option<Expr>);
|
||||
impl Atomic for OptAtom {
|
||||
type Data = Option<api::ExprTicket>;
|
||||
type Variant = OwnedVariant;
|
||||
}
|
||||
impl OwnedAtom for OptAtom {
|
||||
type Refs = Vec<Expr>;
|
||||
async fn val(&self) -> Cow<'_, Self::Data> {
|
||||
Cow::Owned(self.0.as_ref().map(|ex| ex.handle().ticket()))
|
||||
}
|
||||
async fn deserialize(mut ctx: impl DeserializeCtx, refs: Self::Refs) -> Self {
|
||||
Self(ctx.read::<bool>().await.then(|| refs.into_iter().next().unwrap()))
|
||||
}
|
||||
async fn serialize(&self, write: Pin<&mut (impl AsyncWrite + ?Sized)>) -> Self::Refs {
|
||||
self.0.is_some().encode(write).await;
|
||||
self.0.iter().cloned().collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OrcOpt<T>(pub Option<T>);
|
||||
impl<T: TryFromExpr> TryFromExpr for OrcOpt<T> {
|
||||
async fn try_from_expr(expr: Expr) -> orchid_base::error::OrcRes<Self> {
|
||||
let atom = TAtom::<OptAtom>::try_from_expr(expr).await?;
|
||||
match atom.value {
|
||||
None => Ok(OrcOpt(None)),
|
||||
Some(tk) => Ok(OrcOpt(Some(
|
||||
T::try_from_expr(Expr::from_handle(ExprHandle::from_ticket(tk).await)).await?,
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T: ToExpr + 'static> ToExpr for OrcOpt<T> {
|
||||
async fn to_gen(self) -> orchid_extension::gen_expr::GExpr {
|
||||
if let Some(val) = self.0 {
|
||||
call(sym_ref(sym!(std::option::some; i())), [val.to_gen().await])
|
||||
} else {
|
||||
sym_ref(sym!(std::option::none; i()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_option_lib() -> Vec<GenMember> {
|
||||
prefix("std::option", [
|
||||
cnst(true, "none", OptAtom(None)),
|
||||
fun(true, "some", async |ex: Expr| OptAtom(Some(ex))),
|
||||
fun(true, "expect", async |opt: ForeignAtom, msg: OrcString| {
|
||||
match OrcOpt::try_from_expr(opt.clone().ex()).await? {
|
||||
OrcOpt(Some(ex)) => Ok::<Expr, _>(ex),
|
||||
OrcOpt(None) => Err(mk_errv(
|
||||
i().i("Unwrapped std::option::none").await,
|
||||
msg.get_string().await.as_str(),
|
||||
[opt.pos()],
|
||||
)),
|
||||
}
|
||||
}),
|
||||
])
|
||||
}
|
||||
4
orchid-std/src/std/protocol/mod.rs
Normal file
4
orchid-std/src/std/protocol/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod parse_impls;
|
||||
pub mod proto_parser;
|
||||
pub mod type_parser;
|
||||
pub mod types;
|
||||
78
orchid-std/src/std/protocol/parse_impls.rs
Normal file
78
orchid-std/src/std/protocol/parse_impls.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
use itertools::{Itertools, chain};
|
||||
use orchid_base::error::{OrcRes, mk_errv};
|
||||
use orchid_base::interner::Tok;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::parse::{
|
||||
Import, ParseCtx, Parsed, Snippet, expect_tok, line_items, parse_multiname, token_errv,
|
||||
};
|
||||
use orchid_base::tree::{Paren, Token};
|
||||
use orchid_extension::parser::{
|
||||
PTokTree, ParsCtx, ParsedLine, ParsedLineKind, p_tree2gen, p_v2gen,
|
||||
};
|
||||
|
||||
pub async fn parse_impls(
|
||||
ctx: &ParsCtx<'_>,
|
||||
lines: &mut Vec<ParsedLine>,
|
||||
impls: &mut Vec<(Sym, Tok<String>)>,
|
||||
body_tt: &PTokTree,
|
||||
) -> OrcRes<()> {
|
||||
let i = ctx.i().clone();
|
||||
let body = match &body_tt.tok {
|
||||
Token::S(Paren::Round, body) => line_items(ctx, Snippet::new(body_tt, body)).await,
|
||||
Token::S(ptyp, _) =>
|
||||
return Err(mk_errv(
|
||||
i.i("Incorrect paren type").await,
|
||||
format!("Expected () block, found {ptyp}"),
|
||||
[body_tt.sr().pos()],
|
||||
)),
|
||||
_ =>
|
||||
return Err(
|
||||
token_errv(ctx, body_tt, "Expected body", |s| {
|
||||
format!("Expected (impl ...) block, found {s}")
|
||||
})
|
||||
.await,
|
||||
),
|
||||
};
|
||||
for Parsed { tail: line, output: comments } in body {
|
||||
if let Ok(Parsed { tail, .. }) = expect_tok(ctx, line, i.i("impl").await).await {
|
||||
let Parsed { tail, output: name_tt } = parse_multiname(ctx, tail).await?;
|
||||
let (name, name_sr) = match name_tt.into_iter().at_most_one() {
|
||||
Ok(None) => panic!("multiname is always at least one name"),
|
||||
Ok(Some(ref n @ Import { name: Some(_), ref sr, .. })) =>
|
||||
(n.clone().mspath().to_sym(&i).await, sr.clone()),
|
||||
Ok(Some(Import { name: None, sr, .. })) =>
|
||||
return Err(mk_errv(
|
||||
i.i("impl line with globstar").await,
|
||||
"::* is not permitted in a protocol impl",
|
||||
[sr.pos()],
|
||||
)),
|
||||
Err(e) =>
|
||||
return Err(mk_errv(
|
||||
i.i("Impl line with multiple protocol names").await,
|
||||
"::() is not permitted in a protocol impl",
|
||||
e.map(|i| i.sr.pos()),
|
||||
)),
|
||||
};
|
||||
let Parsed { tail, .. } = expect_tok(ctx, tail, i.i("as").await).await?;
|
||||
let cnst_name = i.i(&format!("{}{}", lines.len(), name.iter().join("__"))).await;
|
||||
lines.push(ParsedLine {
|
||||
comments,
|
||||
sr: line.sr(),
|
||||
kind: ParsedLineKind::Rec(Vec::from_iter(chain![
|
||||
[Token::Name(i.i("let").await).at(line.sr())],
|
||||
[Token::Name(cnst_name.clone()).at(name_sr)],
|
||||
[Token::Name(i.i("=").await).at(line.sr())],
|
||||
tail.iter().cloned().map(p_tree2gen),
|
||||
])),
|
||||
});
|
||||
impls.push((name, cnst_name));
|
||||
} else {
|
||||
lines.push(ParsedLine {
|
||||
sr: line.sr(),
|
||||
comments,
|
||||
kind: ParsedLineKind::Rec(p_v2gen(line.to_vec())),
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
77
orchid-std/src/std/protocol/proto_parser.rs
Normal file
77
orchid-std/src/std/protocol/proto_parser.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use orchid_base::error::{OrcRes, mk_errv};
|
||||
use orchid_base::parse::{Comment, Parsed, expect_end, try_pop_no_fluff};
|
||||
use orchid_base::sym;
|
||||
use orchid_base::tree::Token;
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::coroutine_exec::exec;
|
||||
use orchid_extension::gen_expr::{call, sym_ref};
|
||||
use orchid_extension::parser::{PSnippet, ParsCtx, ParsedLine, Parser};
|
||||
|
||||
use crate::std::protocol::parse_impls::parse_impls;
|
||||
use crate::std::protocol::types::Tag;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct AsProtoParser;
|
||||
impl Parser for AsProtoParser {
|
||||
const LINE_HEAD: &'static str = "as_proto";
|
||||
async fn parse<'a>(
|
||||
pcx: ParsCtx<'a>,
|
||||
exported: bool,
|
||||
cmts: Vec<Comment>,
|
||||
line: PSnippet<'a>,
|
||||
) -> OrcRes<Vec<ParsedLine>> {
|
||||
let Parsed { output: body_tt, tail } = try_pop_no_fluff(&pcx, line).await?;
|
||||
expect_end(&pcx, tail).await?;
|
||||
if exported {
|
||||
return Err(mk_errv(
|
||||
i().i("Exported internal line").await,
|
||||
"as_proto cannot be exported, the type shares the enclosing module's visibility",
|
||||
[line.sr().pos()],
|
||||
));
|
||||
}
|
||||
let mut lines = Vec::new();
|
||||
let mut impls = Vec::new();
|
||||
parse_impls(&pcx, &mut lines, &mut impls, body_tt).await?;
|
||||
let id = pcx.module();
|
||||
let proto_tag_name = i().i("__protocol_tag__").await;
|
||||
let proto_tag_path = id.suffix([proto_tag_name.clone()], &i()).await;
|
||||
lines.push(ParsedLine::cnst(&line.sr(), &cmts, true, proto_tag_name, async |_ccx| {
|
||||
exec(async move |mut h| {
|
||||
let mut new_impls = HashMap::new();
|
||||
for (k, v) in impls {
|
||||
new_impls.insert(k.clone(), h.register(sym_ref(id.suffix([v], &i()).await)).await);
|
||||
}
|
||||
Tag { id, impls: Rc::new(new_impls) }
|
||||
})
|
||||
.await
|
||||
}));
|
||||
lines.push(ParsedLine::cnst(&line.sr(), [], false, i().i("resolve").await, async move |_| {
|
||||
call(sym_ref(sym!(std::protocol::resolve; i())), [sym_ref(proto_tag_path)])
|
||||
}));
|
||||
Ok(lines)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ProtoParser;
|
||||
impl Parser for ProtoParser {
|
||||
const LINE_HEAD: &'static str = "proto";
|
||||
async fn parse<'a>(
|
||||
ctx: ParsCtx<'a>,
|
||||
exported: bool,
|
||||
cmts: Vec<Comment>,
|
||||
line: PSnippet<'a>,
|
||||
) -> OrcRes<Vec<ParsedLine>> {
|
||||
let Parsed { output: name_tt, tail } = try_pop_no_fluff(&ctx, line).await?;
|
||||
let Token::Name(name) = &name_tt.tok else {
|
||||
return Err(mk_errv(i().i("missing name for type").await, "A type needs a name", [name_tt
|
||||
.sr()
|
||||
.pos()]));
|
||||
};
|
||||
let lines = AsProtoParser::parse(ctx, false, cmts.clone(), tail).await?;
|
||||
Ok(vec![ParsedLine::module(&line.sr(), &cmts, exported, name, true, lines)])
|
||||
}
|
||||
}
|
||||
82
orchid-std/src/std/protocol/type_parser.rs
Normal file
82
orchid-std/src/std/protocol/type_parser.rs
Normal file
@@ -0,0 +1,82 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use orchid_base::error::{OrcRes, mk_errv};
|
||||
use orchid_base::parse::{Comment, Parsed, expect_end, try_pop_no_fluff};
|
||||
use orchid_base::sym;
|
||||
use orchid_base::tree::Token;
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::coroutine_exec::exec;
|
||||
use orchid_extension::gen_expr::{call, sym_ref};
|
||||
use orchid_extension::parser::{PSnippet, ParsCtx, ParsedLine, Parser};
|
||||
|
||||
use crate::std::protocol::parse_impls::parse_impls;
|
||||
use crate::std::protocol::types::Tag;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct AsTypeParser;
|
||||
impl Parser for AsTypeParser {
|
||||
const LINE_HEAD: &'static str = "as_type";
|
||||
async fn parse<'a>(
|
||||
ctx: ParsCtx<'a>,
|
||||
exported: bool,
|
||||
cmts: Vec<Comment>,
|
||||
line: PSnippet<'a>,
|
||||
) -> OrcRes<Vec<ParsedLine>> {
|
||||
let Parsed { output: body_tt, tail } = try_pop_no_fluff(&ctx, line).await?;
|
||||
expect_end(&ctx, tail).await?;
|
||||
if exported {
|
||||
return Err(mk_errv(
|
||||
i().i("Exported internal line").await,
|
||||
"as_type cannot be exported, the type shares the enclosing module's visibility",
|
||||
[line.sr().pos()],
|
||||
));
|
||||
}
|
||||
let mut lines = Vec::new();
|
||||
let mut impls = Vec::new();
|
||||
parse_impls(&ctx, &mut lines, &mut impls, body_tt).await?;
|
||||
let id = ctx.module();
|
||||
let type_tag_name = i().i("__type_tag__").await;
|
||||
let type_tag_path = id.suffix([type_tag_name.clone()], &i()).await;
|
||||
lines.push(ParsedLine::cnst(&line.sr(), &cmts, true, type_tag_name, async |_ccx| {
|
||||
exec(async move |mut h| {
|
||||
let mut new_impls = HashMap::new();
|
||||
for (k, v) in impls {
|
||||
new_impls.insert(k.clone(), h.register(sym_ref(id.suffix([v], &i()).await)).await);
|
||||
}
|
||||
Tag { id, impls: Rc::new(new_impls) }
|
||||
})
|
||||
.await
|
||||
}));
|
||||
let type_tag_path_1 = type_tag_path.clone();
|
||||
lines.push(ParsedLine::cnst(&line.sr(), [], false, i().i("wrap").await, async move |_ccx| {
|
||||
call(sym_ref(sym!(std::protocol::wrap; i())), [sym_ref(type_tag_path_1)])
|
||||
}));
|
||||
let type_tag_path_1 = type_tag_path.clone();
|
||||
lines.push(ParsedLine::cnst(&line.sr(), [], false, i().i("unwrap").await, async move |_ccx| {
|
||||
call(sym_ref(sym!(std::protocol::unwrap; i())), [sym_ref(type_tag_path_1)])
|
||||
}));
|
||||
Ok(lines)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TypeParser;
|
||||
impl Parser for TypeParser {
|
||||
const LINE_HEAD: &'static str = "type";
|
||||
async fn parse<'a>(
|
||||
ctx: ParsCtx<'a>,
|
||||
exported: bool,
|
||||
cmts: Vec<Comment>,
|
||||
line: PSnippet<'a>,
|
||||
) -> OrcRes<Vec<ParsedLine>> {
|
||||
let Parsed { output: name_tt, tail } = try_pop_no_fluff(&ctx, line).await?;
|
||||
let Token::Name(name) = &name_tt.tok else {
|
||||
return Err(mk_errv(i().i("missing name for type").await, "A type needs a name", [name_tt
|
||||
.sr()
|
||||
.pos()]));
|
||||
};
|
||||
let lines = AsTypeParser::parse(ctx, false, cmts.clone(), tail).await?;
|
||||
Ok(vec![ParsedLine::module(&line.sr(), &cmts, exported, name, true, lines)])
|
||||
}
|
||||
}
|
||||
141
orchid-std/src/std/protocol/types.rs
Normal file
141
orchid-std/src/std/protocol/types.rs
Normal file
@@ -0,0 +1,141 @@
|
||||
use std::borrow::Cow;
|
||||
use std::rc::Rc;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use never::Never;
|
||||
use orchid_api_derive::Coding;
|
||||
use orchid_api_traits::Request;
|
||||
use orchid_base::error::{OrcRes, mk_errv};
|
||||
use orchid_base::format::fmt;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_extension::atom::{AtomMethod, Atomic, ForeignAtom, MethodSetBuilder, Supports, TAtom};
|
||||
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::gen_expr::call;
|
||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||
|
||||
use crate::api;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Tag {
|
||||
pub id: Sym,
|
||||
pub impls: Rc<HashMap<Sym, Expr>>,
|
||||
}
|
||||
impl Atomic for Tag {
|
||||
type Data = api::TStrv;
|
||||
type Variant = OwnedVariant;
|
||||
fn reg_reqs() -> MethodSetBuilder<Self> { MethodSetBuilder::new().handle::<GetImplMethod>() }
|
||||
}
|
||||
impl OwnedAtom for Tag {
|
||||
type Refs = Never;
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.id.to_api()) }
|
||||
}
|
||||
impl Supports<GetImplMethod> for Tag {
|
||||
async fn handle(&self, req: GetImplMethod) -> <GetImplMethod as Request>::Response {
|
||||
self.impls.get(&Sym::from_api(req.0, &i()).await).map(|expr| expr.handle().ticket())
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, Coding)]
|
||||
pub struct GetImplMethod(pub api::TStrv);
|
||||
impl Request for GetImplMethod {
|
||||
type Response = Option<api::ExprTicket>;
|
||||
}
|
||||
impl AtomMethod for GetImplMethod {
|
||||
const NAME: &str = "std::protocol::get_impl";
|
||||
}
|
||||
#[derive(Clone, Debug, Coding)]
|
||||
pub struct GetTagIdMethod;
|
||||
impl Request for GetTagIdMethod {
|
||||
type Response = api::TStrv;
|
||||
}
|
||||
impl AtomMethod for GetTagIdMethod {
|
||||
const NAME: &str = "std::protocol::get_tag_id";
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Tagged {
|
||||
pub tag: Tag,
|
||||
pub value: Expr,
|
||||
}
|
||||
impl Atomic for Tagged {
|
||||
type Data = api::TStrv;
|
||||
type Variant = OwnedVariant;
|
||||
}
|
||||
impl OwnedAtom for Tagged {
|
||||
type Refs = Never;
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.tag.id.to_api()) }
|
||||
}
|
||||
impl Supports<GetImplMethod> for Tagged {
|
||||
async fn handle(&self, req: GetImplMethod) -> <GetImplMethod as Request>::Response {
|
||||
self.tag.handle(req).await
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_impl(receiver: ForeignAtom, proto: ForeignAtom) -> OrcRes<Expr> {
|
||||
let Some(proto_id) = proto.request(GetTagIdMethod).await else {
|
||||
return Err(mk_errv(i().i("Not a protocol").await, "Protocol does not have a tag ID", [
|
||||
proto.pos()
|
||||
]));
|
||||
};
|
||||
let Some(impl_val_opt) = receiver.request(GetImplMethod(proto_id)).await else {
|
||||
return Err(mk_errv(
|
||||
i().i("Receiver not tagged").await,
|
||||
"The receiver does not have a type tag",
|
||||
[receiver.pos()],
|
||||
));
|
||||
};
|
||||
if let Some(impl_val) = impl_val_opt {
|
||||
return Ok(Expr::deserialize(impl_val).await);
|
||||
}
|
||||
let Some(type_id) = receiver.request(GetTagIdMethod).await else {
|
||||
return Err(mk_errv(
|
||||
i().i("Incorrect protocols implementation in extension").await,
|
||||
"Atom provides an impl table but no tag ID",
|
||||
[receiver.pos()],
|
||||
));
|
||||
};
|
||||
let Some(impl_val_opt) = proto.request(GetImplMethod(type_id)).await else {
|
||||
return Err(mk_errv(
|
||||
i().i("Incorrect protocols implementation in extension").await,
|
||||
"Proto table atom provides a tag ID but no impl table",
|
||||
[receiver.pos()],
|
||||
));
|
||||
};
|
||||
if let Some(impl_val) = impl_val_opt {
|
||||
return Ok(Expr::deserialize(impl_val).await);
|
||||
}
|
||||
return Err(mk_errv(
|
||||
i().i("Implementation not found").await,
|
||||
"This protocol is not implemented for this receiver",
|
||||
[receiver.pos(), proto.pos()],
|
||||
));
|
||||
}
|
||||
|
||||
pub fn gen_protocol_lib() -> Vec<GenMember> {
|
||||
prefix("std::protocol", [
|
||||
fun(false, "resolve", async |tag: ForeignAtom, value: ForeignAtom| {
|
||||
Ok(call(get_impl(value.clone(), tag).await?.to_gen().await, [value.to_gen().await]))
|
||||
}),
|
||||
fun(false, "wrap", async |tag: TAtom<Tag>, value: Expr| Tagged { tag: own(&tag).await, value }),
|
||||
fun(false, "unwrap", async |tag: TAtom<Tag>, value: TAtom<Tagged>| {
|
||||
let own_tag = own(&tag).await;
|
||||
let own_val = own(&value).await;
|
||||
if own_val.tag.id == own_tag.id {
|
||||
Ok(own_val.value.to_gen().await)
|
||||
} else {
|
||||
Err(mk_errv(
|
||||
i().i("Type mismatch").await,
|
||||
format!(
|
||||
"{} has type {}, expected {}",
|
||||
fmt(&value, &i()).await,
|
||||
own_val.tag.id,
|
||||
own_tag.id
|
||||
),
|
||||
[value.pos()],
|
||||
))
|
||||
}
|
||||
}),
|
||||
])
|
||||
}
|
||||
2
orchid-std/src/std/record/mod.rs
Normal file
2
orchid-std/src/std/record/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod record_atom;
|
||||
pub mod record_lib;
|
||||
39
orchid-std/src/std/record/record_atom.rs
Normal file
39
orchid-std/src/std/record/record_atom.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use std::borrow::Cow;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::AsyncWrite;
|
||||
use futures::future::join_all;
|
||||
use hashbrown::HashMap;
|
||||
use orchid_api_traits::Encode;
|
||||
use orchid_base::interner::Tok;
|
||||
use orchid_extension::atom::Atomic;
|
||||
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::expr::Expr;
|
||||
|
||||
use crate::api;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Record(pub Rc<HashMap<Tok<String>, Expr>>);
|
||||
impl Atomic for Record {
|
||||
type Data = ();
|
||||
type Variant = OwnedVariant;
|
||||
}
|
||||
impl OwnedAtom for Record {
|
||||
type Refs = Vec<Expr>;
|
||||
async fn serialize(&self, write: Pin<&mut (impl AsyncWrite + ?Sized)>) -> Self::Refs {
|
||||
let (keys, values) =
|
||||
self.0.iter().map(|(k, v)| (k.to_api(), v.clone())).unzip::<_, _, Vec<_>, Vec<_>>();
|
||||
keys.encode(write).await;
|
||||
values
|
||||
}
|
||||
async fn deserialize(mut dctx: impl DeserializeCtx, refs: Self::Refs) -> Self {
|
||||
let keys =
|
||||
join_all(dctx.decode::<Vec<api::TStr>>().await.iter().map(|t| async { i().ex(*t).await }))
|
||||
.await;
|
||||
Record(Rc::new(keys.into_iter().zip(refs).collect()))
|
||||
}
|
||||
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||
}
|
||||
30
orchid-std/src/std/record/record_lib.rs
Normal file
30
orchid-std/src/std/record/record_lib.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use orchid_extension::atom::TAtom;
|
||||
use orchid_extension::atom_owned::own;
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::tree::{GenMember, cnst, fun, prefix};
|
||||
|
||||
use crate::std::option::OrcOpt;
|
||||
use crate::std::record::record_atom::Record;
|
||||
use crate::std::string::str_atom::IntStrAtom;
|
||||
|
||||
pub fn gen_record_lib() -> Vec<GenMember> {
|
||||
prefix("std::record", [
|
||||
cnst(true, "empty", Record(Rc::new(HashMap::new()))),
|
||||
fun(true, "set", async |map: TAtom<Record>, key: IntStrAtom, val: Expr| {
|
||||
let mut map = own(&map).await.0.as_ref().clone();
|
||||
map.insert(key.0.clone(), val);
|
||||
Record(Rc::new(map))
|
||||
}),
|
||||
fun(true, "get", async |map: TAtom<Record>, key: IntStrAtom| {
|
||||
OrcOpt(own(&map).await.0.get(&key.0).cloned())
|
||||
}),
|
||||
fun(true, "delete", async |map: TAtom<Record>, key: IntStrAtom| {
|
||||
let mut map = own(&map).await.0.as_ref().clone();
|
||||
map.remove(&key.0);
|
||||
Record(Rc::new(map))
|
||||
}),
|
||||
])
|
||||
}
|
||||
1
orchid-std/src/std/reflection/mod.rs
Normal file
1
orchid-std/src/std/reflection/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod sym_atom;
|
||||
67
orchid-std/src/std/reflection/sym_atom.rs
Normal file
67
orchid-std/src/std/reflection/sym_atom.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use orchid_api::TStrv;
|
||||
use orchid_api_derive::{Coding, Hierarchy};
|
||||
use orchid_api_traits::Request;
|
||||
use orchid_base::error::mk_errv;
|
||||
use orchid_base::name::{NameLike, Sym};
|
||||
use orchid_extension::atom::{Atomic, Supports, TAtom};
|
||||
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::expr::{Expr, ExprHandle};
|
||||
use orchid_extension::system::dep_req;
|
||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||
|
||||
use crate::std::std_system::StdReq;
|
||||
use crate::std::string::str_atom::IntStrAtom;
|
||||
use crate::std::string::to_string::ToStringMethod;
|
||||
use crate::{HomoTpl, StdSystem, api};
|
||||
|
||||
#[derive(Clone, Coding)]
|
||||
pub struct SymAtomData(pub api::TStrv);
|
||||
#[derive(Clone)]
|
||||
pub struct SymAtom(pub(crate) Sym);
|
||||
impl Atomic for SymAtom {
|
||||
type Data = SymAtomData;
|
||||
type Variant = OwnedVariant;
|
||||
}
|
||||
impl OwnedAtom for SymAtom {
|
||||
type Refs = ();
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(SymAtomData(self.0.tok().to_api())) }
|
||||
}
|
||||
impl Supports<ToStringMethod> for SymAtom {
|
||||
async fn handle(&self, _: ToStringMethod) -> <ToStringMethod as Request>::Response {
|
||||
self.0.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||
#[extends(StdReq)]
|
||||
pub struct CreateSymAtom(pub TStrv);
|
||||
impl Request for CreateSymAtom {
|
||||
type Response = api::ExprTicket;
|
||||
}
|
||||
|
||||
pub async fn sym_expr(sym: Sym) -> Expr {
|
||||
Expr::from_handle(ExprHandle::deserialize(
|
||||
dep_req::<StdSystem, _>(CreateSymAtom(sym.to_api())).await,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn gen_sym_lib() -> Vec<GenMember> {
|
||||
prefix("std::refl::sym", [
|
||||
fun(true, "from_str", async move |str: TAtom<IntStrAtom>| {
|
||||
match Sym::parse(&i().ex(*str).await, &i()).await {
|
||||
Ok(sym) => Ok(SymAtom(sym)),
|
||||
Err(_) => Err(mk_errv(
|
||||
i().i("Cannot parse sym from empty string").await,
|
||||
"Empty string passed to std::refl::sym::from_str",
|
||||
[str.pos()],
|
||||
)),
|
||||
}
|
||||
}),
|
||||
fun(true, "to_tpl", async move |sym: TAtom<SymAtom>| {
|
||||
HomoTpl(own(&sym).await.0.segs().map(IntStrAtom).collect())
|
||||
}),
|
||||
])
|
||||
}
|
||||
@@ -1,10 +1,15 @@
|
||||
use never::Never;
|
||||
use orchid_base::interner::Interner;
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::future::join_all;
|
||||
use orchid_api_derive::{Coding, Hierarchy};
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::reqnot::Receipt;
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::{AtomDynfo, AtomicFeatures};
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::entrypoint::ExtReq;
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::lexer::LexerObj;
|
||||
use orchid_extension::parser::ParserObj;
|
||||
use orchid_extension::system::{System, SystemCard};
|
||||
@@ -15,9 +20,25 @@ use super::number::num_lib::gen_num_lib;
|
||||
use super::string::str_atom::{IntStrAtom, StrAtom};
|
||||
use super::string::str_lib::gen_str_lib;
|
||||
use crate::std::number::num_lexer::NumLexer;
|
||||
use crate::std::option::{OptAtom, gen_option_lib};
|
||||
use crate::std::protocol::proto_parser::{AsProtoParser, ProtoParser};
|
||||
use crate::std::protocol::type_parser::{AsTypeParser, TypeParser};
|
||||
use crate::std::protocol::types::{Tag, Tagged, gen_protocol_lib};
|
||||
use crate::std::record::record_atom::Record;
|
||||
use crate::std::record::record_lib::gen_record_lib;
|
||||
use crate::std::reflection::sym_atom::{CreateSymAtom, SymAtom, gen_sym_lib};
|
||||
use crate::std::string::str_lexer::StringLexer;
|
||||
use crate::std::string::to_string::AsStrTag;
|
||||
use crate::std::tuple::{CreateTuple, Tuple, TupleBuilder, gen_tuple_lib};
|
||||
use crate::{Float, Int};
|
||||
|
||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||
#[extendable]
|
||||
pub enum StdReq {
|
||||
CreateTuple(CreateTuple),
|
||||
CreateSymAtom(CreateSymAtom),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct StdSystem;
|
||||
impl SystemCtor for StdSystem {
|
||||
@@ -29,15 +50,51 @@ impl SystemCtor for StdSystem {
|
||||
}
|
||||
impl SystemCard for StdSystem {
|
||||
type Ctor = Self;
|
||||
type Req = Never;
|
||||
type Req = StdReq;
|
||||
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn AtomDynfo>>> {
|
||||
[Some(Int::dynfo()), Some(Float::dynfo()), Some(StrAtom::dynfo()), Some(IntStrAtom::dynfo())]
|
||||
[
|
||||
Some(Int::dynfo()),
|
||||
Some(Float::dynfo()),
|
||||
Some(StrAtom::dynfo()),
|
||||
Some(IntStrAtom::dynfo()),
|
||||
Some(OptAtom::dynfo()),
|
||||
Some(Record::dynfo()),
|
||||
Some(Tuple::dynfo()),
|
||||
Some(TupleBuilder::dynfo()),
|
||||
Some(Tag::dynfo()),
|
||||
Some(Tagged::dynfo()),
|
||||
Some(AsStrTag::dynfo()),
|
||||
]
|
||||
}
|
||||
}
|
||||
impl System for StdSystem {
|
||||
async fn request(_: ExtReq<'_>, req: Self::Req) -> Receipt<'_> { match req {} }
|
||||
async fn request(xreq: ExtReq<'_>, req: Self::Req) -> Receipt<'_> {
|
||||
match req {
|
||||
StdReq::CreateTuple(ref req @ CreateTuple(ref items)) => {
|
||||
let tpl = Tuple(Rc::new(join_all(items.iter().copied().map(Expr::deserialize)).await));
|
||||
let tk = tpl.to_expr().await.serialize().await;
|
||||
xreq.handle(req, &tk).await
|
||||
},
|
||||
StdReq::CreateSymAtom(ref req @ CreateSymAtom(sym_tok)) => {
|
||||
let sym_atom = SymAtom(Sym::from_api(sym_tok, &i()).await);
|
||||
xreq.handle(req, &sym_atom.to_expr().await.serialize().await).await
|
||||
},
|
||||
}
|
||||
}
|
||||
fn lexers() -> Vec<LexerObj> { vec![&StringLexer, &NumLexer] }
|
||||
fn parsers() -> Vec<ParserObj> { vec![] }
|
||||
fn env() -> Vec<GenMember> { merge_trivial([gen_num_lib(), gen_str_lib()]) }
|
||||
async fn prelude(i: &Interner) -> Vec<Sym> { vec![sym!(std; i).await] }
|
||||
fn parsers() -> Vec<ParserObj> { vec![&AsTypeParser, &TypeParser, &AsProtoParser, &ProtoParser] }
|
||||
async fn env() -> Vec<GenMember> {
|
||||
merge_trivial([
|
||||
gen_num_lib(),
|
||||
gen_str_lib(),
|
||||
gen_option_lib(),
|
||||
gen_record_lib(),
|
||||
gen_tuple_lib(),
|
||||
gen_protocol_lib(),
|
||||
gen_sym_lib().await,
|
||||
])
|
||||
}
|
||||
async fn prelude() -> Vec<Sym> {
|
||||
vec![sym!(std; i()), sym!(std::tuple; i()), sym!(std::option; i())]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod str_atom;
|
||||
pub mod str_lexer;
|
||||
pub mod str_lib;
|
||||
pub mod to_string;
|
||||
|
||||
@@ -11,9 +11,11 @@ use orchid_base::format::{FmtCtx, FmtUnit};
|
||||
use orchid_base::interner::Tok;
|
||||
use orchid_extension::atom::{AtomMethod, Atomic, MethodSetBuilder, Supports, TAtom};
|
||||
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::conv::TryFromExpr;
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::system::SysCtx;
|
||||
|
||||
use crate::std::string::to_string::ToStringMethod;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Coding)]
|
||||
pub struct StringGetVal;
|
||||
@@ -24,8 +26,11 @@ impl AtomMethod for StringGetVal {
|
||||
const NAME: &str = "std::string_get_val";
|
||||
}
|
||||
impl Supports<StringGetVal> for StrAtom {
|
||||
async fn handle(&self, _: SysCtx, _: StringGetVal) -> <StringGetVal as Request>::Response {
|
||||
self.0.clone()
|
||||
async fn handle(&self, _: StringGetVal) -> <StringGetVal as Request>::Response { self.0.clone() }
|
||||
}
|
||||
impl Supports<ToStringMethod> for StrAtom {
|
||||
async fn handle(&self, _: ToStringMethod) -> <ToStringMethod as Request>::Response {
|
||||
self.0.as_str().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +51,7 @@ impl Deref for StrAtom {
|
||||
impl OwnedAtom for StrAtom {
|
||||
type Refs = ();
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||
async fn serialize(&self, _: SysCtx, sink: Pin<&mut (impl AsyncWrite + ?Sized)>) -> Self::Refs {
|
||||
async fn serialize(&self, sink: Pin<&mut (impl AsyncWrite + ?Sized)>) -> Self::Refs {
|
||||
self.deref().encode(sink).await
|
||||
}
|
||||
async fn print_atom<'a>(&'a self, _: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||
@@ -58,7 +63,7 @@ impl OwnedAtom for StrAtom {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IntStrAtom(Tok<String>);
|
||||
pub struct IntStrAtom(pub(crate) Tok<String>);
|
||||
impl Atomic for IntStrAtom {
|
||||
type Variant = OwnedVariant;
|
||||
type Data = orchid_api::TStr;
|
||||
@@ -72,19 +77,28 @@ impl OwnedAtom for IntStrAtom {
|
||||
async fn print_atom<'a>(&'a self, _: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||
format!("{:?}i", *self.0).into()
|
||||
}
|
||||
async fn serialize(&self, _: SysCtx, write: Pin<&mut (impl AsyncWrite + ?Sized)>) {
|
||||
async fn serialize(&self, write: Pin<&mut (impl AsyncWrite + ?Sized)>) {
|
||||
self.0.encode(write).await
|
||||
}
|
||||
async fn deserialize(mut ctx: impl DeserializeCtx, _: ()) -> Self {
|
||||
let s = ctx.decode::<String>().await;
|
||||
Self(ctx.sys().i().i(&s).await)
|
||||
async fn deserialize(mut dctx: impl DeserializeCtx, _: ()) -> Self {
|
||||
let s = dctx.decode::<String>().await;
|
||||
Self(i().i(&s).await)
|
||||
}
|
||||
}
|
||||
impl TryFromExpr for IntStrAtom {
|
||||
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
|
||||
Ok(IntStrAtom(i().ex(TAtom::<IntStrAtom>::try_from_expr(expr).await?.value).await))
|
||||
}
|
||||
}
|
||||
impl Supports<ToStringMethod> for IntStrAtom {
|
||||
async fn handle(&self, _: ToStringMethod) -> <ToStringMethod as Request>::Response {
|
||||
self.0.as_str().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OrcString {
|
||||
kind: OrcStringKind,
|
||||
ctx: SysCtx,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -95,7 +109,7 @@ pub enum OrcStringKind {
|
||||
impl OrcString {
|
||||
pub async fn get_string(&self) -> Rc<String> {
|
||||
match &self.kind {
|
||||
OrcStringKind::Int(tok) => self.ctx.i().ex(**tok).await.rc(),
|
||||
OrcStringKind::Int(tok) => i().ex(**tok).await.rc(),
|
||||
OrcStringKind::Val(atom) => atom.request(StringGetVal).await,
|
||||
}
|
||||
}
|
||||
@@ -104,12 +118,11 @@ impl OrcString {
|
||||
impl TryFromExpr for OrcString {
|
||||
async fn try_from_expr(expr: Expr) -> OrcRes<OrcString> {
|
||||
if let Ok(v) = TAtom::<StrAtom>::try_from_expr(expr.clone()).await {
|
||||
return Ok(OrcString { ctx: expr.ctx(), kind: OrcStringKind::Val(v) });
|
||||
return Ok(OrcString { kind: OrcStringKind::Val(v) });
|
||||
}
|
||||
let ctx = expr.ctx();
|
||||
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())),
|
||||
Ok(t) => Ok(OrcString { kind: OrcStringKind::Int(t) }),
|
||||
Err(e) => Err(mk_errv(i().i("A string was expected").await, "", e.pos_iter())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,12 @@ use orchid_base::location::SrcRange;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::parse::ParseCtx;
|
||||
use orchid_base::sym;
|
||||
use orchid_base::tree::wrap_tokv;
|
||||
use orchid_base::tree::{Paren, wrap_tokv};
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::gen_expr::sym_ref;
|
||||
use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable};
|
||||
use orchid_extension::parser::p_tree2gen;
|
||||
use orchid_extension::tree::{GenTokTree, ref_tok, x_tok};
|
||||
use orchid_extension::tree::{GenTok, GenTokTree, ref_tok, x_tok};
|
||||
|
||||
use super::str_atom::IntStrAtom;
|
||||
|
||||
@@ -97,9 +99,9 @@ fn parse_string(str: &str) -> Result<String, StringError> {
|
||||
pub struct StringLexer;
|
||||
impl Lexer for StringLexer {
|
||||
const CHAR_FILTER: &'static [std::ops::RangeInclusive<char>] = &['"'..='"', '`'..='`'];
|
||||
async fn lex<'a>(all: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree)> {
|
||||
async fn lex<'a>(all: &'a str, lctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree)> {
|
||||
let Some(mut tail) = all.strip_prefix('"') else {
|
||||
return Err(err_not_applicable(ctx.ctx.i()).await);
|
||||
return Err(err_not_applicable().await);
|
||||
};
|
||||
let mut ret = None;
|
||||
let mut cur = String::new();
|
||||
@@ -121,19 +123,27 @@ impl Lexer for StringLexer {
|
||||
}
|
||||
let add_frag = |prev: Option<GenTokTree>, new: GenTokTree| async {
|
||||
let Some(prev) = prev else { return new };
|
||||
let concat_fn = ref_tok(sym!(std::string::concat; ctx.i()).await)
|
||||
let concat_fn = ref_tok(sym!(std::string::concat; lctx.i()))
|
||||
.await
|
||||
.at(SrcRange::zw(prev.sr.path(), prev.sr.start()));
|
||||
wrap_tokv([concat_fn, prev, new])
|
||||
};
|
||||
loop {
|
||||
if let Some(rest) = tail.strip_prefix('"') {
|
||||
return Ok((rest, add_frag(ret, str_to_gen(&mut cur, tail, &mut errors, ctx).await).await));
|
||||
return Ok((
|
||||
rest,
|
||||
add_frag(ret, str_to_gen(&mut cur, tail, &mut errors, lctx).await).await,
|
||||
));
|
||||
} else if let Some(rest) = tail.strip_prefix('$') {
|
||||
ret = Some(add_frag(ret, str_to_gen(&mut cur, tail, &mut errors, ctx).await).await);
|
||||
let (new_tail, tree) = ctx.recurse(rest).await?;
|
||||
ret = Some(add_frag(ret, str_to_gen(&mut cur, tail, &mut errors, lctx).await).await);
|
||||
let (new_tail, tree) = lctx.recurse(rest).await?;
|
||||
tail = new_tail;
|
||||
ret = Some(add_frag(ret, p_tree2gen(tree)).await);
|
||||
// wrap the received token in a call to to_str
|
||||
let to_str = sym_ref(sym!(std::string::to_str; i()));
|
||||
let sr = tree.sr();
|
||||
let inj_to_str_tok = GenTok::NewExpr(to_str).at(sr.map_range(|_| sr.start()..sr.start()));
|
||||
let to_str_call = GenTok::S(Paren::Round, vec![inj_to_str_tok, p_tree2gen(tree)]).at(sr);
|
||||
ret = Some(add_frag(ret, to_str_call).await);
|
||||
} else if tail.starts_with('\\') {
|
||||
// parse_string will deal with it, we just have to skip the next char
|
||||
tail = &tail[2..];
|
||||
@@ -143,11 +153,11 @@ impl Lexer for StringLexer {
|
||||
cur.push(c);
|
||||
tail = ch.as_str();
|
||||
} else {
|
||||
let range = ctx.pos(all)..ctx.pos("");
|
||||
let range = lctx.pos(all)..lctx.pos("");
|
||||
return Err(mk_errv(
|
||||
ctx.i().i("No string end").await,
|
||||
lctx.i().i("No string end").await,
|
||||
"String never terminated with \"",
|
||||
[SrcRange::new(range.clone(), ctx.src())],
|
||||
[SrcRange::new(range.clone(), lctx.src())],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,60 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use orchid_extension::tree::{GenMember, comments, fun, prefix};
|
||||
use orchid_base::format::fmt;
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::ForeignAtom;
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::coroutine_exec::exec;
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::gen_expr::{call, sym_ref};
|
||||
use orchid_extension::tree::{GenMember, cnst, comments, fun, prefix};
|
||||
|
||||
use super::str_atom::StrAtom;
|
||||
use crate::OrcString;
|
||||
use crate::std::protocol::types::get_impl;
|
||||
use crate::std::string::to_string::{AsStrTag, ToStringMethod};
|
||||
|
||||
pub fn gen_str_lib() -> Vec<GenMember> {
|
||||
prefix("std::string", [comments(
|
||||
["Concatenate two strings"],
|
||||
fun(true, "concat", |left: OrcString, right: OrcString| async move {
|
||||
StrAtom::new(Rc::new(left.get_string().await.to_string() + &right.get_string().await))
|
||||
}),
|
||||
)])
|
||||
prefix("std::string", [
|
||||
comments(
|
||||
["Concatenate two strings"],
|
||||
fun(true, "concat", async |left: OrcString, right: OrcString| {
|
||||
StrAtom::new(Rc::new(left.get_string().await.to_string() + &right.get_string().await))
|
||||
}),
|
||||
),
|
||||
comments(
|
||||
["Converts a value to string. This function is used in interpolation. \
|
||||
It supports the std::string::to_string protocol in Orchid, \
|
||||
the std::string::to_string request in Rust, \
|
||||
and expression debug printing as a fallback (print_atom for Atomic implementors in Rust).\n\n\
|
||||
This function is infallible."],
|
||||
fun(true, "to_str", async |input: Expr| {
|
||||
exec(async move |mut h| {
|
||||
if let Ok(atom) = h.exec::<ForeignAtom>(input.clone()).await {
|
||||
if let Some(str) = atom.request(ToStringMethod).await {
|
||||
return StrAtom::new(Rc::new(str)).to_gen().await;
|
||||
}
|
||||
let proto_ref = sym_ref(sym!(std::string::to_string::__protocol_tag__; i()));
|
||||
let proto = h.exec(proto_ref).await.expect("This protocol is defined in this system");
|
||||
if let Ok(cb) = get_impl(atom.clone(), proto).await {
|
||||
return call(cb.to_gen().await, [atom.to_gen().await]).to_gen().await;
|
||||
}
|
||||
}
|
||||
return StrAtom::new(Rc::new(fmt(&input, &i()).await)).to_gen().await;
|
||||
})
|
||||
.await
|
||||
}),
|
||||
),
|
||||
prefix("to_string", [
|
||||
cnst(true, "__type_tag__", AsStrTag),
|
||||
fun(true, "resolve", async |atom: ForeignAtom| {
|
||||
exec(async |mut h| {
|
||||
let proto = h.exec(sym_ref(sym!(std::string::to_string; i()))).await?;
|
||||
Ok(call(get_impl(atom.clone(), proto).await?.to_gen().await, [atom.to_gen().await]))
|
||||
})
|
||||
.await
|
||||
}),
|
||||
]),
|
||||
])
|
||||
}
|
||||
|
||||
36
orchid-std/src/std/string/to_string.rs
Normal file
36
orchid-std/src/std/string/to_string.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use orchid_api_derive::Coding;
|
||||
use orchid_api_traits::Request;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_extension::atom::{AtomMethod, Atomic, MethodSetBuilder, Supports};
|
||||
use orchid_extension::atom_thin::{ThinAtom, ThinVariant};
|
||||
use orchid_extension::context::i;
|
||||
|
||||
use crate::std::protocol::types::{GetImplMethod, GetTagIdMethod};
|
||||
|
||||
#[derive(Coding, Clone, Debug)]
|
||||
pub struct AsStrTag;
|
||||
impl Atomic for AsStrTag {
|
||||
type Data = AsStrTag;
|
||||
type Variant = ThinVariant;
|
||||
fn reg_reqs() -> MethodSetBuilder<Self> {
|
||||
MethodSetBuilder::new().handle::<GetTagIdMethod>().handle::<GetImplMethod>()
|
||||
}
|
||||
}
|
||||
impl ThinAtom for AsStrTag {}
|
||||
impl Supports<GetTagIdMethod> for AsStrTag {
|
||||
async fn handle(&self, _: GetTagIdMethod) -> <GetTagIdMethod as Request>::Response {
|
||||
Sym::parse("std::string::to_string", &i()).await.unwrap().to_api()
|
||||
}
|
||||
}
|
||||
impl Supports<GetImplMethod> for AsStrTag {
|
||||
async fn handle(&self, _: GetImplMethod) -> <GetImplMethod as Request>::Response { None }
|
||||
}
|
||||
|
||||
#[derive(Coding, Clone, Debug)]
|
||||
pub struct ToStringMethod;
|
||||
impl Request for ToStringMethod {
|
||||
type Response = String;
|
||||
}
|
||||
impl AtomMethod for ToStringMethod {
|
||||
const NAME: &str = "std::string::to_string";
|
||||
}
|
||||
211
orchid-std/src/std/tuple.rs
Normal file
211
orchid-std/src/std/tuple.rs
Normal file
@@ -0,0 +1,211 @@
|
||||
use std::borrow::Cow;
|
||||
use std::num::NonZero;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::AsyncWrite;
|
||||
use futures::future::join_all;
|
||||
use never::Never;
|
||||
use orchid_api::ExprTicket;
|
||||
use orchid_api_derive::{Coding, Hierarchy};
|
||||
use orchid_api_traits::Request;
|
||||
use orchid_base::error::{OrcRes, mk_errv};
|
||||
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
|
||||
use orchid_extension::atom::{Atomic, TAtom};
|
||||
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant, own};
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::conv::{ToExpr, TryFromExpr};
|
||||
use orchid_extension::expr::{Expr, ExprHandle};
|
||||
use orchid_extension::gen_expr::GExpr;
|
||||
use orchid_extension::system::dep_req;
|
||||
use orchid_extension::tree::{GenMember, cnst, fun, prefix};
|
||||
|
||||
use crate::std::std_system::StdReq;
|
||||
use crate::{Int, StdSystem, api};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Tuple(pub(super) Rc<Vec<Expr>>);
|
||||
|
||||
impl Atomic for Tuple {
|
||||
type Data = Vec<ExprTicket>;
|
||||
type Variant = OwnedVariant;
|
||||
}
|
||||
|
||||
impl OwnedAtom for Tuple {
|
||||
type Refs = Vec<Expr>;
|
||||
async fn val(&self) -> Cow<'_, Self::Data> {
|
||||
Cow::Owned(self.0.iter().map(|x| x.handle().ticket()).collect())
|
||||
}
|
||||
async fn serialize(&self, _: Pin<&mut (impl AsyncWrite + ?Sized)>) -> Self::Refs {
|
||||
self.0.as_ref().clone()
|
||||
}
|
||||
async fn deserialize(_: impl DeserializeCtx, refs: Self::Refs) -> Self { Self(Rc::new(refs)) }
|
||||
async fn print_atom<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||
Variants::default()
|
||||
.sequence(self.0.len(), "t[", ", ", "]", Some(true))
|
||||
.sequence(self.0.len(), "t[\n", ",\n", "\n]", Some(true))
|
||||
.units_own(join_all(self.0.iter().map(|x| x.print(c))).await)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||
#[extends(StdReq)]
|
||||
pub struct CreateTuple(pub Vec<api::ExprTicket>);
|
||||
impl Request for CreateTuple {
|
||||
type Response = api::ExprTicket;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TupleBuilder {
|
||||
arity: NonZero<u32>,
|
||||
items: Vec<Expr>,
|
||||
}
|
||||
impl Atomic for TupleBuilder {
|
||||
type Data = ();
|
||||
type Variant = OwnedVariant;
|
||||
}
|
||||
impl OwnedAtom for TupleBuilder {
|
||||
type Refs = Never;
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||
async fn call(mut self, arg: Expr) -> GExpr {
|
||||
self.items.push(arg);
|
||||
if self.arity.get() == self.items.len().try_into().expect("counting up from 0") {
|
||||
Tuple(Rc::new(self.items)).to_gen().await
|
||||
} else {
|
||||
self.to_gen().await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_tuple_lib() -> Vec<GenMember> {
|
||||
prefix("std::tuple", [
|
||||
cnst(true, "empty", Tuple(Rc::new(Vec::new()))),
|
||||
fun(true, "one", async |item: Expr| Tuple(Rc::new(vec![item]))),
|
||||
fun(true, "new", async |arity: TAtom<Int>| {
|
||||
if let Ok(arity) = u32::try_from(arity.value.0).and_then(|v| v.try_into()) {
|
||||
TupleBuilder { arity, items: Vec::new() }.to_gen().await
|
||||
} else {
|
||||
Tuple(Rc::new(Vec::new())).to_gen().await
|
||||
}
|
||||
}),
|
||||
fun(true, "get", async |tup: TAtom<Tuple>, idx: TAtom<Int>| {
|
||||
if let Ok(idx) = usize::try_from(idx.0)
|
||||
&& let Some(val) = own(&tup).await.0.get(idx)
|
||||
{
|
||||
return Ok(val.clone());
|
||||
}
|
||||
return Err(mk_errv(
|
||||
i().i("Tuple index out of bounds").await,
|
||||
format!("{} is out of bounds for Tuple{}", idx.0, tup.len()),
|
||||
[idx.pos()],
|
||||
));
|
||||
}),
|
||||
fun(true, "set", async |tup: TAtom<Tuple>, idx: TAtom<Int>, val: Expr| {
|
||||
if let Ok(idx) = usize::try_from(idx.0) {
|
||||
let mut new_vec = own(&tup).await.0.to_vec();
|
||||
if let Some(slot) = new_vec.get_mut(idx) {
|
||||
*slot = val;
|
||||
return Ok(Tuple(Rc::new(new_vec)));
|
||||
}
|
||||
}
|
||||
return Err(mk_errv(
|
||||
i().i("Tuple index out of bounds").await,
|
||||
format!("{} is out of bounds for Tuple{}", idx.0, tup.len()),
|
||||
[idx.pos()],
|
||||
));
|
||||
}),
|
||||
fun(true, "len", async |tup: TAtom<Tuple>| {
|
||||
Int(tup.len().try_into().expect("Tuple was created with an Int length"))
|
||||
}),
|
||||
fun(true, "cat", async |left: TAtom<Tuple>, right: TAtom<Tuple>| {
|
||||
Tuple(Rc::new(own(&left).await.0.iter().chain(own(&right).await.0.iter()).cloned().collect()))
|
||||
}),
|
||||
])
|
||||
}
|
||||
|
||||
pub struct UntypedTuple(pub Vec<Expr>);
|
||||
impl TryFromExpr for UntypedTuple {
|
||||
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
|
||||
let tpl = TAtom::<Tuple>::try_from_expr(expr.clone()).await?;
|
||||
let exprs =
|
||||
join_all(tpl.iter().map(async |t| Expr::from_handle(ExprHandle::from_ticket(*t).await)))
|
||||
.await;
|
||||
Ok(UntypedTuple(exprs))
|
||||
}
|
||||
}
|
||||
impl ToExpr for UntypedTuple {
|
||||
async fn to_gen(self) -> GExpr {
|
||||
let exprs = join_all(self.0.into_iter().map(async |expr| expr.serialize().await)).await;
|
||||
Expr::deserialize(dep_req::<StdSystem, _>(CreateTuple(exprs)).await).await.to_gen().await
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Tpl<T>(pub T);
|
||||
|
||||
mod tpl_impls {
|
||||
use itertools::Itertools;
|
||||
use orchid_base::error::{OrcRes, mk_errv};
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::conv::{ToExpr, TryFromExpr};
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::gen_expr::GExpr;
|
||||
|
||||
use super::{Tpl, UntypedTuple};
|
||||
|
||||
macro_rules! tpl_derives {
|
||||
($len:literal $($t:ident)*) => {
|
||||
pastey::paste! {
|
||||
impl<$( $t: TryFromExpr, )*> TryFromExpr for Tpl<($( $t, )*)> {
|
||||
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
|
||||
let tpl = UntypedTuple::try_from_expr(expr.clone()).await?;
|
||||
let Some([$( [< $t:lower >], )*]) = tpl.0.iter().cloned().collect_array() else {
|
||||
return Err(mk_errv(
|
||||
i().i("Tuple arity mismatch").await,
|
||||
format!("Expected a {}-ary tuple, found {}-ary", $len, tpl.0.len()),
|
||||
[expr.data().await.pos.clone()]
|
||||
));
|
||||
};
|
||||
Ok(Tpl(( $( $t::try_from_expr([< $t:lower >]).await?, )* )))
|
||||
}
|
||||
}
|
||||
impl<$( $t: ToExpr, )*> ToExpr for Tpl<($( $t, )*)> {
|
||||
async fn to_gen(self) -> GExpr {
|
||||
let Self(($( [< $t:lower >], )*)) = self;
|
||||
UntypedTuple(vec![
|
||||
$( [< $t:lower >].to_expr().await, )*
|
||||
]).to_gen().await
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
tpl_derives!(0);
|
||||
tpl_derives!(1 A);
|
||||
tpl_derives!(2 A B);
|
||||
tpl_derives!(3 A B C);
|
||||
tpl_derives!(4 A B C D);
|
||||
tpl_derives!(5 A B C D E);
|
||||
tpl_derives!(6 A B C D E F);
|
||||
tpl_derives!(7 A B C D E F G);
|
||||
tpl_derives!(8 A B C D E F G H);
|
||||
tpl_derives!(9 A B C D E F G H I);
|
||||
tpl_derives!(10 A B C D E F G H I J);
|
||||
}
|
||||
|
||||
pub struct HomoTpl<T>(pub Vec<T>);
|
||||
|
||||
impl<T: TryFromExpr> TryFromExpr for HomoTpl<T> {
|
||||
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
|
||||
let tpl = TAtom::<Tuple>::try_from_expr(expr.clone()).await?;
|
||||
let mut res = Vec::new();
|
||||
for item in tpl.iter() {
|
||||
res.push(T::try_from_expr(Expr::from_handle(ExprHandle::from_ticket(*item).await)).await?);
|
||||
}
|
||||
Ok(HomoTpl(res))
|
||||
}
|
||||
}
|
||||
impl<T: ToExpr> ToExpr for HomoTpl<T> {
|
||||
async fn to_gen(self) -> GExpr {
|
||||
UntypedTuple(join_all(self.0.into_iter().map(async |t| t.to_expr().await)).await).to_gen().await
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user