use std::collections::HashSet;
use super::element::{GrammarElement, GrammarRule, GrammarStack, GreType, RulePos};
use super::error::GrammarError;
use super::lazy::{LazyState, LazyTriggers, TriggerStep};
use super::parser::{parse_with_vocab, GrammarVocab, ParsedGrammar};
use super::utf8::{decode_piece, PartialUtf8};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Grammar {
rules: Vec<GrammarRule>,
stacks: Vec<GrammarStack>,
partial_utf8: PartialUtf8,
lazy: Option<LazyState>,
}
impl Grammar {
pub fn from_str_with_root(src: &str, root_name: &str) -> Result<Self, GrammarError> {
Self::from_str_with_vocab(src, root_name, None)
}
pub fn from_str_with_vocab(
src: &str,
root_name: &str,
vocab: Option<&dyn GrammarVocab>,
) -> Result<Self, GrammarError> {
let parsed = parse_with_vocab(src, vocab)?;
Self::from_parsed(&parsed, root_name)
}
pub fn from_parsed(parsed: &ParsedGrammar, root_name: &str) -> Result<Self, GrammarError> {
let start = parsed
.symbol_id(root_name)
.ok_or_else(|| GrammarError::MissingRoot {
name: root_name.to_string(),
})?;
Self::from_rules(parsed.rules.clone(), start, |id| {
parsed.symbol_name(id).map(str::to_string)
})
}
pub fn from_rules(
rules: Vec<GrammarRule>,
start_rule_index: u32,
name_of: impl Fn(u32) -> Option<String>,
) -> Result<Self, GrammarError> {
let n_rules = rules.len();
for (i, rule) in rules.iter().enumerate() {
if rule.last().map(|e| e.gtype) != Some(GreType::End) {
return Err(GrammarError::UndefinedRule {
name: name_of(i as u32).unwrap_or_else(|| "<unnamed>".into()),
rule_id: i as u32,
});
}
}
for rule in rules.iter() {
for elem in rule {
if elem.gtype == GreType::RuleRef {
let idx = elem.value as usize;
if idx >= n_rules || rules[idx].is_empty() {
return Err(GrammarError::UndefinedRule {
name: name_of(elem.value).unwrap_or_else(|| "<unnamed>".into()),
rule_id: elem.value,
});
}
}
}
}
if start_rule_index as usize >= n_rules {
return Err(GrammarError::MissingRoot {
name: name_of(start_rule_index).unwrap_or_else(|| "root".into()),
});
}
detect_left_recursion_all(&rules, &name_of)?;
let mut stacks: Vec<GrammarStack> = Vec::new();
let mut pos = RulePos::new(start_rule_index, 0);
loop {
let mut stack = GrammarStack::new();
if !elem(&rules, pos).is_end_of_sequence() {
stack.push(pos);
}
advance_stack(&rules, &stack, &mut stacks)?;
while !elem(&rules, pos).is_end_of_sequence() {
pos = pos.next();
}
if elem(&rules, pos).gtype == GreType::Alt {
pos = pos.next();
} else {
break;
}
}
Ok(Grammar {
rules,
stacks,
partial_utf8: PartialUtf8::default(),
lazy: None,
})
}
pub fn into_lazy(mut self, triggers: LazyTriggers) -> Result<Self, GrammarError> {
if triggers.is_empty() {
return Err(GrammarError::LazyWithoutTriggers);
}
self.lazy = Some(LazyState::new(triggers));
Ok(self)
}
pub fn is_lazy(&self) -> bool {
self.lazy.is_some()
}
pub fn is_awaiting_trigger(&self) -> bool {
self.lazy.as_ref().is_some_and(LazyState::awaiting)
}
pub fn trigger_buffer(&self) -> &[u8] {
self.lazy.as_ref().map_or(&[], LazyState::buffer)
}
pub fn rules(&self) -> &[GrammarRule] {
&self.rules
}
pub fn stacks(&self) -> &[GrammarStack] {
&self.stacks
}
pub fn partial_utf8(&self) -> PartialUtf8 {
self.partial_utf8
}
pub fn allows_eog(&self) -> bool {
if self.is_awaiting_trigger() {
return !self.trigger_is_mandatory();
}
self.stacks.iter().any(|s| s.is_empty())
}
pub fn trigger_is_mandatory(&self) -> bool {
self.lazy.as_ref().is_some_and(LazyState::is_mandatory)
}
pub fn is_dead(&self) -> bool {
self.stacks.is_empty()
}
pub fn accept_codepoint(&mut self, chr: u32) -> Result<(), GrammarError> {
let mut next: Vec<GrammarStack> = Vec::with_capacity(self.stacks.len());
for stack in &self.stacks {
accept_chr(&self.rules, stack, chr, &mut next)?;
}
self.stacks = next;
Ok(())
}
pub fn accept_str(&mut self, piece: &str) -> Result<(), GrammarError> {
self.accept_bytes(piece.as_bytes())
}
pub fn accept_bytes(&mut self, piece: &[u8]) -> Result<(), GrammarError> {
let (code_points, partial) = decode_piece(piece, self.partial_utf8);
for &cp in &code_points[..code_points.len() - 1] {
self.accept_codepoint(cp)?;
}
self.partial_utf8 = partial;
if self.stacks.is_empty() {
return Err(GrammarError::NoViableStack {
piece: String::from_utf8_lossy(piece).into_owned(),
});
}
Ok(())
}
pub fn accept_token(&mut self, token: u32, piece: &[u8]) -> Result<(), GrammarError> {
if self.is_awaiting_trigger() {
let step = match self.lazy.as_mut() {
Some(lazy) => lazy.observe(token, piece)?,
None => return Err(GrammarError::Internal("lazy state vanished mid-accept")),
};
let replay = match step {
TriggerStep::Awaiting => return Ok(()),
TriggerStep::Fired(replay) => replay,
};
for (tok, piece) in replay {
self.accept_token_now(tok, &piece)?;
}
return Ok(());
}
self.accept_token_now(token, piece)
}
fn accept_token_now(&mut self, token: u32, piece: &[u8]) -> Result<(), GrammarError> {
let (code_points, partial) = decode_piece(piece, self.partial_utf8);
let chars = &code_points[..code_points.len() - 1];
let mut stacks_new: Vec<GrammarStack> = Vec::with_capacity(self.stacks.len());
for stack in &self.stacks {
let Some(&top) = stack.last() else {
continue;
};
let top_elem = elem(&self.rules, top);
if matches!(top_elem.gtype, GreType::Token | GreType::TokenNot) {
if match_token(top_elem, token) {
let mut new_stack = stack[..stack.len() - 1].to_vec();
if !elem(&self.rules, top.next()).is_end_of_sequence() {
new_stack.push(top.next());
}
advance_stack(&self.rules, &new_stack, &mut stacks_new)?;
}
continue;
}
let mut current: Vec<GrammarStack> = vec![stack.clone()];
for &cp in chars {
let mut next: Vec<GrammarStack> = Vec::new();
for cur in ¤t {
accept_chr(&self.rules, cur, cp, &mut next)?;
}
current = next;
if current.is_empty() {
break;
}
}
for surviving in current {
if !stacks_new.contains(&surviving) {
stacks_new.push(surviving);
}
}
}
self.stacks = stacks_new;
self.partial_utf8 = partial;
if self.stacks.is_empty() {
return Err(GrammarError::NoViableStack {
piece: String::from_utf8_lossy(piece).into_owned(),
});
}
Ok(())
}
pub fn accept_eog(&mut self) -> Result<(), GrammarError> {
if self.allows_eog() {
Ok(())
} else {
Err(GrammarError::NoViableStack {
piece: "<eog>".to_string(),
})
}
}
}
#[inline]
pub(crate) fn elem(rules: &[GrammarRule], pos: RulePos) -> GrammarElement {
rules
.get(pos.rule as usize)
.and_then(|r| r.get(pos.index as usize))
.copied()
.unwrap_or(GrammarElement::new(GreType::End, 0))
}
pub(crate) fn match_char(
rules: &[GrammarRule],
mut pos: RulePos,
chr: u32,
) -> Result<(bool, RulePos), GrammarError> {
let first = elem(rules, pos);
let is_positive_char = matches!(first.gtype, GreType::Char | GreType::CharAny);
if !is_positive_char && first.gtype != GreType::CharNot {
return Err(GrammarError::Internal(
"match_char called on an element that is not a character class",
));
}
let mut found = false;
loop {
let cur = elem(rules, pos);
let nxt = elem(rules, pos.next());
if nxt.gtype == GreType::CharRngUpper {
found = found || (cur.value <= chr && chr <= nxt.value);
pos = pos.advance(2);
} else if cur.gtype == GreType::CharAny {
found = true;
pos = pos.next();
} else {
found = found || cur.value == chr;
pos = pos.next();
}
if elem(rules, pos).gtype != GreType::CharAlt {
break;
}
}
Ok((found == is_positive_char, pos))
}
pub(crate) fn match_partial_char(
rules: &[GrammarRule],
mut pos: RulePos,
partial_utf8: PartialUtf8,
) -> Result<bool, GrammarError> {
let first = elem(rules, pos);
let is_positive_char = matches!(first.gtype, GreType::Char | GreType::CharAny);
if !is_positive_char && first.gtype != GreType::CharNot {
return Err(GrammarError::Internal(
"match_partial_char called on an element that is not a character class",
));
}
let partial_value = partial_utf8.value;
let n_remain = partial_utf8.n_remain;
if n_remain < 0 || (n_remain == 1 && partial_value < 2) {
return Ok(false);
}
if n_remain > 3 {
return Ok(false);
}
let shift = (n_remain * 6) as u32;
let mut low = partial_value << shift;
let high = low | ((1u32 << shift) - 1);
if low == 0 {
if n_remain == 2 {
low = 1 << 11;
} else if n_remain == 3 {
low = 1 << 16;
}
}
loop {
let cur = elem(rules, pos);
let nxt = elem(rules, pos.next());
if nxt.gtype == GreType::CharRngUpper {
if cur.value <= high && low <= nxt.value {
return Ok(is_positive_char);
}
pos = pos.advance(2);
} else if cur.gtype == GreType::CharAny {
return Ok(true);
} else {
if low <= cur.value && cur.value <= high {
return Ok(is_positive_char);
}
pos = pos.next();
}
if elem(rules, pos).gtype != GreType::CharAlt {
break;
}
}
Ok(!is_positive_char)
}
pub(crate) fn match_token(pos_elem: GrammarElement, token: u32) -> bool {
match pos_elem.gtype {
GreType::Token => pos_elem.value == token,
GreType::TokenNot => pos_elem.value != token,
_ => false,
}
}
pub(crate) fn advance_stack(
rules: &[GrammarRule],
stack: &GrammarStack,
new_stacks: &mut Vec<GrammarStack>,
) -> Result<(), GrammarError> {
let mut todo: Vec<GrammarStack> = vec![stack.clone()];
let mut seen: HashSet<GrammarStack> = HashSet::new();
while let Some(curr_stack) = todo.pop() {
if !seen.insert(curr_stack.clone()) {
continue;
}
let Some(&pos) = curr_stack.last() else {
if !new_stacks.contains(&curr_stack) {
new_stacks.push(curr_stack);
}
continue;
};
let pos_elem = elem(rules, pos);
match pos_elem.gtype {
GreType::RuleRef => {
let rule_id = pos_elem.value;
let mut subpos = RulePos::new(rule_id, 0);
loop {
let mut next_stack = curr_stack[..curr_stack.len() - 1].to_vec();
if !elem(rules, pos.next()).is_end_of_sequence() {
next_stack.push(pos.next());
}
if !elem(rules, subpos).is_end_of_sequence() {
next_stack.push(subpos);
}
todo.push(next_stack);
while !elem(rules, subpos).is_end_of_sequence() {
subpos = subpos.next();
}
if elem(rules, subpos).gtype == GreType::Alt {
subpos = subpos.next();
} else {
break;
}
}
}
t if t.is_stack_terminal() => {
if !new_stacks.contains(&curr_stack) {
new_stacks.push(curr_stack);
}
}
_ => {
return Err(GrammarError::Internal(
"parse stack came to rest on END, ALT, CHAR_ALT or CHAR_RNG_UPPER",
));
}
}
}
Ok(())
}
pub(crate) fn accept_chr(
rules: &[GrammarRule],
stack: &GrammarStack,
chr: u32,
new_stacks: &mut Vec<GrammarStack>,
) -> Result<(), GrammarError> {
let Some(&pos) = stack.last() else {
return Ok(());
};
let pos_elem = elem(rules, pos);
if matches!(pos_elem.gtype, GreType::Token | GreType::TokenNot) {
return Ok(());
}
let (matched, after) = match_char(rules, pos, chr)?;
if matched {
let mut new_stack = stack[..stack.len() - 1].to_vec();
if !elem(rules, after).is_end_of_sequence() {
new_stack.push(after);
}
advance_stack(rules, &new_stack, new_stacks)?;
}
Ok(())
}
fn detect_left_recursion_all(
rules: &[GrammarRule],
name_of: &impl Fn(u32) -> Option<String>,
) -> Result<(), GrammarError> {
let n = rules.len();
let mut visited = vec![false; n];
let mut in_progress = vec![false; n];
let mut may_be_empty = vec![false; n];
for i in 0..n {
if visited[i] {
continue;
}
if detect_left_recursion(rules, i, &mut visited, &mut in_progress, &mut may_be_empty) {
return Err(GrammarError::LeftRecursion {
rule_id: i as u32,
name: name_of(i as u32),
});
}
}
Ok(())
}
fn detect_left_recursion(
rules: &[GrammarRule],
rule_index: usize,
visited: &mut [bool],
in_progress: &mut [bool],
may_be_empty: &mut [bool],
) -> bool {
if in_progress[rule_index] {
return true;
}
in_progress[rule_index] = true;
let rule = &rules[rule_index];
let mut at_rule_start = true;
for e in rule {
if e.is_end_of_sequence() {
if at_rule_start {
may_be_empty[rule_index] = true;
break;
}
at_rule_start = true;
} else {
at_rule_start = false;
}
}
let mut recurse_into_nonterminal = true;
for e in rule {
if e.gtype == GreType::RuleRef && recurse_into_nonterminal {
let target = e.value as usize;
if target >= rules.len() {
continue;
}
if detect_left_recursion(rules, target, visited, in_progress, may_be_empty) {
return true;
}
if !may_be_empty[target] {
recurse_into_nonterminal = false;
}
} else {
recurse_into_nonterminal = e.is_end_of_sequence();
}
}
in_progress[rule_index] = false;
visited[rule_index] = true;
false
}