partway towards commands

I got very confused and started mucking about with "spawn" when in fact all I needed was the "inline" extension type in orcx that allows the interpreter to expose custom constants.
This commit is contained in:
2026-03-13 16:48:42 +01:00
parent cdcca694c5
commit 09cfcb1839
146 changed files with 3582 additions and 2822 deletions

View File

@@ -1,4 +1,3 @@
use std::borrow::Borrow;
use std::fmt::{self, Debug, Display};
use std::future::Future;
use std::marker::PhantomData;
@@ -12,62 +11,67 @@ use never::Never;
use orchid_api_traits::Coding;
use trait_set::trait_set;
use crate::error::OrcErrv;
use crate::format::{FmtCtx, FmtUnit, Format, Variants};
use crate::interner::{IStr, es};
use crate::location::{Pos, SrcRange};
use crate::name::{Sym, VName, VPath};
use crate::parse::Snippet;
use crate::{api, match_mapping, tl_cache};
use crate::{
FmtCtx, FmtUnit, Format, IStr, OrcErrv, Pos, Snippet, SrcRange, Sym, VName, VPath, Variants, api,
es, match_mapping, tl_cache,
};
/// The 3 types of parentheses Orchid's lexer recognizes as intrinsic groups in
/// the S-tree
pub type Paren = api::Paren;
/// Helper table with different kinds of parentheses recognized by the language.
/// opening, closing, variant name
pub const PARENS: &[(char, char, Paren)] =
&[('(', ')', Paren::Round), ('[', ']', Paren::Square), ('{', '}', Paren::Curly)];
/// Extension interface for embedded expressions and expression construction
/// commands inside token trees
pub trait TokenVariant<ApiEquiv: Clone + Debug + Coding>: Format + Clone + fmt::Debug {
/// Additional arguments to the deserializer. If deserialization of a token
/// type is impossible, set this to a sentinel unit type that describes why.
/// If you set this to [Never], your token tree type can never be
/// deserialized.
type FromApiCtx<'a>;
/// Additional arguments to the serializer. If serialization of a token type
/// is forbidden, set this to a sentinel unit type that describes how to avoid
/// it.
/// If you set this to [Never], your token tree type can never be serialized.
type ToApiCtx<'a>;
/// Deserializer
#[must_use]
fn from_api(
api: &ApiEquiv,
api: ApiEquiv,
ctx: &mut Self::FromApiCtx<'_>,
pos: SrcRange,
) -> impl Future<Output = Self>;
/// Serializer
#[must_use]
fn into_api(self, ctx: &mut Self::ToApiCtx<'_>) -> impl Future<Output = ApiEquiv>;
}
impl<T: Clone + Debug + Coding> TokenVariant<T> for Never {
type FromApiCtx<'a> = ();
type ToApiCtx<'a> = ();
async fn from_api(_: &T, _: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self {
async fn from_api(_: T, _: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self {
panic!("Cannot deserialize Never")
}
async fn into_api(self, _: &mut Self::ToApiCtx<'_>) -> T { match self {} }
}
trait_set! {
// TokenHandle
/// [api::Token::Handle] variant
pub trait ExprRepr = TokenVariant<api::ExprTicket>;
// TokenExpr
/// [api::Token::NewExpr] variant
pub trait ExtraTok = TokenVariant<api::Expression>;
}
trait_set! {
/// Callback to callback to [recur].
pub trait RecurCB<H: ExprRepr, X: ExtraTok> = Fn(TokTree<H, X>) -> TokTree<H, X>;
}
pub fn recur<H: ExprRepr, X: ExtraTok>(
tt: TokTree<H, X>,
f: &impl Fn(TokTree<H, X>, &dyn RecurCB<H, X>) -> TokTree<H, X>,
) -> TokTree<H, X> {
f(tt, &|TokTree { sr: range, tok }| {
let tok = match tok {
tok @ (Token::BR | Token::Bottom(_) | Token::Comment(_) | Token::Name(_)) => tok,
tok @ (Token::Handle(_) | Token::NewExpr(_)) => tok,
Token::NS(n, b) => Token::NS(n, Box::new(recur(*b, f))),
Token::LambdaHead(arg) => Token::LambdaHead(Box::new(recur(*arg, f))),
Token::S(p, b) => Token::S(p, b.into_iter().map(|tt| recur(tt, f)).collect_vec()),
};
TokTree { sr: range, tok }
})
}
/// An atom that can be passed through the API boundary as part of an
/// expression. In particular, atoms created by extensions use this form.
pub trait AtomRepr: Clone + Format {
type Ctx: ?Sized;
#[must_use]
@@ -93,6 +97,7 @@ impl Display for TokHandle<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Handle({})", self.0.0) }
}
/// Lexer output
#[derive(Clone, Debug)]
pub struct TokTree<H: ExprRepr, X: ExtraTok> {
pub tok: Token<H, X>,
@@ -102,22 +107,37 @@ pub struct TokTree<H: ExprRepr, X: ExtraTok> {
pub sr: SrcRange,
}
impl<H: ExprRepr, X: ExtraTok> TokTree<H, X> {
/// Visit all tokens, modify them at will, and optionally recurse into them by
/// calling the callback passed to your callback
pub fn recur(self, f: &impl Fn(Self, &dyn RecurCB<H, X>) -> Self) -> Self {
f(self, &|TokTree { sr: range, tok }| {
let tok = match tok {
tok @ (Token::BR | Token::Bottom(_) | Token::Comment(_) | Token::Name(_)) => tok,
tok @ (Token::Handle(_) | Token::NewExpr(_)) => tok,
Token::NS(n, b) => Token::NS(n, Box::new(b.recur(f))),
Token::LambdaHead(arg) => Token::LambdaHead(Box::new(arg.recur(f))),
Token::S(p, b) => Token::S(p, b.into_iter().map(|tt| tt.recur(f)).collect_vec()),
};
TokTree { sr: range, tok }
})
}
pub async fn from_api(
tt: &api::TokenTree,
tt: api::TokenTree,
hctx: &mut H::FromApiCtx<'_>,
xctx: &mut X::FromApiCtx<'_>,
src: &Sym,
) -> Self {
let pos = SrcRange::new(tt.range.clone(), src);
let tok = match_mapping!(&tt.token, api::Token => Token::<H, X> {
let pos = SrcRange::new(tt.range, src);
let tok = match_mapping!(tt.token, api::Token => Token::<H, X> {
BR,
NS(n => es(*n).await,
b => Box::new(Self::from_api(b, hctx, xctx, src).boxed_local().await)),
NS(n => es(n).await,
b => Box::new(Self::from_api(*b, hctx, xctx, src).boxed_local().await)),
Bottom(e => OrcErrv::from_api(e).await),
LambdaHead(arg => Box::new(Self::from_api(arg, hctx, xctx, src).boxed_local().await)),
Name(n => es(*n).await),
S(*par, b => ttv_from_api(b, hctx, xctx, src).await),
Comment(c => es(*c).await),
LambdaHead(arg => Box::new(Self::from_api(*arg, hctx, xctx, src).boxed_local().await)),
Name(n => es(n).await),
S(par, b => ttv_from_api(b, hctx, xctx, src).await),
Comment(c => es(c).await),
NewExpr(expr => X::from_api(expr, xctx, pos.clone()).await),
Handle(tk => H::from_api(tk, hctx, pos.clone()).await)
});
@@ -186,21 +206,22 @@ impl<H: ExprRepr, X: ExtraTok> Format for TokTree<H, X> {
}
}
/// Receive a token sequence from API
pub async fn ttv_from_api<H: ExprRepr, X: ExtraTok>(
tokv: impl IntoIterator<Item: Borrow<api::TokenTree>>,
tokv: impl IntoIterator<Item = api::TokenTree>,
hctx: &mut H::FromApiCtx<'_>,
xctx: &mut X::FromApiCtx<'_>,
src: &Sym,
) -> Vec<TokTree<H, X>> {
stream(async |mut cx| {
for tok in tokv {
cx.emit(TokTree::<H, X>::from_api(tok.borrow(), hctx, xctx, src).boxed_local().await).await
cx.emit(TokTree::<H, X>::from_api(tok, hctx, xctx, src).boxed_local().await).await
}
})
.collect()
.await
}
/// Encode a token sequence for sending
pub async fn ttv_into_api<H: ExprRepr, X: ExtraTok>(
tokv: impl IntoIterator<Item = TokTree<H, X>>,
hctx: &mut H::ToApiCtx<'_>,
@@ -215,6 +236,7 @@ pub async fn ttv_into_api<H: ExprRepr, X: ExtraTok>(
.await
}
/// Enclose the tokens in `()` if there is more than one
pub fn wrap_tokv<H: ExprRepr, X: ExtraTok>(
items: impl IntoIterator<Item = TokTree<H, X>>,
) -> TokTree<H, X> {
@@ -229,8 +251,6 @@ pub fn wrap_tokv<H: ExprRepr, X: ExtraTok>(
}
}
pub use api::Paren;
/// Lexer output variant
#[derive(Clone, Debug)]
pub enum Token<H: ExprRepr, X: ExtraTok> {
@@ -272,8 +292,10 @@ impl<H: ExprRepr, X: ExtraTok> Format for Token<H, X> {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
match self {
Self::BR => "\n".to_string().into(),
Self::Bottom(err) if err.len() == 1 => format!("Bottom({}) ", err.one().unwrap()).into(),
Self::Bottom(err) => format!("Botttom(\n{}) ", indent(&err.to_string())).into(),
Self::Bottom(err) => match err.one() {
Some(err) => format!("Bottom({err}) ").into(),
None => format!("Botttom(\n{}) ", indent(&err.to_string())).into(),
},
Self::Comment(c) => format!("--[{c}]--").into(),
Self::LambdaHead(arg) =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("\\{0b}.")))
@@ -295,16 +317,20 @@ impl<H: ExprRepr, X: ExtraTok> Format for Token<H, X> {
}
}
/// Find the location that best describes a sequence of tokens if the sequence
/// isn't empty
pub fn ttv_range<'a>(ttv: &[TokTree<impl ExprRepr + 'a, impl ExtraTok + 'a>]) -> Option<SrcRange> {
let range = ttv.first()?.sr.range.start..ttv.last().unwrap().sr.range.end;
Some(SrcRange { path: ttv.first().unwrap().sr.path(), range })
}
/// Pretty-print a token sequence
pub async fn ttv_fmt<'a: 'b, 'b>(
ttv: impl IntoIterator<Item = &'b TokTree<impl ExprRepr + 'a, impl ExtraTok + 'a>>,
c: &(impl FmtCtx + ?Sized),
) -> FmtUnit {
FmtUnit::sequence("", " ", "", None, join_all(ttv.into_iter().map(|t| t.print(c))).await)
FmtUnit::sequence("", " ", "", true, join_all(ttv.into_iter().map(|t| t.print(c))).await)
}
/// Indent a string by two spaces
pub fn indent(s: &str) -> String { s.replace("\n", "\n ") }