Finally figured out how I want atoms to work

This commit is contained in:
2024-06-14 19:41:08 +02:00
parent b1ab483d92
commit 93867e40c6
42 changed files with 906 additions and 241 deletions

View File

@@ -1,2 +1,4 @@
{ {
"rust-analyzer.check.command": "check",
"rust-analyzer.rustfmt.extraArgs": ["+nightly"]
} }

11
Cargo.lock generated
View File

@@ -259,6 +259,7 @@ dependencies = [
name = "orchid-api-traits" name = "orchid-api-traits"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"never",
"ordered-float", "ordered-float",
] ]
@@ -289,10 +290,14 @@ dependencies = [
"derive_destructure", "derive_destructure",
"hashbrown", "hashbrown",
"itertools", "itertools",
"never",
"orchid-api", "orchid-api",
"orchid-api-traits", "orchid-api-traits",
"orchid-base", "orchid-base",
"ordered-float", "ordered-float",
"substack",
"trait-set",
"typeid",
] ]
[[package]] [[package]]
@@ -491,6 +496,12 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "typeid"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf"
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.17.0" version = "1.17.0"

View File

@@ -12,7 +12,7 @@ pub fn derive(input: TokenStream) -> TokenStream {
let decode = decode_body(&input.data); let decode = decode_body(&input.data);
let expanded = quote! { let expanded = quote! {
impl #impl_generics orchid_api_traits::Decode for #name #ty_generics #where_clause { impl #impl_generics orchid_api_traits::Decode for #name #ty_generics #where_clause {
fn decode<R: std::io::Read>(read: &mut R) -> Self { #decode } fn decode<R: std::io::Read + ?Sized>(read: &mut R) -> Self { #decode }
} }
}; };
TokenStream::from(expanded) TokenStream::from(expanded)

View File

@@ -1,4 +1,3 @@
use proc_macro::TokenStream; use proc_macro::TokenStream;
use proc_macro2 as pm2; use proc_macro2 as pm2;
use quote::ToTokens; use quote::ToTokens;
@@ -15,7 +14,7 @@ pub fn derive(input: TokenStream) -> TokenStream {
let encode = encode_body(&input.data); let encode = encode_body(&input.data);
let expanded = quote! { let expanded = quote! {
impl #e_impl_generics orchid_api_traits::Encode for #name #e_ty_generics #e_where_clause { impl #e_impl_generics orchid_api_traits::Encode for #name #e_ty_generics #e_where_clause {
fn encode<W: std::io::Write>(&self, write: &mut W) { #encode } fn encode<W: std::io::Write + ?Sized>(&self, write: &mut W) { #encode }
} }
}; };
TokenStream::from(expanded) TokenStream::from(expanded)
@@ -65,4 +64,3 @@ fn encode_items(fields: &syn::Fields) -> Option<pm2::TokenStream> {
Some(encode_names((0..fields.len()).map(|i| pos_field_name(i, un.span())))), Some(encode_names((0..fields.len()).map(|i| pos_field_name(i, un.span())))),
} }
} }

View File

@@ -60,7 +60,7 @@ fn gen_casts(ancestry: &[pm2::TokenStream], this: &pm2::TokenStream) -> pm2::Tok
_ => false _ => false
} }
} }
} },
} }
} }
let chk = gen_chk(inter, this); let chk = gen_chk(inter, this);
@@ -75,7 +75,7 @@ fn gen_casts(ancestry: &[pm2::TokenStream], this: &pm2::TokenStream) -> pm2::Tok
_ => unreachable!("Checked above!"), _ => unreachable!("Checked above!"),
} }
} }
} },
} }
} }
let unpk = gen_unpk(inter, this); let unpk = gen_unpk(inter, this);
@@ -104,8 +104,8 @@ fn get_ancestry(input: &DeriveInput) -> Option<Vec<pm2::TokenStream>> {
match &attr.meta { match &attr.meta {
syn::Meta::List(list) => (list.tokens.clone().into_iter()) syn::Meta::List(list) => (list.tokens.clone().into_iter())
.batching(|it| { .batching(|it| {
let grp: pm2::TokenStream = it let grp: pm2::TokenStream =
.take_while(|t| { it.take_while(|t| {
if let TokenTree::Punct(punct) = t { punct.as_char() != ',' } else { true } if let TokenTree::Punct(punct) = t { punct.as_char() != ',' } else { true }
}) })
.collect(); .collect();
@@ -122,6 +122,4 @@ fn is_extendable(input: &DeriveInput) -> bool {
} }
#[test] #[test]
fn test_wtf() { fn test_wtf() { eprintln!("{}", gen_casts(&[quote!(ExtHostReq)], &quote!(BogusReq))) }
eprintln!("{}", gen_casts(&[quote!(ExtHostReq)], &quote!(BogusReq)))
}

View File

@@ -6,4 +6,5 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
never = "0.1.0"
ordered-float = "4.2" ordered-float = "4.2"

View File

@@ -6,6 +6,7 @@ use std::ops::{Range, RangeInclusive};
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use never::Never;
use ordered_float::{FloatCore, NotNan}; use ordered_float::{FloatCore, NotNan};
use crate::encode_enum; use crate::encode_enum;
@@ -13,31 +14,35 @@ use crate::encode_enum;
pub trait Decode { pub trait Decode {
/// Decode an instance from the beginning of the buffer. Return the decoded /// Decode an instance from the beginning of the buffer. Return the decoded
/// data and the remaining buffer. /// data and the remaining buffer.
fn decode<R: Read>(read: &mut R) -> Self; fn decode<R: Read + ?Sized>(read: &mut R) -> Self;
} }
pub trait Encode { pub trait Encode {
/// Append an instance of the struct to the buffer /// Append an instance of the struct to the buffer
fn encode<W: Write>(&self, write: &mut W); fn encode<W: Write + ?Sized>(&self, write: &mut W);
fn enc_vec(&self) -> Vec<u8> { fn enc_vec(&self) -> Vec<u8> {
let mut vec = Vec::new(); let mut vec = Vec::new();
self.encode(&mut vec); self.encode(&mut vec);
vec vec
} }
} }
pub trait Coding: Encode + Decode + Clone {} pub trait Coding: Encode + Decode + Clone {
fn get_decoder<T>(map: impl Fn(Self) -> T + 'static) -> impl Fn(&mut dyn Read) -> T {
move |r| map(Self::decode(r))
}
}
impl<T: Encode + Decode + Clone> Coding for T {} impl<T: Encode + Decode + Clone> Coding for T {}
macro_rules! num_impl { macro_rules! num_impl {
($number:ty, $size:expr) => { ($number:ty, $size:expr) => {
impl Decode for $number { impl Decode for $number {
fn decode<R: Read>(read: &mut R) -> Self { fn decode<R: Read + ?Sized>(read: &mut R) -> Self {
let mut bytes = [0u8; $size]; let mut bytes = [0u8; $size];
read.read_exact(&mut bytes).unwrap(); read.read_exact(&mut bytes).unwrap();
<$number>::from_be_bytes(bytes) <$number>::from_be_bytes(bytes)
} }
} }
impl Encode for $number { impl Encode for $number {
fn encode<W: Write>(&self, write: &mut W) { fn encode<W: Write + ?Sized>(&self, write: &mut W) {
write.write_all(&self.to_be_bytes()).expect("Could not write number") write.write_all(&self.to_be_bytes()).expect("Could not write number")
} }
} }
@@ -62,10 +67,10 @@ num_impl!(f32, 4);
macro_rules! nonzero_impl { macro_rules! nonzero_impl {
($name:ty) => { ($name:ty) => {
impl Decode for $name { impl Decode for $name {
fn decode<R: Read>(read: &mut R) -> Self { Self::new(Decode::decode(read)).unwrap() } fn decode<R: Read + ?Sized>(read: &mut R) -> Self { Self::new(Decode::decode(read)).unwrap() }
} }
impl Encode for $name { impl Encode for $name {
fn encode<W: Write>(&self, write: &mut W) { self.get().encode(write) } fn encode<W: Write + ?Sized>(&self, write: &mut W) { self.get().encode(write) }
} }
}; };
} }
@@ -82,16 +87,18 @@ nonzero_impl!(std::num::NonZeroI64);
nonzero_impl!(std::num::NonZeroI128); nonzero_impl!(std::num::NonZeroI128);
impl<'a, T: Encode + 'a> Encode for &'a T { impl<'a, T: Encode + 'a> Encode for &'a T {
fn encode<W: Write>(&self, write: &mut W) { (**self).encode(write) } fn encode<W: Write + ?Sized>(&self, write: &mut W) { (**self).encode(write) }
} }
impl<T: Decode + FloatCore> Decode for NotNan<T> { impl<T: Decode + FloatCore> Decode for NotNan<T> {
fn decode<R: Read>(read: &mut R) -> Self { NotNan::new(T::decode(read)).expect("Float was NaN") } fn decode<R: Read + ?Sized>(read: &mut R) -> Self {
NotNan::new(T::decode(read)).expect("Float was NaN")
}
} }
impl<T: Encode + FloatCore> Encode for NotNan<T> { impl<T: Encode + FloatCore> Encode for NotNan<T> {
fn encode<W: Write>(&self, write: &mut W) { self.as_ref().encode(write) } fn encode<W: Write + ?Sized>(&self, write: &mut W) { self.as_ref().encode(write) }
} }
impl Decode for String { impl Decode for String {
fn decode<R: Read>(read: &mut R) -> Self { fn decode<R: Read + ?Sized>(read: &mut R) -> Self {
let len = u64::decode(read).try_into().unwrap(); let len = u64::decode(read).try_into().unwrap();
let mut data = vec![0u8; len]; let mut data = vec![0u8; len];
read.read_exact(&mut data).unwrap(); read.read_exact(&mut data).unwrap();
@@ -99,37 +106,37 @@ impl Decode for String {
} }
} }
impl Encode for String { impl Encode for String {
fn encode<W: Write>(&self, write: &mut W) { fn encode<W: Write + ?Sized>(&self, write: &mut W) {
u64::try_from(self.len()).unwrap().encode(write); u64::try_from(self.len()).unwrap().encode(write);
write.write_all(self.as_bytes()).unwrap() write.write_all(self.as_bytes()).unwrap()
} }
} }
impl Encode for str { impl Encode for str {
fn encode<W: Write>(&self, write: &mut W) { fn encode<W: Write + ?Sized>(&self, write: &mut W) {
u64::try_from(self.len()).unwrap().encode(write); u64::try_from(self.len()).unwrap().encode(write);
write.write_all(self.as_bytes()).unwrap() write.write_all(self.as_bytes()).unwrap()
} }
} }
impl<T: Decode> Decode for Vec<T> { impl<T: Decode> Decode for Vec<T> {
fn decode<R: Read>(read: &mut R) -> Self { fn decode<R: Read + ?Sized>(read: &mut R) -> Self {
let len = u64::decode(read).try_into().unwrap(); let len = u64::decode(read).try_into().unwrap();
iter::repeat_with(|| T::decode(read)).take(len).collect() iter::repeat_with(|| T::decode(read)).take(len).collect()
} }
} }
impl<T: Encode> Encode for Vec<T> { impl<T: Encode> Encode for Vec<T> {
fn encode<W: Write>(&self, write: &mut W) { fn encode<W: Write + ?Sized>(&self, write: &mut W) {
u64::try_from(self.len()).unwrap().encode(write); u64::try_from(self.len()).unwrap().encode(write);
self.iter().for_each(|t| t.encode(write)); self.iter().for_each(|t| t.encode(write));
} }
} }
impl<T: Encode> Encode for [T] { impl<T: Encode> Encode for [T] {
fn encode<W: Write>(&self, write: &mut W) { fn encode<W: Write + ?Sized>(&self, write: &mut W) {
u64::try_from(self.len()).unwrap().encode(write); u64::try_from(self.len()).unwrap().encode(write);
self.iter().for_each(|t| t.encode(write)); self.iter().for_each(|t| t.encode(write));
} }
} }
impl<T: Decode> Decode for Option<T> { impl<T: Decode> Decode for Option<T> {
fn decode<R: Read>(read: &mut R) -> Self { fn decode<R: Read + ?Sized>(read: &mut R) -> Self {
match u8::decode(read) { match u8::decode(read) {
0 => None, 0 => None,
1 => Some(T::decode(read)), 1 => Some(T::decode(read)),
@@ -138,14 +145,14 @@ impl<T: Decode> Decode for Option<T> {
} }
} }
impl<T: Encode> Encode for Option<T> { impl<T: Encode> Encode for Option<T> {
fn encode<W: Write>(&self, write: &mut W) { fn encode<W: Write + ?Sized>(&self, write: &mut W) {
let t = if let Some(t) = self { t } else { return 0u8.encode(write) }; let t = if let Some(t) = self { t } else { return 0u8.encode(write) };
1u8.encode(write); 1u8.encode(write);
t.encode(write); t.encode(write);
} }
} }
impl<T: Decode, E: Decode> Decode for Result<T, E> { impl<T: Decode, E: Decode> Decode for Result<T, E> {
fn decode<R: Read>(read: &mut R) -> Self { fn decode<R: Read + ?Sized>(read: &mut R) -> Self {
match u8::decode(read) { match u8::decode(read) {
0 => Self::Ok(T::decode(read)), 0 => Self::Ok(T::decode(read)),
1 => Self::Err(E::decode(read)), 1 => Self::Err(E::decode(read)),
@@ -155,7 +162,7 @@ impl<T: Decode, E: Decode> Decode for Result<T, E> {
} }
impl<T: Encode, E: Encode> Encode for Result<T, E> { impl<T: Encode, E: Encode> Encode for Result<T, E> {
fn encode<W: Write>(&self, write: &mut W) { fn encode<W: Write + ?Sized>(&self, write: &mut W) {
match self { match self {
Ok(t) => encode_enum(write, 0, |w| t.encode(w)), Ok(t) => encode_enum(write, 0, |w| t.encode(w)),
Err(e) => encode_enum(write, 1, |w| e.encode(w)), Err(e) => encode_enum(write, 1, |w| e.encode(w)),
@@ -163,13 +170,13 @@ impl<T: Encode, E: Encode> Encode for Result<T, E> {
} }
} }
impl<K: Decode + Eq + Hash, V: Decode> Decode for HashMap<K, V> { impl<K: Decode + Eq + Hash, V: Decode> Decode for HashMap<K, V> {
fn decode<R: Read>(read: &mut R) -> Self { fn decode<R: Read + ?Sized>(read: &mut R) -> Self {
let len = u64::decode(read).try_into().unwrap(); let len = u64::decode(read).try_into().unwrap();
iter::repeat_with(|| <(K, V)>::decode(read)).take(len).collect() iter::repeat_with(|| <(K, V)>::decode(read)).take(len).collect()
} }
} }
impl<K: Encode + Eq + Hash, V: Encode> Encode for HashMap<K, V> { impl<K: Encode + Eq + Hash, V: Encode> Encode for HashMap<K, V> {
fn encode<W: Write>(&self, write: &mut W) { fn encode<W: Write + ?Sized>(&self, write: &mut W) {
u64::try_from(self.len()).unwrap().encode(write); u64::try_from(self.len()).unwrap().encode(write);
self.iter().for_each(|pair| pair.encode(write)); self.iter().for_each(|pair| pair.encode(write));
} }
@@ -177,10 +184,10 @@ impl<K: Encode + Eq + Hash, V: Encode> Encode for HashMap<K, V> {
macro_rules! tuple { macro_rules! tuple {
(($($t:ident)*) ($($T:ident)*)) => { (($($t:ident)*) ($($T:ident)*)) => {
impl<$($T: Decode),*> Decode for ($($T,)*) { impl<$($T: Decode),*> Decode for ($($T,)*) {
fn decode<R: Read>(read: &mut R) -> Self { ($($T::decode(read),)*) } fn decode<R: Read + ?Sized>(read: &mut R) -> Self { ($($T::decode(read),)*) }
} }
impl<$($T: Encode),*> Encode for ($($T,)*) { impl<$($T: Encode),*> Encode for ($($T,)*) {
fn encode<W: Write>(&self, write: &mut W) { fn encode<W: Write + ?Sized>(&self, write: &mut W) {
let ($($t,)*) = self; let ($($t,)*) = self;
$( $t.encode(write); )* $( $t.encode(write); )*
} }
@@ -206,41 +213,49 @@ tuple!((t u v x y z a b c d e f g h i) (T U V X Y Z A B C D E F G H I));
tuple!((t u v x y z a b c d e f g h i j) (T U V X Y Z A B C D E F G H I J)); // 16 tuple!((t u v x y z a b c d e f g h i j) (T U V X Y Z A B C D E F G H I J)); // 16
impl Decode for () { impl Decode for () {
fn decode<R: Read>(_: &mut R) -> Self {} fn decode<R: Read + ?Sized>(_: &mut R) -> Self {}
} }
impl Encode for () { impl Encode for () {
fn encode<W: Write>(&self, _: &mut W) {} fn encode<W: Write + ?Sized>(&self, _: &mut W) {}
}
impl Decode for Never {
fn decode<R: Read + ?Sized>(_: &mut R) -> Self {
unreachable!("A value of Never cannot exist so it can't have been serialized");
}
}
impl Encode for Never {
fn encode<W: Write + ?Sized>(&self, _: &mut W) { match *self {} }
} }
impl Decode for bool { impl Decode for bool {
fn decode<R: Read>(read: &mut R) -> Self { fn decode<R: Read + ?Sized>(read: &mut R) -> Self {
let mut buf = [0]; let mut buf = [0];
read.read_exact(&mut buf).unwrap(); read.read_exact(&mut buf).unwrap();
buf[0] != 0 buf[0] != 0
} }
} }
impl Encode for bool { impl Encode for bool {
fn encode<W: Write>(&self, write: &mut W) { fn encode<W: Write + ?Sized>(&self, write: &mut W) {
write.write_all(&[if *self { 0xff } else { 0 }]).unwrap() write.write_all(&[if *self { 0xff } else { 0 }]).unwrap()
} }
} }
impl<T: Decode, const N: usize> Decode for [T; N] { impl<T: Decode, const N: usize> Decode for [T; N] {
fn decode<R: Read>(read: &mut R) -> Self { fn decode<R: Read + ?Sized>(read: &mut R) -> Self {
// TODO: figure out how to do this in safe rust on the stack // TODO: figure out how to do this in safe rust on the stack
((0..N).map(|_| T::decode(read)).collect::<Vec<_>>().try_into()) ((0..N).map(|_| T::decode(read)).collect::<Vec<_>>().try_into())
.unwrap_or_else(|_| unreachable!("The length of this iterator is statically known")) .unwrap_or_else(|_| unreachable!("The length of this iterator is statically known"))
} }
} }
impl<T: Encode, const N: usize> Encode for [T; N] { impl<T: Encode, const N: usize> Encode for [T; N] {
fn encode<W: Write>(&self, write: &mut W) { self.iter().for_each(|t| t.encode(write)) } fn encode<W: Write + ?Sized>(&self, write: &mut W) { self.iter().for_each(|t| t.encode(write)) }
} }
macro_rules! two_end_range { macro_rules! two_end_range {
($this:ident, $name:tt, $op:tt, $start:expr, $end:expr) => { ($this:ident, $name:tt, $op:tt, $start:expr, $end:expr) => {
impl<T: Decode> Decode for $name<T> { impl<T: Decode> Decode for $name<T> {
fn decode<R: Read>(read: &mut R) -> Self { T::decode(read) $op T::decode(read) } fn decode<R: Read + ?Sized>(read: &mut R) -> Self { T::decode(read) $op T::decode(read) }
} }
impl<T: Encode> Encode for $name<T> { impl<T: Encode> Encode for $name<T> {
fn encode<W: Write>(&self, write: &mut W) { fn encode<W: Write + ?Sized>(&self, write: &mut W) {
let $this = self; let $this = self;
($start).encode(write); ($start).encode(write);
($end).encode(write); ($end).encode(write);
@@ -255,10 +270,10 @@ two_end_range!(x, RangeInclusive, ..=, x.start(), x.end());
macro_rules! smart_ptr { macro_rules! smart_ptr {
($name:tt) => { ($name:tt) => {
impl<T: Decode> Decode for $name<T> { impl<T: Decode> Decode for $name<T> {
fn decode<R: Read>(read: &mut R) -> Self { $name::new(T::decode(read)) } fn decode<R: Read + ?Sized>(read: &mut R) -> Self { $name::new(T::decode(read)) }
} }
impl<T: Encode> Encode for $name<T> { impl<T: Encode> Encode for $name<T> {
fn encode<W: Write>(&self, write: &mut W) { (**self).encode(write) } fn encode<W: Write + ?Sized>(&self, write: &mut W) { (**self).encode(write) }
} }
}; };
} }
@@ -268,8 +283,8 @@ smart_ptr!(Rc);
smart_ptr!(Box); smart_ptr!(Box);
impl Decode for char { impl Decode for char {
fn decode<R: Read>(read: &mut R) -> Self { char::from_u32(u32::decode(read)).unwrap() } fn decode<R: Read + ?Sized>(read: &mut R) -> Self { char::from_u32(u32::decode(read)).unwrap() }
} }
impl Encode for char { impl Encode for char {
fn encode<W: Write>(&self, write: &mut W) { (*self as u32).encode(write) } fn encode<W: Write + ?Sized>(&self, write: &mut W) { (*self as u32).encode(write) }
} }

View File

@@ -2,16 +2,16 @@ use std::io::{Read, Write};
use crate::Encode; use crate::Encode;
pub fn encode_enum<W: Write>(write: &mut W, id: u8, f: impl FnOnce(&mut W)) { pub fn encode_enum<W: Write + ?Sized>(write: &mut W, id: u8, f: impl FnOnce(&mut W)) {
id.encode(write); id.encode(write);
f(write) f(write)
} }
pub fn write_exact(write: &mut impl Write, bytes: &'static [u8]) { pub fn write_exact<W: Write + ?Sized>(write: &mut W, bytes: &'static [u8]) {
write.write_all(bytes).expect("Failed to write exact bytes") write.write_all(bytes).expect("Failed to write exact bytes")
} }
pub fn read_exact(read: &mut impl Read, bytes: &'static [u8]) { pub fn read_exact<R: Read + ?Sized>(read: &mut R, bytes: &'static [u8]) {
let mut data = vec![0u8; bytes.len()]; let mut data = vec![0u8; bytes.len()];
read.read_exact(&mut data).expect("Failed to read bytes"); read.read_exact(&mut data).expect("Failed to read bytes");
assert_eq!(&data, bytes, "Wrong bytes") assert_eq!(&data, bytes, "Wrong bytes")

View File

@@ -7,6 +7,14 @@ use crate::system::SysId;
pub type AtomData = Vec<u8>; pub type AtomData = Vec<u8>;
/// An atom owned by an implied system. Usually used in responses from a system.
/// This has the same semantics as [Atom] except in that the owner is implied.
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
pub struct LocalAtom {
pub drop: bool,
pub data: AtomData,
}
/// An atom representation that can be serialized and sent around. Atoms /// An atom representation that can be serialized and sent around. Atoms
/// represent the smallest increment of work. /// represent the smallest increment of work.
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)] #[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]

View File

@@ -1 +1,35 @@
use orchid_api_derive::Coding;
use crate::intern::TStr;
use crate::location::Location;
pub type ProjErrId = u16; pub type ProjErrId = u16;
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
pub struct ProjErrLocation {
/// Description of the relation of this location to the error. If not used,
/// set to empty string
message: String,
/// Location in code where the error emerged. This is usually [Location::Gen].
location: Location,
}
/// Programming errors raised by extensions. At runtime these produce the
/// equivalent of a Haskell bottom. Note that runtime errors produced by
/// fallible operations should return an Orchid result and not a bottom.
/// For example, file reading produces result::err when the file doesn't exist,
/// and a bottom if the file name isn't a string.
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
pub struct ProjErr {
/// General description of the kind of error.
description: TStr,
/// Specific information about the exact error, preferably containing concrete
/// values.
message: String,
/// Specific code fragments that have contributed to the emergence of the
/// error.
locations: Vec<ProjErrLocation>,
}
/// If this is an [`Err`] then the [`Vec`] must not be empty.
pub type ProjResult<T> = Result<T, Vec<ProjErr>>;

View File

@@ -1,7 +1,7 @@
use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request; use orchid_api_traits::Request;
use crate::atom::Atom; use crate::atom::LocalAtom;
use crate::intern::{TStr, TStrv}; use crate::intern::{TStr, TStrv};
use crate::location::Location; use crate::location::Location;
use crate::proto::{ExtHostNotif, ExtHostReq}; use crate::proto::{ExtHostNotif, ExtHostReq};
@@ -67,9 +67,13 @@ pub enum Clause {
/// The lhs must be fully processed before the rhs can be processed. /// The lhs must be fully processed before the rhs can be processed.
/// Equivalent to Haskell's function of the same name /// Equivalent to Haskell's function of the same name
Seq(Box<Expr>, Box<Expr>), Seq(Box<Expr>, Box<Expr>),
/// Insert an atom in the tree. When the clause is used in the const tree, the /// Insert a new atom in the tree. When the clause is used in the const tree,
/// atom must be trivial. /// the atom must be trivial. This is always a newly constructed atom, if you
Atom(Atom), /// want to reference an existing atom, use the corresponding [ExprTicket].
/// Because the atom is newly constructed, it also must belong to this system.
/// For convenience, [SysId::MAX] is also accepted as referring to this
/// system.
Atom(LocalAtom),
/// A reference to a constant /// A reference to a constant
Const(TStrv), Const(TStrv),
} }
@@ -77,7 +81,7 @@ pub enum Clause {
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)] #[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
pub struct Expr { pub struct Expr {
pub clause: Clause, pub clause: Clause,
pub location: Location pub location: Location,
} }
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)] #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]

View File

@@ -1,16 +0,0 @@
use orchid_api_derive::Coding;
use orchid_api_traits::Request;
pub type FsId = u16;
#[derive(Clone, Debug, Coding)]
pub enum Loaded {
Code(String),
Collection(Vec<String>),
}
#[derive(Clone, Debug, Coding)]
pub struct FsRead(pub Vec<String>);
impl Request for FsRead {
type Response = Result<Loaded, ()>;
}

View File

@@ -1,10 +1,10 @@
pub mod atom; pub mod atom;
pub mod error; pub mod error;
pub mod expr; pub mod expr;
pub mod fs;
pub mod intern; pub mod intern;
pub mod location; pub mod location;
pub mod parser;
pub mod proto; pub mod proto;
pub mod system; pub mod system;
pub mod tree; pub mod tree;
pub mod parser; pub mod vfs;

View File

@@ -2,11 +2,14 @@ use std::ops::Range;
use orchid_api_derive::Coding; use orchid_api_derive::Coding;
use crate::intern::TStrv; use crate::intern::{TStr, TStrv};
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)] #[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
pub enum Location { pub enum Location {
None, None,
/// Used in functions to denote the generated code that carries on the
/// location of the call. Not allowed in the const tree.
Inherit,
Gen(CodeGenInfo), Gen(CodeGenInfo),
Range(SourceRange), Range(SourceRange),
} }
@@ -19,6 +22,6 @@ pub struct SourceRange {
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)] #[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
pub struct CodeGenInfo { pub struct CodeGenInfo {
pub generator: String, pub generator: TStr,
pub details: String, pub details: TStr,
} }

View File

@@ -3,63 +3,50 @@ use std::ops::RangeInclusive;
use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request; use orchid_api_traits::Request;
use crate::error::ProjResult;
use crate::intern::TStr; use crate::intern::TStr;
use crate::proto::{ExtHostReq, HostExtReq}; use crate::proto::{ExtHostReq, HostExtReq};
use crate::system::SysId; use crate::system::SysId;
use crate::tree::TokenTree; use crate::tree::TokenTree;
/// - All ranges contain at least one character
/// - All ranges are in increasing characeter order
/// - There are excluded characters between each pair of neighboring ranges
#[derive(Clone, Debug, Coding)]
pub struct CharFilter(pub Vec<RangeInclusive<char>>);
#[derive(Clone, Debug, Coding, Hierarchy)] #[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(HostExtReq)] #[extends(HostExtReq)]
#[extendable] #[extendable]
pub enum ParserReq { pub enum ParserReq {
MkLexer(MkLexer),
Lex(Lex), Lex(Lex),
} }
pub type LexerId = u16;
#[derive(Clone, Debug, Coding)]
pub struct Lexer {
id: LexerId,
drop: bool,
notify_chars: Option<Vec<RangeInclusive<char>>>,
}
#[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(ParserReq, HostExtReq)]
pub struct MkLexer(pub SysId, pub TStr);
impl Request for MkLexer {
type Response = Lexer;
}
#[derive(Clone, Debug, Coding, Hierarchy)] #[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(ParserReq, HostExtReq)] #[extends(ParserReq, HostExtReq)]
pub struct Lex { pub struct Lex {
pub parser: LexerId, pub sys: SysId,
pub next: char, pub text: TStr,
pub pos: u32, pub pos: u32,
} }
impl Request for Lex { impl Request for Lex {
type Response = Option<LexResult>; type Response = Option<ProjResult<Lexed>>;
} }
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
pub struct LexResult { pub struct Lexed {
pub consumed: u32, pub pos: u32,
pub data: TokenTree, pub data: TokenTree,
} }
#[derive(Clone, Debug, Coding, Hierarchy)] #[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(ExtHostReq)] #[extends(ExtHostReq)]
pub struct SubLex { pub struct SubLex {
pub lexer: LexerId, pub text: TStr,
pub pos: u32, pub pos: u32,
} }
impl Request for SubLex { impl Request for SubLex {
type Response = SubLex; type Response = ProjResult<Lexed>;
} }
#[derive(Clone, Debug, Coding)]
pub struct LexerDrop;
pub struct ParseLine {} pub struct ParseLine {}

View File

@@ -27,18 +27,18 @@ use std::io::{Read, Write};
use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::{read_exact, write_exact, Channel, Decode, Encode, MsgSet, Request}; use orchid_api_traits::{read_exact, write_exact, Channel, Decode, Encode, MsgSet, Request};
use crate::{atom, expr, intern, parser, system, tree}; use crate::{atom, expr, intern, parser, system, tree, vfs};
static HOST_INTRO: &[u8] = b"Orchid host, binary API v0\n"; static HOST_INTRO: &[u8] = b"Orchid host, binary API v0\n";
pub struct HostHeader; pub struct HostHeader;
impl Decode for HostHeader { impl Decode for HostHeader {
fn decode<R: Read>(read: &mut R) -> Self { fn decode<R: Read + ?Sized>(read: &mut R) -> Self {
read_exact(read, HOST_INTRO); read_exact(read, HOST_INTRO);
Self Self
} }
} }
impl Encode for HostHeader { impl Encode for HostHeader {
fn encode<W: Write>(&self, write: &mut W) { write_exact(write, HOST_INTRO) } fn encode<W: Write + ?Sized>(&self, write: &mut W) { write_exact(write, HOST_INTRO) }
} }
static EXT_INTRO: &[u8] = b"Orchid extension, binary API v0\n"; static EXT_INTRO: &[u8] = b"Orchid extension, binary API v0\n";
@@ -46,13 +46,13 @@ pub struct ExtensionHeader {
pub systems: Vec<system::SystemDecl>, pub systems: Vec<system::SystemDecl>,
} }
impl Decode for ExtensionHeader { impl Decode for ExtensionHeader {
fn decode<R: Read>(read: &mut R) -> Self { fn decode<R: Read + ?Sized>(read: &mut R) -> Self {
read_exact(read, EXT_INTRO); read_exact(read, EXT_INTRO);
Self { systems: Vec::decode(read) } Self { systems: Vec::decode(read) }
} }
} }
impl Encode for ExtensionHeader { impl Encode for ExtensionHeader {
fn encode<W: Write>(&self, write: &mut W) { fn encode<W: Write + ?Sized>(&self, write: &mut W) {
write_exact(write, EXT_INTRO); write_exact(write, EXT_INTRO);
self.systems.encode(write) self.systems.encode(write)
} }
@@ -76,8 +76,8 @@ pub enum ExtHostReq {
} }
/// Notifications sent from the extension to the host /// Notifications sent from the extension to the host
#[derive(Debug, Clone, Coding, Hierarchy)]
#[allow(clippy::enum_variant_names)] #[allow(clippy::enum_variant_names)]
#[derive(Debug, Clone, Coding, Hierarchy)]
#[extendable] #[extendable]
pub enum ExtHostNotif { pub enum ExtHostNotif {
ExprNotif(expr::ExprNotif), ExprNotif(expr::ExprNotif),
@@ -99,6 +99,7 @@ pub enum HostExtReq {
AtomReq(atom::AtomReq), AtomReq(atom::AtomReq),
ParserReq(parser::ParserReq), ParserReq(parser::ParserReq),
GetConstTree(tree::GetConstTree), GetConstTree(tree::GetConstTree),
VfsReq(vfs::VfsReq),
} }
/// Notifications sent from the host to the extension /// Notifications sent from the host to the extension
@@ -107,7 +108,6 @@ pub enum HostExtReq {
pub enum HostExtNotif { pub enum HostExtNotif {
SystemDrop(system::SystemDrop), SystemDrop(system::SystemDrop),
AtomDrop(atom::AtomDrop), AtomDrop(atom::AtomDrop),
LexerDrop(parser::LexerDrop),
/// The host can assume that after this notif is sent, a correctly written /// The host can assume that after this notif is sent, a correctly written
/// extension will eventually exit. /// extension will eventually exit.
Exit, Exit,

View File

@@ -2,6 +2,7 @@ use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request; use orchid_api_traits::Request;
use ordered_float::NotNan; use ordered_float::NotNan;
use crate::parser::CharFilter;
use crate::proto::{HostExtNotif, HostExtReq}; use crate::proto::{HostExtNotif, HostExtReq};
/// ID of a system type /// ID of a system type
@@ -46,7 +47,15 @@ pub struct NewSystem {
pub depends: Vec<SysId>, pub depends: Vec<SysId>,
} }
impl Request for NewSystem { impl Request for NewSystem {
type Response = (); type Response = SystemInst;
}
#[derive(Clone, Debug, Coding)]
pub struct SystemInst {
/// The set of possible starting characters of tokens the lexer of this system
/// can process. The lexer will notify this system if it encounters one of
/// these characters.9
pub lex_filter: CharFilter,
} }
#[derive(Clone, Debug, Coding, Hierarchy)] #[derive(Clone, Debug, Coding, Hierarchy)]

45
orchid-api/src/vfs.rs Normal file
View File

@@ -0,0 +1,45 @@
use std::collections::HashMap;
use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request;
use crate::error::ProjResult;
use crate::intern::TStr;
use crate::proto::HostExtReq;
use crate::system::SysId;
pub type VfsId = u16;
#[derive(Clone, Debug, Coding)]
pub enum Loaded {
Code(String),
Collection(Vec<TStr>),
}
#[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(VfsReq, HostExtReq)]
pub struct VfsRead(pub SysId, pub VfsId, pub Vec<TStr>);
impl Request for VfsRead {
type Response = ProjResult<Loaded>;
}
#[derive(Clone, Debug, Coding)]
pub enum EagerVfs {
Lazy(VfsId),
Eager(HashMap<TStr, EagerVfs>),
}
#[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(VfsReq, HostExtReq)]
pub struct GetVfs(pub SysId);
impl Request for GetVfs {
type Response = EagerVfs;
}
#[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(HostExtReq)]
#[extendable]
pub enum VfsReq {
GetVfs(GetVfs),
VfsRead(VfsRead),
}

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,45 @@
use std::ops::RangeInclusive;
use itertools::Itertools;
use orchid_api::parser::CharFilter;
pub type CRange = RangeInclusive<char>;
fn try_merge_char_ranges(left: CRange, right: CRange) -> Result<CRange, (CRange, CRange)> {
match *left.end() as u32 + 1 < *right.start() as u32 {
true => Err((left, right)),
false => Ok(*left.start()..=*right.end()),
}
}
/// Process the character ranges to make them adhere to the structural
/// requirements of [CharFilter]
pub fn mk_char_filter(items: impl IntoIterator<Item = CRange>) -> CharFilter {
CharFilter(
(items.into_iter())
.filter(|r| *r.start() as u32 + 1 < *r.end() as u32)
.sorted_by_key(|r| *r.start() as u32)
.coalesce(try_merge_char_ranges)
.collect_vec(),
)
}
/// Decide whether a char filter matches a character via binary search
pub fn char_filter_match(cf: &CharFilter, c: char) -> bool {
match cf.0.binary_search_by_key(&c, |l| *l.end()) {
Ok(_) => true, // c is the end of a range
Err(i) if i == cf.0.len() => false, // all ranges end before c
Err(i) => cf.0[i].contains(&c), // c between cf.0[i-1]?.end and cf.0[i].end, check [i]
}
}
/// Merge two char filters into a filter that matches if either of the
/// constituents would match.
pub fn char_filter_union(l: &CharFilter, r: &CharFilter) -> CharFilter {
CharFilter(
(l.0.iter().merge_by(&r.0, |l, r| l.start() <= r.start()))
.cloned()
.coalesce(try_merge_char_ranges)
.collect_vec(),
)
}

View File

@@ -0,0 +1,53 @@
use std::fmt::Debug;
use std::num::NonZeroU64;
use std::ops::{Deref, DerefMut};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Mutex, MutexGuard, OnceLock};
use hashbrown::HashMap;
pub struct IdStore<T> {
table: OnceLock<Mutex<HashMap<NonZeroU64, T>>>,
id: AtomicU64,
}
impl<T> IdStore<T> {
pub const fn new() -> Self { Self { table: OnceLock::new(), id: AtomicU64::new(1) } }
pub fn add<R>(&self, t: T) -> R
where
NonZeroU64: TryInto<R>,
<NonZeroU64 as TryInto<R>>::Error: Debug,
{
let tbl = self.table.get_or_init(Mutex::default);
let mut tbl_g = tbl.lock().unwrap();
let id64: NonZeroU64 = self.id.fetch_add(1, Ordering::Relaxed).try_into().unwrap();
let id: R = id64.try_into().expect("Keyspace exhausted");
assert!(tbl_g.insert(id64, t).is_none(), "atom ID wraparound");
id
}
pub fn get(&self, id: impl Into<NonZeroU64>) -> Option<IdRecord<'_, T>> {
let tbl = self.table.get_or_init(Mutex::default);
let tbl_g = tbl.lock().unwrap();
let id64 = id.into();
if tbl_g.contains_key(&id64) { Some(IdRecord(id64, tbl_g)) } else { None }
}
}
impl<T> Default for IdStore<T> {
fn default() -> Self { Self::new() }
}
pub struct IdRecord<'a, T>(NonZeroU64, MutexGuard<'a, HashMap<NonZeroU64, T>>);
impl<'a, T> IdRecord<'a, T> {
pub fn remove(mut self) -> T { self.1.remove(&self.0).unwrap() }
}
impl<'a, T> Deref for IdRecord<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.1.get(&self.0).expect("Existence checked on construction")
}
}
impl<'a, T> DerefMut for IdRecord<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.1.get_mut(&self.0).expect("Existence checked on construction")
}
}

View File

@@ -1,16 +1,18 @@
pub mod boxed_iter; pub mod boxed_iter;
pub mod msg;
pub mod clone; pub mod clone;
pub mod combine; pub mod combine;
pub mod event; pub mod event;
pub mod msg;
// pub mod gen; // pub mod gen;
pub mod api_utils;
pub mod char_filter;
pub mod id_store;
pub mod intern; pub mod intern;
pub mod join;
pub mod location; pub mod location;
pub mod name; pub mod name;
pub mod proj_error; pub mod proj_error;
pub mod reqnot; pub mod reqnot;
pub mod sequence;
pub mod tree; pub mod tree;
pub mod virt_fs; pub mod virt_fs;
pub mod join;
pub mod sequence;
pub mod api_utils;

View File

@@ -9,8 +9,8 @@ use std::{fmt, process};
use dyn_clone::{clone_box, DynClone}; use dyn_clone::{clone_box, DynClone};
use itertools::Itertools; use itertools::Itertools;
use crate::location::CodeOrigin;
use crate::boxed_iter::{box_once, BoxedIter}; use crate::boxed_iter::{box_once, BoxedIter};
use crate::location::CodeOrigin;
#[allow(unused)] // for doc #[allow(unused)] // for doc
use crate::virt_fs::CodeNotFound; use crate::virt_fs::CodeNotFound;
@@ -220,7 +220,15 @@ impl Reporter {
/// will always return true in the future. /// will always return true in the future.
pub fn failing(&self) -> bool { !self.0.borrow().is_empty() } pub fn failing(&self) -> bool { !self.0.borrow().is_empty() }
/// Report a fatal error /// Report a fatal error
pub fn report(&self, error: ProjectErrorObj) { self.0.borrow_mut().push(error) } pub fn report(&self, error: ProjectErrorObj) {
match error.as_any_ref().downcast_ref::<MultiError>() {
None => self.0.borrow_mut().push(error),
Some(me) =>
for err in me.0.iter() {
self.report(err.clone())
},
}
}
/// Catch a fatal error, report it, and substitute the value /// Catch a fatal error, report it, and substitute the value
pub fn fallback<T>(&self, res: ProjectResult<T>, cb: impl FnOnce(ProjectErrorObj) -> T) -> T { pub fn fallback<T>(&self, res: ProjectResult<T>, cb: impl FnOnce(ProjectErrorObj) -> T) -> T {
res.inspect_err(|e| self.report(e.clone())).unwrap_or_else(cb) res.inspect_err(|e| self.report(e.clone())).unwrap_or_else(cb)
@@ -277,7 +285,11 @@ impl ProjectError for MultiError {
self.0.iter().flat_map(|e| { self.0.iter().flat_map(|e| {
e.positions().map(|pos| { e.positions().map(|pos| {
let emsg = e.message(); let emsg = e.message();
let msg = if let Some(pmsg) = pos.message { format!("{emsg}: {pmsg}") } else { emsg }; let msg = match pos.message {
None => emsg,
Some(s) if s.is_empty() => emsg,
Some(pmsg) => format!("{emsg}: {pmsg}"),
};
ErrorPosition { origin: pos.origin, message: Some(msg) } ErrorPosition { origin: pos.origin, message: Some(msg) }
}) })
}) })

View File

@@ -1,3 +1,4 @@
use std::marker::PhantomData;
use std::mem; use std::mem;
use std::ops::{BitAnd, Deref}; use std::ops::{BitAnd, Deref};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
@@ -12,7 +13,8 @@ use trait_set::trait_set;
trait_set! { trait_set! {
pub trait SendFn<T: MsgSet> = for<'a> FnMut(&'a [u8], ReqNot<T>) + DynClone + Send + 'static; pub trait SendFn<T: MsgSet> = for<'a> FnMut(&'a [u8], ReqNot<T>) + DynClone + Send + 'static;
pub trait ReqFn<T: MsgSet> = FnMut(RequestHandle<T>) + Send + 'static; pub trait ReqFn<T: MsgSet> = FnMut(RequestHandle<T>) + Send + 'static;
pub trait NotifFn<T: MsgSet> = for<'a> FnMut(<T::In as Channel>::Notif, ReqNot<T>) + Send + Sync + 'static; pub trait NotifFn<T: MsgSet> =
for<'a> FnMut(<T::In as Channel>::Notif, ReqNot<T>) + Send + Sync + 'static;
} }
fn get_id(message: &[u8]) -> (u64, &[u8]) { fn get_id(message: &[u8]) -> (u64, &[u8]) {
@@ -22,20 +24,24 @@ fn get_id(message: &[u8]) -> (u64, &[u8]) {
pub struct RequestHandle<T: MsgSet> { pub struct RequestHandle<T: MsgSet> {
id: u64, id: u64,
message: <T::In as Channel>::Req, message: <T::In as Channel>::Req,
send: Box<dyn SendFn<T>>,
parent: ReqNot<T>, parent: ReqNot<T>,
fulfilled: AtomicBool, fulfilled: AtomicBool,
} }
impl<MS: MsgSet> RequestHandle<MS> { impl<MS: MsgSet + 'static> RequestHandle<MS> {
pub fn reqnot(&self) -> ReqNot<MS> { self.parent.clone() } pub fn reqnot(&self) -> ReqNot<MS> { self.parent.clone() }
pub fn req(&self) -> &<MS::In as Channel>::Req { &self.message } pub fn req(&self) -> &<MS::In as Channel>::Req { &self.message }
fn respond(&self, response: &impl Encode) { fn respond(&self, response: &impl Encode) {
assert!(!self.fulfilled.swap(true, Ordering::Relaxed), "Already responded"); assert!(!self.fulfilled.swap(true, Ordering::Relaxed), "Already responded");
let mut buf = (!self.id).to_be_bytes().to_vec(); let mut buf = (!self.id).to_be_bytes().to_vec();
response.encode(&mut buf); response.encode(&mut buf);
clone_box(&*self.send)(&buf, self.parent.clone()); let mut send = clone_box(&*self.reqnot().0.lock().unwrap().send);
(send)(&buf, self.parent.clone());
} }
pub fn handle<T: Request>(&self, _: &T, rep: &T::Response) { self.respond(rep) } pub fn handle<T: Request>(&self, _: &T, rep: &T::Response) { self.respond(rep) }
pub fn will_handle_as<T: Request>(&self, _: &T) -> ReqTypToken<T> { ReqTypToken(PhantomData) }
pub fn handle_as<T: Request>(&self, token: ReqTypToken<T>, rep: &T::Response) {
self.respond(rep)
}
} }
impl<MS: MsgSet> Drop for RequestHandle<MS> { impl<MS: MsgSet> Drop for RequestHandle<MS> {
fn drop(&mut self) { fn drop(&mut self) {
@@ -43,6 +49,8 @@ impl<MS: MsgSet> Drop for RequestHandle<MS> {
} }
} }
pub struct ReqTypToken<T>(PhantomData<T>);
pub fn respond_with<R: Request>(r: &R, f: impl FnOnce(&R) -> R::Response) -> Vec<u8> { pub fn respond_with<R: Request>(r: &R, f: impl FnOnce(&R) -> R::Response) -> Vec<u8> {
r.respond(f(r)) r.respond(f(r))
} }
@@ -83,9 +91,8 @@ impl<T: MsgSet> ReqNot<T> {
let sender = g.responses.remove(&!id).expect("Received response for invalid message"); let sender = g.responses.remove(&!id).expect("Received response for invalid message");
sender.send(message).unwrap(); sender.send(message).unwrap();
} else { } else {
let send = clone_box(&*g.send);
let message = <T::In as Channel>::Req::decode(&mut &payload[..]); let message = <T::In as Channel>::Req::decode(&mut &payload[..]);
(g.req)(RequestHandle { id, message, send, fulfilled: false.into(), parent: self.clone() }) (g.req)(RequestHandle { id, message, fulfilled: false.into(), parent: self.clone() })
} }
} }
@@ -159,7 +166,8 @@ mod test {
use orchid_api_traits::{Channel, Request}; use orchid_api_traits::{Channel, Request};
use super::{MsgSet, ReqNot}; use super::{MsgSet, ReqNot};
use crate::{clone, reqnot::Requester as _}; use crate::clone;
use crate::reqnot::Requester as _;
#[derive(Clone, Debug, Coding, PartialEq)] #[derive(Clone, Debug, Coding, PartialEq)]
pub struct TestReq(u8); pub struct TestReq(u8);

View File

@@ -1,9 +1,9 @@
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use crate::proj_error::{ErrorSansOrigin, ErrorSansOriginObj};
use crate::intern::Token; use crate::intern::Token;
use crate::name::{PathSlice, VPath}; use crate::name::{PathSlice, VPath};
use crate::proj_error::{ErrorSansOrigin, ErrorSansOriginObj};
/// Represents the result of loading code from a string-tree form such /// Represents the result of loading code from a string-tree form such
/// as the file system. Cheap to clone. /// as the file system. Cheap to clone.

View File

@@ -3,11 +3,11 @@ use std::sync::Arc;
use super::common::CodeNotFound; use super::common::CodeNotFound;
use super::{FSResult, Loaded, VirtFS}; use super::{FSResult, Loaded, VirtFS};
use crate::intern::Token;
use crate::proj_error::ErrorSansOrigin;
use crate::name::PathSlice;
use crate::tree::{ModEntry, ModMember};
use crate::combine::Combine; use crate::combine::Combine;
use crate::intern::Token;
use crate::name::PathSlice;
use crate::proj_error::ErrorSansOrigin;
use crate::tree::{ModEntry, ModMember};
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct ConflictingTrees; pub struct ConflictingTrees;

View File

@@ -10,8 +10,8 @@ use hashbrown::HashMap;
use super::common::CodeNotFound; use super::common::CodeNotFound;
use super::{FSResult, Loaded, VirtFS}; use super::{FSResult, Loaded, VirtFS};
use crate::intern::{intern, Token}; use crate::intern::{intern, Token};
use crate::proj_error::{ErrorSansOrigin, ErrorSansOriginObj};
use crate::name::PathSlice; use crate::name::PathSlice;
use crate::proj_error::{ErrorSansOrigin, ErrorSansOriginObj};
#[derive(Clone)] #[derive(Clone)]
struct OpenError { struct OpenError {

View File

@@ -6,9 +6,9 @@ use rust_embed::RustEmbed;
use super::common::CodeNotFound; use super::common::CodeNotFound;
use super::{FSResult, Loaded, VirtFS}; use super::{FSResult, Loaded, VirtFS};
use crate::intern::{intern, Token}; use crate::intern::{intern, Token};
use crate::proj_error::ErrorSansOrigin;
use crate::location::CodeGenInfo; use crate::location::CodeGenInfo;
use crate::name::PathSlice; use crate::name::PathSlice;
use crate::proj_error::ErrorSansOrigin;
use crate::tree::{ModEntry, ModMember, Module}; use crate::tree::{ModEntry, ModMember, Module};
/// An in-memory FS tree for libraries managed internally by the interpreter /// An in-memory FS tree for libraries managed internally by the interpreter

View File

@@ -10,7 +10,11 @@ ahash = "0.8.11"
derive_destructure = "1.0.0" derive_destructure = "1.0.0"
hashbrown = "0.14.5" hashbrown = "0.14.5"
itertools = "0.12.1" itertools = "0.12.1"
never = "0.1.0"
orchid-api = { version = "0.1.0", path = "../orchid-api" } orchid-api = { version = "0.1.0", path = "../orchid-api" }
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" } orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
orchid-base = { version = "0.1.0", path = "../orchid-base" } orchid-base = { version = "0.1.0", path = "../orchid-base" }
ordered-float = "4.2.0" ordered-float = "4.2.0"
substack = "1.1.0"
trait-set = "0.3.0"
typeid = "1.0.0"

View File

@@ -0,0 +1,174 @@
use std::any::{type_name, Any};
use std::io::{Read, Write};
use std::num::NonZeroU64;
use std::ops::Deref;
use std::sync::Arc;
use std::{fmt, mem};
use derive_destructure::destructure;
use orchid_api::atom::{Atom, Fwd};
use orchid_api::expr::{ExprTicket, Release};
use orchid_api::system::SysId;
use orchid_api_traits::{Coding, Decode, Encode, Request};
use orchid_base::id_store::{IdRecord, IdStore};
use orchid_base::reqnot::Requester as _;
use typeid::ConstTypeId;
use crate::expr::GenClause;
use crate::other_system::SystemHandle;
use crate::system::{DynSystemCard, SystemCard};
pub trait AtomCard: 'static + Sized {
type Owner: SystemCard;
type Data: Clone + Coding + Sized;
type Req: Coding;
}
pub fn get_info<A: AtomCard>(sys: &(impl DynSystemCard + ?Sized)) -> (usize, &AtomInfo) {
sys.atom_info_for(ConstTypeId::of::<A>()).unwrap_or_else(|| {
panic!("Atom {} not associated with system {}", type_name::<A>(), sys.name())
})
}
pub fn encode_atom_nodrop<A: AtomCard>(
sys_id: SysId,
sys: &(impl DynSystemCard + ?Sized),
data: &A::Data,
) -> Atom {
let (info_pos, _) = get_info::<A>(sys);
let mut buf = (info_pos as u64).enc_vec();
data.encode(&mut buf);
Atom { owner: sys_id, drop: false, data: buf }
}
pub fn encode_atom_drop<A: AtomCard>(
sys_id: SysId,
sys: &(impl DynSystemCard + ?Sized),
atom_id: u64,
data: &A::Data,
) -> Atom {
let (info_pos, _) = get_info::<A>(sys);
let mut buf = (info_pos as u64).enc_vec();
atom_id.encode(&mut buf);
data.encode(&mut buf);
Atom { owner: sys_id, drop: true, data: buf }
}
pub fn decode_atom<A: AtomCard>(
sys: &(impl DynSystemCard + ?Sized),
Atom { data, drop, owner: _ }: &Atom,
) -> Option<A::Data> {
let (info_pos, info) = get_info::<A>(sys);
let mut data = &data[..];
if u64::decode(&mut data) != info_pos as u64 {
return None;
}
let val = (info.decode)(data);
Some(*val.downcast().expect("The type-id checked out, the decode should've worked"))
}
#[derive(destructure)]
pub struct ForeignAtom<A: AtomCard> {
pub(crate) sys: SystemHandle<A::Owner>,
pub(crate) ticket: ExprTicket,
pub(crate) api: Atom,
pub(crate) value: A::Data,
}
impl<A: AtomCard> ForeignAtom<A> {
/// Unpack the object, returning the held atom and expr ticket. This is in
/// contrast to dropping the atom which releases the expr ticket.
pub fn unpack(self) -> (A::Data, ExprTicket, Atom) {
let (_, ticket, api, value) = self.destructure();
(value, ticket, api)
}
pub fn ticket(&self) -> ExprTicket { self.ticket }
pub fn request<R: Coding + Into<A::Req> + Request>(&self, req: R) -> R::Response {
R::Response::decode(&mut &self.sys.reqnot.request(Fwd(self.api.clone(), req.enc_vec()))[..])
}
}
impl<A: AtomCard> Deref for ForeignAtom<A> {
type Target = A::Data;
fn deref(&self) -> &Self::Target { &self.value }
}
impl<A: AtomCard> Drop for ForeignAtom<A> {
fn drop(&mut self) { self.sys.reqnot.notify(Release(self.sys.id(), self.ticket)) }
}
pub struct AtomInfo {
pub tid: ConstTypeId,
pub decode: fn(&[u8]) -> Box<dyn Any>,
pub call: fn(&[u8], ExprTicket) -> GenClause,
pub call_ref: fn(&[u8], ExprTicket) -> GenClause,
pub same: fn(&[u8], &[u8]) -> bool,
pub handle_req: fn(&[u8], &mut dyn Read, &mut dyn Write),
pub drop: fn(&[u8]),
}
pub trait ThinAtom: AtomCard<Data = Self> + Coding + fmt::Debug {
fn call(&self, arg: ExprTicket) -> GenClause;
fn same(&self, other: &Self) -> bool;
fn handle_req(&self, req: Self::Req, rep: &mut (impl Write + ?Sized));
}
pub const fn thin_atom_info<T: ThinAtom>() -> AtomInfo {
AtomInfo {
tid: ConstTypeId::of::<T>(),
decode: |mut b| Box::new(T::decode(&mut b)),
call: |mut b, extk| T::decode(&mut b).call(extk),
call_ref: |mut b, extk| T::decode(&mut b).call(extk),
handle_req: |mut b, req, rep| T::decode(&mut b).handle_req(Decode::decode(req), rep),
same: |mut b1, mut b2| T::decode(&mut b1).same(&T::decode(&mut b2)),
drop: |mut b1| eprintln!("Received drop signal for non-drop atom {:?}", T::decode(&mut b1)),
}
}
/// Atoms that have a [Drop]
pub trait OwnedAtom: AtomCard + Deref<Target = Self::Data> + Send + Sync + Any + 'static {
fn call_ref(&self, arg: ExprTicket) -> GenClause;
fn call(self, arg: ExprTicket) -> GenClause;
fn same(&self, other: &Self) -> bool;
fn handle_req(&self, req: Self::Req, rep: &mut (impl Write + ?Sized));
}
pub trait DynOwnedAtom: Send + Sync + 'static {
fn atom_tid(&self) -> ConstTypeId;
fn as_any_ref(&self) -> &dyn Any;
fn dyn_call_ref(&self, arg: ExprTicket) -> GenClause;
fn dyn_call(self: Box<Self>, arg: ExprTicket) -> GenClause;
fn dyn_same(&self, other: &dyn DynOwnedAtom) -> bool;
fn dyn_handle_req(&self, req: &mut dyn Read, rep: &mut dyn Write);
}
impl<T: OwnedAtom> DynOwnedAtom for T {
fn atom_tid(&self) -> ConstTypeId { ConstTypeId::of::<T>() }
fn as_any_ref(&self) -> &dyn Any { self }
fn dyn_call_ref(&self, arg: ExprTicket) -> GenClause { self.call_ref(arg) }
fn dyn_call(self: Box<Self>, arg: ExprTicket) -> GenClause { self.call(arg) }
fn dyn_same(&self, other: &dyn DynOwnedAtom) -> bool {
if ConstTypeId::of::<Self>() != other.as_any_ref().type_id() {
return false;
}
let other_self = other.as_any_ref().downcast_ref().expect("The type_ids are the same");
self.same(other_self)
}
fn dyn_handle_req(&self, req: &mut dyn Read, rep: &mut dyn Write) {
self.handle_req(<Self as AtomCard>::Req::decode(req), rep)
}
}
pub(crate) static OBJ_STORE: IdStore<Box<dyn DynOwnedAtom>> = IdStore::new();
const fn owned_atom_info<T: OwnedAtom>() -> AtomInfo {
fn with_atom<U>(mut b: &[u8], f: impl FnOnce(IdRecord<'_, Box<dyn DynOwnedAtom>>) -> U) -> U {
f(OBJ_STORE.get(NonZeroU64::decode(&mut b)).expect("Received invalid atom ID"))
}
AtomInfo {
tid: ConstTypeId::of::<T>(),
decode: |mut b| Box::new(T::Data::decode(&mut b)),
call: |b, arg| with_atom(b, |a| a.remove().dyn_call(arg)),
call_ref: |b, arg| with_atom(b, |a| a.dyn_call_ref(arg)),
same: |b1, b2| with_atom(b1, |a1| with_atom(b2, |a2| a1.dyn_same(&**a2))),
handle_req: |b, req, rep| with_atom(b, |a| a.dyn_handle_req(req, rep)),
drop: |b| mem::drop(with_atom(b, |a| a.remove())),
}
}

View File

@@ -1,83 +0,0 @@
use std::marker::PhantomData;
use std::ops::Deref;
use derive_destructure::destructure;
use orchid_api::atom::{Atom, Fwd};
use orchid_api::expr::{ExprTicket, Release};
use orchid_api::proto::ExtMsgSet;
use orchid_api::system::SysId;
use orchid_api_traits::{Coding, Decode, Request};
use orchid_base::reqnot::{ReqNot, Requester};
pub struct SystemHandle<T: SystemDepCard> {
_t: PhantomData<T>,
id: SysId,
reqnot: ReqNot<ExtMsgSet>,
}
impl<T: SystemDepCard> SystemHandle<T> {
pub(crate) fn new(id: SysId, reqnot: ReqNot<ExtMsgSet>) -> Self {
Self { _t: PhantomData, id, reqnot }
}
pub fn id(&self) -> SysId { self.id }
pub fn wrap_atom<A: Atomic<Owner = T>>(
&self,
api: Atom,
ticket: ExprTicket,
) -> Result<OwnedAtom<A>, Atom> {
if api.owner == self.id {
Ok(OwnedAtom { ticket, sys: self.clone(), value: T::decode_atom(&api), api })
} else {
Err(api)
}
}
}
impl<T: SystemDepCard> Clone for SystemHandle<T> {
fn clone(&self) -> Self { Self { reqnot: self.reqnot.clone(), _t: PhantomData, id: self.id } }
}
pub trait Atomic: 'static {
type Owner: SystemDepCard;
type Req: Coding;
const HAS_DROP: bool;
}
pub trait SystemDepCard: 'static {
const NAME: &'static str;
/// Decode an atom from binary representation.
///
/// This is held in the dep card because there is no global type tag on the
/// atom, so by the logic of the binary coding algorithm the value has to be a
/// concrete type, probably an enum of the viable types.
fn decode_atom<A: Atomic<Owner = Self>>(api: &Atom) -> A;
}
#[derive(destructure)]
pub struct OwnedAtom<A: Atomic> {
sys: SystemHandle<A::Owner>,
ticket: ExprTicket,
api: Atom,
value: A,
}
impl<A: Atomic> OwnedAtom<A> {
/// Unpack the object, returning the held atom and expr ticket. This is in
/// contrast to dropping the atom which releases the expr ticket.
pub fn unpack(self) -> (A, ExprTicket, Atom) {
let (_, ticket, api, value) = self.destructure();
(value, ticket, api)
}
pub fn ticket(&self) -> ExprTicket { self.ticket }
pub fn request<R: Coding + Into<A::Req> + Request>(&self, req: R) -> R::Response {
R::Response::decode(&mut &self.sys.reqnot.request(Fwd(self.api.clone(), req.enc_vec()))[..])
}
}
impl<A: Atomic> Deref for OwnedAtom<A> {
type Target = A;
fn deref(&self) -> &Self::Target { &self.value }
}
impl<A: Atomic> Drop for OwnedAtom<A> {
fn drop(&mut self) {
if A::HAS_DROP {
self.sys.reqnot.notify(Release(self.sys.id(), self.ticket))
}
}
}

View File

@@ -1,34 +1,65 @@
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::{mem, thread};
use hashbrown::HashMap; use hashbrown::HashMap;
use itertools::Itertools; use itertools::Itertools;
use orchid_api::atom::{Atom, AtomReq, AtomSame, CallRef, FinalCall, Fwded};
use orchid_api::parser::{CharFilter, Lex, Lexed, ParserReq, SubLex};
use orchid_api::proto::{ExtMsgSet, ExtensionHeader, HostExtNotif, HostExtReq, HostHeader}; use orchid_api::proto::{ExtMsgSet, ExtensionHeader, HostExtNotif, HostExtReq, HostHeader};
use orchid_api::system::{SysId, SystemInst};
use orchid_api::vfs::{EagerVfs, VfsId, VfsRead, VfsReq};
use orchid_api_traits::{Decode, Encode}; use orchid_api_traits::{Decode, Encode};
use orchid_base::char_filter::{char_filter_union, mk_char_filter};
use orchid_base::clone; use orchid_base::clone;
use orchid_base::intern::{init_replica, sweep_replica}; use orchid_base::intern::{deintern, init_replica, sweep_replica};
use orchid_base::name::PathSlice;
use orchid_base::reqnot::{ReqNot, Requester}; use orchid_base::reqnot::{ReqNot, Requester};
use crate::atom::AtomInfo;
use crate::fs::VirtFS;
use crate::msg::{recv_parent_msg, send_parent_msg}; use crate::msg::{recv_parent_msg, send_parent_msg};
use crate::system::DynSystem;
use crate::system_ctor::DynSystemCtor; use crate::system_ctor::DynSystemCtor;
pub struct ExtensionData { pub struct ExtensionData {
pub systems: Vec<Box<dyn DynSystemCtor>>, pub systems: Vec<Box<dyn DynSystemCtor>>,
} }
pub struct SystemRecord {
instance: Box<dyn DynSystem>,
vfses: HashMap<VfsId, Arc<dyn VirtFS>>,
declfs: EagerVfs,
}
pub fn with_atom_record<T>(
systems: &Mutex<HashMap<SysId, SystemRecord>>,
atom: &Atom,
cb: impl FnOnce(&AtomInfo, &[u8]) -> T,
) -> T {
let mut data = &atom.data[..];
let systems_g = systems.lock().unwrap();
let sys = &systems_g[&atom.owner].instance;
let atom_record =
(sys.card().atoms()[u64::decode(&mut data) as usize].as_ref()).expect("Atom ID reserved");
cb(atom_record, data)
}
pub fn main(data: ExtensionData) { pub fn main(data: ExtensionData) {
HostHeader::decode(&mut &recv_parent_msg().unwrap()[..]); HostHeader::decode(&mut &recv_parent_msg().unwrap()[..]);
let mut buf = Vec::new(); let mut buf = Vec::new();
let decls = data.systems.iter().map(|sys| sys.decl()).collect_vec(); let decls = data.systems.iter().map(|sys| sys.decl()).collect_vec();
let systems = Arc::new(Mutex::new(HashMap::new())); let systems = Arc::new(Mutex::new(HashMap::<SysId, SystemRecord>::new()));
ExtensionHeader { systems: decls.clone() }.encode(&mut buf); ExtensionHeader { systems: decls.clone() }.encode(&mut buf);
send_parent_msg(&buf).unwrap(); send_parent_msg(&buf).unwrap();
let exiting = Arc::new(AtomicBool::new(false)); let exiting = Arc::new(AtomicBool::new(false));
let rn = ReqNot::<ExtMsgSet>::new( let rn = ReqNot::<ExtMsgSet>::new(
|a, _| send_parent_msg(a).unwrap(), |a, _| send_parent_msg(a).unwrap(),
clone!(exiting; move |n, _| match n { clone!(systems, exiting; move |n, _| match n {
HostExtNotif::Exit => exiting.store(true, Ordering::Relaxed), HostExtNotif::Exit => exiting.store(true, Ordering::Relaxed),
_ => todo!(), HostExtNotif::SystemDrop(sys) => mem::drop(systems.lock().unwrap().remove(&sys.0)),
HostExtNotif::AtomDrop(atom) =>
with_atom_record(&systems, &atom.0, |rec, data| (rec.drop)(data)),
}), }),
clone!(systems; move |req| match req.req() { clone!(systems; move |req| match req.req() {
HostExtReq::Ping(ping) => req.handle(ping, &()), HostExtReq::Ping(ping) => req.handle(ping, &()),
@@ -36,10 +67,57 @@ pub fn main(data: ExtensionData) {
HostExtReq::NewSystem(new_sys) => { HostExtReq::NewSystem(new_sys) => {
let i = decls.iter().enumerate().find(|(_, s)| s.id == new_sys.system).unwrap().0; let i = decls.iter().enumerate().find(|(_, s)| s.id == new_sys.system).unwrap().0;
let system = data.systems[i].new_system(new_sys, req.reqnot()); let system = data.systems[i].new_system(new_sys, req.reqnot());
systems.lock().unwrap().insert(new_sys.id, system); let mut vfses = HashMap::new();
req.handle(new_sys, &()) let lex_filter = system.lexers().iter().fold(CharFilter(vec![]), |cf, lx| {
char_filter_union(&cf, &mk_char_filter(lx.char_filter().iter().cloned()))
});
systems.lock().unwrap().insert(new_sys.id, SystemRecord {
declfs: system.source().to_api_rec(&mut vfses),
vfses,
instance: system,
});
req.handle(new_sys, &SystemInst {
lex_filter
})
}
HostExtReq::GetConstTree(get_tree) => {
let systems_g = systems.lock().unwrap();
req.handle(get_tree, &systems_g[&get_tree.0].instance.env())
}
HostExtReq::VfsReq(VfsReq::GetVfs(get_vfs)) => {
let systems_g = systems.lock().unwrap();
req.handle(get_vfs, &systems_g[&get_vfs.0].declfs)
}
HostExtReq::VfsReq(VfsReq::VfsRead(vfs_read@VfsRead(sys_id, vfs_id, path))) => {
let systems_g = systems.lock().unwrap();
let path = path.iter().map(|t| deintern(*t)).collect_vec();
req.handle(vfs_read, &systems_g[sys_id].vfses[vfs_id].load(PathSlice::new(&path)))
}
HostExtReq::ParserReq(ParserReq::Lex(lex)) => {
let systems_g = systems.lock().unwrap();
let Lex{ sys, text, pos } = *lex;
let lexers = systems_g[&sys].instance.lexers();
mem::drop(systems_g);
let source = deintern(text);
let tk = req.will_handle_as(lex);
thread::spawn(move || {
let reqnot = req.reqnot();
let mut recurse = |tail: &str| {
let pos = (source.len() - tail.len()) as u32;
let lexed = reqnot.request(SubLex{ pos, text })?;
Ok((&source[lexed.pos as usize..], lexed.data))
};
let lex_res = lexers.iter().find_map(|lx| lx.lex(&source[pos as usize..], &mut recurse));
req.handle_as(tk, &lex_res.map(|r| r.map(|(s, data)| {
let pos = (source.len() - s.len()) as u32;
Lexed { data, pos }
})))
});
}, },
_ => todo!(), HostExtReq::AtomReq(AtomReq::AtomSame(same@AtomSame(l, r))) => todo!("subsys nimpl"),
HostExtReq::AtomReq(AtomReq::Fwded(call@Fwded(atom, req))) => todo!("subsys nimpl"),
HostExtReq::AtomReq(AtomReq::CallRef(call@CallRef(atom, arg))) => todo!("subsys nimpl"),
HostExtReq::AtomReq(AtomReq::FinalCall(call@FinalCall(atom, arg))) => todo!("subsys nimpl"),
}), }),
); );
init_replica(rn.clone().map()); init_replica(rn.clone().map());

View File

@@ -0,0 +1,53 @@
use orchid_api::atom::Atom;
use orchid_api::expr::ExprTicket;
use orchid_api::system::SysId;
use orchid_base::id_store::IdStore;
use orchid_base::intern::Token;
use crate::atom::{encode_atom_nodrop, DynOwnedAtom, OwnedAtom, ThinAtom, OBJ_STORE};
use crate::system::DynSystem;
pub enum GenClause {
Call(Box<GenClause>, Box<GenClause>),
Lambda(Token<String>, Box<GenClause>),
Arg(Token<String>),
Slot(ExprTicket),
Seq(Box<GenClause>, Box<GenClause>),
Const(Token<Vec<Token<String>>>),
ThinAtom(Box<dyn Fn(SysId, &dyn DynSystem) -> Atom>),
OwnedAtom(u64),
}
pub fn cnst(path: Token<Vec<Token<String>>>) -> GenClause { GenClause::Const(path) }
pub fn val<A: ThinAtom>(atom: A) -> GenClause {
GenClause::ThinAtom(Box::new(move |id, sys| encode_atom_nodrop::<A>(id, sys.card(), &atom)))
}
pub fn obj<A: OwnedAtom>(atom: A) -> GenClause {
GenClause::OwnedAtom(OBJ_STORE.add(Box::new(atom)))
}
pub fn seq(ops: impl IntoIterator<Item = GenClause>) -> GenClause {
fn recur(mut ops: impl Iterator<Item = GenClause>) -> Option<GenClause> {
let op = ops.next()?;
Some(match recur(ops) {
None => op,
Some(rec) => GenClause::Seq(Box::new(op), Box::new(rec)),
})
}
recur(ops.into_iter()).expect("Empty list provided to seq!")
}
pub fn slot(extk: ExprTicket) -> GenClause { GenClause::Slot(extk) }
pub fn arg(n: Token<String>) -> GenClause { GenClause::Arg(n) }
pub fn lambda(n: Token<String>, b: impl IntoIterator<Item = GenClause>) -> GenClause {
GenClause::Lambda(n, Box::new(call(b)))
}
pub fn call(v: impl IntoIterator<Item = GenClause>) -> GenClause {
v.into_iter()
.reduce(|f, x| GenClause::Call(Box::new(f), Box::new(x)))
.expect("Empty call expression")
}

View File

@@ -0,0 +1,48 @@
use std::sync::Arc;
use hashbrown::HashMap;
use orchid_api::error::ProjResult;
use orchid_api::vfs::{EagerVfs, Loaded, VfsId};
use orchid_base::intern::{intern, Token};
use orchid_base::name::PathSlice;
use substack::Substack;
use trait_set::trait_set;
pub trait VirtFS: Send + Sync + 'static {
fn load(&self, path: &PathSlice) -> ProjResult<Loaded>;
}
trait_set! {
pub trait RecFsHandler<E> = FnMut(Substack<Token<String>>, &Arc<dyn VirtFS>) -> Result<(), E>;
}
pub enum DeclFs {
Lazy(Arc<dyn VirtFS>),
Mod(HashMap<Token<String>, DeclFs>),
}
impl DeclFs {
pub fn module(entries: impl IntoIterator<Item = (&'static str, Self)>) -> Self {
Self::Mod(entries.into_iter().map(|(k, v)| (intern(k), v)).collect())
}
fn rec<E>(&self, path: Substack<Token<String>>, f: &mut impl RecFsHandler<E>) -> Result<(), E> {
match self {
DeclFs::Lazy(fs) => f(path, fs),
DeclFs::Mod(entries) => entries.iter().try_for_each(|(k, v)| v.rec(path.push(k.clone()), f)),
}
}
pub fn recurse<E>(&self, f: &mut impl RecFsHandler<E>) -> Result<(), E> {
self.rec(Substack::Bottom, f)
}
pub fn to_api_rec(&self, vfses: &mut HashMap<VfsId, Arc<dyn VirtFS>>) -> EagerVfs {
match self {
DeclFs::Lazy(fs) => {
let id = vfses.len() as VfsId;
vfses.insert(id, fs.clone());
EagerVfs::Lazy(id)
},
DeclFs::Mod(children) => EagerVfs::Eager(
children.into_iter().map(|(k, v)| (k.marker(), v.to_api_rec(vfses))).collect(),
),
}
}
}

View File

@@ -0,0 +1,34 @@
use std::ops::RangeInclusive;
use orchid_api::error::ProjResult;
use orchid_api::tree::TokenTree;
pub trait Lexer: Send + Sync + Sized + Default + 'static {
const CHAR_FILTER: &'static [RangeInclusive<char>];
fn lex<'a>(
tail: &'a str,
recur: impl FnMut(&'a str) -> ProjResult<(&'a str, TokenTree)>,
) -> Option<ProjResult<(&'a str, TokenTree)>>;
}
pub trait DynLexer: Send + Sync + 'static {
fn char_filter(&self) -> &'static [RangeInclusive<char>];
fn lex<'a>(
&self,
tail: &'a str,
recur: &mut dyn FnMut(&'a str) -> ProjResult<(&'a str, TokenTree)>,
) -> Option<ProjResult<(&'a str, TokenTree)>>;
}
impl<T: Lexer> DynLexer for T {
fn char_filter(&self) -> &'static [RangeInclusive<char>] { T::CHAR_FILTER }
fn lex<'a>(
&self,
tail: &'a str,
recur: &mut dyn FnMut(&'a str) -> ProjResult<(&'a str, TokenTree)>,
) -> Option<ProjResult<(&'a str, TokenTree)>> {
T::lex(tail, recur)
}
}
pub type LexerObj = &'static dyn DynLexer;

View File

@@ -1,5 +1,9 @@
pub mod atom;
pub mod entrypoint; pub mod entrypoint;
pub mod data; pub mod expr;
pub mod fs;
pub mod lexer;
pub mod msg; pub mod msg;
pub mod system_ctor; pub mod other_system;
pub mod system; pub mod system;
pub mod system_ctor;

View File

@@ -0,0 +1,37 @@
use std::marker::PhantomData;
use orchid_api::atom::Atom;
use orchid_api::expr::ExprTicket;
use orchid_api::proto::ExtMsgSet;
use orchid_api::system::SysId;
use orchid_base::reqnot::ReqNot;
use crate::atom::{decode_atom, AtomCard, ForeignAtom};
use crate::system::SystemCard;
pub struct SystemHandle<C: SystemCard> {
pub(crate) _card: PhantomData<C>,
pub(crate) id: SysId,
pub(crate) reqnot: ReqNot<ExtMsgSet>,
}
impl<T: SystemCard> SystemHandle<T> {
pub(crate) fn new(id: SysId, reqnot: ReqNot<ExtMsgSet>) -> Self {
Self { _card: PhantomData, id, reqnot }
}
pub fn id(&self) -> SysId { self.id }
pub fn wrap_atom<A: AtomCard<Owner = T>>(
&self,
api: Atom,
ticket: ExprTicket,
) -> Result<ForeignAtom<A>, Atom> {
if api.owner == self.id {
if let Some(value) = decode_atom::<A>(&T::default(), &api) {
return Ok(ForeignAtom { ticket, sys: self.clone(), value, api });
}
}
Err(api)
}
}
impl<T: SystemCard> Clone for SystemHandle<T> {
fn clone(&self) -> Self { Self { reqnot: self.reqnot.clone(), _card: PhantomData, id: self.id } }
}

View File

@@ -1,6 +1,53 @@
use orchid_api::expr::Expr; use std::any::Any;
use std::io::{Read, Write};
pub trait System: Send { use orchid_api::expr::ExprTicket;
fn consts(&self) -> Expr; use orchid_api::tree::TreeModule;
use typeid::ConstTypeId;
use crate::atom::AtomInfo;
use crate::expr::GenClause;
use crate::fs::DeclFs;
use crate::lexer::LexerObj;
/// System as consumed by foreign code
pub trait SystemCard: Default + Send + Sync + 'static {
const NAME: &'static str;
const ATOM_DEFS: &'static [Option<AtomInfo>];
} }
pub trait DynSystemCard: Send + Sync + 'static {
fn name(&self) -> &'static str;
fn atoms(&self) -> &'static [Option<AtomInfo>];
fn atom_info_for(&self, tid: ConstTypeId) -> Option<(usize, &AtomInfo)> {
(self.atoms().iter().enumerate())
.filter_map(|(i, o)| o.as_ref().map(|a| (i, a)))
.find(|ent| ent.1.tid == tid)
}
}
impl<T: SystemCard> DynSystemCard for T {
fn name(&self) -> &'static str { Self::NAME }
fn atoms(&self) -> &'static [Option<AtomInfo>] { Self::ATOM_DEFS }
}
/// System as defined by author
pub trait System: Send + Sync + SystemCard {
fn env() -> TreeModule;
fn source() -> DeclFs;
const LEXERS: &'static [LexerObj];
}
pub trait DynSystem: Send + Sync + 'static {
fn env(&self) -> TreeModule;
fn source(&self) -> DeclFs;
fn lexers(&self) -> &'static [LexerObj];
fn card(&self) -> &dyn DynSystemCard;
}
impl<T: System> DynSystem for T {
fn env(&self) -> TreeModule { <Self as System>::env() }
fn source(&self) -> DeclFs { <Self as System>::source() }
fn lexers(&self) -> &'static [LexerObj] { Self::LEXERS }
fn card(&self) -> &dyn DynSystemCard { self }
}

View File

@@ -1,4 +1,3 @@
use std::any::TypeId;
use std::hash::{Hash as _, Hasher as _}; use std::hash::{Hash as _, Hasher as _};
use itertools::Itertools as _; use itertools::Itertools as _;
@@ -6,9 +5,10 @@ use orchid_api::proto::ExtMsgSet;
use orchid_api::system::{NewSystem, SysId, SystemDecl}; use orchid_api::system::{NewSystem, SysId, SystemDecl};
use orchid_base::reqnot::ReqNot; use orchid_base::reqnot::ReqNot;
use ordered_float::NotNan; use ordered_float::NotNan;
use typeid::ConstTypeId;
use crate::data::{SystemDepCard, SystemHandle}; use crate::other_system::SystemHandle;
use crate::system::System; use crate::system::{DynSystem, System, SystemCard};
pub struct SystemParams<Ctor: SystemCtor + ?Sized> { pub struct SystemParams<Ctor: SystemCtor + ?Sized> {
pub deps: <Ctor::Deps as DepSet>::Sat, pub deps: <Ctor::Deps as DepSet>::Sat,
@@ -22,7 +22,7 @@ pub trait DepSet {
fn create(take: &mut impl FnMut() -> SysId, reqnot: ReqNot<ExtMsgSet>) -> Self::Sat; fn create(take: &mut impl FnMut() -> SysId, reqnot: ReqNot<ExtMsgSet>) -> Self::Sat;
} }
impl<T: SystemDepCard> DepSet for T { impl<T: SystemCard> DepSet for T {
type Sat = SystemHandle<Self>; type Sat = SystemHandle<Self>;
fn report(names: &mut impl FnMut(&'static str)) { names(T::NAME) } fn report(names: &mut impl FnMut(&'static str)) { names(T::NAME) }
fn create(take: &mut impl FnMut() -> SysId, reqnot: ReqNot<ExtMsgSet>) -> Self::Sat { fn create(take: &mut impl FnMut() -> SysId, reqnot: ReqNot<ExtMsgSet>) -> Self::Sat {
@@ -32,15 +32,16 @@ impl<T: SystemDepCard> DepSet for T {
pub trait SystemCtor: Send + 'static { pub trait SystemCtor: Send + 'static {
type Deps: DepSet; type Deps: DepSet;
type Instance: System;
const NAME: &'static str; const NAME: &'static str;
const VERSION: f64; const VERSION: f64;
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
fn new(params: SystemParams<Self>) -> Box<dyn System>; fn new(params: SystemParams<Self>) -> Self::Instance;
} }
pub trait DynSystemCtor: Send + 'static { pub trait DynSystemCtor: Send + 'static {
fn decl(&self) -> SystemDecl; fn decl(&self) -> SystemDecl;
fn new_system(&self, new: &NewSystem, reqnot: ReqNot<ExtMsgSet>) -> Box<dyn System>; fn new_system(&self, new: &NewSystem, reqnot: ReqNot<ExtMsgSet>) -> Box<dyn DynSystem>;
} }
impl<T: SystemCtor> DynSystemCtor for T { impl<T: SystemCtor> DynSystemCtor for T {
@@ -52,19 +53,19 @@ impl<T: SystemCtor> DynSystemCtor for T {
T::Deps::report(&mut |n| depends.push(n.to_string())); T::Deps::report(&mut |n| depends.push(n.to_string()));
// generate definitely unique id // generate definitely unique id
let mut ahash = ahash::AHasher::default(); let mut ahash = ahash::AHasher::default();
TypeId::of::<T>().hash(&mut ahash); ConstTypeId::of::<T>().hash(&mut ahash);
let id = (ahash.finish().to_be_bytes().into_iter().tuples()) let id = (ahash.finish().to_be_bytes().into_iter().tuples())
.map(|(l, b)| u16::from_be_bytes([l, b])) .map(|(l, b)| u16::from_be_bytes([l, b]))
.fold(0, |a, b| a ^ b); .fold(0, |a, b| a ^ b);
SystemDecl { name: T::NAME.to_string(), depends, id, priority } SystemDecl { name: T::NAME.to_string(), depends, id, priority }
} }
fn new_system(&self, new: &NewSystem, reqnot: ReqNot<ExtMsgSet>) -> Box<dyn System> { fn new_system(&self, new: &NewSystem, reqnot: ReqNot<ExtMsgSet>) -> Box<dyn DynSystem> {
let mut ids = new.depends.iter().copied(); let mut ids = new.depends.iter().copied();
T::new(SystemParams { Box::new(T::new(SystemParams {
deps: T::Deps::create(&mut || ids.next().unwrap(), reqnot.clone()), deps: T::Deps::create(&mut || ids.next().unwrap(), reqnot.clone()),
id: new.id, id: new.id,
reqnot, reqnot,
}) }))
} }
} }

View File

@@ -0,0 +1,44 @@
pub struct TraitObject<T: ?Sized + Coding + 'static>(Box<T>, Arc<dyn Fn(&mut dyn Read) -> Self>);
impl<T: ?Sized + Coding + 'static> TraitObject<T> {
fn inner_type_id(&self) -> ConstTypeId { self.0.as_ref().type_id() }
fn get_decoder(&self) -> Arc<dyn Fn(&mut dyn Read) -> Self> { self.1.clone() }
}
pub trait AsTraitObject<T: ?Sized + Coding + 'static>: 'static {
fn trait_box(self) -> Box<T>;
fn into_trait_object(self) -> TraitObject<T>
where Self: Sized + Coding {
let decoder = Self::get_decoder(Self::into_trait_object);
TraitObject(self.trait_box(), Arc::new(decoder))
}
}
pub struct TraitObjectCoder<T: ?Sized + Coding + 'static> {
entries: HashMap<u64, Box<dyn Fn(&mut dyn Read) -> TraitObject<T>>>,
}
impl<T: ?Sized + Coding + 'static> TraitObjectCoder<T> {
pub fn add_type<U: AsTraitObject<T> + Coding>(&mut self, tid_hash: u64) {
self.entries.entry(tid_hash).or_insert_with(|| Box::new(|b| U::decode(b).into_trait_object()));
}
pub fn add_obj(&mut self, tid_hash: u64, obj: &TraitObject<T>) {
self.entries.entry(tid_hash).or_insert_with(|| {
let decoder = obj.get_decoder();
Box::new(move |b| decoder(b))
});
}
pub fn encode<U: AsTraitObject<T> + Coding>(&mut self, data: U, out: &mut impl Write) {
let tid = hash_tid(ConstTypeId::of::<U>());
tid.encode(out);
self.add_type::<U>(tid);
data.encode(out);
}
pub fn encode_obj(&mut self, data: &TraitObject<T>, out: &mut impl Write) {
let tid = hash_tid(data.inner_type_id());
self.add_obj(tid, data);
tid.encode(out);
data.0.as_ref().encode(out);
}
pub fn decode(&mut self, src: &mut impl Read) -> TraitObject<T> {
let tid = u64::decode(src);
(self.entries.get(&tid).expect("Received object of unknown ConstTypeId"))(src)
}
}

View File

@@ -1,6 +1,5 @@
use std::io;
use std::sync::Mutex; use std::sync::Mutex;
use std::{mem, process}; use std::{io, mem, process};
use orchid_base::msg::{recv_msg, send_msg}; use orchid_base::msg::{recv_msg, send_msg};

View File

@@ -10,12 +10,14 @@ use lazy_static::lazy_static;
use orchid_api::atom::{Atom, AtomDrop, AtomSame, CallRef, FinalCall, Fwd, Fwded}; use orchid_api::atom::{Atom, AtomDrop, AtomSame, CallRef, FinalCall, Fwd, Fwded};
use orchid_api::expr::{Acquire, Expr, ExprNotif, ExprTicket, Release, Relocate}; use orchid_api::expr::{Acquire, Expr, ExprNotif, ExprTicket, Release, Relocate};
use orchid_api::intern::IntReq; use orchid_api::intern::IntReq;
use orchid_api::parser::CharFilter;
use orchid_api::proto::{ use orchid_api::proto::{
ExtHostNotif, ExtHostReq, ExtensionHeader, HostExtNotif, HostHeader, HostMsgSet, ExtHostNotif, ExtHostReq, ExtensionHeader, HostExtNotif, HostHeader, HostMsgSet,
}; };
use orchid_api::system::{NewSystem, SysDeclId, SysId, SystemDecl, SystemDrop}; use orchid_api::system::{NewSystem, SysDeclId, SysId, SystemDecl, SystemDrop};
use orchid_api::tree::{GetConstTree, TreeModule}; use orchid_api::tree::{GetConstTree, TreeModule};
use orchid_api_traits::{Decode, Encode}; use orchid_api_traits::{Decode, Encode};
use orchid_base::char_filter::char_filter_match;
use orchid_base::clone; use orchid_base::clone;
use orchid_base::intern::{deintern, intern}; use orchid_base::intern::{deintern, intern};
use orchid_base::reqnot::{ReqNot, Requester as _}; use orchid_base::reqnot::{ReqNot, Requester as _};
@@ -166,11 +168,12 @@ impl SystemCtor {
let ext = self.ext.upgrade().expect("SystemCtor should be freed before Extension"); let ext = self.ext.upgrade().expect("SystemCtor should be freed before Extension");
static NEXT_ID: AtomicU16 = AtomicU16::new(0); static NEXT_ID: AtomicU16 = AtomicU16::new(0);
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
let () = ext.reqnot.request(NewSystem { depends, id, system: self.decl.id }); let sys_inst = ext.reqnot.request(NewSystem { depends, id, system: self.decl.id });
let data = System(Arc::new(SystemInstData { let data = System(Arc::new(SystemInstData {
decl_id: self.decl.id, decl_id: self.decl.id,
ext: Extension(ext), ext: Extension(ext),
exprs: RwLock::default(), exprs: RwLock::default(),
lex_filter: sys_inst.lex_filter,
id, id,
})); }));
inst_g.insert(id, data.clone()); inst_g.insert(id, data.clone());
@@ -187,6 +190,7 @@ pub struct SystemInstData {
exprs: RwLock<HashMap<ExprTicket, (AtomicU32, RtExpr)>>, exprs: RwLock<HashMap<ExprTicket, (AtomicU32, RtExpr)>>,
ext: Extension, ext: Extension,
decl_id: SysDeclId, decl_id: SysDeclId,
lex_filter: CharFilter,
id: u16, id: u16,
} }
impl Drop for SystemInstData { impl Drop for SystemInstData {
@@ -211,8 +215,9 @@ impl System {
.or_insert((AtomicU32::new(1), get_expr())); .or_insert((AtomicU32::new(1), get_expr()));
ticket ticket
} }
pub fn const_tree(&self) -> TreeModule { self.0.ext.0.reqnot.request(GetConstTree(self.0.id)) } pub fn const_tree(&self) -> TreeModule { self.0.ext.0.reqnot.request(GetConstTree(self.0.id)) }
pub fn has_lexer(&self) -> bool { !self.0.lex_filter.0.is_empty() }
pub fn can_lex(&self, c: char) -> bool { char_filter_match(&self.0.lex_filter, c) }
} }
impl fmt::Debug for System { impl fmt::Debug for System {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {