日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

Python實現一個簡單的遞歸下降分析器

瀏覽:26日期:2022-07-15 15:10:50

問題

你想根據一組語法規則解析文本并執行命令,或者構造一個代表輸入的抽象語法樹。 如果語法非常簡單,你可以不去使用一些框架,而是自己寫這個解析器。

解決方案

在這個問題中,我們集中討論根據特殊語法去解析文本的問題。 為了這樣做,你首先要以BNF或者EBNF形式指定一個標準語法。 比如,一個簡單數學表達式語法可能像下面這樣:

expr ::= expr + term | expr - term | term

term ::= term * factor | term / factor | factor

factor ::= ( expr ) | NUM

或者,以EBNF形式:

expr ::= term { (+|-) term }*

term ::= factor { (*|/) factor }*

factor ::= ( expr ) | NUM

在EBNF中,被包含在 {...}* 中的規則是可選的。*代表0次或多次重復(跟正則表達式中意義是一樣的)。

現在,如果你對BNF的工作機制還不是很明白的話,就把它當做是一組左右符號可相互替換的規則。 一般來講,解析的原理就是你利用BNF完成多個替換和擴展以匹配輸入文本和語法規則。 為了演示,假設你正在解析形如 3 + 4 * 5 的表達式。 這個表達式先要通過使用2.18節中介紹的技術分解為一組令牌流。 結果可能是像下列這樣的令牌序列:

NUM + NUM * NUM

在此基礎上, 解析動作會試著去通過替換操作匹配語法到輸入令牌:

exprexpr ::= term { (+|-) term }*expr ::= factor { (*|/) factor }* { (+|-) term }*expr ::= NUM { (*|/) factor }* { (+|-) term }*expr ::= NUM { (+|-) term }*expr ::= NUM + term { (+|-) term }*expr ::= NUM + factor { (*|/) factor }* { (+|-) term }*expr ::= NUM + NUM { (*|/) factor}* { (+|-) term }*expr ::= NUM + NUM * factor { (*|/) factor }* { (+|-) term }*expr ::= NUM + NUM * NUM { (*|/) factor }* { (+|-) term }*expr ::= NUM + NUM * NUM { (+|-) term }*expr ::= NUM + NUM * NUM

下面所有的解析步驟可能需要花點時間弄明白,但是它們原理都是查找輸入并試著去匹配語法規則。 第一個輸入令牌是NUM,因此替換首先會匹配那個部分。 一旦匹配成功,就會進入下一個令牌+,以此類推。 當已經確定不能匹配下一個令牌的時候,右邊的部分(比如 { (*/) factor }* )就會被清理掉。 在一個成功的解析中,整個右邊部分會完全展開來匹配輸入令牌流。

有了前面的知識背景,下面我們舉一個簡單示例來展示如何構建一個遞歸下降表達式求值程序:

#!/usr/bin/env python# -*- encoding: utf-8 -*-'''Topic: 下降解析器Desc :'''import reimport collections# Token specificationNUM = r’(?P<NUM>d+)’PLUS = r’(?P<PLUS>+)’MINUS = r’(?P<MINUS>-)’TIMES = r’(?P<TIMES>*)’DIVIDE = r’(?P<DIVIDE>/)’LPAREN = r’(?P<LPAREN>()’RPAREN = r’(?P<RPAREN>))’WS = r’(?P<WS>s+)’master_pat = re.compile(’|’.join([NUM, PLUS, MINUS, TIMES, DIVIDE, LPAREN, RPAREN, WS]))# TokenizerToken = collections.namedtuple(’Token’, [’type’, ’value’])def generate_tokens(text): scanner = master_pat.scanner(text) for m in iter(scanner.match, None): tok = Token(m.lastgroup, m.group()) if tok.type != ’WS’: yield tok# Parserclass ExpressionEvaluator: ’’’ Implementation of a recursive descent parser. Each method implements a single grammar rule. Use the ._accept() method to test and accept the current lookahead token. Use the ._expect() method to exactly match and discard the next token on on the input (or raise a SyntaxError if it doesn’t match). ’’’ def parse(self, text): self.tokens = generate_tokens(text) self.tok = None # Last symbol consumed self.nexttok = None # Next symbol tokenized self._advance() # Load first lookahead token return self.expr() def _advance(self): ’Advance one token ahead’ self.tok, self.nexttok = self.nexttok, next(self.tokens, None) def _accept(self, toktype): ’Test and consume the next token if it matches toktype’ if self.nexttok and self.nexttok.type == toktype: self._advance() return True else: return False def _expect(self, toktype): ’Consume next token if it matches toktype or raise SyntaxError’ if not self._accept(toktype): raise SyntaxError(’Expected ’ + toktype) # Grammar rules follow def expr(self): 'expression ::= term { (’+’|’-’) term }*' exprval = self.term() while self._accept(’PLUS’) or self._accept(’MINUS’): op = self.tok.type right = self.term() if op == ’PLUS’:exprval += right elif op == ’MINUS’:exprval -= right return exprval def term(self): 'term ::= factor { (’*’|’/’) factor }*' termval = self.factor() while self._accept(’TIMES’) or self._accept(’DIVIDE’): op = self.tok.type right = self.factor() if op == ’TIMES’:termval *= right elif op == ’DIVIDE’:termval /= right return termval def factor(self): 'factor ::= NUM | ( expr )' if self._accept(’NUM’): return int(self.tok.value) elif self._accept(’LPAREN’): exprval = self.expr() self._expect(’RPAREN’) return exprval else: raise SyntaxError(’Expected NUMBER or LPAREN’)def descent_parser(): e = ExpressionEvaluator() print(e.parse(’2’)) print(e.parse(’2 + 3’)) print(e.parse(’2 + 3 * 4’)) print(e.parse(’2 + (3 + 4) * 5’)) # print(e.parse(’2 + (3 + * 4)’)) # Traceback (most recent call last): # File '<stdin>', line 1, in <module> # File 'exprparse.py', line 40, in parse # return self.expr() # File 'exprparse.py', line 67, in expr # right = self.term() # File 'exprparse.py', line 77, in term # termval = self.factor() # File 'exprparse.py', line 93, in factor # exprval = self.expr() # File 'exprparse.py', line 67, in expr # right = self.term() # File 'exprparse.py', line 77, in term # termval = self.factor() # File 'exprparse.py', line 97, in factor # raise SyntaxError('Expected NUMBER or LPAREN') # SyntaxError: Expected NUMBER or LPARENif __name__ == ’__main__’: descent_parser()

討論

文本解析是一個很大的主題, 一般會占用學生學習編譯課程時剛開始的三周時間。 如果你在找尋關于語法,解析算法等相關的背景知識的話,你應該去看一下編譯器書籍。 很顯然,關于這方面的內容太多,不可能在這里全部展開。

盡管如此,編寫一個遞歸下降解析器的整體思路是比較簡單的。 開始的時候,你先獲得所有的語法規則,然后將其轉換為一個函數或者方法。 因此如果你的語法類似這樣:

expr ::= term { (’+’|’-’) term }*term ::= factor { (’*’|’/’) factor }*factor ::= ’(’ expr ’)’ | NUM

你應該首先將它們轉換成一組像下面這樣的方法:

class ExpressionEvaluator: ... def expr(self): ... def term(self): ... def factor(self): ...

每個方法要完成的任務很簡單 - 它必須從左至右遍歷語法規則的每一部分,處理每個令牌。 從某種意義上講,方法的目的就是要么處理完語法規則,要么產生一個語法錯誤。 為了這樣做,需采用下面的這些實現方法:

如果規則中的下個符號是另外一個語法規則的名字(比如term或factor),就簡單的調用同名的方法即可。 這就是該算法中”下降”的由來 - 控制下降到另一個語法規則中去。 有時候規則會調用已經執行的方法(比如,在 factor ::= ’(’expr ’)’ 中對expr的調用)。 這就是算法中”遞歸”的由來。 如果規則中下一個符號是個特殊符號(比如(),你得查找下一個令牌并確認是一個精確匹配)。 如果不匹配,就產生一個語法錯誤。這一節中的 _expect() 方法就是用來做這一步的。 如果規則中下一個符號為一些可能的選擇項(比如 + 或 -), 你必須對每一種可能情況檢查下一個令牌,只有當它匹配一個的時候才能繼續。 這也是本節示例中 _accept() 方法的目的。 它相當于_expect()方法的弱化版本,因為如果一個匹配找到了它會繼續, 但是如果沒找到,它不會產生錯誤而是回滾(允許后續的檢查繼續進行)。 對于有重復部分的規則(比如在規則表達式 ::= term { (’+’|’-’) term }* 中), 重復動作通過一個while循環來實現。 循環主體會收集或處理所有的重復元素直到沒有其他元素可以找到。 一旦整個語法規則處理完成,每個方法會返回某種結果給調用者。 這就是在解析過程中值是怎樣累加的原理。 比如,在表達式求值程序中,返回值代表表達式解析后的部分結果。 最后所有值會在最頂層的語法規則方法中合并起來。

盡管向你演示的是一個簡單的例子,遞歸下降解析器可以用來實現非常復雜的解析。 比如,Python語言本身就是通過一個遞歸下降解析器去解釋的。 如果你對此感興趣,你可以通過查看Python源碼文件Grammar/Grammar來研究下底層語法機制。 看完你會發現,通過手動方式去實現一個解析器其實會有很多的局限和不足之處。

其中一個局限就是它們不能被用于包含任何左遞歸的語法規則中。比如,假如你需要翻譯下面這樣一個規則:

items ::= items ’,’ item | item

為了這樣做,你可能會像下面這樣使用 items() 方法:

def items(self): itemsval = self.items() if itemsval and self._accept(’,’): itemsval.append(self.item()) else: itemsval = [ self.item() ]

唯一的問題是這個方法根本不能工作,事實上,它會產生一個無限遞歸錯誤。

關于語法規則本身你可能也會碰到一些棘手的問題。 比如,你可能想知道下面這個簡單扼語法是否表述得當:

expr ::= factor { (’+’|’-’|’*’|’/’) factor }*factor ::= ’(’ expression ’)’ | NUM

這個語法看上去沒啥問題,但是它卻不能察覺到標準四則運算中的運算符優先級。 比如,表達式 '3 + 4 * 5' 會得到35而不是期望的23. 分開使用”expr”和”term”規則可以讓它正確的工作。

對于復雜的語法,你最好是選擇某個解析工具比如PyParsing或者是PLY。 下面是使用PLY來重寫表達式求值程序的代碼:

from ply.lex import lexfrom ply.yacc import yacc# Token listtokens = [ ’NUM’, ’PLUS’, ’MINUS’, ’TIMES’, ’DIVIDE’, ’LPAREN’, ’RPAREN’ ]# Ignored characterst_ignore = ’ tn’# Token specifications (as regexs)t_PLUS = r’+’t_MINUS = r’-’t_TIMES = r’*’t_DIVIDE = r’/’t_LPAREN = r’(’t_RPAREN = r’)’# Token processing functionsdef t_NUM(t): r’d+’ t.value = int(t.value) return t# Error handlerdef t_error(t): print(’Bad character: {!r}’.format(t.value[0])) t.skip(1)# Build the lexerlexer = lex()# Grammar rules and handler functionsdef p_expr(p): ’’’ expr : expr PLUS term | expr MINUS term ’’’ if p[2] == ’+’: p[0] = p[1] + p[3] elif p[2] == ’-’: p[0] = p[1] - p[3]def p_expr_term(p): ’’’ expr : term ’’’ p[0] = p[1]def p_term(p): ’’’ term : term TIMES factor | term DIVIDE factor ’’’ if p[2] == ’*’: p[0] = p[1] * p[3] elif p[2] == ’/’: p[0] = p[1] / p[3]def p_term_factor(p): ’’’ term : factor ’’’ p[0] = p[1]def p_factor(p): ’’’ factor : NUM ’’’ p[0] = p[1]def p_factor_group(p): ’’’ factor : LPAREN expr RPAREN ’’’ p[0] = p[2]def p_error(p): print(’Syntax error’)parser = yacc()

這個程序中,所有代碼都位于一個比較高的層次。你只需要為令牌寫正則表達式和規則匹配時的高階處理函數即可。 而實際的運行解析器,接受令牌等等底層動作已經被庫函數實現了。

下面是一個怎樣使用得到的解析對象的例子:

>>> parser.parse(’2’)2>>> parser.parse(’2+3’)5>>> parser.parse(’2+(3+4)*5’)37>>>

如果你想在你的編程過程中來點挑戰和刺激,編寫解析器和編譯器是個不錯的選擇。 再次,一本編譯器的書籍會包含很多底層的理論知識。不過很多好的資源也可以在網上找到。 Python自己的ast模塊也值得去看一下。

以上就是Python實現一個簡單的遞歸下降分析器的詳細內容,更多關于Python實現遞歸下降分析器的資料請關注好吧啦網其它相關文章!

標簽: Python 編程
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
国产精品久久久久久久久久齐齐| 亚洲欧美网站| 日本亚洲不卡| 国产精品欧美日韩一区| 久久一区国产| av成人国产| 久久不见久久见免费视频7| 美美哒免费高清在线观看视频一区二区| 欧美日韩视频一区二区三区| 日韩精品视频中文字幕| 久久久精品午夜少妇| 欧美精品一区二区三区精品| 久久亚洲不卡| 久久久人人人| 日韩网站在线| 噜噜噜躁狠狠躁狠狠精品视频| 日韩精品诱惑一区?区三区| 日韩天堂在线| 国产剧情一区二区在线观看| 国产一区丝袜| 在线看片日韩| 91精品国产调教在线观看| 蜜桃久久久久久| 婷婷成人基地| 日本а中文在线天堂| 亚欧洲精品视频在线观看| 久久99国产精品视频| 97精品在线| 麻豆精品在线视频| 中文字幕在线视频久| 不卡在线一区二区| 91久久精品无嫩草影院| 久久蜜桃av| 日本少妇一区| 99在线精品免费视频九九视| 国产精品伦一区二区| 深夜福利亚洲| 久久激情一区| 日韩亚洲国产欧美| 91看片一区| 大香伊人久久精品一区二区| 久久国产乱子精品免费女| 999在线观看精品免费不卡网站| 精品免费在线| 国产探花一区| 亚洲视频播放| 久久久人人人| 激情婷婷久久| 99视频精品全部免费在线视频| 亚洲一二三区视频| 亚洲大全视频| 亚洲一区久久| 日韩福利视频导航| 欧美天堂一区| 日本免费在线视频不卡一不卡二| 免费的成人av| 亚洲久久一区| 国产精品毛片aⅴ一区二区三区| 日本在线视频一区二区| 午夜电影亚洲| 亚洲精品**中文毛片| 你懂的国产精品永久在线| 欧美日韩免费观看一区=区三区| 色综合视频一区二区三区日韩 | 最新亚洲国产| 麻豆久久久久久| 国产视频一区欧美| 久久免费影院| 综合激情婷婷| 日韩欧美在线中字| 日韩欧美三区| 精品欧美久久| 黄色欧美在线| 日本不卡视频在线| 午夜国产精品视频| 精品一区二区三区四区五区| 亚洲精品1区2区| 国产亚洲精品美女久久| 国户精品久久久久久久久久久不卡| 夜夜精品视频| 国产美女视频一区二区| 福利一区二区| 成人黄色av| 日本成人中文字幕| 国产精品嫩模av在线| 尹人成人综合网| 午夜一区在线| 激情综合自拍| 欧美激情视频一区二区三区免费| 国产aa精品| 水野朝阳av一区二区三区| 日韩精品久久久久久| 国产精品xxxav免费视频| 日韩精品亚洲aⅴ在线影院| 久久99久久人婷婷精品综合| av高清不卡| 国产成人精品一区二区三区在线| 久久精品天堂| 神马午夜久久| 国产麻豆精品| 欧美偷窥清纯综合图区| 一区二区三区国产在线| 亚洲婷婷丁香| 在线综合视频| 久久久夜精品| 图片区亚洲欧美小说区| 欧美福利专区| 欧美日韩一区二区三区不卡视频 | 日韩av不卡在线观看| 国产精品99免费看| 婷婷精品在线| 亚洲免费中文| 久久国产毛片| 日韩国产高清在线| 四虎精品一区二区免费| 国产一区 二区| 日本三级亚洲精品| 蜜臀av一区二区在线免费观看| 国产96在线亚洲| 91精品国产成人观看| 亚洲一区免费| 午夜久久av| 国产精品草草| 夜夜嗨网站十八久久| 亚洲ww精品| 视频一区中文字幕国产| 欧美精品一二| 国产一区一一区高清不卡| 国产精品美女久久久| 神马午夜久久| 老司机精品久久| 国产日韩三级| 欧美69视频| 久久国内精品| 欧美午夜精品一区二区三区电影| 日韩高清欧美激情| 欧美手机在线| 国产精品久久久免费| 免费国产自线拍一欧美视频| 久久精品999| 久久亚洲电影| 久久精品影视| 国产精品久久久久77777丨| 午夜欧美精品久久久久久久| 国产精品v日韩精品v欧美精品网站| 精品国产三区在线| 免费国产自线拍一欧美视频| 欧洲av一区二区| 国产综合色区在线观看| 国产超碰精品| 日韩中文字幕区一区有砖一区| 日韩欧美看国产| 人人精品久久| 免费精品国产的网站免费观看| 欧美在线不卡| aⅴ色国产欧美| 国精品一区二区三区| 国产三级一区| 在线一区二区三区视频| 欧美片第1页| 麻豆精品视频在线| 免费视频一区二区三区在线观看| 少妇精品在线| 日韩专区一卡二卡| 免费国产自久久久久三四区久久| 日韩精品欧美精品| 亚洲成av人片一区二区密柚| 国产色播av在线| 日韩区一区二| 9久re热视频在线精品| 亚洲精品在线a| а√天堂8资源中文在线| 久久精品伊人| 免费视频最近日韩| 精精国产xxxx视频在线野外| 亚洲天堂av影院| 日韩欧美另类一区二区| 国产日韩视频| 国产欧美综合一区二区三区| 日本一区免费网站| 国产色综合网| 亚洲一区二区三区久久久| 欧美综合精品| 国产精品97| 日本不卡视频一二三区| 97在线精品| 日韩高清在线不卡| 蜜桃成人av| 欧美精品一区二区三区精品| 日韩一区二区久久| 亚洲三级观看| 麻豆91精品91久久久的内涵| 极品av在线| 日韩专区欧美专区| 中文另类视频| 国产欧美日韩综合一区在线播放| 青青伊人久久| 亚洲成人一区在线观看| 亚洲一区久久| 久久精品国产免费|