//! Compatibility adapters for the retired C# parser-generator object model. //! //! New code should use [`crate::Grammar`], [`crate::Dfa`], and the checked-in //! grammar generator. These adapters keep mapped callers functional without //! dynamic method pointers, reflection, or a CLR. #![allow(clippy::missing_errors_doc)] #![allow(clippy::must_use_candidate)] #![allow(clippy::needless_pass_by_value)] #![allow(clippy::too_many_arguments)] #![allow(non_snake_case)] use std::collections::{BTreeMap, BTreeSet, VecDeque}; use std::fmt; use std::io::Write; use std::sync::{Arc, Mutex, OnceLock}; use libremetaverse_types::compat::{Hashtable, Object, Utf16CodeUnit}; use regex::Regex as NativeRegex; use crate::{ CSymbol, CharacterMatcher, DfaAccept, DfaState, Error, ErrorHandler, Lexer, ObjectList, ParseState, Parser, ParserOldAction, ParserSimpleAction, PrecedencePrecType, Production, SourceLineInfo, SymbolSet, Transition, YyLexer, YyParser, }; #[derive(Clone)] struct Callable(Arc Result + Send + Sync>); impl fmt::Debug for Callable { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("Callable(..)") } } #[derive(Debug)] struct AsyncResult(T); type AsyncCallback = Box; macro_rules! delegate_one { ($name:ident, $arg:ty, $result:ty) => { #[derive(Clone, Debug)] pub struct $name(Callable<$arg, $result>); impl $name { pub fn from_fn(function: F) -> Self where F: Fn($arg) -> Result<$result, Error> + Send + Sync + 'static, { Self(Callable(Arc::new(function))) } pub fn new(object: Object, _method: isize) -> Result { object .downcast_ref::() .cloned() .ok_or(Error::Argument) } pub fn invoke(&self, argument: $arg) -> Result<$result, Error> { (self.0.0)(argument) } pub fn begin_invoke( &self, argument: $arg, callback: AsyncCallback, _state: Object, ) -> Result, Error> where $result: Send + Sync + 'static, { let result = AsyncResult(self.invoke(argument)?); callback(&result); Ok(Box::new(result)) } pub fn end_invoke( &self, result: Box, ) -> Result<$result, Error> where $result: Send + Sync + 'static, { result .downcast::>() .map(|value| value.0) .map_err(|_| Error::Argument) } } }; } delegate_one!(Builder, Transition, ()); delegate_one!(Func, Transition, SymbolSet); delegate_one!(Relation, Transition, Hashtable); delegate_one!(SCreator, Parser, Object); delegate_one!(TCreator, Lexer, Object); #[derive(Clone, Debug)] pub struct AddToFunc(Callable<(Transition, SymbolSet), ()>); impl AddToFunc { pub fn from_fn(function: F) -> Self where F: Fn(Transition, SymbolSet) -> Result<(), Error> + Send + Sync + 'static, { Self(Callable(Arc::new(move |(transition, symbols)| { function(transition, symbols) }))) } pub fn new(object: Object, _method: isize) -> Result { object .downcast_ref::() .cloned() .ok_or(Error::Argument) } pub fn invoke(&self, transition: Transition, symbols: SymbolSet) -> Result<(), Error> { (self.0.0)((transition, symbols)) } pub fn begin_invoke( &self, transition: Transition, symbols: SymbolSet, callback: AsyncCallback, _state: Object, ) -> Result, Error> { self.invoke(transition, symbols)?; let result = AsyncResult(()); callback(&result); Ok(Box::new(result)) } pub fn end_invoke(&self, result: Box) -> Result<(), Error> { result .downcast::>() .map(|_| ()) .map_err(|_| Error::Argument) } } #[derive(Clone, Debug)] pub struct ObjectListOListEnumerator { values: Vec, cursor: Arc>>, } impl ObjectListOListEnumerator { pub fn new(values: ObjectList) -> Result { Ok(Self { values: values.iter().cloned().collect(), cursor: Arc::new(Mutex::new(None)), }) } pub fn move_next(&self) -> Result { let mut cursor = self.cursor.lock().map_err(|_| Error::InvalidOperation)?; let next = cursor.map_or(0, |value| value.saturating_add(1)); if next < self.values.len() { *cursor = Some(next); Ok(true) } else { *cursor = Some(self.values.len()); Ok(false) } } pub fn reset(&self) -> Result<(), Error> { *self.cursor.lock().map_err(|_| Error::InvalidOperation)? = None; Ok(()) } pub fn current(&self) -> Object { self.cursor .lock() .ok() .and_then(|cursor| cursor.and_then(|index| self.values.get(index).cloned())) .unwrap_or(Object::Undefined) } } #[derive(Clone, Debug)] pub struct Path { pub valid: bool, states: Vec, spelling: Vec, } impl Path { pub fn new_with_parse_state_array(states: Vec) -> Result { if states.is_empty() { return Err(Error::Argument); } let spelling = states .iter() .take(states.len().saturating_sub(1)) .map(|state| state.m_accessing_symbol.clone()) .collect(); Ok(Self { valid: true, states, spelling, }) } pub fn new_with_parse_state_c_symbol_array( state: ParseState, symbols: Vec, ) -> Result { let mut states = vec![state.clone()]; let mut current = state; let mut valid = true; for symbol in &symbols { if let Ok(transition) = current.get_transition(symbol.clone()) { current = *transition.m_ps.clone(); states.push(current.clone()); } else { valid = false; states.push(current.clone()); } } Ok(Self { valid, states, spelling: symbols, }) } pub fn new_with_c_symbol_array(symbols: Vec) -> Result { if symbols.is_empty() { return Err(Error::Argument); } Self::new_with_parse_state_c_symbol_array(ParseState::compatibility_numbered(0), symbols) } pub fn spelling(&self) -> Vec { self.spelling.clone() } pub fn top(&self) -> ParseState { self.states .last() .cloned() .unwrap_or_else(|| ParseState::compatibility_numbered(0)) } } #[derive(Clone, Debug)] enum NfaMatcher { Exact(u16), Uppercase(u16), Predicate(Regex), } #[derive(Clone, Debug)] struct NfaArc { matcher: NfaMatcher, target: i32, } #[derive(Clone, Debug, Default)] struct NfaNodeData { arcs: Vec, epsilon: Vec, } #[derive(Clone, Debug, Default)] struct TokenGeneratorState { next_state: i32, nodes: BTreeMap, } #[derive(Clone, Debug)] pub struct TokensGen { pub defines: Hashtable, pub m_tokens: YyLexer, pub states: ObjectList, pub m_outname: String, state: Arc>, } impl TokensGen { pub fn new(error_handler: ErrorHandler) -> Result { Ok(Self { defines: Hashtable::default(), m_tokens: YyLexer::new(error_handler)?, states: ObjectList::default(), m_outname: "tokens".to_owned(), state: Arc::new(Mutex::new(TokenGeneratorState::default())), }) } pub fn fix_actions(&self, source: String) -> Result { Ok(source .replace("yybegin", "yym.yy_begin") .replace("yyl", &format!("(({})yym)", self.m_outname))) } pub fn new_state(&self) -> Result { let mut state = self.state.lock().map_err(|_| Error::InvalidOperation)?; state.next_state = state .next_state .checked_add(1) .ok_or(Error::IndexOutOfRange)?; let number = state.next_state; state.nodes.entry(number).or_default(); Ok(number) } fn add_arc(&self, from: i32, arc: NfaArc) -> Result<(), Error> { self.state .lock() .map_err(|_| Error::InvalidOperation)? .nodes .entry(from) .or_default() .arcs .push(arc); Ok(()) } fn add_epsilon(&self, from: i32, target: i32) -> Result<(), Error> { self.state .lock() .map_err(|_| Error::InvalidOperation)? .nodes .entry(from) .or_default() .epsilon .push(target); Ok(()) } } #[derive(Clone, Debug)] pub struct LNode { pub m_state: i32, pub m_tks: TokensGen, } impl LNode { pub fn new(tokens: TokensGen) -> Result { let state = tokens.new_state()?; Ok(Self { m_state: state, m_tks: tokens, }) } } #[derive(Clone, Debug)] pub struct NfaNode { pub m_arcs: ObjectList, pub m_eps: ObjectList, pub m_s_terminal: String, pub node: LNode, } impl NfaNode { pub fn new(tokens: TokensGen) -> Result { Ok(Self { m_arcs: ObjectList::default(), m_eps: ObjectList::default(), m_s_terminal: String::new(), node: LNode::new(tokens)?, }) } pub fn add_arc(&self, unit: Utf16CodeUnit, next: NfaNode) -> Result<(), Error> { self.node.m_tks.add_arc( self.node.m_state, NfaArc { matcher: NfaMatcher::Exact(unit.0), target: next.node.m_state, }, ) } pub fn add_u_arc(&self, unit: Utf16CodeUnit, next: NfaNode) -> Result<(), Error> { self.node.m_tks.add_arc( self.node.m_state, NfaArc { matcher: NfaMatcher::Uppercase(unit.0), target: next.node.m_state, }, ) } pub fn add_arc_ex(&self, regex: Regex, next: NfaNode) -> Result<(), Error> { self.node.m_tks.add_arc( self.node.m_state, NfaArc { matcher: NfaMatcher::Predicate(regex), target: next.node.m_state, }, ) } pub fn add_eps(&self, next: NfaNode) -> Result<(), Error> { self.node .m_tks .add_epsilon(self.node.m_state, next.node.m_state) } pub fn add_target(&self, unit: Utf16CodeUnit, mut next: crate::Dfa) -> Result<(), Error> { let state = self .node .m_tks .state .lock() .map_err(|_| Error::InvalidOperation)?; let Some(node) = state.nodes.get(&self.node.m_state) else { return Ok(()); }; for arc in &node.arcs { let matched = match &arc.matcher { NfaMatcher::Exact(expected) => *expected == unit.0, NfaMatcher::Uppercase(expected) => String::from_utf16(&[unit.0]) .ok() .and_then(|value| value.chars().next()) .is_some_and(|value| { value .to_uppercase() .next() .is_some_and(|upper| upper as u32 == u32::from(*expected)) }), NfaMatcher::Predicate(regex) => regex.match__with_char(unit)?, }; if matched { next.m_map .0 .insert(Object::Integer(arc.target), Object::Boolean(true)); } } Ok(()) } } #[derive(Clone, Debug)] pub struct Nfa { pub m_end: NfaNode, pub start: NfaNode, regex: Arc>>, } impl Nfa { pub fn new_with_tokens_gen(tokens: TokensGen) -> Result { Ok(Self { start: NfaNode::new(tokens.clone())?, m_end: NfaNode::new(tokens)?, regex: Arc::new(Mutex::new(None)), }) } pub fn new_with_tokens_gen_regex(tokens: TokensGen, regex: Regex) -> Result { let nfa = Self::new_with_tokens_gen(tokens)?; regex.build(nfa.clone())?; Ok(nfa) } fn set_regex(&self, regex: Regex) -> Result<(), Error> { *self.regex.lock().map_err(|_| Error::InvalidOperation)? = Some(regex.clone()); self.start.add_arc_ex(regex, self.m_end.clone()) } pub(crate) fn regex_source(&self) -> Result, Error> { Ok(self .regex .lock() .map_err(|_| Error::InvalidOperation)? .as_ref() .map(|regex| regex.source.clone())) } pub(crate) fn deterministic_states(&self, accept: DfaAccept) -> Result, Error> { const MAX_GENERATED_DFA_STATES: usize = 4_096; fn closure( nodes: &BTreeMap, seeds: impl IntoIterator, ) -> BTreeSet { let mut result = BTreeSet::new(); let mut pending = VecDeque::from_iter(seeds); while let Some(state) = pending.pop_front() { if !result.insert(state) { continue; } if let Some(node) = nodes.get(&state) { pending.extend(node.epsilon.iter().copied()); } } result } fn matches(matcher: &NfaMatcher, unit: u16) -> Result { Ok(match matcher { NfaMatcher::Exact(expected) => unit == *expected, NfaMatcher::Uppercase(expected) => String::from_utf16(&[unit]) .ok() .and_then(|value| value.chars().next()) .is_some_and(|value| { value .to_uppercase() .next() .is_some_and(|upper| upper as u32 == u32::from(*expected)) }), NfaMatcher::Predicate(regex) => regex.match__with_char(Utf16CodeUnit(unit))?, }) } let generator = self .start .node .m_tks .state .lock() .map_err(|_| Error::InvalidOperation)?; let start = closure(&generator.nodes, [self.start.node.m_state]); let mut indexes = BTreeMap::from([(start.clone(), 0usize)]); let mut pending = VecDeque::from([start]); let mut states = Vec::new(); while let Some(subset) = pending.pop_front() { let mut targets = BTreeMap::, BTreeSet>::new(); for unit in 0..=u16::MAX { let mut destination = BTreeSet::new(); for state in &subset { if let Some(node) = generator.nodes.get(state) { for arc in &node.arcs { if matches(&arc.matcher, unit)? { destination.insert(arc.target); } } } } if !destination.is_empty() { targets .entry(closure(&generator.nodes, destination)) .or_default() .insert(unit); } } let mut transitions = Vec::new(); for (target, units) in targets { let index = if let Some(index) = indexes.get(&target).copied() { index } else { if indexes.len() >= MAX_GENERATED_DFA_STATES { return Err(Error::IndexOutOfRange); } let index = indexes.len(); indexes.insert(target.clone(), index); pending.push_back(target); index }; transitions.push((CharacterMatcher::Set(units), index)); } states.push(DfaState { transitions, accept: subset .contains(&self.m_end.node.m_state) .then(|| accept.clone()), }); } if states.iter().all(|state| state.accept.is_none()) { return Err(Error::Argument); } Ok(states) } } #[derive(Clone)] pub struct Serialiser { input: Option>>, output: Option>>>, position: Arc>, column: Arc>, } impl fmt::Debug for Serialiser { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter .debug_struct("Serialiser") .field("encode", &self.encode()) .field("position", &self.position.lock().map_or(0, |value| *value)) .finish_non_exhaustive() } } impl Serialiser { pub fn new_with_text_writer(output: Box) -> Result { Ok(Self { input: None, output: Some(Arc::new(Mutex::new(output))), position: Arc::new(Mutex::new(0)), column: Arc::new(Mutex::new(0)), }) } pub fn new_with_int32_array(input: Vec) -> Result { Ok(Self { input: Some(Arc::new(input)), output: None, position: Arc::new(Mutex::new(0)), column: Arc::new(Mutex::new(0)), }) } pub const fn encode(&self) -> bool { self.output.is_some() } pub fn read(&self) -> Result { let input = self.input.as_ref().ok_or(Error::InvalidOperation)?; let mut position = self.position.lock().map_err(|_| Error::InvalidOperation)?; let value = *input.get(*position).ok_or(Error::IndexOutOfRange)?; *position += 1; Ok(value) } pub fn write(&self, value: i32) -> Result<(), Error> { let output = self.output.as_ref().ok_or(Error::InvalidOperation)?; let mut output = output.lock().map_err(|_| Error::InvalidOperation)?; let mut column = self.column.lock().map_err(|_| Error::InvalidOperation)?; if *column == 5 { output .write_all(b"\n") .map_err(|_| Error::InvalidOperation)?; *column = 0; } write!(output, "{value},").map_err(|_| Error::InvalidOperation)?; *column += 1; Ok(()) } pub fn serialise(&self, object: Object) -> Result<(), Error> { match object { Object::Undefined => self.write(0), Object::Integer(value) => { self.write(1)?; self.write(value) } Object::Boolean(value) => { self.write(2)?; self.write(i32::from(value)) } Object::String(value) => { self.write(4)?; let bytes = value .encode_utf16() .flat_map(u16::to_le_bytes) .collect::>(); self.write(i32::try_from(bytes.len()).map_err(|_| Error::IndexOutOfRange)?)?; for byte in bytes { self.write(i32::from(byte))?; } Ok(()) } Object::Array(values) => { self.write(24)?; self.write(i32::try_from(values.len()).map_err(|_| Error::IndexOutOfRange)?)?; for value in values { self.serialise(value)?; } Ok(()) } Object::Map(values) => { self.write(25)?; self.write(i32::try_from(values.len()).map_err(|_| Error::IndexOutOfRange)?)?; let mut values = values.into_iter().collect::>(); values.sort_by(|left, right| left.0.cmp(&right.0)); for (key, value) in values { self.serialise(Object::String(key))?; self.serialise(value)?; } Ok(()) } _ => Err(Error::Argument), } } pub fn deserialise(&self) -> Result { match self.read()? { 0 => Ok(Object::Undefined), 1 => Ok(Object::Integer(self.read()?)), 2 => Ok(Object::Boolean(self.read()? != 0)), 4 => { let count = usize::try_from(self.read()?).map_err(|_| Error::Argument)?; if count % 2 != 0 { return Err(Error::Parse { position: count, context: "odd UTF-16 byte count", }); } let mut bytes = Vec::with_capacity(count); for _ in 0..count { bytes.push(u8::try_from(self.read()?).map_err(|_| Error::Argument)?); } let units = bytes .chunks_exact(2) .map(|pair| u16::from_le_bytes([pair[0], pair[1]])) .collect::>(); String::from_utf16(&units) .map(Object::String) .map_err(|_| Error::Parse { position: 0, context: "invalid UTF-16 string", }) } 24 => { let count = usize::try_from(self.read()?).map_err(|_| Error::Argument)?; let mut values = Vec::with_capacity(count); for _ in 0..count { values.push(self.deserialise()?); } Ok(Object::Array(values)) } 25 => { let count = usize::try_from(self.read()?).map_err(|_| Error::Argument)?; let mut values = std::collections::HashMap::with_capacity(count); for _ in 0..count { let Object::String(key) = self.deserialise()? else { return Err(Error::Argument); }; values.insert(key, self.deserialise()?); } Ok(Object::Map(values)) } _ => Err(Error::Parse { position: self.position.lock().map_or(0, |value| *value), context: "unknown serialization tag", }), } } pub fn version_check(&self) -> Result<(), Error> { if self.encode() { self.serialise(Object::String("4.5".to_owned())) } else if self.deserialise()? == Object::String("4.5".to_owned()) { Ok(()) } else { Err(Error::Parse { position: 0, context: "expected serialization version 4.5", }) } } } #[derive(Clone)] pub struct GenBase { pub last_symbol: i32, pub erh: ErrorHandler, pub m_out_file: Arc>>, pub m_outname: String, pub m_prod: Production, } impl fmt::Debug for GenBase { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter .debug_struct("GenBase") .field("last_symbol", &self.last_symbol) .field("outname", &self.m_outname) .field("production", &self.m_prod) .finish_non_exhaustive() } } impl GenBase { pub fn new(error_handler: ErrorHandler, output: Box) -> Result { let symbols = SymbolsGen::new(error_handler.clone())?; Ok(Self { last_symbol: 2, erh: error_handler, m_out_file: Arc::new(Mutex::new(output)), m_outname: "tokens".to_owned(), m_prod: Production::new_with_symbols_gen(symbols)?, }) } pub fn white(&self, buffer: String, offset: &mut i32, max: i32) -> Result { scan_while(&buffer, offset, max, |value| matches!(value, b' ' | b'\t')) } pub fn non_white(&self, buffer: String, offset: &mut i32, max: i32) -> Result { scan_while(&buffer, offset, max, |value| !matches!(value, b' ' | b'\t')) } pub fn source_line_info(&self, position: i32) -> Result { SourceLineInfo::new_with_int32(position) } pub fn line(&self, position: i32) -> Result { Ok(self.source_line_info(position)?.line_number) } pub fn position(&self, position: i32) -> Result { Ok(self.source_line_info(position)?.raw_char_position) } pub fn saypos(&self, position: i32) -> Result { Ok(self.source_line_info(position)?.to_string()) } pub fn error(&self, number: i32, position: i32, message: String) -> Result<(), Error> { let exception = crate::CSToolsFatalException::new_with_int32_source_line_info_string_string( number, self.source_line_info(position)?, String::new(), message, )?; self.erh.clone().error(exception.0) } pub fn emit_class_defin( &self, buffer: String, offset: &mut i32, max: i32, _input: crate::CsReader, default_base: String, base: &mut String, name: &mut String, lexer: bool, ) -> Result { if lexer { self.non_white(buffer.clone(), offset, max)?; } self.white(buffer.clone(), offset, max)?; let bytes = buffer.as_bytes(); let limit = usize::try_from(max) .map_err(|_| Error::Argument)? .min(bytes.len()); let mut index = usize::try_from(*offset).map_err(|_| Error::Argument)?; let start = index; while index < limit && !matches!(bytes[index], b'{' | b':' | b';' | b' ' | b'\t' | b'\n') { index += 1; } buffer .get(start..index) .ok_or(Error::Argument)? .clone_into(name); if name.is_empty() { return Err(Error::Parse { position: start, context: "class name is missing", }); } *base = default_base; while index < limit && matches!(bytes[index], b' ' | b'\t') { index += 1; } if bytes.get(index) == Some(&b':') { index += 1; while index < limit && matches!(bytes[index], b' ' | b'\t') { index += 1; } let base_start = index; while index < limit && !matches!(bytes[index], b'{' | b';' | b' ' | b'\t' | b'\n') { index += 1; } buffer .get(base_start..index) .ok_or(Error::Argument)? .clone_into(base); } *offset = i32::try_from(index).map_err(|_| Error::IndexOutOfRange)?; let number = self .last_symbol .checked_add(1) .ok_or(Error::IndexOutOfRange)?; let mut output = self .m_out_file .lock() .map_err(|_| Error::InvalidOperation)?; writeln!( output, "// generated {name}={number}\npub struct {name}; // base: {base}" ) .map_err(|_| Error::InvalidOperation)?; Ok(number) } } fn scan_while(buffer: &str, offset: &mut i32, max: i32, predicate: F) -> Result where F: Fn(u8) -> bool, { let bytes = buffer.as_bytes(); let limit = usize::try_from(max) .map_err(|_| Error::Argument)? .min(bytes.len()); let mut index = usize::try_from(*offset).map_err(|_| Error::Argument)?; if index > limit { return Err(Error::IndexOutOfRange); } while index < limit && predicate(bytes[index]) { index += 1; } *offset = i32::try_from(index).map_err(|_| Error::IndexOutOfRange)?; Ok(index < limit) } #[derive(Clone, Debug, Default)] struct SymbolsGeneratorState { associations: Vec<(PrecedencePrecType, i32)>, class_definitions: Vec, directives: Vec, actions: Vec, } #[derive(Clone, Debug)] pub struct SymbolType { pub name: String, pub defined: bool, registry: Arc>>, } impl SymbolType { fn root() -> Self { Self { name: String::new(), defined: false, registry: Arc::new(Mutex::new(BTreeMap::new())), } } pub fn new_with_symbols_gen_string(symbols: SymbolsGen, name: String) -> Result { Self::new_with_symbols_gen_string_boolean(symbols, name, false) } pub fn new_with_symbols_gen_string_boolean( symbols: SymbolsGen, name: String, defined: bool, ) -> Result { if name.is_empty() { return Err(Error::Argument); } let value = Self { name: name.clone(), defined, registry: symbols.stypes.registry.clone(), }; value .registry .lock() .map_err(|_| Error::InvalidOperation)? .insert(name, value.clone()); Ok(value) } pub fn find(&self, name: String) -> Result { self.registry .lock() .map_err(|_| Error::InvalidOperation)? .get(&name) .cloned() .ok_or(Error::InvalidOperation) } } #[derive(Clone, Debug)] pub struct SymbolsGen { pub action: i32, pub lahead: SymbolSet, pub m_lalr_parser: bool, pub m_lexer: Lexer, pub m_symbols: YyParser, pub m_trans: i32, pub pno: i32, pub prods: ObjectList, pub stypes: SymbolType, state: Arc>, } impl SymbolsGen { pub fn new(error_handler: ErrorHandler) -> Result { let lexer = Lexer::new(YyLexer::new(error_handler.clone())?)?; let mut parser = YyParser::new()?; parser.erh = error_handler; Ok(Self { action: 0, lahead: SymbolSet::compatibility_empty(), m_lalr_parser: true, m_lexer: lexer, m_symbols: parser, m_trans: 0, pno: 0, prods: ObjectList::default(), stypes: SymbolType::root(), state: Arc::new(Mutex::new(SymbolsGeneratorState::default())), }) } pub fn assoc_type(&self, kind: PrecedencePrecType, number: i32) -> Result<(), Error> { if number < 0 { return Err(Error::Argument); } self.state .lock() .map_err(|_| Error::InvalidOperation)? .associations .push((kind, number)); Ok(()) } pub fn class_definition(&self, source: String) -> Result<(), Error> { if source.trim().is_empty() { return Err(Error::Argument); } self.state .lock() .map_err(|_| Error::InvalidOperation)? .class_definitions .push(source); Ok(()) } pub fn copy_segment(&self) -> Result<(), Error> { let segment = self.m_lexer.yytext.clone(); self.state .lock() .map_err(|_| Error::InvalidOperation)? .directives .push(segment); Ok(()) } pub fn declare(&self) -> Result<(), Error> { let declaration = self.m_lexer.yytext.trim().to_owned(); if declaration.is_empty() { return Err(Error::Argument); } self.state .lock() .map_err(|_| Error::InvalidOperation)? .directives .push(declaration); Ok(()) } pub fn find(&self, symbol: CSymbol) -> Result { Ok(self .m_symbols .symbols .0 .values() .any(|value| matches!(value, Object::Integer(number) if *number == symbol.m_yynum))) } pub fn old_action(&self, action: ParserOldAction) -> Result<(), Error> { self.state .lock() .map_err(|_| Error::InvalidOperation)? .actions .push(format!("old:{}", action.act_num()?)); Ok(()) } pub fn simple_action(&self, action: ParserSimpleAction) -> Result<(), Error> { self.state .lock() .map_err(|_| Error::InvalidOperation)? .actions .push(action.type_str()?); Ok(()) } pub fn parser_directive(&self) -> Result<(), Error> { self.state .lock() .map_err(|_| Error::InvalidOperation)? .directives .push("parser".to_owned()); Ok(()) } pub fn set_namespace(&self) -> Result<(), Error> { let namespace = self.m_lexer.yytext.trim(); if namespace.is_empty() { return Err(Error::Argument); } self.state .lock() .map_err(|_| Error::InvalidOperation)? .directives .push(format!("namespace:{namespace}")); Ok(()) } pub fn set_start_symbol(&self) -> Result<(), Error> { let start = self.m_lexer.yytext.trim(); if start.is_empty() { return Err(Error::Argument); } self.state .lock() .map_err(|_| Error::InvalidOperation)? .directives .push(format!("start:{start}")); Ok(()) } } #[derive(Clone, Debug, Eq, PartialEq)] pub struct TokClassDef { pub m_implement: String, pub m_initialisation: String, pub m_name: String, pub m_ref_token: String, pub m_yynum: i32, } impl TokClassDef { pub fn new(generator: GenBase, name: String, base: String) -> Result { if name.is_empty() || base.is_empty() { return Err(Error::Argument); } Ok(Self { m_implement: String::new(), m_initialisation: String::new(), m_name: name, m_ref_token: base, m_yynum: generator .last_symbol .checked_add(1) .ok_or(Error::IndexOutOfRange)?, }) } pub fn serialise(object: Object, serialiser: Serialiser) -> Result { if serialiser.encode() { let value = object.downcast_ref::().ok_or(Error::Argument)?; serialiser.serialise(Object::String(value.m_name.clone()))?; serialiser.serialise(Object::Integer(value.m_yynum))?; Ok(Object::Undefined) } else { let Object::String(name) = serialiser.deserialise()? else { return Err(Error::Argument); }; let Object::Integer(number) = serialiser.deserialise()? else { return Err(Error::Argument); }; Ok(Object::opaque(Self { m_name: name, m_yynum: number, m_implement: String::new(), m_initialisation: String::new(), m_ref_token: "TOKEN".to_owned(), })) } } } fn symbol_factories() -> &'static Mutex> { static FACTORIES: OnceLock>> = OnceLock::new(); FACTORIES.get_or_init(|| Mutex::new(BTreeMap::new())) } fn token_factories() -> &'static Mutex> { static FACTORIES: OnceLock>> = OnceLock::new(); FACTORIES.get_or_init(|| Mutex::new(BTreeMap::new())) } #[derive(Clone, Debug)] pub struct Sfactory { name: String, } impl Sfactory { pub fn new(_symbols: YyParser, class_name: String, creator: SCreator) -> Result { if class_name.is_empty() { return Err(Error::Argument); } symbol_factories() .lock() .map_err(|_| Error::InvalidOperation)? .insert(class_name.clone(), creator); Ok(Self { name: class_name }) } pub fn create(class_name: String, parser: Parser) -> Result { let factories = symbol_factories() .lock() .map_err(|_| Error::InvalidOperation)?; let mut candidate = class_name.as_str(); loop { if let Some(factory) = factories.get(candidate) { return factory.invoke(parser); } let Some((base, _)) = candidate.rsplit_once('_') else { break; }; candidate = base; } Err(Error::InvalidOperation) } pub fn name(&self) -> &str { &self.name } } #[derive(Clone, Debug)] pub struct Tfactory { name: String, } impl Tfactory { pub fn new(_tokens: YyLexer, class_name: String, creator: TCreator) -> Result { if class_name.is_empty() { return Err(Error::Argument); } token_factories() .lock() .map_err(|_| Error::InvalidOperation)? .insert(class_name.clone(), creator); Ok(Self { name: class_name }) } pub fn create(class_name: String, lexer: Lexer) -> Result { let factories = token_factories() .lock() .map_err(|_| Error::InvalidOperation)?; let mut candidate = class_name.as_str(); loop { if let Some(factory) = factories.get(candidate) { return factory.invoke(lexer); } let Some((base, _)) = candidate.rsplit_once('_') else { break; }; candidate = base; } Err(Error::InvalidOperation) } pub fn name(&self) -> &str { &self.name } } /// Portable regular-expression adapter. Matching is anchored at the requested /// input position, as in the original lexer generator. #[derive(Clone)] pub struct Regex { source: String, compiled: NativeRegex, pub m_sub: Option>, } impl fmt::Debug for Regex { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.debug_tuple("Regex").field(&self.source).finish() } } impl Regex { pub fn new(_tokens: TokensGen, position: i32, source: String) -> Result { const MAX_REGEX_UNITS: usize = 1_048_576; if position < 0 { return Err(Error::Argument); } if source.encode_utf16().count() > MAX_REGEX_UNITS { return Err(Error::IndexOutOfRange); } // `position` locates this expression in the generator input for // diagnostics; it is not an offset into the expression itself. let compiled = NativeRegex::new(&format!("^(?:{source})")).map_err(|_| Error::Parse { position: usize::try_from(position).unwrap_or(0), context: "invalid lexer regular expression", })?; Ok(Self { source, compiled, m_sub: None, }) } pub fn build(&self, nfa: Nfa) -> Result<(), Error> { nfa.set_regex(self.clone()) } pub fn match__with_char(&self, unit: Utf16CodeUnit) -> Result { // `System.Char` can hold an unpaired surrogate. Rust strings cannot, // and no valid Unicode regex atom should match one, so it is a clean // mismatch rather than a malformed-regex failure. let Ok(text) = String::from_utf16(&[unit.0]) else { return Ok(false); }; Ok(self .compiled .find(&text) .is_some_and(|value| value.end() == text.len())) } pub fn match__with_string(&self, value: String) -> Result { self.match__with_string_int32_int32( value.clone(), 0, i32::try_from(value.encode_utf16().count()).map_err(|_| Error::IndexOutOfRange)?, ) } pub fn match__with_string_int32_int32( &self, value: String, position: i32, max: i32, ) -> Result { let units = value.encode_utf16().collect::>(); let position = usize::try_from(position).map_err(|_| Error::Argument)?; let max = usize::try_from(max).map_err(|_| Error::Argument)?; if position > units.len() || position > max { return Ok(-1); } let end = max.min(units.len()); let text = String::from_utf16(&units[position..end]).map_err(|_| Error::Argument)?; let Some(found) = self.compiled.find(&text) else { return Ok(-1); }; i32::try_from(text[..found.end()].encode_utf16().count()) .map_err(|_| Error::IndexOutOfRange) } pub fn print(&self, mut output: Box) -> Result<(), Error> { output .write_all(self.source.as_bytes()) .map_err(|_| Error::InvalidOperation) } }