commit before easter break

This commit is contained in:
2025-04-15 00:34:45 +02:00
parent f783445a76
commit 94958bfbf5
85 changed files with 1874 additions and 2189 deletions

7
Cargo.lock generated
View File

@@ -1186,13 +1186,19 @@ dependencies = [
"trait-set",
]
[[package]]
name = "orchid-macros"
version = "0.1.0"
[[package]]
name = "orchid-std"
version = "0.1.0"
dependencies = [
"async-once-cell",
"async-std",
"async-stream",
"futures",
"hashbrown 0.15.2",
"itertools",
"never",
"once_cell",
@@ -1203,6 +1209,7 @@ dependencies = [
"orchid-extension",
"ordered-float",
"rust_decimal",
"test_executors",
"tokio",
]

View File

@@ -10,5 +10,5 @@ members = [
"orchid-api",
"orchid-api-derive",
"orchid-api-traits",
"stdio-perftest", "xtask",
"stdio-perftest", "xtask", "orchid-macros",
]

View File

@@ -42,9 +42,7 @@ Prioritised macro patterns must start and end with a vectorial placeholder. They
Macros are checked from the outermost block inwards.
1. For every name token, test all named macros starting with that name
1. Take the first rule that matches in each block
2. If there are multiple matches across blocks, raise an ambiguity error
3. If the tail is implicit, recurse on it
1. If the tail is implicit, continue iterating
2. Test all prioritized macros
1. Take the first rule that matches in the highest prioritized block
@@ -78,7 +76,8 @@ Recursion has to happen through the interpreter itself, so the macro system is d
- atom `MacRecurState` holds the recursion state
- function `resolve_recur` finds all matches on a MacTree
- type: `MacRecurState -> MacTree -> MacTree`
- use all macros to find all matches in the tree
- use all relevant macros to find all matches in the tree
- since macros must contain a locally defined token, it can be assumed that at the point that a constant is evaluated and all imports in the parent module have been resolved, necessarily all relevant macro rules must have been loaded
- for each match
- check for recursion violations
- wrap the body in iife-s corresponding to the named values in the match state

View File

@@ -1,7 +1,7 @@
[package]
name = "orchid-api-derive"
version = "0.1.0"
edition = "2021"
edition = "2024"
[lib]
proc-macro = true

View File

@@ -1,7 +1,7 @@
[package]
name = "orchid-api-traits"
version = "0.1.0"
edition = "2021"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -1,7 +1,7 @@
[package]
name = "orchid-api"
version = "0.1.0"
edition = "2021"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -3,7 +3,7 @@ use std::ops::RangeInclusive;
use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request;
use crate::{ExtHostReq, HostExtReq, OrcResult, ParsId, SysId, TStr, TokenTree, TreeTicket};
use crate::{ExtHostReq, HostExtReq, OrcResult, ParsId, SysId, TStr, TokenTree};
/// - All ranges contain at least one character
/// - All ranges are in increasing characeter order
@@ -42,5 +42,5 @@ impl Request for SubLex {
#[derive(Clone, Debug, Coding)]
pub struct SubLexed {
pub pos: u32,
pub ticket: TreeTicket,
pub tree: TokenTree,
}

View File

@@ -2,8 +2,6 @@ mod lexer;
pub use lexer::*;
mod format;
pub use format::*;
mod macros;
pub use macros::*;
mod atom;
pub use atom::*;
mod error;

View File

@@ -1,86 +0,0 @@
use std::collections::HashMap;
use std::num::NonZeroU64;
use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request;
use ordered_float::NotNan;
use crate::{
Atom, Comment, ExtHostReq, HostExtReq, Location, OrcResult, Paren, ParsId, SysId, TStr, TStrv,
};
#[derive(Clone, Copy, Debug, Coding, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MacroTreeId(pub NonZeroU64);
#[derive(Clone, Debug, Coding)]
pub struct MacroTree {
pub location: Location,
pub token: MacroToken,
}
#[derive(Clone, Debug, Coding)]
pub enum MacroToken {
S(Paren, Vec<MacroTree>),
Name(TStrv),
Slot(MacroTreeId),
Lambda(Vec<MacroTree>, Vec<MacroTree>),
Ph(Placeholder),
Atom(Atom),
}
#[derive(Clone, Debug, Coding)]
pub struct MacroBlock {
pub priority: Option<NotNan<f64>>,
pub rules: Vec<MacroRule>,
}
#[derive(Clone, Debug, Coding)]
pub struct MacroRule {
pub location: Location,
pub comments: Vec<Comment>,
pub pattern: Vec<MacroTree>,
pub id: MacroId,
}
/// A specific macro rule with a specific pattern across invocations
#[derive(Clone, Copy, Debug, Coding, PartialEq, Eq, Hash)]
pub struct MacroId(pub NonZeroU64);
/// After a pattern matches, this call executes the body of the macro. This
/// request returns None if an inner nested request raised an exception
#[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(HostExtReq)]
pub struct ApplyMacro {
pub sys: SysId,
pub id: MacroId,
/// Recursion token
pub run_id: ParsId,
/// Must contain exactly the keys that were specified as placeholders in the
/// pattern
pub params: HashMap<TStr, Vec<MacroTree>>,
}
impl Request for ApplyMacro {
type Response = Option<OrcResult<Vec<MacroTree>>>;
}
#[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(ExtHostReq)]
pub struct RunMacros {
pub run_id: ParsId,
pub query: Vec<MacroTree>,
}
impl Request for RunMacros {
type Response = Option<Vec<MacroTree>>;
}
#[derive(Clone, Debug, Coding)]
pub struct Placeholder {
pub name: TStr,
pub kind: PhKind,
}
#[derive(Clone, Copy, Debug, Coding)]
pub enum PhKind {
Scalar,
Vector { priority: u8, at_least_one: bool },
}

View File

@@ -1,24 +1,18 @@
use std::num::NonZeroU64;
use orchid_api_derive::{Coding, Decode, Encode, Hierarchy};
use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request;
use crate::{Comment, HostExtReq, OrcResult, SysId, TokenTree};
use crate::{Comment, HostExtReq, OrcResult, SysId, TStrv, TokenTree};
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Encode, Decode)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)]
pub struct ParsId(pub NonZeroU64);
// impl orchid_api_traits::Decode for ParsId {
// async fn decode<R: async_std::io::Read + ?Sized>(mut read:
// std::pin::Pin<&mut R>) -> Self {
// Self(orchid_api_traits::Decode::decode(read.as_mut()).await)
// }
// }
#[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(HostExtReq)]
pub struct ParseLine {
pub sys: SysId,
pub module: TStrv,
pub comments: Vec<Comment>,
pub exported: bool,
pub line: Vec<TokenTree>,

View File

@@ -28,7 +28,7 @@ use async_std::io::{Read, Write};
use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::{Channel, Decode, Encode, MsgSet, Request, read_exact, write_exact};
use crate::{atom, expr, interner, lexer, logging, macros, parser, system, tree, vfs};
use crate::{atom, expr, interner, lexer, logging, parser, system, tree, vfs};
static HOST_INTRO: &[u8] = b"Orchid host, binary API v0\n";
pub struct HostHeader {
@@ -88,7 +88,7 @@ pub enum ExtHostReq {
SysFwd(system::SysFwd),
ExprReq(expr::ExprReq),
SubLex(lexer::SubLex),
RunMacros(macros::RunMacros),
LsModule(tree::LsModule),
}
/// Notifications sent from the extension to the host
@@ -119,7 +119,6 @@ pub enum HostExtReq {
ParseLine(parser::ParseLine),
GetMember(tree::GetMember),
VfsReq(vfs::VfsReq),
ApplyMacro(macros::ApplyMacro),
}
/// Notifications sent from the host to the extension

View File

@@ -52,11 +52,11 @@ pub struct NewSystem {
pub depends: Vec<SysId>,
}
impl Request for NewSystem {
type Response = SystemInst;
type Response = NewSystemResponse;
}
#[derive(Clone, Debug, Coding)]
pub struct SystemInst {
pub struct NewSystemResponse {
/// 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

View File

@@ -1,13 +1,13 @@
use std::collections::HashMap;
use std::num::NonZeroU64;
use std::ops::Range;
use std::sync::Arc;
use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request;
use ordered_float::NotNan;
use crate::{
Atom, Expression, HostExtReq, Location, MacroBlock, OrcError, Placeholder, SysId, TStr, TStrv,
ExprTicket, Expression, ExtHostReq, HostExtReq, Location, OrcError, SysId, TStr, TStrv,
};
/// A token tree from a lexer recursion request. Its lifetime is the lex call,
@@ -32,27 +32,24 @@ pub enum Token {
LambdaHead(Vec<TokenTree>),
/// A name segment or an operator.
Name(TStr),
/// An absolute name
Reference(TStrv),
/// A newly generated expression. The last place this is supposed to happen is
/// in lexers, parsers and macros should have enumerable many outputs
/// expressed as function calls.
NewExpr(Expression),
/// A pre-existing expression
Handle(ExprTicket),
/// ::
NS,
NS(TStr, Box<TokenTree>),
/// Line break.
BR,
/// ( Round parens ), [ Square brackets ] or { Curly braces }
S(Paren, Vec<TokenTree>),
/// A new atom
Atom(Atom),
/// Anchor to insert a subtree
Slot(TreeTicket),
/// A static compile-time error returned by failing lexers if
/// the rest of the source is likely still meaningful
/// the rest of the source is likely still meaningful. This is distinct from
/// NewExpr(Bottom) because it fails in dead branches too.
Bottom(Vec<OrcError>),
/// A comment
Comment(Arc<String>),
/// Placeholder
Ph(Placeholder),
/// Macro block head
Macro(Option<NotNan<f64>>),
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Coding)]
@@ -75,7 +72,6 @@ pub struct Item {
#[derive(Clone, Debug, Coding)]
pub enum ItemKind {
Member(Member),
Macro(MacroBlock),
Export(TStr),
Import(TStrv),
}
@@ -104,9 +100,53 @@ pub struct Module {
pub items: Vec<Item>,
}
/// Evaluate a lazy member. This call will only be issued to each system once.
#[derive(Clone, Copy, Debug, Coding, Hierarchy)]
#[extends(HostExtReq)]
pub struct GetMember(pub SysId, pub TreeId);
impl Request for GetMember {
type Response = MemberKind;
}
/// This request can only be issued while the interpreter is running, so during
/// an atom call.
#[derive(Clone, Copy, Debug, Coding, Hierarchy)]
#[extends(ExtHostReq)]
pub struct LsModule(pub SysId, pub TStrv);
impl Request for LsModule {
type Response = Result<ModuleInfo, LsModuleError>;
}
#[derive(Clone, Debug, Coding)]
pub enum LsModuleError {
InvalidPath,
IsConstant,
TreeUnavailable,
}
#[derive(Clone, Debug, Coding)]
pub struct ModuleInfo {
/// If the name isn't a canonical name, returns the true name.
pub canonical: Option<TStrv>,
/// List the names defined in this module
pub members: HashMap<TStr, MemberInfo>,
}
#[derive(Clone, Copy, Debug, Coding)]
pub struct MemberInfo {
/// true if the name is exported
pub exported: bool,
/// If it's imported, you can find the canonical name here
pub canonical: Option<TStrv>,
/// Whether the tree item is a constant value or a module
pub kind: MemberInfoKind,
}
/// Indicates what kind of node a name refers to
#[derive(Clone, Copy, Debug, Coding)]
pub enum MemberInfoKind {
/// has children obtained with [crate::LsModule]
Module,
/// has a value retrievable in [crate::ExpressionKind::Const]
Constant,
}

View File

@@ -1,7 +1,7 @@
[package]
name = "orchid-base"
version = "0.1.0"
edition = "2021"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -169,20 +169,16 @@ pub fn mk_errv(
mk_err(description, message, posv).into()
}
pub trait Reporter {
fn report(&self, e: impl Into<OrcErrv>);
}
pub struct ReporterImpl {
pub struct Reporter {
errors: RefCell<Vec<OrcErr>>,
}
impl ReporterImpl {
impl Reporter {
pub fn report(&self, e: impl Into<OrcErrv>) { self.errors.borrow_mut().extend(e.into()) }
pub fn new() -> Self { Self { errors: RefCell::new(vec![]) } }
pub fn errv(self) -> Option<OrcErrv> { OrcErrv::new(self.errors.into_inner()).ok() }
}
impl Reporter for ReporterImpl {
fn report(&self, e: impl Into<OrcErrv>) { self.errors.borrow_mut().extend(e.into()) }
}
impl Default for ReporterImpl {
impl Default for Reporter {
fn default() -> Self { Self::new() }
}

View File

@@ -270,3 +270,6 @@ pub trait Format {
impl Format for Never {
async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit { match *self {} }
}
/// Format with default strategy. Currently equal to [take_first_fmt]
pub async fn fmt(v: &(impl Format + ?Sized), i: &Interner) -> String { take_first_fmt(v, i).await }

View File

@@ -15,7 +15,6 @@ pub mod interner;
pub mod join;
pub mod location;
pub mod logging;
pub mod macros;
mod match_mapping;
pub mod msg;
pub mod name;

View File

@@ -1,167 +0,0 @@
use std::marker::PhantomData;
use std::rc::Rc;
use std::sync::Arc;
use async_stream::stream;
use futures::future::join_all;
use futures::{FutureExt, StreamExt};
use never::Never;
use trait_set::trait_set;
use crate::format::{FmtCtx, FmtUnit, Format, Variants};
use crate::interner::Interner;
use crate::location::Pos;
use crate::name::Sym;
use crate::tree::{Paren, Ph};
use crate::{api, match_mapping, tl_cache};
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct MacroSlot<'a>(api::MacroTreeId, PhantomData<&'a ()>);
impl MacroSlot<'_> {
pub fn id(self) -> api::MacroTreeId { self.0 }
}
trait_set! {
pub trait MacroAtomToApi<A> = AsyncFnMut(&A) -> api::MacroToken;
pub trait MacroAtomFromApi<'a, A> = AsyncFnMut(&api::Atom) -> MTok<'a, A>;
}
#[derive(Clone, Debug)]
pub struct MTree<'a, A> {
pub pos: Pos,
pub tok: Rc<MTok<'a, A>>,
}
impl<'a, A> MTree<'a, A> {
pub(crate) async fn from_api(
api: &api::MacroTree,
do_atom: &mut impl MacroAtomFromApi<'a, A>,
i: &Interner,
) -> Self {
Self {
pos: Pos::from_api(&api.location, i).await,
tok: Rc::new(MTok::from_api(&api.token, i, do_atom).await),
}
}
pub(crate) async fn to_api(&self, do_atom: &mut impl MacroAtomToApi<A>) -> api::MacroTree {
api::MacroTree {
location: self.pos.to_api(),
token: self.tok.to_api(do_atom).boxed_local().await,
}
}
}
impl<A: Format> Format for MTree<'_, A> {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
self.tok.print(c).boxed_local().await
}
}
#[derive(Clone, Debug)]
pub enum MTok<'a, A> {
S(Paren, Vec<MTree<'a, A>>),
Name(Sym),
Slot(MacroSlot<'a>),
Lambda(Vec<MTree<'a, A>>, Vec<MTree<'a, A>>),
Ph(Ph),
Atom(A),
/// Used in extensions to directly return input
Ref(Arc<MTok<'a, Never>>),
/// Used in the matcher to skip previous macro output which can only go in
/// vectorial placeholders
Done(Rc<MTok<'a, A>>),
}
impl<'a, A> MTok<'a, A> {
pub(crate) async fn from_api(
api: &api::MacroToken,
i: &Interner,
do_atom: &mut impl MacroAtomFromApi<'a, A>,
) -> Self {
match_mapping!(&api, api::MacroToken => MTok::<'a, A> {
Lambda(x => mtreev_from_api(x, i, do_atom).await, b => mtreev_from_api(b, i, do_atom).await),
Name(t => Sym::from_api(*t, i).await),
Slot(tk => MacroSlot(*tk, PhantomData)),
S(p.clone(), b => mtreev_from_api(b, i, do_atom).await),
Ph(ph => Ph::from_api(ph, i).await),
} {
api::MacroToken::Atom(a) => do_atom(a).await
})
}
pub(crate) async fn to_api(&self, do_atom: &mut impl MacroAtomToApi<A>) -> api::MacroToken {
async fn sink<T>(n: &Never) -> T { match *n {} }
match_mapping!(&self, MTok => api::MacroToken {
Lambda(x => mtreev_to_api(x, do_atom).await, b => mtreev_to_api(b, do_atom).await),
Name(t.tok().to_api()),
Ph(ph.to_api()),
S(p.clone(), b => mtreev_to_api(b, do_atom).await),
Slot(tk.0.clone()),
} {
MTok::Ref(r) => r.to_api(&mut sink).boxed_local().await,
MTok::Done(t) => t.to_api(do_atom).boxed_local().await,
MTok::Atom(a) => do_atom(a).await,
})
}
pub fn at(self, pos: Pos) -> MTree<'a, A> { MTree { pos, tok: Rc::new(self) } }
}
impl<A: Format> Format for MTok<'_, A> {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
match self {
Self::Atom(a) => a.print(c).await,
Self::Done(d) =>
FmtUnit::new(tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("(Done){0l}"))), [
d.print(c).boxed_local().await,
]),
Self::Lambda(arg, b) => FmtUnit::new(
tl_cache!(Rc<Variants>: Rc::new(Variants::default()
.unbounded("\\{0b}.{1l}")
.bounded("(\\{0b}.{1b})"))),
[mtreev_fmt(arg, c).await, mtreev_fmt(b, c).await],
),
Self::Name(n) => format!("{n}").into(),
Self::Ph(ph) => format!("{ph}").into(),
Self::Ref(r) =>
FmtUnit::new(tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("(ref){0l}"))), [
r.print(c).boxed_local().await,
]),
Self::S(p, body) => FmtUnit::new(
match *p {
Paren::Round => Rc::new(Variants::default().bounded("({0b})")),
Paren::Curly => Rc::new(Variants::default().bounded("{{0b}}")),
Paren::Square => Rc::new(Variants::default().bounded("[{0b}]")),
},
[mtreev_fmt(body, c).await],
),
Self::Slot(slot) => format!("{:?}", slot.0).into(),
}
}
}
pub async fn mtreev_from_api<'a, 'b, A>(
apiv: impl IntoIterator<Item = &'b api::MacroTree>,
i: &Interner,
do_atom: &'b mut (impl MacroAtomFromApi<'a, A> + 'b),
) -> Vec<MTree<'a, A>> {
stream! {
for api in apiv {
yield MTree::from_api(api, do_atom, i).boxed_local().await
}
}
.collect()
.await
}
pub async fn mtreev_to_api<'a: 'b, 'b, A: 'b>(
v: impl IntoIterator<Item = &'b MTree<'a, A>>,
do_atom: &mut impl MacroAtomToApi<A>,
) -> Vec<api::MacroTree> {
let mut out = Vec::new();
for t in v {
out.push(t.to_api(do_atom).await);
}
out
}
pub async fn mtreev_fmt<'a: 'b, 'b, A: 'b + Format>(
v: impl IntoIterator<Item = &'b MTree<'a, A>>,
c: &(impl FmtCtx + ?Sized),
) -> FmtUnit {
FmtUnit::sequence(" ", None, join_all(v.into_iter().map(|t| t.print(c))).await)
}

View File

@@ -2,11 +2,10 @@
use std::borrow::Borrow;
use std::hash::Hash;
use std::iter::Cloned;
use std::num::{NonZeroU64, NonZeroUsize};
use std::ops::{Bound, Deref, Index, RangeBounds};
use std::ops::{Deref, Index};
use std::path::Path;
use std::{fmt, slice, vec};
use std::{fmt, vec};
use futures::future::{OptionFuture, join_all};
use itertools::Itertools;

View File

@@ -2,16 +2,30 @@ use std::fmt::{self, Display};
use std::iter;
use std::ops::{Deref, Range};
use futures::FutureExt;
use futures::future::join_all;
use itertools::Itertools;
use crate::api;
use crate::error::{OrcRes, Reporter, mk_err, mk_errv};
use crate::format::{Format, take_first_fmt};
use crate::format::fmt;
use crate::interner::{Interner, Tok};
use crate::location::Pos;
use crate::name::{VName, VPath};
use crate::tree::{AtomRepr, ExtraTok, Paren, TokTree, Token};
use crate::name::VPath;
use crate::tree::{ExprRepr, ExtraTok, Paren, TokTree, Token};
pub trait ParseCtx {
fn i(&self) -> &Interner;
fn reporter(&self) -> &Reporter;
}
pub struct ParseCtxImpl<'a> {
pub i: &'a Interner,
pub r: &'a Reporter,
}
impl ParseCtx for ParseCtxImpl<'_> {
fn i(&self) -> &Interner { self.i }
fn reporter(&self) -> &Reporter { self.r }
}
pub fn name_start(c: char) -> bool { c.is_alphabetic() || c == '_' }
pub fn name_char(c: char) -> bool { name_start(c) || c.is_numeric() }
@@ -21,51 +35,44 @@ 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
/// one token for error reporting on empty subsections.
#[derive(Debug)]
pub struct Snippet<'a, 'b, A: AtomRepr, X: ExtraTok> {
prev: &'a TokTree<'b, A, X>,
cur: &'a [TokTree<'b, A, X>],
interner: &'a Interner,
pub struct Snippet<'a, A: ExprRepr, X: ExtraTok> {
prev: &'a TokTree<A, X>,
cur: &'a [TokTree<A, X>],
}
impl<'a, 'b, A: AtomRepr, X: ExtraTok> Snippet<'a, 'b, A, X> {
pub fn new(
prev: &'a TokTree<'b, A, X>,
cur: &'a [TokTree<'b, A, X>],
interner: &'a Interner,
) -> Self {
Self { prev, cur, interner }
}
pub fn i(&self) -> &'a Interner { self.interner }
impl<'a, A, X> Snippet<'a, A, X>
where
A: ExprRepr,
X: ExtraTok,
{
pub fn new(prev: &'a TokTree<A, X>, cur: &'a [TokTree<A, X>]) -> Self { Self { prev, cur } }
pub fn split_at(self, pos: u32) -> (Self, Self) {
let Self { prev, cur, interner } = self;
let fst = Self { prev, cur: &cur[..pos as usize], interner };
let Self { prev, cur } = self;
let fst = Self { prev, cur: &cur[..pos as usize] };
let new_prev = if pos == 0 { self.prev } else { &self.cur[pos as usize - 1] };
let snd = Self { prev: new_prev, cur: &self.cur[pos as usize..], interner };
let snd = Self { prev: new_prev, cur: &self.cur[pos as usize..] };
(fst, snd)
}
pub fn find_idx(self, mut f: impl FnMut(&Token<'b, A, X>) -> bool) -> Option<u32> {
pub fn find_idx(self, mut f: impl FnMut(&Token<A, X>) -> bool) -> Option<u32> {
self.cur.iter().position(|t| f(&t.tok)).map(|t| t as u32)
}
pub fn get(self, idx: u32) -> Option<&'a TokTree<'b, A, X>> { self.cur.get(idx as usize) }
pub fn get(self, idx: u32) -> Option<&'a TokTree<A, X>> { self.cur.get(idx as usize) }
pub fn len(self) -> u32 { self.cur.len() as u32 }
pub fn prev(self) -> &'a TokTree<'b, A, X> { self.prev }
pub fn prev(self) -> &'a TokTree<A, X> { self.prev }
pub fn pos(self) -> Range<u32> {
(self.cur.first().map(|f| f.range.start..self.cur.last().unwrap().range.end))
.unwrap_or(self.prev.range.clone())
}
pub fn pop_front(self) -> Option<(&'a TokTree<'b, A, X>, Self)> {
pub fn pop_front(self) -> Option<(&'a TokTree<A, X>, Self)> {
self.cur.first().map(|r| (r, self.split_at(1).1))
}
pub fn pop_back(self) -> Option<(Self, &'a TokTree<'b, A, X>)> {
pub fn pop_back(self) -> Option<(Self, &'a TokTree<A, X>)> {
self.cur.last().map(|r| (self.split_at(self.len() - 1).0, r))
}
pub fn split_once(self, f: impl FnMut(&Token<'b, A, X>) -> bool) -> Option<(Self, Self)> {
pub fn split_once(self, f: impl FnMut(&Token<A, X>) -> bool) -> Option<(Self, Self)> {
let idx = self.find_idx(f)?;
Some((self.split_at(idx).0, self.split_at(idx + 1).1))
}
pub fn split(
mut self,
mut f: impl FnMut(&Token<'b, A, X>) -> bool,
) -> impl Iterator<Item = Self> {
pub fn split(mut self, mut f: impl FnMut(&Token<A, X>) -> bool) -> impl Iterator<Item = Self> {
iter::from_fn(move || {
if self.is_empty() {
return None;
@@ -77,26 +84,22 @@ impl<'a, 'b, A: AtomRepr, X: ExtraTok> Snippet<'a, 'b, A, X> {
}
pub fn is_empty(self) -> bool { self.len() == 0 }
pub fn skip_fluff(self) -> Self {
let non_fluff_start = self.find_idx(|t| !matches!(t, Token::NS | Token::Comment(_)));
let non_fluff_start = self.find_idx(|t| !matches!(t, Token::BR | Token::Comment(_)));
self.split_at(non_fluff_start.unwrap_or(self.len())).1
}
/// Format the argument using the context held in this snippet
pub async fn fmt(self, v: &(impl Format + ?Sized)) -> String { take_first_fmt(v, self.i()).await }
}
impl<A: AtomRepr, X: ExtraTok> Copy for Snippet<'_, '_, A, X> {}
impl<A: AtomRepr, X: ExtraTok> Clone for Snippet<'_, '_, A, X> {
impl<A: ExprRepr, X: ExtraTok> Copy for Snippet<'_, A, X> {}
impl<A: ExprRepr, X: ExtraTok> Clone for Snippet<'_, A, X> {
fn clone(&self) -> Self { *self }
}
impl<'b, A: AtomRepr, X: ExtraTok> Deref for Snippet<'_, 'b, A, X> {
type Target = [TokTree<'b, A, X>];
impl<A: ExprRepr, X: ExtraTok> Deref for Snippet<'_, A, X> {
type Target = [TokTree<A, X>];
fn deref(&self) -> &Self::Target { self.cur }
}
/// Remove tokens that aren't meaningful in expression context, such as comments
/// or line breaks
pub fn strip_fluff<'a, A: AtomRepr, X: ExtraTok>(
tt: &TokTree<'a, A, X>,
) -> Option<TokTree<'a, A, X>> {
pub fn strip_fluff<A: ExprRepr, X: ExtraTok>(tt: &TokTree<A, X>) -> Option<TokTree<A, X>> {
let tok = match &tt.tok {
Token::BR => return None,
Token::Comment(_) => return None,
@@ -125,9 +128,10 @@ impl fmt::Display for Comment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "--[{}]--", self.text) }
}
pub async fn line_items<'a, 'b, A: AtomRepr, X: ExtraTok>(
snip: Snippet<'a, 'b, A, X>,
) -> Vec<Parsed<'a, 'b, Vec<Comment>, A, X>> {
pub async fn line_items<'a, A: ExprRepr, X: ExtraTok>(
ctx: &impl ParseCtx,
snip: Snippet<'a, A, X>,
) -> Vec<Parsed<'a, Vec<Comment>, A, X>> {
let mut items = Vec::new();
let mut comments = Vec::new();
for mut line in snip.split(|t| matches!(t, Token::BR)) {
@@ -143,7 +147,7 @@ pub async fn line_items<'a, 'b, A: AtomRepr, X: ExtraTok>(
let comments = join_all(comments.drain(..).chain(cmts.cur).map(|t| async {
match &t.tok {
Token::Comment(c) =>
Comment { text: tail.i().i(&**c).await, pos: Pos::Range(t.range.clone()) },
Comment { text: ctx.i().i(&**c).await, pos: Pos::Range(t.range.clone()) },
_ => unreachable!("All are comments checked above"),
}
}))
@@ -155,23 +159,26 @@ pub async fn line_items<'a, 'b, A: AtomRepr, X: ExtraTok>(
items
}
pub async fn try_pop_no_fluff<'a, 'b, A: AtomRepr, X: ExtraTok>(
snip: Snippet<'a, 'b, A, X>,
) -> ParseRes<'a, 'b, &'a TokTree<'b, A, X>, A, X> {
pub async fn try_pop_no_fluff<'a, A: ExprRepr, X: ExtraTok>(
ctx: &impl ParseCtx,
snip: Snippet<'a, A, X>,
) -> ParseRes<'a, &'a TokTree<A, X>, A, X> {
match snip.skip_fluff().pop_front() {
Some((output, tail)) => Ok(Parsed { output, tail }),
None =>
Err(mk_errv(snip.i().i("Unexpected end").await, "Pattern ends abruptly", [Pos::Range(
None => Err(mk_errv(ctx.i().i("Unexpected end").await, "Pattern ends abruptly", [Pos::Range(
snip.pos(),
)
.into()])),
}
}
pub async fn expect_end(snip: Snippet<'_, '_, impl AtomRepr, impl ExtraTok>) -> OrcRes<()> {
pub async fn expect_end(
ctx: &impl ParseCtx,
snip: Snippet<'_, impl ExprRepr, impl ExtraTok>,
) -> OrcRes<()> {
match snip.skip_fluff().get(0) {
Some(surplus) => Err(mk_errv(
snip.i().i("Extra code after end of line").await,
ctx.i().i("Extra code after end of line").await,
"Code found after the end of the line",
[Pos::Range(surplus.range.clone()).into()],
)),
@@ -179,128 +186,86 @@ pub async fn expect_end(snip: Snippet<'_, '_, impl AtomRepr, impl ExtraTok>) ->
}
}
pub async fn expect_tok<'a, 'b, A: AtomRepr, X: ExtraTok>(
snip: Snippet<'a, 'b, A, X>,
pub async fn expect_tok<'a, A: ExprRepr, X: ExtraTok>(
ctx: &impl ParseCtx,
snip: Snippet<'a, A, X>,
tok: Tok<String>,
) -> ParseRes<'a, 'b, (), A, X> {
let Parsed { output: head, tail } = try_pop_no_fluff(snip).await?;
) -> ParseRes<'a, (), A, X> {
let Parsed { output: head, tail } = try_pop_no_fluff(ctx, snip).await?;
match &head.tok {
Token::Name(n) if *n == tok => Ok(Parsed { output: (), tail }),
t => Err(mk_errv(
snip.i().i("Expected specific keyword").await,
format!("Expected {tok} but found {:?}", snip.fmt(t).await),
ctx.i().i("Expected specific keyword").await,
format!("Expected {tok} but found {:?}", fmt(t, ctx.i()).await),
[Pos::Range(head.range.clone()).into()],
)),
}
}
pub struct Parsed<'a, 'b, T, A: AtomRepr, X: ExtraTok> {
pub struct Parsed<'a, T, H: ExprRepr, X: ExtraTok> {
pub output: T,
pub tail: Snippet<'a, 'b, A, X>,
pub tail: Snippet<'a, H, X>,
}
pub type ParseRes<'a, 'b, T, A, X> = OrcRes<Parsed<'a, 'b, T, A, X>>;
pub type ParseRes<'a, T, H, X> = OrcRes<Parsed<'a, T, H, X>>;
pub async fn parse_multiname<'a, 'b, A: AtomRepr, X: ExtraTok>(
ctx: &(impl Reporter + ?Sized),
tail: Snippet<'a, 'b, A, X>,
) -> ParseRes<'a, 'b, Vec<(Import, Pos)>, A, X> {
let ret = rec(ctx, tail).await;
#[allow(clippy::type_complexity)] // it's an internal function
pub async fn rec<'a, 'b, A: AtomRepr, X: ExtraTok>(
ctx: &(impl Reporter + ?Sized),
tail: Snippet<'a, 'b, A, X>,
) -> ParseRes<'a, 'b, Vec<(Vec<Tok<String>>, Option<Tok<String>>, Pos)>, A, X> {
let comma = tail.i().i(",").await;
let globstar = tail.i().i("*").await;
let Some((name, tail)) = tail.skip_fluff().pop_front() else {
pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>(
ctx: &impl ParseCtx,
tail: Snippet<'a, A, X>,
) -> ParseRes<'a, Vec<(Import, Pos)>, A, X> {
let Some((tt, tail)) = tail.skip_fluff().pop_front() else {
return Err(mk_errv(
tail.i().i("Expected name").await,
"Expected a name, a list of names, or a globstar.",
ctx.i().i("Expected token").await,
"Expected a name, a parenthesized list of names, or a globstar.",
[Pos::Range(tail.pos()).into()],
));
};
if let Some((Token::NS, tail)) = tail.skip_fluff().pop_front().map(|(tt, s)| (&tt.tok, s)) {
let n = match &name.tok {
Token::Name(n) if n.starts_with(name_start) => Ok(n),
_ =>
Err(mk_err(tail.i().i("Unexpected name prefix").await, "Only names can precede ::", [
Pos::Range(name.range.clone()).into(),
])),
};
match (Box::pin(rec(ctx, tail)).await, n) {
(Err(ev), n) => Err(ev.extended(n.err())),
(Ok(Parsed { tail, .. }), Err(e)) => {
ctx.report(e);
Ok(Parsed { output: vec![], tail })
},
(Ok(Parsed { tail, output }), Ok(pre)) => Ok(Parsed {
output: output.into_iter().update(|i| i.0.push(pre.clone())).collect_vec(),
tail,
}),
}
} else {
let output = match &name.tok {
Token::Name(ntok) => {
let nopt = match ntok {
n if *n == globstar => None,
n if n.starts_with(op_char) => {
return Err(mk_errv(
tail.i().i("Unescaped operator in multiname").await,
"Operators in multinames should be enclosed in []",
[Pos::Range(name.range.clone()).into()],
));
},
n => Some(n.clone()),
};
vec![(vec![], nopt, Pos::Range(name.range.clone()))]
},
Token::S(Paren::Square, b) => {
let mut ok = Vec::new();
for tt in b.iter() {
let ret = rec(tt, ctx).await;
#[allow(clippy::type_complexity)] // it's an internal function
pub async fn rec<A: ExprRepr, X: ExtraTok>(
tt: &TokTree<A, X>,
ctx: &impl ParseCtx,
) -> OrcRes<Vec<(Vec<Tok<String>>, Option<Tok<String>>, Pos)>> {
let ttpos = Pos::Range(tt.range.clone());
match &tt.tok {
Token::Name(n) if n.starts_with(op_char) =>
ok.push((vec![], Some(n.clone()), Pos::Range(tt.range.clone()))),
Token::BR | Token::Comment(_) => (),
_ => ctx.report(mk_err(
tail.i().i("Non-operator in escapement in multiname").await,
"In multinames, [] functions as a literal name list reserved for operators",
[Pos::Range(name.range.clone()).into()],
)),
}
}
ok
Token::NS(ns, body) => {
if !ns.starts_with(name_start) {
ctx.reporter().report(mk_err(
ctx.i().i("Unexpected name prefix").await,
"Only names can precede ::",
[ttpos.into()],
))
};
let out = Box::pin(rec(body, ctx)).await?;
Ok(out.into_iter().update(|i| i.0.push(ns.clone())).collect_vec())
},
Token::Name(ntok) => {
let n = ntok;
let nopt = Some(n.clone());
Ok(vec![(vec![], nopt, Pos::Range(tt.range.clone()))])
},
Token::S(Paren::Round, b) => {
let mut ok = Vec::new();
let body = Snippet::new(name, b, tail.interner);
for csent in body.split(|n| matches!(n, Token::Name(n) if *n == comma)) {
match Box::pin(rec(ctx, csent)).await {
Err(e) => ctx.report(e),
Ok(Parsed { output, tail }) => match tail.get(0) {
None => ok.extend(output),
Some(t) => ctx.report(mk_err(
tail.i().i("Unexpected token in multiname group").await,
"Unexpected token. Likely missing a :: or , or wanted [] instead of ()",
[Pos::Range(t.range.clone()).into()],
)),
},
let mut o = Vec::new();
let mut body = Snippet::new(tt, b);
while let Some((output, tail)) = body.pop_front() {
match rec(output, ctx).boxed_local().await {
Ok(names) => o.extend(names),
Err(e) => ctx.reporter().report(e),
}
body = tail;
}
ok
Ok(o)
},
t => {
return Err(mk_errv(
tail.i().i("Unrecognized name end").await,
format!("Names cannot end with {:?} tokens", tail.fmt(t).await),
[Pos::Range(name.range.clone()).into()],
ctx.i().i("Unrecognized name end").await,
format!("Names cannot end with {:?} tokens", fmt(t, ctx.i()).await),
[ttpos.into()],
));
},
};
Ok(Parsed { output, tail })
}
}
ret.map(|Parsed { output, tail }| {
ret.map(|output| {
let output = (output.into_iter())
.map(|(p, name, pos)| (Import { path: VPath::new(p.into_iter().rev()), name }, pos))
.collect_vec();
@@ -327,14 +292,5 @@ mod test {
use super::Snippet;
fn _covary_snip_a<'a, 'b>(
x: Snippet<'static, 'b, Never, Never>,
) -> Snippet<'a, 'b, Never, Never> {
x
}
fn _covary_snip_b<'a, 'b>(
x: Snippet<'a, 'static, Never, Never>,
) -> Snippet<'a, 'b, Never, Never> {
x
}
fn _covary_snip_a<'a>(x: Snippet<'static, Never, Never>) -> Snippet<'a, Never, Never> { x }
}

View File

@@ -1,4 +1,4 @@
use std::any::{Any, TypeId};
use std::any::Any;
use std::cell::RefCell;
use std::future::Future;
use std::marker::PhantomData;
@@ -46,6 +46,9 @@ pub trait ReqHandlish {
}
fn defer_drop_objsafe(&self, val: Box<dyn Any>);
}
impl ReqHandlish for &'_ dyn ReqHandlish {
fn defer_drop_objsafe(&self, val: Box<dyn Any>) { (**self).defer_drop_objsafe(val) }
}
#[derive(destructure)]
pub struct RequestHandle<'a, MS: MsgSet> {
@@ -170,12 +173,16 @@ pub trait DynRequester {
pub struct MappedRequester<'a, T: 'a>(Box<dyn Fn(T) -> LocalBoxFuture<'a, RawReply> + 'a>, Logger);
impl<'a, T> MappedRequester<'a, T> {
fn new<U: DynRequester + 'a>(req: U, logger: Logger) -> Self
where T: Into<U::Transfer> {
fn new<U: DynRequester + 'a, F: Fn(T) -> U::Transfer + 'a>(
req: U,
cb: F,
logger: Logger,
) -> Self {
let req_arc = Arc::new(req);
let cb_arc = Arc::new(cb);
MappedRequester(
Box::new(move |t| {
Box::pin(clone!(req_arc; async move { req_arc.raw_request(t.into()).await}))
Box::pin(clone!(req_arc, cb_arc; async move { req_arc.raw_request(cb_arc(t)).await}))
}),
logger,
)
@@ -217,10 +224,10 @@ pub trait Requester: DynRequester {
&self,
data: R,
) -> impl Future<Output = R::Response>;
fn map<'a, U: Into<Self::Transfer>>(self) -> MappedRequester<'a, U>
fn map<'a, U>(self, cb: impl Fn(U) -> Self::Transfer + 'a) -> MappedRequester<'a, U>
where Self: Sized + 'a {
let logger = self.logger().clone();
MappedRequester::new(self, logger)
MappedRequester::new(self, cb, logger)
}
}

View File

@@ -6,38 +6,61 @@ use std::ops::Range;
use std::rc::Rc;
use std::sync::Arc;
pub use api::PhKind;
use async_stream::stream;
use futures::future::join_all;
use futures::{FutureExt, StreamExt};
use itertools::Itertools;
use never::Never;
use ordered_float::NotNan;
use orchid_api_traits::Coding;
use trait_set::trait_set;
use crate::error::OrcErrv;
use crate::format::{FmtCtx, FmtUnit, Format, Variants};
use crate::interner::{Interner, Tok};
use crate::location::Pos;
use crate::name::Sym;
use crate::parse::Snippet;
use crate::{api, match_mapping, tl_cache};
trait_set! {
pub trait RecurCB<'a, A: AtomRepr, X: ExtraTok> = Fn(TokTree<'a, A, X>) -> TokTree<'a, A, X>;
pub trait ExtraTok = Format + Clone + fmt::Debug;
pub trait RefDoExtra<X> = AsyncFnMut(&X, Range<u32>) -> api::TokenTree;
pub trait TokenVariant<ApiEquiv: Clone + Debug + Coding>: Format + Clone + fmt::Debug {
type FromApiCtx<'a>;
type ToApiCtx<'a>;
fn from_api(
api: &ApiEquiv,
ctx: &mut Self::FromApiCtx<'_>,
pos: Pos,
i: &Interner,
) -> impl Future<Output = Self>;
fn into_api(self, ctx: &mut Self::ToApiCtx<'_>) -> impl Future<Output = ApiEquiv>;
}
impl<T: Clone + Debug + Coding> TokenVariant<T> for Never {
type FromApiCtx<'a> = ();
type ToApiCtx<'a> = ();
async fn from_api(_: &T, _: &mut Self::FromApiCtx<'_>, _: Pos, _: &Interner) -> Self {
panic!("Cannot deserialize Never")
}
async fn into_api(self, _: &mut Self::ToApiCtx<'_>) -> T { match self {} }
}
pub fn recur<'a, A: AtomRepr, X: ExtraTok>(
tt: TokTree<'a, A, X>,
f: &impl Fn(TokTree<'a, A, X>, &dyn RecurCB<'a, A, X>) -> TokTree<'a, A, X>,
) -> TokTree<'a, A, X> {
trait_set! {
// TokenHandle
pub trait ExprRepr = TokenVariant<api::ExprTicket>;
// TokenExpr
pub trait ExtraTok = TokenVariant<api::Expression>;
}
trait_set! {
pub trait RecurCB<H: ExprRepr, X: ExtraTok> = Fn(TokTree<H, X>) -> TokTree<H, X>;
}
pub fn recur<H: ExprRepr, X: ExtraTok>(
tt: TokTree<H, X>,
f: &impl Fn(TokTree<H, X>, &dyn RecurCB<H, X>) -> TokTree<H, X>,
) -> TokTree<H, X> {
f(tt, &|TokTree { range, tok }| {
let tok = match tok {
tok @ (Token::Atom(_) | Token::BR | Token::Bottom(_) | Token::Comment(_) | Token::NS) => tok,
tok @ (Token::Name(_) | Token::Slot(_) | Token::X(_) | Token::Ph(_) | Token::Macro(_)) => tok,
tok @ Token::Reference(_) => tok,
tok @ (Token::BR | Token::Bottom(_) | Token::Comment(_) | Token::Name(_)) => tok,
tok @ (Token::Handle(_) | Token::NewExpr(_)) => tok,
Token::NS(n, b) => Token::NS(n, Box::new(recur(*b, f))),
Token::LambdaHead(arg) =>
Token::LambdaHead(arg.into_iter().map(|tt| recur(tt, f)).collect_vec()),
Token::S(p, b) => Token::S(p, b.into_iter().map(|tt| recur(tt, f)).collect_vec()),
@@ -70,68 +93,48 @@ impl Display for TokHandle<'_> {
}
#[derive(Clone, Debug)]
pub struct TokTree<'a, A: AtomRepr, X: ExtraTok> {
pub tok: Token<'a, A, X>,
pub struct TokTree<H: ExprRepr, X: ExtraTok> {
pub tok: Token<H, X>,
pub range: Range<u32>,
}
impl<'b, A: AtomRepr, X: ExtraTok> TokTree<'b, A, X> {
pub async fn from_api(tt: &api::TokenTree, ctx: &mut A::Ctx, i: &Interner) -> Self {
let tok = match_mapping!(&tt.token, api::Token => Token::<'b, A, X> {
BR, NS,
Atom(a => A::from_api(a, Pos::Range(tt.range.clone()), ctx).await),
impl<H: ExprRepr, X: ExtraTok> TokTree<H, X> {
pub async fn from_api(
tt: &api::TokenTree,
hctx: &mut H::FromApiCtx<'_>,
xctx: &mut X::FromApiCtx<'_>,
i: &Interner,
) -> Self {
let tok = match_mapping!(&tt.token, api::Token => Token::<H, X> {
BR,
NS(n => Tok::from_api(*n, i).await,
b => Box::new(Self::from_api(b, hctx, xctx, i).boxed_local().await)),
Bottom(e => OrcErrv::from_api(e, i).await),
LambdaHead(arg => ttv_from_api(arg, ctx, i).await),
LambdaHead(arg => ttv_from_api(arg, hctx, xctx, i).await),
Name(n => Tok::from_api(*n, i).await),
S(*par, b => ttv_from_api(b, ctx, i).await),
S(*par, b => ttv_from_api(b, hctx, xctx, i).await),
Comment(c.clone()),
Slot(id => TokHandle::new(*id)),
Ph(ph => Ph::from_api(ph, i).await),
Macro(*prio),
Reference(tok => Sym::from_api(*tok, i).await)
NewExpr(expr => X::from_api(expr, xctx, Pos::Range(tt.range.clone()), i).await),
Handle(tk => H::from_api(tk, hctx, Pos::Range(tt.range.clone()), i).await)
});
Self { range: tt.range.clone(), tok }
}
pub async fn to_api(&self, do_extra: &mut impl RefDoExtra<X>) -> api::TokenTree {
let token = match_mapping!(&self.tok, Token => api::Token {
Atom(a.to_api().await),
BR,
NS,
Bottom(e.to_api()),
Comment(c.clone()),
LambdaHead(arg => ttv_to_api(arg, do_extra).boxed_local().await),
Name(n.to_api()),
Slot(tt.ticket()),
S(*p, b => ttv_to_api(b, do_extra).boxed_local().await),
Ph(ph.to_api()),
Macro(*prio),
Reference(sym.to_api()),
} {
Token::X(x) => return do_extra(x, self.range.clone()).await
});
api::TokenTree { range: self.range.clone(), token }
}
pub async fn into_api(
self,
do_extra: &mut impl FnMut(X, Range<u32>) -> api::TokenTree,
hctx: &mut H::ToApiCtx<'_>,
xctx: &mut X::ToApiCtx<'_>,
) -> api::TokenTree {
let token = match self.tok {
Token::Atom(a) => api::Token::Atom(a.to_api().await),
Token::Reference(sym) => api::Token::Reference(sym.to_api()),
Token::BR => api::Token::BR,
Token::NS => api::Token::NS,
Token::Bottom(e) => api::Token::Bottom(e.to_api()),
Token::Comment(c) => api::Token::Comment(c.clone()),
Token::LambdaHead(arg) => api::Token::LambdaHead(ttv_into_api(arg, do_extra).await),
Token::Name(n) => api::Token::Name(n.to_api()),
Token::Slot(tt) => api::Token::Slot(tt.ticket()),
Token::S(p, b) => api::Token::S(p, ttv_into_api(b, do_extra).await),
Token::Ph(Ph { kind, name }) =>
api::Token::Ph(api::Placeholder { name: name.to_api(), kind }),
Token::X(x) => return do_extra(x, self.range.clone()),
Token::Macro(prio) => api::Token::Macro(prio),
};
let token = match_mapping!(self.tok, Token => api::Token {
BR,
NS(n.to_api(), b => Box::new(b.into_api(hctx, xctx).boxed_local().await)),
Bottom(e.to_api()),
Comment(c.clone()),
LambdaHead(arg => ttv_into_api(arg, hctx, xctx).await),
Name(nn.to_api()),
S(p, b => ttv_into_api(b, hctx, xctx).await),
Handle(hand.into_api(hctx).await),
NewExpr(expr.into_api(xctx).await),
});
api::TokenTree { range: self.range.clone(), token }
}
@@ -139,9 +142,16 @@ impl<'b, A: AtomRepr, X: ExtraTok> TokTree<'b, A, X> {
pub fn as_name(&self) -> Option<Tok<String>> {
if let Token::Name(n) = &self.tok { Some(n.clone()) } else { None }
}
pub fn as_s<'a>(&'a self, par: Paren, i: &'a Interner) -> Option<Snippet<'a, 'b, A, X>> {
self.tok.as_s(par).map(|slc| Snippet::new(self, slc, i))
pub fn as_s(&self, par: Paren) -> Option<Snippet<'_, H, X>> {
self.tok.as_s(par).map(|slc| Snippet::new(self, slc))
}
pub fn as_lambda(&self) -> Option<Snippet<'_, H, X>> {
match &self.tok {
Token::LambdaHead(arg) => Some(Snippet::new(self, arg)),
_ => None,
}
}
pub fn is_fluff(&self) -> bool { matches!(self.tok, Token::Comment(_) | Token::BR) }
pub fn lambda(arg: Vec<Self>, mut body: Vec<Self>) -> Self {
let arg_range = ttv_range(&arg);
let s_range = arg_range.start..body.last().expect("Lambda with empty body!").range.end;
@@ -149,53 +159,44 @@ impl<'b, A: AtomRepr, X: ExtraTok> TokTree<'b, A, X> {
Token::S(Paren::Round, body).at(s_range)
}
}
impl<A: AtomRepr, X: ExtraTok> Format for TokTree<'_, A, X> {
impl<H: ExprRepr, X: ExtraTok> Format for TokTree<H, X> {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
self.tok.print(c).await
}
}
pub async fn ttv_from_api<A: AtomRepr, X: ExtraTok>(
pub async fn ttv_from_api<H: ExprRepr, X: ExtraTok>(
tokv: impl IntoIterator<Item: Borrow<api::TokenTree>>,
ctx: &mut A::Ctx,
hctx: &mut H::FromApiCtx<'_>,
xctx: &mut X::FromApiCtx<'_>,
i: &Interner,
) -> Vec<TokTree<'static, A, X>> {
) -> Vec<TokTree<H, X>> {
stream! {
for tok in tokv {
yield TokTree::<A, X>::from_api(tok.borrow(), ctx, i).boxed_local().await
yield TokTree::<H, X>::from_api(tok.borrow(), hctx, xctx, i).boxed_local().await
}
}
.collect()
.await
}
pub async fn ttv_to_api<'a, A: AtomRepr, X: ExtraTok>(
tokv: impl IntoIterator<Item: Borrow<TokTree<'a, A, X>>>,
do_extra: &mut impl RefDoExtra<X>,
) -> Vec<api::TokenTree> {
let mut output = Vec::new();
for tok in tokv {
output.push(Borrow::<TokTree<A, X>>::borrow(&tok).to_api(do_extra).await)
}
output
}
pub async fn ttv_into_api<'a, A: AtomRepr, X: ExtraTok>(
tokv: impl IntoIterator<Item = TokTree<'a, A, X>>,
do_extra: &mut impl FnMut(X, Range<u32>) -> api::TokenTree,
pub async fn ttv_into_api<H: ExprRepr, X: ExtraTok>(
tokv: impl IntoIterator<Item = TokTree<H, X>>,
hctx: &mut H::ToApiCtx<'_>,
xctx: &mut X::ToApiCtx<'_>,
) -> Vec<api::TokenTree> {
stream! {
for tok in tokv {
yield tok.into_api(do_extra).await
yield tok.into_api(hctx, xctx).await
}
}
.collect()
.await
}
pub fn wrap_tokv<'a, A: AtomRepr, X: ExtraTok>(
items: impl IntoIterator<Item = TokTree<'a, A, X>>,
) -> TokTree<'a, A, X> {
pub fn wrap_tokv<H: ExprRepr, X: ExtraTok>(
items: impl IntoIterator<Item = TokTree<H, X>>,
) -> TokTree<H, X> {
let items_v = items.into_iter().collect_vec();
match items_v.len() {
0 => panic!("A tokv with no elements is illegal"),
@@ -211,70 +212,54 @@ pub use api::Paren;
/// Lexer output variant
#[derive(Clone, Debug)]
pub enum Token<'a, A: AtomRepr, X: ExtraTok> {
pub enum Token<H: ExprRepr, X: ExtraTok> {
/// Information about the code addressed to the human reader or dev tooling
/// It has no effect on the behaviour of the program unless it's explicitly
/// read via reflection
Comment(Arc<String>),
/// The part of a lambda between `\` and `.` enclosing the argument. The body
/// stretches to the end of the enclosing parens or the end of the const line
LambdaHead(Vec<TokTree<'a, A, X>>),
LambdaHead(Vec<TokTree<H, X>>),
/// A binding, operator, or a segment of a namespaced::name
Name(Tok<String>),
/// The namespace separator ::
NS,
/// A namespace prefix, like `my_ns::` followed by a token
NS(Tok<String>, Box<TokTree<H, X>>),
/// A line break
BR,
/// `()`, `[]`, or `{}`
S(Paren, Vec<TokTree<'a, A, X>>),
/// A fully formed reference to external code emitted by a lexer plugin
Reference(Sym),
/// A value emitted by a lexer plugin
Atom(A),
S(Paren, Vec<TokTree<H, X>>),
/// A newly instantiated expression
NewExpr(X),
/// An existing expr from a nested lexer
Handle(H),
/// A grammar error emitted by a lexer plugin if it was possible to continue
/// reading. Parsers should treat it as an atom unless it prevents parsing,
/// in which case both this and a relevant error should be returned.
Bottom(OrcErrv),
/// An instruction from a plugin for the lexer to embed a subexpression
/// without retransmitting it. It should not appear anywhere outside lexer
/// plugin responses.
Slot(TokHandle<'a>),
/// Additional domain-specific token types
X(X),
/// A placeholder for metaprogramming, either $name, ..$name, ..$name:N,
/// ...$name, or ...$name:N
Ph(Ph),
/// `macro` or `macro(`X`)` where X is any valid floating point number
/// expression. `macro` is not a valid name in Orchid for this reason.
Macro(Option<NotNan<f64>>),
}
impl<'a, A: AtomRepr, X: ExtraTok> Token<'a, A, X> {
pub fn at(self, range: Range<u32>) -> TokTree<'a, A, X> { TokTree { range, tok: self } }
impl<H: ExprRepr, X: ExtraTok> Token<H, X> {
pub fn at(self, range: Range<u32>) -> TokTree<H, X> { TokTree { range, tok: self } }
pub fn is_kw(&self, tk: Tok<String>) -> bool { matches!(self, Token::Name(n) if *n == tk) }
pub fn as_s(&self, par: Paren) -> Option<&[TokTree<'a, A, X>]> {
pub fn as_s(&self, par: Paren) -> Option<&[TokTree<H, X>]> {
match self {
Self::S(p, b) if *p == par => Some(b),
_ => None,
}
}
}
impl<A: AtomRepr, X: ExtraTok> Format for Token<'_, A, X> {
impl<H: ExprRepr, X: ExtraTok> Format for Token<H, X> {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
match self {
Self::Atom(a) => a.print(c).await,
Self::BR => "\n".to_string().into(),
Self::Bottom(err) if err.len() == 1 => format!("Bottom({}) ", err.one().unwrap()).into(),
Self::Bottom(err) => format!("Botttom(\n{}) ", indent(&err.to_string())).into(),
Self::Comment(c) => format!("--[{c}]--").into(),
Self::LambdaHead(arg) =>
FmtUnit::new(tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("\\{0b}."))), [
ttv_fmt(arg, c).await,
]),
Self::NS => "::".to_string().into(),
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("\\{0b}.")))
.units([ttv_fmt(arg, c).await]),
Self::NS(n, b) => tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{0}::{1l}")))
.units([n.to_string().into(), b.print(c).boxed_local().await]),
Self::Name(n) => format!("{n}").into(),
Self::Reference(sym) => format!("{sym}").into(),
Self::Slot(th) => format!("{th}").into(),
Self::Ph(ph) => format!("{ph}").into(),
Self::S(p, b) => FmtUnit::new(
match *p {
Paren::Round => tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("({0b})"))),
@@ -283,67 +268,22 @@ impl<A: AtomRepr, X: ExtraTok> Format for Token<'_, A, X> {
},
[ttv_fmt(b, c).await],
),
Self::X(x) => x.print(c).await,
Self::Macro(None) => "macro".to_string().into(),
Self::Macro(Some(prio)) => format!("macro({prio})").into(),
Self::Handle(h) => h.print(c).await,
Self::NewExpr(ex) => ex.print(c).await,
}
}
}
pub fn ttv_range(ttv: &[TokTree<'_, impl AtomRepr, impl ExtraTok>]) -> Range<u32> {
pub fn ttv_range<'a>(ttv: &[TokTree<impl ExprRepr + 'a, impl ExtraTok + 'a>]) -> Range<u32> {
assert!(!ttv.is_empty(), "Empty slice has no range");
ttv.first().unwrap().range.start..ttv.last().unwrap().range.end
}
pub async fn ttv_fmt<'a: 'b, 'b>(
ttv: impl IntoIterator<Item = &'b TokTree<'a, impl AtomRepr + 'b, impl ExtraTok + 'b>>,
ttv: impl IntoIterator<Item = &'b TokTree<impl ExprRepr + 'a, impl ExtraTok + 'a>>,
c: &(impl FmtCtx + ?Sized),
) -> FmtUnit {
FmtUnit::sequence(" ", None, join_all(ttv.into_iter().map(|t| t.print(c))).await)
}
pub fn indent(s: &str) -> String { s.replace("\n", "\n ") }
#[derive(Clone, Debug)]
pub struct Ph {
pub name: Tok<String>,
pub kind: PhKind,
}
impl Ph {
pub async fn from_api(api: &api::Placeholder, i: &Interner) -> Self {
Self { name: Tok::from_api(api.name, i).await, kind: api.kind }
}
pub fn to_api(&self) -> api::Placeholder {
api::Placeholder { name: self.name.to_api(), kind: self.kind }
}
}
impl Display for Ph {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let n = &self.name;
match self.kind {
PhKind::Scalar => write!(f, "${n}"),
PhKind::Vector { priority: 0, at_least_one: true } => write!(f, "...${}", self.name),
PhKind::Vector { priority: p, at_least_one: true } => write!(f, "...${}:{}", self.name, p),
PhKind::Vector { priority: 0, at_least_one: false } => write!(f, "..${}", self.name),
PhKind::Vector { priority: p, at_least_one: false } => write!(f, "..${}:{}", self.name, p),
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_covariance() {
fn _f<'a>(x: Token<'static, Never, Never>) -> Token<'a, Never, Never> { x }
}
#[test]
fn fail_covariance() {
// this fails to compile
// fn _f<'a, 'b>(x: &'a mut &'static ()) -> &'a mut &'b () { x }
// this passes because it's covariant
fn _f<'a, 'b>(x: &'a fn() -> &'static ()) -> &'a fn() -> &'b () { x }
}
}

View File

@@ -1,7 +1,7 @@
[package]
name = "orchid-extension"
version = "0.1.0"
edition = "2021"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -1,7 +1,6 @@
use std::any::{Any, TypeId, type_name};
use std::fmt;
use std::future::Future;
use std::marker::PhantomData;
use std::num::NonZeroU32;
use std::ops::Deref;
use std::pin::Pin;
@@ -22,7 +21,6 @@ use orchid_base::interner::Interner;
use orchid_base::location::Pos;
use orchid_base::name::Sym;
use orchid_base::reqnot::Requester;
use orchid_base::tree::AtomRepr;
use trait_set::trait_set;
use crate::api;
@@ -44,12 +42,8 @@ pub trait Atomic: 'static + Sized {
type Data: Clone + Coding + Sized + 'static;
/// Register handlers for IPC calls. If this atom implements [Supports], you
/// should register your implementations here. If this atom doesn't
/// participate in IPC at all, use the below body.
/// ```
/// MethodSetBuilder::new()
/// ```
// this method isn't default-implemented to prevent bugs from forgetting to register IPC requests.
fn reg_reqs() -> MethodSetBuilder<Self>;
/// participate in IPC at all, the default implementation is fine
fn reg_reqs() -> MethodSetBuilder<Self> { MethodSetBuilder::new() }
}
impl<A: Atomic> AtomCard for A {
type Data = <Self as Atomic>::Data;
@@ -91,57 +85,43 @@ pub fn get_info<A: AtomCard>(
}
#[derive(Clone)]
pub struct ForeignAtom<'a> {
pub(crate) expr: Option<Rc<ExprHandle>>,
pub(crate) _life: PhantomData<&'a ()>,
pub(crate) ctx: SysCtx,
pub struct ForeignAtom {
pub(crate) expr: Rc<ExprHandle>,
pub(crate) atom: api::Atom,
pub(crate) pos: Pos,
}
impl ForeignAtom<'_> {
pub fn ex_opt(self) -> Option<Expr> {
let (handle, pos) = (self.expr.as_ref()?.clone(), self.pos.clone());
let data = ExprData { pos, kind: ExprKind::Atom(ForeignAtom { _life: PhantomData, ..self }) };
Some(Expr::new(handle, data))
}
impl ForeignAtom {
pub fn pos(&self) -> Pos { self.pos.clone() }
pub fn ctx(&self) -> SysCtx { self.ctx.clone() }
}
impl ForeignAtom<'static> {
pub fn ex(self) -> Expr { self.ex_opt().unwrap() }
pub fn ctx(&self) -> SysCtx { self.expr.ctx.clone() }
pub fn ex(self) -> Expr {
let (handle, pos) = (self.expr.clone(), self.pos.clone());
let data = ExprData { pos, kind: ExprKind::Atom(ForeignAtom { ..self }) };
Expr::new(handle, data)
}
pub(crate) fn new(handle: Rc<ExprHandle>, atom: api::Atom, pos: Pos) -> Self {
ForeignAtom { _life: PhantomData, atom, ctx: handle.ctx.clone(), expr: Some(handle), pos }
ForeignAtom { atom, expr: handle, pos }
}
pub async fn request<M: AtomMethod>(&self, m: M) -> Option<M::Response> {
let rep = (self.ctx.reqnot().request(api::Fwd(
let rep = (self.ctx().reqnot().request(api::Fwd(
self.atom.clone(),
Sym::parse(M::NAME, self.ctx.i()).await.unwrap().tok().to_api(),
Sym::parse(M::NAME, self.ctx().i()).await.unwrap().tok().to_api(),
enc_vec(&m).await,
)))
.await?;
Some(M::Response::decode(Pin::new(&mut &rep[..])).await)
}
}
impl fmt::Display for ForeignAtom<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}::{:?}", if self.expr.is_some() { "Clause" } else { "Tok" }, self.atom)
}
impl fmt::Display for ForeignAtom {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Atom::{:?}", self.atom) }
}
impl fmt::Debug for ForeignAtom<'_> {
impl fmt::Debug for ForeignAtom {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ForeignAtom({self})") }
}
impl Format for ForeignAtom<'_> {
impl Format for ForeignAtom {
async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
FmtUnit::from_api(&self.ctx.reqnot().request(api::ExtAtomPrint(self.atom.clone())).await)
FmtUnit::from_api(&self.ctx().reqnot().request(api::ExtAtomPrint(self.atom.clone())).await)
}
}
impl AtomRepr for ForeignAtom<'_> {
type Ctx = SysCtx;
async fn from_api(atom: &api::Atom, pos: Pos, ctx: &mut Self::Ctx) -> Self {
Self { atom: atom.clone(), _life: PhantomData, ctx: ctx.clone(), expr: None, pos }
}
async fn to_api(&self) -> orchid_api::Atom { self.atom.clone() }
}
pub struct NotTypAtom {
pub pos: Pos,
@@ -235,11 +215,11 @@ impl<A: AtomCard> Default for MethodSetBuilder<A> {
}
#[derive(Clone)]
pub struct TypAtom<'a, A: AtomicFeatures> {
pub data: ForeignAtom<'a>,
pub struct TypAtom<A: AtomicFeatures> {
pub data: ForeignAtom,
pub value: A::Data,
}
impl<A: AtomicFeatures> TypAtom<'static, A> {
impl<A: AtomicFeatures> TypAtom<A> {
pub async fn downcast(expr: Rc<ExprHandle>) -> Result<Self, NotTypAtom> {
match Expr::from_handle(expr).atom().await {
Err(expr) => Err(NotTypAtom {
@@ -252,21 +232,19 @@ impl<A: AtomicFeatures> TypAtom<'static, A> {
Ok(tatom) => Ok(tatom),
Err(fa) => Err(NotTypAtom {
pos: fa.pos.clone(),
ctx: fa.ctx.clone(),
ctx: fa.ctx().clone(),
expr: fa.ex(),
typ: Box::new(A::info()),
}),
},
}
}
}
impl<A: AtomicFeatures> TypAtom<'_, A> {
pub async fn request<M: AtomMethod>(&self, req: M) -> M::Response
where A: Supports<M> {
M::Response::decode(Pin::new(
&mut &(self.data.ctx.reqnot().request(api::Fwd(
&mut &(self.data.ctx().reqnot().request(api::Fwd(
self.data.atom.clone(),
Sym::parse(M::NAME, self.data.ctx.i()).await.unwrap().tok().to_api(),
Sym::parse(M::NAME, self.data.ctx().i()).await.unwrap().tok().to_api(),
enc_vec(&req).await,
)))
.await
@@ -275,7 +253,7 @@ impl<A: AtomicFeatures> TypAtom<'_, A> {
.await
}
}
impl<A: AtomicFeatures> Deref for TypAtom<'_, A> {
impl<A: AtomicFeatures> Deref for TypAtom<A> {
type Target = A::Data;
fn deref(&self) -> &Self::Target { &self.value }
}
@@ -289,8 +267,8 @@ pub trait AtomDynfo: 'static {
fn tid(&self) -> TypeId;
fn name(&self) -> &'static str;
fn decode<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, Box<dyn Any>>;
fn call<'a>(&'a self, ctx: AtomCtx<'a>, arg: api::ExprTicket) -> LocalBoxFuture<'a, GExpr>;
fn call_ref<'a>(&'a self, ctx: AtomCtx<'a>, arg: api::ExprTicket) -> LocalBoxFuture<'a, GExpr>;
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>(
&'a self,
@@ -304,12 +282,12 @@ pub trait AtomDynfo: 'static {
&'a self,
ctx: AtomCtx<'a>,
write: Pin<&'b mut dyn Write>,
) -> LocalBoxFuture<'a, Option<Vec<api::ExprTicket>>>;
) -> LocalBoxFuture<'a, Option<Vec<Expr>>>;
fn deserialize<'a>(
&'a self,
ctx: SysCtx,
data: &'a [u8],
refs: &'a [api::ExprTicket],
refs: &'a [Expr],
) -> LocalBoxFuture<'a, api::Atom>;
fn drop<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, ()>;
}
@@ -319,9 +297,7 @@ trait_set! {
}
pub struct AtomFactory(Box<dyn AtomFactoryFn>);
impl AtomFactory {
pub fn new(
f: impl AsyncFnOnce(SysCtx) -> api::Atom + Clone + 'static,
) -> Self {
pub fn new(f: impl AsyncFnOnce(SysCtx) -> api::Atom + Clone + 'static) -> Self {
Self(Box::new(|ctx| f(ctx).boxed_local()))
}
pub async fn build(self, ctx: SysCtx) -> api::Atom { (self.0)(ctx).await }

View File

@@ -4,7 +4,6 @@ use std::future::Future;
use std::num::NonZero;
use std::ops::Deref;
use std::pin::Pin;
use std::rc::Rc;
use std::sync::atomic::AtomicU64;
use async_once_cell::OnceCell;
@@ -18,7 +17,7 @@ use never::Never;
use orchid_api::AtomId;
use orchid_api_traits::{Decode, Encode, enc_vec};
use orchid_base::error::OrcRes;
use orchid_base::format::FmtUnit;
use orchid_base::format::{FmtCtx, FmtCtxImpl, FmtUnit};
use orchid_base::name::Sym;
use crate::api;
@@ -26,7 +25,7 @@ use crate::atom::{
AtomCard, AtomCtx, AtomDynfo, AtomFactory, Atomic, AtomicFeaturesImpl, AtomicVariant, MethodSet,
MethodSetBuilder, err_not_callable, err_not_command, get_info,
};
use crate::expr::{Expr, ExprHandle};
use crate::expr::Expr;
use crate::gen_expr::{GExpr, bot};
use crate::system::{SysCtx, SysCtxEntry};
use crate::system_ctor::CtedObj;
@@ -86,17 +85,15 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
Box::new(<T as AtomCard>::Data::decode(Pin::new(&mut &data[..])).await) as Box<dyn Any>
})
}
fn call(&self, AtomCtx(_, id, ctx): AtomCtx, arg: api::ExprTicket) -> LocalBoxFuture<'_, GExpr> {
Box::pin(async move { take_atom(id.unwrap(), &ctx).await.dyn_call(ctx.clone(), arg).await })
fn call(&self, AtomCtx(_, id, ctx): AtomCtx, arg: Expr) -> LocalBoxFuture<'_, GExpr> {
Box::pin(async move { take_atom(id.unwrap(), &ctx).await.dyn_call(arg).await })
}
fn call_ref<'a>(
&'a self,
AtomCtx(_, id, ctx): AtomCtx<'a>,
arg: api::ExprTicket,
arg: Expr,
) -> LocalBoxFuture<'a, GExpr> {
Box::pin(async move {
AtomReadGuard::new(id.unwrap(), &ctx).await.dyn_call_ref(ctx.clone(), arg).await
})
Box::pin(async move { AtomReadGuard::new(id.unwrap(), &ctx).await.dyn_call_ref(arg).await })
}
fn print(&self, AtomCtx(_, id, ctx): AtomCtx<'_>) -> LocalBoxFuture<'_, FmtUnit> {
Box::pin(
@@ -129,24 +126,22 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
&'a self,
AtomCtx(_, id, ctx): AtomCtx<'a>,
mut write: Pin<&'b mut dyn Write>,
) -> LocalBoxFuture<'a, Option<Vec<api::ExprTicket>>> {
) -> LocalBoxFuture<'a, Option<Vec<Expr>>> {
Box::pin(async move {
let id = id.unwrap();
id.encode(write.as_mut()).await;
let refs = AtomReadGuard::new(id, &ctx).await.dyn_serialize(ctx.clone(), write).await;
refs.map(|v| v.into_iter().map(|t| t.handle().tk).collect_vec())
AtomReadGuard::new(id, &ctx).await.dyn_serialize(ctx.clone(), write).await
})
}
fn deserialize<'a>(
&'a self,
ctx: SysCtx,
data: &'a [u8],
refs: &'a [api::ExprTicket],
refs: &'a [Expr],
) -> LocalBoxFuture<'a, api::Atom> {
Box::pin(async move {
let refs =
refs.iter().map(|tk| Expr::from_handle(Rc::new(ExprHandle::from_args(ctx.clone(), *tk))));
let obj = T::deserialize(DeserCtxImpl(data, &ctx), T::Refs::from_iter(refs)).await;
let refs = T::Refs::from_iter(refs.iter().cloned());
let obj = T::deserialize(DeserCtxImpl(data, &ctx), refs).await;
obj._factory().build(ctx).await
})
}
@@ -220,12 +215,12 @@ pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Any + Clone + 'static {
type Refs: RefSet;
fn val(&self) -> impl Future<Output = Cow<'_, Self::Data>>;
#[allow(unused_variables)]
fn call_ref(&self, arg: ExprHandle) -> impl Future<Output = GExpr> {
async move { bot([err_not_callable(arg.ctx.i()).await]) }
fn call_ref(&self, arg: Expr) -> impl Future<Output = GExpr> {
async move { bot([err_not_callable(arg.ctx().i()).await]) }
}
fn call(self, arg: ExprHandle) -> impl Future<Output = GExpr> {
fn call(self, arg: Expr) -> impl Future<Output = GExpr> {
async {
let ctx = arg.get_ctx();
let ctx = arg.ctx();
let gcl = self.call_ref(arg).await;
self.free(ctx).await;
gcl
@@ -238,7 +233,7 @@ pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Any + Clone + 'static {
#[allow(unused_variables)]
fn free(self, ctx: SysCtx) -> impl Future<Output = ()> { async {} }
#[allow(unused_variables)]
fn print(&self, ctx: SysCtx) -> impl Future<Output = FmtUnit> {
fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> impl Future<Output = FmtUnit> {
async { format!("OwnedAtom({})", type_name::<Self>()).into() }
}
#[allow(unused_variables)]
@@ -268,9 +263,8 @@ pub trait DynOwnedAtom: 'static {
fn atom_tid(&self) -> TypeId;
fn as_any_ref(&self) -> &dyn Any;
fn encode<'a>(&'a self, buffer: Pin<&'a mut dyn Write>) -> LocalBoxFuture<'a, ()>;
fn dyn_call_ref(&self, ctx: SysCtx, arg: api::ExprTicket) -> LocalBoxFuture<'_, GExpr>;
fn dyn_call(self: Box<Self>, ctx: SysCtx, arg: api::ExprTicket)
-> LocalBoxFuture<'static, GExpr>;
fn dyn_call_ref(&self, arg: Expr) -> LocalBoxFuture<'_, GExpr>;
fn dyn_call(self: Box<Self>, arg: Expr) -> LocalBoxFuture<'static, GExpr>;
fn dyn_command(self: Box<Self>, ctx: SysCtx) -> LocalBoxFuture<'static, OrcRes<Option<GExpr>>>;
fn dyn_free(self: Box<Self>, ctx: SysCtx) -> LocalBoxFuture<'static, ()>;
fn dyn_print(&self, ctx: SysCtx) -> LocalBoxFuture<'_, FmtUnit>;
@@ -286,15 +280,11 @@ impl<T: OwnedAtom> DynOwnedAtom for T {
fn encode<'a>(&'a self, buffer: Pin<&'a mut dyn Write>) -> LocalBoxFuture<'a, ()> {
async { self.val().await.as_ref().encode(buffer).await }.boxed_local()
}
fn dyn_call_ref(&self, ctx: SysCtx, arg: api::ExprTicket) -> LocalBoxFuture<'_, GExpr> {
self.call_ref(ExprHandle::from_args(ctx, arg)).boxed_local()
fn dyn_call_ref(&self, arg: Expr) -> LocalBoxFuture<'_, GExpr> {
self.call_ref(arg).boxed_local()
}
fn dyn_call(
self: Box<Self>,
ctx: SysCtx,
arg: api::ExprTicket,
) -> LocalBoxFuture<'static, GExpr> {
self.call(ExprHandle::from_args(ctx, arg)).boxed_local()
fn dyn_call(self: Box<Self>, arg: Expr) -> LocalBoxFuture<'static, GExpr> {
self.call(arg).boxed_local()
}
fn dyn_command(self: Box<Self>, ctx: SysCtx) -> LocalBoxFuture<'static, OrcRes<Option<GExpr>>> {
self.command(ctx).boxed_local()
@@ -302,7 +292,9 @@ impl<T: OwnedAtom> DynOwnedAtom for T {
fn dyn_free(self: Box<Self>, ctx: SysCtx) -> LocalBoxFuture<'static, ()> {
self.free(ctx).boxed_local()
}
fn dyn_print(&self, ctx: SysCtx) -> LocalBoxFuture<'_, FmtUnit> { self.print(ctx).boxed_local() }
fn dyn_print(&self, ctx: SysCtx) -> LocalBoxFuture<'_, FmtUnit> {
async move { self.print(&FmtCtxImpl { i: ctx.i() }).await }.boxed_local()
}
fn dyn_serialize<'a>(
&'a self,
ctx: SysCtx,

View File

@@ -16,7 +16,7 @@ use crate::atom::{
AtomCard, AtomCtx, AtomDynfo, AtomFactory, Atomic, AtomicFeaturesImpl, AtomicVariant, MethodSet,
MethodSetBuilder, err_not_callable, err_not_command, get_info,
};
use crate::expr::ExprHandle;
use crate::expr::Expr;
use crate::gen_expr::{GExpr, bot};
use crate::system::SysCtx;
use crate::system_ctor::CtedObj;
@@ -49,23 +49,11 @@ impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
fn decode<'a>(&'a self, AtomCtx(buf, ..): AtomCtx<'a>) -> LocalBoxFuture<'a, Box<dyn Any>> {
Box::pin(async { Box::new(T::decode(Pin::new(&mut &buf[..])).await) as Box<dyn Any> })
}
fn call<'a>(
&'a self,
AtomCtx(buf, _, ctx): AtomCtx<'a>,
arg: api::ExprTicket,
) -> LocalBoxFuture<'a, GExpr> {
Box::pin(async move {
T::decode(Pin::new(&mut &buf[..])).await.call(ExprHandle::from_args(ctx, arg)).await
})
fn call<'a>(&'a self, AtomCtx(buf, ..): AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr> {
Box::pin(async move { T::decode(Pin::new(&mut &buf[..])).await.call(arg).await })
}
fn call_ref<'a>(
&'a self,
AtomCtx(buf, _, ctx): AtomCtx<'a>,
arg: api::ExprTicket,
) -> LocalBoxFuture<'a, GExpr> {
Box::pin(async move {
T::decode(Pin::new(&mut &buf[..])).await.call(ExprHandle::from_args(ctx, arg)).await
})
fn call_ref<'a>(&'a self, AtomCtx(buf, ..): AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr> {
Box::pin(async move { T::decode(Pin::new(&mut &buf[..])).await.call(arg).await })
}
fn handle_req<'a, 'm1: 'a, 'm2: 'a>(
&'a self,
@@ -89,7 +77,7 @@ impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
&'a self,
ctx: AtomCtx<'a>,
write: Pin<&'b mut dyn Write>,
) -> LocalBoxFuture<'a, Option<Vec<api::ExprTicket>>> {
) -> LocalBoxFuture<'a, Option<Vec<Expr>>> {
Box::pin(async {
T::decode(Pin::new(&mut &ctx.0[..])).await.encode(write).await;
Some(Vec::new())
@@ -99,7 +87,7 @@ impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
&'a self,
ctx: SysCtx,
data: &'a [u8],
refs: &'a [api::ExprTicket],
refs: &'a [Expr],
) -> LocalBoxFuture<'a, api::Atom> {
assert!(refs.is_empty(), "Refs found when deserializing thin atom");
Box::pin(async { T::decode(Pin::new(&mut &data[..])).await._factory().build(ctx).await })
@@ -116,8 +104,8 @@ pub trait ThinAtom:
AtomCard<Data = Self> + Atomic<Variant = ThinVariant> + Coding + Send + Sync + 'static
{
#[allow(unused_variables)]
fn call(&self, arg: ExprHandle) -> impl Future<Output = GExpr> {
async move { bot([err_not_callable(arg.ctx.i()).await]) }
fn call(&self, arg: Expr) -> impl Future<Output = GExpr> {
async move { bot([err_not_callable(arg.ctx().i()).await]) }
}
#[allow(unused_variables)]
fn command(&self, ctx: SysCtx) -> impl Future<Output = OrcRes<Option<GExpr>>> {

View File

@@ -31,7 +31,7 @@ async fn err_type(pos: Pos, i: &Interner) -> OrcErr {
mk_err(i.i("Type error").await, "The atom is a different type than expected", [pos.into()])
}
impl<A: AtomicFeatures> TryFromExpr for TypAtom<'_, A> {
impl<A: AtomicFeatures> TryFromExpr for TypAtom<A> {
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
match expr.atom().await {
Err(ex) => Err(err_not_atom(ex.data().await.pos.clone(), ex.ctx().i()).await.into()),
@@ -51,7 +51,7 @@ impl ToExpr for GExpr {
fn to_expr(self) -> GExpr { self }
}
impl ToExpr for Expr {
fn to_expr(self) -> GExpr { self.gen() }
fn to_expr(self) -> GExpr { self.slot() }
}
impl<T: ToExpr> ToExpr for OrcRes<T> {

View File

@@ -12,30 +12,29 @@ use futures::future::{LocalBoxFuture, join_all};
use futures::{FutureExt, StreamExt, stream_select};
use hashbrown::HashMap;
use itertools::Itertools;
use orchid_api::{ApplyMacro, ExtMsgSet};
use orchid_api_traits::{Decode, enc_vec};
use orchid_api::{ExtMsgSet, IntReq};
use orchid_api_traits::{Decode, UnderRoot, enc_vec};
use orchid_base::builtin::{ExtInit, ExtPort, Spawner};
use orchid_base::char_filter::{char_filter_match, char_filter_union, mk_char_filter};
use orchid_base::clone;
use orchid_base::interner::{Interner, Tok};
use orchid_base::logging::Logger;
use orchid_base::macros::{mtreev_from_api, mtreev_to_api};
use orchid_base::name::Sym;
use orchid_base::parse::{Comment, Snippet};
use orchid_base::reqnot::{ReqNot, RequestHandle, Requester};
use orchid_base::tree::{ttv_from_api, ttv_to_api};
use orchid_base::tree::{TokenVariant, ttv_from_api, ttv_into_api};
use substack::Substack;
use trait_set::trait_set;
use crate::api;
use crate::atom::{AtomCtx, AtomDynfo, AtomTypeId};
use crate::atom_owned::take_atom;
use crate::expr::{Expr, ExprHandle};
use crate::fs::VirtFS;
use crate::lexer::{LexContext, err_cascade, err_not_applicable};
use crate::macros::{Rule, RuleCtx};
use crate::system::{SysCtx, atom_by_idx};
use crate::system_ctor::{CtedObj, DynSystemCtor};
use crate::tree::{GenTok, GenTokTree, LazyMemberFactory, TreeIntoApiCtxImpl, do_extra};
use crate::tree::{GenItemKind, GenTok, GenTokTree, LazyMemberFactory, TreeIntoApiCtxImpl};
pub type ExtReq<'a> = RequestHandle<'a, api::ExtMsgSet>;
pub type ExtReqNot = ReqNot<api::ExtMsgSet>;
@@ -60,7 +59,6 @@ pub struct SystemRecord {
vfses: HashMap<api::VfsId, &'static dyn VirtFS>,
declfs: api::EagerVfs,
lazy_members: HashMap<api::TreeId, MemberRecord>,
rules: HashMap<api::MacroId, Rc<Rule>>,
ctx: SysCtx,
}
@@ -197,16 +195,17 @@ pub fn extension_init(
char_filter_union(&cf, &mk_char_filter(lx.char_filter().iter().cloned()))
});
let lazy_mems = Mutex::new(HashMap::new());
let rules = Mutex::new(HashMap::new());
let ctx = init_ctx(new_sys.id, cted.clone(), hand.reqnot()).await;
let const_root = stream::from_iter(cted.inst().dyn_env())
.then(|(k, v)| {
let (req, lazy_mems, rules) = (&hand, &lazy_mems, &rules);
.filter_map(
async |i| if let GenItemKind::Member(m) = i.kind { Some(m) } else { None },
)
.then(|mem| {
let (req, lazy_mems) = (&hand, &lazy_mems);
clone!(i, ctx; async move {
let name = i.i(&k).await.to_api();
let value = v.into_api(&mut TreeIntoApiCtxImpl {
let name = i.i(&mem.name).await.to_api();
let value = mem.kind.into_api(&mut TreeIntoApiCtxImpl {
lazy_members: &mut *lazy_mems.lock().await,
rules: &mut *rules.lock().await,
sys: ctx,
basepath: &[],
path: Substack::Bottom,
@@ -219,24 +218,23 @@ pub fn extension_init(
.collect()
.await;
let declfs = cted.inst().dyn_vfs().to_api_rec(&mut vfses, &i).await;
let record = SystemRecord {
declfs,
vfses,
ctx,
lazy_members: lazy_mems.into_inner(),
rules: rules.into_inner(),
};
let record =
SystemRecord { declfs, vfses, ctx, lazy_members: lazy_mems.into_inner() };
let systems = systems_weak.upgrade().expect("System constructed during shutdown");
systems.lock().await.insert(new_sys.id, record);
hand
.handle(&new_sys, &api::SystemInst { lex_filter, const_root, line_types: vec![] })
.handle(&new_sys, &api::NewSystemResponse {
lex_filter,
const_root,
line_types: vec![],
})
.await
},
api::HostExtReq::GetMember(get_tree @ api::GetMember(sys_id, tree_id)) => {
let sys_ctx = get_ctx(sys_id).await;
let systems = systems_weak.upgrade().expect("Member queried during shutdown");
let mut systems_g = systems.lock().await;
let SystemRecord { lazy_members, rules, .. } =
let SystemRecord { lazy_members, .. } =
systems_g.get_mut(&sys_id).expect("System not found");
let (path, cb) = match lazy_members.insert(tree_id, MemberRecord::Res) {
None => panic!("Tree for ID not found"),
@@ -249,7 +247,6 @@ pub fn extension_init(
path: Substack::Bottom,
basepath: &path,
lazy_members,
rules,
req: &hand,
};
hand.handle(&get_tree, &tree.into_api(&mut tia_ctx).await).await
@@ -277,7 +274,7 @@ pub fn extension_init(
api::HostExtReq::LexExpr(lex @ api::LexExpr { sys, text, pos, id }) => {
let sys_ctx = get_ctx(sys).await;
let text = Tok::from_api(text, &i).await;
let ctx = LexContext { sys, id, pos, reqnot: hand.reqnot(), text: &text, i: &i };
let ctx = LexContext { id, pos, text: &text, ctx: sys_ctx.clone() };
let trigger_char = text.chars().nth(pos as usize).unwrap();
let err_na = err_not_applicable(&i).await;
let err_cascade = err_cascade(&i).await;
@@ -290,8 +287,7 @@ pub fn extension_init(
return hand.handle(&lex, &eopt).await;
},
Ok((s, expr)) => {
let expr =
expr.to_api(&mut async |f, r| do_extra(f, r, sys_ctx.clone()).await).await;
let expr = expr.into_api(&mut (), &mut (sys_ctx, &hand)).await;
let pos = (text.len() - s.len()) as u32;
return hand.handle(&lex, &Some(Ok(api::LexedExpr { pos, expr }))).await;
},
@@ -301,25 +297,21 @@ pub fn extension_init(
hand.handle(&lex, &None).await
},
api::HostExtReq::ParseLine(pline) => {
let api::ParseLine { exported, comments, sys, line } = &pline;
let api::ParseLine { module, exported, comments, sys, line } = &pline;
let mut ctx = get_ctx(*sys).await;
let parsers = ctx.cted().inst().dyn_parsers();
let comments = join_all(comments.iter().map(|c| Comment::from_api(c, &i))).await;
let line: Vec<GenTokTree> = ttv_from_api(line, &mut ctx, &i).await;
let snip = Snippet::new(line.first().expect("Empty line"), &line, &i);
let line: Vec<GenTokTree> = ttv_from_api(line, &mut ctx, &mut (), &i).await;
let snip = Snippet::new(line.first().expect("Empty line"), &line);
let (head, tail) = snip.pop_front().unwrap();
let name = if let GenTok::Name(n) = &head.tok { n } else { panic!("No line head") };
let parser =
parsers.iter().find(|p| p.line_head() == **name).expect("No parser candidate");
let o_line = match parser.parse(*exported, comments, tail) {
let module = Sym::from_api(*module, ctx.i()).await;
let o_line = match parser.parse(ctx.clone(), module, *exported, comments, tail).await
{
Err(e) => Err(e.to_api()),
Ok(t) => Ok(
ttv_to_api(t, &mut async move |f, range| api::TokenTree {
range,
token: api::Token::Atom(f.clone().build(ctx.clone()).await),
})
.await,
),
Ok(t) => Ok(ttv_into_api(t, &mut (), &mut (ctx.clone(), &hand)).await),
};
hand.handle(&pline, &o_line).await
},
@@ -331,8 +323,15 @@ pub fn extension_init(
match &atom_req {
api::AtomReq::SerializeAtom(ser) => {
let mut buf = enc_vec(&id).await;
let refs_opt = nfo.serialize(actx, Pin::<&mut Vec<_>>::new(&mut buf)).await;
hand.handle(ser, &refs_opt.map(|refs| (buf, refs))).await
match nfo.serialize(actx, Pin::<&mut Vec<_>>::new(&mut buf)).await {
None => hand.handle(ser, &None).await,
Some(refs) => {
let refs =
join_all(refs.into_iter().map(|ex| async { ex.into_api(&mut ()).await }))
.await;
hand.handle(ser, &Some((buf, refs))).await
},
}
},
api::AtomReq::AtomPrint(print @ api::AtomPrint(_)) =>
hand.handle(print, &nfo.print(actx).await.to_api()).await,
@@ -351,11 +350,15 @@ pub fn extension_init(
hand.handle(fwded, &some.then_some(reply)).await
},
api::AtomReq::CallRef(call @ api::CallRef(_, arg)) => {
let ret = nfo.call_ref(actx, *arg).await;
// SAFETY: function calls own their argument implicitly
let expr_handle = unsafe { ExprHandle::from_args(ctx.clone(), *arg) };
let ret = nfo.call_ref(actx, Expr::from_handle(Rc::new(expr_handle))).await;
hand.handle(call, &ret.api_return(ctx.clone(), &hand).await).await
},
api::AtomReq::FinalCall(call @ api::FinalCall(_, arg)) => {
let ret = nfo.call(actx, *arg).await;
// SAFETY: function calls own their argument implicitly
let expr_handle = unsafe { ExprHandle::from_args(ctx.clone(), *arg) };
let ret = nfo.call(actx, Expr::from_handle(Rc::new(expr_handle))).await;
hand.handle(call, &ret.api_return(ctx.clone(), &hand).await).await
},
api::AtomReq::Command(cmd @ api::Command(_)) => match nfo.command(actx).await {
@@ -376,41 +379,15 @@ pub fn extension_init(
let api::DeserAtom(sys, buf, refs) = &deser;
let mut read = &mut &buf[..];
let ctx = get_ctx(*sys).await;
// SAFETY: deserialization implicitly grants ownership to previously owned exprs
let refs = (refs.iter())
.map(|tk| unsafe { ExprHandle::from_args(ctx.clone(), *tk) })
.map(|handle| Expr::from_handle(Rc::new(handle)))
.collect_vec();
let id = AtomTypeId::decode(Pin::new(&mut read)).await;
let inst = ctx.cted().inst();
let nfo = atom_by_idx(inst.card(), id).expect("Deserializing atom with invalid ID");
hand.handle(&deser, &nfo.deserialize(ctx.clone(), read, refs).await).await
},
orchid_api::HostExtReq::ApplyMacro(am) => {
let tok = hand.will_handle_as(&am);
let ApplyMacro { id, params, run_id, sys } = am;
let sys_ctx = get_ctx(sys).await;
let mut ctx =
RuleCtx { args: ahash::HashMap::default(), run_id, sys: sys_ctx.clone() };
for (k, v) in params {
ctx.args.insert(
Tok::from_api(k, &i).await,
mtreev_from_api(&v, &i, &mut async |_| panic!("No atom in macro prompt!")).await,
);
}
let err_cascade = err_cascade(&i).await;
let systems = systems_weak.upgrade().expect("macro call during shutdown");
let systems_g = systems.lock().await;
let rule = &systems_g[&sys].rules[&id];
match (rule.apply)(ctx).await {
Err(e) => {
let new_errors = e.keep_only(|e| *e != err_cascade);
hand.handle_as(tok, &new_errors.map(|e| Err(e.to_api()))).await
},
Ok(t) => {
clone!(sys_ctx);
let result = mtreev_to_api(&t, &mut async |a| {
api::MacroToken::Atom(a.clone().build(sys_ctx.clone()).await)
})
.await;
hand.handle_as(tok, &Some(Ok(result))).await
},
}
hand.handle(&deser, &nfo.deserialize(ctx.clone(), read, &refs).await).await
},
}
}
@@ -418,7 +395,8 @@ pub fn extension_init(
}
},
);
*interner_cell.borrow_mut() = Some(Interner::new_replica(rn.clone().map()));
*interner_cell.borrow_mut() =
Some(Interner::new_replica(rn.clone().map(|ir: IntReq| ir.into_root())));
spawner(Box::pin(clone!(spawner; async move {
let mut streams = stream_select! { in_recv.map(Some), exit_recv.map(|_| None) };
while let Some(item) = streams.next().await {

View File

@@ -20,12 +20,20 @@ pub struct ExprHandle {
pub ctx: SysCtx,
}
impl ExprHandle {
pub(crate) fn from_args(ctx: SysCtx, tk: api::ExprTicket) -> Self { Self { ctx, tk } }
/// # Safety
///
/// This function does not signal to take ownership of the expr. It must only
/// be called on tickets that are already implicitly owned.
pub unsafe fn from_args(ctx: SysCtx, tk: api::ExprTicket) -> Self { Self { ctx, tk } }
pub fn get_ctx(&self) -> SysCtx { self.ctx.clone() }
pub async fn clone(&self) -> Self {
self.ctx.reqnot().notify(api::Acquire(self.ctx.sys_id(), self.tk)).await;
Self { ctx: self.ctx.clone(), tk: self.tk }
}
/// Drop the handle and get the ticket without a release notification.
/// Use this with messages that imply ownership transfer. This function is
/// safe because abusing it is a memory leak.
pub fn into_tk(self) -> api::ExprTicket { self.destructure().0 }
}
impl fmt::Debug for ExprHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -66,7 +74,7 @@ impl Expr {
}))
.await
}
pub async fn atom(self) -> Result<ForeignAtom<'static>, Self> {
pub async fn atom(self) -> Result<ForeignAtom, Self> {
match self.data().await {
ExprData { kind: ExprKind::Atom(atom), .. } => Ok(atom.clone()),
_ => Err(self),
@@ -75,7 +83,9 @@ impl Expr {
pub fn handle(&self) -> Rc<ExprHandle> { self.handle.clone() }
pub fn ctx(&self) -> SysCtx { self.handle.ctx.clone() }
pub fn gen(&self) -> GExpr { GExpr { pos: Pos::SlotTarget, kind: GExprKind::Slot(self.clone()) } }
pub fn slot(&self) -> GExpr {
GExpr { pos: Pos::SlotTarget, kind: GExprKind::Slot(self.clone()) }
}
}
impl Format for Expr {
async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
@@ -96,7 +106,7 @@ pub struct ExprData {
#[derive(Clone, Debug)]
pub enum ExprKind {
Atom(ForeignAtom<'static>),
Atom(ForeignAtom),
Bottom(OrcErrv),
Opaque,
}

View File

@@ -13,13 +13,14 @@ use never::Never;
use orchid_api_traits::Encode;
use orchid_base::clone;
use orchid_base::error::OrcRes;
use orchid_base::format::{FmtCtx, FmtUnit};
use orchid_base::name::Sym;
use trait_set::trait_set;
use crate::atom::{Atomic, MethodSetBuilder};
use crate::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
use crate::conv::ToExpr;
use crate::expr::{Expr, ExprHandle};
use crate::expr::Expr;
use crate::gen_expr::GExpr;
use crate::system::{SysCtx, SysCtxEntry};
@@ -66,14 +67,13 @@ impl Fun {
impl Atomic for Fun {
type Data = ();
type Variant = OwnedVariant;
fn reg_reqs() -> MethodSetBuilder<Self> { MethodSetBuilder::new() }
}
impl OwnedAtom for Fun {
type Refs = Vec<Expr>;
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
async fn call_ref(&self, arg: ExprHandle) -> GExpr {
async fn call_ref(&self, arg: Expr) -> GExpr {
std::io::Write::flush(&mut std::io::stderr()).unwrap();
let new_args = self.args.iter().cloned().chain([Expr::from_handle(Rc::new(arg))]).collect_vec();
let new_args = self.args.iter().cloned().chain([arg]).collect_vec();
if new_args.len() == self.arity.into() {
(self.fun)(new_args).await.to_expr()
} else {
@@ -81,7 +81,7 @@ impl OwnedAtom for Fun {
.to_expr()
}
}
async fn call(self, arg: ExprHandle) -> GExpr { self.call_ref(arg).await }
async fn call(self, arg: Expr) -> GExpr { self.call_ref(arg).await }
async fn serialize(&self, _: SysCtx, write: Pin<&mut (impl Write + ?Sized)>) -> Self::Refs {
self.path.to_api().encode(write).await;
self.args.clone()
@@ -92,7 +92,7 @@ impl OwnedAtom for Fun {
let (arity, fun) = sys.get_or_default::<FunsCtx>().0.lock().await.get(&path).unwrap().clone();
Self { args, arity, path, fun }
}
async fn print(&self, _: SysCtx) -> orchid_base::format::FmtUnit {
async fn print<'a>(&'a self, _: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
format!("{}:{}/{}", self.path, self.args.len(), self.arity).into()
}
}
@@ -116,20 +116,19 @@ impl Lambda {
impl Atomic for Lambda {
type Data = ();
type Variant = OwnedVariant;
fn reg_reqs() -> MethodSetBuilder<Self> { MethodSetBuilder::new() }
}
impl OwnedAtom for Lambda {
type Refs = Never;
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
async fn call_ref(&self, arg: ExprHandle) -> GExpr {
let new_args = self.args.iter().cloned().chain([Expr::from_handle(Rc::new(arg))]).collect_vec();
async fn call_ref(&self, arg: Expr) -> GExpr {
let new_args = self.args.iter().cloned().chain([arg]).collect_vec();
if new_args.len() == self.arity.into() {
(self.fun)(new_args).await.to_expr()
} else {
Self { args: new_args, arity: self.arity, fun: self.fun.clone() }.to_expr()
}
}
async fn call(self, arg: ExprHandle) -> GExpr { self.call_ref(arg).await }
async fn call(self, arg: Expr) -> GExpr { self.call_ref(arg).await }
}
mod expr_func_derives {

View File

@@ -1,11 +1,12 @@
use std::future::Future;
use std::rc::Rc;
use futures::FutureExt;
use orchid_base::error::{OrcErr, OrcErrv};
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
use orchid_base::location::Pos;
use orchid_base::match_mapping;
use orchid_base::name::Sym;
use orchid_base::reqnot::ReqHandlish;
use orchid_base::{match_mapping, tl_cache};
use crate::api;
use crate::atom::{AtomFactory, ToAtom};
@@ -14,6 +15,7 @@ use crate::expr::Expr;
use crate::func_atom::Lambda;
use crate::system::SysCtx;
#[derive(Clone, Debug)]
pub struct GExpr {
pub kind: GExprKind,
pub pos: Pos,
@@ -34,7 +36,13 @@ impl GExpr {
}
}
}
impl Format for GExpr {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
self.kind.print(c).await
}
}
#[derive(Clone, Debug)]
pub enum GExprKind {
Call(Box<GExpr>, Box<GExpr>),
Lambda(u64, Box<GExpr>),
@@ -67,6 +75,28 @@ impl GExprKind {
})
}
}
impl Format for GExprKind {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
match self {
GExprKind::Call(f, x) =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{0} ({1})")))
.units([f.print(c).await, x.print(c).await]),
GExprKind::Lambda(arg, body) =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("\\{0}.{1}")))
.units([arg.to_string().into(), body.print(c).await]),
GExprKind::Arg(arg) => arg.to_string().into(),
GExprKind::Seq(a, b) =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("[{0}] {1}")))
.units([a.print(c).await, b.print(c).await]),
GExprKind::Const(sym) => sym.to_string().into(),
GExprKind::NewAtom(atom_factory) => atom_factory.to_string().into(),
GExprKind::Slot(expr) =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{{{0}}}")))
.units([expr.print(c).await]),
GExprKind::Bottom(orc_errv) => orc_errv.to_string().into(),
}
}
}
fn inherit(kind: GExprKind) -> GExpr { GExpr { pos: Pos::Inherit, kind } }

View File

@@ -6,11 +6,11 @@ use futures::future::LocalBoxFuture;
use orchid_base::error::{OrcErr, OrcRes, mk_err};
use orchid_base::interner::{Interner, Tok};
use orchid_base::location::Pos;
use orchid_base::reqnot::{ReqNot, Requester};
use orchid_base::tree::TokHandle;
use orchid_base::reqnot::Requester;
use crate::api;
use crate::tree::{GenTok, GenTokTree};
use crate::system::SysCtx;
use crate::tree::GenTokTree;
pub async fn err_cascade(i: &Interner) -> OrcErr {
mk_err(
@@ -30,20 +30,19 @@ pub async fn err_not_applicable(i: &Interner) -> OrcErr {
}
pub struct LexContext<'a> {
pub ctx: SysCtx,
pub text: &'a Tok<String>,
pub sys: api::SysId,
pub id: api::ParsId,
pub pos: u32,
pub reqnot: ReqNot<api::ExtMsgSet>,
pub i: &'a Interner,
}
impl<'a> LexContext<'a> {
pub async fn recurse(&self, tail: &'a str) -> OrcRes<(&'a str, GenTokTree<'a>)> {
pub async fn recurse(&self, tail: &'a str) -> OrcRes<(&'a str, GenTokTree)> {
let start = self.pos(tail);
let Some(lx) = self.reqnot.request(api::SubLex { pos: start, id: self.id }).await else {
return Err(err_cascade(self.i).await.into());
let Some(lx) = self.ctx.reqnot().request(api::SubLex { pos: start, id: self.id }).await else {
return Err(err_cascade(self.ctx.i()).await.into());
};
Ok((&self.text[lx.pos as usize..], GenTok::Slot(TokHandle::new(lx.ticket)).at(start..lx.pos)))
let tree = GenTokTree::from_api(&lx.tree, &mut self.ctx.clone(), &mut (), self.ctx.i()).await;
Ok((&self.text[lx.pos as usize..], tree))
}
pub fn pos(&self, tail: &'a str) -> u32 { (self.text.len() - tail.len()) as u32 }
@@ -58,7 +57,7 @@ pub trait Lexer: Send + Sync + Sized + Default + 'static {
fn lex<'a>(
tail: &'a str,
ctx: &'a LexContext<'a>,
) -> impl Future<Output = OrcRes<(&'a str, GenTokTree<'a>)>>;
) -> impl Future<Output = OrcRes<(&'a str, GenTokTree)>>;
}
pub trait DynLexer: Send + Sync + 'static {
@@ -67,7 +66,7 @@ pub trait DynLexer: Send + Sync + 'static {
&self,
tail: &'a str,
ctx: &'a LexContext<'a>,
) -> LocalBoxFuture<'a, OrcRes<(&'a str, GenTokTree<'a>)>>;
) -> LocalBoxFuture<'a, OrcRes<(&'a str, GenTokTree)>>;
}
impl<T: Lexer> DynLexer for T {
@@ -76,7 +75,7 @@ impl<T: Lexer> DynLexer for T {
&self,
tail: &'a str,
ctx: &'a LexContext<'a>,
) -> LocalBoxFuture<'a, OrcRes<(&'a str, GenTokTree<'a>)>> {
) -> LocalBoxFuture<'a, OrcRes<(&'a str, GenTokTree)>> {
T::lex(tail, ctx).boxed_local()
}
}

View File

@@ -10,7 +10,6 @@ pub mod fs;
pub mod func_atom;
pub mod gen_expr;
pub mod lexer;
pub mod macros;
pub mod msg;
pub mod other_system;
pub mod parser;

View File

@@ -1,103 +0,0 @@
use std::rc::Rc;
use ahash::HashMap;
use futures::future::{LocalBoxFuture, join_all};
use itertools::Itertools;
use never::Never;
use orchid_api::ExtMsgSet;
use orchid_base::error::OrcRes;
use orchid_base::interner::Tok;
use orchid_base::macros::{MTree, mtreev_from_api, mtreev_to_api};
use orchid_base::reqnot::{ReqNot, Requester};
use trait_set::trait_set;
use crate::api;
use crate::atom::AtomFactory;
use crate::lexer::err_cascade;
use crate::system::SysCtx;
use crate::tree::TreeIntoApiCtx;
pub trait Macro {
fn pattern() -> MTree<'static, Never>;
fn apply(binds: HashMap<Tok<String>, MTree<'_, Never>>) -> MTree<'_, AtomFactory>;
}
pub trait DynMacro {
fn pattern(&self) -> MTree<'static, Never>;
fn apply<'a>(&self, binds: HashMap<Tok<String>, MTree<'a, Never>>) -> MTree<'a, AtomFactory>;
}
impl<T: Macro> DynMacro for T {
fn pattern(&self) -> MTree<'static, Never> { Self::pattern() }
fn apply<'a>(&self, binds: HashMap<Tok<String>, MTree<'a, Never>>) -> MTree<'a, AtomFactory> {
Self::apply(binds)
}
}
pub struct RuleCtx<'a> {
pub(crate) args: HashMap<Tok<String>, Vec<MTree<'a, Never>>>,
pub(crate) run_id: api::ParsId,
pub(crate) sys: SysCtx,
}
impl<'a> RuleCtx<'a> {
pub async fn recurse(&mut self, tree: &[MTree<'a, Never>]) -> OrcRes<Vec<MTree<'a, Never>>> {
let req = api::RunMacros {
run_id: self.run_id,
query: mtreev_to_api(tree, &mut async |b| match *b {}).await,
};
let Some(treev) = self.sys.get::<ReqNot<ExtMsgSet>>().request(req).await else {
return Err(err_cascade(self.sys.i()).await.into());
};
static ATOM_MSG: &str = "Returned atom from Rule recursion";
Ok(mtreev_from_api(&treev, self.sys.i(), &mut async |_| panic!("{ATOM_MSG}")).await)
}
pub fn getv(&mut self, key: &Tok<String>) -> Vec<MTree<'a, Never>> {
self.args.remove(key).expect("Key not found")
}
pub fn gets(&mut self, key: &Tok<String>) -> MTree<'a, Never> {
let v = self.getv(key);
assert!(v.len() == 1, "Not a scalar");
v.into_iter().next().unwrap()
}
pub fn unused_arg<'b>(&mut self, keys: impl IntoIterator<Item = &'b Tok<String>>) {
keys.into_iter().for_each(|k| {
self.getv(k);
});
}
}
trait_set! {
pub trait RuleCB = for<'a> Fn(RuleCtx<'a>) -> LocalBoxFuture<'a, OrcRes<Vec<MTree<'a, AtomFactory>>>>;
}
pub struct Rule {
pub(crate) comments: Vec<String>,
pub(crate) pattern: Vec<MTree<'static, Never>>,
pub(crate) apply: Rc<dyn RuleCB>,
}
impl Rule {
pub(crate) async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MacroRule {
api::MacroRule {
comments: join_all(self.comments.iter().map(|c| async {
api::Comment { text: ctx.sys().i().i(c).await.to_api(), location: api::Location::Inherit }
}))
.await,
location: api::Location::Inherit,
pattern: mtreev_to_api(&self.pattern, &mut async |b| match *b {}).await,
id: ctx.with_rule(Rc::new(self)),
}
}
}
pub fn rule_cmt<'a>(
cmt: impl IntoIterator<Item = &'a str>,
pattern: Vec<MTree<'static, Never>>,
apply: impl RuleCB + 'static,
) -> Rule {
let comments = cmt.into_iter().map(|s| s.to_string()).collect_vec();
Rule { comments, pattern, apply: Rc::new(apply) }
}
pub fn rule(pattern: Vec<MTree<'static, Never>>, apply: impl RuleCB + 'static) -> Rule {
rule_cmt([], pattern, apply)
}

View File

@@ -1,39 +1,49 @@
use futures::future::LocalBoxFuture;
use orchid_base::error::OrcRes;
use orchid_base::name::Sym;
use orchid_base::parse::{Comment, Snippet};
use crate::atom::{AtomFactory, ForeignAtom};
use crate::expr::Expr;
use crate::gen_expr::GExpr;
use crate::system::SysCtx;
use crate::tree::GenTokTree;
pub type GenSnippet<'a> = Snippet<'a, 'a, ForeignAtom<'a>, AtomFactory>;
pub type GenSnippet<'a> = Snippet<'a, Expr, GExpr>;
pub trait Parser: Send + Sync + Sized + Default + 'static {
const LINE_HEAD: &'static str;
fn parse(
ctx: SysCtx,
module: Sym,
exported: bool,
comments: Vec<Comment>,
line: GenSnippet<'_>,
) -> OrcRes<Vec<GenTokTree<'_>>>;
) -> impl Future<Output = OrcRes<Vec<GenTokTree>>> + '_;
}
pub trait DynParser: Send + Sync + 'static {
fn line_head(&self) -> &'static str;
fn parse<'a>(
&self,
ctx: SysCtx,
module: Sym,
exported: bool,
comments: Vec<Comment>,
line: GenSnippet<'a>,
) -> OrcRes<Vec<GenTokTree<'a>>>;
) -> LocalBoxFuture<'a, OrcRes<Vec<GenTokTree>>>;
}
impl<T: Parser> DynParser for T {
fn line_head(&self) -> &'static str { Self::LINE_HEAD }
fn parse<'a>(
&self,
ctx: SysCtx,
module: Sym,
exported: bool,
comments: Vec<Comment>,
line: GenSnippet<'a>,
) -> OrcRes<Vec<GenTokTree<'a>>> {
Self::parse(exported, comments, line)
) -> LocalBoxFuture<'a, OrcRes<Vec<GenTokTree>>> {
Box::pin(async move { Self::parse(ctx, module, exported, comments, line).await })
}
}

View File

@@ -6,7 +6,6 @@ use std::pin::Pin;
use std::rc::Rc;
use futures::future::LocalBoxFuture;
use hashbrown::HashMap;
use memo_map::MemoMap;
use orchid_api::ExtMsgSet;
use orchid_api_traits::{Coding, Decode};
@@ -24,7 +23,7 @@ use crate::func_atom::Fun;
use crate::lexer::LexerObj;
use crate::parser::ParserObj;
use crate::system_ctor::{CtedObj, SystemCtor};
use crate::tree::MemKind;
use crate::tree::GenItem;
/// System as consumed by foreign code
pub trait SystemCard: Default + Send + Sync + 'static {
@@ -83,7 +82,7 @@ impl<T: SystemCard> DynSystemCard for T {
/// System as defined by author
pub trait System: Send + Sync + SystemCard + 'static {
fn env() -> Vec<(String, MemKind)>;
fn env() -> Vec<GenItem>;
fn vfs() -> DeclFs;
fn lexers() -> Vec<LexerObj>;
fn parsers() -> Vec<ParserObj>;
@@ -91,7 +90,7 @@ pub trait System: Send + Sync + SystemCard + 'static {
}
pub trait DynSystem: Send + Sync + DynSystemCard + 'static {
fn dyn_env(&self) -> HashMap<String, MemKind>;
fn dyn_env(&self) -> Vec<GenItem>;
fn dyn_vfs(&self) -> DeclFs;
fn dyn_lexers(&self) -> Vec<LexerObj>;
fn dyn_parsers(&self) -> Vec<ParserObj>;
@@ -100,7 +99,7 @@ pub trait DynSystem: Send + Sync + DynSystemCard + 'static {
}
impl<T: System> DynSystem for T {
fn dyn_env(&self) -> HashMap<String, MemKind> { Self::env().into_iter().collect() }
fn dyn_env(&self) -> Vec<GenItem> { Self::env() }
fn dyn_vfs(&self) -> DeclFs { Self::vfs() }
fn dyn_lexers(&self) -> Vec<LexerObj> { Self::lexers() }
fn dyn_parsers(&self) -> Vec<ParserObj> { Self::parsers() }
@@ -112,10 +111,10 @@ impl<T: System> DynSystem for T {
fn card(&self) -> &dyn DynSystemCard { self }
}
pub async fn downcast_atom<A>(foreign: ForeignAtom<'_>) -> Result<TypAtom<'_, A>, ForeignAtom<'_>>
pub async fn downcast_atom<A>(foreign: ForeignAtom) -> Result<TypAtom<A>, ForeignAtom>
where A: AtomicFeatures {
let mut data = &foreign.atom.data[..];
let ctx = foreign.ctx.clone();
let ctx = foreign.ctx().clone();
let value = AtomTypeId::decode(Pin::new(&mut data)).await;
let own_inst = ctx.get::<CtedObj>().inst();
let owner = if *ctx.get::<api::SysId>() == foreign.atom.owner {

View File

@@ -2,19 +2,16 @@ use crate::entrypoint::ExtensionData;
#[cfg(feature = "tokio")]
pub async fn tokio_main(data: ExtensionData) {
use std::future::Future;
use std::io::Write;
use std::mem;
use std::pin::{Pin, pin};
use std::pin::Pin;
use std::rc::Rc;
use async_std::io;
use async_stream::stream;
use futures::StreamExt;
use futures::future::LocalBoxFuture;
use futures::stream::FuturesUnordered;
use futures::{StreamExt, stream, stream_select};
use orchid_api_traits::{Decode, Encode};
use orchid_base::clone;
use tokio::task::{LocalSet, spawn_local};
use crate::api;

View File

@@ -1,34 +1,67 @@
use std::num::NonZero;
use std::ops::Range;
use std::rc::Rc;
use dyn_clone::{DynClone, clone_box};
use futures::FutureExt;
use futures::future::{LocalBoxFuture, join_all};
use hashbrown::HashMap;
use hashbrown::{HashMap, HashSet};
use itertools::Itertools;
use orchid_base::interner::Tok;
use orchid_base::interner::{Interner, Tok};
use orchid_base::location::Pos;
use orchid_base::name::Sym;
use orchid_base::reqnot::ReqHandlish;
use orchid_base::tree::{TokTree, Token};
use ordered_float::NotNan;
use orchid_base::tree::{TokTree, Token, TokenVariant};
use substack::Substack;
use trait_set::trait_set;
use crate::api;
use crate::atom::{AtomFactory, ForeignAtom};
use crate::conv::ToExpr;
use crate::entrypoint::MemberRecord;
use crate::expr::{Expr, ExprHandle};
use crate::func_atom::{ExprFunc, Fun};
use crate::gen_expr::{GExpr, arg, call, lambda, seq};
use crate::macros::Rule;
use crate::system::SysCtx;
pub type GenTokTree<'a> = TokTree<'a, ForeignAtom<'a>, AtomFactory>;
pub type GenTok<'a> = Token<'a, ForeignAtom<'a>, AtomFactory>;
pub type GenTokTree = TokTree<Expr, GExpr>;
pub type GenTok = Token<Expr, GExpr>;
pub async fn do_extra(f: &AtomFactory, r: Range<u32>, ctx: SysCtx) -> api::TokenTree {
api::TokenTree { range: r, token: api::Token::Atom(f.clone().build(ctx).await) }
impl TokenVariant<api::Expression> for GExpr {
type FromApiCtx<'a> = ();
type ToApiCtx<'a> = (SysCtx, &'a dyn ReqHandlish);
async fn from_api(
_: &api::Expression,
_: &mut Self::FromApiCtx<'_>,
_: Pos,
_: &Interner,
) -> Self {
panic!("Received new expression from host")
}
async fn into_api(self, (ctx, hand): &mut Self::ToApiCtx<'_>) -> api::Expression {
self.api_return(ctx.clone(), hand).await
}
}
impl TokenVariant<api::ExprTicket> for Expr {
type FromApiCtx<'a> = SysCtx;
async fn from_api(
api: &api::ExprTicket,
ctx: &mut Self::FromApiCtx<'_>,
_: Pos,
_: &Interner,
) -> Self {
// SAFETY: receiving trees from sublexers implies ownership transfer
Expr::from_handle(Rc::new(unsafe { ExprHandle::from_args(ctx.clone(), *api) }))
}
type ToApiCtx<'a> = ();
async fn into_api(self, (): &mut Self::ToApiCtx<'_>) -> api::ExprTicket {
let hand = self.handle();
std::mem::drop(self);
let h = match Rc::try_unwrap(hand) {
Ok(h) => h,
Err(h) => h.as_ref().clone().await,
};
h.into_tk()
}
}
fn with_export(mem: GenMember, public: bool) -> Vec<GenItem> {
@@ -47,14 +80,9 @@ impl GenItem {
let kind = match self.kind {
GenItemKind::Export(n) => api::ItemKind::Export(ctx.sys().i().i::<String>(&n).await.to_api()),
GenItemKind::Member(mem) => api::ItemKind::Member(mem.into_api(ctx).await),
GenItemKind::Import(cn) => api::ItemKind::Import(cn.tok().to_api()),
GenItemKind::Macro(priority, gen_rules) => {
let mut rules = Vec::with_capacity(gen_rules.len());
for rule in gen_rules {
rules.push(rule.into_api(ctx).await)
}
api::ItemKind::Macro(api::MacroBlock { priority, rules })
},
GenItemKind::Import(cn) => api::ItemKind::Import(
Sym::parse(&cn, ctx.sys().i()).await.expect("Import path empty string").to_api(),
),
};
let comments = join_all(self.comments.iter().map(|c| async {
api::Comment {
@@ -70,24 +98,23 @@ impl GenItem {
pub fn cnst(public: bool, name: &str, value: impl ToExpr) -> Vec<GenItem> {
with_export(GenMember { name: name.to_string(), kind: MemKind::Const(value.to_expr()) }, public)
}
pub fn import(public: bool, path: &str) -> Vec<GenItem> {
let mut out = vec![GenItemKind::Import(path.to_string())];
if public {
out.push(GenItemKind::Export(path.split("::").last().unwrap().to_string()));
}
out.into_iter().map(|kind| GenItem { comments: vec![], kind }).collect()
}
pub fn module(
public: bool,
name: &str,
imports: impl IntoIterator<Item = Sym>,
items: impl IntoIterator<Item = Vec<GenItem>>,
) -> Vec<GenItem> {
let (name, kind) = root_mod(name, imports, items);
let (name, kind) = root_mod(name, items);
with_export(GenMember { name, kind }, public)
}
pub fn root_mod(
name: &str,
imports: impl IntoIterator<Item = Sym>,
items: impl IntoIterator<Item = Vec<GenItem>>,
) -> (String, MemKind) {
let kind = MemKind::Mod {
imports: imports.into_iter().collect(),
items: items.into_iter().flatten().collect(),
};
pub fn root_mod(name: &str, items: impl IntoIterator<Item = Vec<GenItem>>) -> (String, MemKind) {
let kind = MemKind::Mod { items: items.into_iter().flatten().collect() };
(name.to_string(), kind)
}
pub fn fun<I, O>(exported: bool, name: &str, xf: impl ExprFunc<I, O>) -> Vec<GenItem> {
@@ -107,12 +134,12 @@ pub fn fun<I, O>(exported: bool, name: &str, xf: impl ExprFunc<I, O>) -> Vec<Gen
});
with_export(GenMember { name: name.to_string(), kind: MemKind::Lazy(fac) }, exported)
}
pub fn macro_block(prio: Option<f64>, rules: impl IntoIterator<Item = Rule>) -> Vec<GenItem> {
let prio = prio.map(|p| NotNan::new(p).unwrap());
vec![GenItem {
kind: GenItemKind::Macro(prio, rules.into_iter().collect_vec()),
comments: vec![],
}]
pub fn prefix(path: &str, items: impl IntoIterator<Item = Vec<GenItem>>) -> Vec<GenItem> {
let mut items = items.into_iter().flatten().collect_vec();
for step in path.split("::").collect_vec().into_iter().rev() {
items = module(true, step, [items]);
}
items
}
pub fn comments<'a>(
@@ -126,6 +153,58 @@ pub fn comments<'a>(
val
}
/// Trivially merge a gen tree. Behaviours were chosen to make this simple.
///
/// - Comments on imports are discarded
/// - Comments on exports and submodules are combined
/// - Duplicate constants result in an error
/// - A combination of lazy and anything results in an error
pub fn merge_trivial(trees: impl IntoIterator<Item = Vec<GenItem>>) -> Vec<GenItem> {
let mut imported = HashSet::<String>::new();
let mut exported = HashMap::<String, HashSet<String>>::new();
let mut members = HashMap::<String, (MemKind, HashSet<String>)>::new();
for item in trees.into_iter().flatten() {
match item.kind {
GenItemKind::Import(sym) => {
imported.insert(sym);
},
GenItemKind::Export(e) =>
exported.entry(e.clone()).or_insert(HashSet::new()).extend(item.comments.iter().cloned()),
GenItemKind::Member(mem) => match mem.kind {
unit @ (MemKind::Const(_) | MemKind::Lazy(_)) => {
let prev = members.insert(mem.name.clone(), (unit, item.comments.into_iter().collect()));
assert!(prev.is_none(), "Conflict in trivial tree merge on {}", mem.name);
},
MemKind::Mod { items } => match members.entry(mem.name.clone()) {
hashbrown::hash_map::Entry::Vacant(slot) => {
slot.insert((MemKind::Mod { items }, item.comments.into_iter().collect()));
},
hashbrown::hash_map::Entry::Occupied(mut old) => match old.get_mut() {
(MemKind::Mod { items: old_items }, old_cmts) => {
let mut swap = vec![];
std::mem::swap(&mut swap, old_items);
*old_items = merge_trivial([swap, items]);
old_cmts.extend(item.comments);
},
_ => panic!("Conflict in trivial merge on {}", mem.name),
},
},
},
}
}
(imported.into_iter().map(|txt| GenItem { comments: vec![], kind: GenItemKind::Import(txt) }))
.chain(exported.into_iter().map(|(k, cmtv)| GenItem {
comments: cmtv.into_iter().collect(),
kind: GenItemKind::Export(k),
}))
.chain(members.into_iter().map(|(name, (kind, cmtv))| GenItem {
comments: cmtv.into_iter().collect(),
kind: GenItemKind::Member(GenMember { name, kind }),
}))
.collect()
}
trait_set! {
trait LazyMemberCallback =
FnOnce(Sym, SysCtx) -> LocalBoxFuture<'static, MemKind> + DynClone
@@ -144,13 +223,12 @@ impl Clone for LazyMemberFactory {
pub enum GenItemKind {
Member(GenMember),
Export(String),
Import(Sym),
Macro(Option<NotNan<f64>>, Vec<Rule>),
Import(String),
}
pub struct GenMember {
name: String,
kind: MemKind,
pub name: String,
pub kind: MemKind,
}
impl GenMember {
pub async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::Member {
@@ -164,7 +242,7 @@ impl GenMember {
pub enum MemKind {
Const(GExpr),
Mod { imports: Vec<Sym>, items: Vec<GenItem> },
Mod { items: Vec<GenItem> },
Lazy(LazyMemberFactory),
}
impl MemKind {
@@ -172,15 +250,12 @@ impl MemKind {
match self {
Self::Lazy(lazy) => api::MemberKind::Lazy(ctx.with_lazy(lazy)),
Self::Const(c) => api::MemberKind::Const(c.api_return(ctx.sys(), ctx.req()).await),
Self::Mod { imports, items } => {
let all_items = (imports.into_iter())
.map(|t| GenItem { comments: vec![], kind: GenItemKind::Import(t) })
.chain(items);
let mut items = Vec::new();
for i in all_items {
items.push(i.into_api(ctx).boxed_local().await)
Self::Mod { items } => {
let mut api_items = Vec::new();
for i in items {
api_items.push(i.into_api(ctx).boxed_local().await)
}
api::MemberKind::Module(api::Module { items })
api::MemberKind::Module(api::Module { items: api_items })
},
}
}
@@ -189,7 +264,6 @@ impl MemKind {
pub trait TreeIntoApiCtx {
fn sys(&self) -> SysCtx;
fn with_lazy(&mut self, fac: LazyMemberFactory) -> api::TreeId;
fn with_rule(&mut self, rule: Rc<Rule>) -> api::MacroId;
fn push_path(&mut self, seg: Tok<String>) -> impl TreeIntoApiCtx;
fn req(&self) -> &impl ReqHandlish;
}
@@ -199,7 +273,6 @@ pub struct TreeIntoApiCtxImpl<'a, 'b, RH: ReqHandlish> {
pub basepath: &'a [Tok<String>],
pub path: Substack<'a, Tok<String>>,
pub lazy_members: &'b mut HashMap<api::TreeId, MemberRecord>,
pub rules: &'b mut HashMap<api::MacroId, Rc<Rule>>,
pub req: &'a RH,
}
@@ -209,7 +282,6 @@ impl<RH: ReqHandlish> TreeIntoApiCtx for TreeIntoApiCtxImpl<'_, '_, RH> {
TreeIntoApiCtxImpl {
req: self.req,
lazy_members: self.lazy_members,
rules: self.rules,
sys: self.sys.clone(),
basepath: self.basepath,
path: self.path.push(seg),
@@ -221,10 +293,5 @@ impl<RH: ReqHandlish> TreeIntoApiCtx for TreeIntoApiCtxImpl<'_, '_, RH> {
self.lazy_members.insert(id, MemberRecord::Gen(path, fac));
id
}
fn with_rule(&mut self, rule: Rc<Rule>) -> orchid_api::MacroId {
let id = api::MacroId(NonZero::new((self.lazy_members.len() + 1) as u64).unwrap());
self.rules.insert(id, rule);
id
}
fn req(&self) -> &impl ReqHandlish { self.req }
}

View File

@@ -1,7 +1,7 @@
[package]
name = "orchid-host"
version = "0.1.0"
edition = "2021"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -1,8 +1,9 @@
use std::cell::RefCell;
use std::num::{NonZero, NonZeroU16};
use std::rc::Rc;
use std::rc::{Rc, Weak};
use std::{fmt, ops};
use async_once_cell::OnceCell;
use async_std::sync::RwLock;
use hashbrown::HashMap;
use orchid_api::SysId;
@@ -11,8 +12,9 @@ use orchid_base::interner::Interner;
use crate::api;
use crate::atom::WeakAtomHand;
use crate::expr_store::ExprStore;
use crate::parsed::Root;
use crate::system::{System, WeakSystem};
use crate::tree::Module;
pub struct CtxData {
pub i: Rc<Interner>,
@@ -20,7 +22,8 @@ pub struct CtxData {
pub systems: RwLock<HashMap<api::SysId, WeakSystem>>,
pub system_id: RefCell<NonZeroU16>,
pub owned_atoms: RwLock<HashMap<api::AtomId, WeakAtomHand>>,
// pub root: RwLock<Module>,
pub common_exprs: ExprStore,
pub root: OnceCell<Weak<Root>>,
}
#[derive(Clone)]
pub struct Ctx(Rc<CtxData>);
@@ -36,7 +39,8 @@ impl Ctx {
systems: RwLock::default(),
system_id: RefCell::new(NonZero::new(1).unwrap()),
owned_atoms: RwLock::default(),
// root: RwLock::new(Module::default()),
common_exprs: ExprStore::default(),
root: OnceCell::default(),
}))
}
pub(crate) async fn system_inst(&self, id: api::SysId) -> Option<System> {
@@ -47,6 +51,10 @@ impl Ctx {
*g = g.checked_add(1).unwrap_or(NonZeroU16::new(1).unwrap());
SysId(*g)
}
pub async fn set_root(&self, root: Weak<Root>) {
assert!(self.root.get().is_none(), "Root already assigned");
self.root.get_or_init(async { root }).await;
}
}
impl fmt::Debug for Ctx {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {

View File

@@ -8,9 +8,10 @@ use orchid_base::format::{FmtCtxImpl, Format, take_first};
use orchid_base::interner::{Interner, Tok};
use orchid_base::location::Pos;
use orchid_base::name::{NameLike, Sym, VName};
use substack::Substack;
use crate::macros::{MacTok, MacTree};
use crate::tree::{ItemKind, MemberKind, Module, RuleKind, WalkErrorKind};
use crate::expr::Expr;
use crate::parsed::{ItemKind, ParsedMemberKind, ParsedModule, WalkErrorKind};
/// Errors produced by absolute_path
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
@@ -73,28 +74,36 @@ pub fn absolute_path(
.map_err(|_| AbsPathError::RootPath)
}
pub struct DealiasCtx<'a> {
pub i: &'a Interner,
pub rep: &'a Reporter,
pub consts: &'a mut HashMap<Sym, Expr>,
}
pub async fn resolv_glob(
cwd: &[Tok<String>],
root: &Module,
root: &ParsedModule,
abs_path: &[Tok<String>],
pos: Pos,
i: &Interner,
r: &impl Reporter,
ctx: &mut DealiasCtx<'_>,
) -> Vec<Tok<String>> {
let coprefix_len = cwd.iter().zip(abs_path).take_while(|(a, b)| a == b).count();
let (co_prefix, diff_path) = abs_path.split_at(coprefix_len);
let co_parent = root.walk(false, co_prefix.iter().cloned()).await.expect("Invalid step in cwd");
let target_module = match co_parent.walk(true, diff_path.iter().cloned()).await {
let co_parent =
root.walk(false, co_prefix.iter().cloned(), ctx.consts).await.expect("Invalid step in cwd");
let target_module = match co_parent.walk(true, diff_path.iter().cloned(), ctx.consts).await {
Ok(t) => t,
Err(e) => {
let path = abs_path[..=coprefix_len + e.pos].iter().join("::");
let (tk, msg) = match e.kind {
WalkErrorKind::Constant =>
(i.i("Invalid import path").await, format!("{path} is a constant")),
WalkErrorKind::Missing => (i.i("Invalid import path").await, format!("{path} not found")),
WalkErrorKind::Private => (i.i("Import inaccessible").await, format!("{path} is private")),
(ctx.i.i("Invalid import path").await, format!("{path} is a constant")),
WalkErrorKind::Missing =>
(ctx.i.i("Invalid import path").await, format!("{path} not found")),
WalkErrorKind::Private =>
(ctx.i.i("Import inaccessible").await, format!("{path} is private")),
};
r.report(mk_err(tk, msg, [pos.into()]));
(&ctx.rep).report(mk_err(tk, msg, [pos.into()]));
return vec![];
},
};
@@ -104,36 +113,36 @@ pub async fn resolv_glob(
/// Read import statements and convert them into aliases, rasising any import
/// errors in the process
pub async fn imports_to_aliases(
module: &Module,
module: &ParsedModule,
cwd: &mut Vec<Tok<String>>,
root: &Module,
root: &ParsedModule,
alias_map: &mut HashMap<Sym, Sym>,
alias_rev_map: &mut HashMap<Sym, HashSet<Sym>>,
i: &Interner,
rep: &impl Reporter,
ctx: &mut DealiasCtx<'_>,
) {
let mut import_locs = HashMap::<Sym, Vec<Pos>>::new();
for item in &module.items {
match &item.kind {
ItemKind::Import(imp) => match absolute_path(cwd, &imp.path) {
Err(e) => rep.report(e.err_obj(i, item.pos.clone(), &imp.path.iter().join("::")).await),
Err(e) =>
ctx.rep.report(e.err_obj(ctx.i, item.pos.clone(), &imp.path.iter().join("::")).await),
Ok(abs_path) => {
let names = match imp.name.as_ref() {
Some(n) => Either::Right([n.clone()].into_iter()),
None => Either::Left(
resolv_glob(cwd, root, &abs_path, item.pos.clone(), i, rep).await.into_iter(),
resolv_glob(cwd, root, &abs_path, item.pos.clone(), ctx).await.into_iter(),
),
};
for name in names {
let mut tgt = abs_path.clone().suffix([name.clone()]).to_sym(i).await;
let src = Sym::new(cwd.iter().cloned().chain([name]), i).await.unwrap();
let mut tgt = abs_path.clone().suffix([name.clone()]).to_sym(ctx.i).await;
let src = Sym::new(cwd.iter().cloned().chain([name]), ctx.i).await.unwrap();
import_locs.entry(src.clone()).or_insert(vec![]).push(item.pos.clone());
if let Some(tgt2) = alias_map.get(&tgt) {
tgt = tgt2.clone();
}
if src == tgt {
rep.report(mk_err(
i.i("Circular references").await,
ctx.rep.report(mk_err(
ctx.i.i("Circular references").await,
format!("{src} circularly refers to itself"),
[item.pos.clone().into()],
));
@@ -142,8 +151,8 @@ pub async fn imports_to_aliases(
if let Some(fst_val) = alias_map.get(&src) {
let locations = (import_locs.get(&src))
.expect("The same name could only have appeared in the same module");
rep.report(mk_err(
i.i("Conflicting imports").await,
ctx.rep.report(mk_err(
ctx.i.i("Conflicting imports").await,
if fst_val == &src {
format!("{src} is imported multiple times")
} else {
@@ -163,78 +172,35 @@ pub async fn imports_to_aliases(
}
},
},
ItemKind::Member(mem) => match mem.kind().await {
MemberKind::Const(_) => (),
MemberKind::Mod(m) => {
ItemKind::Member(mem) => match mem.kind(ctx.consts).await {
ParsedMemberKind::Const => (),
ParsedMemberKind::Mod(m) => {
cwd.push(mem.name());
imports_to_aliases(m, cwd, root, alias_map, alias_rev_map, i, rep).boxed_local().await;
imports_to_aliases(m, cwd, root, alias_map, alias_rev_map, ctx).boxed_local().await;
cwd.pop();
},
},
ItemKind::Export(_) | ItemKind::Macro(..) => (),
ItemKind::Export(_) => (),
}
}
}
pub async fn dealias(module: &mut Module, alias_map: &HashMap<Sym, Sym>, i: &Interner) {
pub async fn dealias(
path: Substack<'_, Tok<String>>,
module: &mut ParsedModule,
alias_map: &HashMap<Sym, Sym>,
ctx: &mut DealiasCtx<'_>,
) {
for item in &mut module.items {
match &mut item.kind {
ItemKind::Export(_) | ItemKind::Import(_) => (),
ItemKind::Member(mem) => match mem.kind_mut().await {
MemberKind::Const(c) => {
let Some(source) = c.source() else { continue };
let Some(new_source) = dealias_mactreev(source, alias_map, i).await else { continue };
c.set_source(new_source);
},
MemberKind::Mod(m) => dealias(m, alias_map, i).boxed_local().await,
},
ItemKind::Macro(_, rules) =>
for rule in rules.iter_mut() {
let RuleKind::Native(c) = &mut rule.kind else { continue };
let Some(source) = c.source() else { continue };
let Some(new_source) = dealias_mactreev(source, alias_map, i).await else { continue };
c.set_source(new_source);
ItemKind::Member(mem) => {
let path = path.push(mem.name());
match mem.kind_mut(ctx.consts).await {
ParsedMemberKind::Const => (),
ParsedMemberKind::Mod(m) => dealias(path, m, alias_map, ctx).boxed_local().await,
}
},
}
}
}
async fn dealias_mactree(
mtree: &MacTree,
aliases: &HashMap<Sym, Sym>,
i: &Interner,
) -> Option<MacTree> {
let new_tok = match &*mtree.tok {
MacTok::Atom(_) | MacTok::Ph(_) => return None,
tok @ (MacTok::Done(_) | MacTok::Ref(_) | MacTok::Slot(_)) => panic!(
"{} should not appear in retained pre-macro source",
take_first(&tok.print(&FmtCtxImpl { i }).await, true)
),
MacTok::Name(n) => MacTok::Name(aliases.get(n).unwrap_or(n).clone()),
MacTok::Lambda(arg, body) => {
match (dealias_mactreev(arg, aliases, i).await, dealias_mactreev(body, aliases, i).await) {
(None, None) => return None,
(Some(arg), None) => MacTok::Lambda(arg, body.clone()),
(None, Some(body)) => MacTok::Lambda(arg.clone(), body),
(Some(arg), Some(body)) => MacTok::Lambda(arg, body),
}
},
MacTok::S(p, b) => MacTok::S(*p, dealias_mactreev(b, aliases, i).await?),
};
Some(MacTree { pos: mtree.pos.clone(), tok: Rc::new(new_tok) })
}
async fn dealias_mactreev(
mtreev: &[MacTree],
aliases: &HashMap<Sym, Sym>,
i: &Interner,
) -> Option<Vec<MacTree>> {
let mut results = Vec::with_capacity(mtreev.len());
let mut any_some = false;
for item in mtreev {
let out = dealias_mactree(item, aliases, i).boxed_local().await;
any_some |= out.is_some();
results.push(out.unwrap_or(item.clone()));
}
any_some.then_some(results)
}

View File

@@ -3,15 +3,13 @@ use std::mem;
use async_std::sync::RwLockWriteGuard;
use bound::Bound;
use futures::FutureExt;
use orchid_base::error::{OrcErrv, mk_errv};
use orchid_base::error::OrcErrv;
use orchid_base::format::{FmtCtxImpl, Format, take_first};
use orchid_base::location::Pos;
use orchid_base::logging::Logger;
use orchid_base::name::NameLike;
use crate::ctx::Ctx;
use crate::expr::{Expr, ExprKind, PathSet, Step};
use crate::tree::{ItemKind, MemberKind, Module, Root};
use crate::expr::{Expr, ExprKind, ExprParseCtx, PathSet, PathSetBuilder, Step};
type ExprGuard = Bound<RwLockWriteGuard<'static, ExprKind>, Expr>;
@@ -38,13 +36,12 @@ pub struct ExecCtx {
cur_pos: Pos,
did_pop: bool,
logger: Logger,
root: Root,
}
impl ExecCtx {
pub async fn new(ctx: Ctx, logger: Logger, root: Root, init: Expr) -> Self {
pub async fn new(ctx: Ctx, logger: Logger, init: Expr) -> Self {
let cur_pos = init.pos();
let cur = Bound::async_new(init, |init| init.kind().write()).await;
Self { ctx, gas: None, stack: vec![], cur, cur_pos, did_pop: false, root, logger }
Self { ctx, gas: None, stack: vec![], cur, cur_pos, did_pop: false, logger }
}
pub fn remaining_gas(&self) -> u64 { self.gas.expect("queried remaining_gas but no gas was set") }
pub fn set_gas(&mut self, gas: Option<u64>) { self.gas = gas }
@@ -92,10 +89,13 @@ impl ExecCtx {
},
ExprKind::Seq(a, b) if !self.did_pop => (ExprKind::Seq(a.clone(), b), StackOp::Push(a)),
ExprKind::Seq(_, b) => (ExprKind::Identity(b), StackOp::Nop),
ExprKind::Const(name) =>
match self.root.get_const_value(name, self.cur_pos.clone(), self.ctx.clone()).await {
ExprKind::Const(name) => {
let root = (self.ctx.root.get().and_then(|v| v.upgrade()))
.expect("Root not assigned before execute call");
match root.get_const_value(name, self.cur_pos.clone(), self.ctx.clone()).await {
Err(e) => (ExprKind::Bottom(e), StackOp::Pop),
Ok(v) => (ExprKind::Identity(v), StackOp::Nop),
}
},
ExprKind::Arg => panic!("This should not appear outside function bodies"),
ek @ ExprKind::Atom(_) => (ek, StackOp::Pop),
@@ -103,9 +103,11 @@ impl ExecCtx {
ExprKind::Call(f, x) if !self.did_pop => (ExprKind::Call(f.clone(), x), StackOp::Push(f)),
ExprKind::Call(f, x) => match f.try_into_owned_atom().await {
Ok(atom) => {
let mut ext = atom.sys().ext().clone();
let ext = atom.sys().ext().clone();
let x_norm = self.unpack_ident(&x).await;
let val = Expr::from_api(&atom.call(x_norm).await, &mut ext).await;
let mut parse_ctx = ExprParseCtx { ctx: self.ctx.clone(), exprs: ext.exprs().clone() };
let val =
Expr::from_api(&atom.call(x_norm).await, PathSetBuilder::new(), &mut parse_ctx).await;
(ExprKind::Identity(val.clone()), StackOp::Swap(val))
},
Err(f) => match &*f.kind().read().await {
@@ -113,9 +115,16 @@ impl ExecCtx {
panic!("This should not appear outside function bodies"),
ExprKind::Missing => panic!("Should have been replaced"),
ExprKind::Atom(a) => {
let mut ext = a.sys().ext().clone();
let ext = a.sys().ext().clone();
let x_norm = self.unpack_ident(&x).await;
let val = Expr::from_api(&a.clone().call(x_norm).await, &mut ext).await;
let mut parse_ctx =
ExprParseCtx { ctx: ext.ctx().clone(), exprs: ext.exprs().clone() };
let val = Expr::from_api(
&a.clone().call(x_norm).await,
PathSetBuilder::new(),
&mut parse_ctx,
)
.await;
(ExprKind::Identity(val.clone()), StackOp::Swap(val))
},
ExprKind::Bottom(exprv) => (ExprKind::Bottom(exprv.clone()), StackOp::Pop),

View File

@@ -8,23 +8,24 @@ use async_std::sync::RwLock;
use futures::FutureExt;
use hashbrown::HashSet;
use itertools::Itertools;
use orchid_base::error::{OrcErrv, mk_errv};
use orchid_base::format::{FmtCtx, FmtCtxImpl, FmtUnit, Format, Variants, take_first};
use orchid_base::error::OrcErrv;
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
use orchid_base::location::Pos;
use orchid_base::macros::mtreev_fmt;
use orchid_base::name::Sym;
use orchid_base::tokens::Paren;
use orchid_base::tl_cache;
use orchid_base::tree::{AtomRepr, indent};
use orchid_base::{match_mapping, tl_cache};
use substack::Substack;
use crate::api;
use crate::atom::AtomHand;
use crate::ctx::Ctx;
use crate::extension::Extension;
use crate::macros::{MacTok, MacTree};
use crate::expr_store::ExprStore;
pub type ExprParseCtx = Extension;
#[derive(Clone)]
pub struct ExprParseCtx {
pub ctx: Ctx,
pub exprs: ExprStore,
}
#[derive(Debug)]
pub struct ExprData {
@@ -55,13 +56,44 @@ impl Expr {
.expect("this is a ref, it cannot be null"),
)
}
pub async fn from_api(api: &api::Expression, ctx: &mut ExprParseCtx) -> Self {
if let api::ExpressionKind::Slot(tk) = &api.kind {
return ctx.exprs().get_expr(*tk).expect("Invalid slot");
}
let pos = Pos::from_api(&api.location, &ctx.ctx().i).await;
let kind = RwLock::new(ExprKind::from_api(&api.kind, pos.clone(), ctx).boxed_local().await);
Self(Rc::new(ExprData { pos, kind }))
pub async fn from_api(
api: &api::Expression,
psb: PathSetBuilder<'_, u64>,
ctx: &mut ExprParseCtx,
) -> Self {
let pos = Pos::from_api(&api.location, &ctx.ctx.i).await;
let kind = match &api.kind {
api::ExpressionKind::Arg(n) => {
assert!(psb.register_arg(&n), "Arguments must be enclosed in a matching lambda");
ExprKind::Arg
},
api::ExpressionKind::Bottom(bot) =>
ExprKind::Bottom(OrcErrv::from_api(bot, &ctx.ctx.i).await),
api::ExpressionKind::Call(f, x) => {
let (lpsb, rpsb) = psb.split();
ExprKind::Call(
Expr::from_api(&f, lpsb, ctx).boxed_local().await,
Expr::from_api(&x, rpsb, ctx).boxed_local().await,
)
},
api::ExpressionKind::Const(name) => ExprKind::Const(Sym::from_api(*name, &ctx.ctx.i).await),
api::ExpressionKind::Lambda(x, body) => {
let lbuilder = psb.lambda(&x);
let body = Expr::from_api(&body, lbuilder.stack(), ctx).boxed_local().await;
ExprKind::Lambda(lbuilder.collect(), body)
},
api::ExpressionKind::NewAtom(a) =>
ExprKind::Atom(AtomHand::from_api(a, pos.clone(), &mut ctx.ctx.clone()).await),
api::ExpressionKind::Slot(tk) => return ctx.exprs.get_expr(*tk).expect("Invalid slot"),
api::ExpressionKind::Seq(a, b) => {
let (apsb, bpsb) = psb.split();
ExprKind::Seq(
Expr::from_api(&a, apsb, ctx).boxed_local().await,
Expr::from_api(&b, bpsb, ctx).boxed_local().await,
)
},
};
Self(Rc::new(ExprData { pos, kind: RwLock::new(kind) }))
}
pub async fn to_api(&self) -> api::InspectedKind {
use api::InspectedKind as K;
@@ -107,23 +139,6 @@ pub enum ExprKind {
Missing,
}
impl ExprKind {
pub async fn from_api(api: &api::ExpressionKind, pos: Pos, ctx: &mut ExprParseCtx) -> Self {
match_mapping!(api, api::ExpressionKind => ExprKind {
Lambda(id => PathSet::from_api(*id, api), b => Expr::from_api(b, ctx).await),
Bottom(b => OrcErrv::from_api(b, &ctx.ctx().i).await),
Call(f => Expr::from_api(f, ctx).await, x => Expr::from_api(x, ctx).await),
Const(c => Sym::from_api(*c, &ctx.ctx().i).await),
Seq(a => Expr::from_api(a, ctx).await, b => Expr::from_api(b, ctx).await),
} {
api::ExpressionKind::Arg(_) => ExprKind::Arg,
api::ExpressionKind::NewAtom(a) => ExprKind::Atom(AtomHand::from_api(
a,
pos,
&mut ctx.ctx().clone()
).await),
api::ExpressionKind::Slot(_) => panic!("Handled in Expr"),
})
}
pub fn at(self, pos: Pos) -> Expr { Expr(Rc::new(ExprData { pos, kind: RwLock::new(self) })) }
}
impl Format for ExprKind {
@@ -174,6 +189,93 @@ pub enum Step {
Right,
}
#[derive(Clone)]
pub enum PathSetFrame<'a, T: PartialEq> {
Lambda(&'a T, &'a RefCell<Option<PathSet>>),
Step(Step),
}
#[derive(Clone)]
pub struct PathSetBuilder<'a, T: PartialEq>(Substack<'a, PathSetFrame<'a, T>>);
impl<'a, T: PartialEq> PathSetBuilder<'a, T> {
pub fn new() -> Self { Self(Substack::Bottom) }
pub fn split(&'a self) -> (Self, Self) {
(
Self(self.0.push(PathSetFrame::Step(Step::Left))),
Self(self.0.push(PathSetFrame::Step(Step::Right))),
)
}
pub fn lambda<'b>(self, arg: &'b T) -> LambdaBuilder<'b, T>
where 'a: 'b {
LambdaBuilder { arg, path: RefCell::default(), stack: self }
}
/// Register an argument with the corresponding lambda and return true if one
/// was found. (if false is returned, the name is unbound and may refer to a
/// global)
pub fn register_arg(self, t: &T) -> bool {
let mut steps = VecDeque::new();
for step in self.0.iter() {
match step {
PathSetFrame::Step(step) => steps.push_front(*step),
PathSetFrame::Lambda(name, _) if **name != *t => (),
PathSetFrame::Lambda(_, cell) => {
let mut ps_opt = cell.borrow_mut();
match &mut *ps_opt {
val @ None => *val = Some(PathSet { steps: steps.into(), next: None }),
Some(val) => {
let mut swap = PathSet { steps: Vec::new(), next: None };
mem::swap(&mut swap, val);
*val = merge(swap, &Vec::from(steps));
},
}
return true;
},
};
}
return false;
fn merge(ps: PathSet, steps: &[Step]) -> PathSet {
let diff_idx = ps.steps.iter().zip(steps).take_while(|(l, r)| l == r).count();
if diff_idx == ps.steps.len() {
if diff_idx == steps.len() {
match ps.next {
Some(_) => panic!("New path ends where old path forks"),
None => panic!("New path same as old path"),
}
}
let Some((left, right)) = ps.next else { panic!("Old path ends where new path continues") };
let next = match steps[diff_idx] {
Step::Left => Some((Box::new(merge(*left, &steps[diff_idx + 1..])), right)),
Step::Right => Some((left, Box::new(merge(*right, &steps[diff_idx + 1..])))),
};
PathSet { steps: ps.steps, next }
} else {
let shared_steps = ps.steps.iter().take(diff_idx).cloned().collect();
let main_steps = ps.steps.iter().skip(diff_idx + 1).cloned().collect();
let new_branch = steps[diff_idx + 1..].to_vec();
let main_side = PathSet { steps: main_steps, next: ps.next };
let new_side = PathSet { steps: new_branch, next: None };
let (left, right) = match steps[diff_idx] {
Step::Left => (new_side, main_side),
Step::Right => (main_side, new_side),
};
PathSet { steps: shared_steps, next: Some((Box::new(left), Box::new(right))) }
}
}
}
}
pub struct LambdaBuilder<'a, T: PartialEq> {
arg: &'a T,
path: RefCell<Option<PathSet>>,
stack: PathSetBuilder<'a, T>,
}
impl<'a, T: PartialEq> LambdaBuilder<'a, T> {
pub fn stack(&'a self) -> PathSetBuilder<'a, T> {
PathSetBuilder(self.stack.0.push(PathSetFrame::Lambda(self.arg, &self.path)))
}
pub fn collect(self) -> Option<PathSet> { self.path.into_inner() }
}
#[derive(Clone, Debug)]
pub struct PathSet {
/// The single steps through [super::nort::Clause::Apply]
@@ -186,32 +288,6 @@ impl PathSet {
pub fn next(&self) -> Option<(&PathSet, &PathSet)> {
self.next.as_ref().map(|(l, r)| (&**l, &**r))
}
pub fn from_api(id: u64, api: &api::ExpressionKind) -> Option<Self> {
use api::ExpressionKind as K;
struct Suffix(VecDeque<Step>, Option<(Box<PathSet>, Box<PathSet>)>);
fn seal(Suffix(steps, next): Suffix) -> PathSet { PathSet { steps: steps.into(), next } }
fn after(step: Step, mut suf: Suffix) -> Suffix {
suf.0.push_front(step);
suf
}
return from_api_inner(id, api).map(seal);
fn from_api_inner(id: u64, api: &api::ExpressionKind) -> Option<Suffix> {
match &api {
K::Arg(id2) => (id == *id2).then_some(Suffix(VecDeque::new(), None)),
K::Bottom(_) | K::Const(_) | K::NewAtom(_) | K::Slot(_) => None,
K::Lambda(_, b) => from_api_inner(id, &b.kind),
K::Call(l, r) | K::Seq(l, r) => {
match (from_api_inner(id, &l.kind), from_api_inner(id, &r.kind)) {
(Some(a), Some(b)) =>
Some(Suffix(VecDeque::new(), Some((Box::new(seal(a)), Box::new(seal(b)))))),
(Some(l), None) => Some(after(Step::Left, l)),
(None, Some(r)) => Some(after(Step::Right, r)),
(None, None) => None,
}
},
}
}
}
}
impl fmt::Display for PathSet {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -239,121 +315,3 @@ pub struct WeakExpr(Weak<ExprData>);
impl WeakExpr {
pub fn upgrade(&self) -> Option<Expr> { self.0.upgrade().map(Expr) }
}
#[derive(Clone)]
pub enum SrcToExprStep<'a> {
Left,
Right,
Lambda(Sym, &'a RefCell<Option<PathSet>>),
}
pub async fn mtreev_to_expr(
src: &[MacTree],
stack: Substack<'_, SrcToExprStep<'_>>,
ctx: &Ctx,
) -> ExprKind {
let Some((x, f)) = src.split_last() else { panic!("Empty expression cannot be evaluated") };
let x_stack = if f.is_empty() { stack.clone() } else { stack.push(SrcToExprStep::Right) };
let x_kind = match &*x.tok {
MacTok::Atom(a) => ExprKind::Atom(a.clone()),
MacTok::Name(n) => 'name: {
let mut steps = VecDeque::new();
for step in x_stack.iter() {
match step {
SrcToExprStep::Left => steps.push_front(Step::Left),
SrcToExprStep::Right => steps.push_front(Step::Right),
SrcToExprStep::Lambda(name, _) if name != n => continue,
SrcToExprStep::Lambda(_, cell) => {
let mut ps = cell.borrow_mut();
match &mut *ps {
val @ None => *val = Some(PathSet { steps: steps.into(), next: None }),
Some(val) => {
let mut swap = PathSet { steps: Vec::new(), next: None };
mem::swap(&mut swap, val);
*val = merge(swap, &Vec::from(steps));
fn merge(ps: PathSet, steps: &[Step]) -> PathSet {
let diff_idx = ps.steps.iter().zip(steps).take_while(|(l, r)| l == r).count();
if diff_idx == ps.steps.len() {
if diff_idx == steps.len() {
match ps.next {
Some(_) => panic!("New path ends where old path forks"),
None => panic!("New path same as old path"),
}
}
let Some((left, right)) = ps.next else {
panic!("Old path ends where new path continues")
};
let next = match steps[diff_idx] {
Step::Left => Some((Box::new(merge(*left, &steps[diff_idx + 1..])), right)),
Step::Right => Some((left, Box::new(merge(*right, &steps[diff_idx + 1..])))),
};
PathSet { steps: ps.steps, next }
} else {
let shared_steps = ps.steps.iter().take(diff_idx).cloned().collect();
let main_steps = ps.steps.iter().skip(diff_idx + 1).cloned().collect();
let new_branch = steps[diff_idx + 1..].to_vec();
let main_side = PathSet { steps: main_steps, next: ps.next };
let new_side = PathSet { steps: new_branch, next: None };
let (left, right) = match steps[diff_idx] {
Step::Left => (new_side, main_side),
Step::Right => (main_side, new_side),
};
PathSet { steps: shared_steps, next: Some((Box::new(left), Box::new(right))) }
}
}
},
}
break 'name ExprKind::Arg;
},
}
}
ExprKind::Const(n.clone())
},
MacTok::Ph(_) | MacTok::Done(_) | MacTok::Ref(_) | MacTok::Slot(_) =>
ExprKind::Bottom(mk_errv(
ctx.i.i("placeholder in value").await,
"Placeholders cannot appear anywhere outside macro patterns",
[x.pos.clone().into()],
)),
MacTok::S(Paren::Round, b) if b.is_empty() =>
return ExprKind::Bottom(mk_errv(
ctx.i.i("Empty expression").await,
"Empty parens () are illegal",
[x.pos.clone().into()],
)),
MacTok::S(Paren::Round, b) => mtreev_to_expr(b, x_stack, ctx).boxed_local().await,
MacTok::S(..) => ExprKind::Bottom(mk_errv(
ctx.i.i("non-round parentheses after macros").await,
"[] or {} block was not consumed by macros; expressions may only contain ()",
[x.pos.clone().into()],
)),
MacTok::Lambda(_, b) if b.is_empty() =>
return ExprKind::Bottom(mk_errv(
ctx.i.i("Empty lambda").await,
"Lambdas must have a body",
[x.pos.clone().into()],
)),
MacTok::Lambda(arg, b) => 'lambda_converter: {
if let [MacTree { tok, .. }] = &**arg {
if let MacTok::Name(n) = &**tok {
let path = RefCell::new(None);
let b = mtreev_to_expr(b, x_stack.push(SrcToExprStep::Lambda(n.clone(), &path)), ctx)
.boxed_local()
.await;
break 'lambda_converter ExprKind::Lambda(path.into_inner(), b.at(x.pos.clone()));
}
}
let argstr = take_first(&mtreev_fmt(arg, &FmtCtxImpl { i: &ctx.i }).await, true);
ExprKind::Bottom(mk_errv(
ctx.i.i("Malformeed lambda").await,
format!("Lambda argument should be single name, found {argstr}"),
[x.pos.clone().into()],
))
},
};
if f.is_empty() {
return x_kind;
}
let f = mtreev_to_expr(f, stack.push(SrcToExprStep::Left), ctx).boxed_local().await;
ExprKind::Call(f.at(Pos::None), x_kind.at(x.pos.clone()))
}

View File

@@ -1,5 +1,6 @@
use std::cell::RefCell;
use std::fmt;
use std::rc::Rc;
use hashbrown::HashMap;
use hashbrown::hash_map::Entry;
@@ -8,10 +9,18 @@ use crate::api;
use crate::expr::Expr;
#[derive(Default)]
pub struct ExprStore(RefCell<HashMap<api::ExprTicket, (u32, Expr)>>);
pub struct ExprStoreData {
exprs: RefCell<HashMap<api::ExprTicket, (u32, Expr)>>,
parent: Option<ExprStore>,
}
#[derive(Clone, Default)]
pub struct ExprStore(Rc<ExprStoreData>);
impl ExprStore {
pub fn derive(&self) -> Self {
Self(Rc::new(ExprStoreData { exprs: RefCell::default(), parent: Some(self.clone()) }))
}
pub fn give_expr(&self, expr: Expr) {
match self.0.borrow_mut().entry(expr.id()) {
match self.0.exprs.borrow_mut().entry(expr.id()) {
Entry::Occupied(mut oe) => oe.get_mut().0 += 1,
Entry::Vacant(v) => {
v.insert((1, expr));
@@ -19,16 +28,17 @@ impl ExprStore {
}
}
pub fn take_expr(&self, ticket: api::ExprTicket) {
(self.0.borrow_mut().entry(ticket))
(self.0.exprs.borrow_mut().entry(ticket))
.and_replace_entry_with(|_, (rc, rt)| (1 < rc).then_some((rc - 1, rt)));
}
pub fn get_expr(&self, ticket: api::ExprTicket) -> Option<Expr> {
self.0.borrow().get(&ticket).map(|(_, expr)| expr.clone())
(self.0.exprs.borrow().get(&ticket).map(|(_, expr)| expr.clone()))
.or_else(|| self.0.parent.as_ref()?.get_expr(ticket))
}
}
impl fmt::Display for ExprStore {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let r = self.0.borrow();
let r = self.0.exprs.borrow();
let rc: u32 = r.values().map(|v| v.0).sum();
write!(f, "Store holding {rc} refs to {} exprs", r.len())
}

View File

@@ -13,7 +13,7 @@ use futures::future::{join, join_all};
use futures::{FutureExt, StreamExt, stream, stream_select};
use hashbrown::HashMap;
use itertools::Itertools;
use orchid_api::HostMsgSet;
use orchid_api::{HostMsgSet, LsModule};
use orchid_api_traits::Request;
use orchid_base::builtin::ExtInit;
use orchid_base::clone;
@@ -45,7 +45,6 @@ pub struct ExtensionData {
exprs: ExprStore,
exiting_snd: Sender<()>,
lex_recur: Mutex<HashMap<api::ParsId, channel::Sender<ReqPair<api::SubLex>>>>,
mac_recur: Mutex<HashMap<api::ParsId, channel::Sender<ReqPair<api::RunMacros>>>>,
}
impl Drop for ExtensionData {
fn drop(&mut self) {
@@ -79,7 +78,7 @@ impl Extension {
})));
ExtensionData {
exiting_snd,
exprs: ExprStore::default(),
exprs: ctx.common_exprs.derive(),
ctx: ctx.clone(),
systems: (init.systems.iter().cloned())
.map(|decl| SystemCtor { decl, ext: WeakExtension(weak.clone()) })
@@ -88,7 +87,6 @@ impl Extension {
init: init.clone(),
next_pars: RefCell::new(NonZeroU64::new(1).unwrap()),
lex_recur: Mutex::default(),
mac_recur: Mutex::default(),
reqnot: ReqNot::new(
msg_logger,
move |sfn, _| clone!(init; Box::pin(async move { init.send(sfn).await })),
@@ -169,12 +167,8 @@ impl Extension {
})
.await
},
api::ExtHostReq::RunMacros(rm) => {
let (rep_in, rep_out) = channel::bounded(1);
let lex_g = this.0.mac_recur.lock().await;
let req_in = lex_g.get(&rm.run_id).expect("Sublex for nonexistent lexid");
req_in.send(ReqPair(rm.clone(), rep_in)).await.unwrap();
hand.handle(&rm, &rep_out.recv().await.unwrap()).await
api::ExtHostReq::LsModule(ref ls @ LsModule(ref sys, ref path)) => {
todo!() // TODO
},
api::ExtHostReq::ExtAtomPrint(ref eap @ api::ExtAtomPrint(ref atom)) => {
let atom = AtomHand::new(atom.clone(), &ctx).await;

View File

@@ -1,30 +1,25 @@
use std::num::NonZeroU64;
use std::sync::Arc;
use async_std::sync::Mutex;
use futures::FutureExt;
use hashbrown::HashMap;
use orchid_base::error::{OrcErrv, OrcRes, mk_errv};
use orchid_base::interner::Tok;
use orchid_base::location::Pos;
use orchid_base::match_mapping;
use orchid_base::name::Sym;
use orchid_base::number::{num_to_err, parse_num};
use orchid_base::parse::{name_char, name_start, op_char, unrep_space};
use orchid_base::tokens::PARENS;
use orchid_base::tree::{AtomRepr, Ph};
use orchid_base::tree::recur;
use crate::api;
use crate::atom::AtomHand;
use crate::ctx::Ctx;
use crate::expr::{Expr, ExprParseCtx};
use crate::parsed::{ParsTok, ParsTokTree};
use crate::system::System;
use crate::tree::{ParsTok, ParsTokTree};
pub struct LexCtx<'a> {
pub systems: &'a [System],
pub source: &'a Tok<String>,
pub tail: &'a str,
pub sub_trees: &'a mut HashMap<api::TreeTicket, ParsTokTree>,
pub sub_trees: &'a mut Vec<Expr>,
pub ctx: &'a Ctx,
}
impl<'a> LexCtx<'a> {
@@ -50,13 +45,24 @@ impl<'a> LexCtx<'a> {
}
false
}
pub fn add_subtree(&mut self, subtree: ParsTokTree) -> api::TreeTicket {
let next_idx = api::TreeTicket(NonZeroU64::new(self.sub_trees.len() as u64 + 1).unwrap());
self.sub_trees.insert(next_idx, subtree);
next_idx
pub async fn ser_subtree(&mut self, subtree: ParsTokTree) -> api::TokenTree {
let mut exprs = self.ctx.common_exprs.clone();
let foo = recur(subtree, &|tt, r| {
if let ParsTok::NewExpr(expr) = tt.tok {
return ParsTok::Handle(expr).at(tt.range);
}
pub fn rm_subtree(&mut self, ticket: api::TreeTicket) -> ParsTokTree {
self.sub_trees.remove(&ticket).unwrap()
r(tt)
});
foo.into_api(&mut exprs, &mut ()).await
}
pub async fn des_subtree(&mut self, tree: &api::TokenTree) -> ParsTokTree {
ParsTokTree::from_api(
&tree,
&mut self.ctx.common_exprs.clone(),
&mut ExprParseCtx { ctx: self.ctx.clone(), exprs: self.ctx.common_exprs.clone() },
&self.ctx.i,
)
.await
}
pub fn strip_char(&mut self, tgt: char) -> bool {
if let Some(src) = self.tail.strip_prefix(tgt) {
@@ -86,8 +92,12 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
);
let tok = if ctx.strip_prefix("\r\n") || ctx.strip_prefix("\r") || ctx.strip_prefix("\n") {
ParsTok::BR
} else if ctx.strip_prefix("::") {
ParsTok::NS
} 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()];
let body = lex_once(ctx).boxed_local().await?;
ParsTok::NS(ctx.ctx.i.i(name).await, Box::new(body))
} else if ctx.strip_prefix("--[") {
let Some((cmt, tail)) = ctx.tail.split_once("]--") else {
return Err(mk_errv(
@@ -132,20 +142,6 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
ctx.trim_ws();
}
ParsTok::S(*paren, body)
} else if ctx.strip_prefix("macro")
&& !ctx.tail.chars().next().is_some_and(|x| x.is_ascii_alphabetic())
{
ctx.strip_prefix("macro");
if ctx.strip_char('(') {
let pos = ctx.get_pos();
let numstr = ctx.get_start_matches(|x| x != ')').trim();
match parse_num(numstr) {
Ok(num) => ParsTok::Macro(Some(num.to_f64())),
Err(e) => return Err(num_to_err(e, pos, &ctx.ctx.i).await.into()),
}
} else {
ParsTok::Macro(None)
}
} else {
for sys in ctx.systems {
let mut errors = Vec::new();
@@ -157,7 +153,7 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
.lex(source, pos, |pos| 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.range.end, ticket: ctx_g.add_subtree(t) }),
Ok(t) => Some(api::SubLexed { pos: t.range.end, tree: ctx_g.ser_subtree(t).await }),
Err(e) => {
errors_lck.lock().await.push(e);
None
@@ -172,7 +168,7 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
),
Ok(Some(lexed)) => {
ctx.set_pos(lexed.pos);
return Ok(tt_to_owned(&lexed.expr, ctx).await);
return Ok(ctx.des_subtree(&lexed.expr).await);
},
Ok(None) => match errors.into_iter().reduce(|a, b| a + b) {
Some(errors) => return Err(errors),
@@ -196,39 +192,8 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
Ok(ParsTokTree { tok, range: start..ctx.get_pos() })
}
async fn tt_to_owned(api: &api::TokenTree, ctx: &mut LexCtx<'_>) -> ParsTokTree {
let tok = match_mapping!(&api.token, api::Token => ParsTok {
Atom(atom =>
AtomHand::from_api(atom, Pos::Range(api.range.clone()), &mut ctx.ctx.clone()).await
),
Bottom(err => OrcErrv::from_api(err, &ctx.ctx.i).await),
LambdaHead(arg => ttv_to_owned(arg, ctx).boxed_local().await),
Name(name => Tok::from_api(*name, &ctx.ctx.i).await),
Reference(tstrv => Sym::from_api(*tstrv, &ctx.ctx.i).await),
S(p.clone(), b => ttv_to_owned(b, ctx).boxed_local().await),
BR, NS,
Comment(c.clone()),
Ph(ph => Ph::from_api(ph, &ctx.ctx.i).await),
Macro(*prio),
} {
api::Token::Slot(id) => return ctx.rm_subtree(*id),
});
ParsTokTree { range: api.range.clone(), tok }
}
async fn ttv_to_owned<'a>(
api: impl IntoIterator<Item = &'a api::TokenTree>,
ctx: &mut LexCtx<'_>,
) -> Vec<ParsTokTree> {
let mut out = Vec::new();
for tt in api {
out.push(tt_to_owned(tt, ctx).await)
}
out
}
pub async fn lex(text: Tok<String>, systems: &[System], ctx: &Ctx) -> OrcRes<Vec<ParsTokTree>> {
let mut sub_trees = HashMap::new();
let mut sub_trees = Vec::new();
let mut ctx = LexCtx { source: &text, sub_trees: &mut sub_trees, tail: &text[..], systems, ctx };
let mut tokv = Vec::new();
ctx.trim(unrep_space);

View File

@@ -8,9 +8,8 @@ pub mod expr;
pub mod expr_store;
pub mod extension;
pub mod lex;
pub mod macros;
pub mod parse;
pub mod rule;
pub mod parsed;
pub mod subprocess;
pub mod system;
pub mod tree;

View File

@@ -1,102 +0,0 @@
use std::rc::Rc;
use futures::FutureExt;
use hashbrown::{HashMap, HashSet};
use itertools::Itertools;
use orchid_base::clone;
use orchid_base::macros::{MTok, MTree, mtreev_from_api, mtreev_to_api};
use orchid_base::name::Sym;
use trait_set::trait_set;
use crate::api;
use crate::atom::AtomHand;
use crate::ctx::Ctx;
use crate::rule::state::MatchState;
use crate::tree::Code;
pub type MacTok = MTok<'static, AtomHand>;
pub type MacTree = MTree<'static, AtomHand>;
trait_set! {
trait MacroCB = Fn(Vec<MacTree>) -> Option<Vec<MacTree>>;
}
type Slots = HashMap<api::MacroTreeId, Rc<MacTok>>;
pub async fn macro_treev_to_api(mtree: Vec<MacTree>, slots: &mut Slots) -> Vec<api::MacroTree> {
mtreev_to_api(&mtree, &mut |a: &AtomHand| {
let id = api::MacroTreeId((slots.len() as u64 + 1).try_into().unwrap());
slots.insert(id, Rc::new(MacTok::Atom(a.clone())));
async move { api::MacroToken::Slot(id) }.boxed_local()
})
.await
}
pub async fn macro_treev_from_api(api: Vec<api::MacroTree>, ctx: Ctx) -> Vec<MacTree> {
mtreev_from_api(&api, &ctx.clone().i, &mut async move |atom| {
MacTok::Atom(AtomHand::new(atom.clone(), &ctx).await)
})
.await
}
pub fn deslot_macro(tree: &[MacTree], slots: &mut Slots) -> Option<Vec<MacTree>> {
return work(slots, tree);
fn work(
slots: &mut HashMap<api::MacroTreeId, Rc<MacTok>>,
tree: &[MacTree],
) -> Option<Vec<MacTree>> {
let items = (tree.iter())
.map(|t| {
Some(MacTree {
tok: match &*t.tok {
MacTok::Atom(_) | MacTok::Name(_) | MacTok::Ph(_) => return None,
MacTok::Ref(_) => panic!("Ref is an extension-local optimization"),
MacTok::Done(_) => panic!("Created and removed by matcher"),
MacTok::Slot(slot) => slots.get(&slot.id()).expect("Slot not found").clone(),
MacTok::S(paren, b) => Rc::new(MacTok::S(*paren, work(slots, b)?)),
MacTok::Lambda(a, b) => Rc::new(match (work(slots, a), work(slots, b)) {
(None, None) => return None,
(Some(a), None) => MacTok::Lambda(a, b.clone()),
(None, Some(b)) => MacTok::Lambda(a.clone(), b),
(Some(a), Some(b)) => MacTok::Lambda(a, b),
}),
},
pos: t.pos.clone(),
})
})
.collect_vec();
let any_changed = items.iter().any(Option::is_some);
any_changed.then(|| {
(items.into_iter().enumerate())
.map(|(i, opt)| opt.unwrap_or_else(|| tree[i].clone()))
.collect_vec()
})
}
}
pub struct Macro<Matcher> {
deps: HashSet<Sym>,
cases: Vec<(Matcher, Code)>,
}
fn fill_lexicon(tgt: &MacTree, lexicon: &mut HashSet<Sym>) {
match &*tgt.tok {
MTok::Name(n) => {
lexicon.insert(n.clone());
},
MTok::Lambda(arg, body) => {
arg.iter().for_each(|t| fill_lexicon(t, lexicon));
body.iter().for_each(|t| fill_lexicon(t, lexicon))
},
MTok::S(_, body) => body.iter().for_each(|t| fill_lexicon(t, lexicon)),
_ => (),
}
}
fn run_body(body: &Code, mut state: MatchState<'_>) -> Vec<MacTree> {
let inject: Vec<MacTree> = todo!("Call the interpreter with bindings");
inject
.into_iter()
.map(|MTree { pos, tok }| MTree { pos, tok: Rc::new(MTok::Done(tok)) })
.collect_vec()
}

View File

@@ -1,103 +1,119 @@
use std::rc::Rc;
use std::cell::RefCell;
use futures::FutureExt;
use futures::future::join_all;
use hashbrown::HashMap;
use itertools::Itertools;
use never::Never;
use orchid_base::error::{OrcErrv, OrcRes, Reporter, ReporterImpl, mk_err, mk_errv};
use orchid_base::interner::Tok;
use orchid_base::error::{OrcRes, Reporter, mk_err, mk_errv};
use orchid_base::format::fmt;
use orchid_base::interner::{Interner, Tok};
use orchid_base::location::Pos;
use orchid_base::macros::{MTok, MTree};
use orchid_base::name::Sym;
use orchid_base::parse::{
Comment, Import, Parsed, Snippet, expect_end, line_items, parse_multiname, try_pop_no_fluff,
Comment, Import, ParseCtx, Parsed, Snippet, expect_end, line_items, parse_multiname,
try_pop_no_fluff,
};
use orchid_base::tree::{Paren, TokTree, Token};
use substack::Substack;
use crate::atom::AtomHand;
use crate::macros::MacTree;
use crate::ctx::Ctx;
use crate::expr::{Expr, ExprKind, PathSetBuilder};
use crate::parsed::{Item, ItemKind, ParsTokTree, ParsedMember, ParsedMemberKind, ParsedModule};
use crate::system::System;
use crate::tree::{Code, CodeLocator, Item, ItemKind, Member, MemberKind, Module, Rule, RuleKind};
type ParsSnippet<'a> = Snippet<'a, 'static, AtomHand, Never>;
type ParsSnippet<'a> = Snippet<'a, Expr, Expr>;
pub struct ParseCtxImpl<'a> {
pub struct HostParseCtxImpl<'a> {
pub ctx: Ctx,
pub systems: &'a [System],
pub reporter: &'a ReporterImpl,
pub reporter: &'a Reporter,
pub interner: &'a Interner,
pub consts: RefCell<HashMap<Sym, Vec<ParsTokTree>>>,
}
impl ParseCtx for ParseCtxImpl<'_> {
fn reporter(&self) -> &(impl Reporter + ?Sized) { self.reporter }
impl ParseCtx for HostParseCtxImpl<'_> {
fn reporter(&self) -> &Reporter { self.reporter }
fn i(&self) -> &Interner { self.interner }
}
impl HostParseCtx for HostParseCtxImpl<'_> {
fn ctx(&self) -> &Ctx { &self.ctx }
fn systems(&self) -> impl Iterator<Item = &System> { self.systems.iter() }
async fn save_const(&self, path: Substack<'_, Tok<String>>, value: Vec<ParsTokTree>) {
let name = Sym::new(path.unreverse(), self.interner).await.unwrap();
self.consts.borrow_mut().insert(name, value);
}
}
pub trait ParseCtx {
pub trait HostParseCtx: ParseCtx {
fn ctx(&self) -> &Ctx;
fn systems(&self) -> impl Iterator<Item = &System>;
fn reporter(&self) -> &(impl Reporter + ?Sized);
async fn save_const(&self, path: Substack<'_, Tok<String>>, value: Vec<ParsTokTree>);
}
pub async fn parse_items(
ctx: &impl ParseCtx,
ctx: &impl HostParseCtx,
path: Substack<'_, Tok<String>>,
items: ParsSnippet<'_>,
) -> OrcRes<Vec<Item>> {
let lines = line_items(items).await;
let lines = line_items(ctx, items).await;
let line_res =
join_all(lines.into_iter().map(|p| parse_item(ctx, path.clone(), p.output, p.tail))).await;
Ok(line_res.into_iter().flat_map(|l| l.ok().into_iter().flatten()).collect())
}
pub async fn parse_item(
ctx: &impl ParseCtx,
ctx: &impl HostParseCtx,
path: Substack<'_, Tok<String>>,
comments: Vec<Comment>,
item: ParsSnippet<'_>,
) -> OrcRes<Vec<Item>> {
match item.pop_front() {
Some((TokTree { tok: Token::Name(n), .. }, postdisc)) => match n {
n if *n == item.i().i("export").await => match try_pop_no_fluff(postdisc).await? {
n if *n == ctx.i().i("export").await => match try_pop_no_fluff(ctx, postdisc).await? {
Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } =>
parse_exportable_item(ctx, path, comments, true, n.clone(), tail).await,
Parsed { output: TokTree { tok: Token::NS, .. }, tail } => {
let Parsed { output: exports, tail } = parse_multiname(ctx.reporter(), tail).await?;
Parsed { output: TokTree { tok: Token::S(Paren::Round, body), .. }, tail } => {
expect_end(ctx, tail).await?;
let mut ok = Vec::new();
for (e, pos) in exports {
match (&e.path[..], e.name) {
([], Some(n)) =>
ok.push(Item { comments: comments.clone(), pos, kind: ItemKind::Export(n) }),
(_, Some(_)) => ctx.reporter().report(mk_err(
tail.i().i("Compound export").await,
for tt in body {
let pos = Pos::Range(tt.range.clone());
match &tt.tok {
Token::Name(n) =>
ok.push(Item { comments: comments.clone(), pos, kind: ItemKind::Export(n.clone()) }),
Token::NS(..) => ctx.reporter().report(mk_err(
ctx.i().i("Compound export").await,
"Cannot export compound names (names containing the :: separator)",
[pos.into()],
)),
(_, None) => ctx.reporter().report(mk_err(
tail.i().i("Wildcard export").await,
"Exports cannot contain the globstar *",
t => ctx.reporter().report(mk_err(
ctx.i().i("Invalid export").await,
format!("Invalid export target {}", fmt(t, ctx.i()).await),
[pos.into()],
)),
}
}
expect_end(tail).await?;
expect_end(ctx, tail).await?;
Ok(ok)
},
Parsed { output, tail } => Err(mk_errv(
tail.i().i("Malformed export").await,
"`export` can either prefix other lines or list names inside ::( ) or ::[ ]",
Parsed { output, tail: _ } => Err(mk_errv(
ctx.i().i("Malformed export").await,
"`export` can either prefix other lines or list names inside ( )",
[Pos::Range(output.range.clone()).into()],
)),
},
n if *n == item.i().i("import").await => parse_import(ctx, postdisc).await.map(|v| {
Vec::from_iter(v.into_iter().map(|(t, pos)| Item {
n if *n == ctx.i().i("import").await => {
let imports = parse_import(ctx, postdisc).await?;
Ok(Vec::from_iter(imports.into_iter().map(|(t, pos)| Item {
comments: comments.clone(),
pos,
kind: ItemKind::Import(t),
}))
}),
})))
},
n => parse_exportable_item(ctx, path, comments, false, n.clone(), postdisc).await,
},
Some(_) => Err(mk_errv(
item.i().i("Expected a line type").await,
ctx.i().i("Expected a line type").await,
"All lines must begin with a keyword",
[Pos::Range(item.pos()).into()],
)),
@@ -105,37 +121,37 @@ pub async fn parse_item(
}
}
pub async fn parse_import(
ctx: &impl ParseCtx,
tail: ParsSnippet<'_>,
pub async fn parse_import<'a>(
ctx: &impl HostParseCtx,
tail: ParsSnippet<'a>,
) -> OrcRes<Vec<(Import, Pos)>> {
let Parsed { output: imports, tail } = parse_multiname(ctx.reporter(), tail).await?;
expect_end(tail).await?;
let Parsed { output: imports, tail } = parse_multiname(ctx, tail).await?;
expect_end(ctx, tail).await?;
Ok(imports)
}
pub async fn parse_exportable_item(
ctx: &impl ParseCtx,
pub async fn parse_exportable_item<'a>(
ctx: &impl HostParseCtx,
path: Substack<'_, Tok<String>>,
comments: Vec<Comment>,
exported: bool,
discr: Tok<String>,
tail: ParsSnippet<'_>,
tail: ParsSnippet<'a>,
) -> OrcRes<Vec<Item>> {
let kind = if discr == tail.i().i("mod").await {
let path_sym = Sym::new(path.unreverse(), ctx.i()).await.expect("Files should have a namespace");
let kind = if discr == ctx.i().i("mod").await {
let (name, body) = parse_module(ctx, path, tail).await?;
ItemKind::Member(Member::new(name, MemberKind::Mod(body)))
} else if discr == tail.i().i("const").await {
let (name, val) = parse_const(tail, path.clone()).await?;
let locator = CodeLocator::to_const(tail.i().i(&path.push(name.clone()).unreverse()).await);
ItemKind::Member(Member::new(name, MemberKind::Const(Code::from_code(locator, val))))
ItemKind::Member(ParsedMember::new(name, path_sym, ParsedMemberKind::Mod(body)))
} else if discr == ctx.i().i("const").await {
let name = parse_const(ctx, tail, path.clone()).await?;
ItemKind::Member(ParsedMember::new(name, path_sym, ParsedMemberKind::Const))
} else if let Some(sys) = ctx.systems().find(|s| s.can_parse(discr.clone())) {
let line = sys.parse(tail.to_vec(), exported, comments).await?;
return parse_items(ctx, path, Snippet::new(tail.prev(), &line, tail.i())).await;
let line = sys.parse(path_sym, tail.to_vec(), exported, comments).await?;
return parse_items(ctx, path, Snippet::new(tail.prev(), &line)).await;
} else {
let ext_lines = ctx.systems().flat_map(System::line_types).join(", ");
return Err(mk_errv(
tail.i().i("Unrecognized line type").await,
ctx.i().i("Unrecognized line type").await,
format!("Line types are: const, mod, macro, grammar, {ext_lines}"),
[Pos::Range(tail.prev().range.clone()).into()],
));
@@ -143,175 +159,115 @@ pub async fn parse_exportable_item(
Ok(vec![Item { comments, pos: Pos::Range(tail.pos()), kind }])
}
pub async fn parse_module(
ctx: &impl ParseCtx,
pub async fn parse_module<'a>(
ctx: &impl HostParseCtx,
path: Substack<'_, Tok<String>>,
tail: ParsSnippet<'_>,
) -> OrcRes<(Tok<String>, Module)> {
let (name, tail) = match try_pop_no_fluff(tail).await? {
tail: ParsSnippet<'a>,
) -> OrcRes<(Tok<String>, ParsedModule)> {
let (name, tail) = match try_pop_no_fluff(ctx, tail).await? {
Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } => (n.clone(), tail),
Parsed { output, .. } => {
return Err(mk_errv(
tail.i().i("Missing module name").await,
format!("A name was expected, {} was found", tail.fmt(output).await),
ctx.i().i("Missing module name").await,
format!("A name was expected, {} was found", fmt(output, ctx.i()).await),
[Pos::Range(output.range.clone()).into()],
));
},
};
let Parsed { output, tail: surplus } = try_pop_no_fluff(tail).await?;
expect_end(surplus).await?;
let Some(body) = output.as_s(Paren::Round, tail.i()) else {
let Parsed { output, tail: surplus } = try_pop_no_fluff(ctx, tail).await?;
expect_end(ctx, surplus).await?;
let Some(body) = output.as_s(Paren::Round) else {
return Err(mk_errv(
tail.i().i("Expected module body").await,
format!("A ( block ) was expected, {} was found", tail.fmt(output).await),
ctx.i().i("Expected module body").await,
format!("A ( block ) was expected, {} was found", fmt(output, ctx.i()).await),
[Pos::Range(output.range.clone()).into()],
));
};
let path = path.push(name.clone());
Ok((name, Module::new(parse_items(ctx, path, body).await?)))
Ok((name, ParsedModule::new(parse_items(ctx, path, body).await?)))
}
pub async fn parse_const(
tail: ParsSnippet<'_>,
pub async fn parse_const<'a>(
ctx: &impl HostParseCtx,
tail: ParsSnippet<'a>,
path: Substack<'_, Tok<String>>,
) -> OrcRes<(Tok<String>, Vec<MacTree>)> {
let Parsed { output, tail } = try_pop_no_fluff(tail).await?;
) -> OrcRes<Tok<String>> {
let Parsed { output, tail } = try_pop_no_fluff(ctx, tail).await?;
let Some(name) = output.as_name() else {
return Err(mk_errv(
tail.i().i("Missing module name").await,
format!("A name was expected, {} was found", tail.fmt(output).await),
ctx.i().i("Missing module name").await,
format!("A name was expected, {} was found", fmt(output, ctx.i()).await),
[Pos::Range(output.range.clone()).into()],
));
};
let Parsed { output, tail } = try_pop_no_fluff(tail).await?;
if !output.is_kw(tail.i().i("=").await) {
let Parsed { output, tail } = try_pop_no_fluff(ctx, tail).await?;
if !output.is_kw(ctx.i().i("=").await) {
return Err(mk_errv(
tail.i().i("Missing = separator").await,
format!("Expected = , found {}", tail.fmt(output).await),
ctx.i().i("Missing = separator").await,
format!("Expected = , found {}", fmt(output, ctx.i()).await),
[Pos::Range(output.range.clone()).into()],
));
}
try_pop_no_fluff(tail).await?;
Ok((name, parse_mtree(tail, path).await?))
try_pop_no_fluff(ctx, tail).await?;
ctx.save_const(path, tail[..].to_vec()).await;
Ok(name)
}
pub async fn parse_mtree(
mut snip: ParsSnippet<'_>,
path: Substack<'_, Tok<String>>,
) -> OrcRes<Vec<MacTree>> {
let mut mtreev = Vec::new();
while let Some((ttree, tail)) = snip.pop_front() {
snip = tail;
let (range, tok, tail) = match &ttree.tok {
Token::S(p, b) => {
let b = parse_mtree(Snippet::new(ttree, b, snip.i()), path.clone()).boxed_local().await?;
(ttree.range.clone(), MTok::S(*p, b), tail)
},
Token::Reference(name) => (ttree.range.clone(), MTok::Name(name.clone()), tail),
Token::Name(tok) => {
let mut segments = path.unreverse();
segments.push(tok.clone());
let mut end = ttree.range.end;
while let Some((TokTree { tok: Token::NS, .. }, tail)) = snip.pop_front() {
let Parsed { output, tail } = try_pop_no_fluff(tail).await?;
let Some(seg) = output.as_name() else {
return Err(mk_errv(
tail.i().i("Namespaced name interrupted").await,
"In expression context, :: must always be followed by a name.\n\
::() is permitted only in import and export items",
[Pos::Range(output.range.clone()).into()],
));
};
segments.push(seg);
snip = tail;
end = output.range.end;
}
(ttree.range.start..end, MTok::Name(Sym::new(segments, snip.i()).await.unwrap()), snip)
},
Token::NS => {
return Err(mk_errv(
tail.i().i("Unexpected :: in expression").await,
":: can only follow a name",
[Pos::Range(ttree.range.clone()).into()],
));
},
Token::Ph(ph) => (ttree.range.clone(), MTok::Ph(ph.clone()), tail),
Token::Macro(_) => {
return Err(mk_errv(
tail.i().i("Invalid keyword in expression").await,
"Expressions cannot use `macro` as a name.",
[Pos::Range(ttree.range.clone()).into()],
));
},
Token::Atom(a) => (ttree.range.clone(), MTok::Atom(a.clone()), tail),
Token::BR | Token::Comment(_) => continue,
Token::Bottom(e) => return Err(e.clone()),
Token::LambdaHead(arg) => (
ttree.range.start..snip.pos().end,
MTok::Lambda(
parse_mtree(Snippet::new(ttree, arg, snip.i()), path.clone()).boxed_local().await?,
parse_mtree(tail, path.clone()).boxed_local().await?,
),
Snippet::new(ttree, &[], snip.i()),
),
Token::Slot(_) | Token::X(_) =>
panic!("Did not expect {} in parsed token tree", tail.fmt(ttree).await),
};
mtreev.push(MTree { pos: Pos::Range(range.clone()), tok: Rc::new(tok) });
snip = tail;
}
Ok(mtreev)
}
pub async fn parse_macro(
pub async fn parse_expr(
ctx: &impl HostParseCtx,
path: Sym,
psb: PathSetBuilder<'_, Tok<String>>,
tail: ParsSnippet<'_>,
macro_i: u16,
path: Substack<'_, Tok<String>>,
) -> OrcRes<Vec<Rule>> {
let (surplus, prev, block) = match try_pop_no_fluff(tail).await? {
Parsed { tail, output: o @ TokTree { tok: Token::S(Paren::Round, b), .. } } => (tail, o, b),
Parsed { output, .. } => {
return Err(mk_errv(
tail.i().i("m").await,
"Macro blocks must either start with a block or a ..$:number",
[Pos::Range(output.range.clone()).into()],
));
},
) -> OrcRes<Expr> {
let Some((last_idx, _)) = (tail.iter().enumerate().find(|(_, tt)| tt.as_lambda().is_some()))
.or_else(|| tail.iter().enumerate().rev().find(|(_, tt)| !tt.is_fluff()))
else {
return Err(mk_errv(ctx.i().i("Empty expression").await, "Expression ends abruptly here", [
Pos::Range(tail.pos()).into(),
]));
};
expect_end(surplus).await?;
let mut errors = Vec::new();
let mut rules = Vec::new();
for (i, item) in line_items(Snippet::new(prev, block, tail.i())).await.into_iter().enumerate() {
let Parsed { tail, output } = try_pop_no_fluff(item.tail).await?;
if !output.is_kw(tail.i().i("rule").await) {
errors.extend(mk_errv(
tail.i().i("non-rule in macro").await,
format!("Expected `rule`, got {}", tail.fmt(output).await),
[Pos::Range(output.range.clone()).into()],
));
continue;
};
let arrow = tail.i().i("=>").await;
let (pat, body) = match tail.split_once(|t| t.is_kw(arrow.clone())) {
Some((a, b)) => (a, b),
None => {
errors.extend(mk_errv(
tail.i().i("no => in macro rule").await,
"The pattern and body of a rule must be separated by a =>",
[Pos::Range(tail.pos()).into()],
));
continue;
},
};
rules.push(Rule {
comments: item.output,
pos: Pos::Range(tail.pos()),
pattern: parse_mtree(pat, path.clone()).await?,
kind: RuleKind::Native(Code::from_code(
CodeLocator::to_rule(tail.i().i(&path.unreverse()).await, macro_i, i as u16),
parse_mtree(body, path.clone()).await?,
)),
})
let (function, value) = tail.split_at(last_idx as u32);
let pos = Pos::Range(tail.pos());
if !function.iter().all(TokTree::is_fluff) {
let (f_psb, x_psb) = psb.split();
let x_expr = parse_expr(ctx, path.clone(), x_psb, value).boxed_local().await?;
let f_expr = parse_expr(ctx, path, f_psb, function).boxed_local().await?;
return Ok(ExprKind::Call(f_expr, x_expr).at(pos));
}
let Parsed { output: head, tail } = try_pop_no_fluff(ctx, value).await?;
match &head.tok {
Token::BR | Token::Comment(_) => panic!("Fluff skipped"),
Token::Bottom(b) => Ok(ExprKind::Bottom(b.clone()).at(pos.clone())),
Token::Handle(expr) => Ok(expr.clone()),
Token::NS(n, nametail) => {
let mut nametail = nametail;
let mut segments = path.iter().chain([n]).cloned().collect_vec();
while let Token::NS(n, newtail) = &nametail.tok {
segments.push(n.clone());
nametail = newtail;
}
let Token::Name(n) = &nametail.tok else {
return Err(mk_errv(
ctx.i().i("Loose namespace prefix in constant").await,
"Namespace prefixes in constants must be followed by names",
[pos.into()],
));
};
segments.push(n.clone());
Ok(ExprKind::Const(Sym::new(segments, ctx.i()).await.unwrap()).at(pos.clone()))
},
Token::LambdaHead(h) => {
let [TokTree { tok: Token::Name(arg), .. }] = &h[..] else {
return Err(mk_errv(
ctx.i().i("Complex lambda binding in constant").await,
"Lambda args in constants must be identified by a single name",
[pos.into()],
));
};
let lambda_builder = psb.lambda(arg);
let body = parse_expr(ctx, path.clone(), lambda_builder.stack(), tail).boxed_local().await?;
Ok(ExprKind::Lambda(lambda_builder.collect(), body).at(pos.clone()))
},
_ => todo!("AAAAAA"), // TODO: todo
}
if let Ok(e) = OrcErrv::new(errors) { Err(e) } else { Ok(rules) }
}

383
orchid-host/src/parsed.rs Normal file
View File

@@ -0,0 +1,383 @@
use std::cell::RefCell;
use std::fmt::Debug;
use std::rc::Rc;
use async_once_cell::OnceCell;
use async_std::sync::{Mutex, RwLock};
use async_stream::stream;
use futures::future::join_all;
use futures::{FutureExt, StreamExt};
use hashbrown::HashMap;
use itertools::Itertools;
use orchid_base::error::{OrcRes, mk_errv};
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
use orchid_base::interner::Tok;
use orchid_base::location::Pos;
use orchid_base::name::{NameLike, Sym};
use orchid_base::parse::{Comment, Import};
use orchid_base::tl_cache;
use orchid_base::tree::{TokTree, Token, TokenVariant};
use crate::api;
use crate::ctx::Ctx;
use crate::expr::{Expr, ExprParseCtx, PathSetBuilder};
use crate::expr_store::ExprStore;
use crate::system::System;
pub type ParsTokTree = TokTree<Expr, Expr>;
pub type ParsTok = Token<Expr, Expr>;
impl TokenVariant<api::ExprTicket> for Expr {
type ToApiCtx<'a> = ExprStore;
async fn into_api(self, ctx: &mut Self::ToApiCtx<'_>) -> api::ExprTicket {
ctx.give_expr(self.clone());
self.id()
}
type FromApiCtx<'a> = ExprStore;
async fn from_api(
api: &api::ExprTicket,
ctx: &mut Self::FromApiCtx<'_>,
_: Pos,
_: &orchid_base::interner::Interner,
) -> Self {
let expr = ctx.get_expr(*api).expect("Dangling expr");
ctx.take_expr(*api);
expr
}
}
impl TokenVariant<api::Expression> for Expr {
type FromApiCtx<'a> = ExprParseCtx;
async fn from_api(
api: &api::Expression,
ctx: &mut Self::FromApiCtx<'_>,
_: Pos,
_: &orchid_base::interner::Interner,
) -> Self {
Expr::from_api(api, PathSetBuilder::new(), ctx).await
}
type ToApiCtx<'a> = ();
async fn into_api(self, (): &mut Self::ToApiCtx<'_>) -> api::Expression {
panic!("Failed to replace NewExpr before returning sublexer value")
}
}
pub struct ParsedFromApiCx<'a> {
pub sys: &'a System,
pub consts: &'a mut HashMap<Sym, Expr>,
pub path: Tok<Vec<Tok<String>>>,
}
impl<'a> ParsedFromApiCx<'a> {
pub async fn push<'c>(&'c mut self, name: Tok<String>) -> ParsedFromApiCx<'c> {
let path = self.sys.ctx().i.i(&self.path.iter().cloned().chain([name]).collect_vec()).await;
ParsedFromApiCx { path, consts: &mut *self.consts, sys: self.sys }
}
}
#[derive(Debug)]
pub struct Item {
pub pos: Pos,
pub comments: Vec<Comment>,
pub kind: ItemKind,
}
#[derive(Debug)]
pub enum ItemKind {
Member(ParsedMember),
Export(Tok<String>),
Import(Import),
}
impl ItemKind {
pub fn at(self, pos: Pos) -> Item { Item { comments: vec![], pos, kind: self } }
}
impl Item {
pub async fn from_api<'a>(tree: api::Item, ctx: &mut ParsedFromApiCx<'a>) -> Self {
let kind = match tree.kind {
api::ItemKind::Member(m) => ItemKind::Member(ParsedMember::from_api(m, ctx).await),
api::ItemKind::Import(name) => ItemKind::Import(Import {
path: Sym::from_api(name, &ctx.sys.ctx().i).await.iter().collect(),
name: None,
}),
api::ItemKind::Export(e) => ItemKind::Export(Tok::from_api(e, &ctx.sys.ctx().i).await),
};
let mut comments = Vec::new();
for comment in tree.comments.iter() {
comments.push(Comment::from_api(comment, &ctx.sys.ctx().i).await)
}
Self { pos: Pos::from_api(&tree.location, &ctx.sys.ctx().i).await, comments, kind }
}
}
impl Format for Item {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
let comment_text = self.comments.iter().join("\n");
let item_text = match &self.kind {
ItemKind::Import(i) => format!("import {i}").into(),
ItemKind::Export(e) => format!("export {e}").into(),
ItemKind::Member(mem) => match mem.kind.get() {
None => format!("lazy {}", mem.name).into(),
Some(ParsedMemberKind::Const) =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("const {0}")))
.units([mem.name.rc().into()]),
Some(ParsedMemberKind::Mod(module)) =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("module {0} {{\n\t{1}\n}}")))
.units([mem.name.rc().into(), module.print(c).boxed_local().await]),
},
};
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{0}\n{1}")))
.units([comment_text.into(), item_text])
}
}
pub struct ParsedMember {
name: Tok<String>,
full_name: Sym,
kind: OnceCell<ParsedMemberKind>,
lazy: Mutex<Option<LazyMemberHandle>>,
}
// TODO: this one should own but not execute the lazy handle.
// Lazy handles should run
// - in the tree converter function as needed to resolve imports
// - in the tree itself when a constant is loaded
// - when a different lazy subtree references them in a wildcard import and
// forces the enumeration.
//
// do we actually need to allow wildcard imports in lazy trees? maybe a
// different kind of import is sufficient. Source code never becomes a lazy
// tree. What does?
// - Systems subtrees rarely reference each other at all. They can't use macros
// and they usually point to constants with an embedded expr.
// - Compiled libraries on the long run. The code as written may reference
// constants by indirect path. But this is actually the same as the above,
// they also wouldn't use regular imports because they are distributed as
// exprs.
//
// Everything is distributed either as source code or as exprs. Line parsers
// also operate on tokens.
//
// TODO: The trees produced by systems can be safely changed
// to the new kind of tree. This datastructure does not need to support the lazy
// handle.
impl ParsedMember {
pub fn name(&self) -> Tok<String> { self.name.clone() }
pub async fn kind(&self, consts: &mut HashMap<Sym, Expr>) -> &ParsedMemberKind {
(self.kind.get_or_init(async {
let handle = self.lazy.lock().await.take().expect("Neither known nor lazy");
handle.run(consts).await
}))
.await
}
pub async fn kind_mut(&mut self, consts: &mut HashMap<Sym, Expr>) -> &mut ParsedMemberKind {
self.kind(consts).await;
self.kind.get_mut().expect("kind() already filled the cell")
}
pub async fn from_api<'a>(api: api::Member, ctx: &'_ mut ParsedFromApiCx<'a>) -> Self {
let name = Tok::from_api(api.name, &ctx.sys.ctx().i).await;
let mut ctx: ParsedFromApiCx<'_> = (&mut *ctx).push(name.clone()).await;
let path_sym = Sym::from_tok(ctx.path.clone()).expect("We just pushed on to this");
let kind = match api.kind {
api::MemberKind::Lazy(id) => {
let handle = LazyMemberHandle { id, sys: ctx.sys.clone(), path: path_sym.clone() };
return handle.into_member(name.clone()).await;
},
api::MemberKind::Const(c) => {
let mut pctx =
ExprParseCtx { ctx: ctx.sys.ctx().clone(), exprs: ctx.sys.ext().exprs().clone() };
let expr = Expr::from_api(&c, PathSetBuilder::new(), &mut pctx).await;
ctx.consts.insert(path_sym.clone(), expr);
ParsedMemberKind::Const
},
api::MemberKind::Module(m) =>
ParsedMemberKind::Mod(ParsedModule::from_api(m, &mut ctx).await),
};
ParsedMember { name, full_name: path_sym, kind: OnceCell::from(kind), lazy: Mutex::default() }
}
pub fn new(name: Tok<String>, full_name: Sym, kind: ParsedMemberKind) -> Self {
ParsedMember { name, full_name, kind: OnceCell::from(kind), lazy: Mutex::default() }
}
}
impl Debug for ParsedMember {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Member")
.field("name", &self.name)
.field("kind", &self.kind)
.finish_non_exhaustive()
}
}
#[derive(Debug)]
pub enum ParsedMemberKind {
Const,
Mod(ParsedModule),
}
#[derive(Debug, Default)]
pub struct ParsedModule {
pub imports: Vec<Sym>,
pub exports: Vec<Tok<String>>,
pub items: Vec<Item>,
}
impl ParsedModule {
pub fn new(items: impl IntoIterator<Item = Item>) -> Self {
let items = items.into_iter().collect_vec();
let exports = (items.iter())
.filter_map(|i| match &i.kind {
ItemKind::Export(e) => Some(e.clone()),
_ => None,
})
.collect_vec();
Self { imports: vec![], exports, items }
}
pub fn merge(&mut self, other: ParsedModule) {
let mut swap = ParsedModule::default();
std::mem::swap(self, &mut swap);
*self = ParsedModule::new(swap.items.into_iter().chain(other.items))
}
pub async fn from_api<'a>(m: api::Module, ctx: &mut ParsedFromApiCx<'a>) -> Self {
Self::new(
stream! { for item in m.items { yield Item::from_api(item, ctx).boxed_local().await } }
.collect::<Vec<_>>()
.await,
)
}
pub async fn walk<'a>(
&self,
allow_private: bool,
path: impl IntoIterator<Item = Tok<String>>,
consts: &mut HashMap<Sym, Expr>,
) -> Result<&ParsedModule, WalkError> {
let mut cur = self;
for (pos, step) in path.into_iter().enumerate() {
let Some(member) = (cur.items.iter())
.filter_map(|it| if let ItemKind::Member(m) = &it.kind { Some(m) } else { None })
.find(|m| m.name == step)
else {
return Err(WalkError { pos, kind: WalkErrorKind::Missing });
};
if !allow_private && !cur.exports.contains(&step) {
return Err(WalkError { pos, kind: WalkErrorKind::Private });
}
match member.kind(consts).await {
ParsedMemberKind::Const => return Err(WalkError { pos, kind: WalkErrorKind::Constant }),
ParsedMemberKind::Mod(m) => cur = m,
}
}
Ok(cur)
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum WalkErrorKind {
Missing,
Private,
Constant,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct WalkError {
pub pos: usize,
pub kind: WalkErrorKind,
}
impl Format for ParsedModule {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
let import_str = self.imports.iter().map(|i| format!("import {i}")).join("\n");
let head_str = format!("{import_str}\nexport ::({})\n", self.exports.iter().join(", "));
Variants::sequence(self.items.len() + 1, "\n", None).units(
[head_str.into()].into_iter().chain(join_all(self.items.iter().map(|i| i.print(c))).await),
)
}
}
pub struct LazyMemberHandle {
id: api::TreeId,
sys: System,
path: Sym,
}
impl LazyMemberHandle {
pub async fn run(self, consts: &mut HashMap<Sym, Expr>) -> ParsedMemberKind {
match self.sys.get_tree(self.id).await {
api::MemberKind::Const(c) => {
let mut pctx =
ExprParseCtx { ctx: self.sys.ctx().clone(), exprs: self.sys.ext().exprs().clone() };
consts.insert(self.path, Expr::from_api(&c, PathSetBuilder::new(), &mut pctx).await);
ParsedMemberKind::Const
},
api::MemberKind::Module(m) => ParsedMemberKind::Mod(
ParsedModule::from_api(m, &mut ParsedFromApiCx {
sys: &self.sys,
consts,
path: self.path.tok(),
})
.await,
),
api::MemberKind::Lazy(id) => Self { id, ..self }.run(consts).boxed_local().await,
}
}
pub async fn into_member(self, name: Tok<String>) -> ParsedMember {
ParsedMember {
name,
full_name: self.path.clone(),
kind: OnceCell::new(),
lazy: Mutex::new(Some(self)),
}
}
}
/// TODO:
///
/// idea, does the host need an IR here or can we figure out a way to transcribe
/// these? Should we spin off a new stage for value parsing so that ParsTokTree
/// doesn't appear in the interpreter's ingress?
pub struct Const {
pub source: Option<Vec<ParsTokTree>>,
}
/// Selects a code element
///
/// Either the steps point to a constant and rule_loc is None, or the steps
/// point to a module and rule_loc selects a macro rule within that module
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct ConstPath {
steps: Tok<Vec<Tok<String>>>,
}
impl ConstPath {
pub fn to_const(steps: Tok<Vec<Tok<String>>>) -> Self { Self { steps } }
}
#[derive(Clone)]
pub struct Root {
tree: Rc<ParsedModule>,
consts: Rc<RwLock<HashMap<Sym, Expr>>>,
}
impl Root {
pub fn new(module: ParsedModule, consts: HashMap<Sym, Expr>) -> Self {
Self { tree: Rc::new(module), consts: Rc::new(RwLock::new(consts)) }
}
pub async fn get_const_value(&self, name: Sym, pos: Pos, ctx: Ctx) -> OrcRes<Expr> {
if let Some(val) = self.consts.read().await.get(&name) {
return Ok(val.clone());
}
let (cn, mp) = name.split_last();
let consts_mut = &mut *self.consts.write().await;
let module = self.tree.walk(true, mp.iter().cloned(), consts_mut).await.unwrap();
let member = (module.items.iter())
.filter_map(|it| if let ItemKind::Member(m) = &it.kind { Some(m) } else { None })
.find(|m| m.name() == cn);
match member {
None => Err(mk_errv(
ctx.i.i("Constant does not exist").await,
format!("{name} does not refer to a constant"),
[pos.clone().into()],
)),
Some(mem) => match mem.kind(consts_mut).await {
ParsedMemberKind::Mod(_) => Err(mk_errv(
ctx.i.i("module used as constant").await,
format!("{name} is a module, not a constant"),
[pos.clone().into()],
)),
ParsedMemberKind::Const => Ok(
(consts_mut.get(&name).cloned())
.expect("Tree says the path is correct but no value was found"),
),
},
}
}
}

View File

@@ -1,7 +1,7 @@
use std::collections::VecDeque;
use std::fmt;
use std::future::Future;
use std::rc::{Rc, Weak};
use std::{fmt, mem};
use async_stream::stream;
use derive_destructure::destructure;
@@ -13,8 +13,9 @@ use orchid_base::char_filter::char_filter_match;
use orchid_base::clone;
use orchid_base::error::{OrcErrv, OrcRes};
use orchid_base::format::{FmtCtx, FmtUnit, Format};
use orchid_base::interner::Tok;
use orchid_base::interner::{Interner, Tok};
use orchid_base::location::Pos;
use orchid_base::name::Sym;
use orchid_base::parse::Comment;
use orchid_base::reqnot::{ReqNot, Requester};
use orchid_base::tree::ttv_from_api;
@@ -23,8 +24,9 @@ use substack::{Stackframe, Substack};
use crate::api;
use crate::ctx::Ctx;
use crate::expr::{Expr, ExprParseCtx};
use crate::extension::{Extension, WeakExtension};
use crate::tree::{ItemKind, Member, Module, ParsTokTree, Root};
use crate::parsed::{ItemKind, ParsedMember, ParsedModule, ParsTokTree, ParsedFromApiCx, Root};
#[derive(destructure)]
struct SystemInstData {
@@ -55,6 +57,7 @@ impl System {
pub fn id(&self) -> api::SysId { self.0.id }
pub fn ext(&self) -> &Extension { &self.0.ext }
pub fn ctx(&self) -> &Ctx { &self.0.ctx }
pub fn i(&self) -> &Interner { &self.0.ctx.i }
pub(crate) fn reqnot(&self) -> &ReqNot<api::HostMsgSet> { self.0.ext.reqnot() }
pub async fn get_tree(&self, id: api::TreeId) -> api::MemberKind {
self.reqnot().request(api::GetMember(self.0.id, id)).await
@@ -75,15 +78,23 @@ impl System {
pub fn line_types(&self) -> impl Iterator<Item = &Tok<String>> + '_ { self.0.line_types.iter() }
pub async fn parse(
&self,
module: Sym,
line: Vec<ParsTokTree>,
exported: bool,
comments: Vec<Comment>,
) -> OrcRes<Vec<ParsTokTree>> {
let line =
join_all(line.iter().map(|t| async { t.to_api(&mut async |n, _| match *n {}).await })).await;
let line = join_all(line.into_iter().map(|t| async {
let mut expr_store = self.0.ext.exprs().clone();
t.into_api(&mut expr_store, &mut ()).await
}))
.await;
let comments = comments.iter().map(Comment::to_api).collect_vec();
match self.reqnot().request(api::ParseLine { exported, sys: self.id(), comments, line }).await {
Ok(parsed) => Ok(ttv_from_api(parsed, &mut self.ctx().clone(), &self.ctx().i).await),
let req = api::ParseLine { module: module.to_api(), exported, sys: self.id(), comments, line };
match self.reqnot().request(req).await {
Ok(parsed) => {
let mut pctx = ExprParseCtx { ctx: self.ctx().clone(), exprs: self.ext().exprs().clone() };
Ok(ttv_from_api(parsed, &mut self.ext().exprs().clone(), &mut pctx, self.i()).await)
},
Err(e) => Err(OrcErrv::from_api(&e, &self.ctx().i).await),
}
}
@@ -122,7 +133,11 @@ impl SystemCtor {
self.decl.depends.iter().map(|s| &**s)
}
pub fn id(&self) -> api::SysDeclId { self.decl.id }
pub async fn run<'a>(&self, depends: impl IntoIterator<Item = &'a System>) -> (Module, System) {
pub async fn run<'a>(
&self,
depends: impl IntoIterator<Item = &'a System>,
consts: &mut HashMap<Sym, Expr>,
) -> (ParsedModule, System) {
let depends = depends.into_iter().map(|si| si.id()).collect_vec();
debug_assert_eq!(depends.len(), self.decl.depends.len(), "Wrong number of deps provided");
let ext = self.ext.upgrade().expect("SystemCtor should be freed before Extension");
@@ -139,10 +154,13 @@ impl SystemCtor {
}));
let const_root = clone!(data, ext; stream! {
for (k, v) in sys_inst.const_root {
yield Member::from_api(
yield ParsedMember::from_api(
api::Member { name: k, kind: v },
&mut vec![Tok::from_api(k, &ext.ctx().i).await],
&data,
&mut ParsedFromApiCx {
consts,
path: ext.ctx().i.i(&[]).await,
sys: &data,
}
).await;
}
})
@@ -150,7 +168,7 @@ impl SystemCtor {
.collect::<Vec<_>>()
.await;
ext.ctx().systems.write().await.insert(id, data.downgrade());
let root = Module::new(const_root);
let root = ParsedModule::new(const_root);
(root, data)
}
}
@@ -182,6 +200,7 @@ pub async fn init_systems(
fn walk_deps<'a>(
graph: &mut HashMap<&str, &'a SystemCtor>,
list: &mut Vec<&'a SystemCtor>,
consts: &mut HashMap<Sym, Expr>,
chain: Stackframe<&str>,
) -> Result<(), SysResolvErr> {
if let Some(ctor) = graph.remove(chain.item) {
@@ -193,21 +212,22 @@ pub async fn init_systems(
circle.extend(Substack::Frame(chain).iter().map(|s| s.to_string()));
return Err(SysResolvErr::Loop(circle));
}
walk_deps(graph, list, Substack::Frame(chain).new_frame(dep))?
walk_deps(graph, list, consts, Substack::Frame(chain).new_frame(dep))?
}
list.push(ctor);
}
Ok(())
}
let mut consts = HashMap::new();
for tgt in tgts {
walk_deps(&mut to_load, &mut to_load_ordered, Substack::Bottom.new_frame(tgt))?;
walk_deps(&mut to_load, &mut to_load_ordered, &mut consts, Substack::Bottom.new_frame(tgt))?;
}
let mut systems = HashMap::<&str, System>::new();
let mut root = Module::default();
let mut root_mod = ParsedModule::default();
for ctor in to_load_ordered.iter() {
let (sys_root, sys) = ctor.run(ctor.depends().map(|n| &systems[n])).await;
let (sys_root, sys) = ctor.run(ctor.depends().map(|n| &systems[n]), &mut consts).await;
systems.insert(ctor.name(), sys);
root.merge(sys_root);
root_mod.merge(sys_root);
}
Ok((Root::new(root), systems.into_values().collect_vec()))
Ok((Root::new(root_mod, consts), systems.into_values().collect_vec()))
}

View File

@@ -1,374 +1,59 @@
use std::fmt::Debug;
use std::rc::Rc;
use std::cell::RefCell;
use std::rc::{Rc, Weak};
use async_once_cell::OnceCell;
use async_std::sync::{Mutex, RwLock};
use async_stream::stream;
use futures::future::join_all;
use futures::{FutureExt, StreamExt};
use itertools::Itertools;
use never::Never;
use orchid_base::error::{OrcRes, mk_errv};
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
use hashbrown::HashMap;
use orchid_base::interner::Tok;
use orchid_base::location::Pos;
use orchid_base::macros::{mtreev_fmt, mtreev_from_api};
use orchid_base::name::{NameLike, Sym};
use orchid_base::parse::{Comment, Import};
use orchid_base::tree::{AtomRepr, TokTree, Token};
use orchid_base::{clone, tl_cache};
use ordered_float::NotNan;
use substack::Substack;
use orchid_base::name::Sym;
use crate::api;
use crate::atom::AtomHand;
use crate::ctx::Ctx;
use crate::expr::{Expr, mtreev_to_expr};
use crate::macros::{MacTok, MacTree};
use crate::system::System;
use crate::expr::Expr;
use crate::parsed::{LazyMemberHandle, ParsedMemberKind, ParsedModule};
pub type ParsTokTree = TokTree<'static, AtomHand, Never>;
pub type ParsTok = Token<'static, AtomHand, Never>;
pub struct Tree(Rc<Module>);
#[derive(Debug)]
pub struct Item {
pub pos: Pos,
pub comments: Vec<Comment>,
pub kind: ItemKind,
pub struct WeakTree(Weak<Module>);
pub struct Module {
pub members: HashMap<Tok<String>, Rc<Member>>,
}
#[derive(Debug)]
pub enum ItemKind {
Member(Member),
Export(Tok<String>),
Import(Import),
Macro(Option<NotNan<f64>>, Vec<Rule>),
}
impl ItemKind {
pub fn at(self, pos: Pos) -> Item { Item { comments: vec![], pos, kind: self } }
}
impl Item {
pub async fn from_api(tree: api::Item, path: &mut Vec<Tok<String>>, sys: &System) -> Self {
let kind = match tree.kind {
api::ItemKind::Member(m) => ItemKind::Member(Member::from_api(m, path, sys).await),
api::ItemKind::Import(name) => ItemKind::Import(Import {
path: Sym::from_api(name, &sys.ctx().i).await.iter().collect(),
name: None,
}),
api::ItemKind::Export(e) => ItemKind::Export(Tok::from_api(e, &sys.ctx().i).await),
api::ItemKind::Macro(macro_block) => {
let mut rules = Vec::new();
for rule in macro_block.rules {
let mut comments = Vec::new();
for comment in rule.comments {
comments.push(Comment::from_api(&comment, &sys.ctx().i).await);
}
let pos = Pos::from_api(&rule.location, &sys.ctx().i).await;
let pattern = mtreev_from_api(&rule.pattern, &sys.ctx().i, &mut {
clone!(pos, sys);
async move |a| {
MacTok::Atom(AtomHand::from_api(a, pos.clone(), &mut sys.ctx().clone()).await)
}
})
.await;
rules.push(Rule { pos, pattern, kind: RuleKind::Remote(sys.clone(), rule.id), comments });
}
ItemKind::Macro(macro_block.priority, rules)
},
};
let mut comments = Vec::new();
for comment in tree.comments.iter() {
comments.push(Comment::from_api(comment, &sys.ctx().i).await)
}
Self { pos: Pos::from_api(&tree.location, &sys.ctx().i).await, comments, kind }
}
}
impl Format for Item {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
let comment_text = self.comments.iter().join("\n");
let item_text = match &self.kind {
ItemKind::Import(i) => format!("import {i}").into(),
ItemKind::Export(e) => format!("export {e}").into(),
ItemKind::Macro(None, rules) =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("macro {{\n\t{0}\n}}")))
.units([Variants::sequence(rules.len(), "\n", None)
.units(join_all(rules.iter().map(|r| r.print(c))).await)]),
ItemKind::Member(mem) => match mem.kind.get() {
None => format!("lazy {}", mem.name).into(),
Some(MemberKind::Const(val)) =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("const {0} = {1}")))
.units([mem.name.rc().into(), val.print(c).await]),
Some(MemberKind::Mod(module)) =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("module {0} {{\n\t{1}\n}}")))
.units([mem.name.rc().into(), module.print(c).boxed_local().await]),
},
_ => panic!(),
};
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{0}\n{1}")))
.units([comment_text.into(), item_text])
impl Module {
async fn from_parsed(parsed: &ParsedModule, root: &ParsedModule) -> Self {
let imports =
}
}
pub struct Member {
name: Tok<String>,
kind: OnceCell<MemberKind>,
lazy: Mutex<Option<LazyMemberHandle>>,
pub public: bool,
pub root: WeakTree,
pub canonical_path: Sym,
pub lazy: RefCell<Option<(LazyMemberHandle, Rc<ParsedModule>)>>,
pub kind: OnceCell<MemberKind>,
}
impl Member {
pub fn name(&self) -> Tok<String> { self.name.clone() }
pub async fn kind(&self) -> &MemberKind {
pub async fn kind_mut(&mut self, consts: &mut HashMap<Sym, Expr>) -> &mut MemberKind {
self.kind(consts).await;
self.kind.get_mut().expect("Thhe above line should have initialized it")
}
pub async fn kind(&self, consts: &mut HashMap<Sym, Expr>) -> &MemberKind {
(self.kind.get_or_init(async {
let handle = self.lazy.lock().await.take().expect("Neither known nor lazy");
handle.run().await
let (handle, root) =
self.lazy.borrow_mut().take().expect("If kind is uninit, lazy must be Some");
let parsed = handle.run(consts).await;
MemberKind::from_parsed(&parsed, &root).await
}))
.await
}
pub async fn kind_mut(&mut self) -> &mut MemberKind {
self.kind().await;
self.kind.get_mut().expect("kind() already filled the cell")
}
pub async fn from_api(api: api::Member, path: &mut Vec<Tok<String>>, sys: &System) -> Self {
path.push(Tok::from_api(api.name, &sys.ctx().i).await);
let kind = match api.kind {
api::MemberKind::Lazy(id) => {
let handle = LazyMemberHandle(id, sys.clone(), path.clone());
return handle.into_member(path.pop().unwrap());
},
api::MemberKind::Const(c) => MemberKind::Const(Code::from_expr(
CodeLocator::to_const(sys.ctx().i.i(&*path).await),
Expr::from_api(&c, &mut sys.ext().clone()).await,
)),
api::MemberKind::Module(m) => MemberKind::Mod(Module::from_api(m, path, sys).await),
};
let name = path.pop().unwrap();
Member { name, kind: OnceCell::from(kind), lazy: Mutex::default() }
}
pub fn new(name: Tok<String>, kind: MemberKind) -> Self {
Member { name, kind: OnceCell::from(kind), lazy: Mutex::default() }
}
}
impl Debug for Member {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Member")
.field("name", &self.name)
.field("kind", &self.kind)
.finish_non_exhaustive()
}
}
#[derive(Debug)]
pub enum MemberKind {
Const(Code),
Mod(Module),
Const,
Module(Module),
}
#[derive(Debug, Default)]
pub struct Module {
pub imports: Vec<Sym>,
pub exports: Vec<Tok<String>>,
pub items: Vec<Item>,
}
impl Module {
pub fn new(items: impl IntoIterator<Item = Item>) -> Self {
let items = items.into_iter().collect_vec();
let exports = (items.iter())
.filter_map(|i| match &i.kind {
ItemKind::Export(e) => Some(e.clone()),
_ => None,
})
.collect_vec();
Self { imports: vec![], exports, items }
}
pub fn merge(&mut self, other: Module) {
let mut swap = Module::default();
std::mem::swap(self, &mut swap);
*self = Module::new(swap.items.into_iter().chain(other.items))
}
pub async fn from_api(m: api::Module, path: &mut Vec<Tok<String>>, sys: &System) -> Self {
Self::new(
stream! { for item in m.items { yield Item::from_api(item, path, sys).boxed_local().await } }
.collect::<Vec<_>>()
.await,
)
}
pub async fn walk(
&self,
allow_private: bool,
path: impl IntoIterator<Item = Tok<String>>,
) -> Result<&Module, WalkError> {
let mut cur = self;
for (pos, step) in path.into_iter().enumerate() {
let Some(member) = (cur.items.iter())
.filter_map(|it| if let ItemKind::Member(m) = &it.kind { Some(m) } else { None })
.find(|m| m.name == step)
else {
return Err(WalkError { pos, kind: WalkErrorKind::Missing });
};
if !allow_private && !cur.exports.contains(&step) {
return Err(WalkError { pos, kind: WalkErrorKind::Private });
}
match member.kind().await {
MemberKind::Const(_) => return Err(WalkError { pos, kind: WalkErrorKind::Constant }),
MemberKind::Mod(m) => cur = m,
}
}
Ok(cur)
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum WalkErrorKind {
Missing,
Private,
Constant,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct WalkError {
pub pos: usize,
pub kind: WalkErrorKind,
}
impl Format for Module {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
let import_str = self.imports.iter().map(|i| format!("import {i}")).join("\n");
let head_str = format!("{import_str}\nexport ::({})\n", self.exports.iter().join(", "));
Variants::sequence(self.items.len() + 1, "\n", None).units(
[head_str.into()].into_iter().chain(join_all(self.items.iter().map(|i| i.print(c))).await),
)
}
}
pub struct LazyMemberHandle(api::TreeId, System, Vec<Tok<String>>);
impl LazyMemberHandle {
pub async fn run(self) -> MemberKind {
match self.1.get_tree(self.0).await {
api::MemberKind::Const(c) => MemberKind::Const(Code {
bytecode: Expr::from_api(&c, &mut self.1.ext().clone()).await.into(),
locator: CodeLocator { steps: self.1.ctx().i.i(&self.2).await, rule_loc: None },
source: None,
}),
api::MemberKind::Module(m) =>
MemberKind::Mod(Module::from_api(m, &mut { self.2 }, &self.1).await),
api::MemberKind::Lazy(id) => Self(id, self.1, self.2).run().boxed_local().await,
}
}
pub fn into_member(self, name: Tok<String>) -> Member {
Member { name, kind: OnceCell::new(), lazy: Mutex::new(Some(self)) }
}
}
#[derive(Debug)]
pub struct Rule {
pub pos: Pos,
pub comments: Vec<Comment>,
pub pattern: Vec<MacTree>,
pub kind: RuleKind,
}
impl Format for Rule {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
FmtUnit::new(
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{0b}\n{1} => {2b}"))),
[
self.comments.iter().join("\n").into(),
mtreev_fmt(&self.pattern, c).await,
match &self.kind {
RuleKind::Native(code) => code.print(c).await,
RuleKind::Remote(sys, id) => FmtUnit::new(
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{0} #{1}"))),
[sys.print(c).await, format!("{id:?}").into()],
),
},
],
)
}
}
#[derive(Debug)]
pub enum RuleKind {
Remote(System, api::MacroId),
Native(Code),
}
#[derive(Debug)]
pub struct Code {
locator: CodeLocator,
source: Option<Vec<MacTree>>,
bytecode: OnceCell<Expr>,
}
impl Code {
pub fn from_expr(locator: CodeLocator, expr: Expr) -> Self {
Self { locator, source: None, bytecode: expr.into() }
}
pub fn from_code(locator: CodeLocator, code: Vec<MacTree>) -> Self {
Self { locator, source: Some(code), bytecode: OnceCell::new() }
}
pub fn source(&self) -> Option<&Vec<MacTree>> { self.source.as_ref() }
pub fn set_source(&mut self, source: Vec<MacTree>) {
self.source = Some(source);
self.bytecode = OnceCell::new();
}
pub async fn get_bytecode(&self, ctx: &Ctx) -> &Expr {
(self.bytecode.get_or_init(async {
let src = self.source.as_ref().expect("no bytecode or source");
mtreev_to_expr(src, Substack::Bottom, ctx).await.at(Pos::None)
}))
.await
}
}
impl Format for Code {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
if let Some(bc) = self.bytecode.get() {
return bc.print(c).await;
}
if let Some(src) = &self.source {
return mtreev_fmt(src, c).await;
}
panic!("Code must be initialized with at least one state")
}
}
/// Selects a code element
///
/// Either the steps point to a constant and rule_loc is None, or the steps
/// point to a module and rule_loc selects a macro rule within that module
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct CodeLocator {
steps: Tok<Vec<Tok<String>>>,
/// Index of a macro block in the module demarked by the steps, and a rule in
/// that macro
rule_loc: Option<(u16, u16)>,
}
impl CodeLocator {
pub fn to_const(steps: Tok<Vec<Tok<String>>>) -> Self { Self { steps, rule_loc: None } }
pub fn to_rule(steps: Tok<Vec<Tok<String>>>, macro_i: u16, rule_i: u16) -> Self {
Self { steps, rule_loc: Some((macro_i, rule_i)) }
}
}
#[derive(Clone)]
pub struct Root(Rc<RwLock<Module>>);
impl Root {
pub fn new(module: Module) -> Self { Self(Rc::new(RwLock::new(module))) }
pub async fn get_const_value(&self, name: impl NameLike, pos: Pos, ctx: Ctx) -> OrcRes<Expr> {
let (cn, mp) = name.split_last();
let root_lock = self.0.read().await;
let module = root_lock.walk(true, mp.iter().cloned()).await.unwrap();
let member = (module.items.iter())
.filter_map(|it| if let ItemKind::Member(m) = &it.kind { Some(m) } else { None })
.find(|m| m.name() == cn);
match member {
None => Err(mk_errv(
ctx.i.i("Constant does not exist").await,
format!("{name} does not refer to a constant"),
[pos.clone().into()],
)),
Some(mem) => match mem.kind().await {
MemberKind::Mod(_) => Err(mk_errv(
ctx.i.i("module used as constant").await,
format!("{name} is a module, not a constant"),
[pos.clone().into()],
)),
MemberKind::Const(c) => Ok((c.get_bytecode(&ctx).await).clone()),
},
impl MemberKind {
async fn from_parsed(parsed: &ParsedMemberKind, root: &ParsedModule) -> Self {
match parsed {
ParsedMemberKind::Const => MemberKind::Const,
ParsedMemberKind::Mod(m) => MemberKind::Module(Module::from_parsed(m, root).await),
}
}
}

6
orchid-macros/Cargo.toml Normal file
View File

@@ -0,0 +1,6 @@
[package]
name = "orchid-macros"
version = "0.1.0"
edition = "2024"
[dependencies]

View File

@@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}

View File

@@ -1,12 +1,14 @@
[package]
name = "orchid-std"
version = "0.1.0"
edition = "2021"
edition = "2024"
[dependencies]
async-once-cell = "0.5.4"
async-std = "1.13.0"
async-stream = "0.3.6"
futures = "0.3.31"
hashbrown = "0.15.2"
itertools = "0.14.0"
never = "0.1.0"
once_cell = "1.20.2"
@@ -20,3 +22,6 @@ orchid-extension = { version = "0.1.0", path = "../orchid-extension", features =
ordered-float = "5.0.0"
rust_decimal = "1.36.0"
tokio = { version = "1.43.0", features = ["full"] }
[dev-dependencies]
test_executors = "0.3.2"

View File

@@ -1,7 +1,6 @@
mod number;
mod macros;
mod std;
mod string;
pub use std::StdSystem;
pub use string::str_atom::OrcString;
pub use std::number::num_atom::{Float, HomoArray, Int, Num};
pub use std::std_system::StdSystem;
pub use std::string::str_atom::OrcString;

View File

@@ -0,0 +1,32 @@
use never::Never;
use orchid_base::reqnot::Receipt;
use orchid_extension::atom::AtomDynfo;
use orchid_extension::entrypoint::ExtReq;
use orchid_extension::fs::DeclFs;
use orchid_extension::lexer::LexerObj;
use orchid_extension::parser::ParserObj;
use orchid_extension::system::{System, SystemCard};
use orchid_extension::system_ctor::SystemCtor;
use orchid_extension::tree::GenItem;
#[derive(Default)]
pub struct MacroSystem;
impl SystemCtor for MacroSystem {
type Deps = ();
type Instance = Self;
const NAME: &'static str = "macros";
const VERSION: f64 = 0.00_01;
fn inst() -> Option<Self::Instance> { Some(Self) }
}
impl SystemCard for MacroSystem {
type Ctor = Self;
type Req = Never;
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn AtomDynfo>>> { [] }
}
impl System for MacroSystem {
async fn request(_: ExtReq<'_>, req: Self::Req) -> Receipt<'_> { match req {} }
fn vfs() -> orchid_extension::fs::DeclFs { DeclFs::Mod(&[]) }
fn lexers() -> Vec<LexerObj> { vec![] }
fn parsers() -> Vec<ParserObj> { vec![] }
fn env() -> Vec<GenItem> { vec![] }
}

View File

@@ -0,0 +1,75 @@
use std::borrow::Cow;
use std::rc::Rc;
use futures::future::join_all;
use orchid_api::Paren;
use orchid_base::error::OrcErrv;
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
use orchid_base::location::Pos;
use orchid_base::name::Sym;
use orchid_base::tl_cache;
use orchid_base::tree::Ph;
use orchid_extension::atom::{Atomic, MethodSetBuilder};
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant};
use orchid_extension::expr::Expr;
#[derive(Debug, Clone)]
pub struct MacTree {
pub pos: Pos,
pub tok: Rc<MacTok>,
}
impl MacTree {}
impl Atomic for MacTree {
type Data = ();
type Variant = OwnedVariant;
}
impl OwnedAtom for MacTree {
type Refs = ();
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
self.tok.print(c).await
}
}
#[derive(Debug, Clone)]
pub enum MacTok {
S(Paren, Vec<MacTree>),
Name(Sym),
/// Only permitted in arguments to `instantiate_tpl`
Slot,
Value(Expr),
Lambda(Vec<MacTree>, Vec<MacTree>),
Ph(Ph),
}
impl Format for MacTok {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
match self {
Self::Value(v) => v.print(c).await,
Self::Lambda(arg, b) => FmtUnit::new(
tl_cache!(Rc<Variants>: Rc::new(Variants::default()
.unbounded("\\{0b}.{1l}")
.bounded("(\\{0b}.{1b})"))),
[mtreev_fmt(arg, c).await, mtreev_fmt(b, c).await],
),
Self::Name(n) => format!("{n}").into(),
Self::Ph(ph) => format!("{ph}").into(),
Self::S(p, body) => FmtUnit::new(
match *p {
Paren::Round => Rc::new(Variants::default().bounded("({0b})")),
Paren::Curly => Rc::new(Variants::default().bounded("{{0b}}")),
Paren::Square => Rc::new(Variants::default().bounded("[{0b}]")),
},
[mtreev_fmt(body, c).await],
),
Self::Slot => format!("SLOT").into(),
}
}
}
pub async fn mtreev_fmt<'b>(
v: impl IntoIterator<Item = &'b MacTree>,
c: &(impl FmtCtx + ?Sized),
) -> FmtUnit {
FmtUnit::sequence(" ", None, join_all(v.into_iter().map(|t| t.print(c))).await)
}

View File

@@ -0,0 +1,64 @@
use std::ops::RangeInclusive;
use std::rc::Rc;
use futures::FutureExt;
use orchid_base::error::{OrcRes, mk_errv};
use orchid_base::location::Pos;
use orchid_base::parse::name_start;
use orchid_base::tokens::PARENS;
use orchid_extension::atom::AtomicFeatures;
use orchid_extension::gen_expr::atom;
use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable};
use orchid_extension::tree::{GenTok, GenTokTree};
use crate::macros::mactree::{MacTok, MacTree};
#[derive(Default)]
pub struct MacTreeLexer;
impl Lexer for MacTreeLexer {
const CHAR_FILTER: &'static [RangeInclusive<char>] = &['\''..='\''];
async fn lex<'a>(tail: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree<'a>)> {
let Some(tail2) = tail.strip_prefix('\'') else {
return Err(err_not_applicable(ctx.i).await.into());
};
let tail3 = tail2.trim_start();
return match mac_tree(tail3, ctx).await {
Ok((tail4, mactree)) =>
Ok((tail4, GenTok::X(mactree.factory()).at(ctx.pos(tail)..ctx.pos(tail4)))),
Err(e) => Ok((tail2, GenTok::Bottom(e).at(ctx.tok_ran(1, tail2)))),
};
async fn mac_tree<'a>(tail: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, MacTree)> {
for (lp, rp, paren) in PARENS {
let Some(mut body_tail) = tail.strip_prefix(*lp) else { continue };
let mut items = Vec::new();
return loop {
let tail2 = body_tail.trim();
if let Some(tail3) = tail2.strip_prefix(*rp) {
break Ok((tail3, MacTree {
pos: Pos::Range(ctx.pos(tail)..ctx.pos(tail3)),
tok: Rc::new(MacTok::S(*paren, items)),
}));
} else if tail2.is_empty() {
return Err(mk_errv(
ctx.i.i("Unclosed block").await,
format!("Expected closing {rp}"),
[Pos::Range(ctx.tok_ran(1, tail)).into()],
));
}
let (new_tail, new_item) = mac_tree(tail2, ctx).boxed_local().await?;
body_tail = new_tail;
items.push(new_item);
};
}
const INTERPOL: &[&str] = &["$", "..$"];
for pref in INTERPOL {
let Some(code) = tail.strip_prefix(pref) else { continue };
}
return Err(mk_errv(
ctx.i.i("Expected token after '").await,
format!("Expected a token after ', found {tail:?}"),
[Pos::Range(ctx.tok_ran(1, tail)).into()],
));
}
}
}

View File

@@ -0,0 +1,6 @@
mod macro_system;
pub mod mactree;
mod mactree_lexer;
mod rule;
use mactree::{MacTok, MacTree};

View File

@@ -2,9 +2,9 @@ use orchid_base::name::Sym;
use super::scal_match::scalv_match;
use super::shared::AnyMatcher;
use super::state::MatchState;
use super::vec_match::vec_match;
use crate::macros::MacTree;
use crate::rule::state::MatchState;
#[must_use]
pub fn any_match<'a>(

View File

@@ -5,8 +5,8 @@ use orchid_base::side::Side;
use orchid_base::tree::Ph;
use super::shared::{AnyMatcher, ScalMatcher, VecMatcher};
use super::vec_attrs::vec_attrs;
use crate::macros::{MacTok, MacTree};
use crate::rule::vec_attrs::vec_attrs;
pub type MaxVecSplit<'a> = (&'a [MacTree], (Tok<String>, u8, bool), &'a [MacTree]);
@@ -91,7 +91,6 @@ pub fn mk_vec(pattern: &[MacTree]) -> VecMatcher {
#[must_use]
fn mk_scalar(pattern: &MacTree) -> ScalMatcher {
match &*pattern.tok {
MacTok::Atom(_) | MacTok::Done(_) => panic!("Atoms and Done aren't supported in matchers"),
MacTok::Name(n) => ScalMatcher::Name(n.clone()),
MacTok::Ph(Ph { name, kind }) => match kind {
PhKind::Vector { .. } => {
@@ -101,7 +100,7 @@ fn mk_scalar(pattern: &MacTree) -> ScalMatcher {
},
MacTok::S(c, body) => ScalMatcher::S(*c, Box::new(mk_any(body))),
MacTok::Lambda(arg, body) => ScalMatcher::Lambda(Box::new(mk_any(arg)), Box::new(mk_any(body))),
MacTok::Ref(_) | MacTok::Slot(_) => panic!("Extension-only variants"),
MacTok::Value(_) | MacTok::Slot => panic!("Only used for templating"),
}
}

View File

@@ -1,4 +1,5 @@
use std::fmt;
use std::rc::Rc;
use itertools::Itertools;
use orchid_api::PhKind;
@@ -8,13 +9,12 @@ use orchid_base::name::Sym;
use orchid_base::tree::Ph;
use super::any_match::any_match;
use super::build::mk_any;
use super::build::{mk_any, mk_vec};
use super::shared::{AnyMatcher, VecMatcher};
use super::state::{MatchState, StateEntry};
use super::vec_attrs::vec_attrs;
use super::vec_match::vec_match;
use crate::macros::{MacTok, MacTree};
use crate::rule::build::mk_vec;
pub fn first_is_vec(pattern: &[MacTree]) -> bool { vec_attrs(pattern.first().unwrap()).is_some() }
pub fn last_is_vec(pattern: &[MacTree]) -> bool { vec_attrs(pattern.last().unwrap()).is_some() }
@@ -30,8 +30,9 @@ impl NamedMatcher {
match last_is_vec(pattern) {
true => Self(mk_any(pattern)),
false => {
let kind: PhKind = PhKind::Vector { priority: 0, at_least_one: false };
let suffix = [MacTok::Ph(Ph { name: i.i("::after").await, kind }).at(Pos::None)];
let kind = PhKind::Vector { priority: 0, at_least_one: false };
let tok = MacTok::Ph(Ph { name: i.i("::after").await, kind });
let suffix = [MacTree { pos: Pos::None, tok: Rc::new(tok) }];
Self(mk_any(&pattern.iter().chain(&suffix).cloned().collect_vec()))
},
}

View File

@@ -2,8 +2,8 @@ use orchid_base::name::Sym;
use super::any_match::any_match;
use super::shared::ScalMatcher;
use super::state::{MatchState, StateEntry};
use crate::macros::{MacTok, MacTree};
use crate::rule::state::{MatchState, StateEntry};
#[must_use]
pub fn scal_match<'a>(
@@ -16,7 +16,6 @@ pub fn scal_match<'a>(
true => MatchState::from_name(n1.clone(), expr.pos.clone()),
false => MatchState::default(),
}),
(ScalMatcher::Placeh { .. }, MacTok::Done(_)) => None,
(ScalMatcher::Placeh { key }, _) =>
Some(MatchState::from_ph(key.clone(), StateEntry::Scalar(expr))),
(ScalMatcher::S(c1, b_mat), MacTok::S(c2, body)) if c1 == c2 =>

View File

@@ -40,7 +40,9 @@ impl<'a> MatchState<'a> {
pub fn combine(self, s: Self) -> Self {
Self {
placeholders: self.placeholders.into_iter().chain(s.placeholders).collect(),
name_posv: join_maps(self.name_posv, s.name_posv, |_, l, r| l.into_iter().chain(r).collect()),
name_posv: join_maps::<_, Vec<Pos>>(self.name_posv, s.name_posv, |_, l, r| {
l.into_iter().chain(r).collect()
}),
}
}
pub fn ph_len(&self, key: &Tok<String>) -> Option<usize> {

View File

@@ -5,8 +5,8 @@ use orchid_base::name::Sym;
use super::scal_match::scalv_match;
use super::shared::VecMatcher;
use super::state::{MatchState, StateEntry};
use crate::macros::MacTree;
use crate::rule::state::{MatchState, StateEntry};
#[must_use]
pub fn vec_match<'a>(

View File

@@ -1,77 +0,0 @@
use std::rc::Rc;
use never::Never;
use orchid_base::number::Numeric;
use orchid_base::reqnot::Receipt;
use orchid_extension::atom::{AtomDynfo, AtomicFeatures};
use orchid_extension::entrypoint::ExtReq;
use orchid_extension::fs::DeclFs;
use orchid_extension::system::{System, SystemCard};
use orchid_extension::system_ctor::SystemCtor;
use orchid_extension::tree::{MemKind, comments, fun, module, root_mod};
use ordered_float::NotNan;
use crate::OrcString;
use crate::number::num_atom::{Float, HomoArray, Int, Num};
use crate::number::num_lexer::NumLexer;
use crate::string::str_atom::{IntStrAtom, StrAtom};
use crate::string::str_lexer::StringLexer;
#[derive(Default)]
pub struct StdSystem;
impl SystemCtor for StdSystem {
type Deps = ();
type Instance = Self;
const NAME: &'static str = "orchid::std";
const VERSION: f64 = 0.00_01;
fn inst() -> Option<Self::Instance> { Some(StdSystem) }
}
impl SystemCard for StdSystem {
type Ctor = Self;
type Req = Never;
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn AtomDynfo>>> {
[Some(Int::dynfo()), Some(Float::dynfo()), Some(StrAtom::dynfo()), Some(IntStrAtom::dynfo())]
}
}
impl System for StdSystem {
async fn request(_: ExtReq<'_>, req: Self::Req) -> Receipt<'_> { match req {} }
fn lexers() -> Vec<orchid_extension::lexer::LexerObj> { vec![&StringLexer, &NumLexer] }
fn parsers() -> Vec<orchid_extension::parser::ParserObj> { vec![] }
fn vfs() -> DeclFs { DeclFs::Mod(&[]) }
fn env() -> Vec<(String, MemKind)> {
vec![root_mod("std", [], [
module(true, "string", [], [comments(
["Concatenate two strings"],
fun(true, "concat", |left: OrcString<'static>, right: OrcString<'static>| async move {
StrAtom::new(Rc::new(left.get_string().await.to_string() + &right.get_string().await))
}),
)]),
module(true, "number", [], [
fun(true, "add", |a: Num, b: Num| async move {
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", |a: Num| async move {
Num(match a.0 {
Numeric::Int(i) => Numeric::Int(-i),
Numeric::Float(f) => Numeric::Float(-f),
})
}),
fun(true, "mul", |a: Num, b: Num| async move {
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", |a: Int, b: Int| async move { Int(a.0 / b.0) }),
fun(true, "imod", |a: Int, b: Int| async move { Int(a.0 % b.0) }),
fun(true, "fdiv", |a: Float, b: Float| async move { Float(a.0 / b.0) }),
fun(true, "fmod", |a: Float, b: Float| async move {
Float(a.0 - NotNan::new((a.0 / b.0).trunc()).unwrap() * b.0)
}),
]),
])]
}
}

View File

@@ -0,0 +1,4 @@
pub mod number;
pub mod string;
pub mod std_system;

View File

@@ -1,2 +1,3 @@
pub mod num_atom;
pub mod num_lexer;
pub mod num_lib;

View File

@@ -17,7 +17,6 @@ pub struct Int(pub i64);
impl Atomic for Int {
type Variant = ThinVariant;
type Data = Self;
fn reg_reqs() -> MethodSetBuilder<Self> { MethodSetBuilder::new() }
}
impl ThinAtom for Int {
async fn print(&self, _: SysCtx) -> FmtUnit { self.0.to_string().into() }
@@ -33,7 +32,6 @@ pub struct Float(pub NotNan<f64>);
impl Atomic for Float {
type Variant = ThinVariant;
type Data = Self;
fn reg_reqs() -> MethodSetBuilder<Self> { MethodSetBuilder::new() }
}
impl ThinAtom for Float {
async fn print(&self, _: SysCtx) -> FmtUnit { self.0.to_string().into() }

View File

@@ -0,0 +1,34 @@
use orchid_base::number::Numeric;
use orchid_extension::tree::{GenItem, fun, prefix};
use ordered_float::NotNan;
use super::num_atom::{Float, HomoArray, Int, Num};
pub fn gen_num_lib() -> Vec<GenItem> {
prefix("std::number", [
fun(true, "add", |a: Num, b: Num| async move {
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", |a: Num| async move {
Num(match a.0 {
Numeric::Int(i) => Numeric::Int(-i),
Numeric::Float(f) => Numeric::Float(-f),
})
}),
fun(true, "mul", |a: Num, b: Num| async move {
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", |a: Int, b: Int| async move { Int(a.0 / b.0) }),
fun(true, "imod", |a: Int, b: Int| async move { Int(a.0 % b.0) }),
fun(true, "fdiv", |a: Float, b: Float| async move { Float(a.0 / b.0) }),
fun(true, "fmod", |a: Float, b: Float| async move {
Float(a.0 - NotNan::new((a.0 / b.0).trunc()).unwrap() * b.0)
}),
])
}

View File

@@ -0,0 +1,41 @@
use never::Never;
use orchid_base::reqnot::Receipt;
use orchid_extension::atom::{AtomDynfo, AtomicFeatures};
use orchid_extension::entrypoint::ExtReq;
use orchid_extension::fs::DeclFs;
use orchid_extension::lexer::LexerObj;
use orchid_extension::parser::ParserObj;
use orchid_extension::system::{System, SystemCard};
use orchid_extension::system_ctor::SystemCtor;
use orchid_extension::tree::{GenItem, 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::number::num_lexer::NumLexer;
use crate::std::string::str_lexer::StringLexer;
use crate::{Float, Int};
#[derive(Default)]
pub struct StdSystem;
impl SystemCtor for StdSystem {
type Deps = ();
type Instance = Self;
const NAME: &'static str = "orchid::std";
const VERSION: f64 = 0.00_01;
fn inst() -> Option<Self::Instance> { Some(Self) }
}
impl SystemCard for StdSystem {
type Ctor = Self;
type Req = Never;
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn AtomDynfo>>> {
[Some(Int::dynfo()), Some(Float::dynfo()), Some(StrAtom::dynfo()), Some(IntStrAtom::dynfo())]
}
}
impl System for StdSystem {
async fn request(_: ExtReq<'_>, req: Self::Req) -> Receipt<'_> { match req {} }
fn lexers() -> Vec<LexerObj> { vec![&StringLexer, &NumLexer] }
fn parsers() -> Vec<ParserObj> { vec![] }
fn vfs() -> DeclFs { DeclFs::Mod(&[]) }
fn env() -> Vec<GenItem> { merge_trivial([gen_num_lib(), gen_str_lib()]) }
}

View File

@@ -1,2 +1,3 @@
pub mod str_atom;
pub mod str_lexer;
pub mod str_lib;

View File

@@ -7,7 +7,7 @@ use async_std::io::Write;
use orchid_api_derive::Coding;
use orchid_api_traits::{Encode, Request};
use orchid_base::error::{OrcRes, mk_errv};
use orchid_base::format::FmtUnit;
use orchid_base::format::{FmtCtx, FmtUnit};
use orchid_base::interner::Tok;
use orchid_extension::atom::{AtomMethod, Atomic, MethodSetBuilder, Supports, TypAtom};
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
@@ -49,7 +49,9 @@ impl OwnedAtom for StrAtom {
async fn serialize(&self, _: SysCtx, sink: Pin<&mut (impl Write + ?Sized)>) -> Self::Refs {
self.deref().encode(sink).await
}
async fn print(&self, _: SysCtx) -> FmtUnit { format!("{:?}", &*self.0).into() }
async fn print<'a>(&'a self, _: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
format!("{:?}", &*self.0).into()
}
async fn deserialize(mut ctx: impl DeserializeCtx, _: Self::Refs) -> Self {
Self::new(Rc::new(ctx.read::<String>().await))
}
@@ -60,7 +62,6 @@ pub struct IntStrAtom(Tok<String>);
impl Atomic for IntStrAtom {
type Variant = OwnedVariant;
type Data = orchid_api::TStr;
fn reg_reqs() -> MethodSetBuilder<Self> { MethodSetBuilder::new() }
}
impl From<Tok<String>> for IntStrAtom {
fn from(value: Tok<String>) -> Self { Self(value) }
@@ -68,7 +69,9 @@ impl From<Tok<String>> for IntStrAtom {
impl OwnedAtom for IntStrAtom {
type Refs = ();
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.0.to_api()) }
async fn print(&self, _ctx: SysCtx) -> FmtUnit { format!("{:?}i", *self.0).into() }
async fn print<'a>(&'a self, _: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
format!("{:?}i", *self.0).into()
}
async fn serialize(&self, _: SysCtx, write: Pin<&mut (impl Write + ?Sized)>) {
self.0.encode(write).await
}

View File

@@ -94,11 +94,9 @@ fn parse_string(str: &str) -> Result<String, StringError> {
#[derive(Default)]
pub struct StringLexer;
impl Lexer for StringLexer {
const CHAR_FILTER: &'static [std::ops::RangeInclusive<char>] = &['"'..='"', '\''..='\''];
const CHAR_FILTER: &'static [std::ops::RangeInclusive<char>] = &['"'..='"', '`'..='`'];
async fn lex<'a>(all: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree<'a>)> {
let Some((mut tail, delim)) = (all.strip_prefix('"').map(|t| (t, '"')))
.or_else(|| all.strip_prefix('\'').map(|t| (t, '\'')))
else {
let Some(mut tail) = all.strip_prefix('"') else {
return Err(err_not_applicable(ctx.i).await.into());
};
let mut ret = None;
@@ -125,7 +123,7 @@ impl Lexer for StringLexer {
wrap_tokv([concat_fn, prev, new])
};
loop {
if let Some(rest) = tail.strip_prefix(delim) {
if let Some(rest) = tail.strip_prefix('"') {
return Ok((rest, add_frag(ret, str_to_gen(&mut cur, tail, &mut errors, ctx).await).await));
} else if let Some(rest) = tail.strip_prefix('$') {
ret = Some(add_frag(ret, str_to_gen(&mut cur, tail, &mut errors, ctx).await).await);

View File

@@ -0,0 +1,15 @@
use std::rc::Rc;
use orchid_extension::tree::{GenItem, comments, fun, prefix};
use super::str_atom::StrAtom;
use crate::OrcString;
pub fn gen_str_lib() -> Vec<GenItem> {
prefix("std::string", [comments(
["Concatenate two strings"],
fun(true, "concat", |left: OrcString<'static>, right: OrcString<'static>| async move {
StrAtom::new(Rc::new(left.get_string().await.to_string() + &right.get_string().await))
}),
)])
}

View File

@@ -1,7 +1,7 @@
[package]
name = "orchidlang"
version = "0.3.0"
edition = "2021"
edition = "2024"
license = "GPL-3.0"
repository = "https://github.com/lbfalvy/orchid"
description = """

View File

@@ -1,7 +1,7 @@
[package]
name = "orcx"
version = "0.1.0"
edition = "2021"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -22,7 +22,7 @@ use orchid_host::execute::{ExecCtx, ExecResult};
use orchid_host::expr::mtreev_to_expr;
use orchid_host::extension::Extension;
use orchid_host::lex::lex;
use orchid_host::parse::{ParseCtxImpl, parse_items, parse_mtree};
use orchid_host::parse::{HostParseCtxImpl, parse_items, parse_mtree};
use orchid_host::subprocess::ext_command;
use orchid_host::system::init_systems;
use substack::Substack;
@@ -117,7 +117,7 @@ async fn main() -> io::Result<ExitCode> {
return;
};
let reporter = ReporterImpl::new();
let pctx = ParseCtxImpl { reporter: &reporter, systems: &systems };
let pctx = HostParseCtxImpl { reporter: &reporter, systems: &systems };
let snip = Snippet::new(first, &lexemes, i);
let ptree = parse_items(&pctx, Substack::Bottom, snip).await.unwrap();
if let Some(errv) = reporter.errv() {

View File

@@ -1,7 +1,7 @@
[package]
name = "stdio-perftest"
version = "0.1.0"
edition = "2021"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -1,7 +1,7 @@
[package]
name = "xtask"
version = "0.1.0"
edition = "2021"
edition = "2024"
[dependencies]
clap = { version = "4.5.24", features = ["derive"] }