Skip to content

Commit

Permalink
Add support for generating compiled DFAs as DOT files to scanner impl…
Browse files Browse the repository at this point in the history
…ementation
  • Loading branch information
jsinger67 committed Sep 7, 2024
1 parent 4348b8b commit 421f59a
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 3 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ new minor version numbers. Please note that this is no version handling covered

## 0.3.1 - Not released yet

- Add support of lots of unicode named classes like `XID_Start` and `XID_Continue` by the help of
- Add support for lots of unicode named classes like `XID_Start` and `XID_Continue` by the help of
the `seshat-unicode` crate
- Performance: Scanner holds ScannerImpl in a `Rc<RefCell<>>` to save time during creation of a new
`find_iter`
- Add support for generating compiled DFAs as DOT files to scanner implementation

## 0.3.0 - 2024-08-29

Expand Down
86 changes: 85 additions & 1 deletion src/internal/scanner_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,39 @@ impl ScannerImpl {
Ok(())
}

/// Generates the compiled DFAs as a Graphviz DOT files.
/// The DOT files are written to the target folder.
/// The file names are derived from the scanner mode names and the index of the DFA.
pub(crate) fn generate_compiled_dfas_as_dot<T>(
&self,
modes: &[ScannerMode],
target_folder: T,
) -> Result<()>
where
T: AsRef<std::path::Path>,
{
use std::fs::File;
for (i, scanner_mode) in self.scanner_modes.iter().enumerate() {
for (j, (dfa, t)) in scanner_mode.patterns.iter().enumerate() {
let title = format!(
"Compiled DFA {} - {}",
modes[i].name,
modes[i].patterns[j].0.escape_default()
);
let file_name = format!(
"{}/{}_{}_{}.dot",
target_folder.as_ref().to_str().unwrap(),
modes[i].name,
j,
t
);
let mut file = File::create(file_name)?;
super::dot::compiled_dfa_render(dfa, &title, &self.character_classes, &mut file);
}
}
Ok(())
}

/// Resets the scanner to the initial state.
#[inline]
pub(crate) fn reset(&mut self) {
Expand Down Expand Up @@ -263,7 +296,7 @@ impl std::fmt::Debug for ScannerImpl {
mod tests {
use super::*;
use crate::ScannerMode;
use std::convert::TryInto;
use std::{convert::TryInto, fs};

#[test]
fn test_try_from() {
Expand Down Expand Up @@ -291,4 +324,55 @@ mod tests {
assert!(match_char_class(1.into(), 'b'));
assert!(!match_char_class(1.into(), 'c'));
}

#[test]
fn test_generate_dot_files() {
let path = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/data/string.json");
let file = fs::File::open(path).unwrap();

let scanner_modes: Vec<ScannerMode> = serde_json::from_reader(file)
.unwrap_or_else(|e| panic!("**** Failed to read json file {path}: {e}"));

let scanner_impl: ScannerImpl = scanner_modes.clone().try_into().unwrap();
let target_folder = concat!(env!("CARGO_MANIFEST_DIR"), "/target/string_dfas");

// Delete all previously generated dot files.
let _ = fs::remove_dir_all(target_folder);
// Create the target folder.
fs::create_dir_all(target_folder).unwrap();

// Generate the compiled DFAs as dot files.
scanner_impl
.generate_compiled_dfas_as_dot(&scanner_modes, target_folder)
.unwrap();

// Check if the dot files are generated.
let dot_files: Vec<_> = fs::read_dir(target_folder)
.unwrap()
.map(|entry| entry.unwrap().path())
.collect();

assert_eq!(dot_files.len(), 12);
assert_eq!(
dot_files
.iter()
.filter(|p| p.extension().unwrap() == "dot")
.count(),
12
);
assert_eq!(
dot_files
.iter()
.filter(|p| p.file_stem().unwrap().to_str().unwrap().contains("INITIAL"))
.count(),
7
);
assert_eq!(
dot_files
.iter()
.filter(|p| p.file_stem().unwrap().to_str().unwrap().contains("STRING"))
.count(),
5
);
}
}
18 changes: 17 additions & 1 deletion src/scanner.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{cell::RefCell, fmt::Debug, rc::Rc};
use std::{cell::RefCell, fmt::Debug, path::Path, rc::Rc};

use crate::{FindMatches, Result, ScannerImpl, ScannerMode};

Expand Down Expand Up @@ -52,6 +52,22 @@ impl Scanner {
pub fn log_compiled_dfas_as_dot(&self, modes: &[ScannerMode]) -> Result<()> {
self.inner.borrow().log_compiled_dfas_as_dot(modes)
}

/// Generates the compiled DFAs as a Graphviz DOT files.
/// The DOT files are written to the target folder.
/// The file names are derived from the scanner mode names and the index of the DFA.
pub fn generate_compiled_dfas_as_dot<T>(
&self,
modes: &[ScannerMode],
target_folder: T,
) -> Result<()>
where
T: AsRef<Path>,
{
self.inner
.borrow()
.generate_compiled_dfas_as_dot(modes, target_folder)
}
}

impl Debug for Scanner {
Expand Down

0 comments on commit 421f59a

Please sign in to comment.