add: parser

This commit is contained in:
Akira Tempaku 2025-02-17 12:17:51 +09:00
parent 2a350690b2
commit dc3f497fb7
Signed by: paku
GPG key ID: 5B4E8402BCC50607
3 changed files with 218 additions and 0 deletions

4
src/engine.rs Normal file
View file

@ -0,0 +1,4 @@
//! 正規表現エンジン
// mod codegen;
// mod evaluator;
mod parser;

199
src/engine/parser.rs Normal file
View file

@ -0,0 +1,199 @@
//! 正規表現の式をパースし、抽象構文木に変換
use std::{
error::Error,
fmt::{self, Display},
mem::take,
};
/// パースエラーを表すための型
#[derive(Debug)]
pub enum ParseError {
InvalidEscape(usize, char), // 誤ったエスケープシーケンス
InvalidRightParen(usize), // 左開き括弧無し
NoPrev(usize), // +、|、*、?の前に式がない
NoRightParen, // 右閉じ括弧無し
Empty, // 空のパターン
}
/// パースエラーを表示するために、Displayトレイトを実装
impl Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ParseError::InvalidEscape(pos, c) => {
write!(f, "ParseError: invalid escape: pos = {pos}, char = '{c}'")
}
ParseError::InvalidRightParen(pos) => {
write!(f, "ParseError: invalid right parenthesis: pos = {pos}")
}
ParseError::NoPrev(pos) => {
write!(f, "ParseError: no previous expression: pos = {pos}")
}
ParseError::NoRightParen => {
write!(f, "ParseError: no right parenthesis")
}
ParseError::Empty => write!(f, "ParseError: empty expression"),
}
}
}
impl Error for ParseError {} // エラー用に、Errorトレイトを実装
/// 抽象構文木を表現するための型
#[derive(Debug)]
pub enum AST {
Char(char),
Plus(Box<AST>),
Star(Box<AST>),
Question(Box<AST>),
Or(Box<AST>, Box<AST>),
Seq(Vec<AST>),
}
/// parse_plus_star_question関数で利用するための列挙型
enum PSQ {
Plus,
Star,
Question,
}
/// 正規表現を抽象構文木に変換
pub fn parse(expr: &str) -> Result<AST, ParseError> {
// 内部状態を表現するための型
// Char状態 : 文字列処理中
// Escape状態 : エスケープシーケンス処理中
enum ParseState {
Char,
Escape,
}
let mut seq = Vec::new(); // 現在のSeqのコンテキスト
let mut seq_or = Vec::new(); // 現在のOrのコンテキスト
let mut stack = Vec::new(); // コンテキストのスタック
let mut state = ParseState::Char; // 現在の状態
for (i, c) in expr.chars().enumerate() {
match &state {
ParseState::Char => {
match c {
'+' => parse_plus_star_question(&mut seq, PSQ::Plus, i)?,
'*' => parse_plus_star_question(&mut seq, PSQ::Star, i)?,
'?' => parse_plus_star_question(&mut seq, PSQ::Question, i)?,
'(' => {
// 現在のコンテキストをスタックに追加し、
// 現在のコンテキストを空の状態にする
let prev = take(&mut seq);
let prev_or = take(&mut seq_or);
stack.push((prev, prev_or));
}
')' => {
// 現在のコンテキストをスタックからポップ
if let Some((mut prev, prev_or)) = stack.pop() {
// "()"のように式が空の場合はpushしない
if !seq.is_empty() {
seq_or.push(AST::Seq(seq));
}
// Orを生成
if let Some(ast) = fold_or(seq_or) {
prev.push(ast);
}
// 以前のコンテキストを、現在のコンテキストにする
seq = prev;
seq_or = prev_or;
} else {
// "abc)"のように、開き括弧がないのに閉じ括弧がある場合はエラー
return Err(ParseError::InvalidRightParen(i));
}
}
'|' => {
if seq.is_empty() {
// "||", "(|abc)"などと、式が空の場合はエラー
return Err(ParseError::NoPrev(i));
} else {
let prev = take(&mut seq);
seq_or.push(AST::Seq(prev));
}
}
'\\' => state = ParseState::Escape,
_ => seq.push(AST::Char(c)),
};
}
ParseState::Escape => {
// エスケープシーケンス処理
let ast = parse_escape(i, c)?;
seq.push(ast);
state = ParseState::Char;
}
}
}
// 閉じ括弧が足りない場合はエラー
if !stack.is_empty() {
return Err(ParseError::NoRightParen);
}
// "()"のように式が空の場合はpushしない
if !seq.is_empty() {
seq_or.push(AST::Seq(seq));
}
// Orを生成し、成功した場合はそれを返す
if let Some(ast) = fold_or(seq_or) {
Ok(ast)
} else {
Err(ParseError::Empty)
}
}
/// +、*、?をASTに変換
///
/// 後置記法で、+、*、?の前にパターンがない場合はエラー
///
/// 例 : *ab、abc|+などはエラー
fn parse_plus_star_question(
seq: &mut Vec<AST>,
ast_type: PSQ,
pos: usize,
) -> Result<(), ParseError> {
if let Some(prev) = seq.pop() {
let ast = match ast_type {
PSQ::Plus => AST::Plus(Box::new(prev)),
PSQ::Star => AST::Star(Box::new(prev)),
PSQ::Question => AST::Question(Box::new(prev)),
};
seq.push(ast);
Ok(())
} else {
Err(ParseError::NoPrev(pos))
}
}
/// 特殊文字のエスケープ
fn parse_escape(pos: usize, c: char) -> Result<AST, ParseError> {
match c {
'\\' | '(' | ')' | '|' | '+' | '*' | '?' => Ok(AST::Char(c)),
_ => {
let err = ParseError::InvalidEscape(pos, c);
Err(err)
}
}
}
/// orで結合された複数の式をASTに変換
///
/// たとえば、abc|def|ghi は、AST::Or("abc", AST::Or("def", "ghi"))というASTとなる
fn fold_or(mut seq_or: Vec<AST>) -> Option<AST> {
if seq_or.len() > 1 {
// seq_orの要素が複数ある場合は、Orで式を結合
let mut ast = seq_or.pop().unwrap();
seq_or.reverse();
for s in seq_or {
ast = AST::Or(Box::new(s), Box::new(ast));
}
Some(ast)
} else {
// seq_orの要素が一つのみの場合は、Orではなく、最初の値を返す
seq_or.pop()
}
}

15
src/lib.rs Normal file
View file

@ -0,0 +1,15 @@
//! # 正規表現エンジン用クレート。
//!
//! ## 利用例
//!
//! ```
//! use regex;
//! let expr = "a(bc)+|c(def)*"; // 正規表現
//! let line = "cdefdefdef"; // マッチ対象文字列
//! regex::do_matching(expr, line, true); // 幅優先探索でマッチング
//! regex::print(expr); // 正規表現のASTと命令列を表示
//! ```
mod engine;
// mod helper;
// pub use engine::{do_matching, print};