update: finalize
This commit is contained in:
parent
b356784431
commit
b20e3d3603
3 changed files with 161 additions and 5 deletions
|
@ -1,10 +1,12 @@
|
|||
//! 正規表現エンジン
|
||||
|
||||
use std::fmt::{self, Display};
|
||||
mod codegen;
|
||||
mod evaluator;
|
||||
mod parser;
|
||||
|
||||
use crate::helper::DynError;
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
/// 命令列
|
||||
#[derive(Debug)]
|
||||
pub enum Instruction {
|
||||
Char(char),
|
||||
|
@ -23,3 +25,58 @@ impl Display for Instruction {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 正規表現をパースしてコード生成し、
|
||||
/// ASTと命令列を標準出力に表示。
|
||||
///
|
||||
/// # 利用例
|
||||
///
|
||||
/// ```
|
||||
/// use regex;
|
||||
/// regex::print("abc|(de|cd)+");
|
||||
/// ```
|
||||
///
|
||||
/// # 返り値
|
||||
///
|
||||
/// 入力された正規表現にエラーがあったり、内部的な実装エラーがある場合はErrを返す。
|
||||
pub fn print(expr: &str) -> Result<(), DynError> {
|
||||
println!("expr: {expr}");
|
||||
let ast = parser::parse(expr)?;
|
||||
println!("AST: {:?}", ast);
|
||||
|
||||
println!();
|
||||
println!("code:");
|
||||
let code = codegen::get_code(&ast)?;
|
||||
for (n, c) in code.iter().enumerate() {
|
||||
println!("{:>04}: {c}", n);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 正規表現と文字列をマッチング。
|
||||
///
|
||||
/// # 利用例
|
||||
///
|
||||
/// ```
|
||||
/// use regex;
|
||||
/// regex::do_matching("abc|(de|cd)+", "decddede", true);
|
||||
/// ```
|
||||
///
|
||||
/// # 引数
|
||||
///
|
||||
/// exprに正規表現、lineにマッチ対象とする文字列を与える。
|
||||
/// is_depthがtrueの場合は深さ優先探索を、falseの場合は幅優先探索を利用。
|
||||
///
|
||||
/// # 返り値
|
||||
///
|
||||
/// エラーなく実行でき、かつマッチングに**成功**した場合はOk(true)を返し、
|
||||
/// エラーなく実行でき、かつマッチングに**失敗**した場合はOk(false)を返す。
|
||||
///
|
||||
/// 入力された正規表現にエラーがあったり、内部的な実装エラーがある場合はErrを返す。
|
||||
pub fn do_matching(expr: &str, line: &str, is_depth: bool) -> Result<bool, DynError> {
|
||||
let ast = parser::parse(expr)?;
|
||||
let code = codegen::get_code(&ast)?;
|
||||
let line = line.chars().collect::<Vec<char>>();
|
||||
Ok(evaluator::eval(&code, &line, is_depth)?)
|
||||
}
|
||||
|
|
|
@ -12,4 +12,4 @@
|
|||
mod engine;
|
||||
mod helper;
|
||||
|
||||
// pub use engine::{do_matching, print};
|
||||
pub use engine::{do_matching, print};
|
||||
|
|
103
src/main.rs
103
src/main.rs
|
@ -1,3 +1,102 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
mod engine;
|
||||
mod helper;
|
||||
|
||||
use helper::DynError;
|
||||
use std::{
|
||||
env,
|
||||
fs::File,
|
||||
io::{BufRead, BufReader},
|
||||
};
|
||||
|
||||
fn main() -> Result<(), DynError> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() <= 2 {
|
||||
eprintln!("usage: {} regex file", args[0]);
|
||||
return Err("invalid arguments".into());
|
||||
} else {
|
||||
match_file(&args[1], &args[2])?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// ファイルをオープンし、行ごとにマッチングを行う。
|
||||
///
|
||||
/// マッチングはそれぞれの行頭から1文字ずつずらして行い、
|
||||
/// いずれかにマッチした場合に、その行がマッチしたものとみなす。
|
||||
///
|
||||
/// たとえば、abcdという文字列があった場合、以下の順にマッチが行われ、
|
||||
/// このいずれかにマッチした場合、与えられた正規表現にマッチする行と判定する。
|
||||
///
|
||||
/// - abcd
|
||||
/// - bcd
|
||||
/// - cd
|
||||
/// - d
|
||||
fn match_file(expr: &str, file: &str) -> Result<(), DynError> {
|
||||
let f = File::open(file)?;
|
||||
let reader = BufReader::new(f);
|
||||
|
||||
engine::print(expr)?;
|
||||
println!();
|
||||
|
||||
for line in reader.lines() {
|
||||
let line = line?;
|
||||
for (i, _) in line.char_indices() {
|
||||
if engine::do_matching(expr, &line[i..], true)? {
|
||||
println!("{line}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// 単体テスト。プライベート関数もテスト可能
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
engine::do_matching,
|
||||
helper::{safe_add, SafeAdd},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_safe_add() {
|
||||
let n: usize = 10;
|
||||
assert_eq!(Some(30), n.safe_add(&20));
|
||||
|
||||
let n: usize = !0; // 2^64 - 1 (64 bits CPU)
|
||||
assert_eq!(None, n.safe_add(&1));
|
||||
|
||||
let mut n: usize = 10;
|
||||
assert!(safe_add(&mut n, &20, || ()).is_ok());
|
||||
|
||||
let mut n: usize = !0;
|
||||
assert!(safe_add(&mut n, &1, || ()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_matching() {
|
||||
// パースエラー
|
||||
assert!(do_matching("+b", "bbb", true).is_err());
|
||||
assert!(do_matching("*b", "bbb", true).is_err());
|
||||
assert!(do_matching("|b", "bbb", true).is_err());
|
||||
assert!(do_matching("?b", "bbb", true).is_err());
|
||||
|
||||
// パース成功、マッチ成功
|
||||
assert!(do_matching("abc|def", "def", true).unwrap());
|
||||
assert!(do_matching("(abc)*", "abcabc", true).unwrap());
|
||||
assert!(do_matching("(ab|cd)+", "abcdcd", true).unwrap());
|
||||
assert!(do_matching("abc?", "ab", true).unwrap());
|
||||
assert!(do_matching("((((a*)*)*)*)", "aaaaaaaaa", true).unwrap());
|
||||
assert!(do_matching("(a*)*b", "aaaaaaaaab", true).unwrap());
|
||||
assert!(do_matching("(a*)*b", "b", true).unwrap());
|
||||
assert!(do_matching("a**b", "aaaaaaaaab", true).unwrap());
|
||||
assert!(do_matching("a**b", "b", true).unwrap());
|
||||
|
||||
// パース成功、マッチ失敗
|
||||
assert!(!do_matching("abc|def", "efa", true).unwrap());
|
||||
assert!(!do_matching("(ab|cd)+", "", true).unwrap());
|
||||
assert!(!do_matching("abc?", "acb", true).unwrap());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue