use crate::error::TabnasError;
use crate::options::Options;
use crate::rule::{Rule, RuleSnapshot};
use crate::token::Token;
use crate::value::unwrap_arc;
use crate::value::Value;
use indexmap::IndexMap;
use std::cell::RefCell;
use std::collections::VecDeque;
use std::fmt;
use std::rc::Rc;
use std::sync::Arc;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ActionError {
pub code: String,
pub detail: String,
}
impl ActionError {
pub fn new(code: impl Into<String>, detail: impl Into<String>) -> Self {
Self {
code: code.into(),
detail: detail.into(),
}
}
}
impl fmt::Display for ActionError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.detail)
}
}
impl std::error::Error for ActionError {}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct InstanceInfo {
pub id: String,
pub parent_id: Option<String>,
pub tag: String,
pub plugins: Vec<String>,
pub rule_names: Vec<String>,
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct ContextSeed {
pub meta: Option<Value>,
pub u: IndexMap<String, Value>,
}
impl From<ActionError> for TabnasError {
fn from(action_error: ActionError) -> Self {
let mut error = TabnasError::new(action_error.code, "", "", 0, 1, 1);
error.detail = action_error.detail;
error
}
}
fn follow_stack(frames: &mut Vec<Rc<RuleSnapshot>>, stack: &[Rule]) {
frames.truncate(stack.len());
for rule in &stack[frames.len()..] {
frames.push(rule.snapshot());
}
}
#[cfg(debug_assertions)]
fn same_rule(snapshot: &crate::RuleSnapshot, rule: &Rule) -> bool {
snapshot.i == rule.i
&& snapshot.d == rule.d
&& snapshot.name == rule.name
&& snapshot.state == rule.state
&& snapshot.need == rule.need
&& snapshot.bo == rule.bo
&& snapshot.ao == rule.ao
&& snapshot.bc == rule.bc
&& snapshot.ac == rule.ac
&& snapshot.next_rule_name == rule.next_rule_name
&& snapshot.n == rule.n
&& same_values(&snapshot.u, &rule.u)
&& same_values(&snapshot.k, &rule.k)
&& same_tokens(&snapshot.o, &rule.o)
&& same_tokens(&snapshot.c, &rule.c)
&& same_link(&snapshot.parent_rule, &rule.parent_rule)
&& same_link(&snapshot.child_rule, &rule.child_rule)
&& same_link(&snapshot.prev_rule, &rule.prev_rule)
&& same_link(&snapshot.next_rule, &rule.next_rule)
}
#[cfg(debug_assertions)]
fn same_values(
left: &std::collections::HashMap<String, Value>,
right: &std::collections::HashMap<String, Value>,
) -> bool {
left.len() == right.len()
&& left
.iter()
.all(|(key, value)| right.get(key).is_some_and(|other| value.deep_equal(other)))
}
#[cfg(debug_assertions)]
fn same_tokens(left: &[Token], right: &[Token]) -> bool {
left.len() == right.len()
&& left.iter().zip(right).all(|(left, right)| {
left.name == right.name
&& left.tin == right.tin
&& left.src == right.src
&& left.len == right.len
&& left.site == right.site
&& left.err == right.err
&& left.why == right.why
&& left.val.deep_equal(&right.val)
&& same_values(left.use_data(), right.use_data())
})
}
#[cfg(debug_assertions)]
fn same_link(
snapshot: &Option<std::rc::Rc<crate::RuleSnapshot>>,
rule: &Option<std::rc::Rc<crate::RuleSnapshot>>,
) -> bool {
match (snapshot, rule) {
(None, None) => true,
(Some(left), Some(right)) => std::rc::Rc::ptr_eq(left, right),
_ => false,
}
}
#[derive(Debug)]
pub struct Context {
pub iteration: usize,
pub source: String,
pub meta: Value,
pub u: IndexMap<String, Value>,
pub errs: Vec<TabnasError>,
pub options: Arc<Options>,
pub instance: InstanceInfo,
pub rule: Option<Rc<RuleSnapshot>>,
pub rule_stack: Vec<Rc<RuleSnapshot>>,
#[cfg(debug_assertions)]
rule_stack_shadow: Vec<Rc<RuleSnapshot>>,
pub v: VecDeque<Token>,
pub v_abs: usize,
pub t: Vec<Token>,
replay: VecDeque<Token>,
history_limit: Option<usize>,
root: Option<Rc<RefCell<Value>>>,
pub(crate) recover_at: Option<usize>,
pub(crate) recover_si: Option<usize>,
pub(crate) bad_to: Option<usize>,
pub(crate) bad_error: Option<usize>,
}
impl Context {
pub(crate) fn new(
history_limit: Option<usize>,
source: impl Into<String>,
meta: Value,
options: Arc<Options>,
instance: InstanceInfo,
) -> Self {
Self {
iteration: 0,
source: source.into(),
meta,
u: IndexMap::new(),
errs: Vec::new(),
options,
instance,
rule: None,
rule_stack: Vec::new(),
#[cfg(debug_assertions)]
rule_stack_shadow: Vec::new(),
v: VecDeque::new(),
v_abs: 0,
t: Vec::with_capacity(8),
replay: VecDeque::new(),
history_limit,
root: None,
recover_at: None,
recover_si: None,
bad_to: None,
bad_error: None,
}
}
pub fn mark(&self) -> usize {
self.v_abs
}
pub fn v1(&self) -> Option<&Token> {
self.v.back()
}
pub fn v2(&self) -> Option<&Token> {
self.v.get(self.v.len().wrapping_sub(2))
}
pub fn t0(&self) -> Option<&Token> {
self.t.first()
}
pub fn t1(&self) -> Option<&Token> {
self.t.get(1)
}
pub fn set_t0(&mut self, token: Token) {
if self.t.is_empty() {
self.t.push(token);
} else {
self.t[0] = token;
}
}
pub fn set_t1(&mut self, token: Token) {
while self.t.len() < 2 {
self.t.push(Token::no_token());
}
self.t[1] = token;
}
pub fn set_v1(&mut self, token: Token) {
if let Some(last) = self.v.back_mut() {
*last = token;
} else {
self.v.push_back(token);
}
}
pub fn set_v2(&mut self, token: Token) {
match self.v.len() {
0 => self.v.push_back(token),
1 => self.v.push_front(token),
length => self.v[length - 2] = token,
}
}
pub fn root(&self) -> Option<Value> {
self.root.as_ref().map(|root| root.borrow().clone())
}
pub(crate) fn set_root(&mut self, root: Rc<RefCell<Value>>) {
self.root = Some(root);
}
pub(crate) fn set_active(&mut self, rule: &Rule, stack: &[Rule]) {
self.set_rule(rule);
self.sync_rule_stack(stack);
}
fn sync_rule_stack(&mut self, stack: &[Rule]) {
follow_stack(&mut self.rule_stack, stack);
#[cfg(debug_assertions)]
{
follow_stack(&mut self.rule_stack_shadow, stack);
debug_assert!(
self.rule_stack_shadow
.iter()
.zip(stack)
.all(|(snapshot, rule)| same_rule(snapshot, rule)),
"the incremental rule stack drifted from the live parse stack"
);
}
}
pub(crate) fn set_rule(&mut self, rule: &Rule) {
self.rule = Some(rule.snapshot());
}
pub(crate) fn apply_seed(&mut self, seed: &ContextSeed) {
if let Some(meta) = &seed.meta {
self.meta = merge_seed_value(self.meta.clone(), meta.clone());
}
for (key, value) in &seed.u {
let previous = self.u.shift_remove(key).unwrap_or(Value::Undefined);
self.u
.insert(key.clone(), merge_seed_value(previous, value.clone()));
}
self.errs.clear();
}
pub fn rewind(&mut self, mark: usize) -> Result<(), ActionError> {
let Some(count) = self.v_abs.checked_sub(mark) else {
return Ok(());
};
if count == 0 {
return Ok(());
}
if count > self.v.len() {
return Err(ActionError::new(
"internal",
format!(
"tabnas: ctx.rewind target {mark} is outside the retained history window \
(oldest mark available is {}, current is {}); increase \
options.rewind.history",
self.v_abs - self.v.len(),
self.v_abs,
),
));
}
let retained_at = self.v.len() - count;
let rewound = self.v.split_off(retained_at);
let lookahead = std::mem::take(&mut self.t);
let pending = std::mem::take(&mut self.replay);
self.replay = rewound
.into_iter()
.chain(lookahead)
.chain(pending)
.collect();
self.v_abs -= count;
Ok(())
}
pub(crate) fn record_consumed(&mut self, count: usize) {
if count == 0 {
return;
}
let count = count.min(self.t.len());
self.v.extend(self.t.drain(0..count));
self.v_abs += count;
if let Some(limit) = self.history_limit {
if self.v.len() > 2 * limit {
let remove = self.v.len() - limit;
self.v.drain(0..remove);
}
}
}
pub(crate) fn next_replay(&mut self) -> Option<Token> {
self.replay.pop_front()
}
pub(crate) fn take_replay(&mut self) -> VecDeque<Token> {
std::mem::take(&mut self.replay)
}
pub(crate) fn restore_replay(&mut self, replay: VecDeque<Token>) {
self.replay = replay;
}
}
fn merge_seed_value(base: Value, overlay: Value) -> Value {
match (base, overlay) {
(base, Value::Undefined) => base,
(Value::Object(base), Value::Object(overlay)) => {
let mut base = unwrap_arc(base);
for (key, value) in unwrap_arc(overlay) {
let previous = base.shift_remove(&key).unwrap_or(Value::Undefined);
base.insert(key, merge_seed_value(previous, value));
}
Value::object(base)
}
(_, overlay) => overlay,
}
}