use adze_glr_core::{Action, ParseTable};
use adze_ir::Grammar;
#[cfg(not(debug_assertions))]
macro_rules! debug_trace {
($($arg:tt)*) => {};
}
#[cfg(debug_assertions)]
macro_rules! debug_trace {
($($arg:tt)*) => {
if std::env::var("RUST_LOG")
.ok()
.unwrap_or_default()
.contains("debug")
{
eprintln!($($arg)*);
}
};
}
#[must_use]
pub fn collect_token_indices(grammar: &Grammar, parse_table: &ParseTable) -> Vec<usize> {
let mut token_indices = Vec::new();
if let Some(&eof_idx) = parse_table.symbol_to_index.get(&parse_table.eof_symbol) {
token_indices.push(eof_idx);
} else {
debug_trace!(
"Warning: EOF (symbol {}) not found in symbol_to_index map",
parse_table.eof_symbol.0
);
}
for token_id in grammar.tokens.keys() {
if let Some(&idx) = parse_table.symbol_to_index.get(token_id) {
token_indices.push(idx);
} else {
debug_trace!(
"Warning: Token {:?} not found in symbol_to_index map",
token_id
);
}
}
token_indices.sort_unstable();
token_indices.dedup();
token_indices
}
#[must_use]
pub fn eof_accepts_or_reduces(parse_table: &ParseTable) -> bool {
let eof_idx = match parse_table.symbol_to_index.get(&parse_table.eof_symbol) {
Some(&idx) => idx,
None => return false, };
if parse_table.action_table.is_empty() {
return false;
}
let state0 = &parse_table.action_table[0];
state0.get(eof_idx).is_some_and(|cell| {
cell.iter()
.any(|action| matches!(action, Action::Accept | Action::Reduce(_)))
})
}
#[cfg(test)]
mod tests {
use super::*;
use adze_ir::{Grammar, SymbolId};
#[test]
fn test_collect_token_indices() {
let mut grammar = Grammar::default();
let mut parse_table = crate::empty_table!(states: 1, terms: 4, nonterms: 0);
let eof_col = 5;
let eof_symbol = parse_table.eof_symbol;
use adze_ir::{Token, TokenPattern};
grammar.tokens.insert(
SymbolId(1),
Token {
name: "token1".to_string(),
pattern: TokenPattern::String("a".to_string()),
fragile: false,
},
);
grammar.tokens.insert(
SymbolId(2),
Token {
name: "token2".to_string(),
pattern: TokenPattern::String("b".to_string()),
fragile: false,
},
);
grammar.tokens.insert(
SymbolId(3),
Token {
name: "token3".to_string(),
pattern: TokenPattern::String("c".to_string()),
fragile: false,
},
);
grammar.tokens.insert(
SymbolId(4),
Token {
name: "token4".to_string(),
pattern: TokenPattern::String("d".to_string()),
fragile: false,
},
);
parse_table.symbol_to_index.clear();
parse_table.symbol_to_index.insert(eof_symbol, eof_col); parse_table.symbol_to_index.insert(SymbolId(1), 3); parse_table.symbol_to_index.insert(SymbolId(2), 1); parse_table.symbol_to_index.insert(SymbolId(3), 3); parse_table.symbol_count = 7; parse_table.symbol_to_index.insert(SymbolId(4), 2);
let indices = collect_token_indices(&grammar, &parse_table);
assert_eq!(indices, vec![1, 2, 3, eof_col]);
assert!(
indices.contains(&eof_col),
"Token indices must always include EOF column ({})",
eof_col
);
let mut sorted = indices.clone();
sorted.sort_unstable();
assert_eq!(indices, sorted, "Token indices must be sorted");
let mut deduped = indices.clone();
deduped.dedup();
assert_eq!(indices, deduped, "Token indices must be deduplicated");
}
#[test]
fn test_collect_token_indices_empty() {
let grammar = Grammar::default();
let mut parse_table = crate::empty_table!(states: 1, terms: 0, nonterms: 0);
let eof_symbol = parse_table.eof_symbol;
parse_table.symbol_to_index.clear();
parse_table.symbol_to_index.insert(eof_symbol, 0);
parse_table.symbol_count = 1;
let indices = collect_token_indices(&grammar, &parse_table);
assert_eq!(indices, vec![0]);
}
}