use crate::grammars::{GlobalRuleRef, GrammarId, ROOT_RULE_ID};
use crate::scope::ScopeListId;
#[cfg(feature = "debug")]
use crate::scope::ScopeInterner;
#[derive(Clone, Debug)]
pub struct StackFrame {
pub rule_ref: GlobalRuleRef,
pub name_scopes: ScopeListId,
pub content_scopes: ScopeListId,
pub end_pattern: Option<String>,
pub begin_rule_has_captured_eol: bool,
pub anchor_position: Option<usize>,
pub enter_position: Option<usize>,
}
#[derive(Clone)]
pub struct StateStack {
pub frames: Vec<StackFrame>,
}
impl StateStack {
pub fn new(grammar_id: GrammarId, grammar_scope: ScopeListId) -> Self {
Self {
frames: vec![StackFrame {
rule_ref: GlobalRuleRef {
grammar: grammar_id,
rule: ROOT_RULE_ID,
},
name_scopes: grammar_scope,
content_scopes: grammar_scope,
end_pattern: None,
begin_rule_has_captured_eol: false,
anchor_position: None,
enter_position: None,
}],
}
}
pub fn push(
&mut self,
rule_ref: GlobalRuleRef,
anchor_position: Option<usize>,
begin_rule_has_captured_eol: bool,
enter_position: Option<usize>,
) {
let content_scopes = self.top().content_scopes;
self.frames.push(StackFrame {
rule_ref,
name_scopes: content_scopes,
content_scopes,
end_pattern: None,
begin_rule_has_captured_eol,
anchor_position,
enter_position,
});
}
pub fn push_with_scopes(
&mut self,
rule_ref: GlobalRuleRef,
anchor_position: Option<usize>,
begin_rule_has_captured_eol: bool,
enter_position: Option<usize>,
scopes: ScopeListId,
) {
self.frames.push(StackFrame {
rule_ref,
name_scopes: scopes,
content_scopes: scopes,
end_pattern: None,
begin_rule_has_captured_eol,
anchor_position,
enter_position,
});
}
pub fn set_content_scopes(&mut self, content_scopes: ScopeListId) {
self.top_mut().content_scopes = content_scopes;
}
pub fn set_end_pattern(&mut self, end_pattern: String) {
self.top_mut().end_pattern = Some(end_pattern);
}
pub fn pop(&mut self) -> Option<StackFrame> {
if self.frames.len() > 1 {
self.frames.pop()
} else {
None
}
}
pub fn safe_pop(&mut self) {
if self.frames.len() > 1 {
self.frames.pop();
}
}
pub fn reset(&mut self) {
for frame in &mut self.frames {
frame.enter_position = None;
frame.anchor_position = None;
}
}
pub fn top(&self) -> &StackFrame {
self.frames.last().expect("stack never empty")
}
pub fn top_mut(&mut self) -> &mut StackFrame {
self.frames.last_mut().expect("stack never empty")
}
#[cfg(feature = "debug")]
pub(crate) fn debug(&self, interner: &ScopeInterner) -> Result<String, std::fmt::Error> {
use std::fmt::Write;
let mut f = String::new();
writeln!(f, "StateStack:")?;
for (depth, frame) in self.frames.iter().enumerate() {
let indent = " ".repeat(depth);
write!(
f,
"{}grammar={}, rule={}",
indent, frame.rule_ref.grammar.0, frame.rule_ref.rule.0
)?;
let name_scopes = interner.get_scopes(frame.name_scopes);
if !name_scopes.is_empty() {
write!(f, " name=[")?;
for (i, scope) in name_scopes.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", scope.build_string())?;
}
write!(f, "]")?;
}
let content_scopes = interner.get_scopes(frame.content_scopes);
if !content_scopes.is_empty() {
write!(f, ", content=[")?;
for (i, scope) in content_scopes.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", scope.build_string())?;
}
write!(f, "]")?;
}
if let Some(pattern) = &frame.end_pattern {
write!(f, ", end_pattern=\"{}\"", pattern)?;
}
write!(f, ", anchor_pos={:?}", frame.anchor_position)?;
if let Some(enter_pos) = frame.enter_position
&& frame.anchor_position != Some(enter_pos)
{
write!(f, ", enter_pos={}", enter_pos)?;
}
write!(
f,
", begin_rule_has_captured_eol={}",
frame.begin_rule_has_captured_eol
)?;
writeln!(f)?;
}
Ok(f)
}
}