看手机导致视力下降怎么办(时间长了会不会得眼病)
看手机导致视力下降怎么办,时间长了会不会得眼病? 看手机已经成为当今人们的一种生活习惯。每天早上睁开眼睛的第一件事情――摸手机,打开朋友圈...
2023-04-14
你想根据一组语法规则解析文本并执行命令,或者构造一个代表输入的抽象语法树。如果语法非常简单,你可以自己写这个解析器,而不是使用一些框架。
在这个问题中,我们集中讨论根据特殊语法去解析文本的问题。为了这样做,你首先要以 BNF(巴科斯范式)或者 EBNF(扩展巴科斯范式)形式指定一个标准语法。
具体关于BNF和EBNF的介绍,可以查看中国维基百科:
BNF:
https://zh.wikipedia.org/wiki/巴科斯范式
EBNF:
https://zh.wikipedia.org/wiki/扩展巴科斯范式
比如,一个简单数学表达式语法可能像下面这样:
expr ::= expr + term | expr - term | termterm ::= term * factor | term / factor | factorfactor ::= ( expr ) | NUM或者,以 EBNF 形式:
expr ::= term { (+|-) term }*term ::= factor { (*|/) factor }*factor ::= ( expr ) | NUMBNF形式简单,知道终结符和非终结符,并且知道 三个符号:
“::=”,表示定义为
“|”,表示或
“<>”,用来区分非终结符
EBNF多增加几个符号:
“[]”,表示可选项
“{}”,表示重复0次或者多次
引号本身,便于区分单个符号的终结符
在 EBNF 中,被包含在 {…}* 中的规则是可选的。 *代表 0 次或多次重复 (跟正则表达式中意义是一样的)。
上边例子中更加易读的写法应该是:
# <expr>加个<>尖括号表示非终结符<expr> ::= <expr> + <term> | <expr> - <term> | <term><term> ::= <term> * <factor> | <term> / <factor> | <factor><factor> ::= ( <expr> ) | NUMBNF例子,如正则表达式:”a(bb)*c”
<v0> ::=a<w><w> ::=bb<w>|c现在,如果你对 BNF 的工作机制还不是很明白的话,就把它当做是一组左右符号可相互替换的规则。一般来讲,解析的原理就是你利用 BNF 完成多个替换和扩展以匹配输入文本和语法规则。为了演示,假设你正在解析形如 2 + 3 * 4 的表达式。这个表达式先要通过使用介绍过的令牌解析技术分解为一组令牌流。结果可能是像下列这样的令牌序列:
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 }* ) 就会被清理掉。在一个成功的解析中,整个右边部分会完全展开来匹配输入令牌流。
有了前面的知识背景,下面我们举一个简单示例来展示如何构建一个递归下降表达式求值程序:
"""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): for matched_item in re.finditer(master_pat, text): tok = Token(matched_item.lastgroup, matched_item.group()) if tok.type != 'WS': yield tok# Parserclass ExpressionEvaluator(object): """ 递归下降解析器的实现。 每种方法实现单个语法规则。 使用._accept()方法 测试并接受当前的超前令牌。 使用._expect()完全匹配并丢弃输入的下一个标记的方法 如果不匹配,则引发SyntaxError """ def parse(self, text): self.tokens = generate_tokens(text) self.tok = None # Last symbol consumed self.next_tok = None # Next symbol tokenized self._advance() # Load first lookahead token return self.expr() def _advance(self): # Advance one token ahead self.tok, self.next_tok = self.next_tok, next(self.tokens, None) def _accept(self, tok_type): # Test and consume the next token if it matches tok_type if self.next_tok and self.next_tok.type == tok_type: self._advance() return True else: return False # Consume next token if it matches tok_type or raise SyntaxError def _expect(self, tok_type): if not self._accept(tok_type): raise SyntaxError('Expected ' + tok_type) # Grammar rules follow def expr(self): # expression ::= term { ('+'|'-') term }* expr_val = self.term() while self._accept('PLUS') or self._accept('MINUS'): op = self.tok.type right = self.term() if op == 'PLUS': expr_val += right elif op == 'MINUS': expr_val -= right return expr_val def term(self): # term ::= factor { ('*'|'/') factor }* term_val = self.factor() while self._accept('TIMES') or self._accept('DIVIDE'): op = self.tok.type right = self.factor() if op == 'TIMES': term_val *= right elif op == 'DIVIDE': term_val /= right return term_val def factor(self): # factor ::= NUM | ( expr ) if self._accept('NUM'): return int(self.tok.value) elif self._accept('LPAREN'): expr_val = self.expr() self._expect('RPAREN') return expr_val else: raise SyntaxError('Expected NUMBER or LPAREN')def descent_parser(): e = ExpressionEvaluator() print(e.parse('2'))# 2 print(e.parse('2 + 5'))# 7 print(e.parse('2 + 2 * 4'))# 10 print(e.parse('2 + (5 + 2) * 3'))# 23if __name__ == '__main__': descent_parser()文本解析是一个很大的主题,一般会占很大的精力。如果你在找寻关于语法,解析算法等相关的背景知识的话,你应该去看一下编译器书籍。很显然,关于这方面的内容太多,不可能在这里全部展开。
尽管如此,编写一个递归下降解析器的整体思路是比较简单的。开始的时候,你先获得所有的语法规则,然后将其转换为一个函数或者方法。因此如果你的语法类似这样:
expr ::= term { ('+'|'-') term }*term ::= factor { ('*'|'/') factor }*factor ::= '(' expr ')' | NUM你应该首先将它们转换成一组像下面这样的方法
class ExpressionEvaluator: def expr(self): pass def term(self): pass def factor(self): pass每个方法要完成的任务很简单 – 它必须从左至右遍历语法规则的每一部分,处理每个令牌。从某种意义上讲,方法的目的就是要么处理完语法规则,要么产生一个语法错误。为了这样做,需采用下面的这些实现方法:
尽管向你演示的是一个简单的例子,递归下降解析器可以用来实现非常复杂的解析。比如, 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 来重写表达式求值程序的代码:
#!/usr/bin/env python# -*- coding: utf-8 -*-# @Author : cory# @Time : 2021/2/2223:20# @Email: 1595610424@qq.comfrom ply.lex import lexfrom ply.yacc import yacc# Token listtokens = ['NUM', 'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'LPAREN', 'RPAREN']# Ignored characterst_ignore = ' \t\n'# 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()print(parser.parse('2'))# 2这个程序中,所有代码都位于一个比较高的层次。你只需要为令牌写正则表达式和规则匹配时的高阶处理函数即可。而实际的运行解析器,接受令牌等等底层动作已经被库函数实现了。
如果对编写语法与词法感兴趣,可以查看PLY文档,以及查看更加详细的编译器的书籍,这里就不再赘述了。
以上内容就是为大家推荐的递归下降分析法(编译原理递归下降分析程序)最佳回答,如果还想搜索其他问题,请收藏本网站或点击搜索更多问题
内容来源于网络仅供参考版权声明:所有来源标注为小樱知识网www.xiaoyin02.com的内容版权均为本站所有,若您需要引用、转载,只需要注明来源及原文链接即可。
本文标题:递归下降分析法(编译原理递归下降分析程序)
本文地址:https://www.xiaoyin02.com/shcs/118031.html
相关文章
看手机导致视力下降怎么办,时间长了会不会得眼病? 看手机已经成为当今人们的一种生活习惯。每天早上睁开眼睛的第一件事情――摸手机,打开朋友圈...
2023-04-14
看手机视力下降怎么恢复,视力真的可以恢复吗? 近视再不做手术的情况下,怎么恢复视力?”想必这是每一位近视患者想要知道的,如今在校门口打出“...
2023-04-11
眼睛玩手机视力下降怎么办,怎样让眼睛变大由于长时间玩手机眼睛变小? 那你平时就要多注意少玩会手机尤其是在灯光暗的地方更加不要玩手机哦。平时...
2023-04-05
手机版qq亲密度怎么看,QQ空间亲密度为什么不下降? 需要一段时间内平均的关注那个人,亲密度才会稳定。如:一月份天天关注。亲密度很高很高。 二月...
2023-04-03
苹果手机怎么进去恢复模式,苹果手机电池健康刚开始下降? 1、关机 2、充电12小时左右(不要超过20小时) 3、在2这种充着电的状态下,开机进入recovery,选...
2023-03-24
热点文章
2021年独生子女补贴新政策是真的吗(独生子女证有有效期吗)
2021年国庆节阅兵仪式几点开始几点结束(2021年国庆节还有阅兵吗)
鼠目寸光一点红是什么生肖动物(鼠目寸光一点红)指什么生肖,紧密
k0到k9的玩法大全(强制gc的玩法和注意事项)
入土为安是什么生肖《入土为安》打一个生肖动物,词语解释
浙江12月底全面停工是真的吗(浙江什么时候放假停工)
如何做t(t怎么把p做哭)
北京口碑最差的三甲医院(北京301医院最擅长什么)