commit before easter break
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -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",
|
||||
]
|
||||
|
||||
|
||||
@@ -10,5 +10,5 @@ members = [
|
||||
"orchid-api",
|
||||
"orchid-api-derive",
|
||||
"orchid-api-traits",
|
||||
"stdio-perftest", "xtask",
|
||||
"stdio-perftest", "xtask", "orchid-macros",
|
||||
]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "orchid-api-derive"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 },
|
||||
}
|
||||
@@ -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>,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
snip.pos(),
|
||||
)
|
||||
.into()])),
|
||||
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;
|
||||
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(
|
||||
ctx.i().i("Expected token").await,
|
||||
"Expected a name, a parenthesized list of names, or a globstar.",
|
||||
[Pos::Range(tail.pos()).into()],
|
||||
));
|
||||
};
|
||||
let ret = rec(tt, ctx).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 {
|
||||
return Err(mk_errv(
|
||||
tail.i().i("Expected name").await,
|
||||
"Expected a name, a 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() {
|
||||
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()],
|
||||
)),
|
||||
}
|
||||
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::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 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),
|
||||
}
|
||||
ok
|
||||
},
|
||||
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()],
|
||||
)),
|
||||
},
|
||||
}
|
||||
}
|
||||
ok
|
||||
},
|
||||
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()],
|
||||
));
|
||||
},
|
||||
};
|
||||
Ok(Parsed { output, tail })
|
||||
body = tail;
|
||||
}
|
||||
Ok(o)
|
||||
},
|
||||
t => {
|
||||
return Err(mk_errv(
|
||||
ctx.i().i("Unrecognized name end").await,
|
||||
format!("Names cannot end with {:?} tokens", fmt(t, ctx.i()).await),
|
||||
[ttpos.into()],
|
||||
));
|
||||
},
|
||||
}
|
||||
}
|
||||
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 }
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>>> {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 } }
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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::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,
|
||||
}
|
||||
},
|
||||
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);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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,20 +89,25 @@ 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),
|
||||
ExprKind::Bottom(bot) => (ExprKind::Bottom(bot.clone()), StackOp::Unwind(bot)),
|
||||
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),
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
r(tt)
|
||||
});
|
||||
foo.into_api(&mut exprs, &mut ()).await
|
||||
}
|
||||
pub fn rm_subtree(&mut self, ticket: api::TreeTicket) -> ParsTokTree {
|
||||
self.sub_trees.remove(&ticket).unwrap()
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
383
orchid-host/src/parsed.rs
Normal 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"),
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()))
|
||||
}
|
||||
|
||||
@@ -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
6
orchid-macros/Cargo.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "orchid-macros"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
3
orchid-macros/src/main.rs
Normal file
3
orchid-macros/src/main.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
32
orchid-std/src/macros/macro_system.rs
Normal file
32
orchid-std/src/macros/macro_system.rs
Normal 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![] }
|
||||
}
|
||||
75
orchid-std/src/macros/mactree.rs
Normal file
75
orchid-std/src/macros/mactree.rs
Normal 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)
|
||||
}
|
||||
64
orchid-std/src/macros/mactree_lexer.rs
Normal file
64
orchid-std/src/macros/mactree_lexer.rs
Normal 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()],
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
6
orchid-std/src/macros/mod.rs
Normal file
6
orchid-std/src/macros/mod.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
mod macro_system;
|
||||
pub mod mactree;
|
||||
mod mactree_lexer;
|
||||
mod rule;
|
||||
|
||||
use mactree::{MacTok, MacTree};
|
||||
@@ -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>(
|
||||
@@ -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"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()))
|
||||
},
|
||||
}
|
||||
@@ -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 =>
|
||||
@@ -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> {
|
||||
@@ -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>(
|
||||
@@ -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)
|
||||
}),
|
||||
]),
|
||||
])]
|
||||
}
|
||||
}
|
||||
4
orchid-std/src/std/mod.rs
Normal file
4
orchid-std/src/std/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod number;
|
||||
pub mod string;
|
||||
|
||||
pub mod std_system;
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod num_atom;
|
||||
pub mod num_lexer;
|
||||
pub mod num_lib;
|
||||
@@ -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() }
|
||||
34
orchid-std/src/std/number/num_lib.rs
Normal file
34
orchid-std/src/std/number/num_lib.rs
Normal 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)
|
||||
}),
|
||||
])
|
||||
}
|
||||
41
orchid-std/src/std/std_system.rs
Normal file
41
orchid-std/src/std/std_system.rs
Normal 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()]) }
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod str_atom;
|
||||
pub mod str_lexer;
|
||||
pub mod str_lib;
|
||||
@@ -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
|
||||
}
|
||||
@@ -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);
|
||||
15
orchid-std/src/std/string/str_lib.rs
Normal file
15
orchid-std/src/std/string/str_lib.rs
Normal 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))
|
||||
}),
|
||||
)])
|
||||
}
|
||||
@@ -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 = """
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "xtask"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.24", features = ["derive"] }
|
||||
|
||||
Reference in New Issue
Block a user