New macro system and stdlib additions
This commit is contained in:
@@ -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