From f38193edcc83073f8b5e9902ae3969b8227921cc Mon Sep 17 00:00:00 2001 From: Lawrence Bethlenfalvy Date: Wed, 21 Jan 2026 22:22:58 +0100 Subject: [PATCH] Protocols and operators mostly --- README.md | 2 +- examples/hello-world/main.orc | 9 +- orchid-base/src/future_debug.rs | 13 ++ orchid-base/src/name.rs | 5 + orchid-base/src/reqnot.rs | 38 ++++- orchid-extension/src/atom.rs | 11 +- orchid-extension/src/expr.rs | 1 + orchid-extension/src/func_atom.rs | 13 ++ orchid-extension/src/parser.rs | 10 +- orchid-extension/src/reflection.rs | 15 +- orchid-extension/src/tree.rs | 17 ++- orchid-host/src/execute.rs | 5 +- orchid-host/src/expr.rs | 5 +- orchid-host/src/extension.rs | 5 +- orchid-host/src/system.rs | 5 +- orchid-std/src/lib.rs | 1 + orchid-std/src/macros/macro_lib.rs | 29 +++- orchid-std/src/macros/rule/build.rs | 5 +- orchid-std/src/macros/std_macros.rs | 24 ++++ orchid-std/src/std/mod.rs | 1 + orchid-std/src/std/number/num_atom.rs | 63 ++++++++- orchid-std/src/std/number/num_lib.rs | 40 +++++- orchid-std/src/std/ops/mod.rs | 36 +++++ orchid-std/src/std/protocol/parse_impls.rs | 20 +-- orchid-std/src/std/protocol/types.rs | 153 ++++++++++++++++++--- orchid-std/src/std/record/record_atom.rs | 26 +++- orchid-std/src/std/std_system.rs | 23 +++- orchid-std/src/std/string/str_atom.rs | 72 ++++++++-- orchid-std/src/std/string/str_lib.rs | 17 +-- orchid-std/src/std/string/to_string.rs | 26 +--- orcx/src/main.rs | 7 +- xtask/src/main.rs | 4 +- xtask/src/orcx.rs | 24 ++-- 33 files changed, 578 insertions(+), 147 deletions(-) create mode 100644 orchid-std/src/std/ops/mod.rs diff --git a/README.md b/README.md index 5802962..947fd70 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/examples/hello-world/main.orc b/examples/hello-world/main.orc index 28343f6..7bc2e5b 100644 --- a/examples/hello-world/main.orc +++ b/examples/hello-world/main.orc @@ -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 = diff --git a/orchid-base/src/future_debug.rs b/orchid-base/src/future_debug.rs index a5f27f9..a4d9d08 100644 --- a/orchid-base/src/future_debug.rs +++ b/orchid-base/src/future_debug.rs @@ -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(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) } +} diff --git a/orchid-base/src/name.rs b/orchid-base/src/name.rs index 5951c5f..06bf6dd 100644 --- a/orchid-base/src/name.rs +++ b/orchid-base/src/name.rs @@ -202,6 +202,11 @@ impl Sym { pub async fn parse(s: &str) -> Result { 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 { if t.is_empty() { Err(EmptyNameError) } else { Ok(Self(t)) } diff --git a/orchid-base/src/reqnot.rs b/orchid-base/src/reqnot.rs index 97e3b3d..4286abb 100644 --- a/orchid-base/src/reqnot.rs +++ b/orchid-base/src/reqnot.rs @@ -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::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) + drop_g.defuse(); + Ok(Box::new(IoNotifWriter { o, drop_g: assert_no_drop("Notif writer dropped") }) + as Box) }) } fn start_request(&self) -> LocalBoxFuture<'_, io::Result + '_>>> { @@ -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) + drop_g.defuse(); + Ok(Box::new(IoReqWriter { + reply, + w, + drop_g: assert_no_drop("Request reader dropped without reply"), + }) as Box) }) } } @@ -264,36 +274,49 @@ impl Client for IoClient { struct IoReqWriter { reply: oneshot::Receiver>, w: IoGuard, + 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) -> LocalBoxFuture<'a, io::Result + '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) + drop_g.defuse(); + Ok(Box::new(IoRepReader { + i, + drop_g: assert_no_drop("Reply reader dropped without finishing"), + }) as Box) }) } } struct IoRepReader { i: IoGuard, + 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) -> LocalBoxFuture<'static, ()> { Box::pin(async {}) } + fn finish(self: Box) -> LocalBoxFuture<'static, ()> { + Box::pin(async { self.drop_g.defuse() }) + } } #[derive(destructure)] struct IoNotifWriter { o: IoGuard, + 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) -> 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; diff --git a/orchid-extension/src/atom.rs b/orchid-extension/src/atom.rs index 0225638..92ff882 100644 --- a/orchid-extension/src/atom.rs +++ b/orchid-extension/src/atom.rs @@ -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; diff --git a/orchid-extension/src/expr.rs b/orchid-extension/src/expr.rs index 4d497e6..472da2a 100644 --- a/orchid-extension/src/expr.rs +++ b/orchid-extension/src/expr.rs @@ -136,6 +136,7 @@ impl Expr { _ => Err(self), } } + pub async fn pos(&self) -> Pos { self.data().await.pos.clone() } pub fn handle(&self) -> Rc { self.handle.clone() } pub fn slot(&self) -> GExpr { diff --git a/orchid-extension/src/func_atom.rs b/orchid-extension/src/func_atom.rs index ba0e639..e504cda 100644 --- a/orchid-extension/src/func_atom.rs +++ b/orchid-extension/src/func_atom.rs @@ -31,6 +31,19 @@ trait_set! { trait FunCB = Fn(Vec) -> LocalBoxFuture<'static, OrcRes> + 'static; } +task_local! { + static ARGV: Vec; +} + +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: Clone + 'static { fn argtyps() -> &'static [TypeId]; fn apply<'a>(&self, hand: ExecHandle<'a>, v: Vec) -> impl Future>; diff --git a/orchid-extension/src/parser.rs b/orchid-extension/src/parser.rs index 39a9a60..fbefe52 100644 --- a/orchid-extension/src/parser.rs +++ b/orchid-extension/src/parser.rs @@ -184,12 +184,12 @@ impl ConstCtx { &'b self, names: impl IntoIterator + 'b, ) -> impl Stream> + '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), diff --git a/orchid-extension/src/reflection.rs b/orchid-extension/src/reflection.rs index cf28a4d..27dbdab 100644 --- a/orchid-extension/src/reflection.rs +++ b/orchid-extension/src/reflection.rs @@ -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(()) => (), } diff --git a/orchid-extension/src/tree.rs b/orchid-extension/src/tree.rs index c9147e0..5f33605 100644 --- a/orchid-extension/src/tree.rs +++ b/orchid-extension/src/tree.rs @@ -71,8 +71,7 @@ pub fn module( vec![GenMember { name, kind, public, comments: vec![] }] } pub fn root_mod(name: &str, mems: impl IntoIterator>) -> (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(public: bool, name: &str, xf: impl ExprFunc) -> Vec { let fac = @@ -113,12 +112,12 @@ pub fn merge_trivial(trees: impl IntoIterator>) -> Vec 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 }, + Mod(Vec), Lazy(LazyMemberFactory), } impl MemKind { + pub async fn cnst(val: impl ToExpr) -> Self { Self::Const(val.to_gen().await) } + pub fn module(mems: impl IntoIterator>) -> 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 diff --git a/orchid-host/src/execute.rs b/orchid-host/src/execute.rs index 4276f04..356cf28 100644 --- a/orchid-host/src/execute.rs +++ b/orchid-host/src/execute.rs @@ -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; diff --git a/orchid-host/src/expr.rs b/orchid-host/src/expr.rs index 264eece..ae3ac6c 100644 --- a/orchid-host/src/expr.rs +++ b/orchid-host/src/expr.rs @@ -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(), diff --git a/orchid-host/src/extension.rs b/orchid-host/src/extension.rs index 5e30d77..85615a0 100644 --- a/orchid-host/src/extension.rs +++ b/orchid-host/src/extension.rs @@ -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 { diff --git a/orchid-host/src/system.rs b/orchid-host/src/system.rs index 29c28ab..39e5579 100644 --- a/orchid-host/src/system.rs +++ b/orchid-host/src/system.rs @@ -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() { diff --git a/orchid-std/src/lib.rs b/orchid-std/src/lib.rs index 4941a0d..77446dd 100644 --- a/orchid-std/src/lib.rs +++ b/orchid-std/src/lib.rs @@ -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; diff --git a/orchid-std/src/macros/macro_lib.rs b/orchid-std/src/macros/macro_lib.rs index 3c7a984..6675a43 100644 --- a/orchid-std/src/macros/macro_lib.rs +++ b/orchid-std/src/macros/macro_lib.rs @@ -14,17 +14,36 @@ use crate::{HomoTpl, UntypedTuple}; pub async fn gen_macro_lib() -> Vec { prefix("macros", [ fun(true, "resolve", async |tpl: TAtom| 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", ","]) diff --git a/orchid-std/src/macros/rule/build.rs b/orchid-std/src/macros/rule/build.rs index dd74e34..f3b86dc 100644 --- a/orchid-std/src/macros/rule/build.rs +++ b/orchid-std/src/macros/rule/build.rs @@ -123,12 +123,13 @@ async fn mk_scalar(pattern: &MacTree) -> OrcRes { 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()), }) diff --git a/orchid-std/src/macros/std_macros.rs b/orchid-std/src/macros/std_macros.rs index b505c39..aef34a6 100644 --- a/orchid-std/src/macros/std_macros.rs +++ b/orchid-std/src/macros/std_macros.rs @@ -95,6 +95,30 @@ pub async fn gen_std_macro_lib() -> Vec { .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::>>(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(), + ]) ]) } diff --git a/orchid-std/src/std/mod.rs b/orchid-std/src/std/mod.rs index 7087cc2..2134f37 100644 --- a/orchid-std/src/std/mod.rs +++ b/orchid-std/src/std/mod.rs @@ -1,4 +1,5 @@ pub mod number; +pub mod ops; pub mod option; pub mod protocol; pub mod record; diff --git a/orchid-std/src/std/number/num_atom.rs b/orchid-std/src/std/number/num_atom.rs index c7e1808..99c47fb 100644 --- a/orchid-std/src/std/number/num_atom.rs +++ b/orchid-std/src/std/number/num_atom.rs @@ -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 { + MethodSetBuilder::new() + .handle::() + .handle::() + .handle::() + } } impl ThinAtom for Int { async fn print(&self) -> FmtUnit { self.0.to_string().into() } @@ -33,6 +43,25 @@ impl Supports for Int { Sym::parse("std::number::Int").await.unwrap().to_api() } } +impl Supports for Int { + async fn handle(&self, req: GetImplMethod) -> ::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 for Int { async fn handle(&self, _: ToStringMethod) -> ::Response { self.0.to_string() @@ -44,6 +73,12 @@ pub struct Float(pub NotNan); impl Atomic for Float { type Variant = ThinVariant; type Data = Self; + fn reg_reqs() -> MethodSetBuilder { + MethodSetBuilder::new() + .handle::() + .handle::() + .handle::() + } } 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 for Float { + async fn handle(&self, _: GetTagIdMethod) -> ::Response { + Sym::parse("std::number::Float").await.unwrap().to_api() + } +} +impl Supports for Float { + async fn handle(&self, req: GetImplMethod) -> ::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 for Float { async fn handle(&self, _: ToStringMethod) -> ::Response { self.0.to_string() diff --git a/orchid-std/src/std/number/num_lib.rs b/orchid-std/src/std/number/num_lib.rs index 5df711e..7701684 100644 --- a/orchid-std/src/std/number/num_lib.rs +++ b/orchid-std/src/std/number/num_lib.rs @@ -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 { 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())), ]) } diff --git a/orchid-std/src/std/ops/mod.rs b/orchid-std/src/std/ops/mod.rs new file mode 100644 index 0000000..cd2f36b --- /dev/null +++ b/orchid-std/src/std/ops/mod.rs @@ -0,0 +1,36 @@ +use orchid_extension::tree::{GenMember, comments, prefix}; + +use crate::proto; + +pub fn gen_ops_lib() -> Vec { + 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(), + ), + ]) +} diff --git a/orchid-std/src/std/protocol/parse_impls.rs b/orchid-std/src/std/protocol/parse_impls.rs index 9dbaa55..aeb0779 100644 --- a/orchid-std/src/std/protocol/parse_impls.rs +++ b/orchid-std/src/std/protocol/parse_impls.rs @@ -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; diff --git a/orchid-std/src/std/protocol/types.rs b/orchid-std/src/std/protocol/types.rs index 138d271..28466ec 100644 --- a/orchid-std/src/std/protocol/types.rs +++ b/orchid-std/src/std/protocol/types.rs @@ -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 { MethodSetBuilder::new().handle::() } + fn reg_reqs() -> MethodSetBuilder { + MethodSetBuilder::new().handle::().handle::() + } } impl OwnedAtom for Tag { type Refs = Never; async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.id.to_api()) } } +impl Supports for Tag { + async fn handle(&self, _: GetTagIdMethod) -> ::Response { + self.id.to_api() + } +} impl Supports for Tag { async fn handle(&self, req: GetImplMethod) -> ::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 for Tagged { pub async fn get_impl(receiver: ForeignAtom, proto: ForeignAtom) -> OrcRes { 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 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 } 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 { }), ]) } + +#[derive(Debug, Clone, Coding, Hierarchy)] +#[extends(StdReq)] +pub struct CreateTag { + pub name: api::TStrv, + pub impls: std::collections::HashMap, +} +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>, +} +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 { + let tk = dep_req::(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>, + body: Vec, +} +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) { + self.body.extend(members); + } + pub fn with_body(mut self, members: impl IntoIterator) -> Self { + self.add_body(members); + self + } + pub fn finish(self) -> Vec { + 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 + } +} diff --git a/orchid-std/src/std/record/record_atom.rs b/orchid-std/src/std/record/record_atom.rs index 16109ef..038a38a 100644 --- a/orchid-std/src/std/record/record_atom.rs +++ b/orchid-std/src/std/record/record_atom.rs @@ -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>); @@ -35,3 +39,21 @@ impl OwnedAtom for Record { async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) } } +impl Supports for Record { + async fn handle(&self, _: GetTagIdMethod) -> ::Response { + Sym::literal("std::record::Record").await.to_api() + } +} +impl Supports for Record { + async fn handle(&self, req: GetImplMethod) -> ::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) + } +} diff --git a/orchid-std/src/std/std_system.rs b/orchid-std/src/std/std_system.rs index 5cbe2a8..f59aaf2 100644 --- a/orchid-std/src/std/std_system.rs +++ b/orchid-std/src/std/std_system.rs @@ -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 { 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 { vec![sym!(std), sym!(std::tuple), sym!(std::option)] } diff --git a/orchid-std/src/std/string/str_atom.rs b/orchid-std/src/std/string/str_atom.rs index 8fdd3a0..4c8b375 100644 --- a/orchid-std/src/std/string/str_atom.rs +++ b/orchid-std/src/std/string/str_atom.rs @@ -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; } -impl AtomMethod for StringGetVal { +impl AtomMethod for StringGetValMethod { const NAME: &str = "std::string_get_val"; } -impl Supports for StrAtom { - async fn handle(&self, _: StringGetVal) -> ::Response { self.0.clone() } -} -impl Supports for StrAtom { - async fn handle(&self, _: ToStringMethod) -> ::Response { - self.0.as_str().to_string() - } -} #[derive(Clone)] pub struct StrAtom(Rc); impl Atomic for StrAtom { type Variant = OwnedVariant; type Data = (); - fn reg_reqs() -> MethodSetBuilder { MethodSetBuilder::new().handle::() } + fn reg_reqs() -> MethodSetBuilder { + MethodSetBuilder::new().handle::().handle::() + } } impl StrAtom { pub fn new(str: Rc) -> Self { Self(str) } @@ -60,12 +58,44 @@ impl OwnedAtom for StrAtom { Self::new(Rc::new(ctx.read::().await)) } } +impl Supports for StrAtom { + async fn handle(&self, _: StringGetValMethod) -> ::Response { + self.0.clone() + } +} +impl Supports for StrAtom { + async fn handle(&self, _: ToStringMethod) -> ::Response { + self.0.as_str().to_string() + } +} +impl Supports for StrAtom { + async fn handle(&self, _: GetTagIdMethod) -> ::Response { + Sym::literal("std::string::StrAtom").await.to_api() + } +} +impl Supports for StrAtom { + async fn handle(&self, req: GetImplMethod) -> ::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 { + MethodSetBuilder::new() + .handle::() + .handle::() + .handle::() + } } impl From for IntStrAtom { fn from(value: IStr) -> Self { Self(value) } @@ -94,6 +124,22 @@ impl Supports for IntStrAtom { self.0.to_string() } } +impl Supports for IntStrAtom { + async fn handle(&self, _: GetTagIdMethod) -> ::Response { + Sym::literal("std::string::IntStrAtom").await.to_api() + } +} +impl Supports for IntStrAtom { + async fn handle(&self, req: GetImplMethod) -> ::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 { match &self.kind { OrcStringKind::Int(tok) => es(**tok).await.rc(), - OrcStringKind::Val(atom) => atom.request(StringGetVal).await, + OrcStringKind::Val(atom) => atom.request(StringGetValMethod).await, } } } diff --git a/orchid-std/src/std/string/str_lib.rs b/orchid-std/src/std/string/str_lib.rs index 8c7b321..f1f5b58 100644 --- a/orchid-std/src/std/string/str_lib.rs +++ b/orchid-std/src/std/string/str_lib.rs @@ -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 { prefix("std::string", [ @@ -45,15 +45,6 @@ pub fn gen_str_lib() -> Vec { .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(), ]) } diff --git a/orchid-std/src/std/string/to_string.rs b/orchid-std/src/std/string/to_string.rs index 023cc35..ca2689b 100644 --- a/orchid-std/src/std/string/to_string.rs +++ b/orchid-std/src/std/string/to_string.rs @@ -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 { - MethodSetBuilder::new().handle::().handle::() - } -} -impl ThinAtom for AsStrTag {} -impl Supports for AsStrTag { - async fn handle(&self, _: GetTagIdMethod) -> ::Response { - Sym::parse("std::string::to_string").await.unwrap().to_api() - } -} -impl Supports for AsStrTag { - async fn handle(&self, _: GetImplMethod) -> ::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 { diff --git a/orcx/src/main.rs b/orcx/src/main.rs index 4cbba17..d434548 100644 --- a/orcx/src/main.rs +++ b/orcx/src/main.rs @@ -179,7 +179,7 @@ impl Spawner for SpawnerImpl { #[tokio::main] async fn main() -> io::Result { - 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 { 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!"), } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index c15e7d0..d497eff 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -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, }, @@ -41,7 +43,7 @@ fn main() -> io::Result { 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 }) diff --git a/xtask/src/orcx.rs b/xtask/src/orcx.rs index f0d7d5b..aaac911 100644 --- a/xtask/src/orcx.rs +++ b/xtask/src/orcx.rs @@ -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(())