use std::fmt; use std::future::Future; use std::ops::RangeInclusive; use futures::FutureExt; use futures::future::LocalBoxFuture; use orchid_base::error::{OrcErrv, OrcRes, Reporter, mk_errv}; use orchid_base::interner::{Interner, Tok}; use orchid_base::location::{Pos, SrcRange}; use orchid_base::name::Sym; use orchid_base::parse::ParseCtx; use orchid_base::reqnot::Requester; use crate::api; use crate::parser::PTokTree; use crate::system::SysCtx; use crate::tree::GenTokTree; pub async fn ekey_cascade(i: &Interner) -> Tok { i.i("An error cascading from a recursive call").await } pub async fn ekey_not_applicable(i: &Interner) -> Tok { i.i("Pseudo-error to communicate that the current branch in a dispatch doesn't apply").await } const MSG_INTERNAL_ERROR: &str = "This error is a sentinel for the extension library.\ it should not be emitted by the extension."; pub async fn err_cascade(i: &Interner) -> OrcErrv { mk_errv(ekey_cascade(i).await, MSG_INTERNAL_ERROR, [Pos::None]) } pub async fn err_not_applicable(i: &Interner) -> OrcErrv { mk_errv(ekey_not_applicable(i).await, MSG_INTERNAL_ERROR, [Pos::None]) } pub struct LexContext<'a> { pub ctx: SysCtx, pub text: &'a Tok, pub id: api::ParsId, pub pos: u32, pub(crate) src: Sym, pub(crate) rep: &'a Reporter, } impl<'a> LexContext<'a> { pub fn src(&self) -> &Sym { &self.src } /// This function returns [PTokTree] because it can never return /// [orchid_base::tree::Token::NewExpr]. You can use /// [crate::parser::p_tree2gen] to convert this to [crate::tree::GenTokTree] /// for embedding in the return value. pub async fn recurse(&self, tail: &'a str) -> OrcRes<(&'a str, PTokTree)> { let start = self.pos(tail); let Some(lx) = self.ctx.reqnot().request(api::SubLex { pos: start, id: self.id }).await else { return Err(err_cascade(self.ctx.i()).await); }; let tree = PTokTree::from_api(&lx.tree, &mut self.ctx.clone(), &mut (), &self.src, self.ctx.i()).await; Ok((&self.text[lx.pos as usize..], tree)) } pub fn pos(&self, tail: &'a str) -> u32 { (self.text.len() - tail.len()) as u32 } pub fn pos_tt(&self, tail_with: &'a str, tail_without: &'a str) -> SrcRange { SrcRange::new(self.pos(tail_with)..self.pos(tail_without), &self.src) } pub fn pos_lt(&self, len: impl TryInto, tail: &'a str) -> SrcRange { SrcRange::new(self.pos(tail) - len.try_into().unwrap()..self.pos(tail), &self.src) } } impl ParseCtx for LexContext<'_> { fn i(&self) -> &Interner { self.ctx.i() } fn rep(&self) -> &Reporter { self.rep } } pub trait Lexer: Send + Sync + Sized + Default + 'static { const CHAR_FILTER: &'static [RangeInclusive]; fn lex<'a>( tail: &'a str, ctx: &'a LexContext<'a>, ) -> impl Future>; } pub trait DynLexer: Send + Sync + 'static { fn char_filter(&self) -> &'static [RangeInclusive]; fn lex<'a>( &self, tail: &'a str, ctx: &'a LexContext<'a>, ) -> LocalBoxFuture<'a, OrcRes<(&'a str, GenTokTree)>>; } impl DynLexer for T { fn char_filter(&self) -> &'static [RangeInclusive] { T::CHAR_FILTER } fn lex<'a>( &self, tail: &'a str, ctx: &'a LexContext<'a>, ) -> LocalBoxFuture<'a, OrcRes<(&'a str, GenTokTree)>> { T::lex(tail, ctx).boxed_local() } } pub type LexerObj = &'static dyn DynLexer;