Began implementing fully isomorphic macros

Like Rust's Proc macros. Now we have preprocessor recursion to worry about. I also made a cool macro for enums
This commit is contained in:
2024-10-14 00:13:09 +02:00
parent 84cbcdd4fe
commit 3a3ae98aff
66 changed files with 2302 additions and 1164 deletions

View File

@@ -1,7 +1,8 @@
{ {
"languages": { "languages": {
"Rust": { "Rust": {
"language_servers": ["rust-analyzer", "..."] "language_servers": ["rust-analyzer", "..."],
"formatter": "language_server"
} }
}, },
"wrap_guides": [100], "wrap_guides": [100],
@@ -11,8 +12,11 @@
"path": "C:\\Users\\z004yk5r\\.cargo\\bin\\rust-analyzer.exe" "path": "C:\\Users\\z004yk5r\\.cargo\\bin\\rust-analyzer.exe"
}, },
"initialization_options": { "initialization_options": {
"checkOnSave": { "check": {
"command": "clippy" "command": "clippy"
},
"rustfmt": {
"extraArgs": ["+nightly"]
} }
} }
} }

12
Cargo.lock generated
View File

@@ -477,9 +477,9 @@ checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91"
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.18" version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [ dependencies = [
"autocfg", "autocfg",
] ]
@@ -515,6 +515,7 @@ dependencies = [
name = "orchid-api-traits" name = "orchid-api-traits"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"itertools",
"never", "never",
"ordered-float", "ordered-float",
] ]
@@ -529,6 +530,7 @@ dependencies = [
"itertools", "itertools",
"lazy_static", "lazy_static",
"never", "never",
"num-traits",
"orchid-api", "orchid-api",
"orchid-api-derive", "orchid-api-derive",
"orchid-api-traits", "orchid-api-traits",
@@ -571,12 +573,14 @@ dependencies = [
"itertools", "itertools",
"lazy_static", "lazy_static",
"never", "never",
"num-traits",
"orchid-api", "orchid-api",
"orchid-api-traits", "orchid-api-traits",
"orchid-base", "orchid-base",
"ordered-float", "ordered-float",
"paste", "paste",
"substack", "substack",
"trait-set",
] ]
[[package]] [[package]]
@@ -925,9 +929,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "substack" name = "substack"
version = "1.1.0" version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffccc3d80f0a489de67aa74ff31ab852abb973e1c6dacf3704889e00ca544e7f" checksum = "26ce98c74d8476dd7b8515495625bc1bd4449b50f4926ac030964976e035ed53"
[[package]] [[package]]
name = "syn" name = "syn"

View File

@@ -1,35 +1,2 @@
const main := println "Hello World!" exit_status::success const user := "dave"
const main := println "Hello $user!" exit_status::success
macro (
rule match ...$expr { ...$body } => '(
fn::pass (...$expr) \match::value. ...$(
fn::pass (quote::split body ';) \cases.
fn::pass (list::map cases \x. (
fn::pass (quote::split_once x '=>) \pair.
tuple::destr pair 2 \req. \handler.
fn::pass (macro::run '(match::request (...$key))) \match_res.
quote::match '(match::response $decoder (...$bindings)) match_res \match_res_match.
fn::pass (option::expect match_res_match "Invalid pattern ${key}") \res_parts.
fn::pass (map::get_unwrap res_parts "decoder") \decoder.
fn::pass (map::get_unwrap res_parts "bindings") \bindings.
fn::pass (quote::to_list bindings) \binding_names.
fn::pass (list::rfold handler \tail. \name. '( \ $name . $tail )) \success.
'( $decoder $success )
)) \case_fns.
list::append case_fns '( panic "No cases match" )
)
)
)
--[
Conceptually, all matches are compared.
1. Within a macro, the top rule wins
2. If two winning matches are not within the same macro,
the one that matches the outermost, first token wins,
including tokens that are immediately captured, such that a rule starting with .. is
beaten by the same rule parenthesized and any rule starting with a scalar is beaten
by the same rule prefixed with a vectorial
3. If two winning matches start with the same token, an ambiguity error is raised
]--

Binary file not shown.

45
notes/new_macro_model.md Normal file
View File

@@ -0,0 +1,45 @@
# Code sample for the new macro system
```
macro (
rule match ...$expr { ...$body } => \recurse. '(
fn::pass (...$expr) \match::value. ...$(
fn::pass (quote::split body ';) \cases.
fn::pass (list::map cases \x. (
fn::pass (quote::split_once x '=>) \pair.
tuple::destr pair 2 \req. \handler.
fn::pass (recurse '(match::request (...$key))) \match_res.
quote::match '(match::response $decoder (...$bindings)) match_res \match_res_match.
fn::pass (option::expect match_res_match "Invalid pattern ${key}") \res_parts.
fn::pass (map::get_unwrap res_parts "decoder") \decoder.
fn::pass (map::get_unwrap res_parts "bindings") \bindings.
fn::pass (quote::to_list bindings) \binding_names.
fn::pass (list::rfold handler \tail. \name. '( \ $name . $tail )) \success.
'( $decoder $success )
)) \case_fns.
list::append case_fns '( panic "No cases match" )
)
)
)
--[
Macros are run from the top down.
For every token
1. If it's a name token, test all macros starting with that name
2. If none match and this is the first token in a list, test all macros starting with vectorials
Test all in a set of macros
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 single match is in the recursion stack, raise a recursion error
4. Add the matching rule to the recursion stack, then execute the body.
]--
--[
1. Macro patterns are held in the host, they don't contain atoms, and atoms are never considered equal, so the matcher doesn't have to call an extension.
2. The body of macros may be defined in Rust. If it isn't, the entire interpreter will recurse on the macro to calculate the output.
]--
--[
1. if the rule body uses the same macro, fail with the rule
2. if the rule explicitly recursively invokes the same macro, fail with the first match
]--

View File

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

View File

@@ -0,0 +1,26 @@
pub trait ApiEquiv {
type Api;
}
pub trait ToApi: Sized + ApiEquiv {
type Ctx;
fn to_api(&self, ctx: &mut Self::Ctx) -> Self::Api;
fn into_api(self, ctx: &mut Self::Ctx) -> Self::Api { self.to_api(ctx) }
}
pub trait FromApi: ApiEquiv {
type Ctx;
fn from_api(api: &Self::Api, ctx: &mut Self::Ctx) -> Self;
}
/// This is the weakest kind of conversion possible;
/// By holding a reference to the source type, you can provide a reference to the target type.
/// Unlike Into, the target type may hold references into the source,
/// but unlike AsRef, it doesn't have to be fully contained in the source.
/// The resulting object is stackbound so its utility is very limited.
pub trait ProjectionMut<T> {
fn with_built<R>(&mut self, cb: impl FnOnce(&mut T) -> R) -> R;
}
impl<T> ProjectionMut<T> for T {
fn with_built<R>(&mut self, cb: impl FnOnce(&mut T) -> R) -> R { cb(self) }
}

View File

@@ -1,5 +1,7 @@
use std::io::{Read, Write}; use std::io::{Read, Write};
use itertools::{Chunk, Itertools};
use crate::Encode; use crate::Encode;
pub fn encode_enum<W: Write + ?Sized>(write: &mut W, id: u8, f: impl FnOnce(&mut W)) { pub fn encode_enum<W: Write + ?Sized>(write: &mut W, id: u8, f: impl FnOnce(&mut W)) {
@@ -11,10 +13,20 @@ pub fn write_exact<W: Write + ?Sized>(write: &mut W, bytes: &'static [u8]) {
write.write_all(bytes).expect("Failed to write exact bytes") write.write_all(bytes).expect("Failed to write exact bytes")
} }
pub fn print_bytes(b: &[u8]) -> String {
(b.iter().map(|b| format!("{b:02x}")))
.chunks(4)
.into_iter()
.map(|mut c: Chunk<_>| c.join(" "))
.join(" ")
}
pub fn read_exact<R: Read + ?Sized>(read: &mut R, bytes: &'static [u8]) { pub fn read_exact<R: Read + ?Sized>(read: &mut R, bytes: &'static [u8]) {
let mut data = vec![0u8; bytes.len()]; let mut data = vec![0u8; bytes.len()];
read.read_exact(&mut data).expect("Failed to read bytes"); read.read_exact(&mut data).expect("Failed to read bytes");
assert_eq!(&data, bytes, "Wrong bytes") if data != bytes {
panic!("Wrong bytes!\nExpected: {}\nFound: {}", print_bytes(bytes), print_bytes(&data));
}
} }
pub fn enc_vec(enc: &impl Encode) -> Vec<u8> { pub fn enc_vec(enc: &impl Encode) -> Vec<u8> {

View File

@@ -2,8 +2,10 @@ mod coding;
mod helpers; mod helpers;
mod hierarchy; mod hierarchy;
mod relations; mod relations;
mod api_conv;
pub use coding::{Coding, Decode, Encode}; pub use coding::*;
pub use helpers::{enc_vec, encode_enum, read_exact, write_exact}; pub use helpers::*;
pub use hierarchy::{Extends, InHierarchy, TLBool, TLFalse, TLTrue, UnderRoot}; pub use hierarchy::*;
pub use relations::{Channel, MsgSet, Request}; pub use relations::*;
pub use api_conv::*;

View File

@@ -3,7 +3,11 @@ use crate::helpers::enc_vec;
pub trait Request: Coding + Sized + Send + 'static { pub trait Request: Coding + Sized + Send + 'static {
type Response: Coding + Send + 'static; type Response: Coding + Send + 'static;
fn respond(&self, rep: Self::Response) -> Vec<u8> { enc_vec(&rep) } }
pub fn respond<R: Request>(_: &R, rep: R::Response) -> Vec<u8> { enc_vec(&rep) }
pub fn respond_with<R: Request>(r: &R, f: impl FnOnce(&R) -> R::Response) -> Vec<u8> {
respond(r, f(r))
} }
pub trait Channel: 'static { pub trait Channel: 'static {

View File

@@ -3,10 +3,7 @@ use std::num::NonZeroU64;
use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request; use orchid_api_traits::Request;
use crate::error::OrcResult; use crate::{ExprTicket, Expression, ExtHostReq, HostExtNotif, HostExtReq, OrcResult, SysId, TStrv};
use crate::expr::{Expr, ExprTicket};
use crate::proto::{ExtHostReq, HostExtNotif, HostExtReq};
use crate::system::SysId;
pub type AtomData = Vec<u8>; pub type AtomData = Vec<u8>;
@@ -53,7 +50,7 @@ pub struct Atom {
#[extends(AtomReq, HostExtReq)] #[extends(AtomReq, HostExtReq)]
pub struct CallRef(pub Atom, pub ExprTicket); pub struct CallRef(pub Atom, pub ExprTicket);
impl Request for CallRef { impl Request for CallRef {
type Response = Expr; type Response = Expression;
} }
/// Attempt to apply an atom as a function, consuming the atom and enabling the /// Attempt to apply an atom as a function, consuming the atom and enabling the
@@ -63,7 +60,7 @@ impl Request for CallRef {
#[extends(AtomReq, HostExtReq)] #[extends(AtomReq, HostExtReq)]
pub struct FinalCall(pub Atom, pub ExprTicket); pub struct FinalCall(pub Atom, pub ExprTicket);
impl Request for FinalCall { impl Request for FinalCall {
type Response = Expr; type Response = Expression;
} }
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)] #[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
@@ -80,34 +77,24 @@ impl Request for DeserAtom {
type Response = Atom; type Response = Atom;
} }
/// Determine whether two atoms are identical for the purposes of macro
/// application. If a given atom is never generated by macros or this relation
/// is difficult to define, the module can return false
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
#[extends(AtomReq, HostExtReq)]
pub struct AtomSame(pub Atom, pub Atom);
impl Request for AtomSame {
type Response = bool;
}
/// A request blindly routed to the system that provides an atom. /// A request blindly routed to the system that provides an atom.
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)] #[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
#[extends(AtomReq, HostExtReq)] #[extends(AtomReq, HostExtReq)]
pub struct Fwded(pub Atom, pub Vec<u8>); pub struct Fwded(pub Atom, pub TStrv, pub Vec<u8>);
impl Request for Fwded { impl Request for Fwded {
type Response = Vec<u8>; type Response = Option<Vec<u8>>;
} }
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)] #[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
#[extends(ExtHostReq)] #[extends(ExtHostReq)]
pub struct Fwd(pub Atom, pub Vec<u8>); pub struct Fwd(pub Atom, pub TStrv, pub Vec<u8>);
impl Request for Fwd { impl Request for Fwd {
type Response = Vec<u8>; type Response = Option<Vec<u8>>;
} }
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
pub enum NextStep { pub enum NextStep {
Continue(Expr), Continue(Expression),
Halt, Halt,
} }
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)] #[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
@@ -138,7 +125,6 @@ impl Request for AtomPrint {
pub enum AtomReq { pub enum AtomReq {
CallRef(CallRef), CallRef(CallRef),
FinalCall(FinalCall), FinalCall(FinalCall),
AtomSame(AtomSame),
Fwded(Fwded), Fwded(Fwded),
Command(Command), Command(Command),
AtomPrint(AtomPrint), AtomPrint(AtomPrint),
@@ -149,8 +135,7 @@ impl AtomReq {
/// subclass have at least one atom argument. /// subclass have at least one atom argument.
pub fn get_atom(&self) -> &Atom { pub fn get_atom(&self) -> &Atom {
match self { match self {
Self::AtomSame(AtomSame(a, ..)) Self::CallRef(CallRef(a, ..))
| Self::CallRef(CallRef(a, ..))
| Self::Command(Command(a)) | Self::Command(Command(a))
| Self::FinalCall(FinalCall(a, ..)) | Self::FinalCall(FinalCall(a, ..))
| Self::Fwded(Fwded(a, ..)) | Self::Fwded(Fwded(a, ..))

View File

@@ -3,8 +3,7 @@ use std::sync::Arc;
use orchid_api_derive::Coding; use orchid_api_derive::Coding;
use crate::interner::TStr; use crate::{Location, TStr};
use crate::location::Location;
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)] #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)]
pub struct ErrId(pub NonZeroU16); pub struct ErrId(pub NonZeroU16);

View File

@@ -3,12 +3,7 @@ use std::num::NonZeroU64;
use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request; use orchid_api_traits::Request;
use crate::atom::Atom; use crate::{Atom, ExtHostNotif, ExtHostReq, Location, OrcError, SysId, TStrv};
use crate::error::OrcError;
use crate::interner::TStrv;
use crate::location::Location;
use crate::proto::{ExtHostNotif, ExtHostReq};
use crate::system::SysId;
/// An arbitrary ID associated with an expression on the host side. Incoming /// An arbitrary ID associated with an expression on the host side. Incoming
/// tickets always come with some lifetime guarantee, which can be extended with /// tickets always come with some lifetime guarantee, which can be extended with
@@ -57,11 +52,11 @@ pub struct Move {
/// [crate::atom::Call] or [crate::atom::CallRef], or a constant in the /// [crate::atom::Call] or [crate::atom::CallRef], or a constant in the
/// [crate::tree::Tree]. /// [crate::tree::Tree].
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
pub enum Clause { pub enum ExpressionKind {
/// Apply the lhs as a function to the rhs /// Apply the lhs as a function to the rhs
Call(Box<Expr>, Box<Expr>), Call(Box<Expression>, Box<Expression>),
/// Lambda function. The number operates as an argument name /// Lambda function. The number operates as an argument name
Lambda(u64, Box<Expr>), Lambda(u64, Box<Expression>),
/// Binds the argument passed to the lambda with the same ID in the same /// Binds the argument passed to the lambda with the same ID in the same
/// template /// template
Arg(u64), Arg(u64),
@@ -70,16 +65,12 @@ pub enum Clause {
Slot(ExprTicket), Slot(ExprTicket),
/// The lhs must be fully processed before the rhs can be processed. /// The lhs must be fully processed before the rhs can be processed.
/// Equivalent to Haskell's function of the same name /// Equivalent to Haskell's function of the same name
Seq(Box<Expr>, Box<Expr>), Seq(Box<Expression>, Box<Expression>),
/// Insert a new atom in the tree. When the clause is used in the const tree, /// Insert a new atom in the tree. When the clause is used in the const tree,
/// the atom must be trivial. This is always a newly constructed atom, if you /// the atom must be trivial. This is always a newly constructed atom, if you
/// want to reference an existing atom, use the corresponding [ExprTicket]. /// want to reference an existing atom, use the corresponding [ExprTicket].
/// Because the atom is newly constructed, it also must belong to this system. /// Because the atom is newly constructed, it also must belong to this system.
NewAtom(Atom), NewAtom(Atom),
/// An atom, specifically an atom that already exists. This form is only ever
/// returned from [Inspect], and it's borrowed from the expression being
/// inspected.
Atom(ExprTicket, Atom),
/// A reference to a constant /// A reference to a constant
Const(TStrv), Const(TStrv),
/// A static runtime error. /// A static runtime error.
@@ -87,22 +78,35 @@ pub enum Clause {
} }
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
pub struct Expr { pub struct Expression {
pub clause: Clause, pub kind: ExpressionKind,
pub location: Location, pub location: Location,
} }
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
pub struct Details { pub enum InspectedKind {
pub expr: Expr, Atom(Atom),
Bottom(Vec<OrcError>),
Opaque,
}
#[derive(Clone, Debug, Coding)]
pub struct Inspected {
pub kind: InspectedKind,
pub location: Location,
pub refcount: u32, pub refcount: u32,
} }
/// Obtain information about an expression. Used to act upon arguments by
/// resolving shallowly and operating on the atom, but also usable for
/// reflection
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)] #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
#[extends(ExprReq, ExtHostReq)] #[extends(ExprReq, ExtHostReq)]
pub struct Inspect(pub ExprTicket); pub struct Inspect {
pub target: ExprTicket,
}
impl Request for Inspect { impl Request for Inspect {
type Response = Details; type Response = Inspected;
} }
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)] #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]

View File

@@ -4,7 +4,7 @@ use std::sync::Arc;
use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request; use orchid_api_traits::Request;
use crate::proto::{ExtHostReq, HostExtReq}; use crate::{ExtHostReq, HostExtReq};
/// Intern requests sent by the replica to the master. These requests are /// Intern requests sent by the replica to the master. These requests are
/// repeatable. /// repeatable.

46
orchid-api/src/lexer.rs Normal file
View File

@@ -0,0 +1,46 @@
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};
/// - All ranges contain at least one character
/// - All ranges are in increasing characeter order
/// - There are excluded characters between each pair of neighboring ranges
#[derive(Clone, Debug, Coding)]
pub struct CharFilter(pub Vec<RangeInclusive<char>>);
#[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(HostExtReq)]
pub struct LexExpr {
pub sys: SysId,
pub id: ParsId,
pub text: TStr,
pub pos: u32,
}
impl Request for LexExpr {
type Response = Option<OrcResult<LexedExpr>>;
}
#[derive(Clone, Debug, Coding)]
pub struct LexedExpr {
pub pos: u32,
pub expr: TokenTree,
}
#[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(ExtHostReq)]
pub struct SubLex {
pub id: ParsId,
pub pos: u32,
}
impl Request for SubLex {
type Response = Option<SubLexed>;
}
#[derive(Clone, Debug, Coding)]
pub struct SubLexed {
pub pos: u32,
pub ticket: TreeTicket,
}

View File

@@ -1,35 +1,26 @@
mod lexer;
pub use lexer::*;
mod macros;
pub use macros::*;
mod atom; mod atom;
pub use atom::{ pub use atom::*;
Atom, AtomData, AtomDrop, AtomId, AtomPrint, AtomReq, AtomSame, CallRef, Command, DeserAtom,
FinalCall, Fwd, Fwded, LocalAtom, NextStep, SerializeAtom,
};
mod error; mod error;
pub use error::{ErrId, ErrLocation, OrcError, OrcResult}; pub use error::*;
mod expr; mod expr;
pub use expr::{ pub use expr::*;
Acquire, Clause, Details, Expr, ExprNotif, ExprReq, ExprTicket, Inspect, Move, Release,
};
mod interner; mod interner;
pub use interner::{ pub use interner::*;
ExternStr, ExternStrv, IntReq, InternStr, InternStrv, Retained, Sweep, TStr, TStrv,
};
mod location; mod location;
pub use location::{CodeGenInfo, Location, SourceRange}; pub use location::*;
mod logging; mod logging;
pub use logging::{Log, LogStrategy}; pub use logging::*;
mod parser; mod parser;
pub use parser::{CharFilter, LexExpr, LexedExpr, ParsId, ParseLine, ParserReq, SubLex, SubLexed}; pub use parser::*;
mod proto; mod proto;
pub use proto::{ pub use proto::*;
ExtHostChannel, ExtHostNotif, ExtHostReq, ExtMsgSet, ExtensionHeader, HostExtChannel,
HostExtNotif, HostExtReq, HostHeader, HostMsgSet, Ping,
};
mod system; mod system;
pub use system::{NewSystem, SysDeclId, SysId, SysReq, SystemDecl, SystemDrop, SystemInst}; pub use system::*;
mod tree; mod tree;
pub use tree::{ pub use tree::*;
CompName, GetMember, Item, ItemKind, Member, MemberKind, Module, Paren, Token, TokenTree, TreeId,
TreeTicket,
};
mod vfs; mod vfs;
pub use vfs::{EagerVfs, GetVfs, Loaded, VfsId, VfsRead, VfsReq}; pub use vfs::*;

View File

@@ -2,13 +2,17 @@ use std::ops::Range;
use orchid_api_derive::Coding; use orchid_api_derive::Coding;
use crate::interner::TStrv; use crate::{TStr, TStrv};
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
pub enum Location { pub enum Location {
/// Location inaccessible. Locations are always debugging aids and never
/// mandatory.
None, None,
/// Associated with a slot when wrapped in an expression.
SlotTarget,
/// Used in functions to denote the generated code that carries on the /// Used in functions to denote the generated code that carries on the
/// location of the call. Not allowed in the const tree. /// location of the call.
Inherit, Inherit,
Gen(CodeGenInfo), Gen(CodeGenInfo),
/// Range and file /// Range and file
@@ -26,5 +30,5 @@ pub struct SourceRange {
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
pub struct CodeGenInfo { pub struct CodeGenInfo {
pub generator: TStrv, pub generator: TStrv,
pub details: String, pub details: TStr,
} }

View File

@@ -1,6 +1,6 @@
use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_derive::{Coding, Hierarchy};
use crate::proto::ExtHostNotif; use crate::ExtHostNotif;
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
pub enum LogStrategy { pub enum LogStrategy {

82
orchid-api/src/macros.rs Normal file
View File

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

View File

@@ -1,70 +1,19 @@
use std::num::NonZeroU64; use std::num::NonZeroU64;
use std::ops::RangeInclusive;
use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request; use orchid_api_traits::Request;
use crate::error::OrcResult; use crate::{Comment, HostExtReq, OrcResult, SysId, TokenTree};
use crate::interner::TStr;
use crate::proto::{ExtHostReq, HostExtReq};
use crate::system::SysId;
use crate::tree::{TokenTree, TreeTicket};
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)] #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)]
pub struct ParsId(pub NonZeroU64); pub struct ParsId(pub NonZeroU64);
/// - All ranges contain at least one character
/// - All ranges are in increasing characeter order
/// - There are excluded characters between each pair of neighboring ranges
#[derive(Clone, Debug, Coding)]
pub struct CharFilter(pub Vec<RangeInclusive<char>>);
#[derive(Clone, Debug, Coding, Hierarchy)] #[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(HostExtReq)] #[extends(HostExtReq)]
#[extendable]
pub enum ParserReq {
LexExpr(LexExpr),
ParseLine(ParseLine),
}
#[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(ParserReq, HostExtReq)]
pub struct LexExpr {
pub sys: SysId,
pub id: ParsId,
pub text: TStr,
pub pos: u32,
}
impl Request for LexExpr {
type Response = Option<OrcResult<LexedExpr>>;
}
#[derive(Clone, Debug, Coding)]
pub struct LexedExpr {
pub pos: u32,
pub expr: TokenTree,
}
#[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(ExtHostReq)]
pub struct SubLex {
pub id: ParsId,
pub pos: u32,
}
impl Request for SubLex {
type Response = Option<SubLexed>;
}
#[derive(Clone, Debug, Coding)]
pub struct SubLexed {
pub pos: u32,
pub ticket: TreeTicket,
}
#[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(ParserReq, HostExtReq)]
pub struct ParseLine { pub struct ParseLine {
pub sys: SysId, pub sys: SysId,
pub comments: Vec<Comment>,
pub exported: bool,
pub line: Vec<TokenTree>, pub line: Vec<TokenTree>,
} }
impl Request for ParseLine { impl Request for ParseLine {

View File

@@ -27,17 +27,16 @@ use std::io::{Read, Write};
use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::{read_exact, write_exact, Channel, Decode, Encode, MsgSet, Request}; use orchid_api_traits::{read_exact, write_exact, Channel, Decode, Encode, MsgSet, Request};
use crate::logging::{self, LogStrategy}; use crate::{atom, expr, interner, lexer, logging, macros, parser, system, tree, vfs};
use crate::{atom, expr, interner, parser, system, tree, vfs};
static HOST_INTRO: &[u8] = b"Orchid host, binary API v0\n"; static HOST_INTRO: &[u8] = b"Orchid host, binary API v0\n";
pub struct HostHeader { pub struct HostHeader {
pub log_strategy: LogStrategy, pub log_strategy: logging::LogStrategy,
} }
impl Decode for HostHeader { impl Decode for HostHeader {
fn decode<R: Read + ?Sized>(read: &mut R) -> Self { fn decode<R: Read + ?Sized>(read: &mut R) -> Self {
read_exact(read, HOST_INTRO); read_exact(read, HOST_INTRO);
Self { log_strategy: LogStrategy::decode(read) } Self { log_strategy: logging::LogStrategy::decode(read) }
} }
} }
impl Encode for HostHeader { impl Encode for HostHeader {
@@ -79,8 +78,10 @@ pub enum ExtHostReq {
Ping(Ping), Ping(Ping),
IntReq(interner::IntReq), IntReq(interner::IntReq),
Fwd(atom::Fwd), Fwd(atom::Fwd),
SysFwd(system::SysFwd),
ExprReq(expr::ExprReq), ExprReq(expr::ExprReq),
SubLex(parser::SubLex), SubLex(lexer::SubLex),
RunMacros(macros::RunMacros),
} }
/// Notifications sent from the extension to the host /// Notifications sent from the extension to the host
@@ -107,9 +108,11 @@ pub enum HostExtReq {
Sweep(interner::Sweep), Sweep(interner::Sweep),
AtomReq(atom::AtomReq), AtomReq(atom::AtomReq),
DeserAtom(atom::DeserAtom), DeserAtom(atom::DeserAtom),
ParserReq(parser::ParserReq), LexExpr(lexer::LexExpr),
ParseLine(parser::ParseLine),
GetMember(tree::GetMember), GetMember(tree::GetMember),
VfsReq(vfs::VfsReq), VfsReq(vfs::VfsReq),
ApplyMacro(macros::ApplyMacro),
} }
/// Notifications sent from the host to the extension /// Notifications sent from the host to the extension
@@ -147,13 +150,12 @@ impl MsgSet for HostMsgSet {
mod tests { mod tests {
use orchid_api_traits::enc_vec; use orchid_api_traits::enc_vec;
use ordered_float::NotNan; use ordered_float::NotNan;
use system::{SysDeclId, SystemDecl};
use super::*; use super::*;
#[test] #[test]
fn host_header_enc() { fn host_header_enc() {
let hh = HostHeader { log_strategy: LogStrategy::File("SomeFile".to_string()) }; let hh = HostHeader { log_strategy: logging::LogStrategy::File("SomeFile".to_string()) };
let mut enc = &enc_vec(&hh)[..]; let mut enc = &enc_vec(&hh)[..];
eprintln!("Encoded to {enc:?}"); eprintln!("Encoded to {enc:?}");
HostHeader::decode(&mut enc); HostHeader::decode(&mut enc);
@@ -164,8 +166,8 @@ mod tests {
fn ext_header_enc() { fn ext_header_enc() {
let eh = ExtensionHeader { let eh = ExtensionHeader {
name: "my_extension".to_string(), name: "my_extension".to_string(),
systems: vec![SystemDecl { systems: vec![system::SystemDecl {
id: SysDeclId(1.try_into().unwrap()), id: system::SysDeclId(1.try_into().unwrap()),
name: "misc".to_string(), name: "misc".to_string(),
depends: vec!["std".to_string()], depends: vec!["std".to_string()],
priority: NotNan::new(1f64).unwrap(), priority: NotNan::new(1f64).unwrap(),

View File

@@ -5,10 +5,7 @@ use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request; use orchid_api_traits::Request;
use ordered_float::NotNan; use ordered_float::NotNan;
use crate::interner::TStr; use crate::{CharFilter, ExtHostReq, HostExtNotif, HostExtReq, MemberKind, TStr};
use crate::parser::CharFilter;
use crate::proto::{HostExtNotif, HostExtReq};
use crate::tree::MemberKind;
/// ID of a system type /// ID of a system type
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)] #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)]
@@ -72,9 +69,24 @@ pub struct SystemInst {
#[extends(HostExtNotif)] #[extends(HostExtNotif)]
pub struct SystemDrop(pub SysId); pub struct SystemDrop(pub SysId);
#[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(SysReq, HostExtReq)]
pub struct SysFwded(pub SysId, pub Vec<u8>);
impl Request for SysFwded {
type Response = Vec<u8>;
}
#[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(ExtHostReq)]
pub struct SysFwd(pub SysId, pub Vec<u8>);
impl Request for SysFwd {
type Response = Vec<u8>;
}
#[derive(Clone, Debug, Coding, Hierarchy)] #[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(HostExtReq)] #[extends(HostExtReq)]
#[extendable] #[extendable]
pub enum SysReq { pub enum SysReq {
NewSystem(NewSystem), NewSystem(NewSystem),
SysFwded(SysFwded),
} }

View File

@@ -4,13 +4,11 @@ use std::sync::Arc;
use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request; use orchid_api_traits::Request;
use ordered_float::NotNan;
use crate::error::OrcError; use crate::{
use crate::interner::{TStr, TStrv}; Atom, Expression, HostExtReq, Location, MacroBlock, OrcError, Placeholder, SysId, TStr, TStrv,
use crate::location::Location; };
use crate::proto::HostExtReq;
use crate::system::SysId;
use crate::{Atom, Expr};
/// A token tree from a lexer recursion request. Its lifetime is the lex call, /// A token tree from a lexer recursion request. Its lifetime is the lex call,
/// the lexer can include it in its output or discard it by implication. /// the lexer can include it in its output or discard it by implication.
@@ -31,7 +29,7 @@ pub struct TokenTree {
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
pub enum Token { pub enum Token {
/// Lambda function head, from the opening \ until the beginning of the body. /// Lambda function head, from the opening \ until the beginning of the body.
Lambda(Vec<TokenTree>), LambdaHead(Vec<TokenTree>),
/// A name segment or an operator. /// A name segment or an operator.
Name(TStr), Name(TStr),
/// :: /// ::
@@ -49,9 +47,13 @@ pub enum Token {
Bottom(Vec<OrcError>), Bottom(Vec<OrcError>),
/// A comment /// A comment
Comment(Arc<String>), Comment(Arc<String>),
/// Placeholder
Ph(Placeholder),
/// Macro block head
Macro(Option<NotNan<f64>>),
} }
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)] #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Coding)]
pub enum Paren { pub enum Paren {
Round, Round,
Square, Square,
@@ -64,45 +66,42 @@ pub struct TreeId(pub NonZeroU64);
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
pub struct Item { pub struct Item {
pub location: Location, pub location: Location,
pub comments: Vec<(Arc<String>, Location)>, pub comments: Vec<Comment>,
pub kind: ItemKind, pub kind: ItemKind,
} }
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
pub enum ItemKind { pub enum ItemKind {
Member(Member), Member(Member),
Raw(Vec<TokenTree>), Macro(MacroBlock),
Export(TStr), Export(TStr),
Import(CompName), Import(TStrv),
}
#[derive(Clone, Debug, Coding)]
pub struct Comment {
pub text: TStr,
pub location: Location,
} }
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
pub struct Member { pub struct Member {
pub name: TStr, pub name: TStr,
pub exported: bool,
pub kind: MemberKind, pub kind: MemberKind,
} }
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
pub enum MemberKind { pub enum MemberKind {
Const(Expr), Const(Expression),
Module(Module), Module(Module),
Lazy(TreeId), Lazy(TreeId),
} }
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
pub struct Module { pub struct Module {
pub imports: Vec<TStrv>,
pub items: Vec<Item>, pub items: Vec<Item>,
} }
#[derive(Clone, Debug, Coding)]
pub struct CompName {
pub path: Vec<TStr>,
pub name: Option<TStr>,
pub location: Location,
}
#[derive(Clone, Copy, Debug, Coding, Hierarchy)] #[derive(Clone, Copy, Debug, Coding, Hierarchy)]
#[extends(HostExtReq)] #[extends(HostExtReq)]
pub struct GetMember(pub SysId, pub TreeId); pub struct GetMember(pub SysId, pub TreeId);

View File

@@ -12,6 +12,7 @@ hashbrown = "0.14.3"
itertools = "0.13.0" itertools = "0.13.0"
lazy_static = "1.4.0" lazy_static = "1.4.0"
never = "0.1.0" never = "0.1.0"
num-traits = "0.2.19"
orchid-api = { version = "0.1.0", path = "../orchid-api" } orchid-api = { version = "0.1.0", path = "../orchid-api" }
orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" } orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" }
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" } orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }

View File

@@ -1 +0,0 @@

View File

@@ -1,3 +1,5 @@
use std::fmt;
use std::ops::Add;
use std::sync::Arc; use std::sync::Arc;
use itertools::Itertools; use itertools::Itertools;
@@ -65,16 +67,86 @@ impl PartialEq for OrcErr {
impl From<OrcErr> for Vec<OrcErr> { impl From<OrcErr> for Vec<OrcErr> {
fn from(value: OrcErr) -> Self { vec![value] } fn from(value: OrcErr) -> Self { vec![value] }
} }
impl fmt::Display for OrcErr {
pub fn errv_to_apiv<'a>(errv: impl IntoIterator<Item = &'a OrcErr>) -> Vec<api::OrcError> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
errv.into_iter().map(OrcErr::to_api).collect_vec() let pstr = self.positions.iter().map(|p| format!("{p:?}")).join("; ");
write!(f, "{}: {} @ {}", self.description, self.message, pstr)
}
} }
pub fn errv_from_apiv<'a>(err: impl IntoIterator<Item = &'a api::OrcError>) -> Vec<OrcErr> { #[derive(Clone, Debug)]
err.into_iter().map(OrcErr::from_api).collect() pub struct EmptyErrv;
impl fmt::Display for EmptyErrv {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "OrcErrv must not be empty")
}
} }
pub type OrcRes<T> = Result<T, Vec<OrcErr>>; #[derive(Clone, Debug)]
pub struct OrcErrv(Vec<OrcErr>);
impl OrcErrv {
pub fn new(errors: impl IntoIterator<Item = OrcErr>) -> Result<Self, EmptyErrv> {
let v = errors.into_iter().collect_vec();
if v.is_empty() { Err(EmptyErrv) } else { Ok(Self(v)) }
}
#[must_use]
pub fn to_api(&self) -> Vec<api::OrcError> { self.0.iter().map(OrcErr::to_api).collect_vec() }
#[must_use]
pub fn from_api<'a>(apiv: impl IntoIterator<Item = &'a api::OrcError>) -> Self {
let v = apiv.into_iter().map(OrcErr::from_api).collect_vec();
assert!(!v.is_empty(), "Error condition with 0 errors");
Self(v)
}
#[must_use]
pub fn extended<T>(mut self, errors: impl IntoIterator<Item = T>) -> Self
where Self: Extend<T> {
self.extend(errors);
self
}
#[must_use]
pub fn len(&self) -> usize { self.0.len() }
#[must_use]
pub fn is_empty(&self) -> bool { self.len() == 0 }
#[must_use]
pub fn any(&self, f: impl FnMut(&OrcErr) -> bool) -> bool { self.0.iter().any(f) }
#[must_use]
pub fn keep_only(self, f: impl FnMut(&OrcErr) -> bool) -> Option<Self> {
let v = self.0.into_iter().filter(f).collect_vec();
if v.is_empty() { None } else { Some(Self(v)) }
}
#[must_use]
pub fn one(&self) -> Option<&OrcErr> { (self.0.len() == 1).then(|| &self.0[9]) }
pub fn pos_iter(&self) -> impl Iterator<Item = ErrPos> + '_ {
self.0.iter().flat_map(|e| e.positions.iter().cloned())
}
}
impl From<OrcErr> for OrcErrv {
fn from(value: OrcErr) -> Self { Self(vec![value]) }
}
impl Add for OrcErrv {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output { Self(self.0.into_iter().chain(rhs.0).collect_vec()) }
}
impl Extend<OrcErr> for OrcErrv {
fn extend<T: IntoIterator<Item = OrcErr>>(&mut self, iter: T) { self.0.extend(iter) }
}
impl Extend<OrcErrv> for OrcErrv {
fn extend<T: IntoIterator<Item = OrcErrv>>(&mut self, iter: T) {
self.0.extend(iter.into_iter().flatten())
}
}
impl IntoIterator for OrcErrv {
type IntoIter = std::vec::IntoIter<OrcErr>;
type Item = OrcErr;
fn into_iter(self) -> Self::IntoIter { self.0.into_iter() }
}
impl fmt::Display for OrcErrv {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0.iter().join("\n"))
}
}
pub type OrcRes<T> = Result<T, OrcErrv>;
pub fn mk_err( pub fn mk_err(
description: Tok<String>, description: Tok<String>,
@@ -88,6 +160,14 @@ pub fn mk_err(
} }
} }
pub trait Reporter { pub fn mk_errv(
fn report(&self, e: OrcErr); description: Tok<String>,
message: impl AsRef<str>,
posv: impl IntoIterator<Item = ErrPos>,
) -> OrcErrv {
mk_err(description, message, posv).into()
}
pub trait Reporter {
fn report(&self, e: impl Into<OrcErrv>);
} }

View File

@@ -3,13 +3,14 @@ use std::hash::BuildHasher as _;
use std::num::NonZeroU64; use std::num::NonZeroU64;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::sync::{atomic, Arc, Mutex, MutexGuard}; use std::sync::{atomic, Arc, Mutex, MutexGuard};
use std::{fmt, hash}; use std::{fmt, hash, mem};
use hashbrown::{HashMap, HashSet}; use hashbrown::{HashMap, HashSet};
use itertools::Itertools as _; use itertools::Itertools as _;
use orchid_api_traits::{Decode, Encode, Request}; use orchid_api_traits::{Decode, Encode, Request};
use crate::api; use crate::api;
use orchid_api_traits::{ApiEquiv, FromApi, ToApi};
use crate::reqnot::{DynRequester, Requester}; use crate::reqnot::{DynRequester, Requester};
/// Clippy crashes while verifying `Tok: Sized` without this and I cba to create /// Clippy crashes while verifying `Tok: Sized` without this and I cba to create
@@ -62,7 +63,7 @@ impl<T: Interned + Decode> Decode for Tok<T> {
fn decode<R: std::io::Read + ?Sized>(read: &mut R) -> Self { intern(&T::decode(read)) } fn decode<R: std::io::Read + ?Sized>(read: &mut R) -> Self { intern(&T::decode(read)) }
} }
pub trait Interned: Eq + hash::Hash + Clone + Internable<Interned = Self> { pub trait Interned: Eq + hash::Hash + Clone + fmt::Debug + Internable<Interned = Self> {
type Marker: InternMarker<Interned = Self> + Sized; type Marker: InternMarker<Interned = Self> + Sized;
fn intern( fn intern(
self: Arc<Self>, self: Arc<Self>,
@@ -71,7 +72,7 @@ pub trait Interned: Eq + hash::Hash + Clone + Internable<Interned = Self> {
fn bimap(interner: &mut TypedInterners) -> &mut Bimap<Self>; fn bimap(interner: &mut TypedInterners) -> &mut Bimap<Self>;
} }
pub trait Internable { pub trait Internable: fmt::Debug {
type Interned: Interned; type Interned: Interned;
fn get_owned(&self) -> Arc<Self::Interned>; fn get_owned(&self) -> Arc<Self::Interned>;
} }
@@ -96,7 +97,6 @@ impl Interned for String {
} }
fn bimap(interners: &mut TypedInterners) -> &mut Bimap<Self> { &mut interners.strings } fn bimap(interners: &mut TypedInterners) -> &mut Bimap<Self> { &mut interners.strings }
} }
impl InternMarker for api::TStr { impl InternMarker for api::TStr {
type Interned = String; type Interned = String;
fn resolve( fn resolve(
@@ -108,17 +108,27 @@ impl InternMarker for api::TStr {
fn get_id(self) -> NonZeroU64 { self.0 } fn get_id(self) -> NonZeroU64 { self.0 }
fn from_id(id: NonZeroU64) -> Self { Self(id) } fn from_id(id: NonZeroU64) -> Self { Self(id) }
} }
impl Internable for str { impl Internable for str {
type Interned = String; type Interned = String;
fn get_owned(&self) -> Arc<Self::Interned> { Arc::new(self.to_string()) } fn get_owned(&self) -> Arc<Self::Interned> { Arc::new(self.to_string()) }
} }
impl Internable for String { impl Internable for String {
type Interned = String; type Interned = String;
fn get_owned(&self) -> Arc<Self::Interned> { Arc::new(self.to_string()) } fn get_owned(&self) -> Arc<Self::Interned> { Arc::new(self.to_string()) }
} }
impl ApiEquiv for Tok<String> {
type Api = api::TStr;
}
impl ToApi for Tok<String> {
type Ctx = ();
fn to_api(&self, _: &mut Self::Ctx) -> Self::Api { self.marker() }
}
impl FromApi for Tok<String> {
type Ctx = ();
fn from_api(api: &Self::Api, _: &mut Self::Ctx) -> Self { deintern(*api) }
}
impl Interned for Vec<Tok<String>> { impl Interned for Vec<Tok<String>> {
type Marker = api::TStrv; type Marker = api::TStrv;
fn intern( fn intern(
@@ -129,7 +139,6 @@ impl Interned for Vec<Tok<String>> {
} }
fn bimap(interners: &mut TypedInterners) -> &mut Bimap<Self> { &mut interners.vecs } fn bimap(interners: &mut TypedInterners) -> &mut Bimap<Self> { &mut interners.vecs }
} }
impl InternMarker for api::TStrv { impl InternMarker for api::TStrv {
type Interned = Vec<Tok<String>>; type Interned = Vec<Tok<String>>;
fn resolve( fn resolve(
@@ -143,30 +152,37 @@ impl InternMarker for api::TStrv {
fn get_id(self) -> NonZeroU64 { self.0 } fn get_id(self) -> NonZeroU64 { self.0 }
fn from_id(id: NonZeroU64) -> Self { Self(id) } fn from_id(id: NonZeroU64) -> Self { Self(id) }
} }
impl Internable for [Tok<String>] { impl Internable for [Tok<String>] {
type Interned = Vec<Tok<String>>; type Interned = Vec<Tok<String>>;
fn get_owned(&self) -> Arc<Self::Interned> { Arc::new(self.to_vec()) } fn get_owned(&self) -> Arc<Self::Interned> { Arc::new(self.to_vec()) }
} }
impl Internable for Vec<Tok<String>> { impl Internable for Vec<Tok<String>> {
type Interned = Vec<Tok<String>>; type Interned = Vec<Tok<String>>;
fn get_owned(&self) -> Arc<Self::Interned> { Arc::new(self.to_vec()) } fn get_owned(&self) -> Arc<Self::Interned> { Arc::new(self.to_vec()) }
} }
impl Internable for Vec<api::TStr> { impl Internable for Vec<api::TStr> {
type Interned = Vec<Tok<String>>; type Interned = Vec<Tok<String>>;
fn get_owned(&self) -> Arc<Self::Interned> { fn get_owned(&self) -> Arc<Self::Interned> {
Arc::new(self.iter().map(|ts| deintern(*ts)).collect()) Arc::new(self.iter().map(|ts| deintern(*ts)).collect())
} }
} }
impl Internable for [api::TStr] { impl Internable for [api::TStr] {
type Interned = Vec<Tok<String>>; type Interned = Vec<Tok<String>>;
fn get_owned(&self) -> Arc<Self::Interned> { fn get_owned(&self) -> Arc<Self::Interned> {
Arc::new(self.iter().map(|ts| deintern(*ts)).collect()) Arc::new(self.iter().map(|ts| deintern(*ts)).collect())
} }
} }
impl ApiEquiv for Tok<Vec<Tok<String>>> {
type Api = api::TStrv;
}
impl ToApi for Tok<Vec<Tok<String>>> {
type Ctx = ();
fn to_api(&self, _: &mut Self::Ctx) -> Self::Api { self.marker() }
}
impl FromApi for Tok<Vec<Tok<String>>> {
type Ctx = ();
fn from_api(api: &Self::Api, _: &mut Self::Ctx) -> Self { deintern(*api) }
}
/// The number of references held to any token by the interner. /// The number of references held to any token by the interner.
const BASE_RC: usize = 3; const BASE_RC: usize = 3;
@@ -262,8 +278,10 @@ pub fn init_replica(req: impl DynRequester<Transfer = api::IntReq> + 'static) {
} }
pub fn intern<T: Interned>(t: &(impl Internable<Interned = T> + ?Sized)) -> Tok<T> { pub fn intern<T: Interned>(t: &(impl Internable<Interned = T> + ?Sized)) -> Tok<T> {
let mut g = interner();
let data = t.get_owned(); let data = t.get_owned();
let mut g = interner();
let job = format!("{t:?} in {}", if g.master.is_some() { "replica" } else { "master" });
eprintln!("Interning {job}");
let typed = T::bimap(&mut g.interners); let typed = T::bimap(&mut g.interners);
if let Some(tok) = typed.by_value(&data) { if let Some(tok) = typed.by_value(&data) {
return tok; return tok;
@@ -275,6 +293,8 @@ pub fn intern<T: Interned>(t: &(impl Internable<Interned = T> + ?Sized)) -> Tok<
}; };
let tok = Tok::new(data, marker); let tok = Tok::new(data, marker);
T::bimap(&mut g.interners).insert(tok.clone()); T::bimap(&mut g.interners).insert(tok.clone());
mem::drop(g);
eprintln!("Interned {job}");
tok tok
} }

View File

@@ -5,8 +5,6 @@ pub mod clone;
pub mod combine; pub mod combine;
pub mod event; pub mod event;
pub mod msg; pub mod msg;
// pub mod gen;
pub mod api_utils;
pub mod box_cow; pub mod box_cow;
pub mod char_filter; pub mod char_filter;
pub mod error; pub mod error;
@@ -18,7 +16,10 @@ pub mod logging;
pub mod name; pub mod name;
pub mod number; pub mod number;
pub mod parse; pub mod parse;
pub mod pure_seq;
pub mod reqnot; pub mod reqnot;
pub mod sequence; pub mod sequence;
pub mod tokens; pub mod tokens;
pub mod tree; pub mod tree;
pub mod macros;
mod match_mapping;

View File

@@ -1,15 +1,16 @@
//! Structures that show where code or semantic elements came from //! Structures that show where code or semantic elements came from
use crate::match_mapping;
use std::fmt; use std::fmt;
use std::hash::Hash; use std::hash::Hash;
use std::ops::Range; use std::ops::Range;
use std::sync::Arc;
use trait_set::trait_set; use trait_set::trait_set;
use crate::interner::{deintern, Tok}; use orchid_api_traits::{ApiEquiv, FromApi, ToApi};
use crate::interner::{deintern, intern, Tok};
use crate::name::Sym; use crate::name::Sym;
use crate::{api, sym}; use crate::{api, intern, sym};
trait_set! { trait_set! {
pub trait GetSrc = FnMut(&Sym) -> Tok<String>; pub trait GetSrc = FnMut(&Sym) -> Tok<String>;
@@ -18,6 +19,7 @@ trait_set! {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Pos { pub enum Pos {
None, None,
SlotTarget,
/// Used in functions to denote the generated code that carries on the /// Used in functions to denote the generated code that carries on the
/// location of the call. Not allowed in the const tree. /// location of the call. Not allowed in the const tree.
Inherit, Inherit,
@@ -28,24 +30,6 @@ pub enum Pos {
Range(Range<u32>), Range(Range<u32>),
} }
impl Pos { impl Pos {
pub fn to_api(&self) -> api::Location {
match self {
Self::Inherit => api::Location::Inherit,
Self::None => api::Location::None,
Self::Range(r) => api::Location::Range(r.clone()),
Self::Gen(cgi) => api::Location::Gen(cgi.to_api()),
Self::SourceRange(sr) => api::Location::SourceRange(sr.to_api()),
}
}
pub fn from_api(loc: &api::Location) -> Self {
match loc {
api::Location::Inherit => Self::Inherit,
api::Location::None => Self::None,
api::Location::Range(r) => Self::Range(r.clone()),
api::Location::Gen(cgi) => CodeGenInfo::from_api(cgi).location(),
api::Location::SourceRange(sr) => SourceRange::from_api(sr).location(),
}
}
pub fn pretty_print(&self, get_src: &mut impl GetSrc) -> String { pub fn pretty_print(&self, get_src: &mut impl GetSrc) -> String {
match self { match self {
Self::Gen(g) => g.to_string(), Self::Gen(g) => g.to_string(),
@@ -55,6 +39,32 @@ impl Pos {
} }
} }
} }
impl ApiEquiv for Pos {
type Api = api::Location;
}
impl FromApi for Pos {
type Ctx = ();
fn from_api(api: &Self::Api, ctx: &mut Self::Ctx) -> Self {
match_mapping!(api, api::Location => Pos {
None, Inherit, SlotTarget,
Range(r.clone()),
Gen(cgi => CodeGenInfo::from_api(cgi, &mut ())),
SourceRange(sr => CodeGenInfo::from_api(sr, &mut ()))
})
}
}
impl ToApi for Pos {
type Ctx = ();
fn to_api(&self, ctx: &mut Self::Ctx) -> Self::Api {
match_mapping!(self, Pos => Self::Api {
None, Inherit, SlotTarget,
Range(r.clone()),
Gen(cgi.to_api(ctx)),
SourceRange(sr.to_api(ctx)),
})
}
}
/// Exact source code location. Includes where the code was loaded from, what /// Exact source code location. Includes where the code was loaded from, what
/// the original source code was, and a byte range. /// the original source code was, and a byte range.
@@ -67,12 +77,6 @@ impl SourceRange {
pub fn new(range: &Range<u32>, path: &Sym) -> Self { pub fn new(range: &Range<u32>, path: &Sym) -> Self {
Self { range: range.clone(), path: path.clone() } Self { range: range.clone(), path: path.clone() }
} }
pub fn to_api(&self) -> api::SourceRange {
api::SourceRange { path: self.path.tok().marker(), range: self.range.clone() }
}
pub fn from_api(sr: &api::SourceRange) -> Self {
Self { path: Sym::from_tok(deintern(sr.path)).unwrap(), range: sr.range.clone() }
}
/// Create a dud [SourceRange] for testing. Its value is unspecified and /// Create a dud [SourceRange] for testing. Its value is unspecified and
/// volatile. /// volatile.
pub fn mock() -> Self { Self { range: 0..1, path: sym!(test) } } pub fn mock() -> Self { Self { range: 0..1, path: sym!(test) } }
@@ -85,7 +89,7 @@ impl SourceRange {
/// 0-based index of last byte + 1 /// 0-based index of last byte + 1
pub fn end(&self) -> u32 { self.range.end } pub fn end(&self) -> u32 { self.range.end }
/// Syntactic location /// Syntactic location
pub fn location(&self) -> Pos { Pos::SourceRange(self.clone()) } pub fn pos(&self) -> Pos { Pos::SourceRange(self.clone()) }
/// Transform the numeric byte range /// Transform the numeric byte range
pub fn map_range(&self, map: impl FnOnce(Range<u32>) -> Range<u32>) -> Self { pub fn map_range(&self, map: impl FnOnce(Range<u32>) -> Range<u32>) -> Self {
Self { range: map(self.range()), path: self.path() } Self { range: map(self.range()), path: self.path() }
@@ -99,6 +103,24 @@ impl SourceRange {
(false, _) => format!("{sl}:{sc}..{el}:{ec}"), (false, _) => format!("{sl}:{sc}..{el}:{ec}"),
} }
} }
pub fn zw(path: Sym, pos: u32) -> Self {
Self { path, range: pos..pos }
}
}
impl ApiEquiv for SourceRange {
type Api = api::SourceRange;
}
impl FromApi for SourceRange {
type Ctx = ();
fn from_api(api: &Self::Api, ctx: &mut Self::Ctx) -> Self {
Self { path: Sym::from_api(&api.path, ctx), range: api.range.clone() }
}
}
impl ToApi for SourceRange {
type Ctx = ();
fn to_api(&self, ctx: &mut Self::Ctx) -> Self::Api {
api::SourceRange { path: self.path.to_api(ctx), range: self.range.clone() }
}
} }
/// Information about a code generator attached to the generated code /// Information about a code generator attached to the generated code
@@ -107,26 +129,17 @@ pub struct CodeGenInfo {
/// formatted like a Rust namespace /// formatted like a Rust namespace
pub generator: Sym, pub generator: Sym,
/// Unformatted user message with relevant circumstances and parameters /// Unformatted user message with relevant circumstances and parameters
pub details: Arc<String>, pub details: Tok<String>,
} }
impl CodeGenInfo { impl CodeGenInfo {
/// A codegen marker with no user message and parameters /// A codegen marker with no user message and parameters
pub fn no_details(generator: Sym) -> Self { Self { generator, details: Arc::new(String::new()) } } pub fn no_details(generator: Sym) -> Self { Self { generator, details: intern!(str: "") } }
/// A codegen marker with a user message or parameters /// A codegen marker with a user message or parameters
pub fn details(generator: Sym, details: impl AsRef<str>) -> Self { pub fn details(generator: Sym, details: impl AsRef<str>) -> Self {
Self { generator, details: Arc::new(details.as_ref().to_string()) } Self { generator, details: intern(details.as_ref()) }
} }
/// Syntactic location /// Syntactic location
pub fn location(&self) -> Pos { Pos::Gen(self.clone()) } pub fn pos(&self) -> Pos { Pos::Gen(self.clone()) }
pub fn to_api(&self) -> api::CodeGenInfo {
api::CodeGenInfo { generator: self.generator.tok().marker(), details: self.details.to_string() }
}
pub fn from_api(cgi: &api::CodeGenInfo) -> Self {
Self {
generator: Sym::from_tok(deintern(cgi.generator)).unwrap(),
details: Arc::new(cgi.details.clone()),
}
}
} }
impl fmt::Debug for CodeGenInfo { impl fmt::Debug for CodeGenInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "CodeGenInfo({self})") } fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "CodeGenInfo({self})") }
@@ -137,6 +150,24 @@ impl fmt::Display for CodeGenInfo {
if !self.details.is_empty() { write!(f, ", details: {}", self.details) } else { write!(f, ".") } if !self.details.is_empty() { write!(f, ", details: {}", self.details) } else { write!(f, ".") }
} }
} }
impl ApiEquiv for CodeGenInfo {
type Api = api::CodeGenInfo;
}
impl FromApi for CodeGenInfo {
type Ctx = ();
fn from_api(api: &Self::Api, ctx: &mut Self::Ctx) -> Self {
Self {
generator: Sym::from_api(&api.generator, ctx),
details: Tok::from_api(&api.details, ctx),
}
}
}
impl ToApi for CodeGenInfo {
type Ctx = ();
fn to_api(&self, ctx: &mut Self::Ctx) -> Self::Api {
api::CodeGenInfo { generator: self.generator.to_api(ctx), details: self.details.to_api(ctx) }
}
}
#[must_use] #[must_use]
fn pos2lc(s: &str, i: u32) -> (u32, u32) { fn pos2lc(s: &str, i: u32) -> (u32, u32) {

View File

@@ -14,7 +14,7 @@ impl Logger {
pub fn log(&self, msg: impl AsRef<str>) { writeln!(self, "{}", msg.as_ref()) } pub fn log(&self, msg: impl AsRef<str>) { writeln!(self, "{}", msg.as_ref()) }
pub fn strat(&self) -> api::LogStrategy { self.0.clone() } pub fn strat(&self) -> api::LogStrategy { self.0.clone() }
pub fn log_buf(&self, event: impl AsRef<str>, buf: &[u8]) { pub fn log_buf(&self, event: impl AsRef<str>, buf: &[u8]) {
if !std::env::var("ORCHID_LOG_BUFFERS").unwrap().is_empty() { if std::env::var("ORCHID_LOG_BUFFERS").is_ok_and(|v| !v.is_empty()) {
writeln!(self, "{}: [{}]", event.as_ref(), buf.iter().map(|b| format!("{b:02x}")).join(" ")) writeln!(self, "{}: [{}]", event.as_ref(), buf.iter().map(|b| format!("{b:02x}")).join(" "))
} }
} }

61
orchid-base/src/macros.rs Normal file
View File

@@ -0,0 +1,61 @@
use itertools::Itertools;
use crate::{name::Sym, tree::{AtomTok, Paren, Ph, TokTree}};
use std::marker::PhantomData;
use crate::{api, location::Pos};
#[derive(Clone, Debug)]
pub struct MacroSlot<'a>(api::MacroTreeId, PhantomData<&'a ()>);
#[derive(Clone, Debug)]
pub struct MTree<'a, A: AtomTok> {
pub pos: Pos,
pub tok: MTok<'a, A>
}
#[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)
}
impl<'a, A> MTree<'a, A> {
pub(crate) fn from_api(api: &api::MacroTree) -> Self {
use api::MacroToken as MTK;
let tok = match &api.token {
MTK::Lambda(x, b) => MTok::Lambda(mtreev_from_api(x), mtreev_from_api(b)),
MTK::Name(t) => MTok::Name(Sym::deintern(*t)),
MTK::Slot(tk) => MTok::Slot(MacroSlot(tk.clone(), PhantomData)),
MTK::S(p, b) => MTok::S(p.clone(), mtreev_from_api(b)),
MTK::Ph(ph) => MTok::Ph(Ph::from_api(ph)),
};
Self { pos: Pos::from_api(&api.location), tok }
}
pub(crate) fn to_api(&self) -> api::MacroTree {
use api::MacroToken as MTK;
let token = match &self.tok {
MTok::Lambda(x, b) => MTK::Lambda(mtreev_to_api(x), mtreev_to_api(b)),
MTok::Name(t) => MTK::Name(t.tok().marker()),
MTok::Ph(ph) => MTK::Ph(ph.to_api()),
MTok::S(p, b) => MTK::S(p.clone(), mtreev_to_api(b)),
MTok::Slot(tk) => MTK::Slot(tk.0.clone()),
};
api::MacroTree { location: self.pos.to_api(), token }
}
}
pub fn mtreev_from_api<'a, 'b, A>(
api: impl IntoIterator<Item = &'b api::MacroTree>
) -> Vec<MTree<'a, A>> {
api.into_iter().map(MTree::from_api).collect_vec()
}
pub fn mtreev_to_api<'a: 'b, 'b, A: 'b>(
v: impl IntoIterator<Item = &'b MTree<'a, A>>
) -> Vec<api::MacroTree> {
v.into_iter().map(MTree::to_api).collect_vec()
}

View File

@@ -0,0 +1,95 @@
/// A shorthand for mapping over enums with identical structure. Used for converting between
/// owned enums and the corresponding API enums that only differ in the type of their
/// fields.
///
/// The basic form is
/// ```ignore
/// match_mapping!(self, ThisType => OtherType {
/// EmptyVariant,
/// TupleVariant(foo => intern(foo), bar.clone()),
/// StructVariant{ a.to_api(), b => b.}
/// })
/// ```
#[macro_export]
macro_rules! match_mapping {
($input:expr, $src:ty => $tgt:ty {
$($branches:tt)*
}) => {
match_mapping!(@BRANCH_MUNCH (($input) ($src) ($tgt)) () $($branches)* ,)
};
(@BRANCHES_DONE ( ($input:expr) ($src:ty) ($tgt:ty) )
$( ( $variant:ident $($pat:tt)*) )*
) => {
{
use $src as Foo;
match $input {
$(
match_mapping!(@PAT (Foo :: $variant) $($pat)*) =>
match_mapping!(@VAL (< $tgt >:: $variant) $($pat)*),
)*
}
}
};
(@BRANCH_MUNCH $ext:tt ( $($branches:tt)* ) $(,)?) => {
match_mapping!(@BRANCHES_DONE $ext $($branches)* )
};
(@BRANCH_MUNCH $ext:tt ( $($branches:tt)* ) $variant:ident , $($tail:tt)*) => {
match_mapping!(@BRANCH_MUNCH $ext ( $($branches)* ($variant) ) $($tail)*)
};
(@BRANCH_MUNCH $ext:tt ( $($branches:tt)* ) $variant:ident $pat:tt , $($tail:tt)*) => {
match_mapping!(@BRANCH_MUNCH $ext
( $($branches)* ($variant $pat) )
$($tail)*)
};
(@PAT ($($prefix:tt)*) ( $($fields:tt)* )) => {
$($prefix)* ( match_mapping!(@PAT_MUNCH () $($fields)*) )
};
(@PAT ($($prefix:tt)*) { $($fields:tt)* }) => {
$($prefix)* { match_mapping!(@PAT_MUNCH () $($fields)*) }
};
(@PAT ($($path:tt)*)) => { $($path)* };
(@PAT_MUNCH ($($names:ident)*) $name:ident => $value:expr) => { $($names ,)* $name };
(@PAT_MUNCH ($($names:ident)*) $name:ident => $value:expr , $($tail:tt)*) => {
match_mapping!(@PAT_MUNCH ($($names)* $name) $($tail)*)
};
(@PAT_MUNCH ($($names:ident)*) $name:ident . $($tail:tt)*) => {
match_mapping!(@PAT_DOT_MUNCH ($($names)* $name) $($tail)*)
};
(@PAT_MUNCH ($($names:ident)*)) => { $($names),* };
(@PAT_DOT_MUNCH $names:tt , $($tail:tt)*) => {
match_mapping!(@PAT_MUNCH $names $($tail)*)
};
(@PAT_DOT_MUNCH $names:tt $_:tt $($tail:tt)*) => {
match_mapping!(@PAT_DOT_MUNCH $names $($tail)*)
};
(@PAT_DOT_MUNCH ($($names:tt)*)) => { $($names),* };
(@VAL ($($prefix:tt)*)) => { $($prefix)* };
(@VAL ($($prefix:tt)*) ( $($fields:tt)* )) => {
$($prefix)* ( match_mapping!(@VAL_MUNCH () () $($fields)* ) )
};
(@VAL ($($prefix:tt)*) { $($fields:tt)* }) => {
$($prefix)* { match_mapping!(@VAL_MUNCH {} () $($fields)* ) }
};
(@VAL_MUNCH () ($($prefix:tt)*) $name:ident => $value:expr) => { $($prefix)* $value };
(@VAL_MUNCH () ($($prefix:tt)*) $name:ident => $value:expr , $($tail:tt)*) => {
match_mapping!(@VAL_MUNCH () ($($prefix)* $value, ) $($tail)*)
};
(@VAL_MUNCH {} ($($prefix:tt)*) $name:ident => $value:expr) => { $($prefix)* $name: $value };
(@VAL_MUNCH {} ($($prefix:tt)*) $name:ident => $value:expr , $($tail:tt)*) => {
match_mapping!(@VAL_MUNCH {} ($($prefix)* $name: $value, ) $($tail)*)
};
(@VAL_MUNCH () ($($prefix:tt)*) $name:ident . $member:tt $($tail:tt)*) => {
match_mapping!(@VAL_DOT_MUNCH () ($($prefix)* $name . $member ) $($tail)*)
};
(@VAL_MUNCH {} ($($prefix:tt)*) $name:ident . $member:tt $($tail:tt)*) => {
match_mapping!(@VAL_DOT_MUNCH {} ($($prefix)* $name: $name . $member) $($tail)*)
};
(@VAL_DOT_MUNCH $ptyp:tt ($($prefix:tt)*) , $($tail:tt)*) => {
match_mapping!(@VAL_MUNCH $ptyp ($($prefix)* ,) $($tail)*)
};
(@VAL_DOT_MUNCH $ptyp:tt ($($prefix:tt)*) $tt:tt $($tail:tt)*) => {
match_mapping!(@VAL_DOT_MUNCH $ptyp ($($prefix)* $tt) $($tail)*)
};
(@VAL_DOT_MUNCH $ptyp:tt ($($prefix:tt)*)) => { $($prefix)* };
(@VAL_MUNCH $_ptyp:tt ($($prefix:tt)*)) => { $($prefix)* };
}

View File

@@ -9,10 +9,10 @@ use std::path::Path;
use std::{fmt, slice, vec}; use std::{fmt, slice, vec};
use itertools::Itertools; use itertools::Itertools;
use orchid_api::TStrv;
use trait_set::trait_set; use trait_set::trait_set;
use crate::api; use crate::api;
use crate::api_conv::{ApiEquiv, FromApi, ToApi};
use crate::interner::{deintern, intern, InternMarker, Tok}; use crate::interner::{deintern, intern, InternMarker, Tok};
trait_set! { trait_set! {
@@ -357,7 +357,7 @@ impl Sym {
pub fn id(&self) -> NonZeroU64 { self.0.marker().get_id() } pub fn id(&self) -> NonZeroU64 { self.0.marker().get_id() }
/// Extern the sym for editing /// Extern the sym for editing
pub fn to_vname(&self) -> VName { VName(self[..].to_vec()) } pub fn to_vname(&self) -> VName { VName(self[..].to_vec()) }
pub fn deintern(marker: TStrv) -> Sym { pub fn deintern(marker: api::TStrv) -> Sym {
Self::from_tok(deintern(marker)).expect("Empty sequence found for serialized Sym") Self::from_tok(deintern(marker)).expect("Empty sequence found for serialized Sym")
} }
} }
@@ -386,6 +386,17 @@ impl Deref for Sym {
type Target = PathSlice; type Target = PathSlice;
fn deref(&self) -> &Self::Target { self.borrow() } fn deref(&self) -> &Self::Target { self.borrow() }
} }
impl ApiEquiv for Sym {
type Api = api::TStrv;
}
impl<C> ToApi<C> for Sym {
fn to_api(&self, ctx: &mut C) -> Self::Api { self.tok().to_api(ctx) }
}
impl<C> FromApi<C> for Sym {
fn from_api(api: &Self::Api, ctx: &mut C) -> Self {
Self::from_tok(Tok::from_api(api, ctx)).expect("Empty sequence found for serialized Sym")
}
}
/// An abstraction over tokenized vs non-tokenized names so that they can be /// An abstraction over tokenized vs non-tokenized names so that they can be
/// handled together in datastructures. The names can never be empty /// handled together in datastructures. The names can never be empty

View File

@@ -3,6 +3,7 @@ use std::ops::Range;
use ordered_float::NotNan; use ordered_float::NotNan;
use rust_decimal::Decimal; use rust_decimal::Decimal;
use num_traits::ToPrimitive;
use crate::error::{mk_err, OrcErr}; use crate::error::{mk_err, OrcErr};
use crate::intern; use crate::intern;
@@ -21,6 +22,16 @@ pub enum Numeric {
impl Numeric { impl Numeric {
pub fn decimal(num: i64, scale: u32) -> Self { Self::Decimal(Decimal::new(num, scale)) } pub fn decimal(num: i64, scale: u32) -> Self { Self::Decimal(Decimal::new(num, scale)) }
pub fn float(value: f64) -> Self { Self::Float(NotNan::new(value).unwrap()) } pub fn float(value: f64) -> Self { Self::Float(NotNan::new(value).unwrap()) }
pub fn to_f64(self) -> NotNan<f64> {
match self {
Self::Float(f) => f,
Self::Decimal(d) => {
let f = d.to_f64().expect("This is apparently always possible");
NotNan::new(f).expect("decimal was nan")
},
Self::Uint(i) => NotNan::new(i as f64).expect("int cannot be NaN"),
}
}
} }
/// Rasons why [parse_num] might fail. See [NumError]. /// Rasons why [parse_num] might fail. See [NumError].

View File

@@ -1,14 +1,13 @@
use std::iter;
use std::ops::{Deref, Range}; use std::ops::{Deref, Range};
use std::sync::Arc;
use std::{fmt, iter};
use itertools::Itertools; use itertools::Itertools;
use crate::error::{mk_err, OrcRes, Reporter}; use crate::error::{mk_err, mk_errv, OrcRes, Reporter};
use crate::interner::{deintern, Tok}; use crate::interner::{deintern, intern, Tok};
use crate::location::Pos; use crate::location::Pos;
use crate::name::VPath; use crate::name::VPath;
use crate::tree::{AtomInTok, Paren, TokTree, Token}; use crate::tree::{AtomTok, ExtraTok, Paren, TokTree, Token};
use crate::{api, intern}; use crate::{api, intern};
pub fn name_start(c: char) -> bool { c.is_alphabetic() || c == '_' } pub fn name_start(c: char) -> bool { c.is_alphabetic() || c == '_' }
@@ -17,11 +16,11 @@ pub fn op_char(c: char) -> bool { !name_char(c) && !c.is_whitespace() && !"()[]{
pub fn unrep_space(c: char) -> bool { c.is_whitespace() && !"\r\n".contains(c) } pub fn unrep_space(c: char) -> bool { c.is_whitespace() && !"\r\n".contains(c) }
#[derive(Debug)] #[derive(Debug)]
pub struct Snippet<'a, 'b, A: AtomInTok, X> { pub struct Snippet<'a, 'b, A: AtomTok, X: ExtraTok> {
prev: &'a TokTree<'b, A, X>, prev: &'a TokTree<'b, A, X>,
cur: &'a [TokTree<'b, A, X>], cur: &'a [TokTree<'b, A, X>],
} }
impl<'a, 'b, A: AtomInTok, X> Snippet<'a, 'b, A, X> { impl<'a, 'b, A: AtomTok, X: ExtraTok> Snippet<'a, 'b, A, X> {
pub fn new(prev: &'a TokTree<'b, A, X>, cur: &'a [TokTree<'b, A, X>]) -> Self { pub fn new(prev: &'a TokTree<'b, A, X>, cur: &'a [TokTree<'b, A, X>]) -> Self {
Self { prev, cur } Self { prev, cur }
} }
@@ -44,6 +43,9 @@ impl<'a, 'b, A: AtomInTok, X> Snippet<'a, 'b, A, X> {
pub fn pop_front(self) -> Option<(&'a TokTree<'b, A, X>, Self)> { pub fn pop_front(self) -> Option<(&'a TokTree<'b, A, X>, Self)> {
self.cur.first().map(|r| (r, self.split_at(1).1)) self.cur.first().map(|r| (r, self.split_at(1).1))
} }
pub fn pop_back(self) -> Option<(Self, &'a TokTree<'b, 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<'b, A, X>) -> bool) -> Option<(Self, Self)> {
let idx = self.find_idx(f)?; let idx = self.find_idx(f)?;
Some((self.split_at(idx).0, self.split_at(idx + 1).1)) Some((self.split_at(idx).0, self.split_at(idx + 1).1))
@@ -65,25 +67,25 @@ impl<'a, 'b, A: AtomInTok, X> Snippet<'a, 'b, A, X> {
self.split_at(non_fluff_start.unwrap_or(self.len())).1 self.split_at(non_fluff_start.unwrap_or(self.len())).1
} }
} }
impl<'a, 'b, A: AtomInTok, X> Copy for Snippet<'a, 'b, A, X> {} impl<'a, 'b, A: AtomTok, X: ExtraTok> Copy for Snippet<'a, 'b, A, X> {}
impl<'a, 'b, A: AtomInTok, X> Clone for Snippet<'a, 'b, A, X> { impl<'a, 'b, A: AtomTok, X: ExtraTok> Clone for Snippet<'a, 'b, A, X> {
fn clone(&self) -> Self { *self } fn clone(&self) -> Self { *self }
} }
impl<'a, 'b, A: AtomInTok, X> Deref for Snippet<'a, 'b, A, X> { impl<'a, 'b, A: AtomTok, X: ExtraTok> Deref for Snippet<'a, 'b, A, X> {
type Target = [TokTree<'b, A, X>]; type Target = [TokTree<'b, A, X>];
fn deref(&self) -> &Self::Target { self.cur } fn deref(&self) -> &Self::Target { self.cur }
} }
/// Remove tokens that aren't meaningful in expression context, such as comments /// Remove tokens that aren't meaningful in expression context, such as comments
/// or line breaks /// or line breaks
pub fn strip_fluff<'a, A: AtomInTok, X: Clone>( pub fn strip_fluff<'a, A: AtomTok, X: ExtraTok>(
tt: &TokTree<'a, A, X>, tt: &TokTree<'a, A, X>,
) -> Option<TokTree<'a, A, X>> { ) -> Option<TokTree<'a, A, X>> {
let tok = match &tt.tok { let tok = match &tt.tok {
Token::BR => return None, Token::BR => return None,
Token::Comment(_) => return None, Token::Comment(_) => return None,
Token::LambdaHead(arg) => Token::LambdaHead(arg.iter().filter_map(strip_fluff).collect()), Token::LambdaHead(arg) => Token::LambdaHead(arg.iter().filter_map(strip_fluff).collect()),
Token::S(p, b) => Token::S(p.clone(), b.iter().filter_map(strip_fluff).collect()), Token::S(p, b) => Token::S(*p, b.iter().filter_map(strip_fluff).collect()),
t => t.clone(), t => t.clone(),
}; };
Some(TokTree { tok, range: tt.range.clone() }) Some(TokTree { tok, range: tt.range.clone() })
@@ -91,13 +93,21 @@ pub fn strip_fluff<'a, A: AtomInTok, X: Clone>(
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Comment { pub struct Comment {
pub text: Arc<String>, pub text: Tok<String>,
pub pos: Pos, pub pos: Pos,
} }
impl Comment {
pub fn from_api(api: &api::Comment) -> Self {
Self { pos: Pos::from_api(&api.location), text: deintern(api.text) }
}
pub fn to_api(&self) -> api::Comment {
api::Comment { location: self.pos.to_api(), text: self.text.marker() }
}
}
pub fn line_items<'a, 'b, A: AtomInTok, X>( pub fn line_items<'a, 'b, A: AtomTok, X: ExtraTok>(
snip: Snippet<'a, 'b, A, X>, snip: Snippet<'a, 'b, A, X>,
) -> Vec<(Vec<Comment>, Snippet<'a, 'b, A, X>)> { ) -> Vec<Parsed<'a, 'b, Vec<Comment>, A, X>> {
let mut items = Vec::new(); let mut items = Vec::new();
let mut comments = Vec::new(); let mut comments = Vec::new();
for mut line in snip.split(|t| matches!(t, Token::BR)) { for mut line in snip.split(|t| matches!(t, Token::BR)) {
@@ -109,72 +119,79 @@ pub fn line_items<'a, 'b, A: AtomInTok, X>(
match line.find_idx(|t| !matches!(t, Token::Comment(_))) { match line.find_idx(|t| !matches!(t, Token::Comment(_))) {
None => comments.extend(line.cur), None => comments.extend(line.cur),
Some(i) => { Some(i) => {
let (cmts, line) = line.split_at(i); let (cmts, tail) = line.split_at(i);
let comments = Vec::from_iter(comments.drain(..).chain(cmts.cur).map(|t| match &t.tok { let comments = Vec::from_iter(comments.drain(..).chain(cmts.cur).map(|t| match &t.tok {
Token::Comment(c) => Comment { text: c.clone(), pos: Pos::Range(t.range.clone()) }, Token::Comment(c) => Comment { text: intern(&**c), pos: Pos::Range(t.range.clone()) },
_ => unreachable!("All are comments checked above"), _ => unreachable!("All are comments checked above"),
})); }));
items.push((comments, line)); items.push(Parsed { output: comments, tail });
}, },
} }
} }
items items
} }
pub fn try_pop_no_fluff<'a, 'b, A: AtomInTok, X>( pub fn try_pop_no_fluff<'a, 'b, A: AtomTok, X: ExtraTok>(
snip: Snippet<'a, 'b, A, X>, snip: Snippet<'a, 'b, A, X>,
) -> OrcRes<(&'a TokTree<'b, A, X>, Snippet<'a, 'b, A, X>)> { ) -> ParseRes<'a, 'b, &'a TokTree<'b, A, X>, A, X> {
snip.skip_fluff().pop_front().ok_or_else(|| { snip.skip_fluff().pop_front().map(|(output, tail)| Parsed { output, tail }).ok_or_else(|| {
vec![mk_err(intern!(str: "Unexpected end"), "Pattern ends abruptly", [ mk_errv(
Pos::Range(snip.pos()).into() intern!(str: "Unexpected end"),
])] "Pattern ends abruptly",
[Pos::Range(snip.pos()).into()],
)
}) })
} }
pub fn expect_end(snip: Snippet<'_, '_, impl AtomInTok, impl Sized>) -> OrcRes<()> { pub fn expect_end(snip: Snippet<'_, '_, impl AtomTok, impl ExtraTok>) -> OrcRes<()> {
match snip.skip_fluff().get(0) { match snip.skip_fluff().get(0) {
Some(surplus) => Err(vec![mk_err( Some(surplus) => Err(mk_errv(
intern!(str: "Extra code after end of line"), intern!(str: "Extra code after end of line"),
"Code found after the end of the line", "Code found after the end of the line",
[Pos::Range(surplus.range.clone()).into()], [Pos::Range(surplus.range.clone()).into()],
)]), )),
None => Ok(()), None => Ok(()),
} }
} }
pub fn expect_tok<'a, 'b, A: AtomInTok, X: fmt::Display>( pub fn expect_tok<'a, 'b, A: AtomTok, X: ExtraTok>(
snip: Snippet<'a, 'b, A, X>, snip: Snippet<'a, 'b, A, X>,
tok: Tok<String>, tok: Tok<String>,
) -> OrcRes<Snippet<'a, 'b, A, X>> { ) -> ParseRes<'a, 'b, (), A, X> {
let (head, tail) = try_pop_no_fluff(snip)?; let Parsed { output: head, tail } = try_pop_no_fluff(snip)?;
match &head.tok { match &head.tok {
Token::Name(n) if *n == tok => Ok(tail), Token::Name(n) if *n == tok => Ok(Parsed { output: (), tail }),
t => Err(vec![mk_err( t => Err(mk_errv(
intern!(str: "Expected specific keyword"), intern!(str: "Expected specific keyword"),
format!("Expected {tok} but found {t}"), format!("Expected {tok} but found {t}"),
[Pos::Range(head.range.clone()).into()], [Pos::Range(head.range.clone()).into()],
)]), )),
} }
} }
pub fn parse_multiname<'a, 'b, A: AtomInTok, X: fmt::Display>( pub struct Parsed<'a, 'b, T, A: AtomTok, X: ExtraTok> {
pub output: T,
pub tail: Snippet<'a, 'b, A, X>,
}
pub type ParseRes<'a, 'b, T, A, X> = OrcRes<Parsed<'a, 'b, T, A, X>>;
pub fn parse_multiname<'a, 'b, A: AtomTok, X: ExtraTok>(
ctx: &impl Reporter, ctx: &impl Reporter,
tail: Snippet<'a, 'b, A, X>, tail: Snippet<'a, 'b, A, X>,
) -> OrcRes<(Vec<CompName>, Snippet<'a, 'b, A, X>)> { ) -> ParseRes<'a, 'b, Vec<(Import, Pos)>, A, X> {
let ret = rec(ctx, tail); let ret = rec(ctx, tail);
#[allow(clippy::type_complexity)] // it's an internal function #[allow(clippy::type_complexity)] // it's an internal function
pub fn rec<'a, 'b, A: AtomInTok, X: fmt::Display>( pub fn rec<'a, 'b, A: AtomTok, X: ExtraTok>(
ctx: &impl Reporter, ctx: &impl Reporter,
tail: Snippet<'a, 'b, A, X>, tail: Snippet<'a, 'b, A, X>,
) -> OrcRes<(Vec<(Vec<Tok<String>>, Option<Tok<String>>, Pos)>, Snippet<'a, 'b, A, X>)> { ) -> ParseRes<'a, 'b, Vec<(Vec<Tok<String>>, Option<Tok<String>>, Pos)>, A, X> {
let comma = intern!(str: ","); let comma = intern!(str: ",");
let globstar = intern!(str: "*"); let globstar = intern!(str: "*");
let (name, tail) = tail.skip_fluff().pop_front().ok_or_else(|| { let (name, tail) = tail.skip_fluff().pop_front().ok_or_else(|| {
vec![mk_err( mk_err(intern!(str: "Expected name"), "Expected a name, a list of names, or a globstar.", [
intern!(str: "Expected name"), Pos::Range(tail.pos()).into(),
"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)) { if let Some((Token::NS, tail)) = tail.skip_fluff().pop_front().map(|(tt, s)| (&tt.tok, s)) {
let n = match &name.tok { let n = match &name.tok {
@@ -184,25 +201,27 @@ pub fn parse_multiname<'a, 'b, A: AtomInTok, X: fmt::Display>(
])), ])),
}; };
match (rec(ctx, tail), n) { match (rec(ctx, tail), n) {
(Err(ev), n) => Err(Vec::from_iter(ev.into_iter().chain(n.err()))), (Err(ev), n) => Err(ev.extended(n.err())),
(Ok((_, tail)), Err(e)) => { (Ok(Parsed { tail, .. }), Err(e)) => {
ctx.report(e); ctx.report(e);
Ok((vec![], tail)) Ok(Parsed { output: vec![], tail })
}, },
(Ok((n, tail)), Ok(pre)) => (Ok(Parsed { tail, output }), Ok(pre)) => Ok(Parsed {
Ok((n.into_iter().update(|i| i.0.push(pre.clone())).collect_vec(), tail)), output: output.into_iter().update(|i| i.0.push(pre.clone())).collect_vec(),
tail,
}),
} }
} else { } else {
let names = match &name.tok { let output = match &name.tok {
Token::Name(ntok) => { Token::Name(ntok) => {
let nopt = match ntok { let nopt = match ntok {
n if *n == globstar => None, n if *n == globstar => None,
n if n.starts_with(op_char) => n if n.starts_with(op_char) =>
return Err(vec![mk_err( return Err(mk_errv(
intern!(str: "Unescaped operator in multiname"), intern!(str: "Unescaped operator in multiname"),
"Operators in multinames should be enclosed in []", "Operators in multinames should be enclosed in []",
[Pos::Range(name.range.clone()).into()], [Pos::Range(name.range.clone()).into()],
)]), )),
n => Some(n.clone()), n => Some(n.clone()),
}; };
vec![(vec![], nopt, Pos::Range(name.range.clone()))] vec![(vec![], nopt, Pos::Range(name.range.clone()))]
@@ -226,9 +245,9 @@ pub fn parse_multiname<'a, 'b, A: AtomInTok, X: fmt::Display>(
let body = Snippet::new(name, b); let body = Snippet::new(name, b);
for csent in body.split(|n| matches!(n, Token::Name(n) if *n == comma)) { for csent in body.split(|n| matches!(n, Token::Name(n) if *n == comma)) {
match rec(ctx, csent) { match rec(ctx, csent) {
Err(e) => e.into_iter().for_each(|e| ctx.report(e)), Err(e) => ctx.report(e),
Ok((v, surplus)) => match surplus.get(0) { Ok(Parsed { output, tail }) => match tail.get(0) {
None => ok.extend(v), None => ok.extend(output),
Some(t) => ctx.report(mk_err( Some(t) => ctx.report(mk_err(
intern!(str: "Unexpected token in multiname group"), intern!(str: "Unexpected token in multiname group"),
"Unexpected token. Likely missing a :: or , or wanted [] instead of ()", "Unexpected token. Likely missing a :: or , or wanted [] instead of ()",
@@ -240,40 +259,39 @@ pub fn parse_multiname<'a, 'b, A: AtomInTok, X: fmt::Display>(
ok ok
}, },
t => t =>
return Err(vec![mk_err( return Err(mk_errv(
intern!(str: "Unrecognized name end"), intern!(str: "Unrecognized name end"),
format!("Names cannot end with {t} tokens"), format!("Names cannot end with {t} tokens"),
[Pos::Range(name.range.clone()).into()], [Pos::Range(name.range.clone()).into()],
)]), )),
}; };
Ok((names, tail)) Ok(Parsed { output, tail })
} }
} }
ret.map(|(i, tail)| { ret.map(|Parsed { output, tail }| {
let i = Vec::from_iter((i.into_iter()).map(|(p, name, pos)| CompName { let output = (output.into_iter())
path: VPath::new(p.into_iter().rev()), .map(|(p, name, pos)| (Import { path: VPath::new(p.into_iter().rev()), name }, pos))
name, .collect_vec();
pos, Parsed { output, tail }
}));
(i, tail)
}) })
} }
/// A compound name, possibly ending with a globstar /// A compound name, possibly ending with a globstar
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct CompName { pub struct Import {
pub path: VPath, pub path: VPath,
pub name: Option<Tok<String>>, pub name: Option<Tok<String>>,
pub pos: Pos,
}
impl CompName {
pub fn from_api(i: api::CompName) -> Self {
Self {
path: VPath::new(i.path.into_iter().map(deintern)),
name: i.name.map(deintern),
pos: Pos::from_api(&i.location),
}
} }
impl Import {
// pub fn from_api(i: api::CompName) -> Self {
// Self { path: VPath::new(i.path.into_iter().map(deintern)), name: i.name.map(deintern) }
// }
// pub fn to_api(&self) -> api::CompName {
// api::CompName {
// path: self.path.iter().map(|t| t.marker()).collect(),
// name: self.name.as_ref().map(|t| t.marker()),
// }
// }
} }
#[cfg(test)] #[cfg(test)]
@@ -282,6 +300,14 @@ mod test {
use super::Snippet; use super::Snippet;
fn _covary_snip_a<'a, 'b>(x: Snippet<'static, 'b, Never, ()>) -> Snippet<'a, 'b, Never, ()> { x } fn _covary_snip_a<'a, 'b>(
fn _covary_snip_b<'a, 'b>(x: Snippet<'a, 'static, Never, ()>) -> Snippet<'a, 'b, Never, ()> { x } 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
}
} }

View File

@@ -0,0 +1,35 @@
//! Methods to operate on Rust vectors in a declarative manner
use std::iter;
/// Pure version of [Vec::push]
///
/// Create a new vector consisting of the provided vector with the
/// element appended. See [pushed_ref] to use it with a slice
pub fn pushed<I: IntoIterator, C: FromIterator<I::Item>>(vec: I, t: I::Item) -> C {
vec.into_iter().chain(iter::once(t)).collect()
}
/// Pure version of [Vec::push]
///
/// Create a new vector consisting of the provided slice with the
/// element appended. See [pushed] for the owned version
pub fn pushed_ref<'a, T: Clone + 'a, C: FromIterator<T>>(
vec: impl IntoIterator<Item = &'a T>,
t: T,
) -> C {
vec.into_iter().cloned().chain(iter::once(t)).collect()
}
/// Push an element on the adhoc stack, pass it to the callback, then pop the
/// element out again.
pub fn with_pushed<T, U>(
vec: &mut Vec<T>,
item: T,
cb: impl for<'a> FnOnce(&'a mut Vec<T>) -> U,
) -> (T, U) {
vec.push(item);
let out = cb(vec);
let item = vec.pop().expect("top element stolen by callback");
(item, out)
}

View File

@@ -1,3 +1,5 @@
use std::any::Any;
use std::cell::RefCell;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::{BitAnd, Deref}; use std::ops::{BitAnd, Deref};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
@@ -5,17 +7,24 @@ use std::sync::mpsc::{sync_channel, SyncSender};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::{mem, thread}; use std::{mem, thread};
use derive_destructure::destructure;
use dyn_clone::{clone_box, DynClone}; use dyn_clone::{clone_box, DynClone};
use hashbrown::HashMap; use hashbrown::HashMap;
use orchid_api_traits::{Channel, Coding, Decode, Encode, MsgSet, Request}; use orchid_api_traits::{Channel, Coding, Decode, Encode, MsgSet, Request};
use trait_set::trait_set; use trait_set::trait_set;
pub struct ReplyToken; pub struct Receipt;
impl Receipt {
pub fn off_thread(name: String, cb: impl FnOnce() -> Self + Send + 'static) -> Self {
thread::Builder::new().name(name).spawn(cb).unwrap();
Self
}
}
trait_set! { trait_set! {
pub trait SendFn<T: MsgSet> = for<'a> FnMut(&'a [u8], ReqNot<T>) + DynClone + Send + 'static; pub trait SendFn<T: MsgSet> = for<'a> FnMut(&'a [u8], ReqNot<T>) + DynClone + Send + 'static;
pub trait ReqFn<T: MsgSet> = pub trait ReqFn<T: MsgSet> =
FnMut(RequestHandle<T>) -> ReplyToken + DynClone + Send + Sync + 'static; FnMut(RequestHandle<T>, <T::In as Channel>::Req) -> Receipt + DynClone + Send + Sync + 'static;
pub trait NotifFn<T: MsgSet> = pub trait NotifFn<T: MsgSet> =
for<'a> FnMut(<T::In as Channel>::Notif, ReqNot<T>) + DynClone + Send + Sync + 'static; for<'a> FnMut(<T::In as Channel>::Notif, ReqNot<T>) + DynClone + Send + Sync + 'static;
} }
@@ -24,28 +33,38 @@ fn get_id(message: &[u8]) -> (u64, &[u8]) {
(u64::from_be_bytes(message[..8].to_vec().try_into().unwrap()), &message[8..]) (u64::from_be_bytes(message[..8].to_vec().try_into().unwrap()), &message[8..])
} }
pub struct RequestHandle<T: MsgSet> { pub trait ReqHandlish {
id: u64, fn defer_drop(&self, val: impl Any + 'static);
message: <T::In as Channel>::Req, }
parent: ReqNot<T>,
#[derive(destructure)]
pub struct RequestHandle<MS: MsgSet> {
defer_drop: RefCell<Vec<Box<dyn Any>>>,
fulfilled: AtomicBool, fulfilled: AtomicBool,
id: u64,
parent: ReqNot<MS>,
} }
impl<MS: MsgSet + 'static> RequestHandle<MS> { impl<MS: MsgSet + 'static> RequestHandle<MS> {
fn new(parent: ReqNot<MS>, id: u64) -> Self {
Self { defer_drop: RefCell::default(), fulfilled: false.into(), parent, id }
}
pub fn reqnot(&self) -> ReqNot<MS> { self.parent.clone() } pub fn reqnot(&self) -> ReqNot<MS> { self.parent.clone() }
pub fn req(&self) -> &<MS::In as Channel>::Req { &self.message } pub fn handle<U: Request>(&self, _: &U, rep: &U::Response) -> Receipt { self.respond(rep) }
fn respond(&self, response: &impl Encode) -> ReplyToken { pub fn will_handle_as<U: Request>(&self, _: &U) -> ReqTypToken<U> { ReqTypToken(PhantomData) }
pub fn handle_as<U: Request>(&self, _: ReqTypToken<U>, rep: &U::Response) -> Receipt {
self.respond(rep)
}
pub fn respond(&self, response: &impl Encode) -> Receipt {
assert!(!self.fulfilled.swap(true, Ordering::Relaxed), "Already responded to {}", self.id); assert!(!self.fulfilled.swap(true, Ordering::Relaxed), "Already responded to {}", self.id);
let mut buf = (!self.id).to_be_bytes().to_vec(); let mut buf = (!self.id).to_be_bytes().to_vec();
response.encode(&mut buf); response.encode(&mut buf);
let mut send = clone_box(&*self.reqnot().0.lock().unwrap().send); let mut send = clone_box(&*self.reqnot().0.lock().unwrap().send);
(send)(&buf, self.parent.clone()); (send)(&buf, self.parent.clone());
ReplyToken Receipt
} }
pub fn handle<T: Request>(&self, _: &T, rep: &T::Response) -> ReplyToken { self.respond(rep) }
pub fn will_handle_as<T: Request>(&self, _: &T) -> ReqTypToken<T> { ReqTypToken(PhantomData) }
pub fn handle_as<T: Request>(&self, _token: ReqTypToken<T>, rep: &T::Response) -> ReplyToken {
self.respond(rep)
} }
impl<MS: MsgSet> ReqHandlish for RequestHandle<MS> {
fn defer_drop(&self, val: impl Any) { self.defer_drop.borrow_mut().push(Box::new(val)) }
} }
impl<MS: MsgSet> Drop for RequestHandle<MS> { impl<MS: MsgSet> Drop for RequestHandle<MS> {
fn drop(&mut self) { fn drop(&mut self) {
@@ -56,10 +75,6 @@ impl<MS: MsgSet> Drop for RequestHandle<MS> {
pub struct ReqTypToken<T>(PhantomData<T>); pub struct ReqTypToken<T>(PhantomData<T>);
pub fn respond_with<R: Request>(r: &R, f: impl FnOnce(&R) -> R::Response) -> Vec<u8> {
r.respond(f(r))
}
pub struct ReqNotData<T: MsgSet> { pub struct ReqNotData<T: MsgSet> {
id: u64, id: u64,
send: Box<dyn SendFn<T>>, send: Box<dyn SendFn<T>>,
@@ -104,8 +119,11 @@ impl<T: MsgSet> ReqNot<T> {
let message = <T::In as Channel>::Req::decode(&mut &payload[..]); let message = <T::In as Channel>::Req::decode(&mut &payload[..]);
let mut req = clone_box(&*g.req); let mut req = clone_box(&*g.req);
mem::drop(g); mem::drop(g);
let handle = RequestHandle { id, message, fulfilled: false.into(), parent: self.clone() }; let rn = self.clone();
thread::Builder::new().name(format!("request {id}")).spawn(move || req(handle)).unwrap(); thread::Builder::new()
.name(format!("request {id}"))
.spawn(move || req(RequestHandle::new(rn, id), message))
.unwrap();
} }
} }
@@ -208,12 +226,12 @@ mod test {
let receiver = ReqNot::<TestMsgSet>::new( let receiver = ReqNot::<TestMsgSet>::new(
|_, _| panic!("Should not send anything"), |_, _| panic!("Should not send anything"),
clone!(received; move |notif, _| *received.lock().unwrap() = Some(notif)), clone!(received; move |notif, _| *received.lock().unwrap() = Some(notif)),
|_| panic!("Not receiving a request"), |_, _| panic!("Not receiving a request"),
); );
let sender = ReqNot::<TestMsgSet>::new( let sender = ReqNot::<TestMsgSet>::new(
clone!(receiver; move |d, _| receiver.receive(d.to_vec())), clone!(receiver; move |d, _| receiver.receive(d.to_vec())),
|_, _| panic!("Should not receive notif"), |_, _| panic!("Should not receive notif"),
|_| panic!("Should not receive request"), |_, _| panic!("Should not receive request"),
); );
sender.notify(3); sender.notify(3);
assert_eq!(*received.lock().unwrap(), Some(3)); assert_eq!(*received.lock().unwrap(), Some(3));
@@ -230,7 +248,7 @@ mod test {
move |d, _| receiver.lock().unwrap().as_ref().unwrap().receive(d.to_vec()) move |d, _| receiver.lock().unwrap().as_ref().unwrap().receive(d.to_vec())
}, },
|_, _| panic!("Should not receive notif"), |_, _| panic!("Should not receive notif"),
|_| panic!("Should not receive request"), |_, _| panic!("Should not receive request"),
)); ));
*receiver.lock().unwrap() = Some(ReqNot::new( *receiver.lock().unwrap() = Some(ReqNot::new(
{ {
@@ -238,9 +256,9 @@ mod test {
move |d, _| sender.receive(d.to_vec()) move |d, _| sender.receive(d.to_vec())
}, },
|_, _| panic!("Not receiving notifs"), |_, _| panic!("Not receiving notifs"),
|req| { |hand, req| {
assert_eq!(req.req(), &TestReq(5)); assert_eq!(req, TestReq(5));
req.respond(&6u8) hand.respond(&6u8)
}, },
)); ));
let response = sender.request(TestReq(5)); let response = sender.request(TestReq(5));

View File

@@ -1,6 +1,6 @@
use std::borrow::Borrow; use std::borrow::Borrow;
use std::cell::RefCell; use std::cell::RefCell;
use std::fmt::{self, Display, Write}; use std::fmt::{self, Debug, Display};
use std::iter; use std::iter;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::Range; use std::ops::Range;
@@ -8,26 +8,32 @@ use std::sync::Arc;
use itertools::Itertools; use itertools::Itertools;
use never::Never; use never::Never;
use orchid_api::Placeholder;
use ordered_float::NotNan;
use trait_set::trait_set; use trait_set::trait_set;
use crate::api; use crate::api;
use crate::error::OrcErr; use crate::error::OrcErrv;
use crate::interner::{deintern, Tok}; use crate::interner::{deintern, Tok};
use crate::name::{NameLike, VName}; use crate::name::PathSlice;
use crate::parse::Snippet;
use crate::tokens::PARENS; use crate::tokens::PARENS;
pub use api::PhKind as PhKind;
trait_set! { trait_set! {
pub trait RecurCB<'a, A: AtomInTok, X> = Fn(TokTree<'a, A, X>) -> TokTree<'a, A, X>; pub trait RecurCB<'a, A: AtomTok, X: ExtraTok> = Fn(TokTree<'a, A, X>) -> TokTree<'a, A, X>;
pub trait ExtraTok = Display + Clone + fmt::Debug;
} }
pub fn recur<'a, A: AtomInTok, X>( pub fn recur<'a, A: AtomTok, X: ExtraTok>(
tt: TokTree<'a, A, X>, tt: TokTree<'a, A, X>,
f: &impl Fn(TokTree<'a, A, X>, &dyn RecurCB<'a, A, X>) -> TokTree<'a, A, X>, f: &impl Fn(TokTree<'a, A, X>, &dyn RecurCB<'a, A, X>) -> TokTree<'a, A, X>,
) -> TokTree<'a, A, X> { ) -> TokTree<'a, A, X> {
f(tt, &|TokTree { range, tok }| { f(tt, &|TokTree { range, tok }| {
let tok = match tok { let tok = match tok {
tok @ (Token::Atom(_) | Token::BR | Token::Bottom(_) | Token::Comment(_) | Token::NS) => tok, tok @ (Token::Atom(_) | Token::BR | Token::Bottom(_) | Token::Comment(_) | Token::NS) => tok,
tok @ (Token::Name(_) | Token::Slot(_) | Token::X(_)) => tok, tok @ (Token::Name(_) | Token::Slot(_) | Token::X(_) | Token::Ph(_) | Token::Macro(_)) => tok,
Token::LambdaHead(arg) => Token::LambdaHead(arg) =>
Token::LambdaHead(arg.into_iter().map(|tt| recur(tt, f)).collect_vec()), 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()), Token::S(p, b) => Token::S(p, b.into_iter().map(|tt| recur(tt, f)).collect_vec()),
@@ -36,46 +42,48 @@ pub fn recur<'a, A: AtomInTok, X>(
}) })
} }
pub trait AtomInTok: Display + Clone { pub trait AtomTok: fmt::Display + Clone + fmt::Debug {
type Context: ?Sized; type Context: ?Sized;
fn from_api(atom: &api::Atom, pos: Range<u32>, ctx: &mut Self::Context) -> Self; fn from_api(atom: &api::Atom, pos: Range<u32>, ctx: &mut Self::Context) -> Self;
fn to_api(&self) -> api::Atom; fn to_api(&self) -> api::Atom;
} }
impl AtomInTok for Never { impl AtomTok for Never {
type Context = Never; type Context = Never;
fn from_api(_: &api::Atom, _: Range<u32>, _: &mut Self::Context) -> Self { panic!() } fn from_api(_: &api::Atom, _: Range<u32>, _: &mut Self::Context) -> Self { panic!() }
fn to_api(&self) -> orchid_api::Atom { match *self {} } fn to_api(&self) -> orchid_api::Atom { match *self {} }
} }
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct TreeHandle<'a>(api::TreeTicket, PhantomData<&'a ()>); pub struct TokHandle<'a>(api::TreeTicket, PhantomData<&'a ()>);
impl TreeHandle<'static> { impl TokHandle<'static> {
pub fn new(tt: api::TreeTicket) -> Self { TreeHandle(tt, PhantomData) } pub fn new(tt: api::TreeTicket) -> Self { TokHandle(tt, PhantomData) }
} }
impl<'a> TreeHandle<'a> { impl<'a> TokHandle<'a> {
pub fn ticket(self) -> api::TreeTicket { self.0 } pub fn ticket(self) -> api::TreeTicket { self.0 }
} }
impl<'a> Display for TreeHandle<'a> { impl<'a> Display for TokHandle<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Handle({})", self.0.0) } fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Handle({})", self.0.0) }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct TokTree<'a, A: AtomInTok, X> { pub struct TokTree<'a, A: AtomTok, X: ExtraTok> {
pub tok: Token<'a, A, X>, pub tok: Token<'a, A, X>,
pub range: Range<u32>, pub range: Range<u32>,
} }
impl<'a, A: AtomInTok, X> TokTree<'a, A, X> { impl<'a, A: AtomTok, X: ExtraTok> TokTree<'a, A, X> {
pub fn from_api(tt: &api::TokenTree, ctx: &mut A::Context) -> Self { pub fn from_api(tt: &api::TokenTree, ctx: &mut A::Context) -> Self {
let tok = match &tt.token { let tok = match &tt.token {
api::Token::Atom(a) => Token::Atom(A::from_api(a, tt.range.clone(), ctx)), api::Token::Atom(a) => Token::Atom(A::from_api(a, tt.range.clone(), ctx)),
api::Token::BR => Token::BR, api::Token::BR => Token::BR,
api::Token::NS => Token::NS, api::Token::NS => Token::NS,
api::Token::Bottom(e) => Token::Bottom(e.iter().map(OrcErr::from_api).collect()), api::Token::Bottom(e) => Token::Bottom(OrcErrv::from_api(e)),
api::Token::Lambda(arg) => Token::LambdaHead(ttv_from_api(arg, ctx)), api::Token::LambdaHead(arg) => Token::LambdaHead(ttv_from_api(arg, ctx)),
api::Token::Name(name) => Token::Name(deintern(*name)), api::Token::Name(name) => Token::Name(deintern(*name)),
api::Token::S(par, b) => Token::S(par.clone(), ttv_from_api(b, ctx)), api::Token::S(par, b) => Token::S(*par, ttv_from_api(b, ctx)),
api::Token::Comment(c) => Token::Comment(c.clone()), api::Token::Comment(c) => Token::Comment(c.clone()),
api::Token::Slot(id) => Token::Slot(TreeHandle::new(*id)), api::Token::Slot(id) => Token::Slot(TokHandle::new(*id)),
api::Token::Ph(ph) => Token::Ph(Ph {name: deintern(ph.name), kind: ph.kind }),
api::Token::Macro(prio) => Token::Macro(*prio)
}; };
Self { range: tt.range.clone(), tok } Self { range: tt.range.clone(), tok }
} }
@@ -88,66 +96,110 @@ impl<'a, A: AtomInTok, X> TokTree<'a, A, X> {
Token::Atom(a) => api::Token::Atom(a.to_api()), Token::Atom(a) => api::Token::Atom(a.to_api()),
Token::BR => api::Token::BR, Token::BR => api::Token::BR,
Token::NS => api::Token::NS, Token::NS => api::Token::NS,
Token::Bottom(e) => api::Token::Bottom(e.iter().map(OrcErr::to_api).collect()), Token::Bottom(e) => api::Token::Bottom(e.to_api()),
Token::Comment(c) => api::Token::Comment(c.clone()), Token::Comment(c) => api::Token::Comment(c.clone()),
Token::LambdaHead(arg) => Token::LambdaHead(arg) => api::Token::LambdaHead(ttv_to_api(arg, do_extra)),
api::Token::Lambda(arg.iter().map(|t| t.to_api(do_extra)).collect_vec()),
Token::Name(n) => api::Token::Name(n.marker()), Token::Name(n) => api::Token::Name(n.marker()),
Token::Slot(tt) => api::Token::Slot(tt.ticket()), Token::Slot(tt) => api::Token::Slot(tt.ticket()),
Token::S(p, b) => api::Token::S(p.clone(), b.iter().map(|t| t.to_api(do_extra)).collect()), Token::S(p, b) => api::Token::S(*p, ttv_to_api(b, do_extra)),
Token::Ph(Ph { name, kind }) => api::Token::Ph(Placeholder { name: name.marker(), kind: *kind }),
Token::X(x) => return do_extra(x, self.range.clone()), Token::X(x) => return do_extra(x, self.range.clone()),
Token::Macro(prio) => api::Token::Macro(*prio),
}; };
api::TokenTree { range: self.range.clone(), token } api::TokenTree { range: self.range.clone(), token }
} }
pub fn into_api(
self,
do_extra: &mut impl FnMut(X, Range<u32>) -> api::TokenTree,
) -> api::TokenTree {
let token = match self.tok {
Token::Atom(a) => api::Token::Atom(a.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)),
Token::Name(n) => api::Token::Name(n.marker()),
Token::Slot(tt) => api::Token::Slot(tt.ticket()),
Token::S(p, b) => api::Token::S(p, ttv_into_api(b, do_extra)),
Token::Ph(Ph { kind, name }) => api::Token::Ph(Placeholder { name: name.marker(), kind }),
Token::X(x) => return do_extra(x, self.range.clone()),
Token::Macro(prio) => api::Token::Macro(prio),
};
api::TokenTree { range: self.range.clone(), token }
} }
impl<'a, A: AtomInTok + Display, X: Display> Display for TokTree<'a, A, X> {
pub fn is_kw(&self, tk: Tok<String>) -> bool { self.tok.is_kw(tk) }
pub fn as_name(&self) -> Option<Tok<String>> {
if let Token::Name(n) = &self.tok { Some(n.clone()) } else { None }
}
pub fn as_s(&self, par: Paren) -> Option<Snippet<'_, 'a, A, X>> {
self.tok.as_s(par).map(|slc| Snippet::new(self, slc))
}
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;
body.insert(0, Token::LambdaHead(arg).at(arg_range));
Token::S(Paren::Round, body).at(s_range)
}
}
impl<'a, A: AtomTok, X: ExtraTok> Display for TokTree<'a, A, X> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.tok) } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.tok) }
} }
pub fn ttv_from_api<A: AtomInTok, X>( pub fn ttv_from_api<A: AtomTok, X: ExtraTok>(
tokv: impl IntoIterator<Item: Borrow<api::TokenTree>>, tokv: impl IntoIterator<Item: Borrow<api::TokenTree>>,
ctx: &mut A::Context, ctx: &mut A::Context,
) -> Vec<TokTree<'static, A, X>> { ) -> Vec<TokTree<'static, A, X>> {
tokv.into_iter().map(|t| TokTree::<A, X>::from_api(t.borrow(), ctx)).collect() tokv.into_iter().map(|t| TokTree::<A, X>::from_api(t.borrow(), ctx)).collect()
} }
pub fn ttv_to_api<'a, A: AtomInTok, X>( pub fn ttv_to_api<'a, A: AtomTok, X: ExtraTok>(
tokv: impl IntoIterator<Item: Borrow<TokTree<'a, A, X>>>, tokv: impl IntoIterator<Item: Borrow<TokTree<'a, A, X>>>,
do_extra: &mut impl FnMut(&X, Range<u32>) -> api::TokenTree, do_extra: &mut impl FnMut(&X, Range<u32>) -> api::TokenTree,
) -> Vec<api::TokenTree> { ) -> Vec<api::TokenTree> {
tokv tokv.into_iter().map(|tok| Borrow::<TokTree<A, X>>::borrow(&tok).to_api(do_extra)).collect_vec()
.into_iter()
.map(|tok| {
let tt: &TokTree<A, X> = tok.borrow();
tt.to_api(do_extra)
})
.collect_vec()
} }
pub fn vname_tv<'a: 'b, 'b, A: AtomInTok + 'a, X: 'a>( pub fn ttv_into_api<'a, A: AtomTok, X: ExtraTok>(
name: &'b VName, tokv: impl IntoIterator<Item = TokTree<'a, A, X>>,
ran: Range<u32>, do_extra: &mut impl FnMut(X, Range<u32>) -> api::TokenTree,
) -> Vec<api::TokenTree> {
tokv.into_iter().map(|t| t.into_api(do_extra)).collect_vec()
}
/// This takes a position and not a range because it assigns the range to
/// multiple leaf tokens, which is only valid if it's a zero-width range
pub fn vname_tv<'a: 'b, 'b, A: AtomTok + 'a, X: ExtraTok + 'a>(
name: &'b PathSlice,
pos: u32,
) -> impl Iterator<Item = TokTree<'a, A, X>> + 'b { ) -> impl Iterator<Item = TokTree<'a, A, X>> + 'b {
let (head, tail) = name.split_first(); let (head, tail) = name.split_first().expect("Empty vname");
iter::once(Token::Name(head)) iter::once(Token::Name(head.clone()))
.chain(tail.iter().flat_map(|t| [Token::NS, Token::Name(t)])) .chain(tail.iter().flat_map(|t| [Token::NS, Token::Name(t.clone())]))
.map(move |t| t.at(ran.clone())) .map(move |t| t.at(pos..pos))
} }
pub fn wrap_tokv<'a, A: AtomInTok + 'a, X: 'a>( pub fn wrap_tokv<'a, A: AtomTok, X: ExtraTok>(
items: Vec<TokTree<'a, A, X>>, items: impl IntoIterator<Item = TokTree<'a, A, X>>
range: Range<u32>,
) -> TokTree<'a, A, X> { ) -> TokTree<'a, A, X> {
match items.len() { let items_v = items.into_iter().collect_vec();
1 => items.into_iter().next().unwrap(), match items_v.len() {
_ => Token::S(api::Paren::Round, items).at(range), 0 => panic!("A tokv with no elements is illegal"),
1 => items_v.into_iter().next().unwrap(),
_ => {
let range = items_v.first().unwrap().range.start..items_v.last().unwrap().range.end;
Token::S(api::Paren::Round, items_v).at(range)
},
} }
} }
pub use api::Paren; pub use api::Paren;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Token<'a, A: AtomInTok, X> { pub enum Token<'a, A: AtomTok, X: ExtraTok> {
Comment(Arc<String>), Comment(Arc<String>),
LambdaHead(Vec<TokTree<'a, A, X>>), LambdaHead(Vec<TokTree<'a, A, X>>),
Name(Tok<String>), Name(Tok<String>),
@@ -155,14 +207,25 @@ pub enum Token<'a, A: AtomInTok, X> {
BR, BR,
S(Paren, Vec<TokTree<'a, A, X>>), S(Paren, Vec<TokTree<'a, A, X>>),
Atom(A), Atom(A),
Bottom(Vec<OrcErr>), Bottom(OrcErrv),
Slot(TreeHandle<'a>), Slot(TokHandle<'a>),
X(X), X(X),
Ph(Ph),
Macro(Option<NotNan<f64>>),
} }
impl<'a, A: AtomInTok, X> Token<'a, A, X> { impl<'a, A: AtomTok, X: ExtraTok> Token<'a, A, X> {
pub fn at(self, range: Range<u32>) -> TokTree<'a, A, X> { TokTree { range, tok: self } } pub fn at(self, range: Range<u32>) -> TokTree<'a, A, X> { TokTree { range, tok: self } }
pub fn is_kw(&self, tk: Tok<String>) -> bool {
matches!(self, Token::Name(n) if *n == tk)
} }
impl<'a, A: AtomInTok + Display, X: Display> Display for Token<'a, A, X> { pub fn as_s(&self, par: Paren) -> Option<&[TokTree<'a, A, X>]> {
match self {
Self::S(p, b) if *p == par => Some(b),
_ => None,
}
}
}
impl<'a, A: AtomTok, X: ExtraTok> Display for Token<'a, A, X> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
thread_local! { thread_local! {
static PAREN_LEVEL: RefCell<usize> = 0.into(); static PAREN_LEVEL: RefCell<usize> = 0.into();
@@ -177,29 +240,43 @@ impl<'a, A: AtomInTok + Display, X: Display> Display for Token<'a, A, X> {
match self { match self {
Self::Atom(a) => f.write_str(&indent(&format!("{a} "), get_indent(), false)), Self::Atom(a) => f.write_str(&indent(&format!("{a} "), get_indent(), false)),
Self::BR => write!(f, "\n{}", " ".repeat(get_indent())), Self::BR => write!(f, "\n{}", " ".repeat(get_indent())),
Self::Bottom(err) => write!( Self::Bottom(err) if err.len() == 1 => write!(f, "Bottom({}) ", err.one().unwrap()),
f, Self::Bottom(err) => {
"Botttom({})", write!(f, "Botttom(\n{}) ", indent(&err.to_string(), get_indent() + 1, true))
err.iter().map(|e| format!("{}: {}", e.description, e.message)).join(", ") },
),
Self::Comment(c) => write!(f, "--[{c}]-- "), Self::Comment(c) => write!(f, "--[{c}]-- "),
Self::LambdaHead(arg) => with_indent(|| write!(f, "\\ {} . ", ttv_fmt(arg))), Self::LambdaHead(arg) => with_indent(|| write!(f, "\\ {} . ", ttv_fmt(arg))),
Self::NS => f.write_str(":: "), Self::NS => f.write_str(":: "),
Self::Name(n) => f.write_str(n), Self::Name(n) => write!(f, "{n} "),
Self::Slot(th) => write!(f, "{th} "), Self::Slot(th) => write!(f, "{th} "),
Self::Ph(Ph { kind, name }) => match &kind {
PhKind::Scalar => write!(f, "${name}"),
PhKind::Vector { at_least_one, priority } => {
if *at_least_one { write!(f, ".")? }
write!(f, "..${name}")?;
if 0 < *priority { write!(f, "{priority}") } else { Ok(()) }
}
}
Self::S(p, b) => { Self::S(p, b) => {
let (lp, rp, _) = PARENS.iter().find(|(_, _, par)| par == p).unwrap(); let (lp, rp, _) = PARENS.iter().find(|(_, _, par)| par == p).unwrap();
f.write_char(*lp)?; write!(f, "{lp} ")?;
with_indent(|| f.write_str(&ttv_fmt(b)))?; with_indent(|| f.write_str(&ttv_fmt(b)))?;
f.write_char(*rp) write!(f, "{rp} ")
}, },
Self::X(x) => write!(f, "{x} "), Self::X(x) => write!(f, "{x} "),
Self::Macro(None) => write!(f, "macro "),
Self::Macro(Some(prio)) => write!(f, "macro({prio})"),
} }
} }
} }
pub fn ttv_fmt<'a>( pub fn ttv_range(ttv: &[TokTree<'_, impl AtomTok, impl ExtraTok>]) -> Range<u32> {
ttv: impl IntoIterator<Item = &'a TokTree<'a, impl AtomInTok + 'a, impl Display + 'a>>, assert!(!ttv.is_empty(), "Empty slice has no range");
ttv.first().unwrap().range.start..ttv.last().unwrap().range.end
}
pub fn ttv_fmt<'a: 'b, 'b>(
ttv: impl IntoIterator<Item = &'b TokTree<'a, impl AtomTok + 'b, impl ExtraTok + 'b>>,
) -> String { ) -> String {
ttv.into_iter().join("") ttv.into_iter().join("")
} }
@@ -214,13 +291,23 @@ pub fn indent(s: &str, lvl: usize, first: bool) -> String {
} }
} }
#[derive(Clone, Debug)]
pub struct Ph {
pub name: Tok<String>,
pub kind: PhKind,
}
impl Ph {
pub fn from_api(api: &Placeholder) -> Self { Self { name: deintern(api.name), kind: api.kind } }
pub fn to_api(&self) -> Placeholder { Placeholder { name: self.name.marker(), kind: self.kind } }
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
#[test] #[test]
fn test_covariance() { fn test_covariance() {
fn _f<'a>(x: Token<'static, Never, ()>) -> Token<'a, Never, ()> { x } fn _f<'a>(x: Token<'static, Never, Never>) -> Token<'a, Never, Never> { x }
} }
#[test] #[test]

View File

@@ -3,44 +3,42 @@ use std::fmt;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::{Deref, Range}; use std::ops::{Deref, Range};
use std::sync::OnceLock; use std::sync::{Arc, OnceLock};
use dyn_clone::{clone_box, DynClone}; use dyn_clone::{clone_box, DynClone};
use never::Never; use orchid_api_traits::{enc_vec, Coding, Decode, Encode, Request};
use orchid_api::ExprTicket;
use orchid_api_traits::{enc_vec, Coding, Decode, Request};
use orchid_base::error::{mk_err, OrcErr, OrcRes}; use orchid_base::error::{mk_err, OrcErr, OrcRes};
use orchid_base::intern; use orchid_base::intern;
use orchid_base::location::Pos; use orchid_base::location::Pos;
use orchid_base::name::Sym;
use orchid_base::reqnot::Requester; use orchid_base::reqnot::Requester;
use orchid_base::tree::AtomInTok; use orchid_base::tree::AtomTok;
use trait_set::trait_set; use trait_set::trait_set;
use crate::api; use crate::api;
// use crate::error::{ProjectError, ProjectResult}; // use crate::error::{ProjectError, ProjectResult};
use crate::expr::{ExprHandle, GenClause, GenExpr, OwnedExpr}; use crate::expr::{Expr, ExprData, ExprHandle, ExprKind};
use crate::system::{atom_info_for, downcast_atom, DynSystemCard, SysCtx}; use crate::system::{atom_info_for, downcast_atom, DynSystemCard, SysCtx};
pub trait AtomCard: 'static + Sized { pub trait AtomCard: 'static + Sized {
type Data: Clone + Coding + Sized; type Data: Clone + Coding + Sized;
type Req: Coding;
} }
pub trait AtomicVariant {} pub trait AtomicVariant {}
pub trait Atomic: 'static + Sized { pub trait Atomic: 'static + Sized {
type Variant: AtomicVariant; type Variant: AtomicVariant;
type Data: Clone + Coding + Sized; type Data: Clone + Coding + Sized;
type Req: Coding; fn reg_reqs() -> MethodSet<Self>;
} }
impl<A: Atomic> AtomCard for A { impl<A: Atomic> AtomCard for A {
type Data = <Self as Atomic>::Data; type Data = <Self as Atomic>::Data;
type Req = <Self as Atomic>::Req;
} }
pub trait AtomicFeatures: Atomic { pub trait AtomicFeatures: Atomic {
fn factory(self) -> AtomFactory; fn factory(self) -> AtomFactory;
type Info: AtomDynfo; type Info: AtomDynfo;
const INFO: &'static Self::Info; fn info() -> Self::Info;
fn dynfo() -> Box<dyn AtomDynfo>;
} }
pub trait ToAtom { pub trait ToAtom {
fn to_atom_factory(self) -> AtomFactory; fn to_atom_factory(self) -> AtomFactory;
@@ -54,17 +52,18 @@ impl ToAtom for AtomFactory {
pub trait AtomicFeaturesImpl<Variant: AtomicVariant> { pub trait AtomicFeaturesImpl<Variant: AtomicVariant> {
fn _factory(self) -> AtomFactory; fn _factory(self) -> AtomFactory;
type _Info: AtomDynfo; type _Info: AtomDynfo;
const _INFO: &'static Self::_Info; fn _info() -> Self::_Info;
} }
impl<A: Atomic + AtomicFeaturesImpl<A::Variant> + ?Sized> AtomicFeatures for A { impl<A: Atomic + AtomicFeaturesImpl<A::Variant>> AtomicFeatures for A {
fn factory(self) -> AtomFactory { self._factory() } fn factory(self) -> AtomFactory { self._factory() }
type Info = <Self as AtomicFeaturesImpl<A::Variant>>::_Info; type Info = <Self as AtomicFeaturesImpl<A::Variant>>::_Info;
const INFO: &'static Self::Info = Self::_INFO; fn info() -> Self::Info { Self::_info() }
fn dynfo() -> Box<dyn AtomDynfo> { Box::new(Self::info()) }
} }
pub fn get_info<A: AtomCard>( pub fn get_info<A: AtomCard>(
sys: &(impl DynSystemCard + ?Sized), sys: &(impl DynSystemCard + ?Sized),
) -> (api::AtomId, &'static dyn AtomDynfo) { ) -> (api::AtomId, Box<dyn AtomDynfo>) {
atom_info_for(sys, TypeId::of::<A>()).unwrap_or_else(|| { atom_info_for(sys, TypeId::of::<A>()).unwrap_or_else(|| {
panic!("Atom {} not associated with system {}", type_name::<A>(), sys.name()) panic!("Atom {} not associated with system {}", type_name::<A>(), sys.name())
}) })
@@ -72,34 +71,47 @@ pub fn get_info<A: AtomCard>(
#[derive(Clone)] #[derive(Clone)]
pub struct ForeignAtom<'a> { pub struct ForeignAtom<'a> {
pub expr: Option<ExprHandle>, pub expr: Option<Arc<ExprHandle>>,
pub char_marker: PhantomData<&'a ()>, pub _life: PhantomData<&'a ()>,
pub ctx: SysCtx, pub ctx: SysCtx,
pub atom: api::Atom, pub atom: api::Atom,
pub pos: Pos, pub pos: Pos,
} }
impl<'a> ForeignAtom<'a> { impl<'a> ForeignAtom<'a> {
pub fn oex_opt(self) -> Option<OwnedExpr> { pub fn oex_opt(self) -> Option<Expr> {
self.expr.map(|handle| { let (handle, pos) = (self.expr.as_ref()?.clone(), self.pos.clone());
let gen_expr = GenExpr { pos: self.pos, clause: GenClause::Atom(handle.tk, self.atom) }; let data = ExprData { pos, kind: ExprKind::Atom(ForeignAtom { _life: PhantomData, ..self }) };
OwnedExpr { handle, val: OnceLock::from(Box::new(gen_expr)) } Some(Expr { handle: Some(handle), val: OnceLock::from(data) })
})
} }
} }
impl ForeignAtom<'static> { impl ForeignAtom<'static> {
pub fn oex(self) -> OwnedExpr { self.oex_opt().unwrap() } pub fn oex(self) -> Expr { self.oex_opt().unwrap() }
pub(crate) fn new(handle: Arc<ExprHandle>, atom: api::Atom, pos: Pos) -> Self {
ForeignAtom { _life: PhantomData, atom, ctx: handle.ctx.clone(), expr: Some(handle), pos }
}
pub fn request<M: AtomMethod>(&self, m: M) -> Option<M::Response> {
let rep = self.ctx.reqnot.request(api::Fwd(
self.atom.clone(),
Sym::parse(M::NAME).unwrap().tok().marker(),
enc_vec(&m)
))?;
Some(M::Response::decode(&mut &rep[..]))
}
} }
impl<'a> fmt::Display for ForeignAtom<'a> { impl<'a> fmt::Display for ForeignAtom<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}::{:?}", if self.expr.is_some() { "Clause" } else { "Tok" }, self.atom) write!(f, "{}::{:?}", if self.expr.is_some() { "Clause" } else { "Tok" }, self.atom)
} }
} }
impl<'a> AtomInTok for ForeignAtom<'a> { impl<'a> fmt::Debug for ForeignAtom<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ForeignAtom({self})") }
}
impl<'a> AtomTok for ForeignAtom<'a> {
type Context = SysCtx; type Context = SysCtx;
fn from_api(atom: &api::Atom, pos: Range<u32>, ctx: &mut Self::Context) -> Self { fn from_api(atom: &api::Atom, pos: Range<u32>, ctx: &mut Self::Context) -> Self {
Self { Self {
atom: atom.clone(), atom: atom.clone(),
char_marker: PhantomData, _life: PhantomData,
ctx: ctx.clone(), ctx: ctx.clone(),
expr: None, expr: None,
pos: Pos::Range(pos), pos: Pos::Range(pos),
@@ -108,7 +120,7 @@ impl<'a> AtomInTok for ForeignAtom<'a> {
fn to_api(&self) -> orchid_api::Atom { self.atom.clone() } fn to_api(&self) -> orchid_api::Atom { self.atom.clone() }
} }
pub struct NotTypAtom(pub Pos, pub OwnedExpr, pub &'static dyn AtomDynfo); pub struct NotTypAtom(pub Pos, pub Expr, pub Box<dyn AtomDynfo>);
impl NotTypAtom { impl NotTypAtom {
pub fn mk_err(&self) -> OrcErr { pub fn mk_err(&self) -> OrcErr {
mk_err( mk_err(
@@ -119,26 +131,86 @@ impl NotTypAtom {
} }
} }
pub trait AtomMethod: Request {
const NAME: &str;
}
pub trait Supports<M: AtomMethod>: AtomCard {
fn handle(&self, ctx: SysCtx, req: M) -> <M as Request>::Response;
}
trait_set! {
trait AtomReqCb<A> = Fn(&A, SysCtx, &mut dyn Read, &mut dyn Write) + Send + Sync
}
pub struct AtomReqHandler<A: AtomCard> {
key: Sym,
cb: Box<dyn AtomReqCb<A>>,
}
pub struct MethodSet<A: AtomCard> {
handlers: Vec<AtomReqHandler<A>>,
}
impl<A: AtomCard> MethodSet<A> {
pub fn new() -> Self { Self{ handlers: vec![] } }
pub fn handle<M: AtomMethod>(mut self) -> Self where A: Supports<M> {
self.handlers.push(AtomReqHandler {
key: Sym::parse(M::NAME).expect("AtomMethod::NAME cannoot be empty"),
cb: Box::new(move |
a: &A,
ctx: SysCtx,
req: &mut dyn Read,
rep: &mut dyn Write
| {
Supports::<M>::handle(a, ctx, M::decode(req)).encode(rep);
})
});
self
}
pub(crate) fn dispatch(
&self, atom: &A, ctx: SysCtx, key: Sym, req: &mut dyn Read, rep: &mut dyn Write
) -> bool {
match self.handlers.iter().find(|h| h.key == key) {
None => false,
Some(handler) => {
(handler.cb)(atom, ctx, req, rep);
true
},
}
}
}
impl<A: AtomCard> Default for MethodSet<A> {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct TypAtom<'a, A: AtomicFeatures> { pub struct TypAtom<'a, A: AtomicFeatures> {
pub data: ForeignAtom<'a>, pub data: ForeignAtom<'a>,
pub value: A::Data, pub value: A::Data,
} }
impl<A: AtomicFeatures> TypAtom<'static, A> { impl<A: AtomicFeatures> TypAtom<'static, A> {
pub fn downcast(expr: ExprHandle) -> Result<Self, NotTypAtom> { pub fn downcast(expr: Arc<ExprHandle>) -> Result<Self, NotTypAtom> {
match OwnedExpr::new(expr).foreign_atom() { match Expr::new(expr).foreign_atom() {
Err(oe) => Err(NotTypAtom(oe.get_data().pos.clone(), oe, A::INFO)), Err(oe) => Err(NotTypAtom(oe.get_data().pos.clone(), oe, Box::new(A::info()))),
Ok(atm) => match downcast_atom::<A>(atm) { Ok(atm) => match downcast_atom::<A>(atm) {
Err(fa) => Err(NotTypAtom(fa.pos.clone(), fa.oex(), A::INFO)), Err(fa) => Err(NotTypAtom(fa.pos.clone(), fa.oex(), Box::new(A::info()))),
Ok(tatom) => Ok(tatom), Ok(tatom) => Ok(tatom),
}, },
} }
} }
} }
impl<'a, A: AtomicFeatures> TypAtom<'a, A> { impl<'a, A: AtomicFeatures> TypAtom<'a, A> {
pub fn request<R: Coding + Into<A::Req> + Request>(&self, req: R) -> R::Response { pub fn request<M: AtomMethod>(&self, req: M) -> M::Response where A: Supports<M> {
R::Response::decode( M::Response::decode(
&mut &self.data.ctx.reqnot.request(api::Fwd(self.data.atom.clone(), enc_vec(&req)))[..], &mut &self.data.ctx.reqnot.request(api::Fwd(
self.data.atom.clone(),
Sym::parse(M::NAME).unwrap().tok().marker(),
enc_vec(&req)
)).unwrap()[..]
) )
} }
} }
@@ -153,14 +225,13 @@ pub trait AtomDynfo: Send + Sync + 'static {
fn tid(&self) -> TypeId; fn tid(&self) -> TypeId;
fn name(&self) -> &'static str; fn name(&self) -> &'static str;
fn decode(&self, ctx: AtomCtx<'_>) -> Box<dyn Any>; fn decode(&self, ctx: AtomCtx<'_>) -> Box<dyn Any>;
fn call(&self, ctx: AtomCtx<'_>, arg: api::ExprTicket) -> GenExpr; fn call(&self, ctx: AtomCtx<'_>, arg: api::ExprTicket) -> Expr;
fn call_ref(&self, ctx: AtomCtx<'_>, arg: api::ExprTicket) -> GenExpr; fn call_ref(&self, ctx: AtomCtx<'_>, arg: api::ExprTicket) -> Expr;
fn same(&self, ctx: AtomCtx<'_>, other: &api::Atom) -> bool;
fn print(&self, ctx: AtomCtx<'_>) -> String; fn print(&self, ctx: AtomCtx<'_>) -> String;
fn handle_req(&self, ctx: AtomCtx<'_>, req: &mut dyn Read, rep: &mut dyn Write); fn handle_req(&self, ctx: AtomCtx<'_>, key: Sym, req: &mut dyn Read, rep: &mut dyn Write) -> bool;
fn command(&self, ctx: AtomCtx<'_>) -> OrcRes<Option<GenExpr>>; fn command(&self, ctx: AtomCtx<'_>) -> OrcRes<Option<Expr>>;
fn serialize(&self, ctx: AtomCtx<'_>, write: &mut dyn Write) -> Vec<ExprTicket>; fn serialize(&self, ctx: AtomCtx<'_>, write: &mut dyn Write) -> Vec<api::ExprTicket>;
fn deserialize(&self, ctx: SysCtx, data: &[u8], refs: &[ExprTicket]) -> api::Atom; fn deserialize(&self, ctx: SysCtx, data: &[u8], refs: &[api::ExprTicket]) -> api::Atom;
fn drop(&self, ctx: AtomCtx<'_>); fn drop(&self, ctx: AtomCtx<'_>);
} }
@@ -177,6 +248,12 @@ impl AtomFactory {
impl Clone for AtomFactory { impl Clone for AtomFactory {
fn clone(&self) -> Self { AtomFactory(clone_box(&*self.0)) } fn clone(&self) -> Self { AtomFactory(clone_box(&*self.0)) }
} }
impl fmt::Debug for AtomFactory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "AtomFactory") }
}
impl fmt::Display for AtomFactory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "AtomFactory") }
}
pub fn err_not_callable() -> OrcErr { pub fn err_not_callable() -> OrcErr {
mk_err(intern!(str: "This atom is not callable"), "Attempted to apply value as function", []) mk_err(intern!(str: "This atom is not callable"), "Attempted to apply value as function", [])
@@ -185,26 +262,3 @@ pub fn err_not_callable() -> OrcErr {
pub fn err_not_command() -> OrcErr { pub fn err_not_command() -> OrcErr {
mk_err(intern!(str: "This atom is not a command"), "Settled on an inactionable value", []) mk_err(intern!(str: "This atom is not a command"), "Settled on an inactionable value", [])
} }
pub trait ReqPck<T: AtomCard + ?Sized>: Sized {
type W: Write + ?Sized;
fn unpack<'a>(self) -> (T::Req, &'a mut Self::W, SysCtx)
where Self: 'a;
fn never(self)
where T: AtomCard<Req = Never> {
}
}
pub(crate) struct RequestPack<'a, T: AtomCard + ?Sized, W: Write + ?Sized> {
pub req: T::Req,
pub write: &'a mut W,
pub sys: SysCtx,
}
impl<'a, T: AtomCard + ?Sized, W: Write + ?Sized> ReqPck<T> for RequestPack<'a, T, W> {
type W = W;
fn unpack<'b>(self) -> (<T as AtomCard>::Req, &'b mut Self::W, SysCtx)
where 'a: 'b {
(self.req, self.write, self.sys)
}
}

View File

@@ -1,19 +1,19 @@
use std::any::{type_name, Any, TypeId}; use std::any::{type_name, Any, TypeId};
use std::borrow::Cow; use std::borrow::Cow;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::marker::PhantomData; use std::sync::Arc;
use itertools::Itertools; use itertools::Itertools;
use orchid_api_traits::{enc_vec, Decode, Encode}; use orchid_api_traits::{enc_vec, Decode, Encode};
use orchid_base::error::OrcRes; use orchid_base::error::OrcRes;
use orchid_base::id_store::{IdRecord, IdStore}; use orchid_base::id_store::{IdRecord, IdStore};
use orchid_base::name::Sym;
use crate::api; use crate::api;
use crate::atom::{ use crate::atom::{
err_not_callable, err_not_command, get_info, AtomCard, AtomCtx, AtomDynfo, AtomFactory, Atomic, err_not_callable, err_not_command, get_info, AtomCard, AtomCtx, AtomDynfo, AtomFactory, MethodSet, Atomic, AtomicFeaturesImpl, AtomicVariant,
AtomicFeaturesImpl, AtomicVariant, ReqPck, RequestPack,
}; };
use crate::expr::{bot, ExprHandle, GenExpr}; use crate::expr::{bot, Expr, ExprHandle};
use crate::system::SysCtx; use crate::system::SysCtx;
pub struct OwnedVariant; pub struct OwnedVariant;
@@ -28,15 +28,17 @@ impl<A: OwnedAtom + Atomic<Variant = OwnedVariant>> AtomicFeaturesImpl<OwnedVari
api::Atom { drop: Some(api::AtomId(rec.id())), data, owner: ctx.id } api::Atom { drop: Some(api::AtomId(rec.id())), data, owner: ctx.id }
}) })
} }
fn _info() -> Self::_Info {
OwnedAtomDynfo(A::reg_reqs())
}
type _Info = OwnedAtomDynfo<A>; type _Info = OwnedAtomDynfo<A>;
const _INFO: &'static Self::_Info = &OwnedAtomDynfo(PhantomData);
} }
fn with_atom<U>(id: api::AtomId, f: impl FnOnce(IdRecord<'_, Box<dyn DynOwnedAtom>>) -> U) -> U { fn with_atom<U>(id: api::AtomId, f: impl FnOnce(IdRecord<'_, Box<dyn DynOwnedAtom>>) -> U) -> U {
f(OBJ_STORE.get(id.0).unwrap_or_else(|| panic!("Received invalid atom ID: {}", id.0))) f(OBJ_STORE.get(id.0).unwrap_or_else(|| panic!("Received invalid atom ID: {}", id.0)))
} }
pub struct OwnedAtomDynfo<T: OwnedAtom>(PhantomData<T>); pub struct OwnedAtomDynfo<T: OwnedAtom>(MethodSet<T>);
impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> { impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
fn print(&self, AtomCtx(_, id, ctx): AtomCtx<'_>) -> String { fn print(&self, AtomCtx(_, id, ctx): AtomCtx<'_>) -> String {
with_atom(id.unwrap(), |a| a.dyn_print(ctx)) with_atom(id.unwrap(), |a| a.dyn_print(ctx))
@@ -46,19 +48,18 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
fn decode(&self, AtomCtx(data, ..): AtomCtx) -> Box<dyn Any> { fn decode(&self, AtomCtx(data, ..): AtomCtx) -> Box<dyn Any> {
Box::new(<T as AtomCard>::Data::decode(&mut &data[..])) Box::new(<T as AtomCard>::Data::decode(&mut &data[..]))
} }
fn call(&self, AtomCtx(_, id, ctx): AtomCtx, arg: api::ExprTicket) -> GenExpr { fn call(&self, AtomCtx(_, id, ctx): AtomCtx, arg: api::ExprTicket) -> Expr {
with_atom(id.unwrap(), |a| a.remove().dyn_call(ctx, arg)) with_atom(id.unwrap(), |a| a.remove().dyn_call(ctx, arg))
} }
fn call_ref(&self, AtomCtx(_, id, ctx): AtomCtx, arg: api::ExprTicket) -> GenExpr { fn call_ref(&self, AtomCtx(_, id, ctx): AtomCtx, arg: api::ExprTicket) -> Expr {
with_atom(id.unwrap(), |a| a.dyn_call_ref(ctx, arg)) with_atom(id.unwrap(), |a| a.dyn_call_ref(ctx, arg))
} }
fn same(&self, AtomCtx(_, id, ctx): AtomCtx, a2: &api::Atom) -> bool { fn handle_req(&self, AtomCtx(_, id, ctx): AtomCtx, key: Sym, req: &mut dyn Read, rep: &mut dyn Write) -> bool {
with_atom(id.unwrap(), |a1| with_atom(a2.drop.unwrap(), |a2| a1.dyn_same(ctx, &**a2))) with_atom(id.unwrap(), |a| {
self.0.dispatch(a.as_any_ref().downcast_ref().unwrap(), ctx, key, req, rep)
})
} }
fn handle_req(&self, AtomCtx(_, id, ctx): AtomCtx, req: &mut dyn Read, rep: &mut dyn Write) { fn command(&self, AtomCtx(_, id, ctx): AtomCtx<'_>) -> OrcRes<Option<Expr>> {
with_atom(id.unwrap(), |a| a.dyn_handle_req(ctx, req, rep))
}
fn command(&self, AtomCtx(_, id, ctx): AtomCtx<'_>) -> OrcRes<Option<GenExpr>> {
with_atom(id.unwrap(), |a| a.remove().dyn_command(ctx)) with_atom(id.unwrap(), |a| a.remove().dyn_command(ctx))
} }
fn drop(&self, AtomCtx(_, id, ctx): AtomCtx) { fn drop(&self, AtomCtx(_, id, ctx): AtomCtx) {
@@ -71,10 +72,13 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
) -> Vec<api::ExprTicket> { ) -> Vec<api::ExprTicket> {
let id = id.unwrap(); let id = id.unwrap();
id.encode(write); id.encode(write);
with_atom(id, |a| a.dyn_serialize(ctx, write)).into_iter().map(|t| t.into_tk()).collect_vec() with_atom(id, |a| a.dyn_serialize(ctx, write))
.into_iter()
.map(|t| t.handle.unwrap().tk)
.collect_vec()
} }
fn deserialize(&self, ctx: SysCtx, data: &[u8], refs: &[api::ExprTicket]) -> orchid_api::Atom { fn deserialize(&self, ctx: SysCtx, data: &[u8], refs: &[api::ExprTicket]) -> orchid_api::Atom {
let refs = refs.iter().map(|tk| ExprHandle::from_args(ctx.clone(), *tk)); let refs = refs.iter().map(|tk| Expr::new(Arc::new(ExprHandle::from_args(ctx.clone(), *tk))));
let obj = T::deserialize(DeserCtxImpl(data, &ctx), T::Refs::from_iter(refs)); let obj = T::deserialize(DeserCtxImpl(data, &ctx), T::Refs::from_iter(refs));
obj._factory().build(ctx) obj._factory().build(ctx)
} }
@@ -100,27 +104,25 @@ impl<'a> DeserializeCtx for DeserCtxImpl<'a> {
} }
pub trait RefSet { pub trait RefSet {
fn from_iter<I: Iterator<Item = ExprHandle> + ExactSizeIterator>(refs: I) -> Self; fn from_iter<I: Iterator<Item = Expr> + ExactSizeIterator>(refs: I) -> Self;
fn to_vec(self) -> Vec<ExprHandle>; fn to_vec(self) -> Vec<Expr>;
} }
impl RefSet for () { impl RefSet for () {
fn to_vec(self) -> Vec<ExprHandle> { Vec::new() } fn to_vec(self) -> Vec<Expr> { Vec::new() }
fn from_iter<I: Iterator<Item = ExprHandle> + ExactSizeIterator>(refs: I) -> Self { fn from_iter<I: Iterator<Item = Expr> + ExactSizeIterator>(refs: I) -> Self {
assert_eq!(refs.len(), 0, "Expected no refs") assert_eq!(refs.len(), 0, "Expected no refs")
} }
} }
impl RefSet for Vec<ExprHandle> { impl RefSet for Vec<Expr> {
fn from_iter<I: Iterator<Item = ExprHandle> + ExactSizeIterator>(refs: I) -> Self { fn from_iter<I: Iterator<Item = Expr> + ExactSizeIterator>(refs: I) -> Self { refs.collect_vec() }
refs.collect_vec() fn to_vec(self) -> Vec<Expr> { self }
}
fn to_vec(self) -> Vec<ExprHandle> { self }
} }
impl<const N: usize> RefSet for [ExprHandle; N] { impl<const N: usize> RefSet for [Expr; N] {
fn to_vec(self) -> Vec<ExprHandle> { self.into_iter().collect_vec() } fn to_vec(self) -> Vec<Expr> { self.into_iter().collect_vec() }
fn from_iter<I: Iterator<Item = ExprHandle> + ExactSizeIterator>(refs: I) -> Self { fn from_iter<I: Iterator<Item = Expr> + ExactSizeIterator>(refs: I) -> Self {
assert_eq!(refs.len(), N, "Wrong number of refs provided"); assert_eq!(refs.len(), N, "Wrong number of refs provided");
refs.collect_vec().try_into().unwrap_or_else(|_: Vec<_>| unreachable!()) refs.collect_vec().try_into().unwrap_or_else(|_: Vec<_>| unreachable!())
} }
@@ -131,22 +133,15 @@ pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Send + Sync + Any + Clone
type Refs: RefSet; type Refs: RefSet;
fn val(&self) -> Cow<'_, Self::Data>; fn val(&self) -> Cow<'_, Self::Data>;
#[allow(unused_variables)] #[allow(unused_variables)]
fn call_ref(&self, arg: ExprHandle) -> GenExpr { bot(err_not_callable()) } fn call_ref(&self, arg: ExprHandle) -> Expr { bot([err_not_callable()]) }
fn call(self, arg: ExprHandle) -> GenExpr { fn call(self, arg: ExprHandle) -> Expr {
let ctx = arg.get_ctx(); let ctx = arg.get_ctx();
let gcl = self.call_ref(arg); let gcl = self.call_ref(arg);
self.free(ctx); self.free(ctx);
gcl gcl
} }
#[allow(unused_variables)] #[allow(unused_variables)]
fn same(&self, ctx: SysCtx, other: &Self) -> bool { fn command(self, ctx: SysCtx) -> OrcRes<Option<Expr>> { Err(err_not_command().into()) }
let tname = type_name::<Self>();
writeln!(ctx.logger, "Override OwnedAtom::same for {tname} if it can appear in macro input");
false
}
fn handle_req(&self, pck: impl ReqPck<Self>);
#[allow(unused_variables)]
fn command(self, ctx: SysCtx) -> OrcRes<Option<GenExpr>> { Err(vec![err_not_command()]) }
#[allow(unused_variables)] #[allow(unused_variables)]
fn free(self, ctx: SysCtx) {} fn free(self, ctx: SysCtx) {}
#[allow(unused_variables)] #[allow(unused_variables)]
@@ -159,41 +154,27 @@ pub trait DynOwnedAtom: Send + Sync + 'static {
fn atom_tid(&self) -> TypeId; fn atom_tid(&self) -> TypeId;
fn as_any_ref(&self) -> &dyn Any; fn as_any_ref(&self) -> &dyn Any;
fn encode(&self, buffer: &mut dyn Write); fn encode(&self, buffer: &mut dyn Write);
fn dyn_call_ref(&self, ctx: SysCtx, arg: api::ExprTicket) -> GenExpr; fn dyn_call_ref(&self, ctx: SysCtx, arg: api::ExprTicket) -> Expr;
fn dyn_call(self: Box<Self>, ctx: SysCtx, arg: api::ExprTicket) -> GenExpr; fn dyn_call(self: Box<Self>, ctx: SysCtx, arg: api::ExprTicket) -> Expr;
fn dyn_same(&self, ctx: SysCtx, other: &dyn DynOwnedAtom) -> bool; fn dyn_command(self: Box<Self>, ctx: SysCtx) -> OrcRes<Option<Expr>>;
fn dyn_handle_req(&self, ctx: SysCtx, req: &mut dyn Read, rep: &mut dyn Write);
fn dyn_command(self: Box<Self>, ctx: SysCtx) -> OrcRes<Option<GenExpr>>;
fn dyn_free(self: Box<Self>, ctx: SysCtx); fn dyn_free(self: Box<Self>, ctx: SysCtx);
fn dyn_print(&self, ctx: SysCtx) -> String; fn dyn_print(&self, ctx: SysCtx) -> String;
fn dyn_serialize(&self, ctx: SysCtx, sink: &mut dyn Write) -> Vec<ExprHandle>; fn dyn_serialize(&self, ctx: SysCtx, sink: &mut dyn Write) -> Vec<Expr>;
} }
impl<T: OwnedAtom> DynOwnedAtom for T { impl<T: OwnedAtom> DynOwnedAtom for T {
fn atom_tid(&self) -> TypeId { TypeId::of::<T>() } fn atom_tid(&self) -> TypeId { TypeId::of::<T>() }
fn as_any_ref(&self) -> &dyn Any { self } fn as_any_ref(&self) -> &dyn Any { self }
fn encode(&self, buffer: &mut dyn Write) { self.val().as_ref().encode(buffer) } fn encode(&self, buffer: &mut dyn Write) { self.val().as_ref().encode(buffer) }
fn dyn_call_ref(&self, ctx: SysCtx, arg: api::ExprTicket) -> GenExpr { fn dyn_call_ref(&self, ctx: SysCtx, arg: api::ExprTicket) -> Expr {
self.call_ref(ExprHandle::from_args(ctx, arg)) self.call_ref(ExprHandle::from_args(ctx, arg))
} }
fn dyn_call(self: Box<Self>, ctx: SysCtx, arg: api::ExprTicket) -> GenExpr { fn dyn_call(self: Box<Self>, ctx: SysCtx, arg: api::ExprTicket) -> Expr {
self.call(ExprHandle::from_args(ctx, arg)) self.call(ExprHandle::from_args(ctx, arg))
} }
fn dyn_same(&self, ctx: SysCtx, other: &dyn DynOwnedAtom) -> bool { fn dyn_command(self: Box<Self>, ctx: SysCtx) -> OrcRes<Option<Expr>> { self.command(ctx) }
if TypeId::of::<Self>() != other.as_any_ref().type_id() {
return false;
}
let other_self = other.as_any_ref().downcast_ref().expect("The type_ids are the same");
self.same(ctx, other_self)
}
fn dyn_handle_req(&self, sys: SysCtx, req: &mut dyn Read, write: &mut dyn Write) {
let pack =
RequestPack::<T, dyn Write> { req: <Self as AtomCard>::Req::decode(req), write, sys };
self.handle_req(pack)
}
fn dyn_command(self: Box<Self>, ctx: SysCtx) -> OrcRes<Option<GenExpr>> { self.command(ctx) }
fn dyn_free(self: Box<Self>, ctx: SysCtx) { self.free(ctx) } fn dyn_free(self: Box<Self>, ctx: SysCtx) { self.free(ctx) }
fn dyn_print(&self, ctx: SysCtx) -> String { self.print(ctx) } fn dyn_print(&self, ctx: SysCtx) -> String { self.print(ctx) }
fn dyn_serialize(&self, ctx: SysCtx, sink: &mut dyn Write) -> Vec<ExprHandle> { fn dyn_serialize(&self, ctx: SysCtx, sink: &mut dyn Write) -> Vec<Expr> {
self.serialize(ctx, sink).to_vec() self.serialize(ctx, sink).to_vec()
} }
} }

View File

@@ -1,17 +1,15 @@
use std::any::{type_name, Any, TypeId}; use std::any::{type_name, Any, TypeId};
use std::io::Write; use std::io::Write;
use std::marker::PhantomData;
use orchid_api::ExprTicket; use orchid_api_traits::{enc_vec, Coding};
use orchid_api_traits::{enc_vec, Coding, Decode};
use orchid_base::error::OrcRes; use orchid_base::error::OrcRes;
use orchid_base::name::Sym;
use crate::api; use crate::api;
use crate::atom::{ use crate::atom::{
err_not_callable, err_not_command, get_info, AtomCard, AtomCtx, AtomDynfo, AtomFactory, Atomic, err_not_callable, err_not_command, get_info, AtomCard, AtomCtx, AtomDynfo, AtomFactory, MethodSet, Atomic, AtomicFeaturesImpl, AtomicVariant
AtomicFeaturesImpl, AtomicVariant, ReqPck, RequestPack,
}; };
use crate::expr::{bot, ExprHandle, GenExpr}; use crate::expr::{bot, Expr, ExprHandle};
use crate::system::SysCtx; use crate::system::SysCtx;
pub struct ThinVariant; pub struct ThinVariant;
@@ -25,11 +23,13 @@ impl<A: ThinAtom + Atomic<Variant = ThinVariant>> AtomicFeaturesImpl<ThinVariant
api::Atom { drop: None, data: buf, owner: ctx.id } api::Atom { drop: None, data: buf, owner: ctx.id }
}) })
} }
fn _info() -> Self::_Info {
ThinAtomDynfo(Self::reg_reqs())
}
type _Info = ThinAtomDynfo<Self>; type _Info = ThinAtomDynfo<Self>;
const _INFO: &'static Self::_Info = &ThinAtomDynfo(PhantomData);
} }
pub struct ThinAtomDynfo<T: ThinAtom>(PhantomData<T>); pub struct ThinAtomDynfo<T: ThinAtom>(MethodSet<T>);
impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> { impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
fn print(&self, AtomCtx(buf, _, ctx): AtomCtx<'_>) -> String { fn print(&self, AtomCtx(buf, _, ctx): AtomCtx<'_>) -> String {
T::decode(&mut &buf[..]).print(ctx) T::decode(&mut &buf[..]).print(ctx)
@@ -37,32 +37,29 @@ impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
fn tid(&self) -> TypeId { TypeId::of::<T>() } fn tid(&self) -> TypeId { TypeId::of::<T>() }
fn name(&self) -> &'static str { type_name::<T>() } fn name(&self) -> &'static str { type_name::<T>() }
fn decode(&self, AtomCtx(buf, ..): AtomCtx) -> Box<dyn Any> { Box::new(T::decode(&mut &buf[..])) } fn decode(&self, AtomCtx(buf, ..): AtomCtx) -> Box<dyn Any> { Box::new(T::decode(&mut &buf[..])) }
fn call(&self, AtomCtx(buf, _, ctx): AtomCtx, arg: api::ExprTicket) -> GenExpr { fn call(&self, AtomCtx(buf, _, ctx): AtomCtx, arg: api::ExprTicket) -> Expr {
T::decode(&mut &buf[..]).call(ExprHandle::from_args(ctx, arg)) T::decode(&mut &buf[..]).call(ExprHandle::from_args(ctx, arg))
} }
fn call_ref(&self, AtomCtx(buf, _, ctx): AtomCtx, arg: api::ExprTicket) -> GenExpr { fn call_ref(&self, AtomCtx(buf, _, ctx): AtomCtx, arg: api::ExprTicket) -> Expr {
T::decode(&mut &buf[..]).call(ExprHandle::from_args(ctx, arg)) T::decode(&mut &buf[..]).call(ExprHandle::from_args(ctx, arg))
} }
fn handle_req( fn handle_req(
&self, &self,
AtomCtx(buf, _, sys): AtomCtx, AtomCtx(buf, _, sys): AtomCtx,
key: Sym,
req: &mut dyn std::io::Read, req: &mut dyn std::io::Read,
write: &mut dyn Write, rep: &mut dyn Write,
) { ) -> bool {
let pack = RequestPack::<T, dyn Write> { req: Decode::decode(req), write, sys }; self.0.dispatch(&T::decode(&mut &buf[..]), sys, key, req, rep)
T::decode(&mut &buf[..]).handle_req(pack)
} }
fn same(&self, AtomCtx(buf, _, ctx): AtomCtx, a2: &api::Atom) -> bool { fn command(&self, AtomCtx(buf, _, ctx): AtomCtx<'_>) -> OrcRes<Option<Expr>> {
T::decode(&mut &buf[..]).same(ctx, &T::decode(&mut &a2.data[8..]))
}
fn command(&self, AtomCtx(buf, _, ctx): AtomCtx<'_>) -> OrcRes<Option<GenExpr>> {
T::decode(&mut &buf[..]).command(ctx) T::decode(&mut &buf[..]).command(ctx)
} }
fn serialize(&self, AtomCtx(buf, ..): AtomCtx<'_>, write: &mut dyn Write) -> Vec<ExprTicket> { fn serialize(&self, actx: AtomCtx<'_>, write: &mut dyn Write) -> Vec<api::ExprTicket> {
T::decode(&mut &buf[..]).encode(write); T::decode(&mut &actx.0[..]).encode(write);
Vec::new() Vec::new()
} }
fn deserialize(&self, ctx: SysCtx, data: &[u8], refs: &[ExprTicket]) -> api::Atom { fn deserialize(&self, ctx: SysCtx, data: &[u8], refs: &[api::ExprTicket]) -> api::Atom {
assert!(refs.is_empty(), "Refs found when deserializing thin atom"); assert!(refs.is_empty(), "Refs found when deserializing thin atom");
T::decode(&mut &data[..])._factory().build(ctx) T::decode(&mut &data[..])._factory().build(ctx)
} }
@@ -76,16 +73,9 @@ pub trait ThinAtom:
AtomCard<Data = Self> + Atomic<Variant = ThinVariant> + Coding + Send + Sync + 'static AtomCard<Data = Self> + Atomic<Variant = ThinVariant> + Coding + Send + Sync + 'static
{ {
#[allow(unused_variables)] #[allow(unused_variables)]
fn call(&self, arg: ExprHandle) -> GenExpr { bot(err_not_callable()) } fn call(&self, arg: ExprHandle) -> Expr { bot([err_not_callable()]) }
#[allow(unused_variables)] #[allow(unused_variables)]
fn same(&self, ctx: SysCtx, other: &Self) -> bool { fn command(&self, ctx: SysCtx) -> OrcRes<Option<Expr>> { Err(err_not_command().into()) }
let tname = type_name::<Self>();
writeln!(ctx.logger, "Override ThinAtom::same for {tname} if it can appear in macro input");
false
}
fn handle_req(&self, pck: impl ReqPck<Self>);
#[allow(unused_variables)]
fn command(&self, ctx: SysCtx) -> OrcRes<Option<GenExpr>> { Err(vec![err_not_command()]) }
#[allow(unused_variables)] #[allow(unused_variables)]
fn print(&self, ctx: SysCtx) -> String { format!("ThinAtom({})", type_name::<Self>()) } fn print(&self, ctx: SysCtx) -> String { format!("ThinAtom({})", type_name::<Self>()) }
} }

View File

@@ -3,19 +3,19 @@ use orchid_base::intern;
use orchid_base::location::Pos; use orchid_base::location::Pos;
use crate::atom::{AtomicFeatures, ToAtom, TypAtom}; use crate::atom::{AtomicFeatures, ToAtom, TypAtom};
use crate::expr::{atom, botv, ExprHandle, GenExpr, OwnedExpr}; use crate::expr::{atom, bot, Expr};
use crate::system::downcast_atom; use crate::system::downcast_atom;
pub trait TryFromExpr: Sized { pub trait TryFromExpr: Sized {
fn try_from_expr(expr: ExprHandle) -> OrcRes<Self>; fn try_from_expr(expr: Expr) -> OrcRes<Self>;
} }
impl TryFromExpr for OwnedExpr { impl TryFromExpr for Expr {
fn try_from_expr(expr: ExprHandle) -> OrcRes<Self> { Ok(OwnedExpr::new(expr)) } fn try_from_expr(expr: Expr) -> OrcRes<Self> { Ok(expr) }
} }
impl<T: TryFromExpr, U: TryFromExpr> TryFromExpr for (T, U) { impl<T: TryFromExpr, U: TryFromExpr> TryFromExpr for (T, U) {
fn try_from_expr(expr: ExprHandle) -> OrcRes<Self> { fn try_from_expr(expr: Expr) -> OrcRes<Self> {
Ok((T::try_from_expr(expr.clone())?, U::try_from_expr(expr)?)) Ok((T::try_from_expr(expr.clone())?, U::try_from_expr(expr)?))
} }
} }
@@ -29,31 +29,30 @@ fn err_type(pos: Pos) -> OrcErr {
} }
impl<'a, A: AtomicFeatures> TryFromExpr for TypAtom<'a, A> { impl<'a, A: AtomicFeatures> TryFromExpr for TypAtom<'a, A> {
fn try_from_expr(expr: ExprHandle) -> OrcRes<Self> { fn try_from_expr(expr: Expr) -> OrcRes<Self> {
OwnedExpr::new(expr) (expr.foreign_atom())
.foreign_atom() .map_err(|ex| err_not_atom(ex.pos.clone()).into())
.map_err(|ex| vec![err_not_atom(ex.pos.clone())]) .and_then(|f| downcast_atom(f).map_err(|f| err_type(f.pos).into()))
.and_then(|f| downcast_atom(f).map_err(|f| vec![err_type(f.pos)]))
} }
} }
pub trait ToExpr { pub trait ToExpr {
fn to_expr(self) -> GenExpr; fn to_expr(self) -> Expr;
} }
impl ToExpr for GenExpr { impl ToExpr for Expr {
fn to_expr(self) -> GenExpr { self } fn to_expr(self) -> Expr { self }
} }
impl<T: ToExpr> ToExpr for OrcRes<T> { impl<T: ToExpr> ToExpr for OrcRes<T> {
fn to_expr(self) -> GenExpr { fn to_expr(self) -> Expr {
match self { match self {
Err(e) => botv(e), Err(e) => bot(e),
Ok(t) => t.to_expr(), Ok(t) => t.to_expr(),
} }
} }
} }
impl<A: ToAtom> ToExpr for A { impl<A: ToAtom> ToExpr for A {
fn to_expr(self) -> GenExpr { atom(self) } fn to_expr(self) -> Expr { atom(self) }
} }

View File

@@ -6,16 +6,16 @@ use std::{mem, process, thread};
use hashbrown::HashMap; use hashbrown::HashMap;
use itertools::Itertools; use itertools::Itertools;
use orchid_api::DeserAtom; use orchid_api::ExtMsgSet;
use orchid_api_traits::{enc_vec, Decode, Encode}; use orchid_api_traits::{enc_vec, Decode, Encode};
use orchid_base::char_filter::{char_filter_match, char_filter_union, mk_char_filter}; use orchid_base::char_filter::{char_filter_match, char_filter_union, mk_char_filter};
use orchid_base::clone; use orchid_base::clone;
use orchid_base::error::errv_to_apiv;
use orchid_base::interner::{deintern, init_replica, sweep_replica}; use orchid_base::interner::{deintern, init_replica, sweep_replica};
use orchid_base::logging::Logger; use orchid_base::logging::Logger;
use orchid_base::macros::{mtreev_from_api, mtreev_to_api};
use orchid_base::name::{PathSlice, Sym}; use orchid_base::name::{PathSlice, Sym};
use orchid_base::parse::Snippet; use orchid_base::parse::{Comment, Snippet};
use orchid_base::reqnot::{ReqNot, Requester}; use orchid_base::reqnot::{ReqHandlish, ReqNot, RequestHandle, Requester};
use orchid_base::tree::{ttv_from_api, ttv_to_api}; use orchid_base::tree::{ttv_from_api, ttv_to_api};
use substack::Substack; use substack::Substack;
@@ -23,12 +23,16 @@ use crate::api;
use crate::atom::{AtomCtx, AtomDynfo}; use crate::atom::{AtomCtx, AtomDynfo};
use crate::atom_owned::OBJ_STORE; use crate::atom_owned::OBJ_STORE;
use crate::fs::VirtFS; use crate::fs::VirtFS;
use crate::lexer::{err_cascade, err_lexer_na, LexContext}; use crate::lexer::{err_cascade, err_not_applicable, LexContext};
use crate::macros::{apply_rule, RuleCtx};
use crate::msg::{recv_parent_msg, send_parent_msg}; use crate::msg::{recv_parent_msg, send_parent_msg};
use crate::system::{atom_by_idx, SysCtx}; use crate::system::{atom_by_idx, SysCtx};
use crate::system_ctor::{CtedObj, DynSystemCtor}; use crate::system_ctor::{CtedObj, DynSystemCtor};
use crate::tree::{do_extra, GenTok, GenTokTree, LazyMemberFactory, TIACtxImpl}; use crate::tree::{do_extra, GenTok, GenTokTree, LazyMemberFactory, TIACtxImpl};
pub type ExtReq = RequestHandle<ExtMsgSet>;
pub type ExtReqNot = ReqNot<ExtMsgSet>;
pub struct ExtensionData { pub struct ExtensionData {
pub name: &'static str, pub name: &'static str,
pub systems: &'static [&'static dyn DynSystemCtor], pub systems: &'static [&'static dyn DynSystemCtor],
@@ -56,7 +60,7 @@ pub fn with_atom_record<T>(
get_sys_ctx: &impl Fn(api::SysId, ReqNot<api::ExtMsgSet>) -> SysCtx, get_sys_ctx: &impl Fn(api::SysId, ReqNot<api::ExtMsgSet>) -> SysCtx,
reqnot: ReqNot<api::ExtMsgSet>, reqnot: ReqNot<api::ExtMsgSet>,
atom: &api::Atom, atom: &api::Atom,
cb: impl FnOnce(&'static dyn AtomDynfo, SysCtx, api::AtomId, &[u8]) -> T, cb: impl FnOnce(Box<dyn AtomDynfo>, SysCtx, api::AtomId, &[u8]) -> T,
) -> T { ) -> T {
let mut data = &atom.data[..]; let mut data = &atom.data[..];
let ctx = get_sys_ctx(atom.owner, reqnot); let ctx = get_sys_ctx(atom.owner, reqnot);
@@ -107,12 +111,12 @@ fn extension_main_logic(data: ExtensionData) {
api::HostExtNotif::AtomDrop(api::AtomDrop(sys_id, atom)) => api::HostExtNotif::AtomDrop(api::AtomDrop(sys_id, atom)) =>
OBJ_STORE.get(atom.0).unwrap().remove().dyn_free(mk_ctx(sys_id, reqnot)), OBJ_STORE.get(atom.0).unwrap().remove().dyn_free(mk_ctx(sys_id, reqnot)),
}), }),
clone!(systems, logger; move |req| match req.req() { clone!(systems, logger; move |hand, req| match req {
api::HostExtReq::Ping(ping@api::Ping) => req.handle(ping, &()), api::HostExtReq::Ping(ping@api::Ping) => hand.handle(&ping, &()),
api::HostExtReq::Sweep(sweep@api::Sweep) => req.handle(sweep, &sweep_replica()), api::HostExtReq::Sweep(sweep@api::Sweep) => hand.handle(&sweep, &sweep_replica()),
api::HostExtReq::SysReq(api::SysReq::NewSystem(new_sys)) => { api::HostExtReq::SysReq(api::SysReq::NewSystem(new_sys)) => {
let i = decls.iter().enumerate().find(|(_, s)| s.id == new_sys.system).unwrap().0; let i = decls.iter().enumerate().find(|(_, s)| s.id == new_sys.system).unwrap().0;
let cted = data.systems[i].new_system(new_sys); let cted = data.systems[i].new_system(&new_sys);
let mut vfses = HashMap::new(); let mut vfses = HashMap::new();
let lex_filter = cted.inst().dyn_lexers().iter().fold(api::CharFilter(vec![]), |cf, lx| { let lex_filter = cted.inst().dyn_lexers().iter().fold(api::CharFilter(vec![]), |cf, lx| {
let lxcf = mk_char_filter(lx.char_filter().iter().cloned()); let lxcf = mk_char_filter(lx.char_filter().iter().cloned());
@@ -123,7 +127,7 @@ fn extension_main_logic(data: ExtensionData) {
cted: cted.clone(), cted: cted.clone(),
id: new_sys.id, id: new_sys.id,
logger: logger.clone(), logger: logger.clone(),
reqnot: req.reqnot() reqnot: hand.reqnot()
}; };
let mut tia_ctx = TIACtxImpl{ let mut tia_ctx = TIACtxImpl{
lazy: &mut lazy_mems, lazy: &mut lazy_mems,
@@ -140,7 +144,7 @@ fn extension_main_logic(data: ExtensionData) {
cted, cted,
lazy_members: lazy_mems lazy_members: lazy_mems
}); });
req.handle(new_sys, &api::SystemInst { hand.handle(&new_sys, &api::SystemInst {
lex_filter, lex_filter,
const_root, const_root,
line_types: vec![] line_types: vec![]
@@ -148,16 +152,16 @@ fn extension_main_logic(data: ExtensionData) {
} }
api::HostExtReq::GetMember(get_tree@api::GetMember(sys_id, tree_id)) => { api::HostExtReq::GetMember(get_tree@api::GetMember(sys_id, tree_id)) => {
let mut systems_g = systems.lock().unwrap(); let mut systems_g = systems.lock().unwrap();
let sys = systems_g.get_mut(sys_id).expect("System not found"); let sys = systems_g.get_mut(&sys_id).expect("System not found");
let lazy = &mut sys.lazy_members; let lazy = &mut sys.lazy_members;
let (path, cb) = match lazy.insert(*tree_id, MemberRecord::Res) { let (path, cb) = match lazy.insert(tree_id, MemberRecord::Res) {
None => panic!("Tree for ID not found"), None => panic!("Tree for ID not found"),
Some(MemberRecord::Res) => panic!("This tree has already been transmitted"), Some(MemberRecord::Res) => panic!("This tree has already been transmitted"),
Some(MemberRecord::Gen(path, cb)) => (path, cb), Some(MemberRecord::Gen(path, cb)) => (path, cb),
}; };
let tree = cb.build(path.clone()); let tree = cb.build(path.clone());
req.handle(get_tree, &tree.into_api(&mut TIACtxImpl{ hand.handle(&get_tree, &tree.into_api(&mut TIACtxImpl{
sys: SysCtx::new(*sys_id, &sys.cted, &logger, req.reqnot()), sys: SysCtx::new(sys_id, &sys.cted, &logger, hand.reqnot()),
path: Substack::Bottom, path: Substack::Bottom,
basepath: &path, basepath: &path,
lazy, lazy,
@@ -165,100 +169,124 @@ fn extension_main_logic(data: ExtensionData) {
} }
api::HostExtReq::VfsReq(api::VfsReq::GetVfs(get_vfs@api::GetVfs(sys_id))) => { api::HostExtReq::VfsReq(api::VfsReq::GetVfs(get_vfs@api::GetVfs(sys_id))) => {
let systems_g = systems.lock().unwrap(); let systems_g = systems.lock().unwrap();
req.handle(get_vfs, &systems_g[sys_id].declfs) hand.handle(&get_vfs, &systems_g[&sys_id].declfs)
}
api::HostExtReq::SysReq(api::SysReq::SysFwded(fwd)) => {
let api::SysFwded(sys_id, payload) = fwd;
let ctx = mk_ctx(sys_id, hand.reqnot());
let sys = ctx.cted.inst();
sys.dyn_request(hand, payload)
} }
api::HostExtReq::VfsReq(api::VfsReq::VfsRead(vfs_read)) => { api::HostExtReq::VfsReq(api::VfsReq::VfsRead(vfs_read)) => {
let api::VfsRead(sys_id, vfs_id, path) = vfs_read; let api::VfsRead(sys_id, vfs_id, path) = &vfs_read;
let systems_g = systems.lock().unwrap(); let systems_g = systems.lock().unwrap();
let path = path.iter().map(|t| deintern(*t)).collect_vec(); let path = path.iter().map(|t| deintern(*t)).collect_vec();
req.handle(vfs_read, &systems_g[sys_id].vfses[vfs_id].load(PathSlice::new(&path))) hand.handle(&vfs_read, &systems_g[sys_id].vfses[vfs_id].load(PathSlice::new(&path)))
} }
api::HostExtReq::ParserReq(api::ParserReq::LexExpr(lex)) => { api::HostExtReq::LexExpr(lex @ api::LexExpr{ sys, text, pos, id }) => {
let api::LexExpr{ sys, text, pos, id } = *lex;
let systems_g = systems.lock().unwrap(); let systems_g = systems.lock().unwrap();
let lexers = systems_g[&sys].cted.inst().dyn_lexers(); let lexers = systems_g[&sys].cted.inst().dyn_lexers();
mem::drop(systems_g); mem::drop(systems_g);
let text = deintern(text); let text = deintern(text);
let ctx = LexContext { sys, id, pos, reqnot: req.reqnot(), text: &text }; let ctx = LexContext { sys, id, pos, reqnot: hand.reqnot(), text: &text };
let trigger_char = text.chars().nth(pos as usize).unwrap(); let trigger_char = text.chars().nth(pos as usize).unwrap();
for lx in lexers.iter().filter(|l| char_filter_match(l.char_filter(), trigger_char)) { for lx in lexers.iter().filter(|l| char_filter_match(l.char_filter(), trigger_char)) {
match lx.lex(&text[pos as usize..], &ctx) { match lx.lex(&text[pos as usize..], &ctx) {
Err(e) if e.iter().any(|e| *e == err_lexer_na()) => continue, Err(e) if e.any(|e| *e == err_not_applicable()) => continue,
Err(e) => { Err(e) => {
let errv = errv_to_apiv(e.iter().filter(|e| **e == err_cascade())); let eopt = e.keep_only(|e| *e != err_cascade()).map(|e| Err(e.to_api()));
return req.handle(lex, &if errv.is_empty() { None } else { Some(Err(errv))}) return hand.handle(&lex, &eopt)
}, },
Ok((s, expr)) => { Ok((s, expr)) => {
let ctx = mk_ctx(sys, req.reqnot()); let ctx = mk_ctx(sys, hand.reqnot());
let expr = expr.to_api(&mut |f, r| do_extra(f, r, ctx.clone())); let expr = expr.to_api(&mut |f, r| do_extra(f, r, ctx.clone()));
let pos = (text.len() - s.len()) as u32; let pos = (text.len() - s.len()) as u32;
return req.handle(lex, &Some(Ok(api::LexedExpr{ pos, expr }))) return hand.handle(&lex, &Some(Ok(api::LexedExpr{ pos, expr })))
} }
} }
} }
writeln!(logger, "Got notified about n/a character '{trigger_char}'"); writeln!(logger, "Got notified about n/a character '{trigger_char}'");
req.handle(lex, &None) hand.handle(&lex, &None)
}, },
api::HostExtReq::ParserReq(api::ParserReq::ParseLine(pline@api::ParseLine{ sys, line })) => { api::HostExtReq::ParseLine(pline) => {
let mut ctx = mk_ctx(*sys, req.reqnot()); let api::ParseLine{ exported, comments, sys, line } = &pline;
let mut ctx = mk_ctx(*sys, hand.reqnot());
let parsers = ctx.cted.inst().dyn_parsers(); let parsers = ctx.cted.inst().dyn_parsers();
let comments = comments.iter().map(Comment::from_api).collect();
let line: Vec<GenTokTree> = ttv_from_api(line, &mut ctx); let line: Vec<GenTokTree> = ttv_from_api(line, &mut ctx);
let snip = Snippet::new(line.first().expect("Empty line"), &line); let snip = Snippet::new(line.first().expect("Empty line"), &line);
let (head, tail) = snip.pop_front().unwrap(); let (head, tail) = snip.pop_front().unwrap();
let name = if let GenTok::Name(n) = &head.tok { n } else { panic!("No line head") }; 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 parser = parsers.iter().find(|p| p.line_head() == **name).expect("No parser candidate");
let o_line = match parser.parse(tail) { let o_line = match parser.parse(*exported, comments, tail) {
Err(e) => Err(errv_to_apiv(e.iter())), Err(e) => Err(e.to_api()),
Ok(t) => Ok(ttv_to_api(t, &mut |f, range| { Ok(t) => Ok(ttv_to_api(t, &mut |f, range| {
api::TokenTree{ range, token: api::Token::Atom(f.clone().build(ctx.clone())) } api::TokenTree{ range, token: api::Token::Atom(f.clone().build(ctx.clone())) }
})), })),
}; };
req.handle(pline, &o_line) hand.handle(&pline, &o_line)
} }
api::HostExtReq::AtomReq(atom_req) => { api::HostExtReq::AtomReq(atom_req) => {
let atom = atom_req.get_atom(); let atom = atom_req.get_atom();
with_atom_record(&mk_ctx, req.reqnot(), atom, |nfo, ctx, id, buf| { with_atom_record(&mk_ctx, hand.reqnot(), atom, |nfo, ctx, id, buf| {
let actx = AtomCtx(buf, atom.drop, ctx.clone()); let actx = AtomCtx(buf, atom.drop, ctx.clone());
match atom_req { match &atom_req {
api::AtomReq::SerializeAtom(ser) => { api::AtomReq::SerializeAtom(ser) => {
let mut buf = enc_vec(&id); let mut buf = enc_vec(&id);
let refs = nfo.serialize(actx, &mut buf); let refs = nfo.serialize(actx, &mut buf);
req.handle(ser, &(buf, refs)) hand.handle(ser, &(buf, refs))
} }
api::AtomReq::AtomPrint(print@api::AtomPrint(_)) => req.handle(print, &nfo.print(actx)), api::AtomReq::AtomPrint(print@api::AtomPrint(_)) =>
api::AtomReq::AtomSame(same@api::AtomSame(_, r)) => { hand.handle(print, &nfo.print(actx)),
// different systems or different type tags api::AtomReq::Fwded(fwded) => {
if atom.owner != r.owner || buf != &r.data[..8] { let api::Fwded(_, key, payload) = &fwded;
return req.handle(same, &false)
}
req.handle(same, &nfo.same(actx, r))
},
api::AtomReq::Fwded(fwded@api::Fwded(_, payload)) => {
let mut reply = Vec::new(); let mut reply = Vec::new();
nfo.handle_req(actx, &mut &payload[..], &mut reply); let some = nfo.handle_req(actx, Sym::deintern(*key), &mut &payload[..], &mut reply);
req.handle(fwded, &reply) hand.handle(fwded, &some.then_some(reply))
} }
api::AtomReq::CallRef(call@api::CallRef(_, arg)) api::AtomReq::CallRef(call@api::CallRef(_, arg)) => {
=> req.handle(call, &nfo.call_ref(actx, *arg).to_api(ctx.clone())), let ret = nfo.call_ref(actx, *arg);
api::AtomReq::FinalCall(call@api::FinalCall(_, arg)) hand.handle(call, &ret.api_return(ctx.clone(), &mut |h| hand.defer_drop(h)))
=> req.handle(call, &nfo.call(actx, *arg).to_api(ctx.clone())), },
api::AtomReq::Command(cmd@api::Command(_)) => req.handle(cmd, &match nfo.command(actx) { api::AtomReq::FinalCall(call@api::FinalCall(_, arg)) => {
Err(e) => Err(errv_to_apiv(e.iter())), let ret = nfo.call(actx, *arg);
hand.handle(call, &ret.api_return(ctx.clone(), &mut |h| hand.defer_drop(h)))
}
api::AtomReq::Command(cmd@api::Command(_)) => {
hand.handle(cmd, &match nfo.command(actx) {
Err(e) => Err(e.to_api()),
Ok(opt) => Ok(match opt { Ok(opt) => Ok(match opt {
Some(cont) => api::NextStep::Continue(cont.into_api(ctx.clone())),
None => api::NextStep::Halt, None => api::NextStep::Halt,
Some(cont) => api::NextStep::Continue(
cont.api_return(ctx.clone(), &mut |h| hand.defer_drop(h))
),
}) })
}) })
} }
}
}) })
}, },
api::HostExtReq::DeserAtom(deser@DeserAtom(sys, buf, refs)) => { api::HostExtReq::DeserAtom(deser) => {
let api::DeserAtom(sys, buf, refs) = &deser;
let mut read = &mut &buf[..]; let mut read = &mut &buf[..];
let ctx = mk_ctx(*sys, req.reqnot()); let ctx = mk_ctx(*sys, hand.reqnot());
let id = api::AtomId::decode(&mut read); let id = api::AtomId::decode(&mut read);
let inst = ctx.cted.inst(); let inst = ctx.cted.inst();
let nfo = atom_by_idx(inst.card(), id).expect("Deserializing atom with invalid ID"); let nfo = atom_by_idx(inst.card(), id).expect("Deserializing atom with invalid ID");
req.handle(deser, &nfo.deserialize(ctx.clone(), read, refs)) hand.handle(&deser, &nfo.deserialize(ctx.clone(), read, refs))
},
orchid_api::HostExtReq::ApplyMacro(am) => {
let tok = hand.will_handle_as(&am);
let sys_ctx = mk_ctx(am.sys, hand.reqnot());
let ctx = RuleCtx {
args: am.params.into_iter().map(|(k, v)| (deintern(k), mtreev_from_api(&v))).collect(),
run_id: am.run_id,
sys: sys_ctx.clone(),
};
hand.handle_as(tok, &match apply_rule(am.id, ctx) {
Err(e) => e.keep_only(|e| *e != err_cascade()).map(|e| Err(e.to_api())),
Ok(t) => Some(Ok(mtreev_to_api(&t))),
})
} }
}), }),
); );

View File

@@ -1,10 +1,11 @@
use std::marker::PhantomData; use std::fmt;
use std::ops::Deref; use std::ops::Deref;
use std::sync::OnceLock; use std::sync::{Arc, OnceLock};
use derive_destructure::destructure; use derive_destructure::destructure;
use orchid_base::error::{errv_from_apiv, errv_to_apiv, OrcErr}; use orchid_api::InspectedKind;
use orchid_base::interner::{deintern, Tok}; use orchid_base::error::{OrcErr, OrcErrv};
use orchid_base::interner::Tok;
use orchid_base::location::Pos; use orchid_base::location::Pos;
use orchid_base::reqnot::Requester; use orchid_base::reqnot::Requester;
@@ -19,12 +20,13 @@ pub struct ExprHandle {
} }
impl ExprHandle { impl ExprHandle {
pub(crate) fn from_args(ctx: SysCtx, tk: api::ExprTicket) -> Self { Self { ctx, tk } } pub(crate) fn from_args(ctx: SysCtx, tk: api::ExprTicket) -> Self { Self { ctx, tk } }
pub(crate) fn into_tk(self) -> api::ExprTicket {
let (tk, ..) = self.destructure();
tk
}
pub fn get_ctx(&self) -> SysCtx { self.ctx.clone() } pub fn get_ctx(&self) -> SysCtx { self.ctx.clone() }
} }
impl fmt::Debug for ExprHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ExprHandle({})", self.tk.0)
}
}
impl Clone for ExprHandle { impl Clone for ExprHandle {
fn clone(&self) -> Self { fn clone(&self) -> Self {
self.ctx.reqnot.notify(api::Acquire(self.ctx.id, self.tk)); self.ctx.reqnot.notify(api::Acquire(self.ctx.id, self.tk));
@@ -35,144 +37,128 @@ impl Drop for ExprHandle {
fn drop(&mut self) { self.ctx.reqnot.notify(api::Release(self.ctx.id, self.tk)) } fn drop(&mut self) { self.ctx.reqnot.notify(api::Release(self.ctx.id, self.tk)) }
} }
#[derive(Clone, destructure)] #[derive(Clone, Debug, destructure)]
pub struct OwnedExpr { pub struct Expr {
pub handle: ExprHandle, pub handle: Option<Arc<ExprHandle>>,
pub val: OnceLock<Box<GenExpr>>, pub val: OnceLock<ExprData>,
} }
impl OwnedExpr { impl Expr {
pub fn new(handle: ExprHandle) -> Self { Self { handle, val: OnceLock::new() } } pub fn new(hand: Arc<ExprHandle>) -> Self { Self { handle: Some(hand), val: OnceLock::new() } }
pub fn get_data(&self) -> &GenExpr { pub fn from_data(val: ExprData) -> Self { Self { handle: None, val: OnceLock::from(val) } }
pub fn get_data(&self) -> &ExprData {
self.val.get_or_init(|| { self.val.get_or_init(|| {
Box::new(GenExpr::from_api( let handle = self.handle.as_ref().expect("Either the value or the handle must be set");
self.handle.ctx.reqnot.request(api::Inspect(self.handle.tk)).expr, let details = handle.ctx.reqnot.request(api::Inspect { target: handle.tk });
&self.handle.ctx, let pos = Pos::from_api(&details.location);
)) let kind = match details.kind {
InspectedKind::Atom(a) => ExprKind::Atom(ForeignAtom::new(handle.clone(), a, pos.clone())),
InspectedKind::Bottom(b) => ExprKind::Bottom(OrcErrv::from_api(&b)),
InspectedKind::Opaque => ExprKind::Opaque,
};
ExprData { pos, kind }
}) })
} }
pub fn foreign_atom(self) -> Result<ForeignAtom<'static>, Self> { pub fn foreign_atom(self) -> Result<ForeignAtom<'static>, Self> {
if let GenExpr { clause: GenClause::Atom(_, atom), pos: position } = self.get_data() { match (self.get_data(), &self.handle) {
let (atom, position) = (atom.clone(), position.clone()); (ExprData { kind: ExprKind::Atom(atom), .. }, Some(_)) => Ok(atom.clone()),
return Ok(ForeignAtom { _ => Err(self),
ctx: self.handle.ctx.clone(),
expr: Some(self.handle),
char_marker: PhantomData,
pos: position,
atom,
});
}
Err(self)
} }
} }
impl Deref for OwnedExpr { pub fn api_return(
type Target = GenExpr; self,
ctx: SysCtx,
do_slot: &mut impl FnMut(Arc<ExprHandle>),
) -> api::Expression {
if let Some(h) = self.handle {
do_slot(h.clone());
api::Expression { location: api::Location::SlotTarget, kind: api::ExpressionKind::Slot(h.tk) }
} else {
self.val.into_inner().expect("Either value or handle must be set").api_return(ctx, do_slot)
}
}
pub fn handle(&self) -> Option<Arc<ExprHandle>> { self.handle.clone() }
}
impl Deref for Expr {
type Target = ExprData;
fn deref(&self) -> &Self::Target { self.get_data() } fn deref(&self) -> &Self::Target { self.get_data() }
} }
#[derive(Clone)] #[derive(Clone, Debug)]
pub struct GenExpr { pub struct ExprData {
pub pos: Pos, pub pos: Pos,
pub clause: GenClause, pub kind: ExprKind,
} }
impl GenExpr { impl ExprData {
pub fn to_api(&self, ctx: SysCtx) -> api::Expr { pub fn api_return(
api::Expr { location: self.pos.to_api(), clause: self.clause.to_api(ctx) } self,
} ctx: SysCtx,
pub fn into_api(self, ctx: SysCtx) -> api::Expr { do_slot: &mut impl FnMut(Arc<ExprHandle>),
api::Expr { location: self.pos.to_api(), clause: self.clause.into_api(ctx) } ) -> api::Expression {
} api::Expression { location: self.pos.to_api(), kind: self.kind.api_return(ctx, do_slot) }
pub fn from_api(api: api::Expr, ctx: &SysCtx) -> Self {
Self { pos: Pos::from_api(&api.location), clause: GenClause::from_api(api.clause, ctx) }
} }
} }
#[derive(Clone)] #[derive(Clone, Debug)]
pub enum GenClause { pub enum ExprKind {
Call(Box<GenExpr>, Box<GenExpr>), Call(Box<Expr>, Box<Expr>),
Lambda(u64, Box<GenExpr>), Lambda(u64, Box<Expr>),
Arg(u64), Arg(u64),
Slot(OwnedExpr), Seq(Box<Expr>, Box<Expr>),
Seq(Box<GenExpr>, Box<GenExpr>),
Const(Tok<Vec<Tok<String>>>), Const(Tok<Vec<Tok<String>>>),
NewAtom(AtomFactory), NewAtom(AtomFactory),
Atom(api::ExprTicket, api::Atom), Atom(ForeignAtom<'static>),
Bottom(Vec<OrcErr>), Bottom(OrcErrv),
Opaque,
} }
impl GenClause { impl ExprKind {
pub fn to_api(&self, ctx: SysCtx) -> api::Clause { pub fn api_return(
self,
ctx: SysCtx,
do_slot: &mut impl FnMut(Arc<ExprHandle>),
) -> api::ExpressionKind {
use api::ExpressionKind as K;
match self { match self {
Self::Call(f, x) => Self::Call(f, x) =>
api::Clause::Call(Box::new(f.to_api(ctx.clone())), Box::new(x.to_api(ctx))), K::Call(Box::new(f.api_return(ctx.clone(), do_slot)), Box::new(x.api_return(ctx, do_slot))),
Self::Seq(a, b) => api::Clause::Seq(Box::new(a.to_api(ctx.clone())), Box::new(b.to_api(ctx))),
Self::Lambda(arg, body) => api::Clause::Lambda(*arg, Box::new(body.to_api(ctx))),
Self::Arg(arg) => api::Clause::Arg(*arg),
Self::Const(name) => api::Clause::Const(name.marker()),
Self::Bottom(err) => api::Clause::Bottom(errv_to_apiv(err)),
Self::NewAtom(fac) => api::Clause::NewAtom(fac.clone().build(ctx)),
Self::Atom(tk, atom) => api::Clause::Atom(*tk, atom.clone()),
Self::Slot(_) => panic!("Slot is forbidden in const tree"),
}
}
pub fn into_api(self, ctx: SysCtx) -> api::Clause {
match self {
Self::Call(f, x) =>
api::Clause::Call(Box::new(f.into_api(ctx.clone())), Box::new(x.into_api(ctx))),
Self::Seq(a, b) => Self::Seq(a, b) =>
api::Clause::Seq(Box::new(a.into_api(ctx.clone())), Box::new(b.into_api(ctx))), K::Seq(Box::new(a.api_return(ctx.clone(), do_slot)), Box::new(b.api_return(ctx, do_slot))),
Self::Lambda(arg, body) => api::Clause::Lambda(arg, Box::new(body.into_api(ctx))), Self::Lambda(arg, body) => K::Lambda(arg, Box::new(body.api_return(ctx, do_slot))),
Self::Arg(arg) => api::Clause::Arg(arg), Self::Arg(arg) => K::Arg(arg),
Self::Slot(extk) => api::Clause::Slot(extk.handle.into_tk()), Self::Const(name) => K::Const(name.marker()),
Self::Const(name) => api::Clause::Const(name.marker()), Self::Bottom(err) => K::Bottom(err.to_api()),
Self::Bottom(err) => api::Clause::Bottom(errv_to_apiv(err.iter())), Self::NewAtom(fac) => K::NewAtom(fac.clone().build(ctx)),
Self::NewAtom(fac) => api::Clause::NewAtom(fac.clone().build(ctx)), kind @ (Self::Atom(_) | Self::Opaque) => panic!("{kind:?} should have a token"),
Self::Atom(tk, atom) => api::Clause::Atom(tk, atom),
}
}
pub fn from_api(api: api::Clause, ctx: &SysCtx) -> Self {
match api {
api::Clause::Arg(id) => Self::Arg(id),
api::Clause::Lambda(arg, body) => Self::Lambda(arg, Box::new(GenExpr::from_api(*body, ctx))),
api::Clause::NewAtom(_) => panic!("Clause::NewAtom should never be received, only sent"),
api::Clause::Bottom(s) => Self::Bottom(errv_from_apiv(&s)),
api::Clause::Call(f, x) =>
Self::Call(Box::new(GenExpr::from_api(*f, ctx)), Box::new(GenExpr::from_api(*x, ctx))),
api::Clause::Seq(a, b) =>
Self::Seq(Box::new(GenExpr::from_api(*a, ctx)), Box::new(GenExpr::from_api(*b, ctx))),
api::Clause::Const(name) => Self::Const(deintern(name)),
api::Clause::Slot(exi) => Self::Slot(OwnedExpr::new(ExprHandle::from_args(ctx.clone(), exi))),
api::Clause::Atom(tk, atom) => Self::Atom(tk, atom),
} }
} }
} }
fn inherit(clause: GenClause) -> GenExpr { GenExpr { pos: Pos::Inherit, clause } } fn inherit(kind: ExprKind) -> Expr { Expr::from_data(ExprData { pos: Pos::Inherit, kind }) }
pub fn sym_ref(path: Tok<Vec<Tok<String>>>) -> GenExpr { inherit(GenClause::Const(path)) } pub fn sym_ref(path: Tok<Vec<Tok<String>>>) -> Expr { inherit(ExprKind::Const(path)) }
pub fn atom<A: ToAtom>(atom: A) -> GenExpr { inherit(GenClause::NewAtom(atom.to_atom_factory())) } pub fn atom<A: ToAtom>(atom: A) -> Expr { inherit(ExprKind::NewAtom(atom.to_atom_factory())) }
pub fn seq(ops: impl IntoIterator<Item = GenExpr>) -> GenExpr { pub fn seq(ops: impl IntoIterator<Item = Expr>) -> Expr {
fn recur(mut ops: impl Iterator<Item = GenExpr>) -> Option<GenExpr> { fn recur(mut ops: impl Iterator<Item = Expr>) -> Option<Expr> {
let op = ops.next()?; let op = ops.next()?;
Some(match recur(ops) { Some(match recur(ops) {
None => op, None => op,
Some(rec) => inherit(GenClause::Seq(Box::new(op), Box::new(rec))), Some(rec) => inherit(ExprKind::Seq(Box::new(op), Box::new(rec))),
}) })
} }
recur(ops.into_iter()).expect("Empty list provided to seq!") recur(ops.into_iter()).expect("Empty list provided to seq!")
} }
pub fn slot(extk: OwnedExpr) -> GenClause { GenClause::Slot(extk) } pub fn arg(n: u64) -> ExprKind { ExprKind::Arg(n) }
pub fn arg(n: u64) -> GenClause { GenClause::Arg(n) } pub fn lambda(n: u64, b: impl IntoIterator<Item = Expr>) -> Expr {
inherit(ExprKind::Lambda(n, Box::new(call(b))))
pub fn lambda(n: u64, b: impl IntoIterator<Item = GenExpr>) -> GenExpr {
inherit(GenClause::Lambda(n, Box::new(call(b))))
} }
pub fn call(v: impl IntoIterator<Item = GenExpr>) -> GenExpr { pub fn call(v: impl IntoIterator<Item = Expr>) -> Expr {
v.into_iter() v.into_iter()
.reduce(|f, x| inherit(GenClause::Call(Box::new(f), Box::new(x)))) .reduce(|f, x| inherit(ExprKind::Call(Box::new(f), Box::new(x))))
.expect("Empty call expression") .expect("Empty call expression")
} }
pub fn bot(e: OrcErr) -> GenExpr { botv(vec![e]) } pub fn bot(ev: impl IntoIterator<Item = OrcErr>) -> Expr {
pub fn botv(ev: Vec<OrcErr>) -> GenExpr { inherit(GenClause::Bottom(ev)) } inherit(ExprKind::Bottom(OrcErrv::new(ev).unwrap()))
}

View File

@@ -5,26 +5,25 @@ use std::sync::{Arc, Mutex};
use itertools::Itertools; use itertools::Itertools;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use never::Never;
use orchid_api_traits::Encode; use orchid_api_traits::Encode;
use orchid_base::error::OrcRes; use orchid_base::error::OrcRes;
use orchid_base::interner::Tok; use orchid_base::interner::Tok;
use orchid_base::name::Sym; use orchid_base::name::Sym;
use trait_set::trait_set; use trait_set::trait_set;
use crate::atom::{Atomic, ReqPck}; use crate::atom::{MethodSet, Atomic};
use crate::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant}; use crate::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
use crate::conv::ToExpr; use crate::conv::ToExpr;
use crate::expr::{ExprHandle, GenExpr}; use crate::expr::{Expr, ExprHandle};
use crate::system::SysCtx; use crate::system::SysCtx;
trait_set! { trait_set! {
trait FunCB = Fn(Vec<ExprHandle>) -> OrcRes<GenExpr> + Send + Sync + 'static; trait FunCB = Fn(Vec<Expr>) -> OrcRes<Expr> + Send + Sync + 'static;
} }
pub trait ExprFunc<I, O>: Clone + Send + Sync + 'static { pub trait ExprFunc<I, O>: Clone + Send + Sync + 'static {
const ARITY: u8; const ARITY: u8;
fn apply(&self, v: Vec<ExprHandle>) -> OrcRes<GenExpr>; fn apply(&self, v: Vec<Expr>) -> OrcRes<Expr>;
} }
lazy_static! { lazy_static! {
@@ -34,7 +33,7 @@ lazy_static! {
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct Fun { pub(crate) struct Fun {
path: Sym, path: Sym,
args: Vec<ExprHandle>, args: Vec<Expr>,
arity: u8, arity: u8,
fun: Arc<dyn FunCB>, fun: Arc<dyn FunCB>,
} }
@@ -53,14 +52,14 @@ impl Fun {
} }
impl Atomic for Fun { impl Atomic for Fun {
type Data = (); type Data = ();
type Req = Never;
type Variant = OwnedVariant; type Variant = OwnedVariant;
fn reg_reqs() -> MethodSet<Self> { MethodSet::new() }
} }
impl OwnedAtom for Fun { impl OwnedAtom for Fun {
type Refs = Vec<ExprHandle>; type Refs = Vec<Expr>;
fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) } fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
fn call_ref(&self, arg: ExprHandle) -> GenExpr { fn call_ref(&self, arg: ExprHandle) -> Expr {
let new_args = self.args.iter().cloned().chain([arg]).collect_vec(); let new_args = self.args.iter().cloned().chain([Expr::new(Arc::new(arg))]).collect_vec();
if new_args.len() == self.arity.into() { if new_args.len() == self.arity.into() {
(self.fun)(new_args).to_expr() (self.fun)(new_args).to_expr()
} else { } else {
@@ -68,8 +67,7 @@ impl OwnedAtom for Fun {
.to_expr() .to_expr()
} }
} }
fn call(self, arg: ExprHandle) -> GenExpr { self.call_ref(arg) } fn call(self, arg: ExprHandle) -> Expr { self.call_ref(arg) }
fn handle_req(&self, pck: impl ReqPck<Self>) { pck.never() }
fn serialize(&self, _: SysCtx, sink: &mut (impl io::Write + ?Sized)) -> Self::Refs { fn serialize(&self, _: SysCtx, sink: &mut (impl io::Write + ?Sized)) -> Self::Refs {
self.path.encode(sink); self.path.encode(sink);
self.args.clone() self.args.clone()
@@ -86,7 +84,7 @@ mod expr_func_derives {
use super::ExprFunc; use super::ExprFunc;
use crate::conv::{ToExpr, TryFromExpr}; use crate::conv::{ToExpr, TryFromExpr};
use crate::func_atom::{ExprHandle, GenExpr}; use crate::func_atom::Expr;
macro_rules! expr_func_derive { macro_rules! expr_func_derive {
($arity: tt, $($t:ident),*) => { ($arity: tt, $($t:ident),*) => {
@@ -97,7 +95,7 @@ mod expr_func_derives {
Func: Fn($($t,)*) -> Out + Clone + Send + Sync + 'static Func: Fn($($t,)*) -> Out + Clone + Send + Sync + 'static
> ExprFunc<($($t,)*), Out> for Func { > ExprFunc<($($t,)*), Out> for Func {
const ARITY: u8 = $arity; const ARITY: u8 = $arity;
fn apply(&self, v: Vec<ExprHandle>) -> OrcRes<GenExpr> { fn apply(&self, v: Vec<Expr>) -> OrcRes<Expr> {
assert_eq!(v.len(), Self::ARITY.into(), "Arity mismatch"); assert_eq!(v.len(), Self::ARITY.into(), "Arity mismatch");
let [$([< $t:lower >],)*] = v.try_into().unwrap_or_else(|_| panic!("Checked above")); let [$([< $t:lower >],)*] = v.try_into().unwrap_or_else(|_| panic!("Checked above"));
Ok(self($($t::try_from_expr([< $t:lower >])?,)*).to_expr()) Ok(self($($t::try_from_expr([< $t:lower >])?,)*).to_expr())

View File

@@ -5,22 +5,22 @@ use orchid_base::intern;
use orchid_base::interner::Tok; use orchid_base::interner::Tok;
use orchid_base::location::Pos; use orchid_base::location::Pos;
use orchid_base::reqnot::{ReqNot, Requester}; use orchid_base::reqnot::{ReqNot, Requester};
use orchid_base::tree::TreeHandle; use orchid_base::tree::TokHandle;
use crate::api; use crate::api;
use crate::tree::{GenTok, GenTokTree}; use crate::tree::{GenTok, GenTokTree};
pub fn err_cascade() -> OrcErr { pub fn err_cascade() -> OrcErr {
mk_err( mk_err(
intern!(str: "An error cascading from a recursive sublexer"), intern!(str: "An error cascading from a recursive call"),
"This error should not surface. If you are seeing it, something is wrong", "This error should not surface. If you are seeing it, something is wrong",
[Pos::None.into()], [Pos::None.into()],
) )
} }
pub fn err_lexer_na() -> OrcErr { pub fn err_not_applicable() -> OrcErr {
mk_err( mk_err(
intern!(str: "Pseudo-error to communicate that the lexer doesn't apply"), intern!(str: "Pseudo-error to communicate that the current branch in a dispatch doesn't apply"),
&*err_cascade().message, &*err_cascade().message,
[Pos::None.into()], [Pos::None.into()],
) )
@@ -38,7 +38,7 @@ impl<'a> LexContext<'a> {
let start = self.pos(tail); let start = self.pos(tail);
let lx = let lx =
self.reqnot.request(api::SubLex { pos: start, id: self.id }).ok_or_else(err_cascade)?; self.reqnot.request(api::SubLex { pos: start, id: self.id }).ok_or_else(err_cascade)?;
Ok((&self.text[lx.pos as usize..], GenTok::Slot(TreeHandle::new(lx.ticket)).at(start..lx.pos))) Ok((&self.text[lx.pos as usize..], GenTok::Slot(TokHandle::new(lx.ticket)).at(start..lx.pos)))
} }
pub fn pos(&self, tail: &'a str) -> u32 { (self.text.len() - tail.len()) as u32 } pub fn pos(&self, tail: &'a str) -> u32 { (self.text.len() - tail.len()) as u32 }

View File

@@ -16,3 +16,5 @@ pub mod parser;
pub mod system; pub mod system;
pub mod system_ctor; pub mod system_ctor;
pub mod tree; pub mod tree;
pub mod macros;
pub mod api_conv;

View File

@@ -0,0 +1,89 @@
use ahash::HashMap;
use lazy_static::lazy_static;
use orchid_base::{error::OrcRes, interner::{intern, Tok}, location::Pos, macros::{mtreev_from_api, mtreev_to_api, MTree}, parse::Comment, reqnot::Requester};
use trait_set::trait_set;
use crate::{api, lexer::err_cascade, system::SysCtx};
use std::{num::NonZero, sync::RwLock};
pub trait Macro {
fn pattern() -> MTree<'static>;
fn apply(binds: HashMap<Tok<String>, MTree<'_>>) -> MTree<'_>;
}
pub trait DynMacro {
fn pattern(&self) -> MTree<'static>;
fn apply<'a>(&self, binds: HashMap<Tok<String>, MTree<'a>>) -> MTree<'a>;
}
impl<T: Macro> DynMacro for T {
fn pattern(&self) -> MTree<'static> { Self::pattern() }
fn apply<'a>(&self, binds: HashMap<Tok<String>, MTree<'a>>) -> MTree<'a> { Self::apply(binds) }
}
pub struct RuleCtx<'a> {
pub(crate) args: HashMap<Tok<String>, Vec<MTree<'a>>>,
pub(crate) run_id: api::ParsId,
pub(crate) sys: SysCtx,
}
impl<'a> RuleCtx<'a> {
pub fn recurse(&mut self, tree: &[MTree<'a>]) -> OrcRes<Vec<MTree<'a>>> {
let req = api::RunMacros{ run_id: self.run_id, query: mtreev_to_api(tree) };
Ok(mtreev_from_api(&self.sys.reqnot.request(req).ok_or_else(err_cascade)?))
}
pub fn getv(&mut self, key: &Tok<String>) -> Vec<MTree<'a>> {
self.args.remove(key).expect("Key not found")
}
pub fn gets(&mut self, key: &Tok<String>) -> MTree<'a> {
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>) -> OrcRes<Vec<MTree<'a>>> + Send + Sync;
}
lazy_static!{
static ref RULES: RwLock<HashMap<api::MacroId, Box<dyn RuleCB>>> = RwLock::default();
}
pub struct Rule {
pub(crate) comments: Vec<Comment>,
pub(crate) pattern: Vec<MTree<'static>>,
pub(crate) id: api::MacroId,
}
impl Rule {
pub(crate) fn to_api(&self) -> api::MacroRule {
api::MacroRule {
comments: self.comments.iter().map(|c| c.to_api()).collect(),
location: api::Location::Inherit,
pattern: mtreev_to_api(&self.pattern),
id: self.id,
}
}
}
pub fn rule_cmt<'a>(
cmt: impl IntoIterator<Item = &'a str>,
pattern: Vec<MTree<'static>>,
apply: impl RuleCB + 'static
) -> Rule {
let mut rules = RULES.write().unwrap();
let id = api::MacroId(NonZero::new(rules.len() as u64 + 1).unwrap());
rules.insert(id, Box::new(apply));
let comments = cmt.into_iter().map(|s| Comment { pos: Pos::Inherit, text: intern(s) }).collect();
Rule { comments, pattern, id }
}
pub fn rule(pattern: Vec<MTree<'static>>, apply: impl RuleCB + 'static) -> Rule {
rule_cmt([], pattern, apply)
}
pub(crate) fn apply_rule(id: api::MacroId, ctx: RuleCtx<'static>) -> OrcRes<Vec<MTree<'static>>> {
let rules = RULES.read().unwrap();
rules[&id](ctx)
}

View File

@@ -1,5 +1,5 @@
use orchid_base::error::OrcRes; use orchid_base::error::OrcRes;
use orchid_base::parse::Snippet; use orchid_base::parse::{Comment, Snippet};
use crate::atom::{AtomFactory, ForeignAtom}; use crate::atom::{AtomFactory, ForeignAtom};
use crate::tree::GenTokTree; use crate::tree::GenTokTree;
@@ -8,17 +8,33 @@ pub type GenSnippet<'a> = Snippet<'a, 'a, ForeignAtom<'a>, AtomFactory>;
pub trait Parser: Send + Sync + Sized + Default + 'static { pub trait Parser: Send + Sync + Sized + Default + 'static {
const LINE_HEAD: &'static str; const LINE_HEAD: &'static str;
fn parse(line: GenSnippet<'_>) -> OrcRes<Vec<GenTokTree<'_>>>; fn parse(
exported: bool,
comments: Vec<Comment>,
line: GenSnippet<'_>,
) -> OrcRes<Vec<GenTokTree<'_>>>;
} }
pub trait DynParser: Send + Sync + 'static { pub trait DynParser: Send + Sync + 'static {
fn line_head(&self) -> &'static str; fn line_head(&self) -> &'static str;
fn parse<'a>(&self, line: GenSnippet<'a>) -> OrcRes<Vec<GenTokTree<'a>>>; fn parse<'a>(
&self,
exported: bool,
comments: Vec<Comment>,
line: GenSnippet<'a>,
) -> OrcRes<Vec<GenTokTree<'a>>>;
} }
impl<T: Parser> DynParser for T { impl<T: Parser> DynParser for T {
fn line_head(&self) -> &'static str { Self::LINE_HEAD } fn line_head(&self) -> &'static str { Self::LINE_HEAD }
fn parse<'a>(&self, line: GenSnippet<'a>) -> OrcRes<Vec<GenTokTree<'a>>> { Self::parse(line) } fn parse<'a>(
&self,
exported: bool,
comments: Vec<Comment>,
line: GenSnippet<'a>,
) -> OrcRes<Vec<GenTokTree<'a>>> {
Self::parse(exported, comments, line)
}
} }
pub type ParserObj = &'static dyn DynParser; pub type ParserObj = &'static dyn DynParser;

View File

@@ -3,96 +3,103 @@ use std::num::NonZero;
use std::sync::Arc; use std::sync::Arc;
use hashbrown::HashMap; use hashbrown::HashMap;
use orchid_api::AtomId; use orchid_api_traits::{Coding, Decode};
use orchid_api_traits::Decode; use orchid_base::boxed_iter::BoxedIter;
use orchid_base::interner::Tok; use orchid_base::interner::Tok;
use orchid_base::logging::Logger; use orchid_base::logging::Logger;
use orchid_base::reqnot::ReqNot; use orchid_base::reqnot::{Receipt, ReqNot};
use crate::api; use crate::api;
use crate::atom::{get_info, AtomCtx, AtomDynfo, AtomicFeatures, ForeignAtom, TypAtom}; use crate::atom::{get_info, AtomCtx, AtomDynfo, AtomicFeatures, ForeignAtom, TypAtom};
use crate::entrypoint::ExtReq;
use crate::fs::DeclFs; use crate::fs::DeclFs;
// use crate::fun::Fun; // use crate::fun::Fun;
use crate::lexer::LexerObj; use crate::lexer::LexerObj;
use crate::parser::ParserObj; use crate::parser::ParserObj;
use crate::system_ctor::{CtedObj, SystemCtor}; use crate::system_ctor::{CtedObj, SystemCtor};
use crate::tree::GenMemberKind; use crate::tree::MemKind;
/// System as consumed by foreign code /// System as consumed by foreign code
pub trait SystemCard: Default + Send + Sync + 'static { pub trait SystemCard: Default + Send + Sync + 'static {
type Ctor: SystemCtor; type Ctor: SystemCtor;
const ATOM_DEFS: &'static [Option<&'static dyn AtomDynfo>]; type Req: Coding;
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn AtomDynfo>>>;
} }
pub trait DynSystemCard: Send + Sync + 'static { pub trait DynSystemCard: Send + Sync + 'static {
fn name(&self) -> &'static str; fn name(&self) -> &'static str;
/// Atoms explicitly defined by the system card. Do not rely on this for /// Atoms explicitly defined by the system card. Do not rely on this for
/// querying atoms as it doesn't include the general atom types /// querying atoms as it doesn't include the general atom types
fn atoms(&self) -> &'static [Option<&'static dyn AtomDynfo>]; fn atoms(&self) -> BoxedIter<Option<Box<dyn AtomDynfo>>>;
} }
/// Atoms supported by this package which may appear in all extensions. /// Atoms supported by this package which may appear in all extensions.
/// The indices of these are bitwise negated, such that the MSB of an atom index /// The indices of these are bitwise negated, such that the MSB of an atom index
/// marks whether it belongs to this package (0) or the importer (1) /// marks whether it belongs to this package (0) or the importer (1)
fn general_atoms() -> &'static [Option<&'static dyn AtomDynfo>] { fn general_atoms() -> impl Iterator<Item = Option<Box<dyn AtomDynfo>>> {
&[/*Some(Fun::INFO)*/] [/*Some(Fun::INFO)*/].into_iter()
} }
pub fn atom_info_for( pub fn atom_info_for(
sys: &(impl DynSystemCard + ?Sized), sys: &(impl DynSystemCard + ?Sized),
tid: TypeId, tid: TypeId,
) -> Option<(api::AtomId, &'static dyn AtomDynfo)> { ) -> Option<(api::AtomId, Box<dyn AtomDynfo>)> {
(sys.atoms().iter().enumerate().map(|(i, o)| (NonZero::new(i as u64 + 1).unwrap(), o))) (sys.atoms().enumerate().map(|(i, o)| (NonZero::new(i as u64 + 1).unwrap(), o)))
.chain(general_atoms().iter().enumerate().map(|(i, o)| (NonZero::new(!(i as u64)).unwrap(), o))) .chain(general_atoms().enumerate().map(|(i, o)| (NonZero::new(!(i as u64)).unwrap(), o)))
.filter_map(|(i, o)| o.as_ref().map(|a| (api::AtomId(i), *a))) .filter_map(|(i, o)| o.map(|a| (api::AtomId(i), a)))
.find(|ent| ent.1.tid() == tid) .find(|ent| ent.1.tid() == tid)
} }
pub fn atom_by_idx( pub fn atom_by_idx(
sys: &(impl DynSystemCard + ?Sized), sys: &(impl DynSystemCard + ?Sized),
tid: api::AtomId, tid: api::AtomId,
) -> Option<&'static dyn AtomDynfo> { ) -> Option<Box<dyn AtomDynfo>> {
if (u64::from(tid.0) >> (u64::BITS - 1)) & 1 == 1 { if (u64::from(tid.0) >> (u64::BITS - 1)) & 1 == 1 {
general_atoms()[!u64::from(tid.0) as usize] general_atoms().nth(!u64::from(tid.0) as usize).unwrap()
} else { } else {
sys.atoms()[u64::from(tid.0) as usize - 1] sys.atoms().nth(u64::from(tid.0) as usize - 1).unwrap()
} }
} }
pub fn resolv_atom( pub fn resolv_atom(
sys: &(impl DynSystemCard + ?Sized), sys: &(impl DynSystemCard + ?Sized),
atom: &api::Atom, atom: &api::Atom,
) -> &'static dyn AtomDynfo { ) -> Box<dyn AtomDynfo> {
let tid = api::AtomId::decode(&mut &atom.data[..8]); let tid = api::AtomId::decode(&mut &atom.data[..8]);
atom_by_idx(sys, tid).expect("Value of nonexistent type found") atom_by_idx(sys, tid).expect("Value of nonexistent type found")
} }
impl<T: SystemCard> DynSystemCard for T { impl<T: SystemCard> DynSystemCard for T {
fn name(&self) -> &'static str { T::Ctor::NAME } fn name(&self) -> &'static str { T::Ctor::NAME }
fn atoms(&self) -> &'static [Option<&'static dyn AtomDynfo>] { Self::ATOM_DEFS } fn atoms(&self) -> BoxedIter<Option<Box<dyn AtomDynfo>>> { Box::new(Self::atoms().into_iter()) }
} }
/// System as defined by author /// System as defined by author
pub trait System: Send + Sync + SystemCard + 'static { pub trait System: Send + Sync + SystemCard + 'static {
fn env() -> Vec<(Tok<String>, GenMemberKind)>; fn env() -> Vec<(Tok<String>, MemKind)>;
fn vfs() -> DeclFs; fn vfs() -> DeclFs;
fn lexers() -> Vec<LexerObj>; fn lexers() -> Vec<LexerObj>;
fn parsers() -> Vec<ParserObj>; fn parsers() -> Vec<ParserObj>;
fn request(hand: ExtReq, req: Self::Req) -> Receipt;
} }
pub trait DynSystem: Send + Sync + DynSystemCard + 'static { pub trait DynSystem: Send + Sync + DynSystemCard + 'static {
fn dyn_env(&self) -> HashMap<Tok<String>, GenMemberKind>; fn dyn_env(&self) -> HashMap<Tok<String>, MemKind>;
fn dyn_vfs(&self) -> DeclFs; fn dyn_vfs(&self) -> DeclFs;
fn dyn_lexers(&self) -> Vec<LexerObj>; fn dyn_lexers(&self) -> Vec<LexerObj>;
fn dyn_parsers(&self) -> Vec<ParserObj>; fn dyn_parsers(&self) -> Vec<ParserObj>;
fn dyn_request(&self, hand: ExtReq, req: Vec<u8>) -> Receipt;
fn card(&self) -> &dyn DynSystemCard; fn card(&self) -> &dyn DynSystemCard;
} }
impl<T: System> DynSystem for T { impl<T: System> DynSystem for T {
fn dyn_env(&self) -> HashMap<Tok<String>, GenMemberKind> { Self::env().into_iter().collect() } fn dyn_env(&self) -> HashMap<Tok<String>, MemKind> { Self::env().into_iter().collect() }
fn dyn_vfs(&self) -> DeclFs { Self::vfs() } fn dyn_vfs(&self) -> DeclFs { Self::vfs() }
fn dyn_lexers(&self) -> Vec<LexerObj> { Self::lexers() } fn dyn_lexers(&self) -> Vec<LexerObj> { Self::lexers() }
fn dyn_parsers(&self) -> Vec<ParserObj> { Self::parsers() } fn dyn_parsers(&self) -> Vec<ParserObj> { Self::parsers() }
fn dyn_request(&self, hand: ExtReq, req: Vec<u8>) -> Receipt {
Self::request(hand, <Self as SystemCard>::Req::decode(&mut &req[..]))
}
fn card(&self) -> &dyn DynSystemCard { self } fn card(&self) -> &dyn DynSystemCard { self }
} }
@@ -101,7 +108,7 @@ pub fn downcast_atom<A: AtomicFeatures>(foreign: ForeignAtom) -> Result<TypAtom<
let ctx = foreign.ctx.clone(); let ctx = foreign.ctx.clone();
let info_ent = (ctx.cted.deps().find(|s| s.id() == foreign.atom.owner)) let info_ent = (ctx.cted.deps().find(|s| s.id() == foreign.atom.owner))
.map(|sys| get_info::<A>(sys.get_card())) .map(|sys| get_info::<A>(sys.get_card()))
.filter(|(pos, _)| AtomId::decode(&mut data) == *pos); .filter(|(pos, _)| api::AtomId::decode(&mut data) == *pos);
match info_ent { match info_ent {
None => Err(foreign), None => Err(foreign),
Some((_, info)) => { Some((_, info)) => {

View File

@@ -1,6 +1,5 @@
use std::num::NonZero; use std::num::NonZero;
use std::ops::Range; use std::ops::Range;
use std::sync::Arc;
use dyn_clone::{clone_box, DynClone}; use dyn_clone::{clone_box, DynClone};
use hashbrown::HashMap; use hashbrown::HashMap;
@@ -8,7 +7,9 @@ use itertools::Itertools;
use orchid_base::interner::{intern, Tok}; use orchid_base::interner::{intern, Tok};
use orchid_base::location::Pos; use orchid_base::location::Pos;
use orchid_base::name::Sym; use orchid_base::name::Sym;
use orchid_base::parse::Comment;
use orchid_base::tree::{TokTree, Token}; use orchid_base::tree::{TokTree, Token};
use ordered_float::NotNan;
use substack::Substack; use substack::Substack;
use trait_set::trait_set; use trait_set::trait_set;
@@ -16,8 +17,9 @@ use crate::api;
use crate::atom::{AtomFactory, ForeignAtom}; use crate::atom::{AtomFactory, ForeignAtom};
use crate::conv::ToExpr; use crate::conv::ToExpr;
use crate::entrypoint::MemberRecord; use crate::entrypoint::MemberRecord;
use crate::expr::GenExpr; use crate::expr::Expr;
use crate::func_atom::{ExprFunc, Fun}; use crate::func_atom::{ExprFunc, Fun};
use crate::macros::Rule;
use crate::system::SysCtx; use crate::system::SysCtx;
pub type GenTokTree<'a> = TokTree<'a, ForeignAtom<'a>, AtomFactory>; pub type GenTokTree<'a> = TokTree<'a, ForeignAtom<'a>, AtomFactory>;
@@ -27,68 +29,85 @@ pub fn do_extra(f: &AtomFactory, r: Range<u32>, ctx: SysCtx) -> api::TokenTree {
api::TokenTree { range: r, token: api::Token::Atom(f.clone().build(ctx)) } api::TokenTree { range: r, token: api::Token::Atom(f.clone().build(ctx)) }
} }
fn with_export(mem: GenMember, public: bool) -> Vec<GenItem> {
(public.then(|| GenItemKind::Export(mem.name.clone()).at(Pos::Inherit)).into_iter())
.chain([GenItemKind::Member(mem).at(Pos::Inherit)])
.collect()
}
pub struct GenItem { pub struct GenItem {
pub item: GenItemKind, pub kind: GenItemKind,
pub comments: Vec<(String, Pos)>, pub comments: Vec<Comment>,
pub pos: Pos, pub pos: Pos,
} }
impl GenItem { impl GenItem {
pub fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::Item { pub fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::Item {
let kind = match self.item { let kind = match self.kind {
GenItemKind::Raw(item) => api::ItemKind::Raw(Vec::from_iter( GenItemKind::Export(n) => api::ItemKind::Export(n.marker()),
item.into_iter().map(|t| t.to_api(&mut |f, r| do_extra(f, r, ctx.sys()))),
)),
GenItemKind::Member(mem) => api::ItemKind::Member(mem.into_api(ctx)), GenItemKind::Member(mem) => api::ItemKind::Member(mem.into_api(ctx)),
GenItemKind::Import(cn) => api::ItemKind::Import(cn.tok().marker()),
GenItemKind::Macro(prio, rules) => api::ItemKind::Macro(api::MacroBlock {
priority: prio,
rules: rules.into_iter().map(|r| r.to_api() ).collect_vec(),
})
}; };
let comments = self.comments.into_iter().map(|(s, p)| (Arc::new(s), p.to_api())).collect_vec(); let comments = self.comments.into_iter().map(|c| c.to_api()).collect_vec();
api::Item { location: self.pos.to_api(), comments, kind } api::Item { location: self.pos.to_api(), comments, kind }
} }
} }
pub fn cnst(public: bool, name: &str, value: impl ToExpr) -> GenItem { pub fn cnst(public: bool, name: &str, value: impl ToExpr) -> Vec<GenItem> {
let kind = GenMemberKind::Const(value.to_expr()); with_export(GenMember { name: intern(name), kind: MemKind::Const(value.to_expr()) }, public)
GenItemKind::Member(GenMember { exported: public, name: intern(name), kind }).at(Pos::Inherit)
} }
pub fn module( pub fn module(
public: bool, public: bool,
name: &str, name: &str,
imports: impl IntoIterator<Item = Sym>, imports: impl IntoIterator<Item = Sym>,
items: impl IntoIterator<Item = GenItem>, items: impl IntoIterator<Item = Vec<GenItem>>,
) -> GenItem { ) -> Vec<GenItem> {
let (name, kind) = root_mod(name, imports, items); let (name, kind) = root_mod(name, imports, items);
GenItemKind::Member(GenMember { exported: public, name, kind }).at(Pos::Inherit) with_export(GenMember { name, kind }, public)
} }
pub fn root_mod( pub fn root_mod(
name: &str, name: &str,
imports: impl IntoIterator<Item = Sym>, imports: impl IntoIterator<Item = Sym>,
items: impl IntoIterator<Item = GenItem>, items: impl IntoIterator<Item = Vec<GenItem>>,
) -> (Tok<String>, GenMemberKind) { ) -> (Tok<String>, MemKind) {
let kind = GenMemberKind::Mod { let kind = MemKind::Mod {
imports: imports.into_iter().collect(), imports: imports.into_iter().collect(),
items: items.into_iter().collect(), items: items.into_iter().flatten().collect(),
}; };
(intern(name), kind) (intern(name), kind)
} }
pub fn fun<I, O>(exported: bool, name: &str, xf: impl ExprFunc<I, O>) -> GenItem { pub fn fun<I, O>(exported: bool, name: &str, xf: impl ExprFunc<I, O>) -> Vec<GenItem> {
let fac = LazyMemberFactory::new(move |sym| GenMemberKind::Const(Fun::new(sym, xf).to_expr())); let fac = LazyMemberFactory::new(move |sym| MemKind::Const(Fun::new(sym, xf).to_expr()));
let mem = GenMember { exported, name: intern(name), kind: GenMemberKind::Lazy(fac) }; with_export(GenMember { name: intern(name), kind: MemKind::Lazy(fac) }, exported)
GenItemKind::Member(mem).at(Pos::Inherit) }
pub fn macro_block(prio: Option<f64>, rules: impl IntoIterator<Item = Rule>) -> Vec<GenItem> {
let prio = prio.map(|p| NotNan::new(p).unwrap());
vec![GenItemKind::Macro(prio, rules.into_iter().collect_vec()).gen()]
} }
pub fn comments<'a>(cmts: impl IntoIterator<Item = &'a str>, mut val: GenItem) -> GenItem { pub fn comments<'a>(
val.comments.extend(cmts.into_iter().map(|c| (c.to_string(), Pos::Inherit))); cmts: impl IntoIterator<Item = &'a str> + Clone,
mut val: Vec<GenItem>,
) -> Vec<GenItem> {
for v in val.iter_mut() {
v.comments
.extend(cmts.clone().into_iter().map(|c| Comment { text: intern(c), pos: Pos::Inherit }));
}
val val
} }
trait_set! { trait_set! {
trait LazyMemberCallback = FnOnce(Sym) -> GenMemberKind + Send + Sync + DynClone trait LazyMemberCallback = FnOnce(Sym) -> MemKind + Send + Sync + DynClone
} }
pub struct LazyMemberFactory(Box<dyn LazyMemberCallback>); pub struct LazyMemberFactory(Box<dyn LazyMemberCallback>);
impl LazyMemberFactory { impl LazyMemberFactory {
pub fn new(cb: impl FnOnce(Sym) -> GenMemberKind + Send + Sync + Clone + 'static) -> Self { pub fn new(cb: impl FnOnce(Sym) -> MemKind + Send + Sync + Clone + 'static) -> Self {
Self(Box::new(cb)) Self(Box::new(cb))
} }
pub fn build(self, path: Sym) -> GenMemberKind { (self.0)(path) } pub fn build(self, path: Sym) -> MemKind { (self.0)(path) }
} }
impl Clone for LazyMemberFactory { impl Clone for LazyMemberFactory {
fn clone(&self) -> Self { Self(clone_box(&*self.0)) } fn clone(&self) -> Self { Self(clone_box(&*self.0)) }
@@ -96,42 +115,48 @@ impl Clone for LazyMemberFactory {
pub enum GenItemKind { pub enum GenItemKind {
Member(GenMember), Member(GenMember),
Raw(Vec<GenTokTree<'static>>), Export(Tok<String>),
Import(Sym),
Macro(Option<NotNan<f64>>, Vec<Rule>),
} }
impl GenItemKind { impl GenItemKind {
pub fn at(self, position: Pos) -> GenItem { pub fn at(self, pos: Pos) -> GenItem { GenItem { kind: self, comments: vec![], pos } }
GenItem { item: self, comments: vec![], pos: position } pub fn gen(self) -> GenItem { GenItem { kind: self, comments: vec![], pos: Pos::Inherit } }
pub fn gen_equiv(self, comments: Vec<Comment>) -> GenItem {
GenItem { kind: self, comments, pos: Pos::Inherit }
} }
} }
pub struct GenMember { pub struct GenMember {
exported: bool,
name: Tok<String>, name: Tok<String>,
kind: GenMemberKind, kind: MemKind,
} }
impl GenMember { impl GenMember {
pub fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::Member { pub fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::Member {
api::Member { api::Member {
name: self.name.marker(), name: self.name.marker(),
exported: self.exported,
kind: self.kind.into_api(&mut ctx.push_path(self.name)), kind: self.kind.into_api(&mut ctx.push_path(self.name)),
} }
} }
} }
pub enum GenMemberKind { pub enum MemKind {
Const(GenExpr), Const(Expr),
Mod { imports: Vec<Sym>, items: Vec<GenItem> }, Mod { imports: Vec<Sym>, items: Vec<GenItem> },
Lazy(LazyMemberFactory), Lazy(LazyMemberFactory),
} }
impl GenMemberKind { impl MemKind {
pub fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MemberKind { pub fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MemberKind {
match self { match self {
Self::Lazy(lazy) => api::MemberKind::Lazy(ctx.with_lazy(lazy)), Self::Lazy(lazy) => api::MemberKind::Lazy(ctx.with_lazy(lazy)),
Self::Const(c) => api::MemberKind::Const(c.into_api(ctx.sys())), Self::Const(c) =>
api::MemberKind::Const(c.api_return(ctx.sys(), &mut |_| panic!("Slot found in const tree"))),
Self::Mod { imports, items } => api::MemberKind::Module(api::Module { Self::Mod { imports, items } => api::MemberKind::Module(api::Module {
imports: imports.into_iter().map(|t| t.tok().marker()).collect(), items: (imports.into_iter())
items: items.into_iter().map(|i| i.into_api(ctx)).collect_vec(), .map(|t| GenItemKind::Import(t).gen())
.chain(items)
.map(|i| i.into_api(ctx))
.collect_vec(),
}), }),
} }
} }

View File

@@ -11,9 +11,11 @@ hashbrown = "0.14.5"
itertools = "0.13.0" itertools = "0.13.0"
lazy_static = "1.4.0" lazy_static = "1.4.0"
never = "0.1.0" never = "0.1.0"
num-traits = "0.2.19"
orchid-api = { version = "0.1.0", path = "../orchid-api" } orchid-api = { version = "0.1.0", path = "../orchid-api" }
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" } orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
orchid-base = { version = "0.1.0", path = "../orchid-base" } orchid-base = { version = "0.1.0", path = "../orchid-base" }
ordered-float = "4.2.0" ordered-float = "4.2.0"
paste = "1.0.15" paste = "1.0.15"
substack = "1.1.0" substack = "1.1.1"
trait-set = "0.3.0"

View File

@@ -1,24 +1,34 @@
use std::collections::VecDeque;
use std::num::NonZeroU64; use std::num::NonZeroU64;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use hashbrown::HashMap; use hashbrown::HashMap;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use orchid_base::error::OrcErrv;
use orchid_base::interner::deintern;
use orchid_base::location::Pos;
use orchid_base::name::Sym;
use orchid_base::tree::AtomTok;
use crate::api; use crate::api;
use crate::extension::{AtomHand, System}; use crate::extension::AtomHand;
pub type ExprParseCtx = ();
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct RtExpr { pub struct Expr {
is_canonical: Arc<AtomicBool>, is_canonical: Arc<AtomicBool>,
data: Arc<()>, pos: Pos,
kind: Arc<RwLock<ExprKind>>,
} }
impl RtExpr { impl Expr {
pub fn pos(&self) -> Pos { self.pos.clone() }
pub fn as_atom(&self) -> Option<AtomHand> { todo!() } pub fn as_atom(&self) -> Option<AtomHand> { todo!() }
pub fn strong_count(&self) -> usize { todo!() } pub fn strong_count(&self) -> usize { todo!() }
pub fn id(&self) -> api::ExprTicket { pub fn id(&self) -> api::ExprTicket {
api::ExprTicket( api::ExprTicket(
NonZeroU64::new(self.data.as_ref() as *const () as usize as u64) NonZeroU64::new(self.kind.as_ref() as *const RwLock<_> as usize as u64)
.expect("this is a ref, it cannot be null"), .expect("this is a ref, it cannot be null"),
) )
} }
@@ -31,14 +41,29 @@ impl RtExpr {
pub fn resolve(tk: api::ExprTicket) -> Option<Self> { pub fn resolve(tk: api::ExprTicket) -> Option<Self> {
KNOWN_EXPRS.read().unwrap().get(&tk).cloned() KNOWN_EXPRS.read().unwrap().get(&tk).cloned()
} }
pub fn from_api(api: api::Expr, sys: &System) -> Self { pub fn from_api(api: api::Expression, ctx: &mut ExprParseCtx) -> Self {
Self { data: Arc::default(), is_canonical: Arc::default() } if let api::ExpressionKind::Slot(tk) = &api.kind {
return Self::resolve(*tk).expect("Invalid slot");
}
Self {
kind: Arc::new(RwLock::new(ExprKind::from_api(api.kind, ctx))),
is_canonical: Arc::default(),
pos: Pos::from_api(&api.location),
} }
} }
impl Drop for RtExpr { pub fn to_api(&self) -> api::InspectedKind {
use api::InspectedKind as K;
match &*self.kind.read().unwrap() {
ExprKind::Atom(a) => K::Atom(a.to_api()),
ExprKind::Bottom(b) => K::Bottom(b.to_api()),
_ => K::Opaque,
}
}
}
impl Drop for Expr {
fn drop(&mut self) { fn drop(&mut self) {
// If the only two references left are this and known, remove from known // If the only two references left are this and known, remove from known
if Arc::strong_count(&self.data) == 2 && self.is_canonical.load(Ordering::Relaxed) { if Arc::strong_count(&self.kind) == 2 && self.is_canonical.load(Ordering::Relaxed) {
// if known is poisoned, a leak is preferable to a panicking destructor // if known is poisoned, a leak is preferable to a panicking destructor
if let Ok(mut w) = KNOWN_EXPRS.write() { if let Ok(mut w) = KNOWN_EXPRS.write() {
w.remove(&self.id()); w.remove(&self.id());
@@ -48,5 +73,67 @@ impl Drop for RtExpr {
} }
lazy_static! { lazy_static! {
static ref KNOWN_EXPRS: RwLock<HashMap<api::ExprTicket, RtExpr>> = RwLock::default(); static ref KNOWN_EXPRS: RwLock<HashMap<api::ExprTicket, Expr>> = RwLock::default();
}
#[derive(Clone, Debug)]
pub enum ExprKind {
Seq(Expr, Expr),
Call(Expr, Expr),
Atom(AtomHand),
Argument,
Lambda(Option<PathSet>, Expr),
Bottom(OrcErrv),
Const(Sym),
}
impl ExprKind {
pub fn from_api(api: api::ExpressionKind, ctx: &mut ExprParseCtx) -> Self {
use api::ExpressionKind as K;
match api {
K::Slot(_) => panic!("Handled in Expr"),
K::Lambda(id, b) => ExprKind::Lambda(PathSet::from_api(id, &b), Expr::from_api(*b, ctx)),
K::Arg(_) => ExprKind::Argument,
K::Bottom(b) => ExprKind::Bottom(OrcErrv::from_api(&b)),
K::Call(f, x) => ExprKind::Call(Expr::from_api(*f, ctx), Expr::from_api(*x, ctx)),
K::Const(c) => ExprKind::Const(Sym::from_tok(deintern(c)).unwrap()),
K::NewAtom(a) => ExprKind::Atom(AtomHand::from_api(a)),
K::Seq(a, b) => ExprKind::Seq(Expr::from_api(*a, ctx), Expr::from_api(*b, ctx)),
}
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum Step {
Left,
Right,
}
#[derive(Clone, Debug)]
pub struct PathSet {
/// The single steps through [super::nort::Clause::Apply]
pub steps: VecDeque<Step>,
/// if Some, it splits at a [super::nort::Clause::Apply]. If None, it ends in
/// a [super::nort::Clause::LambdaArg]
pub next: Option<(Box<PathSet>, Box<PathSet>)>,
}
impl PathSet {
pub fn after(mut self, step: Step) -> Self {
self.steps.push_front(step);
self
}
pub fn from_api(id: u64, b: &api::Expression) -> Option<Self> {
use api::ExpressionKind as K;
match &b.kind {
K::Arg(id2) => (id == *id2).then(|| Self { steps: VecDeque::new(), next: None }),
K::Bottom(_) | K::Const(_) | K::NewAtom(_) | K::Slot(_) => None,
K::Lambda(_, b) => Self::from_api(id, b),
K::Call(l, r) | K::Seq(l, r) => match (Self::from_api(id, l), Self::from_api(id, r)) {
(Some(a), Some(b)) =>
Some(Self { steps: VecDeque::new(), next: Some((Box::new(a), Box::new(b))) }),
(Some(l), None) => Some(l.after(Step::Left)),
(None, Some(r)) => Some(r.after(Step::Right)),
(None, None) => None,
},
}
}
} }

View File

@@ -11,19 +11,23 @@ use hashbrown::hash_map::Entry;
use hashbrown::HashMap; use hashbrown::HashMap;
use itertools::Itertools; use itertools::Itertools;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use orchid_api_traits::{enc_vec, Decode, Request}; use orchid_api::TStrv;
use orchid_api_traits::Request;
use orchid_base::char_filter::char_filter_match; use orchid_base::char_filter::char_filter_match;
use orchid_base::error::{errv_from_apiv, mk_err, OrcRes}; use orchid_base::error::{OrcErrv, OrcRes};
use orchid_base::interner::{deintern, intern, Tok}; use orchid_base::interner::{deintern, intern, Tok};
use orchid_base::logging::Logger; use orchid_base::logging::Logger;
use orchid_base::macros::{mtreev_from_api, mtreev_to_api};
use orchid_base::parse::Comment;
use orchid_base::reqnot::{ReqNot, Requester as _}; use orchid_base::reqnot::{ReqNot, Requester as _};
use orchid_base::tree::{ttv_from_api, AtomInTok}; use orchid_base::tree::{ttv_from_api, AtomTok};
use orchid_base::{clone, intern}; use orchid_base::clone;
use ordered_float::NotNan; use ordered_float::NotNan;
use substack::{Stackframe, Substack}; use substack::{Stackframe, Substack};
use crate::api; use crate::api;
use crate::expr::RtExpr; use crate::expr::Expr;
use crate::macros::macro_recur;
use crate::tree::{Member, ParsTokTree}; use crate::tree::{Member, ParsTokTree};
#[derive(Debug, destructure)] #[derive(Debug, destructure)]
@@ -76,7 +80,7 @@ impl AtomHand {
Self::create_new(atom) Self::create_new(atom)
} }
} }
pub fn call(self, arg: RtExpr) -> api::Expr { pub fn call(self, arg: Expr) -> api::Expression {
let owner_sys = self.0.owner.clone(); let owner_sys = self.0.owner.clone();
let reqnot = owner_sys.reqnot(); let reqnot = owner_sys.reqnot();
let ticket = owner_sys.give_expr(arg.canonicalize(), || arg); let ticket = owner_sys.give_expr(arg.canonicalize(), || arg);
@@ -85,20 +89,13 @@ impl AtomHand {
Err(hand) => reqnot.request(api::CallRef(hand.api_ref(), ticket)), Err(hand) => reqnot.request(api::CallRef(hand.api_ref(), ticket)),
} }
} }
pub fn same(&self, other: &AtomHand) -> bool { pub fn req(&self, key: TStrv, req: Vec<u8>) -> Option<Vec<u8>> {
let owner = self.0.owner.id(); self.0.owner.reqnot().request(api::Fwded(self.0.api_ref(), key, req))
if other.0.owner.id() != owner {
return false;
}
self.0.owner.reqnot().request(api::AtomSame(self.0.api_ref(), other.0.api_ref()))
}
pub fn req(&self, req: Vec<u8>) -> Vec<u8> {
self.0.owner.reqnot().request(api::Fwded(self.0.api_ref(), req))
} }
pub fn api_ref(&self) -> api::Atom { self.0.api_ref() } pub fn api_ref(&self) -> api::Atom { self.0.api_ref() }
pub fn print(&self) -> String { self.0.owner.reqnot().request(api::AtomPrint(self.0.api_ref())) } pub fn print(&self) -> String { self.0.owner.reqnot().request(api::AtomPrint(self.0.api_ref())) }
} }
impl AtomInTok for AtomHand { impl AtomTok for AtomHand {
type Context = (); type Context = ();
fn from_api(atom: &orchid_api::Atom, _: Range<u32>, (): &mut Self::Context) -> Self { fn from_api(atom: &orchid_api::Atom, _: Range<u32>, (): &mut Self::Context) -> Self {
Self::from_api(atom.clone()) Self::from_api(atom.clone())
@@ -118,6 +115,7 @@ impl fmt::Display for AtomHand {
pub trait ExtensionPort: Send + Sync { pub trait ExtensionPort: Send + Sync {
fn send(&self, msg: &[u8]); fn send(&self, msg: &[u8]);
fn receive(&self) -> Option<Vec<u8>>; fn receive(&self) -> Option<Vec<u8>>;
fn header(&self) -> &api::ExtensionHeader;
} }
/// Data held about an Extension. This is refcounted within [Extension]. It's /// Data held about an Extension. This is refcounted within [Extension]. It's
@@ -139,7 +137,7 @@ impl Drop for ExtensionData {
fn acq_expr(sys: api::SysId, extk: api::ExprTicket) { fn acq_expr(sys: api::SysId, extk: api::ExprTicket) {
(System::resolve(sys).expect("Expr acq'd by invalid system")) (System::resolve(sys).expect("Expr acq'd by invalid system"))
.give_expr(extk, || RtExpr::resolve(extk).expect("Invalid expr acq'd")); .give_expr(extk, || Expr::resolve(extk).expect("Invalid expr acq'd"));
} }
fn rel_expr(sys: api::SysId, extk: api::ExprTicket) { fn rel_expr(sys: api::SysId, extk: api::ExprTicket) {
@@ -154,10 +152,11 @@ fn rel_expr(sys: api::SysId, extk: api::ExprTicket) {
pub struct Extension(Arc<ExtensionData>); pub struct Extension(Arc<ExtensionData>);
impl Extension { impl Extension {
pub fn new_process(port: Arc<dyn ExtensionPort>, logger: Logger) -> io::Result<Self> { pub fn new_process(port: Arc<dyn ExtensionPort>, logger: Logger) -> io::Result<Self> {
port.send(&enc_vec(&api::HostHeader { log_strategy: logger.strat() })); let eh = port.header();
let header_reply = port.receive().expect("Extension exited immediately");
let eh = api::ExtensionHeader::decode(&mut &header_reply[..]);
let ret = Arc::new_cyclic(|weak: &Weak<ExtensionData>| ExtensionData { let ret = Arc::new_cyclic(|weak: &Weak<ExtensionData>| ExtensionData {
systems: (eh.systems.iter().cloned())
.map(|decl| SystemCtor { decl, ext: weak.clone() })
.collect(),
logger, logger,
port: port.clone(), port: port.clone(),
reqnot: ReqNot::new( reqnot: ReqNot::new(
@@ -175,46 +174,43 @@ impl Extension {
}, },
api::ExtHostNotif::Log(api::Log(str)) => weak.upgrade().unwrap().logger.log(str), api::ExtHostNotif::Log(api::Log(str)) => weak.upgrade().unwrap().logger.log(str),
}), }),
|req| match req.req() { |hand, req| match req {
api::ExtHostReq::Ping(ping) => req.handle(ping, &()), api::ExtHostReq::Ping(ping) => hand.handle(&ping, &()),
api::ExtHostReq::IntReq(intreq) => match intreq { api::ExtHostReq::IntReq(intreq) => match intreq {
api::IntReq::InternStr(s) => req.handle(s, &intern(&**s.0).marker()), api::IntReq::InternStr(s) => hand.handle(&s, &intern(&**s.0).marker()),
api::IntReq::InternStrv(v) => req.handle(v, &intern(&*v.0).marker()), api::IntReq::InternStrv(v) => hand.handle(&v, &intern(&*v.0).marker()),
api::IntReq::ExternStr(si) => req.handle(si, &deintern(si.0).arc()), api::IntReq::ExternStr(si) => hand.handle(&si, &deintern(si.0).arc()),
api::IntReq::ExternStrv(vi) => api::IntReq::ExternStrv(vi) =>
req.handle(vi, &Arc::new(deintern(vi.0).iter().map(|t| t.marker()).collect_vec())), hand.handle(&vi, &Arc::new(deintern(vi.0).iter().map(|t| t.marker()).collect_vec())),
}, },
api::ExtHostReq::Fwd(fw @ api::Fwd(atom, _body)) => { api::ExtHostReq::Fwd(ref fw @ api::Fwd(ref atom, ref key, ref body)) => {
let sys = System::resolve(atom.owner).unwrap(); let sys = System::resolve(atom.owner).unwrap();
req.handle(fw, &sys.reqnot().request(api::Fwded(fw.0.clone(), fw.1.clone()))) hand.handle(fw, &sys.reqnot().request(api::Fwded(fw.0.clone(), *key, body.clone())))
},
api::ExtHostReq::SysFwd(ref fw @ api::SysFwd(id, ref body)) => {
let sys = System::resolve(id).unwrap();
hand.handle(fw, &sys.request(body.clone()))
}, },
api::ExtHostReq::SubLex(sl) => { api::ExtHostReq::SubLex(sl) => {
let (rep_in, rep_out) = sync_channel(0); let (rep_in, rep_out) = sync_channel(0);
let lex_g = LEX_RECUR.lock().unwrap(); let lex_g = LEX_RECUR.lock().unwrap();
let req_in = lex_g.get(&sl.id).expect("Sublex for nonexistent lexid"); let req_in = lex_g.get(&sl.id).expect("Sublex for nonexistent lexid");
req_in.send(ReqPair(sl.clone(), rep_in)).unwrap(); req_in.send(ReqPair(sl.clone(), rep_in)).unwrap();
req.handle(sl, &rep_out.recv().unwrap()) hand.handle(&sl, &rep_out.recv().unwrap())
},
api::ExtHostReq::ExprReq(api::ExprReq::Inspect(ins @ api::Inspect(tk))) => {
let expr = RtExpr::resolve(*tk);
req.handle(ins, &api::Details {
refcount: 1,
expr: api::Expr {
location: api::Location::None,
clause: api::Clause::Bottom(vec![
mk_err(
intern!(str: "Unsupported"),
"Inspecting clauses is unsupported at the moment",
[],
)
.to_api(),
]),
}, },
api::ExtHostReq::ExprReq(api::ExprReq::Inspect(ins @ api::Inspect { target })) => {
let expr = Expr::resolve(target).expect("Invalid ticket");
hand.handle(&ins, &api::Inspected {
refcount: expr.strong_count() as u32,
location: expr.pos().to_api(),
kind: expr.to_api(),
}) })
}, },
api::ExtHostReq::RunMacros(ref rm @ api::RunMacros{ ref run_id, ref query }) => {
hand.handle(rm, &macro_recur(*run_id, mtreev_from_api(query)).map(|x| mtreev_to_api(&x)))
}
}, },
), ),
systems: eh.systems.into_iter().map(|decl| SystemCtor { decl, ext: weak.clone() }).collect(),
}); });
let weak = Arc::downgrade(&ret); let weak = Arc::downgrade(&ret);
thread::Builder::new() thread::Builder::new()
@@ -263,7 +259,11 @@ impl SystemCtor {
id, id,
})); }));
let root = (sys_inst.const_root.into_iter()) let root = (sys_inst.const_root.into_iter())
.map(|(k, v)| Member::from_api(api::Member { exported: true, name: k, kind: v }, &data)) .map(|(k, v)| Member::from_api(
api::Member { name: k, kind: v },
Substack::Bottom.push(deintern(k)),
&data
))
.collect_vec(); .collect_vec();
data.0.const_root.set(root).unwrap(); data.0.const_root.set(root).unwrap();
inst_g.insert(id, data.clone()); inst_g.insert(id, data.clone());
@@ -281,7 +281,7 @@ pub struct ReqPair<R: Request>(R, pub SyncSender<R::Response>);
#[derive(destructure)] #[derive(destructure)]
pub struct SystemInstData { pub struct SystemInstData {
exprs: RwLock<HashMap<api::ExprTicket, (AtomicU32, RtExpr)>>, exprs: RwLock<HashMap<api::ExprTicket, (AtomicU32, Expr)>>,
ext: Extension, ext: Extension,
decl_id: api::SysDeclId, decl_id: api::SysDeclId,
lex_filter: api::CharFilter, lex_filter: api::CharFilter,
@@ -303,11 +303,7 @@ impl System {
pub fn id(&self) -> api::SysId { self.id } pub fn id(&self) -> api::SysId { self.id }
fn resolve(id: api::SysId) -> Option<System> { SYSTEM_INSTS.read().unwrap().get(&id).cloned() } fn resolve(id: api::SysId) -> Option<System> { SYSTEM_INSTS.read().unwrap().get(&id).cloned() }
fn reqnot(&self) -> &ReqNot<api::HostMsgSet> { &self.0.ext.0.reqnot } fn reqnot(&self) -> &ReqNot<api::HostMsgSet> { &self.0.ext.0.reqnot }
fn give_expr( fn give_expr(&self, ticket: api::ExprTicket, get_expr: impl FnOnce() -> Expr) -> api::ExprTicket {
&self,
ticket: api::ExprTicket,
get_expr: impl FnOnce() -> RtExpr,
) -> api::ExprTicket {
match self.0.exprs.write().unwrap().entry(ticket) { match self.0.exprs.write().unwrap().entry(ticket) {
Entry::Occupied(mut oe) => { Entry::Occupied(mut oe) => {
oe.get_mut().0.fetch_add(1, Ordering::Relaxed); oe.get_mut().0.fetch_add(1, Ordering::Relaxed);
@@ -356,12 +352,22 @@ impl System {
pub fn line_types(&self) -> impl Iterator<Item = Tok<String>> + '_ { pub fn line_types(&self) -> impl Iterator<Item = Tok<String>> + '_ {
self.line_types.iter().cloned() self.line_types.iter().cloned()
} }
pub fn parse(&self, line: Vec<ParsTokTree>) -> OrcRes<Vec<ParsTokTree>> { pub fn parse(
&self,
line: Vec<ParsTokTree>,
exported: bool,
comments: Vec<Comment>,
) -> OrcRes<Vec<ParsTokTree>> {
let line = line.iter().map(|t| t.to_api(&mut |n, _| match *n {})).collect_vec(); let line = line.iter().map(|t| t.to_api(&mut |n, _| match *n {})).collect_vec();
let parsed = (self.reqnot().request(api::ParseLine { sys: self.id(), line })) let comments = comments.iter().map(Comment::to_api).collect_vec();
.map_err(|e| errv_from_apiv(e.iter()))?; let parsed =
(self.reqnot().request(api::ParseLine { exported, sys: self.id(), comments, line }))
.map_err(|e| OrcErrv::from_api(&e))?;
Ok(ttv_from_api(parsed, &mut ())) Ok(ttv_from_api(parsed, &mut ()))
} }
pub fn request(&self, req: Vec<u8>) -> Vec<u8> {
self.reqnot().request(api::SysFwded(self.id(), req))
}
} }
impl fmt::Debug for System { impl fmt::Debug for System {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {

View File

@@ -2,12 +2,14 @@ use std::num::NonZeroU64;
use std::sync::Arc; use std::sync::Arc;
use hashbrown::HashMap; use hashbrown::HashMap;
use orchid_base::error::{mk_err, OrcErr, OrcRes}; use orchid_base::error::{mk_errv, OrcErrv, OrcRes};
use orchid_base::intern; use orchid_base::intern;
use orchid_base::interner::{deintern, intern, Tok}; use orchid_base::interner::{deintern, intern, Tok};
use orchid_base::location::Pos; use orchid_base::location::Pos;
use orchid_base::number::{num_to_err, parse_num};
use orchid_base::parse::{name_char, name_start, op_char, unrep_space}; use orchid_base::parse::{name_char, name_start, op_char, unrep_space};
use orchid_base::tokens::PARENS; use orchid_base::tokens::PARENS;
use orchid_base::tree::Ph;
use crate::api; use crate::api;
use crate::extension::{AtomHand, System}; use crate::extension::{AtomHand, System};
@@ -81,11 +83,9 @@ pub fn lex_once(ctx: &mut LexCtx) -> OrcRes<ParsTokTree> {
ParsTok::NS ParsTok::NS
} else if ctx.strip_prefix("--[") { } else if ctx.strip_prefix("--[") {
let (cmt, tail) = ctx.tail.split_once("]--").ok_or_else(|| { let (cmt, tail) = ctx.tail.split_once("]--").ok_or_else(|| {
vec![mk_err( mk_errv(intern!(str: "Unterminated block comment"), "This block comment has no ending ]--", [
intern!(str: "Unterminated block comment"), Pos::Range(start..start + 3).into(),
"This block comment has no ending ]--", ])
[Pos::Range(start..start + 3).into()],
)]
})?; })?;
ctx.set_tail(tail); ctx.set_tail(tail);
ParsTok::Comment(Arc::new(cmt.to_string())) ParsTok::Comment(Arc::new(cmt.to_string()))
@@ -98,11 +98,11 @@ pub fn lex_once(ctx: &mut LexCtx) -> OrcRes<ParsTokTree> {
ctx.trim_ws(); ctx.trim_ws();
while !ctx.strip_char('.') { while !ctx.strip_char('.') {
if ctx.tail.is_empty() { if ctx.tail.is_empty() {
return Err(vec![mk_err( return Err(mk_errv(
intern!(str: "Unclosed lambda"), intern!(str: "Unclosed lambda"),
"Lambdae started with \\ should separate arguments from body with .", "Lambdae started with \\ should separate arguments from body with .",
[Pos::Range(start..start + 1).into()], [Pos::Range(start..start + 1).into()],
)]); ));
} }
arg.push(lex_once(ctx)?); arg.push(lex_once(ctx)?);
ctx.trim_ws(); ctx.trim_ws();
@@ -113,33 +113,46 @@ pub fn lex_once(ctx: &mut LexCtx) -> OrcRes<ParsTokTree> {
ctx.trim_ws(); ctx.trim_ws();
while !ctx.strip_char(*rp) { while !ctx.strip_char(*rp) {
if ctx.tail.is_empty() { if ctx.tail.is_empty() {
return Err(vec![mk_err( return Err(mk_errv(
intern!(str: "unclosed paren"), intern!(str: "unclosed paren"),
format!("this {lp} has no matching {rp}"), format!("this {lp} has no matching {rp}"),
[Pos::Range(start..start + 1).into()], [Pos::Range(start..start + 1).into()],
)]); ));
} }
body.push(lex_once(ctx)?); body.push(lex_once(ctx)?);
ctx.trim_ws(); ctx.trim_ws();
} }
ParsTok::S(paren.clone(), body) ParsTok::S(paren.clone(), 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();
let num = parse_num(numstr).map_err(|e| num_to_err(e, pos))?;
ParsTok::Macro(Some(num.to_f64()))
} else {
ParsTok::Macro(None)
}
} else { } else {
for sys in ctx.systems { for sys in ctx.systems {
let mut errors = Vec::new(); let mut errors = Vec::new();
if ctx.tail.starts_with(|c| sys.can_lex(c)) { if ctx.tail.starts_with(|c| sys.can_lex(c)) {
let lexed = sys.lex(ctx.source.clone(), ctx.get_pos(), |pos| { let lx =
let mut sub_ctx = ctx.push(pos); sys.lex(ctx.source.clone(), ctx.get_pos(), |pos| match lex_once(&mut ctx.push(pos)) {
let ott = Ok(t) => Some(api::SubLexed { pos, ticket: ctx.add_subtree(t) }),
lex_once(&mut sub_ctx).inspect_err(|e| errors.extend(e.iter().cloned())).ok()?; Err(e) => {
Some(api::SubLexed { pos: sub_ctx.get_pos(), ticket: sub_ctx.add_subtree(ott) }) errors.push(e);
None
},
}); });
match lexed { match lx {
Ok(None) if errors.is_empty() => continue, Err(e) => return Err(errors.into_iter().fold(OrcErrv::from_api(&e), |a, b| a + b)),
Ok(None) => return Err(errors), Ok(Some(lexed)) => return Ok(tt_to_owned(&lexed.expr, &mut ctx.push(lexed.pos))),
Err(e) => return Err(e.into_iter().map(|e| OrcErr::from_api(&e)).collect()), Ok(None) => match errors.into_iter().reduce(|a, b| a + b) {
Ok(Some(lexed)) => { Some(errors) => return Err(errors),
ctx.set_pos(lexed.pos); None => continue,
return Ok(tt_to_owned(&lexed.expr, ctx));
}, },
} }
} }
@@ -149,11 +162,11 @@ pub fn lex_once(ctx: &mut LexCtx) -> OrcRes<ParsTokTree> {
} else if ctx.tail.starts_with(op_char) { } else if ctx.tail.starts_with(op_char) {
ParsTok::Name(intern(ctx.get_start_matches(op_char))) ParsTok::Name(intern(ctx.get_start_matches(op_char)))
} else { } else {
return Err(vec![mk_err( return Err(mk_errv(
intern!(str: "Unrecognized character"), intern!(str: "Unrecognized character"),
"The following syntax is meaningless.", "The following syntax is meaningless.",
[Pos::Range(start..start + 1).into()], [Pos::Range(start..start + 1).into()],
)]); ));
} }
}; };
Ok(ParsTokTree { tok, range: start..ctx.get_pos() }) Ok(ParsTokTree { tok, range: start..ctx.get_pos() })
@@ -162,19 +175,28 @@ pub fn lex_once(ctx: &mut LexCtx) -> OrcRes<ParsTokTree> {
fn tt_to_owned(api: &api::TokenTree, ctx: &mut LexCtx<'_>) -> ParsTokTree { fn tt_to_owned(api: &api::TokenTree, ctx: &mut LexCtx<'_>) -> ParsTokTree {
let tok = match &api.token { let tok = match &api.token {
api::Token::Atom(atom) => ParsTok::Atom(AtomHand::from_api(atom.clone())), api::Token::Atom(atom) => ParsTok::Atom(AtomHand::from_api(atom.clone())),
api::Token::Bottom(err) => ParsTok::Bottom(err.iter().map(OrcErr::from_api).collect()), api::Token::Bottom(err) => ParsTok::Bottom(OrcErrv::from_api(err)),
api::Token::Lambda(arg) => api::Token::LambdaHead(arg) => ParsTok::LambdaHead(ttv_to_owned(arg, ctx)),
ParsTok::LambdaHead(arg.iter().map(|t| tt_to_owned(t, ctx)).collect()), api::Token::Lambda(arg, b) => ParsTok::Lambda(ttv_to_owned(arg, ctx), ttv_to_owned(b, ctx)),
api::Token::Name(name) => ParsTok::Name(deintern(*name)), api::Token::Name(name) => ParsTok::Name(deintern(*name)),
api::Token::S(p, b) => ParsTok::S(p.clone(), b.iter().map(|t| tt_to_owned(t, ctx)).collect()), api::Token::S(p, b) => ParsTok::S(p.clone(), b.iter().map(|t| tt_to_owned(t, ctx)).collect()),
api::Token::Slot(id) => return ctx.rm_subtree(*id), api::Token::Slot(id) => return ctx.rm_subtree(*id),
api::Token::BR => ParsTok::BR, api::Token::BR => ParsTok::BR,
api::Token::NS => ParsTok::NS, api::Token::NS => ParsTok::NS,
api::Token::Comment(c) => ParsTok::Comment(c.clone()), api::Token::Comment(c) => ParsTok::Comment(c.clone()),
api::Token::Ph(ph) => ParsTok::Ph(Ph::from_api(ph)),
api::Token::Macro(prio) => ParsTok::Macro(*prio)
}; };
ParsTokTree { range: api.range.clone(), tok } ParsTokTree { range: api.range.clone(), tok }
} }
fn ttv_to_owned<'a>(
api: impl IntoIterator<Item = &'a api::TokenTree>,
ctx: &mut LexCtx<'_>
) -> Vec<ParsTokTree> {
api.into_iter().map(|t| tt_to_owned(t, ctx)).collect()
}
pub fn lex(text: Tok<String>, systems: &[System]) -> OrcRes<Vec<ParsTokTree>> { pub fn lex(text: Tok<String>, systems: &[System]) -> OrcRes<Vec<ParsTokTree>> {
let mut sub_trees = HashMap::new(); let mut sub_trees = HashMap::new();
let mut ctx = LexCtx { source: &text, sub_trees: &mut sub_trees, tail: &text[..], systems }; let mut ctx = LexCtx { source: &text, sub_trees: &mut sub_trees, tail: &text[..], systems };

View File

@@ -7,3 +7,4 @@ pub mod lex;
pub mod parse; pub mod parse;
pub mod subprocess; pub mod subprocess;
pub mod tree; pub mod tree;
pub mod macros;

20
orchid-host/src/macros.rs Normal file
View File

@@ -0,0 +1,20 @@
use std::sync::RwLock;
use hashbrown::HashMap;
use lazy_static::lazy_static;
use orchid_base::macros::MTree;
use trait_set::trait_set;
use crate::api::ParsId;
trait_set!{
trait MacroCB = Fn(Vec<MTree>) -> Option<Vec<MTree>> + Send + Sync;
}
lazy_static!{
static ref RECURSION: RwLock<HashMap<ParsId, Box<dyn MacroCB>>> = RwLock::default();
}
pub fn macro_recur(run_id: ParsId, input: Vec<MTree>) -> Option<Vec<MTree>> {
(RECURSION.read().unwrap()[&run_id])(input)
}

View File

@@ -2,18 +2,21 @@ use std::{iter, thread};
use itertools::Itertools; use itertools::Itertools;
use never::Never; use never::Never;
use orchid_base::error::{mk_err, OrcErr, OrcRes, Reporter}; use orchid_base::error::{mk_err, mk_errv, OrcErrv, OrcRes, Reporter};
use orchid_base::intern; use orchid_base::intern;
use orchid_base::interner::Tok; use orchid_base::interner::Tok;
use orchid_base::location::Pos; use orchid_base::location::Pos;
use orchid_base::macros::{MTok, MTree};
use orchid_base::name::Sym;
use orchid_base::parse::{ use orchid_base::parse::{
expect_end, line_items, parse_multiname, strip_fluff, try_pop_no_fluff, Comment, CompName, expect_end, line_items, parse_multiname, strip_fluff, try_pop_no_fluff, Comment, Import,
Snippet, Parsed, Snippet,
}; };
use orchid_base::tree::{Paren, TokTree, Token}; use orchid_base::tree::{Paren, TokTree, Token};
use substack::Substack;
use crate::extension::{AtomHand, System}; use crate::extension::{AtomHand, System};
use crate::tree::{Item, ItemKind, Member, MemberKind, Module, ParsTokTree}; use crate::tree::{Code, CodeLocator, Item, ItemKind, Member, MemberKind, Module, ParsTokTree, Rule, RuleKind};
type ParsSnippet<'a> = Snippet<'a, 'static, AtomHand, Never>; type ParsSnippet<'a> = Snippet<'a, 'static, AtomHand, Never>;
@@ -22,15 +25,20 @@ pub trait ParseCtx: Send + Sync {
fn reporter(&self) -> &impl Reporter; fn reporter(&self) -> &impl Reporter;
} }
pub fn parse_items(ctx: &impl ParseCtx, items: ParsSnippet) -> OrcRes<Vec<Item>> { pub fn parse_items(
ctx: &impl ParseCtx,
path: Substack<Tok<String>>,
items: ParsSnippet
) -> OrcRes<Vec<Item>> {
let lines = line_items(items); let lines = line_items(items);
let mut ok = iter::from_fn(|| None).take(lines.len()).collect_vec(); let mut ok = iter::from_fn(|| None).take(lines.len()).collect_vec();
thread::scope(|s| { thread::scope(|s| {
let mut threads = Vec::new(); let mut threads = Vec::new();
for (slot, (cmts, item)) in ok.iter_mut().zip(lines.into_iter()) { for (slot, Parsed { output: cmts, tail }) in ok.iter_mut().zip(lines.into_iter()) {
let path = &path;
threads.push(s.spawn(move || { threads.push(s.spawn(move || {
*slot = Some(parse_item(ctx, cmts, item)?); *slot = Some(parse_item(ctx, path.clone(), cmts, tail)?);
Ok::<(), Vec<OrcErr>>(()) Ok::<(), OrcErrv>(())
})) }))
} }
for t in threads { for t in threads {
@@ -42,136 +50,239 @@ pub fn parse_items(ctx: &impl ParseCtx, items: ParsSnippet) -> OrcRes<Vec<Item>>
pub fn parse_item( pub fn parse_item(
ctx: &impl ParseCtx, ctx: &impl ParseCtx,
path: Substack<Tok<String>>,
comments: Vec<Comment>, comments: Vec<Comment>,
item: ParsSnippet, item: ParsSnippet,
) -> OrcRes<Vec<Item>> { ) -> OrcRes<Vec<Item>> {
match item.pop_front() { match item.pop_front() {
Some((TokTree { tok: Token::Name(n), .. }, postdisc)) => match n { Some((TokTree { tok: Token::Name(n), .. }, postdisc)) => match n {
n if *n == intern!(str: "export") => match try_pop_no_fluff(postdisc)? { n if *n == intern!(str: "export") => match try_pop_no_fluff(postdisc)? {
(TokTree { tok: Token::Name(n), .. }, postdisc) => Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } =>
parse_item_2(ctx, comments, true, n.clone(), postdisc), parse_exportable_item(ctx, path, comments, true, n.clone(), tail),
(TokTree { tok: Token::NS, .. }, postdisc) => { Parsed { output: TokTree { tok: Token::NS, .. }, tail } => {
let (exports, surplus) = parse_multiname(ctx.reporter(), postdisc)?; let Parsed { output: exports, tail } = parse_multiname(ctx.reporter(), tail)?;
let mut ok = Vec::new(); let mut ok = Vec::new();
exports.into_iter().for_each(|e| match (&e.path.as_slice(), e.name) { exports.into_iter().for_each(|(e, pos)| match (&e.path.as_slice(), e.name) {
([], Some(n)) => ok.push(Item { ([], Some(n)) =>
comments: comments.clone(), ok.push(Item { comments: comments.clone(), pos, kind: ItemKind::Export(n) }),
pos: e.pos.clone(),
kind: ItemKind::Export(n),
}),
(_, Some(_)) => ctx.reporter().report(mk_err( (_, Some(_)) => ctx.reporter().report(mk_err(
intern!(str: "Compound export"), intern!(str: "Compound export"),
"Cannot export compound names (names containing the :: separator)", "Cannot export compound names (names containing the :: separator)",
[e.pos.into()], [pos.into()],
)), )),
(_, None) => ctx.reporter().report(mk_err( (_, None) => ctx.reporter().report(mk_err(
intern!(str: "Wildcard export"), intern!(str: "Wildcard export"),
"Exports cannot contain the globstar *", "Exports cannot contain the globstar *",
[e.pos.into()], [pos.into()],
)), )),
}); });
expect_end(surplus)?; expect_end(tail)?;
Ok(ok) Ok(ok)
}, },
(bogus, _) => Err(vec![mk_err( Parsed { output, .. } => Err(mk_errv(
intern!(str: "Malformed export"), intern!(str: "Malformed export"),
"`export` can either prefix other lines or list names inside ::( ) or ::[ ]", "`export` can either prefix other lines or list names inside ::( ) or ::[ ]",
[Pos::Range(bogus.range.clone()).into()], [Pos::Range(output.range.clone()).into()],
)]), )),
}, },
n if *n == intern!(str: "import") => parse_import(ctx, postdisc).map(|v| { n if *n == intern!(str: "import") => parse_import(ctx, postdisc).map(|v| {
Vec::from_iter(v.into_iter().map(|t| Item { Vec::from_iter(v.into_iter().map(|(t, pos)| Item {
comments: comments.clone(), comments: comments.clone(),
pos: Pos::Range(postdisc.pos()), pos,
kind: ItemKind::Import(t), kind: ItemKind::Import(t),
})) }))
}), }),
n => parse_item_2(ctx, comments, false, n.clone(), postdisc), n => parse_exportable_item(ctx, path, comments, false, n.clone(), postdisc),
}, },
Some(_) => Err(vec![mk_err( Some(_) =>
intern!(str: "Expected a line type"), Err(mk_errv(intern!(str: "Expected a line type"), "All lines must begin with a keyword", [
"All lines must begin with a keyword", Pos::Range(item.pos()).into(),
[Pos::Range(item.pos()).into()], ])),
)]),
None => unreachable!("These lines are filtered and aggregated in earlier stages"), None => unreachable!("These lines are filtered and aggregated in earlier stages"),
} }
} }
pub fn parse_import(ctx: &impl ParseCtx, tail: ParsSnippet) -> OrcRes<Vec<CompName>> { pub fn parse_import(ctx: &impl ParseCtx, tail: ParsSnippet) -> OrcRes<Vec<(Import, Pos)>> {
let (imports, surplus) = parse_multiname(ctx.reporter(), tail)?; let Parsed { output: imports, tail } = parse_multiname(ctx.reporter(), tail)?;
expect_end(surplus)?; expect_end(tail)?;
Ok(imports) Ok(imports)
} }
pub fn parse_item_2( pub fn parse_exportable_item(
ctx: &impl ParseCtx, ctx: &impl ParseCtx,
path: Substack<Tok<String>>,
comments: Vec<Comment>, comments: Vec<Comment>,
exported: bool, exported: bool,
discr: Tok<String>, discr: Tok<String>,
tail: ParsSnippet, tail: ParsSnippet,
) -> OrcRes<Vec<Item>> { ) -> OrcRes<Vec<Item>> {
let kind = if discr == intern!(str: "mod") { let kind = if discr == intern!(str: "mod") {
let (name, body) = parse_module(ctx, tail)?; let (name, body) = parse_module(ctx, path, tail)?;
ItemKind::Member(Member::new(exported, name, MemberKind::Mod(body))) ItemKind::Member(Member::new(name, MemberKind::Mod(body)))
} else if discr == intern!(str: "const") { } else if discr == intern!(str: "const") {
let (name, val) = parse_const(tail)?; let (name, val) = parse_const(tail)?;
ItemKind::Member(Member::new(exported, name, MemberKind::Const(val))) let locator = CodeLocator::to_const(path.push(name.clone()).unreverse());
ItemKind::Member(Member::new(name, MemberKind::Const(Code::from_code(locator, val))))
} else if let Some(sys) = ctx.systems().find(|s| s.can_parse(discr.clone())) { } else if let Some(sys) = ctx.systems().find(|s| s.can_parse(discr.clone())) {
let line = sys.parse(tail.to_vec())?; let line = sys.parse(tail.to_vec(), exported, comments)?;
return parse_items(ctx, Snippet::new(tail.prev(), &line)); return parse_items(ctx, path, Snippet::new(tail.prev(), &line));
} else { } else {
let ext_lines = ctx.systems().flat_map(System::line_types).join(", "); let ext_lines = ctx.systems().flat_map(System::line_types).join(", ");
return Err(vec![mk_err( return Err(mk_errv(
intern!(str: "Unrecognized line type"), intern!(str: "Unrecognized line type"),
format!("Line types are: const, mod, macro, grammar, {ext_lines}"), format!("Line types are: const, mod, macro, grammar, {ext_lines}"),
[Pos::Range(tail.prev().range.clone()).into()], [Pos::Range(tail.prev().range.clone()).into()],
)]); ));
}; };
Ok(vec![Item { comments, pos: Pos::Range(tail.pos()), kind }]) Ok(vec![Item { comments, pos: Pos::Range(tail.pos()), kind }])
} }
pub fn parse_module(ctx: &impl ParseCtx, tail: ParsSnippet) -> OrcRes<(Tok<String>, Module)> { pub fn parse_module(
ctx: &impl ParseCtx,
path: Substack<Tok<String>>,
tail: ParsSnippet
) -> OrcRes<(Tok<String>, Module)> {
let (name, tail) = match try_pop_no_fluff(tail)? { let (name, tail) = match try_pop_no_fluff(tail)? {
(TokTree { tok: Token::Name(n), .. }, tail) => (n.clone(), tail), Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } => (n.clone(), tail),
(tt, _) => Parsed { output, .. } =>
return Err(vec![mk_err( return Err(mk_errv(
intern!(str: "Missing module name"), intern!(str: "Missing module name"),
format!("A name was expected, {tt} was found"), format!("A name was expected, {output} was found"),
[Pos::Range(tt.range.clone()).into()], [Pos::Range(output.range.clone()).into()],
)]), )),
}; };
let (body, surplus) = match try_pop_no_fluff(tail)? { let Parsed { output, tail: surplus } = try_pop_no_fluff(tail)?;
(TokTree { tok: Token::S(Paren::Round, b), .. }, tail) => (b, tail), expect_end(surplus)?;
(tt, _) => let body = output.as_s(Paren::Round).ok_or_else(|| mk_errv(
return Err(vec![mk_err(
intern!(str: "Expected module body"), intern!(str: "Expected module body"),
format!("A ( block ) was expected, {tt} was found"), format!("A ( block ) was expected, {output} was found"),
[Pos::Range(tt.range.clone()).into()], [Pos::Range(output.range.clone()).into()],
)]), ))?;
}; let path = path.push(name.clone());
let items = parse_items(ctx, ParsSnippet::new(surplus.prev(), body))?; Ok((name, Module::new(parse_items(ctx, path, body)?)))
Ok((name, Module { imports: vec![], items }))
} }
pub fn parse_const(tail: ParsSnippet) -> OrcRes<(Tok<String>, Vec<ParsTokTree>)> { pub fn parse_const(tail: ParsSnippet) -> OrcRes<(Tok<String>, Vec<ParsTokTree>)> {
let (name, tail) = match try_pop_no_fluff(tail)? { let Parsed { output, tail } = try_pop_no_fluff(tail)?;
(TokTree { tok: Token::Name(n), .. }, tail) => (n.clone(), tail), let name = output.as_name().ok_or_else(|| mk_errv(
(tt, _) =>
return Err(vec![mk_err(
intern!(str: "Missing module name"), intern!(str: "Missing module name"),
format!("A name was expected, {tt} was found"), format!("A name was expected, {output} was found"),
[Pos::Range(tt.range.clone()).into()], [Pos::Range(output.range.clone()).into()],
)]), ))?;
}; let Parsed { output, tail } = try_pop_no_fluff(tail)?;
let tail = match try_pop_no_fluff(tail)? { if !output.is_kw(intern!(str: "=")) {
(TokTree { tok: Token::Name(n), .. }, tail) if *n == intern!(str: ":=") => tail, return Err(mk_errv(
(tt, _) =>
return Err(vec![mk_err(
intern!(str: "Missing walrus := separator"), intern!(str: "Missing walrus := separator"),
format!("Expected operator := , found {tt}"), format!("Expected operator := , found {output}"),
[Pos::Range(tt.range.clone()).into()], [Pos::Range(output.range.clone()).into()],
)]), ))
}; }
try_pop_no_fluff(tail)?; try_pop_no_fluff(tail)?;
Ok((name, tail.iter().flat_map(strip_fluff).collect_vec())) Ok((name, tail.iter().flat_map(strip_fluff).collect_vec()))
} }
pub fn parse_mtree<'a>(
mut snip: ParsSnippet<'a>
) -> OrcRes<Vec<MTree<'static>>> {
let mut mtreev = Vec::new();
while let Some((ttree, tail)) = snip.pop_front() {
let (range, tok, tail) = match &ttree.tok {
Token::S(p, b) => (
ttree.range.clone(),
MTok::S(*p, parse_mtree(Snippet::new(ttree, b))?),
tail,
),
Token::Name(tok) => {
let mut segments = vec![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)?;
segments.push(output.as_name().ok_or_else(|| mk_errv(
intern!(str: "Namespaced name interrupted"),
"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()]
))?);
snip = tail;
end = output.range.end;
}
(ttree.range.start..end, MTok::Name(Sym::new(segments).unwrap()), snip)
},
Token::NS => return Err(mk_errv(
intern!(str: "Unexpected :: in macro pattern"),
":: can only follow a name outside export statements",
[Pos::Range(ttree.range.clone()).into()]
)),
Token::Ph(ph) => (ttree.range.clone(), MTok::Ph(ph.clone()), tail),
Token::Atom(_) | Token::Macro(_) => return Err(mk_errv(
intern!(str: "Unsupported token in macro patterns"),
format!("Macro patterns can only contain names, braces, and lambda, not {ttree}."),
[Pos::Range(ttree.range.clone()).into()]
)),
Token::BR | Token::Comment(_) => continue,
Token::Bottom(e) => return Err(e.clone()),
Token::Lambda(arg, body) => {
let tok = MTok::Lambda(
parse_mtree(Snippet::new(&ttree, &arg))?,
parse_mtree(Snippet::new(&ttree, &body))?,
);
(ttree.range.clone(), tok, tail)
},
Token::LambdaHead(arg) => (
ttree.range.start..snip.pos().end,
MTok::Lambda(parse_mtree(Snippet::new(&ttree, &arg))?, parse_mtree(tail)?),
Snippet::new(ttree, &[]),
),
Token::Slot(_) | Token::X(_) => panic!("Did not expect {} in parsed token tree", &ttree.tok),
};
mtreev.push(MTree { pos: Pos::Range(range.clone()), tok });
snip = tail;
}
Ok(mtreev)
}
pub fn parse_macro(tail: ParsSnippet, macro_i: u16, path: Substack<Tok<String>>) -> OrcRes<Vec<Rule>> {
let (surplus, prev, block) = match try_pop_no_fluff(tail)? {
Parsed { tail, output: o@TokTree { tok: Token::S(Paren::Round, b), .. } } => (tail, o, b),
Parsed { output, .. } => return Err(mk_errv(
intern!(str: "m"),
format!("Macro blocks must either start with a block or a ..$:number"),
[Pos::Range(output.range.clone()).into()]
)),
};
expect_end(surplus)?;
let mut errors = Vec::new();
let mut rules = Vec::new();
for (i, item) in line_items(Snippet::new(prev, &block)).into_iter().enumerate() {
let Parsed { tail, output } = try_pop_no_fluff(item.tail)?;
if !output.is_kw(intern!(str: "rule")) {
errors.extend(mk_errv(
intern!(str: "non-rule in macro"),
format!("Expected `rule`, got {output}"),
[Pos::Range(output.range.clone()).into()]
));
continue
};
let (pat, body) = match tail.split_once(|t| t.is_kw(intern!(str: "=>"))) {
Some((a, b)) => (a, b),
None => {
errors.extend(mk_errv(
intern!(str: "no => in macro rule"),
"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)?,
kind: RuleKind::Native(Code::from_code(
CodeLocator::to_rule(path.unreverse(), macro_i, i as u16),
body.to_vec(),
))
})
}
if let Ok(e) = OrcErrv::new(errors) { Err(e) } else { Ok(rules) }
}

View File

@@ -1,17 +1,21 @@
use std::io::{self, BufRead as _}; use std::io::{self, BufRead as _, Write};
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Mutex; use std::sync::Mutex;
use std::{process, thread}; use std::{process, thread};
use orchid_api::ExtensionHeader;
use orchid_api_traits::{Decode, Encode};
use orchid_base::logging::Logger; use orchid_base::logging::Logger;
use orchid_base::msg::{recv_msg, send_msg}; use orchid_base::msg::{recv_msg, send_msg};
use crate::api;
use crate::extension::ExtensionPort; use crate::extension::ExtensionPort;
pub struct Subprocess { pub struct Subprocess {
child: Mutex<process::Child>, child: Mutex<process::Child>,
stdin: Mutex<process::ChildStdin>, stdin: Mutex<process::ChildStdin>,
stdout: Mutex<process::ChildStdout>, stdout: Mutex<process::ChildStdout>,
header: ExtensionHeader,
} }
impl Subprocess { impl Subprocess {
pub fn new(mut cmd: process::Command, logger: Logger) -> io::Result<Self> { pub fn new(mut cmd: process::Command, logger: Logger) -> io::Result<Self> {
@@ -22,8 +26,11 @@ impl Subprocess {
.stdout(process::Stdio::piped()) .stdout(process::Stdio::piped())
.stderr(process::Stdio::piped()) .stderr(process::Stdio::piped())
.spawn()?; .spawn()?;
let stdin = child.stdin.take().unwrap(); let mut stdin = child.stdin.take().unwrap();
let stdout = child.stdout.take().unwrap(); api::HostHeader { log_strategy: logger.strat() }.encode(&mut stdin);
stdin.flush()?;
let mut stdout = child.stdout.take().unwrap();
let header = ExtensionHeader::decode(&mut stdout);
let child_stderr = child.stderr.take().unwrap(); let child_stderr = child.stderr.take().unwrap();
thread::Builder::new().name(format!("stderr-fwd:{prog}")).spawn(move || { thread::Builder::new().name(format!("stderr-fwd:{prog}")).spawn(move || {
let mut reader = io::BufReader::new(child_stderr); let mut reader = io::BufReader::new(child_stderr);
@@ -35,14 +42,25 @@ impl Subprocess {
logger.log(buf); logger.log(buf);
} }
})?; })?;
Ok(Self { child: Mutex::new(child), stdin: Mutex::new(stdin), stdout: Mutex::new(stdout) }) Ok(Self {
child: Mutex::new(child),
stdin: Mutex::new(stdin),
stdout: Mutex::new(stdout),
header,
})
} }
} }
impl Drop for Subprocess { impl Drop for Subprocess {
fn drop(&mut self) { self.child.lock().unwrap().wait().expect("Extension exited with error"); } fn drop(&mut self) { self.child.lock().unwrap().wait().expect("Extension exited with error"); }
} }
impl ExtensionPort for Subprocess { impl ExtensionPort for Subprocess {
fn send(&self, msg: &[u8]) { send_msg(&mut *self.stdin.lock().unwrap(), msg).unwrap() } fn header(&self) -> &orchid_api::ExtensionHeader { &self.header }
fn send(&self, msg: &[u8]) {
if msg.starts_with(&[0, 0, 0, 0x1c]) {
panic!("Received unnecessary prefix");
}
send_msg(&mut *self.stdin.lock().unwrap(), msg).unwrap()
}
fn receive(&self) -> Option<Vec<u8>> { fn receive(&self) -> Option<Vec<u8>> {
match recv_msg(&mut *self.stdout.lock().unwrap()) { match recv_msg(&mut *self.stdout.lock().unwrap()) {
Ok(msg) => Some(msg), Ok(msg) => Some(msg),

View File

@@ -1,16 +1,20 @@
use std::fmt::Debug;
use std::sync::{Mutex, OnceLock}; use std::sync::{Mutex, OnceLock};
use itertools::Itertools; use itertools::Itertools;
use never::Never; use never::Never;
use orchid_base::error::OrcRes; use orchid_base::error::OrcRes;
use orchid_base::interner::{deintern, Tok}; use orchid_base::interner::{deintern, intern, Tok};
use orchid_base::location::Pos; use orchid_base::location::Pos;
use orchid_base::macros::{mtreev_from_api, MTree};
use orchid_base::name::Sym; use orchid_base::name::Sym;
use orchid_base::parse::{Comment, CompName}; use orchid_base::parse::{Comment, Import};
use orchid_base::tree::{ttv_from_api, TokTree, Token}; use orchid_base::tree::{TokTree, Token};
use ordered_float::NotNan;
use substack::{with_iter_stack, Substack};
use crate::api; use crate::api;
use crate::expr::RtExpr; use crate::expr::Expr;
use crate::extension::{AtomHand, System}; use crate::extension::{AtomHand, System};
pub type ParsTokTree = TokTree<'static, AtomHand, Never>; pub type ParsTokTree = TokTree<'static, AtomHand, Never>;
@@ -25,81 +29,165 @@ pub struct Item {
#[derive(Debug)] #[derive(Debug)]
pub enum ItemKind { pub enum ItemKind {
Raw(Vec<ParsTokTree>),
Member(Member), Member(Member),
Export(Tok<String>), Export(Tok<String>),
Import(CompName), Import(Import),
Macro(Option<NotNan<f64>>, Vec<Rule>)
} }
impl Item { impl Item {
pub fn from_api(tree: api::Item, sys: &System) -> Self { pub fn from_api<'a>(
tree: api::Item,
path: Substack<Tok<String>>,
sys: &System
) -> Self {
let kind = match tree.kind { let kind = match tree.kind {
api::ItemKind::Raw(tokv) => ItemKind::Raw(ttv_from_api(tokv, &mut ())), api::ItemKind::Member(m) => ItemKind::Member(Member::from_api(m, path, sys)),
api::ItemKind::Member(m) => ItemKind::Member(Member::from_api(m, sys)), api::ItemKind::Import(i) =>
api::ItemKind::Import(i) => ItemKind::Import(CompName::from_api(i)), ItemKind::Import(Import{ path: Sym::deintern(i).iter().collect(), name: None }),
api::ItemKind::Export(e) => ItemKind::Export(deintern(e)), api::ItemKind::Export(e) => ItemKind::Export(deintern(e)),
api::ItemKind::Macro(api::MacroBlock { priority, rules }) => ItemKind::Macro(priority, {
Vec::from_iter(rules.into_iter().map(|api| Rule {
pos: Pos::from_api(&api.location),
pattern: mtreev_from_api(&api.pattern),
kind: RuleKind::Remote(sys.clone(), api.id),
comments: api.comments.iter().map(Comment::from_api).collect_vec()
}))
})
}; };
let comments = tree let comments = tree.comments.iter().map(Comment::from_api).collect_vec();
.comments
.into_iter()
.map(|(text, l)| Comment { text, pos: Pos::from_api(&l) })
.collect_vec();
Self { pos: Pos::from_api(&tree.location), comments, kind } Self { pos: Pos::from_api(&tree.location), comments, kind }
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Member { pub struct Member {
pub exported: bool,
pub name: Tok<String>, pub name: Tok<String>,
pub kind: OnceLock<MemberKind>, pub kind: OnceLock<MemberKind>,
pub lazy: Mutex<Option<LazyMemberHandle>>, pub lazy: Mutex<Option<LazyMemberHandle>>,
} }
impl Member { impl Member {
pub fn from_api(api::Member { exported: public, name, kind }: api::Member, sys: &System) -> Self { pub fn from_api<'a>(
let (kind, lazy) = match kind { api: api::Member,
api::MemberKind::Const(c) => path: Substack<Tok<String>>,
(OnceLock::from(MemberKind::PreCnst(RtExpr::from_api(c, sys))), None), sys: &System,
api::MemberKind::Module(m) => ) -> Self {
(OnceLock::from(MemberKind::Mod(Module::from_api(m, sys))), None), let name = deintern(api.name);
api::MemberKind::Lazy(id) => (OnceLock::new(), Some(LazyMemberHandle(id, sys.clone()))), let full_path = path.push(name.clone());
let kind = match api.kind {
api::MemberKind::Lazy(id) =>
return LazyMemberHandle(id, sys.clone(), intern(&full_path.unreverse())).to_member(name),
api::MemberKind::Const(c) => MemberKind::Const(Code::from_expr(
CodeLocator::to_const(full_path.unreverse()),
Expr::from_api(c, &mut ())
)),
api::MemberKind::Module(m) => MemberKind::Mod(Module::from_api(m, full_path, sys)),
}; };
Member { exported: public, name: deintern(name), kind, lazy: Mutex::new(lazy) } Member { name, kind: OnceLock::from(kind), lazy: Mutex::default() }
} }
pub fn new(public: bool, name: Tok<String>, kind: MemberKind) -> Self { pub fn new(name: Tok<String>, kind: MemberKind) -> Self {
Member { exported: public, name, kind: OnceLock::from(kind), lazy: Mutex::default() } Member { name, kind: OnceLock::from(kind), lazy: Mutex::default() }
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub enum MemberKind { pub enum MemberKind {
Const(Vec<ParsTokTree>), Const(Code),
PreCnst(RtExpr),
Mod(Module), Mod(Module),
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Module { pub struct Module {
pub imports: Vec<Sym>, pub imports: Vec<Sym>,
pub exports: Vec<Tok<String>>,
pub items: Vec<Item>, pub items: Vec<Item>,
} }
impl Module { impl Module {
pub fn from_api(m: api::Module, sys: &System) -> Self { pub fn new(items: impl IntoIterator<Item = Item>) -> Self {
Self { let items = items.into_iter().collect_vec();
imports: m.imports.into_iter().map(|m| Sym::from_tok(deintern(m)).unwrap()).collect_vec(), let exports = (items.iter())
items: m.items.into_iter().map(|i| Item::from_api(i, sys)).collect_vec(), .filter_map(|i| match &i.kind {
ItemKind::Export(e) => Some(e.clone()),
_ => None,
})
.collect_vec();
Self { imports: vec![], exports, items }
} }
pub fn from_api(m: api::Module, path: Substack<Tok<String>>, sys: &System) -> Self {
let mut output = Vec::new();
for item in m.items.into_iter() {
let next = Item::from_api(item, path.clone(), sys);
output.push(next);
}
Self::new(output)
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct LazyMemberHandle(api::TreeId, System); pub struct LazyMemberHandle(api::TreeId, System, Tok<Vec<Tok<String>>>);
impl LazyMemberHandle { impl LazyMemberHandle {
pub fn run(self) -> OrcRes<MemberKind> { pub fn run(self) -> OrcRes<MemberKind> {
match self.1.get_tree(self.0) { match self.1.get_tree(self.0) {
api::MemberKind::Const(c) => Ok(MemberKind::PreCnst(RtExpr::from_api(c, &self.1))), api::MemberKind::Const(c) => Ok(MemberKind::Const(Code {
api::MemberKind::Module(m) => Ok(MemberKind::Mod(Module::from_api(m, &self.1))), bytecode: Expr::from_api(c, &mut ()).into(),
api::MemberKind::Lazy(id) => Self(id, self.1).run(), locator: CodeLocator { steps: self.2, rule_loc: None },
source: None,
})),
api::MemberKind::Module(m) => with_iter_stack(self.2.iter().cloned(), |path| {
Ok(MemberKind::Mod(Module::from_api(m, path, &self.1)))
}),
api::MemberKind::Lazy(id) => Self(id, self.1, self.2).run(),
} }
} }
pub fn to_member(self, name: Tok<String>) -> Member {
Member { name, kind: OnceLock::new(), lazy: Mutex::new(Some(self)) }
}
}
#[derive(Debug)]
pub struct Rule {
pub pos: Pos,
pub comments: Vec<Comment>,
pub pattern: Vec<MTree<'static>>,
pub kind: RuleKind,
}
#[derive(Debug)]
pub enum RuleKind {
Remote(System, api::MacroId),
Native(Code),
}
#[derive(Debug)]
pub struct Code {
locator: CodeLocator,
source: Option<Vec<ParsTokTree>>,
bytecode: OnceLock<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<ParsTokTree>) -> Self {
Self { locator, source: Some(code), bytecode: OnceLock::new() }
}
}
/// 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(path: impl IntoIterator<Item = Tok<String>>) -> Self {
Self { steps: intern(&path.into_iter().collect_vec()), rule_loc: None }
}
pub fn to_rule(path: impl IntoIterator<Item = Tok<String>>, macro_i: u16, rule_i: u16) -> Self {
Self { steps: intern(&path.into_iter().collect_vec()), rule_loc: Some((macro_i, rule_i)) }
}
} }

View File

@@ -1,10 +1,9 @@
use never::Never;
use orchid_api_derive::Coding; use orchid_api_derive::Coding;
use orchid_base::error::OrcRes; use orchid_base::error::OrcRes;
use orchid_extension::atom::{AtomFactory, Atomic, AtomicFeatures, ReqPck, ToAtom, TypAtom}; use orchid_extension::atom::{AtomFactory, MethodSet, Atomic, AtomicFeatures, ToAtom, TypAtom};
use orchid_extension::atom_thin::{ThinAtom, ThinVariant}; use orchid_extension::atom_thin::{ThinAtom, ThinVariant};
use orchid_extension::conv::TryFromExpr; use orchid_extension::conv::TryFromExpr;
use orchid_extension::expr::ExprHandle; use orchid_extension::expr::Expr;
use ordered_float::NotNan; use ordered_float::NotNan;
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
@@ -12,13 +11,13 @@ pub struct Int(pub i64);
impl Atomic for Int { impl Atomic for Int {
type Variant = ThinVariant; type Variant = ThinVariant;
type Data = Self; type Data = Self;
type Req = Never; fn reg_reqs() -> MethodSet<Self> {
MethodSet::new()
} }
impl ThinAtom for Int {
fn handle_req(&self, pck: impl ReqPck<Self>) { pck.never() }
} }
impl ThinAtom for Int {}
impl TryFromExpr for Int { impl TryFromExpr for Int {
fn try_from_expr(expr: ExprHandle) -> OrcRes<Self> { fn try_from_expr(expr: Expr) -> OrcRes<Self> {
TypAtom::<Int>::try_from_expr(expr).map(|t| t.value) TypAtom::<Int>::try_from_expr(expr).map(|t| t.value)
} }
} }
@@ -28,13 +27,11 @@ pub struct Float(pub NotNan<f64>);
impl Atomic for Float { impl Atomic for Float {
type Variant = ThinVariant; type Variant = ThinVariant;
type Data = Self; type Data = Self;
type Req = Never; fn reg_reqs() -> MethodSet<Self> { MethodSet::new() }
}
impl ThinAtom for Float {
fn handle_req(&self, pck: impl ReqPck<Self>) { pck.never() }
} }
impl ThinAtom for Float {}
impl TryFromExpr for Float { impl TryFromExpr for Float {
fn try_from_expr(expr: ExprHandle) -> OrcRes<Self> { fn try_from_expr(expr: Expr) -> OrcRes<Self> {
TypAtom::<Float>::try_from_expr(expr).map(|t| t.value) TypAtom::<Float>::try_from_expr(expr).map(|t| t.value)
} }
} }
@@ -44,10 +41,10 @@ pub enum Numeric {
Float(NotNan<f64>), Float(NotNan<f64>),
} }
impl TryFromExpr for Numeric { impl TryFromExpr for Numeric {
fn try_from_expr(expr: ExprHandle) -> OrcRes<Self> { fn try_from_expr(expr: Expr) -> OrcRes<Self> {
Int::try_from_expr(expr.clone()).map(|t| Numeric::Int(t.0)).or_else(|e| { Int::try_from_expr(expr.clone())
Float::try_from_expr(expr).map(|t| Numeric::Float(t.0)).map_err(|e2| [e, e2].concat()) .map(|t| Numeric::Int(t.0))
}) .or_else(|e| Float::try_from_expr(expr).map(|t| Numeric::Float(t.0)).map_err(|e2| e + e2))
} }
} }
impl ToAtom for Numeric { impl ToAtom for Numeric {

View File

@@ -20,7 +20,7 @@ impl Lexer for NumLexer {
Ok(Numeric::Float(f)) => Float(f).factory(), Ok(Numeric::Float(f)) => Float(f).factory(),
Ok(Numeric::Uint(uint)) => Int(uint.try_into().unwrap()).factory(), Ok(Numeric::Uint(uint)) => Int(uint.try_into().unwrap()).factory(),
Ok(Numeric::Decimal(dec)) => Float(NotNan::new(dec.try_into().unwrap()).unwrap()).factory(), Ok(Numeric::Decimal(dec)) => Float(NotNan::new(dec.try_into().unwrap()).unwrap()).factory(),
Err(e) => return Err(vec![num_to_err(e, ctx.pos(all))]), Err(e) => return Err(num_to_err(e, ctx.pos(all)).into()),
}; };
Ok((tail, GenTok::X(fac).at(ctx.pos(all)..ctx.pos(tail)))) Ok((tail, GenTok::X(fac).at(ctx.pos(all)..ctx.pos(tail))))
} }

View File

@@ -1,11 +1,13 @@
use std::sync::Arc; use std::sync::Arc;
use never::Never;
use orchid_base::interner::Tok; use orchid_base::interner::Tok;
use orchid_extension::atom::{AtomDynfo, AtomicFeatures}; use orchid_extension::atom::{AtomDynfo, AtomicFeatures};
use orchid_extension::entrypoint::ExtReq;
use orchid_extension::fs::DeclFs; use orchid_extension::fs::DeclFs;
use orchid_extension::system::{System, SystemCard}; use orchid_extension::system::{System, SystemCard};
use orchid_extension::system_ctor::SystemCtor; use orchid_extension::system_ctor::SystemCtor;
use orchid_extension::tree::{comments, fun, module, root_mod, GenMemberKind}; use orchid_extension::tree::{comments, fun, module, root_mod, MemKind};
use crate::number::num_atom::{Float, Int}; use crate::number::num_atom::{Float, Int};
use crate::string::str_atom::{IntStrAtom, StrAtom}; use crate::string::str_atom::{IntStrAtom, StrAtom};
@@ -23,14 +25,17 @@ impl SystemCtor for StdSystem {
} }
impl SystemCard for StdSystem { impl SystemCard for StdSystem {
type Ctor = Self; type Ctor = Self;
const ATOM_DEFS: &'static [Option<&'static dyn AtomDynfo>] = type Req = Never;
&[Some(Int::INFO), Some(Float::INFO), Some(StrAtom::INFO), Some(IntStrAtom::INFO)]; 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 { impl System for StdSystem {
fn request(_: ExtReq, req: Self::Req) -> orchid_base::reqnot::Receipt { match req {} }
fn lexers() -> Vec<orchid_extension::lexer::LexerObj> { vec![&StringLexer] } fn lexers() -> Vec<orchid_extension::lexer::LexerObj> { vec![&StringLexer] }
fn parsers() -> Vec<orchid_extension::parser::ParserObj> { vec![] } fn parsers() -> Vec<orchid_extension::parser::ParserObj> { vec![] }
fn vfs() -> DeclFs { DeclFs::Mod(&[]) } fn vfs() -> DeclFs { DeclFs::Mod(&[]) }
fn env() -> Vec<(Tok<String>, GenMemberKind)> { fn env() -> Vec<(Tok<String>, MemKind)> {
vec![root_mod("std", [], [module(true, "string", [], [comments( vec![root_mod("std", [], [module(true, "string", [], [comments(
["Concatenate two strings"], ["Concatenate two strings"],
fun(true, "concat", |left: OrcString, right: OrcString| { fun(true, "concat", |left: OrcString, right: OrcString| {

View File

@@ -1,52 +1,53 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::io; use std::io;
use std::num::NonZeroU64; use std::ops::Deref;
use std::sync::Arc; use std::sync::Arc;
use never::Never;
use orchid_api_derive::Coding; use orchid_api_derive::Coding;
use orchid_api_traits::{Encode, Request}; use orchid_api_traits::{Encode, Request};
use orchid_base::error::{mk_err, OrcRes}; use orchid_base::error::{mk_errv, OrcRes};
use orchid_base::id_store::IdStore;
use orchid_base::intern; use orchid_base::intern;
use orchid_base::interner::{deintern, intern, Tok}; use orchid_base::interner::{deintern, intern, Tok};
use orchid_extension::atom::{Atomic, ReqPck, TypAtom}; use orchid_extension::atom::{AtomMethod, Atomic, MethodSet, Supports, TypAtom};
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant}; use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
use orchid_extension::conv::TryFromExpr; use orchid_extension::conv::TryFromExpr;
use orchid_extension::expr::ExprHandle; use orchid_extension::expr::Expr;
use orchid_extension::system::SysCtx; use orchid_extension::system::SysCtx;
pub static STR_REPO: IdStore<Arc<String>> = IdStore::new();
#[derive(Copy, Clone, Coding)] #[derive(Copy, Clone, Coding)]
pub struct StringGetVal; pub struct StringGetVal;
impl Request for StringGetVal { impl Request for StringGetVal {
type Response = String; type Response = Arc<String>;
}
impl AtomMethod for StringGetVal {
const NAME: &str = "std::string_get_val";
}
impl Supports<StringGetVal> for StrAtom {
fn handle(&self, _: SysCtx, _: StringGetVal) -> <StringGetVal as Request>::Response {
self.0.clone()
}
} }
pub struct StrAtom(NonZeroU64); #[derive(Clone)]
pub struct StrAtom(Arc<String>);
impl Atomic for StrAtom { impl Atomic for StrAtom {
type Variant = OwnedVariant; type Variant = OwnedVariant;
type Data = NonZeroU64; type Data = ();
type Req = StringGetVal; fn reg_reqs() -> MethodSet<Self> { MethodSet::new().handle::<StringGetVal>() }
} }
impl StrAtom { impl StrAtom {
pub fn new(str: Arc<String>) -> Self { Self(STR_REPO.add(str).id()) } pub fn new(str: Arc<String>) -> Self { Self(str) }
pub fn value(&self) -> Arc<String> { self.0.clone() }
} }
impl Clone for StrAtom { impl Deref for StrAtom {
fn clone(&self) -> Self { Self::new(self.local_value()) } type Target = str;
} fn deref(&self) -> &Self::Target { &self.0 }
impl StrAtom {
fn try_local_value(&self) -> Option<Arc<String>> { STR_REPO.get(self.0).map(|r| r.clone()) }
fn local_value(&self) -> Arc<String> { self.try_local_value().expect("no string found for ID") }
} }
impl OwnedAtom for StrAtom { impl OwnedAtom for StrAtom {
type Refs = (); type Refs = ();
fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.0) } fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
fn same(&self, _: SysCtx, other: &Self) -> bool { self.local_value() == other.local_value() }
fn handle_req(&self, pck: impl ReqPck<Self>) { self.local_value().encode(pck.unpack().1) }
fn serialize(&self, _: SysCtx, sink: &mut (impl io::Write + ?Sized)) -> Self::Refs { fn serialize(&self, _: SysCtx, sink: &mut (impl io::Write + ?Sized)) -> Self::Refs {
self.local_value().encode(sink) self.deref().encode(sink)
} }
fn deserialize(mut ctx: impl DeserializeCtx, _: Self::Refs) -> Self { fn deserialize(mut ctx: impl DeserializeCtx, _: Self::Refs) -> Self {
Self::new(Arc::new(ctx.read::<String>())) Self::new(Arc::new(ctx.read::<String>()))
@@ -58,7 +59,7 @@ pub struct IntStrAtom(Tok<String>);
impl Atomic for IntStrAtom { impl Atomic for IntStrAtom {
type Variant = OwnedVariant; type Variant = OwnedVariant;
type Data = orchid_api::TStr; type Data = orchid_api::TStr;
type Req = Never; fn reg_reqs() -> MethodSet<Self> { MethodSet::new() }
} }
impl From<Tok<String>> for IntStrAtom { impl From<Tok<String>> for IntStrAtom {
fn from(value: Tok<String>) -> Self { Self(value) } fn from(value: Tok<String>) -> Self { Self(value) }
@@ -66,8 +67,7 @@ impl From<Tok<String>> for IntStrAtom {
impl OwnedAtom for IntStrAtom { impl OwnedAtom for IntStrAtom {
type Refs = (); type Refs = ();
fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.0.marker()) } fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.0.marker()) }
fn handle_req(&self, pck: impl ReqPck<Self>) { pck.never() } fn print(&self, _ctx: SysCtx) -> String { format!("{:?}i", *self.0) }
fn print(&self, _ctx: SysCtx) -> String { format!("{:?}i", self.0.as_str()) }
fn serialize(&self, _: SysCtx, write: &mut (impl io::Write + ?Sized)) { self.0.encode(write) } fn serialize(&self, _: SysCtx, write: &mut (impl io::Write + ?Sized)) { self.0.encode(write) }
fn deserialize(ctx: impl DeserializeCtx, _: ()) -> Self { Self(intern(&ctx.decode::<String>())) } fn deserialize(ctx: impl DeserializeCtx, _: ()) -> Self { Self(intern(&ctx.decode::<String>())) }
} }
@@ -81,22 +81,19 @@ impl<'a> OrcString<'a> {
pub fn get_string(&self) -> Arc<String> { pub fn get_string(&self) -> Arc<String> {
match &self { match &self {
Self::Int(tok) => deintern(tok.value).arc(), Self::Int(tok) => deintern(tok.value).arc(),
Self::Val(atom) => match STR_REPO.get(**atom) { Self::Val(atom) => atom.request(StringGetVal),
Some(rec) => rec.clone(),
None => Arc::new(atom.request(StringGetVal)),
},
} }
} }
} }
impl TryFromExpr for OrcString<'static> { impl TryFromExpr for OrcString<'static> {
fn try_from_expr(expr: ExprHandle) -> OrcRes<OrcString<'static>> { fn try_from_expr(expr: Expr) -> OrcRes<OrcString<'static>> {
if let Ok(v) = TypAtom::<StrAtom>::downcast(expr.clone()) { if let Ok(v) = TypAtom::<StrAtom>::try_from_expr(expr.clone()) {
return Ok(OrcString::Val(v)); return Ok(OrcString::Val(v));
} }
match TypAtom::<IntStrAtom>::downcast(expr) { match TypAtom::<IntStrAtom>::try_from_expr(expr) {
Ok(t) => Ok(OrcString::Int(t)), Ok(t) => Ok(OrcString::Int(t)),
Err(e) => Err(vec![mk_err(intern!(str: "A string was expected"), "", [e.0.clone().into()])]), Err(e) => Err(mk_errv(intern!(str: "A string was expected"), "", e.pos_iter())),
} }
} }
} }

View File

@@ -1,11 +1,11 @@
use itertools::Itertools; use itertools::Itertools;
use orchid_base::error::{mk_err, OrcErr, OrcRes}; use orchid_base::error::{mk_err, mk_errv, OrcErr, OrcRes};
use orchid_base::interner::intern; use orchid_base::interner::intern;
use orchid_base::location::Pos; use orchid_base::location::Pos;
use orchid_base::tree::{vname_tv, wrap_tokv}; use orchid_base::tree::{vname_tv, wrap_tokv};
use orchid_base::{intern, vname}; use orchid_base::{intern, vname};
use orchid_extension::atom::AtomicFeatures; use orchid_extension::atom::AtomicFeatures;
use orchid_extension::lexer::{err_lexer_na, LexContext, Lexer}; use orchid_extension::lexer::{err_not_applicable, LexContext, Lexer};
use orchid_extension::tree::{GenTok, GenTokTree}; use orchid_extension::tree::{GenTok, GenTokTree};
use super::str_atom::IntStrAtom; use super::str_atom::IntStrAtom;
@@ -96,33 +96,31 @@ pub struct StringLexer;
impl Lexer for StringLexer { impl Lexer for StringLexer {
const CHAR_FILTER: &'static [std::ops::RangeInclusive<char>] = &['"'..='"']; const CHAR_FILTER: &'static [std::ops::RangeInclusive<char>] = &['"'..='"'];
fn lex<'a>(all: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree<'a>)> { fn lex<'a>(all: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree<'a>)> {
let mut tail = all.strip_prefix('"').ok_or_else(err_lexer_na)?; let mut tail = all.strip_prefix('"').ok_or_else(err_not_applicable)?;
let mut parts = Vec::<GenTokTree<'a>>::new(); let mut ret = GenTok::X(IntStrAtom::from(intern!(str: "")).factory()).at(ctx.tok_ran(0, all));
let mut cur = String::new(); let mut cur = String::new();
let mut errors = vec![]; let mut errors = vec![];
let commit_str = let str_to_gen = |str: &mut String, tail: &str, err: &mut Vec<OrcErr>| {
|str: &mut String, tail: &str, err: &mut Vec<OrcErr>, parts: &mut Vec<GenTokTree<'a>>| { let str_val = parse_string(&str.split_off(0))
let str_val = parse_string(str)
.inspect_err(|e| err.push(e.clone().into_proj(ctx.pos(tail) - str.len() as u32))) .inspect_err(|e| err.push(e.clone().into_proj(ctx.pos(tail) - str.len() as u32)))
.unwrap_or_default(); .unwrap_or_default();
let tok = GenTok::X(IntStrAtom::from(intern(&*str_val)).factory()); GenTok::X(IntStrAtom::from(intern(&*str_val)).factory())
parts.push(tok.at(ctx.tok_ran(str.len() as u32, tail))); .at(ctx.tok_ran(str.len() as u32, tail))
*str = String::new(); };
let add_frag = |prev: GenTokTree<'a>, new: GenTokTree<'a>| {
let range = prev.range.start..new.range.end;
wrap_tokv(vname_tv(&vname!(std::string::concat), new.range.end).chain([prev, new]), range)
}; };
loop { loop {
if let Some(rest) = tail.strip_prefix('"') { if let Some(rest) = tail.strip_prefix('"') {
commit_str(&mut cur, tail, &mut errors, &mut parts); return Ok((rest, add_frag(ret, str_to_gen(&mut cur, tail, &mut errors))));
return Ok((rest, wrap_tokv(parts, ctx.pos(all)..ctx.pos(rest))));
} else if let Some(rest) = tail.strip_prefix('$') { } else if let Some(rest) = tail.strip_prefix('$') {
commit_str(&mut cur, tail, &mut errors, &mut parts); ret = add_frag(ret, str_to_gen(&mut cur, tail, &mut errors));
parts.push(GenTok::Name(intern!(str: "++")).at(ctx.tok_ran(1, rest)));
parts.extend(vname_tv(&vname!(std::string::convert), ctx.tok_ran(1, rest)));
let (new_tail, tree) = ctx.recurse(rest)?; let (new_tail, tree) = ctx.recurse(rest)?;
tail = new_tail; tail = new_tail;
parts.push(tree); ret = add_frag(ret, tree);
} else if tail.starts_with('\\') { } else if tail.starts_with('\\') {
// parse_string will deal with it, we just have to make sure we skip the next // parse_string will deal with it, we just have to skip the next char
// char
tail = &tail[2..]; tail = &tail[2..];
} else { } else {
let mut ch = tail.chars(); let mut ch = tail.chars();
@@ -131,12 +129,9 @@ impl Lexer for StringLexer {
tail = ch.as_str(); tail = ch.as_str();
} else { } else {
let range = ctx.pos(all)..ctx.pos(""); let range = ctx.pos(all)..ctx.pos("");
commit_str(&mut cur, tail, &mut errors, &mut parts); return Err(mk_errv(intern!(str: "No string end"), "String never terminated with \"", [
return Err(vec![mk_err( Pos::Range(range.clone()).into(),
intern!(str: "No string end"), ]));
"String never terminated with \"",
[Pos::Range(range.clone()).into()],
)]);
} }
} }
} }

View File

@@ -27,7 +27,7 @@ fn main() -> io::Result<ExitCode> {
let args = Args::parse(); let args = Args::parse();
match args.command { match args.command {
Commands::CheckApiRefs => walk_wsp(&mut |_| Ok(true), &mut |file| { Commands::CheckApiRefs => walk_wsp(&mut |_| Ok(true), &mut |file| {
if file.path().extension() == Some(OsStr::new("rs")) || file.file_name() == "lib.rs" { if file.path().extension() == Some(OsStr::new("rs")) && file.file_name() != "lib.rs" {
let mut contents = String::new(); let mut contents = String::new();
File::open(file.path())?.read_to_string(&mut contents)?; File::open(file.path())?.read_to_string(&mut contents)?;
for (l, line) in contents.lines().enumerate() { for (l, line) in contents.lines().enumerate() {