Protocols and operators mostly
All checks were successful
Rust / build (push) Successful in 4m8s

This commit is contained in:
2026-01-21 22:22:58 +01:00
parent 75b05a2965
commit f38193edcc
33 changed files with 578 additions and 147 deletions

View File

@@ -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 The project uses both the stable and nightly rust toolchain. Run the examples with
```sh ```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. you can try modifying the examples, but error reporting for the time being is pretty terrible.

View File

@@ -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 { let user =
option::some t[ref head, ..] => head;
option::none => "foo";
}

View File

@@ -5,6 +5,7 @@ use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::task::{Context, Poll, Wake, Waker}; use std::task::{Context, Poll, Wake, Waker};
use std::thread::panicking;
use futures::Stream; use futures::Stream;
use itertools::Itertools; 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) }
}

View File

@@ -202,6 +202,11 @@ impl Sym {
pub async fn parse(s: &str) -> Result<Self, EmptyNameError> { pub async fn parse(s: &str) -> Result<Self, EmptyNameError> {
Ok(Sym(iv(&VName::parse(s).await?.into_vec()).await)) 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] /// Assert that a token isn't empty, and wrap it in a [Sym]
pub fn from_tok(t: IStrv) -> Result<Self, EmptyNameError> { pub fn from_tok(t: IStrv) -> Result<Self, EmptyNameError> {
if t.is_empty() { Err(EmptyNameError) } else { Ok(Self(t)) } if t.is_empty() { Err(EmptyNameError) } else { Ok(Self(t)) }

View File

@@ -17,6 +17,7 @@ use futures::{
use hashbrown::HashMap; use hashbrown::HashMap;
use orchid_api_traits::{Decode, Encode, Request, UnderRoot}; use orchid_api_traits::{Decode, Encode, Request, UnderRoot};
use crate::future_debug::{PanicOnDrop, assert_no_drop};
use crate::localset::LocalSet; use crate::localset::LocalSet;
#[must_use = "Receipts indicate that a required action has been performed within a function. \ #[must_use = "Receipts indicate that a required action has been performed within a function. \
@@ -238,9 +239,12 @@ impl IoClient {
impl Client for IoClient { impl Client for IoClient {
fn start_notif(&self) -> LocalBoxFuture<'_, io::Result<Box<dyn MsgWriter<'_> + '_>>> { fn start_notif(&self) -> LocalBoxFuture<'_, io::Result<Box<dyn MsgWriter<'_> + '_>>> {
Box::pin(async { Box::pin(async {
let drop_g = assert_no_drop("Notif future dropped");
let mut o = self.lock_out().await; let mut o = self.lock_out().await;
0u64.encode(o.as_mut()).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<'_> + '_>>> { fn start_request(&self) -> LocalBoxFuture<'_, io::Result<Box<dyn ReqWriter<'_> + '_>>> {
@@ -252,11 +256,17 @@ impl Client for IoClient {
}; };
let (cb, reply) = oneshot::channel(); let (cb, reply) = oneshot::channel();
let (ack, got_ack) = 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(); self.subscribe.as_ref().clone().send(ReplySub { id, ack, cb }).await.unwrap();
got_ack.await.unwrap(); got_ack.await.unwrap();
let mut w = self.lock_out().await; let mut w = self.lock_out().await;
id.encode(w.as_mut()).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 { struct IoReqWriter {
reply: oneshot::Receiver<IoGuard<dyn AsyncRead>>, reply: oneshot::Receiver<IoGuard<dyn AsyncRead>>,
w: IoGuard<dyn AsyncWrite>, w: IoGuard<dyn AsyncWrite>,
drop_g: PanicOnDrop,
} }
impl<'a> ReqWriter<'a> for IoReqWriter { impl<'a> ReqWriter<'a> for IoReqWriter {
fn writer(&mut self) -> Pin<&mut dyn AsyncWrite> { self.w.as_mut() } 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>>> { fn send(self: Box<Self>) -> LocalBoxFuture<'a, io::Result<Box<dyn RepReader<'a> + 'a>>> {
Box::pin(async { Box::pin(async {
let Self { reply, mut w } = *self; let Self { reply, mut w, drop_g } = *self;
w.flush().await?; w.flush().await?;
mem::drop(w); mem::drop(w);
let i = reply.await.expect("Client dropped before reply received"); 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 { struct IoRepReader {
i: IoGuard<dyn AsyncRead>, i: IoGuard<dyn AsyncRead>,
drop_g: PanicOnDrop,
} }
impl<'a> RepReader<'a> for IoRepReader { impl<'a> RepReader<'a> for IoRepReader {
fn reader(&mut self) -> Pin<&mut dyn AsyncRead> { self.i.as_mut() } 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)] #[derive(destructure)]
struct IoNotifWriter { struct IoNotifWriter {
o: IoGuard<dyn AsyncWrite>, o: IoGuard<dyn AsyncWrite>,
drop_g: PanicOnDrop,
} }
impl<'a> MsgWriter<'a> for IoNotifWriter { impl<'a> MsgWriter<'a> for IoNotifWriter {
fn writer(&mut self) -> Pin<&mut dyn AsyncWrite> { self.o.as_mut() } fn writer(&mut self) -> Pin<&mut dyn AsyncWrite> { self.o.as_mut() }
fn finish(mut self: Box<Self>) -> LocalBoxFuture<'static, io::Result<()>> { 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::Exit) => break,
Ok(Event::Sub(ReplySub { id, ack, cb })) => { Ok(Event::Sub(ReplySub { id, ack, cb })) => {
pending_replies.insert(id, cb); pending_replies.insert(id, cb);
ack.send(()).unwrap(); // this is detected and logged on client
let _ = ack.send(());
}, },
Ok(Event::Input(0, read)) => { Ok(Event::Input(0, read)) => {
let notif = &notif; let notif = &notif;

View File

@@ -1,6 +1,6 @@
use std::any::{Any, TypeId, type_name}; use std::any::{Any, TypeId, type_name};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt::{self, Debug};
use std::future::Future; use std::future::Future;
use std::num::NonZeroU32; use std::num::NonZeroU32;
use std::ops::Deref; 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 { pub trait AtomMethod: Request + Coding {
const NAME: &str; const NAME: &str;

View File

@@ -136,6 +136,7 @@ impl Expr {
_ => Err(self), _ => Err(self),
} }
} }
pub async fn pos(&self) -> Pos { self.data().await.pos.clone() }
pub fn handle(&self) -> Rc<ExprHandle> { self.handle.clone() } pub fn handle(&self) -> Rc<ExprHandle> { self.handle.clone() }
pub fn slot(&self) -> GExpr { pub fn slot(&self) -> GExpr {

View File

@@ -31,6 +31,19 @@ trait_set! {
trait FunCB = Fn(Vec<Expr>) -> LocalBoxFuture<'static, OrcRes<GExpr>> + 'static; 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 { pub trait ExprFunc<I, O>: Clone + 'static {
fn argtyps() -> &'static [TypeId]; fn argtyps() -> &'static [TypeId];
fn apply<'a>(&self, hand: ExecHandle<'a>, v: Vec<Expr>) -> impl Future<Output = OrcRes<GExpr>>; fn apply<'a>(&self, hand: ExecHandle<'a>, v: Vec<Expr>) -> impl Future<Output = OrcRes<GExpr>>;

View File

@@ -184,12 +184,12 @@ impl ConstCtx {
&'b self, &'b self,
names: impl IntoIterator<Item = &'b Sym> + 'b, names: impl IntoIterator<Item = &'b Sym> + 'b,
) -> impl Stream<Item = OrcRes<Sym>> + 'b { ) -> impl Stream<Item = OrcRes<Sym>> + 'b {
let resolve_names = api::ResolveNames { let names = names.into_iter().map(|n| n.to_api()).collect_vec();
constid: self.constid,
sys: sys_id(),
names: names.into_iter().map(|n| n.to_api()).collect_vec(),
};
stream(async |mut cx| { 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 { for name_opt in request(resolve_names).await {
cx.emit(match name_opt { cx.emit(match name_opt {
Err(e) => Err(OrcErrv::from_api(&e).await), Err(e) => Err(OrcErrv::from_api(&e).await),

View File

@@ -47,8 +47,9 @@ impl ReflMod {
async fn try_populate(&self) -> Result<(), api::LsModuleError> { async fn try_populate(&self) -> Result<(), api::LsModuleError> {
let path_tok = iv(&self.0.path[..]).await; let path_tok = iv(&self.0.path[..]).await;
let reply = match request(api::LsModule(sys_id(), path_tok.to_api())).await { let reply = match request(api::LsModule(sys_id(), path_tok.to_api())).await {
Err(api::LsModuleError::TreeUnavailable) => Err(api::LsModuleError::TreeUnavailable) => {
panic!("Reflected tree accessed outside an interpreter call. This extension is faulty."), panic!("Reflected tree accessed outside an interpreter call. This extension is faulty.")
},
Err(err) => return Err(err), Err(err) => return Err(err),
Ok(details) => details, Ok(details) => details,
}; };
@@ -79,10 +80,12 @@ impl ReflMod {
return None; return None;
} }
match self.try_populate().await { match self.try_populate().await {
Err(api::LsModuleError::InvalidPath) => Err(api::LsModuleError::InvalidPath) => {
panic!("Path became invalid since module was created"), 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::IsConstant) => {
panic!("Path previously contained a module but now contains a constant")
},
Err(api::LsModuleError::TreeUnavailable) => unreachable!(), Err(api::LsModuleError::TreeUnavailable) => unreachable!(),
Ok(()) => (), Ok(()) => (),
} }

View File

@@ -71,8 +71,7 @@ pub fn module(
vec![GenMember { name, kind, public, comments: vec![] }] vec![GenMember { name, kind, public, comments: vec![] }]
} }
pub fn root_mod(name: &str, mems: impl IntoIterator<Item = Vec<GenMember>>) -> (String, MemKind) { 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(), MemKind::module(mems))
(name.to_string(), kind)
} }
pub fn fun<I, O>(public: bool, name: &str, xf: impl ExprFunc<I, O>) -> Vec<GenMember> { pub fn fun<I, O>(public: bool, name: &str, xf: impl ExprFunc<I, O>) -> Vec<GenMember> {
let fac = 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())); 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); 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) => { 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() { 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![]; let mut swap = vec![];
std::mem::swap(&mut swap, old_items); std::mem::swap(&mut swap, old_items);
*old_items = merge_trivial([swap, members]); *old_items = merge_trivial([swap, members]);
@@ -167,15 +166,19 @@ impl GenMember {
pub enum MemKind { pub enum MemKind {
Const(GExpr), Const(GExpr),
Mod { members: Vec<GenMember> }, Mod(Vec<GenMember>),
Lazy(LazyMemberFactory), Lazy(LazyMemberFactory),
} }
impl MemKind { 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 { pub(crate) async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MemberKind {
match self { match self {
Self::Lazy(lazy) => api::MemberKind::Lazy(add_lazy(ctx, lazy)), Self::Lazy(lazy) => api::MemberKind::Lazy(add_lazy(ctx, lazy)),
Self::Const(c) => api::MemberKind::Const(c.serialize().await), 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| { members: stream(async |mut cx| {
for m in members { for m in members {
cx.emit(m.into_api(ctx).await).await cx.emit(m.into_api(ctx).await).await

View File

@@ -110,8 +110,9 @@ impl ExecCtx {
(ExprKind::Identity(val.clone()), StackOp::Swap(val)) (ExprKind::Identity(val.clone()), StackOp::Swap(val))
}, },
Err(f) => match &*f.kind().read().await { Err(f) => match &*f.kind().read().await {
ExprKind::Arg | ExprKind::Call(..) | ExprKind::Seq(..) | ExprKind::Const(_) => ExprKind::Arg | ExprKind::Call(..) | ExprKind::Seq(..) | ExprKind::Const(_) => {
panic!("This should not appear outside function bodies"), panic!("This should not appear outside function bodies")
},
ExprKind::Missing => panic!("Should have been replaced"), ExprKind::Missing => panic!("Should have been replaced"),
ExprKind::Atom(a) => { ExprKind::Atom(a) => {
let x_norm = self.unpack_ident(&x).await; let x_norm = self.unpack_ident(&x).await;

View File

@@ -155,8 +155,9 @@ async fn print_exprkind<'a>(
) -> FmtUnit { ) -> FmtUnit {
match &ek { match &ek {
ExprKind::Arg => "Arg".to_string().into(), ExprKind::Arg => "Arg".to_string().into(),
ExprKind::Missing => ExprKind::Missing => {
panic!("This variant is swapped into write guards, so a read can never see it"), panic!("This variant is swapped into write guards, so a read can never see it")
},
ExprKind::Atom(a) => a.print(c).await, ExprKind::Atom(a) => a.print(c).await,
ExprKind::Bottom(e) if e.len() == 1 => format!("Bottom({e})").into(), ExprKind::Bottom(e) if e.len() == 1 => format!("Bottom({e})").into(),
ExprKind::Bottom(e) => format!("Bottom(\n\t{}\n)", indent(&e.to_string())).into(), ExprKind::Bottom(e) => format!("Bottom(\n\t{}\n)", indent(&e.to_string())).into(),

View File

@@ -211,12 +211,13 @@ impl Extension {
.await .await
{ {
Ok(module) => module, Ok(module) => module,
Err(ChildError { kind, .. }) => Err(ChildError { kind, .. }) => {
break 'reply Err(match kind { break 'reply Err(match kind {
ChildErrorKind::Private => panic!("Access checking was disabled"), ChildErrorKind::Private => panic!("Access checking was disabled"),
ChildErrorKind::Constant => api::LsModuleError::IsConstant, ChildErrorKind::Constant => api::LsModuleError::IsConstant,
ChildErrorKind::Missing => api::LsModuleError::InvalidPath, ChildErrorKind::Missing => api::LsModuleError::InvalidPath,
}), });
},
}; };
let mut members = std::collections::HashMap::new(); let mut members = std::collections::HashMap::new();
for (k, v) in &module.members { for (k, v) in &module.members {

View File

@@ -158,14 +158,15 @@ impl System {
} }
match cmod.imports.get(selector) { match cmod.imports.get(selector) {
Some(Ok(dest)) => return Ok(dest.target.to_vname().suffix(tail.iter().cloned())), Some(Ok(dest)) => return Ok(dest.target.to_vname().suffix(tail.iter().cloned())),
Some(Err(dests)) => Some(Err(dests)) => {
return Err(mk_errv_floating( return Err(mk_errv_floating(
is("Ambiguous name").await, is("Ambiguous name").await,
format!( format!(
"{selector} could refer to {}", "{selector} could refer to {}",
dests.iter().map(|ri| &ri.target).display("or") dests.iter().map(|ri| &ri.target).display("or")
), ),
)), ));
},
None => (), None => (),
} }
if root_data.root.members.get(selector).is_some() { if root_data.root.members.get(selector).is_some() {

View File

@@ -3,6 +3,7 @@ mod std;
pub use std::number::num_atom::{Float, HomoArray, Int, Num}; pub use std::number::num_atom::{Float, HomoArray, Int, Num};
pub use std::option::OrcOpt; 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::reflection::sym_atom::{SymAtom, sym_expr};
pub use std::std_system::StdSystem; pub use std::std_system::StdSystem;
pub use std::string::str_atom::OrcString; pub use std::string::str_atom::OrcString;

View File

@@ -14,17 +14,36 @@ use crate::{HomoTpl, UntypedTuple};
pub async fn gen_macro_lib() -> Vec<GenMember> { pub async fn gen_macro_lib() -> Vec<GenMember> {
prefix("macros", [ prefix("macros", [
fun(true, "resolve", async |tpl: TAtom<MacTree>| resolve(own(&tpl).await).await), fun(true, "resolve", async |tpl: TAtom<MacTree>| resolve(own(&tpl).await).await),
// TODO test whether any of this worked
prefix("common", [ prefix("common", [
build_macro(None, ["..", "_"]).finish(), build_macro(None, ["..", "_"]).finish(),
build_macro(Some(1), ["+"]) build_macro(Some(1), ["+"])
.rule(mactreev!("...$" lhs 0 macros::common::+ "...$" rhs 1), [async |[lhs, rhs]| { .rule(mactreev!("...$" lhs 1 macros::common::+ "...$" rhs 0), [async |[lhs, rhs]| {
call(sym_ref(sym!(std::number::add)), [resolve(lhs).await, resolve(rhs).await]) 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(), .finish(),
build_macro(Some(2), ["*"]) build_macro(Some(2), ["*"])
.rule(mactreev!("...$" lhs 0 macros::common::* "...$" rhs 1), [async |[lhs, rhs]| { .rule(mactreev!("...$" lhs 1 macros::common::* "...$" rhs 0), [async |[lhs, rhs]| {
call(sym_ref(sym!(std::number::mul)), [resolve(lhs).await, resolve(rhs).await]) 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(), .finish(),
build_macro(None, ["comma_list", ","]) build_macro(None, ["comma_list", ","])

View File

@@ -123,12 +123,13 @@ async fn mk_scalar(pattern: &MacTree) -> OrcRes<ScalMatcher> {
PhKind::Scalar => ScalMatcher::Placeh { key: name.clone() }, PhKind::Scalar => ScalMatcher::Placeh { key: name.clone() },
}, },
MacTok::S(c, body) => ScalMatcher::S(*c, Box::new(mk_any(&body.items).boxed_local().await?)), MacTok::S(c, body) => ScalMatcher::S(*c, Box::new(mk_any(&body.items).boxed_local().await?)),
MacTok::Lambda(..) => MacTok::Lambda(..) => {
return Err(mk_errv( return Err(mk_errv(
is("Lambda in matcher").await, is("Lambda in matcher").await,
"Lambdas can't be matched for, only generated in templates", "Lambdas can't be matched for, only generated in templates",
[pattern.pos()], [pattern.pos()],
)), ));
},
MacTok::Value(_) | MacTok::Slot => panic!("Only used for templating"), MacTok::Value(_) | MacTok::Slot => panic!("Only used for templating"),
MacTok::Bottom(errv) => return Err(errv.clone()), MacTok::Bottom(errv) => return Err(errv.clone()),
}) })

View File

@@ -95,6 +95,30 @@ pub async fn gen_std_macro_lib() -> Vec<GenMember> {
.finish(), .finish(),
fun(false, "matcher_body", tuple_matcher_body), 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(),
])
]) ])
} }

View File

@@ -1,4 +1,5 @@
pub mod number; pub mod number;
pub mod ops;
pub mod option; pub mod option;
pub mod protocol; pub mod protocol;
pub mod record; pub mod record;

View File

@@ -4,14 +4,18 @@ use orchid_base::error::OrcRes;
use orchid_base::format::FmtUnit; use orchid_base::format::FmtUnit;
use orchid_base::name::Sym; use orchid_base::name::Sym;
use orchid_base::number::Numeric; 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::atom_thin::{ThinAtom, ThinVariant};
use orchid_extension::conv::TryFromExpr; use orchid_extension::conv::TryFromExpr;
use orchid_extension::expr::Expr; use orchid_extension::expr::Expr;
use orchid_extension::gen_expr::sym_ref;
use ordered_float::NotNan; use ordered_float::NotNan;
use rust_decimal::prelude::Zero; 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; use crate::std::string::to_string::ToStringMethod;
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
@@ -19,6 +23,12 @@ pub struct Int(pub i64);
impl Atomic for Int { impl Atomic for Int {
type Variant = ThinVariant; type Variant = ThinVariant;
type Data = Self; type Data = Self;
fn reg_reqs() -> MethodSetBuilder<Self> {
MethodSetBuilder::new()
.handle::<GetTagIdMethod>()
.handle::<GetImplMethod>()
.handle::<ToStringMethod>()
}
} }
impl ThinAtom for Int { impl ThinAtom for Int {
async fn print(&self) -> FmtUnit { self.0.to_string().into() } 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() 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 { impl Supports<ToStringMethod> for Int {
async fn handle(&self, _: ToStringMethod) -> <ToStringMethod as Request>::Response { async fn handle(&self, _: ToStringMethod) -> <ToStringMethod as Request>::Response {
self.0.to_string() self.0.to_string()
@@ -44,6 +73,12 @@ pub struct Float(pub NotNan<f64>);
impl Atomic for Float { impl Atomic for Float {
type Variant = ThinVariant; type Variant = ThinVariant;
type Data = Self; type Data = Self;
fn reg_reqs() -> MethodSetBuilder<Self> {
MethodSetBuilder::new()
.handle::<GetTagIdMethod>()
.handle::<GetImplMethod>()
.handle::<ToStringMethod>()
}
} }
impl ThinAtom for Float { impl ThinAtom for Float {
async fn print(&self) -> FmtUnit { self.0.to_string().into() } 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())) 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 { impl Supports<ToStringMethod> for Float {
async fn handle(&self, _: ToStringMethod) -> <ToStringMethod as Request>::Response { async fn handle(&self, _: ToStringMethod) -> <ToStringMethod as Request>::Response {
self.0.to_string() self.0.to_string()

View File

@@ -1,34 +1,60 @@
use orchid_base::error::mk_errv;
use orchid_base::interner::is;
use orchid_base::number::Numeric; use orchid_base::number::Numeric;
use orchid_extension::func_atom::get_arg;
use orchid_extension::tree::{GenMember, fun, prefix}; use orchid_extension::tree::{GenMember, fun, prefix};
use ordered_float::NotNan; use ordered_float::NotNan;
use rust_decimal::prelude::ToPrimitive;
use super::num_atom::{Float, HomoArray, Int, Num}; use super::num_atom::{Float, HomoArray, Int, Num};
pub fn gen_num_lib() -> Vec<GenMember> { pub fn gen_num_lib() -> Vec<GenMember> {
prefix("std::number", [ 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]) { Num(match HomoArray::new([a.0, b.0]) {
HomoArray::Int([a, b]) => Numeric::Int(a + b), HomoArray::Int([a, b]) => Numeric::Int(a + b),
HomoArray::Float([a, b]) => Numeric::Float(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 { Num(match a.0 {
Numeric::Int(i) => Numeric::Int(-i), Numeric::Int(i) => Numeric::Int(-i),
Numeric::Float(f) => Numeric::Float(-f), 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]) { Num(match HomoArray::new([a.0, b.0]) {
HomoArray::Int([a, b]) => Numeric::Int(a * b), HomoArray::Int([a, b]) => Numeric::Int(a * b),
HomoArray::Float([a, b]) => Numeric::Float(a * b), HomoArray::Float([a, b]) => Numeric::Float(a * b),
}) })
}), }),
fun(true, "idiv", async |a: Int, b: Int| Int(a.0 / b.0)), fun(false, "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(false, "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(false, "fdiv", async |a: Float, b: Float| Float(a.0 / b.0)),
fun(true, "fmod", async |a: Float, b: Float| { fun(false, "fmod", async |a: Float, b: Float| {
Float(a.0 - NotNan::new((a.0 / b.0).trunc()).unwrap() * b.0) 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())),
]) ])
} }

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

View File

@@ -18,17 +18,19 @@ pub async fn parse_impls(
) -> OrcRes<()> { ) -> OrcRes<()> {
let body = match &body_tt.tok { let body = match &body_tt.tok {
Token::S(Paren::Round, body) => line_items(Snippet::new(body_tt, body)).await, Token::S(Paren::Round, body) => line_items(Snippet::new(body_tt, body)).await,
Token::S(ptyp, _) => Token::S(ptyp, _) => {
return Err(mk_errv( return Err(mk_errv(
is("Incorrect paren type").await, is("Incorrect paren type").await,
format!("Expected () block, found {ptyp}"), format!("Expected () block, found {ptyp}"),
[body_tt.sr().pos()], [body_tt.sr().pos()],
)), ));
_ => },
_ => {
return Err( return Err(
token_errv(body_tt, "Expected body", |s| format!("Expected (impl ...) block, found {s}")) token_errv(body_tt, "Expected body", |s| format!("Expected (impl ...) block, found {s}"))
.await, .await,
), );
},
}; };
for Parsed { tail: line, output: comments } in body { for Parsed { tail: line, output: comments } in body {
if let Ok(Parsed { tail, .. }) = expect_tok(line, is("impl").await).await { 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(None) => panic!("multiname is always at least one name"),
Ok(Some(ref n @ Import { name: Some(_), ref sr, .. })) => Ok(Some(ref n @ Import { name: Some(_), ref sr, .. })) =>
(n.clone().mspath().to_sym().await, sr.clone()), (n.clone().mspath().to_sym().await, sr.clone()),
Ok(Some(Import { name: None, sr, .. })) => Ok(Some(Import { name: None, sr, .. })) => {
return Err(mk_errv( return Err(mk_errv(
is("impl line with globstar").await, is("impl line with globstar").await,
"::* is not permitted in a protocol impl", "::* is not permitted in a protocol impl",
[sr.pos()], [sr.pos()],
)), ));
Err(e) => },
Err(e) => {
return Err(mk_errv( return Err(mk_errv(
is("Impl line with multiple protocol names").await, is("Impl line with multiple protocol names").await,
"::() is not permitted in a protocol impl", "::() is not permitted in a protocol impl",
e.map(|i| i.sr.pos()), e.map(|i| i.sr.pos()),
)), ));
},
}; };
let Parsed { tail, .. } = expect_tok(tail, is("as").await).await?; let Parsed { tail, .. } = expect_tok(tail, is("as").await).await?;
let cnst_name = is(&format!("{}{}", lines.len(), name.iter().join("__"))).await; let cnst_name = is(&format!("{}{}", lines.len(), name.iter().join("__"))).await;

View File

@@ -1,22 +1,29 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use futures::FutureExt;
use futures::future::{LocalBoxFuture, join_all};
use hashbrown::HashMap; use hashbrown::HashMap;
use itertools::Itertools;
use never::Never; use never::Never;
use orchid_api_derive::Coding; use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request; use orchid_api_traits::Request;
use orchid_base::error::{OrcRes, mk_errv}; use orchid_base::error::{OrcRes, mk_errv};
use orchid_base::format::fmt; use orchid_base::format::fmt;
use orchid_base::interner::is; use orchid_base::interner::{ev, is};
use orchid_base::name::Sym; use orchid_base::name::{NameLike, Sym, VName};
use orchid_extension::atom::{AtomMethod, Atomic, ForeignAtom, MethodSetBuilder, Supports, TAtom}; use orchid_extension::atom::{AtomMethod, Atomic, ForeignAtom, MethodSetBuilder, Supports, TAtom};
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own}; use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
use orchid_extension::conv::ToExpr; use orchid_extension::conv::{ClonableToExprDyn, ToExpr};
use orchid_extension::expr::Expr; use orchid_extension::coroutine_exec::exec;
use orchid_extension::gen_expr::call; use orchid_extension::expr::{Expr, ExprHandle};
use orchid_extension::tree::{GenMember, fun, prefix}; 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)] #[derive(Clone, Debug)]
pub struct Tag { pub struct Tag {
@@ -26,17 +33,25 @@ pub struct Tag {
impl Atomic for Tag { impl Atomic for Tag {
type Data = api::TStrv; type Data = api::TStrv;
type Variant = OwnedVariant; 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 { impl OwnedAtom for Tag {
type Refs = Never; type Refs = Never;
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.id.to_api()) } 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 { impl Supports<GetImplMethod> for Tag {
async fn handle(&self, req: GetImplMethod) -> <GetImplMethod as Request>::Response { async fn handle(&self, req: GetImplMethod) -> <GetImplMethod as Request>::Response {
self.impls.get(&Sym::from_api(req.0).await).map(|expr| expr.handle().ticket()) self.impls.get(&Sym::from_api(req.0).await).map(|expr| expr.handle().ticket())
} }
} }
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
pub struct GetImplMethod(pub api::TStrv); pub struct GetImplMethod(pub api::TStrv);
impl Request for GetImplMethod { impl Request for GetImplMethod {
@@ -75,14 +90,16 @@ impl Supports<GetImplMethod> for Tagged {
pub async fn get_impl(receiver: ForeignAtom, proto: ForeignAtom) -> OrcRes<Expr> { pub async fn get_impl(receiver: ForeignAtom, proto: ForeignAtom) -> OrcRes<Expr> {
let Some(proto_id) = proto.request(GetTagIdMethod).await else { 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", [ return Err(mk_errv(
proto.pos() 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 { let Some(impl_val_opt) = receiver.request(GetImplMethod(proto_id)).await else {
return Err(mk_errv( return Err(mk_errv(
is("Receiver not tagged").await, 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()], [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 { let Some(type_id) = receiver.request(GetTagIdMethod).await else {
return Err(mk_errv( return Err(mk_errv(
is("Incorrect protocols implementation in extension").await, 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()], [receiver.pos()],
)); ));
}; };
let Some(impl_val_opt) = proto.request(GetImplMethod(type_id)).await else { let Some(impl_val_opt) = proto.request(GetImplMethod(type_id)).await else {
return Err(mk_errv( return Err(mk_errv(
is("Incorrect protocols implementation in extension").await, 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()], [receiver.pos()],
)); ));
}; };
@@ -108,7 +125,7 @@ pub async fn get_impl(receiver: ForeignAtom, proto: ForeignAtom) -> OrcRes<Expr>
} }
return Err(mk_errv( return Err(mk_errv(
is("Implementation not found").await, 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()], [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
}
}

View File

@@ -5,13 +5,17 @@ use std::rc::Rc;
use futures::AsyncWrite; use futures::AsyncWrite;
use futures::future::join_all; use futures::future::join_all;
use hashbrown::HashMap; use hashbrown::HashMap;
use orchid_api_traits::Encode; use orchid_api_traits::{Encode, Request};
use orchid_base::interner::{IStr, es}; 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::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
use orchid_extension::expr::Expr; use orchid_extension::expr::Expr;
use orchid_extension::gen_expr::sym_ref;
use crate::api; use crate::api;
use crate::std::protocol::types::{GetImplMethod, GetTagIdMethod};
#[derive(Clone)] #[derive(Clone)]
pub struct Record(pub Rc<HashMap<IStr, Expr>>); 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(()) } 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)
}
}

View File

@@ -18,21 +18,23 @@ use super::number::num_lib::gen_num_lib;
use super::string::str_atom::{IntStrAtom, StrAtom}; use super::string::str_atom::{IntStrAtom, StrAtom};
use super::string::str_lib::gen_str_lib; use super::string::str_lib::gen_str_lib;
use crate::std::number::num_lexer::NumLexer; 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::option::{OptAtom, gen_option_lib};
use crate::std::protocol::proto_parser::{AsProtoParser, ProtoParser}; use crate::std::protocol::proto_parser::{AsProtoParser, ProtoParser};
use crate::std::protocol::type_parser::{AsTypeParser, TypeParser}; 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_atom::Record;
use crate::std::record::record_lib::gen_record_lib; use crate::std::record::record_lib::gen_record_lib;
use crate::std::reflection::sym_atom::{CreateSymAtom, SymAtom, gen_sym_lib}; use crate::std::reflection::sym_atom::{CreateSymAtom, SymAtom, gen_sym_lib};
use crate::std::string::str_lexer::StringLexer; 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::std::tuple::{CreateTuple, Tuple, TupleBuilder, gen_tuple_lib};
use crate::{Float, Int}; use crate::{Float, Int};
#[derive(Clone, Debug, Coding, Hierarchy)] #[derive(Clone, Debug, Coding, Hierarchy)]
#[extendable] #[extendable]
#[allow(clippy::enum_variant_names, reason = "For the time being there are only ctor calls")]
pub enum StdReq { pub enum StdReq {
CreateTag(CreateTag),
CreateTuple(CreateTuple), CreateTuple(CreateTuple),
CreateSymAtom(CreateSymAtom), CreateSymAtom(CreateSymAtom),
} }
@@ -61,7 +63,6 @@ impl SystemCard for StdSystem {
Some(TupleBuilder::dynfo()), Some(TupleBuilder::dynfo()),
Some(Tag::dynfo()), Some(Tag::dynfo()),
Some(Tagged::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); let sym_atom = SymAtom(Sym::from_api(sym_tok).await);
xreq.reply(req, &sym_atom.to_expr().await.serialize().await).await.unwrap() 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] } fn lexers() -> Vec<LexerObj> { vec![&StringLexer, &NumLexer] }
@@ -90,6 +106,7 @@ impl System for StdSystem {
gen_tuple_lib(), gen_tuple_lib(),
gen_protocol_lib(), gen_protocol_lib(),
gen_sym_lib().await, gen_sym_lib().await,
gen_ops_lib(),
]) ])
} }
async fn prelude() -> Vec<Sym> { vec![sym!(std), sym!(std::tuple), sym!(std::option)] } async fn prelude() -> Vec<Sym> { vec![sym!(std), sym!(std::tuple), sym!(std::option)] }

View File

@@ -9,36 +9,34 @@ use orchid_api_traits::{Encode, Request};
use orchid_base::error::{OrcRes, mk_errv}; use orchid_base::error::{OrcRes, mk_errv};
use orchid_base::format::{FmtCtx, FmtUnit}; use orchid_base::format::{FmtCtx, FmtUnit};
use orchid_base::interner::{IStr, es, is}; 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::{AtomMethod, Atomic, MethodSetBuilder, Supports, TAtom};
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant}; use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
use orchid_extension::conv::TryFromExpr; use orchid_extension::conv::TryFromExpr;
use orchid_extension::expr::Expr; 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; use crate::std::string::to_string::ToStringMethod;
#[derive(Copy, Clone, Debug, Coding)] #[derive(Copy, Clone, Debug, Coding)]
pub struct StringGetVal; pub struct StringGetValMethod;
impl Request for StringGetVal { impl Request for StringGetValMethod {
type Response = Rc<String>; type Response = Rc<String>;
} }
impl AtomMethod for StringGetVal { impl AtomMethod for StringGetValMethod {
const NAME: &str = "std::string_get_val"; 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)] #[derive(Clone)]
pub struct StrAtom(Rc<String>); pub struct StrAtom(Rc<String>);
impl Atomic for StrAtom { impl Atomic for StrAtom {
type Variant = OwnedVariant; type Variant = OwnedVariant;
type Data = (); type Data = ();
fn reg_reqs() -> MethodSetBuilder<Self> { MethodSetBuilder::new().handle::<StringGetVal>() } fn reg_reqs() -> MethodSetBuilder<Self> {
MethodSetBuilder::new().handle::<StringGetValMethod>().handle::<ToStringMethod>()
}
} }
impl StrAtom { impl StrAtom {
pub fn new(str: Rc<String>) -> Self { Self(str) } 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)) 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)] #[derive(Debug, Clone)]
pub struct IntStrAtom(pub(crate) IStr); pub struct IntStrAtom(pub(crate) IStr);
impl Atomic for IntStrAtom { impl Atomic for IntStrAtom {
type Variant = OwnedVariant; type Variant = OwnedVariant;
type Data = orchid_api::TStr; type Data = orchid_api::TStr;
fn reg_reqs() -> MethodSetBuilder<Self> {
MethodSetBuilder::new()
.handle::<GetTagIdMethod>()
.handle::<GetImplMethod>()
.handle::<ToStringMethod>()
}
} }
impl From<IStr> for IntStrAtom { impl From<IStr> for IntStrAtom {
fn from(value: IStr) -> Self { Self(value) } fn from(value: IStr) -> Self { Self(value) }
@@ -94,6 +124,22 @@ impl Supports<ToStringMethod> for IntStrAtom {
self.0.to_string() 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)] #[derive(Clone)]
pub struct OrcString { pub struct OrcString {
@@ -109,7 +155,7 @@ impl OrcString {
pub async fn get_string(&self) -> Rc<String> { pub async fn get_string(&self) -> Rc<String> {
match &self.kind { match &self.kind {
OrcStringKind::Int(tok) => es(**tok).await.rc(), OrcStringKind::Int(tok) => es(**tok).await.rc(),
OrcStringKind::Val(atom) => atom.request(StringGetVal).await, OrcStringKind::Val(atom) => atom.request(StringGetValMethod).await,
} }
} }
} }

View File

@@ -7,12 +7,12 @@ use orchid_extension::conv::ToExpr;
use orchid_extension::coroutine_exec::exec; use orchid_extension::coroutine_exec::exec;
use orchid_extension::expr::Expr; use orchid_extension::expr::Expr;
use orchid_extension::gen_expr::{call, sym_ref}; 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 super::str_atom::StrAtom;
use crate::OrcString; use crate::OrcString;
use crate::std::protocol::types::get_impl; use crate::std::protocol::types::{get_impl, proto};
use crate::std::string::to_string::{AsStrTag, ToStringMethod}; use crate::std::string::to_string::ToStringMethod;
pub fn gen_str_lib() -> Vec<GenMember> { pub fn gen_str_lib() -> Vec<GenMember> {
prefix("std::string", [ prefix("std::string", [
@@ -45,15 +45,6 @@ pub fn gen_str_lib() -> Vec<GenMember> {
.await .await
}), }),
), ),
prefix("to_string", [ proto(true, "to_string").finish(),
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
}),
]),
]) ])
} }

View File

@@ -1,30 +1,8 @@
use orchid_api_derive::Coding; use orchid_api_derive::Coding;
use orchid_api_traits::Request; use orchid_api_traits::Request;
use orchid_base::name::Sym; use orchid_extension::atom::AtomMethod;
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 }
}
/// Method version of std::string::to_string protocol for atoms
#[derive(Coding, Clone, Debug)] #[derive(Coding, Clone, Debug)]
pub struct ToStringMethod; pub struct ToStringMethod;
impl Request for ToStringMethod { impl Request for ToStringMethod {

View File

@@ -179,7 +179,7 @@ impl Spawner for SpawnerImpl {
#[tokio::main] #[tokio::main]
async fn main() -> io::Result<ExitCode> { 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 args = Args::parse();
let exit_code = Rc::new(RefCell::new(ExitCode::SUCCESS)); let exit_code = Rc::new(RefCell::new(ExitCode::SUCCESS));
let local_set = LocalSet::new(); let local_set = LocalSet::new();
@@ -424,8 +424,9 @@ async fn main() -> io::Result<ExitCode> {
xctx.set_gas(Some(10_000)); xctx.set_gas(Some(10_000));
xctx.execute().await; xctx.execute().await;
match xctx.result() { match xctx.result() {
ExecResult::Value(val) => ExecResult::Value(val) => {
println!("{}", take_first(&val.print(&FmtCtxImpl::default()).await, false)), println!("{}", take_first(&val.print(&FmtCtxImpl::default()).await, false))
},
ExecResult::Err(e) => println!("error: {e}"), ExecResult::Err(e) => println!("error: {e}"),
ExecResult::Gas(_) => println!("Ran out of gas!"), ExecResult::Gas(_) => println!("Ran out of gas!"),
} }

View File

@@ -23,6 +23,8 @@ pub struct Args {
pub enum Commands { pub enum Commands {
CheckApiRefs, CheckApiRefs,
Orcx { Orcx {
#[arg(long)]
release: bool,
#[arg(trailing_var_arg = true, num_args = 1..)] #[arg(trailing_var_arg = true, num_args = 1..)]
argv: Vec<String>, argv: Vec<String>,
}, },
@@ -41,7 +43,7 @@ fn main() -> io::Result<ExitCode> {
let args = Args::parse(); let args = Args::parse();
match &args.command { match &args.command {
Commands::CheckApiRefs => check_api_refs(&args)?, 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)?, Commands::Orcxdb { argv } => orcxdb(&args, argv)?,
} }
Ok(if EXIT_OK.load(Ordering::Relaxed) { ExitCode::SUCCESS } else { ExitCode::FAILURE }) Ok(if EXIT_OK.load(Ordering::Relaxed) { ExitCode::SUCCESS } else { ExitCode::FAILURE })

View File

@@ -4,17 +4,25 @@ use std::sync::atomic::Ordering;
use crate::{Args, EXIT_OK}; use crate::{Args, EXIT_OK};
pub fn orcx(_args: &Args, argv: &[String]) -> io::Result<()> { pub fn orcx(release: bool, _args: &Args, argv: &[String]) -> io::Result<()> {
if !Command::new("cargo").args(["build", "-p", "orchid-std", "--quiet"]).status()?.success() { 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); EXIT_OK.store(false, Ordering::Relaxed);
return Ok(()); return Ok(());
} }
if !Command::new("cargo") let mut run_cmd = Command::new("cargo");
.args(["run", "-p", "orcx", "--quiet", "--"]) run_cmd.args(["run", "-p", "orcx", "--quiet"]);
.args(argv) if release {
.status()? run_cmd.arg("--release");
.success() }
{ run_cmd.arg("--");
run_cmd.args(argv);
if !run_cmd.status()?.success() {
EXIT_OK.store(false, Ordering::Relaxed); EXIT_OK.store(false, Ordering::Relaxed);
} }
Ok(()) Ok(())