Compare commits
11 Commits
48942b3b2c
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| cdcca694c5 | |||
| 534f08b45c | |||
| 66e5a71032 | |||
| c461f82de1 | |||
| b9f1bb74d7 | |||
| f38193edcc | |||
| 75b05a2965 | |||
| 9a02c1b3ff | |||
| 4cce216e4e | |||
| 237b40ed2e | |||
| cb111a8d7b |
@@ -1,11 +1,12 @@
|
||||
[alias]
|
||||
xtask = "run --quiet --package xtask --"
|
||||
orcx = "xtask orcx"
|
||||
orcxdb = "xtask orcxdb"
|
||||
orcx = "xtask orcx --"
|
||||
orcxdb = "xtask orcxdb --"
|
||||
|
||||
[env]
|
||||
CARGO_WORKSPACE_DIR = { value = "", relative = true }
|
||||
ORCHID_EXTENSIONS = "target/debug/orchid_std"
|
||||
#ORCHID_EXTENSIONS = "target/debug/orchid-std-piped"
|
||||
ORCHID_DEFAULT_SYSTEMS = "orchid::std;orchid::macros"
|
||||
ORCHID_LOG_BUFFERS = "true"
|
||||
RUST_BACKTRACE = "1"
|
||||
|
||||
@@ -2,17 +2,15 @@ name: Rust
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: ${{ false }} # <- This make sure the workflow is skipped without any alert
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -24,6 +22,8 @@ jobs:
|
||||
- name: Run tests
|
||||
run: $HOME/.cargo/bin/cargo test --verbose
|
||||
- name: Clippy
|
||||
run: cargo clippy
|
||||
run: $HOME/.cargo/bin/cargo clippy
|
||||
- name: Formatting
|
||||
run: cargo fmt +nightly --check
|
||||
run: $HOME/.cargo/bin/cargo +nightly fmt --check
|
||||
- name: No unqualified imports from orchid_api
|
||||
run: $HOME/.cargo/bin/cargo xtask check-api-refs
|
||||
13
.github/FUNDING.yml
vendored
13
.github/FUNDING.yml
vendored
@@ -1,13 +0,0 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: lbfalvy
|
||||
patreon: lbfalvy
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: lbfalvy
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
9
Cargo.lock
generated
9
Cargo.lock
generated
@@ -948,10 +948,10 @@ dependencies = [
|
||||
"orchid-api",
|
||||
"orchid-api-traits",
|
||||
"orchid-base",
|
||||
"orchid-extension",
|
||||
"ordered-float",
|
||||
"pastey",
|
||||
"substack",
|
||||
"test_executors",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"trait-set",
|
||||
@@ -981,6 +981,7 @@ dependencies = [
|
||||
"substack",
|
||||
"test_executors",
|
||||
"tokio",
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1708,6 +1709,12 @@ version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -22,7 +22,7 @@ Namespaces are inspired by Rust modules and ES6. Every file and directory is imp
|
||||
The project uses both the stable and nightly rust toolchain. Run the examples with
|
||||
|
||||
```sh
|
||||
cargo orcx -- exec --proj ./examples/hello-world "src::main::main"
|
||||
cargo orcx --release exec --proj ./examples/hello-world "src::main::main"
|
||||
```
|
||||
|
||||
you can try modifying the examples, but error reporting for the time being is pretty terrible.
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
let my_tuple = option::some t[1, 2]
|
||||
let user = r[ "foo" 1, "bar" t[3, 4] ]
|
||||
let _main = user.bar.1
|
||||
|
||||
let main = match my_tuple {
|
||||
option::some t[ref head, ..] => head;
|
||||
option::none => "foo";
|
||||
}
|
||||
let main = "foo" + string::slice "hello" 1 3 + "bar"
|
||||
|
||||
@@ -28,7 +28,7 @@ impl Request for LexExpr {
|
||||
#[derive(Clone, Debug, Coding)]
|
||||
pub struct LexedExpr {
|
||||
pub pos: u32,
|
||||
pub expr: TokenTree,
|
||||
pub expr: Vec<TokenTree>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||
|
||||
@@ -12,7 +12,10 @@ async-once-cell = "0.5.4"
|
||||
bound = "0.6.0"
|
||||
derive_destructure = "1.0.0"
|
||||
dyn-clone = "1.0.20"
|
||||
futures = { version = "0.3.31", features = ["std"], default-features = false }
|
||||
futures = { version = "0.3.31", default-features = false, features = [
|
||||
"std",
|
||||
"async-await",
|
||||
] }
|
||||
hashbrown = "0.16.1"
|
||||
itertools = "0.14.0"
|
||||
lazy_static = "1.5.0"
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::task::{Context, Poll, Wake, Waker};
|
||||
use std::thread::panicking;
|
||||
|
||||
use futures::Stream;
|
||||
use itertools::Itertools;
|
||||
@@ -155,3 +156,15 @@ pub fn spin_on<Fut: Future>(f: Fut) -> Fut::Output {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an object that will panic if dropped. [PanicOnDrop::defuse] must be
|
||||
/// called once the particular constraint preventing a drop has passed
|
||||
pub fn assert_no_drop(msg: &'static str) -> PanicOnDrop { PanicOnDrop(true, msg) }
|
||||
|
||||
pub struct PanicOnDrop(bool, &'static str);
|
||||
impl PanicOnDrop {
|
||||
pub fn defuse(mut self) { self.0 = false; }
|
||||
}
|
||||
impl Drop for PanicOnDrop {
|
||||
fn drop(&mut self) { assert!(panicking() || !self.0, "{}", self.1) }
|
||||
}
|
||||
|
||||
@@ -28,6 +28,6 @@ pub mod reqnot;
|
||||
pub mod sequence;
|
||||
pub mod side;
|
||||
pub mod stash;
|
||||
mod tl_cache;
|
||||
pub mod tl_cache;
|
||||
pub mod tokens;
|
||||
pub mod tree;
|
||||
|
||||
@@ -198,7 +198,8 @@ impl Sym {
|
||||
let items = v.into_iter().collect_vec();
|
||||
Self::from_tok(iv(&items).await)
|
||||
}
|
||||
/// Read a `::` separated namespaced name.
|
||||
/// Read a `::` separated namespaced name. Do not use this for statically
|
||||
/// known names, use the [sym] macro instead which is cached.
|
||||
pub async fn parse(s: &str) -> Result<Self, EmptyNameError> {
|
||||
Ok(Sym(iv(&VName::parse(s).await?.into_vec()).await))
|
||||
}
|
||||
@@ -285,19 +286,27 @@ impl NameLike for VName {}
|
||||
|
||||
/// Create a [Sym] literal.
|
||||
///
|
||||
/// Both the name and its components will be cached in a thread-local static so
|
||||
/// The name and its components will be cached in a thread-local static so
|
||||
/// that subsequent executions of the expression only incur an Arc-clone for
|
||||
/// cloning the token.
|
||||
#[macro_export]
|
||||
macro_rules! sym {
|
||||
($seg1:tt $( :: $seg:tt)*) => {
|
||||
$crate::tl_cache!(async $crate::name::Sym : {
|
||||
$crate::name::Sym::from_tok(
|
||||
$crate::interner::iv(&[
|
||||
$crate::interner::is(stringify!($seg1)).await
|
||||
$( , $crate::interner::is(stringify!($seg)).await )*
|
||||
$crate::interner::is($crate::sym!(@SEG $seg1)).await
|
||||
$( , $crate::interner::is($crate::sym!(@SEG $seg)).await )*
|
||||
])
|
||||
.await
|
||||
).unwrap()
|
||||
})
|
||||
};
|
||||
(@SEG [ $($data:tt)* ]) => {
|
||||
stringify!($($data)*)
|
||||
};
|
||||
(@SEG $data:tt) => {
|
||||
stringify!($data)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -307,10 +316,12 @@ macro_rules! sym {
|
||||
#[macro_export]
|
||||
macro_rules! vname {
|
||||
($seg1:tt $( :: $seg:tt)*) => {
|
||||
$crate::tl_cache!(async $crate::name::VName : {
|
||||
$crate::name::VName::new([
|
||||
$crate::interner::is(stringify!($seg1)).await
|
||||
$( , $crate::interner::is(stringify!($seg)).await )*
|
||||
]).unwrap()
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
@@ -320,10 +331,12 @@ macro_rules! vname {
|
||||
#[macro_export]
|
||||
macro_rules! vpath {
|
||||
($seg1:tt $( :: $seg:tt)*) => {
|
||||
$crate::tl_cache!(async $crate::name::VPath : {
|
||||
$crate::name::VPath(vec![
|
||||
$crate::interner::is(stringify!($seg1)).await
|
||||
$( , $crate::interner::is(stringify!($seg)).await )*
|
||||
])
|
||||
})
|
||||
};
|
||||
() => {
|
||||
$crate::name::VPath(vec![])
|
||||
@@ -334,30 +347,43 @@ macro_rules! vpath {
|
||||
pub mod test {
|
||||
use std::borrow::Borrow;
|
||||
|
||||
use orchid_api_traits::spin_on;
|
||||
|
||||
use super::{NameLike, Sym, VName};
|
||||
use crate::interner::{IStr, is};
|
||||
use crate::interner::local_interner::local_interner;
|
||||
use crate::interner::{IStr, is, with_interner};
|
||||
use crate::name::VPath;
|
||||
|
||||
pub async fn recur() {
|
||||
#[test]
|
||||
pub fn recur() {
|
||||
spin_on(with_interner(local_interner(), async {
|
||||
let myname = vname!(foo::bar);
|
||||
let _borrowed_slice: &[IStr] = myname.borrow();
|
||||
let _deref_pathslice: &[IStr] = &myname;
|
||||
let _as_slice_out: &[IStr] = myname.as_slice();
|
||||
}))
|
||||
}
|
||||
|
||||
/// Tests that literals are correctly interned as equal
|
||||
pub async fn literals() {
|
||||
#[test]
|
||||
pub fn literals() {
|
||||
spin_on(with_interner(local_interner(), async {
|
||||
assert_eq!(
|
||||
sym!(foo::bar::baz),
|
||||
Sym::new([is("foo").await, is("bar").await, is("baz").await]).await.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
sym!(foo::bar::[|>]),
|
||||
Sym::new([is("foo").await, is("bar").await, is("|>").await]).await.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
vname!(foo::bar::baz),
|
||||
VName::new([is("foo").await, is("bar").await, is("baz").await]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
vpath!(foo::bar::baz),
|
||||
{ vpath!(foo::bar::baz) },
|
||||
VPath::new([is("foo").await, is("bar").await, is("baz").await])
|
||||
);
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ use crate::tree::{ExprRepr, ExtraTok, Paren, TokTree, Token, ttv_fmt, ttv_range}
|
||||
|
||||
pub fn name_start(c: char) -> bool { c.is_alphabetic() || c == '_' }
|
||||
pub fn name_char(c: char) -> bool { name_start(c) || c.is_numeric() }
|
||||
pub fn op_char(c: char) -> bool { !name_char(c) && !c.is_whitespace() && !"()[]{}\\".contains(c) }
|
||||
pub fn op_char(c: char) -> bool { !name_char(c) && !unrep_space(c) && !"()[]{}\\".contains(c) }
|
||||
pub fn unrep_space(c: char) -> bool { c.is_whitespace() && !"\r\n".contains(c) }
|
||||
|
||||
/// A cheaply copiable subsection of a document that holds onto context data and
|
||||
|
||||
@@ -17,6 +17,7 @@ use futures::{
|
||||
use hashbrown::HashMap;
|
||||
use orchid_api_traits::{Decode, Encode, Request, UnderRoot};
|
||||
|
||||
use crate::future_debug::{PanicOnDrop, assert_no_drop};
|
||||
use crate::localset::LocalSet;
|
||||
|
||||
#[must_use = "Receipts indicate that a required action has been performed within a function. \
|
||||
@@ -238,9 +239,12 @@ impl IoClient {
|
||||
impl Client for IoClient {
|
||||
fn start_notif(&self) -> LocalBoxFuture<'_, io::Result<Box<dyn MsgWriter<'_> + '_>>> {
|
||||
Box::pin(async {
|
||||
let drop_g = assert_no_drop("Notif future dropped");
|
||||
let mut o = self.lock_out().await;
|
||||
0u64.encode(o.as_mut()).await?;
|
||||
Ok(Box::new(IoNotifWriter { o }) as Box<dyn MsgWriter>)
|
||||
drop_g.defuse();
|
||||
Ok(Box::new(IoNotifWriter { o, drop_g: assert_no_drop("Notif writer dropped") })
|
||||
as Box<dyn MsgWriter>)
|
||||
})
|
||||
}
|
||||
fn start_request(&self) -> LocalBoxFuture<'_, io::Result<Box<dyn ReqWriter<'_> + '_>>> {
|
||||
@@ -252,11 +256,17 @@ impl Client for IoClient {
|
||||
};
|
||||
let (cb, reply) = oneshot::channel();
|
||||
let (ack, got_ack) = oneshot::channel();
|
||||
let drop_g = assert_no_drop("Request future dropped");
|
||||
self.subscribe.as_ref().clone().send(ReplySub { id, ack, cb }).await.unwrap();
|
||||
got_ack.await.unwrap();
|
||||
let mut w = self.lock_out().await;
|
||||
id.encode(w.as_mut()).await?;
|
||||
Ok(Box::new(IoReqWriter { reply, w }) as Box<dyn ReqWriter>)
|
||||
drop_g.defuse();
|
||||
Ok(Box::new(IoReqWriter {
|
||||
reply,
|
||||
w,
|
||||
drop_g: assert_no_drop("Request reader dropped without reply"),
|
||||
}) as Box<dyn ReqWriter>)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -264,36 +274,49 @@ impl Client for IoClient {
|
||||
struct IoReqWriter {
|
||||
reply: oneshot::Receiver<IoGuard<dyn AsyncRead>>,
|
||||
w: IoGuard<dyn AsyncWrite>,
|
||||
drop_g: PanicOnDrop,
|
||||
}
|
||||
impl<'a> ReqWriter<'a> for IoReqWriter {
|
||||
fn writer(&mut self) -> Pin<&mut dyn AsyncWrite> { self.w.as_mut() }
|
||||
fn send(self: Box<Self>) -> LocalBoxFuture<'a, io::Result<Box<dyn RepReader<'a> + 'a>>> {
|
||||
Box::pin(async {
|
||||
let Self { reply, mut w } = *self;
|
||||
let Self { reply, mut w, drop_g } = *self;
|
||||
w.flush().await?;
|
||||
mem::drop(w);
|
||||
let i = reply.await.expect("Client dropped before reply received");
|
||||
Ok(Box::new(IoRepReader { i }) as Box<dyn RepReader>)
|
||||
drop_g.defuse();
|
||||
Ok(Box::new(IoRepReader {
|
||||
i,
|
||||
drop_g: assert_no_drop("Reply reader dropped without finishing"),
|
||||
}) as Box<dyn RepReader>)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct IoRepReader {
|
||||
i: IoGuard<dyn AsyncRead>,
|
||||
drop_g: PanicOnDrop,
|
||||
}
|
||||
impl<'a> RepReader<'a> for IoRepReader {
|
||||
fn reader(&mut self) -> Pin<&mut dyn AsyncRead> { self.i.as_mut() }
|
||||
fn finish(self: Box<Self>) -> LocalBoxFuture<'static, ()> { Box::pin(async {}) }
|
||||
fn finish(self: Box<Self>) -> LocalBoxFuture<'static, ()> {
|
||||
Box::pin(async { self.drop_g.defuse() })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(destructure)]
|
||||
struct IoNotifWriter {
|
||||
o: IoGuard<dyn AsyncWrite>,
|
||||
drop_g: PanicOnDrop,
|
||||
}
|
||||
impl<'a> MsgWriter<'a> for IoNotifWriter {
|
||||
fn writer(&mut self) -> Pin<&mut dyn AsyncWrite> { self.o.as_mut() }
|
||||
fn finish(mut self: Box<Self>) -> LocalBoxFuture<'static, io::Result<()>> {
|
||||
Box::pin(async move { self.o.flush().await })
|
||||
Box::pin(async move {
|
||||
self.o.flush().await?;
|
||||
self.drop_g.defuse();
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,7 +398,8 @@ impl IoCommServer {
|
||||
Ok(Event::Exit) => break,
|
||||
Ok(Event::Sub(ReplySub { id, ack, cb })) => {
|
||||
pending_replies.insert(id, cb);
|
||||
ack.send(()).unwrap();
|
||||
// this is detected and logged on client
|
||||
let _ = ack.send(());
|
||||
},
|
||||
Ok(Event::Input(0, read)) => {
|
||||
let notif = ¬if;
|
||||
|
||||
@@ -6,4 +6,18 @@ macro_rules! tl_cache {
|
||||
}
|
||||
V.with(|v| v.clone())
|
||||
}};
|
||||
(async $ty:ty : $expr:expr) => {{
|
||||
type CellType = std::cell::OnceCell<$ty>;
|
||||
thread_local! {
|
||||
static V: CellType = std::cell::OnceCell::default();
|
||||
}
|
||||
match V.with(|cell: &CellType| cell.get().cloned()) {
|
||||
Some(val) => val as $ty,
|
||||
None => {
|
||||
let val = $expr;
|
||||
let _ = V.with(|cell: &CellType| cell.set(val.clone()));
|
||||
val as $ty
|
||||
},
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
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::io;
|
||||
use std::marker::PhantomData;
|
||||
use std::num::NonZeroU32;
|
||||
use std::ops::Deref;
|
||||
use std::pin::Pin;
|
||||
@@ -9,14 +11,15 @@ use std::rc::Rc;
|
||||
|
||||
use dyn_clone::{DynClone, clone_box};
|
||||
use futures::future::LocalBoxFuture;
|
||||
use futures::{AsyncRead, AsyncWrite, FutureExt, StreamExt, stream};
|
||||
use futures::{AsyncWrite, FutureExt, StreamExt, stream};
|
||||
use orchid_api_derive::Coding;
|
||||
use orchid_api_traits::{Coding, Decode, Encode, Request, enc_vec};
|
||||
use orchid_api_traits::{Coding, Decode, InHierarchy, Request, UnderRoot, enc_vec};
|
||||
use orchid_base::error::{OrcErrv, OrcRes, mk_errv, mk_errv_floating};
|
||||
use orchid_base::format::{FmtCtx, FmtUnit, Format, fmt};
|
||||
use orchid_base::format::{FmtCtx, FmtUnit, Format, fmt, take_first};
|
||||
use orchid_base::interner::is;
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::reqnot::{Receipt, ReqHandle, ReqReader, ReqReaderExt};
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::api;
|
||||
@@ -98,14 +101,17 @@ impl ForeignAtom {
|
||||
pub(crate) fn new(handle: Rc<ExprHandle>, atom: api::Atom, pos: Pos) -> Self {
|
||||
ForeignAtom { atom, expr: handle, pos }
|
||||
}
|
||||
pub async fn request<M: AtomMethod>(&self, m: M) -> Option<M::Response> {
|
||||
pub async fn request<R: Request + UnderRoot<Root: AtomMethod>>(
|
||||
&self,
|
||||
r: R,
|
||||
) -> Option<R::Response> {
|
||||
let rep = (request(api::Fwd(
|
||||
self.atom.clone(),
|
||||
Sym::parse(M::NAME).await.unwrap().tok().to_api(),
|
||||
enc_vec(&m),
|
||||
Sym::parse(<R as UnderRoot>::Root::NAME).await.unwrap().tok().to_api(),
|
||||
enc_vec(&r.into_root()),
|
||||
)))
|
||||
.await?;
|
||||
Some(M::Response::decode_slice(&mut &rep[..]))
|
||||
Some(R::Response::decode_slice(&mut &rep[..]))
|
||||
}
|
||||
pub async fn downcast<T: AtomicFeatures>(self) -> Result<TAtom<T>, NotTypAtom> {
|
||||
TAtom::downcast(self.ex().handle()).await
|
||||
@@ -144,24 +150,50 @@ 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: Coding + InHierarchy {
|
||||
const NAME: &str;
|
||||
}
|
||||
pub trait Supports<M: AtomMethod>: AtomCard {
|
||||
fn handle(&self, req: M) -> impl Future<Output = <M as Request>::Response>;
|
||||
fn handle<'a>(
|
||||
&self,
|
||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
||||
req: M,
|
||||
) -> impl Future<Output = io::Result<Receipt<'a>>>;
|
||||
}
|
||||
|
||||
trait_set! {
|
||||
trait AtomReqCb<A> = for<'a> Fn(
|
||||
&'a A,
|
||||
Pin<&'a mut dyn AsyncRead>,
|
||||
Pin<&'a mut dyn AsyncWrite>,
|
||||
) -> LocalBoxFuture<'a, ()>
|
||||
trait HandleAtomMethod<A> {
|
||||
fn handle<'a, 'b: 'a>(
|
||||
&'a self,
|
||||
atom: &'a A,
|
||||
req: Box<dyn ReqReader<'b> + 'a>,
|
||||
) -> LocalBoxFuture<'a, ()>;
|
||||
}
|
||||
struct AtomMethodHandler<M, A>(PhantomData<M>, PhantomData<A>);
|
||||
impl<M: AtomMethod, A: Supports<M>> HandleAtomMethod<A> for AtomMethodHandler<M, A> {
|
||||
fn handle<'a, 'b: 'a>(
|
||||
&'a self,
|
||||
a: &'a A,
|
||||
mut reader: Box<dyn ReqReader<'b> + 'a>,
|
||||
) -> LocalBoxFuture<'a, ()> {
|
||||
Box::pin(async {
|
||||
let req = reader.read_req::<M>().await.unwrap();
|
||||
let _ = Supports::<M>::handle(a, reader.finish().await, req).await.unwrap();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MethodSetBuilder<A: AtomCard> {
|
||||
handlers: Vec<(&'static str, Rc<dyn AtomReqCb<A>>)>,
|
||||
handlers: Vec<(&'static str, Rc<dyn HandleAtomMethod<A>>)>,
|
||||
}
|
||||
impl<A: AtomCard> MethodSetBuilder<A> {
|
||||
pub fn new() -> Self { Self { handlers: vec![] } }
|
||||
@@ -169,15 +201,7 @@ impl<A: AtomCard> MethodSetBuilder<A> {
|
||||
pub fn handle<M: AtomMethod>(mut self) -> Self
|
||||
where A: Supports<M> {
|
||||
assert!(!M::NAME.is_empty(), "AtomMethod::NAME cannoot be empty");
|
||||
self.handlers.push((
|
||||
M::NAME,
|
||||
Rc::new(move |a: &A, req: Pin<&mut dyn AsyncRead>, rep: Pin<&mut dyn AsyncWrite>| {
|
||||
async {
|
||||
Supports::<M>::handle(a, M::decode(req).await.unwrap()).await.encode(rep).await.unwrap()
|
||||
}
|
||||
.boxed_local()
|
||||
}),
|
||||
));
|
||||
self.handlers.push((M::NAME, Rc::new(AtomMethodHandler::<M, A>(PhantomData, PhantomData))));
|
||||
self
|
||||
}
|
||||
|
||||
@@ -192,20 +216,19 @@ impl<A: AtomCard> MethodSetBuilder<A> {
|
||||
}
|
||||
|
||||
pub struct MethodSet<A: AtomCard> {
|
||||
handlers: HashMap<Sym, Rc<dyn AtomReqCb<A>>>,
|
||||
handlers: HashMap<Sym, Rc<dyn HandleAtomMethod<A>>>,
|
||||
}
|
||||
impl<A: AtomCard> MethodSet<A> {
|
||||
pub(crate) async fn dispatch<'a>(
|
||||
&'a self,
|
||||
atom: &'a A,
|
||||
&self,
|
||||
atom: &'_ A,
|
||||
key: Sym,
|
||||
req: Pin<&'a mut dyn AsyncRead>,
|
||||
rep: Pin<&'a mut dyn AsyncWrite>,
|
||||
req: Box<dyn ReqReader<'a> + 'a>,
|
||||
) -> bool {
|
||||
match self.handlers.get(&key) {
|
||||
None => false,
|
||||
Some(handler) => {
|
||||
handler(atom, req, rep).await;
|
||||
handler.handle(atom, req).await;
|
||||
true
|
||||
},
|
||||
}
|
||||
@@ -234,13 +257,13 @@ impl<A: AtomicFeatures> TAtom<A> {
|
||||
},
|
||||
}
|
||||
}
|
||||
pub async fn request<M: AtomMethod>(&self, req: M) -> M::Response
|
||||
where A: Supports<M> {
|
||||
M::Response::decode_slice(
|
||||
pub async fn request<R: Request + UnderRoot<Root: AtomMethod>>(&self, req: R) -> R::Response
|
||||
where A: Supports<<R as UnderRoot>::Root> {
|
||||
R::Response::decode_slice(
|
||||
&mut &(request(api::Fwd(
|
||||
self.untyped.atom.clone(),
|
||||
Sym::parse(M::NAME).await.unwrap().tok().to_api(),
|
||||
enc_vec(&req),
|
||||
Sym::parse(<R as UnderRoot>::Root::NAME).await.unwrap().tok().to_api(),
|
||||
enc_vec(&req.into_root()),
|
||||
)))
|
||||
.await
|
||||
.unwrap()[..],
|
||||
@@ -269,12 +292,11 @@ pub trait AtomDynfo: 'static {
|
||||
fn call<'a>(&'a self, ctx: AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr>;
|
||||
fn call_ref<'a>(&'a self, ctx: AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr>;
|
||||
fn print<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, FmtUnit>;
|
||||
fn handle_req<'a, 'b: 'a, 'c: 'a>(
|
||||
fn handle_req<'a>(
|
||||
&'a self,
|
||||
ctx: AtomCtx<'a>,
|
||||
key: Sym,
|
||||
req: Pin<&'b mut dyn AsyncRead>,
|
||||
rep: Pin<&'c mut dyn AsyncWrite>,
|
||||
req: Box<dyn ReqReader<'a> + 'a>,
|
||||
) -> LocalBoxFuture<'a, bool>;
|
||||
fn command<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, OrcRes<Option<GExpr>>>;
|
||||
fn serialize<'a, 'b: 'a>(
|
||||
@@ -311,12 +333,18 @@ impl Format for AtomFactory {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn err_not_callable() -> OrcErrv {
|
||||
mk_errv_floating(is("This atom is not callable").await, "Attempted to apply value as function")
|
||||
pub async fn err_not_callable(unit: &FmtUnit) -> OrcErrv {
|
||||
mk_errv_floating(
|
||||
is("This atom is not callable").await,
|
||||
format!("Attempted to apply {} as function", take_first(unit, false)),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn err_not_command() -> OrcErrv {
|
||||
mk_errv_floating(is("This atom is not a command").await, "Settled on an inactionable value")
|
||||
pub async fn err_not_command(unit: &FmtUnit) -> OrcErrv {
|
||||
mk_errv_floating(
|
||||
is("This atom is not a command").await,
|
||||
format!("Settled on {} which is an inactionable value", take_first(unit, false)),
|
||||
)
|
||||
}
|
||||
|
||||
/// Read the type ID prefix from an atom, return type information and the rest
|
||||
|
||||
@@ -11,14 +11,14 @@ use std::rc::Rc;
|
||||
use async_once_cell::OnceCell;
|
||||
use dyn_clone::{DynClone, clone_box};
|
||||
use futures::future::{LocalBoxFuture, ready};
|
||||
use futures::{AsyncRead, AsyncWrite, FutureExt};
|
||||
use futures::{AsyncWrite, FutureExt};
|
||||
use futures_locks::{RwLock, RwLockReadGuard};
|
||||
use itertools::Itertools;
|
||||
use memo_map::MemoMap;
|
||||
use never::Never;
|
||||
use orchid_api_traits::{Decode, Encode, enc_vec};
|
||||
use orchid_base::error::OrcRes;
|
||||
use orchid_base::format::{FmtCtx, FmtCtxImpl, FmtUnit, take_first};
|
||||
use orchid_base::format::{FmtCtx, FmtCtxImpl, FmtUnit, Format, take_first};
|
||||
use orchid_base::logging::log;
|
||||
use orchid_base::name::Sym;
|
||||
use task_local::task_local;
|
||||
@@ -93,25 +93,42 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
|
||||
})
|
||||
}
|
||||
fn call(&self, AtomCtx(_, id): AtomCtx, arg: Expr) -> LocalBoxFuture<'_, GExpr> {
|
||||
Box::pin(async move { take_atom(id.unwrap()).await.dyn_call(arg).await })
|
||||
Box::pin(async move {
|
||||
writeln!(
|
||||
log("msg"),
|
||||
"owned call {} {}",
|
||||
take_first(&AtomReadGuard::new(id.unwrap()).await.dyn_print().await, false),
|
||||
take_first(&arg.print(&FmtCtxImpl::default()).await, true),
|
||||
)
|
||||
.await;
|
||||
take_atom(id.unwrap()).await.dyn_call(arg).await
|
||||
})
|
||||
}
|
||||
fn call_ref<'a>(&'a self, AtomCtx(_, id): AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr> {
|
||||
Box::pin(async move { AtomReadGuard::new(id.unwrap()).await.dyn_call_ref(arg).await })
|
||||
Box::pin(async move {
|
||||
writeln!(
|
||||
log("msg"),
|
||||
"owned call_ref {} {}",
|
||||
take_first(&AtomReadGuard::new(id.unwrap()).await.dyn_print().await, false),
|
||||
take_first(&arg.print(&FmtCtxImpl::default()).await, true),
|
||||
)
|
||||
.await;
|
||||
AtomReadGuard::new(id.unwrap()).await.dyn_call_ref(arg).await
|
||||
})
|
||||
}
|
||||
fn print(&self, AtomCtx(_, id): AtomCtx<'_>) -> LocalBoxFuture<'_, FmtUnit> {
|
||||
Box::pin(async move { AtomReadGuard::new(id.unwrap()).await.dyn_print().await })
|
||||
}
|
||||
fn handle_req<'a, 'b: 'a, 'c: 'a>(
|
||||
fn handle_req<'a>(
|
||||
&'a self,
|
||||
AtomCtx(_, id): AtomCtx,
|
||||
AtomCtx(_, id): AtomCtx<'a>,
|
||||
key: Sym,
|
||||
req: Pin<&'b mut dyn AsyncRead>,
|
||||
rep: Pin<&'c mut dyn AsyncWrite>,
|
||||
req: Box<dyn orchid_base::reqnot::ReqReader<'a> + 'a>,
|
||||
) -> LocalBoxFuture<'a, bool> {
|
||||
Box::pin(async move {
|
||||
let a = AtomReadGuard::new(id.unwrap()).await;
|
||||
let ms = self.ms.get_or_init(self.msbuild.pack()).await;
|
||||
ms.dispatch(a.as_any_ref().downcast_ref().unwrap(), key, req, rep).await
|
||||
ms.dispatch(a.as_any_ref().downcast_ref().unwrap(), key, req).await
|
||||
})
|
||||
}
|
||||
fn command<'a>(
|
||||
@@ -210,7 +227,7 @@ pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Any + Clone + 'static {
|
||||
fn val(&self) -> impl Future<Output = Cow<'_, Self::Data>>;
|
||||
#[allow(unused_variables)]
|
||||
fn call_ref(&self, arg: Expr) -> impl Future<Output = GExpr> {
|
||||
async move { bot(err_not_callable().await) }
|
||||
async move { bot(err_not_callable(&self.dyn_print().await).await) }
|
||||
}
|
||||
fn call(self, arg: Expr) -> impl Future<Output = GExpr> {
|
||||
async {
|
||||
@@ -221,7 +238,7 @@ pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Any + Clone + 'static {
|
||||
}
|
||||
#[allow(unused_variables)]
|
||||
fn command(self) -> impl Future<Output = OrcRes<Option<GExpr>>> {
|
||||
async move { Err(err_not_command().await) }
|
||||
async move { Err(err_not_command(&self.dyn_print().await).await) }
|
||||
}
|
||||
#[allow(unused_variables)]
|
||||
fn free(self) -> impl Future<Output = ()> { async {} }
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::pin::Pin;
|
||||
|
||||
use async_once_cell::OnceCell;
|
||||
use futures::future::LocalBoxFuture;
|
||||
use futures::{AsyncRead, AsyncWrite, FutureExt};
|
||||
use futures::{AsyncWrite, FutureExt};
|
||||
use orchid_api_traits::{Coding, enc_vec};
|
||||
use orchid_base::error::OrcRes;
|
||||
use orchid_base::format::FmtUnit;
|
||||
@@ -54,16 +54,15 @@ impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
|
||||
fn call_ref<'a>(&'a self, AtomCtx(buf, ..): AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr> {
|
||||
Box::pin(async move { T::decode_slice(&mut &buf[..]).call(arg).await })
|
||||
}
|
||||
fn handle_req<'a, 'm1: 'a, 'm2: 'a>(
|
||||
fn handle_req<'a>(
|
||||
&'a self,
|
||||
AtomCtx(buf, _): AtomCtx<'a>,
|
||||
AtomCtx(buf, ..): AtomCtx<'a>,
|
||||
key: Sym,
|
||||
req: Pin<&'m1 mut dyn AsyncRead>,
|
||||
rep: Pin<&'m2 mut dyn AsyncWrite>,
|
||||
req: Box<dyn orchid_base::reqnot::ReqReader<'a> + 'a>,
|
||||
) -> LocalBoxFuture<'a, bool> {
|
||||
Box::pin(async move {
|
||||
let ms = self.ms.get_or_init(self.msbuild.pack()).await;
|
||||
ms.dispatch(&T::decode_slice(&mut &buf[..]), key, req, rep).await
|
||||
ms.dispatch(&T::decode_slice(&mut &buf[..]), key, req).await
|
||||
})
|
||||
}
|
||||
fn command<'a>(
|
||||
@@ -99,11 +98,11 @@ pub trait ThinAtom:
|
||||
{
|
||||
#[allow(unused_variables)]
|
||||
fn call(&self, arg: Expr) -> impl Future<Output = GExpr> {
|
||||
async move { bot(err_not_callable().await) }
|
||||
async move { bot(err_not_callable(&self.print().await).await) }
|
||||
}
|
||||
#[allow(unused_variables)]
|
||||
fn command(&self) -> impl Future<Output = OrcRes<Option<GExpr>>> {
|
||||
async move { Err(err_not_command().await) }
|
||||
async move { Err(err_not_command(&self.print().await).await) }
|
||||
}
|
||||
#[allow(unused_variables)]
|
||||
fn print(&self) -> impl Future<Output = FmtUnit> {
|
||||
|
||||
@@ -4,13 +4,14 @@ use std::pin::Pin;
|
||||
use dyn_clone::DynClone;
|
||||
use never::Never;
|
||||
use orchid_base::error::{OrcErrv, OrcRes, mk_errv};
|
||||
use orchid_base::format::{Format, fmt};
|
||||
use orchid_base::interner::is;
|
||||
use orchid_base::location::Pos;
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::atom::{AtomicFeatures, ForeignAtom, TAtom, ToAtom};
|
||||
use crate::expr::Expr;
|
||||
use crate::gen_expr::{GExpr, atom, bot};
|
||||
use crate::atom::{AtomicFeatures, ForeignAtom, TAtom};
|
||||
use crate::expr::{Expr, ExprKind};
|
||||
use crate::gen_expr::{GExpr, bot};
|
||||
|
||||
pub trait TryFromExpr: Sized {
|
||||
fn try_from_expr(expr: Expr) -> impl Future<Output = OrcRes<Self>>;
|
||||
@@ -26,14 +27,17 @@ impl<T: TryFromExpr, U: TryFromExpr> TryFromExpr for (T, U) {
|
||||
}
|
||||
}
|
||||
|
||||
async fn err_not_atom(pos: Pos) -> OrcErrv {
|
||||
mk_errv(is("Expected an atom").await, "This expression is not an atom", [pos])
|
||||
async fn err_not_atom(pos: Pos, value: &impl Format) -> OrcErrv {
|
||||
mk_errv(is("Expected an atom").await, format!("{} is not an atom", fmt(value).await), [pos])
|
||||
}
|
||||
|
||||
impl TryFromExpr for ForeignAtom {
|
||||
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
|
||||
match expr.atom().await {
|
||||
Err(ex) => Err(err_not_atom(ex.data().await.pos.clone()).await),
|
||||
if let ExprKind::Bottom(err) = &expr.data().await.kind {
|
||||
return Err(err.clone());
|
||||
}
|
||||
match expr.clone().atom().await {
|
||||
Err(ex) => Err(err_not_atom(ex.data().await.pos.clone(), &expr).await),
|
||||
Ok(f) => Ok(f),
|
||||
}
|
||||
}
|
||||
@@ -107,10 +111,6 @@ impl<T: ToExpr> ToExpr for OrcRes<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: ToAtom> ToExpr for A {
|
||||
async fn to_gen(self) -> GExpr { atom(self) }
|
||||
}
|
||||
|
||||
impl ToExpr for Never {
|
||||
async fn to_gen(self) -> GExpr { match self {} }
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::atom::Atomic;
|
||||
use crate::atom_owned::{OwnedAtom, OwnedVariant};
|
||||
use crate::conv::{ToExpr, TryFromExpr};
|
||||
use crate::expr::Expr;
|
||||
use crate::gen_expr::{GExpr, arg, call, lambda, seq};
|
||||
use crate::gen_expr::{GExpr, arg, call, lambda, new_atom, seq};
|
||||
|
||||
enum Command {
|
||||
Execute(GExpr, Sender<Expr>),
|
||||
@@ -34,14 +34,11 @@ impl BuilderCoroutine {
|
||||
None => panic!("Before the stream ends, we should have gotten a Halt"),
|
||||
Some(Command::Halt(expr)) => expr,
|
||||
Some(Command::Execute(expr, reply)) => call(
|
||||
lambda(0, [seq(
|
||||
[arg(0)],
|
||||
call(Replier { reply, builder: self }.to_gen().await, [arg(0)]),
|
||||
)]),
|
||||
lambda(0, [seq([arg(0)], call(new_atom(Replier { reply, builder: self }), [arg(0)]))]),
|
||||
[expr],
|
||||
),
|
||||
Some(Command::Register(expr, reply)) =>
|
||||
call(Replier { reply, builder: self }.to_gen().await, [expr]),
|
||||
call(new_atom(Replier { reply, builder: self }), [expr]),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,8 +64,8 @@ impl OwnedAtom for Replier {
|
||||
|
||||
pub async fn exec<R: ToExpr>(f: impl for<'a> AsyncFnOnce(ExecHandle<'a>) -> R + 'static) -> GExpr {
|
||||
let (cmd_snd, cmd_recv) = channel(0);
|
||||
let halt = async { Command::Halt(f(ExecHandle(cmd_snd, PhantomData)).await.to_gen().await) }
|
||||
.into_stream();
|
||||
let halt =
|
||||
async { Command::Halt(f(ExecHandle(cmd_snd, PhantomData)).await.to_gen().await) }.into_stream();
|
||||
let coro = BuilderCoroutine(Rc::new(BuilderCoroutineData {
|
||||
receiver: Mutex::new(stream::select(halt, cmd_recv).boxed_local()),
|
||||
}));
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use std::cell::RefCell;
|
||||
use std::future::Future;
|
||||
use std::mem;
|
||||
use std::num::NonZero;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::{io, mem};
|
||||
|
||||
use futures::future::{LocalBoxFuture, join_all};
|
||||
use futures::{AsyncRead, AsyncWrite, AsyncWriteExt, StreamExt, stream};
|
||||
use futures::{AsyncWriteExt, StreamExt, stream};
|
||||
use hashbrown::HashMap;
|
||||
use itertools::Itertools;
|
||||
use orchid_api_traits::{Decode, Encode, Request, UnderRoot, enc_vec};
|
||||
@@ -17,8 +17,7 @@ use orchid_base::logging::{log, with_logger};
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::parse::{Comment, Snippet};
|
||||
use orchid_base::reqnot::{
|
||||
Client, ClientExt, CommCtx, MsgReader, MsgReaderExt, Receipt, RepWriter, ReqHandle, ReqHandleExt,
|
||||
ReqReader, ReqReaderExt, Witness, io_comm,
|
||||
Client, ClientExt, CommCtx, MsgReader, MsgReaderExt, ReqHandleExt, ReqReaderExt, Witness, io_comm,
|
||||
};
|
||||
use orchid_base::stash::with_stash;
|
||||
use orchid_base::tree::{TokenVariant, ttv_from_api};
|
||||
@@ -39,6 +38,7 @@ use crate::reflection::with_refl_roots;
|
||||
use crate::system::{SysCtx, atom_by_idx, cted, with_sys};
|
||||
use crate::system_ctor::{CtedObj, DynSystemCtor, SystemCtor};
|
||||
use crate::tree::{TreeIntoApiCtxImpl, get_lazy, with_lazy_member_store};
|
||||
use crate::trivial_req::TrivialReqCycle;
|
||||
|
||||
task_local::task_local! {
|
||||
static CLIENT: Rc<dyn Client>;
|
||||
@@ -219,36 +219,6 @@ impl ExtensionBuilder {
|
||||
let fwd_tok = Witness::of(&fwd);
|
||||
let api::SysFwded(sys_id, payload) = fwd;
|
||||
with_sys_record(sys_id, async {
|
||||
struct TrivialReqCycle<'a> {
|
||||
req: &'a [u8],
|
||||
rep: &'a mut Vec<u8>,
|
||||
}
|
||||
impl<'a> ReqReader<'a> for TrivialReqCycle<'a> {
|
||||
fn reader(&mut self) -> Pin<&mut dyn AsyncRead> {
|
||||
Pin::new(&mut self.req) as Pin<&mut _>
|
||||
}
|
||||
fn finish(self: Box<Self>) -> LocalBoxFuture<'a, Box<dyn ReqHandle<'a> + 'a>> {
|
||||
Box::pin(async { self as Box<_> })
|
||||
}
|
||||
}
|
||||
impl<'a> ReqHandle<'a> for TrivialReqCycle<'a> {
|
||||
fn start_reply(
|
||||
self: Box<Self>,
|
||||
) -> LocalBoxFuture<'a, io::Result<Box<dyn RepWriter<'a> + 'a>>> {
|
||||
Box::pin(async { Ok(self as Box<_>) })
|
||||
}
|
||||
}
|
||||
impl<'a> RepWriter<'a> for TrivialReqCycle<'a> {
|
||||
fn writer(&mut self) -> Pin<&mut dyn AsyncWrite> {
|
||||
Pin::new(&mut self.rep) as Pin<&mut _>
|
||||
}
|
||||
fn finish(
|
||||
self: Box<Self>,
|
||||
) -> LocalBoxFuture<'a, io::Result<orchid_base::reqnot::Receipt<'a>>>
|
||||
{
|
||||
Box::pin(async { Ok(Receipt::_new()) })
|
||||
}
|
||||
}
|
||||
let mut reply = Vec::new();
|
||||
let req = TrivialReqCycle { req: &payload, rep: &mut reply };
|
||||
let _ = cted().inst().dyn_request(Box::new(req)).await;
|
||||
@@ -261,7 +231,8 @@ impl ExtensionBuilder {
|
||||
let text = es(text).await;
|
||||
let src = Sym::from_api(src).await;
|
||||
let expr_store = BorrowedExprStore::new();
|
||||
let trigger_char = text.chars().nth(pos as usize).unwrap();
|
||||
let tail = &text[pos as usize..];
|
||||
let trigger_char = tail.chars().next().unwrap();
|
||||
let ekey_na = ekey_not_applicable().await;
|
||||
let ekey_cascade = ekey_cascade().await;
|
||||
let lexers = cted().inst().dyn_lexers();
|
||||
@@ -269,7 +240,7 @@ impl ExtensionBuilder {
|
||||
lexers.iter().filter(|l| char_filter_match(l.char_filter(), trigger_char))
|
||||
{
|
||||
let ctx = LexContext::new(&expr_store, &text, id, pos, src.clone());
|
||||
match try_with_reporter(lx.lex(&text[pos as usize..], &ctx)).await {
|
||||
match try_with_reporter(lx.lex(tail, &ctx)).await {
|
||||
Err(e) if e.any(|e| *e == ekey_na) => continue,
|
||||
Err(e) => {
|
||||
let eopt = e.keep_only(|e| *e != ekey_cascade).map(|e| Err(e.to_api()));
|
||||
@@ -277,7 +248,11 @@ impl ExtensionBuilder {
|
||||
return handle.reply(&lex, &eopt).await;
|
||||
},
|
||||
Ok((s, expr)) => {
|
||||
let expr = expr.into_api(&mut (), &mut ()).await;
|
||||
let expr = join_all(
|
||||
(expr.into_iter())
|
||||
.map(|tok| async { tok.into_api(&mut (), &mut ()).await }),
|
||||
)
|
||||
.await;
|
||||
let pos = (text.len() - s.len()) as u32;
|
||||
expr_store.dispose().await;
|
||||
return handle.reply(&lex, &Some(Ok(api::LexedExpr { pos, expr }))).await;
|
||||
@@ -344,14 +319,8 @@ impl ExtensionBuilder {
|
||||
let api::Fwded(_, key, payload) = &fwded;
|
||||
let mut reply = Vec::new();
|
||||
let key = Sym::from_api(*key).await;
|
||||
let some = nfo
|
||||
.handle_req(
|
||||
actx,
|
||||
key,
|
||||
Pin::<&mut &[u8]>::new(&mut &payload[..]),
|
||||
Pin::<&mut Vec<_>>::new(&mut reply),
|
||||
)
|
||||
.await;
|
||||
let req = TrivialReqCycle { req: payload, rep: &mut reply };
|
||||
let some = nfo.handle_req(actx, key, Box::new(req)).await;
|
||||
handle.reply(fwded, &some.then_some(reply)).await
|
||||
},
|
||||
api::AtomReq::CallRef(call @ api::CallRef(_, arg)) => {
|
||||
|
||||
@@ -136,6 +136,7 @@ impl Expr {
|
||||
_ => Err(self),
|
||||
}
|
||||
}
|
||||
pub async fn pos(&self) -> Pos { self.data().await.pos.clone() }
|
||||
pub fn handle(&self) -> Rc<ExprHandle> { self.handle.clone() }
|
||||
|
||||
pub fn slot(&self) -> GExpr {
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::future::LocalBoxFuture;
|
||||
use futures::future::{LocalBoxFuture, join_all};
|
||||
use futures::{AsyncWrite, FutureExt};
|
||||
use itertools::Itertools;
|
||||
use never::Never;
|
||||
@@ -14,6 +14,7 @@ use orchid_api_traits::Encode;
|
||||
use orchid_base::clone;
|
||||
use orchid_base::error::OrcRes;
|
||||
use orchid_base::format::{FmtCtx, FmtUnit};
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::name::Sym;
|
||||
use task_local::task_local;
|
||||
use trait_set::trait_set;
|
||||
@@ -24,13 +25,36 @@ use crate::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
|
||||
use crate::conv::ToExpr;
|
||||
use crate::coroutine_exec::{ExecHandle, exec};
|
||||
use crate::expr::Expr;
|
||||
use crate::gen_expr::GExpr;
|
||||
use crate::gen_expr::{GExpr, new_atom};
|
||||
use crate::system::sys_id;
|
||||
|
||||
trait_set! {
|
||||
trait FunCB = Fn(Vec<Expr>) -> LocalBoxFuture<'static, OrcRes<GExpr>> + 'static;
|
||||
}
|
||||
|
||||
task_local! {
|
||||
static ARGV: Vec<Expr>;
|
||||
}
|
||||
|
||||
pub fn get_arg(idx: usize) -> Expr {
|
||||
ARGV
|
||||
.try_with(|argv| {
|
||||
(argv.get(idx).cloned())
|
||||
.unwrap_or_else(|| panic!("Cannot read argument ##{idx}, only have {}", argv.len()))
|
||||
})
|
||||
.expect("get_arg called outside ExprFunc")
|
||||
}
|
||||
|
||||
pub fn get_argc() -> usize {
|
||||
ARGV.try_with(|argv| argv.len()).expect("get_arg called outside ExprFunc")
|
||||
}
|
||||
|
||||
pub async fn get_arg_posv(idxes: impl IntoIterator<Item = usize>) -> impl Iterator<Item = Pos> {
|
||||
let args = (ARGV.try_with(|argv| idxes.into_iter().map(|i| &argv[i]).cloned().collect_vec()))
|
||||
.expect("get_arg_posv called outside ExprFunc");
|
||||
join_all(args.iter().map(|expr| expr.pos())).await.into_iter()
|
||||
}
|
||||
|
||||
pub trait ExprFunc<I, O>: Clone + 'static {
|
||||
fn argtyps() -> &'static [TypeId];
|
||||
fn apply<'a>(&self, hand: ExecHandle<'a>, v: Vec<Expr>) -> impl Future<Output = OrcRes<GExpr>>;
|
||||
@@ -110,7 +134,7 @@ impl OwnedAtom for Fun {
|
||||
if new_args.len() == self.record.argtyps.len() {
|
||||
(self.record.fun)(new_args).await.to_gen().await
|
||||
} else {
|
||||
Self { args: new_args, record: self.record.clone(), path: self.path.clone() }.to_gen().await
|
||||
new_atom(Self { args: new_args, record: self.record.clone(), path: self.path.clone() })
|
||||
}
|
||||
}
|
||||
async fn call(self, arg: Expr) -> GExpr { self.call_ref(arg).await }
|
||||
@@ -156,7 +180,7 @@ impl OwnedAtom for Lambda {
|
||||
if new_args.len() == self.record.argtyps.len() {
|
||||
(self.record.fun)(new_args).await.to_gen().await
|
||||
} else {
|
||||
Self { args: new_args, record: self.record.clone() }.to_gen().await
|
||||
new_atom(Self { args: new_args, record: self.record.clone() })
|
||||
}
|
||||
}
|
||||
async fn call(self, arg: Expr) -> GExpr { self.call_ref(arg).await }
|
||||
@@ -168,7 +192,7 @@ mod expr_func_derives {
|
||||
|
||||
use orchid_base::error::OrcRes;
|
||||
|
||||
use super::ExprFunc;
|
||||
use super::{ARGV, ExprFunc};
|
||||
use crate::conv::{ToExpr, TryFromExpr};
|
||||
use crate::func_atom::{ExecHandle, Expr};
|
||||
use crate::gen_expr::GExpr;
|
||||
@@ -187,8 +211,9 @@ mod expr_func_derives {
|
||||
}
|
||||
async fn apply<'a>(&self, _: ExecHandle<'a>, v: Vec<Expr>) -> OrcRes<GExpr> {
|
||||
assert_eq!(v.len(), Self::argtyps().len(), "Arity mismatch");
|
||||
let argv = v.clone();
|
||||
let [$([< $t:lower >],)*] = v.try_into().unwrap_or_else(|_| panic!("Checked above"));
|
||||
Ok(self($($t::try_from_expr([< $t:lower >]).await?,)*).await.to_gen().await)
|
||||
Ok(ARGV.scope(argv, self($($t::try_from_expr([< $t:lower >]).await?,)*)).await.to_gen().await)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,8 @@ impl Format for GExprKind {
|
||||
fn inherit(kind: GExprKind) -> GExpr { GExpr { pos: Pos::Inherit, kind } }
|
||||
|
||||
pub fn sym_ref(path: Sym) -> GExpr { inherit(GExprKind::Const(path)) }
|
||||
pub fn atom<A: ToAtom>(atom: A) -> GExpr { inherit(GExprKind::NewAtom(atom.to_atom_factory())) }
|
||||
/// Creates an expression from a new atom that we own.
|
||||
pub fn new_atom<A: ToAtom>(atom: A) -> GExpr { inherit(GExprKind::NewAtom(atom.to_atom_factory())) }
|
||||
|
||||
pub fn seq(deps: impl IntoIterator<Item = GExpr>, val: GExpr) -> GExpr {
|
||||
fn recur(mut ops: impl Iterator<Item = GExpr>) -> Option<GExpr> {
|
||||
|
||||
@@ -72,12 +72,25 @@ impl<'a> LexContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait LexedData {
|
||||
fn into_vec(self) -> Vec<GenTokTree>;
|
||||
}
|
||||
impl LexedData for GenTokTree {
|
||||
fn into_vec(self) -> Vec<GenTokTree> { vec![self] }
|
||||
}
|
||||
impl LexedData for Vec<GenTokTree> {
|
||||
fn into_vec(self) -> Vec<GenTokTree> { self }
|
||||
}
|
||||
impl<const N: usize> LexedData for [GenTokTree; N] {
|
||||
fn into_vec(self) -> Vec<GenTokTree> { self.to_vec() }
|
||||
}
|
||||
|
||||
pub trait Lexer: Debug + Send + Sync + Sized + Default + 'static {
|
||||
const CHAR_FILTER: &'static [RangeInclusive<char>];
|
||||
fn lex<'a>(
|
||||
tail: &'a str,
|
||||
lctx: &'a LexContext<'a>,
|
||||
) -> impl Future<Output = OrcRes<(&'a str, GenTokTree)>>;
|
||||
) -> impl Future<Output = OrcRes<(&'a str, impl LexedData)>>;
|
||||
}
|
||||
|
||||
pub trait DynLexer: Debug + Send + Sync + 'static {
|
||||
@@ -86,7 +99,7 @@ pub trait DynLexer: Debug + Send + Sync + 'static {
|
||||
&self,
|
||||
tail: &'a str,
|
||||
ctx: &'a LexContext<'a>,
|
||||
) -> LocalBoxFuture<'a, OrcRes<(&'a str, GenTokTree)>>;
|
||||
) -> LocalBoxFuture<'a, OrcRes<(&'a str, Vec<GenTokTree>)>>;
|
||||
}
|
||||
|
||||
impl<T: Lexer> DynLexer for T {
|
||||
@@ -95,8 +108,8 @@ impl<T: Lexer> DynLexer for T {
|
||||
&self,
|
||||
tail: &'a str,
|
||||
ctx: &'a LexContext<'a>,
|
||||
) -> LocalBoxFuture<'a, OrcRes<(&'a str, GenTokTree)>> {
|
||||
T::lex(tail, ctx).boxed_local()
|
||||
) -> LocalBoxFuture<'a, OrcRes<(&'a str, Vec<GenTokTree>)>> {
|
||||
async { T::lex(tail, ctx).await.map(|(s, d)| (s, d.into_vec())) }.boxed_local()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ use orchid_api as api;
|
||||
pub mod atom;
|
||||
pub mod atom_owned;
|
||||
pub mod atom_thin;
|
||||
pub mod binary;
|
||||
pub mod conv;
|
||||
pub mod coroutine_exec;
|
||||
pub mod entrypoint;
|
||||
@@ -16,8 +17,9 @@ pub mod logger;
|
||||
pub mod other_system;
|
||||
pub mod parser;
|
||||
pub mod reflection;
|
||||
pub mod stream_reqs;
|
||||
pub mod system;
|
||||
pub mod system_ctor;
|
||||
pub mod tokio;
|
||||
pub mod tree;
|
||||
pub mod binary;
|
||||
mod trivial_req;
|
||||
|
||||
@@ -184,12 +184,12 @@ impl ConstCtx {
|
||||
&'b self,
|
||||
names: impl IntoIterator<Item = &'b Sym> + 'b,
|
||||
) -> impl Stream<Item = OrcRes<Sym>> + 'b {
|
||||
let resolve_names = api::ResolveNames {
|
||||
constid: self.constid,
|
||||
sys: sys_id(),
|
||||
names: names.into_iter().map(|n| n.to_api()).collect_vec(),
|
||||
};
|
||||
let names = names.into_iter().map(|n| n.to_api()).collect_vec();
|
||||
stream(async |mut cx| {
|
||||
if names.is_empty() {
|
||||
return;
|
||||
}
|
||||
let resolve_names = api::ResolveNames { constid: self.constid, sys: sys_id(), names };
|
||||
for name_opt in request(resolve_names).await {
|
||||
cx.emit(match name_opt {
|
||||
Err(e) => Err(OrcErrv::from_api(&e).await),
|
||||
|
||||
@@ -47,8 +47,9 @@ impl ReflMod {
|
||||
async fn try_populate(&self) -> Result<(), api::LsModuleError> {
|
||||
let path_tok = iv(&self.0.path[..]).await;
|
||||
let reply = match request(api::LsModule(sys_id(), path_tok.to_api())).await {
|
||||
Err(api::LsModuleError::TreeUnavailable) =>
|
||||
panic!("Reflected tree accessed outside an interpreter call. This extension is faulty."),
|
||||
Err(api::LsModuleError::TreeUnavailable) => {
|
||||
panic!("Reflected tree accessed outside an interpreter call. This extension is faulty.")
|
||||
},
|
||||
Err(err) => return Err(err),
|
||||
Ok(details) => details,
|
||||
};
|
||||
@@ -79,10 +80,12 @@ impl ReflMod {
|
||||
return None;
|
||||
}
|
||||
match self.try_populate().await {
|
||||
Err(api::LsModuleError::InvalidPath) =>
|
||||
panic!("Path became invalid since module was created"),
|
||||
Err(api::LsModuleError::IsConstant) =>
|
||||
panic!("Path previously contained a module but now contains a constant"),
|
||||
Err(api::LsModuleError::InvalidPath) => {
|
||||
panic!("Path became invalid since module was created")
|
||||
},
|
||||
Err(api::LsModuleError::IsConstant) => {
|
||||
panic!("Path previously contained a module but now contains a constant")
|
||||
},
|
||||
Err(api::LsModuleError::TreeUnavailable) => unreachable!(),
|
||||
Ok(()) => (),
|
||||
}
|
||||
|
||||
72
orchid-extension/src/stream_reqs.rs
Normal file
72
orchid-extension/src/stream_reqs.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
use orchid_api_derive::{Coding, Hierarchy};
|
||||
use orchid_api_traits::Request;
|
||||
|
||||
use crate::atom::AtomMethod;
|
||||
|
||||
/// Represents [std::io::ErrorKind] values that are produced while operating on
|
||||
/// already-opened files
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
|
||||
pub enum IoErrorKind {
|
||||
BrokenPipe,
|
||||
UnexpectedEof,
|
||||
ConnectionAborted,
|
||||
Other,
|
||||
}
|
||||
|
||||
/// Represents [std::io::Error] values that are produced while operating on
|
||||
/// already-opened files
|
||||
#[derive(Clone, Debug, Coding)]
|
||||
pub struct IoError {
|
||||
pub message: String,
|
||||
pub kind: IoErrorKind,
|
||||
}
|
||||
|
||||
/// Read at most the specified number of bytes, but at least one byte, from a
|
||||
/// stream. If the returned vector is empty, the stream has reached its end.
|
||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||
pub struct ReadReq(pub u64);
|
||||
impl Request for ReadReq {
|
||||
type Response = Result<Vec<u8>, IoError>;
|
||||
}
|
||||
impl AtomMethod for ReadReq {
|
||||
const NAME: &str = "orchid::stream::read";
|
||||
}
|
||||
|
||||
/// Write the specified number of bytes into a stream.
|
||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||
#[extends(OutputReq)]
|
||||
pub struct WriteReq {
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
impl Request for WriteReq {
|
||||
type Response = Result<(), IoError>;
|
||||
}
|
||||
|
||||
/// Flush a stream, ensuring that all data reached its destination.
|
||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||
#[extends(OutputReq)]
|
||||
pub struct FlushReq;
|
||||
impl Request for FlushReq {
|
||||
type Response = Result<(), IoError>;
|
||||
}
|
||||
|
||||
/// Close a stream, indicating that no further data will be sent through it.
|
||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||
#[extends(OutputReq)]
|
||||
pub struct CloseReq;
|
||||
impl Request for CloseReq {
|
||||
type Response = Result<(), IoError>;
|
||||
}
|
||||
|
||||
/// Operations on outbound streams across extension boundaries.
|
||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||
#[extendable]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
pub enum OutputReq {
|
||||
WriteReq(WriteReq),
|
||||
FlushReq(FlushReq),
|
||||
CloseReq(CloseReq),
|
||||
}
|
||||
impl AtomMethod for OutputReq {
|
||||
const NAME: &str = "orchid::stream::write";
|
||||
}
|
||||
@@ -155,7 +155,10 @@ where A: AtomicFeatures {
|
||||
Ok(TAtom { value: *value, untyped: foreign })
|
||||
}
|
||||
|
||||
pub async fn dep_req<Sys: SystemCard, Req: Request + Into<Sys::Req>>(req: Req) -> Req::Response {
|
||||
/// Make a global request to a system that supports this request type. The
|
||||
/// target system must either be the system in which this function is called, or
|
||||
/// one of its direct dependencies.
|
||||
pub async fn sys_req<Sys: SystemCard, Req: Request + Into<Sys::Req>>(req: Req) -> Req::Response {
|
||||
let mut msg = Vec::new();
|
||||
req.into().encode_vec(&mut msg);
|
||||
let cted = cted();
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::api;
|
||||
use crate::conv::ToExpr;
|
||||
use crate::expr::{BorrowedExprStore, Expr, ExprHandle};
|
||||
use crate::func_atom::{ExprFunc, Fun};
|
||||
use crate::gen_expr::{GExpr, sym_ref};
|
||||
use crate::gen_expr::{GExpr, new_atom, sym_ref};
|
||||
|
||||
pub type GenTokTree = TokTree<Expr, GExpr>;
|
||||
pub type GenTok = Token<Expr, GExpr>;
|
||||
@@ -71,12 +71,11 @@ pub fn module(
|
||||
vec![GenMember { name, kind, public, comments: vec![] }]
|
||||
}
|
||||
pub fn root_mod(name: &str, mems: impl IntoIterator<Item = Vec<GenMember>>) -> (String, MemKind) {
|
||||
let kind = MemKind::Mod { members: mems.into_iter().flatten().collect() };
|
||||
(name.to_string(), kind)
|
||||
(name.to_string(), MemKind::module(mems))
|
||||
}
|
||||
pub fn fun<I, O>(public: bool, name: &str, xf: impl ExprFunc<I, O>) -> Vec<GenMember> {
|
||||
let fac =
|
||||
LazyMemberFactory::new(async move |sym| MemKind::Const(Fun::new(sym, xf).await.to_gen().await));
|
||||
LazyMemberFactory::new(async move |sym| MemKind::Const(new_atom(Fun::new(sym, xf).await)));
|
||||
vec![GenMember { name: name.to_string(), kind: MemKind::Lazy(fac), public, comments: vec![] }]
|
||||
}
|
||||
pub fn prefix(path: &str, items: impl IntoIterator<Item = Vec<GenMember>>) -> Vec<GenMember> {
|
||||
@@ -113,12 +112,12 @@ pub fn merge_trivial(trees: impl IntoIterator<Item = Vec<GenMember>>) -> Vec<Gen
|
||||
let prev = all_members.insert(mem.name.clone(), (unit, mem.comments.into_iter().collect()));
|
||||
assert!(prev.is_none(), "Conflict in trivial tree merge on {}", mem.name);
|
||||
},
|
||||
MemKind::Mod { members } => match all_members.entry(mem.name.clone()) {
|
||||
MemKind::Mod(members) => match all_members.entry(mem.name.clone()) {
|
||||
hashbrown::hash_map::Entry::Vacant(slot) => {
|
||||
slot.insert((MemKind::Mod { members }, mem.comments.into_iter().collect()));
|
||||
slot.insert((MemKind::Mod(members), mem.comments.into_iter().collect()));
|
||||
},
|
||||
hashbrown::hash_map::Entry::Occupied(mut old) => match old.get_mut() {
|
||||
(MemKind::Mod { members: old_items, .. }, old_cmts) => {
|
||||
(MemKind::Mod(old_items), old_cmts) => {
|
||||
let mut swap = vec![];
|
||||
std::mem::swap(&mut swap, old_items);
|
||||
*old_items = merge_trivial([swap, members]);
|
||||
@@ -167,15 +166,19 @@ impl GenMember {
|
||||
|
||||
pub enum MemKind {
|
||||
Const(GExpr),
|
||||
Mod { members: Vec<GenMember> },
|
||||
Mod(Vec<GenMember>),
|
||||
Lazy(LazyMemberFactory),
|
||||
}
|
||||
impl MemKind {
|
||||
pub async fn cnst(val: impl ToExpr) -> Self { Self::Const(val.to_gen().await) }
|
||||
pub fn module(mems: impl IntoIterator<Item = Vec<GenMember>>) -> Self {
|
||||
Self::Mod(mems.into_iter().flatten().collect())
|
||||
}
|
||||
pub(crate) async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MemberKind {
|
||||
match self {
|
||||
Self::Lazy(lazy) => api::MemberKind::Lazy(add_lazy(ctx, lazy)),
|
||||
Self::Const(c) => api::MemberKind::Const(c.serialize().await),
|
||||
Self::Mod { members } => api::MemberKind::Module(api::Module {
|
||||
Self::Mod(members) => api::MemberKind::Module(api::Module {
|
||||
members: stream(async |mut cx| {
|
||||
for m in members {
|
||||
cx.emit(m.into_api(ctx).await).await
|
||||
|
||||
28
orchid-extension/src/trivial_req.rs
Normal file
28
orchid-extension/src/trivial_req.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
|
||||
use futures::future::LocalBoxFuture;
|
||||
use futures::{AsyncRead, AsyncWrite};
|
||||
use orchid_base::reqnot::{Receipt, RepWriter, ReqHandle, ReqReader};
|
||||
|
||||
pub struct TrivialReqCycle<'a> {
|
||||
pub req: &'a [u8],
|
||||
pub rep: &'a mut Vec<u8>,
|
||||
}
|
||||
impl<'a> ReqReader<'a> for TrivialReqCycle<'a> {
|
||||
fn reader(&mut self) -> Pin<&mut dyn AsyncRead> { Pin::new(&mut self.req) as Pin<&mut _> }
|
||||
fn finish(self: Box<Self>) -> LocalBoxFuture<'a, Box<dyn ReqHandle<'a> + 'a>> {
|
||||
Box::pin(async { self as Box<_> })
|
||||
}
|
||||
}
|
||||
impl<'a> ReqHandle<'a> for TrivialReqCycle<'a> {
|
||||
fn start_reply(self: Box<Self>) -> LocalBoxFuture<'a, io::Result<Box<dyn RepWriter<'a> + 'a>>> {
|
||||
Box::pin(async { Ok(self as Box<_>) })
|
||||
}
|
||||
}
|
||||
impl<'a> RepWriter<'a> for TrivialReqCycle<'a> {
|
||||
fn writer(&mut self) -> Pin<&mut dyn AsyncWrite> { Pin::new(&mut self.rep) as Pin<&mut _> }
|
||||
fn finish(self: Box<Self>) -> LocalBoxFuture<'a, io::Result<Receipt<'a>>> {
|
||||
Box::pin(async { Ok(Receipt::_new()) })
|
||||
}
|
||||
}
|
||||
@@ -22,10 +22,10 @@ num-traits = "0.2.19"
|
||||
orchid-api = { version = "0.1.0", path = "../orchid-api" }
|
||||
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
|
||||
orchid-base = { version = "0.1.0", path = "../orchid-base" }
|
||||
orchid-extension = { version = "0.1.0", path = "../orchid-extension", optional = true }
|
||||
ordered-float = "5.1.0"
|
||||
pastey = "0.2.1"
|
||||
substack = "1.1.1"
|
||||
test_executors = "0.4.1"
|
||||
tokio = { version = "1.49.0", features = ["process"], optional = true }
|
||||
tokio-util = { version = "0.7.18", features = ["compat"], optional = true }
|
||||
trait-set = "0.3.0"
|
||||
@@ -33,3 +33,4 @@ unsync-pipe = { version = "0.2.0", path = "../unsync-pipe" }
|
||||
|
||||
[features]
|
||||
tokio = ["dep:tokio", "dep:tokio-util", "dep:libloading"]
|
||||
orchid-extension = ["dep:orchid-extension"]
|
||||
|
||||
@@ -3,10 +3,14 @@ use std::rc::{Rc, Weak};
|
||||
|
||||
use async_once_cell::OnceCell;
|
||||
use derive_destructure::destructure;
|
||||
#[cfg(feature = "orchid-extension")]
|
||||
use orchid_api_traits::{Request, UnderRoot};
|
||||
use orchid_base::format::{FmtCtx, FmtUnit, Format, take_first_fmt};
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::reqnot::ClientExt;
|
||||
use orchid_base::tree::AtomRepr;
|
||||
#[cfg(feature = "orchid-extension")]
|
||||
use orchid_extension::atom::AtomMethod;
|
||||
|
||||
use crate::api;
|
||||
use crate::ctx::Ctx;
|
||||
@@ -56,6 +60,21 @@ impl AtomHand {
|
||||
Self(Rc::new(AtomData { owner, drop, data, display: OnceCell::new() }))
|
||||
}
|
||||
#[must_use]
|
||||
#[cfg(feature = "orchid-extension")]
|
||||
pub async fn ipc<M: Request + UnderRoot<Root: AtomMethod>>(
|
||||
&self,
|
||||
method: M,
|
||||
) -> Option<M::Response> {
|
||||
use orchid_api_traits::{Decode, Encode};
|
||||
use orchid_base::name::Sym;
|
||||
|
||||
let name = Sym::parse(<M as UnderRoot>::Root::NAME).await.unwrap();
|
||||
let mut buf = Vec::new();
|
||||
method.into_root().encode_vec(&mut buf);
|
||||
let reply_buf = self.req(name.to_api(), buf).await?;
|
||||
Some(M::Response::decode_slice(&mut &reply_buf[..]))
|
||||
}
|
||||
#[must_use]
|
||||
pub async fn call(self, arg: Expr) -> Expr {
|
||||
let owner_sys = self.0.owner.clone();
|
||||
let ctx = owner_sys.ctx();
|
||||
|
||||
@@ -2,9 +2,13 @@ use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use futures::io::BufReader;
|
||||
use futures::{AsyncBufReadExt, StreamExt};
|
||||
use hashbrown::HashMap;
|
||||
use libloading::Library;
|
||||
use libloading::{Library, Symbol};
|
||||
use orchid_base::binary::vt_to_future;
|
||||
use orchid_base::logging::log;
|
||||
use unsync_pipe::pipe;
|
||||
|
||||
use crate::api;
|
||||
use crate::ctx::Ctx;
|
||||
@@ -23,23 +27,16 @@ fn load_dylib(path: &Path) -> Result<Arc<Library>, libloading::Error> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
pub async fn ext_dylib(path: &Path, ctx: Ctx) -> Result<ExtPort, libloading::Error> {
|
||||
use futures::io::BufReader;
|
||||
use futures::{AsyncBufReadExt, StreamExt};
|
||||
use libloading::Symbol;
|
||||
use unsync_pipe::pipe;
|
||||
|
||||
let (write_input, input) = pipe(1024);
|
||||
let (output, read_output) = pipe(1024);
|
||||
let (log, read_log) = pipe(1024);
|
||||
let (write_log, read_log) = pipe(1024);
|
||||
let log_path = path.to_string_lossy().to_string();
|
||||
let _ = ctx.spawn(async move {
|
||||
use orchid_base::logging::log;
|
||||
let mut lines = BufReader::new(read_log).lines();
|
||||
while let Some(line) = lines.next().await {
|
||||
match line {
|
||||
Ok(line) => writeln!(log("stderr"), "{log_path} err> {line}").await,
|
||||
Ok(line) => writeln!(log("stderr"), "dylib {log_path} err> {line}").await,
|
||||
Err(e) => match e.kind() {
|
||||
io::ErrorKind::BrokenPipe | io::ErrorKind::UnexpectedEof => break,
|
||||
_ => panic!("Error while reading stderr {e}"),
|
||||
@@ -56,7 +53,7 @@ pub async fn ext_dylib(path: &Path, ctx: Ctx) -> Result<ExtPort, libloading::Err
|
||||
let _ = unsafe { (data as *mut Ctx).as_mut().unwrap().spawn(vt_to_future(vt)) };
|
||||
}
|
||||
let spawner = api::binary::SpawnerBin { data, drop, spawn };
|
||||
let cx = api::binary::ExtensionContext { input, output, log, spawner };
|
||||
let cx = api::binary::ExtensionContext { input, output, log: write_log, spawner };
|
||||
unsafe { (entrypoint)(cx) };
|
||||
Ok(ExtPort { input: Box::pin(write_input), output: Box::pin(read_output) })
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -103,18 +103,24 @@ impl Expr {
|
||||
}
|
||||
impl Format for Expr {
|
||||
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||
return print_expr(self, c, Substack::Bottom).await;
|
||||
return print_expr(self, c, Substack::Bottom, &[]).await;
|
||||
}
|
||||
}
|
||||
async fn print_expr<'a>(
|
||||
pub async fn print_expr<'a>(
|
||||
expr: &'a Expr,
|
||||
c: &'a (impl FmtCtx + ?Sized + 'a),
|
||||
visited: Substack<'_, api::ExprTicket>,
|
||||
id_only: &[api::ExprTicket],
|
||||
) -> FmtUnit {
|
||||
if visited.iter().any(|id| id == &expr.id()) {
|
||||
return "CYCLIC_EXPR".to_string().into();
|
||||
}
|
||||
print_exprkind(&*expr.kind().read().await, c, visited.push(expr.id())).boxed_local().await
|
||||
if id_only.iter().any(|id| id == &expr.id()) {
|
||||
return format!("{:?}", expr.id()).into();
|
||||
}
|
||||
print_exprkind(&*expr.kind().read().await, c, visited.push(expr.id()), id_only)
|
||||
.boxed_local()
|
||||
.await
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -138,28 +144,30 @@ impl ExprKind {
|
||||
}
|
||||
impl Format for ExprKind {
|
||||
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||
print_exprkind(self, c, Substack::Bottom).await
|
||||
print_exprkind(self, c, Substack::Bottom, &[]).await
|
||||
}
|
||||
}
|
||||
async fn print_exprkind<'a>(
|
||||
ek: &ExprKind,
|
||||
c: &'a (impl FmtCtx + ?Sized + 'a),
|
||||
visited: Substack<'_, api::ExprTicket>,
|
||||
id_only: &[api::ExprTicket],
|
||||
) -> 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(),
|
||||
ExprKind::Call(f, x) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
||||
.unbounded("{0b} {1l}")
|
||||
.bounded("({0b} {1})")))
|
||||
.units([print_expr(f, c, visited).await, print_expr(x, c, visited).await]),
|
||||
.units([print_expr(f, c, visited, id_only).await, print_expr(x, c, visited, id_only).await]),
|
||||
ExprKind::Identity(id) =>
|
||||
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{{{0}}}"))).units([print_expr(
|
||||
id, c, visited,
|
||||
id, c, visited, id_only,
|
||||
)
|
||||
.boxed_local()
|
||||
.await]),
|
||||
@@ -167,14 +175,14 @@ async fn print_exprkind<'a>(
|
||||
ExprKind::Lambda(None, body) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
||||
// .unbounded("\\.{0l}")
|
||||
.bounded("(\\.{0b})")))
|
||||
.units([print_expr(body, c, visited).await]),
|
||||
.units([print_expr(body, c, visited, id_only).await]),
|
||||
ExprKind::Lambda(Some(path), body) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
||||
// .unbounded("\\{0b}. {1l}")
|
||||
.bounded("(\\{0b}. {1b})")))
|
||||
.units([format!("{path}").into(), print_expr(body, c, visited).await]),
|
||||
.units([format!("{path}").into(), print_expr(body, c, visited, id_only).await]),
|
||||
ExprKind::Seq(l, r) =>
|
||||
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("[{0b}]{1l}")))
|
||||
.units([print_expr(l, c, visited).await, print_expr(r, c, visited).await]),
|
||||
.units([print_expr(l, c, visited, id_only).await, print_expr(r, c, visited, id_only).await]),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -102,11 +102,7 @@ impl Extension {
|
||||
this.0.ctx.exprs.give_expr(target)
|
||||
},
|
||||
api::ExtHostNotif::ExprNotif(api::ExprNotif::Release(rel)) => {
|
||||
if this.is_own_sys(rel.0).await {
|
||||
this.0.ctx.exprs.take_expr(rel.1);
|
||||
} else {
|
||||
writeln!(log("warn"), "Not our system {:?}", rel.0).await
|
||||
}
|
||||
},
|
||||
api::ExtHostNotif::Log(api::Log { category, message }) =>
|
||||
write!(log(&es(category).await), "{message}").await,
|
||||
@@ -130,7 +126,7 @@ impl Extension {
|
||||
// Atom printing and interning is never reported because it generates too much
|
||||
// noise
|
||||
if !matches!(req, api::ExtHostReq::ExtAtomPrint(_))
|
||||
|| matches!(req, api::ExtHostReq::IntReq(_))
|
||||
&& !matches!(req, api::ExtHostReq::IntReq(_))
|
||||
{
|
||||
writeln!(log("msg"), "Host received request {req:?}").await;
|
||||
}
|
||||
@@ -215,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 {
|
||||
@@ -299,14 +296,6 @@ impl Extension {
|
||||
pub fn ctx(&self) -> &Ctx { &self.0.ctx }
|
||||
pub fn system_ctors(&self) -> impl Iterator<Item = &SystemCtor> { self.0.systems.iter() }
|
||||
#[must_use]
|
||||
pub async fn is_own_sys(&self, id: api::SysId) -> bool {
|
||||
let Some(sys) = self.ctx().system_inst(id).await else {
|
||||
writeln!(log("warn"), "Invalid system ID {id:?}").await;
|
||||
return false;
|
||||
};
|
||||
Rc::ptr_eq(&self.0, &sys.ext().0)
|
||||
}
|
||||
#[must_use]
|
||||
pub fn next_pars(&self) -> NonZeroU64 {
|
||||
let mut next_pars = self.0.next_pars.borrow_mut();
|
||||
*next_pars = next_pars.checked_add(1).unwrap_or(NonZeroU64::new(1).unwrap());
|
||||
|
||||
45
orchid-host/src/inline.rs
Normal file
45
orchid-host/src/inline.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
#[cfg(feature = "orchid-extension")]
|
||||
use orchid_extension as ox;
|
||||
|
||||
#[cfg(feature = "orchid-extension")]
|
||||
use crate::ctx::Ctx;
|
||||
#[cfg(feature = "orchid-extension")]
|
||||
use crate::extension::ExtPort;
|
||||
|
||||
#[cfg(feature = "orchid-extension")]
|
||||
pub async fn ext_inline(builder: ox::entrypoint::ExtensionBuilder, ctx: Ctx) -> ExtPort {
|
||||
use std::io;
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::io::BufReader;
|
||||
use futures::{AsyncBufReadExt, StreamExt};
|
||||
use orchid_base::logging::log;
|
||||
use unsync_pipe::pipe;
|
||||
|
||||
let (in_stdin, out_stdin) = pipe(1024);
|
||||
let (in_stdout, out_stdout) = pipe(1024);
|
||||
let (in_stderr, out_stderr) = pipe(1024);
|
||||
|
||||
let name = builder.name;
|
||||
|
||||
std::mem::drop(ctx.spawn(async move {
|
||||
let mut lines = BufReader::new(out_stderr).lines();
|
||||
while let Some(line) = lines.next().await {
|
||||
match line {
|
||||
Ok(line) => writeln!(log("stderr"), "inline {name} err> {line}").await,
|
||||
Err(e) => match e.kind() {
|
||||
io::ErrorKind::BrokenPipe | io::ErrorKind::UnexpectedEof => break,
|
||||
_ => panic!("Error while reading stderr {e}"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
builder.build(ox::ext_port::ExtPort {
|
||||
input: Box::pin(out_stdin),
|
||||
output: Box::pin(in_stdout),
|
||||
log: Box::pin(in_stderr),
|
||||
spawn: Rc::new(move |fut| std::mem::drop(ctx.spawn(fut))),
|
||||
});
|
||||
ExtPort { input: Box::pin(in_stdin), output: Box::pin(out_stdout) }
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::ops::Range;
|
||||
|
||||
use futures::FutureExt;
|
||||
use futures::lock::Mutex;
|
||||
use orchid_base::clone;
|
||||
use orchid_base::error::{OrcErrv, OrcRes, mk_errv};
|
||||
use orchid_base::error::{OrcErrv, OrcRes, mk_errv, report};
|
||||
use orchid_base::interner::{IStr, is};
|
||||
use orchid_base::location::SrcRange;
|
||||
use orchid_base::name::Sym;
|
||||
@@ -23,10 +26,11 @@ pub struct LexCtx<'a> {
|
||||
pub tail: &'a str,
|
||||
pub sub_trees: &'a mut Vec<Expr>,
|
||||
pub ctx: &'a Ctx,
|
||||
pub produced: &'a mut VecDeque<ParsTokTree>,
|
||||
}
|
||||
impl<'a> LexCtx<'a> {
|
||||
#[must_use]
|
||||
pub fn push<'b>(&'b mut self, pos: u32) -> LexCtx<'b>
|
||||
pub fn sub<'b>(&'b mut self, pos: u32, produced: &'b mut VecDeque<ParsTokTree>) -> LexCtx<'b>
|
||||
where 'a: 'b {
|
||||
LexCtx {
|
||||
source: self.source,
|
||||
@@ -35,6 +39,7 @@ impl<'a> LexCtx<'a> {
|
||||
systems: self.systems,
|
||||
sub_trees: &mut *self.sub_trees,
|
||||
ctx: self.ctx,
|
||||
produced,
|
||||
}
|
||||
}
|
||||
#[must_use]
|
||||
@@ -44,6 +49,7 @@ impl<'a> LexCtx<'a> {
|
||||
pub fn set_pos(&mut self, pos: u32) { self.tail = &self.source[pos as usize..] }
|
||||
pub fn push_pos(&mut self, delta: u32) { self.set_pos(self.get_pos() + delta) }
|
||||
pub fn set_tail(&mut self, tail: &'a str) { self.tail = tail }
|
||||
pub fn pos_from(&self, tail: &'a str) -> u32 { (self.source.len() - tail.len()) as u32 }
|
||||
#[must_use]
|
||||
pub fn strip_prefix(&mut self, tgt: &str) -> bool {
|
||||
if let Some(src) = self.tail.strip_prefix(tgt) {
|
||||
@@ -79,23 +85,41 @@ impl<'a> LexCtx<'a> {
|
||||
self.tail = rest;
|
||||
matches
|
||||
}
|
||||
pub fn pop_char(&mut self) -> Option<char> {
|
||||
let mut chars = self.tail.chars();
|
||||
let ret = chars.next()?;
|
||||
self.tail = chars.as_str();
|
||||
Some(ret)
|
||||
}
|
||||
pub fn sr_to(&self, start: u32) -> SrcRange { self.sr(start..self.get_pos()) }
|
||||
pub fn sr(&self, range: Range<u32>) -> SrcRange { SrcRange::new(range, self.path) }
|
||||
}
|
||||
|
||||
pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
|
||||
pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<bool> {
|
||||
ctx.trim(unrep_space);
|
||||
if ctx.tail.is_empty() {
|
||||
return Ok(false);
|
||||
}
|
||||
let start = ctx.get_pos();
|
||||
assert!(
|
||||
!ctx.tail.is_empty() && !ctx.tail.starts_with(unrep_space),
|
||||
"Lexing empty string or whitespace to token!\n\
|
||||
Invocations of lex_tok should check for empty string"
|
||||
);
|
||||
let tok = if ctx.strip_prefix("\r\n") || ctx.strip_prefix("\r") || ctx.strip_prefix("\n") {
|
||||
ParsTok::BR
|
||||
} else if let Some(tail) = (ctx.tail.starts_with(name_start).then_some(ctx.tail))
|
||||
.and_then(|t| t.trim_start_matches(name_char).strip_prefix("::"))
|
||||
{
|
||||
let name = &ctx.tail[..ctx.tail.len() - tail.len() - "::".len()];
|
||||
ctx.set_tail(tail);
|
||||
let body = lex_once(ctx).boxed_local().await?;
|
||||
let mut produced = VecDeque::new();
|
||||
let mut sub_cx = ctx.sub(ctx.pos_from(tail), &mut produced);
|
||||
if !lex_once(&mut sub_cx).boxed_local().await? {
|
||||
return Err(mk_errv(
|
||||
is("Unexpected end of source text").await,
|
||||
":: cannot be the last token",
|
||||
[SrcRange::new(start..ctx.get_pos(), ctx.path)],
|
||||
));
|
||||
}
|
||||
let pos = sub_cx.get_pos();
|
||||
ctx.set_pos(pos);
|
||||
let body = produced.pop_front().expect("lex_once returned true");
|
||||
ctx.produced.extend(produced.into_iter());
|
||||
ParsTok::NS(is(name).await, Box::new(body))
|
||||
} else if ctx.strip_prefix("--[") {
|
||||
let Some((cmt, tail)) = ctx.tail.split_once("]--") else {
|
||||
@@ -113,26 +137,86 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
|
||||
ParsTok::Comment(is(&tail[2..end]).await)
|
||||
} else if let Some(tail) = ctx.tail.strip_prefix('\\').filter(|t| t.starts_with(name_start)) {
|
||||
// fanciness like \$placeh in templates is resolved in the macro engine.
|
||||
ctx.set_tail(tail);
|
||||
let arg = lex_once(ctx).boxed_local().await?;
|
||||
let start = ctx.get_pos();
|
||||
let mut produced = VecDeque::new();
|
||||
let mut sub_cx = ctx.sub(ctx.pos_from(tail), &mut produced);
|
||||
if !lex_once(&mut sub_cx).boxed_local().await? {
|
||||
return Err(mk_errv(
|
||||
is("Unexpected end of file").await,
|
||||
"Expected a lambda argument and body",
|
||||
[SrcRange::new(start..ctx.get_pos(), ctx.path)],
|
||||
));
|
||||
}
|
||||
let pos = sub_cx.get_pos();
|
||||
ctx.set_pos(pos);
|
||||
let arg = produced.pop_front().expect("lex_once returned true");
|
||||
ctx.produced.extend(produced);
|
||||
ctx.trim_ws();
|
||||
ParsTok::LambdaHead(Box::new(arg))
|
||||
} else if let Some((lp, rp, paren)) = PARENS.iter().find(|(lp, ..)| ctx.strip_char(*lp)) {
|
||||
let mut body = Vec::new();
|
||||
let mut body = VecDeque::new();
|
||||
ctx.trim_ws();
|
||||
while !ctx.strip_char(*rp) {
|
||||
if ctx.tail.is_empty() {
|
||||
let mut sub_cx = ctx.sub(ctx.get_pos(), &mut body);
|
||||
if !lex_once(&mut sub_cx).boxed_local().await? {
|
||||
return Err(mk_errv(
|
||||
is("unclosed paren").await,
|
||||
format!("this {lp} has no matching {rp}"),
|
||||
[SrcRange::new(start..start + 1, ctx.path)],
|
||||
));
|
||||
}
|
||||
body.push(lex_once(ctx).boxed_local().await?);
|
||||
let pos = sub_cx.get_pos();
|
||||
ctx.set_pos(pos);
|
||||
ctx.trim_ws();
|
||||
}
|
||||
ParsTok::S(*paren, body)
|
||||
ParsTok::S(*paren, body.into_iter().collect())
|
||||
} else if let Some(res) = sys_lex(ctx).await {
|
||||
let token = res?;
|
||||
ctx.produced.extend(token);
|
||||
return Ok(true);
|
||||
} else if ctx.tail.starts_with(name_start) {
|
||||
ParsTok::Name(is(ctx.get_start_matches(name_char)).await)
|
||||
} else if ctx.tail.starts_with(op_char) {
|
||||
let whole_tail = ctx.tail;
|
||||
ctx.pop_char().expect("The above check would have failed");
|
||||
let mut tail_after_op = ctx.tail;
|
||||
|
||||
let mut lookahead = Vec::new();
|
||||
while !ctx.tail.is_empty() && ctx.tail.starts_with(op_char) {
|
||||
match sys_lex(ctx).await {
|
||||
None => {
|
||||
ctx.pop_char();
|
||||
tail_after_op = ctx.tail;
|
||||
},
|
||||
Some(sys_res) => {
|
||||
match sys_res {
|
||||
Err(e) => report(e),
|
||||
Ok(tokv) => lookahead = tokv,
|
||||
}
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let op_str = &whole_tail[0..whole_tail.len() - tail_after_op.len()];
|
||||
ctx.produced.push_back(ParsTok::Name(is(op_str).await).at(ctx.sr_to(start)));
|
||||
ctx.produced.extend(lookahead);
|
||||
return Ok(true);
|
||||
} else {
|
||||
return Err(mk_errv(
|
||||
is("Unrecognized character").await,
|
||||
"The following syntax is meaningless.",
|
||||
[SrcRange::new(start..start + 1, ctx.path)],
|
||||
));
|
||||
};
|
||||
ctx.produced.push_back(ParsTokTree { tok, sr: ctx.sr_to(start) });
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// Parse one token via any of the systems, if we can
|
||||
///
|
||||
/// This function never writes lookahead
|
||||
pub async fn sys_lex(ctx: &mut LexCtx<'_>) -> Option<OrcRes<Vec<ParsTokTree>>> {
|
||||
for sys in ctx.systems {
|
||||
let mut errors = Vec::new();
|
||||
if ctx.tail.starts_with(|c| sys.can_lex(c)) {
|
||||
@@ -146,11 +230,27 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
|
||||
clone!(temp_store_cb);
|
||||
async move {
|
||||
let mut ctx_g = ctx_lck.lock().await;
|
||||
match lex_once(&mut ctx_g.push(pos)).boxed_local().await {
|
||||
Ok(t) => Some(api::SubLexed {
|
||||
pos: t.sr.end(),
|
||||
tree: ctx_g.ser_subtree(t, temp_store_cb.clone()).await,
|
||||
}),
|
||||
let mut produced = VecDeque::new();
|
||||
let mut sub_cx = ctx_g.sub(pos, &mut produced);
|
||||
let lex_res = lex_once(&mut sub_cx).boxed_local().await;
|
||||
let pos1 = sub_cx.get_pos();
|
||||
ctx_g.set_pos(pos1);
|
||||
match lex_res {
|
||||
Ok(false) => {
|
||||
errors_lck.lock().await.push(mk_errv(
|
||||
is("End of file").await,
|
||||
"Unexpected end of source text",
|
||||
[ctx_g.sr_to(pos)],
|
||||
));
|
||||
None
|
||||
},
|
||||
Ok(true) => {
|
||||
let tok = produced.pop_front().unwrap();
|
||||
Some(api::SubLexed {
|
||||
pos: tok.sr.end(),
|
||||
tree: ctx_g.ser_subtree(tok, temp_store_cb.clone()).await,
|
||||
})
|
||||
},
|
||||
Err(e) => {
|
||||
errors_lck.lock().await.push(e);
|
||||
None
|
||||
@@ -160,49 +260,46 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
|
||||
})
|
||||
.await;
|
||||
match lx {
|
||||
Err(e) => return Err(errors.into_iter().fold(OrcErrv::from_api(&e).await, |a, b| a + b)),
|
||||
Err(e) =>
|
||||
return Some(Err(errors.into_iter().fold(OrcErrv::from_api(&e).await, |a, b| a + b))),
|
||||
Ok(Some(lexed)) => {
|
||||
ctx.set_pos(lexed.pos);
|
||||
let lexed_tree = ctx.des_subtree(&lexed.expr, temp_store).await;
|
||||
let stable_tree = recur(lexed_tree, &|tt, r| {
|
||||
let mut stable_trees = Vec::new();
|
||||
for tok in lexed.expr {
|
||||
stable_trees.push(recur(ctx.des_subtree(&tok, temp_store.clone()).await, &|tt, r| {
|
||||
if let ParsTok::NewExpr(expr) = tt.tok {
|
||||
return ParsTok::Handle(expr).at(tt.sr);
|
||||
}
|
||||
r(tt)
|
||||
});
|
||||
return Ok(stable_tree);
|
||||
}));
|
||||
}
|
||||
return Some(Ok(stable_trees));
|
||||
},
|
||||
Ok(None) => match errors.into_iter().reduce(|a, b| a + b) {
|
||||
Some(errors) => return Err(errors),
|
||||
Some(errors) => return Some(Err(errors)),
|
||||
None => continue,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
if ctx.tail.starts_with(name_start) {
|
||||
ParsTok::Name(is(ctx.get_start_matches(name_char)).await)
|
||||
} else if ctx.tail.starts_with(op_char) {
|
||||
ParsTok::Name(is(ctx.get_start_matches(op_char)).await)
|
||||
} else {
|
||||
return Err(mk_errv(
|
||||
is("Unrecognized character").await,
|
||||
"The following syntax is meaningless.",
|
||||
[SrcRange::new(start..start + 1, ctx.path)],
|
||||
));
|
||||
}
|
||||
};
|
||||
Ok(ParsTokTree { tok, sr: SrcRange::new(start..ctx.get_pos(), ctx.path) })
|
||||
None
|
||||
}
|
||||
|
||||
pub async fn lex(text: IStr, path: Sym, systems: &[System], ctx: &Ctx) -> OrcRes<Vec<ParsTokTree>> {
|
||||
let mut sub_trees = Vec::new();
|
||||
let mut ctx =
|
||||
LexCtx { source: &text, sub_trees: &mut sub_trees, tail: &text[..], systems, path: &path, ctx };
|
||||
let mut tokv = Vec::new();
|
||||
let mut produced = VecDeque::new();
|
||||
let mut ctx = LexCtx {
|
||||
source: &text,
|
||||
sub_trees: &mut sub_trees,
|
||||
tail: &text[..],
|
||||
systems,
|
||||
path: &path,
|
||||
ctx,
|
||||
produced: &mut produced,
|
||||
};
|
||||
ctx.trim(unrep_space);
|
||||
while !ctx.tail.is_empty() {
|
||||
tokv.push(lex_once(&mut ctx).await?);
|
||||
while lex_once(&mut ctx).await? {
|
||||
ctx.trim(unrep_space);
|
||||
}
|
||||
Ok(tokv)
|
||||
Ok(produced.into())
|
||||
}
|
||||
|
||||
@@ -3,15 +3,18 @@ use orchid_api as api;
|
||||
pub mod atom;
|
||||
pub mod ctx;
|
||||
pub mod dealias;
|
||||
#[cfg(feature = "tokio")]
|
||||
pub mod dylib;
|
||||
pub mod execute;
|
||||
pub mod expr;
|
||||
pub mod expr_store;
|
||||
pub mod extension;
|
||||
pub mod inline;
|
||||
pub mod lex;
|
||||
pub mod logger;
|
||||
pub mod parse;
|
||||
pub mod parsed;
|
||||
#[cfg(feature = "tokio")]
|
||||
pub mod subprocess;
|
||||
mod sys_parser;
|
||||
pub mod system;
|
||||
|
||||
@@ -3,14 +3,12 @@ use std::{io, process};
|
||||
use futures::io::BufReader;
|
||||
use futures::{self, AsyncBufReadExt, StreamExt};
|
||||
use orchid_base::logging::log;
|
||||
#[cfg(feature = "tokio")]
|
||||
use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
|
||||
|
||||
use crate::ctx::Ctx;
|
||||
use crate::extension::ExtPort;
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
pub async fn ext_command(cmd: std::process::Command, ctx: Ctx) -> io::Result<ExtPort> {
|
||||
pub async fn ext_command(cmd: process::Command, ctx: Ctx) -> io::Result<ExtPort> {
|
||||
let name = cmd.get_program().to_string_lossy().to_string();
|
||||
let mut child = tokio::process::Command::from(cmd)
|
||||
.stdin(process::Stdio::piped())
|
||||
@@ -25,9 +23,13 @@ pub async fn ext_command(cmd: std::process::Command, ctx: Ctx) -> io::Result<Ext
|
||||
let _ = child;
|
||||
let mut lines = BufReader::new(child_stderr.compat()).lines();
|
||||
while let Some(line) = lines.next().await {
|
||||
// route stderr with an empty category string. This is not the intended logging
|
||||
// method
|
||||
writeln!(log("stderr"), "{} err> {}", name, line.expect("Readline implies this")).await;
|
||||
match line {
|
||||
Ok(line) => writeln!(log("stderr"), "subproc {name} err> {line}").await,
|
||||
Err(e) => match e.kind() {
|
||||
io::ErrorKind::BrokenPipe | io::ErrorKind::UnexpectedEof => break,
|
||||
_ => panic!("Error while reading stderr {e}"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}));
|
||||
Ok(ExtPort { input: Box::pin(stdin.compat_write()), output: Box::pin(stdout.compat()) })
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -63,7 +63,7 @@ impl Root {
|
||||
pub async fn add_parsed(&self, parsed: &ParsedModule, pars_prefix: Sym) -> Self {
|
||||
let mut ref_this = self.0.write().await;
|
||||
let this = &mut *ref_this;
|
||||
let mut deferred_consts = HashMap::new();
|
||||
let mut deferred_consts = Vec::new();
|
||||
let consts = this.consts.clone();
|
||||
let mut tfpctx = FromParsedCtx {
|
||||
pars_root: parsed,
|
||||
@@ -86,7 +86,7 @@ impl Root {
|
||||
.expect("Merge conflict between parsed and existing module");
|
||||
let new = Root(Rc::new(RwLock::new(RootData { root, consts, ctx: this.ctx.clone() })));
|
||||
*this.ctx.root.write().await = new.downgrade();
|
||||
for (path, (sys_id, pc_id)) in deferred_consts {
|
||||
for (path, sys_id, pc_id) in deferred_consts {
|
||||
let sys = this.ctx.system_inst(sys_id).await.expect("System dropped since parsing");
|
||||
let api_expr = sys.client().request(api::FetchParsedConst(sys.id(), pc_id)).await.unwrap();
|
||||
let expr = Expr::from_api(&api_expr, PathSetBuilder::new(), this.ctx.clone()).await;
|
||||
@@ -383,7 +383,7 @@ pub struct FromParsedCtx<'a> {
|
||||
root: &'a Module,
|
||||
ctx: &'a Ctx,
|
||||
consts: &'a MemoMap<Sym, Expr>,
|
||||
deferred_consts: &'a mut HashMap<Sym, (api::SysId, api::ParsedConstId)>,
|
||||
deferred_consts: &'a mut Vec<(Sym, api::SysId, api::ParsedConstId)>,
|
||||
}
|
||||
|
||||
impl Tree for Module {
|
||||
@@ -435,7 +435,7 @@ impl MemberKind {
|
||||
async fn from_parsed(parsed: &ParsedMemberKind, path: Sym, ctx: &mut FromParsedCtx<'_>) -> Self {
|
||||
match parsed {
|
||||
ParsedMemberKind::Const(id, sys) => {
|
||||
ctx.deferred_consts.insert(path, (sys.id(), *id));
|
||||
ctx.deferred_consts.push((path, sys.id(), *id));
|
||||
MemberKind::Const
|
||||
},
|
||||
ParsedMemberKind::Mod(m) =>
|
||||
|
||||
@@ -4,7 +4,7 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[[bin]]
|
||||
name = "orchid-std-dbg"
|
||||
name = "orchid-std-piped"
|
||||
path = "src/main.rs"
|
||||
|
||||
[lib]
|
||||
@@ -33,6 +33,7 @@ rust_decimal = "1.39.0"
|
||||
subslice-offset = "0.1.1"
|
||||
substack = "1.1.1"
|
||||
tokio = { version = "1.49.0", features = ["full"] }
|
||||
unicode-segmentation = "1.12.0"
|
||||
|
||||
[dev-dependencies]
|
||||
test_executors = "0.4.1"
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#![allow(refining_impl_trait)]
|
||||
mod macros;
|
||||
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;
|
||||
|
||||
@@ -7,7 +7,7 @@ use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::coroutine_exec::exec;
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::gen_expr::GExpr;
|
||||
use orchid_extension::gen_expr::{GExpr, new_atom};
|
||||
|
||||
use crate::macros::mactree::{MacTok, MacTree};
|
||||
|
||||
@@ -41,7 +41,7 @@ impl OwnedAtom for InstantiateTplCall {
|
||||
Ok(t) => self.argv.push(own(&t).await),
|
||||
};
|
||||
if self.argv.len() < self.argc {
|
||||
return self.to_gen().await;
|
||||
return new_atom(self);
|
||||
}
|
||||
let mut args = self.argv.into_iter();
|
||||
let ret = self.tpl.map(&mut false, &mut |mt| match mt.tok() {
|
||||
@@ -49,7 +49,7 @@ impl OwnedAtom for InstantiateTplCall {
|
||||
_ => None,
|
||||
});
|
||||
assert!(args.next().is_none(), "Too many arguments for all slots");
|
||||
ret.to_gen().await
|
||||
new_atom(ret)
|
||||
})
|
||||
.await
|
||||
.to_gen()
|
||||
|
||||
@@ -11,7 +11,7 @@ use orchid_base::sym;
|
||||
use orchid_base::tree::Paren;
|
||||
use orchid_extension::atom::TAtom;
|
||||
use orchid_extension::conv::TryFromExpr;
|
||||
use orchid_extension::gen_expr::{atom, call, sym_ref};
|
||||
use orchid_extension::gen_expr::{call, new_atom, sym_ref};
|
||||
use orchid_extension::parser::{ConstCtx, PSnippet, PTok, PTokTree, ParsCtx, ParsedLine, Parser};
|
||||
|
||||
use crate::macros::mactree::{MacTok, MacTree, MacTreeSeq};
|
||||
@@ -40,7 +40,7 @@ impl Parser for LetLine {
|
||||
Ok(vec![ParsedLine::cnst(&line.sr(), &comments, exported, name, async move |ctx| {
|
||||
let macro_input =
|
||||
MacTok::S(Paren::Round, with_reporter(dealias_mac_v(&aliased, &ctx)).await?).at(sr.pos());
|
||||
Ok(call(sym_ref(sym!(macros::resolve)), [atom(macro_input)]))
|
||||
Ok(call(sym_ref(sym!(macros::resolve)), [new_atom(macro_input)]))
|
||||
})])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::TAtom;
|
||||
use orchid_extension::atom_owned::own;
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::coroutine_exec::exec;
|
||||
use orchid_extension::gen_expr::{call, sym_ref};
|
||||
use orchid_extension::gen_expr::{call, new_atom, sym_ref};
|
||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||
|
||||
use crate::macros::mactree::MacTree;
|
||||
@@ -14,17 +13,36 @@ use crate::{HomoTpl, UntypedTuple};
|
||||
pub async fn gen_macro_lib() -> Vec<GenMember> {
|
||||
prefix("macros", [
|
||||
fun(true, "resolve", async |tpl: TAtom<MacTree>| resolve(own(&tpl).await).await),
|
||||
// TODO test whether any of this worked
|
||||
prefix("common", [
|
||||
build_macro(None, ["..", "_"]).finish(),
|
||||
build_macro(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(3), ["."])
|
||||
.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", ","])
|
||||
@@ -34,14 +52,14 @@ pub async fn gen_macro_lib() -> Vec<GenMember> {
|
||||
exec(async |mut h| {
|
||||
let recur = resolve(mactree!(macros::common::comma_list "push" tail ;)).await;
|
||||
let mut tail = h.exec::<HomoTpl<TAtom<MacTree>>>(recur).await?;
|
||||
tail.0.insert(0, h.exec(head).await?);
|
||||
tail.0.insert(0, h.exec(new_atom(head)).await?);
|
||||
Ok(tail)
|
||||
})
|
||||
.await
|
||||
}],
|
||||
)
|
||||
.rule(mactreev!(macros::common::comma_list ( "...$" final_tail 0 )), [async |[tail]| {
|
||||
HomoTpl(vec![tail.to_gen().await])
|
||||
HomoTpl(vec![new_atom(tail)])
|
||||
}])
|
||||
.rule(mactreev!(macros::common::comma_list()), [async |[]| UntypedTuple(Vec::new())])
|
||||
.finish(),
|
||||
@@ -52,14 +70,14 @@ pub async fn gen_macro_lib() -> Vec<GenMember> {
|
||||
exec(async |mut h| {
|
||||
let recur = resolve(mactree!(macros::common::semi_list "push" tail ;)).await;
|
||||
let mut tail = h.exec::<HomoTpl<TAtom<MacTree>>>(recur).await?;
|
||||
tail.0.insert(0, h.exec(head).await?);
|
||||
tail.0.insert(0, h.exec(new_atom(head)).await?);
|
||||
Ok(tail)
|
||||
})
|
||||
.await
|
||||
}],
|
||||
)
|
||||
.rule(mactreev!(macros::common::semi_list ( "...$" final_tail 0 )), [async |[tail]| {
|
||||
HomoTpl(vec![tail.to_gen().await])
|
||||
HomoTpl(vec![new_atom(tail)])
|
||||
}])
|
||||
.rule(mactreev!(macros::common::semi_list()), [async |[]| UntypedTuple(Vec::new())])
|
||||
.finish(),
|
||||
|
||||
@@ -13,7 +13,7 @@ use orchid_base::tree::{Paren, Token};
|
||||
use orchid_base::{clone, sym};
|
||||
use orchid_extension::atom::TAtom;
|
||||
use orchid_extension::conv::{ToExpr, TryFromExpr};
|
||||
use orchid_extension::gen_expr::{call, sym_ref};
|
||||
use orchid_extension::gen_expr::{call, new_atom, sym_ref};
|
||||
use orchid_extension::parser::{PSnippet, ParsCtx, ParsedLine, Parser};
|
||||
|
||||
use crate::macros::let_line::{dealias_mac_v, parse_tokv};
|
||||
@@ -133,7 +133,7 @@ impl Parser for MacroLine {
|
||||
let macro_input =
|
||||
MacTok::S(Paren::Round, with_reporter(dealias_mac_v(&body_mactree, &ctx)).await?)
|
||||
.at(body_sr.pos());
|
||||
Ok(call(sym_ref(sym!(macros::resolve)), [macro_input.to_gen().await]))
|
||||
Ok(call(sym_ref(sym!(macros::resolve)), [new_atom(macro_input)]))
|
||||
}))
|
||||
}
|
||||
let mac_cell = Rc::new(OnceCell::new());
|
||||
@@ -165,7 +165,7 @@ impl Parser for MacroLine {
|
||||
rules,
|
||||
})))
|
||||
};
|
||||
mac_cell.get_or_init(mac_future).await.clone().to_gen().await
|
||||
mac_cell.get_or_init(mac_future).await.clone().map(new_atom).to_gen().await
|
||||
}))
|
||||
}
|
||||
Ok(lines)
|
||||
|
||||
@@ -18,7 +18,7 @@ use crate::macros::macro_value::Macro;
|
||||
use crate::macros::mactree_lexer::MacTreeLexer;
|
||||
use crate::macros::match_macros::{MatcherAtom, gen_match_macro_lib};
|
||||
use crate::macros::ph_lexer::{PhAtom, PhLexer};
|
||||
use crate::macros::std_macros::gen_std_macro_lib;
|
||||
use crate::macros::stdlib::gen_std_macro_lib;
|
||||
use crate::macros::utils::MacroBodyArgCollector;
|
||||
use crate::{MacTree, StdSystem};
|
||||
|
||||
@@ -55,10 +55,14 @@ impl System for MacroSystem {
|
||||
sym!(macros::common::;),
|
||||
sym!(macros::common::..),
|
||||
sym!(macros::common::_),
|
||||
sym!(macros::common::=),
|
||||
sym!(macros::common::.),
|
||||
sym!(std::tuple::t),
|
||||
sym!(std::record::r),
|
||||
sym!(pattern::match),
|
||||
sym!(pattern::ref),
|
||||
sym!(pattern::=>),
|
||||
sym!(std::fn::[|>]),
|
||||
]
|
||||
}
|
||||
fn lexers() -> Vec<LexerObj> { vec![&MacTreeLexer, &PhLexer] }
|
||||
|
||||
@@ -6,6 +6,7 @@ use orchid_base::error::{OrcRes, mk_errv};
|
||||
use orchid_base::interner::is;
|
||||
use orchid_base::tokens::PARENS;
|
||||
use orchid_base::tree::Paren;
|
||||
use orchid_extension::gen_expr::new_atom;
|
||||
use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable};
|
||||
use orchid_extension::parser::p_tree2gen;
|
||||
use orchid_extension::tree::{GenTok, GenTokTree, x_tok};
|
||||
@@ -28,11 +29,12 @@ impl Lexer for MacTreeLexer {
|
||||
Ok((tail4, mactree)) => {
|
||||
let range = lctx.pos_tt(tail, tail4);
|
||||
let tok = match &args[..] {
|
||||
[] => x_tok(mactree).await,
|
||||
[] => x_tok(new_atom(mactree)).await,
|
||||
_ => {
|
||||
let instantiate_tpl_call =
|
||||
InstantiateTplCall { argc: args.len(), argv: vec![], tpl: mactree };
|
||||
let call = chain!([x_tok(instantiate_tpl_call).await.at(range.clone())], args);
|
||||
let call =
|
||||
chain!([x_tok(new_atom(instantiate_tpl_call)).await.at(range.clone())], args);
|
||||
GenTok::S(Paren::Round, call.collect())
|
||||
},
|
||||
};
|
||||
|
||||
@@ -15,13 +15,12 @@ use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::coroutine_exec::{ExecHandle, exec};
|
||||
use orchid_extension::expr::{Expr, ExprHandle};
|
||||
use orchid_extension::gen_expr::{GExpr, arg, bot, call, lambda, sym_ref};
|
||||
use orchid_extension::gen_expr::{GExpr, arg, bot, call, lambda, new_atom, sym_ref};
|
||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||
|
||||
use crate::macros::resolve::resolve;
|
||||
use crate::macros::utils::{build_macro, mactree, mactreev};
|
||||
use crate::std::reflection::sym_atom::SymAtom;
|
||||
use crate::std::tuple::Tuple;
|
||||
use crate::{HomoTpl, MacTok, MacTree, OrcOpt, Tpl, UntypedTuple, api};
|
||||
|
||||
#[derive(Clone, Coding)]
|
||||
@@ -83,30 +82,27 @@ pub async fn gen_match_macro_lib() -> Vec<GenMember> {
|
||||
.await
|
||||
},
|
||||
),
|
||||
fun(true, "matcher", async |names: HomoTpl<TAtom<SymAtom>>, matcher: Expr| MatcherAtom {
|
||||
fun(true, "matcher", async |names: HomoTpl<TAtom<SymAtom>>, matcher: Expr| {
|
||||
new_atom(MatcherAtom {
|
||||
keys: join_all(names.0.iter().map(async |atm| Sym::from_api(atm.0).await)).await,
|
||||
matcher,
|
||||
})
|
||||
}),
|
||||
build_macro(None, ["match", "match_rule", "_row", "=>"])
|
||||
.rule(mactreev!("pattern::match" "...$" value 0 { "..$" rules 0 }), [
|
||||
async |[value, rules]| {
|
||||
exec(async move |mut h| {
|
||||
let rule_lines = h
|
||||
.exec::<TAtom<Tuple>>(call(sym_ref(sym!(macros::resolve)), [
|
||||
mactree!(macros::common::semi_list "push" rules.clone();).to_gen().await,
|
||||
]))
|
||||
.exec::<HomoTpl<TAtom<MacTree>>>(call(sym_ref(sym!(macros::resolve)), [new_atom(
|
||||
mactree!(macros::common::semi_list "push" rules.clone();),
|
||||
)]))
|
||||
.await?;
|
||||
let mut rule_atoms = Vec::<(TAtom<MatcherAtom>, Expr)>::new();
|
||||
for line_exprh in rule_lines.iter() {
|
||||
let line_mac = h
|
||||
.exec::<TAtom<MacTree>>(Expr::from_handle(
|
||||
ExprHandle::from_ticket(*line_exprh).await,
|
||||
))
|
||||
.await?;
|
||||
for line_mac in rule_lines.0.iter() {
|
||||
let Tpl((matcher, body)) = h
|
||||
.exec(call(sym_ref(sym!(macros::resolve)), [
|
||||
mactree!(pattern::_row "push" own(&line_mac).await ;).to_gen().await,
|
||||
]))
|
||||
.exec(call(sym_ref(sym!(macros::resolve)), [new_atom(
|
||||
mactree!(pattern::_row "push" own(line_mac).await ;),
|
||||
)]))
|
||||
.await?;
|
||||
rule_atoms.push((matcher, body));
|
||||
}
|
||||
@@ -134,18 +130,18 @@ pub async fn gen_match_macro_lib() -> Vec<GenMember> {
|
||||
resolve(mactree!(pattern::match_rule "push" pattern; )).await
|
||||
}])
|
||||
.rule(mactreev!(pattern::match_rule ( macros::common::_ )), [async |[]| {
|
||||
Ok(MatcherAtom {
|
||||
Ok(new_atom(MatcherAtom {
|
||||
keys: Vec::new(),
|
||||
matcher: lambda(0, [OrcOpt(Some(Tpl(()))).to_gen().await]).create().await,
|
||||
})
|
||||
}))
|
||||
}])
|
||||
.rule(mactreev!(pattern::_row ( "...$" pattern 0 pattern::=> "...$" value 1 )), [
|
||||
async |[pattern, mut value]| {
|
||||
exec(async move |mut h| -> OrcRes<Tpl<(TAtom<MatcherAtom>, GExpr)>> {
|
||||
let Ok(pat) = h
|
||||
.exec::<TAtom<MatcherAtom>>(call(sym_ref(sym!(macros::resolve)), [
|
||||
mactree!(pattern::match_rule "push" pattern.clone();).to_gen().await,
|
||||
]))
|
||||
.exec::<TAtom<MatcherAtom>>(call(sym_ref(sym!(macros::resolve)), [new_atom(
|
||||
mactree!(pattern::match_rule "push" pattern.clone();),
|
||||
)]))
|
||||
.await
|
||||
else {
|
||||
return Err(mk_errv(
|
||||
@@ -177,10 +173,10 @@ pub async fn gen_match_macro_lib() -> Vec<GenMember> {
|
||||
[name.pos()],
|
||||
));
|
||||
};
|
||||
Ok(MatcherAtom {
|
||||
Ok(new_atom(MatcherAtom {
|
||||
keys: vec![name.clone()],
|
||||
matcher: sym_ref(sym!(pattern::ref_body)).to_expr().await,
|
||||
})
|
||||
}))
|
||||
}])
|
||||
.finish(),
|
||||
])
|
||||
|
||||
@@ -10,7 +10,7 @@ pub mod match_macros;
|
||||
mod ph_lexer;
|
||||
mod resolve;
|
||||
mod rule;
|
||||
pub mod std_macros;
|
||||
pub mod stdlib;
|
||||
mod utils;
|
||||
|
||||
use mactree::{MacTok, MacTree};
|
||||
|
||||
@@ -5,6 +5,7 @@ use orchid_base::interner::{es, is};
|
||||
use orchid_base::parse::{name_char, name_start};
|
||||
use orchid_extension::atom::Atomic;
|
||||
use orchid_extension::atom_thin::{ThinAtom, ThinVariant};
|
||||
use orchid_extension::gen_expr::new_atom;
|
||||
use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable};
|
||||
use orchid_extension::tree::{GenTokTree, x_tok};
|
||||
|
||||
@@ -72,6 +73,6 @@ impl Lexer for PhLexer {
|
||||
}
|
||||
};
|
||||
let ph_atom = PhAtom(is(name).await.to_api(), phkind);
|
||||
Ok((tail, x_tok(ph_atom).await.at(ctx.pos_tt(line, tail))))
|
||||
Ok((tail, x_tok(new_atom(ph_atom)).await.at(ctx.pos_tt(line, tail))))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ use orchid_extension::atom::TAtom;
|
||||
use orchid_extension::atom_owned::own;
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::coroutine_exec::{ExecHandle, exec};
|
||||
use orchid_extension::gen_expr::{GExpr, arg, bot, call, lambda, sym_ref};
|
||||
use orchid_extension::gen_expr::{GExpr, arg, bot, call, lambda, new_atom, sym_ref};
|
||||
use orchid_extension::reflection::{ReflMemKind, refl};
|
||||
use subslice_offset::SubsliceOffset;
|
||||
use substack::Substack;
|
||||
@@ -283,9 +283,9 @@ async fn mk_body_call(mac: &Macro, rule: &Rule, state: &MatchState<'_>, pos: Pos
|
||||
let mut call_args = vec![];
|
||||
for name in rule.ph_names.iter() {
|
||||
call_args.push(match state.get(name).expect("Missing state entry for placeholder") {
|
||||
StateEntry::Scalar(scal) => (**scal).clone().to_gen().await,
|
||||
StateEntry::Scalar(scal) => new_atom((**scal).clone()),
|
||||
StateEntry::Vec(vec) =>
|
||||
MacTok::S(Paren::Round, MacTreeSeq::new(vec.iter().cloned())).at(Pos::None).to_gen().await,
|
||||
new_atom(MacTok::S(Paren::Round, MacTreeSeq::new(vec.iter().cloned())).at(Pos::None)),
|
||||
});
|
||||
}
|
||||
call(sym_ref(mac.0.module.suffix([rule.body.clone()]).await), call_args).at(pos.clone())
|
||||
|
||||
@@ -123,12 +123,13 @@ async fn mk_scalar(pattern: &MacTree) -> OrcRes<ScalMatcher> {
|
||||
PhKind::Scalar => ScalMatcher::Placeh { key: name.clone() },
|
||||
},
|
||||
MacTok::S(c, body) => ScalMatcher::S(*c, Box::new(mk_any(&body.items).boxed_local().await?)),
|
||||
MacTok::Lambda(..) =>
|
||||
MacTok::Lambda(..) => {
|
||||
return Err(mk_errv(
|
||||
is("Lambda in matcher").await,
|
||||
"Lambdas can't be matched for, only generated in templates",
|
||||
[pattern.pos()],
|
||||
)),
|
||||
));
|
||||
},
|
||||
MacTok::Value(_) | MacTok::Slot => panic!("Only used for templating"),
|
||||
MacTok::Bottom(errv) => return Err(errv.clone()),
|
||||
})
|
||||
|
||||
12
orchid-std/src/macros/stdlib/funnctional.rs
Normal file
12
orchid-std/src/macros/stdlib/funnctional.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
use orchid_extension::tree::{GenMember, prefix};
|
||||
|
||||
use crate::macros::resolve::resolve;
|
||||
use crate::macros::utils::{build_macro, mactree, mactreev};
|
||||
|
||||
pub async fn gen_functional_macro_lib() -> Vec<GenMember> {
|
||||
prefix("std::fn", [build_macro(Some(4), ["|>"])
|
||||
.rule(mactreev!("...$" lhs 0 "std::fn::|>" "$" fun "...$" rhs 0), [async |[lhs, fun, rhs]| {
|
||||
resolve(mactree!(("push" fun ; "push" lhs ;) "pushv" rhs ;)).await
|
||||
}])
|
||||
.finish()])
|
||||
}
|
||||
20
orchid-std/src/macros/stdlib/mod.rs
Normal file
20
orchid-std/src/macros/stdlib/mod.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
pub mod funnctional;
|
||||
pub mod option;
|
||||
pub mod record;
|
||||
pub mod tuple;
|
||||
|
||||
use orchid_extension::tree::{GenMember, merge_trivial};
|
||||
|
||||
use crate::macros::stdlib::funnctional::gen_functional_macro_lib;
|
||||
use crate::macros::stdlib::option::gen_option_macro_lib;
|
||||
use crate::macros::stdlib::record::gen_record_macro_lib;
|
||||
use crate::macros::stdlib::tuple::gen_tuple_macro_lib;
|
||||
|
||||
pub async fn gen_std_macro_lib() -> Vec<GenMember> {
|
||||
merge_trivial([
|
||||
gen_functional_macro_lib().await,
|
||||
gen_option_macro_lib().await,
|
||||
gen_tuple_macro_lib().await,
|
||||
gen_record_macro_lib().await,
|
||||
])
|
||||
}
|
||||
56
orchid-std/src/macros/stdlib/option.rs
Normal file
56
orchid-std/src/macros/stdlib/option.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use futures::StreamExt;
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::TAtom;
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::coroutine_exec::exec;
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::gen_expr::{call, new_atom, sym_ref};
|
||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||
|
||||
use crate::macros::match_macros::MatcherAtom;
|
||||
use crate::macros::resolve::resolve;
|
||||
use crate::macros::utils::{build_macro, mactree, mactreev};
|
||||
use crate::{OrcOpt, Tpl};
|
||||
|
||||
pub async fn gen_option_macro_lib() -> Vec<GenMember> {
|
||||
prefix("std::option", [
|
||||
fun(false, "is_some_body", |sub: TAtom<MatcherAtom>, val: OrcOpt<Expr>| {
|
||||
exec(async move |mut h| {
|
||||
let Some(sub_val) = val.0 else { return Ok(OrcOpt(None)) };
|
||||
sub.run_matcher(&mut h, sub_val).await
|
||||
})
|
||||
}),
|
||||
fun(
|
||||
false,
|
||||
"is_none_body",
|
||||
async |val: OrcOpt<Expr>| {
|
||||
if val.0.is_none() { OrcOpt(Some(Tpl(()))) } else { OrcOpt(None) }
|
||||
},
|
||||
),
|
||||
build_macro(None, ["some", "none"])
|
||||
.rule(mactreev!(pattern::match_rule ( std::option::some "...$" sub_pattern 0)), [
|
||||
|[sub]: [_; _]| {
|
||||
exec(async move |mut h| {
|
||||
let sub = h
|
||||
.exec::<TAtom<MatcherAtom>>(resolve(mactree!(pattern::match_rule "push" sub;)).await)
|
||||
.await?;
|
||||
Ok(new_atom(MatcherAtom {
|
||||
keys: sub.keys().collect().await,
|
||||
matcher: h
|
||||
.register(call(sym_ref(sym!(std::option::is_some_body)), [sub.to_gen().await]))
|
||||
.await,
|
||||
}))
|
||||
})
|
||||
},
|
||||
])
|
||||
.rule(mactreev!(pattern::match_rule(std::option::none)), [|[]: [_; _]| {
|
||||
exec(async |mut h| {
|
||||
Ok(new_atom(MatcherAtom {
|
||||
keys: vec![],
|
||||
matcher: h.register(sym_ref(sym!(std::option::is_none_body))).await,
|
||||
}))
|
||||
})
|
||||
}])
|
||||
.finish(),
|
||||
])
|
||||
}
|
||||
45
orchid-std/src/macros/stdlib/record.rs
Normal file
45
orchid-std/src/macros/stdlib/record.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::TAtom;
|
||||
use orchid_extension::atom_owned::own;
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::coroutine_exec::exec;
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::gen_expr::{call, new_atom, sym_ref};
|
||||
use orchid_extension::tree::{GenMember, prefix};
|
||||
|
||||
use crate::macros::resolve::resolve;
|
||||
use crate::macros::utils::{build_macro, mactree, mactreev};
|
||||
use crate::std::string::str_atom::IntStrAtom;
|
||||
use crate::{HomoTpl, MacTree, Tpl};
|
||||
|
||||
pub async fn gen_record_macro_lib() -> Vec<GenMember> {
|
||||
prefix("std::record", [build_macro(None, ["r", "_row"])
|
||||
.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)), [new_atom(
|
||||
mactree!((macros::common::comma_list "push" elements ;)),
|
||||
)]))
|
||||
.await?;
|
||||
let mut record = sym_ref(sym!(std::record::empty));
|
||||
for item_exprh in tup.0 {
|
||||
let Tpl((key, value)) = h
|
||||
.exec::<Tpl<(TAtom<IntStrAtom>, Expr)>>(
|
||||
resolve(mactree!(std::record::_row "push" own(&item_exprh).await ;)).await,
|
||||
)
|
||||
.await?;
|
||||
record = call(sym_ref(sym!(std::record::set)), [
|
||||
record.to_gen().await,
|
||||
key.to_gen().await,
|
||||
value.to_gen().await,
|
||||
]);
|
||||
}
|
||||
Ok(record)
|
||||
})
|
||||
.await
|
||||
}])
|
||||
.rule(mactreev!(std::record::_row ( "$" name "...$" value 1 )), [async |[name, value]| {
|
||||
Ok(Tpl((resolve(name).await, resolve(value).await)))
|
||||
}])
|
||||
.finish()])
|
||||
}
|
||||
@@ -6,63 +6,21 @@ use orchid_extension::atom_owned::own;
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::coroutine_exec::exec;
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::gen_expr::{GExpr, call, sym_ref};
|
||||
use orchid_extension::gen_expr::{GExpr, call, new_atom, sym_ref};
|
||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||
|
||||
use crate::macros::match_macros::MatcherAtom;
|
||||
use crate::macros::resolve::resolve;
|
||||
use crate::macros::utils::{build_macro, mactree, mactreev};
|
||||
use crate::{HomoTpl, MacTree, OrcOpt, Tpl};
|
||||
use crate::{HomoTpl, MacTree, OrcOpt};
|
||||
|
||||
pub async fn gen_std_macro_lib() -> Vec<GenMember> {
|
||||
prefix("std", [
|
||||
prefix("option", [
|
||||
fun(false, "is_some_body", |sub: TAtom<MatcherAtom>, val: OrcOpt<Expr>| {
|
||||
exec(async move |mut h| {
|
||||
let Some(sub_val) = val.0 else { return Ok(OrcOpt(None)) };
|
||||
sub.run_matcher(&mut h, sub_val).await
|
||||
})
|
||||
}),
|
||||
fun(false, "is_none_body", async |val: OrcOpt<Expr>| {
|
||||
if val.0.is_none() { OrcOpt(Some(Tpl(()))) } else { OrcOpt(None) }
|
||||
}),
|
||||
build_macro(None, ["some", "none"])
|
||||
.rule(mactreev!(pattern::match_rule ( std::option::some "...$" sub_pattern 0)), [
|
||||
|[sub]: [_; _]| {
|
||||
exec(async move |mut h| {
|
||||
let sub = h
|
||||
.exec::<TAtom<MatcherAtom>>(
|
||||
resolve(mactree!(pattern::match_rule "push" sub;)).await,
|
||||
)
|
||||
.await?;
|
||||
Ok(MatcherAtom {
|
||||
keys: sub.keys().collect().await,
|
||||
matcher: h
|
||||
.register(call(sym_ref(sym!(std::option::is_some_body)), [sub
|
||||
.to_gen()
|
||||
.await]))
|
||||
.await,
|
||||
})
|
||||
})
|
||||
},
|
||||
])
|
||||
.rule(mactreev!(pattern::match_rule(std::option::none)), [|[]: [_; _]| {
|
||||
exec(async |mut h| {
|
||||
Ok(MatcherAtom {
|
||||
keys: vec![],
|
||||
matcher: h.register(sym_ref(sym!(std::option::is_none_body))).await,
|
||||
})
|
||||
})
|
||||
}])
|
||||
.finish(),
|
||||
]),
|
||||
prefix("tuple", [
|
||||
pub async fn gen_tuple_macro_lib() -> Vec<GenMember> {
|
||||
prefix("std::tuple", [
|
||||
build_macro(None, ["t"])
|
||||
.rule(mactreev!(std::tuple::t [ "...$" elements 0 ]), [|[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,
|
||||
new_atom(mactree!((macros::common::comma_list "push" elements ;))),
|
||||
]))
|
||||
.await?;
|
||||
let val = stream::iter(&tup.0[..])
|
||||
@@ -94,37 +52,36 @@ pub async fn gen_std_macro_lib() -> Vec<GenMember> {
|
||||
])
|
||||
.finish(),
|
||||
fun(false, "matcher_body", tuple_matcher_body),
|
||||
]),
|
||||
])
|
||||
}
|
||||
|
||||
fn parse_tpl(elements: MacTree, tail_matcher: Option<MacTree>) -> impl Future<Output = GExpr> {
|
||||
exec(async move |mut h| -> OrcRes<MatcherAtom> {
|
||||
exec(async move |mut h| -> OrcRes<GExpr> {
|
||||
let tup = h
|
||||
.exec::<HomoTpl<TAtom<MacTree>>>(call(sym_ref(sym!(macros::resolve)), [
|
||||
mactree!((macros::common::comma_list "push" elements ;)).to_gen().await,
|
||||
]))
|
||||
.exec::<HomoTpl<TAtom<MacTree>>>(call(sym_ref(sym!(macros::resolve)), [new_atom(
|
||||
mactree!((macros::common::comma_list "push" elements ;)),
|
||||
)]))
|
||||
.await?;
|
||||
let mut subs = Vec::with_capacity(tup.0.len());
|
||||
for mac_a in &tup.0[..] {
|
||||
let mac = own(mac_a).await;
|
||||
let sub = h
|
||||
.exec::<TAtom<MatcherAtom>>(call(sym_ref(sym!(macros::resolve)), [
|
||||
mactree!(pattern::match_rule ("push" mac ;)).to_gen().await,
|
||||
]))
|
||||
.exec::<TAtom<MatcherAtom>>(call(sym_ref(sym!(macros::resolve)), [new_atom(
|
||||
mactree!(pattern::match_rule ("push" mac ;)),
|
||||
)]))
|
||||
.await?;
|
||||
subs.push(sub);
|
||||
}
|
||||
let tail_matcher = match tail_matcher {
|
||||
Some(mac) => Some(
|
||||
h.exec::<TAtom<MatcherAtom>>(call(sym_ref(sym!(macros::resolve)), [
|
||||
mactree!(pattern::match_rule "push" mac ;).to_gen().await,
|
||||
]))
|
||||
h.exec::<TAtom<MatcherAtom>>(call(sym_ref(sym!(macros::resolve)), [new_atom(
|
||||
mactree!(pattern::match_rule "push" mac ;),
|
||||
)]))
|
||||
.await?,
|
||||
),
|
||||
None => None,
|
||||
};
|
||||
Ok(MatcherAtom {
|
||||
Ok(new_atom(MatcherAtom {
|
||||
keys: stream::iter(&subs[..])
|
||||
.flat_map(|t| t.keys())
|
||||
.chain(stream::iter(&tail_matcher).flat_map(|mat| mat.keys()))
|
||||
@@ -136,7 +93,7 @@ fn parse_tpl(elements: MacTree, tail_matcher: Option<MacTree>) -> impl Future<Ou
|
||||
])
|
||||
.to_expr()
|
||||
.await,
|
||||
})
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use orchid_base::name::{NameLike, Sym, VPath};
|
||||
use orchid_extension::atom::{Atomic, TAtom};
|
||||
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::gen_expr::{GExpr, sym_ref};
|
||||
use orchid_extension::gen_expr::{GExpr, new_atom, sym_ref};
|
||||
use orchid_extension::tree::{GenMember, MemKind, cnst, lazy};
|
||||
|
||||
use crate::macros::macro_value::{Macro, MacroData, Rule};
|
||||
@@ -35,7 +35,9 @@ impl OwnedAtom for MacroBodyArgCollector {
|
||||
type Refs = Never;
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||
async fn call_ref(&self, arg: orchid_extension::expr::Expr) -> GExpr {
|
||||
if !self.args.is_empty() {
|
||||
eprintln!("This is an intermediary value. It should never be copied");
|
||||
}
|
||||
self.clone().call(arg).await
|
||||
}
|
||||
async fn call(mut self, arg: orchid_extension::expr::Expr) -> GExpr {
|
||||
@@ -46,7 +48,7 @@ impl OwnedAtom for MacroBodyArgCollector {
|
||||
if self.argc == self.args.len() {
|
||||
(self.cb)(self.args).await.to_gen().await
|
||||
} else {
|
||||
self.to_gen().await
|
||||
new_atom(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,15 +86,20 @@ impl MacroBuilder {
|
||||
let argv = [].into_iter().collect_array().expect("N is 0");
|
||||
MemKind::Const(body(argv).await.to_gen().await)
|
||||
}),
|
||||
1.. => cnst(true, name, MacroBodyArgCollector {
|
||||
1.. => cnst(
|
||||
true,
|
||||
name,
|
||||
new_atom(MacroBodyArgCollector {
|
||||
argc: N,
|
||||
args: Vec::new(),
|
||||
cb: Rc::new(move |argv| {
|
||||
let arr = argv.into_iter().collect_array::<N>().expect("argc should enforce the length");
|
||||
let arr =
|
||||
argv.into_iter().collect_array::<N>().expect("argc should enforce the length");
|
||||
let body = body.clone();
|
||||
Box::pin(async move { body(arr).await.to_gen().await })
|
||||
}),
|
||||
}),
|
||||
),
|
||||
});
|
||||
self.patterns.push(pat);
|
||||
self
|
||||
@@ -103,8 +110,7 @@ impl MacroBuilder {
|
||||
let main_const = lazy(true, &format!("__macro__{name}"), async move |path| {
|
||||
let module = (Sym::new(path.split_last_seg().1.iter().cloned()).await)
|
||||
.expect("Default macro in global root");
|
||||
MemKind::Const(
|
||||
Macro(Rc::new(MacroData {
|
||||
MemKind::Const(new_atom(Macro(Rc::new(MacroData {
|
||||
canonical_name: module.suffix([is(name).await]).await,
|
||||
module,
|
||||
prio,
|
||||
@@ -128,10 +134,7 @@ impl MacroBuilder {
|
||||
})
|
||||
.collect()
|
||||
.await,
|
||||
}))
|
||||
.to_gen()
|
||||
.await,
|
||||
)
|
||||
}))))
|
||||
});
|
||||
let kw_consts = own_kws[1..].iter().flat_map(|kw| {
|
||||
lazy(true, &format!("__macro__{kw}"), async move |path| {
|
||||
@@ -186,6 +189,15 @@ macro_rules! mactreev_impl {
|
||||
$ret.push($arg);
|
||||
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $ret:ident "pushv" $arg:expr ; $($tail:tt)*) => {
|
||||
let $crate::macros::mactree::MacTok::S(_, body) = $arg.tok() else {
|
||||
panic!("pushv used with non-vec value")
|
||||
};
|
||||
for item in body.items.iter() {
|
||||
$ret.push(item.clone());
|
||||
}
|
||||
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
||||
};
|
||||
(@RECUR $ret:ident "l_" $arg:expr ; ($($body:tt)*) $($tail:tt)*) => {
|
||||
$ret.push(MacTok::Lambda(
|
||||
MacTok::Name($arg).at(orchid_base::location::Pos::Inherit),
|
||||
|
||||
25
orchid-std/src/std/binary/binary_atom.rs
Normal file
25
orchid-std/src/std/binary/binary_atom.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use std::borrow::Cow;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::AsyncWrite;
|
||||
use orchid_api_traits::Encode;
|
||||
use orchid_extension::atom::Atomic;
|
||||
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BlobAtom(pub(crate) Rc<Vec<u8>>);
|
||||
impl Atomic for BlobAtom {
|
||||
type Variant = OwnedVariant;
|
||||
type Data = ();
|
||||
}
|
||||
impl OwnedAtom for BlobAtom {
|
||||
type Refs = ();
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||
async fn serialize(&self, write: Pin<&mut (impl AsyncWrite + ?Sized)>) -> Self::Refs {
|
||||
self.0.encode(write).await.unwrap()
|
||||
}
|
||||
async fn deserialize(mut dctx: impl DeserializeCtx, _: Self::Refs) -> Self {
|
||||
Self(dctx.read::<Rc<Vec<u8>>>().await)
|
||||
}
|
||||
}
|
||||
146
orchid-std/src/std/binary/binary_lib.rs
Normal file
146
orchid-std/src/std/binary/binary_lib.rs
Normal file
@@ -0,0 +1,146 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use orchid_base::error::{OrcErrv, mk_errv};
|
||||
use orchid_base::interner::is;
|
||||
use orchid_extension::atom::TAtom;
|
||||
use orchid_extension::atom_owned::own;
|
||||
use orchid_extension::func_atom::get_arg_posv;
|
||||
use orchid_extension::gen_expr::new_atom;
|
||||
use orchid_extension::tree::{GenMember, comments, fun, prefix};
|
||||
|
||||
use crate::std::binary::binary_atom::BlobAtom;
|
||||
use crate::std::boolean::Bool;
|
||||
use crate::{Int, OrcOpt, Tpl};
|
||||
|
||||
async fn bounds_error(
|
||||
expected: String,
|
||||
blob: &BlobAtom,
|
||||
args: impl IntoIterator<Item = usize>,
|
||||
) -> OrcErrv {
|
||||
mk_errv(
|
||||
is("Index out of bounds").await,
|
||||
format!("Selected {expected} from blob of len {}", blob.0.len()),
|
||||
get_arg_posv(args).await,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn gen_binary_lib() -> Vec<GenMember> {
|
||||
prefix("std", [comments(
|
||||
["A Blob is a sequence of bytes stored and processed efficiently."],
|
||||
prefix("binary", [
|
||||
comments(
|
||||
["Appends a binary blob to another", "|type: Blob -> Blob -> Blob|"],
|
||||
fun(true, "concat", async |a: TAtom<BlobAtom>, b: TAtom<BlobAtom>| {
|
||||
new_atom(BlobAtom(Rc::new(
|
||||
own(&a).await.0.iter().chain(&own(&b).await.0[..]).copied().collect(),
|
||||
)))
|
||||
}),
|
||||
),
|
||||
comments(
|
||||
[
|
||||
"Copies out a subsection of the binary into a new blob \
|
||||
specified by starting point and length",
|
||||
"|type: Blob -> Int -> Int -> Blob|",
|
||||
],
|
||||
fun(true, "slice", async |a: TAtom<BlobAtom>, Int(start): Int, Int(len): Int| {
|
||||
let blob = own(&a).await;
|
||||
if start + len > blob.0.len() as i64 {
|
||||
return Err(bounds_error(format!("{start}+{len}"), &blob, 0..3).await);
|
||||
}
|
||||
let sub = blob.0[start as usize..(start + len) as usize].to_vec();
|
||||
Ok(new_atom(BlobAtom(Rc::new(sub))))
|
||||
}),
|
||||
),
|
||||
comments(
|
||||
[
|
||||
"Return the index where the second binary appears as a subsection of the first",
|
||||
"|type: Blob -> Blob -> std::option Int|",
|
||||
],
|
||||
fun(true, "find", async |haystack: TAtom<BlobAtom>, needle: TAtom<BlobAtom>| {
|
||||
let haystack_vec = own(&haystack).await;
|
||||
let needle_vec = own(&needle).await;
|
||||
for i in 0..haystack_vec.0.len() - needle_vec.0.len() {
|
||||
if haystack_vec.0[i..].starts_with(&needle_vec.0) {
|
||||
return OrcOpt(Some(Int(i as i64)));
|
||||
}
|
||||
}
|
||||
OrcOpt(None)
|
||||
}),
|
||||
),
|
||||
comments(
|
||||
[
|
||||
"Splits the binary into two halves at the given byte index",
|
||||
"|type: Blob -> Int -> std::tuple Blob Blob|",
|
||||
],
|
||||
fun(true, "split", async |a: TAtom<BlobAtom>, i: Int| {
|
||||
let v = own(&a).await;
|
||||
if v.0.len() < i.0 as usize {
|
||||
return Err(bounds_error(i.0.to_string(), &v, 1..2).await);
|
||||
}
|
||||
let (l, r) = v.0.split_at(i.0 as usize);
|
||||
Ok(Tpl((
|
||||
new_atom(BlobAtom(Rc::new(l.to_vec()))),
|
||||
new_atom(BlobAtom(Rc::new(r.to_vec()))),
|
||||
)))
|
||||
}),
|
||||
),
|
||||
comments(
|
||||
[
|
||||
"Takes a binary, a starting point, a length no greater than 8, and a boolean flag \
|
||||
which is true if the number is little endian. Reads a usize from \
|
||||
the specified location in the binary.",
|
||||
"|type: Blob -> Int -> Int -> Bool -> Int|",
|
||||
],
|
||||
fun(
|
||||
true,
|
||||
"get_int",
|
||||
async |bin: TAtom<BlobAtom>, Int(start): Int, Int(len): Int, Bool(le): Bool| {
|
||||
let vec = own(&bin).await;
|
||||
if start + len > vec.0.len() as i64 {
|
||||
return Err(bounds_error(format!("{start}+{len}"), &vec, 1..3).await);
|
||||
}
|
||||
if 8 < len {
|
||||
return Err(mk_errv(
|
||||
is("Too many bytes for int conversion").await,
|
||||
format!("At most 8 bytes fit into an Int, requested {len}"),
|
||||
get_arg_posv(3..4).await,
|
||||
));
|
||||
}
|
||||
let slice = &vec.0[start as usize..(start + len) as usize];
|
||||
let mut data = [0u8; 8];
|
||||
Ok(Int(if le {
|
||||
data[..len as usize].copy_from_slice(slice);
|
||||
i64::from_le_bytes(data)
|
||||
} else {
|
||||
data[(8 - len as usize)..].copy_from_slice(slice);
|
||||
i64::from_be_bytes(data)
|
||||
}))
|
||||
},
|
||||
),
|
||||
),
|
||||
comments(
|
||||
[
|
||||
"Takes a length no greater than int_bytes, a little endian flag and a number to encode. \
|
||||
Turns the least significant bytes of the given int into a binary.",
|
||||
"|type: Int -> Bool -> Int -> Blob|",
|
||||
],
|
||||
fun(true, "from_num", async |len: Int, le: Bool, val: Int| {
|
||||
if 8 < len.0 {
|
||||
return Err(mk_errv(
|
||||
is("Too many bytes for int conversion").await,
|
||||
format!("Ints are 8 bytes, attempted to write {} byte buffer", len.0),
|
||||
get_arg_posv(0..1).await,
|
||||
));
|
||||
}
|
||||
let data = if le.0 { val.0.to_le_bytes() } else { val.0.to_be_bytes() };
|
||||
let data = if le.0 { &data[..len.0 as usize] } else { &data[(8 - len.0 as usize)..] };
|
||||
Ok(new_atom(BlobAtom(Rc::new(data.to_vec()))))
|
||||
}),
|
||||
),
|
||||
comments(
|
||||
["Returns the number of bytes in a binary", "|type: Blob -> Int|"],
|
||||
fun(true, "size", async |blob: TAtom<BlobAtom>| Int(own(&blob).await.0.len() as i64)),
|
||||
),
|
||||
]),
|
||||
)])
|
||||
}
|
||||
2
orchid-std/src/std/binary/mod.rs
Normal file
2
orchid-std/src/std/binary/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod binary_atom;
|
||||
pub mod binary_lib;
|
||||
47
orchid-std/src/std/boolean.rs
Normal file
47
orchid-std/src/std/boolean.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
use orchid_api_derive::Coding;
|
||||
use orchid_base::error::OrcRes;
|
||||
use orchid_base::format::FmtUnit;
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::{Atomic, TAtom};
|
||||
use orchid_extension::atom_thin::{ThinAtom, ThinVariant};
|
||||
use orchid_extension::conv::{ToExpr, TryFromExpr};
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::gen_expr::{GExpr, sym_ref};
|
||||
use orchid_extension::tree::{GenMember, cnst, comments, fun, prefix};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding)]
|
||||
pub struct Bool(pub bool);
|
||||
impl Atomic for Bool {
|
||||
type Variant = ThinVariant;
|
||||
type Data = Self;
|
||||
}
|
||||
impl ThinAtom for Bool {
|
||||
async fn print(&self) -> FmtUnit { self.0.to_string().into() }
|
||||
}
|
||||
impl TryFromExpr for Bool {
|
||||
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
|
||||
match TAtom::<Bool>::downcast(expr.handle()).await {
|
||||
Err(e) => Err(e.mk_err().await),
|
||||
Ok(atom) => Ok(atom.value),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ToExpr for Bool {
|
||||
async fn to_gen(self) -> GExpr {
|
||||
sym_ref(if self.0 { sym!(std::true) } else { sym!(std::false) })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_bool_lib() -> Vec<GenMember> {
|
||||
prefix("std", [
|
||||
comments(
|
||||
[
|
||||
"Returns the second argument if the bool is true, the third argument otherwise",
|
||||
"|type: Bool -> T -> T -> T|",
|
||||
],
|
||||
fun(true, "ifthenelse", async |Bool(b): Bool, t: Expr, f: Expr| if b { t } else { f }),
|
||||
),
|
||||
cnst(true, "true", Bool(true)),
|
||||
cnst(true, "false", Bool(false)),
|
||||
])
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
pub mod binary;
|
||||
pub mod boolean;
|
||||
pub mod number;
|
||||
pub mod ops;
|
||||
pub mod option;
|
||||
pub mod protocol;
|
||||
pub mod record;
|
||||
|
||||
@@ -1,24 +1,42 @@
|
||||
use orchid_api_derive::Coding;
|
||||
use std::io;
|
||||
|
||||
use orchid_api_derive::{Coding, Hierarchy};
|
||||
use orchid_api_traits::Request;
|
||||
use orchid_base::error::OrcRes;
|
||||
use orchid_base::format::FmtUnit;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::number::Numeric;
|
||||
use orchid_extension::atom::{AtomFactory, Atomic, AtomicFeatures, Supports, TAtom, ToAtom};
|
||||
use orchid_base::reqnot::{Receipt, ReqHandle, ReqHandleExt};
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::{Atomic, MethodSetBuilder, Supports, TAtom};
|
||||
use orchid_extension::atom_thin::{ThinAtom, ThinVariant};
|
||||
use orchid_extension::conv::TryFromExpr;
|
||||
use orchid_extension::conv::{ToExpr, TryFromExpr};
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::gen_expr::sym_ref;
|
||||
use orchid_extension::system::sys_req;
|
||||
use ordered_float::NotNan;
|
||||
use rust_decimal::prelude::Zero;
|
||||
|
||||
use crate::std::protocol::types::GetTagIdMethod;
|
||||
use crate::std::protocol::types::{GetImpl, ProtocolMethod};
|
||||
use crate::std::std_system::StdReq;
|
||||
use crate::std::string::to_string::ToStringMethod;
|
||||
use crate::{StdSystem, api};
|
||||
|
||||
#[derive(Clone, Debug, Coding)]
|
||||
#[derive(Debug, Clone, Copy, Coding, Hash, PartialEq, Eq, Hierarchy)]
|
||||
#[extends(StdReq)]
|
||||
pub struct CreateInt(pub Int);
|
||||
impl Request for CreateInt {
|
||||
type Response = api::ExprTicket;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Coding, Hash, PartialEq, Eq)]
|
||||
pub struct Int(pub i64);
|
||||
impl Atomic for Int {
|
||||
type Variant = ThinVariant;
|
||||
type Data = Self;
|
||||
fn reg_reqs() -> MethodSetBuilder<Self> {
|
||||
MethodSetBuilder::new().handle::<ProtocolMethod>().handle::<ToStringMethod>()
|
||||
}
|
||||
}
|
||||
impl ThinAtom for Int {
|
||||
async fn print(&self) -> FmtUnit { self.0.to_string().into() }
|
||||
@@ -28,22 +46,64 @@ impl TryFromExpr for Int {
|
||||
TAtom::<Int>::try_from_expr(expr).await.map(|t| t.value)
|
||||
}
|
||||
}
|
||||
impl Supports<GetTagIdMethod> for Int {
|
||||
async fn handle(&self, _: GetTagIdMethod) -> <GetTagIdMethod as Request>::Response {
|
||||
Sym::parse("std::number::Int").await.unwrap().to_api()
|
||||
impl ToExpr for Int {
|
||||
async fn to_gen(self) -> orchid_extension::gen_expr::GExpr {
|
||||
Expr::deserialize(sys_req::<StdSystem, _>(CreateInt(self)).await).await.to_gen().await
|
||||
}
|
||||
}
|
||||
impl Supports<ProtocolMethod> for Int {
|
||||
async fn handle<'a>(
|
||||
&self,
|
||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
||||
req: ProtocolMethod,
|
||||
) -> io::Result<Receipt<'a>> {
|
||||
match req {
|
||||
ProtocolMethod::GetTagId(req) => hand.reply(&req, &sym!(std::number::Int).to_api()).await,
|
||||
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) => {
|
||||
let name = Sym::from_api(key).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 hand.reply(req, &None).await;
|
||||
};
|
||||
hand.reply(req, &Some(val.create().await.serialize().await)).await
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Supports<ToStringMethod> for Int {
|
||||
async fn handle(&self, _: ToStringMethod) -> <ToStringMethod as Request>::Response {
|
||||
self.0.to_string()
|
||||
async fn handle<'a>(
|
||||
&self,
|
||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
||||
req: ToStringMethod,
|
||||
) -> io::Result<Receipt<'a>> {
|
||||
hand.reply(&req, &self.0.to_string()).await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Coding)]
|
||||
#[derive(Clone, Copy, Debug, Coding, Hash, PartialEq, Eq, Hierarchy)]
|
||||
#[extends(StdReq)]
|
||||
pub struct CreateFloat(pub Float);
|
||||
impl Request for CreateFloat {
|
||||
type Response = api::ExprTicket;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Coding, Hash, PartialEq, Eq)]
|
||||
pub struct Float(pub NotNan<f64>);
|
||||
impl Atomic for Float {
|
||||
type Variant = ThinVariant;
|
||||
type Data = Self;
|
||||
fn reg_reqs() -> MethodSetBuilder<Self> {
|
||||
MethodSetBuilder::new().handle::<ProtocolMethod>().handle::<ToStringMethod>()
|
||||
}
|
||||
}
|
||||
impl ThinAtom for Float {
|
||||
async fn print(&self) -> FmtUnit { self.0.to_string().into() }
|
||||
@@ -53,9 +113,46 @@ impl TryFromExpr for Float {
|
||||
Ok(Self(Num::try_from_expr(expr).await?.0.to_f64()))
|
||||
}
|
||||
}
|
||||
impl ToExpr for Float {
|
||||
async fn to_gen(self) -> orchid_extension::gen_expr::GExpr {
|
||||
Expr::deserialize(sys_req::<StdSystem, _>(CreateFloat(self)).await).await.to_gen().await
|
||||
}
|
||||
}
|
||||
impl Supports<ProtocolMethod> for Float {
|
||||
async fn handle<'a>(
|
||||
&self,
|
||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
||||
req: ProtocolMethod,
|
||||
) -> io::Result<Receipt<'a>> {
|
||||
match req {
|
||||
ProtocolMethod::GetTagId(req) => hand.reply(&req, &sym!(std::number::Float).to_api()).await,
|
||||
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) => {
|
||||
let name = Sym::from_api(key).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 hand.reply(req, &None).await;
|
||||
};
|
||||
hand.reply(req, &Some(val.create().await.serialize().await)).await
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Supports<ToStringMethod> for Float {
|
||||
async fn handle(&self, _: ToStringMethod) -> <ToStringMethod as Request>::Response {
|
||||
self.0.to_string()
|
||||
async fn handle<'a>(
|
||||
&self,
|
||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
||||
req: ToStringMethod,
|
||||
) -> io::Result<Receipt<'a>> {
|
||||
hand.reply(&req, &self.0.to_string()).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,12 +169,14 @@ impl TryFromExpr for Num {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ToAtom for Num {
|
||||
fn to_atom_factory(self) -> AtomFactory {
|
||||
impl ToExpr for Num {
|
||||
async fn to_gen(self) -> orchid_extension::gen_expr::GExpr {
|
||||
match self.0 {
|
||||
Numeric::Float(f) => Float(f).factory(),
|
||||
Numeric::Int(i) => Int(i).factory(),
|
||||
Numeric::Float(f) => Float(f).to_expr().await,
|
||||
Numeric::Int(i) => Int(i).to_expr().await,
|
||||
}
|
||||
.to_gen()
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::ops::RangeInclusive;
|
||||
|
||||
use orchid_base::error::OrcRes;
|
||||
use orchid_base::number::{num_to_errv, parse_num};
|
||||
use orchid_extension::atom::ToAtom;
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::lexer::{LexContext, Lexer};
|
||||
use orchid_extension::tree::{GenTokTree, x_tok};
|
||||
|
||||
@@ -15,10 +15,10 @@ impl Lexer for NumLexer {
|
||||
async fn lex<'a>(all: &'a str, lxcx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree)> {
|
||||
let ends_at = all.find(|c: char| !c.is_ascii_hexdigit() && !"xX._pP".contains(c));
|
||||
let (chars, tail) = all.split_at(ends_at.unwrap_or(all.len()));
|
||||
let fac = match parse_num(chars) {
|
||||
Ok(numeric) => Num(numeric).to_atom_factory(),
|
||||
Err(e) => return Err(num_to_errv(e, lxcx.pos(all), lxcx.src()).await),
|
||||
};
|
||||
Ok((tail, x_tok(fac).await.at(lxcx.pos_lt(chars.len(), tail))))
|
||||
match parse_num(chars) {
|
||||
Ok(numeric) =>
|
||||
Ok((tail, x_tok(Num(numeric).to_gen().await).await.at(lxcx.pos_lt(chars.len(), tail)))),
|
||||
Err(e) => Err(num_to_errv(e, lxcx.pos(all), lxcx.src()).await),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,60 @@
|
||||
use orchid_base::error::mk_errv;
|
||||
use orchid_base::interner::is;
|
||||
use orchid_base::number::Numeric;
|
||||
use orchid_extension::func_atom::get_arg;
|
||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||
use ordered_float::NotNan;
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
|
||||
use super::num_atom::{Float, HomoArray, Int, Num};
|
||||
|
||||
pub fn gen_num_lib() -> Vec<GenMember> {
|
||||
prefix("std::number", [
|
||||
fun(true, "add", async |a: Num, b: Num| {
|
||||
fun(false, "add", async |a: Num, b: Num| {
|
||||
Num(match HomoArray::new([a.0, b.0]) {
|
||||
HomoArray::Int([a, b]) => Numeric::Int(a + b),
|
||||
HomoArray::Float([a, b]) => Numeric::Float(a + b),
|
||||
})
|
||||
}),
|
||||
fun(true, "neg", async |a: Num| {
|
||||
fun(false, "sub", async |a: Num, b: Num| {
|
||||
Num(match HomoArray::new([a.0, b.0]) {
|
||||
HomoArray::Int([a, b]) => Numeric::Int(a - b),
|
||||
HomoArray::Float([a, b]) => Numeric::Float(a - b),
|
||||
})
|
||||
}),
|
||||
fun(false, "neg", async |a: Num| {
|
||||
Num(match a.0 {
|
||||
Numeric::Int(i) => Numeric::Int(-i),
|
||||
Numeric::Float(f) => Numeric::Float(-f),
|
||||
})
|
||||
}),
|
||||
fun(true, "mul", async |a: Num, b: Num| {
|
||||
fun(false, "mul", async |a: Num, b: Num| {
|
||||
Num(match HomoArray::new([a.0, b.0]) {
|
||||
HomoArray::Int([a, b]) => Numeric::Int(a * b),
|
||||
HomoArray::Float([a, b]) => Numeric::Float(a * b),
|
||||
})
|
||||
}),
|
||||
fun(true, "idiv", async |a: Int, b: Int| Int(a.0 / b.0)),
|
||||
fun(true, "imod", async |a: Int, b: Int| Int(a.0 % b.0)),
|
||||
fun(true, "fdiv", async |a: Float, b: Float| Float(a.0 / b.0)),
|
||||
fun(true, "fmod", async |a: Float, b: Float| {
|
||||
fun(false, "idiv", async |a: Int, b: Int| Int(a.0 / b.0)),
|
||||
fun(false, "imod", async |a: Int, b: Int| Int(a.0 % b.0)),
|
||||
fun(false, "fdiv", async |a: Float, b: Float| Float(a.0 / b.0)),
|
||||
fun(false, "fmod", async |a: Float, b: Float| {
|
||||
Float(a.0 - NotNan::new((a.0 / b.0).trunc()).unwrap() * b.0)
|
||||
}),
|
||||
fun(false, "to_i", async |a: Num| {
|
||||
Ok(Int(match a.0 {
|
||||
Numeric::Int(i) => i,
|
||||
Numeric::Float(f) => match f.to_i64() {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
return Err(mk_errv(
|
||||
is("Float out of range").await,
|
||||
format!("{f} is not representable as an integer"),
|
||||
[get_arg(0).pos().await],
|
||||
));
|
||||
},
|
||||
},
|
||||
}))
|
||||
}),
|
||||
fun(false, "to_f", async |a: Num| Float(a.0.to_f64())),
|
||||
])
|
||||
}
|
||||
|
||||
38
orchid-std/src/std/ops/mod.rs
Normal file
38
orchid-std/src/std/ops/mod.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
pub mod subscript_lexer;
|
||||
|
||||
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(),
|
||||
),
|
||||
])
|
||||
}
|
||||
29
orchid-std/src/std/ops/subscript_lexer.rs
Normal file
29
orchid-std/src/std/ops/subscript_lexer.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use orchid_base::error::OrcRes;
|
||||
use orchid_base::interner::is;
|
||||
use orchid_base::parse::{name_char, name_start};
|
||||
use orchid_extension::gen_expr::new_atom;
|
||||
use orchid_extension::lexer::{LexContext, LexedData, Lexer, err_not_applicable};
|
||||
use orchid_extension::tree::GenTok;
|
||||
|
||||
use crate::std::string::str_atom::IntStrAtom;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct SubscriptLexer;
|
||||
impl Lexer for SubscriptLexer {
|
||||
const CHAR_FILTER: &'static [std::ops::RangeInclusive<char>] = &['.'..='.'];
|
||||
async fn lex<'a>(tail: &'a str, lctx: &'a LexContext<'a>) -> OrcRes<(&'a str, impl LexedData)> {
|
||||
if tail.len() <= 1 || !name_start(tail.chars().nth(1).unwrap()) {
|
||||
return Err(err_not_applicable().await);
|
||||
}
|
||||
let name_len = match tail[1..].char_indices().find(|(_, c)| !name_char(*c)) {
|
||||
None => tail.len() - 1,
|
||||
Some((pos, _)) => pos,
|
||||
};
|
||||
let new_tail = &tail[name_len + 1..];
|
||||
Ok((new_tail, [
|
||||
GenTok::Name(is(".").await).at(lctx.pos_lt(1, &tail[1..])),
|
||||
GenTok::NewExpr(new_atom(IntStrAtom(is(&tail[1..name_len + 1]).await)))
|
||||
.at(lctx.pos_tt(&tail[1..], new_tail)),
|
||||
]))
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ use orchid_extension::atom::{Atomic, ForeignAtom, TAtom};
|
||||
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
|
||||
use orchid_extension::conv::{ToExpr, TryFromExpr};
|
||||
use orchid_extension::expr::{Expr, ExprHandle};
|
||||
use orchid_extension::gen_expr::{call, sym_ref};
|
||||
use orchid_extension::gen_expr::{call, new_atom, sym_ref};
|
||||
use orchid_extension::tree::{GenMember, cnst, fun, prefix};
|
||||
|
||||
use crate::{OrcString, api};
|
||||
@@ -59,8 +59,8 @@ impl<T: ToExpr + 'static> ToExpr for OrcOpt<T> {
|
||||
|
||||
pub fn gen_option_lib() -> Vec<GenMember> {
|
||||
prefix("std::option", [
|
||||
cnst(true, "none", OptAtom(None)),
|
||||
fun(true, "some", async |ex: Expr| OptAtom(Some(ex))),
|
||||
cnst(true, "none", new_atom(OptAtom(None))),
|
||||
fun(true, "some", async |ex: Expr| new_atom(OptAtom(Some(ex)))),
|
||||
fun(true, "expect", async |opt: ForeignAtom, msg: OrcString| {
|
||||
match OrcOpt::try_from_expr(opt.clone().ex()).await? {
|
||||
OrcOpt(Some(ex)) => Ok::<Expr, _>(ex),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -7,7 +7,7 @@ use orchid_base::parse::{Comment, Parsed, expect_end, try_pop_no_fluff};
|
||||
use orchid_base::sym;
|
||||
use orchid_base::tree::Token;
|
||||
use orchid_extension::coroutine_exec::exec;
|
||||
use orchid_extension::gen_expr::{call, sym_ref};
|
||||
use orchid_extension::gen_expr::{call, new_atom, sym_ref};
|
||||
use orchid_extension::parser::{PSnippet, ParsCtx, ParsedLine, Parser};
|
||||
|
||||
use crate::std::protocol::parse_impls::parse_impls;
|
||||
@@ -44,7 +44,7 @@ impl Parser for AsProtoParser {
|
||||
for (k, v) in impls {
|
||||
new_impls.insert(k.clone(), h.register(sym_ref(id.suffix([v]).await)).await);
|
||||
}
|
||||
Tag { id, impls: Rc::new(new_impls) }
|
||||
new_atom(Tag { id, impls: Rc::new(new_impls) })
|
||||
})
|
||||
.await
|
||||
}));
|
||||
|
||||
@@ -7,7 +7,7 @@ use orchid_base::parse::{Comment, Parsed, expect_end, try_pop_no_fluff};
|
||||
use orchid_base::sym;
|
||||
use orchid_base::tree::Token;
|
||||
use orchid_extension::coroutine_exec::exec;
|
||||
use orchid_extension::gen_expr::{call, sym_ref};
|
||||
use orchid_extension::gen_expr::{call, new_atom, sym_ref};
|
||||
use orchid_extension::parser::{PSnippet, ParsCtx, ParsedLine, Parser};
|
||||
|
||||
use crate::std::protocol::parse_impls::parse_impls;
|
||||
@@ -44,7 +44,7 @@ impl Parser for AsTypeParser {
|
||||
for (k, v) in impls {
|
||||
new_impls.insert(k.clone(), h.register(sym_ref(id.suffix([v]).await)).await);
|
||||
}
|
||||
Tag { id, impls: Rc::new(new_impls) }
|
||||
new_atom(Tag { id, impls: Rc::new(new_impls) })
|
||||
})
|
||||
.await
|
||||
}));
|
||||
|
||||
@@ -1,22 +1,31 @@
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::io;
|
||||
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_base::reqnot::ReqHandleExt;
|
||||
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, new_atom, sym_ref};
|
||||
use orchid_extension::system::sys_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,32 +35,48 @@ pub struct Tag {
|
||||
impl Atomic for Tag {
|
||||
type Data = api::TStrv;
|
||||
type Variant = OwnedVariant;
|
||||
fn reg_reqs() -> MethodSetBuilder<Self> { MethodSetBuilder::new().handle::<GetImplMethod>() }
|
||||
fn reg_reqs() -> MethodSetBuilder<Self> { MethodSetBuilder::new().handle::<ProtocolMethod>() }
|
||||
}
|
||||
impl OwnedAtom for Tag {
|
||||
type Refs = Never;
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.id.to_api()) }
|
||||
}
|
||||
impl Supports<GetImplMethod> for Tag {
|
||||
async fn handle(&self, req: GetImplMethod) -> <GetImplMethod as Request>::Response {
|
||||
self.impls.get(&Sym::from_api(req.0).await).map(|expr| expr.handle().ticket())
|
||||
impl Supports<ProtocolMethod> for Tag {
|
||||
async fn handle<'a>(
|
||||
&self,
|
||||
hand: Box<dyn orchid_base::reqnot::ReqHandle<'a> + '_>,
|
||||
req: ProtocolMethod,
|
||||
) -> std::io::Result<orchid_base::reqnot::Receipt<'a>> {
|
||||
match req {
|
||||
ProtocolMethod::GetTagId(req) => hand.reply(&req, &self.id.to_api()).await,
|
||||
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) =>
|
||||
hand
|
||||
.reply(req, &self.impls.get(&Sym::from_api(key).await).map(|expr| expr.handle().ticket()))
|
||||
.await,
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, Coding)]
|
||||
pub struct GetImplMethod(pub api::TStrv);
|
||||
impl Request for GetImplMethod {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||
#[extends(ProtocolMethod)]
|
||||
pub struct GetImpl(pub api::TStrv);
|
||||
impl Request for GetImpl {
|
||||
type Response = Option<api::ExprTicket>;
|
||||
}
|
||||
impl AtomMethod for GetImplMethod {
|
||||
const NAME: &str = "std::protocol::get_impl";
|
||||
}
|
||||
#[derive(Clone, Debug, Coding)]
|
||||
pub struct GetTagIdMethod;
|
||||
impl Request for GetTagIdMethod {
|
||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||
#[extends(ProtocolMethod)]
|
||||
pub struct GetTagId;
|
||||
impl Request for GetTagId {
|
||||
type Response = api::TStrv;
|
||||
}
|
||||
impl AtomMethod for GetTagIdMethod {
|
||||
const NAME: &str = "std::protocol::get_tag_id";
|
||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||
#[extendable]
|
||||
pub enum ProtocolMethod {
|
||||
GetTagId(GetTagId),
|
||||
GetImpl(GetImpl),
|
||||
}
|
||||
impl AtomMethod for ProtocolMethod {
|
||||
const NAME: &str = "std::protocol";
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -67,39 +92,45 @@ impl OwnedAtom for Tagged {
|
||||
type Refs = Never;
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.tag.id.to_api()) }
|
||||
}
|
||||
impl Supports<GetImplMethod> for Tagged {
|
||||
async fn handle(&self, req: GetImplMethod) -> <GetImplMethod as Request>::Response {
|
||||
self.tag.handle(req).await
|
||||
impl Supports<ProtocolMethod> for Tagged {
|
||||
async fn handle<'a>(
|
||||
&self,
|
||||
hand: Box<dyn orchid_base::reqnot::ReqHandle<'a> + '_>,
|
||||
req: ProtocolMethod,
|
||||
) -> io::Result<orchid_base::reqnot::Receipt<'a>> {
|
||||
self.tag.handle(hand, req).await
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_impl(receiver: ForeignAtom, proto: ForeignAtom) -> OrcRes<Expr> {
|
||||
let Some(proto_id) = proto.request(GetTagIdMethod).await else {
|
||||
return Err(mk_errv(is("Not a protocol").await, "Protocol does not have a tag ID", [
|
||||
proto.pos()
|
||||
]));
|
||||
let Some(proto_id) = proto.request(GetTagId).await else {
|
||||
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 {
|
||||
let Some(impl_val_opt) = receiver.request(GetImpl(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()],
|
||||
));
|
||||
};
|
||||
if let Some(impl_val) = impl_val_opt {
|
||||
return Ok(Expr::deserialize(impl_val).await);
|
||||
}
|
||||
let Some(type_id) = receiver.request(GetTagIdMethod).await else {
|
||||
let Some(type_id) = receiver.request(GetTagId).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 {
|
||||
let Some(impl_val_opt) = proto.request(GetImpl(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 +139,7 @@ pub async fn get_impl(receiver: ForeignAtom, proto: ForeignAtom) -> OrcRes<Expr>
|
||||
}
|
||||
return Err(mk_errv(
|
||||
is("Implementation not found").await,
|
||||
"This protocol is not implemented for this receiver",
|
||||
format!("Protocol {} is not implemented for {}", ev(proto_id).await, ev(type_id).await),
|
||||
[receiver.pos(), proto.pos()],
|
||||
));
|
||||
}
|
||||
@@ -118,7 +149,9 @@ pub fn gen_protocol_lib() -> Vec<GenMember> {
|
||||
fun(false, "resolve", async |tag: ForeignAtom, value: ForeignAtom| {
|
||||
Ok(call(get_impl(value.clone(), tag).await?.to_gen().await, [value.to_gen().await]))
|
||||
}),
|
||||
fun(false, "wrap", async |tag: TAtom<Tag>, value: Expr| Tagged { tag: own(&tag).await, value }),
|
||||
fun(false, "wrap", async |tag: TAtom<Tag>, value: Expr| {
|
||||
new_atom(Tagged { tag: own(&tag).await, value })
|
||||
}),
|
||||
fun(false, "unwrap", async |tag: TAtom<Tag>, value: TAtom<Tagged>| {
|
||||
let own_tag = own(&tag).await;
|
||||
let own_val = own(&value).await;
|
||||
@@ -134,3 +167,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 = sys_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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,33 @@
|
||||
use std::borrow::Cow;
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::AsyncWrite;
|
||||
use futures::future::join_all;
|
||||
use hashbrown::HashMap;
|
||||
use orchid_api_traits::Encode;
|
||||
use orchid_api_derive::Coding;
|
||||
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::reqnot::{Receipt, ReqHandle, ReqHandleExt};
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::{Atomic, MethodSetBuilder, 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::{GetImpl, ProtocolMethod};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Record(pub Rc<HashMap<IStr, Expr>>);
|
||||
impl Atomic for Record {
|
||||
pub struct RecordAtom(pub Rc<HashMap<IStr, Expr>>);
|
||||
impl Atomic for RecordAtom {
|
||||
type Data = ();
|
||||
type Variant = OwnedVariant;
|
||||
fn reg_reqs() -> MethodSetBuilder<Self> { MethodSetBuilder::new().handle::<ProtocolMethod>() }
|
||||
}
|
||||
impl OwnedAtom for Record {
|
||||
impl OwnedAtom for RecordAtom {
|
||||
type Refs = Vec<Expr>;
|
||||
async fn serialize(&self, write: Pin<&mut (impl AsyncWrite + ?Sized)>) -> Self::Refs {
|
||||
let (keys, values) =
|
||||
@@ -30,8 +38,36 @@ impl OwnedAtom for Record {
|
||||
async fn deserialize(mut dctx: impl DeserializeCtx, refs: Self::Refs) -> Self {
|
||||
let keys =
|
||||
join_all(dctx.decode::<Vec<api::TStr>>().await.iter().map(|t| async { es(*t).await })).await;
|
||||
Record(Rc::new(keys.into_iter().zip(refs).collect()))
|
||||
RecordAtom(Rc::new(keys.into_iter().zip(refs).collect()))
|
||||
}
|
||||
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||
}
|
||||
impl Supports<ProtocolMethod> for RecordAtom {
|
||||
async fn handle<'a>(
|
||||
&self,
|
||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
||||
req: ProtocolMethod,
|
||||
) -> io::Result<Receipt<'a>> {
|
||||
match req {
|
||||
ProtocolMethod::GetTagId(req) => hand.reply(&req, &sym!(std::record::Record).to_api()).await,
|
||||
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) => {
|
||||
let name = Sym::from_api(key).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 hand.reply(req, &None).await;
|
||||
};
|
||||
return hand.reply(req, &Some(val.create().await.serialize().await)).await;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Coding)]
|
||||
pub struct CreateRecord(pub std::collections::HashMap<api::TStr, api::ExprTicket>);
|
||||
impl Request for CreateRecord {
|
||||
type Response = api::ExprTicket;
|
||||
}
|
||||
|
||||
@@ -1,30 +1,41 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use itertools::Itertools;
|
||||
use orchid_base::error::mk_errv;
|
||||
use orchid_base::interner::is;
|
||||
use orchid_extension::atom::TAtom;
|
||||
use orchid_extension::atom_owned::own;
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::gen_expr::{arg, new_atom};
|
||||
use orchid_extension::tree::{GenMember, cnst, fun, prefix};
|
||||
|
||||
use crate::std::option::OrcOpt;
|
||||
use crate::std::record::record_atom::Record;
|
||||
use crate::std::record::record_atom::RecordAtom;
|
||||
use crate::std::string::str_atom::IntStrAtom;
|
||||
|
||||
pub fn gen_record_lib() -> Vec<GenMember> {
|
||||
prefix("std::record", [
|
||||
cnst(true, "empty", Record(Rc::new(HashMap::new()))),
|
||||
fun(true, "set", async |map: TAtom<Record>, key: IntStrAtom, val: Expr| {
|
||||
cnst(true, "empty", new_atom(RecordAtom(Rc::new(HashMap::new())))),
|
||||
fun(true, "set", async |map: TAtom<RecordAtom>, key: IntStrAtom, val: Expr| {
|
||||
let mut map = own(&map).await.0.as_ref().clone();
|
||||
map.insert(key.0.clone(), val);
|
||||
Record(Rc::new(map))
|
||||
new_atom(RecordAtom(Rc::new(map)))
|
||||
}),
|
||||
fun(true, "get", async |map: TAtom<Record>, key: IntStrAtom| {
|
||||
OrcOpt(own(&map).await.0.get(&key.0).cloned())
|
||||
fun(true, "get", async |map: TAtom<RecordAtom>, key: IntStrAtom| {
|
||||
let record = own(&map).await;
|
||||
match record.0.get(&key.0) {
|
||||
Some(val) => Ok(val.clone()),
|
||||
None => Err(mk_errv(
|
||||
is("Key not found in record").await,
|
||||
format!("{} is not in this record, valid keys are {}", key.0, record.0.keys().join(", ")),
|
||||
[arg(0).pos.clone(), arg(1).pos.clone()],
|
||||
)),
|
||||
}
|
||||
}),
|
||||
fun(true, "delete", async |map: TAtom<Record>, key: IntStrAtom| {
|
||||
fun(true, "delete", async |map: TAtom<RecordAtom>, key: IntStrAtom| {
|
||||
let mut map = own(&map).await.0.as_ref().clone();
|
||||
map.remove(&key.0);
|
||||
Record(Rc::new(map))
|
||||
new_atom(RecordAtom(Rc::new(map)))
|
||||
}),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -5,10 +5,12 @@ use orchid_api_traits::Request;
|
||||
use orchid_base::error::mk_errv;
|
||||
use orchid_base::interner::{es, is};
|
||||
use orchid_base::name::{NameLike, Sym};
|
||||
use orchid_base::reqnot::ReqHandleExt;
|
||||
use orchid_extension::atom::{Atomic, Supports, TAtom};
|
||||
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
|
||||
use orchid_extension::expr::{Expr, ExprHandle};
|
||||
use orchid_extension::system::dep_req;
|
||||
use orchid_extension::gen_expr::new_atom;
|
||||
use orchid_extension::system::sys_req;
|
||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||
|
||||
use crate::std::std_system::StdReq;
|
||||
@@ -29,8 +31,12 @@ impl OwnedAtom for SymAtom {
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(SymAtomData(self.0.tok().to_api())) }
|
||||
}
|
||||
impl Supports<ToStringMethod> for SymAtom {
|
||||
async fn handle(&self, _: ToStringMethod) -> <ToStringMethod as Request>::Response {
|
||||
self.0.to_string()
|
||||
async fn handle<'a>(
|
||||
&self,
|
||||
hand: Box<dyn orchid_base::reqnot::ReqHandle<'a> + '_>,
|
||||
req: ToStringMethod,
|
||||
) -> std::io::Result<orchid_base::reqnot::Receipt<'a>> {
|
||||
hand.reply(&req, &self.0.to_string()).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +49,7 @@ impl Request for CreateSymAtom {
|
||||
|
||||
pub async fn sym_expr(sym: Sym) -> Expr {
|
||||
Expr::from_handle(ExprHandle::deserialize(
|
||||
dep_req::<StdSystem, _>(CreateSymAtom(sym.to_api())).await,
|
||||
sys_req::<StdSystem, _>(CreateSymAtom(sym.to_api())).await,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -51,7 +57,7 @@ pub async fn gen_sym_lib() -> Vec<GenMember> {
|
||||
prefix("std::refl::sym", [
|
||||
fun(true, "from_str", async move |str: TAtom<IntStrAtom>| {
|
||||
match Sym::parse(&es(*str).await).await {
|
||||
Ok(sym) => Ok(SymAtom(sym)),
|
||||
Ok(sym) => Ok(new_atom(SymAtom(sym))),
|
||||
Err(_) => Err(mk_errv(
|
||||
is("Cannot parse sym from empty string").await,
|
||||
"Empty string passed to std::refl::sym::from_str",
|
||||
@@ -60,7 +66,7 @@ pub async fn gen_sym_lib() -> Vec<GenMember> {
|
||||
}
|
||||
}),
|
||||
fun(true, "to_tpl", async move |sym: TAtom<SymAtom>| {
|
||||
HomoTpl(own(&sym).await.0.segs().map(IntStrAtom).collect())
|
||||
HomoTpl(own(&sym).await.0.segs().map(|seg| new_atom(IntStrAtom(seg))).collect())
|
||||
}),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -2,12 +2,14 @@ use std::rc::Rc;
|
||||
|
||||
use futures::future::join_all;
|
||||
use orchid_api_derive::{Coding, Hierarchy};
|
||||
use orchid_base::interner::es;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::reqnot::{Receipt, ReqHandle, ReqHandleExt};
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::{AtomDynfo, AtomicFeatures};
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::gen_expr::new_atom;
|
||||
use orchid_extension::lexer::LexerObj;
|
||||
use orchid_extension::parser::ParserObj;
|
||||
use orchid_extension::system::{System, SystemCard};
|
||||
@@ -17,23 +19,33 @@ use orchid_extension::tree::{GenMember, merge_trivial};
|
||||
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::binary::binary_atom::BlobAtom;
|
||||
use crate::std::binary::binary_lib::gen_binary_lib;
|
||||
use crate::std::boolean::gen_bool_lib;
|
||||
use crate::std::number::num_atom::{CreateFloat, CreateInt};
|
||||
use crate::std::number::num_lexer::NumLexer;
|
||||
use crate::std::ops::gen_ops_lib;
|
||||
use crate::std::ops::subscript_lexer::SubscriptLexer;
|
||||
use crate::std::option::{OptAtom, gen_option_lib};
|
||||
use crate::std::protocol::proto_parser::{AsProtoParser, ProtoParser};
|
||||
use crate::std::protocol::type_parser::{AsTypeParser, TypeParser};
|
||||
use crate::std::protocol::types::{Tag, Tagged, gen_protocol_lib};
|
||||
use crate::std::record::record_atom::Record;
|
||||
use crate::std::protocol::types::{CreateTag, Tag, Tagged, gen_protocol_lib};
|
||||
use crate::std::record::record_atom::{CreateRecord, RecordAtom};
|
||||
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 {
|
||||
CreateInt(CreateInt),
|
||||
CreateFloat(CreateFloat),
|
||||
CreateTag(CreateTag),
|
||||
CreateTuple(CreateTuple),
|
||||
CreateRecord(CreateRecord),
|
||||
CreateSymAtom(CreateSymAtom),
|
||||
}
|
||||
|
||||
@@ -51,38 +63,66 @@ impl SystemCard for StdSystem {
|
||||
type Req = StdReq;
|
||||
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn AtomDynfo>>> {
|
||||
[
|
||||
Some(BlobAtom::dynfo()),
|
||||
Some(Int::dynfo()),
|
||||
Some(Float::dynfo()),
|
||||
Some(StrAtom::dynfo()),
|
||||
Some(IntStrAtom::dynfo()),
|
||||
Some(OptAtom::dynfo()),
|
||||
Some(Record::dynfo()),
|
||||
Some(RecordAtom::dynfo()),
|
||||
Some(Tuple::dynfo()),
|
||||
Some(TupleBuilder::dynfo()),
|
||||
Some(Tag::dynfo()),
|
||||
Some(Tagged::dynfo()),
|
||||
Some(AsStrTag::dynfo()),
|
||||
]
|
||||
}
|
||||
}
|
||||
impl System for StdSystem {
|
||||
async fn request<'a>(xreq: Box<dyn ReqHandle<'a> + 'a>, req: Self::Req) -> Receipt<'a> {
|
||||
match req {
|
||||
StdReq::CreateInt(ref req @ CreateInt(int)) =>
|
||||
xreq.reply(req, &new_atom(int).to_expr().await.serialize().await).await.unwrap(),
|
||||
StdReq::CreateFloat(ref req @ CreateFloat(float)) =>
|
||||
xreq.reply(req, &new_atom(float).to_expr().await.serialize().await).await.unwrap(),
|
||||
StdReq::CreateTuple(ref req @ CreateTuple(ref items)) => {
|
||||
let tpl = Tuple(Rc::new(join_all(items.iter().copied().map(Expr::deserialize)).await));
|
||||
let tk = tpl.to_expr().await.serialize().await;
|
||||
let tk = new_atom(tpl).to_expr().await.serialize().await;
|
||||
xreq.reply(req, &tk).await.unwrap()
|
||||
},
|
||||
StdReq::CreateRecord(ref req @ CreateRecord(ref items)) => {
|
||||
let values =
|
||||
join_all(items.iter().map(async |(k, v)| (es(*k).await, Expr::deserialize(*v).await)))
|
||||
.await;
|
||||
let rec = RecordAtom(Rc::new(values.into_iter().collect()));
|
||||
let tk = new_atom(rec).to_expr().await.serialize().await;
|
||||
xreq.reply(req, &tk).await.unwrap()
|
||||
},
|
||||
StdReq::CreateSymAtom(ref req @ CreateSymAtom(sym_tok)) => {
|
||||
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, &new_atom(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, &new_atom(tag_atom).to_expr().await.serialize().await).await.unwrap()
|
||||
},
|
||||
}
|
||||
}
|
||||
fn lexers() -> Vec<LexerObj> { vec![&StringLexer, &NumLexer] }
|
||||
fn lexers() -> Vec<LexerObj> { vec![&StringLexer, &NumLexer, &SubscriptLexer] }
|
||||
fn parsers() -> Vec<ParserObj> { vec![&AsTypeParser, &TypeParser, &AsProtoParser, &ProtoParser] }
|
||||
async fn env() -> Vec<GenMember> {
|
||||
merge_trivial([
|
||||
gen_bool_lib(),
|
||||
gen_num_lib(),
|
||||
gen_str_lib(),
|
||||
gen_option_lib(),
|
||||
@@ -90,7 +130,11 @@ impl System for StdSystem {
|
||||
gen_tuple_lib(),
|
||||
gen_protocol_lib(),
|
||||
gen_sym_lib().await,
|
||||
gen_ops_lib(),
|
||||
gen_binary_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), sym!(std::record), sym!(std::string)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +1,47 @@
|
||||
use std::borrow::Cow;
|
||||
use std::io;
|
||||
use std::ops::Deref;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::AsyncWrite;
|
||||
use orchid_api_derive::Coding;
|
||||
use orchid_api_derive::{Coding, Hierarchy};
|
||||
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::reqnot::{Receipt, ReqHandle, ReqHandleExt};
|
||||
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::{GetImpl, ProtocolMethod};
|
||||
use crate::std::string::to_string::ToStringMethod;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Coding)]
|
||||
pub struct StringGetVal;
|
||||
impl Request for StringGetVal {
|
||||
#[derive(Copy, Clone, Debug, Coding, Hierarchy)]
|
||||
pub struct StringGetValMethod;
|
||||
impl Request for StringGetValMethod {
|
||||
type Response = Rc<String>;
|
||||
}
|
||||
impl AtomMethod for StringGetVal {
|
||||
impl AtomMethod for StringGetValMethod {
|
||||
const NAME: &str = "std::string_get_val";
|
||||
}
|
||||
impl Supports<StringGetVal> for StrAtom {
|
||||
async fn handle(&self, _: StringGetVal) -> <StringGetVal as Request>::Response { self.0.clone() }
|
||||
}
|
||||
impl Supports<ToStringMethod> for StrAtom {
|
||||
async fn handle(&self, _: ToStringMethod) -> <ToStringMethod as Request>::Response {
|
||||
self.0.as_str().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StrAtom(Rc<String>);
|
||||
impl Atomic for StrAtom {
|
||||
type Variant = OwnedVariant;
|
||||
type Data = ();
|
||||
fn reg_reqs() -> MethodSetBuilder<Self> { MethodSetBuilder::new().handle::<StringGetVal>() }
|
||||
fn reg_reqs() -> MethodSetBuilder<Self> {
|
||||
MethodSetBuilder::new()
|
||||
.handle::<StringGetValMethod>()
|
||||
.handle::<ToStringMethod>()
|
||||
.handle::<ProtocolMethod>()
|
||||
}
|
||||
}
|
||||
impl StrAtom {
|
||||
pub fn new(str: Rc<String>) -> Self { Self(str) }
|
||||
@@ -60,12 +63,53 @@ impl OwnedAtom for StrAtom {
|
||||
Self::new(Rc::new(ctx.read::<String>().await))
|
||||
}
|
||||
}
|
||||
impl Supports<StringGetValMethod> for StrAtom {
|
||||
async fn handle<'a>(
|
||||
&self,
|
||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
||||
req: StringGetValMethod,
|
||||
) -> io::Result<Receipt<'a>> {
|
||||
hand.reply(&req, &self.0).await
|
||||
}
|
||||
}
|
||||
impl Supports<ToStringMethod> for StrAtom {
|
||||
async fn handle<'a>(
|
||||
&self,
|
||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
||||
req: ToStringMethod,
|
||||
) -> io::Result<Receipt<'a>> {
|
||||
hand.reply(&req, &self.0).await
|
||||
}
|
||||
}
|
||||
impl Supports<ProtocolMethod> for StrAtom {
|
||||
async fn handle<'a>(
|
||||
&self,
|
||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
||||
req: ProtocolMethod,
|
||||
) -> io::Result<Receipt<'a>> {
|
||||
match req {
|
||||
ProtocolMethod::GetTagId(req) => hand.reply(&req, &sym!(std::string::StrAtom).to_api()).await,
|
||||
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) => {
|
||||
let name = Sym::from_api(key).await;
|
||||
let val = if name == sym!(std::ops::add) {
|
||||
sym_ref(sym!(std::string::concat))
|
||||
} else {
|
||||
return hand.reply(req, &None).await;
|
||||
};
|
||||
hand.reply(req, &Some(val.create().await.serialize().await)).await
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IntStrAtom(pub(crate) IStr);
|
||||
impl Atomic for IntStrAtom {
|
||||
type Variant = OwnedVariant;
|
||||
type Data = orchid_api::TStr;
|
||||
fn reg_reqs() -> MethodSetBuilder<Self> {
|
||||
MethodSetBuilder::new().handle::<ProtocolMethod>().handle::<ToStringMethod>()
|
||||
}
|
||||
}
|
||||
impl From<IStr> for IntStrAtom {
|
||||
fn from(value: IStr) -> Self { Self(value) }
|
||||
@@ -90,11 +134,35 @@ impl TryFromExpr for IntStrAtom {
|
||||
}
|
||||
}
|
||||
impl Supports<ToStringMethod> for IntStrAtom {
|
||||
async fn handle(&self, _: ToStringMethod) -> <ToStringMethod as Request>::Response {
|
||||
self.0.to_string()
|
||||
async fn handle<'a>(
|
||||
&self,
|
||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
||||
req: ToStringMethod,
|
||||
) -> io::Result<Receipt<'a>> {
|
||||
hand.reply(&req, &self.0.rc()).await
|
||||
}
|
||||
}
|
||||
impl Supports<ProtocolMethod> for IntStrAtom {
|
||||
async fn handle<'a>(
|
||||
&self,
|
||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
||||
req: ProtocolMethod,
|
||||
) -> io::Result<Receipt<'a>> {
|
||||
match req {
|
||||
ProtocolMethod::GetTagId(req) =>
|
||||
hand.reply(&req, &sym!(std::string::IntStrAtom).to_api()).await,
|
||||
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) => {
|
||||
let name = Sym::from_api(key).await;
|
||||
let val = if name == sym!(std::ops::add) {
|
||||
sym_ref(sym!(std::string::concat))
|
||||
} else {
|
||||
return hand.reply(req, &None).await;
|
||||
};
|
||||
hand.reply(req, &Some(val.create().await.serialize().await)).await
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OrcString {
|
||||
kind: OrcStringKind,
|
||||
@@ -109,7 +177,7 @@ impl OrcString {
|
||||
pub async fn get_string(&self) -> Rc<String> {
|
||||
match &self.kind {
|
||||
OrcStringKind::Int(tok) => es(**tok).await.rc(),
|
||||
OrcStringKind::Val(atom) => atom.request(StringGetVal).await,
|
||||
OrcStringKind::Val(atom) => atom.request(StringGetValMethod).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use orchid_base::location::SrcRange;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::sym;
|
||||
use orchid_base::tree::{Paren, wrap_tokv};
|
||||
use orchid_extension::gen_expr::sym_ref;
|
||||
use orchid_extension::gen_expr::{new_atom, sym_ref};
|
||||
use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable};
|
||||
use orchid_extension::parser::p_tree2gen;
|
||||
use orchid_extension::tree::{GenTok, GenTokTree, ref_tok, x_tok};
|
||||
@@ -99,6 +99,7 @@ impl Lexer for StringLexer {
|
||||
const CHAR_FILTER: &'static [std::ops::RangeInclusive<char>] = &['"'..='"', '`'..='`'];
|
||||
async fn lex<'a>(all: &'a str, lctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree)> {
|
||||
let Some(mut tail) = all.strip_prefix('"') else {
|
||||
eprintln!("Unrecognized start char {:?}", all.chars().next().unwrap());
|
||||
return Err(err_not_applicable().await);
|
||||
};
|
||||
let mut ret = None;
|
||||
@@ -115,8 +116,8 @@ impl Lexer for StringLexer {
|
||||
err.extend(e.clone().into_proj(ctx.src(), ctx.pos(tail) - str.len() as u32).await);
|
||||
}
|
||||
let str_val = str_val_res.unwrap_or_default();
|
||||
x_tok(IntStrAtom::from(is(&str_val).await)).await.at(ctx.pos_lt(str.len() as u32, tail))
|
||||
as GenTokTree
|
||||
let atom = new_atom(IntStrAtom::from(is(&str_val).await));
|
||||
x_tok(atom).await.at(ctx.pos_lt(str.len() as u32, tail)) as GenTokTree
|
||||
}
|
||||
let add_frag = |prev: Option<GenTokTree>, new: GenTokTree| async {
|
||||
let Some(prev) = prev else { return new };
|
||||
|
||||
@@ -1,38 +1,161 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use orchid_base::error::mk_errv;
|
||||
use orchid_base::format::fmt;
|
||||
use orchid_base::interner::is;
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::ForeignAtom;
|
||||
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::func_atom::get_arg;
|
||||
use orchid_extension::gen_expr::{call, new_atom, sym_ref};
|
||||
use orchid_extension::tree::{GenMember, comments, fun, prefix};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
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;
|
||||
use crate::{Int, OrcOpt, OrcString, Tpl};
|
||||
|
||||
pub fn gen_str_lib() -> Vec<GenMember> {
|
||||
prefix("std::string", [
|
||||
prefix("std", [comments(
|
||||
["There are two string types, IntStr and Str. Literals are always IntStr, which are quick to \
|
||||
equality-compare but may leak, so you can't generally create them at runtime.\n\n\
|
||||
All functions here operate on Unicode graphemes. This essentially means that letters with \
|
||||
added diacritics and Mandarin multi-codepoint characters are treated as a single character."],
|
||||
prefix("string", [
|
||||
comments(
|
||||
["Concatenate two strings"],
|
||||
["Concatenate two strings", "|type: Str -> Str -> Str|"],
|
||||
fun(true, "concat", async |left: OrcString, right: OrcString| {
|
||||
StrAtom::new(Rc::new(left.get_string().await.to_string() + &right.get_string().await))
|
||||
new_atom(StrAtom::new(Rc::new(
|
||||
left.get_string().await.to_string() + &right.get_string().await,
|
||||
)))
|
||||
}),
|
||||
),
|
||||
comments(
|
||||
["Converts a value to string. This function is used in interpolation. \
|
||||
[
|
||||
"Find the size of a string in bytes. Strings are stored in UTF-8. \
|
||||
This should be used to determine the computational resource utilization of strings. \
|
||||
It should not be used to determine whether to truncate text.",
|
||||
"|type: Str -> Int|",
|
||||
],
|
||||
fun(true, "size", async |s: OrcString| Int(s.get_string().await.len().try_into().unwrap())),
|
||||
),
|
||||
comments(
|
||||
[
|
||||
"Find the number of characters in a string. This can be used for example to \
|
||||
truncate text. It should not be used to limit the size of messages for security purposes.",
|
||||
"|type: Str -> Int|",
|
||||
],
|
||||
fun(true, "len", async |s: OrcString| {
|
||||
Int(s.get_string().await.graphemes(true).count().try_into().unwrap())
|
||||
}),
|
||||
),
|
||||
comments(
|
||||
[
|
||||
"Takes a string, a start and a length in graphemes. \
|
||||
Slices out the specified subsection of the string.",
|
||||
"|type: Str -> Int -> Int -> Str|",
|
||||
],
|
||||
fun(true, "slice", async |s: OrcString, Int(start): Int, Int(len): Int| {
|
||||
let str = s.get_string().await;
|
||||
if len <= 0 {
|
||||
return Ok(new_atom(StrAtom::new(Rc::default())));
|
||||
}
|
||||
let mut substr_iter = str.graphemes(true).skip(start.try_into().unwrap());
|
||||
let new_str: String =
|
||||
substr_iter.by_ref().take(usize::try_from(len).unwrap() - 1).collect();
|
||||
let Some(s) = substr_iter.next() else {
|
||||
let str_len = str.graphemes(true).count();
|
||||
return Err(mk_errv(
|
||||
is("Index out of bounds").await,
|
||||
format!("Tried to select grapheme {start}+{len} from string that only has {str_len}"),
|
||||
[get_arg(0).pos().await, get_arg(1).pos().await, get_arg(2).pos().await],
|
||||
));
|
||||
};
|
||||
Ok(new_atom(StrAtom::new(Rc::new(new_str + s))))
|
||||
}),
|
||||
),
|
||||
comments(
|
||||
[
|
||||
"If the first string contains the second then returns the index.",
|
||||
"|type: Str -> Str -> std::option Int|",
|
||||
],
|
||||
fun(true, "find", async |haystack: OrcString, needle: OrcString| {
|
||||
let haystack_str = haystack.get_string().await;
|
||||
let needle_str = needle.get_string().await;
|
||||
let mut haystack_graphs = haystack_str.graphemes(true);
|
||||
let mut index = 0;
|
||||
loop {
|
||||
let mut needle_graphs = needle_str.graphemes(true);
|
||||
// check that all chars are equal
|
||||
if haystack_graphs.clone().zip(needle_graphs.by_ref()).all(|(l, r)| l == r) {
|
||||
// if we exhausted the haystack but not the needle, we can't succeed
|
||||
if needle_graphs.next().is_some() {
|
||||
break;
|
||||
}
|
||||
return OrcOpt(Some(Int(index)));
|
||||
}
|
||||
if haystack_graphs.next().is_none() {
|
||||
break;
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
OrcOpt(None)
|
||||
}),
|
||||
),
|
||||
comments(
|
||||
[
|
||||
"Splits the string into two substrings at the nth grapheme.",
|
||||
"|type: Str -> Int -> std::tuple Str Str|",
|
||||
],
|
||||
fun(true, "split", async |s: OrcString, i: Int| {
|
||||
let str = s.get_string().await;
|
||||
let Some((i, _)) = str.grapheme_indices(true).nth(i.0.try_into().unwrap()) else {
|
||||
let len = str.graphemes(true).count();
|
||||
return Err(mk_errv(
|
||||
is("Index out of bounds").await,
|
||||
format!("Tried to split string at {}, it only has {} graphemes", i.0, len),
|
||||
[get_arg(0).pos().await, get_arg(1).pos().await],
|
||||
));
|
||||
};
|
||||
let (left, right) = str.split_at(i);
|
||||
Ok(Tpl((
|
||||
new_atom(StrAtom::new(Rc::new(left.to_string()))),
|
||||
new_atom(StrAtom::new(Rc::new(right.to_string()))),
|
||||
)))
|
||||
}),
|
||||
),
|
||||
comments(
|
||||
["Returns the nth grapheme.", "|type: Str -> Int -> Str|"],
|
||||
fun(true, "char_at", async |s: OrcString, i: Int| {
|
||||
let str = s.get_string().await;
|
||||
let Some(s) = str.graphemes(true).nth(i.0.try_into().unwrap()) else {
|
||||
let len = str.graphemes(true).count();
|
||||
return Err(mk_errv(
|
||||
is("Index out of bounds").await,
|
||||
format!("Tried to read grapheme {} from string, it only has {}", i.0, len),
|
||||
[get_arg(0).pos().await, get_arg(1).pos().await],
|
||||
));
|
||||
};
|
||||
Ok(new_atom(StrAtom::new(Rc::new(s.to_string()))))
|
||||
}),
|
||||
),
|
||||
comments(
|
||||
[
|
||||
"Converts a value to string. This function is used in interpolation. \
|
||||
It supports the std::string::to_string protocol in Orchid, \
|
||||
the std::string::to_string request in Rust, \
|
||||
and expression debug printing as a fallback (print_atom for Atomic implementors in Rust).\n\n\
|
||||
This function is infallible."],
|
||||
This function is infallible.",
|
||||
"|type: any -> Str|",
|
||||
],
|
||||
fun(true, "to_str", async |input: Expr| {
|
||||
exec(async move |mut h| {
|
||||
if let Ok(atom) = h.exec::<ForeignAtom>(input.clone()).await {
|
||||
if let Some(str) = atom.request(ToStringMethod).await {
|
||||
return StrAtom::new(Rc::new(str)).to_gen().await;
|
||||
return new_atom(StrAtom::new(Rc::new(str)));
|
||||
}
|
||||
let proto_ref = sym_ref(sym!(std::string::to_string::__protocol_tag__));
|
||||
let proto = h.exec(proto_ref).await.expect("This protocol is defined in this system");
|
||||
@@ -40,20 +163,12 @@ pub fn gen_str_lib() -> Vec<GenMember> {
|
||||
return call(cb.to_gen().await, [atom.to_gen().await]).to_gen().await;
|
||||
}
|
||||
}
|
||||
return StrAtom::new(Rc::new(fmt(&input).await)).to_gen().await;
|
||||
return new_atom(StrAtom::new(Rc::new(fmt(&input).await)));
|
||||
})
|
||||
.await
|
||||
}),
|
||||
),
|
||||
prefix("to_string", [
|
||||
cnst(true, "__type_tag__", AsStrTag),
|
||||
fun(true, "resolve", async |atom: ForeignAtom| {
|
||||
exec(async |mut h| {
|
||||
let proto = h.exec(sym_ref(sym!(std::string::to_string))).await?;
|
||||
Ok(call(get_impl(atom.clone(), proto).await?.to_gen().await, [atom.to_gen().await]))
|
||||
})
|
||||
.await
|
||||
}),
|
||||
proto(true, "to_string").finish(),
|
||||
]),
|
||||
])
|
||||
)])
|
||||
}
|
||||
|
||||
@@ -1,31 +1,9 @@
|
||||
use orchid_api_derive::Coding;
|
||||
use orchid_api_derive::{Coding, Hierarchy};
|
||||
use orchid_api_traits::Request;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_extension::atom::{AtomMethod, Atomic, MethodSetBuilder, Supports};
|
||||
use orchid_extension::atom_thin::{ThinAtom, ThinVariant};
|
||||
use orchid_extension::atom::AtomMethod;
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
#[derive(Coding, Clone, Debug)]
|
||||
/// Method version of std::string::to_string protocol for atoms
|
||||
#[derive(Coding, Clone, Debug, Hierarchy)]
|
||||
pub struct ToStringMethod;
|
||||
impl Request for ToStringMethod {
|
||||
type Response = String;
|
||||
|
||||
@@ -11,14 +11,18 @@ use orchid_api_traits::Request;
|
||||
use orchid_base::error::{OrcRes, mk_errv};
|
||||
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
|
||||
use orchid_base::interner::is;
|
||||
use orchid_extension::atom::{Atomic, TAtom};
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::reqnot::ReqHandleExt;
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::{Atomic, MethodSetBuilder, Supports, TAtom};
|
||||
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant, own};
|
||||
use orchid_extension::conv::{ToExpr, TryFromExpr};
|
||||
use orchid_extension::expr::{Expr, ExprHandle};
|
||||
use orchid_extension::gen_expr::GExpr;
|
||||
use orchid_extension::system::dep_req;
|
||||
use orchid_extension::gen_expr::{GExpr, new_atom, sym_ref};
|
||||
use orchid_extension::system::sys_req;
|
||||
use orchid_extension::tree::{GenMember, cnst, fun, prefix};
|
||||
|
||||
use crate::std::protocol::types::{GetImpl, ProtocolMethod};
|
||||
use crate::std::std_system::StdReq;
|
||||
use crate::{Int, StdSystem, api};
|
||||
|
||||
@@ -28,6 +32,9 @@ pub struct Tuple(pub(super) Rc<Vec<Expr>>);
|
||||
impl Atomic for Tuple {
|
||||
type Data = Vec<api::ExprTicket>;
|
||||
type Variant = OwnedVariant;
|
||||
fn reg_reqs() -> orchid_extension::atom::MethodSetBuilder<Self> {
|
||||
MethodSetBuilder::new().handle::<ProtocolMethod>()
|
||||
}
|
||||
}
|
||||
|
||||
impl OwnedAtom for Tuple {
|
||||
@@ -46,6 +53,28 @@ impl OwnedAtom for Tuple {
|
||||
.units_own(join_all(self.0.iter().map(|x| x.print(c))).await)
|
||||
}
|
||||
}
|
||||
impl Supports<ProtocolMethod> for Tuple {
|
||||
async fn handle<'a>(
|
||||
&self,
|
||||
hand: Box<dyn orchid_base::reqnot::ReqHandle<'a> + '_>,
|
||||
req: ProtocolMethod,
|
||||
) -> std::io::Result<orchid_base::reqnot::Receipt<'a>> {
|
||||
match req {
|
||||
ProtocolMethod::GetTagId(req) => hand.reply(&req, &sym!(std::tuple).to_api()).await,
|
||||
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) => {
|
||||
let name = Sym::from_api(key).await;
|
||||
let val = if name == sym!(std::ops::get) {
|
||||
sym_ref(sym!(std::tuple::get))
|
||||
} else if name == sym!(std::ops::set) {
|
||||
sym_ref(sym!(std::tuple::set))
|
||||
} else {
|
||||
return hand.reply(req, &None).await;
|
||||
};
|
||||
hand.reply(req, &Some(val.create().await.serialize().await)).await
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||
#[extends(StdReq)]
|
||||
@@ -69,22 +98,22 @@ impl OwnedAtom for TupleBuilder {
|
||||
async fn call(mut self, arg: Expr) -> GExpr {
|
||||
self.items.push(arg);
|
||||
if self.arity.get() == self.items.len().try_into().expect("counting up from 0") {
|
||||
Tuple(Rc::new(self.items)).to_gen().await
|
||||
new_atom(Tuple(Rc::new(self.items)))
|
||||
} else {
|
||||
self.to_gen().await
|
||||
new_atom(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_tuple_lib() -> Vec<GenMember> {
|
||||
prefix("std::tuple", [
|
||||
cnst(true, "empty", Tuple(Rc::new(Vec::new()))),
|
||||
fun(true, "one", async |item: Expr| Tuple(Rc::new(vec![item]))),
|
||||
cnst(true, "empty", new_atom(Tuple(Rc::new(Vec::new())))),
|
||||
fun(true, "one", async |item: Expr| new_atom(Tuple(Rc::new(vec![item])))),
|
||||
fun(true, "new", async |arity: TAtom<Int>| {
|
||||
if let Ok(arity) = u32::try_from(arity.value.0).and_then(|v| v.try_into()) {
|
||||
TupleBuilder { arity, items: Vec::new() }.to_gen().await
|
||||
new_atom(TupleBuilder { arity, items: Vec::new() })
|
||||
} else {
|
||||
Tuple(Rc::new(Vec::new())).to_gen().await
|
||||
new_atom(Tuple(Rc::new(Vec::new())))
|
||||
}
|
||||
}),
|
||||
fun(true, "get", async |tup: TAtom<Tuple>, idx: TAtom<Int>| {
|
||||
@@ -104,7 +133,7 @@ pub fn gen_tuple_lib() -> Vec<GenMember> {
|
||||
let mut new_vec = own(&tup).await.0.to_vec();
|
||||
if let Some(slot) = new_vec.get_mut(idx) {
|
||||
*slot = val;
|
||||
return Ok(Tuple(Rc::new(new_vec)));
|
||||
return Ok(new_atom(Tuple(Rc::new(new_vec))));
|
||||
}
|
||||
}
|
||||
return Err(mk_errv(
|
||||
@@ -117,7 +146,9 @@ pub fn gen_tuple_lib() -> Vec<GenMember> {
|
||||
Int(tup.len().try_into().expect("Tuple was created with an Int length"))
|
||||
}),
|
||||
fun(true, "cat", async |left: TAtom<Tuple>, right: TAtom<Tuple>| {
|
||||
Tuple(Rc::new(own(&left).await.0.iter().chain(own(&right).await.0.iter()).cloned().collect()))
|
||||
new_atom(Tuple(Rc::new(
|
||||
own(&left).await.0.iter().chain(own(&right).await.0.iter()).cloned().collect(),
|
||||
)))
|
||||
}),
|
||||
])
|
||||
}
|
||||
@@ -135,7 +166,7 @@ impl TryFromExpr for UntypedTuple {
|
||||
impl ToExpr for UntypedTuple {
|
||||
async fn to_gen(self) -> GExpr {
|
||||
let exprs = join_all(self.0.into_iter().map(async |expr| expr.serialize().await)).await;
|
||||
Expr::deserialize(dep_req::<StdSystem, _>(CreateTuple(exprs)).await).await.to_gen().await
|
||||
Expr::deserialize(sys_req::<StdSystem, _>(CreateTuple(exprs)).await).await.to_gen().await
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,13 @@ use tokio::time::Instant;
|
||||
pub mod parse_folder;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::pin::pin;
|
||||
use std::process::{Command, ExitCode};
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
use async_fn_stream::try_stream;
|
||||
use camino::{Utf8Path, Utf8PathBuf};
|
||||
@@ -61,7 +64,7 @@ pub struct Args {
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum Commands {
|
||||
Lex {
|
||||
#[arg(short, long)]
|
||||
#[arg()]
|
||||
file: Utf8PathBuf,
|
||||
},
|
||||
Parse {
|
||||
@@ -111,10 +114,7 @@ fn get_all_extensions<'a>(
|
||||
for ext_path in args.extension.iter() {
|
||||
let init = if cfg!(windows) {
|
||||
if ext_path.with_extension("dll").exists() {
|
||||
let dylib =
|
||||
ext_dylib(ext_path.with_extension("dll").as_std_path(), ctx.clone()).await.unwrap();
|
||||
eprintln!("Loaded DLL {ext_path}.dll");
|
||||
dylib
|
||||
ext_dylib(ext_path.with_extension("dll").as_std_path(), ctx.clone()).await.unwrap()
|
||||
} else if ext_path.with_extension("exe").exists() {
|
||||
ext_command(Command::new(ext_path.with_extension("exe").as_os_str()), ctx.clone()).await?
|
||||
} else {
|
||||
@@ -182,7 +182,7 @@ impl Spawner for SpawnerImpl {
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> io::Result<ExitCode> {
|
||||
eprintln!("Orcx was made by Lawrence Bethlenfalvy");
|
||||
eprintln!("Orcx v0.1 is free software provided without warranty.");
|
||||
let args = Args::parse();
|
||||
let exit_code = Rc::new(RefCell::new(ExitCode::SUCCESS));
|
||||
let local_set = LocalSet::new();
|
||||
@@ -190,9 +190,12 @@ async fn main() -> io::Result<ExitCode> {
|
||||
let logger = get_logger(&args);
|
||||
let logger2 = logger.clone();
|
||||
unsafe { STARTUP = Some(Instant::now()) };
|
||||
let ctx = Ctx::new(SpawnerImpl, logger2);
|
||||
let (signal_end_main, on_end_main) = futures::channel::oneshot::channel();
|
||||
let ctx1 = ctx.clone();
|
||||
local_set.spawn_local(async move {
|
||||
let ctx = &Ctx::new(SpawnerImpl, logger2);
|
||||
with_stash(async {
|
||||
let ctx = &ctx1;
|
||||
with_stash(async move {
|
||||
let extensions =
|
||||
get_all_extensions(&args, ctx).try_collect::<Vec<Extension>>().await.unwrap();
|
||||
time_print(&args, "Extensions loaded");
|
||||
@@ -202,8 +205,14 @@ async fn main() -> io::Result<ExitCode> {
|
||||
let mut file = File::open(file.as_std_path()).unwrap();
|
||||
let mut buf = String::new();
|
||||
file.read_to_string(&mut buf).unwrap();
|
||||
let lexemes = lex(is(&buf).await, sym!(usercode), &systems, ctx).await.unwrap();
|
||||
println!("{}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl::default()).await, true))
|
||||
match lex(is(&buf).await, sym!(usercode), &systems, ctx).await {
|
||||
Ok(lexemes) =>
|
||||
println!("{}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl::default()).await, true)),
|
||||
Err(e) => {
|
||||
eprintln!("{e}");
|
||||
exit_code1.replace(ExitCode::FAILURE);
|
||||
},
|
||||
}
|
||||
},
|
||||
Commands::Parse { file } => {
|
||||
let (_, systems) = init_systems(&args.system, &extensions).await.unwrap();
|
||||
@@ -431,21 +440,51 @@ async fn main() -> io::Result<ExitCode> {
|
||||
Ok(new_root) => new_root,
|
||||
};
|
||||
let expr = ExprKind::Const(sym!(usercode::entrypoint)).at(prefix_sr.pos());
|
||||
let mut xctx = ExecCtx::new(root, expr).await;
|
||||
let mut xctx = ExecCtx::new(root.clone(), expr).await;
|
||||
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!"),
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
})
|
||||
.await;
|
||||
signal_end_main.send(()).expect("cleanup should still be waiting");
|
||||
});
|
||||
with_interner(local_interner(), with_logger(logger, local_set)).await;
|
||||
let cleanup = async {
|
||||
if on_end_main.await.is_err() {
|
||||
return;
|
||||
}
|
||||
tokio::time::sleep(Duration::from_secs(2)).await;
|
||||
let mut extensions = HashMap::new();
|
||||
let systems = ctx.systems.read().await.values().filter_map(|v| v.upgrade()).collect_vec();
|
||||
let exprs = ctx.exprs.iter().collect_vec();
|
||||
for system in &systems {
|
||||
extensions.insert(system.ext().name().clone(), system.ext().clone());
|
||||
}
|
||||
if extensions.is_empty() && systems.is_empty() && exprs.is_empty() {
|
||||
return;
|
||||
}
|
||||
eprintln!("Shutdown is taking long. The following language constructs are still live:");
|
||||
eprintln!("Extensions: {}", extensions.keys().join(", "));
|
||||
for sys in &systems {
|
||||
eprintln!("System: {:?} = {}", sys.id(), sys.ctor().name())
|
||||
}
|
||||
for (rc, expr) in &exprs {
|
||||
eprintln!("{rc}x {:?} = {}", expr.id(), fmt(expr).await)
|
||||
}
|
||||
std::process::abort()
|
||||
};
|
||||
futures::future::select(
|
||||
pin!(cleanup),
|
||||
pin!(with_interner(local_interner(), with_logger(logger, local_set))),
|
||||
)
|
||||
.await;
|
||||
let x = *exit_code.borrow();
|
||||
Ok(x)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ pub struct Args {
|
||||
pub enum Commands {
|
||||
CheckApiRefs,
|
||||
Orcx {
|
||||
#[arg(long)]
|
||||
release: bool,
|
||||
#[arg(trailing_var_arg = true, num_args = 1..)]
|
||||
argv: Vec<String>,
|
||||
},
|
||||
@@ -41,7 +43,7 @@ fn main() -> io::Result<ExitCode> {
|
||||
let args = Args::parse();
|
||||
match &args.command {
|
||||
Commands::CheckApiRefs => check_api_refs(&args)?,
|
||||
Commands::Orcx { argv } => orcx(&args, argv)?,
|
||||
Commands::Orcx { release, argv } => orcx(*release, &args, argv)?,
|
||||
Commands::Orcxdb { argv } => orcxdb(&args, argv)?,
|
||||
}
|
||||
Ok(if EXIT_OK.load(Ordering::Relaxed) { ExitCode::SUCCESS } else { ExitCode::FAILURE })
|
||||
|
||||
@@ -4,17 +4,25 @@ use std::sync::atomic::Ordering;
|
||||
|
||||
use crate::{Args, EXIT_OK};
|
||||
|
||||
pub fn orcx(_args: &Args, argv: &[String]) -> io::Result<()> {
|
||||
if !Command::new("cargo").args(["build", "-p", "orchid-std", "--quiet"]).status()?.success() {
|
||||
pub fn orcx(release: bool, _args: &Args, argv: &[String]) -> io::Result<()> {
|
||||
let mut std_build_cmd = Command::new("cargo");
|
||||
std_build_cmd.args(["build", "-p", "orchid-std", "--quiet"]);
|
||||
if release {
|
||||
std_build_cmd.arg("--release");
|
||||
}
|
||||
|
||||
if !std_build_cmd.status()?.success() {
|
||||
EXIT_OK.store(false, Ordering::Relaxed);
|
||||
return Ok(());
|
||||
}
|
||||
if !Command::new("cargo")
|
||||
.args(["run", "-p", "orcx", "--quiet", "--"])
|
||||
.args(argv)
|
||||
.status()?
|
||||
.success()
|
||||
{
|
||||
let mut run_cmd = Command::new("cargo");
|
||||
run_cmd.args(["run", "-p", "orcx", "--quiet"]);
|
||||
if release {
|
||||
run_cmd.arg("--release");
|
||||
}
|
||||
run_cmd.arg("--");
|
||||
run_cmd.args(argv);
|
||||
if !run_cmd.status()?.success() {
|
||||
EXIT_OK.store(false, Ordering::Relaxed);
|
||||
}
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user