forked from Orchid/orchid
Protocols and operators mostly
This commit is contained in:
@@ -22,7 +22,7 @@ Namespaces are inspired by Rust modules and ES6. Every file and directory is imp
|
||||
The project uses both the stable and nightly rust toolchain. Run the examples with
|
||||
|
||||
```sh
|
||||
cargo orcx -- exec --proj ./examples/hello-world "src::main::main"
|
||||
cargo orcx --release exec --proj ./examples/hello-world "src::main::main"
|
||||
```
|
||||
|
||||
you can try modifying the examples, but error reporting for the time being is pretty terrible.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
let my_tuple = option::some t[1, 2]
|
||||
let user = "lorinc" + " " + "bethlenfalvy"
|
||||
let number = 1 + 2
|
||||
let interpolated = "Hello $user $number"
|
||||
|
||||
let main = match my_tuple {
|
||||
option::some t[ref head, ..] => head;
|
||||
option::none => "foo";
|
||||
}
|
||||
let user =
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::task::{Context, Poll, Wake, Waker};
|
||||
use std::thread::panicking;
|
||||
|
||||
use futures::Stream;
|
||||
use itertools::Itertools;
|
||||
@@ -155,3 +156,15 @@ pub fn spin_on<Fut: Future>(f: Fut) -> Fut::Output {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an object that will panic if dropped. [PanicOnDrop::defuse] must be
|
||||
/// called once the particular constraint preventing a drop has passed
|
||||
pub fn assert_no_drop(msg: &'static str) -> PanicOnDrop { PanicOnDrop(true, msg) }
|
||||
|
||||
pub struct PanicOnDrop(bool, &'static str);
|
||||
impl PanicOnDrop {
|
||||
pub fn defuse(mut self) { self.0 = false; }
|
||||
}
|
||||
impl Drop for PanicOnDrop {
|
||||
fn drop(&mut self) { assert!(panicking() || !self.0, "{}", self.1) }
|
||||
}
|
||||
|
||||
@@ -202,6 +202,11 @@ impl Sym {
|
||||
pub async fn parse(s: &str) -> Result<Self, EmptyNameError> {
|
||||
Ok(Sym(iv(&VName::parse(s).await?.into_vec()).await))
|
||||
}
|
||||
/// Read a `::` separated namespaced name from a static string where.
|
||||
pub async fn literal(s: &'static str) -> Self {
|
||||
assert!(!s.is_empty(), "Literal cannot be empty");
|
||||
Self::parse(s).await.unwrap()
|
||||
}
|
||||
/// Assert that a token isn't empty, and wrap it in a [Sym]
|
||||
pub fn from_tok(t: IStrv) -> Result<Self, EmptyNameError> {
|
||||
if t.is_empty() { Err(EmptyNameError) } else { Ok(Self(t)) }
|
||||
|
||||
@@ -17,6 +17,7 @@ use futures::{
|
||||
use hashbrown::HashMap;
|
||||
use orchid_api_traits::{Decode, Encode, Request, UnderRoot};
|
||||
|
||||
use crate::future_debug::{PanicOnDrop, assert_no_drop};
|
||||
use crate::localset::LocalSet;
|
||||
|
||||
#[must_use = "Receipts indicate that a required action has been performed within a function. \
|
||||
@@ -238,9 +239,12 @@ impl IoClient {
|
||||
impl Client for IoClient {
|
||||
fn start_notif(&self) -> LocalBoxFuture<'_, io::Result<Box<dyn MsgWriter<'_> + '_>>> {
|
||||
Box::pin(async {
|
||||
let drop_g = assert_no_drop("Notif future dropped");
|
||||
let mut o = self.lock_out().await;
|
||||
0u64.encode(o.as_mut()).await?;
|
||||
Ok(Box::new(IoNotifWriter { o }) as Box<dyn MsgWriter>)
|
||||
drop_g.defuse();
|
||||
Ok(Box::new(IoNotifWriter { o, drop_g: assert_no_drop("Notif writer dropped") })
|
||||
as Box<dyn MsgWriter>)
|
||||
})
|
||||
}
|
||||
fn start_request(&self) -> LocalBoxFuture<'_, io::Result<Box<dyn ReqWriter<'_> + '_>>> {
|
||||
@@ -252,11 +256,17 @@ impl Client for IoClient {
|
||||
};
|
||||
let (cb, reply) = oneshot::channel();
|
||||
let (ack, got_ack) = oneshot::channel();
|
||||
let drop_g = assert_no_drop("Request future dropped");
|
||||
self.subscribe.as_ref().clone().send(ReplySub { id, ack, cb }).await.unwrap();
|
||||
got_ack.await.unwrap();
|
||||
let mut w = self.lock_out().await;
|
||||
id.encode(w.as_mut()).await?;
|
||||
Ok(Box::new(IoReqWriter { reply, w }) as Box<dyn ReqWriter>)
|
||||
drop_g.defuse();
|
||||
Ok(Box::new(IoReqWriter {
|
||||
reply,
|
||||
w,
|
||||
drop_g: assert_no_drop("Request reader dropped without reply"),
|
||||
}) as Box<dyn ReqWriter>)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -264,36 +274,49 @@ impl Client for IoClient {
|
||||
struct IoReqWriter {
|
||||
reply: oneshot::Receiver<IoGuard<dyn AsyncRead>>,
|
||||
w: IoGuard<dyn AsyncWrite>,
|
||||
drop_g: PanicOnDrop,
|
||||
}
|
||||
impl<'a> ReqWriter<'a> for IoReqWriter {
|
||||
fn writer(&mut self) -> Pin<&mut dyn AsyncWrite> { self.w.as_mut() }
|
||||
fn send(self: Box<Self>) -> LocalBoxFuture<'a, io::Result<Box<dyn RepReader<'a> + 'a>>> {
|
||||
Box::pin(async {
|
||||
let Self { reply, mut w } = *self;
|
||||
let Self { reply, mut w, drop_g } = *self;
|
||||
w.flush().await?;
|
||||
mem::drop(w);
|
||||
let i = reply.await.expect("Client dropped before reply received");
|
||||
Ok(Box::new(IoRepReader { i }) as Box<dyn RepReader>)
|
||||
drop_g.defuse();
|
||||
Ok(Box::new(IoRepReader {
|
||||
i,
|
||||
drop_g: assert_no_drop("Reply reader dropped without finishing"),
|
||||
}) as Box<dyn RepReader>)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct IoRepReader {
|
||||
i: IoGuard<dyn AsyncRead>,
|
||||
drop_g: PanicOnDrop,
|
||||
}
|
||||
impl<'a> RepReader<'a> for IoRepReader {
|
||||
fn reader(&mut self) -> Pin<&mut dyn AsyncRead> { self.i.as_mut() }
|
||||
fn finish(self: Box<Self>) -> LocalBoxFuture<'static, ()> { Box::pin(async {}) }
|
||||
fn finish(self: Box<Self>) -> LocalBoxFuture<'static, ()> {
|
||||
Box::pin(async { self.drop_g.defuse() })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(destructure)]
|
||||
struct IoNotifWriter {
|
||||
o: IoGuard<dyn AsyncWrite>,
|
||||
drop_g: PanicOnDrop,
|
||||
}
|
||||
impl<'a> MsgWriter<'a> for IoNotifWriter {
|
||||
fn writer(&mut self) -> Pin<&mut dyn AsyncWrite> { self.o.as_mut() }
|
||||
fn finish(mut self: Box<Self>) -> LocalBoxFuture<'static, io::Result<()>> {
|
||||
Box::pin(async move { self.o.flush().await })
|
||||
Box::pin(async move {
|
||||
self.o.flush().await?;
|
||||
self.drop_g.defuse();
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,7 +398,8 @@ impl IoCommServer {
|
||||
Ok(Event::Exit) => break,
|
||||
Ok(Event::Sub(ReplySub { id, ack, cb })) => {
|
||||
pending_replies.insert(id, cb);
|
||||
ack.send(()).unwrap();
|
||||
// this is detected and logged on client
|
||||
let _ = ack.send(());
|
||||
},
|
||||
Ok(Event::Input(0, read)) => {
|
||||
let notif = ¬if;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::any::{Any, TypeId, type_name};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::fmt::{self, Debug};
|
||||
use std::future::Future;
|
||||
use std::num::NonZeroU32;
|
||||
use std::ops::Deref;
|
||||
@@ -144,6 +144,15 @@ impl NotTypAtom {
|
||||
)
|
||||
}
|
||||
}
|
||||
impl Debug for NotTypAtom {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("NotTypAtom")
|
||||
.field("pos", &self.pos)
|
||||
.field("expr", &self.expr)
|
||||
.field("typ.name", &self.typ.name())
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AtomMethod: Request + Coding {
|
||||
const NAME: &str;
|
||||
|
||||
@@ -136,6 +136,7 @@ impl Expr {
|
||||
_ => Err(self),
|
||||
}
|
||||
}
|
||||
pub async fn pos(&self) -> Pos { self.data().await.pos.clone() }
|
||||
pub fn handle(&self) -> Rc<ExprHandle> { self.handle.clone() }
|
||||
|
||||
pub fn slot(&self) -> GExpr {
|
||||
|
||||
@@ -31,6 +31,19 @@ trait_set! {
|
||||
trait FunCB = Fn(Vec<Expr>) -> LocalBoxFuture<'static, OrcRes<GExpr>> + 'static;
|
||||
}
|
||||
|
||||
task_local! {
|
||||
static ARGV: Vec<Expr>;
|
||||
}
|
||||
|
||||
pub fn get_arg(idx: usize) -> Expr {
|
||||
ARGV
|
||||
.try_with(|argv| {
|
||||
(argv.get(idx).cloned())
|
||||
.unwrap_or_else(|| panic!("Cannot read argument ##{idx}, only have {}", argv.len()))
|
||||
})
|
||||
.expect("get_arg called outside ExprFunc")
|
||||
}
|
||||
|
||||
pub trait ExprFunc<I, O>: Clone + 'static {
|
||||
fn argtyps() -> &'static [TypeId];
|
||||
fn apply<'a>(&self, hand: ExecHandle<'a>, v: Vec<Expr>) -> impl Future<Output = OrcRes<GExpr>>;
|
||||
|
||||
@@ -184,12 +184,12 @@ impl ConstCtx {
|
||||
&'b self,
|
||||
names: impl IntoIterator<Item = &'b Sym> + 'b,
|
||||
) -> impl Stream<Item = OrcRes<Sym>> + 'b {
|
||||
let resolve_names = api::ResolveNames {
|
||||
constid: self.constid,
|
||||
sys: sys_id(),
|
||||
names: names.into_iter().map(|n| n.to_api()).collect_vec(),
|
||||
};
|
||||
let names = names.into_iter().map(|n| n.to_api()).collect_vec();
|
||||
stream(async |mut cx| {
|
||||
if names.is_empty() {
|
||||
return;
|
||||
}
|
||||
let resolve_names = api::ResolveNames { constid: self.constid, sys: sys_id(), names };
|
||||
for name_opt in request(resolve_names).await {
|
||||
cx.emit(match name_opt {
|
||||
Err(e) => Err(OrcErrv::from_api(&e).await),
|
||||
|
||||
@@ -47,8 +47,9 @@ impl ReflMod {
|
||||
async fn try_populate(&self) -> Result<(), api::LsModuleError> {
|
||||
let path_tok = iv(&self.0.path[..]).await;
|
||||
let reply = match request(api::LsModule(sys_id(), path_tok.to_api())).await {
|
||||
Err(api::LsModuleError::TreeUnavailable) =>
|
||||
panic!("Reflected tree accessed outside an interpreter call. This extension is faulty."),
|
||||
Err(api::LsModuleError::TreeUnavailable) => {
|
||||
panic!("Reflected tree accessed outside an interpreter call. This extension is faulty.")
|
||||
},
|
||||
Err(err) => return Err(err),
|
||||
Ok(details) => details,
|
||||
};
|
||||
@@ -79,10 +80,12 @@ impl ReflMod {
|
||||
return None;
|
||||
}
|
||||
match self.try_populate().await {
|
||||
Err(api::LsModuleError::InvalidPath) =>
|
||||
panic!("Path became invalid since module was created"),
|
||||
Err(api::LsModuleError::IsConstant) =>
|
||||
panic!("Path previously contained a module but now contains a constant"),
|
||||
Err(api::LsModuleError::InvalidPath) => {
|
||||
panic!("Path became invalid since module was created")
|
||||
},
|
||||
Err(api::LsModuleError::IsConstant) => {
|
||||
panic!("Path previously contained a module but now contains a constant")
|
||||
},
|
||||
Err(api::LsModuleError::TreeUnavailable) => unreachable!(),
|
||||
Ok(()) => (),
|
||||
}
|
||||
|
||||
@@ -71,8 +71,7 @@ pub fn module(
|
||||
vec![GenMember { name, kind, public, comments: vec![] }]
|
||||
}
|
||||
pub fn root_mod(name: &str, mems: impl IntoIterator<Item = Vec<GenMember>>) -> (String, MemKind) {
|
||||
let kind = MemKind::Mod { members: mems.into_iter().flatten().collect() };
|
||||
(name.to_string(), kind)
|
||||
(name.to_string(), MemKind::module(mems))
|
||||
}
|
||||
pub fn fun<I, O>(public: bool, name: &str, xf: impl ExprFunc<I, O>) -> Vec<GenMember> {
|
||||
let fac =
|
||||
@@ -113,12 +112,12 @@ pub fn merge_trivial(trees: impl IntoIterator<Item = Vec<GenMember>>) -> Vec<Gen
|
||||
let prev = all_members.insert(mem.name.clone(), (unit, mem.comments.into_iter().collect()));
|
||||
assert!(prev.is_none(), "Conflict in trivial tree merge on {}", mem.name);
|
||||
},
|
||||
MemKind::Mod { members } => match all_members.entry(mem.name.clone()) {
|
||||
MemKind::Mod(members) => match all_members.entry(mem.name.clone()) {
|
||||
hashbrown::hash_map::Entry::Vacant(slot) => {
|
||||
slot.insert((MemKind::Mod { members }, mem.comments.into_iter().collect()));
|
||||
slot.insert((MemKind::Mod(members), mem.comments.into_iter().collect()));
|
||||
},
|
||||
hashbrown::hash_map::Entry::Occupied(mut old) => match old.get_mut() {
|
||||
(MemKind::Mod { members: old_items, .. }, old_cmts) => {
|
||||
(MemKind::Mod(old_items), old_cmts) => {
|
||||
let mut swap = vec![];
|
||||
std::mem::swap(&mut swap, old_items);
|
||||
*old_items = merge_trivial([swap, members]);
|
||||
@@ -167,15 +166,19 @@ impl GenMember {
|
||||
|
||||
pub enum MemKind {
|
||||
Const(GExpr),
|
||||
Mod { members: Vec<GenMember> },
|
||||
Mod(Vec<GenMember>),
|
||||
Lazy(LazyMemberFactory),
|
||||
}
|
||||
impl MemKind {
|
||||
pub async fn cnst(val: impl ToExpr) -> Self { Self::Const(val.to_gen().await) }
|
||||
pub fn module(mems: impl IntoIterator<Item = Vec<GenMember>>) -> Self {
|
||||
Self::Mod(mems.into_iter().flatten().collect())
|
||||
}
|
||||
pub(crate) async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MemberKind {
|
||||
match self {
|
||||
Self::Lazy(lazy) => api::MemberKind::Lazy(add_lazy(ctx, lazy)),
|
||||
Self::Const(c) => api::MemberKind::Const(c.serialize().await),
|
||||
Self::Mod { members } => api::MemberKind::Module(api::Module {
|
||||
Self::Mod(members) => api::MemberKind::Module(api::Module {
|
||||
members: stream(async |mut cx| {
|
||||
for m in members {
|
||||
cx.emit(m.into_api(ctx).await).await
|
||||
|
||||
@@ -110,8 +110,9 @@ impl ExecCtx {
|
||||
(ExprKind::Identity(val.clone()), StackOp::Swap(val))
|
||||
},
|
||||
Err(f) => match &*f.kind().read().await {
|
||||
ExprKind::Arg | ExprKind::Call(..) | ExprKind::Seq(..) | ExprKind::Const(_) =>
|
||||
panic!("This should not appear outside function bodies"),
|
||||
ExprKind::Arg | ExprKind::Call(..) | ExprKind::Seq(..) | ExprKind::Const(_) => {
|
||||
panic!("This should not appear outside function bodies")
|
||||
},
|
||||
ExprKind::Missing => panic!("Should have been replaced"),
|
||||
ExprKind::Atom(a) => {
|
||||
let x_norm = self.unpack_ident(&x).await;
|
||||
|
||||
@@ -155,8 +155,9 @@ async fn print_exprkind<'a>(
|
||||
) -> FmtUnit {
|
||||
match &ek {
|
||||
ExprKind::Arg => "Arg".to_string().into(),
|
||||
ExprKind::Missing =>
|
||||
panic!("This variant is swapped into write guards, so a read can never see it"),
|
||||
ExprKind::Missing => {
|
||||
panic!("This variant is swapped into write guards, so a read can never see it")
|
||||
},
|
||||
ExprKind::Atom(a) => a.print(c).await,
|
||||
ExprKind::Bottom(e) if e.len() == 1 => format!("Bottom({e})").into(),
|
||||
ExprKind::Bottom(e) => format!("Bottom(\n\t{}\n)", indent(&e.to_string())).into(),
|
||||
|
||||
@@ -211,12 +211,13 @@ impl Extension {
|
||||
.await
|
||||
{
|
||||
Ok(module) => module,
|
||||
Err(ChildError { kind, .. }) =>
|
||||
Err(ChildError { kind, .. }) => {
|
||||
break 'reply Err(match kind {
|
||||
ChildErrorKind::Private => panic!("Access checking was disabled"),
|
||||
ChildErrorKind::Constant => api::LsModuleError::IsConstant,
|
||||
ChildErrorKind::Missing => api::LsModuleError::InvalidPath,
|
||||
}),
|
||||
});
|
||||
},
|
||||
};
|
||||
let mut members = std::collections::HashMap::new();
|
||||
for (k, v) in &module.members {
|
||||
|
||||
@@ -158,14 +158,15 @@ impl System {
|
||||
}
|
||||
match cmod.imports.get(selector) {
|
||||
Some(Ok(dest)) => return Ok(dest.target.to_vname().suffix(tail.iter().cloned())),
|
||||
Some(Err(dests)) =>
|
||||
Some(Err(dests)) => {
|
||||
return Err(mk_errv_floating(
|
||||
is("Ambiguous name").await,
|
||||
format!(
|
||||
"{selector} could refer to {}",
|
||||
dests.iter().map(|ri| &ri.target).display("or")
|
||||
),
|
||||
)),
|
||||
));
|
||||
},
|
||||
None => (),
|
||||
}
|
||||
if root_data.root.members.get(selector).is_some() {
|
||||
|
||||
@@ -3,6 +3,7 @@ mod std;
|
||||
|
||||
pub use std::number::num_atom::{Float, HomoArray, Int, Num};
|
||||
pub use std::option::OrcOpt;
|
||||
pub use std::protocol::types::{ProtoBuilder, TagBuilder, proto, type_tag};
|
||||
pub use std::reflection::sym_atom::{SymAtom, sym_expr};
|
||||
pub use std::std_system::StdSystem;
|
||||
pub use std::string::str_atom::OrcString;
|
||||
|
||||
@@ -14,17 +14,36 @@ use crate::{HomoTpl, UntypedTuple};
|
||||
pub async fn gen_macro_lib() -> Vec<GenMember> {
|
||||
prefix("macros", [
|
||||
fun(true, "resolve", async |tpl: TAtom<MacTree>| resolve(own(&tpl).await).await),
|
||||
// TODO test whether any of this worked
|
||||
prefix("common", [
|
||||
build_macro(None, ["..", "_"]).finish(),
|
||||
build_macro(Some(1), ["+"])
|
||||
.rule(mactreev!("...$" lhs 0 macros::common::+ "...$" rhs 1), [async |[lhs, rhs]| {
|
||||
call(sym_ref(sym!(std::number::add)), [resolve(lhs).await, resolve(rhs).await])
|
||||
.rule(mactreev!("...$" lhs 1 macros::common::+ "...$" rhs 0), [async |[lhs, rhs]| {
|
||||
call(sym_ref(sym!(std::ops::add::resolve)), [resolve(lhs).await, resolve(rhs).await])
|
||||
}])
|
||||
.finish(),
|
||||
build_macro(Some(1), ["-"])
|
||||
.rule(mactreev!("...$" lhs 1 macros::common::- "...$" rhs 0), [async |[lhs, rhs]| {
|
||||
call(sym_ref(sym!(std::ops::sub::resolve)), [resolve(lhs).await, resolve(rhs).await])
|
||||
}])
|
||||
.finish(),
|
||||
build_macro(Some(2), ["*"])
|
||||
.rule(mactreev!("...$" lhs 0 macros::common::* "...$" rhs 1), [async |[lhs, rhs]| {
|
||||
call(sym_ref(sym!(std::number::mul)), [resolve(lhs).await, resolve(rhs).await])
|
||||
.rule(mactreev!("...$" lhs 1 macros::common::* "...$" rhs 0), [async |[lhs, rhs]| {
|
||||
call(sym_ref(sym!(std::ops::mul::resolve)), [resolve(lhs).await, resolve(rhs).await])
|
||||
}])
|
||||
.finish(),
|
||||
build_macro(Some(2), ["/"])
|
||||
.rule(mactreev!("...$" lhs 1 macros::common::/ "...$" rhs 0), [async |[lhs, rhs]| {
|
||||
call(sym_ref(sym!(std::ops::div::resolve)), [resolve(lhs).await, resolve(rhs).await])
|
||||
}])
|
||||
.finish(),
|
||||
build_macro(Some(2), ["%"])
|
||||
.rule(mactreev!("...$" lhs 1 macros::common::% "...$" rhs 0), [async |[lhs, rhs]| {
|
||||
call(sym_ref(sym!(std::ops::mod::resolve)), [resolve(lhs).await, resolve(rhs).await])
|
||||
}])
|
||||
.finish(),
|
||||
build_macro(Some(10), ["."])
|
||||
.rule(mactreev!("...$" lhs 1 macros::common::. "...$" rhs 0), [async |[lhs, rhs]| {
|
||||
call(sym_ref(sym!(std::ops::get::resolve)), [resolve(lhs).await, resolve(rhs).await])
|
||||
}])
|
||||
.finish(),
|
||||
build_macro(None, ["comma_list", ","])
|
||||
|
||||
@@ -123,12 +123,13 @@ async fn mk_scalar(pattern: &MacTree) -> OrcRes<ScalMatcher> {
|
||||
PhKind::Scalar => ScalMatcher::Placeh { key: name.clone() },
|
||||
},
|
||||
MacTok::S(c, body) => ScalMatcher::S(*c, Box::new(mk_any(&body.items).boxed_local().await?)),
|
||||
MacTok::Lambda(..) =>
|
||||
MacTok::Lambda(..) => {
|
||||
return Err(mk_errv(
|
||||
is("Lambda in matcher").await,
|
||||
"Lambdas can't be matched for, only generated in templates",
|
||||
[pattern.pos()],
|
||||
)),
|
||||
));
|
||||
},
|
||||
MacTok::Value(_) | MacTok::Slot => panic!("Only used for templating"),
|
||||
MacTok::Bottom(errv) => return Err(errv.clone()),
|
||||
})
|
||||
|
||||
@@ -95,6 +95,30 @@ pub async fn gen_std_macro_lib() -> Vec<GenMember> {
|
||||
.finish(),
|
||||
fun(false, "matcher_body", tuple_matcher_body),
|
||||
]),
|
||||
prefix("record", [
|
||||
build_macro(None, ["r"])
|
||||
.rule(mactreev!(std::record::r[ "...$" elements 0 ]), [async |[elements]: [_;_]| {
|
||||
exec(async move |mut h| {
|
||||
let tup = h
|
||||
.exec::<HomoTpl<TAtom<MacTree>>>(call(sym_ref(sym!(macros::resolve)), [
|
||||
mactree!((macros::common::comma_list "push" elements ;)).to_gen().await,
|
||||
]))
|
||||
.await?;
|
||||
let val = stream::iter(&tup.0[..])
|
||||
.fold(sym_ref(sym!(std::tuple::empty)), async |head, new| {
|
||||
call(sym_ref(sym!(std::tuple::cat)), [
|
||||
head,
|
||||
call(sym_ref(sym!(std::tuple::one)), [call(
|
||||
sym_ref(sym!(macros::resolve)),
|
||||
[new.clone().to_gen().await],
|
||||
)]),
|
||||
])
|
||||
})
|
||||
.await;
|
||||
Ok(val)
|
||||
}).await
|
||||
}]).finish(),
|
||||
])
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod number;
|
||||
pub mod ops;
|
||||
pub mod option;
|
||||
pub mod protocol;
|
||||
pub mod record;
|
||||
|
||||
@@ -4,14 +4,18 @@ 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, Supports, TAtom, ToAtom};
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::{
|
||||
AtomFactory, Atomic, AtomicFeatures, MethodSetBuilder, Supports, TAtom, ToAtom,
|
||||
};
|
||||
use orchid_extension::atom_thin::{ThinAtom, ThinVariant};
|
||||
use orchid_extension::conv::TryFromExpr;
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::gen_expr::sym_ref;
|
||||
use ordered_float::NotNan;
|
||||
use rust_decimal::prelude::Zero;
|
||||
|
||||
use crate::std::protocol::types::GetTagIdMethod;
|
||||
use crate::std::protocol::types::{GetImplMethod, GetTagIdMethod};
|
||||
use crate::std::string::to_string::ToStringMethod;
|
||||
|
||||
#[derive(Clone, Debug, Coding)]
|
||||
@@ -19,6 +23,12 @@ pub struct Int(pub i64);
|
||||
impl Atomic for Int {
|
||||
type Variant = ThinVariant;
|
||||
type Data = Self;
|
||||
fn reg_reqs() -> MethodSetBuilder<Self> {
|
||||
MethodSetBuilder::new()
|
||||
.handle::<GetTagIdMethod>()
|
||||
.handle::<GetImplMethod>()
|
||||
.handle::<ToStringMethod>()
|
||||
}
|
||||
}
|
||||
impl ThinAtom for Int {
|
||||
async fn print(&self) -> FmtUnit { self.0.to_string().into() }
|
||||
@@ -33,6 +43,25 @@ impl Supports<GetTagIdMethod> for Int {
|
||||
Sym::parse("std::number::Int").await.unwrap().to_api()
|
||||
}
|
||||
}
|
||||
impl Supports<GetImplMethod> for Int {
|
||||
async fn handle(&self, req: GetImplMethod) -> <GetImplMethod as Request>::Response {
|
||||
let name = Sym::from_api(req.0).await;
|
||||
let val = if name == sym!(std::ops::add) {
|
||||
sym_ref(sym!(std::number::add))
|
||||
} else if name == sym!(std::ops::sub) {
|
||||
sym_ref(sym!(std::number::sub))
|
||||
} else if name == sym!(std::ops::mul) {
|
||||
sym_ref(sym!(std::number::mul))
|
||||
} else if name == sym!(std::ops::div) {
|
||||
sym_ref(sym!(std::number::idiv))
|
||||
} else if name == sym!(std::ops::mod) {
|
||||
sym_ref(sym!(std::number::imod))
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
Some(val.create().await.serialize().await)
|
||||
}
|
||||
}
|
||||
impl Supports<ToStringMethod> for Int {
|
||||
async fn handle(&self, _: ToStringMethod) -> <ToStringMethod as Request>::Response {
|
||||
self.0.to_string()
|
||||
@@ -44,6 +73,12 @@ pub struct Float(pub NotNan<f64>);
|
||||
impl Atomic for Float {
|
||||
type Variant = ThinVariant;
|
||||
type Data = Self;
|
||||
fn reg_reqs() -> MethodSetBuilder<Self> {
|
||||
MethodSetBuilder::new()
|
||||
.handle::<GetTagIdMethod>()
|
||||
.handle::<GetImplMethod>()
|
||||
.handle::<ToStringMethod>()
|
||||
}
|
||||
}
|
||||
impl ThinAtom for Float {
|
||||
async fn print(&self) -> FmtUnit { self.0.to_string().into() }
|
||||
@@ -53,6 +88,30 @@ impl TryFromExpr for Float {
|
||||
Ok(Self(Num::try_from_expr(expr).await?.0.to_f64()))
|
||||
}
|
||||
}
|
||||
impl Supports<GetTagIdMethod> for Float {
|
||||
async fn handle(&self, _: GetTagIdMethod) -> <GetTagIdMethod as Request>::Response {
|
||||
Sym::parse("std::number::Float").await.unwrap().to_api()
|
||||
}
|
||||
}
|
||||
impl Supports<GetImplMethod> for Float {
|
||||
async fn handle(&self, req: GetImplMethod) -> <GetImplMethod as Request>::Response {
|
||||
let name = Sym::from_api(req.0).await;
|
||||
let val = if name == sym!(std::ops::add) {
|
||||
sym_ref(sym!(std::number::add))
|
||||
} else if name == sym!(std::ops::sub) {
|
||||
sym_ref(sym!(std::number::sub))
|
||||
} else if name == sym!(std::ops::mul) {
|
||||
sym_ref(sym!(std::number::mul))
|
||||
} else if name == sym!(std::ops::div) {
|
||||
sym_ref(sym!(std::number::fdiv))
|
||||
} else if name == sym!(std::ops::mod) {
|
||||
sym_ref(sym!(std::number::fmod))
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
Some(val.create().await.serialize().await)
|
||||
}
|
||||
}
|
||||
impl Supports<ToStringMethod> for Float {
|
||||
async fn handle(&self, _: ToStringMethod) -> <ToStringMethod as Request>::Response {
|
||||
self.0.to_string()
|
||||
|
||||
@@ -1,34 +1,60 @@
|
||||
use orchid_base::error::mk_errv;
|
||||
use orchid_base::interner::is;
|
||||
use orchid_base::number::Numeric;
|
||||
use orchid_extension::func_atom::get_arg;
|
||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||
use ordered_float::NotNan;
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
|
||||
use super::num_atom::{Float, HomoArray, Int, Num};
|
||||
|
||||
pub fn gen_num_lib() -> Vec<GenMember> {
|
||||
prefix("std::number", [
|
||||
fun(true, "add", async |a: Num, b: Num| {
|
||||
fun(false, "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", async |a: Num| {
|
||||
fun(false, "sub", 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(false, "neg", async |a: Num| {
|
||||
Num(match a.0 {
|
||||
Numeric::Int(i) => Numeric::Int(-i),
|
||||
Numeric::Float(f) => Numeric::Float(-f),
|
||||
})
|
||||
}),
|
||||
fun(true, "mul", async |a: Num, b: Num| {
|
||||
fun(false, "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", 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| {
|
||||
fun(false, "idiv", async |a: Int, b: Int| Int(a.0 / b.0)),
|
||||
fun(false, "imod", async |a: Int, b: Int| Int(a.0 % b.0)),
|
||||
fun(false, "fdiv", async |a: Float, b: Float| Float(a.0 / b.0)),
|
||||
fun(false, "fmod", async |a: Float, b: Float| {
|
||||
Float(a.0 - NotNan::new((a.0 / b.0).trunc()).unwrap() * b.0)
|
||||
}),
|
||||
fun(false, "to_i", async |a: Num| {
|
||||
Ok(Int(match a.0 {
|
||||
Numeric::Int(i) => i,
|
||||
Numeric::Float(f) => match f.to_i64() {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
return Err(mk_errv(
|
||||
is("Float out of range").await,
|
||||
format!("{f} is not representable as an integer"),
|
||||
[get_arg(0).pos().await],
|
||||
));
|
||||
},
|
||||
},
|
||||
}))
|
||||
}),
|
||||
fun(false, "to_f", async |a: Num| Float(a.0.to_f64())),
|
||||
])
|
||||
}
|
||||
|
||||
36
orchid-std/src/std/ops/mod.rs
Normal file
36
orchid-std/src/std/ops/mod.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use orchid_extension::tree::{GenMember, comments, prefix};
|
||||
|
||||
use crate::proto;
|
||||
|
||||
pub fn gen_ops_lib() -> Vec<GenMember> {
|
||||
prefix("std::ops", [
|
||||
comments(
|
||||
["Protocol for the infix + operator", "|type: self -> rhs -> self|"],
|
||||
proto(true, "add").finish(),
|
||||
),
|
||||
comments(
|
||||
["Protocol for the infix - operator", "|type: self -> rhs -> self|"],
|
||||
proto(true, "sub").finish(),
|
||||
),
|
||||
comments(
|
||||
["Protocol for the infix * operator", "|type: self -> rhs -> self|"],
|
||||
proto(true, "mul").finish(),
|
||||
),
|
||||
comments(
|
||||
["Protocol for the infix / operator", "|type: self -> rhs -> self|"],
|
||||
proto(true, "div").finish(),
|
||||
),
|
||||
comments(
|
||||
["Protocol for the infix % operator", "|type: self -> rhs -> self|"],
|
||||
proto(true, "mod").finish(),
|
||||
),
|
||||
comments(
|
||||
["Protocol used by paths for reading", "|type: self -> key -> value|"],
|
||||
proto(true, "get").finish(),
|
||||
),
|
||||
comments(
|
||||
["Protocol used by paths for writing", "|type: self -> key -> value -> self|"],
|
||||
proto(true, "set").finish(),
|
||||
),
|
||||
])
|
||||
}
|
||||
@@ -18,17 +18,19 @@ pub async fn parse_impls(
|
||||
) -> OrcRes<()> {
|
||||
let body = match &body_tt.tok {
|
||||
Token::S(Paren::Round, body) => line_items(Snippet::new(body_tt, body)).await,
|
||||
Token::S(ptyp, _) =>
|
||||
Token::S(ptyp, _) => {
|
||||
return Err(mk_errv(
|
||||
is("Incorrect paren type").await,
|
||||
format!("Expected () block, found {ptyp}"),
|
||||
[body_tt.sr().pos()],
|
||||
)),
|
||||
_ =>
|
||||
));
|
||||
},
|
||||
_ => {
|
||||
return Err(
|
||||
token_errv(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(line, is("impl").await).await {
|
||||
@@ -37,18 +39,20 @@ pub async fn parse_impls(
|
||||
Ok(None) => panic!("multiname is always at least one name"),
|
||||
Ok(Some(ref n @ Import { name: Some(_), ref sr, .. })) =>
|
||||
(n.clone().mspath().to_sym().await, sr.clone()),
|
||||
Ok(Some(Import { name: None, sr, .. })) =>
|
||||
Ok(Some(Import { name: None, sr, .. })) => {
|
||||
return Err(mk_errv(
|
||||
is("impl line with globstar").await,
|
||||
"::* is not permitted in a protocol impl",
|
||||
[sr.pos()],
|
||||
)),
|
||||
Err(e) =>
|
||||
));
|
||||
},
|
||||
Err(e) => {
|
||||
return Err(mk_errv(
|
||||
is("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(tail, is("as").await).await?;
|
||||
let cnst_name = is(&format!("{}{}", lines.len(), name.iter().join("__"))).await;
|
||||
|
||||
@@ -1,22 +1,29 @@
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::FutureExt;
|
||||
use futures::future::{LocalBoxFuture, join_all};
|
||||
use hashbrown::HashMap;
|
||||
use itertools::Itertools;
|
||||
use never::Never;
|
||||
use orchid_api_derive::Coding;
|
||||
use orchid_api_derive::{Coding, Hierarchy};
|
||||
use orchid_api_traits::Request;
|
||||
use orchid_base::error::{OrcRes, mk_errv};
|
||||
use orchid_base::format::fmt;
|
||||
use orchid_base::interner::is;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::interner::{ev, is};
|
||||
use orchid_base::name::{NameLike, Sym, VName};
|
||||
use orchid_extension::atom::{AtomMethod, Atomic, ForeignAtom, MethodSetBuilder, Supports, TAtom};
|
||||
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::gen_expr::call;
|
||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||
use orchid_extension::conv::{ClonableToExprDyn, ToExpr};
|
||||
use orchid_extension::coroutine_exec::exec;
|
||||
use orchid_extension::expr::{Expr, ExprHandle};
|
||||
use orchid_extension::gen_expr::{GExpr, call, sym_ref};
|
||||
use orchid_extension::system::dep_req;
|
||||
use orchid_extension::tree::{GenMember, MemKind, cnst, fun, lazy, prefix};
|
||||
|
||||
use crate::api;
|
||||
use crate::std::std_system::StdReq;
|
||||
use crate::{StdSystem, api};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Tag {
|
||||
@@ -26,17 +33,25 @@ pub struct Tag {
|
||||
impl Atomic for Tag {
|
||||
type Data = api::TStrv;
|
||||
type Variant = OwnedVariant;
|
||||
fn reg_reqs() -> MethodSetBuilder<Self> { MethodSetBuilder::new().handle::<GetImplMethod>() }
|
||||
fn reg_reqs() -> MethodSetBuilder<Self> {
|
||||
MethodSetBuilder::new().handle::<GetTagIdMethod>().handle::<GetImplMethod>()
|
||||
}
|
||||
}
|
||||
impl OwnedAtom for Tag {
|
||||
type Refs = Never;
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.id.to_api()) }
|
||||
}
|
||||
impl Supports<GetTagIdMethod> for Tag {
|
||||
async fn handle(&self, _: GetTagIdMethod) -> <GetTagIdMethod as Request>::Response {
|
||||
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).await).map(|expr| expr.handle().ticket())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Coding)]
|
||||
pub struct GetImplMethod(pub api::TStrv);
|
||||
impl Request for GetImplMethod {
|
||||
@@ -75,14 +90,16 @@ impl Supports<GetImplMethod> for Tagged {
|
||||
|
||||
pub async fn get_impl(receiver: ForeignAtom, proto: ForeignAtom) -> OrcRes<Expr> {
|
||||
let Some(proto_id) = proto.request(GetTagIdMethod).await else {
|
||||
return Err(mk_errv(is("Not a protocol").await, "Protocol does not have a tag ID", [
|
||||
proto.pos()
|
||||
]));
|
||||
return Err(mk_errv(
|
||||
is("Not a protocol").await,
|
||||
format!("Protocol ({}) does not have a tag ID", fmt(&proto).await),
|
||||
[proto.pos()],
|
||||
));
|
||||
};
|
||||
let Some(impl_val_opt) = receiver.request(GetImplMethod(proto_id)).await else {
|
||||
return Err(mk_errv(
|
||||
is("Receiver not tagged").await,
|
||||
"The receiver does not have a type tag",
|
||||
format!("The receiver ({}) does not have a type tag", fmt(&receiver).await),
|
||||
[receiver.pos()],
|
||||
));
|
||||
};
|
||||
@@ -92,14 +109,14 @@ pub async fn get_impl(receiver: ForeignAtom, proto: ForeignAtom) -> OrcRes<Expr>
|
||||
let Some(type_id) = receiver.request(GetTagIdMethod).await else {
|
||||
return Err(mk_errv(
|
||||
is("Incorrect protocols implementation in extension").await,
|
||||
"Atom provides an impl table but no tag ID",
|
||||
format!("The receiver ({}) provides an impl table but no tag ID", fmt(&receiver).await),
|
||||
[receiver.pos()],
|
||||
));
|
||||
};
|
||||
let Some(impl_val_opt) = proto.request(GetImplMethod(type_id)).await else {
|
||||
return Err(mk_errv(
|
||||
is("Incorrect protocols implementation in extension").await,
|
||||
"Proto table atom provides a tag ID but no impl table",
|
||||
format!("Protocol ({}) provides a tag ID but no impl table", fmt(&proto).await),
|
||||
[receiver.pos()],
|
||||
));
|
||||
};
|
||||
@@ -108,7 +125,7 @@ pub async fn get_impl(receiver: ForeignAtom, proto: ForeignAtom) -> OrcRes<Expr>
|
||||
}
|
||||
return Err(mk_errv(
|
||||
is("Implementation not found").await,
|
||||
"This protocol is not implemented for this receiver",
|
||||
format!("Protocol {} is not implemented for {}", ev(proto_id).await, ev(type_id).await),
|
||||
[receiver.pos(), proto.pos()],
|
||||
));
|
||||
}
|
||||
@@ -134,3 +151,107 @@ pub fn gen_protocol_lib() -> Vec<GenMember> {
|
||||
}),
|
||||
])
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Coding, Hierarchy)]
|
||||
#[extends(StdReq)]
|
||||
pub struct CreateTag {
|
||||
pub name: api::TStrv,
|
||||
pub impls: std::collections::HashMap<api::TStrv, api::ExprTicket>,
|
||||
}
|
||||
impl Request for CreateTag {
|
||||
type Response = api::ExprTicket;
|
||||
}
|
||||
|
||||
pub fn type_tag<'a>(name: &str) -> TagBuilder<'a> {
|
||||
TagBuilder { name: name.to_owned(), impls: HashMap::default() }
|
||||
}
|
||||
pub struct TagBuilder<'a> {
|
||||
name: String,
|
||||
impls: HashMap<String, LocalBoxFuture<'a, GExpr>>,
|
||||
}
|
||||
impl<'a> TagBuilder<'a> {
|
||||
pub fn add_impl(&mut self, name: &str, val: impl ToExpr + 'a) {
|
||||
self.impls.insert(name.to_owned(), val.to_gen().boxed_local());
|
||||
}
|
||||
pub fn with_impl(mut self, name: &str, val: impl ToExpr + 'a) -> Self {
|
||||
self.add_impl(name, val);
|
||||
self
|
||||
}
|
||||
pub async fn finish(self) -> TAtom<Tag> {
|
||||
let tk = dep_req::<StdSystem, _>(CreateTag {
|
||||
name: Sym::parse(&self.name).await.unwrap().to_api(),
|
||||
impls: join_all(self.impls.into_iter().map(|(s, fut)| async move {
|
||||
(
|
||||
Sym::parse(&s).await.unwrap().to_api(),
|
||||
fut.await.create().await.handle().serialize().await,
|
||||
)
|
||||
}))
|
||||
.await
|
||||
.into_iter()
|
||||
.collect(),
|
||||
})
|
||||
.await;
|
||||
TAtom::downcast(ExprHandle::deserialize(tk)).await.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn proto(public: bool, name: &str) -> ProtoBuilder {
|
||||
ProtoBuilder { public, name: name.to_owned(), impls: HashMap::new(), body: Vec::new() }
|
||||
}
|
||||
pub struct ProtoBuilder {
|
||||
public: bool,
|
||||
name: String,
|
||||
impls: HashMap<String, Box<dyn ClonableToExprDyn>>,
|
||||
body: Vec<GenMember>,
|
||||
}
|
||||
impl ProtoBuilder {
|
||||
pub fn add_impl(&mut self, name: &str, val: impl ToExpr + Clone + 'static) {
|
||||
self.impls.insert(name.to_owned(), Box::new(val));
|
||||
}
|
||||
pub fn with_impl(mut self, name: &str, val: impl ToExpr + Clone + 'static) -> Self {
|
||||
self.add_impl(name, val);
|
||||
self
|
||||
}
|
||||
pub fn add_body(&mut self, members: impl IntoIterator<Item = GenMember>) {
|
||||
self.body.extend(members);
|
||||
}
|
||||
pub fn with_body(mut self, members: impl IntoIterator<Item = GenMember>) -> Self {
|
||||
self.add_body(members);
|
||||
self
|
||||
}
|
||||
pub fn finish(self) -> Vec<GenMember> {
|
||||
lazy(self.public, &self.name, async |path| {
|
||||
let mut tag = type_tag(&path.segs().join("::"));
|
||||
for (name, value) in self.impls {
|
||||
tag.add_impl(&name, value.to_expr().await);
|
||||
}
|
||||
MemKind::module([
|
||||
cnst(true, "__protocol_tag__", tag.finish().await),
|
||||
fun(true, "resolve", resolver_for(path.to_vname())),
|
||||
])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolver_for(proto: VName) -> impl AsyncFn(ForeignAtom) -> GExpr + Clone {
|
||||
let proto_cache = RefCell::new(None);
|
||||
async move |atom| {
|
||||
let proto_cache = proto_cache.clone();
|
||||
let proto = proto.clone();
|
||||
exec(async move |mut h| {
|
||||
let cached_proto = proto_cache.borrow().as_ref().cloned();
|
||||
let proto = match cached_proto {
|
||||
Some(val) => val,
|
||||
None => {
|
||||
let proto: ForeignAtom = h
|
||||
.exec(sym_ref(proto.clone().suffix([is("__protocol_tag__").await]).to_sym().await))
|
||||
.await?;
|
||||
*proto_cache.borrow_mut() = Some(proto.clone());
|
||||
proto
|
||||
},
|
||||
};
|
||||
Ok(call(get_impl(atom.clone(), proto).await?.to_gen().await, [atom.to_gen().await]))
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,17 @@ use std::rc::Rc;
|
||||
use futures::AsyncWrite;
|
||||
use futures::future::join_all;
|
||||
use hashbrown::HashMap;
|
||||
use orchid_api_traits::Encode;
|
||||
use orchid_api_traits::{Encode, Request};
|
||||
use orchid_base::interner::{IStr, es};
|
||||
use orchid_extension::atom::Atomic;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::{Atomic, Supports};
|
||||
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::gen_expr::sym_ref;
|
||||
|
||||
use crate::api;
|
||||
use crate::std::protocol::types::{GetImplMethod, GetTagIdMethod};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Record(pub Rc<HashMap<IStr, Expr>>);
|
||||
@@ -35,3 +39,21 @@ impl OwnedAtom for Record {
|
||||
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||
}
|
||||
impl Supports<GetTagIdMethod> for Record {
|
||||
async fn handle(&self, _: GetTagIdMethod) -> <GetTagIdMethod as Request>::Response {
|
||||
Sym::literal("std::record::Record").await.to_api()
|
||||
}
|
||||
}
|
||||
impl Supports<GetImplMethod> for Record {
|
||||
async fn handle(&self, req: GetImplMethod) -> <GetImplMethod as Request>::Response {
|
||||
let name = Sym::from_api(req.0).await;
|
||||
let val = if name == sym!(std::ops::get) {
|
||||
sym_ref(sym!(std::record::get))
|
||||
} else if name == sym!(std::ops::set) {
|
||||
sym_ref(sym!(std::record::set))
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
Some(val.create().await.serialize().await)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,21 +18,23 @@ 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::ops::gen_ops_lib;
|
||||
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::protocol::types::{CreateTag, 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]
|
||||
#[allow(clippy::enum_variant_names, reason = "For the time being there are only ctor calls")]
|
||||
pub enum StdReq {
|
||||
CreateTag(CreateTag),
|
||||
CreateTuple(CreateTuple),
|
||||
CreateSymAtom(CreateSymAtom),
|
||||
}
|
||||
@@ -61,7 +63,6 @@ impl SystemCard for StdSystem {
|
||||
Some(TupleBuilder::dynfo()),
|
||||
Some(Tag::dynfo()),
|
||||
Some(Tagged::dynfo()),
|
||||
Some(AsStrTag::dynfo()),
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -77,6 +78,21 @@ impl System for StdSystem {
|
||||
let sym_atom = SymAtom(Sym::from_api(sym_tok).await);
|
||||
xreq.reply(req, &sym_atom.to_expr().await.serialize().await).await.unwrap()
|
||||
},
|
||||
StdReq::CreateTag(ref req @ CreateTag { name, ref impls }) => {
|
||||
let tag_atom = Tag {
|
||||
id: Sym::from_api(name).await,
|
||||
impls: Rc::new(
|
||||
join_all(
|
||||
(impls.iter())
|
||||
.map(|(k, v)| async { (Sym::from_api(*k).await, Expr::deserialize(*v).await) }),
|
||||
)
|
||||
.await
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
};
|
||||
xreq.reply(req, &tag_atom.to_expr().await.serialize().await).await.unwrap()
|
||||
},
|
||||
}
|
||||
}
|
||||
fn lexers() -> Vec<LexerObj> { vec![&StringLexer, &NumLexer] }
|
||||
@@ -90,6 +106,7 @@ impl System for StdSystem {
|
||||
gen_tuple_lib(),
|
||||
gen_protocol_lib(),
|
||||
gen_sym_lib().await,
|
||||
gen_ops_lib(),
|
||||
])
|
||||
}
|
||||
async fn prelude() -> Vec<Sym> { vec![sym!(std), sym!(std::tuple), sym!(std::option)] }
|
||||
|
||||
@@ -9,36 +9,34 @@ use orchid_api_traits::{Encode, Request};
|
||||
use orchid_base::error::{OrcRes, mk_errv};
|
||||
use orchid_base::format::{FmtCtx, FmtUnit};
|
||||
use orchid_base::interner::{IStr, es, is};
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::{AtomMethod, Atomic, MethodSetBuilder, Supports, TAtom};
|
||||
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
|
||||
use orchid_extension::conv::TryFromExpr;
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::gen_expr::sym_ref;
|
||||
|
||||
use crate::std::protocol::types::{GetImplMethod, GetTagIdMethod};
|
||||
use crate::std::string::to_string::ToStringMethod;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Coding)]
|
||||
pub struct StringGetVal;
|
||||
impl Request for StringGetVal {
|
||||
pub struct StringGetValMethod;
|
||||
impl Request for StringGetValMethod {
|
||||
type Response = Rc<String>;
|
||||
}
|
||||
impl AtomMethod for StringGetVal {
|
||||
impl AtomMethod for StringGetValMethod {
|
||||
const NAME: &str = "std::string_get_val";
|
||||
}
|
||||
impl Supports<StringGetVal> for StrAtom {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StrAtom(Rc<String>);
|
||||
impl Atomic for StrAtom {
|
||||
type Variant = OwnedVariant;
|
||||
type Data = ();
|
||||
fn reg_reqs() -> MethodSetBuilder<Self> { MethodSetBuilder::new().handle::<StringGetVal>() }
|
||||
fn reg_reqs() -> MethodSetBuilder<Self> {
|
||||
MethodSetBuilder::new().handle::<StringGetValMethod>().handle::<ToStringMethod>()
|
||||
}
|
||||
}
|
||||
impl StrAtom {
|
||||
pub fn new(str: Rc<String>) -> Self { Self(str) }
|
||||
@@ -60,12 +58,44 @@ impl OwnedAtom for StrAtom {
|
||||
Self::new(Rc::new(ctx.read::<String>().await))
|
||||
}
|
||||
}
|
||||
impl Supports<StringGetValMethod> for StrAtom {
|
||||
async fn handle(&self, _: StringGetValMethod) -> <StringGetValMethod 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()
|
||||
}
|
||||
}
|
||||
impl Supports<GetTagIdMethod> for StrAtom {
|
||||
async fn handle(&self, _: GetTagIdMethod) -> <GetTagIdMethod as Request>::Response {
|
||||
Sym::literal("std::string::StrAtom").await.to_api()
|
||||
}
|
||||
}
|
||||
impl Supports<GetImplMethod> for StrAtom {
|
||||
async fn handle(&self, req: GetImplMethod) -> <GetImplMethod as Request>::Response {
|
||||
let name = Sym::from_api(req.0).await;
|
||||
let val = if name == sym!(std::ops::add) {
|
||||
sym_ref(sym!(std::string::concat))
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
Some(val.create().await.serialize().await)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IntStrAtom(pub(crate) IStr);
|
||||
impl Atomic for IntStrAtom {
|
||||
type Variant = OwnedVariant;
|
||||
type Data = orchid_api::TStr;
|
||||
fn reg_reqs() -> MethodSetBuilder<Self> {
|
||||
MethodSetBuilder::new()
|
||||
.handle::<GetTagIdMethod>()
|
||||
.handle::<GetImplMethod>()
|
||||
.handle::<ToStringMethod>()
|
||||
}
|
||||
}
|
||||
impl From<IStr> for IntStrAtom {
|
||||
fn from(value: IStr) -> Self { Self(value) }
|
||||
@@ -94,6 +124,22 @@ impl Supports<ToStringMethod> for IntStrAtom {
|
||||
self.0.to_string()
|
||||
}
|
||||
}
|
||||
impl Supports<GetTagIdMethod> for IntStrAtom {
|
||||
async fn handle(&self, _: GetTagIdMethod) -> <GetTagIdMethod as Request>::Response {
|
||||
Sym::literal("std::string::IntStrAtom").await.to_api()
|
||||
}
|
||||
}
|
||||
impl Supports<GetImplMethod> for IntStrAtom {
|
||||
async fn handle(&self, req: GetImplMethod) -> <GetImplMethod as Request>::Response {
|
||||
let name = Sym::from_api(req.0).await;
|
||||
let val = if name == sym!(std::ops::add) {
|
||||
sym_ref(sym!(std::string::concat))
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
Some(val.create().await.serialize().await)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OrcString {
|
||||
@@ -109,7 +155,7 @@ impl OrcString {
|
||||
pub async fn get_string(&self) -> Rc<String> {
|
||||
match &self.kind {
|
||||
OrcStringKind::Int(tok) => es(**tok).await.rc(),
|
||||
OrcStringKind::Val(atom) => atom.request(StringGetVal).await,
|
||||
OrcStringKind::Val(atom) => atom.request(StringGetValMethod).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ 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 orchid_extension::tree::{GenMember, 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};
|
||||
use crate::std::protocol::types::{get_impl, proto};
|
||||
use crate::std::string::to_string::ToStringMethod;
|
||||
|
||||
pub fn gen_str_lib() -> Vec<GenMember> {
|
||||
prefix("std::string", [
|
||||
@@ -45,15 +45,6 @@ pub fn gen_str_lib() -> Vec<GenMember> {
|
||||
.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))).await?;
|
||||
Ok(call(get_impl(atom.clone(), proto).await?.to_gen().await, [atom.to_gen().await]))
|
||||
})
|
||||
.await
|
||||
}),
|
||||
]),
|
||||
proto(true, "to_string").finish(),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -1,30 +1,8 @@
|
||||
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 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").await.unwrap().to_api()
|
||||
}
|
||||
}
|
||||
impl Supports<GetImplMethod> for AsStrTag {
|
||||
async fn handle(&self, _: GetImplMethod) -> <GetImplMethod as Request>::Response { None }
|
||||
}
|
||||
use orchid_extension::atom::AtomMethod;
|
||||
|
||||
/// Method version of std::string::to_string protocol for atoms
|
||||
#[derive(Coding, Clone, Debug)]
|
||||
pub struct ToStringMethod;
|
||||
impl Request for ToStringMethod {
|
||||
|
||||
@@ -179,7 +179,7 @@ impl Spawner for SpawnerImpl {
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> io::Result<ExitCode> {
|
||||
eprintln!("Orcx was made by Lawrence Bethlenfalvy");
|
||||
eprintln!("Orcx v0.1 is free software provided without warranty.");
|
||||
let args = Args::parse();
|
||||
let exit_code = Rc::new(RefCell::new(ExitCode::SUCCESS));
|
||||
let local_set = LocalSet::new();
|
||||
@@ -424,8 +424,9 @@ async fn main() -> io::Result<ExitCode> {
|
||||
xctx.set_gas(Some(10_000));
|
||||
xctx.execute().await;
|
||||
match xctx.result() {
|
||||
ExecResult::Value(val) =>
|
||||
println!("{}", take_first(&val.print(&FmtCtxImpl::default()).await, false)),
|
||||
ExecResult::Value(val) => {
|
||||
println!("{}", take_first(&val.print(&FmtCtxImpl::default()).await, false))
|
||||
},
|
||||
ExecResult::Err(e) => println!("error: {e}"),
|
||||
ExecResult::Gas(_) => println!("Ran out of gas!"),
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ pub struct Args {
|
||||
pub enum Commands {
|
||||
CheckApiRefs,
|
||||
Orcx {
|
||||
#[arg(long)]
|
||||
release: bool,
|
||||
#[arg(trailing_var_arg = true, num_args = 1..)]
|
||||
argv: Vec<String>,
|
||||
},
|
||||
@@ -41,7 +43,7 @@ fn main() -> io::Result<ExitCode> {
|
||||
let args = Args::parse();
|
||||
match &args.command {
|
||||
Commands::CheckApiRefs => check_api_refs(&args)?,
|
||||
Commands::Orcx { argv } => orcx(&args, argv)?,
|
||||
Commands::Orcx { release, argv } => orcx(*release, &args, argv)?,
|
||||
Commands::Orcxdb { argv } => orcxdb(&args, argv)?,
|
||||
}
|
||||
Ok(if EXIT_OK.load(Ordering::Relaxed) { ExitCode::SUCCESS } else { ExitCode::FAILURE })
|
||||
|
||||
@@ -4,17 +4,25 @@ use std::sync::atomic::Ordering;
|
||||
|
||||
use crate::{Args, EXIT_OK};
|
||||
|
||||
pub fn orcx(_args: &Args, argv: &[String]) -> io::Result<()> {
|
||||
if !Command::new("cargo").args(["build", "-p", "orchid-std", "--quiet"]).status()?.success() {
|
||||
pub fn orcx(release: bool, _args: &Args, argv: &[String]) -> io::Result<()> {
|
||||
let mut std_build_cmd = Command::new("cargo");
|
||||
std_build_cmd.args(["build", "-p", "orchid-std", "--quiet"]);
|
||||
if release {
|
||||
std_build_cmd.arg("--release");
|
||||
}
|
||||
|
||||
if !std_build_cmd.status()?.success() {
|
||||
EXIT_OK.store(false, Ordering::Relaxed);
|
||||
return Ok(());
|
||||
}
|
||||
if !Command::new("cargo")
|
||||
.args(["run", "-p", "orcx", "--quiet", "--"])
|
||||
.args(argv)
|
||||
.status()?
|
||||
.success()
|
||||
{
|
||||
let mut run_cmd = Command::new("cargo");
|
||||
run_cmd.args(["run", "-p", "orcx", "--quiet"]);
|
||||
if release {
|
||||
run_cmd.arg("--release");
|
||||
}
|
||||
run_cmd.arg("--");
|
||||
run_cmd.args(argv);
|
||||
if !run_cmd.status()?.success() {
|
||||
EXIT_OK.store(false, Ordering::Relaxed);
|
||||
}
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user