diff --git a/src/engine.rs b/src/engine.rs index 6cd06f3..d0e0b30 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,25 +1,4 @@ //! 正規表現エンジン - -use std::fmt::{self, Display}; -mod codegen; -mod evaluator; -mod parser; - -#[derive(Debug)] -pub enum Instruction { - Char(char), - Match, - Jump(usize), - Split(usize, usize), -} - -impl Display for Instruction { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Instruction::Char(c) => write!(f, "char {}", c), - Instruction::Match => write!(f, "match"), - Instruction::Jump(addr) => write!(f, "jump {:>04}", addr), - Instruction::Split(addr1, addr2) => write!(f, "split {:>04}, {:>04}", addr1, addr2), - } - } -} +// mod codegen; +// mod evaluator; +mod parser; \ No newline at end of file diff --git a/src/engine/codegen.rs b/src/engine/codegen.rs deleted file mode 100644 index 6ef088f..0000000 --- a/src/engine/codegen.rs +++ /dev/null @@ -1,226 +0,0 @@ -//! ASTからコード生成を行う -use super::{parser::AST, Instruction}; -use crate::helper::safe_add; -use std::{ - error::Error, - fmt::{self, Display}, -}; - -/// コード生成エラーを表す型 -#[derive(Debug)] -pub enum CodeGenError { - PCOverFlow, - FailStar, - FailOr, - FailQuestion, -} - -impl Display for CodeGenError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "CodeGenError: {:?}", self) - } -} - -impl Error for CodeGenError {} - -/// コード生成器 -#[derive(Default, Debug)] -struct Generator { - pc: usize, - insts: Vec<Instruction>, -} - -/// コード生成を行う関数 -pub fn get_code(ast: &AST) -> Result<Vec<Instruction>, CodeGenError> { - let mut generator = Generator::default(); - generator.gen_code(ast)?; - Ok(generator.insts) -} - -/// コード生成器のメソッド定義 -impl Generator { - /// コード生成を行う関数の入り口 - fn gen_code(&mut self, ast: &AST) -> Result<(), CodeGenError> { - self.gen_expr(ast)?; - self.inc_pc()?; - self.insts.push(Instruction::Match); - Ok(()) - } - - /// ASTをパターン分けしコード生成を行う関数 - fn gen_expr(&mut self, ast: &AST) -> Result<(), CodeGenError> { - match ast { - AST::Char(c) => self.gen_char(*c)?, - AST::Or(e1, e2) => self.gen_or(e1, e2)?, - AST::Plus(e) => self.gen_plus(e)?, - AST::Star(e1) => { - match &**e1 { - // `(a*)*`のように`Star`が二重となっている場合にスタックオーバーフローする問題を回避するため、 - // このような`(((r*)*)*...*)*`を再帰的に処理して1つの`r*`へと変換する。 - AST::Star(_) => self.gen_expr(&e1)?, - AST::Seq(e2) if e2.len() == 1 => { - if let Some(e3 @ AST::Star(_)) = e2.get(0) { - self.gen_expr(e3)? - } else { - self.gen_star(e1)? - } - } - e => self.gen_star(&e)?, - } - } - AST::Question(e) => self.gen_question(e)?, - AST::Seq(v) => self.gen_seq(v)?, - } - - Ok(()) - } - - /// char命令生成関数 - fn gen_char(&mut self, c: char) -> Result<(), CodeGenError> { - let inst = Instruction::Char(c); - self.insts.push(inst); - self.inc_pc()?; - Ok(()) - } - - /// OR演算子のコード生成器。 - /// - /// 以下のようなコードを生成。 - /// - /// ```text - /// split L1, L2 - /// L1: e1のコード - /// jmp L3 - /// L2: e2のコード - /// L3: - /// ``` - fn gen_or(&mut self, e1: &AST, e2: &AST) -> Result<(), CodeGenError> { - // split L1, L2 - let split_addr = self.pc; - self.inc_pc()?; - let split = Instruction::Split(self.pc, 0); // L1 = self.pc。L2は仮に0と設定 - self.insts.push(split); - - // L1: e1のコード - self.gen_expr(e1)?; - - // jmp L3 - let jmp_addr = self.pc; - self.insts.push(Instruction::Jump(0)); // L3を仮に0と設定 - - // L2の値を設定 - self.inc_pc()?; - if let Some(Instruction::Split(_, l2)) = self.insts.get_mut(split_addr) { - *l2 = self.pc; - } else { - return Err(CodeGenError::FailOr); - } - - // L2: e2のコード - self.gen_expr(e2)?; - - // L3の値を設定 - if let Some(Instruction::Jump(l3)) = self.insts.get_mut(jmp_addr) { - *l3 = self.pc; - } else { - return Err(CodeGenError::FailOr); - } - - Ok(()) - } - - /// ?限量子のコード生成器。 - /// - /// 以下のようなコードを生成 - /// - /// ```text - /// split L1, L2 - /// L1: eのコード - /// L2: - /// ``` - fn gen_question(&mut self, e: &AST) -> Result<(), CodeGenError> { - // split L1, L2 - let split_addr = self.pc; - self.inc_pc()?; - let split = Instruction::Split(self.pc, 0); // self.pcがL1。L2を仮に0と設定 - self.insts.push(split); - - // L1: eのコード - self.gen_expr(e)?; - - // L2の値を設定 - if let Some(Instruction::Split(_, l2)) = self.insts.get_mut(split_addr) { - *l2 = self.pc; - Ok(()) - } else { - Err(CodeGenError::FailQuestion) - } - } - - /// 以下のようなコードを生成 - /// - /// ```text - /// L1: eのコード - /// split L1, L2 - /// L2: - /// ``` - fn gen_plus(&mut self, e: &AST) -> Result<(), CodeGenError> { - // L1: eのコード - let l1 = self.pc; - self.gen_expr(e)?; - - // split L1, L2 - self.inc_pc()?; - let split = Instruction::Split(l1, self.pc); // self.pcがL2 - self.insts.push(split); - - Ok(()) - } - - /// *限量子のコード生成器。 - /// - /// 以下のようなコードを生成 - /// - /// ```text - /// L1: split L2, L3 - /// L2: eのコード - /// jump L1 - /// L3: - /// ``` - fn gen_star(&mut self, e: &AST) -> Result<(), CodeGenError> { - // L1: split L2, L3 - let l1 = self.pc; - self.inc_pc()?; - let split = Instruction::Split(self.pc, 0); // self.pcがL2。L3を仮に0と設定 - self.insts.push(split); - - // L2: eのコード - self.gen_expr(e)?; - - // jump L1 - self.inc_pc()?; - self.insts.push(Instruction::Jump(l1)); - - // L3の値を設定 - if let Some(Instruction::Split(_, l3)) = self.insts.get_mut(l1) { - *l3 = self.pc; - Ok(()) - } else { - Err(CodeGenError::FailStar) - } - } - - /// 連続する正規表現のコード生成 - fn gen_seq(&mut self, exprs: &[AST]) -> Result<(), CodeGenError> { - for e in exprs { - self.gen_expr(e)?; - } - - Ok(()) - } - - /// プログラムカウンタをインクリメント - fn inc_pc(&mut self) -> Result<(), CodeGenError> { - safe_add(&mut self.pc, &1, || CodeGenError::PCOverFlow) - } -} diff --git a/src/engine/evaluator.rs b/src/engine/evaluator.rs deleted file mode 100644 index 9ad5105..0000000 --- a/src/engine/evaluator.rs +++ /dev/null @@ -1,151 +0,0 @@ -//! 命令列と入力文字列を受け取り、マッチングを行う -use super::Instruction; -use crate::helper::safe_add; -use std::{ - collections::VecDeque, - error::Error, - fmt::{self, Display}, -}; - -#[derive(Debug)] -pub enum EvalError { - PCOverFlow, - SPOverFlow, - InvalidPC, - InvalidContext, -} - -impl Display for EvalError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "CodeGenError: {:?}", self) - } -} - -impl Error for EvalError {} - -/// 命令列の評価を行う関数。 -/// -/// instが命令列となり、その命令列を用いて入力文字列lineにマッチさせる。 -/// is_depthがtrueの場合に深さ優先探索を、falseの場合に幅優先探索を行う。 -/// -/// 実行時エラーが起きた場合はErrを返す。 -/// マッチ成功時はOk(true)を、失敗時はOk(false)を返す。 -pub fn eval(inst: &[Instruction], line: &[char], is_depth: bool) -> Result<bool, EvalError> { - if is_depth { - eval_depth(inst, line, 0, 0) - } else { - eval_width(inst, line) - } -} - -/// 深さ優先探索で再帰的にマッチングを行う評価器 -fn eval_depth( - inst: &[Instruction], - line: &[char], - mut pc: usize, - mut sp: usize, -) -> Result<bool, EvalError> { - loop { - let next = if let Some(i) = inst.get(pc) { - i - } else { - return Err(EvalError::InvalidPC); - }; - - match next { - Instruction::Char(c) => { - if let Some(sp_c) = line.get(sp) { - if c == sp_c { - safe_add(&mut pc, &1, || EvalError::PCOverFlow)?; - safe_add(&mut sp, &1, || EvalError::SPOverFlow)?; - } else { - return Ok(false); - } - } else { - return Ok(false); - } - } - Instruction::Match => { - return Ok(true); - } - Instruction::Jump(addr) => { - pc = *addr; - } - Instruction::Split(addr1, addr2) => { - if eval_depth(inst, line, *addr1, sp)? || eval_depth(inst, line, *addr2, sp)? { - return Ok(true); - } else { - return Ok(false); - } - } - } - } -} - -fn pop_ctx( - pc: &mut usize, - sp: &mut usize, - ctx: &mut VecDeque<(usize, usize)>, -) -> Result<(), EvalError> { - if let Some((p, s)) = ctx.pop_back() { - *pc = p; - *sp = s; - Ok(()) - } else { - Err(EvalError::InvalidContext) - } -} - -/// 幅優先探索で再帰的にマッチングを行う評価器 -fn eval_width(inst: &[Instruction], line: &[char]) -> Result<bool, EvalError> { - let mut ctx = VecDeque::new(); - let mut pc = 0; - let mut sp = 0; - - loop { - let next = if let Some(i) = inst.get(pc) { - i - } else { - return Err(EvalError::InvalidPC); - }; - - match next { - Instruction::Char(c) => { - if let Some(sp_c) = line.get(sp) { - if c == sp_c { - safe_add(&mut pc, &1, || EvalError::PCOverFlow)?; - safe_add(&mut sp, &1, || EvalError::SPOverFlow)?; - } else { - if ctx.is_empty() { - return Ok(false); - } else { - pop_ctx(&mut pc, &mut sp, &mut ctx)?; - } - } - } else { - if ctx.is_empty() { - return Ok(false); - } else { - pop_ctx(&mut pc, &mut sp, &mut ctx)?; - } - } - } - Instruction::Match => { - return Ok(true); - } - Instruction::Jump(addr) => { - pc = *addr; - } - Instruction::Split(addr1, addr2) => { - pc = *addr1; - ctx.push_back((*addr2, sp)); - continue; - } - } - - if !ctx.is_empty() { - ctx.push_back((pc, sp)); - pop_ctx(&mut pc, &mut sp, &mut ctx)?; - } - } -} diff --git a/src/helper.rs b/src/helper.rs deleted file mode 100644 index e88163b..0000000 --- a/src/helper.rs +++ /dev/null @@ -1,24 +0,0 @@ -pub trait SafeAdd: Sized { - fn safe_add(&self, n: &Self) -> Option<Self>; -} - -impl SafeAdd for usize { - fn safe_add(&self, n: &Self) -> Option<Self> { - self.checked_add(*n) - } -} - -pub fn safe_add<T, F, E>(dst: &mut T, src: &T, f: F) -> Result<(), E> -where - T: SafeAdd, - F: Fn() -> E, -{ - if let Some(n) = dst.safe_add(src) { - *dst = n; - Ok(()) - } else { - Err(f()) - } -} - -pub type DynError = Box<dyn std::error::Error + Send + Sync + 'static>; diff --git a/src/lib.rs b/src/lib.rs index 47da32c..46370bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,6 @@ //! regex::print(expr); // 正規表現のASTと命令列を表示 //! ``` mod engine; -mod helper; +// mod helper; -// pub use engine::{do_matching, print}; +// pub use engine::{do_matching, print}; \ No newline at end of file