Files
MetaCrate/crates/libremetaverse-lsl-tools/src/generator.rs
Chili Palmer 2558e2f49f
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
Generate native LSL parser tables (#83)
2026-08-11 03:36:14 +00:00

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)
}
}