forked from Orchid/orchid
103 lines
3.3 KiB
Rust
103 lines
3.3 KiB
Rust
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<String> {
|
|
i.i("An error cascading from a recursive call").await
|
|
}
|
|
pub async fn ekey_not_applicable(i: &Interner) -> Tok<String> {
|
|
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<String>,
|
|
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<u32, Error: fmt::Debug>, 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<char>];
|
|
fn lex<'a>(
|
|
tail: &'a str,
|
|
ctx: &'a LexContext<'a>,
|
|
) -> impl Future<Output = OrcRes<(&'a str, GenTokTree)>>;
|
|
}
|
|
|
|
pub trait DynLexer: Send + Sync + 'static {
|
|
fn char_filter(&self) -> &'static [RangeInclusive<char>];
|
|
fn lex<'a>(
|
|
&self,
|
|
tail: &'a str,
|
|
ctx: &'a LexContext<'a>,
|
|
) -> LocalBoxFuture<'a, OrcRes<(&'a str, GenTokTree)>>;
|
|
}
|
|
|
|
impl<T: Lexer> DynLexer for T {
|
|
fn char_filter(&self) -> &'static [RangeInclusive<char>] { 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;
|