1use crate::analysis::LookaheadDFA;
2use crate::analysis::lookahead_dfa::ProductionIndex;
3use crate::analysis::{FirstSet, FollowSet, first_k, follow_k};
4use crate::grammar::cfg::NonTerminalIndexFn;
5use crate::{GrammarAnalysisError, MAX_K};
6use crate::{GrammarConfig, KTuples};
7use anyhow::{Result, anyhow, bail};
8use parol_runtime::log::trace;
9use std::cell::RefCell;
10use std::collections::BTreeMap;
11use std::rc::Rc;
12
13use super::follow::ResultMap;
14
15#[derive(Debug, Default)]
17pub struct FirstCache(pub [Rc<RefCell<FirstSet>>; MAX_K + 1]);
18
19#[derive(Debug, Clone, Default)]
22pub struct CacheEntry {
23 pub(crate) last_result: ResultMap,
24 pub(crate) follow_set: FollowSet,
25}
26
27impl CacheEntry {
28 pub fn is_empty(&self) -> bool {
31 self.last_result.is_empty() && self.follow_set.is_empty()
32 }
33}
34
35#[derive(Debug, Default)]
37pub struct FollowCache(pub [Rc<RefCell<CacheEntry>>; MAX_K + 1]);
38
39impl FirstCache {
40 pub fn new() -> Self {
42 Self::default()
43 }
44
45 pub fn get(&self, k: usize, grammar_config: &GrammarConfig) -> Rc<RefCell<FirstSet>> {
47 let exists = !self.0[k].borrow().is_empty();
48 if exists {
49 trace!("FirstCache::get: reusing first set for k={k}");
50 self.0[k].clone()
51 } else {
52 trace!("FirstCache::get: calculating first set for k={k}...");
53 let entry = first_k(grammar_config, k, self);
54 trace!(
55 "finished, k:{} prod: {}, nt: {}",
56 k,
57 entry.productions.len(),
58 entry.non_terminals.len()
59 );
60 *self.0[k].borrow_mut() = entry;
61 self.get(k, grammar_config)
62 }
63 }
64}
65
66impl FollowCache {
67 pub fn new() -> Self {
69 Self::default()
70 }
71 pub fn get(
73 &self,
74 k: usize,
75 grammar_config: &GrammarConfig,
76 first_cache: &FirstCache,
77 ) -> Rc<RefCell<CacheEntry>> {
78 let exists = !self.0[k].borrow().is_empty();
79 if exists {
80 trace!("FollowCache::get: reusing follow set for k={k}");
81 self.0[k].clone()
82 } else {
83 trace!("FollowCache::get: calculating follow set for k={k}...");
84 let (r, f) = follow_k(grammar_config, k, first_cache, self);
85 trace!(
86 "finished, k:{} res vec: {}, nt: {}",
87 k,
88 r.len(),
89 f.non_terminals.len()
90 );
91 *self.0[k].borrow_mut() = CacheEntry {
92 last_result: r,
93 follow_set: f,
94 };
95 self.get(k, grammar_config, first_cache)
96 }
97 }
98}
99
100pub fn decidable(
111 grammar_config: &GrammarConfig,
112 non_terminal: &str,
113 max_k: usize,
114 first_cache: &FirstCache,
115 follow_cache: &FollowCache,
116) -> Result<usize> {
117 let cfg = &grammar_config.cfg;
118 let productions = cfg.matching_productions(non_terminal);
119 if productions.is_empty() {
120 Err(anyhow!(
121 "The given non-terminal isn't part of the given grammar!"
122 ))
123 } else if productions.len() == 1 {
124 Ok(0)
126 } else {
127 let nti = cfg.get_non_terminal_index_function();
128 let mut current_k = 1;
129 loop {
130 if current_k > max_k {
131 break;
132 }
133 let productions = cfg.matching_productions(non_terminal);
134 let k_tuples_of_productions = productions
135 .iter()
136 .map(|(pi, _)| {
137 let k_tuples = first_cache
138 .get(current_k, grammar_config)
139 .borrow()
140 .productions[*pi]
141 .clone();
142 (*pi, k_tuples)
143 })
144 .collect::<Vec<(ProductionIndex, KTuples)>>();
145
146 let cached = follow_cache.get(current_k, grammar_config, first_cache);
147 if let Some(follow_set) = cached
148 .borrow()
149 .follow_set
150 .non_terminals
151 .get(nti.non_terminal_index(non_terminal))
152 {
153 let concatenated_k_tuples = k_tuples_of_productions
154 .iter()
155 .map(|(i, t)| (*i, t.clone().k_concat(follow_set, current_k)))
156 .collect::<Vec<(ProductionIndex, KTuples)>>();
157
158 if concatenated_k_tuples.iter().all(|(i, t1)| {
159 concatenated_k_tuples
160 .iter()
161 .all(|(j, t2)| i == j || t1.is_disjoint(t2))
162 }) {
163 return Ok(current_k);
164 }
165 } else {
166 bail!("Internal error");
167 }
168 current_k += 1;
169 }
170 bail!(GrammarAnalysisError::MaxKExceeded { max_k })
171 }
172}
173
174pub fn calculate_k(
178 grammar_config: &GrammarConfig,
179 max_k: usize,
180 first_cache: &FirstCache,
181 follow_cache: &FollowCache,
182) -> Result<usize> {
183 let cfg = &grammar_config.cfg;
184 Ok(cfg
185 .get_non_terminal_set()
186 .iter()
187 .map(|n| decidable(grammar_config, n, max_k, first_cache, follow_cache).unwrap_or(max_k))
188 .fold(0, std::cmp::max))
189}
190
191pub fn calculate_k_tuples(
195 grammar_config: &GrammarConfig,
196 max_k: usize,
197 first_cache: &FirstCache,
198 follow_cache: &FollowCache,
199) -> Result<BTreeMap<usize, KTuples>> {
200 let cfg = &grammar_config.cfg;
201 let nti = Rc::new(cfg.get_non_terminal_index_function());
202 cfg.get_non_terminal_set()
203 .iter()
204 .map(|n| {
205 (
206 n.clone(),
207 decidable(grammar_config, n, max_k, first_cache, follow_cache),
208 )
209 })
210 .try_fold(BTreeMap::new(), |acc, (nt, r)| {
211 r.and_then(|k| {
212 calculate_tuples_for_non_terminal(
213 nt,
214 k,
215 grammar_config,
216 first_cache,
217 follow_cache,
218 nti.clone(),
219 acc,
220 )
221 })
222 })
223}
224
225fn calculate_tuples_for_non_terminal(
226 nt: String,
227 k: usize,
228 grammar_config: &GrammarConfig,
229 first_cache: &FirstCache,
230 follow_cache: &FollowCache,
231 nti: Rc<impl NonTerminalIndexFn>,
232 mut m: BTreeMap<usize, KTuples>,
233) -> std::result::Result<BTreeMap<usize, KTuples>, anyhow::Error> {
234 let productions = grammar_config.cfg.matching_productions(&nt);
235 let mut k_tuples = productions
236 .iter()
237 .fold(BTreeMap::new(), |mut acc, (pi, _)| {
238 let k_tuples = first_cache.get(k, grammar_config).borrow().productions[*pi].clone();
239 let cached = follow_cache.get(k, grammar_config, first_cache);
240 if let Some(follow_set) = cached
241 .borrow()
242 .follow_set
243 .non_terminals
244 .get(nti.non_terminal_index(&nt))
245 {
246 acc.insert(*pi, k_tuples.k_concat(follow_set, k));
247 }
248 acc
249 });
250 m.append(&mut k_tuples);
251 Ok(m)
252}
253
254pub fn calculate_lookahead_dfas(
262 grammar_config: &GrammarConfig,
263 max_k: usize,
264) -> Result<BTreeMap<String, LookaheadDFA>> {
265 let cfg = &grammar_config.cfg;
266
267 let first_cache = FirstCache::new();
268 let follow_cache = FollowCache::new();
269
270 let k_tuples_of_productions =
271 calculate_k_tuples(grammar_config, max_k, &first_cache, &follow_cache)?;
272 k_tuples_of_productions.iter().try_fold(
273 BTreeMap::<String, LookaheadDFA>::new(),
274 |mut acc, (i, t)| {
275 let nt = cfg[*i].get_n();
276 let dfa = LookaheadDFA::from_k_tuples(t, *i);
277 if let Some(found_dfa) = acc.remove(&nt) {
278 let united_dfa = found_dfa.unite(&dfa)?;
279 acc.insert(nt, united_dfa);
280 } else {
281 acc.insert(nt, dfa);
282 }
283 Ok(acc)
284 },
285 )
286}
287
288pub fn explain_conflicts(
292 grammar_config: &GrammarConfig,
293 non_terminal: &str,
294 k: usize,
295 first_cache: &FirstCache,
296 follow_cache: &FollowCache,
297) -> Result<Vec<(ProductionIndex, KTuples, ProductionIndex, KTuples)>> {
298 let cfg = &grammar_config.cfg;
299 let productions = cfg.matching_productions(non_terminal);
300 if productions.is_empty() {
301 Err(anyhow!(
302 "The given non-terminal isn't part of the given grammar!"
303 ))
304 } else if productions.len() == 1 {
305 Ok(Vec::new())
307 } else {
308 let nti = cfg.get_non_terminal_index_function();
309 let productions = cfg.matching_productions(non_terminal);
310 let k_tuples_of_productions = productions
311 .iter()
312 .map(|(pi, _)| {
313 let k_tuples = first_cache.get(k, grammar_config).borrow().productions[*pi].clone();
314 (*pi, k_tuples)
315 })
316 .collect::<Vec<(ProductionIndex, KTuples)>>();
317
318 let cached = follow_cache.get(k, grammar_config, first_cache);
319 if let Some(follow_set) = cached
320 .borrow()
321 .follow_set
322 .non_terminals
323 .get(nti.non_terminal_index(non_terminal))
324 {
325 let concatenated_k_tuples = k_tuples_of_productions
326 .iter()
327 .map(|(i, t)| (*i, t.clone().k_concat(follow_set, k)))
328 .collect::<Vec<(ProductionIndex, KTuples)>>();
329 let mut conflicting_k_tuples = Vec::new();
330 for (i, ki) in &concatenated_k_tuples {
331 for (j, kj) in &concatenated_k_tuples {
332 if i != j
333 && !ki.is_disjoint(kj)
334 && !conflicting_k_tuples
335 .iter()
336 .any(|(p1, _, p2, _)| p1 != i && p2 != j)
337 {
338 conflicting_k_tuples.push((*i, ki.clone(), *j, kj.clone()));
339 }
340 }
341 }
342 return Ok(conflicting_k_tuples);
343 }
344 Err(anyhow!("Internal error"))
345 }
346}
347
348#[cfg(test)]
349mod test {
350 use super::{FirstCache, FollowCache, calculate_k, decidable};
351 use crate::grammar::SymbolAttribute;
352 use crate::{Cfg, GrammarConfig, Pr, Symbol, Terminal, TerminalKind};
353
354 macro_rules! terminal {
355 ($term:literal) => {
356 Symbol::T(Terminal::Trm(
357 $term.to_string(),
358 TerminalKind::Legacy,
359 vec![0],
360 SymbolAttribute::None,
361 None,
362 None,
363 None,
364 ))
365 };
366 }
367
368 #[test]
369 fn check_decidable() {
370 let cfg = Cfg::with_start_symbol("S")
371 .add_pr(Pr::new("S", vec![terminal!("a"), Symbol::n("X")]))
372 .add_pr(Pr::new("X", vec![terminal!("b"), Symbol::n("S")]))
373 .add_pr(Pr::new(
374 "X",
375 vec![
376 terminal!("a"),
377 Symbol::n("Y"),
378 terminal!("b"),
379 Symbol::n("Y"),
380 ],
381 ))
382 .add_pr(Pr::new("Y", vec![terminal!("b"), terminal!("a")]))
383 .add_pr(Pr::new("Y", vec![terminal!("a"), Symbol::n("Z")]))
384 .add_pr(Pr::new(
385 "Z",
386 vec![terminal!("a"), Symbol::n("Z"), Symbol::n("X")],
387 ));
388 let grammar_config = GrammarConfig::new(cfg, 5);
389 let first_cache = FirstCache::new();
390 let follow_cache = FollowCache::new();
391 let result = decidable(&grammar_config, "S", 5, &first_cache, &follow_cache).unwrap();
392 assert_eq!(0, result);
393 let result = decidable(&grammar_config, "X", 5, &first_cache, &follow_cache).unwrap();
394 assert_eq!(1, result);
395 let result = decidable(&grammar_config, "Y", 5, &first_cache, &follow_cache).unwrap();
396 assert_eq!(1, result);
397 let result = decidable(&grammar_config, "Z", 5, &first_cache, &follow_cache).unwrap();
398 assert_eq!(0, result);
399 assert_eq!(
400 "The given non-terminal isn't part of the given grammar!",
401 decidable(&grammar_config, "A", 5, &first_cache, &follow_cache)
402 .err()
403 .unwrap()
404 .to_string()
405 );
406 }
407
408 #[test]
409 fn check_calculate_k() {
410 let cfg = Cfg::with_start_symbol("S")
411 .add_pr(Pr::new("S", vec![terminal!("a"), Symbol::n("X")]))
412 .add_pr(Pr::new("X", vec![terminal!("b"), Symbol::n("S")]))
413 .add_pr(Pr::new(
414 "X",
415 vec![
416 terminal!("a"),
417 Symbol::n("Y"),
418 terminal!("b"),
419 Symbol::n("Y"),
420 ],
421 ))
422 .add_pr(Pr::new("Y", vec![terminal!("b"), terminal!("a")]))
423 .add_pr(Pr::new("Y", vec![terminal!("a"), Symbol::n("Z")]))
424 .add_pr(Pr::new(
425 "Z",
426 vec![terminal!("a"), Symbol::n("Z"), Symbol::n("X")],
427 ));
428 let grammar_config = GrammarConfig::new(cfg, 5);
429 let first_cache = FirstCache::new();
430 let follow_cache = FollowCache::new();
431 let result = calculate_k(&grammar_config, 5, &first_cache, &follow_cache).unwrap();
432 assert_eq!(1, result);
433 }
434}