summaryrefslogtreecommitdiff
path: root/chain/src/atom/mod.rs
blob: c9dadb26803f7534a6ff9b21a652b2cb78f02c84 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//! This file defines the behaviour of the Atomic languages, and
//! provides a default implementation.
//!
//! Here I do not to substitute external packages' implementations in
//! the future, so why define a trait for the atomic languages?
//! Because this way I can easily substitute other implementations if
//! I have better ideas in the future.

use grammar::{Error as GrammarError, Grammar, TNT};
use nfa::{DOption, LabelType, Nfa};

use std::ops::Deref;

/// The expected behaviours of an atomic language.
pub trait Atom: Nfa<LabelType<TNT>> + Deref<Target = Grammar> {
    /// Return the index of a node representing the derivative of the
    /// left-linear null closure of `nt` with respect to `t`.
    fn atom(&self, nt: usize, t: usize) -> Result<Option<usize>, GrammarError>;

    /// A type that iterates through the rule positions.
    type Iter<'a>: Iterator<Item = usize> + 'a
    where
        Self: 'a;

    /// Return an iterator of rule positions responsible for an edge
    /// from the virtual node corresponding to the non-terminal `nt`
    /// and terminal `t` to `target`.
    fn trace(&self, nt: usize, t: usize, target: usize) -> Option<<Self as Atom>::Iter<'_>>;

    /// Return the index of the empty state.
    fn empty(&self) -> usize;

    /// Tell whether a node is accepting.
    fn is_accepting(&self, node_id: usize) -> Result<bool, GrammarError>;
}

pub mod default;

pub use default::DefaultAtom;

#[cfg(test)]
mod tests {
    use super::*;
    use grammar::test_grammar_helper::*;

    #[cfg(feature = "test-print-viz")]
    use graph::Graph;

    #[test]
    fn atom() -> Result<(), Box<dyn std::error::Error>> {
        let grammar = new_notes_grammar()?;

        let atom = DefaultAtom::from_grammar(grammar)?;

        println!("atom = {atom}");

        #[cfg(feature = "test-print-viz")]
        {
            println!("virtual frag for 1, 3: ");

            for (index, frag) in atom
                .generate_virtual_frags(1, 3, None)
                .iter()
                .flatten()
                .enumerate()
            {
                crate::item::default::print_labels(&atom, *frag)?;
                frag.print_viz(&format!("frag {index}.gv"))?;
            }
        }

        Ok(())
    }
}