实现一个正则表达式引擎in Python(二)
項目地址:Regex in Python
在看一下之前正則的語法的 BNF 范式
group ::= ("(" expr ")")* expr ::= factor_conn ("|" factor_conn)* factor_conn ::= factor | factor factor* factor ::= (term | term ("*" | "+" | "?"))* term ::= char | "[" char "-" char "]" | .上一篇構造了 term 的簡單 NFA
構造復雜的 NFA
factor
根據上面的factor ::= (term | term ("*" | "+" | "?"))*,先進行 term 的 NFA 的生成,然后根據詞法分析器來判斷要進行哪個 factor 的 NFA 的構造
def factor(pair_out):term(pair_out)if lexer.match(Token.CLOSURE):nfa_star_closure(pair_out)elif lexer.match(Token.PLUS_CLOSE):nfa_plus_closure(pair_out)elif lexer.match(Token.OPTIONAL):nfa_option_closure(pair_out)nfa_star_closure
*操作就是對之前的 term 再生成兩個節(jié)點進行連接
def nfa_star_closure(pair_out):if not lexer.match(Token.CLOSURE):return Falsestart = Nfa()end = Nfa()start.next_1 = pair_out.start_nodestart.next_2 = endpair_out.end_node.next_1 = pair_out.start_nodepair_out.end_node.next_2 = endpair_out.start_node = startpair_out.end_node = endlexer.advance()return Truenfa_plus_closure
+和*的唯一區(qū)別就是必須至少匹配一個字符,所以不能從節(jié)點 2 直接跳轉到節(jié)點 4
def nfa_plus_closure(pair_out):if not lexer.match(Token.PLUS_CLOSE):return Falsestart = Nfa()end = Nfa()start.next_1 = pair_out.start_nodepair_out.end_node.next_1 = pair_out.start_nodepair_out.end_node.next_2 = endpair_out.start_node = startpair_out.end_node = endlexer.advance()return Truenfa_option_closure
?對應的則是只能輸入 0 個或 1 個的匹配字符,所以相對于*就不能再次從節(jié)點 1 跳轉會節(jié)點 0
def nfa_option_closure(pair_out):if not lexer.match(Token.OPTIONAL):return Falsestart = Nfa()end = Nfa()start.next_1 = pair_out.start_nodestart.next_2 = endpair_out.end_node.next_1 = endpair_out.start_node = startpair_out.end_node = endlexer.advance()return Truefactor_conn
factor_conn ::= factor | factor factor*
對于 factor_conn 就是一個或者多個 factor 相連接,也就是說如果有多個 factor,只要將它們的頭尾節(jié)點相連接
def factor_conn(pair_out):if is_conn(lexer.current_token):factor(pair_out)while is_conn(lexer.current_token):pair = NfaPair()factor(pair)pair_out.end_node.next_1 = pair.start_nodepair_out.end_node = pair.end_nodereturn Trueexpr
expr ::= factor_conn ("|" factor_conn)*
對于 expr 就是一個 factor_conn 或者多個 factor_conn 用|相連接
構建|的 NFA 就是生成兩個新節(jié)點,新生成的頭節(jié)點有兩條邊分別連接到 factor_conn 的頭節(jié)點,對于兩個 factor_conn 的尾節(jié)點分別生成一條邊連接到新生成的尾節(jié)點
def expr(pair_out):factor_conn(pair_out)pair = NfaPair()while lexer.match(Token.OR):lexer.advance()factor_conn(pair)start = Nfa()start.next_1 = pair.start_nodestart.next_2 = pair_out.start_nodepair_out.start_node = startend = Nfa()pair.end_node.next_1 = endpair_out.end_node.next_2 = endpair_out.end_node = endreturn Truegroup
group 其實就是在 expr 上加了兩個括號,完全可以去掉
def group(pair_out):if lexer.match(Token.OPEN_PAREN):lexer.advance()expr(pair_out)if lexer.match(Token.CLOSE_PAREN):lexer.advance()elif lexer.match(Token.EOS):return Falseelse:expr(pair_out)while True:pair = NfaPair()if lexer.match(Token.OPEN_PAREN):lexer.advance()expr(pair)pair_out.end_node.next_1 = pair.start_nodepair_out.end_node = pair.end_nodeif lexer.match(Token.CLOSE_PAREN):lexer.advance()elif lexer.match(Token.EOS):return Falseelse:expr(pair)pair_out.end_node.next_1 = pair.start_nodepair_out.end_node = pair.end_node構造 NFA 總結
可以看到對于整個 NFA 的構造,其實就是從最頂部開始向下遞歸,整個過程大概是:
expr -> factor_conn -> factor -> term
當遞歸過程回到factor_conn會根據factor_conn ::= factor | factor factor*判斷可不可以繼續(xù)構造下一個factor
如果不可以就返回到expr,expr則根據expr ::= factor_conn ("|" factor_conn)*
判斷能不能繼續(xù)構造下一個factor_conn重復上面的過程
匹配輸入字符串
現在已經完成了NFA的構造,接下來就是通過這個NFA來對輸入的字符串進行分析
一個例子
以剛剛的圖作為演示,假設0-1節(jié)點的邊是字符集0-9,4-5節(jié)點的邊是字符集a-z,其它都是空
所以這個圖表示的正則表達式[0-9]*[a-z]+
假設對于分析字符串123a
closure
從開始節(jié)點8進行分析,我們要做的第一個操作就是算出在節(jié)點8時不需要任何輸入就可以到達的節(jié)點,這個操作稱為closure,得到closure集合
move
之后我們就需要根據NFA和當前的輸入字符來進行節(jié)點間的跳轉,得到的自然也是一個集合
closure操作
我們利用一個棧來實現closure操作
- 把傳入集合里的所有節(jié)點壓入棧中
- 然后對這個棧的所有節(jié)點進行判斷是否有可以直接跳轉的節(jié)點
- 如果有的話直接壓入棧中
- 直到棧為空則結束操作
move操作
- move操作就是遍歷當前的狀態(tài)節(jié)點集合,如果符合的edge的條件的話
- 就加入到下一個狀態(tài)集合中
match
現在最后一步就是根據上面的兩個操作進行字符串的分析了
- 首先先計算出開始節(jié)點的closure集合
- 開始遍歷輸入的字符串,從剛剛的closure集合開始做move操作
- 然后判斷當前的集合是不是可以作為接收狀態(tài),只要當前集合有某個狀態(tài)節(jié)點沒有連接到其它節(jié)點,它就是一個可接收的狀態(tài)節(jié)點,能被當前NFA接收還需要一個條件就是當前字符已經全匹配完了
小結
這篇主要講了復雜一點的NFA節(jié)點的構建方法,和對利用構造的NFA來對輸入自負床進行分析。到目前為止,其實一個完整的正則表達式引擎已經完成了,但是如果想更近一步的話,還需要將NFA轉換成DFA,再進行DFA的最小化
轉載于:https://www.cnblogs.com/secoding/p/11579650.html
總結
以上是生活随笔為你收集整理的实现一个正则表达式引擎in Python(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 无缓冲区的注意事项
- 下一篇: 实现一个正则表达式引擎in Python