Some checks failed
Native code generation / deterministic (push) Failing after 2m36s
Imaging and meshing gate / native (push) Successful in 5m29s
JPEG 2000 feature / linux (push) Successful in 2m46s
Native Rust workspace compile / compile (push) Failing after 1m58s
Skia feature / linux (push) Successful in 31m14s
1338 lines
41 KiB
Rust
1338 lines
41 KiB
Rust
//! 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<A, R>(Arc<dyn Fn(A) -> Result<R, Error> + Send + Sync>);
|
|
|
|
impl<A, R> fmt::Debug for Callable<A, R> {
|
|
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
formatter.write_str("Callable(..)")
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct AsyncResult<T>(T);
|
|
|
|
type AsyncCallback = Box<dyn Fn(&dyn std::any::Any) + Send + Sync>;
|
|
|
|
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<F>(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<Self, Error> {
|
|
object
|
|
.downcast_ref::<Self>()
|
|
.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<Box<dyn std::any::Any + Send + Sync>, 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<dyn std::any::Any + Send + Sync>,
|
|
) -> Result<$result, Error>
|
|
where
|
|
$result: Send + Sync + 'static,
|
|
{
|
|
result
|
|
.downcast::<AsyncResult<$result>>()
|
|
.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<F>(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<Self, Error> {
|
|
object
|
|
.downcast_ref::<Self>()
|
|
.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<Box<dyn std::any::Any + Send + Sync>, Error> {
|
|
self.invoke(transition, symbols)?;
|
|
let result = AsyncResult(());
|
|
callback(&result);
|
|
Ok(Box::new(result))
|
|
}
|
|
|
|
pub fn end_invoke(&self, result: Box<dyn std::any::Any + Send + Sync>) -> Result<(), Error> {
|
|
result
|
|
.downcast::<AsyncResult<()>>()
|
|
.map(|_| ())
|
|
.map_err(|_| Error::Argument)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct ObjectListOListEnumerator {
|
|
values: Vec<Object>,
|
|
cursor: Arc<Mutex<Option<usize>>>,
|
|
}
|
|
|
|
impl ObjectListOListEnumerator {
|
|
pub fn new(values: ObjectList) -> Result<Self, Error> {
|
|
Ok(Self {
|
|
values: values.iter().cloned().collect(),
|
|
cursor: Arc::new(Mutex::new(None)),
|
|
})
|
|
}
|
|
|
|
pub fn move_next(&self) -> Result<bool, Error> {
|
|
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<ParseState>,
|
|
spelling: Vec<CSymbol>,
|
|
}
|
|
|
|
impl Path {
|
|
pub fn new_with_parse_state_array(states: Vec<ParseState>) -> Result<Self, Error> {
|
|
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<CSymbol>,
|
|
) -> Result<Self, Error> {
|
|
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<CSymbol>) -> Result<Self, Error> {
|
|
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<CSymbol> {
|
|
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<NfaArc>,
|
|
epsilon: Vec<i32>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default)]
|
|
struct TokenGeneratorState {
|
|
next_state: i32,
|
|
nodes: BTreeMap<i32, NfaNodeData>,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct TokensGen {
|
|
pub defines: Hashtable,
|
|
pub m_tokens: YyLexer,
|
|
pub states: ObjectList,
|
|
pub m_outname: String,
|
|
state: Arc<Mutex<TokenGeneratorState>>,
|
|
}
|
|
|
|
impl TokensGen {
|
|
pub fn new(error_handler: ErrorHandler) -> Result<Self, Error> {
|
|
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<String, Error> {
|
|
Ok(source
|
|
.replace("yybegin", "yym.yy_begin")
|
|
.replace("yyl", &format!("(({})yym)", self.m_outname)))
|
|
}
|
|
|
|
pub fn new_state(&self) -> Result<i32, Error> {
|
|
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<Self, Error> {
|
|
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<Self, Error> {
|
|
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<Mutex<Option<Regex>>>,
|
|
}
|
|
|
|
impl Nfa {
|
|
pub fn new_with_tokens_gen(tokens: TokensGen) -> Result<Self, Error> {
|
|
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<Self, Error> {
|
|
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<Option<String>, 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<Vec<DfaState>, Error> {
|
|
const MAX_GENERATED_DFA_STATES: usize = 4_096;
|
|
|
|
fn closure(
|
|
nodes: &BTreeMap<i32, NfaNodeData>,
|
|
seeds: impl IntoIterator<Item = i32>,
|
|
) -> BTreeSet<i32> {
|
|
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<bool, Error> {
|
|
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<i32>, BTreeSet<u16>>::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<Arc<Vec<i32>>>,
|
|
output: Option<Arc<Mutex<Box<dyn Write + Send>>>>,
|
|
position: Arc<Mutex<usize>>,
|
|
column: Arc<Mutex<usize>>,
|
|
}
|
|
|
|
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<dyn Write + Send>) -> Result<Self, Error> {
|
|
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<i32>) -> Result<Self, Error> {
|
|
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<i32, Error> {
|
|
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::<Vec<_>>();
|
|
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::<Vec<_>>();
|
|
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<Object, Error> {
|
|
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::<Vec<_>>();
|
|
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<Mutex<Box<dyn Write + Send>>>,
|
|
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<dyn Write + Send>) -> Result<Self, Error> {
|
|
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<bool, Error> {
|
|
scan_while(&buffer, offset, max, |value| matches!(value, b' ' | b'\t'))
|
|
}
|
|
|
|
pub fn non_white(&self, buffer: String, offset: &mut i32, max: i32) -> Result<bool, Error> {
|
|
scan_while(&buffer, offset, max, |value| !matches!(value, b' ' | b'\t'))
|
|
}
|
|
|
|
pub fn source_line_info(&self, position: i32) -> Result<SourceLineInfo, Error> {
|
|
SourceLineInfo::new_with_int32(position)
|
|
}
|
|
|
|
pub fn line(&self, position: i32) -> Result<i32, Error> {
|
|
Ok(self.source_line_info(position)?.line_number)
|
|
}
|
|
|
|
pub fn position(&self, position: i32) -> Result<i32, Error> {
|
|
Ok(self.source_line_info(position)?.raw_char_position)
|
|
}
|
|
|
|
pub fn saypos(&self, position: i32) -> Result<String, Error> {
|
|
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<i32, Error> {
|
|
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<F>(buffer: &str, offset: &mut i32, max: i32, predicate: F) -> Result<bool, Error>
|
|
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<String>,
|
|
directives: Vec<String>,
|
|
actions: Vec<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct SymbolType {
|
|
pub name: String,
|
|
pub defined: bool,
|
|
registry: Arc<Mutex<BTreeMap<String, SymbolType>>>,
|
|
}
|
|
|
|
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, Error> {
|
|
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<Self, Error> {
|
|
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, Error> {
|
|
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<Mutex<SymbolsGeneratorState>>,
|
|
}
|
|
|
|
impl SymbolsGen {
|
|
pub fn new(error_handler: ErrorHandler) -> Result<Self, Error> {
|
|
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<bool, Error> {
|
|
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<Self, Error> {
|
|
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<Object, Error> {
|
|
if serialiser.encode() {
|
|
let value = object.downcast_ref::<Self>().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<BTreeMap<String, SCreator>> {
|
|
static FACTORIES: OnceLock<Mutex<BTreeMap<String, SCreator>>> = OnceLock::new();
|
|
FACTORIES.get_or_init(|| Mutex::new(BTreeMap::new()))
|
|
}
|
|
|
|
fn token_factories() -> &'static Mutex<BTreeMap<String, TCreator>> {
|
|
static FACTORIES: OnceLock<Mutex<BTreeMap<String, TCreator>>> = 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<Self, Error> {
|
|
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<Object, Error> {
|
|
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<Self, Error> {
|
|
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<Object, Error> {
|
|
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<Box<Regex>>,
|
|
}
|
|
|
|
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<Self, Error> {
|
|
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<bool, Error> {
|
|
// `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<i32, Error> {
|
|
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<i32, Error> {
|
|
let units = value.encode_utf16().collect::<Vec<_>>();
|
|
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<dyn Write + Send>) -> Result<(), Error> {
|
|
output
|
|
.write_all(self.source.as_bytes())
|
|
.map_err(|_| Error::InvalidOperation)
|
|
}
|
|
}
|