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:
@@ -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 ") }
|
||||
|
||||
Reference in New Issue
Block a user