use color_eyre::eyre::{eyre, Error, Result};
use std::str::FromStr;
use rxp::{Compiler, DfaCompiler, Parser, Scanner};
use structopt::StructOpt;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum Phase {
Tokens,
Ast,
Nfa,
Dfa,
}
impl FromStr for Phase {
type Err = Error;
fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
match s.to_lowercase().as_str() {
"tokens" => Ok(Self::Tokens),
"ast" => Ok(Self::Ast),
"nfa" => Ok(Self::Nfa),
"dfa" => Ok(Self::Dfa),
other => Err(eyre!("Expected scanner, dfa, nfa or parser, got {other}")),
}
}
}
#[derive(StructOpt)]
enum Opt {
Dot {
phase: Phase,
regex: String,
},
Test {
regex: String,
test_string: String,
#[structopt(long)]
dfa: bool,
},
}
fn test_expression(regex_string: &str, test_string: &str, construct_dfa: bool) -> Result<bool> {
let scanner = Scanner::new(regex_string);
let mut parser = Parser::new(&scanner);
let regex = parser.parse()?;
let compiler = Compiler::new();
let nfa = compiler.compile(®ex);
let res = if !construct_dfa {
nfa.matches(test_string)
} else {
let dfa_compiler = DfaCompiler::new();
let dfa = dfa_compiler.create_dfa(nfa);
dfa.matches(test_string)
};
Ok(res)
}
fn visualise_expression(regex_string: &str, phase: Phase) -> Result<String> {
let scanner = Scanner::new(regex_string);
if phase == Phase::Tokens {
return Ok(scanner.graphviz("Scanner"));
}
let mut parser = Parser::new(&scanner);
let regex = parser.parse()?;
if phase == Phase::Ast {
return Ok(regex.graphviz("Parser"));
}
let compiler = Compiler::new();
let nfa = compiler.compile(®ex);
if phase == Phase::Nfa {
return Ok(nfa.graphviz("Nfa"));
}
let dfa_compiler = DfaCompiler::new();
let dfa = dfa_compiler.create_dfa(nfa);
if phase == Phase::Dfa {
return Ok(dfa.graphviz("Dfa"));
}
Err(eyre!("Haven't implemented that yet."))
}
fn main() -> Result<()> {
color_eyre::install()?;
match Opt::from_args() {
Opt::Dot { phase, regex } => println!("{}", visualise_expression(®ex, phase)?),
Opt::Test {
regex,
test_string,
dfa,
} => println!("{}", test_expression(®ex, &test_string, dfa)?),
}
Ok(())
}