New macro system and stdlib additions

This commit is contained in:
2025-11-21 14:25:03 +01:00
parent b77653f841
commit 603efef28e
230 changed files with 3033 additions and 16640 deletions

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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))))
}
}

View File

@@ -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)
}),
])

View 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()],
)),
}
}),
])
}

View File

@@ -0,0 +1,4 @@
pub mod parse_impls;
pub mod proto_parser;
pub mod type_parser;
pub mod types;

View 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(())
}

View 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)])
}
}

View 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)])
}
}

View 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()],
))
}
}),
])
}

View File

@@ -0,0 +1,2 @@
pub mod record_atom;
pub mod record_lib;

View 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(()) }
}

View 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))
}),
])
}

View File

@@ -0,0 +1 @@
pub mod sym_atom;

View 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())
}),
])
}

View File

@@ -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())]
}
}

View File

@@ -1,3 +1,4 @@
pub mod str_atom;
pub mod str_lexer;
pub mod str_lib;
pub mod to_string;

View File

@@ -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())),
}
}
}

View File

@@ -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())],
));
}
}

View File

@@ -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
}),
]),
])
}

View 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
View 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
}
}