dyck/lib.rs
1//! A library for running Dyck algorithms over languages of generic <Token> string slice types.
2//!
3//! This module provides functionalities to create and manipulate languages
4//! based on user-defined tokens, allowing for the evaluation and transformation
5//! of sequences of tokens (words) according to the rules of Dyck languages.
6//!
7//! ## Usage
8//!
9//! Add `dyck` to your `Cargo.toml`:
10//!
11//! ```toml
12//! [dependencies]
13//! dyck = "0.1"
14//! ```
15//! ## Example: Creating a Dyck language and checking if a word is valid.
16//!
17//! ```rust
18//! use dyck::{Language, Word};
19//!
20//! // define pairs of tokens for the language
21//! let pairs = vec![("(", ")"), ("[", "]"), ("{", "}")];
22//! let language = Language::new_from_vec(&pairs).expect("Failed to create language");
23//!
24//! // define a word to check
25//! let word: Word<&str> = vec!["(", "[", "]", "(", ")", ")"];
26//!
27//! // check if the word is a valid Dyck word
28//! if language.is_valid(&word) {
29//! println!("The word is a valid Dyck word.");
30//! } else {
31//! println!("The word is not a valid Dyck word.");
32//! }
33//! ```
34//!
35//! See /examples in the repository root for more examples.
36
37use std::collections::HashMap;
38
39#[cfg(feature = "derive")]
40pub use dyck_derive::DyckToken;
41
42/// A trait implementable by user-defined enums to gain compatiblity with all Dyck functions.
43/// This trait alias is available as a derive macro via the derive feature.
44pub trait DyckToken: Clone + Copy + Eq + std::hash::Hash {}
45
46impl DyckToken for &str {}
47
48/// A word is a sequence of tokens to be evaluated relative to a Dyck language.
49/// It is not necessarily a dyck word, just a dyck candidate.
50pub type Word<T> = Vec<T>;
51
52/// Stores the context to be used to run algorithms on potential dyck words.
53pub struct Language<T>
54where
55 T: DyckToken,
56{
57 alphabet: Vec<T>,
58 open_to_close: HashMap<T, T>,
59 close_to_open: HashMap<T, T>,
60}
61
62impl<T> Language<T>
63where
64 T: DyckToken,
65{
66 /// Creates a new Dyck context from a vector of pairs.
67 pub fn new_from_vec(pairs: &Vec<(T, T)>) -> Result<Self, &'static str> {
68 let mut alphabet = Vec::new();
69 let mut open_to_close = HashMap::new();
70 let mut close_to_open = HashMap::new();
71 for &(open, close) in pairs {
72 if alphabet.contains(&open) || alphabet.contains(&close) {
73 return Err("Duplicate token in the alphabet.");
74 }
75 alphabet.push(open);
76 alphabet.push(close);
77 open_to_close.insert(open, close);
78 close_to_open.insert(close, open);
79 }
80 Ok(Self {
81 alphabet,
82 open_to_close,
83 close_to_open,
84 })
85 }
86
87 /// Creates a new Dyck context from a slice of pairs.
88 pub fn new_from_arr(pairs: &[(T, T)]) -> Result<Self, &'static str> {
89 Self::new_from_vec(&pairs.to_vec())
90 }
91
92 // Returns k, the number of parenthesis types in the alphabet.
93 pub fn get_k(&self) -> usize {
94 self.alphabet.len() / 2
95 }
96
97 /// Checks if a token is an opening token.
98 pub fn is_open(&self, token: T) -> bool {
99 self.open_to_close.contains_key(&token)
100 }
101
102 /// Checks if a token is a closing token.
103 pub fn is_close(&self, token: T) -> bool {
104 self.close_to_open.contains_key(&token)
105 }
106
107 /// Checks if a word is a valid Dyck word.
108 pub fn is_valid(&self, word: &Word<T>) -> bool {
109 let mut stack = Vec::new();
110 for &token in word {
111 if self.is_open(token) {
112 stack.push(token);
113 } else if self.is_close(token) {
114 if let Some(&last) = stack.last() {
115 if self.open_to_close[&last] == token {
116 stack.pop();
117 } else {
118 return false;
119 }
120 } else {
121 return false;
122 }
123 }
124 }
125 stack.is_empty()
126 }
127
128 /// Checks whether a word has balanced open and close tokens, but not necessarily in the
129 /// correct order.
130 pub fn is_balanced(&self, word: &Word<T>) -> bool {
131 let mut count = 0;
132 for &token in word {
133 if self.is_open(token) {
134 count += 1;
135 } else if self.is_close(token) {
136 count -= 1;
137 }
138 if count < 0 {
139 return false;
140 }
141 }
142 count == 0
143 }
144
145 /// Finds the length of the longest valid prefix of a word.
146 pub fn longest_valid_prefix(&self, word: &Word<T>) -> usize {
147 let mut stack = Vec::new();
148 let mut length = 0;
149
150 for (i, &token) in word.iter().enumerate() {
151 if self.is_open(token) {
152 stack.push(token);
153 } else if self.is_close(token) {
154 if let Some(&last) = stack.last() {
155 if self.open_to_close[&last] == token {
156 stack.pop();
157 if stack.is_empty() {
158 length = i + 1;
159 }
160 } else {
161 break;
162 }
163 } else {
164 break;
165 }
166 }
167 }
168
169 length
170 }
171
172 /// Finds the shortest completion of a word to make it a valid Dyck word.
173 /// Returns the validating shortest appendage, or None if no such appendage exists.
174 pub fn shortest_validating_appendage(&self, word: &Word<T>) -> Option<Word<T>> {
175 let mut stack = Vec::new();
176 let mut completed_word = word.clone();
177
178 for &token in word {
179 if self.is_open(token) {
180 stack.push(token);
181 } else if self.is_close(token) {
182 if let Some(&last) = stack.last() {
183 if self.open_to_close[&last] == token {
184 stack.pop();
185 }
186 } else if stack.is_empty() {
187 return None;
188 }
189 }
190 }
191
192 for token in stack.into_iter().rev() {
193 if let Some(close_token) = self.get_close(token) {
194 completed_word.push(close_token);
195 }
196 }
197
198 Some(completed_word)
199 }
200
201 /// Finds the closing token corresponding to an open token.
202 pub fn get_close(&self, open: T) -> Option<T> {
203 self.open_to_close.get(&open).copied()
204 }
205
206 /// Finds the opening token corresponding to a closing token.
207 pub fn get_open(&self, close: T) -> Option<T> {
208 self.close_to_open.get(&close).copied()
209 }
210}