use orchid_api_derive::Coding; use orchid_base::error::{OrcRes, mk_errv}; use orchid_base::format::FmtUnit; use orchid_base::parse::{name_char, name_start}; use orchid_extension::atom::Atomic; use orchid_extension::atom_thin::{ThinAtom, ThinVariant}; use orchid_extension::context::i; use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable}; use orchid_extension::tree::{GenTokTree, x_tok}; use crate::macros::mactree::{Ph, PhKind}; #[derive(Clone, Coding)] pub struct PhAtom(orchid_api::TStr, PhKind); impl PhAtom { pub async fn to_full(&self) -> Ph { Ph { kind: self.1, name: i().ex(self.0).await } } } impl Atomic for PhAtom { type Data = Self; type Variant = ThinVariant; } impl ThinAtom for PhAtom { async fn print(&self) -> FmtUnit { Ph { name: i().ex(self.0).await, kind: self.1 }.to_string().into() } } #[derive(Default)] pub struct PhLexer; impl Lexer for PhLexer { const CHAR_FILTER: &'static [std::ops::RangeInclusive] = &['$'..='$', '.'..='.']; async fn lex<'a>(line: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree)> { let (tail, name, phkind) = if let Some(tail) = line.strip_prefix("$") && tail.starts_with(name_start) { let name = tail.split_once(|c| !name_char(c)).map_or("", |(h, _)| h); let tail = tail.split_at(name.len()).1; (tail, name, PhKind::Scalar) } else { async fn name_and_prio<'a>( tail: &'a str, ctx: &'a LexContext<'a>, ) -> OrcRes<(&'a str, u8, &'a str)> { let name = tail.split_once(|c| !name_char(c)).map_or("", |(h, _)| h); let tail = tail.split_at(name.len()).1; let (prio, tail) = match tail.strip_prefix(":") { None => (0, tail), Some(tail) => { let prio = tail.split_once(|c: char| c.is_ascii_digit()).map_or("", |(h, _)| h); let tail = tail.split_at(prio.len()).1; if let Ok(prio_num) = prio.parse::() { (prio_num, tail) } else { return Err(mk_errv( i().i("Invalid priority, must be 0-255").await, format!("{prio} is not a valid placeholder priority"), [ctx.pos_lt(prio.len(), tail)], )); } }, }; Ok((name, prio, tail)) } if let Some(tail) = line.strip_prefix("..$") { let (name, priority, tail) = name_and_prio(tail, ctx).await?; (tail, name, PhKind::Vector { at_least_one: false, priority }) } else if let Some(tail) = line.strip_prefix("...$") { let (name, priority, tail) = name_and_prio(tail, ctx).await?; (tail, name, PhKind::Vector { at_least_one: true, priority }) } else { return Err(err_not_applicable().await); } }; let ph_atom = PhAtom(i().i::(name).await.to_api(), phkind); Ok((tail, x_tok(ph_atom).await.at(ctx.pos_tt(line, tail)))) } }